ソースを参照

新增游戏模块

Hixon 3 ヶ月 前
コミット
cf0a4e8b42

+ 2 - 2
.env.development

@@ -1,5 +1,5 @@
 # 页面标题
-VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统
+VITE_APP_TITLE = 小游戏管理平台
 
 # 开发环境配置
 VITE_APP_ENV = 'development'
@@ -29,4 +29,4 @@ VITE_APP_RSA_PRIVATE_KEY = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3C
 VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'
 
 # websocket 开关
-VITE_APP_WEBSOCKET = true
+VITE_APP_WEBSOCKET = false

+ 2 - 2
.env.production

@@ -1,5 +1,5 @@
 # 页面标题
-VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统
+VITE_APP_TITLE = 小游戏管理平台
 
 # 生产环境配置
 VITE_APP_ENV = 'production'
@@ -32,4 +32,4 @@ VITE_APP_RSA_PRIVATE_KEY = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3C
 VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'
 
 # websocket 开关
-VITE_APP_WEBSOCKET = true
+VITE_APP_WEBSOCKET = false

+ 1 - 0
.eslintrc.cjs

@@ -20,6 +20,7 @@ module.exports = {
   },
   plugins: ['vue', '@typescript-eslint', 'import', 'promise', 'node', 'prettier'],
   rules: {
+    '*': 'off',
     '@typescript-eslint/no-empty-function': 'off',
     '@typescript-eslint/no-explicit-any': 'off',
     '@typescript-eslint/no-unused-vars': 'off',

+ 63 - 0
src/api/game/channel/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ChannelVO, ChannelForm, ChannelQuery } from '@/api/game/channel/types';
+
+/**
+ * 查询渠道列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listChannel = (query?: ChannelQuery): AxiosPromise<ChannelVO[]> => {
+  return request({
+    url: '/game/channel/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询渠道详细
+ * @param id
+ */
+export const getChannel = (id: string | number): AxiosPromise<ChannelVO> => {
+  return request({
+    url: '/game/channel/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增渠道
+ * @param data
+ */
+export const addChannel = (data: ChannelForm) => {
+  return request({
+    url: '/game/channel',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改渠道
+ * @param data
+ */
+export const updateChannel = (data: ChannelForm) => {
+  return request({
+    url: '/game/channel',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除渠道
+ * @param id
+ */
+export const delChannel = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/game/channel/' + id,
+    method: 'delete'
+  });
+};

+ 235 - 0
src/api/game/channel/types.ts

@@ -0,0 +1,235 @@
+export interface ChannelVO {
+  /**
+   * 渠道ID
+   */
+  id: string | number;
+
+  /**
+   * 产品ID
+   */
+  productId: string | number;
+
+  /**
+   * 代理商ID
+   */
+  proxyId: string | number;
+
+  /**
+   * 媒体账号ID
+   */
+  accountId: string | number;
+
+  /**
+   * 产品名
+   */
+  productName: string;
+
+  /**
+   * 媒体类型
+   */
+  platType: string;
+
+  /**
+   * 投放人员名称
+   */
+  userName: string;
+
+  /**
+   * 渠道名称
+   */
+  name: string;
+
+  /**
+   * 公司名
+   */
+  company: string;
+
+  /**
+   * 运营方式
+   */
+  operatingMode: string;
+
+  /**
+   * 投放方式
+   */
+  deliveryMode: string;
+
+  /**
+   * 激活上报规则
+   */
+  activeRules: string;
+
+  /**
+   * 关键行为上报规则
+   */
+  actionRules: string;
+
+  /**
+   * 是否上报
+   */
+  isReport: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+}
+
+export interface ChannelForm extends BaseEntity {
+  /**
+   * 渠道ID
+   */
+  id?: string | number;
+
+  /**
+   * 产品ID
+   */
+  productId?: string | number;
+
+  /**
+   * 代理商ID
+   */
+  proxyId?: string | number;
+
+  /**
+   * 媒体账号ID
+   */
+  accountId?: string | number;
+
+  /**
+   * 产品名
+   */
+  productName?: string;
+
+  /**
+   * 媒体类型
+   */
+  platType?: string;
+
+  /**
+   * 投放人员名称
+   */
+  userName?: string;
+
+  /**
+   * 渠道名称
+   */
+  name?: string;
+
+  /**
+   * 公司名
+   */
+  company?: string;
+
+  /**
+   * 运营方式
+   */
+  operatingMode?: string;
+
+  /**
+   * 投放方式
+   */
+  deliveryMode?: string;
+
+  /**
+   * 激活上报规则
+   */
+  activeRules?: string;
+
+  /**
+   * 关键行为上报规则
+   */
+  actionRules?: string;
+
+  /**
+   * 是否上报
+   */
+  isReport?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+}
+
+export interface ChannelQuery extends PageQuery {
+  /**
+   * 渠道ID
+   */
+  id?: string | number;
+
+  /**
+   * 产品ID
+   */
+  productId?: string | number;
+
+  /**
+   * 代理商ID
+   */
+  proxyId?: string | number;
+
+  /**
+   * 媒体账号ID
+   */
+  accountId?: string | number;
+
+  /**
+   * 产品名
+   */
+  productName?: string;
+
+  /**
+   * 媒体类型
+   */
+  platType?: string;
+
+  /**
+   * 投放人员名称
+   */
+  userName?: string;
+
+  /**
+   * 渠道名称
+   */
+  name?: string;
+
+  /**
+   * 公司名
+   */
+  company?: string;
+
+  /**
+   * 运营方式
+   */
+  operatingMode?: string;
+
+  /**
+   * 投放方式
+   */
+  deliveryMode?: string;
+
+  /**
+   * 激活上报规则
+   */
+  activeRules?: string;
+
+  /**
+   * 关键行为上报规则
+   */
+  actionRules?: string;
+
+  /**
+   * 是否上报
+   */
+  isReport?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 70 - 0
src/api/game/product/index.ts

@@ -0,0 +1,70 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ProductVO, ProductForm, ProductQuery } from '@/api/game/product/types';
+
+/**
+ * 查询产品列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listProduct = (query?: ProductQuery): AxiosPromise<ProductVO[]> => {
+  return request({
+    url: '/game/product/list',
+    method: 'get',
+    params: query
+  });
+};
+
+export const allProduct = (query?: ProductQuery): AxiosPromise<ProductVO[]> => {
+  return request({
+    url: '/game/product/all',
+    method: 'get'
+  });
+};
+
+/**
+ * 查询产品详细
+ * @param id
+ */
+export const getProduct = (id: string | number): AxiosPromise<ProductVO> => {
+  return request({
+    url: '/game/product/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增产品
+ * @param data
+ */
+export const addProduct = (data: ProductForm) => {
+  return request({
+    url: '/game/product',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改产品
+ * @param data
+ */
+export const updateProduct = (data: ProductForm) => {
+  return request({
+    url: '/game/product',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除产品
+ * @param id
+ */
+export const delProduct = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/game/product/' + id,
+    method: 'delete'
+  });
+};

+ 56 - 0
src/api/game/product/types.ts

@@ -0,0 +1,56 @@
+export interface ProductVO {
+  /**
+   * 产品ID
+   */
+  id: string | number;
+
+  /**
+   * 分类ID
+   */
+  categoryId: string | number;
+
+  /**
+   * 产品名称
+   */
+  name: string;
+
+}
+
+export interface ProductForm extends BaseEntity {
+  /**
+   * 产品ID
+   */
+  id?: string | number;
+
+  /**
+   * 分类ID
+   */
+  categoryId?: string | number;
+
+  /**
+   * 产品名称
+   */
+  name?: string;
+
+}
+
+export interface ProductQuery extends PageQuery {
+
+  /**
+   * 分类ID
+   */
+  categoryId?: string | number;
+
+  /**
+   * 产品名称
+   */
+  name?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 70 - 0
src/api/game/productCategory/index.ts

@@ -0,0 +1,70 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ProductCategoryVO, ProductCategoryForm, ProductCategoryQuery } from '@/api/game/productCategory/types';
+
+/**
+ * 查询产品分类列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listProductCategory = (query?: ProductCategoryQuery): AxiosPromise<ProductCategoryVO[]> => {
+  return request({
+    url: '/game/productCategory/list',
+    method: 'get',
+    params: query
+  });
+};
+
+export const allProductCategory = (query?: ProductCategoryQuery): AxiosPromise<ProductCategoryVO[]> => {
+  return request({
+    url: '/game/productCategory/all',
+    method: 'get'
+  });
+};
+
+/**
+ * 查询产品分类详细
+ * @param id
+ */
+export const getProductCategory = (id: string | number): AxiosPromise<ProductCategoryVO> => {
+  return request({
+    url: '/game/productCategory/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增产品分类
+ * @param data
+ */
+export const addProductCategory = (data: ProductCategoryForm) => {
+  return request({
+    url: '/game/productCategory',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改产品分类
+ * @param data
+ */
+export const updateProductCategory = (data: ProductCategoryForm) => {
+  return request({
+    url: '/game/productCategory',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除产品分类
+ * @param id
+ */
+export const delProductCategory = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/game/productCategory/' + id,
+    method: 'delete'
+  });
+};

+ 41 - 0
src/api/game/productCategory/types.ts

@@ -0,0 +1,41 @@
+export interface ProductCategoryVO {
+  /**
+   * 分类ID
+   */
+  id: string | number;
+
+  /**
+   * 分类名称
+   */
+  name: string;
+
+}
+
+export interface ProductCategoryForm extends BaseEntity {
+  /**
+   * 分类ID
+   */
+  id?: string | number;
+
+  /**
+   * 分类名称
+   */
+  name?: string;
+
+}
+
+export interface ProductCategoryQuery extends PageQuery {
+
+  /**
+   * 分类名称
+   */
+  name?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 70 - 0
src/api/game/productProxy/index.ts

@@ -0,0 +1,70 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ProductProxyVO, ProductProxyForm, ProductProxyQuery } from '@/api/game/productProxy/types';
+
+/**
+ * 查询产品代理商列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listProductProxy = (query?: ProductProxyQuery): AxiosPromise<ProductProxyVO[]> => {
+  return request({
+    url: '/game/productProxy/list',
+    method: 'get',
+    params: query
+  });
+};
+
+export const allProductProxy = (query?: ProductProxyQuery): AxiosPromise<ProductProxyVO[]> => {
+  return request({
+    url: '/game/productProxy/all',
+    method: 'get'
+  });
+};
+
+/**
+ * 查询产品代理商详细
+ * @param id
+ */
+export const getProductProxy = (id: string | number): AxiosPromise<ProductProxyVO> => {
+  return request({
+    url: '/game/productProxy/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增产品代理商
+ * @param data
+ */
+export const addProductProxy = (data: ProductProxyForm) => {
+  return request({
+    url: '/game/productProxy',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改产品代理商
+ * @param data
+ */
+export const updateProductProxy = (data: ProductProxyForm) => {
+  return request({
+    url: '/game/productProxy',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除产品代理商
+ * @param id
+ */
+export const delProductProxy = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/game/productProxy/' + id,
+    method: 'delete'
+  });
+};

+ 41 - 0
src/api/game/productProxy/types.ts

@@ -0,0 +1,41 @@
+export interface ProductProxyVO {
+  /**
+   * 代理商ID
+   */
+  id: string | number;
+
+  /**
+   * 代理商名称
+   */
+  name: string;
+
+}
+
+export interface ProductProxyForm extends BaseEntity {
+  /**
+   * 代理商ID
+   */
+  id?: string | number;
+
+  /**
+   * 代理商名称
+   */
+  name?: string;
+
+}
+
+export interface ProductProxyQuery extends PageQuery {
+
+  /**
+   * 代理商名称
+   */
+  name?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 0 - 7
src/layout/components/Navbar.vue

@@ -43,13 +43,6 @@
             </el-popover>
           </div>
         </el-tooltip>
-        <el-tooltip content="Github" effect="dark" placement="bottom">
-          <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
-        </el-tooltip>
-
-        <el-tooltip :content="$t('navbar.document')" effect="dark" placement="bottom">
-          <ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
-        </el-tooltip>
 
         <el-tooltip :content="$t('navbar.full')" effect="dark" placement="bottom">
           <screenfull id="screenfull" class="right-menu-item hover-effect" />

+ 390 - 0
src/views/game/channel/index.vue

@@ -0,0 +1,390 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+            <el-form-item prop="id">
+              <el-input v-model="queryParams.id" placeholder="渠道" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item prop="productId">
+              <el-select v-model="queryParams.productId" placeholder="请选择产品ID" clearable>
+                <el-option v-for="item in productList" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+            <el-form-item prop="proxyId">
+              <el-select v-model="queryParams.proxyId" placeholder="请选择代理商" clearable>
+                <el-option v-for="item in categoryList" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+            <el-form-item prop="accountId">
+              <el-input v-model="queryParams.accountId" placeholder="请输入媒体账号ID" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item prop="productName">
+              <el-input v-model="queryParams.productName" placeholder="请输入产品名" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item prop="userName">
+              <el-input v-model="queryParams.userName" placeholder="请输入投放人员名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item prop="name">
+              <el-input v-model="queryParams.name" placeholder="请输入渠道名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item prop="company">
+              <el-input v-model="queryParams.company" placeholder="请输入公司名" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item prop="operatingMode">
+              <el-select v-model="queryParams.operatingMode" placeholder="请选择运营方式" clearable>
+<!--                <el-option v-for="dict in dict.type.operating_mode" :key="dict.value" :label="dict.label" :value="dict.value" />-->
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" size="small" @click="handleQuery">查询</el-button>
+              <el-button type="primary" plain size="small" @click="handleAdd" v-hasPermi="['game:channel:add']">添加渠道</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="warning" size="small" @click="handleUpdateStatus(1)">批量暂停</el-button>
+            <el-button size="small" @click="handleUpdateStatus(0)">批量开启</el-button>
+            <el-button size="small" @click="handleActionUpdate">批量修改关键行为</el-button>
+            <el-button size="small" @click="handleActiveUpdate">批量修改激活行为</el-button>
+            <el-button size="small" @click="handleDeliverUpdate">批量修改投放人员</el-button>
+            <el-button size="small" @click="handleProxyUpdate">批量修改代理</el-button>
+            <el-button size="small" @click="handleNameUpdate">批量修改渠道名称规则</el-button>
+          </el-col>
+<!--          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>-->
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="channelList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="50" align="center" />
+        <el-table-column label="渠道号" width="80" align="center" prop="id" />
+        <el-table-column label="产品ID" width="80" align="center" prop="productId" />
+        <el-table-column label="产品名" align="center" prop="productName">
+          <template #default="scope">
+          <span v-if="productList.find(cat => cat.id === scope.row.productId)">
+              {{ productList.find(cat => cat.id === scope.row.productId).name }}
+          </span>
+            <span v-else>scope.row.productId</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="投放人员" align="center" prop="userName" />
+        <el-table-column label="媒体" align="center" prop="platType" >
+          <template #default="scope">
+<!--            <dict-tag :options="dict.type.plat_type" :value="scope.row.platType"/>-->
+          </template>
+        </el-table-column>
+        <el-table-column label="渠道名称" align="center" prop="name" />
+        <el-table-column label="账号ID" align="center" prop="accountId" />
+        <el-table-column label="代理" align="center" prop="proxyId">
+          <template #default="scope">
+            <span v-if="proxyList.find(cat => cat.id === scope.row.proxyId)">
+                {{ proxyList.find(cat => cat.id === scope.row.proxyId).name }}
+            </span>
+            <span v-else>scope.row.proxyId</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="公司名" align="center" prop="company" />
+        <el-table-column label="投放方式" align="center" prop="deliveryMode">
+          <template #default="scope">
+<!--            <dict-tag :options="dict.type.delivery_mode" :value="scope.row.deliveryMode"/>-->
+          </template>
+        </el-table-column>
+        <el-table-column label="运营方式" align="center" prop="operatingMode">
+          <template #default="scope">
+<!--            <dict-tag :options="dict.type.operating_mode" :value="scope.row.operatingMode"/>-->
+          </template>
+        </el-table-column>
+        <el-table-column label="状态" align="center" prop="status">
+          <template #default="scope">
+<!--            <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status"/>-->
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" fixed="right" width="350">
+          <template #default="scope">
+            <el-button
+              size="small"
+              type="info"
+              @click="copyToClipboard(scope.row.name)"
+            >复制渠道</el-button>
+            <el-button
+              size="small"
+              type="primary"
+              @click="handleUpdate(scope.row)"
+              v-hasPermi="['miniGame:channel:edit']"
+            >修改</el-button>
+            <!--          <el-button
+                        size="mini"
+                        type="text"
+                        icon="el-icon-delete"
+                        @click="handleDelete(scope.row)"
+                        v-hasPermi="['miniGame:channel:remove']"
+                      >删除</el-button>-->
+            <el-button
+              size="small"
+              type="warning"
+              @click="copyToClipboard(scope.row.url)"
+            >获取监测地址</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
+    </el-card>
+    <!-- 添加或修改渠道对话框 -->
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
+      <el-form ref="channelFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="产品ID" prop="productId">
+          <el-select v-model="form.productId" placeholder="请选择产品ID">
+            <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="parseInt(dict.value)"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="代理商ID" prop="proxyId">
+          <el-select v-model="form.proxyId" placeholder="请选择代理商ID">
+            <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="parseInt(dict.value)"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="媒体账号ID" prop="accountId">
+          <el-input v-model="form.accountId" placeholder="请输入媒体账号ID" />
+        </el-form-item>
+        <el-form-item label="产品名" prop="productName">
+          <el-input v-model="form.productName" placeholder="请输入产品名" />
+        </el-form-item>
+        <el-form-item label="投放人员名称" prop="userName">
+          <el-input v-model="form.userName" placeholder="请输入投放人员名称" />
+        </el-form-item>
+        <el-form-item label="渠道名称" prop="name">
+          <el-input v-model="form.name" placeholder="请输入渠道名称" />
+        </el-form-item>
+        <el-form-item label="公司名" prop="company">
+          <el-input v-model="form.company" placeholder="请输入公司名" />
+        </el-form-item>
+        <el-form-item label="运营方式" prop="operatingMode">
+          <el-input v-model="form.operatingMode" placeholder="请输入运营方式" />
+        </el-form-item>
+        <el-form-item label="投放方式" prop="deliveryMode">
+          <el-input v-model="form.deliveryMode" placeholder="请输入投放方式" />
+        </el-form-item>
+        <el-form-item label="激活上报规则" prop="activeRules">
+          <el-input v-model="form.activeRules" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+        <el-form-item label="关键行为上报规则" prop="actionRules">
+          <el-input v-model="form.actionRules" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+        <el-form-item label="是否上报" prop="isReport">
+          <el-input v-model="form.isReport" placeholder="请输入是否上报" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Channel" lang="ts">
+import { listChannel, getChannel, delChannel, addChannel, updateChannel } from '@/api/game/channel';
+import { ChannelVO, ChannelQuery, ChannelForm } from '@/api/game/channel/types';
+import { ProductVO } from '@/api/game/product/types';
+import { ProductCategoryVO } from '@/api/game/productCategory/types';
+import { ProductProxyVO } from '@/api/game/productProxy/types';
+import {allProduct} from "@/api/game/product";
+import {allProductProxy} from "@/api/game/productProxy";
+import {allProductCategory} from "@/api/game/productCategory";
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
+
+const channelList = ref<ChannelVO[]>([]);
+const productList = ref<ProductVO[]>([]);
+const categoryList = ref<ProductCategoryVO[]>([]);
+const proxyList = ref<ProductProxyVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const channelFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: ChannelForm = {
+  id: undefined,
+  productId: undefined,
+  proxyId: undefined,
+  accountId: undefined,
+  productName: undefined,
+  platType: undefined,
+  userName: undefined,
+  name: undefined,
+  company: undefined,
+  operatingMode: undefined,
+  deliveryMode: undefined,
+  activeRules: undefined,
+  actionRules: undefined,
+  isReport: undefined,
+  status: undefined
+};
+const data = reactive<PageData<ChannelForm, ChannelQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    id: undefined,
+    productId: undefined,
+    proxyId: undefined,
+    accountId: undefined,
+    productName: undefined,
+    platType: undefined,
+    userName: undefined,
+    name: undefined,
+    company: undefined,
+    operatingMode: undefined,
+    deliveryMode: undefined,
+    activeRules: undefined,
+    actionRules: undefined,
+    isReport: undefined,
+    status: undefined,
+    params: {}
+  },
+  rules: {
+    id: [{ required: true, message: '渠道ID不能为空', trigger: 'blur' }],
+    productId: [{ required: true, message: '产品ID不能为空', trigger: 'change' }],
+    proxyId: [{ required: true, message: '代理商ID不能为空', trigger: 'change' }],
+    accountId: [{ required: true, message: '媒体账号ID不能为空', trigger: 'blur' }],
+    productName: [{ required: true, message: '产品名不能为空', trigger: 'blur' }],
+    platType: [{ required: true, message: '媒体类型不能为空', trigger: 'change' }],
+    userName: [{ required: true, message: '投放人员名称不能为空', trigger: 'blur' }],
+    name: [{ required: true, message: '渠道名称不能为空', trigger: 'blur' }],
+    company: [{ required: true, message: '公司名不能为空', trigger: 'blur' }],
+    operatingMode: [{ required: true, message: '运营方式不能为空', trigger: 'blur' }],
+    deliveryMode: [{ required: true, message: '投放方式不能为空', trigger: 'blur' }],
+    activeRules: [{ required: true, message: '激活上报规则不能为空', trigger: 'blur' }],
+    actionRules: [{ required: true, message: '关键行为上报规则不能为空', trigger: 'blur' }],
+    isReport: [{ required: true, message: '是否上报不能为空', trigger: 'blur' }],
+    status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询渠道列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listChannel(queryParams.value);
+  channelList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+  productList.value = await allProduct();
+  proxyList.value = await allProductProxy();
+  categoryList.value = await allProductCategory();
+};
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  channelFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: ChannelVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = '添加渠道';
+};
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: ChannelVO) => {
+  reset();
+  const _id = row?.id || ids.value[0];
+  const res = await getChannel(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = '修改渠道';
+};
+
+/** 提交按钮 */
+const submitForm = () => {
+  channelFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateChannel(form.value).finally(() => (buttonLoading.value = false));
+      } else {
+        await addChannel(form.value).finally(() => (buttonLoading.value = false));
+      }
+      proxy?.$modal.msgSuccess('操作成功');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+};
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: ChannelVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除渠道编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await delChannel(_ids);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download(
+    'game/channel/export',
+    {
+      ...queryParams.value
+    },
+    `channel_${new Date().getTime()}.xlsx`
+  );
+};
+
+
+
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 240 - 0
src/views/game/product/index.vue

@@ -0,0 +1,240 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+            <el-form-item label="分类ID" prop="categoryId">
+              <el-select v-model="queryParams.categoryId" placeholder="请选择分类ID" clearable>
+                <el-option v-for="dict in categoryList" :key="dict.id" :label="dict.name" :value="dict.id" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="产品名称" prop="name">
+              <el-input v-model="queryParams.name" placeholder="请输入产品名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+              <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['game:product:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['game:product:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['game:product:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"
+              >删除</el-button
+            >
+          </el-col>
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['game:product:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="productList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column v-if="true" label="产品ID" align="center" prop="id" />
+        <el-table-column label="分类ID" align="center" prop="categoryId">
+          <template #default="scope">
+            <span v-if="categoryList.find((cat) => cat.id === scope.row.categoryId)">
+              {{ categoryList.find((cat) => cat.id === scope.row.categoryId).name }}
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column label="产品名称" align="center" prop="name" />
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="修改" placement="top">
+              <el-button v-hasPermi="['game:product:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
+            </el-tooltip>
+            <el-tooltip content="删除" placement="top">
+              <el-button v-hasPermi="['game:product:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
+    </el-card>
+    <!-- 添加或修改产品对话框 -->
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
+      <el-form ref="productFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="分类ID" prop="categoryId">
+          <el-select v-model="form.categoryId" placeholder="请选择分类ID">
+            <el-option v-for="dict in categoryList" :key="dict.id" :label="dict.name" :value="dict.id"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="产品名称" prop="name">
+          <el-input v-model="form.name" placeholder="请输入产品名称" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Product" lang="ts">
+import { addProduct, delProduct, getProduct, listProduct, updateProduct } from '@/api/game/product';
+import { ProductForm, ProductQuery, ProductVO } from '@/api/game/product/types';
+import { ProductCategoryVO } from '@/api/game/productCategory/types';
+import { allProductCategory } from '@/api/game/productCategory';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
+
+const productList = ref<ProductVO[]>([]);
+const categoryList = ref<ProductCategoryVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const productFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: ProductForm = {
+  id: undefined,
+  categoryId: undefined,
+  name: undefined
+};
+const data = reactive<PageData<ProductForm, ProductQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    categoryId: undefined,
+    name: undefined,
+    params: {}
+  },
+  rules: {
+    id: [{ required: true, message: '产品ID不能为空', trigger: 'blur' }],
+    categoryId: [{ required: true, message: '分类ID不能为空', trigger: 'change' }],
+    name: [{ required: true, message: '产品名称不能为空', trigger: 'blur' }]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询产品列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listProduct(queryParams.value);
+  categoryList.value = await allProductCategory();
+  productList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+};
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  productFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: ProductVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = '添加产品';
+};
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: ProductVO) => {
+  reset();
+  const _id = row?.id || ids.value[0];
+  const res = await getProduct(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = '修改产品';
+};
+
+/** 提交按钮 */
+const submitForm = () => {
+  productFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateProduct(form.value).finally(() => (buttonLoading.value = false));
+      } else {
+        await addProduct(form.value).finally(() => (buttonLoading.value = false));
+      }
+      proxy?.$modal.msgSuccess('操作成功');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+};
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: ProductVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除产品编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await delProduct(_ids);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download(
+    'game/product/export',
+    {
+      ...queryParams.value
+    },
+    `product_${new Date().getTime()}.xlsx`
+  );
+};
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 214 - 0
src/views/game/productCategory/index.vue

@@ -0,0 +1,214 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+            <el-form-item label="分类名称" prop="name">
+              <el-input v-model="queryParams.name" placeholder="请输入分类名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+              <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['game:productCategory:add']">新增</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['game:productCategory:edit']">修改</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['game:productCategory:remove']">删除</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['game:productCategory:export']">导出</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="productCategoryList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="分类ID" align="center" prop="id" v-if="true" />
+        <el-table-column label="分类名称" align="center" prop="name" />
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="修改" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['game:productCategory:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="删除" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['game:productCategory:remove']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+    <!-- 添加或修改产品分类对话框 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="productCategoryFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="分类名称" prop="name">
+          <el-input v-model="form.name" placeholder="请输入分类名称" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="ProductCategory" lang="ts">
+import { listProductCategory, getProductCategory, delProductCategory, addProductCategory, updateProductCategory } from '@/api/game/productCategory';
+import { ProductCategoryVO, ProductCategoryQuery, ProductCategoryForm } from '@/api/game/productCategory/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const productCategoryList = ref<ProductCategoryVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const productCategoryFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: ProductCategoryForm = {
+  id: undefined,
+  name: undefined,
+}
+const data = reactive<PageData<ProductCategoryForm, ProductCategoryQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    name: undefined,
+    params: {
+    }
+  },
+  rules: {
+    id: [
+      { required: true, message: "分类ID不能为空", trigger: "blur" }
+    ],
+    name: [
+      { required: true, message: "分类名称不能为空", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询产品分类列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listProductCategory(queryParams.value);
+  productCategoryList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = {...initFormData};
+  productCategoryFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: ProductCategoryVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "添加产品分类";
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: ProductCategoryVO) => {
+  reset();
+  const _id = row?.id || ids.value[0]
+  const res = await getProductCategory(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "修改产品分类";
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  productCategoryFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateProductCategory(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addProductCategory(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("操作成功");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: ProductCategoryVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除产品分类编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
+  await delProductCategory(_ids);
+  proxy?.$modal.msgSuccess("删除成功");
+  await getList();
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('game/productCategory/export', {
+    ...queryParams.value
+  }, `productCategory_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 217 - 0
src/views/game/productProxy/index.vue

@@ -0,0 +1,217 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+            <el-form-item label="代理商名称" prop="name" label-width="90px">
+              <el-input v-model="queryParams.name" placeholder="请输入代理商名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+              <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['game:productProxy:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['game:productProxy:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"
+              >修改</el-button
+            >
+          </el-col>
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['game:productProxy:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"
+              >删除</el-button
+            >
+          </el-col>
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['game:productProxy:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="productProxyList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column v-if="true" label="代理商ID" align="center" prop="id" />
+        <el-table-column label="代理商名称" align="center" prop="name" />
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="修改" placement="top">
+              <el-button v-hasPermi="['game:productProxy:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
+            </el-tooltip>
+            <el-tooltip content="删除" placement="top">
+              <el-button v-hasPermi="['game:productProxy:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
+    </el-card>
+    <!-- 添加或修改产品代理商对话框 -->
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
+      <el-form ref="productProxyFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="代理商名称" prop="name">
+          <el-input v-model="form.name" placeholder="请输入代理商名称" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="ProductProxy" lang="ts">
+import { listProductProxy, getProductProxy, delProductProxy, addProductProxy, updateProductProxy } from '@/api/game/productProxy';
+import { ProductProxyVO, ProductProxyQuery, ProductProxyForm } from '@/api/game/productProxy/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const productProxyList = ref<ProductProxyVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const productProxyFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: ProductProxyForm = {
+  id: undefined,
+  name: undefined
+};
+const data = reactive<PageData<ProductProxyForm, ProductProxyQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    name: undefined,
+    params: {}
+  },
+  rules: {
+    id: [{ required: true, message: '代理商ID不能为空', trigger: 'blur' }],
+    name: [{ required: true, message: '代理商名称不能为空', trigger: 'blur' }]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询产品代理商列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listProductProxy(queryParams.value);
+  productProxyList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+};
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  productProxyFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: ProductProxyVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = '添加产品代理商';
+};
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: ProductProxyVO) => {
+  reset();
+  const _id = row?.id || ids.value[0];
+  const res = await getProductProxy(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = '修改产品代理商';
+};
+
+/** 提交按钮 */
+const submitForm = () => {
+  productProxyFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateProductProxy(form.value).finally(() => (buttonLoading.value = false));
+      } else {
+        await addProductProxy(form.value).finally(() => (buttonLoading.value = false));
+      }
+      proxy?.$modal.msgSuccess('操作成功');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+};
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: ProductProxyVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除产品代理商编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await delProductProxy(_ids);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download(
+    'game/productProxy/export',
+    {
+      ...queryParams.value
+    },
+    `productProxy_${new Date().getTime()}.xlsx`
+  );
+};
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 1 - 93
src/views/index.vue

@@ -1,97 +1,5 @@
 <template>
-  <div class="app-container home">
-    <el-row :gutter="20">
-      <el-col :sm="24" :lg="12" style="padding-left: 20px">
-        <h2>RuoYi-Vue-Plus多租户管理系统</h2>
-        <p>
-          RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 分布式集群 场景升级(不兼容原框架)
-          <br />
-          * 前端开发框架 Vue3、TS、Element Plus<br />
-          * 后端开发框架 Spring Boot<br />
-          * 容器框架 Undertow 基于 Netty 的高性能容器<br />
-          * 权限认证框架 Sa-Token 支持多终端认证系统<br />
-          * 关系数据库 MySQL 适配 8.X 最低 5.7<br />
-          * 缓存数据库 Redis 适配 6.X 最低 4.X<br />
-          * 数据库框架 Mybatis-Plus 快速 CRUD 增加开发效率<br />
-          * 数据库框架 p6spy 更强劲的 SQL 分析<br />
-          * 多数据源框架 dynamic-datasource 支持主从与多种类数据库异构<br />
-          * 序列化框架 Jackson 统一使用 jackson 高效可靠<br />
-          * Redis客户端 Redisson 性能强劲、API丰富<br />
-          * 分布式限流 Redisson 全局、请求IP、集群ID 多种限流<br />
-          * 分布式锁 Lock4j 注解锁、工具锁 多种多样<br />
-          * 分布式幂等 Lock4j 基于分布式锁实现<br />
-          * 分布式链路追踪 SkyWalking 支持链路追踪、网格分析、度量聚合、可视化<br />
-          * 分布式任务调度 SnailJob 高性能 高可靠 易扩展<br />
-          * 文件存储 Minio 本地存储<br />
-          * 文件存储 七牛、阿里、腾讯 云存储<br />
-          * 监控框架 SpringBoot-Admin 全方位服务监控<br />
-          * 校验框架 Validation 增强接口安全性 严谨性<br />
-          * Excel框架 Alibaba EasyExcel 性能优异 扩展性强<br />
-          * 文档框架 SpringDoc、javadoc 无注解零入侵基于java注释<br />
-          * 工具类框架 Hutool、Lombok 减少代码冗余 增加安全性<br />
-          * 代码生成器 适配MP、SpringDoc规范化代码 一键生成前后端代码<br />
-          * 部署方式 Docker 容器编排 一键部署业务集群<br />
-          * 国际化 SpringMessage Spring标准国际化方案<br />
-        </p>
-        <p><b>当前版本:</b> <span>v5.2.1</span></p>
-        <p>
-          <el-tag type="danger">&yen;免费开源</el-tag>
-        </p>
-        <p>
-          <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Vue-Plus')">访问码云</el-button>
-          <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Vue-Plus')">访问GitHub</el-button>
-          <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-vue-plus/changlog')"
-            >更新日志</el-button
-          >
-        </p>
-      </el-col>
-
-      <el-col :sm="24" :lg="12" style="padding-left: 20px">
-        <h2>RuoYi-Cloud-Plus多租户微服务管理系统</h2>
-        <p>
-          RuoYi-Cloud-Plus 微服务通用权限管理系统 重写 RuoYi-Cloud 全方位升级(不兼容原框架)
-          <br />
-          * 前端开发框架 Vue3、TS、Element UI<br />
-          * 后端开发框架 Spring Boot<br />
-          * 微服务开发框架 Spring Cloud、Spring Cloud Alibaba<br />
-          * 容器框架 Undertow 基于 XNIO 的高性能容器<br />
-          * 权限认证框架 Sa-Token、Jwt 支持多终端认证系统<br />
-          * 关系数据库 MySQL 适配 8.X 最低 5.7<br />
-          * 关系数据库 Oracle 适配 11g 12c<br />
-          * 关系数据库 PostgreSQL 适配 13 14<br />
-          * 关系数据库 SQLServer 适配 2017 2019<br />
-          * 缓存数据库 Redis 适配 6.X 最低 5.X<br />
-          * 分布式注册中心 Alibaba Nacos 采用2.X 基于GRPC通信高性能<br />
-          * 分布式配置中心 Alibaba Nacos 采用2.X 基于GRPC通信高性能<br />
-          * 服务网关 Spring Cloud Gateway 响应式高性能网关<br />
-          * 负载均衡 Spring Cloud Loadbalancer 负载均衡处理<br />
-          * RPC远程调用 Apache Dubbo 原生态使用体验、高性能<br />
-          * 分布式限流熔断 Alibaba Sentinel 无侵入、高扩展<br />
-          * 分布式事务 Alibaba Seata 无侵入、高扩展 支持 四种模式<br />
-          * 分布式消息队列 Apache Kafka 高性能高速度<br />
-          * 分布式消息队列 Apache RocketMQ 高可用功能多样<br />
-          * 分布式消息队列 RabbitMQ 支持各种扩展插件功能多样性<br />
-          * 分布式搜索引擎 ElasticSearch 业界知名<br />
-          * 分布式链路追踪 Apache SkyWalking 链路追踪、网格分析、度量聚合、可视化<br />
-          * 分布式日志中心 ELK 业界成熟解决方案<br />
-          * 分布式监控 Prometheus、Grafana 全方位性能监控<br />
-          * 其余与 Vue 版本一致<br />
-        </p>
-        <p><b>当前版本:</b> <span>v2.2.0</span></p>
-        <p>
-          <el-tag type="danger">&yen;免费开源</el-tag>
-        </p>
-        <p>
-          <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Cloud-Plus')">访问码云</el-button>
-          <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Cloud-Plus')">访问GitHub</el-button>
-          <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-cloud-plus/changlog')"
-            >更新日志</el-button
-          >
-        </p>
-      </el-col>
-    </el-row>
-    <el-divider />
-  </div>
+  <div class="app-container home"></div>
 </template>
 
 <script setup name="Index" lang="ts">

+ 1 - 1
src/views/login.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="login">
     <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
-      <h3 class="title">RuoYi-Vue-Plus多租户管理系统</h3>
+      <h3 class="title">小游戏管理平台</h3>
       <el-form-item v-if="tenantEnabled" prop="tenantId">
         <el-select v-model="loginForm.tenantId" filterable placeholder="请选择/输入公司名称" style="width: 100%">
           <el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"></el-option>