first commit
This commit is contained in:
+172
@@ -0,0 +1,172 @@
|
||||
<!--#
|
||||
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>
|
||||
<!-- <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>
|
||||
|
||||
<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>
|
||||
</template>
|
||||
</table-list>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
const vue = new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
data() {
|
||||
return {
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
searchKeyword: null,
|
||||
sessionId: null,
|
||||
delegationId: null,
|
||||
roleId: null
|
||||
},
|
||||
|
||||
sessionOptions: [],
|
||||
delegationOptions: [],
|
||||
roleOptions: [],
|
||||
|
||||
moreOptions: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
historyBack,
|
||||
|
||||
moreOptionChange(options) {
|
||||
this.moreOptions = options
|
||||
this.moreOptions.forEach((item) => {
|
||||
const checkedItems = item.options.filter((obj) => obj.checked)
|
||||
if (item.title === "代表身份") {
|
||||
this.$set(
|
||||
this.pageForm,
|
||||
"roleIds",
|
||||
checkedItems.map((v) => v.id)
|
||||
)
|
||||
}
|
||||
})
|
||||
this.doSearch()
|
||||
},
|
||||
sessionChange() {
|
||||
this.pageForm.delegationId = null
|
||||
if (this.pageForm.sessionId) {
|
||||
this.listDelegation()
|
||||
} else {
|
||||
this.delegationOptions = [
|
||||
{
|
||||
text: "全部代表团",
|
||||
value: null
|
||||
}
|
||||
]
|
||||
}
|
||||
this.doSearch()
|
||||
},
|
||||
listSession() {
|
||||
this.$axios.post("/platform/proposal/common/listSession").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.sessionOptions = res.data.map((v) => ({text: v.fullName, value: v.id}))
|
||||
this.pageForm.sessionId = res.data[0].id
|
||||
this.sessionChange()
|
||||
}
|
||||
})
|
||||
},
|
||||
listDelegation() {
|
||||
this.$axios.post("/platform/proposal/common/listDelegation", {sessionId: this.pageForm.sessionId}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.delegationOptions = [
|
||||
{
|
||||
text: "全部代表团",
|
||||
value: null
|
||||
}
|
||||
].concat(res.data.map((v) => ({...v, text: v.name, value: v.id})))
|
||||
}
|
||||
})
|
||||
},
|
||||
listRole() {
|
||||
this.$axios.post("/platform/teacherCongress/common/listDelegateRole").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.moreOptions.push({
|
||||
title: "代表身份",
|
||||
options: res.data.map((v) => ({...v, text: v.name}))
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
doSearch() {
|
||||
this.$nextTick(() => {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageForm.totalCount = 0
|
||||
this.$refs.tableListRef.doSearch()
|
||||
})
|
||||
},
|
||||
onReady() {
|
||||
this.listRole()
|
||||
this.listSession()
|
||||
},
|
||||
exportXlsx() {
|
||||
this.$downLoad("/platform/teacherCongress/delegate/read/exportXlsx", this.pageForm)
|
||||
},
|
||||
exportDocx() {
|
||||
this.$downLoad("/platform/teacherCongress/delegate/read/exportDocx", this.pageForm)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
+908
@@ -0,0 +1,908 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
*{
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.mobile-seat-container {
|
||||
background: #f7f8fa;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 160px;
|
||||
}
|
||||
|
||||
.mobile-header {
|
||||
background: white;
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid #ebedf0;
|
||||
/*position: sticky;*/
|
||||
/*top: 0;*/
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.mobile-header .meeting-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #323233;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.mobile-header .meeting-time {
|
||||
font-size: 14px;
|
||||
color: #969799;
|
||||
}
|
||||
|
||||
.mobile-stage {
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
background: transparent;
|
||||
margin: 16px;
|
||||
border-radius: 16px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stage-svg {
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
margin-bottom: 8px;
|
||||
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.15));
|
||||
}
|
||||
|
||||
.stage-text {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #1565c0;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
background: linear-gradient(45deg, #1976d2, #42a5f5);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
.mobile-legend {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: 12px 16px;
|
||||
background: white;
|
||||
margin: 0 16px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.mobile-legend-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.mobile-legend-seat {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.mobile-seat-area {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.zoom-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
padding: 8px 16px;
|
||||
margin-bottom: 8px;
|
||||
/*background: rgba(128, 128, 128, 0.1);*/
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.zoom-controls .van-button {
|
||||
background: rgba(128, 128, 128, 0.2) !important;
|
||||
border: 1px solid rgba(128, 128, 128, 0.3) !important;
|
||||
color: #666 !important;
|
||||
font-size: 11px;
|
||||
padding: 2px 6px !important;
|
||||
height: 24px !important;
|
||||
min-width: auto !important;
|
||||
}
|
||||
|
||||
.zoom-controls .van-button:disabled {
|
||||
background: rgba(128, 128, 128, 0.1) !important;
|
||||
color: #ccc !important;
|
||||
}
|
||||
|
||||
.zoom-level {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
color: #666;
|
||||
padding: 3px 6px;
|
||||
background: rgba(128, 128, 128, 0.15);
|
||||
border-radius: 6px;
|
||||
min-width: 45px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.seat-grid-container {
|
||||
width: 100%;
|
||||
min-height: 300px;
|
||||
position: relative;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.mobile-seat-grid {
|
||||
display: grid;
|
||||
/*grid-template-columns: repeat(10, 1fr);*/
|
||||
gap: 4px;
|
||||
background: white;
|
||||
padding: 12px;
|
||||
border-radius: 12px;
|
||||
width: 100%;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
overflow: auto;
|
||||
max-height: 60vh;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
/* 隐藏滚动条 */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE 和 Edge */
|
||||
}
|
||||
|
||||
/* 隐藏 Webkit 浏览器的滚动条 */
|
||||
.mobile-seat-grid::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-seat {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
position: relative;
|
||||
touch-action: manipulation;
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
.mobile-seat-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
.mobile-seat-number {
|
||||
font-size: 8px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* 座位状态样式 */
|
||||
.mobile-seat.available .seat-back {
|
||||
fill: #f8f9fa;
|
||||
stroke: #dee2e6;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.mobile-seat.available .seat-cushion {
|
||||
fill: #ffffff;
|
||||
stroke: #dee2e6;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.mobile-seat.available .seat-armrest {
|
||||
fill: #e9ecef;
|
||||
stroke: #dee2e6;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.mobile-seat.available .mobile-seat-number {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.mobile-seat.available:active .seat-back {
|
||||
fill: #e3f2fd;
|
||||
stroke: #1867b0;
|
||||
}
|
||||
|
||||
.mobile-seat.available:active .seat-cushion {
|
||||
fill: #f0f8ff;
|
||||
stroke: #1867b0;
|
||||
}
|
||||
|
||||
.mobile-seat.available:active .seat-armrest {
|
||||
fill: #e3f2fd;
|
||||
stroke: #1867b0;
|
||||
}
|
||||
|
||||
.mobile-seat.occupied .seat-back {
|
||||
fill: #adb5bd;
|
||||
stroke: #6c757d;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.mobile-seat.occupied .seat-cushion {
|
||||
fill: #d0d0d0;
|
||||
stroke: #6c757d;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.mobile-seat.occupied .seat-armrest {
|
||||
fill: #adb5bd;
|
||||
stroke: #6c757d;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.mobile-seat.occupied .mobile-seat-number {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.mobile-seat.occupied {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.mobile-seat.selected .seat-back {
|
||||
fill: #0d47a1;
|
||||
stroke: #1867b0;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.mobile-seat.selected .seat-cushion {
|
||||
fill: #1867b0;
|
||||
stroke: #0d47a1;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.mobile-seat.selected .seat-armrest {
|
||||
fill: #0d47a1;
|
||||
stroke: #1867b0;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.mobile-seat.selected .mobile-seat-number {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.mobile-seat.my-seat .seat-back {
|
||||
fill: #c62828;
|
||||
stroke: #ff3742;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.mobile-seat.my-seat .seat-cushion {
|
||||
fill: #ff4757;
|
||||
stroke: #c62828;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.mobile-seat.my-seat .seat-armrest {
|
||||
fill: #c62828;
|
||||
stroke: #ff3742;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.mobile-seat.my-seat .mobile-seat-number {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.mobile-seat.disabled .seat-back {
|
||||
fill: #f5f5f5;
|
||||
stroke: #cccccc;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.mobile-seat.disabled .seat-cushion {
|
||||
fill: #f0f0f0;
|
||||
stroke: #cccccc;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.mobile-seat.disabled .seat-armrest {
|
||||
fill: #f5f5f5;
|
||||
stroke: #cccccc;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.mobile-seat.disabled .mobile-seat-number {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.mobile-seat.disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.mobile-bottom-panel {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: white;
|
||||
border-top: 1px solid #ebedf0;
|
||||
padding: 16px;
|
||||
z-index: 200;
|
||||
}
|
||||
|
||||
.mobile-selected-info {
|
||||
background: #f0f8ff;
|
||||
border: 1px solid #1989fa;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.mobile-selected-info .seat-info {
|
||||
color: #1989fa;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.mobile-my-seat-info {
|
||||
background: #fff2f0;
|
||||
border: 1px solid #ff4757;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-top: 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.mobile-my-seat-info .seat-info {
|
||||
color: #ff4757;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.mobile-loading {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
color: #969799;
|
||||
}
|
||||
|
||||
/* 会议选择弹窗样式 */
|
||||
.meeting-modal-content {
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.meeting-modal-content .van-nav-bar__text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.meeting-item {
|
||||
border: 1px solid #ebedf0;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 12px;
|
||||
background: white;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.meeting-item:active {
|
||||
background: #f0f8ff;
|
||||
border-color: #1989fa;
|
||||
}
|
||||
|
||||
.meeting-item.selected {
|
||||
background: #f0f8ff;
|
||||
border-color: #1989fa;
|
||||
}
|
||||
|
||||
.meeting-name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #323233;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.meeting-info {
|
||||
font-size: 14px;
|
||||
color: #646566;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.meeting-time {
|
||||
font-size: 12px;
|
||||
color: #969799;
|
||||
}
|
||||
|
||||
@media (max-width: 375px) {
|
||||
.mobile-seat-grid {
|
||||
grid-template-columns: repeat(8, 28px);
|
||||
gap: 3px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.mobile-seat {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.mobile-seat-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.mobile-seat-number {
|
||||
font-size: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<van-nav-bar title="会议选座" left-text="返回" left-arrow @click-left="historyBack" placeholder fixed></van-nav-bar>
|
||||
|
||||
<!-- 会议选择弹窗 -->
|
||||
<van-popup v-model="showMeetingModal" position="bottom" :close-on-click-overlay="false">
|
||||
<div class="meeting-modal-content">
|
||||
<van-nav-bar title="选择会议"></van-nav-bar>
|
||||
<div style="padding: 16px;">
|
||||
<div v-for="meeting in meetings" :key="meeting.id"
|
||||
:class="['meeting-item', { selected: selectedMeetingId === meeting.id }]"
|
||||
@click="selectMeeting(meeting)">
|
||||
<div class="meeting-name">{{ meeting.name }}</div>
|
||||
<div class="meeting-info">地点:{{ meeting.address }}</div>
|
||||
<div class="meeting-time">时间:{{ meeting.startTime }} 至 {{ meeting.endTime }}</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 22px">
|
||||
<van-button block @click="showMeetingModal = false" style="margin-top: 16px;">
|
||||
取消选择
|
||||
</van-button>
|
||||
|
||||
<van-button block type="info" :disabled="!selectedMeetingId"
|
||||
@click="confirmMeetingSelection" style="margin-top: 16px;">
|
||||
确认选择
|
||||
</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
|
||||
<div class="mobile-seat-container">
|
||||
<!-- 头部信息 -->
|
||||
<div class="mobile-header" v-if="meeting">
|
||||
<div class="meeting-title">{{ meeting.name }}</div>
|
||||
<div class="meeting-time">{{ meeting.startTime }} 至 {{ meeting.endTime }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 主席台 -->
|
||||
<div class="mobile-stage">
|
||||
<svg class="stage-svg" viewBox="0 0 320 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- 背景装饰 -->
|
||||
<rect x="0" y="0" width="320" height="100" fill="url(#bgGradient)" rx="12" ry="12"/>
|
||||
|
||||
<!-- 主舞台基座 -->
|
||||
<ellipse cx="160" cy="75" rx="140" ry="15" fill="url(#baseGradient)" opacity="0.8"/>
|
||||
|
||||
<!-- 主舞台平台 -->
|
||||
<path d="M 30 45 Q 30 35 40 35 L 280 35 Q 290 35 290 45 L 285 65 Q 285 70 280 70 L 40 70 Q 35 70 35 65 Z"
|
||||
fill="url(#stageGradient)" stroke="url(#stageStroke)" stroke-width="2"/>
|
||||
|
||||
<!-- 台阶层次 -->
|
||||
<path d="M 45 50 Q 45 45 50 45 L 270 45 Q 275 45 275 50 L 272 62 Q 272 65 270 65 L 50 65 Q 47 65 47 62 Z"
|
||||
fill="url(#stepGradient)" stroke="#1976d2" stroke-width="1"/>
|
||||
|
||||
<!-- 装饰花纹 -->
|
||||
<path d="M 60 52 Q 65 50 70 52 Q 75 54 80 52" stroke="#ffffff" stroke-width="1.5" fill="none" opacity="0.7"/>
|
||||
<path d="M 240 52 Q 245 50 250 52 Q 255 54 260 52" stroke="#ffffff" stroke-width="1.5" fill="none" opacity="0.7"/>
|
||||
|
||||
<!-- 中央装饰 -->
|
||||
<circle cx="160" cy="52" r="8" fill="url(#centerGradient)" stroke="#ffffff" stroke-width="1" opacity="0.9"/>
|
||||
<circle cx="160" cy="52" r="4" fill="#ffd700" opacity="0.8"/>
|
||||
|
||||
<!-- 聚光灯光束 -->
|
||||
<path d="M 80 20 L 85 35 L 75 35 Z" fill="url(#lightGradient)" opacity="0.6"/>
|
||||
<path d="M 160 15 L 167 35 L 153 35 Z" fill="url(#lightGradient)" opacity="0.8"/>
|
||||
<path d="M 240 20 L 245 35 L 235 35 Z" fill="url(#lightGradient)" opacity="0.6"/>
|
||||
|
||||
<!-- 聚光灯 -->
|
||||
<circle cx="80" cy="18" r="4" fill="#ffd700" stroke="#ffb300" stroke-width="1"/>
|
||||
<circle cx="160" cy="13" r="5" fill="#ffd700" stroke="#ffb300" stroke-width="1"/>
|
||||
<circle cx="240" cy="18" r="4" fill="#ffd700" stroke="#ffb300" stroke-width="1"/>
|
||||
|
||||
<!-- 星光效果 -->
|
||||
<g opacity="0.8">
|
||||
<path d="M 100 25 L 102 30 L 107 30 L 103 33 L 105 38 L 100 35 L 95 38 L 97 33 L 93 30 L 98 30 Z" fill="#ffd700"/>
|
||||
<path d="M 220 28 L 222 32 L 226 32 L 223 35 L 225 39 L 220 36 L 215 39 L 217 35 L 214 32 L 218 32 Z" fill="#ffd700"/>
|
||||
</g>
|
||||
|
||||
<!-- 渐变定义 -->
|
||||
<defs>
|
||||
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#e3f2fd;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#bbdefb;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="stageGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#1e88e5;stop-opacity:1" />
|
||||
<stop offset="30%" style="stop-color:#1976d2;stop-opacity:1" />
|
||||
<stop offset="70%" style="stop-color:#1565c0;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0d47a1;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="stageStroke" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#42a5f5;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#1565c0;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="stepGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#42a5f5;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#1976d2;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="baseGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#90caf9;stop-opacity:0.6" />
|
||||
<stop offset="100%" style="stop-color:#1976d2;stop-opacity:0.8" />
|
||||
</linearGradient>
|
||||
<radialGradient id="centerGradient" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" style="stop-color:#ffffff;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#1976d2;stop-opacity:1" />
|
||||
</radialGradient>
|
||||
<linearGradient id="lightGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#fff9c4;stop-opacity:0.9" />
|
||||
<stop offset="100%" style="stop-color:#fff59d;stop-opacity:0.3" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="stage-text">主席台</div>
|
||||
</div>
|
||||
|
||||
<!-- 图例 -->
|
||||
<div class="mobile-legend">
|
||||
<div class="mobile-legend-item">
|
||||
<svg class="mobile-legend-seat" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="8" y="5" width="34" height="20" rx="3" ry="3" fill="#f8f9fa" stroke="#dee2e6"
|
||||
stroke-width="1"/>
|
||||
<rect x="5" y="22" width="40" height="18" rx="4" ry="4" fill="#ffffff" stroke="#dee2e6"
|
||||
stroke-width="1"/>
|
||||
<rect x="2" y="20" width="6" height="15" rx="2" ry="2" fill="#e9ecef" stroke="#dee2e6"
|
||||
stroke-width="1"/>
|
||||
<rect x="42" y="20" width="6" height="15" rx="2" ry="2" fill="#e9ecef" stroke="#dee2e6"
|
||||
stroke-width="1"/>
|
||||
</svg>
|
||||
<span>可选</span>
|
||||
</div>
|
||||
<div class="mobile-legend-item">
|
||||
<svg class="mobile-legend-seat" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="8" y="5" width="34" height="20" rx="3" ry="3" fill="#adb5bd" stroke="#6c757d"
|
||||
stroke-width="1"/>
|
||||
<rect x="5" y="22" width="40" height="18" rx="4" ry="4" fill="#d0d0d0" stroke="#6c757d"
|
||||
stroke-width="1"/>
|
||||
<rect x="2" y="20" width="6" height="15" rx="2" ry="2" fill="#adb5bd" stroke="#6c757d"
|
||||
stroke-width="1"/>
|
||||
<rect x="42" y="20" width="6" height="15" rx="2" ry="2" fill="#adb5bd" stroke="#6c757d"
|
||||
stroke-width="1"/>
|
||||
</svg>
|
||||
<span>占用</span>
|
||||
</div>
|
||||
<div class="mobile-legend-item">
|
||||
<svg class="mobile-legend-seat" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="8" y="5" width="34" height="20" rx="3" ry="3" fill="#0d47a1" stroke="#1867b0"
|
||||
stroke-width="2"/>
|
||||
<rect x="5" y="22" width="40" height="18" rx="4" ry="4" fill="#1867b0" stroke="#0d47a1"
|
||||
stroke-width="2"/>
|
||||
<rect x="2" y="20" width="6" height="15" rx="2" ry="2" fill="#0d47a1" stroke="#1867b0"
|
||||
stroke-width="2"/>
|
||||
<rect x="42" y="20" width="6" height="15" rx="2" ry="2" fill="#0d47a1" stroke="#1867b0"
|
||||
stroke-width="2"/>
|
||||
</svg>
|
||||
<span>选中</span>
|
||||
</div>
|
||||
<div class="mobile-legend-item">
|
||||
<svg class="mobile-legend-seat" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="8" y="5" width="34" height="20" rx="3" ry="3" fill="#c62828" stroke="#ff3742"
|
||||
stroke-width="2"/>
|
||||
<rect x="5" y="22" width="40" height="18" rx="4" ry="4" fill="#ff4757" stroke="#c62828"
|
||||
stroke-width="2"/>
|
||||
<rect x="2" y="20" width="6" height="15" rx="2" ry="2" fill="#c62828" stroke="#ff3742"
|
||||
stroke-width="2"/>
|
||||
<rect x="42" y="20" width="6" height="15" rx="2" ry="2" fill="#c62828" stroke="#ff3742"
|
||||
stroke-width="2"/>
|
||||
</svg>
|
||||
<span>我的</span>
|
||||
</div>
|
||||
<div class="mobile-legend-item">
|
||||
<svg class="mobile-legend-seat" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="8" y="5" width="34" height="20" rx="3" ry="3" fill="#f5f5f5" stroke="#cccccc"
|
||||
stroke-width="1"/>
|
||||
<rect x="5" y="22" width="40" height="18" rx="4" ry="4" fill="#f0f0f0" stroke="#cccccc"
|
||||
stroke-width="1"/>
|
||||
<rect x="2" y="20" width="6" height="15" rx="2" ry="2" fill="#f5f5f5" stroke="#cccccc"
|
||||
stroke-width="1"/>
|
||||
<rect x="42" y="20" width="6" height="15" rx="2" ry="2" fill="#f5f5f5" stroke="#cccccc"
|
||||
stroke-width="1"/>
|
||||
</svg>
|
||||
<span>禁用</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 座位区域 -->
|
||||
<div class="mobile-seat-area">
|
||||
<van-loading v-if="loading" type="spinner" color="#1989fa" class="mobile-loading">
|
||||
正在加载座位信息...
|
||||
</van-loading>
|
||||
|
||||
<div v-else class="seat-grid-container">
|
||||
<div class="mobile-seat-grid" id="seatGrid" :style="gridStyle">
|
||||
<div v-for="seat in seats" :key="seat.id"
|
||||
:class="getSeatClass(seat)"
|
||||
class="mobile-seat"
|
||||
@click="selectSeat(seat)">
|
||||
<svg class="mobile-seat-icon" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="8" y="5" width="34" height="20" rx="3" ry="3" class="seat-back"/>
|
||||
<rect x="5" y="22" width="40" height="18" rx="4" ry="4" class="seat-cushion"/>
|
||||
<rect x="2" y="20" width="6" height="15" rx="2" ry="2" class="seat-armrest"/>
|
||||
<rect x="42" y="20" width="6" height="15" rx="2" ry="2" class="seat-armrest"/>
|
||||
</svg>
|
||||
<span class="mobile-seat-number">{{ seat.rowNumber }}-{{ seat.colNumber }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 缩放控制按钮 -->
|
||||
<div class="zoom-controls">
|
||||
<van-button type="default" size="mini" @click="zoomOut" :disabled="currentScale <= 0.5">
|
||||
<van-icon name="minus" size="12" />
|
||||
缩小
|
||||
</van-button>
|
||||
<div class="zoom-level">
|
||||
<van-icon name="search" size="12" />
|
||||
{{ Math.round(currentScale * 100) }}%
|
||||
</div>
|
||||
<van-button type="default" size="mini" @click="resetZoom">
|
||||
<van-icon name="replay" size="12" />
|
||||
重置
|
||||
</van-button>
|
||||
<van-button type="default" size="mini" @click="zoomIn" :disabled="currentScale >= 3">
|
||||
<van-icon name="plus" size="12" />
|
||||
放大
|
||||
</van-button>
|
||||
</div>
|
||||
|
||||
<!-- 我的座位信息 -->
|
||||
<div v-if="myCurrentSeat" class="mobile-my-seat-info" style="margin: 16px;">
|
||||
<div style="margin-bottom: 4px;">我的座位</div>
|
||||
<div class="seat-info">{{ myCurrentSeat.rowNumber }}-{{ myCurrentSeat.colNumber }} (第{{ myCurrentSeat.rowNumber }}排第{{
|
||||
myCurrentSeat.colNumber }}列)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部操作面板 -->
|
||||
<div class="mobile-bottom-panel">
|
||||
<div v-if="selectedSeat" class="mobile-selected-info">
|
||||
<div style="margin-bottom: 4px;">已选择座位</div>
|
||||
<div class="seat-info">{{ selectedSeat.rowNumber }}-{{ selectedSeat.colNumber }} (第{{ selectedSeat.rowNumber }}排第{{
|
||||
selectedSeat.colNumber }}列)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 22px">
|
||||
<van-button type="info" block @click="showMeetingModal = true">
|
||||
切换会议
|
||||
</van-button>
|
||||
|
||||
<van-button type="info" block :disabled="!selectedSeat || submitting"
|
||||
:loading="submitting" @click="confirmSelection">
|
||||
{{ submitting ? '提交中...' : '确认选座' }}
|
||||
</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
const vue = new Vue({
|
||||
el: '#app',
|
||||
data() {
|
||||
return {
|
||||
meetings: [], // 会议列表
|
||||
selectedMeetingId: null, // 选中的会议ID
|
||||
showMeetingModal: true, // 显示会议选择弹窗
|
||||
meeting: null,
|
||||
seats: [],
|
||||
selectedSeat: null,
|
||||
myCurrentSeat: null,
|
||||
loading: false,
|
||||
submitting: false,
|
||||
currentScale: 1, // 当前缩放比例
|
||||
seatConfig: {
|
||||
rows: 8,
|
||||
cols: 12
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadMeetings();
|
||||
this.$nextTick(() => {
|
||||
this.initPinchZoom();
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
gridStyle() {
|
||||
return {
|
||||
'grid-template-columns': 'repeat(' + this.seatConfig.cols + ', 1fr)'
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 加载会议列表
|
||||
loadMeetings() {
|
||||
$.post('/platform/guildHall/meeting/selectList').then(res => {
|
||||
if (res.code === 0) {
|
||||
this.meetings = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 选择会议
|
||||
selectMeeting(meeting) {
|
||||
this.selectedMeetingId = meeting.id;
|
||||
},
|
||||
|
||||
// 确认会议选择
|
||||
confirmMeetingSelection() {
|
||||
if (!this.selectedMeetingId) {
|
||||
this.$toast('请选择一个会议');
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedMeeting = this.meetings.find(m => m.id === this.selectedMeetingId);
|
||||
if (selectedMeeting) {
|
||||
this.meeting = selectedMeeting
|
||||
this.loadSeats();
|
||||
}
|
||||
},
|
||||
|
||||
// 查询选座情况
|
||||
async loadSeats() {
|
||||
if (!this.meeting.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
|
||||
const {
|
||||
code,
|
||||
data,
|
||||
msg
|
||||
} = await $.post('/platform/guildHall/selectSeat/loadSeats', {meetingId: this.meeting.id})
|
||||
if (code === 0) {
|
||||
this.seats = data;
|
||||
this.myCurrentSeat = this.seats.find(seat => seat.isMine);
|
||||
// 根据现有数据计算行列数
|
||||
const maxRow = Math.max(...this.seats.map(s => parseInt(s.rowNumber)))
|
||||
const maxCol = Math.max(...this.seats.map(s => parseInt(s.colNumber)))
|
||||
this.seatConfig.rows = maxRow
|
||||
this.seatConfig.cols = maxCol
|
||||
}
|
||||
|
||||
this.loading = false
|
||||
this.showMeetingModal = false;
|
||||
},
|
||||
|
||||
selectSeat(seat) {
|
||||
if (!seat.enable) {
|
||||
this.$toast('该座位已被禁用');
|
||||
return;
|
||||
}
|
||||
|
||||
if (seat.isOccupied && !seat.isMine) {
|
||||
this.$toast('该座位已被占用');
|
||||
return;
|
||||
}
|
||||
|
||||
if (seat.isMine) {
|
||||
this.$toast('这是您当前的座位');
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedSeat = seat;
|
||||
// 添加触觉反馈
|
||||
if (navigator.vibrate) {
|
||||
navigator.vibrate(50);
|
||||
}
|
||||
},
|
||||
|
||||
async confirmSelection() {
|
||||
if (!this.selectedSeat) {
|
||||
this.$toast('请先选择座位');
|
||||
return;
|
||||
}
|
||||
|
||||
this.submitting = true;
|
||||
const {code, data, msg} = await $.post('/platform/guildHall/selectSeat/doSelect', {
|
||||
meetingId: this.meeting.id,
|
||||
rowNumber: this.selectedSeat.rowNumber,
|
||||
colNumber: this.selectedSeat.colNumber
|
||||
})
|
||||
this.submitting = false
|
||||
|
||||
if (code === 0) {
|
||||
this.$toast.success("选座成功")
|
||||
this.selectedSeat = null;
|
||||
this.loadSeats();
|
||||
} else {
|
||||
this.$toast.fail(msg || '选座失败');
|
||||
}
|
||||
},
|
||||
|
||||
getSeatClass(seat) {
|
||||
if (!seat.enable) {
|
||||
return 'disabled';
|
||||
}
|
||||
if (seat.isMine) return 'my-seat';
|
||||
if (seat === this.selectedSeat) return 'selected';
|
||||
if (seat.isOccupied) return 'occupied';
|
||||
return 'available';
|
||||
},
|
||||
|
||||
getSeatTitle(seat) {
|
||||
if (seat.isMine) return '我的座位';
|
||||
if (seat === this.selectedSeat) return '当前选择';
|
||||
if (seat.status === 1) return '已被占用';
|
||||
return '点击选择此座位';
|
||||
},
|
||||
|
||||
formatTime(time) {
|
||||
if (!time) return '';
|
||||
return new Date(time).toLocaleString('zh-CN');
|
||||
},
|
||||
|
||||
// 放大
|
||||
zoomIn() {
|
||||
if (this.currentScale < 3) {
|
||||
this.currentScale = Math.min(3, this.currentScale + 0.2);
|
||||
this.applyZoom();
|
||||
}
|
||||
},
|
||||
|
||||
// 缩小
|
||||
zoomOut() {
|
||||
if (this.currentScale > 0.5) {
|
||||
this.currentScale = Math.max(0.5, this.currentScale - 0.2);
|
||||
this.applyZoom();
|
||||
}
|
||||
},
|
||||
|
||||
// 重置缩放
|
||||
resetZoom() {
|
||||
this.currentScale = 1;
|
||||
this.applyZoom();
|
||||
},
|
||||
|
||||
// 应用缩放
|
||||
applyZoom() {
|
||||
const seats = document.querySelectorAll('.mobile-seat');
|
||||
const seatGrid = document.querySelector('.mobile-seat-grid');
|
||||
|
||||
seats.forEach(seat => {
|
||||
seat.style.transform = 'scale(' + this.currentScale + ')';
|
||||
seat.style.transformOrigin = 'center center';
|
||||
seat.style.transition = 'transform 0.2s ease';
|
||||
});
|
||||
|
||||
// 动态调整网格间距
|
||||
if (seatGrid) {
|
||||
const baseGap = 4;
|
||||
const adjustedGap = baseGap * this.currentScale * 5;
|
||||
seatGrid.style.gap = adjustedGap + 'px';
|
||||
}
|
||||
},
|
||||
|
||||
// 初始化双指缩放功能(已替换为按钮控制)
|
||||
initPinchZoom() {
|
||||
// 双指缩放功能已被按钮控制替代,保留此方法以避免错误
|
||||
console.log('双指缩放功能已替换为按钮控制');
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
Reference in New Issue
Block a user