byTable.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. <template>
  2. <div class="by-table">
  3. <vxe-table
  4. ref="table"
  5. :data="value"
  6. :size="attrs.size?attrs.size:'small'"
  7. sync-resize
  8. auto-resize
  9. :edit-rules="attrs.editRules"
  10. :height="attrs.height"
  11. :max-height="attrs.maxHeight"
  12. :stripe="attrs.stripe"
  13. :border="attrs.border"
  14. :align="attrs.align"
  15. :show-footer="attrs.showFooter"
  16. :footer-method="attrs.footerMethod"
  17. :tree-config="{transform: attrs.transform, rowField: attrs.rowField, parentField: attrs.parentField}"
  18. :row-config="{isHover: true}"
  19. :tooltip-config="{showAll: true}"
  20. :radio-config="{trigger: attrs.triggerRowCheck}"
  21. :checkbox-config="{ checkMethod: attrs.checkMethod, visibleMethod: attrs.visibleMethod,trigger: attrs.triggerRowCheck}">
  22. style="width: 100%">
  23. <vxe-column v-if="attrs.checkbox" type="checkbox" width="50" fixed="left" />
  24. <vxe-column v-if="attrs.radio" type="radio" width="50" fixed="left" />
  25. <vxe-column v-if="attrs.seq" type="seq" title="序号" :width="attrs.seqWidth?attrs.seqWidth:50" fixed="left" :tree-node="attrs.treeNodeSeq" />
  26. <template v-for="(item,index) of columns">
  27. <vxe-column
  28. v-if="item.action"
  29. :key="'action_'+index"
  30. :title="item.title"
  31. :width="item.width"
  32. resizable
  33. fixed="right"
  34. >
  35. <template #default="{ row }">
  36. <template v-if="item.plugins">
  37. <template v-for="(pluginsItem,_index) of item.plugins" >
  38. <span class="col-action"
  39. :key="'plu'+_index"
  40. v-if="showPlugin(pluginsItem,row)"
  41. @click="pluginClick(pluginsItem,row)">
  42. <i v-if="pluginsItem.icon" :class="pluginsItem.icon" ></i>
  43. {{ pluginsItem.name }}
  44. </span>
  45. </template>
  46. </template>
  47. </template>
  48. </vxe-column>
  49. <vxe-column
  50. v-if="!item.action"
  51. :key="item.field?item.field+getUuid():index+getUuid()"
  52. :field="item.field"
  53. :title="item.title"
  54. :width="item.width"
  55. :fixed="item.fixed"
  56. resizable
  57. :align="item.align"
  58. :edit-render="{}"
  59. :tree-node="item.treeNode"
  60. >
  61. <template #default="scope">
  62. <slot v-if="item.slot" :name='item.field' :row='scope.row' :rowIndex="scope.rowIndex"></slot>
  63. <component v-else-if="item.component" :is="item.component" :ref="item.field+'Comp'" :propConfig="item.compConfig" :parentValue="scope.row"
  64. :propValue="scope.row[item.field]" @onChange="onChange($event, scope,item.field)" />
  65. <template v-else>
  66. <div v-if="item.isDetail" :class="{'ellipsis':item.ellipsis}" :style="{ 'text-align': item.align }" class="tdCol detail" @click="detail(scope.row)">
  67. {{ item.formatField ? formatField(item,scope.row) : scope.row[item.field] }}
  68. <div class="copy-out" v-if="item.copy && scope.row[item.field]">
  69. <div class="copy" @click="copyHandle(scope.row[item.field])">复制</div>
  70. </div>
  71. </div>
  72. <div class="tdCol" :class="{'ellipsis':item.ellipsis}" :style="{ 'text-align': item.align }" v-else>
  73. {{ item.formatField ? formatField(item,scope.row) : scope.row[item.field] }}
  74. <div class="copy-out" v-if="item.copy && scope.row[item.field]">
  75. <div class="copy" @click="copyHandle(scope.row[item.field])">复制</div>
  76. </div>
  77. </div>
  78. </template>
  79. </template>
  80. </vxe-column>
  81. </template>
  82. </vxe-table>
  83. <div class="page" v-if="page.total > 0">
  84. <el-pagination
  85. @size-change="handleSizeChange"
  86. @current-change="handleCurrentChange"
  87. :current-page="page.pageNo"
  88. :page-sizes="attrs.pageSizes"
  89. :page-size="page.pageSize?page.pageSize:20"
  90. :layout="attrs.layoutPage?attrs.layoutPage:'total, sizes, prev, pager, next, jumper'"
  91. :total="page.total">
  92. </el-pagination>
  93. </div>
  94. </div>
  95. </template>
  96. <script lang="ts">
  97. /**
  98. config:{
  99. attr:{
  100. copy:true/false //复制
  101. triggerRowCheck:'row' //点击行选中
  102. size: medium / small / mini //尺寸
  103. height: '' //高度
  104. maxHeight:'' //最大高度
  105. stripe: true/false //是否为斑马纹
  106. border: true/false //是否带有纵向边框
  107. seq:true/false //显示序号
  108. seqWidth:'' //序号宽度
  109. checkbox: true/false //显示复选框
  110. radio:true/false //显示单选框
  111. align: left/center/right //对齐方式
  112. transform: true/false //开启自动将列表转成树结构
  113. rowField:'' row对应id
  114. parentField:'' row父id
  115. pageSize:'' //分页条数
  116. pageSizes:[] //每页显示个数选择器的选项设置
  117. layoutPage:'' //分页组件布局
  118. checkMethod:()=>{} //禁用复选框方法
  119. visibleMethod:()=>{} //显示复选框方法
  120. editRules:{}//表格验证
  121. },
  122. columns:[{
  123. ellipsis:true/false //超出省略号表示
  124. title:'' //标题
  125. field:'' //字段名
  126. align: left/center/right //对齐方式
  127. width:'' //列宽
  128. fixed: true, left, right // 固定列
  129. action: true/false 是否操作列
  130. plugins:操作列执行
  131. component:'' //组件
  132. compConfig:{} //组件配置
  133. slot: true/false //是否插槽
  134. isDetail:true/false //点击详情
  135. tree-node: true/false //展开节点
  136. formatField:(item)=>{} //格式化内容
  137. }],
  138. request:{}
  139. }
  140. */
  141. import { Component, Prop, Vue, Watch } from "vue-property-decorator";
  142. import VueViews from '@/benyun/compVue/VueViews'
  143. interface Page{
  144. pageNo?:number,
  145. pageSize?:number,
  146. total?:number
  147. }
  148. @Component
  149. export default class ByTable extends VueViews {
  150. value:Array<any>=[];
  151. key_id="_X_ROW_KEY"
  152. page = {
  153. pageNo: 1, //当前页
  154. pageSize: 20, //每页条数
  155. total: 0 //总条数
  156. }
  157. get columns(){
  158. return this.config?.columns ? this.config.columns : [];
  159. }
  160. created(){
  161. this.initConfig()
  162. }
  163. getUuid(){
  164. return (((1 + Math.random()) * 0x10000) | 0).toString(3).substring(1);
  165. }
  166. initConfig(){
  167. if(this.propConfig){
  168. this.setConfig(this.propConfig)
  169. }
  170. }
  171. mounted(){
  172. this.$nextTick(()=>{
  173. if(this.requestConfig){
  174. this.request();
  175. }
  176. })
  177. }
  178. validate(){
  179. let res = true
  180. const $table:any = this.$refs.table
  181. const data = $table.getTableData()
  182. $table.validate(data.tableData).catch(() => {})
  183. if(this.attrs.editRules && data.tableData && data.tableData.length > 0){
  184. for(const item of data.tableData) {
  185. for(const key in this.attrs.editRules){
  186. if(this.attrs.editRules[key] && this.attrs.editRules[key].length > 0) {
  187. for(const ruleItem of this.attrs.editRules[key]){
  188. if(ruleItem.required) {
  189. if(!item[key]){
  190. res = false;
  191. break
  192. }
  193. }
  194. if(ruleItem.validatorHandle && item[key] || item[key] === 0){
  195. res = ruleItem.validatorHandle(item[key])
  196. }
  197. if(!res) break
  198. }
  199. }
  200. if(!res) break
  201. }
  202. }
  203. }else{
  204. res = true
  205. }
  206. return res
  207. // console.log(errMap)
  208. // if (errMap) {
  209. // this.$message({message:'校验不通过!',type:'warning'})
  210. // } else {
  211. // this.$message({message:'校验成功!',type:'warning'})
  212. // }
  213. }
  214. recalculate(){
  215. if(this.$refs.table){
  216. (this.$refs.table as any).recalculate(true);
  217. this.$forceUpdate();
  218. }
  219. }
  220. loadTableData(data:Array<any>){
  221. if(this.$refs.table){
  222. (this.$refs.table as any).loadData(data);
  223. }
  224. }
  225. //点击详情
  226. detail(row:any){
  227. let data = (this as any).$lodash.cloneDeep(row);
  228. delete data[this.key_id];
  229. this.$emit('detail',data)
  230. }
  231. setConfigAfter(){
  232. if(this.attrs.pageSize){
  233. this.page.pageSize = this.attrs.pageSize;
  234. }
  235. this.recalculate();
  236. }
  237. copyHandle(text:string){
  238. let textarea:any = document.createElement('textarea');
  239. textarea.style.position = 'fixed';
  240. textarea.style.opacity = 0;
  241. textarea.value = text;
  242. document.body.appendChild(textarea);
  243. textarea.select();
  244. document.execCommand('copy');
  245. document.body.removeChild(textarea);
  246. this.$message({
  247. message:'复制成功!',
  248. type:'success'
  249. })
  250. }
  251. //设置分页
  252. setPage(page:Page){
  253. if(page.pageNo){
  254. this.page.pageNo = page.pageNo;
  255. }
  256. if(page.pageSize){
  257. this.page.pageSize = page.pageSize;
  258. }
  259. this.page.total = page.total?page.total:0;
  260. }
  261. getPage(){
  262. return this.page
  263. }
  264. //每页条数发生变化
  265. handleSizeChange(val:number) {
  266. this.page.pageSize = val;
  267. if (this.page.pageSize * val > this.page.total) {
  268. this.page.pageNo = 1
  269. }
  270. this.$emit('pagination',{pageNum:this.page.pageNo,pageSize:this.page.pageSize})
  271. }
  272. //当前页发生变化
  273. handleCurrentChange(val:number) {
  274. this.page.pageNo = val;
  275. this.$emit('pagination',{pageNum:this.page.pageNo,pageSize:this.page.pageSize})
  276. }
  277. //操作列点击操作
  278. pluginClick(config:any,row:any){
  279. if(config?.event?.click){
  280. // let data = (this as any).$lodash.cloneDeep(row)
  281. // delete data[this.key_id];
  282. config.event.click(row)
  283. }
  284. }
  285. //操作按钮是否显示
  286. showPlugin(config:any,row:any){
  287. if(config?.event?.show){
  288. let data = (this as any).$lodash.cloneDeep(row)
  289. delete data[this.key_id];
  290. return config.event.show(data)
  291. }
  292. return true
  293. }
  294. //格式化内容
  295. formatField(config:any,row:any){
  296. if(config.formatField){
  297. return config.formatField(row)
  298. }
  299. }
  300. //组件值的变化
  301. onChange(val:any,item:any,code:string){
  302. (this.$refs.table as any).updateStatus(item);
  303. let row = item.row;
  304. if(val && (val as any).constructor == Object){
  305. for (const key in val) {
  306. if (!row[key]) {
  307. Vue.set(row, key, val[key])
  308. } else {
  309. row[key] = val[key]
  310. }
  311. }
  312. }else{
  313. if(!row[code]){
  314. Vue.set(row, code, val)
  315. }else{
  316. row[code] = val
  317. }
  318. }
  319. this.$emit('onChangeRow',row);
  320. }
  321. //获取表格选中的数据
  322. getSelectData() {
  323. let data: Array<any> = []
  324. if (this.$refs.table) {
  325. if (this.attrs.radio && (this.$refs.table as any).getRadioRecord()) {
  326. data = (this as any).$lodash.cloneDeep([(this.$refs.table as any).getRadioRecord()]);
  327. }
  328. if(this.attrs.checkbox && (this.$refs.table as any).getCheckboxRecords()){
  329. data = (this as any).$lodash.cloneDeep((this.$refs.table as any).getCheckboxRecords());
  330. }
  331. }
  332. for(let item of data){
  333. delete item[this.key_id];
  334. }
  335. return data
  336. }
  337. //清除选中
  338. clearCheckboxRow(){
  339. if(this.attrs.checkbox){
  340. (this.$refs.table as any).clearCheckboxRow();
  341. }
  342. if(this.attrs.radio){
  343. (this.$refs.table as any).clearRadioRow();
  344. }
  345. }
  346. setValue(data:Array<any>){
  347. // setTimeout(()=>{
  348. this.value = data ? data : [];
  349. // },100)
  350. (this.$refs.table as any).clearValidate()
  351. this.$forceUpdate()
  352. }
  353. getValue(){
  354. let d = (this as any).$lodash.cloneDeep(this.value);
  355. for(let item of d){
  356. delete item[this.key_id];
  357. }
  358. return d;
  359. }
  360. request(){
  361. if(!this.requestConfig || !this.requestConfig.url){
  362. return
  363. }
  364. let parame = (this as any).$lodash.cloneDeep(this.requestConfig);
  365. parame.success = (res:any) => {
  366. if(res.data){
  367. this.setValue(res.data);
  368. }
  369. }
  370. parame.fail = (err:any) => {}
  371. this.requestHandle(parame);
  372. }
  373. }
  374. </script>
  375. <style lang="scss" scoped>
  376. .by-table{
  377. width: 100%;
  378. .page{
  379. width: 100%;
  380. display: flex;
  381. padding-top: 16px;
  382. justify-content: flex-end;
  383. }
  384. .tdCol{
  385. width: 100%;
  386. .copy-out{
  387. display: flex;
  388. justify-content: flex-end;
  389. }
  390. .copy{
  391. // position: absolute;
  392. // bottom: 0;
  393. // right: 0;
  394. border: solid 1px #0089ff;
  395. font-size: 12px;
  396. width: 40px;
  397. display: flex;
  398. align-items: center;
  399. justify-content: center;
  400. height: 20px;
  401. border-radius: 3px;
  402. cursor: pointer;
  403. color: #0089ff;
  404. background-color: #f1f7fc;
  405. }
  406. }
  407. .ellipsis{
  408. width: 100%;
  409. overflow: hidden;
  410. white-space: nowrap;
  411. text-overflow: ellipsis;
  412. }
  413. }
  414. .col-action{
  415. color: #0089ff;
  416. cursor: pointer;
  417. padding: 0 4px;
  418. }
  419. .detail{
  420. color: #0089ff;
  421. cursor: pointer;
  422. }
  423. </style>