cmy 1 неделя назад
Родитель
Сommit
733b676c4f

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

@@ -143,4 +143,35 @@ export const setAppInfo = (data: Object) => {
 		method: 'post',
 		data: data,
 	});
+};
+
+/**
+ * 分页统计应用一级营销数据
+ * @param appId	应用ID
+ * @param timeRange	时间范围,0-全部,1-24小时,2-15分钟
+ * @param size	每页显示条数
+ * @param current	当前页
+ */
+export const getStatPage = (params: Object) => {
+	return request({
+		url: '/marketing/app/stat/page',
+		method: 'get',
+		params,
+	});
+};
+
+/**
+ * 分页统计应用二级营销数据
+ * @param appId	应用ID
+ * @param timeRange	时间范围,0-全部,1-24小时,2-15分钟
+ * @param ip	ip
+ * @param size	每页显示条数
+ * @param current	当前页
+ */
+export const getStatSecondPage = (params: Object) => {
+	return request({
+		url: '/marketing/app/stat/second/page',
+		method: 'get',
+		params,
+	});
 };

+ 3 - 1
src/views/marketing/apps/components/domainCell.vue

@@ -5,7 +5,7 @@
     </div>
     <div class="action" :style="{ textAlign: list.length > 0 ? 'left' : 'center'}">
       <el-link type="info" class="btn-more" :class="{disabled: !state}" :disabled="!state" @click="handleEdit" >
-        {{ list.length > 0 ? '查看详情' : '添加域名'}}
+        {{ list.length > 0 ? t('marketingApps.viewDetail') : t('marketingApps.addDomain') }}
       </el-link>
     </div>
   </div>
@@ -13,9 +13,11 @@
 </template>
 <script setup lang="ts">
 import { ref, watch } from 'vue';
+import { useI18n } from 'vue-i18n';
 const DomainForm = defineAsyncComponent(() => import('./domainForm.vue'));
 import { SourceType  } from '../types';
 
+const { t } = useI18n();
 const props = defineProps(['domainList', 'rowData', 'state']);
 const emit = defineEmits(['refresh']);
 const list = ref<string[]>([]);

+ 6 - 8
src/views/marketing/apps/components/domainCollapse.vue

@@ -2,25 +2,23 @@
   <JCollapse :data="[
     { title: '域名集合', id: '1' },
   ]" @update="(item) => domainEditOpen = true" @delete="(item) => domainDeletable = !domainDeletable"
-    :deleteText="domainDeletable ? '取消' : t('marketingConfig.deleteText')" :updateText="'新增'" :activeNames="['1']">
+    :deleteText="domainDeletable ? t('common.cancelButtonText') : t('marketingApps.delete')" :updateText="t('marketingApps.add')" :activeNames="['1']">
     <template #default>
       <div class="p-2 items-center flex flex-wrap">
         <template v-for="item in domains" >
           <!-- 具体域名 -->
             <el-tag v-if="item.sourceType == SourceType.DOMAIN" effect="light" :closable="domainDeletable"
-            @close="handleDeleteDomain(item, SourceType.DOMAIN)" color="#f4f4f4" round class="ml-1 cursor-pointer"
-            style="margin-bottom: 4px;">
+            @close="handleDeleteDomain(item, SourceType.DOMAIN)" color="#f4f4f4" round class="ml-1 mb-1 cursor-pointer">
               <span class="ellipsis">{{ item.domain }}</span>
           </el-tag>
           <!-- 分组域名 -->
           <el-popover v-else width="200" trigger="hover" placement="top" @show="onLoadDetail(item)">
             <div class="flex flex-wrap break-all">
-              {{ item.domains && item.domains.length ? item.domains.map(i=>i.domain).join(',') : '暂无数据' }}
+              {{ item.domains && item.domains.length ? item.domains.map(i=>i.domain).join(',') : t('marketingApps.noData') }}
             </div>
             <template #reference>
               <el-tag effect="light" :closable="domainDeletable"
-                @close="handleDeleteDomain(item, SourceType.GROUP)" color="#f4f4f4" round class="ml-1 cursor-pointer"
-                style="margin-bottom: 4px;">
+                @close="handleDeleteDomain(item, SourceType.GROUP)" color="#f4f4f4" round class="mr-1 mb-1 cursor-pointer">
                 <span class="ellipsis">{{ item.groupName }}</span>
               </el-tag>
             </template>
@@ -106,7 +104,7 @@ function addGroupsUnique(newGroups: DomainItem[]) {
     console.log(filtered);
 		useMessage().success(t('common.addSuccessText'));
   }else{
-    useMessage().warning('该分组已添加到域名集合,无需重复添加');
+    useMessage().warning(t('marketingApps.addDomainTip'));
   }
 }
 
@@ -131,7 +129,7 @@ function addSinglesUnique(newSingles: DomainItem[]) {
     console.log(filtered);
 		useMessage().success(t('common.addSuccessText'));
   }else{
-    useMessage().warning('该域名已存在于集合中,无需重复添加');
+    useMessage().warning(t('marketingApps.addDomainTip2'));
   }
 }
 

+ 4 - 5
src/views/marketing/apps/components/domainEdit.vue

@@ -1,5 +1,5 @@
 <template>
-	<el-dialog :title="type === 'Edit' ? '修改域名' : '添加域名'" width="600" v-model="props.open" :close-on-click-modal="false"
+	<el-dialog :title="t('marketingApps.addDomain')" width="600" v-model="props.open" :close-on-click-modal="false"
 		:destroy-on-close="true" @close="onCancel" draggable>
 		<el-form style="height: 80px;" ref="dialogFormRef" :rules="dataRules" :model="state.ruleForm"
 			v-loading="loading">
