创建仓库

This commit is contained in:
王创世 2025-07-10 16:27:08 +08:00
commit 247eef3bca
217 changed files with 14331 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
project.config.json
.idea

370
app.js Normal file
View File

@ -0,0 +1,370 @@
/**
* tabBar页面路径列表 (用于链接跳转时判断)
* tabBarLinks为常量, 无需修改
*/
const tabBarLinks = [
'pages/index/index',
'pages/category/index',
'pages/flow/index',
'pages/user/index'
];
// 站点信息
import siteInfo from 'siteinfo.js';
App({
/**
* 全局变量
*/
globalData: {
user_id: null,
},
api_root: '', // api地址
/**
* 生命周期函数--监听小程序初始化
*/
onLaunch() {
let App = this;
// 设置api地址
App.setApiRoot();
wx.getSystemInfo({
success: e => {
// 设计稿一般是 750 rpx, 但是 canvas 是 px;
// 1rpx 转换成 px 的时候
this.globalData.rpx2px = 1 / 750 * e.windowWidth;
}
});
},
/**
* 当小程序启动或从后台进入前台显示会触发 onShow
*/
onShow(options) {
},
/**
* 设置api地址
*/
setApiRoot() {
let App = this;
App.api_root = `${siteInfo.siteroot}index.php?s=/api/`;
},
/**
* 获取小程序基础信息
*/
getWxappBase(callback) {
let App = this;
App._get('wxapp/base', {}, result => {
// 记录小程序基础信息
wx.setStorageSync('wxapp', result.data.wxapp);
callback && callback(result.data.wxapp);
}, false, false);
},
/**
* 执行用户登录
*/
doLogin() {
// 保存当前页面
let pages = getCurrentPages();
if (pages.length) {
let currentPage = pages[pages.length - 1];
"pages/login/login" != currentPage.route &&
wx.setStorageSync("currentPage", currentPage);
}
// 跳转授权页面
wx.navigateTo({
url: "/pages/login/login"
});
},
/**
* 当前用户id
*/
getUserId() {
return wx.getStorageSync('user_id') || 0;
},
/**
* 显示成功提示框
*/
showSuccess(msg, callback) {
wx.showToast({
title: msg,
icon: 'success',
success() {
callback && (setTimeout(() => {
callback();
}, 1500));
}
});
},
/**
* 显示失败提示框
*/
showError(msg, callback) {
wx.showModal({
title: '友情提示',
content: msg,
showCancel: false,
success(res) {
// callback && (setTimeout(() => {
// callback();
// }, 1500));
callback && callback();
}
});
},
/**
* get请求
*/
_get(url, data, success, fail, complete, check_login) {
let App = this;
wx.showNavigationBarLoading();
// 构造请求参数
data = Object.assign({
wxapp_id: 10001,
token: wx.getStorageSync('token')
}, data);
// if (typeof check_login === 'undefined')
// check_login = true;
// 构造get请求
let request = () => {
data.token = wx.getStorageSync('token');
wx.request({
url: App.api_root + url,
header: {
'content-type': 'application/json'
},
data,
success(res) {
if (res.statusCode !== 200 || typeof res.data !== 'object') {
console.log(res);
App.showError('网络请求出错');
return false;
}
if (res.data.code === -1) {
// 登录态失效, 重新登录
wx.hideNavigationBarLoading();
App.doLogin();
} else if (res.data.code === 0) {
App.showError(res.data.msg);
return false;
} else {
success && success(res.data);
}
},
fail(res) {
// console.log(res);
App.showError(res.errMsg, () => {
fail && fail(res);
});
},
complete(res) {
wx.hideNavigationBarLoading();
complete && complete(res);
},
});
};
// 判断是否需要验证登录
check_login ? App.doLogin(request) : request();
},
/**
* post提交
*/
_post_form(url, data, success, fail, complete) {
wx.showNavigationBarLoading();
let App = this;
// 构造请求参数
data = Object.assign({
wxapp_id: 10001,
token: wx.getStorageSync('token')
}, data);
wx.request({
url: App.api_root + url,
header: {
'content-type': 'application/x-www-form-urlencoded',
},
method: 'POST',
data,
success(res) {
if (res.statusCode !== 200 || typeof res.data !== 'object') {
App.showError('网络请求出错');
return false;
}
if (res.data.code === -1) {
// 登录态失效, 重新登录
App.doLogin(() => {
App._post_form(url, data, success, fail);
});
return false;
} else if (res.data.code === 0) {
App.showError(res.data.msg, () => {
fail && fail(res);
});
return false;
}
success && success(res.data);
},
fail(res) {
// console.log(res);
App.showError(res.errMsg, () => {
fail && fail(res);
});
},
complete(res) {
wx.hideLoading();
wx.hideNavigationBarLoading();
complete && complete(res);
}
});
},
/**
* 验证是否存在user_info
*/
validateUserInfo() {
let user_info = wx.getStorageSync('user_info');
return !!wx.getStorageSync('user_info');
},
/**
* 对象转URL
*/
urlEncode(data) {
var _result = [];
for (var key in data) {
var value = data[key];
if (value.constructor == Array) {
value.forEach(_value => {
_result.push(key + "=" + _value);
});
} else {
_result.push(key + '=' + value);
}
}
return _result.join('&');
},
/**
* 设置当前页面标题
*/
setTitle() {
let App = this,
wxapp;
if (wxapp = wx.getStorageSync('wxapp')) {
wx.setNavigationBarTitle({
title: wxapp.navbar.wxapp_title
});
} else {
App.getWxappBase(() => {
App.setTitle();
});
}
},
/**
* 设置navbar标题颜色
*/
setNavigationBar() {
let App = this;
// 获取小程序基础信息
App.getWxappBase(wxapp => {
// 设置navbar标题、颜色
wx.setNavigationBarColor({
frontColor: wxapp.navbar.top_text_color.text,
backgroundColor: wxapp.navbar.top_background_color
})
});
},
/**
* 获取tabBar页面路径列表
*/
getTabBarLinks() {
return tabBarLinks;
},
/**
* 验证登录
*/
checkIsLogin() {
return wx.getStorageSync('user_id') != '';
},
/**
* 授权登录
*/
getUserInfo(userInfo,user_id, callback) {
let App = this;
wx.showLoading({
title: "正在授权",
mask: true
});
// 执行微信登录
wx.login({
success(res) {
// 发送用户信息
App._post_form('user/login', {
code: res.code,
user_id: user_id,
user_info: JSON.stringify(userInfo)
}, result => {
// 记录token user_id
wx.setStorageSync('token', result.data.token);
// 执行回调函数
callback && callback();
}, false, () => {
wx.hideLoading();
});
}
});
},
/**
* 绑定用户身份证号
*/
getUserInfoByIdCard(idCard, callback) {
let App = this;
wx.showLoading({
title: "正在验证身份证号",
mask: true
});
App._post_form('user/checkIdCard',{
idCard:idCard
}, result => {
//记录user_id
if(!result.data.user_id){
wx.showToast({
title: '身份证号不存在',
icon: 'error',
duration: 2000
})
}else{
wx.setStorageSync('user_id', result.data.user_id);
// 执行回调函数
callback(result.data.user_id);
}
}, false, () => {
wx.hideLoading();
});
},
});

75
app.json Normal file
View File

@ -0,0 +1,75 @@
{
"pages": [
"pages/index/index",
"pages/rank/index",
"pages/rank/address",
"pages/rank/addInfo",
"pages/category/index",
"pages/category/challenge",
"pages/user/index",
"pages/user/sign",
"pages/login/login",
"pages/topic/index",
"pages/topic/addList",
"pages/topic/addInfo",
"pages/topic/xunzhang",
"pages/topic/address",
"pages/topic/detail",
"pages/category/team"
],
"subpackages": [
{
"root": "packageA",
"pages": [
"user/help",
"user/myChallenge",
"user/myMedal",
"user/profile",
"user/editProfile"
]
},
{
"root": "packageB",
"pages": [
"index/index",
"rule/index",
"user/sign"
]
}
],
"window": {
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTitleText": "",
"navigationBarTextStyle": "black",
"backgroundTextStyle": "dark"
},
"tabBar": {
"color": "#6e6d6b",
"selectedColor": "#890001",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "images/tabBar/home.png",
"selectedIconPath": "images/tabBar/home_on.png"
},
{
"pagePath": "pages/rank/index",
"text": "排行榜",
"iconPath": "images/tabBar/cate.png",
"selectedIconPath": "images/tabBar/cate_on.png"
},
{
"pagePath": "pages/user/index",
"text": "我的",
"iconPath": "images/tabBar/user.png",
"selectedIconPath": "images/tabBar/user_on.png"
}
],
"position": "bottom"
},
"debug": false,
"sitemapLocation": "sitemap.json"
}

927
app.wxss Normal file
View File

@ -0,0 +1,927 @@
/* common.wxss */
@import "/utils/common.wxss";
@import "/common.wxss";
@import 'weui.wxss';
page {
background: #f7f7f7;
}
.common-header-xian {
border-top: 1rpx solid #eee;
position: fixed;
top: 0;
width: 100%;
z-index: 100;
}
.del {
text-decoration: line-through;
padding-left: 10rpx;
color: #999;
}
/* 没有更多 */
.no-more {
text-align: center;
color: #737373;
padding: 20rpx 0;
}
.yoshop-notcont {
margin: 130rpx 100rpx;
}
.yoshop-notcont .cont {
display: block;
text-align: center;
font-size: 30rpx;
color: #999;
margin-top: 20rpx;
}
.yoshop-notcont .iconfont {
font-size: 150rpx;
color: #ccc;
text-align: center;
display: block;
margin-bottom: 24rpx;
}
.yoshop-notcont .img {
width: 200px;
height: 120px;
margin: 0 auto;
}
.yoshop-notcont .img image {
width: 100%;
height: 100%;
}
.category-list {
overflow: hidden;
}
.category-list .list {
box-sizing: border-box;
width: 50%;
float: left;
}
.category-list .list:nth-child(2n) {
border-left: 2px solid #f7f7f7;
border-bottom: 4px solid #f7f7f7;
}
.category-list .list:nth-child(2n-1) {
border-right: 2px solid #f7f7f7;
border-bottom: 4px solid #f7f7f7;
}
.category-list .list .left, .category-list .right {
width: 100%;
}
.category-list .list .left .img image {
width: 100%;
height: 375rpx;
display: block;
}
.category-list .right .cont {
padding: 0 12rpx;
}
.category-list .right .cont .title {
height: 76rpx;
line-height: 1.3;
}
.category-list.arrange .list {
overflow: hidden;
padding: 15rpx;
border-bottom: 1rpx solid #f7f7f7;
width: 100%;
}
.category-list.arrange .list .left {
width: 35%;
float: left;
}
.category-list.arrange .list .right {
width: 65%;
float: left;
}
.category-list.arrange .list .left .img image {
width: 220rpx;
height: 220rpx;
}
.button-common button {
background: none;
line-height: inherit;
border-radius: 0;
border: 0;
font-size: 30rpx;
}
.button-common button[disabled]:not([type]) {
color: #fff;
background-color: #ff495e;
}
.button-common button::after {
content: " ";
width: 0;
height: 0;
border: none;
transform: scale(0);
transform-origin: 0 0;
box-sizing: border-box;
border-radius: 0;
}
.commont-fixed-footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff;
border-top: 1rpx solid #ddd;
padding: 3px 0;
z-index: 1000;
}
.commont-fixed-footer .li {
color: #666;
}
.commont-fixed-footer .li.active {
color: #ff495e;
}
.commont-fixed-footer .li image {
width: 50rpx;
height: 50rpx;
}
.bargain-mol {
background: #fff;
position: fixed;
left: 0;
right: 0;
bottom: -100%;
z-index: 120;
visibility: hidden;
}
.bargain-mol.active {
bottom: 0;
visibility: visible;
}
.bargain-mol .header {
background: #f1f1f5;
}
.bargain-mol .footer {
background: #ff495e;
padding: 26rpx 0;
color: #fff;
}
.bargain-mol .max-cont {
height: 600rpx;
}
.bargain-mol .icon-guanbi {
font-size: 34rpx;
float: right;
color: #999;
}
.bargain-commont-bg {
background: rgba(0, 0, 0, 0.6);
position: fixed;
right: 0;
left: 0;
top: 0;
bottom: 0;
z-index: 20;
}
.selectNumber {
height: 34px;
flex-direction: row;
border: 1rpx solid #eee;
border-radius: 10rpx;
display: inline-block;
}
.selectNumber .default {
width: 34px;
height: 34px;
float: left;
line-height: 32px;
padding: 0;
background: #fff;
color: #444;
font-size: 48rpx;
}
.selectNumber .default-active {
background: #f7f7f7;
color: #ddd;
}
.selectNumber button:after {
content: none;
border: none;
}
.selectNumber input {
float: left;
width: 50px;
height: 34px;
line-height: 34px;
border-right: 1rpx solid #eee;
border-left: 1rpx solid #eee;
text-align: center;
font-size: 28rpx;
color: #444;
}
/* 返回顶部 */
.widget-goTop {
position: fixed;
bottom: 150rpx;
z-index: 20;
right: 12px;
background: rgba(255, 255, 255, 0.9);
width: 76rpx;
height: 76rpx;
border-radius: 76rpx;
border: 1rpx solid #eee;
}
.widget-goTop .icon-fanhuidingbu {
color: #666;
display: block;
text-align: center;
line-height: 76rpx;
font-size: 32rpx;
}
.index-loading .loading {
border-radius: 100%;
margin: 150rpx auto 0;
animation-fill-mode: both;
border: 2px solid #ff495e;
border-bottom-color: transparent;
height: 25px;
width: 25px;
background: transparent !important;
animation: rotate 0.75s 0s linear infinite;
}
@-webkit-keyframes rotate {
0% {
transform: rotate(0deg) scale(1);
}
100% {
transform: rotate(360deg) scale(1);
}
}
@keyframes rotate {
0% {
transform: rotate(0deg) scale(1);
}
100% {
transform: rotate(360deg) scale(1);
}
}
.title-header {
height: 100rpx;
line-height: 100rpx;
font-weight: 700;
margin-left: -10rpx;
}
.title-footer {
position: relative;
z-index: 1;
height: 80rpx;
line-height: 80rpx;
overflow: hidden;
color: #888;
text-align: center;
margin: 0 18rpx 0;
}
.title-footer .cont {
padding: 0 12rpx;
font-size: 28rpx;
z-index: 10;
background-color: #FFF;
}
.title-footer .hr {
background: #eee;
height: 1rpx;
border: 0;
position: absolute;
left: 10%;
right: 10%;
top: 50%;
margin-top: 1px;
z-index: -1;
}
.slide-image {
width: 100%;
height: 100%;
margin: 0 auto;
display: block;
}
.index_sale {
background: #fff;
padding: 0 12px 12px 12px;
}
.index_sale .nav_img, .index-list .nav_img {
padding: 30rpx 0 0 0;
width: 100%;
height: 30rpx;
}
.index_sale scroll-view {
width: 100%;
white-space: nowrap;
}
.index_sale .sale_img {
border: 1rpx solid #f2f2f2;
border-radius: 4px;
overflow: hidden;
width: 100%;
height: 300rpx;
}
.index_sale .sale_img image {
width: 100%;
height: 100%;
}
.index_sale .price {
margin-top: 10rpx;
display: block;
}
.index_sale .page-column {
padding: 0 11rpx 11rpx 0;
}
.index_sale .content {
width: 170rpx;
}
.index_sale .content text {
font-size: 26rpx;
margin: 5rpx 10rpx;
width: 100%;
}
/*
.flex {
display: flex;
} */
.goods-comment-box .admin {
font-size: 26rpx;
color: #999;
padding-right: 10rpx;
}
.goods-comment-cont {
font-size: 30rpx;
color: #333;
margin: 10rpx 0;
}
.footer-fixed {
position: fixed;
display: flex;
bottom: 0px;
left: 0px;
right: 0px;
height: 46px;
z-index: 18;
box-shadow: 1px 5px 15px rgba(50, 50, 50, 0.3);
background: #fff;
}
.order-bt {
width: 50%;
background-color: #ff495e;
color: #fff;
text-align: center;
line-height: 46px;
}
.swiper-box .wx-swiper-dot {
/* width: 0rpx;
height: 0rpx; */
}
.goods_comment_box .comment_btn {
width: 220rpx;
margin: 0 auto;
padding: 20rpx 0;
}
.goods_comment_box .comment_btn text {
display: block;
padding: 5rpx 0;
color: #ff495e;
font-size: 26rpx;
text-align: center;
border: 1px solid #ff495e;
border-radius: 30rpx;
}
.goods-detail-box {
padding: 0;
min-height: 150px;
}
.com_xing .icon-shoucang1 {
padding-right: 6rpx;
color: #ccc;
font-size: 26rpx;
}
.com_xing .icon-shoucang1.active {
color: #f4a213;
}
.goods-comment-box .left {
flex: 3;
position: relative;
}
.goods-comment-box .right {
flex: 3;
}
.bright789-text {
font-size: 40rpx;
line-height: 40px;
color: #f00;
}
.bright789_view_hide {
display: none;
}
.bright789_view_show {
display: block;
}
.show {
display: block;
}
.hide {
display: none;
}
.com_xing {
display: inline-block;
}
.flow-checkout-header {
padding: 28rpx 0;
background: #fff url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANYAAAANCAYAAADVGpDCAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NTc3MiwgMjAxNC8wMS8xMy0xOTo0NDowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3Yjk4M2ExYy1jMDhkLTQ1OTktYTI0Ny1kZjNjYzdiYTQ5ZTgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDQwNkY3RkU5N0NGMTFFNUI3N0M4NTU4MzM2RjlFODIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NDQwNkY3RkQ5N0NGMTFFNUI3N0M4NTU4MzM2RjlFODIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowNzgwZWI1NS03OGFhLTQzOTUtODQ4OC1lOWI5YmVlYTY1ZDciIHN0UmVmOmRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo1OTRiYzUyMy1jMzc3LTExNzgtYTdkZS04NGY3YmM1ZGIxMDMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz556PLxAAACBElEQVR42tyaSyhEYRTHP48imlKibDQeSSlkSlEWLCRFsZNH5FE2FqQ8ErIRC9lIkTwXSpMkWWChhEJCSnlkoUZGSsr78f98n43CMFPu/Z/6NZuZ2zn33/+cb869XkmLx8IDEQaGQJbgiytQDSY3MyL+LYnL/HxPXSoHDIJQQq2WQQk4Dbbb/yUB29LJ+6e3B66VB3ZITbUIEqSpCGoJBP1ghtBUD6ARpEtTGSEhXzd+awE9oJzQUPegWdf3QlBPMhgDMYRa7YNisGWkpP5qrBQtVBShUHugUE9hs4fUtwG0utlEjRivoA/Ug1sj3vjffr8FNJEK1auPFHcE9UTq5pdK2PwcoAzMG7mjuRrRYEIfK9jiDJSCBZJ6ynSTsBBqNQ0qgdPISbq6vJCFbJOaagrEk5gqWNczRGiqG1Ah1LLMafRkf5pYIUKtZnMJDXUNasAIST2ZYFioRx9ssQaKwJFZEv5uYmWDXVJTrYBEElP562PfPKGpnkAbSDOTqb6aWAGgW6iHol5kQj2CdtAJngnqkc1hHMQRNr9DPaXWzZj8Z2PZtFCxhEIdaKE2CGqRJ4060AH8CLUaALX6f5VpBZLhI9SaeZXQVHKNLt84SCIxVbhQi5YuQlNd6OVElZlN9TGxrGBUn2PZ4lyoTdIsST0FQj0UDSLUak6ot3gcBLVY3wQYAJoVXxmNERajAAAAAElFTkSuQmCC') bottom left repeat-x;
background-size: 120rpx auto;
position: relative;
}
.flow-header-left {
flex: 14;
}
.flow-header-right {
flex: 1;
}
.flow-header-right image {
width: 34rpx;
height: 34rpx;
margin-top: 20rpx;
float: right;
}
.flow-checkout-header .flow-checkout-address {
font-size: 26rpx;
color: #777;
margin-top: 6rpx;
}
.flow-shopList {
padding: 20rpx 0;
}
.flow-shopList .flow-list-left {
flex: 2;
}
.flow-shopList .flow-list-left image {
width: 200rpx;
height: 200rpx;
border: 1rpx solid #eee;
background: #fff;
}
.flow-shopList .flow-list-right {
flex: 4;
}
.flow-shopList .flow-list-right .h4 {
font-size: 30rpx;
color: #333;
}
.flow-shopList .flow-list-right .flow-cont {
font-size: 30rpx;
color: #ff495e;
}
.flow-shopList .flow-list-right .small {
float: right;
font-size: 26rpx;
color: #777;
}
.flow-shopList .flow-list-right .flow-list-cont {
padding-top: 10rpx;
}
.flow-fixed-footer {
position: fixed;
bottom: 0;
width: 100%;
background: #fff;
border-top: 1px solid #eee;
z-index: 11;
}
.flow-num-box {
font-size: 30rpx;
color: #777;
padding: 15rpx 12px;
text-align: right;
/* border-top: 1rpx solid #f1f1f1; */
}
.flow-all-money {
padding: 8px 12px;
color: #444;
}
.flow-all-money .flow-all-list {
font-size: 30rpx;
padding: 20rpx 0;
border-bottom: 1rpx solid #f1f1f1;
}
.flow-all-money .flow-all-list:last-child {
border-bottom: none;
}
.flow-all-money .flow-all-list-cont {
font-size: 28rpx;
padding: 6rpx 0;
}
.flow-all-money .flow-arrow {
justify-content: flex-end;
align-items: center;
}
.flow-fixed-footer .chackout-left {
font-size: 32rpx;
line-height: 46px;
color: #777;
flex: 4;
padding-left: 12px;
}
.flow-fixed-footer .chackout-right {
font-size: 34rpx;
flex: 2;
}
.flow-btn {
background-color: #ff495e;
color: #fff;
text-align: center;
line-height: 46px;
display: block;
}
.flow-list .header .shop_name {
padding-left: 10rpx;
font-size: 30rpx;
color: #333;
}
.flow-list .header .icon-dianpu2 {
color: #ff495e;
padding-left: 20rpx;
font-size: 32rpx;
}
.flow-list .header image {
width: 34rpx;
height: 37rpx;
position: absolute;
top: 50%;
margin-top: -18rpx;
left: 15px;
}
.flow-list .header {
background: #fdf9f9;
padding: 24rpx 0;
border-top: 1rpx solid #eee;
border-bottom: 1rpx solid #eee;
font-size: 30rpx;
position: relative;
}
.flow-list custom-li, .addres-list custom-li {
margin-top: 25rpx;
display: block;
}
.flow-list custom-li:first-child, .addres-list custom-li:first-child {
margin-top: 0;
}
.flow-distribution-right .icon-xiangyoujiantou {
font-size: 26rpx;
position: absolute;
right: 15px;
top: 50%;
margin-top: -16rpx;
color: #999;
}
.flow-checkout-address text {
padding-right: 5rpx;
}
.flow-header-right .icon-xiangyoujiantou {
position: absolute;
right: 15px;
top: 50%;
margin-top: -13rpx;
font-size: 32rpx;
color: #999;
}
.wxParse-em, .WxEmojiView {
display: inline-block;
color: #333;
}
.flow-shopList .flow-list-left image {
width: 180rpx;
height: 180rpx;
}
.profile-btn button {
background: #ff495e;
color: white;
margin-bottom: 20rpx;
}
.flow-checkout-header .icon-dingwei1 {
position: absolute;
top: 50%;
left: 15px;
font-size: 40rpx;
color: #777;
margin-top: -20rpx;
}
/*
.index-cont-search {
width: 85%;
font-size: 32rpx;
} */
.index-cont-search {
width: 100%;
font-size: 28rpx;
position: relative;
background: #f1f1f1;
}
.index-cont-search icon {
position: absolute;
left: 50%;
margin-left: -70rpx;
top: 50%;
margin-top: -15rpx;
}
.index-cont-search text {
margin-left: 72rpx;
}
@-webkit-keyframes shop {
0% {
transform: translateY(-80px);
}
50% {
transform: translateY(0px);
}
100% {
transform: translateY(-80px);
}
}
@keyframes shop {
0% {
transform: translateY(-80px);
}
50% {
transform: translateY(0px);
}
100% {
transform: translateY(-80px);
}
}
.user-order {
background: #fff;
}
.user-orderIcon {
width: 46rpx;
height: 46rpx;
padding-left: 15rpx;
margin-top: 15rpx;
}
.user-orderName {
font-size: 30rpx;
color: #444;
position: absolute;
left: 90rpx;
top: 50%;
margin-top: -21rpx;
}
.user-orderJtou {
color: #777;
font-size: 26rpx;
}
.user-orderCont {
font-size: 28rpx;
color: #999;
}
.user-orderContBox {
float: right;
padding: 15rpx;
}
.userinfo {
display: flex;
flex-direction: column;
align-items: center;
}
.address-box .left-name {
width: 95px;
}
.address-box .right-cont {
padding-right: 15px;
font-size: 30rpx;
color: #444;
}
.address-box .right-cont input {
width: 100%;
font-size: 30rpx;
color: #444;
}
.address-cont-box picker {
display: inline-block;
margin-right: 15px;
width: 100%;
}
.button {
border: 1px solid #1aad19;
border-radius: 2px;
}
.picker {
padding: 13px;
background-color: #fff;
}
.profile-list {
padding: 24rpx 0;
border-bottom: 1px solid #f6f6f9;
}
.profile-list .admin {
font-size: 30rpx;
color: #333;
}
.profile-btn button {
background: #ff495e;
color: white;
}
.profile-btn button[disabled] {
background: #f16474;
color: white;
}
.search-box .left {
width: 28px;
}
.search-box .left icon {
padding: 18rpx;
}
.search-box .right {
flex: 1;
}
.wxParse-img {
display: block;
width: 100%;
margin: 0 auto;
}
.wxParse-inline {
font-size: 28rpx;
text-align: center;
}
.wxParse-div {
overflow: hidden;
}
.wxParse-div .kd_pic {
float: left;
width: 50%;
margin: 0 auto;
}
.xEmojiView {
margin: 15rpx 0;
}
.relative {
position: relative;
}
.absolute {
position: absolute;
}
.fixed {
position: fixed;
}
.item-box{
width:100%;
height:380rpx;
border-radius: 12rpx;
overflow: hidden;
margin-bottom:20rpx;
}

410
common.wxss Normal file
View File

