feat:代表证卡套样式优化;消息中心、待办中心样式优化

This commit is contained in:
2026-08-30 17:59:02 +08:00
parent 5f73c6a7a3
commit f89b0f643d
6 changed files with 951 additions and 520 deletions
@@ -62,10 +62,9 @@ public class TeacherCongressDelegateCardController {
private SysRoleService sysRoleService;
@At("/h5")
@Ok("void")
@Ok("beetl:/platform/zhghh5/democratic/teachercongress/delegate/card/index.html")
@SaCheckLogin
public void h5Index(HttpServletResponse response) throws IOException, WriterException {
showCard(response);
public void h5Index() {
}
@At
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

@@ -2,54 +2,240 @@
layout("/layouts/platform_h5.html"){
#-->
<style>
.card-page {
display: flex;
justify-content: center;
height: calc(100vh - 46px);
flex-direction: column;
padding: 0 24px;
box-sizing: border-box;
.van-nav-bar__content {
height: 46px;
}
.card-page .van-button {
width: 184px;
margin-left: auto;
margin-right: auto;
font-weight: 700;
font-size: 17px;
border-radius: 4px;
background-color: #1867b0;
border-color: #1867b0;
.van-nav-bar__title {
color: #1f2937;
font-size: 16px;
}
.card-page .card-tip {
margin-top: 16px;
color: #969799;
.van-nav-bar .van-icon,
.van-nav-bar__text {
color: var(--color-primary);
}
.van-nav-bar__text {
font-size: 14px;
line-height: 22px;
}
.van-nav-bar__arrow {
margin-right: 4px;
font-size: 16px;
}
.card-page {
min-height: calc(100vh - 46px);
overflow: hidden;
padding: 18px 18px calc(28px + env(safe-area-inset-bottom));
box-sizing: border-box;
background:
radial-gradient(circle at 8% 17%, rgba(193, 218, 250, .25) 0, rgba(193, 218, 250, .25) 24px, transparent 25px),
radial-gradient(circle at 96% 72%, rgba(193, 218, 250, .22) 0, rgba(193, 218, 250, .22) 18px, transparent 19px),
radial-gradient(ellipse at 50% 25%, rgba(255, 255, 255, .92) 0, rgba(229, 241, 255, .62) 48%, transparent 72%),
linear-gradient(180deg, #f8fbff 0, #eef5fe 52%, #f8fbff 100%);
}
.card-guide {
position: relative;
z-index: 1;
display: table;
width: auto;
margin: -1.5vw auto 0;
padding: 2.5vw 6.1vw 2vw;
color: #5d6878;
font-size: 4.6vw;
line-height: 6.6vw;
text-align: center;
background: radial-gradient(ellipse at 50% 100%, rgba(255, 255, 255, .86) 0, rgba(255, 255, 255, .48) 42%, rgba(255, 255, 255, 0) 76%);
}
/*.card-guide::before {*/
/* position: absolute;*/
/* z-index: -1;*/
/* bottom: -16px;*/
/* left: 50%;*/
/* width: calc(100% + 56px);*/
/* height: 42px;*/
/* background: radial-gradient(ellipse at 50% 100%, rgba(255, 255, 255, .86) 0, rgba(255, 255, 255, .48) 42%, rgba(255, 255, 255, 0) 76%);*/
/* content: '';*/
/* transform: translateX(-50%);*/
/*}*/
.card-holder {
position: relative;
width: 96vw;
margin: 0 0 14px 50%;
overflow: hidden;
aspect-ratio: 852 / 1450;
transform: translateX(-50%);
}
.card-holder__preview {
position: absolute;
z-index: 1;
top: 25.9%;
right: 15%;
bottom: 7.6%;
left: 15%;
overflow: hidden;
border-radius: 14px;
background: #fff;
box-shadow: 0 9px 26px rgba(44, 94, 162, .15);
}
.card-holder__image {
position: absolute;
top: 50%;
left: 50%;
display: block;
max-width: none;
max-height: none;
transform: translate(-50%, -50%);
}
.card-holder__image--fill-height {
width: auto;
height: 100%;
}
.card-holder__image--fill-width {
width: 100%;
height: auto;
}
.card-holder__loading,
.card-holder__error {
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
flex-direction: column;
color: #9aa5b5;
font-size: 13px;
}
.card-holder__loading .van-loading {
margin-bottom: 10px;
}
.card-holder__error .van-icon {
margin-bottom: 8px;
color: #b7c2d1;
font-size: 32px;
}
.card-holder__overlay {
position: absolute;
z-index: 2;
top: -13.8%;
left: 0;
width: 100%;
height: 113.8%;
object-fit: contain;
pointer-events: none;
}
.card-save-button.van-button {
display: flex;
width: 82vw;
height: 12.6vw;
margin: 0 auto;
border: 0;
border-radius: 2.5vw;
background: #1989fa;
box-shadow: 0 2.3vw 6.2vw rgba(25, 137, 250, .2);
font-size: 4.1vw;
}
.card-save-button .van-icon {
margin-right: 2vw;
font-size: 5.4vw;
}
.card-save-button .van-button__content {
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
line-height: 1;
}
.card-save-button .van-button__text {
line-height: 20px;
}
.card-save-button.van-button--disabled {
opacity: .55;
}
.card-tip {
margin: 12px auto 0;
color: #8d98a8;
font-size: 13px;
line-height: 20px;
text-align: center;
}
@media (max-width: 360px) {
.card-page {
padding-right: 14px;
padding-left: 14px;
}
.card-holder {
width: 96vw;
}
}
</style>
<div id="app" v-cloak>
<van-nav-bar title="代表证" left-arrow placeholder fixed
<van-nav-bar title="代表证" left-text="返回" left-arrow placeholder fixed
@click-left="historyBack"></van-nav-bar>
<div class="card-page">
<van-button type="info" @click="showCard">教代会代表证</van-button>
<main class="card-page">
<p class="card-guide">请出示您的代表证</p>
<section class="card-holder">
<div ref="cardPreview" class="card-holder__preview">
<img v-if="cardUrl" ref="cardImage" class="card-holder__image"
:class="cardImageFillClass" :src="cardUrl"
alt="代表证" @load="onCardLoaded" @error="onCardLoadError">
<div v-if="cardLoading" class="card-holder__loading">
<van-loading type="spinner" color="#1989fa"></van-loading>
<span>代表证加载中...</span>
</div>
<div v-else-if="!cardReady" class="card-holder__error">
<van-icon name="photo-fail"></van-icon>
<span>{{ tipMsg || '暂无代表证' }}</span>
</div>
</div>
<img class="card-holder__overlay"
src="/assets/mobile/img/democratic/teachercongress/delegate-card-holder.png"
alt="代表证卡套">
</section>
<van-button class="card-save-button" type="info" icon="down" :loading="saving"
:disabled="!cardReady" @click="saveCard">保存到相册</van-button>
<div class="card-tip" v-if="tipMsg">{{ tipMsg }}</div>
</div>
</main>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
data: function () {
return {
cardUrl: "",
cardReady: false,
cardLoading: true,
cardImageFillClass: "card-holder__image--fill-height",
saving: false,
tipMsg: ""
}
},
methods: {
async showCard() {
async loadCard() {
try {
const response = await fetch("/platform/teacherCongress/delegate/card/isDb", {
method: "POST",
@@ -64,18 +250,66 @@ layout("/layouts/platform_h5.html"){
title: "温馨提示",
message: this.tipMsg
})
this.cardLoading = false
return
}
this.tipMsg = ""
vant.ImagePreview(["/platform/teacherCongress/delegate/card/showCard?t=" + Date.now()])
this.cardUrl = "/platform/teacherCongress/delegate/card/showCard?t=" + Date.now()
} catch (e) {
this.tipMsg = "代表证信息获取失败"
this.cardLoading = false
vant.Dialog.alert({
title: "温馨提示",
message: this.tipMsg
})
}
},
onCardLoaded(event) {
const image = event.target
const preview = this.$refs.cardPreview
const imageRatio = image.naturalWidth / image.naturalHeight
const previewRatio = preview.clientWidth / preview.clientHeight
this.cardImageFillClass = imageRatio >= previewRatio
? "card-holder__image--fill-height"
: "card-holder__image--fill-width"
this.cardReady = true
this.cardLoading = false
},
onCardLoadError() {
this.cardReady = false
this.cardLoading = false
if (!this.tipMsg) {
this.tipMsg = "代表证图片加载失败"
}
},
async saveCard() {
if (!this.cardReady || this.saving) return
this.saving = true
try {
const response = await fetch(this.cardUrl, {credentials: "same-origin"})
if (!response.ok) throw new Error("download failed")
const blobUrl = URL.createObjectURL(await response.blob())
const link = document.createElement("a")
link.href = blobUrl
link.download = "教代会代表证.png"
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.setTimeout(function () {
URL.revokeObjectURL(blobUrl)
}, 1000)
} catch (e) {
vant.Dialog.alert({
title: "温馨提示",
message: "代表证保存失败,请稍后重试"
})
} finally {
this.saving = false
}
}
},
mounted: function () {
this.loadCard()
}
})
</script>
@@ -569,10 +569,6 @@ const apps = {
// 导航到菜单
navigateToMenu(menu) {
if (this.isTeacherCongressCardMenu(menu)) {
this.showTeacherCongressCard()
return
}
// 关闭弹框
// this.showMenuPopup = false
if (menu.href) {
@@ -585,32 +581,6 @@ const apps = {
} else {
this.$toast('该功能暂不可用')
}
},
isTeacherCongressCardMenu(menu) {
const href = (menu && menu.href) || ""
return href.indexOf("/platform/teacherCongress/delegate/card/") === 0
},
showTeacherCongressCard() {
this.$axios.post("/platform/teacherCongress/delegate/card/isDb").then((resp) => {
if (resp.code !== 0) {
vant.Dialog.alert({
title: "温馨提示",
message: resp.msg || "暂无教代会代表证"
})
return
}
this.showMenuPopup = false
vant.ImagePreview([
"/platform/teacherCongress/delegate/card/showCard?t=" + Date.now()
])
}).catch(() => {
vant.Dialog.alert({
title: "温馨提示",
message: "代表证加载失败"
})
})
}
},
// 初始加载消除长按收藏
File diff suppressed because it is too large Load Diff
@@ -1,7 +1,7 @@
const todo = {
template: /*language=HTML*/ `
<div class="task-center">
<van-nav-bar class="task-nav" placeholder fixed :border="false" style="background-color: #0879f9;">
<van-nav-bar class="task-nav" placeholder fixed :border="false">
<template #title><span class="task-nav-title">待办中心</span></template>
</van-nav-bar>
<div class="task-hero" ref="taskHero">
@@ -314,7 +314,7 @@ const todo = {
min-height: 100vh;
padding-bottom: 72px;
box-sizing: border-box;
background: #f5f7fb;
background: #f3f7fd;
}
/deep/ .task-nav .van-nav-bar {
@@ -337,23 +337,23 @@ const todo = {
.task-hero {
position: relative;
height: 92px;
height: 112px;
background-color: #0879f9;
}
.task-statistics {
position: absolute;
top: 0;
top: 8px;
right: 12px;
left: 12px;
display: flex;
height: 78px;
padding: 10px 6px 8px;
box-sizing: border-box;
border-radius: 10px;
background: #fff;
box-shadow: 0 6px 18px rgba(45, 91, 145, 0.14);
height: 92px;
padding: 12px 6px 10px;
overflow: hidden;
box-sizing: border-box;
border-radius: 12px;
background: #fff;
box-shadow: 0 8px 22px rgba(28, 77, 137, 0.2);
}
.statistic-item {
@@ -375,13 +375,13 @@ const todo = {
.statistic-icon {
display: flex;
width: 24px;
height: 24px;
width: 28px;
height: 28px;
align-items: center;
justify-content: center;
border-radius: 50%;
color: #fff;
font-size: 15px;
font-size: 17px;
}
.statistic-icon-todo {
@@ -397,17 +397,17 @@ const todo = {
}
.statistic-count {
margin-top: 3px;
margin-top: 4px;
color: #26364d;
font-size: 14px;
font-size: 16px;
font-weight: 600;
line-height: 18px;
line-height: 20px;
}
.statistic-label {
color: #8d97a7;
font-size: 12px;
line-height: 15px;
line-height: 16px;
}
/* 标签页样式优化 */
@@ -438,7 +438,7 @@ const todo = {
}
/deep/ .task-tabs .van-tab--active {
color: var(--color-primary, #1989fa);
color: #1989fa;
font-size: 16px;
font-weight: 600;
}
@@ -448,7 +448,7 @@ const todo = {
width: 24px;
height: 4px;
border-radius: 4px;
background-color: var(--color-primary, #1989fa);
background-color: #1989fa;
}
/deep/ .task-tabs--hero-replacement {
@@ -468,19 +468,23 @@ const todo = {
display: flex;
padding: 10px 12px;
align-items: center;
background: #f5f7fb;
background: #f3f7fd;
}
.search-form .van-search{
width: 100%;
padding: 0;
border-radius: 18px;
border-radius: 12px;
overflow: hidden;
background: #fff;
box-shadow: 0 4px 14px rgba(55, 85, 126, 0.07);
}
/deep/ .search-form .van-search__content {
border-radius: 18px;
height: 42px;
padding-left: 13px;
align-items: center;
border-radius: 12px;
background: #fff;
}
@@ -489,7 +493,7 @@ const todo = {
}
/deep/ .van-sticky--fixed {
background: #f5f7fb;
background: #f3f7fd;
}
.search-input {
@@ -561,24 +565,30 @@ const todo = {
.task-skeleton-card {
margin-bottom: 10px;
padding: 18px 4px;
border-radius: 10px;
min-height: 196px;
padding: 20px 16px;
box-sizing: border-box;
border-radius: 12px;
background: #fff;
box-shadow: 0 2px 10px rgba(31, 68, 113, .07);
box-shadow: 0 7px 20px rgba(55, 85, 126, 0.06);
}
.task-skeleton-card .van-skeleton {
padding: 0;
}
/deep/ .table-list-container {
min-height: calc(100vh - 140px);
margin-top: 8px !important;
min-height: 0;
margin-top: 10px !important;
padding: 0 12px !important;
}
/deep/ .table-list-container .table-list-item {
margin-bottom: 10px !important;
padding: 12px !important;
border: 0 !important;
border-radius: 9px !important;
box-shadow: 0 2px 10px rgba(31, 68, 113, 0.07) !important;
margin-bottom: 12px !important;
padding: 16px !important;
border: 1px solid rgba(71, 111, 164, 0.05) !important;
border-radius: 12px !important;
box-shadow: 0 7px 20px rgba(55, 85, 126, 0.08) !important;
}
/deep/ .todo-item-header {
@@ -586,7 +596,7 @@ const todo = {
min-width: 0;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
margin-bottom: 12px;
}
/deep/ .todo-item-title {
@@ -598,22 +608,22 @@ const todo = {
/deep/ .todo-title-icon {
display: flex;
flex: 0 0 24px;
width: 24px;
height: 24px;
margin-right: 8px;
flex: 0 0 38px;
width: 38px;
height: 38px;
margin-right: 10px;
align-items: center;
justify-content: center;
border-radius: 6px;
border-radius: 10px;
color: #438df5;
font-size: 15px;
font-size: 21px;
background: #edf5ff;
}
/deep/ .todo-title-text {
overflow: hidden;
color: #26364d;
font-size: 14px;
font-size: 15px;
font-weight: 600;
line-height: 22px;
text-overflow: ellipsis;
@@ -632,7 +642,19 @@ const todo = {
}
/deep/ .table-column {
padding: 3px 0 !important;
display: grid !important;
width: 100% !important;
padding: 4px 0 !important;
align-items: center;
grid-template-columns: 72px minmax(0, 1fr);
column-gap: 8px;
}
/deep/ .table-list-container .table-list-item .table-list-columns {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
row-gap: 2px;
}
/deep/ .table-column .label,
@@ -642,16 +664,36 @@ const todo = {
line-height: 19px !important;
}
/deep/ .table-column .label {
width: 72px;
max-width: none;
color: #98a3b3 !important;
}
/deep/ .table-column .value {
overflow: hidden;
color: #56657a !important;
text-overflow: ellipsis;
white-space: nowrap;
}
/deep/ .table-list-container .table-list-item .item-actions {
margin-top: 8px !important;
padding-top: 7px !important;
margin-top: 12px !important;
padding-top: 12px !important;
border-top-color: #edf1f6 !important;
}
/deep/ .table-list-container .table-list-item .action-btn {
padding: 0 !important;
min-width: 92px;
height: 32px;
padding: 0 14px !important;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
border-radius: 16px;
color: #2783ef !important;
background: transparent !important;
background: #edf5ff !important;
font-size: 13px !important;
}