|
@@ -0,0 +1,569 @@
|
|
|
+<template>
|
|
|
+ <!-- 第一行:统计卡片行 -->
|
|
|
+ <el-row :gutter="16" class="mb-4">
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-card class="chart-card" shadow="never">
|
|
|
+ <el-form
|
|
|
+ class="-mb-15px"
|
|
|
+ :model="queryParams"
|
|
|
+ ref="queryFormRef"
|
|
|
+ :inline="true"
|
|
|
+ label-width="68px"
|
|
|
+ >
|
|
|
+ <el-form-item label="所属部门" prop="project_name">
|
|
|
+ <el-tree-select
|
|
|
+ v-model="queryParams.deptId"
|
|
|
+ :data="deptList"
|
|
|
+ :props="defaultProps"
|
|
|
+ check-strictly
|
|
|
+ node-key="id"
|
|
|
+ filterable
|
|
|
+ placeholder="请选择所在部门"
|
|
|
+ clearable
|
|
|
+ style="width: 180px"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="创建时间" prop="createTime">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="queryParams.createTime"
|
|
|
+ value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
+ type="daterange"
|
|
|
+ start-placeholder="开始日期"
|
|
|
+ end-placeholder="结束日期"
|
|
|
+ :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
|
|
+ class="!w-220px"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+<!-- <el-form-item label="填写状态" prop="project_name">
|
|
|
+ <el-select
|
|
|
+ v-model="queryParams.status"
|
|
|
+ placeholder="填写状态"
|
|
|
+ clearable
|
|
|
+ class="!w-240px"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="dict in getIntDictOptions(DICT_TYPE.OPERATION_FILL_ORDER_STATUS)"
|
|
|
+ :key="dict.value"
|
|
|
+ :label="dict.label"
|
|
|
+ :value="dict.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>-->
|
|
|
+ <el-form-item>
|
|
|
+ <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
|
|
+ <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <!-- 第二行:图表行 -->
|
|
|
+ <el-row :gutter="16" class="mb-4">
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-card class="chart-card" shadow="never">
|
|
|
+ <template #header>
|
|
|
+ <div class="flex items-center">
|
|
|
+ <span class="text-base font-medium text-gray-600">运行记录填写统计</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <el-row class="h-[220px]">
|
|
|
+ <el-col :span="6" class="flex flex-col items-center">
|
|
|
+ <div ref="reportingChartRef" class="h-[160px] w-full"></div>
|
|
|
+ <div class="text-center mt-2">
|
|
|
+ <span class="text-sm text-gray-600">总数</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6" class="flex flex-col items-center">
|
|
|
+ <div ref="dealFinishedChartRef" class="h-[160px] w-full"></div>
|
|
|
+ <div class="text-center mt-2">
|
|
|
+ <span class="text-sm text-gray-600">已填写</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6" class="flex flex-col items-center">
|
|
|
+ <div ref="transOrderChartRef" class="h-[160px] w-full"></div>
|
|
|
+ <div class="text-center mt-2">
|
|
|
+ <span class="text-sm text-gray-600">未填写</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6" class="flex flex-col items-center">
|
|
|
+ <div ref="writeChartRef" class="h-[160px] w-full"></div>
|
|
|
+ <div class="text-center mt-2">
|
|
|
+ <span class="text-sm text-gray-600">填写中</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 第三行:消息统计行 -->
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-card class="chart-card" shadow="never">
|
|
|
+ <template #header>
|
|
|
+ <div class="flex items-center justify-between">
|
|
|
+ <span class="text-base font-medium text-gray-600">部门数量统计</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <div ref="chartContainer" class="h-[300px]"></div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts" name="Index">
|
|
|
+import * as echarts from 'echarts/core'
|
|
|
+import { BarChart } from 'echarts/charts'; // 显式导入柱状图模块
|
|
|
+
|
|
|
+import {
|
|
|
+ GridComponent,
|
|
|
+ LegendComponent,
|
|
|
+ TitleComponent,
|
|
|
+ ToolboxComponent,
|
|
|
+ TooltipComponent
|
|
|
+} from 'echarts/components'
|
|
|
+import { GaugeChart, LineChart, PieChart } from 'echarts/charts'
|
|
|
+import { LabelLayout, UniversalTransition } from 'echarts/features'
|
|
|
+import { CanvasRenderer } from 'echarts/renderers'
|
|
|
+import {
|
|
|
+ IotStatisticsDeviceMessageSummaryRespVO,
|
|
|
+ IotStatisticsSummaryRespVO
|
|
|
+} from '@/api/iot/statistics'
|
|
|
+import { formatDate } from '@/utils/formatTime'
|
|
|
+import { IotStatApi } from '@/api/pms/stat'
|
|
|
+import { ref, reactive, onMounted, onUnmounted } from "vue";
|
|
|
+import { defaultProps, handleTree } from "@/utils/tree";
|
|
|
+import * as DeptApi from "@/api/system/dept";
|
|
|
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
|
|
+import {useUserStore} from "@/store/modules/user";
|
|
|
+// 导入部门数据类型
|
|
|
+import { DeptTreeItem } from '@/api/system/dept'
|
|
|
+
|
|
|
+// 初始化echarts
|
|
|
+echarts.use([
|
|
|
+ TooltipComponent,
|
|
|
+ LegendComponent,
|
|
|
+ PieChart,
|
|
|
+ CanvasRenderer,
|
|
|
+ LabelLayout,
|
|
|
+ TitleComponent,
|
|
|
+ ToolboxComponent,
|
|
|
+ GridComponent,
|
|
|
+ LineChart,
|
|
|
+ UniversalTransition,
|
|
|
+ GaugeChart,
|
|
|
+ BarChart
|
|
|
+])
|
|
|
+
|
|
|
+const deptList = ref<DeptTreeItem[]>([]) // 树形结构部门列表
|
|
|
+const deptDataList = ref<DeptDataItem[]>([]) // 部门数据列表
|
|
|
+
|
|
|
+// 定义部门数据类型
|
|
|
+interface DeptDataItem {
|
|
|
+ deptId: number;
|
|
|
+ name: string;
|
|
|
+ totalCount: number;
|
|
|
+ filledCount: number;
|
|
|
+ unfilledCount: number;
|
|
|
+ fillingCount: number;
|
|
|
+}
|
|
|
+
|
|
|
+/** IoT 首页 */
|
|
|
+defineOptions({ name: 'iotOpeationSta' })
|
|
|
+
|
|
|
+const timeRange = ref('7d') // 修改默认选择为近一周
|
|
|
+const dateRange = ref<[Date, Date] | null>(null)
|
|
|
+
|
|
|
+const queryParams = reactive({
|
|
|
+ startTime: Date.now() - 7 * 24 * 60 * 60 * 1000, // 设置默认开始时间为 7 天前
|
|
|
+ endTime: Date.now(), // 设置默认结束时间为当前时间
|
|
|
+ deptId: null, // 选中的部门ID
|
|
|
+ status: null // 填写状态
|
|
|
+})
|
|
|
+
|
|
|
+const handleQuery = () => {
|
|
|
+ // 重新获取数据
|
|
|
+ getStats()
|
|
|
+ initChart()
|
|
|
+}
|
|
|
+
|
|
|
+const resetQuery = () => {
|
|
|
+ // 重置查询参数
|
|
|
+ queryParams.startTime = Date.now() - 7 * 24 * 60 * 60 * 1000
|
|
|
+ queryParams.endTime = Date.now()
|
|
|
+ queryParams.deptId = null
|
|
|
+ queryParams.status = null
|
|
|
+
|
|
|
+ // 重新获取数据
|
|
|
+ getStats()
|
|
|
+ initChart()
|
|
|
+}
|
|
|
+
|
|
|
+const reportingChartRef = ref() // 在线设备统计的图表
|
|
|
+const dealFinishedChartRef = ref() // 离线设备统计的图表
|
|
|
+const transOrderChartRef = ref() // 待激活设备统计的图表
|
|
|
+const orderFinishChartRef = ref()
|
|
|
+const deviceMessageCountChartRef = ref() // 上下行消息量统计的图表
|
|
|
+const writeChartRef = ref() // 待填写图表
|
|
|
+const finishedChartRef = ref() // 已完成图表
|
|
|
+
|
|
|
+// 基础统计数据
|
|
|
+const statsData = ref<IotStatisticsSummaryRespVO>({
|
|
|
+ productCategoryCount: 0,
|
|
|
+ productCount: 0,
|
|
|
+ deviceCount: 0,
|
|
|
+ deviceMessageCount: 0,
|
|
|
+ productCategoryTodayCount: 0,
|
|
|
+ productTodayCount: 0,
|
|
|
+ deviceTodayCount: 0,
|
|
|
+ deviceMessageTodayCount: 0,
|
|
|
+ deviceOnlineCount: 0,
|
|
|
+ deviceOfflineCount: 0,
|
|
|
+ deviceInactiveCount: 0,
|
|
|
+ productCategoryDeviceCounts: {}
|
|
|
+})
|
|
|
+
|
|
|
+const day = ref({
|
|
|
+ failureDay: undefined,
|
|
|
+ maintainDay: undefined
|
|
|
+})
|
|
|
+const week = ref({
|
|
|
+ failureWeek: undefined,
|
|
|
+ maintainWeek: undefined
|
|
|
+})
|
|
|
+const month = ref({
|
|
|
+ failureMonth: undefined,
|
|
|
+ maintainMonth: undefined
|
|
|
+})
|
|
|
+const total = ref({
|
|
|
+ failureTotal: undefined,
|
|
|
+ maintainTotal: undefined
|
|
|
+})
|
|
|
+
|
|
|
+const status = ref<IotStatusItem[]>([])
|
|
|
+
|
|
|
+// 定义状态项接口
|
|
|
+interface IotStatusItem {
|
|
|
+ totalCount: number
|
|
|
+ filledCount: number
|
|
|
+ unfilledCount: number
|
|
|
+ fillingCount: number
|
|
|
+}
|
|
|
+
|
|
|
+// 消息统计数据
|
|
|
+const messageStats = ref<IotStatisticsDeviceMessageSummaryRespVO>({
|
|
|
+ upstreamCounts: {},
|
|
|
+ downstreamCounts: {}
|
|
|
+})
|
|
|
+
|
|
|
+/** 处理快捷时间范围选择 */
|
|
|
+const handleTimeRangeChange = (timeRange: string) => {
|
|
|
+ const now = Date.now()
|
|
|
+ let startTime: number
|
|
|
+
|
|
|
+ switch (timeRange) {
|
|
|
+ case '1h':
|
|
|
+ startTime = now - 60 * 60 * 1000
|
|
|
+ break
|
|
|
+ case '24h':
|
|
|
+ startTime = now - 24 * 60 * 60 * 1000
|
|
|
+ break
|
|
|
+ case '7d':
|
|
|
+ startTime = now - 7 * 24 * 60 * 60 * 1000
|
|
|
+ break
|
|
|
+ default:
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清空日期选择器
|
|
|
+ dateRange.value = null
|
|
|
+
|
|
|
+ // 更新查询参数
|
|
|
+ queryParams.startTime = startTime
|
|
|
+ queryParams.endTime = now
|
|
|
+
|
|
|
+ // 重新获取数据
|
|
|
+ getStats()
|
|
|
+ initChart()
|
|
|
+}
|
|
|
+
|
|
|
+/** 处理自定义日期范围选择 */
|
|
|
+const handleDateRangeChange = (value: [Date, Date] | null) => {
|
|
|
+ if (value) {
|
|
|
+ // 清空快捷选项
|
|
|
+ timeRange.value = ''
|
|
|
+
|
|
|
+ // 更新查询参数
|
|
|
+ queryParams.startTime = value[0].getTime()
|
|
|
+ queryParams.endTime = value[1].getTime()
|
|
|
+
|
|
|
+ // 重新获取数据
|
|
|
+ getStats()
|
|
|
+ initChart()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** 获取统计数据 */
|
|
|
+const getStats = async () => {
|
|
|
+ // 获取基础统计数据
|
|
|
+ // 获取部门统计数据
|
|
|
+ IotStatApi.getDeptStatistics(queryParams).then((res) => {
|
|
|
+ deptDataList.value = res.deptCountList || [];
|
|
|
+ status.value = res.totalList;
|
|
|
+ initChart()
|
|
|
+ initCharts()
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+/** 初始化图表 */
|
|
|
+const initCharts = () => {
|
|
|
+ // 使用数组的第一个元素(如果存在)
|
|
|
+ const firstStatus = status.value[0] || {}
|
|
|
+ // 上报中
|
|
|
+ initGaugeChart(
|
|
|
+ reportingChartRef.value,
|
|
|
+ firstStatus.totalCount === undefined ? 0 : firstStatus.totalCount,
|
|
|
+ '#0d9'
|
|
|
+ )
|
|
|
+ // 处理完成
|
|
|
+ initGaugeChart(
|
|
|
+ dealFinishedChartRef.value,
|
|
|
+ firstStatus.filledCount === undefined ? 0 : firstStatus.filledCount,
|
|
|
+ '#f50'
|
|
|
+ )
|
|
|
+ // 转工单
|
|
|
+ initGaugeChart(
|
|
|
+ transOrderChartRef.value,
|
|
|
+ firstStatus.unfilledCount === undefined ? 0 : firstStatus.unfilledCount,
|
|
|
+ '#05b'
|
|
|
+ )
|
|
|
+ // 待填写
|
|
|
+ initGaugeChart(
|
|
|
+ writeChartRef.value,
|
|
|
+ firstStatus.fillingCount === undefined ? 0 : firstStatus.fillingCount,
|
|
|
+ '#05b'
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+/** 初始化仪表盘图表 */
|
|
|
+const initGaugeChart = (el: any, value: number, color: string) => {
|
|
|
+ echarts.init(el).setOption({
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ type: 'gauge',
|
|
|
+ startAngle: 360,
|
|
|
+ endAngle: 0,
|
|
|
+ min: 0,
|
|
|
+ max: statsData.value.deviceCount || 100, // 使用设备总数作为最大值
|
|
|
+ progress: {
|
|
|
+ show: true,
|
|
|
+ width: 12,
|
|
|
+ itemStyle: {
|
|
|
+ color: color
|
|
|
+ }
|
|
|
+ },
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ width: 12,
|
|
|
+ color: [[1, '#E5E7EB']]
|
|
|
+ }
|
|
|
+ },
|
|
|
+ axisTick: { show: false },
|
|
|
+ splitLine: { show: false },
|
|
|
+ axisLabel: { show: false },
|
|
|
+ pointer: { show: false },
|
|
|
+ anchor: { show: false },
|
|
|
+ title: { show: false },
|
|
|
+ detail: {
|
|
|
+ valueAnimation: true,
|
|
|
+ fontSize: 24,
|
|
|
+ fontWeight: 'bold',
|
|
|
+ fontFamily: 'Inter, sans-serif',
|
|
|
+ color: color,
|
|
|
+ offsetCenter: [0, '0'],
|
|
|
+ formatter: (value: number) => {
|
|
|
+ return `${value} `
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data: [{ value: value }]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const chartContainer = ref(null)
|
|
|
+let chartInstance = null
|
|
|
+
|
|
|
+// 初始化部门统计图表
|
|
|
+const initChart = () => {
|
|
|
+ if (!chartContainer.value) return
|
|
|
+
|
|
|
+ // 准备数据
|
|
|
+ const deptNames = deptDataList.value.map(item => item.name)
|
|
|
+ const totalCounts = deptDataList.value.map(item => item.totalCount)
|
|
|
+ const filledCounts = deptDataList.value.map(item => item.filledCount)
|
|
|
+ const unfilledCounts = deptDataList.value.map(item => item.unfilledCount)
|
|
|
+ const fillingCounts = deptDataList.value.map(item => item.fillingCount)
|
|
|
+
|
|
|
+ // ECharts配置
|
|
|
+ const option = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ axisPointer: {
|
|
|
+ type: 'shadow'
|
|
|
+ },
|
|
|
+ formatter: (params) => {
|
|
|
+ let result = `<div class="font-bold">${params[0].axisValue}</div>`
|
|
|
+ params.forEach(param => {
|
|
|
+ result += `<div>${param.marker} ${param.seriesName}: ${param.value}</div>`
|
|
|
+ })
|
|
|
+ return result
|
|
|
+ }
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ data: ['总数', '已填写', '未填写', '填写中'],
|
|
|
+ top: 25
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ left: '3%',
|
|
|
+ right: '4%',
|
|
|
+ bottom: '3%',
|
|
|
+ containLabel: true
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: deptNames,
|
|
|
+ axisLabel: {
|
|
|
+ rotate: 45,
|
|
|
+ margin: 15,
|
|
|
+ fontSize: 10
|
|
|
+ }
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ axisLabel: {
|
|
|
+ formatter: (value) => Math.floor(value).toString()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '总数',
|
|
|
+ type: 'bar',
|
|
|
+ barGap: 0,
|
|
|
+ itemStyle: {
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
+ { offset: 0, color: '#188df0' },
|
|
|
+ { offset: 1, color: '#188df0' }
|
|
|
+ ])
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ focus: 'series'
|
|
|
+ },
|
|
|
+ data: totalCounts
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '已填写',
|
|
|
+ type: 'bar',
|
|
|
+ itemStyle: {
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
+ { offset: 0, color: '#d3a137' },
|
|
|
+ { offset: 1, color: '#d3a137' }
|
|
|
+ ])
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ focus: 'series'
|
|
|
+ },
|
|
|
+ data: filledCounts
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '未填写',
|
|
|
+ type: 'bar',
|
|
|
+ itemStyle: {
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
+ { offset: 0, color: 'green' },
|
|
|
+ { offset: 1, color: 'green' }
|
|
|
+ ])
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ focus: 'series'
|
|
|
+ },
|
|
|
+ data: unfilledCounts
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '填写中',
|
|
|
+ type: 'bar',
|
|
|
+ itemStyle: {
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
+ { offset: 0, color: 'red' },
|
|
|
+ { offset: 1, color: 'red' }
|
|
|
+ ])
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ focus: 'series'
|
|
|
+ },
|
|
|
+ data: fillingCounts
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化图表
|
|
|
+ chartInstance = echarts.init(chartContainer.value)
|
|
|
+ chartInstance.setOption(option)
|
|
|
+
|
|
|
+ // 窗口缩放监听
|
|
|
+ window.addEventListener('resize', handleResize)
|
|
|
+ handleResize()
|
|
|
+}
|
|
|
+
|
|
|
+// 自适应调整
|
|
|
+const handleResize = () => {
|
|
|
+ chartInstance?.resize()
|
|
|
+}
|
|
|
+
|
|
|
+/** 初始化 */
|
|
|
+onMounted(async () => {
|
|
|
+ queryParams.deptId = useUserStore().getUser.deptId;
|
|
|
+ getStats()
|
|
|
+ deptList.value = handleTree(await DeptApi.getSimpleDeptList())
|
|
|
+})
|
|
|
+
|
|
|
+onUnmounted(() => {
|
|
|
+ chartInstance?.dispose()
|
|
|
+ window.removeEventListener('resize', handleResize)
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.chart-card {
|
|
|
+ background-color: white;
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
|
|
|
+ padding: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+// 新增样式,隐藏滚动条但保留功能
|
|
|
+::-webkit-scrollbar {
|
|
|
+ display: none;
|
|
|
+}
|
|
|
+
|
|
|
+:host {
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+// 确保页面铺满屏幕并不出现滚动条
|
|
|
+html, body {
|
|
|
+ height: 100%;
|
|
|
+ margin: 0;
|
|
|
+ padding: 0;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+// 适配图表容器高度
|
|
|
+.el-row {
|
|
|
+ max-height: calc(100vh - 32px); // 减去页面padding
|
|
|
+ overflow: auto;
|
|
|
+ -ms-overflow-style: none; // 隐藏IE滚动条
|
|
|
+ scrollbar-width: none; // 隐藏Firefox滚动条
|
|
|
+}
|
|
|
+</style>
|