图书馆综合管理系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

686 lines
22 KiB

6 months ago
  1. <template>
  2. <div class="app-container">
  3. <div class="venue-header">
  4. <h4 @click="handleToGrids"><i class="iconfont icon-shuju" />书架总览</h4>
  5. <span class="bookshelf-area">{{ floorName }} - {{ regionName }}</span>
  6. <p><i class="iconfont icon-gongsi" />{{ user.fonds.fondsName }}</p>
  7. </div>
  8. <div class="venue-content">
  9. <crudOperation :permission="permission">
  10. <template v-slot:middle>
  11. <el-select v-model="layerVal" clearable size="small" placeholder="楼层" class="filter-item" style="width: 100px; margin-right: 20px;">
  12. <el-option v-for="item in layerOptions" :key="item.id" :label="item.name" :value="item.id" />
  13. </el-select>
  14. <el-button v-permission="permission.add" size="mini" @click="crud.toAdd">
  15. <i class="iconfont icon-shengchengpandiandan" />
  16. 书架盘点
  17. </el-button>
  18. </template>
  19. <template v-slot:right>
  20. <el-button :loading="crud.downloadLoading" size="mini" @click="doExport(crud.selections)">
  21. <i class="iconfont icon-daochu" />
  22. 导出
  23. </el-button>
  24. </template>
  25. </crudOperation>
  26. <div class="venue-left">
  27. <div class="container-right tab-content">
  28. <span class="right-top-line" />
  29. <span class="left-bottom-line" />
  30. <!-- <div style="display: flex;justify-content: flex-start; align-items: center;"> -->
  31. <swiper
  32. ref="swiperTitle"
  33. class="swiper-title"
  34. :options="swiperOptionTitle"
  35. :auto-update="true"
  36. :auto-destroy="true"
  37. :delete-instance-on-destroy="true"
  38. :cleanup-styles-on-destroy="true"
  39. >
  40. <swiper-slide
  41. v-for="(item, index) of tabListData"
  42. ref="swiperSlideItem"
  43. :key="'name' + index"
  44. :iname="item.name"
  45. class="swiper-slide-title"
  46. >
  47. <div
  48. class="tab-name"
  49. :class="{ active: index === swiperActiveIndex }"
  50. @click="handleSlidClickFun(index)"
  51. >
  52. {{ item.name }}
  53. </div>
  54. </swiper-slide>
  55. </swiper>
  56. <div class="tag-info">
  57. <p>错序1</p>
  58. <p>错架1</p>
  59. <p>在架20</p>
  60. </div>
  61. <!-- </div> -->
  62. <swiper
  63. ref="swiperContent"
  64. class="swiper-content"
  65. :options="swiperOptionContent"
  66. :auto-update="true"
  67. :auto-destroy="true"
  68. :delete-instance-on-destroy="true"
  69. :cleanup-styles-on-destroy="true"
  70. >
  71. <swiper-slide
  72. v-for="(item, index) of tabListData"
  73. :key="'content' + index"
  74. class="swiper-slide-content"
  75. >
  76. <ul class="cabinet-row">
  77. <li
  78. v-for="(cell,i) in booShelfGrid"
  79. :key="i"
  80. class="cabinet-cell"
  81. :style="cellStyle"
  82. :class="{ active: i === cellIndex }"
  83. @click="handleCellCurrent(cell,i)"
  84. @mouseenter="showPopover(i)"
  85. @mouseleave="hidePopover"
  86. >
  87. <span class="cell-name">{{ removeAreaPrefix(cell.gridName) }}</span>
  88. <!-- v-if="popoverIndex === i" -->
  89. <el-popover
  90. v-if="popoverIndex === i"
  91. ref="popover"
  92. :visible="popoverVisible[i]"
  93. width="400"
  94. :style="popoverStyles[i]"
  95. trigger="manual"
  96. >
  97. <div slot="reference" class="popover-content">
  98. <div class="tooltip-top">
  99. <h4>层位概况</h4>
  100. <i class="update-time">2024-11-28 09:46</i>
  101. </div>
  102. <ul>
  103. <li><p>层位</p><em class="percentage"><i style="color: #fff;">{{ removeAreaPrefix(cell.gridName) }}</i></em></li>
  104. <li><p>在架</p><em><i>15000</i></em></li>
  105. <li><p>错架</p><em><i>300</i></em> <em class="percentage">2.00%</em></li>
  106. <li><p>错序</p><em><i>0</i></em><em class="percentage">0.00%</em></li>
  107. </ul>
  108. </div>
  109. </el-popover>
  110. </li>
  111. </ul>
  112. </swiper-slide>
  113. </swiper>
  114. </div>
  115. </div>
  116. <div class="venue-right">
  117. <div class="lib-right-item lib-info">
  118. <h4>本架概况</h4>
  119. <ul class="data-right-list">
  120. <li><p>书架</p><span><i>001</i></span></li>
  121. <li><p>规则</p><span><i>双面 6 x 5</i></span></li>
  122. </ul>
  123. </div>
  124. <div class="lib-right-item">
  125. <h4>本架盘点概况</h4>
  126. <div class="refresh-date">2024-11-28 09:46</div>
  127. <ul class="data-right-list">
  128. <li><p>在架</p><span><i>15000</i></span></li>
  129. <li><p>错架</p><span><i>300</i></span> <span class="percentage">2.00%</span></li>
  130. <li><p>错序</p><span><i>0</i></span><span class="percentage">0.00%</span></li>
  131. </ul>
  132. </div>
  133. </div>
  134. </div>
  135. <!-- form -->
  136. <el-dialog append-to-body :close-on-click-modal="false" :modal-append-to-body="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title">
  137. <span class="dialog-right-top" />
  138. <span class="dialog-left-bottom" />
  139. <div class="setting-dialog">
  140. <el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px">
  141. <el-form-item label="盘点单号" prop="taskName">
  142. <el-input v-model="form.taskName" />
  143. </el-form-item>
  144. <el-form-item label="盘点类型" prop="taskType">
  145. <el-input v-model="form.taskType" />
  146. </el-form-item>
  147. <el-form-item label="目标位置" prop="location">
  148. <el-input v-model="form.location" />
  149. </el-form-item>
  150. <el-form-item label="目标数量" prop="number">
  151. <el-input v-model="form.number" />
  152. </el-form-item>
  153. <el-row>
  154. <el-form-item label="备注" prop="remark">
  155. <el-input v-model="form.remark" type="textarea" style="width: 572px;" :rows="4" />
  156. </el-form-item>
  157. </el-row>
  158. </el-form>
  159. <div slot="footer" class="dialog-footer">
  160. <el-button type="text" @click="crud.cancelCU">取消</el-button>
  161. <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">保存</el-button>
  162. </div>
  163. </div>
  164. </el-dialog>
  165. </div>
  166. </template>
  167. <script>
  168. import { FetchInitShelfGridByShelfId, FetchBookShelfDetails } from '@/api/shelf/index'
  169. import crudRegion from '@/api/area/index'
  170. import CRUD, { presenter, header, form, crud } from '@crud/crud'
  171. import crudOperation from '@crud/CRUD.operation'
  172. import { mapGetters } from 'vuex'
  173. import { swiper, swiperSlide } from 'vue-awesome-swiper'
  174. import 'swiper/dist/css/swiper.css'
  175. const defaultForm = { id: null, taskType: null, taskName: null, location: null, number: null, remark: null }
  176. export default {
  177. name: 'DataScreening',
  178. components: { swiper, swiperSlide, crudOperation },
  179. cruds() {
  180. return CRUD({ title: '架位总览', url: 'api/libraryRegion/initLibraryRegionList', crudMethod: { ...crudRegion }, sort: [], optShow: {
  181. add: false,
  182. edit: false,
  183. del: false,
  184. download: false,
  185. group: false,
  186. reset: false
  187. },
  188. queryOnPresenterCreated: false
  189. })
  190. },
  191. mixins: [presenter(), header(), form(defaultForm), crud()],
  192. data() {
  193. const _this = this
  194. return {
  195. floorName: null,
  196. regionName: null,
  197. bookShelfDetails: null,
  198. booShelfGrid: null,
  199. cellInfo: {
  200. gridName: null,
  201. startSortmark: null,
  202. endSortmark: null,
  203. cameraId: null
  204. },
  205. callNumVisible: false,
  206. layerNum: 0,
  207. rackNum: 0,
  208. swiperActiveIndex: 0,
  209. cellIndex: null,
  210. swiperOptionContent: {
  211. slidesPerView: 'auto',
  212. on: {
  213. slideChangeTransitionStart: function() {
  214. _this.cellIndex = null
  215. _this.swiperActiveIndex = this.activeIndex
  216. console.log('activeIndexffff', this.swiperActiveIndex)
  217. _this.swiperTitle.slideTo(this.activeIndex, 500, false)
  218. }
  219. }
  220. },
  221. swiperOptionTitle: {
  222. slidesPerView: 'auto',
  223. freeMode: true
  224. },
  225. layerVal: '001排',
  226. layerOptions: [{ id: 1, name: '001排' }],
  227. tabListData: [],
  228. permission: {
  229. add: ['admin', 'floor:add'],
  230. edit: ['admin', 'floor:edit'],
  231. del: ['admin', 'floor:del']
  232. },
  233. rules: {
  234. taskName: [
  235. { required: true, message: '请输入盘点单号', trigger: 'blur' }
  236. ],
  237. taskType: [
  238. { required: true, message: '请输入盘点类型', trigger: 'blur' }
  239. ],
  240. location: [
  241. { required: true, message: '请输入目标位置', trigger: 'blur' }
  242. ],
  243. number: [
  244. { required: true, message: '请输入目标数量', trigger: 'blur' }
  245. ]
  246. },
  247. popoverIndex: 1,
  248. popoverVisible: [],
  249. popoverStyles: []
  250. }
  251. },
  252. computed: {
  253. ...mapGetters([
  254. 'user',
  255. 'baseApi'
  256. ]),
  257. swiperContent() {
  258. return this.$refs.swiperContent.$el.swiper
  259. },
  260. swiperTitle() {
  261. return this.$refs.swiperTitle.$el.swiper
  262. },
  263. cellStyle: function() {
  264. // const h = '100%/' + this.layerNum
  265. // const w = '100%/' + this.rackNum
  266. const h = '70px'
  267. const w = '100%/' + this.rackNum
  268. return { width: `calc(${w} - 4px )`, height: `calc(${h} - 2px)` }
  269. }
  270. },
  271. async created() {
  272. if (localStorage.getItem('dataScreenRegion')) {
  273. const dataScreenRegion = JSON.parse(localStorage.getItem('dataScreenRegion'))
  274. this.floorName = dataScreenRegion.floorName
  275. this.regionName = dataScreenRegion.regionName
  276. // 单面/双面
  277. this.tabListData = dataScreenRegion.rowType === 1
  278. ? dataScreenRegion.toward === 1
  279. ? [{ name: 'A面' }]
  280. : [{ name: 'B面' }]
  281. : [{ name: 'A面' }, { name: 'B面' }]
  282. FetchBookShelfDetails({ 'shelfId': dataScreenRegion.id }).then(res => {
  283. this.layerNum = res.shelfFloor
  284. this.rackNum = res.shelfShelf
  285. this.bookShelfDetails = res
  286. this.getInitShelfGridByShelfId(this.bookShelfDetails.toward)
  287. }).catch(() => {
  288. })
  289. }
  290. },
  291. methods: {
  292. handleToGrids() {
  293. this.$router.push({ path: '/dataScreening/gird', query: { }})
  294. },
  295. [CRUD.HOOK.beforeRefresh]() {
  296. },
  297. [CRUD.HOOK.afterRefresh](crud) {
  298. },
  299. // 提交前的验证
  300. [CRUD.HOOK.afterValidateCU](crud) {
  301. return true
  302. },
  303. removeAreaPrefix(gridNames) {
  304. const index = gridNames.indexOf('区')
  305. if (index !== -1) {
  306. return gridNames.substring(index + 1)
  307. }
  308. return gridNames
  309. },
  310. getInitShelfGridByShelfId(toward) {
  311. // rowType 1 单 2 双
  312. // toward 1 A面 2 B面
  313. // shelfType 1 '始终最左边为第1架(S型排架)'
  314. // shelfType 2 'A面最左为第1架(B面最左为最后1架)'
  315. // shelfType 3 'B面最左为第1架(A面最左为最后1架)'
  316. // floorType 1 '最顶层为第一层(从上至下)'
  317. // floorType 2 '最底层为第一层(从下至上)'
  318. FetchInitShelfGridByShelfId({ 'shelfId': this.bookShelfDetails.id, 'toward': toward }).then(res => {
  319. const sortFunction = toward === 1 ? {
  320. 1: { 1: 'sortBookshelvesLeftTop', 2: 'sortBookshelvesLeftBottom' },
  321. 2: { 1: 'sortBookshelvesLeftTop', 2: 'sortBookshelvesLeftBottom' },
  322. 3: { 1: 'sortBookshelvesRightTop', 2: 'sortBookshelvesRightBottom' }
  323. } : {
  324. 1: { 1: 'sortBookshelvesLeftTop', 2: 'sortBookshelvesLeftBottom' },
  325. 2: { 1: 'sortBookshelvesRightTop', 2: 'sortBookshelvesRightBottom' },
  326. 3: { 1: 'sortBookshelvesLeftTop', 2: 'sortBookshelvesLeftBottom' }
  327. }
  328. const shelfType = this.bookShelfDetails.shelfType
  329. const floorType = this.bookShelfDetails.floorType
  330. const sortMethod = sortFunction[shelfType][floorType]
  331. this.booShelfGrid = this[sortMethod](res)
  332. this.popoverVisible = Array(this.booShelfGrid.length).fill(false)
  333. // this.popoverStyles = new Array(this.booShelfGrid.length).fill({ position: 'absolute', left: '20%', top: '48px' })
  334. }).catch(() => {
  335. })
  336. },
  337. // 最左为第一架, 最顶层为第一层 从上往下
  338. sortBookshelvesLeftTop(data) {
  339. const sortedData = []
  340. const maxFloor = Math.max(...data.map(item => parseInt(item.gridFloor)))
  341. const maxShelf = Math.max(...data.map(item => parseInt(item.gridShelf.slice(-1))))
  342. for (let i = 1; i <= maxFloor; i++) {
  343. for (let j = 1; j <= maxShelf; j++) {
  344. const currentShelf = data.find(item => parseInt(item.gridFloor) === i && parseInt(item.gridShelf.slice(-1)) === j)
  345. if (currentShelf) {
  346. sortedData.push(currentShelf)
  347. }
  348. }
  349. }
  350. return sortedData
  351. },
  352. // 最右为第一架,最左为最后一架, 最顶层为第一层 从上往下
  353. sortBookshelvesRightTop(data) {
  354. const sortedData = []
  355. // 获取最大的楼层数
  356. const maxFloor = Math.max(...data.map(item => parseInt(item.gridFloor)))
  357. const maxShelf = Math.max(...data.map(item => parseInt(item.gridShelf.match(/\d+$/)[0])))
  358. for (let i = 1; i <= maxFloor; i++) {
  359. // 从最大的书架层数开始,向下排序
  360. for (let j = maxShelf; j >= 1; j--) {
  361. const currentShelf = data.find(item => parseInt(item.gridFloor) === i && parseInt(item.gridShelf.match(/\d+$/)[0]) === j)
  362. if (currentShelf) {
  363. sortedData.push(currentShelf)
  364. }
  365. }
  366. }
  367. return sortedData
  368. },
  369. // 最左为第一架, 最底层为第一层 从下往上
  370. sortBookshelvesLeftBottom(data) {
  371. const sortedData = []
  372. // 获取最大的楼层数
  373. const maxFloor = Math.max(...data.map(item => parseInt(item.gridFloor)))
  374. // 获取最大的书架层数
  375. const maxShelf = Math.max(...data.map(item => parseInt(item.gridShelf.slice(-1))))
  376. for (let i = maxFloor; i >= 1; i--) {
  377. for (let j = 1; j <= maxShelf; j++) {
  378. const currentShelf = data.find(item => parseInt(item.gridFloor) === i && parseInt(item.gridShelf.slice(-1)) === j)
  379. if (currentShelf) {
  380. sortedData.push(currentShelf)
  381. }
  382. }
  383. }
  384. return sortedData
  385. },
  386. // 最左为最后一架, 最底层为第一层 从下往上
  387. sortBookshelvesRightBottom(data) {
  388. const sortedData = []
  389. // 获取最大的楼层数
  390. const maxFloor = Math.max(...data.map(item => parseInt(item.gridFloor)))
  391. const maxShelfPerFloor = data.map(item => parseInt(item.gridShelf.match(/\d+$/)[0]))
  392. .reduce((acc, curr, index, arr) => {
  393. const floor = parseInt(data[index].gridFloor)
  394. if (!acc[floor]) acc[floor] = 1
  395. if (acc[floor] < curr) acc[floor] = curr
  396. return acc
  397. }, {})
  398. // 从最大的楼层开始向下遍历
  399. for (let i = maxFloor; i >= 1; i--) {
  400. // 从最大的书架编号开始向左遍历
  401. for (let j = maxShelfPerFloor[i] || 1; j >= 1; j--) {
  402. const currentShelf = data.find(item => parseInt(item.gridFloor) === i && parseInt(item.gridShelf.match(/\d+$/)[0]) === j)
  403. if (currentShelf) {
  404. sortedData.push(currentShelf)
  405. }
  406. }
  407. }
  408. return sortedData
  409. },
  410. handleSlidClickFun(index) {
  411. this.cellIndex = null
  412. this.handleSlideToFun(index)
  413. if (localStorage.getItem('bookShelfDetails')) {
  414. this.getInitShelfGridByShelfId(index + 1)
  415. }
  416. },
  417. handleSlideToFun(index) {
  418. this.swiperActiveIndex = index
  419. this.swiperContent.slideTo(index, 500, false)
  420. this.swiperTitle.slideTo(index, 500, false)
  421. },
  422. handleCellCurrent(item, index) {
  423. console.log('index', index)
  424. this.cellIndex = index
  425. this.cellInfo = {
  426. id: item.id,
  427. gridName: item.gridName,
  428. startSortmark: item.startSortmark,
  429. endSortmark: item.endSortmark,
  430. cameraId: item.cameraId,
  431. check: item.isCheck,
  432. order: item.isOrder
  433. }
  434. },
  435. showPopover(index) {
  436. this.popoverIndex = index
  437. // 显示对应的 popover
  438. if (!this.popoverVisible[index]) {
  439. this.$set(this.popoverVisible, index, true)
  440. }
  441. const lastColumnIndexes = []
  442. const secondLastColumnIndexes = []
  443. for (let i = 0; i < this.booShelfGrid.length; i++) {
  444. // 计算当前数据项的列索引
  445. const columnIndex = i % this.rackNum
  446. // 如果是最后一列(第5列)
  447. if (columnIndex === this.rackNum - 1) {
  448. lastColumnIndexes.push(i)
  449. // 更新最后一列的样式
  450. this.$set(this.popoverStyles, i, { position: 'absolute', left: '-140px', top: '48px' })
  451. }
  452. // 如果是倒数第二列(第4列)
  453. if (columnIndex === this.rackNum - 2) {
  454. secondLastColumnIndexes.push(i)
  455. // 更新倒数第二列的样式
  456. this.$set(this.popoverStyles, i, { position: 'absolute', left: '-20px', top: '48px' })
  457. }
  458. }
  459. },
  460. hidePopover() {
  461. this.popoverIndex = null
  462. // 隐藏所有的popover
  463. this.popoverVisible.forEach((isVisible, index) => {
  464. if (isVisible) {
  465. this.$set(this.popoverVisible, index, false)
  466. }
  467. })
  468. }
  469. }
  470. }
  471. </script>
  472. <style lang="scss" scoped>
  473. .container-right{
  474. min-height: calc(100vh - 232px) !important;
  475. }
  476. .venue-content{
  477. position: relative;
  478. }
  479. .crud-opts{
  480. position: absolute;
  481. right: 20px;
  482. top: 10px;
  483. }
  484. .venue-left{
  485. flex: 1;
  486. margin-right: 0 !important;
  487. .venue-preview{
  488. height: 633px !important;
  489. }
  490. }
  491. .venue-right{
  492. display: flex;
  493. flex-direction: column;
  494. width: 400px;
  495. padding: 50px 10px 20px 10px !important;
  496. .lib-right-item{
  497. position: relative;
  498. padding-bottom: 10px;
  499. margin-bottom: 10px;
  500. border: 1px solid #E8F2FF;
  501. border-radius: 4px;
  502. h4{
  503. padding: 6px 10px;
  504. background-color: #E8F2FF;
  505. color: #000;
  506. line-height: 30px;
  507. border-bottom: 1px solid #edeff3;
  508. }
  509. .refresh-date{
  510. position: absolute;
  511. right: 14px;
  512. top: 10px;
  513. font-size: 12px;
  514. line-height: 30px;
  515. }
  516. }
  517. }
  518. .data-right-list {
  519. padding-top: 10px;
  520. li{
  521. display: flex;
  522. justify-content: flex-start;
  523. align-items: center;
  524. line-height: 36px;
  525. p{
  526. width: 80px;
  527. font-weight: bold;
  528. text-align: right;
  529. }
  530. span{
  531. width: 140px;
  532. display: block;
  533. text-align: right;
  534. i{
  535. font-style: normal;
  536. font-weight: bold;
  537. padding: 0 10px;
  538. color: #0348f3;
  539. }
  540. &.percentage{
  541. width: auto;
  542. }
  543. }
  544. }
  545. }
  546. .swiper-title{
  547. ::v-deep .swiper-wrapper{
  548. margin: 10px 0;
  549. border-bottom: 1px solid #EDEFF3;
  550. }
  551. }
  552. .swiper-slide-title {
  553. width: auto !important;
  554. margin-right: 20px;
  555. cursor: pointer;
  556. .tab-name {
  557. padding: 10px;
  558. &.active {
  559. color: #0348F3;
  560. border-bottom: 3px solid #0348F3;
  561. }
  562. }
  563. }
  564. .swiper-content{
  565. height: 610px;
  566. }
  567. .tag-info{
  568. position: absolute;
  569. right: 20px;
  570. top: 34px;
  571. display: flex;
  572. justify-content: flex-start;
  573. padding-left: 100px;
  574. p{
  575. margin-left: 20px;
  576. margin-top: -4px;
  577. font-size: 14px;
  578. }
  579. }
  580. .cabinet-row .cabinet-cell{
  581. background: url('~@/assets/images/shelf02.png') repeat-x left top;
  582. background-size: 100% 100%;
  583. border: none;
  584. overflow: inherit;
  585. cursor: pointer;
  586. &::before{
  587. content: "";
  588. position: absolute;
  589. left: 0;
  590. top: 0;
  591. width: 6px;
  592. height: 68px;
  593. background: url('~@/assets/images/shelf01.png') no-repeat left top;
  594. background-size: 100% 100%;
  595. }
  596. &::after{
  597. content: "";
  598. position: absolute;
  599. right: -4px;
  600. top: 0;
  601. width: 6px;
  602. height: 68px;
  603. background: url('~@/assets/images/shelf01.png') no-repeat left top;
  604. background-size: 100% 100%;
  605. }
  606. span.cell-name{
  607. position: initial !important;
  608. transform: none;
  609. line-height: 68px;
  610. }
  611. }
  612. ::v-deep .cabinet-row .cabinet-cell span.el-popover__reference-wrapper{
  613. position: absolute !important;
  614. left: 50% !important;
  615. top: 20px !important;
  616. transform: none;
  617. width: 300px;
  618. height: 210px;
  619. background:rgba(0,0,0,.8);
  620. color: #fff;
  621. border-radius: 6px;
  622. z-index: 99999999;
  623. .popover-content{
  624. .tooltip-top{
  625. display: flex;
  626. justify-content: space-between;
  627. align-items: center;
  628. height: 40px;
  629. line-height: 40px;
  630. padding: 0 10px;
  631. border-bottom: 1px solid #fff;
  632. }
  633. .tooltip-top i{
  634. font-style: normal;
  635. font-size: 12px;
  636. }
  637. ul{
  638. padding: 10px;
  639. }
  640. ul li{
  641. display: flex;
  642. justify-content: flex-start;
  643. align-items: center;
  644. line-height: 36px;
  645. font-style: normal;
  646. }
  647. ul li p{
  648. width: 80px;
  649. font-weight: bold;
  650. text-align: right;
  651. }
  652. ul li em{
  653. width: 100px;
  654. display: block;
  655. text-align: right;
  656. font-style: normal;
  657. }
  658. ul li i{
  659. font-style: normal;
  660. font-weight: bold;
  661. padding: 0 10px;
  662. color: #0348f3;
  663. }
  664. ul li em.percentage{
  665. width: auto;
  666. }
  667. }
  668. }
  669. </style>