@ -0,0 +1,410 @@
/* flex */
.i-flex {
display: flex;
}
.i-flex-wrap {
flex-wrap: wrap;
}
.i-flex-nowrap {
flex-wrap: nowrap;
}
.i-flex-col {
display: flex;
flex-direction: column;
}
.i-flex-item {
flex: 1;
}
.i-flex-alc {
justify-content: center;
align-items: center;
}
.i-flex-spb {
justify-content: space-between;
align-items: center;
}
.i-aic {
align-items: center;
}
.jcontent-c {
justify-content: center;
}
.jcontent-sb {
justify-content: space-between;
}
.jcontent-sa {
justify-content: space-around;
}
.weight {
font-weight: bold;
}
.text-overflow1 {
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
}
.text-overflow2 {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.text-center {
text-align: center;
}
.text-right {
text-align: right;
}
.text-left {
text-align: left;
}
/* 字体大小 */
.fsz-16 {
font-size: 16rpx!important;
}
.fsz-22 {
font-size: 22rpx;
}
.fsz-24 {
font-size: 24rpx;
}
.fsz-26 {
font-size: 26rpx;
}
.fsz-28 {
font-size: 28rpx;
}
.fsz-30 {
font-size: 30rpx;
}
.fsz-32 {
font-size: 32rpx;
}
.fsz-34 {
font-size: 34rpx;
}
.fsz-36 {
font-size: 36rpx;
}
.fsz-38 {
font-size: 38rpx;
}
.fsz-60 {
font-size: 60rpx;
}
/* 字体颜色 */
.text-3 {
color: #333!important;
}
.text-6 {
color: #666;
}
.text-gray {
color: #999;
}
.text-white {
color: #fff!important;
}
.text-dark {
color: #000!important;
}
.text-sucess {
color: #5cb85c!important;
}
.text-warning {
color: #f0ad4e!important;
}
.red {
color: #ff5344;
}
.bule {
color: rgb(59,140,232);
}
/* 背景 */
.bg-f {
background-color: #fff;
}
.bg-blue {
background-color: rgb(59,140,232);
}
.bg-primary {
background-color: #ff5344;
}
.bg-lighter {
background-color: #f8f8f8;
}
.bg-ccc {
background: #ccc!important;
}
.bg-sucess {
background: #5cb85c!important;
}
.bg-warning {
background: #f0ad4e!important;
}
/* 边距 */
.ml5 {
margin-left: 10rpx;
}
.ml10 {
margin-left: 20rpx;
}
.mb5 {
margin-bottom: 10rpx;
}
.mt-auto {
margin-top: auto;
}
.mt5 {
margin-top: 10rpx;
}
.mx5 {
margin-left: 10rpx;
margin-right: 10rpx;
}
.m10 {
margin: 20rpx;
}
.mt10 {
margin-top: 20rpx;
}
.my10 {
margin-top: 10rpx;
margin-bottom: 10rpx;
}
.mx10 {
margin-left: 20rpx;
margin-right: 20rpx;
}
.mx15 {
margin-left: 30rpx;
margin-right: 30rpx;
}
.mb10 {
margin-bottom: 20rpx;
}
.mb20 {
margin-bottom: 40rpx;
}
.m15 {
margin: 30rpx;
}
.p5 {
padding: 10rpx;
}
.p10 {
padding: 20rpx;
}
.p15 {
padding: 30rpx;
}
.pt10 {
padding-top: 20rpx;
}
.pb10 {
padding-bottom: 20rpx!important;
}
.py10 {
padding-top: 20rpx!important;
padding-bottom: 20rpx!important;
}
.pb15 {
padding-bottom: 30rpx;
}
.px15 {
padding-left: 30rpx;
padding-right: 30rpx;
}
.py15 {
padding-top: 30rpx!important;
padding-bottom: 30rpx!important;
}
.pb20 {
padding-bottom: 40rpx;
}
.p30 {
padding: 30rpx;
}
.ml30 {
margin-left: 30rpx;
}
.pb100 {
padding-bottom: 100rpx;
}
.w0 {
width: 0;
}
.w90p {
width: 90%;
}
.maxH {
max-height: 75vh;
}
.rounded-mini {
border-radius: 5rpx;
}
.rounded {
border-radius: 10rpx;
}
.border-bottom,
.border-top {
position: relative;
}
.border-bottom::after {
content: "";
position: absolute;
left: 0;
bottom: 0;
right: 0;
border-bottom: 1px solid #e5e5e5;
transform-origin: 0 0;
transform: scaleY(0.5);
}
.border-top::before {
content: "";
position: absolute;
left: 0;
top: 0;
right: 0;
border-bottom: 1px solid #e5e5e5;
transform-origin: 0 0;
transform: scaleY(0.5);
}
.btn-hollow {
border: 1rpx solid #e5e5e5;
padding: 12rpx 20rpx;
border-radius: 30rpx;
font-size: 22rpx;
color: #666;
line-height: 1;
}
.line-through {
text-decoration: line-through;
}
.line-height {
line-height: 1.5;
}
/* 定位 */
.pos-r {
position: relative;
}
.pos-a {
position: absolute;
}
.pos-f {
position: fixed;
}
.avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
}
.shadow {
box-shadow: 1px 2px 5px #ccc;
}
.shadow-top {
box-shadow: 0px -3px 5px -2px #ccc;
}
.avatar-md {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
overflow: hidden;
}
.pm_index_line{border-bottom: 1px solid #DEDEDE;}
.pm_index{width:40rpx;height:40rpx;border-radius:15rpx;color:#333;text-align:center;line-height:40rpx;}
.pm_index1{
background-color:#FFDC7A;
}
.pm_index2{
background-color:#CBCBF6;
}
.pm_index3{
background-color:#F9A470;
}

View File

@ -0,0 +1,472 @@
// components/calendar/calendar.js
Component({
options: {
styleIsolation: 'apply-shared'
},
properties: {
date: {
type: null,
value: new Date()
},
/**
* 选中的日期
*/
selected: {
type: Array,
value: [],
observer(newVal, oldVal) {
this.getWeek(new Date())
}
},
/**
* 锁定日期
*/
lockDay: {
type: String,
},
/**
* 是否默认展开, 可以调用open事件展开组件
*/
isOpen: {
type: Boolean,
value: false
},
/**
* 是否多选日期
*/
multiple:{
type: Boolean,
value: true
},
/**
* 是否展示头部
*/
showHeader: {
type: Boolean,
value: false
},
/**
* 是否不能修改
*/
readonly: {
type: Boolean,
value: true
},
/**
* 使用mini样式
*/
mini:{
type: Boolean,
value: true
}
},
/**
* 组件的初始数据
*/
data: {
calShow: true, // 日历组件是否打开
dateShow: false, // 日期是否选择
selectDay: '', // 当前选择日期
selectDays: [], //多选日期
canlender: {
"weeks": []
},
currentMonth: null,
nowMonth: new Date().getMonth()+1,
nowDate: new Date().getDate()
},
ready() {
let sdays = this.data.selected;
sdays.forEach(v=>{
this.data.selectDays.push(v);
})
this.getWeek(new Date());
if (this.data.isOpen) {
this.setData({
calShow: false,
dateShow: true
})
}
},
/**
* 组件的方法列表
*/
methods: {
open: function(date) {
if(date && date!=''){
this.data.selectDays = []; //打开时候显示一个
this.data.selectDays.push(date);
this.setData({
selectDays: this.data.selectDays
})
this.getWeek(date);
}
this.dateSelection();
},
close: function() {
this.packup();
},
clear: function(){
this.setData({
selectDays: []
})
let m;
if(this.data.currentMonth == null){
m = this.data.canlender.month
}else{
m = this.data.currentMonth
}
let year = this.data.canlender.year + "-" + m + "-" + this.data.canlender.date
let _date = this.getDate(year, 0);
this.getWeek(_date);
this.triggerEvent("clear",{})
},
dateSelection() {
if (this.data.isOpen) {
return
}
let self = this;
if (self.data.calShow) {
self.setData({
calShow: false
}, () => {
setTimeout(() => {
self.setData({
dateShow: true
}, () => {
self.triggerEvent('show', {
ischeck: !self.data.calShow
})
})
}, 100)
})
} else {
self.setData({
dateShow: false
}, () => {
setTimeout(() => {
self.setData({
calShow: true
}, () => {
self.triggerEvent('show', {
ischeck: !self.data.calShow
})
})
}, 300)
})
}
},
//选择日期
selectDay(e) {
if(this.data.readonly){
return
}
let index = e.currentTarget.dataset.index;
let week = e.currentTarget.dataset.week;
let ischeck = e.currentTarget.dataset.ischeck;
let canlender = this.data.canlender;
if (!ischeck) return false;
let month = canlender.weeks[week][index].month < 10 ? "0" + canlender.weeks[week][index].month : canlender.weeks[week][index].month
let date = canlender.weeks[week][index].date < 10 ? "0" + canlender.weeks[week][index].date : canlender.weeks[week][index].date
let datestr = canlender.year + "-" + month + "-" + date;
let selectDays = this.data.selectDays;
if (selectDays.indexOf(datestr)==-1) {
if(this.data.multiple){
//多选
selectDays.push(datestr);
}else{
selectDays = []; //只保留最后选的
selectDays.push(datestr);
}
}else{
let index = selectDays.indexOf(datestr);
selectDays.splice(index, 1);
}
selectDays.sort(function(a, b){
let ds1 = a.split("-");
let ds2 = b.split("-");
let d1 = new Date(ds1[0], ds1[1], ds1[2]);
let d2 = new Date(ds2[0], ds2[1], ds2[2]);
return d1-d2;
});
this.data.selectDays = selectDays;
this.getWeek(datestr);
this.triggerEvent('getdate', {
datestr
})
},
//点击确定
packup() {
let self = this;
self.triggerEvent('select', {
selectDays: self.data.selectDays
})
if (this.data.isOpen) {
let year = self.data.canlender.year + "-" + self.data.canlender.month + "-" + self.data.canlender.date
let _date = self.getDate(year, 0);
self.getWeek(_date);
return
}
self.setData({
dateShow: false
}, () => {
setTimeout(() => {
self.setData({
calShow: true
}, () => {
let year = self.data.canlender.year + "-" + self.data.canlender.month + "-" + self.data.canlender.date
let _date = self.getDate(year, 0);
self.getWeek(_date);
})
}, 300)
})
},
/**
* 页面通过事件选中日期后调用refresh方法刷新日历组件
*/
refresh: function (num){
this.setData({
selectDays: num
})
let m;
if(this.data.currentMonth == null){
m = this.data.canlender.month
}else{
m = this.data.currentMonth
}
let year = this.data.canlender.year + "-" + m + "-" + this.data.canlender.date
let _date = this.getDate(year, 0);
this.getWeek(_date);
},
// 返回今天
backtoday() {
if(!this.data.readonly){
let datestr = this.dateStr(new Date());
this.data.selectDays.push(datestr);
this.setData({
selectDays: this.data.selectDays
});
this.triggerEvent('getdate', {
datestr
})
}
this.getWeek(new Date());
},
// 前一天|| 后一天
dataBefor(e) {
let num = 0;
let types = e.currentTarget.dataset.type;
if (e.currentTarget.dataset.id === "0") {
num = -1;
} else {
num = 1
}
let year = this.data.canlender.year + "-" + this.data.canlender.month + "-" + this.data.canlender.date
let _date = this.getDate(year, num, types === 'month' ? "month" : "day");
this.getWeek(_date);
},
// 获取日历内容
getWeek(dateData) {
let selected = this.data.selected
let selectDays = this.data.selectDays
// 判断当前是 安卓还是ios ,传入不容的日期格式
if (typeof dateData !== 'object') {
dateData = dateData.replace(/-/g, "/")
}
let _date = new Date(dateData);
let year = _date.getFullYear(); //年
let month = _date.getMonth() + 1; //月
let date = _date.getDate(); //日
let day = _date.getDay(); // 天
let canlender = [];
let dates = {
firstDay: new Date(year, month - 1, 1).getDay(),
lastMonthDays: [], // 上个月末尾几天
currentMonthDys: [], // 本月天数
nextMonthDays: [], // 下个月开始几天
endDay: new Date(year, month, 0).getDay(),
weeks: []
}
// 循环上个月末尾几天添加到数组
for (let i = dates.firstDay; i > 0; i--) {
let dd = new Date(year, month-1, -(i-1));
let checked = false;
selectDays.forEach(v => {
let selDate = v.date.split('-');
let ddstr = this.dateStr(dd);
if (ddstr == v) {
checked = true;
}
})
dates.lastMonthDays.push({
'date': '',
'month': '',
checked
})
}
// 循环本月天数添加到数组
for (let i = 1; i <= new Date(year, month, 0).getDate(); i++) {
let have = false;
let checked = false;
let step = '';
// for (let j = 0; j < selected.length; j++) {
// let selDate = selected[j].split('-');
// if (Number(year) === Number(selDate[0]) && Number(month) === Number(selDate[1]) && Number(i) === Number(selDate[2])) {
// have = true;
// }
// }
selectDays.forEach(v => {
let selDate = v.date.split('-');
if (Number(year) === Number(selDate[0]) && Number(month) === Number(selDate[1]) && Number(i) === Number(selDate[2])) {
checked = true;
step = v.step;
}
})
dates.currentMonthDys.push({
'date': i + "",
'month': month,
// have,
checked,
step
})
}
// 循环下个月开始几天 添加到数组
for (let i = 1; i < 7 - dates.endDay; i++) {
dates.nextMonthDays.push({
'date': '',
'month': ''
})
}
canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
// 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天
for (let i = 0; i < canlender.length; i++) {
if (i % 7 == 0) {
dates.weeks[parseInt(i / 7)] = new Array(7);
}
dates.weeks[parseInt(i / 7)][i % 7] = canlender[i]
}
// 渲染数据
this.setData({
selectDay: month + "月" + date + "日",
"canlender.weeks": dates.weeks,
'canlender.month': month,
'canlender.date': date,
"canlender.day": day,
'canlender.year': year,
})
month = month < 10 ? "0" + month : month
date = date < 10 ? "0" + date : date
// let localstr = year+"-"+month+"-"+date;
// this.triggerEvent('getdate', {
// year,
// month,
// date,
// localstr
// })
// console.log(dates.weeks);
},
checkall(){
let date = new Date();
let m, d, day, dayNum;
let days = [];
if(this.data.currentMonth == null){
m = parseInt(this.data.nowMonth)
}else{
m = parseInt(this.data.currentMonth)
}
switch (m) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
dayNum = 31;
break;
case 4:
case 6:
case 9:
case 11:
dayNum = 30;
break;
case 2:
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
dayNum = 29;
} else {
dayNum = 28;
}
break;
}
for (d = 1; d <= dayNum; d++) {
date.setMonth(m - 1, d);
day = date.getDay();
let str = this.format(date);
if(date.getMonth() == new Date().getMonth()){
if(date.getDate() - new Date().getDate() >= 0){
days.push(str);
}
}else if(date.getMonth() > new Date().getMonth()){
days.push(str);
}
}
this.refresh(days);
this.triggerEvent('checkall', {
days
})
},
/**
* 时间计算
*/
getDate(date, AddDayCount, str = 'day') {
if (typeof date !== 'object') {
date = date.replace(/-/g, "/")
}
let dd = new Date(date)
switch (str) {
case 'day':
dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
break;
case 'month':
dd.setMonth(dd.getMonth() + AddDayCount) // 获取AddDayCount天后的日期
break;
case 'year':
dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
break;
}
let y = dd.getFullYear()
let m = (dd.getMonth() + 1) < 10 ? '0' + (dd.getMonth() + 1) : (dd.getMonth() + 1) // 获取当前月份的日期不足10补0
let d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号不足10补0
this.setData({
currentMonth: m
})
return y + '-' + m + '-' + d
},
//日期转字符串
dateStr(calendar){
let year = calendar.getFullYear();
let month = calendar.getMonth()+1;
let date = calendar.getDate();
month = month < 10 ? "0" + month : month;
date = date < 10 ? "0" + date : date;
let datestr = year + "-" + month + "-" + date;
return datestr;
},
format(dd){
let y = dd.getFullYear()
let m = (dd.getMonth() + 1) < 10 ? '0' + (dd.getMonth() + 1) : (dd.getMonth() + 1)
let d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate()
return y + '-' + m + '-' + d
}
}
})

View File

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

View File

@ -0,0 +1,73 @@
<wxs module="filters">
var strcontains = function (str,c) {
if (str.indexOf(c) >= 0) {
return true
}else{
return false
}
}
var formatNumber = function(n) {
n = n.toString()
return n[1] ? n : '0' + n
}
module.exports = {
formatNumber: formatNumber,
strcontains: strcontains
}
</wxs>
<view class="header space-between" wx:if="{{showHeader}}">
<view class="iconfont" data-type="month" data-id="0" bindtap='dataBefor'>
<view class='left-color'></view>
</view>
<view class="btn flex-center" bindtap="dateSelection">
<view class="text">{{canlender.year}}年{{canlender.month}}月</view>
<view class=""></view>
</view>
<view class="iconfont" data-type="month" data-id="1" bindtap='dataBefor'>
<view class='right-color'></view>
</view>
</view>
<view wx:if='{{!calShow}}' class="{{isOpen?'':'calendar-box'}} {{dateShow?'active':''}}">
<view class="calendar-wrapper {{dateShow?'active':''}}">
<view class="calendar-panel" wx:if="{{!showHeader}}">
<view class="calendar-panel-box">
<view>{{canlender.year}}年</view>
<view>{{canlender.month}}月</view>
</view>
</view>
<view class="calendar-header {{mini?'mini':''}}">
<view>日</view>
<view>一</view>
<view>二</view>
<view>三</view>
<view>四</view>
<view>五</view>
<view>六</view>
</view>
<view class="calendar-body">
<block wx:for="{{canlender.weeks}}" wx:for-item="weeks" wx:for-index="week" wx:key="weeks">
<view class="calender-body-date-week">
<block wx:for="{{weeks}}" wx:for-item="day" wx:key="day">
<view wx:if="{{filters.strcontains(lockDay,canlender.year+'-'+filters.formatNumber(day.month) +'-'+filters.formatNumber(day.date))}}" class="date date-lock">
{{day.date}}
</view>
<view wx:else class="date {{mini?'mini':''}} {{canlender.month==day.month && ((nowMonth === day.month && day.date-nowDate >= 0) || (day.month - nowMonth > 0))? '' : 'placeholder'}} " data-week="{{week}}" data-index="{{index}}" data-ischeck="{{canlender.month==day.month && ((nowMonth === day.month && day.date-nowDate >= 0) || (day.month - nowMonth > 0))}}"
bindtap='selectDay'>
<block wx:if="{{day.date}}">
{{day.date}}
<view wx:if="{{(readonly && day.checked) || (day.checked && canlender.month != day.month)}}" class="data-circle {{mini?'mini':''}}">{{day.step}} </view>
<image src="/images/add_to_min_program_close.png" wx:else style="width:24rpx;height:24rpx;"></image>
</block>
</view>
</block>
</view>
</block>
</view>
</view>
</view>

View File

@ -0,0 +1,260 @@
/* pages/calendar/calendar.wxss */
cover-view{
line-height: initial;
}
.calendar-box {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100vh;
background: rgba(0, 0, 0, 0.5);
z-index: 999999;
padding-top: 100rpx;
box-sizing: border-box;
transition: all 0.3s;
opacity: 0;
}
.calendar-box.active {
opacity: 1;
}
.calendar-wrapper {
width: 100%;
border-top: 1px #f5f5f5 solid;
border-bottom: 1px #f5f5f5 solid;
box-sizing: border-box;
font-size: 26rpx;
background: #fff;
transition: all 0.3s;
transform: translateY(-100%);
}
.calendar-wrapper.active {
transform: translateY(0%);
}
.header {
display: flex;
justify-content: center;
align-items: center;
position: relative;
height: 100rpx;
background: #fff;
z-index: 10000;
}
.top-jiantou {
width: 100rpx;
height: 100rpx;
text-align: center;
box-sizing: border-box;
line-height: 100rpx;
}
.iconfont{
display: flex;
justify-content: center;
align-items: center;
position: relative;
color: #1AB16C;
width: 53rpx;
height: 53rpx;
}
.left-color,
.right-color
{
display: block;
width: 0;
height: 0;
border-top: 20rpx solid transparent;
border-bottom: 20rpx solid transparent;
}
.left-color{
border-right: 40rpx solid transparent;
border-right-color: #1AB16C;
}
.right-color{
border-left: 40rpx solid transparent;
border-left-color: #1AB16C;
}
.btn {
margin: 0 30rpx;
width: 240rpx;
height: 53rpx;
border: 1rpx solid #1AB16C;
border-radius: 26rpx;
color: #1AB16C;
font-size: 26rpx;
box-sizing: border-box;
}
.calendar-panel {
position: relative;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
height: 80rpx;
}
.backtoday {
position: absolute;
right: 0;
top: 15rpx;
padding: 0 10rpx;
padding-left: 20rpx;
height: 50rpx;
line-height: 50rpx;
border: 1px #1AB16C solid;
border-right: none;
font-size: 28rpx;
border-top-left-radius: 50rpx;
border-bottom-left-radius: 50rpx;
color: #1AB16C;
background: rgba(82, 184, 245, 0.1);
}
.checkalllbtn {
position: absolute;
left: 0;
top: 15rpx;
padding: 0 10rpx;
padding-right: 20rpx;
height: 50rpx;
line-height: 50rpx;
border: 1px #1AB16C solid;
border-left: none;
font-size: 28rpx;
border-top-right-radius: 50rpx;
border-bottom-right-radius: 50rpx;
color: #1AB16C;
background: rgba(82, 184, 245, 0.1);
}
.date-befor, .date-after {
/* border: 1px red solid; */
display: flex;
justify-content: center;
align-items: center;
height: 80rpx;
width: 80rpx;
text-align: center;
line-height: 80rpx;
/* margin-right: 20rpx; */
}
/* .date-after {
margin-left: 20rpx;
} */
.calendar-panel-box {
display: flex;
}
.calendar-header {
display: flex;
}
.calendar-header view,
.calendar-header cover-view {
width: 100%;
text-align: center;
line-height: 80rpx;
color: #1AB16C;
}
.calendar-header.mini view,
.calendar-header.mini cover-view
{
line-height: 50rpx;
}
.calendar-body {
display: flex;
flex-wrap: wrap;
}
.calender-body-date-week {
display: flex;
width: 100%;
border-bottom: 1px #f5f5f5 solid;
height:100rpx;
padding:10rpx 0;
}
.date {
width: 100%;
display: flex;
color: #1c1c1c;
background: #fff;
align-items: center;
flex-direction: column;
}
.date.mini{
line-height: 50rpx;
}
.date.active {
background: red;
}
.placeholder {
color: #1c1c1c;
}
.date-current {
background: #1AB16C;
color: #fff;
}
.date-lock {
color: #ff5a5f;
}
.data-circle {
color: #1AB16C;
font-weight: bold;
}
.data-circle.mini{
bottom: 2rpx;
}
.packup {
width: 100%;
height: 100rpx;
line-height: 100rpx;
text-align: center;
color: #1AB16C;
align-items: center;
}
.packup-button{
display: flex;
align-items: center;
justify-content: center;
width: 50%;
height: 55px;
line-height:55px!important;
}
.flex{
display: flex;
}
.flex-between{
justify-content: space-between;
}
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
.border-left{
border-left:1px solid #eee;
}
.cmf-btn{
background-color: #39b54a;
color: #fff;
border: 1rpx solid #39b54a;
}

