Browse Source

first commit

master
jiannibang 4 years ago
commit
7f0a7b5f27
  1. 12
      README.md
  2. 80
      miniprogram/app.js
  3. 21
      miniprogram/app.json
  4. 4
      miniprogram/app.wxss
  5. 56
      miniprogram/components/ticket/ticket.js
  6. 4
      miniprogram/components/ticket/ticket.json
  7. 34
      miniprogram/components/ticket/ticket.wxml
  8. 288
      miniprogram/components/ticket/ticket.wxss
  9. 6191
      miniprogram/crypto-js.js
  10. BIN
      miniprogram/images/empty.png
  11. 80
      miniprogram/js/Coupons.js
  12. 39
      miniprogram/js/Malls.js
  13. 58
      miniprogram/js/cities.js
  14. 29
      miniprogram/js/libs.js
  15. 3
      miniprogram/js/qrcode.js
  16. 72
      miniprogram/pages/checked/index.js
  17. 5
      miniprogram/pages/checked/index.json
  18. 4
      miniprogram/pages/checked/index.wxml
  19. 7
      miniprogram/pages/checked/index.wxss
  20. BIN
      miniprogram/pages/index/arrow.png
  21. BIN
      miniprogram/pages/index/icon.png
  22. 187
      miniprogram/pages/index/index.js
  23. 6
      miniprogram/pages/index/index.json
  24. 37
      miniprogram/pages/index/index.wxml
  25. 226
      miniprogram/pages/index/index.wxss
  26. BIN
      miniprogram/pages/index/share.png
  27. BIN
      miniprogram/pages/index/shareImg.png
  28. BIN
      miniprogram/pages/index/tick.png
  29. BIN
      miniprogram/pages/index/ticket.png
  30. BIN
      miniprogram/pages/index/user-unlogin.png
  31. BIN
      miniprogram/pages/malls/close_white.png
  32. 174
      miniprogram/pages/malls/index.js
  33. 3
      miniprogram/pages/malls/index.json
  34. 57
      miniprogram/pages/malls/index.wxml
  35. 316
      miniprogram/pages/malls/index.wxss
  36. BIN
      miniprogram/pages/malls/pos.png
  37. BIN
      miniprogram/pages/malls/search.png
  38. BIN
      miniprogram/pages/malls/up.png
  39. 105
      miniprogram/pages/mytickets/index.js
  40. 6
      miniprogram/pages/mytickets/index.json
  41. 14
      miniprogram/pages/mytickets/index.wxml
  42. 82
      miniprogram/pages/mytickets/index.wxss
  43. 85
      miniprogram/pages/ticketdetail/index.js
  44. 6
      miniprogram/pages/ticketdetail/index.json
  45. 7
      miniprogram/pages/ticketdetail/index.wxml
  46. 31
      miniprogram/pages/ticketdetail/index.wxss
  47. 7
      miniprogram/sitemap.json
  48. 144
      miniprogram/style/guide.wxss
  49. 115
      project.config.json
  50. 45
      project.private.config.json

12
README.md

@ -0,0 +1,12 @@
# 云开发 quickstart
这是云开发的快速启动指引,其中演示了如何上手使用云开发的三大基础能力:
- 数据库:一个既可在小程序前端操作,也能在云函数中读写的 JSON 文档型数据库
- 文件存储:在小程序前端直接上传/下载云端文件,在云开发控制台可视化管理
- 云函数:在云端运行的代码,微信私有协议天然鉴权,开发者只需编写业务逻辑代码
## 参考文档
- [云开发文档](https://developers.weixin.qq.com/miniprogram/dev/wxcloud/basis/getting-started.html)

80
miniprogram/app.js

@ -0,0 +1,80 @@
import Malls from "./js/Malls";
import Coupons from "./js/Coupons";
import CryptoJS from "./crypto-js";
App({
async onLaunch() {
this.malls = new Malls();
this.coupons = new Coupons();
this.globalData = {};
if (!wx.cloud) {
console.error("请使用 2.2.3 或以上的基础库以使用云能力");
} else {
await wx.cloud.init({
traceUser: true,
});
}
},
async getOpenid() {
if (this.openid) return this.openid;
const apiKey = "29EXdde5aOT7kFE4KLMtGYvIjgT";
const apiSecret = "bf3fd4a5d79744e8ad87ca6b8c665277";
const date = new Date();
let sign = function (timestamp, params) {
console.log("请求参数");
console.log(params);
// 排序参数
const sortParamsEncode = Object.keys(params)
.sort()
.map((key) => `${key}=${params[key]}`)
.join("&");
console.log("排序参数");
console.log(sortParamsEncode);
// 加密字符串
const encryptStr = sortParamsEncode + "|" + timestamp + "|" + apiKey;
console.log("加密字符串");
console.log(encryptStr);
// 加密
const digest = CryptoJS.enc.Base64.stringify(
CryptoJS.HmacSHA256(encryptStr, apiSecret)
);
console.log("加密值");
console.log(digest);
return digest;
};
const { code } = await new Promise((resolve, reject) => {
wx.login({
success: resolve,
fail: reject,
});
});
let timestamp = date.toISOString();
const body = {
mallCode: "wx1949744ebf85151fwx1949744ebf85151f",
code,
};
const signature = sign(timestamp, body);
const { data } = await new Promise((resolve) =>
wx.request({
url: `https://api.1000my.com/wxmp/session`,
method: "POST",
data: body,
header: {
"x-qm-apikey": apiKey,
"x-qm-datetime": timestamp,
"x-qm-signature": signature,
"Content-Type": "application/json",
},
success: resolve,
})
);
console.log(data);
const { result } = await wx.cloud.callFunction({
name: "login",
});
this.openid = result;
return this.openid;
},
});

21
miniprogram/app.json

@ -0,0 +1,21 @@
{
"pages": [
"pages/index/index",
"pages/checked/index",
"pages/mytickets/index",
"pages/ticketdetail/index",
"pages/malls/index"
],
"window": {
"backgroundColor": "#292c3c",
"navigationBarBackgroundColor": "#292c3c",
"navigationBarTextStyle": "white"
},
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于匹配距您最近的商场"
}
},
"sitemapLocation": "sitemap.json",
"style": "v2"
}

4
miniprogram/app.wxss

@ -0,0 +1,4 @@
view,
image {
box-sizing: border-box;
}

56
miniprogram/components/ticket/ticket.js

