删除模块,移到efms里面,修改样式
This commit is contained in:
@@ -21,8 +21,8 @@
|
||||
'web.assets_qweb': [
|
||||
],
|
||||
'web.assets_backend': [
|
||||
'jikimo_frontend/static/src/fields/custom_many2many_checkboxes/*',
|
||||
'jikimo_frontend/static/src/fields/Many2OneRadioField/*',
|
||||
# 'jikimo_frontend/static/src/fields/custom_many2many_checkboxes/*',
|
||||
# 'jikimo_frontend/static/src/fields/Many2OneRadioField/*',
|
||||
# 移除odoo相关标识
|
||||
'jikimo_frontend/static/src/bye_odoo/*',
|
||||
'jikimo_frontend/static/src/scss/custom_style.scss',
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
.many2one_radio_field {
|
||||
display: inline-block;
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { RadioField } from "@web/views/fields/radio/radio_field"; // 导入单选按钮组件
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
export class Many2OneRadioField extends RadioField {
|
||||
// 你可以重写或者添加一些方法和属性
|
||||
// 例如,你可以重写setup方法来添加一些事件监听器或者初始化一些变量
|
||||
setup() {
|
||||
super.setup(); // 调用父类的setup方法
|
||||
// 你自己的代码
|
||||
}
|
||||
|
||||
onImageClick(event) {
|
||||
// 放大图片逻辑
|
||||
// 获取图片元素
|
||||
const img = event.target;
|
||||
const close = img.nextSibling;
|
||||
// 实现放大图片逻辑
|
||||
// 比如使用 CSS 放大
|
||||
img.parentElement.classList.add('zoomed');
|
||||
close.classList.add('img_close');
|
||||
}
|
||||
|
||||
onCloseClick(event) {
|
||||
const close = event.target;
|
||||
const img = close.previousSibling;
|
||||
img.parentElement.classList.remove('zoomed');
|
||||
close.classList.remove('img_close');
|
||||
}
|
||||
|
||||
get items() {
|
||||
return Many2OneRadioField.getItems(this.props.name, this.props.record);
|
||||
}
|
||||
|
||||
static getItems(fieldName, record) {
|
||||
switch (record.fields[fieldName].type) {
|
||||
case "selection":
|
||||
return record.fields[fieldName].selection;
|
||||
case "many2one": {
|
||||
const value = record.preloadedData[fieldName] || [];
|
||||
return value.map((item) => [item.id, item.display_name, item.image]);
|
||||
}
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Many2OneRadioField.template = "jikimo_frontend.Many2OneRadioField";
|
||||
// MyCustomWidget.supportedTypes = ['many2many'];
|
||||
|
||||
registry.category("fields").add("many2one_radio", Many2OneRadioField);
|
||||
@@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="jikimo_frontend.Many2OneRadioField" owl="1">
|
||||
<div
|
||||
role="radiogroup"
|
||||
t-attf-class="o_{{ props.orientation }}"
|
||||
t-att-aria-label="string"
|
||||
>
|
||||
<t t-foreach="items" t-as="item" t-key="item[0]">
|
||||
<div class="form-check o_radio_item many2one_radio_field" aria-atomic="true">
|
||||
<input
|
||||
type="radio"
|
||||
class="form-check-input o_radio_input"
|
||||
t-att-checked="item[0] === value"
|
||||
t-att-disabled="props.readonly"
|
||||
t-att-name="id"
|
||||
t-att-data-value="item[0]"
|
||||
t-att-data-index="item_index"
|
||||
t-att-id="`${id}_${item[0]}`"
|
||||
t-on-change="() => this.onChange(item)"
|
||||
/>
|
||||
<label class="form-check-label o_form_label" t-att-for="`${id}_${item[0]}`" t-esc="item[1]" />
|
||||
<div t-on-dblclick="onImageClick">
|
||||
<t>
|
||||
<img t-att-src="item[2]" width="50" height="50"/>
|
||||
<div class="close" t-on-click="onCloseClick">×</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -1,100 +0,0 @@
|
||||
|
||||
.processing-capabilities-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.grid-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
/*控制图片大小*/
|
||||
.item-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
font-size: 12px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.processing-capabilities-grid {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.processing-capabilities-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.processing-capabilities-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
.image-preview-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.image-preview-container.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.image-preview {
|
||||
max-width: 90%;
|
||||
max-height: 90%;
|
||||
object-fit: contain;
|
||||
box-shadow: 0 0 20px rgba(255, 255, 255, 0.2);
|
||||
border-radius: 5px;
|
||||
transform: scale(0.9);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.image-preview-container.show .image-preview {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.image-preview-close {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 30px;
|
||||
color: #fff;
|
||||
font-size: 40px;
|
||||
font-weight: bold;
|
||||
transition: 0.3s;
|
||||
cursor: pointer;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.image-preview-close:hover,
|
||||
.image-preview-close:focus {
|
||||
opacity: 1;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {Many2ManyCheckboxesField} from "@web/views/fields/many2many_checkboxes/many2many_checkboxes_field";
|
||||
import {registry} from "@web/core/registry";
|
||||
|
||||
export class MyCustomWidget extends Many2ManyCheckboxesField {
|
||||
setup() {
|
||||
super.setup();
|
||||
}
|
||||
|
||||
onImageClick(event, src) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// 创建预览框
|
||||
const previewContainer = document.createElement('div');
|
||||
previewContainer.className = 'image-preview-container';
|
||||
|
||||
const previewImg = document.createElement('img');
|
||||
previewImg.src = src;
|
||||
previewImg.className = 'image-preview';
|
||||
// 设置放大的预览图片大小
|
||||
previewImg.style.width = '600px';
|
||||
previewImg.style.height = 'auto'; // 保持宽高比
|
||||
|
||||
const closeButton = document.createElement('span');
|
||||
closeButton.innerHTML = '×';
|
||||
closeButton.className = 'image-preview-close';
|
||||
|
||||
previewContainer.appendChild(previewImg);
|
||||
previewContainer.appendChild(closeButton);
|
||||
document.body.appendChild(previewContainer);
|
||||
|
||||
// 添加关闭预览的事件监听器
|
||||
const closePreview = () => {
|
||||
previewContainer.classList.remove('show');
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(previewContainer);
|
||||
}, 300);
|
||||
};
|
||||
|
||||
closeButton.addEventListener('click', closePreview);
|
||||
|
||||
// 点击预览框外部也可以关闭
|
||||
previewContainer.addEventListener('click', (e) => {
|
||||
if (e.target === previewContainer) {
|
||||
closePreview();
|
||||
}
|
||||
});
|
||||
|
||||
// 使用 setTimeout 来触发过渡效果
|
||||
setTimeout(() => {
|
||||
previewContainer.classList.add('show');
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
|
||||
MyCustomWidget.template = "jikimo_frontend.MyCustomWidget";
|
||||
|
||||
registry.category("fields").add("custom_many2many_checkboxes", MyCustomWidget);
|
||||
@@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="jikimo_frontend.MyCustomWidget" owl="1">
|
||||
<div aria-atomic="true" class="many2many_flex processing-capabilities-grid">
|
||||
<t t-foreach="items" t-as="item" t-key="item[0]">
|
||||
<div class="grid-item">
|
||||
<CheckBox
|
||||
value="isSelected(item)"
|
||||
disabled="props.readonly"
|
||||
onChange="(ev) => this.onChange(item[0], ev)"
|
||||
>
|
||||
<div class="item-content">
|
||||
<img t-att-src="item[2]" class="item-icon" t-on-click="(ev) => this.onImageClick(ev, item[2])"/>
|
||||
<span class="item-label"><t t-esc="item[1]"/></span>
|
||||
</div>
|
||||
</CheckBox>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -9,6 +9,7 @@ function getDomData() {
|
||||
table.hide()
|
||||
const thead = customTable.children('thead')
|
||||
const tbody = customTable.children('tbody')
|
||||
const tfooter = customTable.children('tfoot')
|
||||
const tableData = []
|
||||
const tbody_child = tbody.children()
|
||||
|
||||
@@ -16,30 +17,29 @@ function getDomData() {
|
||||
|
||||
for (let v = 0; v < tbody_child_len; v++) { // 将数据取出来到tableData里面
|
||||
const data = tbody_child[v].innerText.split('\t')
|
||||
// console.log('dom data',data)
|
||||
const [index, deep, name, Φ, value] = data
|
||||
tableData.push({index, deep, name, Φ, value})
|
||||
tableData.push({ index, deep, name, Φ, value })
|
||||
}
|
||||
const ΦList = [...new Set(tableData.map(_ => _.name))] // ΦList去重
|
||||
const ΦList = [...new Set(tableData.map(_ => _.Φ))] // ΦList去重
|
||||
const newTableData = {}
|
||||
tableData.forEach(_ => {
|
||||
const key = _.deep + '|' + _.Φ
|
||||
!newTableData[key] ? newTableData[key] = {i: _.index} : '';
|
||||
const key = _.deep + '|' + _.name
|
||||
!newTableData[key] ? newTableData[key] = { i: _.index } : '';
|
||||
if (_.Φ) { // 去除没有Φ的脏数据
|
||||
newTableData[key]['Φ' + _.Φ] = _.value
|
||||
newTableData[key]['Φ' + _.Φ + 'i'] = _.index
|
||||
}
|
||||
})
|
||||
// console.log('qwdh',tableData, ΦList, newTableData);
|
||||
// console.log(tableData, ΦList, newTableData);
|
||||
|
||||
if (ΦList.filter(_ => _).length == 0) return;
|
||||
handleThead(thead, ΦList)
|
||||
handleThead(thead, ΦList, tfooter)
|
||||
|
||||
handleTbody(tbody, newTableData, ΦList, table)
|
||||
handleTbody(tbody, newTableData, ΦList, table )
|
||||
}
|
||||
|
||||
// 重新设置表头、
|
||||
function handleThead(thead, ΦList) {
|
||||
function handleThead(thead, ΦList, tfooter) {
|
||||
const dom = thead.children().eq(0).children()
|
||||
const len = dom.length
|
||||
dom.eq(0).attr('rowspan', 2)
|
||||
@@ -47,7 +47,11 @@ function handleThead(thead, ΦList) {
|
||||
len == 5 ? dom.eq(2).attr('rowspan', 2) : ''
|
||||
dom.eq(-2).attr('colspan', ΦList.length)
|
||||
dom.eq(-1).remove()
|
||||
|
||||
if(tfooter && tfooter.length) {
|
||||
tfooter.children().each(function () {
|
||||
$(this).children().eq(-1).remove()
|
||||
})
|
||||
}
|
||||
const tr = document.createElement('tr')
|
||||
for (let v = 0; v < ΦList.length; v++) {
|
||||
const th = document.createElement('th')
|
||||
@@ -68,7 +72,6 @@ function handleTbody(tbody, newTableData, ΦList, table) {
|
||||
// b = b.split('=')[1].split('%')[0]
|
||||
// return a - b
|
||||
// })
|
||||
// console.log('wqoqw ',ΦList)
|
||||
data.forEach(_ => {
|
||||
i++
|
||||
const tr = $('<tr class="o_data_row"></tr>')
|
||||
@@ -98,61 +101,6 @@ function handleTbody(tbody, newTableData, ΦList, table) {
|
||||
// // }
|
||||
tbody.append(tr)
|
||||
})
|
||||
// $(document).click(function (e) {
|
||||
// if ($(e.target).attr('coustomTd')) {
|
||||
// const orginV = $('[coustomInput=1]').children('input').val()
|
||||
// $('[coustomInput=1]').parent().html(orginV)
|
||||
// const v = $(e.target).attr('val')
|
||||
// console.log($(e.target));
|
||||
// $(e.target).html('')
|
||||
// const input = $('<div coustomInput="1" name="feed_per_tooth" class="o_field_widget o_field_char"><input class="o_input" type="text" autocomplete="off" maxlength="20"></div>')
|
||||
// input.children('input').val(v)
|
||||
// $(e.target).append(input)
|
||||
// input.children('input').focus()
|
||||
// input.children('input').select()
|
||||
// } else if ($(e.target).attr('coustomInput')) {
|
||||
//
|
||||
// } else {
|
||||
// const orginV = $('[coustomInput=1]').children('input').val()
|
||||
// $('[coustomInput=1]').parent().html(orginV)
|
||||
// const v = $(e.target).attr('val')
|
||||
// }
|
||||
// })
|
||||
// $(document).off('change') // 防止重复绑定
|
||||
// $(document).on('change', '[coustomInput] input', function () {
|
||||
// $(this).parents('td').attr('val', $(this).val());
|
||||
// var eve1 = new Event('change');
|
||||
// var eve2 = new Event('input');
|
||||
// var eve3 = new Event('click');
|
||||
// const i = $(this).parents('td').attr('col');
|
||||
// let patchDom = table.find('tbody').children('tr').eq(i - 1);
|
||||
//
|
||||
// if (patchDom.length === 0) {
|
||||
// console.error('No such row found');
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// patchDom = patchDom.children().eq(-1);
|
||||
//
|
||||
// setTimeout(() => {
|
||||
// if (patchDom.length === 0) {
|
||||
// console.error('No such cell found');
|
||||
// return;
|
||||
// }
|
||||
// patchDom[0].dispatchEvent(eve3); // Simulate click event
|
||||
//
|
||||
// setTimeout(() => {
|
||||
// patchDom = patchDom.find('input');
|
||||
// if (patchDom.length === 0) {
|
||||
// console.error('No input found in the target cell');
|
||||
// return;
|
||||
// }
|
||||
// patchDom.val($(this).val());
|
||||
// patchDom[0].dispatchEvent(eve2);
|
||||
// patchDom[0].dispatchEvent(eve1);
|
||||
// }, 200);
|
||||
// }, 500);
|
||||
// });
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -177,12 +177,12 @@
|
||||
</group>
|
||||
<group string="适配刀片形状"
|
||||
attrs="{'invisible': [('cutting_tool_type', 'in', ('刀柄','夹头','整体式刀具',False))]}">
|
||||
<field name="fit_blade_shape_id" string="" widget="many2one_radio"/>
|
||||
<field name="fit_blade_shape_id" string="" widget="many2one_radio" attrs="{'showExpand': True}"/>
|
||||
</group>
|
||||
<group string="适合加工方式"
|
||||
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
|
||||
<field name="suitable_machining_method_ids" string=""
|
||||
widget="custom_many2many_checkboxes"/>
|
||||
widget="custom_many2many_checkboxes" attrs="{'showExpand': True}"/>
|
||||
</group>
|
||||
<group string="刀尖特征"
|
||||
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<field name="supply_method" attrs="{'invisible': [('state', '=', 'draft')], 'required': [('state', '=', 'supply method')]}" />
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='order_line']/tree/field[@name='model_glb_file']" position="before">
|
||||
<field name="part_number" optional="show"/>
|
||||
<field name="part_number" optional="show" class="section_and_note_text"/>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//header/button[@name='action_cancel']" position="attributes">
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
.purchase_order_list_name {
|
||||
min-width: 62px !important;
|
||||
}
|
||||
|
||||
.section_and_note_text span{
|
||||
white-space: wrap!important;
|
||||
overflow: auto!important;
|
||||
text-overflow: unset!important;
|
||||
}
|
||||
@@ -102,7 +102,7 @@
|
||||
</field>
|
||||
<xpath expr="//field[@name='order_line']/tree/field[@name='name']" position="before">
|
||||
<field name="model_glb_file" widget="Viewer3D" optional="show"
|
||||
string="模型文件" attrs="{'readonly': [('state', 'in', ['draft'])]}"/>
|
||||
string="模型文件" attrs="{'readonly': [('state', 'in', ['draft'])], 'isInList': True}"/>
|
||||
<field name="part_name" optional="hide"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='order_line']/tree/field[@name='price_subtotal']" position="after">
|
||||
@@ -112,6 +112,7 @@
|
||||
<xpath expr="//field[@name='order_line']/tree/field[@name='product_template_id']" position="attributes">
|
||||
<attribute name="options">{'no_create': True}</attribute>
|
||||
<attribute name="context">{'is_sale_order_line': True }</attribute>
|
||||
<attribute name="class">section_and_note_text</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='order_line']" position="attributes">
|
||||
<attribute name="attrs">{'readonly': [('state', 'in', ['cancel','sale'])]}</attribute>
|
||||
|
||||
3
web_widget_model_viewer/static/src/js/3d_viewer.css
Normal file
3
web_widget_model_viewer/static/src/js/3d_viewer.css
Normal file
@@ -0,0 +1,3 @@
|
||||
.model-viewer-in-list {
|
||||
width: 150px;
|
||||
}
|
||||
@@ -63,11 +63,16 @@ StepViewer.supportedTypes = ["binary"];
|
||||
StepViewer.props = {
|
||||
...standardFieldProps,
|
||||
url: {type: String, optional: true},
|
||||
isInList: {type: Boolean, optional: true},
|
||||
};
|
||||
|
||||
StepViewer.extractProps = ({attrs}) => {
|
||||
const modifiedAttrs = JSON.parse(attrs.modifiers || '{}');
|
||||
|
||||
|
||||
return {
|
||||
url: attrs.options.url,
|
||||
isInList: modifiedAttrs.isInList,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
<t t-if="props.value">
|
||||
<model-viewer
|
||||
t-att-class="props.isInList ? 'model-viewer-in-list' : ''"
|
||||
t-att-src='props.url'
|
||||
name="3D model"
|
||||
alt="3D model"
|
||||
|
||||
Reference in New Issue
Block a user