weich 3 years ago
parent
commit
0c624ae070

+ 2 - 2
src/config/componentsMap.js

@@ -44,8 +44,8 @@ export default {
   wrench: () => import('@/views/device/wrench'),
   flange: () => import('@/views/device/flange'),
   maintenance: () => import('@/views/device/maintenance'),
-  // vehicleBrands: () => import('@/views/yunying/vehicle/brand'),
-  // vehicleColors: () => import('@/views/yunying/vehicle/color'),
+  wind: () => import('@/views/wind/wind'),
+  fan: () => import('@/views/wind/fan'),
   // stolenVehicles: () => import('@/views/yunying/vehicle/stolen'),
   // 短信平台
   sms: () => import('@/views/sms'),

+ 13 - 13
src/router/index.js

@@ -39,19 +39,19 @@ export const constantRoutes = [
     ]
   },
   
-  {
-    path: '/Wind',
-    component: Layout,
-    redirect: '/Wind',
-    children: [
-      {
-        path: 'Wind',
-        component: () => import('@/views/device/maintenance'),
-        name: 'Wind',
-        meta: { title: '风场管理', icon: 'el-icon-wind-power', affix: true }
-      }
-    ]
-  },
+  // {
+  //   path: '/Wind',
+  //   component: Layout,
+  //   redirect: '/Wind',
+  //   children: [
+  //     {
+  //       path: 'Wind',
+  //       component: () => import('@/views/device/maintenance'),
+  //       name: 'Wind',
+  //       meta: { title: '风场管理', icon: 'el-icon-wind-power', affix: true }
+  //     }
+  //   ]
+  // },
   {
     path: '/Maintenance',
     component: Layout,

+ 0 - 0
src/views/wind/fan/index.vue


+ 332 - 0
src/views/wind/wind/floor/index.vue

@@ -0,0 +1,332 @@
+<template>
+    <div class="app-container">
+        <div class="table-header">
+            <div class="tatble-toolbar">
+                <el-row style="float: right;margin: 5px 0;">
+                    <el-button type="primary" icon="el-icon-plus" @click="showFormDialog = true">添加楼层</el-button>
+                </el-row>
+            </div>
+        </div>
+        <div class="table-body">
+            <el-table
+                :data="tableData"
+                border
+                fit
+                style="width: 100%">
+                <el-table-column
+                    fixed
+                    prop="name"
+                    label="楼层名称"
+                    align="center"
+                    width="100"
+                />
+                <el-table-column
+                    prop="building_name"
+                    label="所属建筑"
+                    align="center"
+                />
+                <el-table-column
+                    prop="school_name"
+                    label="所属学校"
+                    align="center"
+                />
+                <el-table-column
+                    prop="drawing_path"
+                    label="楼层图纸"
+                    align="center"
+                >
+                    <template slot-scope="scope">
+                        <el-image
+                            style="width: 50px;"
+                            :src="scope.row.drawing_path"
+                            :preview-src-list="[scope.row.drawing_path]">
+                            <div slot="error" class="image-slot">
+                                <i class="el-icon-picture-outline"></i>
+                            </div>
+                        </el-image>
+                    </template>
+                </el-table-column>
+                <el-table-column
+                    prop="sort"
+                    label="排序"
+                    align="center"
+                    width="100"
+                />
+                <el-table-column
+                    label="操作"
+                    align="center"
+                    width="100">
+                    <template slot-scope="scope">
+                        <el-tooltip content="修改" placement="top" :enterable="false">
+                            <el-button type="text" size="small" icon="el-icon-edit" @click="updateInfo(scope.row)"></el-button>
+                        </el-tooltip>
+                        <el-tooltip content="删除" placement="top" :enterable="false">
+                            <el-button type="text" size="small" icon="el-icon-delete" @click="deleteFloor(scope.row.id)"></el-button>
+                        </el-tooltip>
+                    </template>
+                </el-table-column>
+            </el-table>
+        </div>
+        <div class="table-footer">
+            <div class="table-pagination">
+                <el-pagination
+                    @size-change="handleSizeChange"
+                    @current-change="handleCurrentChange"
+                    :current-page="paginate.current"
+                    :page-sizes="paginate.sizes"
+                    :page-size="paginate.limit"
+                    layout="total, sizes, prev, pager, next, jumper"
+                    :total="paginate.total">
+                </el-pagination>
+            </div>
+        </div>
+
+        <!-- form 表单 -->
+        <el-dialog title="楼层信息" :visible.sync="showFormDialog" append-to-body destroy-on-close @closed="onFormDialogClosed" width="500px">
+            <el-form ref="floorForm" :rules="rules" :model="form" label-width="80px">
+                <!-- <el-form-item label="所属学校" required prop="school_id">
+                    <el-select v-model="form.school_id" filterable placeholder="请选择所属学校">
+                        <el-option v-for="school in schoolData" :key="school.id" :label="school.name" :value="school.id"></el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="所属建筑" required prop="building_id">
+                    <el-select v-model="form.school_id" filterable placeholder="请选择所属建筑">
+                        <el-option v-for="school in schoolData" :key="school.id" :label="school.name" :value="school.id"></el-option>
+                    </el-select>
+                </el-form-item> -->
+                <el-form-item label="楼层名称" required prop="name">
+                    <el-input v-model="form.name" ></el-input>
+                </el-form-item>
+                <el-form-item label="排序编号">
+                    <el-input-number v-model="form.sort" label="楼层排序编号"></el-input-number>
+                </el-form-item>
+                <el-form-item label="楼层图纸">
+                    <!-- <el-upload
+                        class="avatar-uploader"
+                        name="image"
+                        :action="uploadUrl"
+                        :headers="uploadHeaders"
+                        :show-file-list="false"
+                        :on-success="onUploadSuccess"
+                        :before-upload="beforeUpload">
+                        <img v-if="imageUrl" :src="imageUrl" class="avatar">
+                        <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+                    </el-upload> -->
+
+                    <el-web-to-oss
+                        @input="getUploadImages"
+                        type="image"
+                        :fileList="imageList"
+                        :maxSize="2"
+                    />
+                </el-form-item>
+            </el-form>
+            <div slot="footer" class="dialog-footer">
+                <el-button @click="closeDialog">取 消</el-button>
+                <el-button type="primary" @click="submitForm">确 定</el-button>
+            </div>
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+
+import rlListOperate from '@/layout/rl-list-operate/rlListOperate'
+import ElWebToOss from "@/components/Upload/ElWebToOss";
+
+  export default {
+    mixins: [ rlListOperate ],
+    components: {
+        ElWebToOss,
+    },
+    props: {
+        buildingInfo: {
+            type: Object,
+            default: function() {
+                return {}
+            }
+        }
+    },
+    watch: {
+        buildingInfo: function(val) {
+            this.url = 'kqFloor/getBuildingFloorList/' + val.id,
+            this.handleRefresh()
+        },
+    },
+    data() {
+      return {
+        url: 'kqFloor/getBuildingFloorList/' + this.buildingInfo.id,
+        uploadUrl: process.env.VUE_APP_BASE_API + '/upload/image',
+        uploadHeaders: {
+            Authorization: 'Bearer ' + this.$store.state.user.token,
+        },
+        tableData: [],
+        showFormDialog: false, // 显示form表单
+        form: {
+            sort: 0, // 默认排序
+        }, // 
+        rules: {
+            name: [
+                { required: true, message: '请输入楼层名称', trigger: 'blur' },
+                { required: true, message: '请输入楼层名称', trigger: 'change' },
+                { min: 1, max: 50, message: '长度在 1 到 50 个字符', trigger: 'blur' },
+            ],
+        }, // 验证规则
+        // imageUrl: '',
+        imageList: [], // 图片数据
+      }
+    },
+    methods: {
+        /**
+         * 图片文件
+         */
+        getUploadImages(files) {
+            if (files.length) {
+                this.form.drawing_path = files[0].url;
+            } else {
+                this.form.drawing_path = '';
+            }
+        },
+        afterGetList(){
+            this.tableData = this.data
+        },
+        // 提交表单
+        submitForm() {
+            if (!this.form.name) {
+                this.$message.error('请输入楼层名称');
+                return;
+            }
+            if (!this.form.id) {
+                this.form.school_id = this.buildingInfo.school_id;
+                this.form.building_id = this.buildingInfo.id;
+                this.form.longitude = this.buildingInfo.longitude;
+                this.form.latitude = this.buildingInfo.latitude;
+
+                this.$http.post('kqFloor', this.form)
+                            .then(resp => {
+                                if (resp.code === 10000) {
+                                    this.$message.success('添加成功')
+                                    this.showFormDialog = false
+                                }
+                            })
+            } else { // 修改操作,删除不必要字段
+                this.$delete(this.form, 'latitude')
+                this.$delete(this.form, 'longitude')
+                this.$delete(this.form, 'school_id')
+                this.$delete(this.form, 'creator_id')
+                this.$delete(this.form, 'created_at')
+                this.$delete(this.form, 'updated_at')
+                this.$delete(this.form, 'deleted_at')
+
+                this.$http.put('kqFloor/'+ this.form.id, this.form)
+                            .then(resp => {
+                                if (resp.code === 10000) {
+                                    this.$message.success('修改成功')
+                                    this.showFormDialog = false
+                                }
+                            })
+            }
+
+        },
+        onFormDialogClosed() {
+            // 关闭弹窗时重置form
+            this.form = {
+                sort: 0
+            }
+            // 去除照片
+            // this.imageUrl = ''
+            // 刷新数据
+            this.handleRefresh()
+        },
+        // 修改楼层
+        updateInfo(info) {
+            this.form = info
+            this.showFormDialog = true
+            this.imageUrl = [{
+                name: info.drawing_path.slice(info.drawing_path.lastIndexOf('/') + 1),
+                url: info.drawing_path,
+            }]
+        },
+        // 删除楼层
+        deleteFloor(id) {
+            this.$confirm('确定要删除吗?', '提示', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning'
+            }).then(() => {
+                    this.$http.delete('kqFloor/' + id)
+                            .then(resp => {
+                                if (resp.code === 10000) {
+                                    this.$message({
+                                        type: 'success',
+                                        message: '删除成功!'
+                                    });
+                                    this.handleRefresh()
+                                }
+                            }).catch(() => {})
+
+            }).catch(() => {});
+        },
+        // 上传成功回调
+        onUploadSuccess(res, file) {
+            if (res.code !== 10000) {
+                this.$message.error('上传失败,只支持 jpg 和 png 格式');
+                return false;
+            }
+            this.form.drawing_path = res.data;
+            this.imageUrl = URL.createObjectURL(file.raw);
+        },
+        // 上传前
+        beforeUpload(file) {
+            const isTrueFormat = (file.type === 'image/jpeg' || file.type === 'image/png');
+            const isLt2M = file.size / 1024 / 1024 < 2;
+
+            if (!isTrueFormat) {
+            this.$message.error('上传头像图片只能是 JPG 格式!');
+            }
+            if (!isLt2M) {
+            this.$message.error('上传头像图片大小不能超过 2MB!');
+            }
+            return isTrueFormat && isLt2M;
+        },
+        closeDialog() {
+            this.showFormDialog = false
+        },
+    },
+  }
+</script>
+<style lang="scss" scoped>
+    .el-select { width: 100%; }
+
+    .table-footer {
+        margin-top: 10px;
+
+        .table-pagination {
+            float: right;
+        }
+    }
+
+    ::v-deep .avatar-uploader .el-upload {
+        border: 1px dashed #d9d9d9;
+        border-radius: 6px;
+        cursor: pointer;
+        position: relative;
+        overflow: hidden;
+    }
+    ::v-deep .avatar-uploader .el-upload:hover {
+        border-color: #409EFF;
+    }
+    ::v-deep .avatar-uploader-icon {
+        font-size: 28px;
+        color: #8c939d;
+        width: 178px;
+        height: 178px;
+        line-height: 178px;
+        text-align: center;
+    }
+    ::v-deep .avatar {
+        width: 178px;
+        height: 178px;
+        display: block;
+    }
+</style>

