Forráskód Böngészése

广告账号消耗,支付成功率

ith5 2 hónapja
szülő
commit
8071f9f1ed

+ 197 - 0
src/components/ad-advertiser-select/index.vue

@@ -0,0 +1,197 @@
+<template>
+  <div class="game-select-wrapper">
+    <a-tree-select
+      :model-value="modelValue"
+      @update:model-value="onUpdate"
+      :data="gameListTree"
+      tree-checked-strategy="child"
+      :tree-checkable="multiple"
+      :max-tag-count="1"
+      :fieldNames="{ title: 'name', key: 'id' }"
+      allow-search
+      allow-clear
+      :check-strictly="props.multiple"
+      :filter-tree-node="filterTreeNode"
+      :treeProps="{
+        defaultExpandedKeys: [],
+        expandedKeys: expandedKeys,
+        onExpand: expandTreeNode,
+      }"
+      placeholder="媒体和组织"
+    >
+      <template #header v-if="multiple && showFooter">
+        <div class="tree-select-header">
+          <a-space>
+            <a-button type="primary" size="mini" @click="handleSelectAll">
+              全选
+            </a-button>
+            <a-button type="outline" size="mini" @click="handleInvertSelect">
+              反选
+            </a-button>
+            <a-button type="dashed" size="mini" @click="handleClear">
+              清空
+            </a-button>
+          </a-space>
+        </div>
+      </template>
+    </a-tree-select>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, defineProps, defineEmits } from "vue";
+import commonApi from "../../views/v1/api/common";
+
+const props = defineProps({
+  modelValue: {
+    type: [String, Number, Array],
+    default: "",
+  },
+  multiple: {
+    type: Boolean,
+    default: false,
+  },
+  showFooter: {
+    type: Boolean,
+    default: true,
+  },
+});
+
+const expandedKeys = ref([]);
+const searchText = ref(""); // 存储当前搜索文本
+const emit = defineEmits(["update:modelValue", "change"]);
+const onUpdate = (val) => {
+  emit("update:modelValue", val);
+  emit("change", val);
+};
+const gameListTree = ref([]);
+
+const fetchGameList = async () => {
+  // 这里替换为你的实际API请求
+  // const response = await api.getGameList()
+  // gameListTree.value = response.data
+  const response = await commonApi.getMediaOrgTreeKvApi();
+  gameListTree.value = response.data;
+
+  if (!props.multiple) {
+    gameListTree.value = response.data.map((item) => {
+      return {
+        ...item,
+        disabled: true,
+        children: item.children || [],
+      };
+    });
+  }
+};
+const filterTreeNode = (inputText, node) => {
+  // 更新搜索文本
+  searchText.value = inputText || "";
+
+  if (inputText) {
+    const filterData =
+      node.name.toLowerCase().indexOf(inputText.toLowerCase()) > -1;
+    if (filterData) {
+      if (!expandedKeys.value.includes(node.parent_id)) {
+        expandedKeys.value.push(node.parent_id);
+      }
+    }
+    return filterData;
+  }
+};
+
+const expandTreeNode = (expandKeys) => {
+  expandedKeys.value = expandKeys;
+};
+
+// 获取所有子节点的ID(支持搜索过滤)
+const getAllChildIds = (tree, filterText = "") => {
+  const ids = [];
+  const traverse = (nodes) => {
+    nodes.forEach((node) => {
+      // 检查节点是否匹配搜索条件
+      const matchFilter =
+        !filterText ||
+        node.name.toLowerCase().indexOf(filterText.toLowerCase()) > -1;
+
+      if (node.children && node.children.length > 0) {
+        traverse(node.children);
+      } else if (matchFilter) {
+        // 只有叶子节点且匹配搜索条件时才添加
+        ids.push(node.id);
+      }
+    });
+  };
+  traverse(tree);
+  return ids;
+};
+
+// 全选
+const handleSelectAll = () => {
+  if (!props.multiple) return;
+  const currentSelected = Array.isArray(props.modelValue)
+    ? props.modelValue
+    : [];
+  const filterIds = getAllChildIds(gameListTree.value, searchText.value);
+
+  if (searchText.value) {
+    // 有搜索条件:保留原有选中项,添加搜索结果
+    const newIds = [...new Set([...currentSelected, ...filterIds])];
+    onUpdate(newIds);
+  } else {
+    // 无搜索条件:全选所有
+    onUpdate(filterIds);
+  }
+};
+
+// 反选
+const handleInvertSelect = () => {
+  if (!props.multiple) return;
+  const currentSelected = Array.isArray(props.modelValue)
+    ? props.modelValue
+    : [];
+  const filterIds = getAllChildIds(gameListTree.value, searchText.value);
+
+  if (searchText.value) {
+    // 有搜索条件:只在搜索结果范围内反选,保留其他已选项
+    const otherSelected = currentSelected.filter(
+      (id) => !filterIds.includes(id)
+    );
+    const invertedInFilter = filterIds.filter(
+      (id) => !currentSelected.includes(id)
+    );
+    onUpdate([...otherSelected, ...invertedInFilter]);
+  } else {
+    // 无搜索条件:全局反选
+    const invertedIds = filterIds.filter((id) => !currentSelected.includes(id));
+    onUpdate(invertedIds);
+  }
+};
+
+// 清空
+const handleClear = () => {
+  if (!props.multiple) return;
+  onUpdate([]);
+};
+
+onMounted(fetchGameList);
+</script>
+
+<style scoped>
+.game-select-wrapper {
+  width: 100%;
+}
+
+.tree-select-header {
+  padding: 8px 12px;
+  border-bottom: 1px solid var(--color-border-2);
+}
+
+.tree-select-header :deep(.arco-btn-text) {
+  color: var(--color-text-2);
+}
+
+.tree-select-header :deep(.arco-btn-text:hover) {
+  color: rgb(var(--primary-6));
+  background-color: transparent;
+}
+</style>

