合并企业版代码(未测试,先提交到测试分支)

This commit is contained in:
qihao.gong@jikimo.com
2023-04-14 17:42:23 +08:00
parent 7a7b3d7126
commit d28525526a
1300 changed files with 513579 additions and 5426 deletions

View File

@@ -0,0 +1,60 @@
/** @odoo-module **/
import { useService } from "@web/core/utils/hooks";
const { Component } = owl;
class MenuPopup extends Component {
setup() {
this.rpc = useService('rpc');
this.orm = useService('orm');
this.action = useService('action');
}
get step() {
return this.props.popupData.selectedStepId;
}
get title() {
return this.props.popupData.title;
}
block() {
const options = {
additionalContext: { default_workcenter_id: this.props.popupData.workcenterId },
onClose: this.props.onClose,
};
this.props.onClosePopup('menu');
this.action.doAction('mrp.act_mrp_block_workcenter_wo', options);
}
async callAction(method) {
const action = await this.orm.call(
'mrp.workorder',
method,
[[this.props.popupData.workorderId]]
);
this.props.onClosePopup('menu');
this.action.doAction(action, { onClose: this.props.onClose });
}
cancel() {
this.props.onClosePopup('menu');
}
async proposeChange(changeType, title, message) {
const action = await this.orm.call(
'mrp.workorder',
'action_propose_change',
[[this.props.popupData.workorderId], changeType, title],
);
this.props.onClosePopup('menu');
this.action.doAction(action, { onClose: () => {
this.props.onClose(message);
}});
}
}
MenuPopup.template = 'mrp_workorder.MenuPopup';
export default MenuPopup;

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-name="mrp_workorder.MenuPopup" owl="1">
<div role="dialog" class="popup">
<div class="popup-selection">
<header class="title h3" t-esc="title"/>
<div class="button_list">
<button class="btn btn-danger text-uppercase" t-on-click="block">Block</button>
<button class="btn btn-primary" t-on-click="() => this.callAction('button_scrap')">Scrap</button>
<button class="btn btn-primary" t-on-click="() => this.callAction('action_add_component')">Add Component</button>
<button class="btn btn-primary" t-on-click="() => this.callAction('action_add_byproduct')" name="addByProduct">Add By-product</button>
<span><h2>Suggest a Worksheet improvement</h2></span>
<t t-if="step">
<button class="btn btn-primary" t-on-click="() => this.proposeChange('update_step', 'Update Instructions', 'Your New Instructions feedback was created')">Update Instructions</button>
<button class="btn btn-primary" t-on-click="() => this.proposeChange('remove_step', 'Remove Step', 'Your feedback to delete this step was created')">Delete this Step</button>
</t>
<button class="btn btn-primary" t-on-click="() => this.callAction('action_add_step')">Add a Step</button>
<t t-if="step">
<button class="btn btn-primary" t-on-click="() => this.proposeChange('set_picture', 'Change Picture', '')">Set a New picture</button>
</t>
</div>
</div>
<footer class="footer">
<div class="btn cancel text-uppercase" t-on-click="cancel">Cancel</div>
</footer>
</div>
</t>
</templates>

View File

