Files
aida_front/src/views/SellerDashboard/MyOrders/index.vue
2026-04-24 16:57:13 +08:00

382 lines
8.4 KiB
Vue

<template>
<div class="my-orders-index mini-scrollbar">
<div class="total-box">
<div class="total-item" v-for="v in totals" :key="v.title">
<div class="title">
<span class="icon"><svg-icon :name="v.icon" size="18" /></span>
<span class="label">{{ v.title }}</span>
</div>
<div class="value">{{ v.value }}</div>
</div>
</div>
<div class="filter-box">
<div class="left">
<div class="title">All Invoice</div>
<div class="tip">A summary of all completed transactions.</div>
</div>
<div class="right">
<div class="input">
<span class="icon"
><svg-icon name="seller-search" size="20" @click="getList(true)"
/></span>
<input
type="text"
v-model="nameOrId"
placeholder="Search by item name or order ID"
@keydown.enter.prevent="getList(true)"
/>
</div>
</div>
</div>
<div class="table">
<div class="header">
<div class="order-id">Order ID</div>
<div class="item">Item</div>
<div class="price">Price</div>
<div class="buyer-username">Buyer Username</div>
<div class="date">Date</div>
</div>
<div class="body">
<div class="item" v-for="v in list" :key="v.orderId">
<div class="order-id">{{ v.orderId }}</div>
<div class="item">
<div class="images">
<img
v-for="v in v.item.slice(0, maxItemNum)"
:key="v.id"
:src="v.url"
/>
<span v-if="v.item.length > maxItemNum"
>+{{ v.item.length - maxItemNum }} more</span
>
</div>
<div class="titles">
<div v-for="v in v.item.slice(0, maxItemNum)" :key="v.id">
{{ v.title }}
</div>
<span v-if="v.item.length > maxItemNum">...</span>
</div>
</div>
<div class="price">{{ v.price }}</div>
<div class="buyer-username">{{ v.username }}</div>
<div class="date">
<div>{{ v.date }}</div>
<div>{{ v.time }}</div>
</div>
</div>
</div>
<div class="null" v-show="list.length === 0 && !loading && finish">no data</div>
<div class="placeholder" ref="placeholderRef" v-show="!loading"></div>
<div class="footer" :class="{ null: list.length === 0 }" v-if="!finish">
<a-spin :delay="0.5" v-show="loading" />
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount, computed } from "vue"
import { Https } from "@/tool/https"
const totals_obj = ref({
totalRevenue: "--",
totalPurchases: "--",
totalViews: "--"
})
const totals = computed(() => [
{
icon: "seller-qiandaizi",
title: "Total Revenue",
value: `HK$ ${totals_obj.value.totalRevenue}`
},
{
icon: "seller-gouwudai",
title: "Total Purchases",
value: totals_obj.value.totalPurchases
},
{
icon: "seller-eye",
title: "Total Views",
value: totals_obj.value.totalViews
}
])
const maxItemNum = ref(2)
const loading = ref(false)
const finish = ref(false)
const total = ref(0)
const page = ref(1)
const size = ref(10)
const nameOrId = ref("")
const list = ref([])
const getList = (isReload = false) => {
if (loading.value) return
loading.value = true
if (isReload) {
list.value = []
page.value = 1
finish.value = false
}
const data = {
page: page.value,
size: size.value
}
if (nameOrId.value) data.keyword = nameOrId.value
Https.axiosGet(Https.httpUrls.getSellerOrderList, { params: data }).then((res) => {
res.content?.forEach((v) => {
const obj = {
orderId: v.orderId,
items: v.items.map((item) => ({
id: item.productId,
url: item.thumbnailUrl,
title: item.productName
})),
price: "HK$ " + v.price,
username: v.buyerUsername,
date: v.date,
time: v.date
}
list.value.push(obj)
})
total.value = res.total
page.value++
finish.value = page.value > total.value / size.value
loading.value = false
})
}
const getSummary = () => {
Https.axiosGet(Https.httpUrls.getSellerOrderSummary).then((res) => {
totals_obj.value.totalRevenue = res.totalRevenue
totals_obj.value.totalPurchases = res.totalPurchases
totals_obj.value.totalViews = res.totalViews
})
}
getSummary()
getList(true)
const placeholderRef = ref(null)
const observer = new IntersectionObserver(
(entries) => {
if (!entries[0].intersectionRatio || loading.value || finish.value) return
getList()
},
{ root: document.body }
)
onMounted(() => {
observer.observe(placeholderRef.value)
})
onBeforeUnmount(() => {
observer.disconnect()
})
const formatTimestamp = (ts) => {
const d = new Date(ts)
const h = d.getHours()
const m = d.getMinutes()
const date = `${d.toLocaleString("en-US", {
month: "long"
})} ${d.getDate()}, ${d.getFullYear()}`
const time = `${h.toString().padStart(2, "0")}:${m.toString().padStart(2, "0")} ${
h >= 12 ? "PM" : "AM"
}`
return {
date,
time,
dateTime: `${date}\n${time}`
}
}
</script>
<style scoped lang="less">
.my-orders-index {
position: relative;
flex: 1;
overflow-y: auto;
display: flex;
flex-direction: column;
padding: 0 3rem;
margin: 0 3rem;
gap: 4rem;
> .total-box {
display: flex;
gap: 1.8rem;
> .total-item {
flex: 1;
padding: 2.4rem;
background-color: #f6f6f6;
border-radius: 1.2rem;
> .title {
display: flex;
align-items: center;
margin-bottom: 1.2rem;
gap: 1.2rem;
> .icon {
width: 4rem;
height: 4rem;
border-radius: 50%;
background-color: #ffffff;
border: 0.08rem solid #e4e4e4;
}
> .label {
font-size: 2rem;
color: #585858;
}
}
> .value {
font-family: "pingfang_heavy";
font-size: 3.6rem;
}
}
}
> .filter-box {
display: flex;
justify-content: space-between;
align-items: flex-end;
> .left {
> .title {
font-family: "pingfang_heavy";
font-size: 2.4rem;
color: #000;
}
> .tip {
font-family: "pingfang_regular";
font-size: 1.4rem;
color: #999;
}
}
> .right {
> .input {
width: 30rem;
height: 4rem;
border-bottom: 0.15rem solid #000000;
display: flex;
align-items: center;
> .icon {
margin: 0 0.5rem;
}
> input {
padding: 0 2rem;
width: 0;
flex: 1;
outline: none;
border: none;
height: 100%;
font-family: "pingfang_regular";
font-size: 1.2rem;
color: #000;
&::placeholder {
color: #999999;
}
}
}
}
}
> .table {
width: 100%;
> .body > .item,
> .header {
display: flex;
align-items: center;
> div {
padding: 0 1.5rem;
flex: 1;
}
> .order-id {
flex: 1.5;
}
> .item {
flex: 3;
}
> .buyer-username {
flex: 1.5;
}
}
> .header {
position: sticky;
top: 0;
background-color: #f6f6f6;
height: 5.6rem;
border-width: 0.15rem 0 0.15rem 0;
border-style: solid;
border-color: #eaeaea;
> div {
font-size: 2rem;
color: #979797;
}
}
> .body {
> .item {
padding-top: 1.6rem;
padding-bottom: 1.6rem;
border-bottom: 0.1rem solid #f6f6f6;
color: #000;
> .order-id {
font-family: "pingfang_regular";
font-size: 1.8rem;
}
> .price {
font-family: "pingfang_medium";
font-size: 1.8rem;
}
> .buyer-username {
font-family: "pingfang_regular";
font-size: 1.8rem;
color: #666;
}
> .date {
font-family: "pingfang_regular";
font-size: 1.6rem;
color: #666;
}
> .item {
display: flex;
align-items: center;
gap: 3rem;
> .images {
display: flex;
align-items: center;
justify-content: center;
gap: 1.2rem;
> img {
width: auto;
height: 10rem;
border-radius: 0.8rem;
border: 0.1rem solid #e9e9e9;
}
> span {
font-family: "pingfang_medium";
font-size: 1.4rem;
color: #666;
}
}
> .titles {
font-family: "pingfang_medium";
font-size: 1.8rem;
color: #000;
> span {
user-select: none;
}
}
}
}
}
> .null {
margin-top: 10rem;
text-align: center;
color: #999;
}
> .footer {
min-height: 10rem;
display: flex;
align-items: center;
justify-content: center;
&.null {
height: 30rem;
}
}
> .placeholder {
width: 100%;
height: 1px;
}
}
}
</style>