【odoo17】【odoo前端开发】在报表上方增加搜索视图

在报表上方增加搜索视图

先看效果

在这里插入图片描述

1.场景描述

如果不在报表的上方增加搜索视图,通常的做法是将搜索条件用一个TransientModel来完成,如下图,这种方式也是也是可行的,不过想要改变筛选条件时,要重新点开筛选框,比较麻烦。
在这里插入图片描述

2.实现过程

2.1.view视图

<record id="action_asset_liabilities_report" model="ir.actions.client">
    <field name="name">资产负债表</field>
    <field name="res_model">asset.liabilities.report</field>
    <field name="tag">asset_liabilities_report</field>
</record>

<menuitem sequence="40" name='资产负债表'
	   id='menu_account_financial_report_tree'
	   parent="jcerp_account.menu_account_report_config"
	   action='action_account_financial_report_tree'/>

2.2.asset_liabilities_report的前端逻辑

在这里插入图片描述
这里的搜索逻辑与odoo原生的搜索逻辑类似,可以参考odoo源代码:@web/search/search_bar/search_bar。

2.3.report_search_bar设计

两个方面,
1、从report中传入搜索字段
2、是onClickSearch,最后点击搜索时,根据筛选条件获取报表数据。

export class ReportSearchBar extends Component {
}
ReportSearchBar.template = "jcerp_control_panel.ReportSearchBar";
ReportSearchBar.components = {
    DateBetweenValue,
    SearchAutoComplete,
    Dropdown,
    DropdownItem
}
ReportSearchBar.props = {
    items: { type: Object },
    onClickSearch: Function,
};
ReportSearchBar.defaultProps = {
    autofocus: true,
};
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
    <t t-name="jcerp_control_panel.ReportSearchBar.Input">
        <div style="align-items: flex-start;display: grid;grid-template-columns: repeat(4, 1fr);grid-gap: 10px;">
            <t t-foreach="items" t-as="item" t-key="item.id">
                <div class="filter-item px-3 fs-5">
                    <label class="filter-item-label" t-att-for="item.description"><t t-esc="item.description"/></label>
                    <t t-if="item.fieldType === 'date' or item.fieldType === 'datetime'">
                        <div class="filter-no-border-input">
                            <DateBetweenValue onFromToChanged="(value) => this.onDateInput(item, value)"/>
                        </div>
                    </t>
                    <t t-elif="['many2one', 'selection', 'boolean', 'tags'].includes(item.fieldType)">
                        <SearchAutoComplete t-props="getAutoCompleteProps(item)"/>
                    </t>
                    <t t-else="">
                        <input type="text"
                            role="searchbox"
                            class="filter-border-input"
                            t-att-id="item.id"
                            t-on-keydown="onSearchKeydown"
                            t-on-input="onSearchInput"
                        />
                    </t>
                </div>
            </t>
            <div class="filter-item px-3 fs-5">
                <button type="button" class="btn btn-secondary" t-on-click="onClickClear" style="margin-right: 10px;width: 60px;">重置</button>
                <button type="button" class="btn btn-primary" t-on-click="onClickSearch" style="margin-right: 10px;width: 60px;">搜索</button>
                <button type="button" class="btn btn-primary" t-on-click="(ev) => this.export_file_xlsx(ev)" style="width: 60px;">导出</button>
                <Dropdown togglerClass="'btn btn-primary'" showCaret="true" class="'btn-group'">
                    <DropdownItem>
                        <button
                                class="btn btn-link text-nowrap"
                                t-on-click="(ev) => this.export_file_xlsx(ev)"
                        >
                        导出(xlsx)
                        </button>
                    </DropdownItem>
                    <DropdownItem>
                        <button class="btn btn-link text-nowrap" t-on-click="(ev) => this.export_file_pdf(ev)">
                        导出(pdf)
                        </button>
                    </DropdownItem>
                </Dropdown>
            </div>
        </div>
    </t>

    <t t-name="jcerp_control_panel.ReportSearchBar">
        <t t-call="jcerp_control_panel.ReportSearchBar.Input"/>
    </t>