@@ -0,0 +1,163 @@
$wo-border-separation: $border-width solid $border-color;
.o_tablet_popups {
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 10;
background-color: rgba(60, 60, 60, 0.4);
&.o_unblock {
z-index: 100;
}
.popup {
z-index: 1200;
top: 5%;
left: 25%;
width: 50%;
position: fixed;
background: $o-gray-200;
button{
box-shadow: none;
outline: none;
border: none;
}
button:hover{
background: default;
}
.body.traceback {
height: 238px;
overflow: auto;
font-size: 14px;
white-space: pre-wrap;
text-align: left;
font-family: 'Inconsolata';
-webkit-user-select: text;
-moz-user-select: text;
user-select: text;
}
.centered {
text-align: center;
}
@include media-breakpoint-down(sm) {
top: 0;
left: 0;
width: auto;
max-height: 100%;
overflow: auto;
}
.title {
padding: 20px;
border-bottom: $wo-border-separation;
font-size: large;
color: $o-black;
}
.footer {
border-top: $wo-border-separation;
display: flex;
justify-content: space-around;
}
.btn {
min-width: 40%;
line-height: 80px;
margin: 5px;
font-size: 1.3em;
flex: 1 1;
cursor: pointer;
&:not(.btn-link) {
border-radius: 2px;
}
}
.btn_employee {
background-color: $o-gray-100;
}
.button_list {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
padding: 5px;
span {
margin-top: 1.5em;
width: 100%;
height: 35px;
text-align: center;
align-content: center;
h2 {
color: $o-black;
}
}
}
&.popup-selection .selection {
overflow-y: auto;
max-height: 273px;
font-size: 16px;
width: auto;
line-height: 50px;
&-item {
background: rgb(230,230,230);
cursor: pointer;
text-align: left;
padding: 0px 16px;
color: $o-black;
&:nth-child(odd) {
background: $o-tooltip-title-background-color;
}
&.selected {
background: #6EC89B;
}
}
}
.popup-input {
width: 70%;
margin: 20px auto;
padding: 10px;
box-shadow: 0px 0px 0px 3px #6ec89b;
min-height: 50px;
background: white;
border-radius: 3px;
text-align: center;
font-size: 20px;
font-family: "Lato";
}
.popup-numpad {
width: 70%;
margin: 12px auto;
button {
height: 50px;
width: 50px;
border-radius: 25px;
margin: 4px;
border: 1px solid #cacaca;
background: none;
font-size: large;
}
}
}
.footer > div {
color: $o-black;
}
}

View File

@@ -0,0 +1,29 @@
/** @odoo-module **/
const { Component } = owl;
class StepComponent extends Component {
get stepClass() {
if (this.props.step.id === this.props.selectedStepId) {
return "o_selected o_highlight";
} else if (this.props.step.is_deleted) {
return "o_deleted";
} else {
return "";
}
}
selectStep() {
this.props.onSelectStep(this.props.step.id);
}
get title() {
return this.props.step.title || this.props.step.test_type;
}
}
StepComponent.template = 'mrp_workorder.StepComponent';
StepComponent.props = ["step", "onSelectStep", "selectedStepId"];
export default StepComponent;

View File

@@ -0,0 +1,96 @@
.o_tablet_client_action .o_tablet_timeline {
border-right: 1px solid #c9ccd2;
.o_tablet_step {
flex: 0 0 auto;
border-bottom: 1px solid #c9ccd2;
background: none;
padding: 2rem;
user-select: none;
cursor: pointer;
&:first-child {
margin-top: 0;
}
&_details {
.fa:first-child {
opacity: 0.5;
margin-right: 5px;
}
}
&.o_faulty {
background-color: rgba(map-get($theme-colors, 'danger'), 0.25);
&.o_selected {
box-shadow: inset 0px 0px 0px 3px map-get($theme-colors, 'danger');
}
}
&.o_line_completed {
background: #F5F5F5;
}
&.o_selected {
background-color: #f4a46057;
}
&.o_deleted {
background-color: #a3363657;
text-decoration: line-through;
}
.o_barcode_product_ref {
font-size: 1.1em;
}
.o_line_button {
min-width: 60px;
height: 60px;
padding: 0 8px;
border-radius: 8px;
line-height: 16px;
font-size: 16px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-transform: none;
&.o_shortcut_displayed {
padding-top: 14px;
}
&.btn-secondary {
@include o-hover-opacity();
}
&.o_set {
border: 4px solid $o-brand-primary;
color: $o-brand-primary;
&.o_difference {
color: orange;
border-color: orange;
}
}
}
.o_next_expected {
color: #00A09D;
opacity: 1 !important;
}
.o_step_title {
color: white;
}
.o_tablet_quality_state {
text-align: center;
$color_by_state: ("to_do": "lightgray", "ok": "lightgreen", "fail": "lightcoral");
@each $state, $color in $color_by_state {
.o_tablet_step_#{$state} {
color: #{$color};
}
}
}
}
}

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="mrp_workorder.StepComponent" owl="1">
<div class="list-group-item o_tablet_step" t-att-class="stepClass" t-on-click="selectStep">
<div class="row">
<div class="col-9 o_step_title">
<t t-esc="title"/>
</div>
<div class="col-3 o_tablet_quality_state">
<div t-if="props.step.quality_state == 'pass'" class="o_tablet_step_ok">
<i class="fa fa-check"/>
</div>
<div t-elif="props.step.quality_state == 'fail'" class="o_tablet_step_fail" >
<i class="fa fa-times"/>
</div>
</div>
</div>
</div>
</t>
</templates>

