| | |
| | | <img src="../assets/images/ic_zongguige@2x.png" /> |
| | | <div class="left_box_one_item_info"> |
| | | <span>总柜格</span> |
| | | <span>24</span> |
| | | <span v-if="data">{{data.gridNum}}</span> |
| | | </div> |
| | | </div> |
| | | <div class="left_box_one_item"> |
| | | <img src="../assets/images/ic_zaiwei@2x.png" /> |
| | | <div class="left_box_one_item_info"> |
| | | <span>在位</span> |
| | | <span>24</span> |
| | | <span v-if="data">{{data.onlineKeyNum}}</span> |
| | | </div> |
| | | </div> |
| | | <div class="left_box_one_item"> |
| | | <img src="../assets/images/ic_lingyong@2x.png" /> |
| | | <div class="left_box_one_item_info"> |
| | | <span>领用</span> |
| | | <span>24</span> |
| | | <span v-if="data">{{data.outKeyNum}}</span> |
| | | </div> |
| | | </div> |
| | | <div class="left_box_one_item"> |
| | | <img src="../assets/images/ic_kongxian@2x.png" /> |
| | | <div class="left_box_one_item_info"> |
| | | <span>空闲</span> |
| | | <span>24</span> |
| | | <span v-if="data">{{data.unBindGridNum}}</span> |
| | | </div> |
| | | </div> |
| | | <div class="left_box_one_item"> |
| | | <img src="../assets/images/ic_weibao@2x.png" /> |
| | | <div class="left_box_one_item_info"> |
| | | <span>维保</span> |
| | | <span>24</span> |
| | | <span v-if="data">{{data.serviceKeyNum}}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | <div>派车统计</div> |
| | | </div> |
| | | <div class="tabs"> |
| | | <div class="tab active">近7天</div> |
| | | <div class="tab" :class="type1 === 0 ? 'active' : ''" @click="clickOneItem(0)">近7天</div> |
| | | <div class="separate"> | </div> |
| | | <div class="tab">近15天</div> |
| | | <div class="tab" :class="type1 === 1 ? 'active' : ''" @click="clickOneItem(1)">近15天</div> |
| | | </div> |
| | | <img src="@/assets/images/task/title@2x.png" class="bg" alt="" /> |
| | | </div> |
| | |
| | | <div>钥匙领取归还统计</div> |
| | | </div> |
| | | <div class="tabs"> |
| | | <div class="tab active">近7天</div> |
| | | <div class="tab" :class="type2 === 0 ? 'active' : ''" @click="clickTwoItem(0)">近7天</div> |
| | | <div class="separate"> | </div> |
| | | <div class="tab">近15天</div> |
| | | <div class="tab" :class="type2 === 1 ? 'active' : ''" @click="clickTwoItem(1)">近15天</div> |
| | | </div> |
| | | <img src="@/assets/images/task/title@2x.png" class="bg" alt="" /> |
| | | </div> |
| | |
| | | <img src="@/assets/images/ic_title@2x.png" class="icon" alt="" /> |
| | | <div>钥匙取还记录</div> |
| | | </div> |
| | | <div class="search"> |
| | | <div class="search-item" @click.stop="show = true"> |
| | | <div class="search-item-label">状态:</div> |
| | | <div class="search-item-select"> |
| | | <span v-if="form.status === ''">全部</span> |
| | | <span v-if="form.status === 0">未归还</span> |
| | | <span v-if="form.status === 1">已归还</span> |
| | | <span>▼</span> |
| | | <div class="search-item-select-list" v-if="show"> |
| | | <div class="search-item-select-list-item" @click.stop="sele('')">全部</div> |
| | | <div class="search-item-select-list-item" @click.stop="sele(0)">未归还</div> |
| | | <div class="search-item-select-list-item" @click.stop="sele(1)">已归还</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="search-item"> |
| | | <div class="search-item-input"> |
| | | <img src="@/assets/images/ic_search@2x.png" /> |
| | | <input v-model="form.carCode" type="text" @keypress="Enter" placeholder="搜索车牌号" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <img src="@/assets/images/task/title@2x.png" class="bg" alt="" /> |
| | | </div> |
| | | <div class="list_wrap"> |
| | | <div class="header line"> |
| | | <span class="item">合同号</span> |
| | | <span class="item status">订单状态</span> |
| | | <span class="item">目的地</span> |
| | | <span class="item">任务下达时间</span> |
| | | <span class="item">车牌号</span> |
| | | <span class="item">领取人</span> |
| | | <span class="item">归还时间</span> |
| | | <span class="item">状态</span> |
| | | <span class="item">备注</span> |
| | | </div> |
| | | <div class="one-swiper list"> |
| | | <div class="swiper-wrapper"> |
| | | <div class="line one-swiper-slide swiper-slide item" v-for="item in 8" :key="item"> |
| | | <span class="item">contractNumber</span> |
| | | <span class="item status">statusDesc</span> |
| | | <span class="item">receiveEnterprise</span> |
| | | <span class="item">createDate</span> |
| | | <div class="line one-swiper-slide swiper-slide item" v-for="(item, i) in data5" :key="i"> |
| | | <span class="item">{{item.carCode}}</span> |
| | | <span class="item">{{item.memberName}}</span> |
| | | <span class="item">{{item.returnDate || '-'}}</span> |
| | | <span class="item"> |
| | | <span v-if="item.status === 0" style="color: #FEAF01;">未归还</span> |
| | | <span v-else-if="item.status === 1" style="color: #28F0C4;">已归还</span> |
| | | <span v-else>-</span> |
| | | </span> |
| | | <span class="item">{{item.remark || '-'}}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | <span>钥匙告警分布</span> |
| | | </div> |
| | | <div class="row-title-right"> |
| | | <span class="ac">今日</span> |
| | | <span :class="type3 === 0 ? 'ac' : ''" @click="clickThreeItem(0)">今日</span> |
| | | | |
| | | <span>本月</span> |
| | | <span :class="type3 === 1 ? 'ac' : ''" @click="clickThreeItem(1)">本月</span> |
| | | | |
| | | <span>本年</span> |
| | | <span :class="type3 === 2 ? 'ac' : ''" @click="clickThreeItem(2)">本年</span> |
| | | </div> |
| | | </div> |
| | | <div class="car_static"> |
| | | <div class="echart_wrap"> |
| | | <div class="pie_text"> |
| | | <div class="fs30"><strong>30</strong></div> |
| | | <div>入库总量</div> |
| | | <div style="font-size: 15px; color: rgba(255,255,255,0.8); font-weight: 400;">入库总量</div> |
| | | </div> |
| | | <div class="echart1" id="echart1"></div> |
| | | </div> |
| | | <div class="list"> |
| | | <div class="list" v-if="fenbu"> |
| | | <div class="item1"> |
| | | <div class="line"> |
| | | <div class="icon"></div> |
| | | <div class="icon" :style="{ background: `linear-gradient(270deg, ${colors[0].color1} 0%, ${colors[0].color2} 100%)` }"></div> |
| | | <div class="text">酒精检测告警</div> |
| | | </div> |
| | | <div class="num">16次|55.0%</div> |
| | | <div class="num">{{fenbu.alcoholNum}}次|{{fenbu.alcoholNumPct}}%</div> |
| | | </div> |
| | | <div class="item1"> |
| | | <div class="line"> |
| | | <div class="icon"></div> |
| | | <div class="icon" :style="{ background: `linear-gradient(270deg, ${colors[1].color1} 0%, ${colors[1].color2} 100%)` }"></div> |
| | | <div class="text">柜门未关告警</div> |
| | | </div> |
| | | <div class="num">12次|40.0%</div> |
| | | <div class="num">{{fenbu.timeOutNum}}次|{{fenbu.timeOutNumPct}}%</div> |
| | | </div> |
| | | <div class="item1"> |
| | | <div class="line"> |
| | | <div class="icon"></div> |
| | | <div class="icon" :style="{ background: `linear-gradient(270deg, ${colors[2].color1} 0%, ${colors[2].color2} 100%)` }"></div> |
| | | <div class="text">超时未还告警</div> |
| | | </div> |
| | | <div class="num">2次|5.0%</div> |
| | | <div class="num">{{fenbu.unCloseNum}}次|{{fenbu.unCloseNumPct}}%</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | </div> |
| | | <div class="list1 three-swiper"> |
| | | <div class="swiper-wrapper"> |
| | | <div class="item three-swiper-slide swiper-slide" v-for="(item, i) in alarm"> |
| | | <div class="item three-swiper-slide swiper-slide" v-for="(item, i) in alarm" :key="i"> |
| | | <div class="icon"> |
| | | <div class="circle"></div> |
| | | <div class="line"></div> |
| | | </div> |
| | | <div class="content"> |
| | | <div class="header"> |
| | | <div class="time">07月07日 14:23:22</div> |
| | | <div class="time">操作人:唐嫣李</div> |
| | | <div class="time">{{item.createDate}}</div> |
| | | <div class="time">操作人:{{item.createUserName}}</div> |
| | | </div> |
| | | <div class="main"> |
| | | <div class="left"> |
| | | <div class="title"> |
| | | <img class="xf" src="@/assets/images/FireFighting/ic_gaojing_red@2x.png" alt=""> |
| | | <span>酒精检测超标</span> |
| | | <div class="data"> |
| | | <img src="@/assets/images/ic_guihao@2x.png" /> |
| | | <span>柜号:19</span> |
| | | </div> |
| | | <span>{{item.content}}</span> |
| | | <!-- <div class="data">--> |
| | | <!-- <img src="@/assets/images/ic_guihao@2x.png" />--> |
| | | <!-- <span>柜号:19</span>--> |
| | | <!-- </div>--> |
| | | </div> |
| | | </div> |
| | | <!-- <div class="status">处理中</div> --> |
| | |
| | | |
| | | <script setup> |
| | | import VScaleScreen from 'v-scale-screen' |
| | | import {onMounted, ref} from 'vue' |
| | | import {onMounted, ref, nextTick} from 'vue' |
| | | import dayjs from 'dayjs' |
| | | import * as echarts from 'echarts' |
| | | import duration from 'dayjs/plugin/duration' |
| | | import 'swiper/css/swiper.min.css' |
| | | import Swiper from 'swiper' |
| | | import {zxenergyDataList, zxloadCurve} from '@/api' |
| | | import { |
| | | getCabinetGridData, |
| | | getCarUseStatistics, |
| | | getKeyUseStatistics, |
| | | getCabinetBoardWarningPage, |
| | | getWarningRataData, |
| | | getKeyUseRecord |
| | | } from '@/api' |
| | | |
| | | dayjs.extend(duration) |
| | | |
| | |
| | | |
| | | }, 1000) |
| | | |
| | | const colors = [ |
| | | { |
| | | color1: '#007EC3', |
| | | color2: '#53E3FF' |
| | | }, |
| | | { |
| | | color1: '#DC9804', |
| | | color2: '#FFD802' |
| | | }, |
| | | { |
| | | color1: '#14A585', |
| | | color2: '#51F9E4' |
| | | } |
| | | ] |
| | | const data = ref(null) |
| | | const data4 = ref([]) |
| | | let type1 = ref(0) |
| | | const data2 = ref([]) |
| | | const alarm = ref([{},{},{},{},{},{},{},{}]) |
| | | let type2 = ref(0) |
| | | let type3 = ref(0) |
| | | const alarm = ref([]) |
| | | let fenbu = ref(null) |
| | | const data5 = ref([]) |
| | | const dataList5 = ref([]) |
| | | let form = ref({ |
| | | status: '', |
| | | carCode: '' |
| | | }) |
| | | let show = ref(false) |
| | | const claimRecord = ref([{},{},{},{},{},{},{},{}]) |
| | | |
| | | const Enter = (e) => { |
| | | if (e.key === 'Enter') { |
| | | getKeyUseRecords() |
| | | } |
| | | } |
| | | |
| | | const sele = (val) => { |
| | | form.value.status = val |
| | | show.value = false |
| | | getKeyUseRecords() |
| | | } |
| | | |
| | | // 钥匙取还记录 |
| | | const getKeyUseRecords = () => { |
| | | getKeyUseRecord({ carCode: form.value.carCode, status: form.value.status }) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | data5.value = res.data |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // 计算百分比 |
| | | const calculatePercentage = (target, numbers) => { |
| | | const total = numbers.reduce((sum, num) => sum + num, 0); |
| | | if (total === 0) return 0; |
| | | return ((target / total) * 100).toFixed(2); |
| | | } |
| | | |
| | | // 钥匙告警分布 |
| | | const getWarningRataDatas = () => { |
| | | getWarningRataData({ |
| | | type: type3.value |
| | | }).then(res => { |
| | | if (res.code === 200) { |
| | | fenbu.value = { |
| | | ...res.data, |
| | | alcoholNumPct: calculatePercentage(res.data.alcoholNum,[res.data.alcoholNum + res.data.timeOutNum + res.data.unCloseNum]), |
| | | timeOutNumPct: calculatePercentage(res.data.timeOutNum,[res.data.alcoholNum + res.data.timeOutNum + res.data.unCloseNum]), |
| | | unCloseNumPct: calculatePercentage(res.data.unCloseNum,[res.data.alcoholNum + res.data.timeOutNum + res.data.unCloseNum]) |
| | | } |
| | | dataList5.value = [res.data.alcoholNum, res.data.unCloseNum, res.data.timeOutNum] |
| | | nextTick(() => { |
| | | initEchart1() |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // 告警明细 |
| | | const getCabinetBoardWarningPages = () => { |
| | | getCabinetBoardWarningPage({ |
| | | capacity: 8, |
| | | page: 1, |
| | | model: {} |
| | | }).then(res => { |
| | | if (res.code === 200) { |
| | | alarm.value = res.data.records |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // 顶部数据 |
| | | const getZXData = () => { |
| | | getCabinetGridData() |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | data.value = res.data |
| | | } |
| | | }) |
| | | } |
| | | |
| | | const clickOneItem = (type) => { |
| | | type1.value = type |
| | | getData4() |
| | | } |
| | | |
| | | const clickTwoItem = (type) => { |
| | | type2.value = type |
| | | getData2() |
| | | } |
| | | |
| | | const clickThreeItem = (type) => { |
| | | type3.value = type |
| | | getWarningRataDatas() |
| | | } |
| | | |
| | | // 告警分布 |
| | | const initEchart1 = () => { |
| | | const myChart = echarts.init(document.getElementById('echart1')) |
| | | const option = { |
| | | series: [ |
| | | { |
| | | type: 'pie', |
| | | radius: ['86%', '100%'], |
| | | label: { |
| | | show: false, |
| | | position: 'center' |
| | | }, |
| | | tooltip: { |
| | | trigger: 'none' |
| | | }, |
| | | emphasis: { |
| | | // 设置悬浮时样式为空 |
| | | scale: false, // 关闭放大效果 |
| | | itemStyle: { |
| | | shadowBlur: 0, // 阴影模糊度为 0 |
| | | shadowOffsetX: 0, |
| | | shadowColor: 'rgba(0, 0, 0, 0)', |
| | | }, |
| | | }, |
| | | padAngle: 3, |
| | | itemStyle: { |
| | | borderRadius: 10, |
| | | color: function(params) { |
| | | // 使用不同的渐变色 |
| | | let colorList = [ |
| | | {offset: 0, color: colors[params.dataIndex].color1}, // 0% 处的颜色 |
| | | {offset: 1, color: colors[params.dataIndex].color2} // 100% 处的颜色 |
| | | ]; |
| | | return new echarts.graphic.LinearGradient(0, 0, 0, 1, colorList); |
| | | } |
| | | }, |
| | | // color: colors, |
| | | labelLine: { |
| | | show: false |
| | | }, |
| | | data: dataList5.value |
| | | } |
| | | ] |
| | | } |
| | | myChart.setOption(option) |
| | | window.addEventListener('resize', function () { // 执行 |
| | | myChart.resize() |
| | | }) |
| | | } |
| | | |
| | | // 派车统计 |
| | | const getData4 = () => { |
| | | zxloadCurve().then(res => { |
| | | const result = res.data || [] |
| | | data4.value = result |
| | | getCarUseStatistics({ type: type1.value }).then(res => { |
| | | data4.value = res.data || [] |
| | | if (data4.value && data4.value.length > 0) { |
| | | initLoadReal() |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // 派车统计 |
| | | const initLoadReal = () => { |
| | | // 基于准备好的dom,初始化echarts实例 |
| | | var myChart = echarts.init(document.querySelector('.three_one_chat')) |
| | |
| | | axisLabel: { |
| | | color: '#D2E0FF' |
| | | }, |
| | | data: data4.value.map(i => i.timeData) |
| | | data: data4.value.map(i => i.name) |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | |
| | | }, |
| | | series: [ |
| | | { |
| | | data: data4.value.map(i => i.energy), |
| | | data: data4.value.map(i => i.num), |
| | | type: 'line', |
| | | areaStyle: { |
| | | normal: { |
| | |
| | | }) |
| | | } |
| | | |
| | | // 钥匙领取归还统计 |
| | | const getData2 = () => { |
| | | zxenergyDataList({ type: 0 }).then(res => { |
| | | getKeyUseStatistics({ type: type2.value }).then(res => { |
| | | data2.value = res.data || [] |
| | | initEnergy() |
| | | if (data2.value && data2.value.length > 0) { |
| | | initEnergy() |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // 钥匙领取归还统计 |
| | | const initEnergy = () => { |
| | | var myChart = echarts.init(document.querySelector('.three_two_chat')) |
| | | |
| | |
| | | axisLabel: { |
| | | color: '#D2E0FF' |
| | | }, |
| | | data: data2.value.map(i => dayjs(i.timeData).format('M')) |
| | | data: data2.value.map(i => i.name) |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | |
| | | }, |
| | | series: [ |
| | | { |
| | | data: data2.value.map(i => i.energy), |
| | | data: data2.value.map(i => i.num), |
| | | type: 'bar', |
| | | barWidth: 10, |
| | | itemStyle: { |
| | |
| | | } |
| | | }, |
| | | { |
| | | data: data2.value.map(i => i.energy), |
| | | data: data2.value.map(i => i.nextNum), |
| | | type: 'bar', |
| | | barWidth: 10, |
| | | itemStyle: { |
| | |
| | | initialSlide: 0, |
| | | direction: 'vertical', //竖直方向 |
| | | slidesPerView: 4, |
| | | autoplay: autoplayFlag(claimRecord.value, 4, 6000), |
| | | autoplay: autoplayFlag(data5.value, 4, 6000), |
| | | observer: true, //修改swiper自己或子元素时,自动初始化swiper |
| | | }) |
| | | } |
| | |
| | | } |
| | | |
| | | onMounted(() => { |
| | | // getData1() |
| | | getKeyUseRecords() |
| | | getWarningRataDatas() |
| | | getCabinetBoardWarningPages() |
| | | getZXData() |
| | | getData2() |
| | | // getData3() |
| | | getData4() |
| | | // getData5() |
| | | // getData6() |
| | | // getData7() |
| | | // |
| | | // setInterval(() => { |
| | | // getData1() |
| | | // getData2() |
| | | // getData3() |
| | | // getData4() |
| | | // getData5() |
| | | // getData6() |
| | | // getData7() |
| | | // }, 1000 * 60) |
| | | // |
| | | // setTimeout(() => { |
| | | // loopFn2() |
| | | // loopFn5() |
| | | // loopFn7() |
| | | // loopFn6() |
| | | // }, 12000) |
| | | setInterval(() => { |
| | | getKeyUseRecords() |
| | | getWarningRataDatas() |
| | | getCabinetBoardWarningPages() |
| | | getZXData() |
| | | getData2() |
| | | getData4() |
| | | }, 1000 * 60) |
| | | setTimeout(() => { |
| | | loopFn1() |
| | | loopFn7() |
| | |
| | | position: relative; |
| | | |
| | | .pie_text { |
| | | width: 100px; |
| | | height: 100px; |
| | | border: 1px dashed rgba(1, 217, 254, 0.7); |
| | | width: 150px; |
| | | height: 150px; |
| | | border: 1px dashed #FFFFFF; |
| | | border-radius: 50%; |
| | | position: absolute; |
| | | |
| | | left: 50%; |
| | | top: 50%; |
| | | transform: translate(-50%, -50%); |
| | | left: 25px; |
| | | top: 25px; |
| | | z-index: 999; |
| | | display: flex; |
| | | flex-direction: column; |
| | |
| | | font-size: 12px; |
| | | |
| | | .fs30 { |
| | | font-weight: bold; |
| | | font-size: 18px; |
| | | margin-bottom: 4px; |
| | | font-weight: 500; |
| | | font-size: 32px; |
| | | color: #FFFFFF; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .echart1 { |
| | | width: 130px; |
| | | height: 130px; |
| | | width: 200px; |
| | | height: 200px; |
| | | } |
| | | |
| | | .list { |
| | | width: 260px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | |
| | |
| | | height: 12px; |
| | | border-radius: 50%; |
| | | margin-right: 10px; |
| | | background: linear-gradient(270deg, #29aeff 0%, #207ff7 100%); |
| | | } |
| | | } |
| | | |
| | |
| | | padding: 0 15px 0 13px; |
| | | position: relative; |
| | | |
| | | .search { |
| | | display: flex; |
| | | align-items: center; |
| | | .search-item { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-left: 10px; |
| | | .search-item-label { |
| | | font-weight: 400; |
| | | font-size: 13px; |
| | | color: #D2E0FF; |
| | | } |
| | | .search-item-select { |
| | | width: 100px; |
| | | height: 27px; |
| | | background: rgba(0,148,235,0.1); |
| | | border-radius: 14px; |
| | | border: 1px solid #869CC9; |
| | | padding: 0 10px; |
| | | box-sizing: border-box; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | cursor: pointer; |
| | | position: relative; |
| | | .search-item-select-list { |
| | | width: 100%; |
| | | height: auto; |
| | | position: absolute; |
| | | bottom: -120px; |
| | | left: 0; |
| | | z-index: 9; |
| | | border-radius: 10px; |
| | | overflow: hidden; |
| | | background: rgba(0,86,255,0.05); |
| | | .search-item-select-list-item { |
| | | width: 100%; |
| | | height: 40px; |
| | | line-height: 40px; |
| | | text-align: center; |
| | | font-size: 13px; |
| | | font-weight: 400; |
| | | color: #D2E0FF; |
| | | &:hover { |
| | | background-color: #00A68E; |
| | | } |
| | | } |
| | | } |
| | | span { |
| | | &:nth-child(1) { |
| | | font-weight: 400; |
| | | font-size: 13px; |
| | | color: #D2E0FF; |
| | | } |
| | | &:nth-child(2) { |
| | | font-weight: 400; |
| | | font-size: 10px; |
| | | color: #D2E0FF; |
| | | } |
| | | } |
| | | } |
| | | .search-item-input { |
| | | width: 150px; |
| | | height: 27px; |
| | | background: rgba(0,148,235,0.1); |
| | | border-radius: 14px; |
| | | border: 1px solid #869CC9; |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 0 10px; |
| | | box-sizing: border-box; |
| | | img { |
| | | flex-shrink: 0; |
| | | width: 14px; |
| | | height: 14px; |
| | | margin-right: 6px; |
| | | } |
| | | input { |
| | | flex: 1; |
| | | height: 100%; |
| | | font-weight: 400; |
| | | font-size: 13px; |
| | | color: #D2E0FF; |
| | | &::placeholder { |
| | | font-weight: 400; |
| | | font-size: 13px; |
| | | color: rgba(255,255,255,0.36); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .title { |
| | | display: flex; |
| | | align-items: center; |