初始化代码

This commit is contained in:
2025-12-22 17:13:05 +08:00
parent ed0de08e3a
commit 1f7e9d401b
2947 changed files with 526137 additions and 0 deletions

View File

@@ -0,0 +1,320 @@
<template>
<view class="abnor" :style="{paddingBottom: percent}">
<view class="abnor-box">
<image mode="aspectFit" class="abnor-image" v-if="pimage" :src="pimage"></image>
<view class="abnor-text" v-if="ptitle">{{ptitle}}</view>
<view @tap.stop="emitAbnorTipTap" class="abnor-tip" :class="[{'tip-flex':!tipMax}]"
:style="{maxWidth:tipMax,textAlign:tipMax?'left':''}" v-if="ptip">
<text v-for="(item,index) in ptip" :key="index"
:style="{color:item.color == 1 ? primaryColor:''}">{{item.text}}</text>
</view>
<block v-if="pbutton">
<block v-for="(item,index) in pbutton" :key="index">
<view class="abnor-btn" :class="btnSize"
:style="{background:item.type == 'confirm' ? primaryColor : '',color:item.type == 'confirm'?'white':''}"
@tap.stop="emitAbnorTap(index)">{{item.text}}</view>
</block>
</block>
</view>
</view>
</template>
<script>
const Types = {
'REQUEST_ERROR': {
image: 'https://lbqny.migugu.com/admin/public/request-error.png',
title: '网络加载失败',
button: [{
text: '点击刷新',
type: 'confirm'
}],
tip: []
},
'NOT_FOUND': {
image: 'https://lbqny.migugu.com/admin/public/not-found.png',
title: '很抱歉,找不到你要访问的页面',
button: [{
text: '返回',
type: 'cancel'
}],
tip: []
},
'DATA': {
// image: 'https://lbqny.migugu.com/admin/public/no-data-2.png',
// image: 'https://lbqny.migugu.com/admin/public/no-data.png',
image: '/static/image/loading/no-data.png',
title: '没有相关数据哦',
button: [],
tip: [{
text: '',
color: 0
}]
},
'FOLLOW': {
image: 'https://lbqny.migugu.com/admin/public/no-follow.png',
title: '关注有趣的人',
button: [],
tip: [{
text: '不再错过他们每一条动态',
color: 0
}]
},
'FEED': {
image: 'https://lbqny.migugu.com/admin/public/no-feed.png',
title: '还没有任何反馈哦',
button: [],
tip: []
},
'SHOP': {
image: 'https://lbqny.migugu.com/admin/public/no-shop.png',
title: '稍后再来试试吧~',
button: [],
tip: []
},
'WEIBO': {
image: 'https://lbqny.migugu.com/admin/public/no-weibo.png',
title: '',
button: [],
tip: []
},
'SEARCH': {
image: 'https://lbqny.migugu.com/admin/public/no-search.png',
title: '抱歉!没找到相关商品~',
button: [],
tip: []
},
'TAG': {
image: 'https://lbqny.migugu.com/admin/public/no-tag.png',
title: '',
button: [],
tip: []
},
'MESSAGE': {
image: 'https://lbqny.migugu.com/admin/public/no-message.png',
title: '消息通知空空如也',
button: [],
tip: []
},
'LIVE': {
image: 'https://lbqny.migugu.com/admin/public/no-live.png',
title: '',
button: [],
tip: []
},
'ORDER': {
image: 'https://lbqny.migugu.com/admin/public/no-order.png',
title: "还没有相关订单哦",
button: [],
tip: []
},
'CART': {
image: 'https://lbqny.migugu.com/admin/public/no-cart.png',
title: '购物车还是空的哦~',
button: [{
text: '去逛逛',
type: 'confirm'
}],
tip: []
},
'FOOTPRINT': {
image: 'https://lbqny.migugu.com/admin/public/no-footprint.png',
title: '你还没有足迹~',
button: [],
tip: []
},
'COUPON': {
// image: 'https://lbqny.migugu.com/admin/public/no-coupon.png',
image: '/static/image/loading/coupon.png',
title: '暂无卡券',
button: [],
tip: [{
text: '去首页逛逛吧~',
color: 0
}]
},
'REDUCTION': {
image: 'https://lbqny.migugu.com/admin/reduction/nodata.png',
title: '您当前没有满减券,请向工作人员索取',
button: [],
tip: []
}
}
import {
mapState,
} from 'vuex';
export default {
name: 'abnor',
props: {
type: {
type: String,
default () {
return 'DATA'
}
},
image: {
type: String,
default () {
return ''
}
},
title: {
type: String,
default () {
return ''
}
},
tip: {
type: Array,
default () {
return []
}
},
button: {
type: Array,
default () {
return []
}
},
tipTap: {
type: String,
default () {
return ''
}
},
tipMax: {
type: String,
default () {
return '100%'
}
},
btnSize: {
type: String,
default () {
return 'big'
}
},
percent: {
type: String,
default () {
return '100%'
}
},
},
created() {
this.init();
},
data() {
return {
pimage: '',
ptitle: '',
ptip: '',
pbutton: ''
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
}),
methods: {
emitAbnorTipTap() {
let {
tipTap = ''
} = this
if (!tipTap) return
this.$emit(tipTap);
},
emitAbnorTap(index) {
let {
type
} = this.pbutton[index]
this.$emit(type);
},
init() {
let type = this.type;
// console.log(type, "=====type", this.image);
if (Types[type]) {
this.pimage = this.image || Types[type].image;
this.ptitle = this.title || Types[type].title;
this.pbutton = this.button.length > 0 ? this.button : Types[type].button;
this.ptip = this.tip.length > 0 ? this.tip : Types[type].tip;
}
}
},
}
</script>
<style lang="scss">
.abnor {
position: relative;
display: block;
width: 100%;
height: 0;
padding-bottom: 100%;
overflow: hidden;
.abnor-box {
position: absolute;
display: flex;
top: 0;
bottom: 0;
left: 0;
right: 0;
flex-direction: column;
justify-content: center;
align-items: center;
.abnor-image {
width: 514rpx;
height: 260rpx;
background: transparent;
}
.abnor-text {
margin-top: 30rpx;
color: #333;
font-size: 30rpx;
font-weight: 400;
}
.abnor-tip {
margin: 15rpx 0 10rpx 0;
color: #999;
font-size: 26rpx;
text-align: center;
max-height: 30vh;
overflow: auto;
}
.tip-flex {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.abnor-btn {
min-width: 228rpx;
height: 66rpx;
margin-top: 30rpx;
padding: 0 30rpx;
background-color: #eee;
border: 0 none;
border-radius: 10rpx;
color: #333;
font-size: 28rpx;
text-align: center;
// overflow: hidden;
line-height: 66rpx;
}
.abnor-btn.big {
width: 690rpx;
height: 100rpx;
line-height: 100rpx;
font-size: 32rpx;
margin-top: 40rpx;
border-radius: 50rpx;
}
}
}
</style>

View File

@@ -0,0 +1,492 @@
<template>
<view style="width:100%">
<block v-if="needAuth">
<view @tap="toShowAuth">
<slot></slot>
</view>
</block>
<block v-else>
<view @tap="go(1)">
<slot></slot>
</view>
</block>
<uni-popup ref="show_auth_item">
<view class="auth-box fill-base flex-column flex-center radius-26">
<block v-if="pType === 'userInfo'">
<view class="auth-top fill-base flex-column flex-center" :style="{backgroundColor:primaryColor}">
<view class="flex-center pd-lg">
<view class="auth-info" style="overflow: hidden;">
<button class="auth-info" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
<image class="avatar" :src="avatarUrl || '/static/mine/default_user.png'"></image>
</button>
</view>
</view>
<view class="f-paragraph c-base">选择头像</view>
</view>
<view class="auth-w mt-lg mb-lg">
<view class="auth-input">
<view style="width:110rpx;">昵称</view>
<view>
<input type="nickname" v-module="nickName" placeholder="请输入昵称" @blur="onBlur"/>
</view>
</view>
</view>
</block>
<block v-if="pType == 'phone'">
<image class="auth-img" :src="`/static/mine/auth.png`">
</image>
</block>
<view class="space-sm"></view>
<view class="f-caption" :style="{color:primaryColor}">{{contentList[pType][0]}}</view>
<view class="space-lg"></view>
<view class="space-lg"></view>
<block v-if="pType === 'userInfo'">
<button @tap.stop="authUserProfile" hover-class="btn-hover" class="clear-btn flex-center auth-btn"
:style="{backgroundColor:primaryColor,color:'white'}">{{btn_text||contentList[pType][1]}}</button>
</block>
<block v-if="pType === 'phone'">
<button open-type="getPhoneNumber" hover-class="btn-hover" @getphonenumber="authPhone"
class="clear-btn flex-center auth-btn" :style="{backgroundColor:primaryColor,color:'white'}">
{{btn_text||contentList[pType][1]}}
</button>
</block>
<block v-if="pType === 'setting'">
<button open-type="openSetting" hover-class="btn-hover" @opensetting="openSetting"
class="clear-btn flex-center auth-btn" :style="{backgroundColor:primaryColor,color:'white'}">
{{btn_text||contentList[pType][1]}}
</button>
</block>
<view @tap="go(pType == 'phone' && !userInfo.phone ? 2 : 1)" class="f-caption c-caption mt-md"
v-if="!must">{{pType=='userInfo'?'暂不授权':'暂不登录'}}</view>
<view class="space-md"></view>
</view>
</uni-popup>
<uni-popup ref="show_phone_item" :maskClick="false">
<view class="common-popup-content popup-phone pd-lg flex-center flex-column fill-base">
<view class="f-md-title c-black">请输入手机号</view>
<view class="space-lg pb-lg"></view>
<view class="space-lg pb-lg"></view>
<view class="flex-center mb-lg">
<view class="input-info sm mr-md radius-16">
<input v-model="subForm.phone" type="number"
class="item-input flex-y-center pl-lg pr-lg f-sm-title c-title"
placeholder-class="c-placeholder" :placeholder="rule[0].errorMsg" />
</view>
<view @tap="toSend" class="send-btn flex-center c-base radius-16"
:style="{background:primaryColor}">
{{authTime>0?`(${authTime}s)`:'发送'}}
</view>
</view>
<view class="input-info radius-16">
<input v-model="subForm.short_code" type="number"
class="item-input flex-y-center pl-lg pr-lg f-sm-title c-title" maxlength="6"
placeholder-class="c-placeholder" :placeholder="rule[1].errorMsg" />
</view>
<view class="button">
<view @tap.stop="go(3)" class="item-child">
取消
</view>
<view @tap.stop="submit" class="item-child" :style="{background: primaryColor,color:'#fff'}">
确定
</view>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import {
mapState,
mapActions,
mapMutations,
} from "vuex"
export default {
components: {},
name: 'auth',
props: {
needAuth: {
type: Boolean,
default () {
return false
}
},
must: {
type: Boolean,
default () {
return false
}
},
userMust: {
type: Boolean,
default () {
return true
}
},
showAuth: {
type: Boolean,
default () {
return false
}
},
type: {
type: String,
default () {
return 'phone'
}
},
btn_text: {
type: String,
default () {
return ''
}
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
commonOptions: state => state.user.commonOptions,
userInfo: state => state.user.userInfo,
}),
created() {
this.init();
},
data() {
return {
contentList: {
userInfo: ['尊贵的用户,获取授权是为了能更好的为你服务', '立即授权'],
phone: ['尊贵的用户,登录后我们才能更好的为你服务', '立即登录'],
setting: ['为了功能正常使用,你需要打开设置并开启获取相应权限', '打开设置'],
},
pType: '',
pMust: '',
authTime: 0,
timer: null,
subForm: {
phone: '',
short_code: ''
},
rule: [{
name: "phone",
checkType: "isMobile",
errorMsg: "请输入手机号",
regText: "手机号"
}, {
name: "short_code",
checkType: "isNotNull",
errorMsg: "请输入短信验证码",
regText: "短信验证码"
}],
avatarUrl:'',
lockTap: false
}
},
methods: {
...mapActions(['getUserInfo', 'getAuthUserProfile', 'getAuthPhone', ]),
init() {
let {
type,
must,
showAuth
} = this
this.$set(this, 'pType', type)
this.$set(this, 'pMust', must)
if (!showAuth) return
this.$refs.show_auth_item.open()
},
async authPhone(e) {
let {
pMust
} = this
let phone = await this.getAuthPhone({
e,
})
console.log(pMust, phone, "===== pMust auth_phone");
if (!phone) {
this.go(pMust ? 2 : 1)
return false
} else {
this.$set(this, 'pType', 'userInfo')
this.$set(this, 'pMust', this.userMust)
console.log(this.PType, this.pMust, "====auth_to_userInfo");
if (!this.pMust) return
this.$refs.show_auth_item.open()
}
},
async onChooseAvatar(e) {
this.reloadAvatar = await this.$api.base.uploadFile({
filePath: e.detail.avatarUrl,
filetype: 'picture'
})
this.avatarUrl = this.reloadAvatar.attachment_path
},
async onBlur(e){
//console.log(e)
this.nickName = e.detail.value
},
async authUserProfile(e) {
// #ifdef APP-PLUS
uni.getUserInfo({
provider: 'weixin',
success: res => {
let {
userInfo = {}
} = res
this.toUpdateUserInfo(userInfo)
}
})
// #endif
// #ifndef APP-PLUS
uni.getUserProfile({
desc: '用于完善个人资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
success: (res) => {
let {
userInfo = {},
encryptedData,
iv,
} = res
let param = Object.assign({}, userInfo, {
encryptedData,
iv,
});
this.toUpdateUserInfo(param)
},
fail: (res) => {
console.log(res, "=====fail");
this.toUpdateUserInfo()
}
})
// #endif
},
async toUpdateUserInfo(userInfo = {}) {
let {
pMust
} = this
if (userInfo.nickName) {
let {
coupon_atv_id = 0
} = this.commonOptions
userInfo.coupon_atv_id = coupon_atv_id,
userInfo.avatarUrl = this.avatarUrl,
userInfo.nickName = this.nickName
await this.getAuthUserProfile(userInfo)
}
console.log(pMust, userInfo, "===== pMust auth_userinfo");
this.go(pMust && !userInfo.nickName ? 2 : 1);
},
toShowAuth() {
let {
phone = ''
} = this.userInfo
let type = !phone ? 'phone' : 'userInfo'
this.$set(this, 'pType', type)
console.log(this.userInfo, phone, this.pType, "=====toShowAuth")
let refs_key = 'show_auth_item'
// #ifndef MP-WEIXIN
refs_key = 'show_phone_item'
if (this.configInfo.short_code_status * 1 == 0) {
this.go(1)
return
}
// #endif
this.$refs[refs_key].open()
},
go(type = 1) {
this.lockTap = false
this.$emit(type == 1 ? 'go' : 'hide')
let refs_key = type == 3 ? 'show_phone_item' : 'show_auth_item'
this.$refs[refs_key].close();
this.toResetItem()
},
toResetItem() {
this.timer && clearTimeout(this.timer)
this.authTime = 0
this.subForm = {
phone: '',
short_code: ''
}
},
//表单验证
validate(param, is_send = false) {
let validate = new this.$util.Validate();
this.rule.map(item => {
let {
name,
} = item
if (name == 'short_code' && is_send) return
validate.add(param[name], item);
})
let message = validate.start();
return message;
},
async toSend() {
let {
authTime
} = this
if (authTime) return
let {
phone = ''
} = this.subForm
let msg = this.validate({
phone
}, true);
if (msg) {
this.$util.showToast({
title: msg
});
return;
}
if (this.lockTap) return
this.lockTap = true
this.$util.showLoading()
try {
await this.$api.user.sendShortMsg({
phone
})
this.$util.hideAll()
this.lockTap = false
let time = 60
this.timer = setInterval(() => {
if (time === 0) {
clearTimeout(this.timer)
return
}
time--
this.authTime = time
}, 1000)
} catch (e) {
setTimeout(() => {
this.lockTap = false
this.$util.hideAll()
}, 2000)
}
},
async openSetting(e) {
// Handle the openSetting event
console.log('openSetting event:', e);
// The setting was opened, now check if the required permission was granted
// You may need to implement specific logic based on your permission requirements
},
async submit() {
let param = this.$util.deepCopy(this.subForm)
let msg = this.validate(param);
if (msg) {
this.$util.showToast({
title: msg
});
return;
}
if (param.short_code.length != 6) {
this.$util.showToast({
title: `请输入6位数短信验证码`
})
return
}
if (this.lockTap) return
this.lockTap = true
this.$util.showLoading()
try {
await this.$api.user.bindUserPhone(param)
this.$util.hideAll()
this.lockTap = false
this.$refs.show_phone_item.close()
this.toResetItem()
await this.getUserInfo()
setTimeout(() => {
this.$emit('go')
}, 500)
} catch (e) {
setTimeout(() => {
this.lockTap = false
this.$util.hideAll()
}, 2000)
}
}
},
}
</script>
<style lang="scss">
.auth-box {
width: 630rpx;
height: auto;
padding: 0 30rpx 30rpx 30rpx;
overflow: hidden;
.auth-img {
width: 322rpx;
height: 341rpx;
/* background: #f4f6f8; */
}
.auth-btn {
width: 100%;
height: 85rpx;
border-radius: 8rpx;
}
.auth-info {
width: 120rpx;
height: 120rpx;
background: #f4f6f8;
border-radius: 50%;
}
}
.auth-top {
width: 930rpx;
height: 260rpx;
border-radius: 0 0 50% 50%;
overflow: hidden;
}
.auth-w{
width:100%;
}
.auth-input{
align-items: center;
background: #efeff5;
display: flex;
padding: 20rpx;
border-radius: 8rpx;
}
.popup-phone {
width: 630rpx;
.input-info {
width: 570rpx;
height: 90rpx;
background: #F7F7F7;
.item-input {
height: 90rpx;
font-size: 32rpx;
text-align: left;
}
}
.input-info.sm {
width: 400rpx;
}
.send-btn {
width: 150rpx;
height: 90rpx;
}
}
</style>

View File

@@ -0,0 +1,215 @@
<template>
<!-- 轮播图广告 -->
<view class='swiper-box' :style='{margin:`0 ${margin}rpx`,borderRadius:`${.3125}rem`}'>
<swiper class="swiper-ad" :indicator-dots="false" :autoplay="autoplay" :indicator-color="indicatorColor"
:indicator-active-color="indicatorActiveColor" :circular="circular"
:previous-margin="list.length>1?previousMargin:0" :next-margin="list.length>1?nextMargin:0"
@change="handerChange" :style="{height:`${11.375}rem`,borderRadius:`${.3125}rem`}"
easing-function="linear">
<swiper-item v-for="(item,index) in list" :key="index">
<view class='img-box rel' :style="{borderRadius:`${.3125}rem`}" @tap='changeItem(item,index)'>
<image mode="aspectFill" lazy-load :src='item.img || item.cover || item' class="swiper-ad__img"
:style="{borderRadius:`${.3125}rem`}" />
<view class="abs play-video flex-center" :style="{borderRadius:`${.3125}rem`}"
v-if="haveVideo && index == 0"><i class="iconfont icon-bofang"></i>
</view>
</view>
</swiper-item>
</swiper>
<view class='numbers' v-if="list.length>1&&indicatorType=='number'" :style="{textAlign:indicatorStyle}">
<view class="number">{{activeIndex+1}}/{{list.length}}</view>
</view>
<view class='dots' v-if="list.length>1&&indicatorType=='dot'" :style="{textAlign:indicatorStyle}">
<view class='dot' v-for="(item,index) in list" :key="index"
:style='{backgroundColor:index==activeIndex?indicatorActiveColor:indicatorColor,width:index==activeIndex? `${dotWidth}rpx` :"12rpx"}'>
</view>
</view>
</view>
</template>
<script>
import {
mapState,
} from 'vuex';
export default {
name: 'banner',
props: {
list: {
type: Array,
default () {
return [1, 2, 3]
}
},
height: {
type: Number,
default () {
return 400
}
},
indicatorType: {
type: String,
default () {
return "dot"
}
},
indicatorColor: {
type: String,
default () {
return "#FEFFFE"
}
},
indicatorActiveColor: {
type: String,
default () {
return "#fff"
}
},
indicatorStyle: {
type: String,
default () {
return 'center'
}
},
circular: {
type: Boolean,
default () {
return true
}
},
autoplay: {
type: Boolean,
default () {
return false
}
},
previousMargin: {
type: Number,
default () {
return 0
}
},
nextMargin: {
type: Number,
default () {
return 0
}
},
dotWidth: {
type: Number,
default () {
return 12
}
},
margin: {
type: Number,
default () {
return 0
}
},
borderRadius: {
type: Number,
default () {
return 0
}
},
haveVideo: {
type: Boolean,
default () {
return false
}
}
},
created() {
},
data() {
return {
activeIndex: 0
}
},
computed: mapState({
configInfo: state => state.config.configInfo,
commonOptions: state => state.user.commonOptions,
}),
methods: {
handerChange: function(e) {
this.activeIndex = e.detail.current;
},
changeItem(item, index) {
this.$emit("change", {
item,
index
})
}
}
}
</script>
<style lang="scss">
.swiper-box {
position: relative;
overflow: hidden;
.img-box {
width: 100%;
height: 100%;
.swiper-ad__img {
width: 100%;
height: 100%;
background: #fff;
}
.play-video {
width: 100%;
height: 100%;
top: 0;
background: rgba(0, 0, 0, 0.2);
.iconfont {
font-size: 100rpx;
color: #fff;
}
}
}
.dots {
position: absolute;
z-index: 20rpx;
text-align: right;
width: 100%;
transform: translateY(-48rpx);
padding: 0 20rpx;
.dot {
display: inline-block;
height: 12rpx;
width: 12rpx;
background-color: #FEFFFE;
border-radius: 6rpx;
margin: 0 8rpx;
}
}
.numbers {
position: absolute;
z-index: 20rpx;
text-align: right;
width: 100%;
transform: translateY(-80rpx);
padding: 0 20rpx;
.number {
display: inline-block;
width: 90rpx;
line-height: 50rpx;
background: rgba(0, 0, 0, 0.3);
text-align: center;
color: #fff;
border-radius: 45rpx;
}
}
}
</style>

View File

@@ -0,0 +1,323 @@
<template>
<view>
<!-- 轮播图 -->
<view v-if="type=='swiper'" class='column-box' :style='{paddingTop:whiteSpace+"rpx",paddingBottom:whiteSpace+"rpx",paddingLeft:wingBlank+"rpx",paddingRight:wingBlank+"rpx"}'>
<swiper class="swiper-category" @change="handerChange" :style="{height:(146*formatRowNum+20*(formatRowNum-1))+'rpx'}">
<swiper-item class="swiper-category-item" v-for="(pitem,pindex) in formatList" :key="pindex">
<view v-for="(item,index) in pitem" :key="index" class="column-item" :style="{width:100/colNum + '%',marginTop:index<colNum? '0' : '20rpx'}"
@tap='change(item)'>
<image mode="aspectFill" lazy-load v-if="item.icon" class="column-img" :src="item.icon"></image>
<view v-else class="column-no-img" :style="{background:colorList[index%8],borderRadius:'50%',margin:'0rpx auto'}">
{{getFirstText(item.cate_name)}}
</view>
<view class='column-text'>{{item.cate_name}}</view>
</view>
</swiper-item>
</swiper>
<view class='dots' v-if="formatList.length>1">
<view class='dot' v-for="(item,index) in formatList.length" :key="index" :style='{background:index==current?indicatorActiveColor:indicatorColor}'></view>
</view>
</view>
<!-- 滑动 -->
<view v-if="type=='scroll'" class='column-box' :style='{paddingTop:whiteSpace+"rpx",paddingBottom:whiteSpace+"rpx",paddingLeft:wingBlank+"rpx",paddingRight:wingBlank+"rpx"}'>
<scroll-view scroll-x class='scroll-x' @scroll="handerScroll">
<view class='scroll-x-item' v-for="(pItem,pindex) in formatList" :key="pindex">
<view class='column-item' v-for="(item,index) in pItem" :key="index" :style='{width:(750/colNum)+"rpx"}' @tap='change(item)'>
<image mode="aspectFill" lazy-load class='column-img' :src='item.icon'></image>
<view class='column-text'>{{item.cate_name}}</view>
</view>
</view>
</scroll-view>
<view class='ink-bar-box' v-if="list.length>formatRowNum*colNum">
<view class="ui-tabs-ink-bar-wrapper">
<view class="ui-tabs-ink-bar" :style='{left:left+"rpx",background:indicatorActiveColor}'></view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'column',
props: {
type: {
type: String,
default () {
return "swiper"
}
},
list: {
type: Array,
default () {
return []
}
},
colNum: {
type: Number,
default () {
return 4
}
},
rowNum: {
type: Number,
default () {
return 2
}
},
indicatorActiveColor: {
type: String,
default () {
return '#f6f5fa'
}
},
indicatorColor: {
type: String,
default () {
return '#f6f5fa'
}
},
wingBlank: {
type: Number,
default () {
return 0
}
},
whiteSpace: {
type: Number,
default () {
return 30
}
},
borderRadius: {
type: Number,
default () {
return 0
}
}
},
created() {
},
data() {
return {
activeIndex: 0,
newList: [],
left: 0,
current: 0,
colorList: ["#fc7f87", "#56b4fc", "#f8ae41", "#11dd9e", "#fba745", "#fb7c85", "#46adfc", "#fc7d86"]
}
},
computed: {
formatRowNum() {
let {
colNum,
rowNum,
list
} = this;
let length = list.length;
let newRowNum = length <= colNum ? 1 : rowNum;
return newRowNum
},
formatList() {
let {
colNum,
rowNum,
type,
list,
formatRowNum
} = this;
let index = 0;
let length = list.length;
let newList = [];
let count = formatRowNum * colNum
// let count = type == 'scroll' ? colNum : formatRowNum * colNum
while (index < length) {
newList.push(list.slice(index, index += count));
}
return newList
},
},
methods: {
handerScroll: function(e) {
let {
scrollLeft,
scrollWidth
} = e.detail;
let windowWidth = uni.getSystemInfoSync().windowWidth;
let left = scrollLeft * 30 / (scrollWidth - windowWidth);
this.left = left
},
handerChange: function(e) {
this.current = e.detail.current;
},
change(item) {
this.$emit("change", item)
},
getFirstText(d) {
d = d || '名称'
return d.slice(0, 1)
},
},
}
</script>
<style lang="scss">
/* 轮播图 */
.column-box {
background: #fff;
font-size: 24rpx;
color: #888;
.swiper-category {
width: 100%;
background: #fff;
font-size: 24rpx;
color: #666;
border-radius: 10rpx;
.swiper-category-item {
box-sizing: border-box;
display: flex;
align-items: flex-start;
flex-wrap: wrap;
background: #fff;
.column-item {
margin-top: 20rpx;
float: left;
.column-img {
width: 100rpx;
height: 100rpx;
margin: 0 auto;
}
.column-no-img {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 36rpx;
color: #fff;
}
.column-text {
margin-top: 10rpx;
text-align: center;
color: #454A53;
width: 100%;
padding: 0 10%;
display: block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
}
.scroll-x {
white-space: nowrap;
background: #fff;
padding: 20rpx 0;
.scroll-x-item {
position: relative;
display: inline-block;
vertical-align: text-top;
display: flex;
flex-direction: column;
}
.column-item {
margin-top: 20rpx;
float: left;
.column-img {
width: 100rpx;
height: 100rpx;
margin: 0 auto;
}
.column-text {
margin-top: 10rpx;
text-align: center;
color: #454A53;
width: 100%;
padding: 0 10%;
display: block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
.dots {
background: white;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
padding-top: 20rpx;
.dot {
height: 16rpx;
width: 16rpx;
background: #ddd;
border-radius: 8rpx;
margin: 0 8rpx;
}
}
.ink-bar-box {
background: #fff;
padding-bottom: 20rpx;
.ui-tabs-ink-bar-wrapper {
width: 60rpx;
background: #ddd;
position: relative;
left: 345rpx;
bottom: 0;
height: 8rpx;
transform-origin: 50% 50%;
transition: width 250ms ease-out, left 250ms ease-out;
overflow: hidden;
border-radius: 4rpx;
.ui-tabs-ink-bar {
position: absolute;
width: 30rpx;
background: #e74d45;
height: 8rpx;
border-radius: 4rpx;
}
}
}
}
</style>

View File

@@ -0,0 +1,188 @@
<template>
<uni-popup type="center" :maskClick="maskClick" ref="common_popup">
<view class="common-popup-content fill-base">
<view class="title" v-if="ptitle">{{ptitle}}</view>
<view class="desc" v-if="pdesc">{{pdesc}}</view>
<image mode="aspectFill" lazy-load class="image" :class="imgSize" :src="info.image" v-if="info.image">
</image>
<view class="name ellipsis-3" v-if="info.name">{{info.name}}</view>
<view class="button">
<block v-for="(item,index) in pbutton" :key="index">
<view @tap.stop="toEmit(index)" class="item-child"
:style="{background: item.type == 'confirm' ? primaryColor : '',color:item.type == 'confirm' ?'white':''}">
{{item.title}}
</view>
</block>
</view>
</view>
</uni-popup>
</template>
<script>
const Types = {
'CANCEL_ORDER': {
title: '取消订单',
desc: '请确认是否取消订单,取消后将无法恢复',
text: '确认取消'
},
'END_ORDER': {
title: '确认收货',
desc: '请确认是否收货,收货后将无法恢复',
text: '确认收货'
},
'HX_ORDER': {
title: '核销订单',
desc: '请确认是否核销订单,核销订单后将无法恢复',
text: '确认核销'
},
'DELETE_ORDER': {
title: '删除订单',
desc: '请确认是否删除订单,删除后将无法恢复',
text: '确认删除'
},
'REFUND_ORDER': {
title: '申请退款',
desc: '请确认是否申请退款',
text: '确认申请'
},
'CANCEL_REFUND_ORDER': {
title: '取消退款',
desc: '请确认是否取消退款',
text: '确认取消'
},
'NO_PASS_REFUND': {
title: '拒绝退款',
desc: '请确认是否拒绝退款',
text: '确认拒绝'
},
'HX_CODE': {
title: '核销码',
desc: '请出示二维码给核销人员',
},
'CHANGE_UP_ITEM': {
title: '上架',
desc: '请确认是否上架',
text: '确认上架'
},
'CHANGE_DOWN_ITEM': {
title: '下架',
desc: '请确认是否下架',
text: '确认下架'
},
'DEL_ITEM': {
title: '删除',
desc: '请确认是否删除此数据,删除后将无法恢复',
text: '确认删除'
},
}
import {
mapState,
} from "vuex"
export default {
components: {},
props: {
maskClick: {
type: Boolean,
default () {
return false
}
},
type: {
type: String,
default () {
return 'CANCEL_ORDER'
}
},
title: {
type: String,
default () {
return ''
}
},
desc: {
type: String,
default () {
return ''
}
},
text: {
type: String,
default () {
return ''
}
},
info: {
type: Object,
default () {
return {}
}
},
button: {
type: Array,
default () {
return [{
title: '取消',
type: 'cancel'
}, {
title: '确定取消',
type: 'confirm'
}]
}
},
imgSize: {
type: String,
default () {
return ''
}
}
},
created() {
this.init();
},
data() {
return {
ptitle: '',
pdesc: '',
pbutton: [],
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
}),
methods: {
init() {
let {
type,
} = this;
if (Types[type]) {
this.ptitle = this.title || Types[type].title
this.pdesc = this.desc || Types[type].desc
this.pbutton = this.button
if (this.text || Types[type].text) {
this.pbutton[1].title = this.text || Types[type].text
}
}
},
open() {
this.$refs.common_popup.open()
},
close() {
this.$refs.common_popup.close()
},
toEmit(index) {
let {
type
} = this.button[index]
if (type == 'cancel') {
this.close()
} else {
this.$emit(type)
}
}
},
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,69 @@
<template>
<view class="content" :style="{background: bgColor}">
<block v-for="(item,index) in text" :key="index">
<view @click="confirm(item)" class="bottom-view"
:style="{width:(100/ text.length)+ '%',margin:text.length==1?'0 30rpx':index==0?'0 0 0 30rpx': index == text.length -1 ? '0 30rpx 0 0' : '',borderRadius:text.length==1?'88rpx':index==0?'88rpx 0 0 88rpx': index== text.length -1 ? '0 88rpx 88rpx 0' : '',background: item.type == 'confirm'? primaryColor : subColor}">
{{ item.text }}
</view>
</block>
</view>
</template>
<script>
import {
mapState,
} from "vuex"
export default {
props: {
text: {
type: Array,
default () {
return [{
text: '保存',
type: 'confirm'
}]
}
},
bgColor: {
type: String,
default () {
return '#fff'
}
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
}),
methods: {
confirm(item) {
this.$emit(item.type)
}
}
}
</script>
<style scoped lang="scss">
.content {
position: fixed;
bottom: 0rpx;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 128rpx;
z-index: 997;
height: calc(128rpx + env(safe-area-inset-bottom) / 2);
padding-bottom: calc(env(safe-area-inset-bottom) / 2);
.bottom-view {
width: auto;
height: 88rpx;
line-height: 88rpx;
text-align: center;
font-size: 32rpx;
font-weight: bold;
color: #FFFFFF;
}
}
</style>

View File

@@ -0,0 +1,79 @@
<template>
<view>
<view class="ui-fixed" :class="{'fixed-top':position=='top','fixed-bottom':position=='bottom'}"
:style="{zIndex:zIndex}">
<slot></slot>
</view>
<view :style="{height:height+'px'}"></view>
</view>
</template>
<script>
export default {
name: 'fixed',
props: {
position: {
type: [String],
default () {
return 'top'
}
},
zIndex: {
type: Number,
default () {
return 999
}
},
refresh: {
type: Boolean,
default () {
return false
}
}
},
data() {
return {
height: uni.getSystemInfoSync().windowWidth * 100 / 750
}
},
mounted() {
this.setHeight();
},
onReady() {
this.setHeight();
},
watch: {
refresh(val) {
console.log(val, "=======val")
if (val) return
this.setHeight()
},
},
methods: {
setHeight() {
var _this = this;
var query = uni.createSelectorQuery().in(this);
query.select('.ui-fixed').boundingClientRect(function(res) {
_this.height = res.height;
}).exec();
}
},
}
</script>
<style>
.ui-fixed {
position: fixed;
left: 0;
width: 100%;
}
.fixed-top {
top: 0;
}
.fixed-bottom {
bottom: 0;
}
</style>

View File

@@ -0,0 +1,62 @@
var inlineTags = {
abbr: true,
b: true,
big: true,
code: true,
del: true,
em: true,
font: true,
i: true,
ins: true,
label: true,
mark: true,
q: true,
s: true,
small: true,
span: true,
strong: true,
u: true
}
export default {
getStyle: function(style, display) {
var res = "";
var reg = getRegExp("float\s*:\s*[^;]*", "i");
if (reg.test(style)) res += reg.exec(style)[0];
reg = getRegExp("margin[^;]*", "gi");
var margin = reg.exec(style);
while (margin) {
res += (';' + margin[0]);
margin = reg.exec(style);
}
reg = getRegExp("display\s*:\s*([^;]*)", "i");
if (reg.test(style) && reg.exec(style)[1] != "flex") res += (';' + reg.exec(style)[0]);
else res += (';display:' + display);
reg = getRegExp("flex\s*:[^;]*", "i");
if (reg.test(style)) res += (';' + reg.exec(style)[0]);
reg = getRegExp("[^;\s]*width[^;]*", "ig");
var width = reg.exec(style);
while (width) {
res += (';' + width[0]);
width = reg.exec(style);
}
return res;
},
setImgStyle: function(item, imgMode) {
if (imgMode == "widthFix")
item.attrs.style = (item.attrs.style || '') + ";height:auto !important";
if (getRegExp("[^-]width[^pev;]+").test(";" + item.attrs.style))
item.attrs.style = (item.attrs.style || '') + ";width:100%";
if (item.attrs.style)
item.attrs.style = item.attrs.style.replace(getRegExp('margin[^;]*', "gi"), "");
return [item];
},
setStyle: function(item) {
if (item.attrs.style)
item.attrs.style = item.attrs.style.replace(getRegExp("width[^;]*?%", "gi"), "width:100%").replace(getRegExp(
'margin[^;]+', "gi"), "");
return [item];
},
notContinue: function(item) {
return !(item.c || inlineTags[item.name] || item["continue"]);
}
}

View File

@@ -0,0 +1,56 @@
var inlineTags = {
abbr: true,
b: true,
big: true,
code: true,
del: true,
em: true,
font: true,
i: true,
ins: true,
label: true,
mark: true,
q: true,
s: true,
small: true,
span: true,
strong: true,
u: true
}
module.exports = {
getStyle: function(style, display) {
var res = "";
var reg = getRegExp("float[^;]+(?![\s\S]*?float)", "i");
if (reg.test(style)) res += reg.exec(style)[0];
reg = getRegExp("margin[^;]+", "gi");
if (reg.test(style)) res += (';' + style.match(reg).join(';'));
reg = getRegExp("display\s*:\s*([^;]*)(?![\s\S]*?display)", "i");
if (reg.test(style) && reg.exec(style)[1] != "flex") res += (';' + reg.exec(style)[0]);
else res += (';display:' + display);
reg = getRegExp("flex[^;]*:[^;]+", "ig");
if (reg.test(style)) res += (';' + style.match(reg).join(';'));
reg = getRegExp("[^;\s]*width[^;]+", "ig");
if (reg.test(style)) res += (';' + style.match(reg).join(';'));
return res;
},
setImgStyle: function(item, imgMode, imgLoad) {
if (imgMode == "widthFix") item.attrs.style = (item.attrs.style || '') + ";height:auto !important";
if (item.attrs.style)
item.attrs.style = item.attrs.style.replace(getRegExp("width[^;]*?%", "gi"), "width:100%").replace(getRegExp(
'margin[^;]+', "gi"), "");
if (!imgLoad) {
delete item.attrs.src;
item.attrs.style += ";width:20px !important;height:20px !important";
}
return [item];
},
setStyle: function(item) {
if (item.attrs.style)
item.attrs.style = item.attrs.style.replace(getRegExp("width[^;]*?%", "gi"), "width:100%").replace(getRegExp(
'margin[^;]+', "gi"), "");
return [item];
},
notContinue: function(item) {
return !(item.c || inlineTags[item.name] || item["continue"]);
}
}

View File

@@ -0,0 +1,622 @@
<!--
parser 主模块组件
github地址https://github.com/jin-yufeng/Parser
文档地址https://jin-yufeng.github.io/Parser
插件市场https://ext.dcloud.net.cn/plugin?id=805
authorJinYufeng
-->
<template>
<view>
<!--#ifdef H5-->
<slot v-if="!html && !nodes.length"></slot>
<div :id="'rtf' + uid" :style="(selectable ? 'user-select:text;-webkit-user-select:text;' : '') + (showWithAnimation ? ('opacity:0;' + showAnimation) : '')"></div>
<!--#endif-->
<!--#ifndef H5-->
<slot v-if="!html[0].name && !html[0].type && !nodes.length"></slot>
<!--#endif-->
<!--#ifdef MP-ALIPAY-->
<view class="_contain" :style="(selectable ? 'user-select:text;-webkit-user-select:text;' : '') + (showWithAnimation ? ('opacity:0;' + showAnimation) : '')">
<trees :nodes="nodes.length ? nodes : (html[0].name || html[0].type ? html : [])" :imgMode="imgMode" />
</view>
<!--#endif-->
<!--#ifndef MP-ALIPAY || H5-->
<trees class="_contain" :style="'display:block;' + (selectable ? 'user-select:text;-webkit-user-select:text;' : '') + (showWithAnimation ? ('opacity:0;' + showAnimation) : '')"
:nodes="nodes.length ? nodes : (html[0].name || html[0].type ? html : [])" :imgMode="imgMode" :loadVideo="loadVideo" />
<!--#endif-->
</view>
</template>
<script>
// #ifndef H5
import trees from "./trees"
var document; // document 补丁包,详见 https://jin-yufeng.github.io/Parser/#/instructions?id=document
const parseHtmlSync = require('./libs/MpHtmlParser.js').parseHtmlSync;
// #ifdef APP-PLUS
const cache = {};
// #endif
// #ifndef APP-PLUS
const cache = getApp().parserCache = {};
// #endif
const CssHandler = require("./libs/CssHandler.js");
// 散列函数(计算 cache 的 key
const Hash = (str) => {
for (var i = 0, hash = 5381, len = str.length; i < len; i++)
hash += (hash << 5) + str.charCodeAt(i);
return hash;
};
// #endif
// 动画
const showAnimation =
"transition:400ms ease 0ms;transition-property:transform,opacity;transform-origin:50% 50% 0;-webkit-transition:400ms ease 0ms;-webkit-transform:;-webkit-transition-property:transform,opacity;-webkit-transform-origin:50% 50% 0;opacity: 1"
const config = require('./libs/config.js');
// #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU || MP-TOUTIAO
// 图片链接去重
const Deduplication = (src) => {
if (src.indexOf("http") != 0) return src;
var newSrc = '';
for (var i = 0; i < src.length; i++) {
newSrc += (Math.random() >= 0.5 ? src[i].toUpperCase() : src[i].toLowerCase());
if (src[i] == '/' && src[i - 1] != '/' && src[i + 1] != '/') break;
}
newSrc += src.substring(i + 1);
return newSrc;
}
// #endif
export default {
name: 'parser',
data() {
return {
// #ifdef APP-PLUS
loadVideo: false,
// #endif
// #ifdef H5
uid: this._uid,
showAnimation: '',
// #endif
// #ifndef H5
showAnimation: {},
controls: {},
// #endif
nodes: []
}
},
// #ifndef H5
components: {
trees
},
// #endif
props: {
'html': {
type: null,
default: null
},
'autocopy': {
type: Boolean,
default: true
},
// #ifndef MP-ALIPAY
'autopause': {
type: Boolean,
default: true
},
// #endif
'autopreview': {
type: Boolean,
default: true
},
'autosetTitle': {
type: Boolean,
default: true
},
'domain': {
type: String,
default: null
},
'imgMode': {
type: String,
default: 'default'
},
// #ifdef MP-WEIXIN || MP-QQ || H5 || APP-PLUS
'lazyLoad': {
type: Boolean,
default: false
},
// #endif
'selectable': {
type: Boolean,
default: false
},
'tagStyle': {
type: Object,
default: () => {
return {};
}
},
'showWithAnimation': {
type: Boolean,
default: false
},
'useAnchor': {
type: Boolean,
default: false
},
'useCache': {
type: Boolean,
default: false
}
},
watch: {
html(html) {
this.setContent(html, undefined, true);
}
},
mounted() {
this.imgList = [];
this.imgList.each = function(f) {
for (var i = 0; i < this.length; i++) {
// #ifdef MP-ALIPAY || APP-PLUS
this[i] = f(this[i], i, this) || this[i];
// #endif
// #ifndef MP-ALIPAY || APP-PLUS
var newSrc = f(this[i], i, this);
if (newSrc) {
if (this.includes(newSrc)) this[i] = Deduplication(newSrc);
else this[i] = newSrc;
}
// #endif
}
}
this.setContent(this.html, undefined, true);
},
// #ifdef H5
beforeDestroy() {
if (this._observer) this._observer.disconnect();
},
// #endif
methods: {
// #ifdef H5
setContent(html, options, observed) {
if (typeof options == "object")
for (var key in options) {
key = key.replace(/-(\w)/g, function() {
return arguments[1].toUpperCase();
})
this[key] = options[key];
}
html = html || '';
if (!html) {
if (this.rtf) this.rtf.parentNode.removeChild(this.rtf);
return;
}
if (typeof html != 'string') html = this.Dom2Str(html.nodes || html);
// 处理 rpx
if (/[0-9.]*?rpx/.test(html)) {
const rpx = uni.getSystemInfoSync().screenWidth / 750;
html = html.replace(/([0-9.]*?)rpx/g, function() {
return parseFloat(arguments[1]) * rpx + "px";
})
}
// 处理 tag-style 和 userAgentStyles
var style = "<style>";
for (var item in config.userAgentStyles)
style += (item + '{' + config.userAgentStyles[item] + '}');
for (var item in this.tagStyle)
style += (item + '{' + this.tagStyle[item] + '}');
style += "</style>";
html = style + html;
if (this.rtf) this.rtf.parentNode.removeChild(this.rtf);
this.rtf = document.createElement('div');
this.rtf.innerHTML = html;
for (var style of this.rtf.getElementsByTagName("style")) {
style.innerHTML = style.innerHTML.replace(/\s*body/g, "#rtf" + this._uid);
style.setAttribute("scoped", "true");
}
// 懒加载
if (this.lazyLoad && IntersectionObserver) {
if (this._observer) this._observer.disconnect();
this._observer = new IntersectionObserver(changes => {
for (var change of changes) {
if (change.isIntersecting) {
change.target.src = change.target.getAttribute("data-src");
change.target.removeAttribute("data-src");
this._observer.unobserve(change.target);
}
}
}, {
rootMargin: "1000px 0px 1000px 0px"
})
}
var component = this;
// 获取标题
var title = this.rtf.getElementsByTagName("title");
if (title.length && this.autosetTitle)
uni.setNavigationBarTitle({
title: title[0].innerText
})
// 图片处理
this.imgList.length = 0;
var imgs = this.rtf.getElementsByTagName("img");
for (var i = 0; i < imgs.length; i++) {
var img = imgs[i];
img.style.maxWidth = "100%";
img.i = i;
if (this.domain && img.getAttribute("src")[0] == "/") {
if (img.getAttribute("src")[1] == "/")
img.src = (this.domain.includes("://") ? this.domain.split("://")[0] : "http") + ':' + img.getAttribute("src");
else img.src = this.domain + img.getAttribute("src");
}
component.imgList.push(img.src);
if (img.parentElement.nodeName != 'A') {
img.onclick = function() {
if (!this.hasAttribute('ignore')) {
var preview = true;
this.ignore = () => preview = false;
component.$emit('imgtap', this);
if (preview && component.autopreview) {
uni.previewImage({
current: this.i,
urls: component.imgList
});
}
}
}
}
img.onerror = function() {
component.$emit('error', {
source: "img",
target: this
});
}
if (component.lazyLoad && this._observer) {
img.setAttribute("data-src", img.src);
img.removeAttribute("src");
this._observer.observe(img);
}
}
// 链接处理
var links = this.rtf.getElementsByTagName("a");
for (var link of links) {
link.onclick = function(e) {
var jump = true,
href = this.getAttribute("href");
component.$emit('linkpress', {
href,
ignore: () => jump = false
});
if (jump && href) {
if (href[0] == '#') {
if (component.useAnchor) {
component.navigateTo({
id: href.substring(1)
})
}
} else if (href.indexOf("http") == 0 || href.indexOf("//") == 0)
return true;
else {
uni.navigateTo({
url: href
})
}
}
return false;
}
}
// 视频处理
var videos = this.rtf.getElementsByTagName("video");
component.videoContexts = videos;
for (var video of videos) {
video.style.maxWidth = "100%";
video.onerror = function() {
component.$emit('error', {
source: "video",
target: this
});
}
video.onplay = function() {
if (component.autopause) {
for (var video of component.videoContexts) {
if (video != this)
video.pause();
}
}
}
}
// 音频处理
var audios = this.rtf.getElementsByTagName("audios");
for (var audio of audios) {
audio.onerror = function(e) {
component.$emit('error', {
source: "audio",
target: this
});
}
}
document.getElementById("rtf" + this._uid).appendChild(this.rtf);
if (this.showWithAnimation)
this.showAnimation = showAnimation;
if (!observed) this.nodes = [0];
this.$nextTick(() => {
this.$emit("ready", this.rtf.getBoundingClientRect());
})
},
Dom2Str(nodes) {
var str = "";
for (var node of nodes) {
if (node.type == "text")
str += node.text;
else {
str += ('<' + node.name);
for (var attr in node.attrs || {})
str += (' ' + attr + '="' + node.attrs[attr] + '"');
if (!node.children || !node.children.length) str += "/>";
else str += ('>' + this.Dom2Str(node.children) + "</" + node.name + '>');
}
}
return str;
},
getText(whiteSpace = true) {
if (!whiteSpace) return this.rtf.innerText.replace(/\s/g, '');
return this.rtf.innerText;
},
navigateTo(obj) {
if (!obj.id) {
window.scrollTo(0, this.rtf.offsetTop);
return obj.success ? obj.success({
errMsg: "pageScrollTo:ok"
}) : null;
}
var target = document.getElementById(obj.id);
if (!target) return obj.fail ? obj.fail({
errMsg: "Label Not Found"
}) : null;
uni.pageScrollTo({
scrollTop: this.rtf.offsetTop + target.offsetTop,
success: obj.success,
fail: obj.fail
});
},
// #endif
// #ifndef H5
setContent(html, options, observed) {
if (typeof options == "object")
for (var key in options) {
key = key.replace(/-(\w)/g, function() {
return arguments[1].toUpperCase();
})
this[key] = options[key];
}
if (this.showWithAnimation)
this.showAnimation = showAnimation;
if (!html) {
if (observed) return;
else this.nodes = [];
} else if (typeof html == "string") {
var res;
// 缓存读取
if (this.useCache) {
var hash = Hash(html);
if (cache[hash])
res = cache[hash];
else {
res = parseHtmlSync(html, this);
cache[hash] = res;
}
} else res = parseHtmlSync(html, this);
this.nodes = res;
this.$emit('parse', res);
} else if (html.constructor == Array) {
if (!observed) this.nodes = html;
else this.nodes = [];
// 非本插件产生的 array 需要进行一些转换
if (html.length && html[0].PoweredBy != "Parser") {
const Parser = {
_imgNum: 0,
_videoNum: 0,
_audioNum: 0,
_domain: this.domain,
_protocol: this.domain ? (this.domain.includes("://") ? this.domain.split("://")[0] : "http") : undefined,
_STACK: [],
CssHandler: new CssHandler(this.tagStyle)
};
Parser.CssHandler.getStyle('');
const DFS = (nodes) => {
for (var node of nodes) {
if (node.type == "text") continue;
node.attrs = node.attrs || {};
for (var item in node.attrs) {
if (!config.trustAttrs[item]) node.attrs[item] = undefined;
else if (typeof node.attrs[item] != "string") node.attrs[item] = node.attrs[item].toString();
}
config.LabelAttrsHandler(node, Parser);
if (config.blockTags[node.name]) node.name = 'div';
else if (!config.trustTags[node.name]) node.name = 'span';
if (node.children && node.children.length) {
Parser._STACK.push(node);
DFS(node.children);
Parser._STACK.pop();
} else node.children = undefined;
}
}
DFS(html);
this.nodes = html;
}
} else if (typeof html == 'object' && html.nodes) {
this.nodes = html.nodes;
console.warn("Parser 类型错误object 类型已废弃,请直接将 html 设置为 object.nodes array 类型)");
} else {
return this.$emit('error', {
source: "parse",
errMsg: "传入的nodes数组格式不正确应该传入的类型是array实际传入的类型是" + typeof html.nodes
});
}
// #ifdef APP-PLUS
this.loadVideo = false;
// #endif
if (document) this.document = new document("html", this.html || html, this);
this.$nextTick(() => {
this.imgList.length = 0;
this.videoContexts = [];
const getContext = (components) => {
for (let component of components) {
if (component.$options.name == "trees") {
var observered = false;
for (var item of component.nodes) {
if (item.continue) continue;
if (item.name == 'img') {
if (item.attrs.src && item.attrs.i) {
// #ifndef MP-ALIPAY || APP-PLUS
if (this.imgList.indexOf(item.attrs.src) == -1)
this.imgList[item.attrs.i] = item.attrs.src;
else this.imgList[item.attrs.i] = Deduplication(item.attrs.src);
// #endif
// #ifdef MP-ALIPAY || APP-PLUS
this.imgList[item.attrs.i] = item.attrs.src;
// #endif
}
// #ifndef MP-ALIPAY
if (!observered) {
observered = true;
if (this.lazyLoad && uni.createIntersectionObserver) {
if (component._observer) component._observer.disconnect();
component._observer = uni.createIntersectionObserver(component);
component._observer.relativeToViewport({
top: 1000,
bottom: 1000
}).observe('.img', res => {
component.imgLoad = true;
component._observer.disconnect();
component._observer = null;
})
} else
component.imgLoad = true;
}
// #endif
}
// #ifndef MP-ALIPAY
else if (item.name == 'video') {
var context = uni.createVideoContext(item.attrs.id, component);
context.id = item.attrs.id;
this.videoContexts.push(context);
}
// #endif
// #ifdef MP-WEIXIN
else if (item.name == 'audio' && item.attrs.autoplay)
wx.createAudioContext(item.attrs.id, component).play();
// #endif
// 设置标题
else if (item.name == "title") {
if (item.children[0].type == "text" && item.children[0].text && this.autosetTitle)
uni.setNavigationBarTitle({
title: item.children[0].text
})
}
// #ifdef MP-BAIDU || MP-ALIPAY
if (item.attrs && item.attrs.id) {
this.anchors = this.anchors || [];
this.anchors.push({
id: item.attrs.id,
node: component
})
}
// #endif
}
}
if (component.$children.length)
getContext(component.$children)
}
}
// #ifdef MP-TOUTIAO
setTimeout(() => {
// #endif
getContext(this.$children)
uni.createSelectorQuery().in(this).select("._contain").boundingClientRect(res => {
this.$emit("ready", res);
}).exec();
// #ifdef MP-TOUTIAO
}, 200)
// #endif
// #ifdef APP-PLUS
setTimeout(() => {
this.loadVideo = true;
}, 3000);
// #endif
})
},
getText(whiteSpace = true) {
var text = "";
const DFS = (node) => {
if (node.type == "text") return text += node.text;
else {
if (whiteSpace && (((node.name == 'p' || node.name == "div" || node.name == "tr" || node.name == "li" ||
/h[1-6]/.test(node.name)) && text && text[text.length - 1] != '\n') || node.name == "br"))
text += '\n';
for (var child of node.children || [])
DFS(child);
if (whiteSpace && (node.name == 'p' || node.name == "div" || node.name == "tr" || node.name == "li" || /h[1-6]/.test(
node.name)) && text && text[text.length - 1] != '\n')
text += '\n';
else if (whiteSpace && node.name == "td") text += '\t';
}
}
var nodes = ((this.nodes && this.nodes.length) ? this.nodes : (this.html[0] && (this.html[0].name || this.html[0].type) ?
this.html : []));
if (!nodes.length) return "";
for (var node of nodes)
DFS(node);
return text;
},
navigateTo(obj) {
var Scroll = (selector, component) => {
const query = uni.createSelectorQuery().in(component ? component : this);
query.select(selector).boundingClientRect();
query.selectViewport().scrollOffset();
query.exec(res => {
if (!res || !res[0])
return obj.fail ? obj.fail({
errMsg: "Label Not Found"
}) : null;
uni.pageScrollTo({
scrollTop: res[1].scrollTop + res[0].top,
success: obj.success,
fail: obj.fail
})
})
}
if (!obj.id) Scroll("._contain");
else {
// #ifndef MP-BAIDU || MP-ALIPAY
Scroll('._contain >>> #' + obj.id + ', ._contain >>> .' + obj.id);
// #endif
// #ifdef MP-BAIDU || MP-ALIPAY
for (var anchor of this.anchors) {
if (anchor.id == obj.id) {
Scroll("#" + obj.id + ", ." + obj.id, anchor.node);
}
}
// #endif
}
},
// #endif
getVideoContext(id) {
if (!id) return this.videoContexts;
else {
for (var video of this.videoContexts) {
if (video.id == id) return video;
}
}
return null;
}
}
}
</script>
<style>
/* #ifndef MP-BAIDU */
:host {
display: block;
overflow: scroll;
-webkit-overflow-scrolling: touch;
}
/* #endif */
</style>

View File

@@ -0,0 +1,149 @@
/*
解析和匹配 Css 的选择器
github地址https://github.com/jin-yufeng/Parser
文档地址https://jin-yufeng.github.io/Parser
authorJinYufeng
*/
const userAgentStyles = require("./config.js").userAgentStyles;
class CssHandler {
constructor(tagStyle = {}) {
this.styles = Object.assign({}, tagStyle);
};
getStyle(data) {
var style = '';
data = data.replace(/<[sS][tT][yY][lL][eE][\s\S]*?>([\s\S]*?)<\/[sS][tT][yY][lL][eE][\s\S]*?>/g, function() {
style += arguments[1];
return '';
})
this.styles = new CssParser(style, this.styles).parse();
return data;
};
parseCss(css) {
return new CssParser(css, {}, true).parse();
};
match(name, attrs) {
var tmp, matched = ((tmp = this.styles[name]) ? (tmp + ';') : '');
if (attrs.class) {
var classes = attrs.class.split(' ');
for (var i = 0; i < classes.length; i++)
if (tmp = this.styles['.' + classes[i]])
matched += (tmp + ';');
}
if (tmp = this.styles['#' + attrs.id])
matched += tmp;
return matched;
};
}
module.exports = CssHandler;
function isBlankChar(c) {
return c == ' ' || c == '\u00A0' || c == '\t' || c == '\r' || c == '\n' || c == '\f';
};
class CssParser {
constructor(data, tagStyle, api) {
this.data = data;
this.res = tagStyle;
if (!api)
for (var item in userAgentStyles) {
if (tagStyle[item]) tagStyle[item] = userAgentStyles[item] + ';' + tagStyle[item];
else tagStyle[item] = userAgentStyles[item];
}
this._floor = 0;
this._i = 0;
this._list = [];
this._comma = false;
this._sectionStart = 0;
this._state = this.Space;
};
parse() {
for (; this._i < this.data.length; this._i++)
this._state(this.data[this._i]);
return this.res;
};
// 状态机
Space(c) {
if (c == '.' || c == '#' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
this._sectionStart = this._i;
this._state = this.StyleName;
} else if (c == '/' && this.data[this._i + 1] == '*')
this.Comment();
else if (!isBlankChar(c) && c != ';')
this._state = this.Ignore;
};
Comment() {
this._i = this.data.indexOf("*/", this._i);
if (this._i == -1) this._i = this.data.length;
this._i++;
this._state = this.Space;
};
Ignore(c) {
if (c == '{') this._floor++;
else if (c == '}' && --this._floor <= 0) {
this._list = [];
this._state = this.Space;
}
};
StyleName(c) {
if (isBlankChar(c)) {
this._list.push(this.data.substring(this._sectionStart, this._i));
this._state = this.NameSpace;
} else if (c == '{') {
this._list.push(this.data.substring(this._sectionStart, this._i));
this._floor = 1;
this._sectionStart = this._i + 1;
this.Content();
} else if (c == ',') {
this._list.push(this.data.substring(this._sectionStart, this._i));
this._sectionStart = this._i + 1;
this._comma = true;
} else if (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') && !(c >= '0' && c <= '9') && c != '.' && c != '#' &&
c != '-' && c != '_')
this._state = this.Ignore;
};
NameSpace(c) {
if (c == '{') {
this._floor = 1;
this._sectionStart = this._i + 1;
this.Content();
} else if (c == ',') {
this._comma = true;
this._sectionStart = this._i + 1;
this._state = this.StyleName;
} else if (!isBlankChar(c)) {
if (this._comma) {
this._state = this.StyleName;
this._sectionStart = this._i;
this._i--;
this._comma = false;
} else this._state = this.Ignore;
}
};
Content() {
this._i = this.data.indexOf('}', this._i);
if (this._i == -1) this._i = this.data.length;
// 去除空白符
var flag = false,
pos, content = this.data.substring(this._sectionStart, this._i);
for (var i = 0; i < content.length; i++) {
if (isBlankChar(content[i])) {
if (!flag) {
pos = i;
flag = true;
}
} else {
if (flag) {
if (pos == 0) content = content.substring(i);
else if (i - pos > 1) content = content.substring(0, pos) + (content[pos - 1] == ';' ? (pos--, '') : ' ') +
content.substring(i);
i = pos;
flag = false;
}
}
}
if (flag) content = content.substring(0, pos);
for (var i = 0; i < this._list.length; i++)
this.res[this._list[i]] = (this.res[this._list[i]] || '') + content;
this._list = [];
this._state = this.Space;
}
}

View File

@@ -0,0 +1,430 @@
/*
将 html 解析为适用于小程序 rich-text 的 DOM 结构
github地址https://github.com/jin-yufeng/Parser
文档地址https://jin-yufeng.github.io/Parser
authorJinYufeng
*/
const CssHandler = require("./CssHandler.js");
const config = require("./config.js");
var emoji; // 需要使用 emoji 补丁包时将此行改为 const emoji = require("./emoji.js");
function isBlankChar(c) {
return c == ' ' || c == '\u00A0' || c == '\t' || c == '\r' || c == '\n' || c == '\f';
};
class MpHtmlParser {
constructor(data, options = {}, cb) {
this.cb = cb;
this.CssHandler = new CssHandler(options.tagStyle);
this.data = data;
this.DOM = [];
// #ifdef MP-BAIDU || MP-TOUTIAO
this._imgMode = options.imgMode;
// #endif
this._attrName = '';
this._attrValue = '';
this._attrs = {};
this._domain = options.domain;
this._protocol = options.domain ? (options.domain.includes("://") ? this._domain.split("://")[0] : "http") :
undefined;
this._i = 0;
this._sectionStart = 0;
this._state = this.Text;
this._STACK = [];
this._tagName = '';
this._audioNum = 0;
this._imgNum = 0;
this._videoNum = 0;
this._useAnchor = options.useAnchor;
this._whiteSpace = false;
};
parse() {
if (this.CssHandler) this.data = this.CssHandler.getStyle(this.data);
if (emoji) this.data = emoji.parseEmoji(this.data);
// 高亮处理
if (config.highlight)
this.data = this.data.replace(/<[pP][rR][eE]([\s\S]*?)>([\s\S]*?)<\/[pP][rR][eE][\s\S]*?>/g, function() {
return "<pre" + arguments[1] + '>' + config.highlight(arguments[2], "<pre" + arguments[1] + '>') + "</pre>";
})
for (var len = this.data.length; this._i < len; this._i++)
this._state(this.data[this._i]);
if (this._state == this.Text) this.setText();
while (this._STACK.length)
this.popNode(this._STACK.pop());
// #ifdef MP-BAIDU || MP-TOUTIAO
const inlineTags = config.makeMap("abbr,b,big,code,del,em,font,i,ins,label,mark,q,s,small,span,strong,sub,sup,u")
// 将顶层标签的一些样式提取出来给 rich-text
const setContain = function(nodes) {
for (var element of nodes) {
if (element.type == "text")
continue;
if (!element.c) {
var res = "";
var style = element.attrs.style;
var reg = /float[^;]+(?![\s\S]*?float)/i;
if (reg.test(style)) res += reg.exec(style)[0];
reg = /margin[^;]+/gi;
if (reg.test(style)) res += (';' + style.match(reg).join(';'));
reg = /display\s*:\s*([^;]*)(?![\s\S]*?display)/i;
if (reg.test(style) && reg.exec(style)[1] != "flex") res += (';' + reg.exec(style)[0]);
else if (inlineTags[element.name]) res += ";display:inline";
else res += (";display:" + (element.name == 'img' ? 'inline-block' : 'block'));
reg = /flex[^;]*:[^;]+/gi;
if (reg.test(style)) res += (';' + style.match(reg).join(';'));
reg = /[^;\s]*width[^;]+/gi;
if (reg.test(style)) res += (';' + style.match(reg).join(';'));
element.attrs.containStyle = res;
if (/[^-]width[^pev;]+/.test(";" + style))
element.attrs.style += ";width:100%";
let addMargin = "";
if (/margin\s*:/.test(style)) addMargin = ';margin:0';
else if (/margin-top/.test(style)) addMargin = ';margin-top:0';
else if (/margin-bottom/.test(style)) addMargin = ';margin-bottom:0';
element.attrs.style = (element.attrs.style || '').replace(/margin[^;]*/gi, "");
element.attrs.style += addMargin;
} else setContain(element.children);
}
};
setContain(this.DOM);
// #endif
if (this.DOM.length) this.DOM[0].PoweredBy = "Parser";
// console.log(this.DOM)
if (this.cb)
this.cb(this.DOM)
else return this.DOM;
};
// 设置属性
setAttr() {
if (config.trustAttrs[this._attrName])
this._attrs[this._attrName] = (this._attrValue ? this._attrValue : (this._attrName == "src" ? "" : "true"));
this._attrValue = '';
while (isBlankChar(this.data[this._i])) this._i++;
if (this.checkClose()) this.setNode();
else this._state = this.AttrName;
};
// 设置文本节点
setText() {
var text = this.getSelection();
if (text) {
if (!this._whiteSpace) {
// 移除空白符
var flag = false,
has = false,
pos;
for (var i = 0; i < text.length; i++) {
if (isBlankChar(text[i])) {
if (!flag) {
pos = i;
flag = true;
}
} else {
has = true;
if (flag) {
if (i - pos > 1) text = text.substring(0, pos) + ' ' + text.substring(i);
i = pos;
flag = false;
}
}
}
if (flag) text = text.substring(0, pos) + ' ';
if (!text || !has) return;
}
// 检查实体
// #ifdef MP-BAIDU || MP-ALIPAY || MP-TOUTIAO
var entities = {
lt: "<",
gt: ">",
amp: "&",
quot: '"',
apos: "'",
nbsp: "\u00A0",
ensp: "\u2002",
emsp: "\u2003",
ndash: "",
mdash: "—",
middot: "·",
lsquo: "",
rsquo: "",
ldquo: "“",
rdquo: "”",
bull: "•",
hellip: "…",
permil: "‰",
copy: "©",
reg: "®",
trade: "™",
times: "×",
divide: "÷",
cent: "¢",
pound: "£",
yen: "¥",
euro: "€",
sect: "§"
};
// #endif
var i = text.indexOf('&'),
j, decode;
while (i != -1 && i < text.length) {
j = text.indexOf(';', i);
if (j - i >= 2 && j - i <= 7) {
var entity = text.substring(i + 1, j);
// #ifdef MP-WEIXIN || MP-QQ || APP-PLUS
if (!entity.includes("sp") && !entity.includes("lt") && !entity.includes("gt")) {
decode = true
break;
}
// #endif
// #ifdef MP-BAIDU || MP-ALIPAY || MP-TOUTIAO
if (entities[entity]) text = text.substring(0, i) + entities[entity] + text.substring(j + 1);
// #endif
}
i = text.indexOf('&', i + 1);
}
var slibings = this._STACK.length ? this._STACK[this._STACK.length - 1].children : this.DOM;
if (slibings.length && slibings[slibings.length - 1].type == "text") {
slibings[slibings.length - 1].text += text;
if (decode) slibings[slibings.length - 1].decode = true;
} else
slibings.push({
type: "text",
text,
decode
})
}
};
// 设置元素节点
setNode() {
var slibings = this._STACK.length ? this._STACK[this._STACK.length - 1].children : this.DOM;
var node = {
name: this._tagName.toLowerCase(),
attrs: this._attrs
}
config.LabelAttrsHandler(node, this);
this._attrs = {};
if (this.data[this._i] == '>') {
if (!config.selfClosingTags[this._tagName]) {
if (config.ignoreTags[node.name]) {
var j = this._i;
// 处理要被移除的标签
while (this._i < this.data.length) {
this._i = this.data.indexOf("</", this._i);
if (this._i == -1) return this._i = this.data.length;
this._i += 2;
this._sectionStart = this._i;
while (!isBlankChar(this.data[this._i]) && this.data[this._i] != '>' && this.data[this._i] != '/') this._i++;
if (this.data.substring(this._sectionStart, this._i).toLowerCase() == node.name) {
this._i = this.data.indexOf('>', this._i);
if (this._i == -1) this._i = this.data.length;
else this._sectionStart = this._i + 1;
this._state = this.Text;
// 处理svg
if (node.name == "svg") {
var src = this.data.substring(j, this._i + 1);
if (!node.attrs.xmlns) src = " xmlns=\"http://www.w3.org/2000/svg\"" + src;
this._i = j;
while (this.data[j] != '<') j--;
src = this.data.substring(j, this._i) + src;
this._i = this._sectionStart - 1;
node.name = "img";
node.attrs = {
src: "data:image/svg+xml;utf8," + src.replace(/#/g, "%23"),
ignore: "true"
}
slibings.push(node);
}
break;
}
}
return;
} else this._STACK.push(node);
node.children = [];
}
} else this._i++;
this._sectionStart = this._i + 1;
this._state = this.Text;
if (!config.ignoreTags[node.name]) {
// 检查空白符是否有效
if (node.name == "pre" || (node.attrs.style && node.attrs.style.toLowerCase().includes("white-space") && node.attrs
.style.toLowerCase().includes("pre"))) {
this._whiteSpace = true;
node.pre = true;
}
slibings.push(node);
}
};
// 标签出栈处理
popNode(node) {
// 替换一些标签名
if (config.blockTags[node.name]) node.name = 'div';
else if (!config.trustTags[node.name]) node.name = 'span';
// 空白符处理
if (node.pre) {
this._whiteSpace = false;
node.pre = undefined;
for (var i = 0; i < this._STACK.length; i++)
if (this._STACK[i].pre)
this._whiteSpace = true;
}
// 处理表格的边框
if (node.name == 'table') {
if (node.attrs.border)
node.attrs.style = "border:" + node.attrs.border + "px solid gray;" + (node.attrs.style || '');
if (node.attrs.hasOwnProperty("cellspacing"))
node.attrs.style = "border-spacing:" + node.attrs.cellspacing + "px;" + (node.attrs.style || '');
function setBorder(elem) {
if (elem.name == 'th' || elem.name == 'td') {
if (node.attrs.border)
elem.attrs.style = "border:" + node.attrs.border + "px solid gray;" + (elem.attrs.style || '');
if (node.attrs.hasOwnProperty("cellpadding"))
elem.attrs.style = "padding:" + node.attrs.cellpadding + "px;" + (elem.attrs.style || '');
return;
}
if (elem.type == 'text') return;
for (var i = 0; i < elem.children.length; i++)
setBorder(elem.children[i]);
}
if (node.attrs.border || node.attrs.hasOwnProperty("cellpadding"))
for (var i = 0; i < node.children.length; i++)
setBorder(node.children[i]);
}
// 合并一些不必要的层,减小节点深度
if (node.children.length == 1 && node.name == "div" && node.children[0].name == "div") {
var child = node.children[0];
node.attrs.style = node.attrs.style || '';
child.attrs.style = child.attrs.style || '';
if (node.attrs.style.includes("padding") && (node.attrs.style.includes("margin") || child.attrs.style.includes(
"margin")) && node.attrs.style.includes("display") && child.attrs.style.includes("display") && !(node.attrs.id &&
node.attrs.id) && !(node.attrs.class && child.attrs.class)) {
if (child.attrs.style.includes("padding"))
child.attrs.style = "box-sizing:border-box;" + child.attrs.style;
node.attrs.style = node.attrs.style + ";" + child.attrs.style;
node.attrs.id = (child.attrs.id || '') + (node.attrs.id || '');
node.attrs.class = (child.attrs.class || '') + (node.attrs.class || '');
node.children = child.children;
}
}
// 多层样式处理
if (this.CssHandler.pop)
this.CssHandler.pop(node);
};
// 工具函数
checkClose() {
if (this.data[this._i] == '>' || (this.data[this._i] == '/' && this.data[this._i + 1] == '>'))
return true;
return false;
};
getSelection(trim) {
var str = (this._sectionStart == this._i ? '' : this.data.substring(this._sectionStart, this._i));
while (trim && isBlankChar(this.data[++this._i]));
if (trim) this._i--;
this._sectionStart = this._i + 1;
return str;
};
// 状态机
Text(c) {
if (c == '<') {
var next = this.data[this._i + 1];
if ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z')) {
this.setText();
this._state = this.TagName;
} else if (next == '/') {
this.setText();
this._i++;
next = this.data[this._i + 1];
if ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z')) {
this._sectionStart = this._i + 1;
this._state = this.EndTag;
} else
this._state = this.Comment;
} else if (next == '!') {
this.setText();
this._state = this.Comment;
}
}
};
Comment() {
if (this.data.substring(this._i + 1, this._i + 3) == "--" || this.data.substring(this._i + 1, this._i + 7) ==
"[CDATA[") {
this._i = this.data.indexOf("-->", this._i + 1);
if (this._i == -1) return this._i = this.data.length;
else this._i = this._i + 2;
} else {
this._i = this.data.indexOf(">", this._i + 1);
if (this._i == -1) return this._i = this.data.length;
}
this._sectionStart = this._i + 1;
this._state = this.Text;
};
TagName(c) {
if (isBlankChar(c)) {
this._tagName = this.getSelection(true);
if (this.checkClose()) this.setNode();
else this._state = this.AttrName;
} else if (this.checkClose()) {
this._tagName = this.getSelection();
this.setNode();
}
};
AttrName(c) {
if (isBlankChar(c)) {
this._attrName = this.getSelection(true).toLowerCase();
if (this.data[this._i] == '=') {
while (isBlankChar(this.data[++this._i]));
this._sectionStart = this._i;
this._i--;
this._state = this.AttrValue;
} else this.setAttr();
} else if (c == '=') {
this._attrName = this.getSelection().toLowerCase();
while (isBlankChar(this.data[++this._i]));
this._sectionStart = this._i;
this._i--;
this._state = this.AttrValue;
} else if (this.checkClose()) {
this._attrName = this.getSelection().toLowerCase();
this.setAttr();
}
};
AttrValue(c) {
if (c == '"' || c == "'") {
this._sectionStart++;
if ((this._i = this.data.indexOf(c, this._i + 1)) == -1) return this._i = this.data.length;
} else
for (; !isBlankChar(this.data[this._i] && this.data[this._i] != '/' && this.data[this._i] != '>'); this._i++);
this._attrValue = this.getSelection();
while (this._attrValue.includes("&quot;")) this._attrValue = this._attrValue.replace("&quot;", '');
this.setAttr();
};
EndTag(c) {
if (isBlankChar(c) || c == '>' || c == '/') {
var name = this.getSelection().toLowerCase();
var flag = false;
for (var i = this._STACK.length - 1; i >= 0; i--)
if (this._STACK[i].name == name) {
flag = true;
break;
}
if (flag) {
var node;
while (flag) {
node = this._STACK.pop();
if (node.name == name) flag = false;
this.popNode(node);
}
} else if (name == 'p' || name == "br") {
var slibings = this._STACK.length ? this._STACK[this._STACK.length - 1].children : this.DOM;
var node = {
name
}
slibings.push(node);
}
this._i = this.data.indexOf('>', this._i);
if (this._i == -1) this._i = this.data.length;
else this._state = this.Text;
}
};
};
module.exports = {
parseHtml: (data, options) => new Promise((resolve) => new MpHtmlParser(data, options, resolve).parse()),
parseHtmlSync: (data, options) => new MpHtmlParser(data, options).parse()
};

View File

@@ -0,0 +1,247 @@
// <<<<<<< HEAD
/* 配置文件 */
// =======
/* 配置文件 xxx*/
// >>>>>>> 53c74b30a29ef2e569158438db4f5df4b7480643
function makeMap(str) {
var map = Object.create(null),
list = str.split(',');
for (var item of list)
map[item] = true;
return map;
}
// 信任的属性列表,不在列表中的属性将被移除
const trustAttrs = makeMap(
"align,alt,app-id,appId,"
// #ifdef MP-BAIDU
+
"appid,apid,"
// #endif
+
"author,autoplay,border,cellpadding,cellspacing,class,color,colspan,controls,data-src,dir,face,height,href,id,ignore,loop,muted,name,path,poster,rowspan,size,span,src,start,style,type,lbType,lbtype,"
// #ifdef MP-WEIXIN || MP-QQ
+
"unit-id,unitId,"
// #endif
+
"width,xmlns"
);
// 信任的标签,将保持标签名不变
const trustTags = makeMap(
"a,abbr,ad,audio,b,blockquote,br,code,col,colgroup,dd,del,dl,dt,div,em,fieldset,h1,h2,h3,h4,h5,h6,hr,i,img,ins,label,legend,li,ol,p,q,source,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,title,u,ul,video,iframe"
);
// 块级标签,将被转为 div
const blockTags = makeMap("address,article,aside,body,center,cite,footer,header,html,nav,pre,section");
// 被移除的标签(其中 svg 系列标签会被转为图片)
const ignoreTags = makeMap(
"area,base,basefont,canvas,circle,command,ellipse,embed,frame,head,input,isindex,keygen,line,link,map,meta,param,path,polygon,rect,script,source,svg,textarea,track,use,wbr,"
);
// 只能用 rich-text 显示的标签(其中图片不能预览、不能显示视频、音频等)
const richOnlyTags = makeMap(
"a,ad,audio,colgroup,fieldset,legend,li,ol,sub,sup,table,tbody,td,tfoot,th,thead,tr,ul,video,iframe,");
// 自闭合标签
const selfClosingTags = makeMap(
"area,base,basefont,br,col,circle,ellipse,embed,frame,hr,img,input,isindex,keygen,line,link,meta,param,path,polygon,rect,source,track,use,wbr,"
);
// 默认的标签样式
var userAgentStyles = {
a: "color:#366092;word-break:break-all;padding:1.5px 0 1.5px 0",
address: "font-style:italic",
blockquote: "background-color:#f6f6f6;border-left:3px solid #dbdbdb;color:#6c6c6c;padding:5px 0 5px 10px",
center: "text-align:center",
cite: "font-style:italic",
code: "padding:0 1px 0 1px;margin-left:2px;margin-right:2px;background-color:#f8f8f8;border-radius:3px",
dd: "margin-left:40px",
img: "max-width:100%",
mark: "background-color:yellow",
pre: "font-family:monospace;white-space:pre;overflow:scroll",
s: "text-decoration:line-through",
u: "text-decoration:underline"
};
// #ifndef MP-ALIPAY || H5
const SDKVersion = uni.getSystemInfoSync().SDKVersion;
function versionHigherThan(version = '') {
var v1 = SDKVersion.split('.'),
v2 = version.split('.');
while (v1.length != v2.length)
v1.length < v2.length ? v1.push('0') : v2.push('0');
for (var i = 0; i < v1.length; i++) {
if (v1[i] == v2[i]) continue;
if (parseInt(v1[i]) > parseInt(v2[i])) return true;
return false;
}
return true;
};
// #endif
// #ifdef MP-WEIXIN || MP-QQ
// 版本兼容
if (versionHigherThan("2.7.1")) {
trustTags.bdi = true;
trustTags.bdo = true;
trustTags.caption = true;
trustTags.rt = true;
trustTags.ruby = true;
ignoreTags.rp = true;
trustTags.big = true;
trustTags.small = true;
trustTags.pre = true;
trustTags.iframe = true;
richOnlyTags.bdi = true;
richOnlyTags.bdo = true;
richOnlyTags.caption = true;
richOnlyTags.rt = true;
richOnlyTags.ruby = true;
richOnlyTags.pre = true;
blockTags.pre = undefined;
} else {
blockTags.caption = true;
userAgentStyles.big = "display:inline;font-size:1.2em";
userAgentStyles.small = "display:inline;font-size:0.8em";
}
// #endif
function bubbling(Parser) {
for (var i = Parser._STACK.length - 1; i >= 0; i--) {
if (!richOnlyTags[Parser._STACK[i].name])
Parser._STACK[i].c = 1;
else return false;
}
return true;
}
module.exports = {
// 高亮处理函数
highlight: null,
// 处理标签的属性,需要通过组件递归方式显示的标签需要调用 bubbling(Parser)
LabelAttrsHandler(node, Parser) {
node.attrs.style = Parser.CssHandler.match(node.name, node.attrs, node) + (node.attrs.style || '');
switch (node.name) {
case "ul":
case "ol":
case "li":
case "dd":
case "dl":
case "dt":
case "div":
case "span":
case "em":
case 'p':
let default_p_style = "max-width: 100% !important;display:block;"
if (node.attrs.style) {
node.attrs.style = node.attrs.style.includes('width:') ? default_p_style : node.attrs.style + default_p_style
}
if (node.attrs.align) {
node.attrs.style = "text-align:" + node.attrs.align + ';' + node.attrs.style;
node.attrs.align = undefined;
}
break;
case "img":
if (node.attrs.height) {
node.attrs.height = 'auto'
}
let default_style = "max-width: 100% !important;display:block;"
if (node.attrs.style) {
node.attrs.style = node.attrs.style.includes('height:') ? default_style : node.attrs.style + default_style
}
if (node.attrs["data-src"]) {
node.attrs.src = node.attrs.src || node.attrs["data-src"];
node.attrs["data-src"] = undefined;
}
// #ifdef MP-BAIDU || MP-TOUTIAO
if (Parser._imgMode == "widthFix") node.attrs.style = node.attrs.style + ";height:auto !important;";
// #endif
if (node.attrs.src) {
if (!node.attrs.ignore) {
if (bubbling(Parser)) node.attrs.i = (Parser._imgNum++).toString();
else node.attrs.ignore = "true";
}
if (Parser._domain && node.attrs.src[0] == '/') {
if (node.attrs.src[1] == '/') node.attrs.src = Parser._protocol + ":" + node.attrs.src;
else node.attrs.src = Parser._domain + node.attrs.src;
}
}
break;
case 'a':
case "ad":
bubbling(Parser);
break;
case "font":
if (node.attrs.color) {
node.attrs.style = "color:" + node.attrs.color + ';' + node.attrs.style;
node.attrs.color = undefined;
}
if (node.attrs.face) {
node.attrs.style = "font-family:" + node.attrs.face + ';' + node.attrs.style;
node.attrs.face = undefined;
}
if (node.attrs.size) {
var size = parseInt(node.attrs.size);
if (size < 1) size = 1;
else if (size > 7) size = 7;
var map = ["xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large"];
node.attrs.style = "font-size:" + map[size - 1] + ';' + node.attrs.style;
node.attrs.size = undefined;
}
break;
case 'iframe':
case "video":
case "audio":
node.attrs.loop = node.attrs.hasOwnProperty('loop') || false;
node.attrs.controls = node.attrs.hasOwnProperty(
'controls') || true;
node.attrs.autoplay = node.attrs.hasOwnProperty('autoplay') || false;
if (node.attrs.id) Parser['_' + node.name + "Num"]++;
else node.attrs.id = (node.name + (++Parser['_' + node.name + "Num"]));
if (node.name == "video") {
node.attrs.style = node.attrs.style || '';
if (node.attrs.width) {
node.attrs.style = "width:" + parseFloat(node.attrs.width) + (node.attrs.height.includes('%') ? '%' : "px") +
';' + node.attrs.style;
node.attrs.width = undefined;
}
if (node.attrs.height) {
node.attrs.style = "height:" + parseFloat(node.attrs.height) + (node.attrs.height.includes('%') ? '%' : "px") +
';' + node.attrs.style;
node.attrs.height = undefined;
}
if (Parser._videoNum > 3) node.lazyLoad = true;
}
// 新增iframe【create_by_xx】
if (node.name == 'iframe') {
// console.log(node.attrs, "====iframe attrs");
}
node.attrs.source = [];
if (node.attrs.src) node.attrs.source.push(node.attrs.src);
if (!node.attrs.controls && !node.attrs.autoplay)
console.warn("存在没有controls属性的 " + node.name + " 标签,可能导致无法播放", node);
bubbling(Parser);
break;
case "source":
var parent = Parser._STACK[Parser._STACK.length - 1];
if (parent && (parent.name == "video" || parent.name == "audio")) {
parent.attrs.source.push(node.attrs.src);
if (!parent.attrs.src) parent.attrs.src = node.attrs.src;
}
break;
}
if (Parser._domain && node.attrs.style.includes("url"))
node.attrs.style = node.attrs.style.replace(/url\s*\(['"\s]*(\S*?)['"\s]*\)/, function() {
var src = arguments[1];
if (src && src[0] == '/') {
if (src[1] == '/') return "url(" + Parser._protocol + ':' + src + ')';
else return "url(" + Parser._domain + src + ')';
} else return arguments[0];
})
if (!node.attrs.style) node.attrs.style = undefined;
if (Parser._useAnchor && node.attrs.id) bubbling(Parser);
},
trustAttrs,
trustTags,
blockTags,
ignoreTags,
selfClosingTags,
userAgentStyles,
// #ifndef MP-ALIPAY || H5
versionHigherThan,
// #endif
makeMap
}

View File

@@ -0,0 +1,392 @@
<!--
trees 递归显示组件
github地址https://github.com/jin-yufeng/Parser
文档地址https://jin-yufeng.github.io/Parser
插件市场https://ext.dcloud.net.cn/plugin?id=805
authorJinYufeng
-->
<template>
<view style="display: inherit; white-space: inherit;">
<block v-for="(item, index) in nodes" v-bind:key="index">
<!--#ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY || APP-PLUS-->
<block v-if="handler.notContinue(item)">
<!--#endif-->
<!--#ifdef MP-BAIDU || MP-TOUTIAO-->
<block v-if="!item.c">
<!--#endif-->
<!--图片-->
<!--#ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY || APP-PLUS-->
<rich-text v-if="item.name=='img'" :id="item.attrs.id" class="img" :style="'text-indent:0;'+handler.getStyle(item.attrs.style, 'inline-block')"
:nodes='handler.setImgStyle(item, imgMode, imgLoad)' :data-attrs="item.attrs" @tap='previewEvent' />
<!--#endif-->
<!--#ifdef MP-BAIDU || MP-TOUTIAO-->
<rich-text v-if="item.name=='img'" :id="item.attrs.id" :style="'text-indent:0;'+item.attrs.containStyle" :nodes='[item]'
:data-attrs="item.attrs" @tap='previewEvent' />
<!--#endif-->
<!--文本-->
<!--#ifdef MP-WEIXIN || MP-QQ || APP-PLUS-->
<block v-else-if="item.type=='text'">
<text v-if="!item.decode" decode>{{item.text}}</text>
<rich-text v-else style="display:inline-block" :nodes="[item]"></rich-text>
</block>
<!--#endif-->
<!--#ifdef MP-ALIPAY-->
<text v-else-if="item.type=='text'" decode>{{item.text}}</text>
<!--#endif-->
<text v-else-if="item.name=='br'">\n</text>
<!--视频-->
<block v-else-if="item.name=='video'">
<!--#ifdef APP-PLUS-->
<view v-if="(!loadVideo||item.lazyLoad)&&!controls[item.attrs.id].play" :id="item.attrs.id" :class="'_video '+(item.attrs.class||'')"
:style="item.attrs.style||''" @tap="_loadVideo" />
<!--#endif-->
<!--#ifndef APP-PLUS-->
<view v-if="item.lazyLoad&&!controls[item.attrs.id].play" :id="item.attrs.id" :class="'_video '+(item.attrs.class||'')"
:style="item.attrs.style||''" @tap="_loadVideo" />
<!--#endif-->
<video v-else :src="controls[item.attrs.id]?item.attrs.source[controls[item.attrs.id].index] : item.attrs.src"
:id="item.attrs.id" :loop="item.attrs.loop" :controls="item.attrs.controls" :autoplay="item.attrs.autoplay||(controls[item.attrs.id]&&controls[item.attrs.id].play)"
:unit-id="item.attrs['unit-id']" :class="item.attrs.class" :muted="item.attrs.muted" :style="item.attrs.style||''"
:data-source="item.attrs.source" @play="playEvent" @error="videoError" />
</block>
<!-- iframe create_by_xx-->
<block v-else-if="item.name=='iframe'">
<!-- #ifdef MP-WEIXIN -->
<!-- 腾讯视频 -->
<txv-video :vid="item.attrs.src" :playerid="item.attrs.src" width="100%" height="100%" :controls="true" :autoplay="false"
:isHiddenStop="true" v-if="item.attrs.lbType=='vid' || item.attrs.lbtype=='vid'">
</txv-video>
<!-- #endif -->
</block>
<!--音频-->
<audio v-else-if="item.name=='audio'" :src="controls[item.attrs.id]?item.attrs.source[controls[item.attrs.id].index] : item.attrs.src"
:id="item.attrs.id" :loop="item.attrs.loop" :controls="item.attrs.controls" :poster="item.attrs.poster" :name="item.attrs.name"
:author="item.attrs.author" :class="item.attrs.class" :style="item.attrs.style||''" :data-source="item.attrs.source"
@error="audioError" />
<!--链接-->
<view v-else-if="item.name=='a'" :class="'_a '+(item.attrs.class||'')" :style="item.attrs.style||''" :data-attrs="item.attrs"
hover-class="navigator-hover" :hover-start-time="25" :hover-stay-time="300" @tap="tapEvent">
<trees :nodes="item.children" :imgMode="imgMode" />
</view>
<!--广告-->
<!--#ifdef MP-WEIXIN || MP-QQ-->
<ad v-else-if="item.name=='ad'" :unit-id="item.attrs['unit-id']" :class="item.attrs.class||''" :style="item.attrs.style||''"
@error="adError"></ad>
<!--#endif-->
<!--#ifdef MP-BAIDU-->
<ad v-else-if="item.name=='ad'" :appid="item.attrs.appid" :apid="item.attrs.apid" :type="item.attrs.type" :class="item.attrs.class||''"
:style="item.attrs.style" @error="adError"></ad>
<!--#endif-->
<!--富文本-->
<!--#ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY || APP-PLUS-->
<rich-text v-else :id="item.attrs.id" :class="'__'+item.name" :style="''+handler.getStyle(item.attrs.style, 'block')"
:nodes="handler.setStyle(item)" />
<!--#endif-->
<!--#ifdef MP-BAIDU || MP-TOUTIAO-->
<rich-text v-else :id="item.attrs.id" :style="item.attrs?item.attrs.containStyle:''" :nodes="[item]" />
<!--#endif-->
</block>
<!--#ifdef MP-ALIPAY-->
<view v-else :id="item.attrs.id" :class="'_'+item.name+' '+(item.attrs.class||'')" :style="item.attrs.style||''">
<trees :nodes="item.children" :imgMode="imgMode" />
</view>
<!--#endif-->
<!--#ifndef MP-ALIPAY-->
<trees v-else :class="item.attrs.id+' _'+item.name+' '+(item.attrs.class||'')" :style="item.attrs.style||''" :nodes="item.children"
:imgMode="imgMode" :loadVideo="loadVideo" />
<!--#endif-->
</block>
</view>
</template>
<script module="handler" lang="wxs" src="./handler.wxs"></script>
<script module="handler" lang="sjs" src="./handler.sjs"></script>
<script>
import trees from "./trees"
export default {
components: {
trees
},
name: 'trees',
data() {
return {
controls: {},
// #ifdef MP-WEIXIN || MP-QQ || APP-PLUS
imgLoad: true
// #endif
}
},
props: {
nodes: {
type: Array,
default: []
},
// #ifdef APP-PLUS
loadVideo: {
type: Boolean,
default: false
},
// #endif
imgMode: {
type: String,
default: "default"
}
},
mounted() {
// 获取顶层组件
this._top = this.$parent;
while (this._top.$options.name != 'parser') {
if (this._top._top) {
this._top = this._top._top;
break;
}
this._top = this._top.$parent;
}
},
// #ifdef MP-WEIXIN || MP-QQ || APP-PLUS
beforeDestroy() {
if (this._observer)
this._observer.disconnect();
},
// #endif
methods: {
// #ifndef MP-ALIPAY
playEvent(e) {
if ((this._top.videoContexts || []).length > 1 && this._top.autopause) {
for (var video of this._top.videoContexts) {
if (video.id == e.currentTarget.id) continue;
video.pause();
}
}
},
// #endif
previewEvent(e) {
var attrs = e.currentTarget.dataset.attrs;
if (!attrs.ignore) {
var preview = true;
this._top.$emit('imgtap', {
id: e.currentTarget.id,
src: attrs.src,
ignore: () => preview = false
})
if (preview && this._top.autopreview) {
var urls = this._top.imgList || [],
current = urls[attrs.i] ? attrs.i : (urls = [attrs.src], 0);
uni.previewImage({
current,
urls
})
}
}
},
tapEvent(e) {
var jump = true,
attrs = e.currentTarget.dataset.attrs;
attrs.ignore = () => jump = false;
this._top.$emit('linkpress', attrs);
if (jump) {
// #ifdef MP
if (attrs['app-id'] || attrs.appId) {
return uni.navigateToMiniProgram({
appId: attrs['app-id'] || attrs.appId,
path: attrs.path || ''
})
}
// #endif
if (attrs.href) {
if (attrs.href[0] == "#") {
if (this._top.useAnchor)
this._top.navigateTo({
id: attrs.href.substring(1)
})
} else if (attrs.href.indexOf("http") == 0 || attrs.href.indexOf("//") == 0) {
if (this._top.autocopy) {
uni.setClipboardData({
data: attrs.href,
success() {
uni.showToast({
title: '链接已复制'
});
}
});
}
} else
uni.navigateTo({
url: attrs.href
})
}
}
},
triggerError(source, target, errMsg, errCode, context) {
this._top.$emit('error', {
source,
target,
errMsg,
errCode,
context
});
},
loadSource(target) {
// console.log(target)
var index = (this.controls[target.id] ? this.controls[target.id].index : 0) + 1;
if (index < target.dataset.source.length) {
this.$set(this.controls[target.id], "index", index);
return true;
}
return false;
},
adError(e) {
this.triggerError("ad", e.currentTarget, "", e.detail.errorCode);
},
videoError(e) {
if (!this.loadSource(e.currentTarget) && this._top)
this.triggerError("video", e.currentTarget, e.detail.errMsg, undefined, uni.createVideoContext(e.currentTarget.id,
this));
},
audioError(e) {
if (!this.loadSource(e.currentTarget))
this.triggerError("audio", e.currentTarget, e.detail.errMsg);
},
_loadVideo(e) {
this.$set(this.controls, e.currentTarget.id, {
play: true,
index: 0
})
}
}
}
</script>
<style>
/* 可以在这里引入自定义的外部样式 */
/* 链接受到点击的hover-class可自定义修改 */
.navigator-hover {
opacity: 0.7;
text-decoration: underline;
}
/* 以下内容不建议修改 */
/* #ifndef MP-BAIDU */
:host {
display: inherit;
float: inherit;
}
/* #endif */
._b,
._strong {
font-weight: bold;
}
._big {
font-size: 1.2em;
}
._small {
font-size: 0.8em;
}
._blockquote,
._div,
._p {
display: block;
}
._code {
font-family: monospace;
}
._del {
text-decoration: line-through;
}
._em,
._i {
font-style: italic;
}
._h1 {
font-size: 2em;
}
._h2 {
font-size: 1.5em;
}
._h3 {
font-size: 1.17em;
}
._h5 {
font-size: 0.67em;
}
._h1,
._h2,
._h3,
._h4,
._h5,
._h6 {
font-weight: bold;
}
._ins {
text-decoration: underline;
}
._q::before {
content: '"';
}
._q::after {
content: '"';
}
._a,
._abbr,
._b,
._big,
._small,
._code,
._del,
._em,
._i,
._ins,
._label,
._q,
._span,
._strong {
display: inline;
}
/* #ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY */
.__sub,
.__sup,
.__bdo,
.__bdi,
.__ruby,
.__rt {
display: inline-block !important;
}
/* #endif */
._video {
background-color: black;
width: 300px;
height: 225px;
display: inline-block;
position: relative;
}
._video::after {
content: '';
border-width: 15px 0 15px 30px;
border-style: solid;
border-color: transparent transparent transparent white;
position: absolute;
left: 50%;
top: 50%;
margin: -15px 0 0 -15px;
}
</style>

View File

@@ -0,0 +1,131 @@
<template>
<view class='load-more'>
<block v-if="!loading&&noMore">
<view class="loadmore__line"></view>
<text class="loadmore__text">{{noMoreText}}</text>
<view class="loadmore__line"></view>
</block>
<block v-if="loading">
<view class="weui-loading"></view>
<text class="loadmore__text">{{loadText}}</text>
</block>
</view>
</template>
<script>
export default {
name: 'load-more',
props: {
loadText: {
type: String,
default () {
return '努力加载中'
}
},
noMoreText: {
type: String,
default () {
return '没有更多了'
}
},
noMore: {
type: Boolean,
default () {
return false
}
},
loading: {
type: Boolean,
default () {
return true
}
}
},
created() {
},
data() {
return {
}
},
methods: {
},
}
</script>
<style>
.load-more {
display: flex;
width: 100%;
height: 90rpx;
justify-content: center;
align-items: center;
}
.loading-icon {
width: 30rpx;
height: 30rpx;
margin-right: 8rpx;
display: inline-block;
vertical-align: middle;
animation: weuiLoading 1s steps(12, end) infinite;
background: url('https://s10.mogucdn.com/mlcdn/c45406/171016_4a61e09hcadd157gadhdeje55e82c_32x32.png') no-repeat;
-webkit-background-size: 100%;
background-size: 100%;
}
.loadmore__text {
margin: 0 8rpx 0 8rpx;
color: #333;
font-size: 28rpx;
}
.loadmore__line {
width: 100rpx;
height: 1rpx;
border-top: 1rpx solid #d2d2d2;
}
/* 加载中动画 */
.weui-loading {
margin: 0 5px;
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
-webkit-animation: a 1s steps(12) infinite;
animation: a 1s steps(12) infinite;
background: transparent url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAiIGhlaWdodD0iMTIwIiB2aWV3Qm94PSIwIDAgMTAwIDEwMCI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgxMDB2MTAwSDB6Ii8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTlFOUU5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgLTMwKSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iIzk4OTY5NyIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgzMCAxMDUuOTggNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjOUI5OTlBIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDYwIDc1Ljk4IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0EzQTFBMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSg5MCA2NSA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNBQkE5QUEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoMTIwIDU4LjY2IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0IyQjJCMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgxNTAgNTQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjQkFCOEI5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA1MCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDMkMwQzEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTE1MCA0NS45OCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDQkNCQ0IiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTEyMCA0MS4zNCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNEMkQyRDIiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTkwIDM1IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0RBREFEQSIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgtNjAgMjQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTJFMkUyIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKC0zMCAtNS45OCA2NSkiLz48L3N2Zz4=) no-repeat;
background-size: 100%
}
.weui-loading.weui-loading_transparent {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120' viewBox='0 0 100 100'%3E%3Cpath fill='none' d='M0 0h100v100H0z'/%3E%3Crect xmlns='http://www.w3.org/2000/svg' width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.56)' rx='5' ry='5' transform='translate(0 -30)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.5)' rx='5' ry='5' transform='rotate(30 105.98 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.43)' rx='5' ry='5' transform='rotate(60 75.98 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.38)' rx='5' ry='5' transform='rotate(90 65 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.32)' rx='5' ry='5' transform='rotate(120 58.66 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.28)' rx='5' ry='5' transform='rotate(150 54.02 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.25)' rx='5' ry='5' transform='rotate(180 50 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.2)' rx='5' ry='5' transform='rotate(-150 45.98 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.17)' rx='5' ry='5' transform='rotate(-120 41.34 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.14)' rx='5' ry='5' transform='rotate(-90 35 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.1)' rx='5' ry='5' transform='rotate(-60 24.02 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.03)' rx='5' ry='5' transform='rotate(-30 -5.98 65)'/%3E%3C/svg%3E")
}
@-webkit-keyframes a {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg)
}
to {
-webkit-transform: rotate(1turn);
transform: rotate(1turn)
}
}
@keyframes a {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg)
}
to {
-webkit-transform: rotate(1turn);
transform: rotate(1turn)
}
}
</style>

View File

@@ -0,0 +1,107 @@
<template>
<view>
<view class="login-info pd-lg flex-center rel" v-if="isShowLogin">
<view @tap.stop="toClose" class="login-close flex-center radius abs">
<i class="iconfont icon-add c-base"></i>
</view>
<image mode="aspectFill" lazy-load class="logo-img radius" :src="configInfo.app_logo"></image>
<view class="flex-center flex-1 ml-md">
<view class="flex-1">
<view class="f-title c-base max-380 ellipsis">{{`欢迎来到${configInfo.app_text}`}}</view>
<view class="text f-caption">登录后获取更多精彩内容</view>
</view>
<view @tap.stop="toLogin" class="login-btn flex-center f-desc c-base radius"
:style="{background:primaryColor}">去登录</view>
</view>
</view>
</view>
</template>
<script>
import {
mapState,
mapActions,
mapMutations
} from "vuex"
export default {
components: {},
props: {},
mounted() {
this.init();
},
data() {
return {
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
isShowLogin: state => state.user.isShowLogin
}),
methods: {
...mapActions(['getConfigInfo', 'getUserInfo']),
...mapMutations(['updateUserItem']),
async init() {
},
toClose() {
this.updateUserItem({
key: 'isShowLogin',
val: false
})
},
toLogin() {
let pages = getCurrentPages();
let {
route
} = pages[pages.length - 1]
this.updateUserItem({
key: 'loginPage',
val: `/${route}`
})
this.$util.goUrl({
url: `/pages/login?type=1`
})
}
},
}
</script>
<style scoped lang="scss">
.login-info {
position: fixed;
left: 20rpx;
bottom: 20rpx;
width: 710rpx;
height: 139rpx;
background: rgba(0, 0, 0, 0.8);
border-radius: 16rpx;
.logo-img {
width: 90rpx;
height: 90rpx;
}
.text {
color: #9E9E9E;
}
.login-btn {
width: 150rpx;
height: 54rpx;
}
.login-close {
top: -10rpx;
right: -10rpx;
width: 40rpx;
height: 40rpx;
background: #000;
.iconfont {
font-size: 24rpx;
transform: rotate(45deg);
}
}
}
</style>

View File

@@ -0,0 +1,121 @@
<template>
<view :class="['ui-mask ','center',effect,{'show':show}]" @tap="handleMaskTap" :style='{top:top,background:background}'>
<slot></slot>
</view>
</template>
<script>
export default {
name: 'mask',
props: {
background: {
type: String,
default: "rgba(0, 0, 0, 0.5)"
},
show: {
type: Boolean
},
top: {
type: String,
default () {
return '0'
}
},
effect: {
type: String
},
hideDelay: {
type: Number
},
hideOnTap: {
type: Number,
default () {
return 1
}
},
blur: {
type: String
}
},
created() {
},
data() {
return {
}
},
watch: {
show(newValue, oldValue) {
var _this = this;
if (newValue) {
this.selfShow = false
} else {
if (this.hideDelay) {
setTimeout(function() {
_this.selfShow = false
}, this.hideDelay);
} else {
_this.selfShow = false
}
}
}
},
methods: {
handleMaskTap(e) {
if (this.hideOnTap) {
this.show = false;
this.$emit('hide')
}
}
},
}
</script>
<style>
.ui-mask {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
visibility: hidden;
opacity: 0;
transition: all 0.25s ease-in;
-webkit-backface-visibility: hidden;
z-index: 0;
}
.center {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.ui-mask.scale-out {
transition: all 0.25s ease-in;
transform: scale(0);
}
.ui-mask.scale-out.show {
transform: scale(1);
}
.ui-mask.scale-in {
transition: all 0.25s ease-in;
transform: scale(1.5);
}
.ui-mask.scale-in.show {
transform: scale(1);
}
.ui-mask.show {
opacity: 1;
visibility: visible;
}
</style>

View File

@@ -0,0 +1,188 @@
<template>
<view class="min-countdown" :class="className" :style="{color:color}">
<rich-text :nodes="time" v-if="type == 1"></rich-text>
<!-- x天x时x分x秒 -->
<view class="flex-y-center" v-if="type == 2">
<view class="count-tag flex-center" :class="[{'haveColor':borderColor}]"
:style="{background:bgColor,border:borderColor?`1rpx solid ${borderColor}`:``,width:time.d*1>99?'54rpx':''}">
{{time.d}}
</view>
<view class="text" :style="{color:textColor}"></view>
<view class="count-tag flex-center" :class="[{'haveColor':borderColor}]"
:style="{background:bgColor,border:borderColor?`1rpx solid ${borderColor}`:``}">{{time.h}}</view>
<view class="text" :style="{color:textColor}"></view>
<view class="count-tag flex-center" :class="[{'haveColor':borderColor}]"
:style="{background:bgColor,border:borderColor?`1rpx solid ${borderColor}`:``}">
{{time.m}}
</view>
<view class="text" :style="{color:textColor}"></view>
<view class="count-tag flex-center" :class="[{'haveColor':borderColor}]"
:style="{background:bgColor,border:borderColor?`1rpx solid ${borderColor}`:``}">{{time.s}}</view>
<view class="text" :style="{color:textColor}"></view>
</view>
<!-- :: -->
<view class="flex-y-center" v-if="type == 3">
<view class="count-tag flex-center" :class="[{'haveColor':borderColor}]"
:style="{background:bgColor,border:borderColor?`1rpx solid ${borderColor}`:``,width:time.all_h*1>99?className=='mini'?'48rpx':className=='sm'?'54rpx':'66rpx':''}">
{{time.all_h}}
</view>
<view class="text" :style="{color:textColor}">:</view>
<view class="count-tag flex-center" :class="[{'haveColor':borderColor}]"
:style="{background:bgColor,border:borderColor?`1rpx solid ${borderColor}`:``}">
{{time.m}}
</view>
<view class="text" :style="{color:textColor}">:</view>
<view class="count-tag flex-center" :class="[{'haveColor':borderColor}]"
:style="{background:bgColor,border:borderColor?`1rpx solid ${borderColor}`:``}">
{{time.s}}
</view>
</view>
<view v-if="type==4">{{`${time.all_h}:${time.m}:${time.s}`}}</view>
<view v-if="type==5">{{`${time.all_h}小时${time.m}${time.s}`}}</view>
</view>
</template>
<script>
import {
mapState,
} from 'vuex';
export default {
name: 'min-countdown',
props: {
type: {
type: Number,
default: 1
},
targetTime: {
type: Number,
default: 0
},
format: {
type: String,
default: '{%d}天{%h}小时{%m}分{%s}秒'
},
color: {
type: String,
default: '#39b54a'
},
bgColor: {
type: String,
default: '#fff'
},
textColor: {
type: String,
default: '#39b54a'
},
borderColor: {
type: String,
default: ''
},
className: {
type: String,
default: ''
}
},
data() {
return {
time: '00:00:00'
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
}),
methods: {
init() {
setTimeout(() => {
this.getTime()
}, 1000)
},
getTime() {
let time = {}
let format = this.format
function formatNumber(num) {
return num > 9 ? `${num}` : `0${num}`
}
const gapTime = Math.ceil((this.targetTime - new Date().getTime()) / 1000)
if (gapTime >= 0) {
time.d = formatNumber(parseInt(gapTime / 86400))
let lastTime = gapTime % 86400;
time.h = formatNumber(parseInt(lastTime / 3600))
lastTime = lastTime % 3600;
time.all_h = time.d * 24 + time.h * 1
time.m = formatNumber(parseInt(lastTime / 60))
time.s = formatNumber(lastTime % 60);
['d', 'h', 'm', 's'].forEach(item => {
const day = time[item].split('');
format = format.replace('{%' + item + '}', time[item])
format = format.replace('{%' + item + '0}', day[0])
format = format.replace('{%' + item + '1}', day[1])
format = format.replace('{%' + item + '2}', day[2] ? day[2] : '0')
})
this.time = this.type == 1 ? format : time
this.init()
} else {
this.$emit('callback')
}
}
},
mounted() {
this.getTime()
}
}
</script>
<style lang="scss">
.min-countdown {
display: inline-flex;
justify-content: center;
align-items: center;
.count-tag {
width: 42rpx;
height: 42rpx;
margin: 0 10rpx;
padding: 0 10rpx;
border-radius: 4rpx;
}
.text {
color: #fff;
font-size: 20rpx;
}
}
.min-countdown.mini {
.count-tag {
width: 36rpx;
height: 29rpx;
border-radius: 2px;
}
}
.min-countdown.sm {
.count-tag {
width: 42rpx;
height: 42rpx;
}
}
.min-countdown.md {
.count-tag {
width: 54rpx;
height: 54rpx;
font-size: 32rpx;
}
.text {
font-size: 28rpx;
}
}
.haveColor {
transform: rotateZ(360deg);
}
</style>

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Arnout Kazemier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,92 @@
# EventEmitter3
[![Version npm](https://img.shields.io/npm/v/eventemitter3.svg?style=flat-square)](https://www.npmjs.com/package/eventemitter3)[![Build Status](https://img.shields.io/travis/primus/eventemitter3/master.svg?style=flat-square)](https://travis-ci.org/primus/eventemitter3)[![Dependencies](https://img.shields.io/david/primus/eventemitter3.svg?style=flat-square)](https://david-dm.org/primus/eventemitter3)[![Coverage Status](https://img.shields.io/coveralls/primus/eventemitter3/master.svg?style=flat-square)](https://coveralls.io/r/primus/eventemitter3?branch=master)[![IRC channel](https://img.shields.io/badge/IRC-irc.freenode.net%23primus-00a8ff.svg?style=flat-square)](https://webchat.freenode.net/?channels=primus)
[![Sauce Test Status](https://saucelabs.com/browser-matrix/eventemitter3.svg)](https://saucelabs.com/u/eventemitter3)
EventEmitter3 is a high performance EventEmitter. It has been micro-optimized
for various of code paths making this, one of, if not the fastest EventEmitter
available for Node.js and browsers. The module is API compatible with the
EventEmitter that ships by default with Node.js but there are some slight
differences:
- Domain support has been removed.
- We do not `throw` an error when you emit an `error` event and nobody is
listening.
- The `newListener` and `removeListener` events have been removed as they
are useful only in some uncommon use-cases.
- The `setMaxListeners`, `getMaxListeners`, `prependListener` and
`prependOnceListener` methods are not available.
- Support for custom context for events so there is no need to use `fn.bind`.
- The `removeListener` method removes all matching listeners, not only the
first.
It's a drop in replacement for existing EventEmitters, but just faster. Free
performance, who wouldn't want that? The EventEmitter is written in EcmaScript 3
so it will work in the oldest browsers and node versions that you need to
support.
## Installation
```bash
$ npm install --save eventemitter3
```
## CDN
Recommended CDN:
```text
https://unpkg.com/eventemitter3@latest/umd/eventemitter3.min.js
```
## Usage
After installation the only thing you need to do is require the module:
```js
var EventEmitter = require('eventemitter3');
```
And you're ready to create your own EventEmitter instances. For the API
documentation, please follow the official Node.js documentation:
http://nodejs.org/api/events.html
### Contextual emits
We've upgraded the API of the `EventEmitter.on`, `EventEmitter.once` and
`EventEmitter.removeListener` to accept an extra argument which is the `context`
or `this` value that should be set for the emitted events. This means you no
longer have the overhead of an event that required `fn.bind` in order to get a
custom `this` value.
```js
var EE = new EventEmitter()
, context = { foo: 'bar' };
function emitted() {
console.log(this === context); // true
}
EE.once('event-name', emitted, context);
EE.on('another-event', emitted, context);
EE.removeListener('another-event', emitted, context);
```
### Tests and benchmarks
This module is well tested. You can run:
- `npm test` to run the tests under Node.js.
- `npm run test-browser` to run the tests in real browsers via Sauce Labs.
We also have a set of benchmarks to compare EventEmitter3 with some available
alternatives. To run the benchmarks run `npm run benchmark`.
Tests and benchmarks are not included in the npm package. If you want to play
with them you have to clone the GitHub repository.
## License
[MIT](LICENSE)

View File

@@ -0,0 +1,67 @@
type EventNames<T extends string | symbol | { [K in string | symbol]: any[] }> = T extends string | symbol ? T : keyof T;
type EventArgs<T extends string | symbol | { [K in string | symbol]: any[] }, K extends EventNames<T>> = T extends string | symbol ? any[] : K extends keyof T ? T[K] : never;
/**
* Minimal `EventEmitter` interface that is molded against the Node.js
* `EventEmitter` interface.
*/
declare class EventEmitter<EventTypes extends string | symbol | { [K in keyof EventTypes]: any[] } = string | symbol> {
static prefixed: string | boolean;
/**
* Return an array listing the events for which the emitter has registered
* listeners.
*/
eventNames(): Array<EventNames<EventTypes>>;
/**
* Return the listeners registered for a given event.
*/
listeners<T extends EventNames<EventTypes>>(event: T): Array<EventEmitter.ListenerFn<EventArgs<EventTypes, T>>>;
/**
* Return the number of listeners listening to a given event.
*/
listenerCount(event: EventNames<EventTypes>): number;
/**
* Calls each of the listeners registered for a given event.
*/
emit<T extends EventNames<EventTypes>>(event: T, ...args: EventArgs<EventTypes, T>): boolean;
/**
* Add a listener for a given event.
*/
on<T extends EventNames<EventTypes>>(event: T, fn: EventEmitter.ListenerFn<EventArgs<EventTypes, T>>, context?: any): this;
addListener<T extends EventNames<EventTypes>>(event: T, fn: EventEmitter.ListenerFn<EventArgs<EventTypes, T>>, context?: any): this;
/**
* Add a one-time listener for a given event.
*/
once<T extends EventNames<EventTypes>>(event: T, fn: EventEmitter.ListenerFn<EventArgs<EventTypes, T>>, context?: any): this;
/**
* Remove the listeners of a given event.
*/
removeListener<T extends EventNames<EventTypes>>(event: T, fn?: EventEmitter.ListenerFn<EventArgs<EventTypes, T>>, context?: any, once?: boolean): this;
off<T extends EventNames<EventTypes>>(event: T, fn?: EventEmitter.ListenerFn<EventArgs<EventTypes, T>>, context?: any, once?: boolean): this;
/**
* Remove all listeners, or those of the specified event.
*/
removeAllListeners(event?: EventNames<EventTypes>): this;
}
declare namespace EventEmitter {
export interface ListenerFn<Args extends any[] = any[]> {
(...args: Args): void;
}
export interface EventEmitterStatic {
new<EventTypes extends string | symbol | { [K in keyof EventTypes]: any[] } = string | symbol>(): EventEmitter<EventTypes>;
}
export const EventEmitter: EventEmitterStatic;
}
export = EventEmitter;

View File

@@ -0,0 +1,347 @@
'use strict';
var has = Object.prototype.hasOwnProperty,
prefix = '~';
/**
* Constructor to create a storage for our `EE` objects.
* An `Events` instance is a plain object whose properties are event names.
*
* @constructor
* @private
*/
function Events() {} //
// We try to not inherit from `Object.prototype`. In some engines creating an
// instance in this way is faster than calling `Object.create(null)` directly.
// If `Object.create(null)` is not supported we prefix the event names with a
// character to make sure that the built-in object properties are not
// overridden or used as an attack vector.
//
if (Object.create) {
Events.prototype = Object.create(null); //
// This hack is needed because the `__proto__` property is still inherited in
// some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
//
if (!new Events().__proto__) prefix = false;
}
/**
* Representation of a single event listener.
*
* @param {Function} fn The listener function.
* @param {*} context The context to invoke the listener with.
* @param {Boolean} [once=false] Specify if the listener is a one-time listener.
* @constructor
* @private
*/
function EE(fn, context, once) {
this.fn = fn;
this.context = context;
this.once = once || false;
}
/**
* Add a listener for a given event.
*
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} context The context to invoke the listener with.
* @param {Boolean} once Specify if the listener is a one-time listener.
* @returns {EventEmitter}
* @private
*/
function addListener(emitter, event, fn, context, once) {
if (typeof fn !== 'function') {
throw new TypeError('The listener must be a function');
}
var listener = new EE(fn, context || emitter, once),
evt = prefix ? prefix + event : event;
if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);else emitter._events[evt] = [emitter._events[evt], listener];
return emitter;
}
/**
* Clear event by name.
*
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
* @param {(String|Symbol)} evt The Event name.
* @private
*/
function clearEvent(emitter, evt) {
if (--emitter._eventsCount === 0) emitter._events = new Events();else delete emitter._events[evt];
}
/**
* Minimal `EventEmitter` interface that is molded against the Node.js
* `EventEmitter` interface.
*
* @constructor
* @public
*/
function EventEmitter() {
this._events = new Events();
this._eventsCount = 0;
}
/**
* Return an array listing the events for which the emitter has registered
* listeners.
*
* @returns {Array}
* @public
*/
EventEmitter.prototype.eventNames = function eventNames() {
var names = [],
events,
name;
if (this._eventsCount === 0) return names;
for (name in events = this._events) {
if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);
}
if (Object.getOwnPropertySymbols) {
return names.concat(Object.getOwnPropertySymbols(events));
}
return names;
};
/**
* Return the listeners registered for a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Array} The registered listeners.
* @public
*/
EventEmitter.prototype.listeners = function listeners(event) {
var evt = prefix ? prefix + event : event,
handlers = this._events[evt];
if (!handlers) return [];
if (handlers.fn) return [handlers.fn];
for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
ee[i] = handlers[i].fn;
}
return ee;
};
/**
* Return the number of listeners listening to a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Number} The number of listeners.
* @public
*/
EventEmitter.prototype.listenerCount = function listenerCount(event) {
var evt = prefix ? prefix + event : event,
listeners = this._events[evt];
if (!listeners) return 0;
if (listeners.fn) return 1;
return listeners.length;
};
/**
* Calls each of the listeners registered for a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Boolean} `true` if the event had listeners, else `false`.
* @public
*/
EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return false;
var listeners = this._events[evt],
len = arguments.length,
args,
i;
if (listeners.fn) {
if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);
switch (len) {
case 1:
return listeners.fn.call(listeners.context), true;
case 2:
return listeners.fn.call(listeners.context, a1), true;
case 3:
return listeners.fn.call(listeners.context, a1, a2), true;
case 4:
return listeners.fn.call(listeners.context, a1, a2, a3), true;
case 5:
return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
case 6:
return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
}
for (i = 1, args = new Array(len - 1); i < len; i++) {
args[i - 1] = arguments[i];
}
listeners.fn.apply(listeners.context, args);
} else {
var length = listeners.length,
j;
for (i = 0; i < length; i++) {
if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);
switch (len) {
case 1:
listeners[i].fn.call(listeners[i].context);
break;
case 2:
listeners[i].fn.call(listeners[i].context, a1);
break;
case 3:
listeners[i].fn.call(listeners[i].context, a1, a2);
break;
case 4:
listeners[i].fn.call(listeners[i].context, a1, a2, a3);
break;
default:
if (!args) for (j = 1, args = new Array(len - 1); j < len; j++) {
args[j - 1] = arguments[j];
}
listeners[i].fn.apply(listeners[i].context, args);
}
}
}
return true;
};
/**
* Add a listener for a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} [context=this] The context to invoke the listener with.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.on = function on(event, fn, context) {
return addListener(this, event, fn, context, false);
};
/**
* Add a one-time listener for a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} [context=this] The context to invoke the listener with.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.once = function once(event, fn, context) {
return addListener(this, event, fn, context, true);
};
/**
* Remove the listeners of a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn Only remove the listeners that match this function.
* @param {*} context Only remove the listeners that have this context.
* @param {Boolean} once Only remove one-time listeners.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return this;
if (!fn) {
clearEvent(this, evt);
return this;
}
var listeners = this._events[evt];
if (listeners.fn) {
if (listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context)) {
clearEvent(this, evt);
}
} else {
for (var i = 0, events = [], length = listeners.length; i < length; i++) {
if (listeners[i].fn !== fn || once && !listeners[i].once || context && listeners[i].context !== context) {
events.push(listeners[i]);
}
} //
// Reset the array, or remove it completely if we have no more listeners.
//
if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;else clearEvent(this, evt);
}
return this;
};
/**
* Remove all listeners, or those of the specified event.
*
* @param {(String|Symbol)} [event] The event name.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
var evt;
if (event) {
evt = prefix ? prefix + event : event;
if (this._events[evt]) clearEvent(this, evt);
} else {
this._events = new Events();
this._eventsCount = 0;
}
return this;
}; //
// Alias methods names because people roll like that.
//
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.addListener = EventEmitter.prototype.on; //
// Expose the prefix.
//
EventEmitter.prefixed = prefix; //
// Allow `EventEmitter` to be imported as module namespace.
//
EventEmitter.EventEmitter = EventEmitter; //
// Expose the module.
//
if ('undefined' !== typeof module) {
module.exports = EventEmitter;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,84 @@
{
"_from": "eventemitter3@^4.0.0",
"_id": "eventemitter3@4.0.0",
"_inBundle": false,
"_integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==",
"_location": "/eventemitter3",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "eventemitter3@^4.0.0",
"name": "eventemitter3",
"escapedName": "eventemitter3",
"rawSpec": "^4.0.0",
"saveSpec": null,
"fetchSpec": "^4.0.0"
},
"_requiredBy": [
"/widget-ui"
],
"_resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz",
"_shasum": "d65176163887ee59f386d64c82610b696a4a74eb",
"_spec": "eventemitter3@^4.0.0",
"_where": "C:\\Users\\sanfordsun\\WeChatProjects\\minicode-177\\node_modules\\widget-ui",
"author": {
"name": "Arnout Kazemier"
},
"bugs": {
"url": "https://github.com/primus/eventemitter3/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "EventEmitter3 focuses on performance while maintaining a Node.js AND browser compatible interface.",
"devDependencies": {
"assume": "~2.2.0",
"browserify": "~16.2.0",
"mocha": "~6.1.0",
"nyc": "~14.1.0",
"pre-commit": "~1.2.0",
"sauce-browsers": "~2.0.0",
"sauce-test": "~1.3.3",
"uglify-js": "~3.6.0"
},
"files": [
"index.js",
"index.d.ts",
"umd"
],
"homepage": "https://github.com/primus/eventemitter3#readme",
"keywords": [
"EventEmitter",
"EventEmitter2",
"EventEmitter3",
"Events",
"addEventListener",
"addListener",
"emit",
"emits",
"emitter",
"event",
"once",
"pub/sub",
"publish",
"reactor",
"subscribe"
],
"license": "MIT",
"main": "index.js",
"name": "eventemitter3",
"repository": {
"type": "git",
"url": "git://github.com/primus/eventemitter3.git"
},
"scripts": {
"benchmark": "find benchmarks/run -name '*.js' -exec benchmarks/start.sh {} \\;",
"browserify": "rm -rf umd && mkdir umd && browserify index.js -s EventEmitter3 -o umd/eventemitter3.js",
"minify": "uglifyjs umd/eventemitter3.js --source-map -cm -o umd/eventemitter3.min.js",
"prepublishOnly": "npm run browserify && npm run minify",
"test": "nyc --reporter=html --reporter=text mocha test/test.js",
"test-browser": "node test/browser.js"
},
"typings": "index.d.ts",
"version": "4.0.0"
}

View File

@@ -0,0 +1,404 @@
(function (f) {
if (typeof exports === "object" && typeof module !== "undefined") {
module.exports = f();
} else if (typeof define === "function" && define.amd) {
define([], f);
} else {
var g;
if (typeof window !== "undefined") {
g = window;
} else if (typeof global !== "undefined") {
g = global;
} else if (typeof self !== "undefined") {
g = self;
} else {
g = this;
}
g.EventEmitter3 = f();
}
})(function () {
var define, module, exports;
return function () {
function r(e, n, t) {
function o(i, f) {
if (!n[i]) {
if (!e[i]) {
var c = "function" == typeof require && require;
if (!f && c) return c(i, !0);
if (u) return u(i, !0);
var a = new Error("Cannot find module '" + i + "'");
throw a.code = "MODULE_NOT_FOUND", a;
}
var p = n[i] = {
exports: {}
};
e[i][0].call(p.exports, function (r) {
var n = e[i][1][r];
return o(n || r);
}, p, p.exports, r, e, n, t);
}
return n[i].exports;
}
for (var u = "function" == typeof require && require, i = 0; i < t.length; i++) o(t[i]);
return o;
}
return r;
}()({
1: [function (require, module, exports) {
'use strict';
var has = Object.prototype.hasOwnProperty,
prefix = '~';
/**
* Constructor to create a storage for our `EE` objects.
* An `Events` instance is a plain object whose properties are event names.
*
* @constructor
* @private
*/
function Events() {} //
// We try to not inherit from `Object.prototype`. In some engines creating an
// instance in this way is faster than calling `Object.create(null)` directly.
// If `Object.create(null)` is not supported we prefix the event names with a
// character to make sure that the built-in object properties are not
// overridden or used as an attack vector.
//
if (Object.create) {
Events.prototype = Object.create(null); //
// This hack is needed because the `__proto__` property is still inherited in
// some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
//
if (!new Events().__proto__) prefix = false;
}
/**
* Representation of a single event listener.
*
* @param {Function} fn The listener function.
* @param {*} context The context to invoke the listener with.
* @param {Boolean} [once=false] Specify if the listener is a one-time listener.
* @constructor
* @private
*/
function EE(fn, context, once) {
this.fn = fn;
this.context = context;
this.once = once || false;
}
/**
* Add a listener for a given event.
*
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} context The context to invoke the listener with.
* @param {Boolean} once Specify if the listener is a one-time listener.
* @returns {EventEmitter}
* @private
*/
function addListener(emitter, event, fn, context, once) {
if (typeof fn !== 'function') {
throw new TypeError('The listener must be a function');
}
var listener = new EE(fn, context || emitter, once),
evt = prefix ? prefix + event : event;
if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);else emitter._events[evt] = [emitter._events[evt], listener];
return emitter;
}
/**
* Clear event by name.
*
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
* @param {(String|Symbol)} evt The Event name.
* @private
*/
function clearEvent(emitter, evt) {
if (--emitter._eventsCount === 0) emitter._events = new Events();else delete emitter._events[evt];
}
/**
* Minimal `EventEmitter` interface that is molded against the Node.js
* `EventEmitter` interface.
*
* @constructor
* @public
*/
function EventEmitter() {
this._events = new Events();
this._eventsCount = 0;
}
/**
* Return an array listing the events for which the emitter has registered
* listeners.
*
* @returns {Array}
* @public
*/
EventEmitter.prototype.eventNames = function eventNames() {
var names = [],
events,
name;
if (this._eventsCount === 0) return names;
for (name in events = this._events) {
if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);
}
if (Object.getOwnPropertySymbols) {
return names.concat(Object.getOwnPropertySymbols(events));
}
return names;
};
/**
* Return the listeners registered for a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Array} The registered listeners.
* @public
*/
EventEmitter.prototype.listeners = function listeners(event) {
var evt = prefix ? prefix + event : event,
handlers = this._events[evt];
if (!handlers) return [];
if (handlers.fn) return [handlers.fn];
for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
ee[i] = handlers[i].fn;
}
return ee;
};
/**
* Return the number of listeners listening to a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Number} The number of listeners.
* @public
*/
EventEmitter.prototype.listenerCount = function listenerCount(event) {
var evt = prefix ? prefix + event : event,
listeners = this._events[evt];
if (!listeners) return 0;
if (listeners.fn) return 1;
return listeners.length;
};
/**
* Calls each of the listeners registered for a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Boolean} `true` if the event had listeners, else `false`.
* @public
*/
EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return false;
var listeners = this._events[evt],
len = arguments.length,
args,
i;
if (listeners.fn) {
if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);
switch (len) {
case 1:
return listeners.fn.call(listeners.context), true;
case 2:
return listeners.fn.call(listeners.context, a1), true;
case 3:
return listeners.fn.call(listeners.context, a1, a2), true;
case 4:
return listeners.fn.call(listeners.context, a1, a2, a3), true;
case 5:
return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
case 6:
return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
}
for (i = 1, args = new Array(len - 1); i < len; i++) {
args[i - 1] = arguments[i];
}
listeners.fn.apply(listeners.context, args);
} else {
var length = listeners.length,
j;
for (i = 0; i < length; i++) {
if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);
switch (len) {
case 1:
listeners[i].fn.call(listeners[i].context);
break;
case 2:
listeners[i].fn.call(listeners[i].context, a1);
break;
case 3:
listeners[i].fn.call(listeners[i].context, a1, a2);
break;
case 4:
listeners[i].fn.call(listeners[i].context, a1, a2, a3);
break;
default:
if (!args) for (j = 1, args = new Array(len - 1); j < len; j++) {
args[j - 1] = arguments[j];
}
listeners[i].fn.apply(listeners[i].context, args);
}
}
}
return true;
};
/**
* Add a listener for a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} [context=this] The context to invoke the listener with.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.on = function on(event, fn, context) {
return addListener(this, event, fn, context, false);
};
/**
* Add a one-time listener for a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} [context=this] The context to invoke the listener with.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.once = function once(event, fn, context) {
return addListener(this, event, fn, context, true);
};
/**
* Remove the listeners of a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn Only remove the listeners that match this function.
* @param {*} context Only remove the listeners that have this context.
* @param {Boolean} once Only remove one-time listeners.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return this;
if (!fn) {
clearEvent(this, evt);
return this;
}
var listeners = this._events[evt];
if (listeners.fn) {
if (listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context)) {
clearEvent(this, evt);
}
} else {
for (var i = 0, events = [], length = listeners.length; i < length; i++) {
if (listeners[i].fn !== fn || once && !listeners[i].once || context && listeners[i].context !== context) {
events.push(listeners[i]);
}
} //
// Reset the array, or remove it completely if we have no more listeners.
//
if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;else clearEvent(this, evt);
}
return this;
};
/**
* Remove all listeners, or those of the specified event.
*
* @param {(String|Symbol)} [event] The event name.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
var evt;
if (event) {
evt = prefix ? prefix + event : event;
if (this._events[evt]) clearEvent(this, evt);
} else {
this._events = new Events();
this._eventsCount = 0;
}
return this;
}; //
// Alias methods names because people roll like that.
//
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.addListener = EventEmitter.prototype.on; //
// Expose the prefix.
//
EventEmitter.prefixed = prefix; //
// Allow `EventEmitter` to be imported as module namespace.
//
EventEmitter.EventEmitter = EventEmitter; //
// Expose the module.
//
if ('undefined' !== typeof module) {
module.exports = EventEmitter;
}
}, {}]
}, {}, [1])(1);
});

View File

@@ -0,0 +1,162 @@
!function (e) {
if ("object" == typeof exports && "undefined" != typeof module) module.exports = e();else if ("function" == typeof define && define.amd) define([], e);else {
("undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self ? self : this).EventEmitter3 = e();
}
}(function () {
return function i(s, f, c) {
function u(t, e) {
if (!f[t]) {
if (!s[t]) {
var n = "function" == typeof require && require;
if (!e && n) return n(t, !0);
if (a) return a(t, !0);
var r = new Error("Cannot find module '" + t + "'");
throw r.code = "MODULE_NOT_FOUND", r;
}
var o = f[t] = {
exports: {}
};
s[t][0].call(o.exports, function (e) {
return u(s[t][1][e] || e);
}, o, o.exports, i, s, f, c);
}
return f[t].exports;
}
for (var a = "function" == typeof require && require, e = 0; e < c.length; e++) u(c[e]);
return u;
}({
1: [function (e, t, n) {
"use strict";
var r = Object.prototype.hasOwnProperty,
v = "~";
function o() {}
function f(e, t, n) {
this.fn = e, this.context = t, this.once = n || !1;
}
function i(e, t, n, r, o) {
if ("function" != typeof n) throw new TypeError("The listener must be a function");
var i = new f(n, r || e, o),
s = v ? v + t : t;
return e._events[s] ? e._events[s].fn ? e._events[s] = [e._events[s], i] : e._events[s].push(i) : (e._events[s] = i, e._eventsCount++), e;
}
function u(e, t) {
0 == --e._eventsCount ? e._events = new o() : delete e._events[t];
}
function s() {
this._events = new o(), this._eventsCount = 0;
}
Object.create && (o.prototype = Object.create(null), new o().__proto__ || (v = !1)), s.prototype.eventNames = function () {
var e,
t,
n = [];
if (0 === this._eventsCount) return n;
for (t in e = this._events) r.call(e, t) && n.push(v ? t.slice(1) : t);
return Object.getOwnPropertySymbols ? n.concat(Object.getOwnPropertySymbols(e)) : n;
}, s.prototype.listeners = function (e) {
var t = v ? v + e : e,
n = this._events[t];
if (!n) return [];
if (n.fn) return [n.fn];
for (var r = 0, o = n.length, i = new Array(o); r < o; r++) i[r] = n[r].fn;
return i;
}, s.prototype.listenerCount = function (e) {
var t = v ? v + e : e,
n = this._events[t];
return n ? n.fn ? 1 : n.length : 0;
}, s.prototype.emit = function (e, t, n, r, o, i) {
var s = v ? v + e : e;
if (!this._events[s]) return !1;
var f,
c,
u = this._events[s],
a = arguments.length;
if (u.fn) {
switch (u.once && this.removeListener(e, u.fn, void 0, !0), a) {
case 1:
return u.fn.call(u.context), !0;
case 2:
return u.fn.call(u.context, t), !0;
case 3:
return u.fn.call(u.context, t, n), !0;
case 4:
return u.fn.call(u.context, t, n, r), !0;
case 5:
return u.fn.call(u.context, t, n, r, o), !0;
case 6:
return u.fn.call(u.context, t, n, r, o, i), !0;
}
for (c = 1, f = new Array(a - 1); c < a; c++) f[c - 1] = arguments[c];
u.fn.apply(u.context, f);
} else {
var l,
p = u.length;
for (c = 0; c < p; c++) switch (u[c].once && this.removeListener(e, u[c].fn, void 0, !0), a) {
case 1:
u[c].fn.call(u[c].context);
break;
case 2:
u[c].fn.call(u[c].context, t);
break;
case 3:
u[c].fn.call(u[c].context, t, n);
break;
case 4:
u[c].fn.call(u[c].context, t, n, r);
break;
default:
if (!f) for (l = 1, f = new Array(a - 1); l < a; l++) f[l - 1] = arguments[l];
u[c].fn.apply(u[c].context, f);
}
}
return !0;
}, s.prototype.on = function (e, t, n) {
return i(this, e, t, n, !1);
}, s.prototype.once = function (e, t, n) {
return i(this, e, t, n, !0);
}, s.prototype.removeListener = function (e, t, n, r) {
var o = v ? v + e : e;
if (!this._events[o]) return this;
if (!t) return u(this, o), this;
var i = this._events[o];
if (i.fn) i.fn !== t || r && !i.once || n && i.context !== n || u(this, o);else {
for (var s = 0, f = [], c = i.length; s < c; s++) (i[s].fn !== t || r && !i[s].once || n && i[s].context !== n) && f.push(i[s]);
f.length ? this._events[o] = 1 === f.length ? f[0] : f : u(this, o);
}
return this;
}, s.prototype.removeAllListeners = function (e) {
var t;
return e ? (t = v ? v + e : e, this._events[t] && u(this, t)) : (this._events = new o(), this._eventsCount = 0), this;
}, s.prototype.off = s.prototype.removeListener, s.prototype.addListener = s.prototype.on, s.prefixed = v, s.EventEmitter = s, void 0 !== t && (t.exports = s);
}, {}]
}, {}, [1])(1);
});

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,172 @@
import computeLayout from "./css-layout";
import { getDefaultStyle, scalableStyles, layoutAffectedStyles } from "./style";
type LayoutData = {
left: number,
top: number,
width: number,
height: number
};
type LayoutNode = {
id: number,
style: Object,
children: LayoutNode[],
layout?: LayoutData
};
let uuid = 0;
class Element {
public static uuid(): number {
return uuid++;
}
public parent: Element | null = null;
public id: number = Element.uuid();
public style: { [key: string]: any } = {};
public computedStyle: { [key: string]: any } = {};
public lastComputedStyle: { [key: string]: any } = {};
public children: { [key: string]: Element } = {};
public layoutBox: LayoutData = { left: 0, top: 0, width: 0, height: 0 };
constructor(style: { [key: string]: any } = {}) {
// 拷贝一份,防止被外部逻辑修改
style = Object.assign(getDefaultStyle(), style);
this.computedStyle = Object.assign(getDefaultStyle(), style);
this.lastComputedStyle = Object.assign(getDefaultStyle(), style);
Object.keys(style).forEach(key => {
Object.defineProperty(this.style, key, {
configurable: true,
enumerable: true,
get: () => style[key],
set: (value: any) => {
if (value === style[key] || value === undefined) {
return;
}
this.lastComputedStyle = this.computedStyle[key]
style[key] = value
this.computedStyle[key] = value
// 如果设置的是一个可缩放的属性, 计算自己
if (scalableStyles.includes(key) && this.style.scale) {
this.computedStyle[key] = value * this.style.scale
}
// 如果设置的是 scale, 则把所有可缩放的属性计算
if (key === "scale") {
scalableStyles.forEach(prop => {
if (style[prop]) {
this.computedStyle[prop] = style[prop] * value
}
})
}
if (key === "hidden") {
if (value) {
layoutAffectedStyles.forEach((key: string) => {
this.computedStyle[key] = 0;
});
} else {
layoutAffectedStyles.forEach((key: string) => {
this.computedStyle[key] = this.lastComputedStyle[key];
});
}
}
}
})
})
if (this.style.scale) {
scalableStyles.forEach((key: string) => {
if (this.style[key]) {
const computedValue = this.style[key] * this.style.scale;
this.computedStyle[key] = computedValue;
}
});
}
if (style.hidden) {
layoutAffectedStyles.forEach((key: string) => {
this.computedStyle[key] = 0;
});
}
}
getAbsolutePosition(element: Element) {
if (!element) {
return this.getAbsolutePosition(this)
}
if (!element.parent) {
return {
left: 0,
top: 0
}
}
const {left, top} = this.getAbsolutePosition(element.parent)
return {
left: left + element.layoutBox.left,
top: top + element.layoutBox.top
}
}
public add(element: Element) {
element.parent = this;
this.children[element.id] = element;
}
public remove(element?: Element) {
// 删除自己
if (!element) {
Object.keys(this.children).forEach(id => {
const child = this.children[id]
child.remove()
delete this.children[id]
})
} else if (this.children[element.id]) {
// 是自己的子节点才删除
element.remove()
delete this.children[element.id];
}
}
public getNodeTree(): LayoutNode {
return {
id: this.id,
style: this.computedStyle,
children: Object.keys(this.children).map((id: string) => {
const child = this.children[id];
return child.getNodeTree();
})
}
}
public applyLayout(layoutNode: LayoutNode) {
["left", "top", "width", "height"].forEach((key: string) => {
if (layoutNode.layout && typeof layoutNode.layout[key] === "number") {
this.layoutBox[key] = layoutNode.layout[key];
if (this.parent && (key === "left" || key === "top")) {
this.layoutBox[key] += this.parent.layoutBox[key];
}
}
});
layoutNode.children.forEach((child: LayoutNode) => {
this.children[child.id].applyLayout(child);
});
}
layout() {
const nodeTree = this.getNodeTree();
computeLayout(nodeTree);
this.applyLayout(nodeTree);
}
}
export default Element;

View File

@@ -0,0 +1,15 @@
import _EventEmitter from "eventemitter3";
const emitter = new _EventEmitter();
export default class EventEmitter {
public emit(event: string, data?: any) {
emitter.emit(event, data);
}
public on(event: string, callback) {
emitter.on(event, callback);
}
public off(event: string, callback) {
emitter.off(event, callback);
}
}

View File

@@ -0,0 +1,746 @@
module.exports = function () {
var __MODS__ = {};
var __DEFINE__ = function (modId, func, req) {
var m = {
exports: {}
};
__MODS__[modId] = {
status: 0,
func: func,
req: req,
m: m
};
};
var __REQUIRE__ = function (modId, source) {
if (!__MODS__[modId]) return require(source);
if (!__MODS__[modId].status) {
var m = {
exports: {}
};
__MODS__[modId].status = 1;
__MODS__[modId].func(__MODS__[modId].req, m, m.exports);
if (typeof m.exports === "object") {
__MODS__[modId].m.exports.__proto__ = m.exports.__proto__;
Object.keys(m.exports).forEach(function (k) {
__MODS__[modId].m.exports[k] = m.exports[k];
var desp = Object.getOwnPropertyDescriptor(m.exports, k);
if (desp && desp.configurable) Object.defineProperty(m.exports, k, {
set: function (val) {
__MODS__[modId].m.exports[k] = val;
},
get: function () {
return __MODS__[modId].m.exports[k];
}
});
});
if (m.exports.__esModule) Object.defineProperty(__MODS__[modId].m.exports, "__esModule", {
value: true
});
} else {
__MODS__[modId].m.exports = m.exports;
}
}
return __MODS__[modId].m.exports;
};
var __REQUIRE_WILDCARD__ = function (obj) {
if (obj && obj.__esModule) {
return obj;
} else {
var newObj = {};
if (obj != null) {
for (var k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k];
}
}
newObj.default = obj;
return newObj;
}
};
var __REQUIRE_DEFAULT__ = function (obj) {
return obj && obj.__esModule ? obj.default : obj;
};
__DEFINE__(1572960819414, function (require, module, exports) {
!function (t, e) {
if ("object" == typeof exports && "object" == typeof module) module.exports = e();else if ("function" == typeof define && define.amd) define([], e);else {
var o = e();
for (var r in o) ("object" == typeof exports ? exports : t)[r] = o[r];
}
}(this, function () {
return function (t) {
var e = {};
function o(r) {
if (e[r]) return e[r].exports;
var i = e[r] = {
i: r,
l: !1,
exports: {}
};
return t[r].call(i.exports, i, i.exports, o), i.l = !0, i.exports;
}
return o.m = t, o.c = e, o.d = function (t, e, r) {
o.o(t, e) || Object.defineProperty(t, e, {
enumerable: !0,
get: r
});
}, o.r = function (t) {
"undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(t, Symbol.toStringTag, {
value: "Module"
}), Object.defineProperty(t, "__esModule", {
value: !0
});
}, o.t = function (t, e) {
if (1 & e && (t = o(t)), 8 & e) return t;
if (4 & e && "object" == typeof t && t && t.__esModule) return t;
var r = Object.create(null);
if (o.r(r), Object.defineProperty(r, "default", {
enumerable: !0,
value: t
}), 2 & e && "string" != typeof t) for (var i in t) o.d(r, i, function (e) {
return t[e];
}.bind(null, i));
return r;
}, o.n = function (t) {
var e = t && t.__esModule ? function () {
return t.default;
} : function () {
return t;
};
return o.d(e, "a", e), e;
}, o.o = function (t, e) {
return Object.prototype.hasOwnProperty.call(t, e);
}, o.p = "", o(o.s = 0);
}([function (t, e, o) {
var r = this && this.__importDefault || function (t) {
return t && t.__esModule ? t : {
default: t
};
};
Object.defineProperty(e, "__esModule", {
value: !0
});
var i = r(o(1)),
l = o(2),
n = 0,
a = function () {
function t(e) {
var o = this;
void 0 === e && (e = {}), this.parent = null, this.id = t.uuid(), this.style = {}, this.computedStyle = {}, this.lastComputedStyle = {}, this.children = {}, this.layoutBox = {
left: 0,
top: 0,
width: 0,
height: 0
}, e = Object.assign(l.getDefaultStyle(), e), this.computedStyle = Object.assign(l.getDefaultStyle(), e), this.lastComputedStyle = Object.assign(l.getDefaultStyle(), e), Object.keys(e).forEach(function (t) {
Object.defineProperty(o.style, t, {
configurable: !0,
enumerable: !0,
get: function () {
return e[t];
},
set: function (r) {
r !== e[t] && void 0 !== r && (o.lastComputedStyle = o.computedStyle[t], e[t] = r, o.computedStyle[t] = r, l.scalableStyles.includes(t) && o.style.scale && (o.computedStyle[t] = r * o.style.scale), "scale" === t && l.scalableStyles.forEach(function (t) {
e[t] && (o.computedStyle[t] = e[t] * r);
}), "hidden" === t && (r ? l.layoutAffectedStyles.forEach(function (t) {
o.computedStyle[t] = 0;
}) : l.layoutAffectedStyles.forEach(function (t) {
o.computedStyle[t] = o.lastComputedStyle[t];
})));
}
});
}), this.style.scale && l.scalableStyles.forEach(function (t) {
if (o.style[t]) {
var e = o.style[t] * o.style.scale;
o.computedStyle[t] = e;
}
}), e.hidden && l.layoutAffectedStyles.forEach(function (t) {
o.computedStyle[t] = 0;
});
}
return t.uuid = function () {
return n++;
}, t.prototype.getAbsolutePosition = function (t) {
if (!t) return this.getAbsolutePosition(this);
if (!t.parent) return {
left: 0,
top: 0
};
var e = this.getAbsolutePosition(t.parent),
o = e.left,
r = e.top;
return {
left: o + t.layoutBox.left,
top: r + t.layoutBox.top
};
}, t.prototype.add = function (t) {
t.parent = this, this.children[t.id] = t;
}, t.prototype.remove = function (t) {
var e = this;
t ? this.children[t.id] && (t.remove(), delete this.children[t.id]) : Object.keys(this.children).forEach(function (t) {
e.children[t].remove(), delete e.children[t];
});
}, t.prototype.getNodeTree = function () {
var t = this;
return {
id: this.id,
style: this.computedStyle,
children: Object.keys(this.children).map(function (e) {
return t.children[e].getNodeTree();
})
};
}, t.prototype.applyLayout = function (t) {
var e = this;
["left", "top", "width", "height"].forEach(function (o) {
t.layout && "number" == typeof t.layout[o] && (e.layoutBox[o] = t.layout[o], !e.parent || "left" !== o && "top" !== o || (e.layoutBox[o] += e.parent.layoutBox[o]));
}), t.children.forEach(function (t) {
e.children[t.id].applyLayout(t);
});
}, t.prototype.layout = function () {
var t = this.getNodeTree();
i.default(t), this.applyLayout(t);
}, t;
}();
e.default = a;
}, function (t, e, o) {
o.r(e);
var r = function () {
var t,
e = "inherit",
o = "ltr",
r = "rtl",
i = "row",
l = "row-reverse",
n = "column",
a = "column-reverse",
u = "flex-start",
d = "center",
s = "flex-end",
y = "space-between",
c = "space-around",
f = "flex-start",
h = "center",
p = "flex-end",
g = "stretch",
v = "relative",
m = "absolute",
b = {
row: "left",
"row-reverse": "right",
column: "top",
"column-reverse": "bottom"
},
x = {
row: "right",
"row-reverse": "left",
column: "bottom",
"column-reverse": "top"
},
w = {
row: "left",
"row-reverse": "right",
column: "top",
"column-reverse": "bottom"
},
S = {
row: "width",
"row-reverse": "width",
column: "height",
"column-reverse": "height"
};
function W(t) {
return void 0 === t;
}
function L(t) {
return t === i || t === l;
}
function k(t, e) {
if (void 0 !== t.style.marginStart && L(e)) return t.style.marginStart;
var o = null;
switch (e) {
case "row":
o = t.style.marginLeft;
break;
case "row-reverse":
o = t.style.marginRight;
break;
case "column":
o = t.style.marginTop;
break;
case "column-reverse":
o = t.style.marginBottom;
}
return void 0 !== o ? o : void 0 !== t.style.margin ? t.style.margin : 0;
}
function j(t, e) {
if (void 0 !== t.style.marginEnd && L(e)) return t.style.marginEnd;
var o = null;
switch (e) {
case "row":
o = t.style.marginRight;
break;
case "row-reverse":
o = t.style.marginLeft;
break;
case "column":
o = t.style.marginBottom;
break;
case "column-reverse":
o = t.style.marginTop;
}
return null != o ? o : void 0 !== t.style.margin ? t.style.margin : 0;
}
function B(t, e) {
if (void 0 !== t.style.borderStartWidth && t.style.borderStartWidth >= 0 && L(e)) return t.style.borderStartWidth;
var o = null;
switch (e) {
case "row":
o = t.style.borderLeftWidth;
break;
case "row-reverse":
o = t.style.borderRightWidth;
break;
case "column":
o = t.style.borderTopWidth;
break;
case "column-reverse":
o = t.style.borderBottomWidth;
}
return null != o && o >= 0 ? o : void 0 !== t.style.borderWidth && t.style.borderWidth >= 0 ? t.style.borderWidth : 0;
}
function E(t, e) {
if (void 0 !== t.style.borderEndWidth && t.style.borderEndWidth >= 0 && L(e)) return t.style.borderEndWidth;
var o = null;
switch (e) {
case "row":
o = t.style.borderRightWidth;
break;
case "row-reverse":
o = t.style.borderLeftWidth;
break;
case "column":
o = t.style.borderBottomWidth;
break;
case "column-reverse":
o = t.style.borderTopWidth;
}
return null != o && o >= 0 ? o : void 0 !== t.style.borderWidth && t.style.borderWidth >= 0 ? t.style.borderWidth : 0;
}
function C(t, e) {
return function (t, e) {
if (void 0 !== t.style.paddingStart && t.style.paddingStart >= 0 && L(e)) return t.style.paddingStart;
var o = null;
switch (e) {
case "row":
o = t.style.paddingLeft;
break;
case "row-reverse":
o = t.style.paddingRight;
break;
case "column":
o = t.style.paddingTop;
break;
case "column-reverse":
o = t.style.paddingBottom;
}
return null != o && o >= 0 ? o : void 0 !== t.style.padding && t.style.padding >= 0 ? t.style.padding : 0;
}(t, e) + B(t, e);
}
function T(t, e) {
return function (t, e) {
if (void 0 !== t.style.paddingEnd && t.style.paddingEnd >= 0 && L(e)) return t.style.paddingEnd;
var o = null;
switch (e) {
case "row":
o = t.style.paddingRight;
break;
case "row-reverse":
o = t.style.paddingLeft;
break;
case "column":
o = t.style.paddingBottom;
break;
case "column-reverse":
o = t.style.paddingTop;
}
return null != o && o >= 0 ? o : void 0 !== t.style.padding && t.style.padding >= 0 ? t.style.padding : 0;
}(t, e) + E(t, e);
}
function O(t, e) {
return B(t, e) + E(t, e);
}
function _(t, e) {
return k(t, e) + j(t, e);
}
function R(t, e) {
return C(t, e) + T(t, e);
}
function A(t, e) {
return e.style.alignSelf ? e.style.alignSelf : t.style.alignItems ? t.style.alignItems : "stretch";
}
function P(t, e) {
if (e === r) {
if (t === i) return l;
if (t === l) return i;
}
return t;
}
function D(t, e) {
return function (t) {
return t === n || t === a;
}(t) ? P(i, e) : n;
}
function H(t) {
return t.style.position ? t.style.position : "relative";
}
function M(t) {
return H(t) === v && t.style.flex > 0;
}
function I(t, e) {
return t.layout[S[e]] + _(t, e);
}
function N(t, e) {
return void 0 !== t.style[S[e]] && t.style[S[e]] >= 0;
}
function F(t, e) {
return void 0 !== t.style[e];
}
function q(t, e) {
return void 0 !== t.style[e] ? t.style[e] : 0;
}
function z(t, e, o) {
var r = {
row: t.style.minWidth,
"row-reverse": t.style.minWidth,
column: t.style.minHeight,
"column-reverse": t.style.minHeight
}[e],
i = {
row: t.style.maxWidth,
"row-reverse": t.style.maxWidth,
column: t.style.maxHeight,
"column-reverse": t.style.maxHeight
}[e],
l = o;
return void 0 !== i && i >= 0 && l > i && (l = i), void 0 !== r && r >= 0 && l < r && (l = r), l;
}
function U(t, e) {
return t > e ? t : e;
}
function G(t, e) {
void 0 === t.layout[S[e]] && N(t, e) && (t.layout[S[e]] = U(z(t, e, t.style[S[e]]), R(t, e)));
}
function J(t, e, o) {
e.layout[x[o]] = t.layout[S[o]] - e.layout[S[o]] - e.layout[w[o]];
}
function K(t, e) {
return void 0 !== t.style[b[e]] ? q(t, b[e]) : -q(t, x[e]);
}
function Q(r, E, Q) {
var X = function (t, r) {
var i;
return (i = t.style.direction ? t.style.direction : e) === e && (i = void 0 === r ? o : r), i;
}(r, Q),
Y = P(function (t) {
return t.style.flexDirection ? t.style.flexDirection : n;
}(r), X),
Z = D(Y, X),
$ = P(i, X);
G(r, Y), G(r, Z), r.layout.direction = X, r.layout[b[Y]] += k(r, Y) + K(r, Y), r.layout[x[Y]] += j(r, Y) + K(r, Y), r.layout[b[Z]] += k(r, Z) + K(r, Z), r.layout[x[Z]] += j(r, Z) + K(r, Z);
var tt = r.children.length,
et = R(r, $);
if (function (t) {
return void 0 !== t.style.measure;
}(r)) {
var ot = !W(r.layout[S[$]]),
rt = t;
rt = N(r, $) ? r.style.width : ot ? r.layout[S[$]] : E - _(r, $), rt -= et;
var it = !N(r, $) && !ot,
lt = !N(r, n) && W(r.layout[S[n]]);
if (it || lt) {
var nt = r.style.measure(rt);
it && (r.layout.width = nt.width + et), lt && (r.layout.height = nt.height + R(r, n));
}
if (0 === tt) return;
}
var at,
ut,
dt,
st,
yt = function (t) {
return "wrap" === t.style.flexWrap;
}(r),
ct = function (t) {
return t.style.justifyContent ? t.style.justifyContent : "flex-start";
}(r),
ft = C(r, Y),
ht = C(r, Z),
pt = R(r, Y),
gt = R(r, Z),
vt = !W(r.layout[S[Y]]),
mt = !W(r.layout[S[Z]]),
bt = L(Y),
xt = null,
wt = null,
St = t;
vt && (St = r.layout[S[Y]] - pt);
for (var Wt = 0, Lt = 0, kt = 0, jt = 0, Bt = 0, Et = 0; Lt < tt;) {
var Ct,
Tt = 0,
Ot = 0,
_t = 0,
Rt = 0,
At = vt && ct === u || !vt && ct !== d,
Pt = At ? tt : Wt,
Dt = !0,
Ht = tt,
Mt = null,
It = null,
Nt = ft,
Ft = 0;
for (at = Wt; at < tt; ++at) {
if ((dt = r.children[at]).lineIndex = Et, dt.nextAbsoluteChild = null, dt.nextFlexChild = null, (Xt = A(r, dt)) === g && H(dt) === v && mt && !N(dt, Z)) dt.layout[S[Z]] = U(z(dt, Z, r.layout[S[Z]] - gt - _(dt, Z)), R(dt, Z));else if (H(dt) === m) for (null === xt && (xt = dt), null !== wt && (wt.nextAbsoluteChild = dt), wt = dt, ut = 0; ut < 2; ut++) st = 0 !== ut ? i : n, !W(r.layout[S[st]]) && !N(dt, st) && F(dt, b[st]) && F(dt, x[st]) && (dt.layout[S[st]] = U(z(dt, st, r.layout[S[st]] - R(r, st) - _(dt, st) - q(dt, b[st]) - q(dt, x[st])), R(dt, st)));
var qt = 0;
if (vt && M(dt) ? (Ot++, _t += dt.style.flex, null === Mt && (Mt = dt), null !== It && (It.nextFlexChild = dt), It = dt, qt = R(dt, Y) + _(dt, Y)) : (Ct = t, bt || (Ct = N(r, $) ? r.layout[S[$]] - et : E - _(r, $) - et), 0 === kt && V(dt, Ct, X), H(dt) === v && (Rt++, qt = I(dt, Y))), yt && vt && Tt + qt > St && at !== Wt) {
Rt--, kt = 1;
break;
}
At && (H(dt) !== v || M(dt)) && (At = !1, Pt = at), Dt && (H(dt) !== v || Xt !== g && Xt !== f || W(dt.layout[S[Z]])) && (Dt = !1, Ht = at), At && (dt.layout[w[Y]] += Nt, vt && J(r, dt, Y), Nt += I(dt, Y), Ft = U(Ft, z(dt, Z, I(dt, Z)))), Dt && (dt.layout[w[Z]] += jt + ht, mt && J(r, dt, Z)), kt = 0, Tt += qt, Lt = at + 1;
}
var zt = 0,
Ut = 0,
Gt = 0;
if (Gt = vt ? St - Tt : U(Tt, 0) - Tt, 0 !== Ot) {
var Jt,
Kt,
Qt = Gt / _t;
for (It = Mt; null !== It;) (Jt = Qt * It.style.flex + R(It, Y)) !== (Kt = z(It, Y, Jt)) && (Gt -= Kt, _t -= It.style.flex), It = It.nextFlexChild;
for ((Qt = Gt / _t) < 0 && (Qt = 0), It = Mt; null !== It;) It.layout[S[Y]] = z(It, Y, Qt * It.style.flex + R(It, Y)), Ct = t, N(r, $) ? Ct = r.layout[S[$]] - et : bt || (Ct = E - _(r, $) - et), V(It, Ct, X), dt = It, It = It.nextFlexChild, dt.nextFlexChild = null;
} else ct !== u && (ct === d ? zt = Gt / 2 : ct === s ? zt = Gt : ct === y ? (Gt = U(Gt, 0), Ut = Ot + Rt - 1 != 0 ? Gt / (Ot + Rt - 1) : 0) : ct === c && (zt = (Ut = Gt / (Ot + Rt)) / 2));
for (Nt += zt, at = Pt; at < Lt; ++at) H(dt = r.children[at]) === m && F(dt, b[Y]) ? dt.layout[w[Y]] = q(dt, b[Y]) + B(r, Y) + k(dt, Y) : (dt.layout[w[Y]] += Nt, vt && J(r, dt, Y), H(dt) === v && (Nt += Ut + I(dt, Y), Ft = U(Ft, z(dt, Z, I(dt, Z)))));
var Vt = r.layout[S[Z]];
for (mt || (Vt = U(z(r, Z, Ft + gt), gt)), at = Ht; at < Lt; ++at) if (H(dt = r.children[at]) === m && F(dt, b[Z])) dt.layout[w[Z]] = q(dt, b[Z]) + B(r, Z) + k(dt, Z);else {
var Xt,
Yt = ht;
if (H(dt) === v) if ((Xt = A(r, dt)) === g) W(dt.layout[S[Z]]) && (dt.layout[S[Z]] = U(z(dt, Z, Vt - gt - _(dt, Z)), R(dt, Z)));else if (Xt !== f) {
var Zt = Vt - gt - I(dt, Z);
Yt += Xt === h ? Zt / 2 : Zt;
}
dt.layout[w[Z]] += jt + Yt, mt && J(r, dt, Z);
}
jt += Ft, Bt = U(Bt, Nt), Et += 1, Wt = Lt;
}
if (Et > 1 && mt) {
var $t = r.layout[S[Z]] - gt,
te = $t - jt,
ee = 0,
oe = ht,
re = function (t) {
return t.style.alignContent ? t.style.alignContent : "flex-start";
}(r);
re === p ? oe += te : re === h ? oe += te / 2 : re === g && $t > jt && (ee = te / Et);
var ie = 0;
for (at = 0; at < Et; ++at) {
var le = ie,
ne = 0;
for (ut = le; ut < tt; ++ut) if (H(dt = r.children[ut]) === v) {
if (dt.lineIndex !== at) break;
W(dt.layout[S[Z]]) || (ne = U(ne, dt.layout[S[Z]] + _(dt, Z)));
}
for (ie = ut, ne += ee, ut = le; ut < ie; ++ut) if (H(dt = r.children[ut]) === v) {
var ae = A(r, dt);
if (ae === f) dt.layout[w[Z]] = oe + k(dt, Z);else if (ae === p) dt.layout[w[Z]] = oe + ne - j(dt, Z) - dt.layout[S[Z]];else if (ae === h) {
var ue = dt.layout[S[Z]];
dt.layout[w[Z]] = oe + (ne - ue) / 2;
} else ae === g && (dt.layout[w[Z]] = oe + k(dt, Z));
}
oe += ne;
}
}
var de = !1,
se = !1;
if (vt || (r.layout[S[Y]] = U(z(r, Y, Bt + T(r, Y)), pt), Y !== l && Y !== a || (de = !0)), mt || (r.layout[S[Z]] = U(z(r, Z, jt + gt), gt), Z !== l && Z !== a || (se = !0)), de || se) for (at = 0; at < tt; ++at) dt = r.children[at], de && J(r, dt, Y), se && J(r, dt, Z);
for (wt = xt; null !== wt;) {
for (ut = 0; ut < 2; ut++) st = 0 !== ut ? i : n, !W(r.layout[S[st]]) && !N(wt, st) && F(wt, b[st]) && F(wt, x[st]) && (wt.layout[S[st]] = U(z(wt, st, r.layout[S[st]] - O(r, st) - _(wt, st) - q(wt, b[st]) - q(wt, x[st])), R(wt, st))), F(wt, x[st]) && !F(wt, b[st]) && (wt.layout[b[st]] = r.layout[S[st]] - wt.layout[S[st]] - q(wt, x[st]));
dt = wt, wt = wt.nextAbsoluteChild, dt.nextAbsoluteChild = null;
}
}
function V(t, e, r) {
t.shouldUpdate = !0;
var i = t.style.direction || o;
!t.isDirty && t.lastLayout && t.lastLayout.requestedHeight === t.layout.height && t.lastLayout.requestedWidth === t.layout.width && t.lastLayout.parentMaxWidth === e && t.lastLayout.direction === i ? (t.layout.width = t.lastLayout.width, t.layout.height = t.lastLayout.height, t.layout.top = t.lastLayout.top, t.layout.left = t.lastLayout.left) : (t.lastLayout || (t.lastLayout = {}), t.lastLayout.requestedWidth = t.layout.width, t.lastLayout.requestedHeight = t.layout.height, t.lastLayout.parentMaxWidth = e, t.lastLayout.direction = i, t.children.forEach(function (t) {
t.layout.width = void 0, t.layout.height = void 0, t.layout.top = 0, t.layout.left = 0;
}), Q(t, e, r), t.lastLayout.width = t.layout.width, t.lastLayout.height = t.layout.height, t.lastLayout.top = t.layout.top, t.lastLayout.left = t.layout.left);
}
return {
layoutNodeImpl: Q,
computeLayout: V,
fillNodes: function t(e) {
return e.layout && !e.isDirty || (e.layout = {
width: void 0,
height: void 0,
top: 0,
left: 0,
right: 0,
bottom: 0
}), e.style || (e.style = {}), e.children || (e.children = []), e.children.forEach(t), e;
}
};
}();
e.default = function (t) {
r.fillNodes(t), r.computeLayout(t);
};
}, function (t, e, o) {
Object.defineProperty(e, "__esModule", {
value: !0
});
e.textStyles = ["color", "fontSize", "textAlign", "fontWeight", "lineHeight", "lineBreak"];
e.scalableStyles = ["left", "top", "right", "bottom", "width", "height", "margin", "marginLeft", "marginRight", "marginTop", "marginBottom", "padding", "paddingLeft", "paddingRight", "paddingTop", "paddingBottom", "borderWidth", "borderLeftWidth", "borderRightWidth", "borderTopWidth", "borderBottomWidth"];
e.layoutAffectedStyles = ["margin", "marginTop", "marginBottom", "marginLeft", "marginRight", "padding", "paddingTop", "paddingBottom", "paddingLeft", "paddingRight", "width", "height"];
e.getDefaultStyle = function () {
return {
left: void 0,
top: void 0,
right: void 0,
bottom: void 0,
width: void 0,
height: void 0,
maxWidth: void 0,
maxHeight: void 0,
minWidth: void 0,
minHeight: void 0,
margin: void 0,
marginLeft: void 0,
marginRight: void 0,
marginTop: void 0,
marginBottom: void 0,
padding: void 0,
paddingLeft: void 0,
paddingRight: void 0,
paddingTop: void 0,
paddingBottom: void 0,
borderWidth: void 0,
flexDirection: void 0,
justifyContent: void 0,
alignItems: void 0,
alignSelf: void 0,
flex: void 0,
flexWrap: void 0,
position: void 0,
hidden: !1,
scale: 1
};
};
}]).default;
});
}, function (modId) {
var map = {};
return __REQUIRE__(map[modId], modId);
});
return __REQUIRE__(1572960819414);
}(); //# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,87 @@
const textStyles: string[] = ["color", "fontSize", "textAlign", "fontWeight", "lineHeight", "lineBreak"];
const scalableStyles: string[] = ["left", "top", "right", "bottom", "width", "height",
"margin", "marginLeft", "marginRight", "marginTop", "marginBottom",
"padding", "paddingLeft", "paddingRight", "paddingTop", "paddingBottom",
"borderWidth", "borderLeftWidth", "borderRightWidth", "borderTopWidth", "borderBottomWidth"];
const layoutAffectedStyles: string[] = [
"margin", "marginTop", "marginBottom", "marginLeft", "marginRight",
"padding", "paddingTop", "paddingBottom", "paddingLeft", "paddingRight",
"width", "height"];
type Style = {
left: number,
top: number,
right: number,
bottom: number,
width: number,
height: number,
maxWidth: number,
maxHeight: number,
minWidth: number,
minHeight: number,
margin: number,
marginLeft: number,
marginRight: number,
marginTop: number,
marginBottom: number,
padding: number,
paddingLeft: number,
paddingRight: number,
paddingTop: number,
paddingBottom: number,
borderWidth: number,
borderLeftWidth: number,
borderRightWidth: number,
borderTopWidth: number,
borderBottomWidth: number,
flexDirection: "column" | "row",
justifyContent: "flex-start" | "center" | "flex-end" | "space-between" | "space-around",
alignItems: "flex-start" | "center" | "flex-end" | "stretch",
alignSelf: "flex-start" | "center" | "flex-end" | "stretch",
flex: number,
flexWrap: "wrap" | "nowrap",
position: "relative" | "absolute",
hidden: boolean,
scale: number
}
const getDefaultStyle = () => ({
left: undefined,
top: undefined,
right: undefined,
bottom: undefined,
width: undefined,
height: undefined,
maxWidth: undefined,
maxHeight: undefined,
minWidth: undefined,
minHeight: undefined,
margin: undefined,
marginLeft: undefined,
marginRight: undefined,
marginTop: undefined,
marginBottom: undefined,
padding: undefined,
paddingLeft: undefined,
paddingRight: undefined,
paddingTop: undefined,
paddingBottom: undefined,
borderWidth: undefined,
flexDirection: undefined,
justifyContent: undefined,
alignItems: undefined,
alignSelf: undefined,
flex: undefined,
flexWrap: undefined,
position: undefined,
hidden: false,
scale: 1
})
export {
getDefaultStyle, scalableStyles, textStyles, layoutAffectedStyles
}

View File

@@ -0,0 +1,219 @@
class Draw {
constructor(canvas, context) {
this.canvas = canvas;
this.ctx = context;
}
roundRect(x, y, w, h, r, fill = true, stroke = false) {
if (r < 0) return;
const ctx = this.ctx;
ctx.beginPath();
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 3 / 2);
ctx.arc(x + w - r, y + r, r, Math.PI * 3 / 2, 0);
ctx.arc(x + w - r, y + h - r, r, 0, Math.PI / 2);
ctx.arc(x + r, y + h - r, r, Math.PI / 2, Math.PI);
ctx.lineTo(x, y + r);
if (stroke) ctx.stroke();
if (fill) ctx.fill();
}
drawView(box, style) {
const ctx = this.ctx;
const {
left: x,
top: y,
width: w,
height: h
} = box;
const {
borderRadius = 0,
borderWidth = 0,
borderColor,
color = '#000',
backgroundColor = 'transparent'
} = style;
ctx.save(); // 外环
if (borderWidth > 0) {
ctx.fillStyle = borderColor || color;
this.roundRect(x, y, w, h, borderRadius);
} // 内环
ctx.fillStyle = backgroundColor;
const innerWidth = w - 2 * borderWidth;
const innerHeight = h - 2 * borderWidth;
const innerRadius = borderRadius - borderWidth >= 0 ? borderRadius - borderWidth : 0;
this.roundRect(x + borderWidth, y + borderWidth, innerWidth, innerHeight, innerRadius);
ctx.restore();
}
async drawImage(img, box, style) {
await new Promise((resolve, reject) => {
const ctx = this.ctx;
const canvas = this.canvas;
const {
borderRadius = 0
} = style;
const {
left: x,
top: y,
width: w,
height: h
} = box;
ctx.save();
this.roundRect(x, y, w, h, borderRadius, false, false);
ctx.clip();
const Image = canvas.createImage();
Image.onload = () => {
ctx.drawImage(Image, x, y, w, h);
ctx.restore();
resolve();
};
Image.onerror = () => {
reject();
};
Image.src = img;
});
} // eslint-disable-next-line complexity
drawText(text, box, style) {
const ctx = this.ctx;
let {
left: x,
top: y,
width: w,
height: h
} = box;
let {
color = '#000',
lineHeight = '1.4em',
fontSize = 14,
textAlign = 'left',
verticalAlign = 'top',
backgroundColor = 'transparent'
} = style;
if (!text || lineHeight > h) return;
ctx.save();
if (lineHeight) {
// 2em
lineHeight = Math.ceil(parseFloat(lineHeight.replace('em')) * fontSize);
}
ctx.textBaseline = 'top';
ctx.font = `${fontSize}px sans-serif`;
ctx.textAlign = textAlign; // 背景色
ctx.fillStyle = backgroundColor;
this.roundRect(x, y, w, h, 0); // 文字颜色
ctx.fillStyle = color; // 水平布局
switch (textAlign) {
case 'left':
break;
case 'center':
x += 0.5 * w;
break;
case 'right':
x += w;
break;
default:
break;
}
const textWidth = ctx.measureText(text).width;
const actualHeight = Math.ceil(textWidth / w) * lineHeight;
let paddingTop = Math.ceil((h - actualHeight) / 2);
if (paddingTop < 0) paddingTop = 0; // 垂直布局
switch (verticalAlign) {
case 'top':
break;
case 'middle':
y += paddingTop;
break;
case 'bottom':
y += 2 * paddingTop;
break;
default:
break;
}
const inlinePaddingTop = Math.ceil((lineHeight - fontSize) / 2); // 不超过一行
if (textWidth <= w) {
ctx.fillText(text, x, y + inlinePaddingTop);
return;
} // 多行文本
const chars = text.split('');
const _y = y; // 逐行绘制
let line = '';
for (const ch of chars) {
const testLine = line + ch;
const testWidth = ctx.measureText(testLine).width;
if (testWidth > w) {
ctx.fillText(line, x, y + inlinePaddingTop);
y += lineHeight;
line = ch;
if (y + lineHeight > _y + h) break;
} else {
line = testLine;
}
} // 避免溢出
if (y + lineHeight <= _y + h) {
ctx.fillText(line, x, y + inlinePaddingTop);
}
ctx.restore();
}
async drawNode(element) {
const {
layoutBox,
computedStyle,
name
} = element;
const {
src,
text
} = element.attributes;
if (name === 'view') {
this.drawView(layoutBox, computedStyle);
} else if (name === 'image') {
await this.drawImage(src, layoutBox, computedStyle);
} else if (name === 'text') {
this.drawText(text, layoutBox, computedStyle);
}
const childs = Object.values(element.children);
for (const child of childs) {
await this.drawNode(child);
}
}
}
module.exports = {
Draw
};

View File

@@ -0,0 +1,34 @@
const $filterNullChildren = children => {
children = deepFlatten(children);
return children.filter(child => child != null);
};
const deepFlatten = arr => {
let flatten = arr => [].concat(...arr);
return flatten(arr.map(x => Array.isArray(x) ? deepFlatten(x) : x));
};
export default ((data, opt) => {
const {
View,
Text,
Image
} = opt;
Object.assign(data, {});
return new View({
style: {},
attr: {
"needRoot": true
},
children: $filterNullChildren([new Canvas({
style: {},
attr: {
"id": "canvas",
"type": "2d",
"style": "width: " + data.width + "px; height: " + data.height + "px;"
},
children: $filterNullChildren([])
})])
});
});

View File

@@ -0,0 +1,109 @@
<template>
<view>
<canvas id="canvas" type="2d" :style="'width: ' + width + 'px; height: ' + height + 'px;'"></canvas>
</view>
</template>
<script>
const xmlParse = require("./xml-parser");
const {
Widget
} = require("./widget");
const {
Draw
} = require("./draw");
export default {
data() {
return {};
},
components: {},
props: {
width: {
type: Number,
default: 400
},
height: {
type: Number,
default: 300
}
},
beforeMount() {
const dpr = wx.getSystemInfoSync().pixelRatio;
const query = this.createSelectorQuery();
this.dpr = dpr;
query.select('#canvas').fields({
node: true,
size: true
}).exec(res => {
console.log(res, "==res");
const canvas = res[0].node;
const ctx = canvas.getContext('2d');
canvas.width = res[0].width * dpr;
canvas.height = res[0].height * dpr;
ctx.scale(dpr, dpr);
this.ctx = ctx;
this.canvas = canvas;
});
},
methods: {
async renderToCanvas(args) {
const {
wxml,
style
} = args; // 清空画布
const ctx = this.ctx;
const canvas = this.canvas;
if (!ctx || !canvas) {
return Promise.reject(new Error('renderToCanvas: fail canvas has not been created'));
}
ctx.clearRect(0, 0, this.width, this.height);
const {
root: xom
} = xmlParse(wxml);
const widget = new Widget(xom, style);
const container = widget.init();
this.boundary = {
top: container.layoutBox.top,
left: container.layoutBox.left,
width: container.computedStyle.width,
height: container.computedStyle.height
};
const draw = new Draw(canvas, ctx);
await draw.drawNode(container);
return Promise.resolve(container);
},
canvasToTempFilePath(args = {}) {
return new Promise((resolve, reject) => {
const {
top,
left,
width,
height
} = this.boundary;
wx.canvasToTempFilePath({
x: left,
y: top,
width,
height,
destWidth: width * this.dpr,
destHeight: height * this.dpr,
canvas: this.canvas,
fileType: args.fileType || 'png',
quality: args.quality || 1,
success: resolve,
fail: reject
});
});
}
}
};
</script>

View File

@@ -0,0 +1,34 @@
const hex = color => {
let result = null;
if (/^#/.test(color) && (color.length === 7 || color.length === 9)) {
return color; // eslint-disable-next-line no-cond-assign
} else if ((result = /^(rgb|rgba)\((.+)\)/.exec(color)) !== null) {
return '#' + result[2].split(',').map((part, index) => {
part = part.trim();
part = index === 3 ? Math.floor(parseFloat(part) * 255) : parseInt(part, 10);
part = part.toString(16);
if (part.length === 1) {
part = '0' + part;
}
return part;
}).join('');
} else {
return '#00000000';
}
};
const splitLineToCamelCase = str => str.split('-').map((part, index) => {
if (index === 0) {
return part;
}
return part[0].toUpperCase() + part.slice(1);
}).join('');
module.exports = {
hex,
splitLineToCamelCase
};

View File

@@ -0,0 +1,88 @@
const Block = require("@/components/miniprogram_npm/widget-ui/index.js");
const {
splitLineToCamelCase
} = require("./utils.js");
class Element extends Block {
constructor(prop) {
super(prop.style);
this.name = prop.name;
this.attributes = prop.attributes;
}
}
class Widget {
constructor(xom, style) {
this.xom = xom;
this.style = style;
this.inheritProps = ['fontSize', 'lineHeight', 'textAlign', 'verticalAlign', 'color'];
}
init() {
this.container = this.create(this.xom);
this.container.layout();
this.inheritStyle(this.container);
return this.container;
} // 继承父节点的样式
inheritStyle(node) {
const parent = node.parent || null;
const children = node.children || {};
const computedStyle = node.computedStyle;
if (parent) {
this.inheritProps.forEach(prop => {
computedStyle[prop] = computedStyle[prop] || parent.computedStyle[prop];
});
}
Object.values(children).forEach(child => {
this.inheritStyle(child);
});
}
create(node) {
let classNames = (node.attributes.class || '').split(' ');
classNames = classNames.map(item => splitLineToCamelCase(item.trim()));
const style = {};
classNames.forEach(item => {
Object.assign(style, this.style[item] || {});
});
const args = {
name: node.name,
style
};
const attrs = Object.keys(node.attributes);
const attributes = {};
for (const attr of attrs) {
const value = node.attributes[attr];
const CamelAttr = splitLineToCamelCase(attr);
if (value === '' || value === 'true') {
attributes[CamelAttr] = true;
} else if (value === 'false') {
attributes[CamelAttr] = false;
} else {
attributes[CamelAttr] = value;
}
}
attributes.text = node.content;
args.attributes = attributes;
const element = new Element(args);
node.children.forEach(childNode => {
const childElement = this.create(childNode);
element.add(childElement);
});
return element;
}
}
module.exports = {
Widget
};

View File

@@ -0,0 +1,153 @@
/**
* Module dependencies.
*/
/**
* Expose `parse`.
*/
/**
* Parse the given string of `xml`.
*
* @param {String} xml
* @return {Object}
* @api public
*/
function parse(xml) {
xml = xml.trim(); // strip comments
xml = xml.replace(/<!--[\s\S]*?-->/g, '');
return document();
/**
* XML document.
*/
function document() {
return {
declaration: declaration(),
root: tag()
};
}
/**
* Declaration.
*/
function declaration() {
const m = match(/^<\?xml\s*/);
if (!m) return; // tag
const node = {
attributes: {}
}; // attributes
while (!(eos() || is('?>'))) {
const attr = attribute();
if (!attr) return node;
node.attributes[attr.name] = attr.value;
}
match(/\?>\s*/);
return node;
}
/**
* Tag.
*/
function tag() {
const m = match(/^<([\w-:.]+)\s*/);
if (!m) return; // name
const node = {
name: m[1],
attributes: {},
children: []
}; // attributes
while (!(eos() || is('>') || is('?>') || is('/>'))) {
const attr = attribute();
if (!attr) return node;
node.attributes[attr.name] = attr.value;
} // self closing tag
if (match(/^\s*\/>\s*/)) {
return node;
}
match(/\??>\s*/); // content
node.content = content(); // children
let child;
while (child = tag()) {
node.children.push(child);
} // closing
match(/^<\/[\w-:.]+>\s*/);
return node;
}
/**
* Text content.
*/
function content() {
const m = match(/^([^<]*)/);
if (m) return m[1];
return '';
}
/**
* Attribute.
*/
function attribute() {
const m = match(/([\w:-]+)\s*=\s*("[^"]*"|'[^']*'|\w+)\s*/);
if (!m) return;
return {
name: m[1],
value: strip(m[2])
};
}
/**
* Strip quotes from `val`.
*/
function strip(val) {
return val.replace(/^['"]|['"]$/g, '');
}
/**
* Match `re` and advance the string.
*/
function match(re) {
const m = xml.match(re);
if (!m) return;
xml = xml.slice(m[0].length);
return m;
}
/**
* End-of-source.
*/
function eos() {
return xml.length == 0;
}
/**
* Check for `prefix`.
*/
function is(prefix) {
return xml.indexOf(prefix) == 0;
}
}
module.exports = parse;

View File

@@ -0,0 +1,194 @@
<template>
<view class='search-box'
:style='{padding:`${padding}rpx`,margin:`${margin}rpx`,borderRadius:`${radius}rpx`,background:backgroundColor}'>
<view class='search-item' :class="[{'flex-y-center': type == 'text'},{'flex-between': type == 'input'}]"
:style='{borderRadius:`${radius}rpx`,background:searchColor,color:frontColor}'>
<block v-if="type=='text'">
<i class="iconfont icon-search"></i>
<view class='ml-md f-paragraph'>{{placeholder}}</view>
</block>
<block v-if="type=='input'">
<view class="flex-y-center flex-1">
<i class="iconfont icon-search"></i>
<input type='text' class="flex-1 f-paragraph ml-md mr-md" :disabled="disabled"
:placeholder='placeholder'
:placeholder-class='frontColor === "#fff" ? "c-base" : "c-placeholder"' confirm-type="search"
@input="handerInput" :value="keyword" @confirm="confirm" :auto-focus="autofocus"></input>
</view>
<view class=' search-item-btn flex-center radius' :style="{background:primaryColor}" @tap='confirm'
v-if="showbtn">搜索
</view>
</block>
</view>
</view>
</template>
<script>
import {
mapState,
} from "vuex"
export default {
name: 'search',
props: {
type: {
type: String,
default () {
return 'text'
}
},
placeholder: {
type: String,
default () {
return "请输入关键字进行搜索"
}
},
searchStyle: {
type: String,
default () {
return 'circle'
}
},
textAlign: {
type: String,
default () {
return 'center'
}
},
padding: {
type: Number,
default () {
return 30
}
},
margin: {
type: Number,
default () {
return 0
}
},
radius: {
type: Number,
default () {
return 0
}
},
backgroundColor: {
type: String,
default () {
return '#fff'
}
},
searchColor: {
type: String,
default () {
return '#F6F5FA'
}
},
frontColor: {
type: String,
default () {
return '#888'
}
},
autofocus: {
type: Boolean,
default () {
return false
}
},
disabled: {
type: Boolean,
default () {
return false
}
},
showbtn: {
type: Boolean,
default () {
return false
}
},
focus: {
type: Boolean,
default () {
return false
}
},
keyword: {
type: String,
default () {
return ''
}
}
},
created() {
},
data() {
return {
text: '',
searchImg: 'https://lbqny.migugu.com/admin/public/search.png'
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
}),
methods: {
confirm(e) {
let val = this.text;
this.$emit("confirm", val)
},
handerInput(e) {
let val = e.detail.value;
this.text = val;
this.$emit("input", val)
}
},
}
</script>
<style>
.search-box {
padding: 16rpx 16rpx;
display: flex;
align-items: center;
background: #efeff5;
}
.search-item {
width: 100%;
height: 70rpx;
background: #ffffff;
border-radius: 30rpx;
padding: 0 0 0 25rpx;
line-height: 1;
font-size: 26rpx;
}
.search-item-btn {
width: 110rpx;
height: 70rpx;
font-size: 26rpx;
font-weight: 400;
color: #FFFFFF;
}
.search-btn {
padding: 0 20rpx 0 40rpx;
}
.flex-1 {
flex: 1;
}
.icon-md {
width: 40rpx;
height: 40rpx;
}
.ml-md {
margin-left: 16rpx;
}
</style>

View File

@@ -0,0 +1,132 @@
<template>
<view>
<scroll-view scroll-x class='tab-list' :scroll-into-view="'tab'+(activeIndex-1)" :scroll-with-animation="true"
:style="{background: bgColor}">
<view class='tab-item text-bold rel' v-for="(item,index) in list" :key="index"
@tap='handerTabChange(index)' :id="'tab'+index"
:style="{width:width,height:height,lineHeight:height,color:index==activeIndex? activeColor : color,fontSize:fontSize}">
{{item.title || item}}
<view class="abs line" :style="{background: activeColor}" v-if="index==activeIndex && haveLine"></view>
<view v-if="item.number" class="item-msg c-base f-icontext abs"
:style="{width: item.number<10 ? '30rpx' : '50rpx',right: msgRight }">
{{item.number < 100 ? item.number : '99+'}}
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
name: 'tab',
props: {
list: {
type: Array,
default () {
return []
}
},
activeIndex: {
type: Number || String,
default () {
return 0
}
},
color: {
type: String,
default () {
return '#333'
}
},
activeColor: {
type: String,
default () {
return '#e73535'
}
},
bgColor: {
type: String,
default () {
return '#fff'
}
},
width: {
type: String,
default () {
return ''
}
},
height: {
type: String,
default () {
return ''
}
},
haveLine: {
type: Boolean,
default () {
return true
}
},
msgRight: {
type: String,
default () {
return '5rpx'
}
},
fontSize: {
type: String,
default () {
return '28rpx'
}
}
},
created() {
},
data() {
return {
}
},
methods: {
handerTabChange(index) {
this.$emit('change', index);
}
},
}
</script>
<style lang="scss">
.tab-list {
white-space: nowrap;
width: 100%;
}
.tab-item {
display: inline-block;
text-align: center;
line-height: 100rpx;
padding: 0 20rpx;
border-color: #fff;
box-sizing: border-box;
.line {
width: 80rpx;
height: 6rpx;
border-radius: 6rpx;
left: 50%;
bottom: 0rpx;
margin-left: -40rpx;
}
}
.item-msg {
width: 30rpx;
height: 30rpx;
line-height: 30rpx;
border-radius: 15rpx 15rpx 15rpx 0;
background: #f12c20;
top: 5rpx;
}
</style>

View File

@@ -0,0 +1,87 @@
<template>
<view class="custom-tabbar fix flex-center" :class="[{'fill-base b-1px-t':configInfo.tabBar[1].name}]">
<block v-if="configInfo.tabBar[1].name">
<view @tap.stop="changeTab(index)" class="flex-center flex-column mt-sm"
:style="{width: 100/configInfo.tabBar.length + '%'}" v-for="(item,index) in configInfo.tabBar"
:key="index">
<image mode="aspectFill" lazy-load class="cover"
:src="cur == index ? item.selected_img : item.default_img"></image>
<view class="text" :style="{color:cur == index ? primaryColor : '#666'}">{{item.name}}</view>
</view>
</block>
</view>
</template>
<script>
import {
mapState,
mapMutations
} from "vuex"
export default {
components: {},
props: {
cur: {
type: String,
default () {
return '0'
}
},
},
data() {
return {}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
commonOptions: state => state.user.commonOptions,
}),
created() {
let that = this;
let sysheight = uni.getSystemInfoSync().windowHeight
let configInfo = JSON.parse(JSON.stringify(this.configInfo))
let {
navBarHeight
} = configInfo
const query = uni.createSelectorQuery().in(this);
query.select('.custom-tabbar').boundingClientRect(data => {
let curSysHeight = sysheight - data.height - navBarHeight
configInfo.curSysHeight = curSysHeight
configInfo.tabbarHeight = data.height
this.updateConfigItem({
key: 'configInfo',
val: configInfo
})
}).exec();
},
methods: {
...mapMutations(['updateConfigItem']),
// 点击跳转
changeTab(index) {
this.$emit('change',
index
);
},
},
}
</script>
<style scoped lang="scss">
.custom-tabbar {
height: 98rpx;
bottom: 0;
height: calc(98rpx + env(safe-area-inset-bottom) / 2);
padding-bottom: calc(env(safe-area-inset-bottom) / 2);
.cover {
width: 44rpx;
height: 44rpx;
}
.text {
font-size: 22rpx;
margin-top: 5rpx;
height: 32rpx;
}
}
</style>

View File

@@ -0,0 +1,134 @@
<template>
<view>
<view class='record-box fill-base radius-24'>
<view class='record-item' :class="[{'b-1px-l': index != list.length -1}]" v-for="(item,index) in list"
:key="index">
<view class='c-caption rel'>
<text class="item-tag abs" :class="[{'cur':info.pay_type > item.pay_type -1}]"
:style="{border:`5rpx solid ${primaryColor}`,background: info.pay_type > item.pay_type -1 ? activeColor : ''}"></text>
<view class="item-text f-paragraph c-title flex-y-baseline">
{{item[type]}}
</view>
</view>
<view class="f-caption c-caption">{{item.time_text}}</view>
<image @tap.stop="toPreviewImage(index)" mode="aspectFill" class="item-img mt-sm radius-10"
:src="item.cover">
</image>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'timeline',
props: {
list: {
type: Object,
default () {
return {}
}
},
info: {
type: Object,
default () {
return {}
}
},
type: {
type: String,
default () {
return ''
}
},
activeColor: {
type: String,
default () {
return '#39b54a'
}
},
primaryColor: {
type: String,
default () {
return '#39b54a'
}
}
},
created() {
},
data() {
return {
}
},
methods: {
toPreviewImage(index) {
let {
cover: current
} = this.list[index]
let urls = this.list.map(item => {
return item.cover
})
this.$util.previewImage({
current,
urls
})
},
async toMap(key) {
let {
info
} = this
await this.$util.checkAuth({
type: 'userLocation'
})
await uni.getLocation({
type: 'gcj02',
})
await uni.openLocation({
latitude: info[`${key}_lat`] * 1,
longitude: info[`${key}_lng`] * 1,
name: info[`${key}_address`],
scale: 28
})
},
},
}
</script>
<style lang="scss">
.record-item {
padding: 0rpx 0rpx 30rpx 40rpx;
margin-left: 13rpx;
.item-tag {
width: 19rpx;
height: 19rpx;
background: #EEF5F2;
border: 5rpx solid #39b54a;
transform: rotateZ(360deg);
border-radius: 50%;
top: 0rpx;
left: -52rpx;
}
.item-text {
line-height: 34rpx;
}
.item-img {
width: 158rpx;
height: 120rpx;
background: #f4f6f8;
}
}
.record-item.b-1px-l::before {
border: 1rpx solid #DBDBDB;
transform: rotateZ(360deg);
}
.record-item:nth-child(5) {
padding-bottom: 0;
}
</style>