View File

@@ -0,0 +1,32 @@
/** @odoo-module **/
import { useService } from "@web/core/utils/hooks";
const { onMounted, onWillStart, Component } = owl;
class SummaryStep extends Component {
setup() {
this.effect = useService('effect');
this.orm = useService('orm');
onMounted(() => this.makeRainbow());
onWillStart(() => this.getData());
}
async getData() {
this.data = await this.orm.call(
'mrp.workorder',
'get_summary_data',
[this.props.workorder]
);
}
makeRainbow() {
if (this.data.show_rainbow) {
this.effect.add({type: 'rainbow_man', fadeout: "fast"});
}
}
}
SummaryStep.template = 'mrp_workorder.SummaryStep';
SummaryStep.props = ["workorder"];
export default SummaryStep;

View File

@@ -0,0 +1,35 @@
.o_tablet_client_action .o_tablet_summary {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
margin: 4em;
.o_tablet_summary_quality {
display: flex;
flex-direction: row;
align-items: center;
}
h1 {
margin-bottom: 2em;
}
span {
font-size: 1.5em;
margin: 0.5em 0;
}
table {
width: 50%;
th,td {
color: white;
}
}
i {
margin: 0 0.5em;
font-size: 1.5em;
color: yellow;
}
}

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="mrp_workorder.SummaryStep" owl="1">
<div class="o_tablet_summary">
<h1>Good Job!</h1>
<span> Completion Time: <t t-esc="data.duration" t-options='{"widget": "float_time"}'/> minutes</span>
<t t-if="data.position &lt;= 0">
<span>Best Time! Congratulations!</span>
</t>
<t t-elif="data.position &lt;= 4">
<span>Wow, you made the the Top 5!</span>
</t>
<t t-elif="data.position &lt;= 9">
<span>Well done, you're in the Top 10!</span>
</t>
<div class="mt-5 o_tablet_summary_quality">
<span>Quality</span>
<t t-foreach="[...Array(data.quality_score).keys()]" t-key="i" t-as="i">
<i class="fa fa-star"/>
</t>
<t t-foreach="[...Array(3 - data.quality_score).keys()]" t-key="i" t-as="i">
<i class="fa fa-star-o"/>
</t>
</div>
</div>
</t>
</templates>

View File

