From 9f6cb0fa0ceb61626fc941d17242419433eb6b71 Mon Sep 17 00:00:00 2001 From: sunjie Date: Thu, 31 Jul 2025 15:54:17 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E7=94=B1=E4=BA=8E=E8=A7=86=E5=9B=BE?= =?UTF-8?q?=E5=B7=B2=E7=BB=8F=E4=BD=BF=E7=94=A8=E4=BA=86=20js=5Fclass,?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E9=87=8D=E5=A4=8D=E4=BD=BF=E7=94=A8(?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E4=BD=BF=E7=94=A8=E4=BC=9A=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E5=BA=8F=E5=8F=B7=E8=A6=86=E7=9B=96)=202.=20=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E5=9C=A8=20tree=20=E8=A7=86=E5=9B=BE=E6=B7=BB=E5=8A=A0=20class?= =?UTF-8?q?=E5=BD=A2=E5=BC=8F=E8=BF=9B=E8=A1=8C=E4=BA=86=E7=89=B9=E6=AE=8A?= =?UTF-8?q?=E5=88=A4=E6=96=AD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/list_view_sticky_header_and_column.js | 7 +- .../static/src/views/list/list_renderer.js | 15 +- .../list_view_sticky_header_and_column.scss | 2 +- yuthon_salary/__manifest__.py | 48 +++--- .../src/js/js_auto_hide_list_renderer.js | 35 +++++ .../static/src/js/salary_create_button.js | 9 +- .../js_auto_hide_list_renderer.js | 30 ++++ .../js_auto_hide_list_view.js | 12 ++ .../static/src/views/list/list_renderer.js | 142 ++++++++++++++++++ .../views/inherit_hr_payslip_views.xml | 3 +- 10 files changed, 266 insertions(+), 37 deletions(-) create mode 100644 yuthon_salary/static/src/js/js_auto_hide_list_renderer.js create mode 100644 yuthon_salary/static/src/views/js_auto_hide_list/js_auto_hide_list_renderer.js create mode 100644 yuthon_salary/static/src/views/js_auto_hide_list/js_auto_hide_list_view.js create mode 100644 yuthon_salary/static/src/views/list/list_renderer.js diff --git a/list_view_sticky_header_and_column/static/src/js/list_view_sticky_header_and_column.js b/list_view_sticky_header_and_column/static/src/js/list_view_sticky_header_and_column.js index 3ab2197b4..2bb60c6ea 100644 --- a/list_view_sticky_header_and_column/static/src/js/list_view_sticky_header_and_column.js +++ b/list_view_sticky_header_and_column/static/src/js/list_view_sticky_header_and_column.js @@ -1,13 +1,16 @@ /** @odoo-module **/ /** This file will used to stick the selected header and column in the list view **/ -import { patch } from "@web/core/utils/patch"; -import { ListRenderer } from "@web/views/list/list_renderer"; +import {patch} from "@web/core/utils/patch"; +import {ListRenderer} from "@web/views/list/list_renderer"; + patch(ListRenderer.prototype, { /** super the setup function **/ setup() { super.setup(); + }, + /** * function defining the button having t-on-click _onClick */ diff --git a/oo_list_manager/static/src/views/list/list_renderer.js b/oo_list_manager/static/src/views/list/list_renderer.js index 23d445dc8..7e4af5c07 100644 --- a/oo_list_manager/static/src/views/list/list_renderer.js +++ b/oo_list_manager/static/src/views/list/list_renderer.js @@ -1,10 +1,10 @@ /** @odoo-module **/ -import { ListRenderer } from "@web/views/list/list_renderer"; -import { patch } from "@web/core/utils/patch"; -import { loadJS, loadCSS } from "@web/core/assets"; -import { ListRendererCust } from "@tree_view_advanced_filter/tree_view_advanced_filter/tree" -import { onWillDestroy, useEffect } from "@odoo/owl"; +import {ListRenderer} from "@web/views/list/list_renderer"; +import {patch} from "@web/core/utils/patch"; +import {loadJS, loadCSS} from "@web/core/assets"; +import {ListRendererCust} from "@tree_view_advanced_filter/tree_view_advanced_filter/tree" +import {onWillDestroy, useEffect} from "@odoo/owl"; patch(ListRenderer.prototype, { setup() { @@ -14,6 +14,8 @@ patch(ListRenderer.prototype, { } const computeFunction = this.computeFreeze.bind(this); const resizeObserver = new ResizeObserver(computeFunction); + + useEffect( (tableEl) => { if (tableEl) { @@ -28,6 +30,7 @@ patch(ListRenderer.prototype, { }); }, + freezeColumnWidths() { super.freezeColumnWidths(); const table = this.tableRef.el; @@ -358,7 +361,7 @@ patch(ListRenderer.prototype, { tableRef.removeAttribute('style'); const columns = this.getActiveColumns(this.props.list); - const $tr1 = $('', { class: 'o_filter_box' }).append(columns.map(col => this._renderHeaderCellSearch(col))); + const $tr1 = $('', {class: 'o_filter_box'}).append(columns.map(col => this._renderHeaderCellSearch(col))); if (this.props.ooManagerSerialNum) $tr1.prepend($('').addClass('web_list_num')); if (this.hasSelectors) $tr1.prepend($('').addClass('o_list_record_selector')); if (this.isStudioEditable || this.displayOptionalFields) $tr1.append($('').addClass('o_list_record_selector')); diff --git a/tree_view_advanced_filter/static/src/list_view_sticky_header_and_column/scss/list_view_sticky_header_and_column.scss b/tree_view_advanced_filter/static/src/list_view_sticky_header_and_column/scss/list_view_sticky_header_and_column.scss index 8bc0078b0..fe1f837af 100644 --- a/tree_view_advanced_filter/static/src/list_view_sticky_header_and_column/scss/list_view_sticky_header_and_column.scss +++ b/tree_view_advanced_filter/static/src/list_view_sticky_header_and_column/scss/list_view_sticky_header_and_column.scss @@ -25,7 +25,7 @@ overflow: visible; } .o_list_view .table-responsive .table thead { - z-index: 5; + z-index: 20; } .o_list_renderer .o_list_table thead { position: sticky; diff --git a/yuthon_salary/__manifest__.py b/yuthon_salary/__manifest__.py index ad878561f..e14ba9adb 100644 --- a/yuthon_salary/__manifest__.py +++ b/yuthon_salary/__manifest__.py @@ -3,14 +3,14 @@ 'name': "yuthon salary", 'summary': "薪资管理", 'description': """ - - """, + + """, 'author': 'pengyuthon', 'website': 'https://www.pengyuthon.com', 'category': 'Human Resources/Attendances', 'version': '0.1', 'license': 'LGPL-3', - 'depends': ['mail', 'base', 'hr', 'yuthon_hr_employee','hr_payroll'], + 'depends': ['mail', 'base', 'hr', 'yuthon_hr_employee', 'hr_payroll'], 'data': [ 'security/ir.model.access.csv', 'security/salary_security_rule.xml', @@ -47,26 +47,28 @@ 'wizard/salary_print_wizard_views.xml', ], 'assets': { - 'web.assets_backend': [ - 'yuthon_salary/static/scss/approval.scss', - 'yuthon_salary/static/src/js/salary_documents_button.js', - 'yuthon_salary/static/src/js/salary_create_button.js', - 'yuthon_salary/static/src/js/salary_payslip_button.js', - 'yuthon_salary/static/src/js/salary_labor_button.js', - 'yuthon_salary/static/src/js/salary_return_button.js', - 'yuthon_salary/static/src/js/salary_retired_button.js', - 'yuthon_salary/static/src/js/salary_validation_button.js', - 'yuthon_salary/static/src/js/salary_year_button.js', - 'yuthon_salary/static/src/xml/salary_create_button.xml', - 'yuthon_salary/static/src/xml/salary_labor_button.xml', - 'yuthon_salary/static/src/xml/salary_retired_button.xml', - 'yuthon_salary/static/src/xml/salary_payslip_button.xml', - 'yuthon_salary/static/src/xml/salary_documents_button.xml', - 'yuthon_salary/static/src/xml/salary_validation_button.xml', - 'yuthon_salary/static/src/xml/salary_return_button.xml', - 'yuthon_salary/static/src/xml/salary_year_button.xml', - ], - }, + 'web.assets_backend': [ + 'yuthon_salary/static/scss/approval.scss', + 'yuthon_salary/static/src/js/js_auto_hide_list_renderer.js', + 'yuthon_salary/static/src/js/salary_documents_button.js', + 'yuthon_salary/static/src/js/salary_create_button.js', + 'yuthon_salary/static/src/js/salary_payslip_button.js', + 'yuthon_salary/static/src/js/salary_labor_button.js', + 'yuthon_salary/static/src/js/salary_return_button.js', + 'yuthon_salary/static/src/js/salary_retired_button.js', + 'yuthon_salary/static/src/js/salary_validation_button.js', + 'yuthon_salary/static/src/js/salary_year_button.js', + 'yuthon_salary/static/src/xml/salary_create_button.xml', + 'yuthon_salary/static/src/xml/salary_labor_button.xml', + 'yuthon_salary/static/src/xml/salary_retired_button.xml', + 'yuthon_salary/static/src/xml/salary_payslip_button.xml', + 'yuthon_salary/static/src/xml/salary_documents_button.xml', + 'yuthon_salary/static/src/xml/salary_validation_button.xml', + 'yuthon_salary/static/src/xml/salary_return_button.xml', + 'yuthon_salary/static/src/xml/salary_year_button.xml', + 'yuthon_salary/static/src/views/list/*', + ] + }, 'installable': True, 'application': True, } diff --git a/yuthon_salary/static/src/js/js_auto_hide_list_renderer.js b/yuthon_salary/static/src/js/js_auto_hide_list_renderer.js new file mode 100644 index 000000000..925dafd4d --- /dev/null +++ b/yuthon_salary/static/src/js/js_auto_hide_list_renderer.js @@ -0,0 +1,35 @@ +/** @odoo-module **/ + +import { ListRenderer } from "@web/views/list/list_renderer"; + +export class JsAutoHideListRenderer extends ListRenderer { + + getActiveColumns(list) { + const columns = super.getActiveColumns(list); + return columns + let values; + if (this.props.list.selection && this.props.list.selection.length) { + values = this.props.list.selection.map((r) => r.data); + } else if (this.props.list.isGrouped) { + values = this.props.list.groups.map((g) => g.aggregates); + } else { + values = this.props.list.records.map((r) => r.data); + } + return columns.filter(column => { + const fieldInfo = list.fields[column.name]; + if (!fieldInfo) { + return true; + } + if (["float", "monetary"].includes(fieldInfo.type)) { + const fieldValues = values.map((v) => v[fieldInfo.name] || 0); + const aggregateValue = fieldValues.reduce((acc, val) => acc + val); + if (!aggregateValue) { + console.log(column) + return true; + } + } + return true; + }) + } + +} \ No newline at end of file diff --git a/yuthon_salary/static/src/js/salary_create_button.js b/yuthon_salary/static/src/js/salary_create_button.js index f87e8a948..624152f31 100644 --- a/yuthon_salary/static/src/js/salary_create_button.js +++ b/yuthon_salary/static/src/js/salary_create_button.js @@ -1,7 +1,8 @@ /** @odoo-module */ -import { ListController } from "@web/views/list/list_controller"; -import { registry } from '@web/core/registry'; -import { listView } from '@web/views/list/list_view'; +import {ListController} from "@web/views/list/list_controller"; +import {registry} from '@web/core/registry'; +import {listView} from '@web/views/list/list_view'; +import {JsAutoHideListRenderer} from "./js_auto_hide_list_renderer"; export class CustomSalaryStatementsController extends ListController { setup() { @@ -25,5 +26,5 @@ export class CustomSalaryStatementsController extends ListController { registry.category("views").add("custom_salary_button", { ...listView, Controller: CustomSalaryStatementsController, - buttonTemplate: "salary_module.ListView.Buttons", + buttonTemplate: "salary_module.ListView.Buttons" }); \ No newline at end of file diff --git a/yuthon_salary/static/src/views/js_auto_hide_list/js_auto_hide_list_renderer.js b/yuthon_salary/static/src/views/js_auto_hide_list/js_auto_hide_list_renderer.js new file mode 100644 index 000000000..119ee286a --- /dev/null +++ b/yuthon_salary/static/src/views/js_auto_hide_list/js_auto_hide_list_renderer.js @@ -0,0 +1,30 @@ +/** @odoo-module **/ + +import { ListRenderer } from "@web/views/list/list_renderer"; + +export class JsAutoHideListRenderer extends ListRenderer { + + getActiveColumns(list) { + const columns = super.getActiveColumns(list); + let values; + if (this.props.list.selection && this.props.list.selection.length) { + values = this.props.list.selection.map((r) => r.data); + } else if (this.props.list.isGrouped) { + values = this.props.list.groups.map((g) => g.aggregates); + } else { + values = this.props.list.records.map((r) => r.data); + } + return columns.filter(column => { + const fieldInfo = list.fields[column.name]; + if (["float", "integer", "monetary"].includes(fieldInfo.type)) { + const fieldValues = values.map((v) => v[fieldInfo.name] || 0); + const aggregateValue = fieldValues.reduce((acc, val) => acc + val); + if (!aggregateValue) { + return false; + } + } + return true; + }) + } + +} \ No newline at end of file diff --git a/yuthon_salary/static/src/views/js_auto_hide_list/js_auto_hide_list_view.js b/yuthon_salary/static/src/views/js_auto_hide_list/js_auto_hide_list_view.js new file mode 100644 index 000000000..ae29f6817 --- /dev/null +++ b/yuthon_salary/static/src/views/js_auto_hide_list/js_auto_hide_list_view.js @@ -0,0 +1,12 @@ +/** @odoo-module */ + +import { registry } from "@web/core/registry"; +import { listView } from "@web/views/list/list_view"; +import { JsAutoHideListRenderer } from "./js_auto_hide_list_renderer"; + +export const JsAutoHideListView = { + ...listView, + Renderer: JsAutoHideListRenderer +} + +registry.category("views").add("jsAutoHideListView", JsAutoHideListView); \ No newline at end of file diff --git a/yuthon_salary/static/src/views/list/list_renderer.js b/yuthon_salary/static/src/views/list/list_renderer.js new file mode 100644 index 000000000..648fbabdd --- /dev/null +++ b/yuthon_salary/static/src/views/list/list_renderer.js @@ -0,0 +1,142 @@ +/** @odoo-module **/ + +import {ListRenderer} from "@web/views/list/list_renderer"; +import {patch} from "@web/core/utils/patch"; +import {loadJS, loadCSS} from "@web/core/assets"; +import {ListRendererCust} from "@tree_view_advanced_filter/tree_view_advanced_filter/tree" +import {onWillDestroy, useEffect, onMounted} from "@odoo/owl"; + +patch(ListRenderer.prototype, { + setup() { + super.setup(); + // 固定两列 + onMounted(() => { + this.freezeTwoColumns() + }) + }, + /** + * 根据类名固定前两列和最后一列 + */ + freezeTwoColumns() { + + + const root = this.rootRef.el.closest('.sticky-enabled-two-columns'); + + if (!root) return; + // + const headers = this.rootRef.el.querySelectorAll("thead th"); + const dataRows = this.rootRef.el.querySelectorAll("tbody tr.o_data_row"); + const groupRows = this.rootRef.el.querySelectorAll("tbody tr.o_group_header"); + + // + if (headers.length < 3) return; // 至少需要3列才能固定前两和最后一列 + + // 动态获取前两列宽度 + const leftOffsets = []; + const colWidths = []; + let leftWidthTotal = 0 + for (let i = 0; i < 5; i++) { + const width = headers[i].getBoundingClientRect().width; + colWidths.push(width); + leftWidthTotal = leftWidthTotal + width + leftOffsets.push(leftWidthTotal) + } + console.log(leftOffsets) + + const lastIndex = headers.length - 1; + + // 前两列累计偏移 + + + // 固定前两列 header + for (let i = 0; i < 5; i++) { + this._makeSticky(headers[i], i === 0 ? 0 : leftOffsets[i - 1], "left"); + } + + // 固定最后一列 header + this._makeSticky(headers[lastIndex], 0, "right"); + + // 固定数据行的对应列 + dataRows.forEach(row => { + for (let i = 0; i < 5; i++) { + this._makeSticky(row.children[i], i === 0 ? 0 : leftOffsets[i - 1], "left"); + } + this._makeSticky(row.children[lastIndex], 0, "right"); + }); + + // 固定分组标题行(有些分组行列数会少于 header) + // 分组只固定 前2 列 + groupRows.forEach(row => { + const cells = row.querySelectorAll("td"); + + // 分组第一个必然固定 + this._makeSticky(cells[0], 0, "left", true); + // 全部固定 + const cellThs = row.querySelectorAll("th") + for (let i = 0; i < cellThs.length; i++) { + const offset = leftOffsets[i]; + this._makeSticky(cellThs[i], offset, "left", true); + } + }); + }, + _makeSticky(cell, offset = 0, align = "left", isGroup = false) { + + if (!cell) return; + // cell 添加 class position-sticky + cell.classList.add("position-sticky"); + cell.style.zIndex = isGroup ? "15" : "10"; + cell.style.background = "#fff"; + if (align === "left") { + cell.style.left = `${offset}px`; + } else if (align === "right") { + cell.style.right = `${offset}px`; + } + }, + + toggleGroup(group) { + super.toggleGroup(group) + setTimeout(() => { + this.freezeTwoColumns() + }, 500) + + }, + + getActiveColumns(list) { + const columns = super.getActiveColumns(list); + console.log(this.props.archInfo.className) + // 判断 sticky-enabled-two-columns 在 this.props.archInfo.className + if (!this.props.archInfo.className.includes('sticky-enabled-two-columns')) { + return columns + } + + let values; + if (this.props.list.selection && this.props.list.selection.length) { + values = this.props.list.selection.map((r) => r.data); + } else if (this.props.list.isGrouped) { + values = this.props.list.groups.map((g) => g.aggregates); + } else { + values = this.props.list.records.map((r) => r.data); + } + return columns.filter(column => { + const fieldInfo = list.fields[column.name]; + if (!fieldInfo) { + return true; + } + if (["float","int", "monetary"].includes(fieldInfo.type)) { + const fieldValues = values.map((v) => v[fieldInfo.name] || 0); + const aggregateValue = fieldValues.reduce((acc, val) => acc + val); + if (!aggregateValue) { + console.log(column) + return false; + } + } + return true; + }) + } + +}); + +// ListRenderer.props.push('ooManagerColumns?'); +// ListRenderer.props.push('ooManagerFilter?'); +// ListRenderer.props.push('ooManagerSerialNum?'); +// ListRenderer.props.push('ooManagerStickyEnd?'); diff --git a/yuthon_salary/views/inherit_hr_payslip_views.xml b/yuthon_salary/views/inherit_hr_payslip_views.xml index f9003f0ea..da91c27ba 100644 --- a/yuthon_salary/views/inherit_hr_payslip_views.xml +++ b/yuthon_salary/views/inherit_hr_payslip_views.xml @@ -10,6 +10,7 @@ custom_salary_validation_button employee_id 20 + sticky-enabled-two-columns false @@ -415,7 +416,7 @@ hr.payslip.tree.jtbb hr.payslip - +