pull/112/head
ZHW 2 months ago
parent 3f1745885b
commit dbcb4755f9

@ -72,7 +72,7 @@ public class AreaController {
@RequestBody @Parameter(description = "区域信息areaId为必填") Area area
) {
try {
if (area.getAreaId() == null || area.getAreaId().trim().isEmpty()) {
if (area.getAreaId() == null) {
return ResponseEntity.ok(ResultVO.error(400, "区域ID不能为空"));
}
Area updatedArea = areaService.updateArea(area);

@ -193,7 +193,7 @@ public class WorkOrderServiceImpl implements WorkOrderService {
@Component
@RequiredArgsConstructor
@Slf4j
public class WorkOrderTimeoutTask {
public static class WorkOrderTimeoutTask {
private final WorkOrderRepository workOrderRepository;

@ -1,4 +1,4 @@
<!-- src/views/area/CampusManagement.vue -->
<!-- src/views/area/Campus.vue -->
<template>
<div class="campus-page">
<!-- 页面标题和面包屑 -->
@ -11,48 +11,49 @@
<div class="action-bar">
<!-- 新增校区按钮 -->
<button class="btn-add" @click="handleAddCampus"></button>
<!-- 校区下拉筛选 -->
<div class="filter-box">
<label>选择校区</label>
<select v-model="selectedCampus" @change="handleCampusChange" class="campus-select">
<option value="">全部校区</option>
<option v-for="campus in campusList" :key="campus.id" :value="campus.id">
{{ campus.name }}
</option>
</select>
</div>
<!-- 导航到片区管理按钮 -->
<button class="btn-nav" @click="goToZoneManagement"></button>
</div>
<!-- 校区表格新增所属市区列 -->
<!-- 校区表格 -->
<div class="card">
<table class="campus-table">
<thead>
<tr>
<th>校区</th>
<th>所属市区</th> <!-- 新增列 -->
<th>设备数量</th>
<th>范围</th>
<th>校区名称</th>
<th>地址</th>
<th>负责人</th>
<th>联系电话</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="campus in filteredCampus" :key="campus.id">
<td>{{ campus.name }}</td>
<td>{{ getAreaName(campus.areaId) }}</td> <!-- 显示所属市区名称 -->
<td>{{ campus.deviceCount }}</td>
<td>{{ campus.range }}</td>
<tr v-for="campus in filteredCampus" :key="campus.areaId">
<td>{{ campus.areaName }}</td>
<td>{{ campus.address }}</td>
<td>{{ campus.manager }}</td>
<td>{{ campus.managerPhone }}</td>
<td>{{ formatDate(campus.createdTime) }}</td>
<td class="operation-buttons">
<button
class="btn-delete"
@click="handleDelete(campus.id)"
<button
class="btn-edit"
@click="handleEdit(campus)"
>
编辑
</button>
<button
class="btn-delete"
@click="handleDelete(campus.areaId, campus.areaName)"
>
删除
</button>
</td>
</tr>
<tr v-if="filteredCampus.length === 0">
<td colspan="5" class="no-data">暂无校区数据</td> <!-- 列数调整为5 -->
<td colspan="6" class="no-data">
{{ loading ? '正在加载数据...' : '暂无校区数据' }}
</td>
</tr>
</tbody>
</table>
@ -60,9 +61,9 @@
<!-- 分页控件 -->
<div class="pagination">
<button
class="page-btn"
:disabled="currentPage === 1"
<button
class="page-btn"
:disabled="currentPage === 1 || loading"
@click="currentPage--"
>
上一页
@ -70,16 +71,16 @@
<span class="page-info">
{{ currentPage }} / {{ totalPages }}
</span>
<button
class="page-btn"
:disabled="currentPage === totalPages"
<button
class="page-btn"
:disabled="currentPage === totalPages || loading"
@click="currentPage++"
>
下一页
</button>
</div>
<!-- 新增/编辑校区弹窗新增所属市区选择 -->
<!-- 新增/编辑校区弹窗 -->
<div class="modal-mask" v-if="showModal">
<div class="modal-container">
<div class="modal-header">
@ -90,48 +91,45 @@
<form @submit.prevent="handleSave">
<div class="form-item">
<label>校区名称</label>
<input
type="text"
v-model="formData.name"
<input
type="text"
v-model="formData.areaName"
placeholder="请输入校区名称"
required
>
</div>
<div class="form-item">
<label>所属市区</label> <!-- 新增表单项 -->
<select
v-model="formData.areaId"
class="area-select"
<label>地址</label>
<input
type="text"
v-model="formData.address"
placeholder="请输入校区地址"
required
>
<option value="">请选择所属市区</option>
<option v-for="area in areaList" :key="area.id" :value="area.id">
{{ area.name }}
</option>
</select>
</div>
<div class="form-item">
<label>校区范围</label>
<textarea
v-model="formData.range"
placeholder="请输入校区范围描述"
rows="3"
<label>负责人</label>
<input
type="text"
v-model="formData.manager"
placeholder="请输入负责人姓名"
required
></textarea>
>
</div>
<div class="form-item">
<label>设备数量</label>
<input
type="number"
v-model="formData.deviceCount"
placeholder="请输入设备数量"
min="0"
<label>联系电话</label>
<input
type="text"
v-model="formData.managerPhone"
placeholder="请输入负责人联系电话"
required
>
</div>
<div class="form-actions">
<button type="button" class="btn-cancel" @click="showModal = false">取消</button>
<button type="submit" class="btn-submit">保存</button>
<button type="submit" class="btn-submit" :disabled="saving">
{{ saving ? '保存中...' : '保存' }}
</button>
</div>
</form>
</div>
@ -149,7 +147,9 @@
<p>确定要删除 "{{ deleteCampusName }}" 校区吗此操作不可撤销</p>
<div class="form-actions">
<button type="button" class="btn-cancel" @click="showDeleteConfirm = false">取消</button>
<button type="button" class="btn-delete-confirm" @click="confirmDelete"></button>
<button type="button" class="btn-delete-confirm" @click="confirmDelete" :disabled="deleting">
{{ deleting ? '删除中...' : '确认删除' }}
</button>
</div>
</div>
</div>
@ -158,67 +158,30 @@
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { request } from '@/api/request' //
import { useAuthStore } from '@/stores/auth' // authStore
//
//
const router = useRouter()
const authStore = useAuthStore()
// Area
interface Area {
id: string
name: string
deviceCount: number
range: string
}
// areaId
interface Campus {
id: string
name: string
areaId: string // ID
deviceCount: number
range: string
}
//
const areaList = ref<Area[]>([
{
id: '1',
name: '市区东区',
deviceCount: 28,
range: '东至东风路,西至解放路,南至人民路,北至建设路'
},
{
id: '2',
name: '市区西区',
deviceCount: 19,
range: '东至解放路,西至滨河路,南至青年路,北至黄河路'
}
])
//
const campusList = ref<Campus[]>([
{
id: '1',
name: '主校区',
areaId: '1', //
deviceCount: 89,
range: '包含教学区、生活区、运动区,覆盖整个主校区范围'
},
{
id: '2',
name: '分校区',
areaId: '2', // 西
deviceCount: 45,
range: '包含新教学楼、实训楼、学生公寓1-5号楼'
},
{
id: '3',
name: '南校区',
areaId: '1', //
deviceCount: 67,
range: '包含研究生院、国际交流中心、留学生公寓'
}
])
areaId: string
areaName: string
areaType: 'campus' | 'building' | 'zone'
parentAreaId: string | null
address: string
manager: string
managerPhone: string
createdTime?: Date
updatedTime?: Date
}
//
const campusList = ref<Area[]>([])
const selectedCampus = ref('')
const currentPage = ref(1)
const pageSize = ref(10)
@ -227,31 +190,39 @@ const isEdit = ref(false)
const showDeleteConfirm = ref(false)
const deleteCampusId = ref('')
const deleteCampusName = ref('')
const loading = ref(false) //
const saving = ref(false) //
const deleting = ref(false) //
// areaId
const formData = ref<Campus>({
id: '',
name: '',
//
const formData = ref<Area>({
areaId: '',
deviceCount: 0,
range: ''
areaName: '',
areaType: 'campus',
parentAreaId: null,
address: '',
manager: '',
managerPhone: '',
createdTime: undefined,
updatedTime: undefined
})
// ID
const getAreaName = (areaId: string) => {
const area = areaList.value.find(item => item.id === areaId)
return area ? area.name : '未指定'
//
const formatDate = (date: Date | undefined) => {
if (!date) return '-'
const d = new Date(date)
return d.toLocaleDateString('zh-CN')
}
//
const filteredCampus = computed(() => {
let filtered = [...campusList.value]
//
if (selectedCampus.value) {
filtered = filtered.filter(campus => campus.id === selectedCampus.value)
filtered = filtered.filter(campus => campus.areaId === selectedCampus.value)
}
//
const startIndex = (currentPage.value - 1) * pageSize.value
const endIndex = startIndex + pageSize.value
@ -260,15 +231,49 @@ const filteredCampus = computed(() => {
//
const totalPages = computed(() => {
const filteredCount = selectedCampus.value
? campusList.value.filter(campus => campus.id === selectedCampus.value).length
const filteredCount = selectedCampus.value
? campusList.value.filter(campus => campus.areaId === selectedCampus.value).length
: campusList.value.length
return Math.ceil(filteredCount / pageSize.value)
})
//
const handleCampusChange = () => {
currentPage.value = 1 //
//
const fetchCampusList = async () => {
loading.value = true
try {
const token = authStore.token
if (!token) {
console.warn('未获取到 Token跳转到登录页')
router.push('/login')
return
}
const response = await request<{
code: number
msg: string
data: Area[]
}>('/api/web/area/list-campus', {
method: 'GET',
})
if (response.code === 200) {
campusList.value = response.data
} else {
const errorMsg = response.msg || `获取失败(错误码:${response.code}`
console.error('获取校区列表失败:', errorMsg)
alert(`获取校区列表失败:${errorMsg}`)
}
} catch (error: any) {
console.error('请求异常:', error)
const errorMsg = error.message.includes('401') || error.message.includes('403')
? '权限不足或登录已过期,请重新登录'
: error.message.includes('Network')
? '网络连接失败,请检查网络'
: error.message || '获取数据失败,请稍后重试'
alert(`获取校区列表失败:${errorMsg}`)
} finally {
loading.value = false
}
}
//
@ -276,57 +281,160 @@ const handleAddCampus = () => {
isEdit.value = false
//
formData.value = {
id: '',
name: '',
areaId: '',
deviceCount: 0,
range: ''
areaName: '',
areaType: 'campus',
parentAreaId: null,
address: '',
manager: '',
managerPhone: '',
createdTime: undefined,
updatedTime: undefined
}
showModal.value = true
}
//
const handleEdit = (campus: Area) => {
isEdit.value = true
formData.value = { ...campus }
showModal.value = true
}
//
const handleDelete = (id: string) => {
const campus = campusList.value.find(item => item.id === id)
if (campus) {
deleteCampusId.value = id
deleteCampusName.value = campus.name
showDeleteConfirm.value = true
}
const handleDelete = (id: string, name: string) => {
deleteCampusId.value = id
deleteCampusName.value = name
showDeleteConfirm.value = true
}
//
const confirmDelete = () => {
campusList.value = campusList.value.filter(campus => campus.id !== deleteCampusId.value)
showDeleteConfirm.value = false
//
if (selectedCampus.value === deleteCampusId.value) {
selectedCampus.value = ''
const confirmDelete = async () => {
deleting.value = true
try {
const token = authStore.token
if (!token) {
console.warn('未获取到 Token跳转到登录页')
router.push('/login')
return
}
const response = await request<{
code: number
msg: string
}>(`/api/web/area/delete/${deleteCampusId.value}`, {
method: 'DELETE',
})
if (response.code === 200) {
fetchCampusList() //
showDeleteConfirm.value = false
} else {
const errorMsg = response.msg || `删除失败(错误码:${response.code}`
console.error('删除校区失败:', errorMsg)
alert(`删除校区失败:${errorMsg}`)
}
} catch (error: any) {
console.error('删除请求异常:', error)
const errorMsg = error.message.includes('401') || error.message.includes('403')
? '权限不足或登录已过期,请重新登录'
: error.message.includes('Network')
? '网络连接失败,请检查网络'
: error.message || '删除失败,请稍后重试'
alert(`删除校区失败:${errorMsg}`)
} finally {
deleting.value = false
}
}
//
const handleSave = () => {
if (isEdit.value) {
//
const index = campusList.value.findIndex(item => item.id === formData.value.id)
if (index !== -1) {
campusList.value[index] = { ...formData.value }
const handleSave = async () => {
saving.value = true
try {
const token = authStore.token
if (!token) {
console.warn('未获取到 Token跳转到登录页')
router.push('/login')
return
}
} else {
//
const newCampus: Campus = {
...formData.value,
id: Date.now().toString() // ID
let response
let result
if (isEdit.value) {
//
response = await request<{
code: number
msg: string
data: Area
}>('/api/web/area/update', {
method: 'PUT',
body: JSON.stringify(formData.value)
})
if (response.code === 200) {
fetchCampusList() //
showModal.value = false
} else {
const errorMsg = response.msg || `更新失败(错误码:${response.code}`
console.error('更新校区失败:', errorMsg)
alert(`更新校区失败:${errorMsg}`)
}
} else {
//
const newCampus = {
areaName: formData.value.areaName,
areaType: 'campus' as const,
address: formData.value.address,
manager: formData.value.manager,
managerPhone: formData.value.managerPhone
}
response = await request<{
code: number
msg: string
data: Area
}>('/api/web/area/add', {
method: 'POST',
body: JSON.stringify(newCampus)
})
if (response.code === 200) {
fetchCampusList() //
showModal.value = false
} else {
const errorMsg = response.msg || `新增失败(错误码:${response.code}`
console.error('新增校区失败:', errorMsg)
alert(`新增校区失败:${errorMsg}`)
}
}
campusList.value.unshift(newCampus)
} catch (error: any) {
console.error('保存请求异常:', error)
const errorMsg = error.message.includes('401') || error.message.includes('403')
? '权限不足或登录已过期,请重新登录'
: error.message.includes('Network')
? '网络连接失败,请检查网络'
: error.message || '保存失败,请稍后重试'
alert(`保存校区失败:${errorMsg}`)
} finally {
saving.value = false
}
showModal.value = false
}
//
const goToZoneManagement = () => {
router.push('/area/zone')
}
//
onMounted(() => {
console.log('Token:', authStore.token)
fetchCampusList()
})
</script>
<style scoped>
/* 基础样式 - 与市区管理页面完全一致 */
/* 基础样式 */
.campus-page {
padding: 20px;
}
@ -371,19 +479,20 @@ const handleSave = () => {
background: #359e75;
}
.filter-box {
display: flex;
align-items: center;
gap: 8px;
color: #666;
}
.campus-select, .area-select { /* 新增area-select样式 */
padding: 8px 12px;
border: 1px solid #ddd;
/* 新增导航按钮样式 */
.btn-nav {
background: #1890ff;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
min-width: 200px;
cursor: pointer;
font-size: 14px;
transition: background 0.3s;
}
.btn-nav:hover {
background: #096dd9;
}
/* 表格样式 */
@ -415,6 +524,21 @@ const handleSave = () => {
gap: 8px;
}
.btn-edit {
background-color: #e6f4ff;
color: #1890ff;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
border: none;
transition: opacity 0.3s;
}
.btn-edit:hover {
opacity: 0.9;
}
.btn-delete {
background-color: #ffebe6;
color: #cf1322;
@ -534,7 +658,7 @@ const handleSave = () => {
.form-item input,
.form-item textarea,
.form-item select { /* 新增select样式 */
.form-item select {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
@ -570,6 +694,11 @@ const handleSave = () => {
font-size: 14px;
}
.btn-submit:disabled {
background: #a0d911;
cursor: not-allowed;
}
.btn-delete-confirm {
background: #cf1322;
color: white;
@ -580,19 +709,24 @@ const handleSave = () => {
font-size: 14px;
}
.btn-delete-confirm:disabled {
background: #ff7875;
cursor: not-allowed;
}
/* 响应式调整 */
@media (max-width: 768px) {
.action-bar {
flex-direction: column;
align-items: flex-start;
}
.filter-box {
width: 100%;
}
.campus-select, .area-select {
width: 100%;
}
}
</style>
</style>

@ -145,8 +145,8 @@
<div class="form-group">
<label>所属片区:</label>
<select v-model="newDevice.areaId" required>
<option value="市区">市区</option>
<option value="校区">校区</option>
<option value="A区">A</option>
<option value="B区">B</option>
</select>
</div>
<div class="form-group">

@ -287,7 +287,7 @@ const fetchAdminList = async () => {
admins.value = (response.data || []).map((admin: any) => ({
adminId: admin.adminId || '',
name: admin.adminName || '未知姓名', //
account: admin.account || '',
account: admin.adminId || '',
phone: admin.phone || '未知电话',
role: admin.role || '未知角色',
status: 'active' as AdminStatus

@ -26,8 +26,10 @@
<div class="filter-item">
<select v-model="searchFilters.areaId" @change="handleSearch">
<option value="">全部区域</option>
<option value="市区">市区</option>
<option value="校区">校区</option>
<option value="A">A区</option>
<option value="B">B区</option>
<option value="C">C区</option>
<option value="D">D区</option>
</select>
</div>

Loading…
Cancel
Save