| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438 |
- <template>
- <div class="container">
- <Breadcrumb :items="['menu.pushReport', 'menu.pushReport.list']" />
- <a-card class="general-card" :title="$t('menu.pushReport.list')">
- <a-row>
- <a-col :flex="1">
- <a-form :model="formModel" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 18 }"
- label-align="left">
- <a-row :gutter="16">
- <a-col :span="6">
- <a-form-item field="type" label="投放场景">
- <a-select v-model="formModel.type" :options="typeOptions" placeholder="请选择投放场景"
- @change="onTypeOrDateChange" />
- </a-form-item>
- </a-col>
- <a-col :span="6">
- <a-form-item field="report_date" label="报告日期">
- <a-date-picker v-model="formModel.report_date" style="width: 100%;"
- value-format="YYYY-MM-DD" @change="onTypeOrDateChange" />
- </a-form-item>
- </a-col>
- <a-col :span="6">
- <a-form-item field="platform" label="平台">
- <div class="filter-with-clear">
- <a-select v-model="formModel.platform" :options="platformOptions"
- placeholder="请选择平台" allow-search :filter-option="filterPlatformOption"
- @change="onTypeOrDateChange" style="width: 100%;">
- </a-select>
- <a-button v-if="formModel.platform" type="text" class="clear-btn"
- @click="clearPlatform">
- <icon-close />
- </a-button>
- </div>
- </a-form-item>
- </a-col>
- <a-col :span="6">
- <a-form-item field="status" label="推送状态">
- <a-select v-model="formModel.status" :options="statusOptions" placeholder="请选择推送状态"
- @change="onTypeOrDateChange">
- </a-select>
- </a-form-item>
- </a-col>
- </a-row>
- <a-row :gutter="16">
- <a-col :span="6">
- <a-form-item field="is_settlement" label="结算状态">
- <a-select v-model="formModel.is_settlement" :options="settlementOptions"
- placeholder="请选择结算状态" @change="onTypeOrDateChange">
- </a-select>
- </a-form-item>
- </a-col>
- </a-row>
- </a-form>
- </a-col>
- <a-divider style="height: 38px" direction="vertical" />
- <a-col :flex="'86px'" style="text-align: right">
- <a-space direction="vertical" :size="18">
- <a-button type="primary" @click="search">
- <template #icon>
- <icon-search />
- </template>
- {{ $t('searchTable.form.search') }}
- </a-button>
- </a-space>
- </a-col>
- </a-row>
- <a-divider style="margin-top: 0" />
- <a-row style="margin-bottom: 16px">
- <a-col :span="12">
- <a-space>
- <a-button type="primary" @click="onAdd">
- <template #icon>
- <icon-plus />
- </template>
- 新增数据
- </a-button>
- <a-button type="success" :disabled="selectedRowKeys.length === 0" @click="onBatchPush">
- <template #icon>
- <icon-upload />
- </template>
- 批量推送 ({{ selectedRowKeys.length }})
- </a-button>
- <a-button type="danger" :disabled="selectedRowKeys.length === 0" @click="onBatchDelete">
- <template #icon>
- <icon-delete />
- </template>
- 批量删除 ({{ selectedRowKeys.length }})
- </a-button>
- </a-space>
- </a-col>
- <a-col :span="12" style="
- display: flex;
- align-items: center;
- justify-content: end;
- ">
- <a-tooltip :content="$t('searchTable.actions.refresh')">
- <div class="action-icon" @click="search"><icon-refresh size="18" /></div>
- </a-tooltip>
- <a-dropdown @select="handleSelectDensity">
- <a-tooltip :content="$t('searchTable.actions.density')">
- <div class="action-icon"><icon-line-height size="18" /></div>
- </a-tooltip>
- <template #content>
- <a-doption v-for="item in densityList" :key="item.value" :value="item.value"
- :class="{ active: item.value === size }">
- <span>{{ item.name }}</span>
- </a-doption>
- </template>
- </a-dropdown>
- <a-tooltip :content="$t('searchTable.actions.columnSetting')">
- <a-popover trigger="click" position="bl" @popup-visible-change="popupVisibleChange">
- <div class="action-icon"><icon-settings size="18" /></div>
- <template #content>
- <div id="tableSetting">
- <div v-for="(item, index) in showColumns" :key="item.dataIndex" class="setting">
- <div style="
- margin-right: 4px;
- cursor: move;
- ">
- <icon-drag-arrow />
- </div>
- <div>
- <a-checkbox v-model="item.checked" @change="
- handleChange(
- $event,
- item as TableColumnData,
- index
- )
- ">
- </a-checkbox>
- </div>
- <div class="title">
- {{
- item.title === '#'
- ? '序列号'
- : item.title
- }}
- </div>
- </div>
- </div>
- </template>
- </a-popover>
- </a-tooltip>
- </a-col>
- </a-row>
- <a-table row-key="id" :loading="loading" :pagination="pagination"
- :columns="(cloneColumns as TableColumnData[])" :data="renderData" :bordered="{ headerCell: true }"
- :size="size" @page-change="onPageChange" :scroll="{ x: 1700, y: tableHeight }"
- v-model:selectedKeys="selectedRowKeys" :row-selection="{
- type: 'checkbox',
- showCheckedAll: true
- }">
- <template #index="{ record }">
- {{ record.id }}
- </template>
- <template #type="{ record }">
- <div>{{ getTypeName(record.type) }}</div>
- <a-typography-text type="secondary">{{ getSubTypeName(record.sub_type) }}
- </a-typography-text>
- </template>
- <template #date="{ record }">
- {{ dayjs(record.report_date).format('YYYY-MM-DD') }}
- </template>
- <template #platform="{ record }">
- {{ getPlatformNameById(record.platform) }}
- </template>
- <template #adId="{ record }">
- <div v-if="record.adId">
- 广告Id:<a-tag color="blue" bordered> {{ record.adId }} </a-tag>
- </div>
- <div v-if="record.adzoneId">
- 策略Id:<a-tag color="blue" bordered> {{ record.adzoneId }} </a-tag>
- </div>
- </template>
- <template #status="{ record }">
- <div>
- <a-tag :color="getStatusColor(record.status)" bordered>
- {{ getStatusText(record.status) }}
- </a-tag>
- <div class="status-extra-info" v-if="record.last_push_time || record.request_id">
- <div v-if="record.request_id" class="text-ellipsis">
- <span class="text-gray">标识: </span>
- {{ record.request_id }}
- </div>
- </div>
- </div>
- </template>
- <template #time="{ record }">
- <div style="color:gray;">
- <div>添加:{{ dayjs(record.create_time).format('MM-DD HH:mm') }}</div>
- <div>更新:{{ dayjs(record.update_time).format('MM-DD HH:mm') }}</div>
- <div v-if="record.last_push_time">推送:{{ dayjs(record.last_push_time).format('MM-DD HH:mm') }}
- </div>
- </div>
- </template>
- <template #request_metrics="{ record }">
- <div>请求量: <template v-if="record.req_count == 0 || record.req_count == null">-</template><a-tag
- v-else color="green">{{ record.req_count }}</a-tag></div>
- <div>DP转链: <template
- v-if="record.dp_transition_succ_count == 0 || record.dp_transition_succ_count == null">-</template><a-tag
- v-else color="green">{{ record.dp_transition_succ_count }}</a-tag></div>
- <div>商业转链: <template
- v-if="record.transition_succ_count == 0 || record.transition_succ_count == null">-</template><a-tag
- v-else color="green">{{ record.transition_succ_count }}</a-tag></div>
- </template>
- <template #expose_metrics="{ record }">
- <div>曝光PV: <template
- v-if="record.expose_count == 0 || record.expose_count == null">-</template><a-tag v-else
- color="green">{{ record.expose_count }}</a-tag></div>
- <div>曝光UV: <template
- v-if="record.expose_user_count == 0 || record.expose_user_count == null">-</template><a-tag
- v-else color="green">{{ record.expose_user_count }}</a-tag></div>
- </template>
- <template #click_metrics="{ record }">
- <div>点击PV: <template v-if="record.click_count == 0 || record.click_count == null">-</template><a-tag
- v-else color="green">{{ record.click_count }}</a-tag></div>
- <div>点击UV: <template
- v-if="record.click_user_count == 0 || record.click_user_count == null">-</template><a-tag
- v-else color="green">{{ record.click_user_count }}</a-tag></div>
- </template>
- <template #app_metrics="{ record }">
- <div>唤端PV: <template
- v-if="record.app_open_count == 0 || record.app_open_count == null">-</template><a-tag v-else
- color="green">{{ record.app_open_count }}</a-tag></div>
- <div>唤端UV: <template
- v-if="record.app_open_user_count == 0 || record.app_open_user_count == null">-</template><a-tag
- v-else color="green">{{ record.app_open_user_count }}</a-tag></div>
- </template>
- <template #order_metrics="{ record }">
- <div>订单PV: <template v-if="record.order_count == 0 || record.order_count == null">-</template><a-tag
- v-else color="green">{{ record.order_count }}</a-tag></div>
- <div>订单UV: <template
- v-if="record.order_user_count == 0 || record.order_user_count == null">-</template><a-tag
- v-else color="green">{{ record.order_user_count }}</a-tag></div>
- </template>
- <template #income_metrics="{ record }">
- <div>GMV: <template v-if="record.gmv == 0 || record.gmv == null">-</template><a-tag v-else
- color="green">{{ record.gmv }}元</a-tag></div>
- <div>预估收入: <template v-if="record.income == 0 || record.income == null">-</template><a-tag v-else
- color="green">{{
- record.income }}元</a-tag></div>
- </template>
- <template #settlement="{ record }">
- <a-tag :color="record.is_settlement ? 'green' : 'orange'" bordered>
- {{ record.is_settlement ? '已结算' : '未结算' }}
- </a-tag>
- </template>
- <template #command="{ record }">
- <a-space>
- <a-button type="primary" @click="onEdit(record)">
- 编辑
- </a-button>
- <a-button status="danger" @click="onDelete(record)">
- 删除
- </a-button>
- <a-button status="success" @click="onPush(record)">
- 推送
- </a-button>
- </a-space>
- </template>
- </a-table>
- </a-card>
- <a-modal v-model:visible="visible" :title="isEdit ? '修改数据' : '新建数据'" @ok="handleSubmit" @cancel="handleCancel"
- width="800px">
- <a-form :model="editForm" layout="vertical">
- <a-row :gutter="24">
- <a-col :span="12">
- <a-form-item label="报告日期" required>
- <a-date-picker v-model="editForm.report_date" style="width: 100%;" value-format="YYYY-MM-DD"
- format="YYYY-MM-DD" show-time-default-value="00:00:00" />
- </a-form-item>
- </a-col>
- <a-col :span="12">
- <a-form-item label="是否结算" required>
- <a-switch v-model="editForm.is_settlement" :checked-value="true" :unchecked-value="false"
- checked-text="已结算" unchecked-text="未结算" />
- </a-form-item>
- </a-col>
- <a-col :span="12">
- <a-form-item label="投放场景" required>
- <a-select v-model="editForm.type" :options="typeOptions" placeholder="请选择投放场景" />
- </a-form-item>
- </a-col>
- <a-col :span="12">
- <a-form-item label="子场景" required>
- <a-select v-model="editForm.sub_type" :options="subTypeOptions" placeholder="请选择子场景"
- allow-create allow-search />
- </a-form-item>
- </a-col>
- <a-col :span="8">
- <a-form-item label="请求量" required>
- <a-input-number v-model="editForm.req_count" placeholder="请输入请求量" :min="0"
- style="width: 100%;" />
- </a-form-item>
- </a-col>
- <a-col :span="8">
- <a-form-item label="DP转链成功数" required>
- <a-input-number v-model="editForm.dp_transition_succ_count" placeholder="请输入DP转链成功数"
- :min="0" style="width: 100%;" />
- </a-form-item>
- </a-col>
- <a-col :span="8">
- <a-form-item label="商业转链成功数" required>
- <a-input-number v-model="editForm.transition_succ_count" placeholder="请输入商业转链成功数" :min="0"
- style="width: 100%;" />
- </a-form-item>
- </a-col>
- <a-col :span="6">
- <a-form-item label="曝光PV" required>
- <a-input-number v-model="editForm.expose_count" placeholder="请输入曝光PV" :min="0"
- style="width: 100%;" />
- </a-form-item>
- </a-col>
- <a-col :span="6">
- <a-form-item label="曝光UV" required>
- <a-input-number v-model="editForm.expose_user_count" placeholder="请输入曝光UV" :min="0"
- style="width: 100%;" />
- </a-form-item>
- </a-col>
- <a-col :span="6">
- <a-form-item label="点击PV" required>
- <a-input-number v-model="editForm.click_count" placeholder="请输入点击PV" :min="0"
- style="width: 100%;" />
- </a-form-item>
- </a-col>
- <a-col :span="6">
- <a-form-item label="点击UV" required>
- <a-input-number v-model="editForm.click_user_count" placeholder="请输入点击UV" :min="0"
- style="width: 100%;" />
- </a-form-item>
- </a-col>
- <a-col :span="6">
- <a-form-item label="唤端PV" required>
- <a-input-number v-model="editForm.app_open_count" placeholder="请输入唤端PV" :min="0"
- style="width: 100%;" />
- </a-form-item>
- </a-col>
- <a-col :span="6">
- <a-form-item label="唤端UV" required>
- <a-input-number v-model="editForm.app_open_user_count" placeholder="请输入唤端UV" :min="0"
- style="width: 100%;" />
- </a-form-item>
- </a-col>
- <a-col :span="6">
- <a-form-item label="订单PV" required>
- <a-input-number v-model="editForm.order_count" placeholder="请输入订单PV" :min="0"
- style="width: 100%;" />
- </a-form-item>
- </a-col>
- <a-col :span="6">
- <a-form-item label="订单UV" required>
- <a-input-number v-model="editForm.order_user_count" placeholder="请输入订单UV" :min="0"
- style="width: 100%;" />
- </a-form-item>
- </a-col>
- <a-col :span="12">
- <a-form-item label="GMV(元)" required>
- <a-input-number v-model="editForm.gmv" placeholder="请输入GMV" :min="0" :precision="2"
- style="width: 100%;" />
- </a-form-item>
- </a-col>
- <a-col :span="12">
- <a-form-item label="预估收入(元)" required>
- <a-input-number v-model="editForm.income" placeholder="请输入预估收入" :min="0" :precision="2"
- style="width: 100%;" />
- </a-form-item>
- </a-col>
- <a-col :span="12">
- <a-form-item label="平台" required>
- <a-select v-model="editForm.platform" :options="platformOptions" placeholder="请选择平台"
- allow-search :filter-option="filterPlatformOption" />
- </a-form-item>
- </a-col>
- <a-col :span="12">
- <a-form-item label="推送状态">
- <a-select v-model="editForm.status" :options="[
- { label: '未推送', value: 0 },
- { label: '推送成功', value: 1 },
- { label: '推送失败', value: 2 },
- { label: '手动推送', value: 3 }
- ]">
- <template #label="{ data }">
- <span :style="{ color: getStatusColor(data.value) }">{{ data.label }}</span>
- </template>
- </a-select>
- </a-form-item>
- </a-col>
- <a-col :span="12">
- <a-form-item label="广告Id">
- <a-input v-model="editForm.adId" placeholder="请输入广告Id" />
- </a-form-item>
- </a-col>
- <a-col :span="12">
- <a-form-item label="分流id/策略id">
- <a-input v-model="editForm.adzoneId" placeholder="请输入分流id/策略id" />
- </a-form-item>
- </a-col>
- </a-row>
- </a-form>
- </a-modal>
- <a-modal v-model:visible="batchPushModalVisible" :title="batchPushModalLoading ? '推送中' : '推送完成'"
- :mask-closable="false" :closable="!batchPushModalLoading" @before-ok="handleBatchPushComplete" ok-text="完成"
- :ok-button-props="{ disabled: batchPushModalLoading }" :cancel-button-props="{ disabled: true }">
- <div style="text-align: center; padding: 20px 0;">
- <div v-if="batchPushModalLoading">
- <a-spin />
- <div style="margin-top: 15px; font-size: 16px;">
- {{ batchPushProgress }}/{{ batchPushTotal }}条数据已处理
- </div>
- <div style="margin-top: 10px;">
- 成功:<span style="color: #00b42a; font-weight: bold;">{{ batchPushSuccess }}</span>
- 失败:<span style="color: #f53f3f; font-weight: bold;">{{ batchPushFail }}</span>
- </div>
- <div style="margin-top: 20px; color: #86909c;">
- 处理中,请稍候...
- </div>
- </div>
- <div v-else>
- <a-result status="success" title="推送完成">
- <template #subtitle>
- <div>
- 共处理 {{ batchPushTotal }} 条数据,
- 成功:<span style="color: #00b42a; font-weight: bold;">{{ batchPushSuccess }}</span>
- 失败:<span style="color: #f53f3f; font-weight: bold;">{{ batchPushFail }}</span>
- </div>
- </template>
- </a-result>
- </div>
- </div>
- </a-modal>
- </div>
- </template>
- <script lang="ts" setup>
- import { useRouter } from 'vue-router';
- import { computed, ref, reactive, watch, nextTick, onMounted, onUnmounted } from 'vue';
- import { useI18n } from 'vue-i18n';
- import dayjs from 'dayjs';
- import useLoading from '@/hooks/loading';
- import { Pagination } from '@/types/global';
- import { Modal, Message } from '@arco-design/web-vue';
- import type { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
- import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
- import cloneDeep from 'lodash/cloneDeep';
- import Sortable from 'sortablejs';
- import {
- PushDataReportParams, PushDataReportRecord, queryReportList, saveReport, pushReport, deleteReport
- } from '@/api/dataReport';
- import { platformList, getPlatformNameById } from '@/constants/pushPlatform';
- type SizeProps = 'mini' | 'small' | 'medium' | 'large';
- type Column = TableColumnData & { checked?: true };
- const generateFormModel = () => {
- return {
- type: '',
- platform: '',
- status: -1,
- is_settlement: -1,
- report_date: dayjs().format('YYYY-MM-DD'),
- sort: 'report_date',
- order: 'descending',
- };
- };
- const router = useRouter();
- const { loading, setLoading } = useLoading(true);
- const { t } = useI18n();
- const renderData = ref<PushDataReportRecord[]>([]);
- const formModel = ref(generateFormModel());
- const cloneColumns = ref<Column[]>([]);
- const showColumns = ref<Column[]>([]);
- const size = ref<SizeProps>('small');
- const basePagination: Pagination = {
- current: 1,
- pageSize: 50,
- };
- const pagination = reactive({
- ...basePagination,
- });
- const typeOptions = computed<SelectOptionData[]>(() => [
- {
- label: '全部',
- value: '',
- },
- {
- label: '口令',
- value: 1,
- },
- {
- label: 'SDK优惠券',
- value: 2,
- },
- {
- label: '应用建议卡',
- value: 3,
- },
- {
- label: '购物助手',
- value: 4,
- },
- {
- label: '侧边栏',
- value: 7,
- },
- {
- label: '搜同款',
- value: 10,
- },
- ]);
- const densityList = computed(() => [
- {
- name: t('searchTable.size.mini'),
- value: 'mini',
- },
- {
- name: t('searchTable.size.small'),
- value: 'small',
- },
- {
- name: t('searchTable.size.medium'),
- value: 'medium',
- },
- {
- name: t('searchTable.size.large'),
- value: 'large',
- },
- ]);
- // 推送状态选项
- const statusOptions = computed<SelectOptionData[]>(() => [
- {
- label: '全部状态',
- value: -1,
- },
- {
- label: '未推送',
- value: 0,
- },
- {
- label: '推送成功',
- value: 1,
- },
- {
- label: '推送失败',
- value: 2,
- },
- {
- label: '手动推送',
- value: 3,
- },
- ]);
- // 结算状态选项
- const settlementOptions = computed<SelectOptionData[]>(() => [
- {
- label: '全部状态',
- value: -1,
- },
- {
- label: '已结算',
- value: 1,
- },
- {
- label: '未结算',
- value: 0,
- },
- ]);
- // 获取投放场景名称
- const getTypeName = (type: number) => {
- switch (type) {
- case 1:
- return '口令';
- case 2:
- return 'SDK优惠券';
- case 3:
- return '应用建议卡';
- case 4:
- return '购物助手';
- case 7:
- return '侧边栏';
- case 10:
- return '搜同款';
- default:
- return `未知(${type})`;
- }
- };
- // 获取子场景名称
- const getSubTypeName = (subType: number) => {
- switch (subType) {
- case 100000:
- return '口令';
- case 100001:
- return '应用内优惠券';
- case 300001:
- return '应用建议卡';
- case 400002:
- return '全搜';
- case 400003:
- return '分页符';
- case 500008:
- return '浏览器DP';
- case 700001:
- return '侧边栏';
- case 1000001:
- return '搜同款拍一拍';
- case 1000002:
- return '搜同款相册';
- case 1000003:
- return '搜同款助力栏';
- case 1000004:
- return '搜同款默认';
- case 1000005:
- return '搜同款识屏';
- case 1000006:
- return '搜同款小布CUI拍一拍';
- case 1000007:
- return '搜同款小布CUI客户端';
- default:
- return `未知(${subType})`;
- }
- };
- // 子场景选项,根据当前选择的type动态生成
- const subTypeOptions = computed(() => {
- const baseOptions: SelectOptionData[] = [];
- switch (editForm.value.type) {
- case 1:
- baseOptions.push({ label: '口令', value: 100000 });
- break;
- case 2:
- baseOptions.push({ label: '应用内优惠券', value: 100001 });
- break;
- case 3:
- baseOptions.push({ label: '应用建议卡', value: 300001 });
- break;
- case 4:
- baseOptions.push(
- { label: '全搜', value: 400002 },
- { label: '分页符', value: 400003 }
- );
- break;
- case 5:
- baseOptions.push({ label: '浏览器DP', value: 500008 });
- break;
- case 7:
- baseOptions.push({ label: '侧边栏', value: 700001 });
- break;
- case 10:
- baseOptions.push(
- { label: '搜同款拍一拍', value: 1000001 },
- { label: '搜同款相册', value: 1000002 },
- { label: '搜同款助力栏', value: 1000003 },
- { label: '搜同款默认', value: 1000004 },
- { label: '搜同款识屏', value: 1000005 },
- { label: '搜同款小布CUI拍一拍', value: 1000006 },
- { label: '搜同款小布CUI客户端', value: 1000007 }
- );
- break;
- default:
- // 默认不提供选项,允许用户自定义添加
- break;
- }
- return baseOptions;
- });
- const columns = computed<TableColumnData[]>(() => [
- {
- title: '#',
- dataIndex: 'index',
- slotName: 'index',
- width: 80,
- fixed: 'left',
- },
- {
- title: '报告日期',
- slotName: 'date',
- width: 120,
- },
- {
- title: '投放场景',
- slotName: 'type',
- width: 150,
- },
- {
- title: 'adId/adzoneId',
- slotName: 'adId',
- width: 150,
- },
- {
- title: '平台',
- slotName: 'platform',
- width: 200,
- },
- {
- title: '推送状态',
- slotName: 'status',
- width: 200,
- },
- {
- title: '请求量/转链数',
- slotName: 'request_metrics',
- width: 170,
- },
- {
- title: '曝光',
- slotName: 'expose_metrics',
- width: 170,
- },
- {
- title: '点击',
- slotName: 'click_metrics',
- width: 170,
- },
- {
- title: '唤端',
- slotName: 'app_metrics',
- width: 170,
- },
- {
- title: '订单',
- slotName: 'order_metrics',
- width: 170,
- },
- {
- title: '收益',
- slotName: 'income_metrics',
- width: 200,
- },
- {
- title: '时间',
- slotName: 'time',
- width: 180,
- },
- {
- title: '结算状态',
- slotName: 'settlement',
- width: 100,
- },
- {
- title: '操作',
- slotName: 'command',
- width: 240,
- fixed: 'right',
- },
- ]);
- const fetchData = async (
- params: PushDataReportParams = {
- current: 1,
- pageSize: 50,
- sort: 'report_date',
- order: 'descending',
- getTotal: true,
- report_date: dayjs().format('YYYY-MM-DD'),
- },
- tryPreviousDay = true
- ) => {
- setLoading(true);
- try {
- const { data } = await queryReportList(params);
- if (!data) return;
- renderData.value = data.list;
- pagination.current = data.page;
- pagination.total = data.count;
- // Clear selection when data is refreshed
- selectedRowKeys.value = [];
- selectedRows.value = [];
- // Recalculate table height after data is loaded
- nextTick(() => {
- calculateTableHeight();
- });
- if (tryPreviousDay && data.list.length === 0 && !params.type && params.report_date === dayjs().format('YYYY-MM-DD')) {
- const prevDay = dayjs().subtract(1, 'day').format('YYYY-MM-DD');
- formModel.value.report_date = prevDay;
- // 重置分页到第一页
- pagination.current = 1;
- fetchData({
- ...params,
- current: 1,
- report_date: prevDay
- }, false);
- Message.info(`当天无数据,已自动加载 ${prevDay} 的数据`);
- }
- } catch (err) {
- console.log(err);
- } finally {
- setLoading(false);
- }
- };
- const search = () => {
- const statusNumber = Number(formModel.value.status);
- const isSettlementValue = formModel.value.is_settlement;
- fetchData({
- ...basePagination,
- ...formModel.value,
- type: formModel.value.type ? Number(formModel.value.type) : undefined,
- platform: formModel.value.platform ? Number(formModel.value.platform) : undefined,
- status: statusNumber,
- is_settlement: isSettlementValue,
- getTotal: true,
- } as PushDataReportParams);
- };
- const onPageChange = (current: number) => {
- const statusNumber = Number(formModel.value.status);
- const isSettlementValue = Number(formModel.value.is_settlement);
- fetchData({
- ...basePagination,
- ...formModel.value,
- current,
- type: formModel.value.type ? Number(formModel.value.type) : undefined,
- platform: formModel.value.platform ? Number(formModel.value.platform) : undefined,
- status: statusNumber,
- is_settlement: isSettlementValue,
- getTotal: true,
- } as PushDataReportParams);
- };
- const visible = ref(false);
- const isEdit = ref(false);
- const editForm = ref<Partial<PushDataReportRecord>>({
- report_date: new Date(),
- is_settlement: false,
- type: 3,
- sub_type: 300001,
- platform: 1,
- distribution: 0,
- req_count: 0,
- dp_transition_succ_count: 0,
- transition_succ_count: 0,
- expose_count: 0,
- expose_user_count: 0,
- click_count: 0,
- click_user_count: 0,
- app_open_count: 0,
- app_open_user_count: 0,
- order_count: 0,
- order_user_count: 0,
- gmv: 0,
- income: 0,
- request_id: '',
- adId: '',
- adzoneId: '',
- });
- const onEdit = (current: PushDataReportRecord) => {
- console.log('Editing record:', current);
- isEdit.value = true;
- // 处理日期,确保它是正确的格式
- let formattedDate = current.report_date;
- if (typeof current.report_date === 'string') {
- formattedDate = new Date(current.report_date);
- }
- // 确保所有数值字段都是数字类型
- const ensureNumber = (value: any) => {
- return value !== undefined && value !== null ? Number(value) : 0;
- };
- editForm.value = {
- id: current.id,
- report_date: formattedDate,
- is_settlement: Boolean(current.is_settlement),
- type: ensureNumber(current.type),
- sub_type: ensureNumber(current.sub_type),
- platform: ensureNumber(current.platform || 1),
- platform_name: current.platform_name || getPlatformNameById(ensureNumber(current.platform || 1)),
- distribution: ensureNumber(current.distribution || 0),
- req_count: ensureNumber(current.req_count),
- dp_transition_succ_count: ensureNumber(current.dp_transition_succ_count),
- transition_succ_count: ensureNumber(current.transition_succ_count),
- expose_count: ensureNumber(current.expose_count),
- expose_user_count: ensureNumber(current.expose_user_count),
- click_count: ensureNumber(current.click_count),
- click_user_count: ensureNumber(current.click_user_count),
- app_open_count: ensureNumber(current.app_open_count),
- app_open_user_count: ensureNumber(current.app_open_user_count),
- order_count: ensureNumber(current.order_count),
- order_user_count: ensureNumber(current.order_user_count),
- gmv: ensureNumber(current.gmv),
- income: ensureNumber(current.income),
- request_id: current.request_id || '',
- status: ensureNumber(current.status),
- last_push_time: current.last_push_time,
- adId: current.adId,
- adzoneId: current.adzoneId,
- };
- // 确保子场景与投放场景匹配
- nextTick(() => {
- // 在DOM更新后检查子场景是否与投放场景匹配
- if (editForm.value.type !== undefined) {
- const { type } = editForm.value;
- // 根据类型设置对应的默认子场景值
- if (type === 1 && editForm.value.sub_type !== 100000) {
- editForm.value.sub_type = 100000;
- } else if (type === 3 && editForm.value.sub_type !== 300001) {
- editForm.value.sub_type = 300001;
- } else if (type === 4 && !([400002, 400003].includes(editForm.value.sub_type as number))) {
- editForm.value.sub_type = 400002;
- } else if (type === 5 && editForm.value.sub_type !== 500008) {
- editForm.value.sub_type = 500008;
- } else if (type === 7 && editForm.value.sub_type !== 700001) {
- editForm.value.sub_type = 700001;
- } else if (type === 10 && !([1000001, 1000002, 1000003, 1000004, 1000005, 1000006, 1000007].includes(editForm.value.sub_type as number))) {
- editForm.value.sub_type = 1000001;
- }
- }
- });
- console.log('Edit form populated:', editForm.value);
- visible.value = true;
- };
- const onAdd = () => {
- isEdit.value = false;
- const initialPlatform = 1; // 默认淘宝
- editForm.value = {
- report_date: new Date(),
- is_settlement: false,
- type: 3,
- sub_type: 300001,
- platform: initialPlatform,
- platform_name: getPlatformNameById(initialPlatform),
- distribution: 0,
- req_count: 0,
- dp_transition_succ_count: 0,
- transition_succ_count: 0,
- expose_count: 0,
- expose_user_count: 0,
- click_count: 0,
- click_user_count: 0,
- app_open_count: 0,
- app_open_user_count: 0,
- order_count: 0,
- order_user_count: 0,
- gmv: 0,
- income: 0,
- request_id: '',
- adId: '',
- adzoneId: '',
- };
- visible.value = true;
- };
- const onPush = async (record: PushDataReportRecord) => {
- try {
- const response = await pushReport(record.id);
- if (response.data.success) {
- Message.success(response.data.msg || '推送数据成功');
- // 推送成功后刷新当前数据
- search();
- } else {
- Message.error(response.data.msg || '推送数据失败');
- }
- } catch (error) {
- Message.error('请求失败');
- }
- };
- // 初始加载数据
- fetchData();
- const handleSelectDensity = (
- val: string | number | Record<string, any> | undefined,
- ) => {
- size.value = val as SizeProps;
- };
- const handleChange = (
- checked: boolean | (string | boolean | number)[],
- column: Column,
- index: number
- ) => {
- if (!checked) {
- cloneColumns.value = showColumns.value.filter(
- (item) => item.dataIndex !== column.dataIndex
- );
- } else {
- cloneColumns.value.splice(index, 0, column);
- }
- };
- const exchangeArray = <T extends Array<any>>(
- array: T,
- beforeIdx: number,
- newIdx: number,
- isDeep = false
- ): T => {
- const newArray = isDeep ? cloneDeep(array) : array;
- if (beforeIdx > -1 && newIdx > -1) {
- // 先替换后面的,然后拿到替换的结果替换前面的
- newArray.splice(
- beforeIdx,
- 1,
- newArray.splice(newIdx, 1, newArray[beforeIdx]).pop()
- );
- }
- return newArray;
- };
- const popupVisibleChange = (val: boolean) => {
- if (val) {
- nextTick(() => {
- const el = document.getElementById(
- 'tableSetting'
- ) as HTMLElement;
- const sortable = new Sortable(el, {
- onEnd(e: any) {
- const { oldIndex, newIndex } = e;
- exchangeArray(cloneColumns.value, oldIndex, newIndex);
- exchangeArray(showColumns.value, oldIndex, newIndex);
- },
- });
- });
- }
- };
- const handleSubmit = async () => {
- try {
- // 使用统一的saveReport接口
- const response = await saveReport(editForm.value);
- if (response.data.success) {
- Message.success(response.data.msg || '操作成功');
- visible.value = false;
- search(); // 重新加载当前筛选条件下的数据
- } else {
- Message.error(response.data.msg || '操作失败');
- }
- } catch (error) {
- Message.error('请求失败');
- }
- };
- const handleCancel = () => {
- visible.value = false;
- };
- watch(
- () => columns.value,
- (val) => {
- cloneColumns.value = cloneDeep(val);
- cloneColumns.value.forEach((item) => {
- item.checked = true;
- });
- showColumns.value = cloneDeep(cloneColumns.value);
- },
- { deep: true, immediate: true }
- );
- // 当切换type时,更新子场景值
- watch(
- () => editForm.value.type,
- (newType) => {
- if (newType === 1) {
- editForm.value.sub_type = 100000;
- } else if (newType === 2) {
- editForm.value.sub_type = 100001;
- } else if (newType === 3) {
- editForm.value.sub_type = 300001;
- } else if (newType === 4) {
- editForm.value.sub_type = 400002;
- } else if (newType === 7) {
- editForm.value.sub_type = 700001;
- } else if (newType === 10) {
- editForm.value.sub_type = 1000001;
- }
- }
- );
- // 添加自动加载数据的方法
- const onTypeOrDateChange = () => {
- search();
- };
- const getStatusText = (status: number) => {
- switch (status) {
- case 0:
- return '未推送';
- case 1:
- return '推送成功';
- case 2:
- return '推送失败';
- case 3:
- return '手动推送';
- default:
- return `未知(${status})`;
- }
- };
- const getStatusColor = (status: number) => {
- switch (status) {
- case 0:
- return 'gray';
- case 1:
- return 'green';
- case 2:
- return 'red';
- case 3:
- return 'blue';
- default:
- return '';
- }
- };
- // 使用导入的platformList
- const platformOptions = computed<SelectOptionData[]>(() => platformList);
- // 监听platform变化,自动更新platform_name
- watch(
- () => editForm.value.platform,
- (newPlatform) => {
- if (newPlatform !== undefined) {
- editForm.value.platform_name = getPlatformNameById(newPlatform);
- }
- }
- );
- // Add this before the platformOptions declaration
- const filterPlatformOption = (inputValue: string, option: SelectOptionData) => {
- return option.label.toLowerCase().includes(inputValue.toLowerCase()) ||
- String(option.value).includes(inputValue);
- };
- const clearPlatform = () => {
- formModel.value.platform = '';
- search(); // Trigger search after clearing
- };
- // Add table height calculation
- const tableHeight = ref(0);
- onMounted(() => {
- calculateTableHeight();
- window.addEventListener('resize', calculateTableHeight);
- });
- // Cleanup the event listener when component is unmounted
- onUnmounted(() => {
- window.removeEventListener('resize', calculateTableHeight);
- });
- const calculateTableHeight = () => {
- // Get viewport height
- const vh = window.innerHeight;
- // Get search form height dynamically
- const searchArea = document.querySelector('.general-card')?.querySelector('form')?.parentElement?.parentElement;
- const searchHeight = searchArea ? searchArea.offsetHeight + 32 : 160; // 32px for margins
- // Subtract header(64px), breadcrumb(40px), padding(40px), search area, table header(56px), pagination(56px)
- tableHeight.value = vh - searchHeight - 380;
- };
- // Add after renderData declaration
- const selectedRowKeys = ref<(string | number)[]>([]);
- const selectedRows = ref<PushDataReportRecord[]>([]);
- // 在 setup 中添加这些变量用于跟踪批量推送进度
- const batchPushModalVisible = ref(false);
- const batchPushProgress = ref(0);
- const batchPushTotal = ref(0);
- const batchPushSuccess = ref(0);
- const batchPushFail = ref(0);
- const batchPushModalLoading = ref(false);
- // 修改的批量推送函数
- const onBatchPush = async () => {
- if (selectedRowKeys.value.length === 0) {
- Message.warning('请选择要推送的数据');
- return;
- }
- Modal.confirm({
- title: '批量推送确认',
- content: `确定要推送 ${selectedRowKeys.value.length} 条数据吗?推送过程中请勿关闭页面。`,
- onOk: async () => {
- // 重置进度变量
- batchPushProgress.value = 0;
- batchPushTotal.value = selectedRowKeys.value.length;
- batchPushSuccess.value = 0;
- batchPushFail.value = 0;
- batchPushModalLoading.value = true;
- batchPushModalVisible.value = true;
- // 处理每条数据
- for (let i = 0; i < selectedRowKeys.value.length; i += 1) {
- const id = Number(selectedRowKeys.value[i]);
- try {
- // eslint-disable-next-line no-await-in-loop
- const response = await pushReport(id);
- if (response.data && response.data.success) {
- batchPushSuccess.value += 1;
- } else {
- batchPushFail.value += 1;
- }
- } catch (error) {
- batchPushFail.value += 1;
- }
- // 更新进度
- batchPushProgress.value = i + 1;
- // 使用 nextTick 确保界面刷新
- // eslint-disable-next-line no-await-in-loop
- await nextTick();
- }
- // 任务完成
- batchPushModalLoading.value = false;
- },
- onCancel: () => {
- // 用户取消操作
- }
- });
- };
- // 修改函数类型定义
- const handleBatchPushComplete = (done: () => void) => {
- // 清空选择并刷新数据
- selectedRowKeys.value = [];
- selectedRows.value = [];
- search();
- done(); // 关闭对话框
- };
- // 添加删除方法
- const onDelete = (record: PushDataReportRecord) => {
- Modal.confirm({
- title: '确认删除',
- content: `确定要删除 ID 为 ${record.id} 的数据记录吗?此操作不可恢复。`,
- okText: '删除',
- okButtonProps: { status: 'danger' },
- cancelText: '取消',
- onOk: async () => {
- try {
- const response = await deleteReport(record.id);
- if (response.data.success) {
- Message.success(response.data.msg || '删除成功');
- // 刷新数据列表
- search();
- } else {
- Message.error(response.data.msg || '删除失败');
- }
- } catch (error) {
- Message.error('删除请求失败');
- }
- }
- });
- };
- // 批量删除方法
- const onBatchDelete = () => {
- if (selectedRowKeys.value.length === 0) {
- Message.warning('请选择要删除的数据');
- return;
- }
- Modal.confirm({
- title: '批量删除确认',
- content: `确定要删除选中的 ${selectedRowKeys.value.length} 条数据吗?此操作不可恢复。`,
- okText: '删除',
- okButtonProps: { status: 'danger' },
- cancelText: '取消',
- onOk: async () => {
- let successCount = 0;
- let failCount = 0;
- // 创建进度弹窗
- const progressModal = Modal.info({
- title: '删除中',
- content: `0/${selectedRowKeys.value.length}条数据已处理,请稍候...`,
- okButtonProps: { disabled: true },
- cancelButtonProps: { disabled: true },
- footer: false,
- closable: false,
- maskClosable: false
- });
- // 依次删除每条数据
- for (let i = 0; i < selectedRowKeys.value.length; i += 1) {
- const id = Number(selectedRowKeys.value[i]);
- try {
- // eslint-disable-next-line no-await-in-loop
- const response = await deleteReport(id);
- if (response.data && response.data.success) {
- successCount += 1;
- } else {
- failCount += 1;
- }
- } catch (error) {
- failCount += 1;
- }
- // 更新进度信息
- progressModal.update({
- content: `${i + 1}/${selectedRowKeys.value.length}条数据已处理,成功:${successCount},失败:${failCount},请稍候...`
- });
- }
- // 关闭进度弹窗
- progressModal.close();
- // 显示结果
- Modal.success({
- title: '删除完成',
- content: `已完成所有数据删除,成功:${successCount},失败:${failCount}`,
- onOk: () => {
- // 清空选择并刷新数据
- selectedRowKeys.value = [];
- selectedRows.value = [];
- search();
- }
- });
- }
- });
- };
- </script>
- <script lang="ts">
- export default {
- name: 'PushReport',
- };
- </script>
- <style scoped lang="less">
- .container {
- padding: 0 20px 20px 20px;
- }
- :deep(.general-card) {
- height: 100%;
- }
- .action-icon {
- margin-left: 12px;
- cursor: pointer;
- }
- .active {
- color: #0960bd;
- background-color: #e3f4fc;
- }
- .setting {
- display: flex;
- align-items: center;
- width: 200px;
- .title {
- margin-left: 12px;
- cursor: pointer;
- }
- }
- .status-extra-info {
- font-size: 12px;
- margin-top: 4px;
- max-width: 160px;
- }
- .text-ellipsis {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .text-gray {
- color: #86909c;
- }
- .filter-with-clear {
- position: relative;
- width: 100%;
- .clear-btn {
- position: absolute;
- right: 32px;
- top: 50%;
- transform: translateY(-50%);
- color: #86909c;
- z-index: 2;
- height: 20px;
- width: 20px;
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 0;
- margin-right: 4px;
- &:hover {
- color: #165DFF;
- }
- }
- }
- :deep(.arco-checkbox) {
- z-index: 1;
- pointer-events: auto !important;
- }
- :deep(.arco-table-th-item .arco-checkbox),
- :deep(.arco-table-td .arco-checkbox) {
- pointer-events: auto !important;
- }
- :deep(.arco-modal-body) {
- font-size: 14px;
- line-height: 1.5;
- }
- :deep(.arco-modal-title) {
- font-weight: bold;
- color: var(--color-text-1);
- }
- </style>
|