Pārlūkot izejas kodu

Merge branch 'dev-cmn' of https://s-20coaj910c.zht2.com/cmy/data-marketing-platform into dev-ly

luoy 2 dienas atpakaļ
vecāks
revīzija
f1181687d3

+ 15 - 0
src/api/marketing/apps.ts

@@ -174,4 +174,19 @@ export const getStatSecondPage = (params: Object) => {
 		method: 'get',
 		params,
 	});
+};
+
+/**
+ * 分页查询营销推送记录
+ * @param ruleName	规则名称
+ * @param triggerCondition	触发条件
+ * @param pageNum	页码
+ * @param pageSize	每页条数
+ */
+export const getPushPage = (data: Object) => {
+	return request({
+		url: '/marketing/push/page',
+		method: 'post',
+		data,
+	});
 };

+ 15 - 5
src/views/count/engagement/frequency/Month.vue

@@ -14,7 +14,8 @@
           </span>
         </el-tooltip>
       </div>
-      <FormEcharts v-if="type === 'date'" @dateChange="handleDateChange" :averageValue="averageValue" :type="type" :form="form" />
+      <FormEcharts v-if="type === 'date'" @dateChange="handleDateChange" :averageValue="averageValue" :type="type"
+        :form="form" />
       <div class="chart-container">
         <BarChart :data="usageTimeData" :is-multi-series="true" :series-names="seriesNames" title="多系列对比柱状图"
           style="height: 300px;" />
@@ -30,6 +31,7 @@ import BarChart from '/@/views/count/components/BarChart.vue';
 import ExportToCSV from '/@/views/count/components/ExportToCSV.vue';
 import FormEcharts from '../components/FormEcharts.vue';
 import Svg from '/@/components/Svg.vue';
