|
|
@@ -16,6 +16,25 @@
|
|
|
</div>
|
|
|
|
|
|
<div v-loading="loading">
|
|
|
+ <!-- 最近打开 -->
|
|
|
+ <div v-if="recentApps.length > 0" class="recent-section">
|
|
|
+ <h3 class="section-title">最近打开</h3>
|
|
|
+ <div class="apps-grid">
|
|
|
+ <div
|
|
|
+ v-for="app in recentApps"
|
|
|
+ :key="app.app_id"
|
|
|
+ class="app-item"
|
|
|
+ :style="{ width: iconSize + 'px' }"
|
|
|
+ >
|
|
|
+ <PlatformIcon
|
|
|
+ :text="app.app_name"
|
|
|
+ :size="iconSize"
|
|
|
+ @click="handleAppClick(app)"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
<!-- 按分类显示 -->
|
|
|
<template v-if="showCategory">
|
|
|
<div
|
|
|
@@ -42,18 +61,21 @@
|
|
|
</template>
|
|
|
|
|
|
<!-- 不按分类显示(原有方式) -->
|
|
|
- <div v-else class="apps-grid">
|
|
|
- <div
|
|
|
- v-for="app in activeApps"
|
|
|
- :key="app.app_id"
|
|
|
- class="app-item"
|
|
|
- :style="{ width: iconSize + 'px' }"
|
|
|
- >
|
|
|
- <PlatformIcon
|
|
|
- :text="app.app_name"
|
|
|
- :size="iconSize"
|
|
|
- @click="handleAppClick(app)"
|
|
|
- />
|
|
|
+ <div v-else>
|
|
|
+ <h3 class="section-title">全部应用</h3>
|
|
|
+ <div class="apps-grid">
|
|
|
+ <div
|
|
|
+ v-for="app in activeApps"
|
|
|
+ :key="app.app_id"
|
|
|
+ class="app-item"
|
|
|
+ :style="{ width: iconSize + 'px' }"
|
|
|
+ >
|
|
|
+ <PlatformIcon
|
|
|
+ :text="app.app_name"
|
|
|
+ :size="iconSize"
|
|
|
+ @click="handleAppClick(app)"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -105,6 +127,13 @@ interface CategoryGroup {
|
|
|
apps: Mapping[]
|
|
|
}
|
|
|
|
|
|
+interface RecentAppRecord {
|
|
|
+ app_id: string
|
|
|
+ app_name: string
|
|
|
+ count: number
|
|
|
+ lastOpenTime: number
|
|
|
+}
|
|
|
+
|
|
|
// State
|
|
|
const mappings = ref<Mapping[]>([])
|
|
|
const loading = ref(false)
|
|
|
@@ -157,6 +186,78 @@ const categorizedApps = computed(() => {
|
|
|
return result
|
|
|
})
|
|
|
|
|
|
+// 最近打开的应用记录
|
|
|
+const recentApps = computed<Mapping[]>(() => {
|
|
|
+ const records = loadRecentApps()
|
|
|
+ if (records.length === 0) return []
|
|
|
+
|
|
|
+ // 按最近打开时间排序,时间越近的越靠前
|
|
|
+ const sortedRecords = records
|
|
|
+ .sort((a, b) => {
|
|
|
+ return b.lastOpenTime - a.lastOpenTime
|
|
|
+ })
|
|
|
+
|
|
|
+ // 从mappings中获取最新的应用信息,只返回仍然存在且激活的应用
|
|
|
+ const result: Mapping[] = []
|
|
|
+ for (const record of sortedRecords) {
|
|
|
+ const app = mappings.value.find(a => a.app_id === record.app_id && a.is_active)
|
|
|
+ if (app) {
|
|
|
+ result.push(app)
|
|
|
+ if (result.length >= 5) break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result
|
|
|
+})
|
|
|
+
|
|
|
+// 加载最近打开的应用记录
|
|
|
+const loadRecentApps = (): RecentAppRecord[] => {
|
|
|
+ try {
|
|
|
+ const saved = localStorage.getItem('launchpad_recent_apps')
|
|
|
+ if (saved) {
|
|
|
+ return JSON.parse(saved)
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error('Failed to parse recent apps', e)
|
|
|
+ }
|
|
|
+ return []
|
|
|
+}
|
|
|
+
|
|
|
+// 保存最近打开的应用记录
|
|
|
+const saveRecentApps = (records: RecentAppRecord[]) => {
|
|
|
+ try {
|
|
|
+ localStorage.setItem('launchpad_recent_apps', JSON.stringify(records))
|
|
|
+ } catch (e) {
|
|
|
+ console.error('Failed to save recent apps', e)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 记录应用打开
|
|
|
+const recordAppOpen = (app: Mapping) => {
|
|
|
+ if (!app.is_active) return
|
|
|
+
|
|
|
+ const records = loadRecentApps()
|
|
|
+ const existingIndex = records.findIndex(r => r.app_id === app.app_id)
|
|
|
+
|
|
|
+ if (existingIndex >= 0) {
|
|
|
+ // 更新现有记录:增加打开次数,更新最后打开时间
|
|
|
+ records[existingIndex].count += 1
|
|
|
+ records[existingIndex].lastOpenTime = Date.now()
|
|
|
+ // 更新应用信息(名称等可能会变化)
|
|
|
+ records[existingIndex].app_name = app.app_name
|
|
|
+ } else {
|
|
|
+ // 添加新记录
|
|
|
+ records.push({
|
|
|
+ app_id: app.app_id,
|
|
|
+ app_name: app.app_name,
|
|
|
+ count: 1,
|
|
|
+ lastOpenTime: Date.now()
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ saveRecentApps(records)
|
|
|
+}
|
|
|
+
|
|
|
// Load Settings
|
|
|
const loadSettings = () => {
|
|
|
const saved = localStorage.getItem('launchpad_settings')
|
|
|
@@ -208,6 +309,9 @@ const fetchMappings = async () => {
|
|
|
const handleAppClick = async (app: Mapping) => {
|
|
|
if (!app.is_active) return
|
|
|
|
|
|
+ // 记录应用打开
|
|
|
+ recordAppOpen(app)
|
|
|
+
|
|
|
const loadingMsg = ElMessage.info({
|
|
|
message: `正在进入 ${app.app_name}...`,
|
|
|
duration: 0
|
|
|
@@ -304,6 +408,19 @@ onUnmounted(() => {
|
|
|
margin-bottom: 40px;
|
|
|
}
|
|
|
|
|
|
+.recent-section {
|
|
|
+ margin-bottom: 40px;
|
|
|
+}
|
|
|
+
|
|
|
+.section-title {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #303133;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ padding-bottom: 10px;
|
|
|
+ border-bottom: 2px solid #e4e7ed;
|
|
|
+}
|
|
|
+
|
|
|
.category-title {
|
|
|
font-size: 18px;
|
|
|
font-weight: 600;
|