1
This commit is contained in:
@ -7,9 +7,10 @@ import { LOGIN, GET_PHONE, USER_SHARE,USER_WALLET,RECHARGE_WALLET,USER_WXPAY,USE
|
|||||||
// 微信登陆
|
// 微信登陆
|
||||||
export const getCodeByWxLogin = () => {
|
export const getCodeByWxLogin = () => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
uni.login({
|
tt.login({
|
||||||
provider: "weixin",
|
provider: "toutiao",
|
||||||
success: function (loginRes) {
|
success: function (loginRes) {
|
||||||
|
// console.log(loginRes,'?1?')
|
||||||
resolve(loginRes.code);
|
resolve(loginRes.code);
|
||||||
},
|
},
|
||||||
fail: function (err) {
|
fail: function (err) {
|
||||||
@ -22,7 +23,7 @@ export const getCodeByWxLogin = () => {
|
|||||||
import Store from "../store";
|
import Store from "../store";
|
||||||
|
|
||||||
// 登录鉴权
|
// 登录鉴权
|
||||||
export const login = (phone) => {
|
export const login = (nickName, avatarUrl) => {
|
||||||
return getCodeByWxLogin().then((code) => {
|
return getCodeByWxLogin().then((code) => {
|
||||||
// 从 vuex 中获取 referrerID(如果通过二维码扫描进入)
|
// 从 vuex 中获取 referrerID(如果通过二维码扫描进入)
|
||||||
const referrerID = Store.state.user?.referrerID || 0;
|
const referrerID = Store.state.user?.referrerID || 0;
|
||||||
@ -31,12 +32,17 @@ export const login = (phone) => {
|
|||||||
url: LOGIN,
|
url: LOGIN,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
data: {
|
data: {
|
||||||
|
nickName:nickName,
|
||||||
|
avatarUrl:avatarUrl,
|
||||||
code: code,
|
code: code,
|
||||||
// yaoqing_code: inviteCode || null,
|
source:'douyin',
|
||||||
phone: phone || null,
|
referrerID:0,
|
||||||
source: "wechat",
|
referrerType:'douyin'
|
||||||
referrerID: Number(referrerID) || 0,
|
// // yaoqing_code: inviteCode || null,
|
||||||
referrerType: "wechat"
|
// phone: phone || null,
|
||||||
|
// source: "wechat",
|
||||||
|
// referrerID: Number(referrerID) || 0,
|
||||||
|
// referrerType: "wechat"
|
||||||
},
|
},
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
// 登录接口使用完 referrerID 后,清除一次,避免重复使用
|
// 登录接口使用完 referrerID 后,清除一次,避免重复使用
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export const RECHARGE_WALLET = '/wallet/details'
|
|||||||
export const USER_WALLET = '/wallet/info'
|
export const USER_WALLET = '/wallet/info'
|
||||||
|
|
||||||
//钱包充值接口
|
//钱包充值接口
|
||||||
export const USER_WXPAY = '/wxpay/jsapi'
|
export const USER_WXPAY = '/ttpay/jsapi'
|
||||||
//钱包充值金额
|
//钱包充值金额
|
||||||
export const USER_RECHARGE = '/recharge/bonus-list'
|
export const USER_RECHARGE = '/recharge/bonus-list'
|
||||||
//积分充值列表
|
//积分充值列表
|
||||||
@ -186,7 +186,7 @@ export const HOME_ORDERS_CANCEL = "/home/orders/cancel";
|
|||||||
// 校验节假日费用
|
// 校验节假日费用
|
||||||
export const CHECK_HOLIDAY_FEE = "/check_holiday_fee";
|
export const CHECK_HOLIDAY_FEE = "/check_holiday_fee";
|
||||||
//支付订单
|
//支付订单
|
||||||
export const PAY_ORDER = "/wxpay/jsapi";
|
export const PAY_ORDER = "/ttpay/jsapi";
|
||||||
|
|
||||||
//取消订单
|
//取消订单
|
||||||
export const CANCEL_ORDER = "/app/chongwu_order/order_quxiao";
|
export const CANCEL_ORDER = "/app/chongwu_order/order_quxiao";
|
||||||
@ -235,7 +235,7 @@ export const CREATE_ORDER_NEW = "/product/order/create";
|
|||||||
// 创建购物车订单
|
// 创建购物车订单
|
||||||
export const CREATE_CART_ORDER = "/product/order/create";
|
export const CREATE_CART_ORDER = "/product/order/create";
|
||||||
// 订单支付
|
// 订单支付
|
||||||
export const PAY_ORDER_NEW = "/wxpay/jsapi";
|
export const PAY_ORDER_NEW = "/ttpay/jsapi";
|
||||||
// 订单列表
|
// 订单列表
|
||||||
export const SHOP_ORDER_LIST = "/product/order/list";
|
export const SHOP_ORDER_LIST = "/product/order/list";
|
||||||
// 订单详情
|
// 订单详情
|
||||||
@ -304,3 +304,5 @@ export const HOMETRAINING_ORDERS_CANCEL = '/hometraining/orders/cancel'
|
|||||||
// 撤销申请(领养申请)
|
// 撤销申请(领养申请)
|
||||||
export const ADOPTIONS_PET_APPLY_CANCEL = '/adoptions/pet/apply/cancel'
|
export const ADOPTIONS_PET_APPLY_CANCEL = '/adoptions/pet/apply/cancel'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="nav-container">
|
<view class="nav-container">
|
||||||
<view :style="{ height: ststuaBarHeight + 'px' }"></view>
|
<!-- <view :style="{ height: ststuaBarHeight + 'px' }"></view> -->
|
||||||
<view
|
<view
|
||||||
class="flex-row-center nav-title ali-puhui-bold"
|
class="flex-row-center nav-title ali-puhui-bold"
|
||||||
:style="{ height: menuButtonHeight + 'px' }"
|
:style="{ height: menuButtonHeight + 'px' }"
|
||||||
|
|||||||
@ -551,13 +551,12 @@ export default {
|
|||||||
}
|
}
|
||||||
payOrder(this.orderId, chaJiaType).then((res) => {
|
payOrder(this.orderId, chaJiaType).then((res) => {
|
||||||
const payData = res?.info?.pay_data || {};
|
const payData = res?.info?.pay_data || {};
|
||||||
uni.requestPayment({
|
tt.pay({
|
||||||
provider: 'wxpay',
|
orderInfo: {
|
||||||
timeStamp: payData.timeStamp,
|
order_id:res.data.orderInfo.order_id,
|
||||||
nonceStr: payData.nonceStr,
|
order_token:res.data.orderInfo.order_token,
|
||||||
package: payData.package,
|
},
|
||||||
signType: payData.signType,
|
service:5,
|
||||||
paySign: payData.sign,
|
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
console.log('success:' + JSON.stringify(res));
|
console.log('success:' + JSON.stringify(res));
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
|
|||||||
@ -272,14 +272,12 @@ export default {
|
|||||||
},
|
},
|
||||||
weixinPay(orderId) {
|
weixinPay(orderId) {
|
||||||
payOrder(orderId).then((res) => {
|
payOrder(orderId).then((res) => {
|
||||||
const payData = res?.info?.pay_data || {};
|
tt.pay({
|
||||||
uni.requestPayment({
|
orderInfo: {
|
||||||
provider: 'wxpay',
|
order_id:res.data.orderInfo.order_id,
|
||||||
timeStamp: payData.timeStamp,
|
order_token:res.data.orderInfo.order_token,
|
||||||
nonceStr: payData.nonceStr,
|
},
|
||||||
package: payData.package,
|
service:5,
|
||||||
signType: payData.signType,
|
|
||||||
paySign: payData.sign,
|
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
const eventChannel = this.getOpenerEventChannel();
|
const eventChannel = this.getOpenerEventChannel();
|
||||||
|
|||||||
@ -60,7 +60,8 @@
|
|||||||
<text v-if="item.item_cut_price" class="price-original">¥{{ item.item_price }}</text>
|
<text v-if="item.item_cut_price" class="price-original">¥{{ item.item_price }}</text>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
<image class="cart-icon" :src="item.selected ? require('@/static/images/xz.png') : ''" />
|
<image v-show="item.selected" class="cart-icon" src="/static/images/xz.png"></image>
|
||||||
|
<!-- <image class="cart-icon" :src="item.selected ? require('@/static/images/xz.png') : ''" /> -->
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@ -109,8 +110,8 @@
|
|||||||
<text class="zf"> 请选择支付方式 </text>
|
<text class="zf"> 请选择支付方式 </text>
|
||||||
<view class="wechat" @click.stop="selectOption1('1')">
|
<view class="wechat" @click.stop="selectOption1('1')">
|
||||||
<view class="select">
|
<view class="select">
|
||||||
<image class="w" src="@/static/images/wx.png" mode="widthFix" />
|
<image class="w" src="@/static/images/douy.png" mode="widthFix" />
|
||||||
<text class="x">微信</text>
|
<text class="x">抖音</text>
|
||||||
</view>
|
</view>
|
||||||
<image v-if="selected1" class="not-selected" src="@/static/images/w.png" mode="widthFix" />
|
<image v-if="selected1" class="not-selected" src="@/static/images/w.png" mode="widthFix" />
|
||||||
<image v-if="selected2" class="not-selected" src="@/static/images/y.png" mode="widthFix" />
|
<image v-if="selected2" class="not-selected" src="@/static/images/y.png" mode="widthFix" />
|
||||||
@ -533,12 +534,12 @@ export default {
|
|||||||
};
|
};
|
||||||
walletWxpay(data).then((res) => {
|
walletWxpay(data).then((res) => {
|
||||||
// 使用获取的支付参数进行支付
|
// 使用获取的支付参数进行支付
|
||||||
uni.requestPayment({
|
tt.pay({
|
||||||
timeStamp: res.data.timeStamp, // 确保这些字段都正确
|
orderInfo: {
|
||||||
nonceStr: res.data.nonceStr,
|
order_id:res.data.orderInfo.order_id,
|
||||||
package: res.data.package,
|
order_token:res.data.orderInfo.order_token,
|
||||||
signType: res.data.signType,
|
},
|
||||||
paySign: res.data.paySign,
|
service:5,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
// 关闭支付弹窗
|
// 关闭支付弹窗
|
||||||
this.additionalBom = false;
|
this.additionalBom = false;
|
||||||
|
|||||||
@ -192,8 +192,8 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="wechat" @click.stop="selectOption1('1')">
|
<view class="wechat" @click.stop="selectOption1('1')">
|
||||||
<view class="select">
|
<view class="select">
|
||||||
<image class="w" src="@/static/images/wx.png" mode="widthFix" />
|
<image class="w" src="@/static/images/douy.png" mode="widthFix" />
|
||||||
<text class="x">微信</text>
|
<text class="x">抖音</text>
|
||||||
</view>
|
</view>
|
||||||
<image v-if="selected1" class="not-selected" src="@/static/images/w.png" mode="widthFix" />
|
<image v-if="selected1" class="not-selected" src="@/static/images/w.png" mode="widthFix" />
|
||||||
<image v-if="selected2" class="not-selected" src="@/static/images/y.png" mode="widthFix" />
|
<image v-if="selected2" class="not-selected" src="@/static/images/y.png" mode="widthFix" />
|
||||||
@ -702,12 +702,12 @@ export default {
|
|||||||
};
|
};
|
||||||
payOrder(params)
|
payOrder(params)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
uni.requestPayment({
|
tt.pay({
|
||||||
timeStamp: res.data.timeStamp,
|
orderInfo: {
|
||||||
nonceStr: res.data.nonceStr,
|
order_id:res.data.orderInfo.order_id,
|
||||||
package: res.data.package,
|
order_token:res.data.orderInfo.order_token,
|
||||||
signType: res.data.signType,
|
},
|
||||||
paySign: res.data.paySign,
|
service:5,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
|
|||||||
@ -297,8 +297,8 @@
|
|||||||
<view class="recharge-method">
|
<view class="recharge-method">
|
||||||
<view class="wechat" @click.stop="selectOption1('1')">
|
<view class="wechat" @click.stop="selectOption1('1')">
|
||||||
<view class="select">
|
<view class="select">
|
||||||
<image class="w" src="@/static/images/wx.png" mode="widthFix" />
|
<image class="w" src="@/static/images/douy.png" mode="widthFix" />
|
||||||
<text class="x">微信</text>
|
<text class="x">抖音</text>
|
||||||
</view>
|
</view>
|
||||||
<image v-if="selected1" class="not-selected" src="@/static/images/w.png" mode="widthFix" />
|
<image v-if="selected1" class="not-selected" src="@/static/images/w.png" mode="widthFix" />
|
||||||
<image v-if="selected2" class="not-selected" src="@/static/images/y.png" mode="widthFix" />
|
<image v-if="selected2" class="not-selected" src="@/static/images/y.png" mode="widthFix" />
|
||||||
@ -369,8 +369,8 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="wechat">
|
<view class="wechat">
|
||||||
<view class="select">
|
<view class="select">
|
||||||
<image class="w" src="@/static/images/wx.png" mode="widthFix" />
|
<image class="w" src="@/static/images/douy.png" mode="widthFix" />
|
||||||
<text class="x">微信</text>
|
<text class="x">抖音</text>
|
||||||
</view>
|
</view>
|
||||||
<image v-if="select1" class="not-selected" @click.stop="selectOpt1('1')" src="@/static/images/w.png"
|
<image v-if="select1" class="not-selected" @click.stop="selectOpt1('1')" src="@/static/images/w.png"
|
||||||
mode="widthFix" />
|
mode="widthFix" />
|
||||||
@ -792,12 +792,12 @@ export default {
|
|||||||
};
|
};
|
||||||
memberWXPAY(data).then((res) => {
|
memberWXPAY(data).then((res) => {
|
||||||
// 使用获取的支付参数进行支付
|
// 使用获取的支付参数进行支付
|
||||||
uni.requestPayment({
|
tt.pay({
|
||||||
timeStamp: res.data.timeStamp, // 确保这些字段都正确
|
orderInfo: {
|
||||||
nonceStr: res.data.nonceStr,
|
order_id:res.data.orderInfo.order_id,
|
||||||
package: res.data.package,
|
order_token:res.data.orderInfo.order_token,
|
||||||
signType: res.data.signType,
|
},
|
||||||
paySign: res.data.paySign,
|
service:5,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: "支付成功",
|
title: "支付成功",
|
||||||
@ -1202,12 +1202,12 @@ export default {
|
|||||||
}
|
}
|
||||||
payOrder(params)
|
payOrder(params)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
uni.requestPayment({
|
tt.pay({
|
||||||
timeStamp: res.data.timeStamp, // 确保这些字段都正确
|
orderInfo: {
|
||||||
nonceStr: res.data.nonceStr,
|
order_id:res.data.orderInfo.order_id,
|
||||||
package: res.data.package,
|
order_token:res.data.orderInfo.order_token,
|
||||||
signType: res.data.signType,
|
},
|
||||||
paySign: res.data.paySign,
|
service:5,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
const eventChannel = this.getOpenerEventChannel();
|
const eventChannel = this.getOpenerEventChannel();
|
||||||
|
|||||||
@ -115,8 +115,8 @@
|
|||||||
<!-- 支付方式 -->
|
<!-- 支付方式 -->
|
||||||
<view class="info-cell payment-method-cell">
|
<view class="info-cell payment-method-cell">
|
||||||
<view class="payment-option" @click="selectPaymentMethod('wechat')">
|
<view class="payment-option" @click="selectPaymentMethod('wechat')">
|
||||||
<image src="@/static/images/wx.png" mode="aspectFit" class="payment-icon" />
|
<image src="@/static/images/douy.png" mode="aspectFit" class="payment-icon" />
|
||||||
<text class="payment-name">微信支付</text>
|
<text class="payment-name">抖音支付</text>
|
||||||
<!-- 未选中状态 -->
|
<!-- 未选中状态 -->
|
||||||
<image v-if="paymentMethod !== 'wechat'" src="@/static/images/w.png" mode="widthFix" class="not-selected" />
|
<image v-if="paymentMethod !== 'wechat'" src="@/static/images/w.png" mode="widthFix" class="not-selected" />
|
||||||
<!-- 选中状态 -->
|
<!-- 选中状态 -->
|
||||||
@ -436,16 +436,12 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const payData = res.data || res.info?.pay_data || {};
|
tt.pay({
|
||||||
|
orderInfo: {
|
||||||
// 调用微信支付
|
order_id:res.data.orderInfo.order_id,
|
||||||
uni.requestPayment({
|
order_token:res.data.orderInfo.order_token,
|
||||||
provider: 'wxpay',
|
},
|
||||||
timeStamp: payData.timeStamp,
|
service:5,
|
||||||
nonceStr: payData.nonceStr,
|
|
||||||
package: payData.package,
|
|
||||||
signType: payData.signType,
|
|
||||||
paySign: payData.paySign || payData.sign,
|
|
||||||
success: (payRes) => {
|
success: (payRes) => {
|
||||||
console.log('支付成功:', payRes);
|
console.log('支付成功:', payRes);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
|
|||||||
@ -435,13 +435,12 @@ export default {
|
|||||||
order_no: orderNo,
|
order_no: orderNo,
|
||||||
total_fee: 200
|
total_fee: 200
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
uni.requestPayment({
|
tt.pay({
|
||||||
provider: "wxpay",
|
orderInfo: {
|
||||||
timeStamp: res.data.timeStamp,
|
order_id:res.data.orderInfo.order_id,
|
||||||
nonceStr: res.data.nonceStr,
|
order_token:res.data.orderInfo.order_token,
|
||||||
package: res.data.package,
|
},
|
||||||
signType: res.data.signType,
|
service:5,
|
||||||
paySign: res.data.paySign,
|
|
||||||
complete: (res) => {
|
complete: (res) => {
|
||||||
// 支付成功后跳转到订单页面
|
// 支付成功后跳转到订单页面
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
@ -47,8 +47,8 @@
|
|||||||
<!-- 支付方式 -->
|
<!-- 支付方式 -->
|
||||||
<view class="info-cell payment-method-cell">
|
<view class="info-cell payment-method-cell">
|
||||||
<view class="payment-option" @click="selectPaymentMethod('wechat')">
|
<view class="payment-option" @click="selectPaymentMethod('wechat')">
|
||||||
<image src="@/static/images/wx.png" mode="aspectFit" class="payment-icon" />
|
<image src="@/static/images/douy.png" mode="aspectFit" class="payment-icon" />
|
||||||
<text class="payment-name">微信支付</text>
|
<text class="payment-name">抖音支付</text>
|
||||||
<!-- 未选中状态 -->
|
<!-- 未选中状态 -->
|
||||||
<image v-if="paymentMethod !== 'wechat'" src="@/static/images/w.png" mode="widthFix" class="not-selected" />
|
<image v-if="paymentMethod !== 'wechat'" src="@/static/images/w.png" mode="widthFix" class="not-selected" />
|
||||||
<!-- 选中状态 -->
|
<!-- 选中状态 -->
|
||||||
@ -262,16 +262,12 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const payData = res.data || res.info?.pay_data || {};
|
tt.pay({
|
||||||
|
orderInfo: {
|
||||||
// 调用微信支付
|
order_id:res.data.orderInfo.order_id,
|
||||||
uni.requestPayment({
|
order_token:res.data.orderInfo.order_token,
|
||||||
provider: 'wxpay',
|
},
|
||||||
timeStamp: payData.timeStamp,
|
service:5,
|
||||||
nonceStr: payData.nonceStr,
|
|
||||||
package: payData.package,
|
|
||||||
signType: payData.signType,
|
|
||||||
paySign: payData.paySign || payData.sign,
|
|
||||||
success: (payRes) => {
|
success: (payRes) => {
|
||||||
console.log('支付成功:', payRes);
|
console.log('支付成功:', payRes);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
|
|||||||
@ -71,8 +71,15 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<canvas
|
||||||
|
id="myCanvas"
|
||||||
|
type="2d"
|
||||||
|
:style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"
|
||||||
|
style="position: fixed; left: -9999px; top: -9999px;"
|
||||||
|
></canvas>
|
||||||
|
|
||||||
<!-- 隐藏的 wxml-to-canvas 组件用于生成证书图片 -->
|
<!-- 隐藏的 wxml-to-canvas 组件用于生成证书图片 -->
|
||||||
<wxml-to-canvas class="widget" :width="canvasWidth" :height="canvasHeight" style="position: fixed; left: -9999px; top: -9999px;"></wxml-to-canvas>
|
<!-- <wxml-to-canvas class="widget" :width="canvasWidth" :height="canvasHeight" style="position: fixed; left: -9999px; top: -9999px;"></wxml-to-canvas> -->
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -107,7 +114,7 @@ export default {
|
|||||||
},
|
},
|
||||||
onReady() {
|
onReady() {
|
||||||
// 获取 wxml-to-canvas 组件实例
|
// 获取 wxml-to-canvas 组件实例
|
||||||
this.widget = this.$refs.widget || this.selectComponent('.widget');
|
// this.widget = this.$refs.widget || this.selectComponent('.widget');
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getCertificates(data) {
|
getCertificates(data) {
|
||||||
@ -124,6 +131,27 @@ export default {
|
|||||||
},
|
},
|
||||||
closeModal() {
|
closeModal() {
|
||||||
this.showModal = false;
|
this.showModal = false;
|
||||||
|
},
|
||||||
|
wrapText(ctx, text, x, y, maxWidth, lineHeight) {
|
||||||
|
const words = text.split('');
|
||||||
|
let line = '';
|
||||||
|
let currentY = y;
|
||||||
|
|
||||||
|
for (let i = 0; i < words.length; i++) {
|
||||||
|
const testLine = line + words[i];
|
||||||
|
const metrics = ctx.measureText(testLine);
|
||||||
|
const testWidth = metrics.width;
|
||||||
|
|
||||||
|
if (testWidth > maxWidth && i > 0) {
|
||||||
|
ctx.fillText(line, x, currentY);
|
||||||
|
line = words[i];
|
||||||
|
currentY += lineHeight;
|
||||||
|
} else {
|
||||||
|
line = testLine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fillText(line, x, currentY);
|
||||||
},
|
},
|
||||||
onScroll(e) {
|
onScroll(e) {
|
||||||
// 获取滚动位置(单位:px)
|
// 获取滚动位置(单位:px)
|
||||||
@ -141,10 +169,32 @@ export default {
|
|||||||
this.currentPage = newPage;
|
this.currentPage = newPage;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async saveCurrentImage() {
|
async loadImage(src) {
|
||||||
// 保存当前图片 - 使用 wxml-to-canvas 生成
|
console.log('正在加载图片:', src);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
tt.downloadFile({
|
||||||
|
url: src,
|
||||||
|
success: (res) => {
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
console.log('图片加载成功:', res.tempFilePath);
|
||||||
|
resolve(res.tempFilePath);
|
||||||
|
} else {
|
||||||
|
console.error('图片加载失败,状态码:', res.statusCode);
|
||||||
|
reject(new Error(`图片加载失败,状态码: ${res.statusCode}`));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('图片加载失败:', err.errMsg);
|
||||||
|
reject(new Error(`图片加载失败: ${err.errMsg}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async saveCurrentImage() {
|
||||||
const currentIndex = this.currentPage - 1;
|
const currentIndex = this.currentPage - 1;
|
||||||
const currentItem = this.certificateList[currentIndex];
|
const currentItem = this.certificateList[currentIndex];
|
||||||
|
|
||||||
if (!currentItem) {
|
if (!currentItem) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '证书信息加载中,请稍候',
|
title: '证书信息加载中,请稍候',
|
||||||
@ -153,82 +203,109 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 存储当前证书信息到 data,供模板使用
|
|
||||||
this.currentCertificateItem = currentItem;
|
this.currentCertificateItem = currentItem;
|
||||||
|
|
||||||
// 获取组件实例 - 多种方式尝试
|
|
||||||
if (!this.widget) {
|
|
||||||
this.widget = this.$refs.widget;
|
|
||||||
}
|
|
||||||
if (!this.widget) {
|
|
||||||
this.widget = this.selectComponent('.widget');
|
|
||||||
}
|
|
||||||
if (!this.widget) {
|
|
||||||
// 延迟重试一次
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 100));
|
|
||||||
this.widget = this.$refs.widget || this.selectComponent('.widget');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.widget) {
|
|
||||||
uni.hideLoading();
|
|
||||||
uni.showToast({
|
|
||||||
title: '组件初始化失败,请重试',
|
|
||||||
icon: 'none',
|
|
||||||
duration: 3000
|
|
||||||
});
|
|
||||||
console.error('wxml-to-canvas 组件获取失败');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查组件方法是否可用
|
|
||||||
if (typeof this.widget.renderToCanvas !== 'function') {
|
|
||||||
uni.hideLoading();
|
|
||||||
uni.showToast({
|
|
||||||
title: '组件方法不可用,请重试',
|
|
||||||
icon: 'none',
|
|
||||||
duration: 3000
|
|
||||||
});
|
|
||||||
console.error('renderToCanvas 方法不存在');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uni.showLoading({
|
uni.showLoading({
|
||||||
title: '生成图片中...',
|
title: '生成图片中...',
|
||||||
mask: true
|
mask: true
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 固定画布尺寸
|
// 获取 Canvas 节点
|
||||||
const canvasWidth = 317;
|
const query = tt.createSelectorQuery();
|
||||||
const canvasHeight = 224;
|
const canvasNode = await new Promise((resolve, reject) => {
|
||||||
|
query
|
||||||
// 更新 canvas 尺寸
|
.select('#myCanvas')
|
||||||
this.canvasWidth = canvasWidth;
|
.fields({ node: true, size: true })
|
||||||
this.canvasHeight = canvasHeight;
|
.exec((res) => {
|
||||||
|
if (res[0]) {
|
||||||
await this.$nextTick();
|
resolve(res[0].node);
|
||||||
|
} else {
|
||||||
// 生成证书的 wxml 和 style(使用固定尺寸)
|
reject(new Error('Canvas 节点获取失败'));
|
||||||
const { wxml, style } = this.generateCertificateTemplate();
|
}
|
||||||
|
});
|
||||||
// 渲染到 canvas
|
|
||||||
const container = await this.widget.renderToCanvas({ wxml, style });
|
|
||||||
|
|
||||||
// 转换为图片
|
|
||||||
const res = await this.widget.canvasToTempFilePath({
|
|
||||||
fileType: 'png',
|
|
||||||
quality: 1,
|
|
||||||
width: this.canvasWidth,
|
|
||||||
height: this.canvasHeight
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!res || !res.tempFilePath) {
|
// 获取 2D 上下文
|
||||||
throw new Error('生成图片路径失败');
|
const ctx = canvasNode.getContext('2d');
|
||||||
}
|
const dpr = tt.getSystemInfoSync().pixelRatio;
|
||||||
|
canvasNode.width = this.canvasWidth * dpr;
|
||||||
|
canvasNode.height = this.canvasHeight * dpr;
|
||||||
|
ctx.scale(dpr, dpr);
|
||||||
|
|
||||||
|
// 清空画布
|
||||||
|
ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
|
||||||
|
|
||||||
|
// 加载并绘制背景图
|
||||||
|
const backgroundImgPath = await this.loadImage(`${this.imgPrefix}certificateGround.png`);
|
||||||
|
const backgroundImg = canvasNode.createImage();
|
||||||
|
backgroundImg.src = backgroundImgPath;
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
backgroundImg.onload = () => {
|
||||||
|
ctx.drawImage(backgroundImg, 0, 0, this.canvasWidth, this.canvasHeight);
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 设置字体样式
|
||||||
|
ctx.font = '20px sans-serif';
|
||||||
|
ctx.fillStyle = '#FF19A0';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
|
||||||
|
// 绘制证书标题
|
||||||
|
const title = currentItem.title || '为爱续航证书';
|
||||||
|
ctx.fillText(title, this.canvasWidth / 2, 50);
|
||||||
|
|
||||||
|
// 绘制英文标题
|
||||||
|
ctx.font = '12px sans-serif';
|
||||||
|
const titleEn = currentItem.title_en || 'Certificate of Love Endurance';
|
||||||
|
ctx.fillText(titleEn, this.canvasWidth / 2, 70);
|
||||||
|
|
||||||
|
// 绘制用户名
|
||||||
|
ctx.font = '16px sans-serif';
|
||||||
|
const userName = this.userName || '周佳佳';
|
||||||
|
ctx.fillText(userName, this.canvasWidth / 2, 100);
|
||||||
|
|
||||||
|
// 绘制描述文本
|
||||||
|
ctx.font = '12px sans-serif';
|
||||||
|
ctx.fillStyle = '#333333';
|
||||||
|
ctx.textAlign = 'left';
|
||||||
|
const description = `您累计捐赠${currentItem.source_value || 0}克粮,为毛孩子奉献爱心,点燃希望,感谢您的捐赠让世界变得更温暖。`;
|
||||||
|
this.wrapText(ctx, description, 20, 130, this.canvasWidth - 40, 16);
|
||||||
|
|
||||||
|
// 绘制结尾文字
|
||||||
|
ctx.fillText('特发此证,以表谢忱!', 20, 200);
|
||||||
|
|
||||||
|
// 绘制证书编号
|
||||||
|
ctx.font = '10px sans-serif';
|
||||||
|
ctx.fillStyle = '#FF19A0';
|
||||||
|
ctx.textAlign = 'right';
|
||||||
|
const certificateNo = `证书编号: ${currentItem.certificate_no || currentItem.id || '234546678896666788'}`;
|
||||||
|
ctx.fillText(certificateNo, this.canvasWidth - 20, this.canvasHeight - 20);
|
||||||
|
|
||||||
|
// 绘制证书图标
|
||||||
|
const badgeImgPath = await this.loadImage(currentItem.certificate_url);
|
||||||
|
const badgeImg = canvasNode.createImage();
|
||||||
|
badgeImg.src = badgeImgPath;
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
badgeImg.onload = () => {
|
||||||
|
ctx.drawImage(badgeImg, this.canvasWidth - 60, this.canvasHeight - 80, 40, 40);
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 导出为 base64 数据
|
||||||
|
const dataURL = canvasNode.toDataURL('image/png');
|
||||||
|
const base64Data = dataURL.replace(/^data:image\/\w+;base64,/, '');
|
||||||
|
|
||||||
|
// 将 base64 数据写入临时文件
|
||||||
|
const tempFilePath = `${tt.env.USER_DATA_PATH}/temp_certificate.png`;
|
||||||
|
const fs = tt.getFileSystemManager();
|
||||||
|
fs.writeFileSync(tempFilePath, base64Data, 'base64');
|
||||||
|
|
||||||
// 保存到相册
|
// 保存到相册
|
||||||
uni.saveImageToPhotosAlbum({
|
uni.saveImageToPhotosAlbum({
|
||||||
filePath: res.tempFilePath,
|
filePath: tempFilePath,
|
||||||
success: () => {
|
success: () => {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
@ -250,29 +327,14 @@ export default {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
const errorMsg = error?.message || error?.toString() || '未知错误';
|
const errorMsg = error?.message || error?.toString() || '未知错误';
|
||||||
const errorInfo = `生成图片失败: ${errorMsg}`;
|
|
||||||
console.log(errorInfo, '--=')
|
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: errorInfo.length > 20 ? '生成图片失败,请重试' : errorInfo,
|
title: errorMsg.length > 20 ? '生成图片失败,请重试' : errorMsg,
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
duration: 3000
|
duration: 3000
|
||||||
});
|
});
|
||||||
// 使用多种方式记录错误,确保线上环境能捕获
|
|
||||||
console.error('生成证书图片失败:', error);
|
console.error('生成证书图片失败:', error);
|
||||||
console.error('错误详情:', {
|
|
||||||
message: error?.message,
|
|
||||||
stack: error?.stack,
|
|
||||||
error: error
|
|
||||||
});
|
|
||||||
// 如果 console 不可用,尝试通过其他方式记录
|
|
||||||
if (typeof uni.reportError === 'function') {
|
|
||||||
uni.reportError({
|
|
||||||
error: errorInfo,
|
|
||||||
errorInfo: JSON.stringify(error)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
|
||||||
generateCertificateTemplate() {
|
generateCertificateTemplate() {
|
||||||
// 使用存储的证书信息
|
// 使用存储的证书信息
|
||||||
const item = this.currentCertificateItem;
|
const item = this.currentCertificateItem;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<view v-if="show" :class="modalClassList" @click.stop="handleCancel">
|
<view v-if="show" class="donation-confirm-modal" :class="[type === 'dog' ? 'modal-dog' : 'modal-cat']" @click.stop="handleCancel">
|
||||||
<view class="modal-content" @click.stop="">
|
<view class="modal-content" @click.stop="">
|
||||||
<!-- 顶部图标 -->
|
<!-- 顶部图标 -->
|
||||||
<image class="bowl-icon" :src="bowlIcon" mode="aspectFit" />
|
<image class="bowl-icon" :src="bowlIcon" mode="aspectFit" />
|
||||||
@ -64,11 +64,6 @@ export default {
|
|||||||
? `${imgPrefix}welfare-dogFood.png`
|
? `${imgPrefix}welfare-dogFood.png`
|
||||||
: `${imgPrefix}welfare-catFood.png`;
|
: `${imgPrefix}welfare-catFood.png`;
|
||||||
},
|
},
|
||||||
modalClassList() {
|
|
||||||
const baseClass = 'donation-confirm-modal';
|
|
||||||
const typeClass = this.type === 'dog' ? 'modal-dog' : 'modal-cat';
|
|
||||||
return [baseClass, typeClass];
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleCancel() {
|
handleCancel() {
|
||||||
|
|||||||
@ -223,6 +223,7 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleDogCardClick() {
|
handleDogCardClick() {
|
||||||
|
console.log(this)
|
||||||
this.donationType = 'dog';
|
this.donationType = 'dog';
|
||||||
this.showDonationModal = true;
|
this.showDonationModal = true;
|
||||||
},
|
},
|
||||||
|
|||||||
@ -157,10 +157,7 @@
|
|||||||
{
|
{
|
||||||
"path": "welfare/certificate-list",
|
"path": "welfare/certificate-list",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "我的证书",
|
"navigationBarTitleText": "我的证书"
|
||||||
"usingComponents": {
|
|
||||||
"wxml-to-canvas": "/wxcomponents/wxml-to-canvas/index"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
235
src/pages/client/address/components/ListPageTemp.vue
Normal file
235
src/pages/client/address/components/ListPageTemp.vue
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
<template>
|
||||||
|
<scroll-view class="list-template-wrapper" :scroll-y="!disableScroll" :refresher-enabled="true"
|
||||||
|
:refresher-triggered="refreshTriggered" @refresherrefresh="onRefresh" @scrolltolower="onLoadMore">
|
||||||
|
<slot name="top" />
|
||||||
|
<view class="list-content" v-if="list.length > 0">
|
||||||
|
<view v-for="(item, index) in list" :key="item[idKey]" :class="{ left: index % 2 === 0 }"
|
||||||
|
class="flex-column-start news-item" @click="clickCell(item)">
|
||||||
|
<slot style="width: 100%" name="item" :data="{
|
||||||
|
...item,
|
||||||
|
...listExtraFields,
|
||||||
|
deleteSelect: !!item.deleteSelect,
|
||||||
|
}" />
|
||||||
|
</view>
|
||||||
|
<uni-load-more v-if="isLoading || (!isLoading && total && total === list.length)"
|
||||||
|
:status="isLoading ? 'loading' : 'nomore'"></uni-load-more>
|
||||||
|
</view>
|
||||||
|
<view v-else class="empty-container">
|
||||||
|
<image class="home-ration" mode="widthFix" :src="emptyImage || defaultEmptyImage" />
|
||||||
|
<text v-if="emptyText" class="empty-text">{{ emptyText }}</text>
|
||||||
|
</view>
|
||||||
|
<slot name="bottom" />
|
||||||
|
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "ListPageTemp",
|
||||||
|
props: {
|
||||||
|
getDataPromise: {
|
||||||
|
type: Function,
|
||||||
|
},
|
||||||
|
// 格式化请求结果的方法
|
||||||
|
resultFormatFunc: {
|
||||||
|
type: Function,
|
||||||
|
},
|
||||||
|
requestData: {
|
||||||
|
defult: () => { },
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
// 列表元素额外字段
|
||||||
|
listExtraFields: {
|
||||||
|
type: Object,
|
||||||
|
default: () => { },
|
||||||
|
},
|
||||||
|
reloadFlag: {
|
||||||
|
default: 0,
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
// 是否分页
|
||||||
|
isPagiantion: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
// 分页数量, 不分页时有效
|
||||||
|
pageSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 999,
|
||||||
|
},
|
||||||
|
defaultList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
disableScroll: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
idKey: {
|
||||||
|
type: String,
|
||||||
|
default: "id",
|
||||||
|
},
|
||||||
|
// 占位图片地址
|
||||||
|
emptyImage: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
// 空状态文本(传入时才显示)
|
||||||
|
emptyText: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isLoading: false,
|
||||||
|
total: 0,
|
||||||
|
list: [],
|
||||||
|
refreshTriggered: false,
|
||||||
|
p: 1,
|
||||||
|
num: 10,
|
||||||
|
defaultEmptyImage: 'https://activity.wagoo.live/empty.png', // 默认占位图片
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
reloadFlag: {
|
||||||
|
handler(value) {
|
||||||
|
if (value) {
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
requestData: {
|
||||||
|
handler(data) {
|
||||||
|
if (data) {
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
defaultList: {
|
||||||
|
handler(list) {
|
||||||
|
this.list = list;
|
||||||
|
this.$forceUpdate();
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onRefresh() {
|
||||||
|
if (this.refreshTriggered) return;
|
||||||
|
this.refreshTriggered = true;
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
// this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadMore() {
|
||||||
|
if (!this.isPagiantion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.isLoading && this.total > this.list.length) {
|
||||||
|
this.p++;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clickCell(item) {
|
||||||
|
this.$emit("clickCell", item);
|
||||||
|
},
|
||||||
|
getList() {
|
||||||
|
if (this.isLoading) return;
|
||||||
|
this.isLoading = true;
|
||||||
|
|
||||||
|
// TODO: 删除测试数据
|
||||||
|
if (!this.getDataPromise) {
|
||||||
|
this.list = [0, 1, 2, 3, 4, 5, 6].map((v, k) => {
|
||||||
|
return {
|
||||||
|
title: "消息名称0000sssss撒大苏打大苏打" + v,
|
||||||
|
id: k,
|
||||||
|
len: 10,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.total = 9;
|
||||||
|
this.isLoading = false;
|
||||||
|
this.refreshTriggered = false;
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = Object.assign(
|
||||||
|
{},
|
||||||
|
{ p: this.p, num: !this.isPagiantion ? this.pageSize : this.num },
|
||||||
|
this.requestData
|
||||||
|
);
|
||||||
|
this.getDataPromise(data)
|
||||||
|
.then((res) => {
|
||||||
|
const list =
|
||||||
|
this.p === 1
|
||||||
|
? res?.data || []
|
||||||
|
: [...this.list, ...(res?.data || [])];
|
||||||
|
this.list = this.resultFormatFunc
|
||||||
|
? this.resultFormatFunc(list)
|
||||||
|
: list;
|
||||||
|
this.total = res?.count || 0;
|
||||||
|
this.$emit("getList", this.list, res);
|
||||||
|
// console.log(this.list,'???')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.refreshTriggered = false;
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.list-template-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.list-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-item {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 400rpx auto 0;
|
||||||
|
padding: 0 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-ration {
|
||||||
|
width: 160px;
|
||||||
|
height: 174px;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -38,11 +38,13 @@
|
|||||||
<script>
|
<script>
|
||||||
import AddressItem from "./components/AddressItem.vue";
|
import AddressItem from "./components/AddressItem.vue";
|
||||||
import PopUpModal from "../../../components/PopUpModal.vue";
|
import PopUpModal from "../../../components/PopUpModal.vue";
|
||||||
|
import ListPageTemp from "./components/ListPageTemp.vue";
|
||||||
import { delAddress, getAddressList } from "../../../api/address";
|
import { delAddress, getAddressList } from "../../../api/address";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
AddressItem,
|
AddressItem,
|
||||||
|
ListPageTemp,
|
||||||
PopUpModal,
|
PopUpModal,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|||||||
@ -4,10 +4,10 @@
|
|||||||
<image class="login-ground-img" :src="`${imgPrefix}loginGroundImg.png`" mode="widthFix" />
|
<image class="login-ground-img" :src="`${imgPrefix}loginGroundImg.png`" mode="widthFix" />
|
||||||
<view class="btnConent">
|
<view class="btnConent">
|
||||||
<button v-show="!checked" class="loginBtn" @click="unCheckAndGetPhoneNumber">
|
<button v-show="!checked" class="loginBtn" @click="unCheckAndGetPhoneNumber">
|
||||||
手机快捷登录
|
抖音用户信息授权登录
|
||||||
</button>
|
</button>
|
||||||
<button v-show="checked" class="loginBtn" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">
|
<button v-show="checked" class="loginBtn" @tap="getPhoneNumber" >
|
||||||
手机快捷登录
|
抖音用户信息授权登录
|
||||||
</button>
|
</button>
|
||||||
<view class="notLoginBtn" @click="goBack">
|
<view class="notLoginBtn" @click="goBack">
|
||||||
暂不登录
|
暂不登录
|
||||||
@ -49,8 +49,12 @@ export default {
|
|||||||
imgPrefix,
|
imgPrefix,
|
||||||
checked: false,
|
checked: false,
|
||||||
uncheckMessageDialog: false,
|
uncheckMessageDialog: false,
|
||||||
|
yaoqing_code: ''
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
this.yaoqing_code = options.yaoqing_code
|
||||||
|
},
|
||||||
mounted() { },
|
mounted() { },
|
||||||
methods: {
|
methods: {
|
||||||
ptfwxy() {
|
ptfwxy() {
|
||||||
@ -68,35 +72,6 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
changeChecked() {
|
changeChecked() {
|
||||||
uni.requestSubscribeMessage({
|
|
||||||
tmplIds: ['QoTeQwj4xw2UQMK5jI67MzAVOo6og76oqZ7BDIJW7cE', 'GPWlTkaNbi7JqvxltLKuZZMtKedSZfEKlirV7yOUu-0'],// TEMPLATE_ID替换为选用的模版id
|
|
||||||
success(res) {
|
|
||||||
uni.hideLoading()
|
|
||||||
if (res['QoTeQwj4xw2UQMK5jI67MzAVOo6og76oqZ7BDIJW7cE'] === 'accept') {
|
|
||||||
// setSubscribeStatus(true, '已订阅')
|
|
||||||
uni.showToast({
|
|
||||||
title: '订阅成功',
|
|
||||||
icon: 'success',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (res['GPWlTkaNbi7JqvxltLKuZZMtKedSZfEKlirV7yOUu-0'] === 'accept') {
|
|
||||||
// setSubscribeStatus(true, '已订阅')
|
|
||||||
uni.showToast({
|
|
||||||
title: '订阅成功',
|
|
||||||
icon: 'success',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fail() {
|
|
||||||
uni.showToast({
|
|
||||||
title: '订阅失败',
|
|
||||||
icon: 'error',
|
|
||||||
})
|
|
||||||
},
|
|
||||||
complete: () => {
|
|
||||||
// isSubscribing = false
|
|
||||||
},
|
|
||||||
})
|
|
||||||
this.checked = !this.checked;
|
this.checked = !this.checked;
|
||||||
},
|
},
|
||||||
unCheckAndGetPhoneNumber() {
|
unCheckAndGetPhoneNumber() {
|
||||||
@ -106,63 +81,25 @@ export default {
|
|||||||
duration: 2000
|
duration: 2000
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async getPhoneNumber(e) {
|
getmembeInfo(nickName,avatarUrl) {
|
||||||
// console.log(e,'?')
|
const inviteCode = this.yaoqing_code ? this.yaoqing_code : getApp().globalData.inviteCode
|
||||||
// return
|
|
||||||
// 检查是否有 code
|
|
||||||
if (!e.detail.code) {
|
|
||||||
uni.showToast({
|
|
||||||
title: "获取手机号失败,请重试",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uni.showLoading({
|
|
||||||
title: "获取手机号中...",
|
|
||||||
icon: "none",
|
|
||||||
mask: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 登录前先记录是否有邀请码(referrerID),用于登录后导航判断
|
|
||||||
const hasReferrer =
|
|
||||||
this.$store.state?.user && this.$store.state.user.referrerID;
|
|
||||||
// 拿着code获取手机号
|
|
||||||
const codeRes = await getPhone(e.detail.code);
|
|
||||||
const phone = codeRes?.data?.phoneNumber || "";
|
|
||||||
|
|
||||||
if (!phone) {
|
|
||||||
uni.hideLoading();
|
|
||||||
uni.showToast({
|
|
||||||
title: "获取手机号失败,请重试",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uni.showLoading({
|
uni.showLoading({
|
||||||
title: "登录中...",
|
title: "登录中...",
|
||||||
icon: "none",
|
icon: "none",
|
||||||
mask: true,
|
mask: true,
|
||||||
});
|
});
|
||||||
|
login(nickName,avatarUrl).then((res) => {
|
||||||
// 手机号登录
|
uni.hideLoading()
|
||||||
login(phone)
|
|
||||||
.then((res) => {
|
|
||||||
uni.hideLoading();
|
|
||||||
this.$store.dispatch("user/setToken", res?.data?.token || "");
|
this.$store.dispatch("user/setToken", res?.data?.token || "");
|
||||||
this.$store.dispatch("user/setUserInfo", res?.data || {});
|
this.$store.dispatch("user/setUserInfo", res?.data || {});
|
||||||
|
var pages = getCurrentPages();
|
||||||
// 如果是带邀请码(referrerID)的登录,统一跳转到首页
|
console.log(pages,'sssss')
|
||||||
if (hasReferrer) {
|
if (inviteCode) {
|
||||||
uni.reLaunch({
|
uni.reLaunch({
|
||||||
url: "/pages/client/index/index",
|
url: "/pages/client/index/index",
|
||||||
});
|
});
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var pages = getCurrentPages();
|
|
||||||
if (pages.length === 1) {
|
if (pages.length === 1) {
|
||||||
uni.reLaunch({
|
uni.reLaunch({
|
||||||
url: "/pages/client/index/index",
|
url: "/pages/client/index/index",
|
||||||
@ -170,14 +107,95 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uni.navigateBack();
|
uni.navigateBack();
|
||||||
})
|
}).catch((err) => {
|
||||||
.catch((err) => {
|
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: err || "登录失败,请重试",
|
title: err || "登录失败,请重试",
|
||||||
icon: "none",
|
icon: "none",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
// login( this.nickName, this.avatarUrl).then((res) => {
|
||||||
|
// uni.hideLoading()
|
||||||
|
// this.$store.dispatch("user/setToken", res?.data?.token || "");
|
||||||
|
// this.$store.dispatch("user/setUserInfo", res?.data || {});
|
||||||
|
// var pages = getCurrentPages();
|
||||||
|
// if (inviteCode) {
|
||||||
|
// uni.reLaunch({
|
||||||
|
// url: "/pages/client/index/index",
|
||||||
|
// });
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// if (pages.length === 1) {
|
||||||
|
// uni.reLaunch({
|
||||||
|
// url: "/pages/client/index/index",
|
||||||
|
// });
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// uni.navigateBack();
|
||||||
|
// }).catch((err) => {
|
||||||
|
// uni.hideLoading();
|
||||||
|
// uni.showToast({
|
||||||
|
// title: err || "登录失败,请重试",
|
||||||
|
// icon: "none",
|
||||||
|
// });
|
||||||
|
// }),
|
||||||
|
async getPhoneNumber(e) {
|
||||||
|
// console.log(e,'?')
|
||||||
|
// return
|
||||||
|
// 检查是否有 code
|
||||||
|
// if (!e.detail.code) {
|
||||||
|
// uni.showToast({
|
||||||
|
// title: "获取手机号失败,请重试",
|
||||||
|
// icon: "none",
|
||||||
|
// });
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// uni.showLoading({
|
||||||
|
// title: "获取手机号中...",
|
||||||
|
// icon: "none",
|
||||||
|
// mask: true,
|
||||||
|
// });
|
||||||
|
|
||||||
|
try {
|
||||||
|
let that = this
|
||||||
|
tt.getUserProfile({
|
||||||
|
|
||||||
|
success(res) {
|
||||||
|
console.log(res,'???')
|
||||||
|
// this.nickName = res.res.userInfo.nickName
|
||||||
|
// this.avatarUrl = res.userInfo.avatarUrl
|
||||||
|
that.getmembeInfo(res.userInfo.nickName,res.userInfo.avatarUrl)
|
||||||
|
// console.log(res,'--=')
|
||||||
|
},
|
||||||
|
fail(res) {
|
||||||
|
console.log("getUserProfile 调用失败", res);
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// const inviteCode = this.yaoqing_code ? this.yaoqing_code : getApp().globalData.inviteCode
|
||||||
|
// console.log('获取到邀请码----->', inviteCode)
|
||||||
|
// 手机号登录
|
||||||
|
|
||||||
|
// 拿着code获取手机号
|
||||||
|
// const codeRes = await getPhone(e.detail.code);
|
||||||
|
// const phone = codeRes?.data?.phoneNumber || "";
|
||||||
|
|
||||||
|
// if (!phone) {
|
||||||
|
// uni.hideLoading();
|
||||||
|
// uni.showToast({
|
||||||
|
// title: "获取手机号失败,请重试",
|
||||||
|
// icon: "none",
|
||||||
|
// });
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
console.error("获取手机号失败:", error);
|
console.error("获取手机号失败:", error);
|
||||||
@ -215,10 +233,6 @@ export default {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 登录前先记录是否有邀请码(referrerID),用于登录后导航判断
|
|
||||||
const hasReferrer =
|
|
||||||
this.$store.state?.user && this.$store.state.user.referrerID;
|
|
||||||
|
|
||||||
uni.showLoading({
|
uni.showLoading({
|
||||||
title: "加载中...",
|
title: "加载中...",
|
||||||
icon: "none",
|
icon: "none",
|
||||||
@ -228,15 +242,6 @@ export default {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.$store.dispatch("user/setToken", res?.data?.token || "");
|
this.$store.dispatch("user/setToken", res?.data?.token || "");
|
||||||
this.$store.dispatch("user/setUserInfo", res?.data || {});
|
this.$store.dispatch("user/setUserInfo", res?.data || {});
|
||||||
|
|
||||||
// 如果是带邀请码(referrerID)的登录,统一跳转到首页
|
|
||||||
if (hasReferrer) {
|
|
||||||
uni.reLaunch({
|
|
||||||
url: "/pages/client/index/index",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pages = getCurrentPages();
|
var pages = getCurrentPages();
|
||||||
if (pages.length === 1) {
|
if (pages.length === 1) {
|
||||||
uni.reLaunch({
|
uni.reLaunch({
|
||||||
|
|||||||
235
src/pages/client/cart/components/ListPageTemp.vue
Normal file
235
src/pages/client/cart/components/ListPageTemp.vue
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
<template>
|
||||||
|
<scroll-view class="list-template-wrapper" :scroll-y="!disableScroll" :refresher-enabled="true"
|
||||||
|
:refresher-triggered="refreshTriggered" @refresherrefresh="onRefresh" @scrolltolower="onLoadMore">
|
||||||
|
<slot name="top" />
|
||||||
|
<view class="list-content" v-if="list.length > 0">
|
||||||
|
<view v-for="(item, index) in list" :key="item[idKey]" :class="{ left: index % 2 === 0 }"
|
||||||
|
class="flex-column-start news-item" @click="clickCell(item)">
|
||||||
|
<slot style="width: 100%" name="item" :data="{
|
||||||
|
...item,
|
||||||
|
...listExtraFields,
|
||||||
|
deleteSelect: !!item.deleteSelect,
|
||||||
|
}" />
|
||||||
|
</view>
|
||||||
|
<uni-load-more v-if="isLoading || (!isLoading && total && total === list.length)"
|
||||||
|
:status="isLoading ? 'loading' : 'nomore'"></uni-load-more>
|
||||||
|
</view>
|
||||||
|
<view v-else class="empty-container">
|
||||||
|
<image class="home-ration" mode="widthFix" :src="emptyImage || defaultEmptyImage" />
|
||||||
|
<text v-if="emptyText" class="empty-text">{{ emptyText }}</text>
|
||||||
|
</view>
|
||||||
|
<slot name="bottom" />
|
||||||
|
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "ListPageTemp",
|
||||||
|
props: {
|
||||||
|
getDataPromise: {
|
||||||
|
type: Function,
|
||||||
|
},
|
||||||
|
// 格式化请求结果的方法
|
||||||
|
resultFormatFunc: {
|
||||||
|
type: Function,
|
||||||
|
},
|
||||||
|
requestData: {
|
||||||
|
defult: () => { },
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
// 列表元素额外字段
|
||||||
|
listExtraFields: {
|
||||||
|
type: Object,
|
||||||
|
default: () => { },
|
||||||
|
},
|
||||||
|
reloadFlag: {
|
||||||
|
default: 0,
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
// 是否分页
|
||||||
|
isPagiantion: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
// 分页数量, 不分页时有效
|
||||||
|
pageSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 999,
|
||||||
|
},
|
||||||
|
defaultList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
disableScroll: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
idKey: {
|
||||||
|
type: String,
|
||||||
|
default: "id",
|
||||||
|
},
|
||||||
|
// 占位图片地址
|
||||||
|
emptyImage: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
// 空状态文本(传入时才显示)
|
||||||
|
emptyText: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isLoading: false,
|
||||||
|
total: 0,
|
||||||
|
list: [],
|
||||||
|
refreshTriggered: false,
|
||||||
|
p: 1,
|
||||||
|
num: 10,
|
||||||
|
defaultEmptyImage: 'https://activity.wagoo.live/empty.png', // 默认占位图片
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
reloadFlag: {
|
||||||
|
handler(value) {
|
||||||
|
if (value) {
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
requestData: {
|
||||||
|
handler(data) {
|
||||||
|
if (data) {
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
defaultList: {
|
||||||
|
handler(list) {
|
||||||
|
this.list = list;
|
||||||
|
this.$forceUpdate();
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onRefresh() {
|
||||||
|
if (this.refreshTriggered) return;
|
||||||
|
this.refreshTriggered = true;
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
// this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadMore() {
|
||||||
|
if (!this.isPagiantion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.isLoading && this.total > this.list.length) {
|
||||||
|
this.p++;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clickCell(item) {
|
||||||
|
this.$emit("clickCell", item);
|
||||||
|
},
|
||||||
|
getList() {
|
||||||
|
if (this.isLoading) return;
|
||||||
|
this.isLoading = true;
|
||||||
|
|
||||||
|
// TODO: 删除测试数据
|
||||||
|
if (!this.getDataPromise) {
|
||||||
|
this.list = [0, 1, 2, 3, 4, 5, 6].map((v, k) => {
|
||||||
|
return {
|
||||||
|
title: "消息名称0000sssss撒大苏打大苏打" + v,
|
||||||
|
id: k,
|
||||||
|
len: 10,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.total = 9;
|
||||||
|
this.isLoading = false;
|
||||||
|
this.refreshTriggered = false;
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = Object.assign(
|
||||||
|
{},
|
||||||
|
{ p: this.p, num: !this.isPagiantion ? this.pageSize : this.num },
|
||||||
|
this.requestData
|
||||||
|
);
|
||||||
|
this.getDataPromise(data)
|
||||||
|
.then((res) => {
|
||||||
|
const list =
|
||||||
|
this.p === 1
|
||||||
|
? res?.data || []
|
||||||
|
: [...this.list, ...(res?.data || [])];
|
||||||
|
this.list = this.resultFormatFunc
|
||||||
|
? this.resultFormatFunc(list)
|
||||||
|
: list;
|
||||||
|
this.total = res?.count || 0;
|
||||||
|
this.$emit("getList", this.list, res);
|
||||||
|
// console.log(this.list,'???')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.refreshTriggered = false;
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.list-template-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.list-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-item {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 400rpx auto 0;
|
||||||
|
padding: 0 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-ration {
|
||||||
|
width: 160px;
|
||||||
|
height: 174px;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -51,7 +51,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import CartItem from "./components/CartItem.vue";
|
import CartItem from "./components/CartItem.vue";
|
||||||
import ListPageTemp from "@/components/ListPageTemp.vue";
|
import ListPageTemp from "./components/ListPageTemp.vue";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getCartList,
|
getCartList,
|
||||||
|
|||||||
235
src/pages/client/coupon/ListPageTemp.vue
Normal file
235
src/pages/client/coupon/ListPageTemp.vue
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
<template>
|
||||||
|
<scroll-view class="list-template-wrapper" :scroll-y="!disableScroll" :refresher-enabled="true"
|
||||||
|
:refresher-triggered="refreshTriggered" @refresherrefresh="onRefresh" @scrolltolower="onLoadMore">
|
||||||
|
<slot name="top" />
|
||||||
|
<view class="list-content" v-if="list.length > 0">
|
||||||
|
<view v-for="(item, index) in list" :key="item[idKey]" :class="{ left: index % 2 === 0 }"
|
||||||
|
class="flex-column-start news-item" @click="clickCell(item)">
|
||||||
|
<slot style="width: 100%" name="item" :data="{
|
||||||
|
...item,
|
||||||
|
...listExtraFields,
|
||||||
|
deleteSelect: !!item.deleteSelect,
|
||||||
|
}" />
|
||||||
|
</view>
|
||||||
|
<uni-load-more v-if="isLoading || (!isLoading && total && total === list.length)"
|
||||||
|
:status="isLoading ? 'loading' : 'nomore'"></uni-load-more>
|
||||||
|
</view>
|
||||||
|
<view v-else class="empty-container">
|
||||||
|
<image class="home-ration" mode="widthFix" :src="emptyImage || defaultEmptyImage" />
|
||||||
|
<text v-if="emptyText" class="empty-text">{{ emptyText }}</text>
|
||||||
|
</view>
|
||||||
|
<slot name="bottom" />
|
||||||
|
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "ListPageTemp",
|
||||||
|
props: {
|
||||||
|
getDataPromise: {
|
||||||
|
type: Function,
|
||||||
|
},
|
||||||
|
// 格式化请求结果的方法
|
||||||
|
resultFormatFunc: {
|
||||||
|
type: Function,
|
||||||
|
},
|
||||||
|
requestData: {
|
||||||
|
defult: () => { },
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
// 列表元素额外字段
|
||||||
|
listExtraFields: {
|
||||||
|
type: Object,
|
||||||
|
default: () => { },
|
||||||
|
},
|
||||||
|
reloadFlag: {
|
||||||
|
default: 0,
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
// 是否分页
|
||||||
|
isPagiantion: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
// 分页数量, 不分页时有效
|
||||||
|
pageSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 999,
|
||||||
|
},
|
||||||
|
defaultList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
disableScroll: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
idKey: {
|
||||||
|
type: String,
|
||||||
|
default: "id",
|
||||||
|
},
|
||||||
|
// 占位图片地址
|
||||||
|
emptyImage: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
// 空状态文本(传入时才显示)
|
||||||
|
emptyText: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isLoading: false,
|
||||||
|
total: 0,
|
||||||
|
list: [],
|
||||||
|
refreshTriggered: false,
|
||||||
|
p: 1,
|
||||||
|
num: 10,
|
||||||
|
defaultEmptyImage: 'https://activity.wagoo.live/empty.png', // 默认占位图片
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
reloadFlag: {
|
||||||
|
handler(value) {
|
||||||
|
if (value) {
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
requestData: {
|
||||||
|
handler(data) {
|
||||||
|
if (data) {
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
defaultList: {
|
||||||
|
handler(list) {
|
||||||
|
this.list = list;
|
||||||
|
this.$forceUpdate();
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onRefresh() {
|
||||||
|
if (this.refreshTriggered) return;
|
||||||
|
this.refreshTriggered = true;
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
// this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadMore() {
|
||||||
|
if (!this.isPagiantion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.isLoading && this.total > this.list.length) {
|
||||||
|
this.p++;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clickCell(item) {
|
||||||
|
this.$emit("clickCell", item);
|
||||||
|
},
|
||||||
|
getList() {
|
||||||
|
if (this.isLoading) return;
|
||||||
|
this.isLoading = true;
|
||||||
|
|
||||||
|
// TODO: 删除测试数据
|
||||||
|
if (!this.getDataPromise) {
|
||||||
|
this.list = [0, 1, 2, 3, 4, 5, 6].map((v, k) => {
|
||||||
|
return {
|
||||||
|
title: "消息名称0000sssss撒大苏打大苏打" + v,
|
||||||
|
id: k,
|
||||||
|
len: 10,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.total = 9;
|
||||||
|
this.isLoading = false;
|
||||||
|
this.refreshTriggered = false;
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = Object.assign(
|
||||||
|
{},
|
||||||
|
{ p: this.p, num: !this.isPagiantion ? this.pageSize : this.num },
|
||||||
|
this.requestData
|
||||||
|
);
|
||||||
|
this.getDataPromise(data)
|
||||||
|
.then((res) => {
|
||||||
|
const list =
|
||||||
|
this.p === 1
|
||||||
|
? res?.data || []
|
||||||
|
: [...this.list, ...(res?.data || [])];
|
||||||
|
this.list = this.resultFormatFunc
|
||||||
|
? this.resultFormatFunc(list)
|
||||||
|
: list;
|
||||||
|
this.total = res?.count || 0;
|
||||||
|
this.$emit("getList", this.list, res);
|
||||||
|
// console.log(this.list,'???')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.refreshTriggered = false;
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.list-template-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.list-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-item {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 400rpx auto 0;
|
||||||
|
padding: 0 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-ration {
|
||||||
|
width: 160px;
|
||||||
|
height: 174px;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -18,7 +18,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ListPageTemp from "@/components/ListPageTemp";
|
import ListPageTemp from "./ListPageTemp";
|
||||||
import CouponItem from "@/components/coupon/CouponItem";
|
import CouponItem from "@/components/coupon/CouponItem";
|
||||||
import { getCouponData, receiveCoupon } from "@/api/coupon";
|
import { getCouponData, receiveCoupon } from "@/api/coupon";
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TabsList from "@/components/TabsList.vue";
|
import TabsList from "@/components/TabsList.vue";
|
||||||
import ListPageTemp from "@/components/ListPageTemp.vue";
|
import ListPageTemp from "./ListPageTemp.vue";
|
||||||
import ServiceCouponItem from "@/components/coupon/ServiceCouponItem";
|
import ServiceCouponItem from "@/components/coupon/ServiceCouponItem";
|
||||||
|
|
||||||
import { getMyServiceCouponList } from "@/api/coupon";
|
import { getMyServiceCouponList } from "@/api/coupon";
|
||||||
|
|||||||
@ -62,21 +62,21 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="line" />
|
<view class="line" />
|
||||||
<view class="itemWrapper" @click="toDogTraining">
|
<view class="itemWrapper" @click="toPublicBenefit">
|
||||||
<text class="titlWrapper">狗狗训练</text>
|
<text class="titlWrapper">关爱宠物,传递温情</text>
|
||||||
<view class="content">上门服务</view>
|
<view class="content">公益助力</view>
|
||||||
<view class="tips">上门训犬、寄养、喂猫、遛狗</view>
|
<view class="tips">帮我找个家</view>
|
||||||
<view class="itemImg">
|
<view class="itemImg">
|
||||||
<image class="menu-img-sm" :src="`${imgPrefix}home-dogTraining.png`" mode="aspectFill" />
|
<image class="menu-img-sm" :src="`${imgPrefix}h-public.png`" mode="aspectFill" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="secondMenu">
|
<view class="secondMenu">
|
||||||
<scroll-view class="scrollWrapper" :scroll-x="secondMenuItemList.length > 4" enable-flex
|
<scroll-view class="scrollWrapper" scroll-x enable-flex
|
||||||
:scroll-with-animation="false"
|
:scroll-with-animation="false"
|
||||||
@scroll="onSecondMenuScroll">
|
@scroll="onSecondMenuScroll">
|
||||||
<view class="secondMenuInner" :class="{ 'no-scroll': secondMenuItemList.length <= 4 }">
|
<view class="secondMenuInner">
|
||||||
<view class="itemWrapper" v-for="(item, i) in secondMenuItemList" :key="i"
|
<view class="itemWrapper" v-for="(item, i) in secondMenuItemList" :key="i"
|
||||||
@click="handleNav(item)">
|
@click="handleNav(item)">
|
||||||
<view class="imgWrapper">
|
<view class="imgWrapper">
|
||||||
@ -92,19 +92,19 @@
|
|||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<view class="custom-indicator" v-if="secondMenuItemList.length > 4">
|
<view class="custom-indicator">
|
||||||
<view class="itemLine" :class="{ active: secondMenuIndicatorIndex === 0 }"></view>
|
<view class="itemLine" :class="{ active: secondMenuIndicatorIndex === 0 }"></view>
|
||||||
<view class="itemLine" :class="{ active: secondMenuIndicatorIndex === 1 }"></view>
|
<view class="itemLine" :class="{ active: secondMenuIndicatorIndex === 1 }"></view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="thirdMenu">
|
<view class="thirdMenu">
|
||||||
<view class="itemWrapper" @click="toPublicBenefit">
|
<view class="itemWrapper" @click="toDogTraining">
|
||||||
<view>
|
<view>
|
||||||
<view class="title">公益助力</view>
|
<view class="title">上门服务</view>
|
||||||
<view class="tips">帮我找个家</view>
|
<view class="tips">训犬寄养、喂猫遛狗</view>
|
||||||
</view>
|
</view>
|
||||||
<image class="third-icon" :src="`${imgPrefix}home-publicBenefit.png`" mode="aspectFill" />
|
<image class="third-icon1" :src="`${imgPrefix}h-door.png`" mode="aspectFill" />
|
||||||
</view>
|
</view>
|
||||||
<view class="itemWrapper" @click="toJoin">
|
<view class="itemWrapper" @click="toJoin">
|
||||||
<view>
|
<view>
|
||||||
@ -188,6 +188,11 @@ export default {
|
|||||||
img: `${imgPrefix}home-openVip.png`,
|
img: `${imgPrefix}home-openVip.png`,
|
||||||
naviUrl: "/pages/richText/member-interests"
|
naviUrl: "/pages/richText/member-interests"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "健康顾问",
|
||||||
|
tips: "宠物健康智能助手",
|
||||||
|
img: `${imgPrefix}healthConsultant.png`,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
walletInfo: {}, // 钱包信息
|
walletInfo: {}, // 钱包信息
|
||||||
secondMenuIndicatorIndex: 0, // 第二排菜单指示器当前页 0=第一页 1=第二页
|
secondMenuIndicatorIndex: 0, // 第二排菜单指示器当前页 0=第一页 1=第二页
|
||||||
@ -245,9 +250,14 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
toDogTraining() {
|
toDogTraining() {
|
||||||
uni.navigateTo({
|
uni.showToast({
|
||||||
url: '/pageHome/service/index'
|
title: '敬请期待',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
});
|
});
|
||||||
|
// uni.navigateTo({
|
||||||
|
// url: '/pageHome/service/index'
|
||||||
|
// });
|
||||||
},
|
},
|
||||||
// 我的钱包
|
// 我的钱包
|
||||||
buyService() {
|
buyService() {
|
||||||
@ -375,8 +385,13 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu-img-sm {
|
.menu-img-sm {
|
||||||
width: 120rpx;
|
width: 160rpx;
|
||||||
height: 164rpx;
|
height: 160rpx;
|
||||||
|
}
|
||||||
|
.third-icon1{
|
||||||
|
width: 96rpx;
|
||||||
|
height: 120rpx;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.second-icon,
|
.second-icon,
|
||||||
@ -601,13 +616,9 @@ export default {
|
|||||||
|
|
||||||
.scrollWrapper {
|
.scrollWrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto; /* 由内容撑开;小程序 scroll-view 横向滚动时默认可能不随内容计算高度 */
|
|
||||||
min-height: 220rpx; /* 兜底:约等于 padding-top + 图标 + 标题 + 副标题 */
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start; /* 不拉伸子项,高度由内容决定 */
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
padding-top: 24rpx;
|
padding: 32rpx 0 20rpx 0;
|
||||||
padding-bottom: 24rpx;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
.secondMenuInner {
|
.secondMenuInner {
|
||||||
@ -615,16 +626,6 @@ export default {
|
|||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
width: 937.5rpx; /* 5 * 187.5 一屏4个共5项 */
|
width: 937.5rpx; /* 5 * 187.5 一屏4个共5项 */
|
||||||
|
|
||||||
&.no-scroll {
|
|
||||||
width: 100%; /* 四项时一屏排满,不滚动 */
|
|
||||||
|
|
||||||
.itemWrapper {
|
|
||||||
flex: 1;
|
|
||||||
width: 0; /* 均分宽度,避免被 menuBody padding 裁切 */
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.itemWrapper {
|
.itemWrapper {
|
||||||
@ -653,7 +654,7 @@ export default {
|
|||||||
|
|
||||||
.custom-indicator {
|
.custom-indicator {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-top: 12rpx;
|
margin-top:-150rpx;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-bottom: 20rpx;
|
margin-bottom: 20rpx;
|
||||||
gap: 12rpx;
|
gap: 12rpx;
|
||||||
|
|||||||
235
src/pages/client/news/components/ListPageTemp.vue
Normal file
235
src/pages/client/news/components/ListPageTemp.vue
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
<template>
|
||||||
|
<scroll-view class="list-template-wrapper" :scroll-y="!disableScroll" :refresher-enabled="true"
|
||||||
|
:refresher-triggered="refreshTriggered" @refresherrefresh="onRefresh" @scrolltolower="onLoadMore">
|
||||||
|
<slot name="top" />
|
||||||
|
<view class="list-content" v-if="list.length > 0">
|
||||||
|
<view v-for="(item, index) in list" :key="item[idKey]" :class="{ left: index % 2 === 0 }"
|
||||||
|
class="flex-column-start news-item" @click="clickCell(item)">
|
||||||
|
<slot style="width: 100%" name="item" :data="{
|
||||||
|
...item,
|
||||||
|
...listExtraFields,
|
||||||
|
deleteSelect: !!item.deleteSelect,
|
||||||
|
}" />
|
||||||
|
</view>
|
||||||
|
<uni-load-more v-if="isLoading || (!isLoading && total && total === list.length)"
|
||||||
|
:status="isLoading ? 'loading' : 'nomore'"></uni-load-more>
|
||||||
|
</view>
|
||||||
|
<view v-else class="empty-container">
|
||||||
|
<image class="home-ration" mode="widthFix" :src="emptyImage || defaultEmptyImage" />
|
||||||
|
<text v-if="emptyText" class="empty-text">{{ emptyText }}</text>
|
||||||
|
</view>
|
||||||
|
<slot name="bottom" />
|
||||||
|
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "ListPageTemp",
|
||||||
|
props: {
|
||||||
|
getDataPromise: {
|
||||||
|
type: Function,
|
||||||
|
},
|
||||||
|
// 格式化请求结果的方法
|
||||||
|
resultFormatFunc: {
|
||||||
|
type: Function,
|
||||||
|
},
|
||||||
|
requestData: {
|
||||||
|
defult: () => { },
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
// 列表元素额外字段
|
||||||
|
listExtraFields: {
|
||||||
|
type: Object,
|
||||||
|
default: () => { },
|
||||||
|
},
|
||||||
|
reloadFlag: {
|
||||||
|
default: 0,
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
// 是否分页
|
||||||
|
isPagiantion: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
// 分页数量, 不分页时有效
|
||||||
|
pageSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 999,
|
||||||
|
},
|
||||||
|
defaultList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
disableScroll: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
idKey: {
|
||||||
|
type: String,
|
||||||
|
default: "id",
|
||||||
|
},
|
||||||
|
// 占位图片地址
|
||||||
|
emptyImage: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
// 空状态文本(传入时才显示)
|
||||||
|
emptyText: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isLoading: false,
|
||||||
|
total: 0,
|
||||||
|
list: [],
|
||||||
|
refreshTriggered: false,
|
||||||
|
p: 1,
|
||||||
|
num: 10,
|
||||||
|
defaultEmptyImage: 'https://activity.wagoo.live/empty.png', // 默认占位图片
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
reloadFlag: {
|
||||||
|
handler(value) {
|
||||||
|
if (value) {
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
requestData: {
|
||||||
|
handler(data) {
|
||||||
|
if (data) {
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
defaultList: {
|
||||||
|
handler(list) {
|
||||||
|
this.list = list;
|
||||||
|
this.$forceUpdate();
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onRefresh() {
|
||||||
|
if (this.refreshTriggered) return;
|
||||||
|
this.refreshTriggered = true;
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
// this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadMore() {
|
||||||
|
if (!this.isPagiantion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.isLoading && this.total > this.list.length) {
|
||||||
|
this.p++;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clickCell(item) {
|
||||||
|
this.$emit("clickCell", item);
|
||||||
|
},
|
||||||
|
getList() {
|
||||||
|
if (this.isLoading) return;
|
||||||
|
this.isLoading = true;
|
||||||
|
|
||||||
|
// TODO: 删除测试数据
|
||||||
|
if (!this.getDataPromise) {
|
||||||
|
this.list = [0, 1, 2, 3, 4, 5, 6].map((v, k) => {
|
||||||
|
return {
|
||||||
|
title: "消息名称0000sssss撒大苏打大苏打" + v,
|
||||||
|
id: k,
|
||||||
|
len: 10,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.total = 9;
|
||||||
|
this.isLoading = false;
|
||||||
|
this.refreshTriggered = false;
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = Object.assign(
|
||||||
|
{},
|
||||||
|
{ p: this.p, num: !this.isPagiantion ? this.pageSize : this.num },
|
||||||
|
this.requestData
|
||||||
|
);
|
||||||
|
this.getDataPromise(data)
|
||||||
|
.then((res) => {
|
||||||
|
const list =
|
||||||
|
this.p === 1
|
||||||
|
? res?.data || []
|
||||||
|
: [...this.list, ...(res?.data || [])];
|
||||||
|
this.list = this.resultFormatFunc
|
||||||
|
? this.resultFormatFunc(list)
|
||||||
|
: list;
|
||||||
|
this.total = res?.count || 0;
|
||||||
|
this.$emit("getList", this.list, res);
|
||||||
|
// console.log(this.list,'???')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.refreshTriggered = false;
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.list-template-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.list-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-item {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 400rpx auto 0;
|
||||||
|
padding: 0 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-ration {
|
||||||
|
width: 160px;
|
||||||
|
height: 174px;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -14,10 +14,12 @@
|
|||||||
getNoticeList
|
getNoticeList
|
||||||
} from "../../../api/notice";
|
} from "../../../api/notice";
|
||||||
import NewsItem from "./components/NewsItem.vue";
|
import NewsItem from "./components/NewsItem.vue";
|
||||||
|
import ListPageTemp from "./components/ListPageTemp.vue";
|
||||||
import { imgPrefix } from "@/utils/common";
|
import { imgPrefix } from "@/utils/common";
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
NewsItem,
|
NewsItem,
|
||||||
|
ListPageTemp
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TabsList from "@/components/TabsList.vue";
|
import TabsList from "@/components/TabsList.vue";
|
||||||
import ListPageTemp from "@/components/ListPageTemp.vue";
|
import ListPageTemp from "./components/ListPageTemp.vue";
|
||||||
import AfterSaleOrderItem from "./components/AfterSaleOrderItem.vue";
|
import AfterSaleOrderItem from "./components/AfterSaleOrderItem.vue";
|
||||||
|
|
||||||
import { getShopOrderList } from '@/api/shop'
|
import { getShopOrderList } from '@/api/shop'
|
||||||
|
|||||||
235
src/pages/client/order/components/ListPageTemp.vue
Normal file
235
src/pages/client/order/components/ListPageTemp.vue
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
<template>
|
||||||
|
<scroll-view class="list-template-wrapper" :scroll-y="!disableScroll" :refresher-enabled="true"
|
||||||
|
:refresher-triggered="refreshTriggered" @refresherrefresh="onRefresh" @scrolltolower="onLoadMore">
|
||||||
|
<slot name="top" />
|
||||||
|
<view class="list-content" v-if="list.length > 0">
|
||||||
|
<view v-for="(item, index) in list" :key="item[idKey]" :class="{ left: index % 2 === 0 }"
|
||||||
|
class="flex-column-start news-item" @click="clickCell(item)">
|
||||||
|
<slot style="width: 100%" name="item" :data="{
|
||||||
|
...item,
|
||||||
|
...listExtraFields,
|
||||||
|
deleteSelect: !!item.deleteSelect,
|
||||||
|
}" />
|
||||||
|
</view>
|
||||||
|
<uni-load-more v-if="isLoading || (!isLoading && total && total === list.length)"
|
||||||
|
:status="isLoading ? 'loading' : 'nomore'"></uni-load-more>
|
||||||
|
</view>
|
||||||
|
<view v-else class="empty-container">
|
||||||
|
<image class="home-ration" mode="widthFix" :src="emptyImage || defaultEmptyImage" />
|
||||||
|
<text v-if="emptyText" class="empty-text">{{ emptyText }}</text>
|
||||||
|
</view>
|
||||||
|
<slot name="bottom" />
|
||||||
|
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "ListPageTemp",
|
||||||
|
props: {
|
||||||
|
getDataPromise: {
|
||||||
|
type: Function,
|
||||||
|
},
|
||||||
|
// 格式化请求结果的方法
|
||||||
|
resultFormatFunc: {
|
||||||
|
type: Function,
|
||||||
|
},
|
||||||
|
requestData: {
|
||||||
|
defult: () => { },
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
// 列表元素额外字段
|
||||||
|
listExtraFields: {
|
||||||
|
type: Object,
|
||||||
|
default: () => { },
|
||||||
|
},
|
||||||
|
reloadFlag: {
|
||||||
|
default: 0,
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
// 是否分页
|
||||||
|
isPagiantion: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
// 分页数量, 不分页时有效
|
||||||
|
pageSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 999,
|
||||||
|
},
|
||||||
|
defaultList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
disableScroll: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
idKey: {
|
||||||
|
type: String,
|
||||||
|
default: "id",
|
||||||
|
},
|
||||||
|
// 占位图片地址
|
||||||
|
emptyImage: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
// 空状态文本(传入时才显示)
|
||||||
|
emptyText: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isLoading: false,
|
||||||
|
total: 0,
|
||||||
|
list: [],
|
||||||
|
refreshTriggered: false,
|
||||||
|
p: 1,
|
||||||
|
num: 10,
|
||||||
|
defaultEmptyImage: 'https://activity.wagoo.live/empty.png', // 默认占位图片
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
reloadFlag: {
|
||||||
|
handler(value) {
|
||||||
|
if (value) {
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
requestData: {
|
||||||
|
handler(data) {
|
||||||
|
if (data) {
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
defaultList: {
|
||||||
|
handler(list) {
|
||||||
|
this.list = list;
|
||||||
|
this.$forceUpdate();
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onRefresh() {
|
||||||
|
if (this.refreshTriggered) return;
|
||||||
|
this.refreshTriggered = true;
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
// this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadMore() {
|
||||||
|
if (!this.isPagiantion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.isLoading && this.total > this.list.length) {
|
||||||
|
this.p++;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clickCell(item) {
|
||||||
|
this.$emit("clickCell", item);
|
||||||
|
},
|
||||||
|
getList() {
|
||||||
|
if (this.isLoading) return;
|
||||||
|
this.isLoading = true;
|
||||||
|
|
||||||
|
// TODO: 删除测试数据
|
||||||
|
if (!this.getDataPromise) {
|
||||||
|
this.list = [0, 1, 2, 3, 4, 5, 6].map((v, k) => {
|
||||||
|
return {
|
||||||
|
title: "消息名称0000sssss撒大苏打大苏打" + v,
|
||||||
|
id: k,
|
||||||
|
len: 10,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.total = 9;
|
||||||
|
this.isLoading = false;
|
||||||
|
this.refreshTriggered = false;
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = Object.assign(
|
||||||
|
{},
|
||||||
|
{ p: this.p, num: !this.isPagiantion ? this.pageSize : this.num },
|
||||||
|
this.requestData
|
||||||
|
);
|
||||||
|
this.getDataPromise(data)
|
||||||
|
.then((res) => {
|
||||||
|
const list =
|
||||||
|
this.p === 1
|
||||||
|
? res?.data || []
|
||||||
|
: [...this.list, ...(res?.data || [])];
|
||||||
|
this.list = this.resultFormatFunc
|
||||||
|
? this.resultFormatFunc(list)
|
||||||
|
: list;
|
||||||
|
this.total = res?.count || 0;
|
||||||
|
this.$emit("getList", this.list, res);
|
||||||
|
// console.log(this.list,'???')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.refreshTriggered = false;
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.list-template-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.list-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-item {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 400rpx auto 0;
|
||||||
|
padding: 0 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-ration {
|
||||||
|
width: 160px;
|
||||||
|
height: 174px;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -93,8 +93,8 @@
|
|||||||
<view class="info-cell payment-method-cell">
|
<view class="info-cell payment-method-cell">
|
||||||
<view class="payment-item" @click.stop="selectOption1('1')">
|
<view class="payment-item" @click.stop="selectOption1('1')">
|
||||||
<view class="payment-left">
|
<view class="payment-left">
|
||||||
<image class="payment-icon" src="@/static/images/wx.png" mode="widthFix" />
|
<image class="payment-icon" src="@/static/images/douy.png" mode="widthFix" />
|
||||||
<text class="payment-text">微信支付</text>
|
<text class="payment-text">抖音支付</text>
|
||||||
</view>
|
</view>
|
||||||
<image class="payment-check"
|
<image class="payment-check"
|
||||||
:src="selected2 ? require('@/static/images/cart_checked.png') : require('@/static/images/unchecked.png')"
|
:src="selected2 ? require('@/static/images/cart_checked.png') : require('@/static/images/unchecked.png')"
|
||||||
@ -573,15 +573,12 @@
|
|||||||
order_no:orderId.order_no
|
order_no:orderId.order_no
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
const payData = res?.data || {};
|
tt.pay({
|
||||||
console.log(payData,'--=')
|
orderInfo: {
|
||||||
uni.requestPayment({
|
order_id:res.data.orderInfo.order_id,
|
||||||
provider: "wxpay",
|
order_token:res.data.orderInfo.order_token,
|
||||||
timeStamp: payData.timeStamp,
|
},
|
||||||
nonceStr: payData.nonceStr,
|
service:5,
|
||||||
package: payData.package,
|
|
||||||
signType: payData.signType,
|
|
||||||
paySign: payData.paySign,
|
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
console.log(res,'--==')
|
console.log(res,'--==')
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
|
|||||||
@ -227,8 +227,8 @@
|
|||||||
<view class="info-cell payment-method-cell" v-if="orderData.status === SHOP_ORDER_UNPAY">
|
<view class="info-cell payment-method-cell" v-if="orderData.status === SHOP_ORDER_UNPAY">
|
||||||
<view class="payment-item" @click.stop="selectOption1('1')">
|
<view class="payment-item" @click.stop="selectOption1('1')">
|
||||||
<view class="payment-left">
|
<view class="payment-left">
|
||||||
<image class="payment-icon" src="@/static/images/wx.png" mode="widthFix" />
|
<image class="payment-icon" src="@/static/images/douy.png" mode="widthFix" />
|
||||||
<text class="payment-text">微信支付</text>
|
<text class="payment-text">抖音支付</text>
|
||||||
</view>
|
</view>
|
||||||
<image class="payment-check"
|
<image class="payment-check"
|
||||||
:src="selected2 ? require('@/static/images/cart_checked.png') : require('@/static/images/unchecked.png')"
|
:src="selected2 ? require('@/static/images/cart_checked.png') : require('@/static/images/unchecked.png')"
|
||||||
@ -619,14 +619,12 @@
|
|||||||
order_id:this.orderData.order_id,
|
order_id:this.orderData.order_id,
|
||||||
order_no:this.orderData.order_no
|
order_no:this.orderData.order_no
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
const payData = res?.data || {};
|
tt.pay({
|
||||||
uni.requestPayment({
|
orderInfo: {
|
||||||
provider: "wxpay",
|
order_id:res.data.orderInfo.order_id,
|
||||||
timeStamp: payData.timeStamp,
|
order_token:res.data.orderInfo.order_token,
|
||||||
nonceStr: payData.nonceStr,
|
},
|
||||||
package: payData.package,
|
service:5,
|
||||||
signType: payData.signType,
|
|
||||||
paySign: payData.paySign,
|
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
|
|||||||
@ -61,8 +61,8 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="wechat" @click.stop="selectOption1('1')">
|
<view class="wechat" @click.stop="selectOption1('1')">
|
||||||
<view class="select">
|
<view class="select">
|
||||||
<image class="w" src="@/static/images/wx.png" mode="widthFix" />
|
<image class="w" src="@/static/images/douy.png" mode="widthFix" />
|
||||||
<text class="x">微信</text>
|
<text class="x">抖音</text>
|
||||||
</view>
|
</view>
|
||||||
<image v-if="selected1" class="not-selected" src="@/static/images/w.png" mode="widthFix" />
|
<image v-if="selected1" class="not-selected" src="@/static/images/w.png" mode="widthFix" />
|
||||||
<image v-if="selected2" class="not-selected" src="@/static/images/y.png" mode="widthFix" />
|
<image v-if="selected2" class="not-selected" src="@/static/images/y.png" mode="widthFix" />
|
||||||
@ -99,7 +99,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TabsList from "@/components/TabsList.vue";
|
import TabsList from "@/components/TabsList.vue";
|
||||||
import ListPageTemp from "@/components/ListPageTemp.vue";
|
import ListPageTemp from "./components/ListPageTemp.vue";
|
||||||
import OrderItem from "./components/OrderItem.vue";
|
import OrderItem from "./components/OrderItem.vue";
|
||||||
import PopUpModal from "@/components/PopUpModal.vue";
|
import PopUpModal from "@/components/PopUpModal.vue";
|
||||||
import SuccessModal from "@/components/SuccessModal.vue";
|
import SuccessModal from "@/components/SuccessModal.vue";
|
||||||
@ -340,14 +340,12 @@ export default {
|
|||||||
order_id: this.dataList.order_id,
|
order_id: this.dataList.order_id,
|
||||||
order_no: this.dataList.order_no
|
order_no: this.dataList.order_no
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
const payData = res?.data || {};
|
tt.pay({
|
||||||
uni.requestPayment({
|
orderInfo: {
|
||||||
provider: "wxpay",
|
order_id:res.data.orderInfo.order_id,
|
||||||
timeStamp: payData.timeStamp,
|
order_token:res.data.orderInfo.order_token,
|
||||||
nonceStr: payData.nonceStr,
|
},
|
||||||
package: payData.package,
|
service:5,
|
||||||
signType: payData.signType,
|
|
||||||
paySign: payData.paySign,
|
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
|
|||||||
@ -21,8 +21,8 @@
|
|||||||
<view class="recharge-method">
|
<view class="recharge-method">
|
||||||
<view class="wechat">
|
<view class="wechat">
|
||||||
<view class="select">
|
<view class="select">
|
||||||
<image class="w" src="@/static/images/wx.png" mode="widthFix" />
|
<image class="w" src="@/static/images/douy.png" mode="widthFix" />
|
||||||
<text class="x">微信</text>
|
<text class="x">抖音</text>
|
||||||
</view>
|
</view>
|
||||||
<image v-if="selected1" class="not-selected" @click.stop="selectOption1('1')" src="@/static/images/w.png"
|
<image v-if="selected1" class="not-selected" @click.stop="selectOption1('1')" src="@/static/images/w.png"
|
||||||
mode="widthFix" />
|
mode="widthFix" />
|
||||||
@ -212,14 +212,12 @@
|
|||||||
if (this.WeChat == 1) {
|
if (this.WeChat == 1) {
|
||||||
payOrder(this.dataList.order_id, PRICE_DIFF_TYPE_SERVICE)
|
payOrder(this.dataList.order_id, PRICE_DIFF_TYPE_SERVICE)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
const payData = res?.info?.pay_data || {};
|
tt.pay({
|
||||||
uni.requestPayment({
|
orderInfo: {
|
||||||
provider: "wxpay",
|
order_id:res.data.orderInfo.order_id,
|
||||||
timeStamp: payData.timeStamp,
|
order_token:res.data.orderInfo.order_token,
|
||||||
nonceStr: payData.nonceStr,
|
},
|
||||||
package: payData.package,
|
service:5,
|
||||||
signType: payData.signType,
|
|
||||||
paySign: payData.sign,
|
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
|
|||||||
235
src/pages/client/recharge/components/ListPageTemp.vue
Normal file
235
src/pages/client/recharge/components/ListPageTemp.vue
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
<template>
|
||||||
|
<scroll-view class="list-template-wrapper" :scroll-y="!disableScroll" :refresher-enabled="true"
|
||||||
|
:refresher-triggered="refreshTriggered" @refresherrefresh="onRefresh" @scrolltolower="onLoadMore">
|
||||||
|
<slot name="top" />
|
||||||
|
<view class="list-content" v-if="list.length > 0">
|
||||||
|
<view v-for="(item, index) in list" :key="item[idKey]" :class="{ left: index % 2 === 0 }"
|
||||||
|
class="flex-column-start news-item" @click="clickCell(item)">
|
||||||
|
<slot style="width: 100%" name="item" :data="{
|
||||||
|
...item,
|
||||||
|
...listExtraFields,
|
||||||
|
deleteSelect: !!item.deleteSelect,
|
||||||
|
}" />
|
||||||
|
</view>
|
||||||
|
<uni-load-more v-if="isLoading || (!isLoading && total && total === list.length)"
|
||||||
|
:status="isLoading ? 'loading' : 'nomore'"></uni-load-more>
|
||||||
|
</view>
|
||||||
|
<view v-else class="empty-container">
|
||||||
|
<image class="home-ration" mode="widthFix" :src="emptyImage || defaultEmptyImage" />
|
||||||
|
<text v-if="emptyText" class="empty-text">{{ emptyText }}</text>
|
||||||
|
</view>
|
||||||
|
<slot name="bottom" />
|
||||||
|
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "ListPageTemp",
|
||||||
|
props: {
|
||||||
|
getDataPromise: {
|
||||||
|
type: Function,
|
||||||
|
},
|
||||||
|
// 格式化请求结果的方法
|
||||||
|
resultFormatFunc: {
|
||||||
|
type: Function,
|
||||||
|
},
|
||||||
|
requestData: {
|
||||||
|
defult: () => { },
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
// 列表元素额外字段
|
||||||
|
listExtraFields: {
|
||||||
|
type: Object,
|
||||||
|
default: () => { },
|
||||||
|
},
|
||||||
|
reloadFlag: {
|
||||||
|
default: 0,
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
// 是否分页
|
||||||
|
isPagiantion: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
// 分页数量, 不分页时有效
|
||||||
|
pageSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 999,
|
||||||
|
},
|
||||||
|
defaultList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
disableScroll: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
idKey: {
|
||||||
|
type: String,
|
||||||
|
default: "id",
|
||||||
|
},
|
||||||
|
// 占位图片地址
|
||||||
|
emptyImage: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
// 空状态文本(传入时才显示)
|
||||||
|
emptyText: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isLoading: false,
|
||||||
|
total: 0,
|
||||||
|
list: [],
|
||||||
|
refreshTriggered: false,
|
||||||
|
p: 1,
|
||||||
|
num: 10,
|
||||||
|
defaultEmptyImage: 'https://activity.wagoo.live/empty.png', // 默认占位图片
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
reloadFlag: {
|
||||||
|
handler(value) {
|
||||||
|
if (value) {
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
requestData: {
|
||||||
|
handler(data) {
|
||||||
|
if (data) {
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
defaultList: {
|
||||||
|
handler(list) {
|
||||||
|
this.list = list;
|
||||||
|
this.$forceUpdate();
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onRefresh() {
|
||||||
|
if (this.refreshTriggered) return;
|
||||||
|
this.refreshTriggered = true;
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
// this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadMore() {
|
||||||
|
if (!this.isPagiantion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.isLoading && this.total > this.list.length) {
|
||||||
|
this.p++;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clickCell(item) {
|
||||||
|
this.$emit("clickCell", item);
|
||||||
|
},
|
||||||
|
getList() {
|
||||||
|
if (this.isLoading) return;
|
||||||
|
this.isLoading = true;
|
||||||
|
|
||||||
|
// TODO: 删除测试数据
|
||||||
|
if (!this.getDataPromise) {
|
||||||
|
this.list = [0, 1, 2, 3, 4, 5, 6].map((v, k) => {
|
||||||
|
return {
|
||||||
|
title: "消息名称0000sssss撒大苏打大苏打" + v,
|
||||||
|
id: k,
|
||||||
|
len: 10,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.total = 9;
|
||||||
|
this.isLoading = false;
|
||||||
|
this.refreshTriggered = false;
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = Object.assign(
|
||||||
|
{},
|
||||||
|
{ p: this.p, num: !this.isPagiantion ? this.pageSize : this.num },
|
||||||
|
this.requestData
|
||||||
|
);
|
||||||
|
this.getDataPromise(data)
|
||||||
|
.then((res) => {
|
||||||
|
const list =
|
||||||
|
this.p === 1
|
||||||
|
? res?.data || []
|
||||||
|
: [...this.list, ...(res?.data || [])];
|
||||||
|
this.list = this.resultFormatFunc
|
||||||
|
? this.resultFormatFunc(list)
|
||||||
|
: list;
|
||||||
|
this.total = res?.count || 0;
|
||||||
|
this.$emit("getList", this.list, res);
|
||||||
|
// console.log(this.list,'???')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.refreshTriggered = false;
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.list-template-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.list-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-item {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 400rpx auto 0;
|
||||||
|
padding: 0 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-ration {
|
||||||
|
width: 160px;
|
||||||
|
height: 174px;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -32,8 +32,8 @@
|
|||||||
<view class="recharge-method">
|
<view class="recharge-method">
|
||||||
<view class="wechat" @click.stop="selectOption1">
|
<view class="wechat" @click.stop="selectOption1">
|
||||||
<view class="select">
|
<view class="select">
|
||||||
<image class="w" src="@/static/images/wx.png" mode="widthFix" />
|
<image class="w" src="@/static/images/douy.png" mode="widthFix" />
|
||||||
<text class="x">微信</text>
|
<text class="x">抖音</text>
|
||||||
</view>
|
</view>
|
||||||
<image v-if="selected1" class="not-selected"
|
<image v-if="selected1" class="not-selected"
|
||||||
src="@/static/images/w.png" mode="widthFix" />
|
src="@/static/images/w.png" mode="widthFix" />
|
||||||
@ -141,13 +141,12 @@
|
|||||||
walletWxpay(data).then((res) => {
|
walletWxpay(data).then((res) => {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
// 使用获取的支付参数进行支付
|
// 使用获取的支付参数进行支付
|
||||||
uni.requestPayment({
|
tt.pay({
|
||||||
provider: 'wxpay',
|
orderInfo: {
|
||||||
timeStamp: res.data.timeStamp,
|
order_id:res.data.orderInfo.order_id,
|
||||||
nonceStr: res.data.nonceStr,
|
order_token:res.data.orderInfo.order_token,
|
||||||
package: res.data.package,
|
},
|
||||||
signType: res.data.signType,
|
service:5,
|
||||||
paySign: res.data.paySign,
|
|
||||||
success: (payRes) => {
|
success: (payRes) => {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '支付成功',
|
title: '支付成功',
|
||||||
|
|||||||
@ -63,8 +63,8 @@
|
|||||||
<view class="recharge-method">
|
<view class="recharge-method">
|
||||||
<view class="wechat" @click.stop="selectOption1">
|
<view class="wechat" @click.stop="selectOption1">
|
||||||
<view class="select">
|
<view class="select">
|
||||||
<image class="w" src="@/static/images/wx.png" mode="widthFix" />
|
<image class="w" src="@/static/images/douy.png" mode="widthFix" />
|
||||||
<text class="x">微信</text>
|
<text class="x">抖音</text>
|
||||||
</view>
|
</view>
|
||||||
<image v-if="selected1" class="not-selected"
|
<image v-if="selected1" class="not-selected"
|
||||||
src="@/static/images/w.png" mode="widthFix" />
|
src="@/static/images/w.png" mode="widthFix" />
|
||||||
@ -188,12 +188,12 @@
|
|||||||
};
|
};
|
||||||
walletWxpay(data).then((res) => {
|
walletWxpay(data).then((res) => {
|
||||||
// 使用获取的支付参数进行支付
|
// 使用获取的支付参数进行支付
|
||||||
uni.requestPayment({
|
tt.pay({
|
||||||
timeStamp: res.data.timeStamp, // 确保这些字段都正确
|
orderInfo: {
|
||||||
nonceStr: res.data.nonceStr,
|
order_id:res.data.orderInfo.order_id,
|
||||||
package: res.data.package,
|
order_token:res.data.orderInfo.order_token,
|
||||||
signType: res.data.signType,
|
},
|
||||||
paySign: res.data.paySign,
|
service:5,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
this.buyService(this.user_id);
|
this.buyService(this.user_id);
|
||||||
// console.log('支付成功:', res);
|
// console.log('支付成功:', res);
|
||||||
|
|||||||
@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TabsList from "@/components/TabsList.vue";
|
import TabsList from "@/components/TabsList.vue";
|
||||||
import ListPageTemp from "@/components/ListPageTemp.vue";
|
import ListPageTemp from "./components/ListPageTemp.vue";
|
||||||
import CouponItem from "@/components/coupon/CouponItem";
|
import CouponItem from "@/components/coupon/CouponItem";
|
||||||
import { getOwnCouponData } from "@/api/coupon";
|
import { getOwnCouponData } from "@/api/coupon";
|
||||||
|
|
||||||
|
|||||||
@ -34,7 +34,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view v-if="objNew.third_party_sn " class="rech-row">
|
<view v-if="objNew.third_party_sn " class="rech-row">
|
||||||
<text class="c">充值方式</text>
|
<text class="c">充值方式</text>
|
||||||
<text v-if="objNew.payment_method == 1" class="m">微信</text>
|
<text v-if="objNew.payment_method == 1" class="m">抖音</text>
|
||||||
<text v-else class="m">支付宝</text>
|
<text v-else class="m">支付宝</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="rech-row">
|
<view class="rech-row">
|
||||||
|
|||||||
235
src/pages/client/remark/ListPageTemp.vue
Normal file
235
src/pages/client/remark/ListPageTemp.vue
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
<template>
|
||||||
|
<scroll-view class="list-template-wrapper" :scroll-y="!disableScroll" :refresher-enabled="true"
|
||||||
|
:refresher-triggered="refreshTriggered" @refresherrefresh="onRefresh" @scrolltolower="onLoadMore">
|
||||||
|
<slot name="top" />
|
||||||
|
<view class="list-content" v-if="list.length > 0">
|
||||||
|
<view v-for="(item, index) in list" :key="item[idKey]" :class="{ left: index % 2 === 0 }"
|
||||||
|
class="flex-column-start news-item" @click="clickCell(item)">
|
||||||
|
<slot style="width: 100%" name="item" :data="{
|
||||||
|
...item,
|
||||||
|
...listExtraFields,
|
||||||
|
deleteSelect: !!item.deleteSelect,
|
||||||
|
}" />
|
||||||
|
</view>
|
||||||
|
<uni-load-more v-if="isLoading || (!isLoading && total && total === list.length)"
|
||||||
|
:status="isLoading ? 'loading' : 'nomore'"></uni-load-more>
|
||||||
|
</view>
|
||||||
|
<view v-else class="empty-container">
|
||||||
|
<image class="home-ration" mode="widthFix" :src="emptyImage || defaultEmptyImage" />
|
||||||
|
<text v-if="emptyText" class="empty-text">{{ emptyText }}</text>
|
||||||
|
</view>
|
||||||
|
<slot name="bottom" />
|
||||||
|
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "ListPageTemp",
|
||||||
|
props: {
|
||||||
|
getDataPromise: {
|
||||||
|
type: Function,
|
||||||
|
},
|
||||||
|
// 格式化请求结果的方法
|
||||||
|
resultFormatFunc: {
|
||||||
|
type: Function,
|
||||||
|
},
|
||||||
|
requestData: {
|
||||||
|
defult: () => { },
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
// 列表元素额外字段
|
||||||
|
listExtraFields: {
|
||||||
|
type: Object,
|
||||||
|
default: () => { },
|
||||||
|
},
|
||||||
|
reloadFlag: {
|
||||||
|
default: 0,
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
// 是否分页
|
||||||
|
isPagiantion: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
// 分页数量, 不分页时有效
|
||||||
|
pageSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 999,
|
||||||
|
},
|
||||||
|
defaultList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
disableScroll: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
idKey: {
|
||||||
|
type: String,
|
||||||
|
default: "id",
|
||||||
|
},
|
||||||
|
// 占位图片地址
|
||||||
|
emptyImage: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
// 空状态文本(传入时才显示)
|
||||||
|
emptyText: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isLoading: false,
|
||||||
|
total: 0,
|
||||||
|
list: [],
|
||||||
|
refreshTriggered: false,
|
||||||
|
p: 1,
|
||||||
|
num: 10,
|
||||||
|
defaultEmptyImage: 'https://activity.wagoo.live/empty.png', // 默认占位图片
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
reloadFlag: {
|
||||||
|
handler(value) {
|
||||||
|
if (value) {
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
requestData: {
|
||||||
|
handler(data) {
|
||||||
|
if (data) {
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
defaultList: {
|
||||||
|
handler(list) {
|
||||||
|
this.list = list;
|
||||||
|
this.$forceUpdate();
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onRefresh() {
|
||||||
|
if (this.refreshTriggered) return;
|
||||||
|
this.refreshTriggered = true;
|
||||||
|
this.p = 1;
|
||||||
|
this.num = 10;
|
||||||
|
// this.list = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadMore() {
|
||||||
|
if (!this.isPagiantion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.isLoading && this.total > this.list.length) {
|
||||||
|
this.p++;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clickCell(item) {
|
||||||
|
this.$emit("clickCell", item);
|
||||||
|
},
|
||||||
|
getList() {
|
||||||
|
if (this.isLoading) return;
|
||||||
|
this.isLoading = true;
|
||||||
|
|
||||||
|
// TODO: 删除测试数据
|
||||||
|
if (!this.getDataPromise) {
|
||||||
|
this.list = [0, 1, 2, 3, 4, 5, 6].map((v, k) => {
|
||||||
|
return {
|
||||||
|
title: "消息名称0000sssss撒大苏打大苏打" + v,
|
||||||
|
id: k,
|
||||||
|
len: 10,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.total = 9;
|
||||||
|
this.isLoading = false;
|
||||||
|
this.refreshTriggered = false;
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = Object.assign(
|
||||||
|
{},
|
||||||
|
{ p: this.p, num: !this.isPagiantion ? this.pageSize : this.num },
|
||||||
|
this.requestData
|
||||||
|
);
|
||||||
|
this.getDataPromise(data)
|
||||||
|
.then((res) => {
|
||||||
|
const list =
|
||||||
|
this.p === 1
|
||||||
|
? res?.data || []
|
||||||
|
: [...this.list, ...(res?.data || [])];
|
||||||
|
this.list = this.resultFormatFunc
|
||||||
|
? this.resultFormatFunc(list)
|
||||||
|
: list;
|
||||||
|
this.total = res?.count || 0;
|
||||||
|
this.$emit("getList", this.list, res);
|
||||||
|
// console.log(this.list,'???')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.refreshTriggered = false;
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.list-template-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.list-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-item {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 400rpx auto 0;
|
||||||
|
padding: 0 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-ration {
|
||||||
|
width: 160px;
|
||||||
|
height: 174px;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import RemarkItem from "@/components/goods/RemarkItem.vue";
|
import RemarkItem from "@/components/goods/RemarkItem.vue";
|
||||||
import ListPageTemp from "@/components/ListPageTemp.vue";
|
import ListPageTemp from "./ListPageTemp.vue";
|
||||||
import { getRemarkList } from "@/api/shop";
|
import { getRemarkList } from "@/api/shop";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@ -220,14 +220,13 @@ export default {
|
|||||||
// 支付
|
// 支付
|
||||||
pay(orderId) {
|
pay(orderId) {
|
||||||
serviceCouponOrderPay(orderId).then((res) => {
|
serviceCouponOrderPay(orderId).then((res) => {
|
||||||
const payData = res?.info?.pay_data || {};
|
|
||||||
uni.requestPayment({
|
tt.pay({
|
||||||
provider: "wxpay",
|
orderInfo: {
|
||||||
timeStamp: payData.timeStamp,
|
order_id:res.data.orderInfo.order_id,
|
||||||
nonceStr: payData.nonceStr,
|
order_token:res.data.orderInfo.order_token,
|
||||||
package: payData.package,
|
},
|
||||||
signType: payData.signType,
|
service:5,
|
||||||
paySign: payData.sign,
|
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
this.showSuccessModal = true;
|
this.showSuccessModal = true;
|
||||||
|
|||||||
@ -47,8 +47,8 @@
|
|||||||
<text class="zf"> 请选择支付方式 </text>
|
<text class="zf"> 请选择支付方式 </text>
|
||||||
<view class="wechat">
|
<view class="wechat">
|
||||||
<view class="select">
|
<view class="select">
|
||||||
<image class="w" src="@/static/images/wx.png" mode="widthFix" />
|
<image class="w" src="@/static/images/douy.png" mode="widthFix" />
|
||||||
<text class="x">微信</text>
|
<text class="x">抖音</text>
|
||||||
</view>
|
</view>
|
||||||
<image
|
<image
|
||||||
v-if="selected1"
|
v-if="selected1"
|
||||||
|
|||||||
@ -202,8 +202,8 @@
|
|||||||
<text class="zf"> 请选择支付方式 </text>
|
<text class="zf"> 请选择支付方式 </text>
|
||||||
<view class="wechat" @click.stop="selectOption1('1')">
|
<view class="wechat" @click.stop="selectOption1('1')">
|
||||||
<view class="select">
|
<view class="select">
|
||||||
<image class="w" src="@/static/images/wx.png" mode="widthFix" />
|
<image class="w" src="@/static/images/douy.png" mode="widthFix" />
|
||||||
<text class="x">微信</text>
|
<text class="x">抖音</text>
|
||||||
</view>
|
</view>
|
||||||
<image v-if="selected1" class="not-selected" src="@/static/images/w.png"
|
<image v-if="selected1" class="not-selected" src="@/static/images/w.png"
|
||||||
mode="widthFix" />
|
mode="widthFix" />
|
||||||
@ -471,12 +471,12 @@ export default {
|
|||||||
};
|
};
|
||||||
memberWXPAY(data).then((res) => {
|
memberWXPAY(data).then((res) => {
|
||||||
// 使用获取的支付参数进行支付
|
// 使用获取的支付参数进行支付
|
||||||
uni.requestPayment({
|
tt.pay({
|
||||||
timeStamp: res.data.timeStamp, // 确保这些字段都正确
|
orderInfo: {
|
||||||
nonceStr: res.data.nonceStr,
|
order_id:res.data.orderInfo.order_id,
|
||||||
package: res.data.package,
|
order_token:res.data.orderInfo.order_token,
|
||||||
signType: res.data.signType,
|
},
|
||||||
paySign: res.data.paySign,
|
service:5,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: "支付成功",
|
title: "支付成功",
|
||||||
|
|||||||
@ -275,14 +275,12 @@ export default {
|
|||||||
// 支付
|
// 支付
|
||||||
pay(orderId) {
|
pay(orderId) {
|
||||||
serviceCouponOrderPay(orderId).then((res) => {
|
serviceCouponOrderPay(orderId).then((res) => {
|
||||||
const payData = res?.info?.pay_data || {};
|
tt.pay({
|
||||||
uni.requestPayment({
|
orderInfo: {
|
||||||
provider: "wxpay",
|
order_id:res.data.orderInfo.order_id,
|
||||||
timeStamp: payData.timeStamp,
|
order_token:res.data.orderInfo.order_token,
|
||||||
nonceStr: payData.nonceStr,
|
},
|
||||||
package: payData.package,
|
service:5,
|
||||||
signType: payData.signType,
|
|
||||||
paySign: payData.sign,
|
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
this.showSuccessModal = true
|
this.showSuccessModal = true
|
||||||
|
|||||||
BIN
src/static/images/douy.png
Normal file
BIN
src/static/images/douy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
795
src/wxcomponents/wxml-to-canvas/index.vue
Normal file
795
src/wxcomponents/wxml-to-canvas/index.vue
Normal file
@ -0,0 +1,795 @@
|
|||||||
|
<template>
|
||||||
|
<uni-shadow-root class="wxml-to-canvas-index"><canvas v-if="use2dCanvas" id="weui-canvas" type="2d" :style="'width: '+(width)+'px; height: '+(height)+'px;'"></canvas>
|
||||||
|
<canvas v-else canvas-id="weui-canvas" :style="'width: '+(width)+'px; height: '+(height)+'px;'"></canvas></uni-shadow-root>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
global['__wxVueOptions'] = {components:{}}
|
||||||
|
|
||||||
|
global['__wxRoute'] = 'wxml-to-canvas/index'
|
||||||
|
(function webpackUniversalModuleDefinition(root, factory) {
|
||||||
|
if(typeof exports === 'object' && typeof module === 'object')
|
||||||
|
module.exports = factory();
|
||||||
|
else if(typeof define === 'function' && define.amd)
|
||||||
|
define([], factory);
|
||||||
|
else {
|
||||||
|
var a = factory();
|
||||||
|
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
|
||||||
|
}
|
||||||
|
})(window, function() {
|
||||||
|
return /******/ (function(modules) { // webpackBootstrap
|
||||||
|
/******/ // The module cache
|
||||||
|
/******/ var installedModules = {};
|
||||||
|
/******/
|
||||||
|
/******/ // The require function
|
||||||
|
/******/ function __webpack_require__(moduleId) {
|
||||||
|
/******/
|
||||||
|
/******/ // Check if module is in cache
|
||||||
|
/******/ if(installedModules[moduleId]) {
|
||||||
|
/******/ return installedModules[moduleId].exports;
|
||||||
|
/******/ }
|
||||||
|
/******/ // Create a new module (and put it into the cache)
|
||||||
|
/******/ var module = installedModules[moduleId] = {
|
||||||
|
/******/ i: moduleId,
|
||||||
|
/******/ l: false,
|
||||||
|
/******/ exports: {}
|
||||||
|
/******/ };
|
||||||
|
/******/
|
||||||
|
/******/ // Execute the module function
|
||||||
|
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||||
|
/******/
|
||||||
|
/******/ // Flag the module as loaded
|
||||||
|
/******/ module.l = true;
|
||||||
|
/******/
|
||||||
|
/******/ // Return the exports of the module
|
||||||
|
/******/ return module.exports;
|
||||||
|
/******/ }
|
||||||
|
/******/
|
||||||
|
/******/
|
||||||
|
/******/ // expose the modules object (__webpack_modules__)
|
||||||
|
/******/ __webpack_require__.m = modules;
|
||||||
|
/******/
|
||||||
|
/******/ // expose the module cache
|
||||||
|
/******/ __webpack_require__.c = installedModules;
|
||||||
|
/******/
|
||||||
|
/******/ // define getter function for harmony exports
|
||||||
|
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||||
|
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||||
|
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
|
||||||
|
/******/ }
|
||||||
|
/******/ };
|
||||||
|
/******/
|
||||||
|
/******/ // define __esModule on exports
|
||||||
|
/******/ __webpack_require__.r = function(exports) {
|
||||||
|
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
||||||
|
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
||||||
|
/******/ }
|
||||||
|
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
||||||
|
/******/ };
|
||||||
|
/******/
|
||||||
|
/******/ // create a fake namespace object
|
||||||
|
/******/ // mode & 1: value is a module id, require it
|
||||||
|
/******/ // mode & 2: merge all properties of value into the ns
|
||||||
|
/******/ // mode & 4: return value when already ns object
|
||||||
|
/******/ // mode & 8|1: behave like require
|
||||||
|
/******/ __webpack_require__.t = function(value, mode) {
|
||||||
|
/******/ if(mode & 1) value = __webpack_require__(value);
|
||||||
|
/******/ if(mode & 8) return value;
|
||||||
|
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
|
||||||
|
/******/ var ns = Object.create(null);
|
||||||
|
/******/ __webpack_require__.r(ns);
|
||||||
|
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
|
||||||
|
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
|
||||||
|
/******/ return ns;
|
||||||
|
/******/ };
|
||||||
|
/******/
|
||||||
|
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||||
|
/******/ __webpack_require__.n = function(module) {
|
||||||
|
/******/ var getter = module && module.__esModule ?
|
||||||
|
/******/ function getDefault() { return module['default']; } :
|
||||||
|
/******/ function getModuleExports() { return module; };
|
||||||
|
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||||
|
/******/ return getter;
|
||||||
|
/******/ };
|
||||||
|
/******/
|
||||||
|
/******/ // Object.prototype.hasOwnProperty.call
|
||||||
|
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||||
|
/******/
|
||||||
|
/******/ // __webpack_public_path__
|
||||||
|
/******/ __webpack_require__.p = "";
|
||||||
|
/******/
|
||||||
|
/******/
|
||||||
|
/******/ // Load entry module and return exports
|
||||||
|
/******/ return __webpack_require__(__webpack_require__.s = 1);
|
||||||
|
/******/ })
|
||||||
|
/************************************************************************/
|
||||||
|
/******/ ([
|
||||||
|
/* 0 */
|
||||||
|
/***/ (function(module, exports) {
|
||||||
|
|
||||||
|
const hex = (color) => {
|
||||||
|
let result = null
|
||||||
|
|
||||||
|
if (/^#/.test(color) && (color.length === 7 || color.length === 9)) {
|
||||||
|
return color
|
||||||
|
// eslint-disable-next-line no-cond-assign
|
||||||
|
} else if ((result = /^(rgb|rgba)\((.+)\)/.exec(color)) !== null) {
|
||||||
|
return '#' + result[2].split(',').map((part, index) => {
|
||||||
|
part = part.trim()
|
||||||
|
part = index === 3 ? Math.floor(parseFloat(part) * 255) : parseInt(part, 10)
|
||||||
|
part = part.toString(16)
|
||||||
|
if (part.length === 1) {
|
||||||
|
part = '0' + part
|
||||||
|
}
|
||||||
|
return part
|
||||||
|
}).join('')
|
||||||
|
} else {
|
||||||
|
return '#00000000'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const splitLineToCamelCase = (str) => str.split('-').map((part, index) => {
|
||||||
|
if (index === 0) {
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
return part[0].toUpperCase() + part.slice(1)
|
||||||
|
}).join('')
|
||||||
|
|
||||||
|
const compareVersion = (v1, v2) => {
|
||||||
|
v1 = v1.split('.')
|
||||||
|
v2 = v2.split('.')
|
||||||
|
const len = Math.max(v1.length, v2.length)
|
||||||
|
while (v1.length < len) {
|
||||||
|
v1.push('0')
|
||||||
|
}
|
||||||
|
while (v2.length < len) {
|
||||||
|
v2.push('0')
|
||||||
|
}
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
const num1 = parseInt(v1[i], 10)
|
||||||
|
const num2 = parseInt(v2[i], 10)
|
||||||
|
|
||||||
|
if (num1 > num2) {
|
||||||
|
return 1
|
||||||
|
} else if (num1 < num2) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
hex,
|
||||||
|
splitLineToCamelCase,
|
||||||
|
compareVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
/* 1 */
|
||||||
|
/***/ (function(module, exports, __webpack_require__) {
|
||||||
|
|
||||||
|
|
||||||
|
const xmlParse = __webpack_require__(2)
|
||||||
|
const {Widget} = __webpack_require__(3)
|
||||||
|
const {Draw} = __webpack_require__(5)
|
||||||
|
const {compareVersion} = __webpack_require__(0)
|
||||||
|
|
||||||
|
const canvasId = 'weui-canvas'
|
||||||
|
|
||||||
|
Component({
|
||||||
|
properties: {
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
value: 400
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
value: 300
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
use2dCanvas: false, // 2.9.2 后可用canvas 2d 接口
|
||||||
|
},
|
||||||
|
lifetimes: {
|
||||||
|
attached() {
|
||||||
|
const {SDKVersion, pixelRatio: dpr} = wx.getSystemInfoSync()
|
||||||
|
const use2dCanvas = compareVersion(SDKVersion, '2.9.2') >= 0
|
||||||
|
this.dpr = dpr
|
||||||
|
this.setData({use2dCanvas}, () => {
|
||||||
|
if (use2dCanvas) {
|
||||||
|
const query = this.createSelectorQuery()
|
||||||
|
query.select(`#${canvasId}`)
|
||||||
|
.fields({node: true, size: true})
|
||||||
|
.exec(res => {
|
||||||
|
const canvas = res[0].node
|
||||||
|
const ctx = canvas.getContext('2d')
|
||||||
|
canvas.width = res[0].width * dpr
|
||||||
|
canvas.height = res[0].height * dpr
|
||||||
|
ctx.scale(dpr, dpr)
|
||||||
|
this.ctx = ctx
|
||||||
|
this.canvas = canvas
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.ctx = wx.createCanvasContext(canvasId, this)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async renderToCanvas(args) {
|
||||||
|
const {wxml, style} = args
|
||||||
|
const ctx = this.ctx
|
||||||
|
const canvas = this.canvas
|
||||||
|
const use2dCanvas = this.data.use2dCanvas
|
||||||
|
|
||||||
|
if (use2dCanvas && !canvas) {
|
||||||
|
return Promise.reject(new Error('renderToCanvas: fail canvas has not been created'))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, this.data.width, this.data.height)
|
||||||
|
const {root: xom} = xmlParse(wxml)
|
||||||
|
|
||||||
|
const widget = new Widget(xom, style)
|
||||||
|
const container = widget.init()
|
||||||
|
this.boundary = {
|
||||||
|
top: container.layoutBox.top,
|
||||||
|
left: container.layoutBox.left,
|
||||||
|
width: container.computedStyle.width,
|
||||||
|
height: container.computedStyle.height,
|
||||||
|
}
|
||||||
|
const draw = new Draw(ctx, canvas, use2dCanvas)
|
||||||
|
await draw.drawNode(container)
|
||||||
|
|
||||||
|
if (!use2dCanvas) {
|
||||||
|
await this.canvasDraw(ctx)
|
||||||
|
}
|
||||||
|
return Promise.resolve(container)
|
||||||
|
},
|
||||||
|
|
||||||
|
canvasDraw(ctx, reserve) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
ctx.draw(reserve, () => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
canvasToTempFilePath(args = {}) {
|
||||||
|
const use2dCanvas = this.data.use2dCanvas
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const {
|
||||||
|
top, left, width, height
|
||||||
|
} = this.boundary
|
||||||
|
|
||||||
|
const copyArgs = {
|
||||||
|
x: left,
|
||||||
|
y: top,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
destWidth: width * this.dpr,
|
||||||
|
destHeight: height * this.dpr,
|
||||||
|
canvasId,
|
||||||
|
fileType: args.fileType || 'png',
|
||||||
|
quality: args.quality || 1,
|
||||||
|
success: resolve,
|
||||||
|
fail: reject
|
||||||
|
}
|
||||||
|
|
||||||
|
if (use2dCanvas) {
|
||||||
|
delete copyArgs.canvasId
|
||||||
|
copyArgs.canvas = this.canvas
|
||||||
|
}
|
||||||
|
wx.canvasToTempFilePath(copyArgs, this)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
/* 2 */
|
||||||
|
/***/ (function(module, exports) {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose `parse`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the given string of `xml`.
|
||||||
|
*
|
||||||
|
* @param {String} xml
|
||||||
|
* @return {Object}
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
function parse(xml) {
|
||||||
|
xml = xml.trim()
|
||||||
|
|
||||||
|
// strip comments
|
||||||
|
xml = xml.replace(/<!--[\s\S]*?-->/g, '')
|
||||||
|
|
||||||
|
return document()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XML document.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function document() {
|
||||||
|
return {
|
||||||
|
declaration: declaration(),
|
||||||
|
root: tag()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declaration.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function declaration() {
|
||||||
|
const m = match(/^<\?xml\s*/)
|
||||||
|
if (!m) return
|
||||||
|
|
||||||
|
// tag
|
||||||
|
const node = {
|
||||||
|
attributes: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// attributes
|
||||||
|
while (!(eos() || is('?>'))) {
|
||||||
|
const attr = attribute()
|
||||||
|
if (!attr) return node
|
||||||
|
node.attributes[attr.name] = attr.value
|
||||||
|
}
|
||||||
|
|
||||||
|
match(/\?>\s*/)
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tag.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function tag() {
|
||||||
|
const m = match(/^<([\w-:.]+)\s*/)
|
||||||
|
if (!m) return
|
||||||
|
|
||||||
|
// name
|
||||||
|
const node = {
|
||||||
|
name: m[1],
|
||||||
|
attributes: {},
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
|
||||||
|
// attributes
|
||||||
|
while (!(eos() || is('>') || is('?>') || is('/>'))) {
|
||||||
|
const attr = attribute()
|
||||||
|
if (!attr) return node
|
||||||
|
node.attributes[attr.name] = attr.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// self closing tag
|
||||||
|
if (match(/^\s*\/>\s*/)) {
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
match(/\??>\s*/)
|
||||||
|
|
||||||
|
// content
|
||||||
|
node.content = content()
|
||||||
|
|
||||||
|
// children
|
||||||
|
let child
|
||||||
|
while (child = tag()) {
|
||||||
|
node.children.push(child)
|
||||||
|
}
|
||||||
|
|
||||||
|
// closing
|
||||||
|
match(/^<\/[\w-:.]+>\s*/)
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text content.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function content() {
|
||||||
|
const m = match(/^([^<]*)/)
|
||||||
|
if (m) return m[1]
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attribute.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function attribute() {
|
||||||
|
const m = match(/([\w:-]+)\s*=\s*("[^"]*"|'[^']*'|\w+)\s*/)
|
||||||
|
if (!m) return
|
||||||
|
return {name: m[1], value: strip(m[2])}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strip quotes from `val`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function strip(val) {
|
||||||
|
return val.replace(/^['"]|['"]$/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Match `re` and advance the string.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function match(re) {
|
||||||
|
const m = xml.match(re)
|
||||||
|
if (!m) return
|
||||||
|
xml = xml.slice(m[0].length)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End-of-source.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function eos() {
|
||||||
|
return xml.length == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for `prefix`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function is(prefix) {
|
||||||
|
return xml.indexOf(prefix) == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = parse
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
/* 3 */
|
||||||
|
/***/ (function(module, exports, __webpack_require__) {
|
||||||
|
|
||||||
|
const Block = __webpack_require__(4)
|
||||||
|
const {splitLineToCamelCase} = __webpack_require__(0)
|
||||||
|
|
||||||
|
class Element extends Block {
|
||||||
|
constructor(prop) {
|
||||||
|
super(prop.style)
|
||||||
|
this.name = prop.name
|
||||||
|
this.attributes = prop.attributes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Widget {
|
||||||
|
constructor(xom, style) {
|
||||||
|
this.xom = xom
|
||||||
|
this.style = style
|
||||||
|
|
||||||
|
this.inheritProps = ['fontSize', 'lineHeight', 'textAlign', 'verticalAlign', 'color']
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.container = this.create(this.xom)
|
||||||
|
this.container.layout()
|
||||||
|
|
||||||
|
this.inheritStyle(this.container)
|
||||||
|
return this.container
|
||||||
|
}
|
||||||
|
|
||||||
|
// 继承父节点的样式
|
||||||
|
inheritStyle(node) {
|
||||||
|
const parent = node.parent || null
|
||||||
|
const children = node.children || {}
|
||||||
|
const computedStyle = node.computedStyle
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
this.inheritProps.forEach(prop => {
|
||||||
|
computedStyle[prop] = computedStyle[prop] || parent.computedStyle[prop]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.values(children).forEach(child => {
|
||||||
|
this.inheritStyle(child)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
create(node) {
|
||||||
|
let classNames = (node.attributes.class || '').split(' ')
|
||||||
|
classNames = classNames.map(item => splitLineToCamelCase(item.trim()))
|
||||||
|
const style = {}
|
||||||
|
classNames.forEach(item => {
|
||||||
|
Object.assign(style, this.style[item] || {})
|
||||||
|
})
|
||||||
|
|
||||||
|
const args = {name: node.name, style}
|
||||||
|
|
||||||
|
const attrs = Object.keys(node.attributes)
|
||||||
|
const attributes = {}
|
||||||
|
for (const attr of attrs) {
|
||||||
|
const value = node.attributes[attr]
|
||||||
|
const CamelAttr = splitLineToCamelCase(attr)
|
||||||
|
|
||||||
|
if (value === '' || value === 'true') {
|
||||||
|
attributes[CamelAttr] = true
|
||||||
|
} else if (value === 'false') {
|
||||||
|
attributes[CamelAttr] = false
|
||||||
|
} else {
|
||||||
|
attributes[CamelAttr] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attributes.text = node.content
|
||||||
|
args.attributes = attributes
|
||||||
|
const element = new Element(args)
|
||||||
|
node.children.forEach(childNode => {
|
||||||
|
const childElement = this.create(childNode)
|
||||||
|
element.add(childElement)
|
||||||
|
})
|
||||||
|
return element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {Widget}
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
/* 4 */
|
||||||
|
/***/ (function(module, exports) {
|
||||||
|
|
||||||
|
module.exports = require("../widget-ui/index.js")
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
/* 5 */
|
||||||
|
/***/ (function(module, exports) {
|
||||||
|
|
||||||
|
class Draw {
|
||||||
|
constructor(context, canvas, use2dCanvas = false) {
|
||||||
|
this.ctx = context
|
||||||
|
this.canvas = canvas || null
|
||||||
|
this.use2dCanvas = use2dCanvas
|
||||||
|
}
|
||||||
|
|
||||||
|
roundRect(x, y, w, h, r, fill = true, stroke = false) {
|
||||||
|
if (r < 0) return
|
||||||
|
const ctx = this.ctx
|
||||||
|
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 3 / 2)
|
||||||
|
ctx.arc(x + w - r, y + r, r, Math.PI * 3 / 2, 0)
|
||||||
|
ctx.arc(x + w - r, y + h - r, r, 0, Math.PI / 2)
|
||||||
|
ctx.arc(x + r, y + h - r, r, Math.PI / 2, Math.PI)
|
||||||
|
ctx.lineTo(x, y + r)
|
||||||
|
if (stroke) ctx.stroke()
|
||||||
|
if (fill) ctx.fill()
|
||||||
|
}
|
||||||
|
|
||||||
|
drawView(box, style) {
|
||||||
|
const ctx = this.ctx
|
||||||
|
const {
|
||||||
|
left: x, top: y, width: w, height: h
|
||||||
|
} = box
|
||||||
|
const {
|
||||||
|
borderRadius = 0,
|
||||||
|
borderWidth = 0,
|
||||||
|
borderColor,
|
||||||
|
color = '#000',
|
||||||
|
backgroundColor = 'transparent',
|
||||||
|
} = style
|
||||||
|
ctx.save()
|
||||||
|
// 外环
|
||||||
|
if (borderWidth > 0) {
|
||||||
|
ctx.fillStyle = borderColor || color
|
||||||
|
this.roundRect(x, y, w, h, borderRadius)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 内环
|
||||||
|
ctx.fillStyle = backgroundColor
|
||||||
|
const innerWidth = w - 2 * borderWidth
|
||||||
|
const innerHeight = h - 2 * borderWidth
|
||||||
|
const innerRadius = borderRadius - borderWidth >= 0 ? borderRadius - borderWidth : 0
|
||||||
|
this.roundRect(x + borderWidth, y + borderWidth, innerWidth, innerHeight, innerRadius)
|
||||||
|
ctx.restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
async drawImage(img, box, style) {
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
const ctx = this.ctx
|
||||||
|
const canvas = this.canvas
|
||||||
|
|
||||||
|
const {
|
||||||
|
borderRadius = 0
|
||||||
|
} = style
|
||||||
|
const {
|
||||||
|
left: x, top: y, width: w, height: h
|
||||||
|
} = box
|
||||||
|
ctx.save()
|
||||||
|
this.roundRect(x, y, w, h, borderRadius, false, false)
|
||||||
|
ctx.clip()
|
||||||
|
|
||||||
|
const _drawImage = (img) => {
|
||||||
|
if (this.use2dCanvas) {
|
||||||
|
const Image = canvas.createImage()
|
||||||
|
Image.onload = () => {
|
||||||
|
ctx.drawImage(Image, x, y, w, h)
|
||||||
|
ctx.restore()
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
Image.onerror = () => { reject(new Error(`createImage fail: ${img}`)) }
|
||||||
|
Image.src = img
|
||||||
|
} else {
|
||||||
|
ctx.drawImage(img, x, y, w, h)
|
||||||
|
ctx.restore()
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isTempFile = /^wxfile:\/\//.test(img)
|
||||||
|
const isNetworkFile = /^https?:\/\//.test(img)
|
||||||
|
|
||||||
|
if (isTempFile) {
|
||||||
|
_drawImage(img)
|
||||||
|
} else if (isNetworkFile) {
|
||||||
|
wx.downloadFile({
|
||||||
|
url: img,
|
||||||
|
success(res) {
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
_drawImage(res.tempFilePath)
|
||||||
|
} else {
|
||||||
|
reject(new Error(`downloadFile:fail ${img}`))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail() {
|
||||||
|
reject(new Error(`downloadFile:fail ${img}`))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
reject(new Error(`image format error: ${img}`))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line complexity
|
||||||
|
drawText(text, box, style) {
|
||||||
|
const ctx = this.ctx
|
||||||
|
let {
|
||||||
|
left: x, top: y, width: w, height: h
|
||||||
|
} = box
|
||||||
|
let {
|
||||||
|
color = '#000',
|
||||||
|
lineHeight = '1.4em',
|
||||||
|
fontSize = 14,
|
||||||
|
textAlign = 'left',
|
||||||
|
verticalAlign = 'top',
|
||||||
|
backgroundColor = 'transparent'
|
||||||
|
} = style
|
||||||
|
|
||||||
|
if (typeof lineHeight === 'string') { // 2em
|
||||||
|
lineHeight = Math.ceil(parseFloat(lineHeight.replace('em')) * fontSize)
|
||||||
|
}
|
||||||
|
if (!text || (lineHeight > h)) return
|
||||||
|
|
||||||
|
ctx.save()
|
||||||
|
ctx.textBaseline = 'top'
|
||||||
|
ctx.font = `${fontSize}px sans-serif`
|
||||||
|
ctx.textAlign = textAlign
|
||||||
|
|
||||||
|
// 背景色
|
||||||
|
ctx.fillStyle = backgroundColor
|
||||||
|
this.roundRect(x, y, w, h, 0)
|
||||||
|
|
||||||
|
// 文字颜色
|
||||||
|
ctx.fillStyle = color
|
||||||
|
|
||||||
|
// 水平布局
|
||||||
|
switch (textAlign) {
|
||||||
|
case 'left':
|
||||||
|
break
|
||||||
|
case 'center':
|
||||||
|
x += 0.5 * w
|
||||||
|
break
|
||||||
|
case 'right':
|
||||||
|
x += w
|
||||||
|
break
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
|
||||||
|
const textWidth = ctx.measureText(text).width
|
||||||
|
const actualHeight = Math.ceil(textWidth / w) * lineHeight
|
||||||
|
let paddingTop = Math.ceil((h - actualHeight) / 2)
|
||||||
|
if (paddingTop < 0) paddingTop = 0
|
||||||
|
|
||||||
|
// 垂直布局
|
||||||
|
switch (verticalAlign) {
|
||||||
|
case 'top':
|
||||||
|
break
|
||||||
|
case 'middle':
|
||||||
|
y += paddingTop
|
||||||
|
break
|
||||||
|
case 'bottom':
|
||||||
|
y += 2 * paddingTop
|
||||||
|
break
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
|
||||||
|
const inlinePaddingTop = Math.ceil((lineHeight - fontSize) / 2)
|
||||||
|
|
||||||
|
// 不超过一行
|
||||||
|
if (textWidth <= w) {
|
||||||
|
ctx.fillText(text, x, y + inlinePaddingTop)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多行文本
|
||||||
|
const chars = text.split('')
|
||||||
|
const _y = y
|
||||||
|
|
||||||
|
// 逐行绘制
|
||||||
|
let line = ''
|
||||||
|
for (const ch of chars) {
|
||||||
|
const testLine = line + ch
|
||||||
|
const testWidth = ctx.measureText(testLine).width
|
||||||
|
|
||||||
|
if (testWidth > w) {
|
||||||
|
ctx.fillText(line, x, y + inlinePaddingTop)
|
||||||
|
y += lineHeight
|
||||||
|
line = ch
|
||||||
|
if ((y + lineHeight) > (_y + h)) break
|
||||||
|
} else {
|
||||||
|
line = testLine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 避免溢出
|
||||||
|
if ((y + lineHeight) <= (_y + h)) {
|
||||||
|
ctx.fillText(line, x, y + inlinePaddingTop)
|
||||||
|
}
|
||||||
|
ctx.restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
async drawNode(element) {
|
||||||
|
const {layoutBox, computedStyle, name} = element
|
||||||
|
const {src, text} = element.attributes
|
||||||
|
if (name === 'view') {
|
||||||
|
this.drawView(layoutBox, computedStyle)
|
||||||
|
} else if (name === 'image') {
|
||||||
|
await this.drawImage(src, layoutBox, computedStyle)
|
||||||
|
} else if (name === 'text') {
|
||||||
|
this.drawText(text, layoutBox, computedStyle)
|
||||||
|
}
|
||||||
|
const childs = Object.values(element.children)
|
||||||
|
for (const child of childs) {
|
||||||
|
await this.drawNode(child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Draw
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***/ })
|
||||||
|
/******/ ]);
|
||||||
|
});
|
||||||
|
export default global['__wxComponents']['wxml-to-canvas/index']
|
||||||
|
</script>
|
||||||
|
<style platform="mp-weixin">
|
||||||
|
|
||||||
|
</style>
|
||||||
794
src/wxcomponents/wxml-to-canvas/miniprogram_dist/index.vue
Normal file
794
src/wxcomponents/wxml-to-canvas/miniprogram_dist/index.vue
Normal file
@ -0,0 +1,794 @@
|
|||||||
|
<template>
|
||||||
|
<uni-shadow-root class="wxml-to-canvas-miniprogram_dist-index"><canvas v-if="use2dCanvas" id="weui-canvas" type="2d" :style="'width: '+(width)+'px; height: '+(height)+'px;'"></canvas>
|
||||||
|
<canvas v-else canvas-id="weui-canvas" :style="'width: '+(width)+'px; height: '+(height)+'px;'"></canvas></uni-shadow-root>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
global['__wxVueOptions'] = {components:{}}
|
||||||
|
|
||||||
|
global['__wxRoute'] = 'wxml-to-canvas/miniprogram_dist/index'
|
||||||
|
(function webpackUniversalModuleDefinition(root, factory) {
|
||||||
|
if(typeof exports === 'object' && typeof module === 'object')
|
||||||
|
module.exports = factory();
|
||||||
|
else if(typeof define === 'function' && define.amd)
|
||||||
|
define([], factory);
|
||||||
|
else {
|
||||||
|
var a = factory();
|
||||||
|
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
|
||||||
|
}
|
||||||
|
})(window, function() {
|
||||||
|
return /******/ (function(modules) { // webpackBootstrap
|
||||||
|
/******/ // The module cache
|
||||||
|
/******/ var installedModules = {};
|
||||||
|
/******/
|
||||||
|
/******/ // The require function
|
||||||
|
/******/ function __webpack_require__(moduleId) {
|
||||||
|
/******/
|
||||||
|
/******/ // Check if module is in cache
|
||||||
|
/******/ if(installedModules[moduleId]) {
|
||||||
|
/******/ return installedModules[moduleId].exports;
|
||||||
|
/******/ }
|
||||||
|
/******/ // Create a new module (and put it into the cache)
|
||||||
|
/******/ var module = installedModules[moduleId] = {
|
||||||
|
/******/ i: moduleId,
|
||||||
|
/******/ l: false,
|
||||||
|
/******/ exports: {}
|
||||||
|
/******/ };
|
||||||
|
/******/
|
||||||
|
/******/ // Execute the module function
|
||||||
|
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||||
|
/******/
|
||||||
|
/******/ // Flag the module as loaded
|
||||||
|
/******/ module.l = true;
|
||||||
|
/******/
|
||||||
|
/******/ // Return the exports of the module
|
||||||
|
/******/ return module.exports;
|
||||||
|
/******/ }
|
||||||
|
/******/
|
||||||
|
/******/
|
||||||
|
/******/ // expose the modules object (__webpack_modules__)
|
||||||
|
/******/ __webpack_require__.m = modules;
|
||||||
|
/******/
|
||||||
|
/******/ // expose the module cache
|
||||||
|
/******/ __webpack_require__.c = installedModules;
|
||||||
|
/******/
|
||||||
|
/******/ // define getter function for harmony exports
|
||||||
|
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||||
|
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||||
|
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
|
||||||
|
/******/ }
|
||||||
|
/******/ };
|
||||||
|
/******/
|
||||||
|
/******/ // define __esModule on exports
|
||||||
|
/******/ __webpack_require__.r = function(exports) {
|
||||||
|
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
||||||
|
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
||||||
|
/******/ }
|
||||||
|
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
||||||
|
/******/ };
|
||||||
|
/******/
|
||||||
|
/******/ // create a fake namespace object
|
||||||
|
/******/ // mode & 1: value is a module id, require it
|
||||||
|
/******/ // mode & 2: merge all properties of value into the ns
|
||||||
|
/******/ // mode & 4: return value when already ns object
|
||||||
|
/******/ // mode & 8|1: behave like require
|
||||||
|
/******/ __webpack_require__.t = function(value, mode) {
|
||||||
|
/******/ if(mode & 1) value = __webpack_require__(value);
|
||||||
|
/******/ if(mode & 8) return value;
|
||||||
|
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
|
||||||
|
/******/ var ns = Object.create(null);
|
||||||
|
/******/ __webpack_require__.r(ns);
|
||||||
|
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
|
||||||
|
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
|
||||||
|
/******/ return ns;
|
||||||
|
/******/ };
|
||||||
|
/******/
|
||||||
|
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||||
|
/******/ __webpack_require__.n = function(module) {
|
||||||
|
/******/ var getter = module && module.__esModule ?
|
||||||
|
/******/ function getDefault() { return module['default']; } :
|
||||||
|
/******/ function getModuleExports() { return module; };
|
||||||
|
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||||
|
/******/ return getter;
|
||||||
|
/******/ };
|
||||||
|
/******/
|
||||||
|
/******/ // Object.prototype.hasOwnProperty.call
|
||||||
|
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||||
|
/******/
|
||||||
|
/******/ // __webpack_public_path__
|
||||||
|
/******/ __webpack_require__.p = "";
|
||||||
|
/******/
|
||||||
|
/******/
|
||||||
|
/******/ // Load entry module and return exports
|
||||||
|
/******/ return __webpack_require__(__webpack_require__.s = 1);
|
||||||
|
/******/ })
|
||||||
|
/************************************************************************/
|
||||||
|
/******/ ([
|
||||||
|
/* 0 */
|
||||||
|
/***/ (function(module, exports) {
|
||||||
|
|
||||||
|
const hex = (color) => {
|
||||||
|
let result = null
|
||||||
|
|
||||||
|
if (/^#/.test(color) && (color.length === 7 || color.length === 9)) {
|
||||||
|
return color
|
||||||
|
// eslint-disable-next-line no-cond-assign
|
||||||
|
} else if ((result = /^(rgb|rgba)\((.+)\)/.exec(color)) !== null) {
|
||||||
|
return '#' + result[2].split(',').map((part, index) => {
|
||||||
|
part = part.trim()
|
||||||
|
part = index === 3 ? Math.floor(parseFloat(part) * 255) : parseInt(part, 10)
|
||||||
|
part = part.toString(16)
|
||||||
|
if (part.length === 1) {
|
||||||
|
part = '0' + part
|
||||||
|
}
|
||||||
|
return part
|
||||||
|
}).join('')
|
||||||
|
} else {
|
||||||
|
return '#00000000'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const splitLineToCamelCase = (str) => str.split('-').map((part, index) => {
|
||||||
|
if (index === 0) {
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
return part[0].toUpperCase() + part.slice(1)
|
||||||
|
}).join('')
|
||||||
|
|
||||||
|
const compareVersion = (v1, v2) => {
|
||||||
|
v1 = v1.split('.')
|
||||||
|
v2 = v2.split('.')
|
||||||
|
const len = Math.max(v1.length, v2.length)
|
||||||
|
while (v1.length < len) {
|
||||||
|
v1.push('0')
|
||||||
|
}
|
||||||
|
while (v2.length < len) {
|
||||||
|
v2.push('0')
|
||||||
|
}
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
const num1 = parseInt(v1[i], 10)
|
||||||
|
const num2 = parseInt(v2[i], 10)
|
||||||
|
|
||||||
|
if (num1 > num2) {
|
||||||
|
return 1
|
||||||
|
} else if (num1 < num2) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
hex,
|
||||||
|
splitLineToCamelCase,
|
||||||
|
compareVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
/* 1 */
|
||||||
|
/***/ (function(module, exports, __webpack_require__) {
|
||||||
|
|
||||||
|
|
||||||
|
const xmlParse = __webpack_require__(2)
|
||||||
|
const {Widget} = __webpack_require__(3)
|
||||||
|
const {Draw} = __webpack_require__(5)
|
||||||
|
const {compareVersion} = __webpack_require__(0)
|
||||||
|
|
||||||
|
const canvasId = 'weui-canvas'
|
||||||
|
|
||||||
|
Component({
|
||||||
|
properties: {
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
value: 400
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
value: 300
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
use2dCanvas: false, // 2.9.2 后可用canvas 2d 接口
|
||||||
|
},
|
||||||
|
lifetimes: {
|
||||||
|
attached() {
|
||||||
|
const {SDKVersion, pixelRatio: dpr} = wx.getSystemInfoSync()
|
||||||
|
const use2dCanvas = compareVersion(SDKVersion, '2.9.2') >= 0
|
||||||
|
this.dpr = dpr
|
||||||
|
this.setData({use2dCanvas}, () => {
|
||||||
|
if (use2dCanvas) {
|
||||||
|
const query = this.createSelectorQuery()
|
||||||
|
query.select(`#${canvasId}`)
|
||||||
|
.fields({node: true, size: true})
|
||||||
|
.exec(res => {
|
||||||
|
const canvas = res[0].node
|
||||||
|
const ctx = canvas.getContext('2d')
|
||||||
|
canvas.width = res[0].width * dpr
|
||||||
|
canvas.height = res[0].height * dpr
|
||||||
|
ctx.scale(dpr, dpr)
|
||||||
|
this.ctx = ctx
|
||||||
|
this.canvas = canvas
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.ctx = wx.createCanvasContext(canvasId, this)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async renderToCanvas(args) {
|
||||||
|
const {wxml, style} = args
|
||||||
|
const ctx = this.ctx
|
||||||
|
const canvas = this.canvas
|
||||||
|
const use2dCanvas = this.data.use2dCanvas
|
||||||
|
|
||||||
|
if (use2dCanvas && !canvas) {
|
||||||
|
return Promise.reject(new Error('renderToCanvas: fail canvas has not been created'))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, this.data.width, this.data.height)
|
||||||
|
const {root: xom} = xmlParse(wxml)
|
||||||
|
|
||||||
|
const widget = new Widget(xom, style)
|
||||||
|
const container = widget.init()
|
||||||
|
this.boundary = {
|
||||||
|
top: container.layoutBox.top,
|
||||||
|
left: container.layoutBox.left,
|
||||||
|
width: container.computedStyle.width,
|
||||||
|
height: container.computedStyle.height,
|
||||||
|
}
|
||||||
|
const draw = new Draw(ctx, canvas, use2dCanvas)
|
||||||
|
await draw.drawNode(container)
|
||||||
|
|
||||||
|
if (!use2dCanvas) {
|
||||||
|
await this.canvasDraw(ctx)
|
||||||
|
}
|
||||||
|
return Promise.resolve(container)
|
||||||
|
},
|
||||||
|
|
||||||
|
canvasDraw(ctx, reserve) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
ctx.draw(reserve, () => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
canvasToTempFilePath(args = {}) {
|
||||||
|
const use2dCanvas = this.data.use2dCanvas
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const {
|
||||||
|
top, left, width, height
|
||||||
|
} = this.boundary
|
||||||
|
|
||||||
|
const copyArgs = {
|
||||||
|
x: left,
|
||||||
|
y: top,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
destWidth: width * this.dpr,
|
||||||
|
destHeight: height * this.dpr,
|
||||||
|
canvasId,
|
||||||
|
fileType: args.fileType || 'png',
|
||||||
|
quality: args.quality || 1,
|
||||||
|
success: resolve,
|
||||||
|
fail: reject
|
||||||
|
}
|
||||||
|
|
||||||
|
if (use2dCanvas) {
|
||||||
|
delete copyArgs.canvasId
|
||||||
|
copyArgs.canvas = this.canvas
|
||||||
|
}
|
||||||
|
wx.canvasToTempFilePath(copyArgs, this)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
/* 2 */
|
||||||
|
/***/ (function(module, exports) {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose `parse`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the given string of `xml`.
|
||||||
|
*
|
||||||
|
* @param {String} xml
|
||||||
|
* @return {Object}
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
function parse(xml) {
|
||||||
|
xml = xml.trim()
|
||||||
|
|
||||||
|
// strip comments
|
||||||
|
xml = xml.replace(/<!--[\s\S]*?-->/g, '')
|
||||||
|
|
||||||
|
return document()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XML document.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function document() {
|
||||||
|
return {
|
||||||
|
declaration: declaration(),
|
||||||
|
root: tag()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declaration.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function declaration() {
|
||||||
|
const m = match(/^<\?xml\s*/)
|
||||||
|
if (!m) return
|
||||||
|
|
||||||
|
// tag
|
||||||
|
const node = {
|
||||||
|
attributes: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// attributes
|
||||||
|
while (!(eos() || is('?>'))) {
|
||||||
|
const attr = attribute()
|
||||||
|
if (!attr) return node
|
||||||
|
node.attributes[attr.name] = attr.value
|
||||||
|
}
|
||||||
|
|
||||||
|
match(/\?>\s*/)
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tag.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function tag() {
|
||||||
|
const m = match(/^<([\w-:.]+)\s*/)
|
||||||
|
if (!m) return
|
||||||
|
|
||||||
|
// name
|
||||||
|
const node = {
|
||||||
|
name: m[1],
|
||||||
|
attributes: {},
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
|
||||||
|
// attributes
|
||||||
|
while (!(eos() || is('>') || is('?>') || is('/>'))) {
|
||||||
|
const attr = attribute()
|
||||||
|
if (!attr) return node
|
||||||
|
node.attributes[attr.name] = attr.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// self closing tag
|
||||||
|
if (match(/^\s*\/>\s*/)) {
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
match(/\??>\s*/)
|
||||||
|
|
||||||
|
// content
|
||||||
|
node.content = content()
|
||||||
|
|
||||||
|
// children
|
||||||
|
let child
|
||||||
|
while (child = tag()) {
|
||||||
|
node.children.push(child)
|
||||||
|
}
|
||||||
|
|
||||||
|
// closing
|
||||||
|
match(/^<\/[\w-:.]+>\s*/)
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text content.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function content() {
|
||||||
|
const m = match(/^([^<]*)/)
|
||||||
|
if (m) return m[1]
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attribute.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function attribute() {
|
||||||
|
const m = match(/([\w:-]+)\s*=\s*("[^"]*"|'[^']*'|\w+)\s*/)
|
||||||
|
if (!m) return
|
||||||
|
return {name: m[1], value: strip(m[2])}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strip quotes from `val`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function strip(val) {
|
||||||
|
return val.replace(/^['"]|['"]$/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Match `re` and advance the string.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function match(re) {
|
||||||
|
const m = xml.match(re)
|
||||||
|
if (!m) return
|
||||||
|
xml = xml.slice(m[0].length)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End-of-source.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function eos() {
|
||||||
|
return xml.length == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for `prefix`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function is(prefix) {
|
||||||
|
return xml.indexOf(prefix) == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = parse
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
/* 3 */
|
||||||
|
/***/ (function(module, exports, __webpack_require__) {
|
||||||
|
|
||||||
|
const Block = __webpack_require__(4)
|
||||||
|
const {splitLineToCamelCase} = __webpack_require__(0)
|
||||||
|
|
||||||
|
class Element extends Block {
|
||||||
|
constructor(prop) {
|
||||||
|
super(prop.style)
|
||||||
|
this.name = prop.name
|
||||||
|
this.attributes = prop.attributes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Widget {
|
||||||
|
constructor(xom, style) {
|
||||||
|
this.xom = xom
|
||||||
|
this.style = style
|
||||||
|
|
||||||
|
this.inheritProps = ['fontSize', 'lineHeight', 'textAlign', 'verticalAlign', 'color']
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.container = this.create(this.xom)
|
||||||
|
this.container.layout()
|
||||||
|
|
||||||
|
this.inheritStyle(this.container)
|
||||||
|
return this.container
|
||||||
|
}
|
||||||
|
|
||||||
|
// 继承父节点的样式
|
||||||
|
inheritStyle(node) {
|
||||||
|
const parent = node.parent || null
|
||||||
|
const children = node.children || {}
|
||||||
|
const computedStyle = node.computedStyle
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
this.inheritProps.forEach(prop => {
|
||||||
|
computedStyle[prop] = computedStyle[prop] || parent.computedStyle[prop]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.values(children).forEach(child => {
|
||||||
|
this.inheritStyle(child)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
create(node) {
|
||||||
|
let classNames = (node.attributes.class || '').split(' ')
|
||||||
|
classNames = classNames.map(item => splitLineToCamelCase(item.trim()))
|
||||||
|
const style = {}
|
||||||
|
classNames.forEach(item => {
|
||||||
|
Object.assign(style, this.style[item] || {})
|
||||||
|
})
|
||||||
|
|
||||||
|
const args = {name: node.name, style}
|
||||||
|
|
||||||
|
const attrs = Object.keys(node.attributes)
|
||||||
|
const attributes = {}
|
||||||
|
for (const attr of attrs) {
|
||||||
|
const value = node.attributes[attr]
|
||||||
|
const CamelAttr = splitLineToCamelCase(attr)
|
||||||
|
|
||||||
|
if (value === '' || value === 'true') {
|
||||||
|
attributes[CamelAttr] = true
|
||||||
|
} else if (value === 'false') {
|
||||||
|
attributes[CamelAttr] = false
|
||||||
|
} else {
|
||||||
|
attributes[CamelAttr] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attributes.text = node.content
|
||||||
|
args.attributes = attributes
|
||||||
|
const element = new Element(args)
|
||||||
|
node.children.forEach(childNode => {
|
||||||
|
const childElement = this.create(childNode)
|
||||||
|
element.add(childElement)
|
||||||
|
})
|
||||||
|
return element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {Widget}
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
/* 4 */
|
||||||
|
/***/ (function(module, exports) {
|
||||||
|
|
||||||
|
module.exports = require("widget-ui");
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
/* 5 */
|
||||||
|
/***/ (function(module, exports) {
|
||||||
|
|
||||||
|
class Draw {
|
||||||
|
constructor(context, canvas, use2dCanvas = false) {
|
||||||
|
this.ctx = context
|
||||||
|
this.canvas = canvas || null
|
||||||
|
this.use2dCanvas = use2dCanvas
|
||||||
|
}
|
||||||
|
|
||||||
|
roundRect(x, y, w, h, r, fill = true, stroke = false) {
|
||||||
|
if (r < 0) return
|
||||||
|
const ctx = this.ctx
|
||||||
|
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 3 / 2)
|
||||||
|
ctx.arc(x + w - r, y + r, r, Math.PI * 3 / 2, 0)
|
||||||
|
ctx.arc(x + w - r, y + h - r, r, 0, Math.PI / 2)
|
||||||
|
ctx.arc(x + r, y + h - r, r, Math.PI / 2, Math.PI)
|
||||||
|
ctx.lineTo(x, y + r)
|
||||||
|
if (stroke) ctx.stroke()
|
||||||
|
if (fill) ctx.fill()
|
||||||
|
}
|
||||||
|
|
||||||
|
drawView(box, style) {
|
||||||
|
const ctx = this.ctx
|
||||||
|
const {
|
||||||
|
left: x, top: y, width: w, height: h
|
||||||
|
} = box
|
||||||
|
const {
|
||||||
|
borderRadius = 0,
|
||||||
|
borderWidth = 0,
|
||||||
|
borderColor,
|
||||||
|
color = '#000',
|
||||||
|
backgroundColor = 'transparent',
|
||||||
|
} = style
|
||||||
|
ctx.save()
|
||||||
|
// 外环
|
||||||
|
if (borderWidth > 0) {
|
||||||
|
ctx.fillStyle = borderColor || color
|
||||||
|
this.roundRect(x, y, w, h, borderRadius)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 内环
|
||||||
|
ctx.fillStyle = backgroundColor
|
||||||
|
const innerWidth = w - 2 * borderWidth
|
||||||
|
const innerHeight = h - 2 * borderWidth
|
||||||
|
const innerRadius = borderRadius - borderWidth >= 0 ? borderRadius - borderWidth : 0
|
||||||
|
this.roundRect(x + borderWidth, y + borderWidth, innerWidth, innerHeight, innerRadius)
|
||||||
|
ctx.restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
async drawImage(img, box, style) {
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
const ctx = this.ctx
|
||||||
|
const canvas = this.canvas
|
||||||
|
|
||||||
|
const {
|
||||||
|
borderRadius = 0
|
||||||
|
} = style
|
||||||
|
const {
|
||||||
|
left: x, top: y, width: w, height: h
|
||||||
|
} = box
|
||||||
|
ctx.save()
|
||||||
|
this.roundRect(x, y, w, h, borderRadius, false, false)
|
||||||
|
ctx.clip()
|
||||||
|
|
||||||
|
const _drawImage = (img) => {
|
||||||
|
if (this.use2dCanvas) {
|
||||||
|
const Image = canvas.createImage()
|
||||||
|
Image.onload = () => {
|
||||||
|
ctx.drawImage(Image, x, y, w, h)
|
||||||
|
ctx.restore()
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
Image.onerror = () => { reject(new Error(`createImage fail: ${img}`)) }
|
||||||
|
Image.src = img
|
||||||
|
} else {
|
||||||
|
ctx.drawImage(img, x, y, w, h)
|
||||||
|
ctx.restore()
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isTempFile = /^wxfile:\/\//.test(img)
|
||||||
|
const isNetworkFile = /^https?:\/\//.test(img)
|
||||||
|
|
||||||
|
if (isTempFile) {
|
||||||
|
_drawImage(img)
|
||||||
|
} else if (isNetworkFile) {
|
||||||
|
wx.downloadFile({
|
||||||
|
url: img,
|
||||||
|
success(res) {
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
_drawImage(res.tempFilePath)
|
||||||
|
} else {
|
||||||
|
reject(new Error(`downloadFile:fail ${img}`))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail() {
|
||||||
|
reject(new Error(`downloadFile:fail ${img}`))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
reject(new Error(`image format error: ${img}`))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line complexity
|
||||||
|
drawText(text, box, style) {
|
||||||
|
const ctx = this.ctx
|
||||||
|
let {
|
||||||
|
left: x, top: y, width: w, height: h
|
||||||
|
} = box
|
||||||
|
let {
|
||||||
|
color = '#000',
|
||||||
|
lineHeight = '1.4em',
|
||||||
|
fontSize = 14,
|
||||||
|
textAlign = 'left',
|
||||||
|
verticalAlign = 'top',
|
||||||
|
backgroundColor = 'transparent'
|
||||||
|
} = style
|
||||||
|
|
||||||
|
if (typeof lineHeight === 'string') { // 2em
|
||||||
|
lineHeight = Math.ceil(parseFloat(lineHeight.replace('em')) * fontSize)
|
||||||
|
}
|
||||||
|
if (!text || (lineHeight > h)) return
|
||||||
|
|
||||||
|
ctx.save()
|
||||||
|
ctx.textBaseline = 'top'
|
||||||
|
ctx.font = `${fontSize}px sans-serif`
|
||||||
|
ctx.textAlign = textAlign
|
||||||
|
|
||||||
|
// 背景色
|
||||||
|
ctx.fillStyle = backgroundColor
|
||||||
|
this.roundRect(x, y, w, h, 0)
|
||||||
|
|
||||||
|
// 文字颜色
|
||||||
|
ctx.fillStyle = color
|
||||||
|
|
||||||
|
// 水平布局
|
||||||
|
switch (textAlign) {
|
||||||
|
case 'left':
|
||||||
|
break
|
||||||
|
case 'center':
|
||||||
|
x += 0.5 * w
|
||||||
|
break
|
||||||
|
case 'right':
|
||||||
|
x += w
|
||||||
|
break
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
|
||||||
|
const textWidth = ctx.measureText(text).width
|
||||||
|
const actualHeight = Math.ceil(textWidth / w) * lineHeight
|
||||||
|
let paddingTop = Math.ceil((h - actualHeight) / 2)
|
||||||
|
if (paddingTop < 0) paddingTop = 0
|
||||||
|
|
||||||
|
// 垂直布局
|
||||||
|
switch (verticalAlign) {
|
||||||
|
case 'top':
|
||||||
|
break
|
||||||
|
case 'middle':
|
||||||
|
y += paddingTop
|
||||||
|
break
|
||||||
|
case 'bottom':
|
||||||
|
y += 2 * paddingTop
|
||||||
|
break
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
|
||||||
|
const inlinePaddingTop = Math.ceil((lineHeight - fontSize) / 2)
|
||||||
|
|
||||||
|
// 不超过一行
|
||||||
|
if (textWidth <= w) {
|
||||||
|
ctx.fillText(text, x, y + inlinePaddingTop)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多行文本
|
||||||
|
const chars = text.split('')
|
||||||
|
const _y = y
|
||||||
|
|
||||||
|
// 逐行绘制
|
||||||
|
let line = ''
|
||||||
|
for (const ch of chars) {
|
||||||
|
const testLine = line + ch
|
||||||
|
const testWidth = ctx.measureText(testLine).width
|
||||||
|
|
||||||
|
if (testWidth > w) {
|
||||||
|
ctx.fillText(line, x, y + inlinePaddingTop)
|
||||||
|
y += lineHeight
|
||||||
|
line = ch
|
||||||
|
if ((y + lineHeight) > (_y + h)) break
|
||||||
|
} else {
|
||||||
|
line = testLine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 避免溢出
|
||||||
|
if ((y + lineHeight) <= (_y + h)) {
|
||||||
|
ctx.fillText(line, x, y + inlinePaddingTop)
|
||||||
|
}
|
||||||
|
ctx.restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
async drawNode(element) {
|
||||||
|
const {layoutBox, computedStyle, name} = element
|
||||||
|
const {src, text} = element.attributes
|
||||||
|
if (name === 'view') {
|
||||||
|
this.drawView(layoutBox, computedStyle)
|
||||||
|
} else if (name === 'image') {
|
||||||
|
await this.drawImage(src, layoutBox, computedStyle)
|
||||||
|
} else if (name === 'text') {
|
||||||
|
this.drawText(text, layoutBox, computedStyle)
|
||||||
|
}
|
||||||
|
const childs = Object.values(element.children)
|
||||||
|
for (const child of childs) {
|
||||||
|
await this.drawNode(child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Draw
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***/ })
|
||||||
|
/******/ ]);
|
||||||
|
});
|
||||||
|
export default global['__wxComponents']['wxml-to-canvas/miniprogram_dist/index']
|
||||||
|
</script>
|
||||||
|
<style platform="mp-weixin">
|
||||||
|
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user