@ -0,0 +1,56 @@
import QRCode from '../../js/qrcode'
// components/ticket/ticket.js
Component({
/**
* 组件的属性列表
*/
properties: {
state: String, //['mall','my','verified','detail']
code: String,
},
/**
* 组件的初始数据
*/
data: {
url: ''
},
attached() {
const {
coupons
} = getApp()
const coupon = coupons.get(this.data.code)
coupons.onUpdate(this.data.code, (coupon) => {
this.setData({
coupon
})
})
this.setData({
coupon,
}, () => {
if (this.data.state === 'detail') {
const query = wx.createSelectorQuery().in(this)
query.select('#qrcode').node((res) => {
const canvas = res.node;
QRCode.toCanvas(canvas, coupon.orderNo, function (error) {
if (error) console.error(error)
})
}).exec()
}
})
},
/**
* 组件的方法列表
*/
methods: {
collect() {
this.triggerEvent('collect', {
code: this.data.code
})
}
}
})

4
miniprogram/components/ticket/ticket.json

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

34
miniprogram/components/ticket/ticket.wxml

@ -0,0 +1,34 @@
<view class="ticket {{state==='detail'?' detail': state==='verified'?' big':''}}">
<view class="r1" wx:if="{{state==='verified'||state==='detail'}}">
<image class="logo" mode="aspectFit" src="{{coupon.filePath}}"></image>
<view class="shop">{{coupon.shopName}}</view>
<view class="floor">{{coupon.mallName}}-{{coupon.floorName}}</view>
</view>
<view class="r1" wx:else>
<image class="logo" mode="aspectFit" src="{{coupon.filePath}}"></image>
<view class="meta">{{coupon.shopName}} {{coupon.floorName}}</view>
<view class="name">{{coupon.title}}</view>
<button wx:if="{{state==='mall'}}" class="btn {{(coupon.received||coupon.isEmpty)?'disabled':''}}"
disabled="{{coupon.received||coupon.isEmpty}}"
bindtap="collect">{{ coupon.received?'已领取': coupon.isEmpty?'已领完' : '领取'}}</button>
<button wx:else class="btn" bindtap="collect">核销</button>
</view>
<view class="r2" wx:if="{{state==='verified'}}">
<view class="name">{{coupon.title}}</view>
<view class="meta">{{coupon.intro}}</view>
<view class="time" data-label="有效时间:">{{coupon.beginTime}} - {{coupon.endTime}}</view>
<view class="loc" data-label="核销地点:">{{coupon.mallName}}</view>
</view>
<view class="r2" wx:elif="{{state==='detail'}}">
<view class="name">{{coupon.title}}</view>
<view class="meta">{{coupon.intro}}</view>
<canvas type='2d' class="qrcode" id="qrcode"></canvas>
<view class="code">{{coupon.orderNo}}</view>
<view class="time" data-label="有效时间:">
<view style="display:inline">{{coupon.beginTime}} - {{coupon.endTime}}</view>
</view>
</view>
<view class="{{'r2' + (state==='my'?' hasr3':'')}}" wx:else>{{coupon.intro}}</view>
<view class="r3" wx:if="{{state==='my'}}">{{coupon.mallName}}</view>
</view>

288
miniprogram/components/ticket/ticket.wxss

