feat: 会议选座、代表阅览

This commit is contained in:
2026-08-30 15:49:07 +08:00
parent ec54e3fc0d
commit b0182731bd
5 changed files with 2098 additions and 230 deletions
@@ -3,6 +3,7 @@ package com.budwk.app.zhgh.democratic.teachercongress.guildhall.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
@@ -126,4 +127,38 @@ public class GuildHallMeetingController {
}
return Result.success(results);
}
@At
@SaCheckPermission("h5.guildHall.metting")
@ApiOperation("H5进行中会议分页")
public Result selectPage(PageForm pageForm) {
int pageNumber = pageForm.getPageNumber() == null ? 1 : pageForm.getPageNumber();
int pageSize = 10;
Cnd cnd = Cnd.where(GuildHallMeeting::getStartTime, "<=", DateUtil.date())
.and(GuildHallMeeting::getEndTime, ">=", DateUtil.date());
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
cnd.and(GuildHallMeeting::getName, "like", "%" + pageForm.getSearchKeyword().trim() + "%");
}
cnd.desc(GuildHallMeeting::getStartTime);
List<NutMap> availableMeetings = new ArrayList<>();
List<GuildHallMeeting> meetings = dao.query(GuildHallMeeting.class, cnd);
for (GuildHallMeeting meeting : meetings) {
if (meeting.getGroupId() != null
&& !activityBasicScopeService.isUserInGroup(meeting.getGroupId(), SecurityUtil.getUserId())) {
continue;
}
NutMap meetingMap = Lang.obj2nutmap(meeting);
GuildHall hall = dao.fetch(GuildHall.class, meeting.getHallId());
meetingMap.put("address", hall == null ? null : hall.getAddress());
availableMeetings.add(meetingMap);
}
int totalCount = availableMeetings.size();
int fromIndex = Math.min((pageNumber - 1) * pageSize, totalCount);
int toIndex = Math.min(fromIndex + pageSize, totalCount);
Pagination<NutMap> pagination = new Pagination<>(pageNumber, pageSize, totalCount,
availableMeetings.subList(fromIndex, toIndex));
return Result.success(pagination);
}
}
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.democratic.teachercongress.guildhall.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import com.budwk.app.base.result.Result;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.basic.service.ActivityBasicScopeService;
@@ -49,14 +50,14 @@ public class GuildHallSelectSeatController {
}
@At("/h5")
@SaCheckPermission("h5.guildHall.selectSeat")
@SaCheckPermission(value = {"h5.guildHall.metting", "h5.guildHall.selectSeat"}, mode = SaMode.OR)
@Ok("beetl:/platform/zhghh5/democratic/teachercongress/guildhall/selectSeat/index.html")
public void h5Index() {
}
@At
@SaCheckPermission("guildHall.selectSeat")
public Result loadSeats(@Param("meetingId") String meetingId) {
@At
@SaCheckPermission(value = {"guildHall.selectSeat", "h5.guildHall.metting"}, mode = SaMode.OR)
public Result loadSeats(@Param("meetingId") String meetingId) {
Result permissionResult = checkMeetingPermission(meetingId);
if (permissionResult != null) {
return permissionResult;
@@ -87,10 +88,10 @@ public class GuildHallSelectSeatController {
return Result.success(selectedSeats);
}
@At
@SaCheckPermission("guildHall.selectSeat")
@At
@SaCheckPermission(value = {"guildHall.selectSeat", "h5.guildHall.metting"}, mode = SaMode.OR)
@Aop(TransAop.READ_COMMITTED)
public Result doSelect(@Param("meetingId") String meetingId, Integer rowNumber, Integer colNumber) {
public Result doSelect(@Param("meetingId") String meetingId, Integer rowNumber, Integer colNumber) {
Result permissionResult = checkMeetingPermission(meetingId);
if (permissionResult != null) {
return permissionResult;
@@ -2,63 +2,465 @@
layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak style="padding-bottom: 70px;">
<van-nav-bar title="代表阅览" left-text="返回" left-arrow @click-left="historyBack" placeholder fixed></van-nav-bar>
<van-sticky offset-top="46px">
<van-search
v-model="pageForm.searchKeyword"
:show-action="false"
:reverse-color="false"
input-align="left"
placeholder="请输入姓名/工号搜索"
@search="doSearch"
></van-search>
<van-dropdown-menu :close-on-click-outside="false" :close-on-click-overlay="false">
<van-dropdown-item v-model="pageForm.sessionId" :options="sessionOptions"
@change="sessionChange"></van-dropdown-item>
<van-dropdown-item
v-if="pageForm.sessionId"
v-model="pageForm.delegationId"
:options="delegationOptions"
@change="doSearch"
></van-dropdown-item>
<style>
body {
background-color: #f3f7fd;
}
.delegate-read-page {
min-height: 100vh;
padding-bottom: calc(86px + env(safe-area-inset-bottom));
box-sizing: border-box;
background-color: #f3f7fd;
}
.delegate-read-page .van-nav-bar {
background-color: #fff;
}
.delegate-read-page .van-nav-bar__title {
color: #1f2937;
font-size: 16px;
font-weight: 600;
}
.delegate-read-page .van-nav-bar .van-icon,
.delegate-read-page .van-nav-bar__text {
color: #397fbd;
}
.delegate-read-content {
padding: 12px 12px 0;
}
.delegate-read-hero {
position: relative;
height: 134px;
overflow: hidden;
border-radius: 10px;
color: #fff;
background-color: #247fe8;
box-shadow: 0 8px 20px rgba(51, 117, 219, .18);
}
.delegate-read-hero__image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
}
.delegate-read-hero__copy {
position: relative;
z-index: 1;
display: flex;
width: 68%;
height: 100%;
padding-left: 16px;
flex-direction: column;
justify-content: center;
box-sizing: border-box;
background: linear-gradient(90deg, rgba(22, 112, 232, .98) 0%, rgba(22, 112, 232, .88) 52%, rgba(22, 112, 232, 0) 100%);
}
.delegate-read-hero__title {
font-size: 25px;
line-height: 34px;
font-weight: 700;
letter-spacing: 1px;
text-shadow: 0 2px 6px rgba(17, 86, 182, .18);
}
.delegate-read-hero__subtitle {
margin-top: 4px;
font-size: 13px;
line-height: 20px;
letter-spacing: .5px;
opacity: .95;
}
.delegate-read-sticky {
padding: 10px 0 1px;
background-color: #f3f7fd;
}
.van-sticky--fixed .delegate-read-sticky {
padding-right: 12px;
padding-left: 12px;
box-sizing: border-box;
}
.delegate-read-search {
margin: 0 0 10px;
padding: 0;
overflow: hidden;
border-radius: 9px;
background-color: #fff;
box-shadow: 0 3px 12px rgba(43, 73, 112, .06);
}
.delegate-read-search .van-search__content {
height: 40px;
padding-left: 12px;
align-items: center;
border-radius: 9px;
background-color: #fff;
}
.delegate-read-search .van-field__control {
color: #263548;
font-size: 14px;
}
.delegate-read-search .van-field__control::placeholder {
color: #a7b0bf;
}
.delegate-read-filter {
margin: 0 0 10px;
overflow: hidden;
border-radius: 10px;
box-shadow: 0 5px 16px rgba(43, 73, 112, .08);
transition: border-radius .18s ease;
}
.delegate-read-filter.is-open {
border-radius: 10px 10px 0 0;
}
.delegate-read-filter .van-dropdown-menu__bar {
height: 44px;
box-shadow: none;
}
.delegate-read-filter .van-dropdown-menu__item {
min-width: 0;
flex: 1 1 0;
}
.delegate-read-filter .van-dropdown-menu__title {
width: auto;
max-width: calc(100% - 24px);
padding: 0 12px 0 0;
color: #66758a;
font-size: 14px;
}
.delegate-read-filter .van-dropdown-menu__title::after {
right: 0;
}
.delegate-read-filter .van-dropdown-item__content {
width: calc(100% - 24px);
max-height: 80%;
margin: 0 12px;
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
box-sizing: border-box;
border-radius: 0 0 12px 12px;
box-shadow: 0 10px 24px rgba(31, 65, 108, .14);
}
.delegate-read-list .empty-state {
padding: 0;
}
.delegate-read-list .table-list.is-empty {
height: calc(100vh - 400px - env(safe-area-inset-bottom));
min-height: 300px;
box-sizing: border-box;
}
.delegate-read-list .table-list-container {
margin-top: 0;
}
.delegate-read-list .table-list-container .table-list-item {
margin-bottom: 10px;
padding: 12px;
border: 0;
border-radius: 9px;
box-shadow: 0 3px 12px rgba(43, 73, 112, .06);
}
.delegate-read-list .table-list-container .table-list-item .table-list-content,
.delegate-read-list .table-list-container .table-list-item .table-list-columns {
display: block;
width: 100%;
}
.delegate-read-list .table-list-container .table-list-item .item-header,
.delegate-read-list .table-list-container .table-list-item .item-actions {
display: none;
}
.delegate-card {
display: grid;
grid-template-columns: 58px minmax(0, 1fr);
gap: 12px;
width: 100%;
min-height: 76px;
align-items: center;
box-sizing: border-box;
}
.delegate-card__avatar {
display: flex;
width: 58px;
height: 58px;
align-items: center;
justify-content: center;
flex-shrink: 0;
border-radius: 50%;
color: #6ca8f2;
background-color: #eaf3ff;
}
.delegate-card__avatar.is-female {
color: #ee9cab;
background-color: #fff0f3;
}
.delegate-card__avatar .van-icon {
font-size: 34px;
}
.delegate-card__body {
min-width: 0;
}
.delegate-card__name {
margin-bottom: 4px;
overflow: hidden;
color: #1e2c43;
font-size: 16px;
line-height: 22px;
font-weight: 600;
text-overflow: ellipsis;
white-space: nowrap;
}
.delegate-card__row {
display: grid;
grid-template-columns: 76px minmax(0, 1fr);
min-height: 20px;
align-items: center;
color: #52637c;
font-size: 12px;
line-height: 18px;
}
.delegate-card__label {
display: flex;
min-width: 0;
align-items: center;
color: #91a0b3;
white-space: nowrap;
}
.delegate-card__label i {
width: 15px;
margin-right: 5px;
color: #8da2bd;
font-size: 11px;
text-align: center;
}
.delegate-card__value {
min-width: 0;
overflow: hidden;
color: #435570;
text-overflow: ellipsis;
white-space: nowrap;
}
.delegate-read-empty {
display: flex;
min-height: 260px;
padding: 26px 18px 32px;
align-items: center;
justify-content: center;
flex-direction: column;
box-sizing: border-box;
text-align: center;
}
.delegate-read-empty img {
display: block;
width: 82%;
max-width: 280px;
height: auto;
object-fit: contain;
}
.delegate-read-empty__title {
margin-top: 14px;
color: #50627a;
font-size: 15px;
line-height: 22px;
font-weight: 600;
}
.delegate-read-empty__hint {
margin-top: 5px;
color: #9aa7b8;
font-size: 12px;
line-height: 18px;
}
.delegate-read-actions {
position: fixed;
right: 0;
bottom: 0;
left: 0;
z-index: 1000;
display: flex;
gap: 10px;
padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
box-sizing: border-box;
background: #fff;
border-top: 1px solid #edf1f6;
box-shadow: 0 -5px 16px rgba(43, 73, 112, .08);
}
.delegate-read-actions .van-button {
flex: 1;
height: 44px;
border-radius: 9px;
font-size: 14px;
font-weight: 500;
}
.delegate-read-actions .delegate-read-action.is-excel {
color: #217346;
border-color: #8fc6a8;
background-color: #eef8f2;
box-shadow: inset 0 0 0 1px rgba(33, 115, 70, .06);
}
.delegate-read-actions .delegate-read-action.is-word {
color: #2b579a;
border-color: #91afd4;
background-color: #eef4fb;
box-shadow: inset 0 0 0 1px rgba(43, 87, 154, .06);
}
.delegate-read-actions .is-excel i {
color: #217346;
}
.delegate-read-actions .is-word i {
color: #2b579a;
}
.delegate-read-actions i {
margin-right: 7px;
font-size: 17px;
vertical-align: -1px;
}
</style>
<div id="app" v-cloak>
<div class="delegate-read-page">
<van-nav-bar title="代表阅览" left-text="返回" left-arrow @click-left="historyBack" placeholder fixed></van-nav-bar>
<div class="delegate-read-content">
<section class="delegate-read-hero">
<img class="delegate-read-hero__image" src="/assets/mobile/img/democratic/teachercongress/delegate-read-hero.png" alt="代表阅览插画">
<div class="delegate-read-hero__copy">
<div class="delegate-read-hero__title">代表阅览</div>
<div class="delegate-read-hero__subtitle">查阅教代会代表信息</div>
</div>
</section>
<van-sticky :offset-top="46">
<div class="delegate-read-sticky">
<van-search
v-model="pageForm.searchKeyword"
class="delegate-read-search"
:show-action="false"
:reverse-color="false"
input-align="left"
placeholder="请输入姓名/工号搜索"
shape="round"
clearable
@search="doSearch"
@clear="doSearch"
></van-search>
<van-dropdown-menu class="delegate-read-filter" :class="{'is-open': filterOpened}" active-color="#1989fa">
<van-dropdown-item v-model="pageForm.sessionId" :options="sessionOptions"
@change="sessionChange" @open="setFilterOpened(true)" @close="setFilterOpened(false)"></van-dropdown-item>
<van-dropdown-item
v-if="pageForm.sessionId"
v-model="pageForm.delegationId"
:options="delegationOptions"
@change="doSearch"
@open="setFilterOpened(true)"
@close="setFilterOpened(false)"
></van-dropdown-item>
<!-- <van-dropdown-item ref="moreDropDownItemOption" title="更多查询" multiple :options="moreOptions"-->
<!-- @change="moreOptionChange">-->
<!-- <van-button type="primary" class="margin-top20 width100" @click="$refs.moreDropDownItemOption.toggle()">-->
<!-- 查询-->
<!-- </van-button>-->
<!-- </van-dropdown-item>-->
</van-dropdown-menu>
</van-sticky>
</van-dropdown-menu>
</div>
</van-sticky>
<div class="export-buttons" style="position: fixed; bottom: 0; left: 0; right: 0; padding: 10px; display: flex; gap: 10px; background: white; border-top: 1px solid #eee; z-index: 1000; box-shadow: 0 -2px 8px rgba(0,0,0,0.1);">
<van-button type="primary" size="medium" @click="exportXlsx" style="flex: 1;">
<i class="fa fa-file-excel-o"></i> 导出Excel
</van-button>
<van-button type="info" size="medium" @click="exportDocx" style="flex: 1;">
<i class="fa fa-file-word-o"></i> 导出Word
</van-button>
</div>
<table-list api="/platform/teacherCongress/delegate/read/pageData" :page_form.sync="pageForm" json
@ready="onReady"
ref="tableListRef"
style="padding-bottom: 65px">
<template v-slot="{index,row}">
<table-column label="代表姓名">{{row.userName}}</table-column>
<table-column label="代表工号">{{row.loginName}}</table-column>
<table-column label="代表团">{{delegationOptions.find(v=>v.id===row.delegationId)?.name}}</table-column>
<table-column label="代表身份">
{{moreOptions.find(v=>v.title==='代表身份')?.options.find(v=>v.id===row.roleId)?.name}}
</table-column>
</template>
<template #actions="{index,row}">
<div class="action-btn">
<i class="fa fa-eye"></i>
<span>查看</span>
<div class="delegate-read-list">
<table-list api="/platform/teacherCongress/delegate/read/pageData" :page_form.sync="pageForm"
:class="{'is-empty': pageForm.totalCount === 0}" json
@ready="onReady"
ref="tableListRef"
title="userName">
<template #empty>
<div class="delegate-read-empty">
<img src="/assets/mobile/img/democratic/teachercongress/delegate-read-empty.png" alt="暂无代表信息插画">
<div class="delegate-read-empty__title">暂无代表信息</div>
<div class="delegate-read-empty__hint">当前筛选条件下暂时没有可阅览的代表</div>
</div>
</template>
<template #header></template>
<template v-slot="{index,row}">
<div class="delegate-card">
<div class="delegate-card__avatar" :class="{'is-female': row.sex === '女'}">
<van-icon name="manager"></van-icon>
</div>
<div class="delegate-card__body">
<div class="delegate-card__name">{{row.userName || '--'}}</div>
<div class="delegate-card__row">
<div class="delegate-card__label"><i class="fa fa-id-card-o"></i>代表工号</div>
<div class="delegate-card__value">{{row.loginName || '--'}}</div>
</div>
<div class="delegate-card__row">
<div class="delegate-card__label"><i class="fa fa-sitemap"></i>代表团</div>
<div class="delegate-card__value">{{delegationOptions.find(v=>v.id===row.delegationId)?.name || '--'}}</div>
</div>
<div class="delegate-card__row">
<div class="delegate-card__label"><i class="fa fa-user-o"></i>代表身份</div>
<div class="delegate-card__value">{{moreOptions.find(v=>v.title==='代表身份')?.options.find(v=>v.id===row.roleId)?.name || '--'}}</div>
</div>
</div>
</div>
</template>
</table-list>
</div>
</template>
</table-list>
</div>
<div class="delegate-read-actions">
<van-button class="delegate-read-action is-excel" size="medium" @click="exportXlsx">
<i class="fa fa-file-excel-o"></i> 导出Excel
</van-button>
<van-button class="delegate-read-action is-word" type="primary" size="medium" @click="exportDocx">
<i class="fa fa-file-word-o"></i> 导出Word
</van-button>
</div>
</div>
</div>
<script nonce="${cspNonce!}">
@@ -81,11 +483,15 @@ layout("/layouts/platform_h5.html"){
delegationOptions: [],
roleOptions: [],
moreOptions: []
moreOptions: [],
filterOpened: false
}
},
methods: {
historyBack,
setFilterOpened(value) {
this.$set(this, "filterOpened", value)
},
moreOptionChange(options) {
this.moreOptions = options
@@ -0,0 +1,612 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<style>
html,
body,
#app {
height: 100%;
min-height: 100%;
background: #f3f7fd;
}
[v-cloak] {
display: none;
}
#app .van-nav-bar {
background: #fff;
}
#app .van-nav-bar__title {
color: #17233d;
font-size: 17px;
font-weight: 600;
}
#app .van-nav-bar__text,
#app .van-nav-bar .van-icon {
color: #1677ff;
}
.meeting-list-page {
display: flex;
height: calc(100vh - 46px);
min-height: calc(100vh - 46px);
padding: 12px 12px 18px;
flex-direction: column;
background: #f3f7fd;
box-sizing: border-box;
overflow: hidden;
}
.meeting-list-hero {
position: relative;
height: 132px;
margin: 0;
overflow: hidden;
border-radius: 8px;
background: #1677e9;
box-shadow: 0 4px 14px rgba(24, 104, 196, .14);
}
.meeting-list-hero__image {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.meeting-list-hero__copy {
position: absolute;
top: 0;
bottom: 0;
left: 0;
display: flex;
z-index: 1;
width: 68%;
padding-left: 18px;
flex-direction: column;
justify-content: center;
color: #fff;
box-sizing: border-box;
background: linear-gradient(90deg, rgba(22, 112, 232, .98) 0%, rgba(22, 112, 232, .86) 52%, rgba(22, 112, 232, 0) 100%);
}
.meeting-list-hero__title {
font-size: 25px;
font-weight: 700;
line-height: 34px;
}
.meeting-list-hero__subtitle {
margin-top: 5px;
font-size: 12px;
line-height: 18px;
opacity: .92;
}
.meeting-list-toolbar {
padding: 10px 0 1px;
background: #f3f7fd;
}
.van-sticky--fixed .meeting-list-toolbar {
padding-right: 12px;
padding-left: 12px;
box-sizing: border-box;
}
.meeting-list-search-row {
display: block;
}
.meeting-list-search {
flex: 1;
min-width: 0;
margin: 0 0 10px;
padding: 0;
overflow: hidden;
background: #fff;
border-radius: 8px;
box-shadow: 0 3px 12px rgba(43, 73, 112, .06);
}
.meeting-list-search .van-search__content {
height: 40px;
padding-left: 12px;
align-items: center;
background: #fff;
border-radius: 8px;
}
.meeting-list-search .van-field__control {
color: #263548;
font-size: 14px;
}
.meeting-list-search .van-field__control::placeholder {
color: #a7b0bf;
}
.meeting-mine-button {
display: inline-flex;
flex: none;
align-items: center;
justify-content: center;
gap: 4px;
height: 38px;
padding: 0 10px;
color: #37648e;
font-size: 12px;
background: #fff;
border: 1px solid #dce8f5;
border-radius: 6px;
}
.meeting-mine-button.is-active {
color: #1677ff;
background: #edf5ff;
border-color: #8fc2ff;
}
.meeting-status-tabs {
display: grid;
grid-template-columns: repeat(4, 1fr);
margin-top: 10px;
}
.meeting-status-tab {
position: relative;
height: 40px;
padding: 0;
color: #667085;
font-size: 13px;
background: transparent;
border: 0;
}
.meeting-status-tab.is-active {
color: #1677ff;
font-weight: 600;
}
.meeting-status-tab.is-active::after {
position: absolute;
bottom: 0;
left: 50%;
width: 24px;
height: 3px;
background: #1677ff;
border-radius: 2px;
transform: translateX(-50%);
content: '';
}
.meeting-list-content {
flex: 1;
min-height: 0;
padding: 0;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.meeting-list-content .van-pull-refresh,
.meeting-list-content .van-pull-refresh__track,
.meeting-list-content .van-list {
min-height: 100%;
}
.meeting-list-card {
position: relative;
display: flex;
min-height: 214px;
margin: 0 4px 12px;
padding: 18px 16px;
flex-direction: column;
overflow: hidden;
background: #fff;
border: 0;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, .04);
box-sizing: border-box;
transition: transform .15s ease;
}
.meeting-list-card.is-current {
border-color: #4096ff;
box-shadow: 0 3px 10px rgba(22, 119, 255, .12);
}
.meeting-list-card:active {
transform: scale(.99);
box-shadow: 0 4px 16px rgba(25, 137, 250, .08);
}
.meeting-card-status {
position: absolute;
top: 0;
left: 0;
padding: 3px 9px;
color: #fff;
font-size: 10px;
line-height: 17px;
background: #aab2bd;
border-radius: 6px 0 6px 0;
}
.meeting-card-status.is-current {
background: #27ae7a;
}
.meeting-card-status.is-upcoming {
background: #1677ff;
}
.meeting-card-status.is-ended {
background: #a8adb5;
}
.meeting-card-main {
flex: 1;
min-width: 0;
}
.meeting-card-title {
margin-bottom: 12px;
overflow: hidden;
color: #17233d;
font-size: 16px;
font-weight: 600;
line-height: 24px;
text-overflow: ellipsis;
white-space: nowrap;
}
.meeting-card-location {
display: flex;
flex: 1;
min-width: 0;
align-items: center;
gap: 9px;
overflow: visible;
color: #17233d;
}
.meeting-card-location .van-icon {
flex: none;
display: inline-flex;
width: 30px;
height: 30px;
align-items: center;
justify-content: center;
color: #1677ff;
font-size: 16px;
background: #e8f3ff;
border-radius: 50%;
}
.meeting-card-location__value {
overflow: visible;
color: #17233d;
font-size: 13px;
font-weight: 500;
line-height: 19px;
white-space: normal;
word-break: break-all;
}
.meeting-card-time {
display: flex;
min-height: 86px;
padding: 12px 12px 10px;
align-items: center;
justify-content: space-between;
background: #f6faff;
border-radius: 8px;
box-sizing: border-box;
}
.meeting-card-time__item {
flex: 0 0 92px;
min-width: 0;
}
.meeting-card-time__item:last-child {
text-align: right;
}
.meeting-card-date {
color: #73849b;
font-size: 12px;
line-height: 18px;
}
.meeting-card-hour {
margin-top: 4px;
color: #17233d;
font-size: 25px;
font-weight: 600;
line-height: 32px;
}
.meeting-card-time__label {
margin-top: 1px;
color: #9aa7b8;
font-size: 11px;
line-height: 16px;
}
.meeting-card-time__arrow {
position: relative;
flex: 1;
height: 1px;
margin: 0 12px;
background: #cfdae8;
}
.meeting-card-time__arrow::after {
position: absolute;
top: 50%;
right: -1px;
width: 0;
height: 0;
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
border-left: 18px solid #aab8c8;
transform: translateY(-50%);
content: '';
}
.meeting-card-bottom {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 13px;
padding: 12px;
background: #f4f9ff;
border: 1px solid #d9eaff;
border-radius: 8px;
box-sizing: border-box;
}
.meeting-card-available {
margin-top: 9px;
color: #1677ff;
font-size: 12px;
font-weight: 500;
}
.meeting-card-side {
display: flex;
flex: none;
justify-content: flex-end;
margin-left: 10px;
}
.meeting-card-status.is-ended ~ .meeting-card-main .meeting-card-available {
color: #8c939e;
}
.meeting-card-action {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 5px;
height: 32px;
min-width: 78px;
padding: 0 10px;
color: #fff;
font-size: 12px;
font-weight: 600;
line-height: 32px;
white-space: nowrap;
background: linear-gradient(135deg, #1989fa 0%, #0d6efd 100%);
border: 0;
border-radius: 18px;
box-shadow: 0 4px 12px rgba(25, 137, 250, .22);
}
.meeting-card-action .van-icon,
.meeting-card-action i {
font-size: 13px;
}
.mobile-loading {
display: flex;
justify-content: center;
padding: 80px 0;
color: #8a96a8;
}
.meeting-list-empty {
display: flex;
min-height: 310px;
padding: 28px 18px 36px;
align-items: center;
justify-content: center;
flex-direction: column;
box-sizing: border-box;
text-align: center;
}
.meeting-list-empty img {
display: block;
width: 82%;
max-width: 270px;
height: auto;
object-fit: contain;
}
.meeting-list-empty__title {
margin-top: 12px;
color: #50627a;
font-size: 15px;
line-height: 22px;
font-weight: 600;
}
.meeting-list-empty__hint {
margin-top: 5px;
color: #9aa7b8;
font-size: 12px;
line-height: 18px;
}
</style>
<div id="app" v-cloak>
<van-nav-bar title="会议列表" left-text="返回" left-arrow
@click-left="historyBack" placeholder fixed></van-nav-bar>
<div class="meeting-list-page">
<section class="meeting-list-hero">
<img class="meeting-list-hero__image"
src="/assets/mobile/img/democratic/teachercongress/meeting-list-hero.png" alt="会议选座插画">
<div class="meeting-list-hero__copy">
<div class="meeting-list-hero__title">会议选座</div>
<div class="meeting-list-hero__subtitle">查看会议安排并选择座位</div>
</div>
</section>
<van-sticky :offset-top="46">
<div class="meeting-list-toolbar">
<div class="meeting-list-search-row">
<van-search v-model="meetingKeyword" class="meeting-list-search" placeholder="搜索会议名称"
:show-action="false" input-align="left" shape="round" clearable
@search="doSearch" @clear="doSearch"></van-search>
</div>
</div>
</van-sticky>
<div class="meeting-list-content">
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
<van-list v-model="listLoading" :finished="finished" :immediate-check="false"
:finished-text="meetings.length ? '没有更多会议了' : ''" @load="onLoad">
<div v-for="item in meetings" :key="item.id" class="meeting-list-card"
@click="enterMeeting(item)">
<div class="meeting-card-main">
<div class="meeting-card-title">{{ item.name }}</div>
<div class="meeting-card-time">
<div class="meeting-card-time__item">
<div class="meeting-card-date">{{ formatMeetingDate(item.startTime) }}</div>
<div class="meeting-card-hour">{{ formatMeetingHour(item.startTime) }}</div>
<div class="meeting-card-time__label">会议开始</div>
</div>
<div class="meeting-card-time__arrow"></div>
<div class="meeting-card-time__item">
<div class="meeting-card-date">{{ formatMeetingDate(item.endTime) }}</div>
<div class="meeting-card-hour">{{ formatMeetingHour(item.endTime) }}</div>
<div class="meeting-card-time__label">会议结束</div>
</div>
</div>
<div class="meeting-card-bottom">
<div class="meeting-card-location">
<van-icon name="location"></van-icon>
<div class="meeting-card-location__text">
<div class="meeting-card-location__value">{{ item.address || '会议地点待定' }}</div>
</div>
</div>
<div class="meeting-card-side">
<button type="button" class="meeting-card-action">
<i class="fa fa-sign-in"></i>去选座
</button>
</div>
</div>
</div>
</div>
</van-list>
<div v-if="!listLoading && meetings.length === 0" class="meeting-list-empty">
<img src="/assets/mobile/img/democratic/teachercongress/delegate-read-empty.png" alt="暂无会议插画">
<div class="meeting-list-empty__title">暂无进行中的会议</div>
<div class="meeting-list-empty__hint">当前没有可选座的会议安排</div>
</div>
</van-pull-refresh>
</div>
</div>
</div>
<script nonce="${cspNonce!}">
const vue = new Vue({
el: '#app',
data() {
return {
meetings: [],
meetingKeyword: '',
listLoading: false,
refreshing: false,
finished: false,
pageNumber: 1,
pageSize: 10,
totalCount: 0
}
},
mounted() {
this.loadMeetings(true)
},
methods: {
historyBack,
loadMeetings(reset) {
const loading = createListLoading()
if (reset) {
this.pageNumber = 1
this.totalCount = 0
this.meetings = []
this.finished = false
}
this.listLoading = true
$.post('/platform/guildHall/meeting/selectPage', {
pageNumber: this.pageNumber,
pageSize: this.pageSize,
searchKeyword: String(this.meetingKeyword || '').trim()
}).then((res) => {
if (res.code === 0) {
const pageData = res.data || {}
const list = Array.isArray(pageData.list) ? pageData.list : []
this.meetings = reset ? list : this.meetings.concat(list)
this.totalCount = pageData.totalCount || 0
this.finished = this.meetings.length >= this.totalCount
if (!this.finished) {
this.pageNumber++
}
} else {
this.$toast.fail(res.msg || '会议加载失败')
}
}).always(() => {
this.listLoading = false
this.refreshing = false
loading.clear()
})
},
onLoad() {
if (!this.finished) {
this.loadMeetings(false)
}
},
onRefresh() {
this.refreshing = true
this.loadMeetings(true)
},
doSearch() {
this.loadMeetings(true)
},
formatMeetingRange(item) {
const start = item.startTime ? this.$moment(item.startTime).format('YYYY-MM-DD HH:mm') : '--'
const end = item.endTime ? this.$moment(item.endTime).format('YYYY-MM-DD HH:mm') : '--'
return start + ' 至 ' + end
},
formatMeetingDate(time) {
return time ? this.$moment(time).format('YYYY年MM月DD日') : '--'
},
formatMeetingHour(time) {
return time ? this.$moment(time).format('HH:mm') : '--'
},
enterMeeting(item) {
this.$pjaxReplace('/platform/guildHall/selectSeat/h5?meetingId=' + encodeURIComponent(item.id))
}
}
})
</script>
<!--#
}
#-->