Tomei referência para produto configurável e segui as etapas abaixo. Aqui pode haver algum código que não é utilizável.
1) Criar app/code/Namespace/Modulename/etc/adminhtml/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<virtualType name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Pool">
<arguments>
<argument name="modifiers" xsi:type="array">
<item name="modulename" xsi:type="array">
<item name="class" xsi:type="string">Namespace\Modulename\Ui\DataProvider\Product\Modifier\Customtab</item>
<item name="sortOrder" xsi:type="number">200</item>
</item>
</argument>
</arguments>
</virtualType>
<type name="Namespace\Modulename\Ui\DataProvider\Product\Modifier\Customtab">
<arguments>
<argument name="formName" xsi:type="string">product_form</argument>
<argument name="dataScopeName" xsi:type="string">product_form.product_form</argument>
<argument name="dataSourceName" xsi:type="string">product_form.product_form_data_source</argument>
</arguments>
</type>
</config>
2) Criar app/code/Namespace/Modulename/Ui/DataProvider/Product/Modifier/Customtab.php
<?php
namespace Namespace\Modulename\Ui\DataProvider\Product\Modifier;
use Magento\Catalog\Model\Locator\LocatorInterface;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Store\Api\WebsiteRepositoryInterface;
use Magento\Store\Api\GroupRepositoryInterface;
use Magento\Store\Api\StoreRepositoryInterface;
use Magento\Ui\Component\Form;
use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier;
class Customtab extends AbstractModifier
{
const SORT_ORDER = 40;
protected $locator;
protected $websiteRepository;
protected $groupRepository;
protected $storeRepository;
protected $websitesOptionsList;
protected $storeManager;
protected $websitesList;
private $dataScopeName;
public function __construct(
LocatorInterface $locator,
StoreManagerInterface $storeManager,
WebsiteRepositoryInterface $websiteRepository,
GroupRepositoryInterface $groupRepository,
StoreRepositoryInterface $storeRepository,
$dataScopeName
) {
$this->locator = $locator;
$this->storeManager = $storeManager;
$this->websiteRepository = $websiteRepository;
$this->groupRepository = $groupRepository;
$this->storeRepository = $storeRepository;
$this->dataScopeName = $dataScopeName;
}
public function modifyData(array $data)
{
return $data;
}
public function modifyMeta(array $meta)
{
if (!$this->storeManager->isSingleStoreMode()) {
$meta = array_replace_recursive(
$meta,
[
'tabname' => [
'arguments' => [
'data' => [
'config' => [
'additionalClasses' => 'admin__fieldset-product-customclass',
'label' => __('Your Label'),
'collapsible' => true,
'componentType' => Form\Fieldset::NAME,
'sortOrder' => $this->getNextGroupSortOrder(
$meta,
'search-engine-optimization',
self::SORT_ORDER
),
],
],
],
'children' => $this->getPanelChildren(),
],
]
);
}
return $meta;
}
protected function getPanelChildren()
{
return [
'tabname_products_button_set' => $this->getButtonSet()
];
}
protected function getButtonSet()
{
return [
'arguments' => [
'data' => [
'config' => [
'component' => 'Namespace_Modulename/js/components/container-tabname-handler',
'formElement' => 'container',
'componentType' => 'container',
'label' => false,
'content1' => __(
'Add some content'
),
'template' => 'ui/form/components/complex',
'createTabButton' => 'ns = ${ $.ns }, index = create_tabname_products_button',
],
],
],
'children' => [
'create_tabname_products_button' => [
'arguments' => [
'data' => [
'config' => [
'formElement' => 'container',
'componentType' => 'container',
'component' => 'Magento_Ui/js/form/components/button',
'actions' => [
[
'targetName' => $this->dataScopeName.'.customModal',
'actionName' => 'trigger',
'params' => ['active', true],
],
[
'targetName' => $this->dataScopeName.'.customModal',
'actionName' => 'openModal',
],
],
'title' => __('Your Title'),
'sortOrder' => 20,
],
],
],
],
],
];
}
}
3) Criar app/code/Namespace/Modulename/view/adminhtml/layout/catalog_product_new.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock name="product_form">
<block name="product.form.modulename.matrix" class="Magento\ConfigurableProduct\Block\Adminhtml\Product\Edit\Tab\Variations\Config\Matrix" template="Namespace_Modulename::catalog/product/edit/tab/custom.phtml" as="product_custom">
<arguments>
<argument name="config" xsi:type="array">
<item name="collapsible" xsi:type="boolean">false</item>
<item name="label" xsi:type="string" translate="true" />
<item name="sortOrder" xsi:type="string">1000</item>
<item name="canShow" xsi:type="boolean">true</item>
<item name="componentType" xsi:type="string">fieldset</item>
<item name="provider" xsi:type="string">product_form.product_form_data_source</item>
<item name="form" xsi:type="string">product_form.product_form</item>
<item name="modal" xsi:type="string">customModal</item>
<item name="nameStepWizard" xsi:type="string">variation-steps-wizard</item>
<item name="dataScope" xsi:type="string">productFormCustommodule</item>
<item name="urlWizard" xsi:type="string">modulename/index/custom</item>
</argument>
</arguments>
</block>
</referenceBlock>
</body>
</page>
4) Crie um arquivo de layout para ação app/code/Namespace/Modulename/view/adminhtml/layout/modulename_index_custom.xml
<?xml version="1.0" ?>
<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd">
<container name="root" label="Root">
<block class="Namespace\Modulename\Block\Adminhtml\Catalog\Product\Edit\Tab\Custom" template="catalog/product/edit/tab/customfile.phtml" name="product_custom">
</block>
</container>
</layout>
5) Criar arquivo de bloco app/code/Namespace/Modulename/Block/Adminhtml/Catalog/Product/Edit/Tab/Custom.php
<?php
namespace Namespace\Modulename\Block\Adminhtml\Catalog\Product\Edit\Tab;
class Custom extends \Magento\Backend\Block\Widget implements \Magento\Backend\Block\Widget\Tab\TabInterface
{
protected $_template = 'catalog/product/edit/tab/custom.phtml';
public function __construct(
\Magento\Backend\Block\Template\Context $context,
array $data = []
) {
parent::__construct($context, $data);
}
protected function _prepareLayout()
{
return parent::_prepareLayout();
}
}
6) Criar arquivo do controlador para ação app/code/Namespace/Modulename/Controller/Adminhtml/Index/Custom.php
<?php
namespace Namespace\Modulename\Controller\Adminhtml\Index;
class Custom extends \Magento\Framework\App\Action\Action
{
protected $_resultPageFactory;
protected $resultJsonFactory;
protected $_errorHelper;
/**
* @param \Magento\Framework\App\Action\Context $context
* @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
* @param \Magento\Framework\Controller\Result\RawFactory $resultRawFactory
*/
public function __construct(
\Magento\Framework\App\Action\Context $context, \Magento\Framework\View\Result\PageFactory $resultPageFactory, \Magento\Framework\Controller\Result\RawFactory $resultRawFactory
) {
$this->_resultPageFactory = $resultPageFactory;
$this->resultRawFactory = $resultRawFactory;
parent::__construct($context);
}
public function execute()
{
$resultPage = $this->_resultPageFactory->create();
$block = $resultPage->getLayout()->getBlock('product_custom');
$this->getResponse()->appendBody($block->toHtml());
}
}
7) Criar arquivo de modelo app/code/Namespace/Modulename/view/adminhtml/templates/catalog/product/edit/tab/custom.phtml
<div class="<?= /* @noEscape */ $block->getData('config/dataScope') ?>" data-role="step-wizard-dialog" data-bind="scope: '<?= /* @noEscape */ $block->getForm() ?>.<?= /* @noEscape */ $block->getModal() ?>'">
<!-- ko template: getTemplate() --><!-- /ko -->
</div>
<div class="<?= /* @noEscape */ $block->getData('config/dataScope') ?>" id="product-variations-matrix" data-role="product-variations-matrix">
<div data-bind="scope: 'configurableVariations'"></div>
</div>
<script type="text/x-magento-init">
{
"*": {
"Magento_Ui/js/core/app": {
"components": {
"<?= /* @noEscape */ $block->getData('config/form') ?>.<?= /* @noEscape */ $block->getModal() ?>": {
"component": "Namespace_Modulename/js/components/modal-custommodule",
"options": {"type": "slide", "title": "<?php echo $block->escapeHtml(__('Your Title')); ?>"},
"formName": "<?= /* @noEscape */ $block->getForm() ?>",
"isTemplate": false,
"stepWizard": "<?= /* @noEscape */ $block->getData('config/nameStepWizard') ?>",
"children": {
"wizard": {
"url": "<?= /* @noEscape */ $block->getUrl($block->getData('config/urlWizard'), ['id' => $block->getProduct()->getId()]) ?>",
"component": "Magento_Ui/js/form/components/html"
}
}
}
}
}
}
}
</script>
<script>
require(['jquery', 'mage/apply/main'], function ($, main) {
main.apply();
$('.<?= /* @noEscape */ $block->getData('config/dataScope') ?>[data-role=step-wizard-dialog]').applyBindings();
$('.<?= /* @noEscape */ $block->getData('config/dataScope') ?>[data-role=product-variations-matrix]').applyBindings();
})
</script>
8) Crie meu arquivo phtml personalizado app/code/Namespace/Modulename/view/adminhtml/templates/catalog/product/edit/tab/customfile.phtml
e escreva meu código aqui.
9) Criar Js app/code/Namesapce/Modulename/view/adminhtml/web/js/components/modal-custommodule.js
define([
'Magento_Ui/js/modal/modal-component',
'uiRegistry',
'underscore'
], function (Modal, registry, _) {
'use strict';
return Modal.extend({
defaults: {
stepWizard: '',
modules: {
form: '${ $.formName }'
}
},
/**
* Open modal
*/
openModal: function () {
var stepWizard = {};
this.form().validate();
if (this.form().source.get('params.invalid') === false) {
stepWizard = registry.get('index = ' + this.stepWizard);
if (!_.isUndefined(stepWizard)) {
stepWizard.open();
}
this._super();
}
}
});
});
10) Criar Js app/code/Namesapce/Modulename/view/adminhtml/web/js/components/container-tabname-handler.js
define([
'uiComponent'
], function (Element) {
'use strict';
return Element.extend({
defaults: {
listens: {
'${ $.provider }:data.is_downloadable': 'handleProductType'
},
links: {
isDownloadable: '${ $.provider }:data.is_downloadable'
},
modules: {
createConfigurableButton: '${$.createConfigurableButton}'
}
},
/**
* Invokes initialize method of parent class,
* contains initialization logic
*/
initialize: function () {
this._super();
this.handleProductType(this.isDownloadable);
return this;
},
/**
* Calls 'initObservable' of parent
*
* @returns {Object} Chainable.
*/
initObservable: function () {
this._super()
.observe(['content']);
return this;
},
/**
* Change content for container and visibility for button
*
* @param {String} isDownloadable
*/
handleProductType: function (isDownloadable) {
if (isDownloadable === '1') {
this.content(this.content2);
if (this.createConfigurableButton()) {
this.createConfigurableButton().visible(false);
}
} else {
this.content(this.content1);
if (this.createConfigurableButton()) {
this.createConfigurableButton().visible(true);
}
}
}
});
});