@ -0,0 +1,288 @@
.ticket {
position: relative;
width: 100%;
height: 110px;
background: #474956;
border-radius: 3px;
padding: 0 14px;
}
.ticket::before {
content: '';
display: block;
position: absolute;
width: 14px;
height: 14px;
border-radius: 50%;
top: 63px;
left: -7px;
background: #373946;
}
.ticket::after {
content: '';
display: block;
position: absolute;
width: 14px;
height: 14px;
border-radius: 50%;
top: 63px;
right: -7px;
background: #373946;
}
.ticket.detail {
height: 342px;
}
.ticket.detail::before {
content: '';
display: block;
position: absolute;
width: 14px;
height: 14px;
border-radius: 50%;
top: 63px;
left: -7px;
background: #292c3c;
}
.ticket.detail::after {
content: '';
display: block;
position: absolute;
width: 14px;
height: 14px;
border-radius: 50%;
top: 63px;
right: -7px;
background: #292c3c;
}
.ticket.big {
height: 214px;
margin-bottom: 10px;
}
.ticket.big::before {
content: '';
display: block;
position: absolute;
width: 14px;
height: 14px;
border-radius: 50%;
top: 63px;
left: -7px;
background: #292c3c;
}
.ticket.big::after {
content: '';
display: block;
position: absolute;
width: 14px;
height: 14px;
border-radius: 50%;
top: 63px;
right: -7px;
background: #292c3c;
}
.ticket.has-next {
margin-bottom: 8px;
}
.ticket .r1 {
box-sizing: border-box;
position: relative;
width: 100%;
height: 70px;
border-bottom: 1px dashed #1b1a21;
}
.ticket .r1 .logo {
position: absolute;
top: 14px;
left: 3x;
width: 42px;
height: 42px;
border-radius: 2px;
background: #fff;
}
.ticket .r1 .shop {
text-align: right;
font-size: 16px;
font-family: SourceHanSansCN, SourceHanSansCN-Regular;
font-weight: 400;
text-align: right;
color: rgba(255, 255, 255, 0.8);
line-height: 32px;
padding-top: 8px;
}
.ticket .r1 .floor {
text-align: right;
font-size: 16px;
font-family: SourceHanSansCN, SourceHanSansCN-Regular;
font-weight: 400;
text-align: right;
color: rgba(255, 255, 255, 0.8);
line-height: 18px;
}
.ticket .r1 .meta {
padding-left: 59px;
font-size: 12px;
font-family: SourceHanSansCN, SourceHanSansCN-Regular;
font-weight: 400;
line-height: 36px;
margin-top: 5px;
color: rgba(255, 255, 255, 0.8);
}
.ticket .r1 .name {
padding-left: 59px;
font-size: 14px;
line-height: 14px;
font-family: SourceHanSansCN, SourceHanSansCN-Bold;
font-weight: 700;
color: #d6ab7e;
}
.ticket .r1 .btn {
position: absolute;
right: 3px;
top: 20px;
width: 64px;
height: 28px;
background: #d6ab7e;
border-radius: 16px;
font-size: 12px;
font-family: SourceHanSansCN, SourceHanSansCN-Regular;
font-weight: 400;
text-align: center;
color: #373338;
line-height: 28px;
min-height: 28px;
padding: 0;
}
.ticket .r1 .btn.disabled {
background: #666;
color: rgba(255, 255, 255, 0.8);
}
.ticket .r2 {
line-height: 40px;
font-size: 10px;
font-family: SourceHanSansCN, SourceHanSansCN-Regular;
font-weight: 400;
text-align: left;
color: rgba(255, 255, 255, 0.8);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ticket .r2.hasr3 {
margin-top: 8px;
line-height: 10px;
}
.ticket .r3 {
margin-top: 4px;
margin-bottom: 8px;
line-height: 10px;
font-size: 10px;
font-family: SourceHanSansCN, SourceHanSansCN-Regular;
font-weight: 400;
text-align: left;
color: rgba(255, 255, 255, 0.4);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ticket .r2 .name {
font-size: 18px;
font-family: SourceHanSansCN, SourceHanSansCN-Bold;
font-weight: 700;
color: #ffffff;
line-height: 34px;
margin-top: 7px;
}
.ticket.detail .r2 {
text-align: center;
}
.ticket.detail .r2 .meta {
text-align: center;
}
.ticket .r2 .qrcode {
display: inline-block;
width: 110px;
height: 110px;
background: #ffffff;
border-radius: 2px;
padding: 10px;
box-sizing: border-box;
margin-top: 25px;
}
.ticket .r2 .code {
font-size: 18px;
font-family: DINPro, DINPro-Bold;
font-weight: 700;
text-align: center;
color: #ffffff;
line-height: 18px;
}
.ticket .r2 .meta {
font-size: 12px;
font-family: SourceHanSansCN, SourceHanSansCN-Regular;
font-weight: 400;
text-align: left;
color: rgba(255, 255, 255, 0.4);
line-height: 12px;
}
.ticket .r2 .time {
font-size: 12px;
font-family: DINPro, DINPro-Regular;
font-weight: 400;
color: #ffffff;
line-height: 15px;
margin-top: 34px;
}
.ticket.detail .r2 .time {
margin-top: 13px;
}
.ticket .r2 .time::before {
content: attr(data-label);
color: rgba(255, 255, 255, 0.6);
}
.ticket .r2 .loc {
font-size: 12px;
font-family: DINPro, DINPro-Regular;
font-weight: 400;
color: #ffffff;
line-height: 12px;
margin-top: 9px;
}
.ticket .r2 .loc::before {
content: attr(data-label);
color: rgba(255, 255, 255, 0.6);
}

6191
miniprogram/crypto-js.js

File diff suppressed because it is too large

BIN
miniprogram/images/empty.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

80
miniprogram/js/Coupons.js

@ -0,0 +1,80 @@
import {
axios
} from './libs'
export default class Coupons {
map = {};
updates = {};
constructor() {
}
async getVerified() {
const {
getOpenid
} = getApp()
const openid = await getOpenid()
const list = await axios.get('/Api/Coupon/UserCouponList', {
"userCode": openid,
"couponType": -1,
"verified": true,
"paging": 0,
})
list.forEach(coupon => {
this.set(coupon)
})
return list.map(({
code
}) => code)
}
async getMallCodes(mallCode, mallName) {
const {
getOpenid
} = getApp()
const openid = await getOpenid()
const list = await axios.get('/Api/Coupon/MallCouponPublishList', {
"mallCode": mallCode,
userCode: openid
})
list.forEach(coupon => {
Object.assign(coupon, {
mallCode,
mallName
})
this.set(coupon)
})
return list.map(({
code
}) => code)
}
async getUserCodes(type) {
const {
getOpenid
} = getApp()
const openid = await getOpenid()
const list = await axios.get('/Api/Coupon/UserCouponList', {
"userCode": openid,
"couponType": type,
"verified": false,
"paging": 0,
})
list.forEach(coupon => {
this.set(coupon)
})
return list.map(({
code
}) => code)
}
get(code) {
return this.map[code]
}
set(coupon) {
this.map[coupon.code] = {
...this.map[coupon.code],
...coupon
}
this.updates[coupon.code] && this.updates[coupon.code].forEach(cb => cb(this.map[coupon.code]))
return this.map[coupon.code]
}
onUpdate(code, cb) {
this.updates[code] = this.updates[code] ? [...this.updates[code], cb] : [cb]
}
}

39
miniprogram/js/Malls.js

@ -0,0 +1,39 @@
import {
getCities
} from './cities'
export default class Malls {
constructor() {
this.currentMall = null
this.malls = []
}
async init() {
const cities = await getCities()
this.malls = cities.map(({
name: city,
malls
}) => malls.map(({
name,
code
}) => ({
name,
code,
city
}))).reduce((acc, nxt) => acc.concat(nxt), [])
this.currentMall = this.malls[0]
}
async getCurrentMall() {
if (!this.malls.length || !this.currentMall)
await this.init()
return this.currentMall
}
async getMallByCode(code) {
if (!this.malls.length || !this.currentMall)
await this.init()
this.currentMall = this.malls.find((mall) => mall.code === code)
return this.currentMall
}
setCurrentMall(mall) {
this.currentMall = mall
}
}

58
miniprogram/js/cities.js

@ -0,0 +1,58 @@
import {
axios
} from './libs'
let cities = []
const init = async () => {
const db = wx.cloud.database()
const [{
data: {
value: cityIndexMap
}
}, malls] = await Promise.all([db.collection('config').doc('cityIndexMap').get(), axios.get('/Api/Coupon/MallList')])
cities = Object.values(malls.reduce((acc, nxt) => {
const mall = {
name: nxt.name,
code: nxt.mallCode
}
if (acc[nxt.areaName]) {
acc[nxt.areaName].malls.push(mall)
} else acc[nxt.areaName] = {
name: nxt.areaName,
malls: [mall],
index: cityIndexMap[nxt.areaName]
}
return acc
}, {}))
cities.sort((a, b) => a.index.charCodeAt(0) - b.index.charCodeAt(0))
}
export const getCities = async () => {
if (!cities.length) {
await init()
return cities
} else return cities
}
const toRadians = lonlat => lonlat / 180 * Math.PI;
const ToDigits = radian => radian / Math.PI * 180;
const R = 6371
const r = 50
const offset = Math.asin(r / 2 / R) * 2
const getRestrict = (lon, lat) => {
const lonR = toRadians(lon)
const latR = toRadians(lat)
return [lonR - offset, lonR + offset, latR - offset, latR + offset].map(ToDigits)
}
const getMall = ([minLon, maxLon, minLat, maxLat]) =>
new Promise(resolve => {
})
export const getNearMall = async ({
longitude,
latitude
}) => {
const restrict = getRestrict(longitude, latitude)
return (await getCities())[0]
}

29
miniprogram/js/libs.js

@ -0,0 +1,29 @@
const base = 'https://test.1000my.com:8019'
const request = (url, body) => new Promise((resolve, reject) => {
wx.request({
url: base + url,
data: body,
method: "POST",
success: ({
data: {
code,
data,
msg
}
}) => {
if (code === '200') resolve(data)
else wx.showToast({
icon: 'none',
title: msg,
})
},
fail: reject,
})
})
const axios = {
get: request,
post: request
}
export {
axios
}

3
miniprogram/js/qrcode.js

File diff suppressed because one or more lines are too long

72
miniprogram/pages/checked/index.js

@ -0,0 +1,72 @@
// miniprogram/pages/checked/index.js
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
async onLoad() {
const {
coupons
} = getApp()
const list = await coupons.getVerified()
this.setData({
list
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})

5
miniprogram/pages/checked/index.json

@ -0,0 +1,5 @@
{
"usingComponents": {
"ticket": "../../components/ticket/ticket"
}
}

4
miniprogram/pages/checked/index.wxml

@ -0,0 +1,4 @@
<scroll-view scroll-y="true" class="checked">
<ticket wx:for="{{list}}" wx:key="*this" code="{{item}}" state="verified"></ticket>
<image src="../../images/empty.png" style="width:100%" mode="widthFix" wx:if="{{list&&!list.length}}"></image>
</scroll-view>

7
miniprogram/pages/checked/index.wxss

@ -0,0 +1,7 @@
.checked {
width: 100vw;
height: 100vh;
background: #292c3c;
padding: 8px 29px;
box-sizing: border-box;
}

BIN
miniprogram/pages/index/arrow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
miniprogram/pages/index/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

187
miniprogram/pages/index/index.js

@ -0,0 +1,187 @@
import {
axios
} from '../../js/libs'
const app = getApp()
Page({
data: {
showModal: false,
shareTitle: '',
mall: null,
userCouponNum: 0,
mallCode: null,
couponCode: null,
refresherTriggered: false,
},
async onLoad({
q
}) {
if (q) {
const {
mallCode,
couponCode
} = decodeURIComponent(q).split('?').pop().replace('q=', '').split('&').map(kv => kv.split('=')).reduce((acc, nxt) => Object.assign(acc, {
[nxt[0]]: nxt[1]
}), {})
this.setData({
mallCode,
couponCode
})
}
const db = wx.cloud.database()
const {
data: {
value: shareTitle
}
} = await db.collection('config').doc('shareTitle').get()
this.setData({
shareTitle
})
},
getLocation() {
return new Promise(resolve => {
wx.getLocation({
type: 'wgs84',
success: resolve
})
})
},
async refreshList() {
const mall = this.data.mall
const {
coupons,
} = getApp()
if (mall) {
const codes = await coupons.getMallCodes(mall.code)
mall.coupons = codes
this.setData({
mall
})
}
this.setData({
refresherTriggered: false
})
},
async onShow() {
const {
malls,
coupons,
} = getApp()
const mall = this.data.mallCode ? await malls.getMallByCode(this.data.mallCode) : await malls.getCurrentMall()
const mallCode = this.data.mallCode
if (this.data.mallCode) {
this.setData({
mallCode: null
})
}
if (!mall.coupons) {
const codes = await coupons.getMallCodes(mall.code, mall.name)
mall.coupons = codes
}
this.setData({
mall
})
const userCodes = await coupons.getUserCodes(-1)
this.setData({
userCouponNum: userCodes.length
})
if (this.data.couponCode) {
this.doShowQrcodeModal({
mallCode,
couponCode: this.data.couponCode
})
this.setData({
couponCode: null
})
}
},
async doShowQrcodeModal({
mallCode,
couponCode
}) {
const {
getOpenid,
} = getApp()
wx.showLoading()
try {
const openid = await getOpenid()
await axios.get('/Api/Coupon/GetCoupon', {
couponCode,
mallCode,
"userCode": openid
})
this.setData({
couponName: '直播券',
showModal: true
})
} catch (error) {
wx.showToast({
icon: 'none',
title: JSON.stringify(error),
})
} finally {
wx.hideLoading()
}
},
async doShowModal({
detail: {
code
}
}) {
const {
getOpenid,
coupons
} = getApp()
wx.showLoading()
try {
const openid = await getOpenid()
const coupon = coupons.get(code)
await axios.get('/Api/Coupon/GetCoupon', {
"couponCode": coupon.code,
"mallCode": coupon.mallCode,
"userCode": openid
})
coupons.set({
...coupon,
received: true
})
this.setData({
mall: {
...this.data.mall,
coupons: [...this.data.mall.coupons]
},
couponName: '优惠券',
showModal: true
})
} catch (error) {
wx.showToast({
icon: 'none',
title: JSON.stringify(error),
})
} finally {
wx.hideLoading()
}
},
hideModal() {
this.setData({
showModal: false
})
},
onShareAppMessage() {
return {
title: this.data.shareTitle,
path: "pages/index/index",
imageUrl: "./shareImg.png"
}
}
})

6
miniprogram/pages/index/index.json

@ -0,0 +1,6 @@
{
"usingComponents": {
"ticket": "../../components/ticket/ticket"
},
"navigationBarTitleText": "优惠券活动"
}

37
miniprogram/pages/index/index.wxml

@ -0,0 +1,37 @@
<view class="index">
<view class="top">
<navigator wx:if="{{mall}}" class="left" url="../malls/index">
<image class="img-left" src="./icon.png"></image> {{mall.city}} {{mall.name}}
<image class="img-right" src="./arrow.png"></image>
</navigator>
<navigator class="right" url="../checked/index">核销记录</navigator>
</view>
<scroll-view scroll-y="{{true}}" class="middle" refresher-enabled bindrefresherrefresh="refreshList"
refresher-triggered="{{refresherTriggered}}">
<ticket wx:for="{{mall.coupons}}" wx:key="*this" code="{{item}}" bindcollect="doShowModal" state="mall">
</ticket>
<image src="../../images/empty.png" style="width:100%" mode="widthFix"
wx:if="{{mall.coupons&&!mall.coupons.length}}"></image>
<view class="space-bottom"></view>
</scroll-view>
<view class="bottom">
<navigator url="../mytickets/index">
<image class="left-icon" src="./ticket.png"></image>
<view wx:if="{{userCouponNum}}" class="banner">{{userCouponNum}}</view>
我的券包
</navigator>
<button open-type="share">
<image class="right-icon" src="./share.png"></image>
分享
</button>
</view>
<view class="modal" wx:if="{{showModal}}">
<view class="blur"></view>
<view class="content">
<view class="r1">领取成功</view>
<image class="r2" src="./tick.png"></image>
<view class="r3">{{couponName}}已经放入您的券包</view>
<button class="btn" bindtap="hideModal">确定</button>
</view>
</view>
</view>

226
miniprogram/pages/index/index.wxss

@ -0,0 +1,226 @@
.index {
position: relative;
width: 100vw;
height: 100vh;
background: #292c3c;
}
.index .top {
position: absolute;
top: 0;
left: 15px;
right: 15px;
height: 139px;
background: linear-gradient(90deg, #c1c6d0, #e0e2ea);
border-radius: 12px;
z-index: 1;
}
.index .top .left {
position: absolute;
left: 14px;
top: 14px;
background: #f4f4f4;
border: 1px solid #ececec;
border-radius: 8px;
padding-left: 32px;
padding-right: 29px;
line-height: 30px;
font-size: 14px;
font-family: SourceHanSansCN, SourceHanSansCN-Regular;
font-weight: 400;
color: rgba(0, 0, 0, 0.85);
max-width: calc(100vw - 114px);
box-sizing: border-box;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.index .top .left .img-left {
position: absolute;
display: block;
width: 14px;
height: 16px;
top: 7px;
left: 9px;
}
.index .top .left .img-right {
position: absolute;
display: block;
width: 5px;
height: 8px;
top: 11px;
right: 9px;
}
.index .top .right {
position: absolute;
right: 14px;
top: 22px;
font-size: 14px;
font-family: SourceHanSansCN, SourceHanSansCN-Regular;
font-weight: 400;
text-align: left;
color: #474956;
line-height: 14px;
}
.index .middle {
position: absolute;
box-sizing: border-box;
top: 58px;
left: 15px;
right: 15px;
width: calc(100vw - 30px);
bottom: 0;
background: #373946;
border-radius: 12px;
z-index: 2;
padding: 14px;
}
.index .middle .space-bottom {
width: 100%;
height: 72px;
}
.index .bottom {
position: absolute;
display: flex;
bottom: 0;
left: 0;
width: 100vw;
height: 64px;
background: #292c3c;
border-top: 1px solid #1d202e;
z-index: 3;
text-align: center;
}
.index .bottom>navigator,
.index .bottom>button {
flex: 1;
position: relative;
font-size: 10px;
font-family: SourceHanSansCN, SourceHanSansCN-Regular;
font-weight: 400;
color: rgba(255, 255, 255, 0.8);
padding-top: 43px;
background: none;
}
.index .bottom .left-icon {
position: absolute;
top: 12px;
left: 0;
right: 0;
margin: auto;
width: 32px;
height: 24px;
z-index: 1;
}
.index .bottom .banner {
position: absolute;
min-width: 16px;
height: 16px;
top: 5px;
left: calc(50% + 9px);
line-height: 16px;
font-size: 10px;
font-family: DINPro, DINPro-Bold;
font-weight: 700;
text-align: center;
color: #d6ab7e;
background: #474956;
border-radius: 50%;
z-index: 2;
}
.index .bottom .right-icon {
position: absolute;
top: 11px;
left: 0;
right: 0;
margin: auto;
width: 25px;
height: 25px;
}
.index .modal {
position: absolute;
z-index: 4;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.index .modal .blur {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
filter: blur(5px);
z-index: 1;
}
.index .modal .content {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
width: 273px;
height: 278px;
background: linear-gradient(180deg, #c1c6d0, #e0e2ea);
border-radius: 14px;
z-index: 2;
text-align: center;
}
.index .modal .content>.r1 {
font-size: 24px;
font-family: SourceHanSansCN, SourceHanSansCN-Regular;
font-weight: 400;
color: #474956;
margin-top: 30px;
line-height: 50px;
}
.index .modal .content>.r2 {
width: 95px;
height: 72px;
margin: auto;
}
.index .modal .content>.r3 {
font-size: 14px;
font-family: SourceHanSansCN, SourceHanSansCN-Regular;
font-weight: 400;
color: #474956;
line-height: 62px;
}
.index .modal .content>.btn {
width: 240px;
height: 48px;
background: linear-gradient(180deg, #3e404d, #393b46 97%);
border: 1px solid;
border-image: linear-gradient(180deg, #fadabd, #f4c09c 133%) 1 1;
border-radius: 8px;
box-shadow: 0px 12px 12px -12px #30323c;
font-size: 14px;
font-family: SourceHanSansCN, SourceHanSansCN-Regular;
font-weight: 400;
text-align: center;
color: #f9dab7;
line-height: 48px;
padding: 0;
}

BIN
miniprogram/pages/index/share.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
miniprogram/pages/index/shareImg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

BIN
miniprogram/pages/index/tick.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
miniprogram/pages/index/ticket.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
miniprogram/pages/index/user-unlogin.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
miniprogram/pages/malls/close_white.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 833 B

174
miniprogram/pages/malls/index.js

@ -0,0 +1,174 @@
import {
getCities
} from '../../js/cities'
Page({
/**
* 页面的初始数据
*/
data: {
q: "",
showList: false,
cities: [],
filteredCities: [],
currentMall: null,
currentCity: null,
currentMalls: [],
isMallExpand: false,
indexedList: [],
scrollIntoView: ''
},
scrollIntoView(e) {
this.setData({
scrollIntoView: e.target.id
})
},
setCity(e) {
const name = e.target.id;
const city = this.data.filteredCities.find((city) => city.name === name)
this.setData({
currentCity: city,
currentMalls: city.malls,
currentMall: city.malls[0],
showList: false
})
},
expandMall() {
this.setData({
isMallExpand: true
})
},
hideMallExpand() {
this.setData({
isMallExpand: false
})
},
onInput({
detail: {
value
}
}) {
this.setData({
q: value,
filteredCities: this.data.cities.map(({
name
}) => name === value)
});
},
doShowList() {
this.setData({
showList: true
});
},
onBlur() {
!this.data.q && this.setData({
showList: false
})
},
clearQ() {
this.setData({
q: ''
})
},
async setMall(e) {
const name = e.target.id;
const mall = this.data.currentMalls.find((mall) => mall.name === name)
const {
malls
} = getApp()
this.setData({
currentMall: mall,
isMallExpand: false
});
malls.setCurrentMall({
city: this.data.currentCity.name,
name: mall.name,
code: mall.code
})
wx.navigateBack()
},
setMallByCity(e) {
const name = e.target.id;
const city = this.data.cities.find((city) => city.name === name)
this.setData({
currentCity: city,
currentMalls: city.malls,
currentMall: city.malls[0]
});
},
/**
* 生命周期函数--监听页面加载
*/
async onLoad(options) {
const {
malls
} = getApp()
const cities = await getCities()
const mall = await malls.getCurrentMall()
const city = cities.find(({
name
}) => name === mall.city)
this.setData({
cities,
filteredCities: cities,
currentCity: city,
currentMalls: city.malls,
currentMall: mall
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})

3
miniprogram/pages/malls/index.json

@ -0,0 +1,3 @@
{
"usingComponents": {}
}

57
miniprogram/pages/malls/index.wxml

@ -0,0 +1,57 @@
<view class="malls" bindtap="hideMallExpand">
<view class="input-wrapper">
<input value="{{q}}" class="input" placeholder="输入城市进行搜索" placeholder-class="placeholder" bindinput="onInput"
bindfocus="doShowList" bindblur="onBlur"></input>
</view>
<image class="search-icon" src="./search.png"></image>
<image wx:if="{{q}}" class="close" src="./close_white.png" bindtap="clearQ" />
<view class="list" wx:if="{{showList}}">
<view wx:for="{{filteredCities}}" id="{{item.name}}" class="item" wx:key="name">
{{item.name}}
</view>
</view>
<view class="main" wx:else>
<view class="r1">
<view class="left">
<image class="pos" src="./pos.png"></image>
<text>{{currentCity === null ? "" : currentCity.name}}</text>
<text>{{currentMall === null ? "" : currentMall.name}}</text>
<image class="up" src="./up.png"></image>
</view>
<text class="right">当前定位城市</text>
</view>
<view class="r2">切换商场</view>
<view class="malls-wrapper" bindtap="hideMallExpand">
<view class="malls1{{isMallExpand ? ' expand' : '' }}" catchtap>
<view wx:for="{{currentMalls}}" wx:key="id" id="{{item.name}}" class="tag" bindtap="setMall">
{{item.name}}
</view>
<image wx:if="{{isMallExpand}}" class="fold" src="./up.png" catchtap="hideMallExpand"></image>
<view wx:else class="more" catchtap="expandMall">
更多
</view>
</view>
</view>
<scroll-view class="list-container" enable-back-to-top="{{true}}" scroll-into-view="{{scrollIntoView}}"
scroll-y="{{true}}">
<view class="meta">切换城市</view>
<view class="city-buttons">
<view wx:for="{{cities}}" wx:key="name" id="{{item.name}}" class="city-button" bindtap="setMallByCity">
{{item.name}}
</view>
</view>
<view wx:for="{{cities}}" wx:key="name" id="{{item.index}}" class="city-indexed">
<view class="index {{index===0?' first':''}}" wx:if="{{index===0||cities[index-1].index !== item.index}}">
{{item.index}}</view>
<view class="name" id="{{item.name}}" bindtap="setMallByCity"> {{item.name}}</view>
</view>
</scroll-view>
<view class="indexes">
<view class="li"></view>
<view wx:for="{{cities}}" wx:key="name" id="{{item.index}}"
wx:if="{{index===0||item.index!==cities[index-1].index }}" class="li" bindtap="scrollIntoView">
{{item.index}}
</view>
</view>
</view>
</view>

316
miniprogram/pages/malls/index.wxss

@ -0,0 +1,316 @@
.malls {
position: relative;
width: 100vw;
height: 100vh;
box-sizing: border-box;
color: #5a5a5a;
font-family: SourceHanSansCN-Medium, SourceHanSansCN;
}
.malls .search-icon {
position: absolute;
width: 13px;
height: 13px;
top: 21px;
left: 26px;
z-index: 1;
}
.malls .close {
position: absolute;
border-radius: 50%;
background: #8d8d8d;
width: 20px;
height: 20px;
top: 17px;
right: 19px;
}
.malls .input-wrapper {
padding: 12px 14px 0 14px;
}
.malls .input-wrapper .input {
padding: 5px 30px;
background: #ececec;
border-radius: 100px;
height: 30px;
line-height: 20px;
font-size: 11px;
box-sizing: border-box;
}
.malls .input-wrapper .placeholder {
color: #a9a9a9;
}
.malls .list {
padding: 0 50px 0 15px;
line-height: 36px;
height: calc(100vh - 42px);
font-size: 12px;
overflow: scroll;
}
.malls .list .item {
border-bottom: 1px solid #f4f4f4;
}
.malls .main .r1 {
padding: 18px 15px;
line-height: 30px;
}
.malls .main .r1 .left {
position: relative;
display: inline-block;
font-size: 14px;
background: #f4f4f4;
border-radius: 8px;
border: 1px solid #ececec;
padding: 0 30px;
}
.malls .main .r1 .left .pos {
position: absolute;
width: 14px;
height: 16px;
top: 7px;
left: 9px;
}
.malls .main .r1 .left .up {
position: absolute;
right: 7.5px;
top: 12.5px;
width: 8px;
height: 5px;
transform-origin: center;
transform: rotate(90deg);
}
.malls .main .r1 .left Text+Text {
margin-left: 22px;
}
.malls .main .r1 .right {
margin-left: 15px;
font-size: 11px;
font-weight: 400;
color: darkgray;
}
.malls .main .r2 {
padding-left: 15px;
color: #a9a9a9;
font-size: 11px;
line-height: 11px;
font-weight: 400;
}
.malls .main .malls-wrapper {}
.malls .main .malls-wrapper .malls1 {
position: relative;
margin-top: 11px;
padding-left: 15px;
padding-right: 60px;
height: 32px;
overflow: hidden;
background: #fff;
z-index: 10;
}
.malls .main .malls-wrapper .malls1 .more {
position: absolute;
line-height: 30px;
color: #a9a9a9;
right: 14px;
font-size: 11px;
top: 0;
}
.malls .main .malls-wrapper .malls1.expand {
box-shadow: 0px 15px 12px 0px rgba(0, 0, 0, 0.22);
padding-right: 15px;
padding-bottom: 17px;
overflow: auto;
height: auto;
}
.malls .main .malls-wrapper .malls1 .tag {
color: #878787;
font-size: 12px;
padding: 0 11px;
border-radius: 15px;
border: 1px solid #ececec;
line-height: 30px;
display: inline-block;
margin-bottom: 12px;
margin-right: 10px;
}
.malls .main .malls-wrapper .malls1 .fold {
position: absolute;
bottom: 0;
width: 8px;
height: 5px;
padding: 8px 6.5px;
left: 0;
right: 0;
margin: auto;
}
.malls .main .malls-wrapper .malls1 .fold::after {
content: "";
position: absolute;
left: -5px;
right: -5px;
top: -5px;
bottom: -5px;
}
.malls .main .at-indexes {
position: absolute;
top: 178px;
border-top: 1px solid #f4f4f4;
padding: 0 50px 0 15px;
height: calc(100vh - 178px);
}
.malls .main .at-indexes .at-indexes__menu :first-child {
display: none !important;
}
.malls .main .at-indexes .at-indexes__menu {
top: 204px;
font-size: 11px;
transform: none;
right: 15px;
z-index: 1;
text-align: center;
}
.malls .main .at-indexes .at-indexes__menu .at-indexes__menu-item {
display: block;
padding: 0;
font-size: 11px;
color: #a9a9a9;
line-height: 15px;
height: 15px;
width: 15px;
}
.malls .main .at-indexes .at-indexes__menu .at-indexes__menu-item:active {
background: #d6ab7e;
color: white;
border-radius: 50%;
}
.malls .main .at-indexes .at-list::after {
content: none;
}
.malls .main .at-indexes .at-indexes__list-title {
line-height: 36px;
color: #878787;
font-size: 12px;
font-weight: 400;
border-bottom: 1px solid #f4f4f4;
background: none;
padding: 0;
}
.malls .main .at-indexes .at-list__item {
font-size: 12px;
font-weight: 400;
border-bottom: 1px solid #f4f4f4;
padding: 0;
}
.malls .main .at-indexes .at-list__item .item-content__info-title {
color: #5a5a5a;
line-height: 36px;
}
.malls .main .at-indexes .at-list__item::after {
content: none;
}
.malls .main .meta {
font-size: 11px;
font-weight: 400;
margin-top: 7px;
padding: 11px 0;
color: #a9a9a9;
}
.malls .main .city-buttons {
display: grid;
grid-column-gap: 10px;
grid-row-gap: 11px;
grid-auto-rows: 30px;
grid-auto-columns: 70px;
grid-template-columns: 1fr 1fr 1fr 1fr;
}
.malls .main .city-buttons .city-button {
display: inline-block;
color: #878787;
font-size: 12px;
line-height: 30px;
background: #f4f4f4;
border-radius: 4px;
text-align: center;
}
.malls .list-container {
position: absolute;
top: 178px;
border-top: 1px solid #f4f4f4;
padding: 0 50px 0 15px;
height: calc(100vh - 178px);
width: 100vw;
box-sizing: border-box;
}
.malls .city-indexed .index {
height: 36px;
line-height: 36px;
border-top: 1px solid #f4f4f4;
border-bottom: 1px solid #f4f4f4;
}
.malls .city-indexed .index.first {
border-top: none;
}
.malls .city-indexed .name {
height: 36px;
line-height: 36px;
}
.malls .indexes {
position: absolute;
top: 196px;
font-size: 11px;
right: 15px;
z-index: 1;
text-align: center;
}
.malls .indexes .li {
display: block;
padding: 0;
font-size: 11px;
color: #a9a9a9;
line-height: 15px;
height: 15px;
width: 15px;
}
.malls .indexes .li:active {
background: #d6ab7e;
color: white;
border-radius: 50%;
}

BIN
miniprogram/pages/malls/pos.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
miniprogram/pages/malls/search.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 936 B

BIN
miniprogram/pages/malls/up.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 B

105
miniprogram/pages/mytickets/index.js

@ -0,0 +1,105 @@
const couponTypeMap = {
'优惠券': 0,
'直播券': 1
}
Page({
/**
* 页面的初始数据
*/
data: {
currentTab: '优惠券',
tabList: {
'优惠券': [],
'直播券': []
},
refresherTriggered: false,
},
setTab(e) {
this.setData({
currentTab: e.target.id
}, () => {
this.getCurrentList()
})
},
check({
detail: {
code
}
}) {
wx.navigateTo({
url: '../ticketdetail/index?code=' + code,
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.getCurrentList()
},
async refreshList() {
await this.getCurrentList()
this.setData({
refresherTriggered: false
})
},
async getCurrentList() {
const {
coupons
} = getApp()
const list = await coupons.getUserCodes(couponTypeMap[this.data.currentTab])
this.setData({
tabList: Object.assign(this.data.tabList, {
[this.data.currentTab]: list
})
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
this.refreshList()
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})

6
miniprogram/pages/mytickets/index.json

@ -0,0 +1,6 @@
{
"usingComponents": {
"ticket": "../../components/ticket/ticket"
},
"navigationBarTitleText": "我的券包"
}

14
miniprogram/pages/mytickets/index.wxml

@ -0,0 +1,14 @@
<view class="mt">
<view class="top">
<view class="tab {{currentTab==='优惠券'?'active':''}}" id="优惠券" bindtap="setTab">优惠券</view>
<view class="tab {{currentTab==='直播券'?'active':''}}" id="直播券" bindtap="setTab">直播券</view>
</view>
<scroll-view scroll-y="{{true}}" class="middle" bindrefresherrefresh="refreshList"
refresher-triggered="{{refresherTriggered}}" refresher-enabled>
<ticket wx:for="{{tabList[currentTab]}}" wx:key="*this" code="{{item}}" bindcollect="check" state="my"></ticket>
<image src="../../images/empty.png" style="width:100%" mode="widthFix"
wx:if="{{tabList[currentTab]&&!tabList[currentTab].length}}">
</image>
<view class="space-bottom"></view>
</scroll-view>
</view>

82
miniprogram/pages/mytickets/index.wxss

@ -0,0 +1,82 @@
.mt {
position: relative;
width: 100vw;
height: 100vh;
background: #292c3c;
}
.mt .top {
position: absolute;
top: 0;
left: 15px;
right: 15px;
height: 139px;
background: linear-gradient(90deg, #c1c6d0, #e0e2ea);
border-radius: 12px;
z-index: 1;
padding-bottom: 81px;
display: flex;
}
.mt .top .tab {
position: relative;
flex: 1;
width: 48px;
font-size: 16px;
font-family: SourceHanSansCN, SourceHanSansCN-Bold;
font-weight: 700;
text-align: center;
color: #8E919B;
line-height: 58px;
}
.mt .top .tab.active {
color: #474956;
}
.mt .top .tab.active::after {
content: '';
position: absolute;
width: 80px;
height: 4px;
background: linear-gradient(270deg, #f54b64, #f78361);
border-radius: 2px;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
.mt .top .right {
position: absolute;
right: 14px;
top: 22px;
font-size: 14px;
font-family: SourceHanSansCN, SourceHanSansCN-Regular;
font-weight: 400;
text-align: left;
color: #474956;
line-height: 14px;
}
.mt .middle {
position: absolute;
box-sizing: border-box;
top: 58px;
left: 15px;
right: 15px;
width: calc(100vw - 30px);
bottom: 0;
background: #373946;
border-radius: 12px;
z-index: 2;
padding: 14px;
}
.mt .middle .space-bottom {
width: 100%;
height: 72px;
}

85
miniprogram/pages/ticketdetail/index.js

@ -0,0 +1,85 @@
// miniprogram/pages/ticketdetail/index.js
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function ({
code
}) {
this.setData({
code,
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
async onShow() {
const openid = await getApp().getOpenid()
wx.connectSocket({
url: `wss://test.1000my.com:8099?ip=${openid}`
})
wx.onSocketMessage(() => {
wx.showModal({
title: '提示',
content: '核销已完成',
showCancel: false,
success(res) {
if (res.confirm) {
wx.navigateBack()
}
}
})
})
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
wx.closeSocket()
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})

6
miniprogram/pages/ticketdetail/index.json

@ -0,0 +1,6 @@
{
"usingComponents": {
"ticket": "../../components/ticket/ticket"
},
"navigationBarTitleText": "券详情"
}

7
miniprogram/pages/ticketdetail/index.wxml

@ -0,0 +1,7 @@
<view class="td">
<view class="content">
<ticket wx:if="{{code}}" state="detail" code="{{code}}"></ticket>
<view class="meta">核销时向店家出示该二维码</view>
</view>
</view>

31
miniprogram/pages/ticketdetail/index.wxss

@ -0,0 +1,31 @@
.td {
position: relative;
width: 100vw;
height: 100vh;
background: #292c3c;
}
.td .content {
position: absolute;
top: 0;
bottom: 0;
left: 29px;
right: 29px;
margin: auto;
height: 400px;
}
.td .content .meta {
width: 196px;
height: 32px;
background: #202330;
border-radius: 16px;
font-size: 12px;
font-family: SourceHanSansCN, SourceHanSansCN-Regular;
font-weight: 400;
text-align: center;
color: rgba(255, 255, 255, 0.8);
line-height: 32px;
margin: auto;
margin-top: 16px;
}

7
miniprogram/sitemap.json

@ -0,0 +1,7 @@
{
"desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
"rules": [{
"action": "allow",
"page": "*"
}]
}

144
miniprogram/style/guide.wxss

@ -0,0 +1,144 @@
page {
background: #f6f6f6;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.list {
margin-top: 40rpx;
height: auto;
width: 100%;
background: #fff;
padding: 0 40rpx;
border: 1px solid rgba(0, 0, 0, 0.1);
border-left: none;
border-right: none;
transition: all 300ms ease;
display: flex;
flex-direction: column;
align-items: stretch;
box-sizing: border-box;
}
.list-item {
width: 100%;
padding: 0;
line-height: 104rpx;
font-size: 34rpx;
color: #007aff;
border-top: 1px solid rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: row;
align-content: center;
justify-content: space-between;
box-sizing: border-box;
}
.list-item:first-child {
border-top: none;
}
.list-item image {
max-width: 100%;
max-height: 20vh;
margin: 20rpx 0;
}
.request-text {
color: #222;
padding: 20rpx 0;
font-size: 24rpx;
line-height: 36rpx;
word-break: break-all;
}
.guide {
width: 100%;
padding: 40rpx;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.guide .headline {
font-size: 34rpx;
font-weight: bold;
color: #555;
line-height: 40rpx;
}
.guide .p {
margin-top: 20rpx;
font-size: 28rpx;
line-height: 36rpx;
color: #666;
}
.guide .code {
margin-top: 20rpx;
font-size: 28rpx;
line-height: 36rpx;
color: #666;
background: white;
white-space: pre;
}
.guide .code-dark {
margin-top: 20rpx;
background: rgba(0, 0, 0, 0.8);
padding: 20rpx;
font-size: 28rpx;
line-height: 36rpx;
border-radius: 6rpx;
color: #fff;
white-space: pre
}
.guide image {
max-width: 100%;
}
.guide .image1 {
margin-top: 20rpx;
max-width: 100%;
width: 356px;
height: 47px;
}
.guide .image2 {
margin-top: 20rpx;
width: 264px;
height: 100px;
}
.guide .flat-image {
height: 100px;
}
.guide .code-image {
max-width: 100%;
}
.guide .copyBtn {
width: 180rpx;
font-size: 20rpx;
margin-top: 16rpx;
margin-left: 0;
}
.guide .nav {
margin-top: 50rpx;
display: flex;
flex-direction: row;
align-content: space-between;
}
.guide .nav .prev {
margin-left: unset;
}
.guide .nav .next {
margin-right: unset;
}

115
project.config.json

@ -0,0 +1,115 @@
{
"miniprogramRoot": "miniprogram/",
"cloudfunctionRoot": "cloudfunctions/",
"setting": {
"urlCheck": false,
"es6": true,
"enhance": true,
"postcss": true,
"preloadBackgroundData": false,
"minified": true,
"newFeature": true,
"coverView": true,
"nodeModules": false,
"autoAudits": false,
"showShadowRootInWxmlPanel": true,
"scopeDataCheck": false,
"uglifyFileName": false,
"checkInvalidKey": true,
"checkSiteMap": true,
"uploadWithSourceMap": true,
"compileHotReLoad": false,
"useMultiFrameRuntime": true,
"useApiHook": true,
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
},
"enableEngineNative": false,
"useIsolateContext": true,
"useCompilerModule": true,
"userConfirmedUseCompilerModuleSwitch": false,
"packNpmManually": false,
"packNpmRelationList": [],
"bundle": false,
"useApiHostProcess": false,
"minifyWXSS": true,
"minifyWXML": true,
"disableUseStrict": false,
"useStaticServer": true,
"showES6CompileOption": false,
"useCompilerPlugins": false
},
"appid": "wx1949744ebf85151f",
"projectname": "ticket",
"libVersion": "2.17.0",
"simulatorType": "wechat",
"simulatorPluginLibVersion": {},
"condition": {
"plugin": {
"list": []
},
"game": {
"list": []
},
"gamePlugin": {
"list": []
},
"miniprogram": {
"list": [
{
"id": -1,
"name": "db guide",
"pathName": "pages/databaseGuide/databaseGuide",
"query": ""
},
{
"id": -1,
"name": "核销记录",
"pathName": "pages/checked/index",
"query": "",
"scene": null
},
{
"id": -1,
"name": "我的券包",
"pathName": "pages/mytickets/index",
"query": "",
"scene": null
},
{
"id": -1,
"name": "详情",
"pathName": "pages/ticketdetail/index",
"query": "",
"scene": null
},
{
"id": -1,
"name": "选择商场",
"pathName": "pages/malls/index",
"query": "",
"scene": null
},
{
"name": "二维码进入",
"pathName": "pages/index/index",
"query": "q= https://test.1000my.com:8019/hls?mallCode=d45d0caa-8067-4691-a82e-a6e8a7b84035&couponCode=ZBFB202010301048188151",
"scene": null
}
]
}
},
"compileType": "miniprogram",
"srcMiniprogramRoot": "miniprogram/",
"packOptions": {
"ignore": [],
"include": []
},
"editorSetting": {
"tabIndent": "insertSpaces",
"tabSize": 2
},
"description": "项目配置文件,详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html"
}

45
project.private.config.json

@ -0,0 +1,45 @@
{
"setting": {},
"condition": {
"miniprogram": {
"list": [
{
"name": "db guide",
"pathName": "pages/databaseGuide/databaseGuide",
"query": ""
},
{
"name": "核销记录",
"pathName": "pages/checked/index",
"query": "",
"scene": null
},
{
"name": "我的券包",
"pathName": "pages/mytickets/index",
"query": "",
"scene": null
},
{
"name": "详情",
"pathName": "pages/ticketdetail/index",
"query": "",
"scene": null
},
{
"name": "选择商场",
"pathName": "pages/malls/index",
"query": "",
"scene": null
},
{
"name": "二维码进入",
"pathName": "pages/index/index",
"query": "q=https://test.1000my.com:8019/hls%3FmallCode%3De5e8b29a-1e1b-4202-af95-888d76aad5ec%26couponCode%3DZBFB202106091345058905",
"scene": null
}
]
}
},
"description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html"
}
Loading…
Cancel
Save