1
This commit is contained in:
@@ -71,7 +71,7 @@
|
||||
<script src="https://vxeui.com/umd/xe-utils@3.5.30/dist/xe-utils.umd.min.js"></script>
|
||||
<script src="https://vxeui.com/umd/vxe-pc-ui@3.1.25/lib/index.umd.min.js"></script>
|
||||
<script src="https://vxeui.com/umd/vxe-table@3.9.0/lib/index.umd.min.js"></script>
|
||||
l
|
||||
|
||||
<!-- 引入 form-create 和 designer -->
|
||||
<script src="${base!}/assets/platform/plugins/form-create/form-create.min.js"></script>
|
||||
<script src="${base!}/assets/platform/plugins/form-create/index.umd.js"></script>
|
||||
@@ -80,11 +80,12 @@
|
||||
<script src="${base!}/assets/platform/plugins/swiper/swiper-bundle.js"></script>
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/plugins/swiper/swiper-bundle.min.css"></link>
|
||||
|
||||
<!-- <script src="${base!}/assets/platform/plugins/snaker/SnakerflowDesigner.umd.min.js"></script>-->
|
||||
<!--<script src="${base!}/assets/platform/plugins/snaker/SnakerflowDesigner.umd.js"></script>-->
|
||||
|
||||
<script src="${base!}/components/plugins/sysDict/DictData.js"></script>
|
||||
<script src="${base!}/assets/platform/js/util/commonUtil.js"></script>
|
||||
<script src="${base!}/assets/platform/js/util/coordinateUtil.js"></script>
|
||||
<script src="${base!}/assets/platform/js/util/voiceMenuNavigator.js"></script>
|
||||
<script src="${base!}/assets/platform/js/main.js"></script>
|
||||
<script src="${base!}/assets/platform/js/mixin/initTableMixins.js"></script>
|
||||
<script src="${base!}/assets/platform/js/mixin/styleMixin.js"></script>
|
||||
@@ -100,13 +101,32 @@
|
||||
|
||||
<script src="https://map.qq.com/api/gljs?v=2.exp&key=MLLBZ-GQECI-ASXG7-5GNOZ-XW2OF-H5BVH"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
window._AMapSecurityConfig = {
|
||||
securityJsCode: "4fa1e1aeabba7eb9518129cf57ab17c1"
|
||||
<script nonce="${cspNonce!}">
|
||||
// 在加载 lodash 后、使用前插入
|
||||
const originalDefaultsDeep = window._.defaultsDeep;
|
||||
window._.defaultsDeep = function(...args) {
|
||||
// 先对所有参数做原型污染清洗
|
||||
const cleanArgs = args.map(arg => sanitizeForPrototypePollution(arg));
|
||||
return originalDefaultsDeep.apply(window._, cleanArgs);
|
||||
};
|
||||
|
||||
function sanitizeForPrototypePollution(obj) {
|
||||
if (obj === null || typeof obj !== 'object') return obj;
|
||||
if (Array.isArray(obj)) return obj.map(sanitizeForPrototypePollution);
|
||||
|
||||
const clean = {};
|
||||
for (const key in obj) {
|
||||
if (!Object.hasOwn(obj, key)) continue;
|
||||
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
|
||||
continue;
|
||||
}
|
||||
clean[key] = sanitizeForPrototypePollution(obj[key]);
|
||||
}
|
||||
return clean;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script nonce="${cspNonce!}" type="text/javascript">
|
||||
Vue.config.devtools = false
|
||||
ELEMENT.locale(ELEMENT.lang["${lang,escape}"])
|
||||
ELEMENT.Dialog.props.closeOnClickModal.default = false
|
||||
@@ -123,12 +143,12 @@
|
||||
</script>
|
||||
|
||||
<!--广播频道-->
|
||||
<script type="text/javascript">
|
||||
<script nonce="${cspNonce!}" type="text/javascript">
|
||||
window.GlobalBroadcastChannel = new BroadcastChannel("zhgh-global-channel")
|
||||
</script>
|
||||
|
||||
<!--ws-->
|
||||
<script type="text/javascript">
|
||||
<script nonce="${cspNonce!}" type="text/javascript">
|
||||
class WebSocketPubSub {
|
||||
constructor() {
|
||||
this.ws = null
|
||||
@@ -150,6 +170,7 @@
|
||||
|
||||
connect() {
|
||||
const WS_URL = window.location.host + "${base}/websocket"
|
||||
// const WS_URL = 'zhgh.jshvc.edu.cn' + "${base}/websocket"
|
||||
const protocol = window.location.protocol === "http:" ? "ws://" : "wss://"
|
||||
this.ws = new WebSocket(protocol + WS_URL)
|
||||
}
|
||||
@@ -162,13 +183,12 @@
|
||||
this.subscribers.get(type).add(callback)
|
||||
|
||||
setTimeout(() => {
|
||||
console.log(this.ws.readyState)
|
||||
// 所有订阅完成后,发送 join 消息
|
||||
if (!this.isSubscribed && this.ws.readyState === WebSocket.OPEN) {
|
||||
this.isSubscribed = true
|
||||
this.sendJoinMessage()
|
||||
}
|
||||
}, 2000)
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
// 事件处理
|
||||
@@ -202,6 +222,7 @@
|
||||
// )
|
||||
// 连接建立时不立即发送 join,等待订阅完成
|
||||
this.ping()
|
||||
console.info('websocket open success')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,9 +264,9 @@
|
||||
if (this.ws.readyState === WebSocket.OPEN) {
|
||||
this.sendJoinMessage()
|
||||
}
|
||||
}, 1000)
|
||||
}, 500)
|
||||
}
|
||||
}, 3000) // 延迟3秒重连
|
||||
}, 1000) // 延迟2秒重连
|
||||
}
|
||||
|
||||
// 窗口事件处理
|
||||
@@ -277,7 +298,7 @@
|
||||
</script>
|
||||
|
||||
<!--vuex-->
|
||||
<script type="text/javascript">
|
||||
<script nonce="${cspNonce!}" type="text/javascript">
|
||||
window.store = new Vuex.Store({
|
||||
plugins: [
|
||||
createPersistedState({
|
||||
@@ -303,11 +324,11 @@
|
||||
//设置用户信息
|
||||
setUser(state, payload) {
|
||||
state.user = payload
|
||||
state.room = payload.loginname + ":${@auth.getSessionId()}"
|
||||
}
|
||||
},
|
||||
actions: {}
|
||||
})
|
||||
|
||||
$.get("/platform/sys/user/getLogonUser").then((res) => {
|
||||
if (res.code === 0) {
|
||||
window.sessionStorage.setItem("user", JSON.stringify(res.data))
|
||||
@@ -317,13 +338,13 @@
|
||||
</script>
|
||||
|
||||
<!--beetl全局变量方法-->
|
||||
<script type="text/javascript">
|
||||
<script nonce="${cspNonce!}" type="text/javascript">
|
||||
const base = "${base!}"
|
||||
const APP_DOMAIN = "${AppDomain!}"
|
||||
</script>
|
||||
|
||||
<!--vue挂载-->
|
||||
<script type="text/javascript">
|
||||
<script nonce="${cspNonce!}" type="text/javascript">
|
||||
Vue.prototype.$moment = moment
|
||||
Vue.prototype.$businessTool = businessTool
|
||||
Vue.prototype.$commonUtil = commonUtil
|
||||
@@ -331,10 +352,20 @@
|
||||
Vue.prototype.$auth = commonUtil.authService()
|
||||
Vue.prototype.$axios = commonUtil.axiosService()
|
||||
Vue.prototype.$downLoad = commonUtil.downLoadService
|
||||
|
||||
Vue.prototype.$processStatusMap = {
|
||||
10: {text: "进行中", class: "doing"},
|
||||
20: {text: "已完成", class: "finished"},
|
||||
30: {text: "已撤回", class: "withdraw"},
|
||||
40: {text: "强行终止", class: "interrupt"},
|
||||
45: {text: "已拒绝", class: "reject"},
|
||||
50: {text: "挂起", class: "pending"},
|
||||
99: {text: "已废弃", class: "abandon"}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!--vue全局组件-->
|
||||
<script>
|
||||
<script nonce="${cspNonce!}">
|
||||
Vue.component(VueQrcode.name, VueQrcode)
|
||||
Vue.component("guava", httpVueLoader("/components/plugins/Guava.vue?v=" + new Date().getTime()))
|
||||
Vue.component("enum-tag", httpVueLoader("/components/plugins/sysEnum/EnumTag.vue?v=" + new Date().getTime()))
|
||||
@@ -511,6 +542,33 @@
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.v4-voice-menu {
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
padding: 0 14px;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
white-space: nowrap;
|
||||
font: inherit;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.v4-voice-menu:hover,
|
||||
.v4-voice-menu.is-listening {
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.v4-voice-menu.is-listening i {
|
||||
color: #ffdf6b;
|
||||
}
|
||||
|
||||
.v4-avatar {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
@@ -576,6 +634,10 @@
|
||||
.v4-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.voice-menu-text {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 页脚样式 */
|
||||
@@ -647,6 +709,10 @@
|
||||
</div>
|
||||
|
||||
<div class="v4-user-section">
|
||||
<button type="button" class="v4-voice-menu" id="voice-menu-btn" data-platform="PC" title="语音打开菜单">
|
||||
<i class="fa fa-microphone"></i>
|
||||
<span class="voice-menu-text">语音</span>
|
||||
</button>
|
||||
<div class="v4-user-info">
|
||||
<span class="v4-user-name">${@auth.getPrincipalProperty('username')} (${@auth.getPrincipalProperty('loginname')})</span>
|
||||
<!-- <i class="fa fa-angle-down"></i> -->
|
||||
@@ -657,7 +723,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
<div style="height: 45px"></div>
|
||||
<div style="height: 64px"></div>
|
||||
|
||||
<main class="v4-content">
|
||||
<!-- 页面内容区域 -->
|
||||
@@ -679,12 +745,12 @@
|
||||
</footer>
|
||||
|
||||
|
||||
<script>
|
||||
<script nonce="${cspNonce!}">
|
||||
// 处理导航项的active状态
|
||||
$(document).ready(function () {
|
||||
// 控制页脚显示的函数
|
||||
function toggleFooter() {
|
||||
var currentPath = window.location.pathname
|
||||
var currentPath = getBaseSubAppPath()
|
||||
var footer = $("#v4-footer")
|
||||
|
||||
if (currentPath === "/platform/v4/home") {
|
||||
@@ -696,7 +762,7 @@
|
||||
|
||||
// 初始化:根据当前URL设置active状态
|
||||
function setActiveNavItem() {
|
||||
var currentPath = window.location.pathname
|
||||
var currentPath = getBaseSubAppPath()
|
||||
$(".v4-nav-item").removeClass("active")
|
||||
$(".v4-nav-item").each(function () {
|
||||
if ($(this).attr("href") === currentPath) {
|
||||
|
||||
@@ -1139,7 +1139,7 @@ layout("/layouts/v4/baseLayout.html"){
|
||||
|
||||
.oa-grassroots-wrapper {
|
||||
width: 100%;
|
||||
background-image: url('https://cdgh.ncu.edu.cn/images/index_bg2.png');
|
||||
background-image: url(/assets/platform/img/v4/home-bg.png);
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
@@ -1513,7 +1513,7 @@ layout("/layouts/v4/baseLayout.html"){
|
||||
<div class="oa-hero-section">
|
||||
<!-- 背景图 -->
|
||||
<div class="oa-background-image">
|
||||
<img src="${config.AppHomeImg!}" alt=""/>
|
||||
<img :src="homeBannerList[0] || ''" alt=""/>
|
||||
</div>
|
||||
|
||||
<!-- 搜索和统计区域 -->
|
||||
@@ -1999,7 +1999,7 @@ layout("/layouts/v4/baseLayout.html"){
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
<script nonce="${cspNonce!}">
|
||||
const vue = new Vue({
|
||||
el: "#v4-home-app",
|
||||
data() {
|
||||
@@ -2020,6 +2020,7 @@ layout("/layouts/v4/baseLayout.html"){
|
||||
recommendServices: [],
|
||||
favoriteItems: [],
|
||||
recommendApps: [],
|
||||
homeBannerList: "${config.AppHomeImg!}".split(",").map(item => item.trim()).filter(item => item),
|
||||
|
||||
// 工会网站新闻
|
||||
websiteNews: {},
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
const act = {
|
||||
template: /*language=HTML*/ `
|
||||
<!-- 活动 -->
|
||||
<div class="section-wrapper">
|
||||
<div class="activity-section-wrapper">
|
||||
<div class="section-act">
|
||||
<!-- Swiper容器 -->
|
||||
|
||||
<div v-if="actList && actList.length > 0" class="activity-swiper-container">
|
||||
<div class="activity-swiper-container">
|
||||
<!-- 活动数据 - Swiper -->
|
||||
<div class="swiper activity-swiper">
|
||||
<div class="swiper-wrapper">
|
||||
<div class="swiper-slide" v-for="(activity, index) in actList" :key="activity.id">
|
||||
<div class="item" @click="go(activity)">
|
||||
<div class="img-box">
|
||||
<img :src="activity.cover || 'https://www.ncu.edu.cn/__local/8/52/2C/644986B4C9A030F7B65300178A6_8679CB08_18467.jpg'"
|
||||
alt="">
|
||||
<span class="status">
|
||||
|
||||
<div v-if="actList && actList.length > 0" class="activity-swiper-container">
|
||||
<div class="activity-swiper-container">
|
||||
<!-- 活动数据 - Swiper -->
|
||||
<div class="swiper activity-swiper">
|
||||
<div class="swiper-wrapper">
|
||||
<div class="swiper-slide" v-for="(activity, index) in actList" :key="activity.id">
|
||||
<div class="item" @click="go(activity)">
|
||||
<div class="img-box">
|
||||
<img :src="activity.cover || 'https://www.ncu.edu.cn/__local/8/52/2C/644986B4C9A030F7B65300178A6_8679CB08_18467.jpg'"
|
||||
alt="">
|
||||
<span class="status">
|
||||
{{getStatusText(activity)}}
|
||||
</span>
|
||||
</div>
|
||||
<h4 class="title">
|
||||
{{ activity.name}}
|
||||
</h4>
|
||||
<!--报名时间-->
|
||||
<div class="time">
|
||||
<p>
|
||||
<i class="fa fa-clock-o"></i>
|
||||
时间:{{ activity.startDate }}至{{ activity.endDate }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 导航按钮 -->
|
||||
<div class="swiper-button-next"></div>
|
||||
<div class="swiper-button-prev"></div>
|
||||
|
||||
<!-- 进度条 -->
|
||||
<div class="swiper-pagination"></div>
|
||||
</div>
|
||||
</div>
|
||||
<h4 class="title">
|
||||
{{ activity.name}}
|
||||
</h4>
|
||||
<!--报名时间-->
|
||||
<div class="time">
|
||||
<p>
|
||||
<i class="fa fa-clock-o"></i>
|
||||
时间:{{ activity.startDate }}至{{ activity.endDate }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 导航按钮 -->
|
||||
<div class="swiper-button-next"></div>
|
||||
<div class="swiper-button-prev"></div>
|
||||
|
||||
<!-- 进度条 -->
|
||||
<div class="swiper-pagination"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="no-activity-placeholder">
|
||||
<div class="icon">📅</div>
|
||||
<p>暂无近期活动</p>
|
||||
<p class="subtext">敬请关注后续通知</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div v-else class="no-activity-placeholder">
|
||||
<div class="icon">📅</div>
|
||||
<p>暂无近期活动</p>
|
||||
<p class="subtext">敬请关注后续通知</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
@@ -77,8 +77,8 @@ const act = {
|
||||
/*初始化Swiper*/
|
||||
initSwiper() {
|
||||
this.swiper = new Swiper('.activity-swiper', {
|
||||
slidesPerView: 'auto',
|
||||
spaceBetween: 30,
|
||||
slidesPerView: 1,
|
||||
spaceBetween: 18,
|
||||
centeredSlides: false,
|
||||
loop: false,
|
||||
navigation: {
|
||||
@@ -91,13 +91,13 @@ const act = {
|
||||
},
|
||||
breakpoints: {
|
||||
768: {
|
||||
slidesPerView: 1,
|
||||
},
|
||||
1024: {
|
||||
slidesPerView: 2,
|
||||
},
|
||||
1024: {
|
||||
slidesPerView: 4,
|
||||
},
|
||||
1200: {
|
||||
slidesPerView: 3,
|
||||
slidesPerView: 5,
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -119,6 +119,11 @@ const act = {
|
||||
this.$message.warning("管理员未配置活动链接")
|
||||
return
|
||||
}
|
||||
if (act.url === "/platform/activity/apply"
|
||||
&& !this.$auth.hasRoleOr(["SYSADMIN", "SCHOOL_UNION_ADMIN", "BRANCH_UNION_CHAIRMAN", "BRANCH_UNION_WENTI_SPORTS"])) {
|
||||
return this.$message.warning("PC端暂未开放报名,请前往移动端报名!")
|
||||
|
||||
}
|
||||
window.open(act.url)
|
||||
}
|
||||
},
|
||||
@@ -126,7 +131,7 @@ const act = {
|
||||
this.listAct()
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
.section-wrapper {
|
||||
.activity-section-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -171,7 +176,7 @@ const act = {
|
||||
.activity-swiper-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
padding: 9px 14px;
|
||||
/*margin-top: 30px;*/
|
||||
}
|
||||
|
||||
@@ -212,7 +217,7 @@ const act = {
|
||||
/* 进度条样式 */
|
||||
.activity-swiper-container .swiper-pagination {
|
||||
position: relative;
|
||||
margin-top: 30px;
|
||||
margin-top: 12px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
@@ -234,8 +239,8 @@ const act = {
|
||||
|
||||
.activity-swiper .swiper-slide .item .img-box {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
border-radius: 12px;
|
||||
height: 128px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
@@ -245,10 +250,12 @@ const act = {
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: #c11623;
|
||||
padding: 6px 2px;
|
||||
min-width: 50px;
|
||||
padding: 4px 2px;
|
||||
min-width: 42px;
|
||||
color: #ffffff;
|
||||
border-radius: 0 0 50% 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
|
||||
@@ -264,16 +271,16 @@ const act = {
|
||||
}
|
||||
|
||||
.activity-swiper .swiper-slide .item h4 {
|
||||
font-size: 20px;
|
||||
margin-top: 10px;
|
||||
height: 60px;
|
||||
font-size: 15px;
|
||||
margin: 8px 0 0;
|
||||
height: 42px;
|
||||
color: #333;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1.5;
|
||||
line-height: 1.4;
|
||||
text-align: left;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
@@ -287,14 +294,14 @@ const act = {
|
||||
}
|
||||
|
||||
.activity-swiper .swiper-slide .item .time p {
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
/*line-height: 1.5;*/
|
||||
line-height: 1.4;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.activity-swiper .swiper-slide .item .time i {
|
||||
margin-right: 5px;
|
||||
margin-right: 4px;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,39 +1,38 @@
|
||||
const entry = {
|
||||
template: /*language=HTML*/ `
|
||||
<div class="entry-wrapper">
|
||||
<!-- 标签页导航 -->
|
||||
<div class="tabs">
|
||||
<div class="tab-item" :class="{ active: activeCategory === 'serv' }"
|
||||
@click="setActiveCategory('serv')">
|
||||
<i class="fa fa-star"></i> 推荐服务
|
||||
</div>
|
||||
<div class="tab-item" :class="{ active: activeCategory === 'app' }"
|
||||
@click="setActiveCategory('app')">
|
||||
<i class="fa fa-fire"></i> 推荐应用
|
||||
</div>
|
||||
<div class="tab-item" :class="{ active: activeCategory === 'fav' }"
|
||||
@click="setActiveCategory('fav')">
|
||||
<i class="fa fa-heart"></i> 我的收藏
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 应用块区域 -->
|
||||
<div class="apps-container">
|
||||
<div class="app-grid">
|
||||
<div class="app-item" v-for="(item, index) in currentServices" :key="index"
|
||||
@click="openService(item)">
|
||||
<div class="app-icon">
|
||||
<img :src="item.picIcon" alt="" />
|
||||
</div>
|
||||
<div class="app-name">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="entry-wrapper">
|
||||
<div :class="['entry-section', 'entry-section-' + section.key]" v-for="section in entrySections" :key="section.key">
|
||||
<div class="entry-section-header">
|
||||
<div class="entry-section-title">
|
||||
<i :class="section.icon"></i>
|
||||
<span>{{ section.title }}</span>
|
||||
<em>/Applications</em>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="apps-container">
|
||||
<div class="app-grid">
|
||||
<div class="app-item"
|
||||
v-for="(item, index) in section.list"
|
||||
:key="item.id || index"
|
||||
@click="openService(item, section.key)">
|
||||
<div class="app-icon">
|
||||
<img :src="item.picIcon" alt="" />
|
||||
</div>
|
||||
<div class="app-name">{{ item.name }}</div>
|
||||
</div>
|
||||
<el-empty class="app-empty"
|
||||
v-if="!section.list || section.list.length === 0"
|
||||
description="暂无数据"
|
||||
:image-size="72">
|
||||
</el-empty>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
activeCategory: 'serv',
|
||||
hasMore: true,
|
||||
entries: {
|
||||
serv: [],
|
||||
@@ -43,8 +42,12 @@ const entry = {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentServices() {
|
||||
return this.entries[this.activeCategory] || [];
|
||||
entrySections() {
|
||||
return [
|
||||
{ key: 'serv', title: '推荐服务', icon: 'fa fa-star', list: this.entries.serv },
|
||||
{ key: 'app', title: '推荐应用', icon: 'fa fa-fire', list: this.entries.app },
|
||||
{ key: 'fav', title: '我的收藏', icon: 'fa fa-heart', list: this.entries.fav }
|
||||
];
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -53,10 +56,6 @@ const entry = {
|
||||
this.getFavorite()
|
||||
},
|
||||
methods: {
|
||||
setActiveCategory(category) {
|
||||
this.activeCategory = category;
|
||||
},
|
||||
|
||||
// 查询推荐服务
|
||||
async getRecommendService() {
|
||||
this.$axios.post('/platform/home/listRecommendService', {platform: "PC"}).then(res => {
|
||||
@@ -84,35 +83,37 @@ const entry = {
|
||||
})
|
||||
},
|
||||
|
||||
// 点击服务
|
||||
openService(service) {
|
||||
if(this.activeCategory === 'serv'){
|
||||
if(!service.href) this.$message.error('无效的链接地址')
|
||||
// 点击服务或应用
|
||||
openService(service, category) {
|
||||
if (category === 'serv') {
|
||||
if (!service.href) {
|
||||
this.$message.error('无效的链接地址')
|
||||
return
|
||||
}
|
||||
window.open(service.href)
|
||||
return
|
||||
}
|
||||
|
||||
if(this.activeCategory === 'app'){
|
||||
// 储存到缓存
|
||||
if (category === 'app') {
|
||||
window.sessionStorage.setItem("zhgh_sub_app", JSON.stringify(service))
|
||||
// 新标签页打开
|
||||
window.open("/platform/v4/subApp?appId=" + service.id, "_blank")
|
||||
return
|
||||
}
|
||||
|
||||
if(this.activeCategory === 'fav'){
|
||||
if(!service.href) this.$message.error('无效的链接地址')
|
||||
window.open(service.href)
|
||||
return
|
||||
|
||||
if(!service.parentId){
|
||||
// 储存到缓存
|
||||
window.sessionStorage.setItem("zhgh_sub_app", JSON.stringify(app))
|
||||
// 新标签页打开
|
||||
window.open("/platform/v4/subApp?appId=" + app.id, "_blank")
|
||||
if (category === 'fav') {
|
||||
if (service.href) {
|
||||
window.open(service.href)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!service.parentId) {
|
||||
window.sessionStorage.setItem("zhgh_sub_app", JSON.stringify(service))
|
||||
window.open("/platform/v4/subApp?appId=" + service.id, "_blank")
|
||||
return
|
||||
}
|
||||
|
||||
this.$message.error('无效的链接地址')
|
||||
}
|
||||
}
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
@@ -121,37 +122,41 @@ const entry = {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
margin-top: 5px;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
border-bottom: 2px solid #e8e8e8;
|
||||
padding-bottom: 10px;
|
||||
.entry-section + .entry-section {
|
||||
margin-top: 22px;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
/* 临时屏蔽“我的收藏”分组,保留原节点和数据逻辑便于恢复 */
|
||||
.entry-section-fav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.entry-section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.entry-section-title {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 6px;
|
||||
color: #19324d;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.entry-section-title i {
|
||||
color: #409eff;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.entry-section-title em {
|
||||
color: #b5c0d6;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
padding-bottom: 8px;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.tab-item:hover {
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.tab-item.active {
|
||||
color: #1890ff;
|
||||
font-weight: bold;
|
||||
border-bottom: 2px solid #1890ff;
|
||||
}
|
||||
|
||||
.tab-item i {
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.apps-container {
|
||||
@@ -162,19 +167,17 @@ const entry = {
|
||||
|
||||
.app-grid {
|
||||
display: grid;
|
||||
/* 改为自适应列数:每格最小 80px,自动换行 */
|
||||
grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));
|
||||
|
||||
justify-items: center; /* 子项内容居中(图标和文字) */
|
||||
justify-content: start; /* 整体网格靠左对齐,避免居中 */
|
||||
|
||||
gap: 4px;
|
||||
padding: 12px 4px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
|
||||
justify-items: center;
|
||||
justify-content: start;
|
||||
gap: 12px 18px;
|
||||
min-height: 116px;
|
||||
padding: 18px 20px;
|
||||
background: #fff;
|
||||
border: 1px solid #edf1f7;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
|
||||
overflow-x: hidden;
|
||||
|
||||
max-height: calc(350px - 74px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.app-item {
|
||||
@@ -187,6 +190,7 @@ const entry = {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 6px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
@@ -208,6 +212,7 @@ const entry = {
|
||||
}
|
||||
|
||||
.app-name {
|
||||
min-height: 34px;
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
@@ -215,31 +220,26 @@ const entry = {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
.app-empty {
|
||||
grid-column: 1 / -1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.tabs {
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
padding: 8px 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.app-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 15px;
|
||||
grid-template-columns: repeat(3, minmax(72px, 1fr));
|
||||
gap: 12px;
|
||||
padding: 14px 10px;
|
||||
}
|
||||
|
||||
.app-item {
|
||||
padding: 12px;
|
||||
padding: 8px 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.app-grid {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-columns: repeat(2, minmax(72px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ layout("/layouts/v4/baseLayout.html"){
|
||||
|
||||
.section-banner .banner-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.section-banner .banner-img img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.section-wrapper {
|
||||
@@ -28,7 +28,7 @@ layout("/layouts/v4/baseLayout.html"){
|
||||
width: 80%;
|
||||
margin: 20px auto;
|
||||
display: grid;
|
||||
grid-template-columns: 2.2fr 0.8fr;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 24px;
|
||||
align-items: stretch;
|
||||
row-gap: 24px;
|
||||
@@ -38,16 +38,16 @@ layout("/layouts/v4/baseLayout.html"){
|
||||
display: flex;
|
||||
/*align-items: center;*/
|
||||
min-width: 320px;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||||
padding: 16px;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.entry-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(0,0,0,0.12);
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.act-card {
|
||||
@@ -55,43 +55,89 @@ layout("/layouts/v4/baseLayout.html"){
|
||||
align-items: center;
|
||||
min-width: 320px;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
|
||||
padding: 11px;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.home-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.home-section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.home-section-title {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 6px;
|
||||
color: #19324d;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.home-section-title i {
|
||||
color: #409eff;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.home-section-title em {
|
||||
color: #b5c0d6;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="v4-container" id="v4-home-app">
|
||||
<!-- 首页banner -->
|
||||
<div class="section-banner">
|
||||
<div class="banner-img">
|
||||
<img src="${config.AppHomeImg!}" alt=""/>
|
||||
<img :src="homeBannerList[0] || ''" alt=""/>
|
||||
<stats></stats>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
<div class="" style="padding-bottom: 20px">
|
||||
<user></user>
|
||||
|
||||
<div class="home-grid">
|
||||
<div class="act-card">
|
||||
<act></act>
|
||||
</div>
|
||||
|
||||
<div class="entry-card">
|
||||
<entry></entry>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<jcdt :list="websiteNews.grassroots"></jcdt>
|
||||
<div class="home-grid">
|
||||
<div class="home-section">
|
||||
<div class="home-section-header">
|
||||
<div class="home-section-title">
|
||||
<i class="fa fa-calendar"></i>
|
||||
<span>最新活动</span>
|
||||
<em>/Activities</em>
|
||||
</div>
|
||||
</div>
|
||||
<div class="act-card">
|
||||
<act></act>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<work-template v-if="$auth.hasRoleOr(['SYSADMIN','SCHOOL_UNION_ADMIN'])"></work-template>
|
||||
|
||||
<jcdt :list="websiteNews"></jcdt>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include("act.js"){}#-->
|
||||
<!--#include("template.js"){}#-->
|
||||
<!--#include("jcdt.js"){}#-->
|
||||
<!--#include("entry.js"){}#-->
|
||||
<!--#include("user.js"){}#-->
|
||||
@@ -102,13 +148,13 @@ layout("/layouts/v4/baseLayout.html"){
|
||||
el: '#v4-home-app',
|
||||
data(){
|
||||
return{
|
||||
websiteNews:{
|
||||
grassroots:[],
|
||||
}
|
||||
websiteNews: [],
|
||||
homeBannerList: "${config.AppHomeImg!}".split(",").map(item => item.trim()).filter(item => item)
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'act': act,
|
||||
'work-template': workTemplate,
|
||||
'jcdt': jcdt,
|
||||
'entry': entry,
|
||||
'user': user,
|
||||
|
||||
@@ -2,22 +2,29 @@ const jcdt = {
|
||||
template: /*language=HTML*/ `
|
||||
<div class="jcdt-wrapper">
|
||||
<div class="jcdt-container">
|
||||
<!-- <div class="jcdt-title">-->
|
||||
<!-- <span>基层动态</span>-->
|
||||
<!-- </div> -->
|
||||
<div class="jcdt-content">
|
||||
<div class="jcdt-list">
|
||||
<div class="jcdt-item" v-for="item in list" :key="item.title" @click="onLink(item)">
|
||||
<div class="date" v-if="item.date">{{item.date}}</div>
|
||||
<div class="title">{{item.title}}</div>
|
||||
<div class="image">
|
||||
<img v-if="item.image" :src="item.image" alt="">
|
||||
<img v-else src="/assets/platform/img/v4/not-image.png" alt="">
|
||||
</div>
|
||||
<div class="summary">{{item.summary}}</div>
|
||||
<el-carousel height="360px" :interval="10000" indicator-position="none">
|
||||
<el-carousel-item v-for="item,index in list" :key="index">
|
||||
<div class="jcdt-title">
|
||||
<span>{{ item.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="jcdt-content">
|
||||
<div class="jcdt-list">
|
||||
<div class="jcdt-item" v-for="o,i in item.value" :key="i" @click="onLink(o)">
|
||||
<div class="date" v-if="o.time">
|
||||
<i class="el-icon-alarm-clock"></i>
|
||||
<span>{{o.time}}</span>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center">
|
||||
<div>
|
||||
|
||||
</div>
|
||||
<div class="title">{{o.text}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
@@ -32,9 +39,12 @@ const jcdt = {
|
||||
},
|
||||
methods: {
|
||||
onLink(item) {
|
||||
if(!item.url) return
|
||||
window.open(item.url)
|
||||
const url = this.isHttpOrHttps(item.href) ? item.href : "https://xgh.cug.edu.cn/" + item.href
|
||||
window.open(url)
|
||||
},
|
||||
isHttpOrHttps(url) {
|
||||
return /^(http:\/\/|https:\/\/)/i.test(url)
|
||||
}
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
.jcdt-wrapper {
|
||||
@@ -47,16 +57,14 @@ const jcdt = {
|
||||
.jcdt-container {
|
||||
width: 80%;
|
||||
max-width: 80%;
|
||||
margin: 0 auto;
|
||||
|
||||
margin: 0 auto 10px;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin-bottom: 10px;
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.jcdt-title{
|
||||
/deep/ .jcdt-title{
|
||||
font-size: 30px;
|
||||
color: #000000;
|
||||
display: block;
|
||||
@@ -87,14 +95,14 @@ const jcdt = {
|
||||
margin-left: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.jcdt-list {
|
||||
|
||||
/deep/ .jcdt-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.jcdt-item {
|
||||
/deep/ .jcdt-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 16px;
|
||||
@@ -103,18 +111,17 @@ const jcdt = {
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
height: auto;
|
||||
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.jcdt-item:hover{
|
||||
background: #ffffff;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
border-bottom-color: var(--color-primary);
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.jcdt-item .date {
|
||||
/deep/ .jcdt-item:hover{
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
/deep/ .jcdt-item .date {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
@@ -123,19 +130,19 @@ const jcdt = {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.jcdt-item .title {
|
||||
/deep/ .jcdt-item .title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 12px;
|
||||
/*margin-bottom: 12px;*/
|
||||
line-height: 1.4;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
height: 45px;
|
||||
/*height: 45px;*/
|
||||
}
|
||||
|
||||
.jcdt-item .image {
|
||||
/deep/ .jcdt-item .image {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
margin: 0 0 16px 0;
|
||||
@@ -149,14 +156,14 @@ const jcdt = {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.jcdt-item .image img {
|
||||
/deep/ .jcdt-item .image img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.jcdt-item .summary {
|
||||
/deep/ .jcdt-item .summary {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
@@ -168,5 +175,8 @@ const jcdt = {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
/deep/ .jcdt-item i {
|
||||
color: #007aff;
|
||||
}
|
||||
`
|
||||
};
|
||||
|
||||
@@ -7,66 +7,58 @@ const stats = {
|
||||
<div class="stats-grid">
|
||||
<!-- 待办 -->
|
||||
<div
|
||||
class="stat-item pending"
|
||||
@click="handleStatClick('todo')"
|
||||
@mouseenter="onMouseEnter"
|
||||
@mouseleave="onMouseLeave"
|
||||
class="stat-item pending"
|
||||
@click="handleStatClick('todo')"
|
||||
>
|
||||
<div class="stat-icon">
|
||||
<i class="fa fa-clock-o"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-label">待办</div>
|
||||
<div class="stat-number">{{ stats.todoCount }}</div>
|
||||
</div>
|
||||
<div class="stat-icon">
|
||||
<i class="fa fa-clock-o"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 已办 -->
|
||||
<div
|
||||
class="stat-item completed"
|
||||
@click="handleStatClick('done')"
|
||||
@mouseenter="onMouseEnter"
|
||||
@mouseleave="onMouseLeave"
|
||||
class="stat-item completed"
|
||||
@click="handleStatClick('done')"
|
||||
>
|
||||
<div class="stat-icon">
|
||||
<i class="fa fa-check-circle"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-label">已办</div>
|
||||
<div class="stat-number">{{ stats.doneCount }}</div>
|
||||
</div>
|
||||
<div class="stat-icon">
|
||||
<i class="fa fa-check-circle"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 消息 -->
|
||||
<div
|
||||
class="stat-item messages"
|
||||
@click="handleStatClick('notification')"
|
||||
@mouseenter="onMouseEnter"
|
||||
@mouseleave="onMouseLeave"
|
||||
class="stat-item messages"
|
||||
@click="handleStatClick('notification')"
|
||||
>
|
||||
<div class="stat-icon">
|
||||
<i class="fa fa-bell"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-label">消息</div>
|
||||
<div class="stat-number">{{ stats.notifications }}</div>
|
||||
</div>
|
||||
<div class="stat-icon">
|
||||
<i class="fa fa-bell"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 发起 -->
|
||||
<div
|
||||
class="stat-item reminders"
|
||||
@click="handleStatClick('started')"
|
||||
@mouseenter="onMouseEnter"
|
||||
@mouseleave="onMouseLeave"
|
||||
class="stat-item reminders"
|
||||
@click="handleStatClick('started')"
|
||||
>
|
||||
<div class="stat-icon">
|
||||
<i class="fa fa-file-text"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-label">发起</div>
|
||||
<div class="stat-number">{{ stats.startedCount }}</div>
|
||||
</div>
|
||||
<div class="stat-icon">
|
||||
<i class="fa fa-file-text"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -90,15 +82,6 @@ const stats = {
|
||||
}
|
||||
},
|
||||
|
||||
onMouseEnter(event) {
|
||||
event.target.style.transform = 'translateY(-2px)';
|
||||
event.target.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)';
|
||||
},
|
||||
onMouseLeave(event) {
|
||||
event.target.style.transform = 'none';
|
||||
event.target.style.boxShadow = '0 2px 6px rgba(0,0,0,0.08)';
|
||||
},
|
||||
|
||||
loadStats() {
|
||||
this.$axios.post("/flow/todoCenter/statistics").then(res => {
|
||||
if (res.code === 0) {
|
||||
@@ -122,90 +105,128 @@ const stats = {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: -130px auto 0;
|
||||
margin: 20px auto 0;
|
||||
/*border: 1px solid #e8e8e8;*/
|
||||
}
|
||||
|
||||
/* 子内容临时屏蔽时,同步收起统计容器,避免首页 banner 下方出现空白 */
|
||||
.stats-section {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16px;
|
||||
width: 32%;
|
||||
min-width: 360px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* 临时屏蔽首页右上角“待办、已办、消息、发起”统计卡片,保留原模板便于恢复 */
|
||||
.stats-grid {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
background: #f8f9fa;
|
||||
color: #495057;
|
||||
padding: 14px;
|
||||
border-radius: 8px;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
color: #1f2937;
|
||||
padding: 20px;
|
||||
border-radius: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.08);
|
||||
width: 200px;
|
||||
transition: background 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
|
||||
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.12);
|
||||
width: 100%;
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
.stat-item:hover {
|
||||
background: rgba(255, 255, 255, 0.62);
|
||||
border-color: rgba(255, 255, 255, 0.55);
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 16px 36px rgba(15, 23, 42, 0.18);
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
font-size: 30px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: #e6f7ff;
|
||||
color: #1890ff;
|
||||
border-radius: 50%;
|
||||
font-size: 26px;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background: rgba(255, 255, 255, 0.42);
|
||||
color: rgba(31, 41, 55, 0.72);
|
||||
border: 1px solid rgba(255, 255, 255, 0.35);
|
||||
border-radius: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.stat-item.pending .stat-icon,
|
||||
.stat-item.completed .stat-icon,
|
||||
.stat-item.messages .stat-icon,
|
||||
.stat-item.reminders .stat-icon {
|
||||
background: #e6f7ff;
|
||||
color: #1890ff;
|
||||
background: rgba(255, 255, 255, 0.42);
|
||||
}
|
||||
|
||||
.stat-item.pending .stat-icon {
|
||||
color: #2563eb;
|
||||
}
|
||||
|
||||
.stat-item.completed .stat-icon {
|
||||
color: #52c41a;
|
||||
color: #16a34a;
|
||||
}
|
||||
|
||||
.stat-item.messages .stat-icon {
|
||||
color: #faad14;
|
||||
color: #d97706;
|
||||
}
|
||||
|
||||
.stat-item.reminders .stat-icon {
|
||||
color: #13c2c2;
|
||||
color: #0891b2;
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 10px;
|
||||
margin-left: 0;
|
||||
min-width: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 15px;
|
||||
opacity: 0.7;
|
||||
font-size: 14px;
|
||||
color: rgba(31, 41, 55, 0.78);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
letter-spacing: 1px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-top: 2px;
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
margin-top: 6px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.stats-family {
|
||||
width: 68%;
|
||||
}
|
||||
|
||||
|
||||
.stats-family img {
|
||||
height: 190px !important;
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
/* 临时屏蔽“欢迎来到职工之家”图片,保留原节点便于恢复 */
|
||||
.stats-family {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
};
|
||||
|
||||
@@ -68,10 +68,10 @@ const user = {
|
||||
<div class="header">
|
||||
<h3>待办中心</h3>
|
||||
<el-tabs v-model="activeTab" type="card" @tab-click="handleTabClick">
|
||||
<el-tab-pane label="待办事宜" name="todo"></el-tab-pane>
|
||||
<el-tab-pane label="已办事宜" name="done"></el-tab-pane>
|
||||
<el-tab-pane label="我的发起" name="started"></el-tab-pane>
|
||||
<el-tab-pane label="办结事务" name="completed"></el-tab-pane>
|
||||
<el-tab-pane :label="tabLabel('待办事宜', taskStats.todoCount)" name="todo"></el-tab-pane>
|
||||
<el-tab-pane :label="tabLabel('已办事宜', taskStats.doneCount)" name="done"></el-tab-pane>
|
||||
<el-tab-pane :label="tabLabel('我的发起', taskStats.startedCount)" name="started"></el-tab-pane>
|
||||
<el-tab-pane :label="tabLabel('办结事务', taskStats.completedCount)" name="completed"></el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
||||
@@ -97,6 +97,7 @@ const user = {
|
||||
<el-table-column prop="sender" label="申请人" width="120">
|
||||
<template slot-scope="{row}">{{row.variable.initiatorName}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="taskName" label="当前流程节点" width="140" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="date" label="发起日期" width="120">
|
||||
<template slot-scope="{row}">
|
||||
{{$moment(row.createdAt).format('YYYY-MM-DD')}}
|
||||
@@ -108,6 +109,7 @@ const user = {
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -123,6 +125,12 @@ const user = {
|
||||
pageSize: 5,
|
||||
totalCount: 0
|
||||
},
|
||||
taskStats: {
|
||||
todoCount: 0,
|
||||
doneCount: 0,
|
||||
startedCount: 0,
|
||||
completedCount: 0
|
||||
},
|
||||
// 🔆 天气相关数据
|
||||
weatherData: null,
|
||||
weatherError: false,
|
||||
@@ -181,9 +189,10 @@ const user = {
|
||||
|
||||
return info;
|
||||
},
|
||||
async getConfigKey(key) {
|
||||
const resp = await this.$axios.post("/open/common/getConfigKey", { key })
|
||||
return resp.data
|
||||
getConfigKey(key) {
|
||||
return this.$axios.post("/open/common/getConfigKey", { key }).then((resp) => {
|
||||
return resp.data
|
||||
})
|
||||
},
|
||||
// 🔆 获取天气
|
||||
async fetchWeather() {
|
||||
@@ -191,6 +200,9 @@ const user = {
|
||||
// 存localStorage 112.13,32.01 广州 113.27,23.14
|
||||
const AppMapCenterPointX = await this.getConfigKey("AppMapCenterPointX")
|
||||
const AppMapCenterPointY = await this.getConfigKey("AppMapCenterPointY")
|
||||
if (!AppMapCenterPointX || !AppMapCenterPointY){
|
||||
return
|
||||
}
|
||||
|
||||
// 1. 先通过城市名获取 locationId
|
||||
this.$axios.get("https://mx5rk62kvj.re.qweatherapi.com/geo/v2/city/lookup?location=" + AppMapCenterPointX + "," + AppMapCenterPointY + "&key=" + this.QWEATHER_KEY).then((geoRes) => {
|
||||
@@ -224,6 +236,19 @@ const user = {
|
||||
}
|
||||
},
|
||||
// 获取任务列表
|
||||
tabLabel(title, count) {
|
||||
return title + "(" + (count || 0) + ")"
|
||||
},
|
||||
async getStatistics() {
|
||||
try {
|
||||
const res = await $.post("/flow/todoCenter/statistics")
|
||||
if (res.code === 0) {
|
||||
this.taskStats = Object.assign({}, this.taskStats, res.data)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取待办统计失败:", error)
|
||||
}
|
||||
},
|
||||
async getTasks() {
|
||||
this.loading = true
|
||||
try {
|
||||
@@ -241,6 +266,7 @@ const user = {
|
||||
},
|
||||
pageData() {
|
||||
this.getTasks()
|
||||
this.getStatistics()
|
||||
},
|
||||
handleTabClick(tab) {
|
||||
this.pageData()
|
||||
@@ -254,7 +280,7 @@ const user = {
|
||||
this.$message.warning("当前流程没有配置地址")
|
||||
return
|
||||
}
|
||||
window.open(formKey + "?taskId=" + taskId + "bizId=" + businessNo + "taskKey=" + taskKey)
|
||||
window.open(formKey + "?taskId=" + taskId + "&bizId=" + businessNo + "&taskKey=" + taskKey + "&tab=" + this.activeTab)
|
||||
},
|
||||
getRoleNames() {
|
||||
$.post("/platform/sys/role/getRoleNames").then(res => {
|
||||
@@ -280,7 +306,7 @@ const user = {
|
||||
|
||||
.user-container {
|
||||
width: 80%;
|
||||
margin: 20px auto;
|
||||
margin: 12px auto 20px;
|
||||
display: grid;
|
||||
grid-template-columns: 0.7fr 2.3fr;
|
||||
gap: 24px;
|
||||
|
||||
@@ -333,7 +333,7 @@ layout("/layouts/v4/baseLayout.html"){
|
||||
methods: {
|
||||
async loadStats() {
|
||||
try {
|
||||
const resp = await this.$axios.get('/platform/v4/msg/stats')
|
||||
const resp = await Promise.resolve(this.$axios.get('/platform/v4/msg/stats'))
|
||||
if (resp.code === 0) {
|
||||
this.stats = resp.data
|
||||
}
|
||||
@@ -345,7 +345,7 @@ layout("/layouts/v4/baseLayout.html"){
|
||||
async loadMessages() {
|
||||
this.loading = true
|
||||
try {
|
||||
const resp = await this.$axios.post('/platform/v4/msg/pageData', this.pageForm)
|
||||
const resp = await Promise.resolve(this.$axios.post('/platform/v4/msg/pageData', this.pageForm))
|
||||
if (resp.code === 0) {
|
||||
this.messages = resp.data.list
|
||||
this.pageForm.totalCount = resp.data.totalCount
|
||||
@@ -360,7 +360,7 @@ layout("/layouts/v4/baseLayout.html"){
|
||||
},
|
||||
|
||||
async viewMessage(message) {
|
||||
this.$axios.post(`/platform/v4/msg/detail/` + message.id).then(async res => {
|
||||
this.$axios.post('/platform/v4/msg/detail/' + message.id).then(async res => {
|
||||
if (res.code === 0) {
|
||||
this.currentMessage = res.data
|
||||
this.detailVisible = true
|
||||
@@ -376,7 +376,7 @@ layout("/layouts/v4/baseLayout.html"){
|
||||
|
||||
async markAsRead(messageId) {
|
||||
try {
|
||||
await this.$axios.post(`/platform/v4/msg/read/` + messageId)
|
||||
await Promise.resolve(this.$axios.post('/platform/v4/msg/read/' + messageId))
|
||||
} catch (error) {
|
||||
console.error('标记已读失败:', error)
|
||||
}
|
||||
@@ -390,7 +390,7 @@ layout("/layouts/v4/baseLayout.html"){
|
||||
}).then(async () => {
|
||||
this.loading = true
|
||||
try {
|
||||
const resp = await this.$axios.post('/platform/v4/msg/read/all')
|
||||
const resp = await Promise.resolve(this.$axios.post('/platform/v4/msg/read/all'))
|
||||
if (resp.code === 0) {
|
||||
this.$message.success('操作成功')
|
||||
this.loadMessages()
|
||||
|
||||
Reference in New Issue
Block a user