View File

@@ -0,0 +1,256 @@
<template>
<view class="uni-navbar" :class="{'uni-navbar-fixed':isFixed,'uni-navbar-shadow':hasShadow}"
:style="{backgroundColor:backgroundColor,height:navBarHeight+'px'}">
<uni-status-bar v-if="insertStatusBar"></uni-status-bar>
<view class="uni-navbar-header" :style="{color:color}">
<view class="uni-navbar-header-btns left" @tap="onClickLeft">
<slot name="left"></slot>
<view v-if="leftIcon || leftText" class="uni-navbar-btn-text"><i class="iconfont" :class="leftIcon"
v-if="leftIcon"></i>{{leftText || ''}}</view>
</view>
<block v-if="!onlyLeft">
<view class="uni-navbar-container" :class="[{'flex-center':title},{'flex-y-center':image}]">
<view v-if="title" class="uni-navbar-container-title ellipsis">{{title}}</view>
<image mode="aspectFill" lazy-load class="seckill-nav" :src="image" v-if="image">
</image>
<!-- 标题插槽 -->
<slot></slot>
</view>
<view class="uni-navbar-header-btns right" @tap="onClickRight">
<view v-if="rightText" class="uni-navbar-btn-text">{{rightText}}</view>
<slot name="right"></slot>
</view>
</block>
</view>
</view>
</template>
<script>
import uniStatusBar from '@/components/uni-status-bar.vue';
export default {
components: {
uniStatusBar
},
props: {
/**
* 标题文字
*/
title: {
type: String,
default: ''
},
/**
* 标题图片
*/
image: {
type: String,
default: ''
},
/**
* 左侧按钮图标
*/
leftIcon: {
type: String,
default: ''
},
/**
* 左侧按钮文本
*/
leftText: {
type: String,
default: ''
},
/**
* 右侧按钮文本
*/
rightText: {
type: String,
default: ''
},
/**
* 是否固定在顶部
*/
fixed: {
type: [Boolean, String],
default: false
},
/**
* 按钮图标和文字颜色
*/
color: {
type: String,
default: '#000'
},
/**
* 背景颜色
*/
backgroundColor: {
type: String,
default: '#FFF'
},
/**
* 是否仅有左侧
*/
onlyLeft: {
type: [Boolean],
default: false
},
/**
* 是否包含状态栏,默认固定在顶部时包含
*/
statusBar: {
type: [Boolean, String],
default: ''
},
/**
* 是否使用阴影,默认根据背景色判断
*/
shadow: {
type: Boolean,
default () {
return false
}
},
},
computed: {
isFixed() {
return String(this.fixed) === 'true'
},
insertStatusBar() {
switch (String(this.statusBar)) {
case 'true':
return true
case 'false':
return false
default:
return this.isFixed
}
},
hasShadow() {
var backgroundColor = this.backgroundColor
switch (this.shadow) {
case true:
return true
case false:
return false
default:
return backgroundColor !== 'transparent' && backgroundColor.indexOf('rgba') < 0
}
}
},
data() {
return {
navBarHeight: uni.getSystemInfoSync().statusBarHeight * 1 + 44
}
},
methods: {
/**
* 左侧按钮点击事件
*/
onClickLeft() {
let {
leftIcon = ''
} = this
if (leftIcon == 'icon-left') {
this.$util.goUrl({
url: 1,
openType: `navigateBack`
})
} else if (leftIcon == 'iconshouye') {
this.$util.goUrl({
url: `/pages/home`,
openType: 'reLaunch'
})
} else {
this.$emit('clickLeft')
this.$emit('click-left')
}
},
/**
* 右侧按钮点击事件
*/
onClickRight() {
this.$emit('clickRight')
this.$emit('click-right')
}
}
}
</script>
<style>
.uni-navbar {
display: block;
position: relative;
width: 100%;
overflow: hidden;
}
.uni-navbar-shadow {
box-shadow: 0 1px 6px #ccc;
}
.uni-navbar.uni-navbar-fixed {
position: fixed;
z-index: 999999999;
}
.uni-navbar-header {
display: flex;
flex-direction: row;
width: 100%;
/* #ifdef MP-BAIDU */
height: 38px;
line-height: 38px;
font-size: 15px;
/* #endif */
/* #ifndef MP-BAIDU */
height: 44px;
line-height: 44px;
font-size: 14px;
/* #endif */
}
.uni-navbar-header-btns {
display: inline-flex;
flex-wrap: nowrap;
flex-shrink: 0;
width: 100px;
}
.uni-navbar-header-btns.left {
padding-left: 30rpx;
}
.uni-navbar-header-btns.right {
padding-right: 30rpx;
}
.uni-navbar-btn-text {
display: flex;
align-items: center;
}
.uni-navbar-btn-text.iconfont {
font-size: 40rpx;
}
.uni-navbar-container {
width: 100%;
margin: 0 5px;
}
.uni-navbar-container-title {
/* #ifdef MP-BAIDU */
height: 38px;
line-height: 38px;
/* #endif */
/* #ifndef MP-BAIDU */
height: 44px;
line-height: 44px;
/* #endif */
font-size: 15px;
max-width: 330rpx;
/* text-align: center; */
/* padding-right: 30px; */
}
</style>

