Files
olons_server/web/src/mobile/views/MallView.vue
T

116 lines
4.3 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>
<section v-if="!isDetail" class="m-task-mall" :style="{ backgroundImage: `url(${mobileAssets.mallBackground})` }">
<PageHeader title="手工商城" />
<img class="m-mall-banner" :src="mobileAssets.mallBanner" alt="精选任务,轻松赚收益" />
<AsyncState
:loading="loading"
:error="error"
:empty="!products.length"
empty-text="暂无手工商品"
@retry="loadList"
>
<div class="m-mall-list">
<button v-for="item in products" :key="item.id" class="m-mall-task" type="button" @click="openSupport">
<span class="m-mall-cover">
<img v-if="safeImage(item.cover_url)" :src="safeImage(item.cover_url)" :alt="item.name" />
<n-icon v-else :component="ImageOutline" />
</span>
<strong>{{ item.name }}</strong>
<small>已领取:{{ item.claimed_count || 0 }}</small>
</button>
</div>
</AsyncState>
</section>
<section v-else class="m-page m-page--white m-mall-detail-page">
<PageHeader title="商品详情" />
<AsyncState
:loading="loading"
:error="error"
:empty="!product"
empty-text="商品不存在"
@retry="loadDetail"
>
<article v-if="product" class="m-product-detail">
<div class="m-product-hero">
<img v-if="safeImage(product.cover_url)" :src="safeImage(product.cover_url)" :alt="product.name" />
<n-icon v-else :component="ImageOutline" />
</div>
<section>
<h1>{{ product.name }}</h1>
<div class="m-price"><strong>{{ money(product.price_amount, product.currency) }}</strong><span>已领取 {{ product.claimed_count || 0 }} 份</span></div>
</section>
<section class="m-product-intro"><h2>项目介绍</h2><p>{{ product.detail_text || product.summary || '暂无详细介绍' }}</p></section>
<section class="m-process">
<h2>加工说明</h2>
<div>
<span><img :src="mobileAssets.processSimple" alt="" />操作简单</span>
<span><img :src="mobileAssets.processMaterial" alt="" />材料配发</span>
<span><img :src="mobileAssets.processTime" alt="" />时间自由</span>
<span><img :src="mobileAssets.processReturn" alt="" />完成回收</span>
</div>
</section>
<div class="m-buy-bar"><span>合计 <b>{{ money(product.price_amount, product.currency) }}</b></span><router-link to="/app/claim">立即领取</router-link></div>
</article>
</AsyncState>
</section>
</template>
<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { ImageOutline } from '@vicons/ionicons5';
import PageHeader from '../components/PageHeader.vue';
import AsyncState from '../components/AsyncState.vue';
import { errorText, money, request, safeImage } from '../api';
import { mobileAssets } from '../assets';
const route = useRoute();
const router = useRouter();
const isDetail = computed(() => Boolean(route.params.id));
const loading = ref(true);
const error = ref('');
const products = ref<any[]>([]);
const product = ref<any>();
let listController: AbortController | undefined;
function openSupport() {
router.push({ name: 'MobileSupport' });
}
async function loadList() {
listController?.abort();
const controller = new AbortController();
listController = controller;
loading.value = true;
error.value = '';
try {
const data = await request<any>({ url: '/v1/manual-products', params: { page: 1, page_size: 100 }, signal: controller.signal });
products.value = data.items || [];
} catch (err) {
if (!controller.signal.aborted) error.value = errorText(err);
} finally {
if (listController === controller) loading.value = false;
}
}
async function loadDetail() {
loading.value = true;
error.value = '';
try {
product.value = await request({ url: `/v1/manual-products/${route.params.id}` });
} catch (err) {
error.value = errorText(err);
} finally {
loading.value = false;
}
}
function load() {
if (isDetail.value) void loadDetail();
else void loadList();
}
onMounted(load);
onBeforeUnmount(() => listController?.abort());
watch(() => route.params.id, load);
</script>