Browse Source

first commit

master
chenkainan 7 months ago
parent
commit
fc5afb25b5
  1. BIN
      .DS_Store
  2. 20
      .hbuilderx/launch.json
  3. 728
      App.vue
  4. 18
      common/http/index.js
  5. 244
      common/http/interface.js
  6. 91
      common/index.scss
  7. 99
      components/countdown-timer/countdown-timer.vue
  8. 3492
      components/css/animate.min.css
  9. 23
      components/css/iconfont.css
  10. 138
      components/get-phone/index.vue
  11. 66
      components/html/AI.vue
  12. 163
      components/html/back.vue
  13. 103
      components/html/listnav.vue
  14. 99
      components/html/tabBar.vue
  15. BIN
      components/invinbg-image-cropper/.DS_Store
  16. 759
      components/invinbg-image-cropper/invinbg-image-cropper.vue
  17. 488
      components/lb-picker/README.md
  18. 392
      components/lb-picker/index.vue
  19. 93
      components/lb-picker/mixins/index.js
  20. 135
      components/lb-picker/pickers/multi-selector-picker.vue
  21. 113
      components/lb-picker/pickers/selector-picker.vue
  22. 116
      components/lb-picker/pickers/unlinked-selector-picker.vue
  23. 40
      components/lb-picker/style/picker-item.scss
  24. 176
      components/lb-picker/style/picker.scss
  25. 121
      components/lb-picker/utils.js
  26. 278
      components/luch-audio/luch-audio.vue
  27. 65
      components/luch-audio/readme.md
  28. 55
      components/mescroll-uni/components/mescroll-down.css
  29. 47
      components/mescroll-uni/components/mescroll-down.vue
  30. 90
      components/mescroll-uni/components/mescroll-empty.vue
  31. 83
      components/mescroll-uni/components/mescroll-top.vue
  32. 47
      components/mescroll-uni/components/mescroll-up.css
  33. 39
      components/mescroll-uni/components/mescroll-up.vue
  34. 19
      components/mescroll-uni/mescroll-body.css
  35. 344
      components/mescroll-uni/mescroll-body.vue
  36. 65
      components/mescroll-uni/mescroll-mixins.js
  37. 33
      components/mescroll-uni/mescroll-uni-option.js
  38. 36
      components/mescroll-uni/mescroll-uni.css
  39. 788
      components/mescroll-uni/mescroll-uni.js
  40. 420
      components/mescroll-uni/mescroll-uni.vue
  41. 50
      components/mescroll-uni/mixins/mescroll-comp.js
  42. 51
      components/mescroll-uni/mixins/mescroll-more-item.js
  43. 56
      components/mescroll-uni/mixins/mescroll-more.js
  44. 102
      components/mescroll-uni/wxs/mixins.js
  45. 92
      components/mescroll-uni/wxs/renderjs.js
  46. 268
      components/mescroll-uni/wxs/wxs.wxs
  47. 819
      components/mx-datepicker/mx-datepicker.vue
  48. 218
      components/mx-datepicker/uni-pagination.vue
  49. 37
      components/prompt/README.md
  50. 158
      components/prompt/index.vue
  51. 54
      components/tags-list.vue
  52. 1
      components/utils/amap-wx.js
  53. 319
      components/utils/css/application.css
  54. 185
      components/utils/date.js
  55. 1123
      components/utils/erweima.js
  56. 180
      components/utils/gwpingjia.vue
  57. 419
      components/utils/request.js
  58. 5662
      components/utils/u-charts.js
  59. 315
      components/utils/util.js
  60. 363
      components/utils/zixunpj.vue
  61. 1
      components/w-picker/areadata/areadata.js
  62. 742
      components/w-picker/date-picker.vue
  63. 345
      components/w-picker/half-picker.vue
  64. 274
      components/w-picker/linkage-picker.vue
  65. 344
      components/w-picker/range-picker.vue
  66. 183
      components/w-picker/region-picker.vue
  67. 129
      components/w-picker/selector-picker.vue
  68. 250
      components/w-picker/shortterm-picker.vue
  69. 218
      components/w-picker/time-picker.vue
  70. 26
      components/w-picker/w-picker.css
  71. 340
      components/w-picker/w-picker.vue
  72. 669
      components/zhouWei-navBar/index.vue
  73. 152
      components/zhouWei-navBar/zhouWei-navBar.md
  74. 166
      js_sdk/u-charts/u-charts/component.vue
  75. 5662
      js_sdk/u-charts/u-charts/u-charts.js
  76. 67
      main.js
  77. 77
      manifest.json
  78. 26
      other/720sny.vue
  79. 351
      other/allSearch.vue
  80. 937
      other/components/mx-datepicker/mx-datepicker.vue
  81. 54
      other/components/tags-list.vue
  82. 340
      other/consulting.vue
  83. 248
      other/detailList.vue
  84. 373
      other/feiyi.vue
  85. 235
      other/festival.vue
  86. 305
      other/foodCard.vue
  87. 275
      other/foodList.vue
  88. 376
      other/gftjedit.vue
  89. 170
      other/gonggjt.vue
  90. 142
      other/hotel-detail/components/jbxx-page.vue
  91. 259
      other/hotel-detail/components/list-page.vue
  92. 118
      other/hotel-detail/components/swiper-page.vue
  93. 56
      other/hotel-detail/components/tags-list.vue
  94. 233
      other/hotel-detail/components/ydmp-page.vue
  95. 581
      other/hotel-detail/hotel-detail.vue
  96. 508
      other/hotel.vue
  97. 316
      other/pingjia/pingjia.vue
  98. 308
      other/pingjia/pingjiazx.vue
  99. 304
      other/public.vue
  100. 522
      other/scenic.vue

BIN
.DS_Store

Binary file not shown.

20
.hbuilderx/launch.json

@ -0,0 +1,20 @@
{ // launch.json configurations app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
// launchtypelocalremote, localremote
"version": "0.0",
"configurations": [{
"default" :
{
"launchtype" : "local"
},
"h5" :
{
"launchtype" : "local"
},
"mp-weixin" :
{
"launchtype" : "local"
},
"type" : "uniCloud"
}
]
}

728
App.vue

@ -0,0 +1,728 @@
<script>
const updateManager = wx.canIUse('getUpdateManager') ? wx.getUpdateManager() : null;
export default {
onLaunch: function() {
this.$options.checkHasLoginInfo();
},
onShow: function() {
let that = this;
updateManager && updateManager.onUpdateReady(function() {
wx.showModal({
showCancel: false,
title: '更新提示',
content: '新版本已经准备好,请重启应用',
success: function(res) {
// applyUpdate
updateManager.applyUpdate();
}
});
});
},
onHide: function() {
console.log('App Hide')
},
checkHasLoginInfo() {
wx.checkSession({
success: function(res) {
console.warn('已经登录', res);
},
fail: function() {
uni.clearStorageSync();
}
});
}
}
</script>
<style>
/*每个页面公共css */
@font-face {
font-family: 'TpldKhangXiDictTrial'; /*重命名字体名 */
src: url('https://cs.tour-ma.com/r/cms/www/m/yushan/WenYue-GuDianMingChaoTi-NC-W5-1.otf'); //
font-weight: normal;
font-style: normal;
}
/*每个页面公共css */
page{
width: 100%;
height: 100%;
}
.family_regular {
font-family: SourceHanSansCNRegular, PingFangSC-Regular, Microsoft YaHei, arial, sans-serif;
}
.family_heiti {
font-family: heiti, PingFangSC-Regular, Microsoft YaHei, arial, sans-serif;
}
.font_normal {
font-family: PingFangSC-Regular, Microsoft YaHei, arial, sans-serif;
}
.font_bold {
font-family: PingFangSC-Semibold, Microsoft YaHei, arial, sans-serif;
font-weight: bold;
}
.no_data {
padding: 60rpx 0 30rpx 0;
text-align: center;
font-size: 28upx;
color: #999;
}
page,
.body {
font-size: 24upx;
height: 100%;
overflow-y: auto;
font-family: PingFangSC-Regular, Microsoft YaHei, arial, sans-serif;
}
image {
display: block;
}
.mescroll-empty .empty-icon {
margin: auto;
}
.line_through {
text-decoration: line-through;
}
.bottom_line {
position: fixed;
bottom: 0;
width: 100%;
left: 0;
z-index: 998;
height: 1px;
background-color: #eee;
}
.overflow_hidden {
overflow: hidden;
}
button {
margin: 0;
padding: 0;
border-radius: 0;
line-height: 1;
}
button:focus {
outline: none;
}
button::after {
border: none;
border-radius: 0;
}
/*三角形*/
.triangle_left {
position: relative;
height: 0px;
width: 0px;
border-top: 6upx solid transparent;
border-right: 8upx solid #808080;
border-bottom: 6upx solid transparent;
}
.triangle_right {
position: relative;
height: 0px;
width: 0px;
border-top: 6upx solid transparent;
border-left: 8upx solid #808080;
border-bottom: 6upx solid transparent;
}
.triangle_top {
position: relative;
height: 0px;
width: 0px;
border-bottom: 8upx solid #808080;
border-left: 6upx solid transparent;
border-right: 6upx solid transparent;
}
.triangle_bottom {
position: relative;
height: 0px;
width: 0px;
border-top: 8upx solid #808080;
border-left: 6upx solid transparent;
border-right: 6upx solid transparent;
}
/*解决页面渲染看到隐藏的demo*/
[v-cloak] {
display: none;
}
/*按钮*/
.content_btn_con {
overflow: hidden;
padding: 30upx 30upx;
}
.main_content_btn {
height: 88upx;
line-height: 88upx;
text-align: center;
background: #ffb155;
border-radius: 10upx;
color: $color_fff;
font-size: 32upx;
font-weight: bold;
}
/*tab*/
/*文本超出宽度点点*/
.text_hidden {
overflow: hidden;
white-space: nowrap !important;
text-overflow: ellipsis !important;
}
/*行文本溢出点点*/
.one_hidden {
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
}
.two_hidden {
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.three_hidden {
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
}
.color_bg_main {
background: $color_main !important;
color: $color_fff !important;
border-color: $color_main !important;
}
.color_br_main {
color: $color_main !important;
border-color: $color_main !important;
}
.color_black {
color: $color_black !important;
}
.color_main {
color: $color_main !important;
}
.color_orange {
color: $color_orange !important;
}
.color_green {
color: $color_green;
}
.color_light_green {
color: $color_light_green;
}
.color_999 {
color: $color_999;
}
.color_red {
color: $color_red !important;
}
.color_pink {
color: $color_pink;
}
.color_666 {
color: $color_666;
}
.color_808080 {
color: $color_808080;
}
.color_ccc {
color: $color_ccc;
}
.color_333 {
color: $color_333;
}
.color_blue {
color: $color_blue !important;
}
.color_bg_gray {
background-color: #d9d9d9 !important;
}
/*字体大小*/
.font_16 {
font-size: 16upx !important;
}
.font_18 {
font-size: 18upx !important;
}
.font_20 {
font-size: 20upx !important;
}
.font_22 {
font-size: 22upx !important;
}
.font_24 {
font-size: 24upx !important;
}
.font_26 {
font-size: 26upx !important;
}
.font_28 {
font-size: 28upx !important;
}
.font_30 {
font-size: 30upx !important;
}
.font_32 {
font-size: 32upx !important;
}
.font_34 {
font-size: 34upx !important;
}
.font_36 {
font-size: 36upx !important;
}
.font_40 {
font-size: 40upx !important;
}
.font_42 {
font-size: 43upx !important;
}
.font_44 {
font-size: 44upx !important;
}
.font_46 {
font-size: 46upx !important;
}
.font_50 {
font-size: 50upx !important;
}
.font_62 {
font-size: 62upx !important;
}
.font_72 {
font-size: 72upx !important;
}
.float_left {
float: left !important;
}
.float_right {
float: right !important;
}
/*间距*/
.ml_0 {
margin-left: 0 !important;
}
.ml_10 {
margin-left: 10upx;
}
.ml_20 {
margin-left: 20upx;
}
.ml_30 {
margin-left: 30upx;
}
.ml_40 {
margin-left: 40upx;
}
.ml_60 {
margin-left: 60upx;
}
.ml_80 {
margin-left: 80upx;
}
.ml_100 {
margin-left: 100upx;
}
.ml_120 {
margin-left: 120upx;
}
.ml_10 {
margin-left: 10upx;
}
.ml_20 {
margin-left: 20upx;
}
.ml_30 {
margin-left: 30upx;
}
.ml_40 {
margin-left: 40upx;
}
.ml_60 {
margin-left: 60upx;
}
.ml_80 {
margin-left: 80upx;
}
.ml_100 {
margin-left: 100upx;
}
.ml_120 {
margin-left: 120upx;
}
.mt_10 {
margin-top: 10upx;
}
.mt_20 {
margin-top: 20upx;
}
.mt_30 {
margin-top: 30upx;
}
.mt_40 {
margin-top: 40upx;
}
.mt_60 {
margin-top: 60upx;
}
.mt_80 {
margin-top: 80upx;
}
.mt_100 {
margin-top: 100upx;
}
.mt_120 {
margin-top: 120upx;
}
.mb_10 {
margin-bottom: 10upx;
}
.mb_20 {
margin-bottom: 20upx;
}
.mb_30 {
margin-bottom: 30upx;
}
.mb_40 {
margin-bottom: 40upx;
}
.mb_60 {
margin-bottom: 60upx;
}
.mb_80 {
margin-bottom: 80upx;
}
.mb_100 {
margin-bottom: 100upx;
}
.mb_120 {
margin-bottom: 120upx;
}
.mr_10 {
margin-right: 10upx;
}
.mr_20 {
margin-right: 20upx;
}
.mr_30 {
margin-right: 30upx;
}
.mr_40 {
margin-right: 40upx;
}
.mr_60 {
margin-right: 60upx;
}
.mr_80 {
margin-right: 80upx;
}
.mr_100 {
margin-right: 100upx;
}
.mr_120 {
margin-right: 120upx;
}
.p_0 {
padding: 0 !important;
}
.ptb_0 {
padding-top: 0 !important;
padding-bottom: 0 !important;
}
.plr_0 {
padding-left: 0 !important;
padding-right: 0 !important;
}
.pt_10 {
padding-top: 10upx;
}
.pt_5 {
padding-top: 5upx;
}
.pt_20 {
padding-top: 20upx;
}
.pt_30 {
padding-top: 30upx;
}
.pt_40 {
padding-top: 40upx;
}
.pt_60 {
padding-top: 60upx;
}
.pt_80 {
padding-top: 80upx;
}
.pt_100 {
padding-top: 100upx;
}
.pt_120 {
padding-top: 120upx;
}
.pb_20 {
padding-bottom: 20upx;
}
.pb_30 {
padding-bottom: 30upx !important;
}
.pb_40 {
padding-bottom: 40upx;
}
.pb_60 {
padding-bottom: 60upx;
}
.pb_80 {
padding-bottom: 80upx;
}
.pb_100 {
padding-bottom: 100upx;
}
.pb_120 {
padding-bottom: 120upx;
}
.pl_10 {
padding-left: 10upx;
}
.pl_20 {
padding-left: 20upx;
}
.pl_40 {
padding-left: 40upx;
}
.pl_60 {
padding-left: 60upx;
}
.pl_80 {
padding-left: 80upx;
}
.pl_100 {
padding-left: 100upx;
}
.pl_120 {
padding-left: 120upx;
}
/*flex*/
.d_flex {
display: -webkit-flex;
display: flex;
}
.flex_wrap {
display: -webkit-flex;
display: flex;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
}
.j_start {
-webkit-justify-content: flex-start;
justify-content: flex-start;
}
.j_center {
-webkit-justify-content: center;
justify-content: center;
}
.j_end {
-webkit-justify-content: flex-end;
justify-content: flex-end;
}
.j_space_around {
-webkit-justify-content: space-around;
justify-content: space-around;
}
.j_between {
-webkit-justify-content: space-between;
justify-content: space-between;
}
.a_start {
-webkit-align-items: flex-start;
align-items: flex-start;
}
.a_center {
-webkit-align-items: center;
align-items: center;
}
.a_end {
-webkit-align-items: flex-end;
align-items: flex-end;
}
/* 单行文字省略 */
.text_overflow{
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
/* 2行文字省略 */
.text_overflow_2{
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.konglist{
width: 100%;
height: 318rpx;
background: url(https://cs.tour-ma.com/r/cms/www/m/changshu/wf-kong.png);
background-size: 100% 100%;
font-size: 30rpx;
font-family: Microsoft YaHei;
font-weight: 400;
color: #999999;
line-height: 36rpx;
text-align: center;
font-family: Microsoft YaHei;
font-weight: 400;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 20rpx 0;
}
@import "uview-ui/index.scss";
@import './common/index.scss';
</style>

18
common/http/index.js

@ -0,0 +1,18 @@
import http from './interface'
/**
* 将业务所有接口统一起来便于维护
* 如果项目很大可以将 url 独立成文件接口分成不同的模块
*
*/
export const doGet = (url,data) => {
return http.get(url,data);
}
export const doPost = (url,data) => {
return http.post(url,data);
}
// 默认全部导出 import api from '@/common/http/'
export default {
doGet,
doPost
}

244
common/http/interface.js

@ -0,0 +1,244 @@
/**
* 通用uni-app网络请求
* 基于 Promise 对象实现更简单的 request 使用方式支持请求和响应拦截
*/
import config from '@/utils/config.js';
import md5 from '@/utils/md5.js';
import constant from "@/utils/constant"; //常量
/*
// 开放的接口
import http from './interface'
http.config.baseUrl = "http://localhost:8080/api/"
http.request(url:'user/list',method:'GET').then((res)=>{
console.log(JSON.stringify(res))
})
http.get('user/list').then((res)=>{
console.log(JSON.stringify(res))
})
http.get('user/list', {status: 1}).then((res)=>{
console.log(JSON.stringify(res))
})
http.post('user', {id:1, status: 1}).then((res)=>{
console.log(JSON.stringify(res))
})
http.put('user/1', {status: 2}).then((res)=>{
console.log(JSON.stringify(res))
})
http.delete('user/1').then((res)=>{
console.log(JSON.stringify(res))
})
*/
let requestList = {} //api请求记录
// 将当前请求的api记录起来
function addRequestKey(key) {
requestList[key] = true
}
// 将请求完成的api从记录中移除
function removeRequestKey(key) {
delete requestList[key]
}
//当前请求的api是否已有记录
function hitRequestKey(key) {
return requestList[key];
}
// 获取串行请求的key,方便记录
function getLockRequestKey(url, data) {
let lockKey = url;
try {
if (data) {
lockKey += JSON.stringify(data)
}
} catch (e) {}
return lockKey;
}
//签名函数处理
function getSign(jsondata) {
let arr = [];
for (let key in jsondata) {
if (key !== "sign") {
arr.push(key + "=" + jsondata[key]);
}
}
arr.sort();
let sign = decodeURIComponent(arr.join("") + "guagua");
return md5.md5(sign);
}
let that = null,lockKeyData = null;
export default {
config: {
isWaiting: true,
baseUrl:config.baseUrl,
header: {
"Content-Type":"application/x-www-form-urlencoded"
},
data: {},
method: "GET",
dataType: "json",
/* 如设为json,会对返回的数据做一次 JSON.parse */
responseType: "text",
success(res) {
that.config.isWaiting && uni.hideLoading();
},
fail(res) {
that.config.isWaiting && uni.hideLoading();
},
complete() {
}
},
interceptor: {
request: null,
response: null
},
request(options) {
that = this;
if (!options) {
options = {}
}
options.baseUrl = options.baseUrl || this.config.baseUrl;
options.dataType = options.dataType || this.config.dataType;
options.url = options.baseUrl + options.url;
options.data = options.data || {};
options.method = options.method || this.config.method;
//添加参数自定义参数
lockKeyData = getLockRequestKey(options.url, options.data);
if (hitRequestKey(lockKeyData)) {
console.log(requestList)
return false;
}
addRequestKey(lockKeyData);
let isWaiting = true;
if (options.data && options.data.isWaiting === false) {
isWaiting = false;
delete options.data.isWaiting;
}
isWaiting && uni.showLoading({
title: '加载中',
mask: true
});
//TODO 加密数据
//TODO 数据签名
let loginInfo = uni.getStorageSync(constant.LOGIN_INFO) || {};
options.data.time = parseInt(new Date().getTime() / 1000);
if(loginInfo && loginInfo.UserId){
options.data.UserId = loginInfo.UserId;//用户id
}
this.config.header.loginTicket = loginInfo.loginTicket || ""; //防止数据串改
options.data.sign = getSign(options.data);
return new Promise((resolve, reject) => {
let _config = null;
options.complete = (response) => {
let statusCode = response.statusCode;
response.config = _config;
if (process.env.NODE_ENV === 'development') {
if (statusCode === 200) {
//console.log("【" + _config.requestId + "】 结果:" + JSON.stringify(response.data))
}
}
if (this.interceptor.response) {
let newResponse = this.interceptor.response(response);
if (newResponse) {
response = newResponse;
}
}
// 统一的响应日志记录
_reslog(response);
if (statusCode === 200) { //成功
resolve(response);
} else {
reject(response);
}
}
_config = Object.assign({}, this.config, options);
_config.requestId = new Date().getTime();
if (this.interceptor.request) {
this.interceptor.request(_config);
}
// 统一的请求日志记录
_reqlog(_config);
uni.request(_config);
});
},
get(url, data, options) {
if (!options) {
options = {}
}
options.url = url
options.data = data
options.method = 'GET'
return this.request(options)
},
post(url, data, options) {
if (!options) {
options = {}
}
options.url = url
options.data = data
options.method = 'POST'
return this.request(options)
},
put(url, data, options) {
if (!options) {
options = {}
}
options.url = url
options.data = data
options.method = 'PUT'
return this.request(options)
},
delete(url, data, options) {
if (!options) {
options = {}
}
options.url = url
options.data = data
options.method = 'DELETE'
return this.request(options)
}
}
/**
* 请求接口日志记录
*/
function _reqlog(req) {
removeRequestKey(lockKeyData); //删除请求的key
if (process.env.NODE_ENV === 'development') {
//console.log("【" + req.requestId + "】 地址:" + req.url)
if (req.data) {
console.log("【" + req.requestId + "】 请求参数:" + JSON.stringify(req.data))
}
}
//TODO 调接口异步写入日志数据库
}
/**
* 响应接口日志记录
*/
function _reslog(res) {
let _statusCode = res.statusCode;
if (process.env.NODE_ENV === 'development') {
//console.log("【" + res.config.requestId + "】 地址:" + res.config.url)
if (res.config.data) {
console.log("【" + res.config.requestId + "】 响应参数:" + JSON.stringify(res.config.data))
}
//console.log("【" + res.config.requestId + "】 响应结果:" + JSON.stringify(res))
}
//TODO 除了接口服务错误外,其他日志调接口异步写入日志数据库
switch (_statusCode) {
case 200:
break;
case 401:
break;
case 404:
break;
default:
break;
}
}

91
common/index.scss

@ -0,0 +1,91 @@
/**
* 单文件样式
* 请勿使用
*/
.u-dropdown__menu__item:last-child {
margin-right: 21rpx;
margin-left: 143rpx;
background-color: #eeeeee;
border-radius: 26rpx;
height: 55rpx;
width: 144rpx;
font-size: 28rpx;
// background-image: url('https://niangao.oss-cn-hangzhou.aliyuncs.com/ouhai_xcx/public/icon-map.png');
// background-size: 38rpx 38rpx;
background-repeat: no-repeat;
background-size: contain;
margin-top: 10rpx;
box-shadow: 0 3rpx 10rpx rgba(0,0,0,0.3);
.u-dropdown__menu__item__text{
margin-left: 50rpx;
}
.u-icon__icon {
display: none;
}
}
.title-container {
height: 35rpx;
display: flex;
align-items: center;
justify-content: space-between;
.content {
display: flex;
align-items: center;
.title {
color: #2a2a2a;
font-weight: bold;
font-size: 36rpx;
font-family: PingFang SC;
}
.level {
text-align: center;
width: 58rpx;
height: 33rpx;
background: #fff1eb;
border-radius: 4rpx;
font-size: 22rpx;
font-family: DIN;
font-weight: bold;
color: #ff540b;
margin-left: 10rpx;
}
}
}
.price-container {
font-family: DIN;
font-weight: bold;
color: #ff540b;
.name {
font-size: 24rpx;
font-family: PingFang SC;
color: #333333;
margin-right: 11rpx;
}
.symbol {
margin-right: 5rpx;
}
.price {
font-size: 42rpx;
}
.qi {
color: #666666;
font-size: 20rpx;
margin-left: 10rpx;
}
}
.oldPrice {
text-decoration: line-through;
color: #979797;
font-size: 24rpx;
font-weight: 400;
}

99
components/countdown-timer/countdown-timer.vue

@ -0,0 +1,99 @@
<template>
<view><slot :time="time" :remain="timeData.remain" :day="timeData.day" :hour="timeData.hour" :minute="timeData.minute" :second="timeData.second" /></view>
</template>
<script>
export default {
props: {
//
time: {
type: Number,
default: 0
},
//
'autoStart': {
type: Boolean,
default: false
}
},
data() {
return {
timer: null,
timeData: {
remain: 0,
day: 0,
hour: 0,
minute: 0,
second: 0
}
};
},
watch: {
time() {
this.reset()
}
},
methods: {
// timeData
updateTimeData() {
let t = this.timeData.remain;
this.timeData.day = Math.floor(t / 1000 / 60 / 60 / 24);
this.timeData.hour = Math.floor((t / 1000 / 60 / 60) % 24);
this.timeData.minute = Math.floor((t / 1000 / 60) % 60);
this.timeData.second = Math.floor((t / 1000) % 60);
},
//
startTimer() {
if (this.timer) {
clearInterval(this.timer);
}
if(this.timeData.remain < 1000) {
return
}
this.timer = setInterval(() => {
this.timeData.remain -= 1000;
this.updateTimeData()
if (this.timeData.remain < 1000) {
this.pause()
this.$emit('finish');
}
}, 1000);
},
//
reset() {
this.timeData.remain = this.time;
this.updateTimeData();
if(this.autoStart) {
this.start()
}
},
//
pause() {
if(this.timer) {
clearInterval(this.timer);
this.timer = null
}
},
//
start() {
if(this.timer) {
return
}
this.startTimer();
}
},
mounted() {
this.reset();
},
beforeDestroy() {
this.pause()
}
};
</script>

3492
components/css/animate.min.css

File diff suppressed because it is too large

23
components/css/iconfont.css

@ -0,0 +1,23 @@
@font-face {
font-family: 'iconfont';
src: url('https://at.alicdn.com/t/font_2248773_lp8msxycly.eot');
src: url('https://at.alicdn.com/t/font_2248773_lp8msxycly.eot?#iefix') format('embedded-opentype'),
url('https://at.alicdn.com/t/font_2248773_lp8msxycly.woff2') format('woff2'),
url('https://at.alicdn.com/t/font_2248773_lp8msxycly.woff') format('woff'),
url('https://at.alicdn.com/t/font_2248773_lp8msxycly.ttf') format('truetype'),
url('https://at.alicdn.com/t/font_2248773_lp8msxycly.svg#iconfont') format('svg');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 28rpx;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #666;
margin-left: 15rpx;
}
.icon-iconfront::before {
content: "\ue69c";
}

138
components/get-phone/index.vue

@ -0,0 +1,138 @@
<template>
<view class="user_info" v-if="isShow">
<view class="gray_con"></view>
<view class="user_info_con">
<image class="logo" src="/static/logo.png"></image>
<view class="logo_inner">
<view class="font_34 color_main">
<view>常熟小程序</view>
<view>微信授权获取手机号码</view>
</view>
</view>
<button hover-class="none" @getphonenumber="getPhone" open-type="getPhoneNumber" class="content_btn_con">
<view class="main_content_btn">授权</view>
</button>
</view>
</view>
</template>
<script>
export default {
props: {
isClose:{
type: Boolean,
default:true
}
},
data() {
return {
isShow:false
};
},
mounted() {},
methods: {
showAlert(){
this.isShow = true;
},
hideAlert(){
this.isShow = false;
},
getPhone(data) {
let that = this;
let loginInfo = that.$util.getLoginInfo();
if (data.detail.errMsg == "getPhoneNumber:ok") {
that.$doPost('WX.aspx', {
encryptedData: data.detail.encryptedData,
iv: data.detail.iv,
mod:"getPhone"
}).then(res => {
if (res.data.resultcode==0) {
let phone = res.data.data.Phone;
loginInfo.hasPhone = true;
loginInfo.Phone = phone;
uni.setStorageSync(that.$constant.LOGIN_INFO, loginInfo);
that.$emit('success', loginInfo);
that.isShow = false;
}else{
that.$util.showToast(res.data.msg);
}
})
.catch(err => {
console.log('request fail', err);
});
}
}
}
};
</script>
<style lang="scss">
.user_info {
position: fixed;
width: 100%;
height: 100%;
z-index: 9999;
left: 0;
top: 0;
}
.gray_con {
width: 100%;
height: 100%;
z-index: 1;
background: rgba(0, 0, 0, 0.7);
position: absolute;
left: 0;
top: 0;
}
.user_info_con {
width: 75%;
position: absolute;
height: 580upx;
background-color: #fff;
border-radius: 30upx;
z-index: 2;
left: 12.5%;
top: 50%;
transform: translateY(-50%);
}
.close_con {
width: 80upx;
height: 80upx;
position: absolute;
right: 0;
top: 0;
}
.close_con .close_icon {
width: 24upx;
height: 26upx;
}
.logo {
width: 240upx;
height: 76upx;
margin: auto;
margin-top: 100upx;
margin-bottom: 0;
display: block;
}
.logo_title {
font-size: 28upx;
color: #000;
}
.logo_inner {
text-align: center;
margin-top:60upx;
margin-bottom:60upx;
line-height: 46upx;
}
.logo_txt {
margin-top: 40upx;
margin-bottom: 16upx;
}
.content_btn_con {
padding: 0 50upx;
margin-top: 70upx;
background: transparent;
border: none;
outline: none;
}
</style>

66
components/html/AI.vue

@ -0,0 +1,66 @@
<template>
<view class="AIbox" @click="nato()">
<image class="img" v-if="sex=='1'" :src="getImg('AI-ruirui.png')" mode=""></image>
<image class="img" v-if="sex=='2'||sex=='0'" :src="getImg('AI-anan.png')" mode=""></image>
</view>
</template>
<script>
var key;
var web = require('../utils/request.js');
export default {
data() {
return {
sex:'2' //012
};
},
mounted() {
// if(this.info!=''&&this.info!=null){
// console.log(11111)
// this.sex=this.info;
// }else{
// this.sex='2'
// }
key = uni.getStorageSync('allsex');
if(key==''){
this.sex='2';
}else{
if(key==1){
this.sex='2';
}else{
this.sex='1';
}
}
},
methods: {
getUrl(url) {
return 'https://ruianm.tour-ma.com' + url;
},
getImg(url) {
return 'https://ruianm.tour-ma.com/r/cms/www/xcx/img/' + url;
},
nato() {
uni.navigateTo({
url: '../AI/jqr'
});
}
}
};
</script>
<style>
.AIbox{
width: 132upx;
height: 200upx;
position: fixed;
bottom: 8%;
right: 4%;
z-index: 100;
}
.AIbox .img{
width: 100%;
height: 100%;
}
</style>

163
components/html/back.vue

@ -0,0 +1,163 @@
<template>
<view :class="[scrolltop?'backs1':'backs']" :style="{'padding-top':statusBarHeight+'px'}">
<view :class="['header_left_back',backgroundColor==true?'bgcolor':'']" @click="toback">
<image class="header_icon" v-if="!scrolltop" src="/static/zhouWei-navBar/icon_back_white.png" mode="aspectFit"></image>
<image class="header_icon" v-else src="/static/zhouWei-navBar/icon_back_black.png" mode="aspectFit"></image>
</view>
<view class="ctitle" v-if="scrolltop">{{title}}</view>
<view class="sskuang" v-if="info.shou" :style="{height:scrolltop?'80rpx':'100%'}">
<input :class="[scrolltop?'hinput':'winput']" type="text" value="" v-model="name" v-if="ssshow" @input="come"/>
<image :src="$getimg(scrolltop?'wfqy-icon-xss.png':'wfqy-icon-xss1.png')" @click="ss" mode="aspectFill"></image>
</view>
</view>
</template>
<script>
export default {
props: ['info'],
data() {
return {
//true--;false--
navFontColor: false,
backgroundColor: false,
name:'',
statusBarHeight:0,
ssshow:false,
scrolltop:false,
title:''
}
},
onLoad() {
},
mounted() {
//
this.statusBarHeight = uni.getSystemInfoSync()['statusBarHeight'];
console.log(this.statusBarHeight)
},
onReady() {
this.navFontColor = this.info.color;
if (this.info.bg) {
this.backgroundColor = this.info.bg;
}
},
watch:{
info(){
this.scrolltop=this.info.num;
this.title=this.info.name;
this.$forceUpdate();
}
},
methods: {
toback() {
uni.navigateBack();
},
ss(){
var that=this;
this.ssshow=!this.ssshow;
if(!this.ssshow){
this.name='';
this.$emit('getSsname',{
name:''
})
}
},
come(){
var that=this;
this.$emit('getSsname',{
name:that.name
})
}
}
}
</script>
<style lang="scss">
@function unit($num) {
@return $num+0upx;
}
.ctitle{
font-size: 36rpx;
color: #505050;
font-weight: 500;
width: 560rpx;
text-align: center;
}
.sskuang{
width: 465rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
height: 100%;
.hinput{
margin-right: 20rpx;
border: 1rpx solid rgba(0,0,0,0.1);
border-radius: 35rpx;
height: 70%;
color: #333;
padding: 0 20rpx;
background: rgba(0,0,0,0.1);
font-size: 24rpx;
width: 170rpx;
}
.winput{
margin-right: 20rpx;
border: 1rpx solid #fff;
border-radius: 35rpx;
height: 70%;
color: #fff;
padding: 0 20rpx;
background: rgba(255,255,255,0.2);
font-size: 24rpx;
width: 170rpx;
}
image{
width: 37rpx;
height: 36rpx;
}
}
.backs {
position: absolute;
left: 0;
// width: unit(70);
height: 88rpx;
z-index: 100;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.backs1{
position: fixed;
background: rgba(255,255,255,1);
/* top: 40rpx; */
left: 0;
height: 88rpx;
z-index: 100;
display: -webkit-box;
display: -webkit-flex;
display: flex;
flex-direction: row;
align-items: center;
justify-content: left;
width: 100%;
}
.header_left_back {
width: 70rpx;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.bgcolor {
border-radius: 50%;
background-color: #446A86;
}
.header_icon {
width: unit(18);
height: unit(34);
}
</style>

103
components/html/listnav.vue

@ -0,0 +1,103 @@
<template>
<view class="listnav">
<view v-for="(el,ind) in list" :class="['navli',info.ind==el.ind?'act':'']" @click="clickli(el.ind,el.url)">
<image class="icon" v-if="info.ind==el.ind" :src="getImg(el.icon)" mode="aspectFill" lazy-load="true"></image>
<view class="title">{{el.name}}</view>
</view>
<view class="navli">
<image class="lsimg" :src="getImg('ysxcx-img-ys.png')" mode="aspectFill" lazy-load="true"></image>
</view>
</view>
</template>
<script>
export default {
props:["info"],
data() {
return {
list:[
{ind:0,name:'虞·赏味',url:'ctgid=1&ind=0',icon:'ysxcx-dhlm1.png'},
{ind:1,name:'虞·深味',url:'ctgid=2&ind=1',icon:'ysxcx-dhlm2.png'},
{ind:2,name:'虞·民宿',url:'ctgid=164&ind=2',icon:'ysxcx-dhlm2.png'},
{ind:3,name:'虞·趣味',url:'id=78&ind=3',icon:'ysxcx-dhlm3.png'},
{ind:4,name:'虞·滋味',url:'ctgid=3&ind=4',icon:'ysxcx-dhlm4.png'},
{ind:5,name:'虞·韵味',url:'ctgid=162&ind=5',icon:'ysxcx-dhlm5.png'},
{ind:6,name:'虞·风味',url:'ctgid=173&ind=6',icon:'ysxcx-dhlm6.png'}
]
};
},
onLoad() {
},
methods: {
getUrl(url) {
return 'https://cs.tour-ma.com/' + url;
},
getImg(url) {
return 'https://cs.tour-ma.com/r/cms/www/m/yushan/' + url;
},
clickli(ind,url){
this.$emit('getId',{
url:url
})
// uni.navigateTo({
// url:url
// })
}
}
};
</script>
<style>
.listnav{
width: 88upx;
height: 100%;
display: flex;
flex-direction: column;
position: fixed;
top: 0;
left: 0;
}
.listnav .navli{
width: 100%;
height: 170upx;
background: url(https://cs.tour-ma.com/r/cms/www/m/yushan/ysxcx-img-dhk00.jpg) no-repeat;
background-size: 100% 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.listnav .navli.act{
height: 213upx;
background: url(https://cs.tour-ma.com/r/cms/www/m/yushan/ysxcx-img-dhk2.jpg) no-repeat;
background-size: 100% 100%;
}
.listnav .navli .icon{
width: 30upx;
height: 30upx;
margin-left: 20upx;
border-radius: 50%;
margin-bottom: 10upx;
}
.listnav .navli .title{
color: #7A7671;
font-size: 26upx;
width: 26upx;
margin: 0 auto;
line-height: 32upx;
text-align: center;
margin-left: 40upx;
font-family: 'TpldKhangXiDictTrial';
}
.listnav .navli.act .title{
color: #FFFFFF;
}
.listnav .navli .lsimg{
width: 24upx;
height: 157upx;
margin-left: 40upx;
}
</style>

99
components/html/tabBar.vue

@ -0,0 +1,99 @@
<template>
<cover-view class="tabbarbox">
<cover-view class="tablist" v-for="(el,ind) in list" @click="getpath(el.pagePath)" :key="ind">
<cover-image class="tbimg" :src="info.ind==el.index?el.selectedIconPath:el.iconPath" mode="aspectFill"
lazy-load="true"></cover-image>
<cover-view :class="['tbtext',info.ind==el.index?'act':'']">{{el.text}}</cover-view>
</cover-view>
</cover-view>
</template>
<script>
export default {
props: ['info'],
data() {
return {
list: [{
"pagePath": "/pages/index",
"iconPath": "https://cs.tour-ma.com/r/cms/www/m/changshu/ysxcx-icon-sy0.png",
"selectedIconPath": "https://cs.tour-ma.com/r/cms/www/m/changshu/ysxcx-icon-sy1.png",
"text": "首页",
"index": 0
}, {
"pagePath": "/pages/dydl",
"iconPath": "https://cs.tour-ma.com/r/cms/www/m/changshu/ysxcx-icon-dt0.png",
"selectedIconPath": "https://cs.tour-ma.com/r/cms/www/m/changshu/ysxcx-icon-dt1.png",
"text": "文体旅地图",
"index": 1
},
// {
// "pagePath": "/pages/tab/index/index",
// "iconPath": "https://cs.tour-ma.com/r/cms/www/m/changshu/icon-cgyy0.png",
// "selectedIconPath": "https://cs.tour-ma.com/r/cms/www/m/changshu/icon-cgyy1.png",
// "text": "",
// "index": 2
// },
{
"pagePath": "/pages/tab/my/index",
"iconPath": "https://cs.tour-ma.com/r/cms/www/m/changshu/ysxcx-icon-wd0.png",
"selectedIconPath": "https://cs.tour-ma.com/r/cms/www/m/changshu/ysxcx-icon-wd1.png",
"text": "我的",
"index": 2
}]
}
},
onLoad() {
},
onReady() {
},
methods: {
getpath(url) {
console.log(url)
uni.reLaunch({
url: url
})
}
}
}
</script>
<style lang="scss">
.tabbarbox {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 120upx;
background-color: #FFFFFF;
display: flex;
z-index: 999999;
justify-content: space-evenly;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
align-items: center;
.tablist {
width: 25%;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
.tbimg {
width: 62upx;
height: 62upx;
margin: 5upx auto;
}
.tbtext {
font-size: 26upx;
color: #666666;
}
.act {
color: #999999;
}
}
}
</style>

BIN
components/invinbg-image-cropper/.DS_Store

Binary file not shown.

759
components/invinbg-image-cropper/invinbg-image-cropper.vue

@ -0,0 +1,759 @@
<template>
<view class="vue-cropper" ref="cropper" :style="{ top : `${containerTop}px` }" v-show="show">
<view class="cropper-box">
<view class="cropper-box-canvas" @touchstart.stop.prevent="imgTouchStart" @touchmove.stop.prevent="imgMoveing" @touchend.stop.prevent="imgMoveEnd" :style="{
'width': imageWidth + 'px',
'height': imageHeight + 'px',
'transform': 'scale(' + scale + ',' + scale + ') ' + 'translate3d('+ x / scale + 'px,' + y / scale + 'px,' + '0)'
+ 'rotateZ('+ rotate * 90 +'deg)'
}">
<image :src="src" alt="cropper-img" ref="cropperImg" mode="scaleToFill" class="uni-image"></image>
</view>
</view>
<view class="cropper-drag-box cropper-modal cropper-move pointer-events"></view>
<view class="cropper-crop-box" :class="{'pointer-events': cropFixed}" :style="{'width': cropW + 'px','height': cropH + 'px','transform': 'translate3d('+ cropOffsertX + 'px,' + cropOffsertY + 'px,' + '0)'}">
<view class="cropper-view-box">
<image :style="{'width': imageWidth + 'px','height': imageHeight + 'px','transform': 'scale(' + scale + ',' + scale + ') ' + 'translate3d('+ (x - cropOffsertX) / scale + 'px,' + (y - cropOffsertY) / scale + 'px,' + '0)' + 'rotateZ('+ rotate * 90 +'deg)'}" mode="scaleToFill" :src="src" alt="cropper-img"></image>
</view>
<view v-if="!cropFixed" class="cropper-face cropper-move" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="cropMoveing"></view>
<view class="crop-line line-w"></view>
<view class="crop-line line-a"></view>
<view class="crop-line line-s"></view>
<view class="crop-line line-d"></view>
<block v-if="!cropFixed">
<view class="crop-point point-lt" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'left-top')"></view>
<view class="crop-point point-mt" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'middle-top')"></view>
<view class="crop-point point-rt" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'right-top')"></view>
<view class="crop-point point-ml" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'middle-left')"></view>
<view class="crop-point point-mr" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'middle-right')"></view>
<view class="crop-point point-lb" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'left-bottom')"></view>
<view class="crop-point point-mb" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'middle-bottom')"></view>
<view class="crop-point point-rb" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'right-bottom')"></view>
</block>
</view>
<canvas canvas-id="myCanvas" class="cropper-canvas" :style="{ 'width': cropW + 'px','height': cropH + 'px' }"></canvas>
<view class="btn-group">
<view class="btn-item reset-btn" v-show="showResetBtn" @tap="init"></view>
<view class="btn-item rotate-btn" v-show="showRotateBtn" @tap="rotateHandler"></view>
</view>
<view class="uni-info__ft">
<view class="uni-modal__btn uni-modal__btn_default" style="color: rgb(0, 0, 0);" @tap="cancel">取消</view>
<view class="uni-modal__btn uni-modal__btn_primary" style="color: rgb(0, 122, 255);" @tap="confirm">确定</view>
</view>
</view>
</template>
<script>
export default {
name: 'image-cropper',
props: {
cropWidth: {
type: Number,
default: 200,
},
cropHeight: {
type: Number,
default: 200
},
cropFixed: {
type: Boolean,
default: false,
},
src: {
type: String,
},
showResetBtn: {
type: Boolean,
default: true,
},
showRotateBtn: {
type: Boolean,
default: true,
}
},
data() {
const sysInfo = uni.getSystemInfoSync();
const pixelRatio = sysInfo.pixelRatio
return {
show: false,
scale: 1,
rotate: 0,
cropW: 0,
cropH: 0,
cropOldW: 0,
cropOldH: 0,
sysInfo: sysInfo,
pixelRatio: pixelRatio,
imageRealWidth: 0,
imageRealHeight: 0,
cropOffsertX: 0,
cropOffsertY: 0,
startX: 0,
startY: 0,
//
border: 5,
x: 0,
y: 0,
startL: 0,
oldScale: 1,
}
},
watch: {
src(val) {
// if(val.length > 0) {
// this.init()
// }
},
show(val) {
if(!val) {
this.src = ''
}
}
},
computed: {
containerTop() {
let top = 0
// #ifdef H5
top = 44
// #endif
return top;
},
//
containerHeight() {
return this.windowHeight - 48;
},
//
windowWidth() {
return this.sysInfo.windowWidth;
},
windowHeight() {
return this.sysInfo.windowHeight;
},
//
imageRatio() {
if (this.imageRealHeight > 0) {
return this.imageRealWidth / this.imageRealHeight
}
return 0
},
//
imageWidth() {
if (this.imageRatio >= 1) {
return this.windowWidth
}
return this.windowWidth * this.imageRatio
},
//
imageHeight() {
if (this.imageRatio >= 1) {
return this.windowWidth / this.imageRatio
}
return this.windowWidth
},
},
methods: {
rotateHandler() {
if(this.rotate == 3) {
this.rotate = 0;
} else {
++this.rotate
}
},
init() {
this.rotate = 0;
this.scale = 1;
this.cropW = this.cropWidth
this.cropH = this.cropHeight
uni.showLoading({
title: 'Se încarcă...',
})
this.loadImage(this.src).then((e) => {
uni.hideLoading()
}).catch((e) => {
uni.hideLoading()
uni.showModal({
title: 'prompt',
content: 'Imaginea nu a putut fi încărcată'
})
})
},
loadImage(src) {
const _this = this
return new Promise((resolve, reject) => {
uni.getImageInfo({
src: src,
success: (res) => {
_this.imageRealWidth = res.width
_this.imageRealHeight = res.height
_this.cropOffsertX = _this.windowWidth / 2 - _this.cropW / 2
_this.cropOffsertY = _this.windowHeight / 2 - _this.cropH / 2
_this.show = true
_this.$nextTick(() => {
_this.x = _this.windowWidth / 2 - _this.imageWidth / 2
_this.y = _this.containerHeight / 2 - _this.imageHeight / 2
});
resolve(res)
},
fail: (e) => {
_this.show = false
reject(e)
}
})
});
},
cancel() {
this.show = false
this.$emit('cancel')
},
confirm(event) {
uni.showLoading({
title: 'Tăiere...',
})
const _this = this
const ctx = uni.createCanvasContext('myCanvas', _this);
const pixelRatio = _this.pixelRatio
const imgage = _this.src
const imgW = _this.imageWidth * _this.scale;
const imgH = _this.imageHeight * _this.scale
const rotate = _this.rotate
let dx = _this.cropOffsertX - _this.x - (_this.imageWidth - imgW) / 2;
let dy = _this.cropOffsertY - _this.y - (_this.imageHeight - imgH) / 2;
ctx.setFillStyle('white')
ctx.fillRect(0, 0, imgW, imgH)
ctx.save()
ctx.rotate((rotate * 90 * Math.PI) / 180);
switch (rotate) {
case 1:
dx += (imgH-imgW) / 2
dy -= (imgH-imgW) / 2
ctx.drawImage(imgage, -dy, dx, imgW, -imgH);
break;
case 2:
ctx.drawImage(imgage, dx, dy, -imgW, -imgH);
break;
case 3:
dx += (imgH-imgW) / 2
dy -= (imgH-imgW) / 2
ctx.drawImage(imgage, dy, -dx, -imgW, imgH);
break;
default:
ctx.drawImage(imgage, -dx, -dy, imgW, imgH);
break;
}
ctx.restore()
ctx.draw(false, () => {
uni.canvasToTempFilePath({
canvasId: 'myCanvas',
destWidth: _this.cropW * pixelRatio,
destHeight: _this.cropH * pixelRatio,
success: (res) => {
uni.hideLoading()
event.detail.tempFilePath = res.tempFilePath
_this.show = false
_this.$emit('confirm', event)
},
fail: (e) => {
uni.hideLoading()
uni.showModal({
title: 'prompt',
content: 'Eșecul culturilor'
})
}
}, _this);
})
},
imgTouchStart(e) {
if(e.touches.length == 2) {
this.oldScale = this.scale
this.scaling = true
const x = e.touches[0].pageX - e.touches[1].pageX
const y = e.touches[0].pageY - e.touches[1].pageY
const hypotenuse = Math.sqrt(
Math.pow(x, 2) +
Math.pow(y, 2)
)
this.startL = Math.max(x, y, hypotenuse)
uni.showModal({
content: this.startL
})
} else {
this.startX = e.touches[0].pageX - this.x
this.startY = e.touches[0].pageY - this.y
}
},
imgMoveing(e) {
if(this.scaling) {
let scale = this.oldScale
const x = e.touches[0].pageX - e.touches[1].pageX
const y = e.touches[0].pageY - e.touches[1].pageY
const hypotenuse = Math.sqrt(
Math.pow(x, 2) +
Math.pow(y, 2)
)
const newL = Math.max(x, y, hypotenuse)
const cha = newL - this.startL;
// ,
// 1px - 0.2
let coe = 1;
coe =
coe / this.imageWidth > coe / this.imageHeight
? coe / this.imageHeight
: coe / this.imageWidth;
coe = coe > 0.1 ? 0.1 : coe;
const num = coe * cha;
if (cha > 0) {
scale += Math.abs(num);
} else if (cha < 0) {
scale > Math.abs(num) ? (scale -= Math.abs(num)) : scale;
}
this.scale = scale;
} else {
const moveX = e.touches[0].pageX - this.startX
const moveY = e.touches[0].pageY - this.startY
this.x = moveX
this.y = moveY
}
},
imgMoveEnd() {
setTimeout(() => {
this.scaling = false
}, 100)
},
touchStart(e) {
this.startX = e.touches[0].pageX - this.cropOffsertX;
this.startY = e.touches[0].pageY - this.cropOffsertY;
this.cropOldW = this.cropW
this.cropOldH = this.cropH
},
cropMoveing(e) {
const moveX = this._cropX(e.touches[0].pageX - this.startX)
const moveY = this._cropY(e.touches[0].pageY - this.startY)
this.cropOffsertX = moveX
this.cropOffsertY = moveY
},
dragMove(e, type) {
if(this.cropFixed) {
return false
}
const moveX = e.touches[0].pageX - this.startX
const moveY = e.touches[0].pageY - this.startY
switch (type) {
case 'left-top':
this._cropMoveLeft(moveX)
this._cropMoveTop(moveY)
break;
case 'middle-top':
this._cropMoveTop(moveY)
break;
case 'right-top':
this._cropMoveTop(moveY)
this._cropMoveRight(moveX)
break;
case 'middle-right':
this._cropMoveRight(moveX)
break;
case 'right-bottom':
this._cropMoveRight(moveX)
this._cropMoveBottom(moveY)
break;
case 'middle-bottom':
this._cropMoveBottom(moveY)
break;
case 'left-bottom':
this._cropMoveBottom(moveY)
this._cropMoveLeft(moveX)
break;
case 'middle-left':
this._cropMoveLeft(moveX)
break;
default:
break;
}
},
_cropMoveTop(y) {
const topY = this._cropY(y)
this.cropH += this.cropOffsertY - topY
this.cropOffsertY = topY
},
_cropMoveRight(x) {
if(this.cropOldW + x >= this.windowWidth - this.border) {
return false;
}
this.cropW = this.cropOldW + (x - this.cropOffsertX)
},
_cropMoveBottom(y) {
if(this.cropOldH + y >= this.windowHeight - this.containerTop - this.border) {
return false;
}
this.cropH = this.cropOldH + (y - this.cropOffsertY)
},
_cropMoveLeft(x) {
const leftX = this._cropY(x)
this.cropW += this.cropOffsertX - leftX
this.cropOffsertX = leftX
},
_cropX(x) {
if(x <= this.border) {
return this.border
}
if(x + this.cropW >= this.windowWidth - this.border) {
return this.windowWidth - this.cropW - this.border
}
return x
},
_cropY(y) {
if(y <= this.border) {
return this.border
}
if(y + this.cropH >= this.windowHeight - this.containerTop - this.border) {
return this.windowHeight - this.cropH - this.containerTop - this.border
}
return y
}
}
}
</script>
<style scoped lang="css">
@font-face {
font-family: "iconfont";
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAR4AAsAAAAACKgAAAQsAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDBgqEfIRGATYCJAMMCwgABCAFhG0HShugB8gOJUHBwAAAAAFEBNmwzd4dtatSmmpFoVAEhUThEAYkCozFKDCqCVO6RfH/89v869awDnTR1qrSANFt4GG4SNxreBn91fmV9f3+53J613ieHba+N1zmGM8PA7oXTaCAxpjei8IoLWFsGLu4jPME6vWJJdovqmgAO4U2LRBnep0K7GJmpYQWanXVOWuLuAFrtenK4haAa/f38QnKsCOpyrRFh6eFWsh5KXnfYcn958BGQNKfE8wmMmaAQpzkuo9Z+ukZluoltVV5abUipL5i/ysArlhWVut/eCRBVNPUjYg6oUo7JTHFoaYDSvdacnKTq9GAB4AY5y2dtL3qpFh1DENdnJC6Hq+xYb7pyRMDMzc/fYoJjY8flwO3m98rMucF+IZHj6Cagw5UeKpxyFbt2rHGY/8jpa7CYMvLfcIesLjY3bdqhaf+nqgQs2qT/+rjCH/VfA0VFGuAC3iE8NEr/Vau8vZsXiUy7+V3c3tQQXMAuNjDCC89KDIHH0OFhnUi81GEPwyc7wZUaN7DnUf4g+ZLQsMKYV/94NjK7R7TEM4niTY1oJ5zEU62aNVaasUub08YLUEam5EnT6a61/I17dNk+vTu9jpJjXhsTFwjqTtpCBxBIIgS6iQnc/Zod1YGKp0rAwsD8kkyP6AwcK0hcAwkiQmBhWvxPZWKDu86aUH2nLEdi9rGX1eXq5P6A1SrnAucMVMdZH/GKi/jyfCqJyucfK3mXpVujXOPfFf5LC4Dvx0X/943JyOq4HuCTZ8KiIPPAb6ro8akpT6ufiq39BQrNlk5mp8pO0JlJLk8f5QalRjoP60IMx0N8n7wGhSD3n6/F1zlcTVz/cR+Ev0lkLSTd7UiPbD/wCxGRMA2Krwro2O0bTQtImbwhjAJc0S3N4ROx15/PH60IzaIOjCbEelqkDOfETNxb/FMixnWNzeJp2KPQw9A5d76jGUOQOUvH7RE/o2RfkNatd3OGf9q0QKbnq8WB7qy+hVqJRjJn1BQgP/iErks0yy5iGJTrOayW7C/z0IoZH0qNH+7N+31XXc7G2p1hZDU6IWs1ghaqDNQpcEKVKu1BfWmFW9u0IFhKUodpswCEFodgqTZHWStbqOF+hqqdPsG1VrDEuodhfueDcZCj+QzuIrFtZh6BNNraIowbCzi1dbhOlOfionKXHoTzgzoY5hCKk/minEKZ/pYMDCoU7IsgREM3Y8Vgcvwvj4aMzK0AdewUpJljWkyGZH3IKmG7gfEHgZOhYXTwqiNwOhp0CiE3ZiFpL5fB6dj0keFKcGV+JvgGAP0vWMUpOQ10GI1VQt3LoMHDNJRYrEIPInAoPXDFEEnrk9P0zDG/FEGOA2WFNkiaZRGhuoRddXS8bX917cL6mn9c6TIUXSekybKHKQfJXFq2KSiRklLYU8dNKWDIX0cAA==') format('woff2');
}
.vue-cropper {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 998;
box-sizing: border-box;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
direction: ltr;
touch-action: none;
text-align: left;
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC");
}
.cropper-canvas {
position: absolute;
top: -9999px;
left:-9999px;
z-index: -998;
}
.vue-cropper .uni-info__ft {
position: absolute;
line-height: 48px;
font-size: 18px;
display: -webkit-box;
display: -webkit-flex;
display: flex;
bottom: 0;
left: 0;
right: 0;
z-index: 998;
}
.btn-group {
position: absolute;
right: 30px;
bottom: 78px;
z-index: 998;
}
.btn-item {
position: relative;
width: 40px;
height: 40px;
background: #fff;
border-radius: 20px;
padding: 10px;
display: inline-block;
margin-left: 10px;
}
.btn-item:active {
background: #ccc;
}
.rotate-btn {
font-family: "iconfont" !important;
font-size: 24px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
line-height: 20px;
}
.rotate-btn:before {
content: "\e65c";
margin-left: -2px;
}
.reset-btn {
font-family: "iconfont" !important;
font-size: 24px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
line-height: 20px;
}
.reset-btn:before {
content: "\e648";
margin-left: -2px;
}
.vue-cropper .uni-info__ft:after {
content: " ";
position: absolute;
left: 0;
top: 0;
right: 0;
height: 1px;
border-top: 1px solid #d5d5d6;
color: #d5d5d6;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
-webkit-transform: scaleY(.5);
transform: scaleY(.5);
z-index: 998;
}
.vue-cropper .uni-modal__btn {
display: block;
-webkit-box-flex: 1;
-webkit-flex: 1;
flex: 1;
color: #3cc51f;
text-decoration: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
position: relative;
text-align: center;
background-color: #fff;
z-index: 998;
}
.vue-cropper .uni-modal__btn:first-child:after { display: none }
.vue-cropper .uni-modal__btn:after {
content: " ";
position: absolute;
left: 0;
top: 0;
width: 1px;
bottom: 0;
border-left: 1px solid #d5d5d6;
color: #d5d5d6;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
-webkit-transform: scaleX(.5);
transform: scaleX(.5);
z-index: 998;
}
.vue-cropper .uni-modal__btn:active {
background-color: #eee;
}
.cropper-box,
.cropper-box-canvas,
.cropper-drag-box,
.cropper-crop-box,
.cropper-face {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
user-select: none;
z-index: 998;
}
.uni-image {
width: 100%;
height: 100%;
}
.cropper-box-canvas image {
position: relative;
text-align: left;
user-select: none;
transform: none;
max-width: none;
max-height: none;
z-index: 998;
}
.cropper-box {
overflow: hidden;
}
.cropper-move {
cursor: move;
}
.cropper-crop {
cursor: crosshair;
}
.cropper-modal {
background: rgba(0, 0, 0, 0.5);
}
.pointer-events {
pointer-events:none;
}
.cropper-crop-box {
/*border: 2px solid #39f;*/
}
.cropper-view-box {
display: block;
overflow: hidden;
width: 100%;
height: 100%;
outline: 1px solid #39f;
outline-color: rgba(51, 153, 255, 0.75);
user-select: none;
}
.cropper-view-box image {
user-select: none;
text-align: left;
max-width: none;
max-height: none;
}
.cropper-face {
top: 0;
left: 0;
background-color: #fff;
opacity: 0.1;
}
.crop-line {
position: absolute;
display: block;
width: 100%;
height: 100%;
opacity: 0.1;
z-index: 998;
}
.line-w {
top: -3px;
left: 0;
height: 5px;
cursor: n-resize;
}
.line-a {
top: 0;
left: -3px;
width: 5px;
cursor: w-resize;
}
.line-s {
bottom: -3px;
left: 0;
height: 5px;
cursor: s-resize;
}
.line-d {
top: 0;
right: -3px;
width: 5px;
cursor: e-resize;
}
.crop-point {
position: absolute;
width: 8px;
height: 8px;
opacity: 0.75;
background-color: #39f;
border-radius: 100%;
z-index: 998;
}
.point-lt {
top: -4px;
left: -4px;
cursor: nw-resize;
}
.point-mt {
top: -5px;
left: 50%;
margin-left: -3px;
cursor: n-resize;
}
.point-rt {
top: -4px;
right: -4px;
cursor: ne-resize;
}
.point-ml {
top: 50%;
left: -4px;
margin-top: -3px;
cursor: w-resize;
}
.point-mr {
top: 50%;
right: -4px;
margin-top: -3px;
cursor: e-resize;
}
.point-lb {
bottom: -5px;
left: -4px;
cursor: sw-resize;
}
.point-mb {
bottom: -5px;
left: 50%;
margin-left: -3px;
cursor: s-resize;
}
.point-rb {
bottom: -5px;
right: -4px;
cursor: se-resize;
}
</style>

488
components/lb-picker/README.md

@ -0,0 +1,488 @@
<p align="center">
<a href="https://github.com/liub1934/uni-lb-picker">
<img src="https://img.shields.io/github/stars/liub1934/uni-lb-picker">
</a>
<a href="https://github.com/liub1934/uni-lb-picker/fork">
<img src="https://img.shields.io/github/forks/liub1934/uni-lb-picker">
</a>
<a href="https://github.com/liub1934/uni-lb-picker/issues">
<img src="https://img.shields.io/github/issues/liub1934/uni-lb-picker">
</a>
<a href="https://www.npmjs.com/package/uni-lb-picker">
<img src="https://img.shields.io/npm/v/uni-lb-picker">
</a>
<a href="https://npmcharts.com/compare/uni-lb-picker?minimal=true">
<img src="https://img.shields.io/npm/dm/uni-lb-picker">
</a>
<a href="https://standardjs.com">
<img src="https://img.shields.io/badge/code%20style-standard-brightgreen">
</a>
<a href="https://github.com/liub1934/uni-lb-picker/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/liub1934/uni-lb-picker">
</a>
</p>
插件市场里面的 picker 选择器不满足自己的需求,所以自己写了一个简单的 picker 选择器,可扩展、可自定义,一般满足日常需要。
Github:[uni-lb-picker](https://github.com/liub1934/uni-lb-picker)
插件市场:[uni-lb-picker](https://ext.dcloud.net.cn/plugin?id=1111)
H5 Demo:[点击预览](https://github.liubing.me/uni-lb-picker)
> 如果问题最好去 github 反馈,插件市场评论区留下五星好评即可,[点我去反馈](https://github.com/liub1934/uni-lb-picker/issues/new)
> **由于之前`cancel`拼写失误,写成了`cancle`,`v1.08`现已修正,如果之前版本有使用`cancel`事件的,更新后请及时修正。**
## 兼容性
App + Nvue + H5 + 各平台小程序(快应用及 360 未测试)
## 功能
1、单选
2、多级联动,非多级联动,理论支持任意级数
3、省市区选择,基于多级联动
4、自定义选择器头部确定取消按钮颜色及插槽支持
5、选择器可视区自定义滚动个数
6、自定义数据字段,满足不同人的需求
7、自定义选择器样式
8、formatter 格式化自定义显示
9、单选及非联动选择支持扁平化的简单数据,如下形式:
```javascript
// 单选列表
list1: ['选项1', '选项2', '选项2'],
// 非联动选择列表
list2: [
['选项1', '选项2', '选项3'],
['选项11', '选项22', '选项33'],
['选项111', '选项222', '选项333']
]
```
## 引入插件
单独引入,在需要使用的页面上 import 引入即可
```html
<template>
<view>
<lb-picker></lb-picker>
</view>
</template>
<script>
import LbPicker from '@/components/lb-picker'
export default {
components: {
LbPicker
}
}
</script>
```
全局引入,`main.js`中 import 引入并注册即可全局使用
```jsvascript
import LbPicker from '@/components/lb-picker'
Vue.component("lb-picker", LbPicker)
```
easycom 引入
`pages.json`加上如下配置:
```json
"easycom": {
"autoscan": true,
"custom": {
"lb-picker": "@/components/lb-picker/index.vue"
}
}
```
npm 安装引入:
```shell
npm install uni-lb-picker
```
```jsvascript
import LbPicker from 'uni-lb-picker'
```
## 选择器数据格式
### 单选
常规数据
```javascript
list: [
{
label: '选项1',
value: '1'
},
{
label: '选项2',
value: '2'
}
]
```
扁平化简单数据
```javascript
list: ['选项1', '选项2']
```
### 多级联动
```javascript
list: [
{
label: '选项1',
value: '1',
children: [
{
label: '选项1-1',
value: '1-1',
children: [
{
label: '选项1-1-1',
value: '1-1-1'
}
]
}
]
}
]
```
### 非联动选择
常规数据
```javascript
list: [
[
{ label: '选项1', value: '1' },
{ label: '选项2', value: '2' },
{ label: '选项3', value: '3' }
],
[
{ label: '选项11', value: '11' },
{ label: '选项22', value: '22' },
{ label: '选项33', value: '33' }
],
[
{ label: '选项111', value: '111' },
{ label: '选项222', value: '222' },
{ label: '选项333', value: '333' }
]
]
```
扁平化简单数据
```javascript
list: [
['选项1', '选项2', '选项3'],
['选项11', '选项22', '选项33'],
['选项111', '选项222', '选项333']
]
```
## 调用显示选择器
通过`ref`形式手动调用`show`方法显示,隐藏同理调用`hide`
```html
<lb-picker ref="picker"></lb-picker>
```
```javascript
this.$refs.picker.show() // 显示
this.$refs.picker.hide() // 隐藏
```
`v1.1.3`新增,将需要点击的元素包裹在`lb-picker`中即可。
```html
<lb-picker>
<button>点我直接打开选择器</button>
</lb-picker>
```
## 绑定值及设置默认值
支持 vue 中`v-model`写法绑定值,无需自己维护选中值的索引。
```javascript
<lb-picker v-model="value1"></lb-picker>
<lb-picker v-model="value2"></lb-picker>
data () {
return {
value1: '' // 单选
value2: [] // 多列联动选择
}
}
```
## 多个选择器
通过设置不同的`ref`,然后调用即可
```javascript
<lb-picker ref="picker1"></lb-picker>
<lb-picker ref="picker2"></lb-picker>
this.$refs.picker1.show() // picker1显示
this.$refs.picker2.show() // picker2显示
```
## 省市区选择
省市区选择是基于多列联动选择,数据来源:[https://github.com/modood/Administrative-divisions-of-China](https://github.com/modood/Administrative-divisions-of-China),
省市区文件位于`/pages/demos/area-data-min.js`,自行引入即可,可参考`demo3省市区选择`,
也可使用自己已有的省市区数据,如果数据字段不一样,也可以自定义,参考下方自定义数据字段。
## 自定义数据字段
为了满足不同人的需求,插件支持自定义数据字段名称, 插件默认的数据字段如下形式:
```javascript
list: [
{
label: '选择1',
value: 1,
children: []
},
{
label: '选择1',
value: 1,
children: []
}
]
```
如果你的数据字段和上面不一样,如下形式:
```javascript
list: [
{
text: '选择1',
id: 1,
child: []
},
{
text: '选择1',
id: 1,
child: []
}
]
```
通过设置参数中的`props`即可,如下所示:
```javascript
<lb-picker :props="myProps"></lb-picker>
data () {
return {
myProps: {
label: 'text',
value: 'id',
children: 'child'
}
}
}
```
## 插槽使用
选择器支持一些可自定义化的插槽,如选择器的取消和确定文字按钮,如果需要对其自定义处理的话,比如加个 icon 图标之类的,可使用插槽,使用方法如下:
```html
<lb-picker>
<view slot="cancel-text">我是自定义取消</view>
<view slot="confirm-text">我是自定义确定</view>
</lb-picker>
```
也可参考示例中的`demo5`,自定义插槽元素样式交给开发者自由调整,插槽仅提供预留位置。
其他插槽见下。
## 参数及事件
### Props
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
| :--------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------- | :--------------------------------------------------------------- | :------------------------------------------------ |
| value/v-model | 绑定值,联动选择为 Array 类型 | String/Number/Array | - | - |
| mode | 选择器类型,支持单列,多列联动 | String | selector 单选/multiSelector 多级联动/unlinkedSelector 多级非联动 | selector |
| list | 选择器数据(v1.0.7 单选及非联动多选支持扁平数据:['选项 1', '选项 2']) | Array | - | - |
| level | 多列联动层级,仅 mode 为 multiSelector 有效 | Number | - | 1 |
| props | 自定义数据字段 | Object | - | {label:'label',value:'value',children:'children'} |
| cancel-text | 取消文字 | String | - | 取消 |
| cancel-color | 取消文字颜色 | String | - | #999 |
| confirm-text | 确定文字 | String | - | 确定 |
| confirm-color | 确定文字颜色 | String | - | #007aff |
| empty-text | `(v1.0.7 新增)`选择器列表为空的时候显示的文字 | String | - | 暂无数据 |
| empty-color | `(v1.0.7 新增)`暂无数据文字颜色 | String | - | #999 |
| column-num | 可视滚动区域内滚动个数,最好设置奇数值 | Number | - | 5 |
| radius | 选择器顶部圆角,支持 rpx,如 radius="10rpx" | String | - | - |
| column-style | `(v1.1.6 重新新增)`选择器默认样式,仅`nvue`支持,其他端见下`选择器自定义样式`说明 | Object | - | - |
| active-column-style | `(v1.1.6 重新新增)`选择器选中样式,仅`nvue`支持,其他端见下`选择器自定义样式`说明 | Object | - | - |
| loading | 选择器是否显示加载中,可使用 loading 插槽自定义加载效果 | Boolean | - | - |
| mask-color | 遮罩层颜色 | String | - | rgba(0, 0, 0, 0.4) |
| show-mask | `(v1.1.0 新增)`是否显示遮罩层 | Boolean | true/false | true |
| close-on-click-mask | 点击遮罩层是否关闭选择器 | Boolean | true/false | true |
| ~~change-on-init~~ | ~~(v1.0.7 已弃用)初始化时是否触发 change 事件~~ | Boolean | true/false | - |
| dataset | `(v1.0.7 新增)`可以向组件中传递任意的自定义的数据(对象形式数据),如`:dataset="{name:'test'}"`,在`confirm`或`change`事件中可以取到 | Object | - | - |
| show-header | `(v1.0.8 新增)`是否显示选择器头部 | Boolean | - | true |
| inline | `(v1.0.8 新增)`inline 模式,开启后默认显示选择器,无需点击弹出,可以配合`show-header`一起使用 | Boolean | - | - |
| z-index | `(v1.0.9 新增)`选择器层级,遮罩层默认-1 | Number | - | 999 |
| safe-area-inset-bottom | `(v1.1.4 新增)`是否留出底部安全距离(nvue 无效) | Boolean | true/false | true |
| disabled | `(v1.1.4 新增)`是否禁用选择器,禁用后无法弹出选择器 | Boolean | - | - |
| align | `(v1.1.6 新增)`选择器中文字对齐方式,默认居中 | String | left/center/right | center |
| press-enable | `(v1.1.6 新增)`是否开启长按选择器数据`showtoast`弹出`label`提示,部分情况下选择器数据文字过长会显示省略号,如需查看完整的文字内容,可开启此选项,长按后会`showtoast`弹出完整的文字内容,默认不开启(支付宝小程序暂不支持[详情](https://ask.dcloud.net.cn/question/106237)) | Boolean | - | - |
| press-time | `(v1.1.6 新增)`长按触发时间,单位毫秒 ms | Number | - | 500 |
| formatter | `(v1.1.7 新增)`格式化自定义选择器文字内容,`Function`类型,`return`一个字符串(仅`app` `nvue` `h5`支持),`item`当前项信息,`rowIndex`当前数据所在行数,`columnIndex`当前数据所在列数,写法参考[demo14](https://github.com/liub1934/uni-lb-picker/blob/master/pages/demos/demo14/demo14.vue#L206) | Function({ item, rowIndex, columnIndex }) | - | - |
### 方法
| 方法名 | 说明 | 参数 | 返回值 |
| :------------- | :------------------------------------- | :-------------- | :----------------------------------------------------------------------------------------------------------- |
| show | 打开选择器 | - | |
| hide | 关闭选择器 | - | |
| getColumnsInfo | (v1.1.0 新增)根据 value 获取选择器信息 | 绑定值的`value` | 同`change` `confirm`回调参数,如果传入的`value`获取不到信息则只返回一个含有`dataset`的对象,具体自行打印查看 |
### Events
| 事件名称 | 说明 | 回调参数 |
| :------- | :--------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| show | 选择器打开时触发 | - |
| hide | 选择器隐藏时触发 | - |
| change | 选择器滚动时触发,此时不会改变绑定的值 | `{ index, item, value, change }` `index`触发滚动后新的索引,单选时是具体的索引值,多列联动选择时为数组。`item`触发滚动后新的的完整内容,包括`label`、`value`等,单选时为对象,多列选择时为数组对象。`value`触发滚动后新的 value 值,单列选择时为具体值,多列联动选择时为数组。`change`触发事件的类型,详情参考下面的 change 事件备注 |
| confirm | 点击选择器确定时触发,此时会改变绑定的值 | 同上`change`事件说明 |
| cancel | 点击选择器取消时触发 | 同上`change`事件说明 |
### `change` 事件备注
如果绑定的值是空的,`change`触发后里面的内容都是列表的第一项。
`change`事件会在以下情况触发:
- 初始化
- 绑定值 value 变化
- 选择器 list 列表变化
- 滚动选择器
以上情况会在回调函数中都可以取到`change`变化的类型,对应上面的情况包括以下:
- `init`
- `value`
- `list`
- `scroll`
根据这些类型大家可以在`change`的时候按需处理自己的业务逻辑,`init`现在指挥在调用选择器弹出的时候触发。
下面的说明情况已失效,如需要在页面显示的时候根据`value`的值显示相应的中文,调用`v1.10`新增的方法`getColumnsInfo`,传入绑定的值即可获取到你想要的所有信息。
~~比如一种常见的情况,有默认值的时候需要显示默认值的文字,此时可以`change`事件中判断`change`的类型是否是`init`,如果是的话可以取事件回调中的`item`进行显示绑定值对应的文字信息。~~
```javascript
handleChange (e) {
if (e.change === 'init') {
console.log(e.item.label) // 单选 选项1
console.log(e.item.map(item => item.label).join('-')) // 多选 选项1-选项11
}
}
```
### 插槽
| 插槽名 | 说明 |
| :------------ | :--------------------- |
| cancel-text | 选择器取消文字插槽 |
| action-center | 选择器顶部中间插槽 |
| confirm-text | 选择器确定文字插槽 |
| loading | 选择器 loading 插槽 |
| empty | 选择器 空数据 插槽 |
| header-top | 选择器头部顶部插槽 |
| header-bottom | 选择器头部底部插槽 |
| picker-top | 选择器滚动部分顶部插槽 |
| picker-bottom | 选择器滚动部分底部插槽 |
### 选择器自定义样式
> nvue 专属写法,需要定义`column-style`和`active-column-style`,写法如下:
```html
<lb-picker
:column-style="columnStyle"
:active-column-style="activeColumnStyle"
></lb-picker>
```
```javascript
data () {
return {
// 默认样式
columnStyle: {
color: '#f0ad4e'
},
// 选择样式
activeColumnStyle: {
color: '#007aff',
fontWeight: 700
}
}
}
```
> 其他端写法,覆盖默认样式即可。
```css
<style lang="scss" scoped>
/deep/ .lb-picker {
.lb-picker-column-label {
color: #f0ad4e;
}
.lb-picker-column-active {
.lb-picker-column-label {
color: #007aff;
font-weight: 700;
}
}
}
</style>
```
完整代码可以参考`demo9`。
### 获取选中值的文字
`@confirm`事件中可以拿到:
单选:
```javascript
handleConfirm (e) {
console.log(e.item.label) // 选项1
}
```
联动选择:
```javascript
handleConfirm (e) {
console.log(e.item.map(item => item.label).join('-')) // 选项1-选项11
}
```
## Tips
微信小程序端,滚动时在 iOS 自带振动反馈,可在系统设置 -> 声音与触感 -> 系统触感反馈中关闭
## 其他
其他功能参考示例 Demo 代码。

392
components/lb-picker/index.vue

File diff suppressed because one or more lines are too long

93
components/lb-picker/mixins/index.js

@ -0,0 +1,93 @@
import { getColumns, isObject, isFunction } from '../utils'
export const commonMixin = {
data () {
return {
isConfirmChange: false,
indicatorStyle: `height: 34px`,
pressTimeout: null
}
},
created () {
this.init('init')
},
methods: {
init (changeType) {
if (this.list && this.list.length) {
const column = getColumns({
value: this.value,
list: this.list,
mode: this.mode,
props: this.props,
level: this.level
})
const { columns, value, item, index } = column
this.selectValue = value
this.selectItem = item
this.pickerColumns = columns
this.pickerValue = index
this.$emit('change', {
value: this.selectValue,
item: this.selectItem,
index: this.pickerValue,
change: changeType
})
}
},
touchstart (e) {
if (!this.pressEnable) return
clearTimeout(this.pressTimeout)
this.pressTimeout = setTimeout(() => {
let item = {}
let toastTitle = ''
// #ifdef APP-NVUE
item = e.target.dataset.item
// #endif
// #ifdef H5
item = JSON.parse(e.currentTarget.dataset.item)
// #endif
// #ifndef APP-NVUE || H5
item = e.currentTarget.dataset.item
// #endif
// #ifdef APP-PLUS || H5
toastTitle = this.getLabel(item)
// #endif
// #ifndef APP-PLUS || H5
toastTitle = item[this.props.label] || item
// #endif
uni.showToast({
title: toastTitle,
icon: 'none'
})
}, this.pressTime)
},
touchmove () {
if (!this.pressEnable) return
clearTimeout(this.pressTimeout)
},
touchend () {
if (!this.pressEnable) return
clearTimeout(this.pressTimeout)
},
getLabel (item, rowIndex, columnIndex) {
if (this.formatter && isFunction(this.formatter)) {
return this.formatter({ item, rowIndex, columnIndex })
} else {
return item[this.props.label] || item
}
}
},
watch: {
value () {
if (!this.isConfirmChange) {
this.init('value')
}
},
list () {
this.init('list')
}
}
}

135
components/lb-picker/pickers/multi-selector-picker.vue

@ -0,0 +1,135 @@
<template>
<view class="lb-multi-selector lb-picker-item"
:style="{ height: height }">
<picker-view :value="pickerValue"
:indicator-style="indicatorStyle"
:style="{ height: height }"
@change="handleChange">
<picker-view-column v-for="(column, index) in pickerColumns"
:key="index">
<!-- #ifdef H5 -->
<view v-for="(item, i) in column || []"
:class="[
'lb-picker-column',
item[props.value] === selectValue[index]
? 'lb-picker-column-active'
: ''
]"
:key="i"
:data-item="pressEnable ? JSON.stringify(item) : ''"
@touchstart="touchstart"
@touchmove="touchmove"
@touchend="touchend">
<!-- #endif -->
<!-- #ifndef H5 -->
<view v-for="(item, i) in column || []"
:class="[
'lb-picker-column',
item[props.value] === selectValue[index]
? 'lb-picker-column-active'
: ''
]"
:key="i"
:data-item="item"
@touchstart="touchstart"
@touchmove="touchmove"
@touchend="touchend">
<!-- #endif -->
<!-- #ifdef APP-PLUS || H5 -->
<text :class="[
'lb-picker-column-label',
`lb-picker-column-label-${align}`
]"
:style="[
item[props.value] === selectValue[index]
? activeColumnStyle
: columnStyle
]">{{ getLabel(item, i, index) }}</text>
<!-- #endif -->
<!-- #ifndef APP-PLUS || H5 -->
<text :class="[
'lb-picker-column-label',
`lb-picker-column-label-${align}`
]">{{ item[props.label] || item }}</text>
<!-- #endif -->
</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
import { commonMixin } from '../mixins'
export default {
props: {
value: Array,
list: Array,
mode: String,
props: Object,
level: Number,
visible: Boolean,
height: String,
columnStyle: Object,
activeColumnStyle: Object,
align: String,
pressEnable: Boolean,
pressTime: Number,
formatter: Function
},
mixins: [commonMixin],
data () {
return {
pickerValue: [],
pickerColumns: [],
selectValue: [],
selectItem: []
}
},
methods: {
handleChange (item) {
const pickerValue = item.detail.value
const columnIndex = pickerValue.findIndex(
(item, i) => item !== this.pickerValue[i]
)
const valueIndex = pickerValue[columnIndex]
this.setPickerChange(pickerValue, valueIndex, columnIndex)
},
setPickerChange (pickerValue, valueIndex, columnIndex) {
for (let i = 0; i < this.level; i++) {
if (i > columnIndex) {
pickerValue[i] = 0
const column =
this.pickerColumns[i - 1][valueIndex] ||
this.pickerColumns[i - 1][0]
this.$set(this.pickerColumns, i, column[this.props.children] || [])
valueIndex = 0
}
this.$set(this.pickerValue, i, pickerValue[i])
const selectItem = this.pickerColumns[i][pickerValue[i]]
if (selectItem) {
this.selectItem[i] = selectItem
this.selectValue[i] = selectItem[this.props.value]
} else {
const spliceNum = this.level - i
this.pickerValue.splice(i, spliceNum)
this.selectValue.splice(i, spliceNum)
this.selectItem.splice(i, spliceNum)
this.pickerColumns.splice(i, spliceNum)
break
}
}
this.$emit('change', {
value: this.selectValue,
item: this.selectItem,
index: this.pickerValue,
change: 'scroll'
})
}
}
}
</script>
<style lang="scss" scoped>
@import "../style/picker-item.scss";
</style>

113
components/lb-picker/pickers/selector-picker.vue

@ -0,0 +1,113 @@
<template>
<view class="lb-selector-picker lb-picker-item"
:style="{ height: height }">
<picker-view :value="pickerValue"
:style="{ height: height }"
:indicator-style="indicatorStyle"
@change="handleChange">
<picker-view-column>
<!-- #ifdef H5 -->
<view v-for="(item, i) in list"
:class="[
'lb-picker-column',
(item[props.value] || item) === selectValue
? 'lb-picker-column-active'
: ''
]"
:key="i"
:data-item="pressEnable ? JSON.stringify(item) : ''"
@touchstart="touchstart"
@touchmove="touchmove"
@touchend="touchend">
<!-- #endif -->
<!-- #ifndef H5 -->
<view v-for="(item, i) in list"
:class="[
'lb-picker-column',
(item[props.value] || item) === selectValue
? 'lb-picker-column-active'
: ''
]"
:key="i"
:data-item="item"
@touchstart="touchstart"
@touchmove="touchmove"
@touchend="touchend">
<!-- #endif -->
<!-- #ifdef APP-PLUS || H5 -->
<text :class="[
'lb-picker-column-label',
`lb-picker-column-label-${align}`
]"
:style="[
(item[props.value] || item) === selectValue
? activeColumnStyle
: columnStyle
]">{{ getLabel(item, i, 0) }}</text>
<!-- #endif -->
<!-- #ifndef APP-PLUS || H5 -->
<text :class="[
'lb-picker-column-label',
`lb-picker-column-label-${align}`
]">{{ item[props.label] || item }}</text>
<!-- #endif -->
</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
import { isObject } from '../utils'
import { commonMixin } from '../mixins'
export default {
props: {
value: [String, Number],
list: Array,
mode: String,
props: Object,
visible: Boolean,
height: String,
columnStyle: Object,
activeColumnStyle: Object,
align: String,
pressEnable: Boolean,
pressTime: Number,
formatter: Function
},
mixins: [commonMixin],
data () {
return {
pickerValue: [],
selectValue: '',
selectItem: null
}
},
computed: {
isH5 () {
return false
}
},
methods: {
handleChange (item) {
const index = item.detail.value[0] || 0
this.selectItem = this.list[index]
this.selectValue = isObject(this.selectItem)
? this.selectItem[this.props.value]
: this.selectItem
this.pickerValue = item.detail.value
this.$emit('change', {
value: this.selectValue,
item: this.selectItem,
index: index,
change: 'scroll'
})
}
}
}
</script>
<style lang="scss" scoped>
@import "../style/picker-item.scss";
</style>

116
components/lb-picker/pickers/unlinked-selector-picker.vue

@ -0,0 +1,116 @@
<template>
<view class="lb-selector-picker lb-picker-item"
:style="{ height: height }">
<picker-view :value="pickerValue"
:indicator-style="indicatorStyle"
:style="{ height: height }"
@change="handleChange">
<picker-view-column v-for="(column, index) in pickerColumns"
:key="index">
<!-- #ifdef H5 -->
<view v-for="(item, i) in column || []"
:class="[
'lb-picker-column',
(item[props.value] || item) === selectValue[index]
? 'lb-picker-column-active'
: ''
]"
:key="i"
:data-item="pressEnable ? JSON.stringify(item) : ''"
@touchstart="touchstart"
@touchmove="touchmove"
@touchend="touchend">
<!-- #endif -->
<!-- #ifndef H5 -->
<view v-for="(item, i) in column || []"
:class="[
'lb-picker-column',
(item[props.value] || item) === selectValue[index]
? 'lb-picker-column-active'
: ''
]"
:key="i"
:data-item="item"
@touchstart="touchstart"
@touchmove="touchmove"
@touchend="touchend">
<!-- #endif -->
<!-- #ifdef APP-PLUS || H5 -->
<text :class="[
'lb-picker-column-label',
`lb-picker-column-label-${align}`
]"
:style="[
(item[props.value] || item) === selectValue[index]
? activeColumnStyle
: columnStyle
]">{{ getLabel(item, i, index) }}</text>
<!-- #endif -->
<!-- #ifndef APP-PLUS || H5 -->
<text :class="[
'lb-picker-column-label',
`lb-picker-column-label-${align}`
]">{{ item[props.label] || item }}</text>
<!-- #endif -->
</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
import { isObject } from '../utils'
import { commonMixin } from '../mixins'
export default {
props: {
value: Array,
list: Array,
mode: String,
props: Object,
visible: Boolean,
height: String,
columnStyle: Object,
activeColumnStyle: Object,
align: String,
pressEnable: Boolean,
pressTime: Number,
formatter: Function
},
mixins: [commonMixin],
data () {
return {
pickerValue: [],
pickerColumns: [],
selectValue: [],
selectItem: []
}
},
methods: {
handleChange (item) {
const pickerValue = item.detail.value
const columnIndex = pickerValue.findIndex((item, i) => item !== this.pickerValue[i])
if (columnIndex > -1) {
const valueIndex = pickerValue[columnIndex]
const columnItem = this.list[columnIndex][valueIndex]
const valueItem = isObject(columnItem)
? columnItem[this.props.value]
: columnItem
this.pickerValue = pickerValue
this.$set(this.selectValue, columnIndex, valueItem)
this.$set(this.selectItem, columnIndex, columnItem)
this.$emit('change', {
value: this.selectValue,
item: this.selectItem,
index: this.pickerValue,
change: 'scroll'
})
}
}
}
}
</script>
<style lang="scss" scoped>
@import "../style/picker-item.scss";
</style>

40
components/lb-picker/style/picker-item.scss

@ -0,0 +1,40 @@
.lb-picker-column {
height: 34px;
padding: 0 10px;
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
white-space: nowrap;
overflow: hidden;
/* #endif */
flex-direction: row;
align-items: center;
}
.lb-picker-column-label {
font-size: 16px;
text-align: center;
flex: 1;
/* #ifdef APP-NVUE */
lines: 1;
/* #endif */
text-overflow: ellipsis;
transition-property: color;
transition-duration: 0.3s;
/* #ifndef APP-NVUE */
overflow: hidden;
white-space: nowrap;
/* #endif */
}
.lb-picker-column-label-left {
text-align: left;
}
.lb-picker-column-label-center {
text-align: center;
}
.lb-picker-column-label-right {
text-align: right;
}

176
components/lb-picker/style/picker.scss

@ -0,0 +1,176 @@
.lb-picker {
position: relative;
}
.lb-picker-mask {
background-color: rgba(0, 0, 0, 0.0);
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
}
.lb-picker-mask-animation {
transition-property: background-color;
transition-duration: 0.3s;
}
.lb-picker-container {
position: relative;
}
.lb-picker-container-fixed {
position: fixed;
left: 0;
right: 0;
bottom: 0;
transform: translateY(100%);
/* #ifndef APP-NVUE */
overflow: hidden;
/* #endif */
}
.lb-picker-container-animation {
transition-property: transform;
transition-duration: 0.3s;
}
.lb-picker-container-show {
transform: translateY(0);
}
.lb-picker-header {
position: relative;
background-color: #fff;
/* #ifdef APP-NVUE */
border-bottom-width: 1px;
border-bottom-style: solid;
border-bottom-color: #e5e5e5;
border-top-width: 1px;
border-top-style: solid;
border-top-color: #e5e5e5;
/* #endif */
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
}
/* #ifndef APP-NVUE */
.lb-picker-header::before {
content: "";
position: absolute;
left: 0;
top: 0;
right: 0;
height: 1px;
clear: both;
border-bottom: 1px solid #e5e5e5;
color: #e5e5e5;
transform-origin: 0 100%;
transform: scaleY(0.5);
}
.lb-picker-header::after {
content: "";
position: absolute;
left: 0;
bottom: 0;
right: 0;
height: 1px;
clear: both;
border-bottom: 1px solid #e5e5e5;
color: #e5e5e5;
transform-origin: 0 100%;
transform: scaleY(0.5);
}
/* #endif */
.lb-picker-header-actions {
height: 45px;
/* #ifndef APP-NVUE */
box-sizing: border-box;
display: flex;
/* #endif */
flex-direction: row;
justify-content: space-between;
flex-wrap: nowrap;
}
.lb-picker-action {
padding-left: 10px;
padding-right: 10px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
}
.lb-picker-action-cancel-text {
font-size: 16px;
}
.lb-picker-action-confirm-text {
font-size: 16px;
}
.lb-picker-content {
position: relative;
background-color: #fff;
}
/* #ifndef APP-PLUS */
.lb-picker-content-safe-buttom {
padding-bottom: 0;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
/* #endif */
.lb-picker-content-main {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
flex-direction: column;
}
.lb-picker-loading,
.lb-picker-empty {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
}
.lb-picker-empty-text {
font-size: 16px;
}
.lb-picker-loading-img {
width: 25px;
height: 25px;
/* #ifndef APP-NVUE */
animation: rotating 2s linear infinite;
/* #endif */
}
/* #ifndef APP-NVUE */
@keyframes rotating {
0% {
transform: rotate(0deg)
}
100% {
transform: rotate(1turn)
}
}
/* #endif */

121
components/lb-picker/utils.js

@ -0,0 +1,121 @@
/**
* 判断是否是对象
*
* @export
* @param {*} val
* @returns true/false
*/
export function isObject (val) {
return Object.prototype.toString.call(val) === '[object Object]'
}
/**
* 判断是否是Function
*
* @export
* @param {*} val
* @returns true/false
*/
export function isFunction (val) {
return Object.prototype.toString.call(val) === '[object Function]'
}
/**
* 根据value获取columns信息
*
* @export
* @param {*} { value, list, mode, props, level }
* @param {number} [type=2] 查询不到value数据返回数据类型 1空值null 2默认第一个选项
* @returns
*/
export function getColumns ({ value, list, mode, props, level }, type = 2) {
let pickerValue = []
let pickerColumns = []
let selectValue = []
let selectItem = []
let columnsInfo = null
switch (mode) {
case 'selector':
let index = list.findIndex(item => {
return isObject(item) ? item[props.value] === value : item === value
})
if (index === -1 && type === 1) {
columnsInfo = null
} else {
index = index > -1 ? index : 0
selectItem = list[index]
selectValue = isObject(selectItem)
? selectItem[props.value]
: selectItem
pickerColumns = list
pickerValue = [index]
columnsInfo = {
index: pickerValue,
value: selectValue,
item: selectItem,
columns: pickerColumns
}
}
break
case 'multiSelector':
const setPickerItems = (data = [], index = 0) => {
if (!data.length) return
const defaultValue = value || []
if (index < level) {
const value = defaultValue[index] || ''
let i = data.findIndex(item => item[props.value] === value)
if (i === -1 && type === 1) return
i = i > -1 ? i : 0
pickerValue[index] = i
pickerColumns[index] = data
if (data[i]) {
selectValue[index] = data[i][props.value]
selectItem[index] = data[i]
setPickerItems(data[i][props.children] || [], index + 1)
}
}
}
setPickerItems(list)
if (!selectValue.length && type === 1) {
columnsInfo = null
} else {
columnsInfo = {
index: pickerValue,
value: selectValue,
item: selectItem,
columns: pickerColumns
}
}
break
case 'unlinkedSelector':
list.forEach((item, i) => {
let index = item.findIndex(item => {
return isObject(item)
? item[props.value] === value[i]
: item === value[i]
})
if (index === -1 && type === 1) return
index = index > -1 ? index : 0
const columnItem = list[i][index]
const valueItem = isObject(columnItem)
? columnItem[props.value]
: columnItem
pickerValue[i] = index
selectValue[i] = valueItem
selectItem[i] = columnItem
})
pickerColumns = list
if (!selectValue.length && type === 1) {
columnsInfo = null
} else {
columnsInfo = {
index: pickerValue,
value: selectValue,
item: selectItem,
columns: pickerColumns
}
}
break
}
return columnsInfo
}

278
components/luch-audio/luch-audio.vue

@ -0,0 +1,278 @@
<template>
<view class="audio-warp" @click="handleBtnClick">
<view class="cover-warp" :class="{ hasbg: !poster }">
<image :src="poster" v-if="poster" class="cover-img"></image>
<view class="play-btn" :class="{ pause: play }"></view>
</view>
<view class="audio-con">
<view class="info">
<text class="audio-title am-text-eill">{{ name }}</text>
<!-- <text class="audio-author am-text-eill">{{ author }}</text> -->
<text class="audio-author am-text-eill">{{ audioTimeUpdate }}</text>
<slider class="audio-slider" activeColor="#fae68f" block-size="12" block-color="#c0947d" :value="current" :max="duration" @changing="changing" @change="change"></slider>
</view>
<!-- <view class="audio-time">{{ audioTimeUpdate }}</view> -->
</view>
</view>
</template>
<script>
/**
* luch-audio 0.0.1
* @module luch-audio
* @Author lu-ch
* @Date 2019-06-11
* @Email webwork.s@qq.com
* @description 音频播放组件使用了createInnerAudioContext
*/
/**
* Props itemsProps
* @prop {Boolean} play - 是否播放双向绑定绑定时需使用.sync 如果为true 则播放为false 则暂停
* ... 其他api同文档 https://uniapp.dcloud.io/api/media/audio-context?id=createinneraudiocontext
*/
/**
* 将秒转换为 :
* @param {Number} s - 秒数
*/
function sToHs(s) {
//
//60
let h;
h = Math.floor(s / 60);
//
//%60
s = s % 60;
//
h += '';
s += '';
//0
h = (h.length === 1) ? '0' + h : h;
s = (s.length === 1) ? '0' + s : s;
return h + ':' + s;
}
export default {
name: 'ComAudio',
props: {
play: {
type: Boolean,
required: true
},
src: {
type: String
},
poster: {
type: String,
default: ''
},
name: {
type: String,
default: '未知音频'
},
author: {
type: String,
default: '本站编辑'
},
autoplay: {
type: Boolean,
default: false
},
loop: {
type: Boolean,
default: false
},
obeyMuteSwitch: {
type: Boolean,
default: true
}
},
data() {
return {
audioTimeUpdate: '00:00',
innerAudioContext: '',
currentTime: '', //
durationTime: '', //
duration: '',
current: '', //slider
loading: false, //
paused: true, //
seek: false //
};
},
methods: {
//
format(num) {
return '0'.repeat(2 - String(Math.floor(num / 60)).length) + Math.floor(num / 60) + ':' + '0'.repeat(2 - String(
Math.floor(num % 60)).length) + Math.floor(num % 60)
},
changing(e) {
this.seek = false;
this.current = e.detail.value;
//this.innerAudioContext.pause();
this.$emit('update:play', false);
},
//
change(e) {
this.current = e.detail.value;
this.innerAudioContext.seek(this.current);
console.log(this.current);
//this.innerAudioContext.play();
this.$emit('update:play', true);
// this.audioTimeUpdate = sToHs(Math.floor(this.innerAudioContext.duration-this.innerAudioContext.currentTime));
this.audioTimeUpdate = sToHs(Math.floor(this.innerAudioContext.currentTime));
},
audioPlay() {
this.$emit('update:play', true);
},
audioPause() {
this.$emit('update:play', false);
},
handleBtnClick() {
// this.$emit('update:play', !this.play);
this.$emit('change', !this.play);
},
contextInit() {
// console.log('');
let that = this;
that.$emit('update:play', false);
let innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.autoplay = that.autoplay;
innerAudioContext.src = that.src;
innerAudioContext.loop = that.loop;
innerAudioContext.obeyMuteSwitch = that.obeyMuteSwitch;
that.current = 0
innerAudioContext.onPlay(function() {
// that.audioTimeUpdate = sToHs(Math.floor(innerAudioContext.duration-innerAudioContext.currentTime));
that.audioTimeUpdate = sToHs(Math.floor(innerAudioContext.currentTime));
that.durationTime = sToHs(Math.floor(innerAudioContext.duration));
that.audioPlay();
});
innerAudioContext.onPause(function() {
that.audioPause();
});
innerAudioContext.onEnded(function() {
that.audioPause();
});
innerAudioContext.onTimeUpdate(function() {
// that.audioTimeUpdate = sToHs(Math.floor(innerAudioContext.duration-innerAudioContext.currentTime));
that.audioTimeUpdate = sToHs(Math.floor(innerAudioContext.currentTime));
that.duration = innerAudioContext.duration;
that.durationTime = sToHs(Math.floor(innerAudioContext.duration));
if (!that.seek) {
that.current = innerAudioContext.currentTime;
}
});
innerAudioContext.onError(err => {
// console.log(err);
});
this.innerAudioContext = innerAudioContext;
}
},
watch: {
play(n) {
if (n) {
this.innerAudioContext.play();
} else {
this.innerAudioContext.pause();
}
this.$forceUpdate();
},
src() {
this.innerAudioContext.destroy();
this.contextInit();
}
},
created() {
this.contextInit();
},
beforeDestroy() {
this.innerAudioContext.destroy();
}
};
</script>
<style lang="scss">
.am-text-eill {
/*超出省略号*/
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.audio-warp {
display: flex;
overflow: hidden;
height: 134upx;
border-radius: 10upx;
padding: 18upx 0 0 50upx;
background-color: rgba(255, 255, 255, 1);
}
.cover-warp {
position: relative;
flex-shrink: 0;
width: 130upx;
height: 100%;
&.hasbg {
background-color: #e6e6e6;
}
.cover-img {
width: 100%;
height: 100%;
display: none;
}
.play-btn {
position: absolute;
left: 50%;
top: 50%;
width: 80upx;
height: 80upx;
transform: translateX(-50%) translateY(-50%);
border-radius: 50%;
background-size: 100% 100%;
background-image: url('http://ys.tour-ma.com/r/cms/www/m/yushan/icon-nj-ztyy.png');
&.pause {
background-image: url('http://ys.tour-ma.com/r/cms/www/m/yushan/icon-nj-bfyy.png');
}
}
}
.audio-con {
position: relative;
flex: 1;
display: flex;
width: 0;
padding: 0 30upx;
align-items: center;
// background-color: #fcfcfc;
.info {
width: 100%;
}
.audio-title {
display: block;
padding-bottom: 14upx;
// padding-right: 50upx;
font-size: 35upx;
color: #000;
font-weight: 600;
}
.audio-author {
display: inline-block;
font-size: 24upx;
color: #888888;
width: 15%;
vertical-align: middle;
}
.audio-slider{
display: inline-block;
width: 74%;
vertical-align: middle;
margin: 0;
padding: 0 10px;
}
.audio-time {
position: absolute;
right: 30upx;
top: 12upx;
font-size: 26upx;
color: #9d9d9d;
}
}
</style>

65
components/luch-audio/readme.md

@ -0,0 +1,65 @@
**插件使用说明**
- 基于 uni.createInnerAudioContext() 封装audio音频组件(样式同原生audio组件)
- 支持双向绑定
- 支持自定义修改样式
**Example**
---
``` javascript
<luch-audio src="https://img-cdn-qiniu.dcloud.net.cn/uniapp/audio/music.mp3" :play.sync="audioPlay"></luch-audio>
// 控制变量audioPlay ,如果true音频会播放,否则暂停
```
**Attributes**
--
参数|说明|类型|可选值|默认值|required
--|:-
play|是否播放,双向绑定,需加 ` .sync `|Boolean| — | — |true
src|资源路径|String| — | — | —
poster|封面图片路径|String| — | — | —
name|音频名称|String| — | 未知音频 | —
author|作者名字|String| — | 未知作者 | —
autoplay|是否自动开始播放|Boolean| — | false | —
loop|是否循环播放|Boolean| — | false | —
obeyMuteSwitch|是否遵循系统静音开关,当此参数为 false 时,即使用户打开了静音开关,也能继续发出声音|Boolean| — | true | —
其他api 可在组件内 ` contextInit ` 函数初始化时自定义添加
**平台支持度**
--
5+App|H5|微信小程序|支付宝小程序|百度小程序|头条小程序
--|:-
yes|yes|yes|no|yes|yes
**注意:**以上平台支持度为uni-app 对` uni.createInnerAudioContext() ` api 的支持度,本组件的支持度本人只在微信小程序使用过,其他平台*未做测试*
**使用**
--
下载后把该文件放至 components 文件夹或 特定平台组件 文件夹即可
**更新**
--
- v0.0.1 组件
**可扩展性**
--
api 增加参考 ` https://uniapp.dcloud.io/api/media/audio-context?id=createinneraudiocontext `
<br>
组件内的播放暂停图标本人因为要发布组件,所以使用了base64,大家可自行替换;
<br>
实例获取可以通过ref 获取组件内的 ` innerAudioContext ` (未实验,只是估计 0.0)
**说明**
--
切换src 或者切换页面都会销毁实例<br>
写本组件的原因就是原生audio组件无法修改宽度,导致某些情况下显示不全,本组件是100%自适应布局;

55
components/mescroll-uni/components/mescroll-down.css

@ -0,0 +1,55 @@
/* 下拉刷新区域 */
.mescroll-downwarp {
position: absolute;
top: -100%;
left: 0;
width: 100%;
height: 100%;
text-align: center;
}
/* 下拉刷新--内容区,定位于区域底部 */
.mescroll-downwarp .downwarp-content {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
min-height: 60rpx;
padding: 20rpx 0;
text-align: center;
}
/* 下拉刷新--提示文本 */
.mescroll-downwarp .downwarp-tip {
display: inline-block;
font-size: 28rpx;
vertical-align: middle;
margin-left: 16rpx;
/* color: gray; 已在style设置color,此处删去*/
}
/* 下拉刷新--旋转进度条 */
.mescroll-downwarp .downwarp-progress {
display: inline-block;
width: 32rpx;
height: 32rpx;
border-radius: 50%;
border: 2rpx solid gray;
border-bottom-color: transparent !important; /*已在style设置border-color,此处需加 !important*/
vertical-align: middle;
}
/* 旋转动画 */
.mescroll-downwarp .mescroll-rotate {
animation: mescrollDownRotate 0.6s linear infinite;
}
@keyframes mescrollDownRotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

47
components/mescroll-uni/components/mescroll-down.vue

@ -0,0 +1,47 @@
<!-- 下拉刷新区域 -->
<template>
<view v-if="mOption.use" class="mescroll-downwarp" :style="{'background-color':mOption.bgColor,'color':mOption.textColor}">
<view class="downwarp-content">
<view class="downwarp-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mOption.textColor, 'transform':downRotate}"></view>
<view class="downwarp-tip">{{downText}}</view>
</view>
</view>
</template>
<script>
export default {
props: {
option: Object , // down
type: Number, // inOffset1 outOffset2 showLoading3 endDownScroll4
rate: Number // (inOffset: rate<1; outOffset: rate>=1)
},
computed: {
// ,propdefault
mOption(){
return this.option || {}
},
//
isDownLoading(){
return this.type === 3
},
//
downRotate(){
return 'rotate(' + 360 * this.rate + 'deg)'
},
//
downText(){
switch (this.type){
case 1: return this.mOption.textInOffset;
case 2: return this.mOption.textOutOffset;
case 3: return this.mOption.textLoading;
case 4: return this.mOption.textLoading;
default: return this.mOption.textInOffset;
}
}
}
};
</script>
<style>
@import "./mescroll-down.css";
</style>

90
components/mescroll-uni/components/mescroll-empty.vue

@ -0,0 +1,90 @@
<!--空布局
可作为独立的组件, 不使用mescroll的页面也能单独引入, 以便APP全局统一管理:
import MescrollEmpty from '@/components/mescroll-uni/components/mescroll-empty.vue';
<mescroll-empty v-if="isShowEmpty" :option="optEmpty" @emptyclick="emptyClick"></mescroll-empty>
-->
<template>
<view class="mescroll-empty" :class="{ 'empty-fixed': option.fixed }" :style="{ 'z-index': option.zIndex, top: option.top }">
<view> <image v-if="icon" class="empty-icon" :src="icon" mode="widthFix" /> </view>
<view v-if="tip" class="empty-tip">{{ tip }}</view>
<view v-if="option.btnText" class="empty-btn" @click="emptyClick">{{ option.btnText }}</view>
</view>
</template>
<script>
//
import GlobalOption from './../mescroll-uni-option.js';
export default {
props: {
// empty: GlobalOption.up.empty
option: {
type: Object,
default() {
return {};
}
}
},
// 使computed,option
computed: {
//
icon() {
return this.option.icon == null ? GlobalOption.up.empty.icon : this.option.icon; // 使,
},
//
tip() {
return this.option.tip == null ? GlobalOption.up.empty.tip : this.option.tip; // 使,
}
},
methods: {
//
emptyClick() {
this.$emit('emptyclick');
}
}
};
</script>
<style>
/* 无任何数据的空布局 */
.mescroll-empty {
box-sizing: border-box;
width: 100%;
padding: 100rpx 50rpx;
text-align: center;
}
.mescroll-empty.empty-fixed {
z-index: 99;
position: absolute; /*transform会使fixed失效,最终会降级为absolute */
top: 100rpx;
left: 0;
}
.mescroll-empty .empty-icon {
width: 280rpx;
height: 280rpx;
}
.mescroll-empty .empty-tip {
margin-top: 20rpx;
font-size: 24rpx;
color: gray;
}
.mescroll-empty .empty-btn {
display: inline-block;
margin-top: 40rpx;
min-width: 200rpx;
padding: 18rpx;
font-size: 28rpx;
border: 1rpx solid #e04b28;
border-radius: 60rpx;
color: #e04b28;
}
.mescroll-empty .empty-btn:active {
opacity: 0.75;
}
</style>

83
components/mescroll-uni/components/mescroll-top.vue

@ -0,0 +1,83 @@
<!-- 回到顶部的按钮 -->
<template>
<image
v-if="mOption.src"
class="mescroll-totop"
:class="[value ? 'mescroll-totop-in' : 'mescroll-totop-out', {'mescroll-totop-safearea': mOption.safearea}]"
:style="{'z-index':mOption.zIndex, 'left': left, 'right': right, 'bottom':addUnit(mOption.bottom), 'width':addUnit(mOption.width), 'border-radius':addUnit(mOption.radius)}"
:src="mOption.src"
mode="widthFix"
@click="toTopClick"
/>
</template>
<script>
export default {
props: {
// up.toTop
option: Object,
//
value: false
},
computed: {
// ,propdefault
mOption(){
return this.option || {}
},
//
left(){
return this.mOption.left ? this.addUnit(this.mOption.left) : 'auto';
},
// ()
right() {
return this.mOption.left ? 'auto' : this.addUnit(this.mOption.right);
}
},
methods: {
addUnit(num){
if(!num) return 0;
if(typeof num === 'number') return num + 'rpx';
return num
},
toTopClick() {
this.$emit('input', false); // 使v-model
this.$emit('click'); //
}
}
};
</script>
<style>
/* 回到顶部的按钮 */
.mescroll-totop {
z-index: 9990;
position: fixed !important; /* 加上important避免编译到H5,在多mescroll中定位失效 */
right: 20rpx;
bottom: 120rpx;
width: 72rpx;
height: auto;
border-radius: 50%;
opacity: 0;
transition: opacity 0.5s; /* 过渡 */
margin-bottom: var(--window-bottom); /* css变量 */
}
/* 适配 iPhoneX */
@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
.mescroll-totop-safearea {
margin-bottom: calc(var(--window-bottom) + constant(safe-area-inset-bottom)); /* window-bottom + 适配 iPhoneX */
margin-bottom: calc(var(--window-bottom) + env(safe-area-inset-bottom));
}
}
/* 显示 -- 淡入 */
.mescroll-totop-in {
opacity: 1;
}
/* 隐藏 -- 淡出且不接收事件*/
.mescroll-totop-out {
opacity: 0;
pointer-events: none;
}
</style>

47
components/mescroll-uni/components/mescroll-up.css

@ -0,0 +1,47 @@
/* 上拉加载区域 */
.mescroll-upwarp {
box-sizing: border-box;
min-height: 110rpx;
padding: 30rpx 0;
text-align: center;
clear: both;
}
/*提示文本 */
.mescroll-upwarp .upwarp-tip,
.mescroll-upwarp .upwarp-nodata {
display: inline-block;
font-size: 28rpx;
vertical-align: middle;
/* color: gray; 已在style设置color,此处删去*/
}
.mescroll-upwarp .upwarp-tip {
margin-left: 16rpx;
}
/*旋转进度条 */
.mescroll-upwarp .upwarp-progress {
display: inline-block;
width: 32rpx;
height: 32rpx;
border-radius: 50%;
border: 2rpx solid gray;
border-bottom-color: transparent !important; /*已在style设置border-color,此处需加 !important*/
vertical-align: middle;
}
/* 旋转动画 */
.mescroll-upwarp .mescroll-rotate {
animation: mescrollUpRotate 0.6s linear infinite;
}
@keyframes mescrollUpRotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

39
components/mescroll-uni/components/mescroll-up.vue

@ -0,0 +1,39 @@
<!-- 上拉加载区域 -->
<template>
<view class="mescroll-upwarp" :style="{'background-color':mOption.bgColor,'color':mOption.textColor}">
<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
<view v-show="isUpLoading">
<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mOption.textColor}"></view>
<view class="upwarp-tip">{{ mOption.textLoading }}</view>
</view>
<!-- 无数据 -->
<view v-if="isUpNoMore" class="upwarp-nodata">{{ mOption.textNoMore }}</view>
</view>
</template>
<script>
export default {
props: {
option: Object, // up
type: Number // 0loading1loading2
},
computed: {
// ,propdefault
mOption() {
return this.option || {};
},
//
isUpLoading() {
return this.type === 1;
},
//
isUpNoMore() {
return this.type === 2;
}
}
};
</script>
<style>
@import './mescroll-up.css';
</style>

19
components/mescroll-uni/mescroll-body.css

@ -0,0 +1,19 @@
.mescroll-body {
position: relative; /* 下拉刷新区域相对自身定位 */
height: auto; /* 不可固定高度,否则overflow:hidden导致无法滑动; 同时使设置的最小高生效,实现列表不满屏仍可下拉*/
overflow: hidden; /* 当有元素写在mescroll-body标签前面时,可遮住下拉刷新区域 */
box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
}
/* 使sticky生效: 父元素不能overflow:hidden或者overflow:auto属性 */
.mescroll-body.mescorll-sticky{
overflow: unset !important
}
/* 适配 iPhoneX */
@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
.mescroll-safearea {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
}

344
components/mescroll-uni/mescroll-body.vue

@ -0,0 +1,344 @@
<template>
<view
class="mescroll-body mescroll-render-touch"
:class="{'mescorll-sticky': sticky}"
:style="{'minHeight':minHeight, 'padding-top': padTop, 'padding-bottom': padBottom}"
@touchstart="wxsBiz.touchstartEvent"
@touchmove="wxsBiz.touchmoveEvent"
@touchend="wxsBiz.touchendEvent"
@touchcancel="wxsBiz.touchendEvent"
:change:prop="wxsBiz.propObserver"
:prop="wxsProp"
>
<!-- 状态栏 -->
<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
<view class="mescroll-body-content mescroll-wxs-content" :style="{ transform: translateY, transition: transition }" :change:prop="wxsBiz.callObserver" :prop="callProp">
<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
<view class="downwarp-content">
<view class="downwarp-progress mescroll-wxs-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mescroll.optDown.textColor, 'transform': downRotate}"></view>
<view class="downwarp-tip">{{downText}}</view>
</view>
</view>
<!-- 列表内容 -->
<slot></slot>
<!-- 空布局 -->
<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
<view v-show="upLoadType===1">
<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
</view>
<!-- 无数据 -->
<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
</view>
</view>
<!-- 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效) -->
<!-- #ifdef H5 -->
<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
<!-- #endif -->
<!-- 适配iPhoneX -->
<view v-if="safearea" class="mescroll-safearea"></view>
<!-- 回到顶部按钮 (fixed元素需写在transform外面,防止降级为absolute)-->
<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
<!-- #endif -->
</view>
</template>
<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
<script src="./wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
<!-- #endif -->
<!-- app, h5使用renderjs -->
<!-- #ifdef APP-PLUS || H5 -->
<script module="renderBiz" lang="renderjs">
import renderBiz from './wxs/renderjs.js';
export default {
mixins: [renderBiz]
}
</script>
<!-- #endif -->
<script>
// mescroll-uni.js,
import MeScroll from './mescroll-uni.js';
//
import GlobalOption from './mescroll-uni-option.js';
//
import MescrollEmpty from './components/mescroll-empty.vue';
//
import MescrollTop from './components/mescroll-top.vue';
// wxs(renderjs)mixins
import WxsMixin from './wxs/mixins.js';
export default {
mixins: [WxsMixin],
components: {
MescrollEmpty,
MescrollTop
},
data() {
return {
mescroll: {optDown:{},optUp:{}}, // mescroll
downHight: 0, //:
downRate: 0, // (inOffset: rate<1; outOffset: rate>=1)
downLoadType: 0, // : 0(loading), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
upLoadType: 0, // 0loading1loading2,END3,END
isShowEmpty: false, //
isShowToTop: false, //
windowHeight: 0, // 使
windowBottom: 0, // 使
statusBarHeight: 0 //
};
},
props: {
down: Object, //
up: Object, //
top: [String, Number], // (20, "20rpx", "20px", "20%", rpx, windowHeight)
topbar: [Boolean, String], // top, false (使:,, ,,,)
bottom: [String, Number], // (20, "20rpx", "20px", "20%", rpx, windowHeight)
safearea: Boolean, // bottom, false (iPhoneX使)
height: [String, Number], // mescroll,windowHeight,使
bottombar:{ // TabBar(H5tab)
type: Boolean,
default: true
},
sticky: Boolean // sticky,false; true,mescroll-body,
},
computed: {
// mescroll,windowHeight,使
minHeight(){
return this.toPx(this.height || '100%') + 'px'
},
// (px)
numTop() {
return this.toPx(this.top)
},
padTop() {
return this.numTop + 'px';
},
// (px)
numBottom() {
return this.toPx(this.bottom);
},
padBottom() {
return this.numBottom + 'px';
},
//
isDownReset() {
return this.downLoadType === 3 || this.downLoadType === 4;
},
//
transition() {
return this.isDownReset ? 'transform 300ms' : '';
},
translateY() {
return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform使fixed,fixedmescroll
},
//
isDownLoading(){
return this.downLoadType === 3
},
//
downRotate(){
return 'rotate(' + 360 * this.downRate + 'deg)'
},
//
downText(){
if(!this.mescroll) return ""; //
switch (this.downLoadType){
case 1: return this.mescroll.optDown.textInOffset;
case 2: return this.mescroll.optDown.textOutOffset;
case 3: return this.mescroll.optDown.textLoading;
case 4: return this.mescroll.optDown.textLoading;
default: return this.mescroll.optDown.textInOffset;
}
}
},
methods: {
//number,rpx,upx,px,% --> px
toPx(num) {
if (typeof num === 'string') {
if (num.indexOf('px') !== -1) {
if (num.indexOf('rpx') !== -1) {
// "10rpx"
num = num.replace('rpx', '');
} else if (num.indexOf('upx') !== -1) {
// "10upx"
num = num.replace('upx', '');
} else {
// "10px"
return Number(num.replace('px', ''));
}
} else if (num.indexOf('%') !== -1) {
// ,windowHeight,"10%"windowHeight10%
let rate = Number(num.replace('%', '')) / 100;
return this.windowHeight * rate;
}
}
return num ? uni.upx2px(Number(num)) : 0;
},
//
emptyClick() {
this.$emit('emptyclick', this.mescroll);
},
//
toTopClick() {
this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); //
this.$emit('topclick', this.mescroll); //
}
},
// 使createdmescroll; mountedcssH5
created() {
let vm = this;
let diyOption = {
//
down: {
inOffset() {
vm.downLoadType = 1; // offset (mescroll,)
},
outOffset() {
vm.downLoadType = 2; // offset (mescroll,)
},
onMoving(mescroll, rate, downHight) {
// ,;
vm.downHight = downHight; // (mescroll,)
vm.downRate = rate; // (inOffset: rate<1; outOffset: rate>=1)
},
showLoading(mescroll, downHight) {
vm.downLoadType = 3; // (mescroll,)
vm.downHight = downHight; // (mescroll,)
},
endDownScroll() {
vm.downLoadType = 4; // (mescroll,)
vm.downHight = 0; // (mescroll,)
if(vm.downResetTimer) {clearTimeout(vm.downResetTimer); vm.downResetTimer = null} //
vm.downResetTimer = setTimeout(()=>{ // ,0,inOffsettextInOffset
if(vm.downLoadType === 4) vm.downLoadType = 0
},300)
},
//
callback: function(mescroll) {
vm.$emit('down', mescroll);
}
},
//
up: {
//
showLoading() {
vm.upLoadType = 1;
},
//
showNoMore() {
vm.upLoadType = 2;
},
//
hideUpScroll(mescroll) {
vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
},
//
empty: {
onShow(isShow) {
//
vm.isShowEmpty = isShow;
}
},
//
toTop: {
onShow(isShow) {
//
vm.isShowToTop = isShow;
}
},
//
callback: function(mescroll) {
vm.$emit('up', mescroll);
}
}
};
MeScroll.extend(diyOption, GlobalOption); //
let myOption = JSON.parse(JSON.stringify({down: vm.down,up: vm.up})); // ,props
MeScroll.extend(myOption, diyOption); //
// MeScroll
vm.mescroll = new MeScroll(myOption, true); // true,body
// initmescroll
vm.$emit('init', vm.mescroll);
//
const sys = uni.getSystemInfoSync();
if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
if (sys.windowBottom) vm.windowBottom = sys.windowBottom;
if (sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
// 使downbottomOffset
vm.mescroll.setBodyHeight(sys.windowHeight);
// 使pagescroll,scrollTo
vm.mescroll.resetScrollTo((y, t) => {
if(typeof y === 'string'){
// view (ycss)
setTimeout(()=>{ // view; 使$nextTick
let selector;
if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
selector = '#'+y // #. id
}else{
selector = y
// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
if(y.indexOf('>>>')!=-1){ // ()
selector = y.split('>>>')[1].trim()
}
// #endif
}
uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
if (rect) {
let top = rect.top
top += vm.mescroll.getScrollTop()
uni.pageScrollTo({
scrollTop: top,
duration: t
})
} else{
console.error(selector + ' does not exist');
}
}).exec()
},30)
} else{
// (y)
uni.pageScrollTo({
scrollTop: y,
duration: t
})
}
});
// up.toTop.safearea,vuesafearea
if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
vm.mescroll.optUp.toTop.safearea = vm.safearea;
}
}
};
</script>
<style>
@import "./mescroll-body.css";
@import "./components/mescroll-down.css";
@import './components/mescroll-up.css';
</style>

65
components/mescroll-uni/mescroll-mixins.js

@ -0,0 +1,65 @@
// mescroll-body 和 mescroll-uni 通用
// import MescrollUni from "./mescroll-uni.vue";
// import MescrollBody from "./mescroll-body.vue";
const MescrollMixin = {
// components: { // 非H5端无法通过mixin注册组件, 只能在main.js中注册全局组件或具体界面中注册
// MescrollUni,
// MescrollBody
// },
data() {
return {
mescroll: null //mescroll实例对象
}
},
// 注册系统自带的下拉刷新 (配置down.native为true时生效, 还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
onPullDownRefresh(){
this.mescroll && this.mescroll.onPullDownRefresh();
},
// 注册列表滚动事件,用于判定在顶部可下拉刷新,在指定位置可显示隐藏回到顶部按钮 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
onPageScroll(e) {
this.mescroll && this.mescroll.onPageScroll(e);
},
// 注册滚动到底部的事件,用于上拉加载 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
onReachBottom() {
this.mescroll && this.mescroll.onReachBottom();
},
methods: {
// mescroll组件初始化的回调,可获取到mescroll对象
mescrollInit(mescroll) {
this.mescroll = mescroll;
this.mescrollInitByRef(); // 兼容字节跳动小程序
},
// 以ref的方式初始化mescroll对象 (兼容字节跳动小程序: http://www.mescroll.com/qa.html?v=20200107#q26)
mescrollInitByRef() {
if(!this.mescroll || !this.mescroll.resetUpScroll){
let mescrollRef = this.$refs.mescrollRef;
if(mescrollRef) this.mescroll = mescrollRef.mescroll
}
},
// 下拉刷新的回调 (mixin默认resetUpScroll)
downCallback() {
if(this.mescroll.optUp.use){
this.mescroll.resetUpScroll()
}else{
setTimeout(()=>{
this.mescroll.endSuccess();
}, 500)
}
},
// 上拉加载的回调
upCallback() {
// mixin默认延时500自动结束加载
setTimeout(()=>{
this.mescroll.endErr();
}, 500)
}
},
mounted() {
this.mescrollInitByRef(); // 兼容字节跳动小程序, 避免未设置@init或@init此时未能取到ref的情况
}
}
export default MescrollMixin;

33
components/mescroll-uni/mescroll-uni-option.js

@ -0,0 +1,33 @@
// 全局配置
// mescroll-body 和 mescroll-uni 通用
const GlobalOption = {
down: {
// 其他down的配置参数也可以写,这里只展示了常用的配置:
textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
textLoading: '加载中 ...', // 加载中的提示文本
offset: 120, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
native: false // 是否使用系统自带的下拉刷新; 默认false; 仅在mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
},
up: {
// 其他up的配置参数也可以写,这里只展示了常用的配置:
textLoading: '加载中 ...', // 加载中的提示文本
textNoMore: '~ 没有更多数据了 ~', // 没有更多数据的提示文本
offset: 80, // 距底部多远时,触发upCallback
toTop: {
// 回到顶部按钮,需配置src才显示
src: "http://www.mescroll.com/img/mescroll-totop.png?v=1", // 图片路径 (建议放入static目录, 如 /static/img/mescroll-totop.png )
offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000px
right: 20, // 到右边的距离, 默认20 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
bottom: 120, // 到底部的距离, 默认120 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
width: 72 // 回到顶部图标的宽度, 默认72 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
},
empty: {
use: true, // 是否显示空布局
icon: "http://www.mescroll.com/img/mescroll-empty.png?v=1", // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
tip: '~ 暂无数据 ~' // 提示
}
}
}
export default GlobalOption

36
components/mescroll-uni/mescroll-uni.css

@ -0,0 +1,36 @@
.mescroll-uni-warp{
height: 100%;
}
.mescroll-uni-content{
height: 100%;
}
.mescroll-uni {
position: relative;
width: 100%;
height: 100%;
min-height: 200rpx;
overflow-y: auto;
box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
}
/* 定位的方式固定高度 */
.mescroll-uni-fixed{
z-index: 1;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: auto; /* 使right生效 */
height: auto; /* 使bottom生效 */
}
/* 适配 iPhoneX */
@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
.mescroll-safearea {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
}

788
components/mescroll-uni/mescroll-uni.js

@ -0,0 +1,788 @@
/* mescroll
* version 1.3.2
* 2020-08-05 wenju
* http://www.mescroll.com
*/
export default function MeScroll(options, isScrollBody) {
let me = this;
me.version = '1.3.2'; // mescroll版本号
me.options = options || {}; // 配置
me.isScrollBody = isScrollBody || false; // 滚动区域是否为原生页面滚动; 默认为scroll-view
me.isDownScrolling = false; // 是否在执行下拉刷新的回调
me.isUpScrolling = false; // 是否在执行上拉加载的回调
let hasDownCallback = me.options.down && me.options.down.callback; // 是否配置了down的callback
// 初始化下拉刷新
me.initDownScroll();
// 初始化上拉加载,则初始化
me.initUpScroll();
// 自动加载
setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
// 自动触发下拉刷新 (只有配置了down的callback才自动触发下拉刷新)
if ((me.optDown.use || me.optDown.native) && me.optDown.auto && hasDownCallback) {
if (me.optDown.autoShowLoading) {
me.triggerDownScroll(); // 显示下拉进度,执行下拉回调
} else {
me.optDown.callback && me.optDown.callback(me); // 不显示下拉进度,直接执行下拉回调
}
}
// 自动触发上拉加载
if(!me.isUpAutoLoad){ // 部分小程序(头条小程序)emit是异步, 会导致isUpAutoLoad判断有误, 先延时确保先执行down的callback,再执行up的callback
setTimeout(function(){
me.optUp.use && me.optUp.auto && !me.isUpAutoLoad && me.triggerUpScroll();
},100)
}
}, 30); // 需让me.optDown.inited和me.optUp.inited先执行
}
/* 配置参数:下拉刷新 */
MeScroll.prototype.extendDownScroll = function(optDown) {
// 下拉刷新的配置
MeScroll.extend(optDown, {
use: true, // 是否启用下拉刷新; 默认true
auto: true, // 是否在初始化完毕之后自动执行下拉刷新的回调; 默认true
native: false, // 是否使用系统自带的下拉刷新; 默认false; 仅mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
autoShowLoading: false, // 如果设置auto=true(在初始化完毕之后自动执行下拉刷新的回调),那么是否显示下拉刷新的进度; 默认false
isLock: false, // 是否锁定下拉刷新,默认false;
offset: 120, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
startTop: 100, // scroll-view快速滚动到顶部时,此时的scroll-top可能大于0, 此值用于控制最大的误差
inOffsetRate: 1, // 在列表顶部,下拉的距离小于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
outOffsetRate: 0.2, // 在列表顶部,下拉的距离大于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
bottomOffset: 20, // 当手指touchmove位置在距离body底部20px范围内的时候结束上拉刷新,避免Webview嵌套导致touchend事件不执行
minAngle: 45, // 向下滑动最少偏移的角度,取值区间 [0,90];默认45度,即向下滑动的角度大于45度则触发下拉;而小于45度,将不触发下拉,避免与左右滑动的轮播等组件冲突;
textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
textLoading: '加载中 ...', // 加载中的提示文本
bgColor: "transparent", // 背景颜色 (建议在pages.json中再设置一下backgroundColorTop)
textColor: "gray", // 文本颜色 (当bgColor配置了颜色,而textColor未配置时,则textColor会默认为白色)
inited: null, // 下拉刷新初始化完毕的回调
inOffset: null, // 下拉的距离进入offset范围内那一刻的回调
outOffset: null, // 下拉的距离大于offset那一刻的回调
onMoving: null, // 下拉过程中的回调,滑动过程一直在执行; rate下拉区域当前高度与指定距离的比值(inOffset: rate<1; outOffset: rate>=1); downHight当前下拉区域的高度
beforeLoading: null, // 准备触发下拉刷新的回调: 如果return true,将不触发showLoading和callback回调; 常用来完全自定义下拉刷新, 参考案例【淘宝 v6.8.0】
showLoading: null, // 显示下拉刷新进度的回调
afterLoading: null, // 显示下拉刷新进度的回调之后,马上要执行的代码 (如: 在wxs中使用)
beforeEndDownScroll: null, // 准备结束下拉的回调. 返回结束下拉的延时执行时间,默认0ms; 常用于结束下拉之前再显示另外一小段动画,才去隐藏下拉刷新的场景, 参考案例【dotJump】
endDownScroll: null, // 结束下拉刷新的回调
afterEndDownScroll: null, // 结束下拉刷新的回调,马上要执行的代码 (如: 在wxs中使用)
callback: function(mescroll) {
// 下拉刷新的回调;默认重置上拉加载列表为第一页
mescroll.resetUpScroll();
}
})
}
/* 配置参数:上拉加载 */
MeScroll.prototype.extendUpScroll = function(optUp) {
// 上拉加载的配置
MeScroll.extend(optUp, {
use: true, // 是否启用上拉加载; 默认true
auto: true, // 是否在初始化完毕之后自动执行上拉加载的回调; 默认true
isLock: false, // 是否锁定上拉加载,默认false;
isBoth: true, // 上拉加载时,如果滑动到列表顶部是否可以同时触发下拉刷新;默认true,两者可同时触发;
callback: null, // 上拉加载的回调;function(page,mescroll){ }
page: {
num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
size: 10, // 每页数据的数量
time: null // 加载第一页数据服务器返回的时间; 防止用户翻页时,后台新增了数据从而导致下一页数据重复;
},
noMoreSize: 1, // 如果列表已无数据,可设置列表的总数量要大于等于5条才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看
offset: 80, // 距底部多远时,触发upCallback
textLoading: '加载中 ...', // 加载中的提示文本
textNoMore: '-- END --', // 没有更多数据的提示文本
bgColor: "transparent", // 背景颜色 (建议在pages.json中再设置一下backgroundColorBottom)
textColor: "gray", // 文本颜色 (当bgColor配置了颜色,而textColor未配置时,则textColor会默认为白色)
inited: null, // 初始化完毕的回调
showLoading: null, // 显示加载中的回调
showNoMore: null, // 显示无更多数据的回调
hideUpScroll: null, // 隐藏上拉加载的回调
errDistance: 60, // endErr的时候需往上滑动一段距离,使其往下滑动时再次触发onReachBottom,仅mescroll-body生效
toTop: {
// 回到顶部按钮,需配置src才显示
src: null, // 图片路径,默认null (绝对路径或网络图)
offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000
duration: 300, // 回到顶部的动画时长,默认300ms (当值为0或300则使用系统自带回到顶部,更流畅; 其他值则通过step模拟,部分机型可能不够流畅,所以非特殊情况不建议修改此项)
btnClick: null, // 点击按钮的回调
onShow: null, // 是否显示的回调
zIndex: 9990, // fixed定位z-index值
left: null, // 到左边的距离, 默认null. 此项有值时,right不生效. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
right: 20, // 到右边的距离, 默认20 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
bottom: 120, // 到底部的距离, 默认120 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
safearea: false, // bottom的偏移量是否加上底部安全区的距离, 默认false, 需要适配iPhoneX时使用 (具体的界面如果不配置此项,则取本vue的safearea值)
width: 72, // 回到顶部图标的宽度, 默认72 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
radius: "50%" // 圆角, 默认"50%" (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
},
empty: {
use: true, // 是否显示空布局
icon: null, // 图标路径
tip: '~ 暂无相关数据 ~', // 提示
btnText: '', // 按钮
btnClick: null, // 点击按钮的回调
onShow: null, // 是否显示的回调
fixed: false, // 是否使用fixed定位,默认false; 配置fixed为true,以下的top和zIndex才生效 (transform会使fixed失效,最终会降级为absolute)
top: "100rpx", // fixed定位的top值 (完整的单位值,如 "10%"; "100rpx")
zIndex: 99 // fixed定位z-index值
},
onScroll: false // 是否监听滚动事件
})
}
/* 配置参数 */
MeScroll.extend = function(userOption, defaultOption) {
if (!userOption) return defaultOption;
for (let key in defaultOption) {
if (userOption[key] == null) {
let def = defaultOption[key];
if (def != null && typeof def === 'object') {
userOption[key] = MeScroll.extend({}, def); // 深度匹配
} else {
userOption[key] = def;
}
} else if (typeof userOption[key] === 'object') {
MeScroll.extend(userOption[key], defaultOption[key]); // 深度匹配
}
}
return userOption;
}
/* 简单判断是否配置了颜色 (非透明,非白色) */
MeScroll.prototype.hasColor = function(color) {
if(!color) return false;
let c = color.toLowerCase();
return c != "#fff" && c != "#ffffff" && c != "transparent" && c != "white"
}
/* -------初始化下拉刷新------- */
MeScroll.prototype.initDownScroll = function() {
let me = this;
// 配置参数
me.optDown = me.options.down || {};
if(!me.optDown.textColor && me.hasColor(me.optDown.bgColor)) me.optDown.textColor = "#fff"; // 当bgColor有值且textColor未设置,则textColor默认白色
me.extendDownScroll(me.optDown);
// 如果是mescroll-body且配置了native,则禁止自定义的下拉刷新
if(me.isScrollBody && me.optDown.native){
me.optDown.use = false
}else{
me.optDown.native = false // 仅mescroll-body支持,mescroll-uni不支持
}
me.downHight = 0; // 下拉区域的高度
// 在页面中加入下拉布局
if (me.optDown.use && me.optDown.inited) {
// 初始化完毕的回调
setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
me.optDown.inited(me);
}, 0)
}
}
/* 列表touchstart事件 */
MeScroll.prototype.touchstartEvent = function(e) {
if (!this.optDown.use) return;
this.startPoint = this.getPoint(e); // 记录起点
this.startTop = this.getScrollTop(); // 记录此时的滚动条位置
this.startAngle = 0; // 初始角度
this.lastPoint = this.startPoint; // 重置上次move的点
this.maxTouchmoveY = this.getBodyHeight() - this.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况)
this.inTouchend = false; // 标记不是touchend
}
/* 列表touchmove事件 */
MeScroll.prototype.touchmoveEvent = function(e) {
if (!this.optDown.use) return;
let me = this;
let scrollTop = me.getScrollTop(); // 当前滚动条的距离
let curPoint = me.getPoint(e); // 当前点
let moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
// 向下拉 && 在顶部
// mescroll-body,直接判定在顶部即可
// scroll-view在滚动时不会触发touchmove,当触顶/底/左/右时,才会触发touchmove
// scroll-view滚动到顶部时,scrollTop不一定为0,也有可能大于0; 在iOS的APP中scrollTop可能为负数,不一定和startTop相等
if (moveY > 0 && (
(me.isScrollBody && scrollTop <= 0)
||
(!me.isScrollBody && (scrollTop <= 0 || (scrollTop <= me.optDown.startTop && scrollTop === me.startTop)) )
)) {
// 可下拉的条件
if (!me.inTouchend && !me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling &&
me.optUp.isBoth))) {
// 下拉的初始角度是否在配置的范围内
if(!me.startAngle) me.startAngle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
if (me.startAngle < me.optDown.minAngle) return; // 如果小于配置的角度,则不往下执行下拉刷新
// 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
me.inTouchend = true; // 标记执行touchend
me.touchendEvent(); // 提前触发touchend
return;
}
me.preventDefault(e); // 阻止默认事件
let diff = curPoint.y - me.lastPoint.y; // 和上次比,移动的距离 (大于0向下,小于0向上)
// 下拉距离 < 指定距离
if (me.downHight < me.optDown.offset) {
if (me.movetype !== 1) {
me.movetype = 1; // 加入标记,保证只执行一次
me.optDown.inOffset && me.optDown.inOffset(me); // 进入指定距离范围内那一刻的回调,只执行一次
me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
}
me.downHight += diff * me.optDown.inOffsetRate; // 越往下,高度变化越小
// 指定距离 <= 下拉距离
} else {
if (me.movetype !== 2) {
me.movetype = 2; // 加入标记,保证只执行一次
me.optDown.outOffset && me.optDown.outOffset(me); // 下拉超过指定距离那一刻的回调,只执行一次
me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
}
if (diff > 0) { // 向下拉
me.downHight += diff * me.optDown.outOffsetRate; // 越往下,高度变化越小
} else { // 向上收
me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
}
}
me.downHight = Math.round(me.downHight) // 取整
let rate = me.downHight / me.optDown.offset; // 下拉区域当前高度与指定距离的比值
me.optDown.onMoving && me.optDown.onMoving(me, rate, me.downHight); // 下拉过程中的回调,一直在执行
}
}
me.lastPoint = curPoint; // 记录本次移动的点
}
/* 列表touchend事件 */
MeScroll.prototype.touchendEvent = function(e) {
if (!this.optDown.use) return;
// 如果下拉区域高度已改变,则需重置回来
if (this.isMoveDown) {
if (this.downHight >= this.optDown.offset) {
// 符合触发刷新的条件
this.triggerDownScroll();
} else {
// 不符合的话 则重置
this.downHight = 0;
this.endDownScrollCall(this);
}
this.movetype = 0;
this.isMoveDown = false;
} else if (!this.isScrollBody && this.getScrollTop() === this.startTop) { // scroll-view到顶/左/右/底的滑动事件
let isScrollUp = this.getPoint(e).y - this.startPoint.y < 0; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
// 上滑
if (isScrollUp) {
// 需检查滑动的角度
let angle = this.getAngle(this.getPoint(e), this.startPoint); // 两点之间的角度,区间 [0,90]
if (angle > 80) {
// 检查并触发上拉
this.triggerUpScroll(true);
}
}
}
}
/* 根据点击滑动事件获取第一个手指的坐标 */
MeScroll.prototype.getPoint = function(e) {
if (!e) {
return {
x: 0,
y: 0
}
}
if (e.touches && e.touches[0]) {
return {
x: e.touches[0].pageX,
y: e.touches[0].pageY
}
} else if (e.changedTouches && e.changedTouches[0]) {
return {
x: e.changedTouches[0].pageX,
y: e.changedTouches[0].pageY
}
} else {
return {
x: e.clientX,
y: e.clientY
}
}
}
/* 计算两点之间的角度: 区间 [0,90]*/
MeScroll.prototype.getAngle = function(p1, p2) {
let x = Math.abs(p1.x - p2.x);
let y = Math.abs(p1.y - p2.y);
let z = Math.sqrt(x * x + y * y);
let angle = 0;
if (z !== 0) {
angle = Math.asin(y / z) / Math.PI * 180;
}
return angle
}
/* 触发下拉刷新 */
MeScroll.prototype.triggerDownScroll = function() {
if (this.optDown.beforeLoading && this.optDown.beforeLoading(this)) {
//return true则处于完全自定义状态
} else {
this.showDownScroll(); // 下拉刷新中...
!this.optDown.native && this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
}
}
/* 显示下拉进度布局 */
MeScroll.prototype.showDownScroll = function() {
this.isDownScrolling = true; // 标记下拉中
if (this.optDown.native) {
uni.startPullDownRefresh(); // 系统自带的下拉刷新
this.showDownLoadingCall(0); // 仍触发showLoading,因为上拉加载用到
} else{
this.downHight = this.optDown.offset; // 更新下拉区域高度
this.showDownLoadingCall(this.downHight); // 下拉刷新中...
}
}
MeScroll.prototype.showDownLoadingCall = function(downHight) {
this.optDown.showLoading && this.optDown.showLoading(this, downHight); // 下拉刷新中...
this.optDown.afterLoading && this.optDown.afterLoading(this, downHight); // 下拉刷新中...触发之后马上要执行的代码
}
/* 显示系统自带的下拉刷新时需要处理的业务 */
MeScroll.prototype.onPullDownRefresh = function() {
this.isDownScrolling = true; // 标记下拉中
this.showDownLoadingCall(0); // 仍触发showLoading,因为上拉加载用到
this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
}
/* 结束下拉刷新 */
MeScroll.prototype.endDownScroll = function() {
if (this.optDown.native) { // 结束原生下拉刷新
this.isDownScrolling = false;
this.endDownScrollCall(this);
uni.stopPullDownRefresh();
return
}
let me = this;
// 结束下拉刷新的方法
let endScroll = function() {
me.downHight = 0;
me.isDownScrolling = false;
me.endDownScrollCall(me);
if(!me.isScrollBody){
me.setScrollHeight(0) // scroll-view重置滚动区域,使数据不满屏时仍可检查触发翻页
me.scrollTo(0,0) // scroll-view需重置滚动条到顶部,避免startTop大于0时,对下拉刷新的影响
}
}
// 结束下拉刷新时的回调
let delay = 0;
if (me.optDown.beforeEndDownScroll) delay = me.optDown.beforeEndDownScroll(me); // 结束下拉刷新的延时,单位ms
if (typeof delay === 'number' && delay > 0) {
setTimeout(endScroll, delay);
} else {
endScroll();
}
}
MeScroll.prototype.endDownScrollCall = function() {
this.optDown.endDownScroll && this.optDown.endDownScroll(this);
this.optDown.afterEndDownScroll && this.optDown.afterEndDownScroll(this);
}
/* 锁定下拉刷新:isLock=ture,null锁定;isLock=false解锁 */
MeScroll.prototype.lockDownScroll = function(isLock) {
if (isLock == null) isLock = true;
this.optDown.isLock = isLock;
}
/* 锁定上拉加载:isLock=ture,null锁定;isLock=false解锁 */
MeScroll.prototype.lockUpScroll = function(isLock) {
if (isLock == null) isLock = true;
this.optUp.isLock = isLock;
}
/* -------初始化上拉加载------- */
MeScroll.prototype.initUpScroll = function() {
let me = this;
// 配置参数
me.optUp = me.options.up || {use: false}
if(!me.optUp.textColor && me.hasColor(me.optUp.bgColor)) me.optUp.textColor = "#fff"; // 当bgColor有值且textColor未设置,则textColor默认白色
me.extendUpScroll(me.optUp);
if (me.optUp.use === false) return; // 配置不使用上拉加载时,则不初始化上拉布局
me.optUp.hasNext = true; // 如果使用上拉,则默认有下一页
me.startNum = me.optUp.page.num + 1; // 记录page开始的页码
// 初始化完毕的回调
if (me.optUp.inited) {
setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
me.optUp.inited(me);
}, 0)
}
}
/*滚动到底部的事件 (仅mescroll-body生效)*/
MeScroll.prototype.onReachBottom = function() {
if (this.isScrollBody && !this.isUpScrolling) { // 只能支持下拉刷新的时候同时可以触发上拉加载,否则滚动到底部就需要上滑一点才能触发onReachBottom
if (!this.optUp.isLock && this.optUp.hasNext) {
this.triggerUpScroll();
}
}
}
/*列表滚动事件 (仅mescroll-body生效)*/
MeScroll.prototype.onPageScroll = function(e) {
if (!this.isScrollBody) return;
// 更新滚动条的位置 (主要用于判断下拉刷新时,滚动条是否在顶部)
this.setScrollTop(e.scrollTop);
// 顶部按钮的显示隐藏
if (e.scrollTop >= this.optUp.toTop.offset) {
this.showTopBtn();
} else {
this.hideTopBtn();
}
}
/*列表滚动事件*/
MeScroll.prototype.scroll = function(e, onScroll) {
// 更新滚动条的位置
this.setScrollTop(e.scrollTop);
// 更新滚动内容高度
this.setScrollHeight(e.scrollHeight);
// 向上滑还是向下滑动
if (this.preScrollY == null) this.preScrollY = 0;
this.isScrollUp = e.scrollTop - this.preScrollY > 0;
this.preScrollY = e.scrollTop;
// 上滑 && 检查并触发上拉
this.isScrollUp && this.triggerUpScroll(true);
// 顶部按钮的显示隐藏
if (e.scrollTop >= this.optUp.toTop.offset) {
this.showTopBtn();
} else {
this.hideTopBtn();
}
// 滑动监听
this.optUp.onScroll && onScroll && onScroll()
}
/* 触发上拉加载 */
MeScroll.prototype.triggerUpScroll = function(isCheck) {
if (!this.isUpScrolling && this.optUp.use && this.optUp.callback) {
// 是否校验在底部; 默认不校验
if (isCheck === true) {
let canUp = false;
// 还有下一页 && 没有锁定 && 不在下拉中
if (this.optUp.hasNext && !this.optUp.isLock && !this.isDownScrolling) {
if (this.getScrollBottom() <= this.optUp.offset) { // 到底部
canUp = true; // 标记可上拉
}
}
if (canUp === false) return;
}
this.showUpScroll(); // 上拉加载中...
this.optUp.page.num++; // 预先加一页,如果失败则减回
this.isUpAutoLoad = true; // 标记上拉已经自动执行过,避免初始化时多次触发上拉回调
this.num = this.optUp.page.num; // 把最新的页数赋值在mescroll上,避免对page的影响
this.size = this.optUp.page.size; // 把最新的页码赋值在mescroll上,避免对page的影响
this.time = this.optUp.page.time; // 把最新的页码赋值在mescroll上,避免对page的影响
this.optUp.callback(this); // 执行回调,联网加载数据
}
}
/* 显示上拉加载中 */
MeScroll.prototype.showUpScroll = function() {
this.isUpScrolling = true; // 标记上拉加载中
this.optUp.showLoading && this.optUp.showLoading(this); // 回调
}
/* 显示上拉无更多数据 */
MeScroll.prototype.showNoMore = function() {
this.optUp.hasNext = false; // 标记无更多数据
this.optUp.showNoMore && this.optUp.showNoMore(this); // 回调
}
/* 隐藏上拉区域**/
MeScroll.prototype.hideUpScroll = function() {
this.optUp.hideUpScroll && this.optUp.hideUpScroll(this); // 回调
}
/* 结束上拉加载 */
MeScroll.prototype.endUpScroll = function(isShowNoMore) {
if (isShowNoMore != null) { // isShowNoMore=null,不处理下拉状态,下拉刷新的时候调用
if (isShowNoMore) {
this.showNoMore(); // isShowNoMore=true,显示无更多数据
} else {
this.hideUpScroll(); // isShowNoMore=false,隐藏上拉加载
}
}
this.isUpScrolling = false; // 标记结束上拉加载
}
/*
*isShowLoading 是否显示进度布局;
* 1.默认null,不传参,则显示上拉加载的进度布局
* 2.传参true, 则显示下拉刷新的进度布局
* 3.传参false,则不显示上拉和下拉的进度 (常用于静默更新列表数据)
*/
MeScroll.prototype.resetUpScroll = function(isShowLoading) {
if (this.optUp && this.optUp.use) {
let page = this.optUp.page;
this.prePageNum = page.num; // 缓存重置前的页码,加载失败可退回
this.prePageTime = page.time; // 缓存重置前的时间,加载失败可退回
page.num = this.startNum; // 重置为第一页
page.time = null; // 重置时间为空
if (!this.isDownScrolling && isShowLoading !== false) { // 如果不是下拉刷新触发的resetUpScroll并且不配置列表静默更新,则显示进度;
if (isShowLoading == null) {
this.removeEmpty(); // 移除空布局
this.showUpScroll(); // 不传参,默认显示上拉加载的进度布局
} else {
this.showDownScroll(); // 传true,显示下拉刷新的进度布局,不清空列表
}
}
this.isUpAutoLoad = true; // 标记上拉已经自动执行过,避免初始化时多次触发上拉回调
this.num = page.num; // 把最新的页数赋值在mescroll上,避免对page的影响
this.size = page.size; // 把最新的页码赋值在mescroll上,避免对page的影响
this.time = page.time; // 把最新的页码赋值在mescroll上,避免对page的影响
this.optUp.callback && this.optUp.callback(this); // 执行上拉回调
}
}
/* 设置page.num的值 */
MeScroll.prototype.setPageNum = function(num) {
this.optUp.page.num = num - 1;
}
/* 设置page.size的值 */
MeScroll.prototype.setPageSize = function(size) {
this.optUp.page.size = size;
}
/* ,
* dataSize: 当前页的数据量(必传)
* totalPage: 总页数(必传)
* systime: 服务器时间 (可空)
*/
MeScroll.prototype.endByPage = function(dataSize, totalPage, systime) {
let hasNext;
if (this.optUp.use && totalPage != null) hasNext = this.optUp.page.num < totalPage; // 是否还有下一页
this.endSuccess(dataSize, hasNext, systime);
}
/* ,
* dataSize: 当前页的数据量(必传)
* totalSize: 列表所有数据总数量(必传)
* systime: 服务器时间 (可空)
*/
MeScroll.prototype.endBySize = function(dataSize, totalSize, systime) {
let hasNext;
if (this.optUp.use && totalSize != null) {
let loadSize = (this.optUp.page.num - 1) * this.optUp.page.size + dataSize; // 已加载的数据总数
hasNext = loadSize < totalSize; // 是否还有下一页
}
this.endSuccess(dataSize, hasNext, systime);
}
/* ,
* dataSize: 当前页的数据个数(不是所有页的数据总和),用于上拉加载判断是否还有下一页.如果不传,则会判断还有下一页
* hasNext: 是否还有下一页,布尔类型;用来解决这个小问题:比如列表共有20条数据,每页加载10条,共2页.如果只根据dataSize判断,则需翻到第三页才会知道无更多数据,如果传了hasNext,则翻到第二页即可显示无更多数据.
* systime: 服务器时间(可空);用来解决这个小问题:当准备翻下一页时,数据库新增了几条记录,此时翻下一页,前面的几条数据会和上一页的重复;这里传入了systime,那么upCallback的page.time就会有值,把page.time传给服务器,让后台过滤新加入的那几条记录
*/
MeScroll.prototype.endSuccess = function(dataSize, hasNext, systime) {
let me = this;
// 结束下拉刷新
if (me.isDownScrolling) me.endDownScroll();
// 结束上拉加载
if (me.optUp.use) {
let isShowNoMore; // 是否已无更多数据
if (dataSize != null) {
let pageNum = me.optUp.page.num; // 当前页码
let pageSize = me.optUp.page.size; // 每页长度
// 如果是第一页
if (pageNum === 1) {
if (systime) me.optUp.page.time = systime; // 设置加载列表数据第一页的时间
}
if (dataSize < pageSize || hasNext === false) {
// 返回的数据不满一页时,则说明已无更多数据
me.optUp.hasNext = false;
if (dataSize === 0 && pageNum === 1) {
// 如果第一页无任何数据且配置了空布局
isShowNoMore = false;
me.showEmpty();
} else {
// 总列表数少于配置的数量,则不显示无更多数据
let allDataSize = (pageNum - 1) * pageSize + dataSize;
if (allDataSize < me.optUp.noMoreSize) {
isShowNoMore = false;
} else {
isShowNoMore = true;
}
me.removeEmpty(); // 移除空布局
}
} else {
// 还有下一页
isShowNoMore = false;
me.optUp.hasNext = true;
me.removeEmpty(); // 移除空布局
}
}
// 隐藏上拉
me.endUpScroll(isShowNoMore);
}
}
/* 回调失败,结束下拉刷新和上拉加载 */
MeScroll.prototype.endErr = function(errDistance) {
// 结束下拉,回调失败重置回原来的页码和时间
if (this.isDownScrolling) {
let page = this.optUp.page;
if (page && this.prePageNum) {
page.num = this.prePageNum;
page.time = this.prePageTime;
}
this.endDownScroll();
}
// 结束上拉,回调失败重置回原来的页码
if (this.isUpScrolling) {
this.optUp.page.num--;
this.endUpScroll(false);
// 如果是mescroll-body,则需往回滚一定距离
if(this.isScrollBody && errDistance !== 0){ // 不处理0
if(!errDistance) errDistance = this.optUp.errDistance; // 不传,则取默认
this.scrollTo(this.getScrollTop() - errDistance, 0) // 往上回滚的距离
}
}
}
/* 显示空布局 */
MeScroll.prototype.showEmpty = function() {
this.optUp.empty.use && this.optUp.empty.onShow && this.optUp.empty.onShow(true)
}
/* 移除空布局 */
MeScroll.prototype.removeEmpty = function() {
this.optUp.empty.use && this.optUp.empty.onShow && this.optUp.empty.onShow(false)
}
/* 显示回到顶部的按钮 */
MeScroll.prototype.showTopBtn = function() {
if (!this.topBtnShow) {
this.topBtnShow = true;
this.optUp.toTop.onShow && this.optUp.toTop.onShow(true);
}
}
/* 隐藏回到顶部的按钮 */
MeScroll.prototype.hideTopBtn = function() {
if (this.topBtnShow) {
this.topBtnShow = false;
this.optUp.toTop.onShow && this.optUp.toTop.onShow(false);
}
}
/* 获取滚动条的位置 */
MeScroll.prototype.getScrollTop = function() {
return this.scrollTop || 0
}
/* 记录滚动条的位置 */
MeScroll.prototype.setScrollTop = function(y) {
this.scrollTop = y;
}
/* 滚动到指定位置 */
MeScroll.prototype.scrollTo = function(y, t) {
this.myScrollTo && this.myScrollTo(y, t) // scrollview需自定义回到顶部方法
}
/* 自定义scrollTo */
MeScroll.prototype.resetScrollTo = function(myScrollTo) {
this.myScrollTo = myScrollTo
}
/* 滚动条到底部的距离 */
MeScroll.prototype.getScrollBottom = function() {
return this.getScrollHeight() - this.getClientHeight() - this.getScrollTop()
}
/*
star: 开始值
end: 结束值
callback(step,timer): 回调step值,计步器timer,可自行通过window.clearInterval(timer)结束计步器;
t: 计步时长,传0则直接回调end值;不传则默认300ms
rate: 周期;不传则默认30ms计步一次
* */
MeScroll.prototype.getStep = function(star, end, callback, t, rate) {
let diff = end - star; // 差值
if (t === 0 || diff === 0) {
callback && callback(end);
return;
}
t = t || 300; // 时长 300ms
rate = rate || 30; // 周期 30ms
let count = t / rate; // 次数
let step = diff / count; // 步长
let i = 0; // 计数
let timer = setInterval(function() {
if (i < count - 1) {
star += step;
callback && callback(star, timer);
i++;
} else {
callback && callback(end, timer); // 最后一次直接设置end,避免计算误差
clearInterval(timer);
}
}, rate);
}
/* 滚动容器的高度 */
MeScroll.prototype.getClientHeight = function(isReal) {
let h = this.clientHeight || 0
if (h === 0 && isReal !== true) { // 未获取到容器的高度,可临时取body的高度 (可能会有误差)
h = this.getBodyHeight()
}
return h
}
MeScroll.prototype.setClientHeight = function(h) {
this.clientHeight = h;
}
/* 滚动内容的高度 */
MeScroll.prototype.getScrollHeight = function() {
return this.scrollHeight || 0;
}
MeScroll.prototype.setScrollHeight = function(h) {
this.scrollHeight = h;
}
/* body的高度 */
MeScroll.prototype.getBodyHeight = function() {
return this.bodyHeight || 0;
}
MeScroll.prototype.setBodyHeight = function(h) {
this.bodyHeight = h;
}
/* 阻止浏览器默认滚动事件 */
MeScroll.prototype.preventDefault = function(e) {
// 小程序不支持e.preventDefault, 已在wxs中禁止
// app的bounce只能通过配置pages.json的style.app-plus.bounce为"none"来禁止, 或使用renderjs禁止
// cancelable:是否可以被禁用; defaultPrevented:是否已经被禁用
if (e && e.cancelable && !e.defaultPrevented) e.preventDefault()
}

420
components/mescroll-uni/mescroll-uni.vue

@ -0,0 +1,420 @@
<template>
<view class="mescroll-uni-warp">
<scroll-view :id="viewId" class="mescroll-uni" :class="{'mescroll-uni-fixed':isFixed}" :style="{'height':scrollHeight,'padding-top':padTop,'padding-bottom':padBottom,'top':fixedTop,'bottom':fixedBottom}" :scroll-top="scrollTop" :scroll-with-animation="scrollAnim" @scroll="scroll" :scroll-y='scrollable' :enable-back-to-top="true">
<view class="mescroll-uni-content mescroll-render-touch"
@touchstart="wxsBiz.touchstartEvent"
@touchmove="wxsBiz.touchmoveEvent"
@touchend="wxsBiz.touchendEvent"
@touchcancel="wxsBiz.touchendEvent"
:change:prop="wxsBiz.propObserver"
:prop="wxsProp">
<!-- 状态栏 -->
<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
<view class="mescroll-wxs-content" :style="{'transform': translateY, 'transition': transition}" :change:prop="wxsBiz.callObserver" :prop="callProp">
<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
<view class="downwarp-content">
<view class="downwarp-progress mescroll-wxs-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mescroll.optDown.textColor, 'transform': downRotate}"></view>
<view class="downwarp-tip">{{downText}}</view>
</view>
</view>
<!-- 列表内容 -->
<slot></slot>
<!-- 空布局 -->
<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
<view v-show="upLoadType===1">
<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
</view>
<!-- 无数据 -->
<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
</view>
</view>
<!-- 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效) -->
<!-- #ifdef H5 -->
<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
<!-- #endif -->
<!-- 适配iPhoneX -->
<view v-if="safearea" class="mescroll-safearea"></view>
</view>
</scroll-view>
<!-- 回到顶部按钮 (fixed元素,需写在scroll-view外面,防止滚动的时候抖动)-->
<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
<!-- #endif -->
</view>
</template>
<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
<script src="./wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
<!-- #endif -->
<!-- app, h5使用renderjs -->
<!-- #ifdef APP-PLUS || H5 -->
<script module="renderBiz" lang="renderjs">
import renderBiz from './wxs/renderjs.js';
export default {
mixins:[renderBiz]
}
</script>
<!-- #endif -->
<script>
// mescroll-uni.js,
import MeScroll from './mescroll-uni.js';
//
import GlobalOption from './mescroll-uni-option.js';
//
import MescrollEmpty from './components/mescroll-empty.vue';
//
import MescrollTop from './components/mescroll-top.vue';
// wxs(renderjs)mixins
import WxsMixin from './wxs/mixins.js';
export default {
mixins: [WxsMixin],
components: {
MescrollEmpty,
MescrollTop
},
data() {
return {
mescroll: {optDown:{},optUp:{}}, // mescroll
viewId: 'id_' + Math.random().toString(36).substr(2,16), // mescrollid(,)
downHight: 0, //:
downRate: 0, // (inOffset: rate<1; outOffset: rate>=1)
downLoadType: 0, // : 0(loading), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
upLoadType: 0, // : 0(loading), 1loading, 2,END, 3(,END)
isShowEmpty: false, //
isShowToTop: false, //
scrollTop: 0, //
scrollAnim: false, //
windowTop: 0, // 使
windowBottom: 0, // 使
windowHeight: 0, // 使
statusBarHeight: 0 //
}
},
props: {
down: Object, //
up: Object, //
top: [String, Number], // (20, "20rpx", "20px", "20%", rpx, windowHeight)
topbar: [Boolean, String], // top, false (使:,, ,,,)
bottom: [String, Number], // (20, "20rpx", "20px", "20%", rpx, windowHeight)
safearea: Boolean, // bottom, false (iPhoneX使)
fixed: { // fixedmescroll, true
type: Boolean,
default: true
},
height: [String, Number], // mescroll, ,使fixed. (20, "20rpx", "20px", "20%", rpx, windowHeight)
bottombar:{ // TabBar(H5tab)
type: Boolean,
default: true
}
},
computed: {
// 使fixed (height,使)
isFixed(){
return !this.height && this.fixed
},
// mescroll
scrollHeight(){
if (this.isFixed) {
return "auto"
} else if(this.height){
return this.toPx(this.height) + 'px'
}else{
return "100%"
}
},
// (px)
numTop() {
return this.toPx(this.top)
},
fixedTop() {
return this.isFixed ? (this.numTop + this.windowTop) + 'px' : 0
},
padTop() {
return !this.isFixed ? this.numTop + 'px' : 0
},
// (px)
numBottom() {
return this.toPx(this.bottom)
},
fixedBottom() {
return this.isFixed ? (this.numBottom + this.windowBottom) + 'px' : 0
},
padBottom() {
return !this.isFixed ? this.numBottom + 'px' : 0
},
//
isDownReset(){
return this.downLoadType===3 || this.downLoadType===4
},
//
transition() {
return this.isDownReset ? 'transform 300ms' : '';
},
translateY() {
return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform使fixed,fixedmescroll
},
//
scrollable(){
return this.downLoadType===0 || this.isDownReset
},
//
isDownLoading(){
return this.downLoadType === 3
},
//
downRotate(){
return 'rotate(' + 360 * this.downRate + 'deg)'
},
//
downText(){
if(!this.mescroll) return ""; //
switch (this.downLoadType){
case 1: return this.mescroll.optDown.textInOffset;
case 2: return this.mescroll.optDown.textOutOffset;
case 3: return this.mescroll.optDown.textLoading;
case 4: return this.mescroll.optDown.textLoading;
default: return this.mescroll.optDown.textInOffset;
}
}
},
methods: {
//number,rpx,upx,px,% --> px
toPx(num){
if(typeof num === "string"){
if (num.indexOf('px') !== -1) {
if(num.indexOf('rpx') !== -1) { // "10rpx"
num = num.replace('rpx', '');
} else if(num.indexOf('upx') !== -1) { // "10upx"
num = num.replace('upx', '');
} else { // "10px"
return Number(num.replace('px', ''))
}
}else if (num.indexOf('%') !== -1){
// ,windowHeight,"10%"windowHeight10%
let rate = Number(num.replace("%","")) / 100
return this.windowHeight * rate
}
}
return num ? uni.upx2px(Number(num)) : 0
},
//,
scroll(e) {
this.mescroll.scroll(e.detail, () => {
this.$emit('scroll', this.mescroll) // this.mescroll.scrollTop; this.mescroll.isScrollUp
})
},
//
emptyClick() {
this.$emit('emptyclick', this.mescroll)
},
//
toTopClick() {
this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); //
this.$emit('topclick', this.mescroll); //
},
// (使,)
setClientHeight() {
if (this.mescroll.getClientHeight(true) === 0 && !this.isExec) {
this.isExec = true; //
this.$nextTick(() => { // dom
this.getClientInfo(data=>{
this.isExec = false;
if (data) {
this.mescroll.setClientHeight(data.height);
} else if (this.clientNum != 3) { // ,dom,,3
this.clientNum = this.clientNum == null ? 1 : this.clientNum + 1;
setTimeout(() => {
this.setClientHeight()
}, this.clientNum * 100)
}
})
})
}
},
//
getClientInfo(success){
let query = uni.createSelectorQuery();
// #ifndef MP-ALIPAY || MP-DINGTALK
query = query.in(this) // in(this),in(this),
// #endif
let view = query.select('#' + this.viewId);
view.boundingClientRect(data => {
success(data)
}).exec();
}
},
// 使createdmescroll; mountedcssH5
created() {
let vm = this;
let diyOption = {
//
down: {
inOffset() {
vm.downLoadType = 1; // offset (mescroll,)
},
outOffset() {
vm.downLoadType = 2; // offset (mescroll,)
},
onMoving(mescroll, rate, downHight) {
// ,;
vm.downHight = downHight; // (mescroll,)
vm.downRate = rate; // (inOffset: rate<1; outOffset: rate>=1)
},
showLoading(mescroll, downHight) {
vm.downLoadType = 3; // (mescroll,)
vm.downHight = downHight; // (mescroll,)
},
endDownScroll() {
vm.downLoadType = 4; // (mescroll,)
vm.downHight = 0; // (mescroll,)
vm.downResetTimer && clearTimeout(vm.downResetTimer)
vm.downResetTimer = setTimeout(()=>{ // ,0,便this.transition,iOS
if(vm.downLoadType===4) vm.downLoadType = 0
},300)
},
//
callback: function(mescroll) {
vm.$emit('down', mescroll)
}
},
//
up: {
//
showLoading() {
vm.upLoadType = 1;
},
//
showNoMore() {
vm.upLoadType = 2;
},
//
hideUpScroll(mescroll) {
vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
},
//
empty: {
onShow(isShow) { //
vm.isShowEmpty = isShow;
}
},
//
toTop: {
onShow(isShow) { //
vm.isShowToTop = isShow;
}
},
//
callback: function(mescroll) {
vm.$emit('up', mescroll);
// (mescroll)
vm.setClientHeight()
}
}
}
MeScroll.extend(diyOption, GlobalOption); //
let myOption = JSON.parse(JSON.stringify({'down': vm.down,'up': vm.up})) // ,props
MeScroll.extend(myOption, diyOption); //
// MeScroll
vm.mescroll = new MeScroll(myOption);
vm.mescroll.viewId = vm.viewId; // id
// initmescroll
vm.$emit('init', vm.mescroll);
//
const sys = uni.getSystemInfoSync();
if(sys.windowTop) vm.windowTop = sys.windowTop;
if(sys.windowBottom) vm.windowBottom = sys.windowBottom;
if(sys.windowHeight) vm.windowHeight = sys.windowHeight;
if(sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
// 使downbottomOffset
vm.mescroll.setBodyHeight(sys.windowHeight);
// 使scrollview,scrollTo
vm.mescroll.resetScrollTo((y, t) => {
vm.scrollAnim = (t !== 0); // t0,使
if(typeof y === 'string'){
// slotscroll-into-view, 使
vm.getClientInfo(function(rect){
let mescrollTop = rect.top // mescroll
let selector;
if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
selector = '#'+y // #. id
}else{
selector = y
// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
if(y.indexOf('>>>')!=-1){ // ()
selector = y.split('>>>')[1].trim()
}
// #endif
}
uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
if (rect) {
let curY = vm.mescroll.getScrollTop()
let top = rect.top - mescrollTop
top += curY
if(!vm.isFixed) top -= vm.numTop
vm.scrollTop = curY;
vm.$nextTick(function() {
vm.scrollTop = top
})
} else{
console.error(selector + ' does not exist');
}
}).exec()
})
return;
}
let curY = vm.mescroll.getScrollTop()
if (t === 0 || t === 300) { // t使300,使
vm.scrollTop = curY;
vm.$nextTick(function() {
vm.scrollTop = y
})
} else {
vm.mescroll.getStep(curY, y, step => { // t
vm.scrollTop = step
}, t)
}
})
// up.toTop.safearea,vuesafearea
if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
vm.mescroll.optUp.toTop.safearea = vm.safearea;
}
},
mounted() {
//
this.setClientHeight()
}
}
</script>
<style>
@import "./mescroll-uni.css";
@import "./components/mescroll-down.css";
@import './components/mescroll-up.css';
</style>

50
components/mescroll-uni/mixins/mescroll-comp.js

@ -0,0 +1,50 @@
/**
* mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期:
* 当一个页面只有一个mescroll-body写在子组件时, 则使用mescroll-comp.js (参考 mescroll-comp 案例)
* 当一个页面有多个mescroll-body写在子组件时, 则使用mescroll-more.js (参考 mescroll-more 案例)
*/
const MescrollCompMixin = {
// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件 (一级)
onPageScroll(e) {
this.handlePageScroll(e)
},
onReachBottom() {
this.handleReachBottom()
},
// 当down的native: true时, 还需传递此方法进到子组件
onPullDownRefresh(){
this.handlePullDownRefresh()
},
// mescroll-body写在子子子...组件的情况 (多级)
data() {
return {
mescroll: {
onPageScroll: e=>{
this.handlePageScroll(e)
},
onReachBottom: ()=>{
this.handleReachBottom()
},
onPullDownRefresh: ()=>{
this.handlePullDownRefresh()
}
}
}
},
methods:{
handlePageScroll(e){
let item = this.$refs["mescrollItem"];
if(item && item.mescroll) item.mescroll.onPageScroll(e);
},
handleReachBottom(){
let item = this.$refs["mescrollItem"];
if(item && item.mescroll) item.mescroll.onReachBottom();
},
handlePullDownRefresh(){
let item = this.$refs["mescrollItem"];
if(item && item.mescroll) item.mescroll.onPullDownRefresh();
}
}
}
export default MescrollCompMixin;

51
components/mescroll-uni/mixins/mescroll-more-item.js

@ -0,0 +1,51 @@
/**
* mescroll-more-item的mixins, 仅在多个 mescroll-body 写在子组件时使用 (参考 mescroll-more 案例)
*/
const MescrollMoreItemMixin = {
// 支付宝小程序不支持props的mixin,需写在具体的页面中
// #ifndef MP-ALIPAY || MP-DINGTALK
props:{
i: Number, // 每个tab页的专属下标
index: { // 当前tab的下标
type: Number,
default(){
return 0
}
}
},
// #endif
data() {
return {
downOption:{
auto:false // 不自动加载
},
upOption:{
auto:false // 不自动加载
},
isInit: false // 当前tab是否已初始化
}
},
watch:{
// 监听下标的变化
index(val){
if (this.i === val && !this.isInit) {
this.isInit = true; // 标记为true
this.mescroll && this.mescroll.triggerDownScroll();
}
}
},
methods: {
// mescroll组件初始化的回调,可获取到mescroll对象 (覆盖mescroll-mixins.js的mescrollInit, 为了标记isInit)
mescrollInit(mescroll) {
this.mescroll = mescroll;
this.mescrollInitByRef && this.mescrollInitByRef(); // 兼容字节跳动小程序
// 自动加载当前tab的数据
if(this.i === this.index){
this.isInit = true; // 标记为true
this.mescroll.triggerDownScroll();
}
},
}
}
export default MescrollMoreItemMixin;

56
components/mescroll-uni/mixins/mescroll-more.js

@ -0,0 +1,56 @@
/**
* mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期:
* 当一个页面只有一个mescroll-body写在子组件时, 则使用mescroll-comp.js (参考 mescroll-comp 案例)
* 当一个页面有多个mescroll-body写在子组件时, 则使用mescroll-more.js (参考 mescroll-more 案例)
*/
const MescrollMoreMixin = {
data() {
return {
tabIndex: 0 // 当前tab下标
}
},
// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件
onPageScroll(e) {
let mescroll = this.getMescroll(this.tabIndex);
mescroll && mescroll.onPageScroll(e);
},
onReachBottom() {
let mescroll = this.getMescroll(this.tabIndex);
mescroll && mescroll.onReachBottom();
},
// 当down的native: true时, 还需传递此方法进到子组件
onPullDownRefresh(){
let mescroll = this.getMescroll(this.tabIndex);
mescroll && mescroll.onPullDownRefresh();
},
methods:{
// 根据下标获取对应子组件的mescroll
getMescroll(i){
if(!this.mescrollItems) this.mescrollItems = [];
if(!this.mescrollItems[i]) {
// v-for中的refs
let vForItem = this.$refs["mescrollItem"];
if(vForItem){
this.mescrollItems[i] = vForItem[i]
}else{
// 普通的refs,不可重复
this.mescrollItems[i] = this.$refs["mescrollItem"+i];
}
}
let item = this.mescrollItems[i]
return item ? item.mescroll : null
},
// 切换tab,恢复滚动条位置
tabChange(i){
let mescroll = this.getMescroll(i);
if(mescroll){
// 延时(比$nextTick靠谱一些),确保元素已渲染
setTimeout(()=>{
mescroll.scrollTo(mescroll.getScrollTop(),0)
},30)
}
}
}
}
export default MescrollMoreMixin;

102
components/mescroll-uni/wxs/mixins.js

@ -0,0 +1,102 @@
// 定义在wxs (含renderjs) 逻辑层的数据和方法, 与视图层相互通信
const WxsMixin = {
data() {
return {
// 传入wxs视图层的数据 (响应式)
wxsProp: {
optDown:{}, // 下拉刷新的配置
scrollTop:0, // 滚动条的距离
bodyHeight:0, // body的高度
isDownScrolling:false, // 是否正在下拉刷新中
isUpScrolling:false, // 是否正在上拉加载中
isScrollBody:true, // 是否为mescroll-body滚动
isUpBoth:true, // 上拉加载时,是否同时可以下拉刷新
t: 0 // 数据更新的标记 (只有数据更新了,才会触发wxs的Observer)
},
// 标记调用wxs视图层的方法
callProp: {
callType: '', // 方法名
t: 0 // 数据更新的标记 (只有数据更新了,才会触发wxs的Observer)
},
// 不用wxs的平台使用此处的wxsBiz对象,抹平wxs的写法 (微信小程序和APP使用的wxsBiz对象是./wxs/wxs.wxs)
// #ifndef MP-WEIXIN || MP-QQ || APP-PLUS || H5
wxsBiz: {
//注册列表touchstart事件,用于下拉刷新
touchstartEvent: e=> {
this.mescroll.touchstartEvent(e);
},
//注册列表touchmove事件,用于下拉刷新
touchmoveEvent: e=> {
this.mescroll.touchmoveEvent(e);
},
//注册列表touchend事件,用于下拉刷新
touchendEvent: e=> {
this.mescroll.touchendEvent(e);
},
propObserver(){}, // 抹平wxs的写法
callObserver(){} // 抹平wxs的写法
},
// #endif
// 不用renderjs的平台使用此处的renderBiz对象,抹平renderjs的写法 (app 和 h5 使用的renderBiz对象是./wxs/renderjs.js)
// #ifndef APP-PLUS || H5
renderBiz: {
propObserver(){} // 抹平renderjs的写法
}
// #endif
}
},
methods: {
// wxs视图层调用逻辑层的回调
wxsCall(msg){
if(msg.type === 'setWxsProp'){
// 更新wxsProp数据 (值改变才触发更新)
this.wxsProp = {
optDown: this.mescroll.optDown,
scrollTop: this.mescroll.getScrollTop(),
bodyHeight: this.mescroll.getBodyHeight(),
isDownScrolling: this.mescroll.isDownScrolling,
isUpScrolling: this.mescroll.isUpScrolling,
isUpBoth: this.mescroll.optUp.isBoth,
isScrollBody:this.mescroll.isScrollBody,
t: Date.now()
}
}else if(msg.type === 'setLoadType'){
// 设置inOffset,outOffset的状态
this.downLoadType = msg.downLoadType
}else if(msg.type === 'triggerDownScroll'){
// 主动触发下拉刷新
this.mescroll.triggerDownScroll();
}else if(msg.type === 'endDownScroll'){
// 结束下拉刷新
this.mescroll.endDownScroll();
}else if(msg.type === 'triggerUpScroll'){
// 主动触发上拉加载
this.mescroll.triggerUpScroll(true);
}
}
},
mounted() {
// #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5
// 配置主动触发wxs显示加载进度的回调
this.mescroll.optDown.afterLoading = ()=>{
this.callProp = {callType: "showLoading", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
}
// 配置主动触发wxs隐藏加载进度的回调
this.mescroll.optDown.afterEndDownScroll = ()=>{
this.callProp = {callType: "endDownScroll", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
setTimeout(()=>{
if(this.downLoadType === 4 || this.downLoadType === 0){
this.callProp = {callType: "clearTransform", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
}
},320)
}
// 初始化wxs的数据
this.wxsCall({type: 'setWxsProp'})
// #endif
}
}
export default WxsMixin;

92
components/mescroll-uni/wxs/renderjs.js

@ -0,0 +1,92 @@
// 使用renderjs直接操作window对象,实现动态控制app和h5的bounce
// bounce: iOS橡皮筋,Android半月弧,h5浏览器下拉背景等效果 (下拉刷新时禁止)
// https://uniapp.dcloud.io/frame?id=renderjs
// 与wxs的me实例一致
var me = {}
// 初始化window对象的touch事件 (仅初始化一次)
if(window && !window.$mescrollRenderInit){
window.$mescrollRenderInit = true
window.addEventListener('touchstart', function(e){
if (me.disabled()) return;
me.startPoint = me.getPoint(e); // 记录起点
}, {passive: true})
window.addEventListener('touchmove', function(e){
if (me.disabled()) return;
if (me.getScrollTop() > 0) return; // 需在顶部下拉,才禁止bounce
var curPoint = me.getPoint(e); // 当前点
var moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
// 向下拉
if (moveY > 0) {
// 可下拉的条件
if (!me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling && me.isUpBoth))) {
// 只有touch在mescroll的view上面,才禁止bounce
var el = e.target;
var isMescrollTouch = false;
while (el && el.tagName && el.tagName !== 'UNI-PAGE-BODY' && el.tagName != "BODY") {
var cls = el.classList;
if (cls && cls.contains('mescroll-render-touch')) {
isMescrollTouch = true
break;
}
el = el.parentNode; // 继续检查其父元素
}
// 禁止bounce (不会对swiper和iOS侧滑返回造成影响)
if (isMescrollTouch && e.cancelable && !e.defaultPrevented) e.preventDefault();
}
}
}, {passive: false})
}
/* 获取滚动条的位置 */
me.getScrollTop = function() {
return me.scrollTop || 0
}
/* 是否禁用下拉刷新 */
me.disabled = function(){
return !me.optDown || !me.optDown.use || me.optDown.native
}
/* 根据点击滑动事件获取第一个手指的坐标 */
me.getPoint = function(e) {
if (!e) {
return {x: 0,y: 0}
}
if (e.touches && e.touches[0]) {
return {x: e.touches[0].pageX,y: e.touches[0].pageY}
} else if (e.changedTouches && e.changedTouches[0]) {
return {x: e.changedTouches[0].pageX,y: e.changedTouches[0].pageY}
} else {
return {x: e.clientX,y: e.clientY}
}
}
/**
* 监听逻辑层数据的变化 (实时更新数据)
*/
function propObserver(wxsProp) {
me.optDown = wxsProp.optDown
me.scrollTop = wxsProp.scrollTop
me.isDownScrolling = wxsProp.isDownScrolling
me.isUpScrolling = wxsProp.isUpScrolling
me.isUpBoth = wxsProp.isUpBoth
}
/* 导出模块 */
const renderBiz = {
data() {
return {
propObserver: propObserver,
}
}
}
export default renderBiz;

268
components/mescroll-uni/wxs/wxs.wxs

@ -0,0 +1,268 @@
// 使用wxs处理交互动画, 提高性能, 同时避免小程序bounce对下拉刷新的影响
// https://uniapp.dcloud.io/frame?id=wxs
// https://developers.weixin.qq.com/miniprogram/dev/framework/view/interactive-animation.html
// 模拟mescroll实例, 与mescroll.js的写法尽量保持一致
var me = {}
// ------ 自定义下拉刷新动画 start ------
/* 下拉过程中的回调,滑动过程一直在执行 (rate<1为inOffset; rate>1为outOffset) */
me.onMoving = function (ins, rate, downHight){
ins.requestAnimationFrame(function () {
ins.selectComponent('.mescroll-wxs-content').setStyle({
'will-change': 'transform', // 可解决下拉过程中, image和swiper脱离文档流的问题
'transform': 'translateY(' + downHight + 'px)',
'transition': ''
})
// 环形进度条
var progress = ins.selectComponent('.mescroll-wxs-progress')
progress && progress.setStyle({transform: 'rotate(' + 360 * rate + 'deg)'})
})
}
/* 显示下拉刷新进度 */
me.showLoading = function (ins){
me.downHight = me.optDown.offset
ins.requestAnimationFrame(function () {
ins.selectComponent('.mescroll-wxs-content').setStyle({
'will-change': 'auto',
'transform': 'translateY(' + me.downHight + 'px)',
'transition': 'transform 300ms'
})
})
}
/* 结束下拉 */
me.endDownScroll = function (ins){
me.downHight = 0;
me.isDownScrolling = false;
ins.requestAnimationFrame(function () {
ins.selectComponent('.mescroll-wxs-content').setStyle({
'will-change': 'auto',
'transform': 'translateY(0)', // 不可以写空串,否则scroll-view渲染不完整 (延时350ms会调clearTransform置空)
'transition': 'transform 300ms'
})
})
}
/* 结束下拉动画执行完毕后, 清除transform和transition, 避免对列表内容样式造成影响, 如: h5的list-msg示例下拉进度条漏出来等 */
me.clearTransform = function (ins){
ins.requestAnimationFrame(function () {
ins.selectComponent('.mescroll-wxs-content').setStyle({
'will-change': '',
'transform': '',
'transition': ''
})
})
}
// ------ 自定义下拉刷新动画 end ------
/**
* 监听逻辑层数据的变化 (实时更新数据)
*/
function propObserver(wxsProp) {
me.optDown = wxsProp.optDown
me.scrollTop = wxsProp.scrollTop
me.bodyHeight = wxsProp.bodyHeight
me.isDownScrolling = wxsProp.isDownScrolling
me.isUpScrolling = wxsProp.isUpScrolling
me.isUpBoth = wxsProp.isUpBoth
me.isScrollBody = wxsProp.isScrollBody
me.startTop = wxsProp.scrollTop // 及时更新touchstart触发的startTop, 避免scroll-view快速惯性滚动到顶部取值不准确
}
/**
* 监听逻辑层数据的变化 (调用wxs的方法)
*/
function callObserver(callProp, oldValue, ins) {
if (me.disabled()) return;
if(callProp.callType){
// 逻辑层(App Service)的style已失效,需在视图层(Webview)设置style
if(callProp.callType === 'showLoading'){
me.showLoading(ins)
}else if(callProp.callType === 'endDownScroll'){
me.endDownScroll(ins)
}else if(callProp.callType === 'clearTransform'){
me.clearTransform(ins)
}
}
}
/**
* touch事件
*/
function touchstartEvent(e, ins) {
me.downHight = 0; // 下拉的距离
me.startPoint = me.getPoint(e); // 记录起点
me.startTop = me.getScrollTop(); // 记录此时的滚动条位置
me.startAngle = 0; // 初始角度
me.lastPoint = me.startPoint; // 重置上次move的点
me.maxTouchmoveY = me.getBodyHeight() - me.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况)
me.inTouchend = false; // 标记不是touchend
me.callMethod(ins, {type: 'setWxsProp'}) // 同步更新wxsProp的数据 (小程序是异步的,可能touchmove先执行,才到propObserver; h5和app是同步)
}
function touchmoveEvent(e, ins) {
var isPrevent = true // false表示不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault (对小程序生效, h5和app无效)
if (me.disabled()) return isPrevent;
var scrollTop = me.getScrollTop(); // 当前滚动条的距离
var curPoint = me.getPoint(e); // 当前点
var moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
// 向下拉 && 在顶部
// mescroll-body,直接判定在顶部即可
// scroll-view在滚动时不会触发touchmove,当触顶/底/左/右时,才会触发touchmove
// scroll-view滚动到顶部时,scrollTop不一定为0,也有可能大于0; 在iOS的APP中scrollTop可能为负数,不一定和startTop相等
if (moveY > 0 && (
(me.isScrollBody && scrollTop <= 0)
||
(!me.isScrollBody && (scrollTop <= 0 || (scrollTop <= me.optDown.startTop && scrollTop === me.startTop)) )
)) {
// 可下拉的条件
if (!me.inTouchend && !me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling &&
me.isUpBoth))) {
// 下拉的角度是否在配置的范围内
if(!me.startAngle) me.startAngle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
if (me.startAngle < me.optDown.minAngle) return isPrevent; // 如果小于配置的角度,则不往下执行下拉刷新
// 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
me.inTouchend = true; // 标记执行touchend
touchendEvent(e, ins); // 提前触发touchend
return isPrevent;
}
isPrevent = false // 小程序是return false
var diff = curPoint.y - me.lastPoint.y; // 和上次比,移动的距离 (大于0向下,小于0向上)
// 下拉距离 < 指定距离
if (me.downHight < me.optDown.offset) {
if (me.movetype !== 1) {
me.movetype = 1; // 加入标记,保证只执行一次
// me.optDown.inOffset && me.optDown.inOffset(me); // 进入指定距离范围内那一刻的回调,只执行一次
me.callMethod(ins, {type: 'setLoadType', downLoadType: 1})
me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
}
me.downHight += diff * me.optDown.inOffsetRate; // 越往下,高度变化越小
// 指定距离 <= 下拉距离
} else {
if (me.movetype !== 2) {
me.movetype = 2; // 加入标记,保证只执行一次
// me.optDown.outOffset && me.optDown.outOffset(me); // 下拉超过指定距离那一刻的回调,只执行一次
me.callMethod(ins, {type: 'setLoadType', downLoadType: 2})
me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
}
if (diff > 0) { // 向下拉
me.downHight += diff * me.optDown.outOffsetRate; // 越往下,高度变化越小
} else { // 向上收
me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
}
}
me.downHight = Math.round(me.downHight) // 取整
var rate = me.downHight / me.optDown.offset; // 下拉区域当前高度与指定距离的比值
// me.optDown.onMoving && me.optDown.onMoving(me, rate, me.downHight); // 下拉过程中的回调,一直在执行
me.onMoving(ins, rate, me.downHight)
}
}
me.lastPoint = curPoint; // 记录本次移动的点
return isPrevent // false表示不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault (对小程序生效, h5和app无效)
}
function touchendEvent(e, ins) {
// 如果下拉区域高度已改变,则需重置回来
if (me.isMoveDown) {
if (me.downHight >= me.optDown.offset) {
// 符合触发刷新的条件
me.downHight = me.optDown.offset; // 更新下拉区域高度
// me.triggerDownScroll();
me.callMethod(ins, {type: 'triggerDownScroll'})
} else {
// 不符合的话 则重置
me.downHight = 0;
// me.optDown.endDownScroll && me.optDown.endDownScroll(me);
me.callMethod(ins, {type: 'endDownScroll'})
}
me.movetype = 0;
me.isMoveDown = false;
} else if (!me.isScrollBody && me.getScrollTop() === me.startTop) { // scroll-view到顶/左/右/底的滑动事件
var isScrollUp = me.getPoint(e).y - me.startPoint.y < 0; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
// 上滑
if (isScrollUp) {
// 需检查滑动的角度
var angle = me.getAngle(me.getPoint(e), me.startPoint); // 两点之间的角度,区间 [0,90]
if (angle > 80) {
// 检查并触发上拉
// me.triggerUpScroll(true);
me.callMethod(ins, {type: 'triggerUpScroll'})
}
}
}
me.callMethod(ins, {type: 'setWxsProp'}) // 同步更新wxsProp的数据 (小程序是异步的,可能touchmove先执行,才到propObserver; h5和app是同步)
}
/* 是否禁用下拉刷新 */
me.disabled = function(){
return !me.optDown || !me.optDown.use || me.optDown.native
}
/* 根据点击滑动事件获取第一个手指的坐标 */
me.getPoint = function(e) {
if (!e) {
return {x: 0,y: 0}
}
if (e.touches && e.touches[0]) {
return {x: e.touches[0].pageX,y: e.touches[0].pageY}
} else if (e.changedTouches && e.changedTouches[0]) {
return {x: e.changedTouches[0].pageX,y: e.changedTouches[0].pageY}
} else {
return {x: e.clientX,y: e.clientY}
}
}
/* 计算两点之间的角度: 区间 [0,90]*/
me.getAngle = function (p1, p2) {
var x = Math.abs(p1.x - p2.x);
var y = Math.abs(p1.y - p2.y);
var z = Math.sqrt(x * x + y * y);
var angle = 0;
if (z !== 0) {
angle = Math.asin(y / z) / Math.PI * 180;
}
return angle
}
/* 获取滚动条的位置 */
me.getScrollTop = function() {
return me.scrollTop || 0
}
/* 获取body的高度 */
me.getBodyHeight = function() {
return me.bodyHeight || 0;
}
/* 调用逻辑层的方法 */
me.callMethod = function(ins, param) {
if(ins) ins.callMethod('wxsCall', param)
}
/* 导出模块 */
module.exports = {
propObserver: propObserver,
callObserver: callObserver,
touchstartEvent: touchstartEvent,
touchmoveEvent: touchmoveEvent,
touchendEvent: touchendEvent
}

819
components/mx-datepicker/mx-datepicker.vue

@ -0,0 +1,819 @@
<template>
<view v-if="isShow" class="picker">
<!-- 日期选择器 -->
<view v-if="type!='time'" class="picker-modal">
<view class="picker-modal-header">
<view class="picker-icon picker-icon-zuozuo" :hover-stay-time="100" hover-class="picker-icon-active" @click="onSetYear('-1')"></view>
<view class="picker-icon picker-icon-zuo" :hover-stay-time="100" hover-class="picker-icon-active" @click="onSetMonth('-1')"></view>
<text class="picker-modal-header-title">{{title}}</text>
<view class="picker-icon picker-icon-you" :hover-stay-time="100" hover-class="picker-icon-active" @click="onSetMonth('+1')"></view>
<view class="picker-icon picker-icon-youyou" :hover-stay-time="100" hover-class="picker-icon-active" @click="onSetYear('+1')"></view>
</view>
<swiper class="picker-modal-body" :circular="true" :duration="200" :skip-hidden-item-layout="true" :current="calendarIndex" @change="onSwiperChange">
<swiper-item class="picker-calendar" v-for="(calendar,calendarIndex2) in calendars" :key="calendarIndex2">
<view class="picker-calendar-view" v-for="(week,index) in weeks" :key="index - 7">
<view class="picker-calendar-view-item">{{week}}</view>
</view>
<view class="picker-calendar-view" v-for="(date,dateIndex) in calendar" :key="dateIndex" @click="onSelectDate(date)">
<!-- 背景样式 -->
<view v-show="date.bgStyle.type" :class="'picker-calendar-view-'+date.bgStyle.type" :style="{background: date.bgStyle.background}"></view>
<!-- 正常和选中样式 -->
<view class="picker-calendar-view-item" :style="{opacity: date.statusStyle.opacity, color: date.statusStyle.color, background: date.statusStyle.background}">
<text>{{date.title}}</text>
</view>
<!-- 小圆点样式 -->
<view class="picker-calendar-view-dot" :style="{opacity: date.dotStyle.opacity, background: date.dotStyle.background}"></view>
<!-- 信息样式 -->
<view v-show="date.tips" class="picker-calendar-view-tips">{{date.tips}}</view>
</view>
</swiper-item>
</swiper>
<view class="picker-modal-footer">
<view class="picker-modal-footer-info">
<block v-if="isMultiSelect">
<view class="picker-display">
<text>{{beginText}}日期</text>
<text class="picker-display-text">{{BeginTitle}}</text>
<view v-if="isContainTime" class="picker-display-link" :hover-stay-time="100" hover-class="picker-display-link-active"
:style="{color}" @click="onShowTimePicker('begin')">{{BeginTimeTitle}}</view>
</view>
<view class="picker-display">
<text>{{endText}}日期</text>
<text class="picker-display-text">{{EndTitle}}</text>
<view v-if="isContainTime" class="picker-display-link" :hover-stay-time="100" hover-class="picker-display-link-active"
:style="{color}" @click="onShowTimePicker('end')">{{EndTimeTitle}}</view>
</view>
</block>
<block v-else>
<view class="picker-display">
<text>当前选择</text>
<text class="picker-display-text">{{BeginTitle}}</text>
<view v-if="isContainTime" class="picker-display-link" :hover-stay-time="100" hover-class="picker-display-link-active"
:style="{color}" @click="onShowTimePicker('begin')">{{BeginTimeTitle}}</view>
</view>
</block>
</view>
<view class="picker-modal-footer-btn">
<view class="picker-btn" :hover-stay-time="100" hover-class="picker-btn-active" @click="onCancel">取消</view>
<view class="picker-btn" :style="{color}" :hover-stay-time="100" hover-class="picker-btn-active" @click="onConfirm">确定</view>
</view>
</view>
</view>
<!-- 时间选择器 -->
<view v-if="showTimePicker" class="picker">
<view class="picker-modal picker-time">
<view class="picker-modal-header">
<text class="picker-modal-header-title">选择日期</text>
</view>
<picker-view class="picker-modal-time" indicator-class="picker-modal-time-item" :value="timeValue" @change="onTimeChange">
<picker-view-column>
<view v-for="(v,i) in 24" :key="i">{{i<10?'0'+i:i}}</view>
</picker-view-column>
<picker-view-column>
<view v-for="(v,i) in 60" :key="i">{{i<10?'0'+i:i}}</view>
</picker-view-column>
<picker-view-column v-if="showSeconds">
<view v-for="(v,i) in 60" :key="i">{{i<10?'0'+i:i}}</view>
</picker-view-column>
</picker-view>
<view class="picker-modal-footer">
<view class="picker-modal-footer-info">
<view class="picker-display">
<text>当前选择</text>
<text class="picker-display-text">{{PickerTimeTitle}}</text>
</view>
</view>
<view class="picker-modal-footer-btn">
<view class="picker-btn" :hover-stay-time="100" hover-class="picker-btn-active" @click="onCancelTime">取消</view>
<view class="picker-btn" :style="{color}" :hover-stay-time="100" hover-class="picker-btn-active" @click="onConfirmTime">确定</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
/**
* 工具函数库
*/
const DateTools = {
/**
* 获取公历节日
* @param date Date对象
*/
getHoliday(date) {
let holidays = {
'0101': '元旦',
'0214': '情人',
'0308': '妇女',
'0312': '植树',
'0401': '愚人',
'0501': '劳动',
'0504': '青年',
'0601': '儿童',
'0701': '建党',
'0801': '建军',
'0903': '抗日',
'0910': '教师',
'1001': '国庆',
'1031': '万圣',
'1224': '平安',
'1225': '圣诞'
};
let value = this.format(date, 'mmdd');
if (holidays[value]) return holidays[value];
return false;
},
/**
* 解析标准日期格式
* @param s 日期字符串
* @return 返回Date对象
*/
parse: s => new Date(s.replace(/(年|月|-)/g, '-').replace(/(日)/g, '')),
/**
* 比较日期是否为同一天
* @param a Date对象
* @param b Date对象
* @return Boolean
*/
isSameDay: (a, b) => a.getMonth() == b.getMonth() && a.getFullYear() == b.getFullYear() && a.getDate() == b.getDate(),
/**
* 格式化Date对象
* @param d 日期对象
* @param f 格式字符串
* @return 返回格式化后的字符串
*/
format(d, f) {
var o = {
"m+": d.getMonth() + 1,
"d+": d.getDate(),
"h+": d.getHours(),
"i+": d.getMinutes(),
"s+": d.getSeconds(),
"q+": Math.floor((d.getMonth() + 3) / 3),
};
if (/(y+)/.test(f))
f = f.replace(RegExp.$1, (d.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(f))
f = f.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return f;
},
/**
* 用于format格式化后的反解析
* @param s 日期字符串
* @param f 格式字符串
* @return 返回Date对象
*/
inverse(s, f) {
var o = {
"y": '',
"m": '',
"d": '',
"h": '',
"i": '',
"s": '',
};
let d = new Date();
if (s.length != f.length) return d;
for (let i in f)
if (o[f[i]] != undefined) o[f[i]] += s[i];
if (o.y) d.setFullYear(o.y.length < 4 ? (d.getFullYear() + '').substr(0, 4 - o.y.length) + o.y : o.y);
o.m && d.setMonth(o.m - 1, 1);
o.d && d.setDate(o.d - 0);
o.h && d.setHours(o.h - 0);
o.i && d.setMinutes(o.i - 0);
o.s && d.setSeconds(o.s - 0);
return d;
},
/**
* 获取日历数组42
* @param date 日期对象或日期字符串
* @param proc 处理日历(和forEach类似)传递一个数组中的item
* @return Array
*/
getCalendar(date, proc) {
let it = new Date(date),
calendars = [];
it.setDate(1);
it.setDate(it.getDate() - ((it.getDay() == 0 ? 7 : it.getDay()) - 1)); //
for (let i = 0; i < 42; i++) {
let tmp = {
dateObj: new Date(it),
title: it.getDate(),
isOtherMonth: it.getMonth() < date.getMonth() || it.getMonth() > date.getMonth()
};
calendars.push(Object.assign(tmp, proc ? proc(tmp) : {}));
it.setDate(it.getDate() + 1);
}
return calendars;
},
/**
* 获取日期到指定的月份1号(不改变原来的date对象)
* @param d Date对象
* @param v 指定的月份
* @return Date对象
*/
getDateToMonth(d, v) {
let n = new Date(d);
n.setMonth(v, 1);
return n;
},
/**
* 把时间数组转为时间字符串
* @param t Array[,,]
* @param showSecinds 是否显示秒
* @return 字符串 :[:]
*/
formatTimeArray(t, s) {
let r = [...t];
if (!s) r.length = 2;
r.forEach((v, k) => r[k] = ('0' + v).slice(-2));
return r.join(':');
}
};
export default {
props: {
//
color: {
type: String,
default: '#409eff'
},
// typedatetimetime
showSeconds: {
type: Boolean,
default: false
},
//
value: [String, Array],
//date time datetime range rangetime
type: {
type: String,
default: 'range'
},
//
show: {
type: Boolean,
default: false
},
//
format: {
type: String,
default: ''
},
//
showHoliday: {
type: Boolean,
default: true
},
//
showTips: {
type: Boolean,
default: false
},
// type
beginText: {
type: String,
default: '开始'
},
// type
endText: {
type: String,
default: '结束'
}
},
data() {
return {
isShow: false, //
isMultiSelect: false, //
isContainTime: false, //
date: {}, //
weeks: ["一", "二", "三", "四", "五", "六", "日"],
title: '初始化', //
calendars: [[],[],[]], //
calendarIndex: 1, //
checkeds: [], //
showTimePicker: false, //
timeValue: [0, 0, 0], //
timeType: 'begin', //
beginTime: [0, 0, 0], //
endTime: [0, 0, 0], //
};
},
methods: {
//
setValue(value) {
this.date = new Date();
this.checkeds = [];
this.isMultiSelect = this.type.indexOf('range') >= 0;
this.isContainTime = this.type.indexOf('time') >= 0;
//Date
let parseDateStr = (str) => (this.format ? DateTools.inverse(str, this.format) : DateTools.parse(str));
if (value) {
if (this.isMultiSelect) {
Array.isArray(value) && value.forEach((dateStr, index) => {
let date = parseDateStr(dateStr);
let time = [date.getHours(), date.getMinutes(), date.getSeconds()];
if (index == 0) this.beginTime = time;
else this.endTime = time;
this.checkeds.push(date);
});
} else {
if (this.type == 'time') {
let date = parseDateStr('2019/1/1 ' + value);
this.beginTime = [date.getHours(), date.getMinutes(), date.getSeconds()];
this.onShowTimePicker('begin');
} else {
this.checkeds.push(parseDateStr(value));
if (this.isContainTime) this.beginTime = [
this.checkeds[0].getHours(),
this.checkeds[0].getMinutes(),
this.checkeds[0].getSeconds()
];
}
}
if (this.checkeds.length) this.date = new Date(this.checkeds[0]);
} else {
if (this.isContainTime) {
this.beginTime = [this.date.getHours(), this.date.getMinutes(), this.date.getSeconds()];
if (this.isMultiSelect) this.endTime = [...this.beginTime];
}
this.checkeds.push(new Date(this.date));
}
if (this.type != 'time') this.refreshCalendars(true);
else this.onShowTimePicker('begin');
},
//
onSetYear(value) {
this.date.setFullYear(this.date.getFullYear() + parseInt(value));
this.refreshCalendars(true);
},
//
onSetMonth(value) {
this.date.setMonth(this.date.getMonth() + parseInt(value));
this.refreshCalendars(true);
},
//
onTimeChange(e) {
this.timeValue = e.detail.value;
},
//
onShowTimePicker(type) {
this.showTimePicker = true;
this.timeType = type;
this.timeValue = type == 'begin' ? [...this.beginTime] : [...this.endTime];
},
//
procCalendar(item) {
//
item.statusStyle = {
opacity: 1,
color: item.isOtherMonth ? '#ddd' : '#000',
background: 'transparent'
};
item.bgStyle = {
type: '',
background: 'transparent'
};
item.dotStyle = {
opacity: 1,
background: 'transparent'
};
item.tips = "";
//
if (DateTools.isSameDay(new Date(), item.dateObj)) {
item.statusStyle.color = this.color;
if (item.isOtherMonth) item.statusStyle.opacity = 0.3;
}
//
this.checkeds.forEach(date => {
if (DateTools.isSameDay(date, item.dateObj)) {
item.statusStyle.background = this.color;
item.statusStyle.color = '#fff';
item.statusStyle.opacity = 1;
if (this.isMultiSelect && this.showTips) item.tips = this.beginText;
}
});
//
if (item.statusStyle.background != this.color) {
let holiday = this.showHoliday ? DateTools.getHoliday(item.dateObj) : false;
if (holiday || DateTools.isSameDay(new Date(), item.dateObj)) {
item.title = holiday || item.title;
item.dotStyle.background = this.color;
if (item.isOtherMonth) item.dotStyle.opacity = 0.2;
}
} else {
item.title = item.dateObj.getDate();
}
//
if (this.checkeds.length == 2) {
if (DateTools.isSameDay(this.checkeds[0], item.dateObj)) { //
item.bgStyle.type = 'bgbegin';
}
if (DateTools.isSameDay(this.checkeds[1], item.dateObj)) { //
if (this.isMultiSelect && this.showTips) item.tips = item.bgStyle.type ? this.beginText + ' / ' + this.endText : this.endText;
if (!item.bgStyle.type) { //
item.bgStyle.type = 'bgend';
} else {
item.bgStyle.type = '';
}
}
if (!item.bgStyle.type && (+item.dateObj > +this.checkeds[0] && +item.dateObj < +this.checkeds[1])) { //
item.bgStyle.type = 'bg';
item.statusStyle.color = this.color;
}
if (item.bgStyle.type) {
item.bgStyle.background = this.color;
item.dotStyle.opacity = 1;
item.statusStyle.opacity = 1;
}
}
},
//
refreshCalendars(refresh = false) {
let date = new Date(this.date);
let before = DateTools.getDateToMonth(date, date.getMonth() - 1);
let after = DateTools.getDateToMonth(date, date.getMonth() + 1);
if (this.calendarIndex == 0) {
if(refresh) this.calendars.splice(0, 1, DateTools.getCalendar(date, this.procCalendar));
this.calendars.splice(1, 1, DateTools.getCalendar(after, this.procCalendar));
this.calendars.splice(2, 1, DateTools.getCalendar(before, this.procCalendar));
} else if (this.calendarIndex == 1) {
this.calendars.splice(0, 1, DateTools.getCalendar(before, this.procCalendar));
if(refresh) this.calendars.splice(1, 1, DateTools.getCalendar(date, this.procCalendar));
this.calendars.splice(2, 1, DateTools.getCalendar(after, this.procCalendar));
} else if (this.calendarIndex == 2) {
this.calendars.splice(0, 1, DateTools.getCalendar(after, this.procCalendar));
this.calendars.splice(1, 1, DateTools.getCalendar(before, this.procCalendar));
if(refresh) this.calendars.splice(2, 1, DateTools.getCalendar(date, this.procCalendar));
}
this.title = DateTools.format(this.date, 'yyyy年mm月');
},
//
onSwiperChange(e) {
this.calendarIndex = e.detail.current;
let calendar = this.calendars[this.calendarIndex];
this.date = new Date(calendar[22].dateObj); //
this.refreshCalendars();
},
//
onSelectDate(date) {
if (~this.type.indexOf('range') && this.checkeds.length == 2) this.checkeds = [];
else if (!(~this.type.indexOf('range')) && this.checkeds.length) this.checkeds = [];
this.checkeds.push(new Date(date.dateObj));
this.checkeds.sort((a, b) => a - b); //
this.calendars.forEach(calendar => {
calendar.forEach(this.procCalendar); //
});
},
//
onCancelTime() {
this.showTimePicker = false;
this.type == 'time' && this.onCancel();
},
//
onConfirmTime() {
if (this.timeType == 'begin') this.beginTime = this.timeValue;
else this.endTime = this.timeValue;
this.showTimePicker = false;
this.type == 'time' && this.onConfirm();
},
//
onCancel() {
this.$emit('cancel', false);
},
//
onConfirm() {
let result = {
value: null,
date: null
};
//
let defaultFormat = {
'date': 'yyyy-mm-dd',
'time': 'hh:ii' + (this.showSeconds ? ':ss' : ''),
'datetime': ''
};
defaultFormat['datetime'] = defaultFormat.date + ' ' + defaultFormat.time;
let fillTime = (date, timeArr) => {
date.setHours(timeArr[0], timeArr[1]);
if (this.showSeconds) date.setSeconds(timeArr[2]);
};
if (this.type == 'time') {
let date = new Date();
fillTime(date, this.beginTime);
result.value = DateTools.format(date, this.format ? this.format : defaultFormat.time);
result.date = date;
} else {
if (this.isMultiSelect) {
let values = [],
dates = [];
if (this.checkeds.length < 2) return uni.showToast({
icon: 'none',
title: '请选择两个日期'
});
this.checkeds.forEach((date, index) => {
let newDate = new Date(date);
if (this.isContainTime) {
let time = [this.beginTime, this.endTime];
fillTime(newDate, time[index]);
}
values.push(DateTools.format(newDate, this.format ? this.format : defaultFormat[this.isContainTime ?
'datetime' : 'date']));
dates.push(newDate);
});
result.value = values;
result.date = dates;
} else {
let newDate = new Date(this.checkeds[0]);
if (this.isContainTime) {
newDate.setHours(this.beginTime[0], this.beginTime[1]);
if (this.showSeconds) newDate.setSeconds(this.beginTime[2]);
}
result.value = DateTools.format(newDate, this.format ? this.format : defaultFormat[this.isContainTime ?
'datetime' : 'date']);
result.date = newDate;
}
}
this.$emit('confirm', result);
}
},
computed: {
BeginTitle() {
let value = '未选择';
if (this.checkeds.length) value = DateTools.format(this.checkeds[0], 'yy-mm-dd');
return value;
},
EndTitle() {
let value = '未选择';
if (this.checkeds.length == 2) value = DateTools.format(this.checkeds[1], 'yy-mm-dd');
return value;
},
PickerTimeTitle() {
return DateTools.formatTimeArray(this.timeValue, this.showSeconds);
},
BeginTimeTitle() {
return this.BeginTitle != '未选择' ? DateTools.formatTimeArray(this.beginTime, this.showSeconds) : '';
},
EndTimeTitle() {
return this.EndTitle != '未选择' ? DateTools.formatTimeArray(this.endTime, this.showSeconds) : '';
}
},
watch: {
show(newValue, oldValue) {
newValue && this.setValue(this.value);
this.isShow = newValue;
},
value(newValue, oldValue) {
setTimeout(()=>{
this.setValue(newValue);
}, 0);
}
}
}
</script>
<style lang="scss" scoped>
$z-index: 100;
$cell-spacing: 20upx;
$calendar-size: 630upx;
$calendar-item-size: 90upx;
.picker {
position: fixed;
z-index: $z-index;
background: rgba(255, 255, 255, 0);
left: 0;
top: 0;
width: 100%;
height: 100%;
font-size: 28upx;
&-btn {
padding: $cell-spacing*0.5 $cell-spacing;
border-radius: 12upx;
color: #666;
&-active {
background: rgba(0, 0, 0, .1);
}
}
&-display {
color: #666;
&-text {
color: #000;
margin: 0 $cell-spacing*0.5;
}
&-link {
display: inline-block;
&-active {
background: rgba(0, 0, 0, .1);
}
}
}
&-time {
width: $calendar-size - 80upx !important;
left: ((750upx - $calendar-size) / 2 + 40upx) !important;
}
&-modal {
background: #fff;
position: absolute;
top: 50%;
left: (750upx - $calendar-size) / 2;
width: $calendar-size;
transform: translateY(-50%);
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.1);
border-radius: 12upx;
&-header {
text-align: center;
line-height: 80upx;
font-size: 32upx;
&-title {
display: inline-block;
width: 40%;
}
.picker-icon {
display: inline-block;
line-height: 50upx;
width: 50upx;
height: 50upx;
border-radius: 50upx;
text-align: center;
margin: 10upx;
background: #fff;
font-size: 36upx;
&-active {
background: rgba(0, 0, 0, .1);
}
}
}
&-body {
width: $calendar-size !important;
height: $calendar-size !important;
position: relative;
}
&-time {
width: 100%;
height: 180upx;
text-align: center;
line-height: 60upx;
}
&-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: $cell-spacing;
&-info {
flex-grow: 1;
}
&-btn {
flex-shrink: 0;
display: flex;
}
}
}
&-calendar {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
flex-wrap: wrap;
&-view {
position: relative;
width: $calendar-item-size;
height: $calendar-item-size;
text-align: center;
&-bgbegin,
&-bg,
&-bgend,
&-item,
&-dot,
&-tips {
position: absolute;
transition: .2s;
}
&-bgbegin,
&-bg,
&-bgend {
opacity: .15;
height: 80%;
}
&-bg {
left: 0;
top: 10%;
width: 100%;
}
&-bgbegin {
border-radius: $calendar-item-size 0 0 $calendar-item-size;
top: 10%;
left: 10%;
width: 90%;
}
&-bgend {
border-radius: 0 $calendar-item-size $calendar-item-size 0;
top: 10%;
left: 0%;
width: 90%;
}
&-item {
left: 5%;
top: 5%;
width: 90%;
height: 90%;
border-radius: $calendar-item-size;
display: flex;
align-items: center;
justify-content: center;
}
&-dot {
right: 10%;
top: 10%;
width: 12upx;
height: 12upx;
border-radius: 12upx;
}
&-tips {
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: #4E4B46;
color: #fff;
border-radius: 12upx;
padding: 10upx 20upx;
font-size: 24upx;
width: max-content;
margin-bottom: 5px;
pointer-events: none;
&:after {
content: "";
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 0;
border-style: solid;
border-width: 5px 5px 0 5px;
border-color: #4E4B46 transparent transparent transparent;
}
}
}
}
}
@font-face {
font-family: "mxdatepickericon";
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAMYAAsAAAAACBgAAALMAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDIgqDRIJiATYCJAMUCwwABCAFhG0HSRvfBsg+QCa3noNAyAQ9w6GDvbwpNp2vloCyn8bD/x+y+/5qDhtj+T4eRVEcbsCoKMFASzCgLdDkmqYDwgxkWQ6YH5L/YnppOlLEjlnter43YRjU7M6vJ3iGADVAgJn5kqjv/wEii23T86UsAQT+04fV+o97VTMx4PPZt4DlorLXwIQiGMA5uhaVrBWqGHfQXcTEiE+PE+g2SUlxWlLVBHwUYFMgrgwSB3wstTKSGzqF1nOyiGeeOtNjV4An/vvxR58PSc3AzrMViyDvPo/7dVEUzn5GROfIWAcU4rLXfMFdhte56y4We9gGNEVIezkBOOaQXUrbTf/hJVkhGpDdCw7dSOEzByMEn3kIic98hMxnAfeFPKWCbjRcA148/HxhCEkaA94eGWFaGolsblpaWz8/Po2WVuNHh1fmBpZHIpqal9fOjizhTteY+RZ9rv02I/pq0W6QVH3pSncBz3m55r9ZIPycHfmenvxe4uyutIgfT5u4bgkDusl9gcF0rnfnz+b2NpSaQWBFeu8GIL1xQj5AH/6FAsEr/50F28e/gA9ny6KjLrxIp0TE+UucmQOl5AFNLXkzZufWamWHYEI39PEP2If97CMdm51N6DSmIekwAVmneXTBr0PVYx+aTgfQbU3p+R4jKHdRurBq0oEw6AKSfm+QDbpGF/w3VOP+oBnMHbqdx409FjP4RRHHkAj5IWgQiBUjHfMTuQ1Icpg5avI4sQVRu8EHdWptM1aKrIjuscfeL+kZwxBTYoElztOQ2UygjRIjEphaZsyWodHgvm9SC8QC/JygEA6DiCDeEMhAQFhhOpvxa/18A0TiYMahIy0L2hYIZWeYH9JR085Al4qts1re5St2/SR6DINBGEVYQCWOETHDMAHZ+pcZIQJGTV4RtMmg8UbhuWL1+VLLA2RFHYC71kiRo0SNpjwQh8pj2EFU3oTNmS1WqgIA') format('woff2');
}
.picker-icon {
font-family: "mxdatepickericon" !important;
}
.picker-icon-you:before {
content: "\e63e";
}
.picker-icon-zuo:before {
content: "\e640";
}
.picker-icon-zuozuo:before {
content: "\e641";
}
.picker-icon-youyou:before {
content: "\e642";
}
</style>

218
components/mx-datepicker/uni-pagination.vue

@ -0,0 +1,218 @@
<template>
<view class="uni-pagination">
<view class="uni-pagination__btns">
<view
:class="['uni-pagination__btn',{'uni-pagination--disabled':currentIndex === 1}]"
:hover-class="currentIndex === 1 ? '' : 'uni-pagination--hover'"
:hover-start-time="20"
:hover-stay-time="70"
@click="clickLeft">
<template v-if="showIcon===true || showIcon === 'true'">
<uni-icons
color="#000"
size="20"
type="arrowleft"/>
</template>
<template v-else>
{{ prevText }}
</template>
</view>
</view>
<view class="uni-pagination__num">
<text class="uni-pagination__num-current">{{ currentIndex }}</text>/{{ maxPage }}
</view>
<view class="uni-pagination__btns stylebtn">
<view
:class="['uni-pagination__btn',{'uni-pagination--disabled':currentIndex === maxPage}]"
:hover-class="currentIndex === maxPage ? '' : 'uni-pagination--hover'"
:hover-start-time="20"
:hover-stay-time="70"
@click="clickRight">
<template v-if="showIcon===true || showIcon === 'true'">
<uni-icons
color="#000"
size="20"
type="arrowright"/>
</template>
<template v-else>
{{ nextText }}
</template>
</view>
</view>
<!-- <view class="uni-pagination__num">
<input type="text" v-model="currentIndex" class="uniinput"/>
</view> -->
</view>
</template>
<script>
// import uniIcons from 'uni-icons.vue'
export default {
name: 'UniPagination',
components: {
// uniIcons
},
props: {
prevText: {
type: String,
default: '上一页'
},
nextText: {
type: String,
default: '下一页'
},
current: {
type: [Number, String],
default: 1
},
total: { //
type: [Number, String],
default: 0
},
pageSize: { //
type: [Number, String],
default: 10
},
showIcon: { // icon
type: [Boolean, String],
default: false
}
},
data () {
return {
currentIndex: 1
}
},
computed: {
maxPage () {
let maxPage = 1
let total = Number(this.total)
let pageSize = Number(this.pageSize)
if (total && pageSize) {
maxPage = Math.ceil(total / pageSize)
}
return maxPage
}
},
watch: {
current (val) {
this.currentIndex = +val
}
},
created () {
this.currentIndex = +this.current
},
methods: {
clickLeft () {
if (Number(this.currentIndex) === 1) {
return
}
this.currentIndex -= 1
this.change('prev')
},
clickRight () {
if (Number(this.currentIndex) === this.maxPage) {
return
}
this.currentIndex += 1
this.change('next')
},
change (e) {
this.$emit('change', {
type: e,
current: this.currentIndex
})
}
}
}
</script>
<style lang="scss">
// <style>
@mixin pagination-disabled {
opacity: 0.3;
}
@mixin pagination-hover {
color: rgba(0, 0, 0, .6);
background-color: $uni-bg-color-hover;
}
.uni-pagination {
width: 100%;
box-sizing: border-box;
padding: 0 40upx;
position: relative;
overflow: hidden;
// display: flex;
flex-direction: row;
margin: 0upx 0upx 30upx 0upx;
&__btns {
flex: 1;
// display: flex;
width: 150upx;
float: left;
justify-content: space-between;
align-items: center;
flex-direction: row;
color: rgba(51,51,51,1);
}
&__btn {
width: 120upx;
height: 60upx;
padding: 0 16upx;
line-height: 60upx;
font-size: $uni-font-size-base;
box-sizing: border-box;
position: relative;
background-color: #f8f8f8;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
&:after {
content: "";
width: 200%;
height: 200%;
position: absolute;
top: 0;
left: 0;
border: 1px solid $uni-border-color;
transform: scale(.5);
transform-origin: 0 0;
box-sizing: border-box;
border-radius: $uni-border-radius-lg;
}
}
&__num {
font-size: $uni-font-size-base;
color: $uni-text-color;
position: absolute;
left: 50%;
top: 0;
transform: translateX(-50%);
&-current {
color: $uni-color-primary;
}
}
&--disabled {
@include pagination-disabled;
}
&--hover {
@include pagination-hover;
}
}
.uni-pagination__btn {padding: 5px 10px;border: 1px solid rgba(15,130,253,1);color: #333;border-radius: 5px;cursor: pointer;width: 150upx;}
.uni-pagination__btn.uni-pagination--disabled {border: 1px solid rgba(15,130,253,1);color: #fff;background: rgba(15,130,253,1);opacity: 0.8;}
.uni-pagination__num {margin: 5px 10px 0px 10px; color: rgba(51,51,51,1);font-size: 30upx;}
.uni-pagination__num-current {color: rgba(15,130,253,1);}
.uniinput {border: 1px solid #00aaa1;color: #00aaa1;padding: 3px 5px;height: auto;background: #fff;width: 40px;text-align: center;line-height: normal;font-size: 18px;margin: -5px 0px 0px 0px;border-radius: 5px;}
.stylebtn {float: right;}
</style>

37
components/prompt/README.md

@ -0,0 +1,37 @@
# Prompt
## 说明
一个可以输入内容并返回的prompt组件, 支持一个默认slot放入中间.
## 用法
**父组件**
template中
```html
<prompt :visible.sync="promptVisible" :placeholder="输入店号" :defaultValue="123" @confirm="clickPromptConfirm" mainColor="#e74a39">
<!-- 这里放入slot内容-->
</prompt>
```
```js
import Prompt from '@/components/prompt/index.vue'
export default {
data() {
return {
// 控制弹框输入框显示
promptVisible: false,
}
},
methods: {
/**
* 点击弹出输入框确定
*/
clickPromptConfirm(val) {
console.log(val)
},
}
}
```

158
components/prompt/index.vue

@ -0,0 +1,158 @@
<template>
<view class="prompt-box" v-if="visible" @touchmove="true">
<view class="prompt">
<view class="prompt-top">
<text class="prompt-title">{{title}}</text>
<input v-if="!isMutipleLine" class="prompt-input" :style="inputStyle" type="text" :placeholder="placeholder" v-model="value">
<textarea v-else class="prompt-input" :style="inputStyle" type="text" :placeholder="placeholder" v-model="value"></textarea>
</view>
<slot></slot>
<view class="prompt-buttons">
<button class="prompt-cancle" :style="'color:' + mainColor" @click="close">取消</button>
<button class="prompt-confirm" :style="'background:' + mainColor" @click="confirm">确定</button>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
visible: {
type: Boolean,
default: false,
required: true,
},
title: {
type: String,
default: '是否确认此名称',
},
placeholder: {
type: String,
default: '请输入内容',
},
mainColor: {
type: String,
default: '#e74a39',
},
defaultValue: {
type: String,
default: '',
},
inputStyle: {
type: String,
default: '',
},
// textarea
isMutipleLine: {
type: Boolean,
default: false,
}
},
data() {
return {
value: '',
}
},
watch: {
visible(val) {
if(val) {
this.value = this.defaultValue
}
}
},
mounted() {
this.value = this.defaultValue === 'true' ? '' : this.defaultValue
},
methods: {
close() {
this.$emit('update:visible', false)
},
confirm() {
this.$emit('confirm', this.value)
this.value = ''
},
}
}
</script>
<style scoped>
view,
button,
input {
box-sizing: border-box;
}
.prompt-box {
position: fixed;
left: 0;
top: 0;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100vh;
background: rgba(0, 0, 0, .2);
transition: opacity .2s linear;
z-index: 99;
}
.prompt {
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
width: 600upx;
min-height: 300upx;
background: white;
border-radius: 20upx;
overflow: hidden;
}
.prompt-top {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
.prompt-title {
margin: 20upx 0;
color: #333;
}
.prompt-input {
width: 520upx;
min-height: 72upx;
padding: 8upx 16upx;
border: 2upx solid #ddd;
border-radius: 8upx;
font-size: 28upx;
text-align: left;
}
.prompt-buttons {
display: flex;
width: 100%;
box-shadow: 0 0 2upx 2upx #eee;
}
.prompt-buttons button:after {
border-radius: 0;
}
button {
width: 50%;
background: white;
border-radius: 0;
}
.prompt-cancle {
background: white;
}
.prompt-confirm {
color: white;
}
</style>

54
components/tags-list.vue

@ -0,0 +1,54 @@
<template>
<view class="tags-container">
<!-- <view class="tags" v-for="(tag,ind) in tags" v-if="ind<3" :key="tag">{{tag}}</view> -->
<u-tag v-if="index<3" size="mini" v-for="(item,index) in $getTag(list)" class="bq-list" border-color="#ffffff00" :text="item" bg-color="#ffffff00" color="#2BAD56"/>
</view>
</template>
<script>
export default {
components: {},
props: {
list: {
type: String,
default: ""
}
},
data() {
return {
}
},
onLoad() {},
mounted() {
},
methods: {},
}
</script>
<style lang="scss" scoped>
.bq-list{
background: #E9FBF7;
border-radius: 6rpx;
font-size: 22rpx;
font-family: Microsoft YaHei;
font-weight: 400;
color: #2BAD56;
margin-right: 6rpx;
}
.tags-container {
display: flex;
align-items: center;
margin-top: 15rpx;
.tags {
background: #e9fbf7;
border: 1px solid #b2dbbd;
border-radius: 6rpx;
padding: 6rpx 8rpx 7rpx 8rpx;
font-size: 22rpx;
font-family: Microsoft YaHei;
font-weight: 400;
color: #2bad56;
margin-right: 8rpx;
}
}
</style>

1
components/utils/amap-wx.js

File diff suppressed because one or more lines are too long

319
components/utils/css/application.css

@ -0,0 +1,319 @@
/*绉慹浜掕仈鐗规晥鍩烘湰妗嗘灦CSS*/
/* body, ul, dl, dd, dt, ol, li, p, h1, h2, h3, h4, h5, h6, textarea, form, select, fieldset, table, td, div, input {margin:0;padding:0;-webkit-text-size-adjust: none;}
a img {border:0}
body {color: #333;text-align: center;font: 16px "瀹嬩綋";max-width:420px;margin: 0 auto;}
ul, ol, li {list-style-type:none;vertical-align:0}
a {outline-style:none;color:#535353;text-decoration:none}
a:hover { color: #D40000; text-decoration: none}
.button {display: inline-block;zoom: 1; *display: inline;vertical-align: baseline;margin: 0 2px;outline: none;cursor: pointer;text-align: center;text-decoration: none;font: 14px/100% Arial, Helvetica, sans-serif;padding:0.25em 0.6em 0.3em;text-shadow: 0 1px 1px rgba(0,0,0,.3);-webkit-border-radius: .5em; -moz-border-radius: .5em;border-radius: .5em;-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.2);-moz-box-shadow: 0 1px 2px rgba(0,0,0,.2);box-shadow: 0 1px 2px rgba(0,0,0,.2);
} */
.pingfen{
width: 100%;
z-index: 1;
clear: both;
color: #737373;
}
.pingfen .listone {
border-bottom: 0;
}
.pingfen .listone {
border-top: #fff;
}
.pingfen .listone {
clear: both;
/* padding: 10px 15px 10px 15px; */
/* overflow: hidden; */
height: 200px;
position: relative;
/* background-color: #008bd0; */
color: #008bd0;
margin: 0 0 20px 0;
}
.pingfen .listone .imgfirst.pf-img {
/* width: 50%; */
/* height: 120px; */
}
/* .pingfen .listone .imgfirst {
width: 45%;
height: 100px;
min-height: 100px;
padding: 5px;
float: left;
margin-right: 4%;
} */
.pingfen .listone .imgfirst {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
.imgfirst.pf-img .firstimg{
width: 100%;
height: 100%;
}
.imgfirst.pf-img .firstimg img {
width: 100%;
height: 100%;
}
.pingfen .listone .listmiddle.pf-text {position: absolute;z-index: 2;width: 77%;height: 145px;left: 50%;margin-left: -45%;top: 60%;background: rgba(255,255,255,1);box-shadow:0px 2px 20px 0px rgba(153,153,153,0.1);border-radius:16px;border: 1px solid rgba(153, 153, 153, 0.1);padding: 5% 6%;}
.pingfen .listone .listmiddle {
color: rgba(51, 51, 51, 1);
float: left;
/* position: relative; */
overflow: hidden;
width: 56%;
margin: 0px 0px 5px;
height: 100%;
}
.mingchen{
font-size: 18px;
padding: 0 0 10px 0;
text-align: left;
}
.mingchen .mingchen_img{
display:inline-block;
width:20px;
height:20px;
margin-right:15px;
margin-bottom: -3px;
}
.mingchen view{
display:inline-block;
font-size: 18px;
color: rgba(18, 105, 195, 1);
}
.dianping_box{
display: flex;
align-items: center;
}
.pingfen .content{
padding: 80px 0;
text-align: left;
}
.myPoint{
/* padding-left: 10px; */
/* padding-bottom: 5px; */
/* overflow: hidden; */
/* padding-top: 0px; */
/* border-bottom: #ddd 1px solid; */
/* height: 40px; */
/* text-align: center; */
width: 50%;
}
.myPoint span{
float: left;
font-family: Tahoma;
font-weight: bold;
font-size: 24px;
padding: 0 0 0 10px;
}
.myPoint view {
width: 100%;
/* float: left; */
text-align: left;
margin: 5px 0px ;
/* height: 40px; */
font-size: 26px;
height: 30px;
}
.myPoint view:nth-child(3) {
width: 80%!important;
text-align: left;
font-size: 16px;
}
#sum_avg {
width: 140px !important;
}
#sum_avg img{
width: 11%;
}
.doPoint{
padding: 0 10px;
}
.point_one{width: 50%;}
.point_one .point_one_box{
margin-bottom: 15px;
}
.point_one .point_one_box:last-child{
margin-bottom: 0px;
}
.point_one .point_one_box>view{
}
.point_one .point_one_box .point_one_1{width: 50px;}
.point_one .point_one_box .point_one_2{width: 30px;margin: 0 8px 0 0;font-size: 12px;text-align: center;}
.point_one .point_one_box .point_one_2 img{width: 100%;}
.point_one .point_one_box .point_one_3{width: 100%;background: rgba(245, 245, 245, 1);height: 7px;line-height: 7px;margin: auto;border-radius: 10px;overflow: hidden;}
.point_two{
padding:16px 10px;
}
.point_two1 .pf_star{
text-align: center;
}
.point_two1 .pf_star .pf_star_img{
float: none;
height: 35px;
display: inline-block;
}
.point_one>view{
height: 20px;
line-height: 20px;
font-size: 14px;
display: flex;
width: 100%;
}
.doPoint P {
color: #363942;
padding:10px 0 10px 0;
text-align: center;
font-size: 19px;
}
.doPoint2 P {
color: #363942;
padding:10px 0 10px 0;
text-align:left;
font-size: 22px;
}
.pf_star{
width:100%;
/* height: 34px; */
margin-bottom: 15px;
font-size: 14px;
color: rgba(51, 51, 51, 1);
}
.pf_star label{
width: 20%;
float: left;
display: inline-block;
}
.pf_star_ind{
width: 45%;
display: inline-block;
}
.pf_star_img{
float: left;
width: 35px!important;
}
.pf_star_img2{
width: 18px !important;
height: 18px;
display: flex;
justify-content: space-evenly;
margin-right: 10px;
}
.hint{
padding: 0 0 0 10px;
display: none;
}
.hintxin{
padding: 0 0 0 10px;
font-size: 16px !important;
font-family:Microsoft YaHei;
font-weight:400;
color: rgba(255,153,0,1) !important;
}
.hintxin2{
padding: 0 0 0 5%;
width: 30%;
font-size: 14px !important;
display: inline-block;
margin-top: -3px;
}
.point_one .pf_star_img img{
/* padding: 20px 0 0 0; */
width: 12px;
}
.pf_star_img img{
/* padding: 20px 0 0 0; */
width: 24px;
}
.point_two .pf_star_img img{
width: 24px;
}
.point_two .pf_star_img2 img{
width: 18px;
height: 18px;
}
.pj_con,.ni_ming{
padding:20px 20px;
}
.ni_ming_1,.ni_ming_2{
margin-bottom:10px;
color: rgba(153, 153, 153, 1);
}
.ni_ming_1 label{
height: 30px;
margin-left: 10%;
}
.ni_ming_1 input{
width: 20px;
height: 20px;
}
.zui-check {
position: absolute;
z-index: 1;
opacity: 0;
}
.zui-icon-1 {
width: 20px;
height: 20px;
position: absolute;
background: url(http://luci.tour-ma.com/r/cms/www/default/newxt/img/lcimg/icon-dianpinggouxuan1.png) no-repeat;
background-size: 100%;
}
.zui-icon-2 {
width: 20px;
height: 20px;
position: absolute;
background: url(http://luci.tour-ma.com/r/cms/www/default/newxt/img/lcimg/icon-dianpinggouxuan0.png) no-repeat;
background-size: 100%;
}
.ni_ming_2 label{
height: 30px;
display: inline-block;
width: 20%;
}
.ni_ming_2 input{
height: 30px;
margin-left: 5%;
width: 75%;
display: inline-block;
border: none;
}
.pj_con label{
display:block;
color: #363942;
}
.pj_con textarea{
width: 90%;
background: RGBA(247, 244, 248, 1);
border-radius: 10px;
padding: 5%;
color: rgba(153, 153, 153, 1);
border: none;
font-size: 16px;
}
.pj_con textarea:focus{
outline: none;
}
.pj_con textarea::-webkit-input-placeholder {
color: rgba(204, 204, 204, 1);
}
.tijiao-btn {
text-align: center;
padding: 30px 0;
}
.tijiao-btn .btn{
width: 90%;
height: 40px;
line-height: 40px;
text-align: center;
background-color: RGBA(53, 109, 242, 1);
border: 0px;
border-radius: 20px;
color: #fff;
font-size: 18px;
}

185
components/utils/date.js

@ -0,0 +1,185 @@
function formatTime2(date) {
var year = date.getFullYear()
var month = date.getMonth() + 1
var day = date.getDate()
var hour = date.getHours()
var minute = date.getMinutes()
var second = date.getSeconds()
return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
}
function formatTime(time) {
if (typeof time !== 'number' || time < 0) {
return time
}
var hour = parseInt(time / 3600)
time = time % 3600
var minute = parseInt(time / 60)
time = time % 60
var second = time
return ([hour, minute, second]).map(function(n) {
n = n.toString()
return n[1] ? n : '0' + n
}).join(':')
}
function formatLocation(longitude, latitude) {
if (typeof longitude === 'string' && typeof latitude === 'string') {
longitude = parseFloat(longitude)
latitude = parseFloat(latitude)
}
longitude = longitude.toFixed(2)
latitude = latitude.toFixed(2)
return {
longitude: longitude.toString().split('.'),
latitude: latitude.toString().split('.')
}
}
function formatDate(date) {
var year = date.getFullYear()
var month = date.getMonth() + 1
var day = date.getDate()
if (date.getHours() >= 24) {
day = day + 1;
}
return [year, month, day].map(formatNumber).join('-')
}
function formatNumber(n) {
n = n.toString()
return n[1] ? n : '0' + n
}
function formatDates(date) {
var year = date.getFullYear()
var month = date.getMonth() + 1
var day = date.getDate() + 1
if (date.getHours() >= 24) {
day = day + 1;
}
return [year, month, day].map(formatNumber).join('-')
}
function formatDatess(date) {
var year = date.getFullYear()
var month = date.getMonth() + 1
var day = date.getDate() + 19
if (date.getHours() >= 24) {
day = day + 1;
}
return [year, month, day].map(formatNumber).join('-')
}
function formatDatessz(date) {
var year = date.getFullYear()
var month = date.getMonth() + 1
var day = date.getDate() + 18
if (date.getHours() >= 24) {
day = day + 1;
}
return [year, month, day].map(formatNumber).join('-')
}
function stringToTime(string) {
var f = string.split(' ', 2);
var d = (f[0] ? f[0] : '').split('-', 3);
var t = (f[1] ? f[1] : '').split(':', 3);
return (new Date(
parseInt(d[0], 10) || null,
(parseInt(d[1], 10) || 1) - 1,
parseInt(d[2], 10) || null,
parseInt(t[0], 10) || null,
parseInt(t[1], 10) || null,
parseInt(t[2], 10) || null)).getTime();
}
function dateAddDays(dataStr, dayCount) {
var strdate = dataStr; //日期字符串
var isdate = new Date(strdate.replace(/-/g, "/")); //把日期字符串转换成日期格式
isdate = new Date((isdate / 1000 + (86400 * dayCount)) * 1000); //日期加1天
var pdate = isdate.getFullYear() + "-" + (isdate.getMonth() + 1) + "-" + (isdate.getDate()); //把日期格式转换成字符串
return pdate;
}
function dateAddDayz(dataStr, dayCount) {
var strdate = dataStr; //日期字符串
var isdate = new Date(strdate.replace(/-/g, "/")); //把日期字符串转换成日期格式
isdate = new Date((isdate / 1000 - (86400 * dayCount)) * 1000); //日期加1天
var pdate = isdate.getFullYear() + "-" + (isdate.getMonth() + 1) + "-" + (isdate.getDate()); //把日期格式转换成字符串
return pdate;
}
//获取当前时间‘年-月-日 时:分’
function getNowTime() {
var now = new Date();
var year = now.getFullYear();
var month = now.getMonth() + 1;
var day = now.getDate();
if (month < 10) {
month = '0' + month;
};
if (day < 10) {
day = '0' + day;
};
//如果需要时分秒,就放开
var h = now.getHours();
var m = now.getMinutes();
var s = now.getSeconds();
var formatDate = h+':'+m;
return formatDate;
}
function haveSomeMinutesTime(n) {
if (n == null) {
n = 0;
}
// 时间
var newDate = new Date()
// var timeStamp = newDate.getTime(); //获取时间戳
var date = newDate.setMinutes(newDate.getMinutes() + n);
newDate = new Date(date);
var year = newDate.getFullYear();
var month = newDate.getMonth() + 1;
var day = newDate.getDate();
var h = newDate.getHours();
var m = newDate.getMinutes();
var s = newDate.getSeconds();
if (month < 10) {
month = '0' + month;
};
if (day < 10) {
day = '0' + day;
};
if (h < 10) {
h = '0' + h;
};
if (m < 10) {
m = '0' + m;
};
if (s < 10) {
s = '0' + s;
};
var time = h + ':' + m;
return time;
}
module.exports = {
haveSomeMinutesTime: haveSomeMinutesTime,
getNowTime: getNowTime,
formatTime: formatTime,
formatDate: formatDate,
formatDates: formatDates,
stringToTime: stringToTime,
formatDatess: formatDatess,
dateAddDays: dateAddDays,
dateAddDayz: dateAddDayz,
formatDatessz: formatDatessz
}

1123
components/utils/erweima.js

File diff suppressed because it is too large

180
components/utils/gwpingjia.vue

@ -0,0 +1,180 @@
<template>
<view class="yhpj">
<view class="yhpj-all">
<text class="yhpj-all-fs">游友点评</text>
<view class="">
<text class="yhpj-all-sl">{{totalNumber}}条评论</text>
<image class="yhpj-all-img" src="http://qiandaohu.tour-ma.com/r/cms/www/hebi/img/icon_jt.png"></image>
</view>
</view>
<view class="yhpl" v-for="(el,ind) in pjinfo">
<view class="yhxx">
<image class="yhxx-img" :src="el.img"></image>
<view class="yhxx-name">
<text class="yhxx-name-txt">{{el.name}}</text>
<text class="yhxx-name-pftxt">{{el.avg}}</text>
</view>
<text class="yhxx-time">{{el.createTime}}</text>
</view>
<view class="yhplxx">
{{el.des}}
</view>
</view>
<view class="ckpj" v-if="totalNumber>3" @click="gotopj">
<text class="ckpj-txt">查看全部评价</text>
</view>
</view>
</template>
<script>
var web = require('../../components/utils/request.js');
export default {
props: ['info'],
data() {
return {
pjinfo:[],
totalNumber:0
}
},
watch:{
info(){
this.getPinlun();
}
},
created() {
},
methods: {
getPinlun(){
var url='/order/appraiseList.jspx';
var para={
waresId:this.info.waresId,
pageNo:1,
pageSize:3,
orderType:this.info.orderType
}
var that=this;
web.httpPost(that,url,para,function(res){
var tmp=[];
if(res.data.status==200){
tmp=res.data.data;
}
that.totalNumber=res.data.totalNumber;
that.pjinfo=tmp;
})
},
gotopj(){
uni.navigateTo({
url:'/pages/goupiao/pjlist?waresId='+this.info.waresId+'&orderType='+this.info.orderType
})
}
}
}
</script>
<style>
.yhpj-all-img {
width: 9upx;
height: 15upx;
margin-left: 10upx;
margin-top: 5upx;
}
.ckpj-txt {
font-size: 24upx;
color: rgba(93, 163, 67, 1);
}
.ckpj {
margin-top: 48upx;
width: 196upx;
height: 51upx;
margin-left: 221upx;
border: 1upx solid rgba(93, 163, 67, 1);
border-radius: 6upx;
line-height: 40upx;
text-align: center;
}
.yhplxx {
margin-top: 25upx;
line-height: 1.6;
font-size: 24upx;
color: rgba(153, 153, 153, 1);
text-indent: 48upx;
}
.yhxx-time {
position: absolute;
top: 5upx;
right: 0;
font-size: 24upx;
color: rgba(153, 153, 153, 1);
}
.yhxx-name-pftxt {
display: block;
font-size: 28upx;
color: rgba(93, 163, 67, 1);
margin-top: 6upx;
}
.yhxx-name-txt {
font-size: 30upx;
color: rgba(153, 153, 153, 1);
}
.yhxx-name {
margin-top: -2upx;
margin-left: 25upx;
}
.yhxx-img {
width: 83upx;
height: 83upx;
margin-top: -8upx;
border-radius: 50%;
}
.yhxx {
display: flex;
flex-direction: row;
position: relative;
}
.yhpl {
margin-top: 42upx;
}
.yhpj-all-sl {
font-size: 28upx;
color: rgba(153, 153, 153, 1);
}
.yhpj-all-pj {
font-size: 32upx;
color: #5DA343;
margin-right: 350upx;
}
.yhpj-all-fs {
color: #333333;
font-size: 32upx;
}
.yhpj-all {
font-size: 28upx;
font-weight: 600;
color: rgba(51, 51, 51, 1);
display: flex;
justify-content: space-between;
}
.yhpj {
width: 635upx;
margin-top: 28upx;
margin-left: 56upx;
}
</style>

419
components/utils/request.js

@ -0,0 +1,419 @@
//!!!!!!!!!此处不该这样写,只是临时方案。参照纯函数编程以及ES6的Promise
//封装的网络请求方法
var wxurl = 'https://cs.tour-ma.com/';
// var wxurl = '';
// var wxurl = 'https://cs.tour-ma.com/';
// var wxurl = 'http://120.92.133.72/'
var wxtitle = '';
function loginUser(para, callbs, callbf) {
uni.login({ //1.请求微信登录
provider: 'weixin',
success: function(res) {
//通过微信code,从服务器请求session
var url = 'minapps/login.jspx';
var code = res.code;
uni.request({
url: wxurl + url, //拿code请求服务器
data: {
code: code,
},
success: function(res) {
if (res.data.status == 200) {
typeof callbs == "function" && callbs(res); //获取key成功
//getUser(); //回调微信用户
} else if (res.data.status == 2002) {
//无用户状态,跳转绑定页面
} else {
var error = {
msg: '与服务器换取session失败,请稍后重试!jy',
errormsg: res
}
typeof callbf == "function" && callbf(error); //获取key失败
}
},
fail: function(e) {
var error = {
msg: '与服务器换取session失败。请稍后重试!jy',
errormsg: e
}
typeof callbf == "function" && callbf(error); //获取key失败
}
})
//通过微信code,从服务器请求session。 end//
}
})
}
function httpPostai(that, murl, mpara, callbs, callbf) {
// #ifdef MP-WEIXIN
mpara.wx = 'wx';
// #endif
uni.request({
url: murl, //用户信息交换接口
method: 'POST',
data: JSON.stringify(mpara),
header: {
"Content-Type": "text/html;charset=UTF-8"
},
success: function(res) {
if (res.data.status == 200) {
typeof callbs == "function" && callbs(res);
}
},
fail: function(e) {
var error = {
msg: '获取数据失败,请稍后重试!jy',
errormsg: e
}
typeof callbf == "function" && callbf(error);
}
});
}
//post请求,参数分别为:当前页面、url、参数对象、成功回调、失败回调
function httpPost(that, murl, mpara, callbs, callbf) {
var pages = getCurrentPages();
if(pages.length>0){
var page = pages[pages.length - 1].route;
}
//此时key应该有值,无值说明用户未给权限
var key = '';
try {
key = uni.getStorageSync('session');
} catch (e) {
}
console.log('获取key'+key)
mpara.wx = 'wx';
mpara.lng=that.$store.getters.lon==''?0:that.$store.getters.lon;
mpara.lat=that.$store.getters.lat==''?0:that.$store.getters.lat;
uni.request({
url: wxurl + murl, //用户信息交换接口
method: 'POST',
data: JSON.stringify(mpara),
header: {
"Content-Type": "text/html;charset=UTF-8",
'Cookie': 'WXSESSIONID=' + key
},
success: function(res) {
if (res.data.status == 200) {
typeof callbs == "function" && callbs(res);
} else if (res.data.status == 2001) {
//未登录状态
uni.removeStorageSync('session')
try {
if (page == 'pages/my/my') {
typeof callbs == "function" && callbs(res);
return
}
uni.showModal({
title: '',
content: '未登录,是否登录',
showCancel: true,
cancelText: '否',
confirmText: '是',
success: res => {
if (res.confirm) {
uni.navigateTo({
url: '/pages/login/index'
});
} else {
uni.navigateBack({
delta: 1
});
}
}
});
} catch (e) {
var error = {
msg: '删除KEY失败,请稍后重试!wx',
errormsg: e
}
typeof callbf == "function" && callbf(error);
}
} else if (res.data.status == 2002) {
//无用户状态,跳转绑定页面
//uni.redirectTo({ url: "/pages/user/mobile/mobile" });
} else {
var error = {
msg: 'jy出错:' + res.data.message,
errormsg: res
}
typeof callbs == "function" && callbs(res);
}
},
fail: function(e) {
var error = {
msg: '获取数据失败,请稍后重试!jy',
errormsg: e
}
typeof callbf == "function" && callbf(error);
}
});
}
//post请求,参数分别为:当前页面、url、参数对象、成功回调、失败回调
function httpPostxin(that, murl, mpara, callbs, callbf) {
var pages = getCurrentPages();
if(pages.length>0){
var page = pages[pages.length - 1].route;
}
//此时key应该有值,无值说明用户未给权限
var key = '';
try {
key = uni.getStorageSync('session');
} catch (e) {
}
console.log('获取key'+key)
// mpara.wx = 'wx';
// mpara.lng=that.$store.getters.lon==''?0:that.$store.getters.lon;
// mpara.lat=that.$store.getters.lat==''?0:that.$store.getters.lat;
uni.request({
url: murl, //用户信息交换接口
method: 'POST',
data: JSON.stringify(mpara),
header: {
"Content-Type": "application/json;charset=UTF-8"
// 'Cookie': 'WXSESSIONID=' + key
},
success: function(res) {
if (res.data.status == 200) {
typeof callbs == "function" && callbs(res);
} else if (res.data.status == 2001) {
//未登录状态
uni.removeStorageSync('session')
try {
if (page == 'pages/my/my') {
typeof callbs == "function" && callbs(res);
return
}
uni.showModal({
title: '',
content: '未登录,是否登录',
showCancel: true,
cancelText: '否',
confirmText: '是',
success: res => {
if (res.confirm) {
uni.navigateTo({
url: '/pages/login/index'
});
} else {
uni.navigateBack({
delta: 1
});
}
}
});
} catch (e) {
var error = {
msg: '删除KEY失败,请稍后重试!wx',
errormsg: e
}
typeof callbf == "function" && callbf(error);
}
} else if (res.data.status == 2002) {
//无用户状态,跳转绑定页面
//uni.redirectTo({ url: "/pages/user/mobile/mobile" });
} else {
var error = {
msg: 'jy出错:' + res.data.message,
errormsg: res
}
typeof callbs == "function" && callbs(res);
}
},
fail: function(e) {
var error = {
msg: '获取数据失败,请稍后重试!jy',
errormsg: e
}
typeof callbf == "function" && callbf(error);
}
});
}
//post请求,参数分别为:当前页面、url、参数对象、成功回调、失败回调
function httpGet(that, murl, mpara, callbs, callbf) {
var key = '';
try {
key = uni.getStorageSync('session');
} catch (e) {
console.log(e);
}
// #ifndef APP-PLUS
mpara.wx = 'wx';
// #endif
uni.request({
url: wxurl + murl, //用户信息交换接口
method: 'POST',
data: JSON.stringify(mpara),
header: {
"Content-Type": "text/html;charset=UTF-8",
'Cookie': 'WXSESSIONID=' + key
},
success: function(res) {
if (res.data.status == 200) {
typeof callbs == "function" && callbs(res);
} else {
var error = {
msg: 'jy出错:' + res.data.message,
errormsg: res
}
typeof callbs == "function" && callbs(res);
}
},
fail: function(e) {
var error = {
msg: '获取数据失败,请稍后重试!jy',
errormsg: e
}
typeof callbf == "function" && callbf(error);
}
});
}
//post请求,参数分别为:当前页面、url、参数对象、成功回调、失败回调
function httpGetxin(that, murl, mpara, callbs, callbf) {
var key = '';
try {
key = uni.getStorageSync('session');
} catch (e) {
console.log(e);
}
// #ifndef APP-PLUS
mpara.wx = 'wx';
// #endif
uni.request({
url: murl, //用户信息交换接口
method: 'GET',
data: JSON.stringify(mpara),
header: {
"Content-Type": "text/html;charset=UTF-8",
'Cookie': 'WXSESSIONID=' + key
},
success: function(res) {
if (res.data.status == 200) {
typeof callbs == "function" && callbs(res);
} else {
var error = {
msg: 'jy出错:' + res.data.message,
errormsg: res
}
typeof callbs == "function" && callbs(res);
}
},
fail: function(e) {
var error = {
msg: '获取数据失败,请稍后重试!jy',
errormsg: e
}
typeof callbf == "function" && callbf(error);
}
});
}
function httpGetg(that, murl, mpara, callbs, callbf) {
var url = wxurl + murl;
var len = 0;
for (var key in mpara) {
if (len == 0) {
url += '?' + key + '=' + mpara[key];
} else {
url += '&' + key + '=' + mpara[key];
}
len++
}
uni.request({
url: url, //用户信息交换接口
method: 'GET',
header: {
"Content-Type": "text/html;charset=UTF-8"
},
success: function(res) {
if (res.data.status == 200) {
typeof callbs == "function" && callbs(res);
} else {
var error = {
msg: 'jy出错:' + res.data.message,
errormsg: res
}
typeof callbs == "function" && callbs(res);
}
},
fail: function(e) {
var error = {
msg: '获取数据失败,请稍后重试!jy',
errormsg: e
}
typeof callbf == "function" && callbf(error);
}
});
}
function httpGejqr(that, murl, mpara, callbs, callbf) {
var url = wxurl + murl;
uni.request({
url: url, //用户信息交换接口
method: 'GET',
header: {
"Content-Type": "text/html;charset=UTF-8"
},
success: function(res) {
if (res.data.status == 200) {
typeof callbs == "function" && callbs(res);
} else {
var error = {
msg: 'jy出错:' + res.data.message,
errormsg: res
}
typeof callbs == "function" && callbs(res);
}
},
fail: function(e) {
var error = {
msg: '获取数据失败,请稍后重试!jy',
errormsg: e
}
typeof callbf == "function" && callbf(error);
}
});
}
function getWeek(date) {
var week;
if (date.getDay() == 0) week = "周日"
if (date.getDay() == 1) week = "周一"
if (date.getDay() == 2) week = "周二"
if (date.getDay() == 3) week = "周三"
if (date.getDay() == 4) week = "周四"
if (date.getDay() == 5) week = "周五"
if (date.getDay() == 6) week = "周六"
return week;
}
// 计算两地距离
function distance(la1, lo1, la2, lo2) {
var La1 = la1 * Math.PI / 180.0;
var La2 = la2 * Math.PI / 180.0;
var La3 = La1 - La2;
var Lb3 = lo1 * Math.PI / 180.0 - lo2 * Math.PI / 180.0;
var s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(La3 / 2), 2) + Math.cos(La1) * Math.cos(La2) * Math.pow(Math.sin(
Lb3 / 2), 2)));
s = s * 6378.137;
s = Math.round(s * 10000) / 10000;
s = s.toFixed(2);
return s;
}
module.exports.httpGetg = httpGetg;
module.exports.loginUser = loginUser;
module.exports.httpPost = httpPost;
module.exports.httpGet = httpGet;
module.exports.httpPostai = httpPostai;
module.exports.httpGejqr = httpGejqr;
module.exports.getWeek = getWeek;
module.exports.distance=distance;
module.exports.httpGetxin = httpGetxin;
module.exports.httpPostxin = httpPostxin;

5662
components/utils/u-charts.js

File diff suppressed because it is too large

315
components/utils/util.js

@ -0,0 +1,315 @@
function formatTime2(date) {
var year = date.getFullYear()
var month = date.getMonth() + 1
var day = date.getDate()
var hour = date.getHours()
var minute = date.getMinutes()
var second = date.getSeconds()
return [year, month, day].map(formatNumber).join('-')
}
function dateAddDays(date) {
date = date.replace(/\-/g, '\/'); //日期参数 格式new Date("2017/09/12")所有浏览器都兼容,new Date("2017-09-12") 部分IE不兼容
var dd = new Date(date);
dd.setDate(dd.getDate() + 1);
var year = dd.getFullYear();
var m = dd.getMonth() + 1; //获取当前月份的日期
var d = dd.getDate();
var mon = (m < 10) ? ('0' + m) : ('' + m);
var day = (d < 10) ? ('0' + d) : ('' + d);
return year + "-" + mon + "-" + day;
}
function dateAddDayz(date) {
date = date.replace(/\-/g, '\/'); //日期参数 格式new Date("2017/09/12")所有浏览器都兼容,new Date("2017-09-12") 部分IE不兼容
var dd = new Date(date);
dd.setDate(dd.getDate() - 1);
var year = dd.getFullYear();
var m = dd.getMonth() + 1; //获取当前月份的日期
var d = dd.getDate();
var mon = (m < 10) ? ('0' + m) : ('' + m);
var day = (d < 10) ? ('0' + d) : ('' + d);
return year + "-" + mon + "-" + day;
return pdate;
}
function dateDiffIncludeToday(startDateString, endDateString) {
var oDate1 = new Date(startDateString);
var oDate2 = new Date(endDateString);
if (oDate1.getTime() > oDate2.getTime()) {
return 1;
} else {
return 2;
}
}
function jianDate(date, days) {
var d = new Date(date);
d.setDate(d.getDate() - days);
var m = d.getMonth() + 1;
return d.getFullYear() + '-' + m + '-' + d.getDate();
}
function formatTime(time) {
if (typeof time !== 'number' || time < 0) {
return time
}
var hour = parseInt(time / 3600)
time = time % 3600
var minute = parseInt(time / 60)
time = time % 60
var second = time
return ([hour, minute, second]).map(function(n) {
n = n.toString()
return n[1] ? n : '0' + n
}).join(':')
}
function formatLocation(longitude, latitude) {
if (typeof longitude === 'string' && typeof latitude === 'string') {
longitude = parseFloat(longitude)
latitude = parseFloat(latitude)
}
longitude = longitude.toFixed(2)
latitude = latitude.toFixed(2)
return {
longitude: longitude.toString().split('.'),
latitude: latitude.toString().split('.')
}
}
function formatDate(date) {
var year = date.getFullYear()
var month = date.getMonth() + 1
var day = date.getDate()
// if (date.getHours() >= 24) {
// day = date.getDate()+24
// }
return [year, month, day].map(formatNumber).join('-')
}
function adddate(date, num) {
var dd = new Date(date);
dd.setDate(dd.getDate() + num); //获取AddDayCount天后的日期
var y = dd.getFullYear();
var m = dd.getMonth() + 1; //获取当前月份的日期
var d = dd.getDate();
return y + '-' + (m < 10 ? '0' + m : m) + '-' + d
}
function formatNumber(n) {
n = n.toString()
return n[1] ? n : '0' + n
}
//判断两个日期间隔
function comparedate(sDate, eDate){
var sdate = new Date(sDate);
var edate = new Date(eDate);
var comp = Math.abs(sdate.getTime() - edate.getTime());
return Math.floor(comp/86400000);
}
// 农历日期
var lunar = {
tg: '甲乙丙丁戊己庚辛壬癸',
dz: '子丑寅卯辰巳午未申酉戌亥',
number: '一二三四五六七八九十',
year: '鼠牛虎兔龙蛇马羊猴鸡狗猪',
month: '正二三四五六七八九十冬腊',
monthadd: [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],
calendar: [
0xa4b,
0x5164b,
0x6a5,
0x6d4,
0x415b5,
0x2b6,
0x957,
0x2092f,
0x497,
0x60c96,
0xd4a,
0xea5,
0x50da9,
0x5ad,
0x2b6,
0x3126e,
0x92e,
0x7192d,
0xc95,
0xd4a,
0x61b4a,
0xb55,
0x56a,
0x4155b,
0x25d,
0x92d,
0x2192b,
0xa95,
0x71695,
0x6ca,
0xb55,
0x50ab5,
0x4da,
0xa5b,
0x30a57,
0x52b,
0x8152a,
0xe95,
0x6aa,
0x615aa,
0xab5,
0x4b6,
0x414ae,
0xa57,
0x526,
0x31d26,
0xd95,
0x70b55,
0x56a,
0x96d,
0x5095d,
0x4ad,
0xa4d,
0x41a4d,
0xd25,
0x81aa5,
0xb54,
0xb6a,
0x612da,
0x95b,
0x49b,
0x41497,
0xa4b,
0xa164b,
0x6a5,
0x6d4,
0x615b4,
0xab6,
0x957,
0x5092f,
0x497,
0x64b,
0x30d4a,
0xea5,
0x80d65,
0x5ac,
0xab6,
0x5126d,
0x92e,
0xc96,
0x41a95,
0xd4a,
0xda5,
0x20b55,
0x56a,
0x7155b,
0x25d,
0x92d,
0x5192b,
0xa95,
0xb4a,
0x416aa,
0xad5,
0x90ab5,
0x4ba,
0xa5b,
0x60a57,
0x52b,
0xa93,
0x40e95
]
};
// 农历日期
function getLunarDate(date) {
var year, month, day;
if (!date) {
(date = new Date()), (year = date.getFullYear()), (month = date.getMonth()), (day = date.getDate());
} else {
(date = date.split('-')), (year = parseInt(date[0])), (month = date[1] - 1), (day = parseInt(date[2]));
}
if (year < 1921 ) {
return {};
}
var total, m, n, k, bit, lunarYear, lunarMonth, lunarDay;
var isEnd = false;
var tmp = year;
if (tmp < 1900) {
tmp += 1900;
}
total = (tmp - 1921) * 365 + Math.floor((tmp - 1921) / 4) + lunar.monthadd[month] + day - 38;
if (year % 4 == 0 && month > 1) {
total++;
}
for (m = 0; ; m++) {
k = lunar.calendar[m] < 0xfff ? 11 : 12;
for (n = k; n >= 0; n--) {
bit = (lunar.calendar[m] >> n) & 1;
if (total <= 29 + bit) {
isEnd = true;
break;
}
total = total - 29 - bit;
}
if (isEnd) break;
}
lunarYear = 1921 + m;
lunarMonth = k - n + 1;
lunarDay = total;
if (k == 12) {
if (lunarMonth == Math.floor(lunar.calendar[m] / 0x10000) + 1) {
lunarMonth = 1 - lunarMonth;
}
if (lunarMonth > Math.floor(lunar.calendar[m] / 0x10000) + 1) {
lunarMonth--;
}
}
return {
lunarYear: lunarYear,
lunarMonth: lunarMonth,
lunarDay: lunarDay
};
}
// 农历日期
function getLunarDateString(lunarDate) {
if (!lunarDate.lunarDay) return;
var data = {},
lunarYear = lunarDate.lunarYear,
lunarMonth = lunarDate.lunarMonth,
lunarDay = lunarDate.lunarDay;
data.tg = lunar.tg.charAt((lunarYear - 4) % 10);
data.dz = lunar.dz.charAt((lunarYear - 4) % 12);
data.year = lunar.year.charAt((lunarYear - 4) % 12);
data.month = lunarMonth < 1 ? '(闰)' + lunar.month.charAt(-lunarMonth - 1) : lunar.month.charAt(lunarMonth - 1);
data.day = lunarDay < 11 ? '初' : lunarDay < 20 ? '十' : lunarDay < 30 ? '廿' : '三十';
if (lunarDay % 10 != 0 || lunarDay == 10) {
data.day += lunar.number.charAt((lunarDay - 1) % 10);
}
return data;
}
module.exports = {
comparedate:comparedate,
formatTime: formatTime,
formatDate: formatDate,
formatTime2: formatTime2,
dateAddDays: dateAddDays,
dateAddDayz: dateAddDayz,
dateDiffIncludeToday: dateDiffIncludeToday,
adddate: adddate,
getLunarDate:getLunarDate,
getLunarDateString:getLunarDateString
}

363
components/utils/zixunpj.vue

@ -0,0 +1,363 @@
<template>
<view class="">
<view :class="[info.orderType==1?'yhpj':'jdpj']" >
<view class="yhpj-all" :style="{width:info.orderType==1?'700rpx':'650rpx'}">
<view class="yhpj-all-fs">游客评论
</view>
</view>
<view class="konglist" v-if="pjinfo.length==0">空空如也这暂时什么都没有哦</view>
<view class="yhpl" :style="{width:info.orderType==1?'700rpx':'650rpx'}" v-if="pjinfo.length>0" v-for="(el,ind) in pjinfo">
<view class="yhxx">
<image class="yhxx-img" v-if="el.userImg!=null" :src="el.userImg"></image>
<!-- <image class="yhxx-img" v-if="el.userImg==null" src="https://xcx.wfwhy.gov.cn/r/cms/www/xcx/img/wfqy-icon-mddl.png" mode=""></image> -->
<view class="yhxx-name">
{{el.userName}}
<view class="scitemstarts">
<image class="star-image" :src="el.score>0? selectedSrc:normalSrc"></image>
<image class="star-image" :src="el.score>1? selectedSrc:normalSrc"></image>
<image class="star-image" :src="el.score>2? selectedSrc:normalSrc"></image>
<image class="star-image" :src="el.score>3? selectedSrc:normalSrc"></image>
<image class="star-image" :src="el.score>4? selectedSrc:normalSrc"></image>
</view>
</view>
</view>
<view class="yhplxx">
{{el.content}}
</view>
<view class="plsj">
{{el.createTime}}
</view>
<view class="tag">
<view class="left">
<view class="tag-list" v-for="(item,index) in el.tags">
{{item}}
</view>
</view>
</view>
</view>
<view class="ckpj" @click="gotopj">
<text class="ckpj-txt">去评论</text>
</view>
</view>
</view>
</template>
<script>
var web = require('@/components/utils/request.js');
export default {
props: ['info'],
data() {
return {
pjinfo: [],
totalNumber: 0,
normalSrc: '../../static/star-null-big_hong.png',
selectedSrc: '../../static/star-on-big_hong.png',
alldata: [],
}
},
watch: {
info() {
this.getPinlun();
}
},
created() {
},
methods: {
getPinlun() {
var url = 'comment/appraise_list.jspx';
var para = {
"pageNo":1,
"pageSize":1000,
"contentId":this.info.id,
"flag":0
}
var that = this;
web.httpPost(that, url, para, function(res) {
var tmp = [];
if (res.data.status == 200) {
var json=res.data.items;
for(let i=0;i<json.length;i++){
if(json[i].commentTag!=null){
json[i].tags=json[i].commentTag.split(',');
}
}
tmp = json;
}
that.totalNumber = res.data.totalNumber;
that.pjinfo = tmp;
that.alldata = res.data;
})
},
gotopj() {
uni.navigateTo({
url: '/other/pingjia/pingjiazx?id=' + this.info.id
})
}
}
}
</script>
<style lang="scss">
.tag{
margin-top: 15rpx;
display: flex;
flex-direction: row;
flex-wrap: wrap;
.left{
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
width: 85%;
}
&-list{
padding: 5rpx 10rpx;
margin-bottom: 10rpx;
height: 35rpx;
background: #E9F2FF;
font-size: 22rpx;
font-family: Microsoft YaHei;
font-weight: 400;
color: #FFFFFF;
background: #227BFB;
text-align: center;
border-radius: 4rpx;
margin-right: 10rpx;
}
}
.allpl {
width: 700rpx;
display: flex;
align-items: baseline;
margin: 40rpx 0 30rpx;
line-height: 42rpx;
.pfx {
display: flex;
align-items: center;
margin-left: 35rpx;
.pfxlist {
font-size: 29rpx;
color: #999999;
text {
margin-left: 10rpx;
}
margin-right: 20rpx;
}
}
.zpf {
font-size: 56rpx;
font-weight: 550;
color: #FF540B;
text {
font-weight: 500;
font-size: 29rpx;
color: #666666;
margin-left: 10rpx;
}
}
}
.plsj {
font-size: 25rpx;
color: #AAAAAA;
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
position: absolute;
top: 0rpx;
right: 20rpx;
image {
width: 22rpx;
height: 22rpx;
margin-right: 10rpx;
}
}
.imglist {
display: flex;
flex-direction: row;
align-items: center;
margin: 30rpx 0 15rpx;
.leftimg {
border-radius: 17rpx 0 0 17rpx;
}
.rightimg {
border-radius: 0 17rpx 17rpx 0;
}
image {
width: 231rpx;
height: 215rpx;
margin-right: 4rpx;
}
}
.scitemstarts {
display: flex;
align-items: center;
.star-image {
width: 28rpx;
height: 26rpx;
margin-right: 7rpx;
}
}
.yhpj-all-img {
width: 9upx;
height: 15upx;
margin-left: 10upx;
margin-top: 5upx;
}
.ckpj-txt {
font-size: 24upx;
color: #fff;
}
.ckpj {
margin-top: 15upx;
width: 196upx;
height: 60upx;
background: -webkit-linear-gradient(right, #3269F7 0%, #4A9AF9 100%);
background: linear-gradient(-90deg, #3269F7 0%, #4A9AF9 100%);
border: none;
border-radius: 60upx;
text-align: center;
line-height: 60rpx;
}
.yhplxx {
width: 600rpx;
margin-top: 25rpx;
line-height: 45rpx;
font-size: 24rpx;
color: #333333;
max-height: 135rpx;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
line-clamp: 3;
-webkit-box-orient: vertical;
}
.yhxx-time {
position: absolute;
top: 5upx;
right: 0;
font-size: 24upx;
color: rgba(153, 153, 153, 1);
}
.yhxx-name-pftxt {
display: block;
font-size: 28upx;
color: rgba(93, 163, 67, 1);
margin-top: 6upx;
}
.yhxx-name-txt {
font-size: 30upx;
color: rgba(153, 153, 153, 1);
}
.yhxx-name {
margin-left: 25upx;
font-size: 28rpx;
color: #333;
display: flex;
flex-direction: column;
}
.yhxx-img {
width: 63upx;
height: 63upx;
border-radius: 50%;
}
.yhxx {
display: flex;
flex-direction: row;
position: relative;
align-items: center;
}
.yhpl {
width: 700upx;
padding-bottom: 30rpx;
border-bottom: 1rpx solid rgba(0,0,0,0.1);
margin-bottom: 46rpx;
position: relative;
}
.yhpj-all-sl {
font-size: 25upx;
color: #999;
margin-left: 15rpx;
}
.yhpj-all-pj {
font-size: 32upx;
color: #5DA343;
margin-right: 350upx;
}
.yhpj-all-fs {
color: #333333;
font-size: 36upx;
font-weight: bold;
}
.yhpj-all {
width: 700upx;
font-size: 28upx;
color: #222;
display: flex;
align-items: baseline;
margin-top: 30rpx;
justify-content: space-between;
margin-bottom: 50rpx;
.ckgd {
font-size: 28rpx;
color: #5A8BFC;
display: flex;
align-items: center;
image {
width: 13rpx;
height: 21rpx;
margin-left: 10rpx;
}
}
}
.yhpj {
width: 750upx;
margin-top: 20upx;
background: #fff;
display: flex;
flex-direction: column;
align-items: center;
padding-bottom: 30rpx;
margin-top: 80rpx;
}
.jdpj{
width: 750upx;
display: flex;
flex-direction: column;
align-items: center;
padding-bottom: 30rpx;
background-color: #FFFFFF;
border-radius: 8rpx;
}
</style>

1
components/w-picker/areadata/areadata.js

File diff suppressed because one or more lines are too long

742
components/w-picker/date-picker.vue

@ -0,0 +1,742 @@
<template>
<view class="w-picker-view">
<picker-view v-if="fields=='year'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='month'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='day'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='hour'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='minute'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.minutes" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='second'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.minutes" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.seconds" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:{
years:[],
months:[],
days:[],
hours:[],
minutes:[],
seconds:[]
},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
startYear:{
type:[String,Number],
default:""
},
endYear:{
type:[String,Number],
default:""
},
value:{
type:[String,Array,Number],
default:""
},
current:{//
type:Boolean,
default:false
},
disabledAfter:{//
type:Boolean,
default:false
},
fields:{
type:String,
default:"day"
}
},
watch:{
fields(val){
this.initData();
},
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
formatNum(n){
return (Number(n)<10?'0'+Number(n):Number(n)+'');
},
checkValue(value){
let strReg,example
switch(this.fields){
case "year":
strReg=/^\d{4}$/;
example="2019";
break;
case "month":
strReg=/^\d{4}-\d{2}$/;
example="2019-02";
break;
case "day":
strReg=/^\d{4}-\d{2}-\d{2}$/;
example="2019-02-01";
break;
case "hour":
strReg=/^\d{4}-\d{2}-\d{2} \d{2}(:\d{2}){1,2}?$/;
example="2019-02-01 18:00:00或2019-02-01 18";
break;
case "minute":
strReg=/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}(:\d{2}){0,1}?$/;
example="2019-02-01 18:06:00或2019-02-01 18:06";
break;
case "second":
strReg=/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
example="2019-02-01 18:06:01";
break;
}
if(!strReg.test(value)){
console.log(new Error("请传入与mode、fields匹配的value值,例value="+example+""))
}
return strReg.test(value);
},
resetData(year,month,day,hour,minute){
let curDate=this.getCurrenDate();
let curFlag=this.current;
let curYear=curDate.curYear;
let curMonth=curDate.curMonth;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let curMinute=curDate.curMinute;
let curSecond=curDate.curSecond;
let months=[],days=[],hours=[],minutes=[],seconds=[];
let disabledAfter=this.disabledAfter;
let monthsLen=disabledAfter?(year*1<curYear?12:curMonth):12;
let totalDays=new Date(year,month,0).getDate();//;
let daysLen=disabledAfter?((year*1<curYear||month*1<curMonth)?totalDays:curDay):totalDays;
let hoursLen=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay)?24:curHour+1):24;
let minutesLen=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay||hour*1<curHour)?60:curMinute+1):60;
let secondsLen=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay||hour*1<curHour||minute*1<curMinute)?60:curSecond+1):60;
for(let month=1;month<=monthsLen;month++){
months.push(this.formatNum(month));
};
for(let day=1;day<=daysLen;day++){
days.push(this.formatNum(day));
}
for(let hour=0;hour<hoursLen;hour++){
hours.push(this.formatNum(hour));
}
for(let minute=0;minute<minutesLen;minute++){
minutes.push(this.formatNum(minute));
}
for(let second=0;second<secondsLen;second++){
seconds.push(this.formatNum(second));
}
return{
months,
days,
hours,
minutes,
seconds
}
},
isLeapYear (Year) {
if (((Year % 4)==0) && ((Year % 100)!=0) || ((Year % 400)==0)) {
return true;
} else {
return false;
}
},
getData(dVal){
//
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let fields=this.fields;
let curDate=this.getCurrenDate();
let curYear=curDate.curYear;
let curMonthdays=curDate.curMonthdays;
let curMonth=curDate.curMonth;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let curMinute=curDate.curMinute;
let curSecond=curDate.curSecond;
let defaultDate=this.getDefaultDate();
let startYear=this.getStartDate().getFullYear();
let endYear=this.getEndDate().getFullYear();
//year,month,day,hour;,
let years=[],months=[],days=[],hours=[],minutes=[],seconds=[];
let year=dVal[0]*1;
let month=dVal[1]*1;
let day=dVal[2]*1;
let hour=dVal[3]*1;
let minute=dVal[4]*1;
let monthsLen=disabledAfter?(year<curYear?12:curDate.curMonth):12;
let daysLen=disabledAfter?((year<curYear||month<curMonth)?defaultDate.defaultDays:curDay):(curFlag?curMonthdays:defaultDate.defaultDays);
let hoursLen=disabledAfter?((year<curYear||month<curMonth||day<curDay)?24:curHour+1):24;
let minutesLen=disabledAfter?((year<curYear||month<curMonth||day<curDay||hour<curHour)?60:curMinute+1):60;
let secondsLen=disabledAfter?((year<curYear||month<curMonth||day<curDay||hour<curHour||minute<curMinute)?60:curSecond+1):60;
for(let year=startYear;year<=(disabledAfter?curYear:endYear);year++){
years.push(year.toString())
}
for(let month=1;month<=monthsLen;month++){
months.push(this.formatNum(month));
}
for(let day=1;day<=daysLen;day++){
days.push(this.formatNum(day));
}
for(let hour=0;hour<hoursLen;hour++){
hours.push(this.formatNum(hour));
}
for(let minute=0;minute<minutesLen;minute++){
minutes.push(this.formatNum(minute));
}
// for(let second=0;second<(disabledAfter?curDate.curSecond+1:60);second++){
// seconds.push(this.formatNum(second));
// }
for(let second=0;second<60;second++){
seconds.push(this.formatNum(second));
}
return {
years,
months,
days,
hours,
minutes,
seconds
}
},
getCurrenDate(){
let curDate=new Date();
let curYear=curDate.getFullYear();
let curMonth=curDate.getMonth()+1;
let curMonthdays=new Date(curYear,curMonth,0).getDate();
let curDay=curDate.getDate();
let curHour=curDate.getHours();
let curMinute=curDate.getMinutes();
let curSecond=curDate.getSeconds();
return{
curDate,
curYear,
curMonth,
curMonthdays,
curDay,
curHour,
curMinute,
curSecond
}
},
getDefaultDate(){
let value=this.value;
let reg=/-/g;
let defaultDate=value?new Date(value.replace(reg,"/")):new Date();
let defaultYear=defaultDate.getFullYear();
let defaultMonth=defaultDate.getMonth()+1;
let defaultDay=defaultDate.getDate();
let defaultDays=new Date(defaultYear,defaultMonth,0).getDate()*1;
return{
defaultDate,
defaultYear,
defaultMonth,
defaultDay,
defaultDays
}
},
getStartDate(){
let start=this.startYear;
let startDate="";
let reg=/-/g;
if(start){
startDate=new Date(start+"/01/01");
}else{
startDate=new Date("1970/01/01");
}
return startDate;
},
getEndDate(){
let end=this.endYear;
let reg=/-/g;
let endDate="";
if(end){
endDate=new Date(end+"/12/01");
}else{
endDate=new Date();
}
return endDate;
},
getDval(){
let value=this.value;
let fields=this.fields;
let dVal=null;
let aDate=new Date();
let year=this.formatNum(aDate.getFullYear());
let month=this.formatNum(aDate.getMonth()+1);
let day=this.formatNum(aDate.getDate());
let hour=this.formatNum(aDate.getHours());
let minute=this.formatNum(aDate.getMinutes());
let second=this.formatNum(aDate.getSeconds());
if(value){
let flag=this.checkValue(value);
if(!flag){
dVal=[year,month,day,hour,minute,second]
}else{
switch(this.fields){
case "year":
dVal=value?[value]:[];
break;
case "month":
dVal=value?value.split("-"):[];
break;
case "day":
dVal=value?value.split("-"):[];
break;
case "hour":
dVal=[...value.split(" ")[0].split("-"),...value.split(" ")[1].split(":")];
break;
case "minute":
dVal=value?[...value.split(" ")[0].split("-"),...value.split(" ")[1].split(":")]:[];
break;
case "second":
dVal=[...value.split(" ")[0].split("-"),...value.split(" ")[1].split(":")];
break;
}
}
}else{
dVal=[year,month,day,hour,minute,second]
}
return dVal;
},
initData(){
let startDate,endDate,startYear,endYear,startMonth,endMonth,startDay,endDay;
let years=[],months=[],days=[],hours=[],minutes=[],seconds=[];
let dVal=[],pickVal=[];
let value=this.value;
let reg=/-/g;
let range={};
let result="",full="",year,month,day,hour,minute,second,obj={};
let defaultDate=this.getDefaultDate();
let defaultYear=defaultDate.defaultYear;
let defaultMonth=defaultDate.defaultMonth;
let defaultDay=defaultDate.defaultDay;
let defaultDays=defaultDate.defaultDays;
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let curDate=this.getCurrenDate();
let curYear=curDate.curYear;
let curMonth=curDate.curMonth;
let curMonthdays=curDate.curMonthdays;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let curMinute=curDate.curMinute;
let curSecond=curDate.curSecond;
let dateData=[];
dVal=this.getDval();
startDate=this.getStartDate();
endDate=this.getEndDate();
startYear=startDate.getFullYear();
startMonth=startDate.getMonth();
startDay=startDate.getDate();
endYear=endDate.getFullYear();
endMonth=endDate.getMonth();
endDay=endDate.getDate();
dateData=this.getData(dVal);
years=dateData.years;
months=dateData.months;
days=dateData.days;
hours=dateData.hours;
minutes=dateData.minutes;
seconds=dateData.seconds;
switch(this.fields){
case "year":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0
]:(curFlag?[
years.indexOf(curYear+'')
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0
]);
range={years};
year=dVal[0]?dVal[0]:years[0];
result=full=`${year}`;
obj={
year
}
break;
case "month":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth))
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0
]);
range={years,months};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
result=full=`${year+'-'+month}`;
obj={
year,
month
}
break;
case "day":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0
]);
range={years,months,days};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
result=full=`${year+'-'+month+'-'+day}`;
obj={
year,
month,
day
}
break;
case "hour":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
hours.indexOf(this.formatNum(curHour)),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0
]);
range={years,months,days,hours};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
hour=dVal[3]?dVal[3]:hours[0];
result=`${year+'-'+month+'-'+day+' '+hour}`;
full=`${year+'-'+month+'-'+day+' '+hour+':00:00'}`;
obj={
year,
month,
day,
hour
}
break;
case "minute":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
hours.indexOf(this.formatNum(curHour)),
minutes.indexOf(this.formatNum(curMinute)),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0
]);
range={years,months,days,hours,minutes};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
hour=dVal[3]?dVal[3]:hours[0];
minute=dVal[4]?dVal[4]:minutes[0];
full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':00'}`;
result=`${year+'-'+month+'-'+day+' '+hour+':'+minute}`;
obj={
year,
month,
day,
hour,
minute
}
break;
case "second":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0,
dVal[5]&&seconds.indexOf(dVal[5])!=-1?seconds.indexOf(dVal[5]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
hours.indexOf(this.formatNum(curHour)),
minutes.indexOf(this.formatNum(curMinute)),
seconds.indexOf(this.formatNum(curSecond)),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0,
dVal[5]&&seconds.indexOf(dVal[5])!=-1?seconds.indexOf(dVal[5]):0
]);
range={years,months,days,hours,minutes,seconds};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
hour=dVal[3]?dVal[3]:hours[0];
minute=dVal[4]?dVal[4]:minutes[0];
second=dVal[5]?dVal[5]:seconds[0];
result=full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':'+second}`;
obj={
year,
month,
day,
hour,
minute,
second
}
break;
default:
range={years,months,days};
break;
}
this.range=range;
this.checkObj=obj;
this.$emit("change",{
result:result,
value:full,
obj:obj
});
this.$nextTick(()=>{
this.pickVal=pickVal;
})
},
handlerChange(e){
let arr=[...e.detail.value];
let data=this.range;
let year="",month="",day="",hour="",minute="",second="";
let result="",full="",obj={};
let months=null,days=null,hours=null,minutes=null,seconds=null;
let disabledAfter=this.disabledAfter;
let leapYear=false,resetData={};
year=(arr[0]||arr[0]==0)?data.years[arr[0]]||data.years[data.years.length-1]:"";
month=(arr[1]||arr[1]==0)?data.months[arr[1]]||data.months[data.months.length-1]:"";
day=(arr[2]||arr[2]==0)?data.days[arr[2]]||data.days[data.days.length-1]:"";
hour=(arr[3]||arr[3]==0)?data.hours[arr[3]]||data.hours[data.hours.length-1]:"";
minute=(arr[4]||arr[4]==0)?data.minutes[arr[4]]||data.minutes[data.minutes.length-1]:"";
second=(arr[5]||arr[5]==0)?data.seconds[arr[5]]||data.seconds[data.seconds.length-1]:"";
resetData=this.resetData(year,month,day,hour,minute);//;
leapYear=this.isLeapYear(year);//;
switch(this.fields){
case "year":
result=full=`${year}`;
obj={
year
};
break;
case "month":
result=full=`${year+'-'+month}`;
if(this.disabledAfter)months=resetData.months;
if(months)this.range.months=months;
obj={
year,
month
}
break;
case "day":
result=full=`${year+'-'+month+'-'+day}`;
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
}else{
if(leapYear||(month!=this.checkObj.month)||month==2){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
obj={
year,
month,
day
}
break;
case "hour":
result=`${year+'-'+month+'-'+day+' '+hour}`;
full=`${year+'-'+month+'-'+day+' '+hour+':00:00'}`;
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
hours=resetData.hours;
}else{
if(leapYear||(month!=this.checkObj.month)||month==2){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
if(hours)this.range.hours=hours;
obj={
year,
month,
day,
hour
}
break;
case "minute":
full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':00'}`;
result=`${year+'-'+month+'-'+day+' '+hour+':'+minute}`;
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
hours=resetData.hours;
minutes=resetData.minutes;
}else{
if(leapYear||(month!=this.checkObj.month)||month==2){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
if(hours)this.range.hours=hours;
if(minutes)this.range.minutes=minutes;
obj={
year,
month,
day,
hour,
minute
};
break;
case "second":
result=full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':'+second}`;
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
hours=resetData.hours;
minutes=resetData.minutes;
//seconds=resetData.seconds;
}else{
if(leapYear||(month!=this.checkObj.month)||month==2){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
if(hours)this.range.hours=hours;
if(minutes)this.range.minutes=minutes;
//if(seconds)this.range.seconds=seconds;
obj={
year,
month,
day,
hour,
minute,
second
}
break;
}
this.checkObj=obj;
this.$emit("change",{
result:result,
value:full,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

345
components/w-picker/half-picker.vue

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

274
components/w-picker/linkage-picker.vue

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

344
components/w-picker/range-picker.vue

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

183
components/w-picker/region-picker.vue

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

129
components/w-picker/selector-picker.vue

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

250
components/w-picker/shortterm-picker.vue

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

218
components/w-picker/time-picker.vue

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

26
components/w-picker/w-picker.css

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

340
components/w-picker/w-picker.vue

@ -0,0 +1,340 @@
<template name="w-picker">
<view class="w-picker" :key="createKey" :data-key="createKey">
<view class="mask" :class="{'visible':visible}" @tap="onCancel" @touchmove.stop.prevent catchtouchmove="true"></view>
<view class="w-picker-cnt" :class="{'visible':visible}">
<view class="w-picker-header" @touchmove.stop.prevent catchtouchmove="true">
<text @tap.stop.prevent="onCancel">取消</text>
<slot></slot>
<text :style="{'color':themeColor}" @tap.stop.prevent="pickerConfirm">确定</text>
</view>
<date-picker
v-if="mode=='date'"
class="w-picker-wrapper"
:startYear="startYear"
:endYear="endYear"
:value="value"
:fields="fields"
:item-height="itemHeight"
:current="current"
:disabled-after="disabledAfter"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</date-picker>
<range-picker
v-if="mode=='range'"
class="w-picker-wrapper"
:startYear="startYear"
:endYear="endYear"
:value="value"
:item-height="itemHeight"
:current="current"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</range-picker>
<half-picker
v-if="mode=='half'"
class="w-picker-wrapper"
:startYear="startYear"
:endYear="endYear"
:value="value"
:item-height="itemHeight"
:current="current"
:disabled-after="disabledAfter"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</half-picker>
<shortterm-picker
v-if="mode=='shortTerm'"
class="w-picker-wrapper"
:startYear="startYear"
:endYear="endYear"
:value="value"
:item-height="itemHeight"
:current="current"
expand="60"
:disabled-after="disabledAfter"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</shortterm-picker>
<time-picker
v-if="mode=='time'"
class="w-picker-wrapper"
:value="value"
:item-height="itemHeight"
:current="current"
:disabled-after="disabledAfter"
:second="second"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</time-picker>
<selector-picker
v-if="mode=='selector'"
class="w-picker-wrapper"
:value="value"
:item-height="itemHeight"
:options="options"
:default-type="defaultType"
:default-props="defaultProps"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</selector-picker>
<region-picker
v-if="mode=='region'"
class="w-picker-wrapper"
:value="value"
:hide-area="hideArea"
:default-type="defaultType"
:item-height="itemHeight"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</region-picker>
<linkage-picker
v-if="mode=='linkage'"
class="w-picker-wrapper"
:value="value"
:options="options"
:level="level"
:default-type="defaultType"
:default-props="defaultProps"
:item-height="itemHeight"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</linkage-picker>
</view>
</view>
</template>
<script>
import datePicker from "./date-picker.vue"
import rangePicker from "./range-picker.vue"
import halfPicker from "./half-picker.vue"
import shorttermPicker from "./shortterm-picker.vue"
import timePicker from "./time-picker.vue"
import selectorPicker from "./selector-picker.vue"
import regionPicker from "./region-picker.vue"
import linkagePicker from "./linkage-picker.vue"
export default {
name:"w-picker",
components:{
datePicker,
rangePicker,
halfPicker,
timePicker,
selectorPicker,
shorttermPicker,
regionPicker,
linkagePicker
},
props:{
mode:{
type:String,
default:"date"
},
value:{//
type:[String,Array,Number],
default:""
},
current:{//
type:Boolean,
default:false
},
themeColor:{//
type:String,
default:"#f5a200"
},
fields:{//:yearmonthdayhourminutesecond
type:String,
default:"date"
},
disabledAfter:{//
type:Boolean,
default:false
},
second:{//time-picker
type:Boolean,
default:true
},
options:{//selector,region
type:[Array,Object],
default(){
return []
}
},
defaultProps:{//selector,linkagle
type:Object,
default(){
return{
label:"label",
value:"value",
children:"children"
}
}
},
defaultType:{
type:String,
default:"label"
},
hideArea:{//mode=region
type:Boolean,
default:false
},
level:{
//,2-4;
type:[Number,String],
default:2
},
timeout:{//,
type:Boolean,
default:false
},
expand:{//mode=shortterm
type:[Number,String],
default:30
},
startYear:{
type:[String,Number],
default:1970
},
endYear:{
type:[String,Number],
default:new Date().getFullYear()
},
visible:{
type:Boolean,
default:false
}
},
created() {
this.createKey=Math.random()*1000;
},
data() {
return {
itemHeight:`height: ${uni.upx2px(88)}px;`,
result:{},
confirmFlag:true
};
},
methods:{
touchStart(){
if(this.timeout){
this.confirmFlag=false;
}
},
touchEnd(){
if(this.timeout){
setTimeout(()=>{
this.confirmFlag=true;
},500)
}
},
handlerChange(res){
let _this=this;
this.result={...res};
},
show(){
this.$emit("update:visible",true);
},
hide(){
this.$emit("update:visible",false);
},
onCancel(res){
this.$emit("update:visible",false);
this.$emit("cancel");
},
pickerConfirm(){
if(!this.confirmFlag){
return;
};
this.$emit("confirm",this.result);
this.$emit("update:visible",false);
}
}
}
</script>
<style lang="scss">
.w-picker-item {
text-align: center;
width: 100%;
height: 88upx;
line-height: 88upx;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 30upx;
}
.w-picker{
z-index: 888;
.mask {
position: fixed;
z-index: 1000;
top: 0;
right: 0;
left: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
visibility: hidden;
opacity: 0;
transition: all 0.3s ease;
}
.mask.visible{
visibility: visible;
opacity: 1;
}
.w-picker-cnt {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
transition: all 0.3s ease;
transform: translateY(100%);
z-index: 3000;
background-color: #fff;
}
.w-picker-cnt.visible {
transform: translateY(0);
}
.w-picker-header{
display: flex;
align-items: center;
padding: 0 30upx;
height: 88upx;
background-color: #fff;
position: relative;
text-align: center;
font-size: 32upx;
justify-content: space-between;
border-bottom: solid 1px #eee;
.w-picker-btn{
font-size: 30upx;
}
}
.w-picker-hd:after {
content: ' ';
position: absolute;
left: 0;
bottom: 0;
right: 0;
height: 1px;
border-bottom: 1px solid #e5e5e5;
color: #e5e5e5;
transform-origin: 0 100%;
transform: scaleY(0.5);
}
}
</style>

669
components/zhouWei-navBar/index.vue

@ -0,0 +1,669 @@
<template>
<view class="topnav">
<view :class="{ 'header_fixed': navFixed, 'header_absolute': type == 'transparent', 'header_shadow': navShadow, 'header_colorWhite': isWhite, themeBgColor: themeBgColor}"
:style="{ paddingTop: statusBarHeight + 'px', backgroundImage: navBgColor, color: navFontColor, opacity: transparentValue }">
<view class="header_content">
<view class="header_left_box">
<slot name="left">
<view class="header_left_info" :class="{ 'header_btnMongol':isTwoBtn,'header_colorWhite_btnMongol': isWhite && isTwoBtn}"v-if="back || $slots.left || home">
<view class="header_left_back" :class="{'header_btnMongol_left_back':isTwoBtn}" v-if="back && !firstPage">
<image v-if="!isblack" class="header_icon" @click="toback" src="/static/zhouWei-navBar/njdts-zy-zuojiantou-bai.png" mode="aspectFit"></image>
<image v-if="isblack" class="header_icon" @click="toback" src="/static/zhouWei-navBar/njdts-zy-zuojiantou-hei.png" mode="aspectFit"></image>
<view class="zjx"></view>
<image class="header_icon" @click="toindex" src="/static/zhouWei-navBar/njdts-zy-zhuye-hei.png" v-if="isblack" mode="aspectFit"></image>
<image class="header_icon" @click="toindex" src="/static/zhouWei-navBar/njdts-zy-zhuye-bai.png" v-if="!isblack" mode="aspectFit"></image>
</view>
<text class="header_left_line" :class="{'header_colorWhite_left_line': isWhite}" v-if="isTwoBtn"></text>
<view class="header_left_home" :class="{'header_btnMongol_left_home':isTwoBtn}" v-if="(firstPage && back) || home"
@click="onBackHome">
<image class="header_icon" v-if="isWhite" src="/static/zhouWei-navBar/icon_home_white.png" mode="aspectFit"></image>
<image class="header_icon" v-else src="/static/zhouWei-navBar/icon_home_black.png" mode="aspectFit"></image>
</view>
</view>
</slot>
<view class="header_title" v-if="!titleCenter && ($slots.default || navTitle)" :style="{color: navFontColor}">
<slot><text :style="{color: navFontColor}">{{ navTitle }}</text></slot>
</view>
</view>
<view class="sy-tq" @click="nato('/pages/keliu/keliu')">
<view class="tb"><image class="img" :src="gettqImg(cond_code)" mode=""></image></view>
<view class="wz">
<text class="t1">{{ tmp }}</text>
<text class="t2"></text>
</view>
</view>
<!-- <view class="klinfo">
<swiper :autoplay="true" :interval="4000" :duration="1000" easing-function="linear">
<swiper-item v-for="(el,ind) in jqdatas" v-if="ind<3" >
<view class="swiper-item">
<view class="kltitle">{{el.name}}</view>
<view class="kltit">
<view class="klvalue">当前客流</view>
<view class="klvalue">{{el.value}}</view>
</view>
</view>
</swiper-item>
</swiper>
</view> -->
<view class="header_title header_title_center" v-if="titleCenter && ($slots.default || navTitle)" :style="{color: navFontColor}">
<slot><text :style="{color: navFontColor}">{{ navTitle }}</text></slot>
</view>
<view class="header_right_info">
<slot name="right"></slot>
</view>
</view>
</view>
<view class="header_transparentFixed header_fixed" v-if="type == 'transparentFixed'" :style="{ paddingTop: statusBarHeight + 'px', color: navTransparentFixedFontColor, opacity: 1 - transparentValue, zIndex: transparentValue < 0.3 ? 100 : 90 }">
<view class="header_content">
<view class="header_left_box">
<slot name="transparentFixedLeft">
<view class="header_left_info header_transparentFixed_left_info" :class="{'header_transparentFixed_colorWhite_left_info': isWhite}"
v-if="back || $slots.left || home">
<view class="header_left_back" :class="{'header_btnMongol_left_back':isTwoBtn}" v-if="back && !firstPage" @click="onBackPage">
<image class="header_icon" v-if="isWhite" src="/static/zhouWei-navBar/icon_back_white.png" mode="aspectFit"></image>
<image class="header_icon" v-else src="/static/zhouWei-navBar/icon_back_black.png" mode="aspectFit"></image>
</view>
<text class="header_left_line" v-if="isTwoBtn"></text>
<view class="header_left_home" :class="{'header_btnMongol_left_home':isTwoBtn}" v-if="(firstPage && back) || home"
@click="onBackHome">
<image class="header_icon" v-if="isWhite" src="/static/zhouWei-navBar/icon_home_white.png" mode="aspectFit"></image>
<image class="header_icon" v-else src="/static/zhouWei-navBar/icon_home_black.png" mode="aspectFit"></image>
</view>
</view>
</slot>
<view class="header_title" v-if="!titleCenter && (navTitle || $slots.transparentFixed)" :style="{ color: navTransparentFixedFontColor}">
<slot name="transparentFixed">
<text :style="{color: navTransparentFixedFontColor}">{{ navTitle }}</text>
</slot>
</view>
</view>
<view class="header_title header_title_center" v-if="titleCenter && (navTitle || $slots.transparentFixed)" :style="{color: navTransparentFixedFontColor}">
<slot name="transparentFixed"><text :style="{color: navTransparentFixedFontColor}">{{ navTitle }}</text></slot>
</view>
<view class="header_right_info">
<slot name="transparentFixedRight"></slot>
</view>
</view>
</view>
<view v-if="type == 'fixed'" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="header_station"></view>
</view>
</view>
</template>
<script>
//
//
// 使
const mainPagePath = ['pages/navList', 'pages/demo6'];
//
const homePath = '/pages/index/index';
//
const whiteList = ['#FFF', '#FFFFFF', 'white', 'rgb(255,255,255)', 'rgba(255,255,255,1)'];
export default {
props: {
tmp : {
type: String,
default: function() {
return '';
}
},
cond_code : {
type: String,
default: function() {
return '';
}
},
jqdatas : [],
//
// 1000
// 2000
// 3000 backClick
isblack:{
type: Boolean,
default: function() {
return false;
}
},
backState: {
default: function() {
return 1000;
}
},
//
home: {
type: Boolean,
default: function() {
return false;
}
},
//
bgColor: {
type: [String, Array],
default: function() {
return '#FFFFFF';
}
},
//
bgColorAngle: {
type: [String, Number],
default: function() {
return 45;
}
},
//
fontColor: {
type: String,
default: function() {
return '#000000';
}
},
//
titleCenter: {
type: Boolean,
default: function() {
return true;
}
},
//
title: {
type: String,
default: function() {
return '';
}
},
// fixed
// ordinary
// transparent
//transparentFixed
type: {
type: String,
default: function() {
return 'fixed';
}
},
//
transparentFixedFontColor: {
type: String,
default: function() {
return '#000000';
}
},
// ()
scrollTop: {
type: Number,
default: function() {
return 0;
}
},
//
shadow: {
type: Boolean,
default: function() {
return true;
}
},
},
data() {
return {
//
firstPage: false,
//
transparentValue: 1,
//
navTitle: '',
//
navFontColor: '#000000',
//
navBgColor: 'none',
//
navTransparentFixedFontColor: '#000000',
// 使
themeBgColor: false,
//
statusBarHeight: 0,
//
lastFrontColor:"",
};
},
computed: {
back() {
return this.backState == 1000 || this.backState == 3000;
},
//
navFixed() {
if (this.type == 'transparentFixed' || this.type == 'fixed') {
return true;
} else {
return false;
}
},
//线
navShadow() {
if(this.bgColor && typeof(this.bgColor) == "string"){
return this.shadow && this.type !== 'transparent' && whiteList.includes(this.bgColor);
}else{
return false;
}
},
//
isWhite() {
return whiteList.includes(this.navFontColor);
},
//
isTwoBtn() {
return (this.backState == 1000 || this.backState == 3000) && this.home && !this.firstPage;
}
},
watch: {
title(val) {
this.navTitle = val;
},
fontColor(val) {
this.navFontColor = val;
this.settingColor();
},
bgColor(val) {
this.getNavBgColor(val);
},
transparentFixedFontColor(val) {
this.navTransparentFixedFontColor = val;
},
scrollTop(val) {
this.pageScroll({
scrollTop: val
});
}
},
//
created() {
this.navTitle = this.title;
this.navFontColor = this.fontColor;
this.getNavBgColor(this.bgColor);
this.navTransparentFixedFontColor = this.transparentFixedFontColor;
//
this.statusBarHeight = uni.getSystemInfoSync()['statusBarHeight'];
const _this = this;
this.pageScroll({
scrollTop: this.scrollTop
});
//
let currentPages = getCurrentPages();
let pageLen = currentPages.length;
//backtrue
if (pageLen == 1 && !mainPagePath.includes(currentPages[0].route)) {
this.firstPage = true;
}
console.log(this.isblack)
},
//
methods: {
nato(url){
uni.navigateTo({
url:url
})
},
gettqImg(code) {
return 'https://cs.tour-ma.com/r/cms/www/m/tianqi/' + code + '.png';
},
getimg2(url){
return 'https://cs.tour-ma.com/r/cms/www/m/yushan/' + url;
},
toindex(){
console.log(11111)
uni.reLaunch({
url:'../../pages/index/index'
})
},
toback(){
console.log(2222)
uni.navigateBack();
},
//
onBackPage() {
if (this.backState == 3000) {
this.$emit('backClick');
} else {
uni.navigateBack();
}
},
//
onBackHome() {
uni.switchTab({
url: homePath
});
},
pageScroll(e) {
if (this.type == 'transparentFixed') {
if (e.scrollTop && e.scrollTop > 0) {
if (e.scrollTop > 180) {
this.transparentValue = 1;
} else {
this.transparentValue = e.scrollTop / 180;
}
} else {
this.transparentValue = 0;
}
this.settingColor();
}
},
//
getNavBgColor(val) {
if (val == 'themeBgColor') {
this.themeBgColor = true;
this.navBgColor = 'none';
} else if (this.type == 'transparent') {
this.themeBgColor = false;
this.navBgColor = 'none';
} else {
if (typeof val == 'string') {
this.navBgColor = 'linear-gradient(90deg,' + val + ',' + val + ')';
} else if (Array.isArray(val) && val.length >= 2) {
let navBgColor = 'linear-gradient(' + this.bgColorAngle + 'deg';
val.forEach((item,ind) => {
if (typeof item == 'string') {
if(ind==1){
navBgColor += ' 0%,' + item;
}else{
navBgColor += ',' + item;
}
} else if (typeof item == 'object') {
navBgColor += ',' + item.color + ' ' + item.scale;
}
});
console.log(navBgColor)
navBgColor += ' 88%)';
this.navBgColor = navBgColor;
}
}
},
//
settingColor() {
let navColor = this.navFontColor;
if (this.type == 'transparentFixed' && this.transparentValue <= 0.5) {
navColor = this.navTransparentFixedFontColor;
}
let frontColor = "#000000";
if (whiteList.includes(navColor)) {
frontColor = '#ffffff';
}
if (this.lastFrontColor == frontColor) {
return;
}
setTimeout(() => {
this.lastFrontColor = frontColor;
//
uni.setNavigationBarColor({
frontColor: frontColor,
backgroundColor: "#FFFFFF"
});
}, 150);
}
}
};
</script>
<style lang="scss">
/* #ifdef APP-PLUS-NVUE */
@function unit($num) {
@return $num + 0rpx;
}
/* #endif */
/* #ifndef APP-PLUS-NVUE */
@function unit($num) {
@return $num + 0upx;
}
/* #endif */
.header_content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
/* #ifdef MP */
padding-right: unit(190);
box-sizing: border-box;
/* #endif */
width: unit(750);
align-items: flex-end;
justify-content: space-between;
flex-direction: row;
height: unit(88);
position: relative;
}
.header_station {
height: unit(88);
}
.header_shadow {
// border-style: solid;
// border-width: unit(2);
// border-color: #f5f5f5;
box-shadow: 0 0 unit(6) 0 #ddd;
}
.header_fixed {
position: fixed;
top: 0;
left: 0;
width: unit(750);
z-index: 99;
}
.header_absolute {
position: absolute;
top: 0;
left: 0;
z-index: 99;
width: unit(750);
background: transparent!important;
// background-image: url(../../static/banner.png) !important;
}
.header_left_box {
/* #ifndef APP-PLUS-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
height: unit(88);
flex: 1;
}
.header_left_line {
height: unit(30);
width: unit(2);
background-color: rgba(255, 255, 255, 0.4);
}
.header_left_back {
width: unit(120);
border-radius: unit(33);
border-style: solid;
border-width: unit(1);
border-color: #EBEBEB;
height: 100%;
/* #ifndef APP-PLUS-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: space-between;
padding: 0 25upx;
.zjx{
width: unit(2);
height: 60%;
position: absolute;
top: 20%;
left: 50%;
background: #CCCCCC;
}
}
.header_icon {
width: unit(34);
height: unit(34);
}
.header_left_home {
width: unit(56);
height: 100%;
/* #ifndef APP-PLUS-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: center;
}
.header_left_info {
/* #ifndef APP-PLUS-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
height: unit(65);
margin-left: unit(16);
position: fixed;
}
.header_title {
height: unit(88);
font-size: unit(32);
padding-left: unit(30);
padding-right: unit(30);
font-weight: 700;
text-overflow: ellipsis;
/* #ifndef APP-PLUS-NVUE */
white-space: nowrap;
display: flex;
overflow: hidden;
/* #endif */
/* #ifdef APP-PLUS-NVUE */
lines: 1;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
/* #ifdef MP */
max-width: calc(100vw - 160upx);
/* #endif */
}
.header_title_center {
position: absolute;
bottom: unit(0);
left: unit(375);
transform: translateX(-50%);
}
.header_right_info {
height: unit(88);
/* #ifndef APP-PLUS-NVUE */
display: flex;
flex-shrink: 0;
/* #endif */
flex-direction: row;
align-items: center;
}
.header_btnMongol {
border-radius: unit(33);
border-style: solid;
border-width: unit(2);
border-color: rgba(0, 0, 0, 0.1);
background-color: rgba(255, 255, 255, 0.7);
/* #ifndef APP-PLUS-NVUE */
box-sizing: border-box;
/* #endif */
}
.header_btnMongol_left_back,
.header_btnMongol_left_home {
width: unit(70);
}
.header_transparentFixed {
border-bottom-width: 0;
background-color: transparent;
background-image: transparent;
}
.header_transparentFixed_left_info {
border-style: solid;
border-width: unit(2);
border-color: rgba(0, 0, 0, 0.1);
background-color: rgba(255, 255, 255, 0.7);
border-radius: unit(33);
/* #ifndef APP-PLUS-NVUE */
box-sizing: border-box;
/* #endif */
}
.header_transparentFixed_colorWhite_left_info {
border-style: solid;
border-width: unit(2);
border-color: rgba(255, 255, 255, 0.3);
background-color: rgba(0, 0, 0, 0.2);
}
//
.header_colorWhite_btnMongol {
border-style: solid;
border-width: unit(2);
border-color: rgba(255, 255, 255, 0.3);
background-color: rgba(0, 0, 0, 0.2);
}
.header_colorWhite_left_line {
background-color: rgba(255, 255, 255, 0.3);
}
.sy-tq{
position: absolute;
top:0%;
left: unit(30);
z-index: 999;
display: flex;
height: unit(88);
line-height: unit(88);
}
.tb{
display: flex;
align-items: center;
justify-content: center;
margin: unit(0) unit(10) unit(0) unit(0);
}
.tb .img{
width: unit(50);
height: unit(50);
margin-top: unit(-5);
}
.wz{
color: #fff;
}
.klinfo{
position: absolute;
top: 80%;
left: 5%;
width: 65%;
height: 85upx;
font-size: 26upx;
color: #fff;
swiper{
width: 100%;
height: 100%;
swiper-item{
.swiper-item{
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: start;
.kltit{
display: flex;
justify-content: start;
}
.kltitle{
margin-right: 30upx;
}
}
}
}
}
</style>

152
components/zhouWei-navBar/zhouWei-navBar.md

@ -0,0 +1,152 @@
# zhouWei-navBar 适用于 uni-app 项目的头部导航组件,支持V3编译、nvue编译
导航栏组件,主要用于头部导航,组件名:zhouWei-navBar
本组件目前兼容微信小程序、H5、5+APP。其他平台没试过
本组件支持模式:
1. 普通固定顶部导航
2. 透明导航
3. 透明固定顶部导航
4. 不固定普通导航
5. 颜色渐变导航
本组件内置特殊功能:
1. fontColor字体颜色为白色的时候手机状态栏会自动显示白色,否则显示灰色
2. 页面为第一个页面时左上角自动显示返回主页的图标(具体看组件:zhouWei-navBar/index.vue =>页面script)
3. nvue页面必须在当前页面引入本组件才可以使用
### QQ交流群(学习干货多多) 607391225
![QQ交流群](http://qn.kemean.cn//upload/202004/14/15868301778472k7oubi6.png)
### 本组件全局配置(位置:zhouWei-navBar/index.vue =>页面script)
1. 主页页面的页面路径
2. 首页页面路径
```
// 主页页面的页面路径
// 关联功能:打开的页面只有一个的时候右上角自动显示返回首页按钮,下面这个数组是排除显示返回首页的页面。
// 主页使用场景:小程序分享出去的页面,用户点击开是分享页面,很多情况下是没有返回首页按钮的
const mainPagePath = ["pages/navList"];
//返回首页的地址
const homePath = "/pages/navList";
```
### 在main.js引入组件,并注册全局组件
```
import zhouWeiNavBar from "@/components/zhouWei-navBar";
Vue.component("nav-bar", zhouWeiNavBar);
```
### 或者在页面script中引入组件,并注册组件(nvue页面必须是这样引入)
```
import navBar from "@/components/zhouWei-navBar";
export default {
components: {navBar}
}
```
### 案例一
默认特性:左上角有返回箭头,nav-bar导航固定在顶部、标题居中
```
<nav-bar>我的</nav-bar>
```
### 案例二
特性:无返回箭头、字体色为白色、标题左对齐、nav-bar导航透明并不固定在顶部、右边插槽有按钮
```
<nav-bar backState="2000" fontColor="#FFF" :titleCenter="false" type="transparent" title="我的">
<view class="icon_setUp" slot="right">设置</view>
</nav-bar>
```
### 案例三:颜色渐变导航
特性:颜色渐变导航
```
<nav-bar home :bgColor="['#f37402','#0f0']" bgColorAngle="90" fontColor="#FFF" title="颜色渐变导航"></nav-bar>
```
### 案例四:颜色渐变导航
特性:颜色渐变导航
```
<nav-bar :bgColor="bgColorList" bgColorAngle="45" fontColor="#FFF" title="颜色渐变导航"></nav-bar>
<!-- bgColor值 -->
bgColorList:[
{color:"#f37402",scale:"0%"},
{color:"#0f0",scale:"20%"},
{color:"#f00",scale:"80%"},
{color:"#00f",scale:"100%"}
]
```
### 案例五:滑动透明导航
特性:有返回箭头、nav-bar导航透明并固定在顶部、透明状态字体为白色、页面想下滑动nav-bar导航条逐渐变白色、右边插槽有按钮
```
<nav-bar ref="navBar" :scrollTop="scrollTop" transparentFixedFontColor="#FFF" type="transparentFixed" title="我的简历">
<view class="transparent_fixed_preview" slot="transparentFixedRight" @click="onPreview">预览</view> //透明状态下的按钮
<view class="preview" slot="right" @click="onPreview">预览</view> //不状态下的按钮
</nav-bar>
```
```
//设置滚动值方法一:
data() {
return {
scrollTop:0
}
},
onPageScroll(e) {
this.scrollTop = e.scrollTop;
}
//设置滚动值方法二:
onPageScroll (e) {
this.$refs.navBar.pageScroll(e);
}
```
### 案例六:搜索框|地区选择
特性:无返回箭头、nav-bar导航固定在顶部、地区选择、搜索框
```
<nav-bar backState="2000">
<view slot="left" class="address_select">深圳市</view>
<view slot="right" class="search_box" @click="onPageJump('/pages/home/search')">
<text class="icon_search"></text>
<text class="tips">搜索目的地/职位等</text>
</view>
</nav-bar>
```
### 属性
| 名称 | 类型 | 默认值 | 描述 |
| ----------------------------|--------------- | ------------- | ---------------------------------------------------|
| backState | String | 1000 | 返回上一页面按钮,`1000` 显示返回按钮,`2000` 不显示返回按钮,`3000`自定义返回按钮方法,点击返回箭头后会发送一个`backClick`事件|
| home | Boolean | true | 返回首页按钮(首页地址在源文件里配置) |
| bgColor | String,Array | #FFF | 导航背景颜色,值为数组的时候显示渐变颜色,`bgColor="themeBgColor"`的时候会调用全局`class="themeBgColor"`的样式|
| bgColorAngle | String,Number | 90 | 导航背景颜色渐变角度(`bgColor`为数组生效) |
| fontColor | String | #000 | 导航字体颜色,(当颜色为白色的时候导航状态栏和图片为白色的)|
| titleCenter | Boolean | true | 标题`title`居中 |
| title | String | -- | 标题`title`值 |
| transparentFixedFontColor | String | #000 | 导航`type`类型为`transparentFixed`时透明状态下的字体颜色 |
| type | String | fixed | 导航类型,可选:1.`fixed`固定导航 2.`ordinary`不固定导航 3.`transparent`透明不固定导航 4.`transparentFixed`透明固定导航|
| scrollTop | Number | 0 | 导航`type`类型为`transparentFixed`时页面滚动值(`具体看上面的案例五`)|
| shadow | Boolean | true | 背景色为纯白色的时候是否显示底边阴影 |
### bgColor数组值为JSON的参数
| 名称 | 类型 | 默认值 | 描述 |
| ----------------------------|--------------- | ------------- | ---------------------------------------------------|
| color | String | -- | 渐变颜色值 |
| scale | String | -- | 渐变比例(百分比%) |
### 插槽
| 名称 | 描述 |
| ----------------------|-------------------------------------------------------------------|
| left | 左插槽 |
| default | 中间标题插槽(`type`类型为`transparentFixed`时插槽只会穿透到实色背景下) |
| right | 右插槽 |
| transparentFixed | 导航`type`类型为`transparentFixed`时透明状态下中间插槽 |
| transparentFixedRight | 导航`type`类型为`transparentFixed`时透明状态下右插槽 |
| transparentFixedRight | 导航`type`类型为`transparentFixed`时透明状态下右插槽 |
### 事件(type类型为transparentFixed时可用)
| 名称 | 参数 | 描述 |
| -----------------|------------------ | --------------------------|
| backClick | 返回上一页按钮方法 | `backState=3000`时生效 |

166
js_sdk/u-charts/u-charts/component.vue

@ -0,0 +1,166 @@
<template>
<canvas v-if="canvasId" :id="canvasId" :canvasId="canvasId" :style="{'width':cWidth*pixelRatio+'px','height':cHeight*pixelRatio+'px', 'transform': 'scale('+(1/pixelRatio)+')','margin-left':-cWidth*(pixelRatio-1)/2+'px','margin-top':-cHeight*(pixelRatio-1)/2+'px'}"
@touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd" @error="error">
</canvas>
</template>
<script>
import uCharts from './u-charts.js';
var canvases = {};
export default {
props: {
chartType: {
required: true,
type: String,
default: 'column'
},
opts: {
required: true,
type: Object,
default () {
return null;
},
},
canvasId: {
type: String,
default: 'u-canvas',
},
cWidth: {
default: 375,
},
cHeight: {
default: 250,
},
pixelRatio: {
type: Number,
default: 1,
},
},
mounted() {
this.init();
},
methods: {
init() {
switch (this.chartType) {
case 'column':
this.initColumnChart();
break;
case 'line':
this.initLineChart();
break;
default:
break;
}
},
initColumnChart() {
canvases[this.canvasId] = new uCharts({
$this: this,
canvasId: this.canvasId,
type: 'column',
legend: true,
fontSize: 11,
background: '#FFFFFF',
pixelRatio: this.pixelRatio,
animation: true,
categories: this.opts.categories,
series: this.opts.series,
enableScroll: true,
xAxis: {
disableGrid: true,
itemCount: 4,
scrollShow: true
},
yAxis: {
//disabled:true
},
dataLabel: true,
width: this.cWidth * this.pixelRatio,
height: this.cHeight * this.pixelRatio,
extra: {
column: {
type: 'group',
}
}
});
},
initLineChart() {
canvases[this.canvasId] = new uCharts({
$this: this,
canvasId: this.canvasId,
type: 'line',
fontSize: 11,
legend: true,
dataLabel: false,
dataPointShape: true,
background: '#FFFFFF',
pixelRatio: this.pixelRatio,
categories: this.opts.categories,
series: this.opts.series,
animation: true,
enableScroll: true,
xAxis: {
type: 'grid',
gridColor: '#CCCCCC',
gridType: 'dash',
dashLength: 8,
itemCount: 4,
scrollShow: true
},
yAxis: {
gridType: 'dash',
gridColor: '#CCCCCC',
dashLength: 8,
splitNumber: 5,
min: 10,
max: 180,
format: (val) => {
return val.toFixed(0) + '元'
}
},
width: this.cWidth * this.pixelRatio,
height: this.cHeight * this.pixelRatio,
extra: {
line: {
type: 'straight'
}
}
});
},
// cidcanvas-id,newdata
changeData(cid,newdata) {
canvases[cid].updateData({
series: newdata.series,
categories: newdata.categories
});
},
touchStart(e) {
canvases[this.canvasId].showToolTip(e, {
format: function(item, category) {
return category + ' ' + item.name + ':' + item.data
}
});
canvases[this.canvasId].scrollStart(e);
},
touchMove(e) {
canvases[this.canvasId].scroll(e);
},
touchEnd(e) {
canvases[this.canvasId].scrollEnd(e);
},
error(e) {
console.log(e)
}
},
};
</script>
<style scoped>
.charts {
width: 100%;
height: 100%;
flex: 1;
background-color: #FFFFFF;
z-index: 1;
}
</style>

5662
js_sdk/u-charts/u-charts/u-charts.js

File diff suppressed because it is too large

67
main.js

@ -0,0 +1,67 @@
import Vue from 'vue'
import App from './App'
import store from './store'
import uView from "uview-ui"
Vue.use(uView)
import util from "./utils/util"; //加载js公共函数
import constant from "./utils/constant"; //常量
import config from "./utils/config"; //地址
import reQuest from "./common/http/" //请求函数
import MescrollUni from "@/components/mescroll-uni/mescroll-uni.vue";
Vue.prototype.$gettqimg = function(url) {
return 'https://cs.tour-ma.com/r/cms/www/m/tianqi/' + url + '.png'
}
Vue.prototype.$getimg = function(url) {
return 'https://cs.tour-ma.com/r/cms/www/m/changshu/' + url
}
// Vue.prototype.$getimg = function(url) {
// return 'https://cs.tour-ma.comww/xcx/img/' + url
// }
Vue.prototype.$geturl = function(url) {
return 'https://cs.tour-ma.com' + url
}
Vue.prototype.$geturl6 = function(url) {
return 'https://cs.tour-ma.com/w640' + url
}
Vue.prototype.$geturl480 = function(url) {
return 'https://cs.tour-ma.com/w480' + url
}
Vue.prototype.$geturl4 = function(url) {
return 'https://cs.tour-ma.com/wc400' + url
}
Vue.prototype.$getpage = function(url) {
console.log(url);
uni.navigateTo({
url: url
});
}
Vue.prototype.$getTag = function(tags) {
if(!tags){return}
let arry=tags.split(',').filter(function(e){return e});
return arry;
}
Vue.config.productionTip = false
App.mpType = 'app'
Vue.prototype.$constant = constant;
Vue.prototype.$doGet = reQuest.doGet;
Vue.prototype.$doPost = reQuest.doPost;
Vue.prototype.$util = util;
Vue.prototype.isPlatform = "";//平台判断
Vue.prototype.isLogin = true;//登录判断
Vue.prototype.baseImgUrl = config.baseImgUrl;
Vue.prototype.serverUrl = config.serverUrl;
Vue.component('mescroll-uni', MescrollUni);
const app = new Vue({
store,
...App
})
app.$mount()

77
manifest.json

@ -0,0 +1,77 @@
{
"name" : "changshou_xcx",
"appid" : "__UNI__60B00B9",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "wx4df5fd9a6a5ea123",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true,
"permission" : {
"scope.userLocation" : {
"desc" : "小程序需要获取位置信息"
}
}
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "2"
}

26
other/720sny.vue

@ -0,0 +1,26 @@
<template>
<web-view :src="src"></web-view>
</template>
<script>
import back from '@/components/html/back.vue';
var web = require('@/components/utils/request.js');
export default {
components: {
back
},
data() {
return {
src:''
}
},
onLoad(options) {
this.src = uni.getStorageSync('linkUrl');
},
methods: {
}
}
</script>
<style>
</style>

351
other/allSearch.vue

@ -0,0 +1,351 @@
<template>
<view class="content">
<nav-bar isblack="true" bgColor="rgba(255,255,255,1)" fontColor="#000000" backState="3000"></nav-bar>
<view class="top" :style="{background: 'url('+getUrl('/r/cms/www/m/yushan/wf-ss-banner.png')+') center center no-repeat'}">
<view class="ttypebox">
<!-- 关键字 -->
<view class="ttype ttype100">
<input class="searchIpt" type="text" placeholder="请输入关键字" placeholder-style="color: rgba(255,255,255,0.5);" v-model="searchStr" />
<view class="searchBtn" @click="getData(1)">
<image class="Btnimg" :src="getImg('ra-liebiao-icon-sou.png')" mode="" ></image>
</view>
</view>
</view>
</view>
<view class="main">
<view class="mainlist">
<view class="libox" v-for="(el, ind) in dataall" @click="navto(el.typeId,el.uuid,el.uid)">
<view class="liboxtop" :style="{background:el.logo == '' || el.logo == null? 'url(https://cs.tour-ma.com/r/cms/www/m/changshu/noimg.png) center no-repeat' : 'url(' + getUrl(el.logo) + ') center no-repeat'} ">
<view class="xing" :style="{ width: el.level * 29.6 + 'rpx' }"><image class="img" :src="getImg('ra-liebiao-icon-xing.png')" mode=""></image></view>
</view>
<view class="liboxbot">
<view class="title1">
<view class="name" v-text="el.name"></view>
<view class="xingji" v-show="el.level>0" v-if="el.typeId==1" v-text="el.level + 'A'"></view>
<view class="xingji" v-show="el.scapeType!='未定级'" v-if="el.typeId==199" v-text="el.scapeType"></view>
<view class="xingji" v-show="el.level>0" v-if="el.typeId!=1 && el.typeId!=199" v-text="el.level + '星'"></view>
<view class="price" v-if="el.price!=null&&el.price!=0" >
<view class="rmb"></view>
<view class="pricenum" v-text="el.price"></view>
</view>
</view>
<view class="title2">
<view class="address" v-text="el.address"></view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
var web = require('@/components/utils/request.js');
export default {
data() {
return {
id: 0, //
uid:119,//
nowdata:{
name:'全站搜索',
title:'',
contentImg:''
},
dataall: [],
searchStr: '',
levelname:'等级',
level:'',
scapeTypename:'类型',
scapeType:'',
lat:0,
lng:0
};
},
onLoad(option) {
this.searchStr=option.name;
this.getData();
},
mounted() {
},
methods: {
getUrl(url) {
return 'https://cs.tour-ma.com/'+url;
},
getImg(url) {
return 'https://cs.tour-ma.com/r/cms/www/m/yushan/' + url;
},
navto(typeId,uuid,wid){
if(typeId==1){
uni.navigateTo({
url:'/pages/jqDet?uid='+wid
});
}else if(typeId==2){
uni.navigateTo({
url:'/pages/jqDet?uid='+wid
});
}else if(typeId==3){
uni.navigateTo({
url: '/pages/jqDet?uid='+wid
});
}
},
getData(fl) {
var url = '/wareInfo/list4.jspx';
var para = {
ctgId: 0,
name:this.searchStr
};
var that = this;
web.httpPost(that, url, para, function(res) {
console.log(1111,res.data)
if (res.data.status == 200) {
that.dataall=[];
if(fl==1){
for(var i=0;i<res.data.results.length;i++){
that.dataall.push(res.data.results[i]);
}
}else{
for(var i=0;i<res.data.results.length;i++){
if(res.data.results[i].typeId==1||res.data.results[i].typeId==2||res.data.results[i].typeId==3){
that.dataall.push(res.data.results[i]);
}
}
}
// if(res.data.results[i].typeId==1||res.data.results[i].typeId==2||res.data.results[i].typeId==3){
// that.dataall=res.data.results;
// }
} else {
uni.showToast({
title: '查询失败'
});
}
});
}
}
};
</script>
<style>
.content {
width: 100%;
height: 100%;
position: relative;
}
.top {
width: 100%;
height: 320upx;
background-size: 100% auto !important;
position: relative;
}
.tname {
position: absolute;
width: 300upx;
top: 50%;
left: 50%;
margin-top: -100upx;
margin-left: -150upx;
text-align: center;
}
.tname .tnamezw {
font-size: 40upx;
font-weight: 500;
color: rgba(255, 255, 255, 1);
}
.tname .tnameen {
font-size: 28upx;
font-weight: 500;
color: rgba(255, 255, 255, 0.8);
}
.ttypebox {
display: flex;
justify-content: center;
align-items: center;
width: 92%;
height: 60upx;
position: absolute;
top: 50upx;
left: 4%;
}
.ttype {
width: 30%;
display: flex;
justify-content: space-between;
align-items: center;
border: 1upx solid rgba(255, 255, 255, 0.4);
border-radius: 10upx;
padding: 12upx 20upx;
background-color: rgba(255, 255, 255, 0.3);
margin-right: 10upx;
line-height: 1.5rem;
position: relative;
}
.ttype100{
width: 100%;
}
.ttypebox .ttype:last-child {
margin-right: 0upx;
}
.ttype .txt {
color: rgba(255, 255, 255, 1);
font-size: 25upx;
margin-right: 10upx;
}
.ttype .iconbot {
width: 20upx;
height: 12upx;
}
.iconbot180{
transform: rotate(180deg);
}
.ttype .searchIpt {
font-size: 25upx;
color: rgba(255, 255, 255, 1);
}
.ttype .searchBtn {
width: 60upx;
height: 50upx;
position: relative;
}
.ttype .searchBtn .Btnimg{
width: 26upx;
height: 24upx;
position: absolute;
top: 50%;
left: 50%;
margin-top: -12upx;
margin-left: -13upx;
}
.listbox{
position: absolute;
top: 370upx;
left: 0;
z-index: 100;
border-radius: 0 0 10upx 10upx;
padding: 10upx 4% 10upx 4%;
background-color: rgba(255, 255, 255, 1);
text-align: left;
width: 92%;
box-shadow: 0upx 7upx 20upx -5upx #ccc;
}
.listbox .liname{
padding: 10upx 0;
overflow: hidden;
}
.listbox .liname .litxt{
color: #7b797a;
font-size: 25upx;
}
.listbox .linamenow .litxt{
color: #FB7F60;
float: left;
}
.main {
padding: 7% 4%;
}
.mainlist {
}
.mainlist .libox {
margin-bottom: 7%;
border-radius: 20upx;
overflow: hidden;
box-shadow: 0upx 0upx 15upx 10upx #eee;
}
.libox .liboxtop {
width: 100%;
height: 370upx;
position: relative;
background-size: 100% 100% !important;
object-fit: cover;
}
.liboxtop .xing {
position: absolute;
top: 15upx;
left: 35upx;
overflow: hidden;
}
.liboxtop .xing .img {
width: 148upx;
height: 26upx;
}
.libox .liboxbot {
}
.liboxbot .title1 {
display: flex;
align-items: center;
padding: 20upx 0;
}
.title1 .name {
color: #333333;
font-weight: 600;
font-size: 38upx;
display: flex;
align-items: center;
width: 86%;
}
.title1 .name::before {
content: '';
display: inline-block;
width: 8upx;
height: 48upx;
background: url(https://ys.tour-ma.com/r/cms/www/m/yushan/ra-liebiao-icon-deco.png) no-repeat;
background-size: 100% 100%;
margin-right: 35upx;
}
.title1 .xingji {
width: 44upx;
height: 32upx;
background: url(https://ys.tour-ma.com/r/cms/www/m/yushan/ra-liebiao-icon-deco1.png) no-repeat;
background-size: 100% 100%;
font-size: 20upx;
text-align: center;
color: #fff;
margin-left: 20upx;
padding: 0upx 5upx;
}
.title1 .price {
color: #ff0000;
display: flex;
align-items: baseline;
margin-left: auto;
margin-right: 20upx;
}
.title1 .price .rmb{
font-size: 20upx;
}
.title1 .price .pricenum{
font-size: 40upx;
}
.liboxbot .title2 {
margin-bottom: 35rpx;
padding: 0 20rpx 0 40rpx;
display: flex;
align-items: center;
}
.title2 .address{
color: #808080;
width: 100%;
}
.title2 .distancediv{
color: #808080;
width: 25%;
text-align: right;
}
.title2 .address::before {
content: '';
display: inline-block;
width: 26upx;
height: 26upx;
background: url(https://ys.tour-ma.com/r/cms/www/m/yushan/ra-liebiao-icon-dizhi.png) no-repeat;
background-size: 100% 100%;
margin-right: 10upx;
}
.litxtimg {width: 30upx;height: 30upx;float: right;}
</style>

937
other/components/mx-datepicker/mx-datepicker.vue

@ -0,0 +1,937 @@
<template>
<view v-if="isShow" class="picker" @click="onCancel()">
<!-- 日期选择器 -->
<view v-if="type!='time'" class="picker-modal">
<view class="picker-modal-header">
<view class="picker-icon picker-icon-zuozuo" :hover-stay-time="100" hover-class="picker-icon-active" @click.stop="onSetYear('-1')"></view>
<view class="picker-icon picker-icon-zuo" :hover-stay-time="100" hover-class="picker-icon-active" @click.stop="onSetMonth('-1')"></view>
<text class="picker-modal-header-title">{{title}}</text>
<view class="picker-icon picker-icon-you" :hover-stay-time="100" hover-class="picker-icon-active" @click.stop="onSetMonth('+1')"></view>
<view class="picker-icon picker-icon-youyou" :hover-stay-time="100" hover-class="picker-icon-active" @click.stop="onSetYear('+1')"></view>
</view>
<swiper class="picker-modal-body" :circular="true" :duration="200" :skip-hidden-item-layout="true" :current="calendarIndex"
@change="onSwiperChange">
<swiper-item class="picker-calendar" v-for="(calendar,calendarIndex2) in calendars">
<view class="picker-calendar-view" v-for="(week,index) in weeks">
<view class="picker-calendar-view-item">{{week}}</view>
</view>
<view class="picker-calendar-view" v-for="(date,dateIndex) in calendar" @click.stop="onSelectDate(date,date.statusStyle.color)">
<!-- 背景样式 -->
<view v-show="date.bgStyle.type" :class="'picker-calendar-view-'+date.bgStyle.type" :style="{background: date.bgStyle.background}"></view>
<!-- 正常和选中样式 -->
<view class="picker-calendar-view-item" :style="{opacity: date.statusStyle.opacity, color: date.statusStyle.color2, background: date.statusStyle.background,'flex-direction': 'column'}">
<text>{{date.title}}</text>
<view v-if="url!=''&&date.statusStyle.color2!='#ddd'" :style="{'line-height': '20rpx',color: date.statusStyle.color1,'font-weight':400}">
<text style="font-size: 18rpx;"></text>
<text style="font-size: 24rpx;">{{date.price}}</text>
</view>
</view>
<!-- 小圆点样式 -->
<view class="picker-calendar-view-dot" :style="{opacity: date.dotStyle.opacity, background: date.dotStyle.background}"></view>
<!-- 信息样式 -->
<view v-show="date.tips" class="picker-calendar-view-tips">{{date.tips}}</view>
</view>
</swiper-item>
</swiper>
<view class="picker-modal-footer">
<view class="picker-modal-footer-info">
<block v-if="isMultiSelect">
<view class="picker-display">
<text>{{beginText}}日期</text>
<text class="picker-display-text">{{BeginTitle}}</text>
<view v-if="isContainTime" class="picker-display-link" :hover-stay-time="100" hover-class="picker-display-link-active"
:style="{color}" @click.stop="onShowTimePicker('begin')">{{BeginTimeTitle}}</view>
</view>
<view class="picker-display">
<text>{{endText}}日期</text>
<text class="picker-display-text">{{EndTitle}}</text>
<view v-if="isContainTime" class="picker-display-link" :hover-stay-time="100" hover-class="picker-display-link-active"
:style="{color}" @click.stop="onShowTimePicker('end')">{{EndTimeTitle}}</view>
</view>
</block>
<block v-else>
<view class="picker-display">
<text>当前选择</text>
<text class="picker-display-text">{{BeginTitle}}</text>
<view v-if="isContainTime" class="picker-display-link" :hover-stay-time="100" hover-class="picker-display-link-active"
:style="{color}" @click.stop="onShowTimePicker('begin')">{{BeginTimeTitle}}</view>
</view>
</block>
</view>
<view class="picker-modal-footer-btn">
<view class="picker-btns" :hover-stay-time="100" hover-class="picker-btn-active" @click.stop="onCancel">取消</view>
<view class="picker-btn" :style="{color}" :hover-stay-time="100" hover-class="picker-btn-active" @click.stop="onConfirm">确定</view>
</view>
</view>
</view>
<!-- 时间选择器 -->
<view v-if="showTimePicker" class="picker">
<view class="picker-modal picker-time">
<view class="picker-modal-header">
<text class="picker-modal-header-title">选择日期</text>
</view>
<picker-view class="picker-modal-time" indicator-class="picker-modal-time-item" :value="timeValue" @change="onTimeChange">
<picker-view-column>
<view v-for="(v,i) in 24" :key="i">{{i<10?'0'+i:i}}</view>
</picker-view-column>
<picker-view-column>
<view v-for="(v,i) in 60" :key="i">{{i<10?'0'+i:i}}</view>
</picker-view-column>
<picker-view-column v-if="showSeconds">
<view v-for="(v,i) in 60" :key="i">{{i<10?'0'+i:i}}</view>
</picker-view-column>
</picker-view>
<view class="picker-modal-footer">
<view class="picker-modal-footer-info">
<view class="picker-display">
<text>当前选择</text>
<text class="picker-display-text">{{PickerTimeTitle}}</text>
</view>
</view>
<view class="picker-modal-footer-btn">
<view class="picker-btns" :hover-stay-time="100" hover-class="picker-btn-active" @click.stop="onCancelTime">取消</view>
<view class="picker-btn" :style="{color}" :hover-stay-time="100" hover-class="picker-btn-active" @click.stop="onConfirmTime">确定</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
/**
* 工具函数库
*/
const DateTools = {
/**
* 获取公历节日
* @param date Date对象
*/
getHoliday(date) {
let holidays = {
'0101': '元旦',
'0214': '情人',
'0308': '妇女',
'0312': '植树',
'0401': '愚人',
'0501': '劳动',
'0504': '青年',
'0601': '儿童',
'0701': '建党',
'0801': '建军',
'0903': '抗日',
'0910': '教师',
'1001': '国庆',
'1031': '万圣',
'1224': '平安',
'1225': '圣诞'
};
let value = this.format(date, 'mmdd');
if (holidays[value]) return holidays[value];
return false;
},
/**
* 解析标准日期格式
* @param s 日期字符串
* @return 返回Date对象
*/
parse: s => new Date(s.replace(/(年|月|-)/g, '-').replace(/(日)/g, '')),
/**
* 比较日期是否为同一天
* @param a Date对象
* @param b Date对象
* @return Boolean
*/
isSameDay: (a, b) => a.getMonth() == b.getMonth() && a.getFullYear() == b.getFullYear() && a.getDate() == b.getDate(),
/**
* 格式化Date对象
* @param d 日期对象
* @param f 格式字符串
* @return 返回格式化后的字符串
*/
format(d, f) {
var o = {
"m+": d.getMonth() + 1,
"d+": d.getDate(),
"h+": d.getHours(),
"i+": d.getMinutes(),
"s+": d.getSeconds(),
"q+": Math.floor((d.getMonth() + 3) / 3),
};
if (/(y+)/.test(f))
f = f.replace(RegExp.$1, (d.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(f))
f = f.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return f;
},
/**
* 用于format格式化后的反解析
* @param s 日期字符串
* @param f 格式字符串
* @return 返回Date对象
*/
inverse(s, f) {
var o = {
"y": '',
"m": '',
"d": '',
"h": '',
"i": '',
"s": '',
};
let d = new Date();
if (s.length != f.length) return d;
for (let i in f)
if (o[f[i]] != undefined) o[f[i]] += s[i];
if (o.y) d.setFullYear(o.y.length < 4 ? (d.getFullYear() + '').substr(0, 4 - o.y.length) + o.y : o.y);
o.m && d.setMonth(o.m - 1, 1);
o.d && d.setDate(o.d - 0);
o.h && d.setHours(o.h - 0);
o.i && d.setMinutes(o.i - 0);
o.s && d.setSeconds(o.s - 0);
return d;
},
/**
* 获取日历数组42
* @param date 日期对象或日期字符串
* @param proc 处理日历(和forEach类似)传递一个数组中的item
* @return Array
*/
p(s) {
return s < 10 ? '0' + s : s
},
getCalendar(date, proc) {
let it = new Date(date),
calendars = [];
it.setDate(1);
it.setDate(it.getDate() - ((it.getDay() == 0 ? 7 : it.getDay()) - 1)); //
for (let i = 0; i < 42; i++) {
let tmp = {
dateObj: new Date(it),
title: it.getDate(),
isOtherMonth: it.getMonth() < date.getMonth() || it.getMonth() > date.getMonth()
};
calendars.push(Object.assign(tmp, proc ? proc(tmp) : {}));
it.setDate(it.getDate() + 1);
}
return calendars;
},
/**
* 获取日期到指定的月份1号(不改变原来的date对象)
* @param d Date对象
* @param v 指定的月份
* @return Date对象
*/
getDateToMonth(d, v) {
let n = new Date(d);
n.setMonth(v, 1);
return n;
},
/**
* 把时间数组转为时间字符串
* @param t Array[,,]
* @param showSecinds 是否显示秒
* @return 字符串 :[:]
*/
formatTimeArray(t, s) {
let r = [...t];
if (!s) r.length = 2;
r.forEach((v, k) => r[k] = ('0' + v).slice(-2));
return r.join(':');
}
};
var web = require('@/components/utils/request.js');
export default {
props: {
//
color: {
type: String,
default: 'rgba(255, 162, 0, 1)'
},
// typedatetimetime
showSeconds: {
type: Boolean,
default: false
},
//
value: [String, Array],
//date time datetime range rangetime
type: {
type: String,
default: 'range'
},
//
show: {
type: Boolean,
default: false
},
//
format: {
type: String,
default: ''
},
//
showHoliday: {
type: Boolean,
default: true
},
//
showTips: {
type: Boolean,
default: false
},
// type
beginText: {
type: String,
default: '开始'
},
// type
endText: {
type: String,
default: '结束'
},
//
url: {
type: String,
default: ''
},
//
lineId: {
type: String,
default: ''
}
},
data() {
return {
isShow: false, //
isMultiSelect: false, //
isContainTime: false, //
date: {}, //
weeks: ["一", "二", "三", "四", "五", "六", "日"],
title: '初始化', //
calendars: [
[],
[],
[]
], //
calendarIndex: 1, //
checkeds: [], //
showTimePicker: false, //
timeValue: [0, 0, 0], //
timeType: 'begin', //
beginTime: [0, 0, 0], //
endTime: [0, 0, 0], //
};
},
methods: {
getTmprice(json) { //
if (this.url == '') {
return
}
var list = [];
var ind = 0;
for (var i = 0; i < json.length; i++) {
if (this.title == json[i].title) {
ind = i;
list = json[i].dates
}
}
var url = this.url;
var para = {
startTime: list[0],
endTime: list[41]
}
if(this.url=='front/wares/api/type_price'){
para.typeId=this.lineId;
}else{
para.id=this.lineId;
}
var that = this;
web.httpPost(that, url, para, function(res) {
if(url=='front/wares/api/type_price'){
var tmp = res.data.result.records;
}else{
var tmp = res.data.result.priceList;
}
var json = that.calendars;
for (var i = 0; i < json.length; i++) {
for (var j = 0; j < json[i].length; j++) {
json[i][j].price = 0;
for (var l = 0; l < tmp.length; l++) {
if (j == l) {
json[ind][j].price = tmp[l].price
}
}
}
}
that.calendars = json;
that.$forceUpdate()
})
},
//
setValue(value) {
this.date = new Date();
this.checkeds = [];
this.isMultiSelect = this.type.indexOf('range') >= 0;
this.isContainTime = this.type.indexOf('time') >= 0;
//Date
let parseDateStr = (str) => (this.format ? DateTools.inverse(str, this.format) : DateTools.parse(str));
if (value) {
if (this.isMultiSelect) {
Array.isArray(value) && value.forEach((dateStr, index) => {
let date = parseDateStr(dateStr);
let time = [date.getHours(), date.getMinutes(), date.getSeconds()];
if (index == 0) this.beginTime = time;
else this.endTime = time;
this.checkeds.push(date);
});
} else {
if (this.type == 'time') {
let date = parseDateStr('2019/1/1 ' + value);
this.beginTime = [date.getHours(), date.getMinutes(), date.getSeconds()];
this.onShowTimePicker('begin');
} else {
this.checkeds.push(parseDateStr(value));
if (this.isContainTime) this.beginTime = [
this.checkeds[0].getHours(),
this.checkeds[0].getMinutes(),
this.checkeds[0].getSeconds()
];
}
}
if (this.checkeds.length) this.date = new Date(this.checkeds[0]);
} else {
if (this.isContainTime) {
this.beginTime = [this.date.getHours(), this.date.getMinutes(), this.date.getSeconds()];
if (this.isMultiSelect) this.endTime = [...this.beginTime];
}
this.checkeds.push(new Date(this.date));
}
if (this.type != 'time') this.refreshCalendars(true);
else this.onShowTimePicker('begin');
},
//
onSetYear(value) {
this.date.setFullYear(this.date.getFullYear() + parseInt(value));
this.refreshCalendars(true);
},
//
onSetMonth(value) {
this.date.setMonth(this.date.getMonth() + parseInt(value));
this.refreshCalendars(true);
},
//
onTimeChange(e) {
this.timeValue = e.detail.value;
},
//
onShowTimePicker(type) {
this.showTimePicker = true;
this.timeType = type;
this.timeValue = type == 'begin' ? [...this.beginTime] : [...this.endTime];
},
//
procCalendar(item) {
//
item.statusStyle = {
opacity: 1,
color: item.isOtherMonth ? '#ddd' : '#000',
background: 'transparent',
color1: '#666',
color2:'#ddd'
};
item.bgStyle = {
type: '',
background: 'transparent'
};
item.dotStyle = {
opacity: 1,
background: 'transparent'
};
item.tips = "";
//
if (DateTools.isSameDay(new Date(), item.dateObj)) {
item.statusStyle.color = this.color;
item.statusStyle.color1 = '#666';
item.statusStyle.color2 = this.color;
if (item.isOtherMonth) item.statusStyle.opacity = 0.3;
}else{
var nowdate = new Date();
var isday = item.dateObj.toDateString() === nowdate.toDateString();
if (!isday) {
if (nowdate > item.dateObj) {
item.statusStyle.color2 = '#ddd';
return
}else{
item.statusStyle.color2 = '#222';
}
}
}
//
this.checkeds.forEach(date => {
if (DateTools.isSameDay(date, item.dateObj)) {
item.statusStyle.background = 'linear-gradient(90deg, #FF540B 0%, #FFA200 100%)';
item.statusStyle.color = '#fff';
item.statusStyle.color1 = '#fff';
item.statusStyle.color2 = '#fff';
item.statusStyle.opacity = 1;
if (this.isMultiSelect && this.showTips) item.tips = this.beginText;
}
});
//
if (item.statusStyle.background != this.color) {
let holiday = this.showHoliday ? DateTools.getHoliday(item.dateObj) : false;
if (holiday || DateTools.isSameDay(new Date(), item.dateObj)) {
item.title = holiday || item.title;
item.dotStyle.background = this.color;
if (item.isOtherMonth) item.dotStyle.opacity = 0.2;
}
} else {
item.title = item.dateObj.getDate();
}
//
if (this.checkeds.length == 2) {
if (DateTools.isSameDay(this.checkeds[0], item.dateObj)) { //
item.bgStyle.type = 'bgbegin';
}
if (DateTools.isSameDay(this.checkeds[1], item.dateObj)) { //
if (this.isMultiSelect && this.showTips) item.tips = item.bgStyle.type ? this.beginText + ' / ' + this.endText :
this.endText;
if (!item.bgStyle.type) { //
item.bgStyle.type = 'bgend';
} else {
item.bgStyle.type = '';
}
}
if (!item.bgStyle.type && (+item.dateObj > +this.checkeds[0] && +item.dateObj < +this.checkeds[1])) { //
item.bgStyle.type = 'bg';
item.statusStyle.color = this.color;
item.statusStyle.color1 = '#666';
}
if (item.bgStyle.type) {
item.bgStyle.background = this.color;
item.dotStyle.opacity = 1;
item.statusStyle.opacity = 1;
}
}
},
//
refreshCalendars(refresh = false) {
let date = new Date(this.date);
let before = DateTools.getDateToMonth(date, date.getMonth() - 1);
let after = DateTools.getDateToMonth(date, date.getMonth() + 1);
if (this.calendarIndex == 0) {
if (refresh) this.calendars.splice(0, 1, DateTools.getCalendar(date, this.procCalendar));
this.calendars.splice(1, 1, DateTools.getCalendar(after, this.procCalendar));
this.calendars.splice(2, 1, DateTools.getCalendar(before, this.procCalendar));
} else if (this.calendarIndex == 1) {
this.calendars.splice(0, 1, DateTools.getCalendar(before, this.procCalendar));
if (refresh) this.calendars.splice(1, 1, DateTools.getCalendar(date, this.procCalendar));
this.calendars.splice(2, 1, DateTools.getCalendar(after, this.procCalendar));
} else if (this.calendarIndex == 2) {
this.calendars.splice(0, 1, DateTools.getCalendar(after, this.procCalendar));
this.calendars.splice(1, 1, DateTools.getCalendar(before, this.procCalendar));
if (refresh) this.calendars.splice(2, 1, DateTools.getCalendar(date, this.procCalendar));
}
this.title = DateTools.format(this.date, 'yyyy年mm月');
var tmp = [];
var json = this.calendars;
for (var i = 0; i < json.length; i++) {
tmp.push({
dates: [],
title: DateTools.format(json[i][22].dateObj, 'yyyy年mm月')
})
for (var j = 0; j < json[i].length; j++) {
var calendar = DateTools.format(json[i][j].dateObj, 'yyyy-mm-dd')
tmp[i].dates.push(calendar)
}
}
this.getTmprice(tmp)
},
//
onSwiperChange(e) {
this.calendarIndex = e.detail.current;
let calendar = this.calendars[this.calendarIndex];
this.date = new Date(calendar[22].dateObj); //
this.refreshCalendars();
},
//
onSelectDate(date, color) {
var nowdate = new Date();
var isday = date.dateObj.toDateString() === nowdate.toDateString();
if (!isday) {
if (nowdate > date.dateObj) {
uni.showToast({
title: '不可选取'
});
return
}
}
if (~this.type.indexOf('range') && this.checkeds.length == 2) this.checkeds = [];
else if (!(~this.type.indexOf('range')) && this.checkeds.length) this.checkeds = [];
this.checkeds.push(new Date(date.dateObj));
this.checkeds.sort((a, b) => a - b); //
this.calendars.forEach(calendar => {
calendar.forEach(this.procCalendar); //
});
},
//
onCancelTime() {
this.showTimePicker = false;
this.type == 'time' && this.onCancel();
},
//
onConfirmTime() {
if (this.timeType == 'begin') this.beginTime = this.timeValue;
else this.endTime = this.timeValue;
this.showTimePicker = false;
this.type == 'time' && this.onConfirm();
},
//
onCancel() {
this.$emit('cancel', false);
},
//
onConfirm() {
let result = {
value: null,
date: null
};
//
let defaultFormat = {
'date': 'yyyy-mm-dd',
'time': 'hh:ii' + (this.showSeconds ? ':ss' : ''),
'datetime': ''
};
defaultFormat['datetime'] = defaultFormat.date + ' ' + defaultFormat.time;
let fillTime = (date, timeArr) => {
date.setHours(timeArr[0], timeArr[1]);
if (this.showSeconds) date.setSeconds(timeArr[2]);
};
if (this.type == 'time') {
let date = new Date();
fillTime(date, this.beginTime);
result.value = DateTools.format(date, this.format ? this.format : defaultFormat.time);
result.date = date;
} else {
if (this.isMultiSelect) {
let values = [],
dates = [];
if (this.checkeds.length < 2) return uni.showToast({
icon: 'none',
title: '请选择两个日期'
});
this.checkeds.forEach((date, index) => {
let newDate = new Date(date);
if (this.isContainTime) {
let time = [this.beginTime, this.endTime];
fillTime(newDate, time[index]);
}
values.push(DateTools.format(newDate, this.format ? this.format : defaultFormat[this.isContainTime ?
'datetime' : 'date']));
dates.push(newDate);
});
result.value = values;
result.date = dates;
} else {
let newDate = new Date(this.checkeds[0]);
if (this.isContainTime) {
newDate.setHours(this.beginTime[0], this.beginTime[1]);
if (this.showSeconds) newDate.setSeconds(this.beginTime[2]);
}
result.value = DateTools.format(newDate, this.format ? this.format : defaultFormat[this.isContainTime ?
'datetime' : 'date']);
result.date = newDate;
}
}
this.$emit('confirm', result);
}
},
computed: {
BeginTitle() {
let value = '未选择';
if (this.checkeds.length) value = DateTools.format(this.checkeds[0], 'yy-mm-dd');
return value;
},
EndTitle() {
let value = '未选择';
if (this.checkeds.length == 2) value = DateTools.format(this.checkeds[1], 'yy-mm-dd');
return value;
},
PickerTimeTitle() {
return DateTools.formatTimeArray(this.timeValue, this.showSeconds);
},
BeginTimeTitle() {
return this.BeginTitle != '未选择' ? DateTools.formatTimeArray(this.beginTime, this.showSeconds) : '';
},
EndTimeTitle() {
return this.EndTitle != '未选择' ? DateTools.formatTimeArray(this.endTime, this.showSeconds) : '';
}
},
watch: {
show(newValue, oldValue) {
newValue && this.setValue(this.value);
this.isShow = newValue;
},
value(newValue, oldValue) {
setTimeout(() => {
this.setValue(newValue);
}, 0);
}
}
}
</script>
<style lang="scss" scoped>
$z-index: 100;
$cell-spacing: 20upx;
$calendar-size: 630upx;
$calendar-item-size: 100upx;
.picker {
position: fixed;
z-index: 999;
background: rgba(0, 0, 0, 0.5);
left: 0;
top: 0;
width: 100%;
height: 100%;
font-size: 28upx;
&-btn {
padding: $cell-spacing*0.5 $cell-spacing*1.5;
border-radius: 12upx;
color: #fff !important;
background: linear-gradient(90deg, #FF540B 0%, #FFA200 100%);
}
&-btns {
padding: $cell-spacing*0.5 $cell-spacing;
border-radius: 12upx;
color: #666;
background: rgba(0, 0, 0, .1);
}
&-display {
color: #666;
&-text {
color: #000;
margin: 0 $cell-spacing*0.5;
}
&-link {
display: inline-block;
&-active {
background: rgba(0, 0, 0, .1);
}
}
}
&-time {
width: $calendar-size - 80upx !important;
left: ((750upx - $calendar-size) / 2 + 40upx) !important;
}
&-modal {
background: #fff;
position: absolute;
bottom: 0%;
left: 0;
width: 750rpx;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.1);
border-radius: 20rpx 20rpx 0 0;
&-header {
text-align: center;
line-height: 100upx;
font-size: 32upx;
&-title {
display: inline-block;
width: 40%;
}
.picker-icon {
display: inline-block;
line-height: 50upx;
width: 50upx;
height: 50upx;
border-radius: 50upx;
text-align: center;
margin: 10upx;
background: #fff;
font-size: 36upx;
&-active {
background: rgba(0, 0, 0, .1);
}
}
}
&-body {
width: 750rpx !important;
height: 700rpx !important;
position: relative;
padding-bottom: 10rpx;
}
&-time {
width: 100%;
height: 180upx;
text-align: center;
line-height: 60upx;
}
&-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: $cell-spacing;
&-info {
flex-grow: 1;
}
&-btn {
flex-shrink: 0;
display: flex;
width: 240rpx;
justify-content: space-between;
}
}
}
&-calendar {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
flex-wrap: wrap;
&-view {
position: relative;
width: 105rpx;
height: $calendar-item-size;
text-align: center;
&-bgbegin,
&-bg,
&-bgend,
&-item,
&-dot,
&-tips {
position: absolute;
transition: .2s;
}
&-bgbegin,
&-bg,
&-bgend {
opacity: .15;
height: 80%;
}
&-bg {
left: 0;
top: 10%;
width: 100%;
}
&-bgbegin {
border-radius: $calendar-item-size 0 0 $calendar-item-size;
top: 10%;
left: 10%;
width: 90%;
}
&-bgend {
border-radius: 0 $calendar-item-size $calendar-item-size 0;
top: 10%;
left: 0%;
width: 90%;
}
&-item {
left: 5%;
top: 5%;
width: 90%;
height: 90%;
border-radius: 10rpx;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 35rpx;
line-height: 40rpx;
}
&-dot {
right: 10%;
top: 10%;
width: 12upx;
height: 12upx;
border-radius: 12upx;
}
&-tips {
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: #4E4B46;
color: #fff;
border-radius: 12upx;
padding: 10upx 20upx;
font-size: 24upx;
width: max-content;
margin-bottom: 5px;
pointer-events: none;
&:after {
content: "";
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 0;
border-style: solid;
border-width: 5px 5px 0 5px;
border-color: #4E4B46 transparent transparent transparent;
}
}
}
}
}
@font-face {
font-family: "mxdatepickericon";
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAMYAAsAAAAACBgAAALMAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDIgqDRIJiATYCJAMUCwwABCAFhG0HSRvfBsg+QCa3noNAyAQ9w6GDvbwpNp2vloCyn8bD/x+y+/5qDhtj+T4eRVEcbsCoKMFASzCgLdDkmqYDwgxkWQ6YH5L/YnppOlLEjlnter43YRjU7M6vJ3iGADVAgJn5kqjv/wEii23T86UsAQT+04fV+o97VTMx4PPZt4DlorLXwIQiGMA5uhaVrBWqGHfQXcTEiE+PE+g2SUlxWlLVBHwUYFMgrgwSB3wstTKSGzqF1nOyiGeeOtNjV4An/vvxR58PSc3AzrMViyDvPo/7dVEUzn5GROfIWAcU4rLXfMFdhte56y4We9gGNEVIezkBOOaQXUrbTf/hJVkhGpDdCw7dSOEzByMEn3kIic98hMxnAfeFPKWCbjRcA148/HxhCEkaA94eGWFaGolsblpaWz8/Po2WVuNHh1fmBpZHIpqal9fOjizhTteY+RZ9rv02I/pq0W6QVH3pSncBz3m55r9ZIPycHfmenvxe4uyutIgfT5u4bgkDusl9gcF0rnfnz+b2NpSaQWBFeu8GIL1xQj5AH/6FAsEr/50F28e/gA9ny6KjLrxIp0TE+UucmQOl5AFNLXkzZufWamWHYEI39PEP2If97CMdm51N6DSmIekwAVmneXTBr0PVYx+aTgfQbU3p+R4jKHdRurBq0oEw6AKSfm+QDbpGF/w3VOP+oBnMHbqdx409FjP4RRHHkAj5IWgQiBUjHfMTuQ1Icpg5avI4sQVRu8EHdWptM1aKrIjuscfeL+kZwxBTYoElztOQ2UygjRIjEphaZsyWodHgvm9SC8QC/JygEA6DiCDeEMhAQFhhOpvxa/18A0TiYMahIy0L2hYIZWeYH9JR085Al4qts1re5St2/SR6DINBGEVYQCWOETHDMAHZ+pcZIQJGTV4RtMmg8UbhuWL1+VLLA2RFHYC71kiRo0SNpjwQh8pj2EFU3oTNmS1WqgIA') format('woff2');
}
.picker-icon {
font-family: "mxdatepickericon" !important;
}
.picker-icon-you:before {
content: "\e63e";
}
.picker-icon-zuo:before {
content: "\e640";
}
.picker-icon-zuozuo:before {
content: "\e641";
}
.picker-icon-youyou:before {
content: "\e642";
}
</style>

54
other/components/tags-list.vue

@ -0,0 +1,54 @@
<template>
<view class="tags-container">
<!-- <view class="tags" v-for="(tag,ind) in tags" v-if="ind<3" :key="tag">{{tag}}</view> -->
<u-tag v-if="index<3" size="mini" v-for="(item,index) in $getTag(list)" class="bq-list" border-color="#ffffff00" :text="item" bg-color="#ffffff00" color="#2BAD56"/>
</view>
</template>
<script>
export default {
components: {},
props: {
list: {
type: String,
default: ""
}
},
data() {
return {
}
},
onLoad() {},
mounted() {
},
methods: {},
}
</script>
<style lang="scss" scoped>
.bq-list{
background: #E9FBF7;
border-radius: 6rpx;
font-size: 22rpx;
font-family: Microsoft YaHei;
font-weight: 400;
color: #2BAD56;
margin-right: 6rpx;
}
.tags-container {
display: flex;
align-items: center;
margin-top: 15rpx;
.tags {
background: #e9fbf7;
border: 1px solid #b2dbbd;
border-radius: 6rpx;
padding: 6rpx 8rpx 7rpx 8rpx;
font-size: 22rpx;
font-family: Microsoft YaHei;
font-weight: 400;
color: #2bad56;
margin-right: 8rpx;
}
}
</style>

340
other/consulting.vue

@ -0,0 +1,340 @@
<template>
<view class="centent">
<back :info="info"></back>
<view :style="{top:statusBarHeight+'px'}" class="cityicon" ></view>
<view class="topbg">
<view class="bgjb">
<image :src="$getimg('lx-jq.png')"></image>
</view>
<view class="bgjb-jb">
<image :src="$getimg('djcp-bg-jb.png')"></image>
</view>
</view>
<view class="tbb">
<view class="tbboxs" v-for="(el,ind) in allData" :key="el.id" @click="go(el.tel)">
<view class="tboxbot">
<view class="tbtitle">
<view class="tbtitle-il">
<image :src="$getimg('img-iphone.png')" mode="" class="il-img"></image>
</view>
<view class="tbname">
<view class="tbname-l">{{el.name}}</view>
<view class="tbname-k">{{el.tel}}</view>
</view>
</view>
</view>
</view>
</view>
<!-- <view class="szra1">
<view class="list" v-for="(el,ind) in alldata[xwwfind].list" :style="{'margin-right':ind%2==0?'0':'20rpx'}"
v-if="ind>0" @click="$getpage('/pagesys/index/foodCard?uid='+el.uid)">
<view class="liimg">
<image class="szra_img" :src="$geturl(el.logo)" lazy-load="true" mode="aspectFill"></image>
</view>
<view class="sztitle text_overflow">{{el.name}}</view>
<view class="sztitle text_overflow dsadk">{{el.address}}</view>
</view>
</view> -->
</view>
</template>
<script>
import back from '@/components/html/back.vue';
var web = require('@/components/utils/request.js');
export default {
components: {
back
},
data() {
return {
// city:'720',
statusBarHeight: 0,
info: {
color: true,
num: false
},
id: 97,
first: 0,
count: 50,
lmdata: [],
xwwfind: 0,
allData:[]
}
},
onLoad(options) {
this.getTitles();
},
onReady() {
//
this.statusBarHeight = uni.getSystemInfoSync()['statusBarHeight'];
},
onPageScroll(e) {
if (e.scrollTop > 50 && !this.info.num) {
this.info.num = true;
}
if (e.scrollTop < 50 && this.info.num) {
this.info.num = false;
}
},
// onShow(object) { //
// this.getdata2();
// },
methods: {
geturl(url) {
return 'http://cscmp.tour-ma.com' + url;
},
ckxwwfind(ind, id) {
this.xwwfind = ind;
},
getTitles() {
var url = '/addressBook.jspx';
var para = {
unityTypeId:5
};
var that = this;
web.httpGet(that, url, para, function(res) {
let json = res.data.data[0].link;
that.allData = json;
console.log(that.allData)
// that.checkTitleData(0)
// that.nowInfo = [];
// for (var i = 0; i < json.length; i++) {
// if(json[i].addressBookLists.length!=0){
// for(var k=0;k<json[i].addressBookLists.length;k++){
// that.nowInfo.push(json[i].addressBookLists[k]);
// }
// }
// }
});
},
go(phone) {
uni.makePhoneCall({
//
phoneNumber: phone,
//
success: (res) => {
console.log('调用成功!')
},
//
fail: (res) => {
console.log('调用失败!')
}
});
}
}
}
</script>
<style lang="scss">
$url:'https://cs.tour-ma.com/r/cms/www/m/changshu/';
.cityicon{
width: calc(100% - 167rpx);
font-size: 48upx;
color: #FFFFFF;
left: 80upx;
display: flex;
align-items: center;
position: absolute;
z-index: 9999;
height: 80rpx;
justify-content: center;
}
.centent {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 750upx;
overflow-y: scroll;
.topbg {
width: 100%;
position: relative;
.bgjb {
width: 100%;
height: 500rpx;
position: absolute;
z-index: -2;
image {
width: 100%;
height: 500rpx;
}
}
.bgjb-jb {
position: absolute;
top: 300rpx;
z-index: -1;
width: 100%;
image {
width: 100%;
height: 400rpx;
}
}
}
.tbb{
width: 100%;
height: 100%;
.tbboxs:nth-child(1) {
margin-top: 400rpx;
}
}
.tbboxs {
max-width: 690upx;
height: 160upx;
margin: auto;
margin: 20upx auto 5%;
width: 90%;
position: relative;
background-color: #fff;
border-radius: 14rpx;
.tboxbot {
width: 100%;
height: 100%;
position: relative;
background-size: 100% 100% !important;
box-shadow: 0rpx 0rpx 20rpx 0rpx rgba(121, 104, 77, 0.2);
/* position: absolute; */
/* top: 72rpx; */
/* left: 50%; */
/* margin-left: -47%; */
border-radius: 20rpx;
overflow: hidden;
.bot-img{
width: 60rpx;
height: 60rpx;
position: absolute;
top: 20rpx;
right: 20rpx;
}
.tbtitle {
width: 100%;
height: 100%;
position: absolute;
left: 0upx;
bottom: 0upx;
display: flex;
align-items: center;
.tbtitle-il{
background: rgba(98, 212, 166, 1);
border-radius: 50%;
width: 50rpx;
height: 50rpx;
padding: 30rpx;
margin: 0 30rpx 0 50rpx;
.il-img{
width: 100%;
height: 100%;
}
}
.tbname {
// font-size: 36upx;
// font-weight: bold;
.tbname-l{
font-size: 36upx;
font-weight: 400;
color: #444;
margin-bottom: 10rpx;
}
.tbname-k{
font-size: 36upx;
font-weight: bold;
// color: #CCCCCC;
}
}
.tbinfo {
width: 90%;
font-size: 24upx;
font-weight: 500;
line-height: 1.4;
position: absolute;
top: 120rpx;
left: 30rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: flex;
// align-items: center;
&::before {
content: '';
display: inline-block;
width: 24rpx;
height: 28rpx;
margin-right: 10rpx;
background: url($url+'icon-add-da.png') no-repeat;
background-size: 100% 100% !important;
}
}
}
}
}
// .szra1 {
// width: 90%;
// margin: 40upx auto;
// display: flex;
// flex-direction: row;
// flex-wrap: wrap;
// .list {
// width: 48%;
// margin-bottom: 40upx;
// margin-right: 15rpx;
// .liimg {
// margin-bottom: 20upx;
// .szra_img {
// max-width: 360upx;
// width: 100%;
// height: 210upx;
// border-radius: 8upx;
// }
// }
// .sztitle {
// font-size: 32upx;
// font-weight: 600;
// color: #333333;
// margin-bottom: 20upx;
// overflow: hidden;
// text-overflow: ellipsis;
// white-space: nowrap;
// }
// .dsadk {
// font-size: 24rpx;
// font-weight: 500 !important;
// line-height: 1.6;
// &::before {
// content: '';
// display: inline-block;
// width: 24rpx;
// height: 26rpx;
// margin-right: 10rpx;
// background: url($url+'icon-add-xiao.png') no-repeat;
// background-size: 100% 100% !important;
// }
// }
// }
// }
}
</style>

248
other/detailList.vue

@ -0,0 +1,248 @@
<template>
<view class="content">
<back :info="info"></back>
<image class="header-image" :src="getHeaderImg()"></image>
<view class="zixun">
<!-- <view class="zixun-title">
<view v-for="(el,ind) in title" :class="['zixun-title-name',nowId==el.id?'zixun-title-active':'']" @click="getDatas_xw(el.id)">{{el.name}}</view>
</view> -->
<view class="zixun-cont" v-for="el in xwlist" @click="goto('./public?id='+el.id)">
<view class="zixun-cont-left">
<view class="zixun-cont-left-title">
{{el.title}}
</view>
<view class="zixun-cont-left-bq">
<u-tag v-if="index<3" v-for="(item,index) in $getTag(el.channel)" class="zixun-cont-left-bq-list" border-color="#ffffff00" :text="item" bg-color="#ffffff00" color="#2BAD56"/>
</view>
<view class="zixun-cont-left-time" v-if="el.releaseDate !=''">{{(el.releaseDate).substr(0,19)}}</view>
</view>
<view class="zixun-cont-right">
<u-image width="310rpx" height="206rpx" border-radius="10rpx" :src="el.typeImg==''?$getimg('lx-xwt.png'):$geturl(el.typeImg)"></u-image>
<view class="zixun-cont-right-nums">
<image class="zixun-cont-right-nums-icon" :src="$getimg('icon-gd-dz.png')"></image>
<view class="zixun-cont-right-nums-num" style="margin-right: 36rpx;">{{el.ups}}</view>
<image class="zixun-cont-right-nums-icon" :src="$getimg('icon-gd-ll.png')"></image>
<view class="zixun-cont-right-nums-num">{{el.views}}</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
var web = require('@/components/utils/request.js');
// import pingjia from '../../components/utils/zixunpj.vue';
export default {
components: {
// pingjia
},
data() {
return {
id: 75,
infoid: 0,
istop: false,
nowId:0,
xwlist:[],
pageNo:1,
totalPage:1
};
},
onLoad(option) {
// this.nowId= option.id;
// this.id = option.id;
// this.infoid = option.id;
this.getDatas_xw(this.id);
},
onReachBottom() {
if(this.totalPage>this.pageNo){
this.pageNo++;
this.getDatas_xw(this.nowId)
}
},
methods: {
getHeaderImg(){
let imgurl='https://cs.tour-ma.com/r/cms/www/m/changshu/lx-xwt.png'
return imgurl;
},
getDatas_xw(id){
if(this.nowId!=id){
this.xwlist=[];
this.pageNo=1;
this.totalPage=1;
}
this.nowId=id;
var url = 'json/content_list.jspx?channelIds=' + id;
var para={
"pageNo": this.pageNo,
"pageSize": 5,
"channelId":id
}
var that = this;
web.httpPost(that,url,para,function(res){
let records=res.data;
let pages=res.data;
if(that.pageNo==1){
that.xwlist=records;
}else{
that.xwlist=that.xwlist.concat(records)
}
that.totalPage=pages;
})
},
goto(url){
uni.navigateTo({
url:url
})
},
}
};
</script>
<style lang="scss">
@import '@/components/css/animate.min.css';
.content {
overflow: scroll;
width: 750upx;
background-size: 100% 100%;
position: relative;
top: 0;
left: 0;
.header-image{
width: 750rpx;
height: 410rpx;
}
.zixun{
width: 750rpx;
background: #fff;
border-radius: 30rpx 30rpx 0rpx 0rpx;
display: flex;
flex-direction: column;
align-items: center;
padding-bottom: 30rpx;
&-title{
width: 750rpx;
border-radius: 30rpx 30rpx 0rpx 0rpx;
height: 114rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
border-bottom: 1rpx solid #00000012;
&-active:after{
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 140rpx;
height: 4rpx;
background: #04BB6D;
}
&-active{
color: #04BB6D !important;
position: relative;
}
&-name{
width: 140rpx;
height: 114rpx;
line-height: 114rpx;
text-align: center;
font-size: 32rpx;
font-family: PingFang SC;
font-weight: 400;
color: #999999;
margin:0 40rpx;
}
}
&-cont{
width: 690rpx;
display: flex;
flex-direction: row;
justify-content: space-between;
border-bottom: 1rpx solid #E3E3E3;
padding: 34rpx 0 24rpx;
&-left{
width: 350rpx;
display: flex;
flex-direction: column;
align-items: flex-start;
&-title{
font-size: 32rpx;
font-family: Microsoft YaHei;
font-weight: 400;
color: #333333;
line-height: 45rpx;
height: 130rpx;
text-overflow: -o-ellipsis-lastline;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
line-clamp: 3;
-webkit-box-orient: vertical;
}
&-bq{
margin-top: 24rpx;
display: flex;flex-direction: row;
align-items: center;
&-list{
background: #E9FBF7;
border: 1rpx solid #B2DBBD;
border-radius: 6rpx;
font-size: 22rpx;
font-family: Microsoft YaHei;
font-weight: 400;
color: #2BAD56;
margin-right: 6rpx;
}
}
&-time{
font-size: 26rpx;
font-family: Microsoft YaHei;
font-weight: 400;
color: #999999;
line-height: 26rpx;
margin-top: 35rpx;
}
}
&-right{
width: 310rpx;
display: flex;
flex-direction: column;
align-items: flex-end;
&-logo{
width: 310rpx;
height: 206rpx;
border-radius: 10rpx;
}
&-nums{
display: flex;
flex-direction: row;
align-items: center;
margin-top:28rpx;
line-height: 28rpx;
&-icon{
width: 30rpx;
height: 28rpx;
margin-right: 13rpx;
}
&-num{
font-size: 24rpx;
font-family: DIN2014;
font-weight: 600;
color: #333333;
}
}
}
&:last-child{
margin-bottom: 80rpx;
}
}
}
}
</style>

373
other/feiyi.vue

@ -0,0 +1,373 @@
<template>
<view class="centent">
<view class="centent-name">
<view class="name-name">非遗推荐</view>
<view class="name-title">非遗精选</view>
</view>
<view class="fkiml">
<view :class="['xwwfbox',istop?'toplm':'follm']" id="lmid">
<view :class="['xwwfinds',xwwfind==index?'act':'']" v-for="(item,index) in djlist"
@click="ckxwwfind(index)">
{{item.name}}
</view>
</view>
</view>
<view class="main">
<view class="mainlist">
<view class="libox" v-for="(el, ind) in djlist[xwwfind].list" @click="$getpage('/other/foodCard?id='+el.id)">
<view class="name">{{ el.title}}</view>
<!-- <view class="liboxtop"
:style="{background:el.typeImg == '' || el.typeImg == null? 'url(https://cs.tour-ma.com/r/cms/www/m/changshu/noimg.png) center no-repeat' : 'url(' + $geturl(el.typeImg) + ') center no-repeat'}"> -->
<!-- <view class="xing" :style="{ width: el.level * 29.6 + 'rpx' }">
<image class="img" :src="getImg('ra-liebiao-icon-xing.png')" mode=""></image>
</view> -->
<!-- </view> -->
<image class="liboxtop" :src="el.typeImg == '' || el.typeImg == null? 'https://cs.tour-ma.com/r/cms/www/m/changshu/noimg.png':$geturl480(el.typeImg)" mode=""></image>
<view class="liboxbot">
<view class="title1 text_overflow_2" v-html="el.txt"></view>
<view class="lbrli">
<view class="lbrliind">{{el.cateType}}</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
var web = require('@/components/utils/request.js');
import back from '@/components/html/back.vue';
export default {
components: {
back
},
data() {
return {
id: 76, //
info: {
color: true
},
istop: false,
nowdata: {},
zslist: [{
list: []
}],
first: 0,
count: 50,
xwwfind: 0,
djlist: [{
name: '全部',
ind: 0,
list: []
},
{
name: '国家级',
ind: 1,
list: []
},
{
name: '江苏省级',
ind: 2,
list: []
},
{
name: '苏州市级',
ind: 3,
list: []
},
{
name: '常熟市级',
ind: 4,
list: []
}
],
}
},
onReachBottom() {
this.first += 50;
this.getDatalist();
},
onPageScroll(e) {
if (e.scrollTop > this.lmtop) {
this.istop = true;
}
if (e.scrollTop < this.lmtop) {
this.istop = false;
}
},
onReady() {
var that = this;
const query = uni.createSelectorQuery().in(this);
query.select('#lmid').boundingClientRect(data => {
that.lmtop = data.top;
console.log(data)
}).exec();
},
mounted() {
this.getData();
this.getDatalist();
},
methods: {
ckxwwfind(ind) {
this.xwwfind = ind;
this.getDatalist();
},
navto(id) {
uni.navigateTo({
url: url
})
},
getData() {
var url = '/json/channel_get.jspx?id=' + this.id;
var para = {};
var that = this;
web.httpGejqr(that, url, para, function(res) {
if (res.statusCode == 200) {
that.nowdata = res.data;
} else {
uni.showToast({
title: '查询失败'
});
}
})
},
getDatalist() {
uni.showToast({
title: '加载中...',
icon: "loading",
duration: 100000
});
var url = '/json/content_list.jspx?channelIds=' + this.id + '&first=' + this.first + '&count=' + this
.count;
var para = {};
var that = this;
web.httpGejqr(that, url, para, function(res) {
uni.hideToast();
if (res.statusCode == 200) {
if (res.data.length == 0) {
return
}
var json = [];
json=res.data;
var tmp = that.djlist;
if (that.xwwfind == 0) {
for (var i = 0; i < json.length; i++) {
tmp[0].list.push(json[i]);
}
that.djlist = tmp;
}
if (that.xwwfind != 0) {
for (var i = 0; i < json.length; i++) {
for(var j=0;j<tmp.length;j++){
if(json[i].cateLevel == tmp[j].name){
tmp[j].list.push(json[i]);
}
}
}
that.djlist = tmp;
}
} else {
uni.showToast({
title: '查询失败'
});
}
})
},
toOther() {
uni.navigateToMiniProgram({
appId: 'wx8ece732cc3a87e0f',
path: 'pages/index/index',
envVersion: 'release',
extraData: {},
success(res) {
console.log("chenggong");
},
fail(e) {
console.log(e);
}
})
}
}
}
</script>
<style lang="scss">
.centent {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 750upx;
overflow-y: scroll;
position: relative;
// background: #274B6D;
.centent-name {
width: 93%;
margin: 30rpx auto 30rpx;
display: flex;
align-items: center;
.name-name {
font-size: 48rpx;
font-family: Source Han Sans CN;
font-weight: 500;
color: #5a5a5a;
line-height: 58rpx;
}
.name-title {
font-size: 32rpx;
font-family: Source Han Sans CN;
font-weight: 500;
color: #8b8b8b;
line-height: 28rpx;
margin-left: 20rpx;
}
}
.follm {
position: absolute;
left: 0rpx;
top: 14rpx;
}
.toplm {
position: fixed;
top: 0;
left: 0;
background: #F2F2F2;
height: 80rpx;
z-index: 9;
}
.fkiml {
width: 93%;
position: relative;
height: 80rpx;
margin: 0 auto;
}
.xwwfbox {
width: 100%;
display: flex;
flex-direction: row;
white-space: nowrap;
align-items: center;
overflow-x: scroll;
overflow-y: hidden;
justify-content: center;
// margin-bottom: 16upx;
// padding-bottom: 20upx;
.xwwfinds {
background: #E6E7E8;
border-radius: 30upx;
font-size: 28upx;
font-weight: 400;
color: #666666;
padding: 10upx 24upx;
margin-right: 20upx;
&:first-child {
margin-left: 120upx;
}
}
.act {
background: linear-gradient(0deg, #3269F7 0%, #4A9AF9 100%);
color: #FFFFFF;
}
}
.main {
padding: 7% 4%;
}
.mainlist .libox {
margin-bottom: 7%;
border-radius: 20upx;
overflow: hidden;
box-shadow: 0upx 0upx 15upx 10upx #eee;
background: #fff;
}
.libox .name {
color: #333333;
font-weight: 500;
font-size: 38upx;
display: flex;
align-items: center;
padding: 28rpx;
&::after {
content: '';
display: inline-block;
width: 60rpx;
height: 40rpx;
background: url(https://cs.tour-ma.com/r/cms/www/m/changshu/youjt.png) no-repeat;
background-size: 100% 100%;
position: absolute;
right: 49rpx;
}
}
.libox .liboxtop {
width: 93%;
height: 370upx;
position: relative;
background-size: 100% 100%;
object-fit: cover;
margin: 0 auto;
border-radius: 14rpx;
}
.liboxtop .xing {
position: absolute;
top: 15upx;
left: 35upx;
overflow: hidden;
}
.liboxtop .xing .img {
width: 148upx;
height: 26upx;
}
.liboxbot .title1 {
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 400;
opacity: 0.8;
margin: 28rpx;
width: 92%;
}
.lbrli {
display: flex;
white-space: nowrap;
overflow-x: scroll;
overflow-y: hidden;
padding: 0 28rpx 40rpx;
.lbrliind {
font-size: 24upx;
font-weight: 500;
color: #33AC59;
border: 2upx solid #B3DBBE;
border-radius: 6upx;
padding: 5upx 10upx;
margin-right: 16upx;
background: #E9FBF7;
}
}
}
</style>

235
other/festival.vue

@ -0,0 +1,235 @@
<template>
<view class="centent">
<view class="main-top">
<image :src="$getimg('jq-bg.jpg')" mode="" class="top-pos"></image>
</view>
<view class="main">
<view class="mainlist">
<view class="libox" v-for="(el, ind) in zslist" @click="$getpage('./public?id='+el.id)">
<view class="liboxtop"
:style="{background:el.typeImg == '' || el.typeImg == null? 'url(https://cs.tour-ma.com/r/cms/www/m/changshu/noimg.png) center no-repeat' : 'url(' + $geturl(el.typeImg) + ') center no-repeat'}">
</view>
<view class="liboxbot">
<view class="name">{{ el.title}}</view>
<!-- <view class="title1 text_overflow_2" v-html="el.txt"></view> -->
</view>
</view>
</view>
</view>
</view>
</template>
<script>
var web = require('@/components/utils/request.js');
import back from '@/components/html/back.vue';
export default {
components: {
back
},
data() {
return {
id: 93, //
info: {
color: true
},
istop: false,
nowdata: {},
zslist: [],
first: 0,
count: 50,
xwwfind: 0,
}
},
onReachBottom() {
this.first += 50;
this.getDatalist();
},
onPageScroll(e) {
if (e.scrollTop > this.lmtop) {
this.istop = true;
}
if (e.scrollTop < this.lmtop) {
this.istop = false;
}
},
mounted() {
this.getData();
this.getDatalist();
},
methods: {
ckxwwfind(ind) {
this.xwwfind = ind;
this.getDatalist();
},
navto(id) {
uni.navigateTo({
url: url
})
},
getData() {
var url = '/json/channel_get.jspx?id=' + this.id;
var para = {};
var that = this;
web.httpGejqr(that, url, para, function(res) {
if (res.statusCode == 200) {
that.nowdata = res.data;
} else {
uni.showToast({
title: '查询失败'
});
}
})
},
getDatalist() {
uni.showToast({
title: '加载中...',
icon: "loading",
duration: 100000
});
var url = '/json/content_list.jspx?channelIds=' + this.id + '&first=' + this.first + '&count=' + this
.count;
var para = {};
var that = this;
web.httpGejqr(that, url, para, function(res) {
uni.hideToast();
if (res.statusCode == 200) {
if (res.data.length == 0) {
return
}
var json = [];
that.zslist=[];
json=res.data;
for (var i = 0; i < json.length; i++) {
that.zslist.push(json[i]);
}
} else {
uni.showToast({
title: '查询失败'
});
}
})
},
toOther() {
uni.navigateToMiniProgram({
appId: 'wx8ece732cc3a87e0f',
path: 'pages/index/index',
envVersion: 'release',
extraData: {},
success(res) {
console.log("chenggong");
},
fail(e) {
console.log(e);
}
})
}
}
}
</script>
<style lang="scss">
.centent {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 750upx;
overflow-y: scroll;
position: relative;
background: #F5F5F5;
// background: #274B6D;
.main-top {
width: 750rpx;
height: 330rpx;
position: relative;
.top-pos {
width: 100%;
height: 100%;
}
}
.follm {
position: absolute;
left: 0rpx;
top: 14rpx;
}
.toplm {
position: fixed;
top: 0;
left: 0;
background: #F2F2F2;
height: 80rpx;
z-index: 9;
}
.main {
padding: 4% 4%;
}
.mainlist .libox {
margin-bottom: 7%;
// border-radius: 20upx;
overflow: hidden;
// box-shadow: 0upx 0upx 15upx 10upx #eee;
background: #fff;
}
.libox .name {
color: #333333;
font-weight: 500;
font-size: 38upx;
display: flex;
align-items: center;
padding: 28rpx;
justify-content: center;
}
.libox .liboxtop {
width: 100%;
height: 370upx;
position: relative;
background-size: 100% 100%;
object-fit: cover;
margin: 0rpx auto ;
// border-radius: 14rpx;
}
.liboxtop .xing {
position: absolute;
top: 15upx;
left: 35upx;
overflow: hidden;
}
.liboxtop .xing .img {
width: 148upx;
height: 26upx;
}
.liboxbot .title1 {
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 400;
opacity: 0.8;
margin: 28rpx;
}
}
</style>

305
other/foodCard.vue

@ -0,0 +1,305 @@
<template>
<view class="content">
<!-- <view class="bgtoptxt" @click="$getpage('/pages/index/foodList')">查看全部</view> -->
<view class="bgtop" v-if="xllist.midPic!=''" :style="{background: 'url('+$geturl(xllist.midPic)+') no-repeat'}"></view>
<!-- <view class="bgtop" v-if="xllist.midPic==''" :style="{background: 'url('+getImg3('960noimg.png')+') no-repeat'}"></view> -->
<image v-if="xllist.channel_id==117||xllist.channel_id==116" @click="toOther()" class="yudingimg" :src="$getimg('lijigoumai.png')" mode="aspectFill"></image>
<view class="bgboxs">
<view class="xwtitle">
<view class="tit">
<view class="icon">
<image v-if="xllist.channel_id==92" :src="$getimg('wf-x-ms-icon.png')" mode=""></image> <!-- 美食 -->
<image v-if="xllist.channel_id==121" :src="$getimg('wf-x-tc-icon.png')" mode=""></image> <!-- 特产 -->
<image v-if="xllist.channel_id==76" :src="$getimg('icon-yyyc.png')" mode=""></image> <!-- 非遗 -->
<image v-if="xllist.channel_id==116" :src="$getimg('wf-x-wc-icon.png')" mode=""></image> <!-- 文创 -->
</view>
<view class="titwz">{{xllist.title }}</view>
</view>
<!-- <view class="xqtimeright" style="right: 150rpx;display: flex;align-items: center;">
<image :src="$getimg('icon-sc.png')" mode="" class="xwdzimg" style="margin-right: 10rpx;"></image>
{{xllist.collectCount}}
</view> -->
<view class="xqtimeright">
<text class="sjsj">{{xllist.upsNum}}</text>
</view>
</view>
<view class="xwnr">
<view class="xwnrdiv" v-html="xllist.content"></view>
</view>
<view class="xwdzAllfotter">
<view :class="['xwdzAll',!candz?'active':'']" @click="dzsclick">
<image :src="!candz?$getimg('icon-dzxz.png'):$getimg('icon-dianzhan.png')" mode="" class="xwdzimg"></image>
<view :class="['xwdzsl',!candz?'active':'']">{{ xllist.upsNum}}</view>
</view>
<!-- <view :class="['xwdzAll',xllist.isCollect?'active':'']" style="margin-left: 20rpx;" @click="soucan">
<image :src="xllist.isCollect?$getimg('icon-scxz.png'):$getimg('icon-sc.png')" mode="" class="xwdzimg"></image>
<view :class="['xwdzsl',xllist.isCollect?'active':'']">{{ xllist.collectCount}}</view>
</view> -->
</view>
</view>
<pingjia :info="pjinfo"></pingjia>
</view>
</template>
<script>
var web = require('@/components/utils/request.js');
import pingjia from '@/components/utils/zixunpj.vue';
export default {
components:{pingjia},
data() {
return {
id: 0,
xllist: {},
candz:true, //
dzarr:[], //
pjinfo: {
id: ''
},
};
},
onLoad(option) {
this.id = option.id;
this.pjinfo.id=option.id;
var pages = getCurrentPages();
var page = pages[pages.length - 1].route;
// this.share.path='/'+page+'?id='+this.id;
// this.shareline.query={id:this.id}
this.getdatas();
try {
let tmpstr = uni.getStorageSync('dianzan'); //
if(tmpstr){
this.dzarr = tmpstr.split(',');
}
if(this.dzarr.indexOf(this.id) > -1){ //
this.candz = false; //
}
} catch (e) {
this.candz = true;
}
},
onReady() {},
onShareAppMessage() {},
methods: {
getImg(url) {
return 'https://cs.tour-ma.com' + url;
},
getdatas() {
//
var para = {
id: this.id
};
var url = '/newsdetail.jspx';
var that = this;
web.httpPost(that, url, para, function(res) {
if (res.data.status == 200) {
var tmp = res.data;
if(tmp.content!=undefined){
tmp.content = tmp.content.replace(/src="/gi, 'src="https://cs.tour-ma.com');
tmp.content = tmp.content.replace(/<img[^>]*>/gi, function(match, capture) {
return match.replace(/style\s*?=\s*?([‘"])[\s\S]*?\1/gi, 'style="max-width:100%;height:auto;width:100%;"'); // style
});
tmp.content = tmp.content.replace(/<img[^>]*>/gi, function(match, capture) {
return match.replace(/<img /gi, '<img style="max-width:100%;height:auto;width:100%;"'); // style
});
}
that.xllist = tmp;
}
});
},
soucan(id){
if(this.xllist.isCollect){
var type=2;
}else{
var type=1;
}
var url='/content/add_collections.jspx';
var para={
contentId:this.xllist.id,
type:type//1-, 2-
}
var that=this;
web.httpPost(that,url,para,function(res){
that.getdatas();
})
},
dzsclick() {
if(!this.candz){return}
//
try {
if(this.dzarr.indexOf(this.id) > -1){
this.candz = false;
}else{
this.dzarr.push(this.id);
uni.setStorageSync('dianzan',this.dzarr.toString()); //
this.candz = false; //
}
} catch (e) {
this.candz = true;
}
var url = '/content_up.jspx?contentId=' + this.id;
var para = {};
var that = this;
web.httpGejqr(that, url, para, function(res) {
uni.showToast({
title: '点赞成功!'
});
that.getdatas();
});
},
toOther(){
uni.navigateToMiniProgram({
appId:'wx8ece732cc3a87e0f',
path:'pages/index/index',
envVersion:'release',
extraData: {
},
success(res) {
console.log("chenggong");
},
fail(e){
console.log(e);
}
})
}
}
};
</script>
<style lang="scss">
$url:'https://cs.tour-ma.com/r/cms/www/m/changshu/';
page {
background: #ffffff;
}
.content {
width: 750rpx;
// padding-top: 30upx;
overflow-x: hidden;
.bgtoptxt{
font-size: 26upx;
font-weight: 500;
color: #FFFFFF;
background: linear-gradient(0deg, #3269F7 0%, #4A9AF9 100%);
border-radius: 26upx;
width: 160upx;
height: 56upx;
line-height: 56upx;
text-align: center;
margin: 0upx 0 30upx 30upx;
}
.bgtop{
width: 100%;
height: 460upx;
background-size: 100% 100% !important;
}
.bgboxs{
width: 100%;
margin: -40rpx auto 0;
background: #fff;
border-radius: 30rpx 30rpx 0rpx 0rpx;
.xwtitle {
font-size: 40upx;
font-weight: bold;
color: #333333;
display: flex;
align-items: center;
padding: 50rpx 20rpx 0;
position: relative;
.tit{
display: flex;
align-items: center;
}
.icon{
width: 62upx;
height: 56upx;
margin-right: 10rpx;
image{
display: block;
width: 100%;
height: 100%;
}
}
}
.xqtimeright {
font-size: 24rpx;
position: absolute;
right: 50rpx;
image{
width: 28rpx;
height: 28rpx;
}
}
.xqtimeright .sjsj::before {
content: '';
display: inline-block;
width: 28rpx;
height: 28rpx;
background: url( $url+"icon-dianzhan.png") no-repeat;
background-size: 100% auto;
margin-right: 10rpx;
margin-top: -9rpx;
vertical-align: middle;
}
.xwnr {
font-size: 24upx;
font-weight: 500;
color: #666;
width: 90%;
margin: 30upx auto 0 auto;
line-height: 1.6;
.xwnrdiv {
margin: 0rpx 0rpx 55rpx 0rpx;
line-height: 2;
}
}
.xwdzAllfotter {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-bottom: 40rpx;
.xwdzAll {
overflow: hidden;
border: 1rpx solid #d6d6d6;
border-radius: 35rpx;
display: flex;
flex-direction: row;
align-items: center;
width: 155rpx;
justify-content: center;
height: 70rpx;
.xwdzimg {
width: 32rpx;
height: 32rpx;
float: left;
margin-right: 10rpx;
}
.xwdzsl {
font-size: 28rpx;
font-family: Microsoft YaHei;
font-weight: bold;
color: #111111;
margin: 0rpx 0rpx 0rpx 18rpx;
float: left;
&.active {
color: #ff3335;
}
}
&.active{
border: 1rpx solid #ff3334;
}
}
}
}
.yudingimg{
width: 110rpx;
height: 110rpx;
position: fixed;
top: 278rpx;
right: 50rpx;
z-index: 999;
}
}
</style>

275
other/foodList.vue

@ -0,0 +1,275 @@
<template>
<view class="centent">
<back :info="info"></back>
<!-- <view :style="{top:statusBarHeight+'px'}" class="cityicon" @click="$getpage('/pages/other/select?city='+city)">
{{city}}</view> -->
<view class="topbg" :style="{background: 'url('+$geturl(nowdata.contentImg)+') no-repeat'}">
<view class="tbgtitle">
<view class="tbgname">{{nowdata.name}}</view>
<view class="tbginfo">{{nowdata.description}}</view>
</view>
<view class="tbboxs" v-if="alldata.length>0">
<view class="tboxtop">特色美食</view>
<view class="tboxbot" :style="{background: 'url('+$geturl6(alldata[0].typeImg)+') no-repeat'}"
@click="$getpage('/other/foodCard?id='+alldata[0].id)">
<view class="tbtitle">
<view class="tbname">{{alldata[0].title}}</view>
<view class="tbinfo text_overflow_2" v-html="alldata[0].txt">
<!-- {{alldata[0].txt}} -->
</view>
</view>
</view>
</view>
</view>
<view class="szra1" v-if="alldata.length>0">
<view class="list" v-for="(el,ind) in alldata" :style="{'margin-right':ind%3==0?'0':'12rpx'}" v-if="ind>0"
@click="$getpage('/other/foodCard?id='+el.id)">
<view class="liimg">
<image class="szra_img" :src="$geturl4(el.typeImg)" lazy-load="true" mode="aspectFill"></image>
</view>
<view class="sztitle text_overflow">{{el.title}}</view>
</view>
</view>
</view>
</template>
<script>
import back from '@/components/html/back.vue';
var web = require('@/components/utils/request.js');
export default {
components: {
back
},
data() {
return {
statusBarHeight: 0,
info: {
color: false,
num: false
},
id2: 92, //
nowdata: {},
alldata: [],
first: 0,
count: 50,
hasOnShow: true
}
},
onLoad(options) {
if (options.city != undefined) {
this.city = options.city;
}
this.getData();
},
onReady() {
//
this.statusBarHeight = uni.getSystemInfoSync()['statusBarHeight'];
// this.getData();
},
onPageScroll(e) {
if (e.scrollTop > 50 && !this.info.num) {
this.info.num = true;
}
if (e.scrollTop < 50 && this.info.num) {
this.info.num = false;
}
},
onShow(object) { //
if (!this.hasOnShow) {
return
}
this.hasOnShow = false;
this.first = 0;
this.alldata = [];
if (!!object) {
this.city = object.city;
}
this.getdata2();
},
onHide() {
this.hasOnShow = true;
},
onReachBottom() {
this.first += 50;
this.getdata2();
},
methods: {
getData() {
var url = '/json/channel_get.jspx?id=' + this.id2;
var para = {};
var that = this;
web.httpGejqr(that, url, para, function(res) {
that.nowdata = res.data;
})
},
getdata2() {
// var city='';
var url = 'json/content_list.jspx?channelIds=' + this.id2+'&orderBy=2'+'&first='+this.first+'&count='+this.count;
// var url = 'json/content_list.jspx?channelIds=' + this.id2 + '&orderBy=2';
var para = {};
var that = this;
web.httpGejqr(that, url, para, function(res) {
if (res.data.length > 0) {
for (var i = 0; i < res.data.length; i++) {
that.alldata.push(res.data[i]);
}
}
});
},
}
}
</script>
<style lang="scss">
$url:'https://cs.tour-ma.com/r/cms/www/m/changshu/';
.centent {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 750upx;
overflow-y: scroll;
.topbg {
width: 100%;
height: 826upx;
position: relative;
background-size: 100% 100% !important;
.tbgtitle {
position: absolute;
left: 58upx;
top: 150upx;
.tbgname {
font-size: 54upx;
font-weight: 600;
color: #FFFFFF;
// margin-bottom: 20upx;
}
.tbginfo {
font-size: 28upx;
font-weight: 500;
color: #FFFFFF;
}
}
.tbboxs {
background: url($url+'wf-x-mscy-k.png') no-repeat;
background-size: 100% 100%;
max-width: 690upx;
height: 449upx;
margin: auto;
margin: 290upx auto 0;
width: 90%;
position: relative;
.tboxtop {
font-size: 36upx;
font-weight: bold;
font-style: italic;
color: #973F0E;
position: absolute;
top: 16upx;
left: 58upx;
}
.tboxbot {
width: 94%;
// max-width: 650upx;
height: 360upx;
position: relative;
background-size: 100% 100% !important;
position: absolute;
top: 72upx;
left: 50%;
margin-left: -47%;
border-radius: 33upx;
overflow: hidden;
&::before {
content: '';
display: inline-block;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.32);
}
.tbtitle {
position: absolute;
left:30upx;
bottom: 30upx;
color: #FFFFFF;
&::before {
content: '';
display: inline-block;
background: url($url+'wf-x-mscy-yinhao.png') no-repeat;
background-size: 100%;
width: 35upx;
height: 33upx;
}
.tbname {
font-size: 36upx;
font-weight: bold;
margin-bottom: 16upx;
&::after {
content: '';
display: inline-block;
background: url($url+'wf-x-mscy-jt.png') no-repeat;
background-size: 100%;
width: 32upx;
height: 32upx;
margin-left: 20upx;
}
}
.tbinfo {
font-size: 24upx;
font-weight: 500;
line-height: 1.6;
}
}
}
}
}
.szra1 {
width: 90%;
margin: 40upx auto;
display: flex;
flex-direction: row;
flex-wrap: wrap;
.list {
width: 32%;
margin-bottom: 40upx;
margin-right: 15rpx;
.liimg {
margin-bottom: 20upx;
.szra_img {
max-width: 220upx;
width: 100%;
height: 210upx;
border-radius: 8upx;
}
}
.sztitle {
font-size: 32upx;
font-weight: 500;
color: #333333;
margin-bottom: 20upx;
}
}
}
}
</style>

376
other/gftjedit.vue

@ -0,0 +1,376 @@
<template>
<view class="content">
<view class="gxjtop">
<image :src="getUrl(datastypeimg)" class="gxjtopimg" mode="widthFix"></image>
</view>
<view class="gxjcenter">
<!-- <image :src="getImg('ra-xcx-xc-title-deco.png')" class="xctitleimg" mode=""></image> -->
<view class="xctop">
<view class="xctitle">{{defaultValue}}</view>
<view class="xcdzckAll">
<!-- <view class="xcdzcklist">
<image :src="getImg('ra-xcx-xc-title-dianzan.png')" class="xcdzcklistimg" mode=""></image>
<view class="xcdzcklistname">{{giveLike}}人赞</view>
</view> -->
<view class="xcdzcklist">
<image :src="getImg('ra-xcx-xc-title-liulan.png')" class="xcdzcklistimg" mode=""></image>
<view class="xcdzcklistname">{{browse}}人已浏览</view>
</view>
</view>
</view>
<view class="xcdzms" v-if="feature == ''||feature == null||feature == undefined">
登高漫步骑行这个秋日假期给疲惫的身心来一次洗礼感受山水带来的那份怡然自得吧
</view>
<view class="xcdzms" v-if="feature != ''&&feature != null&&feature != undefined">
{{feature}}
</view>
<view class="xllistAll">
<view class="xllist" v-for="(el,ind) in datas" :key="ind">
<view class="xllistts">
<view class="xllisttsleft">D{{el.days}}</view>
<view class="xllisttsright">出发啦~</view>
</view>
<view v-for="(el1,ind1) in el.data.scenic" :key="ind1" v-if="el.data.scenic.length > 0">
<view class="xllistbt">
<view class="xllistbtleft"><view class="xllistbtleft1"></view></view>
<view class="xllistbtright" v-if="ind1==0">虞山<text class="xllistbtrighttext">></text>{{el1.name}}</view>
<view class="xllistbtright" v-if="ind1!=0 && ind1<el.data.scenic.length">{{el.data.scenic[ind1 - 1].name}}<text class="xllistbtrighttext">距离</text>{{el.data.scenic[ind1].name}}<text class="jlstyle">{{el.data.scenic[ind1].distanceToPreviousScenic}}</text></view>
</view>
<view class="xllistdw" @click="nato('/pagesys/list/jqDet?uid='+el1.id)">
<image :src="getImg('ra-xcx-xc-list-icon-jingdian.png')" class="xllistdwleft" mode=""></image>
<view class="xllistdwright">
<image :src="el1.img?getUrl(el1.img):$getimg('noimg.png')" class="xllistsjimg" mode=""></image>
<view class="xllistsjall">
<view class="xllistsjname">{{el1.name}}</view>
<view class="xllistsjjy" v-if="el1.consume">人均预算{{el1.consume}}</view>
<view class="xllistsjjy" v-if="el1.address">地址:{{el1.address}}</view>
</view>
</view>
</view>
</view>
<view class="xllistdw" v-if="el.data.food.length > 0">
<image :src="getImg('ra-xcx-xc-list-icon-canyin.png')" class="xllistdwleft" mode=""></image>
<view class="xllistoverflow canyinlist">
<view class="xllistdwright active" v-for="(el1,ind1) in el.data.food" :key="ind1" @click="nato('/pagesys/list/jqDet?uid='+el1.id)">
<image :src="el1.img?getUrl(el1.img):$getimg('noimg.png')" class="xllistsjimg" mode=""></image>
<view class="xllistsjall active">
<view class="xllistsjname">{{el1.name}}</view>
<view class="xllistsjjy">地址:{{el1.address}}</view>
</view>
</view>
</view>
</view>
<view class="xllistdw" v-if="el.data.stay.length > 0">
<image :src="getImg('ra-xcx-xc-list-icon-zhusu.png')" class="xllistdwleft" mode=""></image>
<view class="xllistoverflow">
<view class="xllistdwright active" v-for="(el1,ind1) in el.data.stay" :key="ind1" @click="nato('/pagesys/list/jqDet?uid='+el1.id)">
<image :src="el1.img?getUrl(el1.img):$getimg('noimg.png')" class="xllistsjimg" mode=""></image>
<view class="xllistsjall active">
<view class="xllistsjname">{{el1.name}}</view>
<view class="xllistsjjy">地址:{{el1.address}}</view>
</view>
</view>
</view>
</view>
<view class="xllistdw" v-if="el.data.cate.length > 0">
<image :src="getImg('ra-xcx-xc-list-icon-meishi.png')" class="xllistdwleft" mode=""></image>
<view class="xllistoverflow canyinlist">
<view class="xllistdwright active" v-for="(el1,ind1) in el.data.cate" :key="ind1" @click="nato('/pagesys/list/jqDet?uid='+el1.id)">
<image :src="el1.img?getUrl(el1.img):$getimg('noimg.png')" class="xllistsjimg" mode=""></image>
<view class="xllistsjall active">
<view class="xllistsjname">{{el1.name}}</view>
</view>
</view>
</view>
</view>
<view class="xllistdw" v-if="el.data.swim.length > 0">
<image :src="getImg('ra-xcx-xc-list-icon-youli.png')" class="xllistdwleft" mode=""></image>
<view class="xllistoverflow">
<view class="xllistdwright active" v-for="(el1,ind1) in el.data.swim" :key="ind1" @click="nato('/pagesys/list/jqDet?uid='+el1.id)">
<image :src="el1.img?getUrl(el1.img):$getimg('noimg.png')" class="xllistsjimg" mode=""></image>
<view class="xllistsjall active">
<view class="xllistsjname">{{el1.name}}</view>
</view>
</view>
</view>
</view>
</view>
<view class="xllistline"></view>
</view>
<prompt :visible.sync="promptVisible" :defaultValue="defaultValue" @confirm="clickPromptConfirm" mainColor="#0078D7">
<!-- 这里放入slot内容-->
</prompt>
</view>
<view class="fixedstyle" v-if="isShow">
<view class="sousuo">
<image :src="getImg('ra-xczs-icon-sousuo.png')" class="sousuoimg" mode=""></image>
<input type="text" value="" placeholder="输入关键词搜索景点" class="sousuoinput" />
</view>
<view class="wodeAll">
<view class="wodelist" v-for="(el,ind) in jqdatas" :key="ind">
<image :src="getUrl(el.img)" class="wodelistimg" mode=""></image>
<view class="wodeleft">
<view class="wodelistname">{{el.scenicName}}</view>
<view class="wodelistnr">位于虞山市飞云江北岸的红</view>
<view class="wodelisttitle">建议游玩1.5小时</view>
</view>
<image :src="getImg('ra-xczs-icon-zengjia.png')" v-if="ind != jqdatasIndex" class="wodelisttj" @click="addjdddclick(ind)" mode=""></image>
<image :src="getImg('ra-xczs-icon-xuanze.png')" v-if="ind == jqdatasIndex" class="wodelisttj" mode=""></image>
</view>
</view>
<view class="fotter">
<view class="fotterleft">已选择{{jdnum}}个景点</view>
<view class="fotterright" @click="qdclick">确定</view>
</view>
</view>
</view>
</template>
<script>
var web = require('@/components/utils/request.js');
import Prompt from '@/components/prompt/index.vue'
export default{
components: {
Prompt
},
data(){
return{
id: 0,
playDays: 2,//
playAdults: 1,//
playChildren: 1,//
stayType: '酒店',//宿宿
stayDemand: '舒适',//宿
foodDemand: '餐饮',//
journeyType: '适中',//
landscapeDemand: '名城古镇',//()
scenicSpotDemand: '不限',// ()
fitTraffic: '自驾游',//()
scenicConsume: '500',//
stayConsume: '500',//宿
foodConsume: '500',//
promptVisible: false,//
defaultValue: '',
datas: [],
datasIndex: 0,
jqdatasIndex: -1,
jdnum: 0,
jqdatas: [],
isShow: false,
browse: 0,//
giveLike: 0,//
feature: '',//
datastypeimg:''
}
},
onShareAppMessage(res) {
if (res.from === 'menu') {//
console.log(res)
}
return {
title: '分享行程',
path: '/pagesys/xczs/edit?id=' + this.id
}
},
onLoad(option) {
// uni.showToast({title:"线",icon:"none"});
this.id = option.id;
this.getData();
this.getData1();
this.dzllclick(1);
uni.showShareMenu();
},
methods:{
getUrl(url) {
return 'https://cs.tour-ma.com/' + url;
},
getImg(url) {
return 'https://cs.tour-ma.com/r/cms/www/m/yushan/' + url;
},
nato(url){
uni.navigateTo({
url:url
})
},
getData: function() {
var url = '/journey/check.jspx';
var para = {id: this.id};
var that = this;
web.httpPost(that, url, para, function(res) {
if(res.data.status == 200) {
// uni.showToast({title:res.data.message,icon:"none"});
for(var i = 0;i<res.data.data.length;i++) {
that.datastypeimg= res.data.data[i].typeImg;
that.defaultValue = res.data.data[i].journeyName;
that.datas = res.data.data[i].userJourney;
that.browse = res.data.data[i].browse;
that.giveLike = res.data.data[i].giveLike;
that.feature = res.data.data[i].feature;
}
console.log(that.datas);
}else{
uni.showToast({title:res.data.message,icon:"none"});
uni.navigateTo({
url:'/pagesys/xczs/biaoqian?playDays='+that.playDays+'&playAdults='+that.playAdults+'&playChildren='+that.playChildren
})
}
});
},
getData1: function() {
var url = '/journey/v_scenicData.jspx';
var para = {};
var that = this;
web.httpPost(that, url, para, function(res) {
if(res.data.status == 200) {
// uni.showToast({title:res.data.message,icon:"none"});
that.jqdatas = res.data.data;
console.log(that.jqdatas);
}else{
uni.showToast({title:res.data.message,icon:"none"});
}
});
},
clickPromptConfirm(val) {
this.defaultValue = val;
this.promptVisible = false;
var url = '/journey/v_update.jspx';
var para = {
id:this.id,journeyName: this.defaultValue,userJourney: this.datas,isOfficial: 0
};
var that = this;
web.httpPost(that, url, para, function(res) {
if(res.data.status == 200) {
uni.showToast({title:'修改行程成功!',icon:"none"});
uni.navigateTo({
url:'/pagesys/xczs/wode'
})
}else{
uni.showToast({title:res.data.message,icon:"none"});
}
});
},
saveData: function() {
this.promptVisible = true;
},
addjdclick: function(ind) {
this.datasIndex = ind;
this.isShow = true;
},
addjdddclick: function(ind) {
this.jqdatasIndex = ind;
this.jdnum = 1;
},
qdclick: function() {
this.isShow = false;
if(this.jqdatasIndex != -1) {
this.datas[this.datasIndex].data.scenic.push(this.jqdatas[this.jqdatasIndex]);
}
this.jqdatasIndex = -1;
this.jdnum = 0;
console.log(this.datas);
},
delclick: function(ind,ind1) {
var that = this;
uni.showModal({
title: '提示',
content: '确定要删除此景点吗?',
success: function (res) {
if (res.confirm) {
that.datas[ind].data.scenic.splice(ind1,1);
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
},
dzllclick: function(type) {
var url = '/journey/v_giveLike.jspx';
var para = {
id:this.id,type: type
};
var that = this;
web.httpPost(that, url, para, function(res) {
if(res.data.status == 200) {
// uni.showToast({title:'',icon:"none"});
that.getData();
}else{
uni.showToast({title:res.data.message,icon:"none"});
}
});
}
}
}
</script>
<style>
@font-face {
font-family: Medium;
src: url('https://ys.tour-ma.com/r/cms/www/m/yushan/SOURCEHANSANSCN-MEDIUM_1.OTF');
}
@font-face {
font-family: Regular;
src: url('https://ys.tour-ma.com/r/cms/www/m/yushan/SOURCEHANSANSCN-REGULAR_1.OTF');
}
.content{width: 750upx;height: 100vh;position: relative;}
.gxjtop {width: 750upx;height: 488upx;position: relative;}
.gxjtopimg {width: 750upx;height: 488upx;}
.gxjcenter {width: 690upx;background: linear-gradient(0deg, #FFFFFF 0%, #F5F6F7 100%);border-radius: 44rpx 44rpx 0px 0px;padding: 0upx 30upx 70upx 30upx;position: relative;top: -120upx;}
.xctop {height: auto;position: relative;}
.xctitleimg {width: 750upx;height: 198upx;position: absolute;top: 0upx;left: 0upx;}
.xctitle {font-size: 40upx;font-family: Medium;font-weight: 500;color: #272829;position: relative;padding: 40upx 0upx 0upx 0upx;}
.xcdzckAll {overflow: hidden;position: relative;margin: 20upx 0upx 0upx 0upx;}
.xcdzcklist {float: left;border-right: 1upx solid #BBBBBB;padding: 0upx 21upx 0upx 0upx;}
.xcdzcklistimg {width: 34upx;height: 34upx;float: left;margin: 3upx 0upx 0upx 0upx;}
.xcdzcklistname {font-size: 28upx;font-family: Regular;font-weight: 400;color: #666666;opacity: 0.92;float: left;margin: 0upx 0upx 0upx 15upx;}
.xcdzcklist:last-child {border: none;/* padding: 0upx 0upx 0upx 31upx; */}
.xcdzms {font-size: 26upx;font-family: Regular;font-weight: 400;color: #666666;line-height: 44upx;margin: 0upx 0upx 51upx 0upx;}
.xllistAll {position: relative;}
.xllistline {width: 2upx;height: 100%;position: absolute;top: 0upx;background: #DDDDDD;left: 33upx;}
.xllist {}
.xllistts {overflow: hidden;position: relative;}
.xllisttsleft {width: 68upx;height: 68upx;background: #0078D7;border-radius: 50%;float: left;font-size: 32upx;font-family: Medium;font-weight: 500;color: #FFFFFF;text-align: center;line-height: 68upx;position: relative;z-index: 1;}
.xllisttsright {font-size: 36upx;font-family: Medium;font-weight: 500;color: #272829;float:left;margin: 0upx 0upx 0upx 23upx;line-height: 68upx;}
.xllistbt {overflow: hidden;}
.xllistbtleft {width: 20upx;height: 20upx;background: #FFFFFF;position: relative;border-radius: 50%;float: left;margin: 40upx 0upx 0upx 24upx;position: relative;z-index: 1;}
.xllistbtleft1 {width: 14upx;height: 14upx;background: #B6B6B6;border-radius: 50%;position: absolute;top: 3upx;left: 3upx;}
.xllistbtright {float: left;font-size: 24upx;font-family: Regular;font-weight: 400;color: #999999;margin: 30upx 0upx 0upx 49upx;}
.xllistbtrighttext {margin: 0upx 10upx;}
.xllistdw {overflow: hidden;padding: 0upx 0upx 18upx 0upx;}
.xllistdwleft {width: 68upx;height: 68upx;float: left;margin: 96upx 0upx 0upx 0upx;position: relative;z-index: 1;}
.xllistdwright {width: 568upx;height: 146upx;background: #FFFFFF;box-shadow: 0px 0px 18upx 0px rgba(0, 0, 0, 0.08);border-radius: 8upx;float: left;margin: 41upx 0upx 10upx 23upx;padding: 15upx;position: relative;}
.xllistsjimg {width: 200upx;height: 146upx;border-radius: 8upx 0upx 0upx 8upx;float: left;}
.xllistsjall {float: left;margin: 0upx 0upx 0upx 25upx;width: 336upx;}
.xllistsjname {font-size: 30upx;font-family: Medium;font-weight: 500;color: #333333;white-space: nowrap;text-overflow: ellipsis;overflow: hidden;word-break: break-all;margin: 17upx 0upx 0upx 0upx;}
.xllistsjjy {font-size: 24upx;font-family: Regular;font-weight: 400;color: #666666;margin: 25upx 0upx 0upx 0upx;white-space: nowrap;text-overflow: ellipsis;overflow: hidden;word-break: break-all;}
.xllistsjimgjs {width: 36upx;height: 36upx;position: absolute;top: 70upx;right: 20upx;}
.jlstyle {font-size: 24upx;font-family: Medium;font-weight: 500;color: #272829;margin: 0upx 0upx 0upx 15upx;}
.zjystyle {float: left;width: 568upx;height: 158upx;background: rgba(0, 120, 215, 0.04);border: 1upx solid #0078D7;border-radius: 8upx;float: left;margin: 41upx 0upx 0upx 23upx;padding: 15upx;}
.zjystylename {font-size: 28upx;font-family: Medium;font-weight: 500;color: #333333;}
.zjystylevalue {font-size: 24upx;font-family: Regular;font-weight: 400;color: #999999;line-height: 36upx; text-overflow: -o-ellipsis-lastline;overflow: hidden;text-overflow: ellipsis;display: -webkit-box;-webkit-line-clamp: 3;line-clamp: 3;-webkit-box-orient: vertical;margin: 9upx 0upx 0upx 0upx;}
.bottom {width: 280upx;height: 84upx;background: #0078D7;box-shadow: 0px 0px 30px 0px rgba(0, 0, 0, 0.14);border-radius: 42upx;text-align: center;line-height: 84upx;font-size: 32upx;font-family: Medium;font-weight: 500;color: #FFFFFF;position: fixed;bottom: 45upx;left: 235upx;cursor: pointer;}
.ztscimg {width: 40upx;height: 40upx;position: absolute;right: 30upx;top: 12upx;}
.tjjd {float: right;margin: 30upx 0upx 0upx 0upx;}
.tjjdimg {width: 28upx;height: 28upx;float: left;margin: 8upx 0upx 0upx 0upx;}
.tjjdname {font-size: 28upx;font-family: Regular;font-weight: 400;color: #0078D7;margin: 0upx 0upx 0upx 6upx;float: left;}
.fixedstyle {width: 750upx;height: 100vh;position: absolute;top: 0upx;left: 0upx;z-index: 9999;background: url('https://ys.tour-ma.com/r/cms/www/m/yushan/ra-xcx-bg-3qiu.png') no-repeat #FFFFFF;background-size: 100% 100%;}
.sousuo {width: 690upx;height: 72upx;background: #F5F5F5;border-radius: 36upx;position: relative;overflow: hidden;padding: 0upx 0upx 0upx 0upx;margin: 34upx auto 40upx auto;}
.sousuoimg {width: 38upx;height: 38upx;float: left;margin: 17upx 0upx 0upx 27upx;}
.sousuoinput {float: left;font-size: 28upx;font-family: Regular;font-weight: 400;color: #B8B8B8;margin: 0upx 0upx 0upx 22upx;width: 540upx;height: 68upx;line-height: 68upx;}
.wodeAll {width: 690upx;margin: 0upx auto 70upx auto;padding: 0upx 0upx 0upx 0upx;}
.wodelist {width: 660upx;height: 170upx;background: #FFFFFF;box-shadow: 0px 0px 18upx 0px rgba(0, 0, 0, 0.08);border-radius: 8upx;padding: 15upx;margin: 0upx 0upx 20upx 0upx;position: relative;}
.wodelistimg {float: left;width: 200upx;height: 170upx;border-radius: 8upx 0upx 0upx 8upx;}
.wodeleft {float: left;margin: 0upx 0upx 0upx 20upx;width: 390upx;}
.wodelistname {font-size: 32upx;font-family: Medium;font-weight: 500;color: #333333;white-space: nowrap;text-overflow: ellipsis;overflow: hidden;word-break: break-all;margin: 20upx 0upx 10upx 0upx;}
.wodelistnr {font-size: 24upx;font-family: Regular;font-weight: 400;color: #999999;white-space: nowrap;text-overflow: ellipsis;overflow: hidden;word-break: break-all;margin: 10upx 0upx 0upx 0upx;}
.wodelisttitle {font-size: 24upx;font-family: Regular;font-weight: 400;color: #D8A04B;margin: 10upx 0upx 0upx 0upx;}
.wodelisttj {width: 36upx;height: 36upx;position: absolute;top: 82upx;right: 20upx;}
.fotter {overflow: hidden;width: 750upx;height: 90upx;background: #FFFFFF;border: 1upx solid #EEEEEE;box-shadow: 0upx 0upx 30upx 0upx rgba(0, 0, 0, 0.14);position: fixed;bottom: 0upx;left: 0upx;z-index: 1;}
.fotterleft {font-size: 28upx;font-family: Regular;font-weight: 400;color: #999999;float: left;margin: 24upx 0upx 0upx 32upx;}
.fotterright {width: 124upx;height: 52upx;background: #0078D7;border-radius: 26upx;font-size: 24upx;font-family: Regular;font-weight: 400;color: #FFFFFF;text-align: center;line-height: 52upx;margin: 19upx 30upx 0upx 0upx;float: right;}
.xllistoverflow {display: flex;overflow-x: auto;}
.xllistdwright.active {height: auto;}
.canyinlist .xllistsjimg{width: 100%;}
</style>

170
other/gonggjt.vue

@ -0,0 +1,170 @@
<template>
<view class="content">
<!-- <web-view src="https://www.changshuxing.cn/csxing-h5/pages/home.html?origin=1&userId=okjmN5jgd0NHd004YZci3cp7wLCE&sign=&timeStamp=1638958342442"></web-view> -->
<web-view src="https://cs.tour-ma.com/csxing-h5/pages/wentilv/index.html"></web-view>
<!-- <view class="zxclist">
<view class="item" v-for="(el,ind) in datas" :key="ind">
<view class="tit">{{el.name}}</view>
<view class="zxcsl">
<view class="cwzs">
<view class="wz">车位信息</view>
<view class="sz">{{el.capacity}}</view>
</view>
<view class="cwzs">
<view class="wz">余位信息</view>
<view class="sz">{{el.availBike}}</view>
</view>
<view class="cwzs02">
<view class="wz">更新时间</view>
<view class="sz">{{el.updateTime}}</view>
</view>
</view>
<view class="dizhi">
<view class="dz">地址{{el.address}}</view>
<view class="dzimg" @click="daohang(el.jwd)">导航</view>
</view>
</view>
</view> -->
</view>
</template>
<script>
var web = require('@/components/utils/request.js');
export default{
data(){
return{
token:'',
datas: [],
lng:'',
lat:'',
}
},
onLoad() {
// this.getToken();
},
methods: {
getToken(){
var url = 'http://36.153.213.27:8888/api/mc-config/model/aggregation-external/getToken?username=user_xcx&password=Supcon';
var para = {};
var that = this;
web.httpGetxin(that, url, para, function(res) {
// console.log(res.data.obj);
that.token=res.data.obj;
if(res.data.success==false){
that.sxToken();
}else{
that.getdata();
}
});
},
sxToken(){
var url = 'http://36.153.213.27:8888/api/mc-config/model/aggregation-external/refreshToken?accessToken='+this.token;
var para = {};
var that = this;
web.httpGetxin(that, url, para, function(res) {
that.token=res.data.obj;
that.getdata();
});
},
getdata(){
uni.showToast({
title: '加载中...',
icon:'loading',
});
var url = 'http://36.153.213.27:8888/api/mc-config/model/aggregation-external/aggregation?accessToken='+this.token;
var para = {
"code": "0001_zxc",
"params": {},
"page": 0,
"size": 0,
"result": {},
"orders": []
};
var that = this;
web.httpPostxin(that, url, para, function(res) {
if (res.data.success) {
uni.hideToast();
that.datas =[];
that.datas = res.data.obj.info;
}else {
uni.showToast({
title: '加载中...',
icon:'loading',
duration:20000
});
}
});
},
daohang(jwd){
var lnglat =jwd.split(",");
// console.log(lnglat[0]);
// console.log(lnglat[1]);
this.lng = lnglat[0];
this.lat = lnglat[1];
var that=this;
uni.openLocation({
latitude:Number(that.lat),
longitude:Number(that.lng),
complete(e) {
console.log(e);
}
})
}
}
}
</script>
<style lang="scss">
.zxclist{
width: 90%;
margin: 30upx auto;
.item{
margin-bottom: 7%;
border-radius: 20rpx;
overflow: hidden;
box-shadow: 0rpx 0rpx 15rpx 10rpx #eee;
background: #fff;
line-height: 2;
color: #666666;
.tit{
font-size: 36rpx;
background:linear-gradient(0deg, #3269F7 0%, #4A9AF9 70%);
padding: 10upx 30upx;
color: #ffffff;
font-weight: 600;
}
.dizhi{
padding:10rpx 30rpx 30rpx 30rpx;
display: flex;
justify-content: space-between;
.dzimg{
padding: 0 30upx;
background:#2dc15c;
color: #ffffff;
border-radius: 50rpx;
}
}
.zxcsl{
padding:0rpx 30rpx;
display: flex;
justify-content:center;
text-align: center;
margin: 10rpx 0 0rpx 0;
.cwzs{
width: 50%;
}
.sz{
color: #333333;
font-size: 32rpx;
font-weight: 600;
}
}
}
}
</style>

142
other/hotel-detail/components/jbxx-page.vue

@ -0,0 +1,142 @@
<template>
<view>
<view class="info" v-if="detailInfo.description" v-html="detailInfo.description">
<!-- <text class="more" @click="checkBasicInformation('#jbxx')" v-if="detailInfo.introduction.length > 100">更多详情</text> -->
</view>
<!-- <view class="button-group">
<view :class="['button-group-item', item.title == activeIndex ? 'button-group-item-active' : '']"
v-for="item in baseinfoList" :key="item.title" @click="handleGroup(item)">
<image :src="item.title == activeIndex ? $getImg(item.activeIcon) : $getImg(item.icon)"></image>
{{ item.title }}
</view>
</view> -->
</view>
</template>
<script>
export default {
props: {
detailInfo: {
type: Object,
default: () => {}
}
},
data() {
return {
baseinfoList: [],
activeIndex: '',
};
},
filters: {
richTextFormat(value) {
if (value && value.length > 100) {
return value.slice(0, 100) + '...';
}
return value;
}
},
mounted() {
if(this.detailInfo.bookDes!=''){
this.baseinfoList.push({
title: '预定说明',
icon: 'ouhai_xcx/hotel/ydsm.png',
activeIcon: 'ouhai_xcx/hotel/ydsmactive.png',
anchor: '#yysj'
})
}
if(this.detailInfo.userDes!=''){
this.baseinfoList.push({
title: '使用说明',
icon: 'ouhai_xcx/hotel/sysm.png',
activeIcon: 'ouhai_xcx/hotel/sysmactive.png',
anchor: '#tsjg'
})
}
if(this.detailInfo.returnDes!=''){
this.baseinfoList.push({
title: '退改说明',
icon: 'ouhai_xcx/hotel/tgsm.png',
activeIcon: 'ouhai_xcx/hotel/tgsmactive.png',
anchor: '#ylsj'
})
}
if(this.detailInfo.addServe!=''){
this.baseinfoList.push({
title: '增值服务',
icon: 'ouhai_xcx/hotel/zzfw.png',
activeIcon: 'ouhai_xcx/hotel/zzfwactive.png',
anchor: '#fwzx'
})
}
},
methods: {
checkBasicInformation(type) {
this.$emit('goMoreDetail', type)
},
handleGroup(item) {
this.activeIndex = item.title;
this.$emit('goMoreDetail', item.anchor)
},
}
};
</script>
<style lang="scss" scoped>
.info {
font-weight: 400;
color: #333333;
text-indent: 50rpx;
line-height: 48rpx;
font-size: 24rpx;
margin-top: 37rpx;
.more {
font-family: Adobe Heiti Std;
font-weight: normal;
color: #04bb6d;
background: url('https://niangao.oss-cn-hangzhou.aliyuncs.com/ouhai_xcx/scenic/icon-%E6%9B%B4%E5%A4%9A-%E7%BB%BF.png') no-repeat;
background-position: right;
padding-right: 20rpx;
background-size: 12rpx;
margin-left: 30rpx;
}
}
.button-group {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-top: 39rpx;
&-item {
width: 222rpx;
height: 56rpx;
border: 1rpx solid #dcdcdc;
border-radius: 28rpx;
margin-top: 16rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx;
font-family: PingFang;
font-weight: 500;
color: #333333;
&-active {
color: #ffffff;
background: #04bb6d;
}
}
image {
width: 28rpx;
height: 28rpx;
margin-right: 12rpx;
}
&:after {
content: '';
width: 32%;
}
}
</style>

259
other/hotel-detail/components/list-page.vue

@ -0,0 +1,259 @@
<template>
<view class="list_container">
<view class="navBar-list">
<view v-for="item in getNavbar()" :key="item.type" @click="handleSurround(item.type)"
:class="['navBar-list-item', item.type == surroundIndex ? 'active' : '']">
{{ item.name }}
</view>
</view>
<view class="card-menu" v-for="item in list" :key="item.id" v-if="list.length>0" @click="gotoDetail(item)">
<view class="img-container">
<image :src="item.icon"></image>
<view class="img-mask">
<image :src="$getImg('index/icon-dw@2x.png')" mode="widthFix"></image>
<text>{{item.distance}}</text>
</view>
</view>
<view class="detail-container">
<view class="list-title">{{item.name}}</view>
<view class="list-info-container">
<view class="list-info">
<view class="list-gradePrice">
<view class="list-content">
<view class="list-review">
<text>{{item.commentNum}}</text>
条点评
</view>
<view class="list-divider"></view>
<view class="list-collection">
<text>{{item.collectNum}}</text>
收藏
</view>
</view>
</view>
<tags-list></tags-list>
<view class="price-container" v-if="item.price>0">
<text class="name">人均价</text>
<text>
<text class="symbol">¥</text>
<text class="price">{{item.price}}</text>
<text>0</text>
<text class="qi"></text>
</text>
</view>
</view>
</view>
</view>
</view>
<u-empty v-if="list.length<=0" text="暂无数据" mode="list"></u-empty>
<view class="viewMore" v-if="list.length>0">
<text @click="lookMore">查看更多</text>
<image :src="$getImg('ouhai_xcx/scenic/icon-%E6%9B%B4%E5%A4%9A-%E7%BB%BF.png')" mode="widthFix"></image>
</view>
</view>
</template>
<script>
import tagsList from '../../components/tags-list'
export default {
props: {
surround: {
type: Array,
default: () => []
}
},
components: {
tagsList
},
data() {
return {
surroundIndex: 1,
list: []
};
},
mounted() {
setTimeout(() => {
this.handleSurround(1)
}, 500)
},
methods: {
lookMore() {
let obj = this.surround.find(item => item.type == this.surroundIndex)
this.$emit('lookMore', obj.name)
},
gotoDetail(item) {
let obj = this.surround.find(item => item.type == this.surroundIndex)
this.$emit('gotoDetail',obj.name, item.id)
},
handleSurround(index) {
this.surroundIndex = index
let obj = this.surround.find(item => item.type == index)
if (obj && obj.list) {
this.list = obj.list
}
},
getNavbar() {
let list = []
this.surround.forEach(item => {
list.push({
name: item.name,
type: item.type
})
})
return list
}
}
};
</script>
<style lang="scss" scoped>
.list_container {
.navBar-list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
margin-top: 27rpx;
margin-bottom: 41rpx;
.navBar-list-item {
display: flex;
align-items: center;
justify-content: center;
background: #e6e7e8;
border-radius: 29rpx;
font-size: 26rpx;
font-family: PingFang SC;
font-weight: 400;
max-width: 170rpx;
height: 58rpx;
padding: 25rpx;
color: #666666;
margin-bottom: 10rpx;
}
.active {
color: #ffffff;
background: #04bb6d;
}
}
.card-menu {
width: 100%;
height: 254rpx;
background: #ffffff;
padding: 25rpx 0;
display: flex;
border-bottom: 1rpx solid #e3e3e3;
.img-container {
position: relative;
color: #ffffff;
font-weight: 400;
font-family: Microsoft YaHei;
image {
width: 194rpx;
height: 194rpx;
border-radius: 10rpx;
}
.img-mask {
position: absolute;
width: 194rpx;
height: 50rpx;
border-radius: 0rpx 0rpx 10rpx 10rpx;
bottom: 10rpx;
display: flex;
align-items: center;
padding-left: 20rpx;
background: linear-gradient(right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.5));
font-size: 24rpx;
image {
width: 30rpx;
height: 30rpx;
}
text {
margin-left: 9rpx;
}
}
}
.detail-container {
margin-left: 15rpx;
.list-title {
color: #2a2a2a;
font-weight: bold;
font-size: 30rpx;
font-family: PingFang SC;
}
.list-info-container {
display: flex;
justify-content: space-between;
margin-top: 15rpx;
.list-info {
display: flex;
flex-direction: column;
}
.list-gradePrice {
display: flex;
justify-content: space-between;
align-items: center;
font-family: PingFang SC;
font-weight: 400;
font-size: 24rpx;
margin-bottom: 10rpx;
.list-content {
display: flex;
align-items: center;
.list-review,
.list-collection {
color: #666666;
text {
font-size: 25rpx;
font-family: DIN;
font-weight: 500;
color: #999999;
margin-right: 10rpx;
}
}
.list-divider {
width: 3rpx;
height: 17rpx;
background: #cccccc;
margin-left: 15rpx;
margin-right: 13rpx;
}
}
}
}
}
}
.viewMore {
display: flex;
justify-content: center;
align-items: center;
font-size: 26rpx;
font-family: PingFang SC;
font-weight: 400;
color: #04BB6D;
margin-top: 49rpx;
image {
width: 10rpx;
height: 10rpx;
margin-left: 12rpx;
}
}
}
</style>

118
other/hotel-detail/components/swiper-page.vue

@ -0,0 +1,118 @@
<template>
<view class="swiper-container">
<swiper class="swiper" :indicator-dots="swiperOptions.indicatorDots" :autoplay="swiperOptions.autoplay"
:interval="swiperOptions.interval" :duration="swiperOptions.duration" :circular="swiperOptions.circular"
>
<swiper-item v-for="item in imgList" :key="item.bigImg">
<view class="swiper-item">
<image :src="$geturl(item.bigImg)"></image>
</view>
</swiper-item>
</swiper>
<!-- <view class="content" v-if="introduce">
<view class="introduce">
<u-icon name="volume-up" class="u-icon" size="35"></u-icon>
<text>{{ introduce | textSlice }}</text>
</view>
<view class="digitalIndicator">{{ current + 1 }}/{{ list.length }}</view>
</view> -->
</view>
</template>
<script>
export default {
components: {},
props: {
imgList: {
type: Array,
default: () => []
}
},
data() {
return {
swiperOptions: {
indicatorDots: false,
autoplay: true,
interval: 2000,
duration: 500,
circular: true
},
// current: 0,
// introduce: '',
// list: []
};
},
// filters: {
// textSlice(value) {
// if (value && value.length > 13) {
// return value.slice(0, 13) + '...';
// }
// return value;
// }
// },
// onLoad() {
// this.introduce = this.list[0].title;
// },
methods: {
// handleChange(e) {
// let {
// current
// } = e.detail;
// this.current = current;
// this.introduce = this.list[current].title;
// }
}
};
</script>
<style lang="scss" scoped>
.swiper-container {
position: relative;
.swiper {
height: 493rpx;
image {
width: 100%;
height: 493rpx;
}
}
.content {
position: absolute;
bottom: 67rpx;
width: 100%;
padding: 0 30rpx;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 25rpx;
font-family: PingFang SC;
font-weight: 400;
color: #ffffff;
text-align: center;
}
//
.introduce {
width: 420rpx;
background: rgba(0, 0, 0, 0.3);
border-radius: 23rpx;
padding: 13rpx 20rpx 12rpx 10rpx;
display: flex;
align-items: center;
justify-content: center;
.u-icon {
margin-right: 7rpx;
}
}
.digitalIndicator {
width: 80rpx;
background: rgba(0, 0, 0, 0.3);
border-radius: 23rpx;
padding: 10rpx;
}
}
</style>

56
other/hotel-detail/components/tags-list.vue

@ -0,0 +1,56 @@
<template>
<view class="tags-container">
<!-- <view class="tags" v-for="(tag,ind) in tags" v-if="ind<3" :key="tag">{{tag}}</view> -->
<u-tag v-if="index<3" size="mini" v-for="(item,index) in $getTag(list)" class="bq-list" border-color="#ffffff00" :text="item" bg-color="#ffffff00" color="#2BAD56"/>
</view>
</template>
<script>
export default {
components: {},
props: {
list: {
type: String,
default: ""
}
},
data() {
return {
}
},
onLoad() {
console.log($getTag(list));
},
mounted() {
},
methods: {},
}
</script>
<style lang="scss" scoped>
.bq-list{
background: #E9FBF7;
border-radius: 6rpx;
font-size: 22rpx;
font-family: Microsoft YaHei;
font-weight: 400;
color: #2BAD56;
margin-right: 6rpx;
}
.tags-container {
display: flex;
align-items: center;
margin-top: 15rpx;
.tags {
background: #e9fbf7;
border: 1px solid #b2dbbd;
border-radius: 6rpx;
padding: 6rpx 8rpx 7rpx 8rpx;
font-size: 22rpx;
font-family: Microsoft YaHei;
font-weight: 400;
color: #2bad56;
margin-right: 8rpx;
}
}
</style>

233
other/hotel-detail/components/ydmp-page.vue

@ -0,0 +1,233 @@
<template>
<view class="card-container">
<view class="mstop-ss-time" @click="onShowDatePicker('range')">
<view class="mstop-ss-time-rl">
<view class="mstop-ss-time-txt">{{getWeek(1)}} 入住 </view>
{{range[0]}}
</view>
<view class="wanshu">{{numwan}}</view>
<view class="mstop-ss-time-rl">
<view class="mstop-ss-time-txt">{{getWeek(2)}} 离店</view>
{{range[1]}}
</view>
</view>
<view class="card-item" v-for="item in roomList" :key="item">
<view class="start">
<image :src="item.icon"></image>
</view>
<view class="left">
<view class="name">{{item.name}}</view>
<view class="time">{{item.roomFacility}}</view>
</view>
<view class="right">
<view class="oldPrice">{{item.price}}</view>
<view class="jiage">
<text class="cfj">政府错峰价</text>
<text class="price">
<text>{{item.sellPrice}}</text>
</text>
</view>
<view class="yd-btn" @click="bookingTicket(item.id)">预定</view>
</view>
</view>
<mx-date-picker :show="showPicker" :type="type" :value="value" :show-tips="true" :begin-text="'入住'" :end-text="'离店'" :show-seconds="true" @confirm="onSelected" @cancel="onSelected" />
</view>
</template>
<script>
import {
httpPost
} from '@/components/utils/request.js';
import util from '@/components/utils/date.js'
import MxDatePicker from "../../components/mx-datepicker/mx-datepicker.vue";
export default {
props: ['roomId'],
components:{MxDatePicker},
data() {
return {
numwan: 1,
startTime: '', //
endTime: '', //
showPicker: false,
range: ['2019/09/01', '2019/09/06'],
type: 'rangetime',
value: '',
roomList: []
}
},
mounted() {
var date = new Date();
this.startTime = util.formatDate(date);
this.endTime = util.adddate(date, 1);
this.range[0] = this.startTime;
this.range[1] = this.endTime;
this.numwan = util.comparedate(this.startTime, this.endTime)
this.getList();
},
methods: {
getWeek(ind){
let week=ind==1?util.getWeek(this.startTime):util.getWeek(this.endTime);
return week;
},
getList() {
let params = {
startTime: this.startTime,
endTime: this.endTime,
roomId: this.roomId
}
httpPost(this, 'front/wares/api/type_list', params, res => {
this.roomList = res.data.result.records;
this.$emit('getRoom',this.roomList)
})
},
bookingTicket(id) {
uni.navigateTo({
url:'/pagesBuy/booking-ticket/booking-room?id='+id
})
},
onShowDatePicker(type) { //
this.type = type;
this.showPicker = true;
this.value = this[type];
},
onSelected(e) { //
this.showPicker = false;
if (e) {
this[this.type] = e.value;
}
this.startTime = e.value[0];
this.endTime = e.value[1];
this.numwan = util.comparedate(this.startTime, this.endTime)
this.getList();
},
}
}
</script>
<style lang="scss" scoped>
.mstop-ss-time {
width: 690rpx;
height: 120rpx;
background: #3D72F2;
border-radius: 8rpx;
display: flex;
justify-content: space-around;
color: #fff;
font-size: 29rpx;
font-weight: 500;
align-items: center;
.wanshu {
height: 45rpx;
line-height: 31rpx;
text-align: center;
border: 1px solid rgba(255, 255, 255, 0.4);
border-radius: 16rpx;
font-size: 25rpx;
padding: 5rpx 15rpx;
}
&-rl{
text-align: center;
}
.mstop-ss-time-txt {
font-size: 22rpx;
margin-bottom: 10rpx;
}
}
.card-container {
margin-top: 35rpx;
.start {
width: 160rpx;
height: 100%;
image {
width: 100%;
height: 100%;
border-radius: 20rpx;
}
}
.card-item {
box-shadow: 0rpx 0rpx 30rpx 0rpx rgba(0, 0, 0, 0.15);
margin-top: 39rpx;
height: 210rpx;
background: #F5F6F7;
border-radius: 14rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 28rpx 30rpx;
font-family: Alibaba PuHuiTi;
font-weight: 400;
font-size: 24rpx;
.left {
width: 205rpx;
margin: 0rpx 0rpx 0rpx 10rpx;
}
.name {
font-size: 32rpx;
color: #222222;
}
.time {
color: #999;
margin-top: 12rpx;
}
.right {
text-align: center;
overflow: hidden;
.oldPrice {
font-size: 28rpx;
font-family: Alibaba PuHuiTi;
font-weight: 400;
text-decoration: line-through;
color: #999999;
text-align: right;
}
.jiage {
margin-top: 10rpx;
margin-bottom: 10rpx;
.cfj {
font-size: 24rpx;
font-family: PingFang SC;
font-weight: 400;
color: #333333;
}
.price {
font-size: 28rpx;
font-family: Alibaba PuHuiTi;
font-weight: 400;
color: #FF540B;
text {
font-size: 42rpx;
}
}
}
.yd-btn {
width: 128rpx;
height: 58rpx;
background: #FF540B;
border-radius: 29rpx;
font-size: 28rpx;
font-family: Alibaba PuHuiTi;
font-weight: 400;
color: #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
// margin-left: 45rpx;
float: right;
}
}
}
}
</style>

581
other/hotel-detail/hotel-detail.vue

@ -0,0 +1,581 @@
<template>
<view>
<back :info="info"></back>
<view class="container">
<swiper-page :imgList="detailInfo.imgList&&detailInfo.imgList.length===0?$getimg('lx-jd.png'):detailInfo.imgList"></swiper-page>
<view class="info-container">
<view class="introduction">
<view class="content">
<view class="title-container">
<image :src="$getimg('icon-jdmp.png')" mode="widthFix"></image>
<view class="name">{{ detailInfo.name }}</view>
<view class="level" v-if="[1, 2, 3, 4, 5].includes(detailInfo.level)">
{{ detailInfo.level }}</view>
</view>
<tags-list v-if="detailInfo.labels!=''" :list="detailInfo.labels" style="margin-top: 40rpx;">
</tags-list>
</view>
</view>
<view class="line"></view>
<view class="address-container">
<view>
<view class="address">{{ detailInfo.address || '' }}</view>
<view class="distance">距离您的位置{{detailInfo.distance}}</view>
</view>
<view class="address-img-container">
<view @click="goAddress(detailInfo.lagoff, detailInfo.lngoff)">
<image :src="$getimg('icon-map.png')" mode="widthFix"></image>
<view class="text">地图</view>
</view>
<!-- <view @click="handleClick(2)" v-if="detailInfo.telConsult!=''">
<image :src="$getimg('icon-iphone.png')" mode="widthFix"></image>
<view class="text">电话</view>
</view> -->
</view>
</view>
<!-- 基本信息 -->
<view class="slotContent" style="padding: 20rpx 30rpx;">
<view class="title">基本信息</view>
<jbxx-page @goMoreDetail="goMoreDetail" :detailInfo="detailInfo"></jbxx-page>
</view>
<pingjia :infoid="roomId" zytype="2" type="1"></pingjia>
<view class="slotContent" v-if="detailInfo.ctgList">
<view class="title">搜周边</view>
<view class="list_container">
<view class="card-menu" v-for="(item, index) in detailInfo.ctgList" :key="item.uid"
:style="{ borderTop: index == 0 ? '1rpx solid #eeeeee' : '' }">
<view class="img-container">
<u-image width="194rpx" height="194rpx" border-radius="10rpx"
:src="item.thumbImg==''?$getimg('noimg.png'):$geturl(item.thumbImg)"></u-image>
<view class="img-mask">
<image :src="$getimg('icon-juli.png')" mode="widthFix"></image>
<text>{{ item.distanceStr }}</text>
</view>
</view>
<view class="detail-container" >
<view class="title-container">
<view class="content">
<text class="title" style="margin-top: 0;">{{ item.name }}</text>
<view class="level" v-if="[1, 2, 3, 4, 5].includes(item.level)">{{ item.level }}A</view>
</view>
</view>
<view class="info-container">
<view class="info">
<view class="gradePrice">
<view class="lbradd">{{item.address}}</view>
</view>
<tags-list :list="item.scapeType"></tags-list>
<!-- <view class="price-container" v-if="item.ticketPrice!=''">
<text v-if="item.ticketPrice!=0">
<text class="symbol">¥</text>
<text class="price">{{item.ticketPrice}}</text>
<text class="qi"></text>
</text>
</view> -->
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import {
httpPost
} from '@/components/utils/request';
import ydmpPage from './components/ydmp-page';
import jbxxPage from './components/jbxx-page';
import swiperPage from './components/swiper-page';
import listPage from './components/list-page';
import tagsList from './components/tags-list';
import pingjia from '@/components/utils/zixunpj.vue';
import back from '@/components/html/back.vue';
export default {
components: {
ydmpPage,
jbxxPage,
swiperPage,
listPage,
tagsList,
pingjia,
back
},
data() {
return {
info: {
color: true,num:false
},
url: {
detail: '/waresdetail.jspx',
},
detailInfo: {},
customStyle: {
width: '170rpx',
height: '64rpx',
lineHeight: '64rpx',
background: '#FF9445',
fontSize: '24rpx'
},
ticketsIndex: 0,
surround: [],
roomId: 0,
roomType: 0,
roomlength:1,
longitude: '',
latitude:''
};
},
onLoad(options) {
console.log(options)
this.roomId = options.uid;
this.roomType = options.type;
var that = this;
uni.getLocation({
type: 'wgs84',
success: function (res) {
that.longitude = res.longitude;
that.latitude = res.latitude;
that.getDetail(options.id,options.type);
}
});
},
methods: {
getRoom(e){
this.roomlength=e.length;
},
getDetail(id,type) {
httpPost(this, this.url.detail, {
id,
type: Number(type),
longitude: this.longitude,
latitude: this.latitude
}, res => {
let json = res.data.data;
this.detailInfo = json;
});
},
clickMore() {
uni.showToast({
title: '点击更多',
icon: 'none'
});
},
goAddress(latitude, longitude) {
console.log(latitude + ',' + longitude);
uni.openLocation({
latitude: Number(latitude),
longitude: Number(longitude)
});
},
// handleClick(type) {
// if (type == 1) {
// let url = '/pages/daohang?lat=' + this.detailInfo.latGd + '&lng=' + this.detailInfo.lngGd +
// '&name=' + this.detailInfo.name + '&address=' + this.detailInfo.address;
// uni.navigateTo({
// url: url
// })
// } else {
// let phone = this.detailInfo.telConsult;
// uni.makePhoneCall({
// phoneNumber: phone,
// success: (res) => {
// console.log('!')
// },
// });
// }
// },
handleTickets(item) {
this.ticketsIndex = item;
},
lookMore(name) {
let path = '';
if (name == '景区') path = '../scenic';
if (name == '酒店') path = '../hotel';
uni.navigateTo({
url: path
});
},
gotoDetail(name, id) {
let path = '';
if (name == '酒店') {
this.getDetail(id);
uni.pageScrollTo({
scrollTop: 0,
duration: 500
});
return
}
if (name == '景区') path = `../scenic-detail/scenic-detail?id=${id}`;
uni.navigateTo({
url: path
});
},
bookingTicket() {
uni.navigateTo({
url: '../booking-ticket/booking-ticket'
});
},
goMoreDetail(anchor) {
uni.navigateTo({
url: `../more-detail/more-detailjd?anchor=${anchor}&id=`+this.roomId
});
},
getListImg(imgs) {
if (!imgs) return this.$getImg('/ouhai_xcx/home/map.png');
return imgs.split(',')[0];
}
}
};
</script>
<style lang="scss">
.container {
width: 100%;
}
.info-container {
position: relative;
top: -50rpx;
.introduction {
// height: 200rpx;
background: #fff;
border-radius: 30rpx 30rpx 0rpx 0rpx;
padding: 47rpx 34rpx 5rpx 30rpx;
display: flex;
justify-content: space-between;
.content {
display: flex;
flex-direction: column;
.title-container {
display: flex;
align-items: center;
image {
width: 54rpx;
height: 40rpx;
}
.name {
font-size: 30rpx;
font-family: PingFang SC;
font-weight: bold;
color: #333333;
margin-left: 17rpx;
margin-right: 18rpx;
}
.level {
width: 60rpx;
height: 40rpx;
text-align: center;
background: #fff1eb;
border-radius: 4rpx;
font-size: 28rpx;
font-family: DIN;
font-weight: bold;
color: #ff540b;
}
}
}
}
.businessInformation {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30rpx 30rpx 30rpx;
font-size: 26rpx;
font-weight: 400;
font-family: PingFang SC;
.isBusiness {
font-weight: bold;
color: #04bb6d;
margin-right: 21rpx;
}
.businessTime {
flex: 1;
color: #333333;
}
.moreMore {
font-size: 22rpx;
color: #666666;
}
}
.line {
width: 690rpx;
height: 1rpx;
margin: auto;
background: #000000;
opacity: 0.1;
}
.address-container {
display: flex;
justify-content: space-between;
font-family: PingFang SC;
font-weight: 400;
padding: 30rpx;
font-size: 22rpx;
image {
width: 48rpx;
height: 48rpx;
}
.address {
font-size: 28rpx;
color: #333333;
margin-bottom: 19rpx;
}
.distance {
color: #999999;
}
.address-img-container {
width: 20%;
display: flex;
text-align: center;
justify-content: space-between;
.text {
color: #666666;
}
}
}
.slotContent {
padding: 30rpx;
.title {
margin-top: 25rpx;
font-size: 36rpx;
font-family: PingFang SC;
font-weight: 600;
color: #333333;
}
}
}
.card-menu {
// width: 750rpx;
height: 207rpx;
background: #ffffff;
padding: 25rpx;
margin-bottom: 20rpx;
display: flex;
.img-container {
position: relative;
color: #ffffff;
font-weight: 400;
font-family: Microsoft YaHei;
image {
width: 194rpx;
height:184rpx;
border-radius: 10rpx;
}
.img-mask {
position: absolute;
width: 180rpx;
height: 50rpx;
border-radius: 0rpx 0rpx 10rpx 10rpx;
bottom: 25rpx;
display: flex;
align-items: center;
padding-left: 20rpx;
background: linear-gradient(right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.5));
font-size: 24rpx;
image {
width: 30rpx;
height: 30rpx;
}
text {
margin-left: 9rpx;
}
}
}
.detail-container {
margin-left: 15rpx;
width: 100%;
display: flex;
flex-direction: column;
.for20Seconds {
background: #fff5f5;
border: 1rpx solid #ff948e;
border-radius: 4rpx;
font-size: 20rpx;
font-family: PingFang SC;
font-weight: 400;
color: #ff0c00;
text-align: center;
margin-left: 10rpx;
padding: 2rpx 9rpx 2rpx 10rpx;
}
.info-container {
display: flex;
justify-content: space-between;
margin-top: 67rpx;
.info {
display: flex;
flex-direction: column;
.gradePrice {
display: flex;
justify-content: space-between;
align-items: center;
font-family: PingFang SC;
font-weight: 400;
font-size: 24rpx;
.lbradd{
font-size: 24upx;
color: #666666;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
&::before{
content: '';
display: inline-block;
width: 18upx;
height: 22upx;
background: url('https://cs.tour-ma.com/r/cms/www/m/changshu/ra-liebiao-icon-dizhi.png') no-repeat;
background-size: 100%;
margin-right: 12upx;
}
}
.content {
display: flex;
align-items: center;
.review,
.collection {
color: #666666;
text {
font-size: 25rpx;
font-family: DIN;
font-weight: 500;
color: #333333;
margin-right: 10rpx;
}
}
.divider {
width: 3rpx;
height: 17rpx;
background: #cccccc;
margin-left: 15rpx;
margin-right: 13rpx;
}
}
.price {
text-decoration: line-through;
color: #979797;
}
}
}
}
.tags-container {
margin-top: 16rpx;
}
.price-container {
line-height: 50rpx;
margin-top: 5rpx;
color: #ff540b;
font-size: 24rpx;
.symbol {
margin-right: 5rpx;
}
.price {
font-size: 42rpx;
}
.qi {
color: #666666;
font-size: 20rpx;
margin-left: 10rpx;
}
.price-name {
color: #66666670;
font-size: 22rpx;
font-weight: 400;
}
.oldPrice {
margin-left: 9rpx;
}
}
}
}
.title-container {
display: flex;
align-items: center;
image {
width: 54rpx;
height: 40rpx;
}
.name {
font-size: 48rpx;
font-family: PingFang SC;
font-weight: bold;
color: #333333;
margin-left: 17rpx;
margin-right: 18rpx;
}
.level {
width: 60rpx;
height: 40rpx;
text-align: center;
background: #fff1eb;
border-radius: 4rpx;
font-size: 28rpx;
font-family: DIN;
font-weight: bold;
color: #ff540b;
margin-left: 10rpx;
}
.content {
display: flex;
align-items: center;
.title {
color: #2a2a2a;
font-weight: bold;
font-size: 36rpx;
font-family: PingFang SC;
}
}
}
</style>

508
other/hotel.vue

@ -0,0 +1,508 @@
<template>
<view>
<view class="container">
<u-dropdown title-size="28" :class="['u-dropdown',istop?'u-dropdown-top':'']"
:style="{top:istop?(navHeight-2)+'px':0}">
<u-dropdown-item v-model="value1" :title="options1[value1].label" :options="options1"
@change="handleChange"></u-dropdown-item>
<u-dropdown-item v-model="value3" :title="options3[value3].label" :options="options3"
@change="handleChange"></u-dropdown-item>
</u-dropdown>
<view class="container_li">
<view class="card-menu" v-for="item in waresList" :key="item.uid" @click="msto(item.wxMiNiAppId,item.wxMiNiPath,item.uid)">
<view class="img-container">
<u-image width="686rpx" height="390rpx" border-radius="14rpx 14rpx 0 0"
:src="item.logo==''||item.logo==null?$getimg('noimg.png'):$geturl(item.logo)"></u-image>
<!-- <view class="img-tags" v-if="item.turnFast==1">30秒入住</view> -->
<view class="img-mask">
<image :src="$getimg('icon-juli.png')" mode="widthFix"></image>
<text>{{ item.distance }}</text>
</view>
</view>
<view class="text-container">
<view class="title-container">
<view class="content">
<text class="title">{{ item.name }}</text>
<view class="level" v-if="[1, 2, 3, 4, 5].includes(item.level)">{{ item.level }}</view>
</view>
<!-- <u-icon @click.native.stop="clickOnCollection(item)" name="star-fill" size="35"
:color="item.isCollect ? '#FF540B' : '#e5e5e5'"></u-icon> -->
</view>
<view>
<view class="gradePrice">
<view class="lbradd">{{item.address}}</view>
<!-- <view class="oldPrice" v-if="item.price!=0">{{ item.price}}</view> -->
</view>
</view>
<view class="label-container">
<!-- <tags-list :list="item.labels"></tags-list> -->
<view class="price-container" v-if="item.roomPrice!=0">
<!-- <text class="name">错峰价</text> -->
<text>
<text class="symbol">¥</text>
<text class="price">{{ item.roomPrice}}</text>
<text class="qi"></text>
</text>
<text v-if="item.wxMiNiAppId!=''&& item.wxMiNiPath!=''" class="oldPrice" >立即预订</text>
</view>
</view>
</view>
</view>
</view>
<!-- <u-loadmore v-if="waresList.length > 0" :status="status" :load-text="loadText" /> -->
</view>
<u-toast ref="uToast" />
</view>
</template>
<script>
import back from '@/components/html/back.vue';
var web = require('@/components/utils/request.js');
// import comfortPage from './components/comfort-page';
import tagsList from '@/components/tags-list';
import {
mapGetters
} from 'vuex'
export default {
components: {
back,
tagsList,
// comfortPage
},
data() {
return {
value1: 0,
value2: 0,
value3: 0,
options1: [{
label: '酒店类型',
value: 0,
type:2,
scapeType: ''
}, {
label: '星级酒店',
value: 1,
type:2,
scapeType: '星级酒店'
}, {
label: '精品酒店',
value: 2,
type:2,
scapeType: '精品酒店'
}, {
label: '经济连锁',
value: 3,
type:2,
scapeType: '经济连锁'
}, {
label: '特色民宿',
value: 4,
type:164,
scapeType: ''
}],
options3: [{
label: '智能排序',
value: 0
}, {
label: '等级优先',
value: 1
}, {
label: '低价优先',
value: 2
}, {
label: '高价优先',
value: 3
}, {
label: '距离优先',
value: 4
}],
params: {
pageNo: 1,
pageSize: 100,
// type: 1,
radius: 0,
ctgId:'',
// level: "0",
name: "",
scapeType: "",
orderBy: 0,
},
url: {
list: 'wareInfo/list4.jspx',
collection: 'front/jcCollect/wares_collect'
},
waresList: [],
listpages: 0,
status: 'loading',
loadText: {
// loadmore: '~~',
loading: '努力加载中~~',
nomore: '已经到底了~~'
},
istop: false,
};
},
mounted() {
this.getList();
},
onReachBottom() {
this.getMore();
},
computed: {
...mapGetters(['navHeight'])
},
onPageScroll(e) {
if (e.scrollTop > 40) {
this.istop = true;
} else {
this.istop = false;
}
},
methods: {
msto(wxMiNiAppId, wxMiNiPath, uid) {
if (wxMiNiAppId && wxMiNiPath) {
if (wxMiNiAppId == 99999999) {
uni.showModal({
title: '提示',
content: '因景区仅提供网页预订,预约地址已复制到剪贴板,请在浏览器中粘贴打开',
showCancel: false,
cancelText: '',
confirmText: '确定',
success: res => {
if (res.confirm) {
uni.setClipboardData({
data: link, //
success: (e) => { //
console.log(e)
}
});
}
}
});
} else {
this.toOther(wxMiNiAppId, wxMiNiPath)
}
} else {
console.log(2)
uni.navigateTo({
url: `../pages/jqDet?uid=${uid}`
});
}
},
toOther(wxMiNiAppId, wxMiNiPath) {
uni.navigateToMiniProgram({
appId: wxMiNiAppId,
path: wxMiNiPath,
envVersion: 'release',
extraData: {},
success(res) {
console.log("chenggong");
},
fail(e) {
console.log(e);
}
})
},
getList() {
for (var i = 0; i < this.options1.length; i++) {
if (this.value1 == this.options1[i].value) {
this.params.ctgId = this.options1[i].type
this.params.scapeType = this.options1[i].scapeType
}
}
// for (var i = 0; i < this.options2.length; i++) {
// if (this.value2 == this.options2[i].value) {
// this.params.scapeType = this.options2[i].type
// }
// }
this.params.orderBy = this.value3;
this.params.sort = this.value1;
web.httpPost(this, this.url.list, this.params, res => {
let waresList = res.data.results;
let pages = res.data.results.length;
this.listpages = pages;
if (this.params.pageNo == 1) {
this.waresList = waresList;
} else {
this.waresList = this.waresList.concat(waresList);
}
});
},
// getMore() {
// if (this.params.pageNo >= this.listpages) {
// this.status = 'nomore'
// } else if (this.params.pageNo < this.listpages) {
// this.params.pageNo = ++this.params.pageNo;
// this.getList();
// }
// },
handleChange(e) {
this.getList();
},
// goDetail(item) {
// uni.navigateTo({
// url: `../other/hotel-detail/hotel-detail?id=${item.uid}`
// });
// },
getListImg(imgs) {
if (!imgs) return this.$getImg('ouhai_xcx/home/map.png');
return imgs.split(',')[0];
},
// clickOnCollection(item) {
// httpPost(this, this.url.collection, {
// id: item.id
// }, res => {
// this.$refs.uToast.show({
// title: item.isCollect ? '' : '',
// type: 'success',
// icon: 'checkmark-circle'
// });
// item.isCollect = !item.isCollect;
// item.collectNum = item.isCollect ? item.collectNum + 1 : item.collectNum - 1;
// });
// },
// cilckMap() {
// uni.navigateTo({
// url: '../map?id=2'
// })
// }
}
};
</script>
<style lang="scss">
@import "uview-ui/index.scss";
.u-dropdown-top {
position: fixed;
background: #fff;
display: flex;
flex-direction: row;
justify-content: space-between;
width: 750rpx;
top: 120rpx;
z-index: 9;
}
.container {
// padding: 30rpx;
background: #ffffff;
}
.container_li{
padding: 30rpx;
// background: #ffffff;
}
.card-menu {
width: 690rpx;
// height: 610rpx;
background: #ffffff;
border: 1rpx solid #dddddd;
border-radius: 14px;
margin-bottom: 30rpx;
.img-container {
position: relative;
font-family: Microsoft YaHei;
font-weight: 400;
color: #ffffff;
image {
width: 100%;
height: 390rpx;
border-radius: 14px 14px 0px 0px;
}
.img-tags {
position: absolute;
top: 20rpx;
right: 20rpx;
min-width: 180rpx;
height: 48rpx;
background: #fd4818;
border-radius: 10rpx;
font-size: 26rpx;
text-align: center;
line-height: 48rpx;
font-weight: bold;
}
.img-mask {
position: absolute;
bottom: 2rpx;
left: 0;
width: 332rpx;
height: 50rpx;
display: flex;
align-items: center;
padding-left: 25rpx;
background: linear-gradient(right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.5));
font-size: 26rpx;
image {
width: 30rpx;
height: 30rpx;
}
text {
margin-left: 9rpx;
}
}
}
.text-container {
display: flex;
flex-direction: column;
padding: 30rpx;
.gradePrice {
display: flex;
justify-content: space-between;
align-items: center;
font-family: PingFang SC;
font-weight: 400;
font-size: 24rpx;
margin-top: 30rpx;
.lbradd{
font-size: 24upx;
color: #666666;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
&::before{
content: '';
display: inline-block;
width: 18upx;
height: 22upx;
background: url('https://cs.tour-ma.com/r/cms/www/m/changshu/ra-liebiao-icon-dizhi.png') no-repeat;
background-size: 100%;
margin-right: 12upx;
}
}
.content {
display: flex;
align-items: center;
.review,
.collection {
color: #666666;
text {
font-size: 25rpx;
font-family: DIN;
font-weight: 500;
color: #333333;
margin-right: 10rpx;
}
}
.divider {
width: 3rpx;
height: 17rpx;
background: #cccccc;
margin-left: 15rpx;
margin-right: 13rpx;
}
}
.price {
text-decoration: line-through;
color: #979797;
}
}
}
.label-container {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10rpx;
}
.price-container {
line-height: 50rpx;
margin-top: 5rpx;
color: #ff540b;
font-size: 24rpx;
.symbol {
margin-right: 5rpx;
}
.price {
font-size: 42rpx;
}
.qi {
color: #666666;
font-size: 20rpx;
margin-left: 10rpx;
}
.price-name {
color: #66666670;
font-size: 22rpx;
font-weight: 400;
}
.oldPrice {
// margin-left: 9rpx;
// margin-left: 54%;
border-color: #ffffff00;
padding: 1rpx 12rpx;
background: #fbe0d4;
position: absolute;
right: 60rpx;
}
}
}
.title-container {
display: flex;
align-items: center;
image {
width: 54rpx;
height: 40rpx;
}
.name {
font-size: 48rpx;
font-family: PingFang SC;
font-weight: bold;
color: #333333;
margin-left: 17rpx;
margin-right: 18rpx;
}
.level {
width: 60rpx;
height: 40rpx;
text-align: center;
background: #fff1eb;
border-radius: 4rpx;
font-size: 28rpx;
font-family: DIN;
font-weight: bold;
color: #ff540b;
margin-left: 10rpx;
}
.content {
display: flex;
align-items: center;
.title {
color: #2a2a2a;
font-weight: bold;
font-size: 36rpx;
font-family: PingFang SC;
}
}
}
</style>

316
other/pingjia/pingjia.vue

@ -0,0 +1,316 @@
<template>
<view class="content">
<view class="detail">
<view class="top">
<image v-if="datas.pic!=''" :src="getImg(datas.pic)"></image>
<image v-else :src="getImg1('wf-xcx-gongyong.jpg')"></image>
<view class="cntern">
<view class="name">{{datas.name}}</view>
</view>
</view>
<view class="tianxie">
<textarea :class="datas.assess.length==0?'active':'nactive'" :placeholder="placeholder" placeholder-style="font-size:26rpx" v-model="datas.assess" />
</view>
<view class="tag">
<view :class="['tag-list',el.isclick?'active':'']" v-for="(el,ind) in tags" :style="{'margin-right':(ind+1)%4==0?'0':'40rpx'}" @click="setTag(ind)">
{{el.name}}
</view>
</view>
</view>
<!-- <view class="xxlist">v-for="(datas,ind) in datas.itemList"
<view class="scitemname">{{datas.name}}</view>
<view class="scitemstarts">
<image class="star-image" :src="datas.score>0? selectedSrc:normalSrc" @click="chooseicon(ind,1)"></image>
<image class="star-image" :src="datas.score>1? selectedSrc:normalSrc" @click="chooseicon(ind,2)"></image>
<image class="star-image" :src="datas.score>2? selectedSrc:normalSrc" @click="chooseicon(ind,3)"></image>
<image class="star-image" :src="datas.score>3? selectedSrc:normalSrc" @click="chooseicon(ind,4)"></image>
<image class="star-image" :src="datas.score>4? selectedSrc:normalSrc" @click="chooseicon(ind,5)"></image>
</view>
</view> -->
<view class="tipj" @click="submit">提交评价</view>
</view>
</template>
<script>
var web = require('@/components/utils/request.js');
export default{
data(){
return{
placeholder:'评价一下吧',
normalSrc: '../../static/star-null-big_hong.png',
selectedSrc: '../../static/star-on-big_hong.png',
datas:{},
textInd:1,
waresId:0,
tags:[]
}
},
onLoad(options) {
var pages = getCurrentPages();
var page = pages[pages.length - 1].route;
// this.share.path='/'+page+'?waresId='+this.waresId;
// this.shareline.query={waresId:this.waresId}
this.waresId=options.waresId
},
onReady() {
this.getadd();
this.getTag();
},
methods:{
setTag(ind){
this.tags[ind].isclick=!this.tags[ind].isclick;
},
getTag(){
var json=this.datas;
var url='/comments.jspx'
var para = {
contentId:this.waresId,
flag:1
};
var that=this;
web.httpPost(this,url,para,function(res) {
if(res.data.status==200){
let json=res.data.data;
let tmp=[];
for(let i=0;i<json.length;i++){
tmp.push({
name:json[i],
isclick:false
})
}
that.tags=tmp;
}
})
},
submit(){
let json=this.tags;
let tmp='';
for(var i=0;i<json.length;i++){
if(json[i].isclick){
tmp+=json[i].name+','
}
}
tmp = tmp.substr(0,tmp.length-1);
var url='/comment.jspx';
var para = {
contentId:this.waresId,
score:this.datas.score,
text:this.datas.assess,
commentTag:tmp
};
var that=this;
web.httpPost(this,url,para,function(res) {
if(res.data.status==200){
uni.showToast({
title: '评价成功'
});
setTimeout(function(){
uni.navigateBack({
delta:1
})
},1000)
}else{
uni.showToast({
title: res.data.message
});
}
})
},
//
chooseicon(ind,score) {
this.datas.score=score;
},
getadd(){
var url = '/waresdetail.jspx';
var para = {
id: this.waresId
};
var that=this;
web.httpPost(this,url,para,function(res) {
if(res.data.status==200){
var json=res.data.data;
json.assess='';
json.imglist=[];
json.score=5;
that.datas=json;
}
})
},
getImg(url){
return 'https://cs.tour-ma.com'+url
},
getImg1(url){
return 'https://cs.tour-ma.com/r/cms/www/m/changshu/'+url
}
}
}
</script>
<style lang="scss">
@import url("@/components/css/iconfont.css");
.tag{
margin-top: 15rpx;
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
.active{
font-size: 22rpx;
font-family: Microsoft YaHei;
font-weight: 400;
color: #FFFFFF;
background: #227BFB;
}
&-list{
padding: 5rpx 10rpx;
margin-bottom: 40rpx;
min-width: 108rpx;
height: 35rpx;
background: #E9F2FF;
border: 1rpx solid #66A6FF;
border-radius: 4rpx;
font-size: 22rpx;
font-family: Microsoft YaHei;
font-weight: 400;
color: #227BFB;
text-align: center;
}
}
.tipj{
font-size: 33rpx;
font-weight: 500;
color: #FFFFFF;
width: 438rpx;
height: 78rpx;
line-height: 78rpx;
background: linear-gradient(90deg, #FF540B 0%, #FFA200 100%);
border-radius: 39rpx;
margin-top: 83rpx;
text-align: center;
letter-spacing: 10rpx;
}
.imglist{
width: 650rpx;
// height: 200rpx;
display: flex;
align-items: center;
flex-wrap: wrap;
margin-top: 20rpx;
justify-content: space-between;
.sctp2{
width: 270rpx;
height: 200rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
image{
border-radius: 14rpx;
width: 270rpx;
height: 200rpx;
}
margin-bottom: 20rpx;
}
.sctp{
width: 270rpx;
height: 200rpx;
background-color: #FAFAFA;
border: 1px solid #EAEAEA;
border-radius: 14rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-bottom: 20rpx;
image{
width: 49rpx;
height: 47rpx;
}
.name{
font-size: 25rpx;
line-height: 25rpx;
margin-top: 20rpx;
color: #666666;
}
}
}
.tianxie{
width: 650rpx;
height: 200rpx;
margin-top:62rpx;
font-size: 26rpx;
.active{
width: 650rpx;
height: 200rpx;
// background: url(https://xcx.wfwhy.gov.cn/r/cms/www/weifang/ximg/bh-pj-icon-pj.png) no-repeat;
background-position: left 3rpx;
background-size: 26rpx;
}
.nactive{
width: 650rpx;
height: 200rpx;
}
}
.xxlist{
height: 40rpx;
display: flex;
align-items: center;
margin-top: 45rpx;
width: 650rpx;
.scitemname{
font-size: 33rpx;
line-height: 33rpx;
font-weight: 500;
color: #222222;
margin-right: 28rpx;
}
.scitemstarts{
display: flex;
align-items: center;
.star-image{
width: 38rpx;
height: 37rpx;
margin-right: 11rpx;
}
}
}
.top{
display: flex;
flex-direction: row;
justify-content: space-around;
image{
width: 111rpx;
height: 111rpx;
background: #f1f1f1;
border-radius: 10rpx;
margin-right: 27rpx;
}
.cntern{
flex: 1;
.name{
font-size: 28rpx;
line-height: 28rpx;
color: #666666;
margin-top: 14rpx;
}
}
}
.detail{
width: 650rpx;
// min-height: 771rpx;
background: #FFFFFF;
border: 1rpx solid #f0f0f0;
border-radius: 17rpx;
padding: 28rpx 25rpx;
}
.content{
padding-top: 3vh;
height: 97vh;
width: 750rpx;
background: linear-gradient(0deg, #FFFFFF 0%, #F9F9F9 100%);
display: flex;
flex-direction: column;
align-items: center;
padding-bottom: 80rpx;
}
</style>

308
other/pingjia/pingjiazx.vue

@ -0,0 +1,308 @@
<template>
<view class="content">
<view class="detail">
<view class="top">
<image v-if="datas.midPic!=''" :src="getImg(datas.midPic)"></image>
<image v-else :src="getImg1('wf-xcx-gongyong.jpg')"></image>
<view class="cntern">
<view class="name">{{datas.title}}</view>
</view>
</view>
<view class="tianxie">
<textarea :class="datas.assess.length==0?'active':'nactive'" :placeholder="placeholder" placeholder-style="font-size:26rpx" v-model="datas.assess" />
</view>
<view class="tag">
<view :class="['tag-list',el.isclick?'active':'']" v-for="(el,ind) in tags" :style="{'margin-right':(ind+1)%4==0?'0':'40rpx'}" @click="setTag(ind)">
{{el.name}}
</view>
</view>
</view>
<!-- <view class="xxlist">
<view class="scitemname">评分:</view>
<view class="scitemstarts">
<image class="star-image" :src="datas.score>0? selectedSrc:normalSrc" @click="chooseicon(ind,1)"></image>
<image class="star-image" :src="datas.score>1? selectedSrc:normalSrc" @click="chooseicon(ind,2)"></image>
<image class="star-image" :src="datas.score>2? selectedSrc:normalSrc" @click="chooseicon(ind,3)"></image>
<image class="star-image" :src="datas.score>3? selectedSrc:normalSrc" @click="chooseicon(ind,4)"></image>
<image class="star-image" :src="datas.score>4? selectedSrc:normalSrc" @click="chooseicon(ind,5)"></image>
</view>
</view> -->
<view class="tipj" @click="submit">提交评价</view>
</view>
</template>
<script>
var web = require('@/components/utils/request.js');
export default{
data(){
return{
placeholder:' '+" "+'评价一下吧',
normalSrc: '../../static/star-null-big_hong.png',
selectedSrc: '../../static/star-on-big_hong.png',
datas:{},
textInd:1,
id:0,
tags:[]
}
},
onLoad(options) {
this.id=options.id;
var pages = getCurrentPages();
var page = pages[pages.length - 1].route;
// this.share.path='/'+page+'?id='+this.id;
// this.shareline.query={id:this.id};
},
onReady() {
this.getadd();
// this.getTag();
},
methods:{
setTag(ind){
this.tags[ind].isclick=!this.tags[ind].isclick;
},
getTag(){
var json=this.datas;
var url='/comment/tag.jspx'
var para = {
id:this.id,
type:2
};
var that=this;
web.httpPost(this,url,para,function(res) {
if(res.data.status==200){
let json=res.data.data;
let tmp=[];
for(let i=0;i<json.length;i++){
tmp.push({
name:json[i],
isclick:false
})
}
that.tags=tmp;
}
})
},
submit(){
var url='comment.jspx'
var para = {
contentId:this.id,
score:5,
content:this.datas.assess,
flag:1
};
var that=this;
web.httpPost(this,url,para,function(res) {
if(res.data.status==200){
uni.showToast({
title: '评价成功'
});
setTimeout(function(){
uni.navigateBack({
delta:1
})
},1000)
}else{
uni.showToast({
title: res.data.message
});
}
})
},
//
chooseicon(ind,score) {
this.datas.score=score;
},
getadd(){
var url = '/newsdetail.jspx';
var para = {
id: this.id
};
var that=this;
web.httpPost(this,url,para,function(res) {
if(res.data.status==200){
var json=res.data;
json.assess='';
json.score=5;
that.datas=json;
}
})
},
getImg(url){
return 'https://cs.tour-ma.com/'+url
},
getImg1(url){
return 'https://cs.tour-ma.com/r/cms/www/m/changshu/'+url
}
}
}
</script>
<style lang="scss">
@import url("@/components/css/iconfont.css");
.tag{
margin-top: 15rpx;
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
.active{
font-size: 22rpx;
font-family: Microsoft YaHei;
font-weight: 400;
color: #FFFFFF;
background: #227BFB;
}
&-list{
padding: 5rpx 10rpx;
margin-bottom: 40rpx;
min-width: 108rpx;
height: 35rpx;
background: #E9F2FF;
border: 1rpx solid #66A6FF;
border-radius: 4rpx;
font-size: 22rpx;
font-family: Microsoft YaHei;
font-weight: 400;
color: #227BFB;
text-align: center;
}
}
.tipj{
font-size: 33rpx;
font-weight: 500;
color: #FFFFFF;
width: 438rpx;
height: 78rpx;
line-height: 78rpx;
background: linear-gradient(90deg, #FF540B 0%, #FFA200 100%);
border-radius: 39rpx;
margin-top: 83rpx;
text-align: center;
letter-spacing: 10rpx;
}
.imglist{
width: 650rpx;
// height: 200rpx;
display: flex;
align-items: center;
flex-wrap: wrap;
margin-top: 20rpx;
justify-content: space-between;
.sctp2{
width: 270rpx;
height: 200rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
image{
border-radius: 14rpx;
width: 270rpx;
height: 200rpx;
}
margin-bottom: 20rpx;
}
.sctp{
width: 270rpx;
height: 200rpx;
background-color: #FAFAFA;
border: 1px solid #EAEAEA;
border-radius: 14rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-bottom: 20rpx;
image{
width: 49rpx;
height: 47rpx;
}
.name{
font-size: 25rpx;
line-height: 25rpx;
margin-top: 20rpx;
color: #666666;
}
}
}
.tianxie{
width: 650rpx;
height: 200rpx;
margin-top:62rpx;
font-size: 26rpx;
.active{
width: 650rpx;
height: 200rpx;
// background: url(https://xcx.wfwhy.gov.cn/r/cms/www/weifang/ximg/bh-pj-icon-pj.png) no-repeat;
background-position: left 3rpx;
background-size: 26rpx;
}
.nactive{
width: 650rpx;
height: 200rpx;
}
}
.xxlist{
height: 40rpx;
display: flex;
align-items: center;
margin-top: 45rpx;
width: 650rpx;
.scitemname{
font-size: 33rpx;
line-height: 33rpx;
font-weight: 500;
color: #222222;
margin-right: 28rpx;
}
.scitemstarts{
display: flex;
align-items: center;
.star-image{
width: 38rpx;
height: 37rpx;
margin-right: 11rpx;
}
}
}
.top{
display: flex;
flex-direction: row;
justify-content: space-around;
image{
width: 111rpx;
height: 111rpx;
background: #f1f1f1;
border-radius: 10rpx;
margin-right: 27rpx;
}
.cntern{
flex: 1;
.name{
font-size: 28rpx;
line-height: 28rpx;
color: #666666;
margin-top: 14rpx;
}
}
}
.detail{
width: 650rpx;
// min-height: 771rpx;
background: #FFFFFF;
border: 1rpx solid #f0f0f0;
border-radius: 17rpx;
padding: 28rpx 25rpx;
}
.content{
padding-top: 3vh;
height: 97vh;
width: 750rpx;
background: linear-gradient(0deg, #FFFFFF 0%, #F9F9F9 100%);
display: flex;
flex-direction: column;
align-items: center;
padding-bottom: 80rpx;
}
</style>

304
other/public.vue

@ -0,0 +1,304 @@
<template>
<view class="centent" :style="{ height: height + 'px' }">
<back :info="info"></back>
<view class="main-top">
<image v-if="xllist.channel_id==75" :src="xllist.midPic!=''? $geturl(xllist.midPic):$getimg('noimg.png')" mode="" class="top-pos1"></image>
<image v-if="xllist.channel_id==85" :src="$geturl(xllist.midPic)" mode="" class="top-pos1"></image>
<image v-if="xllist.channel_id==93" :src="$geturl(xllist.midPic)" mode="" class="top-pos1"></image>
</view>
<view class="main">
<view class="main-top1">
<view class="top1-sdf">
{{ xllist.title }}
</view>
</view>
<view class="xqtimeAll">
<view class="xqtimeleft">
<text class="sjsj">{{ xllist.pubDate }}</text>
</view>
<view class="xqtimeright">
<text class="sjsj">{{ xllist.upsNum }}</text>
</view>
</view>
<view class="main-top2">
<view class="top2-sdf" v-html="xllist.content">
</view>
</view>
<view class="dzys active" v-if="!candz">
<image :src="$getimg('icon-dzxz.png')" class="imgs"></image>
<text class="text active">{{ xllist.upsNum }}</text>
</view>
<view class="dzys" v-if="candz" @click="dzsclick">
<image :src="$getimg('icon-dianzhan.png')" class="imgs"></image>
<text class="text">{{ xllist.upsNum }}</text>
</view>
</view>
</view>
</template>
<script>
import back from '@/components/html/back.vue';
var web = require('@/components/utils/request.js');
export default {
components: {
back
},
data() {
return {
info: {
color: true,num:false
},
id: 0,
xllist: {},
height: 800,
candz:true, //
dzarr:[], //
}
},
onLoad(option) {
var that = this;
uni.getSystemInfo({
//
success(res) {
that.height = res.windowHeight - 40;
}
});
this.id = option.id;
var pages = getCurrentPages();
var page = pages[pages.length - 1].route;
// this.share.path='/'+page+'?id='+this.id;
// this.shareline.query={id:this.id}
this.getdatas();
try {
let tmpstr = uni.getStorageSync('dianzan'); //
if(tmpstr){
this.dzarr = tmpstr.split(',');
}
if(this.dzarr.indexOf(this.id) > -1){ //
this.candz = false; //
}
} catch (e) {
this.candz = true;
}
},
onPageScroll(e) {
if(e.scrollTop>50&&!this.info.num){
this.info.num=true;
}
if(e.scrollTop<50&&this.info.num){
this.info.num=false;
}
},
methods: {
getdatas() {
//
var para = {
id: this.id
};
var url = '/newsdetail.jspx';
var that = this;
web.httpPost(that, url, para, function(res) {
if (res.data.status == 200) {
var tmp = res.data;
tmp.content = tmp.content.replace(/src="/gi, 'src="https://cs.tour-ma.com/');
tmp.content = tmp.content.replace(/<img[^>]*>/gi, function(match, capture) {
return match.replace(/style\s*?=\s*?([‘"])[\s\S]*?\1/gi, 'style="max-width:100%;height:auto;width:100%;"'); // style
});
tmp.content = tmp.content.replace(/<img[^>]*>/gi, function(match, capture) {
return match.replace(/<img /gi, '<img style="max-width:100%;height:auto;width:100%;"'); // style
});
that.xllist = tmp;
console.log(that.xllist.content);
}
});
},
dzsclick() {
//
try {
if(this.dzarr.indexOf(this.id) > -1){
this.candz = false;
}else{
this.dzarr.push(this.id);
uni.setStorageSync('dianzan',this.dzarr.toString()); //
this.candz = false; //
}
} catch (e) {
this.candz = true;
}
var url = '/content_up.jspx?contentId=' + this.id;
var para = {};
var that = this;
web.httpGejqr(that, url, para, function(res) {
uni.showToast({
title: '点赞成功!'
});
that.getdatas();
});
}
}
}
</script>
<style lang="scss">
$url:'https://cs.tour-ma.com/r/cms/www/m/changshu/';
page {
width: 100%;
height: 100%;
background: #FFFFFF;
}
.main-top {
width: 750rpx;
// height: 100%;
position: relative;
.top-pos {
width: 100%;
height: 240rpx;
}
.top-pos1 {
width: 100%;
height: 540rpx;
}
}
.main {
width: 750rpx;
height: 100%;
background: #FFFFFF;
.main-top1 {
width: 100%;
height: auto;
margin: 30rpx auto 0rpx auto;
text-align: center;
.top1-sdf {
font-size: 40rpx;
font-family: PingFang SC;
font-weight: bold;
color: #333333;
line-height: 1.6;
margin: 0rpx auto;
width: 80%;
}
}
.main-top2 {
width: 100%;
height: auto;
margin: 30rpx 0rpx 0 0rpx;
.top2-sdf {
width: 90%;
font-size: 28rpx;
font-family: PingFang SC;
// font-weight: 500;
color: #666666;
line-height: 1.8;
margin: 0rpx auto;
}
}
.main-top3{
width: 100%;
height: auto;
margin: 30rpx 0rpx 0 0rpx;
font-size: 24rpx;
font-family: PingFang SC;
font-weight: bold;
color: #666666;
line-height: 48rpx;
.top3-sdf{
margin-left: 30rpx;
}
.sdf-count{
margin: 0 30rpx 0 40rpx;
}
.top3-sdf1{
margin-left: 30rpx;
}
.sdf-count1{
margin: 0 30rpx 0 40rpx;
}
}
}
.xqtimeAll {
width: 90%;
margin: 40upx auto;
color: #b7987c;
font-size: 24upx;
overflow: hidden;
.sjsj {
vertical-align: middle;
color: #666666;
}
.xqtimeleft {
float: left;
.sjsj{
&::before {
content: '';
display: inline-block;
width: 28upx;
height: 28upx;
background: url($url+'shijian.png') no-repeat;
background-size: 100% auto;
margin-right: 10upx;
margin-top: -5upx;
vertical-align: middle;
}
}
}
.xqtimeright {
float: right;
.sjsj{
&::before {
content: '';
display: inline-block;
width: 28upx;
height: 28upx;
background: url($url+'icon-dianzhan.png') no-repeat;
background-size: 100% auto;
margin-right: 10upx;
margin-top: -5upx;
vertical-align: middle;
}
}
}
}
.dzys{
width: 168upx;
height: 168upx;
position: absolute;
left: 50%;
border-radius: 50%;
margin: 20upx 0upx 100upx -94upx;
border: 1rpx solid #666666;
background-size: 100% auto;
.imgs {
width: 60upx;
height: 60upx;
margin: 22% auto 2%;
display: block;
opacity: 0.7;
}
.text {
display: block;
text-align: center;
font-size: 32rpx;
color: #666666;
}
}
.dzys.active{
border: 1rpx solid #ff3335;
.imgs {
opacity: 1;
}
.text{
color: #ff3335;
}
}
</style>

522
other/scenic.vue

@ -0,0 +1,522 @@
<template>
<view>
<view class="list_container">
<u-dropdown title-size="28" :class="['u-dropdown',istop?'u-dropdown-top':'']"
:style="{top:istop?(navHeight-2)+'px':0}">
<u-dropdown-item v-model="value1" :title="options1[value1].label" :options="options1"
@change="handleChange"></u-dropdown-item>
<u-dropdown-item v-model="value2" :title="options2[value2].label" :options="options2"
@change="handleChange"></u-dropdown-item>
<u-dropdown-item v-model="value3" :title="options3[value3].label" :options="options3"
@change="handleChange"></u-dropdown-item>
</u-dropdown>
<view class="card-menu" v-for="(item, index) in waresList" :key="item.uid"
:style="{ borderTop: index == 0 ? '1rpx solid #eeeeee' : '' }">
<view class="img-container">
<u-image width="200rpx" height="200rpx" border-radius="10rpx"
:src="item.logo==''?$getimg('noimg.png'):$geturl(item.logo)"></u-image>
<view class="img-mask">
<image :src="$getimg('icon-juli.png')" mode="widthFix"></image>
<text>{{ item.distance }}</text>
</view>
</view>
<view class="detail-container" @click="msto(item.wxMiNiAppId,item.wxMiNiPath,item.uid)">
<view class="title-container">
<view class="content">
<text class="title">{{ item.name }}</text>
<view class="level" v-if="[1, 2, 3, 4, 5].includes(item.level)">{{ item.level }}A</view>
<!-- <view class="for20Seconds" v-if="item.price!=0">20秒入园</view> -->
</view>
<!-- 收藏 -->
<!-- <u-icon @click.native.stop="clickOnCollection(item)" style="margin-right: 6rpx;"
name="star-fill" size="35" :color="item.isCollect ? '#FF540B' : '#e5e5e5'"></u-icon> -->
</view>
<view class="info-container">
<view class="info">
<view class="gradePrice">
<view class="lbradd">{{item.address}}</view>
</view>
<tags-list :list="item.scapeType"></tags-list>
<view class="price-container">
<!-- <text class="name">错峰价</text> -->
<text class="price-name" v-if="item.ticketPrice==0&&item.price==0">暂无价格</text>
<text v-if="item.ticketPrice!=0">
<text class="symbol">¥</text>
<text class="price">{{item.ticketPrice}}</text>
<!-- <text>0</text> -->
<text class="qi"></text>
</text>
<!-- <text v-if="item.price!=0" class="oldPrice">{{item.price}}</text> -->
<text v-if="item.wxMiNiAppId!=''&& item.wxMiNiPath!=''" class="oldPrice" >立即预订</text>
</view>
</view>
<comfort-page :value="item.comfortPer" :name="item.comfort"></comfort-page>
</view>
</view>
</view>
<!-- <u-loadmore v-if="waresList.length > 0" :status="status" :load-text="loadText" /> -->
</view>
<u-toast ref="uToast" />
</view>
</template>
<script>
import back from '@/components/html/back.vue';
var web = require('@/components/utils/request.js');
// import comfortPage from './components/comfort-page';
import tagsList from '@/components/tags-list';
import {
mapGetters
} from 'vuex'
export default {
components: {
back,
tagsList,
// comfortPage
},
data() {
return {
value1: 0,
value2: 0,
value3: 0,
options1: [{
label: '景区等级',
value: 0,
type:-1
}, {
label: '5A级',
value: 1,
type:5
}, {
label: '4A级',
value: 2,
type:4
}, {
label: '3A级',
value: 3,
type:3
},
// {
// label: '2A',
// value: 4,
// type:2
// },
{
label: '未评级',
value: 4,
type:0
}],
options2: [{
label: '景区类型',
value: 0,
type: ''
}, {
label: '景区景点',
value: 1,
type: '景区景点'
}, {
label: '红色旅游',
value: 2,
type: '红色旅游'
} , {
label: '乡村旅游',
value: 3,
type: '乡村旅游'
}
// {
// label: '',
// value: 4,
// type: ''
// },
],
options3: [{
label: '智能排序',
value: 0
}, {
label: '等级优先',
value: 1
}, {
label: '低价优先',
value: 2
}, {
label: '高价优先',
value: 3
}, {
label: '距离优先',
value: 4
}],
params: {
pageNo: 1,
pageSize: 100,
// type: 1,
radius: 0,
ctgId: 1,
level: "0",
name: "",
scapeType: "",
orderBy: 0,
},
url: {
list: 'wareInfo/list4.jspx',
collection: 'front/jcCollect/wares_collect'
},
waresList: [],
listpages: 0,
status: 'loading',
loadText: {
// loadmore: '~~',
loading: '努力加载中~~',
nomore: '已经到底了~~'
},
istop: false,
};
},
mounted() {
this.getList();
},
onReachBottom() {
this.getMore();
},
computed: {
...mapGetters(['navHeight'])
},
onPageScroll(e) {
if (e.scrollTop > 40) {
this.istop = true;
} else {
this.istop = false;
}
},
methods: {
msto(wxMiNiAppId, wxMiNiPath, uid) {
if (wxMiNiAppId && wxMiNiPath) {
if (wxMiNiAppId == 99999999) {
uni.showModal({
title: '提示',
content: '因景区仅提供网页预订,预约地址已复制到剪贴板,请在浏览器中粘贴打开',
showCancel: false,
cancelText: '',
confirmText: '确定',
success: res => {
if (res.confirm) {
uni.setClipboardData({
data: link, //
success: (e) => { //
console.log(e)
}
});
}
}
});
} else {
this.toOther(wxMiNiAppId, wxMiNiPath)
}
} else {
console.log(2)
uni.navigateTo({
url: `../pages/jqDet?uid=${uid}`
});
}
},
toOther(wxMiNiAppId, wxMiNiPath) {
uni.navigateToMiniProgram({
appId: wxMiNiAppId,
path: wxMiNiPath,
envVersion: 'release',
extraData: {},
success(res) {
console.log("chenggong");
},
fail(e) {
console.log(e);
}
})
},
getList() {
// this.params.level = this.value1;
for (var i = 0; i < this.options1.length; i++) {
if (this.value1 == this.options1[i].value) {
this.params.level = this.options1[i].type
}
}
for (var i = 0; i < this.options2.length; i++) {
if (this.value2 == this.options2[i].value) {
this.params.scapeType = this.options2[i].type
}
}
this.params.orderBy = this.value3;
web.httpPost(this, this.url.list, this.params, res => {
let waresList = res.data.results;
let pages = res.data.results.length;
this.listpages = pages;
if (this.params.pageNo == 1) {
this.waresList = waresList;
console.log(1)
} else {
this.waresList = this.waresList.concat(waresList);
console.log(2)
}
});
},
// getMore() {
// if (this.params.pageNo >= this.listpages) return (this.status = 'nomore');
// this.status = 'loading';
// this.params.pageNo = ++this.params.pageNo;
// if (this.params.pageNo >= this.listpages) this.status = 'nomore';
// else this.status = 'loading';
// this.getList();
// },
handleChange(e) {
this.getList();
},
}
};
</script>
<style lang="scss">
@import "uview-ui/index.scss";
.u-dropdown-top {
position: fixed;
background: #fff;
display: flex;
flex-direction: row;
justify-content: space-between;
width: 750rpx;
top: 120rpx;
z-index: 9;
}
.list_container {
background: #F3F8F9;
.card-menu {
// width: 750rpx;
height: 250rpx;
background: #ffffff;
padding: 25rpx;
margin-bottom: 20rpx;
display: flex;
.img-container {
position: relative;
color: #ffffff;
font-weight: 400;
font-family: Microsoft YaHei;
image {
width: 200rpx;
height: 200rpx;
border-radius: 10rpx;
}
.img-mask {
position: absolute;
width: 180rpx;
height: 50rpx;
border-radius: 0rpx 0rpx 10rpx 10rpx;
bottom: 0rpx;
display: flex;
align-items: center;
padding-left: 20rpx;
background: linear-gradient(right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.5));
font-size: 24rpx;
image {
width: 30rpx;
height: 30rpx;
}
text {
margin-left: 9rpx;
}
}
}
.detail-container {
margin-left: 15rpx;
width: calc(100% - 220rpx);
display: flex;
flex-direction: column;
.for20Seconds {
background: #fff5f5;
border: 1rpx solid #ff948e;
border-radius: 4rpx;
font-size: 20rpx;
font-family: PingFang SC;
font-weight: 400;
color: #ff0c00;
text-align: center;
margin-left: 10rpx;
padding: 2rpx 9rpx 2rpx 10rpx;
}
.info-container {
display: flex;
justify-content: space-between;
margin-top: 15rpx;
.info {
display: flex;
flex-direction: column;
width: 100%;
.gradePrice {
display: flex;
justify-content: space-between;
align-items: center;
font-family: PingFang SC;
font-weight: 400;
font-size: 24rpx;
.lbradd {
font-size: 24upx;
color: #666666;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
&::before {
content: '';
display: inline-block;
width: 18upx;
height: 22upx;
background: url('https://cs.tour-ma.com/r/cms/www/m/changshu/ra-liebiao-icon-dizhi.png') no-repeat;
background-size: 100%;
margin-right: 12upx;
}
}
.content {
display: flex;
align-items: center;
.review,
.collection {
color: #666666;
text {
font-size: 25rpx;
font-family: DIN;
font-weight: 500;
color: #333333;
margin-right: 10rpx;
}
}
.divider {
width: 3rpx;
height: 17rpx;
background: #cccccc;
margin-left: 15rpx;
margin-right: 13rpx;
}
}
.price {
text-decoration: line-through;
color: #979797;
}
}
}
}
.tags-container {
margin-top: 16rpx;
}
.price-container {
line-height: 50rpx;
margin-top: 5rpx;
color: #ff540b;
font-size: 24rpx;
.symbol {
margin-right: 5rpx;
}
.price {
font-size: 42rpx;
}
.qi {
color: #666666;
font-size: 20rpx;
margin-left: 10rpx;
}
.price-name {
color: #66666670;
font-size: 22rpx;
font-weight: 400;
}
.oldPrice {
// margin-left: 9rpx;
// margin-left: 54%;
border-color: #ffffff00;
padding: 1rpx 12rpx;
background: #fbe0d4;
position: absolute;
right: 30rpx;
}
}
}
}
}
.title-container {
display: flex;
align-items: center;
image {
width: 54rpx;
height: 40rpx;
}
.name {
font-size: 48rpx;
font-family: PingFang SC;
font-weight: bold;
color: #333333;
margin-left: 17rpx;
margin-right: 18rpx;
}
.level {
width: 60rpx;
height: 40rpx;
text-align: center;
background: #fff1eb;
border-radius: 4rpx;
font-size: 28rpx;
font-family: DIN;
font-weight: bold;
color: #ff540b;
margin-left: 10rpx;
}
.content {
display: flex;
align-items: center;
.title {
color: #2a2a2a;
font-weight: bold;
font-size: 36rpx;
font-family: PingFang SC;
}
}
}
</style>

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

Loading…
Cancel
Save