+ 136 - 0
src/views/v1/advert/app/edit.vue

@@ -0,0 +1,136 @@
+<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="media_id">
+        <a-input v-model="formData.media_id" placeholder="请输入媒体渠道ID" />
+      </a-form-item>
+      <a-form-item label="应用名称" field="name">
+        <a-input v-model="formData.name" placeholder="请输入应用名称" />
+      </a-form-item>
+      <a-form-item label="应用ID" field="app_id">
+        <a-input v-model="formData.app_id" placeholder="请输入应用ID" />
+      </a-form-item>
+      <a-form-item label="应用密钥" field="app_secret">
+        <a-input v-model="formData.app_secret" placeholder="请输入应用密钥" />
+      </a-form-item>
+      <a-form-item label="OAuth授权地址" field="auth_url">
+        <a-input v-model="formData.auth_url" placeholder="请输入OAuth授权地址" />
+      </a-form-item>
+      <a-form-item label="Token获取地址" field="token_url">
+        <a-input v-model="formData.token_url" placeholder="请输入Token获取地址" />
+      </a-form-item>
+      <a-form-item label="Token刷新地址" field="refresh_url">
+        <a-input v-model="formData.refresh_url" placeholder="请输入Token刷新地址" />
+      </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/advert/app'
+
+const emit = defineEmits(['success'])
+// 引用定义
+const visible = ref(false)
+const loading = ref(false)
+const formRef = ref()
+const mode = ref('')
+
+let title = computed(() => {
+  return '开放平台应用授权信息' + (mode.value == 'add' ? '-新增' : '-编辑')
+})
+
+// 表单初始值
+const initialFormData = {
+  id: null,
+  media_id: null,
+  name: '',
+  app_id: '',
+  app_secret: '',
+  auth_url: '',
+  token_url: '',
+  refresh_url: '',
+}
+
+// 表单信息
+const formData = reactive({ ...initialFormData })
+
+// 验证规则
+const rules = {
+  media_id: [{ required: true, message: '媒体渠道ID必需填写' }],
+  name: [{ required: true, message: '应用名称必需填写' }],
+  app_id: [{ required: true, message: '应用ID必需填写' }],
+  app_secret: [{ required: true, message: '应用密钥必需填写' }],
+  auth_url: [{ required: true, message: 'OAuth授权地址必需填写' }],
+  token_url: [{ required: true, message: 'Token获取地址必需填写' }],
+  refresh_url: [{ required: true, message: 'Token刷新地址必需填写' }],
+}
+
+// 打开弹框
+const open = async (type = 'add') => {
+  mode.value = type
+  // 重置表单数据
+  Object.assign(formData, initialFormData)
+  formRef.value.clearValidate()
+  visible.value = true
+  await initPage()
+}
+
+// 初始化页面数据
+const initPage = async () => {}
+
+// 设置数据
+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>