+ 644 - 0
src/views/wind/wind/floor/map.vue

@@ -0,0 +1,644 @@
+<template>
+    <div class="app-container">
+        <!-- 平面图 -->
+        <div class="floorMap" :id="mapId"></div>
+        <!-- 右侧列表 -->
+        <div id="floorMapPanel">
+
+            <!-- 左侧:楼层 -->
+            <div id="floorList">
+                <el-tooltip content="展开/收起" placement="left" effect="light" enterable>
+                    <el-button type="text" id="toggleStationList" :icon="'el-icon-d-arrow-' + toggleIcon" style="font-size: 20px;"></el-button>
+                </el-tooltip>
+
+                <p>楼层</p>
+                
+                <el-button type="text" id="scrollUp" icon="el-icon-caret-top"></el-button>
+                <ul id="floorUl">
+                    <li v-for="(floor,index) in floorList" :key="index" :class="currentFloorInfo.id == floor.id ? 'actived' : ''" @click="changeFloor(floor, $event)">{{ floor.name }}</li>
+                </ul>
+                <!-- 向下翻页 -->
+                <el-button type="text" id="scrollDown" icon="el-icon-caret-bottom"></el-button>
+                <!-- 添加插座 -->
+                <el-tooltip content="点击此按钮后,再点击平面图上要安装的位置添加插座" effect="light" placement="left" enterable>
+                    <el-button type="text" id="addStationBtn" icon="el-icon-add-location" @click="addStation"></el-button>
+                </el-tooltip>
+            </div>
+            
+            <!-- 右侧:插座列表 -->
+            <div id="stationList">
+                <!-- 标题 -->
+                <div id="stationTitle">插座列表</div>
+                <div id="stationBody">
+                    <el-table
+                        @row-click="rowClick"
+                        :data="stationList"
+                        style="width: 100%"
+                        height="450">
+                        <el-table-column
+                            type="index"
+                            label="序号"
+                            align="center"
+                            width="50"
+                        />
+                        <el-table-column
+                            prop="room_no"
+                            label="房间号"
+                            align="center"
+                            width="66">
+                        </el-table-column>
+                        <el-table-column
+                            prop="station_mac"
+                            label="设备MAC"
+                            align="center"
+                            width="133">
+                        </el-table-column>
+                        <el-table-column
+                            prop="online_time"
+                            label="在线时间"
+                            align="center"
+                            sortable
+                            width="133">
+                        </el-table-column>
+                        <el-table-column
+                            fixed="right"
+                            label="操作"
+                            align="center"
+                            width="66">
+                            <template slot-scope="scope">
+                                <el-tooltip content="修改" placement="top" effect="light" enterable>
+                                    <el-button type="text" id="scrollUp" icon="el-icon-edit" @click="editStation(scope.row)"></el-button>
+                                </el-tooltip>
+                                <el-tooltip content="删除" placement="top" effect="light" enterable>
+                                    <el-button type="text" id="scrollUp" icon="el-icon-delete" @click="deleteStation(scope.row.id)"></el-button>
+                                </el-tooltip>
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                </div>
+                <div id="stationFooter">
+                    <!-- <el-pagination
+                        small
+                        layout="prev, pager, next"
+                        :total="50">
+                    </el-pagination> -->
+                </div>
+            </div>
+
+        </div>
+        <el-dialog title="插座信息" :visible.sync="showForm" append-to-body>
+            <el-form :model="formData" status-icon :rules="rules" ref="floorForm" label-width="100px">
+                <el-form-item label="房间号" prop="room_no">
+                    <el-input v-model="formData.room_no" placeholder="请输入设备安装房间号"></el-input>
+                </el-form-item>
+                <el-form-item label="设备MAC" prop="station_mac" required>
+                    <el-input type="text" v-model="formData.station_mac" placeholder="请输入设备MAC编号" ></el-input>
+                </el-form-item>
+                <el-form-item label="设备名称" prop="station_name" required>
+                    <el-input type="text" v-model="formData.station_name" placeholder="请输入设备名称" ></el-input>
+                </el-form-item>
+
+                <el-form-item label="横向坐标" prop="coordinate_x" required>
+                    <el-input v-model="formData.coordinate_x" placeholder="请输入设备在房间的横向坐标位置(取值范围:0~500)"></el-input>
+                </el-form-item>
+                <el-form-item label="纵向坐标" prop="coordinate_y" required>
+                    <el-input v-model="formData.coordinate_y" placeholder="请输入设备在房间的纵向坐标位置(取值范围:-374~0)"></el-input>
+                </el-form-item>
+
+                <el-form-item>
+                    <el-button type="primary" @click="submitForm">提交</el-button>
+                    <el-button @click="cancelSubmit">取消</el-button>
+                </el-form-item>
+            </el-form>
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+import L from "leaflet/dist/leaflet-src.js";
+import "leaflet/dist/leaflet.css";
+import { addClass, removeClass } from "@/utils/index.js"
+
+L.Icon.Default.prototype.options.imagePath = '/icons/';
+
+export default {
+    props: {
+        buildingId: {
+            type: [Number,String],
+            required: true
+        },
+        studentId: {
+            type: [Number,String],
+            // default: 83
+        }
+    },
+    watch: {
+        buildingId: function(val) {
+            this.clearData()
+            
+            this.bid = val
+            this.getFloorList()
+        },
+        studentId: function(val) {
+            this.currentStudentId = val
+        }
+    },
+    data() {
+        return {
+            mapId: 'floorMap' + Math.random(), // 防止id重复
+            bid: this.buildingId, // 建筑id
+            currentStudentId: this.studentId, // 当前学生id
+            studentMarker: null,
+            floorList: [], // 楼层数据
+            stationList: [], // 基站数据
+            currentFloorInfo: {}, // 当前楼层信息
+            currentStudentInfo: {}, // 当前学生信息
+            lmap: null, // leaflet map object
+            markersLayer: null, // 覆盖物群组
+            imageObj: null, // 图片覆盖物对象
+            defaultImageUrl: '/icons/picture-outline.png',
+            studentImageUrl: '/icons/student_alarm.png',
+            toggleIcon: 'left',
+            showForm: false, // 显示添加/修改表单
+            formData: {}, // 表单数据
+            rules: {
+                station_mac: [
+                    { required: true, message: '请输入设备MAC编号', trigger: 'blur' },
+                    { required: true, message: '请输入设备MAC编号', trigger: 'change' },
+                    // { min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
+                ],
+                station_name: [
+                    { required: true, message: '请输入设备名称', trigger: 'blur' },
+                    { required: true, message: '请输入设备名称', trigger: 'change' },
+                    { min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
+                ],
+                coordinate_x: [
+                    { required: true, message: '请输入设备在房间的横向坐标位置', trigger: 'blur' },
+                    { required: true, message: '请输入设备在房间的横向坐标位置', trigger: 'change' },
+                ],
+            }
+        }
+    },
+    created() {
+        if (this.studentId) {
+            // 学生定位
+            this.initStudentLocation()
+        } else {
+            // 获取楼层数据
+            this.getFloorList()
+        }
+    },
+    mounted() {
+        this.$nextTick(() => {
+            this.initMap()
+        });
+
+        var that = this;
+        // 楼层滚动
+        var scrollUp = document.getElementById('scrollUp')
+        var scrollDown = document.getElementById('scrollDown')
+        var floorUl = document.getElementById('floorUl')
+        scrollUp.onclick = function(e) {
+            floorUl.scrollTop -= 300
+        }
+        scrollDown.onclick = function(e) {
+            floorUl.scrollTop = floorUl.scrollTop + 300
+        }
+        // 显示隐藏插座列表
+        var stationListElem = document.getElementById('stationList')
+        var toggleStationListElem = document.getElementById('toggleStationList')
+        toggleStationListElem.onclick = function(e){
+            if (stationListElem.style.display === 'none' || !stationListElem.style.display) {
+                stationListElem.style.display = 'block';
+                that.toggleIcon = 'right';
+            } else {
+                stationListElem.style.display = 'none'
+                that.toggleIcon = 'left';
+            }
+        }
+    },
+    methods: {
+        /**
+         * 平面图
+         */
+        initMap() {
+            // this.lmap = L.map('floorMap', {
+            this.lmap = L.map(this.mapId, {
+                minZoom: 1,
+                maxZoom: 4,
+                center: [0, 0],
+                zoom: 1,
+                crs: L.CRS.Simple,
+                drawControl: true, // 使用draw
+            });
+            // 隐藏右下角的控制栏
+            this.lmap.attributionControl.remove()
+            // 图片参数
+            var w = 4000,
+                h = 3000,
+                url = this.currentFloorInfo.drawing_path || this.defaultImageUrl;
+            // calculate the edges of the image, in coordinate space
+            var southWest = this.lmap.unproject([0, h], this.lmap.getMaxZoom()-1);
+            var northEast = this.lmap.unproject([w, 0], this.lmap.getMaxZoom()-1);
+            var bounds = new L.LatLngBounds(southWest, northEast);
+            // 添加图片覆盖物
+            this.imageObj = L.imageOverlay(url, bounds);
+            this.imageObj.addTo(this.lmap)
+            // tell leaflet that the map is exactly as big as the image
+            this.lmap.setMaxBounds(bounds);
+            // 初始化覆盖物群组
+            this.markersLayer = L.layerGroup()
+        },
+        // 学生定位
+        initStudentLocation() {
+            // 获取学生室内基站信息
+            this.$http.get('student/getIndoorStationInfo/' + this.currentStudentId)
+                    .then( resp => {
+                        if (resp.code === 10000) {
+                            this.currentStudentInfo = resp.data
+                            // 先获取学生信息再获取楼层信息(需要用到学生最后经过基站的楼层)
+                            this.getFloorList()
+                        }
+                    })
+        },
+        // 添加基站
+        addStation() {
+            if (!this.lmap) {
+                this.$message.warning('图片加载错误,请关闭重新打开')
+                return;
+            }
+            var that = this;
+            // 修改鼠标样式
+            // L.DomUtil.addClass(this.lmap._container,'leaflet-cursor-Crosshair');
+            // 添加点击事件
+            this.lmap.once('click', function(e) {
+                if (!that.currentFloorInfo.drawing_path) {
+                    that.$message.error('请先上传楼层图片')
+                    return;
+                }
+                that.formData = {
+                    coordinate_x: e.latlng.lng,
+                    coordinate_y: e.latlng.lat,
+                    school_id: that.currentFloorInfo.school_id,
+                    building_id: that.currentFloorInfo.building_id,
+                    floor_id: that.currentFloorInfo.id,
+                }
+                that.showForm = true
+            })
+        },
+        // 获取楼层数据
+        getFloorList() {
+            if (!this.bid) {
+                this.$message.warn('获取楼层数据失败')
+                return;
+            }
+            this.$http.get('kqFloor/getBuildingFloorData/'+ this.bid)
+                    .then(resp => {
+                        this.floorList = []
+                        if (resp.code === 10000) {
+                            this.floorList = resp.data
+                            let len = this.floorList.length;
+                            // 无楼层数据
+                            if (!len) {
+                                return;
+                            }
+                            this.currentFloorInfo = null
+                            // 如果有学生信息存在,定位到学生所在楼层
+                            if (this.currentStudentInfo && this.currentStudentInfo.floor_id) {
+                                this.floorList.forEach(item => {
+                                    if (this.currentStudentInfo.floor_id == item.id) {
+                                        this.currentFloorInfo = item
+                                    }
+                                })
+                                if (!this.currentFloorInfo) {
+                                    this.currentFloorInfo = this.floorList[this.floorList.length - 1]
+                                }
+                            
+                            } else { // 默认选择最底层
+                                this.currentFloorInfo = this.floorList[this.floorList.length - 1]
+                            }
+
+                            this.imageObj.setUrl(this.currentFloorInfo.drawing_path || this.defaultImageUrl)
+                            this.getStationList(this.currentFloorInfo.id)
+                            
+                            this.$nextTick(() => {
+                                // 目前容器内只能显示10层,超过10层要将容器的滚动到最底层
+                                let floorUl = document.getElementById('floorUl')
+                                    floorUl.scrollTo(0, floorUl.scrollHeight)
+                            })
+                        }
+
+                    }).catch(()=>{})
+        },
+        // 切换楼层
+        changeFloor(info, event) {
+            this.currentFloorInfo = info
+            // 换底图
+            this.imageObj.setUrl(info.drawing_path || this.defaultImageUrl)
+            // 换插座数据
+            this.getStationList(info.id)
+            // 按钮样式
+            // addClass(event.target, 'actived')
+            // 移除其他同胞元素样式
+            this.removeSiblingsClass(event.target, 'actived')
+        },
+        // 移除同胞元素样式
+        removeSiblingsClass(el, cls) {
+            var _el = el;
+            // 前
+            while (el = el.previousSibling) {
+                removeClass(el, cls)
+            } 
+            // 后
+            var el = _el;
+            while (el = el.nextSibling) {
+                removeClass(el, cls)
+            }
+        },
+        // 获取插座数据
+        getStationList(fid) {
+            if (!fid) {
+                let len = this.floorList.length
+                if (!len) {
+                    this.stationList = [];
+                    return false;
+                }
+                fid = this.floorList[len - 1]; // 默认排序最小层
+            }
+            this.$http.get('station', {params: {floor_id: fid}})
+                    .then(resp => {
+                        this.stationList = []
+                        // 清空地图覆盖物
+                        this.markersLayer.clearLayers();
+                        // 清除学生覆盖物
+                        this.studentMarker && this.studentMarker.remove()
+                        
+                        if (resp.code === 10000) {
+                            this.stationList = resp.data || []
+                            // 添加覆盖物
+                            this.addMultiMarkers()
+                        }
+                    }).catch(()=>{})
+        },
+        // 添加多覆盖物
+        addMultiMarkers() {
+            if (!this.lmap) {
+                this.initMap()
+            }
+            this.stationList.forEach(item => {
+                if (!item.coordinate_x || !item.coordinate_y) {
+                    return false;
+                }
+                let latlng = L.latLng(item.coordinate_y, item.coordinate_x);
+                // 单个添加
+                this.addMarker(latlng, item)
+                // 如果有学生
+                if (this.currentStudentInfo && this.currentStudentInfo.station_mac == item.station_mac) {
+                    let lng = parseFloat(item.coordinate_x) + 1.5,
+                        lat = item.coordinate_y,
+                        stuIcon = L.icon({
+                            iconUrl: '/icons/student_alarm.png',
+                            iconSize: [16, 16],
+                        }),
+                        content = [
+                            '<b>姓名:</b>' + this.currentStudentInfo.student_name,
+                            '<b>性别:</b>' + this.currentStudentInfo.sex,
+                            '<b>年龄:</b>' + this.currentStudentInfo.age,
+                            '<b>电话:</b>' + this.currentStudentInfo.phone,
+                            '<b>在线时间:</b>' + this.currentStudentInfo.online_time,
+                        ]
+                    this.studentMarker = L.marker(L.latLng(lat, lng), {icon: stuIcon}).addTo(this.lmap);
+                    this.studentMarker.bindPopup(content.join('<br/>')).openPopup();
+                }
+            })
+        },
+        // 添加单个覆盖物
+        addMarker(latlng, info) {
+            var myIcon = L.icon({
+                    iconUrl: '/icons/chazuo.png',
+                    iconSize: [24, 24],
+                });
+            var marker = L.marker(latlng, {icon: myIcon, draggable: true, title: info.room_no}).addTo(this.lmap);
+                // 将点信息保存
+                marker._infoData = info;
+                // 信息窗
+                marker.bindPopup(`<b>房间号:</b> ${info.room_no || ''}<br><b>设备名称:</b> ${info.station_name}<br><b>设备MAC:</b> ${info.station_mac}`);
+                // 移动事件
+                marker.on('moveend', (e) => {
+                    this.onMarkerMoved(e.target)
+                })
+                // 存入群组
+                this.markersLayer.addLayer(marker).addTo(this.lmap)
+        },
+        // 移动覆盖物
+        onMarkerMoved(marker) {
+            var info = marker._infoData
+
+            this.$confirm('确定要移动到当前位置吗?', '移动设备位置', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning'
+            }).then(() => {
+                    var data = {
+                        coordinate_x: marker._latlng.lng,
+                        coordinate_y: marker._latlng.lat,
+                    }
+                    this.$http.put('indoorStation/updateLocation/' + info.id, data)
+                            .then(resp => {
+                                if (resp.code === 10000) {
+                                    this.$message({
+                                        type: 'success',
+                                        message: resp.message
+                                    });
+                                    // 重新获取基站数据
+                                    this.getStationList(this.currentFloorInfo.id)
+                                }
+                            }).catch(() => {})
+
+            }).catch(() => {
+                marker.setLatLng(L.latLng(info.coordinate_y, info.coordinate_x))
+            })
+        },
+        // 提交表单
+        submitForm() {
+            // 检测
+            this.$refs.floorForm.validate((valid) => {
+                if (!valid) {
+                    return false;
+                }
+                if (this.formData.id) {
+                    // 修改
+                    this.$http.put('indoorStation/'+ this.formData.id, this.formData)
+                            .then(resp => {
+                                if (resp.code === 10000) {
+                                    this.$message.success(resp.message)
+                                    this.getStationList(this.currentFloorInfo.id)
+
+                                    this.showForm = false
+                                }
+                            }).catch(() => {})
+                } else {
+                    // 添加
+                    this.$http.post('indoorStation', this.formData)
+                            .then(resp => {
+                                if (resp.code === 10000) {
+                                    this.$message.success(resp.message)
+                                    this.getStationList(this.currentFloorInfo.id)
+                                    this.showForm = false
+                                }
+                            }).catch(() => {})
+                }
+            })
+        },
+        // 取消
+        cancelSubmit() {
+            this.showForm = false
+        },
+        // 表格中选中设备时
+        rowClick(row, col, e) {
+            // 显示覆盖物弹窗
+            var layers = this.markersLayer.getLayers();
+            layers.length && layers.forEach(item => {
+                if (!item._infoData) {
+                    return;
+                }
+                if (row.id == item._infoData.id) {
+                    item.openPopup()
+                }
+            })
+        },
+        // 修改基站
+        editStation(info) {
+            console.log('editStation',info);
+            // this.formData = info
+            this.formData = {
+                id: info.id,
+                room_no: info.room_no,
+                station_mac: info.station_mac,
+                station_name: info.station_name,
+                coordinate_x: info.coordinate_x,
+                coordinate_y: info.coordinate_y,
+            }
+            this.showForm = true
+        },
+        // 删除楼层
+        deleteStation(id) {
+            this.$confirm('确定要删除吗?', '提示', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning'
+            }).then(() => {
+                    this.$http.delete('indoorStation/' + id)
+                            .then(resp => {
+                                if (resp.code === 10000) {
+                                    this.$message({
+                                        type: 'success',
+                                        message: '删除成功!'
+                                    });
+                                    // 重新获取基站数据
+                                    this.getStationList(this.currentFloorInfo.id)
+                                }
+                            }).catch(() => {})
+
+            }).catch(() => {});
+        },
+        // 清除数据
+        clearData() {
+            this.floorList = []
+            this.stationList = []
+            this.currentFloorInfo = []
+            this.currentStudentInfo = []
+
+            this.imageObj.setUrl(this.defaultImageUrl)
+            this.markersLayer && this.markersLayer.clearLayers()
+        },
+
+    },
+  }
+</script>
+<style lang="scss" scoped>
+    .app-container {
+        .actived {
+            background-color: #63a8e0;
+            color: #ffffff;
+        }
+
+        ::v-deep .floorMap {
+            width: 100%;
+            min-width: 500px;
+            height: 100%;
+            min-height: 500px;
+            border: 1px solid #bbb;
+        }
+
+        #floorMapPanel {
+            display: flex;
+            position: absolute;
+            top: 100px;
+            right: 45px;
+            background-color: #ffffff;
+            box-shadow: 0 2px 6px 0 rgb(27 142 236 / 50%);
+            z-index: 10000;
+            // width: 440px;
+            height: 500px;
+
+            #floorList {
+                text-align: center;
+                width: 40px;
+                height: 100%;
+                border-right: 1px dashed #9e9e9e;
+                ul {
+                    height: 300px;
+                    list-style: none;
+                    overflow: hidden;
+                    padding: 0;
+                    margin: 0;
+                    li {
+                        width: 40px;
+                        line-height: 30px;
+                        cursor: pointer;
+                    }
+                    li:hover {
+                        background-color: #63a8e0;
+                        color: #ffffff;
+                    }
+                }
+
+                #addStationBtn {
+                    display: block;
+                    text-align: center;
+                    font-size: 20px;
+                    padding: 0;
+                    margin: 0;
+                    width: 100%;
+                }
+            }
+
+            #stationList {
+                display: none;
+                width: 450px;
+                height: 100%;
+                #stationTitle {
+                    line-height: 35px;
+                    font-weight: 500;
+                    font-size: 18px;
+                    text-align: center;
+                    border-bottom: 1px dashed #ccc;
+                }
+                #stationBody {
+                    font-size: 14px;
+                    line-height: 33px;
+                    text-align: center;
+                    height: 433px;
+                }
+                .stationListHead {
+                    background-color: #cfeff8;
+                }
+
+                #stationFooter {
+                    text-align: center;
+                }
+            }
+        }
+    }
+</style>

