PanelSjwg.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756
  1. <template>
  2. <div class="panel-sjwg flex flex-col aside-left-inner">
  3. <div class="title-main shrink-0">低空数字地图</div>
  4. <div class="title-sub my-4 shrink-0">
  5. 底板数据
  6. <i @click="toggleContentShow('b1')" class="drop-down" :class="{ reverse: contentShow.b1 }"></i>
  7. </div>
  8. <Transition>
  9. <div v-if="contentShow.b1" v-collapse="'scroll'" class="pr-1" style="flex: 1">
  10. <template v-for="item in basicList">
  11. <div class="title-shade">
  12. <span>{{ item.label }}</span>
  13. <i
  14. v-if="'active' in item"
  15. class="btn-selectall"
  16. :class="{ active: item.active }"
  17. @click="basicCheckAll(item)"></i>
  18. <i class="drop-down" @click="toggleB1Show(item)" :class="{ reverse: item.show }"></i>
  19. </div>
  20. <Transition>
  21. <ul v-if="item.show" v-collapse>
  22. <li v-for="child in item.children" class="list-item" :class="{ disabled: child.disabled }">
  23. <img :src="getAssetsFile(`resources/${child.icon}.png`)" alt="" />
  24. <span>{{ child.label }}</span>
  25. <i title="查看" @click="handleBaseClick(child)" :class="{ active: child.active }"></i>
  26. </li>
  27. </ul>
  28. </Transition>
  29. </template>
  30. </div>
  31. </Transition>
  32. <div class="title-sub my-4 shrink-0">
  33. 低空要素数据<i @click="toggleContentShow('b2')" class="drop-down" :class="{ reverse: contentShow.b2 }"></i>
  34. </div>
  35. <Transition>
  36. <div v-if="contentShow.b2" v-collapse="'scroll'" class="pr-1" style="flex: 1">
  37. <template v-for="item in featureLists">
  38. <div class="title-shade">
  39. <span @click="handleCheckShiFei(item.label)">{{ item.label }}</span>
  40. <i class="btn-selectall" :class="{ active: item.check }" @click="handleCheckAll(item)"></i>
  41. <i class="drop-down" @click="toggleB2Show(item)" :class="{ reverse: item.show }"></i>
  42. </div>
  43. <Transition>
  44. <ul v-if="item.show" v-collapse>
  45. <li v-for="child in item.children" class="list-item">
  46. <img :src="getAssetsFile(`resources/${item.icon}.png`)" alt="" />
  47. <span>{{ child.name }}</span>
  48. <i title="查看" @click="handleCheck(child, item.type)" :class="{ active: child.check }"></i>
  49. <i title="网格" @click="handleMesh(child, item.type)" :class="{ active: child.mesh }"></i>
  50. <el-popconfirm title="确定删除?" @confirm="handleDelete(child, item.type)">
  51. <template #reference>
  52. <i title="删除"></i>
  53. </template>
  54. </el-popconfirm>
  55. </li>
  56. <li v-if="!item.children.length" class="no-data">无数据</li>
  57. </ul>
  58. </Transition>
  59. </template>
  60. </div>
  61. </Transition>
  62. <Transition name="emerge-left">
  63. <div class="feature-legend" v-if="layoutStore.floatPanels.layers_legend">
  64. <div class="title-sub">图例</div>
  65. <ul>
  66. <template v-for="item in featureLists">
  67. <li>
  68. <i v-if="item.color" :style="{ background: item.color }"></i>
  69. <span :class="{ bold: item.colors }">{{ item.label }}</span>
  70. </li>
  71. <template v-if="item.colors">
  72. <div v-for="(childColor, childLabel) in item.colors">
  73. <i :style="{ background: childColor }"></i>
  74. <span>{{ childLabel }}</span>
  75. </div>
  76. </template>
  77. </template>
  78. </ul>
  79. </div>
  80. </Transition>
  81. <!-- 空域点查询 -->
  82. <Transition name="emerge-left">
  83. <FloatPanelAirSpace v-if="layoutStore.floatPanels.air_space" />
  84. </Transition>
  85. </div>
  86. </template>
  87. <script setup>
  88. import shifei from '@/data/shifei.json'
  89. import { DeleteArea, DeletePlot, DeleteRoute, GetAreaList, GetPlotList, GetRouteList } from '@/service/http'
  90. import useLayoutStore from '@/store/layout'
  91. import { useMapStore } from '@/store/map.js'
  92. import usePanelStore from '@/store/panel'
  93. import { AddSingleLayer, heatMap, InspectCube, showShapes, toggleFeaturesClickEvent } from '@/utils/map/addLayer'
  94. import { airSpaceTypes } from '@/utils/options'
  95. import { getAssetsFile, getData } from '@/utils/require'
  96. import { hexToRgb } from '@/utils/tools'
  97. import { renderShapes } from '@/utils/ueActions'
  98. import { onBeforeMount, onBeforeUnmount, reactive, ref, watch } from 'vue'
  99. import FloatPanelAirSpace from './FloatPanelAirSpace.vue'
  100. const panelStore = usePanelStore()
  101. const layoutStore = useLayoutStore()
  102. const mapStore = useMapStore()
  103. let shifeiStatus = false
  104. onBeforeMount(() => {
  105. getLists()
  106. addAreaColors()
  107. if (layoutStore.sceneType === 'ue') {
  108. contentShow.b1 = false
  109. }
  110. toggleFeaturesClickEvent(true)
  111. })
  112. function addAreaColors() {
  113. featureLists.value.forEach((i) => {
  114. if (!i.color && !i.colors) {
  115. i.color = airSpaceTypes.find((t) => t.label === i.label).color
  116. }
  117. })
  118. layoutStore.toggleFloatPanel('layers_legend', true)
  119. }
  120. function showWarning(message) {
  121. ElMessage({ offset: 90, type: 'warning', message })
  122. }
  123. function getLists() {
  124. Promise.all([
  125. GetAreaList()
  126. .then((res) => {
  127. if (!Array.isArray(res.data.data)) {
  128. showWarning('空域列表查询失败')
  129. return
  130. }
  131. const resList = res.data.data.map((row) => ({
  132. ...row,
  133. check: false,
  134. mesh: false,
  135. }))
  136. featureLists.value[0].children = resList.filter((r) => r.spaceType === '1')
  137. featureLists.value[1].children = resList.filter((r) => r.spaceType === '2')
  138. featureLists.value[2].children = resList.filter((r) => r.spaceType === '3')
  139. featureLists.value[3].children = resList.filter((r) => r.spaceType === '6')
  140. featureLists.value[0].show = true
  141. })
  142. .catch(() => {
  143. showWarning('空域列表查询失败')
  144. }),
  145. GetPlotList()
  146. .then((res) => {
  147. if (!Array.isArray(res.data.data)) {
  148. showWarning('起降场列表查询失败')
  149. return
  150. }
  151. featureLists.value[6].children = res.data.data.map((row) => ({
  152. ...row,
  153. check: false,
  154. mesh: false,
  155. }))
  156. })
  157. .catch(() => {
  158. showWarning('起降场列表查询失败')
  159. }),
  160. GetRouteList()
  161. .then((res) => {
  162. if (!Array.isArray(res.data.data)) {
  163. showWarning('航线列表查询失败')
  164. return
  165. }
  166. featureLists.value[4].children = res.data.data.map((row) => ({
  167. ...row,
  168. check: false,
  169. mesh: false,
  170. }))
  171. })
  172. .catch(() => {
  173. showWarning('航线列表查询失败')
  174. }),
  175. ]).finally(() => {
  176. // 恢复选中状态
  177. handleRestoreChecked()
  178. })
  179. }
  180. function basicCheckAll(item) {
  181. item.active = !item.active
  182. item.children.forEach((c) => {
  183. c.active = !item.active
  184. handleBaseClick(c)
  185. })
  186. }
  187. const basicList = ref([
  188. {
  189. label: '地形',
  190. show: true,
  191. children: [
  192. { label: '影像', alias: '影像底图', icon: 'dxing', active: false },
  193. { label: '政务底图', alias: '政务底图', icon: 'dxing', active: false },
  194. { label: '暗色底图', alias: '暗色底图', icon: 'dxing', active: false },
  195. ],
  196. },
  197. {
  198. label: '低空障碍物',
  199. show: false,
  200. active: false,
  201. children: [
  202. { label: '全市建筑物', alias: '全市白模', icon: 'qsjzwu', active: false },
  203. { label: '高精度模型', alias: '五角场精模', icon: 'gjdmxing', active: false },
  204. ],
  205. },
  206. {
  207. label: '电磁干扰场域',
  208. show: false,
  209. active: false,
  210. children: [
  211. { label: '无线通信基站', active: false, icon: 'wxtxjzhan', disabled: true },
  212. { label: '电磁干扰', active: false, icon: 'dcgrao', disabled: true },
  213. ],
  214. },
  215. {
  216. label: '其它社会信息',
  217. show: false,
  218. active: false,
  219. children: [
  220. { label: '道路', alias: ['快速路', '高速公路', '地面道路'], icon: 'dlu', active: false },
  221. { label: '河流', alias: '全市河流', icon: 'hliu', active: false },
  222. { label: '轨道交通', icon: 'gdjtong', active: false },
  223. { label: '铁路', icon: 'tlu', active: false },
  224. { label: '绿化', alias: '公园绿地', icon: 'lhua', active: false },
  225. { label: '学校', icon: 'xxiao', active: false },
  226. { label: '医院', alias: '医院', icon: 'yyuan', active: false },
  227. { label: '人口', alias: '人口', icon: 'rkou', active: false },
  228. { label: '政府部门', icon: 'zfbmen', active: false },
  229. ],
  230. },
  231. ])
  232. const featureLists = ref([
  233. { label: '禁飞区', type: 'area', show: false, check: false, icon: 'jfqu', children: [] },
  234. { label: '净空区', type: 'area', show: false, check: false, icon: 'jkqu', children: [] },
  235. { label: '适飞区', type: 'area', show: false, check: false, icon: 'sfqu', children: [] },
  236. {
  237. label: '已批复空域',
  238. type: 'area',
  239. show: false,
  240. check: false,
  241. icon: 'ypfkyu',
  242. children: [],
  243. colors: { '120米以下': '#ccff66', '300米以下': '#ffcc66', '600米以下': '#feb2b2' },
  244. },
  245. {
  246. label: '航线',
  247. type: 'route',
  248. show: false,
  249. check: false,
  250. icon: 'hxian',
  251. children: [],
  252. colors: { 已批复: '#6eff25', 未批复: '#ffff00' },
  253. },
  254. { label: '航路', type: 'hanglu', show: false, check: false, icon: 'hxian', children: [], color: '#c195ff' },
  255. { label: '起降场', type: 'plot', show: false, check: false, icon: 'qjchang', children: [], color: '#45dcb5' },
  256. ])
  257. let resources
  258. async function handleBaseClick(layer) {
  259. layer.active = !layer.active
  260. if (layer.alias) {
  261. const aliasArr = Array.isArray(layer.alias) ? layer.alias : [layer.alias]
  262. if (!resources) {
  263. resources = await getData('resources.json')
  264. }
  265. const targetServices = resources.filter((r) => aliasArr.some((i) => i === r.title))
  266. if (targetServices.length === 0) return
  267. targetServices.forEach((service) => {
  268. if (service.type == 'feature') {
  269. heatMap({
  270. ...service,
  271. visible: layer.active,
  272. })
  273. } else {
  274. AddSingleLayer({
  275. ...service,
  276. visible: layer.active,
  277. opacity: 1,
  278. })
  279. }
  280. })
  281. }
  282. }
  283. function toggleB1Show(item) {
  284. const target = basicList.value.find((i) => i.label === item.label)
  285. target.show = !target.show
  286. basicList.value.forEach((i) => {
  287. if (i.show && i.label !== item.label) {
  288. i.show = false
  289. }
  290. })
  291. }
  292. function toggleB2Show(item) {
  293. const target = featureLists.value.find((i) => i.label === item.label)
  294. target.show = !target.show
  295. featureLists.value.forEach((i) => {
  296. if (i.show && i.label !== item.label) {
  297. i.show = false
  298. }
  299. })
  300. }
  301. function handleCheck(item, type) {
  302. // console.log(item)
  303. item.check = !item.check
  304. let color
  305. const shapeObj = JSON.parse(item.shape)
  306. let additional = {}
  307. switch (type) {
  308. case 'area':
  309. additional = {
  310. attributes: {
  311. id: item.id,
  312. name: item.name,
  313. height: shapeObj.height,
  314. },
  315. }
  316. if (item.spaceType !== '6') {
  317. color = hexToRgb(airSpaceTypes.find((i) => i.value === item.spaceType).color, 0.5)
  318. } else {
  319. const colorArr = Object.values(featureLists.value.find((i) => i.label === '已批复空域').colors)
  320. color = +shapeObj.height < 120 ? colorArr[0] : +shapeObj.height < 300 ? colorArr[1] : colorArr[2]
  321. color = hexToRgb(color, 0.5)
  322. // 已批复空域添加属性
  323. additional.attributes = {
  324. lb: item.category,
  325. rwxz: item.taskProperties,
  326. ssdw: item.affiliatedUnit,
  327. jx_js: item.modelAndAmount,
  328. ...additional.attributes,
  329. }
  330. }
  331. break
  332. case 'plot':
  333. color = hexToRgb(featureLists.value.find((i) => i.type === 'plot').color, 0.7)
  334. const { height, coneHeight, cylinderHeight } = shapeObj
  335. additional = {
  336. attributes: {
  337. id: item.id,
  338. name: item.name,
  339. height: height || coneHeight + cylinderHeight,
  340. },
  341. }
  342. break
  343. case 'route':
  344. const colors = featureLists.value.find((i) => i.type === 'route').colors
  345. color = hexToRgb(
  346. ['复旦第三教学楼', '互联宝地', '黄兴公园', '国正中心'].some((i) => item.name.includes(i))
  347. ? colors['已批复']
  348. : colors['未批复'],
  349. 0.5
  350. )
  351. }
  352. const data = [
  353. {
  354. ...additional,
  355. type: type === 'route' ? item.type : item.geoType,
  356. shape: {
  357. ...shapeObj,
  358. color,
  359. },
  360. },
  361. ]
  362. if (layoutStore.sceneType === 'gis') {
  363. showShapes({
  364. id: item.id,
  365. data: item.check ? data : null,
  366. })
  367. } else {
  368. renderShapes({
  369. id: item.id,
  370. data: item.check ? data[0] : null,
  371. })
  372. }
  373. }
  374. function handleCheckShiFei(label) {
  375. if (label !== '适飞区') return
  376. shifeiStatus = !shifeiStatus
  377. if (shifeiStatus) {
  378. shifei.forEach((item) => {
  379. showShapes({
  380. id: item.attributes.FID,
  381. data: [
  382. {
  383. type: 'polygon',
  384. shape: {
  385. color: [0, 255, 0, 0.5],
  386. height: 120,
  387. rings: item.geometry.rings,
  388. },
  389. },
  390. ],
  391. })
  392. })
  393. } else {
  394. shifei.forEach((item) => {
  395. showShapes({
  396. id: item.attributes.FID,
  397. })
  398. })
  399. }
  400. }
  401. function handleDelete(item, type) {
  402. switch (type) {
  403. case 'area':
  404. DeleteArea(item.id).then((res) => {
  405. if (res.data.code === 200) {
  406. ElMessage.success('删除成功')
  407. getLists()
  408. }
  409. })
  410. break
  411. case 'plot':
  412. DeletePlot(item.id).then((res) => {
  413. if (res.data.code === 200) {
  414. ElMessage.success('删除成功')
  415. getLists()
  416. }
  417. })
  418. break
  419. case 'route':
  420. DeleteRoute(item.id).then((res) => {
  421. if (res.data.code === 200) {
  422. ElMessage.success('删除成功')
  423. getLists()
  424. }
  425. })
  426. }
  427. }
  428. function handleMesh(item, type) {
  429. item.mesh = !item.mesh
  430. if (item.mesh) {
  431. layoutStore.toggleGlobalLoading(true)
  432. }
  433. InspectCube({
  434. id: item.id,
  435. show: item.mesh,
  436. type: item.geoType || item.type,
  437. shape: JSON.parse(item.shape),
  438. })
  439. }
  440. const contentShow = reactive({
  441. b1: true,
  442. b2: true,
  443. })
  444. function toggleContentShow(id) {
  445. contentShow[id] = !contentShow[id]
  446. }
  447. function handleCheckAll(item) {
  448. item.check = !item.check
  449. item.children.forEach((c) => {
  450. if (item.check && !c.check) {
  451. handleCheck(c, item.type)
  452. } else if (!item.check && c.check) {
  453. handleCheck(c, item.type)
  454. }
  455. })
  456. }
  457. function handleStoreChecked() {
  458. panelStore.setSjwg({
  459. basicList: basicList.value,
  460. featureLists: featureLists.value.map((t) => ({
  461. label: t.label,
  462. show: t.show,
  463. check: t.check,
  464. children: t.children.map((c) => ({
  465. id: c.id,
  466. check: c.check,
  467. mesh: c.mesh,
  468. })),
  469. })),
  470. })
  471. }
  472. function handleRestoreChecked() {
  473. if (!Object.keys(panelStore.sjwg).length) return
  474. const temp = panelStore.sjwg
  475. basicList.value = temp.basicList
  476. featureLists.value.forEach((row) => {
  477. const tar_row = temp.featureLists.find((t) => t.label === row.label)
  478. row.check = tar_row.check
  479. row.show = tar_row.show
  480. row.children.forEach((c) => {
  481. const tar_c = tar_row.children.find((tc) => tc.id === c.id)
  482. c.check = tar_c.check
  483. c.mesh = tar_c.mesh
  484. })
  485. })
  486. }
  487. onBeforeUnmount(() => {
  488. handleStoreChecked()
  489. layoutStore.toggleFloatPanel('layers_legend', false)
  490. layoutStore.toggleFloatPanel('air_space', false)
  491. toggleFeaturesClickEvent(false)
  492. })
  493. const vCollapse = {
  494. beforeMount(el, binding) {
  495. el.style.height = '0'
  496. el.style.overflow = binding.value === 'scroll' ? 'auto' : 'hidden'
  497. el.style.transition = 'height 0.5s ease'
  498. },
  499. mounted(el) {
  500. const naturalHeight = el.scrollHeight
  501. el.style.height = `${naturalHeight}px`
  502. },
  503. beforeUnmount(el) {
  504. el.style.height = '0'
  505. },
  506. }
  507. watch(
  508. () => mapStore.cubeResult,
  509. (val) => {
  510. if (val.data == 'error') {
  511. ElMessage({ type: 'error', message: '核查结果为空' })
  512. }
  513. layoutStore.toggleGlobalLoading(false)
  514. },
  515. { deep: true }
  516. )
  517. watch(
  518. () => mapStore.clickResult,
  519. (val) => {
  520. const isShow = val && val.id && val.name && val.height
  521. layoutStore.toggleFloatPanel('air_space', isShow)
  522. },
  523. { deep: true }
  524. )
  525. </script>
  526. <style lang="scss" scoped>
  527. .list-base {
  528. li {
  529. span {
  530. width: 75px;
  531. }
  532. img {
  533. width: 75px;
  534. height: 77px;
  535. }
  536. }
  537. }
  538. .drop-down {
  539. display: block;
  540. width: 20px;
  541. height: 20px;
  542. margin-left: 10px;
  543. background: url('../../../assets/images/buttons/btn-dropdown.png') no-repeat;
  544. background-size: 10px 7px;
  545. background-position: center;
  546. transition: transform 0.3s ease;
  547. cursor: pointer;
  548. &.reverse {
  549. transform: rotateZ(180deg);
  550. }
  551. }
  552. .title-shade {
  553. position: relative;
  554. height: 32px;
  555. background: linear-gradient(to right, rgba(101, 131, 190, 1) 0%, rgba(101, 131, 190, 0) 100%);
  556. display: flex;
  557. align-items: center;
  558. padding: 0 16px;
  559. margin-bottom: 10px;
  560. &::before {
  561. content: '';
  562. position: absolute;
  563. left: 0;
  564. top: 0;
  565. width: 4px;
  566. height: 32px;
  567. background: #c0d5ff;
  568. }
  569. span {
  570. text-shadow: 0px 4px 4px rgba(21, 41, 91, 0.45);
  571. margin-right: 10px;
  572. }
  573. .btn-selectall {
  574. display: block;
  575. width: 26px;
  576. height: 26px;
  577. background: url('../../../assets/images/buttons/btn-selectall.png');
  578. background-size: 100%;
  579. background-position: center 1px;
  580. cursor: pointer;
  581. filter: opacity(0.6);
  582. &.active {
  583. filter: opacity(1) drop-shadow(0 0 5px #379bff);
  584. transform: scale(1.1);
  585. }
  586. }
  587. .drop-down {
  588. margin-left: auto;
  589. }
  590. }
  591. .list-item {
  592. position: relative;
  593. height: 40px;
  594. padding: 0 15px;
  595. margin: 9px 0;
  596. display: flex;
  597. align-items: center;
  598. background: #4352704d;
  599. &.disabled {
  600. pointer-events: none;
  601. opacity: 0.5;
  602. }
  603. &::before {
  604. content: '';
  605. position: absolute;
  606. left: 0;
  607. top: 0;
  608. width: 1px;
  609. height: 40px;
  610. background: #808dc9;
  611. }
  612. &:last-child {
  613. margin-bottom: 15px;
  614. }
  615. img {
  616. width: 24px;
  617. height: 24px;
  618. margin-right: 10px;
  619. }
  620. span {
  621. flex: 1;
  622. }
  623. i {
  624. display: block;
  625. width: 34px;
  626. height: 34px;
  627. margin-left: 5px;
  628. cursor: pointer;
  629. filter: grayscale(1);
  630. &.active {
  631. filter: grayscale(0);
  632. }
  633. }
  634. i:nth-child(3) {
  635. background: url('../../../assets/images/buttons/btn-check.png');
  636. background-size: cover;
  637. }
  638. i:nth-child(4) {
  639. background: url('../../../assets/images/buttons/btn-mesh.png');
  640. background-size: cover;
  641. }
  642. i:nth-child(5) {
  643. background: url('../../../assets/images/buttons/btn-delete.png');
  644. background-size: 40px 40px;
  645. background-position: center;
  646. }
  647. }
  648. .no-data {
  649. display: block;
  650. width: 100%;
  651. text-align: center;
  652. margin: 5px 0 10px;
  653. color: #999;
  654. }
  655. .feature-legend {
  656. position: absolute;
  657. bottom: var(--panel-gap);
  658. left: calc(var(--panel-left) - var(--panel-gap));
  659. width: 140px;
  660. padding: 10px 15px 15px;
  661. background-color: rgba(0, 17, 50, 0.5);
  662. border: 1px solid #055f8d;
  663. border-radius: 5px;
  664. .title-sub {
  665. margin-left: -7px;
  666. }
  667. ul {
  668. margin-top: 5px;
  669. li,
  670. div {
  671. display: flex;
  672. align-items: center;
  673. i {
  674. display: block;
  675. width: 10px;
  676. height: 10px;
  677. margin-right: 10px;
  678. opacity: 0.6;
  679. }
  680. span {
  681. font-size: 14px;
  682. color: #ccc;
  683. }
  684. }
  685. li {
  686. &:not(:last-child) {
  687. margin: 5px 0;
  688. }
  689. span.bold {
  690. font-size: 15px;
  691. }
  692. }
  693. div {
  694. padding-left: 15px;
  695. }
  696. }
  697. }
  698. </style>