first commit
This commit is contained in:
@@ -0,0 +1,386 @@
|
||||
const act = {
|
||||
template: /*language=HTML*/ `
|
||||
<!-- 活动 -->
|
||||
<div class="activity-section-wrapper">
|
||||
<div class="section-act">
|
||||
<!-- Swiper容器 -->
|
||||
|
||||
<div v-if="actList && actList.length > 0" class="activity-swiper-container">
|
||||
<!-- 活动数据 - Swiper -->
|
||||
<div ref="activitySwiper" 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 ref="nextButton" class="swiper-button-next"></div>
|
||||
<div ref="prevButton" class="swiper-button-prev"></div>
|
||||
|
||||
<!-- 进度条 -->
|
||||
<div ref="pagination" class="swiper-pagination"></div>
|
||||
</div>
|
||||
|
||||
<div v-else class="no-activity-placeholder">
|
||||
<div class="icon">📅</div>
|
||||
<p>暂无近期活动</p>
|
||||
<p class="subtext">敬请关注后续通知</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
actList: [],
|
||||
swiper: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/*查询活动*/
|
||||
listAct() {
|
||||
this.$axios.post("/platform/home/listHomeActivity").then((res) => {
|
||||
if (res.code === 0) {
|
||||
// for (let i = 0; i < 10; i++) {
|
||||
// this.actList = this.actList.concat(res.data)
|
||||
// }
|
||||
this.actList = res.data
|
||||
// 数据加载完成后初始化Swiper
|
||||
this.$nextTick(() => {
|
||||
this.initSwiper()
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
/*初始化Swiper*/
|
||||
initSwiper() {
|
||||
if (this.swiper) {
|
||||
this.swiper.destroy(true, true)
|
||||
this.swiper = null
|
||||
}
|
||||
const swiperElement = this.$refs.activitySwiper
|
||||
if (!swiperElement) {
|
||||
return
|
||||
}
|
||||
// 仅操作当前组件的轮播节点,避免页面其他轮播或尺寸观察器影响卡片宽度。
|
||||
this.swiper = new Swiper(swiperElement, {
|
||||
slidesPerView: 1,
|
||||
spaceBetween: 18,
|
||||
centeredSlides: false,
|
||||
loop: false,
|
||||
watchOverflow: true,
|
||||
observer: false,
|
||||
observeParents: false,
|
||||
resizeObserver: false,
|
||||
// 鼠标停留在活动区域时可横向浏览,到首尾后释放页面滚动。
|
||||
mousewheel: {
|
||||
enabled: true,
|
||||
forceToAxis: false,
|
||||
releaseOnEdges: true,
|
||||
sensitivity: 0.8,
|
||||
},
|
||||
navigation: {
|
||||
nextEl: this.$refs.nextButton,
|
||||
prevEl: this.$refs.prevButton,
|
||||
},
|
||||
pagination: {
|
||||
el: this.$refs.pagination,
|
||||
type: 'progressbar',
|
||||
},
|
||||
breakpoints: {
|
||||
768: {
|
||||
slidesPerView: 2,
|
||||
},
|
||||
1024: {
|
||||
slidesPerView: 4,
|
||||
},
|
||||
1200: {
|
||||
slidesPerView: 5,
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 活动状态
|
||||
getStatusText(item) {
|
||||
if (this.$moment().unix() > this.$moment(item.endDate).unix()) {
|
||||
return "已结束"
|
||||
} else if (this.$moment().unix() < this.$moment(item.startDate).unix()) {
|
||||
return "未开始"
|
||||
} else {
|
||||
return "进行中"
|
||||
}
|
||||
},
|
||||
|
||||
go(act) {
|
||||
if (!act.url) {
|
||||
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)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.listAct()
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.swiper) {
|
||||
this.swiper.destroy(true, true)
|
||||
this.swiper = null
|
||||
}
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
.activity-section-wrapper {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.section-act {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.section-act .section-act-title span {
|
||||
font-size: 30px;
|
||||
color: #000000;
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
/*.section-act .section-act-title span:hover {
|
||||
color: var(--color-primary);
|
||||
cursor: pointer;
|
||||
}*/
|
||||
|
||||
/deep/ .section-act .section-act-title span::before {
|
||||
content: '';
|
||||
width: 24px;
|
||||
height: 11px;
|
||||
background: url(https://www.ncu.edu.cn/images/titl.svg) no-repeat center;
|
||||
background-size: 24px 11px;
|
||||
display: inline-block;
|
||||
margin-right: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/deep/ .section-act .section-act-title span::after {
|
||||
content: '';
|
||||
width: 24px;
|
||||
height: 11px;
|
||||
background: url(https://www.ncu.edu.cn/images/titr.svg) no-repeat center;
|
||||
background-size: 24px 11px;
|
||||
display: inline-block;
|
||||
margin-left: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Swiper容器样式 */
|
||||
.activity-swiper-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 9px 54px 12px;
|
||||
/*margin-top: 30px;*/
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.activity-swiper {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
/*padding: 0 50px 20px 50px;*/
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.activity-swiper .swiper-slide {
|
||||
height: auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* 导航按钮样式 */
|
||||
.activity-swiper-container .swiper-button-next,
|
||||
.activity-swiper-container .swiper-button-prev {
|
||||
color: var(--color-primary);
|
||||
background: white;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
top: 72px;
|
||||
margin-top: 0;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.activity-swiper-container .swiper-button-next:hover,
|
||||
.activity-swiper-container .swiper-button-prev:hover {
|
||||
background: var(--color-primary);
|
||||
color: white;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/deep/ .activity-swiper-container .swiper-button-next::after,
|
||||
/deep/ .activity-swiper-container .swiper-button-prev::after {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 进度条不展示,活动通过左右箭头或区域内滚轮浏览。 */
|
||||
.activity-swiper-container .swiper-pagination {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.activity-swiper-container .swiper-pagination-progressbar {
|
||||
background: #e0e0e0;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.activity-swiper-container .swiper-pagination-progressbar-fill {
|
||||
background: var(--color-primary);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.activity-swiper .swiper-slide .item {
|
||||
height: auto;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.activity-swiper .swiper-slide .item .img-box {
|
||||
width: 100%;
|
||||
height: 128px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.activity-swiper .swiper-slide .item .img-box .status {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: #c11623;
|
||||
padding: 4px 2px;
|
||||
min-width: 42px;
|
||||
color: #ffffff;
|
||||
border-radius: 0 0 50% 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
|
||||
.activity-swiper .swiper-slide .item .img-box img {
|
||||
display: block;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
max-width: 100% !important;
|
||||
max-height: 128px !important;
|
||||
object-fit: cover !important;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.activity-swiper .swiper-slide .item:hover .img-box img {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.activity-swiper .swiper-slide .item h4 {
|
||||
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.4;
|
||||
text-align: left;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.activity-swiper .swiper-slide .item:hover h4 {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.activity-swiper .swiper-slide .item .time {
|
||||
/*margin-top: 8px;*/
|
||||
}
|
||||
|
||||
.activity-swiper .swiper-slide .item .time p {
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
line-height: 1.4;
|
||||
color: #666;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.activity-swiper .swiper-slide .item .time i {
|
||||
margin-right: 4px;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
|
||||
/* 无活动占位提示 */
|
||||
.no-activity-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
color: #94a3b8;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.no-activity-placeholder .icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 12px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.no-activity-placeholder p {
|
||||
margin: 4px 0 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.no-activity-placeholder .subtext {
|
||||
font-size: 13px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
`
|
||||
};
|
||||
@@ -0,0 +1,274 @@
|
||||
const entry = {
|
||||
template: /*language=HTML*/ `
|
||||
<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 v-if="item.picIcon" class="app-icon-img" :src="item.picIcon" alt="" />
|
||||
<i v-else class="fa fa-cube app-icon-fallback"></i>
|
||||
</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 {
|
||||
hasMore: true,
|
||||
entries: {
|
||||
serv: [],
|
||||
app: [],
|
||||
fav: []
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
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() {
|
||||
this.getRecommendService()
|
||||
this.getRecommendApp()
|
||||
this.getFavorite()
|
||||
},
|
||||
methods: {
|
||||
// 查询推荐服务
|
||||
async getRecommendService() {
|
||||
this.$axios.post('/platform/home/listRecommendService', {platform: "PC"}).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.entries.serv = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 获取推荐应用
|
||||
async getRecommendApp() {
|
||||
this.$axios.post('/platform/home/listRecommendApp', {platform: "PC"}).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.entries.app = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 获取我的收藏
|
||||
async getFavorite() {
|
||||
this.$axios.post('/platform/home/listFavorite', {platform: "PC"}).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.entries.fav = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 点击服务或应用
|
||||
openService(service, category) {
|
||||
if (category === 'serv') {
|
||||
if (!service.href) {
|
||||
this.$message.error('无效的链接地址')
|
||||
return
|
||||
}
|
||||
window.open(service.href)
|
||||
return
|
||||
}
|
||||
|
||||
if (category === 'app') {
|
||||
window.sessionStorage.setItem("zhgh_sub_app", JSON.stringify(service))
|
||||
window.open("/platform/v4/subApp?appId=" + service.id, "_blank")
|
||||
return
|
||||
}
|
||||
|
||||
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*/ `
|
||||
.entry-wrapper {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.entry-section + .entry-section {
|
||||
margin-top: 22px;
|
||||
}
|
||||
|
||||
/* 临时屏蔽“我的收藏”分组,保留原节点和数据逻辑便于恢复 */
|
||||
.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;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.apps-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.app-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(108px, 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;
|
||||
}
|
||||
|
||||
.app-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
max-width: 108px;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
padding: 6px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.app-item:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.app-icon {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 0 0 56px;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
background: #f5f8ff;
|
||||
}
|
||||
|
||||
.app-icon img,
|
||||
.app-icon-img {
|
||||
display: block;
|
||||
width: 56px !important;
|
||||
height: 56px !important;
|
||||
max-width: 56px !important;
|
||||
max-height: 56px !important;
|
||||
object-fit: contain !important;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.app-icon-fallback {
|
||||
color: #409eff;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.app-name {
|
||||
min-height: 34px;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
word-break: break-word;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.app-empty {
|
||||
grid-column: 1 / -1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.app-grid {
|
||||
grid-template-columns: repeat(3, minmax(72px, 1fr));
|
||||
gap: 12px;
|
||||
padding: 14px 10px;
|
||||
}
|
||||
|
||||
.app-item {
|
||||
padding: 8px 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.app-grid {
|
||||
grid-template-columns: repeat(2, minmax(72px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
`
|
||||
};
|
||||
@@ -0,0 +1,204 @@
|
||||
<!--#
|
||||
layout("/layouts/v4/baseLayout.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
|
||||
.section-banner {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.section-banner .banner-img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.section-banner .banner-img img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.section-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.home-grid {
|
||||
width: 80%;
|
||||
margin: 20px auto;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 24px;
|
||||
align-items: stretch;
|
||||
row-gap: 24px;
|
||||
}
|
||||
|
||||
.entry-card {
|
||||
display: flex;
|
||||
/*align-items: center;*/
|
||||
min-width: 320px;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.entry-card:hover {
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.act-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
|
||||
padding: 22px 11px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.home-section {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
/* 推荐入口的 scoped 样式会在组件挂载后注入,此处提前限制图片尺寸,避免刷新时原图短暂撑开页面。 */
|
||||
#v4-home-app .entry-card .app-icon {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#v4-home-app .entry-card .app-icon img {
|
||||
display: block;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
max-width: 56px;
|
||||
max-height: 56px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="v4-container" id="v4-home-app">
|
||||
<!-- 首页banner -->
|
||||
<div class="section-banner">
|
||||
<div class="banner-img">
|
||||
<img :src="homeBannerList[0] || ''" alt=""/>
|
||||
<stats></stats>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="" style="padding-bottom: 20px">
|
||||
<user></user>
|
||||
|
||||
<div class="home-grid">
|
||||
<div class="entry-card">
|
||||
<entry></entry>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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 nonce="${cspNonce!}">
|
||||
<!--#include("act.js"){}#-->
|
||||
<!--#include("template.js"){}#-->
|
||||
<!--#include("jcdt.js"){}#-->
|
||||
<!--#include("entry.js"){}#-->
|
||||
<!--#include("user.js"){}#-->
|
||||
|
||||
<!--#include("stats.js"){}#-->
|
||||
|
||||
new Vue({
|
||||
el: '#v4-home-app',
|
||||
data(){
|
||||
return{
|
||||
websiteNews: [],
|
||||
homeBannerList: "${config.AppHomeImg!}".split(",").map(item => item.trim()).filter(item => item)
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'act': act,
|
||||
'work-template': workTemplate,
|
||||
'jcdt': jcdt,
|
||||
'entry': entry,
|
||||
'user': user,
|
||||
'stats': stats,
|
||||
},
|
||||
mounted() {
|
||||
this.getWebSiteNews()
|
||||
},
|
||||
methods: {
|
||||
getWebSiteNews(){
|
||||
this.$axios.post('/platform/home/getNews').then(res => {
|
||||
if (res.code === 0) {
|
||||
this.websiteNews = res.data
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,182 @@
|
||||
const jcdt = {
|
||||
template: /*language=HTML*/ `
|
||||
<div class="jcdt-wrapper">
|
||||
<div class="jcdt-container">
|
||||
<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 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>
|
||||
`,
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
props: {
|
||||
list: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onLink(item) {
|
||||
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 {
|
||||
width: 100%;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.jcdt-container {
|
||||
width: 80%;
|
||||
max-width: 80%;
|
||||
margin: 0 auto 10px;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
/deep/ .jcdt-title{
|
||||
font-size: 30px;
|
||||
color: #000000;
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
/deep/ .jcdt-container .jcdt-title span::before {
|
||||
content: '';
|
||||
width: 24px;
|
||||
height: 11px;
|
||||
background: url(https://www.ncu.edu.cn/images/titl.svg) no-repeat center;
|
||||
background-size: 24px 11px;
|
||||
display: inline-block;
|
||||
margin-right: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/deep/ .jcdt-container .jcdt-title span::after {
|
||||
content: '';
|
||||
width: 24px;
|
||||
height: 11px;
|
||||
background: url(https://www.ncu.edu.cn/images/titr.svg) no-repeat center;
|
||||
background-size: 24px 11px;
|
||||
display: inline-block;
|
||||
margin-left: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/deep/ .jcdt-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
/deep/ .jcdt-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 16px;
|
||||
border: none;
|
||||
border-bottom: 3px solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
height: auto;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
border-bottom-color: var(--color-primary);
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
/deep/ .jcdt-item:hover{
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
/deep/ .jcdt-item .date {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
margin-bottom: 8px;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
/deep/ .jcdt-item .title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
/*margin-bottom: 12px;*/
|
||||
line-height: 1.4;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
/*height: 45px;*/
|
||||
}
|
||||
|
||||
/deep/ .jcdt-item .image {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
margin: 0 0 16px 0;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
background: #f5f5f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
/deep/ .jcdt-item .image img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
/deep/ .jcdt-item .summary {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-align: justify;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
/deep/ .jcdt-item i {
|
||||
color: #007aff;
|
||||
}
|
||||
`
|
||||
};
|
||||
@@ -0,0 +1,232 @@
|
||||
const stats = {
|
||||
template: /*language=HTML*/ `
|
||||
<div class="stats-section">
|
||||
<div class="stats-family">
|
||||
<img src="/assets/platform/img/home/family.png"/>
|
||||
</div>
|
||||
<div class="stats-grid">
|
||||
<!-- 待办 -->
|
||||
<div
|
||||
class="stat-item pending"
|
||||
@click="handleStatClick('todo')"
|
||||
>
|
||||
<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')"
|
||||
>
|
||||
<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')"
|
||||
>
|
||||
<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')"
|
||||
>
|
||||
<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>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
stats: {
|
||||
todoCount: 0,
|
||||
doneCount: 0,
|
||||
notifications: 0,
|
||||
startedCount: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleStatClick(type) {
|
||||
if (['todo', 'done', 'started'].includes(type)) {
|
||||
window.open('/flow/todoCenter?status=' + type)
|
||||
} else if (type === 'notification') {
|
||||
window.open('/platform/v4/msg')
|
||||
}
|
||||
},
|
||||
|
||||
loadStats() {
|
||||
this.$axios.post("/flow/todoCenter/statistics").then(res => {
|
||||
if (res.code === 0) {
|
||||
this.stats = res.data;
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadStats();
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
/* 右侧统计区域 */
|
||||
.stats-section {
|
||||
/*background: white;*/
|
||||
color: #333;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
z-index: 10;
|
||||
width: 80%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
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: 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;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
cursor: pointer;
|
||||
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: 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: rgba(255, 255, 255, 0.42);
|
||||
}
|
||||
|
||||
.stat-item.pending .stat-icon {
|
||||
color: #2563eb;
|
||||
}
|
||||
|
||||
.stat-item.completed .stat-icon {
|
||||
color: #16a34a;
|
||||
}
|
||||
|
||||
.stat-item.messages .stat-icon {
|
||||
color: #d97706;
|
||||
}
|
||||
|
||||
.stat-item.reminders .stat-icon {
|
||||
color: #0891b2;
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 0;
|
||||
min-width: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: rgba(31, 41, 55, 0.78);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
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;
|
||||
}
|
||||
`
|
||||
};
|
||||
@@ -0,0 +1,214 @@
|
||||
const workTemplate = {
|
||||
template: /*language=HTML*/ `
|
||||
<div class="section-wrapper work-template-wrapper">
|
||||
<div class="work-template-header">
|
||||
<div class="work-template-title">
|
||||
<i class="fa fa-clipboard"></i>
|
||||
<span>工作模板</span>
|
||||
<em>/Templates</em>
|
||||
</div>
|
||||
</div>
|
||||
<div class="work-template-card">
|
||||
<div v-if="templateList && templateList.length > 0" class="work-template-grid">
|
||||
<div class="work-template-item"
|
||||
v-for="template in templateList"
|
||||
:key="template.id"
|
||||
@click="go(template)">
|
||||
<div class="work-template-icon">
|
||||
<img :src="template.templateIcon || template.cover || 'https://www.ncu.edu.cn/__local/8/52/2C/644986B4C9A030F7B65300178A6_8679CB08_18467.jpg'"
|
||||
alt="">
|
||||
</div>
|
||||
<div class="work-template-name">{{ template.templateName || template.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="no-activity-placeholder">
|
||||
<div class="icon"><i class="fa fa-clipboard"></i></div>
|
||||
<p>暂无工作模板</p>
|
||||
<p class="subtext">敬请关注后续更新</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
templateList: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
listTemplate() {
|
||||
this.$axios.post("/platform/home/listHomeTemplate").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.templateList = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
go(template) {
|
||||
if (!template.url) {
|
||||
this.$message.warning("管理员未配置模板链接")
|
||||
return
|
||||
}
|
||||
const url = new URL(template.url, window.location.origin)
|
||||
if (!url.searchParams.get("mode")) {
|
||||
url.searchParams.set("mode", "edit")
|
||||
}
|
||||
if (!url.searchParams.get("id") && template.id) {
|
||||
url.searchParams.set("id", template.id)
|
||||
}
|
||||
window.localStorage.setItem("zhgh_home_template_edit", JSON.stringify({
|
||||
path: url.pathname,
|
||||
id: url.searchParams.get("id"),
|
||||
expiresAt: Date.now() + 60000
|
||||
}))
|
||||
window.open(url.href)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.listTemplate()
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
.work-template-wrapper {
|
||||
width: 80%;
|
||||
margin: 20px auto 28px;
|
||||
}
|
||||
|
||||
.work-template-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.work-template-title {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 6px;
|
||||
color: #19324d;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.work-template-title i {
|
||||
color: #409eff;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.work-template-title em {
|
||||
color: #b5c0d6;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.work-template-card {
|
||||
background: #ffffff;
|
||||
border: 1px solid #edf1f7;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
|
||||
min-height: 116px;
|
||||
padding: 18px 20px;
|
||||
}
|
||||
|
||||
.work-template-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
|
||||
justify-items: center;
|
||||
justify-content: start;
|
||||
gap: 12px 18px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.work-template-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 6px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.work-template-item:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.work-template-icon {
|
||||
width: 55px;
|
||||
height: 55px;
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.work-template-icon img {
|
||||
width: 55px !important;
|
||||
height: 55px !important;
|
||||
max-width: none;
|
||||
max-height: none;
|
||||
object-fit: cover;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.work-template-name {
|
||||
min-height: 34px;
|
||||
font-size: 12px !important;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
word-break: break-word;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.work-template-wrapper .no-activity-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
color: #94a3b8;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.work-template-wrapper .no-activity-placeholder .icon {
|
||||
font-size: 44px;
|
||||
margin-bottom: 12px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.work-template-wrapper .no-activity-placeholder p {
|
||||
margin: 4px 0 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.work-template-wrapper .no-activity-placeholder .subtext {
|
||||
font-size: 13px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.work-template-grid {
|
||||
grid-template-columns: repeat(3, minmax(72px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.work-template-item {
|
||||
padding: 8px 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.work-template-grid {
|
||||
grid-template-columns: repeat(2, minmax(72px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
`
|
||||
};
|
||||
@@ -0,0 +1,553 @@
|
||||
const user = {
|
||||
template: /*language=HTML*/ `
|
||||
<div class="user-wrapper">
|
||||
<!-- 用户容器:左右布局 -->
|
||||
<div class="user-container">
|
||||
<!-- 左侧:用户信息卡片 -->
|
||||
<div class="user-info-card">
|
||||
<div class="user-details">
|
||||
<div class="user-name">${@auth.getPrincipalProperty('username')}</div>
|
||||
|
||||
<div class="user-meta-grid">
|
||||
<div class="meta-row">
|
||||
<span class="meta-icon">🆔</span>
|
||||
<div>
|
||||
<span class="meta-label">工号</span>
|
||||
<span class="meta-value">${@auth.getPrincipalProperty('loginname')}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="meta-row">
|
||||
<span class="meta-icon">🏢</span>
|
||||
<div>
|
||||
<span class="meta-label">单位信息</span>
|
||||
<span class="meta-value">
|
||||
<!--#if(!isEmpty(@auth.getPrincipalProperty('unit'))){#-->
|
||||
${@auth.getPrincipalProperty('unit').getName()}
|
||||
<!--#}#-->
|
||||
(
|
||||
<!--#if(!isEmpty(@auth.getPrincipalProperty('union'))){#-->
|
||||
${@auth.getPrincipalProperty('union').getName()}
|
||||
<!--#}#-->
|
||||
)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="meta-row">
|
||||
<span class="meta-icon">🎭</span>
|
||||
<div>
|
||||
<span class="meta-label">角色</span>
|
||||
<span class="meta-value">{{ roleNames || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 天气区域 -->
|
||||
<div v-if="weatherData" class="weather-section" :class="weatherData.bgClass">
|
||||
<div style="display: flex; justify-content: space-around">
|
||||
<div class="weather-header">
|
||||
<span class="weather-location">{{ weatherData.name }}</span>
|
||||
<span class="weather-text">{{ weatherData.text }}</span>
|
||||
</div>
|
||||
<div class="weather-main">
|
||||
<span class="weather-emoji">{{ weatherData.emoji }}</span>
|
||||
<span class="weather-temp">{{ weatherData.temp }}°</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="weather-tip">{{ weatherData.tip }}</div>
|
||||
</div>
|
||||
<div v-else-if="weatherError" class="weather-error">
|
||||
🌤️ 天气服务暂时不可用
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:待办中心 -->
|
||||
<div class="todo-center">
|
||||
<!-- 标题和标签 -->
|
||||
<div class="header">
|
||||
<h3>待办中心</h3>
|
||||
<el-tabs v-model="activeTab" type="card" @tab-click="handleTabClick">
|
||||
<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>
|
||||
|
||||
<!-- 表格 -->
|
||||
<el-table
|
||||
:data="todoList"
|
||||
style="width: 100%"
|
||||
border
|
||||
stripe
|
||||
v-loading="loading"
|
||||
:cell-style="{ padding: '8px' }"
|
||||
:header-cell-style="{ background: '#f8f9fa', color: '#444', fontWeight: '500' }"
|
||||
>
|
||||
<el-table-column label="序号" type="index" :index="indexMethod" width="100px"></el-table-column>
|
||||
<el-table-column prop="categoryName" label="流程分类" width="100"></el-table-column>
|
||||
<el-table-column prop="title" label="流程名称" min-width="300">
|
||||
<template slot-scope="scope">
|
||||
<el-tooltip effect="dark" :content="scope.row.instanceName" placement="top">
|
||||
<span class="table-title">{{ scope.row.instanceName }}</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<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')}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="100px" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="primary" size="mini" @click="openView(scope.row)">查看</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'todo',
|
||||
todoList: [],
|
||||
loading: false,
|
||||
pageForm: {
|
||||
pageNum: 1,
|
||||
pageSize: 5,
|
||||
totalCount: 0
|
||||
},
|
||||
taskStats: {
|
||||
todoCount: 0,
|
||||
doneCount: 0,
|
||||
startedCount: 0,
|
||||
completedCount: 0
|
||||
},
|
||||
// 🔆 天气相关数据
|
||||
weatherData: null,
|
||||
weatherError: false,
|
||||
// 🔑 和风天气 API Key(请替换为你自己的)
|
||||
QWEATHER_KEY: 'f916578cbb0e4b55b778b0de5847ecf5', // ←← 务必替换!
|
||||
roleNames: ""
|
||||
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getWeatherInfo(iconCode, temp) {
|
||||
const info = {
|
||||
emoji: '🌤️',
|
||||
tip: '今日天气宜人,心情也该放晴啦!',
|
||||
bgClass: 'weather-sunny' // 用于 CSS 类
|
||||
};
|
||||
|
||||
const tempNum = parseFloat(temp);
|
||||
const isCold = tempNum < 10;
|
||||
const isHot = tempNum > 28;
|
||||
|
||||
// 按照和风天气 iconCode 分类
|
||||
if (['100'].includes(iconCode)) {
|
||||
info.emoji = '☀️';
|
||||
info.tip = isHot ? '高温预警!注意防暑降温,多喝水~' : '阳光灿烂,适合外出走走!';
|
||||
info.bgClass = 'weather-sunny';
|
||||
} else if (['101', '102', '103', '104'].includes(iconCode)) {
|
||||
info.emoji = ['101', '103'].includes(iconCode) ? '⛅' : '☁️';
|
||||
info.tip = isCold ? '天凉加衣,别让感冒找上门!' : '云层温柔,适合静心工作。';
|
||||
info.bgClass = 'weather-cloudy';
|
||||
} else if (['300', '301', '302', '305', '306', '399'].some(code => code === iconCode)) {
|
||||
info.emoji = '🌧️';
|
||||
info.tip = '🌧️ 下雨啦!记得带伞,小心路滑~';
|
||||
info.bgClass = 'weather-rainy';
|
||||
} else if (['307', '308', '309', '310'].includes(iconCode)) {
|
||||
info.emoji = '⛈️';
|
||||
info.tip = '⛈️ 雷雨天气,请减少外出,注意安全!';
|
||||
info.bgClass = 'weather-storm';
|
||||
} else if (['400', '401', '402', '403'].includes(iconCode)) {
|
||||
info.emoji = '❄️';
|
||||
info.tip = '❄️ 天寒地冻,保暖最重要!';
|
||||
info.bgClass = 'weather-snowy';
|
||||
} else if (['404', '405', '406', '407', '408', '409', '410'].includes(iconCode)) {
|
||||
info.emoji = '🌫️';
|
||||
info.tip = '🌫️ 能见度较低,出行请注意安全。';
|
||||
info.bgClass = 'weather-foggy';
|
||||
} else if (['500', '501', '502', '504'].includes(iconCode)) {
|
||||
info.emoji = '🌀';
|
||||
info.tip = '🌀 极端天气预警!请关注官方通知。';
|
||||
info.bgClass = 'weather-extreme';
|
||||
} else {
|
||||
info.emoji = '🌤️';
|
||||
info.tip = '今日天气未知,但愿你心情晴朗!';
|
||||
info.bgClass = 'weather-default';
|
||||
}
|
||||
|
||||
return info;
|
||||
},
|
||||
async getConfigKey(key) {
|
||||
const resp = await this.$axios.post("/open/common/getConfigKey", { key })
|
||||
return resp.data
|
||||
},
|
||||
// 🔆 获取天气
|
||||
async fetchWeather() {
|
||||
try {
|
||||
// 存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) => {
|
||||
if (geoRes.code !== '200' || !geoRes.location?.[0]) {
|
||||
console.log('城市未找到')
|
||||
}
|
||||
const locationId = geoRes.location[0].id;
|
||||
|
||||
// 2. 获取当前天气
|
||||
this.$axios.get("https://mx5rk62kvj.re.qweatherapi.com/v7/weather/now?location=" + locationId + "&key=" + this.QWEATHER_KEY).then((weatherRes) => {
|
||||
if (weatherRes.code !== '200') {
|
||||
console.warn('天气数据异常:', weatherRes);
|
||||
}
|
||||
const weatherInfo = this.getWeatherInfo(weatherRes.now.icon, weatherRes.now.temp);
|
||||
this.weatherData = {
|
||||
...weatherInfo,
|
||||
temp: weatherRes.now.temp,
|
||||
text: weatherRes.now.text,
|
||||
name: geoRes.location[0].name
|
||||
};
|
||||
this.weatherError = false;
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
|
||||
} catch (err) {
|
||||
console.warn('天气加载失败:', err);
|
||||
this.weatherError = true;
|
||||
this.weatherData = null;
|
||||
}
|
||||
},
|
||||
// 获取任务列表
|
||||
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 {
|
||||
const res = await $.post("/flow/todoCenter/" + this.activeTab, this.pageForm)
|
||||
if (res.code === 0) {
|
||||
this.todoList = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.error("获取任务列表失败")
|
||||
console.error("获取任务列表失败:", error)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
pageData() {
|
||||
this.getTasks()
|
||||
this.getStatistics()
|
||||
},
|
||||
handleTabClick(tab) {
|
||||
this.pageData()
|
||||
console.log('切换到:', tab.name);
|
||||
},
|
||||
// 处理任务
|
||||
async openView(task) {
|
||||
console.log(task)
|
||||
const {taskId, taskKey, taskState, instanceId, businessNo, formKey} = task
|
||||
if (!formKey) {
|
||||
this.$message.warning("当前流程没有配置地址")
|
||||
return
|
||||
}
|
||||
window.open(formKey + "?taskId=" + taskId + "&bizId=" + businessNo + "&taskKey=" + taskKey + "&tab=" + this.activeTab)
|
||||
},
|
||||
getRoleNames() {
|
||||
$.post("/platform/sys/role/getRoleNames").then(res => {
|
||||
if (res.code === 0) {
|
||||
this.roleNames = res.data.roleNames
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
this.getRoleNames()
|
||||
},
|
||||
mounted() {
|
||||
this.fetchWeather()
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
.user-wrapper {
|
||||
width: 100%;
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.user-container {
|
||||
width: 80%;
|
||||
margin: 12px auto 20px;
|
||||
display: grid;
|
||||
grid-template-columns: 0.7fr 2.3fr;
|
||||
gap: 24px;
|
||||
align-items: stretch;
|
||||
row-gap: 24px;
|
||||
}
|
||||
|
||||
.user-info-card {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.06);
|
||||
padding: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
transition: transform 0.25s ease, box-shadow 0.25s ease;
|
||||
}
|
||||
|
||||
.user-info-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 头像占位符 */
|
||||
.user-avatar-placeholder {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #409eff, #1877f2);
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
margin: 0 auto;
|
||||
box-shadow: 0 4px 10px rgba(24, 127, 255, 0.3);
|
||||
}
|
||||
|
||||
/* 用户名 */
|
||||
.user-name {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #1f2937;
|
||||
text-align: center;
|
||||
margin: 8px 0 0;
|
||||
}
|
||||
|
||||
/* 元信息网格 */
|
||||
.user-meta-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.meta-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.meta-icon {
|
||||
font-size: 18px;
|
||||
min-width: 24px;
|
||||
text-align: center;
|
||||
color: #409eff;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.meta-row > div {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.meta-label {
|
||||
font-size: 12px;
|
||||
color: #6b7280;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.meta-value {
|
||||
font-size: 15px;
|
||||
color: #1f2937;
|
||||
font-weight: 600;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* === 天气卡片 - 情感化设计 === */
|
||||
.weather-section {
|
||||
border-radius: 16px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
background: linear-gradient(135deg, #a8edea, #fed6e3); /* 默认柔和渐变 */
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
/* 不同天气的背景主题 */
|
||||
.weather-sunny {
|
||||
background: linear-gradient(135deg, #FFD700, #FFA500);
|
||||
color: #2c3e50;
|
||||
}
|
||||
.weather-cloudy {
|
||||
background: linear-gradient(135deg, #B0C4DE, #708090);
|
||||
color: white;
|
||||
}
|
||||
.weather-rainy {
|
||||
background: linear-gradient(135deg, #4A6FA5, #1E3F66);
|
||||
color: white;
|
||||
}
|
||||
.weather-storm {
|
||||
background: linear-gradient(135deg, #2F2F2F, #0D0D0D);
|
||||
color: #FFD700;
|
||||
}
|
||||
.weather-snowy {
|
||||
background: linear-gradient(135deg, #E0F7FA, #B3E5FC);
|
||||
color: #01579B;
|
||||
}
|
||||
.weather-foggy {
|
||||
background: linear-gradient(135deg, #D7CCC8, #A1887F);
|
||||
color: white;
|
||||
}
|
||||
.weather-extreme {
|
||||
background: linear-gradient(135deg, #FF5252, #B71C1C);
|
||||
color: white;
|
||||
}
|
||||
.weather-default {
|
||||
background: linear-gradient(135deg, #E0E0E0, #90A4AE);
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 天气头部:城市 + 描述 */
|
||||
.weather-header {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.weather-location {
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
display: block;
|
||||
}
|
||||
.weather-text {
|
||||
font-size: 13px;
|
||||
opacity: 0.9;
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* 主体:图标 + 温度 */
|
||||
.weather-main {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
position: relative;
|
||||
bottom: 8px;
|
||||
}
|
||||
.weather-emoji {
|
||||
font-size: 36px;
|
||||
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.2));
|
||||
}
|
||||
.weather-temp {
|
||||
font-size: 28px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
/* 贴心提示语 */
|
||||
.weather-tip {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
opacity: 0.95;
|
||||
margin-top: 8px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
/* 错误状态 */
|
||||
.weather-error {
|
||||
text-align: center;
|
||||
color: #94a3b8;
|
||||
font-size: 14px;
|
||||
padding: 16px;
|
||||
background: #f8fafc;
|
||||
border-radius: 12px;
|
||||
border: 1px dashed #cbd5e1;
|
||||
}
|
||||
|
||||
/* 右侧待办中心 */
|
||||
.todo-center {
|
||||
min-width: 320px;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
padding: 16px;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.header h3 {
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
border-left: 4px solid #1890ff;
|
||||
padding-left: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.el-tabs {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.table-title {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 280px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* 响应式:小屏下堆叠 */
|
||||
@media (max-width: 768px) {
|
||||
.user-container {
|
||||
flex-direction: column;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.user-info-card, .todo-center {
|
||||
min-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
`
|
||||
};
|
||||
Reference in New Issue
Block a user