Files
lingji-work-fe/src/views/components/dataEngine/userPainPoints.vue
2025-07-23 16:41:38 +08:00

324 lines
8.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view>
<topHeader ref="topHeaderRef" @search="search"></topHeader>
<!-- 用户痛点观察 -->
<a-space
direction="vertical"
style="background-color: #fff; width: 100%; padding: 0 20px"
class="bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid mb-24px"
>
<div class="title-row">
<span class="title mr-4px">用户痛点观察</span>
<a-tooltip>
<template #content
>基于用户内容中的情绪分析与表达模式提取反复出现的负面倾向主题反映典型使用痛点</template
>
<icon-question-circle size="16" class="!color-#737478" />
</a-tooltip>
</div>
<a-table
:columns="columns"
:data="dataList"
:filter-icon-align-left="alignLeft"
:scroll="true"
:pagination="false"
@change="handleChange"
>
<template #empty>
<NoData />
</template>
<template #rank="{ record }">
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
<span v-else>{{ record.rank }}</span>
</template>
<template #keywords="{ record }">
<a-tag
v-for="item in record.keywords"
:key="item"
class="!rounded-2px !px-8px !py-1px !bg-#F2F3F5 !h-22px !color-#3C4043 mb-5px mr-5px"
>{{ item }}</a-tag
>
</template>
<template #frequency="{ record }">
<a-tag
:class="`!rounded-2px !px-8px !py-1px !bg-${frequencyStatus[record.frequency].bgColor} !h-22px !color-${
frequencyStatus[record.frequency].color
}`"
>{{ frequencyStatus[record.frequency].label }}</a-tag
>
</template>
<template #optional="{ record }">
<a-button type="outline" class="!rounded-4px" @click="gotoDetail(record)">详情</a-button>
</template>
</a-table>
</a-space>
<a-modal
:visible="visible"
modal-class="user-pain-points-modal"
unmountOnClose
width="640px"
@ok="handleOk"
@cancel="handleCancel"
>
<template #title>
<span style="text-align: left; width: 100%">用户痛点观察</span>
</template>
<div>
<a-space direction="vertical" style="font-size: 12px">
<div class="mb-12px flex items-center">
<p class="cts !mr-16px flex-shrink-0 w-60px">痛点</p>
<span class="cts">{{ topicInfo.name }}</span>
</div>
<div class="mb-12px flex items-center">
<p class="cts !mr-16px flex-shrink-0 w-60px">关键词</p>
<a-tag
v-for="item in topicInfo.keywords"
:key="item"
class="mr-8px py-10px px-8px rounded-4px bg-#F2F3F5 cts !h-24px"
>{{ item }}</a-tag
>
</div>
<div class="mb-12px flex items-center">
<p class="cts !mr-16px flex-shrink-0 w-60px">频次</p>
<a-tag
:class="`!rounded-2px !px-8px !py-1px !bg-${
frequencyStatus[topicInfo.frequency].bgColor
} !h-22px !color-${frequencyStatus[topicInfo.frequency].color}`"
>{{ frequencyStatus[topicInfo.frequency].label }}</a-tag
>
</div>
<div class="mb-12px flex items-center">
<p class="cts !mr-16px flex-shrink-0 w-60px">代表性发言</p>
<span class="cts">{{ topicInfo.content }}</span>
</div>
<div class="flex items-start">
<p class="cts !mr-16px flex-shrink-0 w-60px">原始来源</p>
<div class="flex flex-col">
<div v-for="item in topicInfo.user_pain_point_sources" :key="item" class="mb-18px flex items-center">
<a-link style="background-color: initial" :href="item.link" target="_blank" class="!text-12px">{{
item.title
}}</a-link>
<img src="@/assets/img/hottranslation/xhs.png" width="16" height="16" />
</div>
</div>
</div>
</a-space>
</div>
<template #footer>
<a-button size="large" @click="handleCancel">取消</a-button>
<a-button type="primary" size="large" class="rounded-4px" @click="handleOk"> 确定 </a-button>
</template>
</a-modal>
</view>
</template>
<script setup>
import topHeader from './topHeader.vue';
import { fetchUserPainPointsDetail, fetchUserPainPointsList } from '@/api/all/index';
import { ref, onMounted, computed } from 'vue';
import top1 from '@/assets/img/captcha/top1.svg';
import top2 from '@/assets/img/captcha/top2.svg';
import top3 from '@/assets/img/captcha/top3.svg';
const topImages = [top1, top2, top3];
const visible = ref(false);
const topicInfo = ref({});
const topHeaderRef = ref();
// 从topHeader获取统一的状态
const selectedIndustry = computed(() => topHeaderRef.value?.selectedIndustry);
const selectedSubCategory = computed(() => topHeaderRef.value?.selectedSubCategory);
const selectedTimePeriod = computed(() => topHeaderRef.value?.selectedTimePeriod);
const dataList = ref([]);
const frequencyStatus = {
0: {
label: '低频',
color: '#1bae71',
bgColor: '#ebf7f2',
},
1: {
label: '中频',
color: '#cc8b00',
bgColor: '#fff5de',
},
2: {
label: '高频',
color: '#c53c27',
bgColor: '#ffe7e4',
},
};
const columns = [
{
title: '排名',
dataIndex: 'rank',
slotName: 'rank',
width: 60,
minWidth: 60,
},
{
title: '痛点名称',
dataIndex: 'name',
width: 300,
minWidth: 300,
},
{
title: '关键词',
dataIndex: 'keywords',
slotName: 'keywords',
width: 250,
minWidth: 250,
},
{
width: 180,
minWidth: 180,
title: '频次',
dataIndex: 'frequency',
sortable: {
sortDirections: ['ascend', 'descend'],
},
slotName: 'frequency',
},
{
title: '代表性发言',
dataIndex: 'content',
slotName: 'content',
width: 320,
minWidth: 320,
},
{
title: '操作',
slotName: 'optional',
width: 120,
minWidth: 120,
},
];
// 详情
const gotoDetail = async (record) => {
const res = await fetchUserPainPointsDetail(record.id);
if (res.code == 200) {
topicInfo.value = res.data;
visible.value = true;
}
};
const getUserPainPointsList = async () => {
const params = {
industry_id: selectedIndustry.value,
time_dimension: selectedTimePeriod.value,
};
if (selectedIndustry.value == undefined) {
return;
}
if (selectedSubCategory.value == undefined) {
return;
}
if (selectedSubCategory.value != 0) {
params['industry_id'] = selectedSubCategory.value;
}
const res = await fetchUserPainPointsList(params);
if (res.code === 200) {
dataList.value = res.data;
}
};
// 弹窗的取消
const handleCancel = () => {
visible.value = false;
};
// 弹窗的确定
const handleOk = () => {
visible.value = false;
};
// 监听筛选条件变化
watch([selectedIndustry, selectedTimePeriod, selectedSubCategory], () => {
getUserPainPointsList();
});
onMounted(() => {
getUserPainPointsList();
});
const search = () => {
getUserPainPointsList();
};
</script>
<style scoped lang="scss">
/* 自定义样式 */
:deep(.arco-table-th) {
background-color: var(--color-fill-2);
}
:deep(.arco-table-tr):hover {
background-color: var(--color-fill-1);
}
:deep(.arco-statistic-content .arco-statistic-value-integer) {
font-size: 14px;
}
.pop-btn {
background: #fff !important;
border-color: #fff !important;
color: #737478 !important;
margin-left: -5px;
}
:deep(.arco-btn-outline) {
color: #6d4cfe !important;
border-color: #6d4cfe !important;
}
.title-row {
display: flex;
height: 64px;
padding: 10px 0 2px 0;
align-items: center;
.title {
color: var(--Text-1, #211f24);
font-family: $font-family-medium;
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: 24px; /* 150% */
}
}
</style>
<style lang="scss">
.user-pain-points-modal {
.arco-modal-header {
border-bottom: none;
height: 56px;
padding: 0 20px;
.arco-modal-title {
justify-content: flex-start;
}
}
.arco-modal-body {
padding: 12px 20px 0;
.cts {
color: var(--Text-2, #3c4043);
font-family: $font-family-regular;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 166.667% */
}
p {
margin: 0;
padding: 0;
}
}
.arco-modal-footer {
display: flex;
height: 64px;
padding: 0px 20px;
justify-content: flex-end;
align-items: center;
border-top: 1px solid var(--Border-1, #d7d7d9);
}
}
</style>