feat: 添加公告功能,支持查看公告和在系统浏览器中打开链接

This commit is contained in:
2025-08-31 23:11:24 +08:00
parent fe35e37371
commit c5fd0c1253
5 changed files with 108 additions and 6 deletions

View File

@@ -1,4 +1,4 @@
import { app, BrowserWindow, ipcMain, dialog } from 'electron'
import { app, BrowserWindow, ipcMain, dialog, shell } from 'electron'
import * as path from 'path'
import * as fs from 'fs'
import { spawn } from 'child_process'
@@ -74,7 +74,6 @@ function createWindow() {
})
mainWindow.setMenuBarVisibility(false)
const devServer = process.env.VITE_DEV_SERVER_URL
if (devServer) {
mainWindow.loadURL(devServer)
@@ -121,6 +120,17 @@ ipcMain.handle('select-file', async (event, filters = []) => {
return result.canceled ? null : result.filePaths[0]
})
// 在系统默认浏览器中打开URL
ipcMain.handle('open-url', async (event, url: string) => {
try {
await shell.openExternal(url)
return { success: true }
} catch (error) {
console.error('打开链接失败:', error)
return { success: false, error: error.message }
}
})
// 环境检查
ipcMain.handle('check-environment', async () => {
const appRoot = getAppRoot()

View File

@@ -9,6 +9,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
openDevTools: () => ipcRenderer.invoke('open-dev-tools'),
selectFolder: () => ipcRenderer.invoke('select-folder'),
selectFile: (filters?: any[]) => ipcRenderer.invoke('select-file', filters),
openUrl: (url: string) => ipcRenderer.invoke('open-url', url),
// 初始化相关API
checkEnvironment: () => ipcRenderer.invoke('check-environment'),

View File

@@ -23,7 +23,12 @@
class="notice-tab-pane"
>
<div class="notice-content">
<div class="markdown-content" v-html="renderMarkdown(content)"></div>
<div
ref="markdownContentRef"
class="markdown-content"
v-html="renderMarkdown(content)"
@click="handleLinkClick"
></div>
</div>
</a-tab-pane>
</a-tabs>
@@ -116,6 +121,33 @@ const confirmNotices = async () => {
}
}
// 处理链接点击
const handleLinkClick = async (event: MouseEvent) => {
const target = event.target as HTMLElement
if (target.tagName === 'A') {
event.preventDefault()
const url = target.getAttribute('href')
if (url) {
try {
// 检查是否在Electron环境中
if (window.electronAPI && window.electronAPI.openUrl) {
const result = await window.electronAPI.openUrl(url)
if (!result.success) {
console.error('打开链接失败:', result.error)
message.error('打开链接失败,请手动复制链接地址')
}
} else {
// 如果不在Electron环境中使用普通的window.open
window.open(url, '_blank')
}
} catch (error) {
console.error('打开链接失败:', error)
message.error('打开链接失败,请手动复制链接地址')
}
}
}
}
// 监听公告数据变化,设置默认选中第一个公告
watch(
() => props.noticeData,

View File

@@ -2,7 +2,8 @@ export interface ElectronAPI {
openDevTools: () => Promise<void>
selectFolder: () => Promise<string | null>
selectFile: (filters?: any[]) => Promise<string | null>
openUrl: (url: string) => Promise<{ success: boolean; error?: string }>
// 初始化相关API
checkEnvironment: () => Promise<{
pythonExists: boolean

View File

@@ -1,6 +1,21 @@
<template>
<div class="header">
<a-typography-title>{{ greeting }}</a-typography-title>
<!-- 右上角公告按钮 -->
<div class="header-actions">
<a-button
type="primary"
ghost
@click="showNotice"
:loading="noticeLoading"
class="notice-button"
>
<template #icon>
<BellOutlined />
</template>
查看公告
</a-button>
</div>
</div>
<!-- 公告模态框 -->
@@ -230,7 +245,7 @@
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { message } from 'ant-design-vue'
import { ReloadOutlined, ClockCircleOutlined, UserOutlined } from '@ant-design/icons-vue'
import { ReloadOutlined, ClockCircleOutlined, UserOutlined, BellOutlined } from '@ant-design/icons-vue'
import { Service } from '@/api/services/Service'
import NoticeModal from '@/components/NoticeModal.vue'
import dayjs from 'dayjs'
@@ -286,6 +301,7 @@ const proxyData = ref<Record<string, ProxyInfo>>({})
// 公告系统相关状态
const noticeVisible = ref(false)
const noticeData = ref<Record<string, string>>({})
const noticeLoading = ref(false)
// 获取当前活动信息
const currentActivity = computed(() => {
@@ -462,7 +478,6 @@ const fetchNoticeData = async () => {
if (response.code === 200) {
// 检查是否需要显示公告
if (response.if_need_show && response.data && Object.keys(response.data).length > 0) {
// if (response.data && Object.keys(response.data).length > 0) {
noticeData.value = response.data
noticeVisible.value = true
}
@@ -480,6 +495,31 @@ const onNoticeConfirmed = () => {
// message.success('公告已确认')
}
// 显示公告的处理函数
const showNotice = async () => {
noticeLoading.value = true
try {
const response = await Service.getNoticeInfoApiInfoNoticeGetPost()
if (response.code === 200) {
// 忽略 if_need_show 字段,只要有公告数据就显示
if (response.data && Object.keys(response.data).length > 0) {
noticeData.value = response.data
noticeVisible.value = true
} else {
message.info('暂无公告信息')
}
} else {
message.error(response.message || '获取公告失败')
}
} catch (error) {
console.error('显示公告失败:', error)
message.error('显示公告失败,请稍后重试')
} finally {
noticeLoading.value = false
}
}
onMounted(() => {
fetchActivityData()
fetchNoticeData()
@@ -489,6 +529,9 @@ onMounted(() => {
<style scoped>
.header {
margin-bottom: 24px;
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
@@ -498,6 +541,21 @@ onMounted(() => {
font-weight: 600;
}
.header-actions {
display: flex;
align-items: center;
gap: 8px;
}
.notice-button {
min-width: 120px;
}
/* 公告相关样式 */
.notice-modal {
/* 自定义公告模态框样式 */
}
.activity-card {
margin-bottom: 24px;
}