Compare commits

..

2 Commits

Author SHA1 Message Date
ygxbnet 409aac8486 feat(token): 添加数据批量上传功能
部署开发环境 / deploy-dev (push) Failing after 26s
- 新增 AddDataDialog 组件用于数据批量上传
- 在 TokenManageView 中集成数据上传对话框
- 实现按行分割数据并逐条上传的功能
- 添加上传进度显示和计数功能
- 集成 API 接口进行数据提交
- 添加上传状态控制和错误处理机制
2026-04-24 17:48:42 +08:00
ygxbnet 4ca3356214 feat(admin): 添加管理员后台布局并优化Token管理界面
- 新增AdminView.vue作为管理员后台主布局组件
- 集成导航菜单支持Token管理和详情页面切换
- 添加管理员身份标识和退出功能
- 在TokenManageView中引入DocumentAdd图标用于数据添加
- 为表格行添加数据查看对话框功能
- 调整Token管理表格列宽度以改善显示效果
- 优化移动端响应式布局适配
2026-04-24 16:51:20 +08:00
4 changed files with 175 additions and 7 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

+150
View File
@@ -0,0 +1,150 @@
<script setup lang="ts">
import {ref, computed} from "vue"
import axios from "@/axios.ts"
import {ElMessage} from 'element-plus'
import {Upload} from '@element-plus/icons-vue'
import * as timers from "node:timers";
const props = defineProps<{
modelValue: boolean
token?: string
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void
}>()
const inputData = ref('')
const uploading = ref(false)
const uploadedCount = ref(0)
const totalCount = computed(() => {
const lines = inputData.value.split('\n').filter(line => line.trim())
return lines.length
})
const close = () => {
emit('update:modelValue', false)
inputData.value = ''
uploadedCount.value = 0
}
const handleUpload = async () => {
if (!inputData.value.trim()) {
ElMessage.warning('请输入数据')
return
}
uploading.value = true
uploadedCount.value = 0
const lines = inputData.value.split('\n').filter(line => line.trim())
const total = lines.length
uploadedCount.value = 0
for (const line of lines) {
try {
await axios.post('/api/data', {}, {
params: {
data: line,
token: props.token || ''
}
})
uploadedCount.value++
} catch (error: any) {
ElMessage.error(error.response?.data?.error || '上传失败')
uploading.value = false
return
}
}
uploading.value = false
ElMessage({message: '已上传成功 ' + uploadedCount.value + ' 条数据', type: 'success'})
setTimeout(() => {
close()
}, 2000)
}
</script>
<template>
<el-dialog
:model-value="modelValue"
@update:model-value="emit('update:modelValue', $event)"
width="700px"
:close-on-click-modal="false"
@close="close"
>
<template #title>
添加数据&nbsp;
<el-tag type="primary" effect="plain">{{ props.token }}</el-tag>
</template>
<div class="add-data-dialog">
<el-input
v-model="inputData"
type="textarea"
:rows="18"
placeholder="请输入数据,每行一条"
:disabled="uploading"
class="textarea-input"
/>
<div class="footer">
<div class="progress">
<el-progress
:percentage="totalCount > 0 ? Math.round((uploadedCount / totalCount) * 100) : 0"
:show-text="false"
/>
<span class="progress-text">已上传 {{ uploadedCount }}/{{ totalCount }} </span>
</div>
<el-button
type="primary"
:loading="uploading"
@click="handleUpload"
>
<el-icon v-if="!uploading">
<Upload/>
</el-icon>
上传
</el-button>
</div>
</div>
</el-dialog>
</template>
<style scoped>
.add-data-dialog {
display: flex;
flex-direction: column;
gap: 16px;
}
.textarea-input :deep(.el-textarea__inner) {
overflow-x: auto;
white-space: pre;
word-break: keep-all;
}
.footer {
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
}
.progress {
flex: 1;
display: flex;
align-items: center;
gap: 12px;
}
.progress-text {
font-size: 14px;
color: var(--text-secondary);
white-space: nowrap;
}
.footer .el-progress {
flex: 1;
}
</style>
+2 -2
View File
@@ -2,7 +2,7 @@
import {ref, watch, computed} from 'vue' import {ref, watch, computed} from 'vue'
import {useRoute, useRouter} from "vue-router" import {useRoute, useRouter} from "vue-router"
import {useCounterStore} from "@/stores/counter.ts" import {useCounterStore} from "@/stores/counter.ts"
import {Edit, Delete, Clock, User, Document} from '@element-plus/icons-vue' import {Edit, Delete, Clock, User, Document, CloseBold} from '@element-plus/icons-vue'
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
@@ -70,7 +70,7 @@ const logout = () => {
@click="logout" @click="logout"
> >
<el-icon> <el-icon>
<Delete/> <CloseBold/>
</el-icon> </el-icon>
退出 退出
</el-button> </el-button>
+23 -5
View File
@@ -4,7 +4,8 @@ import axios from "@/axios.ts"
import {ElMessage} from 'element-plus' import {ElMessage} from 'element-plus'
import {useCounterStore} from "@/stores/counter.ts" import {useCounterStore} from "@/stores/counter.ts"
import {useRouter} from "vue-router" import {useRouter} from "vue-router"
import {Plus, View, Edit, Delete, Key, Document, Memo, Search, Lock} from '@element-plus/icons-vue' import {Plus, View, Edit, Delete, Key, Document, Memo, Search, Lock, DocumentAdd} from '@element-plus/icons-vue'
import AddDataDialog from '@/components/AddDataDialog.vue'
const store = useCounterStore() const store = useCounterStore()
const router = useRouter() const router = useRouter()
@@ -19,6 +20,7 @@ const inputPassWord = ref('')
const passwordVisible = ref(true) const passwordVisible = ref(true)
const rowOut = ref<any>(null) const rowOut = ref<any>(null)
const addDataDialogVisible = ref(false)
const dedupObjectVisible = ref(false) const dedupObjectVisible = ref(false)
const dataFormatVisible = ref(false) const dataFormatVisible = ref(false)
const inputNotesVisible = ref(false) const inputNotesVisible = ref(false)
@@ -114,6 +116,11 @@ const dialogNotesVisible = (row: any) => {
inputNotesVisible.value = true inputNotesVisible.value = true
} }
const dialogDataADDVisible = (row: any) => {
rowOut.value = row
addDataDialogVisible.value = true
}
const updateDedupObject = async () => { const updateDedupObject = async () => {
try { try {
await axios.put('/api/token', {}, { await axios.put('/api/token', {}, {
@@ -265,7 +272,7 @@ const deleteToken = async (row: any) => {
</div> </div>
<el-table :data="tableData" v-loading="loading" stripe style="width: 100%"> <el-table :data="tableData" v-loading="loading" stripe style="width: 100%">
<el-table-column prop="token" label="Token" min-width="200" show-overflow-tooltip> <el-table-column prop="token" label="Token" min-width="100" show-overflow-tooltip>
<template #default="{ row }"> <template #default="{ row }">
<div class="token-cell"> <div class="token-cell">
<el-icon> <el-icon>
@@ -282,19 +289,19 @@ const deleteToken = async (row: any) => {
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="data_format" label="数据格式" min-width="350" show-overflow-tooltip> <el-table-column prop="data_format" label="数据格式" min-width="200" show-overflow-tooltip>
<template #default="{ row }"> <template #default="{ row }">
<div class="format-cell">{{ row.data_format }}</div> <div class="format-cell">{{ row.data_format }}</div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="notes" label="备注" min-width="250" show-overflow-tooltip> <el-table-column prop="notes" label="备注" min-width="150" show-overflow-tooltip>
<template #default="{ row }"> <template #default="{ row }">
<span class="notes-text">{{ row.notes || '-' }}</span> <span class="notes-text">{{ row.notes || '-' }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="280" fixed="right"> <el-table-column label="操作" width="500" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<div class="action-buttons"> <div class="action-buttons">
<el-button type="primary" size="small" text @click="viewDetails(row)"> <el-button type="primary" size="small" text @click="viewDetails(row)">
@@ -303,6 +310,12 @@ const deleteToken = async (row: any) => {
</el-icon> </el-icon>
详情 详情
</el-button> </el-button>
<el-button size="small" text @click="dialogDataADDVisible(row)">
<el-icon>
<Plus/>
</el-icon>
数据
</el-button>
<el-button size="small" text @click="dialogDedupObjectVisible(row)"> <el-button size="small" text @click="dialogDedupObjectVisible(row)">
<el-icon> <el-icon>
<Edit/> <Edit/>
@@ -371,6 +384,11 @@ const deleteToken = async (row: any) => {
<el-button type="primary" @click="updateNotes">确定</el-button> <el-button type="primary" @click="updateNotes">确定</el-button>
</template> </template>
</el-dialog> </el-dialog>
<AddDataDialog
v-model="addDataDialogVisible"
:token="rowOut?.token"
/>
</div> </div>
</div> </div>
</template> </template>