</templates>

2.4.SearchAutoComplete实现

SearchAutoComplete的实现参考odoo的源代码:@web/core/autocomplete/autocomplete
在这里插入图片描述
1、选择结果,多选时只显示第一项
2、删除单项选择结果
3、多选时显示,单选时不显示
4、输入框,模糊匹配
5、删除全部选择结果
6、可选列表

2.4.1.template代码

<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">

    <t t-name="web.SearchAutoComplete">
        <div class="search-input-container filter-border-input" t-ref="search-input-container">
            <t t-if="selectedOption">
                <div class="left-side">
                    <span class="search-option-label"><t t-esc="selectedOption ? selectedOption.label : ''"/></span>
                    <i class="fa fa-times-circle search-option-close" style="margin: 5px 0;"
                       t-ref="close_one"
                       t-on-click="() => this.closeSearchOption()"/>
                </div>
                <t t-if="effectiveCount > 1">
                    <span class="search-option-count">
                        +<t t-esc="effectiveCount"/>
                    </span>
                </t>
            </t>
            <input
                type="text"
                t-att-id="props.id"
                class="filter-border-input"
                autocomplete="off"
                t-att-placeholder="props.placeholder"
                t-model="state.value"
                t-on-blur="onInputBlur"
                t-on-click.stop="onInputClick"
                t-on-change="onInputChange"
                t-on-input="onInput"
                t-on-keydown="onInputKeydown"
                t-on-focus="onInputFocus"
                t-ref="input"
            />
            <t t-if="selectedOption">
                <i class="fa fa-times-circle search-input-close" style="margin: 5px 0;"
                   t-ref="close_all"
                   t-on-click="() => this.closeAllSearchOption()"/>
            </t>
        </div>
        <button
            type="button"
            t-att-id="props.id"
            disabled="true"
            class="btn btn-search o-no-caret rounded-start-0 h-100"
            t-on-click="onInput"
        >
            <i class="fa fa-caret-down" aria-hidden="true" data-hotkey="shift+q" title="Toggle Search Panel"/>
        </button>
        <t t-if="displayOptions">
            <div class="o-autocomplete--dropdown-menu ui-widget show"
                t-att-class="ulDropdownClass"
                t-on-mousedown.prevent="" t-ref="sourcesList">
                <t t-foreach="sources" t-as="source" t-key="source.id">
                    <t t-if="source.isLoading">
                        <li class="ui-menu-item"
                            t-att-class="{
                                'o-autocomplete--dropdown-item': props.dropdown,
                                'd-block': !props.dropdown
                            }">
                            <a href="#" class="dropdown-item ui-menu-item-wrapper">
                                <i class="fa fa-spin fa-circle-o-notch" /> <t t-esc="source.placeholder" />
                            </a>
                        </li>
                    </t>
                    <t t-else="">
                        <t t-foreach="source.options" t-as="option" t-key="option.id">
                            <li
                                class="o-autocomplete--dropdown-item ui-menu-item d-block"
                                t-att-class="option.classList"
                                t-on-mouseenter="() => this.onOptionMouseEnter([source_index, option_index])"
                                t-on-mouseleave="() => this.onOptionMouseLeave([source_index, option_index])"
                                t-on-click="() => this.onOptionClick([source_index, option_index])"
                            >
                                <div class="d-flex">
                                    <a
                                        href="#"
                                        class="dropdown-item ui-menu-item-wrapper text-truncate"
                                        t-att-style="option.checked ? 'color: #3D7EFF;' : ''"
                                        t-att-class="{ 'ui-state-active': isActiveSourceOption(option)}"
                                    >
                                        <t t-if="source.optionTemplate">
                                            <t t-call="{{ source.optionTemplate }}" />
                                        </t>
                                        <t t-else="">
                                            <t t-esc="option.label" />
                                        </t>
                                    </a>
                                    <t t-if="option.checked">
                                        <i class="fa fa-check" style="color: #3D7EFF;margin: 5px 5px;"/>
                                    </t>
                                </div>
                            </li>
                        </t>
                    </t>
                </t>
            </div>
        </t>
    </t>