View File

@ -0,0 +1,475 @@
// components/calendar/calendar.js
Component({
options: {
styleIsolation: 'apply-shared'
},
properties: {
date: {
type: null,
value: new Date()
},
/**
* 选中的日期
*/
selected: {
type: Array,
value: [],
observer(newVal, oldVal) {
this.getWeek(new Date())
}
},
/**
* 锁定日期
*/
lockDay: {
type: String,
},
/**
* 是否默认展开, 可以调用open事件展开组件
*/
isOpen: {
type: Boolean,
value: false
},
/**
* 是否多选日期
*/
multiple:{
type: Boolean,
value: true
},
/**
* 是否展示头部
*/
showHeader: {
type: Boolean,
value: false
},
/**
* 是否不能修改
*/
readonly: {
type: Boolean,
value: true
},
/**
* 使用mini样式
*/
mini:{
type: Boolean,
value: true
}
},
/**
* 组件的初始数据
*/
data: {
calShow: true, // 日历组件是否打开
dateShow: false, // 日期是否选择
selectDay: '', // 当前选择日期
selectDays: [], //多选日期
canlender: {
"weeks": []
},
currentMonth: null,
nowMonth: new Date().getMonth()+1,
nowDate: new Date().getDate()
},
ready() {
let sdays = this.data.selected;
sdays.forEach(v=>{
this.data.selectDays.push(v);
})
this.getWeek(new Date());
if (this.data.isOpen) {
this.setData({
calShow: false,
dateShow: true
})
}
},
/**
* 组件的方法列表
*/
methods: {
open: function(date) {
if(date && date!=''){
this.data.selectDays = []; //打开时候显示一个
this.data.selectDays.push(date);
this.setData({
selectDays: this.data.selectDays
})
this.getWeek(date);
}
this.dateSelection();
},
close: function() {
this.packup();
},
clear: function(){
this.setData({
selectDays: []
})
let m;
if(this.data.currentMonth == null){
m = this.data.canlender.month
}else{
m = this.data.currentMonth
}
let year = this.data.canlender.year + "-" + m + "-" + this.data.canlender.date
let _date = this.getDate(year, 0);
this.getWeek(_date);
this.triggerEvent("clear",{})
},
dateSelection() {
if (this.data.isOpen) {
return
}
let self = this;
if (self.data.calShow) {
self.setData({
calShow: false
}, () => {
setTimeout(() => {
self.setData({
dateShow: true
}, () => {
self.triggerEvent('show', {
ischeck: !self.data.calShow
})
})
}, 100)
})
} else {
self.setData({
dateShow: false
}, () => {
setTimeout(() => {
self.setData({
calShow: true
}, () => {
self.triggerEvent('show', {
ischeck: !self.data.calShow
})
})
}, 300)
})
}
},
//选择日期
selectDay(e) {
if(this.data.readonly){
return
}
let index = e.currentTarget.dataset.index;
let week = e.currentTarget.dataset.week;
let ischeck = e.currentTarget.dataset.ischeck;
let canlender = this.data.canlender;
if (!ischeck) return false;
let month = canlender.weeks[week][index].month < 10 ? "0" + canlender.weeks[week][index].month : canlender.weeks[week][index].month
let date = canlender.weeks[week][index].date < 10 ? "0" + canlender.weeks[week][index].date : canlender.weeks[week][index].date
let datestr = canlender.year + "-" + month + "-" + date;
let selectDays = this.data.selectDays;
if (selectDays.indexOf(datestr)==-1) {
if(this.data.multiple){
//多选
selectDays.push(datestr);
}else{
selectDays = []; //只保留最后选的
selectDays.push(datestr);
}
}else{
let index = selectDays.indexOf(datestr);
selectDays.splice(index, 1);
}
selectDays.sort(function(a, b){
let ds1 = a.split("-");
let ds2 = b.split("-");
let d1 = new Date(ds1[0], ds1[1], ds1[2]);
let d2 = new Date(ds2[0], ds2[1], ds2[2]);
return d1-d2;
});
this.data.selectDays = selectDays;
this.getWeek(datestr);
this.triggerEvent('getdate', {
datestr
})
},
//点击确定
packup() {
let self = this;
self.triggerEvent('select', {
selectDays: self.data.selectDays
})
if (this.data.isOpen) {
let year = self.data.canlender.year + "-" + self.data.canlender.month + "-" + self.data.canlender.date
let _date = self.getDate(year, 0);
self.getWeek(_date);
return
}
self.setData({
dateShow: false
}, () => {
setTimeout(() => {
self.setData({
calShow: true
}, () => {
let year = self.data.canlender.year + "-" + self.data.canlender.month + "-" + self.data.canlender.date
let _date = self.getDate(year, 0);
self.getWeek(_date);
})
}, 300)
})
},
/**
* 页面通过事件选中日期后调用refresh方法刷新日历组件
*/
refresh: function (num){
this.setData({
selectDays: num
})
let m;
if(this.data.currentMonth == null){
m = this.data.canlender.month
}else{
m = this.data.currentMonth
}
let year = this.data.canlender.year + "-" + m + "-" + this.data.canlender.date
let _date = this.getDate(year, 0);
this.getWeek(_date);
},
// 返回今天
backtoday() {
if(!this.data.readonly){
let datestr = this.dateStr(new Date());
this.data.selectDays.push(datestr);
this.setData({
selectDays: this.data.selectDays
});
this.triggerEvent('getdate', {
datestr
})
}
this.getWeek(new Date());
},
// 前一天|| 后一天
dataBefor(e) {
let num = 0;
let types = e.currentTarget.dataset.type;
if (e.currentTarget.dataset.id === "0") {
num = -1;
} else {
num = 1
}
let year = this.data.canlender.year + "-" + this.data.canlender.month + "-" + this.data.canlender.date
let _date = this.getDate(year, num, types === 'month' ? "month" : "day");
this.getWeek(_date);
},
// 获取日历内容
getWeek(dateData) {
let selected = this.data.selected
let selectDays = this.data.selectDays
// 判断当前是 安卓还是ios ,传入不容的日期格式
if (typeof dateData !== 'object') {
dateData = dateData.replace(/-/g, "/")
}
let _date = new Date(dateData);
let year = _date.getFullYear(); //年
let month = _date.getMonth() + 1; //月
let date = _date.getDate(); //日
let day = _date.getDay(); // 天
let canlender = [];
let dates = {
firstDay: new Date(year, month - 1, 1).getDay(),
lastMonthDays: [], // 上个月末尾几天
currentMonthDys: [], // 本月天数
nextMonthDays: [], // 下个月开始几天
endDay: new Date(year, month, 0).getDay(),
weeks: []
}
// 循环上个月末尾几天添加到数组
for (let i = dates.firstDay; i > 0; i--) {
let dd = new Date(year, month-1, -(i-1));
let checked = false;
selectDays.forEach(v => {
let selDate = v.date.split('-');
let ddstr = this.dateStr(dd);
if (ddstr == v) {
checked = true;
}
})
dates.lastMonthDays.push({
'date': '',
'month': '',
checked
})
}
// 循环本月天数添加到数组
for (let i = 1; i <= new Date(year, month, 0).getDate(); i++) {
let have = false;
let checked = false;
let step = '';
let city = '';
// for (let j = 0; j < selected.length; j++) {
// let selDate = selected[j].split('-');
// if (Number(year) === Number(selDate[0]) && Number(month) === Number(selDate[1]) && Number(i) === Number(selDate[2])) {
// have = true;
// }
// }
selectDays.forEach(v => {
let selDate = v.date.split('-');
if (Number(year) === Number(selDate[0]) && Number(month) === Number(selDate[1]) && Number(i) === Number(selDate[2])) {
checked = true;
step = v.step;
city = v.city_id;
}
})
dates.currentMonthDys.push({
'date': i + "",
'month': month,
// have,
checked,
step,
city
})
}
// 循环下个月开始几天 添加到数组
for (let i = 1; i < 7 - dates.endDay; i++) {
dates.nextMonthDays.push({
'date': '',
'month': ''
})
}
canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
// 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天
for (let i = 0; i < canlender.length; i++) {
if (i % 7 == 0) {
dates.weeks[parseInt(i / 7)] = new Array(7);
}
dates.weeks[parseInt(i / 7)][i % 7] = canlender[i]
}
// 渲染数据
this.setData({
selectDay: month + "月" + date + "日",
"canlender.weeks": dates.weeks,
'canlender.month': month,
'canlender.date': date,
"canlender.day": day,
'canlender.year': year,
})
month = month < 10 ? "0" + month : month
date = date < 10 ? "0" + date : date
// let localstr = year+"-"+month+"-"+date;
// this.triggerEvent('getdate', {
// year,
// month,
// date,
// localstr
// })
// console.log(dates.weeks);
},
checkall(){
let date = new Date();
let m, d, day, dayNum;
let days = [];
if(this.data.currentMonth == null){
m = parseInt(this.data.nowMonth)
}else{
m = parseInt(this.data.currentMonth)
}
switch (m) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
dayNum = 31;
break;
case 4:
case 6:
case 9:
case 11:
dayNum = 30;
break;
case 2:
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
dayNum = 29;
} else {
dayNum = 28;
}
break;
}
for (d = 1; d <= dayNum; d++) {
date.setMonth(m - 1, d);
day = date.getDay();
let str = this.format(date);
if(date.getMonth() == new Date().getMonth()){
if(date.getDate() - new Date().getDate() >= 0){
days.push(str);
}
}else if(date.getMonth() > new Date().getMonth()){
days.push(str);
}
}
this.refresh(days);
this.triggerEvent('checkall', {
days
})
},
/**
* 时间计算
*/
getDate(date, AddDayCount, str = 'day') {
if (typeof date !== 'object') {
date = date.replace(/-/g, "/")
}
let dd = new Date(date)
switch (str) {
case 'day':
dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
break;
case 'month':
dd.setMonth(dd.getMonth() + AddDayCount) // 获取AddDayCount天后的日期
break;
case 'year':
dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
break;
}
let y = dd.getFullYear()
let m = (dd.getMonth() + 1) < 10 ? '0' + (dd.getMonth() + 1) : (dd.getMonth() + 1) // 获取当前月份的日期不足10补0
let d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号不足10补0
this.setData({
currentMonth: m
})
return y + '-' + m + '-' + d
},
//日期转字符串
dateStr(calendar){
let year = calendar.getFullYear();
let month = calendar.getMonth()+1;
let date = calendar.getDate();
month = month < 10 ? "0" + month : month;
date = date < 10 ? "0" + date : date;
let datestr = year + "-" + month + "-" + date;
return datestr;
},
format(dd){
let y = dd.getFullYear()
let m = (dd.getMonth() + 1) < 10 ? '0' + (dd.getMonth() + 1) : (dd.getMonth() + 1)
let d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate()
return y + '-' + m + '-' + d
}
}
})

View File

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

View File

@ -0,0 +1,73 @@
<wxs module="filters">
var strcontains = function (str,c) {
if (str.indexOf(c) >= 0) {
return true
}else{
return false
}
}
var formatNumber = function(n) {
n = n.toString()
return n[1] ? n : '0' + n
}
module.exports = {
formatNumber: formatNumber,
strcontains: strcontains
}
</wxs>
<view class="header space-between" wx:if="{{showHeader}}">
<view class="iconfont" data-type="month" data-id="0" bindtap='dataBefor'>
<view class='left-color'></view>
</view>
<view class="btn flex-center" bindtap="dateSelection">
<view class="text">{{canlender.year}}年{{canlender.month}}月</view>
<view class=""></view>
</view>
<view class="iconfont" data-type="month" data-id="1" bindtap='dataBefor'>
<view class='right-color'></view>
</view>
</view>
<view wx:if='{{!calShow}}' class="{{isOpen?'':'calendar-box'}} {{dateShow?'active':''}}">
<view class="calendar-wrapper {{dateShow?'active':''}}">
<view class="calendar-panel" wx:if="{{!showHeader}}">
<view class="calendar-panel-box">
<view>{{canlender.year}}年</view>
<view>{{canlender.month}}月</view>
</view>
</view>
<view class="calendar-header {{mini?'mini':''}}">
<view>日</view>
<view>一</view>
<view>二</view>
<view>三</view>
<view>四</view>
<view>五</view>
<view>六</view>
</view>
<view class="calendar-body">
<block wx:for="{{canlender.weeks}}" wx:for-item="weeks" wx:for-index="week" wx:key="weeks">
<view class="calender-body-date-week">
<block wx:for="{{weeks}}" wx:for-item="day" wx:key="day">
<view wx:if="{{filters.strcontains(lockDay,canlender.year+'-'+filters.formatNumber(day.month) +'-'+filters.formatNumber(day.date))}}" class="date date-lock">
{{day.date}}
</view>
<view wx:else class="date {{mini?'mini':''}} {{canlender.month==day.month && ((nowMonth === day.month && day.date-nowDate >= 0) || (day.month - nowMonth > 0))? '' : 'placeholder'}} " data-week="{{week}}" data-index="{{index}}" data-ischeck="{{canlender.month==day.month && ((nowMonth === day.month && day.date-nowDate >= 0) || (day.month - nowMonth > 0))}}"
bindtap='selectDay'>
<block wx:if="{{day.date}}">
{{day.date}}
<view wx:if="{{(readonly && day.checked) || (day.checked && canlender.month != day.month)}}" class="data-circle {{mini?'mini':''}}"><view>{{day.step}}</view> <view>{{day.city}}</view></view>
<image src="/images/add_to_min_program_close.png" wx:else style="width:24rpx;height:24rpx;"></image>
</block>
</view>
</block>
</view>
</block>
</view>
</view>
</view>

View File

