修改反馈问题

This commit is contained in:
jingna 2026-05-19 10:46:30 +08:00
parent 239a2401f9
commit a7cc0e19c9
21 changed files with 227 additions and 790 deletions

View File

@ -114,3 +114,7 @@ export async function saveSystemConfig(payload: SystemConfigPayload): Promise<Ap
const response = await http.post<ApiResponse<{ send_status: string }>>('/config/system', payload)
return response.data
}
export async function saveDevicePassword(payload: { password: string }): Promise<ApiResponse<{ send_status: string }>> {
const response = await http.post<ApiResponse<{ send_status: string }>>('/config/device/password', payload)
return response.data
}

View File

@ -1,33 +0,0 @@
<script setup lang="ts">
defineProps<{ store: any; actions: any }>()
</script>
<template>
<section class="panel">
<div class="section-header">
<h2>报警历史</h2>
<div class="actions">
<button class="primary" :disabled="store.alarmLoading" @click="actions.refreshAlarms()">
{{ store.alarmLoading ? '刷新中...' : '刷新列表' }}
</button>
</div>
</div>
<table class="table">
<thead><tr><th>类型</th><th>时间</th><th>编号</th><th>内容</th><th>级别</th></tr></thead>
<tbody>
<tr v-for="alarm in store.alarms" :key="`${alarm.time}-${alarm.no}-${alarm.content}`">
<td>{{ alarm.alarm_type }}</td>
<td>{{ alarm.time }}</td>
<td>{{ alarm.no }}</td>
<td>{{ alarm.content }}</td>
<td>{{ alarm.level }}</td>
</tr>
</tbody>
</table>
<div class="actions">
<button class="primary" :disabled="store.alarmLoading || store.alarmPage <= 1" @click="actions.prevAlarmPage()">上一页</button>
<button class="primary" :disabled="store.alarmLoading || !store.alarmHasNext" @click="actions.nextAlarmPage()">下一页</button>
</div>
<p class="hint">当前页{{ store.alarmPage }}最后刷新{{ store.alarmUpdatedAt || '--' }}</p>
</section>
</template>

View File

@ -1,107 +0,0 @@
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { fetchAiAlarmSetting, fetchLineAlarmSetting, saveAiAlarmSetting, saveLineAlarmSetting } from '../api/platform'
import type { AiAlarmSettingItem, LineAlarmSettingPayload } from '../types/platform'
import { ensureSaveAuthorized } from '../utils/saveGuard'
defineProps<{ store: any; actions: any }>()
const lineAlarm = reactive<LineAlarmSettingPayload>({
line_no: 1,
over_limit_alarm: [
{ category: '电压', limit: 180, delay: 180, output_node: '开出1', enabled: true },
{ category: '电流', limit: 180, delay: 180, output_node: '开出1', enabled: true },
{ category: '差流', limit: 180, delay: 180, output_node: '开出1', enabled: false },
{ category: '频率', limit: 180, delay: 180, output_node: '开出1', enabled: false },
],
fault_alarm: [{ category: 'PT断线', delay: 180, output_node: '开出1', enabled: true }],
})
const aiAlarm = reactive<AiAlarmSettingItem[]>([
{
channel_no: 1,
singal_type: '4-20mA',
limit_low: 0,
limit_high: 20,
delay: 180,
output_node: '开出1',
enabled: true,
},
])
const result = ref('未保存')
const saving = ref(false)
onMounted(async () => {
try {
const [lineData, aiData] = await Promise.all([fetchLineAlarmSetting(), fetchAiAlarmSetting()])
lineAlarm.line_no = lineData.line_no
lineData.over_limit_alarm.forEach((item, index) => {
if (lineAlarm.over_limit_alarm[index]) {
Object.assign(lineAlarm.over_limit_alarm[index], item)
} else {
lineAlarm.over_limit_alarm.push(item)
}
})
lineData.fault_alarm.forEach((item, index) => {
if (lineAlarm.fault_alarm[index]) {
Object.assign(lineAlarm.fault_alarm[index], item)
} else {
lineAlarm.fault_alarm.push(item)
}
})
aiData.forEach((item, index) => {
if (aiAlarm[index]) {
Object.assign(aiAlarm[index], item)
} else {
aiAlarm.push(item)
}
})
result.value = '已加载当前报警设置'
} catch (error) {
result.value = error instanceof Error ? error.message : '加载报警设置失败'
}
})
async function save() {
saving.value = true
try {
const guard = await ensureSaveAuthorized()
if (!guard.ok) {
result.value = guard.message
return
}
const [lineResponse, aiResponse] = await Promise.all([saveLineAlarmSetting(lineAlarm), saveAiAlarmSetting(aiAlarm)])
result.value = `${lineResponse.msg} / ${aiResponse.msg}`
} catch (error) {
result.value = error instanceof Error ? error.message : '保存报警设置失败'
} finally {
saving.value = false
}
}
</script>
<template>
<section class="panel">
<h2>报警设置</h2>
<div class="form-grid">
<label>线路号<input v-model.number="lineAlarm.line_no" type="number" min="1" max="4" /></label>
<label>电压越限<input v-model.number="lineAlarm.over_limit_alarm[0].limit" type="number" /></label>
<label>电流越限<input v-model.number="lineAlarm.over_limit_alarm[1].limit" type="number" /></label>
<label>差流越限<input v-model.number="lineAlarm.over_limit_alarm[2].limit" type="number" /></label>
<label>频率越限<input v-model.number="lineAlarm.over_limit_alarm[3].limit" type="number" /></label>
<label>动作延时<input v-model.number="lineAlarm.over_limit_alarm[0].delay" type="number" /></label>
<label>输出节点<select v-model="lineAlarm.over_limit_alarm[0].output_node"><option>开出1</option><option>开出2</option></select></label>
<label>PT断线延时<input v-model.number="lineAlarm.fault_alarm[0].delay" type="number" /></label>
<label>AI通道号<input v-model.number="aiAlarm[0].channel_no" type="number" min="1" max="12" /></label>
<label>AI信号类型<input v-model="aiAlarm[0].singal_type" /></label>
<label>AI下限<input v-model.number="aiAlarm[0].limit_low" type="number" /></label>
<label>AI上限<input v-model.number="aiAlarm[0].limit_high" type="number" /></label>
</div>
<div class="actions">
<button class="primary" :disabled="saving" @click="save">{{ saving ? '保存中...' : '保存报警设置' }}</button>
</div>
<p>保存结果{{ result }}</p>
</section>
</template>

