提交 5366e586 authored 作者: lidongxu's avatar lidongxu

feat(sycm_store): 生意参谋-竞店分析完成

同上
上级 f12e2966
......@@ -8,6 +8,13 @@ export function getCmmListAPI(params) {
})
}
// 竞店列表
export function getStoreListAPI() {
return request({
url: '/bi/oppo/sycm/store/all'
})
}
// 生意参谋-各大竞店
export function getSycmStoreListAPI(params) {
return request({
......@@ -22,4 +29,4 @@ export function getSycmListAPI(params) {
url: '/bi/oppo/sycm/list',
params
})
}
\ No newline at end of file
}
......@@ -15,8 +15,7 @@
<!-- 工具抽屉 -->
<el-drawer title="工具箱"
v-model="drawer"
size="300px"
@click.native="drawer = false">
size="300px">
<slot></slot>
</el-drawer>
</div>
......
......@@ -22,6 +22,8 @@
</template>
</el-dropdown>
</el-tooltip>
<!-- 自定义功能 -->
<slot></slot>
</el-row>
<el-dialog :title="title" v-model="open" append-to-body>
<el-transfer
......
......@@ -151,7 +151,6 @@ const setOptions = () => {
// tooltip += item.marker + item.seriesName + ': ' + toFixed2(item.value) + (this.myThousand ? '' : '') + '<br>';
// }
// });
// console.log(tooltip)
// return tooltip;
// }
},
......
......@@ -240,7 +240,6 @@ init()
// 筛选条件改变了
const queryChangeFn = async (arg) => {
if (arg === 'date' && !queryParams.date) return
if (arg === 'date' && queryParams.date) {
await getList()
}
......@@ -262,12 +261,14 @@ const reset = async () => {
<style scoped
lang="scss">
.tabs-container {
min-height: 100%;
display: flex;
flex-direction: column;
.chart_wrap {
padding: 20px 0;
flex: 1;
display: flex;
flex-direction: column;
......
......@@ -24,7 +24,7 @@ const list = ref([
{ name: '生意参谋-竞店', component: shallowRef(sycmStore) },
{ name: '生意参谋-竞品', component: shallowRef(sycmPrd) }
])
const activeName = ref(list.value[0].name)
const activeName = ref(list.value[1].name)
</script>
<style scoped
......
......@@ -35,6 +35,8 @@ const echartsRef = ref(null)
const chart = shallowRef(null)
let myThousand = false // 是否显示万单位
const emits = defineEmits(['changeType'])
const setOptions = () => {
chart.value.setOption({
tooltip: {
......@@ -52,11 +54,9 @@ const setOptions = () => {
var color = item.color;
// 拼接提示内容
if (item.seriesName.split('-')[1].includes('速')) {
tooltip += '<span style="display:inline-block;margin-right:5px;width:12px;height:6px;background-color:' + color + ';"></span>' + item.seriesName + ': ' + formatNumberWithUnit(item.value, '%', false, false) + '<br>';
} else if (item.seriesName.split('-')[1] === '观看人次') {
tooltip += '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:' + color + ';"></span>' + item.seriesName + ': ' + formatNumberWithUnit(item.value, '人', myThousand) + '<br>';
tooltip += '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:' + color + ';"></span>' + item.seriesName + ': ' + formatNumberWithUnit(item.value, '%', false, false) + '<br>';
} else {
tooltip += '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:' + color + ';"></span>' + item.seriesName + ':' + item.value + '<br>';
tooltip += '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:' + color + ';"></span>' + item.seriesName + ':' + formatNumberWithUnit(item.value, '人', false, true) + '<br>';
}
});
return tooltip;
......@@ -71,6 +71,14 @@ const setOptions = () => {
magicType: {
type: ['stack', 'tiled'] // 切换图表类型
},
myTool1: {
show: true,
title: '切换表格',
icon: 'path://M5,5 L5,15 L15,15 L15,5 Z M20,5 L20,15 L30,15 L30,5 Z M5,20 L5,30 L15,30 L15,20 Z M20,20 L20,30 L30,30 L30,20 Z',
onclick: () => {
emits('changeType', 'table')
}
}
}
},
grid: {
......
<template>
<div class="toolbar">
<right-toolbar :search="false" @queryTable="queryTable">
<el-tooltip class="item" effect="dark" content="切换图表" placement="top">
<el-button circle icon="Histogram" @click="changeType()" />
</el-tooltip>
<el-tooltip class="item" effect="dark" content="日期合并/分散" placement="top">
<el-button circle icon="Calendar" @click="dateMergeFn()" />
</el-tooltip>
</right-toolbar>
</div>
<el-table :data="data"
style="width: 100%">
<el-table-column prop="platformStore"
label="店铺" />
<el-table-column prop="date"
label="日期" />
<el-table-column prop="zfmjPeakTotal"
label="支付买家数" />
<el-table-column prop="jyzsPeakTotal"
label="交易增速" />
<el-table-column prop="uvPeakTotal"
label="独立访客范围" />
<el-table-column prop="llzsPeakTotal"
label="流量增速" />
label="日期" v-if="!dateMerge"/>
<el-table-column v-for="str in column"
:prop="columnKey[str]"
:label="str"
:formatter="formatterFn" />
</el-table>
</template>
<script lang="ts"
setup>
const props = defineProps({
const columnKey = {
'支付买家数': 'zfmj',
'交易增速': 'jyzs',
'独立访客范围': 'uv',
'流量增速': 'llzs'
}
defineProps({
column: {
type: Object,
default: () => {
......@@ -30,8 +43,39 @@
default: () => {
return []
},
},
dateMerge: { // 是否把日期数据合并
type: Boolean
}
})
console.log(props)
</script>
\ No newline at end of file
const formatterFn = (row, column, cellValue) => {
if (column.property === 'jyzs' || column.property === 'llzs') {
return cellValue + '%'
} else {
return cellValue
}
}
const emits = defineEmits(['changeType', 'queryTable', 'dateMergeFn'])
const queryTable = () => {
emits('queryTable')
}
const changeType = () => {
emits('changeType', 'charts')
}
const dateMergeFn = () => {
emits('dateMergeFn')
}
</script>
<style scoped
style="sass">
.toolbar {
float: right;
}
</style>
\ No newline at end of file
......@@ -9,9 +9,9 @@
clearable
collapse-tags
collapse-tags-tooltip>
<el-option v-for="str in brandList"
:label="str"
:value="str">
<el-option v-for="obj in brandList"
:label="obj.platformStore"
:value="obj.platformStore">
</el-option>
</el-select>
</el-form-item>
......@@ -42,21 +42,33 @@
</el-form>
<div class="chart_wrap"
v-loading="loading">
<template v-if="queryParams.brandList.length > 0 && queryParams.typeList.length > 0">
<template v-if="queryParams.brandList.length > 0 && queryParams.typeList.length > 0 && !loading">
<template v-if="showType === 'charts'">
<group-legend :legendData="chartData.legend"
@click-item="legendChangeFn"></group-legend>
<gradient-area :chartData="chartData"></gradient-area>
<gradient-area :chartData="chartData"
@changeType="changeType"></gradient-area>
</template>
<template v-else-if="showType === 'table'">
<table-list :column="queryParams" :data="useTableList"></table-list>
<table-list :column="queryParams.typeList"
:data="tableList"
@changeType="changeType"
@queryTable="getList"
@dateMergeFn="dateMergeFn"
:dateMerge="dateMerge"></table-list>
</template>
</template>
<no-data v-else></no-data>
<no-data v-else-if="!loading"></no-data>
</div>
<levitated-sphere>
<el-button type="danger"
@click="reset">重置本页</el-button>
<el-button type="primary"
@click="changeType(showType === 'charts' ? 'table' : 'charts')">切换{{ showType === 'charts' ? '表格' :
'图表' }}</el-button>
<el-button v-if="showType === 'table'"
type="warning"
@click="dateMergeFn">日期{{ dateMerge ? '分散' : '合并' }}</el-button>
</levitated-sphere>
</div>
</template>
......@@ -64,7 +76,7 @@
<script setup>
import GradientArea from './GradientArea.vue';
import TableList from './TableList.vue';
import { getSycmStoreListAPI } from '@/api'
import { getStoreListAPI, getSycmStoreListAPI } from '@/api'
import { generatorDayList, parseTime, getBrandColor, resetObjValue } from '@/utils'
import { useDatePickerOptions } from '@/hooks'
......@@ -73,12 +85,12 @@ const dateList = [new Date().setDate((new Date().getDate() - 30)), new Date().se
const dataTypeList = ['支付买家数', '交易增速', '独立访客范围', '流量增速'] // 数据类型
// 本页数据
const typeList = ref(dataTypeList)
const { pickerOptions } = useDatePickerOptions()
const brandList = ref([]) // 店铺列表
const loading = ref(true)
// const showType = ref('charts') // 展示类型('charts' / 'table')
const showType = ref('table') // 展示类型('charts' / 'table')
const brandList = ref([]) // 店铺列表
const typeList = ref(dataTypeList) // 数据类型
const { pickerOptions } = useDatePickerOptions()
const showType = ref('charts') // 展示类型('charts' / 'table')
const dateMerge = ref(false) // 表格中日期是否合并
const queryParams = reactive({ // 查询表单
brandList: [],
......@@ -98,21 +110,24 @@ const chartData = reactive({ // 图表内要用的数据
})
const allTableList = ref([])
const useTableList = ref([])
const tableList = ref([])
// 获取数据
const getList = async () => {
loading.value = true
const changeType = (val) => {
showType.value = val
}
// 获取竞店列表
const getStoreList = async () => {
const { data } = await getStoreListAPI()
brandList.value = data
// 初始化筛选条件(默认请求第一个店铺的第一类型数据)
queryParams.brandList = [data[0]?.platformStore]
}
// 初始化图表数据
const initChartsData = (data) => {
resetObjValue(allChartData)
// 获取 x 轴时间
allChartData.xAxis = generatorDayList(queryParams.date[0], queryParams.date[1])
const { data } = await getSycmStoreListAPI({
startDate: parseTime(queryParams.date[0], '{y}-{m}-{d}'),
endDate: parseTime(queryParams.date[1], '{y}-{m}-{d}'),
platformStore: '沃隆旗舰店,三只松鼠旗舰店'
})
// 表格数据
allTableList.value = data.flat()
// 图表数据
data.forEach(list => {
// list: 每个店铺数据集合
......@@ -183,10 +198,10 @@ const getList = async () => {
allChartData.xAxis.forEach(date => {
const findObj = list.find(o => o.date.includes(date))
if (findObj) {
jyzsObj.data.push((findObj.jyzsPeakTotal + findObj.jyzsUnderTotal) / 2)
llzsObj.data.push((findObj.llzsPeakTotal + findObj.llzsUnderTotal) / 2)
uvObj.data.push((findObj.uvPeakTotal + findObj.uvUnderTotal) / 2)
zfmjObj.data.push((findObj.zfmjPeakTotal + findObj.zfmjUnderTotal) / 2)
jyzsObj.data.push((findObj.jyzsPeak + findObj.jyzsUnder) / 2)
llzsObj.data.push((findObj.llzsPeak + findObj.llzsUnder) / 2)
uvObj.data.push((findObj.uvPeak + findObj.uvUnder) / 2)
zfmjObj.data.push((findObj.zfmjPeak + findObj.zfmjUnder) / 2)
} else {
jyzsObj.data.push(0)
llzsObj.data.push(0)
......@@ -223,46 +238,26 @@ const getList = async () => {
}],
orient: 'verticalAlign'
})
// allChartData.legend.push(zfmjObj.name, uvObj.name, llzsObj.name, jyzsObj.name)
allChartData.series.push(jyzsObj, llzsObj, uvObj, zfmjObj)
})
loading.value = false
return data
}
// 初始化表格数据
const initTableData = (data) => {
// 表格数据
allTableList.value = data
}
// 筛选数据
const filterData = () => {
// 筛选图表数据
const filterChartsData = () => {
let legend = allChartData.legend
let series = allChartData.series
if (queryParams.brandList.length > 0) {
// 选择了店铺,筛选
series = series.filter(item => {
return queryParams.brandList.find(o => o === item.name.split('-')[0])
})
legend = legend.filter(obj => {
return queryParams.brandList.find(nameStr => obj.data.find(o => o.name.split('-')[0] === nameStr))
})
// 表格数据筛选
useTableList.value = allTableList.value.filter(item => {
return queryParams.brandList.find(o => o === item.platformStore)
})
} else {
// 店铺都没选
legend.forEach(obj => {
obj.data.forEach(o => {
o.show = true
})
})
}
// 选择了数据类型,筛选
if (queryParams.typeList.length > 0) {
series = series.filter(item => {
return queryParams.typeList.find(o => o === item.name.split('-')[1])
})
// 图例组件数据源不变,只是显示/隐藏
// // 图例组件数据源不变,只是显示/隐藏
legend.filter(obj => {
obj.data.forEach((o, index) => {
const isHave = queryParams.typeList.includes(o.name.split('-')[1])
......@@ -279,6 +274,7 @@ const filterData = () => {
})
})
}
// 据图例状态,筛选数据显示
series = series.filter(sObj => {
let nowSeries = true
......@@ -296,45 +292,109 @@ const filterData = () => {
chartData.legend = legend
chartData.series = series
console.log(allChartData.xAxis)
chartData.xAxis = allChartData.xAxis
}
// 筛选表格数据
const filterTableData = () => {
// 没有日期合并
if (dateMerge.value) {
tableList.value = allTableList.value.map(o => {
const platformStore = o[0].platformStore
const zfmj = (o.reduce((sum, obj) => {
return sum += ((obj.zfmjPeak + obj.zfmjUnder) / 2)
}, 0) / o.length).toFixed(2)
const jyzs = (o.reduce((sum, obj) => {
return sum += ((obj.jyzsPeak + obj.jyzsUnder) / 2)
}, 0) / o.length).toFixed(2)
const uv = (o.reduce((sum, obj) => {
return sum += ((obj.uvPeak + obj.uvUnder) / 2)
}, 0) / o.length).toFixed(2)
const llzs = (o.reduce((sum, obj) => {
return sum += ((obj.llzsPeak + obj.llzsUnder) / 2)
}, 0) / o.length).toFixed(2)
return {
platformStore, zfmj, jyzs, uv, llzs
}
})
} else {
tableList.value = allTableList.value.flat().map(o => {
return {
...o,
zfmj: (o.zfmjPeak + o.zfmjUnder) / 2,
jyzs: (o.jyzsPeak + o.jyzsUnder) / 2,
uv: (o.uvPeak + o.uvUnder) / 2,
llzs: (o.llzsPeak + o.llzsUnder) / 2,
}
})
}
}
// 获取数据
const getList = async () => {
loading.value = true
const { data } = await getSycmStoreListAPI({
startDate: parseTime(queryParams.date[0], '{y}-{m}-{d}'),
endDate: parseTime(queryParams.date[1], '{y}-{m}-{d}'),
platformStore: queryParams.brandList.join(','),
dataType: queryParams.typeList.join(',')
})
if (showType.value === 'charts') {
initChartsData(data)
filterChartsData()
}
else if (showType.value === 'table') {
initTableData(data)
filterTableData()
}
loading.value = false
return data
}
// 表格数据合并
const dateMergeFn = () => {
dateMerge.value = !dateMerge.value
filterTableData()
}
// 默认打开页面请求一次所有数据,并保存在数据源
async function init() {
const data = await getList()
// 直播间列表
brandList.value = data.map(list => list[0].platformStore)
// 初始化筛选条件(默认请求第一个店铺的第一类型数据)
queryParams.brandList = [data[0][0].platformStore]
// 筛选图表数据
filterData()
await getStoreList()
await getList()
};
watch(showType, () => {
getList()
})
onMounted(() => {
init()
})
const legendChangeFn = () => {
filterData()
filterChartsData()
}
// 查询条件改变
const queryChangeFn = async (arg) => {
await getList()
// 数据筛选
filterData()
getList()
}
// 重置
const reset = async () => {
// 重置查询参数
loading.value = true
queryParams.brandList = []
queryParams.date = dateList
queryParams.typeList = [dataTypeList[0]]
init()
}
</script>
<style scoped></style>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论