数据存储
This commit is contained in:
parent
166c3e5d8c
commit
416a9102b0
|
|
@ -0,0 +1,47 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 系统运维->网络配置->查询TCP/IP配置
|
||||||
|
export function getTCPIPConfigAPI(params) {
|
||||||
|
return request({
|
||||||
|
url: '/smartCar/data/device/getTCPIPConfig',
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 系统运维->网络配置->更新TCP/IP配置
|
||||||
|
export function updateTCPIPConfigAPI(data) {
|
||||||
|
return request({
|
||||||
|
url: '/smartCar/data/device/updateTCPIPConfig',
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 系统运维->网络配置->查询网络状态
|
||||||
|
export function getNetworkStatusAPI(params) {
|
||||||
|
return request({
|
||||||
|
url: '/smartCar/data/device/getNetworkStatus',
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 系统运维->网络配置->查询网络配置
|
||||||
|
export function getNetworkConfigAPI(params) {
|
||||||
|
return request({
|
||||||
|
url: '/smartCar/data/device/getNetworkConfig',
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 系统运维->网络配置->更新网络配置
|
||||||
|
export function updateNetworkConfigAPI(data) {
|
||||||
|
return request({
|
||||||
|
url: '/smartCar/data/device/updateNetworkConfig',
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 系统运维->TF存储->查询存储信息
|
||||||
|
export function getStorageInfoAPI(params) {
|
||||||
|
return request({
|
||||||
|
url: '/smartCar/data/device/getStorageInfo',
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 系统运维->TF存储->格式化存储
|
||||||
|
export function formatStorageAPI(data) {
|
||||||
|
return request({
|
||||||
|
url: '/smartCar/data/device/formatStorage',
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -254,20 +254,22 @@ export default {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background: linear-gradient(180deg, #f1f6ff 20%, #e5efff 100%);
|
background: linear-gradient(180deg, #f1f6ff 20%, #e5efff 100%);
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
::v-deep .el-card__body {
|
::v-deep .el-card__body {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-card {
|
.info-card {
|
||||||
margin-bottom: 16px;
|
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
height: calc((100vh - 84px - 16px * 2 - 20px) / 2);
|
||||||
&:last-child {
|
display: flex;
|
||||||
margin-bottom: 0;
|
flex-direction: column;
|
||||||
}
|
flex-shrink: 0;
|
||||||
|
|
||||||
.card-header {
|
.card-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -303,6 +305,9 @@ export default {
|
||||||
|
|
||||||
::v-deep .el-card__body {
|
::v-deep .el-card__body {
|
||||||
padding: 16px 20px;
|
padding: 16px 20px;
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep .el-descriptions {
|
::v-deep .el-descriptions {
|
||||||
|
|
@ -343,6 +348,8 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.operation-info-card {
|
.operation-info-card {
|
||||||
|
margin-top: 15px;
|
||||||
|
|
||||||
::v-deep .el-descriptions {
|
::v-deep .el-descriptions {
|
||||||
.el-descriptions__table {
|
.el-descriptions__table {
|
||||||
.el-descriptions__label {
|
.el-descriptions__label {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,532 @@
|
||||||
|
<template>
|
||||||
|
<!-- 设备运维-网络配置 -->
|
||||||
|
<el-card class="network-container" v-loading="loading">
|
||||||
|
<el-card class="network-card">
|
||||||
|
<div slot="header" class="card-header">
|
||||||
|
<div class="tab-header">
|
||||||
|
<div class="tab-item" :class="{ active: activeTab === 'tcpip' }" @click="activeTab = 'tcpip'">TCP/IP</div>
|
||||||
|
<div class="tab-item" :class="{ active: activeTab === '4g5g' }" @click="activeTab = '4g5g'">4G/5G</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- TCP/IP 标签页 -->
|
||||||
|
<div v-if="activeTab === 'tcpip'" class="tab-content">
|
||||||
|
<el-form :model="tcpipForm" :rules="tcpipRules" ref="tcpipForm" label-width="120px">
|
||||||
|
<el-form-item label="以太网接口">
|
||||||
|
<el-select v-model="tcpipForm.ethernetInterface" style="width: 400px">
|
||||||
|
<el-option label="eth0" value="eth0"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="启用DHCP">
|
||||||
|
<el-checkbox v-model="tcpipForm.enableDHCP" @change="handleDHCPChange"></el-checkbox>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="IP地址" prop="ipAddress" required>
|
||||||
|
<el-input v-model="tcpipForm.ipAddress" placeholder="请输入IP地址" style="width: 400px" :disabled="tcpipForm.enableDHCP"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="子网掩码" prop="subnetMask" required>
|
||||||
|
<el-input v-model="tcpipForm.subnetMask" placeholder="请输入子网掩码" style="width: 400px" :disabled="tcpipForm.enableDHCP"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="默认网关">
|
||||||
|
<el-input v-model="tcpipForm.defaultGateway" placeholder="请输入默认网关" style="width: 400px" :disabled="tcpipForm.enableDHCP"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="DNS">
|
||||||
|
<div class="dns-list">
|
||||||
|
<div v-for="(dns, index) in tcpipForm.dnsList" :key="index" class="dns-item">
|
||||||
|
<el-input v-model="dns.value" placeholder="请输入DNS" style="width: 350px" :disabled="tcpipForm.enableDHCP"></el-input>
|
||||||
|
<el-button type="text" icon="el-icon-plus" class="dns-btn add-btn" @click="addDNS" :disabled="tcpipForm.enableDHCP"></el-button>
|
||||||
|
<el-button type="text" icon="el-icon-minus" class="dns-btn remove-btn" @click="removeDNS(index)" :disabled="tcpipForm.enableDHCP || tcpipForm.dnsList.length === 1"></el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="MAC地址">
|
||||||
|
<el-input v-model="tcpipForm.macAddress" style="width: 400px" disabled></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button class="save-btn" @click="handleSaveTCPIP">保存</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 4G/5G 标签页 -->
|
||||||
|
<div v-if="activeTab === '4g5g'" class="tab-content">
|
||||||
|
<div class="network-layout">
|
||||||
|
<!-- 左侧:状态参数 -->
|
||||||
|
<div class="status-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<i class="el-icon-connection section-icon"></i>
|
||||||
|
<span class="section-title">状态参数</span>
|
||||||
|
</div>
|
||||||
|
<div class="status-list">
|
||||||
|
<div class="status-item">
|
||||||
|
<span class="status-label">模块状态:</span>
|
||||||
|
<span class="status-value">{{ statusInfo.moduleStatus }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="status-item">
|
||||||
|
<span class="status-label">信号强度:</span>
|
||||||
|
<span class="status-value">{{ statusInfo.signalStrength }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="status-item">
|
||||||
|
<span class="status-label">拨号获取的IP地址:</span>
|
||||||
|
<span class="status-value">{{ statusInfo.dialupIp }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="status-item">
|
||||||
|
<span class="status-label">当前网络模式:</span>
|
||||||
|
<span class="status-value">{{ statusInfo.networkMode }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="status-item">
|
||||||
|
<span class="status-label">当前网络运营商:</span>
|
||||||
|
<span class="status-value">{{ statusInfo.operator }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="status-item">
|
||||||
|
<span class="status-label">模块IMEI:</span>
|
||||||
|
<span class="status-value">{{ statusInfo.imei }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧:配置参数 -->
|
||||||
|
<div class="config-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<i class="el-icon-setting section-icon"></i>
|
||||||
|
<span class="section-title">配置参数</span>
|
||||||
|
</div>
|
||||||
|
<el-form :model="networkForm" :rules="networkRules" ref="networkForm" label-width="120px">
|
||||||
|
<el-form-item label="4G/5G">
|
||||||
|
<el-input v-model="networkForm.networkType" style="width: 400px" disabled></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="启用拨号">
|
||||||
|
<el-checkbox v-model="networkForm.enableDialup"></el-checkbox>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="APN" prop="apn">
|
||||||
|
<el-input v-model="networkForm.apn" placeholder="请输入APN" style="width: 400px"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="接入号" prop="accessNumber">
|
||||||
|
<el-input v-model="networkForm.accessNumber" placeholder="请输入接入号" style="width: 400px"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="拨号用户名" prop="dialupUsername">
|
||||||
|
<el-input v-model="networkForm.dialupUsername" placeholder="请输入拨号用户名" style="width: 400px"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="拨号密码" prop="dialupPassword">
|
||||||
|
<el-input v-model="networkForm.dialupPassword" type="password" placeholder="请输入拨号密码" style="width: 400px" show-password></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button class="save-btn" @click="handleSaveNetwork">保存</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getTCPIPConfigAPI, updateTCPIPConfigAPI, getNetworkStatusAPI, getNetworkConfigAPI, updateNetworkConfigAPI } from '@/api/devops/network'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Network',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
activeTab: 'tcpip',
|
||||||
|
tcpipForm: {
|
||||||
|
ethernetInterface: 'eth0',
|
||||||
|
enableDHCP: false,
|
||||||
|
ipAddress: '',
|
||||||
|
subnetMask: '',
|
||||||
|
defaultGateway: '',
|
||||||
|
dnsList: [{ value: '' }],
|
||||||
|
macAddress: '18-F2-2C-32-DF-B6'
|
||||||
|
},
|
||||||
|
tcpipRules: {
|
||||||
|
ipAddress: [
|
||||||
|
{ required: true, message: 'IP地址不能为空', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
subnetMask: [
|
||||||
|
{ required: true, message: '子网掩码不能为空', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
statusInfo: {
|
||||||
|
moduleStatus: '拨号成功',
|
||||||
|
signalStrength: '96',
|
||||||
|
dialupIp: '10.52.34.103',
|
||||||
|
networkMode: 'LTE TDD',
|
||||||
|
operator: '中国移动',
|
||||||
|
imei: '862819047641246'
|
||||||
|
},
|
||||||
|
networkForm: {
|
||||||
|
networkType: '4G',
|
||||||
|
enableDialup: false,
|
||||||
|
apn: '',
|
||||||
|
accessNumber: '',
|
||||||
|
dialupUsername: '',
|
||||||
|
dialupPassword: ''
|
||||||
|
},
|
||||||
|
networkRules: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getTCPIPConfig()
|
||||||
|
this.getNetworkStatus()
|
||||||
|
this.getNetworkConfig()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/** 获取TCP/IP配置 */
|
||||||
|
async getTCPIPConfig() {
|
||||||
|
try {
|
||||||
|
this.loading = true
|
||||||
|
const res = await getTCPIPConfigAPI()
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
const data = res.data
|
||||||
|
this.tcpipForm = {
|
||||||
|
ethernetInterface: data.ethernetInterface || 'eth0',
|
||||||
|
enableDHCP: data.enableDHCP || false,
|
||||||
|
ipAddress: data.ipAddress || '',
|
||||||
|
subnetMask: data.subnetMask || '',
|
||||||
|
defaultGateway: data.defaultGateway || '',
|
||||||
|
dnsList: data.dnsList && data.dnsList.length > 0 ? data.dnsList.map(dns => ({ value: dns })) : [{ value: '' }],
|
||||||
|
macAddress: data.macAddress || '18-F2-2C-32-DF-B6'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取TCP/IP配置失败:', error)
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 获取网络状态 */
|
||||||
|
async getNetworkStatus() {
|
||||||
|
try {
|
||||||
|
const res = await getNetworkStatusAPI()
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
this.statusInfo = {
|
||||||
|
moduleStatus: res.data.moduleStatus || '拨号成功',
|
||||||
|
signalStrength: res.data.signalStrength || '96',
|
||||||
|
dialupIp: res.data.dialupIp || '10.52.34.103',
|
||||||
|
networkMode: res.data.networkMode || 'LTE TDD',
|
||||||
|
operator: res.data.operator || '中国移动',
|
||||||
|
imei: res.data.imei || '862819047641246'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取网络状态失败:', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 获取网络配置 */
|
||||||
|
async getNetworkConfig() {
|
||||||
|
try {
|
||||||
|
const res = await getNetworkConfigAPI()
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
const data = res.data
|
||||||
|
this.networkForm = {
|
||||||
|
networkType: data.networkType || '4G',
|
||||||
|
enableDialup: data.enableDialup || false,
|
||||||
|
apn: data.apn || '',
|
||||||
|
accessNumber: data.accessNumber || '',
|
||||||
|
dialupUsername: data.dialupUsername || '',
|
||||||
|
dialupPassword: data.dialupPassword || ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取网络配置失败:', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** DHCP切换 */
|
||||||
|
handleDHCPChange() {
|
||||||
|
// DHCP启用时,清空IP相关字段
|
||||||
|
if (this.tcpipForm.enableDHCP) {
|
||||||
|
this.tcpipForm.ipAddress = ''
|
||||||
|
this.tcpipForm.subnetMask = ''
|
||||||
|
this.tcpipForm.defaultGateway = ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 添加DNS */
|
||||||
|
addDNS() {
|
||||||
|
this.tcpipForm.dnsList.push({ value: '' })
|
||||||
|
},
|
||||||
|
/** 删除DNS */
|
||||||
|
removeDNS(index) {
|
||||||
|
if (this.tcpipForm.dnsList.length > 1) {
|
||||||
|
this.tcpipForm.dnsList.splice(index, 1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 保存TCP/IP配置 */
|
||||||
|
async handleSaveTCPIP() {
|
||||||
|
try {
|
||||||
|
await this.$refs.tcpipForm.validate()
|
||||||
|
|
||||||
|
if (!this.tcpipForm.enableDHCP) {
|
||||||
|
if (!this.tcpipForm.ipAddress) {
|
||||||
|
this.$message.error('IP地址不能为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!this.tcpipForm.subnetMask) {
|
||||||
|
this.$message.error('子网掩码不能为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = true
|
||||||
|
const params = {
|
||||||
|
ethernetInterface: this.tcpipForm.ethernetInterface,
|
||||||
|
enableDHCP: this.tcpipForm.enableDHCP,
|
||||||
|
ipAddress: this.tcpipForm.ipAddress,
|
||||||
|
subnetMask: this.tcpipForm.subnetMask,
|
||||||
|
defaultGateway: this.tcpipForm.defaultGateway,
|
||||||
|
dnsList: this.tcpipForm.dnsList.map(item => item.value).filter(item => item),
|
||||||
|
macAddress: this.tcpipForm.macAddress
|
||||||
|
}
|
||||||
|
const res = await updateTCPIPConfigAPI(params)
|
||||||
|
if (res.code === 200) {
|
||||||
|
this.$message.success('保存成功')
|
||||||
|
} else {
|
||||||
|
this.$message.error(res.msg || '保存失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error.message !== '数据未填写完整') {
|
||||||
|
console.error('保存TCP/IP配置失败:', error)
|
||||||
|
this.$message.error('保存失败,请稍后重试')
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 保存网络配置 */
|
||||||
|
async handleSaveNetwork() {
|
||||||
|
try {
|
||||||
|
this.loading = true
|
||||||
|
const params = {
|
||||||
|
networkType: this.networkForm.networkType,
|
||||||
|
enableDialup: this.networkForm.enableDialup,
|
||||||
|
apn: this.networkForm.apn,
|
||||||
|
accessNumber: this.networkForm.accessNumber,
|
||||||
|
dialupUsername: this.networkForm.dialupUsername,
|
||||||
|
dialupPassword: this.networkForm.dialupPassword
|
||||||
|
}
|
||||||
|
const res = await updateNetworkConfigAPI(params)
|
||||||
|
if (res.code === 200) {
|
||||||
|
this.$message.success('保存成功')
|
||||||
|
// 保存后重新获取状态
|
||||||
|
this.getNetworkStatus()
|
||||||
|
} else {
|
||||||
|
this.$message.error(res.msg || '保存失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('保存网络配置失败:', error)
|
||||||
|
this.$message.error('保存失败,请稍后重试')
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.network-container {
|
||||||
|
height: calc(100vh - 84px);
|
||||||
|
overflow-y: auto;
|
||||||
|
background: linear-gradient(180deg, #f1f6ff 20%, #e5efff 100%);
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.network-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
height: calc(100vh - 130px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
background: #fff;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 8px 8px 0 0;
|
||||||
|
|
||||||
|
.tab-header {
|
||||||
|
display: flex;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #1f72ea;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: #1f72ea;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 2px;
|
||||||
|
background: #1f72ea;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-card__body {
|
||||||
|
padding: 20px;
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-form-item {
|
||||||
|
margin-bottom: 18px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item__label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dns-list {
|
||||||
|
.dns-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dns-btn {
|
||||||
|
margin-left: 8px;
|
||||||
|
padding: 0;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&.add-btn {
|
||||||
|
background: #1f72ea;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #4a8bff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.remove-btn {
|
||||||
|
background: #f56c6c;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f78989;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.network-layout {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
|
||||||
|
.status-section,
|
||||||
|
.config-section {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
border-bottom: 1px solid #ebeef5;
|
||||||
|
|
||||||
|
.section-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #1f72ea;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-list {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.status-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-bottom: 1px solid #f5f7fa;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
min-width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-value {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #303133;
|
||||||
|
background: #f5f7fa;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-btn {
|
||||||
|
width: 98px;
|
||||||
|
height: 36px;
|
||||||
|
background: #1f72ea;
|
||||||
|
box-shadow: 0px 4px 8px 0px rgba(51, 135, 255, 0.5);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #4a8bff;
|
||||||
|
box-shadow: 0px 6px 12px 0px rgba(51, 135, 255, 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- 设备运维-时间设置 -->
|
<!-- 设备运维-时间设置 -->
|
||||||
<el-card class="time-setting-container" v-loading="loading">
|
<el-card class="time-setting-container" v-loading="loading">
|
||||||
<div class="current-time">
|
|
||||||
<span class="time-label">设备当前时间:</span>
|
|
||||||
<span class="time-value">{{ currentTime }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-card class="setting-card">
|
<el-card class="setting-card">
|
||||||
<div slot="header" class="card-header">
|
<div slot="header" class="card-header">
|
||||||
<span class="card-title">时间设置</span>
|
<span class="card-title">时间设置</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="current-time">
|
||||||
|
<span class="time-label">设备当前时间:</span>
|
||||||
|
<span class="time-value">{{ currentTime }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<el-form :model="form" :rules="rules" ref="timeSettingForm" label-width="120px">
|
<el-form :model="form" :rules="rules" ref="timeSettingForm" label-width="120px">
|
||||||
<el-form-item label="校时模式" prop="calibrationMode">
|
<el-form-item label="校时模式" prop="calibrationMode">
|
||||||
<el-radio-group v-model="form.calibrationMode" @change="handleModeChange">
|
<el-radio-group v-model="form.calibrationMode" @change="handleModeChange">
|
||||||
|
|
@ -179,34 +179,14 @@ export default {
|
||||||
height: calc(100vh - 84px);
|
height: calc(100vh - 84px);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background: linear-gradient(180deg, #f1f6ff 20%, #e5efff 100%);
|
background: linear-gradient(180deg, #f1f6ff 20%, #e5efff 100%);
|
||||||
|
padding: 0px;
|
||||||
.current-time {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
padding: 8px 14px;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 4px;
|
|
||||||
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
.time-label {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #606266;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time-value {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #303133;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-card {
|
.setting-card {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||||
height: calc(100vh - 180px);
|
height: calc(100vh - 130px);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
|
@ -225,9 +205,32 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.current-time {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 8px 14px;
|
||||||
|
// background: #f5f7fa;
|
||||||
|
border-radius: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.time-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-value {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #303133;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
::v-deep .el-card__body {
|
::v-deep .el-card__body {
|
||||||
padding: 14px 18px;
|
padding: 14px 18px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,346 @@
|
||||||
|
<template>
|
||||||
|
<!-- 设备运维-TF存储 -->
|
||||||
|
<el-card class="storage-container" v-loading="loading">
|
||||||
|
<!-- 存储信息卡片 -->
|
||||||
|
<el-card class="storage-card">
|
||||||
|
<div slot="header" class="card-header">
|
||||||
|
<div class="tab-header">
|
||||||
|
<div class="tab-item active">TF存储</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="storage-info">
|
||||||
|
<div class="storage-text">
|
||||||
|
<span class="available">{{ availableSpace }}GB可用, 共{{ totalSpace }}GB</span>
|
||||||
|
</div>
|
||||||
|
<el-button class="format-btn" @click="handleFormat">格式化</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 进度条 -->
|
||||||
|
<div class="progress-container">
|
||||||
|
<div class="progress-bar">
|
||||||
|
<el-tooltip
|
||||||
|
v-if="usedPercentage > 0"
|
||||||
|
:content="`已用 容量:${usedSpace.toFixed(2)}GB(${usedPercentage.toFixed(2)}%)`"
|
||||||
|
placement="top"
|
||||||
|
effect="dark">
|
||||||
|
<div class="progress-used" :style="{ width: usedPercentage + '%' }"></div>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip
|
||||||
|
v-if="freePercentage > 0"
|
||||||
|
:content="`空闲 容量:${availableSpace.toFixed(2)}GB(${freePercentage.toFixed(2)}%)`"
|
||||||
|
placement="top"
|
||||||
|
effect="dark">
|
||||||
|
<div class="progress-free" :style="{ width: freePercentage + '%' }"></div>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 图例 -->
|
||||||
|
<div class="legend">
|
||||||
|
<div class="legend-item">
|
||||||
|
<span class="legend-color used-color"></span>
|
||||||
|
<span class="legend-label">已用</span>
|
||||||
|
</div>
|
||||||
|
<div class="legend-item">
|
||||||
|
<span class="legend-color free-color"></span>
|
||||||
|
<span class="legend-label">空闲</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 格式化确认对话框 -->
|
||||||
|
<el-dialog
|
||||||
|
title="格式化"
|
||||||
|
:visible.sync="formatDialogVisible"
|
||||||
|
width="400px"
|
||||||
|
:close-on-click-modal="false">
|
||||||
|
<div class="dialog-content">
|
||||||
|
<i class="el-icon-warning warning-icon"></i>
|
||||||
|
<span class="dialog-message">确定格式化当前固态硬盘吗?</span>
|
||||||
|
</div>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="formatDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="confirmFormat">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getStorageInfoAPI, formatStorageAPI } from '@/api/devops/storage'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Storage',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
totalSpace: 128,
|
||||||
|
usedSpace: 100,
|
||||||
|
availableSpace: 28,
|
||||||
|
formatDialogVisible: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
usedPercentage() {
|
||||||
|
if (this.totalSpace === 0) return 0
|
||||||
|
return Math.round((this.usedSpace / this.totalSpace) * 100)
|
||||||
|
},
|
||||||
|
freePercentage() {
|
||||||
|
return 100 - this.usedPercentage
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getStorageInfo()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/** 获取存储信息 */
|
||||||
|
async getStorageInfo() {
|
||||||
|
try {
|
||||||
|
this.loading = true
|
||||||
|
const res = await getStorageInfoAPI()
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
const data = res.data
|
||||||
|
this.totalSpace = data.totalSpace || 128
|
||||||
|
this.usedSpace = data.usedSpace || 100
|
||||||
|
this.availableSpace = data.availableSpace || 28
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取存储信息失败:', error)
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 格式化操作 */
|
||||||
|
handleFormat() {
|
||||||
|
this.formatDialogVisible = true
|
||||||
|
},
|
||||||
|
/** 确认格式化 */
|
||||||
|
async confirmFormat() {
|
||||||
|
try {
|
||||||
|
this.loading = true
|
||||||
|
const res = await formatStorageAPI()
|
||||||
|
if (res.code === 200) {
|
||||||
|
this.$message.success('格式化成功')
|
||||||
|
this.formatDialogVisible = false
|
||||||
|
// 格式化后重新获取存储信息
|
||||||
|
this.getStorageInfo()
|
||||||
|
} else {
|
||||||
|
this.$message.error(res.msg || '格式化失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('格式化失败:', error)
|
||||||
|
this.$message.error('格式化失败,请稍后重试')
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.storage-container {
|
||||||
|
height: calc(100vh - 84px);
|
||||||
|
overflow-y: auto;
|
||||||
|
background: linear-gradient(180deg, #f1f6ff 20%, #e5efff 100%);
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.storage-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
height: calc(100vh - 130px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
background: #fff;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 8px 8px 0 0;
|
||||||
|
|
||||||
|
.tab-header {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: #1f72ea;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 2px;
|
||||||
|
background: #1f72ea;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-card__body {
|
||||||
|
padding: 20px;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.storage-info {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.storage-text {
|
||||||
|
.available {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #303133;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.format-btn {
|
||||||
|
background: #f5f7fa;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
color: #606266;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #e4e7ed;
|
||||||
|
border-color: #c0c4cc;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-container {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
width: 100%;
|
||||||
|
height: 24px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.progress-used {
|
||||||
|
background: #ff6b9d;
|
||||||
|
height: 100%;
|
||||||
|
transition: width 0.3s;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-free {
|
||||||
|
background: #1f72ea;
|
||||||
|
height: 100%;
|
||||||
|
transition: width 0.3s;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.legend-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.legend-color {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 2px;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
&.used-color {
|
||||||
|
background: #ff6b9d;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.free-color {
|
||||||
|
background: #1f72ea;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-dialog {
|
||||||
|
.el-dialog__header {
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid #ebeef5;
|
||||||
|
|
||||||
|
.el-dialog__title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dialog__body {
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.dialog-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.warning-icon {
|
||||||
|
font-size: 24px;
|
||||||
|
color: #f56c6c;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-message {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
padding: 12px 20px;
|
||||||
|
border-top: 1px solid #ebeef5;
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
Loading…
Reference in New Issue