diff --git a/app.json b/app.json index 7e6dcb9..2976a53 100644 --- a/app.json +++ b/app.json @@ -69,19 +69,23 @@ "pages/signup/index", "pages/signup/status", "pages/clues/index", - "pages/clues/detail/index" + "pages/clues/detail/index", + "pages/score/index", + "pages/score/detail" ], "permission": { "scope.userLocation": { "desc": "你的位置信息将用于线下车卖场到访签到" } }, - "echarts": [{ - "root": "ecCanvas", - "pages": [ - "pages/index/index" - ] - }], + "echarts": [ + { + "root": "ecCanvas", + "pages": [ + "pages/index/index" + ] + } + ], "window": { "backgroundTextStyle": "light", "navigationBarBackgroundColor": "#fff", @@ -104,7 +108,7 @@ "navigateToMiniProgramAppIdList": [ "wx98e64c11aac45966" ], - "requiredPrivateInfos":[ + "requiredPrivateInfos": [ "getLocation" ] } \ No newline at end of file diff --git a/commons/css/base/background.wxss b/commons/css/base/background.wxss index 9b07ffe..b79a345 100644 --- a/commons/css/base/background.wxss +++ b/commons/css/base/background.wxss @@ -39,4 +39,16 @@ .bg-ffedeb{background-color:#ffedeb;} .bg-f0f4ff{background-color:#f0f4ff;} -.bg-fffaf3{background-color:#fffaf3;} \ No newline at end of file +.bg-fffaf3{background-color:#fffaf3;} + +.bg-size-cover{background-size: cover;} +.bg-size-fullwidth{background-size: 100% auto;} +.bg-size-fullheight{background-size: auto 100%;} +.bg-no-repeat{background-repeat: no-repeat;} +.bg-repeat-x{background-repeat:repeat-x;} +.bg-repeat-y{background-repeat: repeat-y;} +.bg-pos-top{background-position: top center;} +.bg-pos-bottom{background-position: bottom center;} +.bg-pos-center{background-position: center;} + +.bg-custom-linear-bottom{background-image: linear-gradient(to bottom, var(--linearcolor), transparent);} \ No newline at end of file diff --git a/commons/css/base/border.wxss b/commons/css/base/border.wxss index 72308f0..4b48aed 100644 --- a/commons/css/base/border.wxss +++ b/commons/css/base/border.wxss @@ -25,3 +25,5 @@ .bbs-1-474a65{border-bottom:#474a65 1rpx solid; box-sizing:border-box;} .last-b-none:last-child{border-bottom:none;} + +.bbs-1-f6{border-bottom:#f6f6f6 1rpx solid;box-sizing:border-box;} diff --git a/commons/css/base/imgSize.wxss b/commons/css/base/imgSize.wxss index 0b9b9fa..ab94f44 100644 --- a/commons/css/base/imgSize.wxss +++ b/commons/css/base/imgSize.wxss @@ -1,4 +1,7 @@ .img-24x24{width:24rpx;height:24rpx;box-sizing:border-box;} +.img-28x28{width:28rpx;height:28rpx;box-sizing:border-box;} +.img-32x32{width:32rpx;height:32rpx;box-sizing:border-box;} +.img-38x38{width:38rpx;height:38rpx;box-sizing:border-box;} .img-45x45{width:45rpx;height:45rpx;box-sizing:border-box;} .img-50x50{width:50rpx;height:50rpx;box-sizing:border-box;} .img-55x55{width:55rpx;height:55rpx;box-sizing:border-box;} @@ -15,6 +18,7 @@ .img-130x130{width:130rpx;height:130rpx;box-sizing:border-box;} .img-135x135{width:135rpx;height:135rpx;box-sizing:border-box;} .img-140x140{width:140rpx;height:140rpx;box-sizing:border-box;} +.img-154x154{width:154rpx;height:154rpx;box-sizing:border-box;} .img-160x160{width:160rpx;height:160rpx;box-sizing:border-box;} .img-190x175{width:190rpx;height:175rpx;box-sizing:border-box;} .img-200x180{width:200rpx;height:180rpx;box-sizing:border-box;} diff --git a/commons/css/base/layout.wxss b/commons/css/base/layout.wxss index db9390b..2191d29 100644 --- a/commons/css/base/layout.wxss +++ b/commons/css/base/layout.wxss @@ -20,8 +20,16 @@ .overflowXhidden{overflow-x:hidden;} .overflow-initial{overflow:initial;} .opacity-0{opacity:0;} +.opacity-5{opacity:.05;} +.opacity-10{opacity:.1;} +.opacity-20{opacity:.2;} +.opacity-30{opacity:.3;} +.opacity-40{opacity:.4;} .opacity-50{opacity:.5;} +.opacity-60{opacity:.6;} +.opacity-70{opacity:.7;} .opacity-80{opacity:.8;} +.opacity-90{opacity:.9;} /*line-clamp*/ .line-clamp-2{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;} @@ -44,6 +52,7 @@ .fn-flex-around{justify-content: space-around;}/*各项周围留有空白*/ .fn-flex-middle{align-items:center;}/*交叉轴上上下居中对齐*/ .fn-flex-center{justify-content: center;}/*居中对齐*/ +.fn-flex-column{flex-direction:column;} .wp25{width:25%;box-sizing:border-box;} .wp31{width:31%;box-sizing:border-box;} @@ -135,4 +144,6 @@ .safe-pb{padding-bottom:calc(constant(safe-area-inset-bottom) - 15px );padding-bottom:calc(env(safe-area-inset-bottom) - 15px);} .datecell{width:14.28%;padding:30rpx 0;text-align:center;box-sizing:border-box;} -.datecell.active{background-color:#36afa2;color:#fff;} \ No newline at end of file +.datecell.active{background-color:#36afa2;color:#fff;} + +.transition-all{transition: all .2s ease;} \ No newline at end of file diff --git a/commons/css/base/margin.wxss b/commons/css/base/margin.wxss index 8584c59..7ddef5f 100644 --- a/commons/css/base/margin.wxss +++ b/commons/css/base/margin.wxss @@ -61,6 +61,7 @@ .mr50{margin-right:50rpx;} .mr60{margin-right:60rpx;} .mr70{margin-right:70rpx;} +.mr100{margin-right:100rpx;} .mr150{margin-right:150rpx;} .mr160{margin-right:160rpx;} .mr170{margin-right:170rpx;} diff --git a/commons/js/config.js b/commons/js/config.js index b9778ce..9a150cb 100644 --- a/commons/js/config.js +++ b/commons/js/config.js @@ -189,6 +189,8 @@ api = { //远程图片存储地址 var imgUrl = 'https://qs.haodian.cn/wechat_app/lichebao/' +var licheImgUrl = 'https://img.liche.cn/lichebao/' + /** * 默认头像 @@ -238,6 +240,7 @@ export default { version, app_id, imgUrl, + licheImgUrl, defaultAvartar, elementScrollTop, bookTmpId, diff --git a/components/mp-html/index.js b/components/mp-html/index.js new file mode 100644 index 0000000..1d9caec --- /dev/null +++ b/components/mp-html/index.js @@ -0,0 +1,393 @@ +/*! + * mp-html v2.4.0 + * https://github.com/jin-yufeng/mp-html + * + * Released under the MIT license + * Author: Jin Yufeng + */ +const Parser = require('./parser') +const plugins = [] + +Component({ + data: { + nodes: [] + }, + properties: { + /** + * @description 容器的样式 + * @type {String} + */ + containerStyle: String, + + /** + * @description 用于渲染的 html 字符串 + * @type {String} + */ + content: { + type: String, + value: '', + observer (content) { + this.setContent(content) + } + }, + + /** + * @description 是否允许外部链接被点击时自动复制 + * @type {Boolean} + * @default true + */ + copyLink: { + type: Boolean, + value: true + }, + + /** + * @description 主域名,用于拼接链接 + * @type {String} + */ + domain: String, + + /** + * @description 图片出错时的占位图链接 + * @type {String} + */ + errorImg: String, + + /** + * @description 是否开启图片懒加载 + * @type {Boolean} + * @default false + */ + lazyLoad: Boolean, + + /** + * @description 图片加载过程中的占位图链接 + * @type {String} + */ + loadingImg: String, + + /** + * @description 是否在播放一个视频时自动暂停其他视频 + * @type {Boolean} + * @default true + */ + pauseVideo: { + type: Boolean, + value: true + }, + + /** + * @description 是否允许图片被点击时自动预览 + * @type {Boolean} + * @default true + */ + previewImg: { + type: Boolean, + value: true + }, + + /** + * @description 是否给每个表格添加一个滚动层使其能单独横向滚动 + * @type {Boolean} + * @default false + */ + scrollTable: Boolean, + + /** + * @description 是否开启长按复制 + * @type {Boolean | String} + * @default false + */ + selectable: null, + + /** + * @description 是否将 title 标签的内容设置到页面标题 + * @type {Boolean} + * @default true + */ + setTitle: { + type: Boolean, + value: true + }, + + /** + * @description 是否允许图片被长按时显示菜单 + * @type {Boolean} + * @default true + */ + showImgMenu: { + type: Boolean, + value: true + }, + + /** + * @description 标签的默认样式 + * @type {Object} + */ + tagStyle: Object, + + /** + * @description 是否使用锚点链接 + * @type {Boolean | Number} + * @default false + */ + useAnchor: null + }, + + created () { + this.plugins = [] + for (let i = plugins.length; i--;) { + this.plugins.push(new plugins[i](this)) + } + + // #ifdef MP-ALIPAY + if (this.properties.content) { + this.setContent(this.properties.content) + } + // #endif + }, + + // #ifdef MP-ALIPAY + didUpdate (e) { + if (e.content !== this.properties.content) { + this.setContent(this.properties.content) + } + }, + // #endif + + detached () { + // 注销插件 + this._hook('onDetached') + }, + + methods: { + /** + * @description 将锚点跳转的范围限定在一个 scroll-view 内 + * @param {Object} page scroll-view 所在页面的示例 + * @param {String} selector scroll-view 的选择器 + * @param {String} scrollTop scroll-view scroll-top 属性绑定的变量名 + */ + in (page, selector, scrollTop) { + if (page && selector && scrollTop) { + this._in = { + page, + selector, + scrollTop + } + } + }, + + /** + * @description 锚点跳转 + * @param {String} id 要跳转的锚点 id + * @param {Number} offset 跳转位置的偏移量 + * @returns {Promise} + */ + navigateTo (id, offset) { + return new Promise((resolve, reject) => { + if (!this.properties.useAnchor) { + reject(Error('Anchor is disabled')) + return + } + // 跨组件选择器 + const deep = + // #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO + '>>>' + // #endif + // #ifdef MP-BAIDU || MP-ALIPAY + ' ' // eslint-disable-line + // #endif + const selector = wx.createSelectorQuery() + // #ifndef MP-ALIPAY + .in(this._in ? this._in.page : this) + // #endif + .select((this._in ? this._in.selector : '._root') + (id ? `${deep}#${id}` : '')).boundingClientRect() + if (this._in) { + selector.select(this._in.selector).scrollOffset() + .select(this._in.selector).boundingClientRect() + } else { + // 获取 scroll-view 的位置和滚动距离 + selector.selectViewport().scrollOffset() // 获取窗口的滚动距离 + } + selector.exec(res => { + if (!res[0]) { + reject(Error('Label not found')) + return + } + const scrollTop = res[1].scrollTop + res[0].top - (res[2] ? res[2].top : 0) + (offset || parseInt(this.properties.useAnchor) || 0) + if (this._in) { + // scroll-view 跳转 + this._in.page.setData({ + [this._in.scrollTop]: scrollTop + }) + } else { + // 页面跳转 + wx.pageScrollTo({ + scrollTop, + duration: 300 + }) + } + resolve() + }) + }) + }, + + /** + * @description 获取文本内容 + * @returns {String} + */ + getText (nodes) { + let text = ''; + (function traversal (nodes) { + for (let i = 0; i < nodes.length; i++) { + const node = nodes[i] + if (node.type === 'text') { + text += node.text.replace(/&/g, '&') + } else if (node.name === 'br') { + text += '\n' + } else { + // 块级标签前后加换行 + const isBlock = node.name === 'p' || node.name === 'div' || node.name === 'tr' || node.name === 'li' || (node.name[0] === 'h' && node.name[1] > '0' && node.name[1] < '7') + if (isBlock && text && text[text.length - 1] !== '\n') { + text += '\n' + } + // 递归获取子节点的文本 + if (node.children) { + traversal(node.children) + } + if (isBlock && text[text.length - 1] !== '\n') { + text += '\n' + } else if (node.name === 'td' || node.name === 'th') { + text += '\t' + } + } + } + })(nodes || this.data.nodes) + return text + }, + + /** + * @description 获取内容大小 + * @returns {Promise} + */ + getRect () { + return new Promise((resolve, reject) => { + wx.createSelectorQuery() + // #ifndef MP-ALIPAY + .in(this) + // #endif + .select('._root').boundingClientRect().exec(res => res[0] ? resolve(res[0]) : reject(Error('Root label not found'))) + }) + }, + + /** + * @description 暂停播放媒体 + */ + pauseMedia () { + for (let i = (this._videos || []).length; i--;) { + this._videos[i].pause() + } + }, + + /** + * @description 设置媒体播放速率 + * @param {Number} rate 播放速率 + */ + setPlaybackRate (rate) { + this.playbackRate = rate + for (let i = (this._videos || []).length; i--;) { + this._videos[i].playbackRate(rate) + } + }, + + /** + * @description 设置富文本内容 + * @param {string} content 要渲染的 html 字符串 + * @param {boolean} append 是否在尾部追加 + */ + setContent (content, append) { + if (!this.imgList || !append) { + this.imgList = [] + } + this._videos = [] + + const data = {} + const nodes = new Parser(this).parse(content) + // 尾部追加内容 + if (append) { + for (let i = this.data.nodes.length, j = nodes.length; j--;) { + data[`nodes[${i + j}]`] = nodes[j] + } + } else { + data.nodes = nodes + } + + this.setData(data, + // #ifndef MP-TOUTIAO + () => { + this._hook('onLoad') + this.triggerEvent('load') + } + // #endif + ) + + // #ifdef MP-TOUTIAO + this.selectComponent('#_root', child => { + child.root = this + this._hook('onLoad') + this.triggerEvent('load') + }) + // #endif + + if (this.properties.lazyLoad || this.imgList._unloadimgs < this.imgList.length / 2) { + // 设置懒加载,每 350ms 获取高度,不变则认为加载完毕 + let height + const callback = rect => { + // 350ms 总高度无变化就触发 ready 事件 + if (rect.height === height) { + this.triggerEvent('ready', rect) + } else { + height = rect.height + setTimeout(() => { + this.getRect().then(callback) + }, 350) + } + } + this.getRect().then(callback) + } else { + // 未设置懒加载,等待所有图片加载完毕 + if (!this.imgList._unloadimgs) { + this.getRect(rect => { + this.triggerEvent('ready', rect) + }) + } + } + }, + + /** + * @description 调用插件的钩子函数 + * @private + */ + _hook (name) { + for (let i = plugins.length; i--;) { + if (this.plugins[i][name]) { + this.plugins[i][name]() + } + } + }, + + // #ifndef MP-TOUTIAO + /** + * @description 添加子组件 + * @private + */ + _add (e) { + e + // #ifndef MP-ALIPAY + .detail + // #endif + .root = this + } + // #endif + } +}) diff --git a/components/mp-html/index.json b/components/mp-html/index.json new file mode 100644 index 0000000..f55f207 --- /dev/null +++ b/components/mp-html/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "node": "./node/node" + } +} \ No newline at end of file diff --git a/components/mp-html/index.wxml b/components/mp-html/index.wxml new file mode 100644 index 0000000..6e98f0a --- /dev/null +++ b/components/mp-html/index.wxml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/components/mp-html/index.wxss b/components/mp-html/index.wxss new file mode 100644 index 0000000..abf4467 --- /dev/null +++ b/components/mp-html/index.wxss @@ -0,0 +1,2 @@ +._root{padding:0;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;} +._select{-webkit-user-select:text;user-select:text;} \ No newline at end of file diff --git a/components/mp-html/node/node.js b/components/mp-html/node/node.js new file mode 100644 index 0000000..e205176 --- /dev/null +++ b/components/mp-html/node/node.js @@ -0,0 +1,237 @@ +/** + * @fileoverview 递归子组件,用于显示节点树 + */ +Component({ + data: { + ctrl: {}, // 控制信号 + // #ifdef MP-WEIXIN + isiOS: wx.getSystemInfoSync().system.includes('iOS') + // #endif + }, + properties: { + childs: Array, // 子节点列表 + opts: Array // 设置 [是否开启懒加载, 加载中占位图, 错误占位图, 是否使用长按菜单] + }, + options: { + addGlobalClass: true + }, + // #ifndef MP-TOUTIAO + attached () { + // #ifndef MP-ALIPAY + this.triggerEvent('add', this, { + bubbles: true, + composed: true + }) + // #endif + // #ifdef MP-ALIPAY + // this.props.onAdd(this) + // #endif + }, + // #endif + methods: { + noop () { }, + /** + * @description 获取标签 + * @param {String} path 路径 + */ + getNode (path) { + try { + const nums = path.split('_') + let node = this.properties.childs[nums[0]] + for (let i = 1; i < nums.length; i++) { + node = node.children[nums[i]] + } + return node + } catch { + return { + text: '', + attrs: {}, + children: [] + } + } + }, + /** + * @description 播放视频事件 + * @param {Event} e + */ + play (e) { + this.root.triggerEvent('play') + if (this.root.properties.pauseVideo) { + let flag = false + const id = e.target.id + for (let i = this.root._videos.length; i--;) { + if (this.root._videos[i].id === id) { + flag = true + } else { + this.root._videos[i].pause() // 自动暂停其他视频 + } + } + // 将自己加入列表 + if (!flag) { + const ctx = wx.createVideoContext(id + // #ifndef MP-BAIDU + , this + // #endif + ) + ctx.id = id + if (this.root.playbackRate) { + ctx.playbackRate(this.root.playbackRate) + } + this.root._videos.push(ctx) + } + } + }, + + /** + * @description 图片点击事件 + * @param {Event} e + */ + imgTap (e) { + const node = this.getNode(e.target.dataset.i) + // 父级中有链接 + if (node.a) return this.linkTap(node.a) + if (node.attrs.ignore) return + this.root.triggerEvent('imgtap', node.attrs) + if (this.root.properties.previewImg) { + const current = + // #ifndef MP-ALIPAY + this.root.imgList[node.i] + // #endif + // #ifdef MP-ALIPAY + node.i // eslint-disable-line + // #endif + // 自动预览图片 + wx.previewImage({ + // #ifdef MP-WEIXIN + showmenu: this.root.properties.showImgMenu, + // #endif + // #ifdef MP-ALIPAY + enablesavephoto: this.root.properties.showImgMenu, + enableShowPhotoDownload: this.root.properties.showImgMenu, + // #endif + current, + urls: this.root.imgList + }) + } + }, + + /** + * @description 图片加载完成事件 + * @param {Event} e + */ + imgLoad (e) { + const i = e.target.dataset.i + const node = this.getNode(i) + let val + if (!node.w) { + val = e.detail.width + } else if ((this.properties.opts[1] && !this.data.ctrl[i]) || this.data.ctrl[i] === -1) { + // 加载完毕,取消加载中占位图 + val = 1 + } + if (val + // #ifdef MP-TOUTIAO + && val !== this.data.ctrl[i] // eslint-disable-line + // #endif + ) { + this.setData({ + ['ctrl.' + i]: val + }) + } + this.checkReady() + }, + + /** + * @description 检查是否所有图片加载完毕 + */ + checkReady () { + if (!this.root.properties.lazyLoad) { + this.root.imgList._unloadimgs -= 1 + if (!this.root.imgList._unloadimgs) { + setTimeout(() => { + this.root.getRect().then(rect => { + this.root.triggerEvent('ready', rect) + }) + }, 350) + } + } + }, + + /** + * @description 链接点击事件 + * @param {Event} e + */ + linkTap (e) { + const node = e.currentTarget ? this.getNode(e.currentTarget.dataset.i) : {} + const attrs = node.attrs || e + const href = attrs.href + this.root.triggerEvent('linktap', Object.assign({ + innerText: this.root.getText(node.children || []) // 链接内的文本内容 + }, attrs)) + if (href) { + if (href[0] === '#') { + // 跳转锚点 + this.root.navigateTo(href.substring(1)).catch(() => { }) + } else if (href.split('?')[0].includes('://')) { + // 复制外部链接 + if (this.root.properties.copyLink) { + wx.setClipboardData({ + data: href, + success: () => + wx.showToast({ + title: '链接已复制' + }) + }) + } + } else { + // 跳转页面 + wx.navigateTo({ + url: href, + fail () { + wx.switchTab({ + url: href, + fail () { } + }) + } + }) + } + } + }, + + /** + * @description 错误事件 + * @param {Event} e + */ + mediaError (e) { + const i = e.target.dataset.i + const node = this.getNode(i) + if (node.name === 'video' || node.name === 'audio') { + // 加载其他源 + let index = (this.data.ctrl[i] || 0) + 1 + if (index > node.src.length) { + index = 0 + } + if (index < node.src.length) { + return this.setData({ + ['ctrl.' + i]: index + }) + } + } else if (node.name === 'img') { + // 显示错误占位图 + if (this.properties.opts[2]) { + this.setData({ + ['ctrl.' + i]: -1 + }) + } + this.checkReady() + } + if (this.root) { + this.root.triggerEvent('error', { + source: node.name, + attrs: node.attrs, + errMsg: e.detail.errMsg + }) + } + } + } +}) diff --git a/components/mp-html/node/node.json b/components/mp-html/node/node.json new file mode 100644 index 0000000..7bebc99 --- /dev/null +++ b/components/mp-html/node/node.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "node": "./node" + } +} \ No newline at end of file diff --git a/components/mp-html/node/node.wxml b/components/mp-html/node/node.wxml new file mode 100644 index 0000000..1eeb6d4 --- /dev/null +++ b/components/mp-html/node/node.wxml @@ -0,0 +1,112 @@ + + + + // 行内标签列表 + var inlineTags = { + abbr: true, + b: true, + big: true, + code: true, + del: true, + em: true, + i: true, + ins: true, + label: true, + q: true, + small: true, + span: true, + strong: true, + sub: true, + sup: true + } + /** + * @description 判断是否为行内标签 + */ + module.exports = function (tagName, style) { + return inlineTags[tagName] || (style || '').indexOf('inline') !== -1 + } + + + + + + + + + +