Merge pull request '设备的添加' (#166) from zhanghongwei_branch into develop

pull/167/head
pc8xi2fbj 4 months ago
commit fc97d559a5

@ -7,7 +7,7 @@ module.exports = defineConfig({
devServer: { devServer: {
proxy: { proxy: {
'/api': { '/api': {
target: 'http://localhost:8080', // 后端地址 target: 'https://120.46.151.248:8081', // 后端地址
changeOrigin: true 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 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调用 // 真实的登录API调用
export const realLoginApi = async (data: LoginRequest): Promise<LoginResponse> => { 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('🌐 调用登录接口:', `${API_BASE_URL}/api/common/login`)
console.log('📤 请求数据:', data) console.log('📤 请求数据:', data)

@ -1,5 +1,5 @@
// src/api/request.ts // 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 封装 // 统一的 fetch 封装
export async function request<T>( export async function request<T>(

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

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

Loading…
Cancel
Save