</templates>

2.4.2.js代码

前端代码和odoo源代码类似,这里贴一部分关键代码。
1、selectOption

selectOption(option, params = {}) {
    this.inEdition = false;
    if (option.unselectable) {
        this.inputRef.el.value = "";
        return;
    }
    option.checked = !option.checked
    if (option.value === 0) {
        this.state.selectedOptions = [];
        this.sources.forEach(source => {
            source.options.forEach(source_option => {
                source_option.checked = option.checked
            })
        })
        if (option.checked) {
            this.sources.forEach(source => {
                source.options.forEach(source_option => {
                    this.state.selectedOptions.push(source_option)
                })
            })
            this.props.onSelect(this.state.selectedOption, {
                ...params,
                input: this.inputRef.el,
            });
        } else {
            this.inputRef.el.value = "";
        }
        return;
    }
    const allOption = this.sources[0].options[0]
    if (option.checked) {
        this.state.selectedOptions.push(option)
        if (this.state.selectedOptions.length >= this.optionCount - 1) {
            allOption.checked = true;
            this.state.selectedOptions.push(allOption)
        }
    } else {
        allOption.checked = false;
        this.state.selectedOptions = this.state.selectedOptions.filter(
            item => item.value !== option.value && item.value !== 0
        )
    }
    if (this.props.resetOnSelect) {
        this.inputRef.el.value = "";
    }
    this.forceValFromProp = true;
    this.props.onSelect(this.state.selectedOption, {
        ...params,
        input: this.inputRef.el,
    });
}

async loadSources(useInput) {
   this.sources = [];
   this.state.activeSourceOption = null;
   const proms = [];
   for (const pSource of this.props.sources) {
       const source = this.makeSource(pSource);
       this.sources.push(source);

       const options = this.loadOptions(
           pSource.options,
           useInput ? this.inputRef.el.value.trim() : ""
       );
       if (options instanceof Promise) {
           source.isLoading = true;
           const prom = options.then((options) => {
               source.options = options.map((option) => this.makeOption(option));
               source.isLoading = false;
               this.state.optionsRev++;
           });
           proms.push(prom);
       } else {
           source.options = options.map((option) => this.makeOption(option));
       }
   }
   await Promise.all(proms);
   this.navigate(0);
   this.sources.forEach(source => {
       source.options.forEach(option => {
           // 如果找到了匹配的id,则设置checked为true
           if (this.state.selectedOptions.some(item => item.value === option.value)) {
               option.checked = true;
           }
       });
   })
}

2.5.后端代码实现

这里主要时ReportSearchBar的可选项items的实现,这里没有解析odoo的搜索视图,因为在报表中可能会有一些与模型无关的选择项。因此在搜索选项以及根据搜索条件查询搜索结果、打印结果都由每个报表自定义实现。
在这里插入图片描述

@api.model
def get_search_items(self):
    """
    获取搜索条件
    """
    return [
        {
            'name': 'move_id',
            'fieldType': 'many2one',
            'res_model': 'account.move',
            'label': '',
            'operator': 'ilike',
            'description': '凭证编号',
            'value': '',
        },
        {
            'name': 'ref',
            'fieldType': 'char',
            'label': '',
            'operator': 'ilike',
            'description': '参考',
            'value': '',
        },
        {
            'name': 'invoice_date',
            'fieldType': 'date',
            'label': '',
            'operator': '=',
            'description': '发票/账单日期',
            'value': '',
        },
        {
            'name': 'date',
            'fieldType': 'date',
            'label': '',
            'operator': '=',
            'description': '入账日期',
            'value': '2000-01-01',
        },
        {
            'name': 'partner_id',
            'fieldType': 'many2one',
            'res_model': 'res.partner',
            'label': '',
            'operator': 'ilike',
            'description': '合作伙伴',
            'domain': [],
            'value': '',
        },
    ]

3.总结

此次开发过程都是利用odoo原生的代码及组件实现,会涉及到较多时间的样式、事件调整及编写,后续会用element组件库来改写

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值