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

1262 lines
36 KiB

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