提交 80a23c4d authored 作者: zhuyongshuai's avatar zhuyongshuai

灭火2025/8/19

上级 7701ae0c
# # 页面标题 # # # 页面标题
VITE_APP_TITLE = '卫星仿真系统平台' # VITE_APP_TITLE = '卫星仿真系统平台'
# # 开发环境配置 # # # 开发环境配置
# VITE_APP_ENV = 'development' # # VITE_APP_ENV = 'development'
# # 若依管理系统/开发环境 # # # 若依管理系统/开发环境
# VITE_API_URL = '/dev-api' # # VITE_API_URL = '/dev-api'
# NODE_ENV="development" # # NODE_ENV="development"
# VITE_API_URL = http://211.149.190.82:18080 VITE_API_URL = http://211.149.190.82:18080
# ENV = development # 本地环境 # # ENV = development # 本地环境
# VITE_API_URL = http://localhost:8888/ # 本地环境接口地址 # # VITE_API_URL = http://localhost:8888/ # 本地环境接口地址
# 本地环境 # # 本地环境
MODE_ENV = development # MODE_ENV = development
# 本地环境接口地址 # # 本地环境接口地址
VITE_API_URL = /api VITE_API_URL = /api
\ No newline at end of file
...@@ -21,7 +21,9 @@ ...@@ -21,7 +21,9 @@
"qs": "^6.13.1", "qs": "^6.13.1",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-router": "^4.5.0", "vue-router": "^4.5.0",
"vuedraggable": "4.1.0" "vuedraggable": "4.1.0",
"vxe-pc-ui": "^4.8.20",
"vxe-table": "^4.15.9"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^5.2.1", "@vitejs/plugin-vue": "^5.2.1",
...@@ -1151,6 +1153,18 @@ ...@@ -1151,6 +1153,18 @@
} }
} }
}, },
"node_modules/@vxe-ui/core": {
"version": "4.2.11",
"resolved": "https://registry.npmmirror.com/@vxe-ui/core/-/core-4.2.11.tgz",
"integrity": "sha512-Cv0XKTGgFD+CD2MGixxM+k60Y3tFEJYErQj59tuPd0y0HuSFc7sQk0fuXdbcNgxZxtAi4zX3nsDk3kY+/HeAgQ==",
"dependencies": {
"dom-zindex": "^1.0.6",
"xe-utils": "^3.7.8"
},
"peerDependencies": {
"vue": "^3.2.0"
}
},
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.14.1", "version": "8.14.1",
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.14.1.tgz", "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.14.1.tgz",
...@@ -2043,6 +2057,11 @@ ...@@ -2043,6 +2057,11 @@
"url": "https://github.com/fb55/entities?sponsor=1" "url": "https://github.com/fb55/entities?sponsor=1"
} }
}, },
"node_modules/dom-zindex": {
"version": "1.0.6",
"resolved": "https://registry.npmmirror.com/dom-zindex/-/dom-zindex-1.0.6.tgz",
"integrity": "sha512-FKWIhiU96bi3xpP9ewRMgANsoVmMUBnMnmpCT6dPMZOunVYJQmJhSRruoI0XSPoHeIif3kyEuiHbFrOJwEJaEA=="
},
"node_modules/domelementtype": { "node_modules/domelementtype": {
"version": "1.3.1", "version": "1.3.1",
"resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-1.3.1.tgz", "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-1.3.1.tgz",
...@@ -6114,6 +6133,22 @@ ...@@ -6114,6 +6133,22 @@
"vue": "^3.0.1" "vue": "^3.0.1"
} }
}, },
"node_modules/vxe-pc-ui": {
"version": "4.8.20",
"resolved": "https://registry.npmmirror.com/vxe-pc-ui/-/vxe-pc-ui-4.8.20.tgz",
"integrity": "sha512-//Ixq6Q1iBPZua4ia8/DjF7ZoWG2rb10mr2PuQHTJHsbI3I0mXhq6PyouMnMehjfFBhSyLMl9nrQbtIh/lFFeg==",
"dependencies": {
"@vxe-ui/core": "^4.2.11"
}
},
"node_modules/vxe-table": {
"version": "4.15.9",
"resolved": "https://registry.npmmirror.com/vxe-table/-/vxe-table-4.15.9.tgz",
"integrity": "sha512-zWFyW3Oi4bXQ5J2oiaaa+lXDbo4EBzX2EyjQKagBQYS4zaL8V5mzV4fxG4xWiW8V86pojbubSIVaiSKSaMj4tg==",
"dependencies": {
"vxe-pc-ui": "^4.8.0"
}
},
"node_modules/warning": { "node_modules/warning": {
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmmirror.com/warning/-/warning-4.0.3.tgz", "resolved": "https://registry.npmmirror.com/warning/-/warning-4.0.3.tgz",
...@@ -6219,6 +6254,11 @@ ...@@ -6219,6 +6254,11 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/xe-utils": {
"version": "3.7.8",
"resolved": "https://registry.npmmirror.com/xe-utils/-/xe-utils-3.7.8.tgz",
"integrity": "sha512-V/k6B/ASYir6yLYhp62DnM17po9u1N9mou/rn4if5WoFCsAO49JpCiVpkDpwCv4zxGfWmhWgzmz4FytWF+pDVw=="
},
"node_modules/zrender": { "node_modules/zrender": {
"version": "5.6.0", "version": "5.6.0",
"resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.0.tgz", "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.0.tgz",
......
...@@ -25,7 +25,9 @@ ...@@ -25,7 +25,9 @@
"qs": "^6.13.1", "qs": "^6.13.1",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-router": "^4.5.0", "vue-router": "^4.5.0",
"vuedraggable": "4.1.0" "vuedraggable": "4.1.0",
"vxe-pc-ui": "~4.8.20",
"vxe-table": "~4.15.9"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^5.2.1", "@vitejs/plugin-vue": "^5.2.1",
......
//建筑管理 //建筑管理
import qs from 'qs' import qs from 'qs'
import request from '@/utils/request' import request from '@/utils/request'
// 人员添加接口
export function staffAddApi(data) { /**
* 建筑添加接口
* @param {Object} data - 请求数据
* @returns {Promise} 返回请求结果
*/
export function buildingAddApi(data) {
return request({ return request({
url: '/user/add', url: '/build/add',
method: 'post', method: 'post',
data, data,
}) })
} }
// 人员修改
export function staffEditApi(data) { /**
* 获取建筑信息接口
* @param {Object} data - 请求数据
* @returns {Promise} 返回请求结果
*/
export function buildingGetInfoApi(data) {
return request({ return request({
url: '/user/update', url: '/build/getInfo',
method: 'post', method: 'post',
data, data,
}) })
} }
// 详情
export function staffDetailApi(data) {
return request({ /**
url: `/user/getInfo`, * 建筑分页查询接口
method: 'get', * @param {Object} data - 请求数据
params: data * @returns {Promise} 返回请求结果
}) */
} export function buildingPageApi(data) {
//分页
export function staffListApi(data) {
return request({ return request({
url: '/user/page', url: '/build/page',
method: 'post', method: 'post',
data, data,
}) })
} }
// 删除
export function staffDelApi(data) { /**
* 删除建筑接口
* @param {Object} data - 请求数据
* @returns {Promise} 返回请求结果
*/
export function buildingRemoveApi(data) {
return request({ return request({
url: `/user/remove`, url: '/build/remove',
method: 'get', method: 'post',
params: data data,
headers: { // 请求头
'Content-Type': 'multipart/form-data'
}
}) })
} }
export function staffAerialViewApi(data) { /**
* 更新建筑信息接口
* @param {Object} data - 请求数据
* @returns {Promise} 返回请求结果
*/
export function buildingUpdateApi(data) {
return request({ return request({
url: `/project/list`, url: '/build/update',
method: 'get', method: 'post',
params: data data,
}) })
} }
\ No newline at end of file
import qs from 'qs'
import request from '@/utils/request'
export function floorAddInfoAPI(data) {
return request({
url: '/build/floor/add',
method: 'post',
data,
})
}
export function floorGetDownFloorSortAPI(data) {
return request({
url: '/build/floor/getDownFloorSort',
method: 'GET',
data,
})
}
export function floorGetInfoAPI(data) {
return request({
url: '/build/floor/getInfo',
method: 'GET',
data,
})
}
export function floorGetUpFloorSortAPI(data) {
return request({
url: '/build/floor/getUpFloorSort',
method: 'GET',
data,
})
}
export function floorPageAPI(data) {
return request({
url: '/build/floor/page',
method: 'POST',
data,
})
}
export function floorRemoveAPI(data) {
return request({
url: '/build/floor/remove',
method: 'POST',
data,
headers: { // 请求头
'Content-Type': 'multipart/form-data'
}
})
}
export function floorUpdateAPI(data) {
return request({
url: '/build/floor/update',
method: 'POST',
data,
})
}
\ No newline at end of file
import qs from 'qs'
import request from '@/utils/request'
// 新增天线组
export function addcommonAntennaGroup(data) {
return request({
url: '/simu/commonAntenna',
method: 'post',
data,
})
}
// 修改
export function upcommonAntennaGroup(data) {
return request({
url: '/simu/commonAntenna',
method: 'put',
data,
})
}
// 详情
export function getantenna(data) {
return request({
url: `/simu/commonAntenna/${data.id}`,
method: 'get',
})
}
// 列表
export function getlist(data) {
return request({
url: `/simu/commonAntenna/list?${qs.stringify(data)}`,
method: 'get',
})
}
// 删除
export function deleantenna(data) {
return request({
url: `/simu/commonAntenna/${data.id}`,
method: 'delete',
})
}
\ No newline at end of file
//建筑管理
import axios from 'axios'
import request from '../../utils/request'
import qs from 'qs' import qs from 'qs'
import request from '@/utils/request'
export default { // 图片上传
// 保留请求取消 export function staffAvatorApi(data) {
cancels: {}, return request({
// 话务模型---------------------------------------------------------------------------------------------------------------- url: '/common/image/upload',
// 话务列表 method: 'post',
simu_voiceslist(params) { headers: { // 请求头
this.cancels['simu_voiceslist']?.() 'Content-Type': 'multipart/form-data;'
return request.get(
`/simu/voice/list?${qs.stringify(params)}`,
params,
{
cancelToken: new axios.CancelToken(c => {
this.cancels['simu_voiceslist'] = c
})
}
)
},
//新增话务
simu_voicesAdd(params) {
this.cancels['simu_voicesAdd']?.()
return request.post(
`/simu/voice`,
params,
{
cancelToken: new axios.CancelToken(c => {
this.cancels['simu_voicesAdd'] = c
})
}
)
},
//修改话务
simu_voicesput(params) {
this.cancels['simu_voicesput']?.()
return request.put(
`/simu/voice`,
params,
{
cancelToken: new axios.CancelToken(c => {
this.cancels['simu_voicesput'] = c
})
}
)
},
//删除话务
simu_voicesRemo(data) {
this.cancels['simu_voicesRemo']?.()
return request.delete(
`/simu/voice/${data.ids}`,
{
cancelToken: new axios.CancelToken(c => {
this.cancels['simu_voicesRemo'] = c
})
}
)
},
//话务详细信息
simu_voicesId(params) {
this.cancels['simu_voicesId']?.()
return request.get(
`/simu/voice/${params.id}`,
params,
{
cancelToken: new axios.CancelToken(c => {
this.cancels['simu_voicesId'] = c
})
}
)
},
// 话务模型结束----------------------------------------------------------------------------------------------------------------
// 业务模型----------------------------------------------------------------------------------------------------------------
// 业务列表
simu_businesslist(params) {
this.cancels['simu_businesslist']?.()
return request.get(
`/simu/business/list?${qs.stringify(params)}`,
params,
{
cancelToken: new axios.CancelToken(c => {
this.cancels['simu_businesslist'] = c
})
}
)
},
//新增业务
simu_businessAdd(params) {
this.cancels['simu_businessAdd']?.()
return request.post(
`/simu/business`,
params,
{
cancelToken: new axios.CancelToken(c => {
this.cancels['simu_businessAdd'] = c
})
}
)
},
//修改业务
simu_businessput(params) {
this.cancels['simu_businessput']?.()
return request.put(
`/simu/business`,
params,
{
cancelToken: new axios.CancelToken(c => {
this.cancels['simu_businessput'] = c
})
}
)
}, },
//删除业务 data,
simu_businessRemo(data) {
this.cancels['simu_businessRemo']?.()
return request.delete(
`/simu/business/${data.ids}`,
{
cancelToken: new axios.CancelToken(c => {
this.cancels['simu_businessRemo'] = c
}) })
}
)
},
//业务详细信息
simu_businessId(params) {
this.cancels['simu_businessId']?.()
return request.get(
`/simu/business/${params.id}`,
params,
{
cancelToken: new axios.CancelToken(c => {
this.cancels['simu_businessId'] = c
})
}
)
},
// 业务模型结束------------------------------------------------------------------------------------------------------------- ----
//接口调节-------------------------------------------------------------------------------------------------------------
simu_rdFileInsList(params) {
this.cancels['simu_rdFileInsList']?.()
return request.get(
`/simu/rdFileIns/list?${qs.stringify(params)}`,
params,
{
cancelToken: new axios.CancelToken(c => {
this.cancels['simu_rdFileInsList'] = c
})
}
)
},
} }
//人员管理 //人员管理
import qs from 'qs' import qs from 'qs'
import request from '@/utils/request' import request from '@/utils/request'
import { he } from 'element-plus/es/locales.mjs'
import { helper } from 'echarts'
// 人员添加接口 // 人员添加接口
export function staffAddApi(data) { export function staffAddApi(data) {
return request({ return request({
...@@ -78,7 +80,9 @@ export function staffAddInfoApi(data) { ...@@ -78,7 +80,9 @@ export function staffAddInfoApi(data) {
url: `/user/add`, // 请求的URL路径 url: `/user/add`, // 请求的URL路径
method: 'POST', // 请求方法为POST method: 'POST', // 请求方法为POST
data, // 请求携带的数据 data, // 请求携带的数据
}) })
} }
export function staffGetInfoApi(data) { export function staffGetInfoApi(data) {
return request({ // 调用request方法发送HTTP请求 return request({ // 调用request方法发送HTTP请求
...@@ -93,12 +97,27 @@ export function staffRemoveInfoApi(data) { ...@@ -93,12 +97,27 @@ export function staffRemoveInfoApi(data) {
url: `/user/remove`, // 请求的URL路径 url: `/user/remove`, // 请求的URL路径
method: 'POST', // 请求方法为POST method: 'POST', // 请求方法为POST
data, // 请求携带的数据 data, // 请求携带的数据
headers: { // 请求头
'Content-Type': 'multipart/form-data;'
}
}) })
} }
export function staffResetPasswordApi(data) { export function staffResetPasswordApi(data) {
return request({ // 调用request方法发送HTTP请求 return request({ // 调用request方法发送HTTP请求
url: `/user/resetPassword`, // 请求的URL路径 url: `/user/resetPassword`, // 请求的URL路径
method: 'POST', // 请求方法为POST method: 'POST', // 请求方法为POST
data, // 请求携带的数据 data
})
}
export function staffSetAbleApi(data) {
return request({ // 调用request方法发送HTTP请求
url: `/user/enable`, // 请求的URL路径
method: 'POST', // 请求方法为POST
data,
headers: { // 请求头
'Content-Type': 'multipart/form-data'
}
}) })
} }
\ No newline at end of file
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<div class="tip2">国际花园小区</div> <div class="tip2">国际花园小区</div>
<div class="avatar-container" v-if="useAppStore.isCurUserLogin"> <div class="avatar-container" v-if="useAppStore.isCurUserLogin">
<el-dropdown> <el-dropdown>
<el-avatar shape="square" size="large" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"/> <el-avatar shape="square" size="large" :src="useAppStoreInstance.userInfo.avatar?useAppStoreInstance.userInfo.avatar: useAppStoreInstance.showavatarUrl"/>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item @click="toSatffManage">人员管理</el-dropdown-item> <el-dropdown-item @click="toSatffManage">人员管理</el-dropdown-item>
...@@ -56,7 +56,8 @@ ...@@ -56,7 +56,8 @@
<div class="breadcrumb-container"> <div class="breadcrumb-container">
<el-breadcrumb separator=">"> <el-breadcrumb separator=">">
<el-breadcrumb-item v-for="(crumb, index) in breadcrumbs" :key="index" <el-breadcrumb-item v-for="(crumb, index) in breadcrumbs" :key="index"
@click="handleBreadcrumbClick(index)">
>
{{ crumb.name }} {{ crumb.name }}
</el-breadcrumb-item> </el-breadcrumb-item>
</el-breadcrumb> </el-breadcrumb>
...@@ -96,28 +97,26 @@ ...@@ -96,28 +97,26 @@
</template> </template>
<script setup> <script setup>
import { onMounted, ref, watch } from 'vue'; import { nextTick, onMounted, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import useAppStore from '../store/module/app'; import useAppStore from '../store/module/app';
import { getToken, removeToken } from '../utils/auth'; import { getToken, removeToken } from '../utils/auth';
import { pa } from 'element-plus/es/locales.mjs';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const useAppStoreInstance = useAppStore(); const useAppStoreInstance = useAppStore();
// 1.(根据菜单结构) // 菜单结构
const menuList = ref([ const menuList = ref([
{ {
name: '仪表盘', name: '仪表盘',
id: 'dashboard', id: 'dashboard',
path: '/analySisPage', path: '/analySisPage',
icon: new URL('../static/image/huo.png', import.meta.url).href, icon: new URL('../static/image/huo.png', import.meta.url).href,
children:[ children: [
{ name: '分析页', id: 'analysis', path: '/analysisPage' }, { name: '分析页', id: 'analysis', path: '/analysisPage' },
{ name: '工作台', id: 'workbench', path: '/workBenchPage'}, { name: '工作台', id: 'workbench', path: '/workBenchPage' },
] ]
}, },
{ {
...@@ -127,7 +126,6 @@ const menuList = ref([ ...@@ -127,7 +126,6 @@ const menuList = ref([
icon: new URL('../static/image/huo.png', import.meta.url).href, icon: new URL('../static/image/huo.png', import.meta.url).href,
children: [ children: [
{ name: '用户管理', id: 'user', path: '/usermanage' }, { name: '用户管理', id: 'user', path: '/usermanage' },
] ]
}, },
{ {
...@@ -137,7 +135,6 @@ const menuList = ref([ ...@@ -137,7 +135,6 @@ const menuList = ref([
icon: new URL('../static/image/huo.png', import.meta.url).href, icon: new URL('../static/image/huo.png', import.meta.url).href,
children: [ children: [
{ name: '区域建筑管理', id: 'areaBuilding', path: '/areabuildmanage' }, { name: '区域建筑管理', id: 'areaBuilding', path: '/areabuildmanage' },
] ]
}, },
{ {
...@@ -147,30 +144,29 @@ const menuList = ref([ ...@@ -147,30 +144,29 @@ const menuList = ref([
icon: new URL('../static/image/huo.png', import.meta.url).href, icon: new URL('../static/image/huo.png', import.meta.url).href,
children: [ children: [
{ name: '无人机调度', id: 'droneDispatch', path: '/uavdispatch' }, { name: '无人机调度', id: 'droneDispatch', path: '/uavdispatch' },
{ name: '无人机监控', id: 'droneMonitor', path: '/drone/monitor' } { name: '无人机监控', id: 'droneMonitor', path: '/uavdetaildate' }
] ]
}, },
{ {
name: '火情管理', name: '火情管理',
id: 'fire', id: 'fire',
path: '/fireSurveillance', path: '/firesurveillance',
icon: new URL('../static/image/huo.png', import.meta.url).href, icon: new URL('../static/image/huo.png', import.meta.url).href,
children: [ children: [
{ name: '当前火情', id: 'fireMonitor', path: '/firesurveillance' },
{ name: '历史火情', id: 'fireHistory', path: '/historyfire' } { name: '当前火情', id: 'fireHistory', path: '/historyfire' },
{ name: '历史火情', id: 'fireMonitor', path: '/firesurveillance' },
] ]
}, },
]); ]);
const openMenus = ref([]); const openMenus = ref([]);
const breadcrumbs = ref([]); const breadcrumbs = ref([]);
const tabs = ref([]); const tabs = ref([]);
const activeTabKey = ref(''); const activeTabKey = ref('');
const lastValidMenuPath = ref('/historyfire'); // 记录最后一个有效的菜单路径
// -------------------------- // 获取菜单项名称
// 2. 工具函数
// --------------------------
const getMenuNameByPath = (path) => { const getMenuNameByPath = (path) => {
for (const item of router.options.routes[4].children) { for (const item of router.options.routes[4].children) {
if (item.path === path) { if (item.path === path) {
...@@ -196,40 +192,25 @@ const getMenuNameByPath = (path) => { ...@@ -196,40 +192,25 @@ const getMenuNameByPath = (path) => {
return '未知页面'; return '未知页面';
}; };
// 检查路径是否在菜单中
// 3. 核心函数 const isPathInMenu = (path) => {
const addTab = () => { for (const item of menuList.value) {
const path = route.path; if (item.path === path) return true;
const existingTabIndex = tabs.value.findIndex(tab => tab.key === path); if (item.children && item.children.some(c => c.path === path)) return true;
const preTabs = ref(tabs.value);
console.log("existingTabIndexpath----------------12121----------",preTabs,"----------",path,'----------',tabs.value)
if (existingTabIndex === -1) {
// console.log("existingTabIndexpath--------------------------",tabs.value)
tabs.value.push({
key: path,
label: getMenuNameByPath(path),
path: path
});
}else {
tabs.value=preTabs.value;
} }
return false;
activeTabKey.value = path;
console.log("existingTabIndexpath---------56555555555555555555-----------------",activeTabKey.value)
}; };
const saveMenuState = () => { // 获取当前有效路径
const menuState = { const getCurrentValidPath = () => {
openMenus: openMenus.value return isPathInMenu(route.path) ? route.path : lastValidMenuPath.value;
};
localStorage.setItem('menuState', JSON.stringify(menuState));
}; };
// 更新菜单激活状态
const updateMenuActiveState = () => { const updateMenuActiveState = () => {
useAppStoreInstance.num = route.path; const currentPath = getCurrentValidPath();
useAppStoreInstance.num = currentPath;
const currentPath = route.path;
menuList.value.forEach((item, index) => { menuList.value.forEach((item, index) => {
if (item.children && item.children.length) { if (item.children && item.children.length) {
const hasActiveChild = item.children.some(child => child.path === currentPath); const hasActiveChild = item.children.some(child => child.path === currentPath);
...@@ -238,102 +219,98 @@ const updateMenuActiveState = () => { ...@@ -238,102 +219,98 @@ const updateMenuActiveState = () => {
} }
} }
}); });
};
if (isPathInMenu(route.path)) {
lastValidMenuPath.value = route.path;
}
};
// 检查路径是否存在 // 检查菜单项是否激活
function isPathInMenu(path) { const isActive = (item) => {
// 扁平化所有菜单项 const currentPath = getCurrentValidPath();
const flattenMenu = (menus) => { if (currentPath === item.path) return true;
return menus.reduce((acc, item) => { if (item.children && item.children.length) {
acc.push(item.path); return item.children.some(child => child.path === currentPath);
if (item.children) {
acc.push(...flattenMenu(item.children));
} }
return acc; return false;
}, []); };
};
const allPaths = flattenMenu(menuList.value); // 检查子菜单项是否激活
return allPaths.includes(path); const isSubMenuActive = (child) => {
} const currentPath = getCurrentValidPath();
return currentPath === child.path;
};
// 更新面包屑导航
const updateBreadcrumbs = () => { const updateBreadcrumbs = () => {
const path = route.path; const path = getCurrentValidPath();
const crumbs = []; const crumbs = [{ name: '首页', path: '/' }];
crumbs.push({ name: '首页', path: '/' }); for (const item of menuList.value) {
let preCrumbsDate = breadcrumbs.value; if (item.path === path) {
// if (path === '/') { crumbs.push({ name: item.name, path: item.path });
break;
// } }
if (isPathInMenu(path)) { if (item.children && item.children.length) {
menuList.value.forEach(mainItem => { const child = item.children.find(c => c.path === path);
if (mainItem.path === path) { if (child) {
crumbs.push({ name: mainItem.name, path: mainItem.path }); crumbs.push({ name: item.name, path: item.path });
} else if (mainItem.children && mainItem.children.length) { crumbs.push({ name: child.name, path: child.path });
const matchedChild = mainItem.children.find(child => child.path === path); break;
if (matchedChild) {
crumbs.push({ name: mainItem.name, path: mainItem.path });
crumbs.push({ name: matchedChild.name, path: matchedChild.path });
} }
} }
});
breadcrumbs.value = crumbs;
}else {
// 匹配不到时,返回首页
breadcrumbs.value = preCrumbsDate
console.log('匹配不到时,返回首页',breadcrumbs.value.length)
// useAppStoreInstance.num =
} }
breadcrumbs.value = crumbs;
}; };
const initTabs = () => { // 添加标签页
const initialTab = { const addTab = () => {
key: route.path, const path = route.path;
label: getMenuNameByPath(route.path), const existingTabIndex = tabs.value.findIndex(tab => tab.key === path);
path: route.path
};
console.log("初始化标签页",initialTab);
tabs.value = [initialTab];
activeTabKey.value = route.path;
};
// -------------------------- const preTabs = ref(tabs.value);
// 4. 生命周期钩子 console.log("existingTabIndexpath----------------12121----------",preTabs,"----------",path,'----------',tabs.value)
// -------------------------- if (existingTabIndex === -1) {
onMounted(() => { // console.log("existingTabIndexpath--------------------------",tabs.value)
menuList.value.forEach((item, index) => { tabs.value.push({
if (item.children && item.children.length) { key: path,
openMenus.value[index] = false; label: getMenuNameByPath(path),
path: path
});
}else {
tabs.value=preTabs.value;
} }
const savedState = localStorage.getItem('menuState'); activeTabKey.value = path;
if (savedState) { console.log("existingTabIndexpath---------56555555555555555555-----------------",activeTabKey.value)
const parsedState = JSON.parse(savedState); };
openMenus.value = parsedState.openMenus || openMenus.value;
}
});
initTabs(); // 初始化标签页
updateBreadcrumbs(); const initTabs = () => {
updateMenuActiveState(); const initialPath = isPathInMenu(route.path) ? route.path : lastValidMenuPath.value;
}); tabs.value = [{
key: initialPath,
label: getMenuNameByPath(initialPath),
path: initialPath
}];
activeTabKey.value = initialPath;
};
watch(route, () => { // 保存菜单状态
updateMenuActiveState(); const saveMenuState = () => {
updateBreadcrumbs(); console.log('保存菜单状态',router.options.history.state.back)
addTab(); if (router.options.history.state.back !== null) {
saveMenuState(); const menuState = {
}, { immediate: true }); openMenus: openMenus.value,
lastValidMenuPath: isPathInMenu(router.options.history.state.back)? router.options.history.state.back:(localStorage.getItem('lastValidMenuPath')),
};
localStorage.setItem('menuState', JSON.stringify(menuState));}
};
// -------------------------- // 移除标签页
// 5. 其他辅助函数
// --------------------------
const removeTab = (key) => { const removeTab = (key) => {
const index = tabs.value.findIndex(tab => tab.key === key); const index = tabs.value.findIndex(tab => tab.key === key);
if (index === -1) return; if (index === -1) return;
if (key === activeTabKey.value) { if (key === activeTabKey.value) {
...@@ -346,47 +323,28 @@ const removeTab = (key) => { ...@@ -346,47 +323,28 @@ const removeTab = (key) => {
tabs.value.splice(index, 1); tabs.value.splice(index, 1);
}; };
// 标签页点击处理
const handleTabClick = (tab) => { const handleTabClick = (tab) => {
const tabItem = tabs.value.find(item => item.key === tab.name); const tabItem = tabs.value.find(item => item.key === tab.paneName);
console.log(tab,tab.props.name,"-------999999999999999999-------------"); if (tabItem) {
router.push(tab.props.name); router.push(tabItem.path);
}
}; };
// 切换子菜单展开状态
const toggleSubMenu = (index) => { const toggleSubMenu = (index) => {
openMenus.value[index] = !openMenus.value[index]; openMenus.value[index] = !openMenus.value[index];
saveMenuState(); saveMenuState();
}; };
// 子菜单项点击处理
const handleSubMenuClick = (child) => { const handleSubMenuClick = (child) => {
useAppStoreInstance.num = child.path; useAppStoreInstance.num = child.path;
localStorage.setItem('menu', child.path); localStorage.setItem('menu', child.path);
router.push(child.path); router.push(child.path);
}; };
const menuclick = (item) => { // 面包屑点击处理
if (!item.children || !item.children.length) {
useAppStoreInstance.num = item.id;
localStorage.setItem('menu', item.id);
router.push(item.path);
}
};
const isActive = (item) => {
if (route.path === item.path) return true;
if (item.children && item.children.length) {
return item.children.some(child => child.path === route.path);
}
return false;
};
const isSubMenuActive = (child) => {
return route.path === child.path;
};
const handleBreadcrumbClick = (index) => { const handleBreadcrumbClick = (index) => {
if (index < breadcrumbs.value.length - 1) { if (index < breadcrumbs.value.length - 1) {
const crumb = breadcrumbs.value[index]; const crumb = breadcrumbs.value[index];
...@@ -394,16 +352,75 @@ const handleBreadcrumbClick = (index) => { ...@@ -394,16 +352,75 @@ const handleBreadcrumbClick = (index) => {
} }
}; };
// 跳转到人员管理
const toSatffManage = () => { const toSatffManage = () => {
router.push({ path: '/staff' }); router.push({ path: '/staff' });
useAppStoreInstance.num = '/staff'; useAppStoreInstance.num = '/staff';
}; };
const noremoveItemFn = (data) => {
const valueToKeep = localStorage.getItem(data);
console.log("sdsadadddddd",valueToKeep)
localStorage.clear();
if (valueToKeep !== null) {
localStorage.setItem(data, valueToKeep);
}
};
// 用户退出
const curUserLogout = () => { const curUserLogout = () => {
removeToken(); removeToken();
router.push('/login'); router.push('/login');
useAppStoreInstance.isCurUserLogin = false; useAppStoreInstance.isCurUserLogin = false;
noremoveItemFn('rememberedAccount');
}; };
const showavatarUrl = ref('https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png');
// 初始化
onMounted(() => {
nextTick (()=>{
useAppStoreInstance.initavatarUrlFn()
})
// 初始化菜单展开状态
menuList.value.forEach((_, index) => {
openMenus.value[index] = false;
});
// 从本地存储恢复状态
const savedState = localStorage.getItem('menuState');
if (savedState) {
try {
const parsedState = JSON.parse(savedState);
openMenus.value = parsedState.openMenus || openMenus.value;
lastValidMenuPath.value = parsedState.lastValidMenuPath || '/historyfire';
} catch (e) {
console.error('Failed to parse menu state', e);
}
}
// console.log("currentDisplayPath.value--------------------------",isPathInMenu(route.path))
if (!isPathInMenu(route.path)) {
console.log("currentDisplayPath.value--------------------------",isPathInMenu(route.path),getCurrentValidPath(),JSON.parse(localStorage.getItem('menuState')).lastValidMenuPath)
router.replace(JSON.parse(localStorage.getItem('menuState')).lastValidMenuPath);
}
// console.log("currentDisplayPath.value--------------------------",isPathInMenu(route.path),getCurrentValidPath())
initTabs();
updateBreadcrumbs();
updateMenuActiveState();
});
// 监听路由变化
watch(route, () => {
useAppStoreInstance.initavatarUrlFn()
updateMenuActiveState();
updateBreadcrumbs();
addTab();
saveMenuState();
}, { immediate: true });
</script> </script>
......
...@@ -21,6 +21,10 @@ import store from './store/index.js' ...@@ -21,6 +21,10 @@ import store from './store/index.js'
import router from './router/index.js' import router from './router/index.js'
import SvgIcon from '@/components/SvgIcon/index.vue' // import SvgIcon from '@/components/SvgIcon/index.vue' //
import elementIcons from '@/components/SvgIcon/svgicon.js' import elementIcons from '@/components/SvgIcon/svgicon.js'
import VxeUIAll from 'vxe-pc-ui'
import 'vxe-pc-ui/es/style.css'
import VxeUITable from 'vxe-table'
import 'vxe-table/es/style.css'
import './permission' // 路由拦截和加载页面进度条 import './permission' // 路由拦截和加载页面进度条
// 全局配置组件 // 全局配置组件
...@@ -40,7 +44,7 @@ const app = createApp(App) ...@@ -40,7 +44,7 @@ const app = createApp(App)
const pinia = createPinia(); const pinia = createPinia();
// 全局方法挂载 // 全局方法挂载
app.config.globalProperties.useDict = useDict app.config.globalProperties.useDict = useDict
localStorage.setItem('lastValidMenuPath','/home')
// 全局组件挂载 // 全局组件挂载
...@@ -65,6 +69,6 @@ app.use(ElementPlus, { ...@@ -65,6 +69,6 @@ app.use(ElementPlus, {
size: Cookies.get('size') || 'default' size: Cookies.get('size') || 'default'
}) })
app.use(VxeUIAll).use(VxeUITable)
app.mount('#app') app.mount('#app')
import router from './router' import router from './router'
import { ElMessage } from 'element-plus'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css' import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth' import { getToken } from '@/utils/auth'
import useAppStore from './store/module/app' import useAppStore from './store/module/app'
...@@ -41,8 +40,8 @@ router.beforeEach((to, from, next) => { ...@@ -41,8 +40,8 @@ router.beforeEach((to, from, next) => {
} else { } else {
localStorage.clear()//清空所有本地储存 localStorage.clear()//清空所有本地储存
// 重定向到首页 // 重定向到首页
// next(`/login`) // 否则全部重定向到登录页 next(`/login`) // 否则全部重定向到登录页
next() // next()
} }
} }
......
...@@ -57,6 +57,7 @@ export const constantRoutes = [ ...@@ -57,6 +57,7 @@ export const constantRoutes = [
component: () => import('@/views/error/401.vue'), component: () => import('@/views/error/401.vue'),
hidden: true hidden: true
}, },
{ {
path: '', path: '',
component: Layout, component: Layout,
...@@ -135,25 +136,44 @@ export const constantRoutes = [ ...@@ -135,25 +136,44 @@ export const constantRoutes = [
path: '/firesurveillance', path: '/firesurveillance',
component: () => import('../views/areafiremanage/firesurveillance/index.vue'), component: () => import('../views/areafiremanage/firesurveillance/index.vue'),
name: 'fireSurveillance', name: 'fireSurveillance',
meta: { title: '当前火情', icon: 'dashboard', affix: true } meta: { title: '历史火情', icon: 'dashboard', affix: true }
}, },
{ {
path: '/historyfire', path: '/historyfire',
component: () => import('../views/areafiremanage/historyfire/index.vue'), component: () => import('../views/areafiremanage/historyfire/index.vue'),
name: 'historyFire', name: 'historyFire',
meta: { title: '历史火情', icon: 'dashboard', affix: true }, meta: { title: '当前火情', icon: 'dashboard', affix: true },
children:[
},
{ {
path: '/historyfire/floordetaildate', path: '/floordetaildate',
component: () => import('../views/areafiremanage/historyfire/components/floordetail.vue'), component: () => import('../views/areafiremanage/historyfire/components/floordetail.vue'),
name: 'floorDetailDate', name: 'floorDetailDate',
meta: { title: '详细楼层火情', icon: 'dashboard', affix: true } meta: { title: '详细楼层火情', icon: 'dashboard', affix: true }
}, },
] {
path: '/buildingdetaildate',
component: () => import('../views/areabuildmanage/components/showdetaildate.vue'),
name: 'buildingDetailDate',
meta: { title: '楼详细建筑数据', icon: 'dashboard', affix: true }
}, },
{
path: '/onefloorbuildingdetaildate',
component: () => import('../views/areabuildmanage/onefloor/index.vue'),
name: 'oneFloorBuildingDetailDate',
meta: { title: '层详细建筑数据', icon: 'dashboard', affix: true }
},
{
path: '/uavdetaildate',
component: () => import('../views/uavshowdate/uavmanage/index.vue'),
name: 'uavDetailDate',
meta: { title: '无人机管理', icon: 'dashboard', affix: true }
},
] ]
}, },
] ]
const router = createRouter({ const router = createRouter({
......
import Cookies from 'js-cookie' import Cookies from 'js-cookie'
import { ElMessage } from 'element-plus';
import { tr } from 'element-plus/es/locales.mjs';
const useAppStore = defineStore( const useAppStore = defineStore(
'app', 'app',
{ {
...@@ -12,7 +13,14 @@ const useAppStore = defineStore( ...@@ -12,7 +13,14 @@ const useAppStore = defineStore(
device: 'desktop', device: 'desktop',
size: Cookies.get('size') || 'default', size: Cookies.get('size') || 'default',
num:1, num:1,
isCurUserLogin: false isCurUserLogin: false,
userInfo: {},
showavatarUrl :'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
position:{
10:'超级管理员',
1:'巡检人员',
0:"管理员"
}
}), }),
actions: { actions: {
toggleSideBar(withoutAnimation) { toggleSideBar(withoutAnimation) {
...@@ -43,7 +51,32 @@ const useAppStore = defineStore( ...@@ -43,7 +51,32 @@ const useAppStore = defineStore(
this.sidebar.hide = status this.sidebar.hide = status
}, },
initavatarUrlFn () {
try {
if (!(this.userInfo)) {
this.userInfo = JSON.parse(localStorage.getItem('user'))
console.log('initavatarUrlFn-----我是空------------',this.userInfo)
}else{
(this.userInfo).avatarUrl = (this.userInfo).avatarUrl ? (this.userInfo).avatarUrl : 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png';
console.log('initavatarUrlFn-----我不是空------------',this.userInfo)
}
} catch (error) {
ElMessage.error('avatarUrl is not valid',error);
}
},
chackCurPersionOpition() {
try {
// console.log(this.userInfo,'********************************************');
return this.userInfo.status === 0 ? true : false;
} catch (error) {
ElMessage.error('this.userInfo.state is not valid');
}
}
} }
}) })
......
...@@ -22,9 +22,8 @@ const service = axios.create({ ...@@ -22,9 +22,8 @@ const service = axios.create({
// request拦截器 // request拦截器
service.interceptors.request.use( service.interceptors.request.use(config => {
config => { console.log('--------------config', config)
console.log('--------------config', config.url)
// 是否需要设置 token // 是否需要设置 token
const isToken = (config.headers || {}).isToken === false const isToken = (config.headers || {}).isToken === false
console.log('--------------isToken', isToken) console.log('--------------isToken', isToken)
...@@ -42,6 +41,7 @@ service.interceptors.request.use( ...@@ -42,6 +41,7 @@ service.interceptors.request.use(
config.url = url; config.url = url;
} }
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) { if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
const requestObj = { const requestObj = {
url: config.url, url: config.url,
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data, data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
......
...@@ -19,9 +19,9 @@ ...@@ -19,9 +19,9 @@
status-icon status-icon
> >
<!-- 建筑编号 --> <!-- 建筑编号 -->
<el-form-item label="建筑编号" prop="buildingNo" class="form-item"> <el-form-item label="建筑编号" prop="buildCode" class="form-item">
<el-input <el-input
v-model="form.buildingNo" v-model="form.buildCode"
placeholder="请输入建筑编号" placeholder="请输入建筑编号"
class="input-field" class="input-field"
clearable clearable
...@@ -29,9 +29,9 @@ ...@@ -29,9 +29,9 @@
</el-form-item> </el-form-item>
<!-- 建筑名称 --> <!-- 建筑名称 -->
<el-form-item label="建筑名称" prop="buildingName" class="form-item"> <el-form-item label="建筑名称" prop="buildName" class="form-item">
<el-input <el-input
v-model="form.buildingName" v-model="form.buildName"
placeholder="请输入建筑名称" placeholder="请输入建筑名称"
class="input-field" class="input-field"
clearable clearable
...@@ -39,9 +39,9 @@ ...@@ -39,9 +39,9 @@
</el-form-item> </el-form-item>
<!-- 建筑层数 --> <!-- 建筑层数 -->
<el-form-item label="建筑层数(地上)" prop="floorCount" class="form-item"> <el-form-item label="建筑层数(地上)" prop="floorNum" class="form-item">
<el-input <el-input
v-model="form.floorCount" v-model="form.floorNum"
placeholder="请输入地上层数" placeholder="请输入地上层数"
type="number" type="number"
min="0" min="0"
...@@ -61,13 +61,13 @@ ...@@ -61,13 +61,13 @@
<!-- 地下室层数 --> <!-- 地下室层数 -->
<el-form-item <el-form-item
label="地下室层数" label="地下室层数"
prop="basementCount" prop="undergroundFloorNum"
class="form-item" class="form-item"
:rules="form.hasBasement === 1 ? rules.basementCount : []" :rules="form.hasBasement === 1 ? rules.basementCount : []"
> >
<el-input <el-input
v-model="form.basementCount" v-model="form.undergroundFloorNum"
placeholder="请输入地下室层数" :placeholder ="`${form.hasBasement === 1 ? '请输入地下室层数' : '无地下室'}`"
type="number" type="number"
min="0" min="0"
class="input-field" class="input-field"
...@@ -110,35 +110,35 @@ ...@@ -110,35 +110,35 @@
import { ref, reactive } from 'vue'; import { ref, reactive } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
import { Close } from '@element-plus/icons-vue'; import { Close } from '@element-plus/icons-vue';
import { buildingAddApi } from '../../../api/build';
// 表单引用 // 表单引用
const addBuildingFormRef = ref(null); const addBuildingFormRef = ref(null);
// 提交加载状态 // 提交加载状态
const submitting = ref(false); const submitting = ref(false);
const emit = defineEmits(['closeAddDrawer']); const emit = defineEmits(['closeAddDrawer','handleRefreshInfoFn']);
// 表单数据 // 表单数据
const form = reactive({ const form = reactive({
buildingNo: '', // 建筑编号 buildCode: '', // 建筑编号
buildingName: '', // 建筑名称 buildName: '', // 建筑名称
floorCount: null, // 地上层数 floorNum: null, // 地上层数
hasBasement: 1, // 是否含有地下室 (1:是, 2:否) hasBasement: 1, // 是否含有地下室 (1:是, 2:否)
basementCount: null, // 地下室层数 undergroundFloorNum: null, // 地下室层数
description: '' // 建筑功能描述 description: '' // 建筑功能描述
}); });
// 表单验证规则 // 表单验证规则
const rules = reactive({ const rules = reactive({
buildingNo: [ buildCode: [
{ required: true, message: '请输入建筑编号', trigger: ['blur', 'change'] }, { required: true, message: '请输入建筑编号', trigger: ['blur', 'change'] },
{ min: 2, max: 20, message: '编号长度在2-20个字符之间', trigger: ['blur', 'change'] } { min: 2, max: 20, message: '编号长度在2-20个字符之间', trigger: ['blur', 'change'] }
], ],
buildingName: [ buildName: [
{ required: true, message: '请输入建筑名称', trigger: ['blur', 'change'] }, { required: true, message: '请输入建筑名称', trigger: ['blur', 'change'] },
{ message: '名称长度在2-50个字符之间', trigger: ['blur', 'change'] } { message: '名称长度在2-50个字符之间', trigger: ['blur', 'change'] }
], ],
floorCount: [ floorNum: [
{ required: true, message: '请输入地上层数', trigger: ['blur', 'change'] }, { required: true, message: '请输入地上层数', trigger: ['blur', 'change'] },
{ {
validator: (rule, value, callback) => { validator: (rule, value, callback) => {
...@@ -154,52 +154,39 @@ const rules = reactive({ ...@@ -154,52 +154,39 @@ const rules = reactive({
}, },
trigger: ['blur', 'change'] trigger: ['blur', 'change']
} }
], ]
basementCount: [
{ required: true, message: '请输入地下室层数', trigger: ['blur', 'change'] },
{
validator: (rule, value, callback) => {
if (form.hasBasement === 1) {
if (value === null || value === '') {
callback(new Error('请输入地下室层数'));
} else if (value < 0) {
callback(new Error('层数不能为负数'));
} else {
callback();
}
} else {
callback();
}
},
trigger: ['blur', 'change']
}
]
}); });
// 提交表单 // 提交表单
const handleSubmit = async () => { const handleSubmit = () => {
try { try {
submitting.value = true; submitting.value = true;
if (form.hasBasement === 2) { if (form.hasBasement === 2) {
rules.basementCount = []; rules.basementCount = [];
} }
await addBuildingFormRef.value.validate();
// 准备提交数据 // 准备提交数据
const submitData = { const submitData = {
...form, ...form,
hasBasement: form.hasBasement === 1 undergroundFloorNum:form.hasBasement === 1 ? form.undergroundFloorNum : 0
}; };
console.log('提交新增建筑数据:', submitData); console.log('提交新增建筑数据:', submitData);
await new Promise(resolve => setTimeout(resolve, 1000)); buildingAddApi(submitData).then(res => {
if (res.code === 200) {
ElMessage.success('建筑信息新增成功!'); ElMessage.success('建筑信息新增成功!');
addBuildingFormRef.value.resetFields(); addBuildingFormRef.value.resetFields();
emit('closeAddDrawer'); emit('closeAddDrawer');
emit('tableDataRefresh');
}else{
ElMessage.error('建筑信息新增响应代码错误!');
}
}).catch(err => {
ElMessage.error('添加建筑错误',err);
});
} catch (error) { } catch (error) {
console.error('表单验证失败:', error); console.error('表单验证失败:', error);
ElMessage.error('请完善表单信息后重试'); // ElMessage.error('请完善表单信息后重试');
} finally { } finally {
submitting.value = false; submitting.value = false;
} }
...@@ -213,8 +200,9 @@ const handleCancel = () => { ...@@ -213,8 +200,9 @@ const handleCancel = () => {
type: 'warning', type: 'warning',
center: true center: true
}).then(() => { }).then(() => {
addBuildingFormRef.value.resetFields();
emit('closeAddDrawer'); emit('closeAddDrawer');
addBuildingFormRef.value.resetFields();
}).catch(() => {}); }).catch(() => {});
}; };
......
<template>
<div class="add-building-modal">
<!-- 标题栏 -->
<div class="modal-header">
<h2 class="title">新增建筑</h2>
<el-icon class="close-icon" @click="handleClose" size="20">
<Close />
</el-icon>
</div>
<!-- 表单主体 -->
<el-form
ref="addBuildingFormRef"
:model="form"
:rules="rules"
label-width="120px"
label-position="left"
class="add-building-form"
status-icon
>
<!-- 建筑编号 -->
<el-form-item label="楼层类型" prop="buildingStye" class="form-item">
<el-radio-group v-model="form.buildingStye">
<el-radio :value="1">地上</el-radio>
<el-radio :value="0">地下</el-radio>
</el-radio-group>
</el-form-item>
<!-- 建筑名称 -->
<el-form-item label="楼层号" prop="buildingNo" class="form-item">
<el-select v-model="form.buildingNo" placeholder="选择楼层号" style="width: 240px">
<el-option
v-for="item in form.buildingNo"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<!-- 建筑层数 -->
<el-form-item label="楼层平面图" prop="floorPlan" class="form-item">
<!-- 上传图片 -->
<UpdataImg ref="floorUpdateloader" @getAvatar="getfloorPlan" :updataImageCount="1"></UpdataImg>
</el-form-item>
<!-- 是否有地下室 -->
<el-form-item label="航线绑定" prop="pathBinding" class="form-item">
<el-select v-model="form.pathBinding" placeholder="选择可绑定的航线" style="width: 240px">
<el-option
v-for="item in form.pathBinding"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<!-- 建筑功能描述 -->
<el-form-item label="楼层描述" prop="description" class="form-item">
<el-input
v-model="form.description"
type="textarea"
placeholder="请输入建筑功能描述"
:rows="3"
class="textarea-field"
show-word-limit
maxlength="200"
/>
</el-form-item>
</el-form>
<!-- 底部按钮 -->
<div class="modal-footer">
<el-button @click="handleCancel" class="cancel-btn" size="default">取消</el-button>
<el-button
@click="handleSubmit"
type="primary"
class="confirm-btn"
size="default"
:loading="submitting"
>
确认
</el-button>
</div>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Close } from '@element-plus/icons-vue';
import UpdataImg from '../../systemmanage/components/updataImg.vue';
// 表单引用
const addBuildingFormRef = ref(null);
// 提交加载状态
const submitting = ref(false);
const emit = defineEmits(['closeAddDrawer']);
// 表单数据
const form = reactive({
buildingStye:'',
// 楼层编号初始的时候要获取,数据类型 buildingNo:{label: '1层', value: '1'}
buildingNo:'',
// 航线编号初始的时候要获取,数据类型floorPlan:{label: '航线1', value: '1'}
floorPlan:'',
pathBinding:'',
description:'',
description: "",
});
const getfloorPlan = (url) => {
console.log(url,"const url = URL.createObjectURL(url);");
form.floorPlan = url;
};
// 表单验证规则
const rules = reactive({
});
// 提交表单
const handleSubmit = async () => {
try {
submitting.value = true;
await addBuildingFormRef.value.validate();
// 准备提交数据
const submitData = {
...form,
};
console.log('提交新增建筑数据:', submitData);
await new Promise(resolve => setTimeout(resolve, 1000));
ElMessage.success('建筑信息新增成功!');
addBuildingFormRef.value.resetFields();
emit('closeAddDrawer');
} catch (error) {
console.error('表单验证失败:', error);
ElMessage.error('请完善表单信息后重试');
} finally {
submitting.value = false;
}
};
const floorUpdateloader = ref(null);
// 取消操作
const handleCancel = () => {
ElMessageBox.confirm('确定要取消新增吗?已填写内容将不保存', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
center: true
}).then(() => {
console.log('取消新增');
addBuildingFormRef.value.resetFields();
floorUpdateloader.value.handleRemove()
emit('closeAddDrawer');
}).catch(() => {
console.log('报错了');
});
};
// 关闭弹窗
const handleClose = () => {
addBuildingFormRef.value.resetFields();
handleCancel();
};
</script>
<style scoped lang="scss">
.add-building-modal {
width: 100%;
max-width: 520px;
padding: 15px;
box-sizing: border-box;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
// 标题栏样式
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 1px solid #f0f0f0;
.title {
margin: 0;
font-size: 18px;
font-weight: 600;
color: #303133;
}
.close-icon {
cursor: pointer;
color: #909399;
transition: all 0.2s;
padding: 4px;
border-radius: 50%;
&:hover {
color: #f56c6c;
background-color: #fef0f0;
}
}
}
// 表单样式
.add-building-form {
.form-item {
margin-bottom: 18px;
:deep(.el-form-item__label) {
color: #606266;
font-weight: normal;
}
}
.input-field, .textarea-field {
width: 100%;
max-width: 380px;
:deep(.el-input__inner) {
height: 36px;
line-height: 36px;
border-radius: 4px;
}
:deep(.el-textarea__inner) {
min-height: 100px !important;
border-radius: 4px;
}
}
// 单选框样式
.radio-group {
display: flex;
gap: 20px;
.radio-btn {
margin: 0;
:deep(.el-radio__label) {
padding-left: 6px;
font-size: 14px;
}
}
}
}
// 底部按钮样式
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 16px;
margin-top: 24px;
padding-top: 20px;
border-top: 1px solid #f0f0f0;
.cancel-btn {
width: 90px;
height: 36px;
border-radius: 4px;
color: #606266;
border-color: #dcdfe6;
&:hover {
color: #409eff;
border-color: #c6e2ff;
background-color: #ecf5ff;
}
}
.confirm-btn {
width: 90px;
height: 36px;
border-radius: 4px;
background-color: #409eff;
border-color: #409eff;
&:hover {
background-color: #66b1ff;
border-color: #66b1ff;
}
&:active {
background-color: #3a8ee6;
border-color: #3a8ee6;
}
}
}
// 响应式适配
@media (max-width: 768px) {
padding: 16px;
max-width: 100%;
.add-building-form {
.input-field, .textarea-field {
max-width: 100% !important;
}
.radio-group {
gap: 16px;
}
}
.modal-footer {
justify-content: center;
gap: 12px;
}
}
}
</style>
\ No newline at end of file
<template>
<div class="building-detail-container">
<!-- 页面标题 -->
<div class="page-title">
XX 单元 XX 号楼 XX 层详情信息
</div>
<!-- 折叠面板容器 -->
<el-collapse v-model="activeNames" class="detail-collapse" accordion>
<!-- 楼栋信息详情面板 -->
<el-collapse-item title="楼栋信息详情" name="buildingInfo">
<searchtop :searchShowData="searchShowData"></searchtop>
</el-collapse-item>
<!-- 楼层平面结构图面板 -->
<el-collapse-item title="楼层平面结构图" name="floorPlan">
<div class="floor-plan-container">
<el-empty
v-if="!floorPlanImage"
description="暂无楼层平面图"
class="empty-state"
/>
<el-image
v-else
style="width: 100px; height: 100px;background-color: aquamarine;"
:src="floorPlanImage[0]"
:preview-src-list="floorPlanImage"
show-progress
hide-on-click-modal="true"
fit="cover"
alt="楼层平面结构图"
/>
</div>
</el-collapse-item>
<!-- 航线列表面板 -->
<el-collapse-item title="航线列表" name="routeList">
<div class="table-container">
<el-table
:data="routeTableData"
stripe
border
class="custom-table"
:header-cell-style="headerCellStyle"
>
<el-table-column prop="id" label="序号" align="center" />
<el-table-column prop="routeId" label="航线ID" align="center" />
<el-table-column prop="takeoffPoint" label="起飞点" align="center" />
<el-table-column prop="task" label="任务点" align="center" />
<el-table-column prop="updateTime" label="更新时间" align="center">
<template #default="scope">
<el-popover
placement="top"
width="200"
trigger="hover"
content="具体于安防信息库内容填写<br>Zhen.2025.08.13"
>
<template #reference>
<span class="time-link">{{ scope.row.updateTime }}</span>
</template>
</el-popover>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-container">
<el-pagination
layout="total, sizes, prev, pager, next"
:total="routeTotal"
:page-size="routePageSize"
:page-sizes="[5, 10, 20, 50]"
v-model:current-page="routeCurrentPage"
@size-change="handleRouteSizeChange"
@current-change="handleRouteCurrentChange"
/>
</div>
</div>
</el-collapse-item>
<!-- 火情信息列表面板 -->
<el-collapse-item title="火情信息列表" name="fireInfo">
<div class="table-container">
<el-table
:data="fireTableData"
stripe
border
class="custom-table"
:header-cell-style="headerCellStyle"
>
<el-table-column prop="id" label="序号" width="80" align="center" />
<el-table-column prop="fireTime" label="火情时间" width="180" align="center" />
<el-table-column prop="location" label="着火点" width="150" align="center" />
<el-table-column prop="description" label="火情描述" min-width="200" />
<el-table-column prop="recorder" label="记录人" width="120" align="center" />
<el-table-column prop="image" label="火情图片" width="120" align="center">
<template #default="scope">
<el-image @click="handleFireImageClick(scope.row)" style="width: 100px; height: 100px" :src="scope.row.image[0]" fit="fill" />
<!-- <el-image
v-if="scope.row.image"
:src="scope.row.image[0]"
alt="火情图片"
:preview-src-list="scope.row.image"
class="fire-image"
hide-on-click-modal = true
:initial-index="400000000000000"
>
<template #error>
<div class="image-error">
<el-icon><PictureFilled /></el-icon>
</div>
</template>
</el-image>
<span v-else class="no-image">暂无</span> -->
</template>
</el-table-column>
<el-table-column prop="level" label="火情等级" width="120" align="center">
<template #default="scope">
<el-tag
:type="getTagType(scope.row.level)"
size="small"
>
{{ scope.row.level }}
</el-tag>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-container">
<el-pagination
layout="total, sizes, prev, pager, next"
:total="fireTotal"
:page-size="firePageSize"
:page-sizes="[5, 10, 20, 50]"
v-model:current-page="fireCurrentPage"
@size-change="handleFireSizeChange"
@current-change="handleFireCurrentChange"
/>
</div>
</div>
</el-collapse-item>
</el-collapse>
<el-dialog
v-model="isFireImagedialogVisible"
title="火情照片"
width="800"
:before-close="handleClose"
>
<div class="image-grid">
<div
v-for="(img, index) in fireImagedialogDate.image"
:key="index"
class="image-thumbnail"
@click="openPreview(index)"
>
<el-image
:src="img"
fit="cover"
class="thumbnail-image"
/>
</div>
</div>
<!-- 图片预览组件 -->
<el-image-viewer
v-if="previewVisible"
:url-list="fireImagedialogDate.image"
:initial-index="previewIndex"
@close="previewVisible = false"
/>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue';
import searchtop from '../../commentcomponents/searchtop/index.vue'
// 折叠面板状态 - 默认展开第一个面板
const activeNames = ref(['floorPlan']);
// 楼栋信息
const buildingInfo = reactive({
buildingNo: '',
buildingName: '',
floor: ''
});
const searchShowData = ref([
{ label: '楼栋编号', placeholder: "请输入", type: 'input',content:'' },
{ label: '楼栋名称', placeholder: "请输入", type: 'input',content:'' },
{ label: '对应楼层', placeholder: "请输入", type: 'input',content:'' },
])
// 楼层平面图
const floorPlanImage = ref(['https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg']); // 初始为空,实际项目中可替换为真实图片URL
const handleImageError = () => {
floorPlanImage.value = ''; // 图片加载失败时显示空状态
};
// 航线表格数据
const routeTableData = ref([
{ id: 1, routeId: '', takeoffPoint: '', task: '', updateTime: '2025-08-13' },
{ id: 2, routeId: '', takeoffPoint: '', task: '', updateTime: '2025-08-13' },
{ id: 3, routeId: '', takeoffPoint: '', task: '', updateTime: '2025-08-13' },
{ id: 4, routeId: '', takeoffPoint: '', task: '', updateTime: '2025-08-13' },
{ id: 5, routeId: '', takeoffPoint: '', task: '', updateTime: '2025-08-13' },
{ id: 6, routeId: '', takeoffPoint: '', task: '', updateTime: '2025-08-13' },
{ id: 7, routeId: '', takeoffPoint: '', task: '', updateTime: '2025-08-13' },
{ id: 8, routeId: '', takeoffPoint: '', task: '', updateTime: '2025-08-13' },
{ id: 9, routeId: '', takeoffPoint: '', task: '', updateTime: '2025-08-13' },
{ id: 10, routeId: '', takeoffPoint: '', task: '', updateTime: '2025-08-13' },
{ id: 11, routeId: '', takeoffPoint: '', task: '', updateTime: '2025-08-13' },
{ id: 12, routeId: '', takeoffPoint: '', task: '', updateTime: '2025-08-13' },
{ id: 13, routeId: '', takeoffPoint: '', task: '', updateTime: '2025-08-13' },
{ id: 14, routeId: '', takeoffPoint: '', task: '', updateTime: '2025-08-13' },
{ id: 15, routeId: '', takeoffPoint: '', task: '', updateTime: '2025-08-13' },
{ id: 16, routeId: '', takeoffPoint: '', task: '', updateTime: '2025-08-13' },
{ id: 17, routeId: '', takeoffPoint: '', task: '', updateTime: '2025-08-13' },
{ id: 18, routeId: '', takeoffPoint: '', task: '', updateTime: '2025-08-13' },
]);
const routeTotal = ref(routeTableData.value.length);
const routeCurrentPage = ref(1);
const routePageSize = ref(10);
// 火情表格数据
const fireTableData = ref([
{
id: 1,
fireTime: 'fsaf ',
location: 'fa',
description: 'gtr',
recorder: 'gts',
image: ['https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg',
'https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg',
'https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg',
'https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg',
'https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg',],
level: ''
},
{
id: 1,
fireTime: '',
location: '',
description: '',
recorder: '',
image: ['https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
],
level: ''
},
{
id: 1,
fireTime: '',
location: '',
description: '',
recorder: '',
image: ['https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg',
'https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg',
'https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg',
'https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg',
'https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg',],
level: ''
},
{
id: 1,
fireTime: '',
location: '',
description: '',
recorder: '',
image: ['https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg',
'https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg',
'https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg',
'https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg',
'https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg',],
level: ''
},
{
id: 2,
fireTime: '',
location: '',
description: '',
recorder: '',
image: '',
level: ''
},
{
id: 3,
fireTime: '',
location: '',
description: '',
recorder: '',
image: '',
level: ''
}
]);
const fireTotal = ref(fireTableData.value.length);
const fireCurrentPage = ref(1);
const firePageSize = ref(10);
const isFireImagedialogVisible = ref(false);
const fireImagedialogDate = ref([]);
const handleFireImageClick = (info) => {
console.log('点击图片索引:', info);
isFireImagedialogVisible.value = true;
fireImagedialogDate.value = info
};
// 图片预览状态
const previewVisible = ref(false);
const previewIndex = ref(0);
// 打开图片预览
const openPreview = (index) => {
previewIndex.value = index;
previewVisible.value = true;
};
// 表格头部样式
const headerCellStyle = {
'background-color': '#f5f7fa',
'color': '#1f2937',
'font-weight': '500'
};
// 获取火情等级标签样式
const getTagType = (level) => {
switch(level) {
case '严重':
return 'danger';
case '中等':
return 'warning';
case '轻微':
return 'info';
default:
return 'default';
}
};
// 航线分页事件
const handleRouteSizeChange = (val) => {
routePageSize.value = val;
console.log('航线当前页个数:', val);
// 实际项目中这里应该调用API加载数据
};
const handleRouteCurrentChange = (val) => {
routeCurrentPage.value = val;
console.log('航线当前页码:', val);
// 实际项目中这里应该调用API加载数据
};
// 火情分页事件
const handleFireSizeChange = (val) => {
firePageSize.value = val;
console.log('火情当前页个数:', val);
// 实际项目中这里应该调用API加载数据
};
const handleFireCurrentChange = (val) => {
fireCurrentPage.value = val;
console.log('火情当前页码:', val);
// 实际项目中这里应该调用API加载数据
};
</script>
<style scoped>
.building-detail-container {
padding: 20px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
}
.page-title {
font-size: 18px;
font-weight: 500;
color: #1f2937;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #e5e7eb;
}
/* 折叠面板样式 */
.detail-collapse {
border: none;
}
:deep(.el-collapse-item) {
border: 1px solid #e5e7eb;
border-radius: 6px;
margin-bottom: 15px;
overflow: hidden;
}
:deep(.el-collapse-item__header) {
padding: 12px 20px;
background-color: #f9fafb;
border-bottom: 1px solid #e5e7eb;
font-size: 15px;
font-weight: 500;
}
:deep(.el-collapse-item__content) {
padding: 15px 20px;
}
/* 表格容器样式 */
.table-container {
width: 100%;
/* z-index: 0; */
/* background-color: #1f2937; */
overflow-x: auto;
}
.custom-table {
width: 100%;
height: 380px;
margin-bottom: 16px;
}
.pagination-container {
display: flex;
justify-content: flex-end;
margin-top: 16px;
}
/* 楼栋信息表单样式 */
.info-form {
padding: 5px 0;
}
.form-row {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.form-item {
display: flex;
align-items: center;
margin-bottom: 15px;
min-width: 250px;
flex: 1;
}
.form-label {
width: 100px;
color: #6b7280;
text-align: right;
margin-right: 12px;
white-space: nowrap;
}
.form-input {
flex: 1;
min-width: 150px;
}
/* 楼层平面图样式 */
.floor-plan-container {
display: flex;
justify-content: center;
align-items: center;
height: 220px;
border: 1px dashed #e5e7eb;
border-radius: 4px;
padding: 20px;
}
.floor-plan-image {
max-width: 100%;
max-height: 500px;
object-fit: contain;
border-radius: 4px;
}
.empty-state {
width: 100%;
}
/* 火情信息列表样式 */
.fire-image {
/* width: 80px;
height: 60px; */
object-fit: cover;
cursor: pointer;
border-radius: 4px;
}
.image-error {
width: 80px;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
background-color: #f5f5f5;
color: #909399;
}
.no-image {
color: #909399;
font-size: 14px;
}
/* 航线列表样式 */
.time-link {
color: #409eff;
cursor: pointer;
text-decoration: underline;
}
.fire-image-dialog {
--el-dialog-padding-primary: 20px;
}
.image-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 15px;
max-height: 70vh;
overflow-y: auto;
padding: 10px;
}
.image-thumbnail {
position: relative;
height: 150px;
border-radius: 4px;
overflow: hidden;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.image-thumbnail:hover {
transform: translateY(-3px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.thumbnail-image {
width: 100%;
height: 100%;
object-fit: cover;
}
/* 响应式调整 */
@media (max-width: 768px) {
.image-grid {
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
}
.image-thumbnail {
height: 120px;
}
}
@media (max-width: 480px) {
.image-grid {
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
}
.image-thumbnail {
height: 100px;
}
}
/* 响应式样式 */
@media (max-width: 768px) {
.form-row {
flex-direction: column;
gap: 0;
}
.form-item {
width: 100%;
}
.page-title {
font-size: 16px;
}
.pagination-container {
justify-content: center;
}
:deep(.el-collapse-item__header) {
padding: 10px 15px;
font-size: 14px;
}
:deep(.el-collapse-item__content) {
padding: 10px 15px;
}
}
</style>
\ No newline at end of file
...@@ -19,9 +19,9 @@ ...@@ -19,9 +19,9 @@
status-icon status-icon
> >
<!-- 建筑编号 --> <!-- 建筑编号 -->
<el-form-item label="建筑编号" prop="buildingNo" class="form-item"> <el-form-item label="建筑编号" prop="buildCode" class="form-item">
<el-input <el-input
v-model="form.buildingNo" v-model="form.buildCode"
placeholder="请输入建筑编号" placeholder="请输入建筑编号"
class="input-field" class="input-field"
clearable clearable
...@@ -29,9 +29,9 @@ ...@@ -29,9 +29,9 @@
</el-form-item> </el-form-item>
<!-- 建筑名称 --> <!-- 建筑名称 -->
<el-form-item label="建筑名称" prop="buildingName" class="form-item"> <el-form-item label="建筑名称" prop="buildName" class="form-item">
<el-input <el-input
v-model="form.buildingName" v-model="form.buildName"
placeholder="请输入建筑名称" placeholder="请输入建筑名称"
class="input-field" class="input-field"
clearable clearable
...@@ -39,9 +39,9 @@ ...@@ -39,9 +39,9 @@
</el-form-item> </el-form-item>
<!-- 建筑层数 --> <!-- 建筑层数 -->
<el-form-item label="建筑层数(地上)" prop="buildingFloorCount" class="form-item"> <el-form-item label="建筑层数(地上)" prop="floorNum" class="form-item">
<el-input <el-input
v-model="form.buildingFloorCount" v-model="form.floorNum"
placeholder="请输入地上层数" placeholder="请输入地上层数"
type="number" type="number"
min="0" min="0"
...@@ -58,10 +58,10 @@ ...@@ -58,10 +58,10 @@
label="地下室层数" label="地下室层数"
prop="basementCount" prop="basementCount"
class="form-item" class="form-item"
:rules=" rules.buildingBasementCount "
> >
<el-input <el-input
v-model="form.buildingBasementCount" v-model="form.undergroundFloorNum"
placeholder="请输入地下室层数" placeholder="请输入地下室层数"
type="number" type="number"
min="0" min="0"
...@@ -72,9 +72,9 @@ ...@@ -72,9 +72,9 @@
</el-form-item> </el-form-item>
<!-- 建筑功能描述 --> <!-- 建筑功能描述 -->
<el-form-item label="建筑功能描述" prop="buildingDescribe" class="form-item"> <el-form-item label="建筑功能描述" prop="description" class="form-item">
<el-input <el-input
v-model="form.buildingDescribe" v-model="form.description"
type="textarea" type="textarea"
placeholder="请输入建筑功能描述" placeholder="请输入建筑功能描述"
:rows="3" :rows="3"
...@@ -105,7 +105,7 @@ ...@@ -105,7 +105,7 @@
import { ref, reactive, onMounted, onActivated } from 'vue'; import { ref, reactive, onMounted, onActivated } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
import { Close } from '@element-plus/icons-vue'; import { Close } from '@element-plus/icons-vue';
import { buildingUpdateApi } from '@/api/build';
// 表单引用 // 表单引用
const addBuildingFormRef = ref(null); const addBuildingFormRef = ref(null);
// 提交加载状态 // 提交加载状态
...@@ -122,12 +122,12 @@ watch(() => props.isBuildingEditDrawer, (newVal) => { ...@@ -122,12 +122,12 @@ watch(() => props.isBuildingEditDrawer, (newVal) => {
}); });
// 表单数据 // 表单数据
const form = reactive({ const form = reactive({
buildingNo: '', // 建筑编号 buildCode: '', // 建筑编号
buildingName: '', // 建筑名称 buildName: '', // 建筑名称
buildingFloorCount: null, // 地上层数 floorNum: null, // 地上层数
buildingBasementCount: null, // 地下室层数 undergroundFloorNum: null, // 地下室层数
buildingDescribe: '' // 建筑功能描述 description: '' // 建筑功能描述
}); });
// 初始化表单数据 // 初始化表单数据
...@@ -146,11 +146,11 @@ const initFormData = () => { ...@@ -146,11 +146,11 @@ const initFormData = () => {
// 表单验证规则 // 表单验证规则
const rules = reactive({ const rules = reactive({
buildingNo: [ buildCode: [
{ required: true, message: '请输入建筑编号', trigger: ['blur', 'change'] }, { required: true, message: '请输入建筑编号', trigger: ['blur', 'change'] },
{ min: 2, max: 20, message: '编号长度在2-20个字符之间', trigger: ['blur', 'change'] } { min: 2, max: 20, message: '编号长度在2-20个字符之间', trigger: ['blur', 'change'] }
], ],
buildingName: [ buildName: [
{ required: true, message: '请输入建筑名称', trigger: ['blur', 'change'] }, { required: true, message: '请输入建筑名称', trigger: ['blur', 'change'] },
{ message: '名称长度在2-50个字符之间', trigger: ['blur', 'change'] } { message: '名称长度在2-50个字符之间', trigger: ['blur', 'change'] }
], ],
...@@ -193,27 +193,36 @@ const rules = reactive({ ...@@ -193,27 +193,36 @@ const rules = reactive({
}); });
// 提交表单 // 提交表单
const handleSubmit = async () => { const handleSubmit = () => {
try { try {
submitting.value = true; submitting.value = true;
if (form.hasBasement === 2) { if (form.hasBasement === 2) {
rules.basementCount = []; rules.basementCount = [];
} }
await addBuildingFormRef.value.validate(); const {buildCode,buildId, buildName,description} = form;
// 准备提交数据 // 准备提交数据
const submitData = { const submitData = {
...form, buildCode,buildId, buildName,description
hasBasement: form.hasBasement === 1
}; };
buildingUpdateApi(submitData).then(res => {
console.log('提交新增建筑数据:', submitData); if (res.code === 200) {
await new Promise(resolve => setTimeout(resolve, 1000)); ElMessage.success('建筑信息更新成功!');
ElMessage.success('建筑信息新增成功!');
// addBuildingFormRef.value.resetFields();
emit('closeEditDrawer'); emit('closeEditDrawer');
emit('tableDataRefresh'); emit('tableDataRefresh');
}else{
ElMessage.error('建筑信息更新响应代码错误!');
}
}).catch(err => {
})
// console.log('提交新增建筑数据:', submitData);
// ElMessage.success('建筑信息新增成功!');
// // addBuildingFormRef.value.resetFields();
// emit('closeEditDrawer');
// emit('tableDataRefresh');
} catch (error) { } catch (error) {
console.error('表单验证失败:', error); console.error('表单验证失败:', error);
ElMessage.error('请完善表单信息后重试'); ElMessage.error('请完善表单信息后重试');
......
<template>
<div class="dashboard-container" :style="containerStyle">
<main class="main-content">
<!-- 左侧区域 -->
<div class="left-section">
<!-- 左侧上部分 -->
<div class="left-top">
<!-- 区域信息概览 -->
<div class="info-card region-overview">
<searchtop :searchShowData="searchShowData"></searchtop>
</div>
</div>
<!-- 左侧下部分:统计区域 -->
<div class="info-card stats-alerts">
<div class="card-tabs">
<span class="title">建筑列表</span>
<div class="card-actions">
<el-button class="operation-button" type="primary" size="small">导入</el-button>
<el-button class="operation-button" type="primary" size="small">导出</el-button>
<el-button class="operation-button" type="primary" size="small" @click="handleAddInfoFn">新增建筑</el-button>
<el-button class="operation-button" type="primary" size="small" @click="handleRefreshInfoFn"><el-icon ><RefreshRight /></el-icon></el-button>
<el-button class="operation-button" type="primary" size="small"><el-icon><FullScreen /></el-icon></el-button>
</div>
</div>
<div class="card-table">
<tabledata :tableShowData="tableShowData"></tabledata>
</div>
</div>
</div>
</main>
<!-- 新增建筑抽屉 -->
<el-drawer v-model="isAddDrawer" title="添加建筑信息" :with-header="false" :before-close="beforeCloseAddBuildDrawer">
<Flooraddbuilding @closeAddDrawer="closeAddDrawer" @tableDataRefresh="handleRefreshInfoFn"></Flooraddbuilding>
</el-drawer>
<!-- 修改建筑抽屉 -->
<el-drawer v-model="isBuildingEditDrawer" title="修改用户信息" :with-header="false" :before-close="beforeCloseAddBuildDrawer">
<Flooraddbuilding :rowData="rowData" :isBuildingEditDrawer="isBuildingEditDrawer" @closeEditDrawer="closeBuildingEditDrawer" @tableDataRefresh="handleRefreshInfoFn"></Flooraddbuilding>
</el-drawer>
<!-- 图片预览组件 -->
<el-image-viewer
v-if="previewVisible && previewVisibledialogDate.length > 0"
:url-list="previewVisibledialogDate"
@close="previewVisible = false"
/>
</div>
</template>
<script setup>
import { reactive, computed, ref } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import searchtop from '../../commentcomponents/searchtop/index.vue';
import tabledata from '../../commentcomponents/tabledata/index.vue';
import router from '../../../router';
import Flooraddbuilding from './flooraddbuilding.vue';
import { useRoute,useRouter } from 'vue-router';
const route = useRoute();
// 搜索栏配置
const searchShowData = ref([
{ label: '楼层描述', placeholder: "请输入", type: 'input', content: '' },
{ label: '楼层号', placeholder: "请选择", type: 'select', content: '', options: [
{label:'火灾', value:'烟雾'},
{label:'烟雾', value:'火灾'}
] },
]);
// 布局配置
const layoutConfig = reactive({
containerMinHeight: '100vh',
cardSpacing: '1rem'
});
// 抽屉控制
const isAddDrawer = ref(false);
const isBuildingEditDrawer = ref(false);
const rowData = ref([]);
// 方法定义
const handleAddInfoFn = () => {
console.log("打开新增信息");
isAddDrawer.value = true;
}
const handleRefreshInfoFn = () => {
console.log("刷新数据");
}
const closeAddDrawer = () => {
console.log("000000-------1-----0000000000",isAddDrawer.value)
isAddDrawer.value = false;
}
const beforeCloseAddBuildDrawer = () => {
// 关闭前的处理逻辑
}
const oneBuildingSeeDetails = (data) => {
console.log("查看详情", data);
router.push({
path: '/onefloorbuildingdetaildate',
// params: { data: JSON.stringify(data) }
});
}
const oneBuildingDdeleteData = (data) => {
ElMessageBox.confirm('是否删除该建筑?此操作将无法撤销', '删除', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
handleRefreshInfoFn()
ElMessage.success('已删除');
}).catch(() => {
ElMessage.info('取消删除');
});
}
const oneResetBuildingShowInfo = (data) => {
isBuildingEditDrawer.value = true;
rowData.value = data;
}
const closeBuildingEditDrawer = () => {
isBuildingEditDrawer.value = false;
console.log("000000------------0000000000")
}
// 图片预览状态
const previewVisible = ref(false);
const previewVisibledialogDate = ref([]);
const seeBuildingFloorPlan = (data) => {
console.log("查看楼层平面图", data);
previewVisible.value = true;
if(data.buildingFloorPlan.length > 0) {
previewVisibledialogDate.value = ['https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',]
return;
}else {
ElMessage.warning({
message: '该层暂无楼层平面图',
duration: 2000
})
}
}
// 表格数据
const tableShowData = ref([{
tableHeader: [
{label: '建筑编号', prop: 'buildingNo'},
{label: '建筑名称', prop: 'buildingName'},
{label: '建筑层数', prop: 'buildingFloorCount'},
{label: '楼层平面图', prop: 'buildingFloorPlan'},
{label: '更新时间', prop: 'buildingUpdataTime'},
{label: '楼层描述', prop: 'buildingDescription'},
{label: '操作', prop: 'buildingOperation'}
],
tableBody: [
{buildingNo: '张三', buildingName:"123456", buildingFloorCount: 11, buildingFloorPlan:
[
{label:'查看', type:'primary', icon:'EditPen', click: seeBuildingFloorPlan},
],
buildingUpdataTime: Date.now(),buildingDescription:' TEST ',
buildingOperation: [
{label:'查看详情', type:'primary', icon:'EditPen', click: oneBuildingSeeDetails},
{label:'删除', type:'danger', icon:'Delete', click: oneBuildingDdeleteData},
{label:'修改', type:'primary', icon:'EditPen', click: oneResetBuildingShowInfo}
]},
{buildingNo: '张89三', buildingName:"123456", buildingFloorCount: 11, buildingFloorPlan:
[
{label:'查看', type:'primary', icon:'EditPen', click: seeBuildingFloorPlan},
],
buildingUpdataTime: Date.now(),buildingDescription:' TEST ',
buildingOperation: [
{label:'查看详情', type:'primary', icon:'EditPen', click: oneBuildingSeeDetails},
{label:'删除', type:'danger', icon:'Delete', click: oneBuildingDdeleteData},
{label:'修改', type:'primary', icon:'EditPen', click: oneResetBuildingShowInfo}
]},
{buildingNo: '张45三', buildingName:"123456", buildingFloorCount: 11, buildingFloorPlan:
[
{label:'查看', type:'primary', icon:'EditPen', click: seeBuildingFloorPlan},
],
buildingUpdataTime: Date.now(),buildingDescription:' TEST ',
buildingOperation: [
{label:'查看详情', type:'primary', icon:'EditPen', click: oneBuildingSeeDetails},
{label:'删除', type:'danger', icon:'Delete', click: oneBuildingDdeleteData},
{label:'修改', type:'primary', icon:'EditPen', click: oneResetBuildingShowInfo}
]},
]
}]);
// 计算属性
const containerStyle = computed(() => ({
minHeight: layoutConfig.containerMinHeight,
'--card-spacing': layoutConfig.cardSpacing,
}));
</script>
<style scoped>
.dashboard-container {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: rgba(0, 0, 0, 0.05);
color: #333;
overflow: hidden;
}
.main-content {
display: flex;
gap: var(--card-spacing);
padding: 0.4rem;
height: 100vh;
box-sizing: border-box;
}
.left-section {
flex: 2;
display: flex;
flex-direction: column;
gap: var(--card-spacing);
height: 100%;
}
.left-top {
height: auto;
}
.info-card {
background-color: #fff;
padding: 1rem;
border-radius: 8px;
box-sizing: border-box;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
overflow: auto;
}
.region-overview {
flex: 1;
min-width: 200px;
}
.stats-alerts {
display: flex;
flex-direction: column;
gap: 1rem;
height: 67%;
}
.card-tabs {
display: flex;
border-bottom: 1px solid #292828;
font-size: 1.2rem;
color: #000000;
height: 8%;
align-items: center;
width: 100%;
padding: 10px;
}
.title {
width: 70%;
}
.card-actions {
display: flex;
flex: 1;
justify-content: space-between;
align-items: center;
height: 8%;
}
.operation-button {
width: 15%;
}
.card-table {
width: 100%;
flex:1;
overflow: hidden;
border-radius: 8px;
}
@media (max-width: 1024px) {
.left-top {
flex-direction: column;
}
.region-overview {
width: 100%;
flex: none;
}
}
@media (max-width: 768px) {
.stats-alerts {
flex-direction: column;
}
}
</style>
\ No newline at end of file
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
</div> </div>
</div> </div>
<div class="card-table"> <div class="card-table">
<tabledata :tableShowData="tableShowData"></tabledata> <tabledata :tableShowData="tableShowData" @tableDataRefresh="handleRefreshInfoFn"></tabledata>
</div> </div>
</div> </div>
</div> </div>
...@@ -44,13 +44,16 @@ ...@@ -44,13 +44,16 @@
</template> </template>
<script setup> <script setup>
import { reactive, computed, ref } from 'vue'; import { reactive, computed, ref, onMounted, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
import searchtop from '../commentcomponents/searchtop/index.vue'; import searchtop from '../commentcomponents/searchtop/index.vue';
import tabledata from '../commentcomponents/tabledata/index.vue'; import tabledata from '../commentcomponents/tabledata/index.vue';
import showResetBuildingInfo from './components/showResetBuildingInfo.vue'; import showResetBuildingInfo from './components/showResetBuildingInfo.vue';
import Addbuild from './components/addbuild.vue'; import Addbuild from './components/addbuild.vue';
import router from '../../router';
import { buildingPageApi,buildingRemoveApi } from '../../api/build';
import { useRoute,useRouter } from 'vue-router';
const route = useRoute();
// 搜索栏配置 // 搜索栏配置
const searchShowData = ref([ const searchShowData = ref([
{ label: '功能描述', placeholder: "请输入", type: 'input', content: '' }, { label: '功能描述', placeholder: "请输入", type: 'input', content: '' },
...@@ -84,6 +87,7 @@ const handleAddInfoFn = () => { ...@@ -84,6 +87,7 @@ const handleAddInfoFn = () => {
const handleRefreshInfoFn = () => { const handleRefreshInfoFn = () => {
console.log("刷新数据"); console.log("刷新数据");
initTableDateFn()
} }
const closeAddDrawer = () => { const closeAddDrawer = () => {
isAddDrawer.value = false; isAddDrawer.value = false;
...@@ -97,6 +101,10 @@ const beforeCloseAddBuildDrawer = () => { ...@@ -97,6 +101,10 @@ const beforeCloseAddBuildDrawer = () => {
const buildingSeeDetails = (data) => { const buildingSeeDetails = (data) => {
console.log("查看详情", data); console.log("查看详情", data);
router.push({
path: '/buildingdetaildate',
params: { data: JSON.stringify(data) },
});
} }
const buildingDdeleteData = (data) => { const buildingDdeleteData = (data) => {
...@@ -104,13 +112,34 @@ const buildingDdeleteData = (data) => { ...@@ -104,13 +112,34 @@ const buildingDdeleteData = (data) => {
confirmButtonText: '确认', confirmButtonText: '确认',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning', type: 'warning',
}).then(() => { })
handleRefreshInfoFn() .then(() => {
ElMessage.success('已删除'); try {
}).catch(() => { // 用户点击「确认」
ElMessage.info('取消删除'); const {buildId,buildName} = data;
buildingRemoveApi({ buildId: buildId })
.then(res => {
if (res.code === 200) {
ElMessage.success("`${buildName}已删除`");
handleRefreshInfoFn();
} else {
ElMessage.error(res.message || '删除失败'); // 更明确的错误提示
}
})
.catch(err => {
// API 请求失败
ElMessage.error(err?.message || '删除请求失败');
}); });
} } catch (error) {
console.error(error,'API 请求失败');
}
})
.catch(() => {
// 用户点击「取消」
ElMessage.info('已取消删除'); // 移除多余的 `err` 参数
});
};
const resetBuildingShowInfo = (data) => { const resetBuildingShowInfo = (data) => {
...@@ -121,31 +150,62 @@ const resetBuildingShowInfo = (data) => { ...@@ -121,31 +150,62 @@ const resetBuildingShowInfo = (data) => {
const closeBuildingEditDrawer = () => { const closeBuildingEditDrawer = () => {
isBuildingEditDrawer.value = false; isBuildingEditDrawer.value = false;
// console.log("000000------------0000000000",isBuildingEditDrawer.value)
} }
// 表格数据 // 表格数据
const tableShowData = ref([{ const tableShowData = ref([{
tableHeader: [ tableHeader: [
{label: '建筑编号', prop: 'buildingNo'}, {label: '建筑编号', prop: 'buildCode'},
{label: '建筑名称', prop: 'buildingName'}, {label: '建筑名称', prop: 'buildName'},
{label: '建筑层数', prop: 'buildingFloorCount'}, {label: '建筑层数', prop: 'totalFloorNum'},
{label: '火情次数', prop: 'buildingFireCount'}, {label: '火情次数', prop: 'fireNum'},
{label: '更新时间', prop: 'buildingUpdataTime'}, {label: '更新时间', prop: 'updateTime'},
{label: '操作', prop: 'buildingOperation'} {label: '操作', prop: 'Operation'}
], ],
tableBody: [ tableBody: []
{buildingNo: '张三', buildingName:"123456", buildingFloorCount: 11, buildingFireCount:3, buildingUpdataTime: Date.now(), buildingOperation: [
{label:'查看详情', type:'primary', icon:'EditPen', click: buildingSeeDetails},
{label:'删除', type:'danger', icon:'Delete', click: buildingDdeleteData},
{label:'修改', type:'primary', icon:'EditPen', click: resetBuildingShowInfo}
]},
{buildingNo: '张s三', buildingName:"123456", buildingFloorCount: 11, buildingFireCount:3, buildingUpdataTime: Date.now(), buildingOperation: [
{label:'查看详情', type:'primary', icon:'EditPen', click: buildingSeeDetails},
{label:'删除', type:'danger', icon:'Delete', click: buildingDdeleteData},
{label:'修改', type:'primary', icon:'EditPen', click: resetBuildingShowInfo}
]},
]
}]); }]);
const Operation = ref([
{ label: '查看详情', type: 'primary', icon: 'EditPen', click: buildingSeeDetails },
{ label: '删除', type: 'danger', icon: 'Delete', click: buildingDdeleteData },
{ label: '修改', type: 'primary', icon: 'EditPen', click: resetBuildingShowInfo }
]);
// onMounted(() => {
// initTableDateFn()
// });
const initTableDateFn = () => {
console.log('初始化数据');
buildingPageApi({currentPageNum: 1, currentPageSize: 10}).then(res => {
if(res.code === 200){
let {list ,total,pageSize} = res.data;
if(!list){
ElMessage.warning('建筑列表暂无数据,请先添加建筑数据');
}else{
tableShowData.value[0].tableBody = []
list = list.filter(element => {
return element.isDeleted === 0;
});
list.forEach(element => {
element.Operation = [...Operation.value]
});
tableShowData.value[0].tableBody = list
tableShowData.value[0].total = total
tableShowData.value[0].pageSize = pageSize
console.log(tableShowData.value, '展示数据');
}
}
}).catch(err => {
ElMessage.error(err?.message || "建筑列表请求失败")
})
};
// 监听路由变化
watch(route, () => {
initTableDateFn()
}, { immediate: true });
// 计算属性 // 计算属性
const containerStyle = computed(() => ({ const containerStyle = computed(() => ({
minHeight: layoutConfig.containerMinHeight, minHeight: layoutConfig.containerMinHeight,
......
<template>
<div class="dashboard-container" :style="containerStyle">
<main class="main-content">
<!-- 左侧区域 -->
<div class="left-section">
<!-- 左侧下部分:统计区域 -->
<div class="info-card stats-alerts">
<div class="card-table">
<onefloorbuildingdetaildate></onefloorbuildingdetaildate>
</div>
</div>
</div>
</main>
</div>
</template>
<script setup>
import { reactive, computed, ref, onMounted } from 'vue';
// import searchtop from '../../commentcomponents/searchtop/index.vue';
import onefloorbuildingdetaildate from '../components/onefloorbuildingdetaildate.vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { staffGetPageInfoApi } from "../../../api/staff.js"
import { message } from 'ant-design-vue';
// import Operation from 'ant-design-vue/es/transfer/operation';
// 定义一个名为searchShowData的ref变量,它是一个数组,数组中的每个元素都是一个对象,对象中包含了label、placeholder、type、content和options等属性
const searchShowData = ref([
{ label: '用户姓名', placeholder: "请输入", type: 'input',content:'' },
{ label: '用户状态', placeholder: "请选择", type: 'select',content:'' , options: [{label:'启用', value:'启用'}, {label:'禁用', value:'禁用'}] },
{ label: '用户角色', placeholder: "请选择", type: 'select', content:'' ,options: [{label:'管理员', value:'管理员'}, {label:'巡查员', value:'巡查员'}]},
])
// 响应式布局配置
const layoutConfig = reactive({
containerMinHeight: '100vh',
cardSpacing: '1rem',
statsMinHeight: '300px'
});
const isUserInfoDialogVisible = ref(false); // 控制用户信息弹窗的显示状态
/**
* 刷新表格数据的方法
* 当调用此方法时,会触发表格数据的重新加载
*/
const tableDataRefresh = () => {
initTableData()
console.log("000000-------2-----0000000000,这里获取到最新的数据")
}
/**
* 控制编辑抽屉的显示状态
* @type {Ref<boolean>} - 响应式布尔值,用于控制编辑抽屉的显示
*/
const isEditDrawer = ref(false)
/**
* 存储行数据
* @type {Ref<Array>} - 响应式数组,用于存储当前选中的行数据
*/
const rowData = ref([])
/**
* 编辑数据的方法
* @param {Object} data - 要编辑的数据对象
*/
const editData = (data) => {
// 打开编辑抽屉
isEditDrawer.value = true
// 设置要编辑的数据
rowData.value = data
console.log("000000-------1-----0000000000",data)
}
/**
* 删除数据的方法
* @param {Object} data - 要删除的数据对象
* 显示确认对话框,用户确认后执行删除操作
*/
const deleteData = (data) => {
console.log("000000---2---------0000000000",data)
ElMessageBox.confirm(
'删除操作不可逆,是否继续?',
'删除',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
draggable: true,
}
)
.then(() => {
console.log("000000---1-456464564------0000000000","我是确认")
try {
const res = staffRemoveInfoApi({id:data.id});
if (res.code === 200) {
ElMessage({ type: 'success', message: '已删除' });
tableDataRefresh();
} else {
ElMessage({ type: 'error', message: '删除失败:' + res.msg });
}
} catch (err) {
// API 请求失败时在此捕获,不向上冒泡
ElMessage({ type: 'error', message: '删除失败(网络错误)' });
// console.error('删除接口失败:', err);
}
})
.catch(() => {
ElMessage({
type: 'info',
message: '取消删除',
})
})
}
// const deleteUserDate = (data) => {
// staffRemoveInfoApi(data).then((res) => {
// console.log(res,'--------------8--');
// if(res.code==200){
// ElMessage({
// type: 'success',
// message: '已删除',
// })
// tableDataRefresh()
// }
// }).catch(() => {
// ElMessage({
// type: 'info',
// message: '删除shibai',
// })
// })
// }
const isResetPasswordDialogVisible = ref(false); // 控制重置密码弹窗的显示状态
const resetPasswordData = ref({});
const resetData = (data) => {
console.log("000000-----3-------0000000000",data)
isResetPasswordDialogVisible.value = true;
resetPasswordData.value = data;
}
const showuserinfodata = ref({})
const userData = (data) => {
console.log("000000------4------0000000000",data)
isUserInfoDialogVisible.value = true
showuserinfodata.value = data
}
const tableShowData = ref([{
tableHeader:[
{label: '姓名', prop: 'name'},
{label: '电话', prop: 'phone'},
{label: '是否可用', prop: 'status'},
{label: '用户权限', prop: 'role'},
{label: '创建时间', prop: 'createTime'},
{label: '更新时间', prop: 'updateTime'},
{label: '操作', prop: 'Operation'}
],
tableBody:[
]
}
])
const Operation = ref( [ { label: '编辑', type: 'primary', icon: 'EditPen', click: editData },
{ label: '删除', type: 'danger', icon: 'Delete', click: deleteData },
{ label: '用户信息', type: 'primary', icon: 'EditPen', click: userData },
{ label: '重置密码', type: 'primary', icon: 'EditPen', click: resetData }]
)
const initTableData = () => {
staffGetPageInfoApi({
"currentPageNum": 1,
"currentPageSize": 10,
}).then(res => {
if(res.code === 200){
let tempData = res.data.list
tableShowData.value[0].tableBody = []
//获取未逻辑删除的数据
console.log(res.data,"--------999999999----")
tempData = tempData.filter(element => {
return element.isDeleted === 0;
});
tempData.forEach(element => {
element.Operation = [...Operation.value]
});
tableShowData.value[0].tableBody = tempData
tableShowData.value[0].total = res.data.total
tableShowData.value[0].pageSize = res.data.pageSize
console.log(tableShowData.value, '展示数据');
}
}).catch(err => {
ElMessage.error(err.message)
})
}
onMounted(() => {
// console.log(tableShowData.value, '错误');
initTableData()
})
// 计算容器样式
const containerStyle = computed(() => ({
minHeight: layoutConfig.containerMinHeight,
'--card-spacing': layoutConfig.cardSpacing,
}));
</script>
<style scoped>
.dashboard-container {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: rgba(0, 0, 0, 0.05);
color: #333;
}
.main-content {
display: flex;
gap: var(--card-spacing);
padding: 0.4rem;
height: 100vh;
box-sizing: border-box;
}
.left-section {
flex: 2;
display: flex;
flex-direction: column;
gap: var(--card-spacing);
height: 100%;
}
.left-top {
height: auto;
}
/* 通用卡片样式 */
.info-card {
background-color: #fff;
padding: 1rem;
border-radius: 8px;
box-sizing: border-box;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
overflow: auto;
}
.region-overview {
flex: 1;
min-width: 200px;
}
/* 统计区域 */
.stats-alerts {
display: flex;
flex-direction: column;
gap: 1rem;
height: 79.5%;
overflow: auto;
}
.card-tabs {
display: flex;
border-bottom: 1px solid #292828;
/* margin-bottom: 1rem; */
font-size: 1.2rem;
color: #000000;
/* background-color: #002aff; */
height: 8%;
display: flex;
align-items: center;
width: 100%;
padding: 10px;
}
.title{
width: 70%;
}
.card-actions {
display: flex;
flex: 1;
justify-content: space-between;
align-items: center;
/* margin-bottom: 1rem; */
height: 8%;
padding: 1.5rem;
}
.operation-button {
width: 15%;
}
.card-tabs-footer {
display: flex;
/* margin-bottom: 1rem; */
font-size: 1.2rem;
color: #000000;
/* background-color: #002aff; */
height: 8%;
align-items: center;
width: 100%;
padding: 10px 0;
justify-content: flex-end;
align-items: center;
}
/* 文本样式 */
.page-text {
color: #606266;
font-size: 14px;
white-space: nowrap; /* 防止文本换行 */
}
/* 修复 Element Plus 分页组件默认的块级布局问题 */
:deep .el-pagination {
display: flex;
align-items: center;
margin: 0; /* 清除默认外边距 */
}
.pagination-block{
height: 8%;
}
.card-table{
width: 100%;
/* display: flex; */
/* background-color: #000000; */
flex:1;
overflow: hidden;
border-radius: 8PX;
}
/* 响应式适配 */
@media (max-width: 1024px) {
.left-top {
flex-direction: column;
}
.region-overview {
width: 100%;
flex: none;
}
}
@media (max-width: 768px) {
.stats-alerts {
flex-direction: column;
}
}
</style>
\ No newline at end of file
<template>
<div class="add-user-modal">
<!-- 标题栏 -->
<div class="modal-header">
<h2 class="title">新增用户</h2>
<el-icon class="close-icon" @click="handleClose" size="20">
<Close />
</el-icon>
</div>
<!-- 表单主体 -->
<el-form
ref="addUserFormRef"
:model="form"
:rules="rules"
label-width="100px"
label-position="left"
class="add-user-form"
status-icon
>
<!-- 用户账号 -->
<el-form-item label="用户账号" prop="name" class="form-item">
<el-input
v-model="form.name"
placeholder="请输入用户账号"
class="input-field"
prefix-icon="User"
clearable
/>
</el-form-item>
<!-- 用户密码 -->
<el-form-item label="用户密码" prop="password" class="form-item">
<el-input
v-model="form.password"
placeholder="请输入用户密码"
type="password"
class="input-field"
prefix-icon="Lock"
show-password
clearable
/>
</el-form-item>
<!-- 手机号 -->
<el-form-item label="手机号" prop="phone" class="form-item">
<el-input
v-model="form.phone"
placeholder="请输入手机号"
class="input-field"
prefix-icon="Phone"
maxlength="11"
clearable
/>
</el-form-item>
<!-- 性别 -->
<el-form-item label="性别" prop="gender" class="form-item">
<el-radio-group v-model="form.gender" class="radio-group">
<el-radio :label="1" class="radio-btn" size="large"></el-radio>
<el-radio :label="2" class="radio-btn" size="large"></el-radio>
</el-radio-group>
</el-form-item>
<!-- 状态 -->
<el-form-item label="状态" prop="status" class="form-item">
<el-radio-group v-model="form.status" class="radio-group">
<el-radio :label="0" class="radio-btn" size="large">正常</el-radio>
<el-radio :label="1" class="radio-btn" size="large">停用</el-radio>
</el-radio-group>
</el-form-item>
<!-- 角色 -->
<el-form-item label="角色" prop="role" class="form-item">
<el-select
v-model="form.role"
placeholder="请选择角色"
class="select-field"
prefix-icon="UserFilled"
>
<el-option
v-for="item in roleOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<!-- 头像上传 -->
<el-form-item label="头像上传" prop="avatar" class="form-item">
<div class="avatar-uploader">
<UpdataImg @getAvatar="getAvatar" :updataImageCount="1" ref="avatarUploader"></UpdataImg>
</div>
</el-form-item>
<!-- 备注 -->
<el-form-item label="备注" prop="remark" class="form-item">
<el-input
v-model="form.remark"
type="textarea"
placeholder="请输入备注信息"
:rows="3"
class="textarea-field"
show-word-limit
maxlength="200"
/>
</el-form-item>
</el-form>
<!-- 底部按钮 -->
<div class="modal-footer">
<el-button @click="handleCancel" class="cancel-btn" size="default">取消</el-button>
<el-button
@click="handleSubmit"
type="primary"
class="confirm-btn"
size="default"
:loading="submitting"
>
确认
</el-button>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Close, } from '@element-plus/icons-vue';
import UpdataImg from './updataImg.vue';
import {staffAddInfoApi} from '../../../api/staff'
// 表单引用
const addUserFormRef = ref(null);
// 提交加载状态
const submitting = ref(false);
const avatarUploader = ref(null);
const emit = defineEmits(['closeAddDrawer','tableDataRefresh']);
// 角色选项数据
const roleOptions = ref([
{ label: '管理员', value: 0 },
{ label: '巡察员', value: 1 }
]);
// 表单数据
const form = reactive({
name: '', // 用户账号
password: '', // 用户密码
phone: '', // 手机号
gender: 1, // 性别:1-男,2-女
status: 0, // 状态:0-正常,1-停用
role: '', // 角色
avatar: '', // 头像URL
remark: '' // 备注
});
const getAvatar = (data) => {
console.log("000000000000000000000")
form.avatar = data;
}
// 表单验证规则
const rules = reactive({
name: [
{ required: true, message: '请输入正确的汉字', trigger: 'blur' },
{ min: 2, max: 10, message: '账号长度在4-20个字符之间', trigger: 'blur' },
{ pattern: /^[\u4e00-\u9fa5]+$/, message: '账号只能包含汉字', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入用户密码', trigger: 'blur' },
{ min: 6, max: 30, message: '密码长度在6-30个字符之间', trigger: 'blur' },
{ pattern: /^(?=.*[a-zA-Z])(?=.*\d).+$/, message: '密码需包含字母和数字', trigger: 'blur' }
],
phone: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号格式', trigger: 'blur' }
],
role: [
{ required: true, message: '请选择角色', trigger: 'change' }
]
});
// // 头像上传前验证
// const beforeAvatarUpload = (rawFile) => {
// const isJpgOrPng = rawFile.type === 'image/jpeg' || rawFile.type === 'image/png';
// const isLt2M = rawFile.size / 1024 / 1024 < 2;
// if (!isJpgOrPng) {
// ElMessage.error('上传头像图片只能是 JPG/PNG 格式!');
// }
// if (!isLt2M) {
// ElMessage.error('上传头像图片大小不能超过 2MB!');
// }
// return isJpgOrPng && isLt2M;
// };
// // 头像上传处理(自定义上传)
// const handleAvatarUpload = async (options) => {
// try {
// const formData = new FormData();
// formData.append('file', options.file);
// // 模拟上传请求
// await new Promise(resolve => setTimeout(resolve, 1000));
// // 模拟返回的头像URL
// const avatarUrl = `https://example.com/avatars/${Date.now()}.jpg`;
// form.avatar = avatarUrl;
// options.onSuccess({ data: { url: avatarUrl } });
// } catch (error) {
// options.onError(error);
// ElMessage.error('头像上传失败,请重试');
// }
// };
// // 头像上传成功回调
// const handleAvatarSuccess = (response) => {
// form.avatar = response.data.url;
// ElMessage.success('头像上传成功');
// };
// 提交表单
const handleSubmit = () => {
try {
submitting.value = true;
// 准备提交数据
const submitData = {
...form,
avatar:form.avatar,
};
// console.log('提交新增用户数据:', submitData);
// 模拟API请求
console.log('提交新增用户数据:', submitData,form.avatar);
staffAddInfoApi(submitData).then(res => {
console.log('用户新增成功', res);
if (res.code === 200) {
// 成功后关闭抽屉
emit('closeAddDrawer');
// 通知父组件刷新列表
emit('tableDataRefresh');
ElMessage.success('用户新增成功!');
addUserFormRef.value.resetFields();
// avatarUploader.value.handleRemove(form.avatar[0])
}
}).catch(err => {
ElMessage.error('用户新增失败');
});
} catch (error) {
console.error('表单验证失败:', error);
} finally {
submitting.value = false;
}
};
// 取消操作
const handleCancel = () => {
ElMessageBox.confirm('确定要取消新增吗?已填写内容将不保存', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type:'warning',
center: true
}).then(() => {
addUserFormRef.value.resetFields();
emit('closeAddDrawer');
}).catch(() => {});
};
// 关闭弹窗
const handleClose = () => {
// 通知父组件关闭弹窗
// emit('close');
addUserFormRef.value.resetFields();
handleCancel()
};
onMounted(() => {
console.log('新增组件已挂载');
});
</script>
<style scoped lang="scss">
.add-user-modal {
width: 100%;
max-width: 550px;
padding: 24px;
box-sizing: border-box;
background-color: #fff;
border-radius: 8px;
// 标题栏样式
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
padding-bottom: 16px;
border-bottom: 1px solid #f0f0f0;
.title {
margin: 0;
font-size: 18px;
font-weight: 500;
color: #1f2937;
}
.close-icon {
cursor: pointer;
color: #6b7280;
transition: all 0.2s;
&:hover {
color:#ef4444;
background-color: #f8fafc;
border-radius: 50%;
}
}
}
// 表单样式
.add-user-form {
.form-item {
margin-bottom: 20px;
}
.input-field, .select-field {
width: 100%;
max-width: 320px;
transition: all 0.2s;
.el-input__prefix-inner {
margin-right:.5rem;
}
}
.textarea-field {
width: 100%;
max-width: 320px !important;
resize: vertical;
}
// 单选框样式
.radio-group {
display: flex;
gap: 24px;
padding: 4px 0;
.radio-btn {
padding: 6px 16px !important;
border-radius: 4px !important;
transition: all 0.2s;
& .el-radio__label {
font-size: 14px;
}
&:hover:not(.is-checked) {
background-color: #f5f5f5;
}
}
}
// 选中状态样式
:deep(.el-radio.is-checked .el-radio__inner) {
border-color: #4096ff;
background-color: #4096ff;
}
:deep(.el-radio__input.is-checked + .el-radio__label) {
color: #4096ff;
}
}
// 头像上传样式
.avatar-uploader {
margin-bottom: 12px;
.avatar-upload {
width: 120px !important;
height: 120px !important;
border: 1px dashed #d1d5db;
border-radius: 8px;
cursor:pointer;
position: relative;
overflow: hidden;
transition: all 0.3s;
&:hover {
border-color: #4096ff;
background-color: #f0f9ff;
}
.avatar-img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s;
&:hover {
transform: scale(1.02);
}
}
.avatar-placeholder {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #f9fafb;
color: #6b7280;
gap: 8px;
.avatar-icon {
font-size: 28px;
}
.upload-text {
font-size: 14px;
}
}
}
.upload-tip {
margin: 8px 0 0;
font-size: 12px;
color: #9ca3af;
line-height: 1.5;
}
}
// 底部按钮样式
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 16px;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #f0f0f0;
.cancel-btn, .confirm-btn {
width: 110px;
height: 40px;
transition: all 0.2s;
&:hover {
transform: translateY(-1px);
}
}
.confirm-btn {
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
}
}
// 响应式适配
@media (max-width: 768px) {
padding: 16px;
max-width: 100%;
.add-user-form {
.input-field, .select-field, .textarea-field {
max-width: 100% !important;
}
.radio-group {
gap: 16px;
flex-wrap: wrap;
}
}
.modal-footer {
justify-content: center;
gap: 12px;
margin-top: 24px;
}
}
}
</style>
\ No newline at end of file
<template> <template>
<div> <div>
<el-table <vxe-table
style="width: 100%;"
ref="tableRef"
@checkbox-change="selectTableDateFn"
@checkbox-all="selectTableDateFn"
@edit-closed="handleAfterEdit"
show-overflow
:edit-config="{trigger: 'click', mode: 'cell'}"
:data="tableShowData">
<vxe-column field="checkbox" type="checkbox" width="50"></vxe-column>
<vxe-column field="id" title="火情编号" min-width="80" ></vxe-column>
<vxe-column field="name" title="建筑名称" min-width="150" ></vxe-column>
<vxe-column field="floorcount" title="火情楼层" min-width="150" ></vxe-column>
<vxe-column field="reporter" title="上报人" min-width="150" ></vxe-column>
<vxe-column field="fireLevel" title="火情等级" width="150" :edit-render="fireLevelEditRender" ></vxe-column>
<vxe-column field="updateTime" title="上报时间" min-width="150" ></vxe-column>
<vxe-column field="discription" title="火情描述" :edit-render="fireDescripEditRender" min-width="150" ></vxe-column>
<vxe-column title="详情查看" min-width="220" >
<template #default="scope">
<el-button @click="handleClick(scope.row)">图片查看</el-button>
<el-button @click="handleVideoDialogFn(scope.row)">视频查看</el-button>
</template>
</vxe-column>
<vxe-column title="操作" min-width="220" >
<template #default="scope">
<el-button @click="taskSchedulingFn(scope.row)">任务调度</el-button>
</template>
</vxe-column>
</vxe-table>
</div>
<div>
<!-- <el-table
:data="tableShowData" :data="tableShowData"
style="width: 100%" style="width: 100%"
@select="selectTableDateFn" @select="selectTableDateFn"
...@@ -13,11 +55,21 @@ ...@@ -13,11 +55,21 @@
<el-table-column prop="floorcount" label="火情楼层" width="120" /> <el-table-column prop="floorcount" label="火情楼层" width="120" />
<el-table-column prop="reporter" label="上报人" width="120" /> <el-table-column prop="reporter" label="上报人" width="120" />
<el-table-column prop="updateTime" label="上报时间" width="220" /> <el-table-column prop="updateTime" label="上报时间" width="220" />
<el-table-column prop="fireLevel" label="火情等级" width="220" /> <el-table-column label="火情等级" width="220" >
<template #default="scope">
<el-select v-model="scope.row.fireLevel" placeholder="Select" >
<el-option
v-for="item in fireLevelOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column prop="discription" label="火情描述" /> <el-table-column prop="discription" label="火情描述" />
<el-table-column <el-table-column
label="详情查看" label="详情查看"
show-overflow-tooltip show-overflow-tooltip
> >
<template #default="scope"> <template #default="scope">
...@@ -37,7 +89,7 @@ ...@@ -37,7 +89,7 @@
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table> -->
<!-- 分页区域(固定在右下角) --> <!-- 分页区域(固定在右下角) -->
<div class="card-tabs-footer" > <div class="card-tabs-footer" >
<div class="pagination-block"> <div class="pagination-block">
...@@ -136,7 +188,46 @@ ...@@ -136,7 +188,46 @@
</template> </template>
<script setup> <script setup>
import { message } from 'ant-design-vue'
const fireLevelEditRender = reactive({
name: 'select',
options: [
{ label: '一级火情', value: '1' },
{ label: '二级火情', value: '2' },
{ label: '三级火情', value: '3' },
{ label: '四级火情', value: '4' },
]
})
const fireDescripEditRender = reactive({
name: 'input',
})
const handleAfterEdit = ({ row, column }) => {
console.log('编辑完成后的行数据:', row);
// 可以在这里提交数据或做其他操作
return true; // 返回 true 表示编辑成功,false 会阻止修改
};
const fireLevelOptions = [
{ label: '一级火情', value: '1' },
{ label: '二级火情', value: '2' },
{ label: '三级火情', value: '3' },
{ label: '四级火情', value: '4' },
{ label: '五级火情', value: '5' },
]
const emit = defineEmits(['archiveInfoDateBackcallFn']) const emit = defineEmits(['archiveInfoDateBackcallFn'])
const props = defineProps({ const props = defineProps({
tableShowData: { tableShowData: {
...@@ -185,9 +276,18 @@ const taskSchedulingFn = (info) => { ...@@ -185,9 +276,18 @@ const taskSchedulingFn = (info) => {
console.log("任务调度",info) console.log("任务调度",info)
isTaskSchedulingDialogVisible.value = true isTaskSchedulingDialogVisible.value = true
} }
const selectTableDateFn = (selectedRows) => { const tableRef = ref() // 确保这里声明了 tableRef
console.log('selectTableDateFn选择的数据信息',selectedRows) const selectTableDateFn = () => {
emit('archiveInfoDateBackcallFn',selectedRows)
const $table = tableRef.value
if ($table) {
emit('archiveInfoDateBackcallFn',$table.getCheckboxRecords())
}else{
console.log('未找到表格实例')
message.error('未找到表格实例')
}
// console.log('selectTableDateFn选择的数据信息',tableRef.value)
// emit('archiveInfoDateBackcallFn',selectedRows)
} }
// 分页数据 // 分页数据
......
...@@ -14,9 +14,10 @@ ...@@ -14,9 +14,10 @@
<!-- 左侧下部分:统计区域 --> <!-- 左侧下部分:统计区域 -->
<div class="info-card stats-alerts"> <div class="info-card stats-alerts">
<div class="card-tabs"> <div class="card-tabs">
<span class="title">当前火情</span> <span class="title">历史火情</span>
<div class="card-actions"> <div class="card-actions">
<el-button class="operation-button" type="primary" @cliick="archiveInfoFn" >归档</el-button> <el-button class="operation-button" type="primary" @cliick="archiveInfoFn" >归档</el-button>
<el-button class="operation-button" type="primary" @cliick="archiveInfoFn1" >归档1</el-button>
</div> </div>
</div> </div>
<div class="card-table"> <div class="card-table">
...@@ -25,6 +26,15 @@ ...@@ -25,6 +26,15 @@
</div> </div>
</div> </div>
</main> </main>
<!-- 归档窗口 -->
<el-drawer
v-model="archiveInfoDrawer"
direction="rtl"
:before-close="handleClose"
>
asdada
</el-drawer>
</div> </div>
</template> </template>
...@@ -73,11 +83,24 @@ const archiveInfoDateBackcallFn = (row) => { ...@@ -73,11 +83,24 @@ const archiveInfoDateBackcallFn = (row) => {
archiveInfoDate.value = row archiveInfoDate.value = row
console.log('archiveInfoFn需要归档的数据',archiveInfoDate.value) console.log('archiveInfoFn需要归档的数据',archiveInfoDate.value)
}; };
const archiveInfoDrawer = ref(false)
const archiveInfoFn = () => { const archiveInfoFn = () => {
if ( archiveInfoDate ===undefined ||archiveInfoDate.value===null || archiveInfoDate.value.length<=0 ) {
ElMessage.warning("没有勾选数据")
}else {
console.log('archiveInfoFn归档的数据',archiveInfoDate.value) console.log('archiveInfoFn归档的数据',archiveInfoDate.value)
}; // archiveInfoDrawer.value = true
fireInfoDataImport.value = null
}
}
const archiveInfoFn1 = () => {
ElMessage.warning("没有勾选数据")
}
const handleAddInfoFn = () => { const handleAddInfoFn = () => {
isAddDrawer.value = true; isAddDrawer.value = true;
} }
...@@ -166,7 +189,7 @@ const containerStyle = computed(() => ({ ...@@ -166,7 +189,7 @@ const containerStyle = computed(() => ({
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: rgba(0, 0, 0, 0.05); background-color: rgba(0, 0, 0, 0.05);
color: #333; color: #333;
overflow: hidden; /* overflow: hidden; */
} }
.main-content { .main-content {
...@@ -206,6 +229,7 @@ const containerStyle = computed(() => ({ ...@@ -206,6 +229,7 @@ const containerStyle = computed(() => ({
.stats-alerts { .stats-alerts {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1;
gap: 1rem; gap: 1rem;
height: 67%; height: 67%;
} }
...@@ -218,6 +242,7 @@ const containerStyle = computed(() => ({ ...@@ -218,6 +242,7 @@ const containerStyle = computed(() => ({
height: 8%; height: 8%;
align-items: center; align-items: center;
width: 100%; width: 100%;
/* background-color: aqua; */
padding: 10px; padding: 10px;
} }
...@@ -243,7 +268,7 @@ const containerStyle = computed(() => ({ ...@@ -243,7 +268,7 @@ const containerStyle = computed(() => ({
.card-table { .card-table {
width: 100%; width: 100%;
flex: 1; flex: 1;
overflow: hidden; /* overflow: hidden; */
border-radius: 8px; border-radius: 8px;
} }
......
<template>
<div class="fire-report-detail">
<!-- 页面标题 -->
<div class="report-header">
<h2>火情上报详情</h2>
<span class="close-btn" @click="handleClose"> <el-icon><Close /></el-icon></span>
</div>
<!-- 表单内容 -->
<div class="report-content">
<!-- 基本信息区域 -->
<div class="info-section">
<div class="info-item">
<label class="info-label">上报人:</label>
<div class="info-value">
<input type="text" placeholder="请输入" class="info-value" disabled :value="`${userAppStoreInstance.userInfo.name}`">
</div>
</div>
<div class="info-item">
<label class="info-label">着火建筑:</label>
<div class="info-value"><input type="text" placeholder="请输入" class="info-value" ></div>
</div>
<div class="info-item">
<label class="info-label">着火楼层:</label>
<div class="info-value"><input type="text" placeholder="请输入" class="info-value" ></div>
</div>
</div>
<!-- 图片区域 -->
<div class="media-section">
<h3 class="section-title">现场图片</h3>
<div class="image-container">
<updataImg :updataImageCount="1"></updataImg>
</div>
</div>
<!-- 视频区域 -->
<div class="media-section">
<h3 class="section-title">现场视频</h3>
<div class="video-container">
<video src="" controls></video>
</div>
</div>
<!-- 描述区域 -->
<div class="description-section">
<h3 class="section-title">描述</h3>
<el-input
type="textarea"
placeholder="请输入"
class="description-input"
:rows="4"
/>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import useAppStore from '../../../../store/module/app';
import updataImg from '../../../systemmanage/components/updataImg.vue';
const userAppStoreInstance = useAppStore();
// 关闭按钮事件
const handleClose = () => {
// 这里可以添加关闭当前页面或弹窗的逻辑
console.log('关闭火情上报详情');
};
// 监听窗口大小变化,实现自适应
const handleResize = () => {
// 可以在这里添加需要响应式调整的逻辑
console.log('窗口大小变化');
};
onMounted(() => {
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
});
</script>
<style scoped lang="scss">
.fire-report-detail {
width: 100%;
height: 90%;
min-height: 100vh;
padding: 10px;
box-sizing: border-box;
background-color: #f9f9f9;
.report-header {
display: flex;
justify-content: space-between;
align-items: center;
// margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #e0e0e0;
h2 {
margin: 0;
font-size: 1.5rem;
color: #333;
}
.close-btn {
padding: 5px 10px;
}
.close-btn:hover{
background-color: rgb(118, 143, 168,0.1);
cursor: pointer
}
}
.report-content {
max-width: 1200px;
margin: 0 auto;
}
.info-section {
display: flex;
flex-direction: column; /* 子元素竖直排列 */
gap: 16px; /* info-item 之间的间距(竖直方向) */
padding: 10px; /* 可选:整体内边距 */
.info-item {
display: flex; /* 内部元素水平排列 */
align-items: center; /* 垂直居中对齐 */
gap: 12px; /* label 和 value 之间的间距(水平方向) */
.info-label {
width: 80px; /* 固定宽度,确保所有 label 对齐 */
font-weight: 500; /* 可选:加粗标签文字 */
color: #666; /* 可选:标签文字颜色 */
}
.info-value {
flex: 1; /* 占满剩余宽度 */
padding: 0px 4px; /* 内边距,增强可读性 */
border-radius: 4px; /* 可选:圆角边框 */
min-height: 36px; /* 可选:固定高度,确保对齐 */
display: flex; /* 内容垂直居中 */
align-items: center;
}
}
}
.section-title {
font-size: 1.1rem;
color: #333;
margin-bottom: 15px;
padding-left: 5px;
border-left: 3px solid #409eff;
}
.media-section {
margin-bottom: 30px;
.image-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
.image-item {
aspect-ratio: 4/3;
background-color: #f0f0f0;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
.placeholder-image {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #999;
.image-icon {
font-size: 2rem;
}
}
}
}
.video-container {
width: 100%;
.video-placeholder {
width: 100%;
aspect-ratio: 16/9;
background: linear-gradient(to bottom, #444, #222);
border-radius: 4px;
position: relative;
overflow: hidden;
.video-overlay {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #fff;
font-size: 1rem;
text-align: center;
}
.video-controls {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 10px;
background: linear-gradient(transparent, rgba(0,0,0,0.7));
color: #fff;
display: flex;
align-items: center;
gap: 15px;
.play-btn {
background: rgba(0,0,0,0.5);
color: #fff;
width: 36px;
height: 36px;
border-radius: 50%;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
}
.video-time {
font-size: 0.8rem;
min-width: 80px;
}
.video-progress {
flex: 1;
height: 4px;
background-color: rgba(255,255,255,0.3);
border-radius: 2px;
position: relative;
.progress-bar {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 30%;
background-color: #409eff;
border-radius: 2px;
.progress-indicator {
position: absolute;
right: 0;
top: 50%;
transform: translate(50%, -50%);
width: 12px;
height: 12px;
background-color: #fff;
border-radius: 50%;
box-shadow: 0 0 5px rgba(0,0,0,0.3);
}
}
}
.volume-btn, .fullscreen-btn {
color: #fff;
background: transparent;
padding: 5px;
}
}
}
}
}
.description-section {
.description-input {
width: 100%;
min-height: 120px;
}
}
}
/* 响应式调整 */
@media (max-width: 768px) {
.fire-report-detail {
padding: 15px 10px;
.info-section {
grid-template-columns: 1fr;
}
.image-container {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.video-controls {
gap: 10px;
.video-time {
display: none;
}
}
}
}
</style>
\ No newline at end of file
...@@ -19,10 +19,12 @@ ...@@ -19,10 +19,12 @@
<!-- 左侧下部分:统计区域 --> <!-- 左侧下部分:统计区域 -->
<div class="info-card stats-alerts"> <div class="info-card stats-alerts">
<div class="card-tabs"> <div class="card-tabs">
<span class="title" >单次火情汇报列表</span> <span class="title" >火情汇报列表</span>
<div class="card-actions"> <div class="card-actions">
<el-button class="operation-button" type="primary" size="small" @click="fireInfoDataImportFn" >导入</el-button> <!-- <el-button type="danger" plain>Danger</el-button> -->
<el-button class="operation-button" type="danger" plain @click="fireInfoDataDeleteFn" >撤销归档</el-button>
<el-button class="operation-button" type="danger" plain @click="fireInfoDataDeleteFn" >删除</el-button>
<el-button class="operation-button" type="primary" @click="addFireInfoDataImportFn" >新增</el-button>
</div> </div>
</div> </div>
<div class="card-table"> <div class="card-table">
...@@ -33,8 +35,13 @@ ...@@ -33,8 +35,13 @@
</div> </div>
</div> </div>
</main> </main>
<!-- 新增添加框 -->
<el-drawer
v-model="addFireInfoDataImportFndrawer"
:with-header="false"
>
<addFireInfo></addFireInfo>
</el-drawer>
</div> </div>
</template> </template>
...@@ -45,21 +52,35 @@ import { reactive, computed, ref, onMounted } from 'vue'; ...@@ -45,21 +52,35 @@ import { reactive, computed, ref, onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import addFireInfo from './addFireInfo.vue';
import Tableshowdetail from './tableshowdetail.vue'; import Tableshowdetail from './tableshowdetail.vue';
const addFireInfoDataImportFndrawer = ref(false);
const addFireInfoDataImportFn = () => {
addFireInfoDataImportFndrawer.value = true
}
const fireInfoDataImport = ref(null); const fireInfoDataImport = ref(null);
const fireInfoDataImportBackcallFn = (info) => { const fireInfoDataImportBackcallFn = (info) => {
fireInfoDataImport.value = info fireInfoDataImport.value = info
} }
const fireInfoDataDeleteFn = () => {
if ( fireInfoDataImport ===undefined ||fireInfoDataImport.value===null || fireInfoDataImport.value.length<=0 ) {
ElMessage.warning("没有勾选数据")
}else {
console.log("删除数据",fireInfoDataImport.value)
fireInfoDataImport.value = null
}
}
const fireInfoDataImportFn = () => { const fireInfoDataImportFn = () => {
// if ( fireInfoDataImport.value===undefined ||fireInfoDataImport.value===null) { if ( fireInfoDataImport ===undefined ||fireInfoDataImport.value===null || fireInfoDataImport.value.length<=0 ) {
// ElMessage.warning("没有勾选数据") ElMessage.warning("没有勾选数据")
// } }else {
console.log("导入数据",fireInfoDataImport.value) console.log("导入数据",fireInfoDataImport.value)
fireInfoDataImport.value = null fireInfoDataImport.value = null
}
} }
// import Operation from 'ant-design-vue/es/transfer/operation'; // import Operation from 'ant-design-vue/es/transfer/operation';
...@@ -301,6 +322,7 @@ const containerStyle = computed(() => ({ ...@@ -301,6 +322,7 @@ const containerStyle = computed(() => ({
border-radius: 8px; border-radius: 8px;
box-sizing: border-box; box-sizing: border-box;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
/* overflow: auto; */ /* overflow: auto; */
} }
...@@ -320,11 +342,11 @@ const containerStyle = computed(() => ({ ...@@ -320,11 +342,11 @@ const containerStyle = computed(() => ({
.card-tabs { .card-tabs {
display: flex; display: flex;
border-bottom: 1px solid #8a8787; /* border-bottom: 1px solid #8a8787; */
font-size: 1.2rem; font-size: 1.2rem;
color: #000000; color: #000000;
padding: 1.3rem;
height: 8%; height: 8%;
display: flex; display: flex;
align-items: center; align-items: center;
......
...@@ -47,9 +47,48 @@ ...@@ -47,9 +47,48 @@
/> />
</div> </div>
</div> </div>
<!-- 火情模拟对话框 -->
<el-dialog
v-model="isFireSimulationDialogVisible"
width="800px"
:before-close="handleClose"
custom-class="task-scheduling-dialog"
>
<!-- 标题区域 -->
<template #header>
<div class="dialog-header">
<span class="dialog-title">火情模拟</span>
</div>
</template>
<!-- 内容区域 -->
<div class="dialog-content">
<div class="notification-box">
<el-icon class="notification-icon"><Bell /></el-icon>
<span class="notification-text">系统已开始进行火情模拟,点击跳转按钮可以进入大屏观察火情模拟</span>
</div>
</div> </div>
<!-- 底部按钮 -->
<template #footer>
<div class="dialog-footer">
<el-button type="primary" class="action-button">
<template #icon><VideoPlay /></template>
前往
</el-button>
<el-button class="action-button">
确认
</el-button>
</div>
</template>
</el-dialog>
</div>
</template> </template>
<script setup> <script setup>
...@@ -62,13 +101,14 @@ const props = defineProps({ ...@@ -62,13 +101,14 @@ const props = defineProps({
}, },
}) })
const isFireSimulationDialogVisible = ref(false)
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const imageRef = ref(null) const imageRef = ref(null)
const showPreview = ref(false) const showPreview = ref(false)
const fireSimulationFn = (info) => { const fireSimulationFn = (info) => {
isFireSimulationDialogVisible.value = true
console.log("火情模拟",info) console.log("火情模拟",info)
} }
...@@ -81,7 +121,8 @@ const exportReportFn = (info) => { ...@@ -81,7 +121,8 @@ const exportReportFn = (info) => {
const detailViewsFn = (info) => { const detailViewsFn = (info) => {
console.log("详情查看",info,route) console.log("详情查看",info,route)
router.push({ router.push({
path: '/historyfire/floordetaildate', name: 'floorDetailDate',
}) })
......
...@@ -14,7 +14,18 @@ ...@@ -14,7 +14,18 @@
<el-table-column prop="reporter" label="上报人员" width="120" /> <el-table-column prop="reporter" label="上报人员" width="120" />
<el-table-column prop="updateTime" label="上报时间" width="120" /> <el-table-column prop="updateTime" label="上报时间" width="120" />
<el-table-column prop="discription" label="火情描述" width="220" show-overflow-tooltip/> <el-table-column prop="discription" label="火情描述" width="220" show-overflow-tooltip/>
<el-table-column prop="fireLevel" label="火情等级" width="120" /> <el-table-column prop="fireLevel" label="火情等级" width="120" show-overflow-tooltip>
<template #default="scope">
<el-select v-model="scope.row.fireLevel" >
<el-option
v-for="item in fireOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column prop="firePoint" label="着火点" width="220" show-overflow-tooltip /> <el-table-column prop="firePoint" label="着火点" width="220" show-overflow-tooltip />
<el-table-column <el-table-column
label="详情查看" label="详情查看"
...@@ -98,7 +109,14 @@ const emit = defineEmits(['fireInfoDataImportBackcallFn']) ...@@ -98,7 +109,14 @@ const emit = defineEmits(['fireInfoDataImportBackcallFn'])
// const props = defineProps(['tableShowData']) // const props = defineProps(['tableShowData'])
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
const fireOptions = [
{ label: '一级火情', value: '1' },
{ label: '二级火情', value: '2' },
{ label: '三级火情', value: '3' },
{ label: '四级火情', value: '4' },
{ label: '五级火情', value: '5' },
{ label: '六级火情', value: '6' },
]
const tableShowData =[{ const tableShowData =[{
id: 1, id: 1,
fireNo:12, fireNo:12,
...@@ -108,7 +126,7 @@ const tableShowData =[{ ...@@ -108,7 +126,7 @@ const tableShowData =[{
reporter:"张三", reporter:"张三",
updateTime:"2022-05-04", updateTime:"2022-05-04",
discription: 'Lorem ipsum dolor sit ametisl nisl nisl nisl nisl nisl nisl nisl nis', discription: 'Lorem ipsum dolor sit ametisl nisl nisl nisl nisl nisl nisl nisl nis',
fireLevel: '1', fireLevel: '1',
firePoint: '厨房', firePoint: '厨房',
}, },
...@@ -121,7 +139,7 @@ const tableShowData =[{ ...@@ -121,7 +139,7 @@ const tableShowData =[{
reporter:"张三", reporter:"张三",
updateTime:"2022-05-04", updateTime:"2022-05-04",
discription: 'Lorem ipsum dolor sit ametisl nisl nisl nisl nisl nisl nisl nisl nis', discription: 'Lorem ipsum dolor sit ametisl nisl nisl nisl nisl nisl nisl nisl nis',
fireLevel: '1级', fireLevel: '2',
firePoint: '厨房', firePoint: '厨房',
}] }]
......
...@@ -14,11 +14,8 @@ ...@@ -14,11 +14,8 @@
<!-- 左侧下部分:统计区域 --> <!-- 左侧下部分:统计区域 -->
<div class="info-card stats-alerts"> <div class="info-card stats-alerts">
<div class="card-tabs"> <div class="card-tabs">
<span class="title">历史火情</span> <span class="title">当前火情</span>
<!-- <div class="card-actions">
<el-button class="operation-button" type="primary" @cliick="archiveInfoFn" >归档</el-button>
</div> -->
</div> </div>
<div class="card-table"> <div class="card-table">
<tableshow :tableShowData="tableShowData" @archiveInfoDateBackcallFn="archiveInfoDateBackcallFn"></tableshow> <tableshow :tableShowData="tableShowData" @archiveInfoDateBackcallFn="archiveInfoDateBackcallFn"></tableshow>
......
...@@ -167,7 +167,7 @@ ...@@ -167,7 +167,7 @@
:before-close="handleClose" :before-close="handleClose"
> >
<updataImgInfo :file="aerialViewUrl" @changeAerialView="changeAerialView" ></updataImgInfo> <updataImgInfo :file="aerialViewUrl" @changeAerialView="changeAerialView" :updataImageCount="1" ></updataImgInfo>
</el-dialog> </el-dialog>
<!-- 展示鸟瞰图 --> <!-- 展示鸟瞰图 -->
<el-dialog <el-dialog
......
...@@ -82,8 +82,9 @@ const resetSearchForm = () => { ...@@ -82,8 +82,9 @@ const resetSearchForm = () => {
.search-row { .search-row {
/* background-color: #606266; */ /* background-color: #606266; */
width: 80%;
display: flex; display: flex;
flex: 1;
align-items: center; align-items: center;
justify-items: center; justify-items: center;
justify-content: space-between; justify-content: space-between;
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
<el-table <el-table
:data="curTableShowData[0].tableBody" :data="curTableShowData[0].tableBody"
style="width: 100%;height: 100%;" style="width: 100%;height: 100%;"
fixed
@load="handleTableLoad"
> >
<template v-for="(items, index) in curTableShowData[0].tableHeader" :key="index"> <template v-for="(items, index) in curTableShowData[0].tableHeader" :key="index">
...@@ -33,23 +35,19 @@ ...@@ -33,23 +35,19 @@
<div v-else-if="items.prop == 'status'"> <div v-else-if="items.prop == 'status'">
<el-switch <el-switch
v-model="scope.row[items.prop]" v-model="scope.row[items.prop]"
active-color="#13ce66" class="mt-2"
inactive-color="#ff4949" style="margin-left: 24px"
disabled inline-prompt
:active-icon="Check"
:inactive-icon="Close"
@change="changeStatusFn(scope.row)"
/> />
</div> </div>
<div v-else-if="items.prop == 'role'"> <div v-else-if="items.prop == 'role'">
<span class="role-tag" :class="scope.row[items.prop]==='0'|| scope.row[items.prop] ==='10' ? 'admin' : 'inspector'" >
<span >{{ useAppStoreInstance.position[scope.row[items.prop]] }}</span>
</span>
<el-button
type="primary"
round
:style="{
backgroundColor: scope.row[items.prop] ==='0' ? '#67C23A' : '#F56C6C' // 0→绿色(管理员),1→红色(巡查人员)
}"
>
{{ scope.row[items.prop] === "0" ? "管理员" : "巡查人员" }} <!-- 文本逻辑不变 -->
</el-button>
</div> </div>
<div v-else> <div v-else>
...@@ -84,14 +82,19 @@ ...@@ -84,14 +82,19 @@
<script setup> <script setup>
import { onMounted, ref, toRef } from 'vue'; import { onMounted, ref, toRef } from 'vue';
import { staffGetPageInfoApi } from '../../../api/staff'; import { staffGetPageInfoApi ,staffSetAbleApi} from '../../../api/staff';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { pa } from 'element-plus/es/locales.mjs'; import { pa } from 'element-plus/es/locales.mjs';
import { color } from 'echarts';
import { Check, Close } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus';
import useAppStore from '../../../store/module/app';
const useAppStoreInstance = useAppStore();
// const value = ref(true);
const props = defineProps(["tableShowData",'Operation']) const props = defineProps(["tableShowData",'Operation'])
const emits = defineEmits(["tableDataRefresh"])
const curTableShowData = ref(props.tableShowData) const curTableShowData = ref(props.tableShowData)
console.log("11111111111111111111111props.tableShowData-----------------",props.Operation) console.log("11111111111111111111111props.tableShowData-----------------",props.Operation)
...@@ -99,6 +102,25 @@ console.log("11111111111111111111111props.tableShowData-----------------",props. ...@@ -99,6 +102,25 @@ console.log("11111111111111111111111props.tableShowData-----------------",props.
const currentPage2 = ref(1) const currentPage2 = ref(1)
const pageSize2 = ref(10) const pageSize2 = ref(10)
const changeStatusFn = (info) => {
console.log("changeStatusFn-----------------",info)
const submitDate = {
"id":info.id,
"status": info.status?0:1
}
console.log("submitDate-----------------",submitDate)
if (submitDate.id ==='' || submitDate.status ==='') {
ElMessage.warning('缺少操作的行数据!')
}else {
(staffSetAbleApi(submitDate)) .then(res => {
emits('tableDataRefresh')
ElMessage.success(`${submitDate.status===1?'禁用':'启用'} 操作成功!`)
}).catch(err => {
ElMessage.error("changeStatusFn err",err.message)
})
}
}
// 处理按钮点击事件 // 处理按钮点击事件
const handleButtonClick = (value, row) => { const handleButtonClick = (value, row) => {
// console.log('点击了按钮:', value) // console.log('点击了按钮:', value)
...@@ -159,6 +181,29 @@ const getNewTableData = () => { ...@@ -159,6 +181,29 @@ const getNewTableData = () => {
</script> </script>
<style scoped> <style scoped>
.role-tag {
font-weight: 500;
}
.admin {
background-color: #ecf5ff;
color: #409eff;
}
/* 巡查员标签 */
.inspector {
background-color: #f0f9eb;
color: #67c23a;
}
.gradient-text {
background: linear-gradient(90deg, #ff3366, #ffcc00, #33cc33, #3399ff, #cc33ff);
-webkit-background-clip: text; /* 关键属性 */
background-clip: text;
color: transparent; /* 文字透明显露出背景 */
font-size: 1em;
font-weight: bold;
}
/* 容器样式 */ /* 容器样式 */
.table-container { .table-container {
position: relative; /* 为分页定位提供基准 */ position: relative; /* 为分页定位提供基准 */
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<div class="sidebar-container"> <div class="sidebar-container">
<div class="nav-grid"> <div class="nav-grid">
<!-- 首页 --> <!-- 首页 -->
<div class="nav-item" v-for="(items, index) in menuItems" :key="index" @click="handleNavClick(value)"> <div class="nav-item" v-for="(items, index) in menuItems" :key="index" @click="handleNavClick(items)">
<div class="nav-icon"> <div class="nav-icon">
<img style=" width:100%;" :src="items.icon"> <img style=" width:100%;" :src="items.icon">
</div> </div>
...@@ -17,29 +17,28 @@ ...@@ -17,29 +17,28 @@
</template> </template>
<script setup> <script setup>
import { message } from 'ant-design-vue';
import { it } from 'element-plus/es/locales.mjs'; import { it } from 'element-plus/es/locales.mjs';
import { ref } from 'vue'; import { ref } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
const router = useRouter(); const router = useRouter();
const route = useRoute();
const activeItem = ref('首页'); // 默认激活首页 const activeItem = ref('首页'); // 默认激活首页
const props = defineProps(['menuItems']); const props = defineProps(['menuItems']);
// 导航菜单数据
// const navItems = ref([
// { name: '首页', icon: 'home', path: '/' },
// { name: '系统管理', icon: 'settings', path: '/system' },
// { name: '建筑管理', icon: 'building', path: '/buildings' },
// { name: '无人机管理', icon: 'drone', path: '/drones' },
// { name: '火情管理', icon: 'fire', path: '/fires' },
// { name: '数字孪生管理', icon: 'digital-twin', path: '/digital-twin' },
// { name: '个人信息', icon: 'profile', path: '/profile' }
// ]);
// 导航点击处理 // 导航点击处理
const handleNavClick = (item) => { const handleNavClick = (item) => {
activeItem.value = item.name; console.log('点击了导航项:', item,route);
try {
router.push(item.path); router.push(item.path);
message.success('跳转:' + item.name);
} catch (error) {
console.log('导航项 错误 error', error);
}
}; };
</script> </script>
......
...@@ -9,10 +9,10 @@ ...@@ -9,10 +9,10 @@
<!-- 区域信息概览 --> <!-- 区域信息概览 -->
<div class="info-card region-overview"> <div class="info-card region-overview">
<div class="info-grid"> <div class="info-grid">
<el-avatar size="large" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"/> <el-avatar shape="square" size="large" :src="useAppStoreInstance.userInfo.avatar?useAppStoreInstance.userInfo.avatar: useAppStoreInstance.showavatarUrl"/>
<div class="info-item"> <div class="info-item">
<span>张明</span> <span>{{useAppStoreInstance.userInfo.name}}</span>
<span>高级产品经理</span> <span>{{useAppStoreInstance.position[useAppStoreInstance.userInfo.role]}}</span>
</div> </div>
</div> </div>
</div> </div>
...@@ -42,11 +42,25 @@ ...@@ -42,11 +42,25 @@
</template> </template>
<script setup> <script setup>
import { reactive, computed } from 'vue'; import { reactive, computed, onMounted, watch } from 'vue';
import { ChatDotSquare } from '@element-plus/icons-vue'; import { ChatDotSquare } from '@element-plus/icons-vue';
import currentFire from '../components/currentFire.vue'; import currentFire from '../components/currentFire.vue';
import uav from '../components/uav.vue'; import uav from '../components/uav.vue';
import meanList from '../components/menuList.vue'; import meanList from '../components/menuList.vue';
import useAppStore from '../../../store/module/app';
import { useRouter,useRoute } from 'vue-router';
const useAppStoreInstance = useAppStore();
const curruserInfo = computed(() => (useAppStoreInstance.userInfo));
const router = useRouter();
const route = useRoute();
// const initUserInfoFn = () => {
// return JSON.parse(curruserInfo).avatarUrl = JSON.parse(curruserInfo).avatarUrl?JSON.parse(curruserInfo).avatarUrl:"https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png";
// }
// console.log(curruserInfo.value,'89898989898989',);
// 定义一个ref变量,用于存储当前的火灾数据 // 定义一个ref变量,用于存储当前的火灾数据
const currentFireData = ref([ const currentFireData = ref([
// 火灾数据1 // 火灾数据1
...@@ -119,13 +133,13 @@ const uavRepairData = ref([ ...@@ -119,13 +133,13 @@ const uavRepairData = ref([
const menuItems = ref([ const menuItems = ref([
{ name: '首页', icon: new URL('../../../static/image/huo.png', import.meta.url), path: '/' }, { name: '首页', icon: new URL('../../../static/image/huo.png', import.meta.url), path: '/analysisPage' },
{ name: '系统管理', icon: new URL('../../../static/image/huo.png', import.meta.url), path: '/system' }, { name: '系统管理', icon: new URL('../../../static/image/huo.png', import.meta.url), path: '/usermanage' },
{ name: '建筑管理', icon: new URL('../../../static/image/huo.png', import.meta.url), path: '/buildings' }, { name: '建筑管理', icon: new URL('../../../static/image/huo.png', import.meta.url), path: '/areabuildmanage' },
{ name: '无人机管理', icon: new URL('../../../static/image/huo.png', import.meta.url), path: '/drones' }, { name: '无人机管理', icon: new URL('../../../static/image/huo.png', import.meta.url), path: '/uavdispatch' },
{ name: '火情管理', icon: new URL('../../../static/image/huo.png', import.meta.url), path: '/fires' }, { name: '火情管理', icon: new URL('../../../static/image/huo.png', import.meta.url), path: '/firesurveillance' },
{ name: '数字孪生管理', icon: new URL('../../../static/image/huo.png', import.meta.url), path: '/digital-twin' }, { name: '数字孪生管理', icon: new URL('../../../static/image/huo.png', import.meta.url), path: '/digital-twin' },
{ name: '个人信息', icon: new URL('../../../static/image/huo.png', import.meta.url), path: '/profile' } { name: '个人信息', icon: new URL('../../../static/image/huo.png', import.meta.url), path: '/usermanage' }
]); ]);
// 响应式配置 // 响应式配置
const layoutConfig = reactive({ const layoutConfig = reactive({
...@@ -140,6 +154,18 @@ const containerStyle = computed(() => ({ ...@@ -140,6 +154,18 @@ const containerStyle = computed(() => ({
minHeight: layoutConfig.containerMinHeight, minHeight: layoutConfig.containerMinHeight,
'--card-spacing': layoutConfig.cardSpacing, '--card-spacing': layoutConfig.cardSpacing,
})); }));
onMounted(() => {
nextTick (()=>{
useAppStoreInstance.initavatarUrlFn()
})
});
// 监听路由变化
watch(route, () => {
useAppStoreInstance.initavatarUrlFn()
}, { immediate: true });
</script> </script>
<style scoped> <style scoped>
......
<template> <template>
<div class="login"> <div class="login">
<div class="login_main">
<div class="title1">无人灭火机器人数字孪生后台管理系统</div>
<div class="title2">国际花园小区</div> <div class="login-container">
<el-form class="fromclass" :model="form" :rules="rules" ref="formref" <!-- 左侧标题区域 -->
style="width: 16.2vw;margin: 0 auto;margin-top: 4vh;"> <div class="left-panel">
<img style="width: 100%;height: 100%;" src="../../static/image/login_bg.png" alt="logo"></img>
<!-- <div class="title-group">
<h1 class="main-title">智能消防灭火信息管控平台</h1>
<p class="sub-title">高效·智能·安全</p>
</div> -->
</div>
<!-- 右侧登录表单区域 -->
<div class="right-panel">
<div class="login-card">
<h2 class="welcome-text">欢迎回来</h2>
<p class="login-desc">请输入您的账号和密码登录</p>
<el-form
ref="formref"
:model="form"
:rules="rules"
class="login-form"
>
<el-form-item prop="phone"> <el-form-item prop="phone">
<el-input v-model="form.phone" placeholder="请输入您的账号" clearable class="widthClass" /> <el-input
v-model="form.phone"
placeholder="输入手机号码"
prefix-icon="Phone"
clearable
class="custom-input"
@keyup.enter="commit"
/>
</el-form-item> </el-form-item>
<el-form-item prop="password"> <el-form-item prop="password">
<el-input v-model="form.password" placeholder="请输入您的密码" clearable class="widthClass" @keyup.enter="commit" /> <el-input
v-model="form.password"
placeholder="输入密码"
type="password"
prefix-icon="Lock"
clearable
class="custom-input"
@keyup.enter="commit"
/>
</el-form-item> </el-form-item>
<div class="form-options">
<el-checkbox v-model="rememberMe" class="remember-checkbox">记住账号</el-checkbox>
<el-button type="text" class="forgot-password">忘记密码?</el-button>
</div>
<el-button
type="primary"
class="login-button"
@click="commit"
:loading="loginLoading"
>
登录
</el-button>
</el-form> </el-form>
<div class="logingo" @click="commit">登录</div> </div>
</div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, onMounted, reactive } from 'vue'; import { ref, reactive } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage, ElLoading } from 'element-plus'; import { ElMessage, ElLoading } from 'element-plus';
import { useRoute, useRouter } from 'vue-router';
import { loginApi } from "../../api/login.js"; import { loginApi } from "../../api/login.js";
import { setToken } from '@/utils/auth.js' import { setToken } from '@/utils/auth.js';
const router = useRouter(); import useAppStore from '../../store/module/app.js';
const form = ref({
const useAppStoreInstance = useAppStore();
// 登录表单数据
const form = reactive({
phone: '', phone: '',
password: '' password: ''
}) });
const formref = ref(null)
// 状态管理
const formref = ref(null);
const rememberMe = ref(false);
const loginLoading = ref(false);
const router = useRouter();
// 表单验证规则
const rules = { const rules = {
phone: [{ required: true, trigger: "blur", message: "请输入您的账号" }], phone: [
password: [{ required: true, trigger: "blur", message: "请输入您的密码" }], { required: true, message: "请输入您的账号", trigger: "blur" },
{ pattern: /^1[3-9]\d{9}$/, message: "请输入正确的手机号码", trigger: "blur" }
],
password: [
{ required: true, message: "请输入您的密码", trigger: "blur" },
{ min: 6, message: "密码长度不能少于6位", trigger: "blur" }
]
}; };
// 登录
// 登录处理函数
const commit = () => { const commit = () => {
formref.value.validate(valid => { formref.value.validate(valid => {
if (valid) { if (valid) {
loginApi(form.value).then(res => { loginLoading.value = true;
console.log(res, '登录');
if (res.code == 200) { // 登录请求
setToken(res.data.token)//储存token loginApi(form).then(res => {
localStorage.setItem('menu', 1)//默认选中工作台 loginLoading.value = false;
router.replace({ path: '/home' }) console.log(res, '测试');
if (res.code === 200) {
setToken(res.data.token);
let { phone, id,name,avatarUrl ,role,status} = res.data.user;
// console.log('phone, id,name,avatarUrl ,role',useAppStoreInstance.userInfo);
localStorage.setItem('user', JSON.stringify({ phone, id,name,avatarUrl ,role,status}));
useAppStoreInstance.userInfo = ( {phone, id,name,avatarUrl ,role,status})
console.log('phone, id,name,avatarUrl ,role',useAppStoreInstance.userInfo);
localStorage.setItem('lastValidMenuPath','/home')
// localStorage.setItem('saveMenuState',JSON.stringify({lastValidMenuPath:'/home',openMenus:[true,null,null,null,null]}))
if (rememberMe.value) {
localStorage.setItem('rememberedAccount', form.phone);
} else {
localStorage.removeItem('rememberedAccount');
}
localStorage.setItem('menu', 1); // 默认选中工作台
router.replace({ path: '/analysisPage' });
ElMessage.success('登录成功!');
} else {
ElMessage.error(res.message || '登录失败,请检查账号密码');
} }
}) }).catch(err => {
loginLoading.value = false;
ElMessage.error('网络错误,请稍后重试');
});
}
});
};
// 初始化表单数据
const initFormData = () => {
const rememberedAccount = localStorage.getItem('rememberedAccount');
if (rememberedAccount) {
form.phone = rememberedAccount;
rememberMe.value = true;
} }
}) };
}
// 页面加载时初始化
initFormData();
</script> </script>
<style scoped>
<style scoped lang="scss">
.login { .login {
padding: 10rem;
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
overflow: hidden;
position: relative;
background-color: #f5f7fa;
}
.login-bg-decoration {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url(../../static/image/loginback.png); background-image: url(../../static/image/loginback.png);
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: 100% 100%; background-size: cover;
background-position: center;
z-index: 0;
}
.login-container {
display: flex;
width: 100%;
height: 100%;
position: relative; position: relative;
overflow: auto; z-index: 1;
} }
.login_main { .left-panel {
width: 30vw; flex: 1;
height: 25vw; display: flex;
margin: 0 auto; flex-direction: column;
margin-top: calc((100vh - 25vw)/2); justify-content: center;
background-image: url(../../static/image/loginmain.png); // padding: 0 5vw;
background-repeat: no-repeat; color: #fff;
background-size: 100% 100%; // background-image: url(../../static/image/loginback.png);
padding: 50px; // background-color: #1d2129;
// background: linear-gradient(90deg, rgba(0, 47, 108, 0.8), rgba(0, 47, 108, 0.6) 70%, transparent);
}
.title-group {
// background-color: #165dff;
max-width: 500px;
} }
.title1 { .main-title {
font-family: SourceHanSansCN, SourceHanSansCN; font-size: 2.5rem;
font-weight: bold; font-weight: 700;
line-height: 48px; margin-bottom: 1rem;
text-align: right; line-height: 1.3;
font-style: normal; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
background: linear-gradient(to bottom, #FFFFFF 50%, #96CBFF 100%); }
.sub-title {
font-size: 1rem;
opacity: 0.9;
}
width: 100%; .right-panel {
text-align: center; width: 380px;
font-size: 26px; display: flex;
color: #D5F6FF; justify-content: center;
opacity: 0.79; align-items: center;
background-clip: text; background-color: #fff;
-webkit-background-clip: text; box-shadow: -5px 0 25px rgba(0, 0, 0, 0.05);
color: transparent;
} }
.title2 { .login-card {
width: 100%; width: 100%;
text-align: center; padding: 2.5rem;
font-size: 20px; max-width: 320px;
color: #D5F6FF;
opacity: 0.79;
line-height: 48px;
} }
.widthClass { .welcome-text {
font-size: 1.5rem;
font-weight: 600;
color: #1d2129;
margin-bottom: 0.5rem;
}
.login-desc {
color: #86909c;
margin-bottom: 2rem;
font-size: 0.9rem;
}
.login-form {
width: 100%; width: 100%;
height: 2.2vw; }
margin: 0 auto;
margin-top: 1vh;
.custom-input {
margin-bottom: 1rem;
} }
.logingo { .form-options {
width: 16.5vw; display: flex;
height: 2.2vw; justify-content: space-between;
text-align: center; align-items: center;
line-height: 2.2vw; margin: 0.5rem 0 1.5rem;
margin: 0 auto;
margin-top: 3vh;
color: #ffffff;
background-image: url(../../static/image/loginbutton.png);
background-repeat: no-repeat;
background-size: 100% 100%;
cursor: pointer;
} }
:deep().widthClass .el-input__wrapper { .remember-checkbox {
background-color: #06325c; color: #4e5969;
/* opacity: 0.25; */ font-size: 0.85rem;
box-shadow: none;
border: 1px solid #064a74;
/* color: #06325c; */
} }
:deep().widthClass .el-input__inner { .forgot-password {
color: #ffffff !important; color: #165dff;
font-size: 0.85rem;
padding: 0;
} }
:deep().widthClass .el-input__inner::placeholder { .login-button {
color: #ffffff !important; width: 100%;
opacity: 1; height: 48px;
font-size: 1rem;
background-color: #165dff;
border-radius: 6px;
}
/* 自定义输入框样式 */
:deep(.custom-input .el-input__wrapper) {
height: 48px;
border-radius: 6px;
border-color: #e5e6eb;
box-shadow: none;
transition: all 0.2s ease;
&:focus-within {
border-color: #165dff;
box-shadow: 0 0 0 2px rgba(22, 93, 255, 0.1);
}
}
/* 响应式调整 */
@media (max-width: 768px) {
.login-container {
flex-direction: column;
}
.left-panel {
height: 200px;
width: 100%;
background: linear-gradient(180deg, rgba(0, 47, 108, 0.9), rgba(0, 47, 108, 0.7));
padding: 0 2rem;
}
.main-title {
font-size: 1.8rem;
}
.right-panel {
width: 100%;
height: calc(100vh - 200px);
}
} }
</style> </style>
\ No newline at end of file
...@@ -65,8 +65,8 @@ ...@@ -65,8 +65,8 @@
<!-- 状态 --> <!-- 状态 -->
<el-form-item label="状态" prop="status" class="form-item"> <el-form-item label="状态" prop="status" class="form-item">
<el-radio-group v-model="form.status" class="radio-group"> <el-radio-group v-model="form.status" class="radio-group">
<el-radio :label="1" class="radio-btn" size="large">正常</el-radio> <el-radio :label="0" class="radio-btn" size="large">正常</el-radio>
<el-radio :label="0" class="radio-btn" size="large">停用</el-radio> <el-radio :label="1" class="radio-btn" size="large">停用</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
...@@ -90,7 +90,7 @@ ...@@ -90,7 +90,7 @@
<!-- 头像上传 --> <!-- 头像上传 -->
<el-form-item label="头像上传" prop="avatar" class="form-item"> <el-form-item label="头像上传" prop="avatar" class="form-item">
<div class="avatar-uploader"> <div class="avatar-uploader">
<UpdataImg @getAvatar="getAvatar"></UpdataImg> <UpdataImg @getAvatar="getAvatar" ref="avatarUploader" :updataImageCount="1"></UpdataImg>
</div> </div>
</el-form-item> </el-form-item>
...@@ -134,13 +134,13 @@ import {staffAddInfoApi} from '../../../api/staff' ...@@ -134,13 +134,13 @@ import {staffAddInfoApi} from '../../../api/staff'
const addUserFormRef = ref(null); const addUserFormRef = ref(null);
// 提交加载状态 // 提交加载状态
const submitting = ref(false); const submitting = ref(false);
const avatarUploader = ref(null);
const emit = defineEmits(['closeAddDrawer','tableDataRefresh']); const emit = defineEmits(['closeAddDrawer','tableDataRefresh']);
// 角色选项数据 // 角色选项数据
const roleOptions = ref([ const roleOptions = ref([
{ label: '管理员', value: 'admin' }, { label: '管理员', value: 0 },
{ label: '巡察员', value: 'operator' } { label: '巡察员', value: 1 }
]); ]);
// 表单数据 // 表单数据
...@@ -149,22 +149,25 @@ const form = reactive({ ...@@ -149,22 +149,25 @@ const form = reactive({
password: '', // 用户密码 password: '', // 用户密码
phone: '', // 手机号 phone: '', // 手机号
gender: 1, // 性别:1-男,2-女 gender: 1, // 性别:1-男,2-女
status: 1, // 状态:1-正常,0-停用 status: 0, // 状态:0-正常,1-停用
role: '', // 角色 role: '', // 角色
avatar: '', // 头像URL avatar: '', // 头像URL
remark: '' // 备注 remark: '' // 备注
}); });
const getAvatar = (data) => { const getAvatar = (data) => {
console.log("000000000000000000000",data) console.log("000000000000000000000")
form.avatar = data; form.avatar = data;
} }
// 表单验证规则 // 表单验证规则
const rules = reactive({ const rules = reactive({
name: [ name: [
{ required: true, message: '请输入用户账号', trigger: 'blur' }, { required: true, message: '请输入正确的汉字', trigger: 'blur' },
{ min: 4, max: 20, message: '账号长度在4-20个字符之间', trigger: 'blur' }, { min: 2, max: 10, message: '账号长度在4-20个字符之间', trigger: 'blur' },
{ pattern: /^[a-zA-Z0-9_]+$/, message: '账号只能包含字母、数字和下划线', trigger: 'blur' }
{ pattern: /^[\u4e00-\u9fa5]+$/, message: '账号只能包含汉字', trigger: 'blur' }
], ],
password: [ password: [
{ required: true, message: '请输入用户密码', trigger: 'blur' }, { required: true, message: '请输入用户密码', trigger: 'blur' },
...@@ -221,22 +224,21 @@ const rules = reactive({ ...@@ -221,22 +224,21 @@ const rules = reactive({
// }; // };
// 提交表单 // 提交表单
const handleSubmit = async () => { const handleSubmit = () => {
try { try {
submitting.value = true; submitting.value = true;
await addUserFormRef.value.validate();
// 准备提交数据 // 准备提交数据
const submitData = { const submitData = {
...form, ...form,
gender: form.gender ==='male' ? 0 : 1, avatar:form.avatar,
status: form.status ? 0 : 1
}; };
addUserFormRef.value.resetFields();
console.log('提交新增用户数据:', submitData);
// 模拟API请求
// console.log('提交新增用户数据:', submitData); // console.log('提交新增用户数据:', submitData);
// await new Promise(resolve => setTimeout(resolve, 1000)); // 模拟API请求
console.log('提交新增用户数据:', submitData,form.avatar);
staffAddInfoApi(submitData).then(res => { staffAddInfoApi(submitData).then(res => {
console.log('用户新增成功', res); console.log('用户新增成功', res);
if (res.code === 200) { if (res.code === 200) {
...@@ -245,14 +247,18 @@ const handleSubmit = async () => { ...@@ -245,14 +247,18 @@ const handleSubmit = async () => {
// 通知父组件刷新列表 // 通知父组件刷新列表
emit('tableDataRefresh'); emit('tableDataRefresh');
ElMessage.success('用户新增成功!'); ElMessage.success('用户新增成功!');
addUserFormRef.value.resetFields();
// avatarUploader.value.handleRemove(form.avatar[0])
} }
}).catch(err => { }).catch(err => {
console.error('重置密码失败', err); ElMessage.error('用户新增失败');
}); });
} catch (error) { } catch (error) {
console.error('表单验证失败:', error); console.error('表单验证失败:', error);
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
<el-form-item label="用户号" prop="name"> <el-form-item label="用户号" prop="name">
<el-input <el-input
v-model="form.name" v-model="form.name"
disabled
placeholder="请输入" placeholder="请输入"
class="form-input" class="form-input"
/> />
...@@ -39,37 +39,25 @@ ...@@ -39,37 +39,25 @@
/> />
</el-form-item> </el-form-item>
<!-- 时间信息 --> <el-form-item label="性别" prop="gender">
<div class="time-group"> <el-radio-group v-model="form.gender" class="status-radio">
<!-- 创建时间 --> <el-radio :label="1" size="large">
<el-form-item label="创建时间" prop="createTime"> <span class="radio-label"></span>
<el-input </el-radio>
v-model="form.createTime" <el-radio :label="2" size="large">
placeholder="-" <span class="radio-label"></span>
disabled </el-radio>
class="time-input" </el-radio-group>
/>
</el-form-item>
<!-- 修改时间 -->
<el-form-item label="修改时间" prop="updateTime">
<el-input
v-model="form.updateTime"
placeholder="-"
disabled
class="time-input"
/>
</el-form-item> </el-form-item>
</div>
<!-- 状态 --> <!-- 状态 -->
<el-form-item label="状态" prop="status"> <el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status" class="status-radio"> <el-radio-group v-model="form.status" class="status-radio">
<el-radio :label="1" size="large">
<span class="radio-label">正常</span>
</el-radio>
<el-radio :label="0" size="large"> <el-radio :label="0" size="large">
<span class="radio-label">停用</span> <span class="radio-label">启用</span>
</el-radio>
<el-radio :label="1" size="large">
<span class="radio-label">禁用</span>
</el-radio> </el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
...@@ -125,7 +113,7 @@ ...@@ -125,7 +113,7 @@
<script setup> <script setup>
import { ref, reactive, onMounted, watch } from 'vue'; import { ref, reactive, onMounted, watch } from 'vue';
import { dayjs, ElMessage, ElMessageBox } from 'element-plus'; import { dayjs, ElMessage, ElMessageBox, namespaceContextKey } from 'element-plus';
import {staffUpdateInfoApi } from '../../../api/staff'; import {staffUpdateInfoApi } from '../../../api/staff';
const props = defineProps(["rowData"]); const props = defineProps(["rowData"]);
...@@ -144,8 +132,10 @@ const submitting = ref(false); ...@@ -144,8 +132,10 @@ const submitting = ref(false);
// 角色选项 // 角色选项
const roleOptions = ref([ const roleOptions = ref([
{ label: '管理员', value: 'admin' }, { label: '管理员', value: '0' },
{ label: '巡察员', value: 'operator' } { label: '巡察员', value: '1' },
{ label: '超级管理员', value: '10' },
]); ]);
// 表单数据 // 表单数据
...@@ -182,28 +172,27 @@ const rules = reactive({ ...@@ -182,28 +172,27 @@ const rules = reactive({
const initFormData = () => { const initFormData = () => {
if (!props.rowData) return; if (!props.rowData) return;
const { status, role, ...rest } = props.rowData; const { status, ...rest } = props.rowData;
console.log(props.rowData,"8888888888888888888888"); // console.log(props.rowData,"8888888888888888888888");
// 映射状态值 // 映射状态值
Object.assign(form, { Object.assign(form, {
...rest, ...rest,
status: status ? 1 : 0 , status: status ? 0 : 1
role: role===0 ? 'admin' : 'operator'
}); });
console.log("645555555555",form) console.log("645555--------------555555",form)
}; };
// 提交表单 // 提交表单
const handleSubmit = async () => { const handleSubmit = () => {
try { try {
submitting.value = true; submitting.value = true;
await editFormRef.value.validate(); let { status, role,name, gender,id,phone,remark } = form;
let { status, role,gender, id,phone,remark } = form;
// 准备提交数据 // 准备提交数据
const submitData = { const submitData = {
status, status,
role:role ==="operator" ? 1 : 0, role:role ,
gender, gender,
name,
id, id,
phone, phone,
remark remark
...@@ -212,10 +201,11 @@ const handleSubmit = async () => { ...@@ -212,10 +201,11 @@ const handleSubmit = async () => {
// 模拟API请求 // 模拟API请求
console.log('提交数据:', submitData); console.log('提交数据:', submitData);
staffUpdateInfoApi(submitData).then(res => { staffUpdateInfoApi(submitData).then(res => {
submitting.value = true;
console.log(res,"--------898989---------------"); console.log(res,"--------898989---------------");
if(res.code === 200){ if(res.code === 200){
ElMessage.success('用户信息修改成功'); ElMessage.success('用户信息修改成功');
// submitting.value = true;
emit('tableDataRefresh'); emit('tableDataRefresh');
emit('closeEditDrawer'); emit('closeEditDrawer');
}else{ }else{
......
...@@ -22,8 +22,8 @@ ...@@ -22,8 +22,8 @@
<div class="info-row"> <div class="info-row">
<div class="info-label">用户角色</div> <div class="info-label">用户角色</div>
<div class="info-value"> <div class="info-value">
<span class="role-tag" :class="from.role ? 'admin' : 'inspector'"> <span class="role-tag" :class="from.role==='0'|| from.role ==='10' ? 'admin' : 'inspector'">
{{ from.role ? "管理员" : "巡查员" }} {{ userappstoreinstance.position[from.role] }}
</span> </span>
</div> </div>
</div> </div>
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
type="password" type="password"
placeholder="请输入新密码" placeholder="请输入新密码"
show-password show-password
/> />
</div> </div>
...@@ -54,12 +55,17 @@ ...@@ -54,12 +55,17 @@
</template> </template>
<script setup> <script setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref, watch } from 'vue';
import {staffResetPasswordApi} from '../../../api/staff' import {staffResetPasswordApi} from '../../../api/staff'
import { ElMessageBox } from 'element-plus'; import { ElMessageBox } from 'element-plus';
import useAppStore from '../../../store/module/app';
import { useRouter,useRoute } from 'vue-router';
const userappstoreinstance = useAppStore();
const route = useRoute();
// 接收父组件传入的用户信息 // 接收父组件传入的用户信息
const props = defineProps(['userInfo','isResetPasswordDialogVisible']); const props = defineProps(['userInfo','isResetPasswordDialogVisible']);
const emits = defineEmits(['closeResetPasswordDialogFn']); const emits = defineEmits(['closeResetPasswordDialogFn']);
console.log(props.isResetPasswordDialogVisible,"00000000000000000000000000");
const from = ref({ const from = ref({
id: '', id: '',
password: '', password: '',
...@@ -67,15 +73,14 @@ const from = ref({ ...@@ -67,15 +73,14 @@ const from = ref({
phone: '', phone: '',
role: '' role: ''
}); });
watch(() => props.isResetPasswordDialogVisible, (newVal) => { watch(() => props.isResetPasswordDialogVisible,
(newVal) => {
if (newVal) {
// console.log('抽屉已打开,执行初始化代码');
initFormData(); initFormData();
} }
}); );
onMounted(() => { onMounted(() => {
// 在这里处理初始化逻辑 // 在这里处理初始化逻辑
console.log('组件已挂载',props.userInfo);
initFormData(); initFormData();
}); });
const closeResetPasswordDialogFn = () => { const closeResetPasswordDialogFn = () => {
...@@ -104,11 +109,14 @@ const initFormData = () => { ...@@ -104,11 +109,14 @@ const initFormData = () => {
phone: props.userInfo.phone, phone: props.userInfo.phone,
role: props.userInfo.role role: props.userInfo.role
}; };
console.log(from.value,"----------------------56565656---------------------");
} }
const resetPasswordFn = () => { const resetPasswordFn = () => {
// 在这里处理密码重置的逻辑 // 在这里处理密码重置的逻辑
console.log('重置密码', userInfo.value); console.log('重置密码', userInfo.value);
const { id, password } = from.value; const { id, password } = from.value;
const submitData = { id, password }; // 发送请求到后端进行密码重置 const submitData = { id, password }; // 发送请求到后端进行密码重置
// console.log('提交数据', submitData); // console.log('提交数据', submitData);
staffResetPasswordApi(submitData).then(res => { staffResetPasswordApi(submitData).then(res => {
...@@ -125,6 +133,8 @@ const resetPasswordFn = () => { ...@@ -125,6 +133,8 @@ const resetPasswordFn = () => {
}; };
// 定义一个响应式变量来存储用户信息 // 定义一个响应式变量来存储用户信息
const userInfo = ref(props.userInfo); const userInfo = ref(props.userInfo);
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
<div class="info-row"> <div class="info-row">
<div class="info-label">用户角色</div> <div class="info-label">用户角色</div>
<div class="info-value"> <div class="info-value">
<span class="role-tag">{{ form.role==='0' ? "管理员": "巡查员" }}</span> <span class="role-tag">{{ userappstoreinstance.position[form.role] }}</span>
</div> </div>
</div> </div>
...@@ -46,13 +46,13 @@ ...@@ -46,13 +46,13 @@
<div class="info-row"> <div class="info-row">
<div class="info-label">启用状态</div> <div class="info-label">启用状态</div>
<div class="info-value"> <div class="info-value">
<span class="status-tag" :class=" form.status ? 'status-active' : 'status-inactive'">{{form.status ? '启用' : '禁用' || '-' }}</span> <span class="status-tag" :class=" form.status ? 'status-active' : 'status-inactive'"> {{form.status ? '启用' : '禁用' || '-' }}</span>
</div> </div>
</div> </div>
<div class="info-row"> <div class="info-row">
<div class="info-label">备注</div> <div class="info-label">备注</div>
<div class="info-value">{{ (form.remarks) || '-' }}</div> <div class="info-value">{{ (form.remark) || '-' }}</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -63,8 +63,8 @@ ...@@ -63,8 +63,8 @@
<script setup> <script setup>
import { ref, computed, onActivated, onMounted } from 'vue'; import { ref, computed, onActivated, onMounted } from 'vue';
import useAppStore from '../../../store/module/app';
const userappstoreinstance = useAppStore();
// 接收父组件传入的用户信息和显示状态 // 接收父组件传入的用户信息和显示状态
const props = defineProps({ const props = defineProps({
userInfo: { userInfo: {
...@@ -206,7 +206,7 @@ const formatDate = (timestamp) => { ...@@ -206,7 +206,7 @@ const formatDate = (timestamp) => {
.info-value { .info-value {
width: 70%; width: 70%;
color: #1f2937; color: #6b7280;
font-size: 14px; font-size: 14px;
} }
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
:file-list="fileList" :file-list="fileList"
:on-change="handleFileChange" :on-change="handleFileChange"
:before-upload="beforeUpload" :before-upload="beforeUpload"
:limit="1" :limit="updataImageCount"
:on-exceed="handleExceed" :on-exceed="handleExceed"
accept="image/*" accept="image/*"
class="custom-upload" class="custom-upload"
...@@ -70,12 +70,14 @@ ...@@ -70,12 +70,14 @@
<script setup> <script setup>
import { ref,defineEmits } from 'vue' import { ref,defineEmits } from 'vue'
import {staffAvatorApi} from '../../../api/other'
import { Delete, Plus, ZoomIn, InfoFilled } from '@element-plus/icons-vue' import { Delete, Plus, ZoomIn, InfoFilled } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { message } from 'ant-design-vue'
// 获取父组件传递的值 // 获取父组件传递的值
const emits = defineEmits(['getAvatar']) const emits = defineEmits(['getAvatar'])
const props = defineProps(['updataImageCount'])
// 组件引用 // 组件引用
const uploadRef = ref(null) const uploadRef = ref(null)
...@@ -96,7 +98,16 @@ const handleFileChange = (file, files) => { ...@@ -96,7 +98,16 @@ const handleFileChange = (file, files) => {
if (file.status === 'ready') { if (file.status === 'ready') {
fileList.value = files fileList.value = files
emits('getAvatar', files) // console.log('文件列表', fileList.value[0].url)
try {
staffAvatorApi({file:fileList.value[0].raw}).then(res => {
emits('getAvatar', res.fileAddress)
})
} catch (error) {
message.error('上传失败',err)
}
} }
} }
...@@ -104,10 +115,21 @@ const handleFileChange = (file, files) => { ...@@ -104,10 +115,21 @@ const handleFileChange = (file, files) => {
* 删除文件处理 * 删除文件处理
*/ */
const handleRemove = (file) => { const handleRemove = (file) => {
console.log('删除文件',file)
if (file) {
fileList.value = fileList.value.filter(item => item.uid !== file.uid) fileList.value = fileList.value.filter(item => item.uid !== file.uid)
ElMessage.success(`已移除 ${file.name}`) ElMessage.success(`已移除 ${file.name}`)
} }else {
fileList.value = []
}
}
// 暴露方法给父组件
defineExpose({
handleRemove,
});
/** /**
* 预览图片处理 * 预览图片处理
*/ */
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
</div> </div>
</div> </div>
<div class="card-table"> <div class="card-table">
<tabledata :tableShowData="tableShowData" :Operation="Operation" ></tabledata> <tabledata :tableShowData="tableShowData" :Operation="Operation" @tableDataRefresh="tableDataRefresh"></tabledata>
</div> </div>
</div> </div>
...@@ -57,13 +57,13 @@ ...@@ -57,13 +57,13 @@
:close-on-click-modal = false :close-on-click-modal = false
:show-close = 'false' :show-close = 'false'
> >
<resetpassword :userInfo="resetPasswordData" @closeResetPasswordDialogFn="closeResetPasswordDialogFn"></resetpassword> <resetpassword :userInfo="resetPasswordData" @closeResetPasswordDialogFn="closeResetPasswordDialogFn" :isResetPasswordDialogVisible="isResetPasswordDialogVisible"></resetpassword>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import { reactive, computed, ref, onMounted } from 'vue'; import { reactive, computed, ref, onMounted, watch } from 'vue';
import searchtop from '../../commentcomponents/searchtop/index.vue'; import searchtop from '../../commentcomponents/searchtop/index.vue';
import tabledata from '../../commentcomponents/tabledata/index.vue'; import tabledata from '../../commentcomponents/tabledata/index.vue';
import userinfo from '../components/showuserinfo.vue' import userinfo from '../components/showuserinfo.vue'
...@@ -71,9 +71,13 @@ import resetpassword from '../components/resetpassword.vue' ...@@ -71,9 +71,13 @@ import resetpassword from '../components/resetpassword.vue'
import editinfo from '../components/editinfo.vue'; import editinfo from '../components/editinfo.vue';
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import AddInfo from '../components/addInfo.vue'; import AddInfo from '../components/addInfo.vue';
import { staffGetPageInfoApi } from "../../../api/staff.js" import { staffGetPageInfoApi,staffRemoveInfoApi } from "../../../api/staff.js"
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { useRoute } from 'vue-router';
import useAppStore from '../../../store/module/app.js';
import { el } from 'element-plus/es/locales.mjs';
const useAppStoreInstance = useAppStore()
const route = useRoute()
// import Operation from 'ant-design-vue/es/transfer/operation'; // import Operation from 'ant-design-vue/es/transfer/operation';
// 定义一个名为searchShowData的ref变量,它是一个数组,数组中的每个元素都是一个对象,对象中包含了label、placeholder、type、content和options等属性 // 定义一个名为searchShowData的ref变量,它是一个数组,数组中的每个元素都是一个对象,对象中包含了label、placeholder、type、content和options等属性
const searchShowData = ref([ const searchShowData = ref([
...@@ -132,18 +136,27 @@ const rowData = ref([]) ...@@ -132,18 +136,27 @@ const rowData = ref([])
* @param {Object} data - 要编辑的数据对象 * @param {Object} data - 要编辑的数据对象
*/ */
const editData = (data) => { const editData = (data) => {
try {
if(useAppStoreInstance.chackCurPersionOpition()){
// 打开编辑抽屉 // 打开编辑抽屉
isEditDrawer.value = true isEditDrawer.value = true
// 设置要编辑的数据 // 设置要编辑的数据
rowData.value = data rowData.value = data
console.log("000000-------1-----0000000000",data) console.log("000000-------1-----0000000000",data)
}else{
ElMessage.error("没有权限打开编辑抽屉")
}
} catch (error) {
ElMessage.error("打开编辑抽屉错")
}
} }
/** /**
* 关闭编辑抽屉的方法 * 关闭编辑抽屉的方法
* 调用后将关闭编辑抽屉并重置相关状态 * 调用后将关闭编辑抽屉并重置相关状态
*/ */
const closeEditDrawer = () => { const closeEditDrawer = () => {
console.log("000000-------23-----0000000000") console.log("000000-------23-----0000000000")
// 关闭编辑抽屉 // 关闭编辑抽屉
isEditDrawer.value = false isEditDrawer.value = false
...@@ -155,6 +168,9 @@ const closeEditDrawer = () => { ...@@ -155,6 +168,9 @@ const closeEditDrawer = () => {
* 显示确认对话框,用户确认后执行删除操作 * 显示确认对话框,用户确认后执行删除操作
*/ */
const deleteData = (data) => { const deleteData = (data) => {
try {
if(useAppStoreInstance.chackCurPersionOpition()){
console.log("000000---2---------0000000000",data) console.log("000000---2---------0000000000",data)
ElMessageBox.confirm( ElMessageBox.confirm(
'删除操作不可逆,是否继续?', '删除操作不可逆,是否继续?',
...@@ -167,57 +183,64 @@ const deleteData = (data) => { ...@@ -167,57 +183,64 @@ const deleteData = (data) => {
} }
) )
.then(() => { .then(() => {
console.log("000000---1-456464564------0000000000","我是确认") // console.log("000000---1-456464564------0000000000","我是确认",data.id)
try { try {
const res = staffRemoveInfoApi({id:data.id});
let submitData = {id:data.id};
staffRemoveInfoApi(submitData).then(res => {
if (res.code === 200) { if (res.code === 200) {
ElMessage({ type: 'success', message: '已删除' }); ElMessage({ type: 'success', message: '已删除' });
tableDataRefresh(); tableDataRefresh();
} else { } else {
ElMessage({ type: 'error', message: '删除失败:' + res.msg }); ElMessage({ type: 'error', message: '删除失败:' + res.msg });
} }
})
} catch (err) { } catch (err) {
// API 请求失败时在此捕获,不向上冒泡 // API 请求失败时在此捕获,不向上冒泡
ElMessage({ type: 'error', message: '删除失败(网络错误)' }); ElMessage({ type: 'error', message: `删除失败(网络错误)err:${err}` });
// console.error('删除接口失败:', err);
} }
}) }).catch(() => {
.catch(() => {
ElMessage({ ElMessage({
type: 'info', type: 'info',
message: '取消删除', message: '取消删除',
}) })
}) })
}else{
ElMessage.error("没有权限打开编辑抽屉")
}
} catch (error) {
ElMessage.error("打开编辑抽屉错")
}
} }
// const deleteUserDate = (data) => {
// staffRemoveInfoApi(data).then((res) => {
// console.log(res,'--------------8--');
// if(res.code==200){
// ElMessage({
// type: 'success',
// message: '已删除',
// })
// tableDataRefresh()
// }
// }).catch(() => {
// ElMessage({
// type: 'info',
// message: '删除shibai',
// })
// })
// }
const isResetPasswordDialogVisible = ref(false); // 控制重置密码弹窗的显示状态 const isResetPasswordDialogVisible = ref(false); // 控制重置密码弹窗的显示状态
const closeResetPasswordDialogFn = () => { const closeResetPasswordDialogFn = () => {
isResetPasswordDialogVisible.value = false isResetPasswordDialogVisible.value = false
} }
const resetPasswordData = ref({}); const resetPasswordData = ref({});
const resetData = (data) => { const resetData = (data) => {
if(useAppStoreInstance.userInfo.id === data.id){
ElMessage.error("不能重置自己的密码")
}else{
try {
if(useAppStoreInstance.chackCurPersionOpition() ){
console.log("000000-----3-------0000000000",data) console.log("000000-----3-------0000000000",data)
isResetPasswordDialogVisible.value = true; isResetPasswordDialogVisible.value = true;
resetPasswordData.value = data; resetPasswordData.value = data;
}else{
ElMessage.error("没有权限打开重置密码抽屉")
}
} catch (error) {
ElMessage.error("打开重置密码抽屉错误")
}
}
} }
const showuserinfodata = ref({}) const showuserinfodata = ref({})
...@@ -264,10 +287,10 @@ const initTableData = () => { ...@@ -264,10 +287,10 @@ const initTableData = () => {
}); });
tempData.forEach(element => { tempData.forEach(element => {
element.status = element.status === 0 ? true : false;
element.Operation = [...Operation.value] element.Operation = [...Operation.value]
}); });
tableShowData.value[0].tableBody = tempData tableShowData.value[0].tableBody = tempData
tableShowData.value[0].total = res.data.total tableShowData.value[0].total = res.data.total
tableShowData.value[0].pageSize = res.data.pageSize tableShowData.value[0].pageSize = res.data.pageSize
...@@ -285,11 +308,20 @@ onMounted(() => { ...@@ -285,11 +308,20 @@ onMounted(() => {
// console.log(tableShowData.value, '错误'); // console.log(tableShowData.value, '错误');
initTableData() initTableData()
}) })
// 计算容器样式 // 计算容器样式
const containerStyle = computed(() => ({ const containerStyle = computed(() => ({
minHeight: layoutConfig.containerMinHeight, minHeight: layoutConfig.containerMinHeight,
'--card-spacing': layoutConfig.cardSpacing, '--card-spacing': layoutConfig.cardSpacing,
})); }));
// 监听路由变化
watch(route, () => {
useAppStoreInstance.initavatarUrlFn()
initTableData()
}, { immediate: true });
</script> </script>
<style scoped> <style scoped>
......
...@@ -101,10 +101,10 @@ ...@@ -101,10 +101,10 @@
</div> </div>
</div> </div>
<div class="drone-info-footer"> <div class="drone-info-footer">
<el-steps style="width: 100%;" :active="0"> <el-steps style="width: 100%;" :active="2">
<el-step > <el-step >
<template #icon> <template #icon>
<el-icon style="background-color: rgba(0, 0, 0, 0);"><Edit style="background-color: rgba(0, 0, 0, 0);"/></el-icon> <el-icon style="padding: 0;margin: 0;"><img style="width: 150%;height: 150%;" src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg" alt="" srcset=""></el-icon>
</template> </template>
<template #title> <template #title>
<span>待命</span> <span>待命</span>
...@@ -115,7 +115,7 @@ ...@@ -115,7 +115,7 @@
<el-icon style="background-color: rgba(0, 0, 0, 0);"><Edit style="background-color: rgba(0, 0, 0, 0);"/></el-icon> <el-icon style="background-color: rgba(0, 0, 0, 0);"><Edit style="background-color: rgba(0, 0, 0, 0);"/></el-icon>
</template> </template>
<template #title> <template #title>
<span>待命</span> <span>飞行中</span>
</template> </template>
</el-step> </el-step>
<el-step > <el-step >
...@@ -123,7 +123,7 @@ ...@@ -123,7 +123,7 @@
<el-icon style="background-color: rgba(0, 0, 0, 0);"><Edit style="background-color: rgba(0, 0, 0, 0);"/></el-icon> <el-icon style="background-color: rgba(0, 0, 0, 0);"><Edit style="background-color: rgba(0, 0, 0, 0);"/></el-icon>
</template> </template>
<template #title> <template #title>
<span>待命</span> <span >任务完成</span>
</template> </template>
</el-step> </el-step>
<el-step > <el-step >
...@@ -131,7 +131,7 @@ ...@@ -131,7 +131,7 @@
<el-icon style="background-color: rgba(0, 0, 0, 0);"><Edit style="background-color: rgba(0, 0, 0, 0);"/></el-icon> <el-icon style="background-color: rgba(0, 0, 0, 0);"><Edit style="background-color: rgba(0, 0, 0, 0);"/></el-icon>
</template> </template>
<template #title> <template #title>
<span>待命</span> <span>返航</span>
</template> </template>
</el-step> </el-step>
...@@ -245,6 +245,12 @@ const getBatteryColor = (level) => { ...@@ -245,6 +245,12 @@ const getBatteryColor = (level) => {
width: 100%; width: 100%;
/* background-color: #165DFF; */ /* background-color: #165DFF; */
} }
.drone-info-footer span{
font-size: small;
/* background-color: #165DFF; */
}
.data-column { .data-column {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
......
<template>
<div class="dashboard-container" :style="containerStyle">
<main class="main-content">
<!-- 左侧区域 -->
<div class="left-section">
<!-- 左侧上部分 -->
<div class="left-top">
<!-- 区域信息概览 -->
<div class="info-card region-overview">
<searchtop :searchShowData="searchShowData" ></searchtop>
</div>
</div>
<!-- 左侧下部分:统计区域 -->
<div class="info-card stats-alerts">
<div class="card-tabs">
<span class="title" >用户列表</span>
<div class="card-actions">
<el-button class="operation-button" type="primary" size="small" style="width: 15%;">导入</el-button>
<el-button class="operation-button" type="primary" size="small">导出</el-button>
<el-button class="operation-button" type="primary" size="small" @click="handleAddInfoFn">新增</el-button>
<el-button class="operation-button" type="primary" size="small" @click="tableDataRefresh"><el-icon><RefreshRight /></el-icon></el-button>
<el-button class="operation-button" type="primary" size="small"><el-icon><FullScreen /></el-icon></el-button>
</div>
</div>
<div class="card-table">
<tableshow :tableShowData="tableShowData" @editData="uavseedetails" @deleteData="deleteData"></tableshow>
</div>
</div>
</div>
</main>
</div>
</template>
<script setup>
import { reactive, computed, ref, onMounted } from 'vue';
import searchtop from '../../commentcomponents/searchtop/index.vue';
import { ElMessage, ElMessageBox } from 'element-plus'
import tableshow from '../../commentcomponents/tabledata/index.vue';
import { staffGetPageInfoApi } from "../../../api/staff.js"
import { message } from 'ant-design-vue';
// import Operation from 'ant-design-vue/es/transfer/operation';
// 定义一个名为searchShowData的ref变量,它是一个数组,数组中的每个元素都是一个对象,对象中包含了label、placeholder、type、content和options等属性
const searchShowData = ref([
{ label: '用户姓名', placeholder: "请输入", type: 'input',content:'' },
{ label: '用户状态', placeholder: "请选择", type: 'select',content:'' , options: [{label:'启用', value:'启用'}, {label:'禁用', value:'禁用'}] },
{ label: '用户角色', placeholder: "请选择", type: 'select', content:'' ,options: [{label:'管理员', value:'管理员'}, {label:'巡查员', value:'巡查员'}]},
])
// 响应式布局配置
const layoutConfig = reactive({
containerMinHeight: '100vh',
cardSpacing: '1rem',
statsMinHeight: '300px'
});
const isUserInfoDialogVisible = ref(false); // 控制用户信息弹窗的显示状态
/**
* 控制添加抽屉的显示状态
* @type {Ref<boolean>} - 响应式布尔值,用于控制添加抽屉的显示
*/
const isAddDrawer= ref(false)
const closeAddDrawer = () => {
console.log("000000-------3-----0000000000")
// 关闭添加抽屉
isAddDrawer.value = false
}
const handleAddInfoFn = () => {
isAddDrawer.value = true
}
/**
* 刷新表格数据的方法
* 当调用此方法时,会触发表格数据的重新加载
*/
const tableDataRefresh = () => {
initTableData()
console.log("000000-------2-----0000000000,这里获取到最新的数据")
}
/**
* 控制编辑抽屉的显示状态
* @type {Ref<boolean>} - 响应式布尔值,用于控制编辑抽屉的显示
*/
const isEditDrawer = ref(false)
/**
* 存储行数据
* @type {Ref<Array>} - 响应式数组,用于存储当前选中的行数据
*/
const rowData = ref([])
/**
* 编辑数据的方法
* @param {Object} data - 要编辑的数据对象
*/
const editData = (data) => {
// 打开编辑抽屉
isEditDrawer.value = true
// 设置要编辑的数据
rowData.value = data
console.log("000000-------1-----0000000000",data)
}
/**
* 关闭编辑抽屉的方法
* 调用后将关闭编辑抽屉并重置相关状态
*/
const closeEditDrawer = () => {
console.log("000000-------23-----0000000000")
// 关闭编辑抽屉
isEditDrawer.value = false
}
/**
* 删除数据的方法
* @param {Object} data - 要删除的数据对象
* 显示确认对话框,用户确认后执行删除操作
*/
const deleteData = (data) => {
console.log("000000---2---------0000000000",data)
ElMessageBox.confirm(
'删除操作不可逆,是否继续?',
'删除',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
draggable: true,
}
)
.then(() => {
console.log("000000---1-456464564------0000000000","我是确认")
try {
const res = staffRemoveInfoApi({id:data.id});
if (res.code === 200) {
ElMessage({ type: 'success', message: '已删除' });
tableDataRefresh();
} else {
ElMessage({ type: 'error', message: '删除失败:' + res.msg });
}
} catch (err) {
// API 请求失败时在此捕获,不向上冒泡
ElMessage({ type: 'error', message: '删除失败(网络错误)' });
// console.error('删除接口失败:', err);
}
})
.catch(() => {
ElMessage({
type: 'info',
message: '取消删除',
})
})
}
// const deleteUserDate = (data) => {
// staffRemoveInfoApi(data).then((res) => {
// console.log(res,'--------------8--');
// if(res.code==200){
// ElMessage({
// type: 'success',
// message: '已删除',
// })
// tableDataRefresh()
// }
// }).catch(() => {
// ElMessage({
// type: 'info',
// message: '删除shibai',
// })
// })
// }
const isResetPasswordDialogVisible = ref(false); // 控制重置密码弹窗的显示状态
const closeResetPasswordDialogFn = () => {
isResetPasswordDialogVisible.value = false
}
const resetPasswordData = ref({});
const resetData = (data) => {
console.log("000000-----3-------0000000000",data)
isResetPasswordDialogVisible.value = true;
resetPasswordData.value = data;
}
const showuserinfodata = ref({})
const userData = (data) => {
console.log("000000------4------0000000000",data)
isUserInfoDialogVisible.value = true
showuserinfodata.value = data
}
const uavseedetails = (data) => {
console.log(data,'---------5---------')
// router.push({
// path: '/dialog_infoShow_img_vid/uavseedetails',
// query: { data: JSON.stringify(data) },
// })
}
const tableShowData = ref([{
tableHeader:[
{label: '序号', prop: 'id'},
{label: '无人机编号', prop: 'uavid'},
{label: '无人机机型', prop: 'uavtype'},
{label: '无人机状态', prop: 'uavstate'},
{label: '无人机载弹数量', prop: 'uavpayloadcapacity'},
{label: '上次维护时间', prop: 'premaintenancetime'},
{label: '弹药信息', prop: 'ammunitioninfo'},
{label: '操作', prop: 'Operation'}
],
tableBody:[
{id: 1, uavid: 'UAV-0001', uavtype: 'UAV-M100', uavstate: '正常', uavpayloadcapacity: '10', premaintenancetime: '2023-05-01', ammunitioninfo: '弹药数量:1000',Operation:[ { label: '查看详情', type: 'primary', icon: 'EditPen', click: uavseedetails }]}
]
}
])
const Operation = ref( [ { label: '查看详情', type: 'primary', icon: 'EditPen', click: uavseedetails }])
const initTableData = () => {
// 这里要使用无人机列表接口
// staffGetPageInfoApi({
// "currentPageNum": 1,
// "currentPageSize": 10,
// }).then(res => {
// if(res.code === 200){
// let tempData = res.data.list
// tableShowData.value[0].tableBody = []
// //获取未逻辑删除的数据
// console.log(res.data,"--------999999999----")
// tempData = tempData.filter(element => {
// return element.isDeleted === 0;
// });
// tempData.forEach(element => {
// element.Operation = [...Operation.value]
// });
// tableShowData.value[0].tableBody = tempData
// tableShowData.value[0].total = res.data.total
// tableShowData.value[0].pageSize = res.data.pageSize
// console.log(tableShowData.value, '展示数据');
// }
// }).catch(err => {
// message.error(err.message)
// })
}
onMounted(() => {
// console.log(tableShowData.value, '错误');
initTableData()
})
// 计算容器样式
const containerStyle = computed(() => ({
minHeight: layoutConfig.containerMinHeight,
'--card-spacing': layoutConfig.cardSpacing,
}));
</script>
<style scoped>
.dashboard-container {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: rgba(0, 0, 0, 0.05);
color: #333;
overflow: hidden;
}
.main-content {
display: flex;
gap: var(--card-spacing);
padding: 0.4rem;
height: 100vh;
box-sizing: border-box;
}
.left-section {
flex: 2;
display: flex;
flex-direction: column;
gap: var(--card-spacing);
height: 100%;
}
.left-top {
height: auto;
}
/* 通用卡片样式 */
.info-card {
background-color: #fff;
padding: 1rem;
border-radius: 8px;
box-sizing: border-box;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
overflow: auto;
}
.region-overview {
flex: 1;
min-width: 200px;
}
/* 统计区域 */
.stats-alerts {
display: flex;
flex-direction: column;
gap: 1rem;
height: 67%;
}
.card-tabs {
display: flex;
border-bottom: 1px solid #292828;
/* margin-bottom: 1rem; */
font-size: 1.2rem;
color: #000000;
/* background-color: #002aff; */
height: 8%;
display: flex;
align-items: center;
width: 100%;
padding: 10px;
}
.title{
width: 70%;
}
.card-actions {
display: flex;
flex: 1;
justify-content: space-between;
align-items: center;
/* margin-bottom: 1rem; */
height: 8%;
padding: 1.5rem;
}
.operation-button {
width: 15%;
}
.card-tabs-footer {
display: flex;
/* margin-bottom: 1rem; */
font-size: 1.2rem;
color: #000000;
/* background-color: #002aff; */
height: 8%;
align-items: center;
width: 100%;
padding: 10px 0;
justify-content: flex-end;
align-items: center;
}
/* 文本样式 */
.page-text {
color: #606266;
font-size: 14px;
white-space: nowrap; /* 防止文本换行 */
}
/* 修复 Element Plus 分页组件默认的块级布局问题 */
:deep .el-pagination {
display: flex;
align-items: center;
margin: 0; /* 清除默认外边距 */
}
.pagination-block{
height: 8%;
}
.card-table{
width: 100%;
/* display: flex; */
/* background-color: #000000; */
flex:1;
overflow: hidden;
border-radius: 8PX;
}
/* 响应式适配 */
@media (max-width: 1024px) {
.left-top {
flex-direction: column;
}
.region-overview {
width: 100%;
flex: none;
}
}
@media (max-width: 768px) {
.stats-alerts {
flex-direction: column;
}
}
</style>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论