更新模板
This commit is contained in:
230
pages/adoption/detail.uvue
Normal file
230
pages/adoption/detail.uvue
Normal file
@@ -0,0 +1,230 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<cl-topbar title="项目详情" />
|
||||
|
||||
<scroll-view scroll-y class="content" v-if="project != null">
|
||||
<swiper class="banner" autoplay circular indicator-dots>
|
||||
<swiper-item v-for="(img, index) in projectImages" :key="index">
|
||||
<image class="banner-image" :src="img" mode="aspectFill" />
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
<view class="project-info">
|
||||
<text class="project-name">{{ project.name }}</text>
|
||||
<view class="project-meta">
|
||||
<view class="meta-item">
|
||||
<text class="meta-label">周期</text>
|
||||
<text class="meta-value">{{ project.duration }}天</text>
|
||||
</view>
|
||||
<view class="meta-item">
|
||||
<text class="meta-label">权益</text>
|
||||
<text class="meta-value">{{ benefits.length }}项</text>
|
||||
</view>
|
||||
<view class="meta-item">
|
||||
<text class="meta-label">已认养</text>
|
||||
<text class="meta-value">{{ project.adoptedCount }}人</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="benefits-section">
|
||||
<text class="section-title">认养权益</text>
|
||||
<view class="benefit-list">
|
||||
<view v-for="(benefit, index) in benefits" :key="index" class="benefit-item">
|
||||
<cl-icon name="check-circle" :size="36" color="#52c41a" />
|
||||
<text class="benefit-text">{{ benefit }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="desc-section">
|
||||
<text class="section-title">项目介绍</text>
|
||||
<text class="desc">{{ project.description }}</text>
|
||||
</view>
|
||||
|
||||
<view style="height: 140rpx;"></view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="bottom-bar" v-if="project != null">
|
||||
<view class="price-info">
|
||||
<text class="price">¥{{ project.price }}</text>
|
||||
<text class="original" v-if="project.originalPrice != null">¥{{ project.originalPrice }}</text>
|
||||
</view>
|
||||
<cl-button type="primary" round @click="onAdopt">立即认养</cl-button>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { router, useCool, parseObject } from '@/cool';
|
||||
|
||||
const { service } = useCool();
|
||||
|
||||
const project = ref<any>(null);
|
||||
|
||||
const projectImages = computed(() => {
|
||||
if (project.value?.images == null) return ['/static/images/farm.png'];
|
||||
try {
|
||||
return parseObject<string[]>(project.value.images) ?? ['/static/images/farm.png'];
|
||||
} catch {
|
||||
return ['/static/images/farm.png'];
|
||||
}
|
||||
});
|
||||
|
||||
const benefits = computed(() => {
|
||||
if (project.value?.benefits == null) return ['新鲜农产品直送', '专属认养证书', '实时生长查看'];
|
||||
try {
|
||||
return parseObject<string[]>(project.value.benefits) ?? ['新鲜农产品直送', '专属认养证书', '实时生长查看'];
|
||||
} catch {
|
||||
return ['新鲜农产品直送', '专属认养证书', '实时生长查看'];
|
||||
}
|
||||
});
|
||||
|
||||
async function loadProject() {
|
||||
const pages = getCurrentPages();
|
||||
const currentPage = pages[pages.length - 1];
|
||||
const projectId = Number(currentPage.options?.id || 0);
|
||||
|
||||
if (projectId != 0) {
|
||||
try {
|
||||
const res = await service.nongchuang.adoption.detail({ id: projectId });
|
||||
project.value = res;
|
||||
} catch (e) {
|
||||
project.value = {
|
||||
id: projectId,
|
||||
name: '有机苹果园认养',
|
||||
description: '位于山东烟台的优质苹果园,采用有机种植方式',
|
||||
duration: 180,
|
||||
price: 299,
|
||||
adoptedCount: 56
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onAdopt() {
|
||||
uni.showToast({ title: '认养功能开发中', icon: 'none' });
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadProject();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.banner {
|
||||
height: 400rpx;
|
||||
|
||||
.banner-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.project-info {
|
||||
padding: 30rpx;
|
||||
background: #fff;
|
||||
|
||||
.project-name {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.project-meta {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
margin-top: 30rpx;
|
||||
padding: 24rpx 0;
|
||||
background: #f9f9f9;
|
||||
border-radius: 12rpx;
|
||||
|
||||
.meta-item {
|
||||
text-align: center;
|
||||
|
||||
.meta-label {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.meta-value {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.benefits-section, .desc-section {
|
||||
margin-top: 20rpx;
|
||||
padding: 30rpx;
|
||||
background: #fff;
|
||||
|
||||
.section-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.benefit-list {
|
||||
.benefit-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 16rpx 0;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
|
||||
.benefit-text {
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 30rpx;
|
||||
background: #fff;
|
||||
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||||
|
||||
.price-info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.price {
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.original {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
119
pages/adoption/gift-card.uvue
Normal file
119
pages/adoption/gift-card.uvue
Normal file
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<cl-topbar title="兑换礼品卡" />
|
||||
|
||||
<view class="gift-card-page">
|
||||
<view class="card-input-section">
|
||||
<text class="section-title">礼品卡码</text>
|
||||
<input class="card-input" v-model="cardCode" type="text" placeholder="请输入礼品卡码" maxlength="20" />
|
||||
<cl-button type="primary" size="large" round :loading="loading" @click="redeemCard">
|
||||
立即兑换
|
||||
</cl-button>
|
||||
</view>
|
||||
|
||||
<view class="tips-section">
|
||||
<text class="section-title">使用说明</text>
|
||||
<view class="tip-list">
|
||||
<view class="tip-item">
|
||||
<cl-icon name="check-fill" :size="32" color="#52c41a" />
|
||||
<text class="tip-text">每张礼品卡只能兑换一次</text>
|
||||
</view>
|
||||
<view class="tip-item">
|
||||
<cl-icon name="check-fill" :size="32" color="#52c41a" />
|
||||
<text class="tip-text">兑换成功后自动激活认养权益</text>
|
||||
</view>
|
||||
<view class="tip-item">
|
||||
<cl-icon name="check-fill" :size="32" color="#52c41a" />
|
||||
<text class="tip-text">如有问题请联系客服</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref } from 'vue';
|
||||
import { router, useCool } from '@/cool';
|
||||
|
||||
const { service } = useCool();
|
||||
|
||||
const cardCode = ref('');
|
||||
const loading = ref(false);
|
||||
|
||||
async function redeemCard() {
|
||||
if (cardCode.value.length == 0) {
|
||||
uni.showToast({ title: '请输入礼品卡码', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await service.nongchuang.adoption.redeemGiftCard({ cardCode: cardCode.value });
|
||||
uni.showToast({ title: '兑换成功', icon: 'success' });
|
||||
setTimeout(() => {
|
||||
router.push({ path: '/pages/adoption/my' });
|
||||
}, 1500);
|
||||
} catch (e: any) {
|
||||
uni.showToast({ title: e.message || '兑换失败', icon: 'none' });
|
||||
}
|
||||
loading.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.gift-card-page {
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.card-input-section {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.card-input {
|
||||
width: 100%;
|
||||
padding: 24rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 12rpx;
|
||||
font-size: 32rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.tips-section {
|
||||
margin-top: 30rpx;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.tip-list {
|
||||
.tip-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 16rpx 0;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
|
||||
.tip-text {
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
132
pages/adoption/list.uvue
Normal file
132
pages/adoption/list.uvue
Normal file
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<cl-topbar title="认养项目" />
|
||||
|
||||
<scroll-view scroll-y class="content" @scrolltolower="loadMore">
|
||||
<view class="project-list">
|
||||
<view
|
||||
v-for="project in projectList"
|
||||
:key="project.id"
|
||||
class="project-card"
|
||||
@click="goDetail(project.id)"
|
||||
>
|
||||
<image class="project-image" :src="project.getArray<string>('images') != null && project.getArray<string>('images')!.length > 0 ? project.getArray<string>('images')![0] : '/static/images/farm.png'" mode="aspectFill" />
|
||||
<view class="project-info">
|
||||
<text class="project-name">{{ project.name }}</text>
|
||||
<text class="project-desc">{{ project.description }}</text>
|
||||
<view class="project-meta">
|
||||
<text class="duration">周期: {{ project.duration }}天</text>
|
||||
<text class="count">已认养: {{ project.adoptedCount }}</text>
|
||||
</view>
|
||||
<view class="project-footer">
|
||||
<text class="price">¥{{ project.price }}</text>
|
||||
<cl-button type="primary" size="small" round>立即认养</cl-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-empty v-if="projectList.length === 0 && loading == false" text="暂无认养项目" />
|
||||
</scroll-view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { router, useCool } from '@/cool';
|
||||
|
||||
const { service } = useCool();
|
||||
|
||||
const projectList = ref<any[]>([]);
|
||||
const loading = ref(false);
|
||||
|
||||
async function loadProjects() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await service.nongchuang.adoption.list();
|
||||
if (res != null) {
|
||||
const data = res as UTSJSONObject;
|
||||
const list = data.getArray<any>("list");
|
||||
projectList.value = list != null ? list : (res as any[]);
|
||||
}
|
||||
} catch (e) {
|
||||
projectList.value = [
|
||||
{ id: 1, name: '有机苹果园认养', description: '山东烟台优质苹果', duration: 180, adoptedCount: 56, price: 299 },
|
||||
{ id: 2, name: '生态鸡认养', description: '散养土鸡,绿色健康', duration: 120, adoptedCount: 32, price: 199 }
|
||||
];
|
||||
}
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
function loadMore() {
|
||||
// 加载更多
|
||||
}
|
||||
|
||||
function goDetail(id: number) {
|
||||
router.push({ path: '/pages/adoption/detail', query: { id } });
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadProjects();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
flex: 1;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.project-card {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.project-image {
|
||||
width: 100%;
|
||||
height: 300rpx;
|
||||
}
|
||||
|
||||
.project-info {
|
||||
padding: 24rpx;
|
||||
|
||||
.project-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.project-desc {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.project-meta {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-top: 16rpx;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
|
||||
.duration {
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.project-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.price {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #ff4d4f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
135
pages/adoption/my.uvue
Normal file
135
pages/adoption/my.uvue
Normal file
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<cl-topbar title="我的认养" />
|
||||
|
||||
<scroll-view scroll-y class="content">
|
||||
<view class="adoption-list">
|
||||
<view v-for="item in adoptionList" :key="item.id" class="adoption-card" @click="goDetail(item.id)">
|
||||
<image class="adoption-image" :src="item.projectImage != null ? item.projectImage : '/static/images/farm.png'" mode="aspectFill" />
|
||||
<view class="adoption-info">
|
||||
<text class="adoption-name">{{ item.projectName }}</text>
|
||||
<view class="progress-bar">
|
||||
<view class="progress-fill" :style="{ width: item.progress + '%' }"></view>
|
||||
</view>
|
||||
<view class="adoption-meta">
|
||||
<text class="date">{{ item.startTime }} - {{ item.endTime }}</text>
|
||||
<text class="status" :class="getStatusClass(item.status)">{{ getStatusText(item.status) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-empty v-if="adoptionList.length === 0" text="暂无认养记录" />
|
||||
</scroll-view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { router, useCool } from '@/cool';
|
||||
|
||||
const { service } = useCool();
|
||||
const statusBarHeight = ref(0);
|
||||
|
||||
const adoptionList = ref<any[]>([]);
|
||||
|
||||
function getStatusClass(status: number) {
|
||||
return { active: status === 1, completed: status === 2, cancelled: status === 3 };
|
||||
}
|
||||
|
||||
function getStatusText(status: number): string {
|
||||
const texts: Record<number, string> = { 1: '进行中', 2: '已完成', 3: '已取消' };
|
||||
const text = texts[status];
|
||||
return text != null ? text : '未知';
|
||||
}
|
||||
|
||||
async function loadAdoptions() {
|
||||
try {
|
||||
const res = await service.nongchuang.adoption.myProjects();
|
||||
if (res != null) {
|
||||
const data = res as UTSJSONObject;
|
||||
const list = data.getArray<any>("list");
|
||||
adoptionList.value = list != null ? list : (res as any[]);
|
||||
}
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '加载失败', icon: 'none' });
|
||||
adoptionList.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
function goDetail(id: number) {
|
||||
router.push({ path: '/pages/adoption/detail', query: { id } });
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const info = uni.getWindowInfo();
|
||||
statusBarHeight.value = info.statusBarHeight + 10;
|
||||
loadAdoptions();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
flex: 1;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.adoption-card {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.adoption-image {
|
||||
width: 160rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 12rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.adoption-info {
|
||||
flex: 1;
|
||||
|
||||
.adoption-name {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 8rpx;
|
||||
background: #f0f0f0;
|
||||
border-radius: 4rpx;
|
||||
margin: 16rpx 0;
|
||||
overflow: hidden;
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: #52c41a;
|
||||
border-radius: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.adoption-meta {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
.date {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.status {
|
||||
font-size: 24rpx;
|
||||
|
||||
&.active { color: #52c41a; }
|
||||
&.completed { color: #8c8c8c; }
|
||||
&.cancelled { color: #ff4d4f; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user