odf_new/odf-uniapp/components/port-edit-dialog.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>