+ 138 - 0
src/views/wind/wind/form/AddEditBuilding.vue

@@ -0,0 +1,138 @@
+<template>
+    <el-dialog title="建筑信息" :visible.sync="isVisible" @closed="onClosed" :destroy-on-close="true">
+        <el-form :ref="formName" :rules="rules" :model="form" >
+            <el-form-item label="所属学校" required prop="school_id">
+                <el-select v-model="form.school_id" filterable placeholder="请选择所属学校">
+
+                    <template v-if="schoolData[0] && schoolData[0].department_type == 1">
+                        <el-option-group
+                            v-for="group in schoolData"
+                            :key="group.department_name"
+                            :label="group.department_name"
+                        >
+                            <template v-if="group.children">
+                                <el-option
+                                    v-for="item in group.children"
+                                    :key="item.id"
+                                    :label="item.department_name"
+                                    :value="item.id">
+                                </el-option>
+                            </template>
+                        </el-option-group>
+                    </template>
+                    <template v-else>
+                        <el-option v-for="school in schoolData" :key="school.id" :label="school.department_name" :value="school.id"></el-option>
+                    </template>
+
+                </el-select>
+
+            </el-form-item>
+            <el-form-item label="建筑名称" required prop="name">
+                <el-input v-model="form.name" ></el-input>
+            </el-form-item>
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+            <el-button @click="closeDialog">取 消</el-button>
+            <el-button type="primary" @click="submitForm">确 定</el-button>
+        </div>
+    </el-dialog>
+</template>
+<script>
+export default {
+    props: {
+        visible: {
+            type: Boolean,
+            default: function() {
+                return false;
+            }
+        },
+        schoolData: {
+            type: Array,
+            default: function() {
+                return [];
+            }
+        },
+        form: {
+            type: Object,
+            default: function() {
+                return {}
+            }
+        }
+    },
+    data: function() {
+        return {
+            formName: 'buildingForm',
+            isVisible: this.visible,
+            rules: {
+                school_id: [
+                    { required: true, message: '请选择所属学校', trigger: 'blur' },
+                    { required: true, message: '请选择所属学校', trigger: 'change' },
+                ],
+                name: [
+                    { required: true, message: '请输入建筑名称', trigger: 'blur' },
+                    { required: true, message: '请输入建筑名称', trigger: 'change' },
+                    { min: 1, max: 50, message: '长度在 1 到 50 个字符', trigger: 'blur' }
+                ],
+            }
+        }
+    },
+    watch: {
+        visible(val) {
+            this.isVisible = val
+        }
+    },
+    methods: {
+        closeDialog() {
+            this.isVisible = false
+        },
+        submitForm() {
+            // 检测
+            this.$refs[this.formName].validate((valid) => {
+                if (!valid) {
+                    return false;
+                }
+                if (this.form.id) {
+                    // 修改操作
+                    this.$http.put('kqBuilding/' + this.form.id, this.form)
+                            .then(resp => {
+                                if (resp.code != 10000) {
+                                    return false;
+                                }
+                                this.$message.success('修改成功');
+                                if (!this.form.id) {
+                                    this.form.id = resp.data
+                                }
+                                // 返回结果 
+                                this.$emit('result', this.form);
+                                // 关闭窗口
+                                this.closeDialog()
+
+                            })
+                } else {
+                    // 添加操作
+                    this.$http.post('kqBuilding', this.form)
+                            .then(resp => {
+                                if (resp.code != 10000 || !resp.data.id) {
+                                    return false;
+                                }
+                                this.$message.success(resp.message || '添加成功');
+                                // 返回结果 
+                                this.$emit('result', resp.data);
+                                // 关闭窗口
+                                this.closeDialog()
+                            })
+                }
+                
+            })
+        },
+        onClosed() {
+            this.$parent.visibleBuildingDialog = false
+        }
+    }
+}
+</script>
+<style lang="scss" scoped>
+    .el-select {
+        width: 100%;
+    }
+</style>

+ 232 - 0
src/views/wind/wind/form/AddEditKqFence.vue

@@ -0,0 +1,232 @@
+<template>
+    <div class="drawer-content">
+        <el-form :ref="formName" :rules="rules" :model="form" label-width="150px">
+            <el-form-item label="围栏名称" prop="name">
+                <el-input v-model="form.name"></el-input>
+            </el-form-item>
+            <el-form-item label="围栏类型" required prop="fence_shape">
+                <el-select v-model="form.fence_shape" disabled filterable placeholder="请选择围栏类型">
+                    <el-option v-for="fence in fenceTypeData" :key="fence.key" :label="fence.name" :value="fence.key"></el-option>
+                </el-select>
+            </el-form-item>
+            <el-form-item label="所属机构" required prop="departments">
+                <el-cascader
+               class="filter-item form-search-input fl"
+                
+                placeholder="请选择所属机构"
+                :options="schoolData"
+                v-model="form.departments"
+                :show-all-levels="false"
+                :props="departProps"
+                filterable
+                clearable
+              ></el-cascader>
+            </el-form-item>
+            <el-form-item label="告警推送用户" required prop="push_users">
+                <el-cascader
+                    v-model="form.push_users"
+                    :options="userData"
+                    :props="userProps"
+                    placeholder="请选择告警消息推送用户"
+                    collapse-tags
+                    clearable
+                    filterable
+                    :show-all-levels="false"
+                >
+                </el-cascader>
+            </el-form-item>
+            
+                            
+        </el-form>
+        
+        <div class="drawer-footer">
+            <el-button @click="closeDialog">关闭</el-button>
+            <el-button type="primary" @click="submitForm" >提交</el-button>
+        </div>
+    </div>
+
+</template>
+<script>
+
+import {deepClone} from '@/utils/index'
+
+export default {
+    name: 'AddEditKqFence',
+    props: {
+        formName: {
+            type: String,
+            default: 'kqFenceForm'
+        },
+        schoolData: {
+            type: Array,
+            default: function() {
+                return [];
+            }
+        },
+        formData: {
+            type: Object,
+            default: function() {
+                return {}
+            }
+        },
+    },
+    data: function() {
+        return {
+            departmentData: [],
+            departProps: {
+                label: "department_name",
+                value: "id",
+                checkStrictly: true
+            },
+            userData: [],
+            userProps: {
+                multiple: true,
+                value: 'id',
+                label: 'username',
+            },
+            fenceTypeData: [
+                {key: 'circle', name: '圆形围栏'},
+                {key: 'polygon', name: '多边形围栏'},
+            ],
+            rules: {
+                name: [
+                    { required: true, message: '请输入围栏名称', trigger: 'blur' },
+                    { required: true, message: '请输入围栏名称', trigger: 'change' },
+                    { min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
+                ],
+                departments: [
+                    { required: true, message: '请选择作用机构', trigger: 'blur' },
+                    { required: true, message: '请选择作用机构', trigger: 'change' }
+                ],
+                push_users: [
+                    { required: true, message: '请选择推送用户', trigger: 'blur' },
+                    { required: true, message: '请选择推送用户', trigger: 'change' }
+                ],
+            },
+
+        }
+    },
+    created() {
+      
+        this.getUserData()
+        
+    },
+    computed: {
+        form: {
+            get() {
+                if (this.formData.fence_type !== undefined && this.formData.fence_type != 3) {
+                    return {};
+                }
+                // 级联组件格式
+                if (this.formData.push_users && this.formData.push_users.length && typeof this.formData.push_users[0] == 'number') {
+                    this.formData.push_users = this.formData.push_users.map(item => {
+                        return [item]
+                    })
+                }
+                return this.formData;
+            },
+            set(val) {
+                
+            }
+        }
+    },
+    watch: {
+        formData(val) {
+            if (this.formData.fence_type !== undefined && val.fence_type != 3) {
+                return;
+            }
+            this.form = val
+        },
+        
+    },
+    methods: {
+      
+       
+        // 获取部门用户数据
+        getUserData() {
+            this.$http.get('user/getPushUserList')
+                    .then(resp => {
+                        if (resp.code === 10000) {
+                            this.userData = resp.data || []
+                        }
+                    })
+                    .catch(() => {})
+        },
+        submitForm() {
+            // 检测
+            this.$refs[this.formName].validate((valid) => {
+                if (!valid) {
+                    return false;
+                }
+                var push_users = []
+
+                if (this.form.push_users && this.form.push_users.length) {
+                    push_users = this.form.push_users.map(item => item[0])
+                }
+
+                if (this.form.id) {
+                    // 修改操作
+                    var saveData = {
+                        name: this.form.name,
+                        fence_shape: this.form.fence_shape,
+                        fence_info: this.form.fence_info,
+                        departments: this.form.departments,
+                        push_users: push_users,
+                        
+                    }
+                    this.$http.put('fences/editKqFence/' + this.form.id, saveData)
+                            .then(resp => {
+                                if (resp.code != 10000) {
+                                    return false;
+                                }
+                                this.$message.success('修改成功');
+                                // 返回结果 
+                                this.$emit('result', {success: true, data: this.form});
+
+                            })
+                } else {
+                    var addData = deepClone(this.form);
+                    addData.push_users = push_users;
+                    // addData.fence_type = 2;
+                    // 添加操作
+                    this.$http.post('fences/saveKqFence', addData)
+                            .then(resp => {
+                                if (resp.code != 10000) {
+                                    return false;
+                                }
+                                this.$message.success('添加成功');
+                                this.form.id = resp.data;
+                                // 返回结果 
+                                this.$emit('result', {success: true, data: this.form});
+                            })
+                }
+                
+            })
+        },
+        closeDialog() {
+            this.$parent.handleClose();
+            // this.$emit('result', {success: true});
+        },
+        onClosed() {
+            this.form = {}
+        },
+    }
+}
+</script>
+<style lang="scss" scoped>
+
+    .el-drawer span:focus {
+        outline: none;
+    }
+    .drawer-content {
+        form {
+            .el-input,.el-cascader,.el-select {
+                width: 350px;
+            }
+        }
+        .drawer-footer {
+            display: flex;
+            justify-content: flex-end;
+        }
+    }
+</style>

+ 232 - 0
src/views/wind/wind/form/AddEditSchoolFence.vue

@@ -0,0 +1,232 @@
+<template>
+    <div class="drawer-content">
+        <el-form :ref="formName" :rules="rules" :model="form" label-width="150px">
+            <el-form-item label="围栏名称" prop="name">
+                <el-input v-model="form.name"></el-input>
+            </el-form-item>
+            <el-form-item label="围栏类型" required prop="fence_shape">
+                <el-select v-model="form.fence_shape" disabled filterable placeholder="请选择围栏类型">
+                    <el-option v-for="fence in fenceTypeData" :key="fence.key" :label="fence.name" :value="fence.key"></el-option>
+                </el-select>
+            </el-form-item>
+            <el-form-item label="所属机构" required prop="departments">
+                <el-cascader
+               class="filter-item form-search-input fl"
+                
+                placeholder="请选择所属机构"
+                :options="schoolData"
+                v-model="form.departments"
+                :show-all-levels="false"
+                :props="departProps"
+                filterable
+                clearable
+              ></el-cascader>
+            </el-form-item>
+            <el-form-item label="告警推送用户" required prop="push_users">
+                <el-cascader
+                    v-model="form.push_users"
+                    :options="userData"
+                    :props="userProps"
+                    placeholder="请选择告警消息推送用户"
+                    collapse-tags
+                    clearable
+                    filterable
+                    :show-all-levels="false"
+                >
+                </el-cascader>
+            </el-form-item>
+            
+                            
+        </el-form>
+        
+        <div class="drawer-footer">
+            <el-button @click="closeDialog">关闭</el-button>
+            <el-button type="primary" @click="submitForm" >提交</el-button>
+        </div>
+    </div>
+
+</template>
+<script>
+
+import {deepClone} from '@/utils/index'
+
+export default {
+    name: 'AddEditSchoolFence',
+    props: {
+        formName: {
+            type: String,
+            default: 'schoolFenceForm'
+        },
+        schoolData: {
+            type: Array,
+            default: function() {
+                return [];
+            }
+        },
+        formData: {
+            type: Object,
+            default: function() {
+                return {}
+            }
+        },
+    },
+    data: function() {
+        return {
+            departmentData: [],
+            departProps: {
+                label: "department_name",
+                value: "id",
+                checkStrictly: true
+            },
+            userData: [],
+            userProps: {
+                multiple: true,
+                value: 'id',
+                label: 'username',
+            },
+            fenceTypeData: [
+                {key: 'circle', name: '圆形围栏'},
+                {key: 'polygon', name: '多边形围栏'},
+            ],
+            rules: {
+                name: [
+                    { required: true, message: '请输入围栏名称', trigger: 'blur' },
+                    { required: true, message: '请输入围栏名称', trigger: 'change' },
+                    { min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
+                ],
+                departments: [
+                    { required: true, message: '请选择作用机构', trigger: 'blur' },
+                    { required: true, message: '请选择作用机构', trigger: 'change' }
+                ],
+                push_users: [
+                    { required: true, message: '请选择推送用户', trigger: 'blur' },
+                    { required: true, message: '请选择推送用户', trigger: 'change' }
+                ],
+            },
+
+        }
+    },
+    created() {
+      
+        this.getUserData()
+        
+    },
+    computed: {
+        form: {
+            get() {
+                if (this.formData.fence_type !== undefined && this.formData.fence_type != 2) {
+                    return {};
+                }
+                // 级联组件格式
+                if (this.formData.push_users && this.formData.push_users.length && typeof this.formData.push_users[0] == 'number') {
+                    this.formData.push_users = this.formData.push_users.map(item => {
+                        return [item]
+                    })
+                }
+                return this.formData;
+            },
+            set(val) {
+                
+            }
+        }
+    },
+    watch: {
+        formData(val) {
+            if (this.formData.fence_type !== undefined && val.fence_type != 2) {
+                return;
+            }
+            this.form = val
+        },
+        
+    },
+    methods: {
+      
+       
+        // 获取部门用户数据
+        getUserData() {
+            this.$http.get('user/getPushUserList')
+                    .then(resp => {
+                        if (resp.code === 10000) {
+                            this.userData = resp.data || []
+                        }
+                    })
+                    .catch(() => {})
+        },
+        submitForm() {
+            // 检测
+            this.$refs[this.formName].validate((valid) => {
+                if (!valid) {
+                    return false;
+                }
+                var push_users = []
+
+                if (this.form.push_users && this.form.push_users.length) {
+                    push_users = this.form.push_users.map(item => item[0])
+                }
+
+                if (this.form.id) {
+                    // 修改操作
+                    var saveData = {
+                        name: this.form.name,
+                        fence_shape: this.form.fence_shape,
+                        fence_info: this.form.fence_info,
+                        departments: this.form.departments,
+                        push_users: push_users,
+                        
+                    }
+                    this.$http.put('fences/editSchoolFence/' + this.form.id, saveData)
+                            .then(resp => {
+                                if (resp.code != 10000) {
+                                    return false;
+                                }
+                                this.$message.success('修改成功');
+                                // 返回结果 
+                                this.$emit('result', {success: true, data: this.form});
+
+                            })
+                } else {
+                    var addData = deepClone(this.form);
+                    addData.push_users = push_users;
+                    // addData.fence_type = 2;
+                    // 添加操作
+                    this.$http.post('fences/saveSchoolFence', addData)
+                            .then(resp => {
+                                if (resp.code != 10000) {
+                                    return false;
+                                }
+                                this.$message.success('添加成功');
+                                this.form.id = resp.data;
+                                // 返回结果 
+                                this.$emit('result', {success: true, data: this.form});
+                            })
+                }
+                
+            })
+        },
+        closeDialog() {
+            this.$parent.handleClose();
+            // this.$emit('result', {success: true});
+        },
+        onClosed() {
+            this.form = {}
+        },
+    }
+}
+</script>
+<style lang="scss" scoped>
+
+    .el-drawer span:focus {
+        outline: none;
+    }
+    .drawer-content {
+        form {
+            .el-input,.el-cascader,.el-select {
+                width: 350px;
+            }
+        }
+        .drawer-footer {
+            display: flex;
+            justify-content: flex-end;
+        }
+    }
+</style>

+ 418 - 0
src/views/wind/wind/form/AddEditWind.vue

@@ -0,0 +1,418 @@
+<template>
+    <div class="drawer-content">
+        <el-form :ref="formName" :rules="rules" :model="form" label-width="150px">
+            <el-form-item label="围栏名称" prop="name">
+                <el-input v-model="form.name"></el-input>
+            </el-form-item>
+            <el-form-item label="围栏类型" required prop="fence_shape">
+                <el-select v-model="form.fence_shape" disabled filterable placeholder="请选择围栏类型">
+                    <el-option v-for="fence in fenceTypeData" :key="fence.key" :label="fence.name" :value="fence.key"></el-option>
+                </el-select>
+            </el-form-item>
+            <el-form-item label="作用机构" required prop="departments">
+                <!-- <el-cascader
+                    v-model="form.departments"
+                    :options="departmentData"
+                    :props="departmentProps"
+                    placeholder="请选择作用机构"
+                    collapse-tags
+                    clearable
+                    filterable
+                    :show-all-levels="false"
+                >
+                </el-cascader> -->
+                <el-cascader
+               class="filter-item form-search-input fl"
+                
+                placeholder="请选择作用机构"
+                :options="schoolData"
+                v-model="form.departments"
+                :show-all-levels="false"
+                :props="departProps"
+                filterable
+                clearable
+              ></el-cascader>
+            </el-form-item>
+            <el-form-item label="告警推送间隔" required prop="sent_interval">
+                <el-select v-model="form.sent_interval" clearable class="filter-item form-search-input fl" placeholder="告警推送间隔">
+                    <el-option
+                        v-for="item in SentIntervalOption"
+                        :key="item.value"
+                        :label="item.text"
+                        :value="item.value">
+                    </el-option>
+                </el-select>
+            </el-form-item>
+            <el-form-item label="告警推送用户" required prop="push_users">
+                <el-cascader
+                    v-model="form.push_users"
+                    :options="userData"
+                    :props="userProps"
+                    placeholder="请选择告警消息推送用户"
+                    collapse-tags
+                    clearable
+                    filterable
+                    :show-all-levels="false"
+                >
+                </el-cascader>
+            </el-form-item>
+            <!-- 进检测 -->
+            <el-form-item label="进围栏检测" prop="is_check_in">
+                <el-switch
+                    v-model="form.is_check_in"
+                    active-text="启用"
+                    inactive-text="禁用"
+                    :active-value="1"
+                    :inactive-value="0"
+                    @change="switchChange('in', $event)"
+                >
+                </el-switch>
+            </el-form-item>
+            <template v-if="form.is_check_in">
+                <el-form-item 
+                    v-for="(timeArea,index) in inFenceTimeArea" 
+                    :label="'进围栏检测时间' + (index +1)" 
+                    :key="'in-' + timeArea.key" 
+                >
+                    <el-time-picker
+                        is-range
+                        arrow-control
+                        v-model="timeArea.value"
+                        @change="changeTime('in_fence_time_area', index, $event)"
+                        :clearable="false"
+                        range-separator="至"
+                        start-placeholder="开始时间"
+                        end-placeholder="结束时间"
+                        placeholder="选择时间范围"
+                        value-format="HH:mm:ss"
+                    >
+                    </el-time-picker>
+
+                    <el-button type="text" icon="el-icon-plus" @click.prevent="addInTime"></el-button>
+                    <el-button v-show="index > 0" type="text" icon="el-icon-minus" @click.prevent="removeInTime(timeArea)"></el-button>
+                </el-form-item>
+            </template>
+            <!-- 出检测 -->
+            <el-form-item label="出围栏检测" prop="is_check_out">
+                <el-switch
+                    v-model="form.is_check_out"
+                    active-text="启用"
+                    inactive-text="禁用"
+                    :active-value="1"
+                    :inactive-value="0"
+                    @change="switchChange('out', $event)"
+                >
+                </el-switch>
+            </el-form-item>
+            <template v-if="form.is_check_out">
+                <el-form-item 
+                    v-for="(timeArea,index) in outFenceTimeArea" 
+                    :label="'出围栏检测时间' + (index +1)" 
+                    :key="'out-' + timeArea.key" 
+                >
+                    <el-time-picker
+                        is-range
+                        arrow-control
+                        v-model="timeArea.value"
+                        @change="changeTime('out_fence_time_area', index, $event)"
+                        :clearable="false"
+                        range-separator="至"
+                        start-placeholder="开始时间"
+                        end-placeholder="结束时间"
+                        placeholder="选择时间范围"
+                        value-format="HH:mm:ss"
+                    >
+                    </el-time-picker>
+
+                    <el-button type="text" icon="el-icon-plus" @click.prevent="addOutTime"></el-button>
+                    <el-button v-show="index > 0" type="text" icon="el-icon-minus" @click.prevent="removeOutTime(timeArea)"></el-button>
+                </el-form-item>
+            </template>
+            
+        </el-form>
+        
+        <div class="drawer-footer">
+            <el-button @click="closeDialog">关闭</el-button>
+            <el-button type="primary" @click="submitForm" >提交</el-button>
+        </div>
+    </div>
+
+</template>
+<script>
+
+import {deepClone} from '@/utils/index'
+
+export default {
+    name: 'AddEditFence',
+    props: {
+        formName: {
+            type: String,
+            default: 'fenceForm'
+        },
+        schoolData: {
+            type: Array,
+            default: function() {
+                return [];
+            }
+        },
+        formData: {
+            type: Object,
+            default: function() {
+                return {}
+            }
+        },
+    },
+    data: function() {
+        return {
+            departmentData: [],
+            // departmentProps: {
+            //     multiple: true,
+            //     value: 'id',
+            //     label: 'department_name',
+            // },
+            departProps: {
+                label: "department_name",
+                value: "id",
+                checkStrictly: true
+            },
+            userData: [],
+            userProps: {
+                multiple: true,
+                value: 'id',
+                label: 'username',
+            },
+            fenceTypeData: [
+                {key: 'circle', name: '圆形围栏'},
+                {key: 'polygon', name: '多边形围栏'},
+            ],
+            rules: {
+                name: [
+                    { required: true, message: '请输入围栏名称', trigger: 'blur' },
+                    { required: true, message: '请输入围栏名称', trigger: 'change' },
+                    { min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
+                ],
+                departments: [
+                    { required: true, message: '请选择作用机构', trigger: 'blur' },
+                    { required: true, message: '请选择作用机构', trigger: 'change' }
+                ],
+                push_users: [
+                    { required: true, message: '请选择推送用户', trigger: 'blur' },
+                    { required: true, message: '请选择推送用户', trigger: 'change' }
+                ],
+            },
+            // 时间选择
+            inFenceTimeArea: [],
+            outFenceTimeArea: [],
+            SentIntervalOption:[],
+        }
+    },
+    created() {
+        this.$http.post("sysDictData/getOptions", { type: "FenceSentInterval" }).then(res => {
+            //console.log('AAA',res.data)
+            this.SentIntervalOption =  res.data;
+        });
+        this.getDepartmentData()
+        this.getUserData()
+        
+        // 时间选择器初始化
+        this.$nextTick(() => {
+            if (this.form.in_fence_time_area && this.form.in_fence_time_area[0]) {
+                this.inFenceTimeArea = this.form.in_fence_time_area.map((item,idx) => {
+                    return {key: idx,value: item}
+                })
+            } else {
+                this.form.in_fence_time_area = this.inFenceTimeArea = [{
+                    key: Date.now(),
+                    value: ['00:00:00', '23:59:59']
+                }]
+            }
+            if (this.form.out_fence_time_area && this.form.out_fence_time_area[0]) {
+                this.outFenceTimeArea = this.form.out_fence_time_area.map((item,idx) => {
+                    return {key: idx,value: item}
+                })
+            } else {
+                this.form.out_fence_time_area = this.outFenceTimeArea = [{
+                    key: Date.now(),
+                    value: ['00:00:00', '23:59:59']
+                }]
+            }
+        })
+    },
+    computed: {
+        form: {
+            get() {
+                // 级联组件格式
+                if (this.formData.push_users && this.formData.push_users.length && typeof this.formData.push_users[0] == 'number') {
+                    this.formData.push_users = this.formData.push_users.map(item => {
+                        return [item]
+                    })
+                }
+                return this.formData;
+            },
+            set(val) {
+                if (val.in_fence_time_area) {
+                    this.inFenceTimeArea = val.in_fence_time_area.map(item => {
+                        return { key: Date.now(), value: item }
+                    })
+                } else {
+                    this.inFenceTimeArea = [{key: Date.now(),value: ['00:00:00', '23:59:59']}]
+                }
+                
+                if (val.out_fence_time_area) {
+                    this.outFenceTimeArea = val.out_fence_time_area.map(item => {
+                        return { key: Date.now(), value: item }
+                    })
+                } else {
+                    this.outFenceTimeArea = [{key: Date.now(),value: ['00:00:00', '23:59:59']}]
+                }
+            }
+        }
+    },
+    watch: {
+        formData(val) {
+            console.log(val)
+            this.form = val
+        },
+        inFenceTimeArea(val) {
+            this.form.in_fence_time_area = val.map(item => item.value)
+        },
+        outFenceTimeArea(val) {
+            this.form.out_fence_time_area = val.map(item => item.value)
+        },
+    },
+    methods: {
+        // 获取部门数据
+        getDepartmentData() {
+            this.$http.get('departments')
+                    .then(resp => {
+                        if (resp.code === 10000) {
+                            this.departmentData = resp.data || []
+                        }
+                    })
+                    .catch(() => {})
+        },
+        // 获取部门用户数据
+        getUserData() {
+            this.$http.get('user/getPushUserList')
+                    .then(resp => {
+                        if (resp.code === 10000) {
+                            this.userData = resp.data || []
+                        }
+                    })
+                    .catch(() => {})
+        },
+        submitForm() {
+            // 检测
+            this.$refs[this.formName].validate((valid) => {
+                if (!valid) {
+                    return false;
+                }
+                var push_users = []
+
+                if (this.form.push_users && this.form.push_users.length) {
+                    push_users = this.form.push_users.map(item => item[0])
+                }
+
+                if (this.form.id) {
+                    // 修改操作
+                    var saveData = {
+                        name: this.form.name,
+                        fence_shape: this.form.fence_shape,
+                        fence_info: this.form.fence_info,
+                        departments: this.form.departments,
+                        push_users: push_users,
+                        is_check_in: this.form.is_check_in,
+                        in_fence_time_area: this.form.in_fence_time_area,
+                        is_check_out: this.form.is_check_out,
+                        out_fence_time_area: this.form.out_fence_time_area,
+                        sent_interval: this.form.sent_interval,
+                    }
+                    this.$http.put('fences/' + this.form.id, saveData)
+                            .then(resp => {
+                                if (resp.code != 10000) {
+                                    return false;
+                                }
+                                this.$message.success('修改成功');
+                                // 返回结果 
+                                this.$emit('result', {success: true, data: this.form});
+
+                            })
+                } else {
+                    var addData = deepClone(this.form);
+                    addData.push_users = push_users;
+                    // 添加操作
+                    this.$http.post('fences', addData)
+                            .then(resp => {
+                                if (resp.code != 10000) {
+                                    return false;
+                                }
+                                this.$message.success('添加成功');
+                                this.form.id = resp.data;
+                                // 返回结果 
+                                this.$emit('result', {success: true, data: this.form});
+                            })
+                }
+                
+            })
+        },
+        closeDialog() {
+            this.$parent.handleClose();
+            // this.$emit('result', {success: true});
+        },
+        onClosed() {
+            this.form = {}
+        },
+        removeInTime(item) {
+            var index = this.inFenceTimeArea.indexOf(item)
+            if (index !== -1) {
+                this.inFenceTimeArea.splice(index, 1)
+            }
+        },
+        addInTime() {
+            this.inFenceTimeArea.push({
+                key: Date.now(),
+                value: ['06:00:00', '18:00:00']
+            });
+        },
+        removeOutTime(item) {
+            var index = this.outFenceTimeArea.indexOf(item)
+            if (index !== -1) {
+                this.outFenceTimeArea.splice(index, 1)
+            }
+        },
+        addOutTime() {
+            this.outFenceTimeArea.push({
+                key: Date.now(),
+                value: ['06:00:00', '18:00:00']
+            });
+        },
+        changeTime(field, index, val) {
+            this.form[field][index] = val
+        },
+        switchChange(type, val) {
+            if (type == 'in' && val && !this.form.in_fence_time_area) {
+                this.form.in_fence_time_area = [this.inFenceTimeArea[0].value]
+            } else if (type == 'out' && val && !this.form.out_fence_time_area) {
+                this.form.out_fence_time_area = [this.outFenceTimeArea[0].value]
+            }
+        },
+    }
+}
+</script>
+<style lang="scss" scoped>
+
+    .el-drawer span:focus {
+        outline: none;
+    }
+    .drawer-content {
+        form {
+            .el-input,.el-cascader,.el-select {
+                width: 350px;
+            }
+        }
+        .drawer-footer {
+            display: flex;
+            justify-content: flex-end;
+        }
+    }
+</style>

+ 147 - 0
src/views/wind/wind/index.scss

@@ -0,0 +1,147 @@
+.app-container {
+    height: calc(100vh - 100px);
+    margin: 5px;
+    padding: 5px;
+    // display: flex;
+
+    .bm-view {
+        display: inline-block;
+        width: calc(100% - 500px);
+        // min-width: 600px;
+        height: 100vh;
+
+        ::v-deep .BMap_bubble_content {
+            font-size: 14px;
+        }
+        .autoAddressClass{
+            li {
+              i.el-icon-search {margin-top:11px;}
+              .mgr10 {margin-right: 10px;}
+              .title {
+                text-overflow: ellipsis;
+                overflow: hidden;
+              }
+              .address {
+                line-height: 1;
+                font-size: 12px;
+                color: #b4b4b4;
+                margin-bottom: 5px;
+              }
+            }
+          }
+        
+    }
+    #rightPanel {
+        display: inline-block;
+        width: 500px;
+        height: 100vh;
+
+        #toggleListBtn {
+            position: absolute;
+            top: 12px;
+            right: 510px;
+        }
+        #saveFenceBtn {
+            position: absolute;
+            top: 65px;
+            right: 510px;
+        }
+        #clearFenceBtn {
+            position: absolute;
+            top: 110px;
+            right: 510px;
+        }
+
+        #tableTabs {
+            width: 100%;
+            height: 100%;
+            min-height: 500px;
+    
+            .el-tabs--border-card {
+                height: 100%;
+                
+                ::v-deep .el-tabs__content {
+                    padding: 0;
+                    height: calc(100% - 40px);
+                    overflow: auto;
+    
+                    .app-container {
+                        height: 100%;
+                    }
+                }
+            }
+    
+            ::v-deep .table-body {
+                width: 100%;
+            }
+        }
+    }
+
+
+    #normalCtrl {
+        // display: flex;
+        // flex-flow: column wrap-reverse;
+        inset: 40px auto auto 60px  !important;
+        #searchbox {width: 464px;position: relative;z-index: 5;display: flex;}
+        box-shadow: 1px 1px 9px 0px #8390bfc9;
+    }
+    #addBtns {
+        display: flex;
+        flex-flow: column;
+        width: 200px;
+        button {
+            margin: 0;
+        }
+        .add-btn-item {
+            display: flex;
+            align-items: center;
+            background-color: #fff;
+            // font-size: 12px !important;
+        }
+    }
+    #checkboxArea {
+        background-color: #fff;
+        line-height: 32px;
+        padding: 0 10px;
+    }
+    ::v-deep .win-btn {
+        margin-top: 20px;
+
+        button {
+            display: inline-block;
+            line-height: 1;
+            white-space: nowrap;
+            cursor: pointer;
+            background: #59b0c3;
+            border: 1px solid #DCDFE6;
+            border-color: #DCDFE6;
+            color: #ffffff;
+            -webkit-appearance: none;
+            text-align: center;
+            -webkit-box-sizing: border-box;
+            box-sizing: border-box;
+            outline: none;
+            margin: 0;
+            -webkit-transition: 0.1s;
+            transition: 0.1s;
+            font-weight: 400;
+            -moz-user-select: none;
+            -webkit-user-select: none;
+            -ms-user-select: none;
+            padding: 10px 20px;
+            font-size: 14px;
+            border-radius: 4px;
+        }
+    }
+
+    ::v-deep .dialog-no-padding .el-dialog__body {
+        padding: 0;
+    }
+
+    ::v-deep .fence-drawer {
+        min-width: 500px;
+    }
+    ::v-deep .el-drawer span:focus {
+        outline: none;
+    }
+}