+ 112 - 0
src/views/v1/advert/app/index.vue

@@ -0,0 +1,112 @@
+<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="name">
+            <a-input
+              v-model="searchForm.name"
+              placeholder="请输入应用名称"
+              allow-clear
+            />
+          </a-form-item>
+        </a-col>
+        <a-col :sm="8" :xs="24">
+          <a-form-item label="应用ID" field="app_id">
+            <a-input
+              v-model="searchForm.app_id"
+              placeholder="请输入应用ID"
+              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/advert/app";
+
+// 引用定义
+const crudRef = ref();
+const editRef = ref();
+const viewRef = ref();
+
+// 搜索表单
+const searchForm = ref({
+  name: "",
+  app_id: "",
+});
+
+// SaTable 基础配置
+const options = reactive({
+  api: api.getPageList,
+  rowSelection: { showCheckedAll: true },
+  add: {
+    show: true,
+    auth: ["/v1/advert/AdAdvertiserApp/save"],
+    func: async () => {
+      editRef.value?.open();
+    },
+  },
+  edit: {
+    show: true,
+    auth: ["/v1/advert/AdAdvertiserApp/update"],
+    func: async (record) => {
+      editRef.value?.open("edit");
+      editRef.value?.setFormData(record);
+    },
+  },
+  delete: {
+    show: true,
+    auth: ["/v1/advert/AdAdvertiserApp/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: "media_id", width: 180 },
+  { title: "应用名称", dataIndex: "name", width: 180 },
+  { title: "应用ID", dataIndex: "app_id", width: 180 },
+  { title: "应用密钥", dataIndex: "app_secret", width: 180 },
+  { title: "OAuth授权地址", dataIndex: "auth_url", width: 180 },
+  { title: "Token获取地址", dataIndex: "token_url", width: 180 },
+  { title: "Token刷新地址", dataIndex: "refresh_url", width: 180 },
+]);
+
+// 页面数据初始化
+const initPage = async () => {};
+
+// SaTable 数据请求
+const refresh = async () => {
+  crudRef.value?.refresh();
+};
+
+// 页面加载完成执行
+onMounted(async () => {
+  initPage();
+  refresh();
+});
+</script>

+ 136 - 0
src/views/v1/advert/mediaAuth/app/edit.vue

@@ -0,0 +1,136 @@
+<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="media_id">
+        <a-input v-model="formData.media_id" placeholder="请输入媒体渠道ID" />
+      </a-form-item>
+      <a-form-item label="应用名称" field="name">
+        <a-input v-model="formData.name" placeholder="请输入应用名称" />
+      </a-form-item>
+      <a-form-item label="应用ID" field="app_id">
+        <a-input v-model="formData.app_id" placeholder="请输入应用ID" />
+      </a-form-item>
+      <a-form-item label="应用密钥" field="app_secret">
+        <a-input v-model="formData.app_secret" placeholder="请输入应用密钥" />
+      </a-form-item>
+      <a-form-item label="OAuth授权地址" field="auth_url">
+        <a-input v-model="formData.auth_url" placeholder="请输入OAuth授权地址" />
+      </a-form-item>
+      <a-form-item label="Token获取地址" field="token_url">
+        <a-input v-model="formData.token_url" placeholder="请输入Token获取地址" />
+      </a-form-item>
+      <a-form-item label="Token刷新地址" field="refresh_url">
+        <a-input v-model="formData.refresh_url" placeholder="请输入Token刷新地址" />
+      </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/advert/mediaAuth/app'
+
+const emit = defineEmits(['success'])
+// 引用定义
+const visible = ref(false)
+const loading = ref(false)
+const formRef = ref()
+const mode = ref('')
+
+let title = computed(() => {
+  return '开放平台应用授权信息' + (mode.value == 'add' ? '-新增' : '-编辑')
+})
+
+// 表单初始值
+const initialFormData = {
+  id: null,
+  media_id: null,
+  name: '',
+  app_id: '',
+  app_secret: '',
+  auth_url: '',
+  token_url: '',
+  refresh_url: '',
+}
+
+// 表单信息
+const formData = reactive({ ...initialFormData })
+
+// 验证规则
+const rules = {
+  media_id: [{ required: true, message: '媒体渠道ID必需填写' }],
+  name: [{ required: true, message: '应用名称必需填写' }],
+  app_id: [{ required: true, message: '应用ID必需填写' }],
+  app_secret: [{ required: true, message: '应用密钥必需填写' }],
+  auth_url: [{ required: true, message: 'OAuth授权地址必需填写' }],
+  token_url: [{ required: true, message: 'Token获取地址必需填写' }],
+  refresh_url: [{ required: true, message: 'Token刷新地址必需填写' }],
+}
+
+// 打开弹框
+const open = async (type = 'add') => {
+  mode.value = type
+  // 重置表单数据
+  Object.assign(formData, initialFormData)
+  formRef.value.clearValidate()
+  visible.value = true
+  await initPage()
+}
+
+// 初始化页面数据
+const initPage = async () => {}
+
+// 设置数据
+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>

+ 112 - 0
src/views/v1/advert/mediaAuth/app/index.vue

@@ -0,0 +1,112 @@
+<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="name">
+            <a-input
+              v-model="searchForm.name"
+              placeholder="请输入应用名称"
+              allow-clear
+            />
+          </a-form-item>
+        </a-col>
+        <a-col :sm="8" :xs="24">
+          <a-form-item label="应用ID" field="app_id">
+            <a-input
+              v-model="searchForm.app_id"
+              placeholder="请输入应用ID"
+              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/advert/mediaAuth/app";
+
+// 引用定义
+const crudRef = ref();
+const editRef = ref();
+const viewRef = ref();
+
+// 搜索表单
+const searchForm = ref({
+  name: "",
+  app_id: "",
+});
+
+// SaTable 基础配置
+const options = reactive({
+  api: api.getPageList,
+  rowSelection: { showCheckedAll: true },
+  add: {
+    show: true,
+    auth: ["/v1/advert/mediaAuth/AdAdvertiserApp/save"],
+    func: async () => {
+      editRef.value?.open();
+    },
+  },
+  edit: {
+    show: true,
+    auth: ["/v1/advert/mediaAuth/AdAdvertiserApp/update"],
+    func: async (record) => {
+      editRef.value?.open("edit");
+      editRef.value?.setFormData(record);
+    },
+  },
+  delete: {
+    show: true,
+    auth: ["/v1/advert/mediaAuth/AdAdvertiserApp/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: "media_id", width: 180 },
+  { title: "应用名称", dataIndex: "name", width: 180 },
+  { title: "应用ID", dataIndex: "app_id", width: 180 },
+  { title: "应用密钥", dataIndex: "app_secret", width: 180 },
+  { title: "OAuth授权地址", dataIndex: "auth_url", width: 180 },
+  { title: "Token获取地址", dataIndex: "token_url", width: 180 },
+  { title: "Token刷新地址", dataIndex: "refresh_url", width: 180 },
+]);
+
+// 页面数据初始化
+const initPage = async () => {};
+
+// SaTable 数据请求
+const refresh = async () => {
+  crudRef.value?.refresh();
+};
+
+// 页面加载完成执行
+onMounted(async () => {
+  initPage();
+  refresh();
+});
+</script>

+ 65 - 0
src/views/v1/api/advert/app.js

@@ -0,0 +1,65 @@
+import { request } from "@/utils/request.js";
+
+/**
+ * 开放平台应用授权信息 API接口
+ */
+export default {
+  /**
+   * 数据列表
+   * @returns
+   */
+  getPageList(params = {}) {
+    return request({
+      url: "/v1/advert/AdAdvertiserApp/index",
+      method: "get",
+      params,
+    });
+  },
+
+  /**
+   * 添加数据
+   * @returns
+   */
+  save(params = {}) {
+    return request({
+      url: "/v1/advert/AdAdvertiserApp/save",
+      method: "post",
+      data: params,
+    });
+  },
+
+  /**
+   * 更新数据
+   * @returns
+   */
+  update(id, data = {}) {
+    return request({
+      url: "/v1/advert/AdAdvertiserApp/update?id=" + id,
+      method: "put",
+      data,
+    });
+  },
+
+  /**
+   * 读取数据
+   * @returns
+   */
+  read(id) {
+    return request({
+      url: "/v1/advert/AdAdvertiserApp/read?id=" + id,
+      method: "get",
+    });
+  },
+
+  /**
+   * 删除数据
+   * @returns
+   */
+  destroy(data) {
+    return request({
+      url: "/v1/advert/AdAdvertiserApp/destroy",
+      method: "delete",
+      data,
+    });
+  },
+};

+ 65 - 0
src/views/v1/api/advert/mediaAuth/app.js

@@ -0,0 +1,65 @@
+import { request } from "@/utils/request.js";
+
+/**
+ * 开放平台应用授权信息 API接口
+ */
+export default {
+  /**
+   * 数据列表
+   * @returns
+   */
+  getPageList(params = {}) {
+    return request({
+      url: "/v1/advert/mediaAuth/AdAdvertiserApp/index",
+      method: "get",
+      params,
+    });
+  },
+
+  /**
+   * 添加数据
+   * @returns
+   */
+  save(params = {}) {
+    return request({
+      url: "/v1/advert/mediaAuth/AdAdvertiserApp/save",
+      method: "post",
+      data: params,
+    });
+  },
+
+  /**
+   * 更新数据
+   * @returns
+   */
+  update(id, data = {}) {
+    return request({
+      url: "/v1/advert/mediaAuth/AdAdvertiserApp/update?id=" + id,
+      method: "put",
+      data,
+    });
+  },
+
+  /**
+   * 读取数据
+   * @returns
+   */
+  read(id) {
+    return request({
+      url: "/v1/advert/mediaAuth/AdAdvertiserApp/read?id=" + id,
+      method: "get",
+    });
+  },
+
+  /**
+   * 删除数据
+   * @returns
+   */
+  destroy(data) {
+    return request({
+      url: "/v1/advert/mediaAuth/AdAdvertiserApp/destroy",
+      method: "delete",
+      data,
+    });
+  },
+};

+ 61 - 51
src/views/v1/api/common.js

@@ -1,4 +1,4 @@
-import { request } from '@/utils/request.js'
+import { request } from "@/utils/request.js";
 
 /**
  * 公共 API接口
@@ -10,10 +10,10 @@ export default {
    */
   getPackageOptionsApi(params = {}) {
     return request({
-      url: '/v1/common/getPackageOptions',
-      method: 'get',
-      params
-    })
+      url: "/v1/common/getPackageOptions",
+      method: "get",
+      params,
+    });
   },
 
   /**
@@ -23,10 +23,10 @@ export default {
    */
   getGameOptionsApi(params = {}) {
     return request({
-      url: '/v1/common/getGameOptions',
-      method: 'get',
-      params
-    })
+      url: "/v1/common/getGameOptions",
+      method: "get",
+      params,
+    });
   },
 
   /**
@@ -34,10 +34,10 @@ export default {
    */
   getGameOptionsNoAuthApi(params = {}) {
     return request({
-      url: '/v1/common/getGameOptionsNoAuth',
-      method: 'get',
-      params
-    })
+      url: "/v1/common/getGameOptionsNoAuth",
+      method: "get",
+      params,
+    });
   },
 
   // /**