@@ -0,0 +1,275 @@
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { useBus, useService } from "@web/core/utils/hooks";
import { View } from "@web/views/view";
import DocumentViewer from '@mrp_workorder/components/viewer';
import StepComponent from '@mrp_workorder/components/step';
import ViewsWidgetAdapter from '@mrp_workorder/components/views_widget_adapter';
import MenuPopup from '@mrp_workorder/components/menuPopup';
import SummaryStep from '@mrp_workorder/components/summary_step';
const { EventBus, useState, useEffect, onWillStart, Component, markup} = owl;
/**
* Main Component
* Gather the workorder and its quality check information.
*/
class Tablet extends Component {
//--------------------------------------------------------------------------
// Lifecycle
//--------------------------------------------------------------------------
setup() {
this.rpc = useService('rpc');
this.orm = useService('orm');
this.notification = useService('notification');
this.state = useState({
selectedStepId: 0,
workingState: "",
});
this.popup = useState({
menu: {
isShown: false,
data: {},
}
});
this.workorderId = this.props.action.context.active_id;
this.additionalContext = this.props.action.context;
this.workorderBus = new EventBus();
useBus(this.workorderBus, "refresh", async () => {
await this.getState();
this.render();
});
useBus(this.workorderBus, "workorder_event", (ev) => {
this[ev.detail]();
});
this.barcode = useService('barcode');
useBus(this.barcode.bus, 'barcode_scanned', (event) => this._onBarcodeScanned(event.detail.barcode));
onWillStart(async () => {
await this._onWillStart();
});
useEffect(() => {
this._scrollToHighlighted();
});
}
_scrollToHighlighted() {
let selectedLine = document.querySelector('.o_tablet_timeline .o_tablet_step.o_selected');
if (selectedLine) {
// If a line is selected, checks if this line is entirely visible
// and if it's not, scrolls until the line is.
const headerHeight = document.querySelector('.o_form_view').offsetHeight.height;
const lineRect = selectedLine.getBoundingClientRect();
const page = document.querySelector('.o_tablet_timeline');
// Computes the real header's height (the navbar is present if the page was refreshed).
let scrollCoordY = false;
if (lineRect.top < headerHeight) {
scrollCoordY = lineRect.top - headerHeight + page.scrollTop;
} else if (lineRect.bottom > window.innerHeight) {
const pageRect = page.getBoundingClientRect();
scrollCoordY = page.scrollTop - (pageRect.bottom - lineRect.bottom);
}
if (scrollCoordY !== false) { // Scrolls to the line only if it's not entirely visible.
page.scroll({ left: 0, top: scrollCoordY, behavior: this._scrollBehavior });
this._scrollBehavior = 'smooth';
}
}
}
async getState() {
this.data = await this.orm.call(
'mrp.workorder',
'get_workorder_data',
[this.workorderId],
);
this.viewsId = this.data['views'];
this.steps = this.data['quality.check'];
this.state.workingState = this.data.working_state;
if (this.steps.length && this.steps.every(step => step.quality_state !== 'none')) {
this.createSummaryStep();
} else {
this.state.selectedStepId = this.data['mrp.workorder'].current_quality_check_id;
}
}
createSummaryStep() {
this.steps.push({
id: 0,
title: 'Summary',
test_type: '',
});
this.state.selectedStepId = 0;
}
async selectStep(id) {
await this.saveCurrentStep(id);
}
async saveCurrentStep(newId) {
await new Promise((resolve) =>
this.workorderBus.trigger("force_save_workorder", { resolve })
);
if (this.state.selectedStepId) {
await new Promise((resolve) =>
this.workorderBus.trigger("force_save_check", { resolve })
);
}
await this.orm.write("mrp.workorder", [this.workorderId], {
current_quality_check_id: newId,
});
this.state.selectedStepId = newId;
}
get worksheetData() {
if (this.selectedStep) {
if (this.selectedStep.worksheet_document) {
return {
resModel: 'quality.check',
resId: this.state.selectedStepId,
resField: 'worksheet_document',
value: this.selectedStep.worksheet_document,
page: 1,
};
} else if (this.selectedStep.worksheet_url) {
return {
resModel: "quality.point",
resId: this.selectedStep.point_id,
resField: "worksheet_url",
value: this.selectedStep.worksheet_url,
page: 1,
};
} else if (this.data.operation !== undefined && this.selectedStep.worksheet_page) {
if (this.data.operation.worksheet) {
return {
resModel: "mrp.routing.workcenter",
resId: this.data.operation.id,
resField: "worksheet",
value: this.data.operation.worksheet,
page: this.selectedStep.worksheet_page,
};
} else if (this.data.operation.worksheet_url) {
return {
resModel: "mrp.routing.workcenter",
resId: this.data.operation.id,
resField: "worksheet_url",
value: this.data.operation.worksheet_url,
page: this.selectedStep.worksheet_page,
};
} else {
return false;
}
} else {
return false;
}
} else if (this.data.operation.worksheet) {
return {
resModel: 'mrp.routing.workcenter',
resId: this.data.operation.id,
resField: 'worksheet',
value: this.data.operation.worksheet,
page: 1,
};
} else {
return false;
}
}
get selectedStep() {
return this.state.selectedStepId && this.steps.find(
l => l.id === this.state.selectedStepId
);
}
get views() {
const data = {
workorder: {
type: 'workorder_form',
mode: 'edit',
resModel: 'mrp.workorder',
viewId: this.viewsId.workorder,
resId: this.workorderId,
display: { controlPanel: false },
workorderBus: this.workorderBus,
},
check: {
type: 'workorder_form',
mode: 'edit',
resModel: 'quality.check',
viewId: this.viewsId.check,
resId: this.state.selectedStepId,
display: { controlPanel: false },
workorderBus: this.workorderBus,
},
};
return data;
}
get checkInstruction() {
let note = this.data['mrp.workorder'].operation_note;
if (note && note !== '<p><br></p>') {
return markup(note);
} else {
return undefined;
}
}
get isBlocked() {
return this.state.workingState === 'blocked';
}
showPopup(props, popupId) {
this.popup[popupId].isShown = true;
this.popup[popupId].data = props;
}
closePopup(popupId) {
this.getState();
this.popup[popupId].isShown = false;
}
async onCloseRerender(message) {
if (message) {
this.notification.add(this.env._t(message), {type: 'success'});
}
await this.getState();
this.render();
}
openMenuPopup() {
this.showPopup({
title: 'Menu',
workcenterId: this.data['mrp.workorder'].workcenter_id,
selectedStepId: this.state.selectedStepId,
workorderId: this.workorderId,
}, 'menu');
}
async _onWillStart() {
await this.getState();
}
_onBarcodeScanned(barcode) {
if (barcode.startsWith('O-BTN.') || barcode.startsWith('O-CMD.')) {
// Do nothing. It's already handled by the barcode service.
return;
}
}
}
Tablet.props = ['action', '*'];
Tablet.template = 'mrp_workorder.Tablet';
Tablet.components = {
StepComponent,
DocumentViewer,
ViewsWidgetAdapter,
MenuPopup,
SummaryStep,
View,
};
registry.category('actions').add('tablet_client_action', Tablet);
export default Tablet;

