595 lines
12 KiB
Vue
595 lines
12 KiB
Vue
<template>
|
|
<view v-if="visible" class="port-edit-overlay" @click.self="onOverlayClick">
|
|
<view class="port-edit-content">
|
|
<!-- 加载中 -->
|
|
<view v-if="loading" class="loading-box">
|
|
<text class="loading-text">loading...</text>
|
|
</view>
|
|
|
|
<scroll-view v-else class="scroll-area" scroll-y>
|
|
<!-- 端口位置和状态 -->
|
|
<view class="section">
|
|
<view class="location-row">
|
|
<text class="location-text">位置:{{ portData.frameName }}{{ portData.name }}</text>
|
|
<view class="status-badge">
|
|
<view class="status-dot" :class="portData.status === 1 ? 'dot-green' : 'dot-red'" />
|
|
<text class="status-label">{{ portData.status === 1 ? '已连接' : '已断开' }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 备注说明 -->
|
|
<view class="section">
|
|
<text class="section-title">备注说明</text>
|
|
<view class="remarks-row">
|
|
<view class="textarea-wrap">
|
|
<textarea class="remarks-input" v-model="form.remarks" :maxlength="-1" placeholder="请输入备注说明"
|
|
:disabled="!store.isPermission" auto-height />
|
|
</view>
|
|
<view v-if="store.isPermission" class="add-note-btn" @click="showAddNote = true">
|
|
<text class="add-note-text">添加备注</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 光衰信息 -->
|
|
<view class="section">
|
|
<text class="section-title">光衰信息</text>
|
|
<input class="form-input" v-model="form.opticalAttenuation" placeholder="请输入光衰信息"
|
|
:disabled="!store.isPermission" />
|
|
</view>
|
|
|
|
<!-- 历史障碍记录 -->
|
|
<view class="section">
|
|
<text class="section-title">历史障碍记录</text>
|
|
<view class="fault-list">
|
|
<view class="fault-item" v-for="(item, index) in form.historyFault" :key="index">
|
|
<view class="fault-row">
|
|
<picker mode="date" :value="item.faultTime ? item.faultTime.substring(0, 10) : ''"
|
|
:disabled="!store.isPermission" @change="onFaultDateChange($event, index)">
|
|
<view class="date-picker">
|
|
<text :class="item.faultTime ? 'date-text' : 'date-placeholder'">
|
|
{{ item.faultTime || '选择日期' }}
|
|
</text>
|
|
</view>
|
|
</picker>
|
|
<input class="fault-reason-input" v-model="item.faultReason" placeholder="故障原因"
|
|
:disabled="!store.isPermission" />
|
|
<view v-if="store.isPermission" class="delete-btn" @click="removeFault(index)">
|
|
<text class="delete-btn-text">-</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view v-if="store.isPermission" class="add-record-link" @click="addFault">
|
|
<text class="add-record-text">添加新记录</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 光缆段信息 -->
|
|
<view class="section">
|
|
<text class="section-title">光缆段信息</text>
|
|
<input class="form-input" v-model="form.opticalCableOffRemarks" placeholder="请输入光缆段信息"
|
|
:disabled="!store.isPermission" />
|
|
</view>
|
|
|
|
<!-- 权限控制区域 -->
|
|
<view v-if="store.isPermission" class="section">
|
|
<text class="section-title">改变状态</text>
|
|
<view class="status-toggle-row">
|
|
<view class="toggle-btn toggle-green" :class="{ 'toggle-active': form.status === 1 }"
|
|
@click="setStatus(1)">
|
|
<text class="toggle-text">连接</text>
|
|
</view>
|
|
<view class="toggle-btn toggle-red" :class="{ 'toggle-active': form.status === 0 }"
|
|
@click="setStatus(0)">
|
|
<text class="toggle-text">断开</text>
|
|
</view>
|
|
</view>
|
|
<text class="hint-text">断开后只清空备注说明,其他内容不影响</text>
|
|
</view>
|
|
</scroll-view>
|
|
|
|
<!-- 底部按钮(固定在弹窗底部) -->
|
|
<view v-if="!loading" class="btn-row">
|
|
<template v-if="store.isPermission">
|
|
<view class="btn btn-cancel" @click="onClose">
|
|
<text class="btn-text">取消</text>
|
|
</view>
|
|
<view class="btn btn-submit" @click="onSubmit">
|
|
<text class="btn-text-white">提交</text>
|
|
</view>
|
|
</template>
|
|
<template v-else>
|
|
<view class="btn btn-cancel btn-full" @click="onClose">
|
|
<text class="btn-text">关闭</text>
|
|
</view>
|
|
</template>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 添加备注弹窗 -->
|
|
<addNoteDialog :visible="showAddNote" @close="showAddNote = false" @confirm="onNoteConfirm" />
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import {
|
|
ref,
|
|
reactive,
|
|
watch
|
|
} from 'vue'
|
|
import {
|
|
getPortDetail,
|
|
savePort
|
|
} from '@/services/machine'
|
|
import store from '@/store'
|
|
import addNoteDialog from '@/components/add-note-dialog.vue'
|
|
|
|
const props = defineProps({
|
|
visible: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
portId: {
|
|
type: [Number, String],
|
|
default: ''
|
|
}
|
|
})
|
|
|
|
const emit = defineEmits(['close', 'saved'])
|
|
|
|
const loading = ref(false)
|
|
const showAddNote = ref(false)
|
|
|
|
const portData = reactive({
|
|
id: '',
|
|
name: '',
|
|
frameName: '',
|
|
status: 0,
|
|
remarks: '',
|
|
opticalAttenuation: '',
|
|
opticalCableOffRemarks: '',
|
|
historyRemarks: '',
|
|
historyFault: []
|
|
})
|
|
|
|
const form = reactive({
|
|
status: 0,
|
|
remarks: '',
|
|
opticalAttenuation: '',
|
|
opticalCableOffRemarks: '',
|
|
historyRemarks: '',
|
|
historyFault: []
|
|
})
|
|
|
|
watch(
|
|
() => props.visible,
|
|
(val) => {
|
|
if (val && props.portId) {
|
|
loadPortDetail()
|
|
}
|
|
}
|
|
)
|
|
|
|
async function loadPortDetail() {
|
|
loading.value = true
|
|
try {
|
|
const res = await getPortDetail(props.portId)
|
|
if (res.code === 200 && res.data) {
|
|
const d = res.data
|
|
Object.assign(portData, {
|
|
id: d.id,
|
|
name: d.name || '',
|
|
frameName: d.frameName || '',
|
|
status: d.status,
|
|
remarks: d.remarks || '',
|
|
opticalAttenuation: d.opticalAttenuation || '',
|
|
opticalCableOffRemarks: d.opticalCableOffRemarks || '',
|
|
historyRemarks: d.historyRemarks || '',
|
|
historyFault: d.historyFault || []
|
|
})
|
|
// 初始化表单
|
|
form.status = d.status
|
|
form.remarks = d.remarks || ''
|
|
form.opticalAttenuation = d.opticalAttenuation || ''
|
|
form.opticalCableOffRemarks = d.opticalCableOffRemarks || ''
|
|
form.historyRemarks = d.historyRemarks || ''
|
|
form.historyFault = (d.historyFault || []).map(item => ({
|
|
faultTime: item.faultTime || '',
|
|
faultReason: item.faultReason || ''
|
|
}))
|
|
}
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
function setStatus(status) {
|
|
form.status = status
|
|
if (status === 0) {
|
|
form.remarks = ''
|
|
}
|
|
}
|
|
|
|
function addFault() {
|
|
form.historyFault.push({
|
|
faultTime: '',
|
|
faultReason: ''
|
|
})
|
|
}
|
|
|
|
function removeFault(index) {
|
|
form.historyFault.splice(index, 1)
|
|
}
|
|
|
|
function onFaultDateChange(e, index) {
|
|
form.historyFault[index].faultTime = e.detail.value
|
|
}
|
|
|
|
function onNoteConfirm(text) {
|
|
form.remarks = form.remarks ? form.remarks + '\n' + text : text
|
|
showAddNote.value = false
|
|
}
|
|
|
|
function onClose() {
|
|
emit('close')
|
|
}
|
|
|
|
function onOverlayClick() {
|
|
// 不做任何操作,防止误触关闭
|
|
}
|
|
|
|
async function onSubmit() {
|
|
// 校验历史障碍时间
|
|
for (let i = 0; i < form.historyFault.length; i++) {
|
|
if (!form.historyFault[i].faultTime || !form.historyFault[i].faultTime.trim()) {
|
|
uni.showToast({
|
|
title: '请选择障碍发生时间!',
|
|
icon: 'none'
|
|
})
|
|
return
|
|
}
|
|
}
|
|
|
|
try {
|
|
const res = await savePort({
|
|
Id: Number(props.portId),
|
|
Status: form.status,
|
|
Remarks: form.remarks,
|
|
OpticalAttenuation: form.opticalAttenuation,
|
|
HistoryRemarks: form.historyRemarks,
|
|
HistoryFault: form.historyFault,
|
|
OpticalCableOffRemarks: form.opticalCableOffRemarks
|
|
})
|
|
if (res.code === 200) {
|
|
uni.showToast({
|
|
title: '保存成功',
|
|
icon: 'success'
|
|
})
|
|
emit('close')
|
|
emit('saved')
|
|
} else {
|
|
uni.showToast({
|
|
title: res.msg || '保存失败',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
} catch (e) {
|
|
uni.showToast({
|
|
title: '网络异常',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.port-edit-overlay {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
z-index: 999;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.port-edit-content {
|
|
background-color: #fff;
|
|
border-radius: 20rpx;
|
|
width: 90%;
|
|
max-height: 80vh;
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.loading-box {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
padding: 80rpx 0;
|
|
}
|
|
|
|
.loading-text {
|
|
font-size: 28rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.scroll-area {
|
|
width: 90%;
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
padding: 28rpx 28rpx 0;
|
|
}
|
|
|
|
.section {
|
|
margin-bottom: 24rpx;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 27rpx;
|
|
font-weight: 600;
|
|
color: #333;
|
|
margin-bottom: 10rpx;
|
|
display: block;
|
|
}
|
|
|
|
.location-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding-bottom: 16rpx;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
}
|
|
|
|
.location-text {
|
|
font-size: 28rpx;
|
|
font-weight: 600;
|
|
color: #333;
|
|
flex-shrink: 1;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.status-badge {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6rpx;
|
|
flex-shrink: 0;
|
|
margin-left: 16rpx;
|
|
}
|
|
|
|
.status-dot {
|
|
width: 16rpx;
|
|
height: 16rpx;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.dot-green {
|
|
background-color: #4CAF50;
|
|
}
|
|
|
|
.dot-red {
|
|
background-color: #F44336;
|
|
}
|
|
|
|
.status-label {
|
|
font-size: 24rpx;
|
|
color: #666;
|
|
}
|
|
|
|
.remarks-row {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10rpx;
|
|
}
|
|
|
|
.textarea-wrap {
|
|
width: 100%;
|
|
overflow: hidden;
|
|
border: 1rpx solid #e0e0e0;
|
|
border-radius: 10rpx;
|
|
background: #fafafa;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.remarks-input {
|
|
width: 100%;
|
|
min-height: 120rpx;
|
|
padding: 14rpx;
|
|
font-size: 26rpx;
|
|
box-sizing: border-box;
|
|
background: transparent;
|
|
border: none;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.add-note-btn {
|
|
align-self: flex-end;
|
|
padding: 8rpx 20rpx;
|
|
background-color: #1A73EC;
|
|
border-radius: 8rpx;
|
|
}
|
|
|
|
.add-note-text {
|
|
font-size: 24rpx;
|
|
color: #fff;
|
|
}
|
|
|
|
.form-input {
|
|
width: 100%;
|
|
height: 68rpx;
|
|
border: 1rpx solid #e0e0e0;
|
|
border-radius: 10rpx;
|
|
padding: 0 14rpx;
|
|
font-size: 26rpx;
|
|
box-sizing: border-box;
|
|
background: #fafafa;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.fault-list {
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.fault-item {
|
|
margin-bottom: 12rpx;
|
|
}
|
|
|
|
.fault-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10rpx;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.date-picker {
|
|
flex-shrink: 0;
|
|
min-width: 0;
|
|
width: 200rpx;
|
|
height: 68rpx;
|
|
border: 1rpx solid #e0e0e0;
|
|
border-radius: 10rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0 12rpx;
|
|
background: #fafafa;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.date-text {
|
|
font-size: 23rpx;
|
|
color: #333;
|
|
}
|
|
|
|
.date-placeholder {
|
|
font-size: 23rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.fault-reason-input {
|
|
flex: 1;
|
|
min-width: 0;
|
|
height: 68rpx;
|
|
border: 1rpx solid #e0e0e0;
|
|
border-radius: 10rpx;
|
|
padding: 0 12rpx;
|
|
font-size: 26rpx;
|
|
background: #fafafa;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.delete-btn {
|
|
flex-shrink: 0;
|
|
width: 48rpx;
|
|
height: 48rpx;
|
|
border-radius: 50%;
|
|
background-color: #F44336;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.delete-btn-text {
|
|
font-size: 32rpx;
|
|
color: #fff;
|
|
font-weight: 700;
|
|
line-height: 1;
|
|
}
|
|
|
|
.add-record-link {
|
|
padding: 4rpx 0;
|
|
}
|
|
|
|
.add-record-text {
|
|
font-size: 25rpx;
|
|
color: #1A73EC;
|
|
}
|
|
|
|
.status-toggle-row {
|
|
display: flex;
|
|
gap: 16rpx;
|
|
margin-bottom: 10rpx;
|
|
}
|
|
|
|
.toggle-btn {
|
|
flex: 1;
|
|
height: 68rpx;
|
|
border-radius: 10rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
opacity: 0.35;
|
|
}
|
|
|
|
.toggle-green {
|
|
background-color: #4CAF50;
|
|
}
|
|
|
|
.toggle-red {
|
|
background-color: #F44336;
|
|
}
|
|
|
|
.toggle-active {
|
|
opacity: 1;
|
|
}
|
|
|
|
.toggle-text {
|
|
font-size: 28rpx;
|
|
color: #fff;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.hint-text {
|
|
font-size: 22rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.btn-row {
|
|
display: flex;
|
|
gap: 16rpx;
|
|
padding: 16rpx 28rpx 20rpx;
|
|
border-top: 1rpx solid #f0f0f0;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.btn {
|
|
flex: 1;
|
|
height: 76rpx;
|
|
border-radius: 10rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.btn-cancel {
|
|
background-color: #f5f5f5;
|
|
border: 1rpx solid #e0e0e0;
|
|
}
|
|
|
|
.btn-submit {
|
|
background-color: #1A73EC;
|
|
}
|
|
|
|
.btn-full {
|
|
flex: 1;
|
|
}
|
|
|
|
.btn-text {
|
|
font-size: 28rpx;
|
|
color: #666;
|
|
}
|
|
|
|
.btn-text-white {
|
|
font-size: 28rpx;
|
|
color: #fff;
|
|
}
|
|
</style> |