+import { formatDate } from '/@/utils/formatTime';
 
 const props = defineProps({
   type: {
@@ -78,13 +80,21 @@ const usageTimeData = ref([
 const seriesNames = ref<string[]>([]);
 
 const handleDateChange = (val: string[]) => {
-  // 强制触发响应式更新
-  seriesNames.value = [...val];
+  let uniqueDates: string[] = [];
+  val.forEach((date: string) => {
+    const dateObj = new Date(date);
+    const year = dateObj.getFullYear();
+    const month = dateObj.getMonth();
+    const monthStartDate = new Date(year, month, 1);
+    const monthEndDate = new Date(year, month + 1, 0);
 
-  // 创建新的数据对象以确保响应式更新
+    uniqueDates.push(`${formatDate(new Date(monthStartDate), 'YYYY-mm-dd')}-${formatDate(new Date(monthEndDate), 'YYYY-mm-dd')}`);
+  });
+  uniqueDates = [...new Set(uniqueDates)];
+  seriesNames.value = [...uniqueDates];
   const newData = usageTimeData.value.map((item: any) => ({
     ...item,
-    values: val.map((date: string) => {
+    values: uniqueDates.map((date: string) => {
       const value = Math.floor(Math.random() * 100);
       return {
         value: value,

+ 10 - 4
src/views/count/engagement/frequency/Week.vue

@@ -78,13 +78,19 @@ const usageTimeData = ref([
 const seriesNames = ref<string[]>([]);
 
 const handleDateChange = (val: string[]) => {
-  // 强制触发响应式更新
-  seriesNames.value = [...val];
+  let uniqueDates: string[] = [];
+  val.forEach((date: string) => {
+    const dateObj = new Date(date);
+    const weekStartDate = new Date(dateObj.setDate(dateObj.getDate() - dateObj.getDay()));
+    const weekEndDate = new Date(dateObj.setDate(dateObj.getDate() + 6));
+    uniqueDates.push(`${weekStartDate.toISOString().split('T')[0]}-${weekEndDate.toISOString().split('T')[0]}`);
+  });
 
-  // 创建新的数据对象以确保响应式更新
+  uniqueDates = [...new Set(uniqueDates)];
+  seriesNames.value = [...uniqueDates];
   const newData = usageTimeData.value.map((item: any) => ({
     ...item,
-    values: val.map((date: string) => {
+    values: uniqueDates.map((date: string) => {
       const value = Math.floor(Math.random() * 100);
       return {
         value: value,

+ 11 - 10
src/views/marketing/data/dataInfoModal.vue

@@ -86,16 +86,17 @@ const openDialog = (
   updateTime.value = options?.updateTime || null
   triggerKeyword.value = options?.triggerKeyword || []
 
-  if(triggerKeyword.value.length > 0) {
-    // 转义正则表达式特殊字符,避免关键字中包含特殊字符导致错误
-    const escapedKeywords = triggerKeyword.value.map(keyword => 
-      keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
-    )
-    const keywordRegex = new RegExp(`(${escapedKeywords.join('|')})`, 'gi')
-    content.value = richContent.replace(keywordRegex, '<span class="trigger-keyword-item">$1</span>')
-  } else {
-    content.value = richContent
-  }
+  // if(triggerKeyword.value.length > 0) {
+  //   // 转义正则表达式特殊字符,避免关键字中包含特殊字符导致错误
+  //   const escapedKeywords = triggerKeyword.value.map(keyword => 
+  //     keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+  //   )
+  //   const keywordRegex = new RegExp(`(${escapedKeywords.join('|')})`, 'gi')
+  //   content.value = richContent.replace(keywordRegex, '<span class="trigger-keyword-item">$1</span>')
+  // } else {
+  //   content.value = richContent
+  // }
+  content.value = richContent
 
   // 模拟加载延迟
   setTimeout(() => {

+ 87 - 73
src/views/marketing/record/index.vue

@@ -6,8 +6,8 @@
           <el-form-item :label="'规则名称'" prop="ruleName">
             <el-input :placeholder="'请输入规则名称'" v-model="state.queryForm.ruleName" />
           </el-form-item>
-          <el-form-item :label="'触发信息'" prop="triggerInfo">
-            <el-input :placeholder="'请输入触发信息'" v-model="state.queryForm.triggerInfo" />
+          <el-form-item :label="'触发信息'" prop="triggerCondition">
+            <el-input :placeholder="'请输入触发信息'" v-model="state.queryForm.triggerCondition" />
           </el-form-item>
           <el-form-item>
             <el-button @click="query" class="ml10" icon="search" type="primary">
@@ -26,56 +26,46 @@
         <el-table-column :formatter="statusFormatter" :label="'推送内容'" prop="pushContent" min-width="300"
           show-overflow-tooltip>
           <template #default="{ row }">
-            <div style="display: flex; align-items: center; justify-content: center;">
-              <el-image style="width: 100px; height: 50px;" :src="lockScreen" :fit="'cover'" />
-              <div style="margin-left: 10px; width: 200px; text-align: left; word-break: break-all; text-wrap: auto; ">
-                http://www.baidu.comwww.baidu.com
-              </div>
-            </div>
+            <el-image v-if="row.pushType" :src="row.pushContent" style="width: 100px; height: 100px;" />
+            <div v-else>{{ row.pushContent }}</div>
+          </template>
+        </el-table-column>
+        <el-table-column :formatter="statusFormatter" :label="'推送方式'" prop="pushType" min-width="100"
+          show-overflow-tooltip>
+          <template #default="{ row }">
+              {{ pushMode.filter(item => item.value == row.pushAction)[0]?.label }}
           </template>
         </el-table-column>
         <el-table-column :formatter="statusFormatter" :label="'推送频率'" prop="pushFrequency" min-width="120"
           show-overflow-tooltip>
         </el-table-column>
-        <el-table-column :formatter="statusFormatter" :label="'推送时间'" prop="pushTime" min-width="200"
+        <el-table-column :formatter="statusFormatter" :label="'推送时间'" prop="createTime" min-width="200"
           show-overflow-tooltip></el-table-column>
         <el-table-column :formatter="statusFormatter" :label="'推送状态'" prop="pushStatus" min-width="120"
           show-overflow-tooltip>
           <template #default="{ row }">
-             已推送
+             {{ row.pushStatus ? '已推送' : '未推送' }}
           </template>
         </el-table-column>
-        <el-table-column :formatter="statusFormatter" :label="''" prop="triggerInfo" min-width="200" >
-          <template #header>
-            <div class="header-wrapper">
-              <span>触发信息</span>
-              <el-popover placement="top" trigger="hover" content="点击查看具体设备信息" :width="200">
-                <template #reference>
-                  <el-icon class="header-icon">
-                    <QuestionFilled />
-                  </el-icon>
-                </template>
-              </el-popover>
-            </div>
-          </template>
+        <el-table-column :formatter="statusFormatter" :label="'触发信息'" prop="triggerCondition" min-width="250" >
           <template #default="{ row }">
-            <div class="trigger-info" @click="showTriggerInfo(row)">
-              <div 
-                v-if="row.triggerInfo" 
-                class="rich-content-display"
-                v-html="row.triggerInfo"
-              ></div>
+            <div style="width: 100%; text-align: left;">
+              关键字:{{ row._keywords?.length ? row._keywords.join(', ') : '--' }}
+              <br v-if="row._ip" />
+              {{ row._ip ? 'IP:' + row._ip : '' }}
+              <br v-if="row._domain" />
+              {{ row._domain ? '域名:' + row._domain : '' }}
             </div>
           </template>
         </el-table-column>
-        <el-table-column :formatter="statusFormatter" :label="'触发记录'" prop="triggerRecord" min-width="150"
+        <el-table-column :formatter="statusFormatter" :label="'触发记录'" prop="pushDetail" min-width="250"
           show-overflow-tooltip>
           <template #default="{ row }">
-            203.104.10.10
+            <div class="trigger-info" @click="showTriggerInfo(row)">
+              {{ row.pushDetail }}
+            </div>
           </template>
         </el-table-column>
-        <el-table-column :formatter="statusFormatter" :label="'触发时间'" prop="triggerTime" min-width="150"
-          show-overflow-tooltip></el-table-column>
       </el-table>
       <pagination @current-change="currentChangeHandle" @size-change="sizeChangeHandle" v-bind="state.pagination" />
     </div>
@@ -85,25 +75,61 @@
 </template>
 
 <script lang="ts" name="marketingApps" setup>
-// import { pageList } from '/@/api/marketing/apps';
+import { getPushPage } from '/@/api/marketing/apps';
 import { BasicTableProps, useTable } from '/@/hooks/table';
 import { useI18n } from 'vue-i18n';
-import { ref } from 'vue'
+import { ref, reactive, onMounted, defineAsyncComponent } from 'vue'
 const dataInfoModal = defineAsyncComponent(() => import('/@/views/marketing/data/dataInfoModal.vue'));
 import { ElImage } from 'element-plus';
 import lockScreen from '/@/assets/lockScreen.png';
+import { fetchItemList } from '/@/api/admin/dict';
 
 const { t } = useI18n();
 
+const getDataListWithProcess = (params: any): Promise<any> => {
+  return new Promise((resolve, reject) => {
+    getPushPage(params).then((res: any) => {
+      console.log(res, 'res');
+      try {
+        if (res.data && res.data.records && res.data.records.length > 0) {
+          res.data.records = res.data.records.map((item: any) => {
+            let parsedData: any = null;
+            try {
+              if (item.triggerCondition) {
+                parsedData = JSON.parse(item.triggerCondition);
+              }
+            } catch (error) {
+              console.warn('解析触发器条件失败:', item.triggerCondition, error);
+            }
+            
+            return {
+              ...item,
+              _keywords: parsedData?.keywords || [],
+              _ip: parsedData?.ip || '',
+              _domain: parsedData?.domain || ''
+            };
+          });
+        }
+        resolve(res);
+      } catch (error: any) {
+        console.error('数据处理失败:', error);
+        reject(error);
+      }
+    }).catch((error: any) => {
+      reject(error);
+    });
+  });
+};
+
 // 定义变量内容
 const tableRef = ref();
 const queryRef = ref();
 const state: BasicTableProps = reactive<BasicTableProps>({
-  // pageList: pageList,
+  pageList: getDataListWithProcess,
   createdIsNeed: false,
   queryForm: {
     ruleName: '',
-    triggerInfo: '',
+    triggerCondition: '',
   },
   pagination: {
     current: 1,
@@ -112,10 +138,11 @@ const state: BasicTableProps = reactive<BasicTableProps>({
     pageSizes: [5, 10, 20, 50, 100]
   },
 });
+const pushMode = ref<any>([]);
 
 interface SourceData {
   ruleName: string;
-  triggerInfo: string;
+  triggerCondition: string;
   triggerTime: string;
   pushStatus: string;
   pushContent: string;
@@ -124,24 +151,19 @@ interface SourceData {
 }
 
 const richContentDialogRef = ref();
-const sourceData = ref<SourceData[]>([]);
-
 const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state);
 
 // 搜索事件
 const query = (refresh: boolean = false) => {
   state.dataList = [];
-  // getDataList(refresh);
-  state.dataList = sourceData.value.filter(item => item.ruleName.includes(state.queryForm.ruleName) && item.triggerInfo.includes(state.queryForm.triggerInfo));
-  state.pagination.total = state.dataList.length;
+  getDataList(refresh);
 };
 
 // 清空搜索条件
 const resetQuery = () => {
   queryRef.value.resetFields();
-  query();
-  // state.dataList = [];
-  // getDataList();
+  state.dataList = [];
+  getDataList();
 };
 
 const statusFormatter = (row: any, column: any, cellValue: any, index: any) => {
@@ -151,38 +173,30 @@ const statusFormatter = (row: any, column: any, cellValue: any, index: any) => {
 const showTriggerInfo = (row: any) => {
   // 信息详情弹窗
   richContentDialogRef.value.openDialog(
-    row.triggerInfo, 
+    row.pushDetail, 
     {
-      createTime: row.triggerTime,
-      triggerKeyword: ['战略']
+      createTime: row.createTime,
+      triggerKeyword: row._keywords
     }
   )
 }
 
-onMounted(() => {
-  // query();
-  for (let i = 1; i < 11; i++) {
-    sourceData.value.push({
-      ruleName: '规则名称' + i,
-      triggerInfo:`<div class="fusion-column-wrapper fusion-flex-justify-content-flex-start fusion-content-layout-column"><div class="fusion-text fusion-text-4"><h2 class="fusion-responsive-typography-calculated" style="--fontsize: 36; line-height: 1.25; --fontSize: 36;" data-fontsize="36" data-lineheight="45px" id="toc">什么是产品创新?</h2>
-<p><span style="font-weight: 400;">产品创新是指公司或组织创造和开发新的或改进的产品、服务或流程。 它涉及引入新颖的想法、技术、功能或设计,为客户提供附加值,使产品有别于市场上现有的产品。</span></p>
-<p><span style="font-weight: 400;">成功的产品创新需要将创造力、<a href="https://ideascale.com/zh-hans/%e5%8d%9a%e5%ae%a2/%e4%bb%80%e4%b9%88%e6%98%af%e5%b8%82%e5%9c%ba%e7%a0%94%e7%a9%b6/">市场调研</a>、客户洞察力、技术专长和有效的项目管理结合起来。 其动力来自于满足不断变化的客户需求、领先于竞争对手以及创造可持续业务增长的愿望。</span></p>
-<p><b>产品创新表格</b></p>
-<p><span style="font-weight: 400;">成功的产品创新需要将创造力、<a href="https://ideascale.com/zh-hans/%e5%8d%9a%e5%ae%a2/%e4%bb%80%e4%b9%88%e6%98%af%e5%b8%82%e5%9c%ba%e7%a0%94%e7%a9%b6/">市场调研</a>、客户洞察力、技术专长和有效的项目管理结合起来。 以下是创造可持续业务增长的产品创新形式。</span></p>
-<p><strong><b>了解更多信息:<a href="https://ideascale.com/zh-hans/%e5%8d%9a%e5%ae%a2/%e4%bb%80%e4%b9%88%e6%98%af%e6%88%98%e7%95%a5%e5%88%9b%e6%96%b0/">什么是战略创新?</a></b></strong></p>
-</div></div>`,
-      triggerTime: '2025-08-29 10:00:00',
-      pushStatus: i % 2 === 0 ? '1' : '2',
-      pushContent: i % 2 === 0 ? '1' : '2',
-      pushFrequency: '30分钟一次',
-      pushTime: '2025-08-29 10:00:00',
-    });
-  }
-  state.dataList = sourceData.value;
-  state.pagination.total = state.dataList.length;
+const getFetchItemList = async () => {
+  const res = await fetchItemList({
+    dictType: 'pushMode',
+  });
+
+  pushMode.value = res.data.records.map((item: any) => ({
+    label: item.description,
+    value: item.value
+  }));
+  console.log(pushMode, 'pushMode');
+}
 
-  console.log(sourceData.value);
-})
+onMounted(() => {
+  getFetchItemList();
+  query();
+});
 
 </script>
 <style scoped lang="scss">
@@ -201,7 +215,7 @@ onMounted(() => {
 }
 
 .trigger-info {
-  height: 18px;
+  // height: 18px;
   overflow: hidden;
   text-align: center;
   width: 100%;