View File

@ -1,90 +0,0 @@
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { fetchChannelConfig, saveChannelConfig } from '../api/platform'
import type { ChannelConfigPayload } from '../types/platform'
import { ensureSaveAuthorized } from '../utils/saveGuard'
defineProps<{ store: any; actions: any }>()
const form = reactive<ChannelConfigPayload>({
ai_channel: [{ ch: 1, singal_type: '4-20mA', line_no: 1, type: 'UA', limit_low: 0, limit_high: 20 }],
ao_channel: [{ ch: 1, singal_type: '1-5v', line_no: 2, type: 'UA', limit_low: 0, limit_high: 20 }],
})
const result = ref('未保存')
const saving = ref(false)
onMounted(async () => {
try {
const data = await fetchChannelConfig()
data.ai_channel.forEach((item, index) => {
if (form.ai_channel[index]) {
Object.assign(form.ai_channel[index], item)
} else {
form.ai_channel.push(item)
}
})
data.ao_channel.forEach((item, index) => {
if (form.ao_channel[index]) {
Object.assign(form.ao_channel[index], item)
} else {
form.ao_channel.push(item)
}
})
result.value = '已加载当前通道配置'
} catch (error) {
result.value = error instanceof Error ? error.message : '加载通道配置失败'
}
})
async function save() {
saving.value = true
try {
const guard = await ensureSaveAuthorized()
if (!guard.ok) {
result.value = guard.message
return
}
const response = await saveChannelConfig(form)
result.value = `${response.msg}${response.data.send_status}`
} catch (error) {
result.value = error instanceof Error ? error.message : '保存通道配置失败'
} finally {
saving.value = false
}
}
</script>
<template>
<section class="panel">
<h2>通道配置</h2>
<table class="table">
<thead>
<tr><th>通道</th><th>信号类型</th><th>线路</th><th>测量类型</th><th>下限</th><th>上限</th></tr>
</thead>
<tbody>
<tr>
<td>AI{{ form.ai_channel[0].ch }}</td>
<td><input v-model="form.ai_channel[0].singal_type" /></td>
<td><input v-model.number="form.ai_channel[0].line_no" type="number" min="1" max="4" /></td>
<td><input v-model="form.ai_channel[0].type" /></td>
<td><input v-model.number="form.ai_channel[0].limit_low" type="number" /></td>
<td><input v-model.number="form.ai_channel[0].limit_high" type="number" /></td>
</tr>
<tr>
<td>AO{{ form.ao_channel[0].ch }}</td>
<td><input v-model="form.ao_channel[0].singal_type" /></td>
<td><input v-model.number="form.ao_channel[0].line_no" type="number" min="1" max="4" /></td>
<td><input v-model="form.ao_channel[0].type" /></td>
<td><input v-model.number="form.ao_channel[0].limit_low" type="number" /></td>
<td><input v-model.number="form.ao_channel[0].limit_high" type="number" /></td>
</tr>
</tbody>
</table>
<div class="actions">
<button class="primary" :disabled="saving" @click="save">{{ saving ? '保存中...' : '保存通道配置' }}</button>
</div>
<p>保存结果{{ result }}</p>
</section>
</template>

View File

@ -1,28 +0,0 @@
<script setup lang="ts">
import { ref } from "vue"
import { controlSwitch } from "../api/platform"
defineProps<{ store: any; actions: any }>()
const channel = ref(1)
const result = ref("等待下发")
async function sendControl(action: number) {
const response = await controlSwitch(channel.value, action)
result.value = response.data.control_status
}
</script>
<template>
<section class="panel">
<h2>控制指令</h2>
<div class="form-grid">
<label>开关通道<input v-model="channel" type="number" min="1" max="12" /></label>
</div>
<div class="actions">
<button class="primary" @click="sendControl(1)">合闸</button>
<button class="primary" @click="sendControl(0)">分闸</button>
</div>
<p>执行结果{{ result }}</p>
</section>
</template>

View File