View File

@@ -0,0 +1,196 @@
<template>
<view v-if="showPopup" class="uni-popup">
<view :class="[ani, animation ? 'ani' : '', !custom ? 'uni-custom' : '']" class="uni-popup__mask"
@click="close(true)" />
<view :class="[type, ani, animation ? 'ani' : '', !custom ? 'uni-custom' : '']" class="uni-popup__wrapper"
@click="close(true)">
<view class="uni-popup__wrapper-box" @click.stop="clear">
<slot />
</view>
</view>
</view>
</template>
<script>
export default {
name: 'UniPopup',
props: {
// 开启动画
animation: {
type: Boolean,
default: true
},
// 弹出层类型可选值top: 顶部弹出层bottom底部弹出层center全屏弹出层
type: {
type: String,
default: 'center'
},
// 是否开启自定义
custom: {
type: Boolean,
default: false
},
maskClick: {
type: Boolean,
default: true
},
show: {
type: Boolean,
default: true
}
},
data() {
return {
ani: '',
showPopup: false
}
},
watch: {
show(newValue) {
if (newValue) {
this.open()
} else {
this.close()
}
}
},
created() {},
methods: {
clear() {},
open() {
this.$emit('change', {
show: true
})
this.showPopup = true
this.$nextTick(() => {
setTimeout(() => {
this.ani = 'uni-' + this.type
}, 30)
})
},
close(type) {
if (!this.maskClick && type) return
this.$emit('change', {
show: false
})
this.ani = ''
this.$nextTick(() => {
setTimeout(() => {
this.showPopup = false
}, 300)
})
}
}
}
</script>
<style lang="scss">
@charset "UTF-8";
.uni-popup {
position: fixed;
top: 0;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 998;
overflow: hidden
}
.uni-popup__mask {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 998;
background: rgba(0, 0, 0, .4);
opacity: 0
}
.uni-popup__mask.ani {
transition: all .3s
}
.uni-popup__mask.uni-bottom,
.uni-popup__mask.uni-center,
.uni-popup__mask.uni-top {
opacity: 1
}
.uni-popup__wrapper {
position: absolute;
z-index: 999;
box-sizing: border-box
}
.uni-popup__wrapper.ani {
transition: all .3s
}
.uni-popup__wrapper.top {
top: 0;
left: 0;
width: 100%;
transform: translateY(-100%)
}
.uni-popup__wrapper.bottom {
bottom: 0;
left: 0;
width: 100%;
transform: translateY(100%)
}
.uni-popup__wrapper.center {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
transform: scale(1.2);
opacity: 0
}
.uni-popup__wrapper-box {
position: relative;
box-sizing: border-box
}
.uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
/* padding: 30upx; */
// background: #fff
}
.uni-popup__wrapper.uni-custom.center .uni-popup__wrapper-box {
position: relative;
/* max-width: 80%;
max-height: 80%; */
overflow-y: scroll;
border-radius: 25rpx;
}
.uni-popup__wrapper.uni-custom.bottom .uni-popup__wrapper-box {
width: 100%;
// max-height: 500px;
overflow-y: scroll;
border-radius: 25rpx 25rpx 0 0;
}
.uni-popup__wrapper.uni-custom.top .uni-popup__wrapper-box {
width: 100%;
// max-height: 500px;
overflow-y: scroll;
border-radius: 0 0 25rpx 25rpx;
}
.uni-popup__wrapper.uni-bottom,
.uni-popup__wrapper.uni-top {
transform: translateY(0)
}
.uni-popup__wrapper.uni-center {
transform: scale(1);
opacity: 1
}
</style>