File diff suppressed because it is too large
+ 1062 - 0
src/views/wind/wind/index.vue


+ 233 - 0
src/views/wind/wind/table.vue

@@ -0,0 +1,233 @@
+<template>
+    <div class="app-container">
+        <div class="table-header">
+            <div class="tatble-toolbar">
+                <el-row style="float: right;margin: 5px 0;">
+                    <el-button type="primary" icon="el-icon-plus" v-if="showAddBtn" @click="addBuilding">添加建筑</el-button>
+                </el-row>
+            </div>
+        </div>
+        <div class="table-body">
+            <el-table
+                :data="tableData"
+                border
+                fit
+                @row-click="rowClick"
+                style="width: 100%">
+                <el-table-column
+                    fixed
+                    prop="name"
+                    label="建筑名称"
+                    align="center"
+                />
+                <el-table-column
+                    prop="school_name"
+                    label="所属部门"
+                    align="center"
+                />
+                <el-table-column
+                    prop="created_at"
+                    label="添加时间"
+                    align="center"
+                    width="130"
+                />
+                <el-table-column
+                    label="操作"
+                    align="center"
+                    fixed="right"
+                    width="100">
+                    <template slot-scope="scope">
+                        <el-tooltip content="楼层" placement="top" :enterable="false">
+                            <el-button type="text" size="small" icon="el-icon-coin" @click.stop="showFloorList(scope.row)"></el-button>
+                        </el-tooltip>
+                        <el-tooltip content="修改" placement="top" :enterable="false">
+                            <el-button type="text" size="small" icon="el-icon-edit" @click.stop="updateInfo(scope.row)"></el-button>
+                        </el-tooltip>
+                        <el-tooltip content="删除" placement="top" :enterable="false">
+                            <el-button type="text" size="small" icon="el-icon-delete" @click.stop="deleteBuilding(scope.row.id)"></el-button>
+                        </el-tooltip>
+                    </template>
+                </el-table-column>
+            </el-table>
+            <div class="table-pagination">
+                <el-pagination
+                    @size-change="handleSizeChange"
+                    @current-change="handleCurrentChange"
+                    :current-page="paginate.current"
+                    :page-sizes="paginate.sizes"
+                    :page-size="paginate.limit"
+                    layout="total, sizes, prev, pager, next, jumper"
+                    :total="paginate.total">
+                </el-pagination>
+            </div>
+        </div>
+        <div class="table-footer">
+        </div>
+
+        <!-- form 表单 -->
+        <el-dialog title="建筑信息" :visible.sync="showFormDialog" append-to-body destroy-on-close @closed="onFormDialogClosed" width="500px">
+            <el-form ref="floorForm" :rules="rules" :model="formData" label-width="80px">
+                <el-form-item label="建筑名称" required prop="name">
+                    <el-input v-model="formData.name" ></el-input>
+                </el-form-item>
+            </el-form>
+            <div slot="footer" class="dialog-footer">
+                <el-button @click="closeDialog">取 消</el-button>
+                <el-button type="primary" @click="submitForm">确 定</el-button>
+            </div>
+        </el-dialog>
+        <!-- 楼层列表 -->
+        <el-dialog :title="currentBuildingInfo.name" :visible.sync="visibleFloorList" append-to-body destroy-on-close fullscreen :custom-class="'dialog-no-padding'">
+            <floor-table ref="floor_table" :buildingInfo="currentBuildingInfo" />
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+
+import rlListOperate from '@/layout/rl-list-operate/rlListOperate';
+import FloorTable from './floor/index';
+
+export default {
+    mixins: [ rlListOperate ],
+    components: { FloorTable },
+    props: {
+        schoolId: {
+            type: [String, Number],
+            // required: true
+        },
+        showAddBtn: {
+            type: Boolean,
+            default: true,
+        }
+    },
+    watch: {
+        schoolId: function(val) {
+            this.queryParam.school_id = val
+            this.getList({school_id: val})
+        },
+    },
+    data() {
+      return {
+        url: 'kqBuilding',
+        queryParam: {
+            school_id: this.schoolId
+        },
+        tableData: [],
+        showFormDialog: false, // 显示form表单
+        formData: {}, // 
+        rules: {
+            name: [
+                { required: true, message: '请输入楼层名称', trigger: 'blur' },
+                { min: 1, max: 50, message: '长度在 1 到 50 个字符', trigger: 'blur' }
+            ],
+        }, // 验证规则
+        imageUrl: '',
+        currentBuildingInfo: {}, // 当前建筑信息
+        visibleFloorList: false, // 显示楼层列表
+      }
+    },
+    methods: {
+        afterGetList(){
+            this.tableData = this.data
+        },
+        // 添加建筑
+        addBuilding() {
+            this.$router.push('/building/index')
+        },
+        // 提交表单
+        submitForm() {
+            if (this.formData.id) {
+                let saveData = {
+                    id: this.formData.id,
+                    name: this.formData.name,
+                }
+                this.$http.put('kqBuilding/'+ this.formData.id, saveData)
+                        .then(resp => {
+                            if (resp.code === 10000) {
+                                this.$message.success('修改成功')
+                                this.showFormDialog = false
+                                this.$emit('updateInfo', this.formData)
+                            }
+                        })
+            }
+        },
+        // 关闭表单弹窗
+        onFormDialogClosed() {
+            // 关闭弹窗时重置form
+            this.formData = {}
+            // 刷新数据
+            this.getList({school_id: this.queryParam.school_id})
+        },
+        // 修改楼层
+        updateInfo(info) {
+            this.formData = info
+            this.showFormDialog = true
+        },
+        // 删除楼层
+        deleteBuilding(id) {
+            this.$confirm('确定要删除吗?', '提示', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning'
+            }).then(() => {
+                    this.$http.delete('kqBuilding/' + id)
+                            .then(resp => {
+                                if (resp.code === 10000) {
+                                    this.$message({
+                                        type: 'success',
+                                        message: '删除成功!'
+                                    });
+                                    this.getList({school_id: this.queryParam.school_id})
+                                    this.$emit('afterDeleted', id)
+                                }
+                            }).catch(() => {})
+
+            }).catch(() => {});
+        },
+        // 
+        showFloorList(buildingInfo) {
+            this.currentBuildingInfo = buildingInfo
+            this.visibleFloorList = true
+        },
+        rowClick(row, column, event) {
+            this.$emit('rowClick', row)
+        },
+        closeDialog() {
+            this.showFormDialog = false
+        },
+    },
+}
+</script>
+<style lang="scss" scoped>
+    .el-select { width: 100%; }
+
+    .table-pagination {
+        margin-top: 10px;
+        text-align: right;
+    }
+
+    ::v-deep .avatar-uploader .el-upload {
+        border: 1px dashed #d9d9d9;
+        border-radius: 6px;
+        cursor: pointer;
+        position: relative;
+        overflow: hidden;
+    }
+    ::v-deep .avatar-uploader .el-upload:hover {
+        border-color: #409EFF;
+    }
+    ::v-deep .avatar-uploader-icon {
+        font-size: 28px;
+        color: #8c939d;
+        width: 178px;
+        height: 178px;
+        line-height: 178px;
+        text-align: center;
+    }
+    ::v-deep .avatar {
+        width: 178px;
+        height: 178px;
+        display: block;
+    }
+</style>

