commit
93e30916e9
|
|
@ -456,8 +456,8 @@ class HrPayslip(models.Model):
|
|||
'res_id': payslip.id
|
||||
})
|
||||
# Send email to employees
|
||||
if template:
|
||||
template.send_mail(payslip.id, email_layout_xmlid='mail.mail_notification_light')
|
||||
# if template:
|
||||
# template.send_mail(payslip.id, email_layout_xmlid='mail.mail_notification_light')
|
||||
self.env['ir.attachment'].sudo().create(attachments_vals_list)
|
||||
|
||||
def _filter_out_of_contracts_payslips(self):
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@
|
|||
name="Contracts"
|
||||
action="hr_contract.action_hr_contract"
|
||||
parent="menu_hr_payroll_employees_root"
|
||||
sequence="30"/>
|
||||
sequence="90"/>
|
||||
|
||||
<menuitem
|
||||
id="hr_menu_salary_attachments"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': "Multi Tabs",
|
||||
|
||||
'summary': "Multi Tabs for Odoo 18",
|
||||
|
||||
'description': """
|
||||
Multi Tabs
|
||||
""",
|
||||
|
||||
"author": "1311793927@qq.com",
|
||||
'support': '1311793927qq.com',
|
||||
'images': ['static/description/main_banner.png'],
|
||||
'category': 'General',
|
||||
'version': '0.1',
|
||||
"license": "LGPL-3",
|
||||
'depends': ['base','web'],
|
||||
"installable": True,
|
||||
"auto_install": False,
|
||||
"assets": {
|
||||
"web.assets_backend": [
|
||||
"tabbar/static/src/**/*",
|
||||
],
|
||||
},
|
||||
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 786 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
|
|
@ -0,0 +1,10 @@
|
|||
<section class="oe_container oe_slogan">
|
||||
<span>
|
||||
The basic functions have been implemented, and further optimization will be carried out later</span>
|
||||
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<img src="1.png" style="width:950px">
|
||||
|
||||
</section>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,128 @@
|
|||
import { ActionContainer } from '@web/webclient/actions/action_container';
|
||||
import { patch } from '@web/core/utils/patch';
|
||||
import { AklMultiTab } from './components/multi_tab/akl_multi_tab';
|
||||
|
||||
import { xml, useState } from '@odoo/owl';
|
||||
import { browser } from '@web/core/browser/browser';
|
||||
import { useService } from '@web/core/utils/hooks';
|
||||
import {
|
||||
router as _router,
|
||||
} from '@web/core/browser/router';
|
||||
patch(ActionContainer.prototype, {
|
||||
setup() {
|
||||
|
||||
super.setup();
|
||||
this.action_infos = [];
|
||||
this.controllerStacks = {};
|
||||
// this.action_service = useService('action');
|
||||
|
||||
this.env.bus.addEventListener(
|
||||
'ACTION_MANAGER:UPDATE',
|
||||
({ detail: info }) => {
|
||||
debugger
|
||||
this.action_infos = this.get_controllers(info);
|
||||
this.controllerStacks = info.controllerStacks;
|
||||
this.render();
|
||||
}
|
||||
);
|
||||
},
|
||||
get_controllers(info) {
|
||||
const action_infos = [];
|
||||
const entries = Object.entries(info.controllerStacks);
|
||||
|
||||
entries.forEach(([key, stack]) => {
|
||||
const lastController = stack[stack.length - 1];
|
||||
|
||||
const action_info = {
|
||||
key: key,
|
||||
__info__: lastController,
|
||||
Component: lastController.__info__.Component,
|
||||
active: false,
|
||||
componentProps: lastController.__info__.componentProps || {},
|
||||
}
|
||||
|
||||
if (lastController.count == info.count) {
|
||||
action_info.active = true;
|
||||
}
|
||||
action_infos.push(action_info);
|
||||
})
|
||||
|
||||
|
||||
return action_infos;
|
||||
},
|
||||
|
||||
_on_close_action(action_info) {
|
||||
this.action_infos = this.action_infos.filter((info) => {
|
||||
return info.key !== action_info.key;
|
||||
});
|
||||
if (this.action_infos.length > 0) {
|
||||
|
||||
delete this.controllerStacks[action_info.key];
|
||||
this.action_infos[this.action_infos.length - 1].active = true; // Set last
|
||||
this.render();
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
_on_active_action(action_info) {
|
||||
debugger
|
||||
this.action_infos.forEach((info) => {
|
||||
info.active = info.key === action_info.key;
|
||||
});
|
||||
const url = _router.stateToUrl(action_info.__info__.state)
|
||||
browser.history.pushState({}, "", url);
|
||||
this.render();
|
||||
},
|
||||
_close_other_action() {
|
||||
this.action_infos = this.action_infos.filter((info) => {
|
||||
if (info.active == false) {
|
||||
delete this.controllerStacks[info.key];
|
||||
}
|
||||
return info.active == true
|
||||
});
|
||||
|
||||
this.render();
|
||||
},
|
||||
_close_current_action() {
|
||||
debugger
|
||||
this.action_infos = this.action_infos.filter((info) => {
|
||||
if (info.active == true) {
|
||||
delete this.controllerStacks[info.key];
|
||||
}
|
||||
return info.active == false
|
||||
});
|
||||
this.action_infos[this.action_infos.length - 1].active = true;
|
||||
this.render();
|
||||
},
|
||||
_on_close_all_action() {
|
||||
debugger
|
||||
this.action_infos.forEach((info) => {
|
||||
delete this.controllerStacks[info.key];
|
||||
});
|
||||
this.action_infos = {}
|
||||
window.location.href = "/";
|
||||
|
||||
}
|
||||
});
|
||||
ActionContainer.components = {
|
||||
...ActionContainer.components,
|
||||
AklMultiTab,
|
||||
};
|
||||
ActionContainer.template = xml`
|
||||
<t t-name="web.ActionContainer">
|
||||
<t t-set="action_infos" t-value="action_infos" />
|
||||
<div class="o_action_manager d-flex flex-colum">
|
||||
<AklMultiTab
|
||||
action_infos="action_infos"
|
||||
active_action="(action_info) => this._on_active_action(action_info)"
|
||||
close_action="(action_info) => this._on_close_action(action_info)"
|
||||
close_current_action="() => this._close_current_action()"
|
||||
close_other_action="() => this._close_other_action()"
|
||||
close_all_action="() => this._on_close_all_action()"
|
||||
/>
|
||||
<div t-foreach="action_infos" t-as="action_info" t-if="action_info" t-key="action_info.key" class="akl_controller_container d-flex flex-column" t-att-class="action_info.active ? '' : 'd-none'" >
|
||||
<t t-component="action_info.Component" className="'o_action'" t-props="action_info.componentProps" />
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
`;
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
.akl_controller_container {
|
||||
overflow-y: hidden;
|
||||
flex: 1 1 auto;
|
||||
|
||||
.o_view_controller {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
|
||||
.o_content {
|
||||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.o_action {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: white;
|
||||
.o_content {
|
||||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
background-color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import { Component, useRef } from '@odoo/owl';
|
||||
import { Dropdown } from '@web/core/dropdown/dropdown';
|
||||
import { DropdownItem } from '@web/core/dropdown/dropdown_item';
|
||||
import { DropdownGroup } from '@web/core/dropdown/dropdown_group';
|
||||
export class AklMultiTab extends Component {
|
||||
static template = 'akl_multi_tab.tab';
|
||||
static components = { Dropdown, DropdownItem, DropdownGroup };
|
||||
static props = ['*'];
|
||||
setup() {
|
||||
super.setup();
|
||||
this.tabContainerRef = [];
|
||||
}
|
||||
rollPage() { }
|
||||
_close_all_action() { this.props.close_all_action(); }
|
||||
_close_current_action() {
|
||||
this.props.close_current_action();
|
||||
}
|
||||
_close_other_action() {
|
||||
this.props.close_other_action();
|
||||
}
|
||||
_on_click_tab_close(info) {
|
||||
this.props.close_action(info);
|
||||
}
|
||||
_on_click_tab_item(info) {
|
||||
this.props.active_action(info);
|
||||
}
|
||||
_on_multi_tab_next() { }
|
||||
_on_multi_tab_prev() { }
|
||||
get action_infos() { }
|
||||
get current_action_info() { }
|
||||
}
|
||||
|
|
@ -0,0 +1,295 @@
|
|||
.akl_multi_tab_container {
|
||||
line-height: 40px;
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
box-sizing: border-box;
|
||||
z-index: 3;
|
||||
|
||||
.akl_multi_tab {
|
||||
position: relative;
|
||||
padding: 0 80px 0 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
box-sizing: border-box;
|
||||
|
||||
.akl_tab_scroll {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.akl_page_items {
|
||||
position: relative;
|
||||
left: 0px;
|
||||
height: 100%;
|
||||
flex: 1 1 auto;
|
||||
background: rgb(255, 255, 255);
|
||||
overflow: visible;
|
||||
white-space: nowrap;
|
||||
padding: 0px;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
*zoom: 1;
|
||||
|
||||
vertical-align: middle;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s;
|
||||
-webkit-transition: all 0.2s;
|
||||
min-width: 65px;
|
||||
padding: 0 15px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
|
||||
line-height: 40px;
|
||||
max-width: 160px;
|
||||
text-overflow: ellipsis;
|
||||
padding-right: 40px;
|
||||
overflow: hidden;
|
||||
border-right: 1px solid #f6f6f6;
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
padding: 0px 30px 0px 10px;
|
||||
user-select: none;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 0;
|
||||
height: 2px;
|
||||
border-radius: 0;
|
||||
background-color: #292b34;
|
||||
transition: all 0.3s;
|
||||
-webkit-transition: all 0.3s;
|
||||
}
|
||||
|
||||
&.akl_multi_tab_active {
|
||||
&::after {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f6f6f6;
|
||||
|
||||
&::after {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.akl_tab_close {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
margin: -8px 0 0 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
border-radius: 50%;
|
||||
font-size: 12px;
|
||||
|
||||
&:first {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #ff5722;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
svg {
|
||||
margin-top: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: $o-brand-primary;
|
||||
color:white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.akl_tab_control {
|
||||
width: 40px;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
-webkit-transition: all 0.3s;
|
||||
box-sizing: border-box;
|
||||
border-left: 1px solid #f6f6f6;
|
||||
|
||||
&:hover {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
}
|
||||
|
||||
.akl_icon_prev {
|
||||
border-right: 1px solid #f6f6f6;
|
||||
color: #666;
|
||||
line-height: 40px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-transition: all 0.3s;
|
||||
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
position: absolute;
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 40px;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
border-left: none;
|
||||
border-right: 1px solid #f6f6f6;
|
||||
}
|
||||
|
||||
.akl_icon_next {
|
||||
border-right: 1px solid #f6f6f6;
|
||||
color: #666;
|
||||
line-height: 40px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
font-family: layui-icon !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 40px;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
-webkit-transition: all 0.3s;
|
||||
box-sizing: border-box;
|
||||
border-left: 1px solid #f6f6f6;
|
||||
right: 40px;
|
||||
}
|
||||
|
||||
.akl_icon_down {
|
||||
//right: 1px;
|
||||
color: #666;
|
||||
line-height: 40px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
-webkit-transition: all 0.3s;
|
||||
font-family: layui-icon !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 40px;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
border-left: 1px solid #f6f6f6;
|
||||
right: 0;
|
||||
|
||||
.dropdown-toggle {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
.dropdown-item {
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.akl_multi_tab_active {
|
||||
background-color: #f6f6f6;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
border-radius: 0;
|
||||
background-color: #292b34;
|
||||
transition: all 0.3s;
|
||||
-webkit-transition: all 0.3s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
z-index: 1000;
|
||||
}
|
||||
}
|
||||
|
||||
.o_action_manager {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.akl_tab_page_container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
flex: 1 1 auto;
|
||||
|
||||
// > div {
|
||||
// flex: 1 1 auto;
|
||||
// overflow: auto;
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
// }
|
||||
|
||||
.o_view_controller {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
|
||||
.o_content {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.o_home_menu_background {
|
||||
.akl_multi_tab_container {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
[name='product_template_id'] {
|
||||
div,
|
||||
span {
|
||||
width: 100% !important;
|
||||
display: block !important;
|
||||
}
|
||||
span {
|
||||
white-space: pre-wrap !important;
|
||||
word-break: break-all !important;
|
||||
text-overflow: initial !important;
|
||||
}
|
||||
}
|
||||
|
||||
td[name='product_id'] {
|
||||
white-space: pre-wrap !important;
|
||||
word-break: break-all !important;
|
||||
text-overflow: initial !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template"
|
||||
xml:space="preserve">
|
||||
|
||||
<t t-name="akl_multi_tab.tab"
|
||||
owl="1">
|
||||
<div class="akl_multi_tab_container">
|
||||
<div class="akl_multi_tab">
|
||||
<div class="akl_tab_control akl_icon_prev"
|
||||
t-on-click.stop="_on_multi_tab_prev">
|
||||
<svg viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20">
|
||||
<path d="M269.25 512l271.53-271.53a32 32 0 0 0-45.25-45.25L201.37 489.38a32 32 0 0 0 0 45.25l294.16 294.16a32 32 0 0 0 45.25-45.25z"
|
||||
fill="#adb5bd"/>
|
||||
<path d="M551.1 512l271.53-271.53a32 32 0 0 0-45.26-45.25L483.22 489.38a32 32 0 0 0 0 45.25l294.15 294.16a32 32 0 1 0 45.26-45.25z"
|
||||
fill="#adb5bd"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="akl_tab_control akl_icon_next"
|
||||
t-on-click.stop="_on_multi_tab_next">
|
||||
<svg viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20">
|
||||
<path d="M483.22 240.47L754.73 512 483.22 783.53a32 32 0 0 0 45.25 45.25l294.15-294.15a32 32 0 0 0 0-45.26L528.47 195.22a32 32 0 0 0-45.25 45.25z"
|
||||
fill="#adb5bd"/>
|
||||
<path d="M540.78 534.63a32 32 0 0 0 0-45.26L246.63 195.22a32 32 0 0 0-45.25 45.25L472.89 512 201.38 783.53a32 32 0 0 0 45.25 45.25z"
|
||||
fill="#adb5bd"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class='akl_tab_control akl_icon_down'>
|
||||
<DropdownGroup >
|
||||
<Dropdown >
|
||||
|
||||
<svg viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20">
|
||||
<path fill="#adb5bd"
|
||||
d="M830.24 340.688l11.328 11.312a16 16 0 0 1 0 22.624L530.448 685.76a16 16 0 0 1-22.64 0L196.688 374.624a16 16 0 0 1 0-22.624l11.312-11.312a16 16 0 0 1 22.624 0l288.496 288.496 288.512-288.496a16 16 0 0 1 22.624 0z"/>
|
||||
</svg>
|
||||
|
||||
<t t-set-slot="content">
|
||||
<DropdownItem class="'akl_close_cur_tab'"
|
||||
onSelected="() => this._close_current_action()">
|
||||
Close Current Tab
|
||||
</DropdownItem>
|
||||
<DropdownItem class="'akl_close_other_tabs'"
|
||||
onSelected="() => this._close_other_action()">
|
||||
Close Other Tabs
|
||||
</DropdownItem>
|
||||
<DropdownItem class="'akl_close_all_tabs'"
|
||||
onSelected="() => this._close_all_action()">
|
||||
Close All Tabs
|
||||
</DropdownItem>
|
||||
</t>
|
||||
</Dropdown>
|
||||
</DropdownGroup>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="akl_tab_scroll">
|
||||
<ul class="akl_page_items tab_container"
|
||||
t-ref="tab_container">
|
||||
<li t-foreach="props.action_infos"
|
||||
t-as="action_info"
|
||||
t-if="action_info.key"
|
||||
t-key="action_info.key"
|
||||
t-att-class="{'active': action_info.active, 'akl_page_tab_item': true}"
|
||||
t-on-click.stop="() => this._on_click_tab_item(action_info)">
|
||||
<span>
|
||||
<t t-esc="action_info.key" />
|
||||
</span>
|
||||
|
||||
<span class="akl_tab_close"
|
||||
t-on-click.stop="() => this._on_click_tab_close(action_info)">
|
||||
<svg viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="14"
|
||||
height="14">
|
||||
<path d="M556.8 512l265.6-265.6c12.8-12.8 12.8-32 0-44.8s-32-12.8-44.8 0L512 467.2 246.4 201.6c-12.8-12.8-32-12.8-44.8 0s-12.8 32 0 44.8l265.6 265.6-265.6 265.6c-12.8 12.8-12.8 32 0 44.8 6.4 6.4 12.8 9.6 22.4 9.6s16-3.2 22.4-9.6l265.6-265.6 265.6 265.6c6.4 6.4 16 9.6 22.4 9.6s16-3.2 22.4-9.6c12.8-12.8 12.8-32 0-44.8L556.8 512z"
|
||||
fill="#bfbfbf"/>
|
||||
</svg>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
.tabbar {
|
||||
height: 30px;
|
||||
margin-left: 0px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
.tabbar_left {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
flex: 1 1 0%;
|
||||
height: 100%;
|
||||
}
|
||||
.tabbar_right {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue