新增:应用配置管理模块,优化文件系统功能
- 新增 ConfigAPI 和 ConfigService 实现配置管理 - 新增 SettingsPanel 和 UpdateNotification 组件 - 文件系统模块化重构,提升代码质量 - 提取公共函数,优化代码结构 - 版本号更新至 0.2.0
This commit is contained in:
441
web/src/App.vue
441
web/src/App.vue
@@ -6,16 +6,17 @@
|
||||
<h2>U-Desk</h2>
|
||||
</div>
|
||||
<a-tabs v-model:active-key="activeTab" class="header-tabs">
|
||||
<a-tab-pane key="db-cli" title="数据库"/>
|
||||
<a-tab-pane key="file-system" title="文件管理"/>
|
||||
<a-tab-pane key="user" title="用户查询"/>
|
||||
<a-tab-pane key="device" title="设备调用测试"/>
|
||||
<a-tab-pane
|
||||
v-for="tab in visibleTabs"
|
||||
:key="tab.key"
|
||||
:title="tab.title"
|
||||
/>
|
||||
</a-tabs>
|
||||
<div class="header-actions">
|
||||
<a-tooltip content="版本更新">
|
||||
<a-button type="text" @click="showUpdateModal = true">
|
||||
<a-tooltip content="设置">
|
||||
<a-button type="text" @click="showSettings = true">
|
||||
<template #icon>
|
||||
<IconSync />
|
||||
<IconSettings />
|
||||
</template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
@@ -47,115 +48,248 @@
|
||||
</div>
|
||||
</a-layout-header>
|
||||
<a-layout-content class="content">
|
||||
<!-- 数据库客户端 -->
|
||||
<DbCli v-if="activeTab === 'db-cli'"/>
|
||||
|
||||
<!-- 文件管理 -->
|
||||
<FileSystem v-if="activeTab === 'file-system'"/>
|
||||
|
||||
<!-- 用户查询页面 -->
|
||||
<div v-if="activeTab === 'user'">
|
||||
<!-- 查询表单 -->
|
||||
<a-card class="search-card">
|
||||
<a-form :model="formModel" layout="inline">
|
||||
<a-form-item label="关键字">
|
||||
<a-input
|
||||
v-model="formModel.keyword"
|
||||
placeholder="姓名、账号、电话"
|
||||
allow-clear
|
||||
style="width: 200px"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="状态">
|
||||
<a-select
|
||||
v-model="formModel.status"
|
||||
placeholder="选择状态"
|
||||
style="width: 120px"
|
||||
>
|
||||
<a-option :value="0">全部</a-option>
|
||||
<a-option :value="1">正常</a-option>
|
||||
<a-option :value="2">停用</a-option>
|
||||
<a-option :value="3">已删除</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleSearch">
|
||||
<template #icon>
|
||||
<icon-search/>
|
||||
</template>
|
||||
查询
|
||||
</a-button>
|
||||
<a-button @click="handleReset">
|
||||
<template #icon>
|
||||
<icon-refresh/>
|
||||
</template>
|
||||
重置
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-card>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<a-card class="table-card">
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #status="{ record }">
|
||||
<a-tag v-if="record.status === 1" color="green">正常</a-tag>
|
||||
<a-tag v-else-if="record.status === 2" color="orange">停用</a-tag>
|
||||
<a-tag v-else-if="record.status === 3" color="gray">已删除</a-tag>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
</div>
|
||||
|
||||
<!-- 设备调用测试页面 -->
|
||||
<DeviceTest v-if="activeTab === 'device'"/>
|
||||
<!-- 动态渲染 Tab 内容 -->
|
||||
<template v-for="tab in visibleTabs" :key="tab.key">
|
||||
<KeepAlive>
|
||||
<component :is="getComponent(tab.key)" v-if="activeTab === tab.key" />
|
||||
</KeepAlive>
|
||||
</template>
|
||||
</a-layout-content>
|
||||
|
||||
<!-- 版本更新模态框 -->
|
||||
<a-modal
|
||||
v-model:visible="showUpdateModal"
|
||||
title="版本更新"
|
||||
width="800px"
|
||||
:footer="false"
|
||||
>
|
||||
<UpdatePanel />
|
||||
</a-modal>
|
||||
<!-- 设置抽屉 -->
|
||||
<SettingsPanel
|
||||
v-model="showSettings"
|
||||
:config="appConfig"
|
||||
@save="handleSaveConfig"
|
||||
/>
|
||||
|
||||
<!-- 升级提示弹窗 -->
|
||||
<UpdateNotification
|
||||
v-model="showUpdateNotification"
|
||||
:update-info="updateInfo"
|
||||
@install="handleUpdateInstall"
|
||||
@skip="handleUpdateSkip"
|
||||
/>
|
||||
</a-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onMounted, ref, watch} from 'vue'
|
||||
import {Message} from '@arco-design/web-vue'
|
||||
import {
|
||||
IconMinus,
|
||||
IconFullscreen,
|
||||
IconFullscreenExit,
|
||||
IconClose,
|
||||
IconSync
|
||||
} from '@arco-design/web-vue/es/icon'
|
||||
import { ref, watch, computed, onMounted } from 'vue'
|
||||
import { IconSettings } from '@arco-design/web-vue/es/icon'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import DeviceTest from './components/DeviceTest.vue'
|
||||
import DbCli from './views/db-cli/index.vue'
|
||||
import ThemeToggle from './components/ThemeToggle.vue'
|
||||
import UpdatePanel from './components/UpdatePanel.vue'
|
||||
import FileSystem from './components/FileSystem.vue'
|
||||
import SettingsPanel from './components/SettingsPanel.vue'
|
||||
import UpdateNotification from './components/UpdateNotification.vue'
|
||||
|
||||
// 存储键
|
||||
const ACTIVE_TAB_STORAGE_KEY = 'app-active-tab'
|
||||
|
||||
// 从 localStorage 恢复上次打开的区域,默认为 'db-cli'
|
||||
const activeTab = ref(localStorage.getItem(ACTIVE_TAB_STORAGE_KEY) || 'db-cli')
|
||||
const showUpdateModal = ref(false)
|
||||
const savedTab = localStorage.getItem(ACTIVE_TAB_STORAGE_KEY)
|
||||
const activeTab = ref((savedTab === 'user' ? 'db-cli' : savedTab) || 'db-cli')
|
||||
const showSettings = ref(false)
|
||||
const isMaximized = ref(false)
|
||||
|
||||
// 更新相关状态
|
||||
const showUpdateNotification = ref(false)
|
||||
const updateInfo = ref(null)
|
||||
const checkedUpdate = ref(false)
|
||||
|
||||
// 应用配置
|
||||
const appConfig = ref({
|
||||
tabs: [],
|
||||
visibleTabs: [],
|
||||
defaultTab: 'db-cli'
|
||||
})
|
||||
|
||||
// 可见 Tabs(根据配置动态生成)
|
||||
const visibleTabs = computed(() => {
|
||||
if (!appConfig.value.tabs || appConfig.value.tabs.length === 0) {
|
||||
// 默认配置
|
||||
return [
|
||||
{ key: 'db-cli', title: '数据库' },
|
||||
{ key: 'file-system', title: '文件管理' },
|
||||
{ key: 'device', title: '设备调用测试' }
|
||||
]
|
||||
}
|
||||
|
||||
return appConfig.value.tabs
|
||||
.filter(tab => tab.visible)
|
||||
.sort((a, b) => {
|
||||
const aIndex = appConfig.value.visibleTabs.indexOf(a.key)
|
||||
const bIndex = appConfig.value.visibleTabs.indexOf(b.key)
|
||||
return aIndex - bIndex
|
||||
})
|
||||
})
|
||||
|
||||
// 加载配置
|
||||
const loadConfig = async () => {
|
||||
try {
|
||||
// 检查 Wails 绑定是否准备好
|
||||
if (!window.go || !window.go.main || !window.go.main.App) {
|
||||
console.warn('Wails 绑定未准备好,等待重试...')
|
||||
setTimeout(() => loadConfig(), 100)
|
||||
return
|
||||
}
|
||||
|
||||
const result = await window.go.main.App.GetAppConfig()
|
||||
if (result.success) {
|
||||
const tabs = result.data.tabs || []
|
||||
const visibleTabs = result.data.visibleTabs || []
|
||||
|
||||
// 确保 tabs 数组中的 visible 属性与 visibleTabs 同步
|
||||
const syncedTabs = tabs.map(tab => ({
|
||||
...tab,
|
||||
visible: visibleTabs.includes(tab.key)
|
||||
}))
|
||||
|
||||
appConfig.value = {
|
||||
tabs: syncedTabs,
|
||||
visibleTabs: visibleTabs,
|
||||
defaultTab: result.data.defaultTab || 'db-cli'
|
||||
}
|
||||
|
||||
// 设置默认 Tab
|
||||
activeTab.value = appConfig.value.defaultTab
|
||||
} else {
|
||||
console.error('加载配置失败:', result.message)
|
||||
// 使用默认配置
|
||||
useDefaultConfig()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载配置失败:', error)
|
||||
// 使用默认配置
|
||||
useDefaultConfig()
|
||||
}
|
||||
}
|
||||
|
||||
// 使用默认配置
|
||||
const useDefaultConfig = () => {
|
||||
appConfig.value = {
|
||||
tabs: [
|
||||
{ key: 'db-cli', title: '数据库', visible: true, enabled: true },
|
||||
{ key: 'file-system', title: '文件管理', visible: true, enabled: true },
|
||||
{ key: 'device', title: '设备调用测试', visible: true, enabled: true }
|
||||
],
|
||||
visibleTabs: ['db-cli', 'file-system', 'device'],
|
||||
defaultTab: 'db-cli'
|
||||
}
|
||||
}
|
||||
|
||||
// 保存配置
|
||||
const handleSaveConfig = async (config) => {
|
||||
try {
|
||||
const result = await window.go.main.App.SaveAppConfig({
|
||||
tabs: config.tabs,
|
||||
visibleTabs: config.visibleTabs,
|
||||
defaultTab: config.defaultTab
|
||||
})
|
||||
|
||||
if (result.success) {
|
||||
// 更新本地配置
|
||||
appConfig.value = {
|
||||
tabs: [...config.tabs],
|
||||
visibleTabs: [...config.visibleTabs],
|
||||
defaultTab: config.defaultTab
|
||||
}
|
||||
|
||||
// 如果当前激活的 Tab 被隐藏,切换到默认 Tab
|
||||
if (!config.visibleTabs.includes(activeTab.value)) {
|
||||
activeTab.value = config.defaultTab
|
||||
}
|
||||
|
||||
Message.success('配置保存成功')
|
||||
showSettings.value = false
|
||||
} else {
|
||||
Message.error(result.message || '保存配置失败')
|
||||
throw new Error(result.message)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('保存配置失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 获取组件
|
||||
const getComponent = (key) => {
|
||||
const components = {
|
||||
'db-cli': DbCli,
|
||||
'file-system': FileSystem,
|
||||
'device': DeviceTest
|
||||
}
|
||||
return components[key] || null
|
||||
}
|
||||
|
||||
// 检查更新
|
||||
const checkForUpdates = async () => {
|
||||
try {
|
||||
// 等待 Wails 绑定准备好
|
||||
if (!window.go || !window.go.main || !window.go.main.App) {
|
||||
console.warn('Wails 绑定未准备好,延迟检查更新...')
|
||||
setTimeout(() => checkForUpdates(), 1000)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取更新配置
|
||||
const configResult = await window.go.main.App.GetUpdateConfig()
|
||||
if (!configResult.success) {
|
||||
console.error('获取更新配置失败:', configResult.message)
|
||||
return
|
||||
}
|
||||
|
||||
const config = configResult.data
|
||||
const shouldCheck = config.auto_check_enabled
|
||||
|
||||
if (!shouldCheck) {
|
||||
console.log('自动更新检查已关闭')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('[自动检查] 开始检查更新...')
|
||||
|
||||
// 检查更新
|
||||
const result = await window.go.main.App.CheckUpdate()
|
||||
if (result.success && result.data) {
|
||||
checkedUpdate.value = true
|
||||
|
||||
// 检查是否已跳过此版本
|
||||
const skippedVersion = localStorage.getItem('skipped_version')
|
||||
if (result.data.has_update) {
|
||||
// 如果是强制更新,或者未跳过此版本,则显示提示
|
||||
if (result.data.force_update || skippedVersion !== result.data.latest_version) {
|
||||
console.log('[自动检查] 发现新版本:', result.data.latest_version)
|
||||
updateInfo.value = result.data
|
||||
// 延迟显示,让用户先看到应用界面
|
||||
setTimeout(() => {
|
||||
showUpdateNotification.value = true
|
||||
}, 2000)
|
||||
} else {
|
||||
console.log('[自动检查] 此版本已跳过')
|
||||
}
|
||||
} else {
|
||||
console.log('[自动检查] 已是最新版本')
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('检查更新失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时加载配置和检查更新
|
||||
onMounted(() => {
|
||||
loadConfig()
|
||||
// 延迟检查更新,避免阻塞应用启动
|
||||
setTimeout(() => {
|
||||
if (!checkedUpdate.value) {
|
||||
checkForUpdates()
|
||||
}
|
||||
}, 3000)
|
||||
})
|
||||
|
||||
// 监听 activeTab 变化,自动保存到 localStorage
|
||||
watch(activeTab, (newTab) => {
|
||||
localStorage.setItem(ACTIVE_TAB_STORAGE_KEY, newTab)
|
||||
@@ -192,94 +326,41 @@ const handleClose = async () => {
|
||||
console.error('关闭窗口失败:', error)
|
||||
}
|
||||
}
|
||||
const loading = ref(false)
|
||||
const formModel = ref({
|
||||
keyword: '',
|
||||
status: 0,
|
||||
role: 0,
|
||||
organid: 0
|
||||
})
|
||||
const tableData = ref([])
|
||||
const pagination = ref({
|
||||
current: 1,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
showPageSize: true,
|
||||
showTotal: true
|
||||
})
|
||||
|
||||
const columns = [
|
||||
{title: '编号', dataIndex: 'memberid', width: 80},
|
||||
{title: '姓名', dataIndex: 'membername', width: 120},
|
||||
{title: '账号', dataIndex: 'account', width: 150},
|
||||
{title: '联系电话', dataIndex: 'contactphone', width: 130},
|
||||
{title: '机构ID', dataIndex: 'organid', width: 100},
|
||||
{title: '状态', dataIndex: 'status', slotName: 'status', width: 80},
|
||||
{title: '创建时间', dataIndex: 'createtime', width: 180},
|
||||
{title: '修改时间', dataIndex: 'updatetime', width: 180}
|
||||
]
|
||||
|
||||
const loadData = async () => {
|
||||
if (!window.go || !window.go.main || !window.go.main.App || !window.go.main.App.QueryUsers) {
|
||||
console.error('Go 后端未就绪,请确保应用已启动')
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
// 升级提示事件处理
|
||||
const handleUpdateInstall = async (filePath) => {
|
||||
try {
|
||||
const result = await window.go.main.App.QueryUsers(
|
||||
formModel.value.keyword || '',
|
||||
formModel.value.status || 0,
|
||||
formModel.value.role || 0,
|
||||
formModel.value.organid || 0,
|
||||
pagination.value.current,
|
||||
pagination.value.pageSize,
|
||||
'createtime',
|
||||
'descend'
|
||||
)
|
||||
|
||||
if (result && result.rows) {
|
||||
tableData.value = result.rows
|
||||
pagination.value.total = result.total || 0
|
||||
const result = await window.go.main.App.InstallUpdate(filePath, true)
|
||||
if (result.success) {
|
||||
Message.success({
|
||||
content: '安装成功!应用将在几秒后重启...',
|
||||
duration: 3000
|
||||
})
|
||||
} else {
|
||||
Message.error(result.message || '安装失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('查询失败:', error)
|
||||
Message.error('查询失败: ' + (error.message || error))
|
||||
} finally {
|
||||
loading.value = false
|
||||
console.error('安装失败:', error)
|
||||
Message.error('安装失败:' + (error.message || error))
|
||||
}
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
pagination.value.current = 1
|
||||
loadData()
|
||||
const handleUpdateSkip = () => {
|
||||
// 清除跳过的版本记录(如果用户选择"稍后提醒")
|
||||
// 版本记录在组件内部处理
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
formModel.value = {
|
||||
keyword: '',
|
||||
status: 0,
|
||||
role: 0,
|
||||
organid: 0
|
||||
// 监听 activeTab 变化,如果当前 Tab 不在可见列表中,切换到默认 Tab
|
||||
watch(activeTab, (newTab) => {
|
||||
// 保存到 localStorage
|
||||
localStorage.setItem(ACTIVE_TAB_STORAGE_KEY, newTab)
|
||||
|
||||
// 检查 Tab 是否在可见列表中
|
||||
const isVisible = appConfig.value.visibleTabs.includes(newTab)
|
||||
if (!isVisible && appConfig.value.visibleTabs.length > 0) {
|
||||
// 切换到默认 Tab
|
||||
activeTab.value = appConfig.value.defaultTab
|
||||
}
|
||||
pagination.value.current = 1
|
||||
loadData()
|
||||
}
|
||||
|
||||
const handlePageChange = (page) => {
|
||||
pagination.value.current = page
|
||||
loadData()
|
||||
}
|
||||
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pagination.value.pageSize = pageSize
|
||||
pagination.value.current = 1
|
||||
loadData()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -374,14 +455,6 @@ onMounted(() => {
|
||||
overflow: auto;
|
||||
background: var(--color-bg-1);
|
||||
}
|
||||
|
||||
.search-card {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.table-card {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Wails 拖拽样式 -->
|
||||
|
||||
Reference in New Issue
Block a user