<!--
|
再生资源回收 - 首页功能模块组件
|
路径建议:/components/recycle-home.vue
|
使用方式:在首页 <view class="hot"> 再生资源回收 </view> 区域直接引入
|
-->
|
|
<template>
|
<view class="recycle-home">
|
<!-- ===== 第一行:数据看板 ===== -->
|
<view class="recycle-dashboard">
|
<view class="dashboard-bg"></view>
|
<view class="dashboard-inner">
|
<view class="dash-item">
|
<text class="dash-num">{{ dashboard.carbonPoints ?? 0 }}</text>
|
<text class="dash-label">碳积分</text>
|
</view>
|
<view class="dash-divider"></view>
|
<view class="dash-item">
|
<text class="dash-num">{{ dashboard.recycleCount ?? 0 }}</text>
|
<text class="dash-label">回收次数</text>
|
</view>
|
<view class="dash-divider"></view>
|
<view class="dash-item">
|
<text class="dash-num dash-highlight">
|
{{ dashboard.overPercent ?? 0 }}%
|
</text>
|
<text class="dash-label">积分排名</text>
|
</view>
|
</view>
|
</view>
|
|
<!-- ===== 第二行:4 个功能按钮 ===== -->
|
<view class="recycle-actions">
|
<!-- ① 我的回收码 -->
|
<view class="action-item" @tap="openQrcode">
|
<view class="action-icon icon-green">
|
<image class="icon-img" :src="icons.qrcode" mode="aspectFit" />
|
</view>
|
<text class="action-text">我的回收码</text>
|
</view>
|
|
<!-- ② 回收记录 -->
|
<view class="action-item" @tap="goRecord">
|
<view class="action-icon icon-blue">
|
<image class="icon-img" :src="icons.record" mode="aspectFit" />
|
</view>
|
<text class="action-text">回收记录</text>
|
</view>
|
|
<!-- ③ 我要预约 -->
|
<view class="action-item" @tap="goAppointment">
|
<view class="action-icon icon-orange">
|
<image class="icon-img" :src="icons.appointment" mode="aspectFit" />
|
</view>
|
<text class="action-text">我要预约</text>
|
</view>
|
|
<!-- ④ 积分转赠 -->
|
<view class="action-item" @tap="goTransfer">
|
<view class="action-icon icon-purple">
|
<image class="icon-img" :src="icons.transfer" mode="aspectFit" />
|
</view>
|
<text class="action-text">积分转赠</text>
|
</view>
|
</view>
|
|
<!-- ===== 二维码弹层 ===== -->
|
<cl-popup v-model="showQrcode" direction="center" :close-on-click-overlay="true" background-color="transparent"
|
@close="showQrcode = false">
|
<view class="qrcode-popup" @tap.stop>
|
<view class="qrcode-card">
|
<text class="qrcode-title">我的回收码</text>
|
<text class="qrcode-desc">向回收员出示此码完成回收</text>
|
<view class="qrcode-image-wrap">
|
<!--<image class="qrcode-image" :src="qrcodeUrl" mode="aspectFit" /> -->
|
<canvas
|
canvas-id="recycleQrcode"
|
id="recycleQrcode"
|
style="width: 300px; height: 300px;"
|
/>
|
</view>
|
<text class="qrcode-code">回收码:{{ userCode }}</text>
|
<view class="qrcode-close" @tap="showQrcode = false">
|
<cl-icon name="close" :size="36" color="#999" />
|
</view>
|
</view>
|
</view>
|
</cl-popup>
|
</view>
|
</template>
|
|
<script lang="ts" setup>
|
import { ref, onMounted } from "vue";
|
import { router, useCool, useStore, usePager } from "/@/cool";
|
import { onReady, onShareAppMessage } from "@dcloudio/uni-app";
|
|
import { generateRecycleQrcode } from "/@/cool/utils/qrcode";
|
|
|
const { user } = useStore();
|
|
const { service, refs, setRefs } = useCool();
|
const { onRefresh, onData } = usePager();
|
// ========== 静态图标路径(4 个按钮图标) ==========
|
// 实际项目中放到 /static/recycle/ 目录下
|
const icons = {
|
qrcode: "/static/recycle/qrcode.png",
|
record: "/static/recycle/record.png",
|
appointment: "/static/recycle/appointment.png",
|
transfer: "/static/recycle/transfer.png",
|
};
|
|
// ========== 数据 ==========
|
const dashboard = ref({
|
carbonPoints: 0,
|
recycleCount: 0,
|
overPercent: 0,
|
});
|
|
const showQrcode = ref(false);
|
const userCode = ref("");
|
const qrcodeUrl = ref("");
|
|
// ========== 初始化加载 ==========
|
onMounted(() => {
|
loadDashboard();
|
});
|
|
/** 加载首页数据看板 */
|
async function loadDashboard() {
|
try {
|
const unionid = user.info?.phone||"18918594752";
|
service.user.info.dashboard({ unionid:unionid }).then((res) => {
|
console.log("【再生资源看板数据】", res);
|
dashboard.value = {
|
carbonPoints: res.carbonPoints ?? 0,
|
recycleCount: res.recycleCount ?? 0,
|
overPercent: res.overPercent ?? 0,
|
};
|
});
|
} catch (e) {
|
console.error("【再生资源看板加载失败】", e);
|
}
|
}
|
|
/** 加载用户回收码 */
|
async function loadMyCode() {
|
try {
|
const phone = user.info?.phone ?? "18918594752";
|
const res = await generateRecycleQrcode(phone);
|
console.log("【我的回收码】", res);
|
userCode.value = phone;
|
qrcodeUrl.value = res;
|
} catch (e) {
|
console.error("【回收码加载失败】", e);
|
// 兜底:没有接口时给个默认值
|
userCode.value = "RC-LOADING";
|
}
|
}
|
|
// ========== 按钮事件 ==========
|
|
/** ① 打开二维码 */
|
function openQrcode() {
|
loadMyCode();
|
showQrcode.value = true;
|
}
|
|
/** ② 跳转回收记录 */
|
function goRecord() {
|
router.push("/pages/recycle/record");
|
}
|
|
/** ③ 跳转我要预约 */
|
function goAppointment() {
|
router.push("/pages/recycle/appointment");
|
}
|
|
/** ④ 跳转积分转赠 */
|
function goTransfer() {
|
router.push("/pages/recycle/transfer");
|
}
|
</script>
|
|
<style lang="scss" scoped>
|
.recycle-home {
|
padding: 0 24rpx;
|
|
/* ========== 数据看板 ========== */
|
.recycle-dashboard {
|
position: relative;
|
border-radius: 24rpx;
|
overflow: hidden;
|
margin-bottom: 30rpx;
|
box-shadow: 0 8rpx 24rpx rgba(46, 204, 113, 0.2);
|
|
.dashboard-bg {
|
position: absolute;
|
inset: 0;
|
background: linear-gradient(135deg, #2ecc71 0%, #27ae60 100%);
|
z-index: 0;
|
}
|
|
.dashboard-inner {
|
position: relative;
|
z-index: 1;
|
display: flex;
|
align-items: center;
|
justify-content: space-around;
|
padding: 36rpx 20rpx;
|
}
|
|
.dash-item {
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
flex: 1;
|
}
|
|
.dash-num {
|
font-size: 44rpx;
|
font-weight: 700;
|
color: #ffffff;
|
line-height: 1.2;
|
}
|
|
.dash-highlight {
|
color: #ffd700;
|
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15);
|
}
|
|
.dash-label {
|
font-size: 22rpx;
|
color: rgba(255, 255, 255, 0.85);
|
margin-top: 8rpx;
|
}
|
|
.dash-divider {
|
width: 2rpx;
|
height: 50rpx;
|
background-color: rgba(255, 255, 255, 0.3);
|
}
|
}
|
|
/* ========== 4 个功能按钮 ========== */
|
.recycle-actions {
|
display: flex;
|
justify-content: space-around;
|
align-items: center;
|
background-color: #fff;
|
border-radius: 20rpx;
|
padding: 28rpx 10rpx;
|
margin-bottom: 20rpx;
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
}
|
|
.action-item {
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
transition: transform 0.15s ease;
|
|
&:active {
|
transform: scale(0.92);
|
}
|
}
|
|
.action-icon {
|
width: 96rpx;
|
height: 96rpx;
|
border-radius: 50%;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
margin-bottom: 14rpx;
|
|
.icon-img {
|
width: 52rpx;
|
height: 52rpx;
|
}
|
|
&.icon-green {
|
background: linear-gradient(135deg, #e8f8f0, #d5f5e3);
|
}
|
|
&.icon-blue {
|
background: linear-gradient(135deg, #eaf4fc, #d6eaf8);
|
}
|
|
&.icon-orange {
|
background: linear-gradient(135deg, #fef5e7, #fdebd0);
|
}
|
|
&.icon-purple {
|
background: linear-gradient(135deg, #f5eef8, #ebdef0);
|
}
|
}
|
|
.action-text {
|
font-size: 24rpx;
|
color: #333;
|
font-weight: 500;
|
}
|
|
/* ========== 二维码弹层 ========== */
|
.qrcode-popup {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
width: 100%;
|
}
|
|
.qrcode-card {
|
position: relative;
|
width: 600rpx;
|
background: #fff;
|
border-radius: 28rpx;
|
padding: 50rpx 40rpx 40rpx;
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
}
|
|
.qrcode-title {
|
font-size: 34rpx;
|
font-weight: 700;
|
color: #333;
|
margin-bottom: 10rpx;
|
}
|
|
.qrcode-desc {
|
font-size: 24rpx;
|
color: #999;
|
margin-bottom: 36rpx;
|
}
|
|
.qrcode-image-wrap {
|
width: 480rpx;
|
height: 480rpx;
|
border-radius: 16rpx;
|
overflow: hidden;
|
background: #f7f7f7;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
margin-bottom: 28rpx;
|
}
|
|
.qrcode-image {
|
width: 100%;
|
height: 100%;
|
}
|
|
.qrcode-code {
|
font-size: 26rpx;
|
color: #666;
|
letter-spacing: 2rpx;
|
}
|
|
.qrcode-close {
|
position: absolute;
|
top: 20rpx;
|
right: 20rpx;
|
width: 60rpx;
|
height: 60rpx;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
border-radius: 50%;
|
background: #f5f5f5;
|
}
|
}
|
</style>
|