1.15提交

This commit is contained in:
zoujiandong
2026-01-15 18:38:21 +08:00
parent 00d0ec8a9b
commit cf6df28e8c
16 changed files with 683 additions and 180 deletions
+114 -60
View File
@@ -16,13 +16,13 @@
</view>
<view class="divider-line"></view>
<view class="form-item">
<view class="form-item" v-if="type==2">
<view class="label">邮箱</view>
<input class="input" v-model="email" type="text" placeholder="用于接收电子卡等信息(可选)" />
</view>
<view class="divider-line"></view>
<view class="form-item select-item" @click="openAreaPicker">
<view class="form-item select-item" @click="openAreaPicker" v-if="type==1">
<view class="label">地址</view>
<view class="input placeholder" v-if="!regionText">请选择地址</view>
<view class="input" v-else>{{ regionText }}</view>
@@ -30,14 +30,15 @@
</view>
<view class="divider-line"></view>
<view class="form-item">
<view class="form-item" v-if="type==1">
<view class="label">详细地址</view>
<input class="input" v-model="detail" placeholder="请输入街道、门牌等详细地址信息" />
</view>
<view class="divider-line"></view>
</scroll-view>
<view class="footer-bar" @click="submit">
<view class="confirm-btn" >确定</view>
<view class="confirm-btn" >确定兑换</view>
</view>
<!-- 省市区选择器 -->
@@ -62,20 +63,80 @@
<view v-else class="picker-empty">地区数据加载中...</view>
</view>
</view>
<unidialog
:title="'确认收货信息'"
:visible="freeVisible"
:content="freeContent"
@close="freeClose"
@confirm="freeConfirm"
>
<template #content>
<view>
<view class="row">
<view class="left">收件人:</view>
<view class="right">{{ receiver }}</view>
</view>
<view class="row">
<view class="left">手机号:</view>
<view class="right">{{ mobile }}</view>
</view>
<view class="row" >
<view class="left">地址:</view>
<view class="right">{{ regionText+detail }}</view>
</view>
<view class="row">
<view class="left">兑换积分:</view>
<view class="right"><text class="price">{{ points }}积分</text>(包邮)</view>
</view>
<view class="row">请确认此次兑换,物品不退不换</view>
</view>
</template>
</unidialog>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import areaList from '@/utils/areaList.js'
import api from '@/api/api.js'
import navTo from '@/utils/navTo';
import goods_api from '@/api/goods_api';
import unidialog from '@/components/dialog/dialog.vue';
const freeVisible = ref(false)
const freeContent = ref('')
const goods_order_id = ref('')
const points = ref(0)
const freeClose = () => {
freeVisible.value = false
}
const freeConfirm = () => {
freeVisible.value = false;
goods_api.payGoodsOrder({
goods_order_uuid: goods_order_id.value,
}).then(res => {
if(res.code == 1 || res.code == 200){
uni.showToast({
title: '兑换成功',
icon: 'none',
duration: 2000
});
setTimeout(() => {
uni.navigateBack({
delta: 2
})
}, 2000)
}
})
}
const receiver = ref('')
const mobile = ref('')
const regionText = ref('')
const detail = ref('')
const email = ref('')
const editingId = ref(null)
const goodsUuid = ref('')
const type = ref(1)
const goodsNum = ref(1)
const goBack = () => uni.navigateBack()
// 省市区数据(适配树形数组:[{code,label,value,children:[...] }...])
@@ -142,69 +203,42 @@ const confirmArea = () => {
}
onLoad((opts) => {
if (opts && opts.id) {
// 回填编辑
editingId.value = Number(opts.id)
try {
const list = uni.getStorageSync('goods_addresses') || []
const target = Array.isArray(list) ? list.find(a => a.id === editingId.value) : null
if (target) {
receiver.value = target.receiver || ''
mobile.value = target.mobile || ''
regionText.value = target.region || ''
detail.value = target.detail || ''
email.value = target.email || ''
}
} catch (e) {}
}
console.log(22);
console.log(opts);
goodsUuid.value = opts.goodsUuid
goodsNum.value = opts.goodsNum;
type.value = opts.type;
})
const submit = () => {
if (!receiver.value) return uni.showToast({ title: '请输入收件人', icon: 'none' })
if (!/^1\d{10}$/.test(mobile.value)) return uni.showToast({ title: '请输入正确手机号', icon: 'none' })
if (!regionText.value) return uni.showToast({ title: '请选择地址', icon: 'none' })
if (!detail.value) return uni.showToast({ title: '请输入详细地址', icon: 'none' })
if (email.value && !/^([a-zA-Z0-9_\.-]+)@([a-zA-Z0-9\.-]+)\.([a-zA-Z]{2,})$/.test(email.value)) return uni.showToast({ title: '邮箱格式不正确', icon: 'none' })
const STORAGE_KEY = 'goods_addresses'
let list = []
try {
const cached = uni.getStorageSync(STORAGE_KEY)
list = Array.isArray(cached) ? cached : []
} catch (e) { list = [] }
if (editingId.value) {
// 更新
list = list.map(a => a.id === editingId.value ? {
...a,
receiver: receiver.value,
mobile: mobile.value,
region: regionText.value,
detail: detail.value,
fullAddress: `${regionText.value} ${detail.value}`,
email: email.value
} : a)
} else {
// 新增
const address = {
id: Date.now(),
receiver: receiver.value,
mobile: mobile.value,
region: regionText.value,
detail: detail.value,
fullAddress: `${regionText.value} ${detail.value}`,
email: email.value,
createdAt: Date.now()
}
list.unshift(address)
if(type.value == 1){
if (!regionText.value) return uni.showToast({ title: '请选择地址', icon: 'none' })
if (!detail.value) return uni.showToast({ title: '请输入详细地址', icon: 'none' })
}else{
if (email.value && !/^([a-zA-Z0-9_\.-]+)@([a-zA-Z0-9\.-]+)\.([a-zA-Z]{2,})$/.test(email.value)) return uni.showToast({ title: '邮箱格式不正确', icon: 'none' })
}
uni.setStorageSync(STORAGE_KEY, list)
uni.showToast({ title: '提交成功', icon: 'none' })
setTimeout(() => { uni.navigateBack() }, 500)
goods_api.createGoodsOrder({
goodsUuid: goodsUuid.value,
goodsNum: goodsNum.value,
user_name: receiver.value,
mobile: mobile.value,
email:email.value,
address: regionText.value+detail.value
}).then(res => {
if(res.code == 1 || res.code == 200){
points.value = res.data.pionts*res.data.goods_num;
freeVisible.value = true
goods_order_id.value = res.data.goods_order_id
}
})
}
</script>
<style scoped>
<style scoped lang="scss">
.address-page { min-height: 100vh; background: #fff; }
.content { position: absolute; top: calc(var(--status-bar-height) + 44px); bottom: 120rpx; left: 0; right: 0; background: #fff; }
.form-item { display: flex; align-items: center; padding: 24rpx; }
@@ -227,5 +261,25 @@ const submit = () => {
.picker-view { height: 480rpx; }
.picker-item { height: 80rpx; line-height: 80rpx; text-align: center; color: #333; }
.picker-empty { padding: 40rpx; text-align: center; color: #999; }
.row{
display: flex;
align-items: center;
font-size: 28rpx;
padding: 12rpx 0;
max-width: 800rpx;
margin-left: -100rpx;
.left{
text-align: left;
max-width: 125rpx;
width:auto!important;
}
.price{
color: #FF4D4F;
}
}
.left{
width: 160rpx;
}
</style>
+56 -41
View File
@@ -5,7 +5,7 @@
<scroll-view scroll-y class="content">
<!-- 收货地址 -->
<view class="addr-card" @click="changeAddress">
<!-- <view class="addr-card" @click="changeAddress">
<view class="addr-header">
<text class="addr-title">收货信息</text>
<text class="addr-change">{{ selectedAddress ? '更换' : '添加' }}</text>
@@ -15,7 +15,7 @@
<text class="addr-row small">{{ selectedAddress.fullAddress || (selectedAddress.region + ' ' + selectedAddress.detail) }}</text>
</view>
<view v-else class="addr-empty">请添加收货地址</view>
</view>
</view> -->
<view class="goods-title">{{ title }}</view>
<view class="price">{{ price }}积分</view>
@@ -37,16 +37,38 @@
</view>
</scroll-view>
<view class="footer-bar">
<view class="confirm-btn" @click="goAddress">我要兑换</view>
<view class="footer-bar" @click="goAddress">
<view class="confirm-btn" >我要兑换</view>
</view>
</view>
<unidialog
:title="'确认收货信息'"
:visible="freeVisible"
:content="freeContent"
@close="freeClose"
@confirm="freeConfirm"
>
</unidialog>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'
import goods_api from '@/api/goods_api'
import unidialog from '@/components/dialog/dialog.vue';
const freeVisible = ref(false)
import navTo from '@/utils/navTo';
const freeContent = ref('您的积分不足,是否购买积分?')
const freeClose = () => {
freeVisible.value = false
}
const freeConfirm = () => {
freeVisible.value = false;
navTo({
url: '/pages_app/buyPoint/buyPoint'
})
}
const title = ref('')
const price = ref(0)
const qty = ref(1)
@@ -54,6 +76,7 @@ const needPoints = ref(0)
const totalPoints = ref(0)
const selectedAddress = ref(null)
const id = ref('')
const type = ref(null)
const goBack = () => uni.navigateBack()
@@ -63,10 +86,12 @@ const setQty = (n) => {
}
onLoad((opts) => {
console.log(opts);
if (opts) {
title.value = decodeURIComponent(opts.title || '')
price.value = Number(opts.price || 0)
id.value = opts.id || ''
type.value = opts.type
setQty(1)
getTotalPoints()
}
@@ -110,24 +135,14 @@ const changeAddress = () => {
}
const goAddress = () => {
if (needPoints.value > totalPoints.value) {
uni.showToast({ title: '积分不足', icon: 'none' })
return
if(Number(needPoints.value) > Number(totalPoints.value)){
freeVisible.value = true
}else{
uni.navigateTo({
url: '/pages_goods/exchange/address?goodsUuid='+id.value+'&type='+type.value+'&goodsNum='+qty.value
})
}
if (!selectedAddress.value) {
changeAddress()
return
}
const data = {
goodsUuid: id.value,
address: selectedAddress.value.fullAddress,
user_name: selectedAddress.value.receiver,
mobile: selectedAddress.value.mobile,
goodsNum: qty.value,
email: selectedAddress.value.email
}
console.log(data)
//{"goodsUuid":"e6ca84edc8e64242a379dd0b895ea812",
@@ -137,26 +152,26 @@ const goAddress = () => {
// "goodsNum":"1",
// "email":""}
// 模拟下单:此处可调用后端接口完成兑换
uni.showModal({
title: '确认兑换',
content: `兑换“${title.value}”x${qty.value},共需 ${needPoints.value} 积分`,
success: (r) => {
if (r.confirm) {
// 扣减本地积分(演示)
totalPoints.value = Math.max(0, totalPoints.value - needPoints.value)
uni.removeStorageSync(SELECTED_KEY)
goods_api.createGoodsOrder(data).then(res => {
console.log(res)
if (res.code == 200) {
uni.showToast({ title: '兑换成功', icon: 'none' })
uni.navigateBack()
} else {
uni.showToast({ title: res.msg, icon: 'none' })
}
})
}
}
})
// uni.showModal({
// title: '确认兑换',
// content: `兑换“${title.value}”x${qty.value},共需 ${needPoints.value} 积分`,
// success: (r) => {
// if (r.confirm) {
// // 扣减本地积分(演示)
// totalPoints.value = Math.max(0, totalPoints.value - needPoints.value)
// uni.removeStorageSync(SELECTED_KEY)
// goods_api.createGoodsOrder(data).then(res => {
// console.log(res)
// if (res.code == 200) {
// uni.showToast({ title: '兑换成功', icon: 'none' })
// uni.navigateBack()
// } else {
// uni.showToast({ title: res.msg, icon: 'none' })
// }
// })
// }
// }
// })
}
const getTotalPoints = () => {
goods_api.getTotalPoints && goods_api.getTotalPoints().then(res => {
+45 -5
View File
@@ -13,7 +13,7 @@
>
<template v-slot:right>
<view class="nav-actions">
<view class="btn" @click="goToSearch">搜索</view>
<view class="btn" @click="goSearch">搜索</view>
</view>
</template>
</uni-nav-bar>
@@ -148,11 +148,14 @@
</view>
</view>
<!-- 商品网格(上拉加载) -->
<!-- 商品网格(上拉加载 + 下拉刷新) -->
<scroll-view
class="product-grid"
scroll-y
refresher-enabled="true"
:refresher-triggered="refresherTriggered"
@scrolltolower="getGoodsList"
@refresherrefresh="onRefresherRefresh"
:lower-threshold="100"
>
<view class="grid-wrap">
@@ -160,6 +163,7 @@
class="product-item"
v-for="(product, index) in products"
:key="product.uuid || index"
@click="goToProductDetail(product)"
>
<view class="product-image-container">
@@ -219,6 +223,7 @@ const page = ref(1);
const pageSize = ref(10);
const isLoadingMore = ref(false);
const noMore = ref(false);
const refresherTriggered = ref(false);
const onSwiperChange = (e) => {
currentBannerIndex.value = e.detail.current;
};
@@ -288,7 +293,17 @@ const goToProductDetail = (product) => {
url: `/pages_goods/productDetail/productDetail?id=${id}`,
});
};
const goSearch = () => {
uni.sendNativeEvent(
"goHomeSearch",
{
msg: "pointMall",
},
(ret) => {
console.log(ret);
}
);
}
const goToMyRedemption = () => {
uni.navigateTo({
url: "/pages_goods/myRedemption/myRedemption",
@@ -305,7 +320,7 @@ const goToBannerDetail = (banner) => {
// 跳转到轮播图详情页面
uni.navigateTo({
url: `/pages_app/webview/webview?url=${encodeURIComponent(
banner.path
docUrl+banner.path
)}&title=${banner.title}`,
});
};
@@ -393,6 +408,31 @@ const getGoodsTagList = () => {
});
};
// 下拉刷新处理
const onRefresherRefresh = async () => {
if (refresherTriggered.value) return;
refresherTriggered.value = true;
try {
// 重置分页和列表
page.value = 1;
noMore.value = false;
isLoadingMore.value = false;
products.value = [];
// 重新加载所有数据
getGoodsNewsList();
getGoodsTagList();
await getGoodsList();
} catch (error) {
console.error("刷新数据失败:", error);
uni.showToast({
title: "刷新失败,请重试",
icon: "none",
});
} finally {
refresherTriggered.value = false;
}
};
onMounted(() => {
getGoodsNewsList();
// 初始化分页并加载第一页
@@ -613,7 +653,7 @@ onMounted(() => {
right: 0;
z-index: 1;
height: calc(
100vh - var(--status-bar-height) - 44px - 88rpx
100vh - var(--status-bar-height) - 44px - 575rpx
); /* 预留顶部导航/筛选及底部栏 */
background-color: #f5f5f5;
}
+6 -5
View File
@@ -16,7 +16,7 @@
<scroll-view scroll-y class="detail-scroll">
<!-- 商品主图轮播 -->
<view class="swiper-box">
<swiper class="detail-swiper" :indicator-dots="true" :autoplay="false" :interval="3000" :duration="400" indicator-color="#ccc" indicator-active-color="#8B2316">
<swiper class="detail-swiper" :indicator-dots="true" :autoplay="true" :interval="5000" :duration="400" indicator-color="#ccc" indicator-active-color="#8B2316">
<swiper-item v-for="(img, idx) in images" :key="idx">
<image class="swiper-image" :src="img" mode="aspectFit"></image>
</swiper-item>
@@ -25,7 +25,7 @@
<!-- 标题与价格、销量 -->
<view class="summary">
<view class="title">{{ product.title }}</view>
<view class="title">[包邮]{{ product.title }}</view>
<view class="price-row">
<text class="price">{{ product.price }}积分</text>
<text class="exchanged">已兑换{{ product.times }}件</text>
@@ -51,8 +51,8 @@
</scroll-view>
<!-- 底部兑换按钮 -->
<view class="bottom-bar">
<view class="redeem-btn" @click="goExchange">在线兑换</view>
<view class="bottom-bar" @click="goExchange">
<view class="redeem-btn" >在线兑换</view>
</view>
</view>
</template>
@@ -76,6 +76,7 @@ const fetchDetail = async (id) => {
if ((res.code === 200 || res.code === '200') && res.data) {
product.value = {
id,
type: res.data.type,
title: res.data.name || '',
price: res.data.bonuspoints || 0,
times: res.data.times || 0,
@@ -103,7 +104,7 @@ onLoad((opts) => {
const goExchange = () => {
uni.navigateTo({
url: `/pages_goods/exchange/index?id=${product.value.id}&title=${encodeURIComponent(product.value.title)}&price=${product.value.price}`
url: `/pages_goods/exchange/index?id=${product.value.id}&title=${encodeURIComponent(product.value.title)}&price=${product.value.price}&type=${product.value.type}`
})
}
</script>