Pārlūkot izejas kodu

新增广告转化条件页面;table组件中的查询条件现在可以接受数字类型的输入并校验;

fxs 7 mēneši atpakaļ
vecāks
revīzija
33bcaffd80

+ 1 - 0
components.d.ts

@@ -27,6 +27,7 @@ declare module 'vue' {
     ElMenu: typeof import('element-plus/es')['ElMenu']
     ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
     ElOption: typeof import('element-plus/es')['ElOption']
+    ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
     ElPagination: typeof import('element-plus/es')['ElPagination']
     ElPopover: typeof import('element-plus/es')['ElPopover']
     ElSelect: typeof import('element-plus/es')['ElSelect']

+ 33 - 14
src/components/Table.vue

@@ -2,7 +2,7 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-20 18:16:18
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-10-16 11:15:24
+ * @LastEditTime: 2024-11-06 17:55:04
  * @FilePath: \Quantity-Creation-Management-Systemc:\Users\NINGMEI\Desktop\Game-Backstage-Management-System\src\components\Table.vue
  * @Description: 
  * 
@@ -507,6 +507,15 @@ const outGetTableData = () => {
   return toRaw(tableData).flat()
 }
 
+const numberInput = (key: string) => {
+  let val = queryFormData[key]
+  val = parseInt(val)
+  if (isNaN(val)) {
+    val = 0
+  }
+  queryFormData[key] = val
+}
+
 // 定义暴露出去的方法
 defineExpose({
   getData,
@@ -551,19 +560,29 @@ onMounted(() => {
           class="queryForm"
           :label-position="'left'"
         >
-          <!-- 所有的input查询框 -->
-          <el-form-item
-            @keyup.enter.native="throttleQueryTableData"
-            :label="item.label"
-            v-for="item in inputFieldsList"
-            class="filterItem"
-          >
-            <el-input
-              v-model="queryFormData[item.name]"
-              :placeholder="item.placeholder"
-              clearable
-            />
-          </el-form-item>
+          <template v-for="item in inputFieldsList">
+            <!-- 所有的input查询框 -->
+            <el-form-item
+              @keyup.enter.native="throttleQueryTableData"
+              :label="item.label"
+              v-if="item.valueType === 'string' || item.valueType === 'float'"
+              class="filterItem"
+            >
+              <el-input clearable v-model="queryFormData[item.name]" />
+            </el-form-item>
+            <el-form-item
+              @keyup.enter.native="throttleQueryTableData"
+              :label="item.label"
+              v-else
+              class="filterItem"
+            >
+              <el-input
+                clearable
+                v-model="queryFormData[item.name]"
+                @input="numberInput(item.name)"
+              />
+            </el-form-item>
+          </template>
 
           <!-- 所有选择框 -->
           <!-- <el-config-provider :value-on-clear="null" :empty-values="[undefined, null]"> -->

+ 2 - 0
src/components/dataAnalysis/HeaderCard.vue

@@ -146,6 +146,7 @@ const watchRoute = watch(
   () => [router.currentRoute.value],
   () => {
     clearBreadcrumb()
+    console.log('值')
   },
   { deep: true }
 )
@@ -164,6 +165,7 @@ onMounted(() => {
     title: props.title,
     pathName: router.currentRoute.value.name as string
   })
+  console.log(breadcrumbList)
 })
 </script>
 

+ 7 - 7
src/hooks/useRequest.ts

@@ -2,13 +2,8 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-20 17:24:06
  * @LastEditors: fxs bjnsfxs@163.com
-<<<<<<< HEAD
- * @LastEditTime: 2024-09-18 12:26:12
- * @FilePath: \Game-Backstage-Management-System\src\hooks\useRequest.ts
-=======
- * @LastEditTime: 2024-10-17 09:26:16
+ * @LastEditTime: 2024-11-06 11:19:21
  * @FilePath: \Quantity-Creation-Management-Systemc:\Users\NINGMEI\Desktop\Game-Backstage-Management-System\src\hooks\useRequest.ts
->>>>>>> develop
  * @Description:
  *
  */
@@ -73,7 +68,12 @@ export function useRequest() {
     // 事件分析
     userActionDetailDistribution: `/user/userActionDetailDistribution`, // 事件统计趋势图
     userActionDetail: `/user/userActionDetail`, // 事件统计详情
-    userActionList: `/user/userActionList` // 游戏事件统计列表
+    userActionList: `/user/userActionList`, // 游戏事件统计列表
+
+    // 用户转化条件
+
+    gameConditionList: `/user/gameConditionList`, // 广告列表
+    setGameCondition: `/user/setGameCondition` // 编辑游戏用户转化条件
   }
 
   /**

+ 40 - 2
src/router/appManage.ts

@@ -2,8 +2,8 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-20 14:24:58
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-09-12 17:45:31
- * @FilePath: \Game-Backstage-Management-System\src\router\appManage.ts
+ * @LastEditTime: 2024-11-06 18:07:30
+ * @FilePath: \Quantity-Creation-Management-Systemc:\Users\NINGMEI\Desktop\Game-Backstage-Management-System\src\router\appManage.ts
  * @Description:
  *
  */
@@ -62,6 +62,44 @@ export default [
             component: () => import('@/views/AppManage/EventMangeTable.vue')
           }
         ]
+      },
+      {
+        path: 'userConversion',
+        name: 'UserConversion',
+        icon: 'Crop',
+        cnName: '广告转化条件',
+        component: () => import('@/views/AppManage/useConversionView.vue'),
+        meta: {
+          activeMenu: 'userConversion',
+          needKeepAlive: false
+        },
+        redirect: '/appManage/userConversion/userConversionTable',
+        children: [
+          {
+            path: 'userConversionDetail', // 注意:这里没有使用动态参数,而是使用查询参数
+            name: 'UserConversionDetail',
+            component: () => import('@/views/AppManage/UserConversionDetail.vue'),
+            beforeEnter: (
+              _to: RouteLocationNormalized,
+              from: RouteLocationNormalized,
+              next: NavigationGuardNext
+            ) => {
+              // next()
+              // || to.fullPath === '/appManage/userConversion/userConversionDetail'
+              if (!from.name) {
+                // 当页面刷新(即没有来源路由)时,重定向到 /eventManage/eventTable
+                next({ path: '/appManage/userConversion/userConversionTable' })
+              } else {
+                next()
+              }
+            }
+          },
+          {
+            path: 'userConversionTable',
+            name: 'UserConversionTable',
+            component: () => import('@/views/AppManage/UserConversionTable.vue')
+          }
+        ]
       }
     ]
   }

