first commit

This commit is contained in:
2026-01-08 14:58:16 +08:00
commit 556e5d64aa
9575 changed files with 1621095 additions and 0 deletions
@@ -0,0 +1,167 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<style>
/* 列表卡片样式 */
.wf-welfare-list {
padding: 10px;
}
.wf-welfare-card {
position: relative;
background: #fff;
border-radius: 8px;
margin-bottom: 15px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
/* 左右布局容器 */
.wf-card-layout {
display: flex;
padding: 15px;
}
/* 左侧图片容器 */
.wf-card-img-container {
width: 120px;
height: 120px;
flex-shrink: 0;
margin-right: 15px;
}
/* 右侧信息容器 */
.wf-card-content {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
justify-content: space-between;
}
/* 标题样式 */
.wf-card-heading {
font-size: 16px;
font-weight: bold;
margin-bottom: 8px;
color: #333;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
}
/* 时间信息样式 */
.wf-time-row {
font-size: 14px;
color: #666;
margin-bottom: 5px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
}
.wf-time-label {
color: #999;
display: inline-block;
width: 70px;
}
</style>
<div id="app" v-cloak>
<van-sticky>
<van-nav-bar @click-left="()=>this.$pjaxReplace('/platform/h5/home')" left-arrow left-text="返回" placeholder title="我的福利"></van-nav-bar>
<van-dropdown-menu>
<year-van-dropdown-item :num="5" @change="doSearch" v-model="pageForm.year"></year-van-dropdown-item>
</van-dropdown-menu>
</van-sticky>
<div>
<van-list :finished="finished" @load="pageData" finished-text="没有更多了" v-if="tableData.length>0" v-model="loading">
<div class="wf-welfare-list">
<div class="wf-welfare-card" v-for="row in tableData" :key="row.id" @click="openView(row)">
<van-tag mark :type="row.isChoose?'success':''" style="position: absolute; left: 5px; top: 5px; z-index: 1">
{{row.isChoose?'已选择':'未选择'}}
</van-tag>
<div class="wf-card-layout">
<div class="wf-card-img-container">
<van-image :src="row.cover" fit="cover" width="100%" height="100%" radius="4"></van-image>
</div>
<div class="wf-card-content">
<div class="wf-card-heading">{{row.name}}</div>
<div class="wf-time-row">
<span class="wf-time-label">开始时间:</span>
{{$moment(row.choiceTimeStart).format('YYYY-MM-DD HH:mm')}}
</div>
<div class="wf-time-row">
<span class="wf-time-label">结束时间:</span>
{{$moment(row.choiceTimeEnd).format('YYYY-MM-DD HH:mm')}}
</div>
</div>
</div>
</div>
</div>
</van-list>
<van-empty v-else image="/assets/platform/plugins/vant-green/images/nodata/nodata.png" description="暂无数据"></van-empty>
</div>
<select-view ref="selectViewRef"></select-view>
</div>
<script nonce="${cspNonce!}">
<!--#include('selectView.js'){}#-->
new Vue({
el: "#app",
store,
components: {
"select-view": selectView
},
data() {
return {
tableData: [],
finished: false,
loading: false,
pageForm: {
pageNumber: 1,
pageSize: 5,
totalCount: 0,
year: new Date().getFullYear()
},
yearList: []
}
},
methods: {
doSearch() {
this.tableKey = new Date().getTime()
this.pageForm.pageNumber = 1
this.pageForm.totalCount = 0
this.tableData = []
this.pageData()
},
pageData() {
this.loading = true
this.$axios.post("/platform/welfare/mine/pageData", this.pageForm).then((res) => {
this.tableData = this.tableData.concat(res.data.list)
if (this.tableData.length === res.data.totalCount) {
this.finished = true
} else {
this.pageForm.pageNumber++
}
this.pageForm.totalCount = res.data.totalCount
this.loading = false
})
},
async getUserSelection(projectId) {
const resp = await this.$axios.post("/platform/welfare/mine/getUserSelection", {
projectId: projectId,
userId: this.$store.state.user.id
})
return resp
},
async openView(item) {
this.$refs.selectViewRef.onOpen(item.id)
}
},
created() {
this.pageData()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,541 @@
const selectView = {
template: /*language=HTML*/ `
<van-action-sheet
v-model="visible"
:title="'我的福利选择'"
close-icon="close"
safe-area-inset-bottom
@close="handleClose"
:overlay-class="detailVisible ? 'no-overlay' : ''"
>
<div class="welfare-view">
<!-- 内容区域 -->
<div class="welfare-view__content">
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
<van-skeleton title :row="6" :loading="loading" animated>
<!-- 项目基本信息 -->
<div class="welfare-card">
<van-collapse v-model="activeNames">
<van-collapse-item name="1">
<template #title>
<div class="welfare-card__header">
<i class="el-icon-s-flag"></i>
<span>项目基本信息</span>
</div>
</template>
<div class="welfare-card__content">
<div class="info-row">
<div class="info-row__label">项目名称</div>
<div class="info-row__value">{{ projectInfo.name }}</div>
</div>
<div class="info-row info-row--time">
<div class="info-row__label">选择时间</div>
<div class="info-row__value">
<div class="time-item time-item--start">{{
$moment(projectInfo.choiceTimeStart).format('YYYY-MM-DD HH:mm')
}}
</div>
<div class="time-item time-item--end">{{
$moment(projectInfo.choiceTimeEnd).format('YYYY-MM-DD HH:mm') }}
</div>
</div>
</div>
<div class="info-row info-row--time">
<div class="info-row__label">发放时间</div>
<div class="info-row__value">
<div class="time-item time-item--start">{{
$moment(projectInfo.provideTimeStart).format('YYYY-MM-DD HH:mm')
}}
</div>
<div class="time-item time-item--end">{{
$moment(projectInfo.provideTimeEnd).format('YYYY-MM-DD HH:mm')
}}
</div>
</div>
</div>
<div class="info-row">
<div class="info-row__label">发放地点</div>
<div class="info-row__value">{{ projectInfo.provideAddress || '暂无'
}}
</div>
</div>
</div>
</van-collapse-item>
</van-collapse>
</div>
<!-- 联系信息 -->
<div class="welfare-card">
<div class="welfare-card__header">
<i class="el-icon-user"></i>
<span>联系信息</span>
</div>
<div class="welfare-card__content">
<div class="info-row">
<div class="info-row__label">联系电话</div>
<div class="info-row__value">{{ mergedSelections[0]?.mobile || '暂无' }}</div>
</div>
<div class="info-row">
<div class="info-row__label">收货地址</div>
<div class="info-row__value">{{ mergedSelections[0]?.receiveAddress || '暂无'
}}
</div>
</div>
</div>
</div>
<!-- 我的选择 -->
<div class="selections-container">
<div class="welfare-card">
<div class="welfare-card__header">
<i class="el-icon-shopping-cart-full"></i>
<span>我的选择</span>
</div>
<div v-if="mergedSelections.length > 0" class="selection-list">
<div class="selection-item"
v-for="item in mergedSelections"
:key="item.id"
@click="showOptionDetail(item)">
<div class="selection-item__image">
<img :src="item.imgUrl"
alt="福利图片"
@error="handleImageError">
</div>
<div class="selection-item__content">
<div class="selection-item__name">{{ item.optionName }}</div>
<div class="selection-item__meta">
<div class="selection-item__count">
<i class="el-icon-shopping-cart-2"></i> {{ item.selectNum }}
</div>
<div class="selection-item__time">
<i class="el-icon-time"></i> {{ formatDate(item.selectTime) }}
</div>
</div>
</div>
<div class="selection-item__arrow">
<i class="el-icon-arrow-right"></i>
</div>
</div>
</div>
<div v-else class="empty-state">
<i class="el-icon-warning-outline"></i>
<span>您还未选择任何福利项目</span>
</div>
</div>
</div>
</van-skeleton>
</van-pull-refresh>
</div>
<!-- 选项详情弹窗 -->
<van-action-sheet
v-model="detailVisible"
:title="currentOption?.optionName || '福利详情'"
close-icon="close"
safe-area-inset-bottom
@close="closeDetail"
:z-index="2001"
>
<div class="welfare-detail" v-if="currentOption">
<van-skeleton title :row="10" :loading="descLoading" animated>
<div class="welfare-detail__content rich-text"
v-html="currentOption.description || '暂无详细说明'"></div>
</van-skeleton>
</div>
</van-action-sheet>
</div>
</van-action-sheet>
`,
data() {
return {
visible: false,
projectId: null,
loading: false,
refreshing: false,
descLoading: false,
projectInfo: {
name: "",
choiceTimeStart: "",
choiceTimeEnd: "",
provideTimeStart: "",
provideTimeEnd: "",
provideAddress: "",
options: []
},
selectedOptions: [],
detailVisible: false,
currentOption: null,
activeNames:[]
}
},
computed: {
mergedSelections() {
return this.selectedOptions.map((item) => {
const option = this.projectInfo.options?.find((opt) => opt.id === item.selectOptionId) || {}
return {
...item,
optionName: option.optionName || "未知选项",
imgUrl: option.imgUrl,
description: option.description
}
})
}
},
methods: {
onOpen(id) {
this.projectId = id
this.visible = true
this.loadData()
},
handleClose() {
// 如果详情弹窗打开,先关闭详情弹窗
if (this.detailVisible) {
this.closeDetail()
return
}
// 否则关闭整个组件
this.$emit("close")
},
async onRefresh() {
try {
await this.loadData()
} finally {
this.refreshing = false
}
},
async loadData() {
try {
this.loading = true
await Promise.all([this.getProjectInfo(), this.getUserSelections()])
} catch (error) {
console.error("加载数据失败:", error)
this.$toast && this.$toast("加载数据失败,请稍后重试")
} finally {
this.loading = false
}
},
async getProjectInfo() {
try {
const { code, data, msg } = await this.$axios.post("/platform/welfare/common/projectInfo", {
id: this.projectId
})
if (code === 0) {
this.projectInfo = data || this.projectInfo
} else {
console.warn("获取项目信息失败:", msg)
}
} catch (error) {
console.error("获取项目信息异常:", error)
throw error
}
},
async getUserSelections() {
try {
const { code, data, msg } = await this.$axios.post("/platform/welfare/userSelect/getUserSelection", { projectId: this.projectId })
if (code === 0) {
this.selectedOptions = data || []
} else {
console.warn("获取用户选择失败:", msg)
}
} catch (error) {
console.error("获取用户选择异常:", error)
throw error
}
},
showOptionDetail(option) {
this.descLoading = true
this.currentOption = option
this.detailVisible = true
// 短暂延迟后关闭加载状态,给内容渲染留出时间
setTimeout(() => {
this.descLoading = false
}, 300)
},
closeDetail() {
this.detailVisible = false
},
handleImageError(e) {
e.target.src = "/static/images/default-welfare.png"
},
formatDate(dateStr) {
if (!dateStr) return ""
try {
// 简化日期显示,只保留月-日 时:分
const date = new Date(dateStr.replace(/-/g, "/"))
return date.getMonth() + 1 + "-" + date.getDate() + " " + date.getHours() + ":" + date.getMinutes().toString().padStart(2, "0")
} catch (e) {
console.error("日期格式化错误:", e)
return dateStr
}
}
},
style: /*language=CSS*/ `
/deep/ .welfare-view {
background: #f7f8fa;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
/* 内容区域样式 */
/deep/ .welfare-view__content {
padding: 16px;
height: 100%;
}
/* 卡片通用样式 */
/deep/ .welfare-card {
background: #fff;
border-radius: 8px;
margin-bottom: 12px;
overflow: hidden;
}
/deep/ .welfare-card__header {
display: flex;
align-items: center;
padding: 16px;
position: relative;
}
/deep/ .welfare-card__header::after {
content: '';
position: absolute;
left: 16px;
right: 16px;
bottom: 0;
height: 1px;
background: #f5f5f5;
transform: scaleY(0.5);
}
/deep/ .welfare-card__header i {
font-size: 16px;
margin-right: 6px;
color: var(--color-primary);
}
/deep/ .welfare-card__header span {
font-size: 15px;
font-weight: 500;
color: #323233;
}
/deep/ .welfare-card__content {
padding: 16px;
}
/* 信息行样式 */
/deep/ .info-row {
display: flex;
margin-bottom: 12px;
align-items: center;
}
/deep/ .info-row:last-child {
margin-bottom: 0;
}
/deep/ .info-row__label {
width: 70px;
color: #969799;
font-size: 14px;
flex-shrink: 0;
}
/deep/ .info-row__value {
flex: 1;
color: #323233;
font-size: 14px;
min-width: 0;
}
/deep/ .info-row--time {
align-items: flex-start;
}
/deep/ .info-row--time .info-row__value {
display: flex;
flex-direction: column;
gap: 4px;
}
/deep/ .time-item {
position: relative;
padding-left: 42px;
line-height: 20px;
}
/deep/ .time-item--start::before {
content: "开始:";
position: absolute;
left: 0;
color: #969799;
font-size: 12px;
}
/deep/ .time-item--end::before {
content: "结束:";
position: absolute;
left: 0;
color: #969799;
font-size: 12px;
}
/* 选择列表样式 */
/deep/ .selections-container {
margin-bottom: 12px;
}
/deep/ .selection-list {
padding: 8px 16px;
}
/deep/ .selection-item {
display: flex;
align-items: center;
padding: 12px;
background: #fff;
position: relative;
border-radius: 8px;
margin-bottom: 12px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02);
}
/deep/ .selection-item:active {
background: #f8f9fa;
}
/deep/ .selection-item:last-child {
margin-bottom: 0;
}
/deep/ .selection-item__image {
width: 72px;
height: 72px;
margin-right: 12px;
border-radius: 6px;
overflow: hidden;
background: #f5f6f7;
flex-shrink: 0;
}
/deep/ .selection-item__image img {
width: 100%;
height: 100%;
object-fit: cover;
}
/deep/ .selection-item__content {
flex: 1;
min-width: 0;
padding: 4px 0;
}
/deep/ .selection-item__name {
font-size: 15px;
font-weight: 500;
color: #323233;
margin-bottom: 8px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/deep/ .selection-item__meta {
display: flex;
align-items: center;
font-size: 13px;
color: #969799;
line-height: 1.4;
}
/deep/ .selection-item__count {
display: flex;
align-items: center;
margin-right: 16px;
background: #f7f8fa;
padding: 4px 8px;
border-radius: 4px;
}
/deep/ .selection-item__time {
display: flex;
align-items: center;
background: #f7f8fa;
padding: 4px 8px;
border-radius: 4px;
}
/deep/ .selection-item__meta i {
margin-right: 4px;
font-size: 14px;
}
/deep/ .selection-item__arrow {
color: #c8c9cc;
font-size: 16px;
padding: 0 0 0 12px;
}
/* 空状态样式 */
/deep/ .empty-state {
display: flex;
flex-direction: column;
align-items: center;
padding: 32px 16px;
color: #969799;
}
/deep/ .empty-state__icon {
font-size: 48px;
margin-bottom: 8px;
}
/* 详情弹窗样式 */
/deep/ .welfare-detail {
padding: 20px 16px;
max-height: 60vh;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
/deep/ .welfare-detail__content {
font-size: 14px;
line-height: 1.6;
color: #323233;
}
/deep/ .rich-text {
overflow-x: hidden;
}
/deep/ .rich-text img {
max-width: 100%;
height: auto;
margin: 8px 0;
border-radius: 4px;
}
/deep/ .rich-text p {
margin: 8px 0;
}
/* 阻止嵌套 action-sheet 时的背景重叠 */
/deep/ .no-overlay {
background-color: transparent !important;
}
`
}