View File

@@ -0,0 +1,227 @@
$o-wo-tablet-padding: $o-horizontal-padding;
$o-wo-tablet-btn-margin: 2px;
$o-wo-tablet-bg: #404040; // emulate the pdf reader
$o-wo-tablet-btn-bg: #505050; // emulate the pdf reader
$o-wo-tablet-bg-dark: #333333;
$o-wo-tablet-border-bg: #c9ccd2;
$o-wo-tablet-text: color-contrast($o-wo-tablet-bg, $body-color, #FFF);
.o_tablet_client_action {
display: flex;
flex-flow: column;
padding: 0;
background-color: $o-wo-tablet-bg;
color: $o-wo-tablet-text;
height: 100%;
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-thumb {
background: $o-wo-tablet-border-bg;
}
::-webkit-scrollbar-track {
background: gray;
}
// font-size: $o-navbar-entry-font-size;
// Components
h1,h2,h3,h4,h5,h6 {
color: $o-wo-tablet-text;
}
.o_workorder_blocking_screen {
height: 100%;
width: 100%;
position: fixed;
background: rgba(0, 0, 0, 0.4);
z-index: 50;
}
.o_view_controller.o_form_view {
color: $o-wo-tablet-text;
background-color: $o-wo-tablet-bg;
background-repeat: repeat;
// background-image: url(/web/static/lib/pdfjs/web/images/texture.png);
border: $o-wo-tablet-bg;
text-transform: capitalize;
min-height: auto;
height: auto!important;
position: unset!important;
.row {
width: 100%;
}
.o_form_nosheet {
padding: 0;
}
.o_workorder_tablet_form {
display: flex;
flex-flow: column;
justify-content: flex-start;
align-items: stretch;
padding: 0 $o-wo-tablet-padding*0.5;
margin-top: 0;
font-size: 1.3em;
&.form_top {
min-height: 100px;
border-bottom: 1px solid $o-wo-tablet-border-bg;
background-color: $o-wo-tablet-bg-dark;
padding-bottom: 0;
}
span.o_tag {
background-color: $o-wo-tablet-bg-dark;
box-shadow: none;
}
.o_unblock {
z-index: 100;
position: relative;
}
.o_workorder_bar_content{
display: flex;
flex-flow: row wrap;
align-items: center;
&.o_workorder_bar_left {
flex: 1 0;
}
&.o_workorder_bar_center {
flex: 0 0 auto;
}
@include media-breakpoint-down(sm) {
justify-content: flex-start;
&.workorder_bar_left {
flex-flow: wrap;
}
}
&.o_workorder_bar_right {
justify-content: flex-end;
flex: 1 0;
align-content: baseline;
@include media-breakpoint-down(sm) {
justify-content: flex-start;
.o_actions {
display: flex;
flex-flow: column;
width: 100%;
}
}
}
.o_workorder_lot {
display: flex;
flex-flow: row;
align-items: center;
margin-left: 1em;
}
}
// First Top Block
.o_workorder_bar {
display: flex;
justify-content: space_around;
flex-flow: row nowrap;
padding: $o-wo-tablet-padding*0.5 0;
@include media-breakpoint-down(sm) {
flex-flow: column;
}
.o_workorder_field {
margin: 0 5px;
}
}
// Last top Block
.o_workorder_actions {
display: flex;
flex-flow: row nowrap;
@include media-breakpoint-down(sm) {
flex-flow: column;
}
padding: $o-wo-tablet-padding*0.5 $o-wo-tablet-padding - $o-wo-tablet-btn-margin $o-wo-tablet-padding;
box-shadow: 0 1px 1px rgba(black, 0.3);
font-size: 1.2em;
}
.o_workorder_left {
display: flex;
flex-flow: row;
height:100%;
}
.btn {
font-size: 1em;
padding: 0.4em 1em;
margin: $o-wo-tablet-btn-margin;
&.btn-secondary {
color: $o-wo-tablet-text;
&:not(.btn-link) {
background-color: $o-wo-tablet-btn-bg;
border: $o-wo-tablet-bg;
text-transform: capitalize;
}
}
}
.o_field_widget {
margin-bottom: 0;
}
}
}
.o_tablet_bottom_content {
display: flex;
overflow: auto;
overflow: hidden;
height: 100%;
.o_tablet_timeline {
height: 100%;
overflow: auto;
flex-basis: 20%;
}
.o_tablet_instructions_content {
flex-basis: 100%;
flex-grow: 1;
overflow: hidden;
display: flex;
flex-flow: column;
padding-left: 0;
&:first-child {
margin-bottom: -24px;
}
.o_tablet_instruction_note {
padding: $o-wo-tablet-padding*0.5;
overflow: auto;
}
.o_tablet_document {
width: 100%;
height: 100%;
overflow: auto;
iframe {
border: none;
width: 100%;
height: 100%;
}
img {
max-width: 75%;
}
}
}
}
.o_input {
background-color: $o-wo-tablet-bg;
color: $o-wo-tablet-text;
padding: 0.4em 1em;
}
.o_form_label {
color: $o-wo-tablet-text;
}
}

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<div t-name="mrp_workorder.Tablet" class="o_content o_tablet_client_action" owl="1">
<div class="o_workorder_blocking_screen" t-if="isBlocked"/>
<View t-props="views['workorder']"/>
<div class="o_tablet_bottom_content">
<div t-if="steps.length" class="o_tablet_timeline" t-on-select-step="selectStep">
<t t-foreach="steps" t-as="step" t-key="step.id">
<StepComponent step="step"
selectedStepId="state.selectedStepId"
onSelectStep.bind="selectStep"/>
</t>
</div>
<div class="o_tablet_instructions_content">
<t t-if="state.selectedStepId">
<View t-props="views['check']" t-key="state.selectedStepId"/>
<div t-if="checkInstruction" class="o_tablet_instruction_note">
<t t-out="checkInstruction"/>
</div>
<t t-if="worksheetData">
<DocumentViewer t-props="worksheetData"/>
</t>
</t>
<t t-elif="steps.length !== 0">
<SummaryStep workorder="workorderId"/>
</t>
</div>
</div>
<div t-if="popup['menu'].isShown" class="o_tablet_popups" >
<MenuPopup popupData="popup['menu'].data" onClosePopup.bind="closePopup" onClose.bind="onCloseRerender"/>
</div>
</div>
</templates>

View File

@@ -0,0 +1,81 @@
/** @odoo-module **/
import { PdfViewerField } from '@web/views/fields/pdf_viewer/pdf_viewer_field';
import { ImageField } from '@web/views/fields/image/image_field';
import { SlidesViewer } from "@mrp/views/fields/google_slides_viewer";
const { Component, useEffect, useRef } = owl;
class DocumentViewer extends Component {
setup() {
this.magicNumbers = {
'JVBER': 'pdf',
'/': 'jpg',
'R': 'gif',
'i': 'png',
'P': 'svg+xml',
};
this.pdfIFrame = useRef('pdf_viewer');
useEffect(() => {
this.updatePdf();
});
}
updatePdf() {
if (this.pdfIFrame.el) {
const iframe = this.pdfIFrame.el.firstElementChild;
iframe.removeAttribute('style');
// Once the PDF viewer is loaded, hides everything except the page.
iframe.addEventListener('load', () => {
iframe.contentDocument.querySelector('.toolbar').style.display = 'none';
iframe.contentDocument.querySelector('body').style.background = 'none';
iframe.contentDocument.querySelector('#viewerContainer').style.boxShadow = 'none';
iframe.contentDocument.querySelector('#mainContainer').style.margin = '-2.5em';
});
}
}
get type() {
if (!this.props || !this.props.value) {
return false;
}
if (this.props.resField === "worksheet_url") {
return "google_slide";
}
for (const [magicNumber, type] of Object.entries(this.magicNumbers)) {
if (this.props.value.startsWith(magicNumber)) {
return type;
}
}
return false;
}
get viewerProps() {
let viewerProps = {
record: {
resModel: this.props.resModel,
resId: this.props.resId,
data: {},
},
name: this.props.resField,
value: this.props.value,
readonly: true,
};
viewerProps['record']['data'][this.props.resField] = this.props.resField;
viewerProps['record']['data'][`$(this.props.resField)_page`] = this.props.page || 1;
if (this.type === 'pdf') {
viewerProps['fileNameField'] = this.props.resField;
}
return viewerProps;
}
}
DocumentViewer.template = 'mrp_workorder.DocumentViewer';
DocumentViewer.props = ["resField", "resModel", "resId", "value", "page"];
DocumentViewer.components = {
PdfViewerField,
ImageField,
SlidesViewer,
};
export default DocumentViewer;

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="mrp_workorder.DocumentViewer" owl="1">
<t t-if="type === 'pdf'">
<div class="o_tablet_document" t-ref="pdf_viewer">
<PdfViewerField t-props="viewerProps"/>
</div>
</t>
<t t-elif="type === 'google_slide'">
<div class="o_tablet_document">
<SlidesViewer t-props="viewerProps"/>
</div>
</t>
<t t-elif="type !== false">
<div class="o_tablet_document">
<ImageField t-props="viewerProps"/>
</div>
</t>
</t>
</templates>

View File

@@ -0,0 +1,28 @@
/** @odoo-module **/
import { ComponentAdapter } from 'web.OwlCompatibility';
export default class ViewsWidgetAdapter extends ComponentAdapter {
setup() {
super.setup(...arguments);
// Overwrite the OWL/legacy env with the WOWL's one.
this.env = owl.Component.env;
}
renderWidget() {
this.widget._render(this.props.data.currentId);
}
get widgetArgs() {
const {model, view, additionalContext, params, mode, view_type, bus} = this.props.data;
return [
model,
view,
additionalContext,
params,
mode,
view_type,
bus
];
}
}