From 7cff04e0de33e09946c2b9ed143082d70f47fe07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= <9664676+pengyuthon@user.noreply.gitee.com> Date: Fri, 19 Dec 2025 15:24:31 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{ => header_search_panel}/README.md | 19 +- .../{ => header_search_panel}/__init__.py | 0 .../{ => header_search_panel}/__manifest__.py | 0 .../override_relaxng.py | 0 .../rng/search_view.rng | 0 .../static/src/legacy/backend_utils.js | 33 +++ .../src/search/control_panel/control_panel.js | 0 .../search/control_panel/control_panel.scss | 0 .../search/control_panel/control_panel.xml | 0 .../boolean_filter/boolean_filter.js | 0 .../boolean_filter/boolean_filter.xml | 0 .../fields_filter/char_filter/char_filter.js | 54 +++++ .../fields_filter/char_filter/char_filter.xml | 15 ++ .../fields_filter/date_filter/date_filter.js | 37 ++- .../fields_filter/date_filter/date_filter.xml | 37 +++ .../components/fields_filter/fields_filter.js | 15 ++ .../fields_filter/fields_filter.xml | 0 .../many2one_filter/many2one_filter.js | 1 + .../many2one_filter/many2one_filter.xml | 0 .../selection_filter/selection_filter.js | 0 .../selection_filter/selection_filter.xml | 0 .../standard_field_filter_props.js | 2 + .../x2many_filter/x2many_filter.js | 0 .../x2many_filter/x2many_filter.xml | 0 .../components/quick_filter/quick_filter.js | 0 .../components/quick_filter/quick_filter.xml | 4 +- .../quick_filter/quick_filter_label.scss | 0 .../select_date_filter/select_date_filter.js | 0 .../select_date_filter/select_date_filter.xml | 0 .../header_search/header_search_filter.js | 0 .../header_search/header_search_filter.scss | 14 +- .../header_search/header_search_filter.xml | 0 .../static/src/search/search_arch_parser.js | 14 +- .../static/src/search/search_model.js | 0 header_search_panel/model_test/__init__.py | 4 + .../model_test/__manifest__.py | 21 ++ .../model_test/controllers/__init__.py | 3 + .../model_test/controllers/controllers.py | 21 ++ .../model_test/models/__init__.py | 3 + .../model_test/models/models.py | 211 ++++++++++++++++++ .../model_test/security/ir.model.access.csv | 3 + .../model_test/static/src/model_test_list.xml | 17 ++ .../static/src/model_test_list_controller.js | 10 + .../model_test/views/views.xml | 126 +++++++++++ .../fields_filter/date_filter/date_filter.xml | 30 --- 45 files changed, 636 insertions(+), 58 deletions(-) rename header_search_panel/{ => header_search_panel}/README.md (79%) rename header_search_panel/{ => header_search_panel}/__init__.py (100%) rename header_search_panel/{ => header_search_panel}/__manifest__.py (100%) rename header_search_panel/{ => header_search_panel}/override_relaxng.py (100%) rename header_search_panel/{ => header_search_panel}/rng/search_view.rng (100%) create mode 100644 header_search_panel/header_search_panel/static/src/legacy/backend_utils.js rename header_search_panel/{ => header_search_panel}/static/src/search/control_panel/control_panel.js (100%) rename header_search_panel/{ => header_search_panel}/static/src/search/control_panel/control_panel.scss (100%) rename header_search_panel/{ => header_search_panel}/static/src/search/control_panel/control_panel.xml (100%) rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/components/fields_filter/boolean_filter/boolean_filter.js (100%) rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/components/fields_filter/boolean_filter/boolean_filter.xml (100%) create mode 100644 header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/char_filter/char_filter.js create mode 100644 header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/char_filter/char_filter.xml rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/components/fields_filter/date_filter/date_filter.js (65%) create mode 100644 header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.xml rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/components/fields_filter/fields_filter.js (93%) rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/components/fields_filter/fields_filter.xml (100%) rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.js (98%) rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.xml (100%) rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/components/fields_filter/selection_filter/selection_filter.js (100%) rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/components/fields_filter/selection_filter/selection_filter.xml (100%) rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/components/fields_filter/standard_field_filter_props.js (88%) rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/components/fields_filter/x2many_filter/x2many_filter.js (100%) rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/components/fields_filter/x2many_filter/x2many_filter.xml (100%) rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/components/quick_filter/quick_filter.js (100%) rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/components/quick_filter/quick_filter.xml (92%) rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/components/quick_filter/quick_filter_label.scss (100%) rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/components/select_date_filter/select_date_filter.js (100%) rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/components/select_date_filter/select_date_filter.xml (100%) rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/header_search_filter.js (100%) rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/header_search_filter.scss (96%) rename header_search_panel/{ => header_search_panel}/static/src/search/header_search/header_search_filter.xml (100%) rename header_search_panel/{ => header_search_panel}/static/src/search/search_arch_parser.js (96%) rename header_search_panel/{ => header_search_panel}/static/src/search/search_model.js (100%) create mode 100644 header_search_panel/model_test/__init__.py create mode 100644 header_search_panel/model_test/__manifest__.py create mode 100644 header_search_panel/model_test/controllers/__init__.py create mode 100644 header_search_panel/model_test/controllers/controllers.py create mode 100644 header_search_panel/model_test/models/__init__.py create mode 100644 header_search_panel/model_test/models/models.py create mode 100644 header_search_panel/model_test/security/ir.model.access.csv create mode 100644 header_search_panel/model_test/static/src/model_test_list.xml create mode 100644 header_search_panel/model_test/static/src/model_test_list_controller.js create mode 100644 header_search_panel/model_test/views/views.xml delete mode 100644 header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.xml diff --git a/header_search_panel/README.md b/header_search_panel/header_search_panel/README.md similarity index 79% rename from header_search_panel/README.md rename to header_search_panel/header_search_panel/README.md index 5339e4749..9e9d7b0bf 100644 --- a/header_search_panel/README.md +++ b/header_search_panel/header_search_panel/README.md @@ -6,7 +6,7 @@ - 快捷筛选(quick_filter) - 自选时间筛选(select_date_filter)(待移植) -- 字段搜索(field)(待移植) +- 字段搜索(field)(已移植) 通过这些筛选标签,用户可以更灵活地对列表视图中的数据进行筛选。 @@ -20,9 +20,9 @@ #### 支持的属性: -| 属性名 | 类型 | 说明 | 是否可选 | -| ------- | ------ | ---------- | ---- | -| `class` | String | 自定义 CSS 类名 | ✅ | +| 属性名 | 类型 | 说明 | 是否可选 | +| ------- | ------ |-------------------------------------------------------------------| ---- | +| `class` | String | 自定义 CSS 类名,可使用class="header-search-inline"使所有筛选在同一行并根据宽度自动换行 | ✅ | #### 子标签支持: @@ -56,7 +56,7 @@ | ----------- | ------- | ----------- | ------------ | | `class` | String | 自定义 CSS 类名 | ✅ | | `string` | String | 显示的标头名称 | ✅ | -| `invisible` | Boolean | 是否默认隐藏 | ✅ | +| `invisible` | Boolean | 是否默认隐藏(废弃) | ✅ | | `options` | JSON | 扩展参数,支持以下参数: | ✅ | | | | `radio`: 是否单选 | | | | | `no_label`: 不显示标题 | | @@ -77,16 +77,16 @@ --- -### 3. 自选时间筛选 (select_date_filter) +### 3. 自选时间筛选 (select_date_filter)(废弃) **标签名:** `select_date_filter` #### 支持的属性: | 属性名 | 类型 | 说明 | 是否可选 | -| ------------------- | ------- | -------------- | ---- | +| ------------------- | ------- |----------------| ---- | | `class` | String | 自定义 CSS 类名 | ✅ | -| `invisible` | Boolean | 是否默认隐藏 | ✅ | +| `invisible` | Boolean | 是否默认隐藏(废弃) | ✅ | | `allow_date_fields` | String | 允许筛选的时间字段,逗号分隔 | ✅ | #### 示例: @@ -109,6 +109,9 @@ | `string` | String | 显示的标头名称 | ✅ | | `class` | String | 自定义 CSS | ✅ | | `invisible` | Boolean | 是否默认隐藏 | ✅ | +| `options` | JSON | 扩展参数,支持以下参数: | ✅ | +| | | `radio`: 是否单选 | | +| | | `no_label`: 不显示标题 | | #### 示例: diff --git a/header_search_panel/__init__.py b/header_search_panel/header_search_panel/__init__.py similarity index 100% rename from header_search_panel/__init__.py rename to header_search_panel/header_search_panel/__init__.py diff --git a/header_search_panel/__manifest__.py b/header_search_panel/header_search_panel/__manifest__.py similarity index 100% rename from header_search_panel/__manifest__.py rename to header_search_panel/header_search_panel/__manifest__.py diff --git a/header_search_panel/override_relaxng.py b/header_search_panel/header_search_panel/override_relaxng.py similarity index 100% rename from header_search_panel/override_relaxng.py rename to header_search_panel/header_search_panel/override_relaxng.py diff --git a/header_search_panel/rng/search_view.rng b/header_search_panel/header_search_panel/rng/search_view.rng similarity index 100% rename from header_search_panel/rng/search_view.rng rename to header_search_panel/header_search_panel/rng/search_view.rng diff --git a/header_search_panel/header_search_panel/static/src/legacy/backend_utils.js b/header_search_panel/header_search_panel/static/src/legacy/backend_utils.js new file mode 100644 index 000000000..6f77264e2 --- /dev/null +++ b/header_search_panel/header_search_panel/static/src/legacy/backend_utils.js @@ -0,0 +1,33 @@ +/** @odoo-module **/ +// +// import {patch} from "@web/core/utils/patch"; +// import * as backend_utils from "@web/legacy/backend_utils"; +// +// const originalSearchModelStateToLegacy = backend_utils.searchModelStateToLegacy; +// const originalGetGlobalState = backend_utils.getGlobalState; +// +// patch(backend_utils, "header_search_panel.backend_utils", { +// searchModelStateToLegacy(state) { +// // 原始state +// const originalState = JSON.parse(state); +// // 获取原始state +// let legacyState = JSON.parse(originalSearchModelStateToLegacy(state)); +// if (originalState.headerSearchInfo) { +// legacyState.HeaderSearchInfo = originalState.headerSearchInfo; +// } +// return JSON.stringify(legacyState) +// }, +// +// getGlobalState(legacyControllerState) { +// // 原始search model +// const originalSearchModel = JSON.parse(legacyControllerState.searchModel) +// let globalState = originalGetGlobalState(legacyControllerState); +// let searchModel = JSON.parse(globalState?.searchModel || "{}"); +// if (originalSearchModel.HeaderSearchInfo) { +// searchModel.headerSearchInfo = originalSearchModel.HeaderSearchInfo; +// globalState.searchModel = JSON.stringify(searchModel) +// } +// console.log('headerSearchInfo: getGlobalState') +// return globalState +// }, +// }) \ No newline at end of file diff --git a/header_search_panel/static/src/search/control_panel/control_panel.js b/header_search_panel/header_search_panel/static/src/search/control_panel/control_panel.js similarity index 100% rename from header_search_panel/static/src/search/control_panel/control_panel.js rename to header_search_panel/header_search_panel/static/src/search/control_panel/control_panel.js diff --git a/header_search_panel/static/src/search/control_panel/control_panel.scss b/header_search_panel/header_search_panel/static/src/search/control_panel/control_panel.scss similarity index 100% rename from header_search_panel/static/src/search/control_panel/control_panel.scss rename to header_search_panel/header_search_panel/static/src/search/control_panel/control_panel.scss diff --git a/header_search_panel/static/src/search/control_panel/control_panel.xml b/header_search_panel/header_search_panel/static/src/search/control_panel/control_panel.xml similarity index 100% rename from header_search_panel/static/src/search/control_panel/control_panel.xml rename to header_search_panel/header_search_panel/static/src/search/control_panel/control_panel.xml diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/boolean_filter/boolean_filter.js b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/boolean_filter/boolean_filter.js similarity index 100% rename from header_search_panel/static/src/search/header_search/components/fields_filter/boolean_filter/boolean_filter.js rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/boolean_filter/boolean_filter.js diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/boolean_filter/boolean_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/boolean_filter/boolean_filter.xml similarity index 100% rename from header_search_panel/static/src/search/header_search/components/fields_filter/boolean_filter/boolean_filter.xml rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/boolean_filter/boolean_filter.xml diff --git a/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/char_filter/char_filter.js b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/char_filter/char_filter.js new file mode 100644 index 000000000..80cceca3a --- /dev/null +++ b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/char_filter/char_filter.js @@ -0,0 +1,54 @@ +/** @odoo-module **/ + +import { Component } from "@odoo/owl"; +import { Domain } from "@web/core/domain"; +import { useService } from "@web/core/utils/hooks"; +import { Many2XAutocomplete } from "@web/views/fields/relational_utils"; +import { standardFieldFilterProps } from "../standard_field_filter_props.js" + + +export class CharFilter extends Component { + setup() { + super.setup(); + this.rpc = useService("rpc"); + this.activeValues = this.env.searchModel.headerSearchInfo.FieldsFilter[this.props.id]?.activeValues || undefined; + } + + /** + * 设置筛选字段的值 + */ + setActiveValues(ev) { + this.activeValues = ev.target.value; + } + + /** + * 获取筛选字段的domain + */ + get domain() { + return this.activeValues ? [[this.props.name, "ilike", this.activeValues]]: []; + } + + /** + * 点击清空筛选时触发 + */ + resetFilter() { + // 清空筛选字段的值 + this.activeValues = undefined; + this.props.fieldOnchange({fieldDomainList: this.domain, activeValues: this.activeValues}); + } + + getDomain() { + return new Domain(this.props.domain).toList({}); + } + + async onInputChange(ev) { + this.setActiveValues(ev); + this.props.fieldOnchange({fieldDomainList: this.domain, activeValues: this.activeValues}); + } +} + +CharFilter.template = "header_search_panel.CharFilter"; +CharFilter.components = {}; +CharFilter.props = { + ...standardFieldFilterProps, +}; diff --git a/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/char_filter/char_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/char_filter/char_filter.xml new file mode 100644 index 000000000..66f439179 --- /dev/null +++ b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/char_filter/char_filter.xml @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.js b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.js similarity index 65% rename from header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.js rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.js index e72c40f44..d27ef8ff0 100644 --- a/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.js +++ b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.js @@ -1,8 +1,9 @@ /** @odoo-module **/ -import {Component} from "@odoo/owl"; -import {Domain} from "@web/core/domain"; -import {standardFieldFilterProps} from "../standard_field_filter_props.js" +import { Component } from "@odoo/owl"; +import { DateTimeInput } from "@web/core/datetime/datetime_input"; +import { deserializeDate } from "@web/core/l10n/dates"; +import { standardFieldFilterProps } from "../standard_field_filter_props.js" export class DateFilter extends Component { setup() { @@ -13,13 +14,21 @@ export class DateFilter extends Component { this.endDate = this.env.searchModel.headerSearchInfo.FieldsFilter[this.props.id]?.endDate; } + get dateFrom() { + return this.startDate ? deserializeDate(this.startDate): undefined; + } + + get dateTo() { + return this.endDate ? deserializeDate(this.endDate): undefined; + } + // 设置筛选字段的值 - setActiveValues(ev) { - const value = ev.target.value; - if (ev.target.id === this.props.id + "_start_date") { + setActiveValues(dateValue, type) { + const value = dateValue ? (this.props.type === "datetime" ? dateValue.toFormat("yyyy-MM-dd HH:mm:ss") : dateValue.toFormat("yyyy-MM-dd")) : undefined; + if (type === "startDate") { this.startDate = value } - if (ev.target.id === this.props.id + "_end_date") { + if (type === "endDate") { this.endDate = value } } @@ -41,9 +50,14 @@ export class DateFilter extends Component { return domain } - // 点击日期选择器时触发 - dateOnchange(ev) { - this.setActiveValues(ev); + startDateOnchange (startDate) { + this.setActiveValues(startDate, "startDate"); + this.props.fieldOnchange({fieldDomainList: this.domain, startDate: this.startDate, endDate: this.endDate}); + + } + + endDateOnchange (endDate) { + this.setActiveValues(endDate, "endDate"); this.props.fieldOnchange({fieldDomainList: this.domain, startDate: this.startDate, endDate: this.endDate}); } @@ -67,6 +81,9 @@ export class DateFilter extends Component { } DateFilter.template = "header_search_panel.DateFilter"; +DateFilter.components = { + DateTimeInput, +}; DateFilter.props = { ...standardFieldFilterProps, }; diff --git a/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.xml new file mode 100644 index 000000000..bf86c13a4 --- /dev/null +++ b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.js b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.js similarity index 93% rename from header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.js rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.js index c70c0878c..0295ec005 100644 --- a/header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.js +++ b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.js @@ -8,6 +8,7 @@ import { BooleanFilter } from "./boolean_filter/boolean_filter"; import { DateFilter } from "./date_filter/date_filter"; import { Many2oneFilter } from "./many2one_filter/many2one_filter"; import { X2ManyFilterFilter } from "./x2many_filter/x2many_filter"; +import { CharFilter } from "./char_filter/char_filter"; const fieldRegistry = registry.category("fields"); @@ -41,6 +42,9 @@ export class FieldsFilter extends Component { case "one2many": this.addX2ManyFieldDependencies(fieldInfo) break; + case "char": + this.addCharFieldDependencies(fieldInfo) + break; } return fieldInfo; } @@ -65,6 +69,9 @@ export class FieldsFilter extends Component { case "many2many": case "one2many": return X2ManyFilterFilter; + case "char": + return CharFilter; + } } @@ -128,6 +135,7 @@ export class FieldsFilter extends Component { domain: field.fieldDomain || "[]", fieldString: field.fieldString, searchLimit: field.fieldOptions?.search_limit || 10, + placeholder: field.placeholder, } this._addFieldBaseProps(field.props, field); } @@ -144,6 +152,13 @@ export class FieldsFilter extends Component { this._addFieldBaseProps(field.props, field); } + addCharFieldDependencies(field) { + field.Component = this._getComponent(field); + // 组件props + field.props = {}; + this._addFieldBaseProps(field.props, field); + } + // 筛选字段的onChange事件,将筛选字段的id和domain传给searchModel fieldFilterOnchange(fieldsFilterId, fieldsFilterInfo) { if (fieldsFilterInfo?.fieldDomainList){ diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.xml similarity index 100% rename from header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.xml rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.xml diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.js b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.js similarity index 98% rename from header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.js rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.js index 1c80a1a04..4db0404ab 100644 --- a/header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.js +++ b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.js @@ -104,6 +104,7 @@ export class Many2oneFilter extends Component { activeActions: {create: false, createEdit: false, write: false}, fieldString: this.props.fieldString, searchLimit: this.props.searchLimit, + placeholder: this.props.placeholder, }; } } diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.xml similarity index 100% rename from header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.xml rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.xml diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/selection_filter/selection_filter.js b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/selection_filter/selection_filter.js similarity index 100% rename from header_search_panel/static/src/search/header_search/components/fields_filter/selection_filter/selection_filter.js rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/selection_filter/selection_filter.js diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/selection_filter/selection_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/selection_filter/selection_filter.xml similarity index 100% rename from header_search_panel/static/src/search/header_search/components/fields_filter/selection_filter/selection_filter.xml rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/selection_filter/selection_filter.xml diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/standard_field_filter_props.js b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/standard_field_filter_props.js similarity index 88% rename from header_search_panel/static/src/search/header_search/components/fields_filter/standard_field_filter_props.js rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/standard_field_filter_props.js index 231ebf6b4..2fa7705c2 100644 --- a/header_search_panel/static/src/search/header_search/components/fields_filter/standard_field_filter_props.js +++ b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/standard_field_filter_props.js @@ -13,4 +13,6 @@ export const standardFieldFilterProps = { type: {type: String, required: true}, // 字段变换监听函数 fieldOnchange: { type: Function, required: true }, + // 提示 + placeholder: {type: String, optional: true}, }; diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/x2many_filter/x2many_filter.js b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/x2many_filter/x2many_filter.js similarity index 100% rename from header_search_panel/static/src/search/header_search/components/fields_filter/x2many_filter/x2many_filter.js rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/x2many_filter/x2many_filter.js diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/x2many_filter/x2many_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/x2many_filter/x2many_filter.xml similarity index 100% rename from header_search_panel/static/src/search/header_search/components/fields_filter/x2many_filter/x2many_filter.xml rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/x2many_filter/x2many_filter.xml diff --git a/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.js b/header_search_panel/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.js similarity index 100% rename from header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.js rename to header_search_panel/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.js diff --git a/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.xml similarity index 92% rename from header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.xml rename to header_search_panel/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.xml index 17da74122..7ea0d8bbe 100644 --- a/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.xml +++ b/header_search_panel/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.xml @@ -2,11 +2,11 @@ - + diff --git a/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter_label.scss b/header_search_panel/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter_label.scss similarity index 100% rename from header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter_label.scss rename to header_search_panel/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter_label.scss diff --git a/header_search_panel/static/src/search/header_search/components/select_date_filter/select_date_filter.js b/header_search_panel/header_search_panel/static/src/search/header_search/components/select_date_filter/select_date_filter.js similarity index 100% rename from header_search_panel/static/src/search/header_search/components/select_date_filter/select_date_filter.js rename to header_search_panel/header_search_panel/static/src/search/header_search/components/select_date_filter/select_date_filter.js diff --git a/header_search_panel/static/src/search/header_search/components/select_date_filter/select_date_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/components/select_date_filter/select_date_filter.xml similarity index 100% rename from header_search_panel/static/src/search/header_search/components/select_date_filter/select_date_filter.xml rename to header_search_panel/header_search_panel/static/src/search/header_search/components/select_date_filter/select_date_filter.xml diff --git a/header_search_panel/static/src/search/header_search/header_search_filter.js b/header_search_panel/header_search_panel/static/src/search/header_search/header_search_filter.js similarity index 100% rename from header_search_panel/static/src/search/header_search/header_search_filter.js rename to header_search_panel/header_search_panel/static/src/search/header_search/header_search_filter.js diff --git a/header_search_panel/static/src/search/header_search/header_search_filter.scss b/header_search_panel/header_search_panel/static/src/search/header_search/header_search_filter.scss similarity index 96% rename from header_search_panel/static/src/search/header_search/header_search_filter.scss rename to header_search_panel/header_search_panel/static/src/search/header_search/header_search_filter.scss index 32a658282..26091f318 100644 --- a/header_search_panel/static/src/search/header_search/header_search_filter.scss +++ b/header_search_panel/header_search_panel/static/src/search/header_search/header_search_filter.scss @@ -74,10 +74,12 @@ .fast-date-filter { display: flex; flex-wrap: wrap; + align-items: center; } - input[type="date"], + .o_datetime_input, .fast-date-filter a { + width: auto; margin: 0.2rem; padding: 0.1rem 1rem; border: 0.1rem solid $o-community-color; /* 将边框颜色改为黑色 */ @@ -149,6 +151,14 @@ } } } + +.header-search-inline { + display: flex; + flex-wrap: wrap; + align-items: start; + gap: 5px; +} + .header_search_toggle { width: 6rem; height: 2rem; @@ -161,4 +171,4 @@ line-height: 2rem; margin-left: 0.5rem; } -} \ No newline at end of file +} diff --git a/header_search_panel/static/src/search/header_search/header_search_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/header_search_filter.xml similarity index 100% rename from header_search_panel/static/src/search/header_search/header_search_filter.xml rename to header_search_panel/header_search_panel/static/src/search/header_search/header_search_filter.xml diff --git a/header_search_panel/static/src/search/search_arch_parser.js b/header_search_panel/header_search_panel/static/src/search/search_arch_parser.js similarity index 96% rename from header_search_panel/static/src/search/search_arch_parser.js rename to header_search_panel/header_search_panel/static/src/search/search_arch_parser.js index eaa2fddb4..e53f615e1 100644 --- a/header_search_panel/static/src/search/search_arch_parser.js +++ b/header_search_panel/header_search_panel/static/src/search/search_arch_parser.js @@ -165,6 +165,8 @@ patch(SearchArchParser.prototype, { const fieldString = node.getAttribute("string"); // 自定义css类 const cssClass = node.getAttribute("class"); + // placeholder + const placeholder = node.getAttribute("placeholder"); // 获取字段的options属性,该属性代表字段选项 const fieldOptions = evaluateExpr(node.getAttribute("options") || "{}"); // 获取widget @@ -173,7 +175,7 @@ patch(SearchArchParser.prototype, { if (fieldName) { const field = this.fields[fieldName]; // 如果字段类型为selection或者bool,将字段名和选项添加到headerSearchInfo的fields中 - if (!["selection", "boolean", "date", "datetime", "many2one", "one2many", "many2many"].includes(field.type)) { + if (!["selection", "boolean", "date", "datetime", "many2one", "one2many", "many2many", "char"].includes(field.type)) { return false; } const fieldInfo = { @@ -186,6 +188,7 @@ patch(SearchArchParser.prototype, { fieldOptions: fieldOptions, modifiers: JSON.parse(node.getAttribute("modifiers") || "{}"), class: cssClass, + placeholder: placeholder || undefined, } if (["many2one", "many2many"].includes(field.type)) { fieldInfo.fieldDomain = node.getAttribute("domain"); @@ -237,17 +240,16 @@ patch(SearchArchParser.prototype, { for (const node of headerSearchNode.children) { switch (node.tagName) { case "field": - // // 字段筛选 - // this.visitHeaderField(node); + // 字段筛选 + this.visitHeaderField(node); break; case "quick_filter": // 快捷筛选 this.visitHeaderQuickFilter(node); break; case "select_date_filter": - // // 自选日期筛选 - // this.visitHeaderSelectDateFilter(node); - break; + // 自选日期筛选 + this.visitHeaderSelectDateFilter(node); } } return false; diff --git a/header_search_panel/static/src/search/search_model.js b/header_search_panel/header_search_panel/static/src/search/search_model.js similarity index 100% rename from header_search_panel/static/src/search/search_model.js rename to header_search_panel/header_search_panel/static/src/search/search_model.js diff --git a/header_search_panel/model_test/__init__.py b/header_search_panel/model_test/__init__.py new file mode 100644 index 000000000..511a0ca3a --- /dev/null +++ b/header_search_panel/model_test/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import controllers +from . import models \ No newline at end of file diff --git a/header_search_panel/model_test/__manifest__.py b/header_search_panel/model_test/__manifest__.py new file mode 100644 index 000000000..42eb43c94 --- /dev/null +++ b/header_search_panel/model_test/__manifest__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +{ + 'name': '模型测试', + 'summary': '', + 'description': '', + 'author': 'XSK', + 'website': '', + 'category': 'Uncategorized', + 'version': '0.1', + 'license': 'LGPL-3', + 'depends': ['base', 'web', 'mail'], + 'data': [ + 'security/ir.model.access.csv', + 'views/views.xml', + ], + 'assets': { + 'web.assets_backend': [ + 'model_test/static/src/**/*', + ], + }, +} diff --git a/header_search_panel/model_test/controllers/__init__.py b/header_search_panel/model_test/controllers/__init__.py new file mode 100644 index 000000000..457bae27e --- /dev/null +++ b/header_search_panel/model_test/controllers/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import controllers \ No newline at end of file diff --git a/header_search_panel/model_test/controllers/controllers.py b/header_search_panel/model_test/controllers/controllers.py new file mode 100644 index 000000000..22c44a3ab --- /dev/null +++ b/header_search_panel/model_test/controllers/controllers.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# from odoo import http + + +# class FilePreviewTest(http.Controller): +# @http.route('/file_preview_test/file_preview_test', auth='public') +# def index(self, **kw): +# return "Hello, world" + +# @http.route('/file_preview_test/file_preview_test/objects', auth='public') +# def list(self, **kw): +# return http.request.render('file_preview_test.listing', { +# 'root': '/file_preview_test/file_preview_test', +# 'objects': http.request.env['file_preview_test.file_preview_test'].search([]), +# }) + +# @http.route('/file_preview_test/file_preview_test/objects/', auth='public') +# def object(self, obj, **kw): +# return http.request.render('file_preview_test.object', { +# 'object': obj +# }) diff --git a/header_search_panel/model_test/models/__init__.py b/header_search_panel/model_test/models/__init__.py new file mode 100644 index 000000000..5305644df --- /dev/null +++ b/header_search_panel/model_test/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import models \ No newline at end of file diff --git a/header_search_panel/model_test/models/models.py b/header_search_panel/model_test/models/models.py new file mode 100644 index 000000000..a6b58a725 --- /dev/null +++ b/header_search_panel/model_test/models/models.py @@ -0,0 +1,211 @@ +# -*- coding: utf-8 -*- +from datetime import datetime, date, timedelta +import string +import random +from odoo import models, fields, api + + +class ModelTestManyOne(models.Model): + _name = 'model.test.many.one' + _description = '测试模块' + _inherit = ['mail.thread', 'mail.activity.mixin'] + + name = fields.Char(string='字符串字段', tracking=True) + test_id = fields.Many2one(string='多对一字段', comodel_name='model.test', required=True, ondelete='cascade') + file_id = fields.Many2one(string='多对一文件', comodel_name='ir.attachment', tracking=True) + file_ids = fields.Many2many(string='多对多文件', comodel_name='ir.attachment', relation='file_preview_test_many_one_rel', column1='test_id', column2='test_id_2') + + +class ModelTest(models.Model): + _name = 'model.test' + _description = '测试模块' + _inherit = ['mail.thread', 'mail.activity.mixin'] + + name = fields.Char(string='字符串字段', tracking=True) + int_field = fields.Integer(string='整数', tracking=True) + float_field = fields.Float(string='浮点数', tracking=True) + date_field = fields.Date(string='日期字段', tracking=True) + datetime_field = fields.Datetime(string='日期时间字段', tracking=True) + state = fields.Selection(string='状态', selection=[('draft', '草稿'), ('done', '完成')], default='draft') + file_id = fields.Many2one(string='多对一文件', comodel_name='ir.attachment') + file_ids = fields.Many2many(string='多对多文件', comodel_name='ir.attachment', relation='file_preview_test_rel', column1='test_id', column2='test_id_2') + text = fields.Text(string='长文本测试') + one_to_many_field = fields.One2many(string='one2many字段', comodel_name='model.test.many.one', inverse_name='test_id', tracking=True) + bool_test = fields.Boolean(string='布尔字段') + selection_test = fields.Selection(string='选择字段', selection=[('1', '选项一'), ('2', '选项二')]) + + def action_generate_sample_text(self): + """生成长文本样例""" + sample_text = """ + # 这是Markdown格式的样例文本 + + ## 项目概述 + 这是一个测试项目,用于展示Odoo中各种字段类型的功能和显示效果。 + + ### 功能特点 + 1. 支持多种数据类型 + 2. 自动生成测试数据 + 3. 便于开发和测试 + + ### 技术规格 + - 数据库:PostgreSQL + - 框架:Odoo 14.0+ + - 语言:Python/XML/JavaScript + + --- + **注意**:此内容为自动生成,仅用于测试目的。 + """ + self.write({'text': sample_text}) + + # 辅助方法 + def _generate_random_string(self, length): + """生成随机字符串""" + letters = string.ascii_letters + string.digits + return ''.join(random.choice(letters) for _ in range(length)) + + def _generate_random_date(self): + """生成随机日期(最近365天内)""" + days_ago = random.randint(0, 365) + return date.today() - timedelta(days=days_ago) + + def _generate_random_datetime(self): + """生成随机日期时间(最近30天内)""" + hours_ago = random.randint(0, 720) # 30天*24小时 + return datetime.now() - timedelta(hours=hours_ago) + + # 生成随机数据的方法 + def action_insert_random_data(self): + """生成50条随机测试数据""" + # 预先生成一些附件供选择 + attachment_ids = self.env['ir.attachment'].search([], limit=10) + + for i in range(50): + # 生成随机数据 + random_name = f"测试项目_{i + 1}_{self._generate_random_string(6)}" + random_int = random.randint(1, 1000) + random_float = round(random.uniform(1.0, 10000.0), 2) + random_date = self._generate_random_date() + random_datetime = self._generate_random_datetime() + random_state = random.choice(['draft', 'done']) + random_selection = random.choice(['1', '2']) + random_bool = random.choice([True, False]) + + # 生成随机文本 + random_text = f""" + 这是第{i + 1}条测试记录的长文本内容。 + 生成时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} + 随机ID:{self._generate_random_string(10)} + 描述:这是一条自动生成的测试数据,用于演示各种字段类型的显示效果。 + """ + + # 创建主记录 + test_record = self.create({ + 'name': random_name, + 'int_field': random_int, + 'float_field': random_float, + 'date_field': random_date, + 'datetime_field': random_datetime, + 'state': random_state, + 'bool_test': random_bool, + 'selection_test': random_selection, + 'text': random_text, + }) + + # 随机选择附件 + if attachment_ids: + # 随机选择1-3个附件 + num_attachments = random.randint(1, min(3, len(attachment_ids))) + random_attachments = random.sample(attachment_ids.ids, num_attachments) + test_record.write({ + 'file_id': random.choice(attachment_ids).id if attachment_ids else False, + 'file_ids': [(6, 0, random_attachments)] + }) + + # 返回成功消息 + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'title': '数据生成成功', + 'message': f'已成功生成50条测试数据!', + 'type': 'success', + 'sticky': False, + 'next': {'type': 'ir.actions.act_window_close'}, + } + } + + def action_to_dialog(self): + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'res_model': 'model.test', + 'view_mode': 'form', + 'res_id': self.id, + 'views': [[self.env.ref('model_test.view_file_preview_test_form').id, 'form']], + } + + def cancel(self): + self.state = 'done' + + def test_approval(self): + self.verify_rules(self.cancel) + # print(asd) + + +# class TimeCalculation(models.Model): +# _name = 'example.time.calculation' +# _description = 'Example for Start Time, End Time, Duration' +# +# start_time = fields.Datetime(string='Start Time', required=True) +# end_time = fields.Datetime(string='End Time', required=True) +# duration = fields.Float(string='Duration (hours)', compute='_compute_duration', inverse='_inverse_duration', store=True) +# +# @api.depends('start_time', 'end_time') +# def _compute_duration(self): +# """根据开始时间和结束时间计算时间间隔""" +# for record in self: +# if record.start_time and record.end_time: +# delta = record.end_time - record.start_time +# record.duration = delta.total_seconds() / 3600 # 转换为小时 +# +# @api.depends('start_time', 'duration') +# def _compute_end_time(self): +# """根据开始时间和时间间隔计算结束时间""" +# for record in self: +# if record.start_time and record.duration is not None: +# record.end_time = record.start_time + timedelta(hours=record.duration) +# +# @api.depends('end_time', 'duration') +# def _compute_start_time(self): +# """根据结束时间和时间间隔计算开始时间""" +# for record in self: +# if record.end_time and record.duration is not None: +# record.start_time = record.end_time - timedelta(hours=record.duration) +# +# def _inverse_duration(self): +# """反向更新结束时间""" +# for record in self: +# if record.start_time and record.duration is not None: +# record.end_time = record.start_time + timedelta(hours=record.duration) +# +# @api.onchange('start_time', 'end_time') +# def _onchange_duration(self): +# """确保在界面交互中,时间关系自动更新""" +# for record in self: +# if record.start_time and record.end_time: +# delta = record.end_time - record.start_time +# record.duration = delta.total_seconds() / 3600 +# +# @api.onchange('start_time', 'duration') +# def _onchange_end_time(self): +# """确保在界面交互中,时间关系自动更新""" +# for record in self: +# if record.start_time and record.duration is not None: +# record.end_time = record.start_time + timedelta(hours=record.duration) +# +# @api.onchange('end_time', 'duration') +# def _onchange_start_time(self): +# """确保在界面交互中,时间关系自动更新""" +# for record in self: +# if record.end_time and record.duration is not None: +# record.start_time = record.end_time - timedelta(hours=record.duration) diff --git a/header_search_panel/model_test/security/ir.model.access.csv b/header_search_panel/model_test/security/ir.model.access.csv new file mode 100644 index 000000000..583335d26 --- /dev/null +++ b/header_search_panel/model_test/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_model_test,测试模块,model_model_test,,1,1,1,1 +access_model_test_many_one,测试模块多,model_model_test_many_one,,1,1,1,1 \ No newline at end of file diff --git a/header_search_panel/model_test/static/src/model_test_list.xml b/header_search_panel/model_test/static/src/model_test_list.xml new file mode 100644 index 000000000..c80232c10 --- /dev/null +++ b/header_search_panel/model_test/static/src/model_test_list.xml @@ -0,0 +1,17 @@ + + + + + + + 方案1 + + + 方案2 + + + + + \ No newline at end of file diff --git a/header_search_panel/model_test/static/src/model_test_list_controller.js b/header_search_panel/model_test/static/src/model_test_list_controller.js new file mode 100644 index 000000000..e95ce7236 --- /dev/null +++ b/header_search_panel/model_test/static/src/model_test_list_controller.js @@ -0,0 +1,10 @@ +/** @odoo-module **/ +import { registry } from '@web/core/registry'; +import { listView } from '@web/views/list/list_view'; + + +export const ModelTestListView = { + ...listView, + buttonTemplate: 'ModelTest.buttons' +}; +registry.category('views').add('model_test_list', ModelTestListView); diff --git a/header_search_panel/model_test/views/views.xml b/header_search_panel/model_test/views/views.xml new file mode 100644 index 000000000..c5c99e1c5 --- /dev/null +++ b/header_search_panel/model_test/views/views.xml @@ -0,0 +1,126 @@ + + + + + + model.test + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + model.test.tree + model.test + + + + + + + + + + + + + + + + + + + model.test.form + model.test + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 文件测试 + main + model.test + tree,form + + + + + + + \ No newline at end of file diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.xml b/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.xml deleted file mode 100644 index 707547669..000000000 --- a/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - 近1个月 - 近3个月 - 近半年 - 近1年 - 近2年 - - - -