自主项目,食堂系统,前端uniapp
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.

1218 lines
34 KiB

  1. <template>
  2. <view class="wyb-table-box">
  3. <view v-if="loading" class="wyb-table-loading-box" :style="{
  4. 'max-width': width === 'auto' ? screenWidth : width,
  5. 'max-height': height === 'auto' ? '300rpx' : height,
  6. backgroundColor: loaderBgColor,
  7. borderTop: '1px solid' + borderColor,
  8. borderBottom: '1px solid' + borderColor,
  9. borderLeft: showLeftAndRightBorder ? '1px solid' + borderColor : 'none',
  10. borderRight: showLeftAndRightBorder ? '1px solid' + borderColor : 'none'}">
  11. <view class="loader-one" :style="{
  12. width: loaderSize + 'rpx',
  13. height: loaderSize + 'rpx',
  14. borderTop: '3px solid ' + loadingColor.top,
  15. borderRight: '3px solid ' + loadingColor.right,
  16. borderBottom: '3px solid ' + loadingColor.bottom,
  17. borderLeft: '3px solid ' + loadingColor.left}" />
  18. </view>
  19. <view v-if="!loading" class="wyb-table-scroll-view" :style="{
  20. 'max-width': width,
  21. 'max-height': height}">
  22. <!-- borderTop: '1px solid' + borderColor,
  23. borderLeft: showLeftAndRightBorder ? '1px solid' + borderColor : 'none',
  24. borderRight: showLeftAndRightBorder ? '1px solid' + borderColor : 'none' -->
  25. <view class="wyb-table-header" :style="{borderBottom: '1px solid' + borderColor}">
  26. <view class="wyb-table-header-item" v-if="enableCheck" :style="{
  27. minWidth: checkColWidth + 'rpx',
  28. maxWidth: checkColWidth + 'rpx',
  29. minHeight: minHeight[0] + 'rpx',
  30. textAlign: textAlign,
  31. justifyContent: textAlign === 'center' ? textAlign : (textAlign === 'left' ? 'flex-start' : 'flex-end'),
  32. fontSize: fontSize[0] + 'rpx',
  33. color: headerFtColor,
  34. padding: padding[0] + 'rpx ' + (padding[1] || padding[0]) + 'rpx',
  35. backgroundColor: headerBgColor,
  36. borderRight: '1px solid' + borderColor,
  37. zIndex: 30,
  38. left: 0,
  39. color: headerFtColor,
  40. backgroundColor: headerBgColor,
  41. position: 'sticky'}">
  42. <view
  43. class="wyb-table-checkbox"
  44. v-if="enableCheck === 'multiple'"
  45. @tap.stop="onCheckAllTap"
  46. :style="{
  47. width: checkColWidth * 0.5 + 'rpx',
  48. height: checkColWidth * 0.5 + 'rpx',
  49. backgroundColor: checkerBoxBgColor,
  50. border: '1px solid ' + checkerBorderColor}">
  51. <text
  52. class="iconfont icon-check"
  53. v-show="checkAll"
  54. :style="{
  55. color: checkerColor,
  56. backgroundColor: checkerBgColor,
  57. paddingTop: (fontSize[1] || fontSize[0]) * 0.15 + 'rpx',
  58. fontSize: (fontSize[1] || fontSize[0]) + 'rpx'}" />
  59. </view>
  60. </view>
  61. <view ref="iosBug" class="wyb-table-header-item" v-for="(item, index) in headers" :key="item.key" @tap="onHeaderItemTap(index)"
  62. :style="{
  63. minWidth: (item.width || defaultColWidth) + 'rpx',
  64. maxWidth: (item.width || defaultColWidth) + 'rpx',
  65. minHeight: minHeight[0] + 'rpx',
  66. textAlign: textAlign,
  67. justifyContent: textAlign === 'center' ? textAlign : (textAlign === 'left' ? 'flex-start' : 'flex-end'),
  68. fontSize: fontSize[0] + 'rpx',
  69. fontWeight: headerWeight ? 'bold' : 'normal',
  70. color: headerFtColor,
  71. padding: padding[0] + 'rpx ' + (padding[1] || padding[0]) + 'rpx',
  72. backgroundColor: headerBgColor,
  73. borderRight: index === headers.length - 1 || (!showVertBorder && index !== 0) ? 'none' : '1px solid' + borderColor,
  74. zIndex: index === 0 ? 20 : 0,
  75. left: index === 0 && firstLineFixed ? (enableCheck ? checkColWidth + 'rpx' : 0) : 'auto',
  76. position: index === 0 ? 'sticky' : 'static'}">
  77. <text :style="{marginLeft: autoSortShow(index) && textAlign !== 'left' ? fontSize[0] * 0.65 + 'rpx' : 0}">
  78. {{item.label || emptyString}}
  79. </text>
  80. <view class="wyb-table-header-icon" v-if="autoSortShow(index)">
  81. <text class="iconfont icon-arrow-up" :style="{
  82. color: sortWays[sortWay] === 'asc' && sortActiveKey === item.key ?
  83. headerFtColor : RGBChange(headerFtColor, 0.7, 'light'),
  84. fontWeight: 'normal',
  85. marginBottom: '-12px',
  86. transform: 'scale(0.4)'}" />
  87. <text class="iconfont icon-arrow-down" :style="{
  88. color: sortWays[sortWay] === 'inv' && sortActiveKey === item.key ?
  89. headerFtColor : RGBChange(headerFtColor, 0.7, 'light'),
  90. fontWeight: 'normal',
  91. transform: 'scale(0.4)'}" />
  92. </view>
  93. </view>
  94. </view>
  95. <view class="wyb-table-content">
  96. <view class="wyb-table-content-line" v-for="(content, cIndex) in contentsSort" :key="contentLineKey(content, cIndex)"
  97. :style="{borderTop: cIndex === 0 ? 'none' : '1px solid' + borderColor}">
  98. <view class="wyb-table-content-item" v-if="enableCheck" :style="{
  99. minWidth: checkColWidth + 'rpx',
  100. maxWidth: checkColWidth + 'rpx',
  101. textAlign: textAlign,
  102. justifyContent: textAlign === 'center' ? textAlign : (textAlign === 'left' ? 'flex-start' : 'flex-end'),
  103. fontSize: (fontSize[1] || fontSize[0]) + 'rpx',
  104. minHeight: (minHeight[1] || minHeight[0]) + 'rpx',
  105. padding: padding[0] + 'rpx ' + (padding[1] || padding[0]) + 'rpx',
  106. borderRight: '1px solid' + borderColor,
  107. zIndex: 21,
  108. color: contentFtColor,
  109. backgroundColor: checkerCellBgColor,
  110. left: 0,
  111. position: 'sticky'}">
  112. <view
  113. class="wyb-table-checkbox"
  114. @tap.stop="onCheckItemTap(cIndex)"
  115. :style="{
  116. width: checkColWidth * 0.5 + 'rpx',
  117. height: checkColWidth * 0.5 + 'rpx',
  118. backgroundColor: checkerBoxBgColor,
  119. border: '1px solid ' + checkerBorderColor}">
  120. <text
  121. class="iconfont icon-check"
  122. v-show="contentsSort[cIndex].checked"
  123. :style="{
  124. color: checkerColor,
  125. backgroundColor: checkerBgColor,
  126. paddingTop: (fontSize[1] || fontSize[0]) * 0.15 + 'rpx',
  127. fontSize: (fontSize[1] || fontSize[0]) + 'rpx'}" />
  128. </view>
  129. </view>
  130. <view
  131. class="wyb-table-content-item"
  132. v-for="(header, hIndex) in headers"
  133. @tap.stop="onContentItemTap(cIndex, hIndex)"
  134. :key="contentItemKey(header, hIndex)"
  135. :style="{
  136. minWidth: (header.width || defaultColWidth) + 'rpx',
  137. maxWidth: (header.width || defaultColWidth) + 'rpx',
  138. textAlign: textAlign,
  139. justifyContent: textAlign === 'center' ? textAlign : (textAlign === 'left' ? 'flex-start' : 'flex-end'),
  140. fontSize: (fontSize[1] || fontSize[0]) + 'rpx',
  141. textDecoration: autoTextDecoration(cIndex, hIndex),
  142. color: autoContentColor(cIndex, hIndex),
  143. backgroundColor: autoContentBgColor(cIndex, hIndex),
  144. padding: padding[0] + 'rpx ' + (padding[1] || padding[0]) + 'rpx',
  145. borderBottom: cIndex === contents.length - 1 ? '1px solid' + borderColor : 'none',
  146. borderRight: hIndex === headers.length - 1 || (!showVertBorder && hIndex !== 0) ? 'none' : '1px solid' + borderColor,
  147. zIndex: hIndex === 0 ? 20 : 0,
  148. left: enableCheck ? checkColWidth + 'rpx' : 0,
  149. position: hIndex === 0 && firstLineFixed ? 'sticky' : 'static'}">
  150. <input class="lf-w-100" :disabled="false" placeholder="请输入.." v-if="isObject(autoContentItem(cIndex, hIndex), 'edit')" :style="{minHeight: (minHeight[1] || minHeight[0]) + 'rpx'}" @blur="inputBlur(cIndex, hIndex, $event)" :value="autoContentItem(cIndex, hIndex).value" />
  151. <button class="diy-btn" v-else-if="isObject(autoContentItem(cIndex, hIndex), 'button')" @click="buttonClick(cIndex, hIndex)">{{ autoContentItem(cIndex, hIndex).value }}</button>
  152. <view class="lf-w-100 lf-h-100 lf-row-center" v-else :style="{minHeight: (minHeight[1] || minHeight[0]) + 'rpx'}">{{ autoContentItem(cIndex, hIndex) }}</view>
  153. </view>
  154. </view>
  155. <view v-if="computedCol.length !== 0" class="wyb-table-content-line" :style="{
  156. position: bottomComputedFixed ? 'sticky' : 'static',
  157. bottom: 0,
  158. zIndex: 25,
  159. borderTop: '1px solid' + borderColor}">
  160. <view class="wyb-table-content-item" v-if="enableCheck" :style="{
  161. minWidth: checkColWidth + 'rpx',
  162. maxWidth: checkColWidth + 'rpx',
  163. textAlign: textAlign,
  164. justifyContent: textAlign === 'center' ? textAlign : (textAlign === 'left' ? 'flex-start' : 'flex-end'),
  165. fontSize: (fontSize[1] || fontSize[0]) + 'rpx',
  166. minHeight: (minHeight[1] || minHeight[0]) + 'rpx',
  167. padding: padding[0] + 'rpx ' + (padding[1] || padding[0]) + 'rpx',
  168. borderBottom: '1px solid' + borderColor,
  169. borderRight: '1px solid' + borderColor,
  170. zIndex: 25,
  171. color: contentFtColor,
  172. backgroundColor: checkerCellBgColor,
  173. left: 0,
  174. position: 'sticky'}"></view>
  175. <view class="wyb-table-content-item" v-for="(header, index) in headers" :key="index"
  176. :style="{
  177. minWidth: (header.width || defaultColWidth) + 'rpx',
  178. maxWidth: (header.width || defaultColWidth) + 'rpx',
  179. textAlign: textAlign,
  180. justifyContent: textAlign === 'center' ? textAlign : (textAlign === 'left' ? 'flex-start' : 'flex-end'),
  181. fontSize: (fontSize[1] || fontSize[0]) + 'rpx',
  182. color: contentFtColor,
  183. minHeight: (minHeight[1] || minHeight[0]) + 'rpx',
  184. padding: padding[0] + 'rpx ' + (padding[1] || padding[0]) + 'rpx',
  185. backgroundColor: index === 0 ? firstColBgColor : contentBgColor,
  186. borderBottom: '1px solid' + borderColor,
  187. borderRight: index === headers.length - 1 || (!showVertBorder && index !== 0) ? 'none' : '1px solid' + borderColor,
  188. zIndex: index === 0 ? 20 : 0,
  189. left: enableCheck ? checkColWidth + 'rpx' : 0,
  190. position: index === 0 && firstLineFixed ? 'sticky' : 'static'}">
  191. {{autoBottomComputedItem(index)}}
  192. </view>
  193. </view>
  194. </view>
  195. </view>
  196. </view>
  197. </template>
  198. <script>
  199. import Pinyin from './js/characterToPinyin.js'
  200. import {isEqual} from './js/objEqual.js'
  201. export default {
  202. data() {
  203. return {
  204. bottomComputed: [],
  205. colorList: [],
  206. bgColorList: [],
  207. contentsSort: this.toArray(),
  208. oContentsSort: [],
  209. sortWay: 0,
  210. sortKeys: [],
  211. sortActiveKey: '',
  212. sortIsNumbers: [],
  213. checkAll: false,
  214. checkList: [],
  215. onload: true,
  216. event: {
  217. checkType: this.enableCheck,
  218. data: []
  219. },
  220. chars: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
  221. }
  222. },
  223. computed: {
  224. loadingColor() {
  225. let color = this.loaderColor.slice()
  226. let rgbList = this.hexToRgb(color)
  227. let top = 'rgba(' + rgbList[0] + ',' + rgbList[1] + ',' + rgbList[2] + ', 0.3)'
  228. let bottom = 'rgba(' + rgbList[0] + ',' + rgbList[1] + ',' + rgbList[2] + ', 0.3)'
  229. let right = 'rgba(' + rgbList[0] + ',' + rgbList[1] + ',' + rgbList[2] + ', 0.3)'
  230. let left = 'rgb(' + rgbList[0] + ',' + rgbList[1] + ',' + rgbList[2] + ')'
  231. return {
  232. top,
  233. bottom,
  234. right,
  235. left
  236. }
  237. },
  238. contentLineKey() {
  239. return function(content, cIndex) {
  240. return this.randomString(32, this.chars)
  241. }
  242. },
  243. contentItemKey() {
  244. return function(header, hIndex) {
  245. return this.randomString(16, this.chars)
  246. }
  247. },
  248. autoContentItem() {
  249. return function(cIndex, hIndex) {
  250. let content = this.contentsSort[cIndex]
  251. let header = this.headers[hIndex]
  252. let result = ''
  253. if (content[header.key] || content[header.key] === 0) {
  254. result = content[header.key]
  255. if (this.urlCol.length !== 0) {
  256. for (let i in this.urlCol) {
  257. let item = this.urlCol[i]
  258. if (header.key === item.key) {
  259. // 该单元格为链接
  260. result = content[header.key][0]
  261. }
  262. }
  263. }
  264. if (this.formatCol.length !== 0) {
  265. this.formatCol.forEach(item => {
  266. if (header.key === item.key) {
  267. let needRplace = new RegExp(`\#${item['key']}\#`, 'mg')
  268. result = item.template.replace(needRplace, result)
  269. }
  270. })
  271. }
  272. } else {
  273. result = this.emptyString
  274. }
  275. return result
  276. }
  277. },
  278. autoBottomComputedItem() {
  279. return function(index) {
  280. let bottomComputed = {}
  281. let needComputed = []
  282. this.computedCol.forEach(key => {
  283. let computedColData = []
  284. this.contentsSort.forEach(content => {
  285. computedColData.push(content[key] || '0')
  286. })
  287. needComputed.push(computedColData)
  288. })
  289. needComputed.forEach((item, index) => {
  290. let total = 0
  291. item.forEach(num => {
  292. total += parseFloat(num)
  293. })
  294. bottomComputed[this.computedCol[index]] = total
  295. })
  296. let header = this.headers[index]
  297. let result = this.computedCol.includes(header.key) ?
  298. bottomComputed[header.key] : (index === 0 ? '总计' : this.emptyString)
  299. if (this.formatCol.length !== 0) {
  300. this.formatCol.forEach(item => {
  301. if (item.bottomComputedFormat) {
  302. if (header.key === item.key) {
  303. let needRplace = new RegExp(`\#${item['key']}\#`, 'mg')
  304. result = item.template.replace(needRplace, bottomComputed[item.key])
  305. }
  306. }
  307. })
  308. }
  309. return result
  310. }
  311. },
  312. autoTextDecoration() {
  313. return function(cIndex, hIndex) {
  314. let result = 'auto'
  315. let content = this.contentsSort[cIndex]
  316. let header = this.headers[hIndex]
  317. if (this.urlCol.length !== 0) {
  318. for (let i in this.urlCol) {
  319. let item = this.urlCol[i]
  320. if (header.key === item.key) {
  321. // 该单元格为链接
  322. if (content[header.key]) {
  323. result = 'underline'
  324. }
  325. }
  326. }
  327. }
  328. return result
  329. }
  330. },
  331. autoContentBgColor() {
  332. return function(cIndex, hIndex) {
  333. let result = this.contentBgColor
  334. let content = this.contentsSort[cIndex]
  335. let header = this.headers[hIndex]
  336. let keys = []
  337. // 先判断是不是首列,设置基础样式
  338. if (hIndex === 0) {
  339. result = this.firstColBgColor
  340. }
  341. // 再判断条件格式传没传值,设置条件样式
  342. if (this.valueFormat.length !== 0) {
  343. this.valueFormat.forEach(item => {
  344. keys.push(item.key)
  345. })
  346. if (keys.includes(header.key)) {
  347. // 该列开启了条件格式
  348. let key = header.key
  349. let type = this.valueFormat[keys.indexOf(key)].type
  350. let style = this.valueFormat[keys.indexOf(key)].style
  351. let range = this.valueFormat[keys.indexOf(key)].range || ''
  352. switch(type) {
  353. case 'bigger':
  354. if (parseFloat(content[key]) > range) {
  355. if (style.bgColor) result = style.bgColor
  356. }
  357. break
  358. case 'smaller':
  359. if (parseFloat(content[key]) < range) {
  360. if (style.bgColor) result = style.bgColor
  361. }
  362. break
  363. case 'equal':
  364. let val
  365. if (typeof range === 'number') val = parseFloat(content[key])
  366. else val = content[key]
  367. if (val === range) {
  368. if (style.bgColor) result = style.bgColor
  369. }
  370. break
  371. case 'range':
  372. if (parseFloat(content[key]) > range[0] && parseFloat(content[key]) < range[1]){
  373. if (style.bgColor) result = style.bgColor
  374. }
  375. break
  376. case 'average-bigger':
  377. let average = this.getAverage(key)
  378. if (parseFloat(content[key]) > average) {
  379. if (style.bgColor) result = style.bgColor
  380. }
  381. break
  382. case 'average-smaller':
  383. average = this.getAverage(key)
  384. if (parseFloat(content[key]) < average) {
  385. if (style.bgColor) result = style.bgColor
  386. }
  387. break
  388. case 'average-equal':
  389. average = this.getAverage(key)
  390. if (parseFloat(content[key]) === average) {
  391. if (style.bgColor) result = style.bgColor
  392. }
  393. break
  394. }
  395. }
  396. }
  397. // console.log("=====", content[header.key]);
  398. // return result
  399. if(this.isObject(content[header.key], 'edit')){
  400. return this.$props.contentBgColor;
  401. }else{
  402. return '#fff';
  403. }
  404. }
  405. },
  406. autoContentColor() {
  407. return function(cIndex, hIndex) {
  408. let result = this.contentFtColor
  409. let content = this.contentsSort[cIndex]
  410. let header = this.headers[hIndex]
  411. let keys = []
  412. // 先判断是不是链接,设置基础样式
  413. if (this.urlCol.length !== 0) {
  414. for (let i in this.urlCol) {
  415. let item = this.urlCol[i]
  416. if (header.key === item.key) {
  417. // 该单元格为链接
  418. if (content[header.key]) {
  419. result = this.linkColor
  420. }
  421. }
  422. }
  423. }
  424. // 再判断条件格式传没传值,设置条件样式
  425. if (this.valueFormat.length !== 0) {
  426. this.valueFormat.forEach(item => {
  427. keys.push(item.key)
  428. })
  429. if (keys.includes(header.key)) {
  430. // 该列开启了条件格式
  431. let key = header.key
  432. let type = this.valueFormat[keys.indexOf(key)].type
  433. let style = this.valueFormat[keys.indexOf(key)].style
  434. let range = this.valueFormat[keys.indexOf(key)].range || ''
  435. switch(type) {
  436. case 'bigger':
  437. if (parseFloat(content[key]) > range) {
  438. if (style.color) result = style.color
  439. }
  440. break
  441. case 'smaller':
  442. if (parseFloat(content[key]) < range) {
  443. if (style.color) result = style.color
  444. }
  445. break
  446. case 'equal':
  447. let val
  448. if (typeof range === 'number') val = parseFloat(content[key])
  449. else val = content[key]
  450. if (val === range) {
  451. if (style.color) result = style.color
  452. }
  453. break
  454. case 'range':
  455. if (parseFloat(content[key]) > range[0] && parseFloat(content[key]) < range[1]){
  456. if (style.color) result = style.color
  457. }
  458. break
  459. case 'average-bigger':
  460. let average = this.getAverage(key)
  461. if (parseFloat(content[key]) > average) {
  462. if (style.color) result = style.color
  463. }
  464. break
  465. case 'average-smaller':
  466. average = this.getAverage(key)
  467. if (parseFloat(content[key]) < average) {
  468. if (style.color) result = style.color
  469. }
  470. break
  471. case 'average-equal':
  472. average = this.getAverage(key)
  473. if (parseFloat(content[key]) === average) {
  474. if (style.color) result = style.color
  475. }
  476. break
  477. }
  478. }
  479. }
  480. return result
  481. }
  482. },
  483. autoSortShow() {
  484. return function(hIndex) {
  485. let result = false
  486. let header = this.headers[hIndex]
  487. let keys = []
  488. // 判断排序是否传值
  489. if (this.sortCol.length !== 0 && this.sortKeys.length === 0) {
  490. this.sortCol.forEach(item => {
  491. keys.push(item.key)
  492. })
  493. this.sortKeys = keys
  494. if (keys.includes(header.key)) {
  495. result = true
  496. }
  497. } else if (this.sortCol.length !== 0) {
  498. if (this.sortKeys.includes(header.key)) {
  499. result = true
  500. }
  501. }
  502. return result
  503. }
  504. },
  505. screenWidth() {
  506. return `${uni.getSystemInfoSync()['screenWidth']}px`
  507. }
  508. },
  509. props: {
  510. headers: {
  511. type: Array,
  512. default() {
  513. return [{
  514. key: 'name',
  515. label: '姓名'
  516. },{
  517. key: 'age',
  518. label: '年龄'
  519. },{
  520. key: 'sex',
  521. label: '性别'
  522. },{
  523. key: 'height',
  524. label: '身高'
  525. },{
  526. key: 'info',
  527. label: '描述'
  528. },{
  529. key: 'operation',
  530. label: '操作'
  531. }]
  532. }
  533. },
  534. contents: {
  535. type: [Array, Object],
  536. default() {
  537. return [{
  538. name: '张三',
  539. age: '18',
  540. sex: '男',
  541. height: '192cm',
  542. info: '无敌叫是',
  543. operation: {button: true, key: 'delete', value: '删除'}
  544. }, {
  545. name: '李四',
  546. age: '18',
  547. sex: '男',
  548. height: '192cm',
  549. info: '无敌叫是'
  550. }, {
  551. name: '赵五',
  552. age: '18',
  553. sex: '男',
  554. height: '192cm',
  555. info: '无敌叫是'
  556. }]
  557. }
  558. },
  559. emptyString: {
  560. type: String,
  561. default: '-'
  562. },
  563. width: {
  564. type: String,
  565. default: `${uni.getSystemInfoSync().screenWidth}px`
  566. },
  567. height: {
  568. type: String,
  569. default: 'auto'
  570. },
  571. fontSize: {
  572. type: Array,
  573. default() {
  574. return [30]
  575. }
  576. },
  577. defaultColWidth: {
  578. type: Number,
  579. default: 165
  580. },
  581. headerWeight: {
  582. type: Boolean,
  583. default: true
  584. },
  585. minHeight: {
  586. type: Array,
  587. default() {
  588. return [70]
  589. }
  590. },
  591. headerBgColor: {
  592. type: String,
  593. default: '#fff'
  594. },
  595. contentBgColor: {
  596. type: String,
  597. default: '#fff'
  598. },
  599. headerFtColor: {
  600. type: String,
  601. default: '#3e3e3e'
  602. },
  603. contentFtColor: {
  604. type: String,
  605. default: '#3e3e3e'
  606. },
  607. linkColor: {
  608. type: String,
  609. default: '#0024c8'
  610. },
  611. firstColBgColor: {
  612. type: String,
  613. default: '#f1f1f1'
  614. },
  615. firstLineFixed: {
  616. type: Boolean,
  617. default: false
  618. },
  619. textAlign: {
  620. type: String,
  621. default: 'center'
  622. },
  623. padding: {
  624. type: Array,
  625. default() {
  626. return [5, 10]
  627. }
  628. },
  629. borderColor: {
  630. type: String,
  631. default: '#e1e1e1'
  632. },
  633. urlCol: {
  634. type: Array,
  635. default() {
  636. return []
  637. }
  638. },
  639. computedCol: {
  640. type: Array,
  641. default() {
  642. return []
  643. }
  644. },
  645. bottomComputedFixed: {
  646. type: Boolean,
  647. default: true
  648. },
  649. valueFormat: {
  650. type: Array,
  651. default() {
  652. return []
  653. }
  654. },
  655. formatCol: {
  656. type: Array,
  657. default() {
  658. return []
  659. }
  660. },
  661. showLeftAndRightBorder: {
  662. type: Boolean,
  663. default: false
  664. },
  665. showVertBorder: {
  666. type: Boolean,
  667. default: true
  668. },
  669. sortCol: {
  670. type: Array,
  671. default() {
  672. return []
  673. }
  674. },
  675. sortWays: {
  676. type: Array,
  677. default() {
  678. return ['none', 'asc', 'inv']
  679. }
  680. },
  681. loading: {
  682. type: Boolean,
  683. default: false
  684. },
  685. loaderSize: {
  686. type: [String, Number],
  687. default: 50
  688. },
  689. loaderColor: {
  690. type: String,
  691. default: '#a3a3a3'
  692. },
  693. loaderBgColor: {
  694. type: String,
  695. default: '#f8f8f8'
  696. },
  697. enableCheck: {
  698. type: String,
  699. default: ''
  700. },
  701. checkColWidth: {
  702. type: [String, Number],
  703. default: '70'
  704. },
  705. checkerColor: {
  706. type: String,
  707. default: '#3e3e3e'
  708. },
  709. checkerBorderColor: {
  710. type: String,
  711. default: '#d3d3d3'
  712. },
  713. checkerBgColor: {
  714. type: String,
  715. default: 'rgba(0, 0, 0, 0)'
  716. },
  717. checkerBoxBgColor: {
  718. type: String,
  719. default: 'rgba(0, 0, 0, 0)'
  720. },
  721. checkerCellBgColor: {
  722. type: String,
  723. default: '#f1f1f1'
  724. }
  725. },
  726. watch: {
  727. headers(val) {
  728. this.$forceUpdate()
  729. },
  730. contents(val) {
  731. this.contentsSort = val.slice()
  732. if (this.onload) {
  733. this.contentsSort.forEach(item => {
  734. this.$set(item, 'checked', false)
  735. })
  736. this.oContentsSort = this.contentsSort.slice()
  737. this.onload = false
  738. }
  739. this.$forceUpdate()
  740. }
  741. },
  742. mounted() {
  743. this.contentsSort.forEach(item => {
  744. this.$set(item, 'checked', false)
  745. })
  746. this.oContentsSort = this.contentsSort.slice()
  747. if (this.sortCol.length !== 0) {
  748. this.sortActiveKey = this.sortCol[0].key
  749. uni.setStorageSync('lastSortActiveKey', this.sortActiveKey)
  750. this.doSort(this.sortCol[0].key, this.sortWays[this.sortWay], this.sortCol[0].isNumber)
  751. }
  752. },
  753. methods: {
  754. // 校验数据格式,如果不是数组则转为数组
  755. toArray(){
  756. if(Array.isArray(this.contents)){
  757. return this.contents.slice();
  758. }else{
  759. let contents = {...this.$props.contents};
  760. let arr = [];
  761. for(let i in contents){
  762. arr.push(contents[i]);
  763. }
  764. return arr;
  765. }
  766. },
  767. inputBlur(cIndex, hIndex, event){
  768. let event_obj = {};
  769. let value = event.detail.value;
  770. let content = this.contentsSort[cIndex];
  771. let header = this.headers[hIndex];
  772. if(content[header.key]){
  773. event_obj = {
  774. content: content[header.key],
  775. contentIndex: cIndex,
  776. header: header.label,
  777. headerIndex: hIndex,
  778. key: header.key,
  779. lineData: content,
  780. detailValue: value
  781. }
  782. this.$emit('onInputChange', event_obj);
  783. }
  784. },
  785. buttonClick(cIndex, hIndex){
  786. let event_obj = {};
  787. let content = this.contentsSort[cIndex];
  788. let header = this.headers[hIndex];
  789. if(content[header.key]){
  790. event_obj = {
  791. content: content[header.key],
  792. contentIndex: cIndex,
  793. header: header.label,
  794. headerIndex: hIndex,
  795. key: header.key,
  796. lineData: content
  797. }
  798. this.$emit('onButtonClick', event_obj);
  799. }
  800. },
  801. isObject(val, key){
  802. return this.$shared.isValueType(val) == 'object' && val[key];
  803. },
  804. doSort(key, type, isNumber) {
  805. let arr = this.contentsSort
  806. if (type === 'asc') {
  807. // 升序
  808. if (isNumber) {
  809. arr.sort((a, b) => {
  810. return (parseFloat(a[key].toString().replace(/[^0-9]/ig, "")) || 0) -
  811. (parseFloat(b[key].toString().replace(/[^0-9]/ig, "")) || 0)
  812. })
  813. } else {
  814. arr.sort((a, b) => {
  815. let A = Pinyin.getSpell(a[key].charAt(0), function(charactor, spell) {
  816. return spell[1]
  817. }).charAt(0).charCodeAt()
  818. let B = Pinyin.getSpell(b[key].charAt(0), function(charactor, spell) {
  819. return spell[1]
  820. }).charAt(0).charCodeAt()
  821. return A - B
  822. })
  823. }
  824. } else if (type === 'inv') {
  825. // 倒序
  826. if (isNumber) {
  827. arr.sort((a, b) => {
  828. return (parseFloat(b[key].toString().replace(/[^0-9]/ig, "")) || 0) -
  829. (parseFloat(a[key].toString().replace(/[^0-9]/ig, "")) || 0)
  830. })
  831. } else {
  832. arr.sort((a, b) => {
  833. let A = Pinyin.getSpell(a[key].charAt(0), function(charactor, spell) {
  834. return spell[1]
  835. }).charAt(0).charCodeAt()
  836. let B = Pinyin.getSpell(b[key].charAt(0), function(charactor, spell) {
  837. return spell[1]
  838. }).charAt(0).charCodeAt()
  839. return B - A
  840. })
  841. }
  842. } else {
  843. this.contentsSort = this.oContentsSort.slice()
  844. }
  845. if (this.enableCheck) {
  846. this.event.data.forEach(item => {
  847. this.contentsSort.forEach((content, index) => {
  848. if (isEqual(item.lineData, content)) {
  849. item.index = index
  850. }
  851. })
  852. })
  853. }
  854. this.$forceUpdate()
  855. },
  856. initBottomComputed() {
  857. let result = {}
  858. let needComputed = []
  859. this.computedCol.forEach(key => {
  860. let computedColData = []
  861. this.contentsSort.forEach(content => {
  862. computedColData.push(content[key] || '0')
  863. })
  864. needComputed.push(computedColData)
  865. })
  866. needComputed.forEach((item, index) => {
  867. let total = 0
  868. item.forEach(num => {
  869. total += parseFloat(num)
  870. })
  871. result[this.computedCol[index]] = total
  872. })
  873. this.bottomComputed = result
  874. },
  875. onHeaderItemTap(index) {
  876. let header = this.headers[index]
  877. const lastSortActiveKey = uni.getStorageSync('lastSortActiveKey') || ''
  878. if (this.sortCol.length !== 0) {
  879. if (this.sortKeys.includes(header.key)) {
  880. // 当前列开启了排序
  881. this.sortActiveKey = header.key
  882. uni.setStorageSync('lastSortActiveKey', this.sortActiveKey)
  883. if (this.sortWay < 2 && lastSortActiveKey === this.sortActiveKey) {
  884. this.sortWay++
  885. } else if (lastSortActiveKey !== this.sortActiveKey) {
  886. this.sortWay = 1
  887. } else if (this.sortWay >= 2) {
  888. this.sortWay = 0
  889. }
  890. let isNumber = this.sortCol[this.sortKeys.indexOf(header.key)].isNumber
  891. this.doSort(header.key, this.sortWays[this.sortWay], isNumber)
  892. }
  893. }
  894. },
  895. onContentItemTap(cIndex, hIndex) {
  896. let event = {}
  897. let content = this.contentsSort[cIndex]
  898. let header = this.headers[hIndex]
  899. let keys = []
  900. if (this.urlCol.length !== 0) {
  901. for (let i in this.urlCol) {
  902. let item = this.urlCol[i]
  903. keys.push(item.key)
  904. }
  905. }
  906. if (content[header.key]) {
  907. if (keys.includes(header.key)) {
  908. // 该单元格为链接
  909. switch(this.urlCol[keys.indexOf(header.key)].type) {
  910. case 'route':
  911. let url = content[header.key][1]
  912. if (content[header.key][2]) {
  913. url = `${url}?`
  914. Object.keys(content[header.key][2]).forEach(key => {
  915. url += `&${key}=${content[header['key']][2][key]}`
  916. })
  917. }
  918. uni.navigateTo({url})
  919. break
  920. case 'http':
  921. this.openURL(content[header.key][1])
  922. break
  923. }
  924. } else {
  925. event = {
  926. content: content[header.key],
  927. contentIndex: cIndex,
  928. header: header.label,
  929. headerIndex: hIndex,
  930. key: header.key,
  931. lineData: content
  932. }
  933. this.$emit('onCellClick', event)
  934. }
  935. } else {
  936. event = {
  937. content: '',
  938. contentIndex: cIndex,
  939. header: header.label,
  940. headerIndex: hIndex,
  941. key: header.key,
  942. lineData: content
  943. }
  944. if (keys.includes(header.key)) {
  945. // 该单元格为链接
  946. event['isLink'] = true
  947. }
  948. this.$emit('onCellClick', event)
  949. }
  950. },
  951. onCheckAllTap() {
  952. if (this.enableCheck === 'multiple') {
  953. let checkList = []
  954. this.contentsSort.forEach(item => {
  955. checkList.push(item.checked)
  956. })
  957. this.checkList = checkList
  958. if (!this.checkAll) {
  959. this.checkAll = true
  960. this.contentsSort.forEach(item => {
  961. item.checked = true
  962. })
  963. this.event.data = []
  964. this.contentsSort.forEach((content, index) => {
  965. this.event.data.push({
  966. index,
  967. lineData: content
  968. })
  969. })
  970. } else {
  971. this.checkAll = false
  972. this.event.data = []
  973. this.contentsSort.forEach(item => {
  974. item.checked = false
  975. })
  976. }
  977. this.$emit('onCheck', this.event)
  978. }
  979. },
  980. onCheckItemTap(cIndex) {
  981. let content = this.contentsSort[cIndex]
  982. if (this.enableCheck === 'single') {
  983. this.contentsSort.forEach((item, index) => {
  984. if (cIndex === index) {
  985. item.checked = !item.checked
  986. } else {
  987. item.checked = false
  988. }
  989. })
  990. } else if (this.enableCheck === 'multiple') {
  991. this.contentsSort[cIndex]['checked'] = !this.contentsSort[cIndex]['checked']
  992. }
  993. if (this.contentsSort[cIndex]['checked']) {
  994. if (this.enableCheck === 'single') {
  995. this.event.data = []
  996. }
  997. this.event.data.push({
  998. index: cIndex,
  999. lineData: this.contentsSort[cIndex]
  1000. })
  1001. } else {
  1002. this.event.data.forEach(item => {
  1003. if (item.index === cIndex) this.event.data.splice(this.event.data.indexOf(item), 1)
  1004. })
  1005. if (this.event.data.length === 0) {
  1006. this.checkAll = false
  1007. }
  1008. }
  1009. this.$forceUpdate()
  1010. this.$emit('onCheck', this.event)
  1011. },
  1012. openURL(href) {
  1013. // #ifdef APP-PLUS
  1014. plus.runtime.openURL(href)
  1015. // #endif
  1016. // #ifdef H5
  1017. window.open(href)
  1018. // #endif
  1019. // #ifdef MP
  1020. uni.setClipboardData({
  1021. data: href,
  1022. success() {
  1023. uni.showToast({
  1024. title: '网址已复制,请在手机浏览器里粘贴该网址',
  1025. icon: 'none'
  1026. })
  1027. }
  1028. })
  1029. // #endif
  1030. },
  1031. getAverage(key) {
  1032. let numList = []
  1033. this.contentsSort.forEach(content => {
  1034. numList.push(parseFloat(content[key]) || 0)
  1035. })
  1036. return numList.reduce((a, b) => a + b) / numList.length
  1037. },
  1038. getTotal(key) {
  1039. let numList = []
  1040. this.contentsSort.forEach(content => {
  1041. numList.push(parseFloat(content[key]) || 0)
  1042. })
  1043. return numList.reduce((a, b) => a + b)
  1044. },
  1045. RGBChange(color, level, type) {
  1046. // 判断颜色类型
  1047. let r = 0,
  1048. g = 0,
  1049. b = 0,
  1050. hasAlpha = false,
  1051. alpha = 1
  1052. if (color.indexOf('#') !== -1) {
  1053. // hex转rgb
  1054. if (color.length === 4) {
  1055. let arr = color.split('')
  1056. color = '#' + arr[1] + arr[1] + arr[2] + arr[2] + arr[3] + arr[3]
  1057. }
  1058. let color16List = [color.substring(1, 3), color.substring(3, 5), color.substring(5, 7)]
  1059. r = parseInt(color16List[0], 16)
  1060. g = parseInt(color16List[1], 16)
  1061. b = parseInt(color16List[2], 16)
  1062. } else {
  1063. hasAlpha = color.indexOf('a') !== -1
  1064. let root = color.slice()
  1065. let idx = root.indexOf('(') + 1
  1066. root = root.substring(idx)
  1067. let firstDotIdx = root.indexOf(',')
  1068. r = parseFloat(root.substring(0, firstDotIdx))
  1069. root = root.substring(firstDotIdx + 1)
  1070. let secondDotIdx = root.indexOf(',')
  1071. g = parseFloat(root.substring(0, secondDotIdx))
  1072. root = root.substring(secondDotIdx + 1)
  1073. if (hasAlpha) {
  1074. let thirdDotIdx = root.indexOf(',')
  1075. b = parseFloat(root.substring(0, thirdDotIdx))
  1076. alpha = parseFloat(root.substring(thirdDotIdx + 1))
  1077. } else {
  1078. b = parseFloat(root)
  1079. }
  1080. }
  1081. let rgbc = [r, g, b]
  1082. // 减淡或加深
  1083. for (var i = 0; i < 3; i++)
  1084. type === 'light' ? rgbc[i] = Math.floor((255 - rgbc[i]) * level + rgbc[i]) : rgbc[i] = Math.floor(rgbc[i] * (1 -
  1085. level))
  1086. if (hasAlpha) {
  1087. return `rgba(${rgbc[0]}, ${rgbc[1]}, ${rgbc[2]}, ${alpha})`
  1088. } else {
  1089. return `rgb(${rgbc[0]}, ${rgbc[1]}, ${rgbc[2]})`
  1090. }
  1091. },
  1092. hexToRgb(color) {
  1093. if (color.length === 4) {
  1094. let arr = color.split('')
  1095. color = '#' + arr[1] + arr[1] + arr[2] + arr[2] + arr[3] + arr[3]
  1096. }
  1097. let color16List = [color.substring(1, 3), color.substring(3, 5), color.substring(5, 7)]
  1098. let r = parseInt(color16List[0], 16)
  1099. let g = parseInt(color16List[1], 16)
  1100. let b = parseInt(color16List[2], 16)
  1101. return [r, g, b]
  1102. },
  1103. randomString(length, chars) {
  1104. var result = ''
  1105. for (var i = length; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)]
  1106. return result
  1107. }
  1108. }
  1109. }
  1110. </script>
  1111. <style>
  1112. @import './css/iconfont.css';
  1113. @import './css/loader.css';
  1114. .ios-header-bug {
  1115. height: 0;
  1116. width: 1px;
  1117. opacity: 0;
  1118. }
  1119. .wyb-table-scroll-view {
  1120. overflow: scroll;
  1121. -webkit-overflow-scrolling: touch;
  1122. border: 2rpx solid #e5e5e5;
  1123. }
  1124. .wyb-table-scroll-view::-webkit-scrollbar {
  1125. display: none;
  1126. /* #ifdef MP-WEIXIN */
  1127. width: 0;
  1128. height: 0;
  1129. /* #endif */
  1130. }
  1131. .wyb-table-loading-box {
  1132. display: flex;
  1133. align-items: center;
  1134. justify-content: center;
  1135. z-index: 500;
  1136. }
  1137. .wyb-table-header {
  1138. position: sticky;
  1139. top: 0;
  1140. display: grid;
  1141. grid-auto-flow: column;
  1142. width: max-content;
  1143. z-index: 25;
  1144. }
  1145. .wyb-table-header-item {
  1146. flex: 1;
  1147. display: flex;
  1148. align-items: center;
  1149. box-sizing: border-box;
  1150. position: relative;
  1151. }
  1152. .wyb-table-header-icon {
  1153. display: flex;
  1154. flex-direction: column;
  1155. }
  1156. .wyb-table-content-line {
  1157. display: grid;
  1158. grid-auto-flow: column;
  1159. width: max-content;
  1160. position: relative;
  1161. }
  1162. .wyb-table-content-item {
  1163. display: flex;
  1164. flex-direction: row;
  1165. align-items: center;
  1166. box-sizing: border-box;
  1167. }
  1168. .wyb-table-checkbox {
  1169. border-radius: 3px;
  1170. display: flex;
  1171. align-items: center;
  1172. justify-content: center;
  1173. position: relative;
  1174. }
  1175. .icon-check {
  1176. width: 100%;
  1177. height: 100%;
  1178. position: absolute;
  1179. border-radius: 0;
  1180. border-radius: 3px;
  1181. font-weight: bold;
  1182. box-sizing: border-box;
  1183. transform: scale(1.1);
  1184. }
  1185. .diy-btn{
  1186. background-color: transparent;
  1187. color: #FF3838;
  1188. height: max-content;
  1189. line-height: initial;
  1190. font-size: 28rpx;
  1191. }
  1192. </style>