feat. member.page.profile 完善
This commit is contained in:
parent
d022b0635a
commit
149d603a4c
@ -9,7 +9,7 @@
|
||||
use Illuminate\Http\Request;
|
||||
use Log;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
|
||||
use App\Models\User;
|
||||
class SettingController extends Controller
|
||||
{
|
||||
|
||||
@ -62,4 +62,26 @@ public function promoCodeCreate(Request $request)
|
||||
}
|
||||
}
|
||||
|
||||
public function adminIndex(Request $request)
|
||||
{
|
||||
$data = User::paginate(15);
|
||||
|
||||
return view('admin.setting.adminlist', ['data' => $data]);
|
||||
}
|
||||
|
||||
public function loginStatus(Request $request)
|
||||
{
|
||||
$user = User::where('id', $request->id)->first();
|
||||
$user->can_login = $request->can_login;
|
||||
$user->save();
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'msg' => '狀態已更新',
|
||||
'can_login' => $user->can_login,
|
||||
]);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -42,6 +42,7 @@ public function redirectToProvider(Request $request)
|
||||
*/
|
||||
public function handleProviderCallback(Request $request)
|
||||
{
|
||||
|
||||
// 从 Session 获取 $redirectTo
|
||||
$redirectTo = session('redirect_to', 'member');
|
||||
|
||||
@ -70,6 +71,7 @@ public function handleProviderCallback(Request $request)
|
||||
'avatar' => $avatar,
|
||||
'source' => 'cafeg',
|
||||
'email' => $email,
|
||||
'can_login' => 0,
|
||||
]);
|
||||
Auth::guard('web')->login($newUser);
|
||||
}
|
||||
|
||||
25
app/Http/Middleware/AdminAuth.php
Normal file
25
app/Http/Middleware/AdminAuth.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class AdminAuth
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if (Auth::check() && Auth::user()->can_login == 1) {
|
||||
return $next($request);
|
||||
}
|
||||
return redirect()->route('admin.login')->with('error', '此帳號未開通,請聯繫管理員');
|
||||
|
||||
}
|
||||
}
|
||||
@ -20,7 +20,7 @@ public function handle(Request $request, Closure $next): Response
|
||||
if (!Auth::check() && $request->path() != 'admin/register') {
|
||||
return redirect()->route('admin.login');
|
||||
}
|
||||
if (Auth::check() && $request->path() == 'admin/login') {
|
||||
if (Auth::check() && $request->path() == 'admin/login' && Auth::user()->can_login == 1) {
|
||||
return redirect()->route('admin.index');
|
||||
}
|
||||
|
||||
|
||||
@ -66,5 +66,11 @@ public function getPromoCode()
|
||||
{
|
||||
return $this->hasOne(Promocode::class, 'used_count', 'id');
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ class User extends Authenticatable
|
||||
'phone',
|
||||
'password',
|
||||
'line_id',
|
||||
'can_login',
|
||||
];
|
||||
|
||||
/**
|
||||
@ -34,6 +35,13 @@ class User extends Authenticatable
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
public static $canlogin =
|
||||
[
|
||||
0 => '未允許',
|
||||
1 => '允許',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
@ -46,4 +54,11 @@ protected function casts(): array
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
|
||||
public function getCanLoginStatusAttribute()
|
||||
{
|
||||
return self::$canlogin[$this->attributes['can_login']] ?? '未知狀態';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -21,6 +21,11 @@
|
||||
"url": "admin/setting/promocode",
|
||||
"name": "優惠代碼",
|
||||
"slug": "setting-promocode"
|
||||
},
|
||||
{
|
||||
"url": "admin/setting/adminlist",
|
||||
"name": "後台登入人員",
|
||||
"slug": "setting-adminlist"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@ -72,7 +72,7 @@
|
||||
// Core stylesheets
|
||||
'core.scss': 'https://demos.pixinvent.com/materialize-html-laravel-admin-template/demo/build/assets/core-kL5gEEKA.css',
|
||||
'core-dark.scss': 'https://demos.pixinvent.com/materialize-html-laravel-admin-template/demo/build/assets/core-dark-DFvmi5J3.css',
|
||||
|
||||
|
||||
// Themes
|
||||
'theme-default.scss': 'https://demos.pixinvent.com/materialize-html-laravel-admin-template/demo/build/assets/theme-default-Bt2z4DrM.css',
|
||||
'theme-default-dark.scss': 'https://demos.pixinvent.com/materialize-html-laravel-admin-template/demo/build/assets/theme-default-dark-CHvAJUy2.css',
|
||||
@ -93,7 +93,7 @@
|
||||
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-5J3LMKC" height="0" width="0" style="display: none; visibility: hidden"></iframe></noscript>
|
||||
|
||||
<!-- Layout Content -->
|
||||
|
||||
|
||||
<!-- Content -->
|
||||
<div class="position-relative">
|
||||
<div class="authentication-wrapper authentication-basic container-p-y p-4 p-sm-0">
|
||||
@ -105,13 +105,13 @@
|
||||
<div class="app-brand justify-content-center mt-5">
|
||||
<a href="#" class="app-brand-link gap-2">
|
||||
<span class="app-brand-logo demo"><span>
|
||||
<img src="{{asset('assets/img/logo/cafeg-logo.png')}}" width="50px" height="50px" alt="{{asset('img/logo/cafeg-logo.png')}}"> </img>
|
||||
<img src="{{asset('assets/img/logo/cafeg-logo.png')}}" width="50px" height="50px" alt="{{asset('img/logo/cafeg-logo.png')}}"> </img>
|
||||
|
||||
</span>
|
||||
</span>
|
||||
<span class="app-brand-text demo text-heading fw-semibold">
|
||||
<img src="{{asset('assets/img/logo/cafeg-logo-h.png')}}" width="120px" height="50px" alt="{{asset('img/logo/cafeg-logo.png')}}"> </img>
|
||||
|
||||
<img src="{{asset('assets/img/logo/cafeg-logo-h.png')}}" width="120px" height="50px" alt="{{asset('img/logo/cafeg-logo.png')}}"> </img>
|
||||
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
@ -166,13 +166,13 @@
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-center gap-2">
|
||||
|
||||
|
||||
<a href="{{route('admin.login.line')}}" class="btn btn-icon rounded-circle btn-text-line">
|
||||
<i class="tf-icons ri-line-fill"></i>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{{-- <a href="javascript:;" class="btn btn-icon rounded-circle btn-text-google-plus">
|
||||
<i class="tf-icons ri-google-fill"></i>
|
||||
@ -188,11 +188,11 @@
|
||||
|
||||
<!--/ Layout Content -->
|
||||
|
||||
|
||||
|
||||
{{-- <div class="buy-now">
|
||||
<a href="#" target="" class="btn btn-danger btn-buy-now">Buy Now</a>
|
||||
</div> --}}
|
||||
|
||||
|
||||
|
||||
<!-- Include Scripts -->
|
||||
<!-- $isFront is used to append the front layout scripts only on the front layout otherwise the variable will be blank -->
|
||||
@ -231,7 +231,24 @@
|
||||
</body>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
|
||||
@if (session('error'))
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
console.log('go there');
|
||||
Swal.fire({
|
||||
title: '失敗'
|
||||
, text: "{{ session('error') }}"
|
||||
, icon: 'error'
|
||||
, confirmButtonText: '确定'
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
@endif
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
|
||||
235
resources/views/admin/setting/adminlist.blade.php
Normal file
235
resources/views/admin/setting/adminlist.blade.php
Normal file
@ -0,0 +1,235 @@
|
||||
@extends('layouts.admin_app')
|
||||
@section('header')
|
||||
@endsection
|
||||
@section('content')
|
||||
<style>
|
||||
.table th,
|
||||
.table td {
|
||||
white-space: nowrap;
|
||||
/* 禁止文字換行 */
|
||||
text-align: center;
|
||||
/* 內容置中 */
|
||||
overflow: hidden;
|
||||
/* 隱藏超出內容 */
|
||||
text-overflow: ellipsis;
|
||||
/* 超出用省略號顯示 */
|
||||
}
|
||||
|
||||
.table th {
|
||||
width: auto;
|
||||
/* 可根據內容自適應,但不要超出 */
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 100%;
|
||||
/* 撐滿父容器 */
|
||||
}
|
||||
</style>
|
||||
|
||||
<div clas="content-wrapper">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card-body">
|
||||
<!-- Basic Breadcrumb -->
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item">
|
||||
<a href="javascript:void(0);">首頁</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="javascript:void(0);">設定</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item active">優惠代碼</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content wrapper -->
|
||||
<div class="content-wrapper">
|
||||
|
||||
<!-- Content MemberList-->
|
||||
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<div class="row g-6 mb-6">
|
||||
<div class="col-sm-6 col-xl-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="me-1">
|
||||
<p class="text-heading mb-1">會員共計</p>
|
||||
<div class="d-flex align-items-center">
|
||||
<h4 class="mb-1 me-2">21,459</h4>
|
||||
<p class="text-success mb-1">(+29%)</p>
|
||||
</div>
|
||||
<small class="mb-0">Total Users</small>
|
||||
</div>
|
||||
<div class="avatar">
|
||||
<div class="avatar-initial bg-label-primary rounded-3">
|
||||
<div class="ri-group-line ri-26px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-xl-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="me-1">
|
||||
<p class="text-heading mb-1">普通會員</p>
|
||||
<div class="d-flex align-items-center">
|
||||
<h4 class="mb-1 me-1">4,567</h4>
|
||||
<p class="text-success mb-1">(+18%)</p>
|
||||
</div>
|
||||
<small class="mb-0">Last week analytics</small>
|
||||
</div>
|
||||
<div class="avatar">
|
||||
<div class="avatar-initial bg-label-danger rounded-3">
|
||||
<div class="ri-user-add-line ri-26px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-xl-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="me-1">
|
||||
<p class="text-heading mb-1">白銀會員</p>
|
||||
<div class="d-flex align-items-center">
|
||||
<h4 class="mb-1 me-1">19,860</h4>
|
||||
<p class="text-danger mb-1">(-14%)</p>
|
||||
</div>
|
||||
<small class="mb-0">Last week analytics</small>
|
||||
</div>
|
||||
<div class="avatar">
|
||||
<div class="avatar-initial bg-label-success rounded-3">
|
||||
<div class="ri-user-follow-line ri-26px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-xl-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="me-1">
|
||||
<p class="text-heading mb-1">白金會員</p>
|
||||
<div class="d-flex align-items-center">
|
||||
<h4 class="mb-1 me-1">237</h4>
|
||||
<p class="text-success mb-1">(+42%)</p>
|
||||
</div>
|
||||
<small class="mb-0">Last week analytics</small>
|
||||
</div>
|
||||
<div class="avatar">
|
||||
<div class="avatar-initial bg-label-warning rounded-3">
|
||||
<div class="ri-user-search-line ri-26px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Users List Table -->
|
||||
<div class="card">
|
||||
<div class="card-header border-bottom">
|
||||
<h5 class="card-title mb-0">Filters</h5>
|
||||
<div class="d-flex justify-content-between align-items-center row gx-5 pt-4 gap-5 gap-md-0">
|
||||
<div class="col-md-4 user_role"></div>
|
||||
<div class="col-md-4 user_plan"></div>
|
||||
<div class="col-md-4 user_status"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-datatable table-responsive">
|
||||
<table class="datatables-users table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>會員id</th>
|
||||
<th>會員名稱</th>
|
||||
<th>手機號碼</th>
|
||||
<th>登入狀態</th>
|
||||
<th>操 作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($data as $item)
|
||||
<tr>
|
||||
<td>{{ $item->id }}</td>
|
||||
<td>{{ $item->name }}</td>
|
||||
<td>{{ $item->phone }}</td>
|
||||
<td>
|
||||
<a href="#">
|
||||
<span id="status-{{ $item->id }}"
|
||||
|
||||
onclick="updateStatus({{ $item->id }}, {{ $item->can_login == 1 ? 0 : 1 }})"
|
||||
class="badge rounded-pill {{ $item->can_login == 1 ? 'bg-label-primary' : 'bg-label-danger' }}">
|
||||
{{ $item->can_login_status}}
|
||||
</span>
|
||||
</a>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
@endforeach
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-backdrop fade"></div>
|
||||
{{ $data->links() }}
|
||||
</div>
|
||||
<!-- Content wrapper -->
|
||||
</div>
|
||||
<!-- / Layout page -->
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@section('scripts')
|
||||
<script>
|
||||
function updateStatus(id, status) {
|
||||
$.ajax({
|
||||
type: "patch",
|
||||
url: "{{ route('admin.canlogin.status') }}",
|
||||
data: {
|
||||
id: id,
|
||||
can_login: status,
|
||||
_token: "{{ csrf_token() }}" // 確保 CSRF token 被傳送
|
||||
},
|
||||
dataType: "json",
|
||||
success: function(response) {
|
||||
console.log('can_login status:', response.status);
|
||||
Swal.fire({
|
||||
title: '成功',
|
||||
text: response.msg,
|
||||
icon: 'success',
|
||||
confirmButtonText: '确定'
|
||||
});
|
||||
const statusElement = document.getElementById(`status-${id}`);
|
||||
if (statusElement) {
|
||||
statusElement.textContent = response.can_login == 1 ? '允許' : '未允許';
|
||||
statusElement.className = `badge rounded-pill ${response.can_login == 1 ? 'bg-label-primary' : 'bg-label-danger'}`;
|
||||
statusElement.setAttribute('onclick', `updateStatus(${id}, ${response.can_login == 1 ? 0 : 1})`);
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
Swal.fire({
|
||||
title: '錯誤',
|
||||
text: '更新失敗,請稍後再試。',
|
||||
icon: 'error',
|
||||
confirmButtonText: '确定'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
@endsection
|
||||
@ -415,6 +415,88 @@ function sendVerificationEmail(email) {
|
||||
|
||||
window.location.href = "{{ route('member.index') }}";
|
||||
});
|
||||
$(document).ready(function() {
|
||||
// 監聽表單提交事件
|
||||
$('#editUserForm').on('submit', function(e) {
|
||||
e.preventDefault(); // 阻止默認的表單提交行為
|
||||
|
||||
// 獲取表單數據
|
||||
let email = $('#formValidationEmail').val();
|
||||
let password = $('#basic-default-password').val();
|
||||
let confirmPassword = $('#formValidationConfirmPass').val();
|
||||
let phone = $('#modalEditUserPhone').val();
|
||||
|
||||
// 表單驗證
|
||||
let valid = true;
|
||||
|
||||
// 驗證 Email
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(email)) {
|
||||
valid = false;
|
||||
$('#formValidationEmail').addClass('is-invalid');
|
||||
} else {
|
||||
$('#formValidationEmail').removeClass('is-invalid');
|
||||
}
|
||||
|
||||
// 驗證密碼與確認密碼是否相符
|
||||
if (password !== confirmPassword) {
|
||||
valid = false;
|
||||
$('#formValidationConfirmPass').addClass('is-invalid');
|
||||
} else {
|
||||
$('#formValidationConfirmPass').removeClass('is-invalid');
|
||||
}
|
||||
|
||||
// 驗證手機號碼格式
|
||||
const phoneRegex = /^09\d{8}$/;
|
||||
if (!phoneRegex.test(phone)) {
|
||||
valid = false;
|
||||
$('#modalEditUserPhone').addClass('is-invalid');
|
||||
} else {
|
||||
$('#modalEditUserPhone').removeClass('is-invalid');
|
||||
}
|
||||
|
||||
// 若驗證通過,發送 AJAX 請求
|
||||
if (valid) {
|
||||
// 打包數據
|
||||
let formData = {
|
||||
email: email,
|
||||
password: password,
|
||||
phone: phone
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: "{{ route('member.profile.update') }}", // 替換為後端處理路由
|
||||
type: 'PUT', // 使用 HTTP PUT 方法
|
||||
data: formData,
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') // CSRF 驗證
|
||||
},
|
||||
success: function(response) {
|
||||
// 成功處理邏輯
|
||||
Swal.fire({
|
||||
title: '更新成功',
|
||||
text: response.message || '您的個人資訊已成功更新!',
|
||||
icon: 'success',
|
||||
confirmButtonText: '確定'
|
||||
}).then(() => {
|
||||
// 跳轉至其他頁面
|
||||
window.location.href = "{{ route('member.index') }}";
|
||||
});
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
// 錯誤處理邏輯
|
||||
Swal.fire({
|
||||
title: '更新失敗',
|
||||
text: xhr.responseJSON.message || '請稍後重試!',
|
||||
icon: 'error',
|
||||
confirmButtonText: '確定'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
@endsection
|
||||
@endsection
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<aside id="layout-menu" class="layout-menu menu-vertical menu bg-menu-theme">
|
||||
<div class="app-brand demo">
|
||||
<a href="{{url('/')}}" class="app-brand-link">
|
||||
<a href="{{ route('admin.index') }}" class="app-brand-link">
|
||||
<span class="app-brand-logo demo">
|
||||
<span style="color: var(--bs-primary)">
|
||||
<img src="{{asset('assets/img/logo/cafeg-logo.png')}}" width="50px" height="50px" alt="{{asset('img/logo/cafeg-logo.png')}}"> </img>
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
<?php
|
||||
use App\Http\Controllers\LoginController;
|
||||
use App\Http\Middleware\AdminRedirect;
|
||||
use App\Http\Middleware\AdminAuth;
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
/***********************************************
|
||||
@ -14,7 +16,7 @@
|
||||
|
||||
Route::
|
||||
namespace('App\Http\Controllers\Admin') // 設置命名空間
|
||||
->middleware(AdminRedirect::class)
|
||||
->middleware([AdminRedirect::class, AdminAuth::class])
|
||||
->group(function () {
|
||||
Route::get('/', 'IndexController@index')->name('index');
|
||||
Route::get('register', 'RegisterController@index')->name('register');
|
||||
@ -30,9 +32,8 @@
|
||||
Route::get('promocode', 'SettingController@promoCode')->name('promocode');
|
||||
Route::get('promocode/used', 'SettingController@promoCode')->name('promocode.status');
|
||||
Route::post('promocode', 'SettingController@promoCodeCreate')->name('promocode.create');
|
||||
|
||||
|
||||
|
||||
Route::get('adminlist', 'SettingController@adminIndex')->name('adminlist.index');
|
||||
Route::patch('canlogin/status', 'SettingController@loginStatus')->name('canlogin.status');
|
||||
});
|
||||
});
|
||||
// line 登入後查詢;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user