删除设备主要是关联设备的删除

pull/127/head
ZHW 2 weeks ago
parent a217f0ac6c
commit 6fd4ff8471

@ -126,6 +126,8 @@ public class AdminController {
}
}
/**
*
* /

@ -94,23 +94,30 @@ public class TerminalServiceImpl implements TerminalService {
}
@Override
@Transactional
public void deleteTerminal(String terminalId) {
// 1. 校验终端是否已绑定设备使用新增的existsByTerminalId方法
if (mappingRepository.existsByTerminalId(terminalId)) {
@Transactional
public void deleteTerminal(String terminalId) {
// 1. 检查终端映射记录是否存在
Optional<DeviceTerminalMapping> mappingOpt = mappingRepository.findByTerminalId(terminalId);
if (mappingOpt.isPresent()) {
// 检查是否实际绑定了设备deviceId不为null
DeviceTerminalMapping mapping = mappingOpt.get();
if (mapping.getDeviceId() != null && !mapping.getDeviceId().isEmpty()) {
throw new RuntimeException("终端已绑定设备,无法删除,请先解除设备关联");
}
}
// 2. 校验终端是否存在复用原有existsById方法
if (!locationRepository.existsById(terminalId)) {
throw new RuntimeException("终端不存在,无需删除:" + terminalId);
}
// 3. 级联删除数据(先删映射表,再删位置表,保证数据一致性)
mappingRepository.deleteByTerminalId(terminalId); // 新增的批量删除方法
locationRepository.deleteById(terminalId); // 复用原有删除方法
// 2. 校验终端是否存在
if (!locationRepository.existsById(terminalId)) {
throw new RuntimeException("终端不存在,无需删除:" + terminalId);
}
// 3. 级联删除数据
mappingRepository.deleteByTerminalId(terminalId);
locationRepository.deleteById(terminalId);
}
@Override
public TerminalManageVO getTerminalById(String terminalId) {
// 1. 查询位置信息复用原有findById方法

@ -55,7 +55,8 @@ const menuItems: MenuItem[] = [
route: '/home/equipment',
children: [
{ name: '制水机', route: '/home/equipment/water-maker' },
{ name: '供水机', route: '/home/equipment/water-supplier' }
{ name: '供水机', route: '/home/equipment/water-supplier' },
{ name: '终端机', route: '/home/equipment/terminal' } //
]
},
{

@ -70,6 +70,17 @@ const router = createRouter({
title: '供水设备'
}
},
{
path: '/home/equipment/terminal',
component: () => import('@/views/equipment/Terminal.vue'),
meta: { requiresAuth: true }
},
{
path: '/home/equipment/terminal/:id',
component: () => import('@/views/equipment/TerminalDetail.vue'), // 如果需要详情页
meta: { requiresAuth: true }
},
// 工单管理相关路由
{
path: 'work-order',

@ -0,0 +1,807 @@
<template>
<div class="terminal-page">
<!-- 页面标题和面包屑 -->
<div class="page-header">
<h2>终端机管理</h2>
<div class="breadcrumb">校园矿化水平台 / 设备监控 / 终端机</div>
</div>
<!-- 操作按钮区 -->
<div class="action-bar">
<button class="btn-add" @click="showAddModal = true">添加终端机</button>
<div class="filters">
<!-- 搜索框 -->
<div class="search-box">
<input
type="text"
placeholder="搜索终端ID或名称..."
v-model="searchKeyword"
@input="handleSearch"
>
<button class="search-btn" @click="handleSearch"></button>
</div>
<!-- 状态筛选 -->
<select
v-model="selectedStatus"
class="filter-select"
@change="handleSearch"
>
<option value="">全部状态</option>
<option value="active">在线</option>
<option value="inactive">离线</option>
<option value="warning">警告</option>
<option value="fault">故障</option>
</select>
</div>
</div>
<!-- 终端表格 -->
<div class="card">
<table class="equipment-table">
<thead>
<tr>
<th>终端ID</th>
<th>终端名称</th>
<th>经度</th>
<th>纬度</th>
<th>状态</th>
<th>安装日期</th>
<th>关联设备ID</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="terminal in paginatedTerminals" :key="terminal.terminalId">
<td>{{ terminal.terminalId }}</td>
<td>{{ terminal.terminalName }}</td>
<td>{{ terminal.longitude }}</td>
<td>{{ terminal.latitude }}</td>
<td>
<span :class="`status-tag ${terminal.terminalStatus}`">
{{ formatStatus(terminal.terminalStatus) }}
</span>
</td>
<td>{{ formatDate(terminal.installDate) }}</td>
<td>{{ terminal.deviceId || '-' }}</td>
<td class="operation-buttons">
<button class="btn-view" @click="viewTerminal(terminal.terminalId)"></button>
<button
class="btn-edit"
@click="editTerminal(terminal)"
>
编辑
</button>
<button
class="btn-delete"
@click="deleteTerminal(terminal.terminalId)"
>
删除
</button>
</td>
</tr>
<tr v-if="paginatedTerminals.length === 0">
<td colspan="8" class="no-data">暂无终端数据</td>
</tr>
</tbody>
</table>
</div>
<!-- 分页控件 -->
<div class="pagination">
<button
class="page-btn"
:disabled="currentPage === 1"
@click="currentPage--"
>
上一页
</button>
<span class="page-info">
{{ currentPage }} / {{ totalPages }} ( {{ filteredTerminals.length }} 条记录)
</span>
<button
class="page-btn"
:disabled="currentPage === totalPages"
@click="currentPage++"
>
下一页
</button>
</div>
<!-- 添加/编辑终端模态框 -->
<div v-if="showAddModal" class="modal-overlay" @click="showAddModal = false">
<div class="modal-content" @click.stop>
<h3>{{ isEditing ? '编辑终端' : '添加终端机' }}</h3>
<form @submit.prevent="saveTerminal">
<div class="form-group">
<label>终端ID:</label>
<input v-model="currentTerminal.terminalId" type="text" :disabled="isEditing" required>
</div>
<div class="form-group">
<label>终端名称:</label>
<input v-model="currentTerminal.terminalName" type="text" required>
</div>
<div class="form-group">
<label>经度:</label>
<input v-model="currentTerminal.longitude" type="number" step="any" required>
</div>
<div class="form-group">
<label>纬度:</label>
<input v-model="currentTerminal.latitude" type="number" step="any" required>
</div>
<div class="form-group">
<label>状态:</label>
<select v-model="currentTerminal.terminalStatus">
<option value="active">在线</option>
<option value="inactive">离线</option>
<option value="warning">警告</option>
<option value="fault">故障</option>
</select>
</div>
<div class="form-group">
<label>安装日期:</label>
<input v-model="currentTerminal.installDate" type="date">
</div>
<div class="form-group">
<label>关联设备ID:</label>
<input v-model="currentTerminal.deviceId" type="text">
</div>
<div class="form-actions">
<button type="button" @click="showAddModal = false">取消</button>
<button type="submit">{{ isEditing ? '更新' : '添加' }}</button>
</div>
</form>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import { request } from '@/api/request'
import type { ResultVO } from '@/api/types/auth'
//
const isTerminalManageVO = (obj: any): obj is TerminalManageVO => {
return obj &&
typeof obj === 'object' &&
typeof obj.terminalId === 'string' &&
typeof obj.terminalName === 'string' &&
typeof obj.longitude === 'number' &&
typeof obj.latitude === 'number' &&
['active', 'inactive', 'warning', 'fault'].includes(obj.terminalStatus)
}
//
type TerminalStatus = 'active' | 'inactive' | 'fault' | 'warning'
// - TerminalManageVO
interface TerminalManageVO {
terminalId: string
terminalName: string
longitude: number
latitude: number
terminalStatus: TerminalStatus
installDate?: string
deviceId?: string
}
//
const terminals = ref<TerminalManageVO[]>([])
const searchKeyword = ref('')
const selectedStatus = ref('') //
const currentPage = ref(1)
const pageSize = 10 //
const router = useRouter()
const authStore = useAuthStore() // auth store
//
const showAddModal = ref(false)
const isEditing = ref(false)
//
const currentTerminal = ref<TerminalManageVO>({
terminalId: '',
terminalName: '',
longitude: 0,
latitude: 0,
terminalStatus: 'active'
})
//
//
const loadTerminals = async (): Promise<void> => {
try {
// tokenisLoggedIn
const token = authStore.token
if (!token) {
console.warn('未获取到 Token跳转到登录页')
router.push('/login')
return
}
console.log('开始加载终端机数据...')
//
const result = await request<ResultVO<TerminalManageVO[]>>(
`/api/web/terminal/list`,
{ method: 'GET' }
)
console.log('终端机请求结果:', result)
// ResultVO
if (result && typeof result === 'object' && 'code' in result) {
// ResultVO
if (result.code === 200 && result.data && Array.isArray(result.data)) {
console.log(`获取到${result.data.length}个终端`)
//
terminals.value = result.data.map(item => ({
terminalId: item.terminalId,
terminalName: item.terminalName,
longitude: item.longitude,
latitude: item.latitude,
terminalStatus: item.terminalStatus,
installDate: item.installDate,
deviceId: item.deviceId
}))
} else {
console.warn('API响应非成功状态或数据格式错误:', result)
terminals.value = []
}
} else {
// ResultVO
if (Array.isArray(result)) {
console.log(`获取到${(result as TerminalManageVO[]).length}个终端`)
terminals.value = (result as TerminalManageVO[]).map(item => ({
terminalId: item.terminalId,
terminalName: item.terminalName,
longitude: item.longitude,
latitude: item.latitude,
terminalStatus: item.terminalStatus,
installDate: item.installDate,
deviceId: item.deviceId
}))
} else {
console.warn('API响应数据格式错误:', result)
terminals.value = []
}
}
if (terminals.value.length === 0) {
console.log('提示:未找到任何终端,请确认是否已添加终端')
}
} catch (error) {
console.error('加载终端数据失败:', error)
terminals.value = []
if ((error as Error).message.includes('401')) {
authStore.logout()
router.push('/login')
}
}
}
//
const filteredTerminals = computed(() => {
return terminals.value.filter(terminal => {
const keywordMatch = searchKeyword.value.trim() === '' ||
terminal.terminalId.toLowerCase().includes(searchKeyword.value.toLowerCase()) ||
terminal.terminalName.toLowerCase().includes(searchKeyword.value.toLowerCase())
const statusMatch = selectedStatus.value === '' || terminal.terminalStatus === selectedStatus.value
return keywordMatch && statusMatch
})
})
//
const paginatedTerminals = computed(() => {
const start = (currentPage.value - 1) * pageSize
const end = start + pageSize
return filteredTerminals.value.slice(start, end)
})
const totalPages = computed(() => {
return Math.ceil(filteredTerminals.value.length / pageSize)
})
//
const formatStatus = (status: TerminalStatus): string => {
const statusMap: Record<string, string> = {
active: '在线',
inactive: '离线',
warning: '警告',
fault: '故障'
}
return statusMap[status] || status
}
//
const formatDate = (dateString?: string): string => {
if (!dateString) return '-'
//
return dateString
}
//
const handleSearch = () => {
currentPage.value = 1 //
}
//
const viewTerminal = (id: string) => {
router.push(`/home/equipment/terminal/${id}`)
}
//
const editTerminal = (terminal: TerminalManageVO) => {
currentTerminal.value = { ...terminal }
isEditing.value = true
showAddModal.value = true
}
//
//
const deleteTerminal = async (terminalId: string) => {
if (!confirm(`确定要删除终端 ${terminalId} 吗?`)) {
return
}
try {
// token
const token = authStore.token
if (!token) {
console.warn('未获取到 Token跳转到登录页')
router.push('/login')
return
}
const result = await request<any>(`/api/web/terminal/delete/${terminalId}`, {
method: 'DELETE'
})
// Map
if (result && typeof result === 'object' && result.message) {
if (result.message.includes('成功')) {
//
terminals.value = terminals.value.filter(t => t.terminalId !== terminalId)
alert('终端删除成功')
} else {
alert(`删除终端失败: ${result.message}`)
}
} else {
//
//
terminals.value = terminals.value.filter(t => t.terminalId !== terminalId)
alert('终端删除成功')
}
} catch (error) {
console.error('删除终端失败:', error)
alert('删除终端失败')
if ((error as Error).message.includes('401')) {
authStore.logout()
router.push('/login')
}
}
}
//
const saveTerminal = async () => {
try {
// token
const token = authStore.token
if (!token) {
console.warn('未获取到 Token跳转到登录页')
router.push('/login')
return
}
let result: ResultVO<TerminalManageVO> | TerminalManageVO
if (isEditing.value) {
//
result = await request<ResultVO<TerminalManageVO> | TerminalManageVO>('/api/web/terminal/update', {
method: 'PUT',
body: JSON.stringify(currentTerminal.value)
})
} else {
//
result = await request<ResultVO<TerminalManageVO> | TerminalManageVO>('/api/web/terminal/add', {
method: 'POST',
body: JSON.stringify(currentTerminal.value)
})
}
// ResultVO
if (result && typeof result === 'object' && 'code' in result) {
// ResultVO
if (result.code === 200 && result.data && isTerminalManageVO(result.data)) {
//
await loadTerminals()
//
showAddModal.value = false
isEditing.value = false
currentTerminal.value = {
terminalId: '',
terminalName: '',
longitude: 0,
latitude: 0,
terminalStatus: 'active'
}
alert(isEditing.value ? '终端更新成功' : '终端添加成功')
} else if (result.code === 200) {
// code200data
//
await loadTerminals()
//
showAddModal.value = false
isEditing.value = false
currentTerminal.value = {
terminalId: '',
terminalName: '',
longitude: 0,
latitude: 0,
terminalStatus: 'active'
}
alert(isEditing.value ? '终端更新成功' : '终端添加成功')
} else {
alert(`${isEditing.value ? '更新' : '添加'}终端失败: ${result.message}`)
}
}
//
else if (isTerminalManageVO(result)) {
//
await loadTerminals()
//
showAddModal.value = false
isEditing.value = false
currentTerminal.value = {
terminalId: '',
terminalName: '',
longitude: 0,
latitude: 0,
terminalStatus: 'active'
}
alert(isEditing.value ? '终端更新成功' : '终端添加成功')
}
else {
//
//
await loadTerminals()
//
showAddModal.value = false
isEditing.value = false
currentTerminal.value = {
terminalId: '',
terminalName: '',
longitude: 0,
latitude: 0,
terminalStatus: 'active'
}
alert(isEditing.value ? '终端更新成功' : '终端添加成功')
}
} catch (error) {
console.error(`${isEditing.value ? '更新' : '添加'}终端失败:`, error)
alert(`${isEditing.value ? '更新' : '添加'}终端失败`)
if ((error as Error).message.includes('401')) {
authStore.logout()
router.push('/login')
}
}
}
//
onMounted(async () => {
console.log('🚀 开始加载终端数据...')
await loadTerminals()
})
</script>
<style scoped>
/* 样式与制水机页面保持一致 */
.terminal-page {
padding: 20px;
}
.page-header {
margin-bottom: 24px;
}
.page-header h2 {
font-size: 24px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.breadcrumb {
color: #666;
font-size: 14px;
}
.action-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
flex-wrap: wrap;
gap: 16px;
}
.btn-add {
background: #42b983;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background 0.3s;
}
.btn-add:hover {
background: #359e75;
}
.filters {
display: flex;
gap: 12px;
align-items: center;
}
.search-box {
display: flex;
gap: 8px;
}
.search-box input {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
width: 240px;
}
.search-btn {
background: #667eea;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
}
.filter-select {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
background: white;
cursor: pointer;
}
.equipment-table {
width: 100%;
border-collapse: collapse;
}
.equipment-table th,
.equipment-table td {
padding: 12px 16px;
text-align: left;
border-bottom: 1px solid #f0f0f0;
}
.equipment-table th {
background-color: #f8f9fa;
font-weight: 600;
color: #4e5969;
font-size: 14px;
}
.equipment-table tbody tr:hover {
background-color: #f8f9fa;
}
.status-tag {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.status-tag.active {
background-color: #e6f7ee;
color: #00875a;
}
.status-tag.inactive {
background-color: #f5f5f5;
color: #8c8c8c;
}
.status-tag.warning {
background-color: #fff7e6;
color: #d48806;
}
.status-tag.fault {
background-color: #ffebe6;
color: #cf1322;
}
.operation-buttons {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.operation-buttons button {
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
border: none;
transition: opacity 0.3s;
}
.operation-buttons button:hover:not(:disabled) {
opacity: 0.9;
}
.operation-buttons button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btn-view {
background-color: #e6f7ff;
color: #1890ff;
}
.btn-edit {
background-color: #faad14;
color: white;
}
.btn-delete {
background-color: #ff4d4f;
color: white;
}
.no-data {
text-align: center;
padding: 40px 0;
color: #8c8c8c;
}
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 16px;
margin-top: 24px;
color: #666;
font-size: 14px;
}
.page-btn {
padding: 4px 12px;
border: 1px solid #ddd;
background: white;
border-radius: 4px;
cursor: pointer;
}
.page-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
/* 模态框样式 */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-content {
background: white;
padding: 24px;
border-radius: 8px;
min-width: 400px;
max-width: 500px;
}
.modal-content h3 {
margin-top: 0;
margin-bottom: 20px;
}
.form-group {
margin-bottom: 16px;
}
.form-group label {
display: block;
margin-bottom: 4px;
font-weight: 500;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
.form-group textarea {
min-height: 80px;
resize: vertical;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 24px;
}
.form-actions button {
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
border: 1px solid #ddd;
}
.form-actions button[type="button"] {
background: #f5f5f5;
}
.form-actions button[type="submit"] {
background: #42b983;
color: white;
border: none;
}
/* 响应式调整 */
@media (max-width: 768px) {
.filters {
flex-direction: column;
width: 100%;
}
.search-box, .filter-select {
width: 100%;
}
.modal-content {
width: 90%;
min-width: auto;
}
}
</style>

@ -0,0 +1,572 @@
<template>
<div class="terminal-detail-page">
<!-- 页面标题和面包屑 -->
<div class="page-header">
<h2>终端详情</h2>
<div class="breadcrumb">校园矿化水平台 / 设备监控 / 终端机 / 详情</div>
</div>
<!-- 加载状态 -->
<div v-if="loading" class="loading">...</div>
<!-- 错误状态 -->
<div v-else-if="error" class="error">{{ error }}</div>
<!-- 终端详情内容 -->
<div v-else-if="terminal" class="terminal-detail-content">
<!-- 终端基本信息卡片 -->
<div class="card">
<h3>终端基本信息</h3>
<div class="detail-grid">
<div class="detail-item">
<label>终端ID:</label>
<span>{{ terminal.terminalId }}</span>
</div>
<div class="detail-item">
<label>终端名称:</label>
<span>{{ terminal.terminalName }}</span>
</div>
<div class="detail-item">
<label>经度:</label>
<span>{{ terminal.longitude }}</span>
</div>
<div class="detail-item">
<label>纬度:</label>
<span>{{ terminal.latitude }}</span>
</div>
<div class="detail-item">
<label>状态:</label>
<span :class="`status-tag ${terminal.terminalStatus}`">
{{ formatStatus(terminal.terminalStatus) }}
</span>
</div>
<div class="detail-item">
<label>安装日期:</label>
<span>{{ formatDate(terminal.installDate) }}</span>
</div>
<div class="detail-item">
<label>关联设备ID:</label>
<span>{{ terminal.deviceId || '-' }}</span>
</div>
</div>
</div>
<!-- 操作按钮 -->
<div class="action-buttons">
<button class="btn-back" @click="goBack"></button>
</div>
</div>
<!-- 无数据状态 -->
<div v-else class="no-data">未找到终端信息</div>
<!-- 编辑模态框 -->
<div v-if="showEditModal" class="modal-overlay" @click="showEditModal = false">
<div class="modal-content" @click.stop>
<h3>编辑终端</h3>
<form @submit.prevent="saveTerminal">
<div class="form-group">
<label>终端ID:</label>
<input v-model="editingTerminal.terminalId" type="text" disabled>
</div>
<div class="form-group">
<label>终端名称:</label>
<input v-model="editingTerminal.terminalName" type="text" required>
</div>
<div class="form-group">
<label>经度:</label>
<input v-model="editingTerminal.longitude" type="number" step="any" required>
</div>
<div class="form-group">
<label>纬度:</label>
<input v-model="editingTerminal.latitude" type="number" step="any" required>
</div>
<div class="form-group">
<label>状态:</label>
<select v-model="editingTerminal.terminalStatus">
<option value="active">在线</option>
<option value="inactive">离线</option>
<option value="warning">警告</option>
<option value="fault">故障</option>
</select>
</div>
<div class="form-group">
<label>安装日期:</label>
<input v-model="editingTerminal.installDate" type="date">
</div>
<div class="form-group">
<label>关联设备ID:</label>
<input v-model="editingTerminal.deviceId" type="text">
</div>
<div class="form-actions">
<button type="button" @click="showEditModal = false">取消</button>
<button type="submit">保存</button>
</div>
</form>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import { request } from '@/api/request'
import type { ResultVO } from '@/api/types/auth'
//
type TerminalStatus = 'active' | 'inactive' | 'fault' | 'warning'
// - TerminalManageVO
interface TerminalManageVO {
terminalId: string
terminalName: string
longitude: number
latitude: number
terminalStatus: TerminalStatus
installDate?: string
deviceId?: string
}
//
function isTerminalManageVO(obj: any): obj is TerminalManageVO {
return obj &&
typeof obj === 'object' &&
typeof obj.terminalId === 'string' &&
typeof obj.terminalName === 'string' &&
typeof obj.longitude === 'number' &&
typeof obj.latitude === 'number' &&
['active', 'inactive', 'warning', 'fault'].includes(obj.terminalStatus)
}
//
const route = useRoute()
const router = useRouter()
const authStore = useAuthStore()
const loading = ref(false)
const error = ref('')
const terminal = ref<TerminalManageVO | null>(null)
const showEditModal = ref(false)
const editingTerminal = ref<TerminalManageVO>({
terminalId: '',
terminalName: '',
longitude: 0,
latitude: 0,
terminalStatus: 'active'
})
// ID
const terminalId = route.params.id as string
//
const formatStatus = (status: TerminalStatus): string => {
const statusMap: Record<string, string> = {
active: '在线',
inactive: '离线',
warning: '警告',
fault: '故障'
}
return statusMap[status] || status
}
//
const formatDate = (dateString?: string): string => {
if (!dateString) return '-'
//
return dateString
}
//
const loadTerminal = async () => {
if (!terminalId) {
error.value = '缺少终端ID'
return
}
try {
loading.value = true
error.value = ''
const token = authStore.token
if (!token) {
router.push('/login')
return
}
const result = await request<ResultVO<TerminalManageVO> | TerminalManageVO>(`/api/web/terminal/${terminalId}`, {
method: 'GET'
})
console.log('终端详情请求结果:', result)
// ResultVO
if (result && typeof result === 'object' && 'code' in result) {
// ResultVO
if (result.code === 200 && result.data && isTerminalManageVO(result.data)) {
//
terminal.value = {
terminalId: result.data.terminalId,
terminalName: result.data.terminalName,
longitude: result.data.longitude,
latitude: result.data.latitude,
terminalStatus: result.data.terminalStatus,
installDate: result.data.installDate,
deviceId: result.data.deviceId
}
//
editingTerminal.value = { ...terminal.value }
} else {
error.value = result.message || '获取终端信息失败'
console.warn('API响应非成功状态或数据格式错误:', result)
}
} else if (isTerminalManageVO(result)) {
// ResultVO
terminal.value = {
terminalId: result.terminalId,
terminalName: result.terminalName,
longitude: result.longitude,
latitude: result.latitude,
terminalStatus: result.terminalStatus,
installDate: result.installDate,
deviceId: result.deviceId
}
//
editingTerminal.value = { ...terminal.value }
} else {
error.value = '获取终端信息失败'
console.warn('API响应数据格式错误:', result)
}
} catch (err) {
console.error('加载终端详情失败:', err)
error.value = '加载终端详情失败'
if ((err as Error).message.includes('401')) {
authStore.logout()
router.push('/login')
}
} finally {
loading.value = false
}
}
//
const editTerminal = () => {
if (terminal.value) {
editingTerminal.value = { ...terminal.value }
showEditModal.value = true
}
}
//
//
const saveTerminal = async () => {
try {
const token = authStore.token
if (!token) {
router.push('/login')
return
}
const result = await request<ResultVO<TerminalManageVO> | TerminalManageVO>(`/api/web/terminal/update`, {
method: 'PUT',
body: JSON.stringify(editingTerminal.value)
})
// ResultVO
if (result && typeof result === 'object' && 'code' in result) {
// ResultVO
if (result.code === 200 && result.data && isTerminalManageVO(result.data)) {
//
if (terminal.value) {
Object.assign(terminal.value, result.data)
}
showEditModal.value = false
alert('终端信息更新成功')
} else if (result.code === 200) {
// code200data
if (terminal.value) {
Object.assign(terminal.value, editingTerminal.value)
}
showEditModal.value = false
alert('终端信息更新成功')
} else {
//
const errorMessage = result.message || '更新终端信息失败'
alert(`更新终端信息失败: ${errorMessage}`)
}
}
//
else if (isTerminalManageVO(result)) {
//
if (terminal.value) {
Object.assign(terminal.value, result)
}
showEditModal.value = false
alert('终端信息更新成功')
}
else {
// ResultVO
if (terminal.value) {
Object.assign(terminal.value, editingTerminal.value)
}
showEditModal.value = false
alert('终端信息更新成功')
}
} catch (err) {
console.error('更新终端信息失败:', err)
alert('更新终端信息失败')
if ((err as Error).message.includes('401')) {
authStore.logout()
router.push('/login')
}
}
}
//
const deleteTerminal = async () => {
if (!confirm(`确定要删除终端 ${terminal.value?.terminalId} 吗?`)) {
return
}
try {
const token = authStore.token
if (!token) {
router.push('/login')
return
}
const result = await request<ResultVO<boolean>>(`/api/web/terminal/delete/${terminal.value?.terminalId}`, {
method: 'DELETE'
})
if (result.code === 200 || result.code === 201 || result.code === 204) {
alert('终端删除成功')
router.push('/home/equipment/terminal')
} else {
alert(`删除终端失败: ${result.message}`)
}
} catch (err) {
console.error('删除终端失败:', err)
alert('删除终端失败')
if ((err as Error).message.includes('401')) {
authStore.logout()
router.push('/login')
}
}
}
//
const goBack = () => {
router.push('/home/equipment/terminal')
}
//
onMounted(() => {
loadTerminal()
})
</script>
<style scoped>
.terminal-detail-page {
padding: 20px;
}
.page-header {
margin-bottom: 24px;
}
.page-header h2 {
font-size: 24px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.breadcrumb {
color: #666;
font-size: 14px;
}
.loading, .error, .no-data {
text-align: center;
padding: 40px 0;
color: #666;
}
.error {
color: #f5222d;
}
.card {
background: white;
border-radius: 8px;
padding: 24px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
margin-bottom: 24px;
}
.card h3 {
margin-top: 0;
margin-bottom: 20px;
color: #333;
font-size: 18px;
font-weight: 600;
}
.detail-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 16px;
}
.detail-item {
display: flex;
flex-direction: column;
}
.detail-item label {
font-weight: 500;
color: #666;
margin-bottom: 4px;
font-size: 14px;
}
.detail-item span {
color: #333;
font-size: 15px;
}
.status-tag {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
align-self: flex-start;
max-width: fit-content;
}
.status-tag.active {
background-color: #e6f7ee;
color: #00875a;
}
.status-tag.inactive {
background-color: #f5f5f5;
color: #8c8c8c;
}
.status-tag.warning {
background-color: #fff7e6;
color: #d48806;
}
.status-tag.fault {
background-color: #ffebe6;
color: #cf1322;
}
.action-buttons {
display: flex;
gap: 12px;
margin-top: 24px;
}
.action-buttons button {
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
border: 1px solid #ddd;
font-size: 14px;
}
.btn-edit {
background: #42b983;
color: white;
border: none;
}
.btn-delete {
background: #ff4d4f;
color: white;
border: none;
}
.btn-back {
background: #f5f5f5;
color: #666;
}
/* 模态框样式 */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-content {
background: white;
padding: 24px;
border-radius: 8px;
min-width: 400px;
max-width: 500px;
}
.modal-content h3 {
margin-top: 0;
margin-bottom: 20px;
}
.form-group {
margin-bottom: 16px;
}
.form-group label {
display: block;
margin-bottom: 4px;
font-weight: 500;
}
.form-group input,
.form-group select {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 24px;
}
.form-actions button {
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
border: 1px solid #ddd;
}
.form-actions button[type="button"] {
background: #f5f5f5;
}
.form-actions button[type="submit"] {
background: #42b983;
color: white;
border: none;
}
</style>

@ -10,6 +10,8 @@
<!-- 操作按钮区 -->
<div class="action-bar">
<button class="btn-add" @click="showAddModal = true">添加制水机</button>
<!-- 在操作按钮列中添加删除按钮 -->
<div class="filters">
<!-- 搜索框 -->
@ -99,6 +101,12 @@
设为故障
</button>
<button
class="btn-delete"
@click="deleteDevice(device.deviceId)"
>
删除
</button>
</td>
</tr>
<tr v-if="paginatedDevices.length === 0">
@ -495,6 +503,41 @@ const updateDeviceStatus = async (deviceId: string, status: string) => {
}
}
//
const deleteDevice = async (deviceId: string) => {
if (!confirm(`确定要删除设备 ${deviceId} 吗?此操作不可恢复。`)) {
return
}
try {
const token = authStore.token
if (!token) {
router.push('/login')
return
}
const result = await request<ResultVO<boolean>>(`/api/web/device/delete/${deviceId}`, {
method: 'DELETE'
})
if (result.code === 200) {
//
devices.value = devices.value.filter(d => d.deviceId !== deviceId)
alert('设备删除成功')
} else {
alert(`删除设备失败: ${result.message}`)
}
} catch (error) {
console.error('删除设备失败:', error)
alert('删除设备失败')
if ((error as Error).message.includes('401')) {
authStore.logout()
router.push('/login')
}
}
}
//
const addDevice = async () => {
try {
@ -852,5 +895,10 @@ onMounted(async () => {
width: 90%;
min-width: auto;
}
.btn-delete {
background-color: #ff4d4f;
color: white;
}
}
</style>

Loading…
Cancel
Save