@@ -70,10 +70,10 @@ export default {
    */
   getAgentSiteOptionsApi(params = {}) {
     return request({
-      url: '/v1/common/getAgentSiteOptions',
-      method: 'get',
-      params
-    })
+      url: "/v1/common/getAgentSiteOptions",
+      method: "get",
+      params,
+    });
   },
 
   /**
@@ -82,10 +82,10 @@ export default {
    */
   getMediaOptionsApi(params = {}) {
     return request({
-      url: '/v1/common/getMediaOptions',
-      method: 'get',
-      params
-    })
+      url: "/v1/common/getMediaOptions",
+      method: "get",
+      params,
+    });
   },
 
   /**
@@ -93,20 +93,20 @@ export default {
    */
   getPayChannelOptionsApi(params = {}) {
     return request({
-      url: '/v1/common/getPayChannelOptions',
-      method: 'get',
-      params
-    })
+      url: "/v1/common/getPayChannelOptions",
+      method: "get",
+      params,
+    });
   },
   /**
    * 获取游戏列表根据部门ID
    */
   getGameListByDeptIdApi(params = {}) {
     return request({
-      url: '/v1/common/getGameListOptionsByDeptId',
-      method: 'get',
-      params
-    })
+      url: "/v1/common/getGameListOptionsByDeptId",
+      method: "get",
+      params,
+    });
   },
 
   /**
@@ -114,10 +114,10 @@ export default {
    */
   getGameListOptionsApi(params = {}) {
     return request({
-      url: '/v1/common/getGameListOptions',
-      method: 'get',
-      params
-    })
+      url: "/v1/common/getGameListOptions",
+      method: "get",
+      params,
+    });
   },
 
   /**
@@ -125,10 +125,10 @@ export default {
    */
   getGameListOptionsByGroupApi(params = {}) {
     return request({
-      url: '/v1/common/getGameListOptionsByGroup',
-      method: 'get',
-      params
-    })
+      url: "/v1/common/getGameListOptionsByGroup",
+      method: "get",
+      params,
+    });
   },
   /**
    * 获取后台归属人列表
@@ -136,29 +136,39 @@ export default {
    */
   getAuthOptionsApi(params = {}) {
     return request({
-      url: '/v1/common/getAuthOptions',
-      method: 'get',
-      params
-    })
+      url: "/v1/common/getAuthOptions",
+      method: "get",
+      params,
+    });
   },
   /**
    * 获取设计作者列表
    */
   getDesignAuthOptions(params = {}) {
     return request({
-      url: '/v1/common/getDesignAuthOptions',
-      method: 'get',
-      params
-    })
+      url: "/v1/common/getDesignAuthOptions",
+      method: "get",
+      params,
+    });
   },
   /**
    * 获取系统配置
    */
   getSystemConfigApi(params = {}) {
     return request({
-      url: '/v1/common/getSystemConfig',
-      method: 'get',
-      params
-    })
-  }
-}
+      url: "/v1/common/getSystemConfig",
+      method: "get",
+      params,
+    });
+  },
+  /**
+   * 获取媒体与组织树形KV
+   */
+  getMediaOrgTreeKvApi(params = {}) {
+    return request({
+      url: "/v1/common/getMediaOrgTreeKv",
+      method: "get",
+      params,
+    });
+  },
+};