@ -0,0 +1,258 @@
/* pages/calendar/calendar.wxss */
cover-view{
line-height: initial;
}
.calendar-box {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100vh;
background: rgba(0, 0, 0, 0.5);
z-index: 999999;
padding-top: 100rpx;
box-sizing: border-box;
transition: all 0.3s;
opacity: 0;
}
.calendar-box.active {
opacity: 1;
}
.calendar-wrapper {
width: 100%;
border-top: 1px #f5f5f5 solid;
border-bottom: 1px #f5f5f5 solid;
box-sizing: border-box;
font-size: 26rpx;
background: #fff;
transition: all 0.3s;
transform: translateY(-100%);
}
.calendar-wrapper.active {
transform: translateY(0%);
}
.header {
display: flex;
justify-content: center;
align-items: center;
position: relative;
height: 100rpx;
background: #fff;
z-index: 10000;
padding: 20rpx;
}
.top-jiantou {
width: 100rpx;
height: 100rpx;
text-align: center;
box-sizing: border-box;
line-height: 100rpx;
}
.iconfont{
display: flex;
justify-content: center;
align-items: center;
position: relative;
color: #890001;
width: 53rpx;
height: 53rpx;
}
.left-color,
.right-color
{
display: block;
width: 0;
height: 0;
border-top: 20rpx solid transparent;
border-bottom: 20rpx solid transparent;
}
.left-color{
border-right: 40rpx solid transparent;
border-right-color: #890001;
}
.right-color{
border-left: 40rpx solid transparent;
border-left-color: #890001;
}
.btn {
margin: 0 30rpx;
width: 240rpx;
height: 53rpx;
border: 2rpx solid #890001;
border-radius: 26rpx;
color: #890001;
font-size: 26rpx;
box-sizing: border-box;
}
.calendar-panel {
position: relative;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
height: 80rpx;
}
.backtoday {
position: absolute;
right: 0;
top: 15rpx;
padding: 0 10rpx;
padding-left: 20rpx;
height: 50rpx;
line-height: 50rpx;
border: 1px #890001 solid;
border-right: none;
font-size: 28rpx;
border-top-left-radius: 50rpx;
border-bottom-left-radius: 50rpx;
color: #890001;
background: rgba(82, 184, 245, 0.1);
}
.checkalllbtn {
position: absolute;
left: 0;
top: 15rpx;
padding: 0 10rpx;
padding-right: 20rpx;
height: 50rpx;
line-height: 50rpx;
border: 1px #890001 solid;
border-left: none;
font-size: 28rpx;
border-top-right-radius: 50rpx;
border-bottom-right-radius: 50rpx;
color: #890001;
background: rgba(82, 184, 245, 0.1);
}
.date-befor, .date-after {
/* border: 1px red solid; */
display: flex;
justify-content: center;
align-items: center;
height: 80rpx;
width: 80rpx;
text-align: center;
line-height: 80rpx;
/* margin-right: 20rpx; */
}
/* .date-after {
margin-left: 20rpx;
} */
.calendar-panel-box {
display: flex;
}
.calendar-header {
display: flex;
}
.calendar-header view,
.calendar-header cover-view {
width: 100%;
text-align: center;
line-height: 80rpx;
color: #890001;
}
.calendar-header.mini view,
.calendar-header.mini cover-view
{
line-height: 50rpx;
}
.calendar-body {
display: flex;
flex-wrap: wrap;
}
.calender-body-date-week {
display: flex;
width: 100%;
border-bottom: 1px #f5f5f5 solid;
height:100rpx;
padding:10rpx 0;
}
.date {
width: 100%;
display: flex;
color: #1c1c1c;
background: #fff;
align-items: center;
flex-direction: column;
}
.date.mini{
height:120rpx;
}
.date.active {
background: red;
}
.placeholder {
color: #1c1c1c;
}
.date-current {
background: #890001;
color: #fff;
}
.date-lock {
color: #ff5a5f;
}
.data-circle {
color: #890001;
font-weight: bold;
}
.data-circle.mini{
bottom: 2rpx;
text-align: center;
}
.packup {
width: 100%;
height: 100rpx;
line-height: 100rpx;
text-align: center;
color: #890001;
align-items: center;
}
.packup-button{
display: flex;
align-items: center;
justify-content: center;
width: 50%;
height: 55px;
line-height:55px!important;
}
.flex{
display: flex;
}
.flex-between{
justify-content: space-between;
}
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
.border-left{
border-left:1px solid #eee;
}
.cmf-btn{
background-color: #39b54a;
color: #fff;
border: 1rpx solid #39b54a;
}

23
components/empty/index.js Normal file
View File

@ -0,0 +1,23 @@
// lionfish_comshop/components/empty/index.js
Component({
/**
* 组件的属性列表
*/
properties: {
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
}
})

View File

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

View File

@ -0,0 +1,4 @@
<view class="none-rush-list">
<image class="img-block" src="/images/icon-index-empty.png"></image>
<view class="h1"><slot></slot></view>
</view>

View File

@ -0,0 +1,21 @@
.none-rush-list {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-bottom: 140rpx;
padding-top: 140rpx;
}
.none-rush-list .img-block {
width: 240rpx;
height: 240rpx;
margin-bottom: 30rpx;
}
.none-rush-list .h1 {
font-size: 32rpx;
line-height: 32rpx;
color: #444;
margin-bottom: 20rpx;
}

View File

@ -0,0 +1,97 @@
const cfg = require('./config.js'),
isLetter = c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
function CssHandler(tagStyle) {
var styles = Object.assign(Object.create(null), cfg.userAgentStyles);
for (var item in tagStyle)
styles[item] = (styles[item] ? styles[item] + ';' : '') + tagStyle[item];
this.styles = styles;
}
CssHandler.prototype.getStyle = function (data) {
this.styles = new parser(data, this.styles).parse();
}
CssHandler.prototype.match = function (name, attrs) {
var tmp, matched = (tmp = this.styles[name]) ? tmp + ';' : '';
if (attrs.class) {
var items = attrs.class.split(' ');
for (var i = 0, item; item = items[i]; i++)
if (tmp = this.styles['.' + item])
matched += tmp + ';';
}
if (tmp = this.styles['#' + attrs.id])
matched += tmp + ';';
return matched;
}
module.exports = CssHandler;
function parser(data, init) {
this.data = data;
this.floor = 0;
this.i = 0;
this.list = [];
this.res = init;
this.state = this.Space;
}
parser.prototype.parse = function () {
for (var c; c = this.data[this.i]; this.i++)
this.state(c);
return this.res;
}
parser.prototype.section = function () {
return this.data.substring(this.start, this.i);
}
// 状态机
parser.prototype.Space = function (c) {
if (c == '.' || c == '#' || isLetter(c)) {
this.start = this.i;
this.state = this.Name;
} else if (c == '/' && this.data[this.i + 1] == '*')
this.Comment();
else if (!cfg.blankChar[c] && c != ';')
this.state = this.Ignore;
}
parser.prototype.Comment = function () {
this.i = this.data.indexOf('*/', this.i) + 1;
if (!this.i) this.i = this.data.length;
this.state = this.Space;
}
parser.prototype.Ignore = function (c) {
if (c == '{') this.floor++;
else if (c == '}' && !--this.floor) this.state = this.Space;
}
parser.prototype.Name = function (c) {
if (cfg.blankChar[c]) {
this.list.push(this.section());
this.state = this.NameSpace;
} else if (c == '{') {
this.list.push(this.section());
this.Content();
} else if (c == ',') {
this.list.push(this.section());
this.Comma();
} else if (!isLetter(c) && (c < '0' || c > '9') && c != '-' && c != '_')
this.state = this.Ignore;
}
parser.prototype.NameSpace = function (c) {
if (c == '{') this.Content();
else if (c == ',') this.Comma();
else if (!cfg.blankChar[c]) this.state = this.Ignore;
}
parser.prototype.Comma = function () {
while (cfg.blankChar[this.data[++this.i]]);
if (this.data[this.i] == '{') this.Content();
else {
this.start = this.i--;
this.state = this.Name;
}
}
parser.prototype.Content = function () {
this.start = ++this.i;
if ((this.i = this.data.indexOf('}', this.i)) == -1) this.i = this.data.length;
var content = this.section();
for (var i = 0, item; item = this.list[i++];)
if (this.res[item]) this.res[item] += ';' + content;
else this.res[item] = content;
this.list = [];
this.state = this.Space;
}

View File

