[FIX] 修復帳號管理角色下拉選單消失問題並優化初始化防護 & [STYLE] 新增個人檔案選單圖標
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 58s

This commit is contained in:
2026-03-25 09:47:17 +08:00
parent 2467d9db7a
commit 9f3a90b2b0
2 changed files with 60 additions and 35 deletions

View File

@@ -10,22 +10,12 @@ $roleSelectConfig = [
"hasSearch" => true, "hasSearch" => true,
"searchPlaceholder" => __('Search Role...'), "searchPlaceholder" => __('Search Role...'),
"isHidePlaceholder" => false, "isHidePlaceholder" => false,
"searchClasses" => "block w-[calc(100%-16px)] mx-2 py-2 px-3 text-sm border-slate-200 dark:border-white/10 rounded-lg "searchClasses" => "block w-[calc(100%-16px)] mx-2 py-2 px-3 text-sm border-slate-200 dark:border-white/10 rounded-lg focus:border-cyan-500 focus:ring-cyan-500 bg-slate-50 dark:bg-slate-900/50 dark:text-slate-200 placeholder:text-slate-400 dark:placeholder:text-slate-500",
focus:border-cyan-500 focus:ring-cyan-500 bg-slate-50 dark:bg-slate-900/50 dark:text-slate-200
placeholder:text-slate-400 dark:placeholder:text-slate-500",
"searchWrapperClasses" => "sticky top-0 bg-white/95 dark:bg-slate-900/95 backdrop-blur-md p-2 z-10", "searchWrapperClasses" => "sticky top-0 bg-white/95 dark:bg-slate-900/95 backdrop-blur-md p-2 z-10",
"toggleClasses" => "hs-select-toggle luxury-select-toggle", "toggleClasses" => "hs-select-toggle luxury-select-toggle",
"dropdownClasses" => "hs-select-menu w-full bg-white/95 dark:bg-slate-900/95 backdrop-blur-xl border border-slate-200 "dropdownClasses" => "hs-select-menu w-full bg-white/95 dark:bg-slate-900/95 backdrop-blur-xl border border-slate-200 dark:border-white/10 rounded-xl shadow-[0_20px_50px_rgba(0,0,0,0.3)] mt-2 z-[100] animate-luxury-in",
dark:border-white/10 rounded-xl shadow-[0_20px_50px_rgba(0,0,0,0.3)] mt-2 z-[100] animate-luxury-in", "optionClasses" => "hs-select-option py-2.5 px-3 mb-0.5 text-sm text-slate-800 dark:text-slate-300 cursor-pointer hover:bg-slate-100 dark:hover:bg-cyan-500/10 dark:hover:text-cyan-400 rounded-lg flex items-center justify-between transition-all duration-300",
"optionClasses" => "hs-select-option py-2.5 px-3 mb-0.5 text-sm text-slate-800 dark:text-slate-300 cursor-pointer "optionTemplate" => '<div class="flex items-center justify-between w-full"><span data-title></span><span class="hs-select-active-indicator hidden text-cyan-500"><svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg></span></div>'
hover:bg-slate-100 dark:hover:bg-cyan-500/10 dark:hover:text-cyan-400 rounded-lg flex items-center justify-between
transition-all duration-300",
"optionTemplate" => '<div class="flex items-center justify-between w-full"><span data-title></span><span
class="hs-select-active-indicator hidden text-cyan-500"><svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg"
width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"
stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12"></polyline>
</svg></span></div>'
]; ];
@endphp @endphp
@@ -728,12 +718,17 @@ transition-all duration-300",
this.editing = false; this.editing = false;
const initialCompanyId = initData.oldValues.company_id; const initialCompanyId = initData.oldValues.company_id;
let initialRole = ''; let initialRole = '';
const roles = this.filteredRoles;
let roles = [];
if (!initialCompanyId || initialCompanyId.toString().trim() === '') {
roles = this.allRoles.filter(r => !r.company_id || r.company_id.toString().trim() === '');
} else {
let companyRoles = this.allRoles.filter(r => r.company_id == initialCompanyId);
roles = companyRoles.length > 0 ? companyRoles : this.allRoles.filter(r => !r.company_id || r.company_id.toString().trim() === '');
}
if (roles.length > 0) { if (roles.length > 0) {
initialRole = roles[0].name; initialRole = roles[0].name;
} else if (this.allRoles.length > 0) {
const systemRoles = this.allRoles.filter(r => !r.company_id || r.company_id.toString().trim() === '');
if (systemRoles.length > 0) initialRole = systemRoles[0].name;
} }
this.currentUser = { this.currentUser = {
@@ -785,7 +780,11 @@ transition-all duration-300",
const wrapper = document.getElementById('role-select-wrapper'); const wrapper = document.getElementById('role-select-wrapper');
if (!wrapper) return; if (!wrapper) return;
const configStr = JSON.stringify(this.roleSelectConfig); // 🛡️ 終極防護:自動過濾配置中的換行符號,防止自動排版工具折行導致 Preline 崩潰
const cleanConfig = JSON.parse(JSON.stringify(this.roleSelectConfig), (key, value) => {
return typeof value === 'string' ? value.replace(/\r?\n|\r/g, ' ').trim() : value;
});
const configStr = JSON.stringify(cleanConfig);
const roles = this.filteredRoles; const roles = this.filteredRoles;
if (roles.length > 0 && !roles.find(r => r.name === this.currentUser.role)) { if (roles.length > 0 && !roles.find(r => r.name === this.currentUser.role)) {
@@ -794,18 +793,29 @@ transition-all duration-300",
this.currentUser.role = ''; this.currentUser.role = '';
} }
const oldSelect = document.getElementById('modal-account-role'); const oldSelects = wrapper.querySelectorAll('select');
if (oldSelect && window.HSSelect && window.HSSelect.getInstance(oldSelect)) { oldSelects.forEach(oldSelect => {
window.HSSelect.getInstance(oldSelect).destroy(); if (window.HSSelect && window.HSSelect.getInstance(oldSelect)) {
try { window.HSSelect.getInstance(oldSelect).destroy(); } catch (e) { console.warn('HSSelect destroy warning:', e); }
} }
});
wrapper.innerHTML = ''; wrapper.innerHTML = '';
const selectEl = document.createElement('select'); const selectEl = document.createElement('select');
selectEl.name = 'role'; selectEl.name = 'role';
selectEl.id = 'modal-account-role'; const uniqueSelectId = 'modal-account-role-' + Date.now() + '-' + Math.round(Math.random() * 1000);
selectEl.id = uniqueSelectId;
selectEl.className = 'hidden'; selectEl.className = 'hidden';
selectEl.setAttribute('data-hs-select', configStr); selectEl.setAttribute('data-hs-select', configStr);
if (roles.length === 0) {
const opt = document.createElement('option');
opt.value = '';
opt.textContent = '{{ __("No roles available") }}';
opt.disabled = true;
opt.selected = true;
selectEl.appendChild(opt);
} else {
roles.forEach(r => { roles.forEach(r => {
const opt = document.createElement('option'); const opt = document.createElement('option');
opt.value = r.name; opt.value = r.name;
@@ -814,11 +824,9 @@ transition-all duration-300",
if (r.name === this.currentUser.role) opt.selected = true; if (r.name === this.currentUser.role) opt.selected = true;
selectEl.appendChild(opt); selectEl.appendChild(opt);
}); });
}
wrapper.appendChild(selectEl); wrapper.appendChild(selectEl);
if (window.HSStaticMethods && window.HSStaticMethods.autoInit) {
window.HSStaticMethods.autoInit(['select']);
}
selectEl.addEventListener('change', (e) => { selectEl.addEventListener('change', (e) => {
this.currentUser.role = e.target.value; this.currentUser.role = e.target.value;
@@ -832,12 +840,28 @@ transition-all duration-300",
const select = window.HSSelect ? window.HSSelect.getInstance(selectEl) : null; const select = window.HSSelect ? window.HSSelect.getInstance(selectEl) : null;
if (select) { if (select) {
select.setValue(this.currentUser.role); select.setValue(this.currentUser.role || '');
} else if (attempts < 20) { } else if (attempts < 20) {
setTimeout(() => waitForHSSelect(attempts + 1), 50); setTimeout(() => waitForHSSelect(attempts + 1), 50);
} }
}; };
const initPreline = (attempts = 0) => {
if (currentGen !== this._roleGeneration) return;
if (window.HSStaticMethods && window.HSStaticMethods.autoInit) {
try {
window.HSStaticMethods.autoInit(['select']);
waitForHSSelect(); waitForHSSelect();
} catch (e) {
console.warn('HSStaticMethods autoInit warning:', e);
}
} else if (attempts < 50) {
setTimeout(() => initPreline(attempts + 1), 50);
}
};
initPreline();
}); });
}, },
init() { init() {

View File

@@ -18,6 +18,7 @@
<ul class="luxury-submenu" data-sidebar-sub> <ul class="luxury-submenu" data-sidebar-sub>
<li> <li>
<a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('profile.edit') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('profile.edit') }}"> <a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('profile.edit') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('profile.edit') }}">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 shrink-0 transition-colors" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" /></svg>
{{ __('Profile') }} {{ __('Profile') }}
</a> </a>
</li> </li>