Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
S
saas-dml
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
saas-dml
Commits
2c83c129
提交
2c83c129
authored
11月 14, 2025
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
bug fixes
上级
6a3cdb5f
隐藏空白字符变更
内嵌
并排
正在显示
8 个修改的文件
包含
412 行增加
和
63 行删除
+412
-63
FormPrice.vue
src/modules/live/product/management/components/FormPrice.vue
+42
-7
FormDialog.vue
src/modules/live/reports/components/FormDialog.vue
+8
-26
CompetitionLive.vue
src/modules/live/score/components/CompetitionLive.vue
+7
-6
FormDialog.vue
src/modules/live/talk/components/FormDialog.vue
+15
-2
api.ts
src/modules/live/test/api.ts
+3
-3
Live.vue
src/modules/live/test/components/Live.vue
+2
-1
RecordView.vue
src/modules/live/test/components/RecordView.vue
+327
-15
useLiveChat.ts
src/modules/live/test/composables/useLiveChat.ts
+8
-3
没有找到文件。
src/modules/live/product/management/components/FormPrice.vue
浏览文件 @
2c83c129
...
...
@@ -11,7 +11,7 @@ if (!form.info.specs) {
// 初始化sku列表
if
(
!
form
.
info
.
sku
)
{
form
.
info
.
sku
=
[{
price
:
''
,
stock
:
0
}]
form
.
info
.
sku
=
[{
price
:
''
,
stock
:
0
,
reference_price
:
''
}]
}
// 确保在没有规格时至少有一个SKU
...
...
@@ -22,7 +22,7 @@ onMounted(() => {
function
ensureSkuExists
()
{
const
hasValidSpecs
=
form
.
info
.
specs
&&
form
.
info
.
specs
.
some
((
spec
)
=>
spec
.
name
&&
spec
.
name
.
trim
())
if
(
!
hasValidSpecs
&&
(
!
form
.
info
.
sku
||
form
.
info
.
sku
.
length
===
0
))
{
form
.
info
.
sku
=
[{
price
:
''
,
stock
:
0
}]
form
.
info
.
sku
=
[{
price
:
''
,
stock
:
0
,
reference_price
:
''
}]
}
}
...
...
@@ -79,12 +79,13 @@ function generateSkuList() {
// 如果没有有效规格,确保至少有一个SKU
if
(
validSpecs
.
length
===
0
)
{
if
(
!
form
.
info
.
sku
||
form
.
info
.
sku
.
length
===
0
)
{
form
.
info
.
sku
=
[{
price
:
''
,
stock
:
0
}]
form
.
info
.
sku
=
[{
price
:
''
,
stock
:
0
,
reference_price
:
''
}]
}
else
if
(
form
.
info
.
sku
.
length
===
1
&&
!
form
.
info
.
sku
[
0
].
specs
)
{
// 保持单个SKU,但不添加specs字段
form
.
info
.
sku
[
0
]
=
{
price
:
form
.
info
.
sku
[
0
].
price
||
''
,
stock
:
form
.
info
.
sku
[
0
].
stock
||
0
,
reference_price
:
form
.
info
.
sku
[
0
].
reference_price
||
''
,
}
}
else
{
// 如果之前有多个SKU(有规格时),现在没有规格了,只保留第一个或创建一个新的
...
...
@@ -93,6 +94,7 @@ function generateSkuList() {
{
price
:
firstSku
?.
price
||
''
,
stock
:
firstSku
?.
stock
||
0
,
reference_price
:
firstSku
?.
reference_price
||
''
,
},
]
}
...
...
@@ -107,11 +109,12 @@ function generateSkuList() {
// 如果任何规格没有有效值,确保至少有一个SKU
if
(
specValuesArrays
.
some
((
arr
)
=>
arr
.
length
===
0
))
{
if
(
!
form
.
info
.
sku
||
form
.
info
.
sku
.
length
===
0
)
{
form
.
info
.
sku
=
[{
price
:
''
,
stock
:
0
}]
form
.
info
.
sku
=
[{
price
:
''
,
stock
:
0
,
reference_price
:
''
}]
}
else
if
(
form
.
info
.
sku
.
length
===
1
&&
!
form
.
info
.
sku
[
0
].
specs
)
{
form
.
info
.
sku
[
0
]
=
{
price
:
form
.
info
.
sku
[
0
].
price
||
''
,
stock
:
form
.
info
.
sku
[
0
].
stock
||
0
,
reference_price
:
form
.
info
.
sku
[
0
].
reference_price
||
''
,
}
}
return
...
...
@@ -120,12 +123,16 @@ function generateSkuList() {
// 生成笛卡尔积
const
combinations
=
cartesianProduct
(
specValuesArrays
)
// 生成SKU列表,保留已存在的价格
和库存
数据
// 生成SKU列表,保留已存在的价格
、库存和参考价
数据
const
existingSkuMap
=
new
Map
()
form
.
info
.
sku
.
forEach
((
sku
)
=>
{
// 如果有specs字段,使用specs作为key;否则使用空数组作为key
const
key
=
JSON
.
stringify
(
sku
.
specs
||
[])
existingSkuMap
.
set
(
key
,
{
price
:
sku
.
price
||
''
,
stock
:
sku
.
stock
||
0
})
existingSkuMap
.
set
(
key
,
{
price
:
sku
.
price
||
''
,
stock
:
sku
.
stock
||
0
,
reference_price
:
sku
.
reference_price
||
''
,
})
})
form
.
info
.
sku
=
combinations
.
map
((
combo
)
=>
{
...
...
@@ -135,6 +142,7 @@ function generateSkuList() {
specs
:
combo
,
price
:
existing
?.
price
||
''
,
stock
:
existing
?.
stock
||
0
,
reference_price
:
existing
?.
reference_price
||
''
,
}
})
}
...
...
@@ -164,6 +172,7 @@ watch(hasSpecs, (newVal) => {
// 批量设置相关
const
batchPrice
=
ref
(
''
)
const
batchStock
=
ref
(
''
)
const
batchReferencePrice
=
ref
(
''
)
// 批量设置价格
function
batchSetPrice
()
{
...
...
@@ -188,6 +197,17 @@ function batchSetStock() {
})
batchStock
.
value
=
''
}
// 批量设置参考价
function
batchSetReferencePrice
()
{
if
(
batchReferencePrice
.
value
===
''
||
batchReferencePrice
.
value
===
null
||
batchReferencePrice
.
value
===
undefined
)
return
const
referencePrice
=
batchReferencePrice
.
value
form
.
info
.
sku
.
forEach
((
sku
)
=>
{
sku
.
reference_price
=
referencePrice
})
batchReferencePrice
.
value
=
''
}
</
script
>
<
template
>
...
...
@@ -278,6 +298,14 @@ function batchSetStock() {
</el-input>
<el-button
type=
"primary"
plain
@
click=
"batchSetStock"
style=
"margin-left: 8px"
>
批量设置库存
</el-button>
</div>
<div
class=
"batch-set-item"
>
<el-input
v-model=
"batchReferencePrice"
placeholder=
"批量设置参考价"
type=
"number"
style=
"width: 200px"
>
<
template
#
prefix
>
¥
</
template
>
</el-input>
<el-button
type=
"primary"
plain
@
click=
"batchSetReferencePrice"
style=
"margin-left: 8px"
>
批量设置参考价
</el-button>
</div>
</div>
<el-table
:data=
"form.info.sku"
border
:header-cell-style=
"{ backgroundColor: '#f5f7fa' }"
style=
"width: 100%"
>
<el-table-column
v-if=
"hasSpecs"
label=
"规格组合"
align=
"center"
width=
"200"
>
...
...
@@ -292,6 +320,13 @@ function batchSetStock() {
</el-input>
</template>
</el-table-column>
<el-table-column
prop=
"reference_price"
label=
"参考价"
align=
"center"
>
<
template
#
default=
"{ row }"
>
<el-input
v-model=
"row.reference_price"
placeholder=
"请输入参考价"
type=
"number"
>
<template
#
prefix
>
¥
</
template
>
</el-input>
</template>
</el-table-column>
<el-table-column
prop=
"stock"
label=
"库存"
align=
"center"
>
<
template
#
default=
"{ row }"
>
<el-input
v-model
.
number=
"row.stock"
placeholder=
"请输入库存"
type=
"number"
>
...
...
@@ -301,7 +336,7 @@ function batchSetStock() {
</el-table-column>
</el-table>
</el-form-item>
<el-form-item
label=
"参考价"
prop=
"info.reference_price"
>
<el-form-item
label=
"参考价"
prop=
"info.reference_price"
v-if=
"false"
>
<
template
#
label
>
<span>
参考价
</span>
<el-popover
:width=
"200"
trigger=
"hover"
placement=
"right"
>
...
...
src/modules/live/reports/components/FormDialog.vue
浏览文件 @
2c83c129
<
script
setup
>
import
{
ElMessage
}
from
'element-plus'
import
{
Document
}
from
'@element-plus/icons-vue'
import
{
createReport
,
getReport
,
getTestList
}
from
'../api'
import
AppUpload
from
'@/components/base/AppUpload.vue'
...
...
@@ -17,6 +16,7 @@ const form = reactive({
live_practice_id
:
''
,
report_name
:
''
,
report_url
:
''
,
file
:
[],
})
watchEffect
(()
=>
{
if
(
props
.
data
)
Object
.
assign
(
form
,
props
.
data
)
...
...
@@ -36,7 +36,9 @@ onMounted(() => {
async
function
fetchInfo
()
{
const
res
=
await
getReport
({
id
:
props
.
data
.
id
})
Object
.
assign
(
form
,
res
.
data
.
detail
)
Object
.
assign
(
form
,
res
.
data
.
detail
,
{
file
:
[{
name
:
res
.
data
.
detail
.
report_name
||
''
,
url
:
res
.
data
.
detail
.
report_url
||
''
}],
})
}
watchEffect
(()
=>
{
if
(
props
.
action
!==
'add'
)
fetchInfo
()
...
...
@@ -65,9 +67,8 @@ async function handleUpdate() {
}
function
handleSuccess
(
file
)
{
if
(
!
form
.
report_name
)
{
form
.
report_name
=
file
.
name
}
form
.
report_name
=
file
.
name
form
.
report_url
=
file
.
raw
?.
url
}
</
script
>
...
...
@@ -91,16 +92,10 @@ function handleSuccess(file) {
</el-form-item>
<el-form-item
label=
"总结报告文件"
prop=
"report_url"
>
<div
style=
"width: 100%"
>
<AppUpload
v-model=
"form.
report_url"
accept=
".doc,.docx,.pdf
"
@
success=
"handleSuccess"
>
<AppUpload
v-model=
"form.
file"
accept=
".doc,.docx,.ppt,.pptx"
:limit=
"1
"
@
success=
"handleSuccess"
>
<el-button
type=
"primary"
plain
round
>
本地上传
</el-button>
<template
#
tip
>
报告文件只能上传一个,支持格式包含:
doc docx pdf
,大小不超过50M
</
template
>
<template
#
tip
>
报告文件只能上传一个,支持格式包含:
ppt docx
,大小不超过50M
</
template
>
</AppUpload>
<div
class=
"upload-preview"
v-if=
"form.report_url"
>
<a
:href=
"form.report_url"
target=
"_blank"
>
<el-icon><Document
/></el-icon>
<span>
{{ form.report_name || form.report_url }}
</span>
</a>
</div>
</div>
</el-form-item>
</el-form>
...
...
@@ -114,16 +109,3 @@ function handleSuccess(file) {
</
template
>
</el-dialog>
</template>
<
style
lang=
"scss"
scoped
>
.upload-preview
{
a
{
display
:
flex
;
align-items
:
center
;
white-space
:
nowrap
;
gap
:
5px
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
}
}
</
style
>
src/modules/live/score/components/CompetitionLive.vue
浏览文件 @
2c83c129
...
...
@@ -194,7 +194,11 @@ const handleNext = () => {
v-model:comment=
"scoreDetails.commodity_type.comment"
@
save=
"$emit('save')"
@
next=
"handleNext"
>
<AppList
v-bind=
"categoryTableOptions"
row-key=
"id"
:data=
"detail.live_data.commodity_types"
/>
<AppList
v-bind=
"categoryTableOptions"
row-key=
"id"
:data=
"detail.live_data.commodity_types"
default-expand-all
/>
</ScoreCard>
</el-tab-pane>
<el-tab-pane
label=
"商品属性管理"
:name=
"2"
lazy
>
...
...
@@ -216,7 +220,7 @@ const handleNext = () => {
v-model:comment=
"scoreDetails.commodity.comment"
@
save=
"$emit('save')"
@
next=
"handleNext"
>
<AppList
v-bind=
"productTableOptions"
:data=
"productList"
:default-expand-all=
"productList.length === 1"
>
<AppList
v-bind=
"productTableOptions"
:data=
"productList"
default-expand-all
>
<template
#
table-expand=
"
{ row }">
<div
class=
"table-expand-box"
>
<el-form
label-position=
"top"
>
...
...
@@ -273,10 +277,7 @@ const handleNext = () => {
v-model:comment=
"scoreDetails.speech.comment"
@
save=
"$emit('save')"
@
next=
"handleNext"
>
<AppList
v-bind=
"talkTableOptions"
:data=
"detail.live_data.speeches"
:default-expand-all=
"detail.live_data.speeches.length === 1"
>
<AppList
v-bind=
"talkTableOptions"
:data=
"detail.live_data.speeches"
default-expand-all
>
<
template
#
table-expand=
"{ row }"
>
<div
class=
"table-expand-box"
>
<el-form
label-position=
"top"
>
...
...
src/modules/live/talk/components/FormDialog.vue
浏览文件 @
2c83c129
...
...
@@ -216,6 +216,7 @@ function handleAIGenerate(index) {
plain
size=
"small"
@
click=
"handleAIGenerate(1)"
:disabled=
"isLoading"
:loading=
"isLoading && aiActive === 1"
>
AI一键生成
</el-button
>
...
...
@@ -233,6 +234,7 @@ function handleAIGenerate(index) {
plain
size=
"small"
@
click=
"handleAIGenerate(2)"
:disabled=
"isLoading"
:loading=
"isLoading && aiActive === 2"
>
AI一键生成
</el-button
>
...
...
@@ -253,7 +255,12 @@ function handleAIGenerate(index) {
<el-col
:sm=
"24"
:md=
"12"
style=
"border-left: 1px solid #dcdfe6"
>
<div
style=
"text-align: center; margin-bottom: 20px"
>
<h2
style=
"margin-bottom: 20px"
>
直播话术
</h2>
<el-button
type=
"primary"
size=
"large"
@
click=
"handleAIGenerate(3)"
:loading=
"isLoading && aiActive === 3"
>
<el-button
type=
"primary"
size=
"large"
@
click=
"handleAIGenerate(3)"
:disabled=
"isLoading"
:loading=
"isLoading && aiActive === 3"
>
{{
messages
.
length
?
'再次生成直播话术'
:
'AI生成直播话术'
}}
</el-button>
</div>
...
...
@@ -272,7 +279,13 @@ function handleAIGenerate(index) {
<template
#
footer
>
<el-row
justify=
"center"
>
<el-button
round
auto-insert-space
@
click=
"$emit('update:modelValue', false)"
>
关闭
</el-button>
<el-button
type=
"primary"
round
auto-insert-space
@
click=
"handleSubmit"
v-if=
"action !== 'view'"
<el-button
type=
"primary"
round
auto-insert-space
:disabled=
"isLoading"
@
click=
"handleSubmit"
v-if=
"action !== 'view'"
>
保存
</el-button
>
</el-row>
...
...
src/modules/live/test/api.ts
浏览文件 @
2c83c129
...
...
@@ -72,7 +72,7 @@ export function updateImprovementPlan(data: { id: string; improvement_plan: stri
return
httpRequest
.
post
(
'/api/lab/v1/experiment/live-practice/submit-improvement-plan'
,
data
)
}
//
AI 打分
export
function
aiScore
(
data
:
{
id
:
string
})
{
return
httpRequest
.
post
(
'/api/lab/v1/experiment/live-practice/
scor
e-live-practice-record'
,
data
)
//
更新直播记录的AI分析结果
export
function
updateRecord
(
data
:
{
id
:
string
;
ai_analyze
:
string
})
{
return
httpRequest
.
post
(
'/api/lab/v1/experiment/live-practice/
updat
e-live-practice-record'
,
data
)
}
src/modules/live/test/components/Live.vue
浏览文件 @
2c83c129
...
...
@@ -22,7 +22,7 @@ const props = defineProps({
onStatsChange
:
{
type
:
Function
,
default
:
()
=>
{}
},
})
const
{
messages
,
viewers
,
stats
,
currentTime
,
start
:
startChat
}
=
useLiveChat
()
const
{
messages
,
viewers
,
stats
,
currentTime
,
start
:
startChat
,
stop
:
stopChat
}
=
useLiveChat
()
watch
(
()
=>
[
stats
,
viewers
],
()
=>
{
...
...
@@ -96,6 +96,7 @@ const {
console
.
log
(
'结束直播'
)
if
(
enabled
.
value
)
return
props
.
onStop
&&
props
.
onStop
(
blob
)
stopChat
()
handleUpdateRecord
({
live_video_size
:
blob
.
size
.
toString
()
})
// 保存录像到本地
if
(
props
.
isLocalUpload
)
{
...
...
src/modules/live/test/components/RecordView.vue
浏览文件 @
2c83c129
...
...
@@ -2,7 +2,10 @@
import
VueMarkdown
from
'vue-markdown-render'
import
Demo
from
'../components/Demo.vue'
import
ImprovementPlan
from
'../components/ImprovementPlan.vue'
import
{
getRecord
,
aiScore
}
from
'../api'
import
{
getRecord
,
updateRecord
}
from
'../api'
import
{
useChat
}
from
'@ezijing/ai-vue'
const
{
isLoading
,
generateText
}
=
useChat
({
provider
:
'volcano'
})
const
props
=
defineProps
({
isView
:
{
type
:
Boolean
,
default
:
true
},
...
...
@@ -16,8 +19,7 @@ const fetchRecord = async () => {
const
resDetail
=
res
.
data
.
detail
detail
.
value
=
{
...
resDetail
,
live_info
:
JSON
.
parse
(
resDetail
.
live_info
)
}
try
{
const
subtitle
=
JSON
.
parse
(
resDetail
.
subtitle
)
if
(
subtitle
?.
Result
?.
Sentences
&&
!
resDetail
.
ai_level
)
{
if
(
!
resDetail
.
ai_analyze
)
{
handleAIScore
()
}
}
catch
(
error
)
{
...
...
@@ -36,7 +38,7 @@ const dialogVisible = ref(false)
const
result
=
computed
(()
=>
{
try
{
return
JSON
.
parse
(
detail
.
value
.
illegal_word
)
||
{}
return
JSON
.
parse
(
detail
.
value
.
ai_analyze
)
||
{}
}
catch
(
error
)
{
console
.
log
(
error
)
}
...
...
@@ -80,16 +82,326 @@ const failedWords = computed(() => {
return
detail
.
value
?.
words
?.
filter
((
item
)
=>
!
item
.
time
)
||
[]
})
const
isLoading
=
ref
(
false
)
const
handleAIScore
=
()
=>
{
isLoading
.
value
=
true
aiScore
({
id
:
props
.
recordId
})
.
then
(()
=>
{
fetchRecord
()
})
.
finally
(()
=>
{
isLoading
.
value
=
false
})
const
handleAIScore
=
async
()
=>
{
const
messages
=
[
{
role
:
'system'
,
content
:
'你是一位专业的直播电商分析师,需要为选手的直播演练生成一份全面、详细的AI评价报告。'
,
},
{
role
:
'user'
,
content
:
`
[输入信息]
- 直播视频/音频记录(
${
detail
.
value
?.
live_duration
}
)
-
字幕文本:
$
{
detail
.
value
?.
subtitle
}
-
直播话术脚本:
$
{
detail
.
value
?.
live_practice_info
.
live_speech
.
content
}
-
预设卖点:
$
{
detail
.
value
?.
live_practice_info
.
live_speech
.
selling_point
}
-
营销活动:
$
{
detail
.
value
?.
live_practice_info
.
live_speech
.
marketing_campaign
}
-
商品信息:
$
{
JSON
.
stringify
(
detail
.
value
?.
live_practice_info
.
live_commodity
)}
【输出要求】
请基于以上信息生成结构化的
Markdown
报告,并保证
JSON
字段与现有前端对接保持一致:
{
"sellingPoints"
:
"...卖点讲解覆盖情况的总结内容..."
,
"forbiddenWords"
:
"...违禁词分析报告的 Markdown 内容..."
,
"speechSpeed"
:
"...语速分析报告的 Markdown 内容..."
,
"optimizationTips"
:
"...直播优化建议的 Markdown 内容..."
}
报告中必须覆盖以下四大板块,并按照既定标题层级输出,避免新增或删除节点:
一、卖点讲解覆盖情况
卖点讲解综合评价:
【整体表现】
【用几句话总结卖点讲解的整体情况,如:本次直播时长
XX
,累计口播
XX
字,本次直播共讲解了
X
个卖点,覆盖率达到
XX
%
。讲解较为全面
/
部分遗漏,重点突出
/
略显平淡。建议在二次直播中
...
】
二、违禁词分析报告
检测结果总览:
共发现:
X
个违禁词
/
敏感词
严重程度评估:严重
/
中等
/
轻微
/
零违禁
(严重程度评估标准:严重≥
5
个,中等
3
-
4
个,轻微
1
-
2
个,零违禁
0
个。此说明不写入报告。)
违禁词详细列表:(若为零违禁,则不展示下面的统计内容)
【违禁词
#
1
】━━━━━━━━━━━━
违禁词:
"【具体违禁词】"
类型:绝对化用语
夸大宣传
医疗用语
虚假宣传
出现时间:第
X
分
X
秒
出现次数:
X
次
上下文原文:
"【引用完整句子,用红色标注违禁词】"
例:
"这款面膜效果最好,市场上独一无二的配方..."
↑违禁
↑违禁
【违禁词
#
2
】━━━━━━━━━━━━
违禁词:
"【具体违禁词】"
类型:绝对化用语
夸大宣传
医疗用语
虚假宣传
出现时间:第
X
分
X
秒
出现次数:
X
次
上下文原文:
"【引用完整句子】"
例:
"这款面膜能够治疗痘痘,快速修复受损肌肤..."
↑违禁
【违禁词
#
3
】━━━━━━━━━━━━
违禁词:
"【具体违禁词】"
类型:绝对化用语
夸大宣传
医疗用语
虚假宣传
出现时间:第
X
分
X
秒
出现次数:
X
次
上下文原文:
"【引用完整句子】"
例:
"使用后100%能看到效果,保证满意..."
↑违禁
↑违禁
(依此类推,列出所有违禁词
...
)
违规情况统计(若为零违禁,则不展示下面的统计内容)
违禁词总数:
X
个
违禁词出现总次数:
X
次
类型分布:
-
绝对化用语:
X
个(如:最、第一、唯一、极致、顶级、完美等)
-
夸大宣传:
X
个(如:
100
%
、必然、保证、绝对、立即见效等)
-
医疗用语:
X
个(如:治疗、疗效、药用、治愈、医治等)
-
虚假宣传:
X
个(如:国家级、世界级、最高级、最低价等)
高频违禁词:
1
.
"【违禁词】"
-
出现
X
次
2
.
"【违禁词】"
-
出现
X
次
3
.
"【违禁词】"
-
出现
X
次
常见违禁词快速对照表(不需要体现在报告里面)
类别
1
:绝对化用语(共
20
个)
最、最佳、最好、最优、最大、最小、最新、最先进
第一、首个、首选、唯一、独一无二、前无古人
极致、完美、顶级、至尊、终极、巅峰
类别
2
:夸大宣传(共
15
个)
100
%
、百分百、保证、必然、绝对、肯定
立竿见影、立即见效、马上见效、即刻
永久、终身、永远、一劳永逸、彻底
类别
3
:医疗用语(共
20
个)
治疗、治愈、医治、疗效、药用、处方
抗炎、消炎、杀菌、灭菌、防癌、抗癌
降血压、降血糖、降血脂、治痤疮、祛痘、去斑
修复、再生(需谨慎使用)
类别
4
:虚假宣传(共
10
个)
国家级、世界级、全球级、宇宙级
最高级、最低价、史无前例、前所未有
销量第一、市场占有率第一
三、直播优化建议
综合评价:
【用几句话总结本次直播的整体表现,如:本次直播整体表现良好
/
一般
/
优秀,在卖点讲解和互动方面表现突出,但在语速控制和促单技巧方面还有提升空间。
以下
5
个维度需逐项输出:
1
、语速与节奏
【现状描述】详细描述当前语速和节奏的表现,如:平均语速约
XXX
字
/
分钟,整体偏快
/
适中
/
偏慢。在产品讲解环节语速过快,在互动环节节奏较好。
【存在问题】示例如下:
问题
1
,如:产品讲解环节(
X
分
X
秒
-
X
分
X
秒)语速过快,达到
XXX
字
/
分钟,观众可能跟不上
问题
2
,如:停顿不当,关键信息没有留出思考时间
问题
3
,如:节奏不稳定,时快时慢,影响观看体验
【改进建议】示例如下:
具体建议
1
,如:在讲解三重玻尿酸成分时,有意识放慢语速至
200
字
/
分钟以内,并在关键数据后
停顿
1
-
2
秒
具体建议
2
,如:使用
"那么"
、
"接下来"
等连接词自然控制节奏
具体建议
3
,如:提前充分练习话术脚本,避免因不熟练导致语速失控
(语速标准参考内容需保留原文,不写入报告主体)
语速标准参考
理想语速:
180
-
220
字
/
分钟
-
观众能够轻松跟上
-
有足够时间理解信息
-
节奏舒适,不紧不慢
偏快语速:
221
-
240
字
/
分钟
-
部分观众可能跟不上
-
影响信息吸收效果
-
建议适当放慢
过快语速:
>
240
字
/
分钟
-
大部分观众难以跟上
-
严重影响直播效果
-
必须改进
偏慢语速:
150
-
179
字
/
分钟
-
节奏稍显拖沓
-
可能影响直播间活跃度
-
建议适当加快
过慢语速:
<
150
字
/
分钟
-
节奏过于拖沓
-
观众容易失去耐心
-
必须改进
2
、互动能力
【现状描述】详细描述当前互动情况,如:互动频率一般
/
良好
/
不足,共发起
X
次互动,回应了
X
条观众评论。互动形式包括
...
【存在问题】示例如下:
问题
1
,如:互动频率过低,整场直播仅发起
X
次互动,直播间氛围不够活跃
问题
2
,如:互动问题设计不够吸引人,观众参与度不高
问题
3
,如:对观众评论回应不够及时,错过了多次互动机会
问题
4
,如:抽奖活动执行不到位,未能充分调动观众积极性
【改进建议】示例如下:
具体建议
1
,如:增加互动频率,建议每
3
-
5
分钟发起一次互动,如
"有宝宝用过玻尿酸面膜吗?效果如何?"
具体建议
2
,如:设计更有趣的互动问题,如
"猜猜这款面膜的秒杀价是多少?猜中送小样!"
具体建议
3
,如:及时关注评论区,对高频问题立即回应,如
"敏感肌能用吗?"
具体建议
4
,如:在点赞数接近目标时,强调
"还差XX个就到10000啦,大家帮忙点一下!"
营造参与感
3
、卖点讲解能力
【现状描述】详细描述当前卖点讲解情况,如:共讲解了
X
个卖点,覆盖率
XX
%
。讲解较为清晰
/
逻辑性强
/
略显混乱。
【存在问题】示例如下:
问题
1
,如:卖点讲解不够深入,对
"三重玻尿酸"
的概念解释不清
问题
2
,如:缺少具体案例和数据支撑,说服力不足
问题
3
,如:卖点讲解顺序混乱,没有逻辑层次
问题
4
,如:遗漏重要卖点
"神经酰胺和角鲨烷"
,影响产品价值传达
【改进建议】示例如下:
具体建议
1
,如:讲解
"三重玻尿酸"
时,要说明
"大中小分子玻尿酸,分别作用于肌肤不同层次,大分子锁水、中分子补水、小分子深层渗透"
具体建议
2
,如:引用具体数据
"真人实测48小时后肌肤水润度依然提升40%"
增强说服力
具体建议
3
,如:按照
"成分→功效→效果→价格"
的逻辑顺序讲解卖点
具体建议
4
,如:将所有
5
个卖点都讲解到位,每个卖点至少讲解
30
秒
4
、促单能力
【现状描述】详细描述当前促单话术使用情况,如:促单环节共
X
次,促单话术包括
...
促单效果一般
/
良好
/
不足。
【存在问题】示例如下:
问题
1
,如:促单话术力度不够,缺乏紧迫感营造
问题
2
,如:未充分强调价格优惠,
"秒杀价69元"
只提了一次
问题
3
,如:限时限量信息传达不清,未能激发购买欲望
问题
4
,如:组合优惠活动讲解不够清晰,观众可能不理解
【改进建议】示例如下:
具体建议
1
,如:在秒杀环节强调
"只剩最后5分钟!只有100份!现在还剩XX份!"
营造紧迫感
具体建议
2
,如:多次强调价格对比
"平时99元,今天秒杀只要69元,一片才6.9元,太划算了!"
具体建议
3
,如:在促销环节使用催单话术
"已经有XX位宝宝下单了,还在犹豫的宝宝抓紧啦!"
具体建议
4
,如:清晰讲解组合优惠
"买10片装送2片,相当于12片只要69元,比单买划算多了!"
5
、整体表现力
【现状描述】详细描述整体表现,如:仪表整洁,妆容得体。肢体语言自然
/
略显僵硬。镜头感良好
/
需加强。整体自信度较好
/
略显紧张。
【存在问题】示例如下:
问题
1
,如:肢体语言不够丰富,全程较为僵硬
问题
2
,如:眼神交流不够,经常低头看脚本
问题
3
,如:产品展示不够充分,没有特写展示
问题
4
,如:整体略显紧张,影响自信度
【改进建议】示例如下:
具体建议
1
,如:增加手势配合讲解,如讲
"三重玻尿酸"
时用手势比划
"三层"
具体建议
2
,如:尽量看镜头而不是脚本,熟记话术内容
具体建议
3
,如:展示产品时,将产品靠近镜头,展示包装细节和文字说明
具体建议
4
,如:直播前深呼吸放松,告诉自己
"我准备充分,一定可以"
四、语速分析报告
━━
(一)语速分析
━━
整体语速数据
平均语速:
XXX
字
/
分钟
最快语速:
XXX
字
/
分钟(出现在第
X
分
X
秒)
最慢语速:
XXX
字
/
分钟(出现在第
X
分
X
秒)
语速波动:
XX
字
/
分钟(标准差)
━━
(二)语速标准参考
━━
理想语速:
180
-
220
字
/
分钟
-
观众能够轻松跟上
-
有足够时间理解信息
-
节奏舒适,不紧不慢
偏快语速:
221
-
240
字
/
分钟
-
部分观众可能跟不上
-
影响信息吸收效果
-
建议适当放慢
过快语速:
>
240
字
/
分钟
-
大部分观众难以跟上
-
严重影响直播效果
-
必须改进
偏慢语速:
150
-
179
字
/
分钟
-
节奏稍显拖沓
-
可能影响直播间活跃度
-
建议适当加快
过慢语速:
<
150
字
/
分钟
-
节奏过于拖沓
-
观众容易失去耐心
-
必须改进
━━
(三)语速曲线分析
━━
【语速统计区间】(输出为表格,表头和示例行保持不变)
时间段
|
语速
(
字
/
分
)
|
评价
|
对应环节
---------------|-------------|--------|-------------
00
:
00
-
01
:
00
|
XXX
|
✅正常
|
开场白
01
:
00
-
02
:
00
|
XXX
|
✅正常
|
品牌介绍
02
:
00
-
03
:
00
|
XXX
|
⚠️偏慢
|
品牌介绍
03
:
00
-
04
:
00
|
XXX
|
✅正常
|
产品讲解
-
开始
04
:
00
-
05
:
00
|
XXX
|
⚡偏快
|
产品讲解
-
成分
05
:
00
-
06
:
00
|
XXX
|
❌过快
|
产品讲解
-
卖点
06
:
00
-
07
:
00
|
XXX
|
✅正常
|
产品讲解
-
效果
07
:
00
-
08
:
00
|
XXX
|
✅正常
|
产品讲解
-
价格
08
:
00
-
09
:
00
|
XXX
|
⚠️偏慢
|
互动环节
09
:
00
-
10
:
00
|
XXX
|
⚡偏快
|
促销推动
10
:
00
-
11
:
00
|
XXX
|
⚡偏快
|
促销推动
11
:
00
-
12
:
00
|
XXX
|
✅正常
|
催单话术
12
:
00
-
13
:
00
|
XXX
|
✅正常
|
组合优惠
13
:
00
-
14
:
00
|
XXX
|
✅正常
|
结束语
14
:
00
-
15
:
00
|
XXX
|
✅正常
|
结束语
━━
(四)语速异常时间段详细分析
━━
示例如下:
【异常片段
#
1
】━━━━━━━━━━━━
时间:
XX
:
XX
-
XX
:
XX
(
X
分钟)
问题:语速过快
数据:
XXX
字
/
分钟(超标准
XX
字
/
分钟)
对应内容:【具体内容,如:产品卖点讲解
-
三重玻尿酸成分介绍】
【异常片段
#
2
】━━━━━━━━━━━━
时间:
XX
:
XX
-
XX
:
XX
(
X
分钟)
问题:语速过快
/
持续偏快
数据:
XXX
字
/
分钟(超标准
XX
字
/
分钟)
对应内容:【具体内容,如:促销推动环节】
【异常片段
#
3
】━━━━━━━━━━━━
时间:
XX
:
XX
-
XX
:
XX
(
X
分钟)
问题:语速过慢
数据:
XXX
字
/
分钟(低于标准
XX
字
/
分钟)
对应内容:【具体内容,如:品牌介绍环节】
【异常片段
#
4
】━━━━━━━━━━━━
时间:
XX
:
XX
-
XX
:
XX
(
X
分钟)
问题:语速过慢
数据:
XXX
字
/
分钟(低于标准
XX
字
/
分钟)
对应内容:【具体内容,如:互动环节】
━━
(五)语速变化趋势分析
━━
【整体趋势】描述语速在整场直播中的变化趋势,如:开场语速正常,在产品讲解环节(
4
-
6
分钟)明显加快,互动环节放慢,促销环节又再次加快,结束语恢复正常。整体呈现
"波浪形"
变化,稳定性不足。
【问题诊断】示例如下:
主要问题:【如:在需要重点讲解的环节(产品卖点)语速反而过快,影响效果】
次要问题:【如:语速波动过大,缺乏整体节奏控制】
#
风格与注意事项
-
语言保持专业、客观、具体,结合提供的数据或示例描述。
-
如遇缺失信息,可使用“暂无数据”或根据上下文合理推断,但避免胡乱编造。
-
若违禁词为零,自动隐藏“违禁词详细列表”“违规情况统计”“高频违禁词”等相关段落。
-
JSON
输出中的四个字段必须全部返回,值为字符串类型,内容为对应
Markdown
片段。
-
不允许调整前端依赖的标题、排版符号、表格结构与占位变量名称。
`,
},
]
const result = await generateText({ messages, response_format: { type: 'json_object' } })
const aiAnalyze = result.content
.replaceAll('一、卖点讲解覆盖情况', '')
.replaceAll('二、违禁词分析报告', '')
.replaceAll('三、直播优化建议', '')
.replaceAll('四、语速分析报告', '')
await updateRecord({ id: props.recordId, ai_analyze: aiAnalyze })
fetchRecord()
}
function formatDuration(seconds) {
...
...
@@ -100,7 +412,7 @@ function formatDuration(seconds) {
</
script
>
<
template
>
<div
class=
"live-view"
v-if=
"detail"
v-loading=
"isLoading"
element-loading-text=
"AI
打分
中..."
>
<div
class=
"live-view"
v-if=
"detail"
v-loading=
"isLoading"
element-loading-text=
"AI
分析
中..."
>
<AppCard>
<div
class=
"live-info"
>
<div
class=
"live-info-header"
>
...
...
src/modules/live/test/composables/useLiveChat.ts
浏览文件 @
2c83c129
...
...
@@ -120,12 +120,13 @@ export function useLiveChat(options: UseLiveChatOptions = {}) {
// 随机事件生成
const
generateRandomEvents
=
()
=>
{
const
rand
=
Math
.
random
()
if
(
rand
<
0.
9
)
randomJoin
()
if
(
rand
<
0.
8
)
randomJoin
()
if
(
rand
<
0.4
)
randomLeave
()
if
(
rand
<
0.7
)
randomGift
()
if
(
rand
<
0.8
)
randomLike
()
}
let
timer
:
number
|
null
=
null
// 开始
const
start
=
()
=>
{
// 初始化观众
...
...
@@ -133,7 +134,11 @@ export function useLiveChat(options: UseLiveChatOptions = {}) {
randomJoin
()
}
// 开始定时更新
setInterval
(
generateRandomEvents
,
updateInterval
)
timer
=
setInterval
(
generateRandomEvents
,
updateInterval
)
}
const
stop
=
()
=>
{
if
(
timer
)
clearInterval
(
timer
)
timer
=
null
}
if
(
autoStart
)
start
()
...
...
@@ -161,5 +166,5 @@ export function useLiveChat(options: UseLiveChatOptions = {}) {
{
immediate
:
true
}
)
return
{
viewers
,
messages
,
stats
,
currentTime
,
start
,
reset
}
return
{
viewers
,
messages
,
stats
,
currentTime
,
start
,
stop
,
reset
}
}
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论