Files
dypid-client/frontend/src/App.vue
T
ygxbnet e15debc962
构建上传工具 / build (push) Successful in 2m34s
fix(config): 修复配置目录选择后未保存的问题
- 在用户选择目录后调用 writeCheckDir() 方法保存配置
- 确保选中的目录路径能够正确写入到配置文件中
2026-06-29 23:14:45 +08:00

355 lines
8.4 KiB
Vue

<script lang="ts" setup>
import {computed, nextTick, ref} from 'vue'
import {ElMessage, ElMessageBox} from 'element-plus'
import ConfirmClearDialog from './components/ConfirmClearDialog.vue'
import {GetConfig, SelectPath, StartUpload, StopUpload, WriteConfig} from '../wailsjs/go/main/App';
import {config} from "../wailsjs/go/models.ts";
import {EventsOn, LogPrint} from "../wailsjs/runtime";
import {configModel} from "@/model.ts";
import Config = config.Config;
// const serverUrl = ref('')
const token = ref('')
const checkDir = ref('')
const concurrentFiles = ref(1)
const uploadThreads = ref(1)
// const autoStart = ref(false)
const isRunning = ref(false)
const logOutput = ref<string[]>([])
const logContentRef = ref<HTMLElement>()
const logRoll = ref(true)
const clearDialogVisible = ref(false)
const filesToClear = ref<string[]>([])
const noPromptClear = ref(false)
interface FileProgress {
name: string
uploaded: number
total: number
percentage: number
}
// {name: '测试文件1.txt', uploaded: 100, total: 500, percentage: 20},
const progressList = ref<FileProgress[]>([])
const sortedProgressList = computed(() => {
return [...progressList.value].sort((a, b) => {
const getPriority = (item: FileProgress) => {
if (item.percentage === 100) return 2
if (item.percentage === 0) return 1
return 0
}
return getPriority(a) - getPriority(b)
})
})
const addLog = (msg: string) => {
logOutput.value.push(`[${new Date().toLocaleString()}] ` + msg)
nextTick(() => {
if (logContentRef.value && logRoll.value) {
logContentRef.value.scrollTop = logContentRef.value.scrollHeight
}
})
}
const selectDirectory = () => {
// ElMessage.info('请手动输入检测目录路径')
SelectPath().then((path) => {
if (path) {
checkDir.value = path
writeCheckDir()
} else {
ElMessage.warning('未选择目录,不更改配置')
}
})
}
const startRun = () => {
// if (!serverUrl.value) {
// ElMessage.warning('请输入服务器地址')
// return
// }
if (!token.value) {
ElMessage.error('请输入Token')
return
}
if (!checkDir.value) {
ElMessage.error('请选择检测目录')
return
}
StartUpload()
}
const stopRun = () => {
addLog(`正在停止运行`)
StopUpload().then(() => {
ElMessage.success('已停止运行')
})
}
const clearLog = () => {
ElMessageBox.confirm('确定要清除日志吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
logOutput.value = []
ElMessage.success('日志已清除')
}).catch(() => {
})
}
// const writeServerUrl =() => {
// WriteConfig("url", serverUrl.value)
// }
const writeToken = () => {
WriteConfig(configModel.Token, token.value)
}
const writeCheckDir = () => {
WriteConfig(configModel.CheckDir, checkDir.value)
}
const writeConcurrentFiles = () => {
WriteConfig(configModel.HandleFileCount, concurrentFiles.value)
}
const writeUploadThreads = () => {
WriteConfig(configModel.ThreadCount, uploadThreads.value)
}
// const writeAutoStart = () => {
// WriteConfig("is-run-on-start", autoStart.value)
// }
// 加载配置
try {
GetConfig().then((config: Config) => {
// serverUrl.value = config.url
token.value = config.token
checkDir.value = config.check_dir
concurrentFiles.value = config.handle_file_count
uploadThreads.value = config.thread_count
// autoStart.value = config.is_run_on_start
noPromptClear.value = config.clear_files_no_prompt
LogPrint(`[${new Date().toLocaleString()}] 配置已加载`)
})
} catch (e) {
console.log(e)
}
try {
EventsOn("is-run", (run) => {
isRunning.value = run
})
EventsOn("progress", (progress) => {
progressList.value = progress
})
EventsOn("log", (msg) => {
addLog(msg)
})
EventsOn("clear-files", (files) => {
filesToClear.value = files
if (!noPromptClear.value) {
clearDialogVisible.value = true
}
})
} catch (e) {
console.log(e)
}
</script>
<template>
<div class="container">
<div class="left-panel">
<!-- <div class="form-item">-->
<!-- <label>服务器地址</label>-->
<!-- <el-input v-model="serverUrl" placeholder="请输入服务器地址" :disabled="isRunning" @change="writeServerUrl()"/>-->
<!-- </div>-->
<div class="form-item">
<label>Token</label>
<el-input v-model="token" placeholder="请输入Token" :disabled="isRunning" @change="writeToken()"/>
</div>
<div class="form-item">
<label>检测目录</label>
<div class="dir-input">
<el-input v-model="checkDir" placeholder="请选择检测目录" :disabled="isRunning" @change="writeCheckDir()"/>
<el-button @click="selectDirectory" :disabled="isRunning">选择目录</el-button>
</div>
</div>
<div class="form-item">
<label>同时处理文件数</label>
<el-input-number v-model="concurrentFiles" :min="1" :max="100" :disabled="isRunning"
@change="writeConcurrentFiles()"/>
</div>
<div class="form-item">
<label>单文件上传线程</label>
<el-input-number v-model="uploadThreads" :min="1" :max="100" :disabled="isRunning"
@change="writeUploadThreads()"/>
</div>
<!-- <div class="form-item">-->
<!-- <el-checkbox v-model="autoStart" label="运行时自动启动上传" size="large" :disabled="isRunning" @change="writeAutoStart()"/>-->
<!-- </div>-->
<div class="form-item">
<label>上传进度</label>
<div class="progress-list">
<div v-for="(item, index) in sortedProgressList" :key="index" class="progress-item">
<span class="file-name">{{ item.name }}</span>
<el-progress :percentage="item.percentage" :status="item.percentage === 100 ? 'success' : undefined"/>
<span class="progress-text">{{ item.uploaded }}/{{ item.total }}</span>
</div>
</div>
</div>
<div class="buttons">
<el-button type="primary" @click="startRun" :disabled="isRunning">开始运行</el-button>
<el-button type="danger" @click="stopRun" :disabled="!isRunning">停止运行</el-button>
<el-button @click="clearLog">清除日志</el-button>
</div>
</div>
<div class="right-panel">
<div class="log-header">
日志输出
<el-checkbox v-model="logRoll" label="开启日志滚动"/>
</div>
<div class="log-content" ref="logContentRef">
<div v-for="(log, index) in logOutput" :key="index" class="log-line">{{ log }}</div>
</div>
</div>
</div>
<ConfirmClearDialog
v-model:visible="clearDialogVisible"
:file-list="filesToClear"
/>
</template>
<style scoped>
.container {
display: flex;
height: calc(100vh - 40px);
padding: 20px;
gap: 20px;
background-color: #f5f5f5;
}
.left-panel {
width: 350px;
min-width: 300px;
background: white;
padding: 20px;
border-radius: 8px;
display: flex;
flex-direction: column;
gap: 16px;
}
.form-item {
display: flex;
flex-direction: column;
}
.form-item label {
font-size: 14px;
color: #606266;
font-weight: 500;
}
.dir-input {
display: flex;
gap: 8px;
}
.dir-input .el-input {
flex: 1;
}
.buttons {
display: flex;
gap: 10px;
flex-wrap: wrap;
margin-top: 10px;
}
.progress-list {
margin-top: 12px;
max-height: 250px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 10px;
}
.progress-item {
display: flex;
align-items: center;
}
.file-name {
width: 130px;
font-size: 12px;
color: #606266;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.progress-item .el-progress {
margin-left: 8px;
flex: 1;
}
.progress-text {
width: 75px;
font-size: 12px;
color: #909399;
text-align: right;
}
.right-panel {
flex: 1;
background: white;
border-radius: 8px;
display: flex;
flex-direction: column;
overflow: hidden;
}
.log-header {
padding: 12px 20px;
border-bottom: 1px solid #e4e7ed;
font-size: 14px;
font-weight: 500;
color: #303133;
display: flex;
justify-content: space-between;
align-items: center;
}
.log-content {
flex: 1;
padding: 5px 5px;
overflow-x: auto;
overflow-y: auto;
background: #1e1e1e;
font-family: Consolas, Monaco, 'Courier New', monospace;
font-size: 12px;
}
.log-line {
color: #dcdfe4;
line-height: 1.4;
white-space: nowrap;
}
.log-line:hover {
background: #2d2d2d;
}
</style>