Compare commits
4 Commits
master
...
zq-tableui
Author | SHA1 | Date |
---|---|---|
![]() |
5cdcbf484a | 1 month ago |
![]() |
5d012662a3 | 1 month ago |
![]() |
d694ed9343 | 1 month ago |
![]() |
5b95ce30b0 | 2 months ago |
10 changed files with 1680 additions and 224 deletions
@ -0,0 +1,197 @@ |
|||
// 在有边框,可以进行拖拽的情况下,实现宽度跟随变化(不太行,基本pass)
|
|||
import ResizeObserver from 'resize-observer-polyfill'; |
|||
import { |
|||
debounce |
|||
} from 'lodash'; |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
tableResizeObserver: null, |
|||
tableWidth: 0, |
|||
isDragging: false, // 新增拖拽状态标志
|
|||
lastDragTime: 0 // 记录最后一次拖拽时间
|
|||
}; |
|||
}, |
|||
|
|||
methods: { |
|||
// 销毁观察者
|
|||
destroyTableResizeObserver() { |
|||
if (this.tableResizeObserver) { |
|||
this.tableResizeObserver.disconnect(); |
|||
this.tableResizeObserver = null; |
|||
} |
|||
}, |
|||
// 初始化监听
|
|||
initTableResizeObserver(tableRef, containerRef) { |
|||
this.$nextTick(() => { |
|||
const container = containerRef ? this.$refs[containerRef] : this.$el; |
|||
if (!container) return; |
|||
|
|||
this.destroyTableResizeObserver(); |
|||
|
|||
this.tableResizeObserver = new ResizeObserver( |
|||
debounce(entries => { |
|||
if (this.isDragging || Date.now() - this.lastDragTime < 300) return; |
|||
|
|||
const entry = entries[0]; |
|||
const newWidth = entry.contentRect.width; |
|||
|
|||
if (Math.abs(newWidth - this.tableWidth) > 5) { |
|||
this.tableWidth = newWidth; |
|||
this.forceSyncTableLayout(tableRef); |
|||
} |
|||
}, 100) |
|||
); |
|||
|
|||
this.tableResizeObserver.observe(container); |
|||
}); |
|||
}, |
|||
|
|||
|
|||
// 强制同步表头和表体布局
|
|||
forceSyncTableLayout(tableRef) { |
|||
const table = this.$refs[tableRef].$refs.guiptable; |
|||
if (!table) return; |
|||
|
|||
// 先获取当前实际宽度
|
|||
const currentWidths = this.getCurrentColumnWidths(table); |
|||
|
|||
// 计算理论宽度
|
|||
const calculatedWidths = this.calculateColumnWidths(); |
|||
if (!calculatedWidths) return; |
|||
|
|||
this.$nextTick(() => { |
|||
// 1. 设置列定义的宽度
|
|||
table.columns.forEach(column => { |
|||
const prop = column.property || column.id; |
|||
column.width = currentWidths[prop] || calculatedWidths[prop]; |
|||
}); |
|||
|
|||
// 2. 同步DOM宽度
|
|||
this.updateDOMWidths(table); |
|||
|
|||
// 3. 特殊处理固定列
|
|||
this.handleFixedColumns(table); |
|||
|
|||
// 4. 强制重新布局(两次确保生效)
|
|||
table.doLayout(); |
|||
setTimeout(() => table.doLayout(), 50); |
|||
}); |
|||
}, |
|||
|
|||
// 获取当前DOM中的实际列宽
|
|||
getCurrentColumnWidths(table) { |
|||
const widths = {}; |
|||
const headerCells = table.$el.querySelectorAll('.el-table__header .cell'); |
|||
|
|||
headerCells.forEach(cell => { |
|||
const col = cell.closest('th'); |
|||
if (col && col.style.width) { |
|||
const prop = col.getAttribute('data-property') || |
|||
col.querySelector('.cell').getAttribute('data-property'); |
|||
if (prop) { |
|||
widths[prop] = parseInt(col.style.width); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
return widths; |
|||
}, |
|||
|
|||
// 更新DOM元素宽度
|
|||
updateDOMWidths(table) { |
|||
const headerCols = table.$el.querySelectorAll('.el-table__header col'); |
|||
const bodyCols = table.$el.querySelectorAll('.el-table__body col'); |
|||
const headerCells = table.$el.querySelectorAll('.el-table__header th'); |
|||
const bodyCells = table.$el.querySelectorAll('.el-table__body td'); |
|||
|
|||
table.columns.forEach((column, index) => { |
|||
const width = column.width; |
|||
if (!width) return; |
|||
|
|||
// 设置colgroup中的宽度
|
|||
if (headerCols[index]) { |
|||
headerCols[index].width = width; |
|||
// headerCols[index].style.width = `${width}px`;
|
|||
headerCols[index].style.setProperty('width', `${width}px`, 'important'); |
|||
|
|||
} |
|||
if (bodyCols[index]) { |
|||
bodyCols[index].width = width; |
|||
// bodyCols[index].style.width = `${width}px`;
|
|||
bodyCols[index].style.setProperty('width', `${width}px`, 'important'); |
|||
|
|||
} |
|||
|
|||
// 设置实际单元格宽度
|
|||
if (headerCells[index]) { |
|||
// headerCells[index].style.width = `${width}px`;
|
|||
headerCells[index].style.setProperty('width', `${width}px`, 'important'); |
|||
headerCells[index].style.minWidth = `${width}px`; |
|||
|
|||
} |
|||
if (bodyCells[index]) { |
|||
// bodyCells[index].style.width = `${width}px`;
|
|||
bodyCells[index].style.setProperty('width', `${width}px`, 'important'); |
|||
bodyCells[index].style.minWidth = `${width}px`; |
|||
} |
|||
|
|||
}); |
|||
}, |
|||
|
|||
// 处理固定列
|
|||
handleFixedColumns(table) { |
|||
const fixedLeft = table.$el.querySelector('.el-table__fixed'); |
|||
const fixedRight = table.$el.querySelector('.el-table__fixed-right'); |
|||
|
|||
if (fixedLeft) fixedLeft.style.height = 'auto'; |
|||
if (fixedRight) fixedRight.style.height = 'auto'; |
|||
}, |
|||
|
|||
// 处理拖拽开始
|
|||
handleHeaderDragStart() { |
|||
this.isDragging = true; |
|||
}, |
|||
|
|||
// 处理拖拽结束
|
|||
handleHeaderDragEnd(newWidth, oldWidth, column) { |
|||
this.isDragging = false; |
|||
this.lastDragTime = Date.now(); |
|||
|
|||
// 更新列比例配置
|
|||
const table = this.$refs[Object.keys(this.$refs).find(k => k.startsWith('myTable'))]; |
|||
if (!table || !this.tableWidth) return; |
|||
|
|||
const availableWidth = this.tableWidth - 20; |
|||
const newRatio = newWidth / availableWidth; |
|||
|
|||
// 更新当前列的ratio
|
|||
if (this.columnRatios && column.property) { |
|||
this.columnRatios[column.property] = newRatio; |
|||
} |
|||
|
|||
// 重新平衡其他列的比例
|
|||
this.balanceColumnRatios(column.property); |
|||
}, |
|||
|
|||
// 平衡其他列的比例
|
|||
balanceColumnRatios(changedColumnProp) { |
|||
if (!this.columnRatios) return; |
|||
|
|||
const otherColumns = Object.keys(this.columnRatios) |
|||
.filter(prop => prop !== changedColumnProp); |
|||
|
|||
const totalUsedRatio = Object.values(this.columnRatios).reduce((sum, r) => sum + r, 0); |
|||
const overflow = totalUsedRatio - 1; |
|||
|
|||
if (overflow > 0) { |
|||
const otherTotalRatio = otherColumns.reduce((sum, prop) => sum + this.columnRatios[prop], 0); |
|||
|
|||
otherColumns.forEach(prop => { |
|||
this.columnRatios[prop] -= (this.columnRatios[prop] / otherTotalRatio) * overflow; |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
}; |
@ -0,0 +1,149 @@ |
|||
import ResizeObserver from 'resize-observer-polyfill'; |
|||
import { |
|||
debounce |
|||
} from 'lodash-es'; |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
tableResizeObserver: null, |
|||
tableWidth: 0 |
|||
}; |
|||
}, |
|||
|
|||
methods: { |
|||
// 初始化表格宽度监听
|
|||
initTableResizeObserver(tableRef, containerRef) { |
|||
this.$nextTick(() => { |
|||
const container = containerRef ? this.$refs[containerRef] : this.$el; |
|||
if (!container) { |
|||
console.warn('Table container not found'); |
|||
return; |
|||
} |
|||
|
|||
// 先断开已有观察者
|
|||
this.destroyTableResizeObserver(); |
|||
|
|||
this.tableResizeObserver = new ResizeObserver( |
|||
debounce(entries => { |
|||
const entry = entries[0]; |
|||
const newWidth = entry.contentRect.width; |
|||
|
|||
if (Math.abs(newWidth - this.tableWidth) > 5) { |
|||
this.tableWidth = newWidth; |
|||
this.syncTableColumns(tableRef); |
|||
} |
|||
}, 100) |
|||
); |
|||
|
|||
try { |
|||
this.tableResizeObserver.observe(container); |
|||
} catch (err) { |
|||
console.error('Failed to observe table:', err); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
// 同步表头和表体列宽
|
|||
syncTableColumns(tableRef) { |
|||
// const table = this.$refs[tableRef];
|
|||
const table = this.$refs[tableRef].$refs.guiptable; |
|||
// console.log(table, 'table====--');
|
|||
if (!table) return; |
|||
let columns = table.columns; |
|||
// console.log(table,table['columns'],table.bodyWidth,'table.columns===');
|
|||
// 计算各列宽度(由具体组件实现)
|
|||
const columnWidths = this.calculateColumnWidths(); |
|||
if (!columnWidths) return; |
|||
// console.log(columnWidths, 'table.columns===');
|
|||
// 设置列宽并同步表头表体
|
|||
this.$nextTick(() => { |
|||
// 设置列定义中的宽度
|
|||
columns.forEach(column => { |
|||
// console.log(column.property,'columns====columns');
|
|||
if (columnWidths[column.property]) { |
|||
column.width = columnWidths[column.property]; |
|||
} |
|||
}); |
|||
|
|||
// 同步DOM元素的宽度
|
|||
// const headerCols = table.$el.querySelectorAll('.el-table__header col');
|
|||
// const bodyCols = table.$el.querySelectorAll('.el-table__body col');
|
|||
|
|||
// columns.forEach((column, index) => {
|
|||
// if (columnWidths[column.property] && headerCols[index] && bodyCols[index]) {
|
|||
// const width = columnWidths[column.property];
|
|||
// headerCols[index].width = width;
|
|||
// headerCols[index].style.setProperty('width',`${width}px`, 'important');
|
|||
// // headerCols[index].style.width = `${width}px`;
|
|||
// bodyCols[index].width = width;
|
|||
// bodyCols[index].style.setProperty('width',`${width}px`, 'important');
|
|||
// // bodyCols[index].style.width = `${width}px`;
|
|||
// }
|
|||
// });
|
|||
|
|||
// 强制表格重新布局
|
|||
table.doLayout(); |
|||
// this.syncFixedColumns(table);
|
|||
this.$nextTick(() => { |
|||
// 3. 同步主表格
|
|||
this.syncColumns( |
|||
table.$el, |
|||
columnWidths, |
|||
table.columns |
|||
); |
|||
|
|||
// 4. 同步固定列
|
|||
const fixedLeft = table.$el.querySelector('.el-table__fixed'); |
|||
const fixedRight = table.$el.querySelector('.el-table__fixed-right'); |
|||
|
|||
if (fixedLeft) this.syncColumns(fixedLeft, columnWidths, table.columns); |
|||
if (fixedRight) this.syncColumns(fixedRight, columnWidths, table.columns); |
|||
|
|||
// 5. 强制布局更新(需要两次nextTick确保固定列更新)
|
|||
this.$nextTick(() => { |
|||
table.doLayout(); |
|||
setTimeout(() => table.doLayout(), 50); |
|||
}); |
|||
}); |
|||
|
|||
}); |
|||
}, |
|||
syncColumns(container, columnWidths, columns) { |
|||
const headerCols = container.querySelectorAll('.el-table__header col, [class*=-header-wrapper] col'); |
|||
const bodyCols = container.querySelectorAll('.el-table__body col, [class*=-body-wrapper] col'); |
|||
|
|||
columns.forEach((column, index) => { |
|||
const width = columnWidths[column.property]; |
|||
if (width && headerCols[index] && bodyCols[index]) { |
|||
headerCols[index].width = width; |
|||
// headerCols[index].style.width = `${width}px`;
|
|||
headerCols[index].style.setProperty('width',`${width}px`, 'important'); |
|||
|
|||
bodyCols[index].width = width; |
|||
// bodyCols[index].style.width = `${width}px`;
|
|||
bodyCols[index].style.setProperty('width',`${width}px`, 'important'); |
|||
|
|||
} |
|||
}); |
|||
}, |
|||
|
|||
|
|||
// 销毁观察者
|
|||
destroyTableResizeObserver() { |
|||
if (this.tableResizeObserver) { |
|||
this.tableResizeObserver.disconnect(); |
|||
this.tableResizeObserver = null; |
|||
} |
|||
}, |
|||
|
|||
// 需要组件自己实现的计算列宽方法
|
|||
calculateColumnWidths() { |
|||
throw new Error('Component must implement calculateColumnWidths method'); |
|||
} |
|||
}, |
|||
|
|||
beforeDestroy() { |
|||
this.destroyTableResizeObserver(); |
|||
} |
|||
}; |
@ -0,0 +1,806 @@ |
|||
<template> |
|||
<div class="demo-wrap min-flex-right"> |
|||
<div class="flex-between"> |
|||
<h2>{{ pageTitle }}</h2> |
|||
<CustomDropdown ref="dropdownRef" :placeholder="'('+viewDesc[this.view]+')'+text" width="280px"> |
|||
<DateSelect slot="normal" :view="view" v-model="selectedDate" @update-count="handleUpdateView" |
|||
@change="handleDateChange" /> |
|||
</CustomDropdown> |
|||
</div> |
|||
<div v-if="dataRank == 1 && (dataType == 'ver_type' || dataType == 'check_type')" |
|||
style="margin-bottom: 20px;text-align: left"> |
|||
<el-alert type="info" :closable="false" show-icon> |
|||
<template #title> |
|||
未计成本 |
|||
</template> |
|||
<div style="margin-top: 8px; line-height: 1.6; font-size: 14px; color: #606266;"> |
|||
<template v-if="dataType == 'ver_type'"> |
|||
1. AI(服务器成本) <span v-if="view == 'year'" style="color:red;">2025年(含)后计入成本</span><br /> |
|||
2. Turnitin <span v-if="view == 'year'" style="color:red;">2025年(含)后计入成本</span><br /> |
|||
3. 学术(知网PMLC,硕博VIP)<span v-if="view == 'year'" style="color:red;">2025年(含)后计入成本</span><br /> |
|||
</template> |
|||
<template v-if="dataType == 'check_type'"> |
|||
1. AI(服务器成本)<br /> |
|||
2. Turnitin国际版、TurnitinUK版、Turnitin国际版+AI<br /> |
|||
3. 知网PMLC,硕博VIP <span v-if="view == 'year'" style="color:red;">2025年(含)后计入成本</span><br /> |
|||
</template> |
|||
</div> |
|||
</el-alert> |
|||
</div> |
|||
<div class=" flex-common" id=""> |
|||
<el-form> |
|||
<div class="table-container" ref="tableContainer"> |
|||
<!-- @cell-mouse-enter="handleRowHover" --> |
|||
<GuipTable :tableData="tableData" style="width: 100%;" @sort-change="handleSortChange" |
|||
ref="elTable" :loading="tableLoading"> |
|||
<el-table-column prop="sort" label="排序" show-overflow-tooltip></el-table-column> |
|||
<el-table-column |
|||
v-if="(dataRank == 1 || dataRank == 2) && (dataType == 'ver_type' || dataType == 'check_type')" |
|||
prop="name" :key="selectedType" :label="type_select[selectedType]"> |
|||
|
|||
<template slot="header"> |
|||
<el-select class="custom-select tableHeaderSelect" height="34px" popper-class="custom-select-dropdown" |
|||
v-model="selectedType" @change="changeRankType"> |
|||
<el-option v-for="(item,type) in type_select" :key="type" :label="item" |
|||
:value="type"> |
|||
</el-option> |
|||
</el-select> |
|||
</template> |
|||
|
|||
<template slot-scope="scope"> |
|||
<GuipToolTip :content="scope.row.name"> |
|||
<div class="cell-content nowrap">{{ scope.row.name }}</div> |
|||
</GuipToolTip> |
|||
</template> |
|||
|
|||
</el-table-column> |
|||
<el-table-column v-else prop="name" :label="type_desc[dataType]"> |
|||
<template slot-scope="scope"> |
|||
<GuipToolTip :content="scope.row.name"> |
|||
<div class="cell-content nowrap">{{ scope.row.name }}</div> |
|||
</GuipToolTip> |
|||
</template> |
|||
|
|||
</el-table-column> |
|||
|
|||
<el-table-column v-for="(field, index) in valueFields" :key="field" |
|||
:label="labels[index] + (index == 3 ? current_month : '')" :prop="'value_' + String(index + 1)" |
|||
sortable="custom"> |
|||
|
|||
<!--产品利润排行展示查看更多--> |
|||
<template v-slot="{ row, $index }" v-if="index == 3 && dataRank == 1"> |
|||
<el-popover popper-class="custom-popover" v-model="row.id_popover" placement="top" trigger="manual" |
|||
:ref="`popover-${row.id}`" :visible-arrow="true" :append-to-body="false" @show="popshow"> |
|||
|
|||
<div v-if="type != 'agent'" class="pop-wrap"> |
|||
<div class="flex-between flex pop-top"> |
|||
<h3> |
|||
{{ text }} {{ row.name }} |
|||
<span class="lookMore" @click="goLookMoreData(row.id)">查看更多</span> |
|||
</h3> |
|||
<span class="flex point" @click="closePop(row,'id')"> |
|||
关闭<img src="@/assets/register/close.svg"> |
|||
</span> |
|||
</div> |
|||
<el-table :data="tableData1" style="width: 100%"> |
|||
<el-table-column prop="value_1" width="200" label="日期"></el-table-column> |
|||
|
|||
<el-table-column width="200" :label="rank_type_desc[dataRank]"> |
|||
<template slot-scope="scope"> |
|||
<div class="flex"> |
|||
{{ scope.row.value_2 }} |
|||
</div> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
</div> |
|||
<div v-else class="pop-wrap"> |
|||
<div class="flex-between flex pop-top"> |
|||
<h3> |
|||
{{ text }} {{ row.name }} |
|||
<span class="lookMore" @click="goLookCheckTypeRank(row.id)">查看更多</span> |
|||
</h3> |
|||
<span class="flex point" @click="closePop(row,'id')"> |
|||
关闭<img src="@/assets/register/close.svg"> |
|||
</span> |
|||
</div> |
|||
<el-table :data="tableData1" style="width: 100%"> |
|||
<el-table-column prop="sort" width="95" label="排序"></el-table-column> |
|||
<el-table-column prop="name" width="250" label="服务名称"></el-table-column> |
|||
<el-table-column prop="rate" width="130" label="毛利占比"> |
|||
<template slot-scope="scope"> |
|||
<div class="flex"> |
|||
{{ scope.row.rate }} % |
|||
</div> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column width="150" :label="rank_type_desc[dataRank]"> |
|||
<template slot-scope="scope"> |
|||
<div class="flex"> |
|||
{{ scope.row.value_1 }} |
|||
</div> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="value_2" width="130" label="订单数"></el-table-column> |
|||
</el-table> |
|||
</div> |
|||
|
|||
<div class="flex" slot="reference"> |
|||
{{ row[field] }} |
|||
<svg-icon :size="16" :path="require('@/assets/super/list-detail.svg')" |
|||
:color="'#8A9099'" :hoverColor="'#006AFF'" |
|||
@click="handleClick(row, $index, 'id')" /> |
|||
</div> |
|||
</el-popover> |
|||
</template> |
|||
<template v-else slot-scope="scope"> |
|||
<div class="flex"> |
|||
{{ scope.row[field] }} |
|||
</div> |
|||
</template> |
|||
</el-table-column> |
|||
|
|||
<!--产品利润排行展示代理商排行--> |
|||
<el-table-column v-if="dataRank == 1 && (dataType == 'ver_type' || dataType == 'check_type')" |
|||
key="top" prop="id" :label="'代理商排行'+current_month" :width="valueFields.id"> |
|||
<template v-slot="{ row, $index }"> |
|||
<el-popover v-model="row.id_popover_2" :append-to-body="false" |
|||
popper-class="custom-popover" trigger="manual" :visible-arrow="true" |
|||
:ref="`popover_2-${row.id}`" @show="popshow"> |
|||
<div class="pop-wrap"> |
|||
<div class="flex-between flex pop-top"> |
|||
<h3> |
|||
{{ row.name }} 代理商排行 |
|||
<span class="lookMore" @click="goLookAgentRank(row.id)">查看更多</span> |
|||
</h3> |
|||
<span class="flex point" @click="closePop(row,'id')"> |
|||
关闭<img src="@/assets/register/close.svg"> |
|||
</span> |
|||
</div> |
|||
<el-table :data="tableData1" style="width: 100%"> |
|||
<el-table-column prop="sort" width="100" label="排行"></el-table-column> |
|||
<el-table-column prop="name" width="200" label="代理商"></el-table-column> |
|||
<el-table-column prop="value_1" width="200" label="销售额"></el-table-column> |
|||
</el-table> |
|||
</div> |
|||
<div slot="reference"> |
|||
<GuipToolTip :content="' No.1 '+top_list[row.id]['name']" |
|||
v-if="top_list[row.id]"> |
|||
<div class="flex"> |
|||
<span class="cell-content nowrap"> No.1 {{ top_list[row.id]['name'] |
|||
}}</span> |
|||
<svg-icon :size="16" :path="require('@/assets/super/list-detail.svg')" |
|||
:color="'#8A9099'" :hoverColor="'#006AFF'" |
|||
@click="handleClick2(row, $index, 'id')" /> |
|||
</div> |
|||
</GuipToolTip> |
|||
|
|||
<span class="cell-content" v-else>暂无排行</span> |
|||
</div> |
|||
</el-popover> |
|||
</template> |
|||
</el-table-column> |
|||
</GuipTable> |
|||
</div> |
|||
|
|||
<el-pagination background @size-change='handleSizeChange' @current-change='handleCurrentChange' |
|||
:current-page="currentPage" :page-size=pageSize layout="prev, pager, next,jumper" :total="total"> |
|||
</el-pagination> |
|||
</el-form> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script> |
|||
import DateSelect from '@/components/super/DateSelect.vue'; |
|||
import CustomDropdown from '@/components/CustomDropdown.vue'; |
|||
import GuipTable from '@/components/GuipTable.vue'; |
|||
import GuipToolTip from '@/components/GuipToolTip.vue'; |
|||
import SvgIcon from '@/components/SvgIcon.vue'; |
|||
import tableResizeMixin from '@/mixins/tableResizeMixin' |
|||
export default { |
|||
name: 'rank_batch_list', |
|||
mixins: [tableResizeMixin], |
|||
props: { |
|||
pageTitle: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
rank_type: { |
|||
type: Number, |
|||
default: 0 |
|||
}, |
|||
type: { |
|||
type: String, |
|||
default: '' |
|||
} |
|||
}, |
|||
components: { |
|||
// HoverImage, |
|||
GuipToolTip, |
|||
DateSelect, |
|||
GuipTable, |
|||
SvgIcon, |
|||
CustomDropdown |
|||
}, |
|||
data() { |
|||
return { |
|||
|
|||
popoverRefs: {}, |
|||
resizePending: false, |
|||
resizeObserver: null, |
|||
viewDesc: { |
|||
'month': '月份', |
|||
'monthTwo': '月份', |
|||
'year': '年份', |
|||
}, |
|||
rank_type_desc: { |
|||
1: '毛利润', |
|||
2: '订单数', |
|||
3: '退单数', |
|||
4: '充值金额', |
|||
}, |
|||
type_desc: { |
|||
'agent': '代理商昵称', |
|||
'ver_type': '品牌名称', |
|||
'check_type': '服务名称', |
|||
}, |
|||
type_select: { |
|||
'ver_type': '按品牌名称', |
|||
'check_type': '按服务名称', |
|||
}, |
|||
tableWidth: 0, |
|||
// 列宽比例配置 |
|||
columnRatios: { |
|||
sort: 0.08, // 20% |
|||
name: 0.12, // 30% |
|||
value_1: 0.14, // 50% |
|||
value_2: 0.14, // 50% |
|||
value_3: 0.16, // 50% |
|||
value_4: 0.18, // 50% |
|||
id: 0.18 // 50% |
|||
}, |
|||
// 列最小宽度配置 |
|||
minWidths: { |
|||
name: 120, // 30% |
|||
sort: 90, // 20% |
|||
value_1: 120, // 50% |
|||
value_2: 120, // 50% |
|||
value_3: 120, // 50% |
|||
value_4: 120, // 50% |
|||
id: 120 // 50% |
|||
}, |
|||
selectedType: 'check_type', |
|||
view: 'month', |
|||
labels: ['', '', '', ''], |
|||
current_month: '', |
|||
valueFields: ['value_1', 'value_2', 'value_3', 'value_4'], |
|||
currentPage: 1, //当前页 |
|||
pageSize: 20, //每页的容量 |
|||
total: 0, //列表总数 |
|||
sort_by: 4, |
|||
sort_order: 2, |
|||
text: '',//下拉框显示具体文案 |
|||
selectedDate: new Date(),//默认当天日期 |
|||
dataType: '', |
|||
dataRank: '', |
|||
tableData: [], |
|||
top_list: [], |
|||
tableData1: [], |
|||
show_detail_index: 0, |
|||
tableLoading:false |
|||
} |
|||
}, |
|||
mounted() { |
|||
this.init() |
|||
this.$nextTick(() => { |
|||
this.initTableResizeObserver('elTable', 'tableContainer') |
|||
}); |
|||
}, |
|||
watch: { |
|||
'$route'() { |
|||
this.init() |
|||
} |
|||
}, |
|||
methods: { |
|||
|
|||
handleHeaderDragStart() { |
|||
this.$options.methods.handleHeaderDragStart.call(this); |
|||
}, |
|||
|
|||
handleHeaderDragEnd(newWidth, oldWidth, column) { |
|||
this.$options.methods.handleHeaderDragEnd.call(this, newWidth, oldWidth, column); |
|||
}, |
|||
// 覆盖方法 |
|||
calculateColumnWidths() { |
|||
if (!this.tableWidth) return {}; |
|||
const availableWidth = this.tableWidth ; |
|||
const widths = {}; |
|||
|
|||
Object.keys(this.columnRatios).forEach(prop => { |
|||
const calculatedWidth = Math.floor(availableWidth * this.columnRatios[prop]); |
|||
// acc[key] = Math.max(calculatedWidth, this.minWidths[key]); |
|||
widths[prop] = Math.max( |
|||
Math.floor(calculatedWidth, this.minWidths[prop]), |
|||
80 // 最小宽度 |
|||
); |
|||
}); |
|||
return widths; |
|||
|
|||
}, |
|||
init() { |
|||
document.title = this.pageTitle; |
|||
|
|||
this.text = this.getNowDate() |
|||
this.dataType = this.type |
|||
this.dataRank = this.rank_type |
|||
|
|||
this.getRankingData(); |
|||
}, |
|||
handleUpdateView(newView) { |
|||
this.view = newView; |
|||
}, |
|||
getNowDate() { |
|||
const now = new Date(); |
|||
const year = now.getFullYear(); |
|||
const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从 0 开始,补零 |
|||
const currentYearMonth = `${year}-${month}`; |
|||
return `${currentYearMonth}` |
|||
}, |
|||
getDate(dateStr) { |
|||
const date = new Date(dateStr); |
|||
const year = date.getFullYear(); // 2025 |
|||
const month = date.getMonth() + 1; // 3 (表示3月) |
|||
if (this.view == 'year') { |
|||
return `${year}` |
|||
} else { |
|||
return `${year}-${month}` |
|||
} |
|||
}, |
|||
handleDateChange(date) { |
|||
this.text = this.getDate(date) |
|||
this.selectedDate = date; |
|||
localStorage.setItem('date', JSON.stringify(date)) |
|||
|
|||
this.$refs.dropdownRef.closeDropdown(); |
|||
|
|||
this.currentPage = 1; |
|||
this.getRankingData() |
|||
}, |
|||
goLookMoreData(id) { |
|||
let query = {} |
|||
query.date = this.text |
|||
query.rank_type = this.dataRank |
|||
if (this.dataType == 'agent') { |
|||
query.aid = id |
|||
} |
|||
if (this.dataType == 'ver_type') { |
|||
query.ver_type = id |
|||
} |
|||
if (this.dataType == 'check_type') { |
|||
query.check_type = id |
|||
} |
|||
window.open(this.$router.resolve({ |
|||
path: '/super/ranking/detail', |
|||
query: query |
|||
}).href, '_blank') |
|||
}, |
|||
goLookAgentRank(id) { |
|||
let query = {} |
|||
query.date = this.text |
|||
query.rank_type = this.dataRank |
|||
query.type = 'agent' |
|||
if (this.dataType == 'ver_type') { |
|||
query.ver_type = id |
|||
} |
|||
if (this.dataType == 'check_type') { |
|||
query.check_type = id |
|||
} |
|||
window.open(this.$router.resolve({ |
|||
path: '/super/ranking/list', |
|||
query: query |
|||
}).href, '_blank') |
|||
}, |
|||
goLookCheckTypeRank(id) { |
|||
let query = {} |
|||
query.date = this.text |
|||
query.rank_type = this.dataRank |
|||
query.type = 'check_type' |
|||
query.aid = id |
|||
window.open(this.$router.resolve({ |
|||
path: '/super/ranking/list', |
|||
query: query |
|||
}).href, '_blank') |
|||
}, |
|||
closePop(row, type) { |
|||
row[type + '_popover'] = false; |
|||
row[type + '_popover_2'] = false; |
|||
}, |
|||
setPopoverRef(id, el) { |
|||
if (el) { |
|||
this.popoverRefs[id] = el |
|||
} |
|||
}, |
|||
getPopoverRef(id) { |
|||
return this.popoverRefs[id] |
|||
}, |
|||
handleClick(row, index, type) { |
|||
// 关闭其他行的弹框 |
|||
this.tableData.forEach((item, i) => { |
|||
item[type + '_popover_2'] = false; |
|||
if (i !== index) { |
|||
item[type + '_popover'] = false; |
|||
} |
|||
}); |
|||
// // 打开当前行的弹框 |
|||
row[type + '_popover'] = true; |
|||
|
|||
let obj = {} |
|||
if (this.dataType == 'agent') { |
|||
obj.aid = row.id |
|||
} |
|||
if (this.dataType == 'ver_type') { |
|||
obj.ver_type = row.id |
|||
} |
|||
if (this.dataType == 'check_type') { |
|||
obj.check_type = row.id |
|||
} |
|||
if (this.dataType == 'agent') { |
|||
let obj = {} |
|||
obj.aid = row.id |
|||
this.getCheckTypeRankingList(obj); |
|||
} else { |
|||
this.getRankingDetail(obj); |
|||
} |
|||
}, |
|||
handleClick2(row, index, type) { |
|||
// 关闭其他行的弹框 |
|||
this.tableData.forEach((item, i) => { |
|||
item[type + '_popover'] = false; |
|||
if (i !== index) { |
|||
item[type + '_popover_2'] = false; |
|||
} |
|||
}); |
|||
// 打开当前行的弹框 |
|||
row[type + '_popover_2'] = true; |
|||
|
|||
let obj = {} |
|||
if (this.dataType == 'agent') { |
|||
obj.aid = row.id |
|||
} |
|||
if (this.dataType == 'ver_type') { |
|||
obj.ver_type = row.id |
|||
} |
|||
if (this.dataType == 'check_type') { |
|||
obj.check_type = row.id |
|||
} |
|||
this.getAgentRankingList(obj); |
|||
}, |
|||
popshow() { |
|||
var ariaEls = document.querySelectorAll('.el-popover') |
|||
ariaEls.forEach((item) => { |
|||
item.removeAttribute('aria-hidden') |
|||
}) |
|||
|
|||
ariaEls = document.querySelectorAll('.el-radio__original') |
|||
ariaEls.forEach((item) => { |
|||
item.removeAttribute('aria-hidden') |
|||
}) |
|||
}, |
|||
handleSortChange({ prop, order }) { |
|||
this.sort_by = 4; |
|||
this.sort_order = 2; |
|||
if (order == 'ascending') { |
|||
this.sort_by = prop; |
|||
this.sort_order = 1; |
|||
} |
|||
if (order == 'descending') { |
|||
this.sort_by = prop; |
|||
this.sort_order = 2; |
|||
} |
|||
this.currentPage = 1; |
|||
this.getRankingData() |
|||
}, |
|||
handleRowHover(row) { |
|||
this.show_detail_index = row.sort |
|||
}, |
|||
changeRankType() { |
|||
this.dataType = this.selectedType |
|||
this.getRankingData() |
|||
}, |
|||
getRankingData() { |
|||
this.setLabelText(); |
|||
|
|||
if (this.dataType == 'agent') { |
|||
this.getAgentRanking() |
|||
} |
|||
if (this.dataType == 'ver_type') { |
|||
this.getVerRanking() |
|||
} |
|||
if (this.dataType == 'check_type') { |
|||
this.getTypeRanking() |
|||
} |
|||
}, |
|||
setLabelText() { |
|||
const date = new Date(this.text); |
|||
const year = date.getFullYear(); |
|||
const month = date.getMonth() + 1; |
|||
|
|||
const currentYear = new Date().getFullYear(); |
|||
const currentMonth = new Date().getMonth() + 1; |
|||
|
|||
this.current_month = ''; |
|||
if (this.view === 'month' && year == currentYear && month == currentMonth) { |
|||
this.current_month = '(当月)'; |
|||
} |
|||
|
|||
this.labels = []; |
|||
if (this.view === 'year') { |
|||
for (let i = 3; i >= 0; i--) { |
|||
this.labels.push((year - i) + '年' + this.rank_type_desc[this.dataRank]); |
|||
} |
|||
} else { |
|||
const monthLabels = []; |
|||
for (let i = 3; i >= 0; i--) { |
|||
let m = month - i; |
|||
if (m <= 0) m += 12; |
|||
|
|||
monthLabels.push(m + '月' + this.rank_type_desc[this.dataRank]); |
|||
} |
|||
this.labels = monthLabels; |
|||
} |
|||
}, |
|||
getAgentRanking() { |
|||
//代理商排行 |
|||
const that = this |
|||
that.tableData = [] |
|||
this.$http('POST', '/supernew/ajax_get_agent_batch_list', { |
|||
date: that.text, |
|||
rank_type: that.dataRank, |
|||
sort_by: that.sort_by, |
|||
sort_order: that.sort_order, |
|||
cur_page: that.currentPage, |
|||
page_size: that.pageSize, |
|||
}).then(response => { |
|||
this.$nextTick(() => { |
|||
that.tableData = response.data.list |
|||
that.total = response.data.total |
|||
}) |
|||
}).catch(error => { |
|||
console.error(error, 'error') |
|||
}) |
|||
}, |
|||
getVerRanking() { |
|||
//品牌排行 |
|||
const that = this |
|||
that.tableData = [] |
|||
that.top_list = [] |
|||
this.$http('POST', '/supernew/ajax_get_ver_batch_list', { |
|||
date: that.text, |
|||
rank_type: that.dataRank, |
|||
sort_by: that.sort_by, |
|||
sort_order: that.sort_order, |
|||
cur_page: that.currentPage, |
|||
page_size: that.pageSize, |
|||
}).then(response => { |
|||
this.$nextTick(() => { |
|||
that.tableData = response.data.list |
|||
that.top_list = response.data.top_list |
|||
that.total = response.data.total |
|||
}) |
|||
}).catch(error => { |
|||
console.error(error, 'error') |
|||
}) |
|||
}, |
|||
async getTypeRanking() { |
|||
//产品排行 |
|||
const that = this |
|||
that.tableData = [ |
|||
{ |
|||
id: 6, |
|||
name: "维普大学生版", |
|||
sort: 12334234232, |
|||
value_1: "23754.25", |
|||
value_2: "43012.15", |
|||
value_3: "61869.09", |
|||
value_4: "425537.45" |
|||
}, |
|||
{ |
|||
id: 94, |
|||
name: "AI中文范文", |
|||
sort: 2, |
|||
value_1: "8839.00", |
|||
value_2: "50174.00", |
|||
value_3: "120911.00", |
|||
value_4: "158772.50" |
|||
}, |
|||
{ |
|||
id: 61, |
|||
name: "维普大学生版", |
|||
sort: 11, |
|||
value_1: "23754.25", |
|||
value_2: "43012.15", |
|||
value_3: "61869.09", |
|||
value_4: "425537.45" |
|||
}, |
|||
{ |
|||
id: 941, |
|||
name: "AI中文范文", |
|||
sort: 21, |
|||
value_1: "8839.00", |
|||
value_2: "50174.00", |
|||
value_3: "120911.00", |
|||
value_4: "158772.50" |
|||
}, |
|||
{ |
|||
id: 62, |
|||
name: "维普大学生版", |
|||
sort: 12, |
|||
value_1: "23754.25", |
|||
value_2: "43012.15", |
|||
value_3: "61869.09", |
|||
value_4: "425537.45" |
|||
}, |
|||
{ |
|||
id: 942, |
|||
name: "AI中文范文", |
|||
sort: 22, |
|||
value_1: "8839.00", |
|||
value_2: "50174.00", |
|||
value_3: "120911.00", |
|||
value_4: "158772.50" |
|||
}, |
|||
{ |
|||
id: 63, |
|||
name: "维普大学生版", |
|||
sort: 13, |
|||
value_1: "23754.25", |
|||
value_2: "43012.15", |
|||
value_3: "61869.09", |
|||
value_4: "425537.45" |
|||
}, |
|||
{ |
|||
id: 943, |
|||
name: "AI中文范文", |
|||
sort: 23, |
|||
value_1: "8839.00", |
|||
value_2: "50174.00", |
|||
value_3: "120911.00", |
|||
value_4: "158772.50" |
|||
}, |
|||
] |
|||
that.top_list = { |
|||
6: { |
|||
id: "6", |
|||
name: "千校论文查重平台", |
|||
value_1: "214535.80" |
|||
}, |
|||
94: { |
|||
id: "94", |
|||
name: "尚志教育", |
|||
value_1: "149840.50" |
|||
}, |
|||
61: { |
|||
id: "61", |
|||
name: "千校论文查重平台", |
|||
value_1: "214535.80" |
|||
}, |
|||
941: { |
|||
id: "941", |
|||
name: "尚志教育", |
|||
value_1: "149840.50" |
|||
}, |
|||
62: { |
|||
id: "62", |
|||
name: "千校论文查重平台", |
|||
value_1: "214535.80" |
|||
}, |
|||
942: { |
|||
id: "942", |
|||
name: "尚志教育", |
|||
value_1: "149840.50" |
|||
}, |
|||
|
|||
} |
|||
// this.$http('POST', '/supernew/ajax_get_type_batch_list', { |
|||
// date: that.text, |
|||
// rank_type: that.dataRank, |
|||
// sort_by: that.sort_by, |
|||
// sort_order: that.sort_order, |
|||
// cur_page: that.currentPage, |
|||
// page_size: that.pageSize, |
|||
// }).then(response => { |
|||
// this.$nextTick(() => { |
|||
// that.tableData = response.data.list |
|||
// that.top_list = response.data.top_list |
|||
// that.total = response.data.total |
|||
// }) |
|||
// }).catch(error => { |
|||
// console.error(error, 'error') |
|||
// }) |
|||
}, |
|||
getRankingDetail(obj) { |
|||
const that = this |
|||
that.tableData1 = [] |
|||
this.$http('POST', '/supernew/ajax_get_rank_detail', { |
|||
date: that.text, |
|||
rank_type: that.dataRank, |
|||
sort_by: 2, |
|||
sort_order: 2, |
|||
...obj |
|||
}).then(response => { |
|||
this.$nextTick(() => { |
|||
that.tableData1 = response.data.list.slice(0, 5) |
|||
}) |
|||
}).catch(error => { |
|||
console.error(error, 'error') |
|||
}) |
|||
}, |
|||
getAgentRankingList(obj) { |
|||
const that = this |
|||
that.tableData1 = [] |
|||
this.$http('POST', '/supernew/ajax_get_agent_rank_list', { |
|||
date: that.text, |
|||
rank_type: that.dataRank, |
|||
cur_page: 1, |
|||
page_size: 5, |
|||
...obj |
|||
}).then(response => { |
|||
this.$nextTick(() => { |
|||
that.tableData1 = response.data.list |
|||
}) |
|||
}).catch(error => { |
|||
console.error(error, 'error') |
|||
}) |
|||
}, |
|||
getCheckTypeRankingList(obj) { |
|||
const that = this |
|||
that.tableData1 = [] |
|||
this.$http('POST', '/supernew/ajax_get_type_rank_list', { |
|||
date: that.text, |
|||
rank_type: that.dataRank, |
|||
cur_page: 1, |
|||
page_size: 5, |
|||
...obj |
|||
}).then(response => { |
|||
this.$nextTick(() => { |
|||
that.tableData1 = response.data.list.slice(0, 5) |
|||
}) |
|||
}).catch(error => { |
|||
console.error(error, 'error') |
|||
}) |
|||
}, |
|||
handleSizeChange(val) { |
|||
this.pageSize = val |
|||
this.getRankingData() |
|||
}, |
|||
handleCurrentChange(val) { |
|||
this.currentPage = val |
|||
this.getRankingData() |
|||
}, |
|||
}, |
|||
beforeDestory() { |
|||
if (this.resizeObserver) { |
|||
this.resizeObserver.disconnect(); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
<style scoped lang="scss"> |
|||
.lookMore { |
|||
cursor: pointer; |
|||
font-weight: 400; |
|||
} |
|||
|
|||
.table-container { |
|||
width: 100%; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.tableHeaderSelect ::v-deep .el-input__inner { |
|||
font-size: 14px; |
|||
font-weight: normal; |
|||
letter-spacing: 0.08em; |
|||
font-family: Microsoft YaHei UI; |
|||
color: #1E2226; |
|||
} |
|||
</style> |
Loading…
Reference in new issue