Compare commits
No commits in common. "main" and "cz_dev" have entirely different histories.
116
App.vue
116
App.vue
|
|
@ -4,26 +4,10 @@
|
||||||
export default {
|
export default {
|
||||||
globalData: {
|
globalData: {
|
||||||
// 用于从首页跳转到服务页面时传递需要高亮的分类
|
// 用于从首页跳转到服务页面时传递需要高亮的分类
|
||||||
serviceCategory: null,
|
serviceCategory: null
|
||||||
// 扫码进入时携带的邀请码(小程序码 scene),格式:邀请类型-用户id,如 '0-123'
|
|
||||||
inviteCode: null
|
|
||||||
},
|
},
|
||||||
onLaunch: function(options) {
|
onLaunch: function() {
|
||||||
console.log('App Launch')
|
console.log('App Launch')
|
||||||
// 扫码进入:小程序码的 scene 即邀请码(格式 邀请类型-用户id,如 0-123、1-123)
|
|
||||||
const scene = (options && options.query && options.query.scene) || (options && options.scene)
|
|
||||||
const sceneStr = scene != null ? String(scene) : ''
|
|
||||||
if (sceneStr && /^\d+-\d+$/.test(sceneStr)) {
|
|
||||||
this.globalData.inviteCode = sceneStr
|
|
||||||
uni.setStorageSync('inviteCode', sceneStr)
|
|
||||||
// 未登录时直接进入登录注册页,便于邀请流程
|
|
||||||
if (!uni.getStorageSync('token')) {
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.reLaunch({ url: '/pages/login/login' })
|
|
||||||
}, 50)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 检查登录状态
|
// 检查登录状态
|
||||||
this.checkLoginStatus()
|
this.checkLoginStatus()
|
||||||
},
|
},
|
||||||
|
|
@ -35,52 +19,52 @@
|
||||||
console.log(options)
|
console.log(options)
|
||||||
|
|
||||||
// 2. 注意:scene 返回的是数字类型,不是字符串!原生代码中写 '1038' 会导致判断失败
|
// 2. 注意:scene 返回的是数字类型,不是字符串!原生代码中写 '1038' 会导致判断失败
|
||||||
if (options.scene === 1038 &&
|
// if (options.scene === 1038 &&
|
||||||
options.referrerInfo?.appId === 'wxef277996acc166c3') {
|
// options.referrerInfo?.appId === 'wxef277996acc166c3') {
|
||||||
|
|
||||||
// 从收银台小程序返回的逻辑
|
// // 从收银台小程序返回的逻辑
|
||||||
const extraData = options.referrerInfo.extraData;
|
// const extraData = options.referrerInfo.extraData;
|
||||||
console.log("extraData",extraData)
|
// console.log("extraData",extraData)
|
||||||
|
|
||||||
if (!extraData) {
|
// if (!extraData) {
|
||||||
uni.showToast({
|
// uni.showToast({
|
||||||
title: '当前通过物理按键返回,未接收到返参,建议自行查询交易结果',
|
// title: '当前通过物理按键返回,未接收到返参,建议自行查询交易结果',
|
||||||
icon: 'none',
|
// icon: 'none',
|
||||||
duration: 3000
|
// duration: 3000
|
||||||
});
|
// });
|
||||||
} else {
|
// } else {
|
||||||
if (extraData.code === 'success') {
|
// if (extraData.code === 'success') {
|
||||||
// 有返回成功标记后,启动轮询查询订单状态
|
// // 有返回成功标记后,启动轮询查询订单状态
|
||||||
// 1. 优先使用收银台回传的订单号
|
// // 1. 优先使用收银台回传的订单号
|
||||||
const orderNumberFromExtra =
|
// const orderNumberFromExtra =
|
||||||
extraData.orderNumber || extraData.reqsn || extraData.orderNo;
|
// extraData.orderNumber || extraData.reqsn || extraData.orderNo;
|
||||||
// 2. 如果对方没有回传,则使用我们在跳转前自己缓存到本地的订单号
|
// // 2. 如果对方没有回传,则使用我们在跳转前自己缓存到本地的订单号
|
||||||
const storedOrderNumber = uni.getStorageSync("lastOrderNumber");
|
// const storedOrderNumber = uni.getStorageSync("lastOrderNumber");
|
||||||
const orderNumber = orderNumberFromExtra || storedOrderNumber;
|
// const orderNumber = orderNumberFromExtra || storedOrderNumber;
|
||||||
|
|
||||||
if (orderNumber) {
|
// if (orderNumber) {
|
||||||
this.startOrderStatusPolling(orderNumber);
|
// this.startOrderStatusPolling(orderNumber);
|
||||||
} else {
|
// } else {
|
||||||
uni.showToast({
|
// uni.showToast({
|
||||||
title: '结算返回缺少订单号,请稍后在服务记录中查看',
|
// title: '支付返回缺少订单号,请稍后在服务记录中查看',
|
||||||
icon: 'none',
|
// icon: 'none',
|
||||||
duration: 3000
|
// duration: 3000
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
} else if (extraData.code === 'cancel') {
|
// } else if (extraData.code === 'cancel') {
|
||||||
uni.showToast({
|
// uni.showToast({
|
||||||
title: '已取消',
|
// title: '支付已取消',
|
||||||
icon: 'none'
|
// icon: 'none'
|
||||||
});
|
// });
|
||||||
} else {
|
// } else {
|
||||||
uni.showToast({
|
// uni.showToast({
|
||||||
title: `结算失败:${extraData.errmsg || '未知错误'}`,
|
// title: `支付失败:${extraData.errmsg || '未知错误'}`,
|
||||||
icon: 'none',
|
// icon: 'none',
|
||||||
duration: 3000
|
// duration: 3000
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
},
|
},
|
||||||
onHide: function() {
|
onHide: function() {
|
||||||
console.log('App Hide')
|
console.log('App Hide')
|
||||||
|
|
@ -118,7 +102,7 @@
|
||||||
}, 100)
|
}, 100)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 启动轮询状态:回到小程序立马查一次,失败则每隔 3 秒再查,最多再查 3 次(共 4 次)
|
// 启动轮询支付状态:回到小程序立马查一次,失败则每隔 3 秒再查,最多再查 3 次(共 4 次)
|
||||||
startOrderStatusPolling(orderNumber) {
|
startOrderStatusPolling(orderNumber) {
|
||||||
if (this._orderStatusTimer) {
|
if (this._orderStatusTimer) {
|
||||||
clearInterval(this._orderStatusTimer);
|
clearInterval(this._orderStatusTimer);
|
||||||
|
|
@ -136,7 +120,7 @@
|
||||||
clearInterval(this._orderStatusTimer);
|
clearInterval(this._orderStatusTimer);
|
||||||
this._orderStatusTimer = null;
|
this._orderStatusTimer = null;
|
||||||
uni.removeStorageSync("lastOrderNumber");
|
uni.removeStorageSync("lastOrderNumber");
|
||||||
uni.showToast({ title: '结算成功', icon: 'success' });
|
uni.showToast({ title: '支付成功', icon: 'success' });
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/profileSub/serviceRecords?tab=pending_verification'
|
url: '/pages/profileSub/serviceRecords?tab=pending_verification'
|
||||||
});
|
});
|
||||||
|
|
@ -146,13 +130,13 @@
|
||||||
clearInterval(this._orderStatusTimer);
|
clearInterval(this._orderStatusTimer);
|
||||||
this._orderStatusTimer = null;
|
this._orderStatusTimer = null;
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '状态确认超时,请稍后在服务记录中查看',
|
title: '支付状态确认超时,请稍后在服务记录中查看',
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
duration: 3000
|
duration: 3000
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('查询订单状态失败:', error);
|
console.error('查询订单支付状态失败:', error);
|
||||||
if (times >= maxTimes) {
|
if (times >= maxTimes) {
|
||||||
clearInterval(this._orderStatusTimer);
|
clearInterval(this._orderStatusTimer);
|
||||||
this._orderStatusTimer = null;
|
this._orderStatusTimer = null;
|
||||||
|
|
@ -169,4 +153,4 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/*每个页面公共css */
|
/*每个页面公共css */
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -27,7 +27,7 @@ export function login(data) {
|
||||||
* @param {String} data.phoneCode 手机 code, 小程序通过 wx.getPhoneNumber 方法获得
|
* @param {String} data.phoneCode 手机 code, 小程序通过 wx.getPhoneNumber 方法获得
|
||||||
* @param {String} data.loginCode 登录 code, 小程序通过 wx.login 方法获得
|
* @param {String} data.loginCode 登录 code, 小程序通过 wx.login 方法获得
|
||||||
* @param {String} data.state state 参数,必填,用于回调的随机值
|
* @param {String} data.state state 参数,必填,用于回调的随机值
|
||||||
* @param {String} data.inviteCode 邀请码,可选。格式 "邀请类型-用户id":0=会员邀请会员,1=系统用户邀请会员。例如 0-123、1-123
|
* @param {String} data.inviteCode 邀请码,可选(第一位是类型第二位是用户id,例如:1-1, 0-1)
|
||||||
* @returns {Promise} 返回登录结果(包含token等)
|
* @returns {Promise} 返回登录结果(包含token等)
|
||||||
*/
|
*/
|
||||||
export function loginByPhone(data) {
|
export function loginByPhone(data) {
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@
|
||||||
|
|
||||||
// 基础URL配置(注意:末尾不要加斜杠)
|
// 基础URL配置(注意:末尾不要加斜杠)
|
||||||
const BASE_URL = 'https://guangsh.manage.hschengtai.com'
|
const BASE_URL = 'https://guangsh.manage.hschengtai.com'
|
||||||
// const BASE_URL = 'http://192.168.0.97:48085'
|
// const BASE_URL = 'http://192.168.5.134:48085'
|
||||||
// const BASE_URL = 'http://192.168.5.135:48085'
|
|
||||||
// 是否正在刷新token(防止并发刷新)
|
// 是否正在刷新token(防止并发刷新)
|
||||||
let isRefreshing = false
|
let isRefreshing = false
|
||||||
// 等待刷新完成的请求队列
|
// 等待刷新完成的请求队列
|
||||||
|
|
@ -280,8 +279,7 @@ export function request(options = {}) {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: res.data.msg || res.data.message || '账号未登录,请前往登录',
|
content: res.data.msg || res.data.message || '账号未登录,请前往登录',
|
||||||
showCancel: true,
|
showCancel: false,
|
||||||
cancelText: '取消',
|
|
||||||
confirmText: '去登录',
|
confirmText: '去登录',
|
||||||
success: (modalRes) => {
|
success: (modalRes) => {
|
||||||
if (modalRes.confirm) {
|
if (modalRes.confirm) {
|
||||||
|
|
@ -330,8 +328,7 @@ export function request(options = {}) {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: errorMsg,
|
content: errorMsg,
|
||||||
showCancel: true,
|
showCancel: false,
|
||||||
cancelText: '取消',
|
|
||||||
confirmText: '去登录',
|
confirmText: '去登录',
|
||||||
success: (modalRes) => {
|
success: (modalRes) => {
|
||||||
if (modalRes.confirm) {
|
if (modalRes.confirm) {
|
||||||
|
|
|
||||||
|
|
@ -28,17 +28,6 @@ export function createRealNameInfo(data) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得实名信息
|
|
||||||
export function getRealNameInfo(data) {
|
|
||||||
return request({
|
|
||||||
url: '/app-api/member/user-real-name/get',
|
|
||||||
method: 'GET',
|
|
||||||
data: data,
|
|
||||||
showLoading: false,
|
|
||||||
needAuth: true // 需要token认证
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 我的收藏
|
// 我的收藏
|
||||||
export function getMyCollect(data) {
|
export function getMyCollect(data) {
|
||||||
return request({
|
return request({
|
||||||
|
|
@ -50,17 +39,6 @@ export function getMyCollect(data) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 业务数据获取,主要是分享,收藏等根据业务id 和类型获取数据
|
|
||||||
export function getBusinessData(data) {
|
|
||||||
return request({
|
|
||||||
url: '/app-api/member/lu-buiness/get',
|
|
||||||
method: 'GET',
|
|
||||||
data: data,
|
|
||||||
showLoading: false,
|
|
||||||
needAuth: true // 需要token认证
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建会员建议
|
// 创建会员建议
|
||||||
export function createLaborUnionSuggest(data) {
|
export function createLaborUnionSuggest(data) {
|
||||||
return request({
|
return request({
|
||||||
|
|
@ -72,17 +50,6 @@ export function createLaborUnionSuggest(data) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得会员建议分页
|
|
||||||
export function getLaborUnionSuggestPage(data) {
|
|
||||||
return request({
|
|
||||||
url: '/app-api/member/labor-union-suggest/page',
|
|
||||||
method: 'GET',
|
|
||||||
data: data,
|
|
||||||
showLoading: false,
|
|
||||||
needAuth: true // 需要token认证
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发布消息
|
// 发布消息
|
||||||
export function createLaborUnionMessage(data) {
|
export function createLaborUnionMessage(data) {
|
||||||
return request({
|
return request({
|
||||||
|
|
@ -92,44 +59,4 @@ export function createLaborUnionMessage(data) {
|
||||||
showLoading: false,
|
showLoading: false,
|
||||||
needAuth: true // 需要token认证
|
needAuth: true // 需要token认证
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取邀请小程序码图片。入参格式与后端约定一致,支持返回 URL 或 Base64。
|
|
||||||
* @param {String} inviteCode 邀请码,格式 邀请类型-用户id,如 '0-123',会作为 scene 传给后端(若后端需数字可传 userId)
|
|
||||||
* @returns {Promise<String>} 小程序码图片 URL 或 Data URL(Base64),失败则抛出
|
|
||||||
*/
|
|
||||||
export function getInviteQRCode(inviteCode) {
|
|
||||||
// 入参示例:scene、path、width、autoColor、checkPath、hyaline
|
|
||||||
const scene = inviteCode || ''
|
|
||||||
const path = 'pages/login/login'
|
|
||||||
return request({
|
|
||||||
url: '/app-api/member/social-user/wxa-qrcode',
|
|
||||||
method: 'POST',
|
|
||||||
data: {
|
|
||||||
scene,
|
|
||||||
path,
|
|
||||||
width: 430,
|
|
||||||
autoColor: true,
|
|
||||||
checkPath: true,
|
|
||||||
hyaline: true
|
|
||||||
},
|
|
||||||
showLoading: false,
|
|
||||||
needAuth: true
|
|
||||||
}).then(res => {
|
|
||||||
if (res == null) return null
|
|
||||||
// 接口可能直接返回 base64 字符串(request 里 resolve 的 data 即为该字符串)
|
|
||||||
if (typeof res === 'string') {
|
|
||||||
const raw = res.trim()
|
|
||||||
if (raw.startsWith('data:image')) return raw
|
|
||||||
if (raw.length > 0) return 'data:image/png;base64,' + raw
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const url = res.url || (res.data && res.data.url)
|
|
||||||
if (url) return url
|
|
||||||
const base64 = res.data && typeof res.data === 'string' ? res.data : (res.data && res.data.data)
|
|
||||||
if (typeof base64 === 'string' && base64.length > 0)
|
|
||||||
return base64.startsWith('data:image') ? base64 : 'data:image/png;base64,' + base64
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
@ -30,10 +30,10 @@ export function getGuildStoreDetail(id) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得工会代金券
|
// 获得工会优惠券
|
||||||
export function getGuildVoucher(params = {}) {
|
export function getGuildCoupon(params = {}) {
|
||||||
return request({
|
return request({
|
||||||
url: '/app-api/member/labor-union-voucher/page',
|
url: '/app-api/member/labor-union-coupon/page',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
data: params,
|
data: params,
|
||||||
})
|
})
|
||||||
|
|
@ -100,12 +100,12 @@ export function isNeedMap(params = {}) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 工会代金券/商品下单并发起微信支付(需要后端返回小程序支付参数)
|
// 工会优惠券/商品下单并发起微信支付(需要后端返回小程序支付参数)
|
||||||
// 约定:后端返回字段需包含 timeStamp、nonceStr、package、signType、paySign(或等价字段)
|
// 约定:后端返回字段需包含 timeStamp、nonceStr、package、signType、paySign(或等价字段)
|
||||||
// export function createGuildVoucherWxPay(params = {}) {
|
// export function createGuildCouponWxPay(params = {}) {
|
||||||
// return request({
|
// return request({
|
||||||
// // TODO: 如后端实际路径不同,请替换这里的 url
|
// // TODO: 如后端实际路径不同,请替换这里的 url
|
||||||
// url: '/app-api/member/labor-union-voucher/pay',
|
// url: '/app-api/member/labor-union-coupon/pay',
|
||||||
// method: 'POST',
|
// method: 'POST',
|
||||||
// data: params,
|
// data: params,
|
||||||
// showLoading: true,
|
// showLoading: true,
|
||||||
|
|
@ -136,18 +136,18 @@ export function updateLuOrderPayStatus(params = {}){
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除代金券购买 (暂时不用)
|
// 删除优惠卷购买 (暂时不用)
|
||||||
// export function delVoucherPurchaseBuy(id){
|
// export function delCouponPurchaseBuy(id){
|
||||||
// return request({
|
// return request({
|
||||||
// url: '/app-api/member/labor-union-voucher-purchase/delete?id='+id,
|
// url: '/app-api/member/labor-union-coupon-purchase/delete?id='+id,
|
||||||
// method: 'DELETE',
|
// method: 'DELETE',
|
||||||
// })
|
// })
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// // 取消代金券购买 (暂时不用)
|
// // 取消优惠卷购买 (暂时不用)
|
||||||
// export function cancelVoucherPurchaseBuy(id){
|
// export function cancelCouponPurchaseBuy(id){
|
||||||
// return request({
|
// return request({
|
||||||
// url: '/app-api/member/labor-union-voucher-purchase/cancel?id='+id,
|
// url: '/app-api/member/labor-union-coupon-purchase/cancel?id='+id,
|
||||||
// method: 'POST',
|
// method: 'POST',
|
||||||
// })
|
// })
|
||||||
// }
|
// }
|
||||||
|
|
@ -183,22 +183,4 @@ export function cancelOrder(params = {}){
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得优惠卷分页
|
|
||||||
export function getLuCouponPage(params = {}) {
|
|
||||||
return request({
|
|
||||||
url: '/app-api/member/lu-coupon/page',
|
|
||||||
method: 'GET',
|
|
||||||
data: params,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获得优惠卷详情
|
|
||||||
export function getLuCouponDetail(id) {
|
|
||||||
return request({
|
|
||||||
url: '/app-api/member/lu-coupon/get',
|
|
||||||
method: 'GET',
|
|
||||||
data: { id },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,8 @@
|
||||||
</view> -->
|
</view> -->
|
||||||
|
|
||||||
<view class="action-item" @click="handleLike">
|
<view class="action-item" @click="handleLike">
|
||||||
<image
|
<image class="action-icon" src="/static/service/like_icon.png" mode="aspectFill"></image>
|
||||||
class="action-icon"
|
<text class="action-text">点赞</text>
|
||||||
:class="{ active: isLiked }"
|
|
||||||
:src="isLiked ? '/static/service/like_icon_active.png' : '/static/service/like_icon.png'"
|
|
||||||
mode="aspectFill"
|
|
||||||
></image>
|
|
||||||
<text class="action-text" :class="{ active: isLiked }">点赞</text>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<button class="action-item share-button" open-type="share" @click="handleShareClick">
|
<button class="action-item share-button" open-type="share" @click="handleShareClick">
|
||||||
|
|
@ -21,13 +16,8 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<view class="action-item" @click="handleCollect">
|
<view class="action-item" @click="handleCollect">
|
||||||
<image
|
<image class="action-icon" src="/static/service/collect_icon.png" mode="aspectFill"></image>
|
||||||
class="action-icon"
|
<text class="action-text">收藏</text>
|
||||||
:class="{ active: isCollected }"
|
|
||||||
:src="isCollected ? '/static/service/collect_icon_active.png' : '/static/service/collect_icon.png'"
|
|
||||||
mode="aspectFill"
|
|
||||||
></image>
|
|
||||||
<text class="action-text" :class="{ active: isCollected }">收藏</text>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -265,6 +255,11 @@ export default {
|
||||||
&.active {
|
&.active {
|
||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-text {
|
||||||
|
font-size: 48rpx;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-text {
|
.action-text {
|
||||||
|
|
|
||||||
29
pages.json
29
pages.json
|
|
@ -52,13 +52,6 @@
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "selectCoupon",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "选择优惠卷",
|
|
||||||
"navigationStyle": "custom"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "mapDetail",
|
"path": "mapDetail",
|
||||||
"style": {
|
"style": {
|
||||||
|
|
@ -98,8 +91,14 @@
|
||||||
"navigationBarTitleText": "投诉建议",
|
"navigationBarTitleText": "投诉建议",
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "postMessage",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "发布消息",
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -139,20 +138,6 @@
|
||||||
"navigationBarTitleText": "隐私政策",
|
"navigationBarTitleText": "隐私政策",
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "myCoupons",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "我的优惠卷",
|
|
||||||
"navigationStyle": "custom"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "couponDetail",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "优惠卷详情",
|
|
||||||
"navigationStyle": "custom"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="complaints-page">
|
<view class="complaints-page">
|
||||||
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
<!-- 头部区域 -->
|
||||||
<NavHeader title="投诉建议" />
|
<NavHeader title="投诉建议" />
|
||||||
</view>
|
|
||||||
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
<!-- 表单内容区 -->
|
||||||
<scroll-view
|
<scroll-view class="form-content" scroll-y="true">
|
||||||
class="form-content"
|
|
||||||
scroll-y="true"
|
|
||||||
:refresher-enabled="true"
|
|
||||||
:refresher-triggered="listRefreshing"
|
|
||||||
@refresherrefresh="onListRefresh"
|
|
||||||
@scrolltolower="onListLoadMore"
|
|
||||||
:lower-threshold="80"
|
|
||||||
>
|
|
||||||
<!-- 投诉建议表单 -->
|
<!-- 投诉建议表单 -->
|
||||||
<view class="form-section">
|
<view class="form-section">
|
||||||
<view class="section-title">填写投诉建议</view>
|
<view class="section-title">填写投诉建议</view>
|
||||||
|
|
@ -60,41 +52,12 @@
|
||||||
{{ loading ? '提交中...' : '提交' }}
|
{{ loading ? '提交中...' : '提交' }}
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 我的投诉建议列表 -->
|
|
||||||
<view class="list-section">
|
|
||||||
<view class="section-title">我的投诉建议</view>
|
|
||||||
<view class="empty-list" v-if="!listLoading && suggestList.length === 0">
|
|
||||||
<text class="empty-text">暂无投诉建议记录</text>
|
|
||||||
</view>
|
|
||||||
<view
|
|
||||||
class="suggest-card"
|
|
||||||
v-for="(item, index) in suggestList"
|
|
||||||
:key="item.id || index"
|
|
||||||
>
|
|
||||||
<view class="card-header">
|
|
||||||
<text class="card-title">{{ item.title || '无标题' }}</text>
|
|
||||||
<text class="card-time">{{ formatTime(item.createTime) }}</text>
|
|
||||||
</view>
|
|
||||||
<text class="card-content" v-if="item.content">{{ item.content }}</text>
|
|
||||||
<view class="card-reply" v-if="getReplyText(item)">
|
|
||||||
<text class="reply-label">{{ item.replyName }}回复:</text>
|
|
||||||
<text class="reply-text">{{ getReplyText(item) }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="list-footer" v-if="suggestList.length > 0">
|
|
||||||
<text v-if="listLoadMore" class="footer-text">加载中...</text>
|
|
||||||
<text v-else-if="!listHasMore" class="footer-text">没有更多了</text>
|
|
||||||
<text v-else class="footer-text">上拉加载更多</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { createLaborUnionSuggest,getLaborUnionSuggestPage } from '@/api/profile.js'
|
import { createLaborUnionSuggest } from '@/api/profile.js'
|
||||||
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -103,91 +66,22 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
statusBarHeight: 0,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
formData: {
|
formData: {
|
||||||
title: '',
|
title: '',
|
||||||
content: ''
|
content: ''
|
||||||
},
|
}
|
||||||
suggestList: [],
|
|
||||||
listPageNo: 1,
|
|
||||||
listPageSize: 10,
|
|
||||||
listHasMore: true,
|
|
||||||
listLoading: false,
|
|
||||||
listLoadMore: false,
|
|
||||||
listRefreshing: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
headerHeight() {
|
|
||||||
return this.statusBarHeight + 44
|
|
||||||
},
|
|
||||||
// 是否可以提交
|
// 是否可以提交
|
||||||
canSubmit() {
|
canSubmit() {
|
||||||
return this.formData.title && this.formData.title.trim().length >= 1
|
return this.formData.title && this.formData.title.trim().length >= 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
const systemInfo = uni.getSystemInfoSync()
|
|
||||||
this.statusBarHeight = systemInfo.statusBarHeight || 0
|
|
||||||
this.loadSuggestList()
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
formatTime(val) {
|
|
||||||
if (val == null || val === '') return ''
|
|
||||||
let ts = Number(val)
|
|
||||||
if (!Number.isFinite(ts)) return val
|
|
||||||
if (String(ts).length <= 10) ts *= 1000
|
|
||||||
const d = new Date(ts)
|
|
||||||
const y = d.getFullYear()
|
|
||||||
const m = String(d.getMonth() + 1).padStart(2, '0')
|
|
||||||
const day = String(d.getDate()).padStart(2, '0')
|
|
||||||
const h = String(d.getHours()).padStart(2, '0')
|
|
||||||
const min = String(d.getMinutes()).padStart(2, '0')
|
|
||||||
return `${y}-${m}-${day} ${h}:${min}`
|
|
||||||
},
|
|
||||||
getReplyText(item) {
|
|
||||||
const text = item.replyContent
|
|
||||||
return text ? String(text).trim() : ''
|
|
||||||
},
|
|
||||||
getReplyTime(item) {
|
|
||||||
const t = item.replyTime
|
|
||||||
return t != null && t !== '' ? this.formatTime(t) : ''
|
|
||||||
},
|
|
||||||
async loadSuggestList(append = false) {
|
|
||||||
if (this.listLoading) return
|
|
||||||
this.listLoading = true
|
|
||||||
try {
|
|
||||||
const res = await getLaborUnionSuggestPage({
|
|
||||||
pageNo: this.listPageNo,
|
|
||||||
pageSize: this.listPageSize
|
|
||||||
})
|
|
||||||
const raw = res.list || res.records || (res.data && (res.data.list || res.data.records)) || []
|
|
||||||
const list = Array.isArray(raw) ? raw : []
|
|
||||||
this.suggestList = append ? this.suggestList.concat(list) : list
|
|
||||||
this.listHasMore = list.length >= this.listPageSize
|
|
||||||
} catch (e) {
|
|
||||||
console.error('加载投诉建议列表失败:', e)
|
|
||||||
if (!append) this.suggestList = []
|
|
||||||
uni.showToast({ title: '加载列表失败', icon: 'none' })
|
|
||||||
} finally {
|
|
||||||
this.listLoading = false
|
|
||||||
this.listLoadMore = false
|
|
||||||
this.listRefreshing = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onListRefresh() {
|
|
||||||
this.listRefreshing = true
|
|
||||||
this.listPageNo = 1
|
|
||||||
this.listHasMore = true
|
|
||||||
this.loadSuggestList()
|
|
||||||
},
|
|
||||||
onListLoadMore() {
|
|
||||||
if (!this.listHasMore || this.listLoadMore || this.listLoading) return
|
|
||||||
this.listLoadMore = true
|
|
||||||
this.listPageNo += 1
|
|
||||||
this.loadSuggestList(true)
|
|
||||||
},
|
|
||||||
// 提交表单
|
// 提交表单
|
||||||
async handleSubmit() {
|
async handleSubmit() {
|
||||||
// 验证标题
|
// 验证标题
|
||||||
|
|
@ -216,11 +110,10 @@ export default {
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
|
|
||||||
this.formData.title = ''
|
// 延迟返回上一页
|
||||||
this.formData.content = ''
|
setTimeout(() => {
|
||||||
this.listPageNo = 1
|
uni.navigateBack()
|
||||||
this.listHasMore = true
|
}, 1500)
|
||||||
this.loadSuggestList()
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('提交投诉建议失败:', error)
|
console.error('提交投诉建议失败:', error)
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
|
|
@ -243,22 +136,6 @@ export default {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-fixed-wrapper {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
|
||||||
background: #e2e8f1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-wrap {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-content {
|
.form-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
|
@ -358,7 +235,7 @@ export default {
|
||||||
|
|
||||||
.submit-section {
|
.submit-section {
|
||||||
margin-top: 40rpx;
|
margin-top: 40rpx;
|
||||||
padding-bottom: 24rpx;
|
padding-bottom: 40rpx;
|
||||||
|
|
||||||
.submit-btn {
|
.submit-btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -383,99 +260,4 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-section {
|
|
||||||
margin-top: 24rpx;
|
|
||||||
padding-bottom: 40rpx;
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #333333;
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
padding-left: 10rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-list {
|
|
||||||
padding: 60rpx 0;
|
|
||||||
text-align: center;
|
|
||||||
.empty-text {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.suggest-card {
|
|
||||||
background: #ffffff;
|
|
||||||
border-radius: 16rpx;
|
|
||||||
padding: 24rpx;
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: flex-start;
|
|
||||||
margin-bottom: 12rpx;
|
|
||||||
|
|
||||||
.card-title {
|
|
||||||
flex: 1;
|
|
||||||
font-size: 28rpx;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #1a1819;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-time {
|
|
||||||
font-size: 22rpx;
|
|
||||||
color: #999999;
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-left: 16rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-content {
|
|
||||||
display: block;
|
|
||||||
font-size: 26rpx;
|
|
||||||
color: #666666;
|
|
||||||
line-height: 1.5;
|
|
||||||
margin-bottom: 16rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-reply {
|
|
||||||
background: #f0f6ff;
|
|
||||||
border-radius: 12rpx;
|
|
||||||
padding: 16rpx;
|
|
||||||
border-left: 4rpx solid #004294;
|
|
||||||
|
|
||||||
.reply-label {
|
|
||||||
font-size: 24rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #004294;
|
|
||||||
margin-right: 8rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reply-text {
|
|
||||||
font-size: 26rpx;
|
|
||||||
color: #333333;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reply-time {
|
|
||||||
display: block;
|
|
||||||
font-size: 22rpx;
|
|
||||||
color: #999999;
|
|
||||||
margin-top: 8rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-footer {
|
|
||||||
padding: 24rpx 0;
|
|
||||||
text-align: center;
|
|
||||||
.footer-text {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="activities-list-page">
|
<view class="activities-list-page">
|
||||||
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
<!-- 顶部导航栏 -->
|
||||||
<NavHeader title="工会活动" :show-placeholder="true" />
|
<NavHeader title="工会活动" :show-placeholder="true" />
|
||||||
</view>
|
|
||||||
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
<!-- 活动列表 -->
|
||||||
<scroll-view
|
<scroll-view
|
||||||
class="activities-scroll"
|
class="activities-scroll"
|
||||||
scroll-y="true"
|
scroll-y="true"
|
||||||
|
|
@ -61,7 +61,6 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -76,7 +75,6 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
statusBarHeight: 0,
|
|
||||||
activitiesList: [],
|
activitiesList: [],
|
||||||
// 分页相关
|
// 分页相关
|
||||||
page: 1,
|
page: 1,
|
||||||
|
|
@ -88,14 +86,7 @@ export default {
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
headerHeight() {
|
|
||||||
return this.statusBarHeight + 44;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
const systemInfo = uni.getSystemInfoSync();
|
|
||||||
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
|
||||||
this.loadActivities();
|
this.loadActivities();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -194,22 +185,7 @@ export default {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-fixed-wrapper {
|
/* 活动列表 */
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
|
||||||
background: #e2e8f1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-wrap {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activities-scroll {
|
.activities-scroll {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0; // 配合 flex: 1 使用,让 scroll-view 可以滚动
|
height: 0; // 配合 flex: 1 使用,让 scroll-view 可以滚动
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="my-collect-page">
|
<view class="my-collect-page">
|
||||||
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
<!-- 顶部导航栏 -->
|
||||||
<NavHeader title="我的收藏" />
|
<NavHeader title="我的收藏" />
|
||||||
</view>
|
|
||||||
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
<!-- 收藏列表 -->
|
||||||
<scroll-view
|
<scroll-view
|
||||||
class="collect-scroll"
|
class="collect-scroll"
|
||||||
scroll-y="true"
|
scroll-y="true"
|
||||||
|
|
@ -57,12 +57,11 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getMyCollect,getBusinessData } from "@/api/profile.js";
|
import { getMyCollect } from "@/api/profile.js";
|
||||||
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -71,7 +70,6 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
statusBarHeight: 0,
|
|
||||||
collectList: [],
|
collectList: [],
|
||||||
// 分页相关
|
// 分页相关
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
|
|
@ -83,14 +81,7 @@ export default {
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
headerHeight() {
|
|
||||||
return this.statusBarHeight + 44;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
const systemInfo = uni.getSystemInfoSync();
|
|
||||||
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
|
||||||
this.loadCollectList();
|
this.loadCollectList();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -169,17 +160,11 @@ export default {
|
||||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
// 收藏项点击:跳转详情,详情页通过 getBusinessData(id: collectId, type: collectType) 拉取数据
|
// 收藏项点击
|
||||||
handleCollectClick(item) {
|
handleCollectClick(item) {
|
||||||
const collectId = item.collectId ?? item.id;
|
// uni.navigateTo({
|
||||||
const collectType = item.collectType ?? item.type ?? "";
|
// url: `/pages/detail/richTextDetail?id=${item.collectId}`,
|
||||||
if (collectId == null || collectId === "") {
|
// });
|
||||||
uni.showToast({ title: "数据异常", icon: "none" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uni.navigateTo({
|
|
||||||
url: `/pages/detail/richTextDetail?collectId=${encodeURIComponent(collectId)}&collectType=${encodeURIComponent(collectType)}&hideBar=1`,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -194,22 +179,7 @@ export default {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-fixed-wrapper {
|
/* 收藏列表 */
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
|
||||||
background: #e2e8f1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-wrap {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.collect-scroll {
|
.collect-scroll {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0; // 配合 flex: 1 使用,让 scroll-view 可以滚动
|
height: 0; // 配合 flex: 1 使用,让 scroll-view 可以滚动
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,617 @@
|
||||||
|
<template>
|
||||||
|
<view class="post-message-page">
|
||||||
|
<!-- 头部区域 -->
|
||||||
|
<NavHeader title="发布消息" />
|
||||||
|
|
||||||
|
<!-- 表单内容区 -->
|
||||||
|
<scroll-view class="form-content" scroll-y="true">
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<view class="form-section">
|
||||||
|
<view class="section-title">基本信息</view>
|
||||||
|
|
||||||
|
<!-- 标题 -->
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="input-wrapper">
|
||||||
|
<text class="input-label">标题</text>
|
||||||
|
<input
|
||||||
|
class="input-field"
|
||||||
|
type="text"
|
||||||
|
v-model="formData.title"
|
||||||
|
placeholder="请输入消息标题"
|
||||||
|
maxlength="100"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 摘要 -->
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="textarea-wrapper">
|
||||||
|
<text class="textarea-label">摘要 <text class="optional">(选填)</text></text>
|
||||||
|
<textarea
|
||||||
|
class="textarea-field"
|
||||||
|
v-model="formData.summary"
|
||||||
|
placeholder="请输入消息摘要..."
|
||||||
|
maxlength="200"
|
||||||
|
:auto-height="true"
|
||||||
|
></textarea>
|
||||||
|
<view class="char-count">
|
||||||
|
<text>{{ formData.summary.length }}/200</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 内容 -->
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="textarea-wrapper">
|
||||||
|
<text class="textarea-label">内容 <text class="optional">(选填)</text></text>
|
||||||
|
<textarea
|
||||||
|
class="textarea-field"
|
||||||
|
v-model="formData.content"
|
||||||
|
placeholder="请输入消息详细内容..."
|
||||||
|
maxlength="2000"
|
||||||
|
:auto-height="true"
|
||||||
|
></textarea>
|
||||||
|
<view class="char-count">
|
||||||
|
<text>{{ formData.content.length }}/2000</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 封面图片 -->
|
||||||
|
<view class="form-section">
|
||||||
|
<view class="section-title">封面图片</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="upload-wrapper">
|
||||||
|
<text class="upload-label">封面图片 <text class="optional">(选填)</text></text>
|
||||||
|
<view class="upload-box" @click="chooseCoverImage">
|
||||||
|
<image
|
||||||
|
v-if="formData.coverUrl"
|
||||||
|
class="uploaded-image"
|
||||||
|
:src="formData.coverUrl"
|
||||||
|
mode="aspectFill"
|
||||||
|
></image>
|
||||||
|
<view v-else class="upload-placeholder">
|
||||||
|
<text class="upload-icon">+</text>
|
||||||
|
<text class="upload-text">点击上传封面</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 其他设置 -->
|
||||||
|
<view class="form-section">
|
||||||
|
<view class="section-title">其他设置</view>
|
||||||
|
|
||||||
|
<!-- 跳转链接 -->
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="input-wrapper">
|
||||||
|
<text class="input-label">跳转链接 <text class="optional">(选填)</text></text>
|
||||||
|
<input
|
||||||
|
class="input-field"
|
||||||
|
type="text"
|
||||||
|
v-model="formData.jumpUrl"
|
||||||
|
placeholder="请输入跳转链接,如:https://www.example.com"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 消息类型 -->
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="input-wrapper">
|
||||||
|
<text class="input-label">消息类型 <text class="optional">(选填)</text></text>
|
||||||
|
<picker
|
||||||
|
mode="selector"
|
||||||
|
:range="messageTypeOptions"
|
||||||
|
range-key="label"
|
||||||
|
:value="messageTypeIndex"
|
||||||
|
@change="handleMessageTypeChange"
|
||||||
|
>
|
||||||
|
<view class="picker-view">
|
||||||
|
<text :class="['picker-text', messageTypeIndex === -1 ? 'placeholder' : '']">
|
||||||
|
{{ messageTypeIndex !== -1 ? messageTypeOptions[messageTypeIndex].label : '请选择消息类型' }}
|
||||||
|
</text>
|
||||||
|
<text class="picker-arrow">›</text>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 等级 -->
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="input-wrapper">
|
||||||
|
<text class="input-label">等级 <text class="optional">(选填)</text></text>
|
||||||
|
<picker
|
||||||
|
mode="selector"
|
||||||
|
:range="gradeOptions"
|
||||||
|
range-key="label"
|
||||||
|
:value="gradeIndex"
|
||||||
|
@change="handleGradeChange"
|
||||||
|
>
|
||||||
|
<view class="picker-view">
|
||||||
|
<text :class="['picker-text', gradeIndex === -1 ? 'placeholder' : '']">
|
||||||
|
{{ gradeIndex !== -1 ? gradeOptions[gradeIndex].label : '请选择等级' }}
|
||||||
|
</text>
|
||||||
|
<text class="picker-arrow">›</text>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 开始时间 -->
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="input-wrapper">
|
||||||
|
<text class="input-label">开始时间 <text class="optional">(选填)</text></text>
|
||||||
|
<picker
|
||||||
|
mode="date"
|
||||||
|
:value="startTimeDisplay"
|
||||||
|
@change="handleStartTimeChange"
|
||||||
|
>
|
||||||
|
<view class="picker-view">
|
||||||
|
<text :class="['picker-text', !startTimeDisplay ? 'placeholder' : '']">
|
||||||
|
{{ startTimeDisplay || '请选择开始时间' }}
|
||||||
|
</text>
|
||||||
|
<text class="picker-arrow">›</text>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 结束时间 -->
|
||||||
|
<view class="form-item">
|
||||||
|
<view class="input-wrapper">
|
||||||
|
<text class="input-label">结束时间 <text class="optional">(选填)</text></text>
|
||||||
|
<picker
|
||||||
|
mode="date"
|
||||||
|
:value="endTimeDisplay"
|
||||||
|
@change="handleEndTimeChange"
|
||||||
|
>
|
||||||
|
<view class="picker-view">
|
||||||
|
<text :class="['picker-text', !endTimeDisplay ? 'placeholder' : '']">
|
||||||
|
{{ endTimeDisplay || '请选择结束时间' }}
|
||||||
|
</text>
|
||||||
|
<text class="picker-arrow">›</text>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 提交按钮 -->
|
||||||
|
<view class="submit-section">
|
||||||
|
<button
|
||||||
|
class="submit-btn"
|
||||||
|
:class="{ disabled: loading }"
|
||||||
|
:disabled="loading"
|
||||||
|
@click="handleSubmit"
|
||||||
|
>
|
||||||
|
{{ loading ? '发布中...' : '发布' }}
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { createLaborUnionMessage } from '@/api/profile.js'
|
||||||
|
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
NavHeader
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
messageTypeIndex: -1,
|
||||||
|
messageTypeOptions: [
|
||||||
|
{ label: '类型1', value: 1 },
|
||||||
|
{ label: '类型2', value: 2 },
|
||||||
|
{ label: '类型3', value: 3 }
|
||||||
|
],
|
||||||
|
gradeIndex: -1,
|
||||||
|
gradeOptions: [
|
||||||
|
{ label: '普通', value: 0 },
|
||||||
|
{ label: '重要', value: 1 }
|
||||||
|
],
|
||||||
|
formData: {
|
||||||
|
title: '',
|
||||||
|
summary: '',
|
||||||
|
content: '',
|
||||||
|
coverUrl: '',
|
||||||
|
jumpUrl: '',
|
||||||
|
messageType: null,
|
||||||
|
grade: null,
|
||||||
|
startTime: '',
|
||||||
|
endTime: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 开始时间显示(从 ISO 格式提取日期部分)
|
||||||
|
startTimeDisplay() {
|
||||||
|
if (!this.formData.startTime) return ''
|
||||||
|
return this.formData.startTime.split('T')[0]
|
||||||
|
},
|
||||||
|
// 结束时间显示(从 ISO 格式提取日期部分)
|
||||||
|
endTimeDisplay() {
|
||||||
|
if (!this.formData.endTime) return ''
|
||||||
|
return this.formData.endTime.split('T')[0]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad() {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 选择封面图片
|
||||||
|
chooseCoverImage() {
|
||||||
|
uni.chooseImage({
|
||||||
|
count: 1,
|
||||||
|
sizeType: ['compressed'],
|
||||||
|
sourceType: ['album', 'camera'],
|
||||||
|
success: (res) => {
|
||||||
|
const tempFilePath = res.tempFilePaths[0]
|
||||||
|
// 先显示本地预览
|
||||||
|
this.formData.coverUrl = tempFilePath
|
||||||
|
// 上传图片到服务器
|
||||||
|
this.uploadImage(tempFilePath)
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('选择图片失败:', err)
|
||||||
|
uni.showToast({
|
||||||
|
title: '选择图片失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 上传图片
|
||||||
|
uploadImage(filePath) {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '上传中...',
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取token
|
||||||
|
const token = uni.getStorageSync('token')
|
||||||
|
const BASE_URL = 'https://siji.chenjuncn.top'
|
||||||
|
|
||||||
|
uni.uploadFile({
|
||||||
|
url: `${BASE_URL}/app-api/infra/file/upload`,
|
||||||
|
filePath: filePath,
|
||||||
|
name: 'file',
|
||||||
|
header: {
|
||||||
|
'Authorization': `Bearer ${token}`,
|
||||||
|
'tenant-id': '1'
|
||||||
|
},
|
||||||
|
success: (res) => {
|
||||||
|
uni.hideLoading()
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(res.data)
|
||||||
|
if (data.code === 200 || data.code === 0) {
|
||||||
|
// 上传成功,保存图片URL
|
||||||
|
const imageUrl = data.data?.url || data.data || data.url
|
||||||
|
if (imageUrl) {
|
||||||
|
this.formData.coverUrl = imageUrl
|
||||||
|
uni.showToast({
|
||||||
|
title: '上传成功',
|
||||||
|
icon: 'success',
|
||||||
|
duration: 1500
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
throw new Error('上传成功但未返回图片地址')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(data.message || data.msg || '上传失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解析上传结果失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '上传失败,请重试',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
// 上传失败,清除预览
|
||||||
|
this.formData.coverUrl = ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
uni.hideLoading()
|
||||||
|
console.error('上传图片失败:', err)
|
||||||
|
uni.showToast({
|
||||||
|
title: '上传失败,请检查网络',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
// 上传失败,清除预览
|
||||||
|
this.formData.coverUrl = ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 消息类型选择
|
||||||
|
handleMessageTypeChange(e) {
|
||||||
|
this.messageTypeIndex = e.detail.value
|
||||||
|
this.formData.messageType = this.messageTypeOptions[e.detail.value].value
|
||||||
|
},
|
||||||
|
|
||||||
|
// 等级选择
|
||||||
|
handleGradeChange(e) {
|
||||||
|
this.gradeIndex = e.detail.value
|
||||||
|
this.formData.grade = this.gradeOptions[e.detail.value].value
|
||||||
|
},
|
||||||
|
|
||||||
|
// 开始时间选择
|
||||||
|
handleStartTimeChange(e) {
|
||||||
|
// 将日期转换为 ISO 格式 (date-time)
|
||||||
|
const date = e.detail.value
|
||||||
|
this.formData.startTime = date ? `${date}T00:00:00` : ''
|
||||||
|
},
|
||||||
|
|
||||||
|
// 结束时间选择
|
||||||
|
handleEndTimeChange(e) {
|
||||||
|
// 将日期转换为 ISO 格式 (date-time)
|
||||||
|
const date = e.detail.value
|
||||||
|
this.formData.endTime = date ? `${date}T23:59:59` : ''
|
||||||
|
},
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
async handleSubmit() {
|
||||||
|
try {
|
||||||
|
this.loading = true
|
||||||
|
|
||||||
|
// 构建提交数据
|
||||||
|
const submitData = {
|
||||||
|
title: this.formData.title ? this.formData.title.trim() : '',
|
||||||
|
summary: this.formData.summary ? this.formData.summary.trim() : '',
|
||||||
|
content: this.formData.content ? this.formData.content.trim() : '',
|
||||||
|
coverUrl: this.formData.coverUrl || '',
|
||||||
|
jumpUrl: this.formData.jumpUrl ? this.formData.jumpUrl.trim() : '',
|
||||||
|
messageType: this.formData.messageType,
|
||||||
|
sourceType: 1, // 会员发布
|
||||||
|
grade: this.formData.grade,
|
||||||
|
startTime: this.formData.startTime || null,
|
||||||
|
endTime: this.formData.endTime || null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除空值
|
||||||
|
Object.keys(submitData).forEach(key => {
|
||||||
|
if (submitData[key] === '' || submitData[key] === null || submitData[key] === undefined) {
|
||||||
|
delete submitData[key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 调用接口
|
||||||
|
const res = await createLaborUnionMessage(submitData)
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: '发布成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 延迟返回上一页
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack()
|
||||||
|
}, 1500)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('发布消息失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: error.message || '发布失败,请重试',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.post-message-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #e2e8f1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-content {
|
||||||
|
flex: 1;
|
||||||
|
height: 0;
|
||||||
|
padding: 0 30rpx 40rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
margin-top: 30rpx;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333333;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
padding-left: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item {
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
|
||||||
|
.input-wrapper {
|
||||||
|
position: relative;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 30rpx 24rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
.input-label {
|
||||||
|
width: 160rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333333;
|
||||||
|
font-weight: 500;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.optional {
|
||||||
|
color: #999999;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #1a1819;
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-view {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.picker-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #1a1819;
|
||||||
|
|
||||||
|
&.placeholder {
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-arrow {
|
||||||
|
font-size: 40rpx;
|
||||||
|
color: #cccccc;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.textarea-wrapper {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 30rpx 24rpx;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
.textarea-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333333;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
|
||||||
|
.optional {
|
||||||
|
color: #999999;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.textarea-field {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 200rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #1a1819;
|
||||||
|
line-height: 1.6;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.char-count {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 16rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-wrapper {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 30rpx 24rpx;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
.upload-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333333;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
|
||||||
|
.optional {
|
||||||
|
color: #999999;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 400rpx;
|
||||||
|
border: 2rpx dashed #d0d0d0;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #fafafa;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.uploaded-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-placeholder {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 16rpx;
|
||||||
|
|
||||||
|
.upload-icon {
|
||||||
|
font-size: 60rpx;
|
||||||
|
color: #999999;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-section {
|
||||||
|
margin-top: 40rpx;
|
||||||
|
padding-bottom: 40rpx;
|
||||||
|
|
||||||
|
.submit-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 88rpx;
|
||||||
|
background: linear-gradient(135deg, #004294 0%, #0066cc 100%);
|
||||||
|
border-radius: 44rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
border: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
background: #cccccc;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="map-detail-page">
|
<view class="map-detail-page">
|
||||||
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
<!-- 顶部导航栏 -->
|
||||||
<NavHeader title="店铺位置" />
|
<NavHeader title="店铺位置" />
|
||||||
</view>
|
|
||||||
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
|
||||||
<!-- 地图区域 -->
|
<!-- 地图区域 -->
|
||||||
<view class="map-container" v-if="mapInitialized">
|
<view class="map-container" v-if="mapInitialized">
|
||||||
<map
|
<map
|
||||||
|
|
@ -86,7 +85,6 @@
|
||||||
<view class="loading-mask" v-if="loading">
|
<view class="loading-mask" v-if="loading">
|
||||||
<text class="loading-text">加载中...</text>
|
<text class="loading-text">加载中...</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -101,7 +99,6 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
statusBarHeight: 0,
|
|
||||||
shopId: null,
|
shopId: null,
|
||||||
storeInfo: {},
|
storeInfo: {},
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|
@ -121,9 +118,6 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
headerHeight() {
|
|
||||||
return this.statusBarHeight + 44;
|
|
||||||
},
|
|
||||||
// // 计算距离文本
|
// // 计算距离文本
|
||||||
// distanceText() {
|
// distanceText() {
|
||||||
// if (
|
// if (
|
||||||
|
|
@ -168,8 +162,6 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
const systemInfo = uni.getSystemInfoSync();
|
|
||||||
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
|
||||||
this.shopId = options.id;
|
this.shopId = options.id;
|
||||||
if (this.shopId) {
|
if (this.shopId) {
|
||||||
// 从存储中读取用户位置信息
|
// 从存储中读取用户位置信息
|
||||||
|
|
@ -429,22 +421,6 @@ export default {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-fixed-wrapper {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-wrap {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 地图容器 */
|
/* 地图容器 */
|
||||||
.map-container {
|
.map-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="rich-text-detail-page" :class="{ 'no-bottom-bar': hideBottomBar }">
|
<view class="rich-text-detail-page">
|
||||||
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
<!-- 顶部导航栏 -->
|
||||||
<NavHeader title="详情" />
|
<NavHeader title="详情" />
|
||||||
</view>
|
|
||||||
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
<!-- 内容区域 -->
|
||||||
<scroll-view class="content-scroll" scroll-y="true">
|
<scroll-view class="content-scroll" scroll-y="true">
|
||||||
<!-- 加载中 -->
|
<!-- 加载中 -->
|
||||||
<view class="loading-state" v-if="loading">
|
<view class="loading-state" v-if="loading">
|
||||||
|
|
@ -45,22 +45,20 @@
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 底部操作栏(从收藏进入时隐藏) -->
|
<!-- 底部操作栏 -->
|
||||||
<DetailActionBar
|
<DetailActionBar
|
||||||
v-if="detailData && !hideBottomBar"
|
v-if="detailData"
|
||||||
:liked="isLiked"
|
:liked="isLiked"
|
||||||
:collected="isCollected"
|
:collected="isCollected"
|
||||||
:id="detailId"
|
:id="detailId"
|
||||||
:zanType="noticeType"
|
:zanType="noticeType"
|
||||||
:type="noticeType"
|
:type="noticeType"
|
||||||
/>
|
/>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getGuildDetail } from "@/api/home.js";
|
import { getGuildDetail } from "@/api/home.js";
|
||||||
import { getBusinessData } from "@/api/profile.js";
|
|
||||||
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
||||||
import DetailActionBar from "@/components/DetailActionBar/DetailActionBar.vue";
|
import DetailActionBar from "@/components/DetailActionBar/DetailActionBar.vue";
|
||||||
|
|
||||||
|
|
@ -71,7 +69,6 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
statusBarHeight: 0,
|
|
||||||
detailId: null, // 详情ID(用于通过接口获取数据)
|
detailId: null, // 详情ID(用于通过接口获取数据)
|
||||||
title: "",
|
title: "",
|
||||||
content: "",
|
content: "",
|
||||||
|
|
@ -81,28 +78,13 @@ export default {
|
||||||
isCollected: false, // 是否已收藏
|
isCollected: false, // 是否已收藏
|
||||||
detailData: null, // 详情数据(通过接口获取时使用)
|
detailData: null, // 详情数据(通过接口获取时使用)
|
||||||
noticeType: null, // 内容类型
|
noticeType: null, // 内容类型
|
||||||
hideBottomBar: false, // 为 true 时不显示底部操作栏(如从收藏进入)
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
headerHeight() {
|
|
||||||
return this.statusBarHeight + 44;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
const systemInfo = uni.getSystemInfoSync();
|
// 支持两种模式:
|
||||||
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
// 1. 通过 id 调用接口获取详情(原 activitiesDetail 的用法)
|
||||||
// 支持三种模式:
|
// 2. 直接传入 title 和 content(原 richTextDetail 的用法)
|
||||||
// 1. 从收藏进入:collectId + collectType,用 getBusinessData 拉取详情
|
if (options.id) {
|
||||||
// 2. 通过 id 调用接口获取详情(原 activitiesDetail 的用法)
|
|
||||||
// 3. 直接传入 title 和 content(原 richTextDetail 的用法)
|
|
||||||
const collectId = options.collectId != null ? decodeURIComponent(String(options.collectId)) : null;
|
|
||||||
const collectType = options.collectType != null ? decodeURIComponent(String(options.collectType)) : null;
|
|
||||||
if (collectId != null && collectId !== "") {
|
|
||||||
this.detailId = collectId;
|
|
||||||
this.hideBottomBar = !!(options.hideBar || options.hideBar === "1");
|
|
||||||
this.loadDetailByBusinessData(collectId, collectType || "");
|
|
||||||
} else if (options.id) {
|
|
||||||
this.detailId = options.id;
|
this.detailId = options.id;
|
||||||
this.loadDetailById();
|
this.loadDetailById();
|
||||||
} else if (options.content) {
|
} else if (options.content) {
|
||||||
|
|
@ -166,35 +148,6 @@ export default {
|
||||||
},
|
},
|
||||||
// #endif
|
// #endif
|
||||||
methods: {
|
methods: {
|
||||||
// 判断 VO 是否有数据(有数据即已点赞/已收藏)
|
|
||||||
hasRespData(vo) {
|
|
||||||
if (vo == null) return false;
|
|
||||||
if (Array.isArray(vo)) return vo.length > 0;
|
|
||||||
if (typeof vo === "object") return Object.keys(vo).length > 0;
|
|
||||||
return !!vo;
|
|
||||||
},
|
|
||||||
// 通过收藏进入:getBusinessData(id: collectId, type: collectType) 拉取详情
|
|
||||||
async loadDetailByBusinessData(collectId, collectType) {
|
|
||||||
try {
|
|
||||||
this.loading = true;
|
|
||||||
const res = await getBusinessData({ id: collectId, type: collectType });
|
|
||||||
if (res) {
|
|
||||||
this.detailData = res;
|
|
||||||
this.noticeType = res.noticeType ?? res.messageType ?? res.type ?? collectType;
|
|
||||||
this.title = res.title || "详情";
|
|
||||||
if (res.content) {
|
|
||||||
this.parsedContent = this.parseHtmlContent(res.content);
|
|
||||||
}
|
|
||||||
this.isLiked = this.hasRespData(res.zanRespVO);
|
|
||||||
this.isCollected = this.hasRespData(res.collectRespVO);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("加载详情失败:", error);
|
|
||||||
uni.showToast({ title: "加载失败,请重试", icon: "none" });
|
|
||||||
} finally {
|
|
||||||
this.loading = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 通过 ID 加载详情(原 activitiesDetail 的功能)
|
// 通过 ID 加载详情(原 activitiesDetail 的功能)
|
||||||
async loadDetailById() {
|
async loadDetailById() {
|
||||||
if (!this.detailId) {
|
if (!this.detailId) {
|
||||||
|
|
@ -212,10 +165,13 @@ export default {
|
||||||
if (res.content) {
|
if (res.content) {
|
||||||
this.parsedContent = this.parseHtmlContent(res.content);
|
this.parsedContent = this.parseHtmlContent(res.content);
|
||||||
}
|
}
|
||||||
// 点赞状态:zanRespVO 有数据表示已点赞
|
// 设置点赞和收藏状态(如果接口返回了这些字段)
|
||||||
this.isLiked = this.hasRespData(res.zanRespVO);
|
if (res.isLiked !== undefined) {
|
||||||
// 收藏状态:collectRespVO 有数据表示已收藏
|
this.isLiked = res.isLiked;
|
||||||
this.isCollected = this.hasRespData(res.collectRespVO);
|
}
|
||||||
|
if (res.isCollected !== undefined) {
|
||||||
|
this.isCollected = res.isCollected;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("加载详情失败:", error);
|
console.error("加载详情失败:", error);
|
||||||
|
|
@ -298,28 +254,9 @@ export default {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding-bottom: 120rpx; // 为底部操作栏留出空间
|
padding-bottom: 120rpx; // 为底部操作栏留出空间
|
||||||
|
|
||||||
&.no-bottom-bar {
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-fixed-wrapper {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
|
||||||
background: #e2e8f1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-wrap {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 内容区域 */
|
||||||
.content-scroll {
|
.content-scroll {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0; // 配合 flex: 1 使用,让 scroll-view 可以滚动
|
height: 0; // 配合 flex: 1 使用,让 scroll-view 可以滚动
|
||||||
|
|
|
||||||
|
|
@ -1,253 +0,0 @@
|
||||||
<template>
|
|
||||||
<view class="select-coupon-page">
|
|
||||||
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
|
||||||
<NavHeader title="选择优惠卷" />
|
|
||||||
</view>
|
|
||||||
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
|
||||||
<scroll-view class="coupon-list" scroll-y="true">
|
|
||||||
<view
|
|
||||||
class="coupon-item"
|
|
||||||
v-for="(item, index) in coupons"
|
|
||||||
:key="index"
|
|
||||||
:class="{ disabled: !item.isApplicable }"
|
|
||||||
@click="selectCoupon(item)"
|
|
||||||
>
|
|
||||||
<view class="coupon-left">
|
|
||||||
<!-- 折扣类 -->
|
|
||||||
<view class="coupon-price" v-if="item.type === 2">
|
|
||||||
<text class="amount">{{ (item.discountPercent / 10).toFixed(1).replace(/\.0$/, '') }}</text>
|
|
||||||
<text class="symbol" style="font-size: 24rpx; margin-left: 4rpx;">折</text>
|
|
||||||
</view>
|
|
||||||
<!-- 金额类 -->
|
|
||||||
<view class="coupon-price" v-else>
|
|
||||||
<text class="symbol">¥</text>
|
|
||||||
<!-- 检查返回的字段是 discountPrice, discountAmount 还是抵扣金额相关字段,如果是金额类,应该有值 -->
|
|
||||||
<text class="amount">{{ formatAmount(item.discountAmount || item.discountPrice || item.price || 0) }}</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="coupon-condition" v-if="item.usePrice">
|
|
||||||
满{{ formatAmount(item.usePrice) }}可用
|
|
||||||
</view>
|
|
||||||
<view class="coupon-condition" v-else>
|
|
||||||
无门槛
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="coupon-right">
|
|
||||||
<view class="coupon-name">{{ item.name }}</view>
|
|
||||||
<view class="coupon-time" v-if="item.type === 2 && item.discountLimit > 0" style="margin-bottom: 6rpx;">最多抵扣: ¥{{ formatAmount(item.discountLimit) }}</view>
|
|
||||||
<view class="coupon-time" v-if="item.validEndTime">有效期至: {{ formatTimeStr(item.validEndTime) }}</view>
|
|
||||||
</view>
|
|
||||||
<view class="coupon-radio">
|
|
||||||
<radio :checked="selectedCouponId === item.id" :disabled="!item.isApplicable" color="#d51c3c" style="transform:scale(0.8)" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view v-if="coupons.length === 0" class="empty-tip">暂无优惠卷</view>
|
|
||||||
</scroll-view>
|
|
||||||
<!-- 不使用优惠卷按钮 -->
|
|
||||||
<view class="bottom-bar">
|
|
||||||
<button class="no-coupon-btn" @click="selectNone">不使用优惠卷</button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
|
||||||
import { formatTime } from "@/utils/date.js";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
NavHeader,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
statusBarHeight: 0,
|
|
||||||
coupons: [],
|
|
||||||
selectedCouponId: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
headerHeight() {
|
|
||||||
return this.statusBarHeight + 44;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
onLoad() {
|
|
||||||
const systemInfo = uni.getSystemInfoSync();
|
|
||||||
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
|
||||||
|
|
||||||
// 获取传递过来的数据
|
|
||||||
const eventChannel = this.getOpenerEventChannel();
|
|
||||||
if (eventChannel && eventChannel.on) {
|
|
||||||
eventChannel.on('acceptDataFromOpenerPage', (data) => {
|
|
||||||
this.coupons = data.coupons || [];
|
|
||||||
this.selectedCouponId = data.selectedCouponId || null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
selectCoupon(item) {
|
|
||||||
if (!item.isApplicable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.selectedCouponId = item.id;
|
|
||||||
this.confirmSelection(item);
|
|
||||||
},
|
|
||||||
selectNone() {
|
|
||||||
this.selectedCouponId = null;
|
|
||||||
this.confirmSelection(null);
|
|
||||||
},
|
|
||||||
confirmSelection(coupon) {
|
|
||||||
const eventChannel = this.getOpenerEventChannel();
|
|
||||||
if (eventChannel && eventChannel.emit) {
|
|
||||||
eventChannel.emit('acceptDataFromOpenedPage', { coupon });
|
|
||||||
}
|
|
||||||
uni.navigateBack();
|
|
||||||
},
|
|
||||||
formatTimeStr(timestamp) {
|
|
||||||
if (!timestamp) return '';
|
|
||||||
return formatTime(timestamp, 'YYYY-MM-DD HH:mm:ss');
|
|
||||||
},
|
|
||||||
// 将分转换为元,并处理小数显示
|
|
||||||
formatAmount(amount) {
|
|
||||||
if (!amount) return '0';
|
|
||||||
const yuan = amount / 100;
|
|
||||||
// 如果是整数,直接显示整数;如果有小数,保留两位小数但去掉末尾的0
|
|
||||||
return Number.isInteger(yuan) ? yuan.toString() : yuan.toFixed(2).replace(/\.?0+$/, '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.select-coupon-page {
|
|
||||||
min-height: 100vh;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-fixed-wrapper {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
z-index: 100;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-wrap {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100vh;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-list {
|
|
||||||
flex: 1;
|
|
||||||
padding: 20rpx;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-item {
|
|
||||||
display: flex;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 16rpx;
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
padding: 30rpx;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
opacity: 0.6;
|
|
||||||
background-color: #fafafa;
|
|
||||||
|
|
||||||
.coupon-price {
|
|
||||||
color: #999 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-name {
|
|
||||||
color: #999 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-left {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 160rpx;
|
|
||||||
border-right: 2rpx dashed #eee;
|
|
||||||
padding-right: 20rpx;
|
|
||||||
|
|
||||||
.coupon-price {
|
|
||||||
color: #d51c3c;
|
|
||||||
|
|
||||||
.symbol {
|
|
||||||
font-size: 24rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.amount {
|
|
||||||
font-size: 48rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-condition {
|
|
||||||
font-size: 20rpx;
|
|
||||||
color: #666;
|
|
||||||
margin-top: 10rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-right {
|
|
||||||
flex: 1;
|
|
||||||
padding-left: 30rpx;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.coupon-name {
|
|
||||||
font-size: 28rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 10rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-time {
|
|
||||||
font-size: 22rpx;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-radio {
|
|
||||||
margin-left: 20rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-tip {
|
|
||||||
text-align: center;
|
|
||||||
padding: 60rpx 0;
|
|
||||||
color: #999;
|
|
||||||
font-size: 28rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom-bar {
|
|
||||||
padding: 20rpx 40rpx calc(20rpx + env(safe-area-inset-bottom));
|
|
||||||
background-color: #fff;
|
|
||||||
border-top: 1rpx solid #eee;
|
|
||||||
|
|
||||||
.no-coupon-btn {
|
|
||||||
width: 100%;
|
|
||||||
height: 88rpx;
|
|
||||||
line-height: 88rpx;
|
|
||||||
text-align: center;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
color: #333;
|
|
||||||
font-size: 32rpx;
|
|
||||||
border-radius: 44rpx;
|
|
||||||
border: none;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="store-page">
|
<view class="store-page">
|
||||||
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
<!-- 顶部导航栏 -->
|
||||||
<NavHeader title="店铺详情" />
|
<NavHeader title="店铺详情" />
|
||||||
</view>
|
|
||||||
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
|
||||||
<!-- 顶部店铺信息(点击进入地图页) -->
|
<!-- 顶部店铺信息(点击进入地图页) -->
|
||||||
<view class="store-header" @click="handleGoMap">
|
<view class="store-header" @click="handleGoMap">
|
||||||
<view class="store-brand">
|
<view class="store-brand">
|
||||||
|
|
@ -42,15 +41,10 @@
|
||||||
|
|
||||||
<!-- 中间菜单列表(可滑动) -->
|
<!-- 中间菜单列表(可滑动) -->
|
||||||
<scroll-view class="menu-list" scroll-y="true">
|
<scroll-view class="menu-list" scroll-y="true">
|
||||||
<view
|
<view class="menu-item" v-for="(item, index) in menuList" :key="index">
|
||||||
class="menu-item"
|
|
||||||
v-for="(item, index) in menuList"
|
|
||||||
:key="index"
|
|
||||||
:class="{ 'out-of-stock': item.stock === 0 }"
|
|
||||||
>
|
|
||||||
<view
|
<view
|
||||||
class="checkbox"
|
class="checkbox"
|
||||||
:class="{ checked: item.selected, disabled: item.stock === 0 }"
|
:class="{ checked: item.selected }"
|
||||||
@click="toggleMenuItem(index)"
|
@click="toggleMenuItem(index)"
|
||||||
>
|
>
|
||||||
<text v-if="item.selected" class="checkmark">✓</text>
|
<text v-if="item.selected" class="checkmark">✓</text>
|
||||||
|
|
@ -80,21 +74,17 @@
|
||||||
<view
|
<view
|
||||||
class="quantity-btn minus"
|
class="quantity-btn minus"
|
||||||
:class="{
|
:class="{
|
||||||
disabled: (item.quantity || 0) <= 0 || item.stock === 0,
|
disabled: (item.quantity || 0) <= 0,
|
||||||
}"
|
}"
|
||||||
@click="decreaseQuantity(index)"
|
@click="decreaseQuantity(index)"
|
||||||
>-</view
|
>-</view
|
||||||
>
|
>
|
||||||
<text class="quantity-number">{{ item.quantity || 0 }}</text>
|
<text class="quantity-number">{{ item.quantity || 0 }}</text>
|
||||||
<view
|
<view class="quantity-btn plus" @click="increaseQuantity(index)"
|
||||||
class="quantity-btn plus"
|
|
||||||
:class="{ disabled: item.stock === 0 }"
|
|
||||||
@click="increaseQuantity(index)"
|
|
||||||
>+</view
|
>+</view
|
||||||
>
|
>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view v-if="item.stock === 0" class="stock-tip">库存不足,不可选择</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
@ -102,8 +92,8 @@
|
||||||
<!-- 底部结算栏(固定) -->
|
<!-- 底部结算栏(固定) -->
|
||||||
<view class="checkout-footer">
|
<view class="checkout-footer">
|
||||||
<view class="total-info">
|
<view class="total-info">
|
||||||
<text class="total-label">应付¥</text>
|
<text class="total-label">总金额¥</text>
|
||||||
<text class="total-amount">{{ payableAmountYuan.toFixed(2) }}</text>
|
<text class="total-amount">{{ totalAmount.toFixed(2) }}</text>
|
||||||
<view class="member-benefit">
|
<view class="member-benefit">
|
||||||
<image
|
<image
|
||||||
class="crown-icon"
|
class="crown-icon"
|
||||||
|
|
@ -113,15 +103,6 @@
|
||||||
<text class="benefit-text">{{ memberLevelName }}优惠</text>
|
<text class="benefit-text">{{ memberLevelName }}优惠</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="coupon-brief" @click="openCouponSelect">
|
|
||||||
<image class="coupon-icon" src="/static/service/coupon-icon.png" mode="aspectFill"></image>
|
|
||||||
<text class="coupon-text">{{ selectedCoupon ? selectedCoupon.name : '选择优惠卷' }}</text>
|
|
||||||
<text class="coupon-deduction" v-if="selectedCoupon && couponDiscountFen > 0">-¥{{ (couponDiscountFen / 100).toFixed(2) }}</text>
|
|
||||||
<text class="coupon-arrow">›</text>
|
|
||||||
</view>
|
|
||||||
<view class="settle-info">
|
|
||||||
<text class="settle-text">资金结算方:{{ storeInfo.name || '商户' }}</text>
|
|
||||||
</view>
|
|
||||||
<button
|
<button
|
||||||
class="checkout-btn"
|
class="checkout-btn"
|
||||||
:class="{ disabled: totalAmount <= 0 }"
|
:class="{ disabled: totalAmount <= 0 }"
|
||||||
|
|
@ -131,17 +112,15 @@
|
||||||
去结算
|
去结算
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {
|
import {
|
||||||
getGuildStoreDetail,
|
getGuildStoreDetail,
|
||||||
getGuildVoucher,
|
getGuildCoupon,
|
||||||
getPaySign,
|
getPaySign,
|
||||||
appBuy,
|
appBuy
|
||||||
getLuCouponPage
|
|
||||||
} from "@/api/service";
|
} from "@/api/service";
|
||||||
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
||||||
|
|
||||||
|
|
@ -151,7 +130,6 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
statusBarHeight: 0,
|
|
||||||
storeInfo: {},
|
storeInfo: {},
|
||||||
menuList: [],
|
menuList: [],
|
||||||
categoryLabel: "",
|
categoryLabel: "",
|
||||||
|
|
@ -159,16 +137,9 @@ export default {
|
||||||
userInfo: {},
|
userInfo: {},
|
||||||
distance: null,
|
distance: null,
|
||||||
paying: false,
|
paying: false,
|
||||||
coupons: [],
|
|
||||||
selectedCoupon: null,
|
|
||||||
selectedCouponId: null,
|
|
||||||
couponLoading: false,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
headerHeight() {
|
|
||||||
return this.statusBarHeight + 44;
|
|
||||||
},
|
|
||||||
// 计算总金额(只计算选中且数量大于0的商品)
|
// 计算总金额(只计算选中且数量大于0的商品)
|
||||||
totalAmount() {
|
totalAmount() {
|
||||||
return this.menuList.reduce((total, item) => {
|
return this.menuList.reduce((total, item) => {
|
||||||
|
|
@ -180,36 +151,6 @@ export default {
|
||||||
return total;
|
return total;
|
||||||
}, 0);
|
}, 0);
|
||||||
},
|
},
|
||||||
totalAmountFen() {
|
|
||||||
return Math.round(this.totalAmount * 100);
|
|
||||||
},
|
|
||||||
couponDiscountFen() {
|
|
||||||
if (!this.selectedCoupon) return 0;
|
|
||||||
const now = Date.now();
|
|
||||||
if ((this.selectedCoupon.validStartTime && now < this.selectedCoupon.validStartTime) ||
|
|
||||||
(this.selectedCoupon.validEndTime && now > this.selectedCoupon.validEndTime)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (!this.isCouponApplicable(this.selectedCoupon)) return 0;
|
|
||||||
const totalFen = this.totalAmountFen;
|
|
||||||
if (this.selectedCoupon.type === 2) {
|
|
||||||
const percent = this.selectedCoupon.discountPercent || 0;
|
|
||||||
const raw = Math.round(totalFen * (1 - percent / 100));
|
|
||||||
const limit = this.selectedCoupon.discountLimit || 0;
|
|
||||||
const val = limit > 0 ? Math.min(raw, limit) : raw;
|
|
||||||
return Math.max(0, val);
|
|
||||||
}
|
|
||||||
const amt = this.selectedCoupon.discountAmount || this.selectedCoupon.discountPrice || this.selectedCoupon.price || 0;
|
|
||||||
return Math.max(0, Math.min(amt, totalFen));
|
|
||||||
},
|
|
||||||
payableAmountFen() {
|
|
||||||
const val = this.totalAmountFen - this.couponDiscountFen;
|
|
||||||
if (val <= 0 && this.totalAmountFen > 0) return 1;
|
|
||||||
return Math.max(0, val);
|
|
||||||
},
|
|
||||||
payableAmountYuan() {
|
|
||||||
return this.payableAmountFen / 100;
|
|
||||||
},
|
|
||||||
// 是否有会员优惠
|
// 是否有会员优惠
|
||||||
hasMemberDiscount() {
|
hasMemberDiscount() {
|
||||||
return this.menuList.some((item) => item.selected && item.discount);
|
return this.menuList.some((item) => item.selected && item.discount);
|
||||||
|
|
@ -223,8 +164,7 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
const systemInfo = uni.getSystemInfoSync();
|
console.log(options, 111110);
|
||||||
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
|
||||||
// 从路由参数获取店铺ID和分类标签
|
// 从路由参数获取店铺ID和分类标签
|
||||||
this.shopId = options.id;
|
this.shopId = options.id;
|
||||||
this.categoryLabel = options.categoryLabel;
|
this.categoryLabel = options.categoryLabel;
|
||||||
|
|
@ -309,10 +249,10 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 加载工会代金券
|
// 加载工会优惠券
|
||||||
async loadStoreMenu(shopId) {
|
async loadStoreMenu(shopId) {
|
||||||
try {
|
try {
|
||||||
const res = await getGuildVoucher({ shopId });
|
const res = await getGuildCoupon({ shopId });
|
||||||
console.log(res, 11119);
|
console.log(res, 11119);
|
||||||
if (res && res.list) {
|
if (res && res.list) {
|
||||||
this.menuList = res.list;
|
this.menuList = res.list;
|
||||||
|
|
@ -328,10 +268,6 @@ export default {
|
||||||
// 切换菜单项选择状态
|
// 切换菜单项选择状态
|
||||||
toggleMenuItem(index) {
|
toggleMenuItem(index) {
|
||||||
const item = this.menuList[index];
|
const item = this.menuList[index];
|
||||||
// 库存不足时不可选择
|
|
||||||
if (item && item.stock === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
item.selected = !item.selected;
|
item.selected = !item.selected;
|
||||||
if (!item.selected) {
|
if (!item.selected) {
|
||||||
item.quantity = 0;
|
item.quantity = 0;
|
||||||
|
|
@ -339,32 +275,22 @@ export default {
|
||||||
// 选中时,如果当前数量为 0 或未设置,则默认数量为 1
|
// 选中时,如果当前数量为 0 或未设置,则默认数量为 1
|
||||||
item.quantity = 1;
|
item.quantity = 1;
|
||||||
}
|
}
|
||||||
this.autoSelectBestCoupon();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 增加数量
|
// 增加数量
|
||||||
increaseQuantity(index) {
|
increaseQuantity(index) {
|
||||||
const item = this.menuList[index];
|
const item = this.menuList[index];
|
||||||
// 库存不足时不可增加数量
|
|
||||||
if (item && item.stock === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 如果未选中,先选中
|
// 如果未选中,先选中
|
||||||
if (!item.selected) {
|
if (!item.selected) {
|
||||||
item.selected = true;
|
item.selected = true;
|
||||||
}
|
}
|
||||||
// 增加数量
|
// 增加数量
|
||||||
item.quantity = (item.quantity || 0) + 1;
|
item.quantity = (item.quantity || 0) + 1;
|
||||||
this.autoSelectBestCoupon();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 减少数量
|
// 减少数量
|
||||||
decreaseQuantity(index) {
|
decreaseQuantity(index) {
|
||||||
const item = this.menuList[index];
|
const item = this.menuList[index];
|
||||||
// 库存不足时不处理数量变化
|
|
||||||
if (item && item.stock === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const currentQuantity = item.quantity || 0;
|
const currentQuantity = item.quantity || 0;
|
||||||
// 确保数量不能小于0
|
// 确保数量不能小于0
|
||||||
if (currentQuantity > 0) {
|
if (currentQuantity > 0) {
|
||||||
|
|
@ -374,7 +300,6 @@ export default {
|
||||||
item.selected = false;
|
item.selected = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.autoSelectBestCoupon();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 格式化价格:将分转换为元(除以100,保留两位小数)
|
// 格式化价格:将分转换为元(除以100,保留两位小数)
|
||||||
|
|
@ -388,94 +313,6 @@ export default {
|
||||||
return yuan.toFixed(2);
|
return yuan.toFixed(2);
|
||||||
},
|
},
|
||||||
|
|
||||||
async openCouponSelect() {
|
|
||||||
if (this.totalAmountFen <= 0) {
|
|
||||||
uni.showToast({ title: "请先选择商品", icon: "none" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await this.loadUserCoupons();
|
|
||||||
const eventChannelData = {
|
|
||||||
coupons: this.coupons,
|
|
||||||
selectedCouponId: this.selectedCouponId,
|
|
||||||
};
|
|
||||||
uni.navigateTo({
|
|
||||||
url: "/pages/detail/selectCoupon",
|
|
||||||
success: (res) => {
|
|
||||||
res.eventChannel.emit("acceptDataFromOpenerPage", eventChannelData);
|
|
||||||
res.eventChannel.on("acceptDataFromOpenedPage", ({ coupon }) => {
|
|
||||||
this.selectedCoupon = coupon;
|
|
||||||
this.selectedCouponId = coupon ? coupon.id : null;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async loadUserCoupons() {
|
|
||||||
if (this.couponLoading) return;
|
|
||||||
this.couponLoading = true;
|
|
||||||
try {
|
|
||||||
const res = await getLuCouponPage({ pageNo: 1, pageSize: 100, status: 0 });
|
|
||||||
const list = (res && res.list) || [];
|
|
||||||
const enhanced = list.map((c) => {
|
|
||||||
const copy = { ...c };
|
|
||||||
copy.isApplicable = this.isCouponApplicable(copy);
|
|
||||||
return copy;
|
|
||||||
});
|
|
||||||
this.coupons = enhanced;
|
|
||||||
if (!this.selectedCoupon && enhanced.length > 0) {
|
|
||||||
this.autoSelectBestCoupon();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.couponLoading = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isCouponApplicable(coupon) {
|
|
||||||
const now = Date.now();
|
|
||||||
if (coupon.status !== 0) return false;
|
|
||||||
if (coupon.validStartTime && now < coupon.validStartTime) return false;
|
|
||||||
if (coupon.validEndTime && now > coupon.validEndTime) return false;
|
|
||||||
const totalFen = this.totalAmountFen;
|
|
||||||
if (coupon.usePrice && totalFen < coupon.usePrice) return false;
|
|
||||||
const selectedIds = this.menuList.filter(i => i.selected && i.quantity > 0).map(i => i.id);
|
|
||||||
if (selectedIds.length === 0) return false;
|
|
||||||
if (coupon.productScope === 1) return true;
|
|
||||||
if (coupon.productScope === 2) {
|
|
||||||
const ids = Array.isArray(coupon.voucherIds)
|
|
||||||
? coupon.voucherIds
|
|
||||||
: (typeof coupon.voucherIds === "string" ? coupon.voucherIds.split(",").map(s => Number(s.trim())).filter(Boolean) : []);
|
|
||||||
return selectedIds.some(id => ids.includes(id));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
autoSelectBestCoupon() {
|
|
||||||
if (!this.coupons || this.coupons.length === 0) return;
|
|
||||||
const applicable = this.coupons.filter(c => this.isCouponApplicable(c));
|
|
||||||
if (applicable.length === 0) {
|
|
||||||
this.selectedCoupon = null;
|
|
||||||
this.selectedCouponId = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let best = null;
|
|
||||||
let maxDeduct = -1;
|
|
||||||
applicable.forEach(c => {
|
|
||||||
const deduct = (() => {
|
|
||||||
if (c.type === 2) {
|
|
||||||
const percent = c.discountPercent || 0;
|
|
||||||
const raw = Math.round(this.totalAmountFen * (1 - percent / 100));
|
|
||||||
const limit = c.discountLimit || 0;
|
|
||||||
return Math.max(0, limit > 0 ? Math.min(raw, limit) : raw);
|
|
||||||
}
|
|
||||||
return Math.max(0, Math.min(c.discountAmount || c.discountPrice || c.price || 0, this.totalAmountFen));
|
|
||||||
})();
|
|
||||||
if (deduct > maxDeduct) {
|
|
||||||
maxDeduct = deduct;
|
|
||||||
best = c;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.selectedCoupon = best || null;
|
|
||||||
this.selectedCouponId = best ? best.id : null;
|
|
||||||
this.coupons = this.coupons.map(c => ({ ...c, isApplicable: this.isCouponApplicable(c) }));
|
|
||||||
},
|
|
||||||
|
|
||||||
// 去结算
|
// 去结算
|
||||||
async handleCheckout() {
|
async handleCheckout() {
|
||||||
if (this.totalAmount <= 0) {
|
if (this.totalAmount <= 0) {
|
||||||
|
|
@ -495,21 +332,20 @@ export default {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
// 把选中的数据拼接成这样的数据 voucherId:num;voucherId:num 例如 2324:1;2325:2
|
// 把选中的数据拼接成这样的数据 couponId:num;couponId:num 例如 2324:1;2325:2
|
||||||
const voucherStr = selectedItems
|
const couponStr = selectedItems
|
||||||
.map((item) => `${item.id}:${item.quantity}`)
|
.map((item) => `${item.id}:${item.quantity}`)
|
||||||
.join(";");
|
.join(";");
|
||||||
|
|
||||||
const trxamtFen = this.payableAmountFen;
|
// this.totalAmount 是元, 要转换成分
|
||||||
const trxamt = String(trxamtFen);
|
const trxamt = (this.totalAmount * 100).toFixed(0);
|
||||||
|
|
||||||
console.log("购买信息: " + voucherStr);
|
console.log("购买信息: " + couponStr);
|
||||||
console.log("金额:" + trxamt);
|
console.log("金额:" + trxamt);
|
||||||
const res = await appBuy({
|
const res = await appBuy({
|
||||||
shopId: this.shopId,
|
shopId: this.shopId,
|
||||||
voucherData: voucherStr,
|
couponData: couponStr,
|
||||||
payableAmount: trxamt,
|
payableAmount: trxamt,
|
||||||
couponId: this.selectedCouponId || undefined,
|
|
||||||
});
|
});
|
||||||
console.log(res);
|
console.log(res);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
|
|
@ -519,15 +355,7 @@ export default {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 调用支付信息 res 为订单信息
|
// 调用支付信息 res 为订单信息
|
||||||
uni.showModal({
|
this.handlePay(res);
|
||||||
title: "确认结算",
|
|
||||||
content: `将跳转至商户收款小程序完成结算。\n资金直接结算至:${this.storeInfo.name || '商户'}(商户号:${res.tlPayCusid || '已配置'})。是否继续?`,
|
|
||||||
success: (mres) => {
|
|
||||||
if (mres.confirm) {
|
|
||||||
this.handlePay(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
// 支付调用
|
// 支付调用
|
||||||
async handlePay(order) {
|
async handlePay(order) {
|
||||||
|
|
@ -535,43 +363,38 @@ export default {
|
||||||
// const reqsn =
|
// const reqsn =
|
||||||
// "mini" + Date.now() + "" + Math.floor(Math.random() * 10000);
|
// "mini" + Date.now() + "" + Math.floor(Math.random() * 10000);
|
||||||
|
|
||||||
const voucherArray = order.voucherPurchaseRespVOS || [];
|
const couponArray = order.couponPurchaseRespVOS || [];
|
||||||
const voucherMap = new Map();
|
const couponMap = new Map();
|
||||||
|
|
||||||
voucherArray.forEach((item) => {
|
couponArray.forEach((item) => {
|
||||||
const key = item.voucherId;
|
const key = item.couponId;
|
||||||
if (!key) return;
|
if (!key) return;
|
||||||
|
|
||||||
// 从 Map 中获取已有数组,没有则取空数组
|
// 从 Map 中获取已有数组,没有则取空数组
|
||||||
const currentList = voucherMap.get(key) || [];
|
const currentList = couponMap.get(key) || [];
|
||||||
currentList.push(item);
|
currentList.push(item);
|
||||||
// 更新 Map 中的值
|
// 更新 Map 中的值
|
||||||
voucherMap.set(key, currentList);
|
couponMap.set(key, currentList);
|
||||||
});
|
});
|
||||||
let bodyStr = ''
|
let bodyStr = ''
|
||||||
voucherMap.forEach((voucherList, voucherId) => {
|
couponMap.forEach((couponList, couponId) => {
|
||||||
console.log(`代金券ID:${voucherId}`);
|
console.log(`优惠券ID:${couponId}`);
|
||||||
console.log(`代金券列表:`, voucherList);
|
console.log(`优惠券列表:`, couponList);
|
||||||
const name = voucherList[0] && voucherList[0].voucherName ? voucherList[0].voucherName : "商品";
|
const name = couponList[0] && couponList[0].couponName ? couponList[0].couponName : "商品";
|
||||||
bodyStr = bodyStr + name + 'x' + voucherList.length + ';';
|
bodyStr = bodyStr + name + 'x' + couponList.length + ';';
|
||||||
});
|
});
|
||||||
|
|
||||||
const randomstr = Math.floor(Math.random() * 10000000) + "";
|
const randomstr = Math.floor(Math.random() * 10000000) + "";
|
||||||
// 金额:优先用订单实付金额(分),无则用 "1" 测试
|
// 金额:优先用订单实付金额(分),无则用 "1" 测试
|
||||||
if (order.payableAmount == null || order.payableAmount === ""){
|
const trxamt =
|
||||||
uni.showToast({ title: "结算金额异常", icon: "none" });
|
order.payableAmount != null && order.payableAmount !== ""
|
||||||
return;
|
? String(order.payableAmount)
|
||||||
}
|
: "1";
|
||||||
if (order.tlPayCusid == null || order.tlPayCusid == ""){
|
// 定义请求参数(与服务记录页「立即支付」保持一致)
|
||||||
uni.showToast({ title: "当前商户还未配置结算信息不可购买", icon: "none" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定义请求参数(与服务记录页「去结算」保持一致)
|
|
||||||
let params = {
|
let params = {
|
||||||
appid: "00390105", // 通联分配的appid
|
appid: "00390105", // 平台分配的appid
|
||||||
body: bodyStr, //
|
body: bodyStr, //
|
||||||
cusid: order.tlPayCusid, // 通联分配的商户号
|
cusid: order.tlPayCusid, // 平台分配的商户号
|
||||||
notify_url:
|
notify_url:
|
||||||
"https://guangsh.manage.hschengtai.com/admin-api/member/lu-order/tlNotice", // 保持原值不变
|
"https://guangsh.manage.hschengtai.com/admin-api/member/lu-order/tlNotice", // 保持原值不变
|
||||||
orgid: "56479107392N35H",
|
orgid: "56479107392N35H",
|
||||||
|
|
@ -582,7 +405,7 @@ export default {
|
||||||
reqsn: order.orderNumber,
|
reqsn: order.orderNumber,
|
||||||
sign: "",
|
sign: "",
|
||||||
signtype: "RSA",
|
signtype: "RSA",
|
||||||
trxamt: String(order.payableAmount),
|
trxamt: "1",
|
||||||
version: "12",
|
version: "12",
|
||||||
};
|
};
|
||||||
console.log(params)
|
console.log(params)
|
||||||
|
|
@ -605,8 +428,8 @@ export default {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("获取签名失败:", e);
|
console.error("获取支付签名失败:", e);
|
||||||
uni.showToast({ title: "结算准备失败,请稍后重试", icon: "none" });
|
uni.showToast({ title: "支付准备失败,请稍后重试", icon: "none" });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -622,22 +445,6 @@ export default {
|
||||||
padding-bottom: 120rpx; // 为底部结算栏留出空间
|
padding-bottom: 120rpx; // 为底部结算栏留出空间
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-fixed-wrapper {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
|
||||||
background: #e2e8f1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-wrap {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 顶部店铺信息 */
|
/* 顶部店铺信息 */
|
||||||
.store-header {
|
.store-header {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
|
|
@ -748,10 +555,6 @@ export default {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&.out-of-stock {
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox {
|
.checkbox {
|
||||||
width: 30rpx;
|
width: 30rpx;
|
||||||
height: 30rpx;
|
height: 30rpx;
|
||||||
|
|
@ -763,11 +566,6 @@ export default {
|
||||||
margin-right: 19rpx;
|
margin-right: 19rpx;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
border-color: #dddddd;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.checked {
|
&.checked {
|
||||||
background-color: #004294;
|
background-color: #004294;
|
||||||
border-color: #004294;
|
border-color: #004294;
|
||||||
|
|
@ -872,12 +670,6 @@ export default {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border-radius: 4rpx;
|
border-radius: 4rpx;
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
color: #bbbbbb;
|
|
||||||
border-color: #dddddd;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.quantity-number {
|
.quantity-number {
|
||||||
|
|
@ -890,11 +682,6 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.stock-tip {
|
|
||||||
margin-top: 10rpx;
|
|
||||||
font-size: 20rpx;
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -916,8 +703,6 @@ export default {
|
||||||
.total-info {
|
.total-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
position: relative;
|
|
||||||
top: -15rpx;
|
|
||||||
|
|
||||||
.total-label {
|
.total-label {
|
||||||
font-family: PingFang-SC, PingFang-SC;
|
font-family: PingFang-SC, PingFang-SC;
|
||||||
|
|
@ -941,7 +726,6 @@ export default {
|
||||||
background: rgba(255, 107, 0, 0.1);
|
background: rgba(255, 107, 0, 0.1);
|
||||||
padding: 4rpx 12rpx;
|
padding: 4rpx 12rpx;
|
||||||
border-radius: 8rpx;
|
border-radius: 8rpx;
|
||||||
margin-top: -4rpx;
|
|
||||||
|
|
||||||
.crown-icon {
|
.crown-icon {
|
||||||
width: 23rpx;
|
width: 23rpx;
|
||||||
|
|
@ -957,50 +741,6 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.coupon-brief {
|
|
||||||
position: absolute;
|
|
||||||
left: 20rpx;
|
|
||||||
top: -68rpx;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 8rpx 12rpx;
|
|
||||||
background: #fff7f8;
|
|
||||||
border: 1rpx dashed #ffd4d8;
|
|
||||||
border-radius: 10rpx;
|
|
||||||
color: #d51c3c;
|
|
||||||
}
|
|
||||||
.settle-info {
|
|
||||||
position: absolute;
|
|
||||||
left: 20rpx;
|
|
||||||
bottom: 10rpx;
|
|
||||||
}
|
|
||||||
.settle-text {
|
|
||||||
font-size: 22rpx;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
.coupon-icon {
|
|
||||||
width: 24rpx;
|
|
||||||
height: 24rpx;
|
|
||||||
margin-right: 8rpx;
|
|
||||||
}
|
|
||||||
.coupon-text {
|
|
||||||
font-size: 24rpx;
|
|
||||||
margin-right: 8rpx;
|
|
||||||
max-width: 280rpx;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
.coupon-deduction {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #d51c3c;
|
|
||||||
margin-right: 8rpx;
|
|
||||||
}
|
|
||||||
.coupon-arrow {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #d51c3c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkout-btn {
|
.checkout-btn {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-family: PingFang-SC, PingFang-SC;
|
font-family: PingFang-SC, PingFang-SC;
|
||||||
|
|
@ -1014,4 +754,4 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
@ -1,15 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="home-page">
|
<view class="home-page">
|
||||||
<!-- 应用头部(固定,滑动时不动) -->
|
<!-- 应用头部 -->
|
||||||
<view
|
<view class="header" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||||
class="header header-fixed"
|
|
||||||
:style="{ paddingTop: statusBarHeight + 'px' }"
|
|
||||||
>
|
|
||||||
<image class="logo" src="/static/home/logo.png" mode="aspectFit"></image>
|
<image class="logo" src="/static/home/logo.png" mode="aspectFit"></image>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 主体内容(预留头部高度,避免被遮挡) -->
|
|
||||||
<view class="main-content" :style="{ paddingTop: headerHeight + 'px' }">
|
|
||||||
<!-- 招募宣传横幅 -->
|
<!-- 招募宣传横幅 -->
|
||||||
<swiper
|
<swiper
|
||||||
class="recruit-banner"
|
class="recruit-banner"
|
||||||
|
|
@ -44,7 +39,8 @@
|
||||||
></image>
|
></image>
|
||||||
<text class="title-text">公会福利</text>
|
<text class="title-text">公会福利</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="section-more" @click="handleViewAllBenefits" v-html="'查看全部 >'">
|
<view class="section-more" @click="handleViewAllBenefits">
|
||||||
|
查看全部 >
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="benefits-list">
|
<view class="benefits-list">
|
||||||
|
|
@ -71,7 +67,8 @@
|
||||||
></image>
|
></image>
|
||||||
<text class="title-text">公会活动</text>
|
<text class="title-text">公会活动</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="section-more" @click="handleViewAllActivities" v-html="'查看全部 >'">
|
<view class="section-more" @click="handleViewAllActivities">
|
||||||
|
查看全部 >
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="activities-list">
|
<view class="activities-list">
|
||||||
|
|
@ -104,7 +101,6 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -125,8 +121,6 @@ export default {
|
||||||
statusBarHeight: 0,
|
statusBarHeight: 0,
|
||||||
// 导航栏高度(状态栏 + 导航栏内容)
|
// 导航栏高度(状态栏 + 导航栏内容)
|
||||||
navBarHeight: 0,
|
navBarHeight: 0,
|
||||||
// 头部总高度(用于主体 padding-top,约 72rpx ≈ 36px)
|
|
||||||
headerContentHeight: 44,
|
|
||||||
bannerList: [],
|
bannerList: [],
|
||||||
// 公会福利列表
|
// 公会福利列表
|
||||||
benefitsList: [
|
benefitsList: [
|
||||||
|
|
@ -159,11 +153,6 @@ export default {
|
||||||
activitiesList: [],
|
activitiesList: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
headerHeight() {
|
|
||||||
return this.statusBarHeight + this.headerContentHeight;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.getSystemInfo();
|
this.getSystemInfo();
|
||||||
this.loadHomeData();
|
this.loadHomeData();
|
||||||
|
|
@ -395,15 +384,6 @@ export default {
|
||||||
width: 306rpx;
|
width: 306rpx;
|
||||||
height: 72rpx;
|
height: 72rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.header-fixed {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
|
||||||
background-color: #e2e8f1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 招募宣传横幅 */
|
/* 招募宣传横幅 */
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="login-page">
|
<view class="login-page">
|
||||||
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
<!-- 状态栏占位 -->
|
||||||
<NavHeader title="登录" />
|
<!-- <view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view> -->
|
||||||
</view>
|
|
||||||
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
<NavHeader title="登录" />
|
||||||
|
|
||||||
|
<!-- 登录内容区 -->
|
||||||
<view class="login-content">
|
<view class="login-content">
|
||||||
<!-- Logo区域 -->
|
<!-- Logo区域 -->
|
||||||
<view class="logo-section">
|
<view class="logo-section">
|
||||||
|
|
@ -91,7 +93,6 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -117,9 +118,6 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
headerHeight() {
|
|
||||||
return this.statusBarHeight + 44
|
|
||||||
},
|
|
||||||
// 判断是否可以登录
|
// 判断是否可以登录
|
||||||
canLogin() {
|
canLogin() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -144,17 +142,13 @@ export default {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存页面参数(state、inviteCode;扫码进入时邀请码在 scene 中)
|
// 保存页面参数(state 和 inviteCode)
|
||||||
if (options.state) {
|
if (options.state) {
|
||||||
this.state = options.state
|
this.state = options.state
|
||||||
}
|
}
|
||||||
if (options.inviteCode) {
|
if (options.inviteCode) {
|
||||||
this.inviteCode = options.inviteCode
|
this.inviteCode = options.inviteCode
|
||||||
}
|
}
|
||||||
if (options.scene && /^\d+-\d+$/.test(String(options.scene))) {
|
|
||||||
this.inviteCode = String(options.scene)
|
|
||||||
uni.setStorageSync('inviteCode', this.inviteCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断是否显示返回按钮(从其他页面跳转过来时显示)
|
// 判断是否显示返回按钮(从其他页面跳转过来时显示)
|
||||||
const pages = getCurrentPages()
|
const pages = getCurrentPages()
|
||||||
|
|
@ -498,22 +492,6 @@ export default {
|
||||||
background: linear-gradient(180deg, #e2e8f1 0%, #ffffff 100%);
|
background: linear-gradient(180deg, #e2e8f1 0%, #ffffff 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-fixed-wrapper {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
|
||||||
background: linear-gradient(180deg, #e2e8f1 0%, #ffffff 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-wrap {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-bar {
|
.status-bar {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="profile-page">
|
<view class="profile-page">
|
||||||
<!-- 头部固定,滑动时不动 -->
|
<view class="header" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||||
<view
|
|
||||||
class="header header-fixed"
|
|
||||||
:style="{ paddingTop: statusBarHeight + 'px' }"
|
|
||||||
>
|
|
||||||
<text class="header-text">个人中心</text>
|
<text class="header-text">个人中心</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 主体内容(预留头部高度) -->
|
|
||||||
<view class="profile-main" :style="{ paddingTop: navBarHeight + 'px' }">
|
|
||||||
<!-- 用户资料卡片 -->
|
<!-- 用户资料卡片 -->
|
||||||
<view
|
<view
|
||||||
class="user-card"
|
class="user-card"
|
||||||
|
|
@ -63,9 +57,6 @@
|
||||||
<text class="user-id">NO.{{ userInfo.id }}</text>
|
<text class="user-id">NO.{{ userInfo.id }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="invite-btn" @click="goToInvite">
|
|
||||||
<image src="https://resource2.ctshenglong.cn/20260309/yaoqingma_btn_1773020094853.png" mode="aspectFill"></image>
|
|
||||||
</view>
|
|
||||||
<view class="member-benefits-btn" @click="goToMemberBenefits">
|
<view class="member-benefits-btn" @click="goToMemberBenefits">
|
||||||
<image :src="memberLevelIcon" mode="aspectFill"></image>
|
<image :src="memberLevelIcon" mode="aspectFill"></image>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -110,14 +101,7 @@
|
||||||
<text class="menu-text">投诉建议</text>
|
<text class="menu-text">投诉建议</text>
|
||||||
<text class="menu-arrow">›</text>
|
<text class="menu-arrow">›</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="menu-item" @tap="goToMyCoupons">
|
<view class="menu-item" @tap="goToPostMessage">
|
||||||
<view class="menu-icon"
|
|
||||||
><image src="/static/profile/my_icon.png" mode="aspectFill"></image
|
|
||||||
></view>
|
|
||||||
<text class="menu-text">我的优惠卷</text>
|
|
||||||
<text class="menu-arrow">›</text>
|
|
||||||
</view>
|
|
||||||
<!-- <view class="menu-item" @tap="goToPostMessage">
|
|
||||||
<view class="menu-icon"
|
<view class="menu-icon"
|
||||||
><image
|
><image
|
||||||
src="/static/profile/publish-message.png"
|
src="/static/profile/publish-message.png"
|
||||||
|
|
@ -126,47 +110,18 @@
|
||||||
></view>
|
></view>
|
||||||
<text class="menu-text">发布消息</text>
|
<text class="menu-text">发布消息</text>
|
||||||
<text class="menu-arrow">›</text>
|
<text class="menu-arrow">›</text>
|
||||||
</view> -->
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 退出登录按钮 -->
|
<!-- 退出登录按钮 -->
|
||||||
<view class="logout-section">
|
<view class="logout-section">
|
||||||
<button class="logout-btn" @click="handleLogout">退出登录</button>
|
<button class="logout-btn" @click="handleLogout">退出登录</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 邀请海报弹窗(图5样式) -->
|
|
||||||
<view class="invite-modal-mask" v-if="inviteModalVisible" @click="closeInviteModal"></view>
|
|
||||||
<view class="invite-modal-wrap" v-if="inviteModalVisible" @click.stop>
|
|
||||||
<view class="invite-poster" id="invite-poster">
|
|
||||||
<view class="invite-poster-close" @click="closeInviteModal">×</view>
|
|
||||||
<view class="poster-inner">
|
|
||||||
<view class="poster-tag">邀请好友</view>
|
|
||||||
<view class="poster-qr-wrap" v-if="inviteQRCodeUrl">
|
|
||||||
<view class="poster-qr-outer">
|
|
||||||
<image class="poster-qr-image" :src="inviteQRCodeUrl" mode="aspectFit"></image>
|
|
||||||
</view>
|
|
||||||
<text class="poster-qr-tip">长按识别图中二维码</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="poster-actions">
|
|
||||||
<view class="poster-btn poster-btn-cancel" @click="closeInviteModal">取消</view>
|
|
||||||
<view class="poster-btn poster-btn-save" @click="savePosterImage">保存图片</view>
|
|
||||||
</view>
|
|
||||||
<!-- 用于导出海报的 canvas(隐藏) -->
|
|
||||||
<canvas
|
|
||||||
class="poster-canvas"
|
|
||||||
type="2d"
|
|
||||||
id="poster-canvas"
|
|
||||||
:style="{ width: posterCanvasWidth + 'px', height: posterCanvasHeight + 'px' }"
|
|
||||||
></canvas>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getUserInfo, getInviteQRCode } from "@/api/profile.js";
|
import { getUserInfo } from "@/api/profile.js";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -179,12 +134,6 @@ export default {
|
||||||
},
|
},
|
||||||
noticeNum: 3,
|
noticeNum: 3,
|
||||||
uploading: false, // 是否正在上传头像
|
uploading: false, // 是否正在上传头像
|
||||||
inviteModalVisible: false,
|
|
||||||
inviteQRCodeUrl: "",
|
|
||||||
inviteCodeStr: "", // 邀请码,格式:邀请类型-用户id,如 0-123
|
|
||||||
posterCanvasWidth: 680,
|
|
||||||
posterCanvasHeight: 860,
|
|
||||||
posterSaving: false,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -285,185 +234,6 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 邀请好友:弹出邀请二维码。邀请码格式 "邀请类型-用户id",扫码后打开小程序登录注册页,登录接口 /app-api/member/auth/weixin-mini-app-login 使用 inviteCode
|
|
||||||
// 邀请类型:0=会员邀请会员,1=系统用户邀请会员。当前为会员邀请,故用 0-用户id
|
|
||||||
async goToInvite() {
|
|
||||||
const uid = this.userInfo && this.userInfo.id;
|
|
||||||
if (!uid) {
|
|
||||||
uni.showToast({ title: "请先登录", icon: "none" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const inviteCodeStr = `0-${uid}`;
|
|
||||||
this.inviteCodeStr = inviteCodeStr;
|
|
||||||
this.inviteModalVisible = true;
|
|
||||||
this.inviteQRCodeUrl = "";
|
|
||||||
|
|
||||||
try {
|
|
||||||
const url = await getInviteQRCode(inviteCodeStr);
|
|
||||||
if (url) {
|
|
||||||
this.inviteQRCodeUrl = url;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error("获取邀请二维码失败:", e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
closeInviteModal() {
|
|
||||||
this.inviteModalVisible = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
// 保存海报图片到相册(按图5样式绘制 canvas 后导出)
|
|
||||||
async savePosterImage() {
|
|
||||||
if (this.posterSaving || !this.inviteQRCodeUrl) return;
|
|
||||||
this.posterSaving = true;
|
|
||||||
uni.showLoading({ title: "生成中...", mask: true });
|
|
||||||
const dpr = uni.getSystemInfoSync().pixelRatio || 2;
|
|
||||||
const w = this.posterCanvasWidth;
|
|
||||||
const h = this.posterCanvasHeight;
|
|
||||||
const padding = 48;
|
|
||||||
const qrSize = 260;
|
|
||||||
const qrTop = 220;
|
|
||||||
const avatarSize = 68;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const query = uni.createSelectorQuery().in(this);
|
|
||||||
query
|
|
||||||
.select("#poster-canvas")
|
|
||||||
.fields({ node: true, size: true })
|
|
||||||
.exec(async (res) => {
|
|
||||||
if (!res || !res[0] || !res[0].node) {
|
|
||||||
uni.hideLoading();
|
|
||||||
this.posterSaving = false;
|
|
||||||
uni.showToast({ title: "生成失败,请重试", icon: "none" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const canvas = res[0].node;
|
|
||||||
const ctx = canvas.getContext("2d");
|
|
||||||
canvas.width = w * dpr;
|
|
||||||
canvas.height = h * dpr;
|
|
||||||
ctx.scale(dpr, dpr);
|
|
||||||
|
|
||||||
// 白底圆角
|
|
||||||
ctx.fillStyle = "#ffffff";
|
|
||||||
const r = 16;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(padding + r, padding);
|
|
||||||
ctx.lineTo(w - padding - r, padding);
|
|
||||||
ctx.quadraticCurveTo(w - padding, padding, w - padding, padding + r);
|
|
||||||
ctx.lineTo(w - padding, h - padding - r);
|
|
||||||
ctx.quadraticCurveTo(w - padding, h - padding, w - padding - r, h - padding);
|
|
||||||
ctx.lineTo(padding + r, h - padding);
|
|
||||||
ctx.quadraticCurveTo(padding, h - padding, padding, h - padding - r);
|
|
||||||
ctx.lineTo(padding, padding + r);
|
|
||||||
ctx.quadraticCurveTo(padding, padding, padding + r, padding);
|
|
||||||
ctx.closePath();
|
|
||||||
ctx.fill();
|
|
||||||
|
|
||||||
// 文案
|
|
||||||
ctx.fillStyle = "#333333";
|
|
||||||
ctx.font = "bold 14px PingFang SC, sans-serif";
|
|
||||||
ctx.textAlign = "center";
|
|
||||||
const nickname = (this.userInfo.nickname || "微信用户").slice(0, 20);
|
|
||||||
ctx.font = "bold 16px PingFang SC, sans-serif";
|
|
||||||
ctx.fillText(nickname, w / 2, 96);
|
|
||||||
ctx.font = "13px PingFang SC, sans-serif";
|
|
||||||
ctx.fillStyle = "#666666";
|
|
||||||
ctx.fillText("长按识别图中二维码", w / 2, qrTop + qrSize + 44);
|
|
||||||
|
|
||||||
const drawImage = (src) => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const img = canvas.createImage();
|
|
||||||
img.onload = () => resolve(img);
|
|
||||||
img.onerror = () => reject(new Error("图片加载失败"));
|
|
||||||
img.src = src;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const qrImg = await drawImage(this.inviteQRCodeUrl);
|
|
||||||
const qrX = (w - qrSize) / 2;
|
|
||||||
ctx.save();
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(w / 2, qrTop + qrSize / 2, qrSize / 2, 0, Math.PI * 2);
|
|
||||||
ctx.closePath();
|
|
||||||
ctx.clip();
|
|
||||||
ctx.drawImage(qrImg, qrX, qrTop, qrSize, qrSize);
|
|
||||||
ctx.restore();
|
|
||||||
|
|
||||||
const avatarSrc = this.avatarSrc;
|
|
||||||
const avatarImg = await drawImage(avatarSrc);
|
|
||||||
const ax = (w - avatarSize) / 2;
|
|
||||||
const ay = qrTop + (qrSize - avatarSize) / 2;
|
|
||||||
ctx.save();
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(w / 2, qrTop + qrSize / 2, avatarSize / 2, 0, Math.PI * 2);
|
|
||||||
ctx.closePath();
|
|
||||||
ctx.clip();
|
|
||||||
ctx.drawImage(avatarImg, ax, ay, avatarSize, avatarSize);
|
|
||||||
ctx.restore();
|
|
||||||
} catch (imgErr) {
|
|
||||||
console.warn("海报图片绘制跳过:", imgErr);
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.createSelectorQuery()
|
|
||||||
.in(this)
|
|
||||||
.select("#poster-canvas")
|
|
||||||
.fields({ node: true, size: true })
|
|
||||||
.exec((r2) => {
|
|
||||||
if (!r2 || !r2[0] || !r2[0].node) {
|
|
||||||
uni.hideLoading();
|
|
||||||
this.posterSaving = false;
|
|
||||||
uni.showToast({ title: "导出失败", icon: "none" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const node = r2[0].node;
|
|
||||||
uni.canvasToTempFilePath({
|
|
||||||
canvas: node,
|
|
||||||
fileType: "png",
|
|
||||||
quality: 1,
|
|
||||||
success: (pathRes) => {
|
|
||||||
uni.saveImageToPhotosAlbum({
|
|
||||||
filePath: pathRes.tempFilePath,
|
|
||||||
success: () => {
|
|
||||||
uni.hideLoading();
|
|
||||||
this.posterSaving = false;
|
|
||||||
uni.showToast({ title: "已保存到相册", icon: "success" });
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
uni.hideLoading();
|
|
||||||
this.posterSaving = false;
|
|
||||||
if (err.errMsg && err.errMsg.indexOf("auth") !== -1) {
|
|
||||||
uni.showModal({
|
|
||||||
title: "提示",
|
|
||||||
content: "需要您授权保存图片到相册",
|
|
||||||
confirmText: "去设置",
|
|
||||||
success: (s) => {
|
|
||||||
if (s.confirm) uni.openSetting();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
uni.showToast({ title: "保存失败", icon: "none" });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
fail: () => {
|
|
||||||
uni.hideLoading();
|
|
||||||
this.posterSaving = false;
|
|
||||||
uni.showToast({ title: "导出失败", icon: "none" });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, 400);
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.error("保存海报失败:", e);
|
|
||||||
uni.hideLoading();
|
|
||||||
this.posterSaving = false;
|
|
||||||
uni.showToast({ title: "生成失败,请重试", icon: "none" });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 会员权益
|
// 会员权益
|
||||||
goToMemberBenefits() {
|
goToMemberBenefits() {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
|
|
@ -490,11 +260,6 @@ export default {
|
||||||
url: "/pages/activities/complaints",
|
url: "/pages/activities/complaints",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
goToMyCoupons() {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: "/pages/profileSub/myCoupons",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
goToPostMessage() {
|
goToPostMessage() {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: "/pages/activities/postMessage",
|
url: "/pages/activities/postMessage",
|
||||||
|
|
@ -693,15 +458,6 @@ export default {
|
||||||
height: 88rpx;
|
height: 88rpx;
|
||||||
line-height: 88rpx;
|
line-height: 88rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.header-fixed {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
|
||||||
background: radial-gradient(0% 0% at 0% 0%, #ffffff 0%, #e2e8f1 100%);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 用户资料卡片 */
|
/* 用户资料卡片 */
|
||||||
|
|
@ -809,18 +565,6 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.invite-btn {
|
|
||||||
position: absolute;
|
|
||||||
right: 255rpx;
|
|
||||||
bottom: 24rpx;
|
|
||||||
width: 175rpx;
|
|
||||||
height: 42rpx;
|
|
||||||
image {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.member-benefits-btn {
|
.member-benefits-btn {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 50rpx;
|
right: 50rpx;
|
||||||
|
|
@ -958,153 +702,5 @@ export default {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 邀请海报弹窗(图5样式) */
|
|
||||||
.invite-modal-mask {
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: rgba(0, 0, 0, 0.5);
|
|
||||||
z-index: 900;
|
|
||||||
}
|
|
||||||
.invite-modal-wrap {
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
z-index: 901;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 24rpx;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.invite-poster {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 660rpx;
|
|
||||||
background: #ffffff;
|
|
||||||
border-radius: 24rpx;
|
|
||||||
overflow: visible;
|
|
||||||
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.08);
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.invite-poster-close {
|
|
||||||
position: absolute;
|
|
||||||
right: 20rpx;
|
|
||||||
top: 20rpx;
|
|
||||||
width: 64rpx;
|
|
||||||
height: 64rpx;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 44rpx;
|
|
||||||
color: #999999;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
.poster-inner {
|
|
||||||
padding: 48rpx 48rpx 44rpx;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.poster-tag {
|
|
||||||
position: absolute;
|
|
||||||
top: 28rpx;
|
|
||||||
left: 28rpx;
|
|
||||||
padding: 8rpx 20rpx;
|
|
||||||
background: #f5f5f5;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
font-size: 26rpx;
|
|
||||||
color: #666666;
|
|
||||||
font-family: PingFang-SC, PingFang-SC;
|
|
||||||
}
|
|
||||||
.poster-nickname {
|
|
||||||
font-size: 38rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
font-family: PingFang-SC, PingFang-SC;
|
|
||||||
color: #333333;
|
|
||||||
margin-bottom: 40rpx;
|
|
||||||
}
|
|
||||||
.poster-qr-wrap {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.poster-qr-outer {
|
|
||||||
width: 380rpx;
|
|
||||||
height: 380rpx;
|
|
||||||
position: relative;
|
|
||||||
border-radius: 50%;
|
|
||||||
overflow: hidden;
|
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
|
||||||
.poster-qr-image {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
.poster-qr-avatar-wrap {
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: 120rpx;
|
|
||||||
height: 120rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
overflow: hidden;
|
|
||||||
background: #fff;
|
|
||||||
padding: 8rpx;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.poster-qr-avatar {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
.poster-qr-tip {
|
|
||||||
font-size: 26rpx;
|
|
||||||
color: #999999;
|
|
||||||
margin-top: 28rpx;
|
|
||||||
font-family: PingFang-SC, PingFang-SC;
|
|
||||||
}
|
|
||||||
.poster-actions {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 28rpx;
|
|
||||||
margin-top: 32rpx;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 660rpx;
|
|
||||||
}
|
|
||||||
.poster-btn {
|
|
||||||
flex: 1;
|
|
||||||
max-width: 280rpx;
|
|
||||||
height: 88rpx;
|
|
||||||
border-radius: 44rpx;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 30rpx;
|
|
||||||
font-family: PingFang-SC, PingFang-SC;
|
|
||||||
}
|
|
||||||
.poster-btn-cancel {
|
|
||||||
background: #f0f0f0;
|
|
||||||
color: #666666;
|
|
||||||
}
|
|
||||||
.poster-btn-save {
|
|
||||||
background: #004294;
|
|
||||||
color: #ffffff;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
.poster-canvas {
|
|
||||||
position: fixed;
|
|
||||||
left: -9999px;
|
|
||||||
top: 0;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,404 +0,0 @@
|
||||||
<template>
|
|
||||||
<view class="coupon-detail-page">
|
|
||||||
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
|
||||||
<NavHeader title="优惠卷详情" />
|
|
||||||
</view>
|
|
||||||
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
|
||||||
<view v-if="!loading && detail" class="content">
|
|
||||||
<!-- 优惠卷基础信息卡片 -->
|
|
||||||
<view class="coupon-card">
|
|
||||||
<view class="coupon-left">
|
|
||||||
<view class="coupon-price" v-if="detail.type === 2">
|
|
||||||
<text class="amount">{{ (detail.discountPercent / 10).toFixed(1).replace(/\.0$/, '') }}</text>
|
|
||||||
<text class="symbol" style="font-size: 24rpx; margin-left: 4rpx;">折</text>
|
|
||||||
</view>
|
|
||||||
<view class="coupon-price" v-else>
|
|
||||||
<text class="symbol">¥</text>
|
|
||||||
<text class="amount">{{ formatAmount(detail.discountAmount || detail.discountPrice || detail.price || 0) }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="coupon-condition" v-if="detail.usePrice">满{{ formatAmount(detail.usePrice) }}可用</view>
|
|
||||||
<view class="coupon-condition" v-else>无门槛</view>
|
|
||||||
</view>
|
|
||||||
<view class="coupon-right">
|
|
||||||
<view class="coupon-name">{{ detail.name }}</view>
|
|
||||||
<view class="coupon-time" v-if="detail.type === 2 && detail.discountLimit > 0">最多抵扣: ¥{{ formatAmount(detail.discountLimit) }}</view>
|
|
||||||
<view class="coupon-time" v-if="detail.validEndTime">有效期至: {{ formatTimeStr(detail.validEndTime) }}</view>
|
|
||||||
</view>
|
|
||||||
<view class="coupon-status-stamp" v-if="detail.status === 1">已使用</view>
|
|
||||||
<view class="coupon-status-stamp" v-else-if="detail.status === 2">已过期</view>
|
|
||||||
<view class="coupon-status-stamp" v-else-if="detail.status === 3">已作废</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 详细信息 -->
|
|
||||||
<view class="info-section">
|
|
||||||
<view class="info-title">使用说明</view>
|
|
||||||
<view class="info-item">
|
|
||||||
<text class="label">可用范围:</text>
|
|
||||||
<text class="value">{{ detail.productScope === 1 ? '全部代金券通用' : '指定代金券可用' }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="info-item" v-if="detail.validStartTime && detail.validEndTime">
|
|
||||||
<text class="label">有效期限:</text>
|
|
||||||
<text class="value">{{ formatTimeStr(detail.validStartTime) }} 至 {{ formatTimeStr(detail.validEndTime) }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 适用商品列表 -->
|
|
||||||
<view class="goods-section" v-if="detail.productScope === 2 && detail.voucherList && detail.voucherList.length > 0">
|
|
||||||
<view class="section-title">可用代金卷</view>
|
|
||||||
<view class="goods-list">
|
|
||||||
<view class="goods-item" v-for="(item, index) in detail.voucherList" :key="index">
|
|
||||||
<image class="goods-img" :src="item.coverUrl || item.picUrl || '/static/home/entry_icon.png'" mode="aspectFill"></image>
|
|
||||||
<view class="goods-info">
|
|
||||||
<view class="goods-name">{{ item.name }}</view>
|
|
||||||
<view class="goods-shop-name" v-if="item.shopName">门店: {{ item.shopName }}</view>
|
|
||||||
<view class="goods-meta-list">
|
|
||||||
<view class="goods-meta-item" v-if="item.stock !== undefined">
|
|
||||||
<text class="meta-label">库存:</text>
|
|
||||||
<text class="meta-value">{{ item.stock }}份</text>
|
|
||||||
</view>
|
|
||||||
<view class="goods-meta-item" v-if="item.useRule">
|
|
||||||
<text class="meta-label">使用规则:</text>
|
|
||||||
<text class="meta-value">{{ item.useRule }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="goods-meta-item" v-if="item.validDays">
|
|
||||||
<text class="meta-label">有效天数:</text>
|
|
||||||
<text class="meta-value">{{ item.validDays }}天</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="goods-price-row">
|
|
||||||
<text class="goods-price">¥{{ formatAmount(item.salePrice || item.sellPrice) }}</text>
|
|
||||||
<text class="goods-original-price" v-if="item.originalPrice">¥{{ formatAmount(item.originalPrice) }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view v-if="loading" class="loading-state">
|
|
||||||
<text>加载中...</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
|
||||||
import { getLuCouponDetail } from "@/api/service.js";
|
|
||||||
import { formatTime } from "@/utils/date.js";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
NavHeader,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
statusBarHeight: 0,
|
|
||||||
couponId: null,
|
|
||||||
detail: null,
|
|
||||||
loading: true,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
headerHeight() {
|
|
||||||
return this.statusBarHeight + 44;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
onLoad(options) {
|
|
||||||
const systemInfo = uni.getSystemInfoSync();
|
|
||||||
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
|
||||||
|
|
||||||
if (options.id) {
|
|
||||||
this.couponId = options.id;
|
|
||||||
this.loadDetail();
|
|
||||||
} else {
|
|
||||||
uni.showToast({
|
|
||||||
title: '参数错误',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.navigateBack();
|
|
||||||
}, 1500);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async loadDetail() {
|
|
||||||
this.loading = true;
|
|
||||||
try {
|
|
||||||
const res = await getLuCouponDetail(this.couponId);
|
|
||||||
if (res) {
|
|
||||||
this.detail = res;
|
|
||||||
} else {
|
|
||||||
uni.showToast({
|
|
||||||
title: '获取详情失败',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("加载优惠卷详情失败:", error);
|
|
||||||
uni.showToast({
|
|
||||||
title: '加载失败',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
this.loading = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
formatTimeStr(timestamp) {
|
|
||||||
if (!timestamp) return '';
|
|
||||||
return formatTime(timestamp, 'YYYY-MM-DD HH:mm:ss');
|
|
||||||
},
|
|
||||||
formatAmount(amount) {
|
|
||||||
if (!amount) return '0';
|
|
||||||
const yuan = amount / 100;
|
|
||||||
return Number.isInteger(yuan) ? yuan.toString() : yuan.toFixed(2).replace(/\.?0+$/, '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.coupon-detail-page {
|
|
||||||
min-height: 100vh;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-fixed-wrapper {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
z-index: 100;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-wrap {
|
|
||||||
padding: 20rpx;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-state {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
padding-top: 100rpx;
|
|
||||||
color: #999;
|
|
||||||
font-size: 28rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-card {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 16rpx;
|
|
||||||
margin-bottom: 30rpx;
|
|
||||||
padding: 40rpx 30rpx;
|
|
||||||
align-items: center;
|
|
||||||
overflow: hidden;
|
|
||||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.05);
|
|
||||||
|
|
||||||
.coupon-left {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 180rpx;
|
|
||||||
border-right: 2rpx dashed #eee;
|
|
||||||
padding-right: 20rpx;
|
|
||||||
|
|
||||||
.coupon-price {
|
|
||||||
color: #d51c3c;
|
|
||||||
|
|
||||||
.symbol {
|
|
||||||
font-size: 28rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.amount {
|
|
||||||
font-size: 56rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-condition {
|
|
||||||
font-size: 22rpx;
|
|
||||||
color: #666;
|
|
||||||
margin-top: 10rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-right {
|
|
||||||
flex: 1;
|
|
||||||
padding-left: 30rpx;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.coupon-name {
|
|
||||||
font-size: 34rpx;
|
|
||||||
color: #333;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-time {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #999;
|
|
||||||
margin-bottom: 6rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-status-stamp {
|
|
||||||
position: absolute;
|
|
||||||
right: -20rpx;
|
|
||||||
top: 20rpx;
|
|
||||||
width: 120rpx;
|
|
||||||
height: 40rpx;
|
|
||||||
line-height: 40rpx;
|
|
||||||
text-align: center;
|
|
||||||
background-color: rgba(153, 153, 153, 0.1);
|
|
||||||
color: #999;
|
|
||||||
font-size: 20rpx;
|
|
||||||
transform: rotate(45deg);
|
|
||||||
transform-origin: center;
|
|
||||||
border: 1rpx solid #999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-section {
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 16rpx;
|
|
||||||
padding: 30rpx;
|
|
||||||
margin-bottom: 30rpx;
|
|
||||||
|
|
||||||
.info-title {
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 24rpx;
|
|
||||||
padding-bottom: 20rpx;
|
|
||||||
border-bottom: 1rpx solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-item {
|
|
||||||
display: flex;
|
|
||||||
margin-bottom: 16rpx;
|
|
||||||
font-size: 28rpx;
|
|
||||||
line-height: 1.5;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
color: #666;
|
|
||||||
width: 140rpx;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
color: #333;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-section {
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 16rpx;
|
|
||||||
padding: 30rpx;
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 24rpx;
|
|
||||||
padding-bottom: 20rpx;
|
|
||||||
border-bottom: 1rpx solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-list {
|
|
||||||
.goods-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 20rpx 0;
|
|
||||||
border-bottom: 1rpx solid #f5f5f5;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-img {
|
|
||||||
width: 160rpx;
|
|
||||||
height: 160rpx;
|
|
||||||
border-radius: 12rpx;
|
|
||||||
margin-right: 20rpx;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-info {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: flex-start;
|
|
||||||
|
|
||||||
.goods-name {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #333;
|
|
||||||
font-weight: bold;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
overflow: hidden;
|
|
||||||
margin-bottom: 8rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-shop-name {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 8rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-meta-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-bottom: 12rpx;
|
|
||||||
|
|
||||||
.goods-meta-item {
|
|
||||||
font-size: 22rpx;
|
|
||||||
color: #999;
|
|
||||||
margin-bottom: 4rpx;
|
|
||||||
|
|
||||||
.meta-label {
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
.meta-value {
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-price-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: baseline;
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-price {
|
|
||||||
font-size: 32rpx;
|
|
||||||
color: #d51c3c;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-right: 12rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-original-price {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #999;
|
|
||||||
text-decoration: line-through;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,393 +0,0 @@
|
||||||
<template>
|
|
||||||
<view class="my-coupons-page">
|
|
||||||
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
|
||||||
<NavHeader title="我的优惠卷" />
|
|
||||||
</view>
|
|
||||||
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
|
||||||
<!-- Tab 切换 -->
|
|
||||||
<view class="tab-bar">
|
|
||||||
<view
|
|
||||||
class="tab-item"
|
|
||||||
:class="{ active: currentTab === '0' }"
|
|
||||||
@click="switchTab('0')"
|
|
||||||
>
|
|
||||||
<text class="tab-text">待使用</text>
|
|
||||||
</view>
|
|
||||||
<view
|
|
||||||
class="tab-item"
|
|
||||||
:class="{ active: currentTab === '1' }"
|
|
||||||
@click="switchTab('1')"
|
|
||||||
>
|
|
||||||
<text class="tab-text">已使用</text>
|
|
||||||
</view>
|
|
||||||
<view
|
|
||||||
class="tab-item"
|
|
||||||
:class="{ active: currentTab === '2' }"
|
|
||||||
@click="switchTab('2')"
|
|
||||||
>
|
|
||||||
<text class="tab-text">已过期</text>
|
|
||||||
</view>
|
|
||||||
<view
|
|
||||||
class="tab-item"
|
|
||||||
:class="{ active: currentTab === 'all' }"
|
|
||||||
@click="switchTab('all')"
|
|
||||||
>
|
|
||||||
<text class="tab-text">全部</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 列表内容 -->
|
|
||||||
<scroll-view
|
|
||||||
class="coupon-list"
|
|
||||||
scroll-y="true"
|
|
||||||
:refresher-enabled="true"
|
|
||||||
:refresher-triggered="refreshing"
|
|
||||||
@refresherrefresh="handleRefresh"
|
|
||||||
@scrolltolower="handleLoadMore"
|
|
||||||
:lower-threshold="100"
|
|
||||||
>
|
|
||||||
<!-- 空数据提示 -->
|
|
||||||
<view class="empty-state" v-if="!loading && coupons.length === 0">
|
|
||||||
<image
|
|
||||||
class="empty-icon"
|
|
||||||
src="/static/home/entry_icon.png"
|
|
||||||
mode="aspectFit"
|
|
||||||
></image>
|
|
||||||
<text class="empty-text">暂无优惠卷</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view
|
|
||||||
class="coupon-item"
|
|
||||||
v-for="(item, index) in coupons"
|
|
||||||
:key="index"
|
|
||||||
:class="{ disabled: isCouponDisabled(item) }"
|
|
||||||
@click="goToDetail(item)"
|
|
||||||
>
|
|
||||||
<view class="coupon-left">
|
|
||||||
<!-- 折扣类 -->
|
|
||||||
<view class="coupon-price" v-if="item.type === 2">
|
|
||||||
<text class="amount">{{ (item.discountPercent / 10).toFixed(1).replace(/\.0$/, '') }}</text>
|
|
||||||
<text class="symbol" style="font-size: 24rpx; margin-left: 4rpx;">折</text>
|
|
||||||
</view>
|
|
||||||
<!-- 金额类 -->
|
|
||||||
<view class="coupon-price" v-else>
|
|
||||||
<text class="symbol">¥</text>
|
|
||||||
<text class="amount">{{ formatAmount(item.discountAmount || item.discountPrice || item.price || 0) }}</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="coupon-condition" v-if="item.usePrice">
|
|
||||||
满{{ formatAmount(item.usePrice) }}可用
|
|
||||||
</view>
|
|
||||||
<view class="coupon-condition" v-else>
|
|
||||||
无门槛
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="coupon-right">
|
|
||||||
<view class="coupon-name">{{ item.name }}</view>
|
|
||||||
<view class="coupon-time" v-if="item.type === 2 && item.discountLimit > 0" style="margin-bottom: 6rpx;">最多抵扣: ¥{{ formatAmount(item.discountLimit) }}</view>
|
|
||||||
<view class="coupon-time" v-if="item.validEndTime">有效期至: {{ formatTimeStr(item.validEndTime) }}</view>
|
|
||||||
</view>
|
|
||||||
<view class="coupon-status-stamp" v-if="item.status === 1">已使用</view>
|
|
||||||
<view class="coupon-status-stamp" v-else-if="item.status === 2">已过期</view>
|
|
||||||
<view class="coupon-status-stamp" v-else-if="item.status === 3">已作废</view>
|
|
||||||
</view>
|
|
||||||
</scroll-view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
|
||||||
import { getLuCouponPage } from "@/api/service.js";
|
|
||||||
import { formatTime } from "@/utils/date.js";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
NavHeader,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
statusBarHeight: 0,
|
|
||||||
currentTab: '0', // 默认待使用
|
|
||||||
coupons: [],
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
total: 0,
|
|
||||||
loading: false,
|
|
||||||
refreshing: false,
|
|
||||||
hasMore: true,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
headerHeight() {
|
|
||||||
return this.statusBarHeight + 44;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
onLoad() {
|
|
||||||
const systemInfo = uni.getSystemInfoSync();
|
|
||||||
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
|
||||||
this.loadData();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
switchTab(tab) {
|
|
||||||
if (this.currentTab === tab) return;
|
|
||||||
this.currentTab = tab;
|
|
||||||
this.refreshData();
|
|
||||||
},
|
|
||||||
async loadData() {
|
|
||||||
if (this.loading || !this.hasMore) return;
|
|
||||||
this.loading = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const params = {
|
|
||||||
pageNo: this.pageNo,
|
|
||||||
pageSize: this.pageSize
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.currentTab !== 'all') {
|
|
||||||
params.status = Number(this.currentTab);
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await getLuCouponPage(params);
|
|
||||||
if (res && res.list) {
|
|
||||||
if (this.pageNo === 1) {
|
|
||||||
this.coupons = res.list;
|
|
||||||
} else {
|
|
||||||
this.coupons = this.coupons.concat(res.list);
|
|
||||||
}
|
|
||||||
this.total = res.total || 0;
|
|
||||||
this.hasMore = this.coupons.length < this.total;
|
|
||||||
this.pageNo++;
|
|
||||||
} else {
|
|
||||||
this.hasMore = false;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("加载优惠卷失败:", error);
|
|
||||||
uni.showToast({
|
|
||||||
title: "加载失败",
|
|
||||||
icon: "none"
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
this.loading = false;
|
|
||||||
this.refreshing = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
refreshData() {
|
|
||||||
this.pageNo = 1;
|
|
||||||
this.hasMore = true;
|
|
||||||
this.coupons = [];
|
|
||||||
this.loadData();
|
|
||||||
},
|
|
||||||
handleRefresh() {
|
|
||||||
if (this.refreshing) return;
|
|
||||||
this.refreshing = true;
|
|
||||||
this.pageNo = 1;
|
|
||||||
this.hasMore = true;
|
|
||||||
this.loadData();
|
|
||||||
},
|
|
||||||
handleLoadMore() {
|
|
||||||
this.loadData();
|
|
||||||
},
|
|
||||||
goToDetail(item) {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: `/pages/profileSub/couponDetail?id=${item.id}`
|
|
||||||
});
|
|
||||||
},
|
|
||||||
isCouponDisabled(item) {
|
|
||||||
return item.status === 1 || item.status === 2 || item.status === 3;
|
|
||||||
},
|
|
||||||
formatTimeStr(timestamp) {
|
|
||||||
if (!timestamp) return '';
|
|
||||||
return formatTime(timestamp, 'YYYY-MM-DD HH:mm:ss');
|
|
||||||
},
|
|
||||||
// 将分转换为元,并处理小数显示
|
|
||||||
formatAmount(amount) {
|
|
||||||
if (!amount) return '0';
|
|
||||||
const yuan = amount / 100;
|
|
||||||
return Number.isInteger(yuan) ? yuan.toString() : yuan.toFixed(2).replace(/\.?0+$/, '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.my-coupons-page {
|
|
||||||
min-height: 100vh;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-fixed-wrapper {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
z-index: 100;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-wrap {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100vh;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-bar {
|
|
||||||
display: flex;
|
|
||||||
background-color: #fff;
|
|
||||||
height: 88rpx;
|
|
||||||
border-bottom: 1rpx solid #f0f0f0;
|
|
||||||
|
|
||||||
.tab-item {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.tab-text {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
.tab-text {
|
|
||||||
color: #d51c3c;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
width: 40rpx;
|
|
||||||
height: 6rpx;
|
|
||||||
background-color: #d51c3c;
|
|
||||||
border-radius: 4rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-list {
|
|
||||||
flex: 1;
|
|
||||||
padding: 20rpx;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-state {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding-top: 200rpx;
|
|
||||||
|
|
||||||
.empty-icon {
|
|
||||||
width: 200rpx;
|
|
||||||
height: 200rpx;
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-text {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-item {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 16rpx;
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
padding: 30rpx;
|
|
||||||
align-items: center;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
opacity: 0.6;
|
|
||||||
background-color: #fafafa;
|
|
||||||
|
|
||||||
.coupon-price {
|
|
||||||
color: #999 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-name {
|
|
||||||
color: #999 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-left {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 160rpx;
|
|
||||||
border-right: 2rpx dashed #eee;
|
|
||||||
padding-right: 20rpx;
|
|
||||||
|
|
||||||
.coupon-price {
|
|
||||||
color: #d51c3c;
|
|
||||||
|
|
||||||
.symbol {
|
|
||||||
font-size: 24rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.amount {
|
|
||||||
font-size: 48rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-condition {
|
|
||||||
font-size: 20rpx;
|
|
||||||
color: #666;
|
|
||||||
margin-top: 10rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-right {
|
|
||||||
flex: 1;
|
|
||||||
padding-left: 20rpx;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.coupon-name {
|
|
||||||
font-size: 32rpx;
|
|
||||||
color: #333;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 16rpx;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-time {
|
|
||||||
font-size: 22rpx;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.coupon-status-stamp {
|
|
||||||
position: absolute;
|
|
||||||
right: -20rpx;
|
|
||||||
top: 20rpx;
|
|
||||||
width: 120rpx;
|
|
||||||
height: 40rpx;
|
|
||||||
line-height: 40rpx;
|
|
||||||
text-align: center;
|
|
||||||
background-color: rgba(153, 153, 153, 0.1);
|
|
||||||
color: #999;
|
|
||||||
font-size: 20rpx;
|
|
||||||
transform: rotate(45deg);
|
|
||||||
transform-origin: center;
|
|
||||||
border: 1rpx solid #999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="rules-page">
|
<view class="rules-page">
|
||||||
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
<NavHeader title="积分会员制度" />
|
||||||
<NavHeader title="积分会员制度" />
|
|
||||||
</view>
|
|
||||||
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
|
||||||
<scroll-view class="content-scroll" scroll-y="true">
|
<scroll-view class="content-scroll" scroll-y="true">
|
||||||
<!-- 一、会员等级与积分门槛 -->
|
<!-- 一、会员等级与积分门槛 -->
|
||||||
<view class="section">
|
<view class="section">
|
||||||
|
|
@ -69,7 +67,6 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -80,7 +77,6 @@ export default {
|
||||||
components: { NavHeader },
|
components: { NavHeader },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
statusBarHeight: 0,
|
|
||||||
levelRows: [
|
levelRows: [
|
||||||
{ level: "初级", points: "0", name: "筑巢会员" },
|
{ level: "初级", points: "0", name: "筑巢会员" },
|
||||||
{ level: "中级", points: "1000", name: "安居会员" },
|
{ level: "中级", points: "1000", name: "安居会员" },
|
||||||
|
|
@ -116,49 +112,24 @@ export default {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
headerHeight() {
|
|
||||||
return this.statusBarHeight + 44;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
onLoad() {
|
|
||||||
const systemInfo = uni.getSystemInfoSync();
|
|
||||||
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.rules-page {
|
.rules-page {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background: radial-gradient(0% 0% at 0% 0%, #ffffff 0%, #e2e8f1 100%);
|
background-color: #ffffff;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-fixed-wrapper {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
|
||||||
background: radial-gradient(0% 0% at 0% 0%, #ffffff 0%, #e2e8f1 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-wrap {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-scroll {
|
.content-scroll {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0;
|
height: 0;
|
||||||
padding: 20rpx 24rpx 40rpx;
|
padding: 20rpx 24rpx 40rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: radial-gradient(0% 0% at 0% 0%, #ffffff 0%, #e2e8f1 100%);
|
background: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section {
|
.section {
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="privacy-page">
|
<view class="privacy-page">
|
||||||
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
<NavHeader title="隐私政策" />
|
||||||
<NavHeader title="隐私政策" />
|
|
||||||
</view>
|
|
||||||
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
|
||||||
<scroll-view class="content" scroll-y="true">
|
<scroll-view class="content" scroll-y="true">
|
||||||
<view class="privacy-content">
|
<view class="privacy-content">
|
||||||
<text class="privacy-text">{{ privacyText }}</text>
|
<text class="privacy-text">{{ privacyText }}</text>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -22,7 +18,6 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
statusBarHeight: 0,
|
|
||||||
privacyText: `隐私政策
|
privacyText: `隐私政策
|
||||||
|
|
||||||
本隐私政策(以下简称"本政策")由广厦千万间司机公会(以下简称"我们")制定,旨在说明我们通过运营的微信小程序(以下简称"小程序")向您(以下简称"用户")提供餐饮、维修、保险、健康等服务过程中,对用户个人信息的收集、使用、存储、传输、共享、披露及保护等相关事宜。您在使用我们的服务时,即视为您已充分阅读、理解并同意本政策的全部内容,包括我们对本政策的后续更新。如您不同意本政策,请勿使用我们的服务。
|
本隐私政策(以下简称"本政策")由广厦千万间司机公会(以下简称"我们")制定,旨在说明我们通过运营的微信小程序(以下简称"小程序")向您(以下简称"用户")提供餐饮、维修、保险、健康等服务过程中,对用户个人信息的收集、使用、存储、传输、共享、披露及保护等相关事宜。您在使用我们的服务时,即视为您已充分阅读、理解并同意本政策的全部内容,包括我们对本政策的后续更新。如您不同意本政策,请勿使用我们的服务。
|
||||||
|
|
@ -35,13 +30,13 @@ export default {
|
||||||
为向您提供高质量的服务,我们仅收集为实现服务目的所必需的个人信息,具体包括:
|
为向您提供高质量的服务,我们仅收集为实现服务目的所必需的个人信息,具体包括:
|
||||||
1.基本身份信息:包括您的姓名、身份证号、联系电话等,用于完成身份核验、建立服务关系;
|
1.基本身份信息:包括您的姓名、身份证号、联系电话等,用于完成身份核验、建立服务关系;
|
||||||
2.车辆相关信息:包括您的车牌号、车辆行驶证信息等,用于为您精准匹配维修、保险等服务;
|
2.车辆相关信息:包括您的车牌号、车辆行驶证信息等,用于为您精准匹配维修、保险等服务;
|
||||||
3.服务相关信息:包括您的餐饮偏好、维修需求、保险配置需求、健康状况(仅收集服务必需的部分)、服务订单记录、费用结算记录、评价反馈等,用于处理订单、优化服务体验;
|
3.服务相关信息:包括您的餐饮偏好、维修需求、保险配置需求、健康状况(仅收集服务必需的部分)、服务订单记录、费用支付记录、评价反馈等,用于处理订单、优化服务体验;
|
||||||
4.其他必要信息:为保障服务安全、履行法律法规义务所必需的其他信息。
|
4.其他必要信息:为保障服务安全、履行法律法规义务所必需的其他信息。
|
||||||
|
|
||||||
(二)收集方式
|
(二)收集方式
|
||||||
1.您主动提供:您在小程序注册账号、提交服务申请、填写资料、购买服务、发表评价时,主动向我们提供的个人信息;
|
1.您主动提供:您在小程序注册账号、提交服务申请、填写资料、购买服务、发表评价时,主动向我们提供的个人信息;
|
||||||
2.服务过程获取:在为您提供服务的过程中,我们通过小程序系统自动记录的相关信息,如服务使用记录、结算记录等;
|
2.服务过程获取:在为您提供服务的过程中,我们通过小程序系统自动记录的相关信息,如服务使用记录、支付记录等;
|
||||||
3.第三方协助获取:在取得您明确授权的前提下,我们从合法的第三方机构(如保险机构、结算机构、合作维修商家等)获取的必要信息,用于完成服务对接;
|
3.第三方协助获取:在取得您明确授权的前提下,我们从合法的第三方机构(如保险机构、支付机构、合作维修商家等)获取的必要信息,用于完成服务对接;
|
||||||
4.其他合法方式:依据法律法规规定或有权机关要求,合法收集的相关信息。
|
4.其他合法方式:依据法律法规规定或有权机关要求,合法收集的相关信息。
|
||||||
|
|
||||||
三、个人信息的使用目的
|
三、个人信息的使用目的
|
||||||
|
|
@ -73,7 +68,7 @@ export default {
|
||||||
(一)信息共享
|
(一)信息共享
|
||||||
我们不会随意向第三方共享您的个人信息,除非符合以下情形:
|
我们不会随意向第三方共享您的个人信息,除非符合以下情形:
|
||||||
1.经您明确同意或授权:我们将在您授权的范围内,向您指定的第三方共享必要的个人信息;
|
1.经您明确同意或授权:我们将在您授权的范围内,向您指定的第三方共享必要的个人信息;
|
||||||
2.服务提供必需:为向您提供约定的服务,需向合作的第三方机构(如保险机构、维修商家、结算机构等)共享必要的个人信息,且该第三方已签署保密协议,承诺严格保护您的个人信息;
|
2.服务提供必需:为向您提供约定的服务,需向合作的第三方机构(如保险机构、维修商家、支付机构等)共享必要的个人信息,且该第三方已签署保密协议,承诺严格保护您的个人信息;
|
||||||
3.法律法规要求:依据法律法规的规定、司法机关或行政机关的合法要求,向相关部门披露或共享个人信息;
|
3.法律法规要求:依据法律法规的规定、司法机关或行政机关的合法要求,向相关部门披露或共享个人信息;
|
||||||
4.保护合法权益:为保护我们的合法权益、服务秩序或社会公共利益,在合理必要的范围内共享个人信息;
|
4.保护合法权益:为保护我们的合法权益、服务秩序或社会公共利益,在合理必要的范围内共享个人信息;
|
||||||
5.匿名化处理:经过匿名化处理的信息,因其已无法识别您的身份,共享此类信息无需经过您的同意。
|
5.匿名化处理:经过匿名化处理的信息,因其已无法识别您的身份,共享此类信息无需经过您的同意。
|
||||||
|
|
@ -107,16 +102,7 @@ export default {
|
||||||
|
|
||||||
广厦千万间司机公会`
|
广厦千万间司机公会`
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
computed: {
|
|
||||||
headerHeight() {
|
|
||||||
return this.statusBarHeight + 44;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
onLoad() {
|
|
||||||
const systemInfo = uni.getSystemInfoSync();
|
|
||||||
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -128,22 +114,6 @@ export default {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-fixed-wrapper {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-wrap {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 20rpx;
|
padding: 20rpx;
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="real-name-auth-page">
|
<view class="real-name-auth-page">
|
||||||
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
<!-- 头部区域 -->
|
||||||
<NavHeader title="实名认证" />
|
<NavHeader title="实名认证" />
|
||||||
</view>
|
|
||||||
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
|
||||||
<!-- 加载中 -->
|
|
||||||
<view class="loading-wrap" v-if="realNameLoading">
|
|
||||||
<text class="loading-text">加载实名信息中...</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 表单内容区 -->
|
<!-- 表单内容区 -->
|
||||||
<scroll-view class="form-content" scroll-y="true" v-else>
|
<scroll-view class="form-content" scroll-y="true">
|
||||||
<!-- 基本信息 -->
|
<!-- 基本信息 -->
|
||||||
<view class="form-section">
|
<view class="form-section">
|
||||||
<view class="section-title">基本信息</view>
|
<view class="section-title">基本信息</view>
|
||||||
|
|
||||||
<!-- 实名状态提示(不可编辑时显示) -->
|
|
||||||
<view class="status-tip" v-if="!formEditable && realNameStatus != null">
|
|
||||||
<text class="status-tip-text">当前状态:{{ realNameStatusText }},不可修改</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 真实姓名 -->
|
<!-- 真实姓名 -->
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<view class="input-wrapper">
|
<view class="input-wrapper">
|
||||||
|
|
@ -30,7 +19,6 @@
|
||||||
v-model="formData.realName"
|
v-model="formData.realName"
|
||||||
placeholder="请输入真实姓名"
|
placeholder="请输入真实姓名"
|
||||||
maxlength="20"
|
maxlength="20"
|
||||||
:disabled="!formEditable"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -40,7 +28,6 @@
|
||||||
<view class="input-wrapper">
|
<view class="input-wrapper">
|
||||||
<text class="input-label">证件类型</text>
|
<text class="input-label">证件类型</text>
|
||||||
<picker
|
<picker
|
||||||
v-if="formEditable"
|
|
||||||
mode="selector"
|
mode="selector"
|
||||||
:range="idTypeOptions"
|
:range="idTypeOptions"
|
||||||
range-key="label"
|
range-key="label"
|
||||||
|
|
@ -53,7 +40,6 @@
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
</picker>
|
</picker>
|
||||||
<text v-else class="picker-text readonly">{{ idTypeLabel }}</text>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
|
@ -67,7 +53,6 @@
|
||||||
v-model="formData.idNumber"
|
v-model="formData.idNumber"
|
||||||
placeholder="请输入证件号码"
|
placeholder="请输入证件号码"
|
||||||
maxlength="30"
|
maxlength="30"
|
||||||
:disabled="!formEditable"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -81,7 +66,7 @@
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<view class="upload-wrapper">
|
<view class="upload-wrapper">
|
||||||
<text class="upload-label">身份证正面</text>
|
<text class="upload-label">身份证正面</text>
|
||||||
<view class="upload-box" :class="{ disabled: !formEditable }" @click="formEditable && chooseImage('idFrontImg')">
|
<view class="upload-box" @click="chooseImage('idFrontImg')">
|
||||||
<image
|
<image
|
||||||
v-if="formData.idFrontImg"
|
v-if="formData.idFrontImg"
|
||||||
class="uploaded-image"
|
class="uploaded-image"
|
||||||
|
|
@ -100,7 +85,7 @@
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<view class="upload-wrapper">
|
<view class="upload-wrapper">
|
||||||
<text class="upload-label">身份证背面</text>
|
<text class="upload-label">身份证背面</text>
|
||||||
<view class="upload-box" :class="{ disabled: !formEditable }" @click="formEditable && chooseImage('idBackImg')">
|
<view class="upload-box" @click="chooseImage('idBackImg')">
|
||||||
<image
|
<image
|
||||||
v-if="formData.idBackImg"
|
v-if="formData.idBackImg"
|
||||||
class="uploaded-image"
|
class="uploaded-image"
|
||||||
|
|
@ -124,7 +109,7 @@
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<view class="upload-wrapper">
|
<view class="upload-wrapper">
|
||||||
<text class="upload-label">驾驶证正面</text>
|
<text class="upload-label">驾驶证正面</text>
|
||||||
<view class="upload-box" :class="{ disabled: !formEditable }" @click="formEditable && chooseImage('driverLicenseFrontImg')">
|
<view class="upload-box" @click="chooseImage('driverLicenseFrontImg')">
|
||||||
<image
|
<image
|
||||||
v-if="formData.driverLicenseFrontImg"
|
v-if="formData.driverLicenseFrontImg"
|
||||||
class="uploaded-image"
|
class="uploaded-image"
|
||||||
|
|
@ -143,7 +128,7 @@
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<view class="upload-wrapper">
|
<view class="upload-wrapper">
|
||||||
<text class="upload-label">驾驶证背面</text>
|
<text class="upload-label">驾驶证背面</text>
|
||||||
<view class="upload-box" :class="{ disabled: !formEditable }" @click="formEditable && chooseImage('driverLicenseBackImg')">
|
<view class="upload-box" @click="chooseImage('driverLicenseBackImg')">
|
||||||
<image
|
<image
|
||||||
v-if="formData.driverLicenseBackImg"
|
v-if="formData.driverLicenseBackImg"
|
||||||
class="uploaded-image"
|
class="uploaded-image"
|
||||||
|
|
@ -167,7 +152,7 @@
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<view class="upload-wrapper">
|
<view class="upload-wrapper">
|
||||||
<text class="upload-label">行驶证正面</text>
|
<text class="upload-label">行驶证正面</text>
|
||||||
<view class="upload-box" :class="{ disabled: !formEditable }" @click="formEditable && chooseImage('vehicleLicenseFrontImg')">
|
<view class="upload-box" @click="chooseImage('vehicleLicenseFrontImg')">
|
||||||
<image
|
<image
|
||||||
v-if="formData.vehicleLicenseFrontImg"
|
v-if="formData.vehicleLicenseFrontImg"
|
||||||
class="uploaded-image"
|
class="uploaded-image"
|
||||||
|
|
@ -186,7 +171,7 @@
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<view class="upload-wrapper">
|
<view class="upload-wrapper">
|
||||||
<text class="upload-label">行驶证背面</text>
|
<text class="upload-label">行驶证背面</text>
|
||||||
<view class="upload-box" :class="{ disabled: !formEditable }" @click="formEditable && chooseImage('vehicleLicenseBackImg')">
|
<view class="upload-box" @click="chooseImage('vehicleLicenseBackImg')">
|
||||||
<image
|
<image
|
||||||
v-if="formData.vehicleLicenseBackImg"
|
v-if="formData.vehicleLicenseBackImg"
|
||||||
class="uploaded-image"
|
class="uploaded-image"
|
||||||
|
|
@ -214,14 +199,13 @@
|
||||||
placeholder="请输入备注信息(选填)"
|
placeholder="请输入备注信息(选填)"
|
||||||
maxlength="200"
|
maxlength="200"
|
||||||
:auto-height="true"
|
:auto-height="true"
|
||||||
:disabled="!formEditable"
|
|
||||||
></textarea>
|
></textarea>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 协议同意区域(仅可编辑时显示) -->
|
<!-- 协议同意区域 -->
|
||||||
<view class="agreement-section" v-if="formEditable">
|
<view class="agreement-section">
|
||||||
<view class="agreement-checkbox" @click="toggleAgreement">
|
<view class="agreement-checkbox" @click="toggleAgreement">
|
||||||
<view class="checkbox-icon" :class="{ checked: agreedToTerms }">
|
<view class="checkbox-icon" :class="{ checked: agreedToTerms }">
|
||||||
<text v-if="agreedToTerms" class="checkmark">✓</text>
|
<text v-if="agreedToTerms" class="checkmark">✓</text>
|
||||||
|
|
@ -235,8 +219,8 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 提交按钮(仅可编辑时显示) -->
|
<!-- 提交按钮 -->
|
||||||
<view class="submit-section" v-if="formEditable">
|
<view class="submit-section">
|
||||||
<button
|
<button
|
||||||
class="submit-btn"
|
class="submit-btn"
|
||||||
:class="{ disabled: loading || !agreedToTerms }"
|
:class="{ disabled: loading || !agreedToTerms }"
|
||||||
|
|
@ -247,12 +231,11 @@
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { createRealNameInfo, getRealNameInfo } from '@/api/profile.js'
|
import { createRealNameInfo } from '@/api/profile.js'
|
||||||
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -261,11 +244,8 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
statusBarHeight: 0,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
realNameLoading: true, // 实名信息加载中
|
agreedToTerms: false, // 是否同意协议
|
||||||
realNameStatus: null, // 实名状态:0 未认证/没有 1 待审核 2 已通过 3 已退回 4 已过期
|
|
||||||
agreedToTerms: false, // 是否同意协议
|
|
||||||
idTypeIndex: 0,
|
idTypeIndex: 0,
|
||||||
idTypeOptions: [
|
idTypeOptions: [
|
||||||
{ label: '身份证', value: 1 },
|
{ label: '身份证', value: 1 },
|
||||||
|
|
@ -285,79 +265,9 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
headerHeight() {
|
|
||||||
return this.statusBarHeight + 44
|
|
||||||
},
|
|
||||||
// 仅 没有(无数据/0)、退回(3)、过期(4) 可编辑;待审核(1)、已通过(2) 不可编辑
|
|
||||||
formEditable() {
|
|
||||||
const s = this.realNameStatus
|
|
||||||
if (s == null) return true
|
|
||||||
return s === 0 || s === 3 || s === 4
|
|
||||||
},
|
|
||||||
realNameStatusText() {
|
|
||||||
const map = { 0: '未认证', 1: '待审核', 2: '已通过', 3: '已退回', 4: '已过期' }
|
|
||||||
return map[this.realNameStatus] ?? '未知'
|
|
||||||
},
|
|
||||||
idTypeLabel() {
|
|
||||||
if (this.formData.idType == null) return '请选择证件类型'
|
|
||||||
const opt = this.idTypeOptions.find(item => item.value === this.formData.idType)
|
|
||||||
return opt ? opt.label : '请选择证件类型'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
const systemInfo = uni.getSystemInfoSync()
|
|
||||||
this.statusBarHeight = systemInfo.statusBarHeight || 0
|
|
||||||
this.loadRealNameInfo()
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async loadRealNameInfo() {
|
|
||||||
this.realNameLoading = true
|
|
||||||
try {
|
|
||||||
const userInfo = uni.getStorageSync('userInfo')
|
|
||||||
if (!userInfo || !userInfo.id) {
|
|
||||||
this.realNameStatus = 0
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const res = await getRealNameInfo({ id: userInfo.id })
|
|
||||||
this.fillFormFromApi(res)
|
|
||||||
} catch (e) {
|
|
||||||
console.error('获取实名信息失败:', e)
|
|
||||||
this.realNameStatus = 0
|
|
||||||
} finally {
|
|
||||||
this.realNameLoading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fillFormFromApi(data) {
|
|
||||||
if (!data || typeof data !== 'object') {
|
|
||||||
this.realNameStatus = 0
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const s = data.status != null ? Number(data.status) : (data.id ? 1 : 0)
|
|
||||||
this.realNameStatus = s
|
|
||||||
this.formData.realName = data.realName ?? ''
|
|
||||||
this.formData.idType = data.idType != null ? data.idType : null
|
|
||||||
this.formData.idNumber = data.idNumber ?? ''
|
|
||||||
this.formData.idFrontImg = data.idFrontImg ?? ''
|
|
||||||
this.formData.idBackImg = data.idBackImg ?? ''
|
|
||||||
this.formData.driverLicenseFrontImg = data.driverLicenseFrontImg ?? ''
|
|
||||||
this.formData.driverLicenseBackImg = data.driverLicenseBackImg ?? ''
|
|
||||||
this.formData.vehicleLicenseFrontImg = data.vehicleLicenseFrontImg ?? ''
|
|
||||||
this.formData.vehicleLicenseBackImg = data.vehicleLicenseBackImg ?? ''
|
|
||||||
this.formData.imgVerifyRemark = data.imgVerifyRemark ?? ''
|
|
||||||
const idx = this.idTypeOptions.findIndex(item => item.value === this.formData.idType)
|
|
||||||
this.idTypeIndex = idx >= 0 ? idx : 0
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleAgreement() {
|
|
||||||
this.agreedToTerms = !this.agreedToTerms
|
|
||||||
},
|
|
||||||
goToUserAgreement() {
|
|
||||||
uni.navigateTo({ url: '/pages/profileSub/userAgreement' })
|
|
||||||
},
|
|
||||||
goToPrivacyPolicy() {
|
|
||||||
uni.navigateTo({ url: '/pages/profileSub/privacyPolicy' })
|
|
||||||
},
|
|
||||||
|
|
||||||
// 证件类型选择
|
// 证件类型选择
|
||||||
handleIdTypeChange(e) {
|
handleIdTypeChange(e) {
|
||||||
|
|
@ -520,34 +430,6 @@ export default {
|
||||||
background: #e2e8f1;
|
background: #e2e8f1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-fixed-wrapper {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
|
||||||
background: #e2e8f1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-wrap {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-wrap {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 80rpx 0;
|
|
||||||
.loading-text {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-content {
|
.form-content {
|
||||||
height: calc(100vh - 120rpx);
|
height: calc(100vh - 120rpx);
|
||||||
padding: 0 30rpx 40rpx;
|
padding: 0 30rpx 40rpx;
|
||||||
|
|
@ -564,17 +446,6 @@ export default {
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
padding-left: 10rpx;
|
padding-left: 10rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-tip {
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
padding: 16rpx 20rpx;
|
|
||||||
background: rgba(255, 152, 0, 0.1);
|
|
||||||
border-radius: 12rpx;
|
|
||||||
.status-tip-text {
|
|
||||||
font-size: 26rpx;
|
|
||||||
color: #e65100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-item {
|
.form-item {
|
||||||
|
|
@ -616,10 +487,6 @@ export default {
|
||||||
&.placeholder {
|
&.placeholder {
|
||||||
color: #999999;
|
color: #999999;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.readonly {
|
|
||||||
color: #666666;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.picker-arrow {
|
.picker-arrow {
|
||||||
|
|
@ -679,11 +546,6 @@ export default {
|
||||||
color: #999999;
|
color: #999999;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 0.85;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="service-records-page">
|
<view class="service-records-page">
|
||||||
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
<!-- 头部区域 -->
|
||||||
<NavHeader title="服务记录" />
|
<NavHeader title="服务记录" />
|
||||||
</view>
|
|
||||||
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
|
||||||
<!-- Tab 切换 -->
|
<!-- Tab 切换 -->
|
||||||
<view class="tab-bar">
|
<view class="tab-bar">
|
||||||
<view
|
<view
|
||||||
|
|
@ -11,21 +10,21 @@
|
||||||
:class="{ active: currentTab === 'pending_payment' }"
|
:class="{ active: currentTab === 'pending_payment' }"
|
||||||
@click="switchTab('pending_payment')"
|
@click="switchTab('pending_payment')"
|
||||||
>
|
>
|
||||||
<text class="tab-text">待结算</text>
|
<text class="tab-text">待支付</text>
|
||||||
</view>
|
</view>
|
||||||
<view
|
<view
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
:class="{ active: currentTab === 'pending_verification' }"
|
:class="{ active: currentTab === 'pending_verification' }"
|
||||||
@click="switchTab('pending_verification')"
|
@click="switchTab('pending_verification')"
|
||||||
>
|
>
|
||||||
<text class="tab-text">待核销</text>
|
<text class="tab-text">已完成</text>
|
||||||
</view>
|
</view>
|
||||||
<view
|
<view
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
:class="{ active: currentTab === 'completed' }"
|
:class="{ active: currentTab === 'chargeback' }"
|
||||||
@click="switchTab('completed')"
|
@click="switchTab('chargeback')"
|
||||||
>
|
>
|
||||||
<text class="tab-text">已完成</text>
|
<text class="tab-text">已退款</text>
|
||||||
</view>
|
</view>
|
||||||
<view
|
<view
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
|
|
@ -78,13 +77,13 @@
|
||||||
<view
|
<view
|
||||||
class="record-goods"
|
class="record-goods"
|
||||||
v-for="(goods, gIndex) in getGoodsList(item)"
|
v-for="(goods, gIndex) in getGoodsList(item)"
|
||||||
:key="goods.id || goods.voucherId || gIndex"
|
:key="goods.id || goods.couponId || gIndex"
|
||||||
>
|
>
|
||||||
<image
|
<image
|
||||||
class="goods-image"
|
class="goods-image"
|
||||||
:src="
|
:src="
|
||||||
goods.coverUrl ||
|
goods.coverUrl ||
|
||||||
goods.voucherCoverUrl ||
|
goods.couponCoverUrl ||
|
||||||
goods.picUrl ||
|
goods.picUrl ||
|
||||||
'/static/home/entry_icon.png'
|
'/static/home/entry_icon.png'
|
||||||
"
|
"
|
||||||
|
|
@ -92,7 +91,7 @@
|
||||||
></image>
|
></image>
|
||||||
<view class="goods-info">
|
<view class="goods-info">
|
||||||
<text class="goods-title">{{
|
<text class="goods-title">{{
|
||||||
goods.voucherName || goods.name
|
goods.couponName || goods.name
|
||||||
}}</text>
|
}}</text>
|
||||||
<!-- <text class="goods-subtitle">
|
<!-- <text class="goods-subtitle">
|
||||||
订单号:{{ item.orderNumber }}
|
订单号:{{ item.orderNumber }}
|
||||||
|
|
@ -120,7 +119,6 @@
|
||||||
src="/static/home/qr_code_icon.png"
|
src="/static/home/qr_code_icon.png"
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
class="qr-code-icon"
|
class="qr-code-icon"
|
||||||
:class="{ gray: item.status === 4 }"
|
|
||||||
></image>
|
></image>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -144,7 +142,7 @@
|
||||||
取消订单
|
取消订单
|
||||||
</button>
|
</button>
|
||||||
<button class="action-btn pay-btn" @click.stop="handlePay(item)">
|
<button class="action-btn pay-btn" @click.stop="handlePay(item)">
|
||||||
去结算
|
立即支付
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
<view class="record-actions" v-else-if="item.status === 1">
|
<view class="record-actions" v-else-if="item.status === 1">
|
||||||
|
|
@ -207,19 +205,18 @@
|
||||||
<view class="qr-modal-close" @click="closeQrModal">×</view>
|
<view class="qr-modal-close" @click="closeQrModal">×</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="qr-modal-body">
|
<view class="qr-modal-body">
|
||||||
<view class="qr-code-box" v-if="qrModalData.voucherCode">
|
<view class="qr-code-box" v-if="qrModalData.couponCode">
|
||||||
<image
|
<image
|
||||||
class="qr-code-image"
|
class="qr-code-image"
|
||||||
:src="qrCodeImageUrl"
|
:src="qrCodeImageUrl"
|
||||||
mode="aspectFit"
|
mode="aspectFit"
|
||||||
></image>
|
></image>
|
||||||
<!-- <text class="qr-code-text">{{ qrModalData.voucherCode }}</text> -->
|
<!-- <text class="qr-code-text">{{ qrModalData.couponCode }}</text> -->
|
||||||
</view>
|
</view>
|
||||||
<text v-else class="qr-code-empty">暂无券码</text>
|
<text v-else class="qr-code-empty">暂无券码</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -233,7 +230,6 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
statusBarHeight: 0,
|
|
||||||
currentTab: "pending_payment", // 当前选中的 tab
|
currentTab: "pending_payment", // 当前选中的 tab
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|
@ -251,35 +247,31 @@ export default {
|
||||||
qrModalVisible: false,
|
qrModalVisible: false,
|
||||||
qrModalData: {
|
qrModalData: {
|
||||||
useStatus: 0,
|
useStatus: 0,
|
||||||
voucherCode: "",
|
couponCode: "",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
headerHeight() {
|
|
||||||
return this.statusBarHeight + 44;
|
|
||||||
},
|
|
||||||
currentList() {
|
currentList() {
|
||||||
return this.recordsMap[this.currentTab] || [];
|
return this.recordsMap[this.currentTab] || [];
|
||||||
},
|
},
|
||||||
qrCodeImageUrl() {
|
qrCodeImageUrl() {
|
||||||
const code = this.qrModalData.voucherCode || "";
|
const code = this.qrModalData.couponCode || "";
|
||||||
if (!code) return "";
|
if (!code) return "";
|
||||||
return `https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=${encodeURIComponent(code)}`;
|
return `https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=${encodeURIComponent(code)}`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
const systemInfo = uni.getSystemInfoSync();
|
// 如果通过参数传入 tab,优先使用传入的 tab 值
|
||||||
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
|
||||||
if (options && options.tab) {
|
if (options && options.tab) {
|
||||||
this.currentTab = options.tab;
|
this.currentTab = options.tab;
|
||||||
}
|
}
|
||||||
this.loadData();
|
this.loadData();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 一个订单可能包含多个商品(voucherPurchaseRespVOS)
|
// 一个订单可能包含多个商品(couponPurchaseRespVOS)
|
||||||
getGoodsList(order) {
|
getGoodsList(order) {
|
||||||
const list = (order && order.voucherPurchaseRespVOS) || [];
|
const list = (order && order.couponPurchaseRespVOS) || [];
|
||||||
return Array.isArray(list) && list.length ? list : [order || {}];
|
return Array.isArray(list) && list.length ? list : [order || {}];
|
||||||
},
|
},
|
||||||
// 金额分转元(去掉后两位)
|
// 金额分转元(去掉后两位)
|
||||||
|
|
@ -306,7 +298,7 @@ export default {
|
||||||
// 根据当前 tab 映射到接口所需的 status 值
|
// 根据当前 tab 映射到接口所需的 status 值
|
||||||
getStatusValue() {
|
getStatusValue() {
|
||||||
const map = {
|
const map = {
|
||||||
pending_payment: 0, // 待结算
|
pending_payment: 0, // 待支付
|
||||||
pending_verification: 1, // 已完成
|
pending_verification: 1, // 已完成
|
||||||
chargeback: 3, // 已退款
|
chargeback: 3, // 已退款
|
||||||
cancelled: 4, // 已取消
|
cancelled: 4, // 已取消
|
||||||
|
|
@ -324,7 +316,7 @@ export default {
|
||||||
// 获取 Tab 标签文本
|
// 获取 Tab 标签文本
|
||||||
getTabLabel() {
|
getTabLabel() {
|
||||||
const labels = {
|
const labels = {
|
||||||
pending_payment: "待结算",
|
pending_payment: "待支付",
|
||||||
pending_verification: "待核销",
|
pending_verification: "待核销",
|
||||||
completed: "已完成",
|
completed: "已完成",
|
||||||
cancelled: "已取消",
|
cancelled: "已取消",
|
||||||
|
|
@ -334,7 +326,7 @@ export default {
|
||||||
// 获取状态文本
|
// 获取状态文本
|
||||||
getStatusText(status) {
|
getStatusText(status) {
|
||||||
return status === 0
|
return status === 0
|
||||||
? "待结算"
|
? "待支付"
|
||||||
: status === 1
|
: status === 1
|
||||||
? "已完成"
|
? "已完成"
|
||||||
: status === 3
|
: status === 3
|
||||||
|
|
@ -459,30 +451,30 @@ export default {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// 去结算(与店铺详情页 handlePay 逻辑一致,跳转收银台小程序)
|
// 立即支付(与店铺详情页 handlePay 逻辑一致,跳转收银台小程序)
|
||||||
async handlePay(item) {
|
async handlePay(item) {
|
||||||
if (!item || !item.orderNumber) {
|
if (!item || !item.orderNumber) {
|
||||||
uni.showToast({ title: "订单信息异常", icon: "none" });
|
uni.showToast({ title: "订单信息异常", icon: "none" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const voucherArray = item.voucherPurchaseRespVOS || [];
|
const couponArray = item.couponPurchaseRespVOS || [];
|
||||||
const voucherMap = new Map();
|
const couponMap = new Map();
|
||||||
voucherArray.forEach((goods) => {
|
couponArray.forEach((goods) => {
|
||||||
const key = goods.voucherId;
|
const key = goods.couponId;
|
||||||
if (!key) return;
|
if (!key) return;
|
||||||
const currentList = voucherMap.get(key) || [];
|
const currentList = couponMap.get(key) || [];
|
||||||
currentList.push(goods);
|
currentList.push(goods);
|
||||||
voucherMap.set(key, currentList);
|
couponMap.set(key, currentList);
|
||||||
});
|
});
|
||||||
let bodyStr = "";
|
let bodyStr = "";
|
||||||
voucherMap.forEach((voucherList) => {
|
couponMap.forEach((couponList) => {
|
||||||
bodyStr =
|
bodyStr =
|
||||||
bodyStr +
|
bodyStr +
|
||||||
(voucherList[0] && voucherList[0].voucherName
|
(couponList[0] && couponList[0].couponName
|
||||||
? voucherList[0].voucherName
|
? couponList[0].couponName
|
||||||
: "商品") +
|
: "商品") +
|
||||||
"x" +
|
"x" +
|
||||||
voucherList.length +
|
couponList.length +
|
||||||
";";
|
";";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -528,8 +520,8 @@ export default {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("获取签名失败:", e);
|
console.error("获取支付签名失败:", e);
|
||||||
uni.showToast({ title: "结算准备失败,请稍后重试", icon: "none" });
|
uni.showToast({ title: "支付准备失败,请稍后重试", icon: "none" });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 查看详情
|
// 查看详情
|
||||||
|
|
@ -546,15 +538,15 @@ export default {
|
||||||
icon: "none",
|
icon: "none",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// 点击二维码图标:打开弹窗,展示券状态 + 根据 voucherCode 生成二维码
|
// 点击二维码图标:打开弹窗,展示券状态 + 根据 couponCode 生成二维码
|
||||||
handleQrCode(item, goods) {
|
handleQrCode(item, goods) {
|
||||||
const useStatus = goods?.useStatus ?? item?.useStatus ?? 0;
|
const useStatus = goods?.useStatus ?? item?.useStatus ?? 0;
|
||||||
const voucherCode =
|
const couponCode =
|
||||||
(goods && (goods.voucherCode || goods.voucherNo)) ||
|
(goods && (goods.couponCode || goods.couponNo)) ||
|
||||||
item?.voucherCode ||
|
item?.couponCode ||
|
||||||
item?.voucherNo ||
|
item?.couponNo ||
|
||||||
"";
|
"";
|
||||||
this.qrModalData = { useStatus, voucherCode };
|
this.qrModalData = { useStatus, couponCode };
|
||||||
this.qrModalVisible = true;
|
this.qrModalVisible = true;
|
||||||
},
|
},
|
||||||
getQrUseStatusText(useStatus) {
|
getQrUseStatusText(useStatus) {
|
||||||
|
|
@ -585,22 +577,6 @@ export default {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-fixed-wrapper {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
|
||||||
background: #e2e8f1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-wrap {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tab 切换栏 */
|
/* Tab 切换栏 */
|
||||||
.tab-bar {
|
.tab-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -867,11 +843,6 @@ export default {
|
||||||
.qr-code-icon {
|
.qr-code-icon {
|
||||||
width: 44rpx;
|
width: 44rpx;
|
||||||
height: 44rpx;
|
height: 44rpx;
|
||||||
|
|
||||||
&.gray {
|
|
||||||
filter: grayscale(100%);
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="agreement-page">
|
<view class="agreement-page">
|
||||||
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
<NavHeader title="用户服务协议" />
|
||||||
<NavHeader title="用户服务协议" />
|
|
||||||
</view>
|
|
||||||
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
|
||||||
<scroll-view class="content" scroll-y="true">
|
<scroll-view class="content" scroll-y="true">
|
||||||
<view class="agreement-content">
|
<view class="agreement-content">
|
||||||
<text class="agreement-text">{{ agreementText }}</text>
|
<text class="agreement-text">{{ agreementText }}</text>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -22,7 +18,6 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
statusBarHeight: 0,
|
|
||||||
agreementText: `用户服务协议
|
agreementText: `用户服务协议
|
||||||
|
|
||||||
鉴于甲方运营微信小程序(以下简称"小程序"),为乙方提供餐饮、维修、保险、健康等专属收费服务;乙方自愿使用甲方提供的服务,为明确双方权利义务,根据《中华人民共和国民法典》《中华人民共和国个人信息保护法》等相关法律法规,甲乙双方本着平等、自愿、公平、诚实信用的原则,达成如下协议,以资共同遵守。
|
鉴于甲方运营微信小程序(以下简称"小程序"),为乙方提供餐饮、维修、保险、健康等专属收费服务;乙方自愿使用甲方提供的服务,为明确双方权利义务,根据《中华人民共和国民法典》《中华人民共和国个人信息保护法》等相关法律法规,甲乙双方本着平等、自愿、公平、诚实信用的原则,达成如下协议,以资共同遵守。
|
||||||
|
|
@ -35,11 +30,11 @@ export default {
|
||||||
4.健康服务:包括但不限于司机专属健康体检套餐、健康咨询、职业病预防指导等收费服务。
|
4.健康服务:包括但不限于司机专属健康体检套餐、健康咨询、职业病预防指导等收费服务。
|
||||||
1.2 甲方有权根据市场需求、业务发展等情况调整服务内容及收费项目,相关调整将通过小程序公告或短信提前通知乙方。乙方继续使用服务的,视为认可调整后的内容。
|
1.2 甲方有权根据市场需求、业务发展等情况调整服务内容及收费项目,相关调整将通过小程序公告或短信提前通知乙方。乙方继续使用服务的,视为认可调整后的内容。
|
||||||
|
|
||||||
二、服务费用与结算
|
二、服务费用与支付
|
||||||
2.1 收费标准:本协议项下服务均为收费服务,具体收费标准、计费方式、服务明细均在小程序对应服务页面明确公示,乙方可自行查看并选择购买。
|
2.1 收费标准:本协议项下服务均为收费服务,具体收费标准、计费方式、服务明细均在小程序对应服务页面明确公示,乙方可自行查看并选择购买。
|
||||||
2.2 价格调整:甲方可根据市场行情、服务成本等因素调整收费标准,调整后将通过小程序公告提前7个自然日公示,公示期满后生效。
|
2.2 价格调整:甲方可根据市场行情、服务成本等因素调整收费标准,调整后将通过小程序公告提前7个自然日公示,公示期满后生效。
|
||||||
2.3 结算方式:乙方应通过小程序内指定渠道结算费用,结算成功后视为订单确认,甲方依订单约定提供服务。
|
2.3 支付方式:乙方应通过小程序内指定支付渠道(如微信支付)支付费用,支付成功后视为订单确认,甲方依订单约定提供服务。
|
||||||
2.4 套餐续费:套餐类、周期类服务需一次性结算对应周期费用,乙方应在服务期满前按小程序提示续费,逾期未续费的,服务自动终止。
|
2.4 套餐续费:套餐类、周期类服务需一次性支付对应周期费用,乙方应在服务期满前按小程序提示续费,逾期未续费的,服务自动终止。
|
||||||
|
|
||||||
三、费用退还
|
三、费用退还
|
||||||
3.1 因甲方原因(如无法提供约定服务、服务存在重大瑕疵无法补救)导致服务无法履行的,甲方应根据乙方未使用的服务内容或时长,退还相应费用。
|
3.1 因甲方原因(如无法提供约定服务、服务存在重大瑕疵无法补救)导致服务无法履行的,甲方应根据乙方未使用的服务内容或时长,退还相应费用。
|
||||||
|
|
@ -50,14 +45,14 @@ export default {
|
||||||
4.1 乙方权利:
|
4.1 乙方权利:
|
||||||
1.要求甲方按协议及订单约定提供符合标准的服务;
|
1.要求甲方按协议及订单约定提供符合标准的服务;
|
||||||
2.监督服务质量,对服务问题提出改进建议;
|
2.监督服务质量,对服务问题提出改进建议;
|
||||||
3.查询、更正本人的个人信息及服务订单、结算记录;
|
3.查询、更正本人的个人信息及服务订单、支付记录;
|
||||||
4.符合退款条件时,申请退还相应服务费用;
|
4.符合退款条件时,申请退还相应服务费用;
|
||||||
5.按约定申请终止本协议。
|
5.按约定申请终止本协议。
|
||||||
4.2 乙方义务:
|
4.2 乙方义务:
|
||||||
1.向甲方提供真实、准确、完整的个人信息及相关资料,不得提供虚假信息或隐瞒重要事实;
|
1.向甲方提供真实、准确、完整的个人信息及相关资料,不得提供虚假信息或隐瞒重要事实;
|
||||||
2.遵守国家法律法规及甲方发布的服务规则,不得利用服务从事违法违规、损害他人合法权益的活动;
|
2.遵守国家法律法规及甲方发布的服务规则,不得利用服务从事违法违规、损害他人合法权益的活动;
|
||||||
3.妥善保管小程序账号、密码等身份信息,对账号下所有操作行为承担责任,发现账号泄露、被盗用应及时通知甲方;
|
3.妥善保管小程序账号、密码等身份信息,对账号下所有操作行为承担责任,发现账号泄露、被盗用应及时通知甲方;
|
||||||
4.按协议及小程序公示标准,及时、足额结算服务费用;
|
4.按协议及小程序公示标准,及时、足额支付服务费用;
|
||||||
5.配合甲方完成服务必需的信息核验、服务对接等工作。
|
5.配合甲方完成服务必需的信息核验、服务对接等工作。
|
||||||
|
|
||||||
五、甲方权利与义务
|
五、甲方权利与义务
|
||||||
|
|
@ -85,14 +80,14 @@ export default {
|
||||||
1.提供虚假信息、伪造证明材料,影响服务开展的;
|
1.提供虚假信息、伪造证明材料,影响服务开展的;
|
||||||
2.违反协议或甲方服务规则,经通知后限期未改正的;
|
2.违反协议或甲方服务规则,经通知后限期未改正的;
|
||||||
3.利用服务从事违法违规活动的;
|
3.利用服务从事违法违规活动的;
|
||||||
4.逾期结算费用超过15日,经催告仍未结算的;
|
4.逾期支付费用超过15日,经催告仍未支付的;
|
||||||
5.其他严重损害甲方合法权益的行为。
|
5.其他严重损害甲方合法权益的行为。
|
||||||
7.4 因不可抗力、政策调整等不可归责于双方的原因导致协议无法履行的,协议自动终止,双方互不担责,甲方退还乙方未使用部分的服务费用。
|
7.4 因不可抗力、政策调整等不可归责于双方的原因导致协议无法履行的,协议自动终止,双方互不担责,甲方退还乙方未使用部分的服务费用。
|
||||||
|
|
||||||
八、违约责任
|
八、违约责任
|
||||||
8.1 任何一方违反协议约定,给对方造成损失的,应承担全部赔偿责任(包括直接损失、维权产生的律师费、诉讼费等合理费用)。
|
8.1 任何一方违反协议约定,给对方造成损失的,应承担全部赔偿责任(包括直接损失、维权产生的律师费、诉讼费等合理费用)。
|
||||||
8.2 甲方未按约定提供服务的,除退还相应费用外,还应按乙方已结算对应服务费用的10%承担违约金;违约金不足以弥补损失的,补足差额。
|
8.2 甲方未按约定提供服务的,除退还相应费用外,还应按乙方已支付对应服务费用的10%支付违约金;违约金不足以弥补损失的,补足差额。
|
||||||
8.3 乙方未按时结算费用的,每逾期一日按逾期金额的0.5%计违约金;逾期超过15日的,甲方有权终止协议并要求乙方赔偿损失。
|
8.3 乙方未按时支付费用的,每逾期一日按逾期金额的0.5%支付违约金;逾期超过15日的,甲方有权终止协议并要求乙方赔偿损失。
|
||||||
8.4 乙方利用服务从事违法违规活动或损害第三方权益的,法律责任由乙方自行承担,给甲方造成损失的,乙方全额赔偿。
|
8.4 乙方利用服务从事违法违规活动或损害第三方权益的,法律责任由乙方自行承担,给甲方造成损失的,乙方全额赔偿。
|
||||||
|
|
||||||
九、争议解决
|
九、争议解决
|
||||||
|
|
@ -103,16 +98,7 @@ export default {
|
||||||
10.2 甲方通过小程序公告、短信发送的通知、公示,自发布或送达之日起生效,视为已履行告知义务,乙方应及时关注。
|
10.2 甲方通过小程序公告、短信发送的通知、公示,自发布或送达之日起生效,视为已履行告知义务,乙方应及时关注。
|
||||||
10.3 本协议条款被认定为无效或不可执行的,不影响其他条款的效力。`
|
10.3 本协议条款被认定为无效或不可执行的,不影响其他条款的效力。`
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
computed: {
|
|
||||||
headerHeight() {
|
|
||||||
return this.statusBarHeight + 44;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
onLoad() {
|
|
||||||
const systemInfo = uni.getSystemInfoSync();
|
|
||||||
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -124,22 +110,6 @@ export default {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-fixed-wrapper {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-wrap {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 20rpx;
|
padding: 20rpx;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="webview-page">
|
<view class="webview-page">
|
||||||
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
<!-- 头部导航栏 -->
|
||||||
<NavHeader title="网页" />
|
<NavHeader title="网页" />
|
||||||
</view>
|
|
||||||
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
<!-- WebView 容器 -->
|
||||||
<view class="webview-container">
|
<view class="webview-container">
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
<web-view :src="webviewUrl"></web-view>
|
<web-view :src="webviewUrl"></web-view>
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
</view>
|
</view>
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -28,18 +27,10 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
statusBarHeight: 0,
|
|
||||||
webviewUrl: ''
|
webviewUrl: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
headerHeight() {
|
|
||||||
return this.statusBarHeight + 44;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
const systemInfo = uni.getSystemInfoSync();
|
|
||||||
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
|
||||||
// 从路由参数获取要加载的 URL
|
// 从路由参数获取要加载的 URL
|
||||||
if (options.url) {
|
if (options.url) {
|
||||||
this.webviewUrl = decodeURIComponent(options.url);
|
this.webviewUrl = decodeURIComponent(options.url);
|
||||||
|
|
@ -65,22 +56,6 @@ export default {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-fixed-wrapper {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
|
||||||
background: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-wrap {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.webview-container {
|
.webview-container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0; // 配合 flex: 1 使用
|
height: 0; // 配合 flex: 1 使用
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.8 KiB |
Loading…
Reference in New Issue