View File

@@ -0,0 +1,160 @@
<template>
<view class="segmented-control" :class="styleType" :style="wrapStyle">
<view v-for="(item, index) in values" class="segmented-control-item" :class="styleType" :key="index"
:style="index === currentIndex ? activeStyle : itemStyle" @click="onClick(index)">
{{item.title}}
</view>
</view>
</template>
<script>
export default {
name: 'uni-segmented-control',
props: {
current: {
type: Number,
default: 0
},
values: {
type: Array,
default () {
return [];
}
},
activeColor: {
type: String,
default: '#007aff'
},
styleType: {
type: String,
default: 'button'
},
haveBorder: {
type: Boolean,
default () {
return false
}
},
lockTap: {
type: Boolean,
default () {
return true
}
}
},
data() {
return {
currentIndex: this.current
}
},
watch: {
current(val) {
if (val !== this.currentIndex) {
this.currentIndex = val;
}
}
},
computed: {
wrapStyle() {
let styleString = '';
switch (this.styleType) {
case 'text':
styleString = `border:0;`;
break;
default:
styleString = this.haveBorder ? `border: 1rpx solid ${this.activeColor};` : ``;
break;
}
return styleString;
},
itemStyle() {
let styleString = '';
switch (this.styleType) {
case 'text':
styleString = `color:#000;border-left:0;`;
break;
default:
styleString = `color:#222;background:#eddbba;border-color:#fff;`;
break;
}
return styleString;
},
activeStyle() {
let styleString = '';
switch (this.styleType) {
case 'text':
styleString = `color:${this.activeColor};border-left:0;border-bottom-style:solid;`;
break;
default:
styleString = `color:#fff;border-color:${this.activeColor};background-color:${this.activeColor}`;
break;
}
return styleString;
}
},
methods: {
onClick(index) {
let {
lockTap,
currentIndex
} = this
console.log(lockTap, currentIndex);
if (currentIndex !== index || !lockTap) {
this.currentIndex = index;
this.$emit('clickItem', index);
}
}
},
}
</script>
<style>
.segmented-control {
display: flex;
flex-direction: row;
justify-content: center;
font-size: 26rpx;
border-radius: 5rpx;
box-sizing: border-box;
margin: 0 auto;
overflow: hidden;
}
.segmented-control.button {
border-radius: 54rpx;
box-sizing: border-box;
}
.segmented-control.text {
border: 0;
border-radius: 0rpx;
}
.segmented-control-item {
flex: 1;
text-align: center;
line-height: 54rpx;
box-sizing: border-box;
}
.segmented-control-item.button {
border-left: 1upx solid;
}
.segmented-control-item.button:first-child {
border-radius: 54rpx 0 0 54rpx;
}
.segmented-control-item.button:last-child {
border-radius: 0 54rpx 54rpx 0;
}
.segmented-control-item.text {
border-left: 0;
}
.segmented-control-item:first-child {
border-left-width: 0;
}
</style>

