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

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