@@ -7,19 +7,19 @@
 				<el-segmented v-model="activeName"
 					:options="['group', 'domain']" size="default">
 					<template #default="scope">
-						<div style="min-width: 50px; line-height: 32px;">{{ scope.item == 'group' ? t('marketingConfig.grouping') : t('marketingConfig.domain') }}</div>
+						<div style="min-width: 50px; line-height: 32px;">{{ scope.item == 'group' ? t('marketingApps.group') : t('marketingApps.domain') }}</div>
 					</template>
 				</el-segmented>
 			</div>
 			<el-form-item label="" prop="grouping" v-if="activeName === 'group'">
 				<el-select multiple collapse-tags collapse-tags-tooltip :max-collapse-tags="5"
-					v-model="state.ruleForm.selectedIds" :placeholder="t('marketingConfig.groupingTip')" clearable>
+					v-model="state.ruleForm.selectedIds" :placeholder="t('marketingApps.inputGroupTip')" clearable>
 					<el-option v-for="item in listSelect" :key="item.id" :label="item.groupName" :value="item.id" />
 				</el-select>
 			</el-form-item>
 			<el-form-item label="" prop="domain" v-else>
 				<el-input v-model="state.ruleForm.domain" type="text"
-					:placeholder="t('marketingConfig.domainTip')"></el-input>
+					:placeholder="t('marketingApps.inputDomainTip')"></el-input>
 			</el-form-item>
 		</el-form>
 		<template #footer>
@@ -45,7 +45,6 @@ const { t } = useI18n();
 
 // 定义变量内容
 const loading = ref(false);
-const type = ref('add'); // 'add' or 'edit'
 const activeName = ref('group');
 const dialogFormRef = ref();
 const listSelect = ref<DomainItem[]>([]);

+ 5 - 5
src/views/marketing/apps/components/domainForm.vue

@@ -1,10 +1,10 @@
 <template>
   <div class="apps-form">
     <el-dialog
-      append-to-body :title="'修改域名'" width="880" v-model="visible" :close-on-click-modal="false" :destroy-on-close="true"
+      append-to-body :title="t('marketingApps.editDomain')" width="880" v-model="visible" :close-on-click-modal="false" :destroy-on-close="true"
       draggable>
       <DomainCollapse :data=domains @domains="updateDomains"
-        @delDomains="updateDelDomains"></DomainCollapse>
+        @delDomains="handleDelDomains"></DomainCollapse>
       <template #footer>
         <span class="dialog-footer">
           <el-button @click="visible = false">{{ t('common.cancelButtonText') }}</el-button>
@@ -48,7 +48,7 @@ const openDialog = async (data: any, row) => {
 const updateDomains = (data: DomainItem[])=>{
   childDomains.value = data;
 }
-const updateDelDomains = (data: string[]) => {
+const handleDelDomains = (data: string[]) => {
   delDomains.value = data;
 }
 
@@ -57,10 +57,10 @@ const onSubmit = async () => {
   try {
     updateModDomains({ domains: childDomains.value, delDomains: delDomains.value, id: rowData.value.id})
     emit('refresh');
-    ElMessage.success('提交成功!');
+    ElMessage.success(t('marketingApps.submitSuccess'));
     visible.value = false;
   } catch (error) {
-    ElMessage.error('提交失败!');
+    ElMessage.error(t('marketingApps.submitFail'));
     console.error(error);
   }
 };

+ 19 - 5
src/views/marketing/apps/components/form.vue

@@ -4,10 +4,10 @@
       :close-on-click-modal="false" :destroy-on-close="true" draggable>
       <el-form ref="appDialogFormRef" :rules="dataRules" :model="state.ruleForm" class="demo-form-inline"
         v-loading="loading">
-        <el-form-item v-if="state.ruleForm.appName" :label="'应用名称'" prop="appName">
+        <el-form-item v-if="state.ruleForm.appName" :label="t('marketingApps.name')" prop="appName">
           <el-input style="width: 200px;" v-model="state.ruleForm.appName" disabled />
         </el-form-item>
-        <el-row :gutter="20" style="margin-bottom: 15px;">
+        <el-row :gutter="20" class="mb18">
           <el-col :span="12">
             <el-form-item :label="'营销开关'" prop="launch">
               <el-switch v-model="state.ruleForm.launch" style="--el-switch-on-color: rgb(48, 185, 113);" inline-prompt
@@ -42,7 +42,7 @@
           <DomainCollapse :data=state.ruleForm.domains @domains="updateDomains" @delDomains="updateDelDomains">
           </DomainCollapse>
         </div>
-        <div class="title">备注</div>
+        <div class="title">{{ t('marketingApps.remark') }}</div>
         <el-form-item>
           <el-input :rows="4" v-model="state.ruleForm.remark" type="textarea" placeholder="请输入备注"></el-input>
         </el-form-item>
@@ -121,6 +121,17 @@ const updateDelDomains = (data: string[]) => {
   delDomains.value = data;
 }
 
+// 格式化数据展示
+const formatNum = (value: string | number = 0) => {
+  let num = Number(value);
+  if(num > 0 && num < 1) {
+    return num * 100 + '%';
+  }else if (num >= 1 && num < 10000) {
+    return num;
+  }
+  return '--'
+}
+
 // 打开弹窗
 const openDialog = async (type: string, row: any) => {
   visible.value = true;
@@ -134,6 +145,9 @@ const openDialog = async (type: string, row: any) => {
     } as Form;
     rows.value = row;
   }else if(row instanceof Object) {
+    if(row.triggerNum) {
+      row.triggerNum = formatNum(row.triggerNum);
+    }
     state.ruleForm = row;
     rows.value = [row]
   }
@@ -221,11 +235,11 @@ const onSubmit = async () => {
     console.log('rows.value', data);
     
     await appUpdate(data);
-    ElMessage.success('提交成功!');
+    ElMessage.error(t('marketingApps.submitSuccess'));
     visible.value = false;
     emit('refresh');
   } catch (err: any) {
-    ElMessage.error('提交失败!');
+    ElMessage.error(t('marketingApps.submitFail'));
     console.error(err);
   } finally {
     loading.value = false;

+ 4 - 2
src/views/marketing/apps/components/ipCell.vue

@@ -1,13 +1,13 @@
 <template>
   <div class="cell">
     <div class="ellipsis" style="width: 100%; text-align: left;" v-for="(list, index) in [whitelist, blacklist]">
-      <span class="title">{{ index == 0 ? '白名单:' : '黑名单:' }}</span>
+      <span class="title">{{ index == 0 ? t('marketingApps.whiteList') : t('marketingApps.blackList') }}</span>
       <span class="item" v-for="item in list" :key="item">{{item}}</span>
       <span class="item" v-if="list.length <= 0">--</span>
     </div>
     <div class="action">
       <el-link type="info" class="btn-more" :class="{disabled: !state}" :disabled="!state" @click="handleEdit" >
-        {{ ipList.length > 0 ? '查看详情' : '添加IP'}}
+        {{ ipList.length > 0 ? t('marketingApps.viewDetail') : t('marketingApps.addIp')}}
       </el-link>
     </div>
   </div>
@@ -18,9 +18,11 @@ import { ref, watch } from 'vue';
 const IpForm = defineAsyncComponent(() => import('./ipForm.vue'));
 import { ipSplicing } from '/@/utils/ipUpdate';
 import { SourceType, IpType } from '../types';
+import { useI18n } from 'vue-i18n';
 
 const props = defineProps(['ipList', 'rowData', 'state']);
 const emit = defineEmits(['refresh']);
+const { t } = useI18n();
 
 const blacklist = ref([]);
 const whitelist = ref([]);

+ 7 - 7
src/views/marketing/apps/components/ipCollapse.vue

@@ -1,16 +1,16 @@
 <template>
   <JCollapse :data="[{ title: 'IP集合', id: '1' }]" @update="(item: any) => listEditOpen = true"
     @delete="(item: any) => ipDeletable = !ipDeletable"
-    :deleteText="ipDeletable ? '取消' : t('marketingConfig.deleteText')" :updateText="'新增'" :activeNames="['1']">
+    :deleteText="ipDeletable ? t('common.cancelButtonText') : t('marketingApps.delete')" :updateText="t('marketingApps.add')" :activeNames="['1']">
     <template #default>
       <div class="border-b p-2 items-center flex flex-wrap collapse-group min-h-[40px]" v-for="type in [IpType.WHITE, IpType.BLACK]"
         :key="type">
-        <div class="collapse-group-name">{{ type === IpType.WHITE ? '白名单' : '黑名单' }}:</div>
+        <div class="collapse-group-name">{{ type === IpType.WHITE ? t('marketingApps.whiteList') : t('marketingApps.blackList') }}:</div>
         <div class="tag-content" v-if="getTargetList(type).length > 0">
           <template v-for="item in getTargetList(type)" :key="item.id">
             <!-- 单个 -->
             <el-tag v-if="item.sourceType == 2" effect="light" :closable="ipDeletable" @close="handleDeleteIp(item)"
-              color="#f4f4f4" round class="ml-1 cursor-pointer" style="margin-bottom: 4px;">
+              color="#f4f4f4" round class="ml-1 mb-1 cursor-pointer">
               <span class="ellipsis">{{ ipSplicing(item.startIp, item.endIp) }}</span>
             </el-tag>
             <!-- 分组 -->
@@ -18,10 +18,10 @@
               <div v-if="item.list.length > 0" class="flex flex-wrap break-all">
                 <span v-for="ip in item.list" :key="String(ip)" class="mr-2">{{ ip }}</span>
               </div>
-              <div v-else>暂无数据</div>
+              <div v-else>{{ t('marketingApps.noData') }}</div>
               <template #reference>
                 <el-tag effect="light" :closable="ipDeletable" @close="handleDeleteIp(item)" color="#f4f4f4" round
-                  class="ml-1 cursor-pointer" style="margin-bottom: 4px;">
+                  class="mr-1 mb-1 cursor-pointer">
                   <span class="ellipsis" v-if="item.groupName">{{ item.groupName.length > 30 ?
                     item.groupName.substring(0, 30) + '...' : item.groupName }}</span>
                 </el-tag>
@@ -117,7 +117,7 @@ function addGroupsUnique(newGroups: IpItem[], ipType: number) {
     }));
     setTargetList(ipType, [...targetList, ...processedGroups]);
   } else {
-    useMessage().warning('该分组已添加到IP集合,无需重复添加');
+    useMessage().warning(t('marketingApps.addIpTip'));
   }
 }
 
@@ -152,7 +152,7 @@ function addSinglesUnique(newSingles: IpItem[], ipType: number) {
     }));
     setTargetList(ipType, [...targetList, ...processedSingles]);
   } else {
-    useMessage().warning('该IP已存在于集合中,无需重复添加');
+    useMessage().warning(t('marketingApps.addIpTip2'));
   }
 }
 