View File

@@ -0,0 +1,29 @@
<template>
<view class="uni-status-bar" :style="style">
<slot></slot>
</view>
</template>
<script>
export default {
computed: {
style() {
//#ifdef APP-PLUS
return ''
//#endif
//#ifndef APP-PLUS
return `height:${uni.getSystemInfoSync().statusBarHeight}px`
//#endif
}
}
}
</script>
<style>
.uni-status-bar {
display: block;
width: 100%;
height: 20px;
height: var(--status-bar-height);
}
</style>

View File

@@ -0,0 +1,326 @@
<template>
<view
:class="[{'flex-warp':!imgTypeList.includes(imgclass) || !imgclass},{'flex-center flex-column':imgTypeList.includes(imgclass)}]">
<block v-for="(item,index) in imagelist" :key="index">
<view class="rel item-child radius-16" :class="[imgclass,{'margin border': imgsize > 1}]">
<image mode="aspectFill" lazy-load @tap="previewImage(item,imagelist)" class="upload-img radius-16"
:src="item.path" v-if="filetype == 'picture'"></image>
<video :id="`video_${index}`" class="ipload-video radius-16" :loop="false" enable-play-gesture
enable-progress-gesture :show-center-play-btn="true" :controls="true" :src="item.path"
:data-id="item.id" objectFit="cover" :data-index="index" @play="onPlay" @pause="onPause"
@ended="onEnded" @timeupdate="onTimeUpdate" @waiting="onWaiting" @progress="onProgress"
@loadedmetadata="onLoadedMetaData" v-if="filetype == 'video'">
</video>
<block v-if="imgauth">
<block v-if="imgsize>1">
<view @tap="toDel(index)" class="guanbi abs flex-center" :class="[imgclass]"
style="z-index: 1;"><i class="iconfont icon-add rotate-45 c-base"></i></view>
</block>
<block v-else>
<view @tap="chooseImage"
class="flex-center flex-column item-child border upload-item radius-16 abs"
:class="[imgclass]" style="top:0;margin-top:0;background:rgba(0,0,0,0.5);">
<view class="upload-icon flex-center c-base radius-16">
<i class="iconfont icon-camera"></i>
</view>
<view class="f-caption c-base mt-sm">重新上传</view>
</view>
</block>
</block>
</view>
</block>
<view @tap="chooseImage" class="radius-16 flex-center flex-column item-child border rel upload-item fill-body"
:class="[imgclass,{'margin': imgsize > 1}]" v-if="imgauth && imagelist.length < imgsize">
<image mode="aspectFill" lazy-load class="item-child md bg-img abs radius-16"
style="width: 292rpx;height: 200rpx;" :src="bgimg" v-if="bgimg">
</image>
<block v-else>
<view class="upload-icon flex-center c-desc radius-16">
<i class="iconfont icon-camera"></i>
</view>
<view class="f-caption c-caption mt-sm" v-if="text">{{text}}</view>
<view class="cur-imgsize f-caption c-caption" v-if="imgsize>1">{{`${imagelist.length}/${imgsize}`}}
</view>
</block>
</view>
</view>
</template>
<script>
import {
mapState,
mapActions,
} from 'vuex';
export default {
props: {
// 图片列表
imagelist: {
type: Array,
default () {
return []
}
},
// 图片参数名
imgtype: {
type: String,
default () {
return ''
}
},
// 图片张数
imgsize: {
type: Number,
default () {
return 9
}
},
// 上传类型
filetype: {
type: String,
default () {
return 'picture'
}
},
// 图片样式
imgclass: {
type: String,
default () {
return ''
}
},
// 是否有权限
imgauth: {
type: Boolean,
default () {
return true
}
},
// 备注信息
text: {
type: String,
default () {
return ''
}
},
// 默认背景图
bgimg: {
type: String,
default () {
return ''
}
}
},
data() {
return {
imgTypeList: ['md', 'lg'],
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
}),
methods: {
previewImage(current, urls) {
let res_urls = [];
urls = this.$util.deepCopy(urls);
urls.forEach((item, index) => {
res_urls.push(item.path)
})
uni.previewImage({
current: current.path,
urls: res_urls,
})
},
async toDel(index) {
let fileName = this.filetype == 'picture' ? '图片' : '视频'
let [res_del, {
confirm
}] = await uni.showModal({
content: `请确认是否要删除${fileName}`,
})
if (!confirm) return;
this.imagelist.splice(index, 1);
this.$emit('del', {
imgtype: this.imgtype,
imagelist: this.imagelist
});
},
async chooseImage() {
let {
imgtype,
imgsize,
filetype
} = this;
let imagelist = this.$util.deepCopy(this.imagelist)
let is_upload_img = filetype == 'picture'
let chooseModel = is_upload_img ? 'chooseImage' : 'chooseVideo'
let param = {
count: imgsize - imagelist.length * 1,
}
if (is_upload_img) {
param.sizeType = ['compressed']
}
let [res_upload, res_info] = await uni[chooseModel](param)
if (res_upload) return
let {
size = 0,
tempFiles,
tempFilePath = ''
} = res_info
if (filetype == 'video' && size / 1024 / 1024 > 100) {
this.$util.showToast({
title: `上传视频大小超过限制100M`
})
return
}
let filePath = [];
// 格式化图片参数
this.$util.showLoading({
title: "上传中"
});
if (is_upload_img) {
for (let i = 0; i < tempFiles.length; i++) {
let {
attachment_path: path
} = await this.$api.base.uploadFile({
filePath: tempFiles[i].path,
filetype
})
if (imgsize > 1) {
imagelist.push({
path
})
} else {
imagelist = [{
path
}]
}
}
} else {
let path = await this.$api.base.uploadVideo({
filePath: tempFilePath,
filetype
})
console.log(path, "=====video path");
imagelist.push({
path
})
}
this.$util.hideAll()
this.$emit('upload', {
imgtype,
imagelist
});
},
onPlay(e) {},
onPause(e) {},
onEnded(e) {},
onTimeUpdate(e) {},
onWaiting(e) {},
onProgress(e) {},
onLoadedMetaData(e) {},
}
}
</script>
<style lang="scss">
.item-child {
width: 200rpx;
height: 200rpx;
background: #fff;
margin: 20rpx 0;
}
.border {
border: 1rpx dashed #ccc;
transform: rotateZ(360deg);
}
.item-child.bg-img {
top: 0;
margin: 0;
overflow: hidden;
}
.margin {
margin: 20rpx 20rpx 0 0;
}
.item-child:nth-child(3n) {
margin-right: 0rpx;
}
.item-child.sm {
width: 140rpx;
height: 140rpx;
}
.item-child.mini {
width: 196rpx;
height: 196rpx;
}
.item-child.md {
width: 294rpx;
height: 202rpx;
margin-bottom: 20rpx;
overflow: hidden;
}
.item-child.lg {
width: 690rpx;
height: 400rpx;
}
.upload-img {
width: 100%;
height: 100%;
}
.upload-item {
.upload-icon {
// width: 80rpx;
// height: 76rpx;
// background: #FFFFFF;
.iconfont {
font-size: 70rpx;
display: block;
// color: #BBBBBB;
}
}
.cur-imgsize {
line-height: 1.1;
}
}
.upload-item.margin {
margin-bottom: 0;
}
.guanbi {
width: 32rpx;
height: 32rpx;
background: rgba(0, 0, 0, 0.2);
border-radius: 0 10rpx 0 0;
top: 0rpx;
right: 0rpx;
z-index: 1;
.iconfont {
font-size: 28rpx;
}
}
.guanbi.lg {
width: 50rpx;
height: 50rpx;
.iconfont {
font-size: 38rpx;
}
}
</style>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,742 @@
<template>
<view class="w-picker-view">
<picker-view v-if="fields=='year'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='month'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='day'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='hour'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='minute'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.minutes" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='second'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.minutes" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.seconds" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:{
years:[],
months:[],
days:[],
hours:[],
minutes:[],
seconds:[]
},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
startYear:{
type:[String,Number],
default:""
},
endYear:{
type:[String,Number],
default:""
},
value:{
type:[String,Array,Number],
default:""
},
current:{//是否默认选中当前日期
type:Boolean,
default:false
},
disabledAfter:{//是否禁用当前之后的日期
type:Boolean,
default:false
},
fields:{
type:String,
default:"day"
}
},
watch:{
fields(val){
this.initData();
},
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
formatNum(n){
return (Number(n)<10?'0'+Number(n):Number(n)+'');
},
checkValue(value){
let strReg,example
switch(this.fields){
case "year":
strReg=/^\d{4}$/;
example="2019";
break;
case "month":
strReg=/^\d{4}-\d{2}$/;
example="2019-02";
break;
case "day":
strReg=/^\d{4}-\d{2}-\d{2}$/;
example="2019-02-01";
break;
case "hour":
strReg=/^\d{4}-\d{2}-\d{2} \d{2}(:\d{2}){1,2}?$/;
example="2019-02-01 18:00:00或2019-02-01 18";
break;
case "minute":
strReg=/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}(:\d{2}){0,1}?$/;
example="2019-02-01 18:06:00或2019-02-01 18:06";
break;
case "second":
strReg=/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
example="2019-02-01 18:06:01";
break;
}
if(!strReg.test(value)){
console.log(new Error("请传入与modefields匹配的value值例value="+example+""))
}
return strReg.test(value);
},
resetData(year,month,day,hour,minute){
let curDate=this.getCurrenDate();
let curFlag=this.current;
let curYear=curDate.curYear;
let curMonth=curDate.curMonth;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let curMinute=curDate.curMinute;
let curSecond=curDate.curSecond;
let months=[],days=[],hours=[],minutes=[],seconds=[];
let disabledAfter=this.disabledAfter;
let monthsLen=disabledAfter?(year*1<curYear?12:curMonth):12;
let totalDays=new Date(year,month,0).getDate();//计算当月有几天;
let daysLen=disabledAfter?((year*1<curYear||month*1<curMonth)?totalDays:curDay):totalDays;
let hoursLen=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay)?24:curHour+1):24;
let minutesLen=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay||hour*1<curHour)?60:curMinute+1):60;
let secondsLen=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay||hour*1<curHour||minute*1<curMinute)?60:curSecond+1):60;
for(let month=1;month<=monthsLen;month++){
months.push(this.formatNum(month));
};
for(let day=1;day<=daysLen;day++){
days.push(this.formatNum(day));
}
for(let hour=0;hour<hoursLen;hour++){
hours.push(this.formatNum(hour));
}
for(let minute=0;minute<minutesLen;minute++){
minutes.push(this.formatNum(minute));
}
for(let second=0;second<secondsLen;second++){
seconds.push(this.formatNum(second));
}
return{
months,
days,
hours,
minutes,
seconds
}
},
isLeapYear (Year) {
if (((Year % 4)==0) && ((Year % 100)!=0) || ((Year % 400)==0)) {
return true;
} else {
return false;
}
},
getData(dVal){
//用来处理初始化数据
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let fields=this.fields;
let curDate=this.getCurrenDate();
let curYear=curDate.curYear;
let curMonthdays=curDate.curMonthdays;
let curMonth=curDate.curMonth;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let curMinute=curDate.curMinute;
let curSecond=curDate.curSecond;
let defaultDate=this.getDefaultDate();
let startYear=this.getStartDate().getFullYear();
let endYear=this.getEndDate().getFullYear();
//颗粒度禁用当前之后日期仅对year,month,day,hour生效;分钟秒禁用没有意义,
let years=[],months=[],days=[],hours=[],minutes=[],seconds=[];
let year=dVal[0]*1;
let month=dVal[1]*1;
let day=dVal[2]*1;
let hour=dVal[3]*1;
let minute=dVal[4]*1;
let monthsLen=disabledAfter?(year<curYear?12:curDate.curMonth):12;
let daysLen=disabledAfter?((year<curYear||month<curMonth)?defaultDate.defaultDays:curDay):(curFlag?curMonthdays:defaultDate.defaultDays);
let hoursLen=disabledAfter?((year<curYear||month<curMonth||day<curDay)?24:curHour+1):24;
let minutesLen=disabledAfter?((year<curYear||month<curMonth||day<curDay||hour<curHour)?60:curMinute+1):60;
let secondsLen=disabledAfter?((year<curYear||month<curMonth||day<curDay||hour<curHour||minute<curMinute)?60:curSecond+1):60;
for(let year=startYear;year<=(disabledAfter?curYear:endYear);year++){
years.push(year.toString())
}
for(let month=1;month<=monthsLen;month++){
months.push(this.formatNum(month));
}
for(let day=1;day<=daysLen;day++){
days.push(this.formatNum(day));
}
for(let hour=0;hour<hoursLen;hour++){
hours.push(this.formatNum(hour));
}
for(let minute=0;minute<minutesLen;minute++){
minutes.push(this.formatNum(minute));
}
// for(let second=0;second<(disabledAfter?curDate.curSecond+1:60);second++){
// seconds.push(this.formatNum(second));
// }
for(let second=0;second<60;second++){
seconds.push(this.formatNum(second));
}
return {
years,
months,
days,
hours,
minutes,
seconds
}
},
getCurrenDate(){
let curDate=new Date();
let curYear=curDate.getFullYear();
let curMonth=curDate.getMonth()+1;
let curMonthdays=new Date(curYear,curMonth,0).getDate();
let curDay=curDate.getDate();
let curHour=curDate.getHours();
let curMinute=curDate.getMinutes();
let curSecond=curDate.getSeconds();
return{
curDate,
curYear,
curMonth,
curMonthdays,
curDay,
curHour,
curMinute,
curSecond
}
},
getDefaultDate(){
let value=this.value;
let reg=/-/g;
let defaultDate=value?new Date(value.replace(reg,"/")):new Date();
let defaultYear=defaultDate.getFullYear();
let defaultMonth=defaultDate.getMonth()+1;
let defaultDay=defaultDate.getDate();
let defaultDays=new Date(defaultYear,defaultMonth,0).getDate()*1;
return{
defaultDate,
defaultYear,
defaultMonth,
defaultDay,
defaultDays
}
},
getStartDate(){
let start=this.startYear;
let startDate="";
let reg=/-/g;
if(start){
startDate=new Date(start+"/01/01");
}else{
startDate=new Date("1970/01/01");
}
return startDate;
},
getEndDate(){
let end=this.endYear;
let reg=/-/g;
let endDate="";
if(end){
endDate=new Date(end+"/12/01");
}else{
endDate=new Date();
}
return endDate;
},
getDval(){
let value=this.value;
let fields=this.fields;
let dVal=null;
let aDate=new Date();
let year=this.formatNum(aDate.getFullYear());
let month=this.formatNum(aDate.getMonth()+1);
let day=this.formatNum(aDate.getDate());
let hour=this.formatNum(aDate.getHours());
let minute=this.formatNum(aDate.getMinutes());
let second=this.formatNum(aDate.getSeconds());
if(value){
let flag=this.checkValue(value);
if(!flag){
dVal=[year,month,day,hour,minute,second]
}else{
switch(this.fields){
case "year":
dVal=value?[value]:[];
break;
case "month":
dVal=value?value.split("-"):[];
break;
case "day":
dVal=value?value.split("-"):[];
break;
case "hour":
dVal=[...value.split(" ")[0].split("-"),...value.split(" ")[1].split(":")];
break;
case "minute":
dVal=value?[...value.split(" ")[0].split("-"),...value.split(" ")[1].split(":")]:[];
break;
case "second":
dVal=[...value.split(" ")[0].split("-"),...value.split(" ")[1].split(":")];
break;
}
}
}else{
dVal=[year,month,day,hour,minute,second]
}
return dVal;
},
initData(){
let startDate,endDate,startYear,endYear,startMonth,endMonth,startDay,endDay;
let years=[],months=[],days=[],hours=[],minutes=[],seconds=[];
let dVal=[],pickVal=[];
let value=this.value;
let reg=/-/g;
let range={};
let result="",full="",year,month,day,hour,minute,second,obj={};
let defaultDate=this.getDefaultDate();
let defaultYear=defaultDate.defaultYear;
let defaultMonth=defaultDate.defaultMonth;
let defaultDay=defaultDate.defaultDay;
let defaultDays=defaultDate.defaultDays;
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let curDate=this.getCurrenDate();
let curYear=curDate.curYear;
let curMonth=curDate.curMonth;
let curMonthdays=curDate.curMonthdays;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let curMinute=curDate.curMinute;
let curSecond=curDate.curSecond;
let dateData=[];
dVal=this.getDval();
startDate=this.getStartDate();
endDate=this.getEndDate();
startYear=startDate.getFullYear();
startMonth=startDate.getMonth();
startDay=startDate.getDate();
endYear=endDate.getFullYear();
endMonth=endDate.getMonth();
endDay=endDate.getDate();
dateData=this.getData(dVal);
years=dateData.years;
months=dateData.months;
days=dateData.days;
hours=dateData.hours;
minutes=dateData.minutes;
seconds=dateData.seconds;
switch(this.fields){
case "year":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0
]:(curFlag?[
years.indexOf(curYear+'')
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0
]);
range={years};
year=dVal[0]?dVal[0]:years[0];
result=full=`${year}`;
obj={
year
}
break;
case "month":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth))
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0
]);
range={years,months};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
result=full=`${year+'-'+month}`;
obj={
year,
month
}
break;
case "day":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0
]);
range={years,months,days};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
result=full=`${year+'-'+month+'-'+day}`;
obj={
year,
month,
day
}
break;
case "hour":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
hours.indexOf(this.formatNum(curHour)),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0
]);
range={years,months,days,hours};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
hour=dVal[3]?dVal[3]:hours[0];
result=`${year+'-'+month+'-'+day+' '+hour}`;
full=`${year+'-'+month+'-'+day+' '+hour+':00:00'}`;
obj={
year,
month,
day,
hour
}
break;
case "minute":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
hours.indexOf(this.formatNum(curHour)),
minutes.indexOf(this.formatNum(curMinute)),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0
]);
range={years,months,days,hours,minutes};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
hour=dVal[3]?dVal[3]:hours[0];
minute=dVal[4]?dVal[4]:minutes[0];
full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':00'}`;
result=`${year+'-'+month+'-'+day+' '+hour+':'+minute}`;
obj={
year,
month,
day,
hour,
minute
}
break;
case "second":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0,
dVal[5]&&seconds.indexOf(dVal[5])!=-1?seconds.indexOf(dVal[5]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
hours.indexOf(this.formatNum(curHour)),
minutes.indexOf(this.formatNum(curMinute)),
seconds.indexOf(this.formatNum(curSecond)),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0,
dVal[5]&&seconds.indexOf(dVal[5])!=-1?seconds.indexOf(dVal[5]):0
]);
range={years,months,days,hours,minutes,seconds};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
hour=dVal[3]?dVal[3]:hours[0];
minute=dVal[4]?dVal[4]:minutes[0];
second=dVal[5]?dVal[5]:seconds[0];
result=full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':'+second}`;
obj={
year,
month,
day,
hour,
minute,
second
}
break;
default:
range={years,months,days};
break;
}
this.range=range;
this.checkObj=obj;
this.$emit("change",{
result:result,
value:full,
obj:obj
});
this.$nextTick(()=>{
this.pickVal=pickVal;
})
},
handlerChange(e){
let arr=[...e.detail.value];
let data=this.range;
let year="",month="",day="",hour="",minute="",second="";
let result="",full="",obj={};
let months=null,days=null,hours=null,minutes=null,seconds=null;
let disabledAfter=this.disabledAfter;
let leapYear=false,resetData={};
year=(arr[0]||arr[0]==0)?data.years[arr[0]]||data.years[data.years.length-1]:"";
month=(arr[1]||arr[1]==0)?data.months[arr[1]]||data.months[data.months.length-1]:"";
day=(arr[2]||arr[2]==0)?data.days[arr[2]]||data.days[data.days.length-1]:"";
hour=(arr[3]||arr[3]==0)?data.hours[arr[3]]||data.hours[data.hours.length-1]:"";
minute=(arr[4]||arr[4]==0)?data.minutes[arr[4]]||data.minutes[data.minutes.length-1]:"";
second=(arr[5]||arr[5]==0)?data.seconds[arr[5]]||data.seconds[data.seconds.length-1]:"";
resetData=this.resetData(year,month,day,hour,minute);//重新拉取当前日期数据;
leapYear=this.isLeapYear(year);//判断是否为闰年;
switch(this.fields){
case "year":
result=full=`${year}`;
obj={
year
};
break;
case "month":
result=full=`${year+'-'+month}`;
if(this.disabledAfter)months=resetData.months;
if(months)this.range.months=months;
obj={
year,
month
}
break;
case "day":
result=full=`${year+'-'+month+'-'+day}`;
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
}else{
if(leapYear||(month!=this.checkObj.month)||month==2){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
obj={
year,
month,
day
}
break;
case "hour":
result=`${year+'-'+month+'-'+day+' '+hour}`;
full=`${year+'-'+month+'-'+day+' '+hour+':00:00'}`;
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
hours=resetData.hours;
}else{
if(leapYear||(month!=this.checkObj.month)||month==2){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
if(hours)this.range.hours=hours;
obj={
year,
month,
day,
hour
}
break;
case "minute":
full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':00'}`;
result=`${year+'-'+month+'-'+day+' '+hour+':'+minute}`;
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
hours=resetData.hours;
minutes=resetData.minutes;
}else{
if(leapYear||(month!=this.checkObj.month)||month==2){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
if(hours)this.range.hours=hours;
if(minutes)this.range.minutes=minutes;
obj={
year,
month,
day,
hour,
minute
};
break;
case "second":
result=full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':'+second}`;
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
hours=resetData.hours;
minutes=resetData.minutes;
//seconds=resetData.seconds;
}else{
if(leapYear||(month!=this.checkObj.month)||month==2){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
if(hours)this.range.hours=hours;
if(minutes)this.range.minutes=minutes;
//if(seconds)this.range.seconds=seconds;
obj={
year,
month,
day,
hour,
minute,
second
}
break;
}
this.checkObj=obj;
this.$emit("change",{
result:result,
value:full,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

View File

@@ -0,0 +1,345 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.sections" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:{},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
startYear:{
type:String,
default:""
},
endYear:{
type:String,
default:""
},
value:{
type:[String,Array,Number],
default:""
},
current:{//是否默认选中当前日期
type:Boolean,
default:false
},
disabledAfter:{//是否禁用当前之后的日期
type:Boolean,
default:false
}
},
watch:{
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
formatNum(n){
return (Number(n)<10?'0'+Number(n):Number(n)+'');
},
checkValue(value){
let strReg=/^\d{4}-\d{2}-\d{2} [\u4e00-\u9fa5]{2}$/,example;
if(!strReg.test(value)){
console.log(new Error("请传入与modefields匹配的value值例value="+example+""))
}
return strReg.test(value);
},
resetData(year,month,day){
let curDate=this.getCurrenDate();
let curFlag=this.current;
let curYear=curDate.curYear;
let curMonth=curDate.curMonth;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let months=[],days=[],sections=[];
let disabledAfter=this.disabledAfter;
let monthsLen=disabledAfter?(year*1<curYear?12:curMonth):12;
let totalDays=new Date(year,month,0).getDate();//计算当月有几天;
let daysLen=disabledAfter?((year*1<curYear||month*1<curMonth)?totalDays:curDay):totalDays;
let sectionFlag=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay)==true?false:true):(curHour>12==true?true:false);
sections=["上午","下午"];
for(let month=1;month<=monthsLen;month++){
months.push(this.formatNum(month));
};
for(let day=1;day<=daysLen;day++){
days.push(this.formatNum(day));
}
if(sectionFlag){
sections=["上午"];
}
return{
months,
days,
sections
}
},
getData(dVal){
//用来处理初始化数据
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let curDate=this.getCurrenDate();
let curYear=curDate.curYear;
let curMonthdays=curDate.curMonthdays;
let curMonth=curDate.curMonth;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let defaultDate=this.getDefaultDate();
let startYear=this.getStartDate().getFullYear();
let endYear=this.getEndDate().getFullYear();
let years=[],months=[],days=[],sections=[];
let year=dVal[0]*1;
let month=dVal[1]*1;
let day=dVal[2]*1;
let monthsLen=disabledAfter?(year<curYear?12:curDate.curMonth):12;
let daysLen=disabledAfter?((year<curYear||month<curMonth)?defaultDate.defaultDays:curDay):(curFlag?curMonthdays:defaultDate.defaultDays);
let sectionFlag=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay)==true?false:true):(curHour>12==true?true:false);
for(let year=startYear;year<=(disabledAfter?curYear:endYear);year++){
years.push(year.toString())
}
for(let month=1;month<=monthsLen;month++){
months.push(this.formatNum(month));
}
for(let day=1;day<=daysLen;day++){
days.push(this.formatNum(day));
}
if(sectionFlag){
sections=["下午"];
}else{
sections=["上午","下午"];
}
return {
years,
months,
days,
sections
}
},
getCurrenDate(){
let curDate=new Date();
let curYear=curDate.getFullYear();
let curMonth=curDate.getMonth()+1;
let curMonthdays=new Date(curYear,curMonth,0).getDate();
let curDay=curDate.getDate();
let curHour=curDate.getHours();
let curSection="上午";
if(curHour>=12){
curSection="下午";
}
return{
curDate,
curYear,
curMonth,
curMonthdays,
curDay,
curHour,
curSection
}
},
getDefaultDate(){
let value=this.value;
let reg=/-/g;
let defaultDate=value?new Date(value.split(" ")[0].replace(reg,"/")):new Date();
let defaultYear=defaultDate.getFullYear();
let defaultMonth=defaultDate.getMonth()+1;
let defaultDay=defaultDate.getDate();
let defaultDays=new Date(defaultYear,defaultMonth,0).getDate()*1;
return{
defaultDate,
defaultYear,
defaultMonth,
defaultDay,
defaultDays
}
},
getStartDate(){
let start=this.startYear;
let startDate="";
let reg=/-/g;
if(start){
startDate=new Date(start+"/01/01");
}else{
startDate=new Date("1970/01/01");
}
return startDate;
},
getEndDate(){
let end=this.endYear;
let reg=/-/g;
let endDate="";
if(end){
endDate=new Date(end+"/12/31");
}else{
endDate=new Date();
}
return endDate;
},
getDval(){
let value=this.value;
let dVal=null;
let aDate=new Date();
let year=this.formatNum(aDate.getFullYear());
let month=this.formatNum(aDate.getMonth()+1);
let day=this.formatNum(aDate.getDate());
let hour=aDate.getHours();
let section="上午";
if(hour>=12)section="下午";
if(value){
let flag=this.checkValue(value);
if(!flag){
dVal=[year,month,day,section]
}else{
let v=value.split(" ");
dVal=[...v[0].split("-"),v[1]];
}
}else{
dVal=[year,month,day,section]
}
return dVal;
},
initData(){
let startDate,endDate,startYear,endYear,startMonth,endMonth,startDay,endDay;
let years=[],months=[],days=[],sections=[];
let dVal=[],pickVal=[];
let value=this.value;
let reg=/-/g;
let range={};
let result="",full="",year,month,day,section,obj={};
let defaultDate=this.getDefaultDate();
let defaultYear=defaultDate.defaultYear;
let defaultMonth=defaultDate.defaultMonth;
let defaultDay=defaultDate.defaultDay;
let defaultDays=defaultDate.defaultDays;
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let curDate=this.getCurrenDate();
let curYear=curDate.curYear;
let curMonth=curDate.curMonth;
let curMonthdays=curDate.curMonthdays;
let curDay=curDate.curDay;
let curSection=curDate.curSection;
let dateData=[];
dVal=this.getDval();
startDate=this.getStartDate();
endDate=this.getEndDate();
startYear=startDate.getFullYear();
startMonth=startDate.getMonth();
startDay=startDate.getDate();
endYear=endDate.getFullYear();
endMonth=endDate.getMonth();
endDay=endDate.getDate();
dateData=this.getData(dVal);
years=dateData.years;
months=dateData.months;
days=dateData.days;
sections=dateData.sections;
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&sections.indexOf(dVal[3])!=-1?sections.indexOf(dVal[3]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
sections.indexOf(curSection),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&sections.indexOf(dVal[3])!=-1?sections.indexOf(dVal[3]):0
]);
range={years,months,days,sections};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
section=dVal[3]?dVal[3]:sections[0];
result=full=`${year+'-'+month+'-'+day+' '+section}`;
obj={
year,
month,
day,
section
}
this.range=range;
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=pickVal;
});
this.$emit("change",{
result:result,
value:full,
obj:obj
})
},
handlerChange(e){
let arr=[...e.detail.value];
let data=this.range;
let year="",month="",day="",section="";
let result="",full="",obj={};
let months=null,days=null,sections=null;
let disabledAfter=this.disabledAfter;
year=(arr[0]||arr[0]==0)?data.years[arr[0]]||data.years[data.years.length-1]:"";
month=(arr[1]||arr[1]==0)?data.months[arr[1]]||data.months[data.months.length-1]:"";
day=(arr[2]||arr[2]==0)?data.days[arr[2]]||data.days[data.days.length-1]:"";
section=(arr[3]||arr[3]==0)?data.sections[arr[3]]||data.sections[data.sections.length-1]:"";
result=full=`${year+'-'+month+'-'+day+' '+section}`;
let resetData=this.resetData(year,month,day);
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
sections=resetData.sections;
}else{
if(year%4==0||(month!=this.checkObj.month)){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
if(sections)this.range.sections=sections;
obj={
year,
month,
day,
section
}
this.checkObj=obj;
this.$emit("change",{
result:result,
value:full,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

View File

@@ -0,0 +1,274 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column v-for="(group,gIndex) in range" :key="gIndex">
<view class="w-picker-item" v-for="(item,index) in group" :key="index">{{item[nodeKey]}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:[],
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
value:{
type:[Array,String],
default:""
},
defaultType:{
type:String,
default:"label"
},
options:{
type:Array,
default(){
return []
}
},
defaultProps:{
type:Object,
default(){
return{
lable:"label",
value:"value",
children:"children"
}
}
},
level:{
//多级联动层级,表示几级联动
type:[Number,String],
default:2
}
},
computed:{
nodeKey(){
return this.defaultProps.label;
},
nodeVal(){
return this.defaultProps.value;
},
nodeChild(){
return this.defaultProps.children;
}
},
watch:{
value(val){
if(this.options.length!=0){
this.initData();
}
},
options(val){
this.initData();
}
},
created() {
if(this.options.length!=0){
this.initData();
}
},
methods:{
getData(){
//用来处理初始化数据
let options=this.options;
let col1={},col2={},col3={},col4={};
let arr1=options,arr2=[],arr3=[],arr4=[];
let col1Index=0,col2Index=0,col3Index=0,col4Index=0;
let a1="",a2="",a3="",a4="";
let dVal=[],obj={};
let value=this.value;
let data=[];
a1=value[0];
a2=value[1];
if(this.level>2){
a3=value[2];
}
if(this.level>3){
a4=value[3];
};
/*第1列*/
col1Index=arr1.findIndex((v)=>{
return v[this.defaultType]==a1
});
col1Index=value?(col1Index!=-1?col1Index:0):0;
col1=arr1[col1Index];
/*第2列*/
arr2=arr1[col1Index][this.nodeChild];
col2Index=arr2.findIndex((v)=>{
return v[this.defaultType]==a2
});
col2Index=value?(col2Index!=-1?col2Index:0):0;
col2=arr2[col2Index];
/*第3列*/
if(this.level>2){
arr3=arr2[col2Index][this.nodeChild];
col3Index=arr3.findIndex((v)=>{
return v[this.defaultType]==a3;
});
col3Index=value?(col3Index!=-1?col3Index:0):0;
col3=arr3[col3Index];
};
/*第4列*/
if(this.level>3){
arr4=arr3[col4Index][this.nodeChild];
col4Index=arr4.findIndex((v)=>{
return v[this.defaultType]==a4;
});
col4Index=value?(col4Index!=-1?col4Index:0):0;
col4=arr4[col4Index];
};
switch(this.level*1){
case 2:
dVal=[col1Index,col2Index];
obj={
col1,
col2
}
data=[arr1,arr2];
break;
case 3:
dVal=[col1Index,col2Index,col3Index];
obj={
col1,
col2,
col3
}
data=[arr1,arr2,arr3];
break;
case 4:
dVal=[col1Index,col2Index,col3Index,col4Index];
obj={
col1,
col2,
col3,
col4
}
data=[arr1,arr2,arr3,arr4];
break
}
return {
data,
dVal,
obj
}
},
initData(){
let dataData=this.getData();
let data=dataData.data;
let arr1=data[0];
let arr2=data[1];
let arr3=data[2]||[];
let arr4=data[3]||[];
let obj=dataData.obj;
let col1=obj.col1,col2=obj.col2,col3=obj.col3||{},col4=obj.col4||{};
let result="",value=[];
let range=[];
switch(this.level){
case 2:
value=[col1[this.nodeVal],col2[this.nodeVal]];
result=`${col1[this.nodeKey]+col2[this.nodeKey]}`;
range=[arr1,arr2];
break;
case 3:
value=[col1[this.nodeVal],col2[this.nodeVal],col3[this.nodeVal]];
result=`${col1[this.nodeKey]+col2[this.nodeKey]+col3[this.nodeKey]}`;
range=[arr1,arr2,arr3];
break;
case 4:
value=[col1[this.nodeVal],col2[this.nodeVal],col3[this.nodeVal],col4[this.nodeVal]];
result=`${col1[this.nodeKey]+col2[this.nodeKey]+col3[this.nodeKey]+col4[this.nodeKey]}`;
range=[arr1,arr2,arr3,arr4];
break;
}
this.range=range;
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=dataData.dVal;
});
this.$emit("change",{
result:result,
value:value,
obj:obj
})
},
handlerChange(e){
let arr=[...e.detail.value];
let col1Index=arr[0],col2Index=arr[1],col3Index=arr[2]||0,col4Index=arr[3]||0;
let arr1=[],arr2=[],arr3=[],arr4=[];
let col1,col2,col3,col4,obj={};
let result="",value=[];
arr1=this.options;
arr2=(arr1[col1Index]&&arr1[col1Index][this.nodeChild])||arr1[arr1.length-1][this.nodeChild]||[];
col1=arr1[col1Index]||arr1[arr1.length-1]||{};
col2=arr2[col2Index]||arr2[arr2.length-1]||{};
if(this.level>2){
arr3=(arr2[col2Index]&&arr2[col2Index][this.nodeChild])||arr2[arr2.length-1][this.nodeChild];
col3=arr3[col3Index]||arr3[arr3.length-1]||{};
}
if(this.level>3){
arr4=(arr3[col3Index]&&arr3[col3Index][this.nodeChild])||arr3[arr3.length-1][this.nodeChild]||[];
col4=arr4[col4Index]||arr4[arr4.length-1]||{};
}
switch(this.level){
case 2:
obj={
col1,
col2
}
this.range=[arr1,arr2];
result=`${(col1[this.nodeKey]||'')+(col2[this.nodeKey]||'')}`;
value=[col1[this.nodeVal]||'',col2[this.nodeVal]||''];
break;
case 3:
obj={
col1,
col2,
col3
}
this.range=[arr1,arr2,arr3];
result=`${(col1[this.nodeKey]||'')+(col2[this.nodeKey]||'')+(col3[this.nodeKey]||'')}`;
value=[col1[this.nodeVal]||'',col2[this.nodeVal]||'',col3[this.nodeVal]||''];
break;
case 4:
obj={
col1,
col2,
col3,
col4
}
this.range=[arr1,arr2,arr3,arr4];
result=`${(col1[this.nodeKey]||'')+(col2[this.nodeKey]||'')+(col3[this.nodeKey]||'')+(col4[this.nodeKey]||'')}`;
value=[col1[this.nodeVal]||'',col2[this.nodeVal]||'',col3[this.nodeVal]||'',col4[this.nodeVal]||''];
break;
}
this.checkObj=obj;
this.pickVal=arr;
this.$emit("change",{
result:result,
value:value,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

View File

@@ -0,0 +1,344 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column class="w-picker-flex2">
<view class="w-picker-item" v-for="(item,index) in range.fyears" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column class="w-picker-flex2">
<view class="w-picker-item" v-for="(item,index) in range.fmonths" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column class="w-picker-flex2">
<view class="w-picker-item" v-for="(item,index) in range.fdays" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column class="w-picker-flex1">
<view class="w-picker-item">-</view>
</picker-view-column>
<picker-view-column class="w-picker-flex2">
<view class="w-picker-item" v-for="(item,index) in range.tyears" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column class="w-picker-flex2">
<view class="w-picker-item" v-for="(item,index) in range.tmonths" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column class="w-picker-flex2">
<view class="w-picker-item" v-for="(item,index) in range.tdays" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:{},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
value:{
type:[String,Array],
default(){
return []
}
},
current:{//是否默认选中当前日期
type:Boolean,
default:false
},
startYear:{
type:[String,Number],
default:1970
},
endYear:{
type:[String,Number],
default:new Date().getFullYear()
}
},
watch:{
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
formatNum(n){
return (Number(n)<10?'0'+Number(n):Number(n)+'');
},
checkValue(value){
let strReg=/^\d{4}-\d{2}-\d{2}$/,example="2020-04-03";
if(!strReg.test(value[0])||!strReg.test(value[1])){
console.log(new Error("请传入与mode匹配的value值["+example+","+example+"]"))
}
return strReg.test(value[0])&&strReg.test(value[1]);
},
resetToData(fmonth,fday,tyear,tmonth){
let range=this.range;
let tmonths=[],tdays=[];
let yearFlag=tyear!=range.tyears[0];
let monthFlag=tyear!=range.tyears[0]||tmonth!=range.tmonths[0];
let ttotal=new Date(tyear,tmonth,0).getDate();
for(let i=yearFlag?1:fmonth*1;i<=12;i++){
tmonths.push(this.formatNum(i))
}
for(let i=monthFlag?1:fday*1;i<=ttotal;i++){
tdays.push(this.formatNum(i))
}
return{
tmonths,
tdays
}
},
resetData(fyear,fmonth,fday,tyear,tmonth){
let fyears=[],fmonths=[],fdays=[],tyears=[],tmonths=[],tdays=[];
let startYear=this.startYear;
let endYear=this.endYear;
let ftotal=new Date(fyear,fmonth,0).getDate();
let ttotal=new Date(tyear,tmonth,0).getDate();
for(let i=startYear*1;i<=endYear;i++){
fyears.push(this.formatNum(i))
}
for(let i=1;i<=12;i++){
fmonths.push(this.formatNum(i))
}
for(let i=1;i<=ftotal;i++){
fdays.push(this.formatNum(i))
}
for(let i=fyear*1;i<=endYear;i++){
tyears.push(this.formatNum(i))
}
for(let i=fmonth*1;i<=12;i++){
tmonths.push(this.formatNum(i))
}
for(let i=fday*1;i<=ttotal;i++){
tdays.push(this.formatNum(i))
}
return {
fyears,
fmonths,
fdays,
tyears,
tmonths,
tdays
}
},
getData(dVal){
let start=this.startYear*1;
let end=this.endYear*1;
let value=dVal;
let flag=this.current;
let aToday=new Date();
let tYear,tMonth,tDay,tHours,tMinutes,tSeconds,pickVal=[];
let initstartDate=new Date(start.toString());
let endDate=new Date(end.toString());
if(start>end){
initstartDate=new Date(end.toString());
endDate=new Date(start.toString());
};
let startYear=initstartDate.getFullYear();
let startMonth=initstartDate.getMonth()+1;
let endYear=endDate.getFullYear();
let fyears=[],fmonths=[],fdays=[],tyears=[],tmonths=[],tdays=[],returnArr=[],startDVal=[],endDVal=[];
let curMonth=flag?value[1]*1:(startDVal[1]*1+1);
let curMonth1=flag?value[5][1]*1:(value[5]*1+1);
let totalDays=new Date(value[0],value[1],0).getDate();
let totalDays1=new Date(value[4],value[5],0).getDate();
for(let s=startYear;s<=endYear;s++){
fyears.push(this.formatNum(s));
};
for(let m=1;m<=12;m++){
fmonths.push(this.formatNum(m));
};
for(let d=1;d<=totalDays;d++){
fdays.push(this.formatNum(d));
};
for(let s=value[0]*1;s<=endYear;s++){
tyears.push(this.formatNum(s));
};
if(value[4]*1>value[0]*1){
for(let m=1;m<=12;m++){
tmonths.push(this.formatNum(m));
};
for(let d=1;d<=totalDays1;d++){
tdays.push(this.formatNum(d));
};
}else{
for(let m=value[1]*1;m<=12;m++){
tmonths.push(this.formatNum(m));
};
for(let d=value[2]*1;d<=totalDays1;d++){
tdays.push(this.formatNum(d));
};
};
pickVal=[
fyears.indexOf(value[0])==-1?0:fyears.indexOf(value[0]),
fmonths.indexOf(value[1])==-1?0:fmonths.indexOf(value[1]),
fdays.indexOf(value[2])==-1?0:fdays.indexOf(value[2]),
0,
tyears.indexOf(value[4])==-1?0:tyears.indexOf(value[4]),
tmonths.indexOf(value[5])==-1?0:tmonths.indexOf(value[5]),
tdays.indexOf(value[6])==-1?0:tdays.indexOf(value[6])
];
return {
fyears,
fmonths,
fdays,
tyears,
tmonths,
tdays,
pickVal
}
},
getDval(){
let value=this.value;
let fields=this.fields;
let dVal=null;
let aDate=new Date();
let fyear=this.formatNum(aDate.getFullYear());
let fmonth=this.formatNum(aDate.getMonth()+1);
let fday=this.formatNum(aDate.getDate());
let tyear=this.formatNum(aDate.getFullYear());
let tmonth=this.formatNum(aDate.getMonth()+1);
let tday=this.formatNum(aDate.getDate());
if(value&&value.length>0){
let flag=this.checkValue(value);
if(!flag){
dVal=[fyear,fmonth,fday,"-",tyear,tmonth,tday]
}else{
dVal=[...value[0].split("-"),"-",...value[1].split("-")];
}
}else{
dVal=[fyear,fmonth,fday,"-",tyear,tmonth,tday]
}
return dVal;
},
initData(){
let range=[],pickVal=[];
let result="",full="",obj={};
let dVal=this.getDval();
let dateData=this.getData(dVal);
let fyears=[],fmonths=[],fdays=[],tyears=[],tmonths=[],tdays=[];
let fyear,fmonth,fday,tyear,tmonth,tday;
pickVal=dateData.pickVal;
fyears=dateData.fyears;
fmonths=dateData.fmonths;
fdays=dateData.fdays;
tyears=dateData.tyears;
tmonths=dateData.tmonths;
tdays=dateData.tdays;
range={
fyears,
fmonths,
fdays,
tyears,
tmonths,
tdays,
}
fyear=range.fyears[pickVal[0]];
fmonth=range.fmonths[pickVal[1]];
fday=range.fdays[pickVal[2]];
tyear=range.tyears[pickVal[4]];
tmonth=range.tmonths[pickVal[5]];
tday=range.tdays[pickVal[6]];
obj={
fyear,
fmonth,
fday,
tyear,
tmonth,
tday
}
result=`${fyear+'-'+fmonth+'-'+fday+'至'+tyear+'-'+tmonth+'-'+tday}`;
this.range=range;
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=pickVal;
});
this.$emit("change",{
result:result,
value:result.split("至"),
obj:obj
})
},
handlerChange(e){
let arr=[...e.detail.value];
let result="",full="",obj={};
let year="",month="",day="",hour="",minute="",second="",note=[],province,city,area;
let checkObj=this.checkObj;
let days=[],months=[],endYears=[],endMonths=[],endDays=[],startDays=[];
let mode=this.mode;
let col1,col2,col3,d,a,h,m;
let xDate=new Date().getTime();
let range=this.range;
let fyear=range.fyears[arr[0]]||range.fyears[range.fyears.length-1];
let fmonth=range.fmonths[arr[1]]||range.fmonths[range.fmonths.length-1];
let fday=range.fdays[arr[2]]||range.fdays[range.fdays.length-1];
let tyear=range.tyears[arr[4]]||range.tyears[range.tyears.length-1];
let tmonth=range.tmonths[arr[5]]||range.tmonths[range.tmonths.length-1];
let tday=range.tdays[arr[6]]||range.tdays[range.tdays.length-1];
let resetData=this.resetData(fyear,fmonth,fday,tyear,tmonth);
if(fyear!=checkObj.fyear||fmonth!=checkObj.fmonth||fday!=checkObj.fday){
arr[4]=0;
arr[5]=0;
arr[6]=0;
range.tyears=resetData.tyears;
range.tmonths=resetData.tmonths;
range.tdays=resetData.tdays;
tyear=range.tyears[0];
checkObj.tyears=range.tyears[0];
tmonth=range.tmonths[0];
checkObj.tmonths=range.tmonths[0];
tday=range.tdays[0];
checkObj.tdays=range.tdays[0];
}
if(fyear!=checkObj.fyear||fmonth!=checkObj.fmonth){
range.fdays=resetData.fdays;
};
if(tyear!=checkObj.tyear){
arr[5]=0;
arr[6]=0;
let toData=this.resetToData(fmonth,fday,tyear,tmonth);
range.tmonths=toData.tmonths;
range.tdays=toData.tdays;
tmonth=range.tmonths[0];
checkObj.tmonths=range.tmonths[0];
tday=range.tdays[0];
checkObj.tdays=range.tdays[0];
};
if(tmonth!=checkObj.tmonth){
arr[6]=0;
let toData=this.resetToData(fmonth,fday,tyear,tmonth);
range.tdays=toData.tdays;
tday=range.tdays[0];
checkObj.tdays=range.tdays[0];
};
result=`${fyear+'-'+fmonth+'-'+fday+'至'+tyear+'-'+tmonth+'-'+tday}`;
obj={
fyear,fmonth,fday,tyear,tmonth,tday
}
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=arr;
})
this.$emit("change",{
result:result,
value:result.split("至"),
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

View File

@@ -0,0 +1,183 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.provinces" :key="index">{{item.label}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.citys" :key="index">{{item.label}}</view>
</picker-view-column>
<picker-view-column v-if="!hideArea">
<view class="w-picker-item" v-for="(item,index) in range.areas" :key="index">{{item.label}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
import areaData from "./areadata/areadata.js"
export default {
data() {
return {
pickVal:[],
range:{
provinces:[],
citys:[],
areas:[]
},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
value:{
type:[Array,String],
default:""
},
defaultType:{
type:String,
default:"label"
},
hideArea:{
type:Boolean,
default:false
}
},
watch:{
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
getData(){
//用来处理初始化数据
let provinces=areaData;
let dVal=[];
let value=this.value;
let a1=value[0];//默认值省
let a2=value[1];//默认值市
let a3=value[2];//默认值区、县
let province,city,area;
let provinceIndex=provinces.findIndex((v)=>{
return v[this.defaultType]==a1
});
provinceIndex=value?(provinceIndex!=-1?provinceIndex:0):0;
let citys=provinces[provinceIndex].children;
let cityIndex=citys.findIndex((v)=>{
return v[this.defaultType]==a2
});
cityIndex=value?(cityIndex!=-1?cityIndex:0):0;
let areas=citys[cityIndex].children;
let areaIndex=areas.findIndex((v)=>{
return v[this.defaultType]==a3;
});
areaIndex=value?(areaIndex!=-1?areaIndex:0):0;
dVal=this.hideArea?[provinceIndex,cityIndex]:[provinceIndex,cityIndex,areaIndex];
province=provinces[provinceIndex];
city=citys[cityIndex];
area=areas[areaIndex];
let obj=this.hideArea?{
province,
city
}:{
province,
city,
area
}
return this.hideArea?{
provinces,
citys,
dVal,
obj
}:{
provinces,
citys,
areas,
dVal,
obj
}
},
initData(){
let dataData=this.getData();
let provinces=dataData.provinces;
let citys=dataData.citys;
let areas=this.hideArea?[]:dataData.areas;
let obj=dataData.obj;
let province=obj.province,city=obj.city,area=this.hideArea?{}:obj.area;
let value=this.hideArea?[province.value,city.value]:[province.value,city.value,area.value];
let result=this.hideArea?`${province.label+city.label}`:`${province.label+city.label+area.label}`;
this.range=this.hideArea?{
provinces,
citys,
}:{
provinces,
citys,
areas
};
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=dataData.dVal;
});
this.$emit("change",{
result:result,
value:value,
obj:obj
})
},
handlerChange(e){
let arr=[...e.detail.value];
let provinceIndex=arr[0],cityIndex=arr[1],areaIndex=this.hideArea?0:arr[2];
let provinces=areaData;
let citys=(provinces[provinceIndex]&&provinces[provinceIndex].children)||provinces[provinces.length-1].children||[];
let areas=this.hideArea?[]:((citys[cityIndex]&&citys[cityIndex].children)||citys[citys.length-1].children||[]);
let province=provinces[provinceIndex]||provinces[provinces.length-1],
city=citys[cityIndex]||[citys.length-1],
area=this.hideArea?{}:(areas[areaIndex]||[areas.length-1]);
let obj=this.hideArea?{
province,
city
}:{
province,
city,
area
}
if(this.checkObj.province.label!=province.label){
//当省更新的时候需要刷新市、区县的数据;
this.range.citys=citys;
if(!this.hideArea){
this.range.areas=areas;
}
}
if(this.checkObj.city.label!=city.label){
//当市更新的时候需要刷新区县的数据;
if(!this.hideArea){
this.range.areas=areas;
}
}
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=arr;
})
let result=this.hideArea?`${province.label+city.label}`:`${province.label+city.label+area.label}`;
let value=this.hideArea?[province.value,city.value]:[province.value,city.value,area.value];
this.$emit("change",{
result:result,
value:value,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

View File

@@ -0,0 +1,129 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range" :key="index">{{item[nodeKey]}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
props:{
itemHeight:{
type:String,
default:"44px"
},
options:{
type:[Array,Object],
default(){
return []
}
},
value:{
type:String,
default:""
},
defaultType:{
type:String,
default:"label"
},
defaultProps:{
type:Object,
default(){
return{
label:"label",
value:"value"
}
}
}
},
data() {
return {
pickVal:[]
};
},
computed:{
nodeKey(){
return this.defaultProps.label;
},
nodeValue(){
return this.defaultProps.value;
},
range(){
return this.options
}
},
watch:{
value(val){
if(this.options.length!=0){
this.initData();
}
},
options(val){
this.initData();
}
},
created() {
if(this.options.length!=0){
this.initData();
}
},
methods:{
initData(){
let dVal=this.value||"";
let data=this.range;
let pickVal=[0];
let cur=null;
let label="";
let value,idx;
if(this.defaultType==this.nodeValue){
value=data.find((v)=>v[this.nodeValue]==dVal);
idx=data.findIndex((v)=>v[this.nodeValue]==dVal);
}else{
value=data.find((v)=>v[this.nodeKey]==dVal);
idx=data.findIndex((v)=>v[this.nodeKey]==dVal);
}
pickVal=[idx!=-1?idx:0];
this.$nextTick(()=>{
this.pickVal=pickVal;
});
if(this.defaultType==this.nodeValue){
this.$emit("change",{
result:value?value[this.nodeKey]:data[0][this.nodeKey],
value:dVal||data[0][this.nodeKey],
obj:value?value:data[0]
})
}else{
this.$emit("change",{
result:dVal||data[0][this.nodeKey],
value:value?value[this.nodeValue]:data[0][this.nodeValue],
obj:value?value:data[0]
})
}
},
handlerChange(e){
let arr=[...e.detail.value];
let pickVal=[arr[0]||0];
let data=this.range;
let cur=data[arr[0]];
let label="";
let value="";
this.$nextTick(()=>{
this.pickVal=pickVal;
});
this.$emit("change",{
result:cur[this.nodeKey],
value:cur[this.nodeValue],
obj:cur
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

View File

@@ -0,0 +1,250 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.dates" :key="index">{{item.label}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item.label}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.minutes" :key="index">{{item.label}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:{},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
value:{
type:[String,Array,Number],
default:""
},
current:{//是否默认选中当前日期
type:Boolean,
default:false
},
expand:{
type:[Number,String],
default:30
}
},
watch:{
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
formatNum(n){
return (Number(n)<10?'0'+Number(n):Number(n)+'');
},
checkValue(value){
let strReg=/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}(:\d{2})?$/,example="2019-12-12 18:05:00或者2019-12-12 18:05";
if(!strReg.test(value)){
console.log(new Error("请传入与modefields匹配的value值例value="+example+""))
}
return strReg.test(value);
},
resetData(year,month,day){
let curDate=this.getCurrenDate();
let curFlag=this.current;
let curYear=curDate.curYear;
let curMonth=curDate.curMonth;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let months=[],days=[],sections=[];
let disabledAfter=this.disabledAfter;
let monthsLen=disabledAfter?(year*1<curYear?12:curMonth):12;
let totalDays=new Date(year,month,0).getDate();//计算当月有几天;
for(let month=1;month<=monthsLen;month++){
months.push(this.formatNum(month));
};
for(let day=1;day<=daysLen;day++){
days.push(this.formatNum(day));
}
return{
months,
days,
sections
}
},
getData(dVal){
//用来处理初始化数据
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let dates=[],hours=[],minutes=[];
let curDate=new Date();
let curYear=curDate.getFullYear();
let curMonth=curDate.getMonth();
let curDay=curDate.getDate();
let aDate=new Date(curYear,curMonth,curDay);
for(let i=0;i<this.expand*1;i++){
aDate=new Date(curYear,curMonth,curDay+i);
let year=aDate.getFullYear();
let month=aDate.getMonth()+1;
let day=aDate.getDate();
let label=year+"-"+this.formatNum(month)+"-"+this.formatNum(day);
switch(i){
case 0:
label="今天";
break;
case 1:
label="明天";
break;
case 2:
label="后天";
break
}
dates.push({
label:label,
value:year+"-"+this.formatNum(month)+"-"+this.formatNum(day)
})
};
for(let i=0;i<24;i++){
hours.push({
label:this.formatNum(i),
value:this.formatNum(i)
})
}
for(let i=0;i<60;i++){
minutes.push({
label:this.formatNum(i),
value:this.formatNum(i)
})
}
return {
dates,
hours,
minutes
}
},
getDefaultDate(){
let value=this.value;
let reg=/-/g;
let defaultDate=value?new Date(value.replace(reg,"/")):new Date();
let defaultYear=defaultDate.getFullYear();
let defaultMonth=defaultDate.getMonth()+1;
let defaultDay=defaultDate.getDate();
let defaultDays=new Date(defaultYear,defaultMonth,0).getDate()*1;
return{
defaultDate,
defaultYear,
defaultMonth,
defaultDay,
defaultDays
}
},
getDval(){
let value=this.value;
let dVal=null;
let aDate=new Date();
let year=this.formatNum(aDate.getFullYear());
let month=this.formatNum(aDate.getMonth()+1);
let day=this.formatNum(aDate.getDate());
let date=this.formatNum(year)+"-"+this.formatNum(month)+"-"+this.formatNum(day);
let hour=aDate.getHours();
let minute=aDate.getMinutes();
if(value){
let flag=this.checkValue(value);
if(!flag){
dVal=[date,hour,minute]
}else{
let v=value.split(" ");
dVal=[v[0],...v[1].split(":")];
}
}else{
dVal=[date,hour,minute]
}
return dVal;
},
initData(){
let startDate,endDate,startYear,endYear,startMonth,endMonth,startDay,endDay;
let dates=[],hours=[],minutes=[];
let dVal=[],pickVal=[];
let value=this.value;
let reg=/-/g;
let range={};
let result="",full="",date,hour,minute,obj={};
let defaultDate=this.getDefaultDate();
let defaultYear=defaultDate.defaultYear;
let defaultMonth=defaultDate.defaultMonth;
let defaultDay=defaultDate.defaultDay;
let defaultDays=defaultDate.defaultDays;
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let dateData=[];
dVal=this.getDval();
dateData=this.getData(dVal);
dates=dateData.dates;
hours=dateData.hours;
minutes=dateData.minutes;
pickVal=[
dates.findIndex(n => n.value == dVal[0])!=-1?dates.findIndex(n => n.value == dVal[0]):0,
hours.findIndex(n => n.value == dVal[1])!=-1?hours.findIndex(n => n.value == dVal[1]):0,
minutes.findIndex(n => n.value == dVal[2])!=-1?minutes.findIndex(n => n.value == dVal[2]):0,
];
range={dates,hours,minutes};
date=dVal[0]?dVal[0]:dates[0].label;
hour=dVal[1]?dVal[1]:hours[0].label;
minute=dVal[2]?dVal[2]:minutes[0].label;
result=full=`${date+' '+hour+':'+minute}`;
obj={
date,
hour,
minute
}
this.range=range;
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=pickVal;
});
this.$emit("change",{
result:result,
value:full,
obj:obj
})
},
handlerChange(e){
let arr=[...e.detail.value];
let data=this.range;
let date="",hour="",minute="";
let result="",full="",obj={};
let disabledAfter=this.disabledAfter;
date=(arr[0]||arr[0]==0)?data.dates[arr[0]]||data.dates[data.dates.length-1]:"";
hour=(arr[1]||arr[1]==0)?data.hours[arr[1]]||data.hours[data.hours.length-1]:"";
minute=(arr[2]||arr[2]==0)?data.minutes[arr[2]]||data.minutes[data.minutes.length-1]:"";
result=full=`${date.label+' '+hour.label+':'+minute.label+':00'}`;
obj={
date,
hour,
minute
}
this.checkObj=obj;
this.$emit("change",{
result:result,
value:full,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

View File

@@ -0,0 +1,218 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.minutes" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column v-if="second">
<view class="w-picker-item" v-for="(item,index) in range.seconds" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:{},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
value:{
type:[String,Array,Number],
default:""
},
current:{//是否默认选中当前日期
type:Boolean,
default:false
},
second:{
type:Boolean,
default:true
}
},
watch:{
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
formatNum(n){
return (Number(n)<10?'0'+Number(n):Number(n)+'');
},
checkValue(value){
let strReg=/^\d{2}:\d{2}:\d{2}$/,example="18:00:05";
if(!strReg.test(value)){
console.log(new Error("请传入与modefields匹配的value值例value="+example+""))
}
return strReg.test(value);
},
resetData(year,month,day,hour,minute){
let curDate=this.getCurrenDate();
let curFlag=this.current;
let curHour=curDate.curHour;
let curMinute=curDate.curMinute;
let curSecond=curDate.curSecond;
for(let hour=0;hour<24;hour++){
hours.push(this.formatNum(hour));
}
for(let minute=0;minute<60;minute++){
minutes.push(this.formatNum(minute));
}
for(let second=0;second<60;second++){
seconds.push(this.formatNum(second));
}
return{
hours,
minutes,
seconds
}
},
getData(curDate){
//用来处理初始化数据
let hours=[],minutes=[],seconds=[];
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let fields=this.fields;
let curHour=curDate.curHour;
let curMinute=curDate.curMinute;
let curSecond=curDate.curSecond;
for(let hour=0;hour<24;hour++){
hours.push(this.formatNum(hour));
}
for(let minute=0;minute<60;minute++){
minutes.push(this.formatNum(minute));
}
for(let second=0;second<60;second++){
seconds.push(this.formatNum(second));
}
return this.second?{
hours,
minutes,
seconds
}:{
hours,
minutes
}
},
getCurrenDate(){
let curDate=new Date();
let curHour=curDate.getHours();
let curMinute=curDate.getMinutes();
let curSecond=curDate.getSeconds();
return this.second?{
curHour,
curMinute,
curSecond
}:{
curHour,
curMinute,
}
},
getDval(){
let value=this.value;
let fields=this.fields;
let dVal=null;
let aDate=new Date();
let hour=this.formatNum(aDate.getHours());
let minute=this.formatNum(aDate.getMinutes());
let second=this.formatNum(aDate.getSeconds());
if(value){
let flag=this.checkValue(value);
if(!flag){
dVal=[hour,minute,second]
}else{
dVal=value?value.split(":"):[];
}
}else{
dVal=this.second?[hour,minute,second]:[hour,minute]
}
return dVal;
},
initData(){
let curDate=this.getCurrenDate();
let dateData=this.getData(curDate);
let pickVal=[],obj={},full="",result="",hour="",minute="",second="";
let dVal=this.getDval();
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let hours=dateData.hours;
let minutes=dateData.minutes;
let seconds=dateData.seconds;
let defaultArr=this.second?[
dVal[0]&&hours.indexOf(dVal[0])!=-1?hours.indexOf(dVal[0]):0,
dVal[1]&&minutes.indexOf(dVal[1])!=-1?minutes.indexOf(dVal[1]):0,
dVal[2]&&seconds.indexOf(dVal[2])!=-1?seconds.indexOf(dVal[2]):0
]:[
dVal[0]&&hours.indexOf(dVal[0])!=-1?hours.indexOf(dVal[0]):0,
dVal[1]&&minutes.indexOf(dVal[1])!=-1?minutes.indexOf(dVal[1]):0
];
pickVal=disabledAfter?defaultArr:(curFlag?(this.second?[
hours.indexOf(this.formatNum(curDate.curHour)),
minutes.indexOf(this.formatNum(curDate.curMinute)),
seconds.indexOf(this.formatNum(curDate.curSecond)),
]:[
hours.indexOf(this.formatNum(curDate.curHour)),
minutes.indexOf(this.formatNum(curDate.curMinute))
]):defaultArr);
this.range=dateData;
this.checkObj=obj;
hour=dVal[0]?dVal[0]:hours[0];
minute=dVal[1]?dVal[1]:minutes[0];
if(this.second)second=dVal[2]?dVal[0]:seconds[0];
result=this.second?`${hour+':'+minute+':'+second}`:`${hour+':'+minute}`;
full=this.second?`${hour+':'+minute+':'+second}`:`${hour+':'+minute+':00'}`;
this.$nextTick(()=>{
this.pickVal=pickVal;
});
this.$emit("change",{
result:result,
value:full,
obj:obj
})
},
handlerChange(e){
let arr=[...e.detail.value];
let data=this.range;
let hour="",minute="",second="",result="",full="",obj={};
hour=(arr[0]||arr[0]==0)?data.hours[arr[0]]||data.hours[data.hours.length-1]:"";
minute=(arr[1]||arr[1]==0)?data.minutes[arr[1]]||data.minutes[data.minutes.length-1]:"";
if(this.second)second=(arr[2]||arr[2]==0)?data.seconds[arr[2]]||data.seconds[data.seconds.length-1]:"";
obj=this.second?{
hour,
minute,
second
}:{
hour,
minute
};
this.checkObj=obj;
result=this.second?`${hour+':'+minute+':'+second}`:`${hour+':'+minute}`;
full=this.second?`${hour+':'+minute+':'+second}`:`${hour+':'+minute+':00'}`;
this.$emit("change",{
result:result,
value:full,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

View File

@@ -0,0 +1,26 @@
.w-picker-flex2{
flex:2;
}
.w-picker-flex1{
flex:1;
}
.w-picker-view {
width: 100%;
height: 476upx;
overflow: hidden;
background-color: rgba(255, 255, 255, 1);
z-index: 666;
}
.d-picker-view{
height: 100%;
}
.w-picker-item {
text-align: center;
width: 100%;
height: 88upx;
line-height: 88upx;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 30upx;
}

View File

@@ -0,0 +1,340 @@
<template name="w-picker">
<view class="w-picker" :key="createKey" :data-key="createKey">
<view class="mask" :class="{'visible':visible}" @tap="onCancel" @touchmove.stop.prevent catchtouchmove="true"></view>
<view class="w-picker-cnt" :class="{'visible':visible}">
<view class="w-picker-header" @touchmove.stop.prevent catchtouchmove="true">
<text @tap.stop.prevent="onCancel">取消</text>
<slot></slot>
<text :style="{'color':themeColor}" @tap.stop.prevent="pickerConfirm">确定</text>
</view>
<date-picker
v-if="mode=='date'"
class="w-picker-wrapper"
:startYear="startYear"
:endYear="endYear"
:value="value"
:fields="fields"
:item-height="itemHeight"
:current="current"
:disabled-after="disabledAfter"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</date-picker>
<range-picker
v-if="mode=='range'"
class="w-picker-wrapper"
:startYear="startYear"
:endYear="endYear"
:value="value"
:item-height="itemHeight"
:current="current"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</range-picker>
<half-picker
v-if="mode=='half'"
class="w-picker-wrapper"
:startYear="startYear"
:endYear="endYear"
:value="value"
:item-height="itemHeight"
:current="current"
:disabled-after="disabledAfter"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</half-picker>
<shortterm-picker
v-if="mode=='shortTerm'"
class="w-picker-wrapper"
:startYear="startYear"
:endYear="endYear"
:value="value"
:item-height="itemHeight"
:current="current"
expand="60"
:disabled-after="disabledAfter"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</shortterm-picker>
<time-picker
v-if="mode=='time'"
class="w-picker-wrapper"
:value="value"
:item-height="itemHeight"
:current="current"
:disabled-after="disabledAfter"
:second="second"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</time-picker>
<selector-picker
v-if="mode=='selector'"
class="w-picker-wrapper"
:value="value"
:item-height="itemHeight"
:options="options"
:default-type="defaultType"
:default-props="defaultProps"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</selector-picker>
<region-picker
v-if="mode=='region'"
class="w-picker-wrapper"
:value="value"
:hide-area="hideArea"
:default-type="defaultType"
:item-height="itemHeight"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</region-picker>
<linkage-picker
v-if="mode=='linkage'"
class="w-picker-wrapper"
:value="value"
:options="options"
:level="level"
:default-type="defaultType"
:default-props="defaultProps"
:item-height="itemHeight"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</linkage-picker>
</view>
</view>
</template>
<script>
import datePicker from "./date-picker.vue"
import rangePicker from "./range-picker.vue"
import halfPicker from "./half-picker.vue"
import shorttermPicker from "./shortterm-picker.vue"
import timePicker from "./time-picker.vue"
import selectorPicker from "./selector-picker.vue"
import regionPicker from "./region-picker.vue"
import linkagePicker from "./linkage-picker.vue"
export default {
name:"w-picker",
components:{
datePicker,
rangePicker,
halfPicker,
timePicker,
selectorPicker,
shorttermPicker,
regionPicker,
linkagePicker
},
props:{
mode:{
type:String,
default:"date"
},
value:{//默认值
type:[String,Array,Number],
default:""
},
current:{//是否默认显示当前时间,如果是,传的默认值将失效
type:Boolean,
default:false
},
themeColor:{//确认按钮主题颜色
type:String,
default:"#f5a200"
},
fields:{//日期颗粒度:year、month、day、hour、minute、second
type:String,
default:"date"
},
disabledAfter:{//是否禁用当前之后的日期
type:Boolean,
default:false
},
second:{//time-picker是否显示秒
type:Boolean,
default:true
},
options:{//selector,region数据源
type:[Array,Object],
default(){
return []
}
},
defaultProps:{//selector,linkagle字段转换配置
type:Object,
default(){
return{
label:"label",
value:"value",
children:"children"
}
}
},
defaultType:{
type:String,
default:"label"
},
hideArea:{//mode=region时是否隐藏区县列
type:Boolean,
default:false
},
level:{
//多级联动层级,表示几级联动,区间2-4;
type:[Number,String],
default:2
},
timeout:{//是否开启点击延迟,当快速滚动 还没有滚动完毕点击关闭时得到的值是不准确的
type:Boolean,
default:false
},
expand:{//mode=shortterm 默认往后拓展天数
type:[Number,String],
default:30
},
startYear:{
type:[String,Number],
default:1970
},
endYear:{
type:[String,Number],
default:new Date().getFullYear()
},
visible:{
type:Boolean,
default:false
}
},
created() {
this.createKey=Math.random()*1000;
},
data() {
return {
itemHeight:`height: ${uni.upx2px(88)}px;`,
result:{},
confirmFlag:true
};
},
methods:{
touchStart(){
if(this.timeout){
this.confirmFlag=false;
}
},
touchEnd(){
if(this.timeout){
setTimeout(()=>{
this.confirmFlag=true;
},500)
}
},
handlerChange(res){
let _this=this;
this.result={...res};
},
show(){
this.$emit("update:visible",true);
},
hide(){
this.$emit("update:visible",false);
},
onCancel(res){
this.$emit("update:visible",false);
this.$emit("cancel");
},
pickerConfirm(){
if(!this.confirmFlag){
return;
};
this.$emit("confirm",this.result);
this.$emit("update:visible",false);
}
}
}
</script>
<style lang="scss">
.w-picker-item {
text-align: center;
width: 100%;
height: 88upx;
line-height: 88upx;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 30upx;
}
.w-picker{
z-index: 888;
.mask {
position: fixed;
z-index: 1000;
top: 0;
right: 0;
left: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
visibility: hidden;
opacity: 0;
transition: all 0.3s ease;
}
.mask.visible{
visibility: visible;
opacity: 1;
}
.w-picker-cnt {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
transition: all 0.3s ease;
transform: translateY(100%);
z-index: 3000;
background-color: #fff;
}
.w-picker-cnt.visible {
transform: translateY(0);
}
.w-picker-header{
display: flex;
align-items: center;
padding: 0 30upx;
height: 88upx;
background-color: #fff;
position: relative;
text-align: center;
font-size: 32upx;
justify-content: space-between;
border-bottom: solid 1px #eee;
.w-picker-btn{
font-size: 30upx;
}
}
.w-picker-hd:after {
content: ' ';
position: absolute;
left: 0;
bottom: 0;
right: 0;
height: 1px;
border-bottom: 1px solid #e5e5e5;
color: #e5e5e5;
transform-origin: 0 100%;
transform: scaleY(0.5);
}
}
</style>