Files
zhgh_hmc/src/main/resources/views/mobile/home.html
T
2025-08-13 16:00:10 +08:00

743 lines
28 KiB
HTML

<!--#
layout("/mobile/platform.html"){
#-->
<style>
#app {
background-color: #f6f7f9;
}
.swiper {
height: 180px;
}
.swiper img {
height: 180px;
}
.module {
margin: 10px 10px 14px 10px;
box-shadow: 0 8px 12px #ebedf0;
border-radius: 10px;
}
.van-sidebar-item {
border-top-left-radius: 10px;
border-top-right-radius: 10px;
font-weight: bold;
}
.menu {
display: flex;
flex-wrap: wrap;
row-gap: 10px;
background-color: white;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
/*padding: 6px 0px;*/
}
.menu_item {
width: calc(100% / 4);
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 2px 0;
position: relative;
}
.menu_item_img {
width: 46px;
height: 46px;
}
.menu_item_name {
white-space: nowrap;
overflow: hidden;
width: 80%;
text-align: center;
font-size: 12px;
font-family: "微软雅黑", Helvetica, STHeiTi, sans-serif;
color: grey;
}
.number_text {
font-size: 36px;
color: #409eff;
}
.van-doc-card {
margin: 14px;
padding: 12px 38px;
background-color: #fff;
border-radius: 10px;
box-shadow: 0 8px 12px #ebedf0;
line-height: 36px;
font-size: 13px;
position: relative;
}
.title {
text-align: center;
font-size: 18px;
}
.middle {
background-color: white;
margin: 6px 10px 10px 10px;
padding: 10px;
}
.middle_item {
margin-bottom: 6px;
}
.middle_item:last-of-type {
margin-bottom: 0;
}
.middle_item_pic {
margin-right: 10px;
}
.middle_item_img {
width: 100%;
height: 140px;
object-fit: revert;
display: block;
}
.middle_item_img img {
border-radius: 10px;
}
.middle_item_text {
font-size: 12px;
margin-top: 6px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.middle_item_text > div:nth-of-type(1) {
font-size: 14px;
font-weight: bold;
display: flex;
}
.van-badge {
position: absolute;
right: 20px;
top: -2px;
}
.middle .van-empty {
position: unset;
transform: none;
}
.sort_div {
display: flex;
font-size: 12px;
align-items: center;
color: #1867B0;
justify-content: flex-end;
margin-right: 16px;
}
.van-tabs .van-icon::before {
position: relative;
top: 2px;
}
.msg_div_text {
display: flex;
align-items: flex-start;
width: 100%;
justify-content: space-between;
margin-left: 10px;
}
.sign_popup {
width: 96%;
height: 640px;
border-radius: 20px;
}
.menu_item_name {
margin-top: 6px;
}
</style>
<div id="app">
<!--首页-->
<div style="margin-bottom: 60px;max-height: calc(100vh - 60px);overflow-y: auto" v-if="active === 0">
<!--轮播图-->
<van-swipe :autoplay="3000" class="swiper">
<van-swipe-item :key="index" v-for="(image, index) in images">
<img :src="image" width="100%">
</van-swipe-item>
</van-swipe>
<!--应用窗格-->
<van-row class="first module">
<div class="menu" style="border-radius: 10px">
<div @click="clickIndex = 3; getMsg()" class="menu_item">
<img alt="" class="menu_item_img" src="/assets/mobile/img/home/1.message.png">
<span class="menu_item_name">消息</span>
<van-badge :content="msgCount" v-if="msgCount !== null"></van-badge>
</div>
<!-- <div @click="qiandao" class="menu_item">
<img src="/assets/mobile/img/home/2.签到.png" alt="" class="menu_item_img">
<span class="menu_item_name">签到</span>
</div>
<div @click="integral" class="menu_item">
<img src="/assets/mobile/img/home/11.积分管理.png" alt="" class="menu_item_img">
<span class="menu_item_name">积分详情</span>
</div>-->
<div @click="clickMenu = item; clickIndex = 2" class="menu_item"
v-for="item in moduleMenus">
<img :src="CREATE_PREVIEW_URL(item.icon)" alt="" class="menu_item_img">
<span class="menu_item_name">{{item.moduleName}}</span>
</div>
</div>
</van-row>
<!--下方功能区,包含活动、功能入口、消息列表-->
<van-row>
<div @click="clickIndex = 1" class="sort_div" v-if="clickIndex !== 1">
<van-icon name="sort" style="transform: rotate(90deg);"></van-icon>
<span>切换活动列表</span>
</div>
<!--活动列表-->
<template v-if="clickIndex === 1">
<div class="middle" style="border-radius: 10px;" v-for="item in activityList"
v-if="activityList.length > 0">
<van-row gutter="20">
<van-col @click="menuClick(item.h5Url, item.id)" class="middle_item" span="24">
<div>
<van-image :src="CREATE_PREVIEW_URL(item.cover)"
class="middle_item_img" v-if="item.cover">
<template slot="loading">
<van-loading size="13" type="spinner"/>
</template>
</van-image>
<van-image height="84" src="xxxxxx" v-else width="120">
<template slot="error">图片查询失败</template>
</van-image>
</div>
<div class="middle_item_text">
<van-icon color="red" name="fire" size="12" style="top: 1px">
</van-icon>
{{ item.name }}
</div>
<div style="margin-top: 6px; color: grey; font-size: 11px">
<div>开始时间:{{ moment(item.startDate).format('MM/DD HH:mm') }}
</div>
<div>结束时间:{{ moment(item.endDate).format('MM/DD HH:mm') }}
</div>
</div>
</van-col>
</van-row>
</div>
<div v-else>
<van-empty description="近期暂无活动"></van-empty>
</div>
</template>
<!--功能入口-->
<template v-else-if="clickIndex === 2">
<div class="middle" style="border-radius: 10px;">
<div class="van-sidebar-item van-sidebar-item--select"
style="padding: 0 12px 0 12px !important;">
{{clickMenu.moduleName}}
</div>
<div class="menu" style="margin-top: 10px">
<div :key="m.menuId" @click="menuClick(m.url, null)" class="menu_item"
v-for="m in clickMenu.menuList"
v-if="(m.url == '/mobile/member/apply' && ${@shiro.lacksRole('H01')}) || m.url != '/mobile/member/apply'">
<img :src="CREATE_PREVIEW_URL(m.menuIcon)" alt=""
class="menu_item_img"/>
<span class="menu_item_name">{{m.menuName}}</span>
</div>
</div>
</div>
</template>
<!--消息列表-->
<template v-else-if="clickIndex === 3">
<div class="middle" style="border-radius: 10px; margin-bottom: 30px">
<van-tabs @click="msgList = null; getMsg()" color="#1867B0" type="card"
v-model="msgActive">
<van-tab name="unread">
<template #title>
<van-icon name="chat-o"></van-icon>
未读
</template>
</van-tab>
<van-tab name="read">
<template #title>
<van-icon name="browsing-history-o"></van-icon>
已读
</template>
</van-tab>
</van-tabs>
<div class="msg_div" style="max-height: 280px; overflow-y: auto">
<template v-if="msgList !== null && msgList.length > 0">
<div @click="msgToUrl(item)" style="display: flex; margin-top: 10px"
v-for="item in msgList">
<div>
<van-icon color="red" name="new" size="43px"
style="position: relative; top: 6px"
v-if="msgActive === 'unread'"></van-icon>
<van-icon color="lightgrey" name="new" name="checked"
size="38px"
style="position: relative; top: 3px"
v-else></van-icon>
</div>
<div class="msg_div_text">
<div>
<div style="font-size: 14px">{{ item.title }}</div>
<div style="font-size: 12px; margin-top: 6px"
v-html="item.note"></div>
</div>
<div>
<van-icon name="arrow"></van-icon>
</div>
</div>
</div>
</template>
<div v-else-if="msgList === null"></div>
<div v-else>
<van-empty description="暂无数据"></van-empty>
</div>
</div>
</div>
</template>
</van-row>
</div>
<!--我的办理-->
<div style="background-color: #f6f7f9; min-height: 100vh" v-if="active === 1">
<van-notice-bar
background="#ecf9ff" color="#1989fa" left-icon="volume-o"
text="温馨提示:只显示当前年份的待办和已办事项"
></van-notice-bar>
<van-tabs @click="initData" v-model="activeName">
<van-tab name="0" title="待办">
<div style="min-height: 80vh">
<div v-for="o in needItems">
<div @click="toUrl(o)" class="van-doc-card" v-if="o.count > 0">
<div class="title">{{ o.title }}</div>
<div style="display: flex; align-items: center; justify-content: space-between;">
<div>
<div style="color: grey; font-size: 15px">{{ o.name }}</div>
</div>
<div class="number_text">
<span>{{ o.count }}</span>
</div>
</div>
</div>
</div>
<van-empty
class="custom-image"
description="暂无待办"
image="https://img01.yzcdn.cn/vant/custom-empty-image.png"
style="margin-top: 44px"
v-if="needItems.length === 0"
></van-empty>
</div>
</van-tab>
<van-tab name="1" title="已办">
<div style="min-height: 80vh">
<div @click="toHref(o)" class="van-doc-card" v-for="o in completedItems">
<div class="title">{{ o.moduleName }}</div>
<div style="display: flex; align-items: center; justify-content: space-between;">
<div>
<span>{{ o.auditState }}</span>
</div>
<div>
<div style="color: grey; font-size: 15px">{{ o.auditTime }}</div>
</div>
</div>
</div>
<van-empty
class="custom-image"
description="暂无已办"
image="https://img01.yzcdn.cn/vant/custom-empty-image.png"
style="margin-top: 44px"
v-if="completedItems.length === 0"
></van-empty>
</div>
</van-tab>
</van-tabs>
</div>
<!--工作台-->
<div style="margin-bottom: 60px" v-if="active === 2">
<van-row class="module" v-for="m in moduleMenus">
<div class="van-sidebar-item van-sidebar-item--select">
{{m.moduleName}}
</div>
<div class="menu">
<div :key="m.menuId" @click="menuClick(m.url, null)" class="menu_item"
v-for="m in m.menuList">
<img :src="CREATE_PREVIEW_URL(m.menuIcon)" alt="" class="menu_item_img">
<span class="menu_item_name">{{m.menuName}}</span>
</div>
</div>
</van-row>
</div>
<van-tabbar @change="tabbrChange" v-model="active">
<van-tabbar-item icon="home-o">首页</van-tabbar-item>
<van-tabbar-item :dot="needCount" icon="envelop-o">我的办理</van-tabbar-item>
<van-tabbar-item icon="apps-o">工作台</van-tabbar-item>
</van-tabbar>
<van-popup :style="{ height: '100%',width: '100%'}" position="right" safe-area-inset-bottom
v-model="integralShow">
<van-sticky>
<van-nav-bar @click-left="integralShow=false" left-arrow
title="积分详情"></van-nav-bar>
<div class="search-fixed">
<van-dropdown-menu active-color="#1989fa">
<van-dropdown-item :options="yearList" @change="integralData"
v-model="integralForm.year"></van-dropdown-item>
<van-dropdown-item :options="monthList" @change="integralData"
v-model="integralForm.month"></van-dropdown-item>
</van-dropdown-menu>
</div>
<div style="margin-top: 60px;font-family: 微软雅黑">
<div style="text-align: right;margin-right: 20px;font-size: 20px;color: #1867b0">
<span>总积分:{{totalIntegral}}</span>
</div>
<van-list style="margin-top: 10px">
<div :key="item.id" v-for="item in integralViewData">
<van-cell-group inset>
<van-cell>
<div>
<span style="font-size: 16px;font-weight: bold">{{item.integralNotes}}</span>
</div>
<div>
<span style="font-size: 12px;color: gray;">{{item.integralTime}}</span>
</div>
<div style="text-align: right;margin-top: -30px;font-size: 16px;">
<span>{{item.integral}}积分</span>
</div>
</van-cell>
</van-cell-group>
</div>
</van-list>
</div>
</van-sticky>
</van-popup>
<van-action-sheet :close-on-click-overlay="false" :closeable="false"
title="完善信息" v-model="perfectPersonInfo.isShow">
<van-form @submit="doImprovePersonalInfo">
<van-field
label="工号"
name="loginname"
placeholder="工号"
readonly
value="${@shiro.getPrincipalProperty('loginname')}"
></van-field>
<van-field
label="姓名"
name="loginname"
placeholder="姓名"
readonly
value="${@shiro.getPrincipalProperty('username')}"
></van-field>
<template v-for="column in perfectPersonInfo.columns">
<van-field
:label="column.label"
:name="column.prop"
:placeholder="column.label"
:rules="
[{required: true, message: '必填', trigger: ['blur', 'change']},
{pattern:column.prop==='mobile' ? /^1[3-9]\d{9}$/ : /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/,
message: '请输入正确的'+column.label}]"
v-model="ImprovePersonalInfoData[column.prop]"
></van-field>
</template>
<div style="padding: 10px 16px;font-size: 14px;color: grey">
{{perfectPersonInfo.tip}}
</div>
<div style="margin: 16px;">
<van-button block native-type="doImprovePersonalInfo" round type="info">提交
</van-button>
</div>
</van-form>
</van-action-sheet>
<van-dialog v-model="activityQrCodeShow" title="请扫描二维码参与">
<img :src="CREATE_PREVIEW_URL('/2025/06/24/Mr10MfxSH9L2W1fPEv1Pv0.png')" width="100%" alt="二维码加载失败"/>
<!-- <img :src="CREATE_PREVIEW_URL('8f52c151203e443e9298bca6212bbab8')" width="100%" alt="二维码加载失败"/>-->
<!-- <img src="https://zhgh.hmc.edu.cn/file_server/fileStreamPreview?id=/2025/06/24/Mr10MfxSH9L2W1fPEv1Pv0.png" width="100%" alt="二维码加载失败"/>-->
</van-dialog>
</div>
<script>
const vue = new Vue({
el: '#app',
dicts: ['memberApplyPersonType'],
data() {
return {
swipeShow: false,
images: [
"${AppDomain!}" + '/home1.png',
"${AppDomain!}" + '/home2.png',
"${AppDomain!}" + '/home3.png',
],
qianShow: false,
msgActive: 'unread',
moduleMenus: [],
roles: [],
active: 0,
needCount: 0,
needItems: [],
activeName: '0',
completedItems: [],
bigPictureList: [],
activityList: [],
clickMenu: {},
clickIndex: 1,
msgPageForm: {},
msgList: [],
msgCount: null,
integralShow: false,
integralForm: {},
integralViewData: [],
totalIntegral: null,
yearList: [],
monthList: [],
perfectPersonInfo: {
isShow: false,
tip: null,
columns: []
},
ImprovePersonalInfoData: {},
activityQrCodeShow: false,
}
},
components: {},
methods: {
async getMenus() {
const {data} = await $.post('/platform/sys/appMenu/appMenus')
this.moduleMenus = data
},
menuClick(menu, id) {
/* if (menu.url === '/mobile/member/apply') {
const values = this.dict.type.memberApplyPersonType.map(v => v.value)
const personType = "${@shiro.getPrincipalProperty('personType')}"
const msg = '您当前的人员类型是<span style="color: red">' + (personType || '空') + '</span>,目前只允许<strong>' + values.join('、') + '</strong>加入会员,如有疑问,请联系校工会进行更改。'
if (!values.includes(personType)) {
this.$modal.alert(msg)
return
}
}*/
if (id && id === '54681bfcfa25432784166eb7ecaa477c') {
this.activityQrCodeShow = true
} else {
pjaxReplace(menu)
}
},
tabbrChange(o) {
console.log(o)
sessionStorage.setItem("zhgh-mobile-home-active",o)
this.needItems = []
this.initData()
},
toUrl(o) {
pjaxReplace(o.mobileHref)
},
toHref(o) {
pjaxReplace(o.mobileUrl + '?audit=1');
},
async getNeedItems() {
const {data, code} = await $.get('/platform/needItems/getNeedItems')
if (code === 0) {
const obj = data.filter(v => {
return v.mobileHref != null && v.count > 0
})
this.needItems = obj
}
},
async getCompletes() {
const {data, code} = await $.get('/platform/sys/completed/getCompletes')
if (code === 0) {
this.completedItems = data;
}
},
/**
* 会员个人核对待办
* @returns {Promise<void>}
*/
async getMemberCheckSelfAgenda() {
const {data, code} = await $.get("/platform/memberSelf/agenda/getAgenda")
if (code === 0) {
this.needItems = this.needItems.concat(data.filter(v => v.count > 0))
}
},
/**
* 福利个人核对待办
* @returns {Promise<void>}
*/
async getWelfareCheckSelfAgenda() {
const {data, code} = await $.get("/platform/welfareSelf/agenda/getAgenda")
if (code === 0) {
this.needItems = this.needItems.concat(data.filter(v => v.count > 0))
}
},
async initData() {
await this.getNeedItems();
await this.getMemberCheckSelfAgenda()
await this.getWelfareCheckSelfAgenda()
await this.getCompletes();
await this.getMsg()
this.listActivity();
this.needCount = this.needItems.length
},
async setNeedItems() {
const {data, code} = await $.get('/platform/needItems/getNeedItems')
if (code === 0) {
const obj = data.filter(v => {
return v.mobileHref != null && v.count > 0
})
window.localStorage.setItem('needItems', escape(JSON.stringify(obj)))
}
},
//获取站内消息
async getMsg() {
const resp = await $.post("/platform/sys/msg/user/data/" + this.msgActive, this.msgPageForm)
this.msgList = resp.data.list
this.msgList.forEach(item => {
const index = item.note.indexOf("<a")
if (index !== -1) {
item.note = item.note.substring(0, index)
}
})
if (this.msgActive === 'unread') {
this.msgCount = this.msgList.length
}
},
async msgToUrl(o) {
const resp = await $.get("/platform/sys/msg/user/status/read", {ids: JSON.stringify([o.id])})
location.href = o.mobileurl
},
qiandao() {
this.qianShow = true
},
integral() {
this.integralShow = true
},
getAllMonth() {
for (let i = 12; i >= 1; i--) {
this.monthList.unshift({value: i, text: i + '月'})
}
this.$set(this.integralForm, "month", new Date().getMonth() + 1)
},
createYearList() {
for (let i = new Date().getFullYear() - 50; i <= new Date().getFullYear(); i++) {
this.yearList.unshift({value: i, text: i + '年'},)
}
this.$set(this.integralForm, "year", this.yearList[0].value)
},
async integralData() {
const date = new Date(this.integralForm.year, this.integralForm.month - 1);
const resp = await $.post('/platform/integral/manage/getDetailByUserId', {
month: moment(date).format('YYYY-MM'),
userId: "${@shiro.getPrincipalProperty('id')}"
})
if (resp.code === 0) {
this.integralViewData = resp.data
let sum = a => a.reduce((x, y) => x + y);
this.totalIntegral = 0;
this.totalIntegral = sum(this.integralViewData.map(x => Number(x.integral)));
}
},
//完善个人信息
async doImprovePersonalInfo(values) {
const userFormKeys = Object.keys(this.ImprovePersonalInfoData)
const formData = userFormKeys.map(k => {
return {
column: k,
value: this.ImprovePersonalInfoData[k]
}
})
const resp = await $.post('/platform/sys/user/doImprovePersonalInfo', {
formData: JSON.stringify(formData)
})
if (resp.code === 0) {
this.perfectPersonInfo.isShow = false
this.$modal.msgSuccess(resp.msg)
} else {
this.$modal.msgError(resp.msg)
}
},
//查询个人是否完善信息
async isImprovePersonalInfo() {
const resp = await $.get('/platform/sys/user/isImprovePersonalInfo')
if (resp.code === 0) {
this.perfectPersonInfo = resp.data
}
},
listActivity() {
$.get("/platform/sys/homeActivity/listHomeActivity").then((resp) => {
if (resp.code === 0) {
this.activityList = resp.data
}
})
},
},
beforeMount() {
this.setNeedItems();
},
async created() {
await this.getMenus();
this.getAllMonth();
this.createYearList();
this.initData();
const active = sessionStorage.getItem("zhgh-mobile-home-active")
this.active = active ? parseInt(active) : 0
this.clickIndex = 2
this.clickMenu = this.moduleMenus.find(o => o.moduleName === '职工权益')
}
})
window.addEventListener('pageshow', e => {
if (e.persisted || (window.performance && window.performance.navigation.type === 2)) {
vue.getMenus()
vue.initData()
}
})
</script>
<!--#
}
#-->