+ 273 - 0
src/views/wind/wind/table/FenceTable.vue

@@ -0,0 +1,273 @@
+<template>
+    <div class="app-container">
+        <div class="table-header">
+            <div class="tatble-toolbar">
+                <!-- <el-row style="float: right;margin: 5px 0;">
+                    <el-button type="primary" icon="el-icon-plus" v-if="showAddBtn" @click="addBuilding">添加建筑</el-button>
+                </el-row> -->
+            </div>
+        </div>
+        <div class="table-body">
+            <el-table
+                :data="tableData"
+                border
+                fit
+                @row-click="rowClick"
+                style="width: 100%">
+                <el-table-column
+                    fixed
+                    label="展开"
+                    align="center"
+                    type="expand"
+                >
+                    <template slot-scope="scope">
+                        <el-row style="line-height: 25px;font-size: 15px;">
+                            <el-col :span="24">围栏类型:{{ scope.row.type_name }}</el-col>
+                            <el-col :span="24">进围栏时间:{{ scope.row.in_fence_time_area_text }}</el-col>
+                            <el-col :span="24">出围栏时间:{{ scope.row.out_fence_time_area_text }}</el-col>
+                        </el-row>
+                    </template>
+                </el-table-column>
+                <el-table-column
+                    fixed
+                    prop="name"
+                    label="围栏名称"
+                    align="center"
+                />
+                <!-- <el-table-column
+                    prop="type_name"
+                    label="围栏类型"
+                    align="center"
+                /> -->
+                <el-table-column
+                    prop="is_check_in"
+                    label="进围栏检测"
+                    align="center"
+                >
+                    <template slot-scope="scope">
+                        <el-switch
+                            v-model="scope.row.is_check_in"
+                            active-color="#13ce66"
+                            inactive-color="#ff4949"
+                            @change="updateCheckState(scope.row, 'is_check_in', $event)"
+                            @click.stop.native
+                            :active-value="1"
+                            :inactive-value="0"
+                        >
+                        </el-switch>
+                    </template>
+                </el-table-column>
+                <el-table-column
+                    prop="is_check_out"
+                    label="出围栏检测"
+                    align="center"
+                >
+                    <template slot-scope="scope">
+                        <el-switch
+                            v-model="scope.row.is_check_out"
+                            active-color="#13ce66"
+                            inactive-color="#ff4949"
+                            @change="updateCheckState(scope.row, 'is_check_out', $event)"
+                            @click.stop.native
+                            :active-value="1"
+                            :inactive-value="0"
+                        >
+                        </el-switch>
+                    </template>
+                </el-table-column>
+                <el-table-column
+                    label="操作"
+                    align="center"
+                    fixed="right"
+                    width="100">
+                    <template slot-scope="scope">
+                        <el-tooltip content="告警记录" placement="top" :enterable="false">
+                            <el-button type="text" size="small" icon="el-icon-document" @click.stop="alarmTable(scope.row.id)"></el-button>
+                        </el-tooltip>
+                        <el-tooltip content="修改" placement="top" :enterable="false">
+                            <el-button type="text" size="small" icon="el-icon-edit" @click.stop="updateInfo(scope.row)"></el-button>
+                        </el-tooltip>
+                        <el-tooltip content="删除" placement="top" :enterable="false">
+                            <el-button type="text" size="small" icon="el-icon-delete" @click.stop="deleteItem(scope.row.id)"></el-button>
+                        </el-tooltip>
+                    </template>
+                </el-table-column>
+            </el-table>
+            <div class="table-pagination">
+                <el-pagination
+                    @size-change="handleSizeChange"
+                    @current-change="handleCurrentChange"
+                    :current-page="paginate.current"
+                    :page-sizes="paginate.sizes"
+                    :page-size="paginate.limit"
+                    layout="total, sizes, prev, pager, next, jumper"
+                    :total="paginate.total">
+                </el-pagination>
+            </div>
+        </div>
+        <div class="table-footer">
+        </div>
+
+        <!-- form 表单 -->
+
+        <!-- 围栏日志 -->
+        
+        <el-dialog title="围栏告警记录" :visible.sync="showAlarmDialog" append-to-body fullscreen>
+            <alarm-report-table :fenceId="currentFenceId" />
+        </el-dialog>
+
+    </div>
+</template>
+
+<script>
+
+import rlListOperate from '@/layout/rl-list-operate/rlListOperate';
+import FloorTable from '../floor/index';
+import AlarmReportTable from '@/views/alarm/alarmReport';
+
+export default {
+    name: 'FenceTable',
+    mixins: [ rlListOperate ],
+    components: { FloorTable, AlarmReportTable },
+    props: {
+    },
+    data() {
+      return {
+        url: 'fences',
+        showAlarmDialog: false,
+        currentFenceId: null,
+        queryParam: {
+            
+        },
+        tableData: [],
+        showFormDialog: false, // 显示form表单
+        formData: {}, // 
+        rules: {
+            name: [
+                { required: true, message: '请输入围栏名称', trigger: 'blur' },
+                { required: true, message: '请输入围栏名称', trigger: 'change' },
+                { min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
+            ],
+        }, // 验证规则
+        imageUrl: '',
+        currentBuildingInfo: {}, // 当前建筑信息
+        visibleFloorList: false, // 显示楼层列表
+      }
+    },
+    methods: {
+        afterGetList(){
+            this.tableData = this.data
+        },
+        // 添加建筑
+        addBuilding() {
+            // this.$router.push('/building/index')
+        },
+        // 提交表单
+        submitForm() {
+            if (this.formData.id) {
+                let saveData = {
+                    id: this.formData.id,
+                    name: this.formData.name,
+                }
+                this.$http.put('fences/'+ this.formData.id, saveData)
+                        .then(resp => {
+                            if (resp.code === 10000) {
+                                this.$message.success('修改成功')
+                                this.showFormDialog = false
+                            }
+                        })
+            }
+        },
+        // 关闭表单弹窗
+        onFormDialogClosed() {
+            // 关闭弹窗时重置form
+            this.formData = {}
+            // 刷新数据
+            this.getList()
+        },
+        // 点击行
+        rowClick(row, column, event) {
+            this.$emit('rowClick', row)
+        },
+        // 修改
+        updateInfo(info) {
+            this.$emit('updateInfo', info)
+        },
+        // 删除
+        deleteItem(id) {
+            this.$confirm('确定要删除吗?', '提示', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning'
+            }).then(() => {
+                    this.$http.delete('fences/' + id)
+                            .then(resp => {
+                                if (resp.code === 10000) {
+                                    this.$message({
+                                        type: 'success',
+                                        message: '删除成功!'
+                                    });
+                                    this.getList()
+                                    this.$emit('afterDeleted', id)
+                                }
+                            }).catch(() => {})
+
+            }).catch(() => {});
+        },
+        // 更新启用、禁用状态
+        updateCheckState(row, field, val) {
+            if (!row.id) {
+                this.$message('获取围栏id失败')
+                return ;
+            }
+            this.$http.put('fences/' + row.id, { [field]: val })
+                    .then(resp => {
+                        if (resp.code === 10000) {
+                            this.$message.success('操作成功')
+                            row[field] = val
+                            this.$emit('rowClick', row)
+                        }
+                    })
+
+        },
+        // 
+        alarmTable(id){
+            this.showAlarmDialog = true
+            this.currentFenceId = id
+        },
+
+    },
+}
+</script>
+<style lang="scss" scoped>
+    .el-select { width: 100%; }
+
+    .table-pagination {
+        margin-top: 10px;
+        text-align: right;
+    }
+
+    ::v-deep .avatar-uploader .el-upload {
+        border: 1px dashed #d9d9d9;
+        border-radius: 6px;
+        cursor: pointer;
+        position: relative;
+        overflow: hidden;
+    }
+    ::v-deep .avatar-uploader .el-upload:hover {
+        border-color: #409EFF;
+    }
+    ::v-deep .avatar-uploader-icon {
+        font-size: 28px;
+        color: #8c939d;
+        width: 178px;
+        height: 178px;
+        line-height: 178px;
+        text-align: center;
+    }
+    ::v-deep .avatar {
+        width: 178px;
+        height: 178px;
+        display: block;
+    }
+</style>