@ -1,104 +0,0 @@
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { fetchDeviceConfig, saveDeviceConfig } from '../api/platform'
import type { DeviceConfigPayload } from '../types/platform'
import { ensureSaveAuthorized } from '../utils/saveGuard'
defineProps<{ store: any; actions: any }>()
const form = reactive<DeviceConfigPayload>({
password: '123456',
hardware_version: {
board_version: 'B001.001.001',
display_version: 'S001.001.001',
other_version: 'Y001.001.001',
},
software_version: {
display_program: '001.001.001',
communication_program: '001.001.001',
measurement_program: '001.001.001',
},
net: [
{ nic: '网卡一', ip: '192.168.1.10', mask: '255.255.255.0', gateway: '192.168.1.1', protocol: 'Modbus TCP' },
{ nic: '网卡二', ip: '192.168.1.56', mask: '255.255.255.255', gateway: '192.168.1.56', protocol: 'Modbus TCP' },
],
uart: [
{ port: 'COM1', baud: 9600, parity: 'NONE', data_bits: 8, stop_bits: 1, protocol: '' },
{ port: 'COM2', baud: 115200, parity: 'NONE', data_bits: 8, stop_bits: 1, protocol: 'Modbus RTU' },
],
})
const result = ref('未保存')
const saving = ref(false)
onMounted(async () => {
try {
const data = await fetchDeviceConfig()
form.password = data.password
Object.assign(form.hardware_version, data.hardware_version)
Object.assign(form.software_version, data.software_version)
data.net.forEach((item, index) => {
if (form.net[index]) {
Object.assign(form.net[index], item)
} else {
form.net.push(item)
}
})
data.uart.forEach((item, index) => {
if (form.uart[index]) {
Object.assign(form.uart[index], item)
} else {
form.uart.push(item)
}
})
result.value = '已加载当前设备配置'
} catch (error) {
result.value = error instanceof Error ? error.message : '加载设备配置失败'
}
})
async function save() {
saving.value = true
try {
const guard = await ensureSaveAuthorized()
if (!guard.ok) {
result.value = guard.message
return
}
const response = await saveDeviceConfig(form)
result.value = `${response.msg}${response.data.send_status}`
} catch (error) {
result.value = error instanceof Error ? error.message : '保存设备配置失败'
} finally {
saving.value = false
}
}
</script>
<template>
<section class="panel">
<h2>设备基础配置</h2>
<div class="form-grid">
<label>操作密码<input v-model="form.password" type="password" /></label>
<label>板卡版本<input v-model="form.hardware_version.board_version" /></label>
<label>显示版本<input v-model="form.hardware_version.display_version" /></label>
<label>其他版本<input v-model="form.hardware_version.other_version" /></label>
<label>显示程序版本<input v-model="form.software_version.display_program" /></label>
<label>通信程序版本<input v-model="form.software_version.communication_program" /></label>
<label>测量程序版本<input v-model="form.software_version.measurement_program" /></label>
<label>网卡一 IP<input v-model="form.net[0].ip" /></label>
<label>网卡一 掩码<input v-model="form.net[0].mask" /></label>
<label>网卡一 网关<input v-model="form.net[0].gateway" /></label>
<label>网卡二 IP<input v-model="form.net[1].ip" /></label>
<label>网卡二 协议<input v-model="form.net[1].protocol" /></label>
<label>串口一波特率<input v-model.number="form.uart[0].baud" type="number" /></label>
<label>串口二波特率<input v-model.number="form.uart[1].baud" type="number" /></label>
<label>串口二协议<input v-model="form.uart[1].protocol" /></label>
</div>
<div class="actions">
<button class="primary" :disabled="saving" @click="save">{{ saving ? '保存中...' : '保存设备配置' }}</button>
</div>
<p>保存结果{{ result }}</p>
</section>
</template>

View File

@ -1,35 +0,0 @@
<script setup lang="ts">
defineProps<{ store: any; actions: any }>()
</script>
<template>
<section class="panel">
<h2>实时数据</h2>
<div class="metrics" style="margin-bottom: 12px">
<div class="metric">
<div class="label">数据源</div>
<div class="value">{{ store.realtimeSource }}</div>
</div>
<div class="metric">
<div class="label">连接状态</div>
<div class="value">{{ store.realtimeConnected ? 'WebSocket 已连接' : 'HTTP 轮询回退' }}</div>
</div>
<div class="metric">
<div class="label">最后更新时间</div>
<div class="value">{{ store.realtimeUpdatedAt || '--' }}</div>
</div>
</div>
<div v-if="store.realtime" class="grid">
<div v-for="line in store.realtime.line_list" :key="line.line_no" class="panel">
<h3>线路 {{ line.line_no }}</h3>
<div class="metrics">
<div class="metric"><div class="label">一次电压 Ua</div><div class="value">{{ line.pri_val.Ua }}</div></div>
<div class="metric"><div class="label">一次电流 Ia</div><div class="value">{{ line.pri_val.Ia }}</div></div>
<div class="metric"><div class="label">总有功 Pt</div><div class="value">{{ line.pri_val.Pt }}</div></div>
<div class="metric"><div class="label">频率</div><div class="value">{{ line.pri_val.frq }}</div></div>
</div>
</div>
</div>
<p v-else>正在等待实时数据...</p>
</section>
</template>

View File

@ -1,27 +0,0 @@
<script setup lang="ts">
defineProps<{ store: any; actions: any }>()
</script>
<template>
<section class="panel">
<div class="section-header">
<h2>设备状态</h2>
<div class="actions">
<button class="primary" :disabled="store.statusLoading" @click="actions.refreshStatus()">
{{ store.statusLoading ? '刷新中...' : '刷新状态' }}
</button>
</div>
</div>
<template v-if="store.status">
<div class="metrics">
<div class="metric"><div class="label">自检</div><div class="value">{{ store.status.self_check }}</div></div>
<div class="metric"><div class="label">网口1</div><div class="value">{{ store.status.net1 }}</div></div>
<div class="metric"><div class="label">网口2</div><div class="value">{{ store.status.net2 }}</div></div>
<div class="metric"><div class="label">串口1</div><div class="value">{{ store.status.uart1 }}</div></div>
<div class="metric"><div class="label">串口2</div><div class="value">{{ store.status.uart2 }}</div></div>
</div>
<p class="hint">最后刷新{{ store.statusUpdatedAt || '--' }}</p>
</template>
<p v-else>正在加载状态...</p>
</section>
</template>

