提交 7701ae0c authored 作者: zhuyongshuai's avatar zhuyongshuai

8/14

上级 78ccbadd
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
"dependencies": { "dependencies": {
"ant-design-vue": "^4.2.6", "ant-design-vue": "^4.2.6",
"axios": "0.28.1", "axios": "0.28.1",
"dayjs": "^1.11.13",
"echarts": "5.5.1", "echarts": "5.5.1",
"element-plus": "^2.9.6", "element-plus": "^2.9.6",
"file-saver": "2.0.5", "file-saver": "2.0.5",
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
"dependencies": { "dependencies": {
"ant-design-vue": "^4.2.6", "ant-design-vue": "^4.2.6",
"axios": "0.28.1", "axios": "0.28.1",
"dayjs": "^1.11.13",
"echarts": "5.5.1", "echarts": "5.5.1",
"element-plus": "^2.9.6", "element-plus": "^2.9.6",
"file-saver": "2.0.5", "file-saver": "2.0.5",
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
</template> </template>
<script setup> <script setup>
onMounted(() => { onMounted(() => {
nextTick(() => { nextTick(() => {
// 初始化主题样式 // 初始化主题样式
......
...@@ -40,4 +40,12 @@ export function staffDelApi(data) { ...@@ -40,4 +40,12 @@ export function staffDelApi(data) {
method: 'get', method: 'get',
params: data params: data
}) })
}
export function staffAerialViewApi(data) {
return request({
url: `/project/list`,
method: 'get',
params: data
})
} }
\ No newline at end of file
...@@ -40,4 +40,65 @@ export function staffDelApi(data) { ...@@ -40,4 +40,65 @@ export function staffDelApi(data) {
method: 'get', method: 'get',
params: data params: data
}) })
} }
\ No newline at end of file
// 人员分页
export function staffGetPageInfoApi(data) {
return request({
url: `/user/page`,
method: 'POST',
data,
})
}
/**
* 更新员工信息的API接口函数
* @param {Object} data - 包含员工更新信息的对象
* @returns {Promise} - 返回一个Promise对象,包含服务器响应的数据
*/
export function staffUpdateInfoApi(data) {
return request({ // 调用request方法发送HTTP请求
url: `/user/update`, // 请求的URL路径
method: 'POST', // 请求方法为POST
data, // 请求携带的数据
})
}
export function staffUpdatePasswordApi(data) {
return request({ // 调用request方法发送HTTP请求
url: `/user/updatePassword`, // 请求的URL路径
method: 'POST', // 请求方法为POST
data, // 请求携带的数据
})
}
export function staffAddInfoApi(data) {
return request({ // 调用request方法发送HTTP请求
url: `/user/add`, // 请求的URL路径
method: 'POST', // 请求方法为POST
data, // 请求携带的数据
})
}
export function staffGetInfoApi(data) {
return request({ // 调用request方法发送HTTP请求
url: `/user/getInfo`, // 请求的URL路径
method: 'GET', // 请求方法为POST
data, // 请求携带的数据
})
}
export function staffRemoveInfoApi(data) {
return request({ // 调用request方法发送HTTP请求
url: `/user/remove`, // 请求的URL路径
method: 'POST', // 请求方法为POST
data, // 请求携带的数据
})
}
export function staffResetPasswordApi(data) {
return request({ // 调用request方法发送HTTP请求
url: `/user/resetPassword`, // 请求的URL路径
method: 'POST', // 请求方法为POST
data, // 请求携带的数据
})
}
<template> <template>
<div class="component-upload-image"> <div class="component-upload-image" >
<el-upload multiple :action="uploadImgUrl" list-type="picture-card" :on-success="handleUploadSuccess" <el-upload multiple :action="uploadImgUrl" list-type="picture-card" :on-success="handleUploadSuccess"
:before-upload="handleBeforeUpload" :limit="limit" :on-error="handleUploadError" :on-exceed="handleExceed" :before-upload="handleBeforeUpload" :limit="limit" :on-error="handleUploadError" :on-exceed="handleExceed"
ref="imageUpload" :before-remove="handleDelete" :show-file-list="true" :headers="headers" :file-list="fileList" ref="imageUpload" :before-remove="handleDelete" :show-file-list="true" :headers="headers" :file-list="fileList"
:on-preview="handlePictureCardPreview" :class="{ hide: fileList.length >= limit }"> :on-preview="handlePictureCardPreview" :class="{ hide: fileList.length >= limit }">
<el-icon class="avatar-uploader-icon"> <el-icon class="avatar-uploader-icon" style="color: black;" >
<plus /> <plus />
</el-icon> </el-icon>
</el-upload> </el-upload>
<!-- 上传提示 --> <!-- 上传提示 -->
<div class="el-upload__tip" v-if="showTip"> <div class="el-upload__tip" style="color: black;" v-if="showTip">
请上传 请上传
<template v-if="fileSize"> <template v-if="fileSize">
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
......
...@@ -2,7 +2,10 @@ import hasRole from './permission/hasRole' ...@@ -2,7 +2,10 @@ import hasRole from './permission/hasRole'
import hasPermi from './permission/hasPermi' import hasPermi from './permission/hasPermi'
// 导出一个默认函数,用于注册自定义指令
export default function directive(app) { export default function directive(app) {
// 注册一个名为hasRole的自定义指令
app.directive('hasRole', hasRole) app.directive('hasRole', hasRole)
// 注册一个名为hasPermi的自定义指令
app.directive('hasPermi', hasPermi) app.directive('hasPermi', hasPermi)
} }
\ No newline at end of file
<template>
<div class="allmain">
<div class="tip">
<div class="tip1">无人灭火机器人数字孪生后台管理系统</div>
<div class="tip2">国际花园小区</div>
<div class="avatar-container" v-if="useAppStore.isCurUserLogin">
<el-dropdown>
<el-avatar shape="square" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"/>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="toSatffManage">人员管理</el-dropdown-item>
<el-dropdown-item @click="curUserLogout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
<div class="main">
<div class="left">
<div class="left1">
<div
class="menu"
@click="menuclick(item)"
:class="useAppStoreInstance.num == item.id ? 'active' : ''"
v-for="item in menuList"
:key="item.id"
>
{{ item.name }}
</div>
</div>
<div class="menuTo">数字孪生管理</div>
</div>
<div class="right">
<div class="right-content">
<router-view v-slot="{ Component, route }" :key="$route.fullPath">
<keep-alive>
<component v-if="!route.meta.link" :is="Component" :key="route.path"/>
</keep-alive>
</router-view>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import useAppStore from '../store/module/app';
import { getToken, removeToken} from '../utils/auth'
const router = useRouter();
const route = useRoute();
const useAppStoreInstance = useAppStore();
onMounted(() => {
if (localStorage.getItem('menu')) {
useAppStoreInstance.num = localStorage.getItem('menu')
} else {
numFn()
}
})
const toSatffManage = () => {
router.push({
path: '/staffManage'
})
useAppStoreInstance.num = 6
}
const curUserLogout = () => {
removeToken()
router.push('/login')
useAppStoreInstance.isCurUserLogin = false
}
const numFn = () => {
switch (route.fullPath) {
case '/home': useAppStoreInstance.num = 1; break;
case '/buildManage': useAppStoreInstance.num = 2; break;
case '/uavManage': useAppStoreInstance.num = 3; break;
case '/fireManage': useAppStoreInstance.num = 4; break;
case '/fileHistory': useAppStoreInstance.num = 5; break;
case '/staffManage': useAppStoreInstance.num = 6; break;
}
}
const menuclick = (row) => {
localStorage.setItem('menu', row.id)
useAppStoreInstance.num = row.id
router.push(row.path)
}
const menuList = ref([
{ name: '工作台', id: 1, path: '/home' },
{ name: '建筑管理', id: 2, path: '/buildManage' },
{ name: '无人机管理', id: 3, path: '/uavManage' },
{ name: '火情管理', id: 4, path: '/fireManage' },
{ name: '历史火情', id: 5, path: '/fileHistory' },
{ name: '人员管理', id: 6, path: '/staffManage' },
])
</script>
<style scoped>
.allmain {
width: 100vw;
min-width: 1024px;
height: 100vh;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #051d39;
color: #ffffff;
display: flex;
flex-direction: column;
}
.tip {
width: 100%;
height: 9vh;
min-height: 60px;
background-image: url(../static/image/tip.png);
background-size: cover;
display: flex;
align-items: center;
position: relative;
padding: 0 20px;
flex-shrink: 0;
}
.tip1 {
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: bold;
background: linear-gradient(to bottom, #FFFFFF 40%, #96CBFF 100%);
background-clip: text;
-webkit-background-clip: text;
color: transparent;
font-size: clamp(28px, 1.2vw, 38px);
white-space: nowrap;
margin-right: 20px;
}
.tip2 {
background-image: url(../static/image/tip_name.png);
background-size: contain;
background-repeat: no-repeat;
background-position: center;
padding: 0 20px;
height: 4vh;
min-height: 30px;
line-height: 4vh;
font-size: clamp(16px, 1.5vw, 24px);
white-space: nowrap;
}
.avatar-container {
margin-left: auto;
cursor: pointer;
margin-right: 10px;
}
.main {
display: flex;
flex: 1;
min-height: 0;
width: 100%;
}
.left {
width: 280px;
min-width: 200px;
display: flex;
flex-direction: column;
flex-shrink: 0;
background-color: #0a2a4a;
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.2);
}
.left1 {
flex: 1;
overflow-y: auto;
}
.menu {
height: 70px;
min-height: 50px;
line-height: 70px;
padding: 0 15px;
text-align: center;
cursor: pointer;
font-size: clamp(14px, 1.2vw, 18px);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: all 0.3s ease;
position: relative;
}
.menu:hover {
background: rgba(5, 113, 196, 0.3);
}
.menu.active {
background: linear-gradient(270deg, rgba(6, 176, 225, 0) 0%, #0571C4 100%);
}
.menu.active::after {
content: '';
position: absolute;
right: 0;
top: 0;
height: 100%;
width: 3px;
background: #06b0e1;
}
.menuTo {
height: 100px;
min-height: 60px;
display: flex;
justify-content: center;
align-items: center;
background-image: url(../static/image/shuziluansheng.png);
background-size: contain;
background-repeat: no-repeat;
background-position: center;
margin: 2px 2px 0px 2px;
padding: 0 20px;
font-size: clamp(28px, 1.2vw, 38px);
color: rgba(255, 255, 255, 0.9);
text-shadow: 0 0 10px rgba(6, 176, 225, 0.5);
}
.right {
flex: 1;
min-width: 0;
padding: 0;
overflow: hidden;
display: flex;
flex-direction: column;
background-color: #0a1e38;
position: relative;
}
.right-content {
flex: 1;
overflow: auto;
padding: 20px;
background: linear-gradient(135deg, rgba(10, 30, 56, 0.8) 0%, rgba(5, 29, 57, 0.9) 100%);
}
/* 滚动条样式 */
.right-content::-webkit-scrollbar {
width: 6px;
}
.right-content::-webkit-scrollbar-track {
background: rgba(5, 29, 57, 0.5);
}
.right-content::-webkit-scrollbar-thumb {
background: rgba(6, 176, 225, 0.5);
border-radius: 3px;
}
.right-content::-webkit-scrollbar-thumb:hover {
background: rgba(6, 176, 225, 0.8);
}
/* 响应式调整 */
@media (max-width: 1200px) {
.left {
width: 220px;
}
.menu {
height: 60px;
line-height: 60px;
}
}
@media (max-width: 768px) {
.allmain {
min-width: auto;
}
.main {
flex-direction: column;
}
.left {
width: 100%;
height: auto;
flex-direction: row;
flex-wrap: wrap;
}
.left1 {
display: flex;
flex-wrap: wrap;
height: auto;
}
.menu {
width: 50%;
height: 50px;
line-height: 50px;
}
.menuTo {
display: none;
}
.right {
width: 100%;
}
.right-content {
padding: 15px;
}
.tip1, .tip2 {
font-size: 14px;
}
}
</style>
\ No newline at end of file
差异被折叠。
...@@ -65,4 +65,6 @@ app.use(ElementPlus, { ...@@ -65,4 +65,6 @@ app.use(ElementPlus, {
size: Cookies.get('size') || 'default' size: Cookies.get('size') || 'default'
}) })
app.mount('#app') app.mount('#app')
...@@ -4,28 +4,36 @@ import { ElMessage } from 'element-plus' ...@@ -4,28 +4,36 @@ import { ElMessage } from 'element-plus'
import NProgress from 'nprogress' import NProgress from 'nprogress'
import 'nprogress/nprogress.css' import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth' import { getToken } from '@/utils/auth'
// import { isHttp } from '@/utils/validate' import useAppStore from './store/module/app'
// import { isRelogin } from '@/utils/request'
// import useUserStore from '@/store/modules/user'
// import useSettingsStore from '@/store/modules/settings'
// import usePermissionStore from '@/store/modules/permission'
// NProgress.configure({ showSpinner: false });
const whiteList = ['/login', '/register'];//白名单 const whiteList = ['/login', '/register'];//白名单
const allRoutePath = ["/home","/buildManage","/uavManage","/fireManage","/fileHistory","/staffManage"]
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
console.log(to.path, getToken()); // console.log("99999999------------------",import.meta.env.VITE_API_URL);
if (getToken()) {
useAppStore.isCurUserLogin = true
}
else {
useAppStore.isCurUserLogin = false
}
if (getToken()) { if (getToken()) {
if (to.path === '/') { if (to.path === '/') {
useAppStore.num = 1
localStorage.setItem('menu', 1)//默认选中工作台
console.log("99999999------------------",import.meta.env.VITE_API_URL);
next('/home') next('/home')
} else if (whiteList.indexOf(to.path) !== -1) { } else if (whiteList.indexOf(to.path) !== -1) {
next() next()
} else { } else {
console.log(999); // console.log(999, to.path, allRoutePath.indexOf(to.path));
useAppStore.num = allRoutePath.indexOf(to.path)
localStorage.setItem('menu', allRoutePath.indexOf(to.path))
next() next()
} }
} else { } else {
console.log("99999999-----------88888888888888-------",import.meta.env.VITE_API_URL);
// 没有token--是否在白名单 // 没有token--是否在白名单
if (whiteList.indexOf(to.path) !== -1) { if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单,直接进入 // 在免登录白名单,直接进入
...@@ -33,7 +41,9 @@ router.beforeEach((to, from, next) => { ...@@ -33,7 +41,9 @@ router.beforeEach((to, from, next) => {
} else { } else {
localStorage.clear()//清空所有本地储存 localStorage.clear()//清空所有本地储存
// 重定向到首页 // 重定向到首页
next(`/login`) // 否则全部重定向到登录页 // next(`/login`) // 否则全部重定向到登录页
next()
} }
} }
// NProgress.start() // NProgress.start()
...@@ -97,4 +107,5 @@ router.beforeEach((to, from, next) => { ...@@ -97,4 +107,5 @@ router.beforeEach((to, from, next) => {
router.afterEach(() => { router.afterEach(() => {
// NProgress.done() // NProgress.done()
}) })
...@@ -43,7 +43,8 @@ export const constantRoutes = [ ...@@ -43,7 +43,8 @@ export const constantRoutes = [
// }, // },
{ {
path: '/login', path: '/login',
component: () => import('@/views/login/index.vue'), component: () => import('../views/login/index.vue'),
// component: () => import('@/views/login/index.vue'),
hidden: true hidden: true
}, },
{ {
...@@ -66,13 +67,14 @@ export const constantRoutes = [ ...@@ -66,13 +67,14 @@ export const constantRoutes = [
path: '/home', path: '/home',
component: () => import('../views/home/index.vue'), component: () => import('../views/home/index.vue'),
name: 'Home', name: 'Home',
meta: { title: '工作台', icon: 'dashboard', affix: true } meta: { title: '工作台', icon: 'dashboard', affix: true },
}, },
{ {
path: '/buildManage', path: '/buildManage',
component: () => import('../views/buildManage/index.vue'), component: () => import('../views/buildManage/index.vue'),
name: 'BuildManage', name: 'BuildManage',
meta: { title: '建筑管理', icon: 'dashboard', affix: true } meta: { title: '建筑管理', icon: 'dashboard', affix: true },
}, },
{ {
path: '/uavManage', path: '/uavManage',
...@@ -84,7 +86,8 @@ export const constantRoutes = [ ...@@ -84,7 +86,8 @@ export const constantRoutes = [
path: '/fireManage', path: '/fireManage',
component: () => import('../views/fireManage/index.vue'), component: () => import('../views/fireManage/index.vue'),
name: 'FireManage', name: 'FireManage',
meta: { title: '火情管理', icon: 'dashboard', affix: true } meta: { title: '火情管理', icon: 'dashboard', affix: true },
props: true
}, },
{ {
path: '/fileHistory', path: '/fileHistory',
...@@ -98,6 +101,57 @@ export const constantRoutes = [ ...@@ -98,6 +101,57 @@ export const constantRoutes = [
name: 'StaffManage', name: 'StaffManage',
meta: { title: '人员管理', icon: 'dashboard', affix: true } meta: { title: '人员管理', icon: 'dashboard', affix: true }
}, },
{
path: '/analysisPage',
component: () => import('../views/dashboard/analysis/index.vue'),
name: 'analySisPage',
meta: { title: '分析页面', icon: 'dashboard', affix: true }
},
{
path: '/workBenchPage',
component: () => import('../views/dashboard/workbench/index.vue'),
name: 'WorkbenchPage',
meta: { title: '工作台', icon: 'dashboard', affix: true }
},
{
path: '/usermanage',
component: () => import('../views/systemmanage/staffManage/index.vue'),
name: 'systemManage',
meta: { title: '人员管理', icon: 'dashboard', affix: true }
},
{
path: '/areabuildmanage',
component: () => import('../views/areabuildmanage/index.vue'),
name: 'areaBuildmanage',
meta: { title: '建筑管理', icon: 'dashboard', affix: true }
},
{
path: '/uavdispatch',
component: () => import('../views/uavshowdate/index.vue'),
name: 'uavDispatch',
meta: { title: '无人机调度', icon: 'dashboard', affix: true }
},
{
path: '/firesurveillance',
component: () => import('../views/areafiremanage/firesurveillance/index.vue'),
name: 'fireSurveillance',
meta: { title: '当前火情', icon: 'dashboard', affix: true }
},
{
path: '/historyfire',
component: () => import('../views/areafiremanage/historyfire/index.vue'),
name: 'historyFire',
meta: { title: '历史火情', icon: 'dashboard', affix: true },
children:[
{
path: '/historyfire/floordetaildate',
component: () => import('../views/areafiremanage/historyfire/components/floordetail.vue'),
name: 'floorDetailDate',
meta: { title: '详细楼层火情', icon: 'dashboard', affix: true }
},
]
},
] ]
}, },
] ]
......
...@@ -10,7 +10,9 @@ const useAppStore = defineStore( ...@@ -10,7 +10,9 @@ const useAppStore = defineStore(
hide: false hide: false
}, },
device: 'desktop', device: 'desktop',
size: Cookies.get('size') || 'default' size: Cookies.get('size') || 'default',
num:1,
isCurUserLogin: false
}), }),
actions: { actions: {
toggleSideBar(withoutAnimation) { toggleSideBar(withoutAnimation) {
...@@ -39,7 +41,9 @@ const useAppStore = defineStore( ...@@ -39,7 +41,9 @@ const useAppStore = defineStore(
}, },
toggleSideBarHide(status) { toggleSideBarHide(status) {
this.sidebar.hide = status this.sidebar.hide = status
} },
} }
}) })
......
...@@ -3,6 +3,7 @@ import Cookies from 'js-cookie' ...@@ -3,6 +3,7 @@ import Cookies from 'js-cookie'
const TokenKey = 'Admin-Token' const TokenKey = 'Admin-Token'
export function getToken() { export function getToken() {
// console.log('Cookies.get(TokenKey)', Cookies.get(TokenKey))
return Cookies.get(TokenKey) return Cookies.get(TokenKey)
} }
......
...@@ -20,12 +20,17 @@ const service = axios.create({ ...@@ -20,12 +20,17 @@ const service = axios.create({
timeout: 60000 timeout: 60000
}) })
// request拦截器 // request拦截器
service.interceptors.request.use(config => { service.interceptors.request.use(
config => {
console.log('--------------config', config.url)
// 是否需要设置 token // 是否需要设置 token
const isToken = (config.headers || {}).isToken === false const isToken = (config.headers || {}).isToken === false
console.log('--------------isToken', isToken)
// 是否需要防止数据重复提交 // 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
console.log('--------------isRepeatSubmit', isRepeatSubmit,(config.headers || {}).repeatSubmit,config.headers.repeatSubmit,undefined===false)
if (getToken() && !isToken) { if (getToken() && !isToken) {
config.headers['token'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 config.headers['token'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
} }
...@@ -67,13 +72,15 @@ service.interceptors.request.use(config => { ...@@ -67,13 +72,15 @@ service.interceptors.request.use(config => {
} }
} }
return config return config
}, error => { },
console.log(error) error => {
Promise.reject(error) console.log(error)
Promise.reject(error)
}) })
// 响应拦截器 // 响应拦截器
service.interceptors.response.use(res => { service.interceptors.response.use(
res => {
// 未设置状态码则默认成功状态 // 未设置状态码则默认成功状态
const code = res.data.code || 200; const code = res.data.code || 200;
// 获取错误信息 // 获取错误信息
......
<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="buildingNo" class="form-item">
<el-input
v-model="form.buildingNo"
placeholder="请输入建筑编号"
class="input-field"
clearable
/>
</el-form-item>
<!-- 建筑名称 -->
<el-form-item label="建筑名称" prop="buildingName" class="form-item">
<el-input
v-model="form.buildingName"
placeholder="请输入建筑名称"
class="input-field"
clearable
/>
</el-form-item>
<!-- 建筑层数 -->
<el-form-item label="建筑层数(地上)" prop="floorCount" class="form-item">
<el-input
v-model="form.floorCount"
placeholder="请输入地上层数"
type="number"
min="0"
class="input-field"
clearable
/>
</el-form-item>
<!-- 是否有地下室 -->
<el-form-item label="是否含有地下室" prop="hasBasement" class="form-item">
<el-radio-group v-model="form.hasBasement" class="radio-group">
<el-radio :label="1" class="radio-btn"></el-radio>
<el-radio :label="2" class="radio-btn"></el-radio>
</el-radio-group>
</el-form-item>
<!-- 地下室层数 -->
<el-form-item
label="地下室层数"
prop="basementCount"
class="form-item"
:rules="form.hasBasement === 1 ? rules.basementCount : []"
>
<el-input
v-model="form.basementCount"
placeholder="请输入地下室层数"
type="number"
min="0"
class="input-field"
:disabled="form.hasBasement !== 1"
clearable
/>
</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';
// 表单引用
const addBuildingFormRef = ref(null);
// 提交加载状态
const submitting = ref(false);
const emit = defineEmits(['closeAddDrawer']);
// 表单数据
const form = reactive({
buildingNo: '', // 建筑编号
buildingName: '', // 建筑名称
floorCount: null, // 地上层数
hasBasement: 1, // 是否含有地下室 (1:是, 2:否)
basementCount: null, // 地下室层数
description: '' // 建筑功能描述
});
// 表单验证规则
const rules = reactive({
buildingNo: [
{ required: true, message: '请输入建筑编号', trigger: ['blur', 'change'] },
{ min: 2, max: 20, message: '编号长度在2-20个字符之间', trigger: ['blur', 'change'] }
],
buildingName: [
{ required: true, message: '请输入建筑名称', trigger: ['blur', 'change'] },
{ message: '名称长度在2-50个字符之间', trigger: ['blur', 'change'] }
],
floorCount: [
{ required: true, message: '请输入地上层数', trigger: ['blur', 'change'] },
{
validator: (rule, value, callback) => {
if (value === null || value === '') {
callback(new Error('请输入地上层数'));
} else if (isNaN(value)) {
callback(new Error('请输入有效数字'));
} else if (value < 0) {
callback(new Error('层数不能为负数'));
} else {
callback();
}
},
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 () => {
try {
submitting.value = true;
if (form.hasBasement === 2) {
rules.basementCount = [];
}
await addBuildingFormRef.value.validate();
// 准备提交数据
const submitData = {
...form,
hasBasement: form.hasBasement === 1
};
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 handleCancel = () => {
ElMessageBox.confirm('确定要取消新增吗?已填写内容将不保存', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
center: true
}).then(() => {
addBuildingFormRef.value.resetFields();
emit('closeAddDrawer');
}).catch(() => {});
};
// 关闭弹窗
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="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="buildingNo" class="form-item">
<el-input
v-model="form.buildingNo"
placeholder="请输入建筑编号"
class="input-field"
clearable
/>
</el-form-item>
<!-- 建筑名称 -->
<el-form-item label="建筑名称" prop="buildingName" class="form-item">
<el-input
v-model="form.buildingName"
placeholder="请输入建筑名称"
class="input-field"
clearable
/>
</el-form-item>
<!-- 建筑层数 -->
<el-form-item label="建筑层数(地上)" prop="buildingFloorCount" class="form-item">
<el-input
v-model="form.buildingFloorCount"
placeholder="请输入地上层数"
type="number"
min="0"
class="input-field"
clearable
disabled
/>
</el-form-item>
<!-- 地下室层数 -->
<el-form-item
label="地下室层数"
prop="basementCount"
class="form-item"
:rules=" rules.buildingBasementCount "
>
<el-input
v-model="form.buildingBasementCount"
placeholder="请输入地下室层数"
type="number"
min="0"
class="input-field"
disabled
clearable
/>
</el-form-item>
<!-- 建筑功能描述 -->
<el-form-item label="建筑功能描述" prop="buildingDescribe" class="form-item">
<el-input
v-model="form.buildingDescribe"
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, onActivated } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Close } from '@element-plus/icons-vue';
// 表单引用
const addBuildingFormRef = ref(null);
// 提交加载状态
const submitting = ref(false);
const emit = defineEmits(['closeEditDrawer','tableDataRefresh']);
const props = defineProps(['rowData','isBuildingEditDrawer']);
watch(() => props.isBuildingEditDrawer, (newVal) => {
if (newVal) {
console.log('抽屉已打开,执行初始化代码');
initFormData();
}
});
// 表单数据
const form = reactive({
buildingNo: '', // 建筑编号
buildingName: '', // 建筑名称
buildingFloorCount: null, // 地上层数
buildingBasementCount: null, // 地下室层数
buildingDescribe: '' // 建筑功能描述
});
// 初始化表单数据
const initFormData = () => {
if (!props.rowData) return;
const { ...rest } = props.rowData;
// 映射状态值
Object.assign(form, {
...rest,
});
};
// 表单验证规则
const rules = reactive({
buildingNo: [
{ required: true, message: '请输入建筑编号', trigger: ['blur', 'change'] },
{ min: 2, max: 20, message: '编号长度在2-20个字符之间', trigger: ['blur', 'change'] }
],
buildingName: [
{ required: true, message: '请输入建筑名称', trigger: ['blur', 'change'] },
{ message: '名称长度在2-50个字符之间', trigger: ['blur', 'change'] }
],
// buildingFloorCount: [
// { required: true, message: '请输入地上层数', trigger: ['blur', 'change'] },
// {
// validator: (rule, value, callback) => {
// if (value === null || value === '') {
// callback(new Error('请输入地上层数'));
// } else if (isNaN(value)) {
// callback(new Error('请输入有效数字'));
// } else if (value < 0) {
// callback(new Error('层数不能为负数'));
// } else {
// callback();
// }
// },
// trigger: ['blur', 'change']
// }
// ],
// buildingBasementCount: [
// { 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 () => {
try {
submitting.value = true;
if (form.hasBasement === 2) {
rules.basementCount = [];
}
await addBuildingFormRef.value.validate();
// 准备提交数据
const submitData = {
...form,
hasBasement: form.hasBasement === 1
};
console.log('提交新增建筑数据:', submitData);
await new Promise(resolve => setTimeout(resolve, 1000));
ElMessage.success('建筑信息新增成功!');
// addBuildingFormRef.value.resetFields();
emit('closeEditDrawer');
emit('tableDataRefresh');
} catch (error) {
console.error('表单验证失败:', error);
ElMessage.error('请完善表单信息后重试');
} finally {
submitting.value = false;
}
};
// 取消操作
const handleCancel = () => {
ElMessageBox.confirm('确定要取消修改吗?已填写内容将不保存', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
center: true
}).then(() => {
// addBuildingFormRef.value.resetFields();
emit('closeEditDrawer');
}).catch(() => {});
};
onMounted(() => {
initFormData();
});
// 关闭弹窗
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;
border-radius: 8px;
// 标题栏样式
.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 {
height: 86%;
.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="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" @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">
<Addbuild @closeAddDrawer="closeAddDrawer" @tableDataRefresh="handleRefreshInfoFn"></Addbuild>
</el-drawer>
<!-- 修改建筑抽屉 -->
<el-drawer v-model="isBuildingEditDrawer" title="修改用户信息" :with-header="false" :before-close="beforeCloseAddBuildDrawer">
<showResetBuildingInfo :rowData="rowData" :isBuildingEditDrawer="isBuildingEditDrawer" @closeEditDrawer="closeBuildingEditDrawer" @tableDataRefresh="handleRefreshInfoFn"></showResetBuildingInfo>
</el-drawer>
</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 showResetBuildingInfo from './components/showResetBuildingInfo.vue';
import Addbuild from './components/addbuild.vue';
// 搜索栏配置
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:'火灾1', value:'烟雾1'},
{label:'烟雾1', value:'火灾1'}
] },
]);
// 布局配置
const layoutConfig = reactive({
containerMinHeight: '100vh',
cardSpacing: '1rem'
});
// 抽屉控制
const isAddDrawer = ref(false);
const isBuildingEditDrawer = ref(false);
const rowData = ref([]);
// 方法定义
const handleAddInfoFn = () => {
isAddDrawer.value = true;
}
const handleRefreshInfoFn = () => {
console.log("刷新数据");
}
const closeAddDrawer = () => {
isAddDrawer.value = false;
}
const beforeCloseAddBuildDrawer = () => {
// 关闭前的处理逻辑
}
const buildingSeeDetails = (data) => {
console.log("查看详情", data);
}
const buildingDdeleteData = (data) => {
ElMessageBox.confirm('是否删除该建筑?此操作将无法撤销', '删除', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
handleRefreshInfoFn()
ElMessage.success('已删除');
}).catch(() => {
ElMessage.info('取消删除');
});
}
const resetBuildingShowInfo = (data) => {
isBuildingEditDrawer.value = true;
rowData.value = data;
console.log("000000------------0000000000",rowData.value)
}
const closeBuildingEditDrawer = () => {
isBuildingEditDrawer.value = false;
}
// 表格数据
const tableShowData = ref([{
tableHeader: [
{label: '建筑编号', prop: 'buildingNo'},
{label: '建筑名称', prop: 'buildingName'},
{label: '建筑层数', prop: 'buildingFloorCount'},
{label: '火情次数', prop: 'buildingFireCount'},
{label: '更新时间', prop: 'buildingUpdataTime'},
{label: '操作', prop: 'buildingOperation'}
],
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 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
<template>
<div>
<el-table
:data="tableShowData"
style="width: 100%"
@select="selectTableDateFn"
@select-all="selectTableDateFn" >
<el-table-column type="selection" width="55" />
<el-table-column label="火情编号" width="120">
<template #default="scope">{{ scope.row.id}}</template>
</el-table-column>
<el-table-column prop="name" label="建筑名称" width="120" />
<el-table-column prop="floorcount" label="火情楼层" width="120" />
<el-table-column prop="reporter" label="上报人" width="120" />
<el-table-column prop="updateTime" label="上报时间" width="220" />
<el-table-column prop="fireLevel" label="火情等级" width="220" />
<el-table-column prop="discription" label="火情描述" />
<el-table-column
label="详情查看"
show-overflow-tooltip
>
<template #default="scope">
<el-button @click="handleClick(scope.row)">图片查看</el-button>
<el-button @click="handleVideoDialogFn(scope.row)">视频查看</el-button>
</template>
</el-table-column>
<el-table-column
label="操作"
show-overflow-tooltip
>
<template #default="scope">
<el-button @click="taskSchedulingFn(scope.row)">任务调度</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页区域(固定在右下角) -->
<div class="card-tabs-footer" >
<div class="pagination-block">
<span class="page-text">每页显示</span>
<el-pagination
v-model:current-page="currentPage2"
v-model:page-size="pageSize2"
:page-sizes="[10, 50, 100]"
size="small"
background
layout="sizes, prev, pager, next"
:total="tableShowData.length"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
<div v-if="showPreview">
<el-image-viewer
:url-list="srcList"
show-progress
ref="imageRef"
style="width: 100px; height: 100px"
fit="cover"
@close="showPreview = false"
/>
</div>
<el-dialog
v-model="isVideoDialogVisible"
width="800"
:before-close="handleClose"
>
<template #header>
<span>视频文件 <span style="margin-left: 1rem; color: chartreuse;">{{ videoSrcList.length }}</span></span>
</template>
<div class="video-container">
<div
v-for="(src, index) in videoSrcList"
:key="index"
class="video-wrapper"
>
<div class="video-title">视频 {{ index + 1 }}</div>
<video
:src="src"
controls
class="video-player"
></video>
</div>
</div>
</el-dialog>
<el-dialog
v-model="isTaskSchedulingDialogVisible"
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>
<!-- 底部按钮 -->
<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>
<script setup>
const emit = defineEmits(['archiveInfoDateBackcallFn'])
const props = defineProps({
tableShowData: {
type: Array,
default: () => []
},
})
const imageRef = ref(null)
const showPreview = ref(false)
const handleClick = (info) => {
showPreview.value = true
console.log("图片查看handleClick",info)
}
const srcList = ref([
'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',
])
// const srcList = ref([
// 'https://www.w3schools.com/html/mov_bbb.mp4',
// ])
const videoSrcList = ref([ 'https://www.w3schools.com/html/mov_bbb.mp4',
'https://www.w3schools.com/html/mov_bbb.mp4',
'https://www.w3schools.com/html/mov_bbb.mp4',
'https://www.w3schools.com/html/mov_bbb.mp4',]
)
const isVideoDialogVisible = ref(false)
const handleVideoDialogFn = (info) => {
isVideoDialogVisible.value = true
console.log("图片查看handleClick",info)
}
const isTaskSchedulingDialogVisible = ref(false)
const taskSchedulingFn = (info) => {
console.log("任务调度",info)
isTaskSchedulingDialogVisible.value = true
}
const selectTableDateFn = (selectedRows) => {
console.log('selectTableDateFn选择的数据信息',selectedRows)
emit('archiveInfoDateBackcallFn',selectedRows)
}
// 分页数据
const currentPage2 = ref(1)
const pageSize2 = ref(10)
// 分页事件处理
const handleSizeChange = (val) => {
console.log(`每页 ${val} 条`);
// getNewTableData()//
};
const handleCurrentChange = (val) => {
console.log(`当前页 ${val} 条`);
// getNewTableData()
};
// const getNewTableData = () => {
// staffGetPageInfoApi({
// "currentPageNum": currentPage2.value,
// "currentPageSize": pageSize2.value,
// }).then(res => {
// if(res.code === 200){
// let tempData = res.data.list
// // console.log(props.Operation,"--11111111111111111111111---*****---999555555555555999999----")
// curTableShowData.value[0].tableBody = []
// //获取未逻辑删除的数据
// tempData = tempData.filter(element => {
// return element.isDeleted === 0;
// });
// tempData.forEach(element => {
// element.Operation = [...props.Operation]
// });
// curTableShowData.value[0].tableBody = tempData
// curTableShowData.value[0].total = res.data.total
// curTableShowData.value[0].pageSize = res.data.pageSize
// console.log(curTableShowData.value, '展示数据');
// }
// }).catch(err => {
// message.error(err.message)
// })
// };
</script>
<style scoped>
/* 分页区域固定在右下角 */
.card-tabs-footer {
position: absolute;
bottom: 0;
right: 0;
padding: 15px;
width: auto; /* 自适应宽度 */
}
/* 分页内部 Flex 布局 */
.pagination-block {
display: flex; /* 核心:启用 Flex 布局 */
align-items: center; /* 垂直居中对齐 */
gap: 12px; /* 文本与分页间距 */
}
/* 文本样式 */
.page-text {
color: #606266;
font-size: 14px;
white-space: nowrap; /* 防止文本换行 */
}
/* 穿透修改 Element Plus 分页样式 */
:deep(.el-pagination) {
display: flex; /* 确保分页组件水平排列 */
align-items: center;
margin: 0; /* 清除默认外边距 */
}
/* 表格行样式(全局样式需放在 scoped 外或使用 ::v-deep) */
:deep(.el-table .warning-row) {
--el-table-tr-bg-color: var(--el-color-warning-light-9);
}
:deep(.el-table .success-row) {
--el-table-tr-bg-color: var(--el-color-success-light-9);
}
.video-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
max-height: 70vh;
overflow-y: auto;
}
.video-wrapper {
border: 1px solid #eee;
border-radius: 4px;
padding: 0.5rem;
background: #f9f9f9;
}
.video-title {
font-weight: bold;
margin-bottom: 0.5rem;
color: #666;
}
.video-player {
width: 100%;
border-radius: 4px;
aspect-ratio: 16/9;
background: #000;
}
.dialog-header {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.dialog-title {
font-size: 18px;
font-weight: 600;
color: #303133;
}
.status-indicator {
display: flex;
align-items: center;
color: #67c23a;
font-size: 14px;
}
.status-icon {
margin-right: 6px;
font-size: 16px;
}
.dialog-content {
padding: 0 20px;
height: 300px;
}
.notification-box {
display: flex;
align-items: center;
padding: 15px;
background-color: #f0f7ff;
border-radius: 4px;
margin-bottom: 20px;
}
.notification-icon {
color: #409eff;
font-size: 18px;
margin-right: 10px;
}
.notification-text {
color: #606266;
font-size: 15px;
}
.progress-section {
margin-bottom: 25px;
}
.progress-header {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
color: #606266;
font-size: 14px;
}
.progress-percent {
color: #409eff;
font-weight: 500;
}
.drone-info {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.info-tag {
padding: 8px 12px;
font-size: 13px;
}
.dialog-footer {
width: 100%;
flex: 1;
display: flex;
justify-content: flex-end;
gap: 12px;
}
.action-button {
padding: 10px 16px;
flex: 0.21;
}
.cancel-button {
padding: 10px 16px;
color: #f56c6c;
border-color: #f56c6c;
}
.cancel-button:hover {
background-color: #fef0f0;
color: #f56c6c;
}
</style>
\ No newline at end of file
<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" @cliick="archiveInfoFn" >归档</el-button>
</div>
</div>
<div class="card-table">
<tableshow :tableShowData="tableShowData" @archiveInfoDateBackcallFn="archiveInfoDateBackcallFn"></tableshow>
</div>
</div>
</div>
</main>
</div>
</template>
<script setup>
import { reactive, computed, ref, onMounted } from 'vue';
import searchtop from '../../commentcomponents/searchtop/index.vue';
// import tabledata from '../../commentcomponents/tabledata/index.vue';
import { ElMessage, ElMessageBox } from 'element-plus'
import { staffGetPageInfoApi } from "../../../api/staff.js";
import tableshow from './components/tableshow.vue';
const searchShowData = ref([
{ label: '火情描述', placeholder: "请输入", type: 'input', content: '' },
{ label: '上报人', placeholder: "请输入", type: 'input', content: '' },
{ 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 isAddDrawer = ref(false);
const isEditDrawer = ref(false);
const isUserInfoDialogVisible = ref(false);
const isResetPasswordDialogVisible = ref(false);
const rowData = ref([]);
const resetPasswordData = ref({});
const showuserinfodata = ref({});
const tableShowData = ref([
{id: 1, name: '背景', floorcount: 18, reporter: '张三', updateTime: '12345678901', discription: '打个人脸',fireLevel: '1'},
{id: 2, name: '背景', floorcount: 18, reporter: '张三', updateTime: '12345678901', discription: '说实在的',fireLevel: '1'},
{id: 3, name: '背景', floorcount: 18, reporter: '张三', updateTime: '12345678901', discription: '打个人脸',fireLevel: '1'},
{id: 4, name: '背景', floorcount: 18, reporter: '张三', updateTime: '12345678901', discription: '说实在的',fireLevel: '1'},
]);
const archiveInfoDate = ref(null)
const archiveInfoDateBackcallFn = (row) => {
archiveInfoDate.value = null
archiveInfoDate.value = row
console.log('archiveInfoFn需要归档的数据',archiveInfoDate.value)
};
const archiveInfoFn = () => {
console.log('archiveInfoFn归档的数据',archiveInfoDate.value)
};
const handleAddInfoFn = () => {
isAddDrawer.value = true;
}
const tableDataRefresh = () => {
initTableData();
}
const editData = (data) => {
isEditDrawer.value = true;
rowData.value = data;
}
const deleteData = (data) => {
ElMessageBox.confirm(
'删除操作不可逆,是否继续?',
'删除',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
draggable: true,
}
).then(() => {
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) {
ElMessage({ type: 'error', message: '删除失败(网络错误)' });
}
}).catch(() => {
ElMessage({ type: 'info', message: '取消删除' });
});
}
const resetData = (data) => {
isResetPasswordDialogVisible.value = true;
resetPasswordData.value = data;
}
const userData = (data) => {
isUserInfoDialogVisible.value = true;
showuserinfodata.value = data;
}
const initTableData = () => {
// staffGetPageInfoApi({
// "currentPageNum": 1,
// "currentPageSize": 10,
// }).then(res => {
// if(res.code === 200) {
// let tempData = res.data.list
// tableShowData.value[0].tableBody = []
// tempData = tempData.filter(element => 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;
// }
// }).catch(err => {
// ElMessage.error(err.message);
// });
}
onMounted(() => {
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;
font-size: 1.2rem;
color: #000000;
height: 8%;
align-items: center;
width: 100%;
padding: 10px;
}
.title {
width: 90%;
}
.card-actions {
display: flex;
flex: 1;
justify-content: space-between;
align-items: center;
/* height: %; */
padding: 1.5rem;
}
.operation-button {
flex: 1;
display: flex;
justify-self: end;
}
.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
<template>
<div>
<el-table
:data="tableShowData"
style="width: 100%"
>
<el-table-column prop="id" label="火情编号" width="120" />
<el-table-column prop="name" label="建筑名称" width="120" />
<el-table-column prop="floorcount" label="火情楼层" width="120" />
<el-table-column prop="reporter" label="记录次数" width="120" />
<el-table-column prop="updateTime" label="归档时间" width="220" />
<el-table-column prop="discription" label="火情描述" show-overflow-tooltip />
<el-table-column label="详情查看">
<template #default="scope">
<el-button @click="detailViewsFn(scope.row)">详情查看</el-button>
</template>
</el-table-column>
<el-table-column
label="操作"
>
<template #default="scope">
<el-button @click="fireSimulationFn(scope.row)">火情模拟</el-button>
<el-button @click="exportReportFn (scope.row)">导出报告</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页区域(固定在右下角) -->
<div class="card-tabs-footer" >
<div class="pagination-block">
<span class="page-text">每页显示</span>
<el-pagination
v-model:current-page="currentPage2"
v-model:page-size="pageSize2"
:page-sizes="[10, 50, 100]"
size="small"
background
layout="sizes, prev, pager, next"
:total="tableShowData.length"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</div>
</template>
<script setup>
import { useRoute,useRouter } from 'vue-router'
const emit = defineEmits(['archiveInfoDateBackcallFn'])
const props = defineProps({
tableShowData: {
type: Array,
default: () => []
},
})
const route = useRoute()
const router = useRouter()
const imageRef = ref(null)
const showPreview = ref(false)
const fireSimulationFn = (info) => {
console.log("火情模拟",info)
}
const exportReportFn = (info) => {
console.log("导出报告",info)
}
const detailViewsFn = (info) => {
console.log("详情查看",info,route)
router.push({
path: '/historyfire/floordetaildate',
})
}
// 分页数据
const currentPage2 = ref(1)
const pageSize2 = ref(10)
// 分页事件处理
const handleSizeChange = (val) => {
console.log(`每页 ${val} 条`);
// getNewTableData()//
};
const handleCurrentChange = (val) => {
console.log(`当前页 ${val} 条`);
// getNewTableData()
};
// const getNewTableData = () => {
// staffGetPageInfoApi({
// "currentPageNum": currentPage2.value,
// "currentPageSize": pageSize2.value,
// }).then(res => {
// if(res.code === 200){
// let tempData = res.data.list
// // console.log(props.Operation,"--11111111111111111111111---*****---999555555555555999999----")
// curTableShowData.value[0].tableBody = []
// //获取未逻辑删除的数据
// tempData = tempData.filter(element => {
// return element.isDeleted === 0;
// });
// tempData.forEach(element => {
// element.Operation = [...props.Operation]
// });
// curTableShowData.value[0].tableBody = tempData
// curTableShowData.value[0].total = res.data.total
// curTableShowData.value[0].pageSize = res.data.pageSize
// console.log(curTableShowData.value, '展示数据');
// }
// }).catch(err => {
// message.error(err.message)
// })
// };
</script>
<style scoped>
/* 分页区域固定在右下角 */
.card-tabs-footer {
position: absolute;
bottom: 0;
right: 0;
padding: 15px;
width: auto; /* 自适应宽度 */
}
/* 分页内部 Flex 布局 */
.pagination-block {
display: flex; /* 核心:启用 Flex 布局 */
align-items: center; /* 垂直居中对齐 */
gap: 12px; /* 文本与分页间距 */
}
/* 文本样式 */
.page-text {
color: #606266;
font-size: 14px;
white-space: nowrap; /* 防止文本换行 */
}
/* 穿透修改 Element Plus 分页样式 */
:deep(.el-pagination) {
display: flex; /* 确保分页组件水平排列 */
align-items: center;
margin: 0; /* 清除默认外边距 */
}
/* 表格行样式(全局样式需放在 scoped 外或使用 ::v-deep) */
:deep(.el-table .warning-row) {
--el-table-tr-bg-color: var(--el-color-warning-light-9);
}
:deep(.el-table .success-row) {
--el-table-tr-bg-color: var(--el-color-success-light-9);
}
.video-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
max-height: 70vh;
overflow-y: auto;
}
.video-wrapper {
border: 1px solid #eee;
border-radius: 4px;
padding: 0.5rem;
background: #f9f9f9;
}
.video-title {
font-weight: bold;
margin-bottom: 0.5rem;
color: #666;
}
.video-player {
width: 100%;
border-radius: 4px;
aspect-ratio: 16/9;
background: #000;
}
.dialog-header {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.dialog-title {
font-size: 18px;
font-weight: 600;
color: #303133;
}
.status-indicator {
display: flex;
align-items: center;
color: #67c23a;
font-size: 14px;
}
.status-icon {
margin-right: 6px;
font-size: 16px;
}
.dialog-content {
padding: 0 20px;
height: 300px;
}
.notification-box {
display: flex;
align-items: center;
padding: 15px;
background-color: #f0f7ff;
border-radius: 4px;
margin-bottom: 20px;
}
.notification-icon {
color: #409eff;
font-size: 18px;
margin-right: 10px;
}
.notification-text {
color: #606266;
font-size: 15px;
}
.progress-section {
margin-bottom: 25px;
}
.progress-header {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
color: #606266;
font-size: 14px;
}
.progress-percent {
color: #409eff;
font-weight: 500;
}
.drone-info {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.info-tag {
padding: 8px 12px;
font-size: 13px;
}
.dialog-footer {
width: 100%;
flex: 1;
display: flex;
justify-content: flex-end;
gap: 12px;
}
.action-button {
padding: 10px 16px;
flex: 0.21;
}
.cancel-button {
padding: 10px 16px;
color: #f56c6c;
border-color: #f56c6c;
}
.cancel-button:hover {
background-color: #fef0f0;
color: #f56c6c;
}
</style>
\ No newline at end of file
<template>
<div>
<el-table
:data="tableShowData"
style="width: 100%"
@select="selectTableDateFn"
@select-all="selectTableDateFn" >
<el-table-column type="selection" width="55" />
<el-table-column prop="id" label="记录序号" width="80" />
<el-table-column prop="fireNo" label="火情编号" width="80" />
<el-table-column prop="name" label="建筑名称" width="120" />
<el-table-column prop="floorcount" label="火情楼层" width="100" />
<el-table-column prop="reporter" 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="fireLevel" label="火情等级" width="120" />
<el-table-column prop="firePoint" label="着火点" width="220" show-overflow-tooltip />
<el-table-column
label="详情查看"
show-overflow-tooltip
>
<template #default="scope">
<el-button @click="handleClick(scope.row)">图片查看</el-button>
<el-button @click="handleVideoDialogFn(scope.row)">视频查看</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页区域(固定在右下角) -->
<div class="card-tabs-footer" >
<div class="pagination-block">
<span class="page-text">每页显示</span>
<el-pagination
v-model:current-page="currentPage2"
v-model:page-size="pageSize2"
:page-sizes="[10, 50, 100]"
size="small"
background
layout="sizes, prev, pager, next"
:total="tableShowData.length"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
<div v-if="showPreview">
<el-image-viewer
:url-list="srcList"
show-progress
ref="imageRef"
style="width: 100px; height: 100px"
fit="cover"
@close="showPreview = false"
/>
</div>
<el-dialog
v-model="isVideoDialogVisible"
width="800"
:before-close="handleClose"
>
<template #header>
<span>视频文件 <span style="margin-left: 1rem; color: chartreuse;">{{ videoSrcList.length }}</span></span>
</template>
<div class="video-container">
<div
v-for="(src, index) in videoSrcList"
:key="index"
class="video-wrapper"
>
<div class="video-title">视频 {{ index + 1 }}</div>
<video
:src="src"
controls
class="video-player"
></video>
</div>
</div>
</el-dialog>
</div>
</template>
<script setup>
import { onMounted } from 'vue'
import { useRoute,useRouter } from 'vue-router'
const emit = defineEmits(['fireInfoDataImportBackcallFn'])
// const props = defineProps(['tableShowData'])
const router = useRouter()
const route = useRoute()
const tableShowData =[{
id: 1,
fireNo:12,
updataTime:'2016-05-04',
name: 'Aleyna Kutzner',
floorcount: '1',
reporter:"张三",
updateTime:"2022-05-04",
discription: 'Lorem ipsum dolor sit ametisl nisl nisl nisl nisl nisl nisl nisl nis',
fireLevel: '1级',
firePoint: '厨房',
},
{
id: 1,
fireNo:12,
updataTime:'2016-05-04',
name: 'Aleyna Kutzner',
floorcount: '1',
reporter:"张三",
updateTime:"2022-05-04",
discription: 'Lorem ipsum dolor sit ametisl nisl nisl nisl nisl nisl nisl nisl nis',
fireLevel: '1级',
firePoint: '厨房',
}]
const imageRef = ref(null)
const showPreview = ref(false)
const handleClick = (info) => {
showPreview.value = true
console.log("图片查看handleClick",info)
}
const srcList = ref([
'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',
])
const videoSrcList = ref([ 'https://www.w3schools.com/html/mov_bbb.mp4',
'https://www.w3schools.com/html/mov_bbb.mp4',
'https://www.w3schools.com/html/mov_bbb.mp4',
'https://www.w3schools.com/html/mov_bbb.mp4',]
)
const isVideoDialogVisible = ref(false)
const handleVideoDialogFn = (info) => {
isVideoDialogVisible.value = true
console.log("图片查看handleClick",info)
}
const selectTableDateFn = (selectedRows) => {
console.log('selectTableDateFn选择的数据信息',selectedRows)
emit('fireInfoDataImportBackcallFn',selectedRows)
}
// 分页数据
const currentPage2 = ref(1)
const pageSize2 = ref(10)
// 分页事件处理
const handleSizeChange = (val) => {
console.log(`每页 ${val} 条`);
// getNewTableData()//
};
const handleCurrentChange = (val) => {
console.log(`当前页 ${val} 条`);
// getNewTableData()
};
// onMounted(() => {
// console.log('table组件已挂载');
// })
// const getNewTableData = () => {
// staffGetPageInfoApi({
// "currentPageNum": currentPage2.value,
// "currentPageSize": pageSize2.value,
// }).then(res => {
// if(res.code === 200){
// let tempData = res.data.list
// // console.log(props.Operation,"--11111111111111111111111---*****---999555555555555999999----")
// curTableShowData.value[0].tableBody = []
// //获取未逻辑删除的数据
// tempData = tempData.filter(element => {
// return element.isDeleted === 0;
// });
// tempData.forEach(element => {
// element.Operation = [...props.Operation]
// });
// curTableShowData.value[0].tableBody = tempData
// curTableShowData.value[0].total = res.data.total
// curTableShowData.value[0].pageSize = res.data.pageSize
// console.log(curTableShowData.value, '展示数据');
// }
// }).catch(err => {
// message.error(err.message)
// })
// };
</script>
<style scoped>
/* 分页区域固定在右下角 */
.card-tabs-footer {
position: absolute;
bottom: 0;
right: 0;
padding: 15px;
width: auto; /* 自适应宽度 */
}
/* 分页内部 Flex 布局 */
.pagination-block {
display: flex; /* 核心:启用 Flex 布局 */
align-items: center; /* 垂直居中对齐 */
gap: 12px; /* 文本与分页间距 */
}
/* 文本样式 */
.page-text {
color: #606266;
font-size: 14px;
white-space: nowrap; /* 防止文本换行 */
}
/* 穿透修改 Element Plus 分页样式 */
:deep(.el-pagination) {
display: flex; /* 确保分页组件水平排列 */
align-items: center;
margin: 0; /* 清除默认外边距 */
}
/* 表格行样式(全局样式需放在 scoped 外或使用 ::v-deep) */
:deep(.el-table .warning-row) {
--el-table-tr-bg-color: var(--el-color-warning-light-9);
}
:deep(.el-table .success-row) {
--el-table-tr-bg-color: var(--el-color-success-light-9);
}
.video-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
max-height: 70vh;
overflow-y: auto;
}
.video-wrapper {
border: 1px solid #eee;
border-radius: 4px;
padding: 0.5rem;
background: #f9f9f9;
}
.video-title {
font-weight: bold;
margin-bottom: 0.5rem;
color: #666;
}
.video-player {
width: 100%;
border-radius: 4px;
aspect-ratio: 16/9;
background: #000;
}
.dialog-header {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.dialog-title {
font-size: 18px;
font-weight: 600;
color: #303133;
}
.status-indicator {
display: flex;
align-items: center;
color: #67c23a;
font-size: 14px;
}
.status-icon {
margin-right: 6px;
font-size: 16px;
}
.dialog-content {
padding: 0 20px;
height: 300px;
}
.notification-box {
display: flex;
align-items: center;
padding: 15px;
background-color: #f0f7ff;
border-radius: 4px;
margin-bottom: 20px;
}
.notification-icon {
color: #409eff;
font-size: 18px;
margin-right: 10px;
}
.notification-text {
color: #606266;
font-size: 15px;
}
.progress-section {
margin-bottom: 25px;
}
.progress-header {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
color: #606266;
font-size: 14px;
}
.progress-percent {
color: #409eff;
font-weight: 500;
}
.drone-info {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.info-tag {
padding: 8px 12px;
font-size: 13px;
}
.dialog-footer {
width: 100%;
flex: 1;
display: flex;
justify-content: flex-end;
gap: 12px;
}
.action-button {
padding: 10px 16px;
flex: 0.21;
}
.cancel-button {
padding: 10px 16px;
color: #f56c6c;
border-color: #f56c6c;
}
.cancel-button:hover {
background-color: #fef0f0;
color: #f56c6c;
}
</style>
\ No newline at end of file
<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" @cliick="archiveInfoFn" >归档</el-button>
</div> -->
</div>
<div class="card-table">
<tableshow :tableShowData="tableShowData" @archiveInfoDateBackcallFn="archiveInfoDateBackcallFn"></tableshow>
</div>
</div>
</div>
</main>
</div>
</template>
<script setup>
import { reactive, computed, ref, onMounted } from 'vue';
import searchtop from '../../commentcomponents/searchtop/index.vue';
// import tabledata from '../../commentcomponents/tabledata/index.vue';
import { ElMessage, ElMessageBox } from 'element-plus'
import { staffGetPageInfoApi } from "../../../api/staff.js";
import tableshow from './components/tableshow.vue';
const searchShowData = ref([
{ label: '火情描述', placeholder: "请输入", type: 'input', content: '' },
{ label: '上报人', placeholder: "请输入", type: 'input', content: '' },
{ 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 isAddDrawer = ref(false);
const isEditDrawer = ref(false);
const isUserInfoDialogVisible = ref(false);
const isResetPasswordDialogVisible = ref(false);
const rowData = ref([]);
const resetPasswordData = ref({});
const showuserinfodata = ref({});
const tableShowData = ref([
{id: 1, name: '背景', floorcount: 18, reporter: '张三', updateTime: '12345678901', discription: '打个人脸',fireLevel: '1'},
{id: 2, name: '背景', floorcount: 18, reporter: '张三', updateTime: '12345678901', discription: '说实在的',fireLevel: '1'},
{id: 3, name: '背景', floorcount: 18, reporter: '张三', updateTime: '12345678901', discription: '打个人脸',fireLevel: '1'},
{id: 4, name: '背景', floorcount: 18, reporter: '张三', updateTime: '12345678901', discription: '说实在的',fireLevel: '1'},
]);
// const archiveInfoDate = ref(null)
// const archiveInfoDateBackcallFn = (row) => {
// archiveInfoDate.value = null
// archiveInfoDate.value = row
// console.log('archiveInfoFn需要归档的数据',archiveInfoDate.value)
// };
// const archiveInfoFn = () => {
// console.log('archiveInfoFn归档的数据',archiveInfoDate.value)
// };
const handleAddInfoFn = () => {
isAddDrawer.value = true;
}
const tableDataRefresh = () => {
initTableData();
}
const editData = (data) => {
isEditDrawer.value = true;
rowData.value = data;
}
const deleteData = (data) => {
ElMessageBox.confirm(
'删除操作不可逆,是否继续?',
'删除',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
draggable: true,
}
).then(() => {
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) {
ElMessage({ type: 'error', message: '删除失败(网络错误)' });
}
}).catch(() => {
ElMessage({ type: 'info', message: '取消删除' });
});
}
const resetData = (data) => {
isResetPasswordDialogVisible.value = true;
resetPasswordData.value = data;
}
const userData = (data) => {
isUserInfoDialogVisible.value = true;
showuserinfodata.value = data;
}
const initTableData = () => {
// staffGetPageInfoApi({
// "currentPageNum": 1,
// "currentPageSize": 10,
// }).then(res => {
// if(res.code === 200) {
// let tempData = res.data.list
// tableShowData.value[0].tableBody = []
// tempData = tempData.filter(element => 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;
// }
// }).catch(err => {
// ElMessage.error(err.message);
// });
}
onMounted(() => {
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;
font-size: 1.2rem;
color: #000000;
height: 8%;
align-items: center;
width: 100%;
padding: 10px;
}
.title {
width: 90%;
}
.card-actions {
display: flex;
flex: 1;
justify-content: space-between;
align-items: center;
/* height: %; */
padding: 1.5rem;
}
.operation-button {
flex: 1;
display: flex;
justify-self: end;
}
.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
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论