+ 229 - 0
src/views/wind/wind/table/KqFenceTable.vue

@@ -0,0 +1,229 @@
+<template>
+    <div class="app-container">
+        <div class="table-header">
+            <div class="tatble-toolbar">
+                <!-- <el-row style="float: right;margin: 5px 0;">
+                    <el-button type="primary" icon="el-icon-plus" v-if="showAddBtn" @click="addBuilding">添加建筑</el-button>
+                </el-row> -->
+            </div>
+        </div>
+        <div class="table-body">
+            <el-table
+                :data="tableData"
+                border
+                fit
+                @row-click="rowClick"
+                style="width: 100%">
+                <el-table-column
+                    fixed
+                    prop="name"
+                    label="围栏名称"
+                    align="center"
+                />
+                <el-table-column
+                    prop="department_name"
+                    label="所属部门"
+                    align="center"
+                /> 
+                <el-table-column
+                    prop="created_at"
+                    label="添加时间"
+                    align="center"
+                     width="140"
+                /> 
+                <el-table-column
+                    label="操作"
+                    align="center"
+                    fixed="right"
+                    width="100">
+                    <template slot-scope="scope">
+                        <!-- <el-tooltip content="告警记录" placement="top" :enterable="false">
+                            <el-button type="text" size="small" icon="el-icon-document" @click.stop="alarmTable(scope.row.id)"></el-button>
+                        </el-tooltip> -->
+                        <el-tooltip content="修改" placement="top" :enterable="false">
+                            <el-button type="text" size="small" icon="el-icon-edit" @click.stop="updateInfo(scope.row)"></el-button>
+                        </el-tooltip>
+                        <el-tooltip content="删除" placement="top" :enterable="false">
+                            <el-button type="text" size="small" icon="el-icon-delete" @click.stop="deleteItem(scope.row.id)"></el-button>
+                        </el-tooltip>
+                    </template>
+                </el-table-column>
+            </el-table>
+            <div class="table-pagination">
+                <el-pagination
+                    @size-change="handleSizeChange"
+                    @current-change="handleCurrentChange"
+                    :current-page="paginate.current"
+                    :page-sizes="paginate.sizes"
+                    :page-size="paginate.limit"
+                    layout="total, sizes, prev, pager, next, jumper"
+                    :total="paginate.total">
+                </el-pagination>
+            </div>
+        </div>
+        <div class="table-footer">
+        </div>
+
+        <!-- form 表单 -->
+
+        <!-- 围栏日志 -->
+        
+        <el-dialog title="围栏告警记录" :visible.sync="showAlarmDialog" append-to-body fullscreen>
+            <alarm-report-table :fenceId="currentFenceId" />
+        </el-dialog>
+
+    </div>
+</template>
+
+<script>
+
+import rlListOperate from '@/layout/rl-list-operate/rlListOperate';
+
+import AlarmReportTable from '@/views/alarm/alarmReport';
+
+export default {
+    name: 'FenceTable',
+    mixins: [ rlListOperate ],
+    components: { AlarmReportTable },
+    props: {
+    },
+    data() {
+      return {
+        url: 'fences/getKqFenceList',
+        showAlarmDialog: false,
+        currentFenceId: null,
+        queryParam: {
+            
+        },
+        tableData: [],
+        showFormDialog: false, // 显示form表单
+        formData: {}, // 
+        rules: {
+            name: [
+                { required: true, message: '请输入围栏名称', trigger: 'blur' },
+                { required: true, message: '请输入围栏名称', trigger: 'change' },
+                { min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
+            ],
+        }, // 验证规则
+        imageUrl: '',
+        currentBuildingInfo: {}, // 当前建筑信息
+        visibleFloorList: false, // 显示楼层列表
+      }
+    },
+    methods: {
+        afterGetList(){
+            this.tableData = this.data
+        },
+        // 添加建筑
+        addBuilding() {
+            // this.$router.push('/building/index')
+        },
+        // 提交表单
+        submitForm() {
+            if (this.formData.id) {
+                let saveData = {
+                    id: this.formData.id,
+                    name: this.formData.name,
+                }
+                this.$http.put('fences/saveKqFence/'+ this.formData.id, saveData)
+                        .then(resp => {
+                            if (resp.code === 10000) {
+                                this.$message.success('修改成功')
+                                this.showFormDialog = false
+                            }
+                        })
+            }
+        },
+        // 关闭表单弹窗
+        onFormDialogClosed() {
+            // 关闭弹窗时重置form
+            this.formData = {}
+            // 刷新数据
+            this.getList()
+        },
+        // 点击行
+        rowClick(row, column, event) {
+            this.$emit('rowClick', row)
+        },
+        // 修改
+        updateInfo(info) {
+            this.$emit('updateInfo', info)
+        },
+        // 删除
+        deleteItem(id) {
+            this.$confirm('确定要删除吗?', '提示', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning'
+            }).then(() => {
+                    this.$http.delete('fences/' + id)
+                            .then(resp => {
+                                if (resp.code === 10000) {
+                                    this.$message({
+                                        type: 'success',
+                                        message: '删除成功!'
+                                    });
+                                    this.getList()
+                                    this.$emit('afterDeleted', id)
+                                }
+                            }).catch(() => {})
+
+            }).catch(() => {});
+        },
+        // 更新启用、禁用状态
+        updateCheckState(row, field, val) {
+            if (!row.id) {
+                this.$message('获取围栏id失败')
+                return ;
+            }
+            this.$http.put('fences/' + row.id, { [field]: val })
+                    .then(resp => {
+                        if (resp.code === 10000) {
+                            this.$message.success('操作成功')
+                            row[field] = val
+                            this.$emit('rowClick', row)
+                        }
+                    })
+
+        },
+        // 
+        alarmTable(id){
+            this.showAlarmDialog = true
+            this.currentFenceId = id
+        },
+
+    },
+}
+</script>
+<style lang="scss" scoped>
+    .el-select { width: 100%; }
+
+    .table-pagination {
+        margin-top: 10px;
+        text-align: right;
+    }
+
+    ::v-deep .avatar-uploader .el-upload {
+        border: 1px dashed #d9d9d9;
+        border-radius: 6px;
+        cursor: pointer;
+        position: relative;
+        overflow: hidden;
+    }
+    ::v-deep .avatar-uploader .el-upload:hover {
+        border-color: #409EFF;
+    }
+    ::v-deep .avatar-uploader-icon {
+        font-size: 28px;
+        color: #8c939d;
+        width: 178px;
+        height: 178px;
+        line-height: 178px;
+        text-align: center;
+    }
+    ::v-deep .avatar {
+        width: 178px;
+        height: 178px;
+        display: block;
+    }
+</style>

