web
2025-03-20 200737d7c5fee9de223a92bd1a4aaeb05733a27f
feat:添加报表,监控功能
已修改13个文件
已添加3个文件
1224 ■■■■■ 文件已修改
index.html 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/configuration/warning/alarmScheme.js 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/facility/parameter.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/styles/index.css 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/styles/index.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/styles/ruoyi.css 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/styles/ruoyi.scss 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Pagination/index.vue 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Table/index.vue 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/configuration/warning/index.vue 264 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/screen/flow/ecology/index.vue 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/screen/flow/graphic/index.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/screen/flow/report/index.vue 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/screen/flow/shebei/index.vue 350 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/screen/flow/warning/index.vue 166 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
index.html
@@ -17,6 +17,10 @@
      height: 100%;
      margin: 0;
      padding: 0;
        //控制初始字体大小
        @media(min-width: 1921px) and (max-width: 3840px) {
            font-size: 32px;
        }
    }
    .chromeframe {
src/api/configuration/warning/alarmScheme.js
对比新文件
@@ -0,0 +1,69 @@
/*
 * @Author: elkers
 * @Date: 2024-08-09 14:29:49
 * @LastEditors: hqs elkers@163.com
 * @LastEditTime: 2024-08-10 08:57:04
 * @FilePath: \water-qinghe-web\src\api\alarmApi\alarmHistory.js
 * @Description: 历史记录api
 */
import { publicRequest } from '@/utils/request'
export default function alarmSchemeApi() {
    return {
        create: (data) => {
            return publicRequest({
                url: '/alarmScheme/create',
                method: 'post',
                data,
            });
        },
        modify: (data) => {
            return publicRequest({
                url: '/alarmScheme/modify',
                method: 'post',
                data,
            });
        },
        remove: (data) => {
            return publicRequest({
                url: `/alarmScheme/remove?id=${data}`,
                method: 'post',
                data,
            });
        },
        stop: (data) => {
            return publicRequest({
                url: `/alarmScheme/stop?id=${data}`,
                method: 'post',
                data,
            });
        },
        enable: (data) => {
            return publicRequest({
                url: `/alarmScheme/enable?id=${data}`,
                method: 'post',
                data,
            });
        },
        search: (data) => {
            return publicRequest({
                url: '/alarmScheme/search',
                method: 'post',
                data,
            });
        },
        get: (data) => {
            return publicRequest({
                url: `/alarmScheme/get?id=${data}`,
                method: 'get',
                data,
            });
        },
        setSort: (data) => {
            return publicRequest({
                url: `/alarmScheme/setSort`,
                method: 'post',
                data,
            });
        }
    };
}
src/api/facility/parameter.js
@@ -9,7 +9,7 @@
import { publicRequest } from '@/utils/request.js'
//公司配置信息
//配置信息
export default function waterFacilityParameter() {
    return {
        create: (data) => {
src/assets/styles/index.css
@@ -646,19 +646,6 @@
  padding-bottom: 5px;
}
/** 表格布局 **/
.pagination-container {
  position: relative;
  height: 25px;
  margin-bottom: 10px;
  margin-top: 15px;
  padding: 10px 20px !important;
}
.el-dialog .pagination-container {
  position: static !important;
}
/* tree border */
.tree-border {
  margin-top: 5px;
@@ -666,20 +653,6 @@
  background: #FFFFFF none;
  border-radius: 4px;
  width: 100%;
}
.pagination-container .el-pagination {
  right: 0;
  position: absolute;
}
@media (max-width: 768px) {
  .pagination-container .el-pagination > .el-pagination__jump {
    display: none !important;
  }
  .pagination-container .el-pagination > .el-pagination__sizes {
    display: none !important;
  }
}
.el-table .fixed-width .el-button--small {
@@ -946,10 +919,6 @@
.components-container {
  margin: 30px 50px;
  position: relative;
}
.pagination-container {
  margin-top: 30px;
}
.text-center {
src/assets/styles/index.scss
@@ -131,10 +131,6 @@
  position: relative;
}
.pagination-container {
  margin-top: 30px;
}
.text-center {
  text-align: center
}
src/assets/styles/ruoyi.css
@@ -107,19 +107,6 @@
  padding-bottom: 5px;
}
/** 表格布局 **/
.pagination-container {
  position: relative;
  height: 25px;
  margin-bottom: 10px;
  margin-top: 15px;
  padding: 10px 20px !important;
}
.el-dialog .pagination-container {
  position: static !important;
}
/* tree border */
.tree-border {
  margin-top: 5px;
@@ -127,20 +114,6 @@
  background: #FFFFFF none;
  border-radius: 4px;
  width: 100%;
}
.pagination-container .el-pagination {
  right: 0;
  position: absolute;
}
@media (max-width: 768px) {
  .pagination-container .el-pagination > .el-pagination__jump {
    display: none !important;
  }
  .pagination-container .el-pagination > .el-pagination__sizes {
    display: none !important;
  }
}
.el-table .fixed-width .el-button--small {
src/assets/styles/ruoyi.scss
@@ -100,19 +100,6 @@
    padding-bottom:5px
}
/** 表格布局 **/
.pagination-container {
    position: relative;
    height: 25px;
    margin-bottom: 10px;
    margin-top: 15px;
    padding: 10px 20px !important;
}
.el-dialog .pagination-container {
    position: static !important;
}
/* tree border */
.tree-border {
    margin-top: 5px;
@@ -120,20 +107,6 @@
    background: #FFFFFF none;
    border-radius:4px;
    width: 100%;
}
.pagination-container .el-pagination {
    right: 0;
    position: absolute;
}
@media ( max-width : 768px) {
  .pagination-container .el-pagination > .el-pagination__jump {
    display: none !important;
  }
  .pagination-container .el-pagination > .el-pagination__sizes {
    display: none !important;
  }
}
.el-table .fixed-width .el-button--small {
src/components/Pagination/index.vue
@@ -1,16 +1,16 @@
<template>
  <div :class="{ 'hidden': hidden }" class="pagination-container">
    <el-pagination
      :background="background"
      v-model:current-page="currentPage"
      v-model:page-size="pageSize"
      :layout="layout"
      :page-sizes="pageSizes"
      :pager-count="pagerCount"
      :total="total"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
    />
      <el-pagination
          :background="background"
          v-model:current-page="currentPage"
          v-model:page-size="pageSize"
          :layout="layout"
          :page-sizes="pageSizes"
          :pager-count="pagerCount"
          :total="total"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
      />
  </div>
</template>
@@ -98,6 +98,8 @@
.pagination-container {
  background: #fff;
  padding: 32px 16px;
    display: flex;
    justify-content: flex-end;
}
.pagination-container.hidden {
  display: none;
src/components/Table/index.vue
对比新文件
@@ -0,0 +1,127 @@
<script setup>
/**
 * getList 获取列表
 * headList 列定义
 *          order 是否排序
 *          orderFun: function (a, b){ return Number(a.code) > Number(b.code) ? 1 : -1 }  排序函数
 *          fixed 是否固定列
 *          filters 列过滤
 *          filterFun 过滤函数
 *          slot 自定义列, row为当前列插槽传参, 接收 <template #code="scope"> </template>, code为插槽名
 * select 是否可选择
 *
 */
import {onMounted, ref} from "vue";
const props = defineProps({
    getList: {
        type: Function,
        default: () => {},
        required: true
    },
    headList: {
        type: Array,
        default: [],
        required: true
    },
    select: {
        type: Boolean,
        default: false
    }
})
const state = reactive({
    page: 1,
    limit: 10,
    total: 0,
    list: []
})
const loading = ref(false);
const getData = (data) => {
    const pagedata = { limit: state.limit, page: data.page}
    loading.value = true;
    props.getList(pagedata).then(res => {
        state.list = res.list
        state.total = res.total
    }).finally(() => {
        loading.value = false;
    })
}
onMounted(() =>{
    getData({ page: state.page })
})
</script>
<template>
    <div style="width: 100%; height: 100%">
        <el-table :data="state.list"
                  v-loading="loading"
                  ref="multipleTable"
                  style="width: 100%; height: 85%">
            <el-table-column v-if="props.select" type="selection"></el-table-column>
            <el-table-column
                type="index"
                label="序号"
                align="center"
                width="80px"
            ></el-table-column>
            <template v-for="item in props.headList">
                <!-- 排序列 -->
                <el-table-column
                    v-if="item.order"
                    :prop="item.prop"
                    :label="item.label"
                    :sortable="item?.order"
                    :sort-method="item?.orderFun"
                />
                <!-- 正常列 -->
                <el-table-column
                    v-else
                    :prop="item.prop"
                    :label="item.label"
                    :fixed="item?.fixed"
                    :filters="item?.filters"
                    :filter-method="item?.filterFun"
                >
                    <template #default="scope">
                        <!-- 自定义列 -->
                        <template v-if="item?.slot">
                            <slot :name="item.prop" :row="scope.row"></slot>
                        </template>
                    </template>
                </el-table-column>
            </template>
        </el-table>
        <div class="pagination">
            <div class="pagination-total">共{{state.total}}条</div>
            <pagination
                v-show="state.total > 0"
                layout="prev, pager, next, jumper"
                :total="state.total"
                :page="state.current"
                :limit="state.limit"
                @pagination="getData"
            />
            <!-- 页面右侧自定义插槽,可以加自定义按钮 -->
            <slot name="pagination"></slot>
        </div>
    </div>
</template>
<style scoped lang="scss">
.pagination{
    display: flex;
    align-items: center;
    justify-content: flex-end;
    .pagination-total{
        color: #fff;
    }
}
:deep(.pagination-container){
    background-color: transparent;
    margin: 0;
    padding: 20px;
}
</style>
src/main.js
@@ -34,6 +34,15 @@
import { parseTime, resetForm, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi'
// 按需引入echarts
import * as echarts from 'echarts/core';
import { GridComponent, ToolboxComponent, TooltipComponent, TitleComponent } from 'echarts/components';
import { LineChart, BarChart } from 'echarts/charts';
import { UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
echarts.use([GridComponent, LineChart, BarChart, CanvasRenderer, UniversalTransition, ToolboxComponent, TooltipComponent, TitleComponent]);
// 分页组件
import Pagination from '@/components/Pagination'
// 自定义树选择组件
src/views/configuration/warning/index.vue
对比新文件
@@ -0,0 +1,264 @@
<template>
    <div class="app-container">
        <el-form :model="queryParams" ref="queryRef" :inline="true">
            <el-form-item prop="name">
                <el-input
                    v-model="queryParams.name"
                    placeholder="请输入内容"
                    clearable
                    style="width: 200px"
                    @keyup.enter="handleQuery"
                />
            </el-form-item>
            <el-form-item>
                <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
                <el-button icon="Refresh" @click="resetQuery">重置</el-button>
            </el-form-item>
        </el-form>
        <el-row :gutter="10" class="mb8">
            <el-col :span="1.5">
                <el-button
                    type="primary"
                    plain
                    icon="Plus"
                    @click="handleAdd"
                >新增</el-button>
            </el-col>
        </el-row>
        <!--表格及分页-->
        <el-table v-loading="loading" :data="tableData">
            <el-table-column
                v-for="(item, key, index) of tableHeader"
                :prop="key.toString()"
                :label="item"
                :key="index"
                align="center"
            >
            </el-table-column>
            <el-table-column label="操作" width="auto" align="center" class-name="small-padding fixed-width">
                <template #default="scope">
                    <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
                    <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
                </template>
            </el-table-column>
        </el-table>
        <pagination
            :total="pageParam.total"
            v-model:page="pageParam.page"
            v-model:limit="pageParam.limit"
            :page-sizes="[10,20,30]"
            @pagination="getList"
        />
        <!-- 添加或修改对话框 -->
        <el-dialog :title="title" v-model="open" append-to-body center>
            <el-form class="form-box" ref="formRef" :model="form" :rules="rules" label-width='auto'>
                <el-form-item label="选择设备" prop="facilityId">
                    <el-select v-model="form.facilityId" placeholder="请选择设备" @change="getFacityCodeList">
                        <el-option
                            v-for="(item,index) in facityList"
                            :label="item.facilityName"
                            :value="item.id"
                            :key="index"
                        ></el-option>
                    </el-select>
                </el-form-item>
                <el-form-item label="设备参数" prop="columnsCode">
                    <el-select v-model="form.columnsCode" placeholder="请选择设备参数">
                        <el-option
                            v-for="(item,index) in facityCodeList"
                            :label="item.columnsShow"
                            :value="item.columnsCode"
                            :key="index"
                        ></el-option>
                    </el-select>
                </el-form-item>
                <el-form-item label="方案名称" prop="schemeName">
                    <el-input v-model="form.schemeName" placeholder="请输入方案名称" />
                </el-form-item>
                <el-form-item label="低报警值" prop="lowAlarm">
                    <el-input v-model="form.lowAlarm" placeholder="请输入低报警值" />
                </el-form-item>
                <el-form-item label="高报警值" prop="tallAlarm">
                    <el-input v-model="form.tallAlarm" placeholder="请输入高报警值" />
                </el-form-item>
                <el-form-item label="备注" prop="remark">
                    <el-input v-model="form.remark" placeholder="请输入备注" />
                </el-form-item>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
                    <el-button type="primary" @click="submitForm">确 定</el-button>
                    <el-button @click="cancel">取 消</el-button>
                </div>
            </template>
        </el-dialog>
    </div>
</template>
<script setup name="Menu">
import facilityApi from '@/api/facility/index.js'
import waterFacilityParameter from '@/api/facility/parameter.js'
import alarmSchemeApi from '@/api/configuration/warning/alarmScheme.js'
import setPostParams from "@/utils/searchParams.js";
import {onMounted} from "vue";
const { proxy } = getCurrentInstance();
const pageParam = ref({
    total:0,
    limit:0,
    page:0,
})
const tableData = ref([]);
let  tableHeader = ref({
    schemeName: '方案名称',
    facilityName: '设备名称',
    columnsName: '参数名称',
    lowAlarm: '低报警值',
    tallAlarm: '高报警值',
    remark: '备注',
    createTimeView: '创建时间',
})
const open = ref(false);
const loading = ref(false);
const title = ref("");
const data = reactive({
    form:{},
    queryParams: {
        name: undefined,
    },
    rules: {
        facilityId: [{ required: true, message: "请选择设备", trigger: "blur" }],
        columnsCode: [{ required: true, message: "请选择设备参数", trigger: "blur" }],
        schemeName: [{ required: true, message: "请输入方案名称", trigger: "blur" }],
        lowAlarm: [{ required: true, message: "请输入低报警值", trigger: "blur" }],
        tallAlarm: [{ required: true, message: "请输入高报警值", trigger: "blur" }],
        remark: [{ required: false, message: "请输入备注信息", trigger: "blur" }],
    },
});
const { queryParams, form, rules } = toRefs(data);
const facityList = ref([]); //设备列表
const facityCodeList = ref([]); //设备参数列表
/**
 * 搜索相关
 */
/** 搜索按钮操作 */
function handleQuery() {
    getList({keywords:queryParams.value.name,page:1})
}
/** 重置按钮操作 */
function resetQuery() {
    proxy.resetForm("queryRef");
    handleQuery();
}
/** 获取列表 */
async function getList(val) {
    loading.value = true;
    let postParam = setPostParams(val)
    await alarmSchemeApi().search(postParam).then((res) =>{
        tableData.value = res.data.list
        pageParam.value.total = res.data.total
        pageParam.value.limit = res.data.limit
        pageParam.value.page = res.data.page
    })
    loading.value = false;
}
// 获取设备
const getFacityList = () => {
    let postParam = setPostParams()
    facilityApi().search(postParam).then(res => {
        facityList.value = res.data.list
    })
}
// 获取设备参数
const getFacityCodeList = (id) => {
    const code = id ? id : form.value.facilityId
    waterFacilityParameter().getParam(code).then(res => {
        facityCodeList.value = res.data
    })
}
/** 新增按钮操作 */
async function handleAdd() {
    reset();
    open.value = true;
    title.value = "新增方案配置";
}
/** 修改按钮操作 */
async function handleUpdate(row) {
    reset();
    getFacityCodeList(row.facilityId)
    form.value = Object.assign({},row)
    open.value = true;
    title.value = "修改方案配置";
}
/** 删除按钮操作 */
function handleDelete(row) {
    proxy.$modal.confirm('是否确认删除名称为"' + row.name + '"的数据项?').then(function() {
        return alarmSchemeApi().remove(row.id);
    }).then(() => {
        getList();
        proxy.$modal.msgSuccess("删除成功");
    }).catch(() => {});
}
/** 提交按钮 */
async function submitForm() {
    proxy.$refs["formRef"].validate( async valid => {
        if (valid) {
            form.value.lowAlarm = Number(form.value.lowAlarm)
            form.value.tallAlarm = Number(form.value.tallAlarm)
            if (form.value.id != undefined) {
                await alarmSchemeApi().modify(form.value).then(res => {
                    proxy.$modal.msgSuccess("修改成功");
                    open.value = false;
                    getList();
                }).catch(() =>{
                    open.value = false;
                    proxy.$modal.msgError("修改失败");
                });
            } else {
                await alarmSchemeApi().create(form.value).then(res => {
                    proxy.$modal.msgSuccess("新增成功");
                    open.value = false;
                    getList();
                }).catch(() =>{
                    open.value = false;
                    proxy.$modal.msgError("新增失败");
                });
            }
        }
    });
}
/** 取消按钮 */
function cancel() {
    open.value = false;
    reset();
}
/** 表单重置 */
function reset() {
    form.value = {
        facilityId:'',
        columnsCode:'',
        schemeName:'',
        lowAlarm:'',
        tallAlarm:'',
        remark:'',
    };
    proxy.resetForm("formRef");
}
onMounted(() => {
    getList();
    getFacityList()
})
</script>
src/views/screen/flow/ecology/index.vue
@@ -1,12 +1,6 @@
<script setup>
import {onMounted, reactive} from 'vue'
import * as echarts from 'echarts/core';
import { GridComponent, ToolboxComponent, TooltipComponent, TitleComponent } from 'echarts/components';
import { LineChart, BarChart } from 'echarts/charts';
import { UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
echarts.use([GridComponent, LineChart, BarChart, CanvasRenderer, UniversalTransition, ToolboxComponent, TooltipComponent, TitleComponent]);
const state = reactive({
    zhakouVal: 1,
@@ -287,6 +281,7 @@
        height: 96%;
        background: rgba(23,108,229,0.3);
        border: 1px solid #176CE5;
        border-radius: 8px;
        .item-t{
            height: 10%;
            padding: 0 30px;
src/views/screen/flow/graphic/index.vue
@@ -68,7 +68,7 @@
                            :value="item.value"
                        />
                    </el-select>
                    <el-input v-model="searchVal" style="width: 20rem" />
                    <el-input v-model="searchVal" style="width: 20rem" placeholder="请输入监测点名称" />
                    <el-button><el-icon><Search /></el-icon>搜索</el-button>
                    <el-button style="margin-left: 0" v-if="userType === '1'"><el-icon><Plus /></el-icon>新增</el-button>
                </div>
@@ -252,6 +252,7 @@
                    background: rgba(23,108,229,0.3);
                    border: 1px solid #176CE5;
                    padding: 20px;
                    border-radius: 8px;
                    .title{
                        height: 12%;
                        text-align: center;
@@ -262,6 +263,7 @@
                        width: 100%;
                        height: 60%;
                        background: url("@/assets/images/login_icon.png") no-repeat;
                        background-size: 100% 100%;
                    }
                    .info{
                        width: 100%;
src/views/screen/flow/report/index.vue
@@ -1,13 +1,116 @@
<script setup>
import {ref} from "vue";
import Table from '@/components/Table/index.vue'
const selectType = ref(1);
const typeOption = ref([
    { label: '水电站流量监测点', value: 1 },
    { label: '新扎口流量监测点', value: 2 },
])
const time = ref()
let tableHead = [
    { prop: 'code', label: '报警代码' },
    { prop: 'shebei', label: '报警设备', },
    { prop: 'content', label: '报警内容' },
    { prop: 'time', label: '报警时间' },
    { prop: 'sure', label: '报警确认', slot: true },
]
const getTableData = () => {
    return new Promise(resolve => {
        let arr = {
            list: [
                { code: '201', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 1 },
                { code: '202', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 2 },
                { code: '203', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 3 },
                { code: '204', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 1 },
                { code: '205', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 1 },
                { code: '206', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 1 },
                { code: '207', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 1 },
                { code: '208', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 2 },
                { code: '209', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 3 },
                { code: '2010', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 3 },
            ],
            total: 30,
        }
        resolve(arr)
    })
}
</script>
<template>
    <div>
        报表管理
    <div class="report">
        <div class="report-tool">
            <el-select
                v-model="selectType"
                class="tool-select"
                size="large"
                placeholder="Select"
                style="width: 20rem"
            >
                <el-option
                    v-for="item in typeOption"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                />
            </el-select>
            <el-select
                v-model="selectType"
                class="tool-select"
                size="large"
                placeholder="Select"
                style="width: 20rem"
            >
                <el-option
                    v-for="item in typeOption"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                />
            </el-select>
            <el-date-picker
                v-model="time"
                type="datetimerange"
                size="large"
                style="width: 30rem"
                range-separator="至"
                start-placeholder="开始时间"
                end-placeholder="结束时间"
            />
            <el-button type="success" size="large" style="width: 6rem">一键导出</el-button>
        </div>
        <div class="report-table">
            <Table :getList="getTableData" :headList="tableHead"></Table>
        </div>
    </div>
</template>
<style scoped lang="scss">
.report{
    height: 100%;
    background: linear-gradient( 180deg, #91BDDB 0%, rgba(102, 102, 102, 0.5) 100%);
    display: flex;
    flex-direction: column;
    align-items: center;
    &-tool{
        padding: 10px 0;
        display: flex;
        justify-content: center;
        align-items: center;
        gap: 30px;
        :deep(.el-date-editor){
            flex-grow: 0;
        }
    }
    &-table{
        width: 96%;
        height: 90%;
        background: rgba(23,108,229,0.3);
        border: 1px solid #176CE5;
        border-radius: 8px;
        padding: 20px;
    }
}
</style>
src/views/screen/flow/shebei/index.vue
@@ -1,13 +1,357 @@
<script setup>
import {ref} from "vue";
import {getUserType} from '@/utils/auth.js'
const userType = ref(getUserType())
const monitorRef = ref()
const searchVal = ref('')
const selectNum = ref(6);
const numOption = ref([
    { label: '6个', value: 6 },
    { label: '2个', value: 2 },
    { label: '1个', value: 1 },
])
const shebeiData = ref([
    { name: '雷达水位计', code: 'SWY001', image: '', online: true, time: '2025-02-15', address: '上游灌木丛' },
    { name: '雷达水位计', code: 'SWY001', image: '', online: true, time: '2025-02-15', address: '上游灌木丛' },
    { name: '雷达水位计', code: 'SWY001', image: '', online: true, time: '2025-02-15', address: '上游灌木丛' },
    { name: '雷达水位计', code: 'SWY001', image: '', online: true, time: '2025-02-15', address: '上游灌木丛' },
    { name: '雷达水位计', code: 'SWY001', image: '', online: true, time: '2025-02-15', address: '上游灌木丛' },
    { name: '雷达水位计', code: 'SWY001', image: '', online: true, time: '2025-02-15', address: '上游灌木丛' },
    { name: '雷达水位计', code: 'SWY001', image: '', online: true, time: '2025-02-15', address: '上游灌木丛' },
    { name: '雷达水位计', code: 'SWY001', image: '', online: true, time: '2025-02-15', address: '上游灌木丛' },
])
// 全屏操作
const handleFullScreen = () => {
    monitorRef.value.requestFullscreen()
}
</script>
<template>
    <div>
        设备管理
    <div class="shebei">
        <div class="shebei-menu">
            <div class="menu-t">设备列表</div>
            <el-menu class="el-menu">
                <el-sub-menu index="1">
                    <template #title>
                        <span>雷达水位计</span>
                    </template>
                    <el-menu-item index="1-1">SWY001</el-menu-item>
                    <el-menu-item index="1-2">SWY002</el-menu-item>
                </el-sub-menu>
                <el-sub-menu index="2">
                    <template #title>
                        <span>雷达测速仪</span>
                    </template>
                    <el-menu-item index="2-1">CY001</el-menu-item>
                    <el-menu-item index="2-2">CY002</el-menu-item>
                </el-sub-menu>
                <el-sub-menu index="3">
                    <template #title>
                        <span>雷达流量计</span>
                    </template>
                    <el-menu-item index="3-1">LJL001</el-menu-item>
                    <el-menu-item index="3-2">LJL002</el-menu-item>
                </el-sub-menu>
            </el-menu>
        </div>
        <div class="shebei-monitor">
            <div class="monitor-tool">
                <div class="tool-l">
                    <div class="name">展示数量</div>
                    <el-select
                        v-model="selectNum"
                        class="tool-select"
                        placeholder="Select"
                        style="width: 15rem"
                    >
                        <el-option
                            v-for="item in numOption"
                            :key="item.value"
                            :label="item.label"
                            :value="item.value"
                        />
                    </el-select>
                    <el-input v-model="searchVal" style="width: 20rem" placeholder="请输入监测点名称" />
                    <el-button><el-icon><Search /></el-icon>搜索</el-button>
                    <el-button style="margin-left: 0" v-if="userType === '1'"><el-icon><Plus /></el-icon>新增</el-button>
                </div>
                <div class="tool-r" @click="handleFullScreen">
                    <img src="@/assets/images/flow/fullscreen.png" />
                    全屏
                </div>
            </div>
            <div class="monitor-box" ref="monitorRef">
                <div class="list-six list" v-if="selectNum === 6">
                    <div class="item" v-for="(item, index) in shebeiData" :key="index">
                        <div class="item-t">
                            <div>{{item.name}}</div>
                            <div>{{item.code}}</div>
                        </div>
                        <div class="item-img">
                            <img v-if="item.image" :src="item.image" />
                        </div>
                        <div class="item-info">
                            <div class="online">设备在线状态: <span :style="{color: item.online ? '#56d12c' : '#bababa'}">{{item.online ? '在线' : '掉线'}}</span></div>
                            <div class="time">安装时间: <span>{{item.time}}</span></div>
                            <div class="address">安装位置: <span>{{item.address}}</span></div>
                        </div>
                    </div>
                </div>
                <div class="list-two list" v-else-if="selectNum === 2">
                    <div class="item" v-for="(item, index) in shebeiData" :key="index">
                        <div class="item-t">
                            <div>{{item.name}}</div>
                            <div>{{item.code}}</div>
                        </div>
                        <div class="item-img">
                            <img v-if="item.image" :src="item.image" />
                        </div>
                        <div class="item-info">
                            <div class="online">设备在线状态: <span :style="{color: item.online ? '#56d12c' : '#bababa'}">{{item.online ? '在线' : '掉线'}}</span></div>
                            <div class="time">安装时间: <span>{{item.time}}</span></div>
                            <div class="address">安装位置: <span>{{item.address}}</span></div>
                        </div>
                    </div>
                </div>
                <div class="list-one list" v-else-if="selectNum === 1">
                    <div class="item" v-for="(item, index) in shebeiData" :key="index">
                        <div class="item-t">
                            <div>{{item.name}}</div>
                            <div>{{item.code}}</div>
                        </div>
                        <div class="item-img">
                            <img v-if="item.image" :src="item.image" />
                        </div>
                        <div class="item-info">
                            <div class="online">设备在线状态: <span :style="{color: item.online ? '#56d12c' : '#bababa'}">{{item.online ? '在线' : '掉线'}}</span></div>
                            <div class="time">安装时间: <span>{{item.time}}</span></div>
                            <div class="address">安装位置: <span>{{item.address}}</span></div>
                        </div>
                    </div>
                </div>
                <div class="list-single" v-else></div>
            </div>
        </div>
    </div>
</template>
<style scoped lang="scss">
.shebei{
    height: 100%;
    display: flex;
    &-menu{
        flex-shrink: 0;
        width: 20%;
        height: 100%;
        padding: 10px 0;
        background: linear-gradient( 135deg, #91BDDB 0%, #9EC2DB 99%);
        overflow-y: scroll;
        &::-webkit-scrollbar {
            display: none;
        }
        .menu-t{
            height: 40px;
            line-height: 40px;
            padding-left: 20px;
            font-size: 26px;
            color: #fff;
            background: url("@/assets/images/flow/monitor-title-bg.png") no-repeat;
            background-size: 100% 100%;
        }
        .el-menu{
            background-color: transparent;
            border-right: none;
            :deep(.el-menu){
                background-color: transparent;
            }
            :deep(.el-sub-menu__title:hover) {
                background-color: rgba(0, 0, 0, 0.06);
            }
            :deep(.el-menu-item.is-active) {
                color: #fff;
            }
        }
    }
    &-monitor{
        flex-shrink: 0;
        width: 80%;
        height: 100%;
        background: linear-gradient( 180deg, #91BDDB 0%, rgba(102, 102, 102, 0.5) 100%);
        .monitor-tool{
            width: 100%;
            height: 60px;
            padding: 0 30px;
            background: linear-gradient( 90deg, #91BDDB 0%, #DADFE3 100%);
            display: flex;
            align-items: center;
            justify-content: space-between;
            .tool-l{
                display: flex;
                align-items: center;
                gap: 1rem;
                .name{
                    font-size: 1.1rem;
                }
            }
            .tool-r{
                display: flex;
                align-items: center;
                img{
                    width: 25px;
                    margin-right: 10px;
                }
            }
        }
        .monitor-box{
            height: calc(100% - 60px);
            padding-top: 10px;
            .list{
                height: 100%;
                padding: 0 30px;
                overflow-y: scroll;
                &::-webkit-scrollbar {
                    display: none;
                }
                .item{
                    flex-shrink: 0;
                    background: rgba(23,108,229,0.3);
                    border: 1px solid #176CE5;
                    border-radius: 8px;
                }
            }
            .list-six{
                display: flex;
                flex-wrap: wrap;
                gap: 20px;
                .item{
                    width: 32%;
                    height: 48%;
                    padding: 10px 20px;
                    .item-t{
                        height: 10%;
                        display: flex;
                        align-items: center;
                        justify-content: space-between;
                        font-size: 28px;
                        color: #fff;
                    }
                    .item-img{
                        margin-top: 10px;
                        width: 100%;
                        height: 68%;
                        background-color: #000;
                        img{
                            width: 100%;
                            height: 100%;
                        }
                    }
                    .item-info{
                        height: 18%;
                        display: flex;
                        flex-wrap: wrap;
                        color: #fff;
                        font-size: 1.2rem;
                        .online,
                        .time{
                            width: 50%;
                            padding: 6px 0;
                        }
                        .address{
                            width: 100%;
                        }
                    }
                }
            }
            .list-two{
                display: flex;
                flex-wrap: wrap;
                gap: 20px;
                .item{
                    width: 49%;
                    height: 99%;
                    padding: 30px;
                    .item-t{
                        height: 10%;
                        display: flex;
                        align-items: center;
                        justify-content: space-between;
                        font-size: 36px;
                        color: #fff;
                    }
                    .item-img{
                        margin-top: 10px;
                        width: 100%;
                        height: 68%;
                        background-color: #000;
                        img{
                            width: 100%;
                            height: 100%;
                        }
                    }
                    .item-info{
                        display: flex;
                        flex-wrap: wrap;
                        color: #fff;
                        font-size: 1.5rem;
                        .online,
                        .time{
                            width: 50%;
                            padding: 20px 0;
                        }
                        .address{
                            width: 100%;
                        }
                    }
                }
            }
            .list-one{
                display: flex;
                flex-direction: column;
                align-items: center;
                .item{
                    width: 70%;
                    height: 99%;
                    padding: 30px;
                    margin-bottom: 30px;
                    .item-t{
                        height: 10%;
                        display: flex;
                        align-items: center;
                        justify-content: space-between;
                        font-size: 36px;
                        color: #fff;
                    }
                    .item-img{
                        margin-top: 10px;
                        width: 100%;
                        height: 75%;
                        background-color: #000;
                        img{
                            width: 100%;
                            height: 100%;
                        }
                    }
                    .item-info{
                        display: flex;
                        flex-wrap: wrap;
                        color: #fff;
                        font-size: 1.5rem;
                        .online,
                        .time{
                            width: 50%;
                            padding: 20px 0;
                        }
                        .address{
                            width: 100%;
                        }
                    }
                }
            }
        }
    }
}
</style>
src/views/screen/flow/warning/index.vue
@@ -1,13 +1,173 @@
<script setup>
import {onMounted, ref} from "vue";
import * as echarts from 'echarts/core';
import Table from '@/components/Table/index.vue'
const timeType = ref(0)
const warnChartRef = ref()
let warnCharts = null;
let tableHead = [
    { prop: 'code', label: '报警代码' },
    { prop: 'shebei', label: '报警设备', },
    { prop: 'content', label: '报警内容' },
    { prop: 'time', label: '报警时间' },
    { prop: 'sure', label: '报警确认', slot: true },
]
const initWarnChart = () => {
    if(warnChartRef.value) {
        warnCharts = echarts.init(warnChartRef.value);
        const options = {
            tooltip: {
                trigger: 'axis',
            },
            grid: {
                top: 80,
                left: 60,
                right: 60,
                bottom: 60
            },
            xAxis: {
                type: 'category',
                data: ['设备离线', '水位异常', '流量异常', '流速异常', '其他异常'],
                axisLabel: {
                    color: '#fff',
                    fontSize: '1.2rem'
                }
            },
            yAxis: {
                type: 'value',
                name: '次',
                nameTextStyle: {
                    color: '#fff',
                    fontSize: '1.2rem'
                },
                axisLabel: {
                    color: '#fff',
                    fontSize: '1.2rem'
                }
            },
            series: [
                {
                    data: [36, 44, 38, 24, 63],
                    type: 'bar',
                    itemStyle: {
                        color: 'rgba(187,207,255,0.6)'
                    }
                }
            ]
        }
        warnCharts.setOption(options);
    }
}
const getTableData = () => {
    return new Promise(resolve => {
        let arr = {
            list: [
                { code: '201', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 1 },
                { code: '202', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 2 },
                { code: '203', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 3 },
                { code: '204', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 1 },
                { code: '205', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 1 },
                { code: '206', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 1 },
                { code: '207', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 1 },
                { code: '208', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 2 },
                { code: '209', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 3 },
                { code: '2010', shebei: '雷达水位计', content: '设备离线', time: '2025-02-08', sure: 3 },
            ],
            total: 30,
        }
        resolve(arr)
    })
}
onMounted(() => {
    initWarnChart()
})
</script>
<template>
    <div>
        预警管理
    <div class="warn">
        <div class="warn-l item">
            <div class="item-t">
                <div class="name">报警分析</div>
                <div class="select">
                    <el-radio-group v-model="timeType">
                        <el-radio :value="1">日</el-radio>
                        <el-radio :value="2">月</el-radio>
                        <el-radio :value="3">年</el-radio>
                    </el-radio-group>
                </div>
            </div>
            <div class="charts" ref="warnChartRef"></div>
        </div>
        <div class="warn-r item">
            <div class="item-t">
                <div class="name">报警记录</div>
            </div>
            <div class="warn-table">
                <Table :getList="getTableData" :headList="tableHead">
                    <template #sure="scope">
                        <div v-if="scope.row.sure === 1" style="color: #1ab394">已处理</div>
                        <div v-else-if="scope.row.sure === 2"  style="color: #e8ab04">未处理</div>
                        <div v-else style="color: #f30101">待确认</div>
                    </template>
                    <template v-slot:pagination>
                        <el-button type="success" style="width: 6rem">导出</el-button>
                    </template>
                </Table>
            </div>
        </div>
    </div>
</template>
<style scoped lang="scss">
.warn{
    height: 100%;
    background: linear-gradient( 180deg, #91BDDB 0%, rgba(102, 102, 102, 0.5) 100%);
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 30px;
    .item{
        width: 48%;
        height: 96%;
        background: rgba(23,108,229,0.3);
        border: 1px solid #176CE5;
        border-radius: 8px;
        .item-t{
            height: 10%;
            padding: 0 30px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            .name{
                font-size: 36px;
                color: #fff;
            }
            .select{
                :deep(.el-radio){
                    color: #fff;
                }
                :deep(.el-radio__label){
                    font-size: 20px;
                }
                :deep(.el-radio__input.is-checked+.el-radio__label){
                    color: #00ff00;
                }
                :deep(.el-radio__input.is-checked .el-radio__inner){
                    background-color: #00ff00;
                }
            }
        }
        .charts{
            height: 90%;
        }
        .warn-table{
            padding: 0 20px;
            height: 90%;
        }
    }
}
</style>