mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-11-06 17:09:08 +08:00
feat(home): 丰富PC买家中心主页,增加订单快捷入口与资产卡片
This commit is contained in:
@@ -40,6 +40,105 @@
|
||||
</div>
|
||||
<span class="asset-link">查看</span>
|
||||
</button>
|
||||
<button type="button" class="asset-card" @click="go('Point')">
|
||||
<div>
|
||||
<div class="asset-label">我的积分</div>
|
||||
<div class="asset-value">{{ point }}<span>积分</span></div>
|
||||
</div>
|
||||
<span class="asset-link">查看</span>
|
||||
</button>
|
||||
<button type="button" class="asset-card" @click="go('MemberGrade')">
|
||||
<div>
|
||||
<div class="asset-label">会员等级</div>
|
||||
<div class="asset-value grade-value">{{ gradeName }}</div>
|
||||
</div>
|
||||
<span class="asset-link">查看</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
<span class="panel-title">最近订单</span>
|
||||
<button type="button" class="panel-more" @click="go('MyOrder')">全部订单 ›</button>
|
||||
</div>
|
||||
<div v-if="recentOrders.length" class="order-list">
|
||||
<button
|
||||
v-for="order in recentOrders"
|
||||
:key="order.sn"
|
||||
type="button"
|
||||
class="order-row"
|
||||
@click="goOrderDetail(order.sn)"
|
||||
>
|
||||
<div class="order-thumb">
|
||||
<img
|
||||
:src="firstItem(order).image"
|
||||
:alt="firstItem(order).name"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
</div>
|
||||
<div class="order-info">
|
||||
<div class="order-name">
|
||||
{{ firstItem(order).name }}
|
||||
<span v-if="order.orderItems.length > 1" class="order-more">
|
||||
等{{ order.orderItems.length }}件
|
||||
</span>
|
||||
</div>
|
||||
<div class="order-meta">{{ order.createTime }} · {{ order.storeName }}</div>
|
||||
</div>
|
||||
<div class="order-side">
|
||||
<div class="order-amount">{{ $filters.unitPrice(order.flowPrice, "¥") }}</div>
|
||||
<div class="order-status">{{ orderStatusText(order.orderStatus) }}</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div v-else-if="ordersLoaded" class="panel-empty">暂无订单</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
<span class="panel-title">我的足迹</span>
|
||||
<button type="button" class="panel-more" @click="go('MyTracks')">查看更多 ›</button>
|
||||
</div>
|
||||
<div v-if="tracks.length" class="goods-strip">
|
||||
<button
|
||||
v-for="item in tracks"
|
||||
:key="item.id"
|
||||
type="button"
|
||||
class="goods-card"
|
||||
@click="openGoods(item.id, item.goodsId)"
|
||||
>
|
||||
<div class="goods-thumb">
|
||||
<img :src="item.thumbnail" :alt="item.goodsName" loading="lazy" decoding="async" />
|
||||
</div>
|
||||
<div class="goods-name">{{ item.goodsName }}</div>
|
||||
<div class="goods-price">{{ $filters.unitPrice(item.price, "¥") }}</div>
|
||||
</button>
|
||||
</div>
|
||||
<div v-else-if="tracksLoaded" class="panel-empty">暂无足迹</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
<span class="panel-title">我的收藏</span>
|
||||
<button type="button" class="panel-more" @click="go('Favorites')">查看更多 ›</button>
|
||||
</div>
|
||||
<div v-if="favorites.length" class="goods-strip">
|
||||
<button
|
||||
v-for="item in favorites"
|
||||
:key="item.skuId"
|
||||
type="button"
|
||||
class="goods-card"
|
||||
@click="openGoods(item.skuId, item.goodsId)"
|
||||
>
|
||||
<div class="goods-thumb">
|
||||
<img :src="item.image" :alt="item.goodsName" loading="lazy" decoding="async" />
|
||||
</div>
|
||||
<div class="goods-name">{{ item.goodsName }}</div>
|
||||
<div class="goods-price">{{ $filters.unitPrice(item.price, "¥") }}</div>
|
||||
</button>
|
||||
</div>
|
||||
<div v-else-if="favoritesLoaded" class="panel-empty">暂无收藏</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -50,9 +149,18 @@ import { useRouter } from "vue-router";
|
||||
import { Bell } from "@element-plus/icons-vue";
|
||||
import storage from "@/plugins/storage";
|
||||
import { getOrderList } from "@/api/order";
|
||||
import { afterSaleList, getMembersWallet, memberCouponList } from "@/api/member";
|
||||
import {
|
||||
afterSaleList,
|
||||
collectList,
|
||||
getMembersWallet,
|
||||
memberCouponList,
|
||||
memberGradeCurrent,
|
||||
memberPoint,
|
||||
tracksList,
|
||||
} from "@/api/member";
|
||||
import { refreshUnreadMessage } from "@/utils/unreadMessage";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
import { orderStatusList } from "./enumeration.js";
|
||||
|
||||
defineOptions({ name: "UserCenterHome" });
|
||||
|
||||
@@ -63,6 +171,12 @@ function totalOf(res) {
|
||||
return res && res.success ? Number(res.result && res.result.total) || 0 : 0;
|
||||
}
|
||||
|
||||
function recordsOf(res) {
|
||||
return res && res.success && res.result && Array.isArray(res.result.records)
|
||||
? res.result.records.filter((item) => item != null)
|
||||
: [];
|
||||
}
|
||||
|
||||
const displayName = ref("会员");
|
||||
const waitPay = ref(0);
|
||||
const waitShip = ref(0);
|
||||
@@ -70,6 +184,14 @@ const waitReceive = ref(0);
|
||||
const afterSale = ref(0);
|
||||
const couponCount = ref(0);
|
||||
const balance = ref(0);
|
||||
const point = ref(0);
|
||||
const gradeName = ref("-");
|
||||
const recentOrders = ref([]);
|
||||
const tracks = ref([]);
|
||||
const favorites = ref([]);
|
||||
const ordersLoaded = ref(false);
|
||||
const tracksLoaded = ref(false);
|
||||
const favoritesLoaded = ref(false);
|
||||
|
||||
const unreadMsg = computed(() => appStore.unreadMsg);
|
||||
const unreadLabel = computed(() => (unreadMsg.value > 99 ? "99+" : String(unreadMsg.value)));
|
||||
@@ -90,6 +212,15 @@ function readUser() {
|
||||
}
|
||||
}
|
||||
|
||||
function firstItem(order) {
|
||||
return (order.orderItems && order.orderItems[0]) || {};
|
||||
}
|
||||
|
||||
function orderStatusText(status) {
|
||||
const hit = orderStatusList.find((e) => e.status === status);
|
||||
return hit ? hit.name : status;
|
||||
}
|
||||
|
||||
function loadSummary() {
|
||||
const page = { pageNumber: 1, pageSize: 1 };
|
||||
Promise.all([
|
||||
@@ -100,14 +231,45 @@ function loadSummary() {
|
||||
memberCouponList({ ...page, memberCouponStatus: "NEW" }).catch(() => null),
|
||||
getMembersWallet().catch(() => null),
|
||||
refreshUnreadMessage(),
|
||||
]).then(([pay, ship, receive, afterSaleRes, coupon, wallet]) => {
|
||||
waitPay.value = totalOf(pay);
|
||||
waitShip.value = totalOf(ship);
|
||||
waitReceive.value = totalOf(receive);
|
||||
afterSale.value = totalOf(afterSaleRes);
|
||||
couponCount.value = totalOf(coupon);
|
||||
balance.value = wallet && wallet.success ? wallet.result.memberWallet || 0 : 0;
|
||||
});
|
||||
getOrderList({ pageNumber: 1, pageSize: 3 }).catch(() => null),
|
||||
tracksList({ pageNumber: 1, pageSize: 10, order: "desc", sort: "updateTime" }).catch(() => null),
|
||||
collectList({ pageNumber: 1, pageSize: 10, type: "GOODS" }).catch(() => null),
|
||||
memberPoint().catch(() => null),
|
||||
memberGradeCurrent({ pageNumber: 1, pageSize: 10 }).catch(() => null),
|
||||
]).then(
|
||||
([
|
||||
pay,
|
||||
ship,
|
||||
receive,
|
||||
afterSaleRes,
|
||||
coupon,
|
||||
wallet,
|
||||
,
|
||||
recent,
|
||||
tracksRes,
|
||||
favoritesRes,
|
||||
pointRes,
|
||||
gradeRes,
|
||||
]) => {
|
||||
waitPay.value = totalOf(pay);
|
||||
waitShip.value = totalOf(ship);
|
||||
waitReceive.value = totalOf(receive);
|
||||
afterSale.value = totalOf(afterSaleRes);
|
||||
couponCount.value = totalOf(coupon);
|
||||
balance.value = wallet && wallet.success ? wallet.result.memberWallet || 0 : 0;
|
||||
|
||||
recentOrders.value = recordsOf(recent).filter((order) => order && order.sn);
|
||||
ordersLoaded.value = true;
|
||||
tracks.value = recordsOf(tracksRes);
|
||||
tracksLoaded.value = true;
|
||||
favorites.value = recordsOf(favoritesRes);
|
||||
favoritesLoaded.value = true;
|
||||
|
||||
point.value = pointRes && pointRes.success && pointRes.result ? pointRes.result.point || 0 : 0;
|
||||
const grade = gradeRes && gradeRes.success && gradeRes.result ? gradeRes.result.grade : null;
|
||||
gradeName.value = grade && grade.gradeName ? grade.gradeName : "暂无等级";
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function go(name) {
|
||||
@@ -118,6 +280,15 @@ function goOrder(tag) {
|
||||
router.push({ name: "MyOrder", query: { tag } });
|
||||
}
|
||||
|
||||
function goOrderDetail(sn) {
|
||||
router.push({ name: "OrderDetail", query: { sn } });
|
||||
}
|
||||
|
||||
function openGoods(skuId, goodsId) {
|
||||
const routeUrl = router.resolve({ path: "/goodsDetail", query: { skuId, goodsId } });
|
||||
window.open(routeUrl.href, "_blank");
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
readUser();
|
||||
loadSummary();
|
||||
@@ -196,7 +367,7 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.asset-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.stat-card,
|
||||
@@ -236,7 +407,12 @@ onMounted(() => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
padding: 18px 20px;
|
||||
|
||||
> div {
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.asset-label {
|
||||
@@ -251,6 +427,9 @@ onMounted(() => {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
line-height: 28px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
span {
|
||||
margin-left: 4px;
|
||||
@@ -260,8 +439,201 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.grade-value {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.asset-link {
|
||||
flex: 0 0 auto;
|
||||
color: $theme_color;
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.panel {
|
||||
border: 1px solid #eee;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
padding: 16px 20px 20px;
|
||||
}
|
||||
|
||||
.panel-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.panel-more {
|
||||
border: none;
|
||||
background: none;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
|
||||
&:hover {
|
||||
color: $theme_color;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-empty {
|
||||
padding: 32px 0;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.order-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.order-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
|
||||
&:hover {
|
||||
border-color: $theme_color;
|
||||
}
|
||||
}
|
||||
|
||||
.order-thumb {
|
||||
flex: 0 0 56px;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 6px;
|
||||
background: #f5f5f5;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.order-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.order-name {
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.order-more {
|
||||
color: #999;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.order-meta {
|
||||
margin-top: 4px;
|
||||
color: #999;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.order-side {
|
||||
flex: 0 0 auto;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.order-amount {
|
||||
color: #333;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.order-status {
|
||||
margin-top: 4px;
|
||||
color: $theme_color;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.goods-strip {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
overflow-x: auto;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.goods-card {
|
||||
flex: 0 0 140px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
padding: 0;
|
||||
|
||||
&:hover {
|
||||
border-color: $theme_color;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.goods-thumb {
|
||||
width: 100%;
|
||||
aspect-ratio: 1;
|
||||
background: #f5f5f5;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-name {
|
||||
margin: 8px 10px 0;
|
||||
color: #333;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
height: 36px;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.goods-price {
|
||||
margin: 6px 10px 10px;
|
||||
color: $theme_color;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user