Przeglądaj źródła

广告管理、打包管理

ith5 6 miesięcy temu
rodzic
commit
4fa6e60a38

+ 1 - 1
src/components/sa-table/index.vue

@@ -229,7 +229,7 @@
               :pagination="false"
               :columns="columns"
               :loading="loading"
-              :size="options.size"
+              :size="options.size || 'mini'"
               :stripe="options.stripe"
               :data="tableData.data"
               :scroll="{ x: '100%', y: '100%' }"

+ 18 - 4
src/views/v1/advert/agenList/addSite.vue

@@ -19,18 +19,30 @@
       <a-form-item label="媒体ID" field="media_id">
         <a-select
           v-model="formData.media_id"
-          :options="mediaOptions"
           placeholder="请选择媒体ID"
           allow-clear
-        />
+        >
+          <a-option
+            v-for="item in mediaOptions"
+            :key="item.id"
+            :value="item.id"
+            :label="`${item.id}:${item.name}`"
+          />
+        </a-select>
       </a-form-item>
       <a-form-item label="渠道ID" field="agent_id">
         <a-select
           v-model="formData.agent_id"
-          :options="agentOptions"
           placeholder="请选择渠道ID"
           allow-clear
-        />
+        >
+          <a-option
+            v-for="item in agentOptions"
+            :key="item.id"
+            :value="item.id"
+            :label="`${item.id}:${item.name}`"
+          />
+        </a-select>
       </a-form-item>
       <a-form-item label="负责人ID" field="auth_id">
         <a-select
@@ -142,6 +154,8 @@ const setFormData = async (data) => {
       formData[key] = data[key];
     }
   }
+  formData["name"] = "";
+  formData["agent_id"] = data.id;
 };
 
 // 数据保存

+ 8 - 2
src/views/v1/advert/agenList/edit.vue

@@ -19,10 +19,16 @@
       <a-form-item label="媒体ID" field="media_id">
         <a-select
           v-model="formData.media_id"
-          :options="mediaOptions"
           placeholder="请选择媒体ID"
           allow-clear
-        />
+        >
+          <a-option
+            v-for="item in mediaOptions"
+            :key="item.id"
+            :value="item.id"
+            :label="`${item.id}:${item.name}`"
+          />
+        </a-select>
       </a-form-item>
       <a-form-item label="渠道名称" field="name">
         <a-input v-model="formData.name" placeholder="请输入渠道名称" />

+ 16 - 3
src/views/v1/advert/agenList/index.vue

@@ -12,7 +12,7 @@
           <a-form-item label="后台归属人" field="auth_id">
             <a-select
               v-model="searchForm.auth_id"
-              :options="[]"
+              :options="authOptions"
               placeholder="请选择后台归属人"
               allow-clear
             />
@@ -51,12 +51,14 @@ import { Message } from "@arco-design/web-vue";
 import EditForm from "./edit.vue";
 import addSiteForm from "./addSite.vue";
 import api from "../../api/advert/agenList";
+import advertCommonApi from "../../api/advert/common";
 
 // 引用定义
 const crudRef = ref();
 const editRef = ref();
 const viewRef = ref();
 const addSiteRef = ref();
