Files
klp-oa/klp-ui/src/views/components/AllApplications.vue
2025-08-22 14:28:10 +08:00

396 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="all-applications-container">
<!-- 常用应用区域 -->
<div class="frequently-used-section">
<div class="frequently-used-header">
<h3 class="frequently-title">常用应用</h3>
<el-button
type="text"
size="small"
class="edit-btn"
@click="isEditingFavorites = !isEditingFavorites"
>
{{ isEditingFavorites ? '完成' : '编辑' }}
</el-button>
</div>
<div class="frequently-used-grid" v-if="frequentlyUsedApps.length > 0">
<div
v-for="app in frequentlyUsedApps"
:key="`fav-${app.parentPath}-${app.childPath}`"
class="frequently-app-item"
@click="handleAppClick(getParentMenu(app.parentPath), getChildMenu(app.parentPath, app.childPath))"
>
<div class="app-icon-wrapper">
<svg-icon :icon-class="getChildMenu(app.parentPath, app.childPath).meta.icon || 'documentation'" class="app-icon" />
</div>
<span class="app-name">{{ getChildMenu(app.parentPath, app.childPath).meta.title }}</span>
<!-- 删除按钮 - 仅在编辑模式显示 -->
<div
class="remove-btn"
v-if="isEditingFavorites"
@click.stop="removeFromFavorites(app.parentPath, app.childPath)"
>
<i class="el-icon-close"></i>
</div>
</div>
</div>
<div v-else>
<el-empty description="暂无常用应用" />
</div>
</div>
<!-- 原有全部应用区域 -->
<h3 class="title" v-if="isEditingFavorites">全部应用</h3>
<el-tabs v-model="activeTabName" class="app-tabs" v-if="isEditingFavorites">
<el-tab-pane
v-for="menu in filteredMenus"
:key="menu.path"
:label="menu.meta.title"
:name="menu.path"
>
<div class="app-grid">
<div
v-for="child in menu.children"
:key="child.path"
class="app-item"
>
<!-- @click="handleAppClick(menu, child)" -->
<div class="app-icon-wrapper">
<svg-icon :icon-class="child.meta.icon || 'documentation'" class="app-icon" />
<!-- 添加到常用按钮 -->
<div
class="add-to-favorite-btn"
@click.stop="addToFavorites(menu.path, child.path)"
v-if="!isInFavorites(menu.path, child.path)"
>
<i class="el-icon-star-off"></i>
</div>
<!-- 已在常用中的标识 -->
<div
class="in-favorite-indicator"
v-if="isInFavorites(menu.path, child.path)"
>
<i class="el-icon-star-on"></i>
</div>
</div>
<span class="app-name">{{ child.meta.title }}</span>
</div>
</div>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import { getRouters } from '@/api/menu'
import path from 'path'
export default {
name: 'AllApplications',
data() {
return {
allMenus: [],
activeTabName: '',
frequentlyUsedApps: [], // 存储常用应用
isEditingFavorites: false // 是否处于编辑常用应用模式
}
},
computed: {
filteredMenus() {
const filterHidden = (menus) => {
return menus
.filter(menu => menu.hidden!== true)
.map(menu => {
if (menu.children) {
menu.children = filterHidden(menu.children)
}
return menu
})
}
const topLevelMenus = filterHidden(this.allMenus).filter(
menu => menu.children && menu.children.length > 0
)
return topLevelMenus
}
},
created() {
this.fetchMenus()
this.loadFrequentlyUsedApps()
},
methods: {
fetchMenus() {
getRouters().then(response => {
this.allMenus = response.data
if (this.filteredMenus.length > 0) {
this.activeTabName = this.filteredMenus[0].path
}
})
},
// 加载常用应用从localStorage
loadFrequentlyUsedApps() {
const saved = localStorage.getItem('frequentlyUsedApps')
if (saved) {
try {
this.frequentlyUsedApps = JSON.parse(saved)
} catch (e) {
console.error('Failed to parse frequently used apps', e)
this.frequentlyUsedApps = []
}
}
},
// 保存常用应用到localStorage
saveFrequentlyUsedApps() {
localStorage.setItem('frequentlyUsedApps', JSON.stringify(this.frequentlyUsedApps))
},
// 添加到常用应用
addToFavorites(parentPath, childPath) {
if (!this.isInFavorites(parentPath, childPath)) {
// 限制最多10个常用应用
if (this.frequentlyUsedApps.length >= 10) {
this.$message.warning('常用应用最多只能添加10个')
return
}
this.frequentlyUsedApps.unshift({ parentPath, childPath })
this.saveFrequentlyUsedApps()
this.$message.success('已添加到常用应用')
}
},
// 从常用应用中移除
removeFromFavorites(parentPath, childPath) {
this.frequentlyUsedApps = this.frequentlyUsedApps.filter(
app =>!(app.parentPath === parentPath && app.childPath === childPath)
)
this.saveFrequentlyUsedApps()
},
// 检查应用是否在常用列表中
isInFavorites(parentPath, childPath) {
return this.frequentlyUsedApps.some(
app => app.parentPath === parentPath && app.childPath === childPath
)
},
// 根据路径获取父菜单
getParentMenu(parentPath) {
return this.filteredMenus.find(menu => menu.path === parentPath)
},
// 根据路径获取子菜单
getChildMenu(parentPath, childPath) {
const parent = this.getParentMenu(parentPath)
if (parent && parent.children) {
return parent.children.find(child => child.path === childPath)
}
return null
},
handleAppClick(parentMenu, childMenu) {
if (!childMenu) return
const basePath = parentMenu.path
const fullPath = path.resolve(basePath, childMenu.path)
this.$router.push(fullPath)
}
}
}
</script>
<style lang="scss" scoped>
.all-applications-container {
background-color: #fff;
padding: 16px; /* 调整间距,更贴近飞书紧凑感 */
border-radius: 12px; /* 飞书常用较大圆角 */
margin-top: 24px;
}
// 常用应用样式
.frequently-used-section {
margin-bottom: 32px; /* 增大间距 */
padding-bottom: 20px;
border-bottom: 1px solid #ebeef5; /* 飞书浅灰边框色 */
}
.frequently-used-header {
display: flex;
justify-content: space-between;
align-items: center;
// margin-bottom: 20px;
}
.frequently-title {
font-size: 16px; /* 稍小字体,飞书风格更简洁 */
font-weight: 500; /* 调整 FontWeight */
color: #1f2329; /* 深一点的标题色 */
}
.edit-btn {
color: #0078ff; /* 飞书常用的主题蓝 */
padding: 0;
font-size: 14px;
}
.frequently-used-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); /* 调整卡片宽度 */
gap: 20px; /* 增大间距 */
}
.frequently-app-item {
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
padding: 16px; /* 调整内边距 */
border-radius: 12px; /* 大圆角 */
transition: all 0.3s ease;
position: relative;
background-color: #f7f8fa; /* 飞书卡片常用浅灰底色 */
&:hover {
background-color: #eaeef7; /* hover 时的浅灰 */
}
}
// 全部应用标题
.title {
font-size: 16px;
font-weight: 500;
margin-bottom: 20px;
color: #1f2329;
}
// 应用网格布局
.app-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); /* 调整卡片宽度 */
gap: 24px; /* 增大间距 */
padding-top: 16px;
}
.app-item {
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
padding: 16px;
border-radius: 12px;
transition: background-color 0.3s ease;
position: relative;
background-color: #f7f8fa;
&:hover {
background-color: #eaeef7;
}
}
// 应用图标样式
.app-icon-wrapper {
width: 48px; /* 稍小图标容器 */
height: 48px;
border-radius: 10px; /* 图标容器圆角 */
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 12px;
background-color: #e6f0ff; /* 飞书风格的浅蓝底色 */
position: relative;
}
.app-icon {
font-size: 24px; /* 调整图标大小 */
color: #0078ff; /* 主题蓝 */
}
// 应用名称样式
.app-name {
font-size: 14px;
color: #515767; /* 飞书常用的文本色 */
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
}
// 添加到常用按钮
.add-to-favorite-btn {
position: absolute;
bottom: 4px;
right: 4px;
width: 24px; /* 稍大按钮 */
height: 24px;
border-radius: 6px; /* 小圆角 */
background-color: rgba(255, 255, 255, 0.9);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 14px;
color: #c0c4cc;
transition: all 0.2s;
&:hover {
color: #ffb400; /* 飞书常用的强调色 */
background-color: #fff;
}
}
// 已在常用中的标识
.in-favorite-indicator {
position: absolute;
bottom: 4px;
right: 4px;
width: 24px;
height: 24px;
border-radius: 6px;
background-color: rgba(255, 255, 255, 0.9);
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
color: #ffb400;
}
// 删除常用应用按钮
.remove-btn {
position: absolute;
top: -6px;
right: -6px;
width: 24px;
height: 24px;
border-radius: 6px;
background-color: #ff4d4f; /* 飞书删除按钮红 */
color: white;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 14px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
transition: all 0.2s;
&:hover {
background-color: #ff3839;
transform: scale(1.05);
}
}
// 标签页样式
::v-deep .el-tabs__header {
margin-bottom: 0;
}
::v-deep .el-tabs__nav-wrap::after {
height: 1px;
}
</style>