Tree hai 5 horas
pai
achega
3827ce1041

+ 16 - 6
app/Http/Controllers/Admin/CommonConfigController.php

@@ -10,9 +10,7 @@ class CommonConfigController extends Controller
 {
     public function index()
     {
-        // 从 Redis 获取公用配置数据
-        $configData = Redis::get("GameConfigX_Common");
-        $config = $configData ? json_decode($configData, true) : [
+        $defaultConfig = [
             ["RechargeMin" => 0, "RechargeMax" => 1, "FreeWinMax" => 1000, "RechargeMaxPercent" => 0, "TaxPercent" => 100],
             ["RechargeMin" => 1, "RechargeMax" => 101, "FreeWinMax" => 20, "RechargeMaxPercent" => 80, "TaxPercent" => 100],
             ["RechargeMin" => 101, "RechargeMax" => 501, "FreeWinMax" => 100, "RechargeMaxPercent" => 70, "TaxPercent" => 95],
@@ -21,14 +19,26 @@ class CommonConfigController extends Controller
             ["RechargeMin" => 10001, "RechargeMax" => 99999999, "FreeWinMax" => 0, "RechargeMaxPercent" => 50, "TaxPercent" => 80]
         ];
 
-        return view('admin.common_config.index', compact('config'));
+        // 从 Redis 获取公用配置数据
+        $configData = Redis::get("GameConfigX_Common");
+        $config = $configData ? json_decode($configData, true) : $defaultConfig;
+
+        // 从 Redis 获取库存模式配置数据
+        $specialConfigData = Redis::get("GameConfigX_Special");
+        $specialConfig = $specialConfigData ? json_decode($specialConfigData, true) : $defaultConfig;
+
+        return view('admin.common_config.index', compact('config', 'specialConfig'));
     }
 
     public function update(Request $request)
     {
         $config = $request->input('config');
+        $configType = $request->input('config_type', 'common'); // 获取配置类型,默认为 common
+
+        $redisKey = $configType === 'special' ? 'GameConfigX_Special' : 'GameConfigX_Common';
 
         \Log::info('公用配置更新请求', [
+            'config_type' => $configType,
             'config' => $config,
             'all_data' => $request->all()
         ]);
@@ -45,8 +55,8 @@ class CommonConfigController extends Controller
                 throw new \Exception('JSON格式错误: ' . json_last_error_msg());
             }
 
-            Redis::set("GameConfigX_Common", $config);
-            \Log::info('公用配置更新成功');
+            Redis::set($redisKey, $config);
+            \Log::info('公用配置更新成功', ['redis_key' => $redisKey]);
             return response()->json(['status' => 'success', 'message' => '更新成功']);
         } catch (\Exception $e) {
             \Log::error('公用配置更新失败:异常', ['message' => $e->getMessage()]);

+ 341 - 0
app/Http/Controllers/Admin/StockModeController.php

@@ -0,0 +1,341 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+use App\Http\Controllers\Controller;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\DB;
+
+class StockModeController extends Controller
+{
+    /**
+     * 显示库存模式配置页面
+     */
+    public function index()
+    {
+        // 获取 SystemStatusInfo 中的配置
+        $systemConfig = DB::connection('write')
+            ->table('QPAccountsDB.dbo.SystemStatusInfo')
+            ->whereIn('StatusName', ['StockMode2BetLevels', 'StockMode2RevenueRatio', 'StockMode2SwitchRecharge'])
+            ->get()
+            ->keyBy('StatusName');
+
+        // 解析 StockMode2BetLevels
+        $betLevelsString = $systemConfig['StockMode2BetLevels']->StatusString ?? '0|2|10|10000,0|0|100|1000';
+        $betLevelsParts = explode(',', $betLevelsString);
+        $betMaxLimits = explode('|', $betLevelsParts[0] ?? '0|2|10|10000');
+        $rechargeMinLimits = explode('|', $betLevelsParts[1] ?? '0|0|100|1000');
+
+        // 获取 RoomStockStatic2 数据
+        $roomStocks = DB::connection('write')
+            ->table('QPPlatformDB.dbo.RoomStockStatic2')
+            ->where('GameID', 0)
+            ->orderBy('SortID')
+            ->get();
+
+        return view('admin.stock_mode.index', compact(
+            'systemConfig',
+            'betMaxLimits',
+            'rechargeMinLimits',
+            'roomStocks'
+        ));
+    }
+
+    /**
+     * 更新系统配置参数
+     */
+    public function updateSystemConfig(Request $request)
+    {
+        try {
+            $betMaxLimits = $request->input('bet_max_limits', []);
+            $rechargeMinLimits = $request->input('recharge_min_limits', []);
+            $revenueRatio = $request->input('revenue_ratio', 50);
+            $switchRecharge = $request->input('switch_recharge', 100);
+
+            // 构建 StockMode2BetLevels 的 StatusString
+            $betMaxString = implode('|', $betMaxLimits);
+            $rechargeMinString = implode('|', $rechargeMinLimits);
+            $betLevelsString = $betMaxString . ',' . $rechargeMinString;
+
+            // 更新 StockMode2BetLevels
+            DB::connection('write')
+                ->table('QPAccountsDB.dbo.SystemStatusInfo')
+                ->where('StatusName', 'StockMode2BetLevels')
+                ->update([
+                    'StatusString' => $betLevelsString,
+                    'StatusValue' => 0
+                ]);
+
+            // 更新 StockMode2RevenueRatio
+            DB::connection('write')
+                ->table('QPAccountsDB.dbo.SystemStatusInfo')
+                ->where('StatusName', 'StockMode2RevenueRatio')
+                ->update([
+                    'StatusValue' => $revenueRatio
+                ]);
+
+            // 更新 StockMode2SwitchRecharge
+            DB::connection('write')
+                ->table('QPAccountsDB.dbo.SystemStatusInfo')
+                ->where('StatusName', 'StockMode2SwitchRecharge')
+                ->update([
+                    'StatusValue' => $switchRecharge
+                ]);
+
+            \Log::info('库存模式系统配置更新成功', [
+                'bet_levels' => $betLevelsString,
+                'revenue_ratio' => $revenueRatio,
+                'switch_recharge' => $switchRecharge
+            ]);
+
+            return response()->json(['status' => 'success', 'message' => '系统配置更新成功']);
+        } catch (\Exception $e) {
+            \Log::error('库存模式系统配置更新失败', ['error' => $e->getMessage()]);
+            return response()->json(['status' => 'error', 'message' => '更新失败: ' . $e->getMessage()]);
+        }
+    }
+
+    /**
+     * 更新房间库存配置
+     */
+    public function updateRoomStock(Request $request)
+    {
+        try {
+            $sortId = $request->input('sort_id');
+            $levelBase = $request->input('level_base');
+
+            if (!$sortId || !is_numeric($levelBase)) {
+                return response()->json(['status' => 'error', 'message' => '参数错误']);
+            }
+
+            // 更新 RoomStockStatic2
+            $affected = DB::connection('write')
+                ->table('QPPlatformDB.dbo.RoomStockStatic2')
+                ->where('GameID', 0)
+                ->where('SortID', $sortId)
+                ->update([
+                    'LevelBase' => $levelBase
+                ]);
+
+            if ($affected === 0) {
+                // 如果没有记录,则插入新记录
+                DB::connection('write')
+                    ->table('QPPlatformDB.dbo.RoomStockStatic2')
+                    ->insert([
+                        'GameID' => 0,
+                        'SortID' => $sortId,
+                        'Stock' => 0,
+                        'LevelBase' => $levelBase,
+                        'Revenue' => 0,
+                        'RevenueD' => 0
+                    ]);
+            }
+
+            \Log::info('房间库存配置更新成功', [
+                'sort_id' => $sortId,
+                'level_base' => $levelBase
+            ]);
+
+            return response()->json(['status' => 'success', 'message' => '房间库存配置更新成功']);
+        } catch (\Exception $e) {
+            \Log::error('房间库存配置更新失败', ['error' => $e->getMessage()]);
+            return response()->json(['status' => 'error', 'message' => '更新失败: ' . $e->getMessage()]);
+        }
+    }
+
+    /**
+     * 更新库存(增减)
+     */
+    public function updateStock(Request $request)
+    {
+        try {
+            $sortId = $request->input('sort_id');
+            $adjustValue = $request->input('adjust_value'); // 已经是数据库值(前端已乘以100)
+
+            if (!$sortId || !is_numeric($adjustValue)) {
+                return response()->json(['status' => 'error', 'message' => '参数错误']);
+            }
+
+            // 使用 increment/decrement 方法更新库存
+            $query = DB::connection('write')
+                ->table('QPPlatformDB.dbo.RoomStockStatic2')
+                ->where('GameID', 0)
+                ->where('SortID', $sortId);
+
+            if ($adjustValue > 0) {
+                $query->increment('Stock', $adjustValue);
+            } elseif ($adjustValue < 0) {
+                $query->decrement('Stock', abs($adjustValue));
+            }
+
+            // 获取更新后的库存值
+            $roomStock = DB::connection('write')
+                ->table('QPPlatformDB.dbo.RoomStockStatic2')
+                ->where('GameID', 0)
+                ->where('SortID', $sortId)
+                ->first();
+
+            if (!$roomStock) {
+                return response()->json(['status' => 'error', 'message' => '房间数据不存在']);
+            }
+
+            \Log::info('库存更新成功', [
+                'sort_id' => $sortId,
+                'adjust_value' => $adjustValue,
+                'new_stock' => $roomStock->Stock
+            ]);
+
+            return response()->json([
+                'status' => 'success',
+                'message' => '库存更新成功',
+                'new_stock' => $roomStock->Stock
+            ]);
+        } catch (\Exception $e) {
+            \Log::error('库存更新失败', ['error' => $e->getMessage()]);
+            return response()->json(['status' => 'error', 'message' => '更新失败: ' . $e->getMessage()]);
+        }
+    }
+
+    /**
+     * 获取房间库存统计信息
+     */
+    public function getRoomStockStats()
+    {
+        try {
+            $roomStocks = DB::connection('write')
+                ->table('QPPlatformDB.dbo.RoomStockStatic2')
+                ->where('GameID', 0)
+                ->orderBy('SortID')
+                ->get();
+
+            return response()->json(['status' => 'success', 'data' => $roomStocks]);
+        } catch (\Exception $e) {
+            \Log::error('获取房间库存统计失败', ['error' => $e->getMessage()]);
+            return response()->json(['status' => 'error', 'message' => '获取失败: ' . $e->getMessage()]);
+        }
+    }
+
+    /**
+     * 获取库存快照历史数据
+     */
+    public function getSnapshotHistory(Request $request)
+    {
+        try {
+            $sortId = $request->input('sort_id', 1);
+            $minutes = $request->input('minutes', 60); // 默认1小时
+
+            // 计算开始时间
+            $startTime = date('Y-m-d H:i:s', strtotime("-{$minutes} minutes"));
+
+            // 查询起始时间之前的最后一条数据(用于填充前置空白)
+            $previousSnapshot = DB::connection('write')
+                ->table('QPPlatformDB.dbo.RoomStockSnapshot2')
+                ->where('GameID', 0)
+                ->where('SortID', $sortId)
+                ->where('CreateTime', '<', $startTime)
+                ->orderBy('CreateTime', 'desc')
+                ->first();
+
+            // 查询时间范围内的快照数据
+            $snapshots = DB::connection('write')
+                ->table('QPPlatformDB.dbo.RoomStockSnapshot2')
+                ->where('GameID', 0)
+                ->where('SortID', $sortId)
+                ->where('CreateTime', '>=', $startTime)
+                ->orderBy('CreateTime', 'asc')
+                ->get();
+
+            // 处理数据
+            $data = [];
+
+            // 添加前置数据(如果存在)
+            if ($previousSnapshot) {
+                $stock = $previousSnapshot->Stock;
+                $levelBase = $previousSnapshot->LevelBase;
+                $stockRatio = $levelBase > 0 ? round($stock / $levelBase, 2) : 0;
+                $zValue = $this->calculateZValue($stock, $levelBase);
+
+                $data[] = [
+                    'id' => $previousSnapshot->ID,
+                    'stock' => round($stock / 100, 2),
+                    'levelBase' => round($levelBase / 100, 2),
+                    'revenue' => round($previousSnapshot->Revenue / 100, 2),
+                    'revenueD' => round($previousSnapshot->RevenueD / 100, 2),
+                    'stockChange' => round($previousSnapshot->StockChange / 100, 2),
+                    'revenueChange' => round($previousSnapshot->RevenueChange / 100, 2),
+                    'stockRatio' => $stockRatio,
+                    'zValue' => $zValue,
+                    'createTime' => $previousSnapshot->CreateTime,
+                    'timestamp' => strtotime($previousSnapshot->CreateTime) * 1000,
+                    'isPrevious' => true // 标记为前置数据
+                ];
+            }
+
+            // 添加时间范围内的数据
+            foreach ($snapshots as $snapshot) {
+                $stock = $snapshot->Stock;
+                $levelBase = $snapshot->LevelBase;
+                $stockRatio = $levelBase > 0 ? round($stock / $levelBase, 2) : 0;
+                $zValue = $this->calculateZValue($stock, $levelBase);
+
+                $data[] = [
+                    'id' => $snapshot->ID,
+                    'stock' => round($stock / 100, 2),
+                    'levelBase' => round($levelBase / 100, 2),
+                    'revenue' => round($snapshot->Revenue / 100, 2),
+                    'revenueD' => round($snapshot->RevenueD / 100, 2),
+                    'stockChange' => round($snapshot->StockChange / 100, 2),
+                    'revenueChange' => round($snapshot->RevenueChange / 100, 2),
+                    'stockRatio' => $stockRatio,
+                    'zValue' => $zValue,
+                    'createTime' => $snapshot->CreateTime,
+                    'timestamp' => strtotime($snapshot->CreateTime) * 1000,
+                    'isPrevious' => false
+                ];
+            }
+
+            return response()->json(['status' => 'success', 'data' => $data]);
+        } catch (\Exception $e) {
+            \Log::error('获取快照历史失败', ['error' => $e->getMessage()]);
+            return response()->json(['status' => 'error', 'message' => '获取失败: ' . $e->getMessage()]);
+        }
+    }
+
+    /**
+     * 计算 Z 值(个控)
+     */
+    private function calculateZValue($stock, $levelBase)
+    {
+        if ($levelBase == 0) {
+            return 0;
+        }
+
+        if ($stock < $levelBase * 2) {
+            return 0;
+        } elseif ($stock >= $levelBase * 2 && $stock < $levelBase * 4) {
+            // 2x ~ 4x 区间:Z = FLOOR((stock - 2*base) / (2*base / 5)) + 1
+            $stockDiff = $stock - ($levelBase * 2);
+            $baseStep = $levelBase * 2 / 5;
+            $zValue = floor($stockDiff / $baseStep) + 1;
+            return min(max($zValue, 1), 5);
+        } elseif ($stock >= $levelBase * 4 && $stock < $levelBase * 6) {
+            // 4x ~ 6x 区间
+            $stockDiff = $stock - ($levelBase * 4);
+            $baseStep = $levelBase * 2 / 5;
+            $zValue = floor($stockDiff / $baseStep) + 6;
+            return min(max($zValue, 6), 10);
+        } elseif ($stock >= $levelBase * 6 && $stock < $levelBase * 8) {
+            // 6x ~ 8x 区间
+            $stockDiff = $stock - ($levelBase * 6);
+            $baseStep = $levelBase * 2 / 5;
+            $zValue = floor($stockDiff / $baseStep) + 11;
+            return min(max($zValue, 11), 15);
+        } else {
+            // 8x+ 区间
+            $stockDiff = $stock - ($levelBase * 8);
+            $baseStep = $levelBase * 4 / 5;
+            $zValue = floor($stockDiff / $baseStep) + 16;
+            return min(max($zValue, 16), 20);
+        }
+    }
+}

+ 149 - 61
resources/views/admin/common_config/index.blade.php

@@ -10,66 +10,151 @@
                     <small class="text-muted">编辑 6 档参数并一键保存 (索引 0-5)</small>
                 </div>
                 <div class="card-body">
-                    <div class="table-responsive">
-                        <table class="table table-striped table-hover align-middle mb-0" style="min-width: 900px;">
-                            <thead class="thead-light">
-                                <tr>
-                                    <th style="width:82px;">档位</th>
-                                    <th style="width:180px;">充值区间下限 (RechargeMin)</th>
-                                    <th style="width:180px;">充值区间上限 (RechargeMax)</th>
-                                    <th style="width:180px;">固定值 (FreeWinMax)</th>
-                                    <th style="width:180px;">充值最大百分比 (RechargeMaxPercent)</th>
-                                    <th style="width:160px;">税收比例 (TaxPercent)</th>
-                                </tr>
-                            </thead>
-                            <tbody class="config-form">
-                                @foreach([0,1,2,3,4,5] as $index => $level)
-                                    <tr>
-                                        <td>
-                                            <span class="badge badge-primary">等级{{ $level }}</span>
-                                        </td>
-                                        <td>
-                                            <input type="number" class="form-control form-control-sm" 
-                                                   name="level[{{ $level }}][RechargeMin]" 
-                                                   value="{{ $config[$level]['RechargeMin'] ?? 0 }}" />
-                                        </td>
-                                        <td>
-                                            <input type="number" class="form-control form-control-sm" 
-                                                   name="level[{{ $level }}][RechargeMax]" 
-                                                   value="{{ $config[$level]['RechargeMax'] ?? 0 }}" />
-                                        </td>
-                                        <td>
-                                            <input type="number" class="form-control form-control-sm" 
-                                                   name="level[{{ $level }}][FreeWinMax]" 
-                                                   value="{{ $config[$level]['FreeWinMax'] ?? 0 }}" />
-                                        </td>
-                                        <td>
-                                            <div class="input-group input-group-sm">
-                                                <input type="number" class="form-control" 
-                                                       name="level[{{ $level }}][RechargeMaxPercent]" 
-                                                       value="{{ $config[$level]['RechargeMaxPercent'] ?? 0 }}" />
-                                                <div class="input-group-append"><span class="input-group-text">%</span></div>
-                                            </div>
-                                        </td>
-                                        <td>
-                                            <div class="input-group input-group-sm">
-                                                <input type="number" class="form-control" 
-                                                       name="level[{{ $level }}][TaxPercent]" 
-                                                       value="{{ $config[$level]['TaxPercent'] ?? 0 }}" />
-                                                <div class="input-group-append"><span class="input-group-text">%</span></div>
-                                            </div>
-                                        </td>
-                                    </tr>
-                                @endforeach
-                            </tbody>
-                        </table>
-                    </div>
+                    <!-- 标签页导航 -->
+                    <ul class="nav nav-tabs mb-3" role="tablist">
+                        <li class="nav-item">
+                            <a class="nav-link active" data-toggle="tab" href="#common-config" role="tab">
+                                <i class="mdi mdi-cog"></i> 常规配置
+                            </a>
+                        </li>
+                        <li class="nav-item">
+                            <a class="nav-link" data-toggle="tab" href="#special-config" role="tab">
+                                <i class="mdi mdi-package-variant"></i> 库存模式
+                            </a>
+                        </li>
+                    </ul>
+
+                    <!-- 标签页内容 -->
+                    <div class="tab-content">
+                        <!-- 常规配置标签页 -->
+                        <div class="tab-pane fade show active" id="common-config" role="tabpanel">
+                            <div class="table-responsive">
+                                <table class="table table-striped table-hover align-middle mb-0" style="min-width: 900px;">
+                                    <thead class="thead-light">
+                                        <tr>
+                                            <th style="width:82px;">档位</th>
+                                            <th style="width:180px;">充值区间下限 (RechargeMin)</th>
+                                            <th style="width:180px;">充值区间上限 (RechargeMax)</th>
+                                            <th style="width:180px;">固定值 (FreeWinMax)</th>
+                                            <th style="width:180px;">充值最大百分比 (RechargeMaxPercent)</th>
+                                            <th style="width:160px;">税收比例 (TaxPercent)</th>
+                                        </tr>
+                                    </thead>
+                                    <tbody class="config-form" data-config-type="common">
+                                        @foreach([0,1,2,3,4,5] as $index => $level)
+                                            <tr>
+                                                <td>
+                                                    <span class="badge badge-primary">等级{{ $level }}</span>
+                                                </td>
+                                                <td>
+                                                    <input type="number" class="form-control form-control-sm"
+                                                           name="level[{{ $level }}][RechargeMin]"
+                                                           value="{{ $config[$level]['RechargeMin'] ?? 0 }}" />
+                                                </td>
+                                                <td>
+                                                    <input type="number" class="form-control form-control-sm"
+                                                           name="level[{{ $level }}][RechargeMax]"
+                                                           value="{{ $config[$level]['RechargeMax'] ?? 0 }}" />
+                                                </td>
+                                                <td>
+                                                    <input type="number" class="form-control form-control-sm"
+                                                           name="level[{{ $level }}][FreeWinMax]"
+                                                           value="{{ $config[$level]['FreeWinMax'] ?? 0 }}" />
+                                                </td>
+                                                <td>
+                                                    <div class="input-group input-group-sm">
+                                                        <input type="number" class="form-control"
+                                                               name="level[{{ $level }}][RechargeMaxPercent]"
+                                                               value="{{ $config[$level]['RechargeMaxPercent'] ?? 0 }}" />
+                                                        <div class="input-group-append"><span class="input-group-text">%</span></div>
+                                                    </div>
+                                                </td>
+                                                <td>
+                                                    <div class="input-group input-group-sm">
+                                                        <input type="number" class="form-control"
+                                                               name="level[{{ $level }}][TaxPercent]"
+                                                               value="{{ $config[$level]['TaxPercent'] ?? 0 }}" />
+                                                        <div class="input-group-append"><span class="input-group-text">%</span></div>
+                                                    </div>
+                                                </td>
+                                            </tr>
+                                        @endforeach
+                                    </tbody>
+                                </table>
+                            </div>
+
+                            <div class="d-flex align-items-center mt-4">
+                                <button class="btn btn-gradient-primary btn-sm save-config" data-config-type="common">
+                                    <i class="mdi mdi-content-save"></i> 保存配置
+                                </button>
+                                <span class="save-status ml-3"></span>
+                            </div>
+                        </div>
 
-                    <div class="d-flex align-items-center mt-4">
-                        <button class="btn btn-gradient-primary btn-sm save-config">
-                            <i class="mdi mdi-content-save"></i> 保存配置
-                        </button>
-                        <span class="save-status ml-3"></span>
+                        <!-- 库存模式标签页 -->
+                        <div class="tab-pane fade" id="special-config" role="tabpanel">
+                            <div class="table-responsive">
+                                <table class="table table-striped table-hover align-middle mb-0" style="min-width: 900px;">
+                                    <thead class="thead-light">
+                                        <tr>
+                                            <th style="width:82px;">档位</th>
+                                            <th style="width:180px;">充值区间下限 (RechargeMin)</th>
+                                            <th style="width:180px;">充值区间上限 (RechargeMax)</th>
+                                            <th style="width:180px;">固定值 (FreeWinMax)</th>
+                                            <th style="width:180px;">充值最大百分比 (RechargeMaxPercent)</th>
+                                            <th style="width:160px;">税收比例 (TaxPercent)</th>
+                                        </tr>
+                                    </thead>
+                                    <tbody class="config-form" data-config-type="special">
+                                        @foreach([0,1,2,3,4,5] as $index => $level)
+                                            <tr>
+                                                <td>
+                                                    <span class="badge badge-success">等级{{ $level }}</span>
+                                                </td>
+                                                <td>
+                                                    <input type="number" class="form-control form-control-sm"
+                                                           name="level[{{ $level }}][RechargeMin]"
+                                                           value="{{ $specialConfig[$level]['RechargeMin'] ?? 0 }}" />
+                                                </td>
+                                                <td>
+                                                    <input type="number" class="form-control form-control-sm"
+                                                           name="level[{{ $level }}][RechargeMax]"
+                                                           value="{{ $specialConfig[$level]['RechargeMax'] ?? 0 }}" />
+                                                </td>
+                                                <td>
+                                                    <input type="number" class="form-control form-control-sm"
+                                                           name="level[{{ $level }}][FreeWinMax]"
+                                                           value="{{ $specialConfig[$level]['FreeWinMax'] ?? 0 }}" />
+                                                </td>
+                                                <td>
+                                                    <div class="input-group input-group-sm">
+                                                        <input type="number" class="form-control"
+                                                               name="level[{{ $level }}][RechargeMaxPercent]"
+                                                               value="{{ $specialConfig[$level]['RechargeMaxPercent'] ?? 0 }}" />
+                                                        <div class="input-group-append"><span class="input-group-text">%</span></div>
+                                                    </div>
+                                                </td>
+                                                <td>
+                                                    <div class="input-group input-group-sm">
+                                                        <input type="number" class="form-control"
+                                                               name="level[{{ $level }}][TaxPercent]"
+                                                               value="{{ $specialConfig[$level]['TaxPercent'] ?? 0 }}" />
+                                                        <div class="input-group-append"><span class="input-group-text">%</span></div>
+                                                    </div>
+                                                </td>
+                                            </tr>
+                                        @endforeach
+                                    </tbody>
+                                </table>
+                            </div>
+
+                            <div class="d-flex align-items-center mt-4">
+                                <button class="btn btn-gradient-primary btn-sm save-config" data-config-type="special">
+                                    <i class="mdi mdi-content-save"></i> 保存配置
+                                </button>
+                                <span class="save-status ml-3"></span>
+                            </div>
+                        </div>
                     </div>
                 </div>
             </div>
@@ -80,9 +165,11 @@
 <script>
 $(function() {
     $('.save-config').click(function(event) {
-        const $form = $('.config-form');
         const $btn = $(this);
-        const $status = $('.save-status');
+        const configType = $btn.data('config-type'); // 获取配置类型 common 或 special
+        const $form = $('.config-form[data-config-type="' + configType + '"]');
+        const $status = $btn.closest('.tab-pane').find('.save-status');
+
         $btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> 保存中...');
         $status.html('').removeClass('text-success text-danger');
 
@@ -113,6 +200,7 @@ $(function() {
 
         $.post("{{ url('/admin/common-config/update') }}", {
             config: JSON.stringify(configArray),
+            config_type: configType,
             _token: "{{ csrf_token() }}"
         }).done(function(res){
             $btn.prop('disabled', false).html('<i class="mdi mdi-content-save"></i> 保存配置');

+ 58 - 27
resources/views/admin/game_some_config/index.blade.php

@@ -8,20 +8,35 @@
                     <div class="card-body">
                         <h4 class="card-title">个控回报配置</h4>
                         <p class="card-description">配置不同个控区间和倍率的权重调节</p>
-                        
-                        <!-- 游戏选择 -->
-                        <div class="form-group" style="margin-bottom: 20px;">
-                            <label for="game_select" style="font-weight: bold; margin-right: 10px;">选择游戏:</label>
-                            <select id="game_select" class="form-control" style="width: 300px; display: inline-block;" onchange="switchGame(this.value)">
-                                @foreach($games as $id => $name)
-                                    <option value="{{ $id }}" {{ $id == $gameId ? 'selected' : '' }}>
-                                        {{ $name }} (GameID: {{ $id }})
-                                    </option>
-                                @endforeach
-                            </select>
-                            <span style="margin-left: 20px; color: #666;">
-                                当前显示:<strong>{{ $games[$gameId] ?? 'Unknown' }}</strong> 的配置
-                            </span>
+
+                        <div class="row" style="margin-bottom: 20px;">
+                            <!-- 游戏选择 -->
+                            <div class="col-md-6 form-group">
+                                <label for="game_select" style="font-weight: bold; margin-right: 10px;">选择游戏:</label>
+                                <select id="game_select" class="form-control" style="width: 300px; display: inline-block;" onchange="switchGame(this.value)">
+                                    @foreach($games as $id => $name)
+                                        <option value="{{ $id }}" {{ $id == $gameId ? 'selected' : '' }}>
+                                            {{ $name }} (GameID: {{ $id }})
+                                        </option>
+                                    @endforeach
+                                </select>
+                            </div>
+
+                            <!-- 库存模式切换 -->
+                            <div class="col-md-6 form-group">
+                                <label style="font-weight: bold; margin-right: 10px;">库存模式:</label>
+                                <div class="btn-group btn-group-toggle" data-toggle="buttons">
+                                    <label class="btn btn-outline-primary {{ $stockMode == 1 ? 'active' : '' }}" onclick="switchStockMode(1)">
+                                        <input type="radio" name="stock_mode" value="1" {{ $stockMode == 1 ? 'checked' : '' }}> 普通模式
+                                    </label>
+                                    <label class="btn btn-outline-success {{ $stockMode == 2 ? 'active' : '' }}" onclick="switchStockMode(2)">
+                                        <input type="radio" name="stock_mode" value="2" {{ $stockMode == 2 ? 'checked' : '' }}> 库存模式
+                                    </label>
+                                </div>
+                                <span style="margin-left: 15px; color: #666;">
+                                    当前:<strong>{{ $stockMode == 2 ? '库存模式' : '普通模式' }}</strong>
+                                </span>
+                            </div>
                         </div>
 
 <style>
@@ -249,6 +264,30 @@
 // 切换游戏
 function switchGame(gameId) {
     // 检查是否有未保存的修改
+    if (!checkUnsavedChanges('切换游戏')) {
+        $('#game_select').val('{{ $gameId }}');
+        return;
+    }
+
+    // 切换到选择的游戏,保持当前的库存模式
+    const stockMode = {{ $stockMode }};
+    window.location.href = '/admin/game-some-config?game_id=' + gameId + '&stock_mode=' + stockMode;
+}
+
+// 切换库存模式
+function switchStockMode(mode) {
+    // 检查是否有未保存的修改
+    if (!checkUnsavedChanges('切换库存模式')) {
+        return;
+    }
+
+    // 切换到选择的模式,保持当前的游戏选择
+    const gameId = {{ $gameId }};
+    window.location.href = '/admin/game-some-config?game_id=' + gameId + '&stock_mode=' + mode;
+}
+
+// 检查是否有未保存的修改
+function checkUnsavedChanges(action) {
     let hasUnsavedChanges = false;
     $('input[name^="configs"]').each(function() {
         const name = $(this).attr('name');
@@ -258,17 +297,11 @@ function switchGame(gameId) {
             return false; // break
         }
     });
-    
+
     if (hasUnsavedChanges) {
-        if (!confirm('有未保存的修改,确定要切换游戏吗?')) {
-            // 恢复选择
-            $('#game_select').val('{{ $gameId }}');
-            return;
-        }
+        return confirm('有未保存的修改,确定要' + action + '吗?');
     }
-    
-    // 切换到选择的游戏
-    window.location.href = '/admin/game-some-config?game_id=' + gameId;
+    return true;
 }
 
 // 保存原始数据
@@ -326,10 +359,8 @@ function saveConfig() {
         return;
     }
     
-    // 构建只包含变动数据的表单(确保空值也会被提交)
-    const formData = Object.keys(changedData).map(function(key) {
-        return encodeURIComponent(key) + '=' + encodeURIComponent(changedData[key] ?? '');
-    }).join('&');
+    // 构建只包含变动数据的表单
+    const formData = $.param(changedData);
     
     layer.msg('正在保存 ' + Object.keys(changedData).length + ' 个变动...', {icon: 16, time: 0, shade: 0.3});
     

+ 1200 - 0
resources/views/admin/stock_mode/index.blade.php

@@ -0,0 +1,1200 @@
+@extends('base.base')
+@section('base')
+<meta name="csrf-token" content="{{ csrf_token() }}">
+<div class="container-fluid">
+    <div class="row">
+        <div class="col-12">
+            <div class="card">
+                <div class="card-header">
+                    <h3 class="card-title mb-0">库存模式配置管理</h3>
+                </div>
+                <div class="card-body">
+                    <!-- 标签页导航 -->
+                    <ul class="nav nav-tabs mb-4" role="tablist">
+                        <li class="nav-item">
+                            <a class="nav-link active" data-toggle="tab" href="#config-tab" role="tab">
+                                <i class="mdi mdi-cog"></i> 参数配置
+                            </a>
+                        </li>
+                        <li class="nav-item">
+                            <a class="nav-link" data-toggle="tab" href="#snapshot-tab" role="tab">
+                                <i class="mdi mdi-chart-line"></i> 快照历史
+                            </a>
+                        </li>
+                    </ul>
+
+                    <!-- 标签页内容 -->
+                    <div class="tab-content">
+                        <!-- 参数配置标签页 -->
+                        <div class="tab-pane fade show active" id="config-tab" role="tabpanel">
+                            <!-- 系统参数配置 -->
+                            <div class="config-section mb-5">
+                        <h5 class="section-title mb-3">
+                            <i class="mdi mdi-cog-outline"></i> 系统参数配置
+                        </h5>
+
+                        <!-- 房间等级分割配置 -->
+                        <div class="card mb-3">
+                            <div class="card-header bg-light">
+                                <h6 class="mb-0">StockMode2BetLevels - 房间等级分割配置</h6>
+                                <small class="text-muted">配置低中高三个房间的下注上限和最低充值要求</small>
+                            </div>
+                            <div class="card-body">
+                                <div class="table-responsive">
+                                    <table class="table table-bordered">
+                                        <thead class="thead-light">
+                                            <tr>
+                                                <th width="120">房间</th>
+                                                <th>最大下注额</th>
+                                                <th>最低充值金额</th>
+                                            </tr>
+                                        </thead>
+                                        <tbody>
+                                            <tr>
+                                                <td><span class="badge badge-success">低级房 (索引1)</span></td>
+                                                <td>
+                                                    <div class="input-group input-group-sm">
+                                                        <input type="number" class="form-control" id="bet_max_1"
+                                                               value="{{ $betMaxLimits[1] ?? 2 }}" />
+                                                        <div class="input-group-append">
+                                                            <span class="input-group-text">下注 ≤ 此值</span>
+                                                        </div>
+                                                    </div>
+                                                </td>
+                                                <td>
+                                                    <div class="input-group input-group-sm">
+                                                        <input type="number" class="form-control" id="recharge_min_1"
+                                                               value="{{ $rechargeMinLimits[1] ?? 0 }}" />
+                                                        <div class="input-group-append">
+                                                            <span class="input-group-text">充值 ≥ 此值</span>
+                                                        </div>
+                                                    </div>
+                                                </td>
+                                            </tr>
+                                            <tr>
+                                                <td><span class="badge badge-info">中级房 (索引2)</span></td>
+                                                <td>
+                                                    <div class="input-group input-group-sm">
+                                                        <input type="number" class="form-control" id="bet_max_2"
+                                                               value="{{ $betMaxLimits[2] ?? 10 }}" />
+                                                        <div class="input-group-append">
+                                                            <span class="input-group-text">下注 ≤ 此值</span>
+                                                        </div>
+                                                    </div>
+                                                </td>
+                                                <td>
+                                                    <div class="input-group input-group-sm">
+                                                        <input type="number" class="form-control" id="recharge_min_2"
+                                                               value="{{ $rechargeMinLimits[2] ?? 100 }}" />
+                                                        <div class="input-group-append">
+                                                            <span class="input-group-text">充值 ≥ 此值</span>
+                                                        </div>
+                                                    </div>
+                                                </td>
+                                            </tr>
+                                            <tr>
+                                                <td><span class="badge badge-warning">高级房 (索引3)</span></td>
+                                                <td>
+                                                    <div class="input-group input-group-sm">
+                                                        <input type="number" class="form-control" id="bet_max_3"
+                                                               value="{{ $betMaxLimits[3] ?? 10000 }}" />
+                                                        <div class="input-group-append">
+                                                            <span class="input-group-text">下注 ≤ 此值</span>
+                                                        </div>
+                                                    </div>
+                                                </td>
+                                                <td>
+                                                    <div class="input-group input-group-sm">
+                                                        <input type="number" class="form-control" id="recharge_min_3"
+                                                               value="{{ $rechargeMinLimits[3] ?? 1000 }}" />
+                                                        <div class="input-group-append">
+                                                            <span class="input-group-text">充值 ≥ 此值</span>
+                                                        </div>
+                                                    </div>
+                                                </td>
+                                            </tr>
+                                        </tbody>
+                                    </table>
+                                </div>
+                            </div>
+                        </div>
+
+                        <!-- 其他系统参数 -->
+                        <div class="row">
+                            <div class="col-md-6">
+                                <div class="card">
+                                    <div class="card-header bg-light">
+                                        <h6 class="mb-0">StockMode2RevenueRatio - 税收比例</h6>
+                                    </div>
+                                    <div class="card-body">
+                                        <div class="form-group">
+                                            <label>税收千分比</label>
+                                            <div class="input-group">
+                                                <input type="number" class="form-control" id="revenue_ratio"
+                                                       value="{{ $systemConfig['StockMode2RevenueRatio']->StatusValue ?? 50 }}" />
+                                                <div class="input-group-append">
+                                                    <span class="input-group-text">‰</span>
+                                                </div>
+                                            </div>
+                                            <small class="form-text text-muted">
+                                                库存模式下的税收比例,计算公式:赢钱 × 值 / 1000<br>
+                                                例如:值为 50 时,税收 = 赢钱 × 50/1000 = 赢钱 × 5%
+                                            </small>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="col-md-6">
+                                <div class="card">
+                                    <div class="card-header bg-light">
+                                        <h6 class="mb-0">StockMode2SwitchRecharge - 模式切换充值阈值</h6>
+                                    </div>
+                                    <div class="card-body">
+                                        <div class="form-group">
+                                            <label>切换充值金额</label>
+                                            <input type="number" class="form-control" id="switch_recharge"
+                                                   value="{{ $systemConfig['StockMode2SwitchRecharge']->StatusValue ?? 100 }}" />
+                                            <small class="form-text text-muted">
+                                                玩家充值达到此金额后,游戏模式切换为库存模式
+                                            </small>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+
+                        <div class="text-right mt-3">
+                            <button class="btn btn-primary" id="save-system-config">
+                                <i class="mdi mdi-content-save"></i> 保存系统配置
+                            </button>
+                            <span class="ml-2 system-status"></span>
+                        </div>
+                    </div>
+
+                    <hr class="my-5">
+
+                    <!-- 房间库存配置 -->
+                    <div class="config-section">
+                        <h5 class="section-title mb-3">
+                            <i class="mdi mdi-database"></i> 房间库存配置 (RoomStockStatic2)
+                        </h5>
+                        <p class="text-muted mb-3">
+                            LevelBase 基数配置说明:根据 LevelBase 的倍数区间计算对应的个控(Z)随机范围
+                        </p>
+
+                        <div class="card">
+                            <div class="card-body">
+                                <div class="alert alert-warning">
+                                    <i class="mdi mdi-alert"></i> <strong>注意:</strong>所有数值显示时已除以100,保存时会自动乘以100还原到数据库
+                                </div>
+                                <div class="table-responsive">
+                                    <table class="table table-bordered table-hover">
+                                        <thead class="thead-light">
+                                            <tr>
+                                                <th width="120">房间等级</th>
+                                                <th>当前库存 (Stock)</th>
+                                                <th>基数 (LevelBase)</th>
+                                                <th>累计税收 (Revenue)</th>
+                                                <th>累计暗税 (RevenueD)</th>
+                                                <th width="120">操作</th>
+                                            </tr>
+                                        </thead>
+                                        <tbody>
+                                            @foreach([1 => '低级房', 2 => '中级房', 3 => '高级房'] as $sortId => $roomName)
+                                                @php
+                                                    $roomStock = $roomStocks->firstWhere('SortID', $sortId);
+                                                    $stock = ($roomStock->Stock ?? 0) / 100;
+                                                    $levelBase = ($roomStock->LevelBase ?? 10000) / 100;
+                                                    $revenue = ($roomStock->Revenue ?? 0) / 100;
+                                                    $revenueD = ($roomStock->RevenueD ?? 0) / 100;
+                                                @endphp
+                                                <tr>
+                                                    <td>
+                                                        <span class="badge badge-{{ $sortId == 1 ? 'success' : ($sortId == 2 ? 'info' : 'warning') }}">
+                                                            {{ $roomName }} (ID:{{ $sortId }})
+                                                        </span>
+                                                    </td>
+                                                    <td>
+                                                        <div class="input-group input-group-sm">
+                                                            <div class="input-group-prepend">
+                                                                <button class="btn btn-outline-danger stock-decrease" type="button" data-sort-id="{{ $sortId }}">
+                                                                    <i class="mdi mdi-minus"></i>
+                                                                </button>
+                                                            </div>
+                                                            <input type="number" step="0.01" class="form-control text-center stock-adjust-input"
+                                                                   data-sort-id="{{ $sortId }}" placeholder="增减数值" />
+                                                            <div class="input-group-append">
+                                                                <button class="btn btn-outline-success stock-increase" type="button" data-sort-id="{{ $sortId }}">
+                                                                    <i class="mdi mdi-plus"></i>
+                                                                </button>
+                                                            </div>
+                                                        </div>
+                                                        <small class="text-muted d-block mt-1">
+                                                            当前: <strong class="current-stock-display" data-sort-id="{{ $sortId }}">{{ number_format($stock, 2) }}</strong>
+                                                        </small>
+                                                    </td>
+                                                    <td>
+                                                        <input type="number" step="0.01" class="form-control form-control-sm level-base-input"
+                                                               data-sort-id="{{ $sortId }}"
+                                                               value="{{ number_format($levelBase, 2, '.', '') }}" />
+                                                    </td>
+                                                    <td class="text-right text-muted">
+                                                        {{ number_format($revenue, 2) }}
+                                                    </td>
+                                                    <td class="text-right text-muted">
+                                                        {{ number_format($revenueD, 2) }}
+                                                    </td>
+                                                    <td class="text-center">
+                                                        <button class="btn btn-sm btn-primary save-room-stock" data-sort-id="{{ $sortId }}">
+                                                            <i class="mdi mdi-content-save"></i> 保存
+                                                        </button>
+                                                    </td>
+                                                </tr>
+                                            @endforeach
+                                        </tbody>
+                                    </table>
+                                </div>
+
+                                <!-- 个控计算说明 -->
+                                <div class="alert alert-info mt-4">
+                                    <h6><i class="mdi mdi-information"></i> 个控计算规则说明</h6>
+                                    <p class="mb-2">根据当前库存(Stock)与基数(LevelBase)的比值,计算个控(Z)的随机范围:</p>
+                                    <div class="table-responsive">
+                                        <table class="table table-sm table-bordered bg-white">
+                                            <thead>
+                                                <tr>
+                                                    <th>库存区间</th>
+                                                    <th>个控范围(Z)</th>
+                                                </tr>
+                                            </thead>
+                                            <tbody>
+                                                <tr>
+                                                    <td>0 ~ 2×LevelBase</td>
+                                                    <td>0(固定)</td>
+                                                </tr>
+                                                <tr>
+                                                    <td>2×LevelBase ~ 4×LevelBase</td>
+                                                    <td>1 ~ 5(随机)</td>
+                                                </tr>
+                                                <tr>
+                                                    <td>4×LevelBase ~ 6×LevelBase</td>
+                                                    <td>6 ~ 10(随机)</td>
+                                                </tr>
+                                                <tr>
+                                                    <td>6×LevelBase ~ 8×LevelBase</td>
+                                                    <td>11 ~ 15(随机)</td>
+                                                </tr>
+                                                <tr>
+                                                    <td>8×LevelBase ~ 10×LevelBase</td>
+                                                    <td>16 ~ 20(随机)</td>
+                                                </tr>
+                                                <tr>
+                                                    <td>10×LevelBase 以上</td>
+                                                    <td>20(固定)</td>
+                                                </tr>
+                                            </tbody>
+                                        </table>
+                                    </div>
+                                    <p class="mb-0 mt-2">
+                                        <strong>示例:</strong>当 LevelBase = 100 时
+                                        <ul class="mb-0">
+                                            <li>Stock 在 0-200 时,个控为 0</li>
+                                            <li>Stock 在 200-400 时,个控为 1-5 随机</li>
+                                            <li>Stock 在 400-600 时,个控为 6-10 随机</li>
+                                            <li>以此类推...</li>
+                                        </ul>
+                                    </p>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                        </div>
+                        <!-- 参数配置标签页结束 -->
+
+                        <!-- 快照历史标签页 -->
+                        <div class="tab-pane fade" id="snapshot-tab" role="tabpanel">
+                            <!-- 时间范围选择器 -->
+                            <div class="card mb-3">
+                                <div class="card-body">
+                                    <div class="row">
+                                        <div class="col-md-12 mb-3">
+                                            <label>快捷时间区间:</label>
+                                            <div class="btn-group d-block" role="group">
+                                                <button type="button" class="btn btn-sm btn-outline-primary time-range-btn" data-minutes="5">5分钟</button>
+                                                <button type="button" class="btn btn-sm btn-outline-primary time-range-btn" data-minutes="15">15分钟</button>
+                                                <button type="button" class="btn btn-sm btn-outline-primary time-range-btn active" data-minutes="60">1小时</button>
+                                                <button type="button" class="btn btn-sm btn-outline-primary time-range-btn" data-minutes="240">4小时</button>
+                                                <button type="button" class="btn btn-sm btn-outline-primary time-range-btn" data-minutes="720">12小时</button>
+                                                <button type="button" class="btn btn-sm btn-outline-primary time-range-btn" data-minutes="1440">24小时</button>
+                                                <button type="button" class="btn btn-sm btn-outline-primary time-range-btn" data-minutes="10080">一周</button>
+                                            </div>
+                                        </div>
+                                        <div class="col-md-12">
+                                            <label>自定义时间范围(拖动滑块调整):</label>
+                                            <div id="time-range-slider" class="mb-2"></div>
+                                            <div class="d-flex justify-content-between">
+                                                <span id="range-start" class="text-muted small"></span>
+                                                <span id="range-duration" class="badge badge-info"></span>
+                                                <span id="range-end" class="text-muted small"></span>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+
+                            <!-- 库存值图表 -->
+                            <div class="card mb-3">
+                                <div class="card-header">
+                                    <div class="d-flex justify-content-between align-items-center mb-2">
+                                        <h6 class="mb-0">库存变化</h6>
+                                        <div class="btn-group btn-group-sm" role="group">
+                                            <button type="button" class="btn btn-primary chart-type-btn active" data-type="stock">库存值</button>
+                                            <button type="button" class="btn btn-outline-primary chart-type-btn" data-type="ratio">比值</button>
+                                            <button type="button" class="btn btn-outline-primary chart-type-btn" data-type="both">都显示</button>
+                                        </div>
+                                    </div>
+                                    <div class="d-flex align-items-center">
+                                        <span class="mr-2 text-muted small">显示房间:</span>
+                                        <div class="form-check form-check-inline mb-0">
+                                            <input class="form-check-input room-checkbox" type="checkbox" id="room-1" value="1" checked>
+                                            <label class="form-check-label" for="room-1">
+                                                <span class="badge badge-success">低级房</span>
+                                            </label>
+                                        </div>
+                                        <div class="form-check form-check-inline mb-0">
+                                            <input class="form-check-input room-checkbox" type="checkbox" id="room-2" value="2" checked>
+                                            <label class="form-check-label" for="room-2">
+                                                <span class="badge badge-info">中级房</span>
+                                            </label>
+                                        </div>
+                                        <div class="form-check form-check-inline mb-0">
+                                            <input class="form-check-input room-checkbox" type="checkbox" id="room-3" value="3" checked>
+                                            <label class="form-check-label" for="room-3">
+                                                <span class="badge badge-warning">高级房</span>
+                                            </label>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="card-body">
+                                    <div id="stock-ratio-chart" style="height: 500px;"></div>
+                                </div>
+                            </div>
+
+                            <!-- 房间数据表格 -->
+                            <div class="row">
+                                <div class="col-md-4">
+                                    <div class="card">
+                                        <div class="card-header bg-success text-white">
+                                            <h6 class="mb-0">低级房快照数据</h6>
+                                        </div>
+                                        <div class="card-body p-0">
+                                            <div class="table-responsive" style="max-height: 400px; overflow-y: auto;">
+                                                <table class="table table-sm table-hover mb-0" id="snapshot-table-1">
+                                                    <thead class="thead-light sticky-top">
+                                                        <tr>
+                                                            <th>时间</th>
+                                                            <th>比值</th>
+                                                            <th>库存</th>
+                                                            <th>基数</th>
+                                                            <th>税收</th>
+                                                            <th>Z值</th>
+                                                        </tr>
+                                                    </thead>
+                                                    <tbody>
+                                                        <tr><td colspan="6" class="text-center text-muted">加载中...</td></tr>
+                                                    </tbody>
+                                                </table>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-md-4">
+                                    <div class="card">
+                                        <div class="card-header bg-info text-white">
+                                            <h6 class="mb-0">中级房快照数据</h6>
+                                        </div>
+                                        <div class="card-body p-0">
+                                            <div class="table-responsive" style="max-height: 400px; overflow-y: auto;">
+                                                <table class="table table-sm table-hover mb-0" id="snapshot-table-2">
+                                                    <thead class="thead-light sticky-top">
+                                                        <tr>
+                                                            <th>时间</th>
+                                                            <th>比值</th>
+                                                            <th>库存</th>
+                                                            <th>基数</th>
+                                                            <th>税收</th>
+                                                            <th>Z值</th>
+                                                        </tr>
+                                                    </thead>
+                                                    <tbody>
+                                                        <tr><td colspan="6" class="text-center text-muted">加载中...</td></tr>
+                                                    </tbody>
+                                                </table>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-md-4">
+                                    <div class="card">
+                                        <div class="card-header bg-warning text-white">
+                                            <h6 class="mb-0">高级房快照数据</h6>
+                                        </div>
+                                        <div class="card-body p-0">
+                                            <div class="table-responsive" style="max-height: 400px; overflow-y: auto;">
+                                                <table class="table table-sm table-hover mb-0" id="snapshot-table-3">
+                                                    <thead class="thead-light sticky-top">
+                                                        <tr>
+                                                            <th>时间</th>
+                                                            <th>比值</th>
+                                                            <th>库存</th>
+                                                            <th>基数</th>
+                                                            <th>税收</th>
+                                                            <th>Z值</th>
+                                                        </tr>
+                                                    </thead>
+                                                    <tbody>
+                                                        <tr><td colspan="6" class="text-center text-muted">加载中...</td></tr>
+                                                    </tbody>
+                                                </table>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <!-- 快照历史标签页结束 -->
+                    </div>
+                    <!-- tab-content结束 -->
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<!-- 引入 ECharts -->
+<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
+<!-- 引入 noUiSlider -->
+<link href="https://cdn.jsdelivr.net/npm/nouislider@15.7.1/dist/nouislider.min.css" rel="stylesheet">
+<script src="https://cdn.jsdelivr.net/npm/nouislider@15.7.1/dist/nouislider.min.js"></script>
+
+<script>
+$(function() {
+    // 保存系统配置
+    $('#save-system-config').click(function() {
+        const $btn = $(this);
+        const $status = $('.system-status');
+
+        $btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> 保存中...');
+        $status.html('').removeClass('text-success text-danger');
+
+        const data = {
+            bet_max_limits: [
+                0, // 索引0轮空
+                parseInt($('#bet_max_1').val()) || 0,
+                parseInt($('#bet_max_2').val()) || 0,
+                parseInt($('#bet_max_3').val()) || 0
+            ],
+            recharge_min_limits: [
+                0, // 索引0轮空
+                parseInt($('#recharge_min_1').val()) || 0,
+                parseInt($('#recharge_min_2').val()) || 0,
+                parseInt($('#recharge_min_3').val()) || 0
+            ],
+            revenue_ratio: parseInt($('#revenue_ratio').val()) || 50,
+            switch_recharge: parseInt($('#switch_recharge').val()) || 100,
+            _token: "{{ csrf_token() }}"
+        };
+
+        $.post("{{ url('/admin/stock-mode/update-system-config') }}", data)
+            .done(function(res) {
+                $btn.prop('disabled', false).html('<i class="mdi mdi-content-save"></i> 保存系统配置');
+                if (res.status === 'success') {
+                    $status.text('更新成功').addClass('text-success');
+                } else {
+                    $status.text(res.message || '更新失败').addClass('text-danger');
+                }
+                setTimeout(function() {
+                    $status.fadeOut(function() {
+                        $(this).text('').show().removeClass('text-success text-danger');
+                    });
+                }, 3000);
+            })
+            .fail(function() {
+                $btn.prop('disabled', false).html('<i class="mdi mdi-content-save"></i> 保存系统配置');
+                $status.text('系统错误').addClass('text-danger');
+            });
+    });
+
+    // 库存增加
+    $('.stock-increase').click(function() {
+        const sortId = $(this).data('sort-id');
+        const $input = $('.stock-adjust-input[data-sort-id="' + sortId + '"]');
+        const adjustValue = parseFloat($input.val()) || 0;
+
+        if (adjustValue === 0) {
+            alert('请输入要增加的数值');
+            return;
+        }
+
+        updateStock(sortId, adjustValue);
+    });
+
+    // 库存减少
+    $('.stock-decrease').click(function() {
+        const sortId = $(this).data('sort-id');
+        const $input = $('.stock-adjust-input[data-sort-id="' + sortId + '"]');
+        const adjustValue = parseFloat($input.val()) || 0;
+
+        if (adjustValue === 0) {
+            alert('请输入要减少的数值');
+            return;
+        }
+
+        updateStock(sortId, -adjustValue);
+    });
+
+    // 格式化数字为千分位
+    function formatNumber(num) {
+        return num.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
+    }
+
+    // 更新库存函数
+    function updateStock(sortId, adjustValue) {
+        // 将显示的数值(已除以100)乘以100还原成数据库值
+        const adjustValueDb = Math.round(adjustValue * 100);
+
+        $.post("{{ url('/admin/stock-mode/update-stock') }}", {
+            sort_id: sortId,
+            adjust_value: adjustValueDb,
+            _token: "{{ csrf_token() }}"
+        })
+        .done(function(res) {
+            if (res.status === 'success') {
+                // 更新显示的当前库存(已除以100)
+                const newStock = res.new_stock / 100;
+                $('.current-stock-display[data-sort-id="' + sortId + '"]').text(formatNumber(newStock));
+                $('.stock-adjust-input[data-sort-id="' + sortId + '"]').val('');
+
+                // 显示成功提示
+                const $display = $('.current-stock-display[data-sort-id="' + sortId + '"]');
+                $display.addClass('text-success');
+                setTimeout(function() {
+                    $display.removeClass('text-success');
+                }, 1000);
+            } else {
+                alert(res.message || '更新失败');
+            }
+        })
+        .fail(function() {
+            alert('系统错误');
+        });
+    }
+
+    // 保存房间库存配置(LevelBase)
+    $('.save-room-stock').click(function() {
+        const $btn = $(this);
+        const sortId = $btn.data('sort-id');
+        const $input = $('.level-base-input[data-sort-id="' + sortId + '"]');
+        const levelBaseDisplay = parseFloat($input.val()) || 100;
+
+        // 将显示的数值(已除以100)乘以100还原成数据库值
+        const levelBase = Math.round(levelBaseDisplay * 100);
+
+        $btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i>');
+
+        $.post("{{ url('/admin/stock-mode/update-room-stock') }}", {
+            sort_id: sortId,
+            level_base: levelBase,
+            _token: "{{ csrf_token() }}"
+        })
+        .done(function(res) {
+            $btn.prop('disabled', false).html('<i class="mdi mdi-content-save"></i> 保存');
+            if (res.status === 'success') {
+                $btn.removeClass('btn-primary btn-danger').addClass('btn-success');
+                setTimeout(function() {
+                    $btn.removeClass('btn-success').addClass('btn-primary');
+                }, 1500);
+            } else {
+                $btn.removeClass('btn-primary btn-success').addClass('btn-danger');
+                alert(res.message || '更新失败');
+                setTimeout(function() {
+                    $btn.removeClass('btn-danger').addClass('btn-primary');
+                }, 2000);
+            }
+        })
+        .fail(function() {
+            $btn.prop('disabled', false).html('<i class="mdi mdi-content-save"></i> 保存');
+            $btn.removeClass('btn-primary btn-success').addClass('btn-danger');
+            alert('系统错误');
+            setTimeout(function() {
+                $btn.removeClass('btn-danger').addClass('btn-primary');
+            }, 2000);
+        });
+    });
+
+    // ============ 快照历史相关功能 ============
+    let ratioChart = null;
+    let timeRangeSlider = null;
+    let allRoomsData = {1: [], 2: [], 3: []};  // 三个房间的所有数据
+    let filteredRoomsData = {1: [], 2: [], 3: []}; // 筛选后的数据
+    let chartType = 'stock'; // 默认显示库存值,可选:stock, ratio, both
+    let selectedRooms = [1, 2, 3]; // 默认显示所有房间
+    const WEEK_IN_MS = 7 * 24 * 60 * 60 * 1000;
+    const MINUTE_IN_MS = 60 * 1000;
+
+    // 初始化图表
+    function initCharts() {
+        ratioChart = echarts.init(document.getElementById('stock-ratio-chart'));
+    }
+
+    // 补全数据:按固定间隔填充缺失的数据点,确保图表连续
+    function fillDataGaps(data, minutes) {
+        if (data.length === 0) return [];
+
+        const ACTUAL_INTERVAL_MS = 3000; // 实际数据的间隔是3秒
+        const FILL_INTERVAL_MS = 10000; // 填充数据使用10秒间隔,降低数据量
+        const now = Date.now();
+        const startTime = now - (minutes * 60 * 1000);
+        const endTime = now;
+
+        // 分离前置数据和正常数据
+        let previousData = null;
+        let normalData = [];
+
+        data.forEach(item => {
+            if (item.isPrevious) {
+                previousData = item;
+            } else {
+                normalData.push(item);
+            }
+        });
+
+        // 如果没有正常数据但有前置数据,使用前置数据填充整个时间范围
+        if (normalData.length === 0 && previousData) {
+            const filledData = [];
+            for (let t = startTime; t <= endTime; t += FILL_INTERVAL_MS) {
+                filledData.push({
+                    ...previousData,
+                    timestamp: t,
+                    createTime: new Date(t).toISOString().replace('T', ' ').substring(0, 19),
+                    isFilled: true
+                });
+            }
+            console.log(`房间无新数据,使用前置数据填充: 填充=${filledData.length}`);
+            return filledData;
+        }
+
+        // 如果完全没有数据,返回空
+        if (normalData.length === 0) {
+            console.log('房间无任何数据');
+            return [];
+        }
+
+        const filledData = [];
+        let fillCount = 0;
+
+        const firstDataTime = normalData[0].timestamp;
+        const lastDataTime = normalData[normalData.length - 1].timestamp;
+
+        // 1. 填充起始时间到第一条数据之间的空白
+        if (firstDataTime > startTime) {
+            const fillSource = previousData || normalData[0];
+            for (let t = startTime; t < firstDataTime; t += FILL_INTERVAL_MS) {
+                filledData.push({
+                    ...fillSource,
+                    timestamp: t,
+                    createTime: new Date(t).toISOString().replace('T', ' ').substring(0, 19),
+                    isFilled: true
+                });
+                fillCount++;
+            }
+        }
+
+        // 2. 处理实际数据和中间空白
+        for (let i = 0; i < normalData.length; i++) {
+            const currentItem = normalData[i];
+
+            // 添加当前实际数据
+            filledData.push({
+                ...currentItem,
+                isFilled: false
+            });
+
+            // 检查与下一条数据之间的间隔
+            if (i < normalData.length - 1) {
+                const nextItem = normalData[i + 1];
+                const gap = nextItem.timestamp - currentItem.timestamp;
+
+                // 如果间隔大于实际间隔(说明有数据缺失),填充中间
+                if (gap > ACTUAL_INTERVAL_MS * 2) {
+                    for (let t = currentItem.timestamp + FILL_INTERVAL_MS; t < nextItem.timestamp; t += FILL_INTERVAL_MS) {
+                        filledData.push({
+                            ...currentItem,
+                            timestamp: t,
+                            createTime: new Date(t).toISOString().replace('T', ' ').substring(0, 19),
+                            isFilled: true
+                        });
+                        fillCount++;
+                    }
+                }
+            }
+        }
+
+        // 3. 填充最后一条数据到结束时间的空白
+        if (lastDataTime < endTime) {
+            const lastItem = normalData[normalData.length - 1];
+            for (let t = lastDataTime + FILL_INTERVAL_MS; t <= endTime; t += FILL_INTERVAL_MS) {
+                filledData.push({
+                    ...lastItem,
+                    timestamp: t,
+                    createTime: new Date(t).toISOString().replace('T', ' ').substring(0, 19),
+                    isFilled: true
+                });
+                fillCount++;
+            }
+        }
+
+        console.log(`数据补全完成: 原始=${normalData.length}, 填充=${fillCount}, 总计=${filledData.length}`);
+        return filledData;
+    }
+
+    // 加载所有房间的快照数据
+    function loadAllRoomsData() {
+        const minutes = $('.time-range-btn.active').data('minutes');
+        const requests = [];
+
+        // 并行请求三个房间的数据
+        for (let sortId = 1; sortId <= 3; sortId++) {
+            requests.push(
+                $.get("{{ url('/admin/stock-mode/snapshot-history') }}", {
+                    sort_id: sortId,
+                    minutes: minutes
+                })
+            );
+        }
+
+        Promise.all(requests).then(function(responses) {
+            console.log('收到响应数量:', responses.length);
+
+            responses.forEach(function(res, index) {
+                const sortId = index + 1;
+                console.log(`房间${sortId} 响应:`, res.status, '原始数据量:', res.data ? res.data.length : 0);
+
+                if (res.status === 'success') {
+                    // 补全数据
+                    console.log(`房间${sortId} 开始补全数据`);
+                    const filledData = fillDataGaps(res.data, minutes);
+                    console.log(`房间${sortId} 补全后数据量:`, filledData.length);
+                    allRoomsData[sortId] = filledData;
+                    filteredRoomsData[sortId] = filledData;
+                } else {
+                    console.error(`房间${sortId} 加载失败`);
+                    allRoomsData[sortId] = [];
+                    filteredRoomsData[sortId] = [];
+                }
+            });
+
+            console.log('所有房间数据加载完成:', {
+                room1: allRoomsData[1].length,
+                room2: allRoomsData[2].length,
+                room3: allRoomsData[3].length
+            });
+
+            renderChart();
+            renderTables();
+            initTimeRangeSlider();
+        }).catch(function(err) {
+            console.error('加载数据失败:', err);
+            alert('加载数据失败');
+        });
+    }
+
+    // 渲染库存比值图表(三条线)
+    function renderChart() {
+        if (!ratioChart) return;
+
+        // 合并选中房间的所有时间点并去重排序
+        let allTimestamps = [];
+        selectedRooms.forEach(sortId => {
+            allTimestamps = allTimestamps.concat(filteredRoomsData[sortId].map(d => d.timestamp));
+        });
+        allTimestamps = [...new Set(allTimestamps)].sort((a, b) => a - b);
+
+        const times = allTimestamps.map(ts => new Date(ts).toLocaleString('zh-CN', {
+            month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit'
+        }));
+
+        // 为每个房间创建数据映射(保存完整数据用于 tooltip)
+        const roomStockData = {};
+        const roomRatioData = {};
+        const roomDetailData = {};
+        [1, 2, 3].forEach(sortId => {
+            const stockMap = {};
+            const ratioMap = {};
+            const detailMap = {};
+            filteredRoomsData[sortId].forEach(item => {
+                stockMap[item.timestamp] = item.stock;
+                ratioMap[item.timestamp] = item.stockRatio;
+                detailMap[item.timestamp] = item;
+            });
+            roomStockData[sortId] = allTimestamps.map(ts => stockMap[ts] || null);
+            roomRatioData[sortId] = allTimestamps.map(ts => ratioMap[ts] || null);
+            roomDetailData[sortId] = detailMap;
+        });
+
+        // 房间配置
+        const roomConfigs = [
+            { id: 1, name: '低级房', color: '#67C23A', lightColor: '#95D475' },
+            { id: 2, name: '中级房', color: '#409EFF', lightColor: '#79BBFF' },
+            { id: 3, name: '高级房', color: '#E6A23C', lightColor: '#F3D19E' }
+        ];
+
+        // 根据图表类型确定标题和Y轴名称
+        let chartTitle = '';
+        let yAxisName = '';
+        let legendData = [];
+        let series = [];
+
+        if (chartType === 'stock') {
+            chartTitle = '库存值变化';
+            yAxisName = '库存值';
+
+            roomConfigs.forEach(room => {
+                if (selectedRooms.includes(room.id)) {
+                    legendData.push(room.name);
+                    series.push({
+                        name: room.name,
+                        type: 'line',
+                        data: roomStockData[room.id],
+                        smooth: true,
+                        lineStyle: { width: 2 },
+                        itemStyle: { color: room.color },
+                        connectNulls: true
+                    });
+                }
+            });
+        } else if (chartType === 'ratio') {
+            chartTitle = '库存比值变化';
+            yAxisName = '比值';
+
+            roomConfigs.forEach(room => {
+                if (selectedRooms.includes(room.id)) {
+                    legendData.push(room.name);
+                    series.push({
+                        name: room.name,
+                        type: 'line',
+                        data: roomRatioData[room.id],
+                        smooth: true,
+                        lineStyle: { width: 2 },
+                        itemStyle: { color: room.color },
+                        connectNulls: true
+                    });
+                }
+            });
+        } else { // both - 使用双Y轴
+            chartTitle = '库存值与比值变化(双Y轴)';
+            yAxisName = ['库存值', '比值']; // 双Y轴
+
+            roomConfigs.forEach(room => {
+                if (selectedRooms.includes(room.id)) {
+                    legendData.push(`${room.name}-库存`);
+                    legendData.push(`${room.name}-比值`);
+                    series.push({
+                        name: `${room.name}-库存`,
+                        type: 'line',
+                        data: roomStockData[room.id],
+                        yAxisIndex: 0, // 使用左侧Y轴
+                        smooth: true,
+                        lineStyle: { width: 2 },
+                        itemStyle: { color: room.color },
+                        connectNulls: true
+                    });
+                    series.push({
+                        name: `${room.name}-比值`,
+                        type: 'line',
+                        data: roomRatioData[room.id],
+                        yAxisIndex: 1, // 使用右侧Y轴
+                        smooth: true,
+                        lineStyle: { width: 2, type: 'dashed' },
+                        itemStyle: { color: room.lightColor },
+                        connectNulls: true
+                    });
+                }
+            });
+        }
+
+        const option = {
+            title: {
+                text: chartTitle,
+                left: 'center'
+            },
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: { type: 'cross' },
+                formatter: function(params) {
+                    let tooltip = `<div style="font-weight:bold;">${params[0].axisValue}</div>`;
+
+                    // 只显示选中房间的信息
+                    const roomConfigs = [
+                        { id: 1, name: '低级房', color: '#67C23A' },
+                        { id: 2, name: '中级房', color: '#409EFF' },
+                        { id: 3, name: '高级房', color: '#E6A23C' }
+                    ];
+
+                    roomConfigs.forEach(room => {
+                        if (selectedRooms.includes(room.id)) {
+                            const dataIndex = params[0].dataIndex;
+                            const timestamp = allTimestamps[dataIndex];
+                            const detail = roomDetailData[room.id][timestamp];
+
+                            if (detail) {
+                                tooltip += `<div style="margin-top:5px;">
+                                    <span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${room.color};margin-right:5px;"></span>
+                                    <strong>${room.name}</strong><br/>
+                                    <span style="margin-left:15px;">比值: ${detail.stockRatio}</span><br/>
+                                    <span style="margin-left:15px;">库存: ${formatNumber(detail.stock)}</span><br/>
+                                    <span style="margin-left:15px;">基数: ${formatNumber(detail.levelBase)}</span><br/>
+                                    <span style="margin-left:15px;">税收: ${formatNumber(detail.revenue)}</span>
+                                </div>`;
+                            }
+                        }
+                    });
+
+                    return tooltip;
+                }
+            },
+            legend: {
+                data: legendData,
+                top: 35
+            },
+            grid: {
+                left: '3%',
+                right: '4%',
+                bottom: '3%',
+                containLabel: true,
+                top: 70
+            },
+            xAxis: {
+                type: 'category',
+                data: times,
+                axisLabel: {
+                    rotate: 45,
+                    fontSize: 10
+                }
+            },
+            yAxis: Array.isArray(yAxisName) ? [
+                {
+                    type: 'value',
+                    name: yAxisName[0],
+                    position: 'left',
+                    axisLabel: {
+                        formatter: '{value}'
+                    }
+                },
+                {
+                    type: 'value',
+                    name: yAxisName[1],
+                    position: 'right',
+                    axisLabel: {
+                        formatter: '{value}'
+                    }
+                }
+            ] : {
+                type: 'value',
+                name: yAxisName
+            },
+            series: series
+        };
+
+        // 使用 notMerge: true 完全替换配置,避免旧配置残留
+        ratioChart.setOption(option, true);
+    }
+
+    // 渲染三个房间的数据表格
+    function renderTables() {
+        [1, 2, 3].forEach(sortId => {
+            const $tbody = $(`#snapshot-table-${sortId} tbody`);
+            $tbody.empty();
+
+            const data = filteredRoomsData[sortId];
+            if (data.length === 0) {
+                $tbody.append('<tr><td colspan="6" class="text-center text-muted">暂无数据</td></tr>');
+                return;
+            }
+
+            data.forEach(item => {
+                const time = item.createTime.substring(11, 19);
+                const row = `
+                    <tr>
+                        <td>${time}</td>
+                        <td>${item.stockRatio}</td>
+                        <td>${formatNumber(item.stock)}</td>
+                        <td>${formatNumber(item.levelBase)}</td>
+                        <td>${formatNumber(item.revenue)}</td>
+                        <td><span class="badge badge-${item.zValue <= 5 ? 'success' : (item.zValue <= 15 ? 'warning' : 'danger')}">${item.zValue}</span></td>
+                    </tr>
+                `;
+                $tbody.append(row);
+            });
+        });
+    }
+
+    // 初始化时间范围滑块
+    function initTimeRangeSlider() {
+        // 收集所有数据的时间戳
+        let allTimestamps = [];
+        [1, 2, 3].forEach(sortId => {
+            allTimestamps = allTimestamps.concat(allRoomsData[sortId].map(d => d.timestamp));
+        });
+
+        if (allTimestamps.length === 0) return;
+
+        const minTime = Math.min(...allTimestamps);
+        const maxTime = Math.max(...allTimestamps);
+
+        // 限制最小范围为1分钟,最大为一周
+        const totalRange = maxTime - minTime;
+        const limitedMaxTime = Math.min(maxTime, minTime + WEEK_IN_MS);
+
+        if (timeRangeSlider) {
+            timeRangeSlider.destroy();
+        }
+
+        const sliderElement = document.getElementById('time-range-slider');
+        timeRangeSlider = noUiSlider.create(sliderElement, {
+            start: [minTime, limitedMaxTime],
+            connect: true,
+            range: {
+                'min': minTime,
+                'max': limitedMaxTime
+            },
+            step: 1000
+        });
+
+        // 更新时间显示和时长
+        function updateTimeLabels(values) {
+            const start = parseInt(values[0]);
+            const end = parseInt(values[1]);
+            const duration = end - start;
+
+            $('#range-start').text(new Date(start).toLocaleString());
+            $('#range-end').text(new Date(end).toLocaleString());
+
+            // 计算时长
+            const minutes = Math.floor(duration / MINUTE_IN_MS);
+            const hours = Math.floor(minutes / 60);
+            const days = Math.floor(hours / 24);
+
+            let durationText = '';
+            if (days > 0) {
+                durationText = `${days}天${hours % 24}小时`;
+            } else if (hours > 0) {
+                durationText = `${hours}小时${minutes % 60}分钟`;
+            } else {
+                durationText = `${minutes}分钟`;
+            }
+            $('#range-duration').text(durationText);
+
+            // 验证最小1分钟限制
+            if (duration < MINUTE_IN_MS) {
+                timeRangeSlider.set([start, start + MINUTE_IN_MS]);
+                return;
+            }
+
+            // 验证最大一周限制
+            if (duration > WEEK_IN_MS) {
+                timeRangeSlider.set([start, start + WEEK_IN_MS]);
+                return;
+            }
+        }
+
+        updateTimeLabels(timeRangeSlider.get());
+
+        // 滑块变化事件
+        timeRangeSlider.on('update', function(values) {
+            updateTimeLabels(values);
+        });
+
+        timeRangeSlider.on('change', function(values) {
+            const startTime = parseInt(values[0]);
+            const endTime = parseInt(values[1]);
+
+            // 筛选数据
+            [1, 2, 3].forEach(sortId => {
+                filteredRoomsData[sortId] = allRoomsData[sortId].filter(d =>
+                    d.timestamp >= startTime && d.timestamp <= endTime
+                );
+            });
+
+            renderChart();
+            renderTables();
+        });
+    }
+
+    // 时间区间按钮点击
+    $('.time-range-btn').on('click', function() {
+        $('.time-range-btn').removeClass('active');
+        $(this).addClass('active');
+        loadAllRoomsData();
+    });
+
+    // 图表类型切换按钮点击
+    $('.chart-type-btn').on('click', function() {
+        $('.chart-type-btn').removeClass('active btn-primary').addClass('btn-outline-primary');
+        $(this).removeClass('btn-outline-primary').addClass('active btn-primary');
+        chartType = $(this).data('type');
+        renderChart();
+    });
+
+    // 房间复选框变化事件
+    $('.room-checkbox').on('change', function() {
+        selectedRooms = [];
+        $('.room-checkbox:checked').each(function() {
+            selectedRooms.push(parseInt($(this).val()));
+        });
+
+        // 至少选择一个房间
+        if (selectedRooms.length === 0) {
+            $(this).prop('checked', true);
+            selectedRooms.push(parseInt($(this).val()));
+            alert('至少需要选择一个房间');
+            return;
+        }
+
+        renderChart();
+    });
+
+    // 切换到快照历史标签页时初始化
+    $('a[href="#snapshot-tab"]').on('shown.bs.tab', function() {
+        if (!ratioChart) {
+            initCharts();
+        }
+        loadAllRoomsData();
+    });
+
+    // 窗口大小改变时重新渲染图表
+    $(window).on('resize', function() {
+        if (ratioChart) ratioChart.resize();
+    });
+});
+</script>
+
+<style>
+.section-title {
+    font-weight: 600;
+    color: #495057;
+    padding-bottom: 10px;
+    border-bottom: 2px solid #e9ecef;
+}
+.card-header h6 {
+    font-weight: 600;
+}
+.table th {
+    font-weight: 600;
+}
+.alert-info {
+    background-color: #e7f3ff;
+    border-color: #b3d9ff;
+}
+</style>
+@endsection

+ 11 - 1
routes/web.php

@@ -790,7 +790,17 @@ Route::group([
         // 个控回报配置
         $route->any('/game-some-config', 'Admin\GameSomeConfigController@index')->name('admin.game-some-config');
         $route->post('/game-some-config/update', 'Admin\GameSomeConfigController@update')->name('admin.game-some-config.update');
-        
+
+
+        // 库存模式配置
+        $route->any('/stock-mode', 'Admin\StockModeController@index')->name('admin.stock-mode');
+        $route->post('/stock-mode/update-system-config', 'Admin\StockModeController@updateSystemConfig')->name('admin.stock-mode.update-system-config');
+        $route->post('/stock-mode/update-room-stock', 'Admin\StockModeController@updateRoomStock')->name('admin.stock-mode.update-room-stock');
+        $route->post('/stock-mode/update-stock', 'Admin\StockModeController@updateStock')->name('admin.stock-mode.update-stock');
+        $route->get('/stock-mode/stats', 'Admin\StockModeController@getRoomStockStats')->name('admin.stock-mode.stats');
+        $route->get('/stock-mode/snapshot-history', 'Admin\StockModeController@getSnapshotHistory')->name('admin.stock-mode.snapshot-history');
+
+
         // 游戏税收比例配置
         $route->any('/game-tax', 'Admin\GameTaxController@index')->name('admin.game-tax');
         $route->post('/game-tax/update', 'Admin\GameTaxController@update')->name('admin.game-tax.update');