Răsfoiți Sursa

第三方游戏

pxb 3 zile în urmă
părinte
comite
007f6f14ef

+ 9 - 0
src/components/Dialog/index.vue

@@ -54,6 +54,15 @@ export default {
 
     }
   },
+  watch: {
+    // dialogActions 变化时同步更新 actions,支持动态标题等
+    dialogActions: {
+      handler(val) {
+        this.actions = Object.assign({}, this.actions, val)
+      },
+      deep: true
+    }
+  },
   mounted() {
   },
   created() {

+ 27 - 0
src/router/index.js

@@ -134,6 +134,33 @@ export const constantRoutes = [
       },
     ],
   },
+  {
+    path: "/thirdGame",
+    name: "thirdGame",
+    component: Layout,
+    redirect: "/thirdGame/order",
+    meta: { title: "第三方游戏", icon: "form" },
+    children: [
+      {
+        path: "order",
+        component: () => import("@/views/thirdGame/order/index"),
+        name: "ThirdGameOrder",
+        meta: { title: "第三方游戏订单" },
+      },
+      {
+        path: "category",
+        component: () => import("@/views/thirdGame/setting/index"),
+        name: "ThirdGameCategory",
+        meta: { title: "游戏大类" },
+      },
+      {
+        path: "list",
+        component: () => import("@/views/thirdGame/list/index"),
+        name: "ThirdGameList",
+        meta: { title: "游戏列表" },
+      },
+    ],
+  },
   {
     path: "/withdraw",
     name: "withdraw",

+ 526 - 0
src/views/thirdGame/list/index.vue

@@ -0,0 +1,526 @@
+<template>
+  <div class="new-app-container layout-vertical third-game-list-page">
+    <div class="content-panel">
+      <div class="filter-panel">
+        <el-form inline :model="query" class="filter-form">
+          <el-form-item label="游戏分类">
+            <el-select v-model="query.category" clearable placeholder="请选择" style="width: 130px">
+              <el-option label="全部" value="" />
+              <el-option v-for="c in categoryOptions" :key="c" :label="c" :value="c" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="平台类型">
+            <el-select v-model="query.platform" clearable placeholder="请选择" style="width: 130px">
+              <el-option label="全部" value="" />
+              <el-option v-for="p in platformOptions" :key="p" :label="p" :value="p" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="显示名称">
+            <el-input v-model="query.display_name" clearable placeholder="请输入" style="width: 150px" />
+          </el-form-item>
+          <el-form-item label="平台状态">
+            <el-select v-model="query.status" clearable placeholder="请选择" style="width: 120px">
+              <el-option label="全部" value="" />
+              <el-option label="正常" value="正常" />
+              <el-option label="停用" value="停用" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="支持终端类型">
+            <el-select v-model="query.terminal" clearable placeholder="请选择" style="width: 150px">
+              <el-option label="全部" value="" />
+              <el-option label="电脑网页" value="电脑网页" />
+              <el-option label="手机网页" value="手机网页" />
+              <el-option label="电脑/手机网页" value="电脑/手机网页" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="支持网络">
+            <el-select v-model="query.network" clearable placeholder="请选择" style="width: 120px">
+              <el-option label="全部" value="" />
+              <el-option v-for="n in networkOptions" :key="n" :label="n" :value="n" />
+            </el-select>
+          </el-form-item>
+          <el-form-item class="filter-actions">
+            <el-button type="primary" size="small" :loading="loading" @click="handleSearch">查询</el-button>
+            <el-button size="small" @click="handleReset">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+
+      <div class="table-section">
+        <el-table v-loading="loading" :data="pagedData"
+          height="calc(100vh - 330px)" style="width: 100%; border-radius: 12px; overflow: hidden;"
+          empty-text="暂无数据">
+          <el-table-column type="index" label="序号" width="70" align="center" />
+          <el-table-column prop="platform_id" label="平台ID" width="100" align="center" />
+          <el-table-column prop="category" label="游戏分类" width="140" align="center" />
+          <el-table-column prop="platform" label="平台类型" width="130" align="center" />
+          <el-table-column prop="display_name" label="显示名称" width="200" align="center" />
+          <el-table-column prop="terminal" label="支持终端类型" width="180" align="center" />
+          <el-table-column prop="network" label="支持网络" width="140" align="center" />
+          <el-table-column label="平台状态" width="120" align="center">
+            <template slot-scope="{ row }">
+              <el-switch v-model="row.platform_status"
+                :active-value="'正常'" :inactive-value="'停用'" active-color="#13ce66" inactive-color="#f56c6c"
+                @change="handlePlatformStatusChange(row)" />
+            </template>
+          </el-table-column>
+          <el-table-column prop="sort" label="排序" width="140" align="center">
+            <template slot-scope="{ row }">
+              <el-input-number v-model="row.sort" :min="1" size="mini" controls-position="right" style="width: 100px"
+                @change="handleSortChange(row)" />
+            </template>
+          </el-table-column>
+          <el-table-column label="PC游戏图片" width="130" align="center">
+            <template slot-scope="{ row }">
+              <el-image v-if="row.pc_image" :src="row.pc_image" style="width: 40px; height: 40px;" fit="cover"
+                :preview-src-list="[row.pc_image]" />
+              <span v-else>--</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="H5/APP游戏图片" width="140" align="center">
+            <template slot-scope="{ row }">
+              <el-image v-if="row.h5_image" :src="row.h5_image" style="width: 40px; height: 40px;" fit="cover"
+                :preview-src-list="[row.h5_image]" />
+              <span v-else>--</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" width="180" align="center" fixed="right">
+            <template slot-scope="{ row }">
+              <el-button type="text" size="small" class="ym-view-btn" @click="handleEditImage(row)">修改</el-button>
+              <el-button v-if="row.has_manager" type="text" size="small" class="ym-view-btn"
+                @click="handleGameManage(row)">游戏管理</el-button>
+              <span v-else>--</span>
+            </template>
+          </el-table-column>
+        </el-table>
+        <el-pagination v-if="tableData.length" background
+          layout="total, prev, pager, next, jumper" :page-size="pageSize"
+          :page-sizes="[15, 30, 45, 60]" :current-page="page"
+          :total="tableData.length"
+          @current-change="page = $event" @size-change="handleSizeChange" />
+      </div>
+    </div>
+
+    <!-- 游戏管理弹窗 -->
+    <Dialog ref="manageDialog" :dialog-actions="{
+      width: 'auto',
+      title: manageTitle,
+      confirmButtom: false,
+    }" close-on-click-modal="false" top="5vh" custom-class="game-manage-dialog">
+      <template slot="content">
+        <div class="manage-dialog-content">
+          <!-- 内部分页列表 -->
+          <div class="inner-filter">
+            <el-form inline :model="innerQuery" class="inner-form">
+              <el-form-item label="游戏ID">
+                <el-input v-model="innerQuery.game_id" clearable placeholder="请输入" style="width: 120px" />
+              </el-form-item>
+              <el-form-item label="游戏名称">
+                <el-input v-model="innerQuery.game_name" clearable placeholder="请输入" style="width: 150px" />
+              </el-form-item>
+              <el-form-item label="状态">
+                <el-select v-model="innerQuery.enabled" clearable placeholder="请选择" style="width: 110px">
+                  <el-option label="全部" value="" />
+                  <el-option label="启用" :value="true" />
+                  <el-option label="停用" :value="false" />
+                </el-select>
+              </el-form-item>
+              <el-form-item label="支持终端类型">
+                <el-select v-model="innerQuery.terminal" clearable placeholder="请选择" style="width: 150px">
+                  <el-option label="全部" value="" />
+                  <el-option label="电脑网页" value="电脑网页" />
+                  <el-option label="手机网页" value="手机网页" />
+                  <el-option label="电脑/手机网页" value="电脑/手机网页" />
+                </el-select>
+              </el-form-item>
+              <el-form-item label="支持网络">
+                <el-select v-model="innerQuery.network" clearable placeholder="请选择" style="width: 120px">
+                  <el-option label="全部" value="" />
+                  <el-option v-for="n in networkOptions" :key="n" :label="n" :value="n" />
+                </el-select>
+              </el-form-item>
+              <el-form-item>
+                <el-button type="primary" size="small" @click="handleInnerSearch">查询</el-button>
+              </el-form-item>
+            </el-form>
+          </div>
+
+          <el-table v-loading="innerLoading" :data="innerPagedData" height="420px"
+            style="width: 100%; border-radius: 8px; overflow: hidden;" empty-text="暂无数据">
+            <el-table-column type="index" label="序号" width="70" align="center" />
+            <el-table-column prop="game_id" label="游戏ID" width="90" align="center" />
+            <el-table-column prop="game_name" label="游戏名称" width="180" align="center" />
+            <el-table-column label="PC游戏图片" width="130" align="center">
+              <template slot-scope="{ row }">
+                <el-image v-if="row.pc_image" :src="row.pc_image" style="width: 40px; height: 40px;" fit="cover"
+                  :preview-src-list="[row.pc_image]" />
+                <span v-else>--</span>
+              </template>
+            </el-table-column>
+            <el-table-column label="H5/APP游戏图片" width="140" align="center">
+              <template slot-scope="{ row }">
+                <el-image v-if="row.h5_image" :src="row.h5_image" style="width: 40px; height: 40px;" fit="cover"
+                  :preview-src-list="[row.h5_image]" />
+                <span v-else>--</span>
+              </template>
+            </el-table-column>
+            <el-table-column label="排序" width="140" align="center">
+              <template slot-scope="{ row }">
+                <el-input-number v-model="row.sort" :min="1" size="mini" controls-position="right" style="width: 100px"
+                  @change="handleInnerSortChange(row)" />
+              </template>
+            </el-table-column>
+            <el-table-column label="状态" width="120" align="center">
+              <template slot-scope="{ row }">
+                <el-switch v-model="row.enabled" active-color="#13ce66" inactive-color="#f56c6c"
+                  @change="handleInnerEnabledChange(row)" />
+              </template>
+            </el-table-column>
+            <el-table-column prop="terminal" label="支持终端类型" width="180" align="center" />
+            <el-table-column prop="network" label="支持网络" width="140" align="center" />
+            <el-table-column label="操作" width="90" align="center" fixed="right">
+              <template slot-scope="{ row }">
+                <el-button type="text" size="small" class="ym-view-btn" @click="handleInnerEdit(row)">修改</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+          <el-pagination v-if="innerTotal" background
+            layout="total, prev, pager, next, jumper" :page-size="innerPageSize"
+            :page-sizes="[15, 30, 45, 60]" :current-page="innerPage"
+            :total="innerTotal"
+            @current-change="innerPage = $event" @size-change="handleInnerSizeChange" />
+        </div>
+      </template>
+    </Dialog>
+
+    <!-- 游戏修改弹窗 -->
+    <Dialog ref="innerEditDialog" :dialog-actions="{
+      width: '560px',
+      title: '修改游戏',
+    }" top="8vh" @confirm="handleInnerConfirm">
+      <template slot="content">
+        <el-form ref="innerFormRef" :model="innerEditForm" label-width="130px">
+          <el-form-item label="游戏名称">
+            <el-input v-model="innerEditForm.game_name" placeholder="请输入" style="width: 250px" />
+          </el-form-item>
+          <el-form-item label="游戏分类">
+            <el-select v-model="innerEditForm.category" style="width: 250px">
+              <el-option v-for="c in categoryOptions" :key="c" :label="c" :value="c" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="平台类型">
+            <el-select v-model="innerEditForm.platform" style="width: 250px">
+              <el-option v-for="p in platformOptions" :key="p" :label="p" :value="p" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="PC游戏图片">
+            <UploadOneImage v-model="innerEditForm.pc_image" />
+          </el-form-item>
+          <el-form-item label="H5/APP游戏图片">
+            <UploadOneImage v-model="innerEditForm.h5_image" />
+          </el-form-item>
+          <el-form-item label="是否启用">
+            <el-radio-group v-model="innerEditForm.enabled">
+              <el-radio :label="true">启用</el-radio>
+              <el-radio :label="false">停用</el-radio>
+            </el-radio-group>
+          </el-form-item>
+        </el-form>
+      </template>
+    </Dialog>
+
+    <!-- 修改弹窗 -->
+    <Dialog ref="imageEditDialog" :dialog-actions="{
+      width: '560px',
+      title: '修改',
+    }" top="8vh" @confirm="handleImageConfirm">
+      <template slot="content">
+        <el-form ref="imageFormRef" :model="imageEditForm" label-width="130px">
+          <el-form-item label="显示名称">
+            <el-input v-model="imageEditForm.display_name" placeholder="请输入" style="width: 250px" />
+          </el-form-item>
+          <el-form-item label="PC游戏图片">
+            <UploadOneImage v-model="imageEditForm.pc_image" />
+          </el-form-item>
+          <el-form-item label="H5/APP游戏图片">
+            <UploadOneImage v-model="imageEditForm.h5_image" />
+          </el-form-item>
+        </el-form>
+      </template>
+    </Dialog>
+  </div>
+</template>
+
+<script>
+import Dialog from '@/components/Dialog/index.vue'
+import UploadOneImage from '@/components/UploadOneImage/index.vue'
+
+// 假数据
+const MOCK_DATA = [
+  // 电子
+  { id: 1, platform_id: 'FL01', game_id: 'G001', category: '电子', platform: 'jdb', display_name: 'JDB电子', terminal: '电脑/手机网页', network: '大陆', platform_status: '正常', sort: 1, pc_image: '', h5_image: '', has_manager: true },
+  { id: 2, platform_id: 'FL02', game_id: 'G002', category: '电子', platform: 'mg', display_name: 'MG电子', terminal: '电脑/手机网页', network: '香港', platform_status: '停用', sort: 2, pc_image: '', h5_image: '', has_manager: false },
+  // 捕鱼
+  { id: 3, platform_id: 'FL03', game_id: 'G003', category: '捕鱼', platform: 'pt', display_name: 'PT捕鱼', terminal: '手机网页', network: '韩国', platform_status: '正常', sort: 3, pc_image: '', h5_image: '', has_manager: false },
+  { id: 4, platform_id: 'FL04', game_id: 'G004', category: '捕鱼', platform: 'fg', display_name: 'FG捕鱼', terminal: '电脑/手机网页', network: '日本', platform_status: '正常', sort: 4, pc_image: '', h5_image: '', has_manager: true },
+  // 棋牌
+  { id: 5, platform_id: 'FL05', game_id: 'G005', category: '棋牌', platform: 'ky', display_name: '开元棋牌', terminal: '电脑/手机网页', network: '大陆', platform_status: '正常', sort: 5, pc_image: '', h5_image: '', has_manager: true },
+  { id: 6, platform_id: 'FL06', game_id: 'G006', category: '棋牌', platform: 'db7', display_name: 'DB棋牌', terminal: '电脑网页', network: '香港', platform_status: '停用', sort: 6, pc_image: '', h5_image: '', has_manager: false },
+  // 视讯
+  { id: 7, platform_id: 'FL07', game_id: 'G007', category: '视讯', platform: 'ag', display_name: 'PA真人', terminal: '电脑/手机网页', network: '大陆', platform_status: '正常', sort: 7, pc_image: '', h5_image: '', has_manager: true },
+  { id: 8, platform_id: 'FL08', game_id: 'G008', category: '视讯', platform: 'evo', display_name: 'EVO真人', terminal: '手机网页', network: '香港', platform_status: '停用', sort: 8, pc_image: '', h5_image: '', has_manager: true },
+  { id: 9, platform_id: 'FL09', game_id: 'G009', category: '视讯', platform: 'bbin', display_name: 'BBIN真人', terminal: '电脑/手机网页', network: '韩国', platform_status: '正常', sort: 9, pc_image: '', h5_image: '', has_manager: false }
+]
+
+// 各平台对应的游戏名(点击游戏管理时按平台生成)
+const PLATFORM_GAMES = {
+  jdb: ['JDB夺宝', 'JDB招财进宝', 'JDB三国'],
+  mg: ['MG大丰收', 'MG糖果派对', 'MG龙虎斗'],
+  pt: ['PT深海捕鱼', 'PT龙王捕鱼', 'PT美人鱼'],
+  fg: ['FG捕鱼达人', 'FG捕鱼大师', 'FG深海狂鲨'],
+  ky: ['开元斗地主', '开元麻将', '开元百人牛牛'],
+  db7: ['DB德州扑克', 'DB斗牛', 'DB炸金花'],
+  ag: ['PA龙虎斗', 'PA骰宝', 'PA百家乐'],
+  evo: ['EVO百家乐', 'EVO龙虎', 'EVO三公'],
+  bbin: ['BBIN轮盘', 'BBIN德州', 'BBIN骰宝']
+}
+
+// 默认内层游戏假数据(兜底)
+const INNER_MOCK = [
+  { id: 1, game_id: 'G001', game_name: '游戏1', sort: 1, enabled: true, terminal: '电脑/手机网页', network: '大陆', category: '电子', platform: 'jdb', pc_image: '', h5_image: '' },
+  { id: 2, game_id: 'G002', game_name: '游戏2', sort: 2, enabled: true, terminal: '电脑网页', network: '香港', category: '电子', platform: 'jdb', pc_image: '', h5_image: '' }
+]
+
+export default {
+  name: 'ThirdGameList',
+  components: {
+    Dialog,
+    UploadOneImage
+  },
+  data() {
+    return {
+      loading: false,
+      page: 1,
+      pageSize: 15,
+      tableData: [...MOCK_DATA],
+      filteredData: [...MOCK_DATA],
+      categoryOptions: ['视讯', '电子', '捕鱼', '体育', '棋牌'],
+      platformOptions: ['ag', 'pg', 'bg', 'dg', 'db5'],
+      networkOptions: ['大陆', '香港', '韩国', '日本'],
+      query: {
+        category: '',
+        platform: '',
+        display_name: '',
+        status: '',
+        terminal: '',
+        network: ''
+      },
+      // 内层游戏管理
+      innerLoading: false,
+      innerPage: 1,
+      innerPageSize: 15,
+      innerTotal: 0,
+      innerTableData: [...INNER_MOCK],
+      innerFiltered: [...INNER_MOCK],
+      innerQuery: {
+        game_id: '',
+        game_name: '',
+        enabled: '',
+        terminal: '',
+        network: ''
+      },
+      innerEditForm: {},
+      imageEditForm: {},
+      manageTitle: '游戏管理'
+    }
+  },
+  computed: {
+    pagedData() {
+      const start = (this.page - 1) * this.pageSize
+      return this.filteredData.slice(start, start + this.pageSize)
+    },
+    innerPagedData() {
+      const start = (this.innerPage - 1) * this.innerPageSize
+      return this.innerFiltered.slice(start, start + this.innerPageSize)
+    }
+  },
+  methods: {
+    handlePlatformStatusChange(row) {
+      this.$message.success(`平台状态已改为${row.platform_status}`)
+    },
+    handleSortChange(row) {
+      if (row.sort < 1) row.sort = 1
+      this.$message.success(`排序已修改为 ${row.sort}`)
+    },
+    handleInnerSortChange(row) {
+      if (row.sort < 1) row.sort = 1
+      this.$message.success(`游戏 ${row.game_name} 排序已修改为 ${row.sort}`)
+    },
+    handleSizeChange(val) {
+      this.pageSize = val
+      this.page = 1
+    },
+    handleSearch() {
+      this.page = 1
+      let filtered = [...this.tableData]
+      if (this.query.category) filtered = filtered.filter(r => r.category === this.query.category)
+      if (this.query.platform) filtered = filtered.filter(r => r.platform === this.query.platform)
+      if (this.query.display_name) filtered = filtered.filter(r => r.display_name.includes(this.query.display_name))
+      if (this.query.status) filtered = filtered.filter(r => r.platform_status === this.query.status)
+      if (this.query.terminal) filtered = filtered.filter(r => r.terminal === this.query.terminal)
+      if (this.query.network) filtered = filtered.filter(r => r.network === this.query.network)
+      this.filteredData = filtered
+    },
+    // 重置筛选条件到初始状态
+    handleReset() {
+      this.query = {
+        category: '',
+        platform: '',
+        display_name: '',
+        status: '',
+        terminal: '',
+        network: ''
+      }
+      this.filteredData = [...this.tableData]
+      this.page = 1
+    },
+    // 游戏管理弹窗
+    handleGameManage(row) {
+      // 弹窗标题用显示名称,如「JDB电子游戏管理」
+      this.manageTitle = `${row.display_name || ''}游戏管理`
+      // 根据当前行平台生成对应游戏列表
+      const games = PLATFORM_GAMES[row.platform] || ['游戏1', '游戏2']
+      const terminalOptions = ['电脑网页', '手机网页', '电脑/手机网页']
+      const networkOptions = ['大陆', '香港', '韩国', '日本']
+      const list = games.map((name, idx) => ({
+        id: idx + 1,
+        game_id: `${String(row.platform).toUpperCase()}${String(idx + 1).padStart(3, '0')}`,
+        game_name: name,
+        sort: idx + 1,
+        enabled: Math.random() > 0.3, // 随机启用/停用
+        terminal: terminalOptions[Math.floor(Math.random() * terminalOptions.length)],
+        network: networkOptions[Math.floor(Math.random() * networkOptions.length)],
+        category: row.category,
+        platform: row.platform,
+        pc_image: '',
+        h5_image: ''
+      }))
+      this.innerTableData = list
+      this.innerFiltered = [...list]
+      this.innerTotal = list.length
+      this.innerPage = 1
+      this.$refs.manageDialog.open()
+    },
+    handleInnerSizeChange(val) {
+      this.innerPageSize = val
+      this.innerPage = 1
+    },
+    handleInnerSearch() {
+      this.innerPage = 1
+      let filtered = [...this.innerTableData]
+      if (this.innerQuery.game_id) filtered = filtered.filter(r => r.game_id.includes(this.innerQuery.game_id))
+      if (this.innerQuery.game_name) filtered = filtered.filter(r => r.game_name.includes(this.innerQuery.game_name))
+      // enabled 可能是布尔值(0 不是),需区分空串和值
+      if (this.innerQuery.enabled !== '' && this.innerQuery.enabled !== null && this.innerQuery.enabled !== undefined) {
+        filtered = filtered.filter(r => r.enabled === this.innerQuery.enabled)
+      }
+      if (this.innerQuery.terminal) filtered = filtered.filter(r => r.terminal === this.innerQuery.terminal)
+      if (this.innerQuery.network) filtered = filtered.filter(r => r.network === this.innerQuery.network)
+      this.innerFiltered = filtered
+      this.innerTotal = filtered.length
+    },
+    handleInnerEnabledChange(row) {
+      this.$message.success(`游戏 ${row.game_name} 已${row.enabled ? '启用' : '停用'}`)
+    },
+    handleInnerEdit(row) {
+      this.innerEditForm = { ...row }
+      this.$refs.innerEditDialog.open()
+    },
+    handleInnerConfirm() {
+      const idx = this.innerTableData.findIndex(item => item.id === this.innerEditForm.id)
+      if (idx > -1) {
+        this.innerTableData[idx] = { ...this.innerEditForm }
+        this.innerFiltered = [...this.innerTableData]
+        this.innerTotal = this.innerFiltered.length
+      }
+      this.$message.success('修改成功')
+      this.$refs.innerEditDialog.close()
+    },
+    // 打开修改图片弹窗
+    handleEditImage(row) {
+      this.imageEditForm = { ...row }
+      this.$refs.imageEditDialog.open()
+    },
+    // 保存修改图片到列表
+    handleImageConfirm() {
+      const idx = this.tableData.findIndex(item => item.id === this.imageEditForm.id)
+      if (idx > -1) {
+        this.tableData[idx].display_name = this.imageEditForm.display_name
+        this.tableData[idx].pc_image = this.imageEditForm.pc_image
+        this.tableData[idx].h5_image = this.imageEditForm.h5_image
+        this.filteredData = [...this.tableData]
+      }
+      this.$message.success('修改成功')
+      this.$refs.imageEditDialog.close()
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.third-game-list-page {
+  padding: 16px;
+  background: #f5f7fa;
+}
+.content-panel {
+  min-height: calc(100vh - 82px);
+  overflow: hidden;
+  border-radius: 8px;
+  background: #fff;
+}
+.filter-panel {
+  padding: 16px 20px 0;
+  border-bottom: 1px solid #ebeef5;
+  background: #fff;
+}
+.filter-form {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+}
+.filter-actions {
+  margin-left: auto;
+}
+.table-section {
+  padding: 18px 20px 20px;
+}
+.el-pagination {
+  margin-top: 14px;
+  text-align: right;
+}
+.inner-filter {
+  padding-bottom: 12px;
+}
+.inner-form {
+  display: flex;
+  align-items: center;
+}
+.ym-view-btn {
+  padding: 8px 0;
+  font-size: 14px;
+}
+/* 游戏管理弹窗:宽度自适应,内容不超出屏幕,滚动条在内 */
+.manage-dialog-content {
+  max-height: calc(100vh - 120px);
+  overflow-y: auto;
+  padding-right: 8px;
+}
+</style>
+<style lang="scss">
+/* 游戏管理弹窗宽度自适应屏幕(width: auto 配合 min-width) */
+.game-manage-dialog {
+  width: auto !important;
+  min-width:700px;
+  max-width: calc(75vw - 40px);
+}
+</style>

+ 328 - 0
src/views/thirdGame/order/index.vue

@@ -0,0 +1,328 @@
+<template>
+  <div class="new-app-container layout-vertical third-game-order-page">
+    <div class="content-panel">
+      <div class="filter-panel">
+        <el-form inline :model="query" class="filter-form">
+          <el-form-item label="注册来源">
+            <el-select v-model="query.from" clearable placeholder="请选择" style="width: 130px">
+              <el-option label="全部" value="" />
+              <el-option label="tg用户" value="-1" />
+              <el-option label="后台客服账号" value="0" />
+              <el-option label="真实用户" value="1" />
+              <el-option label="虚拟用户" value="2" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="会员ID">
+            <el-input v-model="query.member_id" clearable placeholder="请输入" style="width: 130px" />
+          </el-form-item>
+          <el-form-item label="会员昵称">
+            <el-input v-model="query.first_name" clearable placeholder="请输入" style="width: 130px" />
+          </el-form-item>
+          <el-form-item label="订单号">
+            <el-input v-model="query.order_no" clearable placeholder="请输入" style="width: 150px" />
+          </el-form-item>
+          <el-form-item label="游戏分类">
+            <el-select v-model="query.category" clearable placeholder="请选择" style="width: 130px">
+              <el-option label="全部" value="" />
+              <el-option v-for="c in categoryOptions" :key="c" :label="c" :value="c" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="平台类型">
+            <el-select v-model="query.platform" clearable placeholder="请选择" style="width: 130px">
+              <el-option label="全部" value="" />
+              <el-option v-for="p in platformOptions" :key="p" :label="p" :value="p" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="结算状态">
+            <el-select v-model="query.settle_status" clearable placeholder="请选择" style="width: 130px">
+              <el-option label="全部" value="" />
+              <el-option label="未结算" value="未结算" />
+              <el-option label="已结算" value="已结算" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="投注时间">
+            <el-date-picker
+              v-model="query.dateRange"
+              type="daterange"
+              align="right"
+              unlink-panels
+              range-separator="至"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+              value-format="yyyy-MM-dd HH:mm:ss"
+              format="yyyy年MM月dd日"
+              :default-time="['00:00:00', '23:59:59']"
+              :default-value="[new Date(new Date().getFullYear(), new Date().getMonth() - 1, 1), new Date()]"
+              :picker-options="pickerOptions"
+              style="width: 380px"
+            />
+          </el-form-item>
+          <el-form-item class="filter-actions">
+            <el-button type="primary" size="small" :loading="loading" @click="handleSearch">查询</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+
+      <div class="table-section">
+        <el-table v-loading="loading" :data="pagedData"
+          height="calc(100vh - 380px)" style="width: 100%; border-radius: 12px; overflow: hidden;"
+          empty-text="暂无数据">
+          <el-table-column type="index" label="序号" width="80" align="center" />
+          <el-table-column prop="member_id" label="会员ID" width="110" align="center" />
+          <el-table-column prop="member_name" label="会员昵称" width="130" align="center" />
+          <el-table-column prop="from" label="注册来源" width="110" align="center">
+            <template slot-scope="{ row }">
+              <span :style="{
+                color: Number(row.from) === 1 ? '#05A58F' : Number(row.from) === 2 ? '#F3061F' : '#606266'
+              }">
+                {{
+                  Number(row.from) === 1 ? '真实用户' :
+                    Number(row.from) === 2 ? '虚拟用户' :
+                      Number(row.from) === -1 ? 'tg用户' :
+                        Number(row.from) === 0 ? '后台客服账号' : '--'
+                }}
+              </span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="order_no" label="订单号" width="180" align="center" />
+          <el-table-column prop="game_type" label="游戏类型" width="110" align="center" />
+          <el-table-column prop="platform" label="平台类型" width="100" align="center" />
+          <el-table-column prop="game_name" label="游戏" min-width="140" align="center" />
+          <el-table-column prop="amount" label="投注金额" width="110" align="center">
+            <template slot-scope="{ row }">
+              <span style="color: #1677FF;">{{ row.amount != null ? Number(row.amount).toFixed(2) : '--' }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="currency" label="游戏货币" width="100" align="center" />
+          <el-table-column prop="valid_amount" label="有效投注" width="110" align="center">
+            <template slot-scope="{ row }">
+              <span style="color: #1677FF;">{{ row.valid_amount != null ? Number(row.valid_amount).toFixed(2) : '--' }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="win_loss" label="输赢金额" width="110" align="center">
+            <template slot-scope="{ row }">
+              <span :style="{ color: Number(row.win_loss) > 0 ? '#18C80A' : Number(row.win_loss) < 0 ? '#F56C6C' : '#606266' }">
+                {{ row.win_loss != null ? Number(row.win_loss).toFixed(2) : '--' }}
+              </span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="settle_status" label="结算状态" width="100" align="center">
+            <template slot-scope="{ row }">
+              <span :style="{ color: row.settle_status === '已结算' ? '#18C80A' : '#909399' }">{{ row.settle_status }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" width="90" align="center" fixed="right">
+            <template slot-scope="{ row }">
+              <el-button type="text" size="small" class="ym-view-btn" @click="handleView(row)">查看</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        <el-pagination v-if="tableData.length" background
+          layout="total, prev, pager, next, jumper" :page-size="pageSize"
+          :page-sizes="[15, 30, 45, 60]" :current-page="page"
+          :total="tableData.length"
+          @current-change="page = $event" @size-change="handleSizeChange" />
+      </div>
+    </div>
+
+    <!-- 查看详情弹窗 -->
+    <Dialog ref="viewDialog" :dialog-actions="{
+      width: '820px',
+      title: '第三方游戏订单详情',
+      confirmButtom: false,
+    }">
+      <template slot="content">
+        <div style="display: flex; flex-wrap: wrap; gap: 12px; padding: 10px 0; max-height: 400px; overflow-y: auto;">
+          <div v-for="field in viewFields" :key="field.prop"
+            style="flex: 0 0 calc(33.33% - 12px); background: #f5f7fa; border-radius: 6px; padding: 12px 16px; box-sizing: border-box; border: 1px solid #e4e7ed;">
+            <div style="font-size: 13px; color: #909399; margin-bottom: 4px;">{{ field.label }}</div>
+            <div :style="{ color: field.color || '#303133', fontSize: '15px' }">{{ field.value }}</div>
+          </div>
+        </div>
+      </template>
+    </Dialog>
+  </div>
+</template>
+
+<script>
+import Dialog from '@/components/Dialog/index.vue'
+import { buildDatePickerOptions } from '@/utils/index'
+
+// 假数据
+const MOCK_DATA = [
+  { id: 1, member_id: 10001, member_name: 'test01', from: '1', order_no: 'TG202608130001', game_type: '电子', platform: 'pg', game_name: '麻将胡了', amount: 100, currency: 'CNY', valid_amount: 100, win_loss: 50, settle_status: '已结算', bet_time: '2026-08-13 10:20:30' },
+  { id: 2, member_id: 10002, member_name: 'test02', from: '2', order_no: 'TG202608130002', game_type: '捕鱼', platform: 'ag', game_name: '冰川时代', amount: 200, currency: 'CNY', valid_amount: 180, win_loss: -60, settle_status: '已结算', bet_time: '2026-08-13 11:00:00' },
+  { id: 3, member_id: 10003, member_name: 'test03', from: '1', order_no: 'TG202608130003', game_type: '棋牌', platform: 'bg', game_name: '捕鱼达人', amount: 500, currency: 'CNY', valid_amount: 500, win_loss: 120, settle_status: '未结算', bet_time: '2026-08-13 14:30:00' },
+  { id: 4, member_id: 10004, member_name: 'test04', from: '0', order_no: 'TG202608130004', game_type: '体育', platform: 'dg', game_name: '功夫熊猫', amount: 1000, currency: 'CNY', valid_amount: 1000, win_loss: -300, settle_status: '已结算', bet_time: '2026-08-13 16:40:00' },
+  { id: 5, member_id: 10005, member_name: 'test05', from: '1', order_no: 'TG202608130005', game_type: '视讯', platform: 'db5', game_name: '捕鱼3D', amount: 300, currency: 'CNY', valid_amount: 300, win_loss: 0, settle_status: '未结算', bet_time: '2026-08-13 18:00:00' }
+]
+
+export default {
+  name: 'ThirdGameOrder',
+  components: {
+    Dialog
+  },
+  data() {
+    return {
+      loading: false,
+      page: 1,
+      pageSize: 15,
+      tableData: [...MOCK_DATA],
+      filteredData: [...MOCK_DATA],
+      viewRow: null,
+      categoryOptions: ['视讯', '电子', '捕鱼', '体育', '棋牌'],
+      platformOptions: ['ag', 'pg', 'bg', 'dg', 'db5'],
+      query: {
+        from: '',
+        member_id: '',
+        first_name: '',
+        order_no: '',
+        category: '',
+        platform: '',
+        settle_status: '',
+        dateRange: []
+      },
+      pickerOptions: buildDatePickerOptions({
+        shortcuts: [
+          {
+            text: '今天',
+            onClick(picker) {
+              const start = new Date()
+              start.setHours(0, 0, 0, 0)
+              const end = new Date()
+              end.setHours(23, 59, 59, 999)
+              picker.$emit('pick', [start, end])
+            }
+          },
+          {
+            text: '昨天',
+            onClick(picker) {
+              const start = new Date()
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 1)
+              start.setHours(0, 0, 0, 0)
+              const end = new Date()
+              end.setTime(end.getTime() - 3600 * 1000 * 24 * 1)
+              end.setHours(23, 59, 59, 999)
+              picker.$emit('pick', [start, end])
+            }
+          },
+          {
+            text: '7天内',
+            onClick(picker) {
+              const start = new Date()
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
+              start.setHours(0, 0, 0, 0)
+              const end = new Date()
+              end.setHours(23, 59, 59, 999)
+              picker.$emit('pick', [start, end])
+            }
+          }
+        ]
+      })
+    }
+  },
+  computed: {
+    pagedData() {
+      const start = (this.page - 1) * this.pageSize
+      return this.filteredData.slice(start, start + this.pageSize)
+    },
+    viewFields() {
+      if (!this.viewRow) return []
+      const row = this.viewRow
+      const fromMap = { '-1': 'tg用户', 0: '后台客服账号', 1: '真实用户', 2: '虚拟用户' }
+      const fromText = fromMap[row.from] || '--'
+      const fromColor = Number(row.from) === 1 ? '#05A58F' : Number(row.from) === 2 ? '#F3061F' : '#606266'
+      const winLossColor = Number(row.win_loss) > 0 ? '#18C80A' : Number(row.win_loss) < 0 ? '#F56C6C' : '#606266'
+      return [
+        { label: '序号', prop: 'id', value: row.id },
+        { label: '会员ID', prop: 'member_id', value: row.member_id },
+        { label: '会员昵称', prop: 'member_name', value: row.member_name || '--' },
+        { label: '注册来源', prop: 'from_text', value: fromText, color: fromColor },
+        { label: '订单号', prop: 'order_no', value: row.order_no || '--' },
+        { label: '游戏类型', prop: 'game_type', value: row.game_type || '--' },
+        { label: '平台类型', prop: 'platform', value: row.platform || '--' },
+        { label: '游戏', prop: 'game_name', value: row.game_name || '--' },
+        { label: '投注金额', prop: 'amount', value: row.amount != null ? Number(row.amount).toFixed(2) : '--', color: '#1677FF' },
+        { label: '游戏货币', prop: 'currency', value: row.currency || '--' },
+        { label: '有效投注', prop: 'valid_amount', value: row.valid_amount != null ? Number(row.valid_amount).toFixed(2) : '--', color: '#1677FF' },
+        { label: '输赢金额', prop: 'win_loss', value: row.win_loss != null ? Number(row.win_loss).toFixed(2) : '--', color: winLossColor },
+        { label: '结算状态', prop: 'settle_status', value: row.settle_status || '--', color: row.settle_status === '已结算' ? '#18C80A' : '#909399' },
+        { label: '投注时间', prop: 'bet_time', value: row.bet_time || '--' }
+      ]
+    }
+  },
+  methods: {
+    handleSizeChange(val) {
+      this.pageSize = val
+      this.page = 1
+    },
+    handleSearch() {
+      this.page = 1
+      let filtered = [...this.tableData]
+      if (this.query.from) {
+        filtered = filtered.filter(r => String(r.from) === this.query.from)
+      }
+      if (this.query.member_id) {
+        filtered = filtered.filter(r => String(r.member_id).includes(this.query.member_id))
+      }
+      if (this.query.first_name) {
+        filtered = filtered.filter(r => r.member_name.includes(this.query.first_name))
+      }
+      if (this.query.order_no) {
+        filtered = filtered.filter(r => r.order_no.includes(this.query.order_no))
+      }
+      if (this.query.category) {
+        filtered = filtered.filter(r => r.game_type === this.query.category)
+      }
+      if (this.query.platform) {
+        filtered = filtered.filter(r => r.platform === this.query.platform)
+      }
+      if (this.query.settle_status) {
+        filtered = filtered.filter(r => r.settle_status === this.query.settle_status)
+      }
+      this.filteredData = filtered
+    },
+    handleView(row) {
+      this.viewRow = { ...row }
+      this.$refs.viewDialog.open()
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.third-game-order-page {
+  padding: 16px;
+  background: #f5f7fa;
+}
+.content-panel {
+  min-height: calc(100vh - 82px);
+  overflow: hidden;
+  border-radius: 8px;
+  background: #fff;
+}
+.filter-panel {
+  padding: 16px 20px 0;
+  border-bottom: 1px solid #ebeef5;
+  background: #fff;
+}
+.filter-form {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+}
+.filter-actions {
+  margin-left: auto;
+}
+.table-section {
+  padding: 18px 20px 20px;
+}
+.el-pagination {
+  margin-top: 14px;
+  text-align: right;
+}
+.ym-view-btn {
+  padding: 8px 0;
+  font-size: 14px;
+}
+</style>

+ 142 - 0
src/views/thirdGame/setting/index.vue

@@ -0,0 +1,142 @@
+<template>
+  <div class="new-app-container layout-vertical game-category-page">
+    <div class="content-panel">
+      <div class="table-section">
+        <el-table v-loading="loading" :data="tableData"
+          height="calc(100vh - 200px)" style="width: 100%; border-radius: 12px; overflow: hidden;"
+          empty-text="暂无数据">
+          <el-table-column prop="id" label="游戏大类ID" width="120" align="center" />
+          <el-table-column prop="name" label="游戏大类名称" width="140" align="center" />
+          <el-table-column label="PC Logo" width="120" align="center">
+            <template slot-scope="{ row }">
+              <el-image v-if="row.pc_image" :src="row.pc_image" style="width: 50px; height: 50px;" fit="cover"
+                :preview-src-list="[row.pc_image]" />
+              <span v-else>--</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="H5/APP Logo" width="130" align="center">
+            <template slot-scope="{ row }">
+              <el-image v-if="row.h5_image" :src="row.h5_image" style="width: 50px; height: 50px;" fit="cover"
+                :preview-src-list="[row.h5_image]" />
+              <span v-else>--</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="description" label="游戏说明" min-width="180" align="center" show-overflow-tooltip />
+          <el-table-column prop="sort" label="排序" width="180" align="center">
+            <template slot-scope="{ row }">
+              <el-input-number v-model="row.sort" :min="1" size="small" controls-position="right" style="width: 120px"
+                @change="handleSortChange(row)" />
+            </template>
+          </el-table-column>
+          <el-table-column label="启用状态" width="110" align="center">
+            <template slot-scope="{ row }">
+              <el-switch v-model="row.enabled" active-color="#13ce66" inactive-color="#f56c6c"
+                @change="handleEnabledChange(row)" />
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" width="100" align="center" fixed="right">
+            <template slot-scope="{ row }">
+              <el-button type="text" size="small" class="ym-view-btn" @click="handleEdit(row)">修改</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </div>
+
+    <!-- 修改弹窗 -->
+    <Dialog ref="editDialog" :dialog-actions="{
+      width: '700px',
+      title: '修改游戏大类',
+    }" :loading="saving" @confirm="handleConfirm">
+      <template slot="content">
+        <el-form ref="editFormRef" :model="editForm" label-width="120px">
+          <el-form-item label="游戏大类名称">
+            <span>{{ editForm.name }}</span>
+          </el-form-item>
+          <el-form-item label="PC Logo">
+            <UploadOneImage v-model="editForm.pc_image" />
+          </el-form-item>
+          <el-form-item label="H5/APP Logo">
+            <UploadOneImage v-model="editForm.h5_image" />
+          </el-form-item>
+          <el-form-item label="游戏说明">
+            <el-input v-model="editForm.description" type="textarea" :rows="4" placeholder="请输入游戏说明" style="width: 400px" />
+          </el-form-item>
+        </el-form>
+      </template>
+    </Dialog>
+  </div>
+</template>
+
+<script>
+import Dialog from '@/components/Dialog/index.vue'
+import UploadOneImage from '@/components/UploadOneImage/index.vue'
+
+// 假数据
+const MOCK_DATA = [
+  { id: 1, name: '视讯', pc_image: '', h5_image: '', sort: 1, description: '真人视讯游戏', enabled: true },
+  { id: 2, name: '电子', pc_image: '', h5_image: '', sort: 2, description: '电子老虎机游戏', enabled: true },
+  { id: 3, name: '捕鱼', pc_image: '', h5_image: '', sort: 3, description: '捕鱼游戏', enabled: true },
+  { id: 4, name: '体育', pc_image: '', h5_image: '', sort: 4, description: '体育赛事游戏', enabled: false },
+  { id: 5, name: '棋牌', pc_image: '', h5_image: '', sort: 5, description: '棋牌游戏', enabled: true }
+]
+
+export default {
+  name: 'ThirdGameCategory',
+  components: {
+    Dialog,
+    UploadOneImage
+  },
+  data() {
+    return {
+      loading: false,
+      saving: false,
+      tableData: [...MOCK_DATA],
+      editForm: {}
+    }
+  },
+  methods: {
+    handleSortChange(row) {
+      // 排序修改(可直接输入或上下调整),最低 1
+      if (row.sort < 1) row.sort = 1
+      this.$message.success(`游戏大类 ${row.name} 排序已修改为 ${row.sort}`)
+    },
+    handleEnabledChange(row) {
+      this.$message.success(`游戏大类 ${row.name} 已${row.enabled ? '开启' : '关闭'}`)
+    },
+    handleEdit(row) {
+      this.editForm = { ...row }
+      this.$refs.editDialog.open()
+    },
+    handleConfirm() {
+      // 保存修改到列表
+      const idx = this.tableData.findIndex(item => item.id === this.editForm.id)
+      if (idx > -1) {
+        this.tableData[idx] = { ...this.editForm }
+      }
+      this.$message.success('修改成功')
+      this.$refs.editDialog.close()
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.game-category-page {
+  padding: 16px;
+  background: #f5f7fa;
+}
+.content-panel {
+  min-height: calc(100vh - 82px);
+  overflow: hidden;
+  border-radius: 8px;
+  background: #fff;
+}
+.table-section {
+  padding: 18px 20px 20px;
+}
+.ym-view-btn {
+  padding: 8px 0;
+  font-size: 14px;
+}
+</style>