[DOCS] 更新 RBAC 實作規範與角色初始化流程建議
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 55s
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 55s
This commit is contained in:
@@ -7,28 +7,53 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6" x-data="{
|
||||
showModal: false,
|
||||
editing: false,
|
||||
showModal: {{ $errors->any() ? 'true' : 'false' }},
|
||||
editing: {{ old('_method') === 'PUT' || (isset($user) && $errors->any()) ? 'true' : 'false' }},
|
||||
allRoles: @js($roles),
|
||||
currentUser: {
|
||||
id: '',
|
||||
name: '',
|
||||
username: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
company_id: '',
|
||||
role: 'user',
|
||||
status: 1
|
||||
id: '{{ old('id') }}',
|
||||
name: '{{ old('name') }}',
|
||||
username: '{{ old('username') }}',
|
||||
email: '{{ old('email') }}',
|
||||
phone: '{{ old('phone') }}',
|
||||
company_id: '{{ old('company_id', auth()->user()->isSystemAdmin() ? '' : auth()->user()->company_id) }}',
|
||||
role: '{{ old('role', '') }}',
|
||||
status: {{ old('status', 1) }}
|
||||
},
|
||||
get filteredRoles() {
|
||||
if (this.currentUser.company_id === '' || this.currentUser.company_id === null) {
|
||||
// 系統層級:顯示 is_system = 1 的角色
|
||||
return this.allRoles.filter(r => r.is_system);
|
||||
} else {
|
||||
// 客戶層級:只顯示該公司的角色
|
||||
let roles = this.allRoles.filter(r => r.company_id == this.currentUser.company_id);
|
||||
|
||||
// 如果是系統管理員,額外允許選擇「客戶層級範本」
|
||||
@if(auth()->user()->isSystemAdmin())
|
||||
let templates = this.allRoles.filter(r => !r.is_system && (r.company_id === null || r.company_id === ''));
|
||||
roles = [...roles, ...templates];
|
||||
@endif
|
||||
|
||||
return roles;
|
||||
}
|
||||
},
|
||||
openCreateModal() {
|
||||
this.editing = false;
|
||||
this.currentUser = { id: '', name: '', username: '', email: '', phone: '', company_id: '', role: 'user', status: 1 };
|
||||
this.currentUser = { id: '', name: '', username: '', email: '', phone: '', company_id: '{{ auth()->user()->isSystemAdmin() ? "" : auth()->user()->company_id }}', role: '', status: 1 };
|
||||
this.showModal = true;
|
||||
// 預設選取第一個可用的角色
|
||||
this.$nextTick(() => {
|
||||
if (this.filteredRoles.length > 0) {
|
||||
this.currentUser.role = this.filteredRoles[0].name;
|
||||
}
|
||||
});
|
||||
},
|
||||
openEditModal(user) {
|
||||
this.editing = true;
|
||||
this.currentUser = {
|
||||
...user,
|
||||
role: user.roles && user.roles.length > 0 ? user.roles[0].name : 'user'
|
||||
company_id: user.company_id || '',
|
||||
role: user.roles && user.roles.length > 0 ? user.roles[0].name : ''
|
||||
};
|
||||
this.showModal = true;
|
||||
}
|
||||
@@ -217,61 +242,97 @@
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div class="space-y-2">
|
||||
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{ __('Full Name') }}</label>
|
||||
<input type="text" name="name" x-model="currentUser.name" required class="luxury-input" placeholder="{{ __('e.g. John Doe') }}">
|
||||
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">
|
||||
{{ __('Full Name') }} <span class="text-rose-500">*</span>
|
||||
</label>
|
||||
<input type="text" name="name" x-model="currentUser.name" required class="luxury-input @error('name') border-rose-500 @enderror" placeholder="{{ __('e.g. John Doe') }}">
|
||||
@error('name')
|
||||
<p class="text-[10px] font-bold text-rose-500 mt-1 pl-1 uppercase tracking-tight">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{ __('Username') }}</label>
|
||||
<input type="text" name="username" x-model="currentUser.username" required class="luxury-input" placeholder="{{ __('e.g. johndoe') }}">
|
||||
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">
|
||||
{{ __('Username') }} <span class="text-rose-500">*</span>
|
||||
</label>
|
||||
<input type="text" name="username" x-model="currentUser.username" required class="luxury-input @error('username') border-rose-500 @enderror" placeholder="{{ __('e.g. johndoe') }}">
|
||||
@error('username')
|
||||
<p class="text-[10px] font-bold text-rose-500 mt-1 pl-1 uppercase tracking-tight">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div class="space-y-2">
|
||||
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{ __('Email') }}</label>
|
||||
<input type="email" name="email" x-model="currentUser.email" class="luxury-input" placeholder="{{ __('john@example.com') }}">
|
||||
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">
|
||||
{{ __('Email') }} <span class="text-rose-500">*</span>
|
||||
</label>
|
||||
<input type="email" name="email" x-model="currentUser.email" required class="luxury-input @error('email') border-rose-500 @enderror" placeholder="{{ __('john@example.com') }}">
|
||||
@error('email')
|
||||
<p class="text-[10px] font-bold text-rose-500 mt-1 pl-1 uppercase tracking-tight">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{ __('Phone') }}</label>
|
||||
<input type="text" name="phone" x-model="currentUser.phone" class="luxury-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div class="space-y-2">
|
||||
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{ __('Role') }}</label>
|
||||
<select name="role" x-model="currentUser.role" class="luxury-select">
|
||||
@foreach($roles as $role)
|
||||
<option value="{{ $role->name }}">{{ __($role->name) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{ __('Status') }}</label>
|
||||
<select name="status" x-model="currentUser.status" class="luxury-select">
|
||||
<option value="1">{{ __('Active') }}</option>
|
||||
<option value="0">{{ __('Disabled') }}</option>
|
||||
</select>
|
||||
<input type="text" name="phone" x-model="currentUser.phone" class="luxury-input @error('phone') border-rose-500 @enderror">
|
||||
@error('phone')
|
||||
<p class="text-[10px] font-bold text-rose-500 mt-1 pl-1 uppercase tracking-tight">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if(auth()->user()->isSystemAdmin())
|
||||
<div class="space-y-2">
|
||||
<div class="space-y-2 mb-6">
|
||||
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{ __('Company') }}</label>
|
||||
<select name="company_id" x-model="currentUser.company_id" class="luxury-select">
|
||||
<select name="company_id" x-model="currentUser.company_id" class="luxury-select @error('company_id') border-rose-500 @enderror"
|
||||
@change="$nextTick(() => { if (filteredRoles.length > 0 && !filteredRoles.find(r => r.name === currentUser.role)) { currentUser.role = filteredRoles[0].name; } })">
|
||||
<option value="">{{ __('SYSTEM') }}</option>
|
||||
@foreach($companies as $company)
|
||||
<option value="{{ $company->id }}">{{ $company->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('company_id')
|
||||
<p class="text-[10px] font-bold text-rose-500 mt-1 pl-1 uppercase tracking-tight">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div class="space-y-2">
|
||||
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">
|
||||
{{ __('Role') }} <span class="text-rose-500">*</span>
|
||||
</label>
|
||||
<select name="role" x-model="currentUser.role" class="luxury-select @error('role') border-rose-500 @enderror">
|
||||
<template x-for="role in filteredRoles" :key="role.id">
|
||||
<option :value="role.name" x-text="role.name"></option>
|
||||
</template>
|
||||
</select>
|
||||
@error('role')
|
||||
<p class="text-[10px] font-bold text-rose-500 mt-1 pl-1 uppercase tracking-tight">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{ __('Status') }}</label>
|
||||
<select name="status" x-model="currentUser.status" class="luxury-select @error('status') border-rose-500 @enderror">
|
||||
<option value="1">{{ __('Active') }}</option>
|
||||
<option value="0">{{ __('Disabled') }}</option>
|
||||
</select>
|
||||
@error('status')
|
||||
<p class="text-[10px] font-bold text-rose-500 mt-1 pl-1 uppercase tracking-tight">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">
|
||||
<span x-text="editing ? '{{ __('New Password (leave blank to keep current)') }}' : '{{ __('Password') }}'"></span>
|
||||
<template x-if="!editing">
|
||||
<span class="text-rose-500">*</span>
|
||||
</template>
|
||||
</label>
|
||||
<input type="password" name="password" :required="!editing" class="luxury-input" placeholder="••••••••">
|
||||
<input type="password" name="password" :required="!editing" class="luxury-input @error('password') border-rose-500 @enderror" placeholder="••••••••">
|
||||
@error('password')
|
||||
<p class="text-[10px] font-bold text-rose-500 mt-1 pl-1 uppercase tracking-tight">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end gap-x-4 pt-8">
|
||||
|
||||
Reference in New Issue
Block a user