+ 229 - 0
src/views/wind/wind/table/SchoolFenceTable.vue

@@ -0,0 +1,229 @@
+<template>
+    <div class="app-container">
+        <div class="table-header">
+            <div class="tatble-toolbar">
+                <!-- <el-row style="float: right;margin: 5px 0;">
+                    <el-button type="primary" icon="el-icon-plus" v-if="showAddBtn" @click="addBuilding">添加建筑</el-button>
+                </el-row> -->
+            </div>
+        </div>
+        <div class="table-body">
+            <el-table
+                :data="tableData"
+                border
+                fit
+                @row-click="rowClick"
+                style="width: 100%">
+                <el-table-column
+                    fixed
+                    prop="name"
+                    label="围栏名称"
+                    align="center"
+                />
+                <el-table-column
+                    prop="department_name"
+                    label="所属部门"
+                    align="center"
+                /> 
+                <el-table-column
+                    prop="created_at"
+                    label="添加时间"
+                    align="center"
+                     width="140"
+                /> 
+                <el-table-column
+                    label="操作"
+                    align="center"
+                    fixed="right"
+                    width="100">
+                    <template slot-scope="scope">
+                        <!-- <el-tooltip content="告警记录" placement="top" :enterable="false">
+                            <el-button type="text" size="small" icon="el-icon-document" @click.stop="alarmTable(scope.row.id)"></el-button>
+                        </el-tooltip> -->
+                        <el-tooltip content="修改" placement="top" :enterable="false">
+                            <el-button type="text" size="small" icon="el-icon-edit" @click.stop="updateInfo(scope.row)"></el-button>
+                        </el-tooltip>
+                        <el-tooltip content="删除" placement="top" :enterable="false">
+                            <el-button type="text" size="small" icon="el-icon-delete" @click.stop="deleteItem(scope.row.id)"></el-button>
+                        </el-tooltip>
+                    </template>
+                </el-table-column>
+            </el-table>
+            <div class="table-pagination">
+                <el-pagination
+                    @size-change="handleSizeChange"
+                    @current-change="handleCurrentChange"
+                    :current-page="paginate.current"
+                    :page-sizes="paginate.sizes"
+                    :page-size="paginate.limit"
+                    layout="total, sizes, prev, pager, next, jumper"
+                    :total="paginate.total">
+                </el-pagination>
+            </div>
+        </div>
+        <div class="table-footer">
+        </div>
+
+        <!-- form 表单 -->
+
+        <!-- 围栏日志 -->
+        
+        <el-dialog title="围栏告警记录" :visible.sync="showAlarmDialog" append-to-body fullscreen>
+            <alarm-report-table :fenceId="currentFenceId" />
+        </el-dialog>
+
+    </div>
+</template>
+
+<script>
+
+import rlListOperate from '@/layout/rl-list-operate/rlListOperate';
+import FloorTable from '../floor/index';
+import AlarmReportTable from '@/views/alarm/alarmReport';
+
+export default {
+    name: 'FenceTable',
+    mixins: [ rlListOperate ],
+    components: { FloorTable, AlarmReportTable },
+    props: {
+    },
+    data() {
+      return {
+        url: 'fences/getSchoolFenceList',
+        showAlarmDialog: false,
+        currentFenceId: null,
+        queryParam: {
+            
+        },
+        tableData: [],
+        showFormDialog: false, // 显示form表单
+        formData: {}, // 
+        rules: {
+            name: [
+                { required: true, message: '请输入围栏名称', trigger: 'blur' },
+                { required: true, message: '请输入围栏名称', trigger: 'change' },
+                { min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
+            ],
+        }, // 验证规则
+        imageUrl: '',
+        currentBuildingInfo: {}, // 当前建筑信息
+        visibleFloorList: false, // 显示楼层列表
+      }
+    },
+    methods: {
+        afterGetList(){
+            this.tableData = this.data
+        },
+        // 添加建筑
+        addBuilding() {
+            // this.$router.push('/building/index')
+        },
+        // 提交表单
+        submitForm() {
+            if (this.formData.id) {
+                let saveData = {
+                    id: this.formData.id,
+                    name: this.formData.name,
+                }
+                this.$http.put('fences/saveSchoolFence/'+ this.formData.id, saveData)
+                        .then(resp => {
+                            if (resp.code === 10000) {
+                                this.$message.success('修改成功')
+                                this.showFormDialog = false
+                            }
+                        })
+            }
+        },
+        // 关闭表单弹窗
+        onFormDialogClosed() {
+            // 关闭弹窗时重置form
+            this.formData = {}
+            // 刷新数据
+            this.getList()
+        },
+        // 点击行
+        rowClick(row, column, event) {
+            this.$emit('rowClick', row)
+        },
+        // 修改
+        updateInfo(info) {
+            this.$emit('updateInfo', info)
+        },
+        // 删除
+        deleteItem(id) {
+            this.$confirm('确定要删除吗?', '提示', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning'
+            }).then(() => {
+                    this.$http.delete('fences/' + id)
+                            .then(resp => {
+                                if (resp.code === 10000) {
+                                    this.$message({
+                                        type: 'success',
+                                        message: '删除成功!'
+                                    });
+                                    this.getList()
+                                    this.$emit('afterDeleted', id)
+                                }
+                            }).catch(() => {})
+
+            }).catch(() => {});
+        },
+        // 更新启用、禁用状态
+        updateCheckState(row, field, val) {
+            if (!row.id) {
+                this.$message('获取围栏id失败')
+                return ;
+            }
+            this.$http.put('fences/' + row.id, { [field]: val })
+                    .then(resp => {
+                        if (resp.code === 10000) {
+                            this.$message.success('操作成功')
+                            row[field] = val
+                            this.$emit('rowClick', row)
+                        }
+                    })
+
+        },
+        // 
+        alarmTable(id){
+            this.showAlarmDialog = true
+            this.currentFenceId = id
+        },
+
+    },
+}
+</script>
+<style lang="scss" scoped>
+    .el-select { width: 100%; }
+
+    .table-pagination {
+        margin-top: 10px;
+        text-align: right;
+    }
+
+    ::v-deep .avatar-uploader .el-upload {
+        border: 1px dashed #d9d9d9;
+        border-radius: 6px;
+        cursor: pointer;
+        position: relative;
+        overflow: hidden;
+    }
+    ::v-deep .avatar-uploader .el-upload:hover {
+        border-color: #409EFF;
+    }
+    ::v-deep .avatar-uploader-icon {
+        font-size: 28px;
+        color: #8c939d;
+        width: 178px;
+        height: 178px;
+        line-height: 178px;
+        text-align: center;
+    }
+    ::v-deep .avatar {
+        width: 178px;
+        height: 178px;
+        display: block;
+    }
+</style>