View File

@ -1,48 +0,0 @@
<script setup lang="ts">
import { onMounted, reactive, ref } from "vue"
import { fetchSystemConfig, saveSystemConfig } from "../api/platform"
import { ensureSaveAuthorized } from "../utils/saveGuard"
defineProps<{ store: any; actions: any }>()
const form = reactive({
time_sync: "auto",
brightness: 80,
screen_saver: 60,
})
const result = ref("未保存")
onMounted(async () => {
try {
const data = await fetchSystemConfig()
Object.assign(form, data)
result.value = "已加载当前系统设置"
} catch (error) {
result.value = error instanceof Error ? error.message : "加载系统设置失败"
}
})
async function save() {
const guard = await ensureSaveAuthorized()
if (!guard.ok) {
result.value = guard.message
return
}
const response = await saveSystemConfig(form)
result.value = response.msg
}
</script>
<template>
<section class="panel">
<h2>系统设置</h2>
<div class="form-grid">
<label>对时模式<select v-model="form.time_sync"><option value="auto">自动</option><option value="manual">手动</option></select></label>
<label>亮度<input v-model="form.brightness" type="number" min="0" max="100" /></label>
<label>屏保时间<input v-model="form.screen_saver" type="number" min="0" /></label>
</div>
<div class="actions"><button class="primary" @click="save">保存系统设置</button></div>
<p>保存结果{{ result }}</p>
</section>
</template>

View File

