123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- <template>
- <view class="base-table" :style="[getTableStyle]" :class="{ 'is-border': border, 'no-data': data.length === 0 }">
- <view class="base-table-inner">
- <view class="base-table-header" v-if="showHeader">
- <view class="b-table" :style="[tableBodyStyle]">
- <view class="b-thead">
- <view class="b-tr" :class="getHeaderClass" :style="getHeaderStyle" @click="handleHeaderClick">
- <view class="b-th" v-if="indexShow" :style="[getIndexColStyle]"><view class="b-cell">序号</view></view>
- <view class="b-th" v-for="item in columns" :key="item.fieldName" :class="[getCellProps(item).class]" :style="[getCellProps(item).style]">
- <view class="b-cell" :style="{fontSize:fontSize+'px'}">{{ item.fieldDesc }}</view>
- </view>
- </view>
- </view>
- </view>
- </view>
- <view class="base-table-body">
- <view class="b-table" :style="[tableBodyStyle]">
- <view class="b-tbody" v-if="data.length > 0">
- <view
- class="b-tr"
- v-for="(scope, index) in data"
- :key="index"
- :class="[getBodyClass(scope, index)]"
- :style="[getBodyStyle(scope, index)]"
- @click="handleRowClick(scope, index)"
- >
- <view class="b-td" v-if="indexShow" :style="[getIndexColStyle]">
- <view class="b-cell" :style="{fontSize:fontSize+'px'}">{{ getIndexMethod(index) }}</view>
- </view>
- <view class="b-td" v-for="column in columns" :key="column.fieldName" :class="[getCellProps(column).class]" :style="[getCellProps(column).style]">
- <view class="b-cell" @click.stop="handleCellClick(scope, column, index)">
- <slot name="item" :scope="scope" :column="column" v-if="column.fieldType === 'slot'"></slot>
- <view v-else :style="{fontSize:fontSize+'px'}">{{ scope[column.fieldName] }}</view>
- </view>
- </view>
- </view>
- </view>
- <view class="base-table-empty" v-else>
- <view class="mt20" v-if="!$slots.empty">{{ emptyText }}</view>
- <slot name="empty"></slot>
- </view>
- </view>
- </view>
- <view class="base-table-footer" v-if="showFooter">
- <view class="b-table" :style="[tableBodyStyle]">
- <view class="b-tbody">
- <view class="b-tr">
- <view class="b-td" v-if="indexShow" :style="[getIndexColStyle]">
- <view class="b-cell">{{ footerText }}</view>
- </view>
- <view
- class="b-td"
- v-for="(item, index) in sumList"
- :key="index"
- :class="[getCellProps(columns[index]).class]"
- :style="[getCellProps(columns[index]).style]"
- >
- <view class="b-cell">{{ item }}</view>
- </view>
- </view>
- </view>
- </view>
- </view>
- </view>
- </view>
- </template>
- <script>
- export default {
- props: {
- columns: {
- type: Array,
- default: () => []
- },
- data: {
- type: Array,
- default: () => []
- },
- align: {
- type: String,
- default: 'left'
- },
- height: {
- type: String
- },
- maxHeight: {
- type: String
- },
- width: {
- type: String,
- default: '100%'
- },
- emptyText: {
- type: String,
- default: '暂无数据'
- },
- border: {
- type: Boolean,
- default: false
- },
- stripe: {
- type: Boolean,
- default: false
- },
- showHeader: {
- type: Boolean,
- default: true
- },
- showFooter: {
- type: Boolean,
- default: false
- },
- footerMethod: {
- type: Function
- },
- footerText: {
- type: String,
- default: '合计'
- },
- indexShow: {
- type: Boolean,
- default: false
- },
- minItemWidth: {
- type: Number,
- default: 80
- },
- rowClassName: {
- type: [Function, String]
- },
- rowStyle: {
- type: [Function, Object]
- },
- indexMethod: {
- type: Function
- },
- headerRowClassName: {
- type: String
- },
- headerRowStyle: {
- type: Object
- },
- fontSize:{
- type: Number,
- default: 15
- },
- indexWidth: {
- type: String,
- default: '60px'
- }
- },
- data() {
- return {
- sumList: [],
- tableWidth: 0
- };
- },
- mounted() {
- const query = uni.createSelectorQuery().in(this).select('.base-table');
- query.boundingClientRect(data => {
- this.tableWidth = data.width;
- }).exec();
- },
- computed: {
- getTableStyle() {
- const { width, height, maxHeight } = this;
- const styleObj = {};
- if (width) {
- styleObj.width = width;
- }
- if (height) {
- styleObj.height = height;
- }
- if (maxHeight) {
- styleObj.maxHeight = maxHeight;
- }
- return styleObj;
- },
- tableBodyStyle() {
- if (!this.tableWidth) return {};
- const clienWidth = this.tableWidth;
- const flexColumn = this.columns.filter(item => !item.width);
- //set min width
- const minWidth = this.minItemWidth;
- let bodyMinWidth = this.columns.reduce((t, c) => {
- c.width = c.width || minWidth;
- return t + parseFloat(c.width);
- }, 0);
- if(this.indexShow){
- bodyMinWidth+=parseFloat(this.indexWidth)
- }
- if (flexColumn.length > 0 && bodyMinWidth < clienWidth) {
- const flexWidth = clienWidth - bodyMinWidth;
- if (flexColumn.length === 1) {
- flexColumn[0].width = minWidth + flexWidth;
- } else {
- const scaleWidth = flexWidth / flexColumn.length;
- flexColumn.forEach(item => {
- item.width = minWidth + Math.floor(scaleWidth);
- });
- }
- }
- bodyMinWidth = Math.max(bodyMinWidth, clienWidth);
- return {
- width: `${bodyMinWidth}px`
- };
- },
- showXScroll() {
- const clienWidth = this.tableWidth;
- return clienWidth < parseFloat(this.tableBodyStyle?.width || 0);
- },
- isEmpty() {
- return this.data.length === 0;
- },
- getHeaderClass() {
- const headerClass = [];
- if (this.headerRowClassName) {
- headerClass.push(this.headerRowClassName);
- }
- return headerClass;
- },
- getHeaderStyle() {
- const headerStyle = [];
- if (typeof this.headerRowStyle === 'object') {
- if (this.headerRowStyle) {
- headerStyle.push(this.headerRowStyle);
- }
- }
- return headerStyle;
- },
- getIndexColStyle() {
- return {
- textAlign: this.align,
- width: this.indexWidth
- };
- }
- },
- methods: {
- init() {
- this.sumList = [];
- if (this.showFooter && this.data.length > 0) {
- const { columns, data, footerText } = this;
- if (typeof this.footerMethod === 'function') {
- this.sumList = this.footerMethod({ columns, data });
- } else {
- columns.forEach((column, index) => {
- if (!this.indexShow && index === 0) {
- this.sumList[index] = footerText;
- return;
- }
- const values = data.map(item => Number(item[column.fieldName]));
- const precisions = [];
- let notNumber = true;
- values.forEach(value => {
- if (!Number.isNaN(+value)) {
- notNumber = false;
- const decimal = `${value}`.split('.')[1];
- precisions.push(decimal ? decimal.length : 0);
- }
- });
- const precision = Math.max.apply(null, precisions);
- if (!notNumber) {
- this.sumList[index] = values.reduce((prev, curr) => {
- const value = Number(curr);
- if (!Number.isNaN(+value)) {
- return Number.parseFloat((prev + curr).toFixed(Math.min(precision, 20)));
- } else {
- return prev;
- }
- }, 0);
- } else {
- this.sumList[index] = '';
- }
- });
- }
- }
- },
- getBodyClass(scope, index) {
- const bodyClass = [];
- if (this.stripe) {
- bodyClass.push({ 'is-stripe': index % 2 === 1 });
- }
- if (typeof this.rowClassName === 'function') {
- const rowClass = this.rowClassName?.(scope, index);
- if (rowClass) {
- bodyClass.push(rowClass);
- }
- } else if (typeof this.rowClassName === 'string') {
- if (this.rowClassName) {
- bodyClass.push(this.rowClassName);
- }
- }
- return bodyClass;
- },
- getBodyStyle(scope, index) {
- const bodyStyle = [];
- if (typeof this.rowStyle === 'function') {
- const rowStyle = this.rowStyle?.(scope, index);
- if (rowStyle) {
- bodyStyle.push(rowStyle);
- }
- } else if (typeof this.rowStyle === 'object') {
- if (this.rowStyle) {
- bodyStyle.push(this.rowStyle);
- }
- }
- return bodyStyle;
- },
- getIndexMethod(index) {
- let curIndex = index + 1;
- if (typeof this.indexMethod === 'function') {
- curIndex = this.indexMethod?.(index);
- }
- return curIndex;
- },
- getCellProps(row) {
- const classList = [];
- if (this.showXScroll && row.fixed) {
- classList.push('fixed');
- if (row.fixed === 'left') {
- classList.push('fixed-left');
- } else {
- classList.push('fixed-right');
- }
- }
- return {
- class: classList,
- style: {
- width: `${row.width}px`,
- textAlign: this.align,
- minWidth: `${this.minItemWidth}px`
- }
- };
- },
- handleHeaderClick() {
- this.$emit('header-click');
- },
- handleRowClick(scope, index) {
- this.$emit('row-click', scope, index);
- },
- handleCellClick(scope, column, index) {
- this.$emit('cell-click', { scope, column, index });
- },
- },
- watch: {
- data: {
- handler() {
- this.init();
- },
- immediate: true,
- deep: true
- }
- }
- };
- </script>
- <style lang="scss" scoped>
- @import './basic-table.scss';
- </style>
|