HaniBlindBox/honey_box/pages/mall/index.vue
zpc aa11230116 feat: 前端UI优化和后台管理功能修复
前端优化:
- 修复分享图片配置,使用API返回的share_image
- 新增设置页面,包含用户协议、隐私政策、退出登录、注销账号
- 调整用户中心菜单,将协议相关功能移至设置页面
- 隐藏首页和详情页的参与次数显示
- 商城页面价格显示改为哈尼券(价格x100)

后台管理修复:
- 修复用户列表货币字段映射错误(Diamond字段)
- 修复资金变动对话框,动态加载货币名称
- 修复ChangeDiamondAsync方法使用正确的Money2字段
2026-02-27 20:37:00 +08:00

644 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<page-container title="商城好物">
<banner :type-id="10" :height="326"></banner>
<view style="padding-bottom: 300rpx; background-color: white;">
<view class="" style="display: flex; flex-direction: column;">
<view class="grid-container"
:style="datas.length === 0 ? 'display:flex;justify-content:center;align-items:center;' : ''">
<empty-state v-if="datas.length === 0" message="暂无商品数据"></empty-state>
<view v-else v-for="(item, index) in datas" :key="index" class="grid-item"
@click="order_money(item)">
<view class=""
style="background-color: #D8D8D8; height: 324rpx; border-radius: 16rpx 16rpx 0rpx 0rpx;">
<image :src="item.imgurl"
style=" width: 326.39rpx; height: 324rpx; position: absolute; left: 2rpx; border-radius: 16rpx"
mode="">
</image>
</view>
<view class="goods-name hang1" style=" width: 290rpx;">
<text style="color: #333333; font-size: 27rpx;">{{ item.title }}</text>
</view>
<view class="price-box">
<view class="price">
<text>{{ item.price * 100 }}<text style="font-size: 20rpx;">{{ $config.getAppSetting('currency2_name') }}</text></text>
</view>
</view>
<view class="num-box">
<view class="num ml10"> {{ item.sale_stock }}/{{ item.stock }} </view>
<view class="box icon">
<image :src="$img1('index/box.png')" lazy-load></image>
</view>
</view>
</view>
</view>
</view>
<uni-popup ref="pop" type="bottom">
<view v-if="orderData" class="order-popup">
<!-- 头部 -->
<view class="popup-header">
<text class="popup-title">确认订单</text>
<view class="close-btn" @click="close('pop')">×</view>
</view>
<!-- 商品信息 -->
<view class="goods-card">
<view class="goods-img">
<image :src="orderData.goods.imgurl_detail" mode="aspectFill"></image>
</view>
<view class="goods-info">
<view class="goods-title">{{ orderData.goods.title }}</view>
<view class="goods-meta">
<text>类型:明信片</text>
<text class="qty">×{{ orderData.goods.prize_num }}</text>
</view>
<view class="goods-price">{{ orderData.goods.price * 100 }}{{ $config.getAppSetting('currency2_name') }}</view>
</view>
</view>
<!-- 货币选项 -->
<view class="option-row" @click="changePay(0)"
v-if="orderData != null && orderData.goods_extend && orderData.goods_extend.pay_wechat == 1">
<view class="label-with-tip">
<text class="label">微信支付</text>
</view>
<view class="radio" :class="{ active: zhifu == 0 }"></view>
</view>
<view class="option-row" @click="changePay(1)"
v-if="orderData != null && orderData.goods_extend && orderData.goods_extend.pay_currency2 == 1">
<view class="label-with-tip">
<text class="label">{{ $config.getAppSetting('currency2_name') }} {{ orderData.goods.price * 100 }}</text>
<text class="tip">(剩余{{ orderData.score }}</text>
</view>
<view class="radio" :class="{ active: zhifu == 1 }"></view>
</view>
<!-- 售前售后须知 -->
<view class="notice-row" @click="$c.to({ url: '/pages/guize/guize?type=10' })">
<text class="notice-text">售前·售后须知</text>
<text class="arrow"></text>
</view>
<!-- 支付按钮 -->
<view class="pay-btn" v-if="zhifu == 0" @click="$c.noDouble(() => confirmSubmit(1))">
<text>确认支付 ¥{{ orderData.price }}</text>
</view>
<view class="pay-btn" v-if="zhifu == 1" @click="$c.noDouble(() => pay(1))">
<text>{{ orderData.price * 100 }}{{ $config.getAppSetting('currency2_name') }}</text>
</view>
<!-- 协议 -->
<view class="agree-row" @click="isAgree = !isAgree">
<view class="check-icon" :class="{ active: isAgree }"></view>
<text class="agree-text">我已满18岁阅读即同意用户协议隐私政策</text>
</view>
</view>
</uni-popup>
<PayDialog ref="payDialog" />
</view>
</page-container>
</template>
<script>
import { getAdvert } from '@/common/server/config.js';
import { getGoodsList } from '@/common/server/goods.js';
import { calcMallOrderMoney, createMallOrder, createOrder } from '@/common/server/order.js';
export default {
components: {
EmptyState: () => import('@/components/empty-state/empty-state.vue')
},
onShareAppMessage() {
let imageUrl = this.$config.getShareImageUrl();
// 使用小写 id兼容新版 .NET API
const userinfo = uni.getStorageSync('userinfo') || {};
const pid = userinfo.id || userinfo.ID || '';
return {
imageUrl: imageUrl,
title: this.$config.getAppSetting('share_title') || "哈尼盲盒上线,来就送!",
path: '/pages/shouye/index?pid=' + pid
}
},
data() {
return {
statusBarHeight: 0,
datas: [],
advert: [],
sendRuleData: '',
goodsId: "",
orderData: null,
isAgree: true,
isWXPay: true,
useMoney: false,
integral: true,
zhifu: 0,
}
},
onLoad() {
// 获取系统信息
uni.getSystemInfo({
success: (res) => {
// 状态栏的高度
this.statusBarHeight = res.statusBarHeight + 20;
}
});
this.getData();
this.load();
this.$c.getRule(10).then(res => {
if (res.status == 1) {
this.sendRuleData = res.data
}
});
},
onShow() {
const curPages = getCurrentPages()[0]; // 获取当前页面实例
if (typeof curPages.getTabBar === 'function' && curPages.getTabBar()) {
this.$mp.page.getTabBar().setData({
selected: 2
});
}
this.$platform.getOrderNo(this).then(order_num => {
if (order_num != null && order_num != "") {
this.$c.msg("购买成功!");
}
})
},
methods: {
async load() {
// API: getAdvert?type_id=3
const res = await getAdvert(3);
if (res) {
this.advert = res;
}
},
getStatusBarHeight() {
const systemInfo = uni.getSystemInfoSync();
this.statusBarHeight = systemInfo.statusBarHeight;
console.log('statusBarHeight', this.statusBarHeight);
},
async getData() {
let isWXPay = this.$config.GetVersion();
// API: goods
const res = await getGoodsList({
type: '10',
page: 1,
use_money_is: this.useMoney ? 1 : 2,
use_integral_is: this.isWXPay ? 1 : 2,
});
if (res.status == 1 && res.data && res.data.data) {
if (isWXPay) {
if (this.datas.length > 0) {
this.datas = this.datas.slice(0, this.datas.length);
}
for (let index = 0; index < res.data.data.length; index++) {
const element = res.data.data[index];
if (element.title.indexOf('京东卡') == -1) {
this.datas.push(element);
}
}
} else {
this.datas = res.data.data;
}
}
},
async order_money(data) {
console.log(234);
// 检查库存
if (data.sale_stock <= 0) {
uni.showToast({
title: '库存不足',
icon: 'none',
});
return;
}
this.goodsId = data.id;
// API: mall_ordermoney
const res = await calcMallOrderMoney({
prize_num: 1,
goods_id: data.id,
num: 1,
});
if (res.status == 1) {
this.orderData = res.data;
this.zhifu = 0;
// 使用 goods_extendAPI 返回的下划线命名)
const goodsExtend = this.orderData.goods_extend || {};
if (goodsExtend.pay_wechat == 1) {
this.zhifu = 0;
} else if (goodsExtend.pay_currency2 == 1) {
this.zhifu = 1;
}
this.$refs.pop.open();
}
},
close(e) {
this.$refs[e].close()
},
changePay(e) {
if (this.zhifu != e) {
this.zhifu = e;
}
if (this.zhifu == 1) {
}
},
async confirmSubmit(type) {
if (!this.useMoney && !this.isWXPay) {
uni.showToast({
title: '请选择支付方式',
icon: 'none',
});
return;
}
console.log("type", type);
let data = {
goods_id: this.goodsId,
prize_num: 1,
num: 1,
use_money_is: 2,
use_integral_is: 2,
coupon_id: "",
use_money2_is: this.useMoney ? 1 : 2
};
let res;
if (type == 1) {
// API: orderbuy
res = await createOrder(data);
} else {
// API: mall_ordermoney
res = await calcMallOrderMoney(data);
}
if (res.status == 1) {
if (type == 0) {
console.log("res.data", res.data);
this.orderData = res.data;
}
if (type == 1) {
this.close('pop');
if (res.data.status == 1) {
const status = await this.$platform.pay({
data: res.data.res
}, this);
if (status == 'success') {
this.getData();
}
} else {
this.$c.toast({
title: res.msg,
duration: 500,
success: () => {
this.getData();
}
});
}
}
}
},
async pay(type) {
var pri = this.orderData.goods.price * 100;
if (this.orderData.score < pri) {
uni.showToast({
title: this.$config.getAppSetting("currency2_name") + '不足',
icon: 'none',
});
return;
}
console.log("type", type);
let data = {
goods_id: this.goodsId,
prize_num: 1,
};
// API: mall_orderbuy
const res = await createMallOrder(data);
if (res.status == 1) {
if (type == 0) {
console.log("res.data", res.data);
this.orderData = res.data;
}
if (type == 1) {
this.close('pop');
this.$c.toast({
title: res.msg || '购买成功',
duration: 500,
success: () => {
this.getData();
}
});
}
} else {
// 购买失败也刷新数据
this.getData();
}
},
},
}
</script>
<style lang="scss" scoped>
.page-title {
width: 100%;
top: 108rpx;
position: absolute;
display: flex;
align-items: center;
font-size: 34rpx;
justify-content: center;
color: black;
z-index: 100;
}
.grid-container {
display: grid;
margin: 33rpx;
padding-bottom: 200rpx;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
min-height: 55vh;
}
.grid-item {
height: 487rpx;
position: relative;
background-color: #F8F8F8;
border-radius: 0rpx 0rpx 16rpx 16rpx;
}
.goods-name {
position: absolute;
left: 25rpx;
font-size: 20rpx;
bottom: 90rpx;
}
.price-box {
display: flex;
position: absolute;
left: 18rpx;
bottom: 24rpx;
justify-content: space-between;
align-items: center;
.price {
font-weight: 500;
font-size: 16rpx;
color: #333333;
}
}
.num-box {
display: flex;
align-items: center;
position: absolute;
bottom: 24rpx;
right: 20rpx;
.num {
font-size: 18rpx;
font-family: Source Han Sans CN;
font-weight: 400;
color: #6C6C6C;
}
.box {
width: 20rpx;
height: 18rpx;
margin-left: 6rpx;
}
}
.order-popup {
background: #FFFFFF;
border-radius: 24rpx 24rpx 0 0;
padding: 0 32rpx 60rpx;
.popup-header {
display: flex;
align-items: center;
justify-content: center;
padding: 32rpx 0;
position: relative;
.popup-title {
position: static;
width: auto;
top: auto;
font-size: 32rpx;
font-weight: 500;
color: #333;
z-index: auto;
}
.close-btn {
position: absolute;
right: 0;
font-size: 48rpx;
color: #999;
line-height: 1;
}
}
.goods-card {
display: flex;
padding: 24rpx;
background: #F7F7F7;
border-radius: 16rpx;
margin-bottom: 24rpx;
.goods-img {
width: 160rpx;
height: 160rpx;
border-radius: 12rpx;
overflow: hidden;
background: #EEE;
flex-shrink: 0;
image {
width: 100%;
height: 100%;
}
}
.goods-info {
flex: 1;
margin-left: 24rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
.goods-title {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.goods-meta {
font-size: 24rpx;
color: #999;
display: flex;
justify-content: space-between;
.qty {
color: #666;
}
}
.goods-price {
font-size: 32rpx;
color: #333;
font-weight: 600;
}
}
}
.option-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 28rpx 0;
border-bottom: 1rpx solid #F0F0F0;
.label-with-tip {
display: flex;
align-items: center;
.label {
font-size: 28rpx;
color: #333;
}
.tip {
font-size: 24rpx;
color: #999;
margin-left: 8rpx;
}
}
.radio {
width: 40rpx;
height: 40rpx;
border: 2rpx solid #DDD;
border-radius: 50%;
flex-shrink: 0;
&.active {
border-color: #03D8F4;
background: #03D8F4;
position: relative;
&::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 16rpx;
height: 16rpx;
background: #FFF;
border-radius: 50%;
}
}
}
}
.notice-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx;
background: #E8F9E9;
border-radius: 12rpx;
margin: 24rpx 0;
.notice-text {
font-size: 28rpx;
color: #2DB84D;
}
.arrow {
font-size: 32rpx;
color: #2DB84D;
}
}
.pay-btn {
width: 100%;
height: 96rpx;
background: #03D8F4;
border-radius: 14rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: 500;
color: #404040;
margin-bottom: 24rpx;
}
.agree-row {
display: flex;
align-items: center;
justify-content: center;
.check-icon {
width: 32rpx;
height: 32rpx;
border: 2rpx solid #DDD;
border-radius: 50%;
margin-right: 12rpx;
flex-shrink: 0;
&.active {
border-color: #03D8F4;
background: #03D8F4;
position: relative;
&::after {
content: '✓';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 20rpx;
color: #FFF;
}
}
}
.agree-text {
font-size: 24rpx;
color: #999;
}
}
}
</style>