|
|
@@ -5,95 +5,96 @@
|
|
|
|
|
|
{{-- 页面标题和操作按钮 --}}
|
|
|
<div class="flex flex-wrap gap-4 justify-between items-center mb-6">
|
|
|
- <div class="flex items-center gap-3">
|
|
|
- <div class="w-10 h-10 bg-gradient-to-br from-purple-500 to-purple-600 rounded-xl flex items-center justify-center shadow-lg">
|
|
|
- <span class="icon-star text-white text-xl"></span>
|
|
|
+ <div class="flex items-center gap-4">
|
|
|
+ <div class="w-12 h-12 bg-gradient-to-br from-purple-500 to-purple-600 rounded-xl flex items-center justify-center shadow-lg">
|
|
|
+ <svg class="w-7 h-7 text-white" fill="currentColor" viewBox="0 0 20 20">
|
|
|
+ <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/>
|
|
|
+ </svg>
|
|
|
</div>
|
|
|
<div>
|
|
|
- <p class="text-2xl font-bold text-gray-800 dark:text-white">
|
|
|
+ <h1 class="text-2xl font-bold text-gray-800 dark:text-white">
|
|
|
会员成长值
|
|
|
- </p>
|
|
|
+ </h1>
|
|
|
<p class="text-sm text-gray-500 dark:text-gray-400 mt-0.5">
|
|
|
管理会员成长值和等级体系
|
|
|
</p>
|
|
|
</div>
|
|
|
- <span class="px-3 py-1 text-sm bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 rounded-full font-medium">
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="flex items-center gap-3">
|
|
|
+ <span class="px-3 py-1.5 text-sm bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 rounded-full font-medium">
|
|
|
{{ $customers->total() }} 位会员
|
|
|
</span>
|
|
|
- </div>
|
|
|
|
|
|
- <div class="flex gap-x-3 items-center">
|
|
|
{{-- 调整成长值按钮 --}}
|
|
|
<button
|
|
|
+ type="button"
|
|
|
onclick="openAdjustModal()"
|
|
|
- class="px-4 py-2 bg-gradient-to-r from-purple-600 to-purple-700 text-white rounded-lg hover:from-purple-700 hover:to-purple-800 transition-all duration-200 flex items-center gap-2 shadow-md hover:shadow-lg"
|
|
|
+ style="padding: 10px 20px; background: linear-gradient(135deg, #8b5cf6, #7c3aed); color: white; border-radius: 8px; border: none; cursor: pointer; display: flex; align-items: center; gap: 8px; font-weight: 500; box-shadow: 0 1px 3px 0 rgba(0,0,0,0.1);"
|
|
|
+ onmouseover="this.style.background='linear-gradient(135deg, #7c3aed, #6d28d9)'"
|
|
|
+ onmouseout="this.style.background='linear-gradient(135deg, #8b5cf6, #7c3aed)'"
|
|
|
>
|
|
|
- <span class="icon-plus text-lg"></span>
|
|
|
+ <svg style="width: 20px; height: 20px;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"/>
|
|
|
+ </svg>
|
|
|
调整成长值
|
|
|
</button>
|
|
|
-
|
|
|
- {{-- 等级配置按钮 --}}
|
|
|
- <a
|
|
|
- href="{{ route('admin.growth-value.levels') }}"
|
|
|
- class="px-4 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-all duration-200 flex items-center gap-2"
|
|
|
- >
|
|
|
- <span class="icon-settings text-lg"></span>
|
|
|
- 等级配置
|
|
|
- </a>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
{{-- 统计卡片 --}}
|
|
|
- <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
|
|
|
- <div class="bg-white dark:bg-gray-900 rounded-xl shadow-sm border border-gray-200 dark:border-gray-800 p-6 hover:shadow-md transition-shadow">
|
|
|
+ <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
|
|
|
+ <div class="bg-white dark:bg-gray-900 rounded-xl shadow-sm border border-gray-200 dark:border-gray-800 p-5">
|
|
|
<div class="flex items-center justify-between">
|
|
|
<div>
|
|
|
- <p class="text-sm text-gray-500 dark:text-gray-400 mb-1">
|
|
|
- 总会员数
|
|
|
- </p>
|
|
|
- <p class="text-3xl font-bold text-purple-600 dark:text-purple-400">
|
|
|
- {{ number_format($customers->total()) }}
|
|
|
- </p>
|
|
|
+ <p class="text-xs text-gray-500 dark:text-gray-400 mb-1">总会员数</p>
|
|
|
+ <p class="text-2xl font-bold text-purple-600">{{ number_format($totalCustomers) }}</p>
|
|
|
</div>
|
|
|
- <div class="w-12 h-12 bg-purple-100 dark:bg-purple-900/30 rounded-xl flex items-center justify-center">
|
|
|
- <svg class="w-6 h-6 text-purple-600 dark:text-purple-400" fill="currentColor" viewBox="0 0 20 20">
|
|
|
+ <div class="w-10 h-10 bg-purple-100 rounded-lg flex items-center justify-center">
|
|
|
+ <svg class="w-5 h-5 text-purple-600" fill="currentColor" viewBox="0 0 20 20">
|
|
|
<path d="M9 6a3 3 0 11-6 0 3 3 0 016 0zM17 6a3 3 0 11-6 0 3 3 0 016 0zM12.93 17c.046-.327.07-.66.07-1a6.97 6.97 0 00-1.5-4.33A5 5 0 0119 16v1h-6.07zM6 11a5 5 0 015 5v1H1v-1a5 5 0 015-5z"/>
|
|
|
</svg>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div class="bg-white dark:bg-gray-900 rounded-xl shadow-sm border border-gray-200 dark:border-gray-800 p-6 hover:shadow-md transition-shadow">
|
|
|
+ <div class="bg-white dark:bg-gray-900 rounded-xl shadow-sm border border-gray-200 dark:border-gray-800 p-5">
|
|
|
<div class="flex items-center justify-between">
|
|
|
<div>
|
|
|
- <p class="text-sm text-gray-500 dark:text-gray-400 mb-1">
|
|
|
- 总成长值
|
|
|
- </p>
|
|
|
- <p class="text-3xl font-bold text-blue-600 dark:text-blue-400">
|
|
|
- {{ number_format($totalGrowthValue) }}
|
|
|
- </p>
|
|
|
+ <p class="text-xs text-gray-500 dark:text-gray-400 mb-1">总成长值</p>
|
|
|
+ <p class="text-2xl font-bold text-blue-600">{{ number_format($totalGrowthValue) }}</p>
|
|
|
</div>
|
|
|
- <div class="w-12 h-12 bg-blue-100 dark:bg-blue-900/30 rounded-xl flex items-center justify-center">
|
|
|
- <svg class="w-6 h-6 text-blue-600 dark:text-blue-400" fill="currentColor" viewBox="0 0 20 20">
|
|
|
+ <div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
|
|
|
+ <svg class="w-5 h-5 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
|
|
|
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/>
|
|
|
</svg>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div class="bg-white dark:bg-gray-900 rounded-xl shadow-sm border border-gray-200 dark:border-gray-800 p-6 hover:shadow-md transition-shadow">
|
|
|
+ <div class="bg-white dark:bg-gray-900 rounded-xl shadow-sm border border-gray-200 dark:border-gray-800 p-5">
|
|
|
<div class="flex items-center justify-between">
|
|
|
<div>
|
|
|
- <p class="text-sm text-gray-500 dark:text-gray-400 mb-1">
|
|
|
- 平均成长值
|
|
|
- </p>
|
|
|
- <p class="text-3xl font-bold text-green-600 dark:text-green-400">
|
|
|
- {{ number_format($avgGrowthValue) }}
|
|
|
- </p>
|
|
|
+ <p class="text-xs text-gray-500 dark:text-gray-400 mb-1">平均成长值</p>
|
|
|
+ <p class="text-2xl font-bold text-green-600">{{ number_format($avgGrowthValue) }}</p>
|
|
|
</div>
|
|
|
- <div class="w-12 h-12 bg-green-100 dark:bg-green-900/30 rounded-xl flex items-center justify-center">
|
|
|
- <svg class="w-6 h-6 text-green-600 dark:text-green-400" fill="currentColor" viewBox="0 0 20 20">
|
|
|
- <path fill-rule="evenodd" d="M12 7a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0V8.414l-4.293 4.293a1 1 0 01-1.414 0L8 10.414l-4.293 4.293a1 1 0 01-1.414-1.414l5-5a1 1 0 011.414 0L11 10.586 14.586 7H12z" clip-rule="evenodd"/>
|
|
|
+ <div class="w-10 h-10 bg-green-100 rounded-lg flex items-center justify-center">
|
|
|
+ <svg class="w-5 h-5 text-green-600" fill="currentColor" viewBox="0 0 20 20">
|
|
|
+ <path fill-rule="evenodd" d="M12 7a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0V8.414l-4.293 4.293a1 1 0 01-1.414 0L8 10.414l-4.293 4.293a1 1 0 01-1.414-1.414l5-5a1 1 0 011.414 0L11 10.586 14.586 7H12z"/>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="bg-white dark:bg-gray-900 rounded-xl shadow-sm border border-gray-200 dark:border-gray-800 p-5">
|
|
|
+ <div class="flex items-center justify-between">
|
|
|
+ <div>
|
|
|
+ <p class="text-xs text-gray-500 dark:text-gray-400 mb-1">已完成首单</p>
|
|
|
+ <p class="text-2xl font-bold text-orange-600">{{ number_format($firstOrderCount) }}</p>
|
|
|
+ </div>
|
|
|
+ <div class="w-10 h-10 bg-orange-100 rounded-lg flex items-center justify-center">
|
|
|
+ <svg class="w-5 h-5 text-orange-600" fill="currentColor" viewBox="0 0 20 20">
|
|
|
+ <path d="M3 1a1 1 0 000 2h1.22l.305 1.222a.997.997 0 00.01.042l1.358 5.43-.893.892C3.74 11.846 4.632 14 6.414 14H15a1 1 0 000-2H6.414l1-1H14a1 1 0 00.894-.553l3-6A1 1 0 0017 3H6.28l-.31-1.243A1 1 0 005 1H3zM16 16.5a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0zM6.5 18a1.5 1.5 0 100-3 1.5 1.5 0 000 3z"/>
|
|
|
</svg>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -104,66 +105,26 @@
|
|
|
<div class="bg-white dark:bg-gray-900 rounded-xl shadow-sm border border-gray-200 dark:border-gray-800 p-6 mb-6">
|
|
|
<form method="GET" action="{{ route('admin.growth-value.index') }}" class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
|
<div>
|
|
|
- <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
|
- 客户邮箱
|
|
|
- </label>
|
|
|
- <input
|
|
|
- type="text"
|
|
|
- name="email"
|
|
|
- value="{{ request('email') }}"
|
|
|
- placeholder="搜索客户邮箱..."
|
|
|
- class="w-full px-3 py-2 border border-gray-300 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 dark:bg-gray-800 dark:text-white transition-all"
|
|
|
- >
|
|
|
+ <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">客户邮箱/姓名</label>
|
|
|
+ <input type="text" name="search" value="{{ request('search') }}" placeholder="搜索客户..." class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500">
|
|
|
</div>
|
|
|
-
|
|
|
<div>
|
|
|
- <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
|
- 最小成长值
|
|
|
- </label>
|
|
|
- <input
|
|
|
- type="number"
|
|
|
- name="min_growth_value"
|
|
|
- value="{{ request('min_growth_value') }}"
|
|
|
- placeholder="0"
|
|
|
- min="0"
|
|
|
- class="w-full px-3 py-2 border border-gray-300 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 dark:bg-gray-800 dark:text-white transition-all"
|
|
|
- >
|
|
|
+ <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">成长值等级</label>
|
|
|
+ <select name="growth_level" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500">
|
|
|
+ <option value="">全部</option>
|
|
|
+ <option value="V0" {{ request('growth_level') == 'V0' ? 'selected' : '' }}>V0</option>
|
|
|
+ <option value="V1" {{ request('growth_level') == 'V1' ? 'selected' : '' }}>V1</option>
|
|
|
+ <option value="V2" {{ request('growth_level') == 'V2' ? 'selected' : '' }}>V2</option>
|
|
|
+ <option value="V3" {{ request('growth_level') == 'V3' ? 'selected' : '' }}>V3</option>
|
|
|
+ </select>
|
|
|
</div>
|
|
|
-
|
|
|
<div>
|
|
|
- <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
|
- 最大成长值
|
|
|
- </label>
|
|
|
- <input
|
|
|
- type="number"
|
|
|
- name="max_growth_value"
|
|
|
- value="{{ request('max_growth_value') }}"
|
|
|
- placeholder="10000"
|
|
|
- min="0"
|
|
|
- class="w-full px-3 py-2 border border-gray-300 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 dark:bg-gray-800 dark:text-white transition-all"
|
|
|
- >
|
|
|
+ <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">最小成长值</label>
|
|
|
+ <input type="number" name="min_growth_value" value="{{ request('min_growth_value') }}" placeholder="0" min="0" class="w-full px-3 py-2 border border-gray-300 rounded-lg">
|
|
|
</div>
|
|
|
-
|
|
|
<div class="flex items-end gap-2">
|
|
|
- <button
|
|
|
- type="submit"
|
|
|
- class="flex-1 px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-all duration-200 flex items-center justify-center gap-2"
|
|
|
- >
|
|
|
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
|
|
|
- </svg>
|
|
|
- 搜索
|
|
|
- </button>
|
|
|
-
|
|
|
- <a
|
|
|
- href="{{ route('admin.growth-value.index') }}"
|
|
|
- class="px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 transition-all duration-200 flex items-center justify-center gap-2"
|
|
|
- >
|
|
|
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
|
|
|
- </svg>
|
|
|
- 重置
|
|
|
- </a>
|
|
|
+ <button type="submit" class="flex-1 px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700">搜索</button>
|
|
|
+ <a href="{{ route('admin.growth-value.index') }}" class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200">重置</a>
|
|
|
</div>
|
|
|
</form>
|
|
|
</div>
|
|
|
@@ -171,101 +132,55 @@
|
|
|
{{-- 数据表格 --}}
|
|
|
<div class="bg-white dark:bg-gray-900 rounded-xl shadow-sm border border-gray-200 dark:border-gray-800 overflow-hidden">
|
|
|
<div class="overflow-x-auto">
|
|
|
- <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-800">
|
|
|
- <thead class="bg-gray-50 dark:bg-gray-800/50">
|
|
|
+ <table class="min-w-full divide-y divide-gray-200">
|
|
|
+ <thead class="bg-gray-50">
|
|
|
<tr>
|
|
|
- <th class="px-6 py-4 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
|
- 客户ID
|
|
|
- </th>
|
|
|
- <th class="px-6 py-4 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
|
- 客户姓名
|
|
|
- </th>
|
|
|
- <th class="px-6 py-4 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
|
- 邮箱
|
|
|
- </th>
|
|
|
- <th class="px-6 py-4 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
|
- 成长值
|
|
|
- </th>
|
|
|
- <th class="px-6 py-4 text-center text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
|
- 当前等级
|
|
|
- </th>
|
|
|
- <th class="px-6 py-4 text-center text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
|
- 首单时间
|
|
|
- </th>
|
|
|
- <th class="px-6 py-4 text-center text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
|
- 最后活动
|
|
|
- </th>
|
|
|
- <th class="px-6 py-4 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
|
- 操作
|
|
|
- </th>
|
|
|
+ <th class="px-6 py-4 text-left text-xs font-medium text-gray-500">客户信息</th>
|
|
|
+ <th class="px-6 py-4 text-right text-xs font-medium text-gray-500">当前成长值</th>
|
|
|
+ <th class="px-6 py-4 text-center text-xs font-medium text-gray-500">当前等级</th>
|
|
|
+ <th class="px-6 py-4 text-center text-xs font-medium text-gray-500">首单状态</th>
|
|
|
+ <th class="px-6 py-4 text-center text-xs font-medium text-gray-500">等级更新时间</th>
|
|
|
+ <th class="px-6 py-4 text-right text-xs font-medium text-gray-500">操作</th>
|
|
|
</tr>
|
|
|
</thead>
|
|
|
- <tbody class="bg-white dark:bg-gray-900 divide-y divide-gray-200 dark:divide-gray-800">
|
|
|
+ <tbody class="divide-y divide-gray-200">
|
|
|
@forelse($customers as $customer)
|
|
|
- <tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors">
|
|
|
- <td class="px-6 py-4 whitespace-nowrap text-sm font-mono text-gray-900 dark:text-gray-300">
|
|
|
- #{{ $customer->customer_id }}
|
|
|
- </td>
|
|
|
- <td class="px-6 py-4 whitespace-nowrap">
|
|
|
- <div class="flex items-center gap-2">
|
|
|
- <div class="w-8 h-8 bg-gradient-to-br from-purple-400 to-purple-600 rounded-full flex items-center justify-center text-white text-sm font-medium">
|
|
|
- {{ substr($customer->customer->name ?? 'U', 0, 1) }}
|
|
|
+ <tr class="hover:bg-gray-50">
|
|
|
+ <td class="px-6 py-4">
|
|
|
+ <div class="flex items-center gap-3">
|
|
|
+ <div class="w-10 h-10 bg-gradient-to-br from-purple-400 to-purple-600 rounded-full flex items-center justify-center text-white text-sm font-medium">
|
|
|
+ {{ substr($customer->customer->name ?? $customer->customer->email ?? 'U', 0, 1) }}
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <p class="text-sm font-medium text-gray-900">{{ $customer->customer->name ?? 'N/A' }}</p>
|
|
|
+ <p class="text-xs text-gray-500">{{ $customer->customer->email ?? 'N/A' }}</p>
|
|
|
</div>
|
|
|
- <span class="text-sm font-medium text-gray-900 dark:text-gray-300">
|
|
|
- {{ $customer->customer->name ?? 'N/A' }}
|
|
|
- </span>
|
|
|
- </div>
|
|
|
- </td>
|
|
|
- <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-600 dark:text-gray-400">
|
|
|
- {{ $customer->customer->email ?? 'N/A' }}
|
|
|
- </td>
|
|
|
- <td class="px-6 py-4 whitespace-nowrap text-right">
|
|
|
- <div class="flex items-center justify-end gap-1">
|
|
|
- <span class="text-xl font-bold text-blue-600 dark:text-blue-400">
|
|
|
- {{ number_format($customer->growth_value) }}
|
|
|
- </span>
|
|
|
- <span class="text-xs text-gray-500">分</span>
|
|
|
</div>
|
|
|
</td>
|
|
|
- <td class="px-6 py-4 whitespace-nowrap text-center">
|
|
|
- @php
|
|
|
- $levelBadge = match($customer->customer->group->growth_level_name ?? 'V0') {
|
|
|
- 'V0' => 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300',
|
|
|
- 'V1' => 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300',
|
|
|
- 'V2' => 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300',
|
|
|
- 'V3' => 'bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-300',
|
|
|
- 'V4' => 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-300',
|
|
|
- 'V5' => 'bg-pink-100 text-pink-700 dark:bg-pink-900/30 dark:text-pink-300',
|
|
|
- default => 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-300',
|
|
|
- };
|
|
|
- @endphp
|
|
|
- <span class="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium {{ $levelBadge }}">
|
|
|
- {{ $customer->customer->group->growth_level_name ?? 'V0' }}
|
|
|
- </span>
|
|
|
+ <td class="px-6 py-4 text-right">
|
|
|
+ <span class="text-xl font-bold text-blue-600">{{ number_format($customer->growth_value) }}</span>
|
|
|
+ <span class="text-xs text-gray-500">分</span>
|
|
|
</td>
|
|
|
- <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400 text-center">
|
|
|
- {{ $customer->first_order_completed_at ? \Carbon\Carbon::parse($customer->first_order_completed_at)->format('Y-m-d') : '-' }}
|
|
|
+ <td class="px-6 py-4 text-center">
|
|
|
+ <span class="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-purple-100 text-purple-700">{{ $customer->growth_level }}</span>
|
|
|
</td>
|
|
|
- <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400 text-center">
|
|
|
- {{ $customer->updated_at ? \Carbon\Carbon::parse($customer->updated_at)->diffForHumans() : '-' }}
|
|
|
+ <td class="px-6 py-4 text-center">
|
|
|
+ @if($customer->first_order_completed_at)
|
|
|
+ <span class="text-green-600">✓ 已完成</span>
|
|
|
+ @else
|
|
|
+ <span class="text-gray-500">- 未完成</span>
|
|
|
+ @endif
|
|
|
</td>
|
|
|
- <td class="px-6 py-4 whitespace-nowrap text-right">
|
|
|
+ <td class="px-6 py-4 text-center text-sm text-gray-500">{{ $customer->level_updated_at ? \Carbon\Carbon::parse($customer->level_updated_at)->format('Y-m-d') : '-' }}</td>
|
|
|
+ <td class="px-6 py-4 text-right">
|
|
|
<div class="flex items-center justify-end gap-2">
|
|
|
- <button
|
|
|
- onclick="viewDetails({{ $customer->customer_id }})"
|
|
|
- class="p-1 text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 transition-colors"
|
|
|
- title="查看详情"
|
|
|
- >
|
|
|
+ <button onclick="viewDetails({{ $customer->customer_id }})" class="text-blue-600 hover:text-blue-800" title="查看详情">
|
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
|
|
|
</svg>
|
|
|
</button>
|
|
|
- <button
|
|
|
- onclick="quickAdjust({{ $customer->customer_id }}, '{{ addslashes($customer->customer->name ?? '') }}')"
|
|
|
- class="p-1 text-green-600 hover:text-green-800 dark:text-green-400 dark:hover:text-green-300 transition-colors"
|
|
|
- title="快速调整"
|
|
|
- >
|
|
|
+ <button onclick="quickAdjust({{ $customer->customer_id }}, '{{ addslashes($customer->customer->name ?? $customer->customer->email ?? '') }}')" class="text-green-600 hover:text-green-800" title="快速调整">
|
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
|
|
|
</svg>
|
|
|
@@ -275,504 +190,170 @@
|
|
|
</tr>
|
|
|
@empty
|
|
|
<tr>
|
|
|
- <td colspan="8" class="px-6 py-12 text-center">
|
|
|
- <div class="flex flex-col items-center gap-4">
|
|
|
- <div class="w-24 h-24 bg-gray-100 dark:bg-gray-800 rounded-full flex items-center justify-center">
|
|
|
- <svg class="w-12 h-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/>
|
|
|
- </svg>
|
|
|
- </div>
|
|
|
- <div>
|
|
|
- <p class="text-lg font-medium text-gray-600 dark:text-gray-400 mb-1">
|
|
|
- 暂无会员成长值数据
|
|
|
- </p>
|
|
|
- <p class="text-sm text-gray-500 dark:text-gray-500">
|
|
|
- 当会员完成订单后,将自动记录成长值
|
|
|
- </p>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </td>
|
|
|
+ <td colspan="6" class="px-6 py-12 text-center text-gray-500">暂无会员成长值数据</td>
|
|
|
</tr>
|
|
|
@endforelse
|
|
|
</tbody>
|
|
|
</table>
|
|
|
</div>
|
|
|
-
|
|
|
- {{-- 分页 --}}
|
|
|
@if($customers->hasPages())
|
|
|
- <div class="px-6 py-4 border-t border-gray-200 dark:border-gray-800">
|
|
|
- {{ $customers->links() }}
|
|
|
- </div>
|
|
|
+ <div class="px-6 py-4 border-t">{{ $customers->links() }}</div>
|
|
|
@endif
|
|
|
</div>
|
|
|
|
|
|
- {{-- 调整成长值模态框 --}}
|
|
|
- <div id="adjustModal" class="fixed inset-0 z-50 hidden">
|
|
|
- <div class="absolute inset-0 bg-black bg-opacity-50 transition-opacity" onclick="closeAdjustModal()"></div>
|
|
|
- <div class="relative min-h-screen flex items-center justify-center p-4">
|
|
|
- <div class="bg-white dark:bg-gray-900 rounded-2xl shadow-2xl max-w-lg w-full transform transition-all duration-300 scale-95 opacity-0" id="modalContent">
|
|
|
- {{-- 模态框头部 --}}
|
|
|
- <div class="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700 bg-gradient-to-r from-purple-50 to-white dark:from-purple-900/20 dark:to-gray-900 rounded-t-2xl">
|
|
|
- <div class="flex items-center gap-3">
|
|
|
- <div class="w-10 h-10 bg-purple-100 dark:bg-purple-900/50 rounded-xl flex items-center justify-center">
|
|
|
- <svg class="w-5 h-5 text-purple-600 dark:text-purple-400" fill="currentColor" viewBox="0 0 20 20">
|
|
|
- <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/>
|
|
|
- </svg>
|
|
|
- </div>
|
|
|
- <h3 class="text-xl font-bold text-gray-900 dark:text-white">
|
|
|
- 调整成长值
|
|
|
- </h3>
|
|
|
- </div>
|
|
|
- <button onclick="closeAdjustModal()" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
|
|
- <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
|
|
- </svg>
|
|
|
- </button>
|
|
|
- </div>
|
|
|
-
|
|
|
- <form id="adjustForm" method="POST" class="p-6 space-y-5">
|
|
|
- @csrf
|
|
|
-
|
|
|
- {{-- 客户信息 --}}
|
|
|
- <div>
|
|
|
- <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
|
- 选择客户 <span class="text-red-500">*</span>
|
|
|
- </label>
|
|
|
- <select
|
|
|
- id="customerId"
|
|
|
- name="customer_id"
|
|
|
- class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 dark:bg-gray-800 dark:text-white transition-all"
|
|
|
- required
|
|
|
- >
|
|
|
- <option value="">请选择客户</option>
|
|
|
- @foreach($allCustomers as $allCustomer)
|
|
|
- <option value="{{ $allCustomer->customer_id }}">
|
|
|
- {{ $allCustomer->customer->name ?? 'Unknown' }} ({{ $allCustomer->customer->email ?? '' }}) - {{ number_format($allCustomer->growth_value) }}分
|
|
|
- </option>
|
|
|
- @endforeach
|
|
|
- </select>
|
|
|
+ {{-- 使用原生 alert 确认框代替复杂模态框 --}}
|
|
|
+ <script>
|
|
|
+ function openAdjustModal() {
|
|
|
+ // 获取客户列表用于选择
|
|
|
+ let customerOptions = '';
|
|
|
+ @foreach($allCustomers as $allCustomer)
|
|
|
+ customerOptions += `<option value="{{ $allCustomer->customer_id }}">{{ addslashes($allCustomer->customer->name ?? 'Unknown') }} ({{ $allCustomer->customer->email ?? '' }}) - {{ $allCustomer->growth_value }}分</option>`;
|
|
|
+ @endforeach
|
|
|
+
|
|
|
+ // 创建简单但完整的模态框
|
|
|
+ const modalHtml = `
|
|
|
+ <div id="simpleModal" style="position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);z-index:99999;display:flex;align-items:center;justify-content:center;">
|
|
|
+ <div style="background:white;border-radius:12px;width:450px;max-width:90%;padding:20px;box-shadow:0 20px 25px -5px rgba(0,0,0,0.1);">
|
|
|
+ <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;padding-bottom:10px;border-bottom:1px solid #e5e7eb;">
|
|
|
+ <h3 style="font-size:18px;font-weight:bold;color:#1f2937;">调整成长值</h3>
|
|
|
+ <button onclick="closeSimpleModal()" style="background:none;border:none;font-size:24px;cursor:pointer;color:#9ca3af;">×</button>
|
|
|
</div>
|
|
|
-
|
|
|
- {{-- 操作类型和数量 --}}
|
|
|
- <div class="grid grid-cols-2 gap-4">
|
|
|
- <div>
|
|
|
- <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
|
- 操作类型 <span class="text-red-500">*</span>
|
|
|
- </label>
|
|
|
- <div class="flex gap-2">
|
|
|
- <label class="flex-1 cursor-pointer">
|
|
|
- <input type="radio" name="action" value="add" class="peer sr-only" checked onchange="updateAmountField()">
|
|
|
- <div class="text-center px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg peer-checked:bg-purple-50 peer-checked:border-purple-500 peer-checked:text-purple-700 dark:peer-checked:bg-purple-900/30 transition-all">
|
|
|
- <span class="text-sm font-medium">➕ 增加</span>
|
|
|
- </div>
|
|
|
+ <form id="simpleAdjustForm">
|
|
|
+ <input type="hidden" name="_token" value="{{ csrf_token() }}">
|
|
|
+ <div style="margin-bottom:15px;">
|
|
|
+ <label style="display:block;font-size:14px;font-weight:500;margin-bottom:5px;color:#374151;">选择客户 *</label>
|
|
|
+ <select name="customer_id" id="simpleCustomerId" style="width:100%;padding:8px 12px;border:1px solid #d1d5db;border-radius:8px;" required>
|
|
|
+ <option value="">请选择客户</option>
|
|
|
+ ${customerOptions}
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ <div style="margin-bottom:15px;">
|
|
|
+ <label style="display:block;font-size:14px;font-weight:500;margin-bottom:5px;color:#374151;">操作类型 *</label>
|
|
|
+ <div style="display:flex;gap:10px;">
|
|
|
+ <label style="flex:1;padding:8px;text-align:center;border:2px solid #d1d5db;border-radius:8px;cursor:pointer;">
|
|
|
+ <input type="radio" name="action" value="add" checked style="margin-right:5px;"> 增加
|
|
|
</label>
|
|
|
- <label class="flex-1 cursor-pointer">
|
|
|
- <input type="radio" name="action" value="deduct" class="peer sr-only" onchange="updateAmountField()">
|
|
|
- <div class="text-center px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg peer-checked:bg-red-50 peer-checked:border-red-500 peer-checked:text-red-700 dark:peer-checked:bg-red-900/30 transition-all">
|
|
|
- <span class="text-sm font-medium">➖ 减少</span>
|
|
|
- </div>
|
|
|
+ <label style="flex:1;padding:8px;text-align:center;border:2px solid #d1d5db;border-radius:8px;cursor:pointer;">
|
|
|
+ <input type="radio" name="action" value="deduct" style="margin-right:5px;"> 减少
|
|
|
</label>
|
|
|
</div>
|
|
|
</div>
|
|
|
-
|
|
|
- <div>
|
|
|
- <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
|
- 数量 <span class="text-red-500">*</span>
|
|
|
- </label>
|
|
|
- <input
|
|
|
- type="number"
|
|
|
- name="amount"
|
|
|
- id="amount"
|
|
|
- min="1"
|
|
|
- class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 dark:bg-gray-800 dark:text-white transition-all"
|
|
|
- placeholder="输入数量"
|
|
|
- required
|
|
|
- />
|
|
|
+ <div style="margin-bottom:15px;">
|
|
|
+ <label style="display:block;font-size:14px;font-weight:500;margin-bottom:5px;color:#374151;">数量 *</label>
|
|
|
+ <input type="number" name="amount" min="1" required style="width:100%;padding:8px 12px;border:1px solid #d1d5db;border-radius:8px;" placeholder="输入成长值数量">
|
|
|
</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- {{-- 调整原因 --}}
|
|
|
- <div>
|
|
|
- <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
|
- 调整原因
|
|
|
- </label>
|
|
|
- <div class="relative">
|
|
|
- <span class="absolute left-3 top-2.5 text-gray-400">📝</span>
|
|
|
- <input
|
|
|
- type="text"
|
|
|
- name="description"
|
|
|
- id="description"
|
|
|
- class="w-full pl-9 pr-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 dark:bg-gray-800 dark:text-white transition-all"
|
|
|
- placeholder="例如:管理员手动调整"
|
|
|
- value="管理员调整"
|
|
|
- />
|
|
|
+ <div style="margin-bottom:20px;">
|
|
|
+ <label style="display:block;font-size:14px;font-weight:500;margin-bottom:5px;color:#374151;">调整原因</label>
|
|
|
+ <input type="text" name="description" value="管理员手动调整" style="width:100%;padding:8px 12px;border:1px solid #d1d5db;border-radius:8px;" placeholder="调整原因">
|
|
|
</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- {{-- 提示信息 --}}
|
|
|
- <div class="bg-gradient-to-r from-purple-50 to-purple-100 dark:from-purple-900/20 dark:to-purple-800/20 border-l-4 border-purple-500 rounded-lg p-3">
|
|
|
- <div class="flex items-start gap-2">
|
|
|
- <svg class="w-5 h-5 text-purple-500 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
|
|
|
- <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/>
|
|
|
- </svg>
|
|
|
- <div class="flex-1">
|
|
|
- <p class="text-sm font-medium text-purple-800 dark:text-purple-300">
|
|
|
- 温馨提示
|
|
|
- </p>
|
|
|
- <p class="text-xs text-purple-700 dark:text-purple-400 mt-0.5">
|
|
|
- 系统会自动计算并更新会员等级
|
|
|
- </p>
|
|
|
- </div>
|
|
|
+ <div style="display:flex;gap:10px;">
|
|
|
+ <button type="button" onclick="closeSimpleModal()" style="flex:1;padding:10px;background:#f3f4f6;border:none;border-radius:8px;cursor:pointer;">取消</button>
|
|
|
+ <button type="submit" style="flex:1;padding:10px;background:#7c3aed;color:white;border:none;border-radius:8px;cursor:pointer;">确认调整</button>
|
|
|
</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- {{-- 底部按钮 --}}
|
|
|
- <div class="flex justify-end gap-3 pt-2">
|
|
|
- <button
|
|
|
- type="button"
|
|
|
- onclick="closeAdjustModal()"
|
|
|
- class="px-4 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-all"
|
|
|
- >
|
|
|
- 取消
|
|
|
- </button>
|
|
|
- <button
|
|
|
- type="submit"
|
|
|
- class="px-4 py-2 bg-gradient-to-r from-purple-600 to-purple-700 text-white rounded-lg hover:from-purple-700 hover:to-purple-800 transition-all shadow-md"
|
|
|
- >
|
|
|
- 确认调整
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </form>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- {{-- 详情模态框 --}}
|
|
|
- <div id="detailModal" class="fixed inset-0 z-50 hidden">
|
|
|
- <div class="absolute inset-0 bg-black bg-opacity-50 transition-opacity" onclick="closeDetailModal()"></div>
|
|
|
- <div class="relative min-h-screen flex items-center justify-center p-4">
|
|
|
- <div class="bg-white dark:bg-gray-900 rounded-2xl shadow-2xl max-w-2xl w-full transform transition-all duration-300 scale-95 opacity-0" id="detailModalContent">
|
|
|
- <div class="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700 bg-gradient-to-r from-blue-50 to-white dark:from-blue-900/20 dark:to-gray-900 rounded-t-2xl">
|
|
|
- <div class="flex items-center gap-3">
|
|
|
- <div class="w-10 h-10 bg-blue-100 dark:bg-blue-900/50 rounded-xl flex items-center justify-center">
|
|
|
- <svg class="w-5 h-5 text-blue-600 dark:text-blue-400" fill="currentColor" viewBox="0 0 20 20">
|
|
|
- <path d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"/>
|
|
|
- </svg>
|
|
|
- </div>
|
|
|
- <h3 class="text-xl font-bold text-gray-900 dark:text-white" id="detailTitle">
|
|
|
- 会员详情
|
|
|
- </h3>
|
|
|
- </div>
|
|
|
- <button onclick="closeDetailModal()" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
|
|
- <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
|
|
- </svg>
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- <div id="detailContent" class="p-6">
|
|
|
- <!-- 动态内容 -->
|
|
|
+ </form>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <script>
|
|
|
- // 打开调整模态框
|
|
|
- function openAdjustModal() {
|
|
|
- const modal = document.getElementById('adjustModal');
|
|
|
- const modalContent = document.getElementById('modalContent');
|
|
|
- modal.classList.remove('hidden');
|
|
|
- setTimeout(() => {
|
|
|
- modalContent.classList.remove('scale-95', 'opacity-0');
|
|
|
- modalContent.classList.add('scale-100', 'opacity-100');
|
|
|
- }, 10);
|
|
|
- document.getElementById('adjustForm').action = '';
|
|
|
- document.getElementById('customerId').value = '';
|
|
|
- document.querySelector('input[name="action"][value="add"]').checked = true;
|
|
|
- document.getElementById('amount').value = '';
|
|
|
- document.getElementById('description').value = '管理员调整';
|
|
|
- updateAmountField();
|
|
|
- }
|
|
|
-
|
|
|
- // 关闭调整模态框
|
|
|
- function closeAdjustModal() {
|
|
|
- const modalContent = document.getElementById('modalContent');
|
|
|
- modalContent.classList.remove('scale-100', 'opacity-100');
|
|
|
- modalContent.classList.add('scale-95', 'opacity-0');
|
|
|
- setTimeout(() => {
|
|
|
- document.getElementById('adjustModal').classList.add('hidden');
|
|
|
- }, 200);
|
|
|
- }
|
|
|
-
|
|
|
- // 更新数量字段提示
|
|
|
- function updateAmountField() {
|
|
|
- const action = document.querySelector('input[name="action"]:checked');
|
|
|
- const amountInput = document.getElementById('amount');
|
|
|
-
|
|
|
- if (action && action.value === 'add') {
|
|
|
- amountInput.placeholder = '输入增加的数值 (正整数)';
|
|
|
- } else {
|
|
|
- amountInput.placeholder = '输入减少的数值 (正整数)';
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 快速调整指定客户
|
|
|
- function quickAdjust(customerId, customerName) {
|
|
|
- openAdjustModal();
|
|
|
- document.getElementById('adjustForm').action = "{{ url('admin/growth-value') }}/" + customerId + "/adjust";
|
|
|
- document.getElementById('customerId').value = customerId;
|
|
|
- document.getElementById('amount').focus();
|
|
|
- showSuccessMessage(`正在为 ${customerName} 调整成长值`);
|
|
|
- }
|
|
|
+ `;
|
|
|
|
|
|
- // 查看详情
|
|
|
- function viewDetails(customerId) {
|
|
|
- const modal = document.getElementById('detailModal');
|
|
|
- const modalContent = document.getElementById('detailModalContent');
|
|
|
- modal.classList.remove('hidden');
|
|
|
- setTimeout(() => {
|
|
|
- modalContent.classList.remove('scale-95', 'opacity-0');
|
|
|
- modalContent.classList.add('scale-100', 'opacity-100');
|
|
|
- }, 10);
|
|
|
+ document.body.insertAdjacentHTML('beforeend', modalHtml);
|
|
|
|
|
|
- fetch(`{{ url('admin/growth-value') }}/${customerId}/detail`)
|
|
|
- .then(response => response.json())
|
|
|
- .then(data => {
|
|
|
- if (data.success) {
|
|
|
- document.getElementById('detailContent').innerHTML = `
|
|
|
- <div class="space-y-4">
|
|
|
- <div class="flex items-center gap-4 p-4 bg-gradient-to-r from-purple-50 to-blue-50 dark:from-purple-900/20 dark:to-blue-900/20 rounded-xl">
|
|
|
- <div class="w-16 h-16 bg-gradient-to-br from-purple-400 to-purple-600 rounded-full flex items-center justify-center text-white text-2xl font-bold">
|
|
|
- ${data.customer.name ? data.customer.name.charAt(0) : 'U'}
|
|
|
- </div>
|
|
|
- <div class="flex-1">
|
|
|
- <h4 class="text-lg font-bold text-gray-900 dark:text-white">${data.customer.name || 'N/A'}</h4>
|
|
|
- <p class="text-sm text-gray-600 dark:text-gray-400">${data.customer.email || 'N/A'}</p>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ document.getElementById('simpleAdjustForm').addEventListener('submit', function(e) {
|
|
|
+ e.preventDefault();
|
|
|
+ const formData = new FormData(this);
|
|
|
+ let amount = parseInt(formData.get('amount'));
|
|
|
+ const action = formData.get('action');
|
|
|
+ if (action === 'deduct') amount = -amount;
|
|
|
|
|
|
- <div class="grid grid-cols-2 gap-4">
|
|
|
- <div class="text-center p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
|
|
|
- <p class="text-sm text-gray-600 dark:text-gray-400">当前成长值</p>
|
|
|
- <p class="text-2xl font-bold text-blue-600 dark:text-blue-400">${formatNumber(data.growth_value)} 分</p>
|
|
|
- </div>
|
|
|
- <div class="text-center p-3 bg-purple-50 dark:bg-purple-900/20 rounded-lg">
|
|
|
- <p class="text-sm text-gray-600 dark:text-gray-400">当前等级</p>
|
|
|
- <p class="text-2xl font-bold text-purple-600 dark:text-purple-400">${data.level_name || 'V0'}</p>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- ${data.growth_logs && data.growth_logs.length > 0 ? `
|
|
|
- <div>
|
|
|
- <h5 class="font-medium text-gray-900 dark:text-white mb-3">成长值变动记录</h5>
|
|
|
- <div class="space-y-2 max-h-64 overflow-y-auto">
|
|
|
- ${data.growth_logs.map(log => `
|
|
|
- <div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
|
|
- <div class="flex items-center gap-2">
|
|
|
- <span class="text-sm ${log.amount > 0 ? 'text-green-600' : 'text-red-600'} font-medium">
|
|
|
- ${log.amount > 0 ? '+' : ''}${log.amount}
|
|
|
- </span>
|
|
|
- <span class="text-sm text-gray-600 dark:text-gray-400">${log.description || '系统调整'}</span>
|
|
|
- </div>
|
|
|
- <span class="text-xs text-gray-500">${log.created_at}</span>
|
|
|
- </div>
|
|
|
- `).join('')}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- ` : '<p class="text-center text-gray-500 py-4">暂无变动记录</p>'}
|
|
|
- </div>
|
|
|
- `;
|
|
|
- } else {
|
|
|
- document.getElementById('detailContent').innerHTML = `
|
|
|
- <div class="text-center py-8 text-red-500">
|
|
|
- 加载失败,请重试
|
|
|
- </div>
|
|
|
- `;
|
|
|
- }
|
|
|
+ fetch('{{ route("admin.growth-value.adjust-from-list") }}', {
|
|
|
+ method: 'POST',
|
|
|
+ body: formData,
|
|
|
+ headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
|
|
})
|
|
|
- .catch(error => {
|
|
|
- console.error('Error:', error);
|
|
|
- document.getElementById('detailContent').innerHTML = `
|
|
|
- <div class="text-center py-8 text-red-500">
|
|
|
- 加载失败,请重试
|
|
|
- </div>
|
|
|
- `;
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // 关闭详情模态框
|
|
|
- function closeDetailModal() {
|
|
|
- const modalContent = document.getElementById('detailModalContent');
|
|
|
- modalContent.classList.remove('scale-100', 'opacity-100');
|
|
|
- modalContent.classList.add('scale-95', 'opacity-0');
|
|
|
- setTimeout(() => {
|
|
|
- document.getElementById('detailModal').classList.add('hidden');
|
|
|
- }, 200);
|
|
|
+ .then(response => response.json())
|
|
|
+ .then(data => {
|
|
|
+ if (data.success) {
|
|
|
+ alert(data.message || '调整成功!');
|
|
|
+ closeSimpleModal();
|
|
|
+ location.reload();
|
|
|
+ } else {
|
|
|
+ alert(data.message || '调整失败');
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(error => {
|
|
|
+ alert('网络错误,请重试');
|
|
|
+ });
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
- // 格式化数字
|
|
|
- function formatNumber(num) {
|
|
|
- return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
|
+ function closeSimpleModal() {
|
|
|
+ const modal = document.getElementById('simpleModal');
|
|
|
+ if (modal) modal.remove();
|
|
|
}
|
|
|
|
|
|
- // 表单提交
|
|
|
- document.getElementById('adjustForm').addEventListener('submit', function(e) {
|
|
|
- e.preventDefault();
|
|
|
-
|
|
|
- const customerId = document.getElementById('customerId').value;
|
|
|
- if (!customerId) {
|
|
|
- showErrorMessage('请选择客户');
|
|
|
- return;
|
|
|
- }
|
|
|
+ function quickAdjust(customerId, customerName) {
|
|
|
+ // 快速调整:直接使用确认框
|
|
|
+ const amount = prompt(`为 ${customerName} 调整成长值\n输入正数表示增加,负数表示减少`, "100");
|
|
|
+ if (amount === null) return;
|
|
|
|
|
|
- const amount = parseInt(document.getElementById('amount').value);
|
|
|
- if (!amount || amount <= 0) {
|
|
|
- showErrorMessage('请输入有效的数量');
|
|
|
+ const finalAmount = parseInt(amount);
|
|
|
+ if (isNaN(finalAmount) || finalAmount === 0) {
|
|
|
+ alert('请输入有效的数字');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- const action = document.querySelector('input[name="action"]:checked').value;
|
|
|
- let finalAmount = amount;
|
|
|
- if (action === 'deduct') {
|
|
|
- finalAmount = -Math.abs(amount);
|
|
|
- }
|
|
|
-
|
|
|
const formData = new FormData();
|
|
|
formData.append('_token', '{{ csrf_token() }}');
|
|
|
formData.append('customer_id', customerId);
|
|
|
formData.append('amount', finalAmount);
|
|
|
- formData.append('description', document.getElementById('description').value);
|
|
|
-
|
|
|
- const action_url = this.action || "{{ route('admin.growth-value.adjust') }}";
|
|
|
+ formData.append('description', '管理员快速调整');
|
|
|
|
|
|
- fetch(action_url, {
|
|
|
+ fetch('{{ route("admin.growth-value.adjust-from-list") }}', {
|
|
|
method: 'POST',
|
|
|
body: formData,
|
|
|
- headers: {
|
|
|
- 'X-Requested-With': 'XMLHttpRequest'
|
|
|
- }
|
|
|
+ headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
|
|
})
|
|
|
.then(response => response.json())
|
|
|
.then(data => {
|
|
|
if (data.success) {
|
|
|
- showSuccessMessage(data.message || '成长值调整成功!');
|
|
|
- closeAdjustModal();
|
|
|
- setTimeout(() => {
|
|
|
- location.reload();
|
|
|
- }, 1500);
|
|
|
+ alert(data.message || '调整成功!');
|
|
|
+ location.reload();
|
|
|
} else {
|
|
|
- showErrorMessage(data.message || '调整失败,请重试');
|
|
|
+ alert(data.message || '调整失败');
|
|
|
}
|
|
|
})
|
|
|
.catch(error => {
|
|
|
- console.error('Error:', error);
|
|
|
- showErrorMessage('网络错误,请重试');
|
|
|
+ alert('网络错误,请重试');
|
|
|
});
|
|
|
- });
|
|
|
-
|
|
|
- // 显示成功消息
|
|
|
- function showSuccessMessage(message) {
|
|
|
- const toast = document.createElement('div');
|
|
|
- toast.className = 'fixed top-20 right-4 bg-green-500 text-white px-5 py-3 rounded-lg shadow-lg z-50 flex items-center gap-2 animate-fade-in';
|
|
|
- toast.innerHTML = `
|
|
|
- <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
|
|
- <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
|
|
|
- </svg>
|
|
|
- <span>${message}</span>
|
|
|
- `;
|
|
|
- document.body.appendChild(toast);
|
|
|
- setTimeout(() => toast.remove(), 3000);
|
|
|
}
|
|
|
|
|
|
- // 显示错误消息
|
|
|
- function showErrorMessage(message) {
|
|
|
- const toast = document.createElement('div');
|
|
|
- toast.className = 'fixed top-20 right-4 bg-red-500 text-white px-5 py-3 rounded-lg shadow-lg z-50 flex items-center gap-2 animate-fade-in';
|
|
|
- toast.innerHTML = `
|
|
|
- <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
|
|
- <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
|
|
|
- </svg>
|
|
|
- <span>${message}</span>
|
|
|
- `;
|
|
|
- document.body.appendChild(toast);
|
|
|
- setTimeout(() => toast.remove(), 3000);
|
|
|
+ function viewDetails(customerId) {
|
|
|
+ fetch(`{{ url('admin/growth-value') }}/${customerId}`)
|
|
|
+ .then(response => response.json())
|
|
|
+ .then(data => {
|
|
|
+ if (data.success) {
|
|
|
+ let historyHtml = '';
|
|
|
+ if (data.history && data.history.length > 0) {
|
|
|
+ historyHtml = '<div style="margin-top:15px;"><h4 style="font-weight:bold;margin-bottom:10px;">变动记录</h4>';
|
|
|
+ data.history.forEach(log => {
|
|
|
+ historyHtml += `<div style="padding:8px;border-bottom:1px solid #e5e7eb;display:flex;justify-content:space-between;">
|
|
|
+ <span style="color:${log.growth_value_earned > 0 ? '#16a34a' : '#dc2626'}">${log.growth_value_earned > 0 ? '+' : ''}${log.growth_value_earned}</span>
|
|
|
+ <span>${log.description || '-'}</span>
|
|
|
+ <span style="color:#6b7280;font-size:12px;">${log.created_at}</span>
|
|
|
+ </div>`;
|
|
|
+ });
|
|
|
+ historyHtml += '</div>';
|
|
|
+ } else {
|
|
|
+ historyHtml = '<p style="margin-top:15px;color:#9ca3af;">暂无变动记录</p>';
|
|
|
+ }
|
|
|
+
|
|
|
+ alert(`客户:${data.customer.name || data.customer.email}\n当前成长值:${data.growth_value}分\n当前等级:${data.growth_level || 'V0'}\n首单时间:${data.first_order_completed_at || '未完成'}\n\n${historyHtml}`);
|
|
|
+ } else {
|
|
|
+ alert('获取详情失败');
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(error => {
|
|
|
+ alert('获取详情失败');
|
|
|
+ });
|
|
|
}
|
|
|
-
|
|
|
- // ESC 键关闭模态框
|
|
|
- document.addEventListener('keydown', function(e) {
|
|
|
- if (e.key === 'Escape') {
|
|
|
- if (!document.getElementById('adjustModal').classList.contains('hidden')) {
|
|
|
- closeAdjustModal();
|
|
|
- }
|
|
|
- if (!document.getElementById('detailModal').classList.contains('hidden')) {
|
|
|
- closeDetailModal();
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
</script>
|
|
|
-
|
|
|
- <style>
|
|
|
- @keyframes fade-in {
|
|
|
- from {
|
|
|
- opacity: 0;
|
|
|
- transform: translateY(-10px);
|
|
|
- }
|
|
|
- to {
|
|
|
- opacity: 1;
|
|
|
- transform: translateY(0);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .animate-fade-in {
|
|
|
- animation: fade-in 0.3s ease-out;
|
|
|
- }
|
|
|
-
|
|
|
- .scale-95 {
|
|
|
- transform: scale(0.95);
|
|
|
- }
|
|
|
-
|
|
|
- .scale-100 {
|
|
|
- transform: scale(1);
|
|
|
- }
|
|
|
-
|
|
|
- .opacity-0 {
|
|
|
- opacity: 0;
|
|
|
- }
|
|
|
-
|
|
|
- .opacity-100 {
|
|
|
- opacity: 1;
|
|
|
- }
|
|
|
-
|
|
|
- .transition-all {
|
|
|
- transition: all 0.2s ease;
|
|
|
- }
|
|
|
-
|
|
|
- .duration-300 {
|
|
|
- transition-duration: 300ms;
|
|
|
- }
|
|
|
-
|
|
|
- .ease-out {
|
|
|
- transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
|
|
|
- }
|
|
|
-
|
|
|
- /* 滚动条美化 */
|
|
|
- ::-webkit-scrollbar {
|
|
|
- width: 8px;
|
|
|
- height: 8px;
|
|
|
- }
|
|
|
-
|
|
|
- ::-webkit-scrollbar-track {
|
|
|
- background: #f1f1f1;
|
|
|
- border-radius: 10px;
|
|
|
- }
|
|
|
-
|
|
|
- ::-webkit-scrollbar-thumb {
|
|
|
- background: #c1c1c1;
|
|
|
- border-radius: 10px;
|
|
|
- }
|
|
|
-
|
|
|
- ::-webkit-scrollbar-thumb:hover {
|
|
|
- background: #a8a8a8;
|
|
|
- }
|
|
|
- </style>
|
|
|
</x-admin::layouts>
|