设备的添加 #166

Merged
pc8xi2fbj merged 3 commits from zhanghongwei_branch into develop 1 week ago

@ -7,7 +7,7 @@ module.exports = defineConfig({
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8080', // 后端地址
target: 'https://120.46.151.248:8081', // 后端地址
changeOrigin: true
}
}

@ -1,2 +1,2 @@
VITE_API_BASE_URL=http://localhost:8080
VITE_API_BASE_URL=https://120.46.151.248:8081
VITE_APP_ORIGIN=http://localhost:5173

@ -1 +1 @@
VITE_API_BASE_URL=http://localhost:8080
VITE_API_BASE_URL=https://120.46.151.248:8081

@ -3,7 +3,7 @@ import type { LoginRequest, LoginResponse, LoginVO } from './types/auth'
// 真实的登录API调用
export const realLoginApi = async (data: LoginRequest): Promise<LoginResponse> => {
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080'
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'https://120.46.151.248:8081'
console.log('🌐 调用登录接口:', `${API_BASE_URL}/api/common/login`)
console.log('📤 请求数据:', data)

@ -1,5 +1,5 @@
// src/api/request.ts
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080'
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'https://120.46.151.248:8081'
// 统一的 fetch 封装
export async function request<T>(

@ -97,7 +97,6 @@
<button
class="btn-delete"
@click="deleteDevice(device.deviceId)"
:disabled="device.status === 'online'"
>
删除
</button>
@ -130,22 +129,22 @@
</button>
</div>
<!-- 添加设备模态框 -->
<!-- 添加/编辑设备模态框 -->
<div v-if="showAddModal" class="modal-overlay" @click="showAddModal = false">
<div class="modal-content" @click.stop>
<h3>添加制水机</h3>
<form @submit.prevent="addDevice">
<h3>{{ isEditing ? '编辑设备' : '添加制水机' }}</h3>
<form @submit.prevent="saveDevice">
<div class="form-group">
<label>设备ID:</label>
<input v-model="newDevice.deviceId" type="text" required>
<input v-model="currentDevice.deviceId" type="text" :disabled="isEditing" required>
</div>
<div class="form-group">
<label>设备名称:</label>
<input v-model="newDevice.deviceName" type="text" required>
<input v-model="currentDevice.deviceName" type="text" required>
</div>
<!-- 市区选择 -->
<div class="form-group">
<div class="form-group" v-if="!isEditing">
<label>选择市区:</label>
<select
v-model="selectedCityId"
@ -164,7 +163,7 @@
</div>
<!-- 校区选择 -->
<div class="form-group" v-if="selectedCityId">
<div class="form-group" v-if="!isEditing && selectedCityId">
<label>选择校区:</label>
<select
v-model="selectedCampusId"
@ -188,79 +187,39 @@
<div class="form-group">
<label>安装位置:</label>
<input v-model="newDevice.installLocation" type="text" required>
</div>
<div class="form-actions">
<button type="button" @click="showAddModal = false">取消</button>
<button type="submit">添加</button>
</div>
</form>
</div>
</div>
<!-- 编辑设备模态框 -->
<div v-if="showEditModal" class="modal-overlay" @click="showEditModal = false">
<div class="modal-content" @click.stop>
<h3>编辑制水机</h3>
<form @submit.prevent="updateDevice">
<div class="form-group">
<label>设备ID:</label>
<input v-model="editingDevice.deviceId" type="text" disabled>
<input v-model="currentDevice.installLocation" type="text" required>
</div>
<div class="form-group">
<label>设备名称:</label>
<input v-model="editingDevice.deviceName" type="text" required>
<label>设备类型:</label>
<select v-model="currentDevice.deviceType" required>
<option value="water_maker">制水机</option>
<option value="other">其他</option>
</select>
</div>
<!-- 市区选择 -->
<div class="form-group">
<label>选择市区:</label>
<select
v-model="selectedEditCityId"
@change="onEditCityChange"
class="select-input"
>
<option value="">请选择市区</option>
<option
v-for="city in cityList"
:key="city.areaId"
:value="city.areaId"
>
{{ city.areaName }}
</option>
<label>状态:</label>
<select v-model="currentDevice.status">
<option value="online">在线</option>
<option value="offline">离线</option>
<option value="warning">警告</option>
<option value="fault">故障</option>
</select>
</div>
<!-- 区选择 -->
<div class="form-group" v-if="selectedEditCityId">
<label>选择校:</label>
<select
v-model="selectedEditCampusId"
@change="onEditCampusChange"
class="select-input"
:disabled="!editCampusList.length"
<!-- 片区选择 -->
<div class="form-group">
<label>所属片区:</label>
<input
v-model="currentDevice.areaId"
type="text"
:disabled="true"
placeholder="选择校区后自动填充"
>
<option value="">请选择校区</option>
<option
v-for="campus in editCampusList"
:key="campus.areaId"
:value="campus.areaId"
>
{{ campus.areaName }}
</option>
</select>
<p v-if="selectedEditCityId && !editCampusList.length" class="no-data-message">
该市区暂无校区
</p>
</div>
<div class="form-group">
<label>安装位置:</label>
<input v-model="editingDevice.installLocation" type="text" required>
</div>
<div class="form-actions">
<button type="button" @click="showEditModal = false">取消</button>
<button type="submit">更新</button>
<button type="button" @click="showAddModal = false">取消</button>
<button type="submit">{{ isEditing ? '更新' : '添加' }}</button>
</div>
</form>
</div>
@ -306,6 +265,7 @@
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
@ -313,18 +273,28 @@ import { useAuthStore } from '@/stores/auth'
import { request } from '@/api/request'
import type { ResultVO } from '@/api/types/auth'
//
const isDeviceManageVO = (obj: any): obj is DeviceManageVO => {
return obj &&
typeof obj === 'object' &&
typeof obj.deviceId === 'string' &&
typeof obj.deviceName === 'string' &&
typeof obj.installLocation === 'string' &&
typeof obj.deviceType === 'string' &&
['online', 'offline', 'warning', 'fault'].includes(obj.status)
}
//
type DeviceStatus = 'online' | 'offline' | 'warning' | 'fault'
//
interface WaterMakerDevice {
// - DeviceManageVO
interface DeviceManageVO {
deviceId: string
deviceName: string
deviceType: string
areaId: string
installLocation: string
deviceType: string
status: DeviceStatus
// lastHeartbeatTime
areaId?: string
}
//
@ -339,7 +309,7 @@ interface Area {
}
//
const devices = ref<WaterMakerDevice[]>([])
const devices = ref<DeviceManageVO[]>([])
const searchKeyword = ref('')
const selectedCity = ref('') //
const selectedCampus = ref('') //
@ -351,30 +321,21 @@ const authStore = useAuthStore() // 初始化auth store
//
const showAddModal = ref(false)
const showEditModal = ref(false)
const isEditing = ref(false)
const showOfflineReasonModal = ref(false)
const showFaultModal = ref(false)
// ID
const currentDeviceId = ref('')
//
const newDevice = ref({
//
const currentDevice = ref<DeviceManageVO>({
deviceId: '',
deviceName: '',
areaId: '',
installLocation: '',
deviceType: 'water_maker'
})
//
const editingDevice = ref<WaterMakerDevice>({
deviceId: '',
deviceName: '',
deviceType: '',
areaId: '',
installLocation: '',
status: 'online'
deviceType: 'water_maker',
status: 'online',
areaId: undefined
})
//
@ -383,11 +344,6 @@ const campusList = ref<Area[]>([]) // 校区列表
const selectedCityId = ref('') // ID
const selectedCampusId = ref('') // ID
//
const editCampusList = ref<Area[]>([]) //
const selectedEditCityId = ref('') // ID
const selectedEditCampusId = ref('') // ID
const offlineReason = ref('')
const faultInfo = ref({
faultType: '',
@ -424,7 +380,7 @@ const loadDevices = async (): Promise<void> => {
const url = `/api/web/device-status/by-type${queryString ? `?${queryString}` : ''}`
//
const result = await request<ResultVO<WaterMakerDevice[]>>(
const result = await request<ResultVO<DeviceManageVO[]>>(
url,
{ method: 'GET' }
)
@ -440,7 +396,6 @@ const loadDevices = async (): Promise<void> => {
areaId: item.areaId,
installLocation: item.installLocation,
status: item.status
// lastHeartbeatTime
}))
}
@ -534,94 +489,24 @@ const loadCampusListByCity = async (cityId: string): Promise<void> => {
//
const onCityChange = async () => {
//
//
selectedCampus.value = ''
currentDevice.value.areaId = undefined
campusList.value = []
if (selectedCity.value) {
await loadCampusListByCity(selectedCity.value)
} else {
//
campusList.value = []
if (selectedCityId.value) {
await loadCampusListByCity(selectedCityId.value)
}
}
// ID
const loadEditCampusListByCity = async (cityId: string): Promise<void> => {
try {
const token = authStore.token
if (!token) {
console.warn('未获取到 Token跳转到登录页')
await router.push('/login')
return
}
console.log(`开始加载市区 ${cityId} 的校区列表...`)
const result = await request<any>(`/api/web/area/campuses/${cityId}`, { method: 'GET' })
if (result && typeof result === 'object' && 'code' in result) {
if (result.code === 200 && result.data && Array.isArray(result.data)) {
editCampusList.value = result.data
console.log(`获取到${editCampusList.value.length}个校区`)
} else {
console.warn('API响应非成功状态或数据格式错误:', result)
editCampusList.value = []
}
} else if (Array.isArray(result)) {
editCampusList.value = result
} else {
console.warn('API响应数据格式错误:', result)
editCampusList.value = []
}
} catch (error) {
console.error('加载校区列表失败:', error)
editCampusList.value = []
if ((error as Error).message.includes('401')) {
authStore.logout()
await router.push('/login')
}
}
}
//
//
const onCampusChange = () => {
// areaIdareaNameareaId
// areaIdareaName
const selectedCampus = campusList.value.find(campus => campus.areaId === selectedCampusId.value)
if (selectedCampus) {
newDevice.value.areaId = selectedCampus.areaName // 使areaNameareaId
currentDevice.value.areaId = selectedCampus.areaName // 使areaNameareaId
} else {
newDevice.value.areaId = ''
}
}
//
const onEditCityChange = async () => {
//
selectedEditCampusId.value = ''
editCampusList.value = []
if (selectedEditCityId.value) {
await loadEditCampusListByCity(selectedEditCityId.value)
//
const matchingCampus = editCampusList.value.find(campus =>
campus.areaName === editingDevice.value.areaId
)
if (matchingCampus) {
selectedEditCampusId.value = matchingCampus.areaId
}
}
}
//
const onEditCampusChange = () => {
// areaIdareaNameareaId
const selectedCampus = editCampusList.value.find(campus => campus.areaId === selectedEditCampusId.value)
if (selectedCampus) {
editingDevice.value.areaId = selectedCampus.areaName // 使areaNameareaId
} else {
editingDevice.value.areaId = ''
currentDevice.value.areaId = undefined
}
}
@ -792,167 +677,162 @@ const deleteDevice = async (deviceId: string) => {
}
//
const openEditModal = (device: WaterMakerDevice) => {
//
editingDevice.value = { ...device }
//
selectedEditCityId.value = ''
selectedEditCampusId.value = ''
editCampusList.value = []
//
findCurrentAreaInCityList(device.areaId)
showEditModal.value = true
}
//
const findCurrentAreaInCityList = (areaId: string) => {
for (const city of cityList.value) {
loadEditCampusListByCity(city.areaId).then(() => {
const matchingCampus = editCampusList.value.find(campus => campus.areaName === areaId)
if (matchingCampus) {
selectedEditCityId.value = city.areaId
selectedEditCampusId.value = matchingCampus.areaId
return
}
})
const openEditModal = (device: DeviceManageVO) => {
currentDevice.value = { ...device }
isEditing.value = true
showAddModal.value = true
//
if (cityList.value.length === 0) {
loadCityList()
}
}
//
const updateDevice = async () => {
//
const saveDevice = async () => {
try {
// token
const token = authStore.token
if (!token) {
console.warn('未获取到 Token跳转到登录页')
await router.push('/login')
return
}
//
if (!selectedEditCampusId.value) {
//
if (!isEditing.value && !selectedCampusId.value) {
alert('请选择校区')
return
}
// areaId
if (!editingDevice.value.areaId) {
if (!currentDevice.value.areaId) {
alert('请先选择校区以确定所属片区')
return
}
//
const deviceToUpdate = {
deviceId: editingDevice.value.deviceId,
deviceName: editingDevice.value.deviceName,
areaId: editingDevice.value.areaId,
installLocation: editingDevice.value.installLocation,
deviceType: editingDevice.value.deviceType
let result: ResultVO<DeviceManageVO> | DeviceManageVO
if (isEditing.value) {
//
result = await request<ResultVO<DeviceManageVO> | DeviceManageVO>('/api/web/device/edit', {
method: 'PUT',
body: JSON.stringify(currentDevice.value)
})
} else {
//
//
if (!selectedCampusId.value) {
alert('请选择校区')
return
}
//
if (!currentDevice.value.areaId) {
alert('请选择校区以确定所属片区')
return
}
result = await request<ResultVO<DeviceManageVO> | DeviceManageVO>('/api/web/device/add', {
method: 'POST',
body: JSON.stringify(currentDevice.value)
})
}
const result = await request<ResultVO>('/api/web/device/edit', {
method: 'PUT',
body: JSON.stringify(deviceToUpdate)
})
// ResultVO
if (result && typeof result === 'object' && 'code' in result) {
// ResultVO
if (result.code === 200 && result.data && isDeviceManageVO(result.data)) {
//
await loadDevices()
//
showAddModal.value = false
isEditing.value = false
currentDevice.value = {
deviceId: '',
deviceName: '',
installLocation: '',
deviceType: 'water_maker',
status: 'online',
areaId: undefined
}
selectedCityId.value = ''
selectedCampusId.value = ''
campusList.value = []
if (result.code === 200) {
//
const index = devices.value.findIndex(d => d.deviceId === editingDevice.value.deviceId)
if (index !== -1) {
devices.value[index] = { ...editingDevice.value }
alert(isEditing.value ? '设备更新成功' : '设备添加成功')
} else if (result.code === 200) {
// code200data
//
await loadDevices()
//
showAddModal.value = false
isEditing.value = false
currentDevice.value = {
deviceId: '',
deviceName: '',
installLocation: '',
deviceType: 'water_maker',
status: 'online',
areaId: undefined
}
selectedCityId.value = ''
selectedCampusId.value = ''
campusList.value = []
alert(isEditing.value ? '设备更新成功' : '设备添加成功')
} else {
alert(`${isEditing.value ? '更新' : '添加'}设备失败: ${result.message}`)
}
}
//
else if (isDeviceManageVO(result)) {
//
await loadDevices()
//
showEditModal.value = false
editingDevice.value = {
showAddModal.value = false
isEditing.value = false
currentDevice.value = {
deviceId: '',
deviceName: '',
deviceType: '',
areaId: '',
installLocation: '',
status: 'online'
deviceType: 'water_maker',
status: 'online',
areaId: undefined
}
selectedEditCityId.value = ''
selectedEditCampusId.value = ''
editCampusList.value = []
alert('设备更新成功')
} else {
alert(`设备更新失败: ${result.message}`)
}
} catch (error) {
console.error('更新设备失败:', error)
alert('更新设备失败')
if ((error as Error).message.includes('401')) {
authStore.logout()
await router.push('/login')
}
}
}
//
const addDevice = async () => {
try {
// token
const token = authStore.token
if (!token) {
console.warn('未获取到 Token跳转到登录页')
await router.push('/login')
return
}
//
if (!selectedCampusId.value) {
alert('请选择校区')
return
}
// areaId
if (!newDevice.value.areaId) {
alert('请先选择校区以确定所属片区')
return
}
selectedCityId.value = ''
selectedCampusId.value = ''
campusList.value = []
//
const deviceToAdd = {
deviceId: newDevice.value.deviceId,
deviceName: newDevice.value.deviceName,
areaId: newDevice.value.areaId,
installLocation: newDevice.value.installLocation,
deviceType: newDevice.value.deviceType
alert(isEditing.value ? '设备更新成功' : '设备添加成功')
}
// 使token
const result = await request<ResultVO>('/api/web/device/add', {
method: 'POST',
body: JSON.stringify(deviceToAdd)
})
if (result.code === 200) {
else {
//
//
await loadDevices()
//
showAddModal.value = false
newDevice.value = {
isEditing.value = false
currentDevice.value = {
deviceId: '',
deviceName: '',
areaId: '',
installLocation: '',
deviceType: 'water_maker'
deviceType: 'water_maker',
status: 'online',
areaId: undefined
}
selectedCityId.value = ''
selectedCampusId.value = ''
campusList.value = []
alert('设备添加成功')
} else {
alert(`设备添加失败: ${result.message}`)
alert(isEditing.value ? '设备更新成功' : '设备添加成功')
}
} catch (error) {
console.error('添加设备失败:', error)
alert('添加设备失败')
console.error(`${isEditing.value ? '更新' : '添加'}设备失败:`, error)
alert(`${isEditing.value ? '更新' : '添加'}设备失败`)
if ((error as Error).message.includes('401')) {
authStore.logout()
await router.push('/login')
@ -968,6 +848,7 @@ onMounted(async () => {
})
</script>
<style scoped>
/* 样式与终端机页面保持一致 */
.water-maker-page {

@ -15,7 +15,7 @@ export default defineConfig({
proxy: {
// 代理所有以 /api 开头的请求到后端
'/api': {
target: 'http://localhost:8080', // Spring Boot 后端地址
target: 'https://120.46.151.248:8081', // Spring Boot 后端地址
changeOrigin: true, // 改变请求来源
secure: false, // 如果是https可能需要设置为false
// 如果需要重写路径,可以取消下面的注释

Loading…
Cancel
Save