+const authOptions = ref([]);
 
 // 搜索表单
 const searchForm = ref({
@@ -67,6 +69,7 @@ const searchForm = ref({
 // 添加广告位
 const addAdvert = (record) => {
   addSiteRef.value?.open();
+  addSiteRef.value?.setFormData(record);
 };
 
 // SaTable 基础配置
@@ -103,13 +106,23 @@ const options = reactive({
 
 // SaTable 列配置
 const columns = reactive([
-  { title: "媒体ID", dataIndex: "media_id", width: 180 },
+  { title: "媒体ID", dataIndex: "media_id", width: 120 },
+  { title: "媒体", dataIndex: "media_name", width: 180 },
   { title: "渠道名称", dataIndex: "name", width: 180 },
   { title: "后台归属人", dataIndex: "auth_name", width: 180 },
 ]);
 
 // 页面数据初始化
-const initPage = async () => {};
+const initPage = async () => {
+  await getAuthOptions();
+};
+
+const getAuthOptions = async () => {
+  const resp = await advertCommonApi.getAuthOptionsApi();
+  if (resp.code === 200) {
+    authOptions.value = resp.data;
+  }
+};
 
 // SaTable 数据请求
 const refresh = async () => {

+ 60 - 6
src/views/v1/advert/agentSite/index.vue

@@ -5,6 +5,7 @@
       :options="options"
       :columns="columns"
       :searchForm="searchForm"
+      @selectionChange="selectChange"
     >
       <!-- 搜索区 tableSearch -->
       <template #tableSearch>
@@ -12,22 +13,34 @@
           <a-form-item label="媒体ID" field="media_id">
             <a-select
               v-model="searchForm.media_id"
-              :options="mediaOptions"
               placeholder="请选择媒体ID"
               allow-search
               allow-clear
-            />
+            >
+              <a-option
+                v-for="item in mediaOptions"
+                :key="item.id"
+                :value="item.id"
+                :label="`${item.id}:${item.name}`"
+              />
+            </a-select>
           </a-form-item>
         </a-col>
         <a-col :sm="8" :xs="24">
           <a-form-item label="渠道ID" field="agent_id">
             <a-select
               v-model="searchForm.agent_id"
-              :options="agentOptions"
               placeholder="请选择渠道ID"
               allow-search
               allow-clear
-            />
+            >
+              <a-option
+                v-for="item in agentOptions"
+                :key="item.id"
+                :value="item.id"
+                :label="`${item.id}:${item.name}`"
+              />
+            </a-select>
           </a-form-item>
         </a-col>
         <a-col :sm="8" :xs="24">
@@ -52,10 +65,24 @@
         </a-col>
       </template>
 
+      <template #tableBeforeButtons>
+        <a-button
+          size="mini"
+          type="primary"
+          @click="addApk"
+          :disabled="selecteds.length != 1"
+        >
+          APK打包
+        </a-button>
+      </template>
+
       <!-- Table 自定义渲染 -->
     </sa-table>
     <!-- 编辑弹框 -->
     <edit-form ref="editRef" @success="refresh" />
+
+    <!-- 打包新增 -->
+    <add-apk-form ref="addApkRef" @success="refresh" />
   </div>
 </template>
 
@@ -64,16 +91,18 @@ import { onMounted, ref, reactive } from "vue";
 import { Message } from "@arco-design/web-vue";
 import advertCommonApi from "../../api/advert/common";
 import editForm from "./edit.vue";
-
+import addApkForm from "../../gameLog/gamePackageLog/edit.vue";
 import api from "../../api/advert/agentSite";
 
 // 引用定义
 const crudRef = ref();
 const viewRef = ref();
+const addApkRef = ref();
 const mediaOptions = ref([]);
 const agentOptions = ref([]);
 const authOptions = ref([]);
 const editRef = ref();
+const selecteds = ref([]);
 
 // 搜索表单
 const searchForm = ref({
@@ -103,7 +132,7 @@ const options = reactive({
     },
   },
   delete: {
-    show: true,
+    show: false,
     auth: ["/v1/advert/AgentSite/destroy"],
     func: async (params) => {
       const resp = await api.destroy(params);
@@ -149,6 +178,31 @@ const getAuthList = async () => {
   }
 };
 
+// 选中数据
+const selectChange = (val) => (selecteds.value = val);
+
+// 获取广告位详情
+const getAdvertSiteDetail = async () => {
+  let id = selecteds.value[0];
+  const res = await api.read(id);
+  if (res.code == 200) {
+    return res.data;
+  }
+  return null;
+};
+
+// 打包新增
+const addApk = async () => {
+  let id = selecteds.value[0];
+  const siteDetail = await getAdvertSiteDetail(id);
+  let data = {
+    site_id: siteDetail.id, // 广告位
+    agent_id: siteDetail.agent_id, // 渠道
+  };
+  addApkRef.value?.open();
+  addApkRef.value?.setFormData(data);
+};
+
 // 页面数据初始化
 const initPage = async () => {
   await getMediaOptions();

+ 17 - 5
src/views/v1/advert/mediaCost/index.vue

@@ -22,10 +22,16 @@
             <a-select
               v-model="searchForm.game_id"
               placeholder="请选择投放游戏"
-              :options="gameOptions"
               allow-clear
               allow-search
-            />
+            >
+              <a-option
+                v-for="item in gameOptions"
+                :key="item.id"
+                :value="item.id"
+                :label="`${item.id}:${item.name}`"
+              />
+            </a-select>
           </a-form-item>
         </a-col>
         <a-col :sm="8" :xs="24">
@@ -33,10 +39,16 @@
             <a-select
               v-model="searchForm.media_id"
               placeholder="请选择媒体类型"
-              :options="mediaOptions"
               allow-clear
               allow-search
-            />
+            >
+              <a-option
+                v-for="item in mediaOptions"
+                :key="item.id"
+                :value="item.id"
+                :label="`${item.id}:${item.name}`"
+              />
+            </a-select>
           </a-form-item>
         </a-col>
         <a-col :sm="8" :xs="24">
@@ -71,7 +83,7 @@
           <a-form-item label="录入方式" field="add_type">
             <sa-select
               v-model="searchForm.add_type"
-              dict=""
+              dict="add_type"
               placeholder="请选择录入方式"
               allow-clear
             />

+ 31 - 0
src/views/v1/api/common.js

@@ -0,0 +1,31 @@
+import { request } from "@/utils/request.js";
+
+/**
+ * 公共 API接口
+ */
+export default {
+  /**
+   * 获取母包列表Options
+   * @returns
+   */
+  getPackageOptionsApi(params = {}) {
+    return request({
+      url: "/v1/common/getPackageOptions",
+      method: "get",
+      params,
+    });
+  },
+
+  /**
+   * 获取子游戏options
+   * @param {Object} params
+   * @returns
+   */
+  getGameOptionsApi(params = {}) {
+    return request({
+      url: "/v1/common/getGameOptions",
+      method: "get",
+      params,
+    });
+  },
+};

+ 67 - 0
src/views/v1/api/gameLog/gamePackage.js

@@ -0,0 +1,67 @@
+import { request } from '@/utils/request.js'
+
+/**
+ * 母包管理 API接口
+ */
+export default {
+
+  /**
+   * 数据列表
+   * @returns
+   */
+  getPageList(params = {}) {
+    return request({
+      url: '/v1/gameLog/GamePackage/index',
+      method: 'get',
+      params
+    })
+  },
+
+  /**
+   * 添加数据
+   * @returns
+   */
+  save(params = {}) {
+    return request({
+      url: '/v1/gameLog/GamePackage/save',
+      method: 'post',
+      data: params
+    })
+  },
+
+  /**
+   * 更新数据
+   * @returns
+   */
+  update(id, data = {}) {
+    return request({
+      url: '/v1/gameLog/GamePackage/update?id=' + id,
+      method: 'put',
+      data
+    })
+  },
+
+  /**
+   * 读取数据
+   * @returns
+   */
+  read(id) {
+    return request({
+      url: '/v1/gameLog/GamePackage/read?id=' + id,
+      method: 'get'
+    })
+  },
+
+  /**
+   * 删除数据
+   * @returns
+   */
+  destroy(data) {
+    return request({
+      url: '/v1/gameLog/GamePackage/destroy',
+      method: 'delete',
+      data
+    })
+  },
+
+}

+ 67 - 0
src/views/v1/api/gameLog/gamePackageLog.js

@@ -0,0 +1,67 @@
+import { request } from '@/utils/request.js'
+
+/**
+ * 打包记录 API接口
+ */
+export default {
+
+  /**
+   * 数据列表
+   * @returns
+   */
+  getPageList(params = {}) {
+    return request({
+      url: '/v1/gameLog/GamePackLog/index',
+      method: 'get',
+      params
+    })
+  },
+
+  /**
+   * 添加数据
+   * @returns
+   */
+  save(params = {}) {
+    return request({
+      url: '/v1/gameLog/GamePackLog/save',
+      method: 'post',
+      data: params
+    })
+  },
+
+  /**
+   * 更新数据
+   * @returns
+   */
+  update(id, data = {}) {
+    return request({
+      url: '/v1/gameLog/GamePackLog/update?id=' + id,
+      method: 'put',
+      data
+    })
+  },
+
+  /**
+   * 读取数据
+   * @returns
+   */
+  read(id) {
+    return request({
+      url: '/v1/gameLog/GamePackLog/read?id=' + id,
+      method: 'get'
+    })
+  },
+
+  /**
+   * 删除数据
+   * @returns
+   */
+  destroy(data) {
+    return request({
+      url: '/v1/gameLog/GamePackLog/destroy',
+      method: 'delete',
+      data
+    })
+  },
+
+}

+ 1 - 0
src/views/v1/center/game/edit.vue

@@ -21,6 +21,7 @@
           v-model="formData.main_id"
           :options="mainGameOptions"
           placeholder="请选择主包归属"
+          :disabled="mode == 'edit'"
           allow-clear
         />
       </a-form-item>

+ 8 - 2
src/views/v1/center/iosPayWay/edit.vue

@@ -19,11 +19,17 @@
       <a-form-item label="游戏名称" field="game_id">
         <a-select
           v-model="formData.game_id"
-          :options="gameOptions"
           placeholder="请选择游戏"
           search
           allow-clear
-        />
+        >
+          <a-option
+            v-for="item in gameOptions"
+            :key="item.id"
+            :value="item.id"
+            :label="`${item.id}:${item.name}`"
+          />
+        </a-select>
       </a-form-item>
       <a-form-item label="开启时间" field="pay_stime">
         <a-time-picker

+ 240 - 0
src/views/v1/gameLog/gamePackage/edit.vue

@@ -0,0 +1,240 @@
+<template>
+  <component
+    is="a-modal"
+    :width="tool.getDevice() === 'mobile' ? '100%' : '600px'"
+    v-model:visible="visible"
+    :title="title"
+    :mask-closable="false"
+    :ok-loading="loading"
+    @cancel="close"
+    @before-ok="submit"
+  >
+    <!-- 表单信息 start -->
+    <a-form
+      ref="formRef"
+      :model="formData"
+      :rules="rules"
+      :auto-label-width="true"
+    >
+      <a-form-item label="游戏" field="game_id">
+        <a-select
+          v-model="formData.game_id"
+          :options="gameOptions"
+          placeholder="请选择游戏ID"
+          allow-clear
+          @change="handleGameChange"
+        >
+          <a-option
+            v-for="item in gameOptions"
+            :key="item.id"
+            :value="item.id"
+            :label="`${item.id}:${item.name}:${item.package_name}`"
+          />
+        </a-select>
+      </a-form-item>
+      <a-form-item label="所属媒体" field="media_id">
+        <a-select
+          v-model="formData.media_id"
+          placeholder="请选择媒体ID"
+          allow-clear
+          @change="handleMediaChange"
+        >
+          <a-option
+            v-for="item in mediaOptions"
+            :key="item.id"
+            :value="item.id"
+            :label="`${item.id}:${item.name}`"
+          />
+        </a-select>
+      </a-form-item>
+      <a-form-item label="母包名称" field="name">
+        <a-input v-model="formData.name" placeholder="请输入母包名称" />
+        <template #extra>
+          <div>格式:游戏名(媒体名)</div>
+        </template>
+      </a-form-item>
+
+      <a-form-item label="打包目录" field="letter">
+        <a-input v-model="formData.letter" placeholder="请输入打包目录" />
+        <template #extra>
+          <div>格式:gamejrtt</div>
+        </template>
+      </a-form-item>
+      <a-form-item label="母包包名" field="package_name">
+        <a-input
+          v-model="formData.package_name"
+          placeholder="请输入母包包名com.xxx"
+        />
+        <template #extra>
+          <div>格式:com.gamegame.game.jrtt</div>
+        </template>
+      </a-form-item>
+
+      <div v-if="formData.media_id == 1">
+        <a-divider orientation="center">头条</a-divider>
+        <a-form-item label="媒体APPID" field="appid">
+          <a-input v-model="formData.appid" placeholder="请输入媒体APPID" />
+        </a-form-item>
+        <a-form-item label="母包ID" field="package_id">
+          <a-input v-model="formData.package_id" placeholder="请输入母包ID" />
+        </a-form-item>
+        <a-form-item label="账号ID" field="advertiser_id">
+          <a-input
+            v-model="formData.advertiser_id"
+            placeholder="请输入账号ID"
+          />
+        </a-form-item>
+      </div>
+    </a-form>
+    <!-- 表单信息 end -->
+  </component>
+</template>
+
+<script setup>
+import { ref, reactive, computed } from "vue";
+import tool from "@/utils/tool";
+import { Message, Modal } from "@arco-design/web-vue";
+import api from "../../api/gameLog/gamePackage";
+import advertCommonApi from "../../api/advert/common";
+import centerCommonApi from "../../api/center/common";
+
+const emit = defineEmits(["success"]);
+// 引用定义
+const visible = ref(false);
+const loading = ref(false);
+const formRef = ref();
+const mode = ref("");
+const mediaOptions = ref([]);
+const gameOptions = ref([]);
+
+let title = computed(() => {
+  return "母包管理" + (mode.value == "add" ? "-新增" : "-编辑");
+});
+
+// 表单初始值
+const initialFormData = {
+  id: null,
+  media_id: null,
+  game_id: null,
+  name: "",
+  package_name: "",
+  appid: "0",
+  letter: "",
+};
+
+// 表单信息
+const formData = reactive({ ...initialFormData });
+
+// 验证规则
+const rules = {
+  media_id: [{ required: true, message: "媒体ID必需填写" }],
+  game_id: [{ required: true, message: "游戏ID必需填写" }],
+  name: [{ required: true, message: "母包名称必需填写" }],
+  package_name: [{ required: true, message: "母包包名com.xxx必需填写" }],
+  appid: [{ required: true, message: "媒体APPID必需填写" }],
+};
+
+// 打开弹框
+const open = async (type = "add") => {
+  mode.value = type;
+  // 重置表单数据
+  Object.assign(formData, initialFormData);
+  formRef.value.clearValidate();
+  visible.value = true;
+  await initPage();
+};
+
+// 游戏ID改变
+const handleGameChange = (value) => {
+  console.log(value);
+  if (value) {
+    const media = mediaOptions.value.find(
+      (item) => item.id === formData.media_id
+    );
+    const game = gameOptions.value.find((item) => item.id === value);
+    if (media) {
+      formData.name = `${game.name}(${media.name})`;
+    } else {
+      formData.name = game.name;
+    }
+    formData.package_name = game.package_name;
+  }
+};
+
+// 媒体ID改变
+const handleMediaChange = (value) => {
+  if (value) {
+    const media = mediaOptions.value.find((item) => item.id === value);
+    const game = gameOptions.value.find((item) => item.id === formData.game_id);
+    if (game) {
+      formData.name = `${game.name}(${media.name})`;
+    } else {
+      formData.name = `(${media.name})`;
+    }
+  }
+};
+
+// 初始化页面数据
+const initPage = async () => {
+  await getMediaOptions();
+  await getGameOptions();
+};
+
+// 获取媒体列表Options
+const getMediaOptions = async () => {
+  const res = await advertCommonApi.getMediaOptionsApi();
+  if (res.code == 200) {
+    mediaOptions.value = res.data;
+  }
+};
+
+// 获取游戏列表
+const getGameOptions = async () => {
+  const res = await centerCommonApi.getGameOptionsApi();
+  if (res.code == 200) {
+    gameOptions.value = res.data;
+  }
+};
+
+// 设置数据
+const setFormData = async (data) => {
+  for (const key in formData) {
+    if (data[key] != null && data[key] != undefined) {
+      formData[key] = data[key];
+    }
+  }
+};
+
+// 数据保存
+const submit = async (done) => {
+  const validate = await formRef.value?.validate();
+  if (!validate) {
+    loading.value = true;
+    let data = { ...formData };
+    let result = {};
+    if (mode.value === "add") {
+      // 添加数据
+      data.id = undefined;
+      result = await api.save(data);
+    } else {
+      // 修改数据
+      result = await api.update(data.id, data);
+    }
+    if (result.code === 200) {
+      Message.success("操作成功");
+      emit("success");
+      done(true);
+    }
+    // 防止连续点击提交
+    setTimeout(() => {
+      loading.value = false;
+    }, 500);
+  }
+  done(false);
+};
+
+// 关闭弹窗
+const close = () => (visible.value = false);
+
+defineExpose({ open, setFormData });
+</script>

+ 148 - 0
src/views/v1/gameLog/gamePackage/index.vue

@@ -0,0 +1,148 @@
+<template>
+  <div class="ma-content-block">
+    <sa-table
+      ref="crudRef"
+      :options="options"
+      :columns="columns"
+      :searchForm="searchForm"
+    >
+      <!-- 搜索区 tableSearch -->
+      <template #tableSearch>
+        <a-col :sm="8" :xs="24">
+          <a-form-item label="媒体ID" field="media_id">
+            <a-select
+              v-model="searchForm.media_id"
+              placeholder="请选择媒体ID"
+              allow-clear
+            >
+              <a-option
+                v-for="item in mediaOptions"
+                :key="item.id"
+                :value="item.id"
+                :label="`${item.id}:${item.name}`"
+              />
+            </a-select>
+          </a-form-item>
+        </a-col>
+        <a-col :sm="8" :xs="24">
+          <a-form-item label="游戏" field="game_id">
+            <a-select
+              v-model="searchForm.game_id"
+              placeholder="请选择游戏ID"
+              allow-clear
+            >
+              <a-option
+                v-for="item in gameOptions"
+                :key="item.id"
+                :value="item.id"
+                :label="`${item.id}:${item.name}`"
+              />
+            </a-select>
+          </a-form-item>
+        </a-col>
+      </template>
+
+      <!-- Table 自定义渲染 -->
+    </sa-table>
+
+    <!-- 编辑表单 -->
+    <edit-form ref="editRef" @success="refresh" />
+  </div>
+</template>
+
+<script setup>
+import { onMounted, ref, reactive } from "vue";
+import { Message } from "@arco-design/web-vue";
+import EditForm from "./edit.vue";
+import advertCommonApi from "../../api/advert/common";
+import centerCommonApi from "../../api/center/common";
+import api from "../../api/gameLog/gamePackage";
+
+// 引用定义
+const crudRef = ref();
+const editRef = ref();
+const viewRef = ref();
+const mediaOptions = ref([]);
+const gameOptions = ref([]);
+
+// 搜索表单
+const searchForm = ref({
+  media_id: "",
+  game_id: "",
+});
+
+// SaTable 基础配置
+const options = reactive({
+  api: api.getPageList,
+  rowSelection: { showCheckedAll: true },
+  add: {
+    show: true,
+    auth: ["/v1/gameLog/GamePackage/save"],
+    func: async () => {
+      editRef.value?.open();
+    },
+  },
+  edit: {
+    show: true,
+    auth: ["/v1/gameLog/GamePackage/update"],
+    func: async (record) => {
+      editRef.value?.open("edit");
+      editRef.value?.setFormData(record);
+    },
+  },
+  delete: {
+    show: false,
+    auth: ["/v1/gameLog/GamePackage/destroy"],
+    func: async (params) => {
+      const resp = await api.destroy(params);
+      if (resp.code === 200) {
+        Message.success(`删除成功!`);
+        crudRef.value?.refresh();
+      }
+    },
+  },
+});
+
+// SaTable 列配置
+const columns = reactive([
+  { title: "游戏ID", dataIndex: "game_id", width: 80 },
+  { title: "母包名称", dataIndex: "name", width: 220 },
+  { title: "打包目录", dataIndex: "letter", width: 120 },
+  { title: "包名", dataIndex: "package_name", width: 180 },
+  { title: "媒体APPID", dataIndex: "appid", width: 180 },
+  { title: "账号ID", dataIndex: "advertiser_id", width: 180 },
+]);
+
+// 页面数据初始化
+const initPage = async () => {
+  await getGameOptions();
+  await getMediaOptions();
+};
+
+// 获取媒体列表
+const getMediaOptions = async () => {
+  const resp = await advertCommonApi.getMediaOptionsApi();
+  if (resp.code === 200) {
+    mediaOptions.value = resp.data;
+  }
+};
+
+// 获取游戏列表
+const getGameOptions = async () => {
+  const resp = await centerCommonApi.getGameOptionsApi();
+  if (resp.code === 200) {
+    gameOptions.value = resp.data;
+  }
+};
+
+// SaTable 数据请求
+const refresh = async () => {
+  crudRef.value?.refresh();
+};
+
+// 页面加载完成执行
+onMounted(async () => {
+  initPage();
+  refresh();
+});
+</script>

+ 195 - 0
src/views/v1/gameLog/gamePackageLog/edit.vue

@@ -0,0 +1,195 @@
+<template>
+  <component
+    is="a-modal"
+    :width="tool.getDevice() === 'mobile' ? '100%' : '600px'"
+    v-model:visible="visible"
+    :title="title"
+    :mask-closable="false"
+    :ok-loading="loading"
+    @cancel="close"
+    @before-ok="submit"
+  >
+    <!-- 表单信息 start -->
+    <a-form
+      ref="formRef"
+      :model="formData"
+      :rules="rules"
+      :auto-label-width="true"
+    >
+      <a-form-item label="母包ID" field="package_id">
+        <a-select
+          v-model="formData.package_id"
+          placeholder="请选择母包"
+          allow-clear
+          @change="getPackageDetail"
+        >
+          <a-option
+            v-for="item in packageOptions"
+            :key="item.id"
+            :value="item.id"
+            :label="`${item.id}:${item.name}`"
+          />
+        </a-select>
+      </a-form-item>
+      <a-form-item label="游戏ID" field="game_id" hidden>
+        <a-select
+          v-model="formData.game_id"
+          placeholder="请选择游戏"
+          allow-clear
+        >
+          <a-option
+            v-for="item in gameOptions"
+            :key="item.id"
+            :value="item.id"
+            :label="`${item.id}:${item.name}`"
+          />
+        </a-select>
+      </a-form-item>
+      <a-form-item label="目录" field="letter" hidden>
+        <a-input v-model="formData.letter" placeholder="请输入目录" />
+      </a-form-item>
+      <a-form-item label="渠道" field="agent_id" hidden>
+        <a-input
+          v-model="formData.agent_id"
+          placeholder="请选择渠道"
+          allow-clear
+        />
+      </a-form-item>
+      <a-form-item label="广告位" field="site_id" hidden>
+        <a-input
+          v-model="formData.site_id"
+          placeholder="请输入广告位"
+          allow-clear
+        />
+      </a-form-item>
+      <a-form-item label="icon" field="icon">
+        <sa-upload-image v-model="formData.icon" :limit="1" :multiple="false" />
+      </a-form-item>
+    </a-form>
+    <!-- 表单信息 end -->
+  </component>
+</template>
+
+<script setup>
+import { ref, reactive, computed } from "vue";
+import tool from "@/utils/tool";
+import { Message, Modal } from "@arco-design/web-vue";
+import api from "../../api/gameLog/gamePackageLog";
+import gamePackageApi from "../../api/gameLog/gamePackage";
+import commonApi from "../../api/common";
+const emit = defineEmits(["success"]);
+// 引用定义
+const visible = ref(false);
+const loading = ref(false);
+const formRef = ref();
+const mode = ref("");
+const packageOptions = ref([]);
+const gameOptions = ref([]);
+
+let title = computed(() => {
+  return "打包记录" + (mode.value == "add" ? "-新增" : "-编辑");
+});
+
+// 表单初始值
+const initialFormData = {
+  id: null,
+  package_id: null,
+  game_id: null,
+  letter: "",
+  agent_id: null,
+  site_id: null,
+  icon: "",
+  status: null,
+};
+
+// 表单信息
+const formData = reactive({ ...initialFormData });
+
+// 验证规则
+const rules = {
+  package_id: [{ required: true, message: "母包ID必需填写" }],
+};
+
+// 打开弹框
+const open = async (type = "add") => {
+  mode.value = type;
+  // 重置表单数据
+  Object.assign(formData, initialFormData);
+  formRef.value.clearValidate();
+  visible.value = true;
+  await initPage();
+};
+
+// 初始化页面数据
+const initPage = async () => {
+  await getPackageOptions();
+  await getGameOptions();
+};
+
+// 获取母包列表Options
+const getPackageOptions = async () => {
+  const resp = await commonApi.getPackageOptionsApi();
+  if (resp.code === 200) {
+    packageOptions.value = resp.data;
+  }
+};
+
+// 获取游戏列表Options
+const getGameOptions = async () => {
+  const resp = await commonApi.getGameOptionsApi();
+  if (resp.code === 200) {
+    gameOptions.value = resp.data;
+  }
+};
+
+// 设置数据
+const setFormData = async (data) => {
+  for (const key in formData) {
+    if (data[key] != null && data[key] != undefined) {
+      formData[key] = data[key];
+    }
+  }
+};
+
+// 获取母包详情
+const getPackageDetail = async (id) => {
+  const resp = await gamePackageApi.read(id);
+  if (resp.code === 200) {
+    formData.letter = resp.data.letter;
+    formData.game_id = resp.data.game_id;
+  }
+};
+
+// 数据保存
+const submit = async (done) => {
+  const validate = await formRef.value?.validate();
+  if (!validate) {
+    loading.value = true;
+    let data = { ...formData };
+    let result = {};
+    if (mode.value === "add") {
+      // 添加数据
+      data.id = undefined;
+      result = await api.save(data);
+    } else {
+      // 修改数据
+      result = await api.update(data.id, data);
+    }
+    if (result.code === 200) {
+      Message.success("操作成功");
+      emit("success");
+      done(true);
+    }
+    // 防止连续点击提交
+    setTimeout(() => {
+      loading.value = false;
+    }, 500);
+  }
+  done(false);
+};
+
+// 关闭弹窗
+const close = () => (visible.value = false);
+
+defineExpose({ open, setFormData });
+</script>

+ 103 - 0
src/views/v1/gameLog/gamePackageLog/index.vue

@@ -0,0 +1,103 @@
+<template>
+  <div class="ma-content-block">
+    <sa-table
+      ref="crudRef"
+      :options="options"
+      :columns="columns"
+      :searchForm="searchForm"
+    >
+      <!-- 搜索区 tableSearch -->
+      <template #tableSearch>
+        <a-col :sm="8" :xs="24">
+          <a-form-item label="游戏名" field="game_name">
+            <a-input
+              v-model="searchForm.game_name"
+              placeholder="请输入游戏名"
+              allow-clear
+            />
+          </a-form-item>
+        </a-col>
+      </template>
+
+      <!-- Table 自定义渲染 -->
+    </sa-table>
+
+    <!-- 编辑表单 -->
+    <edit-form ref="editRef" @success="refresh" />
+  </div>
+</template>
+
+<script setup>
+import { onMounted, ref, reactive } from "vue";
+import { Message } from "@arco-design/web-vue";
+import EditForm from "./edit.vue";
+import api from "../../api/gameLog/gamePackageLog";
+
+// 引用定义
+const crudRef = ref();
+const editRef = ref();
+const viewRef = ref();
+
+// 搜索表单
+const searchForm = ref({
+  game_name: "",
+});
+
+// SaTable 基础配置
+const options = reactive({
+  api: api.getPageList,
+  rowSelection: { showCheckedAll: true },
+  add: {
+    show: false,
+    auth: ["/v1/gameLog/GamePackLog/save"],
+    func: async () => {
+      editRef.value?.open();
+    },
+  },
+  edit: {
+    show: false,
+    auth: ["/v1/gameLog/GamePackLog/update"],
+    func: async (record) => {
+      editRef.value?.open("edit");
+      editRef.value?.setFormData(record);
+    },
+  },
+  delete: {
+    show: false,
+    auth: ["/v1/gameLog/GamePackLog/destroy"],
+    func: async (params) => {
+      const resp = await api.destroy(params);
+      if (resp.code === 200) {
+        Message.success(`删除成功!`);
+        crudRef.value?.refresh();
+      }
+    },
+  },
+});
+
+// SaTable 列配置
+const columns = reactive([
+  { title: "广告位ID", dataIndex: "site_id", width: 120 },
+  { title: "渠道ID", dataIndex: "agent_id", width: 120, dict: "agent_list" },
+  { title: "母包名称", dataIndex: "package_name", width: 240 },
+  { title: "目录", dataIndex: "letter", width: 180 },
+  { title: "icon", dataIndex: "icon", type: "image", width: 120 },
+  { title: "状态", dataIndex: "status", width: 180, dict: "package_status" },
+  { title: "创建时间", dataIndex: "create_time", width: 180, type: "date" },
+  { title: "更新时间", dataIndex: "update_time", width: 180, type: "date" },
+]);
+
+// 页面数据初始化
+const initPage = async () => {};
+
+// SaTable 数据请求
+const refresh = async () => {
+  crudRef.value?.refresh();
+};
+
+// 页面加载完成执行
+onMounted(async () => {
+  initPage();
+  refresh();
+});
+</script>