瀏覽代碼

coding:投流代码提交

zhangqin 2 天之前
父節點
當前提交
8313d47506

+ 8 - 0
src/api/adv/advertiser.js

@@ -52,3 +52,11 @@ export function batchDelAdvertiser(ids) {
   })
 }
 
+// 启用/停用广告商
+export function enableAdvertiser(id) {
+  return request({
+    url: '/advertiser/enable/' + id,
+    method: 'post'
+  })
+}
+

+ 16 - 0
src/api/adv/callbackAccount.js

@@ -52,3 +52,19 @@ export function batchDelCallbackAccount(ids) {
   })
 }
 
+// 查询事件类型
+export function queryEventType(advertiserId) {
+  return request({
+    url: '/callback-account/queryEventType/' + advertiserId,
+    method: 'post'
+  })
+}
+
+// 保存转换事件
+export function saveEventType(id, data) {
+  return request({
+    url: '/callback-account/saveEventType/' + id,
+    method: 'post',
+    data: data
+  })
+}

+ 0 - 54
src/api/adv/leadSource.js

@@ -1,54 +0,0 @@
-import request from '@/utils/request'
-
-// 分页查询线索来源列表
-export function pageLeadSource(query) {
-  return request({
-    url: '/lead-source/page',
-    method: 'get',
-    params: query
-  })
-}
-
-// 根据ID查询线索来源详情
-export function getLeadSource(id) {
-  return request({
-    url: '/lead-source/' + id,
-    method: 'get'
-  })
-}
-
-// 创建线索来源
-export function addLeadSource(data) {
-  return request({
-    url: '/lead-source',
-    method: 'post',
-    data: data
-  })
-}
-
-// 更新线索来源
-export function updateLeadSource(id, data) {
-  return request({
-    url: '/lead-source/' + id,
-    method: 'put',
-    data: data
-  })
-}
-
-// 删除线索来源
-export function delLeadSource(id) {
-  return request({
-    url: '/lead-source/' + id,
-    method: 'delete'
-  })
-}
-
-// 批量删除线索来源
-export function batchDelLeadSource(ids) {
-  return request({
-    url: '/lead-source/batch',
-    method: 'delete',
-    data: ids
-  })
-}
-

+ 8 - 0
src/api/adv/site.js

@@ -50,3 +50,11 @@ export function getSiteStatistics(id) {
   })
 }
 
+// 启用/停用站点
+export function enableSite(id) {
+  return request({
+    url: '/site/enable/' + id,
+    method: 'put'
+  })
+}
+

+ 34 - 0
src/api/adv/siteStatistics.js

