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.
 
 
 
 
 
 

791 lines
20 KiB

  1. <template>
  2. <view id="home" class="page">
  3. <!-- 顶部搜索栏 -->
  4. <view class="header text-white">
  5. <!-- #ifndef H5 -->
  6. <l-icon @click="scanClick" type="scan" color="white" class="header-left text-xxl margin-left-sm" />
  7. <!-- #endif -->
  8. <view @click="moreClick(1)" class="search header-mid margin-lr-sm">
  9. <l-icon type="search" color="white" class="margin-lr-sm" />
  10. 搜索更多应用
  11. </view>
  12. <!-- <l-icon
  13. @click="msgClick"
  14. type="mail"
  15. color="white"
  16. class="header-right text-xxl margin-right-sm"
  17. /> -->
  18. </view>
  19. <!-- 轮播图片 -->
  20. <swiper v-if="imgCount > 0" style="height: 120px">
  21. <swiper-item v-for="(item, index) of imgData" :key="index" style="height: 120px">
  22. <image :src="item" mode="aspectFill" style="height: 100%; width: 100%"></image>
  23. </swiper-item>
  24. </swiper>
  25. <!-- 功能宫格列表 -->
  26. <view class="col-4 function-list cu-list grid no-border">
  27. <view v-for="(item, index) in funcListDisplay" @click="funcListClick(item)" :key="index"
  28. class="cu-item text-center flex flex-wrap justify-center align-center">
  29. <view class="app-item align-center flex flex-wrap justify-center align-center"
  30. style="position: relative;">
  31. <l-icon :type="item.icon" color="white" class="text-sl" />
  32. <view v-if="item.F_Name == '我的审批'&&CornerMarker" class="CornerMarker">
  33. {{CornerMarker}}
  34. </view>
  35. </view>
  36. <text>{{ item.F_Name }}</text>
  37. </view>
  38. </view>
  39. <view class="text-center bg-white margin-bottom"><text @click="moreClick(0)"
  40. class="function-more-btn margin-tb-sm text-gray">更多应用</text></view>
  41. <!-- 通知列表区块 -->
  42. <view v-for="(block, blockIndex) of noticeData" :key="blockIndex" class="margin-bottom">
  43. <!-- <view class="margin-top"></view> -->
  44. <l-title @click="informClick(block.title)">{{ block.title }}</l-title>
  45. <l-list>
  46. <l-list-item v-for="(item, i) of block.content" @click="noticeClick(item)" :key="i" arrow>
  47. {{ item.f_title || "(无标题)" }}
  48. <l-tag slot="action" line="gray">{{
  49. postDateTime(item.f_time)
  50. }}</l-tag>
  51. </l-list-item>
  52. </l-list>
  53. </view>
  54. <!-- 统计数据宫格 -->
  55. <l-title>统计数据</l-title>
  56. <view class="count-list cu-list grid col-3 margin-bottom">
  57. <view v-for="(item, index) in countData" :key="index" class="cu-item text-center">
  58. <text class="margin-bottom-xs">{{ item.title || "(未命名)" }}</text>
  59. <text class="count-item-value">{{ item.value || "-" }}</text>
  60. </view>
  61. </view>
  62. <!-- 图表区块 -->
  63. <view v-for="item of chartData" :key="item.id" class="margin-bottom">
  64. <l-title>{{ item.title }}</l-title>
  65. <view :style="{ width: cWidth + 'px', height: cHeight + 'px' }" class="chart-list">
  66. <!--#ifndef MP-ALIPAY -->
  67. <canvas v-if="item.type === 1" @tap="chartTap(item, $event)" @touchstart="touchStart(item.id, $event)"
  68. @touchmove="touchMove(item.id, $event)" @touchend="touchEnd(item.id, $event)"
  69. :style="{ width: cWidth + 'px', height: cHeight + 'px' }" :canvas-id="item.id" :id="item.id"
  70. class="charts"></canvas>
  71. <canvas v-else @tap="chartTap(item, $event)" :style="{ width: cWidth + 'px', height: cHeight + 'px' }"
  72. :canvas-id="item.id" :id="item.id" class="charts"></canvas>
  73. <!--#endif -->
  74. <!-- 阿里小程序,需要以2倍尺寸显示,然后缩放为50% -->
  75. <!--#ifdef MP-ALIPAY -->
  76. <canvas v-if="item.type === 1" @tap="chartTap(item, $event)" @touchstart="touchStart(item.id, $event)"
  77. @touchmove="touchMove(item.id, $event)" @touchend="touchEnd(item.id, $event)" :style="{
  78. width: cWidth * pixelRatio + 'px',
  79. height: cHeight * pixelRatio + 'px',
  80. transform: 'scale(' + 1 / pixelRatio + ')',
  81. marginLeft: (-cWidth * (pixelRatio - 1)) / 2 + 'px',
  82. marginTop: (-cHeight * (pixelRatio - 1)) / 2 + 'px',
  83. }" :canvas-id="item.id" :id="item.id" class="charts"></canvas>
  84. <canvas v-else @tap="chartTap(item, $event)" :style="{
  85. width: cWidth * pixelRatio + 'px',
  86. height: cHeight * pixelRatio + 'px',
  87. transform: 'scale(' + 1 / pixelRatio + ')',
  88. marginLeft: (-cWidth * (pixelRatio - 1)) / 2 + 'px',
  89. marginTop: (-cHeight * (pixelRatio - 1)) / 2 + 'px',
  90. }" :canvas-id="item.id" :id="item.id" class="charts"></canvas>
  91. <!--#endif -->
  92. </view>
  93. </view>
  94. </view>
  95. </template>
  96. <script>
  97. import moment from "moment";
  98. import mapValues from "lodash/mapValues";
  99. import uCharts from "@/common/u-charts.js";
  100. // 用于保存图表操作对象
  101. let chartsObject = {};
  102. let chartsConfig = {};
  103. export default {
  104. data() {
  105. return {
  106. imgData: [],
  107. listData: [],
  108. myList: [],
  109. countData: [],
  110. noticeData: [],
  111. chartData: [],
  112. pixelRatio: 1,
  113. cWidth: "",
  114. cHeight: "",
  115. chartsFontSize: 10,
  116. ready: false,
  117. CornerMarker: '',
  118. timer:''
  119. };
  120. },
  121. async onLoad(param) {
  122. await this.init(param);
  123. this.ON('home-openorclosetimer', this.openorclosetimer)
  124. },
  125. async onShow() {
  126. if (this.ready) {
  127. this.ready = false
  128. await this.refresh()
  129. this.ready = true
  130. }
  131. },
  132. // 本页面开启下拉刷新,用于刷新首页数据
  133. onPullDownRefresh() {
  134. this.refresh().then(() => {
  135. this.TOAST("已更新首页数据");
  136. uni.stopPullDownRefresh();
  137. });
  138. },
  139. methods: {
  140. // 页面初始化
  141. async init(param) {
  142. this.HIDE_LOADING();
  143. // 有参数表示可能是打开分享消息;将数据存入全局变量以备后续跳转
  144. if (param && param.learun && param.pagePath) {
  145. this.SET_GLOBAL("jumpParam", param);
  146. }
  147. //单客户端登录判断
  148. this.checkonedrivelogin();
  149. // 登录状态无效,则跳转到登录页
  150. const stateValid = await this.checkLoginState();
  151. if (!stateValid) {
  152. this.RELAUNCH_TO("/pages/login");
  153. return;
  154. }
  155. // #ifdef APP
  156. //检查更新
  157. this.checkupdate();
  158. // #endif
  159. // 图表相关参数初始化
  160. this.initCharts();
  161. // 加载页面数据和全局数据
  162. await this.FETCH_CLIENTDATA();
  163. await this.refresh();
  164. this.ready = true
  165. // 监听「我的应用」列表修改
  166. this.ON("home-list-change", () => {
  167. this.HTTP_GET("learun/adms/function/mylist").then((newList) => {
  168. this.myList = newList;
  169. });
  170. });
  171. this.SET_STORAGE("nextTime", null);
  172. // 处理小程序分享消息跳转
  173. // #ifdef MP
  174. const jumpParam = this.GET_GLOBAL("jumpParam");
  175. if (jumpParam) {
  176. this.SET_GLOBAL("jumpParam", null);
  177. this.MP_SHARE_DECODE(jumpParam);
  178. }
  179. // #endif
  180. //微信推送跳转
  181. if (!!param.page) {
  182. //通知公告
  183. if (param.page == "notice") {
  184. this.NAV_TO("/pages/LR_OAModule/list");
  185. }
  186. //邮件
  187. if (param.page == "mail") {
  188. this.NAV_TO("/pages/EducationalAdministration/SYS_ReceiveMessage/list");
  189. }
  190. //OA待办
  191. if (param.page == "oa") {
  192. this.NAV_TO("/pages/nworkflow/myflow/list");
  193. }
  194. //公文下发
  195. if (param.page == "file") {
  196. this.NAV_TO("/pages/EducationalAdministration/Sys_ReceiveFile/list");
  197. }
  198. return;
  199. }
  200. //间隔10秒请求
  201. this.timer= setInterval(()=>{
  202. this.checkonedrivelogin()
  203. },10000)
  204. },
  205. async checkupdate(){
  206. var result=true;
  207. var appinfo = await this.HTTP_GET("learun/adms/user/checkupdate");
  208. if(appinfo)
  209. {
  210. if(appinfo.url)
  211. {
  212. var appversion=this.CONFIG('appVersion');
  213. var webHost=this.CONFIG('webHost');
  214. if(appinfo.version!=appversion)
  215. {
  216. result=false;
  217. uni.showModal({
  218. showCancel:false,
  219. title: '发现新版本:'+appinfo.version,
  220. content: '请您立即更新,否则会造成使用不稳定或出现错误',
  221. success: function (res) {
  222. if (res.confirm) {
  223. //下载
  224. plus.runtime.openURL(webHost+appinfo.url.replace(/\\/g, "\/"));
  225. }
  226. }
  227. });
  228. }
  229. }
  230. }
  231. return result;
  232. },
  233. //验证多客户端登录
  234. async checkonedrivelogin()
  235. {
  236. const token = this.GET_GLOBAL("token") || uni.getStorageSync("token");
  237. if (!token || token === "null" || token === "undefined") {
  238. this.RELAUNCH_TO("/pages/login");
  239. this.HIDE_LOADING();
  240. return false;
  241. }
  242. this.SET_GLOBAL("token", token);
  243. var logintime=this.GET_STORAGE("logintime");
  244. if(logintime)
  245. {
  246. var userInfo = await this.HTTP_GET("learun/adms/user/onedrivelogin");
  247. if (userInfo) {
  248. if(userInfo.logintime!=logintime)
  249. {
  250. clearInterval(this.timer);
  251. this.RELAUNCH_TO("/pages/login");
  252. }
  253. }
  254. else{
  255. clearInterval(this.timer);
  256. this.RELAUNCH_TO("/pages/login");
  257. }
  258. }
  259. },
  260. openorclosetimer(){
  261. this.timer&&clearInterval(this.timer);
  262. },
  263. // 验证登录状态
  264. async checkLoginState() {
  265. const token = this.GET_GLOBAL("token") || uni.getStorageSync("token");
  266. if (!token || token === "null" || token === "undefined") {
  267. this.RELAUNCH_TO("/pages/login");
  268. this.HIDE_LOADING();
  269. return false;
  270. }
  271. this.SET_GLOBAL("token", token);
  272. // 判断是否有 loginUser 对象
  273. if (this.GET_GLOBAL("loginUser")) {
  274. return true;
  275. }
  276. // 拉取用户信息验证登录态;如果失败则跳转至登录页
  277. const userInfo = await this.HTTP_GET("learun/adms/user/info");
  278. if (!userInfo) {
  279. this.SET_GLOBAL("token", null);
  280. this.SET_STORAGE("token", null);
  281. return false;
  282. }
  283. // 有登录态,则设置用户信息和全局数据
  284. const {
  285. baseinfo,
  286. mpinfo,
  287. post,
  288. role
  289. } = userInfo;
  290. const user = {
  291. ...baseinfo,
  292. post,
  293. role
  294. };
  295. if (mpinfo && Array.isArray(mpinfo) && mpinfo.includes(this.PLATFORM)) {
  296. user.miniProgram = true;
  297. }
  298. this.SET_GLOBAL("loginUser", user);
  299. return true;
  300. },
  301. // 刷新首页数据
  302. async refresh() {
  303. // 清空页面数据
  304. chartsObject = {};
  305. this.imgData = [];
  306. this.listData = [];
  307. this.myList = [];
  308. this.countData = [];
  309. this.noticeData = [];
  310. this.chartData = [];
  311. // 同时发出请求,获取轮播图、所有功能列表、我的功能列表、商机通知提醒图表数据
  312. // 商机、通知提醒图表数据,获取的是数据ID,所以还需要进一步请求
  313. const [imgData, listData, myList, settingData] = await Promise.all([
  314. this.HTTP_GET(
  315. "learun/adms/desktop/imgid" +
  316. (this.CONFIG("isDistributed") == true ? "?isDistributed=true" : "")
  317. ),
  318. this.HTTP_GET("learun/adms/function/list").then(
  319. (result) => result.data
  320. ),
  321. this.HTTP_GET("learun/adms/function/mylist"),
  322. this.HTTP_GET("learun/adms/desktop/setting").then(
  323. (result) => result.data
  324. ),
  325. ]);
  326. this.imgData = imgData;
  327. if (!this.CONFIG("isDistributed"))
  328. // 轮播图需要加上 url 前缀
  329. this.imgData = imgData.map((t) => this.API + `/desktop/img?data=${t}`);
  330. // 功能区按钮需要处理 icon
  331. this.listData = listData.map((item) => {
  332. const icon = item.F_Icon ?
  333. item.F_Icon.replace(`iconfont icon-`, ``) :
  334. "";
  335. const existsIcon = this.getUiIcons().some((t) => t === icon);
  336. return {
  337. ...item,
  338. icon: existsIcon ? icon : "roundright",
  339. };
  340. });
  341. // 我的应用列表需要过滤掉不存在的按钮
  342. this.myList = myList.filter((t) =>
  343. listData.find((item) => item.F_Id === t)
  344. );
  345. // 发出请求,获取商机信息、消息通知信息、图表信息;三类数据全部同时请求
  346. await Promise.all([
  347. ...settingData.target.map((item) =>
  348. this.HTTP_GET("learun/adms/desktop/data", {
  349. type: "Target",
  350. id: item.F_Id,
  351. }).then((data) => {
  352. if (data && data.value) {
  353. const {
  354. value
  355. } = data;
  356. this.countData.push({
  357. title: item.F_Name,
  358. value,
  359. });
  360. }
  361. })
  362. ),
  363. ...settingData.list.map((item) =>
  364. this.HTTP_GET("learun/adms/desktop/data", {
  365. type: "list",
  366. id: item.F_Id,
  367. }).then((data) => {
  368. if (data && data.value) {
  369. let {
  370. value
  371. } = data;
  372. if (value && value.length) {
  373. value = value.sort((a, b) => {
  374. return new Date(b.f_time).valueOf() - new Date(a.f_time)
  375. .valueOf()
  376. })
  377. }
  378. if (item.F_Name == "待办事项") {
  379. this.CornerMarker = value.length || '0'
  380. }
  381. this.noticeData.push({
  382. title: item.F_Name,
  383. F_Sort:item.F_Sort,
  384. content: value,
  385. });
  386. this.noticeData = this.noticeData.sort((a,b)=>a.F_Sort - b.F_Sort)
  387. }
  388. })
  389. ),
  390. ...settingData.chart.map((item) =>
  391. this.HTTP_GET("learun/adms/desktop/data", {
  392. type: "chart",
  393. id: item.F_Id,
  394. }).then((data) => {
  395. if (data && data.value) {
  396. const {
  397. value
  398. } = data;
  399. this.chartData.push({
  400. title: item.F_Name,
  401. value,
  402. id: item.F_Id,
  403. type: item.F_Type,
  404. });
  405. }
  406. })
  407. ),
  408. ]);
  409. // 渲染图表
  410. this.chartData.forEach((item) => {
  411. // 根据 item.type 的值选用对应的图表初始化配置项
  412. // 0=环形图;1=折线图;2=柱状图
  413. const charts = new uCharts(
  414. [{
  415. ...chartsConfig,
  416. canvasId: item.id,
  417. type: "ring",
  418. series: item.value.map((t) => ({
  419. name: t.name,
  420. data: t.value,
  421. })),
  422. extra: {
  423. pie: {
  424. offsetAngle: -45,
  425. ringWidth: 20,
  426. labelWidth: 15,
  427. },
  428. },
  429. legend: {
  430. lineHeight: 20,
  431. },
  432. },
  433. {
  434. ...chartsConfig,
  435. canvasId: item.id,
  436. type: "line",
  437. series: [{
  438. name: item.title,
  439. data: item.value.map((t) => t.value),
  440. }, ],
  441. categories: item.value.map((t) => t.name),
  442. extra: {
  443. line: {
  444. type: "straight",
  445. },
  446. },
  447. xAxis: {
  448. rotateLabel: true,
  449. fontSize: this.chartsFontSize,
  450. itemCount: 8,
  451. },
  452. enableScroll: true,
  453. },
  454. {
  455. ...chartsConfig,
  456. canvasId: item.id,
  457. type: "column",
  458. series: [{
  459. name: item.title,
  460. data: item.value.map((t) => t.value),
  461. }, ],
  462. categories: item.value.map((t) => t.name),
  463. xAxis: {
  464. rotateLabel: true,
  465. fontSize: this.chartsFontSize,
  466. },
  467. },
  468. ][item.type]
  469. );
  470. chartsObject[item.id] = charts;
  471. });
  472. },
  473. // 初始化图表
  474. initCharts() {
  475. // (阿里小程序)设置图表尺寸为2倍
  476. // #ifdef MP-ALIPAY
  477. const pixelRatio = uni.getSystemInfoSync().pixelRatio;
  478. if (pixelRatio > 1) {
  479. this.pixelRatio = 2;
  480. this.chartsFontSize = 15;
  481. }
  482. // #endif
  483. this.cWidth = uni.upx2px(750);
  484. this.cHeight = uni.upx2px(500);
  485. chartsConfig = {
  486. $this: this,
  487. pixelRatio: this.pixelRatio,
  488. width: this.cWidth * this.pixelRatio,
  489. height: this.cHeight * this.pixelRatio,
  490. background: "#FFFFFF",
  491. dataLabel: true,
  492. padding: [20, 15, 5, 15],
  493. };
  494. },
  495. // 图表点击事件
  496. chartTap(chartItem, e) {
  497. const format = ({
  498. name,
  499. data
  500. }, category) =>
  501. `${category || ""} ${name}: ${data}`;
  502. // #ifdef MP-DINGTALK
  503. const detail = e.detail;
  504. const item = detail;
  505. e.mp.changedTouches = [item];
  506. // #endif
  507. chartsObject[chartItem.id].showToolTip(e, {
  508. format,
  509. });
  510. },
  511. // 图表拖动事件(开始)
  512. touchStart(chartId, e) {
  513. chartsObject[chartId].scrollStart(e);
  514. },
  515. // 图表拖动事件(拖动)
  516. touchMove(chartId, e) {
  517. chartsObject[chartId].scroll(e);
  518. },
  519. // 图表拖动事件(结束)
  520. touchEnd(chartId, e) {
  521. chartsObject[chartId].scrollEnd(e);
  522. },
  523. // 格式化日期的显示(新闻列表、通知公告等)
  524. postDateTime(timeStr) {
  525. return moment(timeStr).fromNow();
  526. },
  527. // 点击功能按钮
  528. funcListClick(item) {
  529. if (item.F_IsSystem === 2) {
  530. this.NAV_TO(
  531. `/pages/customapp/list?formId=${item.F_FormId}`,
  532. item,
  533. true
  534. );
  535. return;
  536. }
  537. this.NAV_TO(`/pages/${item.F_Url}/list`);
  538. },
  539. // 点击通知公告的标题
  540. async noticeClick(item) {
  541. if (item.f_category !== undefined) {
  542. this.NAV_TO("/pages/home/notice", item, true);
  543. } else {
  544. // const result = await this.HTTP_GET(
  545. // 'learun/adms/newwf/mytask',
  546. // {
  547. // pagination:{"rows":100,"page":1,"sidx":"F_CreateDate","sord":"DESC"},
  548. // queryJson: JSON.stringify({keyword:item.f_title})
  549. // },
  550. // '加载任务时出错'
  551. // )
  552. // if(!result){
  553. // return
  554. // }
  555. // this.processId = this.currentTask.F_Id
  556. // this.taskId = this.currentTask.F_TaskId
  557. let params = {
  558. F_Id: item.f_processid,
  559. F_TaskId: item.f_id,
  560. mark: "pre",
  561. F_Title: item.f_title,
  562. }
  563. this.NAV_TO('/pages/nworkflow/myflow/single', params, true)
  564. }
  565. },
  566. informClick(titleName) {
  567. if (titleName == "通知公告") {
  568. this.NAV_TO("/pages/LR_OAModule/list", "", true);
  569. }
  570. },
  571. // #ifndef H5
  572. // 点击左上角扫码图标,H5 无此功能
  573. scanClick() {
  574. uni.scanCode({
  575. scanType: ["qrCode", "barCode"],
  576. success: ({
  577. result,
  578. charSet
  579. }) => {
  580. // 您可以在这里自行定制扫码后的功能
  581. },
  582. });
  583. },
  584. // #endif
  585. // 点击更多功能按钮
  586. moreClick(openSearch) {
  587. this.NAV_TO(`/pages/home/more${openSearch ? "?search=1" : ""}`);
  588. },
  589. // 点击右上角的消息按钮
  590. msgClick() {
  591. this.TAB_TO("/pages/msg");
  592. },
  593. },
  594. computed: {
  595. // 「我的功能」区域按钮列表
  596. funcListDisplay() {
  597. const {
  598. myList,
  599. listData
  600. } = this;
  601. const myFuncList = myList.reduce((list, id) => {
  602. if (listData.find((t) => t.F_Id === id)) {
  603. return [...list, listData.find((t) => t.F_Id === id)];
  604. }
  605. return list;
  606. }, []);
  607. return myFuncList;
  608. },
  609. // imgData 数组的长度
  610. imgCount() {
  611. return this.imgData.length;
  612. },
  613. },
  614. };
  615. </script>
  616. <style lang="less" scoped>
  617. .page {
  618. background-color: #f3f3f3;
  619. .header {
  620. height: 100rpx;
  621. background-color: #0c86d8;
  622. display: flex;
  623. align-items: center;
  624. .header-left {
  625. flex-grow: 0;
  626. }
  627. .header-mid {
  628. flex-grow: 1;
  629. background-color: #3d9ee0;
  630. line-height: 60rpx;
  631. height: 60rpx;
  632. font-size: 24rpx;
  633. color: #fff;
  634. border-radius: 6rpx;
  635. }
  636. .header-right {
  637. flex-grow: 0;
  638. }
  639. }
  640. .content {
  641. background-color: #fff;
  642. }
  643. .function-list {
  644. padding-bottom: 0;
  645. .cu-item {
  646. .app-item {
  647. border-radius: 50%;
  648. height: 45px;
  649. width: 45px;
  650. }
  651. &:nth-child(7n + 1)>.app-item {
  652. background-color: #62bbff;
  653. }
  654. &:nth-child(7n + 2)>.app-item {
  655. background-color: #7bd2ff;
  656. }
  657. &:nth-child(7n + 3)>.app-item {
  658. background-color: #ffd761;
  659. }
  660. &:nth-child(7n + 4)>.app-item {
  661. background-color: #fe955c;
  662. }
  663. &:nth-child(7n + 5)>.app-item {
  664. background-color: #ff6283;
  665. }
  666. &:nth-child(7n + 6)>.app-item {
  667. background-color: #60e3f3;
  668. }
  669. &:nth-child(7n)>.app-item {
  670. background-color: #acc8fe;
  671. }
  672. }
  673. }
  674. .function-more-btn {
  675. display: inline-block;
  676. border: currentColor 1px solid;
  677. border-radius: 2px;
  678. padding: 10rpx 50rpx;
  679. }
  680. .count-list {
  681. &:after {
  682. content: "";
  683. clear: both;
  684. display: table;
  685. }
  686. .count-item-value {
  687. color: #0188d2;
  688. font-size: 24px;
  689. }
  690. }
  691. .chart-list {
  692. background-color: #fff;
  693. overflow: hidden;
  694. }
  695. }
  696. </style>
  697. <style lang="less">
  698. #home {
  699. width: 750rpx;
  700. overflow-x: hidden;
  701. .function-list .cu-item text[class*="cuIcon"] {
  702. margin-top: 0 !important;
  703. }
  704. }
  705. .CornerMarker {
  706. padding: 1px 2px;
  707. position: absolute;
  708. right: 0px;
  709. top: 0px;
  710. background-color: red;
  711. min-width: 14px;
  712. border-radius: 7px;
  713. font-size: 12px;
  714. color: #fff;
  715. }
  716. </style>