+ 21 - 11
src/views/v1/api/customer/reconciliation.js

@@ -1,4 +1,4 @@
-import { request } from '@/utils/request.js'
+import { request } from "@/utils/request.js";
 
 /**
  * 充值对账 API接口
@@ -10,19 +10,29 @@ export default {
    */
   getPageList(params = {}) {
     return request({
-      url: '/v1/customer/Reconciliation/getChannelIncome',
-      method: 'get',
-      params
-    })
+      url: "/v1/customer/Reconciliation/getChannelIncome",
+      method: "get",
+      params,
+    });
   },
   /**
    * 渠道汇总(财务)
    */
   getCwAgentList(params = {}) {
     return request({
-      url: '/v1/customer/Reconciliation/getChannelSummary',
-      method: 'get',
-      params
-    })
-  }
-}
+      url: "/v1/customer/Reconciliation/getChannelSummary",
+      method: "get",
+      params,
+    });
+  },
+  /**
+   * 广告账号消耗
+   */
+  getAdCostList(params = {}) {
+    return request({
+      url: "/v1/customer/Reconciliation/getAdCost",
+      method: "get",
+      params,
+    });
+  },
+};

+ 93 - 0
src/views/v1/customer/reconciliation/adCost/index.vue

@@ -0,0 +1,93 @@
+<template>
+  <div class="ma-content-block">
+    <sa-table
+      ref="crudRef"
+      :options="options"
+      :columns="columns"
+      :searchForm="searchForm"
+    >
+      <!-- 搜索区 tableSearch -->
+      <template #tableSearch>
+        <a-col :sm="6" :xs="24">
+          <a-form-item label="媒体和组织" field="bm_id">
+            <ad-advertiser-select v-model="searchForm.bm_id" multiple />
+          </a-form-item>
+        </a-col>
+        <a-col :sm="6" :xs="24">
+          <a-form-item label="广告账号ID" field="advertiser_id">
+            <a-input
+              v-model="searchForm.advertiser_id"
+              placeholder="请输入广告账号ID"
+              allow-clear
+            />
+          </a-form-item>
+        </a-col>
+        <a-col :sm="6" :xs="24">
+          <a-form-item label="账户名称" field="advertiser_name">
+            <a-input
+              v-model="searchForm.advertiser_name"
+              placeholder="请输入广告账户名称"
+              allow-clear
+            />
+          </a-form-item>
+        </a-col>
+        <a-col :sm="6" :xs="24">
+          <a-form-item label="日期" field="date">
+            <a-range-picker
+              v-model="searchForm.date"
+              :show-time="false"
+              mode="date"
+              class="w-full"
+            />
+          </a-form-item>
+        </a-col>
+      </template>
+    </sa-table>
+  </div>
+</template>
+
+<script setup>
+import { onMounted, ref, reactive } from "vue";
+import api from "../../../api/customer/reconciliation";
+import AdAdvertiserSelect from "@/components/ad-advertiser-select/index.vue";
+import dayjs from "dayjs";
+const crudRef = ref();
+const options = reactive({
+  api: api.getAdCostList,
+  pk: "id",
+  showSummary: true,
+  operationColumn: false,
+  showSort: false,
+  summary: [
+    {
+      action: "totalRow",
+      dataIndex: "advertiser_name",
+    },
+    {
+      action: "totalRow",
+      dataIndex: "money",
+    },
+    {
+      action: "totalRow",
+      dataIndex: "ori_money",
+    },
+  ],
+});
+
+const searchForm = ref({
+  advertiser_name: "",
+  advertiser_id: "",
+  bm_id: [],
+  date: [
+    dayjs().subtract(8, "day").format("YYYY-MM-DD"),
+    dayjs().format("YYYY-MM-DD"),
+  ],
+});
+
+const columns = reactive([
+  { title: "广告账号名称", dataIndex: "advertiser_name", width: 120 },
+  { title: "原始金额", dataIndex: "ori_money", width: 120 },
+  { title: "结算金额", dataIndex: "money", width: 120 },
+  { title: "返点", dataIndex: "fandain", width: 120 },
+]);
+</script>

