feat(property-marketing): 新增投放指南详情页功能
refactor(property-marketing): 优化评分图标映射逻辑 feat(property-marketing): 实现投放指南历史记录操作功能 perf(property-marketing): 优化图表数据更新逻辑 feat(property-marketing): 添加时间筛选功能 实现保存投放指南功能 - 优化页面样式和布局
This commit is contained in:
@ -20,12 +20,14 @@
|
||||
"axios": "^1.3.0",
|
||||
"dayjs": "^1.11.7",
|
||||
"echarts": "^5.6.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mitt": "^3.0.0",
|
||||
"normalize.css": "^8.0.1",
|
||||
"pinia": "^2.0.29",
|
||||
"sass": "^1.89.2",
|
||||
"swiper": "^11.2.8",
|
||||
"update": "^0.7.4",
|
||||
"vue": "^3.2.45",
|
||||
"vue-cropper": "^1.1.4",
|
||||
"vue-draggable-plus": "^0.6.0",
|
||||
|
||||
@ -295,13 +295,32 @@ export const getPlacementAccountProjectsTrend = (params = {}) => {
|
||||
export const getPlacementGuide = (params: {}) => {
|
||||
return Http.get(`/v1/placement-account-projects/getGuideList`,params);
|
||||
};
|
||||
//查询投放指南历史
|
||||
export const getPlacementGuideHistory = (params: {}) => {
|
||||
return Http.get(`/v1/placement-account-projects/getGuideListHistory`, params);
|
||||
};
|
||||
|
||||
// 前端定时轮询获取ai检测结果
|
||||
export const getAiResult = (params: {}) => {
|
||||
return Http.get(`/v1/placement-account-projects/getAiResult`, params);
|
||||
};
|
||||
|
||||
export const savePlacementGuide = (params: {}) => {
|
||||
return Http.post(`/v1/placement-account-projects/saveGuideResult`, params);
|
||||
};
|
||||
|
||||
export const getPlacementGuideDetail = (id: string) => {
|
||||
return Http.get(`/v1/placement-account-projects/historylog/${id}`);
|
||||
};
|
||||
|
||||
//删除记录
|
||||
export const deleteHistorylog = (id: string) => {
|
||||
return Http.delete(`/v1/placement-account-projects/historylog/${id}`);
|
||||
};
|
||||
|
||||
// 投放账号-列表
|
||||
export const getPlacementAccountsList = (params = {}) => {
|
||||
return Http.get('/v1/placement-accounts/list', params);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -129,12 +129,23 @@ const COMPONENTS: AppRouteRecordRaw[] = [
|
||||
path: 'investmentGuidelines',
|
||||
name: 'PutAccountInvestmentGuidelines',
|
||||
meta: {
|
||||
locale: '平台投放指南',
|
||||
locale: '投放指南',
|
||||
requiresAuth: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
component: () => import('@/views/property-marketing/put-account/investment-guidelines'),
|
||||
},
|
||||
{
|
||||
path: 'detail/:id',
|
||||
name: 'guideDetail',
|
||||
meta: {
|
||||
locale: '投放指南详情',
|
||||
requiresAuth: true,
|
||||
hideInMenu: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
component: () => import('@/views/property-marketing/put-account/investment-guidelines/detail'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
* @Date: 2025-06-27 17:36:31
|
||||
*/
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import lodash from 'lodash';
|
||||
export function toFixed(num: number | string, n: number): number {
|
||||
return parseFloat(parseFloat(num.toString()).toFixed(n));
|
||||
}
|
||||
@ -106,3 +106,12 @@ export function downloadByUrl(url: string, filename?: string) {
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查数据是否为空
|
||||
* @param data
|
||||
*/
|
||||
export function isEmpty(data: any): boolean {
|
||||
// 使用 lodash 的 isEmpty 方法检查数据是否为空
|
||||
return lodash.isEmpty(data);
|
||||
}
|
||||
|
||||
@ -43,7 +43,6 @@ let chartInstance: echarts.ECharts | null = null;
|
||||
|
||||
const xAxisData = props.xAxisData;
|
||||
const seriesData = props.seriesData;
|
||||
console.log(seriesData, 'seriesData');
|
||||
|
||||
const initChart = () => {
|
||||
if (!chart.value) return;
|
||||
@ -53,8 +52,6 @@ const initChart = () => {
|
||||
}
|
||||
chartInstance = echarts.init(chart.value);
|
||||
|
||||
console.log('init');
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
@ -98,9 +95,14 @@ const initChart = () => {
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.seriesData,
|
||||
() => {
|
||||
initChart(); //重新渲染的方法
|
||||
() => [props.xAxisData, props.seriesData],
|
||||
([newXAxis, newSeries]) => {
|
||||
if (chartInstance) {
|
||||
chartInstance.setOption({
|
||||
xAxis: newXAxis,
|
||||
series: newSeries,
|
||||
});
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
@ -45,7 +45,7 @@
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">时间筛选</span>
|
||||
<a-space class="w-240px">
|
||||
<a-range-picker size="medium" allow-clear format="YYYY-MM-DD HH:mm" class="w-100%" />
|
||||
<a-range-picker v-model="query.data_time" size="medium" allow-clear format="YYYY-MM-DD" class="w-100%" />
|
||||
</a-space>
|
||||
</div>
|
||||
<a-button class="w-84px search-btn mr-12px" size="medium" @click="handleSearch">
|
||||
@ -156,6 +156,7 @@ const query = reactive({
|
||||
names: '',
|
||||
platform: '',
|
||||
operator_id: '',
|
||||
data_time: [],
|
||||
});
|
||||
const xhlEcharts = reactive({});
|
||||
const getAccountsTrends = async () => {
|
||||
|
||||
@ -25,23 +25,12 @@
|
||||
<div class="overall-strategy">
|
||||
<span class="placement-optimization-title">人群分析</span>
|
||||
<a-space direction="vertical">
|
||||
<a-space class="placement-optimization-str">
|
||||
<span>
|
||||
<img :src="getStarIcon(1)" style="width: 25px; height: 17px" />
|
||||
18-24岁女性,兴趣为“美妆/穿搭”,一线城市,抖音平台 ROI 3.2</span
|
||||
>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span>
|
||||
<img :src="getStarIcon(2)" style="width: 25px; height: 17px" />
|
||||
25-34岁男性,兴趣为“数码产品”,二线城市,巨量引擎 ROI 2.8</span
|
||||
>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span>
|
||||
<img :src="getStarIcon(3)" style="width: 25px; height: 17px" />
|
||||
18-24岁男性,兴趣为“运动/健身”,三线城市,抖音 ROI 2.3</span
|
||||
>
|
||||
<a-space
|
||||
v-if="getSubmoduleContent(MODEL_PERFORMANCE_ANALYSIS, '人群分析').length > 0"
|
||||
v-for="(item, index) in getSubmoduleContent(MODEL_PERFORMANCE_ANALYSIS, '人群分析')"
|
||||
:key="index"
|
||||
>
|
||||
<span><img :src="getStarIcon(index)" style="width: 25px; height: 17px" />{{ item }}</span>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</div>
|
||||
@ -50,23 +39,12 @@
|
||||
<div class="overall-strategy">
|
||||
<span class="placement-optimization-title">投放素材</span>
|
||||
<a-space direction="vertical">
|
||||
<a-space class="placement-optimization-str">
|
||||
<span>
|
||||
<img :src="getStarIcon(1)" style="width: 25px; height: 17px" />
|
||||
图文风格 + 明确福利点,CTR 3.2%、CVR 8.5%</span
|
||||
>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span>
|
||||
<img :src="getStarIcon(1)" style="width: 25px; height: 17px" />
|
||||
场景短视频 + 明确人设定位,CTR 2.7%、CVR 7.1%</span
|
||||
>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span>
|
||||
<img :src="getStarIcon(1)" style="width: 25px; height: 17px" />
|
||||
口播讲解类 + 产品对比,CTR 2.1%、CVR 6.0%</span
|
||||
>
|
||||
<a-space
|
||||
v-if="getSubmoduleContent(MODEL_PERFORMANCE_ANALYSIS, '投放素材').length > 0"
|
||||
v-for="(item, index) in getSubmoduleContent(MODEL_PERFORMANCE_ANALYSIS, '投放素材')"
|
||||
:key="index"
|
||||
>
|
||||
<span><img :src="getStarIcon(index)" style="width: 25px; height: 17px" />{{ item }}</span>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</div>
|
||||
@ -77,23 +55,12 @@
|
||||
<div class="overall-strategy">
|
||||
<span class="placement-optimization-title">投放时段</span>
|
||||
<a-space direction="vertical">
|
||||
<a-space class="placement-optimization-str">
|
||||
<span>
|
||||
<img :src="getStarIcon(1)" style="width: 25px; height: 17px" />
|
||||
晚高峰时段(19:00–21:00),ROI 3.1</span
|
||||
>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span>
|
||||
<img :src="getStarIcon(1)" style="width: 25px; height: 17px" />
|
||||
中午时段(11:30–13:00),ROI 2.5</span
|
||||
>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span>
|
||||
<img :src="getStarIcon(1)" style="width: 25px; height: 17px" />
|
||||
下午茶时段(15:00–17:00),ROI 2.3</span
|
||||
>
|
||||
<a-space
|
||||
v-if="getSubmoduleContent(MODEL_PERFORMANCE_ANALYSIS, '投放时段').length > 0"
|
||||
v-for="(item, index) in getSubmoduleContent(MODEL_PERFORMANCE_ANALYSIS, '投放时段')"
|
||||
:key="index"
|
||||
>
|
||||
<span><img :src="getStarIcon(index)" style="width: 25px; height: 17px" />{{ item }}</span>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</div>
|
||||
@ -103,23 +70,12 @@
|
||||
<div class="overall-strategy">
|
||||
<span class="placement-optimization-title">平台表现</span>
|
||||
<a-space direction="vertical">
|
||||
<a-space class="placement-optimization-str">
|
||||
<span>
|
||||
<img :src="getStarIcon(1)" style="width: 25px; height: 17px" />
|
||||
抖音 - ROI 3.2,CVR 8.5%</span
|
||||
>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span>
|
||||
<img :src="getStarIcon(2)" style="width: 25px; height: 17px" />
|
||||
聚光平台 - ROI 2.7,CVR 7.3%</span
|
||||
>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span>
|
||||
<img :src="getStarIcon(3)" style="width: 25px; height: 17px" />
|
||||
B站 - ROI 2.4,CVR 6.8%</span
|
||||
>
|
||||
<a-space
|
||||
v-if="getSubmoduleContent(MODEL_PERFORMANCE_ANALYSIS, '平台表现').length > 0"
|
||||
v-for="(item, index) in getSubmoduleContent(MODEL_PERFORMANCE_ANALYSIS, '平台表现')"
|
||||
:key="index"
|
||||
>
|
||||
<span><img :src="getStarIcon(index)" style="width: 25px; height: 17px" />{{ item }}</span>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</div>
|
||||
@ -140,23 +96,12 @@
|
||||
<div class="overall-strategy">
|
||||
<span class="placement-optimization-title">人群建议</span>
|
||||
<a-space direction="vertical">
|
||||
<a-space class="placement-optimization-str">
|
||||
<span>
|
||||
<img :src="getStarIcon(1)" style="width: 25px; height: 17px" />
|
||||
集中在 18–24 岁女性 + 精准兴趣标签(如“护肤”、“口红”)</span
|
||||
>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span>
|
||||
<img :src="getStarIcon(2)" style="width: 25px; height: 17px" />
|
||||
24–30 岁男性 + 实用类内容受众(如“工具控”、“搞机党”)</span
|
||||
>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span>
|
||||
<img :src="getStarIcon(3)" style="width: 25px; height: 17px" />
|
||||
泛娱乐向受众 + 较大地域分布(兴趣“短剧”、“直播带货”)</span
|
||||
>
|
||||
<a-space
|
||||
v-if="getSubmoduleContent(MODEL_PLACEMENT_SUGGESTION, '人群建议').length > 0"
|
||||
v-for="(item, index) in getSubmoduleContent(MODEL_PLACEMENT_SUGGESTION, '人群建议')"
|
||||
:key="index"
|
||||
>
|
||||
<span><img :src="getStarIcon(index)" style="width: 25px; height: 17px" />{{ item }}</span>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</div>
|
||||
@ -167,23 +112,12 @@
|
||||
<div class="overall-strategy">
|
||||
<span class="placement-optimization-title">素材建议</span>
|
||||
<a-space direction="vertical">
|
||||
<a-space class="placement-optimization-str">
|
||||
<span>
|
||||
<img :src="getStarIcon(1)" style="width: 25px; height: 17px" />
|
||||
福利明确+钩子强的图文短视频,建议加限时优惠提示</span
|
||||
>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span>
|
||||
<img :src="getStarIcon(2)" style="width: 25px; height: 17px" />
|
||||
场景代入型视频,突出客户痛点与产品关联</span
|
||||
>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span>
|
||||
<img :src="getStarIcon(3)" style="width: 25px; height: 17px" />
|
||||
达人口播/测评,搭配标题党封面吸引点击</span
|
||||
>
|
||||
<a-space
|
||||
v-if="getSubmoduleContent(MODEL_PLACEMENT_SUGGESTION, '素材建议').length > 0"
|
||||
v-for="(item, index) in getSubmoduleContent(MODEL_PLACEMENT_SUGGESTION, '素材建议')"
|
||||
:key="index"
|
||||
>
|
||||
<span><img :src="getStarIcon(index)" style="width: 25px; height: 17px" />{{ item }}</span>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</div>
|
||||
@ -195,23 +129,12 @@
|
||||
<div class="overall-strategy">
|
||||
<span class="placement-optimization-title">投放策略建议</span>
|
||||
<a-space direction="vertical">
|
||||
<a-space class="placement-optimization-str">
|
||||
<span>
|
||||
<img :src="getStarIcon(1)" style="width: 25px; height: 17px" />
|
||||
预算前置在 ROI 最佳时段和平台,优先抢头部流量</span
|
||||
>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span>
|
||||
<img :src="getStarIcon(2)" style="width: 25px; height: 17px" />
|
||||
中等预算组合投放 + 高点击素材A/B测试</span
|
||||
>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span>
|
||||
<img :src="getStarIcon(3)" style="width: 25px; height: 17px" />
|
||||
低预算长周期测试,重点看 CVR,优胜劣汰</span
|
||||
>
|
||||
<a-space
|
||||
v-if="getSubmoduleContent(MODEL_PLACEMENT_SUGGESTION, '投放策略建议').length > 0"
|
||||
v-for="(item, index) in getSubmoduleContent(MODEL_PLACEMENT_SUGGESTION, '投放策略建议')"
|
||||
:key="index"
|
||||
>
|
||||
<span><img :src="getStarIcon(index)" style="width: 25px; height: 17px" />{{ item }}</span>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</div>
|
||||
@ -223,13 +146,37 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { IconQuestionCircle } from '@arco-design/web-vue/es/icon';
|
||||
import { isEmpty } from '@/utils/tools';
|
||||
|
||||
const props = defineProps({
|
||||
action_guide: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
tmp: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const MODEL_PERFORMANCE_ANALYSIS = '表现分析';
|
||||
const MODEL_PLACEMENT_SUGGESTION = '新投放建议生成';
|
||||
|
||||
// 封装通用方法来获取 submodules 的内容
|
||||
const getSubmoduleContent = (moduleName: string, submoduleName: string) => {
|
||||
console.log(props.action_guide, 'getSubmoduleContent');
|
||||
const module = props.action_guide
|
||||
.find((item) => item.title === moduleName)
|
||||
?.submodules.find((mod) => mod.subtitle === submoduleName);
|
||||
const content = [];
|
||||
if (module) {
|
||||
const content = Object.values(module.content);
|
||||
console.log(content, 'content');
|
||||
return content;
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
import { getStarIcon } from '../../constants';
|
||||
import { defineProps } from 'vue';
|
||||
</script>
|
||||
|
||||
@ -1,36 +1,55 @@
|
||||
<template>
|
||||
<view>
|
||||
<div class="part-div">
|
||||
<div class="part-div-header">
|
||||
<span class="part-div-header-title">本月摘要</span>
|
||||
<a-popover position="tl">
|
||||
<icon-question-circle />
|
||||
<template #content>
|
||||
<p style="margin: 0">本月摘要。</p>
|
||||
</template>
|
||||
</a-popover>
|
||||
</div>
|
||||
<div class="month-data-div">
|
||||
<div style="align-self: stretch">
|
||||
<span class="month-text-black">总消耗:</span>
|
||||
<span class="month-text-blue">¥52,382.16</span>
|
||||
<span class="month-normal-span">,较上周期</span><span class="month-text-red">↑12.6%</span>
|
||||
<span class="month-normal-span">;<br />整体ROI:</span><span class="month-text-blue">2.84</span>
|
||||
<span class="month-normal-span">,属于</span><span class="month-text-red">中等偏高水平</span>
|
||||
<span class="month-text-black">,较上周期 </span><span class="month-text-red">+0.45</span>
|
||||
<span class="month-normal-span">;<br />主要转化来源:</span><span class="month-text-blue">抖音 46.3%</span>
|
||||
<span class="month-normal-span">,CTR 2.91%;<br />优质素材表现:</span>
|
||||
<span class="month-text-blue">美团酒店爆款横版1号</span>
|
||||
<span class="month-normal-spanw">。CTR 3.47%,CVR 5.92%。</span>
|
||||
</div>
|
||||
<div class="part-div">
|
||||
<div class="part-div-header">
|
||||
<span class="part-div-header-title">本月摘要</span>
|
||||
<a-popover position="tl">
|
||||
<icon-question-circle />
|
||||
<template #content>
|
||||
<p style="margin: 0">本月摘要。</p>
|
||||
</template>
|
||||
</a-popover>
|
||||
</div>
|
||||
<div class="month-data-div">
|
||||
<div style="align-self: stretch">
|
||||
<a-space direction="vertical" v-for="(item, index) in overview">
|
||||
<span :style="{ color: getColor(item?.highlight) }">{{ item.text }}</span>
|
||||
<br />
|
||||
</a-space>
|
||||
<!-- <span class="month-text-black">总消耗:</span>-->
|
||||
<!-- <span class="month-text-blue">¥52,382.16</span>-->
|
||||
<!-- <span class="month-normal-span">,较上周期</span><span class="month-text-red">↑12.6%</span>-->
|
||||
<!-- <span class="month-normal-span">;<br />整体ROI:</span><span class="month-text-blue">2.84</span>-->
|
||||
<!-- <span class="month-normal-span">,属于</span><span class="month-text-red">中等偏高水平</span>-->
|
||||
<!-- <span class="month-text-black">,较上周期 </span><span class="month-text-red">+0.45</span>-->
|
||||
<!-- <span class="month-normal-span">;<br />主要转化来源:</span><span class="month-text-blue">抖音 46.3%</span>-->
|
||||
<!-- <span class="month-normal-span">,CTR 2.91%;<br />优质素材表现:</span>-->
|
||||
<!-- <span class="month-text-blue">美团酒店爆款横版1号</span>-->
|
||||
<!-- <span class="month-normal-spanw">。CTR 3.47%,CVR 5.92%。</span>-->
|
||||
</div>
|
||||
</div>
|
||||
</view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { IconQuestionCircle } from '@arco-design/web-vue/es/icon';
|
||||
import { defineProps } from 'vue';
|
||||
|
||||
const colorMap = {
|
||||
purple: 'purple',
|
||||
orange: 'orange',
|
||||
};
|
||||
const props = defineProps({
|
||||
overview: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const getColor = (highlight?: string) => {
|
||||
return highlight ? colorMap[highlight] : undefined;
|
||||
};
|
||||
console.log(props.overview, 'overvie333');
|
||||
</script>
|
||||
<style lang="scss">
|
||||
<style lang="scss">
|
||||
@import './style.scss';
|
||||
</style>
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
//本月摘要数据-div
|
||||
.month-data-div {
|
||||
align-self: stretch;
|
||||
padding: 16px 30px 16px 16px;
|
||||
@ -15,7 +14,6 @@
|
||||
display: flex;
|
||||
}
|
||||
|
||||
//本月摘要-蓝色字体
|
||||
.month-text-blue {
|
||||
color: var(--Brand-Brand-6, #6D4CFE);
|
||||
font-size: 16px;
|
||||
@ -25,7 +23,6 @@
|
||||
word-wrap: break-word
|
||||
}
|
||||
|
||||
//红色字体
|
||||
.month-text-red {
|
||||
color: var(--Functional-Danger-6, #F64B31);
|
||||
font-size: 16px;
|
||||
@ -35,7 +32,6 @@
|
||||
word-wrap: break-word
|
||||
}
|
||||
|
||||
//黑色字体
|
||||
.month-text-black {
|
||||
color: var(--Text-1, #211F24);
|
||||
font-size: 16px;
|
||||
|
||||
@ -38,13 +38,13 @@
|
||||
<a-col :span="12">
|
||||
<div class="overall-strategy">
|
||||
<span class="placement-optimization-title">人群包优化</span>
|
||||
<span class="placement-optimization-str">{{props.optimization?.[3]?.['content']}}</span>
|
||||
<span class="placement-optimization-str">{{props?.optimization?.[3]?.['content']}}</span>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<div class="overall-strategy">
|
||||
<span class="placement-optimization-title">素材优化</span>
|
||||
<span class="placement-optimization-str">{{props.optimization?.[4]['content']}}</span>
|
||||
<span class="placement-optimization-str">{{props?.optimization?.[4]?.['content']}}</span>
|
||||
|
||||
</div>
|
||||
</a-col>
|
||||
@ -65,6 +65,8 @@ const props = defineProps({
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
console.log(props.optimization,'optimization')
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<view>
|
||||
<a-table
|
||||
class="account-table"
|
||||
:columns="columns"
|
||||
:data="listResult.data"
|
||||
:filter-icon-align-left="alignLeft"
|
||||
:pagination="false"
|
||||
>
|
||||
<a-table class="account-table" :columns="columns" :data="listData" :pagination="false">
|
||||
<template #platform="{ record }">
|
||||
<span class="mr-5px" v-if="record.platform.length > 0" v-for="(item, index) in record.platform">
|
||||
<img :src="PLATFORM_LIST[item].icon" width="19" class="mr-4px" />
|
||||
<span>{{ PLATFORM_LIST[item].label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
<a-space size="medium">
|
||||
<a-space>
|
||||
@ -15,14 +15,14 @@
|
||||
type="warning"
|
||||
ok-text="确认删除"
|
||||
cancel-text="取消"
|
||||
@ok="deleteBrand(record.id)"
|
||||
@ok="deleteData(record.id)"
|
||||
>
|
||||
<icon-delete></icon-delete>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<a-button type="outline" class="operation-btn">下载</a-button>
|
||||
<a-button type="outline" class="operation-btn">详情</a-button>
|
||||
<a-button type="outline" @click="downloadDetailAsImage(record.id)" class="operation-btn">下载</a-button>
|
||||
<a-button type="outline" @click="goDetail(record.id)" class="operation-btn">详情</a-button>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</template>
|
||||
@ -30,23 +30,15 @@
|
||||
</view>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import top1 from '@/assets/img/captcha/top1.svg';
|
||||
import top2 from '@/assets/img/captcha/top2.svg';
|
||||
import top3 from '@/assets/img/captcha/top3.svg';
|
||||
import { IconDelete } from '@arco-design/web-vue/es/icon';
|
||||
import { PLATFORM_LIST } from '@/views/property-marketing/put-account/common_constants';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import html2canvas from 'html2canvas';
|
||||
|
||||
const listQuery = reactive({
|
||||
project_id: ref(''),
|
||||
platform: ref(''),
|
||||
page: ref(1),
|
||||
name: ref(''),
|
||||
page_size: ref('10'),
|
||||
});
|
||||
const columns = [
|
||||
{
|
||||
title: '生成日期',
|
||||
dataIndex: 'date',
|
||||
slotName: 'account_name',
|
||||
dataIndex: 'created_at',
|
||||
width: 60,
|
||||
minWidth: 60,
|
||||
},
|
||||
@ -57,18 +49,16 @@ const columns = [
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
titleSlotName: 'project_name',
|
||||
width: 180,
|
||||
minWidth: 180,
|
||||
title: '项目',
|
||||
dataIndex: 'project_name',
|
||||
title: '计划',
|
||||
dataIndex: 'plan',
|
||||
},
|
||||
{
|
||||
title: '平台',
|
||||
titleSlotName: 'week_consumption',
|
||||
dataIndex: 'platform',
|
||||
width: 120,
|
||||
minWidth: 120,
|
||||
slotName: 'platform',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
@ -78,40 +68,54 @@ const columns = [
|
||||
minWidth: 60,
|
||||
},
|
||||
];
|
||||
const listResult = reactive({
|
||||
data: [
|
||||
{
|
||||
date: '日期',
|
||||
account: '账号',
|
||||
project_name: '项目名称',
|
||||
week_consumption: '本周总消耗',
|
||||
week_consumption_rate: 0.1,
|
||||
roi: '2.6',
|
||||
ctr: '3.1%',
|
||||
},
|
||||
{
|
||||
account_name: '全球旅行',
|
||||
platform_name: '平台',
|
||||
project_name: '项目名称',
|
||||
week_consumption: '本周总消耗',
|
||||
week_consumption_rate: -0.1,
|
||||
roi: '2.6',
|
||||
ctr: '3.1%',
|
||||
},
|
||||
{
|
||||
account_name: '全球旅行',
|
||||
platform_name: '平台',
|
||||
project_name: '项目名称',
|
||||
week_consumption: '本周总消耗',
|
||||
week_consumption_rate: -0.1,
|
||||
roi: '2.6',
|
||||
ctr: '3.1%',
|
||||
},
|
||||
],
|
||||
total: ref(0),
|
||||
});
|
||||
// hotTranslation.vue
|
||||
import { useRouter } from 'vue-router';
|
||||
import { deleteHistorylog } from '@/api/all/propertyMarketing'; // 引入 useRouter
|
||||
|
||||
const topImages = [top1, top2, top3];
|
||||
const router = useRouter(); // 创建 router 实例
|
||||
|
||||
// 详情
|
||||
const goDetail = async (id) => {
|
||||
// 使用 router.push 进行编程式导航,并传递话题 ID
|
||||
router.push(`/put-account/detail/${id}`);
|
||||
};
|
||||
|
||||
const downloadDetailAsImage = (id) => {
|
||||
const url = `/put-account/detail/${id}`;
|
||||
const win = window.open(url, '_blank');
|
||||
|
||||
win.onload = () => {
|
||||
setTimeout(() => {
|
||||
html2canvas(win.document.body, {
|
||||
useCORS: true,
|
||||
scale: 2,
|
||||
}).then((canvas) => {
|
||||
const imgData = canvas.toDataURL('image/png');
|
||||
const link = document.createElement('a');
|
||||
link.href = imgData;
|
||||
link.download = `详情页面_${Date.now()}.png`;
|
||||
link.click();
|
||||
win.close(); // 关闭新窗口
|
||||
});
|
||||
}, 2000); // 等待页面加载
|
||||
};
|
||||
};
|
||||
const deleteData = async (id) => {
|
||||
const { code, data } = await deleteHistorylog(id);
|
||||
|
||||
if (code === 200) {
|
||||
Message.success('删除成功');
|
||||
}
|
||||
};
|
||||
|
||||
const props = defineProps({
|
||||
listData: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
data: [];
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
@ -1,95 +1,77 @@
|
||||
<!--表单搜索组件-->
|
||||
<template>
|
||||
<view>
|
||||
<a-space size="large" direction="vertical" class="search-form">
|
||||
<a-row class="grid-demo" :gutter="{ md: 8, lg: 24, xl: 32 }">
|
||||
<a-col :span="8">
|
||||
<a-space>
|
||||
<span>账户</span>
|
||||
<a-input
|
||||
class="w-310px"
|
||||
:style="{ width: '320px' }"
|
||||
v-model="query.account_name"
|
||||
placeholder="请搜索..."
|
||||
size="medium"
|
||||
allow-clear
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-search />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-space>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-space>
|
||||
<span>计划</span>
|
||||
<a-input
|
||||
class="w-310px"
|
||||
:style="{ width: '320px' }"
|
||||
v-model="query.plan"
|
||||
placeholder="请搜索..."
|
||||
size="medium"
|
||||
allow-clear
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-search />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-space>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-space>
|
||||
<span>平台</span>
|
||||
<div class="container px-24px">
|
||||
<div class="filter-row flex mb-20px">
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">账户名称</span>
|
||||
<a-space size="medium" class="w-240px">
|
||||
<a-input v-model="query.account_name" placeholder="请搜索..." size="medium" allow-clear>
|
||||
<template #prefix>
|
||||
<icon-search />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<a-select
|
||||
v-model="query.platform"
|
||||
class="w-320"
|
||||
size="medium"
|
||||
placeholder="全部"
|
||||
allow-clear
|
||||
@change="handleSearch"
|
||||
>
|
||||
<a-option v-for="(item, index) in PLATFORM_LIST" :key="index" :value="item.value" :label="item.label"
|
||||
>{{ item.label }}
|
||||
</a-option>
|
||||
</a-select>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">计划</span>
|
||||
<a-space size="medium" class="w-240px">
|
||||
<a-input
|
||||
class="w-310px"
|
||||
:style="{ width: '320px' }"
|
||||
v-model="query.plan"
|
||||
placeholder="请搜索..."
|
||||
size="medium"
|
||||
allow-clear
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-search />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<a-row class="grid-demo" :gutter="{ md: 8, lg: 24, xl: 32 }">
|
||||
<a-col :span="10">
|
||||
<a-space>
|
||||
<span>时间筛选</span>
|
||||
<a-range-picker
|
||||
showTime
|
||||
:time-picker-props="{
|
||||
defaultValue: ['00:00:00', '00:00:00'],
|
||||
}"
|
||||
@select="onSelect"
|
||||
style="width: 380px"
|
||||
/>
|
||||
</a-space>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-space>
|
||||
<a-button type="outline" class="search-btn" @click="handleSearch">
|
||||
<template #icon>
|
||||
<icon-search />
|
||||
</template>
|
||||
<template #default>搜索</template>
|
||||
</a-button>
|
||||
<a-button type="outline" class="reset-btn" @click="handleSearch">
|
||||
<template #icon>
|
||||
<icon-refresh />
|
||||
</template>
|
||||
<template #default>重置</template>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-space>
|
||||
</view>
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">平台</span>
|
||||
<a-select
|
||||
v-model="query.platform"
|
||||
class="w-150"
|
||||
size="medium"
|
||||
placeholder="全部"
|
||||
allow-clear
|
||||
@change="handleSearch"
|
||||
>
|
||||
<a-option v-for="(item, index) in PLATFORM_LIST" :key="index" :value="item.value" :label="item.label"
|
||||
>{{ item.label }}
|
||||
</a-option>
|
||||
</a-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-row flex mb-20px">
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">时间筛选</span>
|
||||
<a-space size="medium" class="w-240px">
|
||||
<a-range-picker v-model="query.data_time" size="medium" allow-clear format="YYYY-MM-DD" class="w-310" />
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<div class="filter-row-item flex items-center">
|
||||
<a-button class="w-84px search-btn mr-12px" size="medium" @click="handleSearch">
|
||||
<template #icon>
|
||||
<icon-search />
|
||||
</template>
|
||||
<template #default>搜索</template>
|
||||
</a-button>
|
||||
<a-button class="w-84px reset-btn" size="medium" @click="handleReset">
|
||||
<template #icon>
|
||||
<icon-refresh />
|
||||
</template>
|
||||
<template #default>重置</template>
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -103,52 +85,77 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
const emits = defineEmits('onSearch', 'onReset', 'update:query');
|
||||
|
||||
// 获取最近7天的日期
|
||||
const getLast7Days = () => {
|
||||
const today = new Date();
|
||||
const last7Days = new Date(today);
|
||||
last7Days.setDate(today.getDate() - 7);
|
||||
return [last7Days.toISOString().split('T')[0], today.toISOString().split('T')[0]];
|
||||
};
|
||||
const handleSearch = () => {
|
||||
emits('onSearch', props.query);
|
||||
};
|
||||
// 使用 computed 实现 v-model
|
||||
const localQuery = computed({
|
||||
get: () => props.query,
|
||||
set: (value) => emits('update:query', value),
|
||||
});
|
||||
onMounted(() => {
|
||||
localQuery.value.data_time = getLast7Days();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
:deep(.arco-select-view-single),
|
||||
:deep(.arco-select-view-multiple),
|
||||
:deep(.arco-picker),
|
||||
:deep(.arco-input-wrapper) {
|
||||
border-radius: 4px;
|
||||
border-color: #d7d7d9;
|
||||
background-color: #fff;
|
||||
width: 224px;
|
||||
height: 32px;
|
||||
.container {
|
||||
:deep(.arco-input-wrapper),
|
||||
:deep(.arco-select-view-single),
|
||||
:deep(.arco-select-view-multiple),
|
||||
:deep(.arco-picker) {
|
||||
border-radius: 4px;
|
||||
border-color: #d7d7d9;
|
||||
background-color: #fff;
|
||||
|
||||
&:focus-within,
|
||||
&.arco-input-focus {
|
||||
background-color: var(--color-bg-2);
|
||||
border-color: rgb(var(--primary-6));
|
||||
box-shadow: 0 0 0 0 var(--color-primary-light-2);
|
||||
&:focus-within,
|
||||
&.arco-input-focus {
|
||||
background-color: var(--color-bg-2);
|
||||
border-color: rgb(var(--primary-6));
|
||||
box-shadow: 0 0 0 0 var(--color-primary-light-2);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.search-btn) {
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--Brand-Brand-6, #6d4cfe);
|
||||
color: #6d4cfe;
|
||||
}
|
||||
|
||||
:deep(.reset-btn) {
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--BG-500, #b1b2b5);
|
||||
background: var(--BG-white, #fff);
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
.filter-row-item {
|
||||
&:not(:last-child) {
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
.label {
|
||||
margin-right: 8px;
|
||||
color: #211f24;
|
||||
font-family: 'PuHuiTi-Regular';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
flex-shrink: 0;
|
||||
line-height: 22px; /* 157.143% */
|
||||
}
|
||||
|
||||
:deep(.arco-space-item) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
// 搜索
|
||||
color: var(--Brand-6, #6d4cfe);
|
||||
font-size: 14px;
|
||||
font-family: PuHuiTi-Medium;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
height: 32px;
|
||||
border-radius: 3px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.reset-btn {
|
||||
// 重置
|
||||
color: var(--Text-2, #3c4043);
|
||||
font-size: 14px;
|
||||
font-family: PuHuiTi-Medium;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
height: 32px;
|
||||
border-radius: 3px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -3,9 +3,9 @@
|
||||
<a-table
|
||||
class="account-table"
|
||||
:columns="columns"
|
||||
:data="listResult.data"
|
||||
:filter-icon-align-left="alignLeft"
|
||||
:data="listData"
|
||||
:pagination="false"
|
||||
@sorter-change="handleSorterChange"
|
||||
>
|
||||
<template #week_consumption>
|
||||
<a-space>
|
||||
@ -53,8 +53,10 @@
|
||||
</template>
|
||||
|
||||
<template #platform="{ record }">
|
||||
<img :src="PLATFORM_LIST[record.platform].icon" width="19" class="mr-4px" />
|
||||
<span>{{ PLATFORM_LIST[record.platform].label }}</span>
|
||||
<a-space size="medium" v-if="record.platform">
|
||||
<img :src="PLATFORM_LIST[record.platform]?.icon" width="19" class="mr-4px" />
|
||||
<span>{{ PLATFORM_LIST[record.platform].label }}</span>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<template #weekConsumptionRate="{ record }">
|
||||
@ -73,22 +75,30 @@
|
||||
<template #suffix>%</template>
|
||||
</a-statistic>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<template #clickRate="{ record }">
|
||||
<span>{{ `${record.click_rate}%` }}</span>
|
||||
</template>
|
||||
</a-table>
|
||||
</view>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { PLATFORM_LIST } from '../../../common_constants';
|
||||
|
||||
defineProps({
|
||||
listResult: {
|
||||
type: Object,
|
||||
const props = defineProps({
|
||||
listData: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
total: 0;
|
||||
data: [];
|
||||
},
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['updateQuery']);
|
||||
|
||||
const handleSorterChange = (column, order) => {
|
||||
emit('updateQuery', { column, order });
|
||||
};
|
||||
const listQuery = reactive({
|
||||
project_id: ref(''),
|
||||
platform: ref(''),
|
||||
@ -122,6 +132,9 @@ const columns = [
|
||||
dataIndex: 'total_use_amount',
|
||||
width: 120,
|
||||
minWidth: 120,
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '本周总消耗环比',
|
||||
@ -130,25 +143,34 @@ const columns = [
|
||||
width: 120,
|
||||
minWidth: 120,
|
||||
slotName: 'weekConsumptionRate',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
},
|
||||
{
|
||||
titleSlotName: 'roi',
|
||||
dataIndex: 'roi',
|
||||
width: 120,
|
||||
minWidth: 120,
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
},
|
||||
{
|
||||
titleSlotName: 'ctr',
|
||||
dataIndex: 'click_rate',
|
||||
width: 120,
|
||||
minWidth: 120,
|
||||
slotName: 'clickRate',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.table-data {
|
||||
//账户列表-分页
|
||||
.account-page {
|
||||
padding: 10px 24px 20px 24px;
|
||||
background-color: #fff;
|
||||
|
||||
@ -9,11 +9,11 @@ import top3 from '@/assets/img/captcha/top3.svg';
|
||||
*/
|
||||
export function getStarIcon(score: number): string {
|
||||
switch (score) {
|
||||
case 1:
|
||||
case 0:
|
||||
return top1;
|
||||
case 2:
|
||||
case 1:
|
||||
return top2;
|
||||
case 3:
|
||||
case 2:
|
||||
return top3;
|
||||
default:
|
||||
return top1; // 默认返回最高分图标
|
||||
|
||||
@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<div class="guidelines-data-wrap">
|
||||
<div class="filter-wrap bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid">
|
||||
<div class="top flex h-64px px-24px py-10px justify-between items-center">
|
||||
<p class="text-18px font-400 lh-26px color-#211F24 title">投放信息</p>
|
||||
</div>
|
||||
|
||||
<div class="container px-24px pt-12px pb-24px">
|
||||
<a-row class="grid-demo" :gutter="24">
|
||||
<a-col :span="12">
|
||||
<div class="">
|
||||
<a-space direction="vertical">
|
||||
<span>账户</span>
|
||||
<span>{{ detailData?.account }}</span>
|
||||
</a-space>
|
||||
</div>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="12">
|
||||
<div class="">
|
||||
<a-space direction="vertical">
|
||||
<span>计划</span>
|
||||
<span>3</span>
|
||||
</a-space>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row class="grid-demo" :gutter="24" style="margin-top: 30px">
|
||||
<a-col :span="12">
|
||||
<div class="">
|
||||
<a-space direction="vertical">
|
||||
<span>平台</span>
|
||||
<span class="mr-5px" v-if="detailData.platform.length > 0" v-for="(item, index) in detailData.platform">
|
||||
<img :src="PLATFORM_LIST[item].icon" width="19" class="mr-4px" />
|
||||
<span>{{ PLATFORM_LIST[item].label }}</span>
|
||||
</span>
|
||||
</a-space>
|
||||
</div>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="12">
|
||||
<div class="">
|
||||
<a-space direction="vertical">
|
||||
<span>生成时间</span>
|
||||
<span>{{ detailData.created_at }}</span>
|
||||
</a-space>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-spin :loading="loading" tip="数据分析中">
|
||||
<div>
|
||||
<!-- 本月摘要-->
|
||||
<MonthData></MonthData>
|
||||
|
||||
<!-- <!– 投放建议–>-->
|
||||
<PlacementSuggestions :optimization="detailData?.ai_result?.optimization?.modules"></PlacementSuggestions>
|
||||
<!-- <!– 投放行动指南–>-->
|
||||
<ActionGuideDistribution :action_guide="detailData?.ai_result.action_guide?.modules"></ActionGuideDistribution>
|
||||
</div>
|
||||
</a-spin>
|
||||
<div>
|
||||
<a-space class="down-btn">
|
||||
<a-button type="outline" @click="onSearch">
|
||||
<template #icon>
|
||||
<icon-download />
|
||||
</template>
|
||||
<template #default>下载</template>
|
||||
</a-button>
|
||||
<a-button type="primary" @click="handleSave">
|
||||
<template #icon>
|
||||
<icon-drive-file />
|
||||
</template>
|
||||
<template #default>保存</template>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue';
|
||||
|
||||
import MonthData from './components/month-data/index.vue';
|
||||
import PlacementSuggestions from './components/placement-suggestions/index.vue';
|
||||
import ActionGuideDistribution from './components/action-guide-distribution';
|
||||
import { PLATFORM_LIST } from '@/views/property-marketing/put-account/common_constants.ts';
|
||||
import { getPlacementGuideDetail } from '@/api/all/propertyMarketing';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
const tabData = ref('placement_guide');
|
||||
|
||||
const query = reactive({
|
||||
platform: '',
|
||||
date_time: '',
|
||||
sort_column: '',
|
||||
sort_order: '',
|
||||
page_size: 20,
|
||||
page: 1,
|
||||
});
|
||||
|
||||
const handleUpdateQuery = (payload) => {
|
||||
payload.order = payload.order === 'ascend' ? 'asc' : 'desc';
|
||||
query.sort_column = payload.column;
|
||||
query.sort_order = payload.order;
|
||||
};
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
const saveForm = reactive({
|
||||
account: [],
|
||||
plan: [],
|
||||
platform: [],
|
||||
});
|
||||
|
||||
const detailData = reactive({
|
||||
platform: [],
|
||||
ai_result: {},
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
const id = route.params.id;
|
||||
const getDetail = async () => {
|
||||
const { code, data } = await getPlacementGuideDetail(id);
|
||||
console.log(data, 'data');
|
||||
console.log(code, 'code');
|
||||
if (code === 200) {
|
||||
Object.assign(detailData, data);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getDetail();
|
||||
});
|
||||
// 定时任务请求接口
|
||||
// const timer = setInterval(() => {
|
||||
// getSyncAiResult();
|
||||
// }, 5000);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import './style.scss';
|
||||
</style>
|
||||
@ -1,8 +1,13 @@
|
||||
<template>
|
||||
<view>
|
||||
<div class="guidelines-data-wrap">
|
||||
<div class="part-div">
|
||||
<div>
|
||||
<a-tabs v-model:activeKey="tabData" class="a-tab-class" default-active-key="placement_guide">
|
||||
<a-tabs
|
||||
v-model:activeKey="tabData"
|
||||
@tab-click="onSearch"
|
||||
class="a-tab-class"
|
||||
default-active-key="placement_guide"
|
||||
>
|
||||
<a-tab-pane key="placement_guide" title="投放指南"></a-tab-pane>
|
||||
<a-tab-pane key="guide_history">
|
||||
<template #title>历史投放指南</template>
|
||||
@ -14,30 +19,45 @@
|
||||
<!-- 投放指南-->
|
||||
<PlacementGuideList
|
||||
v-if="tabData === 'placement_guide'"
|
||||
:listResult="{ data: guideListData.data, total: pageInfo.total }"
|
||||
:listData="listData.list"
|
||||
@updateQuery="handleUpdateQuery"
|
||||
></PlacementGuideList>
|
||||
<!-- 历史指南列表-->
|
||||
<GuideListHistory v-if="tabData === 'guide_history'"></GuideListHistory>
|
||||
<GuideListHistory v-if="tabData === 'guide_history'" :listData="listData.list"></GuideListHistory>
|
||||
|
||||
<div v-if="listData.total > 0" class="pagination-box flex justify-end">
|
||||
<a-pagination
|
||||
:total="listData.total"
|
||||
size="mini"
|
||||
show-total
|
||||
show-jumper
|
||||
show-page-size
|
||||
:current="query.page"
|
||||
:page-size="query.pageSize"
|
||||
@change="onPageChange"
|
||||
@page-size-change="onPageSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-spin :loading="loading" tip="数据分析中">
|
||||
<div v-if="loading === false">
|
||||
<!-- 本月摘要-->
|
||||
<MonthData></MonthData>
|
||||
<MonthData :overview="aiResult.overview"></MonthData>
|
||||
|
||||
<!-- 投放建议-->
|
||||
<PlacementSuggestions :optimization="aiResult.optimization"></PlacementSuggestions>
|
||||
<!-- 投放行动指南-->
|
||||
<ActionGuideDistribution :action_guide="aiResult.action_guide"></ActionGuideDistribution>
|
||||
</a-spin>
|
||||
<ActionGuideDistribution :action_guide="aiResult.action_guide" :tmp="tmp"></ActionGuideDistribution>
|
||||
</div>
|
||||
<div>
|
||||
<a-space class="down-btn">
|
||||
<a-button type="outline" @click="handleSearch">
|
||||
<a-button type="outline" @click="onSearch">
|
||||
<template #icon>
|
||||
<icon-download />
|
||||
</template>
|
||||
<template #default>下载</template>
|
||||
</a-button>
|
||||
<a-button type="primary" @click="handleSearch">
|
||||
<a-button type="primary" @click="handleSave">
|
||||
<template #icon>
|
||||
<icon-drive-file />
|
||||
</template>
|
||||
@ -45,7 +65,7 @@
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -58,58 +78,112 @@ import PlacementSuggestions from './components/placement-suggestions/index.vue';
|
||||
import ActionGuideDistribution from './components/action-guide-distribution';
|
||||
import {
|
||||
getAiResult,
|
||||
getPlacementAccountData,
|
||||
getPlacementAccountDataList,
|
||||
getPlacementGuide,
|
||||
getPlacementGuideHistory,
|
||||
savePlacementGuide,
|
||||
} from '@/api/all/propertyMarketing';
|
||||
|
||||
const tabData = ref('placement_guide');
|
||||
|
||||
const query = reactive({
|
||||
platform: '',
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
const guideListData = reactive({
|
||||
data: [],
|
||||
});
|
||||
const pageInfo = reactive({
|
||||
total: 0,
|
||||
page_size: 0,
|
||||
date_time: '',
|
||||
sort_column: '',
|
||||
sort_order: '',
|
||||
page_size: 20,
|
||||
page: 1,
|
||||
});
|
||||
|
||||
const onSearch = async () => {
|
||||
const { code, data } = await getPlacementGuide(query);
|
||||
if (code === 200) {
|
||||
guideListData.data = data.data;
|
||||
getSyncAiResult();
|
||||
}
|
||||
console.log(guideListData, 'guideListData');
|
||||
const tmp = ref(0);
|
||||
|
||||
const onPageChange = (current) => {
|
||||
query.page = current;
|
||||
onSearch();
|
||||
};
|
||||
const onPageSizeChange = (pageSize) => {
|
||||
query.page_size = pageSize;
|
||||
onSearch();
|
||||
};
|
||||
|
||||
const handleUpdateQuery = (payload) => {
|
||||
payload.order = payload.order === 'ascend' ? 'asc' : 'desc';
|
||||
query.sort_column = payload.column;
|
||||
query.sort_order = payload.order;
|
||||
onSearch();
|
||||
};
|
||||
|
||||
const loading = ref(true);
|
||||
|
||||
const listData = reactive({
|
||||
list: [],
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const onSearch = async () => {
|
||||
let result;
|
||||
if (tabData.value === 'placement_guide') {
|
||||
result = await getPlacementGuide(query);
|
||||
} else {
|
||||
result = await getPlacementGuideHistory(query);
|
||||
}
|
||||
const { code, data } = result;
|
||||
console.log(data, 'data');
|
||||
if (code === 200) {
|
||||
listData.list = data.data;
|
||||
listData.total = data.total;
|
||||
if (tabData.value === 'placement_guide') {
|
||||
getSyncAiResult();
|
||||
}
|
||||
}
|
||||
};
|
||||
const aiResult = reactive({
|
||||
optimization: [], //投放建议优化
|
||||
action_guide: [], //新投放建议生成
|
||||
overview: [], //新投放建议生成
|
||||
});
|
||||
|
||||
const saveForm = reactive({
|
||||
account: [],
|
||||
plan: [],
|
||||
platform: [],
|
||||
aiResult: [],
|
||||
});
|
||||
const timer = ref(null);
|
||||
const getSyncAiResult = async () => {
|
||||
if (listData.list.length == 0) {
|
||||
return;
|
||||
}
|
||||
const { code, data } = await getAiResult(query);
|
||||
if (code === 200) {
|
||||
// 成功或者失败清除定时任务
|
||||
if ((data.status && data.status === 3) || data.status === 2) {
|
||||
// clearInterval(timer);
|
||||
loading.value = false;
|
||||
}
|
||||
aiResult.optimization = data.result.optimization.modules;
|
||||
aiResult.action_guide = data.result?.action_guide?.modules;
|
||||
aiResult.overview = data.result?.overview?.content_blocks;
|
||||
Object.assign(saveForm, data);
|
||||
}
|
||||
loading.value = false;
|
||||
// 定时任务请求接口
|
||||
timer.value = setInterval(() => {
|
||||
getSyncAiResult();
|
||||
}, 5000);
|
||||
console.log(loading.value, 'loading.value');
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
const updatedSaveForm = {
|
||||
...saveForm,
|
||||
ai_result: aiResult,
|
||||
};
|
||||
const { code, data } = await savePlacementGuide(updatedSaveForm);
|
||||
if (code === 200) {
|
||||
}
|
||||
};
|
||||
// 定时任务请求接口
|
||||
// const timer = setInterval(() => {
|
||||
// getSyncAiResult();
|
||||
// }, 5000);
|
||||
onMounted(() => {
|
||||
onSearch();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
display: inline-flex;
|
||||
margin-top: 15px;
|
||||
// margin: 10px;
|
||||
|
||||
.arco-tabs {
|
||||
@ -42,7 +43,6 @@
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
//每块div 标题
|
||||
.part-div-header-title {
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
@ -55,13 +55,12 @@
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
//账户指南-搜索div
|
||||
|
||||
.search-form {
|
||||
padding: 10px 24px 20px 24px;
|
||||
|
||||
}
|
||||
|
||||
//账户列表-表格
|
||||
.account-table {
|
||||
padding: 1px 24px 20px 24px;
|
||||
width: 100%;
|
||||
@ -85,7 +84,6 @@
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
//本周摘要div
|
||||
.month-body-div {
|
||||
align-self: stretch;
|
||||
height: 64px;
|
||||
@ -96,7 +94,7 @@
|
||||
display: inline-flex
|
||||
}
|
||||
|
||||
//本月摘要标题
|
||||
|
||||
.month-data-title {
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
@ -110,7 +108,6 @@
|
||||
}
|
||||
|
||||
|
||||
//投放建议-总体策略
|
||||
.overall-strategy {
|
||||
width: 98%;
|
||||
padding: 20px 10px 20px 16px;
|
||||
@ -126,9 +123,7 @@
|
||||
|
||||
}
|
||||
|
||||
//投放优化每块div小标题
|
||||
.placement-optimization-title {
|
||||
// 总体策略
|
||||
color: var(--Text-1, #211F24);
|
||||
font-size: 16px;
|
||||
font-family: PuHuiTi-Medium;
|
||||
@ -147,10 +142,8 @@
|
||||
word-wrap: break-word
|
||||
}
|
||||
|
||||
//表现分析标题
|
||||
.player-title {
|
||||
margin: 10px 0px 15px 20px;
|
||||
// 表现分析
|
||||
color: var(--Text-1, #211F24);
|
||||
font-size: 16px;
|
||||
font-family: PuHuiTi-Medium;
|
||||
@ -174,3 +167,103 @@
|
||||
margin-bottom: 20px
|
||||
}
|
||||
|
||||
|
||||
.guidelines-data-wrap {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.filter-wrap {
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e6e6e8;
|
||||
|
||||
:deep(.arco-tabs) {
|
||||
.arco-tabs-tab {
|
||||
height: 56px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.arco-btn) {
|
||||
.arco-btn-icon {
|
||||
line-height: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.top {
|
||||
.title {
|
||||
font-family: 'PuHuiTi-Medium';
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.overview-row {
|
||||
.overview-item {
|
||||
height: 88px;
|
||||
border-radius: 8px;
|
||||
background: var(--BG-100, #f7f8fa);
|
||||
padding: 16px;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-wrap {
|
||||
width: 100%;
|
||||
|
||||
.pagination-box {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 16px 24px;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-box {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 16px 24px;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
|
||||
.arco-pagination {
|
||||
.arco-pagination-list {
|
||||
.arco-pagination-item {
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--BG-300, #e6e6e8);
|
||||
|
||||
&.arco-pagination-item-ellipsis {
|
||||
border: none;
|
||||
}
|
||||
|
||||
&.arco-pagination-item-active {
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--Brand-Brand-6, #6d4cfe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.arco-pagination-jumper-prepend {
|
||||
color: var(--Text-2, #3c4043);
|
||||
text-align: right;
|
||||
font-family: 'PuHuiTi-Medium';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px; /* 157.143% */
|
||||
}
|
||||
|
||||
.arco-select-view-single,
|
||||
.arco-pagination-jumper-input {
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--BG-300, #e6e6e8);
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user