+ 3 - 3
src/views/marketing/apps/components/ipEdit.vue

@@ -1,11 +1,11 @@
 <template>
-	<el-dialog :title="type === 'Edit' ? '修改IP' : '添加IP'" width="600" v-model="props.open" :close-on-click-modal="false"
+	<el-dialog :title="t('marketingApps.addIp')" width="600" v-model="props.open" :close-on-click-modal="false"
 		:destroy-on-close="true" @close="onCancel" draggable>
 		<el-form style="height: 128px;" ref="formRef" :rules="dataRules" :model="state.ruleForm" v-loading="loading">
 			<el-form-item label="" prop="ipType">
 				<el-radio-group v-model="state.ruleForm.ipType">
-					<el-radio :value="IpType.WHITE">白名单</el-radio>
-					<el-radio :value="IpType.BLACK">黑名单</el-radio>
+					<el-radio :value="IpType.WHITE">{{ t('marketingApps.whiteList') }}</el-radio>
+					<el-radio :value="IpType.BLACK">{{ t('marketingApps.blackList') }}</el-radio>
 				</el-radio-group>
 			</el-form-item>
 			<div class="custom-style">

+ 3 - 3
src/views/marketing/apps/components/ipForm.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="apps-form">
     <el-dialog
-      append-to-body :title="'修改IP'" width="880" v-model="visible" :close-on-click-modal="false" :destroy-on-close="true"
+      append-to-body :title="t('marketingApps.editIp')" width="880" v-model="visible" :close-on-click-modal="false" :destroy-on-close="true"
       draggable>
       <IpCollapse :data=ips  @ips="updateIps" @delIps="updateDelIps"></IpCollapse>
       <template #footer>
@@ -53,10 +53,10 @@ const onSubmit = async () => {
   try {
     await updateModIp({ ips: childIps.value, delIps: delIps.value, id: rowData.value.id});
     emit('refresh');
-    ElMessage.success('提交成功!');
+    ElMessage.error(t('marketingApps.submitSuccess'));
     visible.value = false;
   } catch (error) {
-    ElMessage.error('提交失败!');
+    ElMessage.error(t('marketingApps.submitFail'));
     console.error(error);
   }
 };

+ 86 - 72
src/views/marketing/apps/components/statistical.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="dialog">
     <el-dialog
-      :title="'统计'"
+      :title="t('marketingApps.statistical')"
       width="80%"
       v-model="visible"
       :destroy-on-close="true"
@@ -9,68 +9,68 @@
       :before-close="done=>handleClose(done)"
       draggable>
       <div class="dialog-body">