@ -0,0 +1,526 @@
/**
* html 解析器
* @tutorial https://github.com/jin-yufeng/Parser
* @version 20200630
* @author JinYufeng
* @listens MIT
*/
const cfg = require('./config.js'),
blankChar = cfg.blankChar,
CssHandler = require('./CssHandler.js'),
windowWidth = wx.getSystemInfoSync().windowWidth;
var emoji;
function MpHtmlParser(data, options = {}) {
this.attrs = {};
this.CssHandler = new CssHandler(options.tagStyle, windowWidth);
this.data = data;
this.domain = options.domain;
this.DOM = [];
this.i = this.start = this.audioNum = this.imgNum = this.videoNum = 0;
options.prot = (this.domain || '').includes('://') ? this.domain.split('://')[0] : 'http';
this.options = options;
this.state = this.Text;
this.STACK = [];
// 工具函数
this.bubble = () => {
for (var i = this.STACK.length, item; item = this.STACK[--i];) {
if (cfg.richOnlyTags[item.name]) {
if (item.name == 'table' && !Object.hasOwnProperty.call(item, 'c')) item.c = 1;
return false;
}
item.c = 1;
}
return true;
}
this.decode = (val, amp) => {
var i = -1,
j, en;
while (1) {
if ((i = val.indexOf('&', i + 1)) == -1) break;
if ((j = val.indexOf(';', i + 2)) == -1) break;
if (val[i + 1] == '#') {
en = parseInt((val[i + 2] == 'x' ? '0' : '') + val.substring(i + 2, j));
if (!isNaN(en)) val = val.substr(0, i) + String.fromCharCode(en) + val.substr(j + 1);
} else {
en = val.substring(i + 1, j);
if (cfg.entities[en] || en == amp)
val = val.substr(0, i) + (cfg.entities[en] || '&') + val.substr(j + 1);
}
}
return val;
}
this.getUrl = url => {
if (url[0] == '/') {
if (url[1] == '/') url = this.options.prot + ':' + url;
else if (this.domain) url = this.domain + url;
} else if (this.domain && url.indexOf('data:') != 0 && !url.includes('://'))
url = this.domain + '/' + url;
return url;
}
this.isClose = () => this.data[this.i] == '>' || (this.data[this.i] == '/' && this.data[this.i + 1] == '>');
this.section = () => this.data.substring(this.start, this.i);
this.parent = () => this.STACK[this.STACK.length - 1];
this.siblings = () => this.STACK.length ? this.parent().children : this.DOM;
}
MpHtmlParser.prototype.parse = function () {
if (emoji) this.data = emoji.parseEmoji(this.data);
for (var c; c = this.data[this.i]; this.i++)
this.state(c);
if (this.state == this.Text) this.setText();
while (this.STACK.length) this.popNode(this.STACK.pop());
return this.DOM;
}
// 设置属性
MpHtmlParser.prototype.setAttr = function () {
var name = this.attrName.toLowerCase(),
val = this.attrVal;
if (cfg.boolAttrs[name]) this.attrs[name] = 'T';
else if (val) {
if (name == 'src' || (name == 'data-src' && !this.attrs.src)) this.attrs.src = this.getUrl(this.decode(val, 'amp'));
else if (name == 'href' || name == 'style') this.attrs[name] = this.decode(val, 'amp');
else if (name.substr(0, 5) != 'data-') this.attrs[name] = val;
}
this.attrVal = '';
while (blankChar[this.data[this.i]]) this.i++;
if (this.isClose()) this.setNode();
else {
this.start = this.i;
this.state = this.AttrName;
}
}
// 设置文本节点
MpHtmlParser.prototype.setText = function () {
var back, text = this.section();
if (!text) return;
text = (cfg.onText && cfg.onText(text, () => back = true)) || text;
if (back) {
this.data = this.data.substr(0, this.start) + text + this.data.substr(this.i);
let j = this.start + text.length;
for (this.i = this.start; this.i < j; this.i++) this.state(this.data[this.i]);
return;
}
if (!this.pre) {
// 合并空白符
var tmp = [];
for (let i = text.length, c; c = text[--i];)
if (!blankChar[c] || (!blankChar[tmp[0]] && (c = ' '))) tmp.unshift(c);
text = tmp.join('');
}
this.siblings().push({
type: 'text',
text: this.decode(text)
});
}
// 设置元素节点
MpHtmlParser.prototype.setNode = function () {
var node = {
name: this.tagName.toLowerCase(),
attrs: this.attrs
},
close = cfg.selfClosingTags[node.name];
this.attrs = {};
if (!cfg.ignoreTags[node.name]) {
// 处理属性
var attrs = node.attrs,
style = this.CssHandler.match(node.name, attrs, node) + (attrs.style || ''),
styleObj = {};
if (attrs.id) {
if (this.options.compress & 1) attrs.id = void 0;
else if (this.options.useAnchor) this.bubble();
}
if ((this.options.compress & 2) && attrs.class) attrs.class = void 0;
switch (node.name) {
case 'a':
case 'ad':
this.bubble();
break;
case 'font':
if (attrs.color) {
styleObj['color'] = attrs.color;
attrs.color = void 0;
}
if (attrs.face) {
styleObj['font-family'] = attrs.face;
attrs.face = void 0;
}
if (attrs.size) {
var size = parseInt(attrs.size);
if (size < 1) size = 1;
else if (size > 7) size = 7;
var map = ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'];
styleObj['font-size'] = map[size - 1];
attrs.size = void 0;
}
break;
case 'embed':
var src = node.attrs.src || '',
type = node.attrs.type || '';
if (type.includes('video') || src.includes('.mp4') || src.includes('.3gp') || src.includes('.m3u8'))
node.name = 'video';
else if (type.includes('audio') || src.includes('.m4a') || src.includes('.wav') || src.includes('.mp3') || src.includes('.aac'))
node.name = 'audio';
else break;
if (node.attrs.autostart)
node.attrs.autoplay = 'T';
node.attrs.controls = 'T';
// falls through
case 'video':
case 'audio':
if (!attrs.id) attrs.id = node.name + (++this[`${node.name}Num`]);
else this[`${node.name}Num`]++;
if (node.name == 'video') {
if (this.videoNum > 3)
node.lazyLoad = 1;
if (attrs.width) {
styleObj.width = parseFloat(attrs.width) + (attrs.width.includes('%') ? '%' : 'px');
attrs.width = void 0;
}
if (attrs.height) {
styleObj.height = parseFloat(attrs.height) + (attrs.height.includes('%') ? '%' : 'px');
attrs.height = void 0;
}
}
attrs.source = [];
if (attrs.src) {
attrs.source.push(attrs.src);
attrs.src = void 0;
}
this.bubble();
break;
case 'td':
case 'th':
if (attrs.colspan || attrs.rowspan)
for (var k = this.STACK.length, item; item = this.STACK[--k];)
if (item.name == 'table') {
item.c = void 0;
break;
}
}
if (attrs.align) {
styleObj['text-align'] = attrs.align;
attrs.align = void 0;
}
// 压缩 style
var styles = style.split(';');
style = '';
for (var i = 0, len = styles.length; i < len; i++) {
var info = styles[i].split(':');
if (info.length < 2) continue;
let key = info[0].trim().toLowerCase(),
value = info.slice(1).join(':').trim();
if (value.includes('-webkit') || value.includes('-moz') || value.includes('-ms') || value.includes('-o') || value.includes('safe'))
style += `;${key}:${value}`;
else if (!styleObj[key] || value.includes('import') || !styleObj[key].includes('import'))
styleObj[key] = value;
}
if (node.name == 'img') {
if (attrs.src && !attrs.ignore) {
if (this.bubble())
attrs.i = (this.imgNum++).toString();
else attrs.ignore = 'T';
}
if (attrs.ignore) {
style += ';-webkit-touch-callout:none';
styleObj['max-width'] = '100%';
}
if (!styleObj.position)
styleObj.top = styleObj.bottom = styleObj.left = styleObj.right = styleObj['z-index'] = void 0;
var width;
if (styleObj.width) width = styleObj.width;
else if (attrs.width) width = attrs.width.includes('%') ? attrs.width : attrs.width + 'px';
if (width) {
styleObj.width = width;
attrs.width = '100%';
if (parseInt(width) > windowWidth) {
styleObj.height = '';
if (attrs.height) attrs.height = void 0;
}
}
if (styleObj.height) {
attrs.height = styleObj.height;
styleObj.height = '';
} else if (attrs.height && !attrs.height.includes('%'))
attrs.height += 'px';
}
for (var key in styleObj) {
var value = styleObj[key];
if (!value) continue;
if (key.includes('flex') || key == 'order' || key == 'self-align') node.c = 1;
// 填充链接
if (value.includes('url')) {
var j = value.indexOf('(');
if (j++ != -1) {
while (value[j] == '"' || value[j] == "'" || blankChar[value[j]]) j++;
value = value.substr(0, j) + this.getUrl(value.substr(j));
}
}
// 转换 rpx
else if (value.includes('rpx'))
value = value.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * windowWidth / 750 + 'px');
else if (key == 'white-space' && value.includes('pre') && !close)
this.pre = node.pre = true;
style += `;${key}:${value}`;
}
style = style.substr(1);
if (style) attrs.style = style;
if (!close) {
node.children = [];
if (node.name == 'pre' && cfg.highlight) {
this.remove(node);
this.pre = node.pre = true;
}
this.siblings().push(node);
this.STACK.push(node);
} else if (!cfg.filter || cfg.filter(node, this) != false)
this.siblings().push(node);
} else {
if (!close) this.remove(node);
else if (node.name == 'source') {
var parent = this.parent();
if (parent && (parent.name == 'video' || parent.name == 'audio') && node.attrs.src)
parent.attrs.source.push(node.attrs.src);
} else if (node.name == 'base' && !this.domain) this.domain = node.attrs.href;
}
if (this.data[this.i] == '/') this.i++;
this.start = this.i + 1;
this.state = this.Text;
}
// 移除标签
MpHtmlParser.prototype.remove = function (node) {
var name = node.name,
j = this.i;
// 处理 svg
var handleSvg = () => {
var src = this.data.substring(j, this.i + 1);
if (!node.attrs.xmlns) src = ' xmlns="http://www.w3.org/2000/svg"' + src;
var i = j;
while (this.data[j] != '<') j--;
src = this.data.substring(j, i) + src;
var parent = this.parent();
if (node.attrs.width == '100%' && parent && (parent.attrs.style || '').includes('inline'))
parent.attrs.style = 'width:300px;max-width:100%;' + parent.attrs.style;
this.siblings().push({
name: 'img',
attrs: {
src: 'data:image/svg+xml;utf8,' + src.replace(/#/g, '%23'),
style: (/vertical[^;]+/.exec(node.attrs.style) || []).shift(),
ignore: 'T'
}
})
}
if (node.name == 'svg' && this.data[j] == '/') return handleSvg(this.i++);
while (1) {
if ((this.i = this.data.indexOf('</', this.i + 1)) == -1) {
if (name == 'pre' || name == 'svg') this.i = j;
else this.i = this.data.length;
return;
}
this.start = (this.i += 2);
while (!blankChar[this.data[this.i]] && !this.isClose()) this.i++;
if (this.section().toLowerCase() == name) {
// 代码块高亮
if (name == 'pre') {
this.data = this.data.substr(0, j + 1) + cfg.highlight(this.data.substring(j + 1, this.i - 5), node.attrs) + this.data.substr(this.i - 5);
return this.i = j;
} else if (name == 'style')
this.CssHandler.getStyle(this.data.substring(j + 1, this.i - 7));
else if (name == 'title')
this.DOM.title = this.data.substring(j + 1, this.i - 7);
if ((this.i = this.data.indexOf('>', this.i)) == -1) this.i = this.data.length;
if (name == 'svg') handleSvg();
return;
}
}
}
// 节点出栈处理
MpHtmlParser.prototype.popNode = function (node) {
// 空白符处理
if (node.pre) {
node.pre = this.pre = void 0;
for (let i = this.STACK.length; i--;)
if (this.STACK[i].pre)
this.pre = true;
}
var siblings = this.siblings(),
len = siblings.length,
childs = node.children;
if (node.name == 'head' || (cfg.filter && cfg.filter(node, this) == false))
return siblings.pop();
var attrs = node.attrs;
// 替换一些标签名
if (cfg.blockTags[node.name]) node.name = 'div';
else if (!cfg.trustTags[node.name]) node.name = 'span';
// 去除块标签前后空串
if (node.name == 'div' || node.name == 'p' || node.name[0] == 't') {
if (len > 1 && siblings[len - 2].text == ' ')
siblings.splice(--len - 1, 1);
if (childs.length && childs[childs.length - 1].text == ' ')
childs.pop();
}
// 处理列表
if (node.c && (node.name == 'ul' || node.name == 'ol')) {
if ((node.attrs.style || '').includes('list-style:none')) {
for (let i = 0, child; child = childs[i++];)
if (child.name == 'li')
child.name = 'div';
} else if (node.name == 'ul') {
var floor = 1;
for (let i = this.STACK.length; i--;)
if (this.STACK[i].name == 'ul') floor++;
if (floor != 1)
for (let i = childs.length; i--;)
childs[i].floor = floor;
} else {
for (let i = 0, num = 1, child; child = childs[i++];)
if (child.name == 'li') {
child.type = 'ol';
child.num = ((num, type) => {
if (type == 'a') return String.fromCharCode(97 + (num - 1) % 26);
if (type == 'A') return String.fromCharCode(65 + (num - 1) % 26);
if (type == 'i' || type == 'I') {
num = (num - 1) % 99 + 1;
var one = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'],
ten = ['X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'],
res = (ten[Math.floor(num / 10) - 1] || '') + (one[num % 10 - 1] || '');
if (type == 'i') return res.toLowerCase();
return res;
}
return num;
})(num++, attrs.type) + '.';
}
}
}
// 处理表格的边框
if (node.name == 'table') {
var padding = attrs.cellpadding,
spacing = attrs.cellspacing,
border = attrs.border;
if (node.c) {
this.bubble();
attrs.style = (attrs.style || '') + ';display:table';
if (!padding) padding = 2;
if (!spacing) spacing = 2;
}
if (border) attrs.style = `border:${border}px solid gray;${attrs.style || ''}`;
if (spacing) attrs.style = `border-spacing:${spacing}px;${attrs.style || ''}`;
if (border || padding || node.c)
(function f(ns) {
for (var i = 0, n; n = ns[i]; i++) {
if (n.type == 'text') continue;
var style = n.attrs.style || '';
if (node.c && n.name[0] == 't') {
n.c = 1;
style += ';display:table-' + (n.name == 'th' || n.name == 'td' ? 'cell' : (n.name == 'tr' ? 'row' : 'row-group'));
}
if (n.name == 'th' || n.name == 'td') {
if (border) style = `border:${border}px solid gray;${style}`;
if (padding) style = `padding:${padding}px;${style}`;
} else f(n.children || []);
if (style) n.attrs.style = style;
}
})(childs)
if (this.options.autoscroll) {
var table = Object.assign({}, node);
node.name = 'div';
node.attrs = {
style: 'overflow:scroll'
}
node.children = [table];
}
}
this.CssHandler.pop && this.CssHandler.pop(node);
// 自动压缩
if (node.name == 'div' && !Object.keys(attrs).length && childs.length == 1 && childs[0].name == 'div')
siblings[len - 1] = childs[0];
}
// 状态机
MpHtmlParser.prototype.Text = function (c) {
if (c == '<') {
var next = this.data[this.i + 1],
isLetter = c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
if (isLetter(next)) {
this.setText();
this.start = this.i + 1;
this.state = this.TagName;
} else if (next == '/') {
this.setText();
if (isLetter(this.data[++this.i + 1])) {
this.start = this.i + 1;
this.state = this.EndTag;
} else this.Comment();
} else if (next == '!') {
this.setText();
this.Comment();
}
}
}
MpHtmlParser.prototype.Comment = function () {
var key;
if (this.data.substring(this.i + 2, this.i + 4) == '--') key = '-->';
else if (this.data.substring(this.i + 2, this.i + 9) == '[CDATA[') key = ']]>';
else key = '>';
if ((this.i = this.data.indexOf(key, this.i + 2)) == -1) this.i = this.data.length;
else this.i += key.length - 1;
this.start = this.i + 1;
this.state = this.Text;
}
MpHtmlParser.prototype.TagName = function (c) {
if (blankChar[c]) {
this.tagName = this.section();
while (blankChar[this.data[this.i]]) this.i++;
if (this.isClose()) this.setNode();
else {
this.start = this.i;
this.state = this.AttrName;
}
} else if (this.isClose()) {
this.tagName = this.section();
this.setNode();
}
}
MpHtmlParser.prototype.AttrName = function (c) {
if (c == '=' || blankChar[c] || this.isClose()) {
this.attrName = this.section();
if (blankChar[c])
while (blankChar[this.data[++this.i]]);
if (this.data[this.i] == '=') {
while (blankChar[this.data[++this.i]]);
this.start = this.i--;
this.state = this.AttrValue;
} else this.setAttr();
}
}
MpHtmlParser.prototype.AttrValue = function (c) {
if (c == '"' || c == "'") {
this.start++;
if ((this.i = this.data.indexOf(c, this.i + 1)) == -1) return this.i = this.data.length;
this.attrVal = this.section();
this.i++;
} else {
for (; !blankChar[this.data[this.i]] && !this.isClose(); this.i++);
this.attrVal = this.section();
}
this.setAttr();
}
MpHtmlParser.prototype.EndTag = function (c) {
if (blankChar[c] || c == '>' || c == '/') {
var name = this.section().toLowerCase();
for (var i = this.STACK.length; i--;)
if (this.STACK[i].name == name) break;
if (i != -1) {
var node;
while ((node = this.STACK.pop()).name != name) this.popNode(node);
this.popNode(node);
} else if (name == 'p' || name == 'br')
this.siblings().push({
name,
attrs: {}
});
this.i = this.data.indexOf('>', this.i);
this.start = this.i + 1;
if (this.i == -1) this.i = this.data.length;
else this.state = this.Text;
}
}
module.exports = MpHtmlParser;

View File

@ -0,0 +1,63 @@
/* 配置文件 */
const canIUse = wx.canIUse('editor'); // 高基础库标识,用于兼容
module.exports = {
// 出错占位图
errorImg: null,
// 过滤器函数
filter: null,
// 代码高亮函数
highlight: null,
// 文本处理函数
onText: null,
// 实体编码列表
entities: {
quot: '"',
apos: "'",
semi: ';',
nbsp: '\xA0',
ndash: '',
mdash: '—',
middot: '·',
lsquo: '',
rsquo: '',
ldquo: '“',
rdquo: '”',
bull: '•',
hellip: '…'
},
blankChar: makeMap(' ,\xA0,\t,\r,\n,\f'),
boolAttrs: makeMap('autoplay,autostart,controls,ignore,loop,muted'),
// 块级标签,将被转为 div
blockTags: makeMap('address,article,aside,body,caption,center,cite,footer,header,html,nav,section' + (canIUse ? '' : ',pre')),
// 将被移除的标签
ignoreTags: makeMap('area,base,canvas,frame,iframe,input,link,map,meta,param,script,source,style,svg,textarea,title,track,wbr' + (canIUse ? ',rp' : '')),
// 只能被 rich-text 显示的标签
richOnlyTags: makeMap('a,colgroup,fieldset,legend,table' + (canIUse ? ',bdi,bdo,rt,ruby' : '')),
// 自闭合的标签
selfClosingTags: makeMap('area,base,br,col,circle,ellipse,embed,frame,hr,img,input,line,link,meta,param,path,polygon,rect,source,track,use,wbr'),
// 信任的标签
trustTags: makeMap('a,abbr,ad,audio,b,blockquote,br,code,col,colgroup,dd,del,dl,dt,div,em,fieldset,h1,h2,h3,h4,h5,h6,hr,i,img,ins,label,legend,li,ol,p,q,source,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,title,ul,video' + (canIUse ? ',bdi,bdo,caption,pre,rt,ruby' : '')),
// 默认的标签样式
userAgentStyles: {
address: 'font-style:italic',
big: 'display:inline;font-size:1.2em',
blockquote: 'background-color:#f6f6f6;border-left:3px solid #dbdbdb;color:#6c6c6c;padding:5px 0 5px 10px',
caption: 'display:table-caption;text-align:center',
center: 'text-align:center',
cite: 'font-style:italic',
dd: 'margin-left:40px',
mark: 'background-color:yellow',
pre: 'font-family:monospace;white-space:pre;overflow:scroll',
s: 'text-decoration:line-through',
small: 'display:inline;font-size:0.8em',
u: 'text-decoration:underline'
}
}
function makeMap(str) {
var map = Object.create(null),
list = str.split(',');
for (var i = list.length; i--;)
map[list[i]] = true;
return map;
}

204
components/parser/parser.js Normal file
View File

@ -0,0 +1,204 @@
/**
* Parser 富文本组件
* @tutorial https://github.com/jin-yufeng/Parser
* @version 20200630
* @author JinYufeng
* @listens MIT
*/
var cache = {},
Parser = require('./libs/MpHtmlParser.js'),
fs = wx.getFileSystemManager && wx.getFileSystemManager();
var dom;
// 计算 cache 的 key
function hash(str) {
for (var i = str.length, val = 5381; i--;)
val += (val << 5) + str.charCodeAt(i);
return val;
}
Component({
options: {
pureDataPattern: /^[acdgtu]|W/
},
data: {
nodes: []
},
properties: {
html: {
type: String,
observer(html) {
this.setContent(html);
}
},
autopause: {
type: Boolean,
value: true
},
autoscroll: Boolean,
autosetTitle: {
type: Boolean,
value: true
},
compress: Number,
domain: String,
lazyLoad: Boolean,
loadingImg: String,
selectable: Boolean,
tagStyle: Object,
showWithAnimation: Boolean,
useAnchor: Boolean,
useCache: Boolean
},
relations: {
'../parser-group/parser-group': {
type: 'ancestor'
}
},
created() {
// 图片数组
this.imgList = [];
this.imgList.setItem = function(i, src) {
if (!i || !src) return;
// 去重
if (src.indexOf('http') == 0 && this.includes(src)) {
var newSrc = '';
for (var j = 0, c; c = src[j]; j++) {
if (c == '/' && src[j - 1] != '/' && src[j + 1] != '/') break;
newSrc += Math.random() > 0.5 ? c.toUpperCase() : c;
}
newSrc += src.substr(j);
return this[i] = newSrc;
}
this[i] = src;
// 暂存 data src
if (src.includes('data:image')) {
var info = src.match(/data:image\/(\S+?);(\S+?),(.+)/);
if (!info) return;
var filePath = `${wx.env.USER_DATA_PATH}/${Date.now()}.${info[1]}`;
fs && fs.writeFile({
filePath,
data: info[3],
encoding: info[2],
success: () => this[i] = filePath
})
}
}
this.imgList.each = function(f) {
for (var i = 0, len = this.length; i < len; i++)
this.setItem(i, f(this[i], i, this));
}
if (dom) this.document = new dom(this);
},
detached() {
// 删除暂存
this.imgList.each(src => {
if (src && src.includes(wx.env.USER_DATA_PATH) && fs)
fs.unlink({
filePath: src
})
})
clearInterval(this._timer);
},
methods: {
// 锚点跳转
navigateTo(obj) {
if (!this.data.useAnchor)
return obj.fail && obj.fail({
errMsg: 'Anchor is disabled'
})
this.createSelectorQuery()
.select('.top' + (obj.id ? '>>>#' + obj.id : '')).boundingClientRect()
.selectViewport().scrollOffset().exec(res => {
if (!res[0])
return this.group ? this.group.navigateTo(this.i, obj) :
obj.fail && obj.fail({
errMsg: 'Label not found'
});
obj.scrollTop = res[1].scrollTop + res[0].top + (obj.offset || 0);
wx.pageScrollTo(obj);
})
},
// 获取文本
getText(ns = this.data.html) {
var txt = '';
for (var i = 0, n; n = ns[i++];) {
if (n.type == 'text') txt += n.text.replace(/&nbsp;/g, '\u00A0').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');
else if (n.type == 'br') txt += '\n';
else {
// 块级标签前后加换行
var br = n.name == 'p' || n.name == 'div' || n.name == 'tr' || n.name == 'li' || (n.name[0] == 'h' && n.name[1] > '0' && n.name[1] < '7');
if (br && txt && txt[txt.length - 1] != '\n') txt += '\n';
if (n.children) txt += this.getText(n.children);
if (br && txt[txt.length - 1] != '\n') txt += '\n';
else if (n.name == 'td' || n.name == 'th') txt += '\t';
}
}
return txt;
},
// 获取视频 context
getVideoContext(id) {
if (!id) return this.videoContexts;
for (var i = this.videoContexts.length; i--;)
if (this.videoContexts[i].id == id) return this.videoContexts[i];
},
// 渲染富文本
setContent(html, append) {
var nodes, parser = new Parser(html, this.data);
// 缓存读取
if (this.data.useCache) {
var hashVal = hash(html);
if (cache[hashVal]) nodes = cache[hashVal];
else cache[hashVal] = nodes = parser.parse();
} else nodes = parser.parse();
this.triggerEvent('parse', nodes);
var data = {};
if (append)
for (let i = this.data.nodes.length, j = nodes.length; j--;)
data[`nodes[${i + j}]`] = nodes[j];
else data.nodes = nodes;
if (this.showWithAnimation) data.showAm = 'animation: show .5s';
this.setData(data, () => {
this.triggerEvent('load')
});
// 设置标题
if (nodes.title && this.data.autosetTitle)
wx.setNavigationBarTitle({
title: nodes.title
})
this.imgList.length = 0;
this.videoContexts = [];
var ns = this.selectAllComponents('.top,.top>>>._node');
for (let i = 0, n; n = ns[i++];) {
n.top = this;
for (let j = 0, item; item = n.data.nodes[j++];) {
if (item.c) continue;
// 获取图片列表
if (item.name == 'img')
this.imgList.setItem(item.attrs.i, item.attrs.src);
// 音视频控制
else if (item.name == 'video' || item.name == 'audio') {
var ctx;
if (item.name == 'video') ctx = wx.createVideoContext(item.attrs.id, n);
else ctx = n.selectComponent('#' + item.attrs.id);
if (ctx) {
ctx.id = item.attrs.id;
this.videoContexts.push(ctx);
}
}
}
}
var height;
clearInterval(this._timer);
this._timer = setInterval(() => {
this.createSelectorQuery().select('.top').boundingClientRect(res => {
if (!res) return;
this.rect = res;
if (res.height == height) {
this.triggerEvent('ready', res)
clearInterval(this._timer);
}
height = res.height;
}).exec();
}, 350)
}
}
})

View File

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"trees": "./trees/trees"
}
}

View File

@ -0,0 +1,3 @@
<!--parser 主组件-->
<slot wx:if="{{!nodes.length}}" />
<trees class="top" style="{{selectable?'user-select:text;-webkit-user-select:text;':''}}{{showAm}}" lazy-load="{{lazyLoad}}" loading="{{loadingImg}}" nodes="{{nodes}}" />

View File

@ -0,0 +1,19 @@
:host {
display: block;
overflow: scroll;
-webkit-overflow-scrolling: touch;
}
.top {
display: inherit;
}
@keyframes show {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}

View File

@ -0,0 +1,42 @@
var inlineTags = {
abbr: 1,
b: 1,
big: 1,
code: 1,
del: 1,
em: 1,
i: 1,
ins: 1,
label: 1,
q: 1,
small: 1,
span: 1,
strong: 1
}
module.exports = {
// 从 rich-text 顶层标签的样式中取出一些给 rich-text
getStyle: function(style, display) {
if (style) {
var i, j, res = "";
if ((i = style.indexOf("display")) != -1)
res = style.substring(i, (j = style.indexOf(';', i)) == -1 ? style.length : j);
else res = "display:" + display;
if (style.indexOf("flex") != -1) res += ';' + style.match(getRegExp("flex[:-][^;]+/g")).join(';');
return res;
} else return "display:" + display;
},
// 处理懒加载
getNode: function(item, imgLoad) {
if (!imgLoad) {
delete item.attrs.src;
item.attrs.style += ";width:20px !important;height:20px !important";
}
return [item];
},
// 是否通过 rich-text 显示
useRichText: function(item) {
// rich-text 不支持 inline
if (item.c || inlineTags[item.name] || (item.attrs.style && item.attrs.style.indexOf("display:inline") != -1)) return false;
return true;
}
}

View File

@ -0,0 +1,122 @@
const errorImg = require('../libs/config.js').errorImg;
Component({
data: {
canIUse: !!wx.chooseMessageFile,
placeholder: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='300' height='225'/>"
},
properties: {
nodes: Array,
lazyLoad: Boolean,
loading: String
},
methods: {
// 视频播放事件
play(e) {
this.top.group && this.top.group.pause(this.top.i);
if (this.top.videoContexts.length > 1 && this.top.data.autopause)
for (var i = this.top.videoContexts.length; i--;)
if (this.top.videoContexts[i].id != e.currentTarget.id)
this.top.videoContexts[i].pause();
},
// 图片事件
imgtap(e) {
var attrs = e.currentTarget.dataset.attrs;
if (!attrs.ignore) {
var preview = true;
this.top.triggerEvent('imgtap', {
id: e.currentTarget.id,
src: attrs.src,
ignore: () => preview = false
})
if (preview) {
if (this.top.group) return this.top.group.preview(this.top.i, attrs.i);
var urls = this.top.imgList,
current = urls[attrs.i] ? urls[attrs.i] : (urls = [attrs.src], attrs.src);
wx.previewImage({
current,
urls
})
}
}
},
loadImg(e) {
var i = e.target.dataset.i;
if (this.data.lazyLoad && !this.data.nodes[i].load)
this.setData({
[`nodes[${i}].load`]: 1
})
else if (this.data.loading && this.data.nodes[i].load != 2)
this.setData({
[`nodes[${i}].load`]: 2
})
},
// 链接点击事件
linkpress(e) {
var jump = true,
attrs = e.currentTarget.dataset.attrs;
attrs.ignore = () => jump = false;
this.top.triggerEvent('linkpress', attrs);
if (jump) {
if (attrs['app-id'])
wx.navigateToMiniProgram({
appId: attrs['app-id'],
path: attrs.path
})
else if (attrs.href) {
if (attrs.href[0] == '#')
this.top.navigateTo({
id: attrs.href.substring(1)
})
else if (attrs.href.indexOf('http') == 0 || attrs.href.indexOf('//') == 0)
wx.setClipboardData({
data: attrs.href,
success: () =>
wx.showToast({
title: '链接已复制'
})
})
else
wx.navigateTo({
url: attrs.href,
fail() {
wx.switchTab({
url: attrs.href,
})
}
})
}
}
},
// 错误事件
error(e) {
var source = e.target.dataset.source,
i = e.target.dataset.i,
node = this.data.nodes[i];
if (source == 'video' || source == 'audio') {
// 加载其他 source
var index = (node.i || 0) + 1;
if (index < node.attrs.source.length)
return this.setData({
[`nodes[${i}].i`]: index
})
} else if (source == 'img' && errorImg) {
this.top.imgList.setItem(e.target.dataset.index, errorImg);
this.setData({
[`nodes[${i}].attrs.src`]: errorImg
})
}
this.top && this.top.triggerEvent('error', {
source,
target: e.target,
errMsg: e.detail.errMsg
})
},
// 加载视频
loadVideo(e) {
var i = e.target.dataset.i;
this.setData({
[`nodes[${i}].attrs.autoplay`]: true
})
}
}
})

View File

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"trees": "./trees"
}
}

View File

@ -0,0 +1,65 @@
<!--trees 递归子组件-->
<wxs module="handler">
var inline = {
abbr: 1,
b: 1,
big: 1,
code: 1,
del: 1,
em: 1,
i: 1,
ins: 1,
label: 1,
q: 1,
small: 1,
span: 1,
strong: 1
}
module.exports = {
visited: function (e, owner) {
if (!e.instance.hasClass('_visited'))
e.instance.addClass('_visited')
owner.callMethod('linkpress', e)
},
use: function (item) {
return !item.c && !inline[item.name] && (item.attrs.style || '').indexOf('display:inline') == -1
}
}
</wxs>
<block wx:for="{{nodes}}" wx:key="index" wx:for-item="n">
<!--图片-->
<view wx:if="{{n.name=='img'}}" id="{{n.attrs.id}}" class="_img {{n.attrs.class}}" style="{{n.attrs.style}}" data-attrs="{{n.attrs}}" bindtap="imgtap">
<rich-text nodes="{{[{attrs:{src:loading&&n.load!=2?loading:(lazyLoad&&!n.load?placeholder:n.attrs.src||''),alt:n.attrs.alt||'',width:n.attrs.width||'',style:'-webkit-touch-callout:none;max-width:100%;display:block'+(n.attrs.height?';height:'+n.attrs.height:'')},name:'img'}]}}" />
<image class="_image" src="{{lazyLoad&&!n.load?placeholder:n.attrs.src}}" lazy-load="{{lazyLoad}}" show-menu-by-longpress="{{!n.attrs.ignore}}" data-i="{{index}}" data-index="{{n.attrs.i}}" data-source="img" bindload="loadImg" binderror="error" />
</view>
<!--文本-->
<text wx:elif="{{n.type=='text'}}" decode>{{n.text}}</text>
<text wx:elif="{{n.name=='br'}}">\n</text>
<!--链接-->
<view wx:elif="{{n.name=='a'}}" id="{{n.attrs.id}}" class="_a {{n.attrs.class}}" hover-class="_hover" style="{{n.attrs.style}}" data-attrs="{{n.attrs}}" bindtap="{{canIUse?handler.visited:'linkpress'}}">
<trees class="_node" nodes="{{n.children}}" />
</view>
<!--视频-->
<block wx:elif="{{n.name=='video'}}">
<view wx:if="{{n.lazyLoad&&!n.attrs.autoplay}}" id="{{n.attrs.id}}" class="_video {{n.attrs.class}}" style="{{n.attrs.style}}" data-i="{{index}}" bindtap="loadVideo" />
<video wx:else id="{{n.attrs.id}}" class="{{n.attrs.class}}" style="{{n.attrs.style}}" autoplay="{{n.attrs.autoplay}}" controls="{{!n.attrs.autoplay||n.attrs.controls}}" loop="{{n.attrs.loop}}" muted="{{n.attrs.muted}}" poster="{{n.attrs.poster}}" src="{{n.attrs.source[n.i||0]}}" unit-id="{{n.attrs['unit-id']}}" data-i="{{index}}" data-source="video" binderror="error" bindplay="play" />
</block>
<!--音频-->
<audio wx:elif="{{n.name=='audio'}}" id="{{n.attrs.id}}" class="{{n.attrs.class}}" style="{{n.attrs.style}}" author="{{n.attrs.author}}" autoplay="{{n.attrs.autoplay}}" controls="{{n.attrs.controls}}" loop="{{n.attrs.loop}}" name="{{n.attrs.name}}" poster="{{n.attrs.poster}}" src="{{n.attrs.source[n.i||0]}}" data-i="{{index}}" data-source="audio" binderror="error" bindplay="play" />
<!--广告-->
<ad wx:elif="{{n.name=='ad'}}" class="{{n.attrs.class}}" style="{{n.attrs.style}}" unit-id="{{n.attrs['unit-id']}}" data-source="ad" binderror="error" />
<!--列表-->
<view wx:elif="{{n.name=='li'}}" id="{{n.attrs.id}}" class="{{n.attrs.class}}" style="{{n.attrs.style}};display:flex">
<view wx:if="{{n.type=='ol'}}" class="_ol-bef">{{n.num}}</view>
<view wx:else class="_ul-bef">
<view wx:if="{{n.floor%3==0}}" class="_ul-p1">█</view>
<view wx:elif="{{n.floor%3==2}}" class="_ul-p2" />
<view wx:else class="_ul-p1" style="border-radius:50%">█</view>
</view>
<trees class="_node _li" lazyLoad="{{lazyLoad}}" loading="{{loading}}" nodes="{{n.children}}" />
</view>
<!--富文本-->
<rich-text wx:elif="{{handler.use(n)}}" id="{{n.attrs.id}}" class="_p __{{n.name}}" nodes="{{[n]}}" />
<!--继续递归-->
<trees wx:else id="{{n.attrs.id}}" class="_node _{{n.name}} {{n.attrs.class}}" style="{{n.attrs.style}}" lazyLoad="{{lazyLoad}}" loading="{{loading}}" nodes="{{n.children}}" />
</block>

View File

@ -0,0 +1,184 @@
/* 在这里引入自定义样式 */
/* 链接和图片效果 */
._a {
display: inline;
padding: 1.5px 0 1.5px 0;
color: #366092;
word-break: break-all;
}
._hover {
text-decoration: underline;
opacity: 0.7;
}
._visited {
color: #551a8b;
}
._img {
position: relative;
display: inline-block;
max-width: 100%;
}
/* 内部样式 */
:host {
display: inline;
}
._blockquote,
._div,
._p,
._ul,
._ol,
._li {
display: block;
}
._b,
._strong {
font-weight: bold;
}
._code {
font-family: monospace;
}
._del {
text-decoration: line-through;
}
._em,
._i {
font-style: italic;
}
._h1 {
font-size: 2em;
}
._h2 {
font-size: 1.5em;
}
._h3 {
font-size: 1.17em;
}
._h5 {
font-size: 0.83em;
}
._h6 {
font-size: 0.67em;
}
._h1,
._h2,
._h3,
._h4,
._h5,
._h6 {
display: block;
font-weight: bold;
}
._image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
}
._ins {
text-decoration: underline;
}
._li {
flex: 1;
width: 0;
}
._ol-bef {
width: 36px;
margin-right: 5px;
text-align: right;
}
._ul-bef {
margin: 0 12px 0 23px;
line-height: normal;
}
._ol-bef,
._ul_bef {
flex: none;
user-select: none;
}
._ul-p1 {
display: inline-block;
width: 0.3em;
height: 0.3em;
overflow: hidden;
line-height: 0.3em;
}
._ul-p2 {
display: inline-block;
width: 0.23em;
height: 0.23em;
border: 0.05em solid black;
border-radius: 50%;
}
._q::before {
content: '"';
}
._q::after {
content: '"';
}
._sub {
font-size: smaller;
vertical-align: sub;
}
._sup {
font-size: smaller;
vertical-align: super;
}
.__bdi,
.__bdo,
.__ruby,
.__rt {
display: inline-block;
}
._video {
position: relative;
display: inline-block;
width: 300px;
height: 225px;
background-color: black;
}
._video::after {
position: absolute;
top: 50%;
left: 50%;
margin: -15px 0 0 -15px;
content: '';
border-color: transparent transparent transparent white;
border-style: solid;
border-width: 15px 0 15px 30px;
}
._img {
vertical-align: top;
}

View File

@ -0,0 +1,57 @@
const App = getApp();
Component({
options: {
multipleSlots: true // 在组件定义时的选项中启用多slot支持
},
/**
* 组件的属性列表
* 用于组件自定义设置
*/
properties: {
// 弹窗标题
title: {
type: String,
value: '弹窗标题'
}
},
/**
* 私有数据, 组件的初始数据
* 可用于模版渲染
*/
data: {
// 弹窗显示控制
isShow: false,
transparent: true
},
/**
* 组件的方法列表
* 更新属性和数据的方法与更新页面数据的方法类似
*/
methods: {
/**
* 导航菜单切换事件
*/
_onToggleShow(e) {
this.setData({
isShow: !this.data.isShow,
transparent: false
})
},
/**
* 导航页面跳转
*/
_onTargetPage(e) {
let urls = App.getTabBarLinks();
wx.switchTab({
url: '/' + urls[e.detail.target.dataset.index]
});
}
}
})

View File

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

View File

@ -0,0 +1,32 @@
<view class="shortcut">
<!-- 首页 -->
<form bindsubmit="_onTargetPage">
<button formType="submit" data-index="0" class="nav-item btn-normal {{ isShow ? 'show_80' : (transparent ? '' : 'hide_80') }}">
<text class="iconfont icon-home"></text>
</button>
</form>
<!-- 全部活动 -->
<form bindsubmit="_onTargetPage">
<button formType="submit" data-index="1" class="nav-item btn-normal {{ isShow ? 'show_60' : (transparent ? '' : 'hide_60') }}">
<text class="iconfont icon-cate"></text>
</button>
</form>
<!-- 个人中心 -->
<form bindsubmit="_onTargetPage">
<button formType="submit" data-index="3" class="nav-item btn-normal {{ isShow ? 'show_20' : (transparent ? '' : 'hide_20') }}">
<text class="iconfont icon-profile"></text>
</button>
</form>
<!-- 显示隐藏开关 -->
<form bindsubmit="_onToggleShow">
<button formType="submit" class="nav-item nav-item__switch btn-normal {{ isShow ? 'shortcut_click_show' : '' }}">
<text class='iconfont icon-daohang'></text>
</button>
</form>
</view>

View File

@ -0,0 +1,205 @@
@import "/utils/common.wxss";
/* 快捷导航 */
.shortcut {
position: fixed;
right: 12px;
bottom: 250rpx;
width: 76rpx;
line-height: 1;
z-index: 5;
border-radius: 50%;
}
/* 导航菜单元素 */
.nav-item {
position: absolute;
bottom: 0;
padding: 0;
width: 76rpx;
height: 76rpx;
line-height: 76rpx;
color: #fff;
background: rgba(0, 0, 0, 0.4);
border-radius: 50%;
text-align: center;
transform: rotate(0deg);
opacity: 0;
}
.nav-item text {
font-size: 40rpx;
}
/* 导航开关 */
.nav-item__switch {
opacity: 1;
}
.shortcut_click_show {
margin-bottom: 0;
background: #ff5454;
}
/* 显示动画 */
.show_80 {
bottom: 384rpx;
animation: show_80 0.3s forwards;
}
.show_60 {
bottom: 288rpx;
animation: show_60 0.3s forwards;
}
.show_40 {
bottom: 192rpx;
animation: show_40 0.3s forwards;
}
.show_20 {
bottom: 96rpx;
animation: show_20 0.3s forwards;
}
@keyframes show_20 {
from {
bottom: 0;
transform: rotate(0deg);
opacity: 0;
}
to {
bottom: 96rpx;
transform: rotate(360deg);
opacity: 1;
}
}
@keyframes show_40 {
from {
bottom: 0;
transform: rotate(0deg);
opacity: 0;
}
to {
bottom: 192rpx;
transform: rotate(360deg);
opacity: 1;
}
}
@keyframes show_60 {
from {
bottom: 0;
transform: rotate(0deg);
opacity: 0;
}
to {
bottom: 288rpx;
transform: rotate(360deg);
opacity: 1;
}
}
@keyframes show_80 {
from {
bottom: 0;
transform: rotate(0deg);
opacity: 0;
}
to {
bottom: 384rpx;
transform: rotate(360deg);
opacity: 1;
}
}
/* 隐藏动画 */
.hide_80 {
bottom: 0;
animation: hide_80 0.3s;
opacity: 0;
}
.hide_60 {
bottom: 0;
animation: hide_60 0.3s;
opacity: 0;
}
.hide_40 {
bottom: 0;
animation: hide_40 0.3s;
opacity: 0;
}
.hide_20 {
bottom: 0;
animation: hide_20 0.3s;
opacity: 0;
}
@keyframes hide_20 {
from {
bottom: 96rpx;
transform: rotate(360deg);
opacity: 1;
}
to {
bottom: 0;
transform: rotate(0deg);
opacity: 0;
}
}
@keyframes hide_40 {
from {
bottom: 192rpx;
transform: rotate(360deg);
opacity: 1;
}
to {
bottom: 0;
transform: rotate(0deg);
opacity: 0;
}
}
@keyframes hide_60 {
from {
bottom: 288rpx;
transform: rotate(360deg);
opacity: 1;
}
to {
bottom: 0;
transform: rotate(0deg);
opacity: 0;
}
}
@keyframes hide_80 {
from {
bottom: 384rpx;
transform: rotate(360deg);
opacity: 1;
}
to {
bottom: 0;
transform: rotate(0deg);
opacity: 0;
}
}

235
ec-canvas/ec-canvas.js Normal file
View File

@ -0,0 +1,235 @@
import WxCanvas from './wx-canvas';
import * as echarts from './echarts';
let ctx;
function compareVersion(v1, v2) {
v1 = v1.split('.')
v2 = v2.split('.')
const len = Math.max(v1.length, v2.length)
while (v1.length < len) {
v1.push('0')
}
while (v2.length < len) {
v2.push('0')
}
for (let i = 0; i < len; i++) {
const num1 = parseInt(v1[i])
const num2 = parseInt(v2[i])
if (num1 > num2) {
return 1
} else if (num1 < num2) {
return -1
}
}
return 0
}
Component({
properties: {
canvasId: {
type: String,
value: 'ec-canvas'
},
ec: {
type: Object
},
forceUseOldCanvas: {
type: Boolean,
value: false
}
},
data: {
isUseNewCanvas: false
},
ready: function () {
if (!this.data.ec) {
console.warn('组件需绑定 ec 变量,例:<ec-canvas id="mychart-dom-bar" '
+ 'canvas-id="mychart-bar" ec="{{ ec }}"></ec-canvas>');
return;
}
if (!this.data.ec.lazyLoad) {
this.init();
}
},
methods: {
init: function (callback) {
const version = wx.getSystemInfoSync().SDKVersion
const canUseNewCanvas = compareVersion(version, '2.9.0') >= 0;
const forceUseOldCanvas = this.data.forceUseOldCanvas;
const isUseNewCanvas = canUseNewCanvas && !forceUseOldCanvas;
this.setData({ isUseNewCanvas });
if (forceUseOldCanvas && canUseNewCanvas) {
console.warn('开发者强制使用旧canvas,建议关闭');
}
if (isUseNewCanvas) {
// console.log('微信基础库版本大于2.9.0,开始使用<canvas type="2d"/>');
// 2.9.0 可以使用 <canvas type="2d"></canvas>
this.initByNewWay(callback);
} else {
const isValid = compareVersion(version, '1.9.91') >= 0
if (!isValid) {
console.error('微信基础库版本过低,需大于等于 1.9.91。'
+ '参见https://github.com/ecomfe/echarts-for-weixin'
+ '#%E5%BE%AE%E4%BF%A1%E7%89%88%E6%9C%AC%E8%A6%81%E6%B1%82');
return;
} else {
console.warn('建议将微信基础库调整大于等于2.9.0版本。升级后绘图将有更好性能');
this.initByOldWay(callback);
}
}
},
initByOldWay(callback) {
// 1.9.91 <= version < 2.9.0:原来的方式初始化
ctx = wx.createCanvasContext(this.data.canvasId, this);
const canvas = new WxCanvas(ctx, this.data.canvasId, false);
echarts.setCanvasCreator(() => {
return canvas;
});
// const canvasDpr = wx.getSystemInfoSync().pixelRatio // 微信旧的canvas不能传入dpr
const canvasDpr = 1
var query = wx.createSelectorQuery().in(this);
query.select('.ec-canvas').boundingClientRect(res => {
if (typeof callback === 'function') {
this.chart = callback(canvas, res.width, res.height, canvasDpr);
}
else if (this.data.ec && typeof this.data.ec.onInit === 'function') {
this.chart = this.data.ec.onInit(canvas, res.width, res.height, canvasDpr);
}
else {
this.triggerEvent('init', {
canvas: canvas,
width: res.width,
height: res.height,
canvasDpr: canvasDpr // 增加了dpr可方便外面echarts.init
});
}
}).exec();
},
initByNewWay(callback) {
// version >= 2.9.0:使用新的方式初始化
const query = wx.createSelectorQuery().in(this)
query
.select('.ec-canvas')
.fields({ node: true, size: true })
.exec(res => {
const canvasNode = res[0].node
this.canvasNode = canvasNode
const canvasDpr = wx.getSystemInfoSync().pixelRatio
const canvasWidth = res[0].width
const canvasHeight = res[0].height
const ctx = canvasNode.getContext('2d')
const canvas = new WxCanvas(ctx, this.data.canvasId, true, canvasNode)
echarts.setCanvasCreator(() => {
return canvas
})
if (typeof callback === 'function') {
this.chart = callback(canvas, canvasWidth, canvasHeight, canvasDpr)
} else if (this.data.ec && typeof this.data.ec.onInit === 'function') {
this.chart = this.data.ec.onInit(canvas, canvasWidth, canvasHeight, canvasDpr)
} else {
this.triggerEvent('init', {
canvas: canvas,
width: canvasWidth,
height: canvasHeight,
dpr: canvasDpr
})
}
})
},
canvasToTempFilePath(opt) {
if (this.data.isUseNewCanvas) {
// 新版
const query = wx.createSelectorQuery().in(this)
query
.select('.ec-canvas')
.fields({ node: true, size: true })
.exec(res => {
const canvasNode = res[0].node
opt.canvas = canvasNode
wx.canvasToTempFilePath(opt)
})
} else {
// 旧的
if (!opt.canvasId) {
opt.canvasId = this.data.canvasId;
}
ctx.draw(true, () => {
wx.canvasToTempFilePath(opt, this);
});
}
},
touchStart(e) {
if (this.chart && e.touches.length > 0) {
var touch = e.touches[0];
var handler = this.chart.getZr().handler;
handler.dispatch('mousedown', {
zrX: touch.x,
zrY: touch.y
});
handler.dispatch('mousemove', {
zrX: touch.x,
zrY: touch.y
});
handler.processGesture(wrapTouch(e), 'start');
}
},
touchMove(e) {
if (this.chart && e.touches.length > 0) {
var touch = e.touches[0];
var handler = this.chart.getZr().handler;
handler.dispatch('mousemove', {
zrX: touch.x,
zrY: touch.y
});
handler.processGesture(wrapTouch(e), 'change');
}
},
touchEnd(e) {
if (this.chart) {
const touch = e.changedTouches ? e.changedTouches[0] : {};
var handler = this.chart.getZr().handler;
handler.dispatch('mouseup', {
zrX: touch.x,
zrY: touch.y
});
handler.dispatch('click', {
zrX: touch.x,
zrY: touch.y
});
handler.processGesture(wrapTouch(e), 'end');
}
}
}
});
function wrapTouch(event) {
for (let i = 0; i < event.touches.length; ++i) {
const touch = event.touches[i];
touch.offsetX = touch.x;
touch.offsetY = touch.y;
}
return event;
}

4
ec-canvas/ec-canvas.json Normal file
View File

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

4
ec-canvas/ec-canvas.wxml Normal file
View File

@ -0,0 +1,4 @@
<!-- 新的接口对其了H5 -->
<canvas wx:if="{{isUseNewCanvas}}" type="2d" class="ec-canvas" canvas-id="{{ canvasId }}" bindinit="init" bindtouchstart="{{ ec.disableTouch ? '' : 'touchStart' }}" bindtouchmove="{{ ec.disableTouch ? '' : 'touchMove' }}" bindtouchend="{{ ec.disableTouch ? '' : 'touchEnd' }}"></canvas>
<!-- 旧的 -->
<canvas wx:else class="ec-canvas" type="2d" canvas-id="{{ canvasId }}" bindinit="init" bindtouchstart="{{ ec.disableTouch ? '' : 'touchStart' }}" bindtouchmove="{{ ec.disableTouch ? '' : 'touchMove' }}" bindtouchend="{{ ec.disableTouch ? '' : 'touchEnd' }}"></canvas>

5
ec-canvas/ec-canvas.wxss Normal file
View File

@ -0,0 +1,5 @@
.ec-canvas {
width: 100%;
height: 100vh;
z-index:1;
}

22
ec-canvas/echarts.js Normal file

File diff suppressed because one or more lines are too long

121
ec-canvas/wx-canvas.js Normal file
View File

@ -0,0 +1,121 @@
export default class WxCanvas {
constructor(ctx, canvasId, isNew, canvasNode) {
this.ctx = ctx;
this.canvasId = canvasId;
this.chart = null;
this.isNew = isNew
if (isNew) {
this.canvasNode = canvasNode;
}
else {
this._initStyle(ctx);
}
// this._initCanvas(zrender, ctx);
this._initEvent();
}
getContext(contextType) {
if (contextType === '2d') {
return this.ctx;
}
}
// canvasToTempFilePath(opt) {
// if (!opt.canvasId) {
// opt.canvasId = this.canvasId;
// }
// return wx.canvasToTempFilePath(opt, this);
// }
setChart(chart) {
this.chart = chart;
}
attachEvent() {
// noop
}
detachEvent() {
// noop
}
_initCanvas(zrender, ctx) {
zrender.util.getContext = function () {
return ctx;
};
zrender.util.$override('measureText', function (text, font) {
ctx.font = font || '12px sans-serif';
return ctx.measureText(text);
});
}
_initStyle(ctx) {
var styles = ['fillStyle', 'strokeStyle', 'globalAlpha',
'textAlign', 'textBaseAlign', 'shadow', 'lineWidth',
'lineCap', 'lineJoin', 'lineDash', 'miterLimit', 'fontSize'];
styles.forEach(style => {
Object.defineProperty(ctx, style, {
set: value => {
if (style !== 'fillStyle' && style !== 'strokeStyle'
|| value !== 'none' && value !== null
) {
ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value);
}
}
});
});
ctx.createRadialGradient = () => {
return ctx.createCircularGradient(arguments);
};
}
_initEvent() {
this.event = {};
const eventNames = [{
wxName: 'touchStart',
ecName: 'mousedown'
}, {
wxName: 'touchMove',
ecName: 'mousemove'
}, {
wxName: 'touchEnd',
ecName: 'mouseup'
}, {
wxName: 'touchEnd',
ecName: 'click'
}];
eventNames.forEach(name => {
this.event[name.wxName] = e => {
const touch = e.touches[0];
this.chart.getZr().handler.dispatch(name.ecName, {
zrX: name.wxName === 'tap' ? touch.clientX : touch.x,
zrY: name.wxName === 'tap' ? touch.clientY : touch.y
});
};
});
}
set width(w) {
if (this.canvasNode) this.canvasNode.width = w
}
set height(h) {
if (this.canvasNode) this.canvasNode.height = h
}
get width() {
if (this.canvasNode)
return this.canvasNode.width
return 0
}
get height() {
if (this.canvasNode)
return this.canvasNode.height
return 0
}
}

BIN
images/0181008111258.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
images/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

BIN
images/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
images/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
images/5-121204193R0-50.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

BIN
images/avds.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
images/back.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
images/bk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
images/closeLogin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
images/default-avatar.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
images/deng.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
images/dianzan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 B

BIN
images/ditu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
images/fabulous.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 B

BIN
images/fan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
images/guan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
images/guane.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
images/guasn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
images/guji.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
images/gzh_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

BIN
images/handle_calendar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
images/icon-index-empty.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

BIN
images/link_more.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

BIN
images/moah.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
images/my_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
images/no_content.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
images/nofabulous.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
images/qi_on.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 B

BIN
images/ren.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
images/sacsa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
images/saoua.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
images/sign_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
images/snji.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
images/suo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
images/t1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
images/t2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
images/t3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
images/tabBar/cate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 B

BIN
images/tabBar/cate_on.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 B

BIN
images/tabBar/home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 B

BIN
images/tabBar/home_on.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
images/tabBar/user.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1005 B

BIN
images/tabBar/user_on.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
images/tc_bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
images/tian.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
images/ty.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
images/user-bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
images/userHelp1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
images/userPer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 949 B

BIN
images/userServer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
images/userXun.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
images/userhelp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
images/we.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
images/wei.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
images/wen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
images/white.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
images/xai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Some files were not shown because too many files have changed in this diff Show More