zb-tables.vue 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308
  1. <template>
  2. <!-- #ifdef H5 || APP-PLUS -->
  3. <view :class="['zb-table','zb-table-fixed-header',!border&&(bodyTableLeft>50||headerTableLeft>50)&&'scroll-left-fixed']">
  4. <view class="zb-table-content" style="flex: 1">
  5. <view class="zb-table-scroll" style="height: 100%;">
  6. <template v-if="showHeader">
  7. <view class="zb-table-header top-header-uni" :style="{paddingRight:`${scrollbarSize}rpx`}"
  8. >
  9. <scroll-view class="zb-table-headers"
  10. @touchmove.stop="getMove"
  11. @scroll="handleTableScrollLeft"
  12. scroll-x="true"
  13. scroll-y="false"
  14. id="tableHeaders"
  15. scroll-anchoring="true"
  16. :scroll-left="headerTableLeft"
  17. style="
  18. height: 100%">
  19. <view class="zb-table-fixed" >
  20. <view class="zb-table-thead" style="position: relative;" >
  21. <view class="item-tr">
  22. <view
  23. @click.stop="sortAction(item,index)"
  24. class="item-th"
  25. :style="[{
  26. width:`${item.width?item.width:'200'}rpx`,
  27. flex:index===transColumns.length-1?1:'none',
  28. minWidth:`${item.width?item.width:'200'}rpx`,
  29. borderRight:`${border?'2rpx solid #e8e8e8':''}`,
  30. borderRight:`${(scrollbarSize&&index===transColumns.length-1)?'':border?'2rpx solid #e8e8e8':''}`,
  31. borderTop:`${border?'2rpx solid #e8e8e8':''}`,
  32. textAlign:item.align||'left',
  33. paddingRight:`${item.paddingRight?item.paddingRight:'8'}rpx`,
  34. },getHeaderCellStyle(item,index)]"
  35. v-for="(item,index) in transColumns" :key="index">
  36. <template v-if="item.type==='selection'">
  37. <view class="checkbox-item">
  38. <tableCheckbox
  39. :indeterminate="indeterminate" :checked="checkedAll" @checkboxSelected="checkboxSelectedAll"></tableCheckbox>
  40. </view>
  41. </template>
  42. <template v-else>
  43. {{ item.label }}
  44. <block v-if="item.manfen">/{{item.manfen}}</block>
  45. <view class="sorter-table" v-if="item.sorter">
  46. <view :class="['sorter-table-icon',item.sorterMode==='_asc'&&`sorting${item.sorterMode||''}`]"></view>
  47. <view :class="['sorter-table-icon',item.sorterMode==='_desc'&&`sorting${item.sorterMode||''}`]"></view>
  48. </view>
  49. </template>
  50. </view>
  51. </view>
  52. </view>
  53. </view>
  54. </scroll-view>
  55. </view>
  56. </template>
  57. <template v-if="!data.length">
  58. <view class="no-data">暂无数据~~</view>
  59. </template>
  60. <scroll-view
  61. class="zb-table-body" ref="tableBody" scroll-x="true" scroll-y="true" id="tableBody"
  62. :lower-threshold="40"
  63. :upper-threshold="10"
  64. @touchmove.stop="getMove"
  65. @scrolltolower="scrolltolower"
  66. @scrolltoupper="(e)=>debounce(scrollToLeft)(e)"
  67. @scroll="handleBodyScroll" :scroll-left="bodyTableLeft" :scroll-top="bodyScrollTop"
  68. :style=" `height: calc(100% - ${showSummary?160:80}rpx)`" >
  69. <view class="zb-table-fixed">
  70. <view class="zb-table-tbody">
  71. <view :class="['item-tr',highlight&&isHighlight(item,index)?'current-row':'']"
  72. @click.stop="rowClick(item,index)"
  73. v-for="(item,index) in transData" :key="item.key" >
  74. <view
  75. :style="[{
  76. width:`${ite.width?ite.width:'200'}rpx`,
  77. flex:i===transColumns.length-1?1:'none',
  78. minWidth:`${ite.width?ite.width:'200'}rpx`,
  79. borderRight:`${border?'2rpx solid #e8e8e8':''}`,
  80. textAlign:ite.align||'left',
  81. },cellStyle&&getCellStyle(item,ite,index,i)]"
  82. :class="['item-td',stripe?(index % 2) != 0?'odd':'even':'']"
  83. v-for="(ite,i) in transColumns" :key="i">
  84. <template v-if="ite.type==='operation'">
  85. <view style="display: flex;align-items: center;height: 100%">
  86. <view
  87. v-for="ren,ind in permission(item,ite.renders,index)"
  88. :key="ind"
  89. @click.stop="$emit(ren.func,item,index)"
  90. :style="{
  91. display:'flex',
  92. alignItems: 'center',
  93. marginRight:ite.renders.length>1?'16rpx':'0'
  94. }">
  95. <template v-if="ren.type==='custom'">
  96. <view :class="ren.class||''" style="cursor: pointer">
  97. {{ren.name}}
  98. </view>
  99. </template>
  100. <template v-else>
  101. <button
  102. :class="ren.class||''"
  103. :type="ren.type||'primary'" :size="ren.size||'mini'">{{ren.name}}</button>
  104. </template>
  105. </view>
  106. </view>
  107. </template>
  108. <!-- 格式化处理 -->
  109. <template v-else-if="ite.type==='format'">
  110. <view>{{kayType(itemFilter(item,ite),parameter[ite.key])}}</view>
  111. </template>
  112. <template v-else-if="ite.type==='selection'">
  113. <view class="checkbox-item">
  114. <tableCheckbox @checkboxSelected="(e)=>checkboxSelected(e,item)" :cellData="item" :checked="item.checked"/>
  115. </view>
  116. </template>
  117. <template v-else-if="ite.type==='index'">
  118. {{index+1}}
  119. </template>
  120. <template v-else-if="ite.type==='img'">
  121. <view class="checkbox-item">
  122. <block v-if="item[ite.name]" >
  123. <image v-for="(pite,pidx) in getPhotoFn(item[ite.name])" :key="pidx"
  124. @click.stop="previewImage(getPhotoFn(item[ite.name]),pite,pidx)"
  125. :show-menu-by-longpress="false"
  126. :src="pite" style="width: 80rpx;height:60rpx; " mode="aspectFit"></image>
  127. </block>
  128. <!-- <image
  129. @click.stop="previewImage(item,item[ite.name],index)"
  130. v-if="item[ite.name]"
  131. :show-menu-by-longpress="false"
  132. :src="item[ite.name]" style="width: 80rpx;height:60rpx; " mode="aspectFit"></image> -->
  133. <text v-else>{{ite.emptyString}}</text>
  134. </view>
  135. </template>
  136. <template v-else>
  137. <!-- {{ ite.filters?itemFilter(item,ite):(item[ite.name]==null||item[ite.name]==='')?ite.emptyString:item[ite.name] }}-->
  138. {{ ite.filters?itemFilter(item,ite):formatterAction(item,ite,index,i) }}
  139. </template>
  140. </view>
  141. </view>
  142. </view>
  143. </view>
  144. </scroll-view>
  145. <table-h5-summary
  146. :scrollbarSize="scrollbarSize"
  147. :data="data"
  148. :handleFooterTableScrollLeft="handleFooterTableScrollLeft"
  149. :headerFooterTableLeft="headerFooterTableLeft"
  150. v-if="showSummary"
  151. :showSummary="showSummary"
  152. :transColumns="transColumns"
  153. :border="border"
  154. :summary-method="summaryMethod"
  155. :sumText="sumText"
  156. :fixedLeftColumns="fixedLeftColumns"/>
  157. </view>
  158. <view class="zb-table-fixed-left"
  159. v-if="isFixedLeft"
  160. :style=" {height: `calc(100% - ${scrollbarSize}rpx)`}"
  161. >
  162. <template v-if="showHeader">
  163. <view class="zb-table-header" style="display: flex">
  164. <view class="item-tr"
  165. style=""
  166. @click.stop="rowClick(item,index)"
  167. v-for="(item,index) in fixedLeftColumns" :key="index">
  168. <view
  169. :style="{
  170. width:`${item.width?item.width:'200'}rpx`,
  171. borderRight:`${border?'2rpx solid #e8e8e8':''}`,
  172. borderTop:`${border?'2rpx solid #e8e8e8':''}`,
  173. textAlign:item.align||'left'
  174. }"
  175. @click.stop="sortAction(item,index)"
  176. class="item-th"
  177. >
  178. <template v-if="item.type==='selection'">
  179. <view class="checkbox-item">
  180. <tableCheckbox
  181. :indeterminate="indeterminate" :checked="checkedAll" @checkboxSelected="checkboxSelectedAll"></tableCheckbox>
  182. </view>
  183. </template>
  184. <template v-else>
  185. {{ item.label }}
  186. <block v-if="item.manfen">/{{item.manfen}}</block>
  187. <view class="sorter-table" v-if="item.sorter">
  188. <view :class="['sorter-table-icon',item.sorterMode==='_asc'&&`sorting${item.sorterMode||''}`]"></view>
  189. <view :class="['sorter-table-icon',item.sorterMode==='_desc'&&`sorting${item.sorterMode||''}`]"></view>
  190. </view>
  191. </template>
  192. </view>
  193. </view>
  194. </view>
  195. </template>
  196. <scroll-view
  197. scroll-y="true"
  198. id="leftTableFixed"
  199. :upper-threshold="15"
  200. @scrolltoupper="(e)=>scrollToFixedLeft(e)"
  201. @scroll="leftFixedScrollAction"
  202. :scroll-top="leftFiexScrollTop"
  203. class="zb-table-body-inner"
  204. :style=" `height: calc(100% - ${showSummary?160:80}rpx)`">
  205. <view class="zb-table-fixed">
  206. <view class="zb-table-tbody">
  207. <view
  208. :class="['item-tr',stripe?(i % 2) != 0?'odd':'even':'',highlight&&isHighlight(ite,i)?'current-row':'']"
  209. v-for="(ite,i) in transData"
  210. @click.stop="rowClick(ite,i)"
  211. :key="ite.key"
  212. style="">
  213. <view class='item-td'
  214. :style="[{
  215. width:`${item.width?item.width:'200'}rpx`,
  216. borderRight:`${border?'2rpx solid #e8e8e8':''}`,
  217. textAlign:item.align||'left'
  218. },cellStyle&&getCellStyle(ite,item,i,index)]"
  219. :key="index"
  220. v-for="(item,index) in fixedLeftColumns">
  221. <template v-if="item.type==='selection'">
  222. <view class="checkbox-item">
  223. <tableCheckbox @checkboxSelected="(e)=>checkboxSelected(e,ite)" :cellData="ite" :checked="ite.checked"/>
  224. </view>
  225. </template>
  226. <template v-else-if="item.type==='index'">
  227. {{i+1}}
  228. </template>
  229. <template v-else>
  230. {{ite[item.name]||item.emptyString}}
  231. </template>
  232. </view>
  233. </view>
  234. </view>
  235. </view>
  236. </scroll-view>
  237. <table-side-summary
  238. :scrollbarSize="scrollbarSize"
  239. v-if="showSummary&&!(scrollbarSize>0)"
  240. :data="data"
  241. :showSummary="showSummary"
  242. :transColumns="transColumns"
  243. :border="border"
  244. :summary-method="summaryMethod"
  245. :sumText="sumText"
  246. :fixedLeftColumns="fixedLeftColumns"/>
  247. </view>
  248. </view>
  249. <zb-load-more v-if="isLoadMore&&!completeLoading"/>
  250. </view>
  251. <!-- #endif -->
  252. <!-- #ifndef H5 || APP-PLUS -->
  253. <view class="zb-table-applet">
  254. <view class="zb-table-content">
  255. <scroll-view
  256. <!-- #ifdef MP-ALIPAY -->
  257. @scroll="scrollAlipay"
  258. <!-- #endif -->
  259. @scrolltolower="scrolltolower"
  260. <!-- #ifdef MP-ALIPAY -->
  261. style=" height: 100%;overflow-x:scroll"
  262. <!-- #endif -->
  263. <!-- #ifndef MP-ALIPAY -->
  264. style=" height: 100%"
  265. <!-- #endif -->
  266. scroll-y="true"
  267. scroll-x="true">
  268. <view class="zb-table-scroll" >
  269. <template v-if="showHeader">
  270. <view class="zb-table-header top-header-uni" style="">
  271. <view class="zb-table-fixed" >
  272. <view class="zb-table-thead" style="position: relative;" >
  273. <view class="item-tr">
  274. <view
  275. @click.stop="sortAction(item,index)"
  276. :class="['item-th',index <fixedLeftColumns.length&&'zb-stick-side']"
  277. :style="{
  278. left:`${item.left}rpx`,
  279. width:`${item.width?item.width:'200'}rpx`,
  280. flex:index===transColumns.length-1?1:'none',
  281. minWidth:`${item.width?item.width:'200'}rpx`,
  282. borderRight:`${border?'2rpx solid #e8e8e8':''}`,
  283. borderTop:`${border?'2rpx solid #e8e8e8':''}`,
  284. textAlign:item.align||'left'
  285. }"
  286. v-for="(item,index) in transColumns" :key="index">
  287. <template v-if="item.type==='selection'">
  288. <view class="checkbox-item">
  289. <tableCheckbox
  290. :indeterminate="indeterminate" :checked="checkedAll" @checkboxSelected="checkboxSelectedAll"></tableCheckbox>
  291. </view>
  292. </template>
  293. <template v-else>
  294. {{ item.label||'' }}
  295. <block v-if="item.manfen">/{{item.manfen}}</block>
  296. <view class="sorter-table" v-if="item.sorter">
  297. <view :class="['sorter-table-icon',item.sorterMode==='_asc'&&`sorting${item.sorterMode||''}`]"></view>
  298. <view :class="['sorter-table-icon',item.sorterMode==='_desc'&&`sorting${item.sorterMode||''}`]"></view>
  299. </view>
  300. </template>
  301. </view>
  302. </view>
  303. </view>
  304. </view>
  305. </view>
  306. </template>
  307. <template v-if="!data.length">
  308. <view class="no-data">暂无数据~~</view>
  309. </template>
  310. <view class="zb-table-fixed">
  311. <view class="zb-table-tbody">
  312. <view :class="['item-tr',highlight&&isHighlight(item,index)?'current-row':'']"
  313. @click.stop="rowClick(item,index)"
  314. v-for="(item,index) in transData" :key="item.key" >
  315. <view
  316. :style="[{
  317. left:`${ite.left}rpx`,
  318. width:`${ite.width?ite.width:'200'}rpx`,
  319. flex:i===transColumns.length-1?1:'none',
  320. minWidth:`${ite.width?ite.width:'200'}rpx`,
  321. borderRight:`${border?'2rpx solid #e8e8e8':''}`,
  322. textAlign:ite.align||'left',
  323. },getCellStyle(item,ite,index,i)]"
  324. :class="['item-td', i <fixedLeftColumns.length&&'zb-stick-side',stripe?(index % 2) != 0?'odd':'even':'']"
  325. v-for="(ite,i) in transColumns" :key="i">
  326. <template v-if="ite.type==='operation'">
  327. <view style="display: flex;align-items: center;height: 100%">
  328. <view
  329. v-for="ren,ind in permission(item,ite.renders,index)"
  330. :key="ind"
  331. @click.stop="$emit(ren.func,item,index)"
  332. :style="{
  333. display:'flex',
  334. alignItems: 'center',
  335. marginRight:ite.renders.length>1?'16rpx':'0'
  336. }">
  337. <template v-if="ren.type==='custom'">
  338. <view :class="ren.class||''" style="cursor: pointer">
  339. {{ren.name}}
  340. </view>
  341. </template>
  342. <template v-else>
  343. <button
  344. :class="ren.class||''"
  345. :type="ren.type||'primary'" :size="ren.size||'mini'">{{ren.name}}</button>
  346. </template>
  347. </view>
  348. </view>
  349. </template>
  350. <!-- 格式化处理 -->
  351. <template v-else-if="ite.type==='format'">
  352. <view>{{kayType(itemFilter(item,ite),parameter[ite.key])}}</view>
  353. </template>
  354. <template v-else-if="ite.type==='selection'">
  355. <view class="checkbox-item">
  356. <tableCheckbox @checkboxSelected="(e)=>checkboxSelected(e,item)" :cellData="item" :checked="item.checked"/>
  357. </view>
  358. </template>
  359. <template v-else-if="ite.type==='img'">
  360. <block v-if="item[ite.name]" >
  361. <image v-for="(pite,pidx) in getPhotoFn(item[ite.name])" :key="pidx"
  362. @click.stop="previewImage(getPhotoFn(item[ite.name]),pite,pidx)"
  363. :show-menu-by-longpress="false"
  364. :src="pite" style="width: 80rpx;height:60rpx; " mode="aspectFit"></image>
  365. </block>
  366. <text v-else>{{ite.emptyString}}</text>
  367. </template>
  368. <template v-else-if="ite.type==='index'">
  369. {{index+1}}
  370. </template>
  371. <template v-else>
  372. <!-- {{ ite.filters?itemFilter(item,ite):(item[ite.name]==null||item[ite.name]==='')?ite.emptyString:item[ite.name] }}-->
  373. {{ ite.filters?itemFilter(item,ite):formatterAction(item,ite,index,i) }}
  374. </template>
  375. </view>
  376. </view>
  377. </view>
  378. </view>
  379. <table-summary
  380. v-if="showSummary"
  381. :data="data"
  382. :showSummary="showSummary"
  383. :fixedLeftColumns="fixedLeftColumns"
  384. :transColumns="transColumns"
  385. :border="border"
  386. :summary-method="summaryMethod"
  387. :sumText="sumText"
  388. />
  389. </view>
  390. </scroll-view>
  391. </view>
  392. <zb-load-more v-if="isLoadMore&&!completeLoading"/>
  393. </view>
  394. <!-- #endif -->
  395. </template>
  396. <script>
  397. import TableCheckbox from './components/table-checkbox.vue'
  398. import TableSummary from "./components/table-summary.vue";
  399. import TableSideSummary from "./components/table-side-summary.vue";
  400. import TableH5Summary from './components/table-h5-summary'
  401. import ZbLoadMore from './components/zb-load-more'
  402. import config from '@/config'
  403. const baseUrl = config.baseUrl
  404. import {getScrollbarSize} from "./js/util";
  405. // fit 列的宽度是否自撑开
  406. // column 显示的列数据
  407. // stripe 是否为斑马纹 table
  408. // show-header 是否显示表头
  409. // parameter 格式化数据总对象
  410. export default {
  411. components:{
  412. TableCheckbox,
  413. TableSummary,
  414. TableSideSummary,
  415. TableH5Summary,
  416. ZbLoadMore
  417. },
  418. props:{
  419. parameter:{
  420. type:Object,
  421. default:null
  422. },
  423. highlight:{
  424. type:Boolean,
  425. default:false
  426. },
  427. itemDate:{
  428. type:Object,
  429. default:()=>{}
  430. },
  431. columns:{
  432. type:Array,
  433. default:()=>[]
  434. },
  435. showSummary:{
  436. type:Boolean,
  437. default:false
  438. },
  439. isShowLoadMore:{
  440. type:Boolean,
  441. default:false
  442. },
  443. data:{
  444. type:[Object,Array],
  445. default:()=>[]
  446. },
  447. sumText:{
  448. type:String,
  449. default:'合计'
  450. },
  451. showHeader:{
  452. type:Boolean,
  453. default:true
  454. },
  455. border:{
  456. type:Boolean,
  457. default:false
  458. },
  459. stripe:{
  460. type:Boolean,
  461. default:true
  462. },
  463. fit:{
  464. type:Boolean,
  465. default:false
  466. },
  467. rowKey:[String, Function],
  468. summaryMethod:Function,
  469. pullUpLoading:Function,
  470. formatter:Function,
  471. cellStyle:Function,
  472. cellHeaderStyle:Function,
  473. permissionBtn:Function,
  474. },
  475. computed:{
  476. loadMoreHeight(){
  477. return this.isLoadMore?40:0
  478. },
  479. fixedLeftColumns(){
  480. let arr = []
  481. for(let i=0;i<this.columns.length;i++){
  482. let item = this.columns[i]
  483. if(item.fixed){
  484. arr.push(item)
  485. }else {
  486. break
  487. }
  488. }
  489. return arr
  490. },
  491. itemfilters(){
  492. return(item,ite)=>{
  493. if(item[ite.name]==null){
  494. return ite.emptyString
  495. }
  496. return item[ite.name]
  497. }
  498. },
  499. scrollbarSize(){
  500. // #ifdef H5
  501. return getScrollbarSize()
  502. // #endif
  503. // #ifndef H5
  504. return 0
  505. // #endif
  506. },
  507. isFixedLeft(){
  508. if(!this.columns.length){
  509. return false
  510. }
  511. if(!this.data.length){
  512. return false
  513. }
  514. let [firstArr] = this.columns
  515. return !!firstArr.fixed;
  516. },
  517. transColumns(){
  518. if(this.fit){
  519. this.columns.forEach(column=>{
  520. if(column.type==="operation"&&column.renders){
  521. let str = ''
  522. column.renders.map((item)=>{
  523. str+=item.name
  524. })
  525. column.width = this.getTextWidth(str)+column.renders.length*40
  526. }else if(column.type==="img"){
  527. }else if(column.type==="selection"){
  528. }else{
  529. let arr = [this.getTextWidth(column.label)]
  530. this.data.forEach(data=>{
  531. let str = (data[column.name]+'')
  532. if(str==='undefined'){
  533. arr.push(60)
  534. }else{
  535. let width = this.getTextWidth(str)
  536. arr.push(width)
  537. }
  538. })
  539. column.width = Math.max(...arr)+40
  540. }
  541. })
  542. }
  543. let number = 0
  544. this.columns.forEach((item,index)=>{
  545. if(item.type==="operation"&&item.renders&&!item.width){
  546. let str = ''
  547. item.renders.map((item)=>{
  548. str+=item.name
  549. })
  550. item.width = this.getTextWidth(str)+item.renders.length*40
  551. }
  552. if(item.fixed){
  553. if(index===0){
  554. item.left = 0
  555. number+=item.width
  556. }else {
  557. item.left = number
  558. number+=item.width
  559. }
  560. }
  561. item.emptyString = item.emptyString||'--'
  562. })
  563. return this.columns
  564. },
  565. transData(){
  566. let flag = this.columns.some(item=>item.type==='selection')
  567. this.data.forEach((item,index)=>{
  568. if(flag){
  569. if(item.checked){
  570. if(!this.selectArr.length){
  571. this.selectArr.push(item)
  572. }
  573. }
  574. }
  575. if(this.rowKey){
  576. if(typeof this.rowKey==='function'){
  577. item.key = Object.freeze(this.rowKey(item))||Date.now()
  578. }else {
  579. item.key = Object.freeze(item[this.rowKey])||Date.now()
  580. }
  581. }else {
  582. item.key = index
  583. }
  584. })
  585. if(flag&&this.data.length){
  586. let le = this.data.filter(item=>item.checked).length
  587. if(le){
  588. if(le===this.data.length){
  589. this.checkedAll = true
  590. }else {
  591. this.indeterminate = true
  592. }
  593. }
  594. }
  595. return this.data
  596. },
  597. isHighlight(){
  598. return (item,index)=>{
  599. if(this.rowKey){
  600. return item.key === this.currentRow['key']
  601. }else{
  602. return index === this.currentRowIndex
  603. }
  604. }
  605. },
  606. getHeaderCellStyle() {
  607. return (column, columnIndex,childIndex)=>{
  608. const cellStyle = this.cellHeaderStyle;
  609. if(typeof cellStyle==='function'){
  610. return cellStyle({ column, columnIndex})
  611. }
  612. return {}
  613. }
  614. },
  615. getCellStyle() {
  616. return (row, column, rowIndex, columnIndex)=>{
  617. const cellStyle = this.cellStyle;
  618. if(typeof cellStyle==='function'){
  619. return cellStyle({row, column, rowIndex, columnIndex})
  620. }
  621. return {}
  622. }
  623. },
  624. },
  625. data() {
  626. return {
  627. button:[],
  628. alipayScrollTop:0,
  629. alipayScrollOldTop:0,
  630. alipayFlag:false,
  631. bodyTableLeft:0,
  632. headerTableLeft:0,
  633. lastScrollLeft:0,
  634. isLoadMore:false,
  635. headerFooterTableLeft:0,
  636. leftFiexScrollTop:0,
  637. bodyScrollTop:0,
  638. currentDriver:null,
  639. currentDriver1:null,
  640. bodyTime:null,
  641. currentRowIndex:null,
  642. currentRow: {},
  643. bodyTime1:null,
  644. headerTime:null,
  645. debounceTime:null,
  646. operation:{},
  647. completedFlag:false,
  648. selectArr:[],
  649. indeterminate:false,
  650. checkedAll:false,
  651. completeLoading:false,
  652. aliTime:null,
  653. }
  654. },
  655. created(){
  656. },
  657. mounted(){
  658. },
  659. methods: {
  660. kayType(value,datas){
  661. var actions = [];
  662. var idx=0;
  663. Object.keys(datas).some((key) => {
  664. if (datas[key].value == ('' + value)) {
  665. actions.push(datas[key].label);
  666. return true;
  667. }
  668. })
  669. return actions.join('')
  670. },
  671. getMove(){
  672. },
  673. formatterAction(row,column,rowIndex,columnIndex){
  674. if(column.formatter&&typeof this.formatter==='function'){
  675. return this.formatter(row,column,rowIndex,columnIndex)
  676. }
  677. return (row[column.name]==null||row[column.name]==='')?column.emptyString:row[column.name]
  678. },
  679. permission(item,renders,index){
  680. if(this.permissionBtn&&typeof this.permissionBtn==='function'){
  681. return this.permissionBtn(item,renders,index)
  682. }
  683. return renders
  684. },
  685. pullUpCompleteLoading(type){
  686. this.isLoadMore = false
  687. if(type==='ok'){
  688. this.completeLoading = true
  689. }
  690. },
  691. scrollAlipay(e){
  692. if(!this.alipayScrollOldTop){
  693. this.alipayScrollOldTop = e.detail.scrollTop
  694. }
  695. this.aliTime&&clearTimeout(this.aliTime)
  696. this.aliTime = setTimeout(()=>{
  697. if(this.alipayFlag&&e.detail.scrollTop>this.alipayScrollOldTop){
  698. this.pullLoad()
  699. }
  700. this.alipayFlag = false
  701. this.alipayScrollOldTop = null
  702. },500)
  703. },
  704. pullLoad(){
  705. if(this.isShowLoadMore){
  706. this.isLoadMore = true
  707. this.$emit('pullUpLoading')
  708. let that = this
  709. this.pullUpLoading&&this.pullUpLoading.call(this.$parent.$parent, (type)=>{
  710. that.isLoadMore = false
  711. if(type==='ok'){
  712. that.completeLoading=true
  713. }
  714. })
  715. }
  716. },
  717. scrolltolower(e){
  718. this.alipayFlag = true
  719. if(e.detail.direction==='bottom'){
  720. this.pullLoad()
  721. }
  722. // this.pullUpLoading.call(this.$parent)
  723. },
  724. // 多张图片处理
  725. getPhotoFn(url){
  726. var list=url.split(',')
  727. var newArr=[];
  728. list.forEach(ite=>{
  729. var s=baseUrl+ite;
  730. newArr.push(s)
  731. })
  732. return newArr
  733. },
  734. previewImage(item,url,current){
  735. uni.previewImage({
  736. current,
  737. urls:item
  738. })
  739. },
  740. resetHighlight(){
  741. this.currentRowIndex = null
  742. this.currentRow = {}
  743. },
  744. rowClick(row,index){
  745. if(this.highlight){
  746. this.currentRowIndex = index
  747. this.currentRow = row
  748. this.$emit('currentChange',row,index)
  749. }
  750. this.$emit('rowClick',row,index)
  751. },
  752. checkboxSelectedAll(e){
  753. this.indeterminate = false
  754. if(e.checked){
  755. this.selectArr = []
  756. this.checkedAll = true
  757. this.data.forEach(item=>{
  758. // this.$set(item,'checked',true)
  759. item.checked = true
  760. this.selectArr.push(item)
  761. })
  762. }else{
  763. this.checkedAll = false
  764. this.data.forEach(item=>{
  765. this.$set(item,'checked',false)
  766. })
  767. this.selectArr = []
  768. }
  769. // #ifndef H5 || APP-PLUS
  770. this.$forceUpdate()
  771. // #endif
  772. this.$emit('toggleAllSelection',e.checked,this.selectArr)
  773. },
  774. checkboxSelected(e,item){
  775. // #ifdef H5 || APP-PLUS
  776. this.$set(item,'checked',e.checked)
  777. // #endif
  778. // #ifndef H5 || APP-PLUS
  779. this.data.forEach(item=>{
  780. if(item.key===e.data.key){
  781. item.checked = e.checked
  782. }
  783. })
  784. // #endif
  785. item.checked = e.checked
  786. e.data.checked = e.checked
  787. if(e.checked){
  788. this.selectArr.push(e.data)
  789. }else{
  790. this.selectArr = this.selectArr.filter(item=>item.key!==e.data.key)
  791. }
  792. if(this.selectArr.length===this.transData.length){
  793. this.indeterminate = false
  794. this.checkedAll = true
  795. }else{
  796. this.indeterminate = true
  797. this.checkedAll = false
  798. }
  799. if(!this.selectArr.length){
  800. this.checkedAll = false
  801. this.indeterminate = false
  802. }
  803. // #ifndef H5 || APP-PLUS
  804. this.$forceUpdate()
  805. // #endif
  806. this.$emit('toggleRowSelection',e.checked,this.selectArr)
  807. },
  808. itemFilter(item,ite){
  809. if(ite.filters&&ite.name){
  810. let key = item[ite.name]
  811. return ite.filters[key]||''
  812. }
  813. return item[ite.name]||ite.emptyString
  814. },
  815. // 默认字体为微软雅黑 Microsoft YaHei,字体大小为 14px
  816. getTextWidth(str) {
  817. if(str.length<3){
  818. return 80
  819. }
  820. let regx = /^[0-9]+.?[0-9]*$/
  821. let flexWidth = 0
  822. for (const char of str) {
  823. if ((char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z')) {
  824. // 如果是英文字符,为字符分配8个单位宽度
  825. flexWidth += 20
  826. } else if (char >= '\u4e00' && char <= '\u9fa5') {
  827. // 如果是中文字符,为字符分配15个单位宽度
  828. flexWidth += 30
  829. } else if(regx.test(char)){
  830. flexWidth += 18
  831. }else {
  832. // 其他种类字符,为字符分配8个单位宽度
  833. flexWidth += 14
  834. }
  835. }
  836. return flexWidth
  837. },
  838. width(item){
  839. return `${item.width?item.width:'200'}rpx`
  840. },
  841. showStripe(index){
  842. if(this.currentDriver)return
  843. if(this.stripe){
  844. return (index % 2) != 0?'odd':'even'
  845. }else{
  846. return ''
  847. }
  848. },
  849. //验证字符串是否是数字
  850. checkNumber(theObj) {
  851. var reg = /^[0-9]+.?[0-9]*$/;
  852. if (reg.test(theObj)) {
  853. return true;
  854. }
  855. return false;
  856. },
  857. isDate(data){
  858. if(isNaN(data)&&!isNaN(Date.parse(data))){
  859. return true
  860. }
  861. return false
  862. },
  863. sortAction(item,index){
  864. if(!item.sorter){return false}
  865. this.$set(item,'sorterMode',item.sorterMode==='_asc'?'_desc':'_asc')
  866. if(item.sorter==='custom'){
  867. this.$emit('sort-change',item,item.sorterMode.replace('_',''),index)
  868. }else {
  869. this.sortData(item)
  870. }
  871. // #ifndef H5 || APP-PLUS
  872. this.$forceUpdate()
  873. // #endif
  874. },
  875. sortData(item){
  876. let key = item.name
  877. if(item.sorterMode==='_asc'){
  878. this.data.sort((a,b)=>{
  879. if(this.checkNumber(a[key])){
  880. return a[key]-b[key]
  881. }
  882. if(this.isDate(a[key])){
  883. let a1 = new Date(a[key]).getTime()
  884. let b1 = new Date(b[key]).getTime()
  885. return a1-b1
  886. }
  887. })
  888. }else {
  889. this.data.sort((a,b)=>{
  890. if(this.checkNumber(a[key])){
  891. return b[key]-a[key]
  892. }
  893. if(this.isDate(a[key])){
  894. let a1 = new Date(a[key]).getTime()
  895. let b1 = new Date(b[key]).getTime()
  896. return b1-a1
  897. }
  898. })
  899. }
  900. },
  901. throttle(method,delay=60){
  902. let time = null
  903. return (...args)=>{
  904. if(!time){
  905. time = setTimeout(()=>{
  906. method(...args)
  907. time = null;
  908. },delay)
  909. }
  910. }
  911. },
  912. debounce(method,delay=1000){
  913. return (...args)=>{
  914. this.debounceTime&&clearTimeout(this.debounceTime)
  915. this.debounceTime = setTimeout(()=>{
  916. method(...args)
  917. },delay)
  918. }
  919. },
  920. handleBodyScroll(e){
  921. if(this.currentDriver&&this.currentDriver!==e.currentTarget.id)return
  922. this.currentDriver = e.currentTarget.id
  923. this.headerTableLeft = e.detail.scrollLeft
  924. this.headerFooterTableLeft = e.detail.scrollLeft
  925. this.leftFiexScrollTop = e.detail.scrollTop
  926. this.bodyTime&&clearTimeout(this.bodyTime)
  927. this.bodyTime = setTimeout(()=>{
  928. this.currentDriver=null
  929. },200)
  930. },
  931. leftFixedScrollAction(e){
  932. if(this.currentDriver&&this.currentDriver!==e.currentTarget.id)return
  933. this.currentDriver = e.currentTarget.id
  934. this.bodyScrollTop = e.detail.scrollTop
  935. this.bodyTime&&clearTimeout(this.bodyTime)
  936. this.bodyTime = setTimeout(()=>{
  937. this.currentDriver=null
  938. },200)
  939. },
  940. scrollToLeft(e){
  941. if(this.currentDriver1&&this.currentDriver1!==e.currentTarget.id)return
  942. this.currentDriver1 = e.currentTarget.id
  943. if(e.detail.direction==='left'&&this.headerTableLeft<10){
  944. this.headerTableLeft = 0
  945. }else if(e.detail.direction==='top'&&this.leftFiexScrollTop<10){
  946. this.leftFiexScrollTop = 0
  947. }
  948. this.bodyTime&&clearTimeout(this.bodyTime)
  949. this.bodyTime = setTimeout(()=>{
  950. this.currentDriver1=null
  951. },200)
  952. },
  953. scrollToFixedLeft(e){
  954. if(this.currentDriver1&&this.currentDriver1!==e.currentTarget.id)return
  955. this.currentDriver1 = e.currentTarget.id
  956. if(e.detail.direction==='top'&&this.bodyScrollTop<10){
  957. this.bodyScrollTop = 0
  958. }
  959. this.bodyTime&&clearTimeout(this.bodyTime)
  960. this.bodyTime = setTimeout(()=>{
  961. this.currentDriver1=null
  962. },200)
  963. },
  964. handleTableScrollLeft(e,type){
  965. if(this.currentDriver&&this.currentDriver!==e.currentTarget.id)return
  966. this.currentDriver = e.currentTarget.id
  967. this.bodyTableLeft = e.detail.scrollLeft
  968. this.headerFooterTableLeft = e.detail.scrollLeft
  969. this.bodyTime&&clearTimeout(this.bodyTime)
  970. this.bodyTime = setTimeout(()=>{
  971. this.currentDriver=null
  972. },200)
  973. },
  974. handleFooterTableScrollLeft(e){
  975. if(this.currentDriver&&this.currentDriver!==e.currentTarget.id)return
  976. this.currentDriver = e.currentTarget.id
  977. this.bodyTableLeft = e.detail.scrollLeft
  978. this.headerTableLeft = e.detail.scrollLeft
  979. this.bodyTime&&clearTimeout(this.bodyTime)
  980. this.bodyTime = setTimeout(()=>{
  981. this.currentDriver=null
  982. },200)
  983. }
  984. }
  985. }
  986. </script>
  987. <style lang="scss">
  988. .zb-table-fixed-left{
  989. /*去除左边滚动条 */
  990. scroll-view ::-webkit-scrollbar {
  991. display: none !important;
  992. width: 0 !important;
  993. height: 0 !important;
  994. -webkit-appearance: none;
  995. background: transparent;
  996. }
  997. }
  998. .zb-table-header{
  999. ///*去除头部滚动条 */
  1000. scroll-view ::-webkit-scrollbar {
  1001. display: none !important;
  1002. width: 0 !important;
  1003. height: 0 !important;
  1004. -webkit-appearance: none;
  1005. background: transparent;
  1006. }
  1007. }
  1008. </style>
  1009. <style lang="scss" scoped>
  1010. .sorter-table{
  1011. position: absolute;
  1012. right: 2rpx;
  1013. top:50%;
  1014. transform:translateY(-50%);
  1015. .sorter-table-icon{
  1016. width: 0;
  1017. height: 0;
  1018. color: #dcdcdc;
  1019. border-right: 8rpx solid transparent;
  1020. border-left: 8rpx solid transparent;
  1021. }
  1022. .sorter-table-icon:first-child{
  1023. border-bottom: 14rpx solid currentColor;
  1024. }
  1025. .sorter-table-icon:last-child{
  1026. margin-top: 3rpx;
  1027. border-top: 14rpx solid currentColor;
  1028. }
  1029. .sorting_desc{
  1030. color: #2979ff;
  1031. }
  1032. .sorting_asc{
  1033. color: #2979ff;
  1034. }
  1035. }
  1036. .checkbox-item{
  1037. display: flex;align-items: center;justify-content: center;width: 100%;height: 100%
  1038. }
  1039. .no-data{
  1040. width: 100%;
  1041. height: 80rpx;
  1042. display: flex;
  1043. justify-content: center;
  1044. align-items: center;
  1045. // border-bottom: 1px solid #e8e8e8;
  1046. }
  1047. .item-th{
  1048. position: relative;
  1049. flex-shrink: 0;
  1050. width: 200rpx;
  1051. overflow-wrap: break-word;
  1052. border-bottom: 2rpx solid #e8e8e8;
  1053. transition: background 0.3s;
  1054. padding-right: 8rpx;
  1055. word-break:keep-all; /* 不换行 */
  1056. white-space:nowrap; /* 不换行 */
  1057. overflow:hidden; /* 内容超出宽度时隐藏超出部分的内容 */
  1058. text-overflow:ellipsis; /* 当对象内文本溢出时显示省略标记(...) ;需与overflow:hidden;一起使用。*/
  1059. overflow-wrap: break-word;
  1060. font-weight: bold;
  1061. font-size: 26rpx;
  1062. color: #161616;
  1063. }
  1064. .zb-table{
  1065. height: 100%;
  1066. overflow: hidden;
  1067. width: 100%;
  1068. display: flex;
  1069. flex-direction: column;
  1070. font-size: 28rpx;
  1071. position: relative;
  1072. .zb-table-content{
  1073. //height: 100%;
  1074. //flex: 1;
  1075. position: relative;
  1076. overflow: hidden;
  1077. }
  1078. .zb-table-fixed{
  1079. min-width: 100%;
  1080. }
  1081. .zb-table-body{
  1082. position: relative;
  1083. background: #fff;
  1084. transition: opacity 0.3s;
  1085. }
  1086. .item-tr{
  1087. display: flex;
  1088. //height: 41px;
  1089. }
  1090. .item-td{
  1091. flex-shrink: 0;
  1092. width:200rpx;
  1093. padding-left: 8rpx;
  1094. height: 80rpx;
  1095. line-height: 80rpx;
  1096. padding-right: 8rpx;
  1097. box-sizing: border-box;
  1098. word-break:keep-all; /* 不换行 */
  1099. white-space:nowrap; /* 不换行 */
  1100. overflow:hidden; /* 内容超出宽度时隐藏超出部分的内容 */
  1101. text-overflow:ellipsis; /* 当对象内文本溢出时显示省略标记(...) ;需与overflow:hidden;一起使用。*/
  1102. overflow-wrap: break-word;
  1103. border-bottom: 2rpx solid #e8e8e8;
  1104. //transition: background 0.3s;
  1105. }
  1106. .zb-table-fixed-left .zb-table-header{
  1107. overflow-y: hidden;
  1108. }
  1109. .zb-table-header {
  1110. overflow: hidden;
  1111. background: #F4F9FC;
  1112. .item-th{
  1113. padding-left: 8rpx;
  1114. line-height: 78rpx;
  1115. height: 80rpx;
  1116. //display: flex;
  1117. //align-items: center;
  1118. box-sizing: border-box;
  1119. }
  1120. }
  1121. .zb-table-fixed-left .zb-table-fixed{
  1122. background: #fff;
  1123. }
  1124. .zb-table-fixed-right .zb-table-fixed{
  1125. background: #fff;
  1126. }
  1127. .zb-table-body-inner{
  1128. height: 100%;
  1129. // overflow: scroll;
  1130. }
  1131. .zb-table-fixed-left{
  1132. position: absolute;
  1133. top: 0;
  1134. z-index: 1;
  1135. overflow: hidden;
  1136. border-radius: 0;
  1137. height: 100%;
  1138. //transition: box-shadow 0.3s ease;
  1139. }
  1140. .odd{
  1141. background: #F4F9FC;
  1142. //height: 100%;
  1143. width: 100%;
  1144. }
  1145. .even{
  1146. background-color:white ;
  1147. //height: 100%;
  1148. width: 100%;
  1149. }
  1150. }
  1151. .scroll-left-fixed{
  1152. .zb-table-fixed-left {
  1153. left: 0;
  1154. box-shadow: 12rpx 0 12rpx -8rpx #ccc;
  1155. }
  1156. }
  1157. .zb-table-applet{
  1158. height: 100%;
  1159. //overflow: hidden;
  1160. width: 100%;
  1161. position: relative;
  1162. display: flex;
  1163. flex-direction: column;
  1164. font-size: 28rpx;
  1165. .zb-table-content{
  1166. //height: 100%;
  1167. flex: 1;
  1168. overflow: hidden;
  1169. position: relative;
  1170. }
  1171. .zb-table-fixed{
  1172. min-width: 100%;
  1173. width: fit-content;
  1174. }
  1175. .zb-table-body{
  1176. position: relative;
  1177. background: #fff;
  1178. transition: opacity 0.3s;
  1179. }
  1180. .item-tr{
  1181. display: flex;
  1182. //height: 41px;
  1183. }
  1184. .item-td{
  1185. flex-shrink: 0;
  1186. width: 200rpx;
  1187. padding-left: 8rpx;
  1188. height: 80rpx;
  1189. line-height: 80rpx;
  1190. padding-right:8rpx;
  1191. box-sizing: border-box;
  1192. word-break:keep-all; /* 不换行 */
  1193. white-space:nowrap; /* 不换行 */
  1194. overflow:hidden; /* 内容超出宽度时隐藏超出部分的内容 */
  1195. text-overflow:ellipsis; /* 当对象内文本溢出时显示省略标记(...) ;需与overflow:hidden;一起使用。*/
  1196. overflow-wrap: break-word;
  1197. border-bottom: 2rpx solid #e8e8e8;
  1198. //transition: background 0.3s;
  1199. }
  1200. .zb-table-header {
  1201. //overflow: hidden;
  1202. position: sticky;
  1203. top: 0;
  1204. z-index: 2;
  1205. //width: fit-content;
  1206. .item-th{
  1207. padding-left: 8rpx;
  1208. line-height: 78rpx;
  1209. height: 80rpx;
  1210. box-sizing: border-box;
  1211. background: #fafafa;
  1212. }
  1213. .zb-stick-side{
  1214. position: sticky;
  1215. top: 0;
  1216. left: 0;
  1217. z-index: 2;
  1218. //border-right: solid 1rpx #dbdbdb;
  1219. box-sizing: border-box;
  1220. background: #fafafa;
  1221. //box-shadow: 6px 0 6px -4px #ccc;
  1222. }
  1223. }
  1224. .zb-table-fixed-left .zb-table-fixed{
  1225. background: #fff;
  1226. }
  1227. .zb-table-fixed-right .zb-table-fixed{
  1228. background: #fff;
  1229. }
  1230. .zb-table-fixed-header .zb-table-body-inner{
  1231. height: 100%;
  1232. // overflow: scroll;
  1233. }
  1234. .zb-table-fixed-left{
  1235. position: absolute;
  1236. top: 0;
  1237. z-index: 1;
  1238. overflow: hidden;
  1239. border-radius: 0;
  1240. height: 100%;
  1241. //transition: box-shadow 0.3s ease;
  1242. }
  1243. .scroll-left-fixed{
  1244. .zb-table-fixed-left {
  1245. left: 0;
  1246. box-shadow: 12rpx 0 12rpx -8rpx #ccc;
  1247. }
  1248. }
  1249. .odd{
  1250. background-color:rgba(249,249,249,0.6);
  1251. //height: 100%;
  1252. width: 100%;
  1253. }
  1254. .even{
  1255. background-color:white ;
  1256. //height: 100%;
  1257. width: 100%;
  1258. }
  1259. .zb-table-tbody {
  1260. .zb-stick-side{
  1261. position: sticky;
  1262. left: 0;
  1263. z-index: 1;
  1264. box-sizing: border-box;
  1265. background:white;
  1266. //box-shadow: 6px 0 6px -2px #ccc;
  1267. }
  1268. .odd{
  1269. background:#f9f9f9;
  1270. //height: 100%;
  1271. width: 100%;
  1272. }
  1273. .even{
  1274. background:white ;
  1275. //height: 100%;
  1276. width: 100%;
  1277. }
  1278. }
  1279. .current-row{
  1280. .item-td{
  1281. background-color: #ecf5ff;
  1282. }
  1283. }
  1284. }
  1285. .current-row{
  1286. .item-td{
  1287. background-color: #ecf5ff;
  1288. }
  1289. }
  1290. .zb-table-header{
  1291. height: 80rpx;
  1292. }
  1293. </style>