@ -20,6 +20,7 @@ const channelList = ref([
const nodeOptions = ref(['开出一', '开出二', '开出三', '开出四', '开出五', '开出六', '开出七', '开出八', '开出九', '开出十', '开出十一', '开出十二'])
const isswitch = ref(false)
const dialogVisible = ref(false)
const handleSave = () => {
ElMessageBox.prompt('请输入密码', '保存', {
confirmButtonText: '确定',
@ -39,10 +40,13 @@ const handleSave = () => {
ElMessage.success('保存成功')
init()
isswitch.value = false
} else {
ElMessage.error('保存失败')
}
})
}else{
ElMessage.error('密码错误,不能修改!')
isswitch.value = false
dialogVisible.value = true
}
})
})
@ -84,7 +88,9 @@ onMounted(() => {
</div>
<div class="row" v-for="(item, idx) in channelList" :key="idx">
<div class="cell cell-no">{{ item.channel_no }}</div>
<div class="cell cell-type" style="background: #ffffff;">{{ item.singal_type }}</div>
<div class="cell cell-type" style="background: #ffffff;">
<el-input v-model="item.singal_type" placeholder="" />
</div>
<div class="cell cell-line" style="background: #ffffff;">
<el-input-number v-model="item.limit_low" style="width: 100%;" :controls="false" />
</div>
@ -108,6 +114,12 @@ onMounted(() => {
<div class="btn-wrap">
<button class="save-btn" @click="handleSave">保存</button>
</div>
<el-dialog v-model="dialogVisible" :close-on-click-modal="false" :show-close="false" title="错误提示" width="300">
<div style="color: #FF4D4F;font-size: 16px;text-align: center;">原密码验证错误</div>
<template #footer>
<el-button type="primary" @click="dialogVisible = false">确定</el-button>
</template>
</el-dialog>
</div>
</template>

View File

@ -19,6 +19,8 @@ const channelList: any = ref([
const aoChannelList: any = ref([])
const lineOptions = ref([{ label: '线路一', value: 1 }, { label: '线路二', value: 2 }, { label: '线路三', value: 3 }, { label: '线路四', value: 4 }])
const categoryOptions = ref(['UA', 'UB', 'UC'])
const categoryOptions2 = ref(['IA', 'IB', 'IC'])
const dialogVisible = ref(false)
const isswitch = ref(false)
const handleSave = () => {
ElMessageBox.prompt('请输入密码', '保存', {
@ -42,12 +44,15 @@ const handleSave = () => {
if (res.data) {
ElMessage.success('保存成功')
isswitch.value = false
} else {
ElMessage.error('保存失败')
}
}).catch(err => {
isswitch.value = false
})
} else {
ElMessage.error('密码错误,不能修改!')
isswitch.value = false
dialogVisible.value = true
}
})
})
@ -68,6 +73,7 @@ function init() {
})
})
}
onMounted(() => {
init()
})
@ -87,7 +93,9 @@ onMounted(() => {
</div>
<div class="row" v-for="(item, idx) in channelList" :key="idx">
<div class="cell cell-no">{{ item.ch }}</div>
<div class="cell cell-type" style="background: #ffffff;">{{ item.singal_type }}</div>
<div class="cell cell-type" style="background: #ffffff;">
<el-input v-model="item.singal_type" placeholder="" />
</div>
<div class="cell cell-line" style="background: #ffffff;">
<el-select v-model="item.line_no">
<el-option v-for="line in lineOptions" :key="line.value" :value="line.value"
@ -96,7 +104,7 @@ onMounted(() => {
</div>
<div class="cell cell-category" style="background: #ffffff;">
<el-select v-model="item.type">
<el-option v-for="cat in categoryOptions" :key="cat" :value="cat" :label="cat"></el-option>
<el-option v-for="cat in (item.singal_type.includes('mA') ? categoryOptions2 : categoryOptions)" :key="cat" :value="cat" :label="cat"></el-option>
</el-select>
</div>
<div class="cell cell-low" style="background: #ffffff;">
@ -111,6 +119,16 @@ onMounted(() => {
<div class="btn-wrap">
<button class="save-btn" @click="handleSave">保存</button>
</div>
<el-dialog v-model="dialogVisible" :close-on-click-modal="false" :show-close="false" title="错误提示" width="300">
<div style="color: #FF4D4F;font-size: 16px;text-align: center;">原密码验证错误</div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">
确定
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
@ -239,5 +257,7 @@ onMounted(() => {
:deep(.el-input__inner) {
text-align: center !important;
font-size: 14px !important;
}
</style>

View File

@ -34,21 +34,24 @@ function formatTime(dateStr: string): string {
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}
const tableData = computed(() => {
const list = state.alarms[state.alarms.length - 1] || {}
const arr = []
let reslist:any = {}
if(list.alarm_type == 'line_alarm'){
reslist.name = `线路${list?.no || 1}`
}else if(list.alarm_type == 'ai_alarm'){
reslist.name = `通道${list?.no || 1}`
} else if(list.alarm_type == 'operate_alarm'){
reslist.name = ''
}
reslist.typeName = list.type
reslist.content = list.content
reslist.time = list.time ? formatTime(list.time) : ''
reslist.level = list.level || ''
return [reslist]
const alarms = state.alarms || []
const firstThree = alarms.slice(0, 2)
return firstThree.map(list => {
const reslist: any = {}
if (list.alarm_type == 'line_alarm') {
reslist.name = `线路${list?.no || 1}`
} else if (list.alarm_type == 'ai_alarm') {
reslist.name = `通道${list?.no || 1}`
} else if (list.alarm_type == 'operate_alarm') {
reslist.name = ''
}
reslist.typeName = list.type
reslist.content = list.content
reslist.time = list.time ? formatTime(list.time) : ''
reslist.level = list.level || ''
reslist.id = list.id || '0'
return reslist
})
})
function init(){
fetchChannelConfig().then(res => {
@ -191,7 +194,7 @@ onMounted(() => {
.container-bottom-box {
width: 100%;
height: calc(100vh - 660px);
height: calc(100vh - 646px);
padding: 20px;
background: #ffffff;
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);

View File

@ -31,8 +31,8 @@ const currentValues = computed<ValueGroup | null>(() => {
})
const frequency = computed(() => {
if (!currentValues.value) return '50'
return currentValues.value.frq
if (!currentValues.value) return '50.000'
return currentValues.value.frq.toFixed(3)
})
const infoList = computed(() => {
@ -50,45 +50,45 @@ const infoList = computed(() => {
return [
{
name: '单项电压V',
a: values.Ua,
b: values.Ub,
c: values.Uc,
a: values.Ua.toFixed(3),
b: values.Ub.toFixed(3),
c: values.Uc.toFixed(3),
total: '',
},
{
name: '单相电流A',
a: values.Ia,
b: values.Ib,
c: values.Ic,
a: values.Ia.toFixed(3),
b: values.Ib.toFixed(3),
c: values.Ic.toFixed(3),
total: '',
},
{
name: '有功功率kw',
a: values.Pa,
b: values.Pb,
c: values.Pc,
total: values.Pt
a: values.Pa.toFixed(2),
b: values.Pb.toFixed(2),
c: values.Pc.toFixed(2),
total: values.Pt.toFixed(2)
},
{
name: '无功功率kvar',
a: values.Qa,
b: values.Qb,
c: values.Qc,
total: values.Qt
a: values.Qa.toFixed(2),
b: values.Qb.toFixed(2),
c: values.Qc.toFixed(2),
total: values.Qt.toFixed(2)
},
{
name: '视在功率kVA',
a: values.Sa,
b: values.Sb,
c: values.Sc,
total: values.St
a: values.Sa.toFixed(2),
b: values.Sb.toFixed(2),
c: values.Sc.toFixed(2),
total: values.St.toFixed(2)
},
{
name: '功率因数cos',
a: values.PFa,
b: values.PFb,
c: values.PFc,
total: values.PFt
a: values.PFa.toFixed(3),
b: values.PFb.toFixed(3),
c: values.PFc.toFixed(3),
total: values.PFt.toFixed(3)
},
]
})
@ -100,9 +100,9 @@ const infoData = computed(() => {
}
return {
name: '线电压V',
a: values?.Uab,
b: values?.Ubc,
c: values?.Uca,
a: values?.Uab?.toFixed(3),
b: values?.Ubc?.toFixed(3),
c: values?.Uca?.toFixed(3),
}
})
@ -118,28 +118,24 @@ function formatTime(dateStr: string): string {
}
const tableData = computed(() => {
const list = state.alarms[state.alarms.length - 1] || {}
const arr = []
let reslist:any = {}
if(list.alarm_type == 'line_alarm'){
reslist.name = `线路${list?.no || 1}`
}else if(list.alarm_type == 'ai_alarm'){
reslist.name = `通道${list?.no || 1}`
} else if(list.alarm_type == 'operate_alarm'){
reslist.name = ''
}
reslist.typeName = list.type
reslist.content = list.content
reslist.time = list.time ? formatTime(list.time) : ''
reslist.level = list.level || ''
return [reslist]
// return state.alarms.slice(0, 10).map((alarm, index) => ({
// name: `线${currentLineData.value?.line_no || 1}`,
// typeName: alarm.type,
// content: alarm.content,
// time: alarm.time,
// ts: '0',
// }))
const alarms = state.alarms || []
const firstThree = alarms.slice(0, 2)
return firstThree.map(list => {
const reslist: any = {}
if (list.alarm_type == 'line_alarm') {
reslist.name = `线路${list?.no || 1}`
} else if (list.alarm_type == 'ai_alarm') {
reslist.name = `通道${list?.no || 1}`
} else if (list.alarm_type == 'operate_alarm') {
reslist.name = ''
}
reslist.typeName = list.type
reslist.content = list.content
reslist.time = list.time ? formatTime(list.time) : ''
reslist.level = list.level || ''
reslist.id = list.id || '0'
return reslist
})
})
function handleClick(name: string) {
@ -218,7 +214,7 @@ onMounted(() => {
<div class="analog-quantity-container-bottom">
<div class="top-title">报警信息</div>
<div class="container-bottom-box">
<div v-for="(item, index) in tableData" :key="item.name" class="container-bottom-box-line1">
<div v-for="(item, index) in tableData" :key="item.id" class="container-bottom-box-line1">
<div style="width: 8%;text-align: center;">{{ index + 1 }}</div>
<div style="width: 14%;text-align: center;">{{ item.name }}</div>
<div style="width: 20%;text-align: center;">{{ item.typeName }}</div>
@ -349,7 +345,7 @@ onMounted(() => {
.container-bottom-box {
width: 100%;
height: calc(100vh - 660px);
height: calc(100vh - 645px);
padding: 20px;
background: #ffffff;
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
@ -371,4 +367,4 @@ onMounted(() => {
}
}
}
</style>
</style>

View File

@ -18,7 +18,8 @@ const channelList: any = ref([
])
const lineOptions = ref([{ label: '线路一', value: 1 }, { label: '线路二', value: 2 }, { label: '线路三', value: 3 }, { label: '线路四', value: 4 }])
const categoryOptions = ref(['UA', 'UB', 'UC'])
const categoryOptions2 = ref(['IA', 'IB', 'IC'])
const dialogVisible = ref(false)
const aiChannelList = ref([])
function init() {
fetchChannelConfig().then((res: any) => {
@ -59,12 +60,15 @@ const handleSave = () => {
if (res.code === 200) {
ElMessage.success('保存成功')
isswitch.value = false
} else {
ElMessage.error('保存失败')
}
}).catch(err => {
isswitch.value = false
})
} else {
ElMessage.error('密码错误,不能修改!')
isswitch.value = false
dialogVisible.value = true
}
})
})
@ -88,7 +92,9 @@ onMounted(() => {
</div>
<div class="row" v-for="(item, idx) in channelList" :key="idx">
<div class="cell cell-no">{{ item.ch }}</div>
<div class="cell cell-type" style="background: #ffffff;">{{ item.singal_type }}</div>
<div class="cell cell-type" style="background: #ffffff;">
<el-input v-model="item.singal_type" placeholder="" />
</div>
<div class="cell cell-line" style="background: #ffffff;">
<el-select v-model="item.line_no">
<el-option v-for="line in lineOptions" :key="line.value" :value="line.value"
@ -97,7 +103,8 @@ onMounted(() => {
</div>
<div class="cell cell-category" style="background: #ffffff;">
<el-select v-model="item.type">
<el-option v-for="cat in categoryOptions" :key="cat" :value="cat" :label="cat"></el-option>
<el-option v-for="cat in (item.singal_type.includes('mA') ? categoryOptions2 : categoryOptions)" :key="cat"
:value="cat" :label="cat"></el-option>
</el-select>
</div>
<div class="cell cell-low" style="background: #ffffff;">
@ -112,6 +119,16 @@ onMounted(() => {
<div class="btn-wrap">
<button class="save-btn" @click="handleSave">保存</button>
</div>
<el-dialog v-model="dialogVisible" :close-on-click-modal="false" :show-close="false" title="错误提示" width="300">
<div style="color: #FF4D4F;font-size: 16px;text-align: center;">原密码验证错误</div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">
确定
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>

View File

@ -63,7 +63,8 @@ const rtuProtocolOptions = [
{ label: 'Modbus RTU', value: 'Modbus RTU' },
{ label: 'IEC101 ', value: 'IEC101' }
]
const isswitch = ref(false)
const dialogVisible = ref(false)
//
const handleSave = () => {
ElMessageBox.prompt('请输入密码', '保存', {
@ -74,9 +75,11 @@ const handleSave = () => {
.then(({ value }) => {
verifyAccessPassword(value).then((res: any) => {
if (res.data) {
saveDeviceNetConfig(formData.value).then((res: any) => {
console.log(res)
if (isswitch.value) {
return
}
isswitch.value = true
if (res.code == 200) {
saveDevicePortConfig(formData2.value).then((res: any) => {
if (res.code == 200) {
@ -84,25 +87,26 @@ const handleSave = () => {
type: 'success',
message: `保存成功`,
})
isswitch.value = false
} else {
ElMessage({
type: 'info',
type: 'error',
message: '串口设置保存失败',
})
isswitch.value = false
}
})
} else {
ElMessage({
type: 'info',
type: 'error',
message: '常规配置保存失败',
})
isswitch.value = false
}
})
} else {
ElMessage({
type: 'info',
message: '密码错误,不能修改!',
})
isswitch.value = false
dialogVisible.value = true
}
})
})
@ -232,6 +236,12 @@ onMounted(() => {
<el-button type="primary" style="background-color: #0099ff;width: 150px;height: 40px;"
@click="handleSave">保存</el-button>
</div>
<el-dialog v-model="dialogVisible" :close-on-click-modal="false" :show-close="false" title="错误提示" width="300">
<div style="color: #FF4D4F;font-size: 16px;text-align: center;">原密码验证错误</div>
<template #footer>
<el-button type="primary" @click="dialogVisible = false">确定</el-button>
</template>
</el-dialog>
</div>
</template>

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { fetchAlarmHistory } from '@/api/platform'
const formData = ref({
const formData: any = ref({
no: null,
type: '',
timeRange: [],
@ -44,15 +44,31 @@ function init() {
const params: any = {
page: pagination.value.currentPage,
size: pagination.value.pageSize,
}
if (formData.value.timeRange && formData.value.timeRange.length > 0) {
params.start_time = formData.value.timeRange[0]
params.end_time = formData.value.timeRange[1]
}
fetchAlarmHistory(params).then((res: any) => {
tableData.value = res
})
}
const formatDate = (date: Date): string => {
const pad = (n: number): string => n.toString().padStart(2, '0')
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
}
const getDefaultTimeRange = (): [string, string] => {
const end = new Date()
const start = new Date()
start.setDate(start.getDate() - 7)
return [formatDate(start), formatDate(end)]
}
onMounted(() => {
formData.value.no = null
formData.value.type = ''
formData.value.timeRange = []
formData.value.timeRange = getDefaultTimeRange()
init()
})
</script>

View File

@ -22,6 +22,8 @@ const handleSave = async () => {
saveSystemConfig(form.value).then(res => {
if(res.code === 200){
ElMessage.success('保存成功')
} else {
ElMessage.error('保存失败')
}
isswitch.value = false
})

View File

@ -141,6 +141,7 @@ const init = () => {
})
}
const isswitch = ref(false)
const dialogVisible = ref(false)
const handleSave = () => {
ElMessageBox.prompt('请输入密码', '保存', {
confirmButtonText: '确定',
@ -160,10 +161,13 @@ const handleSave = () => {
ElMessage.success('保存成功')
init()
isswitch.value = false
} else {
ElMessage.error('保存失败')
}
})
}else{
ElMessage.error('密码错误,不能修改!')
isswitch.value = false
dialogVisible.value = true
}
})
})
@ -307,6 +311,12 @@ const handleSave = () => {
</div>
<el-button type="primary" class="save-btn" @click="handleSave">保存</el-button>
</div>
<el-dialog v-model="dialogVisible" :close-on-click-modal="false" :show-close="false" title="错误提示" width="300">
<div style="color: #FF4D4F;font-size: 16px;text-align: center;">原密码验证错误</div>
<template #footer>
<el-button type="primary" @click="dialogVisible = false">确定</el-button>
</template>
</el-dialog>
</div>
</template>

View File

@ -1,193 +0,0 @@
<!-- <script setup lang="ts">
import { ref, reactive } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { login } from '@/api/auth'
import { ElMessage } from 'element-plus'
const route = useRoute()
const router = useRouter()
const loginForm = ref({
account: '',
password: ''
})
const loading = ref(false)
const error = ref('')
const loginFormRef = ref()
const loginRules = reactive({
account: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
]
})
const handleLogin = () => {
loginFormRef.value.validate((valid: any) => {
if (valid) {
loading.value = true;
const params = {
account: loginForm.value.account,
password: loginForm.value.password
};
login(params)
.then((response: any) => {
localStorage.setItem('token', response.token)
localStorage.setItem('username', response.user.username)
localStorage.setItem('account', response.user.account)
const redirect = (route.query.redirect as string) || '/dashboard'
router.push(redirect)
})
.catch((error: any) => {
ElMessage.error(error.message || '登录失败,请检查用户名和密码');
})
.finally(() => {
loading.value = false;
});
}
});
};
</script>
<template>
<div class="login-container">
<div class="login-title-box">
<div class="login-title">PEX50K台架试验数据监控系统</div>
</div>
<div class="login-form">
<div class="login-form-title">欢迎登录</div>
<el-form ref="loginFormRef" :model="loginForm" :rules="loginRules" class="login-form-box">
<el-form-item prop="account">
<el-input v-model="loginForm.account" placeholder="请输入用户名">
<template #prefix>
<img src="../../assets/login/yh.png" alt="" />
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="loginForm.password" type="password" placeholder="请输入密码">
<template #prefix>
<img src="../../assets/login/mm.png" alt="" />
</template>
</el-input>
</el-form-item>
</el-form>
<div class="login-btn" @click="handleLogin">
<img src="../../assets/login/loginbt.png" alt="">
<span class="login-text">登录</span>
</div>
</div>
</div>
</template>
<style scoped>
.login-container {
display: flex;
justify-content: center;
align-items: center;
background-repeat: no-repeat;
height: 100vh;
padding: 16px;
background-image: url('../../assets/login/dl_bg.png');
position: relative;
}
.login-title-box {
width: 100%;
height: 80px;
position: absolute;
top: 100px;
display: flex;
align-items: center;
justify-content: center;
}
.login-title {
font-size: 50px;
font-weight: bold;
letter-spacing: 4px;
background: linear-gradient(to bottom, #FFFFFF 0%, #C3E3FF 75%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
color: transparent;
}
.login-form {
width: 650px;
height: 350px;
background-image: url('../../assets/login/dl_kuang.png');
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: right;
position: relative;
top: 80px;
right: -385px;
}
.login-form-title {
text-align: center;
font-size: 24px;
font-weight: 400;
color: #FFFFFF;
margin-top: 35px;
margin-bottom: 20px;
}
.login-form-box {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 20px;
}
.login-form-box {
:deep(.el-input__wrapper) {
width: 300px;
height: 46px;
background-color: transparent;
border-radius: 4px;
/* border: 1px solid rgba(0, 153, 255, 0.5); */
box-shadow: 0 0 0 1px rgba(0, 153, 255, 0.5) inset;
position: relative;
top: 30px;
}
:deep(.el-input__inner) {
font-size: 16px;
font-weight: 400;
color: #FFFFFF;
}
:deep(.el-input__prefix-inner>:last-child) {
margin-right: 10px;
padding-left: 10px;
}
:deep(.el-form-item__error) {
padding-top: 32px;
}
}
.login-btn img {
display: flex;
justify-content: center;
align-items: center;
margin-top: 20px;
cursor: pointer;
position: relative;
right: -177px;
top: 20px;
}
.login-text {
position: absolute;
left: 305px;
top: 267px;
font-size: 20px;
color: #FFFFFF;
cursor: pointer;
}
</style> -->

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import { ref, reactive,onMounted } from 'vue'
import { ref, reactive, onMounted } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { verifyAccessPassword,saveDeviceConfig,fetchDeviceConfig } from '@/api/platform'
import { verifyAccessPassword, saveDeviceConfig, fetchDeviceConfig, saveDevicePassword } from '@/api/platform'
import { ElMessage } from 'element-plus'
const formRef = ref<FormInstance>()
@ -19,7 +19,17 @@ const rules: FormRules = {
],
newPassword: [
{ required: true, message: '请输入新密码', trigger: 'blur' },
{ min: 6, max: 20, message: '密码长度需在 6~20 位之间', trigger: 'blur' }
{ min: 6, max: 6, message: '密码长度需 6位', trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (!/^\d{6}$/.test(value)) {
callback(new Error('密码必须为6位数字'))
} else {
callback()
}
},
trigger: 'blur'
}
],
confirmPassword: [
{ required: true, message: '请再次输入新密码', trigger: 'blur' },
@ -36,6 +46,10 @@ const rules: FormRules = {
]
}
const isSubmit = ref(false)
const dialogVisible = ref(false)
const handleClose = () => {
dialogVisible.value = false
}
//
const handleSubmit = async () => {
if (!formRef.value) return
@ -45,16 +59,8 @@ const handleSubmit = async () => {
isSubmit.value = true
verifyAccessPassword(form.oldPassword).then(res => {
if (res.data) {
// ElMessage({
// type: 'success',
// message: '',
// })
saveDeviceConfig({
saveDevicePassword({
password: form.newPassword,
hardware_version: deviceConfig.value.hardware_version,
software_version: deviceConfig.value.software_version,
net: deviceConfig.value.net,
uart: deviceConfig.value.uart,
}).then(res => {
if (res.data) {
ElMessage({
@ -64,7 +70,7 @@ const handleSubmit = async () => {
isSubmit.value = false
} else {
ElMessage({
type: 'info',
type: 'error',
message: '密码设置失败',
})
isSubmit.value = false
@ -72,27 +78,16 @@ const handleSubmit = async () => {
}
})
} else {
ElMessage({
type: 'error',
message: '原密码验证失败',
})
isSubmit.value = false
dialogVisible.value = true
return
}
})
}
})
}
function init() {
fetchDeviceConfig().then(res => {
if (res) {
deviceConfig.value = res
}
})
}
onMounted(() => {
init()
})
</script>
@ -122,9 +117,20 @@ onMounted(() => {
<!-- 保存按钮 -->
<el-button type="primary" style="background-color: #0099ff;width: 150px;height: 40px;" class="save-btn" @click="handleSubmit">
<el-button type="primary" style="background-color: #0099ff;width: 150px;height: 40px;" class="save-btn"
@click="handleSubmit">
保存
</el-button>
<el-dialog v-model="dialogVisible" :close-on-click-modal="false" :show-close="false" title="错误提示" width="500" :before-close="handleClose">
<div style="color: #FF4D4F;font-size: 16px;text-align: center;">原密码验证错误</div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">
确定
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
@ -142,6 +148,7 @@ onMounted(() => {
border-radius: 4px;
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
}
.title {
font-size: 18px;
font-weight: 700;
@ -162,6 +169,7 @@ onMounted(() => {
.password-form {
width: 60%;
}
.save-btn {
margin-top: 30px;
font-size: 14px;
@ -178,7 +186,8 @@ onMounted(() => {
color: #363636;
min-height: 40px;
}
:deep(.el-form-item__label) {
line-height: 40px !important;
line-height: 40px !important;
}
</style>

View File

@ -34,21 +34,24 @@ function formatTime(dateStr: string): string {
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}
const tableData = computed(() => {
const list = state.alarms[state.alarms.length - 1] || {}
const arr = []
let reslist: any = {}
const alarms = state.alarms || []
const firstThree = alarms.slice(0, 2)
return firstThree.map(list => {
const reslist: any = {}
if (list.alarm_type == 'line_alarm') {
reslist.name = `线路${list?.no || 1}`
reslist.name = `线路${list?.no || 1}`
} else if (list.alarm_type == 'ai_alarm') {
reslist.name = `通道${list?.no || 1}`
reslist.name = `通道${list?.no || 1}`
} else if (list.alarm_type == 'operate_alarm') {
reslist.name = ''
reslist.name = ''
}
reslist.typeName = list.type
reslist.content = list.content
reslist.time = list.time ? formatTime(list.time) : ''
reslist.level = list.level || ''
return [reslist]
reslist.id = list.id || '0'
return reslist
})
})
onMounted(() => {
@ -204,7 +207,7 @@ onMounted(() => {
.container-bottom-box {
width: 100%;
height: calc(100vh - 660px);
height: calc(100vh - 645px);
padding: 20px;
background: #ffffff;
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);