@@ -0,0 +1,34 @@
+import request from '@/utils/request'
+
+// 查询站点统计列表
+export function pageSiteStatistics(query) {
+  return request({
+    url: '/site-statistics/page',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询站点统计详情
+export function getSiteStatistics(id) {
+  return request({
+    url: '/site-statistics/' + id,
+    method: 'get'
+  })
+}
+
+// 根据站点ID查询统计
+export function getSiteStatisticsBySiteId(siteId) {
+  return request({
+    url: '/site-statistics/site/' + siteId,
+    method: 'get'
+  })
+}
+
+// 刷新站点统计数据
+export function refreshSiteStatistics(siteId) {
+  return request({
+    url: '/site-statistics/refresh/' + siteId,
+    method: 'post'
+  })
+}

+ 47 - 55
src/views/adv/advertiser/index.vue

@@ -10,10 +10,10 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
-      <el-form-item label="类型" prop="type">
-        <el-select v-model="queryParams.type" placeholder="请选择类型" clearable size="small">
-          <el-option label="类型1" :value="1" />
-          <el-option label="类型2" :value="2" />
+      <el-form-item label="类型" prop="custom">
+        <el-select v-model="queryParams.custom" placeholder="请选择类型" clearable size="small">
+          <el-option label="线上广告商" :value="1" />
+          <el-option label="自定义广告商" :value="2" />
         </el-select>
       </el-form-item>
       <el-form-item label="状态" prop="enabled">
@@ -38,55 +38,45 @@
           @click="handleAdd"
         >新增</el-button>
       </el-col>
-      <el-col :span="1.5">
-        <el-button
-          plain
-          type="danger"
-          icon="el-icon-delete"
-          size="mini"
-          :disabled="multiple"
-          @click="handleDelete"
-        >删除</el-button>
-      </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="advertiserList" @selection-change="handleSelectionChange">
-      <el-table-column type="selection" width="55" align="center" />
-      <el-table-column label="ID" align="center" prop="id" width="80" />
-      <el-table-column label="广告商名称" align="center" prop="advertiserName" />
-      <el-table-column label="类型" align="center" prop="type" width="100">
+    <el-table border v-loading="loading" :data="advertiserList">
+      <el-table-column label="广告商名称" align="left" prop="advertiserName" show-overflow-tooltip>
         <template slot-scope="scope">
-          <span v-if="scope.row.type === 1">类型1</span>
-          <span v-else-if="scope.row.type === 2">类型2</span>
-          <span v-else>{{ scope.row.type }}</span>
+          <el-switch
+            v-model="scope.row.enabled"
+            :active-value="1"
+            :inactive-value="0"
+            @change="handleStatusChange(scope.row)"
+            style="margin-right: 10px; vertical-align: middle;"
+          >
+          </el-switch>
+          <span>{{ scope.row.advertiserName }}</span>
         </template>
       </el-table-column>
-      <el-table-column label="状态" align="center" prop="enabled" width="100">
+      <el-table-column label="类型" align="center" prop="custom">
         <template slot-scope="scope">
-          <el-tag v-if="scope.row.enabled === 1" type="success">启用</el-tag>
-          <el-tag v-else type="danger">禁用</el-tag>
+          <el-tag v-if="scope.row.custom === 1" type="success">线上广告商</el-tag>
+          <el-tag v-else type="danger">自定义广告商</el-tag>
         </template>
       </el-table-column>
-      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+      <el-table-column label="是否支持API" align="center" prop="supportApi">
         <template slot-scope="scope">
-          <span>{{ parseTime(scope.row.createTime) }}</span>
+          <el-tag v-if="scope.row.supportApi === 1" type="success">是</el-tag>
+          <el-tag v-else type="info">否</el-tag>
         </template>
       </el-table-column>
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180">
+      <el-table-column label="是否支持回传" align="center" prop="supportCallback">
         <template slot-scope="scope">
-          <el-button
-            size="mini"
-            type="text"
-            icon="el-icon-edit"
-            @click="handleUpdate(scope.row)"
-          >修改</el-button>
-          <el-button
-            size="mini"
-            type="text"
-            icon="el-icon-delete"
-            @click="handleDelete(scope.row)"
-          >删除</el-button>
+          <el-tag v-if="scope.row.supportCallback === 1" type="success">是</el-tag>
+          <el-tag v-else type="info">否</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" align="center" prop="enabled">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.enabled === 1" type="success">启用</el-tag>
+          <el-tag v-else type="danger">禁用</el-tag>
         </template>
       </el-table-column>
     </el-table>
@@ -105,10 +95,9 @@
         <el-form-item label="广告商名称" prop="advertiserName">
           <el-input v-model="form.advertiserName" placeholder="请输入广告商名称" />
         </el-form-item>
-        <el-form-item label="类型" prop="type">
-          <el-select v-model="form.type" placeholder="请选择类型" style="width: 100%">
-            <el-option label="类型1" :value="1" />
-            <el-option label="类型2" :value="2" />
+        <el-form-item label="类型" prop="custom">
+          <el-select v-model="form.custom" placeholder="请选择类型" style="width: 100%" disabled>
+            <el-option label="自定义广告商" :value="2" />
           </el-select>
         </el-form-item>
         <el-form-item label="状态" prop="enabled">
@@ -127,7 +116,7 @@
 </template>
 
 <script>
-import { pageAdvertiser, getAdvertiser, addAdvertiser, updateAdvertiser, delAdvertiser, batchDelAdvertiser } from "@/api/adv/advertiser";
+import { pageAdvertiser, getAdvertiser, addAdvertiser, updateAdvertiser, delAdvertiser, batchDelAdvertiser, enableAdvertiser } from "@/api/adv/advertiser";
 
 export default {
   name: "Advertiser",
@@ -156,7 +145,7 @@ export default {
         current: 1,
         size: 10,
         advertiserName: undefined,
-        type: undefined,
+        custom: undefined,
         enabled: undefined
       },
       // 表单参数
@@ -166,7 +155,7 @@ export default {
         advertiserName: [
           { required: true, message: "广告商名称不能为空", trigger: "blur" }
         ],
-        type: [
+        custom: [
           { required: true, message: "类型不能为空", trigger: "change" }
         ],
         enabled: [
@@ -198,7 +187,7 @@ export default {
       this.form = {
         id: undefined,
         advertiserName: undefined,
-        type: undefined,
+        custom: 2,
         enabled: 1
       };
       this.resetForm("form");
@@ -259,19 +248,22 @@ export default {
         }
       });
     },
-    /** 删除按钮操作 */
-    handleDelete(row) {
-      const ids = row.id ? [row.id] : this.ids;
-      this.$confirm('是否确认删除选中的广告商?', "警告", {
+    /** 启用/停用状态切换 */
+    handleStatusChange(row) {
+      const statusText = row.enabled === 1 ? '启用' : '停用';
+      this.$confirm(`确认要${statusText}广告商“${row.advertiserName}”吗?`, "提示", {
         confirmButtonText: "确定",
         cancelButtonText: "取消",
         type: "warning"
       }).then(() => {
-        return batchDelAdvertiser(ids);
+        return enableAdvertiser(row.id);
       }).then(() => {
+        this.msgSuccess(`${statusText}成功`);
         this.getList();
-        this.msgSuccess("删除成功");
-      }).catch(function() {});
+      }).catch(() => {
+        // 取消操作,恢复状态
+        row.enabled = row.enabled === 1 ? 0 : 1;
+      });
     }
   }
 };

+ 385 - 12
src/views/adv/callbackAccount/index.vue

@@ -1,15 +1,25 @@
 <template>
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
-      <el-form-item label="账号名称" prop="callbackAccountName">
+      <el-form-item label="账号名称" prop="accountName">
         <el-input
-          v-model="queryParams.callbackAccountName"
+          v-model="queryParams.accountName"
           placeholder="请输入账号名称"
           clearable
           size="small"
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
+      <el-form-item label="广告商" prop="advertiserId">
+        <el-select v-model="queryParams.advertiserId" placeholder="请选择广告商" clearable size="small" filterable>
+          <el-option
+            v-for="item in advertiserOptions"
+            :key="item.id"
+            :label="item.advertiserName"
+            :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
       <el-form-item>
         <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
@@ -42,14 +52,21 @@
     <el-table border v-loading="loading" :data="accountList" @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="ID" align="center" prop="id" width="80" />
-      <el-table-column label="账号名称" align="center" prop="callbackAccountName" />
+      <el-table-column label="回传账号" align="center" prop="accountName" min-width="150" show-overflow-tooltip />
+      <el-table-column label="广告商" align="center" prop="advertiserName" min-width="150" show-overflow-tooltip />
       <el-table-column label="创建时间" align="center" prop="createTime" width="180">
         <template slot-scope="scope">
           <span>{{ parseTime(scope.row.createTime) }}</span>
         </template>
       </el-table-column>
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180">
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="240">
         <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-setting"
+            @click="handleConversionType(scope.row)"
+          >添加转换类型</el-button>
           <el-button
             size="mini"
             type="text"
@@ -76,21 +93,154 @@
 
     <!-- 添加或修改回传账号对话框 -->
     <el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
-      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
-        <el-form-item label="账号名称" prop="callbackAccountName">
-          <el-input v-model="form.callbackAccountName" placeholder="请输入账号名称" />
+      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+        <el-form-item label="广告商" prop="advertiserId">
+          <el-select 
+            v-model="form.advertiserId" 
+            placeholder="请选择广告商" 
+            style="width: 100%"
+            filterable
+            @change="handleAdvertiserChange"
+          >
+            <el-option
+              v-for="item in advertiserOptions"
+              :key="item.id"
+              :label="item.advertiserName"
+              :value="item.id"
+            />
+          </el-select>
         </el-form-item>
+        
+        <el-form-item label="账号名称" prop="accountName">
+          <el-input v-model="form.accountName" placeholder="请输入账号名称" />
+        </el-form-item>
+        
+        <!-- advertiserId = 1 的字段 -->
+        <template v-if="form.advertiserId === 1">
+          <el-form-item label="ocpcUid" prop="adAccountId">
+            <el-input v-model="form.adAccountId" placeholder="请输入ocpcUid" />
+          </el-form-item>
+          <el-form-item label="ocpcToken" prop="accessToken">
+            <el-input v-model="form.accessToken" placeholder="请输入ocpcToken" type="password" show-password />
+          </el-form-item>
+        </template>
+        
+        <!-- advertiserId = 3 的字段 -->
+        <template v-if="form.advertiserId === 3">
+          <el-form-item label="广告账户id" prop="adAccountId">
+            <el-input v-model="form.adAccountId" placeholder="请输入广告账户id" />
+          </el-form-item>
+          <el-form-item label="数据源id" prop="scrId">
+            <el-input v-model="form.scrId" placeholder="请输入数据源id" />
+          </el-form-item>
+          <el-form-item label="数据源token" prop="accessToken">
+            <el-input v-model="form.accessToken" placeholder="请输入数据源token" type="password" show-password />
+          </el-form-item>
+        </template>
+        
+        <!-- advertiserId = 4 的字段 -->
+        <template v-if="form.advertiserId === 4">
+          <el-form-item label="ownerId" prop="adAccountId">
+            <el-input v-model="form.adAccountId" placeholder="请输入ownerId" />
+          </el-form-item>
+          <el-form-item label="apiId" prop="appId">
+            <el-input v-model="form.appId" placeholder="请输入apiId" />
+          </el-form-item>
+          <el-form-item label="apiKey" prop="appSecret">
+            <el-input v-model="form.appSecret" placeholder="请输入apiKey" type="password" show-password />
+          </el-form-item>
+        </template>
+        
+        <!-- advertiserId = 5 的字段 -->
+        <template v-if="form.advertiserId === 5">
+          <el-form-item label="账户id" prop="adAccountId">
+            <el-input v-model="form.adAccountId" placeholder="请输入账户id" />
+          </el-form-item>
+          <el-form-item label="应用id" prop="appId">
+            <el-input v-model="form.appId" placeholder="请输入应用id" />
+          </el-form-item>
+          <el-form-item label="应用秘钥" prop="appSecret">
+            <el-input v-model="form.appSecret" placeholder="请输入应用秘钥" type="password" show-password />
+          </el-form-item>
+          <el-form-item label="scr_id" prop="scrId">
+            <el-input v-model="form.scrId" placeholder="请输入scr_id" />
+          </el-form-item>
+        </template>
       </el-form>
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="submitForm">确 定</el-button>
         <el-button @click="cancel">取 消</el-button>
       </div>
     </el-dialog>
+
+    <!-- 转换类型配置对话框 -->
+    <el-dialog title="添加转换类型" :visible.sync="conversionOpen" width="700px" append-to-body>
+      <div style="margin-bottom: 15px;">
+        <el-button type="primary" size="small" icon="el-icon-plus" @click="addConversionEvent">添加事件</el-button>
+      </div>
+      
+      <div v-if="conversionEvents.length === 0" style="text-align: center; padding: 40px 0; color: #909399;">
+        暂无转换事件,请点击“添加事件”按钮添加
+      </div>
+      
+      <div v-for="(event, index) in conversionEvents" :key="index" class="conversion-event-item">
+        <div class="event-header">
+          <span class="event-title">事件{{ index + 1 }}</span>
+          <el-button 
+            type="danger" 
+            size="mini" 
+            icon="el-icon-delete" 
+            circle
+            @click="removeConversionEvent(index)"
+          ></el-button>
+        </div>
+        
+        <el-form label-width="140px" style="margin-top: 10px;">
+          <el-form-item label="广告商转化类型">
+            <el-select 
+              v-model="event.advertiserEventType" 
+              placeholder="请选择广告商转化类型" 
+              style="width: 100%"
+              @change="handleAdvertiserEventChange(index, $event)"
+            >
+              <el-option
+                v-for="item in advertiserEventOptions"
+                :key="item.eventType"
+                :label="item.eventName"
+                :value="item.eventType"
+              />
+            </el-select>
+          </el-form-item>
+          
+          <el-form-item label="回传数据类型">
+            <el-select 
+              v-model="event.systemEventType" 
+              placeholder="请选择回传数据类型" 
+              style="width: 100%"
+              @change="handleSystemEventChange(index, $event)"
+            >
+              <el-option
+                v-for="item in systemEventOptions"
+                :key="item.eventType"
+                :label="item.eventName"
+                :value="item.eventType"
+              />
+            </el-select>
+          </el-form-item>
+        </el-form>
+      </div>
+      
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="saveConversionEvents">保 存</el-button>
+        <el-button @click="conversionOpen = false">取 消</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script>
-import { pageCallbackAccount, getCallbackAccount, addCallbackAccount, updateCallbackAccount, delCallbackAccount, batchDelCallbackAccount } from "@/api/adv/callbackAccount";
+import { pageCallbackAccount, getCallbackAccount, addCallbackAccount, updateCallbackAccount, delCallbackAccount, batchDelCallbackAccount, queryEventType, saveEventType } from "@/api/adv/callbackAccount";
+import { pageAdvertiser } from "@/api/adv/advertiser";
 
 export default {
   name: "CallbackAccount",
@@ -110,22 +260,98 @@ export default {
       total: 0,
       // 回传账号表格数据
       accountList: [],
+      // 广告商选项
+      advertiserOptions: [],
       // 弹出层标题
       title: "",
       // 是否显示弹出层
       open: false,
+      // 是否显示转换类型弹窗
+      conversionOpen: false,
+      // 当前操作的回传账号
+      currentAccount: null,
+      // 转换事件列表
+      conversionEvents: [],
+      // systemBuiltIn=0)
+      advertiserEventOptions: [],
+      // systemBuiltIn=1)
+      systemEventOptions: [],
       // 查询参数
       queryParams: {
         current: 1,
         size: 10,
-        callbackAccountName: undefined
+        accountName: undefined,
+        advertiserId: undefined
       },
       // 表单参数
       form: {},
       // 表单校验
       rules: {
-        callbackAccountName: [
+        advertiserId: [
+          { required: true, message: "请选择广告商", trigger: "change" }
+        ],
+        accountName: [
           { required: true, message: "账号名称不能为空", trigger: "blur" }
+        ],
+        adAccountId: [
+          { 
+            validator: (rule, value, callback) => {
+              if ([1, 3, 4, 5].includes(this.form.advertiserId) && !value) {
+                callback(new Error('此字段不能为空'));
+              } else {
+                callback();
+              }
+            }, 
+            trigger: "blur" 
+          }
+        ],
+        accessToken: [
+          { 
+            validator: (rule, value, callback) => {
+              if ([1, 3].includes(this.form.advertiserId) && !value) {
+                callback(new Error('此字段不能为空'));
+              } else {
+                callback();
+              }
+            }, 
+            trigger: "blur" 
+          }
+        ],
+        scrId: [
+          { 
+            validator: (rule, value, callback) => {
+              if ([3, 5].includes(this.form.advertiserId) && !value) {
+                callback(new Error('此字段不能为空'));
+              } else {
+                callback();
+              }
+            }, 
+            trigger: "blur" 
+          }
+        ],
+        appId: [
+          { 
+            validator: (rule, value, callback) => {
+              if ([4, 5].includes(this.form.advertiserId) && !value) {
+                callback(new Error('此字段不能为空'));
+              } else {
+                callback();
+              }
+            }, 
+            trigger: "blur" 
+          }
+        ],
+        appSecret: [
+          { 
+            validator: (rule, value, callback) => {
+              if ([4, 5].includes(this.form.advertiserId) && !value) {
+                callback(new Error('此字段不能为空'));
+              } else {
+                callback();
+              }
+            }, 
+            trigger: "blur" 
+          }
         ]
       }
     };
@@ -134,6 +360,12 @@ export default {
     this.getList();
   },
   methods: {
+    /** 查询广告商选项 */
+    getAdvertiserOptions() {
+      pageAdvertiser({ current: 1, size: 1000 }).then(response => {
+        this.advertiserOptions = response.data.records;
+      });
+    },
     /** 查询回传账号列表 */
     getList() {
       this.loading = true;
@@ -152,7 +384,14 @@ export default {
     reset() {
       this.form = {
         id: undefined,
-        callbackAccountName: undefined
+        advertiserId: undefined,
+        advertiserName: undefined,
+        accountName: undefined,
+        adAccountId: undefined,
+        accessToken: undefined,
+        scrId: undefined,
+        appId: undefined,
+        appSecret: undefined
       };
       this.resetForm("form");
     },
@@ -175,12 +414,14 @@ export default {
     /** 新增按钮操作 */
     handleAdd() {
       this.reset();
+      this.getAdvertiserOptions();
       this.open = true;
       this.title = "添加回传账号";
     },
     /** 修改按钮操作 */
     handleUpdate(row) {
       this.reset();
+      this.getAdvertiserOptions();
       const id = row.id || this.ids[0];
       getCallbackAccount(id).then(response => {
         this.form = response.data;
@@ -212,6 +453,111 @@ export default {
         }
       });
     },
+    /** 广告商变化时 */
+    handleAdvertiserChange(advertiserId) {
+      this.form.advertiserName = "";
+      if (advertiserId) {
+        const advertiser = this.advertiserOptions.find(item => item.id === advertiserId);
+        if (advertiser) {
+          this.form.advertiserName = advertiser.advertiserName;
+        }
+      }
+      // 清空其他字段
+      this.form.adAccountId = undefined;
+      this.form.accessToken = undefined;
+      this.form.scrId = undefined;
+      this.form.appId = undefined;
+      this.form.appSecret = undefined;
+    },
+    /** 添加转换类型按钮操作 */
+    handleConversionType(row) {
+      this.currentAccount = row;
+      this.conversionOpen = true;
+      
+      // 加载事件类型选项
+      this.loadEventTypes(row.advertiserId);
+      
+      // 解析已有的转换事件
+      this.parseConversionEvents(row.conversionEvent);
+    },
+    /** 加载事件类型选项 */
+    loadEventTypes(advertiserId) {
+      queryEventType(advertiserId).then(response => {
+        const eventTypes = response.data || [];
+        // systemBuiltin='0' 为广告商事件
+        this.advertiserEventOptions = eventTypes.filter(item => item.systemBuiltin === '0');
+        // systemBuiltin='1' 为系统事件
+        this.systemEventOptions = eventTypes.filter(item => item.systemBuiltin === '1');
+      }).catch(error => {
+        console.error('加载事件类型失败:', error);
+        this.msgError('加载事件类型失败');
+      });
+    },
+    /** 解析转换事件 */
+    parseConversionEvents(conversionEvent) {
+      try {
+        if (conversionEvent && typeof conversionEvent === 'string') {
+          this.conversionEvents = JSON.parse(conversionEvent);
+        } else if (Array.isArray(conversionEvent)) {
+          this.conversionEvents = conversionEvent;
+        } else {
+          this.conversionEvents = [];
+        }
+      } catch (error) {
+        console.error('解析转换事件失败:', error);
+        this.conversionEvents = [];
+      }
+    },
+    /** 添加转换事件 */
+    addConversionEvent() {
+      this.conversionEvents.push({
+        systemEventType: '',
+        systemEventTypeName: '',
+        advertiserEventType: '',
+        advertiserEventName: ''
+      });
+    },
+    /** 删除转换事件 */
+    removeConversionEvent(index) {
+      this.conversionEvents.splice(index, 1);
+    },
+    /** 广告商事件变化 */
+    handleAdvertiserEventChange(index, eventType) {
+      const event = this.advertiserEventOptions.find(item => item.eventType === eventType);
+      if (event) {
+        this.conversionEvents[index].advertiserEventName = event.eventName;
+      }
+    },
+    /** 系统事件变化 */
+    handleSystemEventChange(index, eventType) {
+      const event = this.systemEventOptions.find(item => item.eventType === eventType);
+      if (event) {
+        this.conversionEvents[index].systemEventTypeName = event.eventName;
+      }
+    },
+    /** 保存转换事件 */
+    saveConversionEvents() {
+      // 校验数据
+      for (let i = 0; i < this.conversionEvents.length; i++) {
+        const event = this.conversionEvents[i];
+        if (!event.systemEventType || !event.advertiserEventType) {
+          this.msgError(`请完善事件${i + 1}的配置`);
+          return;
+        }
+      }
+      
+      // 调用保存接口
+      saveEventType(this.currentAccount.id, this.conversionEvents).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess('保存成功');
+          this.conversionOpen = false;
+          this.getList();
+        }
+      }).catch(error => {
+        console.error('保存失败:', error);
+        this.msgError('保存失败');
+      });
+    },
     /** 删除按钮操作 */
     handleDelete(row) {
       const ids = row.id ? [row.id] : this.ids;
@@ -220,7 +566,12 @@ export default {
         cancelButtonText: "取消",
         type: "warning"
       }).then(() => {
-        return batchDelCallbackAccount(ids);
+        // 单条删除使用 delCallbackAccount,批量删除使用 batchDelCallbackAccount
+        if (row.id) {
+          return delCallbackAccount(row.id);
+        } else {
+          return batchDelCallbackAccount(ids);
+        }
       }).then(() => {
         this.getList();
         this.msgSuccess("删除成功");
@@ -230,3 +581,25 @@ export default {
 };
 </script>
 
+<style scoped>
+.conversion-event-item {
+  margin-bottom: 20px;
+  padding: 15px;
+  border: 1px solid #e8e8e8;
+  border-radius: 4px;
+  background-color: #fafafa;
+}
+
+.event-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 10px;
+}
+
+.event-title {
+  font-weight: 600;
+  font-size: 15px;
+  color: #303133;
+}
+</style>

+ 109 - 55
src/views/adv/leadSource/index.vue → src/views/adv/customPromotionAccount/index.vue

@@ -1,19 +1,23 @@
 <template>
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
-      <el-form-item label="来源名称" prop="sourceName">
+      <el-form-item label="账号名称" prop="accountName">
         <el-input
-          v-model="queryParams.sourceName"
-          placeholder="请输入来源名称"
+          v-model="queryParams.accountName"
+          placeholder="请输入账号名称"
           clearable
           size="small"
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
-      <el-form-item label="分配类型" prop="distributeType">
-        <el-select v-model="queryParams.distributeType" placeholder="请选择分配类型" clearable size="small">
-          <el-option label="类型1" :value="1" />
-          <el-option label="类型2" :value="2" />
+      <el-form-item label="广告商" prop="advertiserId">
+        <el-select v-model="queryParams.advertiserId" placeholder="请选择广告商" clearable size="small" filterable>
+          <el-option
+            v-for="item in advertiserOptions"
+            :key="item.id"
+            :label="item.advertiserName"
+            :value="item.id"
+          />
         </el-select>
       </el-form-item>
       <el-form-item>
@@ -32,7 +36,7 @@
           @click="handleAdd"
         >新增</el-button>
       </el-col>
-      <el-col :span="1.5">
+      <!-- <el-col :span="1.5">
         <el-button
           plain
           type="danger"
@@ -41,27 +45,20 @@
           :disabled="multiple"
           @click="handleDelete"
         >删除</el-button>
-      </el-col>
+      </el-col> -->
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="leadSourceList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="accountList" @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
-      <el-table-column label="ID" align="center" prop="id" width="80" />
-      <el-table-column label="来源名称" align="center" prop="sourceName" />
-      <el-table-column label="分配类型" align="center" prop="distributeType" width="120">
-        <template slot-scope="scope">
-          <span v-if="scope.row.distributeType === 1">类型1</span>
-          <span v-else-if="scope.row.distributeType === 2">类型2</span>
-          <span v-else>{{ scope.row.distributeType }}</span>
-        </template>
-      </el-table-column>
+      <el-table-column label="推广账户名称" align="center" prop="accountName" show-overflow-tooltip />
+      <el-table-column label="广告商名称" align="center" prop="advertiserName" show-overflow-tooltip />
       <el-table-column label="创建时间" align="center" prop="createTime" width="180">
         <template slot-scope="scope">
           <span>{{ parseTime(scope.row.createTime) }}</span>
         </template>
       </el-table-column>
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180">
+      <!-- <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
         <template slot-scope="scope">
           <el-button
             size="mini"
@@ -76,7 +73,7 @@
             @click="handleDelete(scope.row)"
           >删除</el-button>
         </template>
-      </el-table-column>
+      </el-table-column> -->
     </el-table>
 
     <pagination
@@ -87,32 +84,49 @@
       @pagination="getList"
     />
 
-    <!-- 添加或修改线索来源对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
-      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
-        <el-form-item label="来源名称" prop="sourceName">
-          <el-input v-model="form.sourceName" placeholder="请输入来源名称" />
-        </el-form-item>
-        <el-form-item label="分配类型" prop="distributeType">
-          <el-select v-model="form.distributeType" placeholder="请选择分配类型" style="width: 100%">
-            <el-option label="类型1" :value="1" />
-            <el-option label="类型2" :value="2" />
+    <!-- 添加或修改自定义推广账号对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+        <el-form-item label="广告商" prop="advertiserId">
+          <el-select 
+            v-model="form.advertiserId" 
+            placeholder="请选择广告商" 
+            style="width: 100%" 
+            filterable
+            @change="handleAdvertiserChange"
+            :disabled="isEdit"
+          >
+            <el-option
+              v-for="item in advertiserOptions"
+              :key="item.id"
+              :label="item.advertiserName"
+              :value="item.id"
+            />
           </el-select>
         </el-form-item>
+        <el-form-item label="推广账号名称" prop="accountName">
+          <el-input 
+            v-model="form.accountName" 
+            placeholder="请输入推广账号名称"
+            clearable
+            :disabled="isEdit"
+          />
+        </el-form-item>
       </el-form>
       <div slot="footer" class="dialog-footer">
-        <el-button type="primary" @click="submitForm">确 定</el-button>
         <el-button @click="cancel">取 消</el-button>
+        <el-button type="primary" @click="submitForm">确 定</el-button>
       </div>
     </el-dialog>
   </div>
 </template>
 
 <script>
-import { pageLeadSource, getLeadSource, addLeadSource, updateLeadSource, delLeadSource, batchDelLeadSource } from "@/api/adv/leadSource";
+import { pagePromotionAccount, getPromotionAccount, addPromotionAccount, updatePromotionAccount, delPromotionAccount, batchDelPromotionAccount } from "@/api/adv/promotionAccount";
+import { pageAdvertiser } from "@/api/adv/advertiser";
 
 export default {
-  name: "LeadSource",
+  name: "CustomPromotionAccount",
   data() {
     return {
       // 遮罩层
@@ -127,28 +141,35 @@ export default {
       showSearch: true,
       // 总条数
       total: 0,
-      // 线索来源表格数据
-      leadSourceList: [],
+      // 推广账号表格数据
+      accountList: [],
+      // 广告商选项
+      advertiserOptions: [],
       // 弹出层标题
       title: "",
       // 是否显示弹出层
       open: false,
+      // 是否为编辑模式
+      isEdit: false,
       // 查询参数
       queryParams: {
         current: 1,
         size: 10,
-        sourceName: undefined,
-        distributeType: undefined
+        custom: 2,
+        accountName: undefined,
+        advertiserId: undefined
       },
       // 表单参数
-      form: {},
+      form: {
+
+      },
       // 表单校验
       rules: {
-        sourceName: [
-          { required: true, message: "来源名称不能为空", trigger: "blur" }
+        advertiserId: [
+          { required: true, message: "请选择广告商", trigger: "change" }
         ],
-        distributeType: [
-          { required: true, message: "分配类型不能为空", trigger: "change" }
+        accountName: [
+          { required: true, message: "推广账号名称不能为空", trigger: "blur" }
         ]
       }
     };
@@ -157,11 +178,17 @@ export default {
     this.getList();
   },
   methods: {
-    /** 查询线索来源列表 */
+    /** 查询广告商选项(只加载自定义广告商custom=2) */
+    getAdvertiserOptions() {
+      pageAdvertiser({ pageNum: 1, pageSize: 1000, enabled: 1, custom: 2 }).then(response => {
+        this.advertiserOptions = response.data.records;
+      });
+    },
+    /** 查询推广账号列表 */
     getList() {
       this.loading = true;
-      pageLeadSource(this.queryParams).then(response => {
-        this.leadSourceList = response.data.records;
+      pagePromotionAccount(this.queryParams).then(response => {
+        this.accountList = response.data.records;
         this.total = response.data.total;
         this.loading = false;
       });
@@ -175,8 +202,19 @@ export default {
     reset() {
       this.form = {
         id: undefined,
-        sourceName: undefined,
-        distributeType: undefined
+        promotionType: "展示类",
+        advertiserId: undefined,
+        advertiserName: undefined,
+        configMode: 1,
+        accountName: undefined,
+        accountShortName: undefined,
+        apiSwitch: 2,
+        custom: 2,
+        appId: undefined,
+        appSecret: undefined,
+        adAccountId: undefined,
+        callbackUrl: undefined,
+        authUrl: undefined
       };
       this.resetForm("form");
     },
@@ -199,17 +237,21 @@ export default {
     /** 新增按钮操作 */
     handleAdd() {
       this.reset();
+      this.isEdit = false;
+      // 新增时加载广告商列表(只加载自定义广告商custom=2)
+      this.getAdvertiserOptions();
       this.open = true;
-      this.title = "添加线索来源";
+      this.title = "添加自定义推广账号";
     },
     /** 修改按钮操作 */
     handleUpdate(row) {
       this.reset();
+      this.isEdit = true;
       const id = row.id || this.ids[0];
-      getLeadSource(id).then(response => {
+      getPromotionAccount(id).then(response => {
         this.form = response.data;
         this.open = true;
-        this.title = "修改线索来源";
+        this.title = "修改自定义推广账号";
       });
     },
     /** 提交按钮 */
@@ -217,7 +259,7 @@ export default {
       this.$refs["form"].validate(valid => {
         if (valid) {
           if (this.form.id != undefined) {
-            updateLeadSource(this.form.id, this.form).then(response => {
+            updatePromotionAccount(this.form.id, this.form).then(response => {
               if (response.code === 200) {
                 this.msgSuccess("修改成功");
                 this.open = false;
@@ -225,7 +267,7 @@ export default {
               }
             });
           } else {
-            addLeadSource(this.form).then(response => {
+            addPromotionAccount(this.form).then(response => {
               if (response.code === 200) {
                 this.msgSuccess("新增成功");
                 this.open = false;
@@ -239,18 +281,30 @@ export default {
     /** 删除按钮操作 */
     handleDelete(row) {
       const ids = row.id ? [row.id] : this.ids;
-      this.$confirm('是否确认删除选中的线索来源?', "警告", {
+      this.$confirm('是否确认删除选中的推广账号?', "警告", {
         confirmButtonText: "确定",
         cancelButtonText: "取消",
         type: "warning"
       }).then(() => {
-        return batchDelLeadSource(ids);
+        return batchDelPromotionAccount(ids);
       }).then(() => {
         this.getList();
         this.msgSuccess("删除成功");
       }).catch(function() {});
+    },
+    /** 广告商变化时 */
+    handleAdvertiserChange(advertiserId) {
+      this.form.advertiserName = "";
+      if (advertiserId) {
+        const advertiser = this.advertiserOptions.find(item => item.id === advertiserId);
+        if (advertiser) {
+          this.form.advertiserName = advertiser.advertiserName;
+        }
+      }
     }
   }
 };
 </script>
 
+<style lang="scss" scoped>
+</style>

+ 12 - 8
src/views/adv/promotionAccount/index.vue

@@ -51,17 +51,16 @@
 
     <el-table border v-loading="loading" :data="accountList" @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
-      <el-table-column label="ID" align="center" prop="id" width="80" />
-      <el-table-column label="账户简称" align="center" prop="accountShortName" width="150" show-overflow-tooltip />
-      <el-table-column label="广告商名称" align="center" prop="advertiserName" width="150" show-overflow-tooltip />
+      <el-table-column label="账户简称" align="center" prop="accountShortName" min-width="150" show-overflow-tooltip />
+      <el-table-column label="广告商名称" align="center" prop="advertiserName" min-width="150" show-overflow-tooltip />
       <el-table-column label="读取方式" align="center" prop="apiSwitch" width="120">
         <template slot-scope="scope">
           <el-tag :type="scope.row.apiSwitch === 1 ? 'success' : 'info'" size="small">
-            {{ scope.row.apiSwitch === 1 ? 'API读取' : '手动录入' }}
+            {{ scope.row.apiSwitch === 1 ? 'API' : '手动录入' }}
           </el-tag>
         </template>
       </el-table-column>
-      <el-table-column label="广告主ID" align="center" prop="adAccountId" width="180" show-overflow-tooltip />
+      <el-table-column label="广告主ID" align="center" prop="adAccountId" min-width="180" show-overflow-tooltip />
       <el-table-column label="创建时间" align="center" prop="createTime" width="180">
         <template slot-scope="scope">
           <span>{{ parseTime(scope.row.createTime) }}</span>
@@ -256,11 +255,13 @@ export default {
       queryParams: {
         current: 1,
         size: 10,
+        custom:1,
         accountName: undefined,
         advertiserId: undefined
       },
       // 表单参数
-      form: {},
+      form: {
+      },
       // 表单校验
       rules: {
         promotionType: [
@@ -322,12 +323,12 @@ export default {
   },
   created() {
     this.getList();
-    this.getAdvertiserOptions();
+    // 移除自动加载广告商列表,改为新增时按需加载
   },
   methods: {
     /** 查询广告商选项 */
     getAdvertiserOptions() {
-      pageAdvertiser({ pageNum: 1, pageSize: 1000 }).then(response => {
+      pageAdvertiser({ pageNum: 1, pageSize: 1000, enabled: 1, custom: 1 }).then(response => {
         this.advertiserOptions = response.data.records;
       });
     },
@@ -356,6 +357,7 @@ export default {
         accountName: undefined,
         accountShortName: undefined,
         apiSwitch: 2,
+        custom: 1,
         appId: undefined,
         appSecret: undefined,
         adAccountId: undefined,
@@ -384,6 +386,8 @@ export default {
     handleAdd() {
       this.reset();
       this.isEdit = false;
+      // 新增时加载广告商列表(只加载线上广告商custom=1)
+      this.getAdvertiserOptions();
       this.open = true;
       this.title = "添加推广账号";
     },

+ 464 - 80
src/views/adv/site/index.vue

@@ -23,15 +23,8 @@
 
     <el-table border v-loading="loading" :data="siteList">
       <el-table-column label="ID" align="center" prop="id" width="80" />
-      <el-table-column label="站点名称" align="center" prop="siteName" width="150" show-overflow-tooltip />
-      <el-table-column label="站点地址" align="center" prop="siteUrl" width="180" show-overflow-tooltip />
+      <el-table-column label="站点名称" align="center" prop="siteName" min-width="150" show-overflow-tooltip />
       <el-table-column label="投放类型" align="center" prop="launchType" width="100" />
-      <el-table-column label="广告类型" align="center" prop="adType" width="120" />
-      <el-table-column label="投放广告商" align="center" prop="advertiserName" width="150" show-overflow-tooltip />
-      <el-table-column label="投放广告商账号" align="center" prop="promotionAccountName" width="150" show-overflow-tooltip />
-      <el-table-column label="投放页面" align="center" prop="launchPageName" width="150" show-overflow-tooltip />
-      <el-table-column label="来源" align="center" prop="sourceName" width="120" show-overflow-tooltip />
-      <el-table-column label="投放域名" align="center" prop="launchDomain" width="150" show-overflow-tooltip />
       <el-table-column label="配置回传" align="center" prop="configCallback" width="100">
         <template slot-scope="scope">
           <el-tag :type="scope.row.configCallback === 1 ? 'success' : 'info'" size="small">
@@ -39,14 +32,26 @@
           </el-tag>
         </template>
       </el-table-column>
-      <el-table-column label="回传账号" align="center" prop="callbackAccountName" width="150" show-overflow-tooltip />
+      <el-table-column label="站点状态" align="center" prop="status" width="100">
+        <template slot-scope="scope">
+          <el-tag :type="scope.row.status === 1 ? 'success' : 'info'" size="small">
+            {{ scope.row.status === 1 ? '启用' : '停用' }}
+          </el-tag>
+        </template>
+      </el-table-column>
       <el-table-column label="创建时间" align="center" prop="createTime" width="180">
         <template slot-scope="scope">
           <span>{{ parseTime(scope.row.createTime) }}</span>
         </template>
       </el-table-column>
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="280" fixed="right">
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="360" fixed="right">
         <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-view"
+            @click="handleDetail(scope.row)"
+          >详情</el-button>
           <el-button
             size="mini"
             type="text"
@@ -59,6 +64,12 @@
             icon="el-icon-data-line"
             @click="handleStatistics(scope.row)"
           >统计</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            :icon="scope.row.status === 1 ? 'el-icon-video-pause' : 'el-icon-video-play'"
+            @click="handleEnable(scope.row)"
+          >{{ scope.row.status === 1 ? '停用' : '启用' }}</el-button>
           <el-button
             size="mini"
             type="text"
@@ -84,6 +95,7 @@
               placeholder="请输入站点名称"
               prefix-icon="el-icon-notebook-2"
               clearable
+              :disabled="isDetail"
             />
           </el-form-item>
         </div>
@@ -100,6 +112,8 @@
               placeholder="请选择投放类型" 
               style="width: 100%"
               prefix-icon="el-icon-s-flag"
+              @change="handleLaunchTypeChange"
+              :disabled="isDetail"
             >
               <el-option
                 v-for="item in launchTypeOptions"
@@ -114,6 +128,7 @@
               v-model="form.adType" 
               placeholder="请选择广告类型" 
               style="width: 100%"
+              :disabled="isDetail"
             >
               <el-option
                 v-for="item in adTypeOptions"
@@ -134,10 +149,11 @@
           <el-form-item label="投放广告商" prop="advertiserId">
             <el-select 
               v-model="form.advertiserId" 
-              placeholder="请选择投放广告商" 
+              placeholder="请先选择投放类型" 
               style="width: 100%"
               @change="handleAdvertiserChange"
               filterable
+              :disabled="!form.launchType || isDetail"
             >
               <el-option
                 v-for="item in advertiserList"
@@ -154,7 +170,7 @@
               style="width: 100%"
               @change="handlePromotionAccountChange"
               filterable
-              :disabled="!form.advertiserId"
+              :disabled="!form.advertiserId || isDetail"
             >
               <el-option
                 v-for="item in promotionAccountList"
@@ -179,6 +195,7 @@
               style="width: 100%"
               @change="handleLaunchPageChange"
               filterable
+              :disabled="isDetail"
             >
               <el-option
                 v-for="item in landingPageTemplateList"
@@ -194,6 +211,7 @@
               placeholder="请选择投放域名" 
               style="width: 100%"
               filterable
+              :disabled="isDetail"
             >
               <el-option
                 v-for="item in launchDomainList"
@@ -203,22 +221,7 @@
               />
             </el-select>
           </el-form-item>
-          <el-form-item label="来源" prop="sourceId">
-            <el-select 
-              v-model="form.sourceId" 
-              placeholder="请选择来源" 
-              style="width: 100%"
-              @change="handleSourceChange"
-              filterable
-            >
-              <el-option
-                v-for="item in leadSourceList"
-                :key="item.id"
-                :label="item.sourceName"
-                :value="item.id"
-              />
-            </el-select>
-          </el-form-item>
+
         </div>
 
         <!-- 回传配置 -->
@@ -228,7 +231,7 @@
             <span>回传配置</span>
           </div>
           <el-form-item label="是否配置回传" prop="configCallback">
-            <el-radio-group v-model="form.configCallback">
+            <el-radio-group v-model="form.configCallback" @change="handleConfigCallbackChange" :disabled="isDetail">
               <el-radio 
                 v-for="item in configCallbackOptions"
                 :key="item.value"
@@ -249,20 +252,122 @@
               style="width: 100%"
               @change="handleCallbackAccountChange"
               filterable
+              :disabled="isDetail"
             >
               <el-option
                 v-for="item in callbackAccountList"
                 :key="item.id"
-                :label="item.callbackAccountName"
+                :label="item.accountName"
                 :value="item.id"
               />
             </el-select>
           </el-form-item>
+          
+          <!-- 转换类型配置区域 -->
+          <div v-if="form.configCallback === 1 && form.callbackAccountId" class="conversion-config-section">
+            <div class="config-header">
+              <span class="config-title">转换类型配置</span>
+              <el-button type="primary" size="small" icon="el-icon-plus" @click="addConversionEvent">添加事件</el-button>
+            </div>
+            
+            <div v-if="conversionEvents.length === 0" class="empty-tip">
+              暂无转换事件,请点击“添加事件”按钮添加
+            </div>
+            
+            <div v-for="(event, index) in conversionEvents" :key="index" class="conversion-event-item">
+              <div class="event-header">
+                <span class="event-title">事件{{ index + 1 }}</span>
+                <el-button 
+                  type="danger" 
+                  size="mini" 
+                  icon="el-icon-delete" 
+                  circle
+                  @click="removeConversionEvent(index)"
+                ></el-button>
+              </div>
+              
+              <el-form label-width="130px" style="margin-top: 10px;">
+                <el-form-item label="广告商转化类型">
+                  <el-select 
+                    v-model="event.advertiserEventType" 
+                    placeholder="请选择广告商转化类型" 
+                    style="width: 100%"
+                    @change="handleAdvertiserEventChange(index, $event)"
+                  >
+                    <el-option
+                      v-for="item in advertiserEventOptions"
+                      :key="item.eventType"
+                      :label="item.eventName"
+                      :value="item.eventType"
+                    />
+                  </el-select>
+                </el-form-item>
+                
+                <el-form-item label="回传数据类型">
+                  <el-select 
+                    v-model="event.systemEventType" 
+                    placeholder="请选择回传数据类型" 
+                    style="width: 100%"
+                    @change="handleSystemEventChange(index, $event)"
+                  >
+                    <el-option
+                      v-for="item in systemEventOptions"
+                      :key="item.eventType"
+                      :label="item.eventName"
+                      :value="item.eventType"
+                    />
+                  </el-select>
+                </el-form-item>
+              </el-form>
+            </div>
+          </div>
         </div>
       </el-form>
       <div slot="footer" class="dialog-footer">
         <el-button @click="cancel">取 消</el-button>
-        <el-button type="primary" @click="submitForm" icon="el-icon-check">确 定</el-button>
+        <el-button v-if="!isDetail" type="primary" @click="submitForm" icon="el-icon-check">确 定</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 站点详情对话框 -->
+    <el-dialog title="站点详情" :visible.sync="detailOpen" width="900px" append-to-body>
+      <el-descriptions :column="2" border>
+        <el-descriptions-item label="站点名称">{{ detail.siteName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="站点URL" :span="2">{{ detail.siteUrl || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="投放类型">{{ detail.launchType || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="广告类型">{{ detail.adType || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="运营部门">{{ detail.operationDept || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="负责人">{{ detail.manager || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="广告商名称" :span="2">{{ detail.advertiserName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="推广账户名称" :span="2">{{ detail.promotionAccountName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="投放页面名称" :span="2">{{ detail.launchPageName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="投放域名" :span="2">{{ detail.launchDomain || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="分配方式">
+          <span v-if="detail.distributeType === 1">手动分配</span>
+          <span v-else-if="detail.distributeType === 2">自动分配</span>
+          <span v-else>-</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="分配规则">{{ detail.distributeRule || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="来源名称">{{ detail.sourceName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="项目名称">{{ detail.projectName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="配置回传">
+          <el-tag :type="detail.configCallback === 1 ? 'success' : 'info'" size="small">
+            {{ detail.configCallback === 1 ? '是' : '否' }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="回传账号名称">{{ detail.callbackAccountName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="站点状态">
+          <el-tag :type="detail.status === 1 ? 'success' : 'info'" size="small">
+            {{ detail.status === 1 ? '启用' : '停用' }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="创建人">{{ detail.creator || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="创建时间">{{ parseTime(detail.createTime) || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="更新人">{{ detail.updater || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="更新时间" :span="2">{{ parseTime(detail.updateTime) || '-' }}</el-descriptions-item>
+      </el-descriptions>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="detailOpen = false">关 闭</el-button>
       </div>
     </el-dialog>
 
@@ -289,12 +394,12 @@
 </template>
 
 <script>
-import { listSite, getSite, addSite, updateSite, delSite, getSiteStatistics } from "@/api/adv/site";
+import { listSite, getSite, addSite, updateSite, delSite, getSiteStatistics, enableSite } from "@/api/adv/site";
 import { pageAdvertiser } from "@/api/adv/advertiser";
 import { pagePromotionAccount } from "@/api/adv/promotionAccount";
 import { pageTemplate } from "@/api/adv/landingPageTemplate";
-import { pageLeadSource } from "@/api/adv/leadSource";
-import { pageCallbackAccount } from "@/api/adv/callbackAccount";
+
+import { pageCallbackAccount, getCallbackAccount, queryEventType, saveEventType } from "@/api/adv/callbackAccount";
 import { pageDomain } from "@/api/adv/domain";
 
 export default {
@@ -309,6 +414,12 @@ export default {
       title: "",
       // 是否显示弹出层
       open: false,
+      // 是否为详情模式(只读)
+      isDetail: false,
+      // 是否显示详情对话框
+      detailOpen: false,
+      // 详情数据
+      detail: {},
       // 是否显示统计弹出层
       statisticsOpen: false,
       // 统计数据
@@ -317,8 +428,8 @@ export default {
       form: {},
       // 投放类型选项
       launchTypeOptions: [
-        { label: "线上投放", value: "线上投放" },
-        { label: "线下投放", value: "线下投放" }
+        { label: "线上投放", value: 1 },
+        { label: "线下投放", value: 2 }
       ],
       // 广告类型选项
       adTypeOptions: [
@@ -335,12 +446,17 @@ export default {
       promotionAccountList: [],
       // 落地页模板列表
       landingPageTemplateList: [],
-      // 线索来源列表
-      leadSourceList: [],
+
       // 回传账号列表
       callbackAccountList: [],
       // 投放域名列表
       launchDomainList: [],
+      // 转换事件列表
+      conversionEvents: [],
+      // 广告商事件选项(systemBuiltin='0')
+      advertiserEventOptions: [],
+      // 系统事件选项(systemBuiltin='1')
+      systemEventOptions: [],
       // 表单校验
       rules: {
         siteName: [
@@ -364,9 +480,7 @@ export default {
         launchPageId: [
           { required: true, message: "请选择投放页面", trigger: "change" }
         ],
-        sourceId: [
-          { required: true, message: "请选择来源", trigger: "change" }
-        ],
+
         launchDomain: [
           { required: true, message: "请选择投放域名", trigger: "change" }
         ],
@@ -419,8 +533,7 @@ export default {
         promotionAccountName: undefined,
         launchPageId: undefined,
         launchPageName: undefined,
-        sourceId: undefined,
-        sourceName: undefined,
+
         launchDomain: undefined,
         configCallback: 0,
         callbackAccountId: undefined,
@@ -428,24 +541,44 @@ export default {
       };
       this.promotionAccountList = [];
       this.launchDomainList = [];
+      this.conversionEvents = [];
+      this.advertiserEventOptions = [];
+      this.systemEventOptions = [];
       this.resetForm("form");
     },
     /** 新增按钮操作 */
     handleAdd() {
       this.reset();
-      this.loadSelectOptions();
+      // 不再预加载广告商列表,等待选择投放类型后再加载
+      this.loadCommonSelectOptions();
+      this.isDetail = false;
       this.open = true;
       this.title = "添加站点";
     },
     /** 修改按钮操作 */
     handleUpdate(row) {
       this.reset();
-      this.loadSelectOptions();
+      this.loadCommonSelectOptions();
+      this.isDetail = false;
       getSite(row.id).then(response => {
         this.form = response.data;
+        // 根据投放类型加载广告商列表
+        if (this.form.launchType) {
+          this.loadAdvertiserList(this.form.launchType);
+        }
         // 如果有广告商ID,加载对应的广告商账号列表
         if (this.form.advertiserId) {
           this.loadPromotionAccountList(this.form.advertiserId);
+          
+          // 如果配置了回传,加载回传账号列表
+          if (this.form.configCallback === 1) {
+            this.loadCallbackAccountList(this.form.advertiserId);
+            
+            // 如果有回传账号,加载事件类型和转换事件
+            if (this.form.callbackAccountId) {
+              this.loadEventTypesAndConversionEvents(this.form.advertiserId, this.form.callbackAccountId);
+            }
+          }
         }
         this.open = true;
         this.title = "修改站点";
@@ -453,32 +586,60 @@ export default {
     },
     /** 查看统计按钮操作 */
     handleStatistics(row) {
-      this.statistics = null;
-      this.statisticsOpen = true;
-      getSiteStatistics(row.id).then(response => {
-        this.statistics = response.data;
-      }).catch(() => {
-        this.statistics = null;
+      this.$router.push({
+        path: '/adv/statistics',
+        query: { siteId: row.id }
+      });
+    },
+    /** 详情按钮操作 */
+    handleDetail(row) {
+      this.detail = {};
+      this.detailOpen = true;
+      getSite(row.id).then(response => {
+        this.detail = response.data;
       });
     },
     /** 提交按钮 */
     submitForm() {
       this.$refs["form"].validate(valid => {
         if (valid) {
+          // 如果配置了回传且有转换事件,校验转换事件
+          if (this.form.configCallback === 1 && this.form.callbackAccountId && this.conversionEvents.length > 0) {
+            for (let i = 0; i < this.conversionEvents.length; i++) {
+              const event = this.conversionEvents[i];
+              if (!event.systemEventType || !event.advertiserEventType) {
+                this.msgError(`请完善事件${i + 1}的配置`);
+                return;
+              }
+            }
+          }
+          
           if (this.form.id != undefined) {
+            // 修改
             updateSite(this.form.id, this.form).then(response => {
               if (response.code === 200) {
-                this.msgSuccess("修改成功");
-                this.open = false;
-                this.getList();
+                // 如果配置了回传,保存转换事件
+                if (this.form.configCallback === 1 && this.form.callbackAccountId) {
+                  this.saveConversionEvents();
+                } else {
+                  this.msgSuccess("修改成功");
+                  this.open = false;
+                  this.getList();
+                }
               }
             });
           } else {
+            // 新增
             addSite(this.form).then(response => {
               if (response.code === 200) {
-                this.msgSuccess("新增成功");
-                this.open = false;
-                this.getList();
+                // 如果配置了回传,保存转换事件
+                if (this.form.configCallback === 1 && this.form.callbackAccountId) {
+                  this.saveConversionEvents();
+                } else {
+                  this.msgSuccess("新增成功");
+                  this.open = false;
+                  this.getList();
+                }
               }
             });
           }
@@ -498,9 +659,23 @@ export default {
         this.msgSuccess("删除成功");
       }).catch(function() {});
     },
+    /** 启用/停用按钮操作 */
+    handleEnable(row) {
+      const statusText = row.status === 1 ? '停用' : '启用';
+      this.$confirm(`是否确认${statusText}站点"${row.siteName}"?`, "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        return enableSite(row.id);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess(`${statusText}成功`);
+      }).catch(function() {});
+    },
     /** 加载下拉选项数据 */
     loadSelectOptions() {
-      // 加载广告商列表
+      // 加载广告商列表(已废弃,改用loadAdvertiserList)
       pageAdvertiser({ pageNum: 1, pageSize: 1000 }).then(response => {
         this.advertiserList = response.data.records;
       }).catch(error => {
@@ -516,20 +691,22 @@ export default {
         this.landingPageTemplateList = [];
       });
       
-      // 加载线索来源列表
-      pageLeadSource({ pageNum: 1, pageSize: 1000 }).then(response => {
-        this.leadSourceList = response.data.records;
+      // 加载域名列表
+      pageDomain({ pageNum: 1, pageSize: 1000, status: 1 }).then(response => {
+        this.launchDomainList = response.data.records;
       }).catch(error => {
-        console.error('加载线索来源失败:', error);
-        this.leadSourceList = [];
+        console.error('加载域名失败:', error);
+        this.launchDomainList = [];
       });
-      
-      // 加载回传账号列表
-      pageCallbackAccount({ pageNum: 1, pageSize: 1000 }).then(response => {
-        this.callbackAccountList = response.data.records;
+    },
+    /** 加载公共下拉选项数据(不包含广告商) */
+    loadCommonSelectOptions() {
+      // 加载落地页模板列表
+      pageTemplate({ pageNum: 1, pageSize: 1000, status: 1 }).then(response => {
+        this.landingPageTemplateList = response.data.records;
       }).catch(error => {
-        console.error('加载回传账号失败:', error);
-        this.callbackAccountList = [];
+        console.error('加载落地页模板失败:', error);
+        this.landingPageTemplateList = [];
       });
       
       // 加载域名列表
@@ -540,6 +717,47 @@ export default {
         this.launchDomainList = [];
       });
     },
+    /** 投放类型变化时 */
+    handleLaunchTypeChange(launchType) {
+      // 重置广告商相关数据
+      this.form.advertiserId = undefined;
+      this.form.advertiserName = "";
+      this.form.promotionAccountId = undefined;
+      this.form.promotionAccountName = "";
+      this.advertiserList = [];
+      this.promotionAccountList = [];
+      
+      // 重置回传账号相关数据
+      this.form.callbackAccountId = undefined;
+      this.form.callbackAccountName = "";
+      this.callbackAccountList = [];
+      this.conversionEvents = [];
+      this.advertiserEventOptions = [];
+      this.systemEventOptions = [];
+      
+      // 根据投放类型加载广告商列表
+      if (launchType) {
+        this.loadAdvertiserList(launchType);
+      }
+    },
+    /** 加载广告商列表 */
+    loadAdvertiserList(launchType) {
+      if (!launchType) return;
+      // launchType: 1=线上投放, 2=线下投放
+      // custom: 1=线上广告商, 2=自定义广告商
+      const customValue = launchType; // launchType值直接对应custom值
+      pageAdvertiser({ 
+        pageNum: 1, 
+        pageSize: 1000, 
+        enabled: 1,
+        custom: customValue 
+      }).then(response => {
+        this.advertiserList = response.data.records;
+      }).catch(error => {
+        console.error('加载广告商列表失败:', error);
+        this.advertiserList = [];
+      });
+    },
     /** 广告商变化时 */
     handleAdvertiserChange(advertiserId) {
       this.form.advertiserName = "";
@@ -547,6 +765,11 @@ export default {
       this.form.promotionAccountName = "";
       this.promotionAccountList = [];
       
+      // 重置回传账号相关数据
+      this.form.callbackAccountId = undefined;
+      this.form.callbackAccountName = "";
+      this.callbackAccountList = [];
+      
       if (advertiserId) {
         const advertiser = this.advertiserList.find(item => item.id === advertiserId);
         if (advertiser) {
@@ -554,6 +777,11 @@ export default {
         }
         // 加载对应的广告商账号列表
         this.loadPromotionAccountList(advertiserId);
+        
+        // 如果已选择配置回传,加载回传账号列表
+        if (this.form.configCallback === 1) {
+          this.loadCallbackAccountList(advertiserId);
+        }
       }
     },
     /** 加载广告商账号列表 */
@@ -587,25 +815,124 @@ export default {
         }
       }
     },
-    /** 线索来源变化时 */
-    handleSourceChange(sourceId) {
-      this.form.sourceName = "";
-      if (sourceId) {
-        const source = this.leadSourceList.find(item => item.id === sourceId);
-        if (source) {
-          this.form.sourceName = source.sourceName;
-        }
-      }
-    },
+
     /** 回传账号变化时 */
     handleCallbackAccountChange(callbackAccountId) {
       this.form.callbackAccountName = "";
+      this.conversionEvents = [];
+      this.advertiserEventOptions = [];
+      this.systemEventOptions = [];
+      
       if (callbackAccountId) {
         const account = this.callbackAccountList.find(item => item.id === callbackAccountId);
         if (account) {
-          this.form.callbackAccountName = account.callbackAccountName;
+          this.form.callbackAccountName = account.accountName;
+          // 加载事件类型和转换事件
+          this.loadEventTypesAndConversionEvents(this.form.advertiserId, callbackAccountId);
         }
       }
+    },
+    /** 配置回传变化时 */
+    handleConfigCallbackChange(value) {
+      // 重置回传账号
+      this.form.callbackAccountId = undefined;
+      this.form.callbackAccountName = "";
+      this.callbackAccountList = [];
+      this.conversionEvents = [];
+      this.advertiserEventOptions = [];
+      this.systemEventOptions = [];
+      
+      // 如果选择配置回传,且已选择广告商,加载回传账号列表
+      if (value === 1 && this.form.advertiserId) {
+        this.loadCallbackAccountList(this.form.advertiserId);
+      }
+    },
+    /** 加载回传账号列表 */
+    loadCallbackAccountList(advertiserId) {
+      if (!advertiserId) return;
+      pageCallbackAccount({ pageNum: 1, pageSize: 1000, advertiserId: advertiserId }).then(response => {
+        this.callbackAccountList = response.data.records;
+      }).catch(error => {
+        console.error('加载回传账号列表失败:', error);
+        this.callbackAccountList = [];
+      });
+    },
+    /** 加载事件类型和转换事件 */
+    loadEventTypesAndConversionEvents(advertiserId, callbackAccountId) {
+      // 加载事件类型选项
+      queryEventType(advertiserId).then(response => {
+        const eventTypes = response.data || [];
+        // systemBuiltin='0' 为广告商事件
+        this.advertiserEventOptions = eventTypes.filter(item => item.systemBuiltin === '0');
+        // systemBuiltin='1' 为系统事件
+        this.systemEventOptions = eventTypes.filter(item => item.systemBuiltin === '1');
+      }).catch(error => {
+        console.error('加载事件类型失败:', error);
+      });
+      
+      // 加载已有的转换事件
+      getCallbackAccount(callbackAccountId).then(response => {
+        const account = response.data;
+        this.parseConversionEvents(account.conversionEvent);
+      }).catch(error => {
+        console.error('加载回传账号详情失败:', error);
+      });
+    },
+    /** 解析转换事件 */
+    parseConversionEvents(conversionEvent) {
+      try {
+        if (conversionEvent && typeof conversionEvent === 'string') {
+          this.conversionEvents = JSON.parse(conversionEvent);
+        } else if (Array.isArray(conversionEvent)) {
+          this.conversionEvents = conversionEvent;
+        } else {
+          this.conversionEvents = [];
+        }
+      } catch (error) {
+        console.error('解析转换事件失败:', error);
+        this.conversionEvents = [];
+      }
+    },
+    /** 添加转换事件 */
+    addConversionEvent() {
+      this.conversionEvents.push({
+        systemEventType: '',
+        systemEventTypeName: '',
+        advertiserEventType: '',
+        advertiserEventName: ''
+      });
+    },
+    /** 删除转换事件 */
+    removeConversionEvent(index) {
+      this.conversionEvents.splice(index, 1);
+    },
+    /** 广告商事件变化 */
+    handleAdvertiserEventChange(index, eventType) {
+      const event = this.advertiserEventOptions.find(item => item.eventType === eventType);
+      if (event) {
+        this.conversionEvents[index].advertiserEventName = event.eventName;
+      }
+    },
+    /** 系统事件变化 */
+    handleSystemEventChange(index, eventType) {
+      const event = this.systemEventOptions.find(item => item.eventType === eventType);
+      if (event) {
+        this.conversionEvents[index].systemEventTypeName = event.eventName;
+      }
+    },
+    /** 保存转换事件 */
+    saveConversionEvents() {
+      // 调用保存接口
+      saveEventType(this.form.callbackAccountId, this.conversionEvents).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess('保存成功');
+          this.open = false;
+          this.getList();
+        }
+      }).catch(error => {
+        console.error('保存失败:', error);
+        this.msgError('保存失败');
+      });
     }
   }
 };
@@ -869,5 +1196,62 @@ export default {
     }
   }
 }
+
+// 转换类型配置区域样式
+.conversion-config-section {
+  margin-top: 20px;
+  padding: 20px;
+  background: #f8f9fa;
+  border-radius: 8px;
+  border: 1px solid #e8e8e8;
+  
+  .config-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 15px;
+    
+    .config-title {
+      font-size: 15px;
+      font-weight: 600;
+      color: #303133;
+    }
+  }
+  
+  .empty-tip {
+    text-align: center;
+    padding: 30px 0;
+    color: #909399;
+    font-size: 14px;
+  }
+}
+
+.conversion-event-item {
+  margin-bottom: 15px;
+  padding: 15px;
+  border: 1px solid #e8e8e8;
+  border-radius: 6px;
+  background-color: #fff;
+  transition: all 0.3s ease;
+  
+  &:hover {
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+  }
+  
+  .event-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 10px;
+    padding-bottom: 10px;
+    border-bottom: 1px solid #f0f0f0;
+    
+    .event-title {
+      font-weight: 600;
+      font-size: 14px;
+      color: #303133;
+    }
+  }
+}
 </style>
 

+ 447 - 0
src/views/adv/statistics/index.vue

@@ -0,0 +1,447 @@
+<template>
+  <div class="app-container statistics-container">
+    <!-- 搜索筛选 -->
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" class="search-form">
+      <el-form-item label="站点" prop="siteId">
+        <el-select
+          v-model="queryParams.siteId"
+          placeholder="请选择站点"
+          clearable
+          size="small"
+          filterable
+        >
+          <el-option
+            v-for="item in siteList"
+            :key="item.id"
+            :label="item.siteName"
+            :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 工具栏 -->
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          plain
+          type="success"
+          icon="el-icon-refresh"
+          size="mini"
+          @click="getList"
+        >刷新</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <!-- 数据表格 -->
+    <el-table border v-loading="loading" :data="statisticsList" class="stats-table">
+      <el-table-column label="站点ID" align="center" prop="siteId" width="80" />
+      <el-table-column label="站点名称" align="center" prop="siteName" min-width="150" show-overflow-tooltip />
+      
+      <!-- 流量数据 -->
+      <el-table-column label="流量数据" align="center">
+        <el-table-column label="PV" align="center" prop="pv" width="100">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.pv || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="UV" align="center" prop="uv" width="100">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.uv || 0 }}</span>
+          </template>
+        </el-table-column>
+      </el-table-column>
+
+      <!-- 广告数据 -->
+      <el-table-column label="广告数据" align="center">
+        <el-table-column label="展示数" align="center" prop="impressionCount" width="100">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.impressionCount || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="点击数" align="center" prop="clickCount" width="100">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.clickCount || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="点击率(%)" align="center" prop="clickRate" width="100">
+          <template slot-scope="scope">
+            <span class="rate-text">{{ scope.row.clickRate || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="平均点击单价" align="center" prop="avgClickPrice" width="120">
+          <template slot-scope="scope">
+            <span class="money-text">¥{{ scope.row.avgClickPrice || 0 }}</span>
+          </template>
+        </el-table-column>
+      </el-table-column>
+
+      <!-- 成本数据 -->
+      <el-table-column label="成本数据" align="center">
+        <el-table-column label="账面花费" align="center" prop="accountCost" width="120">
+          <template slot-scope="scope">
+            <span class="money-text">¥{{ scope.row.accountCost || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="实际花费" align="center" prop="actualCost" width="120">
+          <template slot-scope="scope">
+            <span class="money-text primary">¥{{ scope.row.actualCost || 0 }}</span>
+          </template>
+        </el-table-column>
+      </el-table-column>
+
+      <!-- 转化数据 -->
+      <el-table-column label="转化数据" align="center">
+        <el-table-column label="名片数" align="center" prop="cardCount" width="100">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.cardCount || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="名片获取率(%)" align="center" prop="cardAcquireRate" width="120">
+          <template slot-scope="scope">
+            <span class="rate-text">{{ scope.row.cardAcquireRate || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="名片获取成本" align="center" prop="cardAcquireCost" width="120">
+          <template slot-scope="scope">
+            <span class="money-text">¥{{ scope.row.cardAcquireCost || 0 }}</span>
+          </template>
+        </el-table-column>
+      </el-table-column>
+
+      <!-- 企微数据 -->
+      <el-table-column label="企微数据" align="center">
+        <el-table-column label="企微添加人数" align="center" prop="wechatAddCount" width="120">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.wechatAddCount || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="企微添加率(%)" align="center" prop="wechatAddRate" width="120">
+          <template slot-scope="scope">
+            <span class="rate-text">{{ scope.row.wechatAddRate || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="企微添加成本" align="center" prop="wechatAddCost" width="120">
+          <template slot-scope="scope">
+            <span class="money-text">¥{{ scope.row.wechatAddCost || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="企微加群人数" align="center" prop="wechatGroupCount" width="120">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.wechatGroupCount || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="企微加群率(%)" align="center" prop="wechatGroupRate" width="120">
+          <template slot-scope="scope">
+            <span class="rate-text">{{ scope.row.wechatGroupRate || 0 }}</span>
+          </template>
+        </el-table-column>
+      </el-table-column>
+
+      <!-- ROI与销售 -->
+      <el-table-column label="效果数据" align="center">
+        <el-table-column label="ROI" align="center" prop="roi" width="100">
+          <template slot-scope="scope">
+            <el-tag :type="getRoiType(scope.row.roi)" size="small">
+              {{ scope.row.roi || 0 }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="销售额" align="center" prop="salesAmount" width="120">
+          <template slot-scope="scope">
+            <span class="money-text success">¥{{ scope.row.salesAmount || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="报名成功人数" align="center" prop="registerSuccessCount" width="120">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.registerSuccessCount || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="报名成功加微率(%)" align="center" prop="registerWechatRate" width="140">
+          <template slot-scope="scope">
+            <span class="rate-text">{{ scope.row.registerWechatRate || 0 }}</span>
+          </template>
+        </el-table-column>
+      </el-table-column>
+
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180" fixed="right">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="操作" align="center" width="120" fixed="right">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-view"
+            @click="handleDetail(scope.row)"
+          >详情</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.current"
+      :limit.sync="queryParams.size"
+      @pagination="getList"
+    />
+
+    <!-- 详情对话框 -->
+    <el-dialog title="统计详情" :visible.sync="detailOpen" width="900px" append-to-body class="detail-dialog">
+      <el-descriptions :column="2" border>
+        <el-descriptions-item label="站点ID">{{ detail.siteId || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="站点名称">{{ detail.siteName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="PV">{{ detail.pv || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="UV">{{ detail.uv || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="展示数">{{ detail.impressionCount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="点击数">{{ detail.clickCount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="点击率">{{ detail.clickRate || 0 }}%</el-descriptions-item>
+        <el-descriptions-item label="平均点击单价">¥{{ detail.avgClickPrice || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="账面花费">¥{{ detail.accountCost || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="实际花费">¥{{ detail.actualCost || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="名片数">{{ detail.cardCount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="名片获取率">{{ detail.cardAcquireRate || 0 }}%</el-descriptions-item>
+        <el-descriptions-item label="名片获取成本">¥{{ detail.cardAcquireCost || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="企微添加人数">{{ detail.wechatAddCount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="企微添加率">{{ detail.wechatAddRate || 0 }}%</el-descriptions-item>
+        <el-descriptions-item label="企微添加成本">¥{{ detail.wechatAddCost || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="企微加群人数">{{ detail.wechatGroupCount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="企微加群率">{{ detail.wechatGroupRate || 0 }}%</el-descriptions-item>
+        <el-descriptions-item label="报名成功人数">{{ detail.registerSuccessCount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="报名成功加微率">{{ detail.registerWechatRate || 0 }}%</el-descriptions-item>
+        <el-descriptions-item label="ROI">
+          <el-tag :type="getRoiType(detail.roi)" size="medium">{{ detail.roi || 0 }}</el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="销售额">¥{{ detail.salesAmount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="创建时间">{{ parseTime(detail.createTime) }}</el-descriptions-item>
+        <el-descriptions-item label="更新时间">{{ parseTime(detail.updateTime) }}</el-descriptions-item>
+      </el-descriptions>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="detailOpen = false">关 闭</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { pageSiteStatistics, getSiteStatistics } from "@/api/adv/siteStatistics";
+import { listSite } from "@/api/adv/site";
+
+export default {
+  name: "SiteStatistics",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 统计列表
+      statisticsList: [],
+      // 站点列表
+      siteList: [],
+      // 详情对话框
+      detailOpen: false,
+      // 详情数据
+      detail: {},
+      // 查询参数
+      queryParams: {
+        current: 1,
+        size: 10,
+        siteId: undefined
+      }
+    };
+  },
+  created() {
+    // 从 URL参数获取站点ID
+    const siteId = this.$route.query.siteId;
+    if (siteId) {
+      this.queryParams.siteId = parseInt(siteId);
+    }
+    this.getSiteList();
+    this.getList();
+  },
+  methods: {
+    /** 查询站点列表 */
+    getSiteList() {
+      listSite().then(response => {
+        this.siteList = response.data || [];
+      });
+    },
+    /** 查询统计列表 */
+    getList() {
+      this.loading = true;
+      pageSiteStatistics(this.queryParams).then(response => {
+        this.statisticsList = response.data.records || [];
+        this.total = response.data.total || 0;
+        this.loading = false;
+      }).catch(() => {
+        this.loading = false;
+      });
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.current = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    /** 详情按钮操作 */
+    handleDetail(row) {
+      getSiteStatistics(row.id).then(response => {
+        this.detail = response.data;
+        this.detailOpen = true;
+      });
+    },
+    /** 获取ROI标签类型 */
+    getRoiType(roi) {
+      const value = parseFloat(roi || 0);
+      if (value >= 3) return 'success';
+      if (value >= 2) return 'warning';
+      if (value >= 1) return 'info';
+      return 'danger';
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.statistics-container {
+  padding: 20px;
+}
+
+// 搜索表单样式
+.search-form {
+  background: #fff;
+  padding: 20px;
+  border-radius: 8px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+  margin-bottom: 20px;
+}
+
+// 表格样式
+.stats-table {
+  border-radius: 8px;
+  overflow: hidden;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+  
+  .number-text {
+    color: #409eff;
+    font-weight: 500;
+  }
+  
+  .rate-text {
+    color: #e6a23c;
+    font-weight: 500;
+  }
+  
+  .money-text {
+    color: #f56c6c;
+    font-weight: 500;
+    
+    &.primary {
+      color: #409eff;
+    }
+    
+    &.success {
+      color: #67c23a;
+    }
+  }
+  
+  ::v-deep .el-table__header {
+    th {
+      background-color: #f5f7fa;
+      color: #606266;
+      font-weight: 600;
+      font-size: 13px;
+    }
+  }
+  
+  ::v-deep .el-table__row {
+    transition: all 0.3s ease;
+    
+    &:hover {
+      background-color: rgba(102, 126, 234, 0.05);
+    }
+  }
+}
+
+// 工具栏样式
+.mb8 {
+  margin-bottom: 15px;
+  
+  .el-button {
+    border-radius: 6px;
+    padding: 10px 20px;
+    font-weight: 500;
+    transition: all 0.3s ease;
+    
+    &.el-button--success {
+      &:hover {
+        transform: translateY(-2px);
+        box-shadow: 0 4px 12px rgba(103, 194, 58, 0.4);
+      }
+    }
+  }
+}
+
+// 详情对话框样式
+::v-deep .detail-dialog {
+  .el-dialog__header {
+    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+    padding: 20px;
+    margin: 0;
+    border-radius: 4px 4px 0 0;
+    
+    .el-dialog__title {
+      color: #fff;
+      font-size: 18px;
+      font-weight: 500;
+    }
+    
+    .el-dialog__headerbtn .el-dialog__close {
+      color: #fff;
+      font-size: 20px;
+      
+      &:hover {
+        color: #f0f0f0;
+      }
+    }
+  }
+  
+  .el-dialog__body {
+    padding: 25px 30px;
+  }
+  
+  .el-descriptions {
+    ::v-deep .el-descriptions-item__label {
+      font-weight: 600;
+      color: #606266;
+      background-color: #fafafa;
+    }
+  }
+}
+
+.dialog-footer {
+  text-align: right;
+  
+  .el-button {
+    padding: 10px 24px;
+    border-radius: 6px;
+    font-weight: 500;
+  }
+}
+</style>

+ 4 - 13
src/views/adv/trackingLink/index.vue

@@ -51,18 +51,9 @@
     <el-table border v-loading="loading" :data="linkList">
       <el-table-column label="ID" align="center" prop="id" width="80" />
       <el-table-column label="链接名称" align="center" prop="trackingName" show-overflow-tooltip />
-      <el-table-column label="广告商" align="center" prop="advertiserId" width="200">
-        <template slot-scope="scope">
-          <span>{{ getAdvertiserName(scope.row.advertiserId) }}</span>
-        </template>
-      </el-table-column>
+      <el-table-column label="广告商" align="center" prop="advertiserName" width="200" show-overflow-tooltip />
       <el-table-column label="推广类型" align="center" prop="promotionType" width="120" />
       <el-table-column label="监测链接" align="center" prop="trackingUrl" show-overflow-tooltip min-width="200" />
-      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
-        <template slot-scope="scope">
-          <span>{{ parseTime(scope.row.createTime) }}</span>
-        </template>
-      </el-table-column>
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150">
         <template slot-scope="scope">
           <el-button
@@ -94,13 +85,13 @@
       <el-descriptions :column="1" border v-if="detail">
         <el-descriptions-item label="链接ID">{{ detail.id }}</el-descriptions-item>
         <el-descriptions-item label="链接名称">{{ detail.trackingName }}</el-descriptions-item>
-        <el-descriptions-item label="广告商">{{ getAdvertiserName(detail.advertiserId) }}</el-descriptions-item>
+        <el-descriptions-item label="广告商">{{ detail.advertiserName || '-' }}</el-descriptions-item>
         <el-descriptions-item label="推广类型">{{ detail.promotionType }}</el-descriptions-item>
         <el-descriptions-item label="监测链接">
           <el-link :href="detail.trackingUrl" target="_blank" type="primary">{{ detail.trackingUrl }}</el-link>
         </el-descriptions-item>
-        <el-descriptions-item label="创建时间">{{ parseTime(detail.createTime) }}</el-descriptions-item>
-        <el-descriptions-item label="更新时间">{{ parseTime(detail.updateTime) }}</el-descriptions-item>
+        <el-descriptions-item label="创建时间">{{ detail.createTime }}</el-descriptions-item>
+        <el-descriptions-item label="更新时间">{{ detail.updateTime }}</el-descriptions-item>
       </el-descriptions>
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="handleCopy(detail)">复制链接</el-button>