This commit is contained in:
那些花儿
2025-09-17 17:52:09 +08:00
parent 9821aebd0a
commit 3f40ee599e
59 changed files with 3697 additions and 4029 deletions
+102 -50
View File
@@ -112,6 +112,7 @@ layout("/layouts/v4/baseLayout.html"){
.content-body {
min-height: 400px;
height: 100%;
}
@media (max-width: 768px) {
@@ -137,7 +138,8 @@ layout("/layouts/v4/baseLayout.html"){
</div>
<el-scrollbar>
<el-menu unique-opened :default-active="activeMenuIndex" :default-openeds="openedMenus" @select="menuSelect">
<el-menu unique-opened :default-active="activeMenuIndex" :default-openeds="openedMenus"
@select="menuSelect">
<menu-list :menus="leftMenus" :depth="0"></menu-list>
</el-menu>
</el-scrollbar>
@@ -145,11 +147,6 @@ layout("/layouts/v4/baseLayout.html"){
<!-- 右侧主体区域 -->
<main class="sub-app-container-main-content" id="sub-app-container-main-content">
<!-- <header class="content-header">-->
<!-- <h1 class="content-title"></h1>-->
<!-- &lt;!&ndash; <p class="content-subtitle"></p>&ndash;&gt;-->
<!-- </header>-->
<div class="content-body" id="sub-app-container-main-content-body">${layoutContent!}</div>
</main>
@@ -169,7 +166,7 @@ layout("/layouts/v4/baseLayout.html"){
})
$(document).on("pjax:send", function () {
NProgress.configure({ parent: ".sub-app-container-main-content" })
NProgress.configure({parent: ".sub-app-container-main-content"})
NProgress.start()
$("#sub-app-container-main-content").hide()
})
@@ -190,7 +187,8 @@ layout("/layouts/v4/baseLayout.html"){
try {
const app = JSON.parse(window.sessionStorage.getItem("zhgh_sub_app"))
$("#sub-app-container #sidebar-menu .menu-header .menu-title").text(app?.name)
} catch (e) {}
} catch (e) {
}
}
document.addEventListener("DOMContentLoaded", function () {
@@ -209,36 +207,41 @@ layout("/layouts/v4/baseLayout.html"){
"menu-list": {
name: "menu-list",
template:
/*language=HTML*/
`
<div>
<template v-for="(item, index) in menus">
<el-submenu :index="item.id" v-if="item.children && item.children.length > 0">
<template #title>
<i :class="item.icon" v-if="item.icon" :size="18"></i>
<i v-else class="el-icon-paperclip"></i>
<span>{{ item.name }}</span>
</template>
<menu-list :menus="item.children" :depth="depth + 1" />
</el-submenu>
<el-menu-item :index="item.id" v-else>
<a :href="item.href" data-pjax>
<i :class="item.icon" v-if="item.icon" :size="18"></i>
<i v-else :class="item.icon ? item.icon : 'el-icon-menu'"></i>
<span>{{ item.name }}</span>
</a>
</el-menu-item>
</template>
</div>
`,
/*language=HTML*/
`
<div>
<template v-for="(item, index) in menus">
<el-submenu :index="item.id" v-if="item.children && item.children.length > 0">
<template #title>
<i :class="item.icon" v-if="item.icon" :size="18"></i>
<i v-else class="el-icon-paperclip"></i>
<span>{{ item.name }}</span>
</template>
<menu-list :menus="item.children" :depth="depth + 1"/>
</el-submenu>
<el-menu-item :index="item.id" v-else>
<a :href="item.href" data-pjax>
<i :class="item.icon" v-if="item.icon" :size="18"></i>
<i v-else :class="item.icon ? item.icon : 'el-icon-menu'"></i>
<span>{{ item.name }}</span>
</a>
</el-menu-item>
</template>
</div>
`,
props: ["menus", "depth"]
}
},
data() {
return {
// 菜单数据
menus: [],
// 当前激活的菜单索引
activeMenuIndex: null,
openedMenus: []
// 当前打开的菜单索引
openedMenus: [],
// 默认选中菜单是否加载中
defaultSelectLoading: true
}
},
computed: {
@@ -260,12 +263,13 @@ layout("/layouts/v4/baseLayout.html"){
const app = JSON.parse(window.sessionStorage.getItem("zhgh_sub_app"))
window.sessionStorage.setItem("zhgh_sub_app_left_menu_active_index-" + app.id, index)
window.sessionStorage.setItem("zhgh_sub_app_left_menu_opens-" + app.id, JSON.stringify(indexPath))
} catch (e) {}
} catch (e) {
}
},
// 获取菜单
getMenus(appId) {
if (!appId) return
$.get("/platform/sys/user/subAppMenus", { appId }).then((res) => {
this.$axios.post("/platform/sys/user/subAppMenus", {appId}).then((res) => {
if (res.code === 0) {
this.menus = res.data
window.sessionStorage.setItem("zhgh_sub_app_menus", JSON.stringify(res.data))
@@ -276,6 +280,7 @@ layout("/layouts/v4/baseLayout.html"){
})
},
// 设置应用信息
setAppInfo(app) {
if (app) {
// 储存到缓存
@@ -294,26 +299,73 @@ layout("/layouts/v4/baseLayout.html"){
this.openedMenus = JSON.parse(window.sessionStorage.getItem("zhgh_sub_app_left_menu_opens-" + app.id)) || []
console.log(this.openedMenus)
console.log(this.activeMenuIndex)
} catch (e) {}
if (!this.activeMenuIndex) {
this.defaultSelect()
}
} catch (e) {
}
},
// 默认选中
defaultSelect() {
// 查找第一个有href的子菜单
function findFirstChildWithHref(menus) {
for (const menu of menus) {
// 如果当前菜单有href,返回它
if (menu.href) {
return {
menu: menu,
parentIds: [menu.parentId]
};
}
// 如果有子菜单,递归查找
if (menu.children && menu.children.length > 0) {
const result = findFirstChildWithHref(menu.children);
if (result) {
// 添加当前菜单的ID到父ID集合
result.parentIds.push(menu.id);
return result;
}
}
}
return null;
}
const result = findFirstChildWithHref(this.menus)
if (result) {
this.activeMenuIndex = result.menu.id
this.openedMenus = result.parentIds
this.$nextTick(() => {
this.menuSelect(this.activeMenuIndex, this.openedMenus)
// 使用pjax跳转
commonUtil.pjaxPush(result.menu.href)
})
}
},
init() {
// 页面加载时获取到正确的菜单
const pathname = window.location.pathname
if (!pathname.startsWith("/platform/v4/subApp")) {
this.$axios.post("/platform/sys/user/rootMenuByPath", {pathname}).then((res) => {
if (res.code === 0) {
this.setAppInfo(res.data)
this.getMenus(res.data?.id)
}
})
} else {
const appId = GetQueryString("appId")
if (!appId) return
this.getMenus(appId)
}
}
},
mounted() {
// 页面加载时获取到正确的菜单
const pathname = window.location.pathname
if (!pathname.startsWith("/platform/v4/subApp")) {
$.get("/platform/sys/user/rootMenuByPath", { pathname }).then((res) => {
if (res.code === 0) {
this.setAppInfo(res.data)
this.getMenus(res.data?.id)
}
})
} else {
const appId = GetQueryString("appId")
if (!appId) {
return
}
this.getMenus(appId)
}
this.init()
}
})
</script>
@@ -165,12 +165,12 @@
} else {
window.location.href = "/platform/h5/home"
}
func && func()
func && typeof func === "function" && func()
}
Vue.mixin({
methods: {
historyBack: function(func) {
historyBack: function(func = ()=>{}) {
historyBack(func);
}
}
@@ -32,8 +32,8 @@
<script src="${base!}/assets/platform/plugins/jquery/jquery.js"></script>
<!-- pjax是异步加载html片段的工具,模拟前端路由机制 -->
<script src="${base!}/assets/platform/plugins/pjax/jquery.pjax.js"></script>
<!--nprogress 配合pjax使用-->
<link ref="stylesheet" href="${base!}/assets/platform/plugins/nprogress/nprogress.css"/>
<!-- nprogress 配合pjax使用 -->
<link rel="stylesheet" href="${base!}/assets/platform/plugins/nprogress/nprogress.css" />
<script src="${base!}/assets/platform/plugins/nprogress/nprogress.js"></script>
<!-- 农历插件 -->
@@ -97,6 +97,8 @@
ELEMENT.Dialog.props.closeOnClickModal.default = false
ELEMENT.Dialog.props.top.default = "50px"
ELEMENT.Table.props.border.default = true
ELEMENT.TableColumn.props.headerAlign.default = 'center'
ELEMENT.TableColumn.props.align.default = 'center'
ELEMENT.TableColumn.props.showOverflowTooltip = { type: Boolean, default: true }
Vue.use(ELEMENT, {
zIndex: 20000
@@ -603,7 +605,7 @@
</div>
<nav class="v4-nav">
<a href="/platform/v4/home2" data-pjax class="v4-nav-item">
<a href="/platform/v4/home" data-pjax class="v4-nav-item">
<i class="fa fa-home"></i>
首页
</a>
@@ -615,10 +617,14 @@
<i class="fa fa-th-large"></i>
服务中心
</a>
<a href="/flow/todoCenter" data-pjax class="v4-nav-item">
<a href="/platform/v4/todo" data-pjax class="v4-nav-item">
<i class="fa fa-th-large"></i>
待办中心
</a>
<a href="/platform/v4/msg" data-pjax class="v4-nav-item">
<i class="fa fa-th-large"></i>
消息中心
</a>
</nav>
</div>
@@ -663,7 +669,7 @@
var currentPath = window.location.pathname
var footer = $("#v4-footer")
if (currentPath === "/platform/v4/home2") {
if (currentPath === "/platform/v4/home") {
footer.show()
} else {
footer.hide()
File diff suppressed because it is too large Load Diff
@@ -1,936 +0,0 @@
<!--#
layout("/layouts/v4/baseLayout.html"){
#-->
<style>
/* 主容器 */
.oa-container {
position: relative;
}
/* 主要内容区域 */
.oa-main-content {
margin: 0 auto;
padding-bottom: 20px;
background: url(/assets/platform/img/v4/home-bg.png);
background-size: cover;
}
/* 背景图和搜索统计区域容器 */
.oa-hero-section {
position: relative;
height: 350px;
overflow: hidden;
}
.oa-background-image {
width: 100%;
height: 100%;
}
.oa-background-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* 搜索和统计区域 */
.oa-search-stats-section {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: grid;
grid-template-columns: 1fr 500px;
gap: 40px;
align-items: center;
width: 1500px;
z-index: 10;
}
/* 搜索区域 */
.oa-search-section {
text-align: left;
border-radius: 20px;
padding: 40px;
}
.oa-search-container {
display: flex;
max-width: 600px;
background: white;
overflow: hidden;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
}
.oa-search-input {
flex: 1;
padding: 15px 25px;
border: none;
outline: none;
font-size: 16px;
background: transparent;
}
.oa-search-btn {
background: #ff6b35;
color: white;
border: none;
padding: 15px 30px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
}
.oa-search-btn:hover {
background: #e55a2b;
}
/* 数字提醒区域 */
.oa-stats-section {
background: rgba(0, 109, 185, 0.7);
padding: 20px 25px;
}
.oa-stats-title {
color: white;
font-size: 20px;
font-weight: 600;
margin: 0 0 15px 0;
text-align: left;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.oa-stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
}
.oa-stat-card {
background: #ffffff;
padding: 20px;
display: flex;
flex-direction: column;
gap: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
border: 1px solid rgba(255, 255, 255, 0.8);
position: relative;
overflow: hidden;
}
.oa-stat-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, #4a90e2, #357abd);
transform: scaleX(0);
transition: transform 0.3s ease;
}
.oa-stat-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
cursor: pointer;
}
.oa-stat-card:hover::before {
transform: scaleX(1);
}
.oa-stat-top {
display: flex;
align-items: center;
gap: 12px;
}
.oa-stat-icon {
font-size: 28px;
color: #4a90e2;
flex-shrink: 0;
transition: all 0.3s ease;
}
.oa-stat-card:hover .oa-stat-icon {
transform: scale(1.1);
color: #357abd;
}
.oa-stat-content {
display: flex;
flex-direction: column;
gap: 4px;
flex: 1;
}
.oa-stat-number {
font-size: 24px;
font-weight: 700;
color: #2c3e50;
line-height: 1;
transition: color 0.3s ease;
}
.oa-stat-card:hover .oa-stat-number {
color: #4a90e2;
}
.oa-stat-label {
font-weight: 500;
line-height: 1;
text-align: left;
font-size: 18px;
}
/* 活动中心 */
.oa-box-1-section {
display: grid;
grid-template-columns: 400px 1fr;
gap: 30px;
border-radius: 6px;
overflow: hidden;
width: 1500px;
margin: 20px auto 0;
}
/* 左侧用户信息 */
.oa-user-panel {
background: rgba(0, 109, 185, 0.9);
color: white;
padding: 25px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
position: relative;
overflow: hidden;
max-height: 410px;
}
.oa-user-panel::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
z-index: 0;
}
.oa-user-panel > * {
position: relative;
z-index: 1;
}
.oa-user-avatar {
width: 90px;
height: 90px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
font-size: 36px;
margin-bottom: 15px;
border: 3px solid rgba(255, 255, 255, 0.3);
transition: all 0.3s ease;
}
.oa-user-avatar:hover {
transform: scale(1.05);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2);
}
.oa-user-name {
font-size: 20px;
font-weight: 600;
margin-bottom: 8px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.oa-user-info {
line-height: 1.6;
margin-bottom: 20px;
background: rgba(255, 255, 255, 0.05);
padding: 15px;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.1);
text-align: left;
width: 100%;
}
.oa-user-info .info-item {
margin-bottom: 8px;
display: flex;
align-items: center;
line-height: 1.6;
width: 100%;
}
.oa-user-panel .info-label {
font-weight: 600;
margin-right: 8px;
flex-shrink: 0;
min-width: 50px;
max-width: 60px;
text-align: left;
}
.oa-user-panel .info-value {
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
min-width: 0;
}
.oa-user-homepage-btn {
background: rgba(255, 255, 255, 0.2);
color: white;
border: 2px solid rgba(255, 255, 255, 0.3);
padding: 10px 20px;
border-radius: 25px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 8px;
}
.oa-user-homepage-btn:hover {
background: rgba(255, 255, 255, 0.3);
border-color: rgba(255, 255, 255, 0.5);
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
color: white;
text-decoration: none;
}
/* 右侧活动列表 */
.oa-activity-content {
padding: 20px;
background: #ffffff;
max-height: 410px;
display: flex;
flex-direction: column;
min-width: 0; /* 允许flex子项收缩 */
overflow: hidden; /* 防止内容溢出 */
}
.oa-activity-header {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #eee;
padding-bottom: 15px;
flex-shrink: 0; /* 防止头部被压缩 */
}
.oa-activity-tabs {
display: flex;
gap: 10px;
}
.oa-activity-tab {
padding: 10px 0;
font-size: 20px;
color: #666;
cursor: pointer;
transition: color 0.3s ease;
position: relative;
background: none;
border: none;
outline: none;
}
.oa-activity-tab:hover {
color: #4a90e2;
}
.oa-activity-tab.active {
color: #4a90e2;
font-weight: 500;
}
.oa-activity-tab.active::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2px;
background: #4a90e2;
border-radius: 1px;
}
/* 活动轮播样式 */
.oa-activity-carousel {
flex: 1;
margin-top: 15px;
}
.oa-activity-carousel .el-carousel,.oa-activity-carousel .el-carousel .el-carousel__container {
height: 100%;
}
.oa-activity-carousel .el-carousel__item {
display: flex;
justify-content: center;
align-items: center;
}
.oa-activity-card {
width: 100%;
background: #fff;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
overflow: hidden;
transition: all 0.3s ease;
height: 100%;
position: relative;
cursor: pointer;
}
.oa-activity-image {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
}
.oa-activity-image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
.oa-activity-card:hover .oa-activity-image img {
transform: scale(1.05);
}
/* 图片上的文字叠加层 */
.oa-activity-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
padding: 40px 20px 20px;
color: white;
}
.oa-activity-title {
font-size: 18px;
font-weight: 600;
color: white;
margin-bottom: 8px;
line-height: 1.4;
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
.oa-activity-meta {
font-size: 14px;
color: white;
margin-bottom: 15px;
line-height: 1.5;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
/* 无活动时的提示区域 */
.oa-no-activity {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
background: #f8f9fa;
border-radius: 12px;
color: #6c757d;
text-align: center;
}
.oa-no-activity-icon {
font-size: 48px;
margin-bottom: 16px;
color: #dee2e6;
}
.oa-no-activity-title {
font-size: 18px;
font-weight: 500;
margin-bottom: 8px;
color: #495057;
}
.oa-no-activity-desc {
font-size: 14px;
color: #6c757d;
}
/* box2 - 系统通知公告 */
.oa-box-2-section {
width: 1500px;
margin: 30px auto 0;
background: #ffffff;
overflow: hidden;
}
.oa-notice-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
color: #000;
}
.oa-notice-header-content {
display: flex;
align-items: center;
gap: 12px;
position: relative;
}
.oa-notice-header-content::after {
content: '';
position: absolute;
bottom: -10px;
left: 0;
right: 0;
height: 2px;
background-color: #000000;
}
.oa-notice-header-icon {
font-size: 24px;
}
.oa-notice-header-title {
font-size: 20px;
font-weight: 600;
margin: 0;
}
.oa-notice-header-more {
flex-shrink: 0;
}
.oa-header-more-btn {
background: transparent;
border: none;
color: #666;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
padding: 6px 12px;
border-radius: 4px;
transition: all 0.3s ease;
}
.oa-header-more-btn:hover {
background: #f5f5f5;
color: #4a90e2;
}
.oa-header-more-btn i {
font-size: 12px;
transition: transform 0.3s ease;
}
.oa-header-more-btn:hover i {
transform: translateX(2px);
}
.oa-notice-content {
padding: 10px;
}
.oa-notice-list {
display: grid;
gap: 15px;
}
.oa-notice-item {
display: flex;
align-items: flex-start;
padding: 15px 20px;
background: #ffffff;
border-bottom: 1px solid #e8e8e8;
transition: all 0.2s ease;
cursor: pointer;
}
.oa-notice-item:hover {
background: #f5f5f5;
}
.oa-notice-item:last-child {
border-bottom: none;
}
.oa-notice-item-icon {
width: 16px;
height: 16px;
color: #666;
margin-right: 12px;
margin-top: 2px;
flex-shrink: 0;
}
.oa-notice-item-content {
flex: 1;
min-width: 0;
}
.oa-notice-item-title {
font-size: 16px;
font-weight: 500;
color: #333;
margin-bottom: 8px;
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
}
.oa-notice-item-desc {
font-size: 14px;
color: #666;
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.oa-notice-item-date {
margin-left: 15px;
flex-shrink: 0;
white-space: nowrap;
font-size: 16px;
}
</style>
<div class="oa-container" id="v4-home-app">
<!-- 顶部导航 -->
<!-- 主要内容 -->
<div class="oa-main-content">
<!-- 背景图和搜索统计区域容器 -->
<div class="oa-hero-section">
<!-- 背景图 -->
<div class="oa-background-image">
<img
src="https://i.cpu.edu.cn/mnews/_upload/article/images/ed/dc/eff0e4b84ee6b2d7e770ed1de071/0f515087-45e3-4dbf-acf4-e7096d6cbdc0.jpg"
alt=""
/>
</div>
<!-- 搜索和统计区域 -->
<div class="oa-search-stats-section">
<!-- 搜索区域 -->
<div class="oa-search-section">
<!-- <h1 class="oa-search-title">精业济群</h1>-->
<div class="oa-search-container">
<input type="text" class="oa-search-input" placeholder="请输入您要查询的关键字"
v-model="searchQuery" />
<button class="oa-search-btn" @click="performSearch">搜索</button>
</div>
</div>
<!-- 数字提醒 -->
<div class="oa-stats-section">
<h3 class="oa-stats-title">提醒</h3>
<div class="oa-stats-grid">
<div class="oa-stat-card" @click="handleStatClick('todo')">
<div class="oa-stat-top">
<div class="oa-stat-icon">
<i class="fa fa-clock-o"></i>
</div>
<div class="oa-stat-number">{{ stats.todo }}</div>
</div>
<div class="oa-stat-label">待办</div>
</div>
<div class="oa-stat-card" @click="handleStatClick('done')">
<div class="oa-stat-top">
<div class="oa-stat-icon">
<i class="fa fa-check-circle"></i>
</div>
<div class="oa-stat-number">{{ stats.done }}</div>
</div>
<div class="oa-stat-label">已办</div>
</div>
<div class="oa-stat-card" @click="handleStatClick('notification')">
<div class="oa-stat-top">
<div class="oa-stat-icon">
<i class="fa fa-bell"></i>
</div>
<div class="oa-stat-number">{{ stats.notifications }}</div>
</div>
<div class="oa-stat-label">消息</div>
</div>
<div class="oa-stat-card" @click="handleStatClick('started')">
<div class="oa-stat-top">
<div class="oa-stat-icon">
<i class="fa fa-file-text"></i>
</div>
<div class="oa-stat-number">{{ stats.started }}</div>
</div>
<div class="oa-stat-label">申请</div>
</div>
</div>
</div>
</div>
</div>
<!-- box1 -->
<div class="oa-box-1-section">
<!-- 左侧用户信息 -->
<div class="oa-user-panel">
<div class="oa-user-avatar">
<i class="fa fa-user"></i>
</div>
<div class="oa-user-name">${@auth.getPrincipalProperty('username')}</div>
<div class="oa-user-info">
<div class="info-item">
<span class="info-label">工号:</span>
<span class="info-value">${@auth.getPrincipalProperty('loginname')}</span>
</div>
<div class="info-item">
<span class="info-label">性别:</span>
<span class="info-value">${@auth.getPrincipalProperty('sex')}</span>
</div>
<div class="info-item">
<span class="info-label">单位:</span>
<span class="info-value">
<!--#if(!isEmpty(@auth.getPrincipalProperty('unit'))){#-->
${@auth.getPrincipalProperty('unit').getName()}
<!--#}#-->
</span>
</div>
<div class="info-item">
<span class="info-label">工会:</span>
<span class="info-value">
<!--#if(!isEmpty(@auth.getPrincipalProperty('union'))){#-->
${@auth.getPrincipalProperty('union').getName()}
<!--#}#-->
</span>
</div>
</div>
<a href="/platform/v4/personCenter" class="oa-user-homepage-btn" target="_blank">
<i class="fa fa-home"></i>
个人主页
</a>
</div>
<!-- 右侧活动列表 -->
<div class="oa-activity-content">
<div class="oa-activity-header">
<div class="oa-activity-tabs">
<div class="oa-activity-tab" :class="{active: activeTab === 'recent'}"
@click="setActiveTab('recent')">最新活动
</div>
<div class="oa-activity-tab" :class="{active: activeTab === 'history'}"
@click="setActiveTab('history')">历史活动
</div>
</div>
</div>
<div class="oa-activity-carousel">
<div v-if="currentActivityList.length === 0" class="oa-no-activity">
<div class="oa-no-activity-icon">
<i class="fa fa-calendar-o"></i>
</div>
<div class="oa-no-activity-title">暂无活动信息</div>
<div class="oa-no-activity-desc">{{ activeTab === 'recent' ? '当前没有最新活动' : '当前没有历史活动'
}}
</div>
</div>
<el-carousel v-else :autoplay="true" arrow="always" indicator-position="none">
<el-carousel-item v-for="activity in currentActivityList" :key="activity.id">
<div class="oa-activity-card">
<div class="oa-activity-image">
<img
src="https://images.unsplash.com/photo-1517245386807-bb43f82c33c4?w=800&auto=format&fit=crop"
:alt="activity.name" />
<div class="oa-activity-overlay">
<div class="oa-activity-title">{{ activity.name }}</div>
<div class="oa-activity-meta">{{ activity.startDate }} ~ {{ activity.endDate
}}
</div>
</div>
</div>
</div>
</el-carousel-item>
</el-carousel>
</div>
</div>
</div>
<!-- box2 - 系统通知公告 -->
<div class="oa-box-2-section">
<div class="oa-notice-header">
<div class="oa-notice-header-content">
<div class="oa-notice-header-icon">
<i class="fa fa-bullhorn"></i>
</div>
<h2 class="oa-notice-header-title">系统通知公告</h2>
</div>
<div class="oa-notice-header-more">
<button class="oa-header-more-btn">
更多
<i class="fa fa-angle-right"></i>
</button>
</div>
</div>
<div class="oa-notice-content">
<div class="oa-notice-list">
<div v-for="notice in noticeList" :key="notice.id" class="oa-notice-item" @click="viewNotice(notice)">
<div class="oa-notice-item-icon">
<i class="fa fa-bullhorn"></i>
</div>
<div class="oa-notice-item-content">
<div class="oa-notice-item-title">{{ notice.title }}</div>
<div class="oa-notice-item-desc">{{ notice.content }}</div>
</div>
<div class="oa-notice-item-date">{{ notice.publishDate }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
const vue = new Vue({
el: "#v4-home-app",
data() {
return {
searchQuery: "",
activeTab: "recent",
noticeTab: "system",
currentIndex: 0,
stats: {
todo: 3,
done: 0,
notifications: 7,
started: 9
},
activityList: [],
noticeList: [
{
id: 1,
type: 'system',
title: '系统维护通知',
content: '系统将于本周六晚上22:00-24:00进行例行维护,期间可能影响正常使用,请提前做好相关准备。',
publishDate: '2024-01-15',
isRead: false
},
{
id: 2,
type: 'announcement',
title: '新功能上线公告',
content: '移动端应用已正式上线,支持手机端办公审批,欢迎大家下载使用。',
publishDate: '2024-01-14',
isRead: true
},
{
id: 3,
type: 'urgent',
title: '紧急安全提醒',
content: '近期发现钓鱼邮件攻击,请勿点击可疑链接,如有疑问请联系IT部门。',
publishDate: '2024-01-13',
isRead: false
}
]
}
},
computed: {
recentActivities() {
const now = new Date()
return this.activityList
.filter(activity => new Date(activity.endDate) >= now)
.sort((a, b) => new Date(b.startDate) - new Date(a.startDate))
},
historyActivities() {
const now = new Date()
return this.activityList
.filter(activity => new Date(activity.endDate) < now)
.sort((a, b) => new Date(b.startDate) - new Date(a.startDate))
},
currentActivityList() {
return this.activeTab === "recent" ? this.recentActivities : this.historyActivities
}
},
mounted() {
// 初始化数据
this.getActivity()
this.getStatistics()
},
methods: {
performSearch() {
if (this.searchQuery.trim()) {
console.log("搜索:", this.searchQuery)
// 实现搜索逻辑
}
},
setActiveTab(tab) {
this.activeTab = tab
this.currentIndex = 0 // 切换标签时重置索引
},
getActivity() {
$.get("/platform/home/listHomeActivity").then((res) => {
if (res.code === 0) {
this.activityList = res.data
}
})
},
viewNotice(notice) {
console.log('查看通知:', notice.title)
// 标记为已读
notice.isRead = true
// 这里可以添加查看详情的逻辑
},
handleStatClick(val){
if(['todo', 'done', 'application']){
window.open('/flow/todoCenter?status=' + val)
}
},
async getStatistics() {
try {
const res = await $.post("/flow/todoCenter/statistics")
if (res.code === 0) {
const { todoCount, doneCount, startedCount } = res.data
this.stats.todo = todoCount
this.stats.done = doneCount
this.stats.started = startedCount
}
} catch (error) {
this.$message.error("获取统计数据失败")
console.error("获取统计数据失败:", error)
}
},
}
})
</script>
<!--#}#-->
@@ -0,0 +1,444 @@
<!--#
layout("/layouts/v4/baseLayout.html"){
#-->
<style>
.message-center {
padding: 20px;
}
.statistics-section {
margin-bottom: 20px;
}
.stat-card {
border: none;
height: 100%;
cursor: pointer;
transition: all 0.3s ease;
}
.stat-card .el-card__body {
padding: 20px;
}
.stat-content {
display: flex;
align-items: center;
}
.stat-icon {
width: 60px;
height: 60px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20px;
font-size: 28px;
}
.all-icon {
background-color: rgba(64, 158, 255, 0.1);
color: #409eff;
}
.unread-icon {
background-color: rgba(245, 108, 108, 0.1);
color: #f56c6c;
}
.read-icon {
background-color: rgba(103, 194, 58, 0.1);
color: #67c23a;
}
.stat-info {
flex: 1;
}
.stat-value {
font-size: 28px;
font-weight: 600;
line-height: 1.2;
margin-bottom: 5px;
}
.stat-label {
font-size: 14px;
color: #909399;
}
/* 搜索区域样式 */
.filter-section {
margin-bottom: 20px;
}
.filter-section .el-card__body {
padding: 15px 20px;
}
.search-form {
display: flex;
flex-wrap: wrap;
}
.search-form .el-form-item {
margin-bottom: 0;
margin-right: 15px;
}
/* 消息列表区域样式 */
.message-section .el-card__header {
padding: 0;
border-bottom: none;
}
/* 优化后的 tabs 样式 */
.message-header .el-tabs__header {
margin: 0;
padding: 0 20px;
background-color: #fff;
}
.message-header .el-tabs__nav-wrap::after {
height: 1px;
background-color: #ebeef5;
}
.message-header .el-tabs__item {
height: 50px;
line-height: 50px;
font-size: 15px;
color: #606266;
padding: 0 20px;
transition: all 0.3s;
}
.message-header .el-tabs__item.is-active {
color: var(--color-primary);
font-weight: 500;
}
.message-header .el-tabs__active-bar {
height: 3px;
border-radius: 3px;
}
.message-title {
font-weight: 500;
color: #303133;
}
.pagination-container {
margin-top: 20px;
display: flex;
justify-content: center;
padding: 10px 0;
}
</style>
<div id="app" v-cloak>
<div class="message-center">
<!-- 统计数据区域 -->
<el-row :gutter="20" class="statistics-section">
<el-col :span="8">
<el-card shadow="never" class="stat-card">
<div class="stat-content">
<div class="stat-icon all-icon">
<i class="el-icon-message"></i>
</div>
<div class="stat-info">
<div class="stat-value">{{stats.total}}</div>
<div class="stat-label">全部消息</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="never" class="stat-card">
<div class="stat-content">
<div class="stat-icon unread-icon">
<i class="el-icon-bell"></i>
</div>
<div class="stat-info">
<div class="stat-value">{{stats.unread}}</div>
<div class="stat-label">未读消息</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="never" class="stat-card">
<div class="stat-content">
<div class="stat-icon read-icon">
<i class="el-icon-finished"></i>
</div>
<div class="stat-info">
<div class="stat-value">{{stats.read}}</div>
<div class="stat-label">已读消息</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 搜索筛选区域 -->
<el-card shadow="never" class="filter-section">
<el-form :inline="true" :model="filters" class="search-form">
<el-form-item>
<el-select v-model="filters.type" placeholder="消息类型" clearable @change="loadMessages"
style="width: 150px;">
<el-option label="全部类型" value=""></el-option>
<el-option label="系统公告" value="1"></el-option>
<el-option label="消息通知" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item v-if="pageForm.isRead == 0">
<el-button size="medium" type="primary" @click="markAllAsRead" :loading="loading">
<i class="el-icon-check"></i> 全部标记已读
</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 消息列表区域 -->
<el-card shadow="never" class="message-section">
<div slot="header" class="message-header">
<el-tabs v-model="pageForm.isRead" @tab-click="handleTabClick">
<el-tab-pane label="全部消息" name="-1">
<span slot="label">
<i class="el-icon-message"></i>
全部消息
</span>
</el-tab-pane>
<el-tab-pane label="未读消息" name="0">
<span slot="label">
<i class="el-icon-bell"></i>
未读消息
</span>
</el-tab-pane>
<el-tab-pane label="已读消息" name="1">
<span slot="label">
<i class="el-icon-finished"></i>
已读消息
</span>
</el-tab-pane>
</el-tabs>
</div>
<!-- 表格展示 -->
<el-table v-loading="loading" :data="messages" style="width: 100%" :key="currentTab"
empty-text="暂无消息"
:header-cell-style="{backgroundColor: '#f5f7fa'}">
<el-table-column prop="type" label="消息类型" width="120">
<template slot-scope="scope">
<el-tag size="mini" :type="scope.row.type === 1 ? 'danger' : 'primary'">
{{getMessageTypeName(scope.row.type)}}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="title" label="标题" min-width="200" show-overflow-tooltip>
<template slot-scope="scope">
<span class="message-title">{{scope.row.title}}</span>
</template>
</el-table-column>
<el-table-column prop="content" label="内容预览" min-width="250" show-overflow-tooltip>
<template slot-scope="scope">
{{getContentPreview(scope.row.content)}}
</template>
</el-table-column>
<el-table-column prop="createdAt" label="接收时间" width="180">
<template slot-scope="scope">
{{formatTime(scope.row.createdAt)}}
</template>
</el-table-column>
<el-table-column prop="isRead" label="状态" width="100">
<template slot-scope="scope">
<el-tag size="mini" :type="scope.row.isRead ? 'success' : 'warning'">
{{scope.row.isRead ? '已读' : '未读'}}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="100" fixed="right">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="viewMessage(scope.row)">查看</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-container" v-if="messages.length > 0">
<el-pagination
background
@current-change="handlePageChange"
:current-page="pageForm.pageNumber"
:page-size="pageForm.pageSize"
layout="prev, pager, next, jumper"
:total="pageForm.totalCount"
></el-pagination>
</div>
</el-card>
</div>
<!-- 消息详情弹窗 -->
<el-dialog
:visible.sync="detailVisible"
:title="currentMessage?.globalMessage?.title"
width="60%"
class="message-detail-dialog"
>
<div v-if="currentMessage.id">
<div style="text-align: center; margin-bottom: 20px; padding-bottom: 16px; border-bottom: 1px solid #eee;">
<div style="font-size: 18px; font-weight: 500; margin-bottom: 8px;">{{currentMessage.title}}</div>
<div style="font-size: 14px; color: #666;">
<span>{{getMessageTypeName(currentMessage?.globalMessage?.type)}}</span>
<span style="margin: 0 8px;"></span>
<span>{{formatTime(currentMessage.createdAt)}}</span>
</div>
</div>
<div style="line-height: 1.8; font-size: 15px;" v-html="currentMessage?.globalMessage?.content"></div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="detailVisible = false">关闭</el-button>
</span>
</el-dialog>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
currentTab: 'all',
loading: false,
messages: [],
stats: {
total: 0,
unread: 0,
read: 0
},
filters: {
type: ''
},
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0
},
detailVisible: false,
currentMessage: {}
}
},
methods: {
async loadStats() {
try {
const resp = await this.$axios.get('/platform/v4/msg/stats')
if (resp.code === 0) {
this.stats = resp.data
}
} catch (error) {
console.error('加载统计数据失败:', error)
}
},
async loadMessages() {
this.loading = true
try {
const resp = await this.$axios.post('/platform/v4/msg/pageData', this.pageForm)
if (resp.code === 0) {
this.messages = resp.data.list
this.pageForm.totalCount = resp.data.totalCount
} else {
this.$message.error(resp.msg)
}
} catch (error) {
this.$message.error('加载消息失败')
} finally {
this.loading = false
}
},
async viewMessage(message) {
this.$axios.post(`/platform/v4/msg/detail/` + message.id).then(async res => {
if (res.code === 0) {
this.currentMessage = res.data
this.detailVisible = true
// 如果是未读消息,标记为已读
if (!message.isRead) {
await this.markAsRead(message.id)
this.loadStats()
this.loadMessages()
}
}
})
},
async markAsRead(messageId) {
try {
await this.$axios.post(`/platform/v4/msg/read/` + messageId)
} catch (error) {
console.error('标记已读失败:', error)
}
},
async markAllAsRead() {
this.$confirm('确定要将所有未读消息标记为已读吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
this.loading = true
try {
const resp = await this.$axios.post('/platform/v4/msg/read/all')
if (resp.code === 0) {
this.$message.success('操作成功')
this.loadMessages()
this.loadStats()
} else {
this.$message.error(resp.msg)
}
} catch (error) {
this.$message.error('操作失败')
} finally {
this.loading = false
}
})
},
handleTabClick(tab) {
this.pageForm.pageNumber = 1
this.loadMessages()
},
handlePageChange(pageNumber) {
this.pageForm.pageNumber = pageNumber
this.loadMessages()
},
getMessageTypeName(type) {
return type === 1 ? '系统公告' : '消息通知'
},
getContentPreview(content) {
if (!content) return ''
// 移除HTML标签
const text = content.replace(/<[^>]*>/g, '')
return text.length > 50 ? text.substring(0, 50) + '...' : text
},
formatTime(timestamp) {
return this.$moment(timestamp).format('YYYY-MM-DD HH:mm')
}
},
created() {
this.loadStats()
this.loadMessages()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,448 @@
<!--#
layout("/layouts/v4/baseLayout.html"){
#-->
<style>
.task-todo-center {
padding: 20px;
}
/* 统计卡片样式 */
.statistics-section {
margin-bottom: 20px;
}
.stat-card {
border: none;
height: 100%;
}
.stat-card .el-card__body {
padding: 20px;
}
.stat-content {
display: flex;
align-items: center;
}
.stat-icon {
width: 60px;
height: 60px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20px;
font-size: 28px;
}
.todo-icon {
background-color: rgba(64, 158, 255, 0.1);
color: #409eff;
}
.done-icon {
background-color: rgba(103, 194, 58, 0.1);
color: #67c23a;
}
.started-icon {
background-color: rgba(230, 162, 60, 0.1);
color: #e6a23c;
}
.stat-info {
flex: 1;
}
.stat-value {
font-size: 28px;
font-weight: 600;
line-height: 1.2;
margin-bottom: 5px;
}
.stat-label {
font-size: 14px;
color: #909399;
}
/* 搜索区域样式 */
.filter-section {
margin-bottom: 20px;
}
.filter-section .el-card__body {
padding: 15px 20px;
}
.search-form {
display: flex;
flex-wrap: wrap;
}
.search-form .el-form-item {
margin-bottom: 0;
margin-right: 15px;
}
/* 任务列表区域样式 */
.task-section .el-card__header {
padding: 0;
border-bottom: none;
}
/* 优化后的 tabs 样式 */
.task-header .el-tabs__header {
margin: 0;
padding: 0 20px;
background-color: #fff;
}
.task-header .el-tabs__nav-wrap::after {
height: 1px;
background-color: #ebeef5;
}
.task-header .el-tabs__item {
height: 50px;
line-height: 50px;
font-size: 15px;
color: #606266;
padding: 0 20px;
transition: all 0.3s;
}
.task-header .el-tabs__item.is-active {
color: #409eff;
font-weight: 500;
}
.task-header .el-tabs__active-bar {
height: 3px;
border-radius: 3px;
}
.task-title {
font-weight: 500;
color: #303133;
}
/* 表格行样式 */
.el-table .hover-row {
background-color: #f5f7fa;
}
.pagination-container {
margin-top: 20px;
display: flex;
justify-content: center;
padding: 10px 0;
}
</style>
<div id="app" v-cloak>
<div class="task-todo-center">
<!-- 统计数据区域 -->
<el-row :gutter="20" class="statistics-section">
<el-col :span="8">
<el-card shadow="hover" class="stat-card">
<div class="stat-content">
<div class="stat-icon todo-icon">
<i class="el-icon-s-claim"></i>
</div>
<div class="stat-info">
<div class="stat-value">{{todoCount}}</div>
<div class="stat-label">待办任务</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover" class="stat-card">
<div class="stat-content">
<div class="stat-icon done-icon">
<i class="el-icon-finished"></i>
</div>
<div class="stat-info">
<div class="stat-value">{{doneCount}}</div>
<div class="stat-label">已办任务</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover" class="stat-card">
<div class="stat-content">
<div class="stat-icon started-icon">
<i class="el-icon-s-promotion"></i>
</div>
<div class="stat-info">
<div class="stat-value">{{startedCount}}</div>
<div class="stat-label">我发起的</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 搜索筛选区域 -->
<el-card shadow="never" class="filter-section">
<el-form :inline="true" :model="pageForm" class="search-form">
<el-form-item>
<el-input v-model="pageForm.searchKeyword" placeholder="请输入关键词搜索"
prefix-icon="el-icon-search"
clearable></el-input>
</el-form-item>
<el-form-item>
<el-select v-model="pageForm.category" placeholder="所有流程分类" clearable>
<el-option v-for="type in categoryOptions" :key="type.id" :label="type.name"
:value="type.id"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="search">搜索</el-button>
<el-button @click="reset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 任务列表区域 -->
<el-card shadow="never" class="task-section">
<div slot="header" class="task-header">
<el-tabs v-model="activeTab" @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-tabs>
</div>
<!-- 表格展示 -->
<el-table v-loading="loading" :data="tasks" style="width: 100%" :key="activeTab"
:header-cell-style="{backgroundColor: '#f5f7fa'}">
<el-table-column prop="processInstanceName" label="流程名称" min-width="180" show-overflow-tooltip>
<template slot-scope="scope">
<span class="task-title">{{scope.row.variable?.instanceName}}</span>
</template>
</el-table-column>
<el-table-column prop="taskName" label="任务节点" min-width="150"
show-overflow-tooltip></el-table-column>
<el-table-column prop="categoryName" label="流程分类" min-width="150" show-overflow-tooltip>
<template slot-scope="{row}">{{categoryOptions.find(item => item.id === row.category)?.name}}
</template>
</el-table-column>
<el-table-column prop="initiatorName" label="申请人" min-width="120">
<template slot-scope="{row}">{{row.variable.initiatorName}}</template>
</el-table-column>
<!-- <el-table-column label="时间" min-width="180">-->
<!-- <template slot-scope="scope">-->
<!-- <div v-if="activeTab === 'todo'">-->
<!-- <i class="el-icon-time"></i>-->
<!-- {{scope.row.createdAt}}-->
<!-- </div>-->
<!-- <div v-else-if="activeTab === 'done'">-->
<!-- <i class="el-icon-check"></i>-->
<!-- {{scope.row.finishTime}}-->
<!-- </div>-->
<!-- <div v-else-if="activeTab === 'started'">-->
<!-- <i class="el-icon-s-promotion"></i>-->
<!-- {{scope.row.createdAt}}-->
<!-- </div>-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column v-if="activeTab === 'started'" label="状态" width="100">
<template slot-scope="{row}">{{processStatusMap[row.state]?.text}}</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>
<!-- 空状态展示 -->
<el-empty v-if="tasks.length === 0" :description="getEmptyText()"></el-empty>
<!-- 分页 -->
<div class="pagination-container" v-if="tasks.length > 0">
<el-pagination
background
@current-change="handleCurrentChange"
:current-page="pageForm.pageNumber"
:page-size="pageForm.pageSize"
layout="prev, pager, next, jumper"
:total="pageForm.totalCount"
></el-pagination>
</div>
</el-card>
</div>
</div>
<script>
new Vue({
el: "#app",
data() {
return {
// 统计数据
todoCount: 0,
doneCount: 0,
startedCount: 0,
// 查询表单
pageForm: {
pageNumber: 1,
pageSize: 10,
todoCount: 0,
keyword: "",
category: ""
},
// 流程类型选项
categoryOptions: [],
// 任务列表
tasks: [],
loading: false,
// 标签页
activeTab: "todo",
// 当前任务
currentTask: null,
dialogVisible: false,
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"}
},
// 任务状态枚举
taskStateEnum: {
DOING: 10,
FINISHED: 20,
WITHDRAW: 30,
INTERRUPT: 40,
PENDING: 50,
ABANDON: 99
}
}
},
created() {
this.initData()
},
methods: {
// 初始化数据
async initData() {
await Promise.all([this.getStatistics(), this.listCategory(), this.getTasks()])
},
// 获取统计数据
async getStatistics() {
try {
const res = await $.post("/flow/todoCenter/statistics")
if (res.code === 0) {
const {todoCount, doneCount, startedCount} = res.data
this.todoCount = todoCount
this.doneCount = doneCount
this.startedCount = startedCount
}
} catch (error) {
this.$message.error("获取统计数据失败")
console.error("获取统计数据失败:", error)
}
},
// 获取流程类型
async listCategory() {
this.$axios.post("/flow/category/list").then((res) => {
if (res.code === 0) {
this.categoryOptions = res.data
}
})
},
// 获取任务列表
async getTasks() {
this.loading = true
try {
const res = await $.post("/flow/todoCenter/" + this.activeTab, this.pageForm)
if (res.code === 0) {
this.tasks = res.data.list
this.pageForm.totalCount = res.data.totalCount
this.dialogVisible = false
}
} catch (error) {
this.$message.error("获取任务列表失败")
console.error("获取任务列表失败:", error)
} finally {
this.loading = false
}
},
// 处理标签页点击
handleTabClick(tab) {
this.activeTab = tab.name
this.pageForm.pageNumber = 1
this.getTasks()
},
// 搜索
search() {
this.pageForm.pageNumber = 1
this.getTasks()
this.getStatistics()
},
// 重置搜索
reset() {
this.pageForm.searchKeyword = ""
this.pageForm.category = ""
this.search()
},
// 处理页码变化
handleCurrentChange(val) {
this.pageForm.pageNumber = val
this.getTasks()
},
// 处理任务
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)
},
// 获取空状态文本
getEmptyText() {
switch (this.activeTab) {
case "todo":
return "您当前没有需要办理的任务,辛苦了"
case "done":
return "您当前没有已办理的任务记录"
case "started":
return "您当前没有发起的流程"
default:
return "暂无数据"
}
}
}
})
</script>
<!--#
}
#-->