+ 12 - 8
src/views/v1/gameLog/paySuccessRate/index.vue

@@ -9,6 +9,7 @@
         @click="
           () => {
             searchForm.type = item.value;
+            activeTab = item.value;
             refresh();
           }
         "
@@ -80,7 +81,7 @@
         </div>
         <a-statistic
           class="my-[5px]"
-          :value="payNum || 0"
+          :value="successNum || 0"
           :value-from="0"
           animation
           show-group-separator
@@ -148,14 +149,14 @@
       </a-card>
     </div>
     <div class="mt-[24px] gap-[15px] flex">
-      <a-card class="w-[70%]" title="今日 VS 昨日支付成功率对比">
+      <a-card class="w-[70%]" title="支付成功率对比">
         <sa-chart :option="lineChartOption" height="300px" />
       </a-card>
       <a-card class="w-[30%]" title="支付渠道下单比例">
         <!-- <sa-chart :option="lineChartOption" height="300px" /> -->
       </a-card>
     </div>
-    <a-card title="今日 vs 昨日详细数据" class="mt-[24px]">
+    <a-card title="详细数据" class="mt-[24px]">
       <sa-table
         ref="crudRef"
         :searchForm="searchForm"
@@ -167,14 +168,14 @@
 </template>
 
 <script setup>
