日志
This commit is contained in:
@@ -5,6 +5,7 @@ export const authlogin = (username, password) => post('auth/login ', {
|
|||||||
userName: username,
|
userName: username,
|
||||||
password: password
|
password: password
|
||||||
})
|
})
|
||||||
|
|
||||||
// 建图
|
// 建图
|
||||||
export const startMapping = (na) => post('teaching/startMapping?mapName=' + na, {})
|
export const startMapping = (na) => post('teaching/startMapping?mapName=' + na, {})
|
||||||
export const getMappingStatus = () => post('teaching/getMappingStatus', {})
|
export const getMappingStatus = () => post('teaching/getMappingStatus', {})
|
||||||
|
|||||||
@@ -855,4 +855,40 @@ export const relocate = (x, y, angle) => {
|
|||||||
message: 'ok'
|
message: 'ok'
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
export const apiLogs = () => {
|
||||||
|
let res = [
|
||||||
|
{
|
||||||
|
"fileName": "info.2025-11-03.log",
|
||||||
|
"filePath": "D:\\Procedure\\noblelift\\hangzhou\\APT15E\\logs\\root\\info.2025-11-03.log",
|
||||||
|
"fileSize": 272452,
|
||||||
|
"lastModified": [
|
||||||
|
2025,
|
||||||
|
11,
|
||||||
|
3,
|
||||||
|
15,
|
||||||
|
54,
|
||||||
|
54,
|
||||||
|
959000000
|
||||||
|
],
|
||||||
|
"readableSize": "266.1 KB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fileName": "info.2025-10-29.log",
|
||||||
|
"filePath": "D:\\Procedure\\noblelift\\hangzhou\\APT15E\\logs\\root\\info.2025-10-29.log",
|
||||||
|
"fileSize": 0,
|
||||||
|
"lastModified": [
|
||||||
|
2025,
|
||||||
|
10,
|
||||||
|
29,
|
||||||
|
14,
|
||||||
|
27,
|
||||||
|
51,
|
||||||
|
873000000
|
||||||
|
],
|
||||||
|
"readableSize": "0 B"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ import App from './App.vue'
|
|||||||
import router from './router'
|
import router from './router'
|
||||||
import store from './vuex/store'
|
import store from './vuex/store'
|
||||||
import './style/reset.css'
|
import './style/reset.css'
|
||||||
import { Row, Col, Button, Icon, Dialog, Form, FormItem, Input, Select, Option, Table, TableColumn, Tabs, TabPane, Popover, Loading, MessageBox, Message, Progress, Upload } from 'element-ui'
|
import { Row, Col, Button, Icon, Dialog, Form, FormItem, Input, Select, Option, Table, TableColumn, Tabs, TabPane, Popover, Loading, MessageBox, Message, Progress, Upload, Menu, MenuItem } from 'element-ui'
|
||||||
import 'element-ui/lib/theme-chalk/index.css'
|
import 'element-ui/lib/theme-chalk/index.css'
|
||||||
import './style/common.styl'
|
import './style/common.styl'
|
||||||
import i18n from './i18n/i18n'
|
import i18n from './i18n/i18n'
|
||||||
@@ -30,6 +30,8 @@ Vue.use(Popover)
|
|||||||
Vue.use(Loading)
|
Vue.use(Loading)
|
||||||
Vue.use(Progress)
|
Vue.use(Progress)
|
||||||
Vue.use(Upload)
|
Vue.use(Upload)
|
||||||
|
Vue.use(Menu)
|
||||||
|
Vue.use(MenuItem)
|
||||||
Vue.prototype.$confirm = MessageBox.confirm
|
Vue.prototype.$confirm = MessageBox.confirm
|
||||||
Vue.prototype.$message = Message
|
Vue.prototype.$message = Message
|
||||||
Vue.prototype.$post = post
|
Vue.prototype.$post = post
|
||||||
@@ -45,12 +47,16 @@ Vue.prototype.$langPre = {
|
|||||||
// 从本地存储中读取 serverUrl,userRole
|
// 从本地存储中读取 serverUrl,userRole
|
||||||
const savedServerUrl = localStorage.getItem('serverUrl');
|
const savedServerUrl = localStorage.getItem('serverUrl');
|
||||||
const savedUserRole = localStorage.getItem('userRole');
|
const savedUserRole = localStorage.getItem('userRole');
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
if (savedServerUrl) {
|
if (savedServerUrl) {
|
||||||
store.commit('SET_BASE_URL', savedServerUrl);
|
store.commit('SET_BASE_URL', savedServerUrl);
|
||||||
}
|
}
|
||||||
if (savedUserRole) {
|
if (savedUserRole) {
|
||||||
store.commit('SET_USER_ROLE', parseInt(savedUserRole, 10));
|
store.commit('SET_USER_ROLE', parseInt(savedUserRole, 10));
|
||||||
}
|
}
|
||||||
|
if (token) {
|
||||||
|
store.commit('SET_TOKEN', token);
|
||||||
|
}
|
||||||
|
|
||||||
Vue.filter('findByValue', (array, value) => {
|
Vue.filter('findByValue', (array, value) => {
|
||||||
if (!Array.isArray(array)) return ''
|
if (!Array.isArray(array)) return ''
|
||||||
|
|||||||
@@ -1,277 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="page_container">
|
<div class="app-container">
|
||||||
<div class="container">
|
<el-menu
|
||||||
<div class="upload-container">
|
:default-active="$route.path"
|
||||||
<!-- Excel文件上传 -->
|
class="top-menu"
|
||||||
<div class="upload-card">
|
mode="horizontal"
|
||||||
<div class="upload-icon" style="color: #67C23A;">
|
background-color="#304156"
|
||||||
<i class="el-icon-document"></i>
|
text-color="#fff"
|
||||||
</div>
|
active-text-color="#69cdfb"
|
||||||
<div class="upload-title">异常信息Excel文件上传</div>
|
router
|
||||||
<div class="upload-desc">支持.xlsx和.xls格式,最大10MB</div>
|
>
|
||||||
<div class="upload-btn">
|
<el-menu-item index="/hub/upload">
|
||||||
<el-upload
|
<i class="el-icon-upload"></i>
|
||||||
ref="excelUpload1"
|
<span>文件上传</span>
|
||||||
:action="`${serverUrl}/anomalyInfo/importErrorInfoExcel`"
|
</el-menu-item>
|
||||||
:limit="1"
|
<el-menu-item index="/hub/logs">
|
||||||
:before-upload="beforeExcelUpload"
|
<i class="el-icon-document"></i>
|
||||||
:on-success="handleExcelSuccess1"
|
<span>日志文件列表</span>
|
||||||
:on-error="handleError"
|
</el-menu-item>
|
||||||
:file-list="excelFileList1"
|
</el-menu>
|
||||||
accept=".xlsx, .xls">
|
<router-view></router-view>
|
||||||
<el-button size="medium" type="success" icon="el-icon-upload">上传Excel文件</el-button>
|
|
||||||
</el-upload>
|
|
||||||
</div>
|
|
||||||
<div class="upload-progress" v-if="excelProgress1 > 0">
|
|
||||||
<el-progress :percentage="excelProgress1" :status="excelStatus1"></el-progress>
|
|
||||||
</div>
|
|
||||||
<div class="file-list">
|
|
||||||
<div class="file-item" v-for="file in excelFileList1" :key="file.uid">
|
|
||||||
<i class="el-icon-document" style="color: #67C23A;"></i>
|
|
||||||
<span class="file-name">{{ file.name }}</span>
|
|
||||||
<span class="file-status" :class="{ error: file.status === 'error' }">
|
|
||||||
{{ file.status === 'success' ? '上传成功' : (file.status === 'error' ? '上传失败' : '上传中') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 第二个Excel文件上传 -->
|
|
||||||
<div class="upload-card">
|
|
||||||
<div class="upload-icon" style="color: #E6A23C;">
|
|
||||||
<i class="el-icon-document"></i>
|
|
||||||
</div>
|
|
||||||
<div class="upload-title">异常信息处理方式Excel文件上传</div>
|
|
||||||
<div class="upload-desc">支持.xlsx和.xls格式,最大10MB</div>
|
|
||||||
<div class="upload-btn">
|
|
||||||
<el-upload
|
|
||||||
ref="excelUpload2"
|
|
||||||
:action="`${serverUrl}/anomalyInfo/importErrorHandlingExcel`"
|
|
||||||
:on-success="handleExcelSuccess2"
|
|
||||||
:on-error="handleError"
|
|
||||||
:before-upload="beforeExcelUpload"
|
|
||||||
:file-list="excelFileList2"
|
|
||||||
:limit="1"
|
|
||||||
accept=".xlsx, .xls">
|
|
||||||
<el-button size="medium" type="warning" icon="el-icon-upload">上传Excel文件</el-button>
|
|
||||||
</el-upload>
|
|
||||||
</div>
|
|
||||||
<div class="upload-progress" v-if="excelProgress2 > 0">
|
|
||||||
<el-progress :percentage="excelProgress2" :status="excelStatus2"></el-progress>
|
|
||||||
</div>
|
|
||||||
<div class="file-list">
|
|
||||||
<div class="file-item" v-for="file in excelFileList2" :key="file.uid">
|
|
||||||
<i class="el-icon-document" style="color: #E6A23C;"></i>
|
|
||||||
<span class="file-name">{{ file.name }}</span>
|
|
||||||
<span class="file-status" :class="{ error: file.status === 'error' }">
|
|
||||||
{{ file.status === 'success' ? '上传成功' : (file.status === 'error' ? '上传失败' : '上传中') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ZIP文件上传 -->
|
|
||||||
<div class="upload-card">
|
|
||||||
<div class="upload-icon" style="color: #409EFF;">
|
|
||||||
<i class="el-icon-folder-opened"></i>
|
|
||||||
</div>
|
|
||||||
<div class="upload-title">异常处理图片ZIP文件上传</div>
|
|
||||||
<div class="upload-desc">支持.zip格式,最大50MB</div>
|
|
||||||
<div class="upload-btn">
|
|
||||||
<el-upload
|
|
||||||
ref="zipUpload"
|
|
||||||
:action="`${serverUrl}/anomalyInfo/importErrorImage`"
|
|
||||||
:on-success="handleZipSuccess"
|
|
||||||
:on-error="handleError"
|
|
||||||
:before-upload="beforeZipUpload"
|
|
||||||
:file-list="zipFileList"
|
|
||||||
:limit="1"
|
|
||||||
accept=".zip">
|
|
||||||
<el-button size="medium" type="primary" icon="el-icon-upload">上传ZIP文件</el-button>
|
|
||||||
</el-upload>
|
|
||||||
</div>
|
|
||||||
<div class="upload-progress" v-if="zipProgress > 0">
|
|
||||||
<el-progress :percentage="zipProgress" :status="zipStatus"></el-progress>
|
|
||||||
</div>
|
|
||||||
<div class="file-list">
|
|
||||||
<div class="file-item" v-for="file in zipFileList" :key="file.uid">
|
|
||||||
<i class="el-icon-folder-opened" style="color: #409EFF;"></i>
|
|
||||||
<span class="file-name">{{ file.name }}</span>
|
|
||||||
<span class="file-status" :class="{ error: file.status === 'error' }">
|
|
||||||
{{ file.status === 'success' ? '上传成功' : (file.status === 'error' ? '上传失败' : '上传中') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/* eslint-disable */
|
|
||||||
import { mapGetters } from 'vuex'
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ModuleRelocation',
|
name: 'IndexHub',
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {}
|
||||||
dataList: [],
|
|
||||||
excelFileList1: [],
|
|
||||||
excelProgress1: 0,
|
|
||||||
excelStatus1: '',
|
|
||||||
excelFileList2: [],
|
|
||||||
excelProgress2: 0,
|
|
||||||
excelStatus2: '',
|
|
||||||
zipFileList: [],
|
|
||||||
zipProgress: 0,
|
|
||||||
zipStatus: ''
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
computed: {
|
methods: {}
|
||||||
...mapGetters(['serverUrl'])
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// Excel文件上传前的验证
|
|
||||||
beforeExcelUpload(file) {
|
|
||||||
const isExcel = file.type === 'application/vnd.ms-excel' ||
|
|
||||||
file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
|
||||||
const isLt10M = file.size / 1024 / 1024 < 10;
|
|
||||||
|
|
||||||
if (!isExcel) {
|
|
||||||
this.$message.error('只能上传Excel文件!');
|
|
||||||
}
|
|
||||||
if (!isLt10M) {
|
|
||||||
this.$message.error('Excel文件大小不能超过10MB!');
|
|
||||||
}
|
|
||||||
return isExcel && isLt10M;
|
|
||||||
},
|
|
||||||
// ZIP文件上传前的验证
|
|
||||||
beforeZipUpload(file) {
|
|
||||||
const isZip = file.type === 'application/zip' ||
|
|
||||||
file.type === 'application/x-zip-compressed';
|
|
||||||
const isLt50M = file.size / 1024 / 1024 < 50;
|
|
||||||
|
|
||||||
if (!isZip) {
|
|
||||||
this.$message.error('只能上传ZIP文件!');
|
|
||||||
}
|
|
||||||
if (!isLt50M) {
|
|
||||||
this.$message.error('ZIP文件大小不能超过50MB!');
|
|
||||||
}
|
|
||||||
return isZip && isLt50M;
|
|
||||||
},
|
|
||||||
// 第一个Excel上传成功回调
|
|
||||||
handleExcelSuccess1(response, file, fileList) {
|
|
||||||
this.excelFileList1 = fileList;
|
|
||||||
this.excelProgress1 = 100;
|
|
||||||
this.excelStatus1 = 'success';
|
|
||||||
this.$message.success('第一个Excel文件上传成功!');
|
|
||||||
},
|
|
||||||
// 第二个Excel上传成功回调
|
|
||||||
handleExcelSuccess2(response, file, fileList) {
|
|
||||||
this.excelFileList2 = fileList;
|
|
||||||
this.excelProgress2 = 100;
|
|
||||||
this.excelStatus2 = 'success';
|
|
||||||
this.$message.success('第二个Excel文件上传成功!');
|
|
||||||
},
|
|
||||||
// ZIP上传成功回调
|
|
||||||
handleZipSuccess(response, file, fileList) {
|
|
||||||
this.zipFileList = fileList;
|
|
||||||
this.zipProgress = 100;
|
|
||||||
this.zipStatus = 'success';
|
|
||||||
this.$message.success('ZIP文件上传成功!');
|
|
||||||
},
|
|
||||||
// 上传失败回调
|
|
||||||
handleError(err, file, fileList) {
|
|
||||||
this.$message.error('文件上传失败!');
|
|
||||||
if (file.raw.type.includes('excel') || file.name.includes('.xls')) {
|
|
||||||
this.excelProgress1 = 0;
|
|
||||||
this.excelProgress2 = 0;
|
|
||||||
} else {
|
|
||||||
this.zipProgress = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
.upload-container {
|
.app-container {
|
||||||
display: flex;
|
height: 100%;
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
.upload-card {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 300px;
|
|
||||||
background: white;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
||||||
padding: 25px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
.upload-card:hover {
|
|
||||||
transform: translateY(-5px);
|
|
||||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
.upload-icon {
|
|
||||||
font-size: 48px;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
.upload-title {
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: 600;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
.upload-desc {
|
|
||||||
color: #909399;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
.upload-btn {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.file-list {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
.file-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 8px 0;
|
|
||||||
border-bottom: 1px solid #ebeef5;
|
|
||||||
}
|
|
||||||
.file-item:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
.file-name {
|
|
||||||
flex: 1;
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
.file-status {
|
|
||||||
color: #67C23A;
|
|
||||||
}
|
|
||||||
.file-status.error {
|
|
||||||
color: #F56C6C;
|
|
||||||
}
|
|
||||||
.upload-progress {
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
|
||||||
.footer {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 40px;
|
|
||||||
color: #909399;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.upload-container {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.upload-card {
|
|
||||||
min-width: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
159
src/pages/modules/hub/logs.vue
Normal file
159
src/pages/modules/hub/logs.vue
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page_container">
|
||||||
|
<div class="logs-header">
|
||||||
|
<el-button type="primary" icon="el-icon-refresh" style="margin: 20px 0;" @click="_apiLogs">刷新</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
:data="logFiles"
|
||||||
|
style="width: 100%"
|
||||||
|
v-loading="loading"
|
||||||
|
height="calc(100% - 75px)"
|
||||||
|
>
|
||||||
|
<el-table-column prop="fileName" label="文件名称" min-width="110" fixed="left">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<i class="el-icon-document" style="margin-right: 8px;"></i>
|
||||||
|
{{ scope.row.fileName }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="filePath" label="文件地址" min-width="100" show-overflow-tooltip>
|
||||||
|
<template slot-scope="scope">
|
||||||
|
{{ scope.row.filePath }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="fileSize" label="文件大小">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
{{ scope.row.fileSize }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="lastModified" label="最后修改日期" min-width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
{{ formatDate(scope.row.lastModified) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="readableSize" label="可读大小">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
{{ scope.row.readableSize }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button
|
||||||
|
size="mini"
|
||||||
|
type="success"
|
||||||
|
@click="downloadLog(scope.row)"
|
||||||
|
>下载</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
import axios from 'axios'
|
||||||
|
export default {
|
||||||
|
name: 'hubLogs',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
logFiles: [],
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(['serverUrl', 'token'])
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this._apiLogs()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async _apiLogs () {
|
||||||
|
this.loading = true
|
||||||
|
this.logFiles = []
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${this.serverUrl}/api/logs/list`, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': this.token,
|
||||||
|
'Content-Type': 'application/json;charset=UTF-8'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (response.data && response.status === 200) {
|
||||||
|
this.logFiles = response.data.map(file => ({
|
||||||
|
...file,
|
||||||
|
downloading: false
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
this.$message.error('获取日志文件列表失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取日志文件列表错误:', error)
|
||||||
|
this.logFiles = []
|
||||||
|
this.$message.error(error.response?.data?.message || error.message || '获取日志文件列表失败')
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async downloadLog(file) {
|
||||||
|
if (!file.fileName) {
|
||||||
|
this.$message.error('文件名不能为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file.downloading = true
|
||||||
|
try {
|
||||||
|
// 构建下载URL
|
||||||
|
const downloadUrl = `${this.serverUrl}/api/logs/download/${encodeURIComponent(file.fileName)}`
|
||||||
|
|
||||||
|
// 使用 axios 下载文件
|
||||||
|
const response = await axios.get(downloadUrl, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': this.token
|
||||||
|
},
|
||||||
|
responseType: 'blob' // 重要:指定响应类型为 blob
|
||||||
|
})
|
||||||
|
|
||||||
|
// 创建 blob URL 并下载
|
||||||
|
const blob = new Blob([response.data])
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = file.fileName
|
||||||
|
link.style.display = 'none'
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
window.URL.revokeObjectURL(url) // 释放 blob URL
|
||||||
|
|
||||||
|
this.$message.success(`文件 ${file.fileName} 下载完成`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('下载失败:', error)
|
||||||
|
this.$message.error(`下载失败: ${error.response?.data?.message || error.message || '未知错误'}`)
|
||||||
|
} finally {
|
||||||
|
// 重置下载状态
|
||||||
|
file.downloading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formatDate(dateArray) {
|
||||||
|
if (!dateArray || !Array.isArray(dateArray) || dateArray.length < 6) {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [year, month, day, hour, minute, second] = dateArray
|
||||||
|
|
||||||
|
// 手动格式化,确保格式一致
|
||||||
|
const pad = (num) => num.toString().padStart(2, '0')
|
||||||
|
|
||||||
|
return `${year}-${pad(month)}-${pad(day)} ${pad(hour)}:${pad(minute)}:${pad(second)}`
|
||||||
|
} catch (e) {
|
||||||
|
console.error('日期格式化错误:', e)
|
||||||
|
return dateArray.slice(0, 6).map(num => num.toString().padStart(2, '0')).join('-')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.page_container {
|
||||||
|
height: calc(100% - 70px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
291
src/pages/modules/hub/upload.vue
Normal file
291
src/pages/modules/hub/upload.vue
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page_container">
|
||||||
|
<div class="upload-container">
|
||||||
|
<!-- Excel文件上传 -->
|
||||||
|
<div class="upload-card">
|
||||||
|
<div class="upload-icon" style="color: #67C23A;">
|
||||||
|
<i class="el-icon-document"></i>
|
||||||
|
</div>
|
||||||
|
<div class="upload-title">异常信息Excel文件上传</div>
|
||||||
|
<div class="upload-desc">支持.xlsx和.xls格式,最大10MB</div>
|
||||||
|
<div class="upload-btn">
|
||||||
|
<el-upload
|
||||||
|
ref="excelUpload1"
|
||||||
|
:action="`${serverUrl}/anomalyInfo/importErrorInfoExcel`"
|
||||||
|
:limit="1"
|
||||||
|
:before-upload="beforeExcelUpload"
|
||||||
|
:on-success="handleExcelSuccess1"
|
||||||
|
:on-error="handleError"
|
||||||
|
:file-list="excelFileList1"
|
||||||
|
:headers="uploadHeaders"
|
||||||
|
accept=".xlsx, .xls">
|
||||||
|
<el-button size="medium" type="success" icon="el-icon-upload">上传Excel文件</el-button>
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
<div class="upload-progress" v-if="excelProgress1 > 0">
|
||||||
|
<el-progress :percentage="excelProgress1" :status="excelStatus1"></el-progress>
|
||||||
|
</div>
|
||||||
|
<div class="file-list">
|
||||||
|
<div class="file-item" v-for="file in excelFileList1" :key="file.uid">
|
||||||
|
<i class="el-icon-document" style="color: #67C23A;"></i>
|
||||||
|
<span class="file-name">{{ file.name }}</span>
|
||||||
|
<span class="file-status" :class="{ error: file.status === 'error' }">
|
||||||
|
{{ file.status === 'success' ? '上传成功' : (file.status === 'error' ? '上传失败' : '上传中') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 第二个Excel文件上传 -->
|
||||||
|
<div class="upload-card">
|
||||||
|
<div class="upload-icon" style="color: #E6A23C;">
|
||||||
|
<i class="el-icon-document"></i>
|
||||||
|
</div>
|
||||||
|
<div class="upload-title">异常信息处理方式Excel文件上传</div>
|
||||||
|
<div class="upload-desc">支持.xlsx和.xls格式,最大10MB</div>
|
||||||
|
<div class="upload-btn">
|
||||||
|
<el-upload
|
||||||
|
ref="excelUpload2"
|
||||||
|
:action="`${serverUrl}/anomalyInfo/importErrorHandlingExcel`"
|
||||||
|
:on-success="handleExcelSuccess2"
|
||||||
|
:on-error="handleError"
|
||||||
|
:before-upload="beforeExcelUpload"
|
||||||
|
:file-list="excelFileList2"
|
||||||
|
:headers="uploadHeaders"
|
||||||
|
:limit="1"
|
||||||
|
accept=".xlsx, .xls">
|
||||||
|
<el-button size="medium" type="warning" icon="el-icon-upload">上传Excel文件</el-button>
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
<div class="upload-progress" v-if="excelProgress2 > 0">
|
||||||
|
<el-progress :percentage="excelProgress2" :status="excelStatus2"></el-progress>
|
||||||
|
</div>
|
||||||
|
<div class="file-list">
|
||||||
|
<div class="file-item" v-for="file in excelFileList2" :key="file.uid">
|
||||||
|
<i class="el-icon-document" style="color: #E6A23C;"></i>
|
||||||
|
<span class="file-name">{{ file.name }}</span>
|
||||||
|
<span class="file-status" :class="{ error: file.status === 'error' }">
|
||||||
|
{{ file.status === 'success' ? '上传成功' : (file.status === 'error' ? '上传失败' : '上传中') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ZIP文件上传 -->
|
||||||
|
<div class="upload-card">
|
||||||
|
<div class="upload-icon" style="color: #409EFF;">
|
||||||
|
<i class="el-icon-folder-opened"></i>
|
||||||
|
</div>
|
||||||
|
<div class="upload-title">异常处理图片ZIP文件上传</div>
|
||||||
|
<div class="upload-desc">支持.zip格式,最大50MB</div>
|
||||||
|
<div class="upload-btn">
|
||||||
|
<el-upload
|
||||||
|
ref="zipUpload"
|
||||||
|
:action="`${serverUrl}/anomalyInfo/importErrorImage`"
|
||||||
|
:on-success="handleZipSuccess"
|
||||||
|
:on-error="handleError"
|
||||||
|
:before-upload="beforeZipUpload"
|
||||||
|
:file-list="zipFileList"
|
||||||
|
:headers="uploadHeaders"
|
||||||
|
:limit="1"
|
||||||
|
accept=".zip">
|
||||||
|
<el-button size="medium" type="primary" icon="el-icon-upload">上传ZIP文件</el-button>
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
<div class="upload-progress" v-if="zipProgress > 0">
|
||||||
|
<el-progress :percentage="zipProgress" :status="zipStatus"></el-progress>
|
||||||
|
</div>
|
||||||
|
<div class="file-list">
|
||||||
|
<div class="file-item" v-for="file in zipFileList" :key="file.uid">
|
||||||
|
<i class="el-icon-folder-opened" style="color: #409EFF;"></i>
|
||||||
|
<span class="file-name">{{ file.name }}</span>
|
||||||
|
<span class="file-status" :class="{ error: file.status === 'error' }">
|
||||||
|
{{ file.status === 'success' ? '上传成功' : (file.status === 'error' ? '上传失败' : '上传中') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
export default {
|
||||||
|
name: 'hubUpload',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
excelFileList1: [],
|
||||||
|
excelProgress1: 0,
|
||||||
|
excelStatus1: '',
|
||||||
|
excelFileList2: [],
|
||||||
|
excelProgress2: 0,
|
||||||
|
excelStatus2: '',
|
||||||
|
zipFileList: [],
|
||||||
|
zipProgress: 0,
|
||||||
|
zipStatus: '',
|
||||||
|
uploadHeaders: {}, // 存储上传请求头
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(['serverUrl', 'token'])
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.setUploadHeaders()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setUploadHeaders() {
|
||||||
|
const token = this.token
|
||||||
|
if (token) {
|
||||||
|
this.uploadHeaders = {
|
||||||
|
'Authorization': token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Excel文件上传前的验证
|
||||||
|
beforeExcelUpload(file) {
|
||||||
|
const isExcel = file.type === 'application/vnd.ms-excel' ||
|
||||||
|
file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||||||
|
const isLt10M = file.size / 1024 / 1024 < 10;
|
||||||
|
|
||||||
|
if (!isExcel) {
|
||||||
|
this.$message.error('只能上传Excel文件!');
|
||||||
|
}
|
||||||
|
if (!isLt10M) {
|
||||||
|
this.$message.error('Excel文件大小不能超过10MB!');
|
||||||
|
}
|
||||||
|
return isExcel && isLt10M;
|
||||||
|
},
|
||||||
|
// ZIP文件上传前的验证
|
||||||
|
beforeZipUpload(file) {
|
||||||
|
const isZip = file.type === 'application/zip' ||
|
||||||
|
file.type === 'application/x-zip-compressed';
|
||||||
|
const isLt50M = file.size / 1024 / 1024 < 50;
|
||||||
|
|
||||||
|
if (!isZip) {
|
||||||
|
this.$message.error('只能上传ZIP文件!');
|
||||||
|
}
|
||||||
|
if (!isLt50M) {
|
||||||
|
this.$message.error('ZIP文件大小不能超过50MB!');
|
||||||
|
}
|
||||||
|
return isZip && isLt50M;
|
||||||
|
},
|
||||||
|
// 第一个Excel上传成功回调
|
||||||
|
handleExcelSuccess1(response, file, fileList) {
|
||||||
|
this.excelFileList1 = fileList;
|
||||||
|
this.excelProgress1 = 100;
|
||||||
|
this.excelStatus1 = 'success';
|
||||||
|
this.$message.success('第一个Excel文件上传成功!');
|
||||||
|
},
|
||||||
|
// 第二个Excel上传成功回调
|
||||||
|
handleExcelSuccess2(response, file, fileList) {
|
||||||
|
this.excelFileList2 = fileList;
|
||||||
|
this.excelProgress2 = 100;
|
||||||
|
this.excelStatus2 = 'success';
|
||||||
|
this.$message.success('第二个Excel文件上传成功!');
|
||||||
|
},
|
||||||
|
// ZIP上传成功回调
|
||||||
|
handleZipSuccess(response, file, fileList) {
|
||||||
|
this.zipFileList = fileList;
|
||||||
|
this.zipProgress = 100;
|
||||||
|
this.zipStatus = 'success';
|
||||||
|
this.$message.success('ZIP文件上传成功!');
|
||||||
|
},
|
||||||
|
// 上传失败回调
|
||||||
|
handleError(err, file,) {
|
||||||
|
this.$message.error('文件上传失败!');
|
||||||
|
if (file.raw.type.includes('excel') || file.name.includes('.xls')) {
|
||||||
|
this.excelProgress1 = 0;
|
||||||
|
this.excelProgress2 = 0;
|
||||||
|
} else {
|
||||||
|
this.zipProgress = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.page_container {
|
||||||
|
height: calc(100% - 70px);
|
||||||
|
}
|
||||||
|
.upload-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
.upload-card {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 300px;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
padding: 25px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
.upload-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
.upload-icon {
|
||||||
|
font-size: 48px;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.upload-title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.upload-desc {
|
||||||
|
color: #909399;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.upload-btn {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.file-list {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.file-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 0;
|
||||||
|
border-bottom: 1px solid #ebeef5;
|
||||||
|
}
|
||||||
|
.file-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.file-name {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.file-status {
|
||||||
|
color: #67C23A;
|
||||||
|
}
|
||||||
|
.file-status.error {
|
||||||
|
color: #F56C6C;
|
||||||
|
}
|
||||||
|
.upload-progress {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 40px;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.upload-container {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.upload-card {
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -110,7 +110,7 @@ export default {
|
|||||||
let res = await authlogin(this.username, this.encryptData(this.password))
|
let res = await authlogin(this.username, this.encryptData(this.password))
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
this.setToken(res.data.token)
|
this.setToken(res.data.token)
|
||||||
this.$router.push('/hub?user=' + res.data.user.userName)
|
this.$router.push('/hub/upload')
|
||||||
} else {
|
} else {
|
||||||
this.Dialog(res.desc)
|
this.Dialog(res.desc)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ const Building = r => require.ensure([], () => r(require('../pages/modules/build
|
|||||||
const Map = r => require.ensure([], () => r(require('../pages/modules/map/index.vue')), 'Map')
|
const Map = r => require.ensure([], () => r(require('../pages/modules/map/index.vue')), 'Map')
|
||||||
const Relocation = r => require.ensure([], () => r(require('../pages/modules/relocation.vue')), 'Relocation')
|
const Relocation = r => require.ensure([], () => r(require('../pages/modules/relocation.vue')), 'Relocation')
|
||||||
const Login = r => require.ensure([], () => r(require('../pages/modules/login/index.vue')), 'login')
|
const Login = r => require.ensure([], () => r(require('../pages/modules/login/index.vue')), 'login')
|
||||||
const Hub = r => require.ensure([], () => r(require('../pages/modules/hub/index.vue')), 'Hub')
|
const IndexHub = r => require.ensure([], () => r(require('../pages/modules/hub/index.vue')), 'IndexHub')
|
||||||
|
const Upload = r => require.ensure([], () => r(require('../pages/modules/hub/upload.vue')), 'Upload')
|
||||||
|
const Logs = r => require.ensure([], () => r(require('../pages/modules/hub/logs.vue')), 'Logs')
|
||||||
Vue.use(VueRouter)
|
Vue.use(VueRouter)
|
||||||
|
|
||||||
const router = new VueRouter({
|
const router = new VueRouter({
|
||||||
@@ -47,7 +49,16 @@ const router = new VueRouter({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/hub',
|
path: '/hub',
|
||||||
component: Hub
|
component: IndexHub,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'upload',
|
||||||
|
component: Upload
|
||||||
|
}, {
|
||||||
|
path: 'logs',
|
||||||
|
component: Logs
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -384,3 +384,11 @@
|
|||||||
margin 0 !important
|
margin 0 !important
|
||||||
height .44rem
|
height .44rem
|
||||||
line-height .44rem
|
line-height .44rem
|
||||||
|
|
||||||
|
.el-loading-mask {
|
||||||
|
background-color: rgba(0, 11, 41, .4)
|
||||||
|
}
|
||||||
|
// table
|
||||||
|
.el-table__body tr.hover-row>td.el-table__cell {
|
||||||
|
background-color: rgba(63,106,202,0.3)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user