自主产品,供应链食堂系统。将两个端拆开了。
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.

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