-import { onMounted, reactive, ref, watch } from "vue";
+import { onMounted, reactive, ref, watch, nextTick } from "vue";
 import SaIcon from "@/components/sa-icon/index.vue";
 import SaChart from "@/components/sa-chart/index.vue";
 import api from "../../api/gameLog/analyse";
 
 const crudRef = ref();
 const orderNum = ref(0);
-const payNum = ref(0);
+const successNum = ref(0);
 const paySuccessRate = ref(0);
 const dictOptions = ref({
   day: "今日",
@@ -223,6 +224,8 @@ const options = reactive({
   operationColumn: false,
   showSearch: false,
   pageSimple: true,
+  pageLayout: "normal", // 改为普通布局,避免固定高度问题
+  height: "auto", // 自动高度
 });
 const columns = reactive([
   {
@@ -286,7 +289,7 @@ const getPaySuccessRate = async () => {
   });
   let data = res.data;
   orderNum.value = data.orderNum;
-  payNum.value = data.payNum;
+  successNum.value = data.successNum;
   paySuccessRate.value = data.paySuccessRate;
   chartOption.value = {
     legend: {
@@ -377,12 +380,13 @@ const getPaySuccessRate = async () => {
 
 // SaTable 数据请求
 const refresh = async () => {
+  await getPaySuccessRate();
+  // 延迟刷新表格,确保数据已经更新
+  await nextTick();
   crudRef.value?.refresh();
-  getPaySuccessRate();
 };
 
 onMounted(() => {
-  getPaySuccessRate();
   refresh();
 });
 </script>