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

1323 lines
38 KiB

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