index.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842
  1. <template>
  2. <div class="header-actions" v-if="getActionList.length != 0">
  3. <div class="overflow-box">
  4. <el-button
  5. v-for="(item, index) in getActionList"
  6. :key="index"
  7. :type="item.type || 'primary'"
  8. :plain="item.plain || false"
  9. v-bind="getHeaderActions(item)"
  10. @click="item.action"
  11. :disabled="item.disabled || false"
  12. >
  13. {{ item.text }}
  14. </el-button>
  15. </div>
  16. </div>
  17. <div
  18. class="stat-warp"
  19. v-if="statConfig.length != 0"
  20. :class="statWarpHeight > 200 && isMore ? 'show-more' : ''"
  21. >
  22. <div class="title">
  23. <select
  24. v-model="statSelectVal"
  25. v-if="statConfig.length > 1"
  26. @change="changeStatData"
  27. >
  28. <option :value="index" v-for="(i, index) in statConfig" :key="index">
  29. {{ i.label }}
  30. </option>
  31. </select>
  32. <div v-if="statConfig.length === 1">{{ statConfig[0].label }}</div>
  33. </div>
  34. <div class="more-btn">
  35. <span @click="isMore = !isMore" v-if="statWarpHeight > 200">
  36. {{ isMore ? "收起" : "展开" }}
  37. <i
  38. class="el-icon-arrow-down"
  39. :class="isMore ? 'el-icon-arrow-up' : ''"
  40. ></i>
  41. </span>
  42. </div>
  43. <ul id="statWarp">
  44. <li
  45. v-show="!i.data"
  46. :class="'theme' + i.type"
  47. v-for="(i, index) in statConfig[statSelectVal].data"
  48. :key="index"
  49. @click="i.click ? i.click(i, index) : ''"
  50. :style="i.click ? 'cursor: pointer' : ''"
  51. >
  52. <div class="label">{{ i.label }}</div>
  53. <div class="num">{{ i.num }}</div>
  54. </li>
  55. <li
  56. v-show="i.data"
  57. v-for="(i, index) in statConfig[statSelectVal].data"
  58. :key="index"
  59. class="multi-data"
  60. :class="'theme' + i.type"
  61. @click="i.click ? i.click(i, index) : ''"
  62. :style="i.click ? 'cursor: pointer' : ''"
  63. >
  64. <div class="label">{{ i.label }}</div>
  65. <div class="num-warp">
  66. <div class="num-box" v-for="(j, jindex) in i.data" :key="jindex">
  67. <div class="num-small" :style="'color:' + j.color">
  68. {{ j.num }}
  69. </div>
  70. <div class="label-small">{{ j.label }}</div>
  71. </div>
  72. </div>
  73. </li>
  74. </ul>
  75. </div>
  76. <div class="table-list-container by-table">
  77. <!-- v-if="!hideHeader" -->
  78. <header v-if="false" class="header">
  79. <h2>{{ title }}</h2>
  80. </header>
  81. <div class="by-search" v-if="!hideSearch">
  82. <div style="display: flex">
  83. <div
  84. class="by-dropdown"
  85. v-for="(i, index) in selectConfigCopy"
  86. :key="i.prop"
  87. style="margin-right: 10px"
  88. >
  89. <div class="by-dropdown-title">
  90. {{
  91. pagination[i.prop]
  92. ? i.data.find((j) => j.value === pagination[i.prop])
  93. ? i.data.find((j) => j.value === pagination[i.prop]).label
  94. : i.label
  95. : i.labelCopy
  96. }}
  97. <!-- {{ i.label || i.labelCopy }} -->
  98. <i style="margin-left: 5px" class="iconfont icon-iconm_xialan1"></i>
  99. </div>
  100. <ul class="by-dropdown-lists">
  101. <li
  102. @click="searchItemSelct('all', i, index)"
  103. v-if="i.isShowAll === false ? i.isShowAll : true"
  104. style="
  105. display: flex;
  106. align-items: center;
  107. justify-content: center;
  108. "
  109. >
  110. {{ $t("common.all") }}
  111. </li>
  112. <li
  113. v-for="j in i.data"
  114. :key="j.value"
  115. @click="searchItemSelct(j, i)"
  116. style="
  117. display: flex;
  118. align-items: center;
  119. justify-content: center;
  120. "
  121. >
  122. {{ j.label }}
  123. </li>
  124. </ul>
  125. </div>
  126. </div>
  127. <div style="display: flex">
  128. <el-input
  129. :placeholder="$t('common.pleaseEnterKeywords')"
  130. suffix-icon="search"
  131. size="mini"
  132. v-model="pagination.keyword"
  133. @keyup.enter="searchFn"
  134. >
  135. </el-input>
  136. <el-button
  137. type="primary"
  138. style="margin-left: 10px"
  139. size="default"
  140. @click="searchFn"
  141. >{{ $t("common.search") }}</el-button
  142. >
  143. <div
  144. class="more-icon"
  145. @click="retrievalModalFn"
  146. v-if="$attrs.onMoreSearch"
  147. >
  148. <i class="iconfont icon-iconx_saixuan"></i>
  149. </div>
  150. </div>
  151. </div>
  152. <component :is="containerTag">
  153. <div class="filter-form-container">
  154. <slot />
  155. </div>
  156. <el-table
  157. ref="hocElTable"
  158. v-loading="loading"
  159. :data="source"
  160. v-if="!hideTable"
  161. style="width: 100%"
  162. v-bind="$attrs"
  163. v-on="tableEvents"
  164. row-key="id"
  165. :tree-props="{
  166. children: 'children',
  167. hasChildren: 'hasChildren',
  168. }"
  169. :height="tableHeight"
  170. >
  171. <el-table-column
  172. v-for="(item, index) in config"
  173. :key="index"
  174. v-bind="getAttrsValue(item)"
  175. :type="item.type || ''"
  176. :selectable="
  177. (rowData, rowIndex) => isSelectable(rowData, rowIndex, item)
  178. "
  179. >
  180. <template #default="scope" v-if="!item.type">
  181. <slot
  182. :name="item.attrs.slot"
  183. :item="scope.row"
  184. v-if="item.attrs.slot"
  185. >
  186. 插槽占位符
  187. </slot>
  188. <div v-else-if="isFunction(getValue(scope, item))">
  189. <component
  190. :is="renderTypeList[getMatchRenderFunction(item)].target"
  191. :cell-list="getValue(scope, item)()"
  192. :row="scope.row"
  193. :parent="getParent"
  194. @click="
  195. ($event) => {
  196. handleNativeClick(getAttrsValue(item), $event, item);
  197. }
  198. "
  199. />
  200. </div>
  201. <div v-else>
  202. {{ getValue(scope, item) }}
  203. </div>
  204. </template>
  205. </el-table-column>
  206. </el-table>
  207. <el-row
  208. v-if="!hidePagination"
  209. class="table-pagination"
  210. justify="end"
  211. type="flex"
  212. >
  213. <el-pagination
  214. background
  215. layout="total, sizes, prev, pager, next, jumper"
  216. :current-page="getPagination.pageNum"
  217. :page-size="getPagination.pageSize"
  218. :total="getPagination.total"
  219. @size-change="handleSizeChange"
  220. @current-change="handlePageChange"
  221. />
  222. </el-row>
  223. </component>
  224. </div>
  225. </template>
  226. <script>
  227. import { isFunction as isFn, isBoolean } from "./type";
  228. import ElementsMapping from "./ElementsMapping";
  229. import ComponentsMapping from "./ComponentsMapping";
  230. import {
  231. computed,
  232. defineComponent,
  233. getCurrentInstance,
  234. nextTick,
  235. ref,
  236. watch,
  237. } from "vue";
  238. import expand from "./expand";
  239. import Sortable from "sortablejs";
  240. export default defineComponent({
  241. name: "Table",
  242. components: {
  243. ElementsMapping,
  244. ComponentsMapping,
  245. },
  246. props: {
  247. hideSearch: {
  248. type: Boolean,
  249. default: false,
  250. },
  251. hideTable: {
  252. type: Boolean,
  253. default: false,
  254. },
  255. //顶部搜索下拉配置
  256. selectConfig: {
  257. type: Array,
  258. default() {
  259. return [];
  260. },
  261. },
  262. // 获取表格元数据时携带的参数
  263. filterParams: {
  264. type: Object,
  265. default() {
  266. return {};
  267. },
  268. },
  269. // 表格加载 loading
  270. loading: {
  271. type: Boolean,
  272. default: false,
  273. },
  274. // 表格名称
  275. title: {
  276. type: String,
  277. default: "",
  278. },
  279. // 表格元数据
  280. source: {
  281. type: Array,
  282. required: true,
  283. default() {
  284. return [];
  285. },
  286. },
  287. tableHeight: {
  288. type: Number,
  289. required: false,
  290. },
  291. searchConfig: {
  292. type: Object,
  293. default() {
  294. return {
  295. keyword: "",
  296. };
  297. },
  298. },
  299. statConfig: {
  300. type: Array,
  301. default() {
  302. return [];
  303. },
  304. },
  305. // 指定外层容器的渲染组件
  306. containerTag: {
  307. type: String,
  308. default: "div",
  309. },
  310. // 是否隐藏表头
  311. hideHeader: {
  312. type: Boolean,
  313. default: false,
  314. },
  315. // 是否隐藏分页
  316. hidePagination: {
  317. type: Boolean,
  318. default: false,
  319. },
  320. // 分页配置
  321. pagination: {
  322. type: Object,
  323. default() {
  324. return {};
  325. },
  326. },
  327. // 表格配置文件
  328. config: {
  329. type: Array,
  330. default() {
  331. return [];
  332. },
  333. },
  334. // 表头右上方的按钮组
  335. actionList: {
  336. type: Array,
  337. default() {
  338. return [{ text: "", action: () => {} }];
  339. },
  340. },
  341. // element table 原生事件
  342. tableEvents: {
  343. type: Object,
  344. default() {
  345. return {};
  346. },
  347. },
  348. searchKey: {
  349. type: String,
  350. default: "keyword",
  351. },
  352. // 是否显示过滤的全部选项
  353. // isShowAll: {
  354. // type: Boolean,
  355. // default: true,
  356. // },
  357. },
  358. setup(props) {
  359. const { proxy } = getCurrentInstance();
  360. const selectConfigCopy = computed(() => {
  361. return props.selectConfig.map((item) => {
  362. if (!item.labelCopy) item.labelCopy = { ...item }.label;
  363. return item;
  364. });
  365. });
  366. let isMore = ref(true);
  367. const changeStatData = () => {
  368. nextTick(() => {
  369. statWarpHeight.value = document.getElementById("statWarp").offsetHeight;
  370. console.log(statWarpHeight.value, "awda");
  371. });
  372. };
  373. let statWarpHeight = ref(0);
  374. watch(
  375. proxy.statConfig,
  376. (newValue, oldValue) => {
  377. setTimeout(() => {
  378. //获取statWarp的height
  379. statWarpHeight.value =
  380. document.getElementById("statWarp").offsetHeight;
  381. }, 500);
  382. },
  383. { immediate: true }
  384. );
  385. let statSelectVal = ref(0);
  386. const retrievalModal = ref(false);
  387. const getAttrsValue = (item) => {
  388. const { attrs } = item;
  389. const result = {
  390. ...attrs,
  391. };
  392. delete result.prop;
  393. return result;
  394. };
  395. const renderTypeList = ref({
  396. render: {},
  397. renderHTML: {
  398. target: "elements-mapping",
  399. },
  400. renderComponent: {
  401. target: "components-mapping",
  402. },
  403. renderMoreBtn: {
  404. target: "more-btn",
  405. },
  406. });
  407. const getParent = computed(() => {
  408. return proxy.$parent;
  409. });
  410. const getPagination = computed(() => {
  411. const params = {
  412. pageNum: 1,
  413. pageSize: 10,
  414. total: 0,
  415. };
  416. return Object.assign({}, params, props.pagination);
  417. });
  418. const getActionList = computed(() => {
  419. return props.actionList
  420. .slice()
  421. .reverse()
  422. .filter((it) => it.text);
  423. });
  424. const getValue = (scope, configItem) => {
  425. const prop = configItem.attrs.prop;
  426. const renderName = getMatchRenderFunction(configItem);
  427. const renderObj = renderTypeList.value[renderName];
  428. if (renderObj && isFunction(configItem[renderName])) {
  429. return renderObj.target
  430. ? getRenderValue(scope, configItem, {
  431. name: renderName,
  432. type: "bind",
  433. })
  434. : getRenderValue(scope, configItem);
  435. }
  436. return scope.row[prop];
  437. };
  438. const getRenderValue = (
  439. scope,
  440. item,
  441. fn = { name: "render", type: "call" }
  442. ) => {
  443. const prop = item.attrs.prop;
  444. const propValue = prop && scope.row[prop];
  445. scope.row.$index = scope.$index;
  446. const args = propValue !== undefined ? propValue : scope.row;
  447. return item[fn.name][fn.type](getParent.value, args);
  448. };
  449. // 匹配 render 开头的函数
  450. const getMatchRenderFunction = (obj) => {
  451. return Object.keys(obj).find((key) => {
  452. const matchRender = key.match(/^render.*/);
  453. return matchRender && matchRender[0];
  454. });
  455. };
  456. const isFunction = (fn) => {
  457. return isFn(fn);
  458. };
  459. const searchFn = (val) => {
  460. proxy.$emit(
  461. "getList",
  462. Object.assign(props.filterParams, {
  463. [props.searchKey]: props.pagination.keyword,
  464. })
  465. );
  466. };
  467. const retrievalModalFn = () => {
  468. proxy.$emit("moreSearch", "");
  469. //获取父组件定义的moreSearch方法
  470. };
  471. const handlePageChange = (val) => {
  472. proxy.$emit(
  473. "getList",
  474. Object.assign(props.filterParams, { pageNum: val })
  475. );
  476. };
  477. const handleSizeChange = (val) => {
  478. proxy.$emit(
  479. "getList",
  480. Object.assign(props.filterParams, { pageSize: val })
  481. );
  482. };
  483. const getHeaderActions = (item) => {
  484. return {
  485. ...item.attrs,
  486. };
  487. };
  488. const stopBubbles = (e) => {
  489. const event = e || window.event;
  490. if (event && event.stopPropagation) {
  491. event.stopPropagation();
  492. } else {
  493. event.cancelBubble = true;
  494. }
  495. };
  496. const handleNativeClick = ({ isBubble }, e, item) => {
  497. // 考虑到单元格内渲染了组件,并且组件自身可能含有点击事件,故添加了阻止冒泡机制
  498. // 若指定 isBubble 为 false,则当前单元格恢复冒泡机制
  499. if (isBoolean(isBubble) && !isBubble) return;
  500. stopBubbles(e);
  501. };
  502. //下拉搜索相关
  503. const searchItemSelct = (item, i, index) => {
  504. if (item == "all") {
  505. i.label = { ...props.selectConfig[index] }.labelCopy;
  506. proxy.$emit(
  507. "getList",
  508. Object.assign(props.filterParams, { [i.prop]: "" })
  509. );
  510. return;
  511. }
  512. i.label = item.label;
  513. proxy.$emit(
  514. "getList",
  515. Object.assign(props.filterParams, { [i.prop]: item.value })
  516. );
  517. };
  518. const isSelectable = (row, index, item) => {
  519. if (item.type === "selection") {
  520. if (item.attrs && item.attrs.checkAtt) {
  521. if (row[item.attrs.checkAtt]) {
  522. return row[item.attrs.checkAtt];
  523. }
  524. } else {
  525. return true;
  526. }
  527. }
  528. };
  529. const hocElTable = ref();
  530. return {
  531. getParent,
  532. getPagination,
  533. renderTypeList,
  534. getActionList,
  535. getAttrsValue,
  536. getValue,
  537. getRenderValue,
  538. getMatchRenderFunction,
  539. isFunction,
  540. handlePageChange,
  541. handleSizeChange,
  542. getHeaderActions,
  543. stopBubbles,
  544. handleNativeClick,
  545. searchFn,
  546. searchItemSelct,
  547. selectConfigCopy,
  548. isSelectable,
  549. retrievalModal,
  550. retrievalModalFn,
  551. statSelectVal,
  552. statWarpHeight,
  553. isMore,
  554. changeStatData,
  555. hocElTable,
  556. };
  557. },
  558. });
  559. </script>
  560. <style>
  561. .table-list-container th {
  562. color: #333 !important;
  563. }
  564. .by-table td .el-button + .el-button {
  565. margin-left: 0 !important;
  566. }
  567. .by-table td .el-button {
  568. background: none !important;
  569. margin: 0 !important;
  570. padding: 8px 6px !important;
  571. }
  572. .el-checkbox__input.is-disabled .el-checkbox__inner {
  573. background-color: #dee1e6;
  574. border-color: #b2b4b9;
  575. }
  576. .el-table .cell {
  577. line-height: 34px;
  578. }
  579. </style>
  580. <style lang="scss" scoped>
  581. .sortableActive {
  582. background: #f5f7fa !important;
  583. }
  584. .show-more {
  585. height: auto !important;
  586. }
  587. .stat-warp {
  588. margin-bottom: 20px;
  589. background: #fff;
  590. padding: 0 20px;
  591. height: 200px;
  592. overflow: hidden;
  593. position: relative;
  594. .more-btn {
  595. position: absolute;
  596. right: 0;
  597. top: 0;
  598. width: 40px;
  599. height: 30px;
  600. cursor: pointer;
  601. font-size: 12px;
  602. line-height: 30px;
  603. text-align: center;
  604. }
  605. .title {
  606. height: 60px;
  607. select {
  608. height: 60px;
  609. border: none;
  610. outline: none;
  611. -webkit-appearance: none;
  612. appearance: none;
  613. font-size: 14px;
  614. font-weight: bold;
  615. background: url("@/assets/images/sanjiao.png") no-repeat right center;
  616. padding-right: 20px;
  617. }
  618. div {
  619. height: 60px;
  620. font-size: 14px;
  621. font-weight: bold;
  622. line-height: 60px;
  623. }
  624. }
  625. ul {
  626. padding: 0;
  627. overflow: hidden;
  628. margin: 0;
  629. li {
  630. list-style: none;
  631. min-width: 285px;
  632. box-sizing: border-box;
  633. margin: 0 20px 20px 0;
  634. background: #eff6ff;
  635. float: left;
  636. overflow: hidden;
  637. padding: 20px;
  638. color: #333333;
  639. border-radius: 10px;
  640. .label {
  641. font-size: 14px;
  642. }
  643. .label::before {
  644. width: 10px;
  645. height: 10px;
  646. content: "";
  647. border-radius: 50%;
  648. background: #0084ff;
  649. display: inline-block;
  650. margin-right: 10px;
  651. }
  652. .num {
  653. margin-top: 10px;
  654. font-size: 24px;
  655. font-weight: bold;
  656. }
  657. }
  658. //#F5F3FF #9E64ED
  659. .theme2 {
  660. background: #f5f3ff;
  661. .label::before {
  662. background: #9e64ed;
  663. }
  664. }
  665. //#FFF1E1 #FF9315
  666. .theme3 {
  667. background: #fff1e1;
  668. .label::before {
  669. background: #ff9315;
  670. }
  671. }
  672. //#E2FBE8 #39C55A
  673. .theme4 {
  674. background: #e2fbe8;
  675. .label::before {
  676. background: #39c55a;
  677. }
  678. }
  679. .theme5 {
  680. background: #ffebe9;
  681. .label::before {
  682. background: #f94539;
  683. }
  684. }
  685. .theme6 {
  686. background: #e4f9f9;
  687. .label::before {
  688. background: #53cbcb;
  689. }
  690. }
  691. .multi-data {
  692. .label::before {
  693. display: none;
  694. }
  695. .label {
  696. font-size: 14px;
  697. font-weight: bold;
  698. color: #333;
  699. margin-bottom: 8px;
  700. }
  701. .num-warp {
  702. overflow: hidden;
  703. .num-box {
  704. float: left;
  705. min-width: 80px;
  706. margin-right: 20px;
  707. .num-small {
  708. font-size: 16px;
  709. font-weight: bold;
  710. margin-bottom: 8px;
  711. }
  712. .label-small {
  713. color: #666;
  714. font-size: 14px;
  715. }
  716. }
  717. }
  718. }
  719. }
  720. }
  721. .by-search {
  722. display: flex;
  723. justify-content: space-between;
  724. margin-bottom: 10px;
  725. .more-icon {
  726. float: right;
  727. cursor: pointer;
  728. line-height: 32px;
  729. text-align: center;
  730. margin-left: 5px;
  731. }
  732. }
  733. .by-dropdown {
  734. position: relative;
  735. text-align: left;
  736. height: 32px;
  737. z-index: 1010;
  738. padding: 0 10px;
  739. transition: all 0.5s ease;
  740. cursor: pointer;
  741. line-height: 32px;
  742. .by-dropdown-title {
  743. font-size: 14px;
  744. background-color: #fff;
  745. }
  746. ul {
  747. position: absolute;
  748. left: 0;
  749. top: 32px;
  750. padding: 0;
  751. margin: 0;
  752. z-index: 100;
  753. display: none;
  754. white-space: nowrap;
  755. li {
  756. list-style: none;
  757. font-size: 12px;
  758. height: 30px;
  759. padding: 0 10px;
  760. }
  761. li:hover {
  762. background-color: #eff6ff;
  763. color: #0084ff;
  764. }
  765. }
  766. }
  767. .by-dropdown::before {
  768. display: block;
  769. width: 1px;
  770. content: " ";
  771. position: absolute;
  772. height: 14px;
  773. top: 8px;
  774. background-color: #ddd;
  775. right: 0;
  776. z-index: 1011;
  777. }
  778. .by-dropdown:hover {
  779. background: #ffffff;
  780. border-radius: 2px 2px 2px 2px;
  781. opacity: 1;
  782. ul {
  783. background: #ffffff;
  784. box-shadow: 0px 2px 16px 1px rgba(0, 0, 0, 0.06);
  785. border-radius: 2px 2px 2px 2px;
  786. opacity: 1;
  787. display: block;
  788. text-align: left;
  789. }
  790. }
  791. .header-actions {
  792. flex: 1;
  793. overflow-x: auto;
  794. padding: 20px;
  795. background: #fff;
  796. margin-bottom: 20px;
  797. .overflow-box {
  798. :deep() .el-button:nth-child(1) {
  799. margin-left: 10px;
  800. }
  801. }
  802. }
  803. .table-list-container {
  804. background: #fff;
  805. padding: 13px 20px 20px;
  806. .table-pagination {
  807. padding-top: 20px;
  808. }
  809. .header {
  810. display: flex;
  811. padding-bottom: 20px;
  812. }
  813. .el-table {
  814. :deep() th {
  815. font-size: 14px;
  816. }
  817. :deep() td {
  818. font-size: 14px;
  819. }
  820. }
  821. }
  822. .by-dropdown-lists {
  823. max-height: 50vh;
  824. overflow-y: auto;
  825. line-height: 1;
  826. }
  827. </style>