feat: 添加公告功能,支持查看公告和在系统浏览器中打开链接
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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,
|
||||
|
||||
3
frontend/src/types/electron.d.ts
vendored
3
frontend/src/types/electron.d.ts
vendored
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user