business 目录调整
This commit is contained in:
parent
02545369a6
commit
4722f9d199
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"component": true
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/** index.wxss **/
|
||||
.divider {
|
||||
margin: 0;
|
||||
flex-shrink: 0;
|
||||
border-width: 0;
|
||||
border-style: solid;
|
||||
border-color: rgb(0 0 0 / 12%);
|
||||
border-bottom-width: thin;
|
||||
}
|
||||
|
||||
.flexItem {
|
||||
align-self: stretch;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.vertical {
|
||||
height: 100%;
|
||||
border-bottom-width: 0;
|
||||
border-right-width: thin;
|
||||
margin: 0rpx 8rpx;
|
||||
}
|
||||
|
||||
.horizontal {
|
||||
display: block;
|
||||
unicode-bidi: isolate;
|
||||
margin-block-start: 0.5em;
|
||||
margin-block-end: 0.5em;
|
||||
margin-inline-start: auto;
|
||||
margin-inline-end: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
Component({
|
||||
properties: {
|
||||
// circle || square
|
||||
mode: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
// small || large || default
|
||||
flexItem: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<!-- index.wxml -->
|
||||
<hr class="divider {{mode === 'vertical' ? mode : 'horizontal'}} {{flexItem === true ? 'flexItem' : ''}}" />
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"component": true
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
@import "../../../config/styles/_base.less";
|
||||
|
||||
.btn-view {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.fab {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
outline: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
vertical-align: middle;
|
||||
color: #fff;
|
||||
width: 112rpx;
|
||||
height: 112rpx;
|
||||
z-index: 1050;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.75;
|
||||
text-transform: uppercase;
|
||||
border-radius: 50%;
|
||||
transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, border-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
|
||||
box-shadow: 0 3px 5px -1px rgb(0 0 0 / 20%), 0 6px 10px 0 rgb(0 0 0 / 14%), 0 1px 18px 0 rgb(0 0 0 / 12%);
|
||||
|
||||
&:active {
|
||||
box-shadow: 0 7px 8px -4px rgb(0 0 0 / 20%), 0 12px 17px 2px rgb(0 0 0 / 14%), 0 5px 22px 4px rgb(0 0 0 / 12%);
|
||||
}
|
||||
|
||||
&-default {
|
||||
background-color: @default-color;
|
||||
}
|
||||
|
||||
&-success {
|
||||
background-color: @success-color;
|
||||
}
|
||||
|
||||
&-warning {
|
||||
background-color: @warning-color;
|
||||
}
|
||||
|
||||
&-error {
|
||||
background-color: @error-color;
|
||||
}
|
||||
}
|
||||
|
||||
.ripple {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ripple:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.ripple::after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
left: -10px;
|
||||
right: -10px;
|
||||
bottom: -10px;
|
||||
pointer-events: none;
|
||||
background-color: #888;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50%;
|
||||
opacity: 0;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.ripple:active::after {
|
||||
opacity: 0.3;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
transition: 0s;
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
Component({
|
||||
|
||||
properties: {
|
||||
// button组建标识
|
||||
bottom: {
|
||||
type: Number,
|
||||
value: 50,
|
||||
},
|
||||
right: {
|
||||
type: Number,
|
||||
value: 32,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
value: 'lin'
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
value: 'default',
|
||||
options: ['warning', 'success', 'error', 'default']
|
||||
},
|
||||
plain: Boolean,
|
||||
size: {
|
||||
type: String,
|
||||
value: 'medium',
|
||||
options: ['medium', 'large', 'mini', 'long']
|
||||
},
|
||||
shape: {
|
||||
type: String,
|
||||
value: 'circle',
|
||||
options: ['square', 'circle', 'semicircle']
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
special: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
// 微信原生接口
|
||||
width: Number,
|
||||
height: Number,
|
||||
icon: String,
|
||||
image: String,
|
||||
bgColor: String,
|
||||
iconColor: String,
|
||||
iconSize: String,
|
||||
openType: String,
|
||||
appParameter: String,
|
||||
lang: String,
|
||||
hoverStopPropagation: Boolean,
|
||||
hoverStartTime: {
|
||||
type: Number,
|
||||
value: 20
|
||||
},
|
||||
hoverStayTime: {
|
||||
type: Number,
|
||||
value: 70
|
||||
},
|
||||
sessionFrom: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
sendMessageTitle: String,
|
||||
sendMessagePath: String,
|
||||
sendMessageImg: String,
|
||||
showMessageCard: Boolean,
|
||||
formType: String,
|
||||
disabledHover: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// button点击事件
|
||||
handleTap() {
|
||||
if (this.data.disabled || this.data.loading) return false;
|
||||
this.triggerEvent('click', {}, {
|
||||
bubbles: true,
|
||||
composed: true
|
||||
});
|
||||
},
|
||||
// 开放能力事件回调
|
||||
openTypeEvent(data:any) {
|
||||
this.triggerEvent(data.type, data.detail, {});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<view class="btn-view" style="bottom: {{bottom}}rpx; right: {{right}}rpx">
|
||||
<button class="fab fab-{{type}} ripple" id="{{name}}" lang="{{lang}}" form-type="{{formType}}" open-type="{{openType}}" app-parameter="{{ appParameter }}" hover-stop-propagation="{{ hoverStopPropagation }}" hover-start-time="{{ hoverStartTime }}" hover-stay-time="{{ hoverStayTime }}" session-from="{{ sessionFrom }}" send-message-title="{{ sendMessageTitle }}" send-message-path="{{ sendMessagePath }}" send-message-img="{{ sendMessageImg }}" show-message-card="{{ showMessageCard }}" bindcontact="openTypeEvent" bindgetuserinfo="openTypeEvent" bindgetphonenumber="openTypeEvent" bindopensetting="openTypeEvent" bind:tap="handleTap">
|
||||
<slot />
|
||||
</button>
|
||||
</view>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"l-icon": "../../../lin-ui/icon/index"
|
||||
"l-icon": "../../../miniprogram_npm/lin-ui/icon/index"
|
||||
},
|
||||
"componentGenerics": {
|
||||
"item": true
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"l-icon": "../../lin-ui/icon/index"
|
||||
"l-icon": "../../miniprogram_npm/lin-ui/icon/index"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
@import "_theme";
|
||||
@import "./_theme.less";
|
||||
|
||||
// Color
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
@import "../../../config/styles/_base.less";
|
||||
|
|
@ -0,0 +1 @@
|
|||
@import "../../../config/styles/_mixins.less";
|
||||
|
|
@ -0,0 +1 @@
|
|||
@import "../../../config/styles/_theme.less";
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
import zIndex from '../behaviors/zIndex';
|
||||
import hover from '../behaviors/hover';
|
||||
|
||||
Component({
|
||||
behaviors: [zIndex,hover],
|
||||
externalClasses: ['l-class-title', 'l-class-item', 'l-class-cancel','l-title-class','l-item-class','l-cancel-class'],
|
||||
properties: {
|
||||
locked: Boolean,
|
||||
showCancel: Boolean,
|
||||
show: Boolean,
|
||||
itemList: Array,
|
||||
cancelText: {
|
||||
type: String,
|
||||
value: '取消'
|
||||
},
|
||||
title: String,
|
||||
zIndex:{
|
||||
type:Number,
|
||||
value: 777
|
||||
},
|
||||
openApi: {
|
||||
type: Boolean,
|
||||
value: true,
|
||||
}
|
||||
},
|
||||
data: {
|
||||
success: '',
|
||||
fail: '',
|
||||
isIphoneX: false
|
||||
},
|
||||
attached() {
|
||||
if (this.data.openApi) {
|
||||
this.initActionSheet();
|
||||
}
|
||||
this.initUIAdapter();
|
||||
},
|
||||
|
||||
pageLifetimes: {
|
||||
show() {
|
||||
if (this.data.openApi) {
|
||||
this.initActionSheet();
|
||||
}
|
||||
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 区分UI尺寸
|
||||
*/
|
||||
initUIAdapter() {
|
||||
wx.getSystemInfo({
|
||||
success: (res) => {
|
||||
this.setData({
|
||||
isIphoneX: res.model === 'iPhone X',
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
initActionSheet() {
|
||||
wx.lin = wx.lin || {};
|
||||
wx.lin.showActionSheet = (options={}) => {
|
||||
const {
|
||||
itemList = [],
|
||||
success = null,
|
||||
fail = null,
|
||||
title = '',
|
||||
locked = false,
|
||||
cancelText = '取消',
|
||||
showCancel = false,
|
||||
} = options;
|
||||
this.setData({
|
||||
itemList: itemList.slice(0, 10),
|
||||
success,
|
||||
fail,
|
||||
title,
|
||||
locked,
|
||||
cancelText,
|
||||
showCancel,
|
||||
show: true,
|
||||
});
|
||||
return this;
|
||||
};
|
||||
},
|
||||
handleClickItem(e) {
|
||||
const {
|
||||
success
|
||||
} = this.data;
|
||||
success && success({ ...e.currentTarget.dataset });
|
||||
this.triggerEvent('linitemtap', { ...e.currentTarget.dataset },{ bubbles: true, composed: true });
|
||||
this._hideActionSheet();
|
||||
},
|
||||
|
||||
_showActionSheet() {
|
||||
this.setData({
|
||||
show: true
|
||||
});
|
||||
},
|
||||
|
||||
_hideActionSheet() {
|
||||
this.setData({
|
||||
show: false
|
||||
});
|
||||
},
|
||||
|
||||
handleClickCancel() {
|
||||
const {
|
||||
fail
|
||||
} = this.data;
|
||||
fail && fail({
|
||||
errMsg: 'showactionsheet:fail cancel'
|
||||
});
|
||||
this.triggerEvent('lincancel', {
|
||||
errMsg: 'showactionsheet:fail cancel'
|
||||
},{ bubbles: true, composed: true });
|
||||
this._hideActionSheet();
|
||||
},
|
||||
|
||||
handleClickPopUp() {
|
||||
if (!this.data.locked) {
|
||||
this.handleClickCancel();
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"l-icon":"../icon/index",
|
||||
"l-popup":"../popup/index",
|
||||
"l-button":"../button/index"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
.l-action-sheet {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
||||
.l-item-button {
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
border-bottom: 2rpx solid #f3f3f3;
|
||||
font-size: 28rpx;
|
||||
color: #45526b;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.l-cancel {
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
|
||||
.l-cancel-x .l-item-button {
|
||||
padding-bottom: 44rpx;
|
||||
}
|
||||
|
||||
.l-image-button > .l-button-text {
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
|
||||
.list-hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<l-popup show="{{show}}" showMask="{{true}}" contentAlign="bottom" locked="{{locked}}" bind:lintap="handleClickPopUp" z-index="{{zIndex}}">
|
||||
<view class='l-action-sheet'>
|
||||
<view class="l-item-button l-class-title l-title-class" wx:if="{{title}}">
|
||||
{{ title }}
|
||||
</view>
|
||||
<view wx:for="{{ itemList }}" wx:key="name" hover-class="{{isHover?'list-hover':''}}">
|
||||
<l-button bind:lintap="handleClickItem" data-index="{{ index }}" data-item="{{ item }}" open-type="{{ item.openType }}" icon="{{ item.icon }}" type="default" size="large" special="{{true}}" long>
|
||||
<view style="{{ item.color ? 'color: ' + item.color : '' }}" class="l-item-button l-class-item l-item-class {{item.image || item.icon ? 'l-image-button':''}}">
|
||||
<image wx:if="{{item.image}}" class="l-button-image" src="{{item.image}}" style="{{item.imageStyle}}"/>
|
||||
<l-icon
|
||||
wx:elif="{{ item.icon }}"
|
||||
name="{{ item.icon }}"
|
||||
l-class="l-item-button"
|
||||
size="{{ item.iconSize }}"
|
||||
color="{{item.iconColor?item.iconColor:item.color}}"></l-icon>
|
||||
<text class="l-button-text">{{ item.name }}</text>
|
||||
</view>
|
||||
</l-button>
|
||||
</view>
|
||||
<view class="l-cancel l-class-cancel l-cancel-class {{isIphoneX ? 'l-cancel-x':''}}" wx:if="{{ showCancel }}" hover-class="{{isHover?'list-hover':''}}">
|
||||
<l-button type="default" size="large" long="true" bind:lintap="handleClickCancel" special="{{true}}">
|
||||
<view class="l-item-button l-cancel-button">{{ cancelText }}</view>
|
||||
</l-button>
|
||||
</view>
|
||||
</view>
|
||||
</l-popup>
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
// miniprogram_npm/lin-ui/album/index.js
|
||||
Component({
|
||||
externalClasses: ['l-class', 'l-single-image-class', 'l-multi-image-class'],
|
||||
properties: {
|
||||
urls: {
|
||||
type: Array
|
||||
},
|
||||
// 是否可以预览
|
||||
preview: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
gapRow: {
|
||||
type: Number,
|
||||
value: 10,
|
||||
},
|
||||
gapColumn: {
|
||||
type: Number,
|
||||
value: 10,
|
||||
},
|
||||
// 单图时长边大小
|
||||
singleSize: {
|
||||
type: Number,
|
||||
value: 360,
|
||||
},
|
||||
// 多图时图片边长
|
||||
multipleSize: {
|
||||
type: Number,
|
||||
value: 158,
|
||||
},
|
||||
// 单图显示模式
|
||||
singleMode: {
|
||||
type: String,
|
||||
value: 'aspectFit',
|
||||
},
|
||||
// 多图显示模式
|
||||
multipleMode: {
|
||||
type: String,
|
||||
value: 'aspectFill',
|
||||
},
|
||||
key: {
|
||||
type: String,
|
||||
value: 'url'
|
||||
},
|
||||
maxNumber: {
|
||||
type: Number,
|
||||
value: 9
|
||||
},
|
||||
customRowNumber: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
everyRowNumber: {
|
||||
type: Number,
|
||||
value: 3
|
||||
},
|
||||
previewFullImage: {
|
||||
type: Boolean,
|
||||
value: true,
|
||||
}
|
||||
},
|
||||
|
||||
data: {
|
||||
// 传值方式是新方式还是旧方式
|
||||
newType: true,
|
||||
// 单图短边大小
|
||||
shortSideValue: 0,
|
||||
// 用于显示的图片列表
|
||||
showUrls: [],
|
||||
// 传入的url长度是否大于maxNumber指定的数量
|
||||
isLong: false,
|
||||
},
|
||||
|
||||
observers: {
|
||||
'urls': function () {
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
/**
|
||||
* 在 urls 数据变化后进行初始化
|
||||
*/
|
||||
init() {
|
||||
// 取出参数
|
||||
let {urls, maxNumber, key} = this.data;
|
||||
|
||||
// 如果 urls 长度超出指定图片数量,则将其截断
|
||||
if (urls.length > maxNumber) {
|
||||
urls = urls.slice(0, maxNumber);
|
||||
this.setData({
|
||||
isLong: true,
|
||||
});
|
||||
}
|
||||
|
||||
this.setData({
|
||||
showUrls: urls
|
||||
});
|
||||
|
||||
if (!this.data.customRowNumber) {
|
||||
let urlLength = this.data.showUrls.length;
|
||||
if (urlLength > 1 && urlLength < 5 && urlLength !== 3) {
|
||||
this.setData({
|
||||
everyRowNumber: 2
|
||||
});
|
||||
} else (this.setData({
|
||||
everyRowNumber: 3
|
||||
}));
|
||||
}
|
||||
|
||||
// 判断传入模式
|
||||
const newType = this.judgeType();
|
||||
this.setData({
|
||||
newType
|
||||
});
|
||||
|
||||
if (urls.length === 1) {
|
||||
this.horizontalOrVertical(newType ? urls[0][key] : urls[0]);
|
||||
}
|
||||
},
|
||||
|
||||
// 判断传入的urls是字符串列表(old模式)还是对象列表(new模式)
|
||||
judgeType() {
|
||||
const urls = this.data.urls;
|
||||
if (urls.length !== 0) {
|
||||
if (typeof (urls[0]) !== 'object') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
//判断照片是横屏还是竖屏并计算短边的长度
|
||||
//如不指定短边的长度,短边会默认显示image组件的长度
|
||||
horizontalOrVertical: function (src) {
|
||||
wx.getImageInfo({
|
||||
src: src,
|
||||
success: (res) => {
|
||||
const longSide = res.width >= res.height ? res.width : res.height;
|
||||
const shortSide = res.width >= res.height ? res.height : res.width;
|
||||
this.setData({
|
||||
horizontalScreen: res.width >= res.height,
|
||||
shortSideValue: shortSide * this.data.singleSize / longSide
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onPreviewTap(e) {
|
||||
const index = e.currentTarget.id;
|
||||
let urls;
|
||||
if (this.data.previewFullImage) {
|
||||
urls = this.data.urls;
|
||||
} else {
|
||||
urls = this.data.showUrls;
|
||||
}
|
||||
|
||||
let tempFilePath = '';
|
||||
let previewImageList = [];
|
||||
const newType = this.data.newType;
|
||||
const key = this.data.key;
|
||||
|
||||
if (newType) {
|
||||
tempFilePath = urls[index][key];
|
||||
for (let i = 0; i < urls.length; i++) {
|
||||
previewImageList.push(urls[i][key]);
|
||||
}
|
||||
} else {
|
||||
tempFilePath = urls[index];
|
||||
previewImageList = urls;
|
||||
}
|
||||
|
||||
let detail = {
|
||||
index, // 下标
|
||||
current: urls[index], // 当前显示图片的http链接
|
||||
all: urls // 需要预览的图片http链接列表
|
||||
};
|
||||
let option = {};
|
||||
if (this.data.preview === true) {
|
||||
wx.previewImage({
|
||||
current: tempFilePath, // 当前显示图片的http链接
|
||||
urls: previewImageList // 需要预览的图片http链接列表
|
||||
});
|
||||
}
|
||||
this.triggerEvent('lintap', detail, option);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/* miniprogram_npm/lin-ui/picture-album/index.wxss */
|
||||
.container {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.vertical {
|
||||
height: 360rpx;
|
||||
}
|
||||
|
||||
.parent {
|
||||
display: inline-grid;
|
||||
grid-template-columns: repeat(auto-fill);
|
||||
}
|
||||
|
||||
.child {
|
||||
box-sizing: border-box;
|
||||
background-color: white;
|
||||
flex: 0 0 44%;
|
||||
height: 100px;
|
||||
border: 1px solid red;
|
||||
margin-top: 4%;
|
||||
margin-left: 2%;
|
||||
margin-right: 2%;
|
||||
}
|
||||
|
||||
.dimback {
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.dim {
|
||||
opacity: 0.6;
|
||||
filter: alpha(opacity=60);
|
||||
}
|
||||
|
||||
.imageContainer {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.imageContainer:last-child > .text {
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: larger;
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<wxs src="index.wxs" module="album"></wxs>
|
||||
<!-- 最外层 view 用于修复 issue 1100 -->
|
||||
<view class="l-class" style="display:flex">
|
||||
<view style="{{album.gridStyle(gapRow, gapColumn, multipleSize, everyRowNumber)}}">
|
||||
<block wx:for="{{showUrls}}" wx:key="index" class="child">
|
||||
<view class="imageContainer {{album.dimBack(isLong, index, maxNumber, previewFullImage)}}" style="{{album.blockStyle(urls, horizontalScreen, shortSideValue, singleSize, multipleSize)}}">
|
||||
<image class="{{album.blockClass(urls, horizontalScreen) + ' ' + album.dim(isLong, index, maxNumber, previewFullImage)}}" id='{{index}}' bind:tap="onPreviewTap" style="{{album.blockStyle(urls, horizontalScreen, shortSideValue, singleSize, multipleSize)}}" src="{{newType?item[key]:item}}" mode="{{urls.length === 1?singleMode:multipleMode}}" />
|
||||
<view wx:if="{{album.image(isLong, index, maxNumber, previewFullImage)}}" class="text">+{{urls.length-maxNumber}}</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
var containerStyle = function (urls, multipleSize, gapRow, gapColumn) {
|
||||
if (urls.length === 2 || urls.length === 4) {
|
||||
return 'width:' + (2 * multipleSize + gapRow) + 'rpx; grid-row-gap:' + gapColumn + 'rpx; grid-column-gap:' + gapRow + 'rpx;grid-template-columns:repeat(auto-fit, ' + multipleSize + 'rpx);'
|
||||
} else {
|
||||
return 'width:' + (3 * multipleSize + 2 * gapRow) + 'rpx; grid-row-gap:' + gapColumn + 'rpx; grid-column-gap:' + gapRow + 'rpx;grid-template-columns:repeat(auto-fit, ' + multipleSize + 'rpx);'
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var blockClass = function (urls, horizontalScreen) {
|
||||
if (urls.length === 1) {
|
||||
if (horizontalScreen) {
|
||||
return 'l-single-image-class'
|
||||
} else {
|
||||
return 'vertical l-single-image-class'
|
||||
}
|
||||
} else {
|
||||
return 'l-multi-image-class'
|
||||
}
|
||||
}
|
||||
|
||||
var blockStyle = function (urls, horizontalScreen, shortSideValue, singleSize, multipleSize) {
|
||||
if (urls.length === 1) {
|
||||
if (horizontalScreen) {
|
||||
return 'height:' + shortSideValue + 'rpx;width:' + singleSize + 'rpx;'
|
||||
} else {
|
||||
return 'width:' + shortSideValue + 'rpx;height:' + singleSize + 'rpx;'
|
||||
}
|
||||
} else {
|
||||
return 'height:' + multipleSize + 'rpx;width:' + multipleSize + 'rpx;'
|
||||
}
|
||||
}
|
||||
|
||||
var gridStyle = function (gapRow, gapColumn, multipleSize, everyRowNumber) {
|
||||
return 'display:inline-grid;grid-template-columns: repeat(' + everyRowNumber + ',' + multipleSize + 'rpx);grid-row-gap:' + gapRow + 'rpx;grid-column-gap:' + gapColumn + 'rpx;'
|
||||
}
|
||||
|
||||
var dimBack = function (isLong, index, maxNumber, previewFullImage) {
|
||||
if (previewFullImage) {
|
||||
if (isLong) {
|
||||
if (index == maxNumber - 1) {
|
||||
return 'dimback'
|
||||
}
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
var dim = function (isLong, index, maxNumber, previewFullImage) {
|
||||
if (previewFullImage) {
|
||||
if (isLong) {
|
||||
if (index == maxNumber - 1) {
|
||||
return 'dim'
|
||||
}
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
var image = function (isLong, index, maxNumber, previewFullImage) {
|
||||
if (previewFullImage) {
|
||||
if (isLong) {
|
||||
if (index == maxNumber - 1) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
containerStyle: containerStyle,
|
||||
blockClass: blockClass,
|
||||
blockStyle: blockStyle,
|
||||
gridStyle: gridStyle,
|
||||
dimBack: dimBack,
|
||||
dim: dim,
|
||||
image: image,
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
import validator from '../behaviors/validator';
|
||||
import zIndex from '../behaviors/zIndex';
|
||||
const detail = true;
|
||||
const option = {
|
||||
bubbles: true,
|
||||
composed: true
|
||||
};
|
||||
|
||||
Component({
|
||||
/**
|
||||
* 组件的属性列表
|
||||
*/
|
||||
behaviors: [zIndex, validator],
|
||||
externalClasses: ['l-class', 'l-panel-class', 'l-bg-class', 'l-header-class'],
|
||||
options: {
|
||||
multipleSlots: true // 在组件定义时的选项中启用多slot支持
|
||||
},
|
||||
properties: {
|
||||
// 显示与隐藏
|
||||
show: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
// 最大高度
|
||||
maxHeight: {
|
||||
type: Number,
|
||||
value: 600
|
||||
},
|
||||
// 最小高度
|
||||
minHeight: {
|
||||
type: Number,
|
||||
value: 200
|
||||
},
|
||||
// 顶部弧度
|
||||
arcRadius: {
|
||||
type: Number,
|
||||
value: 18
|
||||
},
|
||||
// 动画效果的显示和隐藏
|
||||
transition: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
// 锁定
|
||||
locked: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
// 背景透明度
|
||||
opacity: {
|
||||
type: Number,
|
||||
value: 0.4
|
||||
},
|
||||
// 弹出方向
|
||||
direction: {
|
||||
type: String,
|
||||
options: ['top', 'bottom'],
|
||||
value: 'bottom'
|
||||
},
|
||||
// header是否吸顶
|
||||
headerFixed: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 组件的初始数据
|
||||
*/
|
||||
data: {
|
||||
_arcRadiusTop: 12,
|
||||
_ardRadiusBottom: 18,
|
||||
arcStyle: ''
|
||||
},
|
||||
|
||||
/**
|
||||
* 侦听器
|
||||
*/
|
||||
observers: {
|
||||
'show': function (show) {
|
||||
if (show) {
|
||||
this.triggerEvent('linshow', detail, option);
|
||||
this.getArcPopupStyle();
|
||||
} else {
|
||||
this.triggerEvent('linclose', detail, option);
|
||||
}
|
||||
},
|
||||
'arcRadius': function (arcRadius) {
|
||||
if (this.properties.direction === 'top') {
|
||||
this.data._arcRadiusTop = arcRadius;
|
||||
} else {
|
||||
this.data._ardRadiusBottom = arcRadius;
|
||||
}
|
||||
this.getArcPopupStyle();
|
||||
}
|
||||
},
|
||||
|
||||
pageLifetimes: {
|
||||
show() {
|
||||
this._init();
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 组件的方法列表
|
||||
*/
|
||||
methods: {
|
||||
_init() {
|
||||
wx.lin = wx.lin || {};
|
||||
wx.lin.showArcPopup = (options) => {
|
||||
const {
|
||||
zIndex = 99,
|
||||
tranistion = true,
|
||||
direction = 'bottom',
|
||||
locked = false
|
||||
} = {
|
||||
...options
|
||||
};
|
||||
this.setData({
|
||||
zIndex,
|
||||
tranistion,
|
||||
direction,
|
||||
locked,
|
||||
show: true
|
||||
});
|
||||
};
|
||||
wx.lin.hideArcPopup = () => {
|
||||
this.setData({
|
||||
show: false
|
||||
});
|
||||
};
|
||||
},
|
||||
getArcPopupStyle() {
|
||||
const direction = this.properties.direction;
|
||||
const arcRadiusTop = this.data._arcRadiusTop;
|
||||
const ardRadiusBottom = this.data._ardRadiusBottom;
|
||||
const maxHeight = this.properties.maxHeight;
|
||||
const minHeight = this.properties.minHeight;
|
||||
const arcStyle = `
|
||||
border-bottom-left-radius:${direction === 'top' ? arcRadiusTop : 0}rpx;
|
||||
border-bottom-right-radius:${direction === 'top' ? arcRadiusTop : 0}rpx;
|
||||
border-top-left-radius:${direction === 'bottom' ? ardRadiusBottom : 0}rpx;
|
||||
border-top-right-radius:${direction === 'bottom' ? ardRadiusBottom : 0}rpx;
|
||||
max-height:${maxHeight}rpx;
|
||||
min-height:${minHeight}rpx;
|
||||
`;
|
||||
this.setData({
|
||||
arcStyle,
|
||||
});
|
||||
},
|
||||
onArcPopupTap() {
|
||||
if (this.data.locked) {
|
||||
return;
|
||||
}
|
||||
if (this.properties.show) {
|
||||
this.setData({
|
||||
show: false
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ready() {
|
||||
this.getArcPopupStyle();
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"l-popup": "../popup/index"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
.container-arc-popup {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.arc-popup {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.content-arc-popup {
|
||||
padding: 30rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.header-popup {
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
|
||||
&.fixed {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<l-popup
|
||||
show="{{show}}"
|
||||
direction="{{direction}}"
|
||||
transition="{{transition}}"
|
||||
opacity="{{opacity}}"
|
||||
locked="{{locked}}"
|
||||
z-index="{{zIndex}}"
|
||||
l-class="l-class"
|
||||
l-bg-class="l-bg-class"
|
||||
bind:lintap="onArcPopupTap"
|
||||
>
|
||||
<scroll-view
|
||||
scroll-y="true"
|
||||
class="arc-popup l-panel-class"
|
||||
style="{{arcStyle}}"
|
||||
>
|
||||
<view class="header-popup {{headerFixed ? 'fixed' : ''}} l-header-class">
|
||||
<slot name="header" />
|
||||
</view>
|
||||
<view class="content-arc-popup">
|
||||
<slot />
|
||||
</view>
|
||||
</scroll-view>
|
||||
</l-popup>
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
Component({
|
||||
externalClasses: [
|
||||
'l-class',
|
||||
'l-class-text',
|
||||
'l-text-class'
|
||||
],
|
||||
properties: {
|
||||
icon: String,
|
||||
iconColor: {
|
||||
type: String,
|
||||
value: '#3963BC'
|
||||
},
|
||||
iconSize: {
|
||||
type: String,
|
||||
value: '28'
|
||||
},
|
||||
text: String,
|
||||
src: String,
|
||||
openData: {
|
||||
type: Array,
|
||||
observer: '_initOpenData'
|
||||
},
|
||||
shape: {
|
||||
type: String,
|
||||
value: 'circle'
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
value: 'scaleToFill'
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
value: 120,
|
||||
},
|
||||
placement: {
|
||||
type: String,
|
||||
value: 'right'
|
||||
},
|
||||
},
|
||||
data: {
|
||||
_isHaveUserNickName: false,
|
||||
_isHaveUserAvatarUrl: false,
|
||||
_iconSize: '',
|
||||
_iconColor: '#ffffff'
|
||||
},
|
||||
methods: {
|
||||
_initOpenData: function (openData) {
|
||||
this._isHaveUserAvatarUrl(openData);
|
||||
this._isHaveUserNickName(openData);
|
||||
},
|
||||
|
||||
_isHaveUserAvatarUrl: function (openData) {
|
||||
this.setData({
|
||||
_isHaveUserAvatarUrl: openData.indexOf('userAvatarUrl') !== -1
|
||||
});
|
||||
},
|
||||
|
||||
_isHaveUserNickName: function (openData) {
|
||||
this.setData({
|
||||
_isHaveUserNickName: openData.indexOf('userNickName') !== -1
|
||||
});
|
||||
},
|
||||
tapAvatar: function (e) {
|
||||
this.triggerEvent('lintap', e, {
|
||||
bubbles: true,
|
||||
composed: true
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"l-icon":"../icon/index"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
@import "../../config/styles/_base.less";
|
||||
|
||||
.l-avatar {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.l-avatar-image {
|
||||
flex: 1;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: @avatar-bg;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.l-avatar-text {
|
||||
display: inline-block;
|
||||
height: max-content;
|
||||
width: max-content;
|
||||
font-size: 28rpx;
|
||||
color: rgb(69 82 107 / 100%);
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.open-data {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.l-avatar-text .open-data,
|
||||
.l-avatar-text .l-avatar-text-text {
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.l-square {
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.l-circle {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.l-placement-left,
|
||||
.l-placement-right {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.l-placement-left {
|
||||
margin-right: 24rpx;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.l-placement-left .l-avatar-text {
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.l-placement-right {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.l-placement-right .l-avatar-text {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
|
||||
.l-placement-top {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.l-placement-top .l-avatar-text {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.l-placement-bottom {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.l-placement-bottom .l-avatar-text {
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
<view class="l-avatar {{text||_isHaveUserNickName?'l-placement-'+placement:''}}" mut-bind:tap="tapAvatar">
|
||||
<view class="l-avatar-image {{shape?'l-'+shape:''}} l-class" wx:if="{{_isHaveUserAvatarUrl||icon||src}}" style="width:{{size}}rpx;height:{{size}}rpx;min-width:{{size}}rpx;min-height:{{size}}rpx;">
|
||||
<open-data class="open-data" wx:if="{{_isHaveUserAvatarUrl}}" type="userAvatarUrl" />
|
||||
<l-icon wx:elif="{{icon}}" size="{{iconSize || size*0.6}}" color="{{iconColor||'#ffffff'}}" name="{{icon}}" />
|
||||
<image wx:elif="{{src}}" src="{{src}}" mode="{{mode}}" style="width:{{size}}rpx;height:{{size}}rpx" />
|
||||
</view>
|
||||
<view class="l-avatar-text l-class-text l-text-class" wx:if="{{text||_isHaveUserNickName}}">
|
||||
<open-data class="open-data" wx:if="{{_isHaveUserNickName}}" type="userNickName" />
|
||||
<text class="l-avatar-text-text" wx:elif="{{text}}">{{text}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
import validator from '../behaviors/validator';
|
||||
|
||||
Component({
|
||||
externalClasses: ['l-class', 'l-class-self', 'l-self-class'],
|
||||
behaviors: [validator],
|
||||
properties: {
|
||||
// 红点模式
|
||||
dot: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
shape: {
|
||||
type: String,
|
||||
value: 'horn',
|
||||
options: ['horn', 'circle']
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
value: '0'
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
value: 'number',
|
||||
options: ['number', 'text']
|
||||
},
|
||||
// 数字最大值
|
||||
maxCount: {
|
||||
type: Number,
|
||||
value: 99
|
||||
},
|
||||
// 数字形式
|
||||
numberType: {
|
||||
type: String,
|
||||
value: 'overflow',
|
||||
options: ['overflow', 'limit', 'ellipsis']
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
}
|
||||
},
|
||||
data: {
|
||||
finalCount: 0
|
||||
},
|
||||
observers: {
|
||||
'value': function () {
|
||||
this.finalCount();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 最终数字
|
||||
finalCount() {
|
||||
if (isNaN(Number(this.data.value)) || (this.data.mode === 'text')) {
|
||||
this.setData({
|
||||
finalCount: this.data.value
|
||||
});
|
||||
} else {
|
||||
this.switchType();
|
||||
}
|
||||
},
|
||||
switchType() {
|
||||
switch (this.data.numberType) {
|
||||
case 'overflow':
|
||||
this.setData({
|
||||
finalCount: Number(this.data.value) > Number(this.data.maxCount) ? `${this.data.maxCount}+` : this.data.value
|
||||
});
|
||||
break;
|
||||
case 'ellipsis':
|
||||
this.setData({
|
||||
finalCount: Number(this.data.value) > Number(this.data.maxCount) ? '...' : this.data.value
|
||||
});
|
||||
break;
|
||||
case 'limit':
|
||||
this.setData({
|
||||
finalCount: Number(this.data.value) > 999 ? (Number(this.data.value) >= 9999 ? Math.floor(this.data.value / 10000 * 100) / 100 + 'w' : Math.floor(this.data.value / 1000 * 100) / 100 + 'k') : this.data.value
|
||||
});
|
||||
break;
|
||||
default:
|
||||
this.setData({
|
||||
finalCount: Number(this.data.value)
|
||||
});
|
||||
break;
|
||||
}
|
||||
},
|
||||
// 点击事件
|
||||
handleTap() {
|
||||
this.triggerEvent('lintap', {}, {
|
||||
bubbles: true,
|
||||
composed: true
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
@import "../../config/styles/_base.less";
|
||||
|
||||
.l-badge {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
vertical-align: middle;
|
||||
|
||||
&-content {
|
||||
max-width: 650rpx;
|
||||
left: 70%;
|
||||
background-color: #ff474b;
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
font-size: 20rpx;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
z-index: 10;
|
||||
letter-spacing: 0.5rpx;
|
||||
|
||||
&-horn {
|
||||
height: 28rpx;
|
||||
min-width: 54rpx;
|
||||
padding: 0 8rpx;
|
||||
border-radius: 14rpx 14rpx 14rpx 0rpx;
|
||||
line-height: 28rpx;
|
||||
top: -10rpx;
|
||||
}
|
||||
|
||||
&-circle {
|
||||
height: 32rpx;
|
||||
min-width: 32rpx;
|
||||
padding: 0 10rpx;
|
||||
border-radius: 16rpx;
|
||||
line-height: 32rpx;
|
||||
top: -10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&-dot {
|
||||
height: 16rpx;
|
||||
width: 16rpx;
|
||||
transform: translateX(50%);
|
||||
border-radius: 50%;
|
||||
background-color: #ff474b;
|
||||
position: absolute;
|
||||
top: -4rpx;
|
||||
right: 0rpx;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<view class="l-badge" mut-bind:tap="handleTap">
|
||||
<slot/>
|
||||
<block wx:if="{{show}}">
|
||||
<view wx:if="{{dot}}" class="l-badge-dot l-class l-class-self l-self-class"></view>
|
||||
<view wx:else class="{{'l-badge-content-'+shape}} l-badge-content l-class l-class-self l-self-class">{{finalCount}}</view>
|
||||
</block>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// eslint-disable-next-line no-undef
|
||||
export default Behavior({
|
||||
behaviors: [],
|
||||
properties: {},
|
||||
data: {
|
||||
distance: 0
|
||||
},
|
||||
attached(){
|
||||
this.offsetMargin();
|
||||
},
|
||||
methods: {
|
||||
offsetMargin() {
|
||||
const { windowHeight, screenHeight } = wx.getSystemInfoSync();
|
||||
this.setData({
|
||||
distance: (screenHeight-windowHeight )
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
// eslint-disable-next-line no-undef
|
||||
export default Behavior({
|
||||
behaviors: [],
|
||||
properties: {
|
||||
time: {
|
||||
optionalTypes: [String, Object],
|
||||
value: new Date().getTime() + 86400000,
|
||||
observer: function (newVal, oldVal) {
|
||||
if (newVal && !oldVal) {
|
||||
this.getLatestTime();
|
||||
}
|
||||
}
|
||||
},
|
||||
status: {
|
||||
type: Boolean,
|
||||
value: true,
|
||||
observer: function (newVal) {
|
||||
if (newVal) {
|
||||
this.init();
|
||||
} else if (!newVal) {
|
||||
clearInterval(this.data.timer);
|
||||
}
|
||||
}
|
||||
},
|
||||
timeType: {
|
||||
type: String,
|
||||
value: 'datetime'
|
||||
},
|
||||
format: {
|
||||
type: String,
|
||||
value: '{%d}天{%h}时{%m}分{%s}秒'
|
||||
},
|
||||
isZeroPadd: {
|
||||
type: Boolean,
|
||||
value: true,
|
||||
},
|
||||
countdownType: {
|
||||
type: String,
|
||||
value: 'normal'
|
||||
},
|
||||
isClearInterval: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
}
|
||||
},
|
||||
data: {
|
||||
initAddTime: 0,
|
||||
timer: null,
|
||||
date: [],
|
||||
},
|
||||
ready: function () {
|
||||
this.getLatestTime();
|
||||
},
|
||||
|
||||
detached: function () {
|
||||
if (this.data.isClearInterval) {
|
||||
clearInterval(this.data.timer);
|
||||
}
|
||||
},
|
||||
|
||||
pageLifetimes: {
|
||||
hide() {
|
||||
if (this.data.isClearInterval) {
|
||||
clearInterval(this.data.timer);
|
||||
}
|
||||
|
||||
},
|
||||
show() {
|
||||
if (this.data.isClearInterval) {
|
||||
this.getLatestTime();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 自动补零
|
||||
zeroPadding(num) {
|
||||
num = num.toString();
|
||||
return num[1] ? num : '0' + num;
|
||||
},
|
||||
|
||||
init() {
|
||||
clearInterval(this.data.timer);
|
||||
const timer = setTimeout(() => {
|
||||
this.getLatestTime.call(this);
|
||||
}, 1000);
|
||||
this.setData({
|
||||
timer
|
||||
});
|
||||
},
|
||||
|
||||
getLatestTime() {
|
||||
let {
|
||||
time,
|
||||
status,
|
||||
timeType,
|
||||
initAddTime,
|
||||
countdownType,
|
||||
} = this.data;
|
||||
// IOS不支持2019-04-23 的日期格式
|
||||
let countDownTime = time;
|
||||
if (countdownType === 'normal') { //当countdownType === normal时,不影响之前的代码
|
||||
if (timeType !== 'second') {
|
||||
countDownTime = typeof time === 'string' ? countDownTime.replace(/-/g, '/') : countDownTime;
|
||||
countDownTime = Math.ceil((new Date(countDownTime).getTime() - new Date().getTime()) / 1000);
|
||||
}
|
||||
|
||||
if (countDownTime < 0 && timeType !== 'second') {
|
||||
this._getTimeValue(0);
|
||||
this.CountdownEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
if (countDownTime - initAddTime > 0) {
|
||||
this.getLatestForCountDown(countDownTime);
|
||||
} else if (countDownTime - initAddTime < 0) {
|
||||
this.getLatestForAddTime(countDownTime);
|
||||
} else if (countDownTime - initAddTime === 0) {
|
||||
if (initAddTime <= 0) {
|
||||
this._getTimeValue(countDownTime);
|
||||
}
|
||||
this.CountdownEnd();
|
||||
}
|
||||
|
||||
if (status && countDownTime - initAddTime !== 0) {
|
||||
this.init.call(this);
|
||||
}
|
||||
|
||||
} else if (countdownType === 'anniversary') { // 当countdownType === anniversary时,为纪念日模式
|
||||
if (timeType === 'second') { // 纪念日模式不能设置timeType === second
|
||||
console.error(`countdownType为${countdownType}类型时,不可设置timeType值为second`);
|
||||
} else {
|
||||
countDownTime = typeof time === 'string' ? countDownTime.replace(/-/g, '/') : countDownTime;
|
||||
countDownTime = Math.ceil((new Date().getTime() - new Date(countDownTime).getTime()) / 1000);
|
||||
if (countDownTime >= 0) { // countDownTime计算结果不能为负数
|
||||
this.getLatestForCountDown(countDownTime);
|
||||
this.init.call(this);
|
||||
} else {
|
||||
console.error('time传值错误');
|
||||
}
|
||||
}
|
||||
} else { // countdownType 不能设置为 normal,anniversary 以外的值
|
||||
console.error('错误的countdownType类型');
|
||||
}
|
||||
},
|
||||
|
||||
getLatestForAddTime(countDownTime) {
|
||||
let {
|
||||
initAddTime
|
||||
} = this.data;
|
||||
if (initAddTime !== Math.abs(countDownTime)) {
|
||||
initAddTime++;
|
||||
this._getTimeValue(initAddTime);
|
||||
this.setData({
|
||||
initAddTime
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
getLatestForCountDown(countDownTime) {
|
||||
this._getTimeValue(countDownTime);
|
||||
this.setData({
|
||||
time: this.data.timeType === 'second' ? --countDownTime : this.data.time,
|
||||
});
|
||||
},
|
||||
|
||||
_getTimeValue(countDownTime) {
|
||||
const {
|
||||
format
|
||||
} = this.data;
|
||||
const date = [];
|
||||
const fomatArray = format.split(/(\{.*?\})/);
|
||||
const formatType = [{
|
||||
key: '{%d}',
|
||||
type: 'day',
|
||||
count: 86400
|
||||
}, {
|
||||
key: '{%h}',
|
||||
type: 'hour',
|
||||
count: 3600
|
||||
}, {
|
||||
key: '{%m}',
|
||||
type: 'minute',
|
||||
count: 60
|
||||
}, {
|
||||
key: '{%s}',
|
||||
type: 'second',
|
||||
count: 1,
|
||||
}];
|
||||
let diffSecond = countDownTime;
|
||||
formatType.forEach(format => {
|
||||
const index = this._findTimeName(fomatArray, format.key);
|
||||
if (index === -1) return;
|
||||
const name = fomatArray[index];
|
||||
const formatItem = {
|
||||
type: format.type,
|
||||
name,
|
||||
value: parseInt(diffSecond / format.count)
|
||||
};
|
||||
if (this.data.isZeroPadd) {
|
||||
formatItem.value = this.zeroPadding(formatItem.value);
|
||||
}
|
||||
diffSecond %= format.count;
|
||||
date.push(formatItem);
|
||||
});
|
||||
this.setData({
|
||||
date
|
||||
});
|
||||
return date;
|
||||
},
|
||||
|
||||
_findTimeName(fomatArray, str) {
|
||||
const index = fomatArray.indexOf(str);
|
||||
if (index === -1) return -1;
|
||||
return index + 1;
|
||||
},
|
||||
|
||||
CountdownEnd() {
|
||||
this.triggerEvent('linend', {});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// eslint-disable-next-line no-undef
|
||||
export default Behavior({
|
||||
behaviors: [],
|
||||
properties: {
|
||||
isHover:{
|
||||
type: Boolean,
|
||||
value: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
import Schema from '../common/async-validator/index';
|
||||
import validator from '../behaviors/validator';
|
||||
|
||||
/**
|
||||
* @param tipType String [toast , message , text]
|
||||
*/
|
||||
// eslint-disable-next-line no-undef
|
||||
export default Behavior({
|
||||
behaviors: [validator],
|
||||
properties: {
|
||||
// 校验
|
||||
rules: {
|
||||
optionalTypes: [Object, Array],
|
||||
value: []
|
||||
},
|
||||
tipType: {
|
||||
type: String,
|
||||
value: 'toast',
|
||||
options: ['toast', 'message', 'text']
|
||||
}
|
||||
},
|
||||
data: {
|
||||
schema: '',
|
||||
tipFun: {
|
||||
'message': 'showMessage',
|
||||
'toast': 'showToast',
|
||||
},
|
||||
tipContent: {
|
||||
'message': 'content',
|
||||
'toast': 'title',
|
||||
},
|
||||
errorText: '',
|
||||
errors: []
|
||||
},
|
||||
|
||||
methods: {
|
||||
initRules() {
|
||||
// const rulesName = this.data.name;
|
||||
const {
|
||||
rules
|
||||
} = this.data;
|
||||
if (!rules) return;
|
||||
// 如果rule 是单个object
|
||||
if (Object.prototype.toString.call(rules) === '[object Object]') {
|
||||
this.data.rules = [rules];
|
||||
}
|
||||
|
||||
this.data.rules.forEach(item => {
|
||||
if (!item.trigger) {
|
||||
item.trigger = [];
|
||||
return;
|
||||
}
|
||||
if (typeof item.trigger === 'string') {
|
||||
item.trigger = [item.trigger];
|
||||
return;
|
||||
}
|
||||
// if(Object.prototype.toString.call(item.trigger) === '[object Object]') {
|
||||
// item.trigger = ['blur'];
|
||||
// return;
|
||||
// }
|
||||
});
|
||||
|
||||
},
|
||||
getNeedValidateRule(type) {
|
||||
const rulesName = this.data.name;
|
||||
const {
|
||||
rules
|
||||
} = this.data;
|
||||
if (!rules) return;
|
||||
|
||||
const list = type ? rules.filter(item => {
|
||||
return item.trigger.indexOf(type) > -1;
|
||||
}) : rules;
|
||||
const schema = new Schema({
|
||||
[rulesName]: list,
|
||||
});
|
||||
this.setData({
|
||||
schema,
|
||||
});
|
||||
return list;
|
||||
},
|
||||
validatorData(value, type) {
|
||||
const {
|
||||
tipType,
|
||||
tipFun,
|
||||
tipContent
|
||||
} = this.data;
|
||||
const rules = this.getNeedValidateRule(type);
|
||||
|
||||
if (!rules) return;
|
||||
|
||||
// 把空字符串设置为 undefined ,见 issue 856
|
||||
// async-validator 对空字符串会进行类型检查,与required会冲突
|
||||
Object.getOwnPropertyNames(value).forEach((key) => {
|
||||
if (value[key] === '') {
|
||||
value[key] = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
this.data.schema.validate(value, (errors) => {
|
||||
this.setData({
|
||||
errors: errors || []
|
||||
});
|
||||
|
||||
this.triggerEvent('linvalidate', {
|
||||
errors,
|
||||
isError: !!errors
|
||||
});
|
||||
|
||||
if (errors && tipType) {
|
||||
const funName = tipFun[tipType];
|
||||
const contentName = tipContent[tipType];
|
||||
if (tipType === 'text') {
|
||||
this.setData({
|
||||
errorText: errors[0].message
|
||||
});
|
||||
return errors;
|
||||
}
|
||||
|
||||
if (!wx.lin || !wx.lin[funName]) {
|
||||
wx.showToast({
|
||||
icon: 'none',
|
||||
title: `请在页面内引入${tipType}组件`
|
||||
});
|
||||
return errors;
|
||||
}
|
||||
|
||||
wx.lin[funName] && wx.lin[funName]({
|
||||
[contentName]: errors[0].message,
|
||||
duration: 1500,
|
||||
mask: false,
|
||||
});
|
||||
return errors;
|
||||
} else if (!errors && tipType) {
|
||||
this.setData({
|
||||
errorText: ''
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// eslint-disable-next-line no-undef
|
||||
export default Behavior({
|
||||
methods: {
|
||||
getRect(selector, all = false) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = wx.createSelectorQuery().in(this);
|
||||
const type = all ? query.selectAll(selector) : query.select(selector);
|
||||
type.boundingClientRect((res) => {
|
||||
if (!res) return reject('找不到元素');
|
||||
resolve(res);
|
||||
}).exec();
|
||||
});
|
||||
},
|
||||
queryScrollNode(res, currentIndex, type = 'width') {
|
||||
if (currentIndex < 0) return;
|
||||
const currentRect = res[currentIndex];
|
||||
this.getRect('.l-tabsscroll').then(_ => {
|
||||
if (!_) return console.error('找不到元素');
|
||||
const scrollWidth = _[type];
|
||||
let transformDistance = res
|
||||
.slice(0, currentIndex)
|
||||
.reduce((prev, curr) => prev + curr[type], 0);
|
||||
transformDistance += (currentRect[type] - scrollWidth) / 2;
|
||||
|
||||
if (type === 'width') {
|
||||
this.setData({
|
||||
transformX: transformDistance,
|
||||
transformY: 0
|
||||
});
|
||||
} else {
|
||||
this.setData({
|
||||
transformX: 0,
|
||||
transformY: transformDistance
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
},
|
||||
queryMultipleNodes() {
|
||||
const {
|
||||
placement,
|
||||
currentIndex
|
||||
} = this.data;
|
||||
this.getRect('.l-tabs-item', true)
|
||||
.then((res) => {
|
||||
if (['top', 'bottom'].indexOf(placement) !== -1) {
|
||||
this.queryScrollNode(res, currentIndex);
|
||||
} else {
|
||||
this.queryScrollNode(res, currentIndex, 'height');
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
const isObj = (x) => {
|
||||
const type = typeof x;
|
||||
return x !== null && (type === 'object' || type === 'function');
|
||||
};
|
||||
const getClassNames = (name) => ({
|
||||
enter: `l-${name}-enter l-${name}-enter-active l-enter-class l-enter-active-class`,
|
||||
'enter-to': `l-${name}-enter-to l-${name}-enter-active l-enter-to-class l-enter-active-class`,
|
||||
leave: `l-${name}-leave l-${name}-leave-active l-leave-class l-leave-active-class`,
|
||||
'leave-to': `l-${name}-leave-to l-${name}-leave-active l-leave-to-class l-leave-active-class`
|
||||
});
|
||||
const nextTick = () => new Promise(resolve => setTimeout(resolve, 1000 / 30));
|
||||
export default (showDefaultValue) => {
|
||||
// eslint-disable-next-line no-undef
|
||||
return Behavior({
|
||||
properties: {
|
||||
customStyle: String,
|
||||
show: {
|
||||
type: Boolean,
|
||||
value: showDefaultValue,
|
||||
observer: 'observeShow'
|
||||
},
|
||||
duration: {
|
||||
type: null,
|
||||
value: 300,
|
||||
observer: 'observeDuration'
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
value: 'fade'
|
||||
}
|
||||
},
|
||||
data: {
|
||||
type: '',
|
||||
inited: false,
|
||||
display: false
|
||||
},
|
||||
attached() {
|
||||
if (this.data.show) {
|
||||
this.enter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
observeShow(value) {
|
||||
value ? this.enter() : this.leave();
|
||||
},
|
||||
enter() {
|
||||
const { duration, name } = this.data;
|
||||
const classNames = getClassNames(name);
|
||||
const currentDuration = isObj(duration) ? duration.enter : duration;
|
||||
this.status = 'enter';
|
||||
this.triggerEvent('linbeforeenter');
|
||||
Promise.resolve()
|
||||
.then(nextTick)
|
||||
.then(() => {
|
||||
this.checkStatus('enter');
|
||||
this.triggerEvent('linenter');
|
||||
this.setData({
|
||||
inited: true,
|
||||
display: true,
|
||||
classes: classNames.enter,
|
||||
currentDuration
|
||||
});
|
||||
})
|
||||
.then(nextTick)
|
||||
.then(() => {
|
||||
this.checkStatus('enter');
|
||||
this.transitionEnded = false;
|
||||
this.setData({
|
||||
classes: classNames['enter-to']
|
||||
});
|
||||
})
|
||||
.catch(() => { });
|
||||
},
|
||||
leave() {
|
||||
if (!this.data.display) {
|
||||
return;
|
||||
}
|
||||
const { duration, name } = this.data;
|
||||
const classNames = getClassNames(name);
|
||||
const currentDuration = isObj(duration) ? duration.leave : duration;
|
||||
this.status = 'leave';
|
||||
this.triggerEvent('linbeforeleave');
|
||||
Promise.resolve()
|
||||
.then(nextTick)
|
||||
.then(() => {
|
||||
this.checkStatus('leave');
|
||||
this.triggerEvent('linleave');
|
||||
this.setData({
|
||||
classes: classNames.leave,
|
||||
currentDuration
|
||||
});
|
||||
})
|
||||
.then(nextTick)
|
||||
.then(() => {
|
||||
this.checkStatus('leave');
|
||||
this.transitionEnded = false;
|
||||
setTimeout(() => this.onTransitionEnd(), currentDuration);
|
||||
this.setData({
|
||||
classes: classNames['leave-to']
|
||||
});
|
||||
})
|
||||
.catch(() => { });
|
||||
},
|
||||
checkStatus(status) {
|
||||
if (status !== this.status) {
|
||||
throw new Error(`incongruent status: ${status}`);
|
||||
}
|
||||
},
|
||||
onTransitionEnd() {
|
||||
if (this.transitionEnded) {
|
||||
return;
|
||||
}
|
||||
this.transitionEnded = true;
|
||||
this.triggerEvent(`linafter${this.status}`);
|
||||
const { show, display } = this.data;
|
||||
if (!show && display) {
|
||||
this.setData({ display: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// eslint-disable-next-line no-undef
|
||||
export default Behavior({
|
||||
definitionFilter(defFields) {
|
||||
const {
|
||||
properties
|
||||
} = defFields;
|
||||
const propsKey = Object.keys(properties);
|
||||
propsKey.forEach(name => {
|
||||
const {
|
||||
options
|
||||
} = properties[name];
|
||||
if (options) {
|
||||
properties[name].observer = function (newValue) {
|
||||
if (!options.includes(newValue) && newValue) {
|
||||
console.error(`${name}: ${newValue} must be in the [${options}]`);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// eslint-disable-next-line no-undef
|
||||
export default Behavior({
|
||||
observers: {
|
||||
'show': function (show) {
|
||||
show && this.changeStatus();
|
||||
if (!show) this.setData({
|
||||
status: show
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeStatus() {
|
||||
this.setData({
|
||||
status: true
|
||||
});
|
||||
if (this.data.timer) clearTimeout(this.data.timer);
|
||||
this.data.timer = setTimeout(() => {
|
||||
this.setData({
|
||||
status: false
|
||||
});
|
||||
if (this.data.success) this.data.success();
|
||||
this.data.timer = null;
|
||||
}, this.properties.duration);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// eslint-disable-next-line no-undef
|
||||
export default Behavior({
|
||||
behaviors: [],
|
||||
properties: {
|
||||
zIndex:{
|
||||
type: Number,
|
||||
value: 777
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
import validator from '../behaviors/validator';
|
||||
|
||||
Component({
|
||||
externalClasses: [
|
||||
'l-class',
|
||||
'l-label-class',
|
||||
'l-hover-class',
|
||||
'l-img-class',
|
||||
'l-icon-class'
|
||||
],
|
||||
behaviors: [validator],
|
||||
properties: {
|
||||
// button组建标识
|
||||
name: {
|
||||
type: String,
|
||||
value: 'lin'
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
value: 'default',
|
||||
options: ['warning', 'success', 'error', 'default']
|
||||
},
|
||||
plain: Boolean,
|
||||
size: {
|
||||
type: String,
|
||||
value: 'medium',
|
||||
options: ['medium', 'large', 'mini', 'long']
|
||||
},
|
||||
shape: {
|
||||
type: String,
|
||||
value: 'circle',
|
||||
options: ['square', 'circle', 'semicircle']
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
special: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
// 微信原生接口
|
||||
width: Number,
|
||||
height: Number,
|
||||
icon: String,
|
||||
image: String,
|
||||
bgColor: String,
|
||||
iconColor: String,
|
||||
iconSize: String,
|
||||
openType: String,
|
||||
appParameter: String,
|
||||
lang: String,
|
||||
hoverStopPropagation: Boolean,
|
||||
hoverStartTime: {
|
||||
type: Number,
|
||||
value: 20
|
||||
},
|
||||
hoverStayTime: {
|
||||
type: Number,
|
||||
value: 70
|
||||
},
|
||||
sessionFrom: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
sendMessageTitle: String,
|
||||
sendMessagePath: String,
|
||||
sendMessageImg: String,
|
||||
showMessageCard: Boolean,
|
||||
formType: String,
|
||||
disabledHover: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// button点击事件
|
||||
handleTap() {
|
||||
if (this.data.disabled || this.data.loading) return false;
|
||||
this.triggerEvent('lintap', {}, {
|
||||
bubbles: true,
|
||||
composed: true
|
||||
});
|
||||
},
|
||||
// 开放能力事件回调
|
||||
openTypeEvent(data) {
|
||||
this.triggerEvent(data.type, data.detail, {});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"l-icon":"../icon/index"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
@import "../../config/styles/_base.less";
|
||||
@import "../../config/styles/_mixins.less";
|
||||
|
||||
.btn-hover::before {
|
||||
.active();
|
||||
}
|
||||
|
||||
.special-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.l-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
white-space: nowrap;
|
||||
font-size: 28rpx;
|
||||
position: relative;
|
||||
color: #fff;
|
||||
padding: 0 12rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
// img-size
|
||||
&-img {
|
||||
&-mini {
|
||||
width: @btn-height-mini/2;
|
||||
height: @btn-height-mini/2;
|
||||
}
|
||||
|
||||
&-medium {
|
||||
width: @btn-height/2;
|
||||
height: @btn-height/2;
|
||||
}
|
||||
|
||||
&-large {
|
||||
height: @btn-height-large/2;
|
||||
width: @btn-height-large/2;
|
||||
}
|
||||
}
|
||||
|
||||
// Size
|
||||
&-long {
|
||||
border-radius: 0;
|
||||
height: 88rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-medium {
|
||||
height: @btn-height;
|
||||
min-width: @btn-width;
|
||||
}
|
||||
|
||||
&-large {
|
||||
height: @btn-height-large;
|
||||
min-width: @btn-width-large;
|
||||
}
|
||||
|
||||
&-mini {
|
||||
height: @btn-height-mini;
|
||||
min-width: @btn-width-mini;
|
||||
font-size: @size-font-mini;
|
||||
}
|
||||
|
||||
//Type
|
||||
&-default {
|
||||
background-color: @default-color;
|
||||
}
|
||||
|
||||
&-success {
|
||||
background-color: @success-color;
|
||||
}
|
||||
|
||||
&-warning {
|
||||
background-color: @warning-color;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
&-error {
|
||||
background-color: @error-color;
|
||||
}
|
||||
|
||||
// Shape
|
||||
&-square {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
&-semicircle {
|
||||
border-radius: @btn-circle-size;
|
||||
}
|
||||
|
||||
&-large&-semicircle {
|
||||
border-radius: @btn-circle-size-large;
|
||||
}
|
||||
|
||||
&-mini&-semicircle {
|
||||
border-radius: @btn-circle-size-mini;
|
||||
}
|
||||
|
||||
&-circle {
|
||||
border-radius: @btn-circle-size/5;
|
||||
}
|
||||
|
||||
&-large&-circle {
|
||||
border-radius: @btn-circle-size-large/5;
|
||||
}
|
||||
|
||||
&-mini&-circle {
|
||||
border-radius: @btn-circle-size-mini/5;
|
||||
}
|
||||
|
||||
// Plain
|
||||
&-plain {
|
||||
background-color: #fff;
|
||||
color: @default-color;
|
||||
border: 2rpx solid @default-color;
|
||||
}
|
||||
|
||||
&-success&-plain {
|
||||
background-color: #fff;
|
||||
color: @success-color;
|
||||
border: 2rpx solid @success-color;
|
||||
}
|
||||
|
||||
&-error&-plain {
|
||||
background-color: #fff;
|
||||
color: @error-color;
|
||||
border: 2rpx solid @error-color;
|
||||
}
|
||||
|
||||
&-warning&-plain {
|
||||
background-color: #fff;
|
||||
color: @warning-color;
|
||||
border: 2rpx solid @warning-color;
|
||||
}
|
||||
|
||||
// Loading
|
||||
&-loading {
|
||||
opacity: 0.6;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
background: transparent;
|
||||
border-radius: 50%;
|
||||
border: 4rpx solid @btn-loading-color;
|
||||
border-color: @btn-loading-color @btn-loading-color @btn-loading-color transparent;
|
||||
animation: btn-spin 0.6s linear;
|
||||
animation-iteration-count: infinite;
|
||||
|
||||
&-default {
|
||||
border: 4rpx solid @default-color;
|
||||
border-color: @default-color @default-color @default-color transparent;
|
||||
}
|
||||
|
||||
&-success {
|
||||
border: 4rpx solid @success-color;
|
||||
border-color: @success-color @success-color @success-color transparent;
|
||||
}
|
||||
|
||||
&-error {
|
||||
border: 4rpx solid @error-color;
|
||||
border-color: @error-color @error-color @error-color transparent;
|
||||
}
|
||||
|
||||
&-warning {
|
||||
border: 4rpx solid @warning-color;
|
||||
border-color: @warning-color @warning-color @warning-color transparent;
|
||||
}
|
||||
}
|
||||
|
||||
// Disabled
|
||||
&-disabled {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.margin {
|
||||
&-mini {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
&-medium {
|
||||
margin-right: 18rpx;
|
||||
}
|
||||
|
||||
&-large {
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
&-long {
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes btn-spin {
|
||||
0% {
|
||||
transform: rotate(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<label for="{{name}}" mut-bind:tap="handleTap" class="l-label-class">
|
||||
<block wx:if="{{special}}">
|
||||
<view class="special-container l-class">
|
||||
<slot/>
|
||||
</view>
|
||||
</block>
|
||||
<block wx:else>
|
||||
<view class="l-btn {{ 'l-btn-' + size }} {{ 'l-btn-' + type }} {{ 'l-btn-' + shape }} {{plain?'l-btn-plain':''}} {{ disabled ? 'l-btn-disabled' : ''}} l-class "
|
||||
hover-class="{{disabled?'':disabledHover?'':'btn-hover l-hover-class'}}"
|
||||
hover-stop-propagation="{{hoverStopPropagation}}"
|
||||
hover-start-time="{{hoverStartTime}}"
|
||||
hover-stay-time="{{hoverStayTime}}"
|
||||
style="{{width?'min-width:'+width+'rpx;':''}} {{height?'height:'+height+'rpx;'+'line-height:'+height+'rpx;':''}} {{size=='long'?'border-radius:0;':''}} {{'background-color:'+bgColor}}">
|
||||
<view wx:if="{{loading}}" class="l-btn-loading {{'margin-' + size}} {{ plain ?'l-btn-loading-' + type : '' }}"></view>
|
||||
<l-icon l-class="l-icon-class" class="{{'margin-' + size}}" wx:if="{{icon}}" name="{{icon}}" color="{{iconColor}}" size="{{iconSize}}" />
|
||||
<slot/>
|
||||
</view>
|
||||
</block>
|
||||
</label>
|
||||
<button style="position: absolute;top: -999px;left: -999px;"
|
||||
wx:if="{{openType}}"
|
||||
id="{{name}}"
|
||||
lang="{{lang}}"
|
||||
form-type="{{formType}}"
|
||||
open-type="{{openType}}"
|
||||
app-parameter="{{ appParameter }}"
|
||||
hover-stop-propagation="{{ hoverStopPropagation }}"
|
||||
hover-start-time="{{ hoverStartTime }}"
|
||||
hover-stay-time="{{ hoverStayTime }}"
|
||||
session-from="{{ sessionFrom }}"
|
||||
send-message-title="{{ sendMessageTitle }}"
|
||||
send-message-path="{{ sendMessagePath }}"
|
||||
send-message-img="{{ sendMessageImg }}"
|
||||
show-message-card="{{ showMessageCard }}"
|
||||
bindcontact="openTypeEvent"
|
||||
bindgetuserinfo="openTypeEvent"
|
||||
bindgetphonenumber="openTypeEvent"
|
||||
bindopensetting="openTypeEvent">
|
||||
</button>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<wxs src="./index.wxs" module="computed"></wxs>
|
||||
|
||||
<template name="calendar">
|
||||
<view class="calendar-container">
|
||||
<header showTitle="{{ showTitle }}" showSubtitle="{{ showSubtitle }}" title="{{ title }}" subTitle="{{ subTitle }}"></header>
|
||||
<scroll-view class="calendar-body-wrap" scroll-y scroll-into-view="{{ scrollIntoViewIndex }}">
|
||||
<mounth
|
||||
wx:for="{{ computed.getMonths(minDate, maxDate) }}"
|
||||
wx:key="index"
|
||||
id="month{{ index }}"
|
||||
class="month"
|
||||
data-date="{{ item }}"
|
||||
date="{{ item }}"
|
||||
minDate="{{ minDate }}"
|
||||
maxDate="{{ maxDate }}"
|
||||
currentDate="{{ currentDate }}"
|
||||
type="{{ type }}"
|
||||
bind:clickDay="clickDay"
|
||||
showMonthTitle="{{ index !== 0 }}"
|
||||
formatter="{{ formatter }}"
|
||||
color="{{ color }}"
|
||||
>
|
||||
</mounth>
|
||||
</scroll-view>
|
||||
<view>
|
||||
<l-button type="default" l-class="bottom-button" size="long" bind:lintap="onClickConfirm" bg-color="{{ color }}">{{confirmText}}</l-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
Component({
|
||||
data: {},
|
||||
properties: {
|
||||
text: null,
|
||||
topInfo: null,
|
||||
bottomInfo: null,
|
||||
type: null,
|
||||
color: {
|
||||
type: String,
|
||||
value: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"component": true
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
@import "../../../../config/styles/_base.less";
|
||||
|
||||
.calendar-day-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
color: #666;
|
||||
|
||||
&.selected {
|
||||
background-color: @default-color;
|
||||
color: #fff;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
&.start {
|
||||
background-color: @default-color;
|
||||
color: #fff;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
&.end {
|
||||
background-color: @default-color;
|
||||
color: #fff;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
color: #c8c9cc;
|
||||
cursor: default;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
&.middle {
|
||||
color: @default-color;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: currentcolor;
|
||||
opacity: 0.1;
|
||||
content: "";
|
||||
}
|
||||
}
|
||||
// background-color: red;
|
||||
// color: #eff9f0;
|
||||
.top {
|
||||
height: 24rpx;
|
||||
line-height: 24rpx;
|
||||
font-size: 16rpx;
|
||||
}
|
||||
|
||||
.text {
|
||||
flex: 1;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
height: 24rpx;
|
||||
line-height: 24rpx;
|
||||
font-size: 16rpx;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<wxs src="./index.wxs" module="computed"></wxs>
|
||||
<view class="calendar-day-container {{ type }}"
|
||||
style="{{ computed.getDayStyle(type, color) }}"
|
||||
>
|
||||
<view class="top">{{ topInfo }}</view>
|
||||
<view class="text">{{ text }}</view>
|
||||
<view class="bottom">{{ bottomInfo }}</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
function getDayStyle(type, color) {
|
||||
var style = [];
|
||||
|
||||
if (color) {
|
||||
if (
|
||||
type === 'start' ||
|
||||
type === 'end' ||
|
||||
type === 'selected'
|
||||
) {
|
||||
style.push(['background', color]);
|
||||
} else if (type === 'middle') {
|
||||
style.push(['color', color]);
|
||||
// style.push(['background', color]);
|
||||
// style.push(['opacity', '0.1']);
|
||||
}
|
||||
}
|
||||
|
||||
return style
|
||||
.map(function(item) {
|
||||
return item.join(':');
|
||||
})
|
||||
.join(';');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getDayStyle: getDayStyle,
|
||||
};
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
Component({
|
||||
data: {
|
||||
weekdays: ['日', '一', '二', '三', '四', '五', '六']
|
||||
},
|
||||
properties: {
|
||||
title: {
|
||||
type: String,
|
||||
value: '日期选择'
|
||||
},
|
||||
subTitle: String,
|
||||
showTitle: Boolean,
|
||||
showSubtitle: Boolean
|
||||
},
|
||||
methods: {}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"component": true
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
.calendar-header-container {
|
||||
.calendar-header-container_title {
|
||||
text-align: center;
|
||||
padding: 20rpx 0;
|
||||
font-size: 36rpx;
|
||||
line-height: 44rpx;
|
||||
}
|
||||
|
||||
.calendar-header-container_subtitle {
|
||||
text-align: center;
|
||||
font-size: 36rpx;
|
||||
line-height: 44rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.calendar-header__weekdays {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 0 20rpx;
|
||||
padding: 10rpx 0;
|
||||
border-bottom: 1rpx solid #f3f3f3;
|
||||
|
||||
.calendar-header__weekday {
|
||||
width: 14.285%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<view class="calendar-header-container">
|
||||
<block wx:if="{{ showTitle }}">
|
||||
<!-- <view class="calendar-header-container_title">
|
||||
<slot name="title"></slot>
|
||||
</view> -->
|
||||
<view class="calendar-header-container_title">
|
||||
{{ title }}
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<view wx:if="{{ showSubtitle }}" class="calendar-header-container_subtitle">
|
||||
{{ subTitle }}
|
||||
</view>
|
||||
|
||||
<view class="calendar-header__weekdays">
|
||||
<view wx:for="{{ weekdays }}" wx:key="index" class="calendar-header__weekday">
|
||||
{{ item }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
import { compareDay, getMonthEndDay } from '../../util';
|
||||
import * as config from '../../config';
|
||||
Component({
|
||||
data: {
|
||||
days: []
|
||||
},
|
||||
properties: {
|
||||
minDate: {
|
||||
type: Object,
|
||||
optionalTypes: [String, null],
|
||||
},
|
||||
maxDate: {
|
||||
type: Object,
|
||||
optionalTypes: [String, null],
|
||||
},
|
||||
formatter: {
|
||||
type: null,
|
||||
observer: 'setDays'
|
||||
},
|
||||
date: {
|
||||
type: null,
|
||||
observer: 'setDays'
|
||||
},
|
||||
currentDate: {
|
||||
type: Array,
|
||||
optionalTypes: [null],
|
||||
observer() {
|
||||
this.setDays();
|
||||
}
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
observer: 'setDays'
|
||||
},
|
||||
showMonthTitle: Boolean,
|
||||
color: {
|
||||
type: String,
|
||||
value: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
/**
|
||||
* 某一天的点击事件
|
||||
*/
|
||||
onClick(event) {
|
||||
const { item } = event.currentTarget.dataset;
|
||||
if (item.type !== 'disabled' && item.type !== 'empty') {
|
||||
this.triggerEvent('clickDay', item);
|
||||
}
|
||||
},
|
||||
debounce(fn) {
|
||||
let timer;
|
||||
return () => {
|
||||
let that = this;
|
||||
let args = arguments;
|
||||
if(timer) clearTimeout(timer);
|
||||
timer = setTimeout(function() {
|
||||
fn.apply(that, args);
|
||||
}, 300);
|
||||
};
|
||||
},
|
||||
setDays() {
|
||||
this.debounce(this.setDay)();
|
||||
},
|
||||
/**
|
||||
* 设置某月分的天数
|
||||
*/
|
||||
setDay() {
|
||||
let days = [];
|
||||
const startDate = new Date(this.data.date);
|
||||
const year = startDate.getFullYear();
|
||||
const month = startDate.getMonth();
|
||||
const day = new Date(year, month, 1).getDay();
|
||||
|
||||
const totalDay = getMonthEndDay(year, month + 1);
|
||||
|
||||
for (let day = 1; day <= totalDay; day++) {
|
||||
const date = new Date(year, month, day).getTime();
|
||||
const type = this.getDayType(date);
|
||||
|
||||
|
||||
let config = {
|
||||
date,
|
||||
type,
|
||||
text: day,
|
||||
bottomInfo: this.getBottomInfo(type),
|
||||
topInfo: ''
|
||||
};
|
||||
|
||||
if (this.data.formatter) {
|
||||
config = this.data.formatter(config);
|
||||
}
|
||||
days.push(config);
|
||||
}
|
||||
|
||||
for(let i = 0; i < day; i++) {
|
||||
days.unshift({
|
||||
type: 'empty'
|
||||
});
|
||||
}
|
||||
|
||||
this.setData({
|
||||
days
|
||||
});
|
||||
},
|
||||
|
||||
isDateInCurrent(date) {
|
||||
const { currentDate } = this.data;
|
||||
return currentDate.some(item => {
|
||||
return compareDay(item, date) === 0;
|
||||
});
|
||||
},
|
||||
|
||||
getMultipleDayType(date) {
|
||||
const { currentDate } = this.data;
|
||||
|
||||
if (!Array.isArray(currentDate)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (this.isDateInCurrent(date)) {
|
||||
return 'selected';
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
|
||||
getRangeDayType(day) {
|
||||
const { currentDate } = this.data;
|
||||
if (!Array.isArray(currentDate)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [startDay, endDay] = currentDate;
|
||||
|
||||
if (!startDay) {
|
||||
return;
|
||||
}
|
||||
const compareToStart = compareDay(day, startDay);
|
||||
|
||||
if (!endDay) {
|
||||
return compareToStart === 0 ? 'start' : '';
|
||||
}
|
||||
|
||||
const compareToEnd = compareDay(day, endDay);
|
||||
|
||||
if (compareToStart === 0) {
|
||||
return 'start';
|
||||
}
|
||||
|
||||
if (compareToEnd === 0) {
|
||||
return 'end';
|
||||
}
|
||||
|
||||
if (compareToStart > 0 && compareToEnd < 0) {
|
||||
return 'middle';
|
||||
}
|
||||
},
|
||||
|
||||
// date 循环的某一天
|
||||
getDayType(date) {
|
||||
const { type, minDate, maxDate, currentDate } = this.data;
|
||||
|
||||
if (compareDay(date, minDate) < 0 || compareDay(date, maxDate) > 0) {
|
||||
return 'disabled';
|
||||
}
|
||||
|
||||
if (type === config.TYPE_SINGLE) {
|
||||
return compareDay(date, currentDate) === 0 ? 'selected' : '';
|
||||
}
|
||||
|
||||
if (type === config.TYPE_MULTIPLE) {
|
||||
return this.getMultipleDayType(date);
|
||||
}
|
||||
|
||||
if (type === config.TYPE_RANGE) {
|
||||
return this.getRangeDayType(date);
|
||||
}
|
||||
},
|
||||
|
||||
getBottomInfo(type) {
|
||||
if (this.data.type === config.TYPE_RANGE) {
|
||||
if (type === 'start') {
|
||||
return '开始';
|
||||
}
|
||||
if (type === 'end') {
|
||||
return '结束';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"day":"../day/index"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
.calendar-mounth-container {
|
||||
color: #333;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.calendar-day-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.calendar-day-wrap {
|
||||
width: 14.285%;
|
||||
// height: 110rpx;
|
||||
// padding: 10rpx 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<wxs src="./index.wxs" module="computed"></wxs>
|
||||
|
||||
<view class="calendar-mounth-container">
|
||||
<view class="title" wx:if="{{ showMonthTitle }}">
|
||||
{{ computed.formatMonthTitle(date) }}
|
||||
</view>
|
||||
<view class="calendar-day-container">
|
||||
<view class="calendar-day-wrap"
|
||||
wx:for="{{ days }}"
|
||||
wx:key="index"
|
||||
>
|
||||
<day
|
||||
bind:tap="onClick"
|
||||
data-item="{{ item }}"
|
||||
wx:if="{{ item.type !== 'empty' }}"
|
||||
text="{{ item.text }}"
|
||||
topInfo="{{ item.topInfo }}"
|
||||
bottomInfo="{{ item.bottomInfo }}"
|
||||
type="{{ item.type }}"
|
||||
color=" {{ color }}"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
/* eslint-disable */
|
||||
|
||||
function formatMonthTitle(date) {
|
||||
date = getDate(date);
|
||||
return date.getFullYear() + '年' + (date.getMonth() + 1) + '月';
|
||||
}
|
||||
|
||||
|
||||
|
||||
module.exports = {
|
||||
formatMonthTitle: formatMonthTitle
|
||||
};
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
export const TYPE_SINGLE = 'single';
|
||||
export const TYPE_RANGE = 'range';
|
||||
export const TYPE_MULTIPLE = 'multiple';
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
const formatFlags = {
|
||||
format: function (format, date) {
|
||||
date = new Date(date);
|
||||
let ret;
|
||||
const opt = {
|
||||
'y+': date.getFullYear().toString(), // 年
|
||||
'M+': (date.getMonth() + 1).toString(), // 月
|
||||
'd+': date.getDate().toString(), // 日
|
||||
};
|
||||
for (let k in opt) {
|
||||
ret = new RegExp('(' + k + ')').exec(format);
|
||||
if (ret) {
|
||||
format = format.replace(ret[1], (ret[1].length === 1) ? opt[k] : this.padZero(opt[k], ret[1].length));
|
||||
}
|
||||
}
|
||||
return format;
|
||||
},
|
||||
|
||||
/**
|
||||
* PC 端微信不支持 padStart,这里写一个补 0 函数
|
||||
* 如果测试已兼容,则可使用原生 padStart
|
||||
* issue #1277
|
||||
*/
|
||||
padZero(str, length) {
|
||||
let res = str;
|
||||
for (let i = 0; i < length - str; i++) {
|
||||
res = '0' + res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
formatFlags.format('yyyy/MM/dd', new Date());
|
||||
formatFlags.format('yyyy-MM-dd', new Date());
|
||||
formatFlags.format('yyyy-M-dd', new Date());
|
||||
formatFlags.format('yyyy-M-d', new Date());
|
||||
// formatFlags.format('yy-M-dd', new Date())
|
||||
formatFlags.format('M-dd', new Date());
|
||||
formatFlags.format('MM-dd', new Date());
|
||||
|
||||
|
||||
export default formatFlags;
|
||||
|
|
@ -0,0 +1,346 @@
|
|||
import eventBus from '../core/utils/event-bus.js';
|
||||
import validator from '../behaviors/validator';
|
||||
import rules from '../behaviors/rules';
|
||||
import * as config from './config';
|
||||
import formatFlags from './dete';
|
||||
|
||||
import { getDayByOffset, getDate, compareDay, calcDateNum, copyDates, getTime, formatMonthTitle, compareMonth, getMonths } from './util';
|
||||
|
||||
Component({
|
||||
externalClasses: [
|
||||
'l-class'
|
||||
],
|
||||
behaviors: ['wx://form-field', validator, rules],
|
||||
properties: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
observer(val) {
|
||||
if (val) {
|
||||
this.initRect();
|
||||
this.scrollIntoView();
|
||||
}
|
||||
}
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
value: config.TYPE_SINGLE,
|
||||
options: [config.TYPE_SINGLE, config.TYPE_MULTIPLE, config.TYPE_RANGE],
|
||||
observer() {
|
||||
this.setData({ currentDate: this.initCurrentDate() });
|
||||
}
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
defaultDate: {
|
||||
optionalTypes: [String, Number, Date, Array],
|
||||
value: '',
|
||||
observer() {
|
||||
this.setData({ currentDate: this.initCurrentDate() });
|
||||
}
|
||||
},
|
||||
format: {
|
||||
type: String,
|
||||
value: 'timestamp'
|
||||
},
|
||||
formatter: {
|
||||
type: Function,
|
||||
optionalTypes: [null],
|
||||
value: null
|
||||
},
|
||||
minDate: {
|
||||
type: String,
|
||||
optionalTypes: [null, Number],
|
||||
value: Date.now()
|
||||
},
|
||||
maxDate: {
|
||||
type: String,
|
||||
optionalTypes: [null, Number],
|
||||
value: new Date(
|
||||
new Date().getFullYear(),
|
||||
new Date().getMonth() + 6,
|
||||
new Date().getDate()
|
||||
).getTime()
|
||||
},
|
||||
minSelect: {
|
||||
type: Number,
|
||||
optionalTypes: [null],
|
||||
value: null
|
||||
},
|
||||
maxSelect: {
|
||||
type: Number,
|
||||
optionalTypes: [null],
|
||||
value: null
|
||||
},
|
||||
allowSameDay: Boolean,
|
||||
showConfirm: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
confirmText: {
|
||||
type: String,
|
||||
value: '确认'
|
||||
},
|
||||
maxLimitMessage: {
|
||||
type: String
|
||||
},
|
||||
minLimitMessage: {
|
||||
type: String
|
||||
},
|
||||
showTitle: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
showSubtitle: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
value: '日期选择'
|
||||
}
|
||||
|
||||
},
|
||||
lifetimes: {
|
||||
attached() {
|
||||
this.setData({
|
||||
currentDate: this.initCurrentDate()
|
||||
});
|
||||
},
|
||||
ready() {
|
||||
if (this.data.show) {
|
||||
this.initRect();
|
||||
this.scrollIntoView();
|
||||
}
|
||||
}
|
||||
},
|
||||
data: {
|
||||
currentDate: null,
|
||||
types: config,
|
||||
subTitle: '',
|
||||
scrollIntoViewIndex: ''
|
||||
},
|
||||
methods: {
|
||||
clickDay(event) {
|
||||
const { type, currentDate, maxLimitMessage, maxSelect, allowSameDay } = this.data;
|
||||
const { date } = event.detail;
|
||||
if(type === config.TYPE_SINGLE) {
|
||||
this.setData({
|
||||
currentDate: getTime(date)
|
||||
});
|
||||
this.triggerEvent('linclickday', copyDates(date));
|
||||
this.triggerEvent('linselect', copyDates(date));
|
||||
}
|
||||
if(type === config.TYPE_MULTIPLE) {
|
||||
let _index = null;
|
||||
const isSelected = currentDate.some((item, index) => {
|
||||
const res = compareDay(item, date) === 0;
|
||||
if(res) {
|
||||
_index = index;
|
||||
}
|
||||
return res;
|
||||
});
|
||||
|
||||
if(isSelected) {
|
||||
// 取消选择
|
||||
currentDate.splice(_index, 1);
|
||||
this.setData({
|
||||
currentDate: getTime(currentDate)
|
||||
});
|
||||
this.triggerEvent('linunselect', copyDates(currentDate));
|
||||
} else {
|
||||
if(maxSelect && currentDate.length >= maxSelect) {
|
||||
wx.lin.showToast({
|
||||
title: maxLimitMessage || `选择天数不能超过 ${ maxSelect } 天`
|
||||
});
|
||||
this.triggerEvent('linclickday', copyDates(date));
|
||||
return;
|
||||
}
|
||||
// 添加
|
||||
this.setData({
|
||||
currentDate: getTime([...currentDate, date])
|
||||
});
|
||||
this.triggerEvent('linselect', copyDates([...currentDate, date]));
|
||||
}
|
||||
this.triggerEvent('linclickday', copyDates(date));
|
||||
}
|
||||
|
||||
if(type === config.TYPE_RANGE) {
|
||||
const [startDay, endDay] = currentDate;
|
||||
if (startDay && !endDay) {
|
||||
const compareToStart = compareDay(date, startDay);
|
||||
if (compareToStart === 1) {
|
||||
if(this.checkSelectRange([startDay, date])) {
|
||||
this.setData({
|
||||
currentDate: getTime([startDay, date])
|
||||
});
|
||||
this.triggerEvent('linselect', copyDates([startDay, date]));
|
||||
}
|
||||
}
|
||||
else if (compareToStart === -1) {
|
||||
// 选择结束日期在开始日期之前 重新开始选择
|
||||
this.setData({
|
||||
currentDate: getTime([date, null])
|
||||
});
|
||||
} else if(allowSameDay){
|
||||
this.setData({
|
||||
currentDate: getTime([date, date])
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 重新开始选择
|
||||
this.setData({
|
||||
currentDate: getTime([date, null])
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
checkSelectRange(date) {
|
||||
const { maxSelect, maxLimitMessage, minSelect, minLimitMessage } = this.data;
|
||||
if (maxSelect && calcDateNum(date) > maxSelect) {
|
||||
|
||||
wx.lin.showToast({
|
||||
title: maxLimitMessage || `选择天数不能超过 ${ maxSelect } 天`
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (minSelect && calcDateNum(date) < minSelect) {
|
||||
wx.lin.showToast({
|
||||
title: minLimitMessage || `选择天数不能少于 ${ minSelect } 天`
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
initCurrentDate() {
|
||||
|
||||
const { type, minDate, defaultDate, maxDate } = this.data;
|
||||
const defaultDateIsArr = Array.isArray(defaultDate);
|
||||
|
||||
if (type === config.TYPE_SINGLE) {
|
||||
if(!defaultDate) {
|
||||
return minDate;
|
||||
}
|
||||
if(defaultDateIsArr) {
|
||||
return minDate;
|
||||
}
|
||||
if(compareDay(defaultDate, minDate) === -1) {
|
||||
return minDate;
|
||||
}
|
||||
if(compareDay(defaultDate, maxDate) === 1) {
|
||||
return maxDate;
|
||||
}
|
||||
return defaultDate;
|
||||
}
|
||||
|
||||
if(type === config.TYPE_MULTIPLE) {
|
||||
if(!defaultDate) {
|
||||
return [];
|
||||
}
|
||||
if(defaultDateIsArr) {
|
||||
return defaultDate.filter(item => {
|
||||
return compareDay(item, minDate) === 1 && compareDay(item, maxDate) === -1;
|
||||
});
|
||||
}
|
||||
if(compareDay(defaultDate, minDate) === -1) {
|
||||
return [minDate];
|
||||
}
|
||||
if(compareDay(defaultDate, maxDate) === 1) {
|
||||
return [maxDate];
|
||||
}
|
||||
return [minDate];
|
||||
}
|
||||
|
||||
if(type === config.TYPE_RANGE) {
|
||||
if(defaultDateIsArr) {
|
||||
let [startDay = minDate, endDay] = defaultDate;
|
||||
if(compareDay(startDay, minDate) === -1 || compareDay(startDay, maxDate) !== -1) {
|
||||
startDay = minDate;
|
||||
}
|
||||
if(!endDay) {
|
||||
endDay = getDayByOffset(getDate(startDay), 1).getTime();
|
||||
}
|
||||
if(compareDay(endDay, maxDate) === 1 || compareDay(endDay, minDate) === -1) {
|
||||
endDay = getDayByOffset(getDate(startDay), 1).getTime();
|
||||
}
|
||||
return [
|
||||
startDay,
|
||||
endDay
|
||||
];
|
||||
}
|
||||
return [
|
||||
minDate,
|
||||
getDayByOffset(getDate(minDate), 1).getTime()
|
||||
];
|
||||
}
|
||||
},
|
||||
initRect() {
|
||||
if (!this.contentObserver !== null && this.contentObserver !== undefined) {
|
||||
this.contentObserver.disconnect();
|
||||
}
|
||||
const contentObserver = this.createIntersectionObserver({
|
||||
thresholds: [0, 0.1, 0.9, 1],
|
||||
observeAll: true
|
||||
});
|
||||
this.contentObserver = contentObserver;
|
||||
contentObserver.relativeTo('.calendar-body-wrap');
|
||||
contentObserver.observe('.month', res => {
|
||||
if (res.boundingClientRect.top <= res.relativeRect.top) {
|
||||
// @ts-ignore
|
||||
this.setData({ subTitle: formatMonthTitle(res.dataset.date) });
|
||||
}
|
||||
});
|
||||
},
|
||||
scrollIntoView() {
|
||||
setTimeout(() => {
|
||||
const { currentDate, type, show, minDate, maxDate } = this.data;
|
||||
const targetDate = type === 'single' ? currentDate : currentDate[0];
|
||||
const displayed = show;
|
||||
if (!targetDate || !displayed) {
|
||||
return;
|
||||
}
|
||||
const months = getMonths(minDate, maxDate);
|
||||
months.some((month, index) => {
|
||||
if (compareMonth(month, targetDate) === 0) {
|
||||
this.setData({ scrollIntoViewIndex: `month${index}` });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}, 100);
|
||||
},
|
||||
closePicker() {
|
||||
this.setData({
|
||||
show: false
|
||||
});
|
||||
},
|
||||
onClickConfirm() {
|
||||
const {format, type, currentDate} = this.data;
|
||||
eventBus.emit(`lin-form-blur-${this.id}`, this.id);
|
||||
let value = null;
|
||||
if(type === 'single') {
|
||||
value = format !== 'timestamp' ? formatFlags.format('yyyy-MM-dd', currentDate) : currentDate;
|
||||
} else {
|
||||
value = currentDate.map(item => {
|
||||
return format !== 'timestamp' ? formatFlags.format('yyyy-MM-dd', item) : item;
|
||||
});
|
||||
}
|
||||
this.triggerEvent('linconfirm', value);
|
||||
},
|
||||
getValues() {
|
||||
return this.data.currentDate;
|
||||
},
|
||||
reset() {
|
||||
this.setData({
|
||||
currentDate: null
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"header":"./components/header/index",
|
||||
"mounth":"./components/mounth/index",
|
||||
"l-toast": "../toast/index",
|
||||
"l-popup": "../popup/index",
|
||||
"l-button": "../button/index"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
@import "../../config/styles/_theme.less";
|
||||
@import "../../config/styles/_mixins.less";
|
||||
|
||||
.calendar-container {
|
||||
display: flex;
|
||||
height: 1000rpx;
|
||||
flex-direction: column;
|
||||
background-color: #fff;
|
||||
|
||||
.calendar-body-wrap {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding: 0 20rpx;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// .bottom {
|
||||
// width: 100%;
|
||||
// background: #fff;
|
||||
// height: 300rpx;
|
||||
// overflow: hidden;
|
||||
// text-align: center;
|
||||
// line-height: 300rpx;
|
||||
// font-size: 28rpx;
|
||||
// color: #555;
|
||||
// border-top-left-radius: 12rpx;
|
||||
// border-top-right-radius: 12rpx;
|
||||
// }
|
||||
|
||||
.bottom-button {
|
||||
padding: 0 !important;
|
||||
.safe-area-inset-bottom();
|
||||
|
||||
box-sizing: content-box !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<import src="./calendar.wxml" />
|
||||
|
||||
<l-popup content-align="bottom" show="{{ show }}" bind:lintap="closePicker">
|
||||
<template
|
||||
is="calendar"
|
||||
data="{{ currentDate, type, color, defaultDate, format, formatter, minDate, maxDate, minSelect, maxSelect, showConfirm, confirmText, maxLimitMessage, minLimitMessage, showTitle, showSubtitle, title, subTitle, scrollIntoViewIndex, color }}"
|
||||
/>
|
||||
</l-popup>
|
||||
|
||||
<l-toast></l-toast>
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/* eslint-disable */
|
||||
var utils = require('./utils.wxs');
|
||||
|
||||
function getMonths(minDate, maxDate) {
|
||||
var months = [];
|
||||
var cursor = getDate(minDate);
|
||||
|
||||
cursor.setDate(1);
|
||||
|
||||
do {
|
||||
months.push(cursor.getTime());
|
||||
cursor.setMonth(cursor.getMonth() + 1);
|
||||
} while (utils.compareMonth(cursor, getDate(maxDate)) !== 1);
|
||||
|
||||
return months;
|
||||
}
|
||||
|
||||
function getButtonDisabled(type, currentDate) {
|
||||
if (currentDate == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type === 'range') {
|
||||
return !currentDate[0] || !currentDate[1];
|
||||
}
|
||||
|
||||
if (type === 'multiple') {
|
||||
return !currentDate.length;
|
||||
}
|
||||
|
||||
return !currentDate;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getMonths: getMonths,
|
||||
getButtonDisabled: getButtonDisabled
|
||||
};
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
export function getMonthEndDay(year, month) {
|
||||
return new Date(year, month, 0).getDate();
|
||||
}
|
||||
|
||||
export function isTimeTemp(data) {
|
||||
var reg = /^\d+(\.\d+)?$/;
|
||||
return reg.test(data);
|
||||
}
|
||||
|
||||
export function getDate(str) {
|
||||
if (isTimeTemp(str)) {
|
||||
str = parseInt(str);
|
||||
}
|
||||
str = new Date(str);
|
||||
return str;
|
||||
}
|
||||
|
||||
export function getDayByOffset(date, number) {
|
||||
date = new Date(date);
|
||||
date.setDate(date.getDate() + number);
|
||||
return date;
|
||||
}
|
||||
|
||||
export function compareMonth(date1, date2) {
|
||||
if (!(date1 instanceof Date)) {
|
||||
date1 = getDate(date1);
|
||||
}
|
||||
|
||||
if (!(date2 instanceof Date)) {
|
||||
date2 = getDate(date2);
|
||||
}
|
||||
|
||||
const year1 = date1.getFullYear();
|
||||
const year2 = date2.getFullYear();
|
||||
const month1 = date1.getMonth();
|
||||
const month2 = date2.getMonth();
|
||||
|
||||
if (year1 === year2) {
|
||||
return month1 === month2 ? 0 : month1 > month2 ? 1 : -1;
|
||||
}
|
||||
|
||||
return year1 > year2 ? 1 : -1;
|
||||
}
|
||||
|
||||
export function compareDay(date1, date2) {
|
||||
if (!(date1 instanceof Date)) {
|
||||
date1 = getDate(date1);
|
||||
}
|
||||
|
||||
if (!(date2 instanceof Date)) {
|
||||
date2 = getDate(date2);
|
||||
}
|
||||
|
||||
const compareMonthResult = compareMonth(date1, date2);
|
||||
|
||||
if (compareMonthResult === 0) {
|
||||
const _date1 = date1.getDate();
|
||||
const _date2 = date2.getDate();
|
||||
|
||||
return _date1 === _date2 ? 0 : _date1 > _date2 ? 1 : -1;
|
||||
}
|
||||
|
||||
return compareMonthResult;
|
||||
}
|
||||
|
||||
export function calcDateNum(date) {
|
||||
const day1 = new Date(date[0]).getTime();
|
||||
const day2 = new Date(date[1]).getTime();
|
||||
return (day2 - day1) / (1000 * 60 * 60 * 24) + 1;
|
||||
}
|
||||
|
||||
export function copyDates(dates) {
|
||||
if (Array.isArray(dates)) {
|
||||
return dates.map(date => {
|
||||
if (date === null) {
|
||||
return date;
|
||||
}
|
||||
return new Date(date);
|
||||
});
|
||||
}
|
||||
return new Date(dates);
|
||||
}
|
||||
|
||||
export function getTime (dates) {
|
||||
if (Array.isArray(dates)) {
|
||||
return dates.map(date => {
|
||||
if (date instanceof Date) {
|
||||
return date.getTime();
|
||||
}
|
||||
return date;
|
||||
});
|
||||
}
|
||||
return (dates instanceof Date ? dates.getTime() : dates);
|
||||
}
|
||||
|
||||
export function formatMonthTitle(date) {
|
||||
if (!(date instanceof Date)) {
|
||||
date = new Date(date);
|
||||
}
|
||||
return `${date.getFullYear()}年${date.getMonth() + 1}月`;
|
||||
}
|
||||
|
||||
export function getMonths(minDate, maxDate) {
|
||||
const months = [];
|
||||
const cursor = new Date(minDate);
|
||||
cursor.setDate(1);
|
||||
do {
|
||||
months.push(cursor.getTime());
|
||||
cursor.setMonth(cursor.getMonth() + 1);
|
||||
} while (compareMonth(cursor, maxDate) !== 1);
|
||||
return months;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
/* eslint-disable */
|
||||
function getMonthEndDay(year, month) {
|
||||
return getDate(year, month - 1, 0).getDate();
|
||||
}
|
||||
function compareMonth(date1, date2) {
|
||||
date1 = getDate(date1);
|
||||
date2 = getDate(date2);
|
||||
|
||||
var year1 = date1.getFullYear();
|
||||
var year2 = date2.getFullYear();
|
||||
var month1 = date1.getMonth();
|
||||
var month2 = date2.getMonth();
|
||||
|
||||
if (year1 === year2) {
|
||||
return month1 === month2 ? 0 : month1 > month2 ? 1 : -1;
|
||||
}
|
||||
|
||||
return year1 > year2 ? 1 : -1;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getMonthEndDay: getMonthEndDay,
|
||||
compareMonth: compareMonth
|
||||
};
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 550 B |
Binary file not shown.
|
After Width: | Height: | Size: 552 B |
Binary file not shown.
|
After Width: | Height: | Size: 591 B |
Binary file not shown.
|
After Width: | Height: | Size: 600 B |
|
|
@ -0,0 +1,179 @@
|
|||
import deviceUtil from '../utils/device-util';
|
||||
import validator from '../behaviors/validator';
|
||||
import eventUtil from '../core/utils/event-util';
|
||||
|
||||
Component({
|
||||
options: {
|
||||
multipleSlots: true
|
||||
},
|
||||
behaviors: [validator],
|
||||
externalClasses: ['l-title-class'],
|
||||
properties: {
|
||||
// 胶囊栏颜色
|
||||
bgColor: {
|
||||
type: String,
|
||||
value: 'white'
|
||||
},
|
||||
// 状态栏颜色
|
||||
statusBarColor: {
|
||||
type: String,
|
||||
value: 'transparent'
|
||||
},
|
||||
// 标题栏颜色
|
||||
titleBarColor: {
|
||||
type: String,
|
||||
value: 'transparent'
|
||||
},
|
||||
// 标题颜色
|
||||
titleColor: {
|
||||
type: String,
|
||||
value: 'black'
|
||||
},
|
||||
// 胶囊按钮颜色
|
||||
capsuleColor: {
|
||||
type: String,
|
||||
value: 'black',
|
||||
options: ['white', 'black']
|
||||
},
|
||||
// 禁用左侧按钮返回上一页
|
||||
disableBack: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
// 禁用右侧按钮返回主页
|
||||
disableHome: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
// 隐藏胶囊按钮
|
||||
hiddenCapsule: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
// 主页路径
|
||||
homePage: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
// 页面标题
|
||||
title: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
// 顶部填充
|
||||
hasPadding: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
// 隐藏标题
|
||||
hiddenTitle: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
// 左侧胶囊按钮左侧图标路径
|
||||
capsuleLeftIconPath: {
|
||||
type: String,
|
||||
value: null
|
||||
},
|
||||
// 左侧胶囊按钮左侧图标宽度,单位:rpx
|
||||
capsuleLeftIconWidth: {
|
||||
type: Number,
|
||||
value: null
|
||||
},
|
||||
// 左侧胶囊按钮左侧图标高度,单位:rpx
|
||||
capsuleLeftIconHeight: {
|
||||
type: Number,
|
||||
value: null
|
||||
},
|
||||
// 左侧胶囊按钮右侧图标路径
|
||||
capsuleRightIconPath: {
|
||||
type: String,
|
||||
value: null
|
||||
},
|
||||
// 左侧胶囊按钮右侧图标宽度,单位:rpx
|
||||
capsuleRightIconWidth: {
|
||||
type: Number,
|
||||
value: null
|
||||
},
|
||||
// 左侧胶囊按钮右侧图标高度,单位:rpx
|
||||
capsuleRightIconHeight: {
|
||||
type: Number,
|
||||
value: null
|
||||
}
|
||||
},
|
||||
|
||||
data: {
|
||||
// 标题栏高度(单位px)
|
||||
titleBarHeight: deviceUtil.getTitleBarHeight(),
|
||||
// 状态栏高度(单位px)
|
||||
statusBarHeight: deviceUtil.getStatusBarHeight(),
|
||||
// 左侧胶囊按钮信息
|
||||
capsuleButtonInfo: null
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
ready: function () {
|
||||
this.setData({
|
||||
capsuleButtonInfo: this.getCapsuleButtonInfo()
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
/**
|
||||
* 获取左侧胶囊按钮信息
|
||||
*/
|
||||
getCapsuleButtonInfo() {
|
||||
const screenWidth = wx.getSystemInfoSync().screenWidth;
|
||||
const capsuleButtonInfo = wx.getMenuButtonBoundingClientRect();
|
||||
capsuleButtonInfo.left = screenWidth - capsuleButtonInfo.right;
|
||||
capsuleButtonInfo.right = capsuleButtonInfo.left + capsuleButtonInfo.width;
|
||||
return capsuleButtonInfo;
|
||||
},
|
||||
|
||||
/**
|
||||
* 监听:点击左侧按钮
|
||||
*/
|
||||
onTapLeftButton() {
|
||||
eventUtil.emit(this, 'linlefttap');
|
||||
|
||||
if (!this.data.disableBack) {
|
||||
wx.navigateBack();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 监听:长按左侧按钮
|
||||
*/
|
||||
onLongPressLeftButton() {
|
||||
eventUtil.emit(this, 'linleftlongpress');
|
||||
},
|
||||
|
||||
/**
|
||||
* 监听:点击右侧按钮
|
||||
*/
|
||||
async onTapRightButton() {
|
||||
eventUtil.emit(this, 'linrighttap');
|
||||
|
||||
const homePage = this.data.homePage;
|
||||
if (!this.data.disableHome) {
|
||||
wx.switchTab({
|
||||
url: homePage,
|
||||
fail() {
|
||||
wx.navigateTo({
|
||||
url: homePage
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 监听:长按右侧按钮
|
||||
*/
|
||||
onLongPressRightButton() {
|
||||
eventUtil.emit(this, 'linrightlongpress');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"component": true
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
.container {
|
||||
position: relative;
|
||||
|
||||
.capsule-bar {
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
|
||||
.status-bar {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.title-bar {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: -4rpx;
|
||||
padding-top: 4rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.capsule-button {
|
||||
border-radius: 99999px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid rgb(0 0 0 / 10%);
|
||||
position: fixed;
|
||||
|
||||
.icon-wrapper-hover-black {
|
||||
background-color: rgb(0 0 0 / 36%);
|
||||
}
|
||||
|
||||
.icon-wrapper-hover-white {
|
||||
background-color: rgb(255 255 255 / 33.9%);
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.icon-left {
|
||||
display: block;
|
||||
width: 20rpx;
|
||||
height: 34rpx;
|
||||
}
|
||||
|
||||
.icon-right {
|
||||
display: block;
|
||||
width: 38rpx;
|
||||
height: 34rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.line {
|
||||
height: 18px;
|
||||
width: 1px;
|
||||
line-height: 1;
|
||||
background-color: rgb(255 255 255 / 25%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-container {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<!--为什么没有设置一个cover-image用来承载背景图?因为CapsuleBar在不同机型上大小是动态变化的-->
|
||||
<!--cover-image又不能设置填充方式,所以将此部分开放给用户处理是最佳的选择-->
|
||||
<view class="container" style="padding-top: {{hasPadding?titleBarHeight+statusBarHeight:'0'}}rpx">
|
||||
<!--胶囊栏-->
|
||||
<cover-view class="capsule-bar" style="background-color: {{bgColor}};">
|
||||
<!--状态栏-->
|
||||
<!--这里加4是因为不加4的时候statusBar和titleBar之间容易出现一条白线,所以加上4让两部分重叠,消除白线-->
|
||||
<cover-view
|
||||
class="status-bar"
|
||||
style="height: {{statusBarHeight+4}}rpx;background-color: {{statusBarColor}};"
|
||||
></cover-view>
|
||||
|
||||
<!--标题栏-->
|
||||
<cover-view
|
||||
class="title-bar"
|
||||
style="height: {{titleBarHeight}}rpx;background-color: {{titleBarColor}};"
|
||||
>
|
||||
<cover-view wx:if="{{!hiddenTitle}}" class="title l-title-class" style="color: {{titleColor}};">{{title}}</cover-view>
|
||||
<slot name="title"/>
|
||||
</cover-view>
|
||||
|
||||
<!--胶囊按钮-->
|
||||
<cover-view
|
||||
wx:if="{{!hiddenCapsule}}"
|
||||
class="capsule-button"
|
||||
style="border-color: rgba({{capsuleColor==='black'?'0,0,0,0.1':'255,255,255,0.25'}});background-color: rgba({{capsuleColor==='black'?'255,255,255,0.6':'0,0,0,0.15'}});width: {{capsuleButtonInfo.width}}px;height: {{capsuleButtonInfo.height}}px;left: {{capsuleButtonInfo.left}}px;top: {{capsuleButtonInfo.top}}px;"
|
||||
>
|
||||
<!--左侧按钮-->
|
||||
<cover-view mut-bind:tap="onTapLeftButton"
|
||||
catch:longpress="onLongPressLeftButton"
|
||||
hover-class="icon-wrapper-hover-{{capsuleColor}}"
|
||||
class="icon-wrapper"
|
||||
style="width: {{capsuleButtonInfo.width/2}}px;height: {{capsuleButtonInfo.height}}px;">
|
||||
<cover-image
|
||||
class="icon-left"
|
||||
style="width:{{capsuleLeftIconWidth?capsuleLeftIconWidth+'rpx':''}};height:{{capsuleLeftIconHeight?capsuleLeftIconHeight+'rpx':''}};"
|
||||
src="{{capsuleLeftIconPath?capsuleLeftIconPath:'icons/capsule-left-'+capsuleColor+'.png'}}"></cover-image>
|
||||
</cover-view>
|
||||
|
||||
<!--分割线-->
|
||||
<cover-view class="line"></cover-view>
|
||||
|
||||
<!--右侧按钮-->
|
||||
<cover-view mut-bind:tap="onTapRightButton"
|
||||
catch:longpress="onLongPressRightButton"
|
||||
hover-class="icon-wrapper-hover-{{capsuleColor}}"
|
||||
class="icon-wrapper"
|
||||
style="width: {{capsuleButtonInfo.width/2}}px;height: {{capsuleButtonInfo.height}}px;">
|
||||
<cover-image
|
||||
class="icon-right"
|
||||
style="width:{{capsuleRightIconWidth?capsuleRightIconWidth+'rpx':''}};height:{{capsuleRightIconHeight?capsuleRightIconHeight+'rpx':''}}"
|
||||
src="{{capsuleRightIconPath?capsuleRightIconPath:'icons/capsule-right-'+capsuleColor+'.png'}}"></cover-image>
|
||||
</cover-view>
|
||||
</cover-view>
|
||||
</cover-view>
|
||||
|
||||
<view class="content-container">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import validator from '../behaviors/validator';
|
||||
|
||||
Component({
|
||||
/**
|
||||
* 组件的属性列表
|
||||
*/
|
||||
externalClasses: ['l-class', 'l-img-class', 'l-title-class'],
|
||||
options: {
|
||||
multipleSlots: true // 在组件定义时的选项中启用多slot支持
|
||||
},
|
||||
behaviors: [validator],
|
||||
properties: {
|
||||
image: String,
|
||||
title: String,
|
||||
describe: String,
|
||||
plaintext: Boolean,
|
||||
full: Boolean,
|
||||
position: {
|
||||
type: String,
|
||||
value: 'left',
|
||||
options: ['left', 'right']
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
value: 'primary',
|
||||
options: ['primary', 'avatar','cover']
|
||||
},
|
||||
imageMode: {
|
||||
type: String,
|
||||
value: 'scaleToFill'
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 组件的初始数据
|
||||
*/
|
||||
data: {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 组件的方法列表
|
||||
*/
|
||||
methods: {
|
||||
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
.card-container {
|
||||
margin: 0 auto;
|
||||
padding: 30rpx;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
|
||||
&-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-unfull {
|
||||
width: 92%;
|
||||
box-shadow: 0 4rpx 20rpx 0 rgb(212 217 223 / 50%);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&-primary-left {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
&-primary-right {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
&-avatar {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&-cover {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.cover-img {
|
||||
&-full {
|
||||
width: 100%;
|
||||
height: 260rpx;
|
||||
}
|
||||
|
||||
&-unfull {
|
||||
width: 100%;
|
||||
height: 260rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.card-img {
|
||||
&-primary {
|
||||
height: 240rpx;
|
||||
width: 240rpx;
|
||||
overflow: hidden;
|
||||
|
||||
&-left {
|
||||
margin-right: 40rpx;
|
||||
}
|
||||
|
||||
&-right {
|
||||
margin-left: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&-avatar {
|
||||
height: 60rpx;
|
||||
width: 60rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.card-img-avatar {
|
||||
height: 60rpx;
|
||||
width: 60rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.title-view {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: #333;
|
||||
|
||||
&-primary {
|
||||
font-size: 32rpx;
|
||||
display: box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
&-avatar {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
&-cover {
|
||||
font-size: 30rpx;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.card-title-avatar {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.card-avatar-top {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100rpx;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.card-avatar-left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card-avatar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.describe {
|
||||
color: #666;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<view class="l-class card-container {{'card-container-' + type}} {{'card-container-' + type + '-' + position}} {{full?'card-container-full':'card-container-unfull'}}">
|
||||
<block wx:if="{{type ==='primary' || type ==='cover'}}">
|
||||
<image wx:if="{{!plaintext}}" class="l-img-class {{full?'cover-img-full':'cover-img-unfull'}} {{ 'card-img-' + type }} {{ 'card-img-' + type + '-' + position }}" mode="{{imageMode}}" lazy-load src="{{image}}"></image>
|
||||
<view class="card-content">
|
||||
<view class="title-view">
|
||||
<text class="l-title-class card-title {{'card-title-' + type}}">{{title}}</text>
|
||||
<slot name="more" />
|
||||
</view>
|
||||
<slot />
|
||||
</view>
|
||||
</block>
|
||||
<block wx:if="{{type ==='avatar'}}">
|
||||
<view class="card-avatar-top">
|
||||
<view class="card-avatar-left">
|
||||
<image mode="aspectFill" class="l-img-class {{ 'card-img-' + type }}" src="{{image}}" mode="{{imageMode}}" lazy-load></image>
|
||||
<view class="card-avatar">
|
||||
<text class="l-title-class card-title {{'card-title-' + type}}">{{title}}</text>
|
||||
<text class="describe">{{describe}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<slot name="more" />
|
||||
</view>
|
||||
<slot />
|
||||
</block>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
import eventBus from '../core/utils/event-bus';
|
||||
import rules from '../behaviors/rules';
|
||||
|
||||
|
||||
Component({
|
||||
behaviors: ['wx://form-field',rules],
|
||||
externalClasses: ['l-class', 'l-error-text', 'l-error-text-class'],
|
||||
relations: {
|
||||
'../checkbox/index': {
|
||||
type: 'child',
|
||||
linked(target) {
|
||||
this.init(target);
|
||||
},
|
||||
linkChanged() {
|
||||
},
|
||||
unlinked() {
|
||||
// this.init(target);
|
||||
}
|
||||
}
|
||||
},
|
||||
properties: {
|
||||
// 选项的排列方式 一行显示 or 多行显示
|
||||
placement: {
|
||||
type: String,
|
||||
value: 'column', //column row
|
||||
},
|
||||
// 最多选中值
|
||||
maxSelected: {
|
||||
type: Number,
|
||||
value: null
|
||||
},
|
||||
minSelected: {
|
||||
type: Number,
|
||||
value: null
|
||||
},
|
||||
},
|
||||
data: {
|
||||
},
|
||||
attached() {
|
||||
let { minSelected, maxSelected} = this.properties;
|
||||
this.checkMax(minSelected, maxSelected);
|
||||
},
|
||||
methods: {
|
||||
|
||||
init(target) {
|
||||
if(this._keys === undefined) this._keys = {};
|
||||
if(this._selected === undefined) this._selected = {};
|
||||
this.checkDefaultItem(target);
|
||||
this.checkedKeyRepeat(target);
|
||||
},
|
||||
|
||||
checkedKeyRepeat(target) {
|
||||
let { key } = target.properties;
|
||||
if(this._keys[key]) {
|
||||
throw new Error(`keys有重复元素, checkbox的key属性不能重复:${key}`);
|
||||
} else {
|
||||
this._keys[key] = true;
|
||||
}
|
||||
},
|
||||
|
||||
checkDefaultItem(target) {
|
||||
const { key, checked, cell } = target.properties;
|
||||
if(checked) {
|
||||
this._selected[key] = {...cell,checked:true, value: key};
|
||||
}
|
||||
},
|
||||
|
||||
checkMax(min, max) {
|
||||
if(min !== null && min < 0) {
|
||||
throw new Error('最小选择个数必须大于等于0');
|
||||
}
|
||||
if(max !== null && max < 0) {
|
||||
throw new Error('最多选择个数必须大于0');
|
||||
}
|
||||
if(max !== null && min !== null && min >= max) {
|
||||
throw new Error('最多选择个数必须大于最小选择个数');
|
||||
}
|
||||
},
|
||||
|
||||
onEmitEventHandle(currentItem) {
|
||||
currentItem.checked ? this.addSelect (currentItem):this.removeSelect(currentItem.key);
|
||||
|
||||
this.validatorData({
|
||||
[this.data.name]: Object.values(this._selected)
|
||||
});
|
||||
|
||||
this.triggerEvent('linchange', currentItem, {
|
||||
bubbles: true,
|
||||
composed: true
|
||||
});
|
||||
eventBus.emit(`lin-form-change-${this.id}`,this.id);
|
||||
},
|
||||
onEmitOverflowHandle(data){
|
||||
this.triggerEvent('linout', data, {
|
||||
bubbles: true,
|
||||
composed: true
|
||||
});
|
||||
},
|
||||
removeSelect(key) {
|
||||
delete this._selected[key];
|
||||
},
|
||||
addSelect(currentItem) {
|
||||
let {key, ...obj} = currentItem;
|
||||
this._selected[key] = {...obj, value: key};
|
||||
},
|
||||
getValues() {
|
||||
return Object.values(this._selected);
|
||||
},
|
||||
reset() {
|
||||
this._selected = {};
|
||||
const list = this.getRelationNodes('../checkbox/index');
|
||||
return list.forEach(item => {
|
||||
return item.setData({
|
||||
checked: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"l-error-tip":"../error-tip/index",
|
||||
"l-checkbox": "../checkbox/index"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
.checkbox-group {
|
||||
flex-wrap: wrap;
|
||||
|
||||
&-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
&-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
// .checkbox-group-button{
|
||||
// display: flex;
|
||||
// flex-direction: row !important;
|
||||
// flex-wrap: wrap;
|
||||
// }
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<view class="l-class checkbox-group checkbox-group-{{placement}} ">
|
||||
<slot></slot>
|
||||
</view>
|
||||
<l-error-tip l-error-text-class="l-error-text l-error-text-class" errorText="{{errorText}}" wx:if="{{errorText}}"/>
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
Component({
|
||||
behaviors: ['wx://form-field'],
|
||||
externalClasses: ['l-class', 'l-disabled-class', 'l-icon-class'],
|
||||
relations: {
|
||||
'../checkbox-group/index': {
|
||||
type: 'parent'
|
||||
}
|
||||
},
|
||||
options: {
|
||||
multipleSlots: true
|
||||
},
|
||||
properties: {
|
||||
// checkbox 按钮的位置
|
||||
placement: {
|
||||
type: String,
|
||||
value: 'left'
|
||||
},
|
||||
// 是否自定义图标内容
|
||||
custom: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
key: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
cell: {
|
||||
type: Object,
|
||||
value: {}
|
||||
},
|
||||
// checkbox的大小
|
||||
size: {
|
||||
type: String,
|
||||
value: '38rpx'
|
||||
},
|
||||
// 不可选状态
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
// 选中后的颜色
|
||||
selectColor: {
|
||||
type: String,
|
||||
value: '#3963BC'
|
||||
},
|
||||
disabledColor: {
|
||||
type: String,
|
||||
value: '#ccc'
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
value: '#ccc'
|
||||
},
|
||||
checked: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
}
|
||||
},
|
||||
data: {
|
||||
parentPlacement: ''
|
||||
},
|
||||
ready() {
|
||||
console.log(this.data.checked);
|
||||
const parent = this.getRelationNodes('../checkbox-group/index')[0];
|
||||
let {placement: parentPlacement} = parent.properties;
|
||||
this.setData({parentPlacement});
|
||||
},
|
||||
methods: {
|
||||
// 点击checkbox
|
||||
onCheckboxChangeTap() {
|
||||
if (this.properties.disabled || this.data.parentDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parent = this.getRelationNodes('../checkbox-group/index')[0];
|
||||
|
||||
if (this.properties.checked) {
|
||||
if (this.isOverflow('minSelected')) return;
|
||||
} else {
|
||||
if (this.isOverflow('maxSelected')) return;
|
||||
}
|
||||
|
||||
const item = {
|
||||
checked: !this.properties.checked,
|
||||
key: this.properties.key,
|
||||
cell: this.properties.cell
|
||||
};
|
||||
if (parent) {
|
||||
parent.onEmitEventHandle(item);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} type (max/min)
|
||||
*/
|
||||
isOverflow(type) {
|
||||
const parent = this.getRelationNodes('../checkbox-group/index')[0];
|
||||
|
||||
const limit = parent.properties[type];
|
||||
if (!limit) return false;
|
||||
const selectedLength = Object.values(parent._selected).length;
|
||||
let isOverflow = type === 'minSelected' ? selectedLength <= limit : selectedLength >= limit;
|
||||
if (isOverflow) {
|
||||
let backType = type === 'minSelected' ? 'min_selected' : 'max_selected';
|
||||
parent.onEmitOverflowHandle && parent.onEmitOverflowHandle({
|
||||
key: this.properties.key,
|
||||
limitNumber: limit,
|
||||
type: `overflow_${backType}`
|
||||
});
|
||||
}
|
||||
return isOverflow;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"l-button": "../button/index"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
@import "../../config/styles/_theme.less";
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
&-left {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
&-right {
|
||||
flex-direction: row-reverse;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&-disabled {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
&-placement-row {
|
||||
padding-right: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-left {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.checkbox-right {
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: iconfont !important;
|
||||
display: inline-flex;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: iconfont;
|
||||
src: url("data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAMMAAsAAAAABxgAAAK9AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDBgqBeIFdATYCJAMMCwgABCAFhG0HQBstBlGUzMkA2c8E020e0uBCKA1ReoTOr2jhX60Fez8evvb7nbu7X40GSTTS8S6aRBOpkCB1plMyIWkJb/+nLTemUXn+tjy1proDbGd+u9OXhuW20ygDQoJwOPhjfw50kxP0dlsW4tKSEtLyKhWqIpT/3MKy5KTZgMvpr8TnWZbTWts2jgmYYEBjDHuKrEQCzjUF2R2BWvJ9Am0LeioHm9MVtBXOskC8RIED7VJMUaRiU6i37C3iHV4zvaZPKG/x5+OfLdoktcxZe/S89mH27bv8VD0phAT5ch5Ih8jYBApx3eq7Uktzm2rahqZu1oO6IqSlklVFLDMN9YP+8RJR55R2wSrNJ775IoLvWEgggzoKDoFYipp4U38JhsVnsZqMDSLDTR9O5v6lxM+emWKnJ0j1WkJfv81PpvtdvqfbrTPuXw1+/Yswbx81bvFu4GNdLq7/WnYQrkR7OT7DTrwSHIOw0QhHGI+BEQb/TCiV/n/7MQFAQ9mO3iwSi9Gz9MCEpIu0tWgZiFkilLN8tRFgA1SnfMgz8o0Kgo9Q+Gcx3f/tTgFfe45JtDoVFO7QvAt/8FflYE8xlY49F13my+xAtjTRk6ASOahCOaGxn+mGGVxKaJrlSBrmkTUtkoWziZqOLdQ1baNtw/Zwx4SoidLCuneCMPSBpO8T2dAXWTjfqJnzi7ph1NB2EdwzO5Yj6oaEHCUf3UeMPM6N59RuUn6m4DZ1pKiMuTckZZTE8WBULW4pJ1liQ3kXTFQNGuEMN+QxSlPGQjgmTwehajEdDk3biwYeZ9C1TxCHIj7keoQiHpYz4XDVrb3/jARupRzS09AQv0FEKRodjQ2MAHKry6GGR2FTuhOYUMpAhmAZtCEekro5hor2WTHiUQPhhExhasgGGVQ32N6Q/UCp1ILzXI4UOYrOQ0he4vKDxXk7twIAAAAA") format("woff2"),;
|
||||
}
|
||||
|
||||
.icon-unselect::before {
|
||||
content: "\e727";
|
||||
}
|
||||
|
||||
.icon-select::before {
|
||||
content: "\e725";
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<view mut-bind:lintap="onCheckboxChangeTap" mut-bind:tap="onCheckboxChangeTap" class="label label-{{placement}} label-placement-{{parentPlacement}} {{disabled ? 'label-disabled l-disabled-class' : 'l-class'}}">
|
||||
<view class="checkbox-{{placement}} l-icon-class" style="color:{{checked ? selectColor : (disabled ? disabledColor : color)}};font-size: {{size}}">
|
||||
<slot wx:if="{{custom}}" name="icon" />
|
||||
<view wx:else class="iconfont {{checked? 'icon-select': 'icon-unselect'}}"></view>
|
||||
</view>
|
||||
<slot />
|
||||
</view>
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
// miniprogram_npm/lin-ui/circle/index.js
|
||||
Component({
|
||||
/**
|
||||
* 组件的属性列表
|
||||
*/
|
||||
externalClasses: ['l-value-class'],
|
||||
properties: {
|
||||
percent: {
|
||||
type: Number,
|
||||
value: 0
|
||||
},
|
||||
outerDiameter: {
|
||||
type: Number,
|
||||
value: 220
|
||||
},
|
||||
innerDiameter: {
|
||||
type: Number,
|
||||
value: 170
|
||||
},
|
||||
activeColor: {
|
||||
type: String
|
||||
},
|
||||
backgroundColor: {
|
||||
type: String,
|
||||
value: '#EBEBEB',
|
||||
},
|
||||
innerColor: {
|
||||
type: String,
|
||||
value: '#FFFFFF',
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
duration: {
|
||||
type: Number,
|
||||
value: 30
|
||||
},
|
||||
showValue: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
valueColor: {
|
||||
type: String,
|
||||
},
|
||||
valueSize: {
|
||||
type: Number,
|
||||
value: 25
|
||||
}
|
||||
},
|
||||
|
||||
options: {
|
||||
multipleSlots: true,
|
||||
pureDataPattern: /^_/ // 指定所有 _ 开头的数据字段为纯数据字段
|
||||
},
|
||||
|
||||
/**
|
||||
* 组件的初始数据
|
||||
*/
|
||||
data: {
|
||||
displayPercent: 0,
|
||||
},
|
||||
|
||||
observers: {
|
||||
'percent': async function (percent) {
|
||||
if (percent > 100) {
|
||||
this.setData({
|
||||
percent: 100
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (percent < 0) {
|
||||
this.setData({
|
||||
percent: 0
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this.data.active) {
|
||||
let displayPercent = this.data.displayPercent;
|
||||
|
||||
if (displayPercent < percent) {
|
||||
while (displayPercent < percent) {
|
||||
await this.sleep(this.data.duration);
|
||||
displayPercent += 1;
|
||||
this.setData({
|
||||
displayPercent: displayPercent
|
||||
});
|
||||
}
|
||||
} else if (displayPercent > percent) {
|
||||
while (displayPercent > percent) {
|
||||
await this.sleep(this.data.duration);
|
||||
displayPercent -= 1;
|
||||
this.setData({
|
||||
displayPercent
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.setData({
|
||||
displayPercent: percent
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
'outerDiameter': function (outerDiameter) {
|
||||
if (outerDiameter < this.data.innerDiameter) {
|
||||
outerDiameter = this.data.innerDiameter;
|
||||
this.setData({
|
||||
outerDiameter
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
'innerDiameter': function (innerDiameter) {
|
||||
if (innerDiameter < 0) {
|
||||
this.setData({
|
||||
innerDiameter: 0
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 组件的方法列表
|
||||
*/
|
||||
methods: {
|
||||
sleep(milSec) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, milSec);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/* miniprogram_npm/lin-ui/circle/index.wxss */
|
||||
@import "../../config/styles/_base.less";
|
||||
|
||||
.sector {
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sx1,
|
||||
.sx2 {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.sx1 {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.sx2 {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.sx_t {
|
||||
border-radius: 50%;
|
||||
z-index: 3;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sector1 {
|
||||
background: @progress-active-color;
|
||||
}
|
||||
|
||||
.sector2 .sx1,
|
||||
.sector2 .sx2 {
|
||||
background: @progress-active-color;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<view class="sector {{((displayPercent < 50 && displayPercent !== 0) || displayPercent === 100) ? 'sector1' : 'sector2'}}" style="height:{{outerDiameter}}rpx;width:{{outerDiameter}}rpx;{{((displayPercent < 50 && displayPercent !== 0) || displayPercent === 100) ?activeColor?'background:'+activeColor:'':'background:'+backgroundColor}}">
|
||||
<view wx:if="{{displayPercent !== 0 && displayPercent !== 100}}" class="sx1" style="height:{{outerDiameter}}rpx;width:{{outerDiameter}}rpx;clip: rect(0rpx, {{outerDiameter/2}}rpx, {{outerDiameter}}rpx, 0rpx);background:{{((displayPercent < 50 && displayPercent !== 0) || displayPercent === 100) ?backgroundColor:activeColor?activeColor:''}}"></view>
|
||||
<view wx:if="{{displayPercent !== 50 && displayPercent !== 0 && displayPercent !== 100}}" class="sx2" style="transform: rotate({{displayPercent < 50 ? ((360 * displayPercent / 100) - 180):(360 * (displayPercent - 100) / 100)}}deg);height:{{outerDiameter}}rpx;width:{{outerDiameter}}rpx;clip: rect(0rpx, {{outerDiameter/2}}rpx, {{outerDiameter}}rpx, 0rpx);background:{{((displayPercent < 50 && displayPercent !== 0) || displayPercent === 100) ?backgroundColor:activeColor}}"></view>
|
||||
<view class="sx_t" style="width:{{innerDiameter}}rpx;height:{{innerDiameter}}rpx;background:{{innerColor}}">
|
||||
<view wx:if="{{showValue}}" class="l-value-class" style="color:{{valueColor?valueColor:''}};font-size:{{valueSize}}rpx">
|
||||
{{displayPercent}}%
|
||||
</view>
|
||||
<slot></slot>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
import nodeUtil from '../core/utils/node-util';
|
||||
|
||||
Component({
|
||||
|
||||
externalClasses: ['l-class', 'l-title-class', 'l-body-class'],
|
||||
|
||||
relations: {
|
||||
'../collapse/index': {
|
||||
type: 'parent'
|
||||
}
|
||||
},
|
||||
|
||||
options: {
|
||||
multipleSlots: true,
|
||||
pureDataPattern: /^_/
|
||||
},
|
||||
|
||||
properties: {
|
||||
/**
|
||||
* 折叠面板子项自定义id
|
||||
*/
|
||||
itemId: {
|
||||
type: String,
|
||||
value: 'default'
|
||||
},
|
||||
/**
|
||||
* 标题文字
|
||||
*/
|
||||
title: {
|
||||
type: String,
|
||||
value: '默认标题'
|
||||
},
|
||||
/**
|
||||
* 是否开启自定义标题
|
||||
*/
|
||||
customTitle: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
/**
|
||||
* 是否禁用内容区展开
|
||||
*/
|
||||
disable: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
/**
|
||||
* 内容区域展开动画速度
|
||||
*/
|
||||
animationTime: {
|
||||
type: String,
|
||||
value: '0.3'
|
||||
}
|
||||
},
|
||||
data: {
|
||||
/**
|
||||
* 内容区高度
|
||||
*/
|
||||
bodyHeight: '0',
|
||||
/**
|
||||
* 内容区是否展开
|
||||
*/
|
||||
isExpandContent: false,
|
||||
/**
|
||||
* 默认id
|
||||
*/
|
||||
_idDefault: -1,
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 点击标题
|
||||
*/
|
||||
async onTapTitle() {
|
||||
if (this.data.disable) {
|
||||
return;
|
||||
}
|
||||
let parents = this.getRelationNodes('../collapse/index');
|
||||
await parents[0].onTapCollapseItem(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* 折叠内容区
|
||||
*/
|
||||
async foldContent() {
|
||||
// 获取 container-body-wrapper 的 css 属性信息
|
||||
const containerBodyWrapperRect =
|
||||
await nodeUtil.getNodeRectFromComponent(this, '.container-body-wrapper');
|
||||
|
||||
// 这里很重要,先把高度改为固定高度,transition 才会生效
|
||||
if (this.data.isExpandContent) {
|
||||
this.setData({
|
||||
bodyHeight: containerBodyWrapperRect.height + 'px'
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.setData({
|
||||
isExpandContent: false,
|
||||
bodyHeight: '0px'
|
||||
});
|
||||
}, 20);
|
||||
} else {
|
||||
this.setData({
|
||||
isExpandContent: false,
|
||||
bodyHeight: '0px'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 展开内容区
|
||||
*/
|
||||
async expandContent() {
|
||||
// 获取 container-body-wrapper 的 css 属性信息
|
||||
const containerBodyWrapperRect =
|
||||
await nodeUtil.getNodeRectFromComponent(this, '.container-body-wrapper');
|
||||
|
||||
this.setData({
|
||||
isExpandContent: true,
|
||||
bodyHeight: containerBodyWrapperRect.height + 'px'
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 过渡效果结束后,把高度改为 auto
|
||||
* 不然内容改变时,由于高度固定,内容会显示不全
|
||||
*/
|
||||
onTransitionend() {
|
||||
if (this.data.isExpandContent) {
|
||||
this.setData({
|
||||
bodyHeight: 'auto'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"l-list": "../list/index",
|
||||
"l-icon": "../icon/index"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
@import "../../config/styles/_base";
|
||||
|
||||
.container {
|
||||
//background-color: gainsboro;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
&-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 10rpx;
|
||||
align-items: center;
|
||||
font-size: @size-font-large;
|
||||
color: #333;
|
||||
|
||||
&-l-icon {
|
||||
transition: 0.3s;
|
||||
}
|
||||
}
|
||||
|
||||
&-body {
|
||||
width: 100%;
|
||||
color: @text-color;
|
||||
box-sizing: border-box;
|
||||
font-size: @size-font-base;
|
||||
transition: height 0.3s ease-in-out;
|
||||
overflow: hidden;
|
||||
|
||||
&-wrapper {
|
||||
position: relative;
|
||||
padding: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<view class="container l-class">
|
||||
<view mut-bind:tap="onTapTitle" class="container-title l-title-class">
|
||||
<view style="{{disable?'color:#DEE2E6':''}}" wx:if="{{!customTitle}}">{{title}}</view>
|
||||
<l-icon class="container-title-icon" wx:if="{{!customTitle}}" style="{{isExpandContent?'transform:rotate(-180deg);':''}}" name="down" size="28"
|
||||
color="{{disable?'#DEE2E6':'#333'}}"></l-icon>
|
||||
<slot name="title"></slot>
|
||||
</view>
|
||||
<view catch:transitionend="onTransitionend" class="container-body" style="height:{{bodyHeight}};transition-duration:{{animationTime}}s">
|
||||
<view class="container-body-wrapper l-body-class">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue