feat: h5页面-积分商城首页优化

This commit is contained in:
2026-07-27 15:09:52 +08:00
parent f054226683
commit a2b769aeff
12 changed files with 576 additions and 397 deletions
@@ -12,6 +12,7 @@ import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
import java.util.Date;
import java.util.List;
@IocBean(args = {"refer:dao"})
@@ -46,6 +47,7 @@ public class PointsMallSupplierServiceImpl extends BaseServiceImpl<PointsMallSup
@Override
public List<PointsMallSupplier> listByMallType(Integer mallType) {
Cnd cnd = Cnd.where("delFlag", "=", false);
cnd.and(Cnd.exps("cooperationExpTime", "is", null).or("cooperationExpTime", ">=", new Date()));
if (mallType != null) {
cnd.and("mallType", "=", mallType);
}
+16
View File
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This is the JRebel configuration file. It maps the running application to your IDE workspace, enabling JRebel reloading for this project.
Refer to https://manuals.jrebel.com/jrebel/standalone/config.html for more information.
-->
<application generated-by="intellij" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.zeroturnaround.com" xsi:schemaLocation="http://www.zeroturnaround.com http://update.zeroturnaround.com/jrebel/rebel-2_3.xsd">
<id>zhgh_cug_v4</id>
<classpath>
<dir name="E:/DevOpsCode/zhgh_cug_v4/target/classes">
</dir>
</classpath>
</application>
Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 KiB

@@ -140,13 +140,15 @@ layout("/layouts/platform.html"){
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="图片地址" prop="supplierPic">
<el-input maxlength="1000" placeholder="请输入供应商图片地址" v-model="formData.supplierPic"></el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="图片文件路径" prop="supplierPicPath">
<el-input maxlength="1000" placeholder="请输入供应商图片文件路径" v-model="formData.supplierPicPath"></el-input>
<el-form-item label="图标/图片" prop="supplierPic">
<file-upload
:value.sync="formData.supplierPic"
:upload_number="1"
accept=".jpg,.jpeg,.png,.gif,.webp"
upload_mode="image"
upload_result_category="interval"
upload_result_type="url"
></file-upload>
</el-form-item>
</el-col>
<el-col :span="24">
@@ -201,7 +203,6 @@ layout("/layouts/platform.html"){
supplierReturnOrderUrl: null,
supplierApiUrl: null,
supplierPic: null,
supplierPicPath: null,
contactInfo: null,
sroNo: null,
cooperationExpTime: null,
@@ -250,6 +251,7 @@ layout("/layouts/platform.html"){
},
openEdit(row) {
this.formData = Object.assign(this.defaultForm(), row)
delete this.formData.supplierPicPath
this.editDialogVisible = true
this.$nextTick(() => {
this.$refs.form && this.$refs.form.clearValidate()
@@ -262,7 +264,9 @@ layout("/layouts/platform.html"){
}
this.submitLoading = true
const url = this.formData.id ? "/doEdit" : "/doAdd"
const resp = await this.$axios.post(loc() + url, this.formData)
const formData = Object.assign({}, this.formData)
delete formData.supplierPicPath
const resp = await this.$axios.post(loc() + url, formData)
this.submitLoading = false
if (resp.code === 0) {
this.$message.success(resp.msg)
@@ -2,88 +2,126 @@
layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak>
<van-nav-bar title="积分商城" left-arrow left-text="返回" @click-left="back" fixed placeholder></van-nav-bar>
<van-nav-bar fixed placeholder title="积分商城" left-arrow left-text="返回" :border="false"
@click-left="back"></van-nav-bar>
<van-search v-model="keyword" class="fixed-search" shape="round" placeholder="搜索供应商名称"></van-search>
<div class="mall-page">
<section class="hero">
<div>
<div class="hero-title">积分商城</div>
<div class="hero-subtitle">好物兑换,积分随心用</div>
</div>
<div class="hero-points">
<span>可用积分</span>
<strong>{{ userPoints }}</strong>
</div>
</section>
<section class="quick-grid">
<div class="quick-card" @click="go('/platform/h5/points-mall/order')">
<van-icon name="orders-o"></van-icon>
<div>
<strong>我的订单</strong>
<span>查看兑换记录</span>
</div>
</div>
<div class="quick-card" @click="go('/platform/h5/points-mall/me')">
<van-icon name="manager-o"></van-icon>
<div>
<strong>我的积分</strong>
<span>账户积分概览</span>
</div>
</div>
</section>
<van-swipe class="banner" :autoplay="3500" indicator-color="#fff">
<van-swipe-item>
<div class="banner-inner red">
<strong>品牌专区</strong>
<span>精选供应商服务入口</span>
</div>
</van-swipe-item>
<van-swipe-item>
<div class="banner-inner blue">
<strong>积分抵扣</strong>
<span>1 积分可抵扣 1 元</span>
</div>
<main class="mall-page">
<van-swipe class="banner" :autoplay="3200" :height="bannerHeight" :show-indicators="false">
<van-swipe-item v-for="banner in banners" :key="banner.id">
<van-image class="banner-image" :src="banner.image" :alt="banner.alt" fit="fill"></van-image>
</van-swipe-item>
</van-swipe>
<section class="supplier-list">
<div class="section-title">供应商专区</div>
<van-skeleton title :row="6" :loading="loading" animated>
<van-empty v-if="!supplierList.length" description="暂无供应商"></van-empty>
<div class="supplier-card" v-for="item in supplierList" :key="item.id">
<div class="supplier-head">
<img :src="item.supplierPic || defaultLogo" class="supplier-logo" @error="onImgError">
<div class="supplier-meta">
<strong>{{ item.supplierName }}</strong>
<span>品牌专区</span>
</div>
<van-tag plain type="danger">积分兑换</van-tag>
</div>
<div class="supplier-products">
<div class="product-placeholder" v-for="n in 3" :key="n">
<van-icon name="gift-o"></van-icon>
<span>精选好物</span>
</div>
</div>
<van-button block color="#d64536" round size="small" @click="openSupplier(item)">查看更多</van-button>
<section class="account-panel">
<div class="account-card points-card">
<strong>我的积分</strong>
<p>购物时 1 积分可抵扣 1 元</p>
<div class="points-value">
<span>¥</span>
<b>{{ userPoints }}</b>
</div>
</div>
<div class="account-card order-card">
<strong>我的订单</strong>
<p>积分兑换,好物随心换</p>
<van-button class="order-button" round color="#35bfe6" @click="go('/platform/h5/points-mall/order')">
去查看
</van-button>
</div>
</section>
<section class="supplier-section">
<div class="section-head">
<strong>供应商专区</strong>
<span>{{ filteredSuppliers.length }} 家</span>
</div>
<van-skeleton title :row="6" :loading="loading" animated>
<van-empty v-if="!filteredSuppliers.length" description="暂无供应商"></van-empty>
<article v-for="item in filteredSuppliers" :key="item.id || item.supplierId" class="supplier-card">
<div class="supplier-icon">
<img :src="item.supplierPic || defaultLogo" :alt="item.supplierName" @error="onImgError">
</div>
<div class="supplier-info">
<div class="supplier-title">
<h2>{{ item.supplierName }}</h2>
<van-tag class="official-tag" plain type="warning">官方</van-tag>
</div>
<p>{{ item.contactInfo || '精选服务,品质兑换' }}</p>
<div class="tags">
<span>正品保障</span>
<span>积分兑换</span>
<span>售后无忧</span>
</div>
<div class="stats">
<span>{{ mallTypeName(item.mallType) }}</span>
<i></i>
<span>{{ item.sroNo || '服务专区' }}</span>
</div>
</div>
<van-button class="enter-supplier" round color="#ff651d" @click="openSupplier(item)">进入店铺
</van-button>
</article>
</van-skeleton>
</section>
</div>
</main>
<footer class="services">
<div class="service">
<van-icon name="shield-o" color="#3d78ed" size="22"></van-icon>
<span><b>正品保障</b><small>品牌服务</small></span>
</div>
<div class="service">
<van-icon name="logistics" color="#15b8a3" size="22"></van-icon>
<span><b>便捷配送</b><small>订单直达</small></span>
</div>
<div class="service">
<van-icon name="service-o" color="#725cf2" size="22"></van-icon>
<span><b>售后无忧</b><small>服务跟进</small></span>
</div>
</footer>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
data() {
return {
keyword: "",
loading: false,
bannerHeight: 86,
banners: [
{
id: "welfare",
image: "/assets/mobile/img/mall/supplier-banner/supplier-banner-welfare.png",
alt: "优质供应商精选"
},
{
id: "quality",
image: "/assets/mobile/img/mall/supplier-banner/supplier-banner-quality.png",
alt: "品质好物精选直供"
},
{
id: "daily",
image: "/assets/mobile/img/mall/supplier-banner/supplier-banner-daily.png",
alt: "精选商品每日推荐"
}
],
supplierList: [],
userPoints: 0,
defaultLogo: "/assets/platform/images/logo.png"
}
},
computed: {
filteredSuppliers() {
const keyword = (this.keyword || "").trim()
if (!keyword) {
return this.supplierList
}
return this.supplierList.filter((item) => {
return [item.supplierName, item.contactInfo, item.sroNo].join("").indexOf(keyword) > -1
})
}
},
methods: {
back() {
this.$pjaxReplace("/platform/h5/home")
@@ -93,7 +131,7 @@ layout("/layouts/platform_h5.html"){
},
async loadSuppliers() {
this.loading = true
const resp = await this.$axios.post("/platform/h5/points-mall/supplierList", { mallType: 1 })
const resp = await this.$axios.post("/platform/h5/points-mall/supplierList", {mallType: 1})
this.loading = false
if (resp.code === 0) {
this.supplierList = resp.data || []
@@ -116,44 +154,338 @@ layout("/layouts/platform_h5.html"){
},
onImgError(e) {
e.target.src = this.defaultLogo
},
mallTypeName(type) {
if (type === 1) {
return "积分商城"
}
if (type === 2) {
return "观影商城"
}
return "商城服务"
},
updateBannerHeight() {
const pagePadding = 34
const width = Math.max(document.documentElement.clientWidth - pagePadding, 0)
this.bannerHeight = Math.max(Math.round(width * 315 / 1492 * 1.18), 86)
}
},
created() {
this.loadSuppliers()
this.loadPoints()
},
mounted() {
this.updateBannerHeight()
window.addEventListener("resize", this.updateBannerHeight)
},
beforeDestroy() {
window.removeEventListener("resize", this.updateBannerHeight)
}
})
</script>
<style>
body { background: #f6f7fb; }
.mall-page { min-height: 100vh; padding: 12px 12px 24px; box-sizing: border-box; }
.hero { min-height: 126px; border-radius: 14px; padding: 18px; color: #fff; background: linear-gradient(135deg, #c62828 0%, #f2a13a 100%); display: flex; justify-content: space-between; align-items: flex-end; box-shadow: 0 8px 20px rgba(198, 40, 40, .18); }
.hero-title { font-size: 24px; font-weight: 700; }
.hero-subtitle { margin-top: 8px; font-size: 14px; opacity: .9; }
.hero-points { text-align: right; }
.hero-points span { display: block; font-size: 12px; opacity: .85; }
.hero-points strong { display: block; margin-top: 4px; font-size: 26px; }
.quick-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 12px; }
.quick-card { background: #fff; border-radius: 10px; padding: 14px 12px; display: flex; align-items: center; gap: 10px; box-shadow: 0 4px 14px rgba(20, 24, 35, .05); }
.quick-card .van-icon { font-size: 28px; color: #d64536; }
.quick-card strong { display: block; color: #222; font-size: 15px; }
.quick-card span { display: block; margin-top: 4px; color: #8a8f99; font-size: 12px; }
.banner { height: 86px; margin-top: 12px; border-radius: 12px; overflow: hidden; }
.banner-inner { height: 100%; color: #fff; padding: 18px; box-sizing: border-box; }
.banner-inner strong { display: block; font-size: 20px; }
.banner-inner span { display: block; margin-top: 8px; font-size: 13px; opacity: .9; }
.banner-inner.red { background: linear-gradient(135deg, #8f2d2d, #e85c4a); }
.banner-inner.blue { background: linear-gradient(135deg, #2262a6, #42a5f5); }
.section-title { margin: 18px 2px 10px; font-size: 17px; font-weight: 700; color: #222; }
.supplier-card { background: #fff; border-radius: 12px; padding: 12px; margin-bottom: 12px; box-shadow: 0 4px 14px rgba(20, 24, 35, .05); }
.supplier-head { display: flex; align-items: center; gap: 10px; }
.supplier-logo { width: 42px; height: 42px; border-radius: 10px; object-fit: cover; background: #f3f4f6; }
.supplier-meta { flex: 1; min-width: 0; }
.supplier-meta strong { display: block; font-size: 16px; color: #222; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.supplier-meta span { display: block; margin-top: 4px; font-size: 12px; color: #999; }
.supplier-products { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin: 12px 0; }
.product-placeholder { height: 74px; background: #fff7ed; border: 1px solid #ffe1bd; border-radius: 8px; color: #c76b2b; display: flex; flex-direction: column; justify-content: center; align-items: center; font-size: 12px; }
.product-placeholder .van-icon { font-size: 24px; margin-bottom: 6px; }
body {
background: #fafafa;
}
.fixed-search {
position: fixed;
z-index: 10;
top: 46px;
right: 0;
left: 0;
}
.mall-page {
display: flow-root;
box-sizing: border-box;
background: #fafafa;
}
.banner {
overflow: hidden;
margin: 64px 17px 0;
border-radius: 14px;
background: #fff;
}
.banner .van-swipe__track,
.banner .van-swipe-item {
height: 100%;
}
.banner-image {
display: block;
width: 100%;
height: 100%;
}
.banner-image .van-image__img {
display: block;
width: 100%;
height: 100%;
object-fit: fill;
}
.account-panel {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 14px;
margin: 12px 17px 0;
}
.account-card {
min-height: 110px;
padding: 13px 12px 12px;
box-sizing: border-box;
border-radius: 10px;
background: #fff;
box-shadow: 0 10px 22px rgba(25, 35, 60, .06);
}
.account-card strong {
display: block;
color: #8a4d12;
font-size: 15px;
line-height: 20px;
}
.account-card p {
overflow: hidden;
margin: 4px 0 0;
color: #d6a887;
font-size: 11px;
line-height: 16px;
white-space: nowrap;
text-overflow: ellipsis;
}
.points-value {
display: flex;
align-items: center;
gap: 15px;
margin-top: 13px;
}
.points-value span {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
border-radius: 50%;
color: #fff;
background: #ffb400;
font-size: 14px;
font-weight: 700;
box-shadow: 0 5px 12px rgba(255, 180, 0, .28);
}
.points-value b {
min-width: 0;
color: #ff8a00;
font-size: 20px;
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.order-card {
position: relative;
}
.order-button {
position: absolute;
right: 18px;
bottom: 15px;
min-width: 64px;
height: 28px;
padding: 0 12px;
font-size: 13px;
}
.supplier-section {
margin: 14px 17px 76px;
}
.section-head {
display: flex;
align-items: center;
justify-content: space-between;
margin: 0 2px 10px;
}
.section-head strong {
color: #1c1c1c;
font-size: 17px;
}
.section-head span {
color: #999;
font-size: 11px;
}
.supplier-card {
display: flex;
align-items: center;
gap: 14px;
margin-bottom: 13px;
padding: 14px;
border-radius: 17px;
background: #fff;
}
.supplier-icon {
width: 74px;
height: 74px;
flex: none;
overflow: hidden;
border-radius: 16px;
background: #f4f7ff;
}
.supplier-icon img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.supplier-info {
min-width: 0;
flex: 1;
}
.supplier-title, .tags, .stats, .service {
display: flex;
align-items: center;
}
.supplier-title {
gap: 7px;
}
.supplier-title h2 {
min-width: 0;
overflow: hidden;
margin: 0;
color: #1c1c1c;
font-size: 16px;
line-height: 20px;
text-overflow: ellipsis;
white-space: nowrap;
}
.official-tag {
flex: none;
padding: 0 4px;
font-size: 10px;
line-height: 16px;
}
.supplier-info p {
overflow: hidden;
margin: 4px 0;
color: #777;
font-size: 10px;
text-overflow: ellipsis;
white-space: nowrap;
}
.tags {
gap: 5px;
flex-wrap: nowrap;
overflow-x: auto;
overflow-y: hidden;
padding-bottom: 1px;
font-size: 9px;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
.tags::-webkit-scrollbar {
display: none;
}
.tags span {
flex: 0 0 auto;
padding: 2px 6px;
border-radius: 5px;
color: #ff651d;
background: #fff2e8;
white-space: nowrap;
}
.stats {
gap: 5px;
min-width: 0;
margin-top: 3px;
overflow: hidden;
color: #868686;
font-size: 10px;
white-space: nowrap;
}
.stats span {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.stats i {
width: 1px;
height: 13px;
flex: none;
background: #ddd;
}
.enter-supplier {
width: 72px;
height: 24px;
flex: none;
font-size: 10px;
}
.services {
position: fixed;
right: 0;
bottom: 0;
left: 0;
z-index: 9;
display: flex;
height: 58px;
background: #fff;
border-top: 1px solid #f2f2f2;
}
.service {
flex: 1;
justify-content: center;
gap: 5px;
min-width: 0;
font-size: 10px;
}
.service span {
display: grid;
min-width: 0;
font-size: 9px;
}
.service b {
color: #333;
font-weight: 600;
}
.service small {
overflow: hidden;
margin: 0;
color: #c0c4cc;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>
<!--#
}
@@ -1,101 +0,0 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak>
<van-nav-bar title="爱心帮扶" left-arrow left-text="返回" @click-left="back" fixed placeholder></van-nav-bar>
<div class="aid-page">
<section class="aid-hero">
<div class="aid-title">互助关爱基金</div>
<div class="aid-subtitle">自愿参与,互助共济,专款专用</div>
</section>
<section class="aid-card">
<div class="action-row">
<van-button round block color="#2f6fbd" @click="introVisible = true">查看基金介绍</van-button>
<van-button round block color="#d64536" @click="recordVisible = true">我的爱心记录</van-button>
</div>
<div class="identity-tabs">
<button :class="{active: anonymousType === 1}" @click="anonymousType = 1">实名</button>
<button :class="{active: anonymousType === 2}" @click="anonymousType = 2">匿名</button>
</div>
<div class="donate-box">
<div class="donate-title">捐款渠道</div>
<p v-if="anonymousType === 1">
当前为实名捐款。请通过支付宝或微信扫码捐款,捐款人信息将以当前登录用户记录。
</p>
<p v-else>
当前为匿名捐款。系统不会记录个人捐款身份,可从支付渠道记录中查询。
</p>
<div class="qr-placeholder">
<van-icon name="qr"></van-icon>
<span>二维码接口接入后展示</span>
</div>
</div>
</section>
</div>
<van-popup v-model="introVisible" round position="center" :style="{ width: '86%' }">
<div class="popup-content">
<h3>互助关爱基金</h3>
<p>
为完善员工福利与帮扶体系,互助关爱基金用于在员工家庭遭遇突发困难时提供及时、有力的支持。
</p>
<p>
基金遵循自愿参与、互助共济、公平公正、专款专用原则。
</p>
<van-button block round color="#d64536" @click="introVisible = false">我知道了</van-button>
</div>
</van-popup>
<van-popup v-model="recordVisible" round position="bottom" :style="{ height: '70%' }">
<div class="record-sheet">
<div class="sheet-title">我的爱心记录</div>
<van-empty description="记录接口接入后展示"></van-empty>
<van-button block round color="#d64536" @click="recordVisible = false">关闭</van-button>
</div>
</van-popup>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
data() {
return {
anonymousType: 1,
introVisible: false,
recordVisible: false
}
},
methods: {
back() {
this.$pjaxReplace("/platform/h5/points-mall")
}
}
})
</script>
<style>
body { background: #f6f7fb; }
.aid-page { min-height: 100vh; padding-bottom: 24px; background: #f6f7fb; }
.aid-hero { min-height: 180px; padding: 28px 18px; color: #fff; box-sizing: border-box; background: linear-gradient(135deg, #1f5f8b, #d64536); display: flex; flex-direction: column; justify-content: flex-end; }
.aid-title { font-size: 27px; font-weight: 700; }
.aid-subtitle { margin-top: 8px; font-size: 14px; opacity: .9; }
.aid-card { margin: -24px 12px 0; border-radius: 14px; background: #fff; padding: 14px; box-shadow: 0 8px 22px rgba(20, 24, 35, .08); position: relative; }
.action-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.identity-tabs { margin: 16px 0; padding: 4px; border-radius: 10px; background: #f1f3f6; display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
.identity-tabs button { height: 38px; border: 0; border-radius: 8px; background: transparent; color: #666; font-size: 15px; }
.identity-tabs button.active { background: #2f6fbd; color: #fff; font-weight: 700; }
.donate-box { border: 1px solid #e8edf5; border-radius: 12px; padding: 14px; }
.donate-title { font-size: 17px; font-weight: 700; color: #222; }
.donate-box p { color: #555; font-size: 14px; line-height: 1.7; }
.qr-placeholder { height: 190px; border-radius: 12px; background: #f7f8fb; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #999; }
.qr-placeholder .van-icon { font-size: 58px; color: #c8ccd6; margin-bottom: 8px; }
.popup-content { padding: 22px; }
.popup-content h3 { text-align: center; margin: 0 0 14px; color: #1f5f8b; }
.popup-content p { font-size: 14px; line-height: 1.8; color: #444; }
.record-sheet { padding: 18px; }
.sheet-title { text-align: center; font-size: 18px; font-weight: 700; margin-bottom: 12px; }
</style>
<!--#
}
#-->
@@ -1,74 +0,0 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak>
<van-nav-bar title="我的" left-arrow left-text="返回" @click-left="back" fixed placeholder></van-nav-bar>
<div class="me-page">
<section class="profile">
<div class="avatar">{{ avatarText }}</div>
<div class="profile-text">
<strong>{{ displayName }}</strong>
<span>工号:{{ userInfo.loginname || userInfo.username || userInfo.userId || "-" }}</span>
</div>
</section>
<section class="points-card">
<div>
<span>我的积分</span>
<strong>{{ userPoints }}</strong>
</div>
<van-icon name="gold-coin-o"></van-icon>
</section>
<van-cell-group inset>
<van-cell title="我的订单" icon="orders-o" is-link @click="go('/platform/h5/points-mall/order')"></van-cell>
<van-cell title="积分商城" icon="shop-o" is-link @click="go('/platform/h5/points-mall')"></van-cell>
<van-cell title="影票专区" icon="video-o" is-link @click="go('/platform/h5/points-mall/movies')"></van-cell>
</van-cell-group>
</div>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
data() {
return { userInfo: {}, userPoints: 0 }
},
computed: {
displayName() {
return this.userInfo.username || this.userInfo.loginname || "用户"
},
avatarText() {
return this.displayName ? this.displayName.slice(0, 1) : "我"
}
},
methods: {
back() { this.$pjaxReplace("/platform/h5/points-mall") },
go(url) { this.$pjaxReplace(url) },
async loadUser() {
const resp = await this.$axios.post("/platform/h5/points-mall/me/userInfo")
if (resp.code === 0) this.userInfo = resp.data || {}
},
async loadPoints() {
const resp = await this.$axios.post("/platform/h5/points-mall/me/points")
if (resp.code === 0) this.userPoints = resp.data || 0
}
},
created() {
this.loadUser()
this.loadPoints()
}
})
</script>
<style>
body { background: #f6f7fb; }
.me-page { min-height: 100vh; padding: 12px; box-sizing: border-box; }
.profile { min-height: 128px; border-radius: 14px; padding: 18px; display: flex; align-items: center; gap: 14px; background: linear-gradient(135deg, #c62828, #28456f); color: #fff; box-shadow: 0 8px 20px rgba(20, 24, 35, .12); }
.avatar { width: 58px; height: 58px; border-radius: 50%; background: rgba(255,255,255,.2); border: 2px solid rgba(255,255,255,.75); display: flex; align-items: center; justify-content: center; font-size: 24px; font-weight: 700; }
.profile-text strong { display: block; font-size: 19px; }
.profile-text span { display: block; margin-top: 8px; font-size: 13px; opacity: .9; }
.points-card { margin: 12px 0; border-radius: 12px; padding: 18px; background: #fff; display: flex; justify-content: space-between; align-items: center; box-shadow: 0 4px 14px rgba(20, 24, 35, .05); }
.points-card span { display: block; color: #888; font-size: 13px; }
.points-card strong { display: block; margin-top: 6px; color: #d64536; font-size: 28px; }
.points-card .van-icon { color: #f2a13a; font-size: 44px; }
</style>
<!--#
}
#-->
@@ -1,73 +0,0 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak>
<van-nav-bar title="影票专区" left-arrow left-text="返回" @click-left="back" fixed placeholder></van-nav-bar>
<div class="mall-page">
<section class="hero movie">
<div>
<div class="hero-title">影票专区</div>
<div class="hero-subtitle">精选观影权益,积分兑换更轻松</div>
</div>
<van-icon name="video-o"></van-icon>
</section>
<section class="supplier-list">
<div class="section-title">影票供应商</div>
<van-empty v-if="!supplierList.length && !loading" description="暂无影票供应商"></van-empty>
<van-skeleton title :row="5" :loading="loading" animated>
<div class="supplier-card" v-for="item in supplierList" :key="item.id" @click="openSupplier(item)">
<div class="supplier-head">
<img :src="item.supplierPic || defaultLogo" class="supplier-logo" @error="onImgError">
<div class="supplier-meta">
<strong>{{ item.supplierName }}</strong>
<span>点击进入兑换</span>
</div>
<van-icon name="arrow"></van-icon>
</div>
</div>
</van-skeleton>
</section>
</div>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
data() {
return { loading: false, supplierList: [], defaultLogo: "/assets/platform/images/logo.png" }
},
methods: {
back() { this.$pjaxReplace("/platform/h5/points-mall") },
async loadSuppliers() {
this.loading = true
const resp = await this.$axios.post("/platform/h5/points-mall/supplierList", { mallType: 2 })
this.loading = false
if (resp.code === 0) this.supplierList = resp.data || []
},
openSupplier(item) {
if (item.supplierUrl) window.location.href = item.supplierUrl
else this.$toast("暂无跳转地址")
},
onImgError(e) { e.target.src = this.defaultLogo }
},
created() { this.loadSuppliers() }
})
</script>
<style>
body { background: #f6f7fb; }
.mall-page { min-height: 100vh; padding: 12px; box-sizing: border-box; }
.hero { min-height: 126px; border-radius: 14px; padding: 18px; color: #fff; display: flex; justify-content: space-between; align-items: flex-end; box-shadow: 0 8px 20px rgba(20, 24, 35, .12); }
.hero.movie { background: linear-gradient(135deg, #28456f, #d64536); }
.hero .van-icon { font-size: 46px; opacity: .88; }
.hero-title { font-size: 24px; font-weight: 700; }
.hero-subtitle { margin-top: 8px; font-size: 14px; opacity: .9; }
.section-title { margin: 18px 2px 10px; font-size: 17px; font-weight: 700; color: #222; }
.supplier-card { background: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 4px 14px rgba(20, 24, 35, .05); }
.supplier-head { display: flex; align-items: center; gap: 10px; }
.supplier-logo { width: 44px; height: 44px; border-radius: 10px; object-fit: cover; background: #f3f4f6; }
.supplier-meta { flex: 1; min-width: 0; }
.supplier-meta strong { display: block; font-size: 16px; color: #222; }
.supplier-meta span { display: block; margin-top: 4px; font-size: 12px; color: #999; }
</style>
<!--#
}
#-->
@@ -3,37 +3,81 @@ layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak>
<van-nav-bar title="我的订单" left-arrow left-text="返回" @click-left="back" fixed placeholder></van-nav-bar>
<section class="order-summary">
<span>剩余积分</span>
<strong>{{ userPoints }}</strong>
</section>
<van-sticky offset-top="46px">
<van-tabs v-model="pageForm.orderState" color="#d64536" title-active-color="#d64536" @change="doSearch">
<van-tab v-for="tab in tabList" :key="tab.code" :name="tab.code" :title="tab.name"></van-tab>
</van-tabs>
</van-sticky>
<van-list v-model="tableLoading" :finished="tableFinished" finished-text="没有更多了" @load="pageData">
<div class="order-card" v-for="item in tableData" :key="item.id || item.orderId">
<div class="card-head">
<strong>{{ item.supplierName || "积分商城" }}</strong>
<span :class="'state state-' + item.orderState">{{ orderStateName(item.orderState) }}</span>
<div class="order-page">
<section class="order-header">
<div class="points-content">
<div class="points-label">剩余积分</div>
<div class="points-value">
<span>¥</span>
<strong>{{ userPoints }}</strong>
</div>
</div>
<div class="product-row" v-if="item.productImgList && item.productImgList.length">
<van-image v-for="(img, index) in item.productImgList.slice(0, 3)" :key="index" :src="img" fit="cover" radius="6"></van-image>
</section>
<van-sticky>
<div class="filter-bar">
<van-tabs
v-model="pageForm.orderState"
class="custom-tabs"
line-width="20px"
color="#1989fa"
title-active-color="#1989fa"
title-inactive-color="#666"
:border="false"
@change="doSearch"
>
<van-tab v-for="tab in tabList" :key="tab.code" :name="tab.code" :title="tab.name"></van-tab>
</van-tabs>
</div>
<div class="order-info">
<div><span>订单号</span><em>{{ item.orderId }}</em></div>
<div><span>消费积分</span><em class="points">{{ item.pointsPrice || 0 }}</em></div>
<div><span>运费</span><em>{{ item.freightCost || 0 }}</em></div>
<div><span>创建时间</span><em>{{ item.orderCreateTime || "-" }}</em></div>
</div>
<div class="card-actions">
<van-button v-if="Number(item.orderState) === 4" round size="small" color="#4caf50" @click="openSupplierOrder(item)">确认收货</van-button>
<van-button round size="small" color="#409eff" @click="openSupplierOrder(item)">查看订单</van-button>
</div>
</div>
<van-empty v-if="!tableLoading && !tableData.length" description="暂无订单"></van-empty>
</van-list>
</van-sticky>
<van-list v-if="tableData.length" class="list-container" v-model="tableLoading" :finished="tableFinished" finished-text="没有更多了" @load="pageData">
<article class="order-card" v-for="item in tableData" :key="item.id || item.orderId">
<header class="card-header">
<div class="shop-info">
<span class="shop-name">{{ item.supplierName || "积分商城" }}</span>
<van-icon name="arrow" color="#ccc" size="12"></van-icon>
</div>
<span class="status-text" :class="getStatusClass(item.orderState)">{{ orderStateName(item.orderState) }}</span>
</header>
<div class="product-row" v-if="item.productImgList && item.productImgList.length">
<van-image v-for="(img, index) in item.productImgList.slice(0, 3)" :key="index" :src="img" fit="cover" radius="4"></van-image>
</div>
<div class="order-info">
<div class="info-row">
<span>订单号:</span>
<em>{{ item.orderId }}</em>
</div>
<div class="info-row">
<span>消费积分:</span>
<em class="points">{{ item.pointsPrice || 0 }}</em>
</div>
<div class="info-row" v-if="item.freightCost">
<span>运费:</span>
<em>¥ {{ item.freightCost }}</em>
</div>
<div class="info-row">
<span>创建时间:</span>
<em>{{ item.orderCreateTime || "-" }}</em>
</div>
</div>
<footer class="card-footer">
<van-button v-if="Number(item.orderState) === 4" round size="small" color="#4CAF50" @click.stop="openSupplierOrder(item)">确认收货</van-button>
<van-button round size="small" color="#409EFF" @click.stop="openSupplierOrder(item)">查看订单</van-button>
</footer>
</article>
</van-list>
<van-empty
v-else-if="!tableLoading"
class="empty-tip"
image="/assets/platform/plugins/vant-green/images/nodata/nodata.png"
description="暂无订单数据"
></van-empty>
</div>
</div>
<script nonce="${cspNonce!}">
new Vue({
@@ -102,35 +146,64 @@ layout("/layouts/platform_h5.html"){
orderStateName(value) {
const item = this.tabList.find((tab) => String(tab.code) === String(value))
if (item) return item.name
const map = { 0: "待付款", 1: "已支付", 2: "进行中", 4: "已签收", 5: "已完成", 6: "换货", 7: "退货" }
return map[value] || "未知"
const map = { 0: "待付款", 1: "已支付", 2: "进行中", 3: "已取消", 4: "已签收", 5: "已完成", 6: "换货", 7: "退货" }
return map[value] || "未知状态"
},
getStatusClass(status) {
const value = Number(status)
if (value === 0) return "orange"
if (value === 1 || value === 2) return "blue"
if (value === 4) return "green"
if (value === 6) return "purple"
if (value === 7) return "red"
return "gray"
}
},
created() {
this.loadTabs()
this.loadPoints()
this.pageData()
}
})
</script>
<style>
body { background: #f6f7fb; }
.order-summary { margin: 12px; min-height: 92px; border-radius: 14px; padding: 18px; color: #fff; background: linear-gradient(135deg, #c62828, #f2a13a); box-sizing: border-box; }
.order-summary span { display: block; font-size: 13px; opacity: .9; }
.order-summary strong { display: block; margin-top: 8px; font-size: 28px; }
.order-card { margin: 12px; padding: 14px; border-radius: 12px; background: #fff; box-shadow: 0 4px 14px rgba(20, 24, 35, .05); }
.card-head { display: flex; justify-content: space-between; align-items: center; padding-bottom: 10px; border-bottom: 1px solid #f0f1f5; }
.card-head strong { font-size: 15px; color: #222; }
.state { font-size: 13px; color: #999; }
.state-4, .state-5 { color: #43a047; }
.state-6, .state-7 { color: #d64536; }
body { background: #fbfcff; }
.order-page { background: #fbfcff; }
.order-header { position: relative; height: 166px; overflow: hidden; background: url("/assets/mobile/img/mall/order/my-order.png") center / cover no-repeat; }
.points-content { position: absolute; left: 24px; top: 50px; z-index: 2; }
.points-label { color: #111; font-size: 14px; line-height: 20px; }
.points-value { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
.points-value span { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 50%; color: #fff; background: #ffbf00; font-size: 15px; font-weight: 700; box-shadow: 0 3px 8px rgba(255, 191, 0, .35); }
.points-value strong { color: #000; font-size: 28px; line-height: 32px; font-weight: 800; }
.filter-bar { display: flex; align-items: center; height: 46px; background: #fff; box-shadow: 0 2px 8px rgba(40, 50, 70, .03); }
.custom-tabs { flex: 1; min-width: 0; }
.custom-tabs .van-tabs__wrap { height: 46px; }
.custom-tabs .van-tab { flex: none; padding: 0 14px; color: #666; font-size: 14px; }
.custom-tabs .van-tab--active { color: #1989fa; font-weight: 500; }
.custom-tabs .van-tabs__line { bottom: 16px; height: 2px; background: #1989fa; }
.list-container { background: #fbfcff; }
.order-card { margin: 12px; padding: 16px; border-radius: 12px; background: #fff; box-shadow: 0 2px 8px rgba(0, 0, 0, .04); }
.card-header { display: flex; align-items: center; justify-content: space-between; padding-bottom: 12px; border-bottom: 1px solid #f5f5f5; }
.shop-info { display: flex; align-items: center; min-width: 0; }
.shop-name { overflow: hidden; color: #333; font-size: 15px; font-weight: 700; white-space: nowrap; text-overflow: ellipsis; }
.status-text { flex: none; margin-left: 10px; font-size: 13px; font-weight: 500; }
.status-text.orange { color: #ff8a00; }
.status-text.blue { color: #1989fa; }
.status-text.green { color: #23b65d; }
.status-text.purple { color: #7b61ee; }
.status-text.red { color: #e64a3b; }
.status-text.gray { color: #999; }
.product-row { display: flex; gap: 8px; padding: 12px 0; }
.product-row .van-image { width: 70px; height: 70px; background: #f3f4f6; }
.order-info { padding: 10px; border-radius: 8px; background: #fafafa; }
.order-info div { display: flex; justify-content: space-between; font-size: 13px; line-height: 28px; }
.order-info span { color: #888; }
.order-info em { color: #333; font-style: normal; max-width: 65%; text-align: right; word-break: break-all; }
.order-info .points { color: #d64536; font-weight: 700; }
.card-actions { display: flex; justify-content: flex-end; gap: 8px; padding-top: 12px; }
.order-info { padding: 12px; margin-bottom: 12px; border-radius: 8px; background: #fafafa; }
.info-row { display: flex; justify-content: space-between; gap: 12px; margin-bottom: 8px; font-size: 13px; line-height: 18px; }
.info-row:last-child { margin-bottom: 0; }
.info-row span { flex: none; color: #999; }
.info-row em { min-width: 0; color: #333; font-style: normal; text-align: right; word-break: break-all; }
.info-row .points { color: #c62828; font-weight: 700; }
.card-footer { display: flex; justify-content: flex-end; gap: 10px; }
.card-footer .van-button { min-width: 86px; height: 32px; padding: 0 16px; font-size: 13px; }
.empty-tip { padding-top: 36px; }
</style>
<!--#
}