123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928 |
- <template>
- <el-container>
- <el-header class="gva-search-box">
- <el-form
- v-model="searchData"
- :inline="true"
- style="line-height: 75px"
- >
- <el-form-item label="名称">
- <el-input
- v-model="searchData.name"
- />
- </el-form-item>
- <el-form-item label="类型">
- <el-select
- v-model.number="searchData.regionId"
- style="width: 240px"
- clearable
- >
- <el-option
- v-for="item in region"
- :key="item.ID"
- :label="item.name"
- :value="item.ID"
- />
- </el-select>
- </el-form-item>
- <el-form-item>
- <el-button
- type="primary"
- @click="getData"
- >查询</el-button>
- </el-form-item>
- </el-form>
- </el-header>
- <el-main class="gva-table-box">
- <div style="margin: 0 0 20px 0 ">
- <el-button
- type="success"
- @click="isTunnelAdd"
- >添加</el-button>
- <el-button
- type="primary"
- :disabled="tunnelIds.length === 0"
- @click="allocationRegionDialog = true"
- >分配</el-button>
- </div>
- <el-table
- :data="tunnel"
- style="width: 100%;min-height:600px"
- row-key="id"
- @selection-change="handleSelectionChange"
- >
- <el-table-column
- type="selection"
- width="55"
- align="center"
- />
- <el-table-column
- prop="tunnelSn"
- label="序号"
- align="center"
- />
- <el-table-column
- prop="name"
- label="名称"
- align="center"
- />
- <el-table-column
- prop="region.name"
- label="地区"
- align="center"
- />
- <el-table-column
- prop="switchType"
- label="开关类型"
- align="center"
- />
- <el-table-column
- label="策略"
- align="center"
- >
- <template #default="scope">
- <el-select
- v-model="scope.row.tactics"
- style="width: 100px"
- @change="changeTactics(scope.row)"
- >
- <el-option
- v-for="item in tacticsOptions"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
- </template>
- </el-table-column>
- <el-table-column
- label="操作"
- align="center"
- width="500"
- >
- <template #default="scope">
- <el-button
- type="primary"
- size="small"
- @click="jumpScreen"
- >
- 大屏
- </el-button>
- <el-button
- type="primary"
- size="small"
- @click="tunnelData = scope.row ; tunnelEditDialog = true"
- >
- 修改
- </el-button>
- <el-button
- type="danger"
- size="small"
- @click="tunnelDelete(scope.row)"
- >
- 删除
- </el-button>
- <el-button
- type="primary"
- size="small"
- @click="tunnelControlPanel(scope.row)"
- >
- 控制面板
- </el-button>
- <el-button
- type="success"
- size="small"
- @click="tunnelDataPanel(scope.row)"
- >
- 数据面板
- </el-button>
- <el-button
- type="primary"
- size="small"
- @click="getDeviceFile(scope.row.ID)"
- >
- 生成设备文件
- </el-button>
- </template>
- </el-table-column>
- </el-table>
- <div class="gva-pagination">
- <el-pagination
- :current-page="searchData.pageInfo.page"
- :page-size="searchData.pageInfo.pageSize"
- :page-sizes="[10, 30, 50, 100]"
- :total="total"
- layout="total, sizes, prev, pager, next, jumper"
- @current-change="handleCurrentChange"
- @size-change="handleSizeChange"
- />
- </div>
- </el-main>
- <!-- 隧道添加-->
- <el-dialog
- v-model="tunnelAddDialog"
- title="隧道添加"
- width="500"
- align-center
- >
- <el-form
- v-model="tunnelData"
- style="line-height: 75px"
- >
- <el-form-item label="名称">
- <el-input
- v-model="tunnelData.name"
- style="width: 200px;"
- />
- </el-form-item>
- <el-form-item label="序号">
- <el-input
- v-model="tunnelData.tunnelSn"
- style="width: 200px;"
- />
- </el-form-item>
- <el-form-item label="地区">
- <el-select
- v-model.number="tunnelData.regionId"
- style="width: 240px"
- clearable
- >
- <el-option
- v-for="item in region"
- :key="item.ID"
- :label="item.name"
- :value="item.ID"
- />
- </el-select>
- </el-form-item>
- <el-form-item
- v-if="userInfo.authorityId === 888"
- label="开关类型"
- >
- <el-input
- v-model="tunnelData.switchType"
- style="width: 200px;"
- />
- </el-form-item>
- </el-form>
- <template #footer>
- <div class="dialog-footer">
- <el-button @click="tunnelAddDialog = false">取消</el-button>
- <el-button
- type="primary"
- @click="tunnelAdd"
- >
- 确定
- </el-button>
- </div>
- </template>
- </el-dialog>
- <!-- 隧道修改-->
- <el-dialog
- v-model="tunnelEditDialog"
- title="隧道修改"
- width="500"
- align-center
- >
- <el-form
- v-model="tunnelData"
- style="line-height: 75px"
- >
- <el-form-item label="名称">
- <el-input
- v-model="tunnelData.name"
- style="width: 200px;"
- />
- </el-form-item>
- <el-form-item label="序号">
- <el-input
- v-model="tunnelData.tunnelSn"
- style="width: 200px;"
- />
- </el-form-item>
- <el-form-item label="地区">
- <el-select
- v-model.number="tunnelData.regionId"
- style="width: 240px"
- clearable
- >
- <el-option
- v-for="item in region"
- :key="item.ID"
- :label="item.name"
- :value="item.ID"
- />
- </el-select>
- </el-form-item>
- <el-form-item
- v-if="userInfo.authorityId === 888"
- label="开关类型"
- >
- <el-input
- v-model="tunnelData.switchType"
- style="width: 200px;"
- />
- </el-form-item>
- </el-form>
- <template #footer>
- <div class="dialog-footer">
- <el-button @click="tunnelEditDialog = false">取消</el-button>
- <el-button
- type="primary"
- @click="tunnelEdit"
- >
- 确定
- </el-button>
- </div>
- </template>
- </el-dialog>
- <!-- 隧道分配-->
- <el-dialog
- v-model="allocationRegionDialog"
- title="隧道分配"
- width="500"
- align-center
- >
- <el-form>
- <el-form-item label="地区">
- <el-select
- v-model.number="regionId"
- style="width: 240px"
- clearable
- >
- <el-option
- v-for="item in region"
- :key="item.ID"
- :label="item.name"
- :value="item.ID"
- />
- </el-select>
- </el-form-item>
- </el-form>
- <template #footer>
- <div class="dialog-footer">
- <el-button @click="allocationRegionDialog = false">取消</el-button>
- <el-button
- type="primary"
- @click="allocationRegion"
- >
- 确定
- </el-button>
- </div>
- </template>
- </el-dialog>
- <!-- 生成设备文件-->
- <el-drawer
- v-model="isDeviceFile"
- title="设备"
- direction="rtl"
- size="80%"
- >
- <el-button @click="downloadDeviceFile">下载</el-button>
- </el-drawer>
- <!-- 控制面板-->
- <el-drawer
- v-model="isControlPanel"
- title="控制面板"
- direction="rtl"
- size="80%"
- >
- <el-collapse
- v-model="activeNames"
- >
- <el-collapse-item
- title="开关"
- name="1"
- >
- <div v-if="tunnelTimeData.switchType === '四路控制器'">
- <div
- v-for="(item, index) in tunnelTimeData.devices"
- key="index"
- style="margin: 10px 0"
- >
- <span style="margin: 0 20px 0 0;font-weight: 600;font-size: 16px">道路 {{ item.radarId }}</span>
- <el-switch
- v-for="(v, k) in item.deviceRelays"
- key="k"
- v-model="v.state"
- class="ml-2"
- style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
- @click="switchButton(item, v)"
- />
- </div>
- </div>
- <div v-if="tunnelTimeData.switchType === '单灯控制器'">
- <div style="margin: 0 0 0 20px">
- <el-slider
- v-model="tunnelTimeData.lampValue1"
- :step="10"
- style="width: 50%;"
- show-stops
- />
- </div>
- <div style="margin: 0 0 0 20px">
- <el-slider
- v-model="tunnelTimeData.lampValue2"
- :step="10"
- style="width: 50%;"
- show-stops
- />
- </div>
- <el-button
- type="success"
- @click="lampSet(tunnelTimeData)"
- >设定</el-button>
- </div>
- </el-collapse-item>
- <el-collapse-item
- title="定时"
- name="2"
- >
- <div style="margin-bottom: 30px">
- <span style="margin-right: 30px">开始:</span>
- <el-time-select
- v-model="tunnelTiming.startTime"
- style="width: 150px"
- start="00:00"
- end="23:55"
- step="00:05"
- format="HH:mm"
- />
- </div>
- <div style="margin-bottom: 30px">
- <span style="margin-right: 30px">结束:</span>
- <el-time-select
- v-model="tunnelTiming.endTime"
- style="width: 150px"
- start="00:00"
- end="23:55"
- step="00:05"
- format="HH:mm"
- />
- </div>
- <div>
- <el-button
- type="success"
- @click="editTunnelTiming"
- >
- 存储数据
- </el-button>
- <!-- <span style="margin-left: 30px">-->
- <!-- <!– <el-tag–>-->
- <!-- <!– v-if="tunnelTimeData.tunnelTime.isComplete"–>-->
- <!-- <!– type="success"–>-->
- <!-- <!– >完成</el-tag>–>-->
- <!-- <el-tag-->
- <!-- v-if="!tunnelTimeData.tunnelTime.isComplete"-->
- <!-- type="warning"-->
- <!-- >正在发送</el-tag>-->
- <!-- </span>-->
- </div>
- </el-collapse-item>
- </el-collapse>
- </el-drawer>
- <!-- 数据面板-->
- <el-drawer
- v-model="isDataPanel"
- title="数据面板"
- direction="rtl"
- size="80%"
- >
- <el-collapse
- v-model="dataNames"
- >
- <el-collapse-item
- title="环境"
- name="1"
- >
- <div
- v-for="[sn, data] in Array.from(groupedBySn)"
- :id="'chart-' + sn"
- :key="sn"
- style="width: 600px; height: 400px; margin: 10px;"
- />
- </el-collapse-item>
- <el-collapse-item
- title="光感"
- name="2"
- >
- <div
- v-for="[sn, data] in Array.from(lightBySn)"
- :id="'chart-' + sn"
- :key="sn"
- style="width: 600px; height: 400px; margin: 10px;"
- />
- </el-collapse-item>
- </el-collapse>
- </el-drawer>
- </el-container>
- </template>
- <script setup>
- import { ref, onMounted } from 'vue'
- import { queryAllRegions } from '@/api/region'
- import {
- createTunnel,
- deleteTunnel,
- queryTunnelList, updateTactics,
- updateTunnel, updateTunnelLamp,
- updateTunnelsRegion,
- updateTunnelTime
- } from '@/api/tunnel'
- import { ElMessage, ElMessageBox } from 'element-plus'
- import { nextTick } from 'vue'
- import { deviceSwitch, generateDeviceFile } from '@/api/device'
- import * as echarts from 'echarts'
- import { useUserStore } from '@/pinia/modules/user'
- const userData = useUserStore()
- const userInfo = userData.userInfo
- import { useRouter } from 'vue-router'
- const router = useRouter()
- const searchData = ref({
- pageInfo: {
- page: 1,
- pageSize: 10
- },
- name: '',
- regionId: undefined,
- userId: userInfo.ID
- })
- // 分页
- const handleSizeChange = (val) => {
- searchData.value.pageInfo.pageSize = val
- getData()
- }
- const handleCurrentChange = (val) => {
- searchData.value.pageInfo.page = val
- getData()
- }
- const total = ref(0)
- const region = ref()
- const tunnel = ref()
- const getData = async() => {
- await queryAllRegions().then(res => {
- region.value = res.data
- })
- await queryTunnelList(searchData.value).then(res => {
- tunnel.value = res.data.list
- total.value = res.data.total
- searchData.value.pageInfo.page = res.data.page
- searchData.value.pageInfo.pageSize = res.data.pageSize
- })
- }
- // 新增
- const tunnelAddDialog = ref(false)
- const isTunnelAdd = () => {
- tunnelAddDialog.value = true
- tunnelData.value.tunnelSn = 'LC' + getFormattedDateTime()
- }
- function getFormattedDateTime() {
- const now = new Date()
- const year = now.getFullYear()
- const month = String(now.getMonth() + 1).padStart(2, '0') // 月份从0开始,所以需要加1
- const day = String(now.getDate()).padStart(2, '0')
- const hour = String(now.getHours()).padStart(2, '0')
- const minute = String(now.getMinutes()).padStart(2, '0')
- const second = String(now.getSeconds()).padStart(2, '0')
- return `${year}${month}${day}${hour}${minute}${second}`
- }
- const tunnelData = ref({
- id: 0,
- name: '',
- regionId: undefined,
- tunnelSn: '',
- switchType: ''
- })
- const tunnelAdd = async() => {
- tunnelData.value.id = 0
- await createTunnel(tunnelData.value).then(res => {
- if (res.code === 0) {
- ElMessage.success('添加成功')
- }
- tunnelAddDialog.value = false
- getData()
- })
- }
- // 修改
- const tunnelEditDialog = ref(false)
- const tunnelEdit = async() => {
- await updateTunnel(tunnelData.value).then(res => {
- if (res.code === 0) {
- ElMessage.success('修改成功')
- }
- tunnelEditDialog.value = false
- getData()
- })
- }
- // 删除
- const tunnelDelete = (val) => {
- ElMessageBox.confirm(
- '将永久删除改数据,请问继续吗?',
- '删除',
- {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning',
- }
- )
- .then(async() => {
- await deleteTunnel(val.ID).then(res => {
- if (res.code === 0) {
- ElMessage.success('删除成功')
- }
- getData()
- })
- })
- .catch(() => {
- ElMessage({
- type: 'info',
- message: '删除已取消',
- })
- })
- }
- // 多选
- const tunnelIds = ref([])
- const handleSelectionChange = (val) => {
- const array = []
- for (const i in val) {
- array.push(val[i].ID)
- }
- tunnelIds.value = array
- }
- // 分配
- const allocationRegionDialog = ref(false)
- const regionId = ref()
- const allocationRegion = async() => {
- await updateTunnelsRegion({
- tunnelIds: tunnelIds.value,
- regionId: regionId.value
- }).then(res => {
- if (res.code === 0) {
- ElMessage.success('分配成功')
- tunnelIds.value = []
- getData()
- }
- allocationRegionDialog.value = false
- })
- }
- // 生成设备文件
- const isDeviceFile = ref(false)
- const fileData = ref()
- const getDeviceFile = async(val) => {
- isDeviceFile.value = true
- await generateDeviceFile(val).then(res => {
- fileData.value = res.data
- })
- }
- const downloadDeviceFile = () => {
- // 将对象转换为 JSON 字符串
- const jsonString = JSON.stringify(fileData.value, null, 2)
- // 创建一个新的 Blob 对象
- const blob = new Blob([jsonString], { type: 'application/json' })
- // 创建一个隐藏的可下载链接
- const url = window.URL.createObjectURL(blob)
- const link = document.createElement('a')
- // 设置链接的 href 和 download 属性
- link.href = url
- link.setAttribute('download', 'data.json') // 下载文件名
- // 将链接添加到 DOM 中
- document.body.appendChild(link)
- // 触发点击事件,开始下载
- link.click()
- // 下载完成后移除链接并释放 URL 对象
- link.remove()
- window.URL.revokeObjectURL(url) // 清理资源
- }
- // 策略
- const tacticsOptions = [
- {
- value: 1,
- label: '环境',
- },
- {
- value: 2,
- label: '雷达',
- },
- {
- value: 3,
- label: '定时',
- }
- ]
- const changeTactics = (val) => {
- ElMessageBox.confirm(
- '你确定修改当前策略吗?',
- '警告',
- {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning',
- }
- )
- .then(async() => {
- await updateTactics({
- sn: val.tunnelSn,
- tactics: val.tactics
- }).then(res => {
- if (res.code === 0) {
- ElMessage.success('修改成功')
- }
- getData()
- })
- })
- .catch(() => {
- ElMessage({
- type: 'info',
- message: '取消修改',
- })
- })
- }
- // 控制面板
- const tunnelTimeData = ref()
- const isControlPanel = ref(false)
- const tunnelControlPanel = (val) => {
- isControlPanel.value = true
- tunnelTimeData.value = val
- tunnelTiming.value = val.tunnelTime
- console.log(val)
- }
- const activeNames = ref(['1', '2'])
- // 定时
- const tunnelTiming = ref({
- startTime: '00:00',
- endTime: '00:00'
- })
- const editTunnelTiming = async() => {
- console.log(tunnelTiming.value)
- await updateTunnelTime(tunnelTiming.value).then(res => {
- if (res.code === 0) {
- ElMessage.success('已发送')
- }
- getData()
- })
- }
- const lampSet = async(val) => {
- await updateTunnelLamp({
- id: val.ID,
- tunnelSn: val.tunnelSn,
- lampValue1: val.lampValue1,
- lampValue2: val.lampValue2
- }).then(res => {
- if (res.code === 0) {
- ElMessage.success('已发送')
- }
- getData()
- })
- }
- // 数据面板
- const isDataPanel = ref(false)
- const dataNames = ref(['1'])
- const groupedBySn = ref()
- const lightBySn = ref()
- const tunnelDataPanel = (val) => {
- isDataPanel.value = true
- tunnelTimeData.value = val
- console.log(val)
- groupedBySn.value = tunnelTimeData.value.envData.reduce((acc, current) => {
- const { sn } = current
- // 如果 acc(累加器)中没有当前 sn 对应的数组,则创建一个新的数组
- if (!acc.has(sn)) {
- acc.set(sn, [])
- }
- // 将当前对象添加到对应 sn 的数组中
- acc.get(sn).push(current)
- return acc
- }, new Map())
- lightBySn.value = tunnelTimeData.value.opticalData.reduce((acc, current) => {
- const { sn } = current
- // 如果 acc(累加器)中没有当前 sn 对应的数组,则创建一个新的数组
- if (!acc.has(sn)) {
- acc.set(sn, [])
- }
- // 将当前对象添加到对应 sn 的数组中
- acc.get(sn).push(current)
- return acc
- }, new Map())
- nextTick(() => {
- groupedBySn.value.forEach((data, index) => {
- initChart(index, data)
- })
- lightBySn.value.forEach((data, index) => {
- initLightBySnChart(index, data)
- })
- })
- }
- // 环境
- function initChart(index, data) {
- const chartDom = document.getElementById('chart-' + index)
- if (!chartDom) {
- console.error(`无法找到元素: chart-${index}`)
- return
- }
- const myChart = echarts.init(chartDom)
- const option = {
- title: {
- text: '环境监测'
- },
- tooltip: {
- trigger: 'axis'
- },
- legend: {
- data: ['温度', '湿度', '光照度']
- },
- xAxis: {
- type: 'time', // 使用时间类型的x轴
- boundaryGap: false,
- },
- yAxis: {},
- series: [
- {
- name: '温度',
- type: 'line',
- data: data.map(item => [new Date(item.CreatedAt).getTime(), item.temperature])
- },
- {
- name: '湿度',
- type: 'line',
- data: data.map(item => [new Date(item.CreatedAt).getTime(), item.humidity])
- },
- {
- name: '光照度',
- type: 'line',
- data: data.map(item => [new Date(item.CreatedAt).getTime(), item.illuminance])
- }
- ],
- dataZoom: [
- {
- type: 'slider', // 滑动条型数据区域缩放组件
- start: 0, // 默认显示数据的起始百分比
- end: 100 // 默认显示数据的结束百分比
- },
- {
- type: 'inside', // 支持鼠标滚轮和双指缩放
- start: 0,
- end: 100
- }
- ]
- }
- myChart.setOption(option)
- }
- // 光感
- function initLightBySnChart(index, data) {
- const chartDom = document.getElementById('chart-' + index)
- if (!chartDom) {
- console.error(`无法找到元素: chart-${index}`)
- return
- }
- const myChart = echarts.init(chartDom)
- const option = {
- title: {
- text: '光感监测'
- },
- tooltip: {
- trigger: 'axis'
- },
- legend: {
- data: ['光照度']
- },
- xAxis: {
- type: 'time', // 使用时间类型的x轴
- boundaryGap: false,
- },
- yAxis: {},
- series: [
- {
- name: '光照度',
- type: 'line',
- data: data.map(item => [new Date(item.CreatedAt).getTime(), item.illuminance])
- }
- ],
- dataZoom: [
- {
- type: 'slider', // 滑动条型数据区域缩放组件
- start: 0, // 默认显示数据的起始百分比
- end: 100 // 默认显示数据的结束百分比
- },
- {
- type: 'inside', // 支持鼠标滚轮和双指缩放
- start: 0,
- end: 100
- }
- ]
- }
- myChart.setOption(option)
- }
- // 开关
- const switchButton = async(device, relay) => {
- console.log(device, relay)
- await deviceSwitch({
- tunnelSn: device.tunnel.tunnelSn,
- radarId: device.radarId,
- relayId: relay.relayId,
- state: relay.state
- }).then(res => {
- if (res.code === 0) {
- ElMessage.success('已切换')
- }
- getData()
- })
- }
- // 大屏
- const jumpScreen = () => {
- router.push('/dataDashboard')
- // const routeData = router.resolve({ name: 'dataDashboard' })
- // window.open(routeData.href, '_blank')
- }
- onMounted(() => {
- getData()
- })
- </script>
|