diff --git a/addons_extensions/tabbar/__init__.py b/addons_extensions/tabbar/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/addons_extensions/tabbar/__manifest__.py b/addons_extensions/tabbar/__manifest__.py
new file mode 100644
index 000000000..95111363d
--- /dev/null
+++ b/addons_extensions/tabbar/__manifest__.py
@@ -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/**/*",
+ ],
+ },
+
+}
diff --git a/addons_extensions/tabbar/static/description/1.png b/addons_extensions/tabbar/static/description/1.png
new file mode 100644
index 000000000..04cc792e0
Binary files /dev/null and b/addons_extensions/tabbar/static/description/1.png differ
diff --git a/addons_extensions/tabbar/static/description/icon.png b/addons_extensions/tabbar/static/description/icon.png
new file mode 100644
index 000000000..fd8242404
Binary files /dev/null and b/addons_extensions/tabbar/static/description/icon.png differ
diff --git a/addons_extensions/tabbar/static/description/index.html b/addons_extensions/tabbar/static/description/index.html
new file mode 100644
index 000000000..fc996a05c
--- /dev/null
+++ b/addons_extensions/tabbar/static/description/index.html
@@ -0,0 +1,10 @@
+
+
+The basic functions have been implemented, and further optimization will be carried out later
+
+
+
+
+
+
+
diff --git a/addons_extensions/tabbar/static/description/main_banner.png b/addons_extensions/tabbar/static/description/main_banner.png
new file mode 100644
index 000000000..e85a4140d
Binary files /dev/null and b/addons_extensions/tabbar/static/description/main_banner.png differ
diff --git a/addons_extensions/tabbar/static/src/action_service.js b/addons_extensions/tabbar/static/src/action_service.js
new file mode 100644
index 000000000..fe9e262dc
--- /dev/null
+++ b/addons_extensions/tabbar/static/src/action_service.js
@@ -0,0 +1,1942 @@
+import { _t } from '@web/core/l10n/translation';
+import { browser } from '@web/core/browser/browser';
+import { makeContext } from '@web/core/context';
+import { useDebugCategory } from '@web/core/debug/debug_context';
+import { evaluateExpr } from '@web/core/py_js/py';
+import { rpc, rpcBus } from '@web/core/network/rpc';
+import { registry } from '@web/core/registry';
+import { user } from '@web/core/user';
+import { Deferred, KeepLast } from '@web/core/utils/concurrency';
+import { useBus, useService } from '@web/core/utils/hooks';
+import { View, ViewNotFoundError } from '@web/views/view';
+import { ActionDialog } from '@web/webclient/actions/action_dialog';
+import { ReportAction } from '@web/webclient/actions/reports/report_action';
+import { UPDATE_METHODS } from '@web/core/orm_service';
+import { CallbackRecorder } from '@web/search/action_hook';
+import { ControlPanel } from '@web/search/control_panel/control_panel';
+import {
+ PATH_KEYS,
+ router as _router,
+ stateToUrl,
+} from '@web/core/browser/router';
+import {
+ Component,
+ markup,
+ onMounted,
+ onWillUnmount,
+ onError,
+ useChildSubEnv,
+ xml,
+ reactive,
+ status,
+ useSubEnv,
+} from '@odoo/owl';
+import { downloadReport, getReportUrl } from '@web/webclient/actions/reports/utils';
+import { zip } from '@web/core/utils/arrays';
+import {
+ isHtmlEmpty,
+} from "@web/views/kanban/kanban_record";
+//import { isHtmlEmpty } from '@web/core/utils/html';
+import { omit, pick, shallowEqual } from '@web/core/utils/objects';
+import { session } from '@web/session';
+import { exprToBoolean } from '@web/core/utils/strings';
+import { clearUncommittedChanges, standardActionServiceProps, ControllerNotFoundError, InvalidButtonParamsError } from '@web/webclient/actions/action_service';
+
+class BlankComponent extends Component {
+ static props = ['onMounted', 'withControlPanel', '*'];
+ static template = xml`
+
+
+
+
+ `;
+ static components = { ControlPanel };
+
+ setup() {
+ useChildSubEnv({ config: { breadcrumbs: [], noBreadcrumbs: true } });
+ onMounted(() => this.props.onMounted());
+ }
+}
+
+const actionHandlersRegistry = registry.category('action_handlers');
+const actionRegistry = registry.category('actions');
+
+
+
+
+function parseActiveIds(ids) {
+ const activeIds = [];
+ if (typeof ids === 'string') {
+ activeIds.push(...ids.split(',').map(Number));
+ } else if (typeof ids === 'number') {
+ activeIds.push(ids);
+ }
+ return activeIds;
+}
+
+const DIALOG_SIZES = {
+ 'extra-large': 'xl',
+ large: 'lg',
+ medium: 'md',
+ small: 'sm',
+};
+
+
+
+const CTX_KEY_REGEX =
+ /^(?:(?:default_|search_default_|show_).+|.+_view_ref|group_by|active_id|active_ids|orderedBy)$/;
+// keys added to the context for the embedded actions feature
+const EMBEDDED_ACTIONS_CTX_KEYS = [
+ 'current_embedded_action_id',
+ 'parent_action_embedded_actions',
+ 'parent_action_id',
+ 'from_embedded_action',
+];
+
+// only register this template once for all dynamic classes ControllerComponent
+const ControllerComponentTemplate = xml``;
+
+export function makeActionManager(env, router = _router) {
+ const breadcrumbCache = {};
+ // ! my edit
+ const controllerStacks = {};
+ let count = 0
+ const keepLast = new KeepLast();
+ let id = 0;
+ let controllerStack = [];
+ let dialogCloseProm;
+ let actionCache = {};
+ let dialog = null;
+ let nextDialog = null;
+
+ router.hideKeyFromUrl('globalState');
+
+ env.bus.addEventListener('CLEAR-CACHES', () => {
+ actionCache = {};
+ });
+ rpcBus.addEventListener('RPC:RESPONSE', (ev) => {
+ const { model, method } = ev.detail.data.params;
+ if (
+ model === 'ir.actions.act_window' &&
+ UPDATE_METHODS.includes(method)
+ ) {
+ actionCache = {};
+ }
+ });
+
+ // ---------------------------------------------------------------------------
+ // misc
+ // ---------------------------------------------------------------------------
+
+ /**
+ * Create an array of virtual controllers based on the current state of the
+ * router.
+ *
+ * @returns {Promise