Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
mengchangaigc
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
姚珂
mengchangaigc
Commits
034d0c1b
Commit
034d0c1b
authored
Aug 24, 2026
by
黄同智
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
广告账户分析、团队分析、直播管理等页面的交互和布局
parent
0983a8bd
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
2467 additions
and
102 deletions
+2467
-102
index.db
apps/web-vue/.pnpm-store/v11/index.db
+0
-0
package.json
apps/web-vue/package.json
+1
-0
index.vue
apps/web-vue/src/layout/index.vue
+5
-0
index.ts
apps/web-vue/src/router/index.ts
+13
-0
ad.vue
apps/web-vue/src/views/analysis/ad.vue
+35
-5
Tab1.vue
apps/web-vue/src/views/analysis/components/AD/Tab1.vue
+361
-0
Tab2.vue
apps/web-vue/src/views/analysis/components/AD/Tab2.vue
+204
-0
Tab3.vue
apps/web-vue/src/views/analysis/components/AD/Tab3.vue
+200
-0
Tab4.vue
apps/web-vue/src/views/analysis/components/AD/Tab4.vue
+224
-0
Tab1.vue
apps/web-vue/src/views/analysis/components/Team/Tab1.vue
+180
-0
Tab2.vue
apps/web-vue/src/views/analysis/components/Team/Tab2.vue
+190
-0
Tab3.vue
apps/web-vue/src/views/analysis/components/Team/Tab3.vue
+220
-0
TaskDetailDialog.vue
...e/src/views/analysis/components/Team/TaskDetailDialog.vue
+92
-0
TaskStatsDialog.vue
...ue/src/views/analysis/components/Team/TaskStatsDialog.vue
+103
-0
AdPlatformTags.vue
...ue/src/views/analysis/components/video/AdPlatformTags.vue
+24
-17
DeliveryReport.vue
...ue/src/views/analysis/components/video/DeliveryReport.vue
+41
-39
TagAnalysis.vue
...b-vue/src/views/analysis/components/video/TagAnalysis.vue
+3
-3
TencentDeliveryReport.vue
...views/analysis/components/video/TencentDeliveryReport.vue
+52
-29
department.vue
apps/web-vue/src/views/analysis/department.vue
+33
-5
video.vue
apps/web-vue/src/views/analysis/video.vue
+4
-4
index.vue
apps/web-vue/src/views/zhibo/index.vue
+482
-0
No files found.
apps/web-vue/.pnpm-store/v11/index.db
0 → 100644
View file @
034d0c1b
File added
apps/web-vue/package.json
View file @
034d0c1b
...
...
@@ -9,6 +9,7 @@
"preview"
:
"vite preview"
},
"dependencies"
:
{
"@element-plus/icons-vue"
:
"^2.3.2"
,
"@ffmpeg/ffmpeg"
:
"^0.12.15"
,
"@ffmpeg/util"
:
"^0.12.2"
,
"@vue-flow/additional-components"
:
"^1.3.3"
,
...
...
apps/web-vue/src/layout/index.vue
View file @
034d0c1b
...
...
@@ -110,6 +110,11 @@ const menus = [
icon
:
'redraw'
,
},
{
name
:
'直播管理'
,
path
:
'/zhibo'
,
icon
:
'map'
,
},
{
name
:
'任务协作'
,
path
:
'/task'
,
icon
:
'map'
,
...
...
apps/web-vue/src/router/index.ts
View file @
034d0c1b
...
...
@@ -30,6 +30,19 @@ const routes = [
]
},
{
path
:
'/zhibo'
,
component
:
Layout
,
children
:
[
{
path
:
'zhibo'
,
alias
:
''
,
name
:
'Zhibo'
,
component
:
import
(
'@/views/zhibo/index.vue'
),
meta
:
{
title
:
'直播管理'
,
icon
:
'House'
,
affix
:
true
}
}
]
},
{
path
:
'/task'
,
component
:
Layout
,
children
:
[
...
...
apps/web-vue/src/views/analysis/ad.vue
View file @
034d0c1b
<
template
>
<div>
<div
class=
"bgmr vh100 over-y-auto"
>
<section
class=
"bg-fff px-20 py-10 analysis-tabs"
>
<div
class=
"flex-items-center gap-16"
>
<div
v-for=
"item in tabs"
:key=
"item.value"
class=
"px-12 py-8 round-6 font-bold pointer"
:class=
"activeTab === item.value ? 'bg-main color-fff' : 'color-000'"
@
click=
"activeTab = item.value"
>
{{
item
.
label
}}
</div>
</div>
</section>
<div
class=
"p-16"
>
<component
:is=
"currentComponent"
/>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
}
from
'vue'
;
import
Tab1
from
'./components/AD/Tab1.vue'
;
import
Tab2
from
'./components/AD/Tab2.vue'
;
import
Tab3
from
'./components/AD/Tab3.vue'
;
import
Tab4
from
'./components/AD/Tab4.vue'
;
const
activeTab
=
ref
(
'tab1'
);
const
tabs
=
[
{
label
:
'广告账户数据'
,
value
:
'tab1'
,
component
:
Tab1
},
{
label
:
'广告账户财务报表'
,
value
:
'tab2'
,
component
:
Tab2
},
{
label
:
'投放状态报表'
,
value
:
'tab3'
,
component
:
Tab3
},
{
label
:
'TikTok店铺'
,
value
:
'tab4'
,
component
:
Tab4
},
];
const
currentComponent
=
computed
(()
=>
tabs
.
find
((
item
)
=>
item
.
value
===
activeTab
.
value
)?.
component
);
</
script
>
<
style
scoped
lang=
"scss"
>
</
style
>
\ No newline at end of file
.analysis-tabs
{
border-bottom
:
1px
solid
#dfe7f2
;
}
</
style
>
apps/web-vue/src/views/analysis/components/AD/Tab1.vue
0 → 100644
View file @
034d0c1b
<
template
>
<div
class=
"ad-report"
>
<el-card
class=
"report-card round-12"
shadow=
"never"
>
<div
class=
"flex-between tab-row"
>
<div
class=
"flex gap-30"
>
<button
v-for=
"item in platformTabs"
:key=
"item"
class=
"text-tab"
:class=
"
{ active: platform === item }" @click="platform = item">
{{
item
}}
</button>
</div>
<div
class=
"w-300"
>
<el-date-picker
v-model=
"sForm.range"
type=
"daterange"
range-separator=
"至"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
style=
"width: 100%;"
/>
</div>
</div>
<div
class=
"mt-16 flex gap-10"
>
<el-radio-group
v-model=
"metric"
class=
"mr-10"
>
<el-radio-button
v-for=
"(item, index) in metricTabs"
:label=
"item"
:value=
"item"
:key=
"index"
/>
</el-radio-group>
<el-select
v-model=
"sForm.department"
placeholder=
"请选择部门"
style=
"width: 200px"
>
<el-option
v-for=
"item in options1"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
<el-select
v-model=
"sForm.group"
placeholder=
"请选择分组"
style=
"width: 200px"
>
<el-option
v-for=
"item in options2"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
<el-select
v-model=
"sForm.userId"
placeholder=
"请选择账户"
style=
"width: 200px"
>
<el-option
v-for=
"item in options3"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
<el-select
v-model=
"sForm.type"
placeholder=
"请选择分类"
style=
"width: 200px"
>
<el-option
v-for=
"item in options4"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
<el-button
@
click=
"reset"
>
重置
</el-button>
</div>
</el-card>
<el-card
class=
"report-card round-12 mt-16"
shadow=
"never"
>
<div
class=
"flex-between mb-16"
>
<el-radio-group
v-model=
"promotion"
class=
"mr-10"
>
<el-radio-button
v-for=
"(item, index) in promotionTabs"
:label=
"item"
:value=
"item"
:key=
"index"
/>
</el-radio-group>
<div
class=
"flex-between gap-16"
>
<div
class=
"toolbar-left"
>
<el-input
v-model=
"sForm.advertiserId"
class=
"w-170"
placeholder=
"请输入广告主ID"
clearable
/>
<el-input
v-model=
"sForm.advertiserName"
class=
"w-170"
placeholder=
"请输入广告主名称"
clearable
/>
<span
class=
"muted"
>
未绑定数据
</span>
<el-button
link
type=
"primary"
@
click=
"openBind"
>
点击前往
</el-button>
<el-button
@
click=
"reset"
>
重置
</el-button>
</div>
<div
class=
"toolbar-right"
>
<el-button
@
click=
"exportData"
>
导出
</el-button>
<el-button
class=
"icon-button"
@
click=
"settingVisible = !settingVisible"
>
☷
</el-button>
</div>
</div>
</div>
<el-alert
v-if=
"settingVisible"
class=
"mb-10"
title=
"已打开字段配置面板"
type=
"info"
show-icon
:closable=
"false"
/>
<el-table
v-if=
"metric === '直播间汇总'"
:data=
"pagedRows"
class=
"analysis-table"
row-key=
"room"
>
<el-table-column
fixed
prop=
"room"
label=
"直播间"
min-width=
"200"
show-overflow-tooltip
/>
<el-table-column
prop=
"owner"
label=
"负责人"
width=
"130"
align=
"center"
/>
<el-table-column
prop=
"cost"
label=
"消耗 ↑↓"
width=
"140"
align=
"center"
>
<template
#
default=
"
{ row }">
<span
:class=
"row.highlightCost ? 'color-main font-bold' : 'font-bold'"
>
{{
row
.
cost
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"roi"
label=
"roi"
width=
"110"
align=
"center"
>
<
template
#
default=
"{ row }"
><span
class=
"color-red font-bold"
>
{{
row
.
roi
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"amount"
label=
"成交金额"
width=
"150"
align=
"center"
/>
<el-table-column
prop=
"coupon"
label=
"智能优惠券"
width=
"150"
align=
"center"
/>
<el-table-column
prop=
"conversion"
label=
"转化数"
width=
"120"
align=
"center"
/>
<el-table-column
prop=
"rate"
label=
"转化率"
width=
"120"
align=
"center"
/>
<el-table-column
prop=
"unitCost"
label=
"转化成本"
width=
"130"
align=
"center"
/>
<el-table-column
prop=
"show"
label=
"展示数"
width=
"150"
align=
"center"
/>
</el-table>
<el-table
v-else-if=
"metric === '人员数据'"
:data=
"pagedRows"
class=
"analysis-table"
row-key=
"name"
>
<el-table-column
fixed
prop=
"name"
label=
"人员名称"
min-width=
"220"
>
<
template
#
default=
"{ row }"
><span
:class=
"row.link ? 'link-text font-bold' : 'font-bold'"
>
{{
row
.
name
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"accountCount"
label=
"关联账户数"
min-width=
"130"
align=
"center"
/>
<el-table-column
prop=
"cost"
label=
"消耗"
min-width=
"140"
align=
"center"
>
<
template
#
default=
"{ row }"
><span
:class=
"row.highlightCost ? 'color-main font-bold' : 'font-bold'"
>
{{
row
.
cost
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"roi"
label=
"roi"
min-width=
"110"
align=
"center"
>
<
template
#
default=
"{ row }"
><span
class=
"color-red font-bold"
>
{{
row
.
roi
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"amount"
label=
"成交金额"
min-width=
"150"
align=
"center"
/>
<el-table-column
prop=
"coupon"
label=
"智能优惠券"
min-width=
"150"
align=
"center"
/>
<el-table-column
prop=
"conversion"
label=
"转化数"
min-width=
"120"
align=
"center"
/>
<el-table-column
prop=
"rate"
label=
"转化率"
min-width=
"120"
align=
"center"
/>
<el-table-column
prop=
"unitCost"
label=
"转化成本"
min-width=
"130"
align=
"center"
/>
</el-table>
<el-table
v-else-if=
"metric === '分类数据'"
:data=
"pagedRows"
class=
"analysis-table"
row-key=
"firstCategory"
>
<el-table-column
fixed
prop=
"firstCategory"
label=
"一级分类"
min-width=
"180"
/>
<el-table-column
prop=
"secondCategory"
label=
"二级分类"
min-width=
"200"
/>
<el-table-column
prop=
"accountCount"
label=
"关联账户数"
min-width=
"130"
align=
"center"
/>
<el-table-column
prop=
"cost"
label=
"消耗"
min-width=
"140"
align=
"center"
>
<
template
#
default=
"{ row }"
><span
:class=
"row.highlightCost ? 'color-main font-bold' : 'font-bold'"
>
{{
row
.
cost
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"roi"
label=
"roi"
min-width=
"110"
align=
"center"
>
<
template
#
default=
"{ row }"
><span
class=
"color-red font-bold"
>
{{
row
.
roi
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"amount"
label=
"成交金额"
min-width=
"150"
align=
"center"
/>
<el-table-column
prop=
"coupon"
label=
"智能优惠券"
min-width=
"150"
align=
"center"
/>
<el-table-column
prop=
"conversion"
label=
"转化数"
min-width=
"120"
align=
"center"
/>
</el-table>
<el-table
v-else
:data=
"pagedRows"
class=
"analysis-table"
row-key=
"account"
>
<el-table-column
fixed
prop=
"account"
label=
"广告账户"
min-width=
"240"
>
<
template
#
default=
"{ row }"
>
<div
class=
"font-bold"
>
{{
row
.
account
}}
</div>
<div
v-if=
"row.id"
class=
"fs-12 color-8aa"
>
{{
row
.
id
}}
</div>
</
template
>
</el-table-column>
<el-table-column
prop=
"department"
label=
"部门"
min-width=
"140"
>
<
template
#
default=
"{ row }"
><span
:class=
"row.linkDepartment ? 'link-text' : ''"
>
{{
row
.
department
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"group"
label=
"分组"
min-width=
"140"
>
<
template
#
default=
"{ row }"
><span
:class=
"row.linkGroup ? 'link-text' : ''"
>
{{
row
.
group
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"user"
label=
"用户"
min-width=
"120"
/>
<el-table-column
prop=
"firstCategory"
label=
"一级分类"
min-width=
"150"
>
<
template
#
default=
"{ row }"
><span
:class=
"row.linkCategory ? 'link-text' : ''"
>
{{
row
.
firstCategory
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"secondCategory"
label=
"二级分类"
min-width=
"150"
>
<
template
#
default=
"{ row }"
><span
:class=
"row.linkCategory ? 'link-text' : ''"
>
{{
row
.
secondCategory
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"cost"
label=
"消耗 ↑↓"
min-width=
"140"
align=
"center"
>
<
template
#
default=
"{ row }"
><span
:class=
"row.highlightCost ? 'color-main font-bold' : 'font-bold'"
>
{{
row
.
cost
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"roi"
label=
"roi"
min-width=
"110"
align=
"center"
>
<
template
#
default=
"{ row }"
><span
class=
"color-red font-bold"
>
{{
row
.
roi
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"amount"
label=
"成交金额"
min-width=
"150"
align=
"center"
/>
<el-table-column
prop=
"coupon"
label=
"智能优惠券"
min-width=
"150"
align=
"center"
/>
</el-table>
<div
class=
"table-footer"
>
<el-pagination
v-model:current-page=
"pageData.currentPage"
v-model:page-size=
"pageData.pageSize"
background
layout=
"total, sizes, prev, pager, next, jumper"
:total=
"currentTableRows.length"
:page-sizes=
"pageData.pageSizes"
@
size-change=
"handleSizeChange"
/>
</div>
</el-card>
</div>
</template>
<
script
setup
lang=
"ts"
>
import
{
computed
,
reactive
,
ref
}
from
'vue'
;
import
{
ElMessage
}
from
'element-plus'
;
const
platform
=
ref
(
'巨量千川'
);
const
promotion
=
ref
(
'标准推广'
);
const
metric
=
ref
(
'直播间汇总'
);
const
settingVisible
=
ref
(
false
);
const
pageData
=
reactive
({
currentPage
:
1
,
pageSize
:
50
,
pageSizes
:
[
10
,
20
,
50
,
100
],
});
const
sForm
=
ref
({
department
:
undefined
,
group
:
undefined
,
userId
:
undefined
,
type
:
undefined
,
range
:
undefined
,
advertiserId
:
''
,
advertiserName
:
''
})
const
platformTabs
=
[
'巨量千川'
,
'巨量广告'
,
'巨量本地推'
,
'磁力智投'
,
'磁力金牛'
,
'腾讯ADQ'
,
'TikTok'
,
'百度营销'
,
'淘宝超级短视频'
];
const
promotionTabs
=
[
'标准推广'
,
'直播全域推广'
,
'商品全域推广'
];
const
metricTabs
=
[
'直播间汇总'
,
'人员数据'
,
'分类数据'
,
'广告主明细'
];
const
options1
=
ref
([
{
label
:
'A部门'
,
value
:
1
},
{
label
:
'B部门'
,
value
:
2
},
{
label
:
'外部部门'
,
value
:
3
},
{
label
:
'未绑定部门分组'
,
value
:
4
},
])
const
options2
=
ref
([
{
label
:
'核心二组'
,
value
:
1
},
{
label
:
'B部门'
,
value
:
2
},
{
label
:
'外部部门'
,
value
:
3
},
{
label
:
'未绑定部门分组'
,
value
:
4
},
])
const
options3
=
ref
([
{
label
:
'张伟'
,
value
:
1
},
{
label
:
'瑶光'
,
value
:
2
},
{
label
:
'徐振'
,
value
:
3
},
])
const
options4
=
ref
([
{
label
:
'美妆护肤'
,
value
:
1
},
{
label
:
'3C数码'
,
value
:
2
},
])
const
liveRoomRows
=
[
{
room
:
'总计'
,
owner
:
'-'
,
cost
:
'406200.50'
,
roi
:
'3.58'
,
amount
:
'¥1,454,649.9'
,
coupon
:
'¥90,400'
,
conversion
:
'13,020'
,
rate
:
'-'
,
unitCost
:
'-'
,
show
:
'8,250,000'
,
highlightCost
:
true
},
{
room
:
'未绑定直播间'
,
owner
:
'-'
,
cost
:
'0.00'
,
roi
:
'0.00'
,
amount
:
'¥0'
,
coupon
:
'¥0'
,
conversion
:
'0'
,
rate
:
'-'
,
unitCost
:
'-'
,
show
:
'0'
},
{
room
:
'直播间-美妆旗舰店爆品 (ID: 89021)'
,
owner
:
'展展'
,
cost
:
'185400.50'
,
roi
:
'3.92'
,
amount
:
'¥726,769.9'
,
coupon
:
'¥42,100'
,
conversion
:
'6,180'
,
rate
:
'4.85%'
,
unitCost
:
'30.00'
,
show
:
'3,820,000'
},
{
room
:
'直播间-3C数码科技专场 (ID: 89035)'
,
owner
:
'李强'
,
cost
:
'124000.00'
,
roi
:
'3.45'
,
amount
:
'¥427,800'
,
coupon
:
'¥28,500'
,
conversion
:
'3,950'
,
rate
:
'4.12%'
,
unitCost
:
'31.39'
,
show
:
'2,450,000'
},
{
room
:
'直播间-服装鞋包新品发布 (ID: 89048)'
,
owner
:
'王丹'
,
cost
:
'96800.00'
,
roi
:
'3.10'
,
amount
:
'¥300,080'
,
coupon
:
'¥19,800'
,
conversion
:
'2,890'
,
rate
:
'3.80%'
,
unitCost
:
'33.49'
,
show
:
'1,980,000'
},
];
const
peopleRows
=
[
{
name
:
'总计'
,
accountCount
:
'28'
,
cost
:
'481500.50'
,
roi
:
'3.62'
,
amount
:
'¥1,740,760'
,
coupon
:
'¥102,500'
,
conversion
:
'15,380'
,
rate
:
'-'
,
unitCost
:
'-'
,
highlightCost
:
true
},
{
name
:
'张伟 (投放主管)'
,
accountCount
:
'12 个'
,
cost
:
'215000.00'
,
roi
:
'3.88'
,
amount
:
'¥834,200'
,
coupon
:
'¥48,000'
,
conversion
:
'6,980'
,
rate
:
'4.65%'
,
unitCost
:
'30.80'
,
link
:
true
},
{
name
:
'李娜 (高级优化师)'
,
accountCount
:
'8 个'
,
cost
:
'168000.50'
,
roi
:
'3.52'
,
amount
:
'¥591,360'
,
coupon
:
'¥35,000'
,
conversion
:
'5,320'
,
rate
:
'4.30%'
,
unitCost
:
'31.58'
,
link
:
true
},
{
name
:
'王磊 (主播运营)'
,
accountCount
:
'6 个'
,
cost
:
'98500.00'
,
roi
:
'3.20'
,
amount
:
'¥315,200'
,
coupon
:
'¥19,500'
,
conversion
:
'3,080'
,
rate
:
'3.90%'
,
unitCost
:
'31.98'
,
link
:
true
},
{
name
:
'未绑定人员账号'
,
accountCount
:
'2 个'
,
cost
:
'0.00'
,
roi
:
'0.00'
,
amount
:
'¥0'
,
coupon
:
'¥0'
,
conversion
:
'0'
,
rate
:
'-'
,
unitCost
:
'-'
,
link
:
true
},
];
const
categoryRows
=
[
{
firstCategory
:
'总计'
,
secondCategory
:
'-'
,
accountCount
:
'32'
,
cost
:
'476000.00'
,
roi
:
'3.63'
,
amount
:
'¥1,728,230'
,
coupon
:
'¥96,500'
,
conversion
:
'15,020'
,
highlightCost
:
true
},
{
firstCategory
:
'美妆护肤'
,
secondCategory
:
'面部精华/霜'
,
accountCount
:
'15 个'
,
cost
:
'245000.00'
,
roi
:
'3.95'
,
amount
:
'¥967,750'
,
coupon
:
'¥52,000'
,
conversion
:
'7,850'
},
{
firstCategory
:
'3C数码'
,
secondCategory
:
'智能穿戴与耳机'
,
accountCount
:
'9 个'
,
cost
:
'142000.00'
,
roi
:
'3.40'
,
amount
:
'¥482,800'
,
coupon
:
'¥28,000'
,
conversion
:
'4,420'
},
{
firstCategory
:
'个人护理'
,
secondCategory
:
'身体洗护'
,
accountCount
:
'6 个'
,
cost
:
'89000.00'
,
roi
:
'3.12'
,
amount
:
'¥277,680'
,
coupon
:
'¥16,500'
,
conversion
:
'2,750'
},
{
firstCategory
:
'未绑定分类'
,
secondCategory
:
'未绑定分类'
,
accountCount
:
'2 个'
,
cost
:
'0.00'
,
roi
:
'0.00'
,
amount
:
'¥0'
,
coupon
:
'¥0'
,
conversion
:
'0'
},
];
const
advertiserRows
=
[
{
account
:
'总计'
,
id
:
''
,
department
:
'-'
,
group
:
'-'
,
user
:
'-'
,
firstCategory
:
'-'
,
secondCategory
:
'-'
,
cost
:
'292800.50'
,
roi
:
'3.55'
,
amount
:
'¥1,038,165.7'
,
coupon
:
'¥63,400'
,
highlightCost
:
true
},
{
account
:
'直播-铃音-牧唐-芜湖1'
,
id
:
'1787869271614468'
,
department
:
'未绑定分组..'
,
group
:
'未绑定分组'
,
user
:
'-'
,
firstCategory
:
'未绑定分类..'
,
secondCategory
:
'未绑定分类'
,
cost
:
'0.00'
,
roi
:
'0.00'
,
amount
:
'¥0'
,
coupon
:
'¥0'
,
linkDepartment
:
true
,
linkGroup
:
true
,
linkCategory
:
true
},
{
account
:
'直播-颜姿2-牧唐-芜湖'
,
id
:
'1783526642794571'
,
department
:
'未绑定分组..'
,
group
:
'未绑定分组'
,
user
:
'-'
,
firstCategory
:
'未绑定分类..'
,
secondCategory
:
'未绑定分类'
,
cost
:
'0.00'
,
roi
:
'0.00'
,
amount
:
'¥0'
,
coupon
:
'¥0'
,
linkDepartment
:
true
,
linkGroup
:
true
,
linkCategory
:
true
},
{
account
:
'千川直通车-美妆03'
,
id
:
'1792182049182310'
,
department
:
'A部门'
,
group
:
'核心二组'
,
user
:
'张伟'
,
firstCategory
:
'美妆护肤'
,
secondCategory
:
'精华面霜'
,
cost
:
'142500.00'
,
roi
:
'3.85'
,
amount
:
'¥548,625'
,
coupon
:
'¥31,200'
,
linkDepartment
:
true
,
linkGroup
:
true
,
linkCategory
:
true
},
{
account
:
'千川直播号-数码01'
,
id
:
'1795123019823122'
,
department
:
'C部门'
,
group
:
'管理层组'
,
user
:
'李娜'
,
firstCategory
:
'3C数码'
,
secondCategory
:
'蓝牙耳机'
,
cost
:
'98200.50'
,
roi
:
'3.42'
,
amount
:
'¥335,845.7'
,
coupon
:
'¥21,000'
,
linkDepartment
:
true
,
linkGroup
:
true
,
linkCategory
:
true
},
{
account
:
'巨量信息流-洗护备用'
,
id
:
'1781203912039102'
,
department
:
'外部部门'
,
group
:
'分组一'
,
user
:
'zs_test'
,
firstCategory
:
'个人护理'
,
secondCategory
:
'洗发护发'
,
cost
:
'52100.00'
,
roi
:
'2.95'
,
amount
:
'¥153,695'
,
coupon
:
'¥11,200'
,
linkDepartment
:
true
,
linkGroup
:
true
,
linkCategory
:
true
},
];
const
currentTableRows
=
computed
(()
=>
{
if
(
metric
.
value
===
'人员数据'
)
return
peopleRows
;
if
(
metric
.
value
===
'分类数据'
)
return
categoryRows
;
if
(
metric
.
value
===
'广告主明细'
)
return
advertiserRows
;
return
liveRoomRows
;
});
const
pagedRows
=
computed
(()
=>
{
const
start
=
(
pageData
.
currentPage
-
1
)
*
pageData
.
pageSize
;
return
currentTableRows
.
value
.
slice
(
start
,
start
+
pageData
.
pageSize
);
});
const
reset
=
()
=>
{
sForm
.
value
.
department
=
undefined
sForm
.
value
.
group
=
undefined
sForm
.
value
.
userId
=
undefined
sForm
.
value
.
type
=
undefined
sForm
.
value
.
range
=
undefined
sForm
.
value
.
advertiserId
=
''
sForm
.
value
.
advertiserName
=
''
};
const
openBind
=
()
=>
ElMessage
.
info
(
'跳转到未绑定数据处理页'
);
const
exportData
=
()
=>
ElMessage
.
success
(
'已生成导出任务'
);
const
setMetric
=
(
value
:
string
)
=>
{
metric
.
value
=
value
;
pageData
.
currentPage
=
1
;
};
const
handleSizeChange
=
()
=>
{
pageData
.
currentPage
=
1
;
};
</
script
>
<
style
scoped
lang=
"scss"
>
.ad-report
{
color
:
#10233f
;
}
.report-card
{
border
:
1px
solid
#dfe7f2
;
}
.tab-row
{
border-bottom
:
1px
solid
#edf0f5
;
}
.metric-tabs
{
gap
:
34px
;
}
.text-tab
{
height
:
44px
;
padding
:
0
;
border
:
0
;
border-bottom
:
2px
solid
transparent
;
background
:
transparent
;
color
:
#3f5574
;
cursor
:
pointer
;
&
.small
{
height
:
40px
;
font-size
:
13px
;
}
&
.active
{
color
:
rgba
(
var
(
--
main-color
)
,
1
);
border-bottom-color
:
rgba
(
var
(
--
main-color
)
,
1
);
}
}
.toolbar-left
,
.toolbar-right
{
display
:
flex
;
align-items
:
center
;
gap
:
10px
;
flex-wrap
:
wrap
;
}
.w-150
{
width
:
150px
;
}
.w-170
{
width
:
170px
;
}
.muted
{
color
:
#607086
;
font-size
:
13px
;
}
.icon-button
{
width
:
34px
;
padding
:
0
;
}
.color-main
{
color
:
rgba
(
var
(
--
main-color
)
,
1
);
}
.color-red
{
color
:
#ff0055
;
}
.scroll-bar
{
height
:
5px
;
margin
:
10px
0
14px
;
border-radius
:
999px
;
background
:
#dfe7f2
;
}
.table-footer
{
display
:
flex
;
justify-content
:
flex-end
;
padding-top
:
10px
;
}
.analysis-table
{
:deep
(
.el-table__header
th
)
{
background
:
#f5f5f5
;
color
:
#888
;
padding
:
12px
0
;
}
}
</
style
>
apps/web-vue/src/views/analysis/components/AD/Tab2.vue
0 → 100644
View file @
034d0c1b
<
template
>
<div
class=
"finance-report"
>
<el-card
class=
"report-card"
shadow=
"never"
>
<div
class=
"platform-tabs"
>
<button
v-for=
"item in platforms"
:key=
"item"
class=
"text-tab"
:class=
"
{ active: platform === item }"
@click="platform = item">
{{
item
}}
</button>
</div>
<div
class=
"flex items-center gap-10 flex-wrap mt-16"
>
<el-input
v-model=
"keyword"
class=
"w-180"
placeholder=
"请输入账户名称/ID"
clearable
/>
<el-select
v-model=
"department"
class=
"w-150"
placeholder=
"请选择部门"
>
<el-option
label=
"A部门"
value=
"A部门"
/>
<el-option
label=
"未绑定部门"
value=
"未绑定部门"
/>
</el-select>
<el-select
v-model=
"group"
class=
"w-150"
placeholder=
"请选择分组"
>
<el-option
label=
"核心一组"
value=
"核心一组"
/>
<el-option
label=
"未绑定分组"
value=
"未绑定分组"
/>
</el-select>
<el-select
v-model=
"account"
class=
"w-150"
placeholder=
"请选择账号"
>
<el-option
label=
"张伟"
value=
"张伟"
/>
<el-option
label=
"未绑定账号"
value=
"未绑定账号"
/>
</el-select>
<el-select
v-model=
"category"
class=
"w-150"
placeholder=
"请选择分类"
>
<el-option
label=
"美妆护肤"
value=
"美妆护肤"
/>
<el-option
label=
"洗面奶"
value=
"洗面奶"
/>
</el-select>
<div
class=
""
>
<el-date-picker
v-model=
"date"
class=
"w-170 mr-10"
type=
"date"
placeholder=
"选择时间"
/>
<el-button
type=
"primary"
@
click=
"query"
>
查询
</el-button>
<el-button
@
click=
"reset"
>
重置
</el-button>
</div>
</div>
</el-card>
<el-card
class=
"report-card mt-16"
shadow=
"never"
>
<div
class=
"flex-between pb-12 px-6"
>
<span
class=
"font-bold"
>
详细数据
</span>
<el-button
@
click=
"exportData"
>
导出
</el-button>
</div>
<el-table
:data=
"pagedRows"
class=
"analysis-table"
row-key=
"name"
>
<el-table-column
fixed
prop=
"name"
label=
"广告账户"
min-width=
"220"
>
<template
#
default=
"
{ row }">
<div
class=
"font-bold"
>
{{
row
.
name
}}
</div>
<div
v-if=
"row.id"
class=
"fs-12 color-8aa"
>
{{
row
.
id
}}
</div>
</
template
>
</el-table-column>
<el-table-column
prop=
"department"
label=
"部门"
width=
"110"
>
<
template
#
default=
"{ row }"
><span
class=
"link-text"
>
{{
row
.
department
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"group"
label=
"分组"
width=
"110"
>
<
template
#
default=
"{ row }"
><span
class=
"link-text"
>
{{
row
.
group
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"user"
label=
"关联用户"
width=
"110"
>
<
template
#
default=
"{ row }"
><span
class=
"link-text"
>
{{
row
.
user
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"firstCategory"
label=
"一级分类"
width=
"120"
>
<
template
#
default=
"{ row }"
><span
class=
"link-text"
>
{{
row
.
firstCategory
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"secondCategory"
label=
"二级分类"
width=
"120"
>
<
template
#
default=
"{ row }"
><span
class=
"link-text"
>
{{
row
.
secondCategory
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"totalCost"
label=
"总消耗 ⓘ ↑↓"
width=
"140"
align=
"right"
/>
<el-table-column
prop=
"walletCost"
label=
"非赠款消耗 ⓘ ↑↓"
width=
"150"
align=
"right"
/>
<el-table-column
prop=
"grantCost"
label=
"赠款消耗 ⓘ ↑↓"
width=
"140"
align=
"right"
/>
<el-table-column
prop=
"refund"
label=
"消返红包消耗 ⓘ ↑↓"
width=
"160"
align=
"right"
/>
<el-table-column
prop=
"sharedWallet"
label=
"共享钱包消耗 ⓘ ↑↓"
width=
"160"
align=
"right"
/>
<el-table-column
prop=
"deposit"
label=
"总存入 ⓘ ↑↓"
width=
"130"
align=
"right"
>
<
template
#
default=
"{ row }"
><span
class=
"color-main font-bold"
>
{{
row
.
deposit
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"transferIn"
label=
"总转入 ⓘ ↑↓"
width=
"130"
align=
"right"
>
<
template
#
default=
"{ row }"
><span
class=
"color-main font-bold"
>
{{
row
.
transferIn
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"transferOut"
label=
"总转出 ↑↓"
width=
"130"
align=
"right"
/>
<el-table-column
prop=
"balance"
label=
"总余额 ↑↓"
width=
"140"
align=
"right"
>
<
template
#
default=
"{ row }"
><span
class=
"color-green font-bold"
>
{{
row
.
balance
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"grantBalance"
label=
"赠款余额 ↑↓"
width=
"140"
align=
"right"
/>
<el-table-column
prop=
"cashBalance"
label=
"非赠款余额 ↑↓"
width=
"150"
align=
"right"
/>
<el-table-column
prop=
"standardCost"
label=
"标准消耗"
width=
"130"
align=
"right"
/>
<el-table-column
prop=
"globalCost"
label=
"全域消耗"
width=
"130"
align=
"right"
/>
<el-table-column
prop=
"remark"
label=
"备注"
width=
"100"
align=
"center"
/>
</el-table>
<div
class=
"flex justify-end"
>
<el-pagination
v-model:current-page=
"pageData.currentPage"
v-model:page-size=
"pageData.pageSize"
background
layout=
"total, sizes, prev, pager, next, jumper"
:total=
"tableRows.length"
:page-sizes=
"pageData.pageSizes"
@
size-change=
"handleSizeChange"
/>
</div>
</el-card>
</div>
</template>
<
script
setup
lang=
"ts"
>
import
{
computed
,
reactive
,
ref
}
from
'vue'
;
import
{
ElMessage
}
from
'element-plus'
;
const
platform
=
ref
(
'巨量千川'
);
const
keyword
=
ref
(
''
);
const
department
=
ref
(
''
);
const
group
=
ref
(
''
);
const
account
=
ref
(
''
);
const
category
=
ref
(
''
);
const
date
=
ref
(
''
);
const
pageData
=
reactive
({
currentPage
:
1
,
pageSize
:
50
,
pageSizes
:
[
10
,
20
,
50
,
100
],
});
const
platforms
=
[
'巨量千川'
,
'巨量广告'
];
const
tableRows
=
[
{
name
:
'总计'
,
id
:
''
,
department
:
'-'
,
group
:
'-'
,
user
:
'-'
,
firstCategory
:
'-'
,
secondCategory
:
'-'
,
totalCost
:
'323165.40'
,
walletCost
:
'319208.88'
,
grantCost
:
'3956.52'
,
refund
:
'0.00'
,
sharedWallet
:
'0.00'
,
deposit
:
'3588.16'
,
transferIn
:
'566500.00'
,
transferOut
:
'100000.00'
,
balance
:
'1638168.22'
,
grantBalance
:
'70439.87'
,
cashBalance
:
'1567728.35'
,
standardCost
:
'0.00'
,
globalCost
:
'103725.62'
,
remark
:
'-'
},
{
name
:
'青简-元素-持之以恒'
,
id
:
'1862066464789898'
,
department
:
'未绑定部门'
,
group
:
'未绑定分组'
,
user
:
'未绑定账号'
,
firstCategory
:
'未绑定分类..'
,
secondCategory
:
'未绑定分类'
,
totalCost
:
'92393.53'
,
walletCost
:
'90523.42'
,
grantCost
:
'1870.11'
,
refund
:
'0'
,
sharedWallet
:
'0'
,
deposit
:
'1870.11'
,
transferIn
:
'100000.00'
,
transferOut
:
'0.00'
,
balance
:
'88527.53'
,
grantBalance
:
'0.00'
,
cashBalance
:
'88527.53'
,
standardCost
:
'0'
,
globalCost
:
'0.00'
,
remark
:
'-'
},
{
name
:
'书意-RICH-扶摇直上九万里'
,
id
:
'1839701482129801'
,
department
:
'未绑定部门'
,
group
:
'未绑定分组'
,
user
:
'未绑定账号'
,
firstCategory
:
'未绑定分类..'
,
secondCategory
:
'未绑定分类'
,
totalCost
:
'68531.65'
,
walletCost
:
'68202.75'
,
grantCost
:
'328.90'
,
refund
:
'0'
,
sharedWallet
:
'0'
,
deposit
:
'328.90'
,
transferIn
:
'100000.00'
,
transferOut
:
'0.00'
,
balance
:
'140294.48'
,
grantBalance
:
'0.00'
,
cashBalance
:
'140294.48'
,
standardCost
:
'0'
,
globalCost
:
'68351.04'
,
remark
:
'-'
},
{
name
:
'兰君-母理-绿单888'
,
id
:
'1843476828396544'
,
department
:
'未绑定部门'
,
group
:
'未绑定分组'
,
user
:
'未绑定账号'
,
firstCategory
:
'未绑定分类..'
,
secondCategory
:
'未绑定分类'
,
totalCost
:
'38148.62'
,
walletCost
:
'37734.20'
,
grantCost
:
'414.42'
,
refund
:
'0'
,
sharedWallet
:
'0'
,
deposit
:
'414.42'
,
transferIn
:
'52500.00'
,
transferOut
:
'0.00'
,
balance
:
'55533.17'
,
grantBalance
:
'0.00'
,
cashBalance
:
'55533.17'
,
standardCost
:
'0'
,
globalCost
:
'0.00'
,
remark
:
'-'
},
{
name
:
'素棉-母理-绿米花'
,
id
:
'1868511341739399'
,
department
:
'未绑定部门'
,
group
:
'未绑定分组'
,
user
:
'未绑定账号'
,
firstCategory
:
'未绑定分类..'
,
secondCategory
:
'未绑定分类'
,
totalCost
:
'21948.51'
,
walletCost
:
'21948.51'
,
grantCost
:
'0.00'
,
refund
:
'0'
,
sharedWallet
:
'0'
,
deposit
:
'0.00'
,
transferIn
:
'0.00'
,
transferOut
:
'0.00'
,
balance
:
'38610.93'
,
grantBalance
:
'0.00'
,
cashBalance
:
'38610.93'
,
standardCost
:
'0'
,
globalCost
:
'0.00'
,
remark
:
'-'
},
{
name
:
'初理-惠理-跑量'
,
id
:
'1851290312019482'
,
department
:
'A部门'
,
group
:
'核心一组'
,
user
:
'张伟'
,
firstCategory
:
'美妆护肤'
,
secondCategory
:
'洗面奶'
,
totalCost
:
'102143.09'
,
walletCost
:
'100800.00'
,
grantCost
:
'1343.09'
,
refund
:
'0'
,
sharedWallet
:
'0'
,
deposit
:
'974.73'
,
transferIn
:
'314000.00'
,
transferOut
:
'100000.00'
,
balance
:
'1315202.11'
,
grantBalance
:
'70439.87'
,
cashBalance
:
'1244762.24'
,
standardCost
:
'0'
,
globalCost
:
'35374.58'
,
remark
:
'-'
},
];
const
pagedRows
=
computed
(()
=>
{
const
start
=
(
pageData
.
currentPage
-
1
)
*
pageData
.
pageSize
;
return
tableRows
.
slice
(
start
,
start
+
pageData
.
pageSize
);
});
const
query
=
()
=>
ElMessage
.
success
(
'查询完成'
);
const
exportData
=
()
=>
ElMessage
.
success
(
'已生成导出任务'
);
const
reset
=
()
=>
{
keyword
.
value
=
''
;
department
.
value
=
''
;
group
.
value
=
''
;
account
.
value
=
''
;
category
.
value
=
''
;
date
.
value
=
''
;
};
const
handleSizeChange
=
()
=>
{
pageData
.
currentPage
=
1
;
};
</
script
>
<
style
scoped
lang=
"scss"
>
.report-card
{
border
:
1px
solid
#dfe7f2
;
border-radius
:
8px
;
}
.platform-tabs
{
display
:
flex
;
gap
:
34px
;
border-bottom
:
1px
solid
#edf0f5
;
}
.text-tab
{
padding-bottom
:
10px
;
border
:
0
;
border-bottom
:
2px
solid
transparent
;
background
:
transparent
;
color
:
#3f5574
;
font-weight
:
700
;
cursor
:
pointer
;
&
.active
{
color
:
rgba
(
var
(
--
main-color
)
,
1
);
border-bottom-color
:
rgba
(
var
(
--
main-color
)
,
1
);
}
}
.w-150
{
width
:
150px
;
}
.w-170
,
.w-180
{
width
:
180px
;
}
.analysis-table
{
:deep
(
.el-table__header
th
)
{
background
:
#f5f5f5
;
color
:
#888
;
padding
:
12px
0
;
}
}
.color-main
{
color
:
rgba
(
var
(
--
main-color
)
,
1
);
}
.color-green
{
color
:
#00a86b
;
}
.color-8aa
{
color
:
#8aa0bf
;
}
.scroll-bar
{
height
:
5px
;
margin
:
10px
0
14px
;
border-radius
:
999px
;
background
:
#dfe7f2
;
}
</
style
>
apps/web-vue/src/views/analysis/components/AD/Tab3.vue
0 → 100644
View file @
034d0c1b
<
template
>
<div
class=
"status-report flex-col gap-12"
>
<el-card
class=
"report-card round-8"
shadow=
"never"
>
<div
class=
"top-line flex-between gap-16"
>
<div
class=
"flex-items-center gap-26 flex-wrap"
>
<button
v-for=
"item in dataTabs"
:key=
"item"
class=
"text-tab pointer border-none bg-none font-bold"
:class=
"
{ active: dataTab === item }" @click="dataTab = item">
{{
item
}}
</button>
</div>
<div
class=
"date-filter flex-items-center gap-8"
>
<span>
计划搭建时间
</span>
<el-date-picker
v-model=
"range"
type=
"daterange"
range-separator=
"至"
start-placeholder=
"2025/03/31"
end-placeholder=
"2025/04/14"
/>
</div>
</div>
<div
class=
"flex-items-center justify-start gap-16 pt-14"
>
<div
class=
"flex-items-center gap-26 flex-wrap"
>
<button
v-for=
"item in platforms"
:key=
"item.value"
class=
"platform-item flex-items-center gap-6 pointer border-none bg-none"
:class=
"
{ active: platform === item.value }" @click="platform = item.value">
<span
class=
"w-10 h-10 round-2"
:style=
"
{ backgroundColor: item.color }">
</span>
{{
item
.
label
}}
</button>
</div>
<el-select
v-model=
"department"
class=
"w-180"
placeholder=
"请选择部门"
>
<el-option
label=
"抖音投放"
value=
"抖音投放"
/>
<el-option
label=
"测试指定员工可见-部门"
value=
"测试指定员工可见-部门"
/>
<el-option
label=
"未绑定"
value=
"未绑定"
/>
</el-select>
</div>
</el-card>
<el-card
class=
"report-card chart-card round-8"
shadow=
"never"
>
<div
class=
"flex-between gap-16"
>
<el-segmented
v-model=
"statusType"
:options=
"statusTabs"
/>
<div
class=
"flex-items-center gap-26 flex-wrap"
>
<span
v-for=
"item in legendItems"
:key=
"item.name"
class=
"flex-items-center gap-6"
>
<span
class=
"legend-dot w-10 h-10 round"
:style=
"
{ borderColor: item.color }">
</span>
{{
item
.
name
}}
</span>
</div>
</div>
<div
class=
"h-250 mt-10"
>
<v-chart
class=
"w-full h-full"
:option=
"lineOption"
autoresize
/>
</div>
</el-card>
<el-card
class=
"report-card round-8"
shadow=
"never"
>
<div
class=
"flex-between gap-16 pb-12"
>
<span
class=
"font-bold"
>
详细数据
</span>
<el-button
class=
"w-34 p-0"
@
click=
"settingVisible = !settingVisible"
>
☷
</el-button>
</div>
<el-alert
v-if=
"settingVisible"
class=
"mb-10"
title=
"已打开字段配置面板"
type=
"info"
show-icon
:closable=
"false"
/>
<el-table
:data=
"tableRows"
class=
"analysis-table"
row-key=
"department"
>
<el-table-column
prop=
"department"
label=
"部门"
min-width=
"260"
>
<template
#
default=
"
{ row }">
<span
:class=
"row.link ? 'link-text font-bold' : 'font-bold'"
>
{{
row
.
department
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"built"
label=
"搭建计划总数 ↑↓"
align=
"center"
/>
<el-table-column
prop=
"running"
label=
"投放中 ↑↓"
align=
"center"
>
<
template
#
default=
"{ row }"
><span
class=
"color-green font-bold"
>
{{
row
.
running
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"notStarted"
label=
"未投放 ⓘ ↑↓"
align=
"center"
/>
<el-table-column
prop=
"ended"
label=
"已终止 ↑↓"
align=
"center"
/>
<el-table-column
prop=
"finished"
label=
"已完成 ↑↓"
align=
"center"
/>
<el-table-column
prop=
"deleted"
label=
"已删除 ↑↓"
align=
"center"
>
<
template
#
default=
"{ row }"
><span
class=
"color-8aa"
>
{{
row
.
deleted
}}
</span></
template
>
</el-table-column>
</el-table>
<div
class=
"scroll-bar mt-14"
></div>
</el-card>
</div>
</template>
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
}
from
'vue'
;
import
VChart
from
'vue-echarts'
;
import
{
use
}
from
'echarts/core'
;
import
{
CanvasRenderer
}
from
'echarts/renderers'
;
import
{
LineChart
}
from
'echarts/charts'
;
import
{
GridComponent
,
LegendComponent
,
TooltipComponent
}
from
'echarts/components'
;
use
([
CanvasRenderer
,
LineChart
,
GridComponent
,
LegendComponent
,
TooltipComponent
]);
const
dataTab
=
ref
(
'部门数据'
);
const
platform
=
ref
(
'ad'
);
const
department
=
ref
(
''
);
const
range
=
ref
<
[
Date
,
Date
]
|
''
>
(
''
);
const
statusType
=
ref
(
'搭建计划总数'
);
const
settingVisible
=
ref
(
false
);
const
dataTabs
=
[
'部门数据'
,
'分组数据'
,
'个人数据'
,
'直播间数据'
,
'广告主明细数据'
];
const
statusTabs
=
[
'搭建计划总数'
,
'投放中'
,
'未投放'
];
const
platforms
=
[
{
label
:
'巨量广告'
,
value
:
'ad'
,
color
:
'#7c3aed'
},
{
label
:
'巨量千川'
,
value
:
'qianchuan'
,
color
:
'#2d8cff'
},
];
const
legendItems
=
[
{
name
:
'未绑定'
,
color
:
'#2d8cff'
},
{
name
:
'测试指定员工可见-部门'
,
color
:
'#00b578'
},
{
name
:
'抖音投放'
,
color
:
'#ff9800'
},
];
const
dates
=
[
'2025-03-31'
,
'2025-04-02'
,
'2025-04-04'
,
'2025-04-06'
,
'2025-04-08'
,
'2025-04-10'
,
'2025-04-12'
,
'2025-04-14'
];
const
tableRows
=
[
{
department
:
'总计'
,
built
:
18
,
running
:
5
,
notStarted
:
8
,
ended
:
3
,
finished
:
2
,
deleted
:
0
},
{
department
:
'抖音投放'
,
built
:
8
,
running
:
3
,
notStarted
:
3
,
ended
:
1
,
finished
:
1
,
deleted
:
0
,
link
:
true
},
{
department
:
'测试指定员工可见-部门'
,
built
:
6
,
running
:
2
,
notStarted
:
3
,
ended
:
1
,
finished
:
0
,
deleted
:
0
,
link
:
true
},
{
department
:
'未绑定'
,
built
:
4
,
running
:
0
,
notStarted
:
2
,
ended
:
1
,
finished
:
1
,
deleted
:
0
,
link
:
true
},
];
const
lineOption
=
computed
(()
=>
({
color
:
legendItems
.
map
((
item
)
=>
item
.
color
),
tooltip
:
{
trigger
:
'axis'
},
legend
:
{
show
:
false
},
grid
:
{
left
:
38
,
right
:
24
,
top
:
26
,
bottom
:
34
},
xAxis
:
{
type
:
'category'
,
boundaryGap
:
false
,
data
:
dates
,
axisLine
:
{
lineStyle
:
{
color
:
'#e8eef6'
}
},
axisLabel
:
{
color
:
'#8aa0bf'
},
},
yAxis
:
{
type
:
'value'
,
min
:
0
,
max
:
statusType
.
value
===
'搭建计划总数'
?
1
:
5
,
splitLine
:
{
lineStyle
:
{
color
:
'#edf3fb'
,
type
:
'dashed'
}
},
axisLabel
:
{
color
:
'#8aa0bf'
},
},
series
:
[
{
name
:
'未绑定'
,
type
:
'line'
,
smooth
:
true
,
data
:
[
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
],
symbolSize
:
8
},
{
name
:
'测试指定员工可见-部门'
,
type
:
'line'
,
smooth
:
true
,
data
:
[
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
],
symbolSize
:
8
},
{
name
:
'抖音投放'
,
type
:
'line'
,
smooth
:
true
,
data
:
[
0.05
,
0.05
,
0.05
,
0.05
,
0.05
,
0.05
,
0.05
,
0.05
],
symbolSize
:
8
},
],
}));
</
script
>
<
style
scoped
lang=
"scss"
>
.report-card
{
border
:
1px
solid
#dfe7f2
;
}
.top-line
{
border-bottom
:
1px
solid
#edf0f5
;
}
.text-tab
{
height
:
38px
;
padding
:
0
;
border-bottom
:
2px
solid
transparent
;
color
:
#3f5574
;
&
.active
{
color
:
rgba
(
var
(
--
main-color
)
,
1
);
border-bottom-color
:
rgba
(
var
(
--
main-color
)
,
1
);
}
}
.date-filter
{
color
:
#607086
;
}
.platform-item
{
color
:
#3f5574
;
&
.active
{
color
:
rgba
(
var
(
--
main-color
)
,
1
);
}
}
.chart-card
{
min-height
:
310px
;
}
.legend-dot
{
border
:
2px
solid
;
}
.analysis-table
{
:deep
(
.el-table__header
th
)
{
background
:
#f5f5f5
;
color
:
#888
;
padding
:
12px
0
;
}
}
.link-text
{
color
:
rgba
(
var
(
--
main-color
)
,
1
);
}
.color-green
{
color
:
#00a86b
;
}
.color-8aa
{
color
:
#8aa0bf
;
}
.scroll-bar
{
height
:
5px
;
border-radius
:
999px
;
background
:
#dfe7f2
;
}
</
style
>
apps/web-vue/src/views/analysis/components/AD/Tab4.vue
0 → 100644
View file @
034d0c1b
<
template
>
<div
class=
"shop-report flex-col gap-14"
>
<el-card
class=
"report-card round-8"
shadow=
"never"
>
<div
class=
"flex-between flex-wrap gap-12 pb-14"
>
<div
class=
"flex-items-center gap-12"
>
<span
class=
"font-bold"
>
授权店铺
</span>
<el-button
type=
"primary"
@
click=
"authorizeShop"
>
授权店铺
</el-button>
</div>
<el-button
link
@
click=
"expanded = !expanded"
>
{{
expanded
?
'收起'
:
'展开'
}}
</el-button>
</div>
<div
v-show=
"expanded"
class=
"shop-grid flex flex-wrap gap-16"
>
<div
v-for=
"shop in shops"
:key=
"shop.code"
class=
"shop-card p-16 round-8 pointer"
:class=
"
{ selected: selectedShop === shop.code }" @click="selectedShop = shop.code">
<div
class=
"flex-items-center gap-12"
>
<div
class=
"tiktok-logo flex-center w-40 h-40 round-8 bg-000 color-fff fs-26 font-bold"
>
♪
</div>
<div
class=
"minw-0"
>
<div
class=
"flex-items-center gap-8 font-bold"
>
{{
shop
.
name
}}
<span
class=
"shop-tag px-6 py-2 round-4 fs-12"
>
{{
shop
.
type
}}
</span>
</div>
<div
class=
"shop-code mt-8 fs-12"
>
{{
shop
.
code
}}
</div>
</div>
</div>
<div
class=
"flex-between gap-8 mt-16"
>
<el-button
type=
"primary"
@
click
.
stop=
"analyzeShop(shop.code)"
size=
"small"
>
数据分析
</el-button>
<el-button
@
click
.
stop=
"moreShopActions(shop.code)"
size=
"small"
>
更多操作
</el-button>
</div>
</div>
</div>
</el-card>
<el-card
class=
"report-card round-8"
shadow=
"never"
>
<div
class=
"flex-between flex-wrap gap-12 pb-14"
>
<span
class=
"font-bold"
>
数据概览
</span>
<div
class=
"flex-items-center gap-12 flex-wrap"
>
<el-select
v-model=
"selectedShop"
class=
"w-180"
>
<el-option
v-for=
"shop in shops"
:key=
"shop.code"
:label=
"shop.name"
:value=
"shop.code"
/>
</el-select>
<el-segmented
v-model=
"granularity"
:options=
"granularityTabs"
/>
<el-select
v-model=
"timezone"
class=
"w-90"
>
<el-option
label=
"GMT-8"
value=
"GMT-8"
/>
<el-option
label=
"GMT+8"
value=
"GMT+8"
/>
</el-select>
<el-date-picker
v-model=
"range"
type=
"daterange"
range-separator=
"至"
start-placeholder=
"2025/04/15"
end-placeholder=
"2025/04/15"
/>
</div>
</div>
<div
class=
"metric-grid"
>
<div
v-for=
"item in metrics"
:key=
"item.label"
class=
"metric-card minh-88 p-14 round-8"
>
<div
class=
"metric-label fs-12"
>
{{
item
.
label
}}
</div>
<div
class=
"metric-value mt-8 fs-20 fw-800"
>
{{
item
.
value
}}
</div>
<div
class=
"metric-compare mt-8 fs-12"
>
较上一周期
<span>
--
</span></div>
</div>
</div>
</el-card>
<el-card
class=
"report-card round-8"
shadow=
"never"
>
<div
class=
"flex-between flex-wrap gap-12 pb-14"
>
<div
class=
"flex-items-center gap-12"
>
<button
v-for=
"item in tableTabs"
:key=
"item"
class=
"text-tab border-none bg-none font-bold pointer"
:class=
"
{ active: tableTab === item }" @click="tableTab = item">
{{
item
}}
</button>
</div>
<el-button
type=
"primary"
@
click=
"exportData"
>
导出
</el-button>
</div>
<el-table
:data=
"tableRows"
class=
"analysis-table"
row-key=
"name"
>
<el-table-column
label=
"店铺"
align=
"center"
>
<el-table-column
prop=
"name"
label=
"名称"
min-width=
"260"
/>
<el-table-column
prop=
"code"
label=
"code"
min-width=
"180"
/>
</el-table-column>
<el-table-column
label=
"Statements"
align=
"center"
>
<el-table-column
prop=
"statementProcessing"
label=
"Processing 累计处理中"
min-width=
"160"
align=
"right"
/>
<el-table-column
prop=
"statementPaid"
label=
"Paid 总支付"
min-width=
"150"
align=
"right"
/>
<el-table-column
prop=
"statementFailed"
label=
"Failed 支付失败"
min-width=
"150"
align=
"right"
/>
</el-table-column>
<el-table-column
label=
"Payouts"
align=
"center"
>
<el-table-column
prop=
"payoutPaid"
label=
"Paid 总支付"
min-width=
"150"
align=
"right"
/>
<el-table-column
prop=
"payoutProcessing"
label=
"Processing 待入账"
min-width=
"160"
align=
"right"
/>
<el-table-column
prop=
"payoutFailed"
label=
"Failed 入账失败"
min-width=
"150"
align=
"right"
/>
</el-table-column>
</el-table>
</el-card>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
'vue'
;
import
{
ElMessage
}
from
'element-plus'
;
const
expanded
=
ref
(
true
);
const
selectedShop
=
ref
(
'USLCHNEAYD'
);
const
granularity
=
ref
(
'按日'
);
const
timezone
=
ref
(
'GMT-8'
);
const
range
=
ref
<
[
Date
,
Date
]
|
''
>
(
''
);
const
tableTab
=
ref
(
'店铺数据'
);
const
granularityTabs
=
[
'按日'
,
'按周'
,
'按月'
];
const
tableTabs
=
[
'店铺数据'
,
'订单数据'
];
const
shops
=
[
{
name
:
'SANDBOX7397710...'
,
code
:
'USLCHNEAYD'
,
type
:
'本地'
},
{
name
:
'GLOBAL_STORE_UK_01'
,
code
:
'UK88291045'
,
type
:
'跨境'
},
];
const
metrics
=
[
{
label
:
'Statements-Processing'
,
value
:
'$0'
},
{
label
:
'Statements-Paid'
,
value
:
'$0'
},
{
label
:
'Statements-Failed'
,
value
:
'$0'
},
{
label
:
'Payouts-Paid'
,
value
:
'$0'
},
{
label
:
'Payouts-Processing'
,
value
:
'$0'
},
{
label
:
'Payouts-Failed'
,
value
:
'$0'
},
{
label
:
'总订单数'
,
value
:
'0'
},
{
label
:
'总订单金额'
,
value
:
'$0'
},
{
label
:
'未支付订单数'
,
value
:
'0'
},
{
label
:
'未支付订单金额'
,
value
:
'$0'
},
{
label
:
'取消订单数'
,
value
:
'0'
},
{
label
:
'取消订单金额'
,
value
:
'$0'
},
{
label
:
'退货退款订单数'
,
value
:
'0'
},
{
label
:
'退货退款订单金额'
,
value
:
'$0'
},
];
const
tableRows
=
[
{
name
:
'SANDBOX7397710...'
,
code
:
'USLCHNEAYD'
,
statementProcessing
:
'$0.00'
,
statementPaid
:
'$0.00'
,
statementFailed
:
'$0.00'
,
payoutPaid
:
'$0.00'
,
payoutProcessing
:
'$0.00'
,
payoutFailed
:
'$0.00'
},
{
name
:
'GLOBAL_STORE_UK_01'
,
code
:
'UK88291045'
,
statementProcessing
:
'$0.00'
,
statementPaid
:
'$0.00'
,
statementFailed
:
'$0.00'
,
payoutPaid
:
'$0.00'
,
payoutProcessing
:
'$0.00'
,
payoutFailed
:
'$0.00'
},
];
const
authorizeShop
=
()
=>
ElMessage
.
success
(
'已打开店铺授权流程'
);
const
analyzeShop
=
(
code
:
string
)
=>
ElMessage
.
success
(
`正在分析店铺
${
code
}
`
);
const
moreShopActions
=
(
code
:
string
)
=>
ElMessage
.
info
(
`已打开
${
code
}
的更多操作`
);
const
exportData
=
()
=>
ElMessage
.
success
(
'已生成导出任务'
);
</
script
>
<
style
scoped
lang=
"scss"
>
.report-card
{
border
:
1px
solid
#dfe7f2
;
}
.shop-card
{
flex
:
0
0
calc
((
100%
-
64px
)
/
5
);
border
:
1px
solid
#eadffd
;
background
:
#faf7ff
;
&
.selected
{
box-shadow
:
0
0
0
1px
rgba
(
var
(
--
main-color
)
,
.45
)
inset
;
}
}
.tiktok-logo
{
flex-shrink
:
0
;
}
.minw-0
{
min-width
:
0
;
}
.shop-tag
{
background
:
#d9fbe8
;
color
:
#00875a
;
}
.shop-code
{
color
:
#607086
;
}
.metric-grid
{
display
:
grid
;
grid-template-columns
:
repeat
(
5
,
minmax
(
180px
,
1fr
));
gap
:
12px
;
}
.metric-card
{
border
:
1px
solid
#dfe7f2
;
background
:
#fbfdff
;
}
.metric-label
{
color
:
#48607f
;
}
.metric-value
{
color
:
#001a3d
;
}
.metric-compare
{
color
:
#8aa0bf
;
}
.text-tab
{
height
:
34px
;
padding
:
0
;
border-bottom
:
2px
solid
transparent
;
color
:
#3f5574
;
&
.active
{
color
:
rgba
(
var
(
--
main-color
)
,
1
);
border-bottom-color
:
rgba
(
var
(
--
main-color
)
,
1
);
}
}
.analysis-table
{
:deep
(
.el-table__header
th
)
{
background
:
#f5f5f5
;
color
:
#888
;
padding
:
12px
0
;
}
}
@media
(
max-width
:
1600px
)
{
.shop-card
{
flex-basis
:
calc
((
100%
-
48px
)
/
4
);
}
}
@media
(
max-width
:
1000px
)
{
.shop-card
{
flex-basis
:
calc
((
100%
-
32px
)
/
3
);
}
}
@media
(
max-width
:
1200px
)
{
.metric-grid
{
grid-template-columns
:
repeat
(
2
,
minmax
(
240px
,
1fr
));
}
}
</
style
>
apps/web-vue/src/views/analysis/components/Team/Tab1.vue
0 → 100644
View file @
034d0c1b
<
template
>
<div
class=
"flex-col gap-16"
>
<section
class=
"insight-hero flex-between gap-16 p-20 round-12 color-fff"
>
<div
class=
"flex-items-center gap-16"
>
<button
v-for=
"item in insightTabs"
:key=
"item.value"
class=
"hero-tab flex-items-center gap-8 px-18 py-10 round-10 border-none color-fff font-bold pointer"
:class=
"
{ active: insightType === item.value }" @click="insightType = item.value">
<span>
{{
item
.
icon
}}
</span>
{{
item
.
label
}}
</button>
</div>
<div
class=
"flex-col items-center gap-10"
>
<div
class=
"flex-items-center gap-8"
>
<el-select
v-model=
"sourceRole"
class=
"hero-select w-160"
>
<el-option
label=
"致上运营"
value=
"致上运营"
/>
<el-option
label=
"默认部门"
value=
"默认部门"
/>
</el-select>
<span
class=
"px-12 py-6 round-10"
>
VS
</span>
<el-select
v-model=
"targetRole"
class=
"hero-select w-160"
>
<el-option
label=
"抖音1"
value=
"抖音1"
/>
<el-option
label=
"B部门"
value=
"B部门"
/>
</el-select>
</div>
<el-button
class=
"hero-action px-22"
round
@
click=
"showLeaderInsight"
>
领导力洞察
</el-button>
</div>
<div
class=
"w-300"
>
<el-date-picker
v-model=
"range"
class=
"hero-date"
type=
"daterange"
range-separator=
"至"
start-placeholder=
"开始时间"
end-placeholder=
"结束时间"
style=
"width: 100%;"
/>
</div>
</section>
<el-card
class=
"report-card round-8"
shadow=
"never"
>
<div
class=
"flex-between pb-12"
>
<div
class=
"flex-items-center gap-8"
>
<span
class=
"font-bold fs-16"
>
雷达图分析
</span>
<span
class=
"fs-12 color-8aa"
>
(
{{
sourceRole
}}
对比
{{
targetRole
}}
)
</span>
</div>
<el-button
type=
"primary"
@
click=
"exportData"
>
导出
</el-button>
</div>
<div
class=
"radar-wrap flex-center"
>
<v-chart
class=
"radar-chart"
:option=
"radarOption"
autoresize
/>
</div>
</el-card>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
}
from
'vue'
;
import
VChart
from
'vue-echarts'
;
import
{
use
}
from
'echarts/core'
;
import
{
CanvasRenderer
}
from
'echarts/renderers'
;
import
{
RadarChart
}
from
'echarts/charts'
;
import
{
LegendComponent
,
TooltipComponent
}
from
'echarts/components'
;
import
{
ElMessage
}
from
'element-plus'
;
use
([
CanvasRenderer
,
RadarChart
,
TooltipComponent
,
LegendComponent
]);
const
insightType
=
ref
(
'personal'
);
const
sourceRole
=
ref
(
'致上运营'
);
const
targetRole
=
ref
(
'抖音1'
);
const
range
=
ref
<
[
Date
,
Date
]
|
''
>
(
''
);
const
insightTabs
=
[
{
label
:
'个人数据洞察'
,
value
:
'personal'
,
icon
:
'♙'
},
{
label
:
'小组数据洞察'
,
value
:
'team'
,
icon
:
'♧'
},
];
const
radarOption
=
computed
(()
=>
({
color
:
[
'#3f82ff'
,
'#8b5cf6'
],
tooltip
:
{},
legend
:
{
bottom
:
8
,
itemWidth
:
12
,
itemHeight
:
12
,
data
:
[
sourceRole
.
value
,
targetRole
.
value
],
textStyle
:
{
color
:
'#24405f'
,
fontWeight
:
700
},
},
radar
:
{
center
:
[
'50%'
,
'47%'
],
radius
:
150
,
splitNumber
:
5
,
axisName
:
{
color
:
'#48607f'
,
fontWeight
:
700
},
splitLine
:
{
lineStyle
:
{
color
:
'#e2ebf7'
,
type
:
'dashed'
}
},
splitArea
:
{
areaStyle
:
{
color
:
[
'rgba(63,130,255,.02)'
,
'rgba(63,130,255,.04)'
]
}
},
axisLine
:
{
lineStyle
:
{
color
:
'#d9e4f2'
}
},
indicator
:
[
{
name
:
'消耗力'
,
max
:
100
},
{
name
:
'贡献度'
,
max
:
100
},
{
name
:
'多样性'
,
max
:
100
},
{
name
:
'爆片率'
,
max
:
100
},
{
name
:
'勤奋度'
,
max
:
100
},
{
name
:
'创造力'
,
max
:
100
},
],
},
series
:
[
{
type
:
'radar'
,
symbolSize
:
8
,
areaStyle
:
{
opacity
:
0.18
},
data
:
[
{
name
:
sourceRole
.
value
,
value
:
[
35
,
78
,
38
,
45
,
86
,
78
]
},
{
name
:
targetRole
.
value
,
value
:
[
48
,
36
,
46
,
38
,
42
,
28
]
},
],
},
],
}));
const
showLeaderInsight
=
()
=>
ElMessage
.
success
(
'已生成领导力洞察'
);
const
exportData
=
()
=>
ElMessage
.
success
(
'已生成导出任务'
);
</
script
>
<
style
scoped
lang=
"scss"
>
.insight-hero
{
min-height
:
110px
;
background
:
linear-gradient
(
110deg
,
#3f82f6
0%
,
#8a4ce6
100%
);
box-shadow
:
0
12px
28px
rgba
(
63
,
130
,
246
,
.18
);
}
.hero-tab
{
background
:
transparent
;
&
.active
{
background
:
rgba
(
255
,
255
,
255
,
.16
);
box-shadow
:
0
0
0
1px
rgba
(
255
,
255
,
255
,
.35
)
inset
;
}
}
.hero-select
{
:deep
(
.el-select__wrapper
)
{
background
:
rgba
(
255
,
255
,
255
,
.18
);
border
:
1px
solid
rgba
(
255
,
255
,
255
,
.35
);
box-shadow
:
none
;
}
:deep
(
.el-select__selected-item
),
:deep
(
.el-select__placeholder
),
:deep
(
.el-select__caret
)
{
color
:
#fff
;
}
}
.vs-pill
,
.hero-action
{
background
:
rgba
(
255
,
255
,
255
,
.18
);
color
:
#fff
;
border
:
1px
solid
rgba
(
255
,
255
,
255
,
.35
);
}
.hero-date
{
:deep
(
.el-range-input
),
:deep
(
.el-range-separator
),
:deep
(
.el-icon
)
{
color
:
#fff
;
}
:deep
(
.el-input__wrapper
)
{
background
:
rgba
(
255
,
255
,
255
,
.18
);
box-shadow
:
0
0
0
1px
rgba
(
255
,
255
,
255
,
.35
)
inset
;
}
}
.report-card
{
border
:
1px
solid
#dfe7f2
;
}
.radar-wrap
{
min-height
:
470px
;
}
.radar-chart
{
width
:
560px
;
height
:
430px
;
}
.color-8aa
{
color
:
#8aa0bf
;
}
</
style
>
apps/web-vue/src/views/analysis/components/Team/Tab2.vue
0 → 100644
View file @
034d0c1b
<
template
>
<div
class=
"analysis-panel bg-fff round-8 p-24"
>
<div
class=
"flex-items-center gap-34 page-tabs pb-14"
>
<button
v-for=
"item in dataTabs"
:key=
"item"
class=
"text-tab border-none bg-none font-bold pointer"
:class=
"
{ active: dataTab === item }" @click="dataTab = item">
{{
item
}}
</button>
</div>
<div
class=
"flex-between flex-wrap gap-14 py-24"
>
<div
class=
"flex-items-center gap-12 flex-wrap"
>
<el-segmented
v-model=
"contentType"
:options=
"contentTabs"
/>
<el-select
v-model=
"department"
class=
"w-160"
placeholder=
"请选择部门"
>
<el-option
v-for=
"item in departments"
:key=
"item"
:label=
"item"
:value=
"item"
/>
</el-select>
</div>
<div
class=
"flex-items-center gap-12"
>
<el-select
v-model=
"summaryType"
class=
"w-90"
>
<el-option
label=
"汇总"
value=
"汇总"
/>
<el-option
label=
"明细"
value=
"明细"
/>
</el-select>
<el-date-picker
v-model=
"range"
type=
"daterange"
range-separator=
"至"
start-placeholder=
"开始时间"
end-placeholder=
"结束时间"
/>
</div>
</div>
<el-card
class=
"report-card round-8"
shadow=
"never"
>
<div
class=
"flex-between chart-tabs pb-10"
>
<div
class=
"flex-items-center gap-26"
>
<button
v-for=
"item in chartTabs"
:key=
"item"
class=
"text-tab small border-none bg-none font-bold pointer"
:class=
"
{ active: chartTab === item }" @click="chartTab = item">
{{
item
}}
</button>
</div>
<el-button
link
type=
"primary"
@
click=
"collapsed = !collapsed"
>
{{
collapsed
?
'展开'
:
'收起'
}}
</el-button>
</div>
<div
v-show=
"!collapsed"
class=
"pt-16"
>
<div
class=
"flex-between gap-12 mb-16"
>
<div
class=
"flex-items-center gap-10"
>
<el-segmented
v-model=
"metric"
:options=
"metricTabs"
/>
<el-select
v-model=
"rankLimit"
class=
"w-200"
>
<el-option
label=
"显示top5"
value=
"top5"
/>
<el-option
label=
"显示top10"
value=
"top10"
/>
</el-select>
</div>
<div
class=
"flex-items-center gap-18 flex-wrap"
>
<span
v-for=
"item in legendItems"
:key=
"item.name"
class=
"flex-items-center gap-6 fs-12"
>
<span
class=
"w-8 h-8 round"
:style=
"
{ backgroundColor: item.color }">
</span>
{{
item
.
name
}}
</span>
</div>
</div>
<div
class=
"chart-box round-8"
>
<v-chart
class=
"w-full h-full"
:option=
"lineOption"
autoresize
/>
</div>
</div>
</el-card>
<div
class=
"flex-between mt-30 mb-14"
>
<span
class=
"font-bold fs-16"
>
详细数据
</span>
<div
class=
"flex-items-center gap-8"
>
<el-button
@
click=
"exportData"
>
导出数据
</el-button>
<el-button
class=
"w-34 p-0"
@
click=
"fieldVisible = !fieldVisible"
>
☷
</el-button>
</div>
</div>
<el-alert
v-if=
"fieldVisible"
class=
"mb-10"
title=
"已打开字段配置面板"
type=
"info"
show-icon
:closable=
"false"
/>
<el-table
:data=
"tableRows"
class=
"analysis-table"
row-key=
"department"
>
<el-table-column
prop=
"date"
label=
"数据时间"
min-width=
"190"
/>
<el-table-column
prop=
"department"
label=
"部门名称"
min-width=
"160"
>
<template
#
default=
"
{ row }">
<span
class=
"color-main font-bold"
>
{{
row
.
department
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"uploadPeople"
label=
"上传人数 ⓘ ↑↓"
align=
"center"
/>
<el-table-column
prop=
"uploadCount"
label=
"上传次数 ↑↓"
align=
"center"
/>
<el-table-column
prop=
"downloadCount"
label=
"下载次数 ↑↓"
align=
"center"
/>
<el-table-column
prop=
"downloadPeople"
label=
"被下载人数 ↑↓"
align=
"center"
/>
<el-table-column
prop=
"pushPeople"
label=
"被推送人数 ↑↓"
align=
"center"
/>
<el-table-column
prop=
"copyPeople"
label=
"被复制剪映人数 ↑↓"
align=
"center"
/>
<el-table-column
prop=
"usage"
label=
"作品被使用率 ⓘ ↑↓"
align=
"center"
/>
<el-table-column
prop=
"viral"
label=
"爆款视频数 ↑↓"
align=
"center"
>
<
template
#
default=
"{ row }"
><span
class=
"color-8aa"
>
{{
row
.
viral
}}
</span></
template
>
</el-table-column>
</el-table>
</div>
</template>
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
}
from
'vue'
;
import
VChart
from
'vue-echarts'
;
import
{
use
}
from
'echarts/core'
;
import
{
LineChart
}
from
'echarts/charts'
;
import
{
GridComponent
,
LegendComponent
,
TooltipComponent
}
from
'echarts/components'
;
import
{
CanvasRenderer
}
from
'echarts/renderers'
;
import
{
ElMessage
}
from
'element-plus'
;
use
([
CanvasRenderer
,
LineChart
,
GridComponent
,
TooltipComponent
,
LegendComponent
]);
const
dataTab
=
ref
(
'部门数据'
);
const
contentType
=
ref
(
'全部'
);
const
department
=
ref
(
''
);
const
summaryType
=
ref
(
'汇总'
);
const
range
=
ref
<
[
Date
,
Date
]
|
''
>
(
''
);
const
chartTab
=
ref
(
'时间趋势'
);
const
metric
=
ref
(
'上传'
);
const
rankLimit
=
ref
(
'top5'
);
const
collapsed
=
ref
(
false
);
const
fieldVisible
=
ref
(
false
);
const
dataTabs
=
[
'部门数据'
,
'分组数据'
,
'个人数据'
];
const
contentTabs
=
[
'全部'
,
'成片'
,
'素材'
,
'第三方'
,
'图片'
,
'音频'
];
const
chartTabs
=
[
'时间趋势'
,
'占比分析'
];
const
metricTabs
=
[
'上传'
,
'下载'
,
'复制到剪映'
];
const
departments
=
[
'默认部门'
,
'B部门'
,
'Ian部门1'
,
'7-20部门2'
,
'A部门'
];
const
legendItems
=
[
{
name
:
'默认部门'
,
color
:
'#6366f1'
},
{
name
:
'B部门'
,
color
:
'#10b981'
},
{
name
:
'Ian部门1'
,
color
:
'#f59e0b'
},
{
name
:
'7-20部门2'
,
color
:
'#ef4444'
},
{
name
:
'A部门'
,
color
:
'#06b6d4'
},
];
const
dates
=
[
'2025-04-06'
,
'2025-04-08'
,
'2025-04-10'
,
'2025-04-12'
,
'2025-04-14'
,
'2025-04-16'
,
'2025-04-18'
,
'2025-04-20'
];
const
tableRows
=
[
{
date
:
'2025-04-06-2025-04-21'
,
department
:
'默认部门'
,
uploadPeople
:
1
,
uploadCount
:
47
,
downloadCount
:
120
,
downloadPeople
:
22
,
pushPeople
:
8
,
copyPeople
:
12
,
usage
:
'59.57'
,
viral
:
0
},
{
date
:
'2025-04-06-2025-04-21'
,
department
:
'B部门'
,
uploadPeople
:
1
,
uploadCount
:
6
,
downloadCount
:
1
,
downloadPeople
:
0
,
pushPeople
:
1
,
copyPeople
:
1
,
usage
:
'33.33'
,
viral
:
0
},
{
date
:
'2025-04-06-2025-04-21'
,
department
:
'Ian部门1'
,
uploadPeople
:
1
,
uploadCount
:
3
,
downloadCount
:
0
,
downloadPeople
:
0
,
pushPeople
:
0
,
copyPeople
:
0
,
usage
:
'0.00'
,
viral
:
0
},
{
date
:
'2025-04-06-2025-04-21'
,
department
:
'7-20部门2'
,
uploadPeople
:
1
,
uploadCount
:
3
,
downloadCount
:
0
,
downloadPeople
:
0
,
pushPeople
:
0
,
copyPeople
:
0
,
usage
:
'0.00'
,
viral
:
0
},
{
date
:
'2025-04-06-2025-04-21'
,
department
:
'A部门'
,
uploadPeople
:
0
,
uploadCount
:
0
,
downloadCount
:
0
,
downloadPeople
:
0
,
pushPeople
:
0
,
copyPeople
:
0
,
usage
:
'0.00'
,
viral
:
0
},
];
const
lineOption
=
computed
(()
=>
({
color
:
legendItems
.
map
((
item
)
=>
item
.
color
),
tooltip
:
{
trigger
:
'axis'
},
legend
:
{
show
:
false
},
grid
:
{
left
:
54
,
right
:
40
,
top
:
35
,
bottom
:
42
},
xAxis
:
{
type
:
'category'
,
boundaryGap
:
false
,
data
:
dates
,
axisLabel
:
{
color
:
'#8aa0bf'
},
axisLine
:
{
lineStyle
:
{
color
:
'#edf3fb'
}
}
},
yAxis
:
{
type
:
'value'
,
splitLine
:
{
lineStyle
:
{
color
:
'#edf3fb'
,
type
:
'dashed'
}
},
axisLabel
:
{
color
:
'#8aa0bf'
}
},
series
:
[
{
name
:
'默认部门'
,
type
:
'line'
,
smooth
:
true
,
symbolSize
:
7
,
areaStyle
:
{
opacity
:
.
18
},
data
:
[
0
,
4
,
2
,
1
,
0
,
6
,
35
,
1
]
},
{
name
:
'B部门'
,
type
:
'line'
,
smooth
:
true
,
symbolSize
:
7
,
data
:
[
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
]
},
{
name
:
'Ian部门1'
,
type
:
'line'
,
smooth
:
true
,
symbolSize
:
7
,
data
:
[
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
]
},
{
name
:
'7-20部门2'
,
type
:
'line'
,
smooth
:
true
,
symbolSize
:
7
,
data
:
[
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
]
},
{
name
:
'A部门'
,
type
:
'line'
,
smooth
:
true
,
symbolSize
:
7
,
data
:
[
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
]
},
],
}));
const
exportData
=
()
=>
ElMessage
.
success
(
'已生成导出任务'
);
</
script
>
<
style
scoped
lang=
"scss"
>
.analysis-panel
{
border
:
1px
solid
#dfe7f2
;
}
.page-tabs
,
.chart-tabs
{
border-bottom
:
1px
solid
#edf0f5
;
}
.text-tab
{
height
:
36px
;
padding
:
0
;
border-bottom
:
2px
solid
transparent
;
color
:
#10233f
;
&
.small
{
height
:
34px
;
}
&
.active
{
color
:
rgba
(
var
(
--
main-color
)
,
1
);
border-bottom-color
:
rgba
(
var
(
--
main-color
)
,
1
);
}
}
.report-card
,
.chart-box
{
border
:
1px
solid
#dfe7f2
;
}
.chart-box
{
height
:
240px
;
}
.analysis-table
{
:deep
(
.el-table__header
th
)
{
background
:
#f8fbff
;
color
:
#48607f
;
font-weight
:
700
;
}
}
.color-8aa
{
color
:
#8aa0bf
;
}
</
style
>
apps/web-vue/src/views/analysis/components/Team/Tab3.vue
0 → 100644
View file @
034d0c1b
<
template
>
<div
class=
"analysis-panel bg-fff round-8 p-24"
>
<div
class=
"flex-items-center gap-34 page-tabs pb-14"
>
<button
v-for=
"item in dataTabs"
:key=
"item"
class=
"text-tab border-none bg-none font-bold pointer"
:class=
"
{ active: dataTab === item }" @click="dataTab = item">
{{
item
}}
</button>
</div>
<div
class=
"flex-between flex-wrap gap-14 py-24"
>
<div
class=
"flex-items-center gap-12 flex-wrap"
>
<el-segmented
v-model=
"taskSource"
:options=
"sourceTabs"
/>
<el-segmented
v-model=
"taskStatus"
:options=
"statusTabs"
/>
<el-select
v-model=
"department"
class=
"w-200"
placeholder=
"请选择部门"
>
<el-option
v-for=
"item in departments"
:key=
"item"
:label=
"item"
:value=
"item"
/>
</el-select>
</div>
<div
class=
"flex-items-center gap-12"
>
<el-select
v-model=
"timeField"
class=
"w-200"
>
<el-option
label=
"下单日期"
value=
"下单日期"
/>
<el-option
label=
"发布日期"
value=
"发布日期"
/>
</el-select>
<el-date-picker
v-model=
"range"
type=
"daterange"
range-separator=
"至"
start-placeholder=
"2025/04/06"
end-placeholder=
"2025/04/21"
/>
</div>
</div>
<el-card
class=
"report-card round-8"
shadow=
"never"
>
<div
class=
"flex-between chart-tabs pb-10"
>
<div
class=
"flex-items-center gap-26"
>
<button
v-for=
"item in chartTabs"
:key=
"item"
class=
"text-tab small border-none bg-none font-bold pointer"
:class=
"
{ active: chartTab === item }" @click="chartTab = item">
{{
item
}}
</button>
</div>
<el-button
link
type=
"primary"
@
click=
"collapsed = !collapsed"
>
{{
collapsed
?
'展开'
:
'收起'
}}
</el-button>
</div>
<div
v-show=
"!collapsed"
class=
"pt-16"
>
<div
class=
"flex-between gap-12 mb-16"
>
<div
class=
"flex-items-center gap-10"
>
<el-segmented
v-model=
"metric"
:options=
"metricTabs"
/>
<el-select
v-model=
"rankLimit"
class=
"w-200"
>
<el-option
label=
"显示top5"
value=
"top5"
/>
<el-option
label=
"显示top10"
value=
"top10"
/>
</el-select>
</div>
<div
class=
"flex-items-center gap-18 flex-wrap"
>
<span
v-for=
"item in legendItems"
:key=
"item.name"
class=
"flex-items-center gap-6 fs-12"
>
<span
class=
"w-8 h-8 round"
:style=
"
{ backgroundColor: item.color }">
</span>
{{
item
.
name
}}
</span>
</div>
</div>
<div
class=
"chart-box round-8"
>
<v-chart
class=
"w-full h-full"
:option=
"lineOption"
autoresize
/>
</div>
</div>
</el-card>
<div
class=
"flex-between mt-30 mb-14"
>
<span
class=
"font-bold fs-16"
>
详细数据
</span>
<div
class=
"flex-items-center gap-8"
>
<el-button
@
click=
"exportData"
>
导出数据
</el-button>
<el-button
class=
"w-34 p-0"
@
click=
"fieldVisible = !fieldVisible"
>
☷
</el-button>
</div>
</div>
<el-alert
v-if=
"fieldVisible"
class=
"mb-10"
title=
"已打开字段配置面板"
type=
"info"
show-icon
:closable=
"false"
/>
<el-table
:data=
"tableRows"
class=
"analysis-table"
row-key=
"department"
>
<el-table-column
prop=
"department"
label=
"部门名称"
min-width=
"180"
>
<template
#
default=
"
{ row }">
<span
class=
"color-main font-bold"
>
{{
row
.
department
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"taskCount"
label=
"任务数 ↑↓"
align=
"center"
/>
<el-table-column
prop=
"reached"
label=
"已达标 ↑↓"
align=
"center"
/>
<el-table-column
prop=
"pending"
label=
"待完成 ↑↓"
align=
"center"
/>
<el-table-column
prop=
"orders"
label=
"下单数 ↑↓"
align=
"center"
/>
<el-table-column
prop=
"published"
label=
"出片数 ↑↓"
align=
"center"
>
<
template
#
default=
"{ row }"
><span
class=
"color-main font-bold"
>
{{
row
.
published
}}
</span></
template
>
</el-table-column>
<el-table-column
label=
"操作"
min-width=
"190"
align=
"center"
>
<
template
#
default=
"{ row }"
>
<div
class=
"flex-center gap-12"
>
<el-button
link
type=
"primary"
@
click=
"openStats(row)"
>
统计
</el-button>
<el-button
link
type=
"primary"
@
click=
"openDetail(row)"
>
查看详情
</el-button>
<el-button
link
type=
"primary"
>
数据洞察
</el-button>
</div>
</
template
>
</el-table-column>
</el-table>
<TaskStatsDialog
v-model:visible=
"statsVisible"
:row=
"currentRow"
/>
<TaskDetailDialog
v-model:visible=
"detailVisible"
:department=
"currentRow?.department"
/>
</div>
</template>
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
}
from
'vue'
;
import
VChart
from
'vue-echarts'
;
import
{
use
}
from
'echarts/core'
;
import
{
LineChart
}
from
'echarts/charts'
;
import
{
GridComponent
,
LegendComponent
,
TooltipComponent
}
from
'echarts/components'
;
import
{
CanvasRenderer
}
from
'echarts/renderers'
;
import
{
ElMessage
}
from
'element-plus'
;
import
TaskStatsDialog
from
'./TaskStatsDialog.vue'
;
import
TaskDetailDialog
from
'./TaskDetailDialog.vue'
;
use
([
CanvasRenderer
,
LineChart
,
GridComponent
,
TooltipComponent
,
LegendComponent
]);
type
TaskRow
=
{
department
:
string
;
taskCount
:
number
;
reached
:
number
;
pending
:
number
;
orders
:
number
;
published
:
number
;
};
const
dataTab
=
ref
(
'部门数据'
);
const
taskSource
=
ref
(
'发布的任务'
);
const
taskStatus
=
ref
(
'全部状态'
);
const
department
=
ref
(
''
);
const
timeField
=
ref
(
'下单日期'
);
const
range
=
ref
<
[
Date
,
Date
]
|
''
>
(
''
);
const
chartTab
=
ref
(
'时间趋势'
);
const
metric
=
ref
(
'任务数'
);
const
rankLimit
=
ref
(
'top5'
);
const
collapsed
=
ref
(
false
);
const
fieldVisible
=
ref
(
false
);
const
statsVisible
=
ref
(
false
);
const
detailVisible
=
ref
(
false
);
const
currentRow
=
ref
<
TaskRow
>
();
const
dataTabs
=
[
'部门数据'
,
'分组数据'
,
'个人数据'
];
const
sourceTabs
=
[
'发布的任务'
,
'被指派的任务'
,
'下单运营'
];
const
statusTabs
=
[
'全部状态'
,
'待完成'
,
'已达标'
];
const
chartTabs
=
[
'时间趋势'
,
'占比分析'
,
'视频状态分布'
];
const
metricTabs
=
[
'任务数'
,
'下单数'
,
'出片数'
];
const
departments
=
[
'B部门'
,
'电商运营部'
,
'A部门'
,
'Ian部门1'
,
'默认部门'
];
const
legendItems
=
[
{
name
:
'B部门'
,
color
:
'#6366f1'
},
{
name
:
'默认部门'
,
color
:
'#10b981'
},
{
name
:
'A部门'
,
color
:
'#f59e0b'
},
{
name
:
'C部门'
,
color
:
'#ef4444'
},
{
name
:
'抖音投放'
,
color
:
'#06b6d4'
},
];
const
dates
=
[
'2025-04-06'
,
'2025-04-07'
,
'2025-04-08'
,
'2025-04-10'
,
'2025-04-12'
,
'2025-04-14'
,
'2025-04-16'
,
'2025-04-18'
,
'2025-04-20'
];
const
tableRows
:
TaskRow
[]
=
[
{
department
:
'B部门'
,
taskCount
:
2
,
reached
:
2
,
pending
:
0
,
orders
:
4
,
published
:
4
},
{
department
:
'电商运营部'
,
taskCount
:
1
,
reached
:
0
,
pending
:
1
,
orders
:
3
,
published
:
0
},
{
department
:
'A部门'
,
taskCount
:
0
,
reached
:
0
,
pending
:
0
,
orders
:
0
,
published
:
0
},
{
department
:
'Ian部门1'
,
taskCount
:
0
,
reached
:
0
,
pending
:
0
,
orders
:
0
,
published
:
0
},
{
department
:
'Ian部门1'
,
taskCount
:
0
,
reached
:
0
,
pending
:
0
,
orders
:
0
,
published
:
0
},
{
department
:
'默认部门'
,
taskCount
:
0
,
reached
:
0
,
pending
:
0
,
orders
:
0
,
published
:
0
},
];
const
lineOption
=
computed
(()
=>
({
color
:
legendItems
.
map
((
item
)
=>
item
.
color
),
tooltip
:
{
trigger
:
'axis'
},
legend
:
{
show
:
false
},
grid
:
{
left
:
54
,
right
:
40
,
top
:
35
,
bottom
:
42
},
xAxis
:
{
type
:
'category'
,
boundaryGap
:
false
,
data
:
dates
,
axisLabel
:
{
color
:
'#8aa0bf'
},
axisLine
:
{
lineStyle
:
{
color
:
'#edf3fb'
}
}
},
yAxis
:
{
type
:
'value'
,
splitLine
:
{
lineStyle
:
{
color
:
'#edf3fb'
,
type
:
'dashed'
}
},
axisLabel
:
{
color
:
'#8aa0bf'
}
},
series
:
[
{
name
:
'B部门'
,
type
:
'line'
,
smooth
:
true
,
symbolSize
:
7
,
areaStyle
:
{
opacity
:
.
18
},
data
:
[
0
,
2
,
0
,
0
,
0
,
0
,
0
,
0
,
0
]
},
{
name
:
'默认部门'
,
type
:
'line'
,
smooth
:
true
,
symbolSize
:
7
,
areaStyle
:
{
opacity
:
.
16
},
data
:
[
0
,
1
,
0
,
0
,
0
,
0
,
0
,
0
,
0
]
},
{
name
:
'A部门'
,
type
:
'line'
,
smooth
:
true
,
symbolSize
:
7
,
data
:
[
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
]
},
{
name
:
'C部门'
,
type
:
'line'
,
smooth
:
true
,
symbolSize
:
7
,
data
:
[
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
]
},
{
name
:
'抖音投放'
,
type
:
'line'
,
smooth
:
true
,
symbolSize
:
7
,
data
:
[
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
]
},
],
}));
const
exportData
=
()
=>
ElMessage
.
success
(
'已生成导出任务'
);
const
openStats
=
(
row
:
TaskRow
)
=>
{
currentRow
.
value
=
row
;
statsVisible
.
value
=
true
;
};
const
openDetail
=
(
row
:
TaskRow
)
=>
{
currentRow
.
value
=
row
;
detailVisible
.
value
=
true
;
};
</
script
>
<
style
scoped
lang=
"scss"
>
.analysis-panel
{
border
:
1px
solid
#dfe7f2
;
}
.page-tabs
,
.chart-tabs
{
border-bottom
:
1px
solid
#edf0f5
;
}
.text-tab
{
height
:
36px
;
padding
:
0
;
border-bottom
:
2px
solid
transparent
;
color
:
#10233f
;
&
.small
{
height
:
34px
;
}
&
.active
{
color
:
rgba
(
var
(
--
main-color
)
,
1
);
border-bottom-color
:
rgba
(
var
(
--
main-color
)
,
1
);
}
}
.report-card
,
.chart-box
{
border
:
1px
solid
#dfe7f2
;
}
.chart-box
{
height
:
240px
;
}
.analysis-table
{
:deep
(
.el-table__header
th
)
{
background
:
#f8fbff
;
color
:
#48607f
;
font-weight
:
700
;
}
}
</
style
>
apps/web-vue/src/views/analysis/components/Team/TaskDetailDialog.vue
0 → 100644
View file @
034d0c1b
<
template
>
<Dialog
v-model=
"visible"
bg-color=
"#fff"
width=
"900"
>
<div
class=
"bg-fff round-12 hidden"
>
<div
class=
"dialog-header h-64 px-26 flex-items-center"
>
<span
class=
"color-main fs-18 mr-10"
>
⊙
</span>
<span
class=
"fs-16 font-bold color-000"
>
任务详情列表 -
{{
departmentName
}}
</span>
</div>
<div
class=
"px-26 pt-16 pb-26"
>
<el-table
:data=
"detailRows"
class=
"detail-table"
row-key=
"id"
>
<el-table-column
prop=
"id"
label=
"任务编号"
min-width=
"130"
/>
<el-table-column
prop=
"name"
label=
"任务名称"
min-width=
"170"
>
<template
#
default=
"
{ row }">
<span
class=
"color-main"
>
{{
row
.
name
}}
</span>
</
template
>
</el-table-column>
<el-table-column
prop=
"target"
label=
"目标出片"
width=
"80"
align=
"center"
/>
<el-table-column
prop=
"finished"
label=
"实际完成"
width=
"80"
align=
"center"
>
<
template
#
default=
"{ row }"
>
<span
class=
"color-green"
>
{{
row
.
finished
}}
</span>
</
template
>
</el-table-column>
<el-table-column
prop=
"status"
label=
"状态"
width=
"90"
align=
"center"
>
<
template
#
default=
"{ row }"
>
<span
class=
"status-tag px-8 py-4 round-4"
:class=
"row.status === '已达标' ? 'success' : 'pending'"
>
{{
row
.
status
}}
</span>
</
template
>
</el-table-column>
<el-table-column
prop=
"time"
label=
"时间"
width=
"200"
align=
"center"
/>
</el-table>
<div
class=
"flex justify-end mt-26"
>
<el-button
type=
"primary"
class=
"w-64"
@
click=
"visible = false"
>
关闭
</el-button>
</div>
</div>
</div>
</Dialog>
</template>
<
script
setup
lang=
"ts"
>
import
{
computed
}
from
'vue'
;
import
Dialog
from
'@/components/Dialog/index.vue'
;
const
visible
=
defineModel
<
boolean
>
(
'visible'
,
{
required
:
true
});
const
props
=
defineProps
<
{
department
?:
string
;
}
>
();
const
departmentName
=
computed
(()
=>
props
.
department
||
'部门'
);
const
detailRows
=
computed
(()
=>
[
{
id
:
'TSK_20250410_01'
,
name
:
'美妆搜索对比视频批量制作'
,
target
:
20
,
finished
:
20
,
status
:
'已达标'
,
time
:
'2025-04-07 10:15'
},
{
id
:
'TSK_20250415_02'
,
name
:
'痛点拆解口播卡点视频'
,
target
:
15
,
finished
:
8
,
status
:
'待完成'
,
time
:
'2025-04-12 16:30'
},
]);
</
script
>
<
style
scoped
lang=
"scss"
>
.dialog-header
{
border-bottom
:
1px
solid
#edf0f5
;
}
.detail-table
{
border
:
1px
solid
#dfe7f2
;
border-radius
:
8px
;
overflow
:
hidden
;
:deep
(
.el-table__header
th
)
{
background
:
#f8fbff
;
color
:
#48607f
;
font-weight
:
700
;
}
}
.color-green
{
color
:
#00a86b
;
}
.status-tag
{
display
:
inline-block
;
line-height
:
18px
;
&
.success
{
color
:
#00a86b
;
background
:
#e9fbf3
;
}
&
.pending
{
color
:
#246bfe
;
background
:
#eef4ff
;
}
}
</
style
>
apps/web-vue/src/views/analysis/components/Team/TaskStatsDialog.vue
0 → 100644
View file @
034d0c1b
<
template
>
<Dialog
v-model=
"visible"
bg-color=
"#fff"
width=
"520"
>
<div
class=
"bg-fff round-12 hidden"
>
<div
class=
"dialog-header h-64 px-26 flex-items-center"
>
<span
class=
"w-4 h-18 round-10 bg-main mr-10"
></span>
<span
class=
"fs-16 font-bold color-000"
>
数据汇总
</span>
</div>
<div
class=
"px-26 pt-18 pb-24"
>
<div
class=
"summary-table round-8 hidden"
>
<div
class=
"summary-row"
>
<div
class=
"summary-label px-18 py-12 font-bold"
>
下单数量
</div>
<div
class=
"px-18 py-12 font-bold"
>
{{
stats
.
orders
}}
</div>
</div>
<div
class=
"summary-row"
>
<div
class=
"summary-label px-18 py-12 font-bold"
>
出片数量
</div>
<div
class=
"px-18 py-12 font-bold"
>
{{
stats
.
published
}}
</div>
</div>
</div>
<div
class=
"mt-18 mb-8 font-bold color-000"
>
状态汇总
</div>
<div
class=
"summary-table round-8 hidden"
>
<div
v-for=
"item in statusRows"
:key=
"item.label"
class=
"status-row"
>
<div
class=
"summary-label px-18 py-10 font-bold"
>
{{
item
.
label
}}
</div>
<div
class=
"px-18 py-10 text-center"
>
{{
item
.
rate
}}
</div>
<div
class=
"summary-label px-18 py-10 text-center font-bold"
>
数量
</div>
<div
class=
"px-18 py-10 text-center font-bold"
>
{{
item
.
count
}}
</div>
</div>
</div>
<div
class=
"flex justify-end mt-28"
>
<el-button
type=
"primary"
class=
"w-64"
@
click=
"visible = false"
>
确定
</el-button>
</div>
</div>
</div>
</Dialog>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
computed
}
from
'vue'
;
import
Dialog
from
'@/components/Dialog/index.vue'
;
const
visible
=
defineModel
<
boolean
>
(
'visible'
,
{
required
:
true
});
const
props
=
defineProps
<
{
row
?:
{
orders
?:
number
;
published
?:
number
;
reached
?:
number
;
pending
?:
number
;
};
}
>
();
const
stats
=
computed
(()
=>
({
orders
:
props
.
row
?.
orders
??
0
,
published
:
props
.
row
?.
published
??
0
,
reached
:
props
.
row
?.
reached
??
0
,
pending
:
props
.
row
?.
pending
??
0
,
}));
const
statusRows
=
computed
(()
=>
{
const
total
=
stats
.
value
.
reached
+
stats
.
value
.
pending
;
const
rate
=
(
value
:
number
)
=>
(
total
?
`
${
Math
.
round
((
value
/
total
)
*
100
)}
%`
:
'0%'
);
return
[
{
label
:
'已上机'
,
rate
:
rate
(
stats
.
value
.
reached
),
count
:
stats
.
value
.
reached
},
{
label
:
'待审核'
,
rate
:
rate
(
stats
.
value
.
pending
),
count
:
stats
.
value
.
pending
},
];
});
</
script
>
<
style
scoped
lang=
"scss"
>
.dialog-header
{
border-bottom
:
1px
solid
#edf0f5
;
}
.summary-table
{
border
:
1px
solid
#dfe7f2
;
}
.summary-row
{
display
:
grid
;
grid-template-columns
:
1fr
1
.6fr
;
border-bottom
:
1px
solid
#edf0f5
;
&
:last-child
{
border-bottom
:
none
;
}
}
.status-row
{
display
:
grid
;
grid-template-columns
:
1fr
1fr
.5fr
.5fr
;
border-bottom
:
1px
solid
#edf0f5
;
&
:last-child
{
border-bottom
:
none
;
}
}
.summary-label
{
background
:
#f8fbff
;
color
:
#48607f
;
}
</
style
>
apps/web-vue/src/views/analysis/components/AdPlatformTags.vue
→
apps/web-vue/src/views/analysis/components/
video/
AdPlatformTags.vue
View file @
034d0c1b
...
...
@@ -21,7 +21,8 @@
<el-option
label=
"全部分类"
value=
"all"
/>
<el-option
label=
"服饰鞋包"
value=
"clothes"
/>
</el-select>
<div
class=
"w-240 mx-10"
>
<div
class=
"w-400 mx-10 flex-items-center"
>
<div
class=
"minw-70 color-888"
>
消耗时间:
</div>
<el-date-picker
v-model=
"dateRange"
type=
"daterange"
range-separator=
"至"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
style=
"width: 100% !important;"
/>
</div>
...
...
@@ -35,28 +36,34 @@
<div
class=
"pb-16 flex-between ratio-title"
>
<span
class=
"fs-16 font-bold color-000"
>
占比分析
</span>
<el-button
type=
"primary"
link
@
click=
"collapsed = !collapsed"
>
{{
collapsed
?
'展开'
:
'收起'
}}
</el-button>
}}
</el-button>
</div>
<div
v-show=
"!collapsed"
class=
"p-20 flex gap-26"
>
<div
class=
"flex-1 gap-14 card-grid"
>
<div
v-for=
"item in ratioList"
:key=
"item.name"
class=
"p-18 round-6 flex-between metric-card"
>
<div
class=
"flex-items-center"
>
<div
class=
"flex-items-center
w-full
"
>
<span
class=
"w-42 h-42 round-8 flex-center fs-20"
:style=
"
{ color: item.color, backgroundColor: item.bg }">
{{
item
.
icon
}}
</span>
<div
class=
"ml-16"
>
<div
class=
"ml-16
flex-1
"
>
<div
class=
"font-bold color-000"
>
{{
item
.
name
}}
</div>
<div
class=
"mt-8"
>
数量:
<span
class=
"font-bold"
>
{{
item
.
count
.
toLocaleString
()
}}
</span>
</div>
<div
class=
"mt-6"
>
消耗:
<span
class=
"font-bold"
>
¥
{{
item
.
cost
.
toLocaleString
()
}}
</span>
<div
class=
"flex flex-between mt-10"
>
<div>
<div>
数量:
<span
class=
"font-bold"
>
{{
item
.
count
.
toLocaleString
()
}}
</span>
</div>
<div>
消耗:
<span
class=
"font-bold"
>
¥
{{
item
.
cost
.
toLocaleString
()
}}
</span>
</div>
</div>
<div
class=
"lh-24"
>
<div>
数量占比:
<span
class=
"font-bold"
>
{{
item
.
countRate
}}
%
</span></div>
<div>
消耗占比:
<span
class=
"font-bold"
>
{{
item
.
costRate
}}
%
</span></div>
</div>
</div>
</div>
</div>
<div
class=
"lh-24"
>
<div>
数量占比:
<span
class=
"font-bold"
>
{{
item
.
countRate
}}
%
</span></div>
<div>
消耗占比:
<span
class=
"font-bold"
>
{{
item
.
costRate
}}
%
</span></div>
</div>
</div>
</div>
<div
class=
"flex-1 minw-400 flex-items-center justify-center chart-panel"
>
...
...
@@ -87,7 +94,7 @@
<el-table
:data=
"pagedTableData"
class=
"analysis-table"
>
<el-table-column
prop=
"account"
label=
"广告账户"
min-width=
"210"
show-overflow-tooltip
/>
<el-table-column
prop=
"subject"
label=
"主体"
min-width=
"180"
show-overflow-tooltip
/>
<el-table-column
prop=
"department"
label=
"
部门
"
width=
"130"
/>
<el-table-column
prop=
"department"
label=
"
团队
"
width=
"130"
/>
<el-table-column
prop=
"group"
label=
"分组"
width=
"120"
/>
<el-table-column
prop=
"user"
label=
"用户"
width=
"100"
/>
<el-table-column
prop=
"category1"
label=
"一级分类"
width=
"110"
/>
...
...
@@ -104,7 +111,7 @@
</el-table-column>
<el-table-column
prop=
"goodCost"
label=
"优质素材消耗"
width=
"140"
align=
"center"
>
<
template
#
default=
"{ row }"
><span
class=
"font-bold color-green"
>
{{
row
.
goodCost
}}
</span></
template
>
}}
</span></
template
>
</el-table-column>
<el-table-column
prop=
"low"
label=
"低效素材"
width=
"110"
align=
"center"
>
<
template
#
default=
"{ row }"
><span
class=
"font-bold color-main"
>
{{
row
.
low
}}
</span></
template
>
...
...
@@ -160,7 +167,7 @@ const platformTabs = [
{
label
:
'巨量千川'
,
value
:
'qianchuan'
,
icon
:
'Ⅲ'
},
{
label
:
'巨量广告'
,
value
:
'ad'
,
icon
:
'⊘'
},
];
const
scopes
=
[
'汇总'
,
'
部门
'
,
'分组'
,
'个人'
,
'广告账户'
];
const
scopes
=
[
'汇总'
,
'
团队
'
,
'分组'
,
'个人'
,
'广告账户'
];
const
ratioList
=
[
{
name
:
'首发素材'
,
count
:
1280
,
cost
:
342800
,
countRate
:
28.5
,
costRate
:
32.4
,
color
:
'#5046e5'
,
bg
:
'#f0efff'
,
icon
:
'⌘'
},
{
name
:
'优质素材'
,
count
:
860
,
cost
:
415200
,
countRate
:
19.1
,
costRate
:
39.2
,
color
:
'#ef3d3d'
,
bg
:
'#fff0f0'
,
icon
:
'♙'
},
...
...
@@ -239,9 +246,9 @@ const handleSizeChange = () => {
.analysis-table
{
:deep
(
.el-table__header
th
)
{
background
:
#f
8fbff
;
color
:
#
48607f
;
font-weight
:
bold
;
background
:
#f
5f5f5
;
color
:
#
888
;
padding
:
12px
0
;
}
}
...
...
apps/web-vue/src/views/analysis/components/DeliveryReport.vue
→
apps/web-vue/src/views/analysis/components/
video/
DeliveryReport.vue
View file @
034d0c1b
<
template
>
<div
class=
"p-6"
>
<
el-card
class=
"round-12 mt-10 filter-card"
shadow=
"hover
"
>
<
div
class=
"bg-fff pt-16 px-16 round-8
"
>
<div
class=
"flex-items-center gap-34 header-tabs"
>
<div
v-for=
"item in platforms"
:key=
"item"
class=
"pb-16 font-bold pointer"
:class=
"
{ active: platform === item }" @click="platform = item">
{{
item
}}
</div>
</div>
<div
class=
"py-14 sub-tabs"
>
<div
class=
"flex-items-center gap-24"
>
<span
v-for=
"item in scopes"
:key=
"item"
class=
"font-bold pointer"
:class=
"
{ 'color-main': scope === item }" @click="scope = item">
{{
item
}}
</span>
</div>
<div
class=
"flex-items-center gap-10"
>
<div
class=
"w-300 flex-items-center mr-10"
>
<div
class=
"w-90"
>
上传时间:
</div>
<el-date-picker
v-model=
"uploadRange"
type=
"daterange"
range-separator=
"至"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
style=
"width: 100% !important;"
/>
</div>
<div
class=
"w-300 flex-items-center mr-10"
>
<div
class=
"w-90"
>
投放时间:
</div>
<el-date-picker
v-model=
"deliveryRange"
type=
"daterange"
range-separator=
"至"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
style=
"width: 100% !important;"
/>
</div>
</div>
<el-card
class=
"round-12 mt-16 filter-card"
shadow=
"hover"
>
<div
class=
"flex-between pb-16 sub-tabs"
>
<div
class=
"flex-items-center gap-30"
>
<span
v-for=
"item in scopes"
:key=
"item"
class=
"pointer"
:class=
"
{ 'color-main': scope === item }"
@click="scope = item">
{{
item
}}
</span>
</div>
<div
class=
"flex-between mt-16"
>
<div
class=
""
>
<el-button
type=
"primary"
>
视频作者
</el-button>
<el-button
class=
"mr-10"
>
下单运营
</el-button>
<el-select
v-model=
"department"
class=
"w-180 mr-10"
placeholder=
"请选择部门"
/>
<el-select
v-model=
"category"
class=
"w-180 mr-10"
placeholder=
"请选择分类"
/>
<el-button
type=
"primary"
class=
"w-66"
plain
>
查询
</el-button>
<el-button>
重置
</el-button>
<div
class=
"flex-items-center gap-10"
>
<div
class=
"w-300 flex-items-center mr-10"
>
<div
class=
"w-90"
>
上传时间:
</div>
<el-date-picker
v-model=
"uploadRange"
type=
"daterange"
range-separator=
"至"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
style=
"width: 100% !important;"
/>
</div>
<div>
<el-button>
导出数据
</el-button>
<el-button
class=
"w-34 h-34"
>
☷
</el-button>
<div
class=
"w-300 flex-items-center mr-10"
>
<div
class=
"w-90"
>
投放时间:
</div>
<el-date-picker
v-model=
"deliveryRange"
type=
"daterange"
range-separator=
"至"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
style=
"width: 100% !important;"
/>
</div>
</div>
</div>
</el-card>
<div
class=
"flex-between my-16"
>
<el-radio-group
v-model=
"radioVal"
class=
"mr-10"
>
<el-radio-button
label=
"视频作者"
:value=
"1"
/>
<el-radio-button
label=
"下单运营"
:value=
"2"
/>
</el-radio-group>
<div
class=
"flex"
>
<el-select
v-model=
"department"
class=
"w-180 mr-10"
placeholder=
"请选择团队"
/>
<el-select
v-model=
"category"
class=
"w-180 mr-10"
placeholder=
"请选择分类"
/>
<el-button
type=
"primary"
class=
"w-66"
plain
>
查询
</el-button>
<el-button>
重置
</el-button>
<el-button>
导出数据
</el-button>
<el-button
class=
"w-34"
>
☷
</el-button>
</div>
</div>
<el-card
class=
"round-12 mt-16"
shadow=
"hover"
>
<el-table
:data=
"pagedTableData"
class=
"analysis-table"
>
<el-table-column
prop=
"department"
label=
"
部门
"
min-width=
"160"
/>
<el-table-column
prop=
"department"
label=
"
团队
"
min-width=
"160"
/>
<el-table-column
prop=
"cost"
label=
"消耗"
width=
"120"
align=
"center"
/>
<el-table-column
prop=
"roi"
label=
"roi"
width=
"90"
align=
"center"
/>
<el-table-column
prop=
"totalAmount"
label=
"总成交金额"
width=
"130"
align=
"center"
/>
...
...
@@ -70,24 +71,25 @@
<
script
setup
lang=
"ts"
>
import
{
computed
,
reactive
,
ref
}
from
'vue'
;
const
platform
=
ref
(
'巨量广告'
);
const
scope
=
ref
(
'
部门
'
);
const
scope
=
ref
(
'
团队
'
);
const
department
=
ref
(
''
);
const
category
=
ref
(
''
);
const
uploadRange
=
ref
(
''
);
const
deliveryRange
=
ref
(
''
);
const
radioVal
=
ref
(
1
)
const
pageData
=
reactive
({
currentPage
:
1
,
pageSize
:
10
,
pageSizes
:
[
10
,
20
,
50
,
100
],
});
const
platforms
=
[
'巨量广告'
,
'巨量千川'
,
'磁力智投'
,
'磁力金牛'
,
'百度营销'
,
'小红书'
];
const
scopes
=
[
'
部门
'
,
'分组'
,
'个人'
,
'明细'
,
'分日'
,
'分月'
];
const
scopes
=
[
'
团队
'
,
'分组'
,
'个人'
,
'明细'
,
'分日'
,
'分月'
];
const
tableData
=
[
{
department
:
'总计'
,
cost
:
'1375201.50'
,
roi
:
'3.69'
,
totalAmount
:
'¥5,074,860'
,
amount
:
'¥4,740,000'
,
coupon
:
'¥334,860'
,
conversions
:
'44,240'
,
rate
:
'4.54%'
,
costRate
:
'¥31.09'
,
show
:
'26,040,000'
,
avgCost
:
'¥52.81'
,
click
:
'974,510'
,
clickRate
:
'3.74%'
,
avgPrice
:
'¥1.41'
,
play
:
'22,300,000'
},
{
department
:
'项目1-爆款电商'
,
cost
:
'342100.80'
,
roi
:
'4.12'
,
totalAmount
:
'¥1,409,450'
,
amount
:
'¥1,320,000'
,
coupon
:
'¥89,450'
,
conversions
:
'11,400'
,
rate
:
'5.10%'
,
costRate
:
'¥30.01'
,
show
:
'6,450,000'
,
avgCost
:
'¥53.03'
,
click
:
'223,500'
,
clickRate
:
'3.46%'
,
avgPrice
:
'¥1.53'
,
play
:
'5,600,000'
},
{
department
:
'达人测试
部门
'
,
cost
:
'285400.00'
,
roi
:
'3.85'
,
totalAmount
:
'¥1,098,790'
,
amount
:
'¥1,020,000'
,
coupon
:
'¥78,790'
,
conversions
:
'9,240'
,
rate
:
'4.82%'
,
costRate
:
'¥30.88'
,
show
:
'5,120,000'
,
avgCost
:
'¥55.74'
,
click
:
'191,980'
,
clickRate
:
'3.75%'
,
avgPrice
:
'¥1.48'
,
play
:
'4,320,000'
},
{
department
:
'达人测试
团队
'
,
cost
:
'285400.00'
,
roi
:
'3.85'
,
totalAmount
:
'¥1,098,790'
,
amount
:
'¥1,020,000'
,
coupon
:
'¥78,790'
,
conversions
:
'9,240'
,
rate
:
'4.82%'
,
costRate
:
'¥30.88'
,
show
:
'5,120,000'
,
avgCost
:
'¥55.74'
,
click
:
'191,980'
,
clickRate
:
'3.75%'
,
avgPrice
:
'¥1.48'
,
play
:
'4,320,000'
},
{
department
:
'xx素颜霜项目'
,
cost
:
'220500.00'
,
roi
:
'3.68'
,
totalAmount
:
'¥811,440'
,
amount
:
'¥760,000'
,
coupon
:
'¥51,440'
,
conversions
:
'7,350'
,
rate
:
'4.50%'
,
costRate
:
'¥30.00'
,
show
:
'4,100,000'
,
avgCost
:
'¥53.78'
,
click
:
'163,330'
,
clickRate
:
'3.98%'
,
avgPrice
:
'¥1.35'
,
play
:
'3,680,000'
},
{
department
:
'小真测试
部门
'
,
cost
:
'196200.50'
,
roi
:
'3.42'
,
totalAmount
:
'¥671,000'
,
amount
:
'¥630,000'
,
coupon
:
'¥41,000'
,
conversions
:
'6,150'
,
rate
:
'4.25%'
,
costRate
:
'¥31.90'
,
show
:
'3,820,000'
,
avgCost
:
'¥51.36'
,
click
:
'144,700'
,
clickRate
:
'3.78%'
,
avgPrice
:
'¥1.35'
,
play
:
'3,100,000'
},
{
department
:
'小真测试
团队
'
,
cost
:
'196200.50'
,
roi
:
'3.42'
,
totalAmount
:
'¥671,000'
,
amount
:
'¥630,000'
,
coupon
:
'¥41,000'
,
conversions
:
'6,150'
,
rate
:
'4.25%'
,
costRate
:
'¥31.90'
,
show
:
'3,820,000'
,
avgCost
:
'¥51.36'
,
click
:
'144,700'
,
clickRate
:
'3.78%'
,
avgPrice
:
'¥1.35'
,
play
:
'3,100,000'
},
{
department
:
'抖音投放一组'
,
cost
:
'189000.20'
,
roi
:
'3.52'
,
totalAmount
:
'¥665,280'
,
amount
:
'¥620,000'
,
coupon
:
'¥45,280'
,
conversions
:
'5,980'
,
rate
:
'4.15%'
,
costRate
:
'¥31.60'
,
show
:
'3,600,000'
,
avgCost
:
'¥52.50'
,
click
:
'144,000'
,
clickRate
:
'4.00%'
,
avgPrice
:
'¥1.31'
,
play
:
'3,150,000'
},
];
const
pagedTableData
=
computed
(()
=>
{
...
...
@@ -105,7 +107,6 @@ const handleSizeChange = () => {
border
:
1px
solid
#dfe7f2
;
}
.header-tabs
,
.sub-tabs
,
.table-footer
{
border-bottom
:
1px
solid
#edf0f5
;
...
...
@@ -119,13 +120,14 @@ const handleSizeChange = () => {
.header-tabs
.active
{
color
:
rgba
(
var
(
--
main-color
)
,
1
);
border-bottom
:
2px
solid
rgba
(
var
(
--
main-color
)
,
1
);
box-sizing
:
content-box
;
}
.analysis-table
{
:deep
(
.el-table__header
th
)
{
background
:
#f
8fbff
;
color
:
#
48607f
;
font-weight
:
bold
;
background
:
#f
5f5f5
;
color
:
#
888
;
padding
:
12px
0
;
}
}
</
style
>
apps/web-vue/src/views/analysis/components/TagAnalysis.vue
→
apps/web-vue/src/views/analysis/components/
video/
TagAnalysis.vue
View file @
034d0c1b
...
...
@@ -208,9 +208,9 @@ const handleSizeChange = () => {
.analysis-table
{
:deep
(
.el-table__header
th
)
{
background
:
#f
8fbff
;
color
:
#
48607f
;
font-weight
:
bold
;
background
:
#f
5f5f5
;
color
:
#
888
;
padding
:
12px
0
;
}
}
</
style
>
apps/web-vue/src/views/analysis/components/TencentDeliveryReport.vue
→
apps/web-vue/src/views/analysis/components/
video/
TencentDeliveryReport.vue
View file @
034d0c1b
...
...
@@ -2,21 +2,30 @@
<div
class=
"p-6"
>
<el-card
class=
"bg-fff round-12 mt-10"
shadow=
"hover"
>
<div
class=
"flex-items-center gap-30 header-tabs"
>
<div
v-for=
"item in dataTabs"
:key=
"item"
class=
"pb-16 font-bold pointer"
:class=
"
{ active: dataTab === item }" @click="dataTab = item">
{{
item
}}
</div>
<div
v-for=
"item in dataTabs"
:key=
"item"
class=
"pb-16 font-bold pointer"
:class=
"
{ active: dataTab === item }" @click="dataTab = item">
{{
item
}}
</div>
</div>
<div
class=
"pt-16 flex-between"
>
<div
class=
"flex-items-center"
>
<el-button
type=
"primary"
>
汇总
</el-button>
<el-button
class=
"mr-10"
>
明细
</el-button>
<el-select
v-model=
"department"
class=
"w-180 mr-10"
placeholder=
"请选择部门"
><el-option
label=
"默认部门"
value=
"默认部门"
/></el-select>
<el-select
v-model=
"group"
class=
"w-180 mr-10"
placeholder=
"请选择"
><el-option
label=
"华南电商一队"
value=
"华南电商一队"
/></el-select>
<div
class=
"w-240 mr-10"
>
<el-date-picker
v-model=
"range"
type=
"daterange"
range-separator=
"至"
start-placeholder=
"2022/04/27"
end-placeholder=
"2025/05/12"
style=
"width: 100% !important;"
/>
<div
class=
"pt-16"
>
<div
class=
"flex-between"
>
<div
class=
"flex"
>
<el-radio-group
v-model=
"radioVal"
class=
"mr-10"
>
<el-radio-button
label=
"汇总"
:value=
"1"
/>
<el-radio-button
label=
"明细"
:value=
"2"
/>
</el-radio-group>
<el-select
v-model=
"department"
class=
"w-180 mr-10"
placeholder=
"请选择团队"
><el-option
label=
"默认团队"
value=
"默认团队"
/></el-select>
<el-select
v-model=
"group"
class=
"w-180 mr-10"
placeholder=
"请选择"
><el-option
label=
"华南电商一队"
value=
"华南电商一队"
/></el-select>
<el-button
type=
"primary"
plain
>
查询
</el-button>
<el-button>
重置
</el-button>
</div>
<div
class=
"w-240"
>
<el-date-picker
v-model=
"range"
type=
"daterange"
range-separator=
"至"
start-placeholder=
"开始时间"
end-placeholder=
"结束时间"
style=
"width: 100% !important;"
/>
</div>
<el-button
type=
"primary"
plain
>
查询
</el-button>
<el-button>
重置
</el-button>
</div>
</div>
</el-card>
...
...
@@ -26,7 +35,7 @@
<el-button
type=
"default"
plain
>
导出数据
</el-button>
</div>
<el-table
:data=
"pagedTableData"
class=
"analysis-table"
>
<el-table-column
prop=
"department"
label=
"
部门
"
min-width=
"180"
/>
<el-table-column
prop=
"department"
label=
"
团队
"
min-width=
"180"
/>
<el-table-column
prop=
"cost"
label=
"消耗"
align=
"center"
/>
<el-table-column
prop=
"conversions"
label=
"转化数"
align=
"center"
/>
<el-table-column
prop=
"rate"
label=
"转化率"
align=
"center"
/>
...
...
@@ -39,15 +48,9 @@
<el-table-column
prop=
"complete"
label=
"3S完播率"
align=
"center"
/>
</el-table>
<div
class=
"px-20 py-12 flex justify-end table-footer"
>
<el-pagination
v-model:current-page=
"pageData.currentPage"
v-model:page-size=
"pageData.pageSize"
background
layout=
"total, sizes, prev, pager, next, jumper"
:total=
"tableData.length"
:page-sizes=
"pageData.pageSizes"
@
size-change=
"handleSizeChange"
/>
<el-pagination
v-model:current-page=
"pageData.currentPage"
v-model:page-size=
"pageData.pageSize"
background
layout=
"total, sizes, prev, pager, next, jumper"
:total=
"tableData.length"
:page-sizes=
"pageData.pageSizes"
@
size-change=
"handleSizeChange"
/>
</div>
</el-card>
</div>
...
...
@@ -55,19 +58,20 @@
<
script
setup
lang=
"ts"
>
import
{
computed
,
reactive
,
ref
}
from
'vue'
;
const
dataTab
=
ref
(
'
部门
数据'
);
const
dataTab
=
ref
(
'
团队
数据'
);
const
department
=
ref
(
''
);
const
group
=
ref
(
''
);
const
range
=
ref
(
''
);
const
radioVal
=
ref
(
1
)
const
pageData
=
reactive
({
currentPage
:
1
,
pageSize
:
10
,
pageSizes
:
[
10
,
20
,
50
,
100
],
});
const
dataTabs
=
[
'
部门
数据'
,
'分组数据'
,
'个人数据'
];
const
dataTabs
=
[
'
团队
数据'
,
'分组数据'
,
'个人数据'
];
const
tableData
=
[
{
department
:
'总计'
,
cost
:
'361051.50'
,
conversions
:
'11,550'
,
rate
:
'4.27%'
,
costRate
:
'¥31.26'
,
show
:
'6,880,000'
,
click
:
'270,530'
,
clickRate
:
'3.93%'
,
avgPrice
:
'1.33'
,
play
:
'5,860,000'
,
complete
:
'20.6%'
},
{
department
:
'默认
部门
'
,
cost
:
'128450.50'
,
conversions
:
'4,210'
,
rate
:
'4.12%'
,
costRate
:
'¥30.50'
,
show
:
'2,450,000'
,
click
:
'98,200'
,
clickRate
:
'4.01%'
,
avgPrice
:
'1.31'
,
play
:
'2,100,000'
,
complete
:
'22.5%'
},
{
department
:
'默认
团队
'
,
cost
:
'128450.50'
,
conversions
:
'4,210'
,
rate
:
'4.12%'
,
costRate
:
'¥30.50'
,
show
:
'2,450,000'
,
click
:
'98,200'
,
clickRate
:
'4.01%'
,
avgPrice
:
'1.31'
,
play
:
'2,100,000'
,
complete
:
'22.5%'
},
{
department
:
'华南电商一队'
,
cost
:
'96200.80'
,
conversions
:
'3,120'
,
rate
:
'3.95%'
,
costRate
:
'¥30.83'
,
show
:
'1,820,000'
,
click
:
'74,620'
,
clickRate
:
'4.10%'
,
avgPrice
:
'1.29'
,
play
:
'1,560,000'
,
complete
:
'21.2%'
},
{
department
:
'华东投放二队'
,
cost
:
'84300.00'
,
conversions
:
'2,680'
,
rate
:
'3.78%'
,
costRate
:
'¥31.45'
,
show
:
'1,590,000'
,
click
:
'62,010'
,
clickRate
:
'3.90%'
,
avgPrice
:
'1.36'
,
play
:
'1,320,000'
,
complete
:
'20.4%'
},
{
department
:
'海外品牌推广组'
,
cost
:
'52100.20'
,
conversions
:
'1,540'
,
rate
:
'3.45%'
,
costRate
:
'¥33.83'
,
show
:
'1,020,000'
,
click
:
'35,700'
,
clickRate
:
'3.50%'
,
avgPrice
:
'1.46'
,
play
:
'880,000'
,
complete
:
'18.5%'
},
...
...
@@ -82,8 +86,27 @@ const handleSizeChange = () => {
</
script
>
<
style
scoped
lang=
"scss"
>
.header-tabs
,
.table-title
,
.table-footer
{
border-bottom
:
1px
solid
#edf0f5
;
}
.table-footer
{
border-top
:
1px
solid
#edf0f5
;
border-bottom
:
none
;
}
.header-tabs
.active
{
color
:
rgba
(
var
(
--
main-color
)
,
1
);
border-bottom
:
2px
solid
rgba
(
var
(
--
main-color
)
,
1
);
}
.analysis-table
{
:deep
(
.el-table__header
th
)
{
background
:
#f8fbff
;
color
:
#48607f
;
font-weight
:
bold
;
}
}
.header-tabs
,
.table-title
,
.table-footer
{
border-bottom
:
1px
solid
#edf0f5
;
}
.table-footer
{
border-top
:
1px
solid
#edf0f5
;
border-bottom
:
none
;
}
.header-tabs
.active
{
color
:
rgba
(
var
(
--
main-color
)
,
1
);
border-bottom
:
2px
solid
rgba
(
var
(
--
main-color
)
,
1
);
}
.analysis-table
{
:deep
(
.el-table__header
th
)
{
background
:
#f5f5f5
;
color
:
#888
;
padding
:
12px
0
;
}
}
</
style
>
apps/web-vue/src/views/analysis/department.vue
View file @
034d0c1b
<
template
>
<div>
<div
class=
"bgmr vh100 over-y-auto"
>
<section
class=
"bg-fff px-20 py-10 analysis-tabs"
>
<div
class=
"flex-items-center gap-16"
>
<div
v-for=
"item in tabs"
:key=
"item.value"
class=
"px-12 py-8 round-6 font-bold pointer"
:class=
"activeTab === item.value ? 'bg-main color-fff' : 'color-000'"
@
click=
"activeTab = item.value"
>
{{
item
.
label
}}
</div>
</div>
</section>
<div
class=
"p-16"
>
<component
:is=
"currentComponent"
/>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
}
from
'vue'
;
import
Tab1
from
'./components/Team/Tab1.vue'
;
import
Tab2
from
'./components/Team/Tab2.vue'
;
import
Tab3
from
'./components/Team/Tab3.vue'
;
const
activeTab
=
ref
(
'tab1'
);
const
tabs
=
[
{
label
:
'数据洞察'
,
value
:
'tab1'
,
component
:
Tab1
},
{
label
:
'创作分析'
,
value
:
'tab2'
,
component
:
Tab2
},
{
label
:
'任务分析'
,
value
:
'tab3'
,
component
:
Tab3
},
];
const
currentComponent
=
computed
(()
=>
tabs
.
find
((
item
)
=>
item
.
value
===
activeTab
.
value
)?.
component
);
</
script
>
<
style
scoped
lang=
"scss"
>
</
style
>
\ No newline at end of file
.analysis-tabs
{
border-bottom
:
1px
solid
#dfe7f2
;
}
</
style
>
apps/web-vue/src/views/analysis/video.vue
View file @
034d0c1b
...
...
@@ -21,10 +21,10 @@
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
}
from
'vue'
;
import
AdPlatformTags
from
'./components/AdPlatformTags.vue'
;
import
TagAnalysis
from
'./components/TagAnalysis.vue'
;
import
TencentDeliveryReport
from
'./components/TencentDeliveryReport.vue'
;
import
DeliveryReport
from
'./components/DeliveryReport.vue'
;
import
AdPlatformTags
from
'./components/
video/
AdPlatformTags.vue'
;
import
TagAnalysis
from
'./components/
video/
TagAnalysis.vue'
;
import
TencentDeliveryReport
from
'./components/
video/
TencentDeliveryReport.vue'
;
import
DeliveryReport
from
'./components/
video/
DeliveryReport.vue'
;
const
activeTab
=
ref
(
'platform'
);
const
tabs
=
[
...
...
apps/web-vue/src/views/zhibo/index.vue
0 → 100644
View file @
034d0c1b
<
template
>
<div
class=
"live-page bgmr vh100 over-y-auto p-24"
>
<section
class=
"flex-between gap-20"
>
<div>
<h2
class=
"fs-16 color-000"
>
直播账号总览
</h2>
<p
class=
"mt-4 color-888 fs-12"
>
查看所有已绑定直播号的点赞、消耗与经营表现
</p>
</div>
<el-button
type=
"primary"
class=
"h-36 px-20"
@
click=
"bindVisible = true"
>
<el-icon
class=
"mr-6"
><Link
/></el-icon>
绑定直播号
</el-button>
</section>
<section
class=
"summary-list flex bg-fff round-8 hidden mt-16"
>
<div
v-for=
"item in summaryCards"
:key=
"item.label"
class=
"summary-card flex-1 px-22 py-12"
>
<div
class=
"color-888 fs-12"
>
{{
item
.
label
}}
</div>
<div
class=
"mt-8 fs-18 font-bold"
:class=
"item.color"
>
{{
item
.
value
}}
</div>
</div>
</section>
<el-card
class=
"mt-16 bg-fff round-8 account-table-wrap hidden"
shadow=
"never"
>
<SeTable
:data-list=
"accounts"
class=
"account-table"
:total=
"accounts.length"
:default-page-size=
"50"
size=
"default"
@
update:page=
"handlePageChange"
>
<el-table-column
label=
"直播账号"
min-width=
"300"
>
<template
#
default=
"
{ row }">
<div
class=
"flex-items-center minw-0"
>
<div
class=
"live-icon flex-center wh-40 round-8 color-fff mr-14"
>
<el-icon><VideoCamera
/></el-icon>
</div>
<div
class=
"minw-0"
>
<div
class=
"font-bold color-000 ellipsis"
>
{{
row
.
name
}}
</div>
<div
class=
"mt-6 fs-12 color-8aa ellipsis"
>
@
{{
row
.
handle
}}
·
{{
row
.
lastSync
}}
</div>
</div>
</div>
</
template
>
</el-table-column>
<el-table-column
prop=
"rooms"
label=
"直播间数"
min-width=
"110"
/>
<el-table-column
prop=
"likes"
label=
"累计点赞"
min-width=
"130"
>
<
template
#
default=
"{ row }"
>
<span
class=
"like-text font-bold"
>
♡
{{
row
.
likes
.
toLocaleString
()
}}
</span>
</
template
>
</el-table-column>
<el-table-column
prop=
"cost"
label=
"投放消耗"
min-width=
"130"
>
<
template
#
default=
"{ row }"
>
<span
class=
"font-bold"
>
¥
{{
row
.
cost
.
toLocaleString
()
}}
</span>
</
template
>
</el-table-column>
<el-table-column
prop=
"sales"
label=
"成交销量"
min-width=
"130"
>
<
template
#
default=
"{ row }"
>
<span
class=
"font-bold"
>
{{
row
.
sales
.
toLocaleString
()
}}
</span>
</
template
>
</el-table-column>
<el-table-column
prop=
"amount"
label=
"成交金额"
min-width=
"130"
>
<
template
#
default=
"{ row }"
>
<span
class=
"font-bold"
>
¥
{{
row
.
amount
.
toLocaleString
()
}}
</span>
</
template
>
</el-table-column>
<el-table-column
prop=
"roi"
label=
"综合 ROI"
min-width=
"120"
>
<
template
#
default=
"{ row }"
>
<span
class=
"color-main font-bold"
>
{{
row
.
roi
.
toFixed
(
2
)
}}
</span>
</
template
>
</el-table-column>
<el-table-column
label=
"状态"
min-width=
"140"
>
<
template
#
default=
"{ row }"
>
<span
class=
"status-tag px-12 py-2 round-20 font-bold fs-12"
:class=
"getStatusClass(row.status)"
>
{{
row
.
status
}}
</span>
</
template
>
</el-table-column>
<el-table-column
label=
"操作"
min-width=
"190"
align=
"center"
>
<
template
#
default=
"{ row }"
>
<div
class=
"flex-center gap-8"
>
<el-button
class=
"w-34 h-34 p-0"
@
click=
"openDetail(row)"
>
<el-icon><View
/></el-icon>
</el-button>
<el-button
v-if=
"row.canSync"
class=
"w-34 h-34 p-0"
@
click=
"syncAccount(row)"
>
<el-icon><Refresh
/></el-icon>
</el-button>
<el-button
class=
"w-34 h-34 p-0"
@
click=
"removeAccount(row)"
>
<el-icon><Delete
/></el-icon>
</el-button>
</div>
</
template
>
</el-table-column>
</SeTable>
</el-card>
<Dialog
v-model=
"bindVisible"
bg-color=
"#fff"
width=
"520"
>
<div
class=
"bg-fff round-8 hidden"
>
<div
class=
"dialog-header flex-items-center px-24 py-18"
>
<span
class=
"w-4 h-16 round-10 bg-main mr-12"
></span>
<span
class=
"font-bold color-000"
>
绑定直播号
</span>
</div>
<div
class=
"p-24"
>
<el-form
:model=
"bindForm"
label-width=
"92px"
>
<el-form-item
label=
"直播号名称"
>
<el-input
v-model=
"bindForm.name"
placeholder=
"请输入直播号名称"
/>
</el-form-item>
<el-form-item
label=
"账号ID"
>
<el-input
v-model=
"bindForm.handle"
placeholder=
"例如 MCBeauty_2026"
/>
</el-form-item>
<el-form-item
label=
"授权状态"
>
<el-select
v-model=
"bindForm.status"
class=
"w-full"
>
<el-option
label=
"已绑定"
value=
"已绑定"
/>
<el-option
label=
"首次同步中"
value=
"首次同步中"
/>
<el-option
label=
"授权即将到期"
value=
"授权即将到期"
/>
</el-select>
</el-form-item>
</el-form>
<div
class=
"flex justify-end gap-12 mt-20"
>
<el-button
@
click=
"bindVisible = false"
>
取消
</el-button>
<el-button
type=
"primary"
@
click=
"confirmBind"
>
确定绑定
</el-button>
</div>
</div>
</div>
</Dialog>
<Dialog
v-model=
"detailVisible"
bg-color=
"#fff"
width=
"900"
>
<div
class=
"bg-fff round-8 hidden"
>
<div
class=
"dialog-header flex-items-center px-24 py-18"
>
<span
class=
"w-4 h-14 round-10 bg-main mr-12"
></span>
<span
class=
"font-bold color-000"
>
直播账号详情
</span>
</div>
<div
v-if=
"currentAccount"
class=
"detail-panel pt-4"
>
<div
class=
"flex-items-center mb-20 px-24 pt-24"
>
<div
class=
"live-icon flex-center wh-44 round-8 color-fff mr-14"
>
<el-icon><VideoCamera
/></el-icon>
</div>
<div
class=
"minw-0"
>
<div
class=
"font-bold fs-16 color-000"
>
{{ currentAccount.name }}
</div>
<div
class=
"fs-12 color-8aa mt-4"
>
@{{ currentAccount.handle }} · {{ currentAccount.lastSync }}
</div>
</div>
<span
class=
"status-tag px-12 py-6 round-20 font-bold fs-12 ml-18"
:class=
"getStatusClass(currentAccount.status)"
>
{{ currentAccount.status }}
</span>
</div>
<div
class=
"detail-metrics flex gap-12 mb-20 px-24"
>
<div
v-for=
"item in detailItems"
:key=
"item.label"
class=
"detail-metric flex-1 p-16 round-6"
>
<div
class=
"fs-12 color-8aa"
>
{{ item.label }}
</div>
<div
class=
"mt-8 font-bold color-000 fs-20"
>
{{ item.value }}
</div>
</div>
</div>
<div
class=
"font-bold color-000 mb-12 px-24"
>
直播间销量与销售额
</div>
<div
class=
"px-24"
>
<SeTable
:data-list=
"currentAccount.roomsData"
class=
"detail-room-table"
:total=
"currentAccount.roomsData.length"
:default-page-size=
"10"
size=
"default"
>
<el-table-column
prop=
"room"
label=
"直播间"
min-width=
"170"
/>
<el-table-column
prop=
"host"
label=
"主播"
min-width=
"120"
/>
<el-table-column
prop=
"sales"
label=
"销量"
min-width=
"100"
/>
<el-table-column
prop=
"amount"
label=
"销售额"
min-width=
"130"
>
<
template
#
default=
"{ row }"
>
<span
class=
"font-bold"
>
¥
{{
row
.
amount
.
toLocaleString
()
}}
</span>
</
template
>
</el-table-column>
<el-table-column
prop=
"roi"
label=
"ROI"
min-width=
"100"
>
<
template
#
default=
"{ row }"
>
<span
class=
"color-main font-bold"
>
{{
row
.
roi
.
toFixed
(
2
)
}}
</span>
</
template
>
</el-table-column>
<el-table-column
prop=
"status"
label=
"状态"
min-width=
"120"
>
<
template
#
default=
"{ row }"
>
<span
class=
"room-status px-10 py-2 round-20 font-bold fs-12"
:class=
"row.status === '直播中' ? 'live' : 'ended'"
>
{{
row
.
status
}}
</span>
</
template
>
</el-table-column>
</SeTable>
</div>
</div>
<div
class=
"dialog-footer flex justify-end p-24"
>
<el-button
type=
"primary"
class=
"w-64"
@
click=
"detailVisible = false"
>
关闭
</el-button>
</div>
</div>
</Dialog>
</div>
</template>
<
script
setup
lang=
"ts"
>
import
{
computed
,
reactive
,
ref
}
from
'vue'
;
import
{
Delete
,
Link
,
Refresh
,
VideoCamera
,
View
}
from
'@element-plus/icons-vue'
;
import
{
ElMessage
,
ElMessageBox
}
from
'element-plus'
;
type
LiveStatus
=
'已绑定'
|
'授权即将到期'
|
'首次同步中'
;
type
LiveAccount
=
{
id
:
number
;
name
:
string
;
handle
:
string
;
lastSync
:
string
;
rooms
:
number
;
likes
:
number
;
cost
:
number
;
sales
:
number
;
amount
:
number
;
roi
:
number
;
status
:
LiveStatus
;
canSync
:
boolean
;
roomsData
:
LiveRoom
[];
};
type
LiveRoom
=
{
room
:
string
;
host
:
string
;
sales
:
number
;
amount
:
number
;
roi
:
number
;
status
:
'直播中'
|
'已结束'
;
};
const
bindVisible
=
ref
(
false
);
const
detailVisible
=
ref
(
false
);
const
currentAccount
=
ref
<
LiveAccount
>
();
const
bindForm
=
reactive
({
name
:
''
,
handle
:
''
,
status
:
'已绑定'
as
LiveStatus
,
});
const
accounts
=
ref
<
LiveAccount
[]
>
([
{
id
:
1
,
name
:
'梦畅美妆旗舰店'
,
handle
:
'MCBeauty_2026'
,
lastSync
:
'今天 10:26'
,
rooms
:
3
,
likes
:
286400
,
cost
:
82640
,
sales
:
2158
,
amount
:
368420
,
roi
:
4.46
,
status
:
'已绑定'
,
canSync
:
false
,
roomsData
:
[
{
room
:
'秋季通勤穿搭'
,
host
:
'梁清淇'
,
sales
:
498
,
amount
:
88720
,
roi
:
4.72
,
status
:
'直播中'
},
{
room
:
'显瘦牛仔合集'
,
host
:
'徐云卿'
,
sales
:
624
,
amount
:
116800
,
roi
:
4.96
,
status
:
'已结束'
},
{
room
:
'晚间精华专场'
,
host
:
'苏沐'
,
sales
:
1036
,
amount
:
162900
,
roi
:
4.28
,
status
:
'已结束'
},
],
},
{
id
:
2
,
name
:
'梦畅服饰直播号'
,
handle
:
'MCFashion_Live'
,
lastSync
:
'今天 10:24'
,
rooms
:
2
,
likes
:
184600
,
cost
:
59320
,
sales
:
1462
,
amount
:
286900
,
roi
:
4.84
,
status
:
'已绑定'
,
canSync
:
false
,
roomsData
:
[
{
room
:
'秋季通勤穿搭'
,
host
:
'梁清淇'
,
sales
:
498
,
amount
:
88720
,
roi
:
4.72
,
status
:
'直播中'
},
{
room
:
'显瘦牛仔合集'
,
host
:
'徐云卿'
,
sales
:
624
,
amount
:
116800
,
roi
:
4.96
,
status
:
'已结束'
},
],
},
{
id
:
3
,
name
:
'梦畅家居好物'
,
handle
:
'MCHome_Select'
,
lastSync
:
'昨天 23:58'
,
rooms
:
1
,
likes
:
93200
,
cost
:
31860
,
sales
:
708
,
amount
:
128760
,
roi
:
4.04
,
status
:
'授权即将到期'
,
canSync
:
true
,
roomsData
:
[
{
room
:
'小户型收纳节'
,
host
:
'周宁'
,
sales
:
708
,
amount
:
128760
,
roi
:
4.04
,
status
:
'已结束'
},
],
},
{
id
:
4
,
name
:
'梦畅个护精选'
,
handle
:
'MCCare_Select'
,
lastSync
:
'等待首次同步'
,
rooms
:
1
,
likes
:
0
,
cost
:
0
,
sales
:
0
,
amount
:
0
,
roi
:
0
,
status
:
'首次同步中'
,
canSync
:
true
,
roomsData
:
[
{
room
:
'个护新品首场'
,
host
:
'待分配'
,
sales
:
0
,
amount
:
0
,
roi
:
0
,
status
:
'直播中'
},
],
},
]);
const
statusMeta
:
Record
<
LiveStatus
,
{
className
:
string
}
>
=
{
已绑定
:
{
className
:
'success'
},
授权即将到期
:
{
className
:
'warning'
},
首次同步中
:
{
className
:
'syncing'
},
};
const
getStatusClass
=
(
status
:
LiveStatus
)
=>
statusMeta
[
status
].
className
;
const
summaryCards
=
computed
(()
=>
[
{
label
:
'正常同步'
,
value
:
accounts
.
value
.
filter
((
item
)
=>
item
.
status
===
'已绑定'
).
length
,
color
:
'color-green'
},
{
label
:
'首次同步中'
,
value
:
accounts
.
value
.
filter
((
item
)
=>
item
.
status
===
'首次同步中'
).
length
,
color
:
'color-main'
},
{
label
:
'授权待处理'
,
value
:
accounts
.
value
.
filter
((
item
)
=>
item
.
status
===
'授权即将到期'
).
length
,
color
:
'color-orange'
},
{
label
:
'当前直播中'
,
value
:
accounts
.
value
.
filter
((
item
)
=>
item
.
rooms
>
0
).
length
,
color
:
'color-red'
},
]);
const
detailItems
=
computed
(()
=>
{
const
item
=
currentAccount
.
value
;
if
(
!
item
)
return
[];
return
[
{
label
:
'成交销量'
,
value
:
item
.
sales
.
toLocaleString
()
},
{
label
:
'销售额'
,
value
:
`¥
${
item
.
amount
.
toLocaleString
()}
`
},
{
label
:
'累计点赞'
,
value
:
item
.
likes
.
toLocaleString
()
},
{
label
:
'投放消耗'
,
value
:
`¥
${
item
.
cost
.
toLocaleString
()}
`
},
];
});
const
openDetail
=
(
row
:
LiveAccount
)
=>
{
currentAccount
.
value
=
row
;
detailVisible
.
value
=
true
;
};
const
syncAccount
=
(
row
:
LiveAccount
)
=>
{
row
.
status
=
'已绑定'
;
row
.
canSync
=
false
;
row
.
lastSync
=
'刚刚同步'
;
ElMessage
.
success
(
`
${
row
.
name
}
已同步`
);
};
const
removeAccount
=
async
(
row
:
LiveAccount
)
=>
{
try
{
await
ElMessageBox
.
confirm
(
`确认删除直播号「
${
row
.
name
}
」吗?`
,
'删除确认'
,
{
type
:
'warning'
});
accounts
.
value
=
accounts
.
value
.
filter
((
item
)
=>
item
.
id
!==
row
.
id
);
ElMessage
.
success
(
'已删除直播号'
);
}
catch
{
ElMessage
.
info
(
'已取消删除'
);
}
};
const
confirmBind
=
()
=>
{
const
name
=
bindForm
.
name
.
trim
()
||
'新绑定直播号'
;
const
handle
=
bindForm
.
handle
.
trim
()
||
`Live_
${
Date
.
now
().
toString
().
slice
(
-
4
)}
`
;
accounts
.
value
.
unshift
({
id
:
Date
.
now
(),
name
,
handle
,
lastSync
:
bindForm
.
status
===
'首次同步中'
?
'等待首次同步'
:
'刚刚绑定'
,
rooms
:
0
,
likes
:
0
,
cost
:
0
,
sales
:
0
,
amount
:
0
,
roi
:
0
,
status
:
bindForm
.
status
,
canSync
:
bindForm
.
status
!==
'已绑定'
,
roomsData
:
[],
});
bindVisible
.
value
=
false
;
bindForm
.
name
=
''
;
bindForm
.
handle
=
''
;
bindForm
.
status
=
'已绑定'
;
ElMessage
.
success
(
'直播号绑定成功'
);
};
const
handlePageChange
=
()
=>
{
// 当前为静态数据,预留分页事件用于后续接接口。
};
</
script
>
<
style
scoped
lang=
"scss"
>
.page-header
{
border-bottom
:
1px
solid
#dfe7f2
;
}
.summary-list
{
border
:
1px
solid
#dfe7f2
;
}
.summary-card
{
border-right
:
1px
solid
#edf0f5
;
&
:last-child
{
border-right
:
none
;
}
}
.account-table-wrap
{
border
:
1px
solid
#dfe7f2
;
}
.account-table
{
:deep
(
.el-table__header
th
)
{
background
:
#f5f5f5
;
color
:
#888
;
padding
:
12px
0
;
}
:deep
(
.el-table__row
)
{
height
:
74px
;
}
}
.live-icon
{
flex-shrink
:
0
;
background
:
linear-gradient
(
135deg
,
#9d19ff
,
#7c3aed
);
}
.status-tag
{
display
:
inline-block
;
&
.success
{
color
:
#00875a
;
background
:
#e7fbf1
;
}
&
.warning
{
color
:
#d97706
;
background
:
#fff7e6
;
}
&
.syncing
{
color
:
#8a22fd
;
background
:
#fbf0ff
;
}
}
.detail-metrics
{
min-width
:
0
;
}
.detail-metric
{
border
:
1px
solid
#dfe7f2
;
}
.detail-room-table
{
border
:
1px
solid
#dfe7f2
;
border-radius
:
8px
;
overflow
:
hidden
;
:deep
(
.el-table__header
th
)
{
background
:
#f8fbff
;
color
:
#8aa0bf
;
font-weight
:
700
;
}
}
.room-status
{
display
:
inline-block
;
&
.live
{
color
:
#00875a
;
background
:
#e7fbf1
;
}
&
.ended
{
color
:
#48607f
;
background
:
#f1f5f9
;
}
}
.minw-0
{
min-width
:
0
;
}
.color-green
{
color
:
#00a86b
;
}
.color-orange
{
color
:
#f59e0b
;
}
.color-red
,
.like-text
{
color
:
#ff0055
;
}
.dialog-header
,
.dialog-footer
{
border-top
:
1px
solid
#edf0f5
;
}
.dialog-header
{
border-top
:
none
;
border-bottom
:
1px
solid
#edf0f5
;
}
</
style
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment