first commit
This commit is contained in:
@@ -0,0 +1,806 @@
|
||||
const apps = {
|
||||
template: /*language=HTML*/ `
|
||||
<div class="apps-container">
|
||||
<van-nav-bar title="应用中心" placeholder fixed></van-nav-bar>
|
||||
<!-- 搜索栏 -->
|
||||
<van-sticky offset-top="46px">
|
||||
<van-search
|
||||
v-model="searchKeyword"
|
||||
placeholder="搜索应用"
|
||||
show-action
|
||||
@search="onSearch"
|
||||
@cancel="onCancel"
|
||||
></van-search>
|
||||
</van-sticky>
|
||||
|
||||
<div class="custom-tree-select">
|
||||
<!-- 左侧导航 -->
|
||||
<div class="tree-nav">
|
||||
<div
|
||||
v-for="(category, index) in allCategories"
|
||||
:key="category.id"
|
||||
:class="['nav-item', { active: activeTab === index }]"
|
||||
@click="onClickNav(index)"
|
||||
>
|
||||
<div class="nav-icon">
|
||||
<!--<i v-if="category.icon" :class="category.icon"></i>
|
||||
<i v-else class="fa fa-book"></i>-->
|
||||
<img v-if="category.picIcon" :src="category.picIcon" :alt="category.name || ''" style="width: 18px"/>
|
||||
<i v-else :class="category.icon || 'fa fa-th-large'"></i>
|
||||
</div>
|
||||
<div class="nav-text">{{ category.name }}</div>
|
||||
<div class="nav-indicator" v-if="activeTab === index"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧内容 -->
|
||||
<div class="tree-content">
|
||||
<div v-if="loading" class="module-loading">
|
||||
<van-loading size="24px">加载中...</van-loading>
|
||||
</div>
|
||||
|
||||
<div :class="['app-grid', { 'app-grid-single': applications.length === 1 }]"
|
||||
v-else-if="isFixedCategory && applications.length > 0">
|
||||
<div v-for="app in applications"
|
||||
:key="app.id"
|
||||
class="app-grid-item"
|
||||
:class="{ 'is-favorite': app.isFavorite }"
|
||||
@click="openApp(app)"
|
||||
@touchstart="onTouchStart(app)"
|
||||
@touchend="onTouchEnd"
|
||||
@touchmove="onTouchMove">
|
||||
<div class="app-grid-icon">
|
||||
<van-icon v-if="app.picIcon" :name="app.picIcon" size="45"></van-icon>
|
||||
<van-icon v-else-if="app.icon" :name="app.icon" size="28"></van-icon>
|
||||
<van-icon v-else name="apps-o" size="28"></van-icon>
|
||||
</div>
|
||||
<div class="app-grid-name">{{ app.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="!isFixedCategory && moduleSections.length > 0" class="module-sections">
|
||||
<div v-for="section in moduleSections" :key="section.id" class="module-section">
|
||||
<div class="module-title">{{ section.name }}</div>
|
||||
<div :class="['module-grid', { 'module-grid-single': section.menus.length === 1 }]">
|
||||
<div
|
||||
v-for="menu in section.menus"
|
||||
:key="menu.id"
|
||||
class="module-feature"
|
||||
@click="navigateToMenu(menu)"
|
||||
>
|
||||
<img v-if="menu.picIcon" :src="menu.picIcon" :alt="menu.name || ''" class="module-feature-img"/>
|
||||
<div v-else class="module-feature-icon">
|
||||
<van-icon :name="menu.icon || 'apps-o'" size="24"></van-icon>
|
||||
</div>
|
||||
<div class="module-feature-name">{{ menu.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<van-empty v-else description="暂无应用"></van-empty>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 应用菜单弹框 -->
|
||||
<van-popup v-model="showMenuPopup" position="bottom" :style="{ width: '100%', maxHeight: '60%' }" round>
|
||||
<div class="menu-popup">
|
||||
<div class="popup-header">
|
||||
<div class="popup-title">{{ currentApp.name }}</div>
|
||||
<van-icon name="cross" @click="showMenuPopup = false" class="close-icon"></van-icon>
|
||||
</div>
|
||||
<div class="menu-grid">
|
||||
<div
|
||||
v-for="menu in currentMenus"
|
||||
:key="menu.id"
|
||||
class="grid-item"
|
||||
@click="navigateToMenu(menu)"
|
||||
>
|
||||
<img v-if="menu.picIcon" :src="menu.picIcon" :alt="menu.name || ''" style="width: 48px" class="grid-img"/>
|
||||
<div v-else class="grid-icon">
|
||||
<van-icon :name="menu.icon || 'apps-o'" size="24"></van-icon>
|
||||
</div>
|
||||
<div class="grid-name">{{ menu.name }}</div>
|
||||
<div class="grid-desc" v-if="menu.aliasName">{{ menu.aliasName }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
</div>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
// 应用列表
|
||||
applications: [],
|
||||
moduleSections: [],
|
||||
// 固定分类列表
|
||||
categories: [
|
||||
{id: "all", name: "全部应用", icon: "fa fa-th-large"},
|
||||
{id: "favorites", name: "我的收藏", icon: "fa fa-star-o"},
|
||||
{id: "recommended", name: "推荐应用", icon: "fa fa-thumbs-o-up"}
|
||||
],
|
||||
// 动态加载的分类
|
||||
dynamicCategories: [],
|
||||
// 当前选中的分类索引
|
||||
activeTab: 0,
|
||||
// 搜索关键词
|
||||
searchKeyword: "",
|
||||
// 列表相关
|
||||
loading: true,
|
||||
// 弹框相关
|
||||
showMenuPopup: false,
|
||||
currentApp: {},
|
||||
currentMenus: [],
|
||||
requestSeq: 0,
|
||||
|
||||
touchTimer: null,
|
||||
isTouchMoved: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 所有分类(固定分类 + 动态分类)
|
||||
allCategories() {
|
||||
return [...this.categories, ...this.dynamicCategories]
|
||||
},
|
||||
// 当前选中的分类ID
|
||||
currentCategoryId() {
|
||||
return this.allCategories[this.activeTab]?.id || "all"
|
||||
},
|
||||
isFixedCategory() {
|
||||
return ["all", "favorites", "recommended"].includes(this.currentCategoryId)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 页面加载时获取分类和应用数据
|
||||
this.loadCategories()
|
||||
this.loadApps()
|
||||
},
|
||||
methods: {
|
||||
// 加载分类数据
|
||||
loadCategories() {
|
||||
this.$axios.post("/platform/v4/apps/categories", {platform: "H5"}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.dynamicCategories = res.data || []
|
||||
} else {
|
||||
console.error("获取应用分类失败:", res.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 加载应用数据
|
||||
loadApps() {
|
||||
const requestSeq = ++this.requestSeq
|
||||
this.loading = true
|
||||
this.moduleSections = []
|
||||
|
||||
// 构建请求参数
|
||||
const params = {
|
||||
categoryId:
|
||||
this.currentCategoryId === "all"
|
||||
? ""
|
||||
: this.currentCategoryId,
|
||||
keyword: this.searchKeyword,
|
||||
platform: "H5"
|
||||
}
|
||||
|
||||
// 将参数转换为URL查询参数
|
||||
const queryString = Object.keys(params)
|
||||
.filter((key) => params[key] !== null && params[key] !== undefined && params[key] !== "")
|
||||
.map((key) => encodeURIComponent(key) + "=" + encodeURIComponent(params[key]))
|
||||
.join("&")
|
||||
|
||||
// 如果是"我的收藏"分类
|
||||
if (this.currentCategoryId === "favorites") {
|
||||
this.loadFavorites()
|
||||
return
|
||||
}
|
||||
|
||||
// 如果是"推荐应用"分类
|
||||
if (this.currentCategoryId === "recommended") {
|
||||
this.$axios.post("/platform/home/listRecommendApp", { platform: "H5" }).then((result) => {
|
||||
if (requestSeq !== this.requestSeq) return
|
||||
if (result.code === 0) {
|
||||
this.applications = result.data || []
|
||||
this.finishLoadApps(requestSeq)
|
||||
} else {
|
||||
console.error("获取推荐应用失败:", result.msg)
|
||||
this.loading = false
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 发送请求获取普通应用列表
|
||||
this.$axios.post("/platform/v4/apps/list?" + queryString)
|
||||
.then((result) => {
|
||||
if (requestSeq !== this.requestSeq) return
|
||||
if (result.code === 0) {
|
||||
this.applications = result.data || []
|
||||
this.finishLoadApps(requestSeq)
|
||||
} else {
|
||||
console.error("获取应用列表失败:", result.msg)
|
||||
this.loading = false
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 加载收藏的应用
|
||||
loadFavorites() {
|
||||
const requestSeq = this.requestSeq
|
||||
this.$axios.post("/platform/v4/apps/favorite", {platform: 'H5'})
|
||||
.then((result) => {
|
||||
if (requestSeq !== this.requestSeq) return
|
||||
if (result.code === 0) {
|
||||
this.applications = result.data || []
|
||||
this.finishLoadApps(requestSeq)
|
||||
} else {
|
||||
console.error("获取收藏应用失败:", result.msg)
|
||||
this.loading = false
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
finishLoadApps(requestSeq) {
|
||||
if (requestSeq !== this.requestSeq) return
|
||||
if (this.isFixedCategory) {
|
||||
this.moduleSections = []
|
||||
this.loading = false
|
||||
return
|
||||
}
|
||||
this.loadModuleSections(this.applications, requestSeq)
|
||||
},
|
||||
|
||||
// 加载每个模块下的功能菜单
|
||||
loadModuleSections(apps, requestSeq) {
|
||||
const list = apps || []
|
||||
if (!list.length) {
|
||||
this.moduleSections = []
|
||||
this.loading = false
|
||||
return
|
||||
}
|
||||
Promise.all(list.map(app => {
|
||||
return this.$axios.post("/platform/sys/user/subAppMenus", {appId: app.id, platform: "H5"}).then((res) => {
|
||||
const menus = res.code === 0 ? (res.data || []) : []
|
||||
return {
|
||||
id: app.id,
|
||||
name: app.name,
|
||||
app: app,
|
||||
menus: this.resolveModuleMenus(app, menus)
|
||||
}
|
||||
}).catch(() => {
|
||||
return {
|
||||
id: app.id,
|
||||
name: app.name,
|
||||
app: app,
|
||||
menus: this.resolveModuleMenus(app, [])
|
||||
}
|
||||
})
|
||||
})).then(sections => {
|
||||
if (requestSeq !== this.requestSeq) return
|
||||
this.moduleSections = sections.filter(section => section.menus.length > 0)
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
resolveModuleMenus(app, menus) {
|
||||
const items = this.flattenMenus(menus || [])
|
||||
if (items.length > 0) {
|
||||
return items
|
||||
}
|
||||
if (app && app.href) {
|
||||
return [{
|
||||
id: app.id,
|
||||
name: app.name,
|
||||
href: app.href,
|
||||
icon: app.icon,
|
||||
picIcon: app.picIcon,
|
||||
aliasName: app.aliasName
|
||||
}]
|
||||
}
|
||||
return []
|
||||
},
|
||||
|
||||
flattenMenus(menus) {
|
||||
const result = []
|
||||
;(menus || []).forEach(menu => {
|
||||
if (menu.children && menu.children.length > 0) {
|
||||
result.push.apply(result, this.flattenMenus(menu.children))
|
||||
} else if (menu.href) {
|
||||
result.push(menu)
|
||||
}
|
||||
})
|
||||
return result
|
||||
},
|
||||
|
||||
// 切换应用收藏状态
|
||||
toggleFavorite(appId) {
|
||||
const app = this.applications.find((a) => a.id === appId)
|
||||
if (!app) return
|
||||
|
||||
const url = app.isFavorite ? "/platform/v4/apps/removeFavorite" : "/platform/v4/apps/addFavorite"
|
||||
const params = {appId: appId}
|
||||
|
||||
$.post(url, params)
|
||||
.then((result) => {
|
||||
if (result.code === 0) {
|
||||
app.isFavorite = !app.isFavorite
|
||||
this.$toast(app.isFavorite ? ('收藏成功:' + app.name) : ('取消收藏:' + app.name))
|
||||
} else {
|
||||
console.error(app.isFavorite ? ('取消收藏:' + app.name) : ('收藏失败:' + app.name), result.msg)
|
||||
}
|
||||
})
|
||||
.fail((error) => {
|
||||
console.error(app.isFavorite ? ('取消收藏:' + app.name) : ('收藏异常:' + app.name), error)
|
||||
})
|
||||
},
|
||||
|
||||
// 长按开始
|
||||
onTouchStart(app) {
|
||||
this.isTouchMoved = false
|
||||
this.touchTimer = setTimeout(() => {
|
||||
this.toggleFavorite(app.id)
|
||||
}, 800) // 长按 800ms 触发
|
||||
},
|
||||
|
||||
// 触摸移动(判断是否滑动,避免误触长按)
|
||||
onTouchMove() {
|
||||
this.isTouchMoved = true
|
||||
},
|
||||
|
||||
// 触摸结束(清除计时器)
|
||||
onTouchEnd() {
|
||||
if (this.touchTimer) {
|
||||
clearTimeout(this.touchTimer)
|
||||
this.touchTimer = null
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// 搜索
|
||||
onSearch() {
|
||||
this.applications = []
|
||||
this.moduleSections = []
|
||||
this.loadApps()
|
||||
},
|
||||
|
||||
// 取消搜索
|
||||
onCancel() {
|
||||
this.searchKeyword = ""
|
||||
this.onSearch()
|
||||
},
|
||||
|
||||
// 切换标签页 - TreeSelect导航点击事件
|
||||
onClickNav(index) {
|
||||
this.activeTab = index
|
||||
this.applications = []
|
||||
this.moduleSections = []
|
||||
this.loadApps()
|
||||
},
|
||||
|
||||
// 打开应用
|
||||
openApp(app) {
|
||||
this.$axios.post("/platform/sys/user/subAppMenus", {appId: app.id, platform: "H5"}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.showAppMenus(app, res.data)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 显示应用菜单弹框
|
||||
showAppMenus(app, menus) {
|
||||
if(app.href && menus.length === 0){
|
||||
this.$pjaxReplace(app.href)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!menus || menus.length === 0) {
|
||||
this.$toast('该应用暂无可用功能')
|
||||
return
|
||||
}
|
||||
|
||||
this.currentApp = app
|
||||
this.currentMenus = menus
|
||||
this.showMenuPopup = true
|
||||
},
|
||||
|
||||
// 导航到菜单
|
||||
navigateToMenu(menu) {
|
||||
// 关闭弹框
|
||||
// this.showMenuPopup = false
|
||||
if (menu.href) {
|
||||
if(menu.href.startsWith('http://') || menu.href.startsWith('https://')){
|
||||
window.open(menu.href, '_blank')
|
||||
}else{
|
||||
this.$pjaxReplace(menu.href)
|
||||
}
|
||||
} else {
|
||||
this.$toast('该功能暂不可用')
|
||||
}
|
||||
}
|
||||
},
|
||||
// 初始加载消除长按收藏
|
||||
beforeDestroy() {
|
||||
if (this.touchTimer) {
|
||||
clearTimeout(this.touchTimer)
|
||||
}
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
.apps-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100vh - 50px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 自定义树形选择组件样式 */
|
||||
.custom-tree-select {
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
height: calc(100vh - 150px);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.tree-nav {
|
||||
flex: 0 0 90px;
|
||||
background: #f7f8fa;
|
||||
border-right: 1px solid #ebedf0;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
}
|
||||
|
||||
.tree-nav::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari and Opera */
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
position: relative;
|
||||
padding: 12px 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
border-bottom: 1px solid #ebedf0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
background: #f2f3f5;
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
background: #fff;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.nav-item.active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 3px;
|
||||
background: #1989fa;
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
font-size: 18px;
|
||||
margin-bottom: 6px;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.nav-text {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
line-height: 1.2;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.nav-indicator {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 6px solid #1989fa;
|
||||
border-top: 6px solid transparent;
|
||||
border-bottom: 6px solid transparent;
|
||||
}
|
||||
|
||||
.tree-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.module-loading {
|
||||
min-height: 180px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #8a94a6;
|
||||
}
|
||||
|
||||
.module-sections {
|
||||
padding: 12px 12px 18px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.module-section {
|
||||
padding: 0 0 18px;
|
||||
}
|
||||
|
||||
.module-section + .module-section {
|
||||
border-top: 1px solid #f1f3f6;
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.module-title {
|
||||
position: relative;
|
||||
padding-left: 10px;
|
||||
margin-bottom: 10px;
|
||||
color: #202733;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.module-title::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 4px;
|
||||
width: 3px;
|
||||
height: 14px;
|
||||
border-radius: 2px;
|
||||
background: #1989fa;
|
||||
}
|
||||
|
||||
.module-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 10px 8px;
|
||||
}
|
||||
|
||||
.module-grid-single {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.module-feature {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 8px 2px;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.module-feature:active {
|
||||
background: #f2f7ff;
|
||||
}
|
||||
|
||||
.module-feature-img {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
object-fit: contain;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.module-feature-icon {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 6px;
|
||||
color: #ffffff;
|
||||
background: linear-gradient(135deg, #2f8af5 0%, #49c3dc 100%);
|
||||
}
|
||||
|
||||
.module-feature-name {
|
||||
width: 100%;
|
||||
color: #323233;
|
||||
font-size: 13px;
|
||||
line-height: 1.25;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.app-grid {
|
||||
display: grid;
|
||||
/* 改为自适应列数:每格最小 80px,自动换行 */
|
||||
grid-template-columns: repeat(auto-fit, minmax(60px, 1fr));
|
||||
|
||||
justify-items: center; /* 子项内容居中(图标和文字) */
|
||||
justify-content: start; /* 整体网格靠左对齐,避免居中 */
|
||||
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
background: #fff;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.app-grid-single {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.app-grid-single .app-grid-item {
|
||||
max-width: 80px;
|
||||
margin: 0 12px;
|
||||
}
|
||||
|
||||
.app-grid-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 12px 4px;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.app-grid-icon {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
max-width: 45px;
|
||||
max-height: 45px;
|
||||
/*border-radius: 12px;
|
||||
background: #f2f3f5;*/
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 6px;
|
||||
overflow: hidden;
|
||||
|
||||
/*border: 2px solid transparent;*/
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.app-grid-name {
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
color: #323233;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.app-grid-item.is-favorite .app-grid-icon,
|
||||
.app-grid-icon.is-favorite {
|
||||
border-color: #ff9800;
|
||||
box-shadow: 0 0 0 2px rgba(255, 152, 0, 0.2);
|
||||
}
|
||||
|
||||
|
||||
/* 菜单弹框样式 */
|
||||
/deep/ .menu-popup {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/deep/ .popup-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #ebedf0;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/deep/ .popup-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #323233;
|
||||
}
|
||||
|
||||
/deep/ .close-icon {
|
||||
font-size: 18px;
|
||||
color: #969799;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/deep/ .menu-grid {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
/deep/ .grid-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px 12px;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/deep/ .grid-item:active {
|
||||
transform: scale(0.95);
|
||||
background: #f2f3f5;
|
||||
}
|
||||
|
||||
/deep/ .grid-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 12px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 12px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/deep/ .grid-img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 12px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/deep/ .grid-name {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #323233;
|
||||
text-align: center;
|
||||
margin-bottom: 4px;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/deep/ .grid-desc {
|
||||
font-size: 12px;
|
||||
color: #969799;
|
||||
text-align: center;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/deep/ .menu-name {
|
||||
font-size: 16px;
|
||||
color: #323233;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/deep/ .menu-desc {
|
||||
font-size: 12px;
|
||||
color: #969799;
|
||||
}
|
||||
|
||||
/deep/ .menu-arrow {
|
||||
font-size: 16px;
|
||||
color: #c8c9cc;
|
||||
}
|
||||
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<div class="featured-page">
|
||||
<van-nav-bar
|
||||
title="精彩活动"
|
||||
left-text="返回"
|
||||
left-arrow
|
||||
fixed
|
||||
placeholder
|
||||
@click-left="goBack"
|
||||
></van-nav-bar>
|
||||
|
||||
<div class="featured-banner">
|
||||
<img :src="topImage" alt="">
|
||||
<div class="featured-banner__shade"></div>
|
||||
<div class="featured-banner__text">
|
||||
<div class="featured-banner__title">精彩活动</div>
|
||||
<div class="featured-banner__sub">发现更多校园工会活动</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="featured-list">
|
||||
<template v-if="activityOptions && activityOptions.length > 0">
|
||||
<div v-for="item in activityOptions" :key="item.id" class="act-item" @click="enterAct(item)">
|
||||
<van-tag class="act-item-wrapper-tag" v-if="$moment().unix() > $moment(item.endDate).unix()">
|
||||
已结束
|
||||
</van-tag>
|
||||
|
||||
<van-tag class="act-item-wrapper-tag" type="success"
|
||||
v-else-if="$moment().unix() > $moment(item.startDate).unix() && $moment().unix() < $moment(item.endDate).unix()">
|
||||
进行中
|
||||
</van-tag>
|
||||
|
||||
<van-tag class="act-item-wrapper-tag" type="success" v-else-if="$moment().unix() < $moment(item.startDate).unix()">
|
||||
即将开始
|
||||
</van-tag>
|
||||
|
||||
<div class="act-item-cover">
|
||||
<van-image v-if="item.cover" width="120" height="84" :src="item.cover"></van-image>
|
||||
</div>
|
||||
<div class="act-item-wrapper">
|
||||
<div class="content-title">{{ item.name }}</div>
|
||||
<div class="content-text">
|
||||
<div>开始时间:{{ $moment(item.startDate).format('MM-DD HH:mm') }}</div>
|
||||
<div class="mt5">结束时间:{{ $moment(item.endDate).format('MM-DD HH:mm') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<van-empty v-if="!activityOptions || activityOptions.length === 0" description="暂无精彩活动"></van-empty>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data: function () {
|
||||
return {
|
||||
topImage: "/assets/mobile/img/home/home1.png",
|
||||
activityOptions: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goBack: function () {
|
||||
this.returnHome()
|
||||
},
|
||||
returnHome: function () {
|
||||
if (window.store && typeof window.store.commit === "function") {
|
||||
window.store.commit("setActiveTarBar", "home")
|
||||
}
|
||||
if (!window.$ || !$.support || !$.support.pjax) {
|
||||
window.location.replace("/platform/h5/home")
|
||||
return
|
||||
}
|
||||
var fallbackTimer = window.setTimeout(function () {
|
||||
window.location.replace("/platform/h5/home")
|
||||
}, 1200)
|
||||
$(document).one("pjax:complete", function () {
|
||||
window.clearTimeout(fallbackTimer)
|
||||
})
|
||||
$(document).one("pjax:error pjax:timeout", function () {
|
||||
window.clearTimeout(fallbackTimer)
|
||||
window.location.replace("/platform/h5/home")
|
||||
})
|
||||
$.pjax({
|
||||
url: "/platform/h5/home",
|
||||
container: "#container",
|
||||
fragment: "#container",
|
||||
push: false,
|
||||
replace: true,
|
||||
timeout: 8000
|
||||
})
|
||||
},
|
||||
loadTopImage: function () {
|
||||
var self = this
|
||||
this.$axios.post("/open/common/getConfigKey", { key: "AppFeaturedActivityImg" }).then(function (res) {
|
||||
if (res.code === 0 && res.data) {
|
||||
self.topImage = String(res.data).split(",")[0]
|
||||
}
|
||||
})
|
||||
},
|
||||
listActivity: function () {
|
||||
var self = this
|
||||
this.$axios.post("/platform/home/listHomeActivity").then(function (resp) {
|
||||
if (resp.code === 0) {
|
||||
self.activityOptions = (resp.data || []).filter(function (item) {
|
||||
return !self.isWelfareItem(item)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
isWelfareItem: function (item) {
|
||||
var url = ((item && item.url) || "") + "," + ((item && item.h5Url) || "")
|
||||
return url.indexOf("/welfare/") > -1
|
||||
},
|
||||
enterAct: function (item) {
|
||||
if (this.$moment().unix() < this.$moment(item.startDate).unix()) {
|
||||
this.$toast("活动即将开始")
|
||||
return
|
||||
}
|
||||
this.$pjaxReplace(item.h5Url)
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.loadTopImage()
|
||||
this.listActivity()
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
body {
|
||||
background-color: #f4f7fb;
|
||||
}
|
||||
|
||||
.featured-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f4f7fb;
|
||||
padding-bottom: 18px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.featured-page /deep/ .van-nav-bar {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.featured-page /deep/ .van-nav-bar__title {
|
||||
color: #1f2937;
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.featured-page /deep/ .van-nav-bar .van-icon,
|
||||
.featured-page /deep/ .van-nav-bar__text {
|
||||
color: #1989fa;
|
||||
}
|
||||
|
||||
.featured-banner {
|
||||
position: relative;
|
||||
height: 140px;
|
||||
margin: 12px 12px 10px;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
background-color: #e9eef6;
|
||||
box-shadow: 0 8px 22px rgba(31, 41, 55, .1);
|
||||
}
|
||||
|
||||
.featured-banner img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.featured-banner__shade {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(90deg, rgba(0, 0, 0, .42), rgba(0, 0, 0, .08));
|
||||
}
|
||||
|
||||
.featured-banner__text {
|
||||
position: absolute;
|
||||
left: 18px;
|
||||
bottom: 18px;
|
||||
color: #fff;
|
||||
text-shadow: 0 2px 10px rgba(0, 0, 0, .26);
|
||||
}
|
||||
|
||||
.featured-banner__title {
|
||||
font-size: 24px;
|
||||
line-height: 30px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.featured-banner__sub {
|
||||
margin-top: 5px;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
opacity: .92;
|
||||
}
|
||||
|
||||
.featured-list {
|
||||
margin: 0 10px;
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
padding: 8px 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.act-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
padding: 10px 12px;
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.act-item + .act-item {
|
||||
border-top: 1px solid #f0f2f5;
|
||||
}
|
||||
|
||||
.featured-list /deep/ .act-item-wrapper-tag {
|
||||
position: absolute !important;
|
||||
top: 10px !important;
|
||||
left: 12px !important;
|
||||
z-index: 2;
|
||||
width: auto !important;
|
||||
min-width: 42px;
|
||||
height: 20px !important;
|
||||
flex: none !important;
|
||||
padding: 0 6px !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
border-radius: 2px !important;
|
||||
display: inline-flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff !important;
|
||||
background-color: #1f73b7 !important;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
white-space: nowrap !important;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.featured-list /deep/ .act-item-wrapper-tag.van-tag--success {
|
||||
background-color: #1f73b7 !important;
|
||||
}
|
||||
|
||||
.act-item-cover {
|
||||
width: 120px;
|
||||
height: 84px;
|
||||
flex: 0 0 120px;
|
||||
overflow: hidden;
|
||||
border-radius: 4px;
|
||||
background-color: #f2f3f5;
|
||||
}
|
||||
|
||||
.act-item-cover /deep/ .van-image,
|
||||
.act-item-cover /deep/ img {
|
||||
display: block;
|
||||
width: 120px;
|
||||
height: 84px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.act-item-wrapper {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
padding-left: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.content-title {
|
||||
color: #111827;
|
||||
font-size: 15px;
|
||||
line-height: 21px;
|
||||
font-weight: 600;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content-text {
|
||||
margin-top: 9px;
|
||||
color: #6b7280;
|
||||
font-size: 12px;
|
||||
line-height: 17px;
|
||||
}
|
||||
|
||||
.mt5 {
|
||||
margin-top: 5px;
|
||||
}
|
||||
`
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,303 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<div class="festival-page">
|
||||
<van-nav-bar
|
||||
title="节日福利"
|
||||
left-text="返回"
|
||||
left-arrow
|
||||
fixed
|
||||
placeholder
|
||||
@click-left="goBack"
|
||||
></van-nav-bar>
|
||||
|
||||
<div class="festival-banner">
|
||||
<img :src="topImage" alt="">
|
||||
<div class="festival-banner__shade"></div>
|
||||
<div class="festival-banner__text">
|
||||
<div class="festival-banner__title">节日福利</div>
|
||||
<div class="festival-banner__sub">查看可参与的节日福利活动</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="festival-list">
|
||||
<template v-if="activityOptions && activityOptions.length > 0">
|
||||
<div v-for="item in activityOptions" :key="item.id" class="act-item" @click="enterAct(item)">
|
||||
<van-tag class="act-item-wrapper-tag" v-if="$moment().unix() > $moment(item.endDate).unix()">
|
||||
已结束
|
||||
</van-tag>
|
||||
|
||||
<van-tag class="act-item-wrapper-tag" type="success"
|
||||
v-else-if="$moment().unix() > $moment(item.startDate).unix() && $moment().unix() < $moment(item.endDate).unix()">
|
||||
进行中
|
||||
</van-tag>
|
||||
|
||||
<van-tag class="act-item-wrapper-tag" type="success" v-else-if="$moment().unix() < $moment(item.startDate).unix()">
|
||||
即将开始
|
||||
</van-tag>
|
||||
|
||||
<div class="act-item-cover">
|
||||
<van-image v-if="item.cover" width="120" height="84" :src="item.cover"></van-image>
|
||||
</div>
|
||||
<div class="act-item-wrapper">
|
||||
<div class="content-title">{{ item.name }}</div>
|
||||
<div class="content-text">
|
||||
<div>开始时间:{{ $moment(item.startDate).format('MM-DD HH:mm') }}</div>
|
||||
<div class="mt5">结束时间:{{ $moment(item.endDate).format('MM-DD HH:mm') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<van-empty v-if="!activityOptions || activityOptions.length === 0" description="暂无节日福利"></van-empty>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data: function () {
|
||||
return {
|
||||
topImage: "/assets/mobile/img/home/home2.png",
|
||||
activityOptions: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goBack: function () {
|
||||
this.returnHome()
|
||||
},
|
||||
returnHome: function () {
|
||||
if (window.store && typeof window.store.commit === "function") {
|
||||
window.store.commit("setActiveTarBar", "home")
|
||||
}
|
||||
if (!window.$ || !$.support || !$.support.pjax) {
|
||||
window.location.replace("/platform/h5/home")
|
||||
return
|
||||
}
|
||||
var fallbackTimer = window.setTimeout(function () {
|
||||
window.location.replace("/platform/h5/home")
|
||||
}, 1200)
|
||||
$(document).one("pjax:complete", function () {
|
||||
window.clearTimeout(fallbackTimer)
|
||||
})
|
||||
$(document).one("pjax:error pjax:timeout", function () {
|
||||
window.clearTimeout(fallbackTimer)
|
||||
window.location.replace("/platform/h5/home")
|
||||
})
|
||||
$.pjax({
|
||||
url: "/platform/h5/home",
|
||||
container: "#container",
|
||||
fragment: "#container",
|
||||
push: false,
|
||||
replace: true,
|
||||
timeout: 8000
|
||||
})
|
||||
},
|
||||
loadTopImage: function () {
|
||||
var self = this
|
||||
this.$axios.post("/open/common/getConfigKey", { key: "AppFestivalBenefitImg" }).then(function (res) {
|
||||
if (res.code === 0 && res.data) {
|
||||
self.topImage = String(res.data).split(",")[0]
|
||||
}
|
||||
})
|
||||
},
|
||||
listActivity: function () {
|
||||
var self = this
|
||||
this.$axios.post("/platform/home/listHomeActivity").then(function (resp) {
|
||||
if (resp.code === 0) {
|
||||
self.activityOptions = (resp.data || []).filter(function (item) {
|
||||
return self.isWelfareItem(item)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
isWelfareItem: function (item) {
|
||||
var url = ((item && item.url) || "") + "," + ((item && item.h5Url) || "")
|
||||
return url.indexOf("/welfare/") > -1
|
||||
},
|
||||
enterAct: function (item) {
|
||||
if (this.$moment().unix() < this.$moment(item.startDate).unix()) {
|
||||
this.$toast("活动即将开始")
|
||||
return
|
||||
}
|
||||
this.$pjaxReplace(item.h5Url)
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.loadTopImage()
|
||||
this.listActivity()
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
body {
|
||||
background-color: #f4f7fb;
|
||||
}
|
||||
|
||||
.festival-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f4f7fb;
|
||||
padding-bottom: 18px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.festival-page /deep/ .van-nav-bar {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.festival-page /deep/ .van-nav-bar__title {
|
||||
color: #1f2937;
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.festival-page /deep/ .van-nav-bar .van-icon,
|
||||
.festival-page /deep/ .van-nav-bar__text {
|
||||
color: #1989fa;
|
||||
}
|
||||
|
||||
.festival-banner {
|
||||
position: relative;
|
||||
height: 140px;
|
||||
margin: 12px 12px 10px;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
background-color: #e9eef6;
|
||||
box-shadow: 0 8px 22px rgba(31, 41, 55, .1);
|
||||
}
|
||||
|
||||
.festival-banner img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.festival-banner__shade {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(90deg, rgba(0, 0, 0, .42), rgba(0, 0, 0, .08));
|
||||
}
|
||||
|
||||
.festival-banner__text {
|
||||
position: absolute;
|
||||
left: 18px;
|
||||
bottom: 18px;
|
||||
color: #fff;
|
||||
text-shadow: 0 2px 10px rgba(0, 0, 0, .26);
|
||||
}
|
||||
|
||||
.festival-banner__title {
|
||||
font-size: 24px;
|
||||
line-height: 30px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.festival-banner__sub {
|
||||
margin-top: 5px;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
opacity: .92;
|
||||
}
|
||||
|
||||
.festival-list {
|
||||
margin: 0 10px;
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
padding: 8px 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.act-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
padding: 10px 12px;
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.act-item + .act-item {
|
||||
border-top: 1px solid #f0f2f5;
|
||||
}
|
||||
|
||||
.festival-list /deep/ .act-item-wrapper-tag {
|
||||
position: absolute !important;
|
||||
top: 10px !important;
|
||||
left: 12px !important;
|
||||
z-index: 2;
|
||||
width: auto !important;
|
||||
min-width: 42px;
|
||||
height: 20px !important;
|
||||
flex: none !important;
|
||||
padding: 0 6px !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
border-radius: 2px !important;
|
||||
display: inline-flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff !important;
|
||||
background-color: #1f73b7 !important;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
white-space: nowrap !important;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.festival-list /deep/ .act-item-wrapper-tag.van-tag--success {
|
||||
background-color: #1f73b7 !important;
|
||||
}
|
||||
|
||||
.act-item-cover {
|
||||
width: 120px;
|
||||
height: 84px;
|
||||
flex: 0 0 120px;
|
||||
overflow: hidden;
|
||||
border-radius: 4px;
|
||||
background-color: #f2f3f5;
|
||||
}
|
||||
|
||||
.act-item-cover /deep/ .van-image,
|
||||
.act-item-cover /deep/ img {
|
||||
display: block;
|
||||
width: 120px;
|
||||
height: 84px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.act-item-wrapper {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
padding-left: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.content-title {
|
||||
color: #111827;
|
||||
font-size: 15px;
|
||||
line-height: 21px;
|
||||
font-weight: 600;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content-text {
|
||||
margin-top: 9px;
|
||||
color: #6b7280;
|
||||
font-size: 12px;
|
||||
line-height: 17px;
|
||||
}
|
||||
|
||||
.mt5 {
|
||||
margin-top: 5px;
|
||||
}
|
||||
`
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,965 @@
|
||||
const home = {
|
||||
template: /*language=HTML*/ `
|
||||
<div>
|
||||
<div class="home-hero">
|
||||
<img class="home-hero__bg" :src="currentBanner.src" alt="">
|
||||
<div class="home-hero__mask"></div>
|
||||
<div class="home-hero__logo">
|
||||
<img v-if="appLogo" class="home-hero__logo-img" :src="appLogo" alt="">
|
||||
<div v-else class="home-hero__logo-mark"></div>
|
||||
<div class="home-hero__logo-title" :class="{ 'is-placeholder': !appName }">
|
||||
<span v-if="appName">{{ appName }}</span>
|
||||
<template v-else>
|
||||
<i></i>
|
||||
<i></i>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<van-swipe
|
||||
:autoplay="4000"
|
||||
:height="193"
|
||||
class="home__swipe"
|
||||
indicator-color="#e13b4c"
|
||||
@change="bannerChange">
|
||||
<van-swipe-item v-for="banner in bannerList" :key="banner.src">
|
||||
<div class="home__swipe-card">
|
||||
<img :src="banner.src" alt="">
|
||||
</div>
|
||||
</van-swipe-item>
|
||||
</van-swipe>
|
||||
</div>
|
||||
|
||||
<!--蹇嵎鍏ュ彛-->
|
||||
<div class="quick-grid">
|
||||
<div class="section-title">推荐服务</div>
|
||||
<div class="quick-scroll">
|
||||
<div class="quick-scroll-grid"
|
||||
:class="{ 'quick-scroll-grid--single-row': quickEntries.length <= 4 }">
|
||||
<div class="quick-entry-item"
|
||||
v-for="item in quickEntries"
|
||||
@click="menuClick(item)"
|
||||
:key="item.id">
|
||||
<div class="quick-entry-icon">
|
||||
<img v-if="item.picIcon" :src="item.picIcon" alt="">
|
||||
<van-icon v-else name="apps-o" size="28"></van-icon>
|
||||
</div>
|
||||
<div class="quick-entry-name">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--娲诲姩鍒楄〃-->
|
||||
<div class="feature-panel">
|
||||
<div
|
||||
class="feature-card"
|
||||
:class="[item.className, { 'feature-card--large': item.large }]"
|
||||
v-for="item in featuredEntries"
|
||||
:key="item.name"
|
||||
@click="featureClick(item)">
|
||||
<div class="feature-card__text">
|
||||
<div class="feature-card__title">{{ item.name }}</div>
|
||||
<div class="feature-card__desc">{{ item.desc }}</div>
|
||||
</div>
|
||||
<van-icon class="feature-card__icon" :name="item.icon"></van-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="classroom-panel" @click="enterClassroom">
|
||||
<div class="classroom-panel__title">我的课堂</div>
|
||||
<div class="classroom-panel__course">{{ currentClassroomCourseName }}</div>
|
||||
<div class="classroom-panel__action">
|
||||
<span>个人学习</span>
|
||||
<van-icon name="arrow"></van-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="policy-panel">
|
||||
<div class="policy-panel__title">政策文件</div>
|
||||
<div class="policy-panel__action">
|
||||
<span>更多</span>
|
||||
<van-icon name="arrow"></van-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="act-list" v-if="showLatestActivity">
|
||||
<div class="section-title section-title--hidden">最新活动</div>
|
||||
<template v-if="activityOptions && activityOptions.length > 0">
|
||||
<div v-for="item in activityOptions" :key="item.id" class="act-item" @click="enterAct(item)">
|
||||
<van-tag class="act-item-wrapper-tag" v-if="$moment().unix() > $moment(item.endDate).unix()">
|
||||
已结束
|
||||
</van-tag>
|
||||
|
||||
<van-tag class="act-item-wrapper-tag" type="success"
|
||||
v-else-if="$moment().unix() > $moment(item.startDate).unix() && $moment().unix() < $moment(item.endDate).unix()">
|
||||
进行中
|
||||
</van-tag>
|
||||
|
||||
<van-tag class="act-item-wrapper-tag" type="success" v-else-if="$moment().unix() < $moment(item.startDate).unix()">
|
||||
即将开始
|
||||
</van-tag>
|
||||
|
||||
<div class="act-item-cover">
|
||||
<van-image v-if="item.cover" width="120" height="84" :src="item.cover"></van-image>
|
||||
</div>
|
||||
<div class="act-item-wrapper">
|
||||
<div class="content-title">
|
||||
{{item.name}}
|
||||
</div>
|
||||
<div class="content-text">
|
||||
<div>开始时间:{{$moment(item.startDate).format('MM-DD HH:mm')}}</div>
|
||||
<div class="mt5">结束时间:{{$moment(item.endDate).format('MM-DD HH:mm')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<van-empty v-if="!activityOptions || activityOptions.length === 0" description="暂无最新活动"></van-empty>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
activityOptions: [],
|
||||
quickEntries: [],
|
||||
classroomCourses: [],
|
||||
classroomCourseIndex: 0,
|
||||
classroomCourseTimer: null,
|
||||
showLatestActivity: false,
|
||||
featuredEntries: [
|
||||
{
|
||||
name: "精彩活动",
|
||||
desc: "更多精彩活动",
|
||||
icon: "fire-o",
|
||||
className: "feature-card--activity",
|
||||
large: true,
|
||||
href: "/platform/h5/featuredActivity"
|
||||
},
|
||||
{
|
||||
name: "节日福利",
|
||||
desc: "领取节日福利",
|
||||
icon: "gift-o",
|
||||
className: "feature-card--benefit",
|
||||
href: "/platform/h5/festivalBenefit"
|
||||
},
|
||||
{
|
||||
name: "劳模先进",
|
||||
desc: "先进风采展示",
|
||||
icon: "medal-o",
|
||||
className: "feature-card--model",
|
||||
href: "/platform/honor/manage/h5"
|
||||
},
|
||||
{
|
||||
name: "普惠信息",
|
||||
desc: "普惠服务信息",
|
||||
icon: "bullhorn-o",
|
||||
className: "feature-card--inclusive",
|
||||
href: ""
|
||||
}
|
||||
],
|
||||
appLogo: "",
|
||||
appName: "",
|
||||
activeBannerIndex: 0,
|
||||
bannerLoadSeq: 0,
|
||||
bannerImageCache: {},
|
||||
defaultBannerList: [
|
||||
{ src: "/assets/mobile/img/home/home1.png" },
|
||||
{ src: "/assets/mobile/img/home/home2.png" },
|
||||
{ src: "/assets/mobile/img/home/home3.png" }
|
||||
],
|
||||
bannerList: [
|
||||
{ src: "/assets/mobile/img/home/home1.png" },
|
||||
{ src: "/assets/mobile/img/home/home2.png" },
|
||||
{ src: "/assets/mobile/img/home/home3.png" }
|
||||
],
|
||||
homeCachePrefix: "zhgh:h5:home:",
|
||||
homeCacheTtl: {
|
||||
banner: 30 * 60 * 1000,
|
||||
brand: 30 * 60 * 1000,
|
||||
recommend: 10 * 60 * 1000,
|
||||
classroom: 5 * 60 * 1000,
|
||||
activity: 2 * 60 * 1000
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentBanner() {
|
||||
return this.bannerList[this.activeBannerIndex] || this.bannerList[0] || {}
|
||||
},
|
||||
currentClassroomCourseName() {
|
||||
const course = this.classroomCourses[this.classroomCourseIndex]
|
||||
return course && course.courseName ? course.courseName : ""
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getCacheKey(key) {
|
||||
const userId = this.$store && this.$store.state.user ? (this.$store.state.user.id || "anonymous") : "anonymous"
|
||||
return this.homeCachePrefix + userId + ":" + key
|
||||
},
|
||||
readHomeCache(key) {
|
||||
try {
|
||||
const raw = window.localStorage.getItem(this.getCacheKey(key))
|
||||
if (!raw) {
|
||||
return null
|
||||
}
|
||||
const cache = JSON.parse(raw)
|
||||
if (!cache || !cache.expireAt || cache.expireAt < Date.now()) {
|
||||
window.localStorage.removeItem(this.getCacheKey(key))
|
||||
return null
|
||||
}
|
||||
return cache.value
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
},
|
||||
writeHomeCache(key, value, ttl) {
|
||||
try {
|
||||
window.localStorage.setItem(this.getCacheKey(key), JSON.stringify({
|
||||
expireAt: Date.now() + ttl,
|
||||
value: value
|
||||
}))
|
||||
} catch (e) {
|
||||
// Ignore cache quota and private mode errors.
|
||||
}
|
||||
},
|
||||
parseBannerList(value) {
|
||||
if (!value || typeof value !== "string") {
|
||||
return []
|
||||
}
|
||||
return value.split(",")
|
||||
.map(item => item.trim())
|
||||
.filter(item => item)
|
||||
.map(src => ({ src }))
|
||||
},
|
||||
listHomeBanner() {
|
||||
const cached = this.readHomeCache("h5Banner")
|
||||
if (cached && cached.length) {
|
||||
this.bannerList = cached
|
||||
this.activeBannerIndex = 0
|
||||
}
|
||||
this.$axios.post("/open/common/getConfigKey", { key: "H5AppHomeImg" }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const configBannerList = this.parseBannerList(res.data)
|
||||
const nextBannerList = configBannerList.length > 0 ? configBannerList : this.defaultBannerList
|
||||
this.bannerList = nextBannerList
|
||||
this.activeBannerIndex = 0
|
||||
this.writeHomeCache("h5Banner", nextBannerList, this.homeCacheTtl.banner)
|
||||
this.cacheBannerImages(nextBannerList)
|
||||
}
|
||||
}).catch(() => {
|
||||
if (!cached) {
|
||||
this.bannerList = this.defaultBannerList
|
||||
}
|
||||
})
|
||||
},
|
||||
shouldInlineBanner(src) {
|
||||
return typeof src === "string" && src.indexOf("/platform/sys/file/download") !== -1
|
||||
},
|
||||
cacheBannerImages(list) {
|
||||
const seq = ++this.bannerLoadSeq
|
||||
Promise.all(list.map((banner) => {
|
||||
return this.inlineBannerImage(banner.src).then((src) => Object.assign({}, banner, { src }))
|
||||
})).then((cachedList) => {
|
||||
if (seq === this.bannerLoadSeq) {
|
||||
this.bannerList = cachedList
|
||||
this.activeBannerIndex = 0
|
||||
}
|
||||
})
|
||||
},
|
||||
inlineBannerImage(src) {
|
||||
if (!this.shouldInlineBanner(src)) {
|
||||
return Promise.resolve(src)
|
||||
}
|
||||
if (this.bannerImageCache[src]) {
|
||||
return Promise.resolve(this.bannerImageCache[src])
|
||||
}
|
||||
return fetch(src, {
|
||||
credentials: "same-origin",
|
||||
cache: "force-cache"
|
||||
}).then((resp) => {
|
||||
if (!resp.ok) {
|
||||
throw new Error("load image failed")
|
||||
}
|
||||
return resp.blob()
|
||||
}).then((blob) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = () => resolve(reader.result)
|
||||
reader.onerror = reject
|
||||
reader.readAsDataURL(blob)
|
||||
})
|
||||
}).then((dataUrl) => {
|
||||
this.bannerImageCache[src] = dataUrl
|
||||
return dataUrl
|
||||
}).catch(() => src)
|
||||
},
|
||||
getConfigValue(key) {
|
||||
return this.$axios.post("/open/common/getConfigKey", { key }).then((res) => {
|
||||
return res.code === 0 ? (res.data || "") : ""
|
||||
}).catch(() => "")
|
||||
},
|
||||
listHomeBrand() {
|
||||
const cached = this.readHomeCache("brand")
|
||||
if (cached) {
|
||||
this.appLogo = cached.appLogo || ""
|
||||
this.appName = cached.appName || ""
|
||||
}
|
||||
Promise.all([
|
||||
this.getConfigValue("AppLogo"),
|
||||
this.getConfigValue("AppName")
|
||||
]).then(([appLogo, appName]) => {
|
||||
this.appLogo = appLogo
|
||||
this.appName = appName
|
||||
this.writeHomeCache("brand", { appLogo, appName }, this.homeCacheTtl.brand)
|
||||
})
|
||||
},
|
||||
listRecommendApp() {
|
||||
const cached = this.readHomeCache("recommend")
|
||||
if (cached && cached.length) {
|
||||
this.quickEntries = cached
|
||||
}
|
||||
this.$axios.post("/platform/home/listRecommendService", { platform: "H5" }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.quickEntries = res.data
|
||||
this.writeHomeCache("recommend", res.data || [], this.homeCacheTtl.recommend)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
listActivity() {
|
||||
const cached = this.readHomeCache("activity")
|
||||
if (cached && cached.length) {
|
||||
this.activityOptions = cached
|
||||
}
|
||||
this.$axios.post("/platform/home/listHomeActivity").then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.activityOptions = resp.data
|
||||
this.writeHomeCache("activity", resp.data || [], this.homeCacheTtl.activity)
|
||||
}
|
||||
})
|
||||
},
|
||||
listClassroomCourses() {
|
||||
const cached = this.readHomeCache("classroom")
|
||||
if (cached && cached.length) {
|
||||
this.classroomCourses = cached
|
||||
this.classroomCourseIndex = 0
|
||||
this.startClassroomCourseTimer()
|
||||
}
|
||||
this.$axios.post("/platform/learning/course/display/pageData", {
|
||||
pageNumber: 1,
|
||||
pageSize: 20
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const data = res.data || {}
|
||||
this.classroomCourses = data.list || []
|
||||
this.classroomCourseIndex = 0
|
||||
this.writeHomeCache("classroom", this.classroomCourses, this.homeCacheTtl.classroom)
|
||||
this.startClassroomCourseTimer()
|
||||
}
|
||||
}).catch(() => {
|
||||
if (!cached) {
|
||||
this.classroomCourses = []
|
||||
this.classroomCourseIndex = 0
|
||||
}
|
||||
})
|
||||
},
|
||||
startClassroomCourseTimer() {
|
||||
if (this.classroomCourseTimer) {
|
||||
clearInterval(this.classroomCourseTimer)
|
||||
this.classroomCourseTimer = null
|
||||
}
|
||||
if (this.classroomCourses.length <= 1) {
|
||||
return
|
||||
}
|
||||
this.classroomCourseTimer = setInterval(() => {
|
||||
this.classroomCourseIndex = (this.classroomCourseIndex + 1) % this.classroomCourses.length
|
||||
}, 3000)
|
||||
},
|
||||
menuClick(item) {
|
||||
this.$pjaxReplace(item.href)
|
||||
},
|
||||
featureClick(item) {
|
||||
if (item.href) {
|
||||
this.$pjaxReplace(item.href)
|
||||
}
|
||||
},
|
||||
enterClassroom() {
|
||||
this.$pjaxReplace("/platform/learning/course/h5")
|
||||
},
|
||||
bannerChange(index) {
|
||||
this.activeBannerIndex = index
|
||||
},
|
||||
//鐐瑰嚮杩涘叆娲诲姩
|
||||
enterAct(item) {
|
||||
if(this.$moment().unix() < this.$moment(item.startDate).unix()) {
|
||||
this.$toast("活动即将开始")
|
||||
return
|
||||
}
|
||||
this.$pjaxReplace(item.h5Url)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.listHomeBanner()
|
||||
this.listHomeBrand()
|
||||
this.listRecommendApp()
|
||||
this.listClassroomCourses()
|
||||
if (this.showLatestActivity) {
|
||||
this.listActivity()
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.classroomCourseTimer) {
|
||||
clearInterval(this.classroomCourseTimer)
|
||||
this.classroomCourseTimer = null
|
||||
}
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
.home-hero {
|
||||
position: relative;
|
||||
min-height: 281px;
|
||||
overflow: hidden;
|
||||
padding: 26px 16px 0;
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.home-hero::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 187px;
|
||||
z-index: 1;
|
||||
width: 180%;
|
||||
height: 96px;
|
||||
transform: translateX(-50%);
|
||||
background-color: #fff;
|
||||
border-radius: 50% 50% 0 0 / 100% 100% 0 0;
|
||||
}
|
||||
|
||||
.home-hero__bg {
|
||||
position: absolute;
|
||||
left: -24px;
|
||||
top: -24px;
|
||||
width: calc(100% + 48px);
|
||||
height: 239px;
|
||||
object-fit: cover;
|
||||
filter: blur(20px);
|
||||
transform: scale(1.12);
|
||||
opacity: .88;
|
||||
transition: opacity .25s ease;
|
||||
}
|
||||
|
||||
.home-hero__mask {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 239px;
|
||||
background: rgba(0, 0, 0, .06);
|
||||
}
|
||||
|
||||
.home-hero__logo {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 36px;
|
||||
margin-bottom: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.home-hero__logo-mark {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border: 1px solid rgba(255, 255, 255, .72);
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
background: rgba(255, 255, 255, .2);
|
||||
backdrop-filter: blur(2px);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.home-hero__logo-img {
|
||||
display: block;
|
||||
max-width: 210px;
|
||||
max-height: 36px;
|
||||
object-fit: contain;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.home-hero__logo-title {
|
||||
min-width: 0;
|
||||
margin-left: 10px;
|
||||
color: #fff;
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
line-height: 22px;
|
||||
text-shadow: 0 1px 8px rgba(0, 0, 0, .24);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.home-hero__logo-title.is-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
width: 154px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.home-hero__logo-title i {
|
||||
display: block;
|
||||
height: 10px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, .72);
|
||||
}
|
||||
|
||||
.home-hero__logo-title i + i {
|
||||
width: 66%;
|
||||
height: 7px;
|
||||
opacity: .75;
|
||||
}
|
||||
|
||||
.home__swipe {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
overflow: visible;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.home__swipe-card {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
border-radius: 12px;
|
||||
background-color: rgba(255, 255, 255, .26);
|
||||
box-shadow: 0 10px 28px rgba(0, 0, 0, .1);
|
||||
}
|
||||
|
||||
.home__swipe-card img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
.home__swipe /deep/ .van-swipe__track,
|
||||
.home__swipe /deep/ .van-swipe-item {
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.home__swipe /deep/ .van-swipe__indicators {
|
||||
bottom: -14px;
|
||||
}
|
||||
|
||||
.home__swipe /deep/ .van-swipe__indicator {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
background-color: rgba(225, 59, 76, .28);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.home__swipe /deep/ .van-swipe__indicator--active {
|
||||
background-color: #e13b4c;
|
||||
}
|
||||
|
||||
.quick-grid {
|
||||
background-color: #fff;
|
||||
margin: 10px 10px 10px;
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.quick-scroll {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.quick-scroll::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.quick-scroll-grid {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-template-rows: repeat(2, 82px);
|
||||
grid-auto-columns: 25%;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
/* 推荐服务不超过四项时横向排列,避免少量服务按两行纵向展示。 */
|
||||
.quick-scroll-grid--single-row {
|
||||
grid-auto-flow: row;
|
||||
grid-template-rows: 82px;
|
||||
grid-template-columns: repeat(4, 25%);
|
||||
}
|
||||
|
||||
.quick-entry-item {
|
||||
min-width: 0;
|
||||
height: 82px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4px 2px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.quick-entry-item:active {
|
||||
background-color: #f2f7ff;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.quick-entry-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 7px;
|
||||
color: #1989fa;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.quick-entry-icon img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.quick-entry-name {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 90%;
|
||||
text-align: center;
|
||||
color: #323233;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.feature-panel {
|
||||
margin: 10px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
grid-auto-rows: 82px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 10px;
|
||||
padding: 14px 12px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
min-width: 0;
|
||||
box-shadow: 0 6px 18px rgba(42, 65, 92, .08);
|
||||
}
|
||||
|
||||
.feature-card:active {
|
||||
transform: scale(.985);
|
||||
}
|
||||
|
||||
.feature-card--large {
|
||||
grid-column: span 2;
|
||||
grid-row: span 2;
|
||||
}
|
||||
|
||||
.feature-card__text {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.feature-card__title {
|
||||
font-size: 17px;
|
||||
line-height: 22px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 5px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.feature-card__desc {
|
||||
color: rgba(55, 65, 81, .64);
|
||||
font-size: 12px;
|
||||
line-height: 17px;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.feature-card__icon {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
bottom: 12px;
|
||||
font-size: 36px;
|
||||
opacity: .78;
|
||||
}
|
||||
|
||||
.feature-card--large .feature-card__icon {
|
||||
right: 18px;
|
||||
bottom: 18px;
|
||||
font-size: 56px;
|
||||
}
|
||||
|
||||
.feature-card--benefit,
|
||||
.feature-card--model {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.feature-card--benefit .feature-card__desc,
|
||||
.feature-card--model .feature-card__desc {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.feature-card--benefit .feature-card__title,
|
||||
.feature-card--model .feature-card__title {
|
||||
font-size: 15px;
|
||||
line-height: 20px;
|
||||
margin-bottom: 0;
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
.feature-card--benefit .feature-card__icon,
|
||||
.feature-card--model .feature-card__icon {
|
||||
position: static;
|
||||
display: block;
|
||||
margin: 8px auto 0;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.feature-card--inclusive .feature-card__icon {
|
||||
font-size: 44px;
|
||||
}
|
||||
|
||||
.feature-card--activity {
|
||||
background: linear-gradient(145deg, #fff2e5 0%, #ffe2c8 100%);
|
||||
color: #ff7a25;
|
||||
}
|
||||
|
||||
.feature-card--benefit {
|
||||
background: linear-gradient(145deg, #fff1f4 0%, #ffd5dc 100%);
|
||||
color: #f35f74;
|
||||
}
|
||||
|
||||
.feature-card--model {
|
||||
background: linear-gradient(145deg, #eef5ff 0%, #d9e8ff 100%);
|
||||
color: #5b83d7;
|
||||
}
|
||||
|
||||
.feature-card--inclusive {
|
||||
grid-column: span 2;
|
||||
background: linear-gradient(145deg, #f2eeff 0%, #ded4ff 100%);
|
||||
color: #8063dc;
|
||||
}
|
||||
|
||||
.classroom-panel {
|
||||
height: 66px;
|
||||
margin: 10px;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-radius: 10px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 8px 22px rgba(42, 65, 92, .07);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.classroom-panel:active {
|
||||
transform: scale(.99);
|
||||
}
|
||||
|
||||
.classroom-panel__title {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
color: #202124;
|
||||
font-size: 15px;
|
||||
line-height: 21px;
|
||||
}
|
||||
|
||||
.classroom-panel__title::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -5px;
|
||||
width: 22px;
|
||||
height: 2px;
|
||||
border-radius: 999px;
|
||||
background-color: #e13b4c;
|
||||
}
|
||||
|
||||
.classroom-panel__course {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
margin: 0 12px;
|
||||
color: #1989fa;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.classroom-panel__action {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
color: #969799;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.classroom-panel__action .van-icon {
|
||||
flex-shrink: 0;
|
||||
margin-left: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.policy-panel {
|
||||
height: 66px;
|
||||
margin: 10px;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-radius: 10px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 8px 22px rgba(42, 65, 92, .07);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.policy-panel__title {
|
||||
position: relative;
|
||||
color: #202124;
|
||||
font-size: 15px;
|
||||
line-height: 21px;
|
||||
}
|
||||
|
||||
.policy-panel__title::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -5px;
|
||||
width: 22px;
|
||||
height: 2px;
|
||||
border-radius: 999px;
|
||||
background-color: #e13b4c;
|
||||
}
|
||||
|
||||
.policy-panel__action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #969799;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.policy-panel__action .van-icon {
|
||||
margin-left: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.act-list {
|
||||
background-color: #fff;
|
||||
margin: 10px;
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.act-list .act-item {
|
||||
width: 100%;
|
||||
padding: 6px;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.act-list .act-item-cover {
|
||||
margin: 0 14px 0 0;
|
||||
width: 120px;
|
||||
height: 84px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.act-list .act-item-cover .van-image{
|
||||
/*width: 120px;*/
|
||||
/*height: 84px;*/
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.act-list .act-item-wrapper{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.act-list /deep/ .act-item-wrapper-tag{
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.act-item-wrapper .content-title{
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.act-item-wrapper .content-text{
|
||||
font-size: 12px;
|
||||
color: grey;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.section-title{
|
||||
padding: 0 0 5px 5px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.section-title--hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/deep/ .home__swipe img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
/deep/ .act-item-cover img {
|
||||
border-radius: 8px;
|
||||
}
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.profile-card{
|
||||
background: #0a84ff;
|
||||
color: #0b5a2c;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<div>
|
||||
<div>
|
||||
<home v-if="homeTabbarActive === 'home'" @module-click="moduleClick"></home>
|
||||
<todo v-if="homeTabbarActive === 'todo'"></todo>
|
||||
<work v-if="homeTabbarActive === 'work'"></work>
|
||||
<mine v-if="homeTabbarActive === 'mine'"></mine>
|
||||
<apps v-if="homeTabbarActive === 'apps'"></apps>
|
||||
<msg v-if="homeTabbarActive === 'msg'"></msg>
|
||||
</div>
|
||||
|
||||
<div id="page-tarbar" class="page-tarbar">
|
||||
<van-tabbar v-model="homeTabbarActive" @change="homeTabbarChange">
|
||||
<van-tabbar-item name="apps" icon="apps-o">应用</van-tabbar-item>
|
||||
<!-- <van-tabbar-item name="work" icon="gem">工作台</van-tabbar-item>-->
|
||||
<van-tabbar-item name="todo" icon="records-o">待办</van-tabbar-item>
|
||||
<van-tabbar-item name="home" icon="wap-home-o">首页</van-tabbar-item>
|
||||
<van-tabbar-item name="msg" icon="envelop-o">消息</van-tabbar-item>
|
||||
<van-tabbar-item name="mine" icon="user-o">我的</van-tabbar-item>
|
||||
</van-tabbar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include("home.js"){}#-->
|
||||
<!--#include("work.js"){}#-->
|
||||
<!--#include("mine.js"){}#-->
|
||||
<!--#include("todo.js"){}#-->
|
||||
<!--#include("apps.js"){}#-->
|
||||
<!--#include("msg.js"){}#-->
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
components: {
|
||||
home,
|
||||
mine,
|
||||
work,
|
||||
todo,
|
||||
apps,
|
||||
msg
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
homeTabbarActive: "home"
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
homeTabbarChange(val) {
|
||||
this.$store.commit("setActiveTarBar", val)
|
||||
},
|
||||
moduleClick(val) {
|
||||
console.log(val)
|
||||
this.homeTabbarActive = "work"
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (!this.$store.state.activeTarBar) {
|
||||
this.$store.commit("setActiveTarBar", "home")
|
||||
} else {
|
||||
this.homeTabbarActive = this.$store.state.activeTarBar
|
||||
}
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
|
||||
`
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,372 @@
|
||||
const mine = {
|
||||
template: /*language=HTML*/ `
|
||||
<div class="home-mine">
|
||||
<van-nav-bar title="个人中心" class="nav-bar"></van-nav-bar>
|
||||
|
||||
<div class="profile-card">
|
||||
<div class="profile-header">
|
||||
<div class="avatar-section">
|
||||
<div class="avatar-wrapper">
|
||||
<van-image :src="avatar" round width="80" height="80" class="avatar">
|
||||
<van-loading slot="loading" type="spinner" size="20"></van-loading>
|
||||
</van-image>
|
||||
<div class="status-badge">
|
||||
<div class="status-dot"></div>
|
||||
<span>{{ $store.state.user.userState }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="basic-info">
|
||||
<div class="name-row">
|
||||
<h2 class="username">{{ $store.state.user.username }}</h2>
|
||||
<div class="gender-tag">
|
||||
<van-icon name="friends-o" class="gender-icon"></van-icon>
|
||||
<span>{{ $store.state.user.sex }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="employee-id">
|
||||
工号:
|
||||
<span class="id-value">{{ $store.state.user.loginname }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-list">
|
||||
<!-- <div class="info-item">
|
||||
<van-icon name="user-o"></van-icon>
|
||||
<span class="item-label">人员类型</span>
|
||||
<span class="info-value">{{ $store.state.user.personType }}</span>
|
||||
</div>-->
|
||||
<div class="info-item">
|
||||
<van-icon name="user-o"></van-icon>
|
||||
<span class="item-label">在职状态</span>
|
||||
<span class="info-value">{{ $store.state.user.userState }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<van-icon name="wap-home-o"></van-icon>
|
||||
<span class="item-label">所属单位</span>
|
||||
<span class="info-value">{{ $store.state.user.unit.name }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<van-icon name="wap-home-o"></van-icon>
|
||||
<span class="item-label">所属工会</span>
|
||||
<span class="info-value">{{ $store.state.user.union.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-cards">
|
||||
<van-cell-group>
|
||||
<van-cell is-link @click="$pjaxReplace('/platform/h5/indexMine/userInfo')" class="action-cell">
|
||||
<span class="cell-title" slot="title">
|
||||
<van-icon name="user-o" class="cell-icon"></van-icon>
|
||||
个人信息
|
||||
</span>
|
||||
</van-cell>
|
||||
|
||||
<van-cell is-link @click="$pjaxReplace('/platform/signature/h5')" class="action-cell">
|
||||
<span class="cell-title" slot="title">
|
||||
<van-icon name="edit" class="cell-icon"></van-icon>
|
||||
我的签名
|
||||
</span>
|
||||
</van-cell>
|
||||
|
||||
<!-- <van-cell :value="$store.state.user.totalIntegral" is-link @click="$pjaxReplace('/platform/integral/records/h5')" class="action-cell">-->
|
||||
<!-- <span class="cell-title" slot="title">-->
|
||||
<!-- <van-icon name="points" class="cell-icon"></van-icon>-->
|
||||
<!-- 我的积分-->
|
||||
<!-- </span>-->
|
||||
<!-- </van-cell>-->
|
||||
</van-cell-group>
|
||||
</div>
|
||||
|
||||
<div class="logout-wrapper">
|
||||
<van-button round block color="#ee0a24" @click="logout" class="logout-btn">退出登录</van-button>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
computed: {
|
||||
avatar() {
|
||||
const sex = this.$store.state.user.sex
|
||||
if (sex === "男" || !sex) {
|
||||
return "/assets/platform/img/home/avatar_boy.png"
|
||||
} else {
|
||||
return "/assets/platform/img/home/avatar_girl.png"
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
logout() {
|
||||
this.$dialog
|
||||
.confirm({
|
||||
title: "温馨提示",
|
||||
message: "您确认要退出登录吗?"
|
||||
})
|
||||
.then(() => {
|
||||
this.$store.dispatch("logout")
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
style() {
|
||||
return /*language=CSS*/ `
|
||||
.home-mine {
|
||||
background: #f7f8fa;
|
||||
}
|
||||
|
||||
.profile-card {
|
||||
margin: 10px;
|
||||
padding: 20px;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
|
||||
animation: slideIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
.profile-header {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 20px;
|
||||
animation: slideIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
.avatar-section {
|
||||
flex-shrink: 0;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.avatar-wrapper {
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
border: 2px solid #f5f5f5;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
right: -2px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: #34c759;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
z-index: 1;
|
||||
animation: fadeInScale 0.3s ease-out 0.2s backwards;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: #34c759;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.basic-info {
|
||||
flex: 1;
|
||||
padding-top: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
animation: slideLeft 0.3s ease-out;
|
||||
}
|
||||
|
||||
.username {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.gender-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
background: #f5f5f5;
|
||||
padding: 2px 10px;
|
||||
border-radius: 20px;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.employee-id {
|
||||
color: #666;
|
||||
font-size: 15px;
|
||||
animation: slideLeft 0.3s ease-out 0.1s backwards;
|
||||
}
|
||||
|
||||
.id-value {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.info-list {
|
||||
margin-top: 20px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #f5f5f5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
animation: slideLeft 0.3s ease-out;
|
||||
transition: background-color 0.3s;
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.info-item:active {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
color: #999;
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.info-item .van-icon {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.action-cards {
|
||||
margin: 10px;
|
||||
animation: slideUp 0.3s ease-out;
|
||||
}
|
||||
|
||||
.action-cell {
|
||||
padding: 16px !important;
|
||||
}
|
||||
|
||||
.cell-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 15px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.cell-icon {
|
||||
font-size: 18px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.logout-wrapper {
|
||||
padding: 16px;
|
||||
margin-top: 20px;
|
||||
animation: fadeIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
height: 44px;
|
||||
font-size: 16px;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.logout-btn:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 动画定义 */
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideLeft {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInScale {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* 为每个列表项添加延迟动画 */
|
||||
.info-item:nth-child(1) {
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
.info-item:nth-child(2) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.info-item:nth-child(3) {
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.info-item:nth-child(4) {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,599 @@
|
||||
const msg= {
|
||||
template: /*language=HTML*/ `
|
||||
<div class="message-center">
|
||||
<van-nav-bar title="消息中心" placeholder fixed></van-nav-bar>
|
||||
|
||||
<!-- 筛选区域 -->
|
||||
<van-sticky offset-top="46px">
|
||||
<!-- <div class="filter-section">-->
|
||||
<!-- <van-row gutter="10">-->
|
||||
<!-- <van-col span="12">-->
|
||||
<!-- <van-field-->
|
||||
<!-- v-model="filters.type"-->
|
||||
<!-- is-link-->
|
||||
<!-- readonly-->
|
||||
<!-- label="消息类型"-->
|
||||
<!-- placeholder="选择消息类型"-->
|
||||
<!-- @click="showTypePicker = true"-->
|
||||
<!-- />-->
|
||||
<!-- </van-col>-->
|
||||
<!-- <van-col span="12" v-if="pageForm.isRead == 0">-->
|
||||
<!-- <van-button -->
|
||||
<!-- type="primary" -->
|
||||
<!-- size="small" -->
|
||||
<!-- :loading="loading"-->
|
||||
<!-- @click="markAllAsRead"-->
|
||||
<!-- style="margin-top: 6px; width: 100%;"-->
|
||||
<!-- >-->
|
||||
<!-- 全部已读-->
|
||||
<!-- </van-button>-->
|
||||
<!-- </van-col>-->
|
||||
<!-- </van-row>-->
|
||||
<!-- </div>-->
|
||||
<!-- 标签页 -->
|
||||
<van-tabs v-model="activeTab" @change="handleTabChange">
|
||||
<van-tab title="全部消息" name="-1">
|
||||
<template #title>
|
||||
<van-icon name="chat-o" />
|
||||
全部消息 ({{stats.total}})
|
||||
</template>
|
||||
</van-tab>
|
||||
<van-tab title="未读消息" name="0">
|
||||
<template #title>
|
||||
<van-icon name="bell" />
|
||||
未读消息 ({{stats.unread}})
|
||||
</template>
|
||||
</van-tab>
|
||||
<van-tab title="已读消息" name="1">
|
||||
<template #title>
|
||||
<van-icon name="passed" />
|
||||
已读消息 ({{stats.read}})
|
||||
</template>
|
||||
</van-tab>
|
||||
</van-tabs>
|
||||
</van-sticky>
|
||||
|
||||
<!-- 消息列表 -->
|
||||
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
|
||||
<van-list
|
||||
style="padding: 10px"
|
||||
v-model="loading"
|
||||
:finished="finished"
|
||||
finished-text="没有更多了"
|
||||
@load="onLoad"
|
||||
>
|
||||
<div
|
||||
v-for="message in messages"
|
||||
:key="message.id"
|
||||
@click="viewMessage(message)"
|
||||
class="custom-message-item"
|
||||
:class="{'unread-message': !message.isRead}"
|
||||
>
|
||||
<div class="message-header">
|
||||
<div class="message-title-wrapper">
|
||||
<van-tag
|
||||
:type="message.type === 1 ? 'danger' : 'primary'"
|
||||
size="mini"
|
||||
style="margin-right: 8px;"
|
||||
>
|
||||
{{getMessageTypeName(message.type)}}
|
||||
</van-tag>
|
||||
<!-- <span class="message-title">{{message.title}}</span>-->
|
||||
<van-tag
|
||||
v-if="!message.isRead"
|
||||
type="warning"
|
||||
size="mini"
|
||||
style="margin-left: 8px;"
|
||||
>
|
||||
未读
|
||||
</van-tag>
|
||||
</div>
|
||||
<div class="message-time">{{formatTime(message.createdAt)}}</div>
|
||||
</div>
|
||||
<div class="message-content">{{getContentPreview(message.content)}}</div>
|
||||
<div class="message-arrow">
|
||||
<van-icon name="arrow" />
|
||||
</div>
|
||||
</div>
|
||||
</van-list>
|
||||
</van-pull-refresh>
|
||||
|
||||
<!-- 消息类型选择器 -->
|
||||
<van-popup v-model="showTypePicker" position="bottom">
|
||||
<van-picker
|
||||
:columns="typeColumns"
|
||||
@confirm="onTypeConfirm"
|
||||
@cancel="showTypePicker = false"
|
||||
/>
|
||||
</van-popup>
|
||||
|
||||
<!-- 消息详情弹窗 -->
|
||||
<van-popup
|
||||
v-model="detailVisible"
|
||||
position="bottom"
|
||||
:style="{ height: '80%' }"
|
||||
closeable
|
||||
close-icon-position="top-right"
|
||||
>
|
||||
<div class="message-detail" v-if="currentMessage.id">
|
||||
<div class="detail-header">
|
||||
<h3>{{currentMessage.title}}</h3>
|
||||
<div class="detail-meta">
|
||||
<van-tag :type="currentMessage?.globalMessage?.type === 1 ? 'danger' : 'primary'">
|
||||
{{getMessageTypeName(currentMessage?.globalMessage?.type)}}
|
||||
</van-tag>
|
||||
<span class="detail-time">{{formatTime(currentMessage.createdAt)}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-content" v-html="currentMessage?.globalMessage?.content"></div>
|
||||
</div>
|
||||
</van-popup>
|
||||
</div>
|
||||
`,
|
||||
data(){
|
||||
return{
|
||||
activeTab: '-1',
|
||||
loading: false,
|
||||
refreshing: false,
|
||||
finished: false,
|
||||
messages: [],
|
||||
stats: {
|
||||
total: 0,
|
||||
unread: 0,
|
||||
read: 0
|
||||
},
|
||||
filters: {
|
||||
type: ''
|
||||
},
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
isRead: -1
|
||||
},
|
||||
detailVisible: false,
|
||||
currentMessage: {},
|
||||
showTypePicker: false,
|
||||
typeColumns: [
|
||||
{ text: '全部类型', value: '' },
|
||||
{ text: '系统公告', value: '1' },
|
||||
{ text: '消息通知', value: '2' }
|
||||
]
|
||||
}
|
||||
},
|
||||
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(isRefresh = false) {
|
||||
if (isRefresh) {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.messages = []
|
||||
this.finished = false
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
try {
|
||||
const resp = await this.$axios.post('/platform/v4/msg/pageData', this.pageForm)
|
||||
if (resp.code === 0) {
|
||||
const newMessages = resp.data.list || []
|
||||
if (isRefresh) {
|
||||
this.messages = newMessages
|
||||
} else {
|
||||
this.messages = [...this.messages, ...newMessages]
|
||||
}
|
||||
|
||||
this.pageForm.totalCount = resp.data.totalCount
|
||||
|
||||
// 判断是否还有更多数据
|
||||
if (this.messages.length >= this.pageForm.totalCount) {
|
||||
this.finished = true
|
||||
}
|
||||
} else {
|
||||
this.$toast(resp.msg)
|
||||
}
|
||||
} catch (error) {
|
||||
this.$toast('加载消息失败')
|
||||
} finally {
|
||||
this.loading = false
|
||||
this.refreshing = false
|
||||
}
|
||||
},
|
||||
|
||||
// 查看消息详情
|
||||
async viewMessage(message) {
|
||||
try {
|
||||
const resp = await this.$axios.post(`/platform/v4/msg/detail/` + message.id)
|
||||
if (resp.code === 0) {
|
||||
this.currentMessage = resp.data
|
||||
this.detailVisible = true
|
||||
|
||||
// 如果是未读消息,标记为已读
|
||||
if (!message.isRead) {
|
||||
await this.markAsRead(message.id)
|
||||
message.isRead = true
|
||||
this.loadStats()
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.$toast('加载消息详情失败')
|
||||
}
|
||||
},
|
||||
|
||||
// 标记单条消息为已读
|
||||
async markAsRead(messageId) {
|
||||
try {
|
||||
await this.$axios.post(`/platform/v4/msg/read/` + messageId)
|
||||
} catch (error) {
|
||||
console.error('标记已读失败:', error)
|
||||
}
|
||||
},
|
||||
|
||||
// 全部标记为已读
|
||||
async markAllAsRead() {
|
||||
this.$dialog.confirm({
|
||||
title: '提示',
|
||||
message: '确定要将所有未读消息标记为已读吗?'
|
||||
}).then(async () => {
|
||||
this.loading = true
|
||||
try {
|
||||
const resp = await this.$axios.post('/platform/v4/msg/read/all')
|
||||
if (resp.code === 0) {
|
||||
this.$toast.success('操作成功')
|
||||
this.loadMessages(true)
|
||||
this.loadStats()
|
||||
} else {
|
||||
this.$toast(resp.msg)
|
||||
}
|
||||
} catch (error) {
|
||||
this.$toast('操作失败')
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
}).catch(() => {
|
||||
// 用户取消
|
||||
})
|
||||
},
|
||||
|
||||
// 切换标签页
|
||||
handleTabChange(name) {
|
||||
this.pageForm.isRead = parseInt(name)
|
||||
this.loadMessages(true)
|
||||
},
|
||||
|
||||
// 点击统计卡片切换标签
|
||||
switchTab(tabValue) {
|
||||
this.activeTab = tabValue.toString()
|
||||
this.pageForm.isRead = tabValue
|
||||
this.loadMessages(true)
|
||||
},
|
||||
|
||||
// 下拉刷新
|
||||
onRefresh() {
|
||||
this.loadMessages(true)
|
||||
this.loadStats()
|
||||
},
|
||||
|
||||
// 上拉加载更多
|
||||
onLoad() {
|
||||
if (this.finished) {
|
||||
return
|
||||
}
|
||||
this.pageForm.pageNumber++
|
||||
this.loadMessages()
|
||||
},
|
||||
|
||||
// 消息类型选择确认
|
||||
onTypeConfirm(value) {
|
||||
this.filters.type = value.value
|
||||
this.showTypePicker = false
|
||||
this.loadMessages(true)
|
||||
},
|
||||
|
||||
// 获取消息类型名称
|
||||
getMessageTypeName(type) {
|
||||
return type === 1 ? '系统公告' : '消息通知'
|
||||
},
|
||||
|
||||
// 获取内容预览
|
||||
getContentPreview(content) {
|
||||
if (!content) return ''
|
||||
// 移除HTML标签
|
||||
const text = content.replace(/<[^>]*>/g, '')
|
||||
return text.length > 60 ? text.substring(0, 30) + '...' : text
|
||||
},
|
||||
|
||||
// 格式化时间
|
||||
formatTime(timestamp) {
|
||||
return this.$moment(timestamp).format('MM-DD HH:mm')
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.loadStats()
|
||||
this.loadMessages(true)
|
||||
},
|
||||
|
||||
style: /*language=CSS*/ `
|
||||
.message-center {
|
||||
background-color: #f5f7fa;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 自定义消息列表项样式 */
|
||||
/deep/ .custom-message-item {
|
||||
background-color: #fff;
|
||||
padding: 10px;
|
||||
margin-bottom: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
position: relative;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/deep/ .custom-message-item:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
/deep/ .custom-message-item:active {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
/deep/ .message-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
/deep/ .message-time {
|
||||
font-size: 12px;
|
||||
color: #969799;
|
||||
flex-shrink: 0;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
/deep/ .message-content {
|
||||
font-size: 14px;
|
||||
color: #646566;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 8px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
/deep/ .message-arrow {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #c8c9cc;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 筛选区域样式 */
|
||||
/deep/ .filter-section {
|
||||
padding: 12px 16px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/deep/ .message-title-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/deep/ .message-title {
|
||||
flex: 1;
|
||||
/*font-weight: 500;*/
|
||||
color: #323233;
|
||||
}
|
||||
|
||||
/* 消息详情弹窗样式 */
|
||||
/deep/ .message-detail {
|
||||
padding: 20px;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/deep/ .detail-header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
/deep/ .detail-header h3 {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
margin: 0 0 8px 0;
|
||||
color: #323233;
|
||||
}
|
||||
|
||||
/deep/ .detail-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/deep/ .detail-time {
|
||||
color: #969799;
|
||||
}
|
||||
|
||||
/deep/ .detail-content {
|
||||
line-height: 1.8;
|
||||
font-size: 15px;
|
||||
color: #323233;
|
||||
}
|
||||
|
||||
/deep/ .detail-content img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 4px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
/deep/ .detail-content p {
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
/* 标签页样式优化 */
|
||||
/deep/ .van-tabs__nav {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/deep/ .van-tab {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/deep/ .van-tab--active {
|
||||
color: var(--color-primary, #1989fa);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/deep/ .van-tabs__line {
|
||||
background-color: var(--color-primary, #1989fa);
|
||||
}
|
||||
|
||||
/* 列表样式优化 */
|
||||
/deep/ .van-cell__title {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
/deep/ .van-cell__label {
|
||||
font-size: 13px;
|
||||
color: #969799;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/deep/ .van-cell__value {
|
||||
font-size: 12px;
|
||||
color: #969799;
|
||||
}
|
||||
|
||||
/* 标签样式 */
|
||||
/deep/ .van-tag--mini {
|
||||
font-size: 10px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* 弹窗样式 */
|
||||
/deep/ .van-popup--bottom {
|
||||
border-radius: 16px 16px 0 0;
|
||||
}
|
||||
|
||||
/* 下拉刷新和上拉加载样式 */
|
||||
/deep/ .van-pull-refresh__track {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
/deep/ .van-list__finished-text {
|
||||
color: #969799;
|
||||
font-size: 12px;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
/* 空状态样式 */
|
||||
/deep/ .van-empty {
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
/deep/ .van-empty__description {
|
||||
color: #969799;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 简约动画效果 */
|
||||
|
||||
/* 基础过渡效果 */
|
||||
/deep/ .oa-message-item,
|
||||
/deep/ .oa-stat-item,
|
||||
/deep/ .oa-refresh-btn,
|
||||
/deep/ .oa-search-input {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
/* 页面进入动画 */
|
||||
/deep/ .page-enter {
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 简化的响应式动画 */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 480px) {
|
||||
/deep/ .oa-header {
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
/deep/ .oa-header-title {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
/deep/ .oa-stats-panel,
|
||||
/deep/ .oa-toolbar,
|
||||
/deep/ .oa-message-list {
|
||||
margin: 6px 12px;
|
||||
}
|
||||
|
||||
/deep/ .oa-stat-item {
|
||||
padding: 12px 8px;
|
||||
}
|
||||
|
||||
/deep/ .oa-stat-number {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/deep/ .oa-stat-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/deep/ .oa-message-item {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
/deep/ .oa-message-title {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/deep/ .oa-message-content {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
const todo = {
|
||||
template: /*language=HTML*/ `
|
||||
<div class="task-center">
|
||||
<van-nav-bar title="待办中心" placeholder fixed></van-nav-bar>
|
||||
<!-- 标签页 -->
|
||||
<van-sticky offset-top="46px">
|
||||
<van-tabs v-model="activeTab" @change="handleTabChange" animated swipeable>
|
||||
<van-tab :title="'待办(' + todoCount + ')'" name="todo"></van-tab>
|
||||
<van-tab :title="'已办(' + doneCount + ')'" name="done"></van-tab>
|
||||
<van-tab :title="'发起(' + startedCount + ')'" name="started"></van-tab>
|
||||
</van-tabs>
|
||||
<!-- 搜索筛选区域 -->
|
||||
<div class="filter-section">
|
||||
<div>
|
||||
<div class="search-form">
|
||||
<van-search v-model="pageForm.searchKeyword" placeholder="请输入搜索关键词"
|
||||
@search="doSearch"></van-search>
|
||||
</div>
|
||||
|
||||
<!-- <van-field-->
|
||||
<!-- readonly-->
|
||||
<!-- clickable-->
|
||||
<!-- :value="categoryLabel"-->
|
||||
<!-- placeholder="选择流程分类"-->
|
||||
<!-- class="category-select"-->
|
||||
<!-- @click="showCategoryPicker = true"-->
|
||||
<!-- ></van-field>-->
|
||||
</div>
|
||||
|
||||
<van-popup v-model="showCategoryPicker" round position="bottom">
|
||||
<van-picker
|
||||
show-toolbar
|
||||
:columns="categoryOptions"
|
||||
value-key="name"
|
||||
@confirm="onCategoryConfirm"
|
||||
@cancel="showCategoryPicker = false"
|
||||
></van-picker>
|
||||
</van-popup>
|
||||
</div>
|
||||
</van-sticky>
|
||||
|
||||
<!-- 任务列表区域 -->
|
||||
<table-list :api="'/flow/todoCenter/'+ activeTab" :page_form.sync="pageForm"
|
||||
ref="tableListRef"
|
||||
@ready="doSearch"
|
||||
title="variable.instanceName">
|
||||
<template v-slot="{index,row}">
|
||||
<table-column label="任务节点">{{row.taskName}}</table-column>
|
||||
<table-column label="流程分类">{{row.categoryName}}</table-column>
|
||||
<table-column label="申请人">{{row.variable?.initiatorName || '未知'}}</table-column>
|
||||
<table-column label="状态" v-if="activeTab === 'started'">{{processStatusMap[row.state]?.text ||
|
||||
'未知状态'}}
|
||||
</table-column>
|
||||
</template>
|
||||
<template #actions="{index,row}">
|
||||
<div class="action-btn" @click="onView(row)">
|
||||
<i class="fa fa-eye"></i>
|
||||
<span>查看</span>
|
||||
</div>
|
||||
</template>
|
||||
</table-list>
|
||||
</div>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
// 统计数据
|
||||
todoCount: 0,
|
||||
doneCount: 0,
|
||||
startedCount: 0,
|
||||
|
||||
// 查询表单
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
searchKeyword: "",
|
||||
category: ""
|
||||
},
|
||||
|
||||
// 流程类型选项
|
||||
categoryOptions: [],
|
||||
showCategoryPicker: false,
|
||||
categoryLabel: "",
|
||||
|
||||
// 任务列表
|
||||
tasks: [],
|
||||
loading: false,
|
||||
finished: false,
|
||||
refreshing: false,
|
||||
|
||||
// 标签页
|
||||
activeTab: "todo",
|
||||
|
||||
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"}
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.initData();
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 初始化数据
|
||||
async initData() {
|
||||
await Promise.all([this.getStatistics(), this.listCategory()]);
|
||||
},
|
||||
|
||||
// 获取统计数据
|
||||
async getStatistics() {
|
||||
try {
|
||||
const {code, data, msg} = await this.$axios.post("/flow/todoCenter/statistics");
|
||||
if (code === 0) {
|
||||
const {todoCount, doneCount, startedCount} = data
|
||||
this.todoCount = todoCount;
|
||||
this.doneCount = doneCount;
|
||||
this.startedCount = startedCount;
|
||||
}
|
||||
} catch (error) {
|
||||
this.$toast("获取统计数据失败");
|
||||
console.error("获取统计数据失败:", error);
|
||||
}
|
||||
},
|
||||
|
||||
// 获取流程类型
|
||||
async listCategory() {
|
||||
try {
|
||||
// 这里使用模拟数据,实际项目中替换为API调用
|
||||
const res = await axios.post("/flow/category/list");
|
||||
if (res.data.code === 0) {
|
||||
this.categoryOptions = res.data.data;
|
||||
}
|
||||
} catch (error) {
|
||||
this.$toast("获取流程分类失败");
|
||||
console.error("获取流程分类失败:", error);
|
||||
}
|
||||
},
|
||||
|
||||
// 处理标签页变化
|
||||
handleTabChange(name) {
|
||||
this.activeTab = name;
|
||||
this.doSearch()
|
||||
this.getStatistics();
|
||||
},
|
||||
|
||||
// 搜索
|
||||
doSearch() {
|
||||
this.$nextTick(() => {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageForm.totalCount = 0
|
||||
this.$refs.tableListRef.doSearch()
|
||||
})
|
||||
},
|
||||
|
||||
// 重置搜索
|
||||
reset() {
|
||||
this.pageForm.searchKeyword = "";
|
||||
this.pageForm.category = "";
|
||||
this.categoryLabel = "";
|
||||
this.doSearch();
|
||||
},
|
||||
|
||||
// 分类选择确认
|
||||
onCategoryConfirm(value) {
|
||||
this.pageForm.category = value.id;
|
||||
this.categoryLabel = value.name;
|
||||
this.showCategoryPicker = false;
|
||||
},
|
||||
|
||||
// 处理任务
|
||||
onView(task) {
|
||||
const {taskId, taskKey, instanceId, businessNo, formKey, h5FormKey} = task;
|
||||
if (!h5FormKey) {
|
||||
this.$toast("当前流程暂不支持手机端审核,请前往PC端页面审核!");
|
||||
return;
|
||||
}
|
||||
this.$pjaxReplace(h5FormKey + "?taskId=" + taskId + "bizId=" + businessNo + "taskKey=" + taskKey+ "&tab=" + this.activeTab)
|
||||
},
|
||||
|
||||
// 获取空状态文本
|
||||
getEmptyText() {
|
||||
switch (this.activeTab) {
|
||||
case "todo":
|
||||
return "您当前没有需要办理的任务,辛苦了";
|
||||
case "done":
|
||||
return "您当前没有已办理的任务记录";
|
||||
case "started":
|
||||
return "您当前没有发起的流程";
|
||||
default:
|
||||
return "暂无数据";
|
||||
}
|
||||
}
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
/* 标签页样式优化 */
|
||||
/deep/ .van-tabs__nav {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/deep/ .van-tab {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/deep/ .van-tab--active {
|
||||
color: var(--color-primary, #1989fa);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/deep/ .van-tabs__line {
|
||||
background-color: var(--color-primary, #1989fa);
|
||||
}
|
||||
|
||||
/deep/ .filter-section {
|
||||
/*margin-bottom: 16px;*/
|
||||
}
|
||||
|
||||
.search-form {
|
||||
display: flex;
|
||||
background: #fff;
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-form .van-search{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.task-list {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
/deep/ .task-item {
|
||||
margin-bottom: 12px;
|
||||
overflow: hidden;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
/deep/ .task-content {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
/deep/ .task-title {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
margin-bottom: 8px;
|
||||
color: #323233;
|
||||
}
|
||||
|
||||
/deep/ .task-info {
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
color: #969799;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/deep/ .task-info-label {
|
||||
width: 70px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/deep/ .task-info-value {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/deep/ .task-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
/deep/ .empty-section {
|
||||
padding: 40px 0;
|
||||
text-align: center;
|
||||
color: #969799;
|
||||
}
|
||||
|
||||
.category-select {
|
||||
margin: 0 12px 12px 0;
|
||||
}
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
const work = {
|
||||
template: /*language=HTML*/ `
|
||||
<div class="workbenche">
|
||||
<van-nav-bar title="工作台" placeholder fixed></van-nav-bar>
|
||||
<div class="module-item" v-for="item in $store.state.user.h5ModuleMenus" :key="item.id">
|
||||
<div class="module-header">
|
||||
<div class="module-header__content">{{item.name}}</div>
|
||||
</div>
|
||||
<div class="module-content">
|
||||
<van-grid :column-num="3">
|
||||
<van-grid-item icon="photo-o" :text="menu.name" v-for="menu in item.menus" :key="menu.id"
|
||||
@click="menuClick(menu)">
|
||||
<template #icon>
|
||||
<img :src="menu.icon" alt="" style="width: 40px;height: 40px">
|
||||
</template>
|
||||
<template #text>
|
||||
<span>{{menu.name}}</span>
|
||||
</template>
|
||||
</van-grid-item>
|
||||
</van-grid>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
store,
|
||||
methods: {
|
||||
menuClick(item) {
|
||||
this.$pjaxReplace(item.href)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const styles = {
|
||||
workbenche: window.goober.css`
|
||||
padding: 16px;
|
||||
background: #f5f5f5;
|
||||
`,
|
||||
moduleItem: goober.css`
|
||||
margin-bottom: 16px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
`,
|
||||
moduleHeader: goober.css`
|
||||
padding: 12px 16px;
|
||||
background: #fafafa;
|
||||
border-bottom: 1px solid #eee;
|
||||
`,
|
||||
moduleHeaderContent: goober.css`
|
||||
border-left: 4px solid var(--color-primary);
|
||||
font-size: 15px;
|
||||
color: #000;
|
||||
font-weight: 600;
|
||||
padding-left: 8px;
|
||||
`,
|
||||
moduleContent: goober.css`
|
||||
padding: 8px;
|
||||
|
||||
.van-grid-item__content {
|
||||
padding: 14px 8px;
|
||||
}
|
||||
`,
|
||||
menuIcon: goober.css`
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
`
|
||||
};
|
||||
console.log(styles)
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
.left-span-label {
|
||||
border-left: 4px solid var(--color-primary);
|
||||
font-size: 15px;
|
||||
color: #000;
|
||||
font-weight: 600;
|
||||
padding-left: 8px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.van-grid-item__content {
|
||||
padding: 14px 8px;
|
||||
}
|
||||
|
||||
.grid_text {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
font-family: "微软雅黑", Helvetica, STHeiTi, sans-serif;
|
||||
color: grey;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.module-item {
|
||||
background-color: #fff;
|
||||
box-shadow: 1px 1px #ccc;
|
||||
margin-bottom: 10px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.module-header {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 12px 10px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.workbenche .module-item .module-header .module-header__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.bgcolor-white {
|
||||
background: #ffffff;
|
||||
}
|
||||
.table-list .table-card {
|
||||
margin: 10px;
|
||||
background: #fff;
|
||||
padding: 8px;
|
||||
border-bottom: 1px #ebedf0 solid;
|
||||
position: relative;
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
.table-list .table-card:nth-of-type(1) {
|
||||
margin: 0 10px 10px 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.flex-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.left {
|
||||
font-size: 14px;
|
||||
width: 50%;
|
||||
}
|
||||
.right {
|
||||
text-align: right;
|
||||
}
|
||||
.increase {
|
||||
font-weight: bold;
|
||||
color: red;
|
||||
}
|
||||
.decrease {
|
||||
font-weight: bold;
|
||||
}
|
||||
.time {
|
||||
color: grey;
|
||||
font-size: 13px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<van-sticky>
|
||||
<van-nav-bar title="我的积分" @click-left="historyBack" left-arrow left-text="返回" placeholder fixed></van-nav-bar>
|
||||
<van-dropdown-menu>
|
||||
<van-dropdown-item v-model="pageForm.changeType" :options="changeTypeOptions" @change="doSearch"></van-dropdown-item>
|
||||
<van-dropdown-item v-model="pageForm.state" :options="stateOptions" @change="doSearch"></van-dropdown-item>
|
||||
<van-dropdown-item v-model="pageForm.integralOrigin" :options="integralOriginOptions" @change="doSearch"></van-dropdown-item>
|
||||
</van-dropdown-menu>
|
||||
</van-sticky>
|
||||
|
||||
<div class="bgcolor-white">
|
||||
<van-list v-if="tableData.length>0" v-model="tableLoading" :finished="tableFinished" finished-text="没有更多了" @load="onLoad">
|
||||
<div class="table-list">
|
||||
<div class="table-card flex-card" v-for="row in tableData" :key="row.id">
|
||||
<div class="left">
|
||||
<van-icon name="points" size="16" style="top: 1px"></van-icon>
|
||||
{{ row.remark ? row.remark : row.integralOrigin }}
|
||||
</div>
|
||||
<div class="right">
|
||||
<div :class="row.changeValue > 0 ? 'increase' : 'decrease'">{{ (row.changeValue > 0 ? '+' : '') + row.changeValue }}</div>
|
||||
<div class="time">{{ row.changeTime }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-list>
|
||||
<van-empty v-else image="/assets/platform/plugins/vant-green/images/nodata/nodata.png" description="暂无数据"></van-empty>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
const vue = new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
totalIntegral: 0,
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
searchKeyword: ""
|
||||
},
|
||||
|
||||
tableData: [],
|
||||
tableLoading: false,
|
||||
tableFinished: false,
|
||||
|
||||
changeTypeOptions: [],
|
||||
stateOptions: [],
|
||||
integralOriginOptions: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
historyBack,
|
||||
doSearch() {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageForm.totalCount = 0
|
||||
this.tableData = []
|
||||
this.onLoad()
|
||||
},
|
||||
onLoad() {
|
||||
this.tableLoading = true
|
||||
this.$axios.post("/platform/integral/records/pageData", this.pageForm).then((resp) => {
|
||||
this.tableData = this.tableData.concat(resp.data.list)
|
||||
this.pageForm.totalCount = resp.data.totalCount
|
||||
if (this.tableData.length >= this.pageForm.totalCount) {
|
||||
this.tableFinished = true
|
||||
} else {
|
||||
this.pageForm.pageNumber++
|
||||
}
|
||||
this.tableLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.totalIntegral = this.$store.state.user.totalIntegral
|
||||
this.$businessTool.getDictOptions("INTEGRAL_CHANGE_TYPE").then((data) => {
|
||||
data.forEach((item) => {
|
||||
this.changeTypeOptions.push({ text: item.name, value: item.code })
|
||||
})
|
||||
this.changeTypeOptions.unshift({ text: "全部变动类型" })
|
||||
})
|
||||
this.$businessTool.getDictOptions("INTEGRAL_STATE").then((data) => {
|
||||
data.forEach((item) => {
|
||||
this.stateOptions.push({ text: item.name, value: item.code })
|
||||
})
|
||||
this.stateOptions.unshift({ text: "全部积分状态" })
|
||||
})
|
||||
this.$businessTool.getDictOptions("INTEGRAL_ORIGIN").then((data) => {
|
||||
data.forEach((item) => {
|
||||
this.integralOriginOptions.push({ text: item.name, value: item.code })
|
||||
})
|
||||
this.integralOriginOptions.unshift({ text: "全部积分来源" })
|
||||
})
|
||||
this.onLoad()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,99 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.bgcolor-white {
|
||||
background: #ffffff;
|
||||
}
|
||||
.van-status__figure {
|
||||
text-align: center;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.van-status__figure img {
|
||||
width: 130px;
|
||||
height: 130px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<van-nav-bar title="我的签名" @click-left="historyBack" left-arrow left-text="返回" placeholder fixed></van-nav-bar>
|
||||
|
||||
<div class="bgcolor-white">
|
||||
<div v-if="!showSignature" class="p10">
|
||||
<van-image width="100%" height="200" :src="userSignatureUrl" v-if="userSignatureUrl" style="border: 1px dashed #000">
|
||||
<template slot="error">获取签名失败</template>
|
||||
</van-image>
|
||||
<div v-else class="van-status__figure">
|
||||
<img src="/assets/platform/plugins/vant-green/images/figure/fail.png" class="" />
|
||||
<div class="text-center pt10 pb10">暂无签名</div>
|
||||
</div>
|
||||
<!-- <van-status orient="vertical" :figure="14" title="暂无签名" v-else></van-status>-->
|
||||
|
||||
<div class="margin-top10">
|
||||
<van-button v-if="!userSignatureUrl" block type="primary" @click="openSignature">去签名</van-button>
|
||||
<van-button v-else block type="primary" @click="openSignature">修改签名</van-button>
|
||||
</div>
|
||||
</div>
|
||||
<signature v-if="showSignature" @save="saveSignature"></signature>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
const vue = new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
components: {
|
||||
signature: httpVueLoader("/components/plugins/sysSignature/index.vue?v=" + new Date().getTime())
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showSignature: false,
|
||||
userSignatureUrl: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getUserSignature() {
|
||||
this.$axios.post("/platform/signature/get").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.userSignatureUrl = res.data.signature
|
||||
}
|
||||
})
|
||||
},
|
||||
openSignature() {
|
||||
this.showSignature = true
|
||||
},
|
||||
saveSignature(signature) {
|
||||
console.log(signature)
|
||||
const file = base64ToFile(signature, "signature.png")
|
||||
const formData = new FormData()
|
||||
formData.append("file", file)
|
||||
axios
|
||||
.post("/platform/signature/update", formData, {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data"
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
return res.data
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.showSignature = false
|
||||
this.$toast.success(res.msg)
|
||||
this.getUserSignature()
|
||||
} else {
|
||||
this.$toast.fail(res.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getUserSignature()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,35 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<van-nav-bar title="个人信息" @click-left="historyBack" left-arrow left-text="返回" placeholder fixed></van-nav-bar>
|
||||
<member-info :user-id="userId" ref="memberInfoRef"></member-info>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include("/platform/zhghh5/staffmanage/member/common/mobileMemberInfo.js"){}#-->
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
data() {
|
||||
return {
|
||||
userId: ""
|
||||
}
|
||||
},
|
||||
components: {
|
||||
"member-info": MOBILE_MEMBER_INFO
|
||||
},
|
||||
methods: {
|
||||
viewInfo() {
|
||||
this.userId = this.$store.state.user.id
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.viewInfo()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
Reference in New Issue
Block a user