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

1261 lines
36 KiB

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