Merge branch 'master-dev' into feature-Beta-dev-renpy

This commit is contained in:
orgin 2024-03-11 11:31:30 +08:00
commit ed159cbb0b
15 changed files with 461 additions and 128 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -32,7 +32,9 @@ const sample = {
REMOVE_SAMPLE_DATA: (state, inputFileName) => {
const findIndex = state.sampleList.findIndex(item => item.inputFileName == inputFileName)
state.sampleList.splice(findIndex, 1)
if(-1 !== findIndex) {
state.sampleList.splice(findIndex, 1)
}
},
CLEAR_SAMPLE_DATA: (state) => {

View File

@ -111,7 +111,7 @@
<img src="@/assets/images/abnormalAlarm/email-split.png" alt="" />
</div>
<div class="email-top-content-li-right">
<div class="email-top-content-li-right-title">An Unread Mail</div>
<div class="email-top-content-li-right-title">Unread Mail</div>
<div class="email-top-content-li-right-val" style="color: #ade6ee; font-size: 26px; margin-top: 36px">
{{ emailSpace.unreadMsg || 0 }}
</div>

View File

@ -7,17 +7,17 @@
Detailed-Information
<beta-gamma-detailed-infomation slot="content" :data="spectrumData" />
</pop-over-with-icon>
<pop-over-with-icon placement="bottomLeft">
QC Flags
<beta-gamma-qc-flags slot="content" :data="qcFlags" @click="handleQcFlagClick" />
</pop-over-with-icon>
<custom-select
v-model="spectraType"
:options="SampleType"
@change="changeChartByType"
style="width: 246px"
style="width: 154px"
class="sample-select"
></custom-select>
<pop-over-with-icon placement="right" v-model="qcFlagsVisible">
QC Flags
<beta-gamma-qc-flags slot="content" :data="qcFlags" @click="handleQcFlagClick" />
</pop-over-with-icon>
</div>
<!-- 二级交互栏结束 -->
@ -161,6 +161,8 @@ const SampleType = [
},
]
const sortList = ['Xe131m', 'Xe133', 'Xe133m', 'Xe135']
export default {
components: {
BetaGammaChartContainer,
@ -226,14 +228,24 @@ export default {
statisticsType: StatisticsType['Collection Time'],
currSample: {},
copyXeData: null,
qcFlagsVisible: false,
}
},
created() {
this.$bus.$on('ReAnalyses', this.handleReAnalyse)
},
mounted() {
this.qcFlagsTimer = setTimeout(() => {
this.qcFlagsVisible = true
}, 100)
},
destroyed() {
this.cancelLastRequest()
this.$bus.$off('ReAnalyses', this.handleReAnalyse)
if (this.qcFlagsTimer) {
clearTimeout(this.qcFlagsTimer)
}
},
methods: {
//
@ -440,6 +452,8 @@ export default {
? this.resultDisplay
: XeData
this.sortResultDisplay()
this.$emit('sendInfo', this.resultDisplay, this.spectrumData.stationCode, savedAnalysisResult)
this.qcFlags = {
@ -623,6 +637,15 @@ export default {
this.$refs.lineChart4Ref.setRange(minX, maxX)
}
},
// result display
sortResultDisplay() {
this.resultDisplay.sort((a, b) => {
const index1 = sortList.indexOf(a.nuclideName)
const index2 = sortList.indexOf(b.nuclideName)
return index1 - index2
})
},
},
watch: {
sample: {
@ -653,7 +676,7 @@ export default {
handler(newVal, oldVal) {
// this.currResultDisplay = newVal.XeData
this.resultDisplay = newVal.XeData || []
this.sortResultDisplay()
this.$store.commit('UPDATE_SAMPLE_DATA', {
inputFileName: this.sample.inputFileName,
key: 'XeData',
@ -689,6 +712,31 @@ export default {
}
}
//
.spectrum-analysis-sub-operators {
flex-shrink: 0;
margin-bottom: 19px;
display: flex;
gap: 11px;
flex-wrap: nowrap;
overflow: auto;
height: 46px;
align-items: center;
.pop-over-with-icon {
height: 32px;
&:nth-child(1) {
width: 224px;
}
&:nth-child(3) {
width: 125px;
}
}
}
//
.sample-select {
::v-deep {
.ant-select-selection {
@ -699,7 +747,7 @@ export default {
}
&-main {
height: calc(100% - 51px);
height: calc(100% - 65px);
display: flex;
gap: 30px;
overflow: auto hidden;

View File

@ -46,8 +46,6 @@
</template>
<script>
import { cloneDeep } from 'lodash'
const columns = [
{
title: 'Flag',
@ -96,8 +94,6 @@ const columns = [
width: 34,
},
]
const sortList = ['Xe131m', 'Xe133', 'Xe133m', 'Xe135']
export default {
props: {
data: {
@ -125,8 +121,7 @@ export default {
data: {
handler(val) {
if (val && Array.isArray(val)) {
const list = cloneDeep(val)
list.forEach((item) => {
val.forEach((item) => {
if (item.conc < 0) {
item.className = 'error'
} else if (item.conc > 0 && item.conc < item.mdc) {
@ -135,15 +130,8 @@ export default {
item.className = 'success'
}
})
list.sort((a, b) => {
const index1 = sortList.indexOf(a.nuclideName)
const index2 = sortList.indexOf(b.nuclideName)
return index1 - index2
})
this.source1 = list.slice(0, 2)
this.source2 = list.slice(2, 4)
this.source1 = val.slice(0, 2)
this.source2 = val.slice(2, 4)
}
},
immediate: true,

View File

@ -57,20 +57,23 @@ export default {
<style lang="less" scoped>
.qc-flags {
display: flex;
max-width: calc(100vw - 685px);
overflow: auto;
&-item {
background-color: #46738e;
display: flex;
align-items: center;
width: 150px;
height: 30px;
cursor: pointer;
padding: 0 10px;
flex-shrink: 0;
&:not(:last-child) {
margin-right: 2px;
}
span {
margin-left: 20px;
margin-right: 5px;
width: 14px;
height: 14px;

View File

@ -1,14 +1,21 @@
<template>
<a-popover :placement="placement" overlayClassName="popover-with-icon" v-model="innerVisible">
<a-popover
v-bind="$attrs"
:placement="placement"
overlayClassName="popover-with-icon"
:visible="innerVisible"
:getPopupContainer="(ele) => ele"
@click="handleClick"
>
<div class="pop-over-with-icon">
<span class="text">
<slot />
</span>
<img src="@/assets/images/global/select-down.png" alt="" />
</div>
<template slot="content">
<div slot="content" @click.stop>
<slot name="content" />
</template>
</div>
</a-popover>
</template>
<script>
@ -16,27 +23,32 @@ export default {
props: {
placement: {
type: String,
default: 'bottom'
default: 'bottom',
},
value: {
type: Boolean
}
type: Boolean,
},
},
data() {
return {
innerVisible: false
innerVisible: false,
}
},
methods: {
handleClick() {
this.innerVisible = !this.innerVisible
},
},
watch: {
value: {
handler(val) {
this.innerVisible = val
},
immediate: true
immediate: true,
},
innerVisible(val) {
this.$emit('input', val)
}
}
},
},
}
</script>

View File

@ -15,31 +15,34 @@ export default {
props: {
data: {
type: Array,
default: () => []
}
default: () => [],
},
},
created() {
this.items = items
}
},
}
</script>
<style lang="less" scoped>
.qc-flags {
display: flex;
max-width: calc(100vw - 1120px);
overflow: auto;
&-item {
background-color: #46738e;
display: flex;
align-items: center;
width: 150px;
height: 30px;
padding: 0 10px;
flex-shrink: 0;
&:not(:last-child) {
margin-right: 2px;
}
span {
margin-left: 20px;
margin-right: 5px;
width: 14px;
height: 14px;
@ -58,7 +61,7 @@ export default {
}
&.BLUE {
background: radial-gradient(circle, #00E57D 0, #00E57D 100%);
background: radial-gradient(circle, #00e57d 0, #00e57d 100%);
}
&.YELLOW {

View File

@ -7,10 +7,6 @@
Detailed-Information
<detailed-infomation slot="content" :data="detailedInfomation" />
</pop-over-with-icon>
<pop-over-with-icon placement="bottomLeft">
QC Flags
<qc-flags slot="content" :data="qcFlags" />
</pop-over-with-icon>
<pop-over-with-icon>
Graph Assistance
<graph-assistance
@ -20,24 +16,20 @@
@reset="handleResetChart"
/>
</pop-over-with-icon>
<a-popover
overlayClassName="popover-with-icon"
:visible="nuclideLibraryVisible"
@click="handleChangeNuclideVisible"
placement="bottom"
>
<div class="pop-over-with-icon">
<span class="text"> Nuclide Library </span>
<img src="@/assets/images/global/select-down.png" alt="" />
</div>
<pop-over-with-icon v-model="subOperatorsState.nuclideLibraryVisible">
Nuclide Library
<a-spin slot="content" :spinning="isLoadingNuclide">
<nuclide-library :list="nuclideLibraryList" @dblclick="handleNuclideDblClick" />
</a-spin>
</a-popover>
</pop-over-with-icon>
<div class="peak-info">
<button-with-switch-icon @change="handlePeakInfoChange" @click="handleTogglePeak"></button-with-switch-icon>
</div>
<pop-over-with-icon placement="right" v-model="subOperatorsState.qcFlagsVisible">
QC Flags
<qc-flags slot="content" :data="qcFlags" />
</pop-over-with-icon>
</div>
<!-- 二级交互栏结束 -->
<!-- 主体部分 -->
@ -184,7 +176,12 @@ export default {
detailedInfomation: [],
qcFlags: [],
graphAssistance: cloneDeep(graphAssistance),
nuclideLibraryVisible: false,
//
subOperatorsState: {
nuclideLibraryVisible: false,
qcFlagsVisible: false,
},
channelData: {
peakGroup: [],
@ -263,13 +260,22 @@ export default {
window.removeEventListener('keydown', this.handleKeyboardEvent)
window.removeEventListener('click', this.closePeakInfomationTooltip)
if (this.qcFlagsTimer) {
clearTimeout(this.qcFlagsTimer)
}
},
deactivated() {
this.nuclideLibraryVisible = false
// Object.keys(this.subOperatorsState).forEach(k => {
// this.subOperatorsState[k] = false
// })
},
mounted() {
this.option.brush = { toolbox: [] }
this.initWebSocket()
this.qcFlagsTimer = setTimeout(() => {
this.subOperatorsState.qcFlagsVisible = true
}, 100)
},
methods: {
//
@ -519,7 +525,10 @@ export default {
}
} catch (error) {
console.error(error)
this.isLoading = false
const isCancel = axios.isCancel(error)
if(!isCancel) {
this.isLoading = false
}
}
},
@ -555,6 +564,7 @@ export default {
cancelLastRequest() {
if (this._cancelToken && typeof this._cancelToken == 'function') {
this._cancelToken()
this._cancelToken = undefined
}
},
@ -900,7 +910,7 @@ export default {
changeSeriesType() {},
handleChangeNuclideVisible() {
this.nuclideLibraryVisible = !this.nuclideLibraryVisible
this.subOperatorsState.nuclideLibraryVisible = !this.subOperatorsState.nuclideLibraryVisible
},
// seriesName线
@ -1024,7 +1034,7 @@ export default {
sampleId,
channel,
fileName,
energy
energy,
})
if (success) {
const { possible } = result
@ -1042,7 +1052,7 @@ export default {
// Nuclide Library
handleNuclideDblClick(nuclide) {
this.nuclideLibraryVisible = false
this.subOperatorsState.nuclideLibraryVisible = false
this.nuclideReview.nuclide = nuclide
this.nuclideReview.visible = true
},
@ -1802,6 +1812,7 @@ export default {
return success
} else {
this.isLoading = false
this.reprocessingModalVisible = false
if (showMessage) {
const arr = message.split('\n')
this.$warning({
@ -2051,6 +2062,8 @@ export default {
this.handleResetState()
const sampleData = await this.$store.dispatch('GET_SAMPLE_DATA', newVal.inputFileName)
if (sampleData) {
this.cancelLastRequest()
this.isLoading = false
this.dataProcess(sampleData.data, sampleData.from)
} else {
if (newVal.sampleId) {
@ -2102,8 +2115,44 @@ export default {
}
}
//
.spectrum-analysis-sub-operators {
flex-shrink: 0;
margin-bottom: 19px;
display: flex;
gap: 11px;
flex-wrap: nowrap;
overflow: auto;
height: 46px;
align-items: center;
.pop-over-with-icon {
height: 32px;
&:nth-child(1) {
width: 216px;
}
&:nth-child(2) {
width: 182px;
}
&:nth-child(3) {
width: 170px;
}
&:nth-child(5) {
width: 156px;
}
}
.peak-info {
width: 226px;
height: 32px;
display: inline-block;
}
}
//
&-main {
height: calc(100% - 51px);
height: calc(100% - 65px);
display: flex;
overflow: auto hidden;
position: relative;

View File

@ -1776,49 +1776,10 @@ export default {
}
//
::v-deep {
//
.spectrum-analysis-sub-operators {
flex-shrink: 0;
margin-bottom: 19px;
display: flex;
flex-wrap: nowrap;
overflow: auto;
.pop-over-with-icon {
height: 32px;
&:not(:last-child) {
margin-right: 11px;
}
&:nth-child(1) {
width: 256px;
}
&:nth-child(2) {
width: 186px;
}
&:nth-child(3) {
width: 246px;
}
&:nth-child(4) {
width: 246px;
}
}
.peak-info {
width: 306px;
height: 32px;
display: inline-block;
}
}
//
}
//
&-main {
margin-top: 15px;
height: calc(100% - 45px);
padding-top: 5px;
height: calc(100% - 15px);
overflow: hidden;
}
//

View File

@ -11,6 +11,14 @@ import Map from 'ol/Map'
import XYZ from 'ol/source/XYZ'
import View from 'ol/View'
import { fromLonLat } from 'ol/proj'
import Stroke from 'ol/style/Stroke'
import { MarkerIcon, MarkerType } from './markerEnum'
import Icon from 'ol/style/Icon'
import VectorSource from 'ol/source/Vector'
import VectorLayer from 'ol/layer/Vector'
import { LineString, Point } from 'ol/geom'
import { Feature } from 'ol'
import Style from 'ol/style/Style'
const mapSourceUrl = process.env.VUE_APP_MAP_BASE_URL
export default {
@ -57,21 +65,29 @@ export default {
}),
})
const layers = [this.tileLayer]
this.animationSource = new VectorSource({
features: [],
})
const view = new View({
this.animationLayer = new VectorLayer({
source: this.animationSource,
})
const layers = [this.tileLayer, this.animationLayer]
this.view = new View({
projection: 'EPSG:3857', // 使
center: fromLonLat([longitude, latitude]),
zoom: this.zoom,
maxZoom: this.maxZoom,
minZoom: this.minZoom,
extent: [-20037508.34 - 3500000, -20037508.34 , 20037508.34 + 3500000, 20037508.34],
extent: [-20037508.34 - 3500000, -20037508.34, 20037508.34 + 3500000, 20037508.34],
})
this.map = new Map({
target: this.$refs.mapContainerRef,
layers,
view,
view: this.view,
controls: [],
})
},
@ -120,6 +136,110 @@ export default {
})
)
},
// 线
animateByRoute(coordinates) {
if (!coordinates) {
return
}
if (!coordinates.length) {
this.stopRouteAnimation()
this.removeAnimationFeatures()
return
}
this.startRouteAnimation(
coordinates,
{
stroke: new Stroke({
color: '#17f85c',
lineDash: [6],
width: 2,
}),
},
{
image: new Icon({
src: MarkerIcon[MarkerType.Car],
anchor: [0.65, 0.5],
}),
},
[100, 430, 100, 450]
)
},
/**
* 沿轨迹运动
* @param {Array<any>} route 轨迹路线
* @param {Object} marker 运动的物体
*/
startRouteAnimation(route, lineStyleOptions, markerStyleOptions, padding = []) {
//
this.stopRouteAnimation()
//
this.removeAnimationFeatures()
const lineString = new LineString(route.map((item) => fromLonLat(item)))
const extent = lineString.getExtent()
this.view.fit(extent, {
padding,
duration: 1000,
})
// 线
const routeFeature = new Feature({
geometry: lineString,
})
// 线
routeFeature.setStyle(new Style(lineStyleOptions))
this.animationSource.addFeature(routeFeature)
// 线
const markerFeature = new Feature({
geometry: new Point(lineString.getFirstCoordinate()),
})
//
markerFeature.setStyle(new Style(markerStyleOptions))
this.animationSource.addFeature(markerFeature)
let lastTime = Date.now(),
distance = 0
this.postRenderCallback = (event) => {
const speed = 60
const time = event.frameState.time
const elapsedTime = time - lastTime
distance = (distance + (speed * elapsedTime) / 1e6) % 2
lastTime = time
if (distance >= 1) {
this.stopRouteAnimation()
return
}
const currentCoordinate = lineString.getCoordinateAt(distance > 1 ? 2 - distance : distance)
markerFeature.setGeometry(new Point(currentCoordinate))
// tell OpenLayers to continue the postrender animation
this.map.render()
}
this.animationLayer.on('postrender', this.postRenderCallback)
},
//
removeAnimationFeatures() {
this.animationSource.clear()
},
//
stopRouteAnimation() {
if (this.postRenderCallback) {
this.animationLayer.un('postrender', this.postRenderCallback)
this.postRenderCallback = null
}
},
},
}
</script>

View File

@ -32,6 +32,14 @@
/>
<img v-else src="@/assets/images/station-operation/filter-station.png" @click="onPaneChange(2)" />
</div>
<div title="Route">
<img
v-if="active == 3 && showPane"
src="@/assets/images/station-operation/icon-route-active.png"
@click="showPane = !showPane"
/>
<img v-else src="@/assets/images/station-operation/icon-route.png" @click="onPaneChange(3)" />
</div>
</div>
<div class="map-pane-operators-zoom">
@ -149,6 +157,41 @@
</div>
</div>
<!-- 站点筛选结束 -->
<!-- 路径 -->
<div class="route" v-show="active == 3">
<div class="map-pane-content-header">Route</div>
<div class="map-pane-content-main">
<div class="route-form-item">
<div class="label">Station</div>
<custom-select v-model="routeParams.stationCode" :options="stationSelectOptions"></custom-select>
</div>
<div class="route-form-item">
<div class="label">Start Date</div>
<custom-date-picker
show-time
format="YYYY/MM/DD HH:mm:ss"
valueFormat="YYYY/MM/DD HH:mm:ss"
v-model="routeParams.startDate"
></custom-date-picker>
</div>
<div class="route-form-item">
<div class="label">End Date</div>
<custom-date-picker
show-time
format="YYYY/MM/DD HH:mm:ss"
valueFormat="YYYY/MM/DD HH:mm:ss"
v-model="routeParams.endDate"
></custom-date-picker>
</div>
<div class="route-form-item">
<a-button class="btn" type="primary" :loading="isSearchingRoute" @click="handleRouteSearch"
>Search</a-button
>
</div>
</div>
</div>
<!-- 路径结束 -->
</div>
<!-- 主体部分结束 -->
@ -274,6 +317,7 @@ import { MarkerType, FilterIcon } from './markerEnum'
import { Vector as VectorLayer } from 'ol/layer'
import VectorSource from 'ol/source/Vector'
import { cloneDeep } from 'lodash'
import dayjs from 'dayjs'
// Filter
const filterList = [
@ -393,10 +437,13 @@ export default {
type: Number,
default: 500,
},
treeData: {
type: Array,
},
originalDataList: {
type: Array,
default: () => [],
},
},
components: {
CustomModal,
@ -445,6 +492,14 @@ export default {
stationInfo: undefined,
mapSource: 'online',
routeParams: {
stationCode: undefined,
startDate: dayjs().subtract(7, 'd').format('YYYY-MM-DD'),
endDate: dayjs().format('YYYY-MM-DD'),
},
isSearchingRoute: false,
}
},
created() {
@ -492,18 +547,33 @@ export default {
//
onPaneChange(active) {
this.showPane = true
this.prevPane = this.active //
this.active = active
const source = this.circleLayer.getSource()
source.clear() //
switch (active) {
case 1: //
// 使
if (active == 3) {
if (this.lastRoute) {
this.$emit('drawRoute', this.lastRoute)
}
this.$emit('changeMarker', [])
} else {
//
if (this.prevPane == 3) {
this.$emit('drawRoute', [])
}
if (active == 1) {
this.emitDrawCircle(2)
this.emitStationChange()
break
case 2: //
}
if (active == 2) {
this.emitDrawCircle(1)
this.emitFilter()
break
}
}
},
@ -822,6 +892,39 @@ export default {
handleResize() {
this.$refs.realtimeChartRef.resize()
},
//
async handleRouteSearch() {
if (!this.routeParams.stationCode) {
this.$message.warn('Station Code Cannot Be Null')
return
}
this.isSearchingRoute = true
try {
const { success, result, message } = await getAction('/stationOperation/getSelfStationGPS', {
...this.routeParams,
})
if (success) {
if (!result.length) {
this.$message.warn('No Route Found')
return
}
this.lastRoute = result.map(({ lon, lat }) => [lon, lat])
this.$emit('drawRoute', this.lastRoute)
} else {
this.$message.error(message)
}
} catch (e) {
console.error(e)
} finally {
this.isSearchingRoute = false
}
},
},
watch: {
async dataStatusModalVisible(val) {
@ -840,6 +943,16 @@ export default {
}
},
},
computed: {
stationSelectOptions() {
return this.originalDataList
.filter(({ stationType }) => stationType == 'Car')
.map(({ stationName: label }) => ({
label,
value: label,
}))
},
},
}
</script>
<style lang="less" scoped>
@ -868,6 +981,8 @@ export default {
img {
cursor: pointer;
width: 30px;
height: 30px;
}
&-main-operator {
margin-top: 5px;
@ -1057,17 +1172,12 @@ export default {
}
.ant-table-thead > tr th {
padding-top: 2px !important;
padding-bottom: 2px !important;
padding-top: 2px 6px !important;
}
.ant-table-tbody .ant-table-row td {
padding: 6px !important;
}
.ant-table-placeholder {
background-color: transparent;
}
}
}
}
@ -1135,6 +1245,27 @@ export default {
}
}
}
//
.route {
pointer-events: all;
&-form-item {
.label {
margin-bottom: 10px;
}
&:not(:first-child) {
margin-top: 15px;
}
}
.btn {
width: 100%;
margin-top: 50px;
height: 40px;
}
}
}
}
//

View File

@ -152,11 +152,13 @@
<MapPane
ref="mapPane"
:treeData="treeData"
:originalDataList="originalDataList"
@changeMarker="onChangeMarker"
@filterMarker="onFilterMarker"
@drawCircle="onDrawCircle"
@mapSourceChange="handleMapSourceChange"
@focusStationChange="getFollowedStationList"
@drawRoute="handleDrawRoute"
/>
</Map>
</div>
@ -240,6 +242,7 @@ export default {
updataFilterType: [],
updataFilterDataQuality: [],
httpNum: 0,
originalDataList: [],
}
},
created() {
@ -546,8 +549,8 @@ export default {
//
onMarkerClick(stationInfo) {
const { stationType, stationName } = stationInfo
const find = this.orgStationList.find(item => item.stationCode == stationName)
if(!find) {
const find = this.orgStationList.find((item) => item.stationCode == stationName)
if (!find) {
return
}
@ -577,6 +580,11 @@ export default {
getDictSelectTagContainer() {
return document.body
},
//
handleDrawRoute(routes) {
this.$refs.mapRef.animateByRoute(routes)
},
},
}
</script>

View File

@ -10,7 +10,7 @@
</template>
<!-- 标题结束 -->
<!-- 内容 -->
<div class="scheduling-list-content">
<div class="scheduling-list-content" ref="listContainer">
<div class="scheduling-list-item" v-for="item of schedulingInfo" :key="item.id">
<h4 class="title">
<span>
@ -320,6 +320,9 @@ export default {
return
}
this.schedulingInfo = this.scheduleList[date.format(dateFormat)]
this.$nextTick(() => {
this.$refs.listContainer.scrollTop = 0
})
},
//
@ -656,9 +659,12 @@ export default {
height: 100%;
overflow: hidden;
//display: flex;
/deep/.ant-spin-container {
::v-deep.ant-spin-nested-loading {
height: 100%;
display: flex;
.ant-spin-container {
height: 100%;
display: flex;
}
}
&-list {
flex-shrink: 0;
@ -695,13 +701,15 @@ export default {
}
}
&-body {
height: calc(100% - 103px);
overflow: auto;
height: calc(100% - 52px);
// overflow: auto;
}
}
}
&-content {
height: 100%;
padding: 0 12px;
overflow: auto;
}
&-item {
margin-top: 15px;