-
-      <div class="statistics-table-wrapper">
-        <el-row v-show="showSearch">
-          <el-form :inline="true" :model="state.queryForm" @keyup.enter="withCollapsedChildren(getDataList)" ref="queryRef">
-            <el-form-item :label="'时间范围'" prop="timeRange">
-              <el-select v-model="state.queryForm.timeRange" @change="handleTimeChange()">
-                  <el-option :value="TimeRange.ALL" :label="'全部'">全部</el-option>
-                  <el-option :value="TimeRange.TWENTY_FOUR_HOURS" :label="'24小时'">24小时</el-option>
-                  <el-option :value="TimeRange.FIFTEEN_MINUTES" :label="'15分钟'">15分钟</el-option>
-              </el-select>
-            </el-form-item>
-          </el-form>
-        </el-row>
-        <el-table class="statistics-table" :data="state.dataList" row-key="ip" @sort-change="withCollapsedChildren(sortChangeHandle)" style="width: 100%"
-          v-loading="state.loading" border :cell-style="tableStyle.cellStyle"
-          :header-cell-style="tableStyle.headerCellStyle"
-          :expand-row-keys="expandedRowKeys"
-          @expand-change="handleExpandChange"
-          >
-          <!-- <el-table-column align="center" type="selection" width="40" /> -->
-          <el-table-column show-overflow-tooltip type="expand">
-            <template #default="{ row }">
-              <div class="child-table-container">
-                <el-table :data="row.childTableData" v-loading="row.childTableData.childLoading" border :cell-style="tableStyle.cellStyle"
-                  :header-cell-style="tableStyle.headerCellStyle">
-                  <!-- <el-table-column :label="t('apps.ip')" prop="ip" show-overflow-tooltip></el-table-column> -->
-                  <el-table-column :label="t('apps.fingerprint')" prop="fingerprint" show-overflow-tooltip></el-table-column>
-                  <el-table-column :label="t('apps.device')" prop="device" show-overflow-tooltip></el-table-column>
-                  <el-table-column :label="t('apps.firstAccessTime')" prop="firstAccessTime" show-overflow-tooltip></el-table-column>
-                  <el-table-column :label="t('apps.lastAccessTime')" prop="lastAccessTime" show-overflow-tooltip></el-table-column>
-                  <el-table-column :label="t('apps.lastVisitedPage')" prop="lastVisitedPage" show-overflow-tooltip></el-table-column>
-                  <el-table-column :label="t('apps.accessVolume')" prop="accessVolume" show-overflow-tooltip></el-table-column>
-                </el-table>
-                <div class="pagination-container" @click.stop>
-                  <pagination v-bind="row.childPagination"
-                    :current-page="row.childPagination.page"
-                    :page-size="row.childPagination.size"
-                    :total="row.childPagination.total"
-                    :key="`pagination-${row.ip}-${row.childPagination.page}`"
-                    @size-change="size => handlePageSizeChange(row, size)"
-                    @current-change="page => handlePageChange(row, page)" />
+        <div class="statistics-table-wrapper">
+          <el-row v-show="showSearch">
+            <el-form :inline="true" :model="state.queryForm" @keyup.enter="withCollapsedChildren(getDataList)" ref="queryRef">
+              <el-form-item :label="t('marketingApps.timeRange')" prop="timeRange">
+                <el-select v-model="state.queryForm.timeRange" @change="handleTimeChange()">
+                    <el-option v-for="item in timeRangeOptions" :value="item.value" :label="item.label">{{ item.label }}</el-option>
+                </el-select>
+              </el-form-item>
+            </el-form>
+          </el-row>
+          <el-table class="statistics-table" :data="state.dataList" row-key="ip" @sort-change="withCollapsedChildren(sortChangeHandle)" style="width: 100%"
+            v-loading="state.loading" border :cell-style="tableStyle.cellStyle"
+            :header-cell-style="tableStyle.headerCellStyle"
+            :expand-row-keys="expandedRowKeys"
+            @expand-change="handleExpandChange"
+            >
+            <!-- <el-table-column align="center" type="selection" width="40" /> -->
+            <el-table-column show-overflow-tooltip type="expand">
+              <template #default="{ row }">
+                <div class="child-table-container">
+                  <el-table :data="row.childTableData" v-loading="row.childTableData.childLoading" border :cell-style="tableStyle.cellStyle"
+                    :header-cell-style="tableStyle.headerCellStyle">
+                    <!-- <el-table-column :label="t('marketingApps.ip')" prop="ip" show-overflow-tooltip></el-table-column> -->
+                    <el-table-column :label="t('marketingApps.fingerprint')" prop="fingerprint" show-overflow-tooltip></el-table-column>
+                    <el-table-column :label="t('marketingApps.device')" prop="device" show-overflow-tooltip>
+                      <template>
+                        {{ row.osType + " " + row.osVersion }}
+                      </template>
+                    </el-table-column>
+                    <el-table-column :label="t('marketingApps.firstAccessTime')" prop="firstTime" show-overflow-tooltip></el-table-column>
+                    <el-table-column :label="t('marketingApps.lastAccessTime')" prop="lastTime" show-overflow-tooltip></el-table-column>
+                    <el-table-column :label="t('marketingApps.lastVisitedPage')" prop="url" show-overflow-tooltip></el-table-column>
+                    <el-table-column :label="t('marketingApps.accessVolume')" prop="count" show-overflow-tooltip></el-table-column>
+                  </el-table>
+                  <div class="pagination-container" @click.stop>
+                    <pagination v-bind="row.childPagination"
+                      :current-page="row.childPagination.page"
+                      :page-size="row.childPagination.size"
+                      :total="row.childPagination.total"
+                      :key="`pagination-${row.ip}-${row.childPagination.page}`"
+                      @size-change="size => handlePageSizeChange(row, size)"
+                      @current-change="page => handlePageChange(row, page)" />
+                  </div>
                 </div>
-              </div>
-            </template>
-          </el-table-column>
-          <el-table-column :label="t('apps.firstAccessTime')" prop="firstAccessTime" show-overflow-tooltip></el-table-column>
-          <el-table-column :label="t('apps.lastAccessTime')" prop="lastAccessTime" show-overflow-tooltip></el-table-column>
-          <el-table-column :label="t('apps.accessSource')" prop="accessSource" show-overflow-tooltip></el-table-column>
-          <el-table-column :label="t('apps.lastVisitedPage')" prop="lastVisitedPage" show-overflow-tooltip></el-table-column>
-          <el-table-column :label="t('apps.IP')" prop="IP" show-overflow-tooltip></el-table-column>
-          <el-table-column :label="t('apps.area')" prop="area" show-overflow-tooltip></el-table-column>
-          <el-table-column :label="t('apps.accessVolume')" prop="accessVolume" show-overflow-tooltip></el-table-column>
-        </el-table>
-
-        <pagination 
-          @current-change="page => withCollapsedChildren(currentChangeHandle, page)" 
-          @size-change="size => withCollapsedChildren(sizeChangeHandle, size)" 
-          v-bind="state.pagination">
-        </pagination>
-        <br>
-      </div>
-
+              </template>
+            </el-table-column>
+            <el-table-column :label="t('marketingApps.firstAccessTime')" prop="firstTime" show-overflow-tooltip></el-table-column>
+            <el-table-column :label="t('marketingApps.lastAccessTime')" prop="lastTime" show-overflow-tooltip></el-table-column>
+            <el-table-column :label="t('marketingApps.accessSource')" prop="utmSource" show-overflow-tooltip></el-table-column>
+            <el-table-column :label="t('marketingApps.lastVisitedPage')" prop="url" show-overflow-tooltip></el-table-column>
+            <el-table-column :label="t('marketingApps.IP')" prop="ip" show-overflow-tooltip></el-table-column>
+            <el-table-column :label="t('marketingApps.area')" prop="region" show-overflow-tooltip></el-table-column>
+            <el-table-column :label="t('marketingApps.accessVolume')" prop="count" show-overflow-tooltip></el-table-column>
+          </el-table>
+
+          <pagination 
+            @current-change="page => withCollapsedChildren(currentChangeHandle, page)" 
+            @size-change="size => withCollapsedChildren(sizeChangeHandle, size)" 
+            v-bind="state.pagination">
+          </pagination>
+          <br>
+        </div>
       </div>
     </el-dialog>
   </div>
@@ -79,13 +79,14 @@
 <script setup lang="ts">
 import { ref, reactive, watchEffect } from 'vue';
 import { BasicTableProps, useTable } from '/@/hooks/table';
-import { pageList, detailList } from '/@/api/marketing/statistics';
+import { getStatPage, getStatSecondPage } from '/@/api/marketing/apps';
+import { fetchItemList } from '/@/api/admin/dict';
 import { useI18n } from 'vue-i18n';
 
 enum TimeRange {
-  ALL = 0,
-  TWENTY_FOUR_HOURS = 1,
-  FIFTEEN_MINUTES = 2,
+  ALL = "0",
+  TWENTY_FOUR_HOURS = "1",
+  FIFTEEN_MINUTES = "2",
 }
 
 const visible = ref(false);
@@ -103,7 +104,7 @@ const state: BasicTableProps = reactive<BasicTableProps>({
     timeRange: TimeRange.ALL,
     appId: '',
   },
-  pageList: pageList,
+  pageList: getStatPage,
   dataListLoading: true,
   pagination: {
     current: 1,
@@ -112,15 +113,25 @@ const state: BasicTableProps = reactive<BasicTableProps>({
     pageSizes: [5, 10, 20, 50, 100]
   },
 });
+const timeRangeOptions = ref([]) as any;
 
 const { getDataList, currentChangeHandle, sortChangeHandle, sizeChangeHandle, tableStyle } = useTable(state);
 
+// 获取时间范围列表
+const getTimeRangeList = async () => {
+  const { data } = await fetchItemList({
+    dictType: 'timeRange'
+  });
+  timeRangeOptions.value = data?.records || [];
+  timeRangeOptions.value.length && (state.queryForm.timeRange = timeRangeOptions.value[0].value);
+}
+
 // 打开弹窗
 const openDialog = async (_row: any) => {
+  await getTimeRangeList();
   visible.value = true;
-  getDataList();
-  state.queryForm.timeRange = TimeRange.ALL;
   state.queryForm.appId = _row.id;
+  getDataList();
 };
 
 const handleClose = (done) => {
@@ -134,13 +145,16 @@ const handleClose = (done) => {
 //   withCollapsedChildren(getDataList);
 // };
 
+// 时间范围下拉框选中事件
 const handleTimeChange = () => {
   withCollapsedChildren(getDataList);
+  // 如果是15分钟,则每分钟刷新一次数据
   if(state.queryForm.timeRange === TimeRange.FIFTEEN_MINUTES) {
     setIntervalTime.value = setInterval(() => {
       getDataList();
-    }, 4000);
+    }, 60*1000);
   }else{
+    // 清除定时器
     clearInterval(setIntervalTime.value);
   }
 }
@@ -169,7 +183,7 @@ watchEffect(() => {
 const loadChildData = async (parentRow) => {
   parentRow.childLoading = true;
   try {
-    const res = await detailList({
+    const res = await getStatSecondPage({
       ip: parentRow.ip,
       current: parentRow.childPagination.page,
       size: parentRow.childPagination.size
@@ -185,7 +199,7 @@ const loadChildData = async (parentRow) => {
 /**
  * 关闭所有二级表格,后执行对应函数
  * @param callback 执行函数
- * @param args 执行函数(参数)
+ * @param args 执行函数的参数
  */
 const withCollapsedChildren = (callback: Function, ...args: any[]) => {
   expandedRowKeys.value = [];

+ 50 - 15
src/views/marketing/apps/i18n/zh-cn.ts

@@ -1,32 +1,67 @@
 export default {
 	marketingApps: {
+		/* 应用列表 */
 		index: '#',
 		name: '应用名称',
 		config: '配置方案',
-		inputNameTip: '请输入应用名称名称',
-		inputIPTip: '请输入IP名称',
 		app: '应用',
-		inputAppSel: '请选择应用',
-
-		// ID
 		id: 'ID',
-		// 应用ID
 		appId: '应用ID',
-		// 应用图片
 		appImg: '应用图片',
-		// 应用类型
 		appType: '应用类型',
-		// 备注
 		remark: '备注',
-		// 触发规则
 		triggerRule: '触发规则',
-		// 黑名单
 		blackList: '黑名单',
-		// 白名单
 		whiteList: '白名单',
-		// 修改
-		edit: '修改',
-		// 统计
 		statistical: '统计',
+		ipSegment: 'IP/网段',
+
+		add: '新增',
+		edit: '修改',
+		delete: '删除',
+		group: '分组',
+		domain: '域名',
+		editIp: '修改IP',
+		addIp: '添加IP',
+		noData: '暂无数据',
+		viewDetail: '查看详情',
+		addDomain: '添加域名',
+		editDomain: '修改域名',
+
+		inputAppSel: '请选择应用',
+		inputNameTip: '请输入应用名称名称',
+		addDomainTip: '该分组已添加到域名集合,无需重复添加',
+		addDomainTip2: '该域名已存在于集合中,无需重复添加',
+		addIpTip: '该分组已添加到IP集合,无需重复添加',
+		addIpTip2: '该IP已存在于集合中,无需重复添加',
+		inputGroupTip: '请选择分组名称',
+		submitSuccess: '提交成功!',
+		submitFail: '提交失败!',
+
+		/* 数据统计 */
+		statistics: '统计',
+		ip: 'IP',
+		content: '访问量',
+		dayActive: '日活',
+		hourActive: '时活',
+		fifteenOnline: '15分钟在线人数',
+		referrer: '来源',
+		fingerprint: '指纹',
+		timeRange: '时间范围',
+		firstAccessTime: '首次访问时间',
+		lastAccessTime: '最后访问时间',
+		accessSource: '访问来源',
+		lastVisitedPage: '最后受访页面',
+		IP: 'IP',
+		area: '地区',
+		accessVolume: '访问量',
+		device: '设备',
+
+		queryBtn: '查询',
+		resetBtn: '重置',
+
+		inputReferrer: '请输入来源',
+		inputIpAddress: '请输入IP地址',
+		inputDomainTip: '请输入域名',
 	},
 };

+ 21 - 8
src/views/marketing/apps/index.vue

@@ -7,15 +7,15 @@
             <el-input :placeholder="t('marketingApps.inputNameTip')" clearable
               v-model="state.queryForm.appName" />
           </el-form-item>
-          <el-form-item :label="'应用ID'" prop="appId">
+          <el-form-item :label="t('marketingApps.appId')" prop="appId">
             <el-input :placeholder="'请输入应用ID'" clearable v-model="state.queryForm.appId" />
           </el-form-item>
-          <el-form-item :label="'应用类型'" prop="domainSelected">
+          <el-form-item :label="t('marketingApps.appType')" prop="domainSelected">
             <el-select placeholder="请选择应用类型" v-model="state.queryForm.domainSelected">
               <el-option v-for="item in domainTypeOptions" :key="item.value" :label="item.description" :value="item.value" />
             </el-select>
           </el-form-item>
-          <el-form-item :label="'备注'" prop="remark">
+          <el-form-item :label="t('marketingApps.remark')" prop="remark">
             <el-input :placeholder="'请输入备注'" clearable v-model="state.queryForm.remark" />
           </el-form-item>
           <el-form-item>
@@ -91,7 +91,7 @@
         <el-table-column :label="'触发频率'" prop="triggerNum" width="120" show-overflow-tooltip>
           <template #default="{ row }">
             <!-- <el-input-number v-model="row.triggerNum" :max=20 /> -->
-             {{ row.triggerNum }}
+             {{ formatNum(row.triggerNum)  }}
           </template>
         </el-table-column>
         <el-table-column :label="t('marketingApps.triggerRule')" width="140" prop="triggerRule" show-overflow-tooltip>
@@ -101,9 +101,9 @@
             </el-select>
           </template>
         </el-table-column>
-        <el-table-column :label="'备注'" prop="remark" width="150" show-overflow-tooltip>
+        <el-table-column :label="t('marketingApps.remark')" prop="remark" width="150" show-overflow-tooltip>
           <template #default="{ row }">
-             {{ row.remark }}
+             {{ row.remark || '--' }}
           </template>
         </el-table-column>
         <el-table-column fixed="right" :label="t('common.action')" width="200">
@@ -121,7 +121,8 @@
                   <path d="M8.5 2H4C2.89543 2 2 2.89543 2 4V13C2 14.1046 2.89543 15 4 15H13C14.1046 15 15 14.1046 15 13V8.5" stroke="#167AF0" stroke-linecap="round"/>
                   </g>
                 </svg>
-                修改</el-button>
+                {{ t('marketingApps.edit') }}
+              </el-button>
               <el-button style="margin-right: 0px;" @click="onOpenStatistical(scope.row)" text type="primary"
                 v-auth="'sys_menu_edit'">
                 <svg style="margin-right: 5px;" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
@@ -131,7 +132,8 @@
                   <path d="M5.33325 6.33334V10.3333" stroke="#167AF0" stroke-linecap="round" stroke-linejoin="round"/>
                   <path d="M2 10V13C2 13.5523 2.44772 14 3 14H13C13.5523 14 14 13.5523 14 13V10" stroke="#167AF0" stroke-linecap="round" stroke-linejoin="round"/>
                 </svg>
-                统计</el-button>
+                {{ t('marketingApps.statistics') }}
+              </el-button>
               <el-button v-if="activeName == 'tab1'" style="margin-right: 0px;" @click="handleDelete(scope.row)" text type="danger"
                 v-auth="'sys_menu_edit'">
                 <svg style="margin-right: 5px;" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
@@ -300,6 +302,17 @@ const handleDelete = async (row: any) => {
   query();
 };
 
+// 格式化数据展示
+const formatNum = (value: string | number = 0) => {
+  let num = Number(value);
+  if(num > 0 && num < 1) {
+    return num * 100 + '%';
+  }else if (num >= 1 && num < 10000) {
+    return num;
+  }
+  return '--'
+}
+
 onMounted(()=>{
   query();
 })

+ 1 - 1
src/views/marketing/statistics/i18n/en.ts

@@ -1,5 +1,5 @@
 export default {
-	apps: {
+	statistics: {
 		index: '#',
 		userId: 'userId',
 		username: 'username',

+ 1 - 10
src/views/marketing/statistics/i18n/zh-cn.ts

@@ -1,5 +1,5 @@
 export default {
-	apps: {
+	statistics: {
 		ip: 'IP',
 		domain: '域名',
 		content: '访问量',
@@ -14,23 +14,14 @@ export default {
 		resetBtn: '重置',
 		fingerprint: '指纹',
 
-		// 时间范围
 		timeRange: '时间范围',
-		// 首次访问时间
 		firstAccessTime: '首次访问时间',
-		// 最后访问时间
 		lastAccessTime: '最后访问时间',
-		// 访问来源
 		accessSource: '访问来源',
-		// 最后受访页面
 		lastVisitedPage: '最后受访页面',
-		// IP
 		IP: 'IP',
-		// 地区
 		area: '地区',
-		// 访问量
 		accessVolume: '访问量',
-		// 设备
 		device: '设备',
 	},
 };

+ 19 - 19
src/views/marketing/statistics/index.vue

@@ -3,14 +3,14 @@
     <div class="layout-padding-auto layout-padding-view">
       <el-row class="ml10" v-show="showSearch">
         <el-form :inline="true" :model="state.queryForm" @keyup.enter="withCollapsedChildren(getDataList)" ref="queryRef">
-          <el-form-item :label="t('apps.ip')" prop="ip">
-            <el-input :placeholder="t('apps.inputIpTip')" v-model="state.queryForm.ip"></el-input>
+          <el-form-item :label="t('statistics.ip')" prop="ip">
+            <el-input :placeholder="t('statistics.inputIpTip')" v-model="state.queryForm.ip"></el-input>
           </el-form-item>
-          <!-- <el-form-item :label="t('apps.domain')" prop="domain">
-            <el-input :placeholder="t('apps.inputDomainTip')" v-model="state.queryForm.domain"></el-input>
+          <!-- <el-form-item :label="t('statistics.domain')" prop="domain">
+            <el-input :placeholder="t('statistics.inputDomainTip')" v-model="state.queryForm.domain"></el-input>
           </el-form-item>
-          <el-form-item :label="t('apps.referrer')" prop="referrer">
-            <el-input :placeholder="t('apps.inputReferrer')" v-model="state.queryForm.referrer"></el-input>
+          <el-form-item :label="t('statistics.referrer')" prop="referrer">
+            <el-input :placeholder="t('statistics.inputReferrer')" v-model="state.queryForm.referrer"></el-input>
           </el-form-item> -->
           <el-form-item>
             <el-button @click="withCollapsedChildren(getDataList)" icon="Search" type="primary">{{ t('common.queryBtn') }} </el-button>
@@ -30,14 +30,14 @@
             <div class="child-table-container">
               <el-table :data="row.childTableData" v-loading="row.childTableData.childLoading" border :cell-style="tableStyle.cellStyle"
                 :header-cell-style="tableStyle.headerCellStyle">
-                <!-- <el-table-column :label="t('apps.ip')" prop="ip" show-overflow-tooltip></el-table-column> -->
-                <el-table-column :label="t('apps.domain')" prop="domain" show-overflow-tooltip></el-table-column>
-                <el-table-column :label="t('apps.fingerprint')" prop="fingerprint" show-overflow-tooltip></el-table-column>
-                <el-table-column :label="t('apps.referrer')" prop="referrer" show-overflow-tooltip></el-table-column>
-                <el-table-column :label="t('apps.content')" prop="total" show-overflow-tooltip></el-table-column>
-                <el-table-column :label="t('apps.dayActive')" prop="daily" show-overflow-tooltip></el-table-column>
-                <el-table-column :label="t('apps.hourActive')" prop="hourly" show-overflow-tooltip></el-table-column>
-                <el-table-column :label="t('apps.fifteenOnline')" prop="online" show-overflow-tooltip></el-table-column>
+                <!-- <el-table-column :label="t('statistics.ip')" prop="ip" show-overflow-tooltip></el-table-column> -->
+                <el-table-column :label="t('statistics.domain')" prop="domain" show-overflow-tooltip></el-table-column>
+                <el-table-column :label="t('statistics.fingerprint')" prop="fingerprint" show-overflow-tooltip></el-table-column>
+                <el-table-column :label="t('statistics.referrer')" prop="referrer" show-overflow-tooltip></el-table-column>
+                <el-table-column :label="t('statistics.content')" prop="total" show-overflow-tooltip></el-table-column>
+                <el-table-column :label="t('statistics.dayActive')" prop="daily" show-overflow-tooltip></el-table-column>
+                <el-table-column :label="t('statistics.hourActive')" prop="hourly" show-overflow-tooltip></el-table-column>
+                <el-table-column :label="t('statistics.fifteenOnline')" prop="online" show-overflow-tooltip></el-table-column>
               </el-table>
               <div class="pagination-container" @click.stop>
                 <pagination v-bind="row.childPagination"
@@ -51,11 +51,11 @@
             </div>
           </template>
         </el-table-column>
-        <el-table-column :label="t('apps.ip')" prop="ip" show-overflow-tooltip></el-table-column>
-        <el-table-column :label="t('apps.content')" prop="total" show-overflow-tooltip></el-table-column>
-        <el-table-column :label="t('apps.dayActive')" prop="daily" show-overflow-tooltip></el-table-column>
-        <el-table-column :label="t('apps.hourActive')" prop="hourly" show-overflow-tooltip></el-table-column>
-        <el-table-column :label="t('apps.fifteenOnline')" prop="online" show-overflow-tooltip></el-table-column>
+        <el-table-column :label="t('statistics.ip')" prop="ip" show-overflow-tooltip></el-table-column>
+        <el-table-column :label="t('statistics.content')" prop="total" show-overflow-tooltip></el-table-column>
+        <el-table-column :label="t('statistics.dayActive')" prop="daily" show-overflow-tooltip></el-table-column>
+        <el-table-column :label="t('statistics.hourActive')" prop="hourly" show-overflow-tooltip></el-table-column>
+        <el-table-column :label="t('statistics.fifteenOnline')" prop="online" show-overflow-tooltip></el-table-column>
       </el-table>
 
       <pagination