diff --git a/phinx-bootstrap.php b/phinx-bootstrap.php index 75d5b3a..f4de892 100644 --- a/phinx-bootstrap.php +++ b/phinx-bootstrap.php @@ -50,4 +50,19 @@ Config::load($baseDir . '/config', ['route', 'middleware', 'process', 'server', $thinkorm = array_replace_recursive(config('thinkorm', []), config('think-orm', [])); if (!empty($thinkorm)) { support\think\Db::setConfig($thinkorm); + // Webman DbManager 使用连接池且忽略 force;安装向导在同进程内迁移时若不清理,会沿用旧前缀连接, + // 导致 Phinx 已建带前缀表而 Db::name() 仍查无前缀表(如 menu_rule 不存在)。 + if (class_exists(\Webman\ThinkOrm\DbManager::class)) { + $ref = new \ReflectionClass(\Webman\ThinkOrm\DbManager::class); + if ($ref->hasProperty('pools')) { + $poolsProp = $ref->getProperty('pools'); + $poolsProp->setAccessible(true); + $poolsProp->setValue(null, []); + } + } + if (class_exists(\Webman\Context::class)) { + foreach (array_keys($thinkorm['connections'] ?? []) as $connName) { + \Webman\Context::set('think-orm.connections.' . $connName, null); + } + } } diff --git a/public/install/index.html b/public/install/index.html index 5164917..66fe5d0 100644 --- a/public/install/index.html +++ b/public/install/index.html @@ -11,6 +11,98 @@ fetch('/api/install/accessUrls').then(function(r){return r.json();}).then(function(res){ if (res && res.data) { urls.adminUrl = res.data.adminUrl || ''; urls.frontUrl = res.data.frontUrl || ''; } }).catch(function(){}); + function ensureQuickPanel() { + if (!urls.adminUrl && !urls.frontUrl) return; + if (document.getElementById('__ba_install_quick_urls__')) return; + var wrap = document.createElement('div'); + wrap.id = '__ba_install_quick_urls__'; + wrap.style.position = 'fixed'; + wrap.style.right = '16px'; + wrap.style.bottom = '16px'; + wrap.style.zIndex = '99999'; + wrap.style.maxWidth = '560px'; + wrap.style.fontFamily = 'ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji"'; + wrap.innerHTML = + '
' + + '
' + + '
安装完成快捷入口
' + + '' + + '
' + + '
' + + '
' + + '
后台地址
' + + '
' + + '' + + '' + + '
' + + '
' + + '
' + + '
前台地址
' + + '
' + + '' + + '' + + '
' + + '
' + + '
' + + '
' + + '
'; + document.body.appendChild(wrap); + + var closeBtn = wrap.querySelector('button[aria-label="close"]'); + if (closeBtn) closeBtn.addEventListener('click', function(){ wrap.remove(); }); + + function setLink(which, val) { + var a = wrap.querySelector('a[data-k="' + which + '"]'); + if (!a) return; + a.textContent = val || ''; + a.href = val || 'javascript:void(0)'; + } + setLink('admin', urls.adminUrl); + setLink('front', urls.frontUrl); + + function showMsg(text, ok) { + var el = wrap.querySelector('[data-msg]'); + if (!el) return; + el.style.color = ok ? '#52c41a' : '#ff4d4f'; + el.textContent = text; + window.clearTimeout(el.__t); + el.__t = window.setTimeout(function(){ el.textContent = ''; }, 1800); + } + function copyText(text) { + if (!text) return Promise.reject(new Error('empty')); + if (navigator.clipboard && navigator.clipboard.writeText) { + return navigator.clipboard.writeText(text); + } + return new Promise(function(resolve, reject){ + try { + var ta = document.createElement('textarea'); + ta.value = text; + ta.setAttribute('readonly', 'readonly'); + ta.style.position = 'fixed'; + ta.style.left = '-9999px'; + document.body.appendChild(ta); + ta.select(); + var ok = document.execCommand('copy'); + document.body.removeChild(ta); + ok ? resolve() : reject(new Error('copy failed')); + } catch (e) { + reject(e); + } + }); + } + wrap.addEventListener('click', function(e){ + var t = e.target; + if (!t || !t.getAttribute) return; + var which = t.getAttribute('data-copy'); + if (!which) return; + var text = which === 'admin' ? urls.adminUrl : urls.frontUrl; + copyText(text).then(function(){ + showMsg('已复制:' + text, true); + }).catch(function(){ + showMsg('复制失败,请手动复制', false); + }); + }); + } function applyUrls() { if (!urls.adminUrl && !urls.frontUrl) return; document.querySelectorAll('input[type="text"], input:not([type])').forEach(function(inp){ @@ -24,6 +116,7 @@ document.querySelectorAll('a[href*="#/"]').forEach(function(a){ if (urls.frontUrl && a.href.indexOf('#/admin') < 0) a.href = urls.frontUrl; }); + ensureQuickPanel(); } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ setInterval(applyUrls, 800); }); else setInterval(applyUrls, 800);