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

1205 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.contents.slice(),
  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,
  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. inputBlur(cIndex, hIndex, event){
  755. let event_obj = {};
  756. let value = event.detail.value;
  757. let content = this.contentsSort[cIndex];
  758. let header = this.headers[hIndex];
  759. if(content[header.key]){
  760. event_obj = {
  761. content: content[header.key],
  762. contentIndex: cIndex,
  763. header: header.label,
  764. headerIndex: hIndex,
  765. key: header.key,
  766. lineData: content,
  767. detailValue: value
  768. }
  769. this.$emit('onInputChange', event_obj);
  770. }
  771. },
  772. buttonClick(cIndex, hIndex){
  773. let event_obj = {};
  774. let content = this.contentsSort[cIndex];
  775. let header = this.headers[hIndex];
  776. if(content[header.key]){
  777. event_obj = {
  778. content: content[header.key],
  779. contentIndex: cIndex,
  780. header: header.label,
  781. headerIndex: hIndex,
  782. key: header.key,
  783. lineData: content
  784. }
  785. this.$emit('onButtonClick', event_obj);
  786. }
  787. },
  788. isObject(val, key){
  789. return this.$shared.isValueType(val) == 'object' && val[key];
  790. },
  791. doSort(key, type, isNumber) {
  792. let arr = this.contentsSort
  793. if (type === 'asc') {
  794. // 升序
  795. if (isNumber) {
  796. arr.sort((a, b) => {
  797. return (parseFloat(a[key].toString().replace(/[^0-9]/ig, "")) || 0) -
  798. (parseFloat(b[key].toString().replace(/[^0-9]/ig, "")) || 0)
  799. })
  800. } else {
  801. arr.sort((a, b) => {
  802. let A = Pinyin.getSpell(a[key].charAt(0), function(charactor, spell) {
  803. return spell[1]
  804. }).charAt(0).charCodeAt()
  805. let B = Pinyin.getSpell(b[key].charAt(0), function(charactor, spell) {
  806. return spell[1]
  807. }).charAt(0).charCodeAt()
  808. return A - B
  809. })
  810. }
  811. } else if (type === 'inv') {
  812. // 倒序
  813. if (isNumber) {
  814. arr.sort((a, b) => {
  815. return (parseFloat(b[key].toString().replace(/[^0-9]/ig, "")) || 0) -
  816. (parseFloat(a[key].toString().replace(/[^0-9]/ig, "")) || 0)
  817. })
  818. } else {
  819. arr.sort((a, b) => {
  820. let A = Pinyin.getSpell(a[key].charAt(0), function(charactor, spell) {
  821. return spell[1]
  822. }).charAt(0).charCodeAt()
  823. let B = Pinyin.getSpell(b[key].charAt(0), function(charactor, spell) {
  824. return spell[1]
  825. }).charAt(0).charCodeAt()
  826. return B - A
  827. })
  828. }
  829. } else {
  830. this.contentsSort = this.oContentsSort.slice()
  831. }
  832. if (this.enableCheck) {
  833. this.event.data.forEach(item => {
  834. this.contentsSort.forEach((content, index) => {
  835. if (isEqual(item.lineData, content)) {
  836. item.index = index
  837. }
  838. })
  839. })
  840. }
  841. this.$forceUpdate()
  842. },
  843. initBottomComputed() {
  844. let result = {}
  845. let needComputed = []
  846. this.computedCol.forEach(key => {
  847. let computedColData = []
  848. this.contentsSort.forEach(content => {
  849. computedColData.push(content[key] || '0')
  850. })
  851. needComputed.push(computedColData)
  852. })
  853. needComputed.forEach((item, index) => {
  854. let total = 0
  855. item.forEach(num => {
  856. total += parseFloat(num)
  857. })
  858. result[this.computedCol[index]] = total
  859. })
  860. this.bottomComputed = result
  861. },
  862. onHeaderItemTap(index) {
  863. let header = this.headers[index]
  864. const lastSortActiveKey = uni.getStorageSync('lastSortActiveKey') || ''
  865. if (this.sortCol.length !== 0) {
  866. if (this.sortKeys.includes(header.key)) {
  867. // 当前列开启了排序
  868. this.sortActiveKey = header.key
  869. uni.setStorageSync('lastSortActiveKey', this.sortActiveKey)
  870. if (this.sortWay < 2 && lastSortActiveKey === this.sortActiveKey) {
  871. this.sortWay++
  872. } else if (lastSortActiveKey !== this.sortActiveKey) {
  873. this.sortWay = 1
  874. } else if (this.sortWay >= 2) {
  875. this.sortWay = 0
  876. }
  877. let isNumber = this.sortCol[this.sortKeys.indexOf(header.key)].isNumber
  878. this.doSort(header.key, this.sortWays[this.sortWay], isNumber)
  879. }
  880. }
  881. },
  882. onContentItemTap(cIndex, hIndex) {
  883. let event = {}
  884. let content = this.contentsSort[cIndex]
  885. let header = this.headers[hIndex]
  886. let keys = []
  887. if (this.urlCol.length !== 0) {
  888. for (let i in this.urlCol) {
  889. let item = this.urlCol[i]
  890. keys.push(item.key)
  891. }
  892. }
  893. if (content[header.key]) {
  894. if (keys.includes(header.key)) {
  895. // 该单元格为链接
  896. switch(this.urlCol[keys.indexOf(header.key)].type) {
  897. case 'route':
  898. let url = content[header.key][1]
  899. if (content[header.key][2]) {
  900. url = `${url}?`
  901. Object.keys(content[header.key][2]).forEach(key => {
  902. url += `&${key}=${content[header['key']][2][key]}`
  903. })
  904. }
  905. uni.navigateTo({url})
  906. break
  907. case 'http':
  908. this.openURL(content[header.key][1])
  909. break
  910. }
  911. } else {
  912. event = {
  913. content: content[header.key],
  914. contentIndex: cIndex,
  915. header: header.label,
  916. headerIndex: hIndex,
  917. key: header.key,
  918. lineData: content
  919. }
  920. this.$emit('onCellClick', event)
  921. }
  922. } else {
  923. event = {
  924. content: '',
  925. contentIndex: cIndex,
  926. header: header.label,
  927. headerIndex: hIndex,
  928. key: header.key,
  929. lineData: content
  930. }
  931. if (keys.includes(header.key)) {
  932. // 该单元格为链接
  933. event['isLink'] = true
  934. }
  935. this.$emit('onCellClick', event)
  936. }
  937. },
  938. onCheckAllTap() {
  939. if (this.enableCheck === 'multiple') {
  940. let checkList = []
  941. this.contentsSort.forEach(item => {
  942. checkList.push(item.checked)
  943. })
  944. this.checkList = checkList
  945. if (!this.checkAll) {
  946. this.checkAll = true
  947. this.contentsSort.forEach(item => {
  948. item.checked = true
  949. })
  950. this.event.data = []
  951. this.contentsSort.forEach((content, index) => {
  952. this.event.data.push({
  953. index,
  954. lineData: content
  955. })
  956. })
  957. } else {
  958. this.checkAll = false
  959. this.event.data = []
  960. this.contentsSort.forEach(item => {
  961. item.checked = false
  962. })
  963. }
  964. this.$emit('onCheck', this.event)
  965. }
  966. },
  967. onCheckItemTap(cIndex) {
  968. let content = this.contentsSort[cIndex]
  969. if (this.enableCheck === 'single') {
  970. this.contentsSort.forEach((item, index) => {
  971. if (cIndex === index) {
  972. item.checked = !item.checked
  973. } else {
  974. item.checked = false
  975. }
  976. })
  977. } else if (this.enableCheck === 'multiple') {
  978. this.contentsSort[cIndex]['checked'] = !this.contentsSort[cIndex]['checked']
  979. }
  980. if (this.contentsSort[cIndex]['checked']) {
  981. if (this.enableCheck === 'single') {
  982. this.event.data = []
  983. }
  984. this.event.data.push({
  985. index: cIndex,
  986. lineData: this.contentsSort[cIndex]
  987. })
  988. } else {
  989. this.event.data.forEach(item => {
  990. if (item.index === cIndex) this.event.data.splice(this.event.data.indexOf(item), 1)
  991. })
  992. if (this.event.data.length === 0) {
  993. this.checkAll = false
  994. }
  995. }
  996. this.$forceUpdate()
  997. this.$emit('onCheck', this.event)
  998. },
  999. openURL(href) {
  1000. // #ifdef APP-PLUS
  1001. plus.runtime.openURL(href)
  1002. // #endif
  1003. // #ifdef H5
  1004. window.open(href)
  1005. // #endif
  1006. // #ifdef MP
  1007. uni.setClipboardData({
  1008. data: href,
  1009. success() {
  1010. uni.showToast({
  1011. title: '网址已复制,请在手机浏览器里粘贴该网址',
  1012. icon: 'none'
  1013. })
  1014. }
  1015. })
  1016. // #endif
  1017. },
  1018. getAverage(key) {
  1019. let numList = []
  1020. this.contentsSort.forEach(content => {
  1021. numList.push(parseFloat(content[key]) || 0)
  1022. })
  1023. return numList.reduce((a, b) => a + b) / numList.length
  1024. },
  1025. getTotal(key) {
  1026. let numList = []
  1027. this.contentsSort.forEach(content => {
  1028. numList.push(parseFloat(content[key]) || 0)
  1029. })
  1030. return numList.reduce((a, b) => a + b)
  1031. },
  1032. RGBChange(color, level, type) {
  1033. // 判断颜色类型
  1034. let r = 0,
  1035. g = 0,
  1036. b = 0,
  1037. hasAlpha = false,
  1038. alpha = 1
  1039. if (color.indexOf('#') !== -1) {
  1040. // hex转rgb
  1041. if (color.length === 4) {
  1042. let arr = color.split('')
  1043. color = '#' + arr[1] + arr[1] + arr[2] + arr[2] + arr[3] + arr[3]
  1044. }
  1045. let color16List = [color.substring(1, 3), color.substring(3, 5), color.substring(5, 7)]
  1046. r = parseInt(color16List[0], 16)
  1047. g = parseInt(color16List[1], 16)
  1048. b = parseInt(color16List[2], 16)
  1049. } else {
  1050. hasAlpha = color.indexOf('a') !== -1
  1051. let root = color.slice()
  1052. let idx = root.indexOf('(') + 1
  1053. root = root.substring(idx)
  1054. let firstDotIdx = root.indexOf(',')
  1055. r = parseFloat(root.substring(0, firstDotIdx))
  1056. root = root.substring(firstDotIdx + 1)
  1057. let secondDotIdx = root.indexOf(',')
  1058. g = parseFloat(root.substring(0, secondDotIdx))
  1059. root = root.substring(secondDotIdx + 1)
  1060. if (hasAlpha) {
  1061. let thirdDotIdx = root.indexOf(',')
  1062. b = parseFloat(root.substring(0, thirdDotIdx))
  1063. alpha = parseFloat(root.substring(thirdDotIdx + 1))
  1064. } else {
  1065. b = parseFloat(root)
  1066. }
  1067. }
  1068. let rgbc = [r, g, b]
  1069. // 减淡或加深
  1070. for (var i = 0; i < 3; i++)
  1071. type === 'light' ? rgbc[i] = Math.floor((255 - rgbc[i]) * level + rgbc[i]) : rgbc[i] = Math.floor(rgbc[i] * (1 -
  1072. level))
  1073. if (hasAlpha) {
  1074. return `rgba(${rgbc[0]}, ${rgbc[1]}, ${rgbc[2]}, ${alpha})`
  1075. } else {
  1076. return `rgb(${rgbc[0]}, ${rgbc[1]}, ${rgbc[2]})`
  1077. }
  1078. },
  1079. hexToRgb(color) {
  1080. if (color.length === 4) {
  1081. let arr = color.split('')
  1082. color = '#' + arr[1] + arr[1] + arr[2] + arr[2] + arr[3] + arr[3]
  1083. }
  1084. let color16List = [color.substring(1, 3), color.substring(3, 5), color.substring(5, 7)]
  1085. let r = parseInt(color16List[0], 16)
  1086. let g = parseInt(color16List[1], 16)
  1087. let b = parseInt(color16List[2], 16)
  1088. return [r, g, b]
  1089. },
  1090. randomString(length, chars) {
  1091. var result = ''
  1092. for (var i = length; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)]
  1093. return result
  1094. }
  1095. }
  1096. }
  1097. </script>
  1098. <style>
  1099. @import './css/iconfont.css';
  1100. @import './css/loader.css';
  1101. .ios-header-bug {
  1102. height: 0;
  1103. width: 1px;
  1104. opacity: 0;
  1105. }
  1106. .wyb-table-scroll-view {
  1107. overflow: scroll;
  1108. -webkit-overflow-scrolling: touch;
  1109. border: 2rpx solid #e5e5e5;
  1110. }
  1111. .wyb-table-scroll-view::-webkit-scrollbar {
  1112. display: none;
  1113. /* #ifdef MP-WEIXIN */
  1114. width: 0;
  1115. height: 0;
  1116. /* #endif */
  1117. }
  1118. .wyb-table-loading-box {
  1119. display: flex;
  1120. align-items: center;
  1121. justify-content: center;
  1122. z-index: 500;
  1123. }
  1124. .wyb-table-header {
  1125. position: sticky;
  1126. top: 0;
  1127. display: grid;
  1128. grid-auto-flow: column;
  1129. width: max-content;
  1130. z-index: 25;
  1131. }
  1132. .wyb-table-header-item {
  1133. flex: 1;
  1134. display: flex;
  1135. align-items: center;
  1136. box-sizing: border-box;
  1137. position: relative;
  1138. }
  1139. .wyb-table-header-icon {
  1140. display: flex;
  1141. flex-direction: column;
  1142. }
  1143. .wyb-table-content-line {
  1144. display: grid;
  1145. grid-auto-flow: column;
  1146. width: max-content;
  1147. position: relative;
  1148. }
  1149. .wyb-table-content-item {
  1150. display: flex;
  1151. flex-direction: row;
  1152. align-items: center;
  1153. box-sizing: border-box;
  1154. }
  1155. .wyb-table-checkbox {
  1156. border-radius: 3px;
  1157. display: flex;
  1158. align-items: center;
  1159. justify-content: center;
  1160. position: relative;
  1161. }
  1162. .icon-check {
  1163. width: 100%;
  1164. height: 100%;
  1165. position: absolute;
  1166. border-radius: 0;
  1167. border-radius: 3px;
  1168. font-weight: bold;
  1169. box-sizing: border-box;
  1170. transform: scale(1.1);
  1171. }
  1172. .diy-btn{
  1173. background-color: transparent;
  1174. color: #FF3838;
  1175. height: max-content;
  1176. line-height: initial;
  1177. font-size: 28rpx;
  1178. }
  1179. </style>