+ 4 - 0
src/types/form.ts

@@ -1,5 +1,8 @@
 import type { ReqConfig } from '@/types/dataAnalysis'
 import type { FormRules } from 'element-plus'
+
+export type ValueTypes = 'string' | 'int' | 'float'
+
 export enum FormFieldType {
   SELECT = 'select',
   INPUT = 'input',
@@ -10,6 +13,7 @@ export interface FormField {
   name: string
   cnName: string
   type: FormFieldType
+  valueType?: ValueTypes
   otherOptions?: {
     placeholder?: string
     options?: Array<{ name: string; label: string; value: any }>

+ 4 - 2
src/types/table.ts

@@ -2,12 +2,13 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-20 17:56:13
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-10-12 18:25:23
- * @FilePath: \Game-Backstage-Management-System\src\types\table.ts
+ * @LastEditTime: 2024-11-06 17:44:35
+ * @FilePath: \Quantity-Creation-Management-Systemc:\Users\NINGMEI\Desktop\Game-Backstage-Management-System\src\types\table.ts
  * @Description:
  *
  */
 import type { ReqConfig } from './dataAnalysis'
+import type { ValueTypes } from './form'
 // 颜色类型
 export enum ColorType {
   PRIMARY = 'primary',
@@ -47,6 +48,7 @@ export interface QueryInfo {
   label: string
   type: FilterType
   placeholder: string
+  valueType?: ValueTypes
   otherOption?: any
   default?: any
 }

+ 0 - 4
src/views/AppManage/EventDetailsView.vue

@@ -63,10 +63,6 @@ const ruleForm = reactive({
 
 // 表单规则
 const rules = reactive<FormRules<typeof ruleForm>>({
-  // actionId: [
-  //   { required: true, message: '事件ID是必填项', trigger: 'blur' },
-  //   { min: 1, max: 10, message: '事件ID长度必须在1到10之间', trigger: 'blur' }
-  // ],
   actionName: [
     { required: true, message: '事件名称是必填项', trigger: 'blur' },
     { min: 5, max: 20, message: '事件名称长度必须在5到20之间', trigger: 'blur' }

+ 27 - 0
src/views/AppManage/UseConversionView.vue

@@ -0,0 +1,27 @@
+<script setup lang="ts">
+import HeaderCard from '@/components/dataAnalysis/HeaderCard.vue'
+</script>
+
+<template>
+  <div class="overviewContainer">
+    <div class="overviewHeader">
+      <HeaderCard :title="'用户转化条件'" :need-pf-select="false"></HeaderCard>
+    </div>
+
+    <div class="overviewBody">
+      <router-view v-slot="{ Component }">
+        <component :is="Component" />
+      </router-view>
+    </div>
+  </div>
+</template>
+
+<style scoped>
+.overviewContainer {
+  width: 98%;
+  margin: 1% auto;
+  background-color: white;
+  box-sizing: border-box;
+  position: relative;
+}
+</style>

+ 386 - 0
src/views/AppManage/UserConversionDetail.vue

@@ -0,0 +1,386 @@
+<script setup lang="ts">
+import { FormFieldType } from '@/types/form'
+import type { FormField, ValueTypes } from '@/types/form'
+
+import { useRequest } from '@/hooks/useRequest'
+import { onMounted, reactive, ref } from 'vue'
+import { useCommonStore } from '@/stores/useCommon'
+import type { FormInstance, FormRules } from 'element-plus'
+import axiosInstance from '@/utils/axios/axiosInstance'
+import type { ResponseInfo } from '@/types/res'
+import { throttleFunc } from '@/utils/common'
+import { useRoute } from 'vue-router'
+import router from '@/router'
+
+const route = useRoute()
+const { selectInfo } = useCommonStore()
+
+interface FormData {
+  [key: string]: any
+  gid: string
+  aid: number | null
+  type: string
+  pid: number
+  start_num: number
+  revenue: number
+  duration: number
+  req_count: number
+  exp_count: number
+}
+
+const { AllApi } = useRequest()
+
+const formRef = ref<FormInstance>()
+
+// 是否是编辑状态进入
+const isEdit = ref<boolean>(false)
+
+// 现在进入的广告ID
+const nowAd = ref<string>('')
+
+const formData = ref<FormData>({
+  gid: selectInfo.gid, // 游戏Id
+  aid: null, // 广告ID
+  type: 'active', // 广告类型
+  pid: 0, // 广告父ID
+  start_num: 0, // 启动次数
+  revenue: 0.0, // 预估收益
+  duration: 0, // 在线时长(秒)
+  req_count: 0, // 观看广告次数
+  exp_count: 0 // 完整观看广告次数
+})
+
+const fieldsInfo: Array<FormField> = [
+  {
+    name: 'gid',
+    cnName: '游戏Id',
+    type: FormFieldType.INPUT,
+    valueType: 'string'
+  },
+  {
+    name: 'aid',
+    cnName: '广告ID',
+    type: FormFieldType.INPUT,
+    valueType: 'int'
+  },
+  {
+    name: 'type',
+    cnName: '广告类型',
+    type: FormFieldType.SELECT,
+    valueType: 'string',
+    otherOptions: {
+      placeholder: '请选择广告类型',
+      options: [
+        {
+          label: '激活',
+          value: 'active',
+          name: 'active'
+        },
+        {
+          label: '转化',
+          value: 'conversion',
+          name: 'conversion'
+        }
+      ]
+    }
+  },
+  {
+    name: 'pid',
+    cnName: '广告父ID',
+    type: FormFieldType.INPUT,
+    valueType: 'int'
+  },
+
+  {
+    name: 'start_num',
+    cnName: '启动次数',
+    type: FormFieldType.INPUT,
+    valueType: 'int'
+  },
+  {
+    name: 'revenue',
+    cnName: '预估收益',
+    type: FormFieldType.INPUT,
+    valueType: 'float'
+  },
+  {
+    name: 'duration',
+    cnName: '在线时长(秒)',
+    type: FormFieldType.INPUT,
+    valueType: 'int'
+  },
+  {
+    name: 'req_count',
+    cnName: '观看广告次数',
+    type: FormFieldType.INPUT,
+    valueType: 'int'
+  },
+  {
+    name: 'exp_count',
+    cnName: '完整观看广告次数',
+    type: FormFieldType.INPUT,
+    valueType: 'int'
+  }
+]
+
+const rules = reactive<FormRules<FormData>>({
+  gid: [{ required: true, message: '游戏Id是必填项', trigger: 'blur' }],
+  aid: [
+    { required: true, message: '广告ID是必填项', trigger: 'blur' },
+    { type: 'number', message: '广告ID必须是数字', trigger: 'blur' }
+  ],
+  pid: [
+    { required: false, message: '广告父ID是可选项', trigger: 'blur' },
+    { type: 'number', message: '广告父ID必须是数字', trigger: 'blur' }
+  ],
+  type: [
+    { required: true, message: '广告类型是必填项', trigger: 'trigger' },
+
+    {
+      validator: (_rule, value) =>
+        ['active', 'conversion'].includes(value) || '广告类型必须是 "active" 或 "conversion"',
+      trigger: 'blur'
+    }
+  ],
+  start_num: [
+    { required: false, message: '启动次数是可选项', trigger: 'blur' },
+    { type: 'number', message: '启动次数必须是数字', trigger: 'blur' }
+  ],
+  revenue: [
+    { required: false, message: '预估收益是可选项', trigger: 'blur' },
+    {
+      validator: (_rule, value, callback) => {
+        // 允许为空值,不进行验证
+        if (value === '' || value === null || value === undefined) {
+          callback() // 允许空值通过
+          return
+        }
+
+        // 检查是否为有效的浮动数值
+        const validFloatValue = /^(\d+(\.\d+)?|\.\d+)$/.test(value)
+        if (!validFloatValue) {
+          callback(new Error('预估收益必须是有效的数字'))
+        } else {
+          callback() // 验证通过
+        }
+      },
+      trigger: 'blur'
+    }
+  ],
+  duration: [
+    { required: false, message: '在线时长是可选项', trigger: 'blur' },
+    { type: 'number', message: '在线时长必须是数字', trigger: 'blur' }
+  ],
+  req_count: [
+    { required: false, message: '观看广告次数是可选项', trigger: 'blur' },
+    { type: 'number', message: '观看广告次数必须是数字', trigger: 'blur' }
+  ],
+  exp_count: [
+    { required: false, message: '完整观看广告次数是可选项', trigger: 'blur' },
+    { type: 'number', message: '完整观看广告次数必须是数字', trigger: 'blur' }
+  ]
+})
+
+// 编辑页面不能修改的字段
+const disableEditField = ['pid', 'gid', 'aid', 'type']
+
+/**
+ * @description: 格式化输入,主要是对数字处理
+ * @param {*} val 值
+ * @param {*} key 键
+ * @param {*} valueType 值类型
+ * @return {*}
+ */
+const formatInput = (val: any, valueType: ValueTypes | undefined): any => {
+  let newVal: any = ''
+  if (valueType === 'int') {
+    // 格式化为整数
+    const intValue = parseInt(val, 10)
+    newVal = isNaN(intValue) ? 0 : intValue // 如果转换失败,则返回 null
+  }
+
+  if (valueType === 'float') {
+    const inputValue = parseFloat(val)
+    newVal = isNaN(inputValue) ? 0 : inputValue
+  }
+
+  if (valueType === 'string') {
+    // 直接返回字符串,去掉两边的空格
+    newVal = String(val).trim()
+  }
+
+  return newVal // 默认返回原值
+}
+
+/**
+ * @description: 重置表单
+ * @return {*}
+ */
+const resetForm = () => {
+  formRef.value?.resetFields()
+}
+
+/**
+ * @description: 提交表单
+ * @param {*} formEl 表单实例
+ * @return {*}
+ */
+const subForm = (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+
+  formEl.validate(async (valid) => {
+    if (valid) {
+      try {
+        // 转为对应类型
+        for (let [k, v] of Object.entries(formData.value)) {
+          let valueType = fieldsInfo.find((item) => item.name === k)?.valueType
+          formData.value[k] = formatInput(v, valueType)
+        }
+        let result = (await axiosInstance.post(
+          AllApi.setGameCondition,
+          formData.value
+        )) as ResponseInfo
+        if (result.code !== 0) {
+          throw new Error(result.msg)
+        }
+        ElMessage.success('设置成功')
+      } catch (err) {
+        ElMessage.error('设置失败')
+        console.log(err)
+      }
+    } else {
+      ElMessage.warning('请检查表单内容是否正确')
+    }
+  })
+}
+
+/**
+ * @description: 初始化表单数据,根据查询参数来
+ * @return {*}
+ */
+const initFormData = () => {
+  let query = route.query
+  if (Object.keys(query).length > 0) {
+    for (let [k, v] of Object.entries(query)) {
+      if (k !== 'head')
+        formData.value[k] = formatInput(v, fieldsInfo.find((item) => item.name === k)?.valueType)
+    }
+    nowAd.value = query.aid as string
+    isEdit.value = true
+  }
+}
+
+const throttleSub = throttleFunc(subForm, 1000)
+const throttleReset = throttleFunc(resetForm, 1000)
+
+const goBack = () => {
+  router.push('/appManage/userConversion')
+}
+initFormData()
+
+onMounted(() => {})
+</script>
+
+<template>
+  <div class="conditionContainer">
+    <div class="conditionHeader">
+      <el-page-header @back="goBack">
+        <template #content>
+          <span class="text-large font-600 mr-3" style="font-weight: 600">
+            {{ nowAd || '新增' }}
+          </span>
+        </template>
+      </el-page-header>
+    </div>
+    <div class="conditionBody">
+      <el-form
+        ref="formRef"
+        :model="formData"
+        :rules="rules"
+        label-width="auto"
+        status-icon
+        :inline="true"
+      >
+        <template v-for="item in fieldsInfo" :key="item.name">
+          <el-form-item
+            style="width: 350px"
+            v-if="item.type === FormFieldType.INPUT"
+            :label="item.cnName"
+            :prop="item.name"
+          >
+            <el-input
+              v-if="item.valueType === 'string' || item.valueType === 'float'"
+              :disabled="(disableEditField.includes(item.name) && isEdit) || item.name === 'gid'"
+              v-model="formData[item.name]"
+            />
+            <el-input
+              v-else
+              :disabled="(disableEditField.includes(item.name) && isEdit) || item.name === 'gid'"
+              v-model.number="formData[item.name]"
+            />
+          </el-form-item>
+          <el-form-item
+            :label="item.cnName"
+            :prop="item.name"
+            v-if="item.type === FormFieldType.SELECT"
+            style="width: 350px"
+          >
+            <el-select
+              v-model="formData[item.name]"
+              :label="item.cnName"
+              :placeholder="item.otherOptions?.placeholder"
+              :disabled="(disableEditField.includes(item.name) && isEdit) || item.name === 'gid'"
+              size="default"
+            >
+              <el-option
+                v-for="option in item.otherOptions?.options"
+                :key="option.name"
+                :label="option.label"
+                :value="option.value"
+              />
+            </el-select>
+          </el-form-item>
+        </template>
+      </el-form>
+      <div class="formBtnGroup">
+        <el-button class="handleBtn" @click="throttleReset" plain color="#626aef">重置</el-button>
+        <el-button class="handleBtn" @click="throttleSub(formRef)" color="#626aef">提交</el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style scoped>
+.conditionContainer {
+  width: 98%;
+  margin: 1% auto;
+  box-sizing: border-box;
+}
+
+.conditionBody {
+  box-sizing: border-box;
+  margin-top: 15px;
+  padding: 24px;
+  display: flex;
+  align-items: center;
+  width: 100%;
+  position: relative;
+  background-color: white;
+  box-shadow:
+    0 4px 8px 0 rgba(0, 0, 0, 0.02),
+    0 1px 3px 0 rgba(0, 0, 0, 0.02);
+}
+
+.formBtnGroup {
+  display: flex;
+  flex-direction: column;
+}
+
+.handleBtn {
+  margin-bottom: 18px;
+}
+
+.conditionHeader {
+  padding: 0 24px;
+}
+</style>

+ 181 - 0
src/views/AppManage/UserConversionTable.vue

@@ -0,0 +1,181 @@
+<script setup lang="ts">
+import { useRequest } from '@/hooks/useRequest'
+import { useCommonStore } from '@/stores/useCommon'
+
+import Table from '@/components/Table.vue'
+import type { ReqConfig } from '@/types/dataAnalysis'
+import { reactive, watch } from 'vue'
+import {
+  FilterType,
+  type QueryInfo,
+  type SelectInfo,
+  type TableFieldInfo,
+  type TablePaginationSetting
+} from '@/types/table'
+import router from '@/router'
+
+const { selectInfo } = useCommonStore()
+const { AllApi } = useRequest()
+
+// 表格请求配置
+const requestConfig = reactive<ReqConfig>({
+  url: AllApi.gameConditionList,
+  otherOptions: {
+    gid: selectInfo.gid,
+    type: 'active'
+  }
+})
+
+// 表格分页设置
+const pagingConfig = reactive<TablePaginationSetting>({
+  limit: 20,
+  currentPage: 1,
+  total: 0,
+  pagesizeList: [20, 30]
+})
+
+// 表格字段信息
+const tableFieldsInfo = reactive<Array<TableFieldInfo>>([
+  {
+    name: 'gid',
+    cnName: '游戏ID',
+    isShow: true,
+    needSort: false
+  },
+  {
+    name: 'aid',
+    cnName: '广告ID',
+    isShow: true,
+    needSort: false
+  },
+  {
+    name: 'type',
+    cnName: '类型',
+    isShow: true,
+    needSort: false
+  },
+  {
+    name: 'pid',
+    cnName: '广告父ID',
+    isShow: true,
+    needSort: false
+  },
+  {
+    name: 'start_num',
+    cnName: '启动次数',
+    isShow: true,
+    needSort: false
+  },
+  {
+    name: 'revenue',
+    cnName: '预估收益',
+    isShow: true,
+    needSort: false
+  },
+  {
+    name: 'duration',
+    cnName: '在线时长',
+    isShow: true,
+    needSort: false
+  },
+  {
+    name: 'req_count',
+    cnName: '观看广告次数',
+    isShow: true,
+    needSort: false
+  },
+  {
+    name: 'exp_count',
+    cnName: '完整观看广告次数',
+    isShow: true,
+    needSort: false
+  }
+])
+
+// 事件类型查询信息
+const convertType: Array<SelectInfo> = [
+  {
+    name: 'active',
+    cnName: '激活',
+    value: 'active'
+  },
+  {
+    name: 'conversion',
+    cnName: '转化',
+    value: 'conversion'
+  }
+]
+
+// 查询字段设置
+const queryInfo: Array<QueryInfo> = [
+  {
+    name: 'pid',
+    label: '父级ID',
+    type: FilterType.INPUT,
+    placeholder: '请输入父级ID',
+    valueType: 'int',
+    default: 0
+  },
+  {
+    name: 'aid',
+    label: '广告ID',
+    type: FilterType.INPUT,
+    placeholder: '请输入广告ID',
+    default: 0,
+    valueType: 'int'
+  },
+  {
+    name: 'type',
+    label: '类型',
+    type: FilterType.SELECT,
+    placeholder: '请选择类型',
+    otherOption: convertType,
+    default: 'active'
+  }
+]
+
+watch(
+  () => selectInfo.gid,
+  (val: string) => {
+    requestConfig.otherOptions.gid = val
+  }
+)
+
+const goDetail = (data: any) => {
+  router.push({
+    path: '/appManage/userConversion/userConversionDetail',
+    query: data
+  })
+}
+</script>
+
+<template>
+  <div class="conversionTable">
+    <Table
+      :request-config="requestConfig"
+      :table-fields-info="tableFieldsInfo"
+      :pagination-config="pagingConfig"
+      :open-page-query="true"
+      :query-info="queryInfo"
+      :open-filter-query="true"
+      :need-left-tools="true"
+      :open-remoteinquiry="true"
+      @add-new-item="goDetail"
+    >
+      <template #tableOperation>
+        <el-table-column label="操作" align="center">
+          <template #default="scope">
+            <el-text class="operationBtn" type="primary" @click="goDetail(scope.row)">编辑</el-text>
+          </template>
+        </el-table-column>
+      </template>
+    </Table>
+  </div>
+</template>
+
+<style scoped>
+.conversionTable {
+  padding: 10px 24px;
+  background-color: white;
+}
+</style>