提交 f96316c7 authored 作者: 王鹏飞's avatar 王鹏飞

feat: 增加AI分析与总结

上级 78ec16fa
...@@ -34,3 +34,8 @@ export function getMemberMetaAttrs() { ...@@ -34,3 +34,8 @@ export function getMemberMetaAttrs() {
export function getUserTags(params: { sso_id: string; limit: number }) { export function getUserTags(params: { sso_id: string; limit: number }) {
return httpRequest.get('/api/lab/v1/experiment/analyse/user-tags', { params }) return httpRequest.get('/api/lab/v1/experiment/analyse/user-tags', { params })
} }
// AI分析与总结
export function getAISummary() {
return httpRequest.get('/api/lab/v1/experiment/member/ai-all-person')
}
<script setup>
import { getAISummary } from '../api'
const content = ref('')
const isLoading = ref(false)
async function fetchAI() {
isLoading.value = true
const res = await getAISummary()
content.value = res.data.result
isLoading.value = false
}
onMounted(() => {
fetchAI()
})
</script>
<template>
<el-dialog title="AI用户整体画像分析与建议">
<div v-loading="isLoading">
<el-input type="textarea" :rows="15" :value="content"></el-input>
</div>
<template #footer>
<el-row justify="center">
<el-button round @click="$emit('update:modelValue', false)">关闭</el-button>
</el-row>
</template>
</el-dialog>
</template>
...@@ -8,6 +8,9 @@ import { useMapStore } from '@/stores/map' ...@@ -8,6 +8,9 @@ import { useMapStore } from '@/stores/map'
import { getNameByValue } from '@/utils/dictionary' import { getNameByValue } from '@/utils/dictionary'
import * as api from '../api' import * as api from '../api'
const AISummaryDialog = defineAsyncComponent(() => import('../components/AISummaryDialog.vue'))
const aiDialogVisible = ref(false)
const connectionTypeList = useMapStore().getMapValuesByKey('experiment_connection_type') const connectionTypeList = useMapStore().getMapValuesByKey('experiment_connection_type')
const statusList = useMapStore().getMapValuesByKey('system_status') const statusList = useMapStore().getMapValuesByKey('system_status')
...@@ -57,7 +60,7 @@ const genderOption = computed(() => { ...@@ -57,7 +60,7 @@ const genderOption = computed(() => {
return { return {
grid: { left: '60', right: '60' }, grid: { left: '60', right: '60' },
tooltip: { tooltip: {
trigger: 'item' trigger: 'item',
}, },
yAxis: { yAxis: {
data: ['男性', '女性'], data: ['男性', '女性'],
...@@ -67,15 +70,20 @@ const genderOption = computed(() => { ...@@ -67,15 +70,20 @@ const genderOption = computed(() => {
axisLabel: { axisLabel: {
formatter: function (value, index) { formatter: function (value, index) {
const total = parseInt(man.total) + parseInt(woman.total) const total = parseInt(man.total) + parseInt(woman.total)
return value + '\n' + (index === 0 ? ((man.total / total) * 100).toFixed(1) : ((woman.total / total) * 100).toFixed(1)) + '%' return (
} value +
} '\n' +
(index === 0 ? ((man.total / total) * 100).toFixed(1) : ((woman.total / total) * 100).toFixed(1)) +
'%'
)
},
},
}, },
xAxis: { xAxis: {
splitLine: { show: false }, splitLine: { show: false },
axisLabel: { show: false }, axisLabel: { show: false },
axisTick: { show: false }, axisTick: { show: false },
axisLine: { show: false } axisLine: { show: false },
}, },
series: [ series: [
{ {
...@@ -86,10 +94,10 @@ const genderOption = computed(() => { ...@@ -86,10 +94,10 @@ const genderOption = computed(() => {
symbolMargin: 10, symbolMargin: 10,
data: [ data: [
{ value: man.total, symbol: manIcon, itemStyle: { color: '#767aca' } }, { value: man.total, symbol: manIcon, itemStyle: { color: '#767aca' } },
{ value: woman.total, symbol: womanIcon, itemStyle: { color: '#d26080' } } { value: woman.total, symbol: womanIcon, itemStyle: { color: '#d26080' } },
] ],
} },
] ],
} }
}) })
...@@ -101,7 +109,7 @@ async function fetchConnections() { ...@@ -101,7 +109,7 @@ async function fetchConnections() {
loading2.value = true loading2.value = true
try { try {
const res = await api.getMemberConnections({ sso_id: userValue.value }) const res = await api.getMemberConnections({ sso_id: userValue.value })
connection.value = res.data.items.map(item => { connection.value = res.data.items.map((item) => {
return { ...item, group_name: getNameByValue(item.group_name, connectionTypeList) } return { ...item, group_name: getNameByValue(item.group_name, connectionTypeList) }
}) })
} finally { } finally {
...@@ -113,24 +121,24 @@ const connectionOption = computed(() => { ...@@ -113,24 +121,24 @@ const connectionOption = computed(() => {
return { return {
grid: { left: '5%', top: '10%', right: '5%', bottom: '5%', containLabel: true }, grid: { left: '5%', top: '10%', right: '5%', bottom: '5%', containLabel: true },
tooltip: { tooltip: {
trigger: 'axis' trigger: 'axis',
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
axisLabel: { interval: 0 }, axisLabel: { interval: 0 },
data: connection.value.map(item => item.group_name) data: connection.value.map((item) => item.group_name),
}, },
yAxis: { yAxis: {
type: 'value' type: 'value',
}, },
series: [ series: [
{ {
name: '数据', name: '数据',
type: 'bar', type: 'bar',
label: { show: true, position: 'top' }, label: { show: true, position: 'top' },
data: connection.value.map(item => item.total) data: connection.value.map((item) => item.total),
} },
] ],
} }
}) })
...@@ -142,7 +150,7 @@ async function fetchStatus() { ...@@ -142,7 +150,7 @@ async function fetchStatus() {
loading3.value = true loading3.value = true
try { try {
const res = await api.getMemberStatus({ sso_id: userValue.value }) const res = await api.getMemberStatus({ sso_id: userValue.value })
status.value = res.data.items.map(item => { status.value = res.data.items.map((item) => {
return { name: getNameByValue(item.group_name, statusList), value: item.total } return { name: getNameByValue(item.group_name, statusList), value: item.total }
}) })
} finally { } finally {
...@@ -155,7 +163,7 @@ const statusOption = computed(() => { ...@@ -155,7 +163,7 @@ const statusOption = computed(() => {
grid: { left: '5%', top: '10%', right: '5%', bottom: '5%', containLabel: true }, grid: { left: '5%', top: '10%', right: '5%', bottom: '5%', containLabel: true },
tooltip: { tooltip: {
trigger: 'item', trigger: 'item',
formatter: '{b}: {c}<br />{d}%' formatter: '{b}: {c}<br />{d}%',
}, },
series: [ series: [
{ {
...@@ -163,9 +171,9 @@ const statusOption = computed(() => { ...@@ -163,9 +171,9 @@ const statusOption = computed(() => {
label: { formatter: '{b}\n{d}%' }, label: { formatter: '{b}\n{d}%' },
itemStyle: { borderRadius: 6 }, itemStyle: { borderRadius: 6 },
radius: [0, '70%'], radius: [0, '70%'],
data: status.value data: status.value,
} },
] ],
} }
}) })
</script> </script>
...@@ -173,15 +181,28 @@ const statusOption = computed(() => { ...@@ -173,15 +181,28 @@ const statusOption = computed(() => {
<template> <template>
<AppCard title="用户分析"> <AppCard title="用户分析">
<el-form inline label-suffix=":"> <el-form inline label-suffix=":">
<el-form-item label="实验名称">{{ info?.name }}</el-form-item> <div style="display: flex; justify-content: space-between">
<el-form-item label="请选择学生/老师"> <div>
<el-select v-model="userValue" filterable> <el-form-item label="实验名称">{{ info?.name }}</el-form-item>
<el-option v-for="item in userList" :label="item.name" :value="item.sso_id" :key="item.sso_id"></el-option> <el-form-item label="请选择学生/老师">
</el-select> <el-select v-model="userValue" filterable>
</el-form-item> <el-option
<el-form-item> v-for="item in userList"
<el-button type="primary" :icon="DataLine" :loading="loading" @click="handleStart">分析</el-button> :label="item.name"
</el-form-item> :value="item.sso_id"
:key="item.sso_id"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="DataLine" :loading="loading" @click="handleStart">分析</el-button>
</el-form-item>
</div>
<div>
<el-form-item>
<el-button type="primary" @click="aiDialogVisible = true">AI分析与总结</el-button>
</el-form-item>
</div>
</div>
<el-divider style="margin: 10px 0" /> <el-divider style="margin: 10px 0" />
<el-form-item label="用户总数"> <el-form-item label="用户总数">
<b class="total">{{ userTotal }}</b> <b class="total">{{ userTotal }}</b>
...@@ -199,6 +220,7 @@ const statusOption = computed(() => { ...@@ -199,6 +220,7 @@ const statusOption = computed(() => {
<UserChart :ssoId="userValue" /> <UserChart :ssoId="userValue" />
</div> </div>
</AppCard> </AppCard>
<AISummaryDialog v-model="aiDialogVisible" v-if="aiDialogVisible"></AISummaryDialog>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
......
...@@ -70,3 +70,8 @@ export function updateLabelRule(data: { id: string; rules: string }) { ...@@ -70,3 +70,8 @@ export function updateLabelRule(data: { id: string; rules: string }) {
export function getLabelMembers(params: { tag_id: string }) { export function getLabelMembers(params: { tag_id: string }) {
return httpRequest.get('/api/lab/v1/experiment/tag/bda-statistics-users', { params }) return httpRequest.get('/api/lab/v1/experiment/tag/bda-statistics-users', { params })
} }
// AI分析与总结
export function getAISummary() {
return httpRequest.get('/api/lab/v1/experiment/member/ai-tag')
}
<script setup>
import { getAISummary } from '../api'
const content = ref('')
const isLoading = ref(false)
async function fetchAI() {
isLoading.value = true
const res = await getAISummary()
content.value = res.data.result
isLoading.value = false
}
onMounted(() => {
fetchAI()
})
</script>
<template>
<el-dialog title="AI用户整体画像分析与建议">
<div v-loading="isLoading">
<el-input type="textarea" :rows="15" :value="content"></el-input>
</div>
<template #footer>
<el-row justify="center">
<el-button round @click="$emit('update:modelValue', false)">关闭</el-button>
</el-row>
</template>
</el-dialog>
</template>
...@@ -16,6 +16,8 @@ const userStore = useUserStore() ...@@ -16,6 +16,8 @@ const userStore = useUserStore()
const LabelFormDialog = defineAsyncComponent(() => import('../components/LabelFormDialog.vue')) const LabelFormDialog = defineAsyncComponent(() => import('../components/LabelFormDialog.vue'))
const LabelViewDialog = defineAsyncComponent(() => import('../components/LabelViewDialog.vue')) const LabelViewDialog = defineAsyncComponent(() => import('../components/LabelViewDialog.vue'))
const LabelRuleDialog = defineAsyncComponent(() => import('../components/LabelRuleDialog.vue')) const LabelRuleDialog = defineAsyncComponent(() => import('../components/LabelRuleDialog.vue'))
const AISummaryDialog = defineAsyncComponent(() => import('../components/AISummaryDialog.vue'))
const aiDialogVisible = ref(false)
const statusList = useMapStore().getMapValuesByKey('system_status') const statusList = useMapStore().getMapValuesByKey('system_status')
const { typeList } = useLabelType() const { typeList } = useLabelType()
...@@ -36,7 +38,7 @@ const listOptions = computed(() => { ...@@ -36,7 +38,7 @@ const listOptions = computed(() => {
params.updated_operator = listParams.updated_operator params.updated_operator = listParams.updated_operator
} }
return params return params
} },
}, },
filters: [ filters: [
{ type: 'input', prop: 'name', placeholder: '请输入标签名称' }, { type: 'input', prop: 'name', placeholder: '请输入标签名称' },
...@@ -46,16 +48,16 @@ const listOptions = computed(() => { ...@@ -46,16 +48,16 @@ const listOptions = computed(() => {
placeholder: '请选择标签目录', placeholder: '请选择标签目录',
options: typeList.value, options: typeList.value,
labelKey: 'name', labelKey: 'name',
valueKey: 'id' valueKey: 'id',
}, },
{ {
type: 'select', type: 'select',
prop: 'label', prop: 'label',
placeholder: '请选择标签类型', placeholder: '请选择标签类型',
options: labelList options: labelList,
}, },
{ type: 'select', prop: 'status', placeholder: '请选择标签状态', options: statusList }, { type: 'select', prop: 'status', placeholder: '请选择标签状态', options: statusList },
{ type: 'input', prop: 'updated_operator', placeholder: '更新人', slots: 'filter-user' } { type: 'input', prop: 'updated_operator', placeholder: '更新人', slots: 'filter-user' },
], ],
columns: [ columns: [
{ type: 'selection' }, { type: 'selection' },
...@@ -67,7 +69,7 @@ const listOptions = computed(() => { ...@@ -67,7 +69,7 @@ const listOptions = computed(() => {
prop: 'label', prop: 'label',
computed({ row }: { row: Label }) { computed({ row }: { row: Label }) {
return getNameByValue(row.label, labelList) || row.label return getNameByValue(row.label, labelList) || row.label
} },
}, },
{ label: '标签目录', prop: 'tag_type.name' }, { label: '标签目录', prop: 'tag_type.name' },
{ label: '标签权重', prop: 'weight' }, { label: '标签权重', prop: 'weight' },
...@@ -76,7 +78,7 @@ const listOptions = computed(() => { ...@@ -76,7 +78,7 @@ const listOptions = computed(() => {
prop: 'update_status', prop: 'update_status',
computed({ row }: { row: Label }) { computed({ row }: { row: Label }) {
return getNameByValue(row.update_status, updateStatusRuleList) return getNameByValue(row.update_status, updateStatusRuleList)
} },
}, },
{ {
label: '状态', label: '状态',
...@@ -84,18 +86,18 @@ const listOptions = computed(() => { ...@@ -84,18 +86,18 @@ const listOptions = computed(() => {
computed({ row }: { row: Label }) { computed({ row }: { row: Label }) {
const color = row.status === '1' ? 'var(--main-success-color)' : 'var(--main-color)' const color = row.status === '1' ? 'var(--main-success-color)' : 'var(--main-color)'
return `<span style="color: ${color}">${getNameByValue(row.status, statusList)}</span>` return `<span style="color: ${color}">${getNameByValue(row.status, statusList)}</span>`
} },
}, },
{ {
label: '更新人', label: '更新人',
prop: 'updated_operator.real_name', prop: 'updated_operator.real_name',
computed({ row }: any) { computed({ row }: any) {
return row.updated_operator?.real_name || row.updated_operator?.nickname return row.updated_operator?.real_name || row.updated_operator?.nickname
} },
}, },
{ label: '更新时间', prop: 'updated_time' }, { label: '更新时间', prop: 'updated_time' },
{ label: '操作', slots: 'table-x', width: 320 } { label: '操作', slots: 'table-x', width: 320 },
] ],
} }
}) })
// 刷新 // 刷新
...@@ -151,11 +153,11 @@ function handleSelectionChange(selection: Label[]) { ...@@ -151,11 +153,11 @@ function handleSelectionChange(selection: Label[]) {
} }
const handleRemoves = async function () { const handleRemoves = async function () {
const ids = multipleSelection.map(item => item.id) const ids = multipleSelection.map((item) => item.id)
await ElMessageBox.confirm('确定要删除选中的标签数据吗?', '提示', { await ElMessageBox.confirm('确定要删除选中的标签数据吗?', '提示', {
confirmButtonText: '确认', confirmButtonText: '确认',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning',
}) })
await deleteLabels({ ids: JSON.stringify(ids) }) await deleteLabels({ ids: JSON.stringify(ids) })
appList?.refetch(true) appList?.refetch(true)
...@@ -169,10 +171,23 @@ const handleRemoves = async function () { ...@@ -169,10 +171,23 @@ const handleRemoves = async function () {
<div class="label-left"><LabelType :active-id="listParams.type_id" @select="handleSelect"></LabelType></div> <div class="label-left"><LabelType :active-id="listParams.type_id" @select="handleSelect"></LabelType></div>
<AppList v-bind="listOptions" ref="appList" class="label-right" @selection-change="handleSelectionChange"> <AppList v-bind="listOptions" ref="appList" class="label-right" @selection-change="handleSelectionChange">
<template #header-buttons> <template #header-buttons>
<el-button type="primary" :icon="Plus" @click="handleAdd" v-if="!userStore.status.tag_status">新建</el-button> <div style="display: flex; justify-content: space-between">
<el-button type="primary" plain :icon="Delete" :disabled="!multipleSelection.length" @click="handleRemoves" v-permission="'experiment_tag_delete'" <div>
>删除</el-button <el-button type="primary" :icon="Plus" @click="handleAdd" v-if="!userStore.status.tag_status"
> >新建</el-button
>
<el-button
type="primary"
plain
:icon="Delete"
:disabled="!multipleSelection.length"
@click="handleRemoves"
v-permission="'experiment_tag_delete'"
>删除</el-button
>
</div>
<el-button type="primary" @click="aiDialogVisible = true">AI建议</el-button>
</div>
</template> </template>
<template #filter-user> <template #filter-user>
<SelectUser v-model="listParams.updated_operator" placeholder="更新人" @change="handleRefresh"></SelectUser> <SelectUser v-model="listParams.updated_operator" placeholder="更新人" @change="handleRefresh"></SelectUser>
...@@ -181,18 +196,27 @@ const handleRemoves = async function () { ...@@ -181,18 +196,27 @@ const handleRemoves = async function () {
<template #table-x="{ row }"> <template #table-x="{ row }">
<el-button type="primary" plain @click="handleRule(row)">规则</el-button> <el-button type="primary" plain @click="handleRule(row)">规则</el-button>
<el-button type="primary" plain @click="handleView(row)">查看</el-button> <el-button type="primary" plain @click="handleView(row)">查看</el-button>
<el-button type="primary" plain @click="handleUpdate(row)" v-permission="'experiment_tag_update'">编辑</el-button> <el-button type="primary" plain @click="handleUpdate(row)" v-permission="'experiment_tag_update'"
<el-button type="primary" plain @click="handleRemove(row)" v-permission="'experiment_tag_delete'">删除</el-button> >编辑</el-button
>
<el-button type="primary" plain @click="handleRemove(row)" v-permission="'experiment_tag_delete'"
>删除</el-button
>
</template> </template>
</AppList> </AppList>
</div> </div>
</AppCard> </AppCard>
<!-- 新建/修改标签 --> <!-- 新建/修改标签 -->
<LabelFormDialog v-model="formVisible" :data="currentRow" @update="handleRefresh" v-if="formVisible"></LabelFormDialog> <LabelFormDialog
v-model="formVisible"
:data="currentRow"
@update="handleRefresh"
v-if="formVisible"></LabelFormDialog>
<!-- 查看标签 --> <!-- 查看标签 -->
<LabelViewDialog v-model="viewVisible" :data="currentRow" v-if="viewVisible && currentRow"></LabelViewDialog> <LabelViewDialog v-model="viewVisible" :data="currentRow" v-if="viewVisible && currentRow"></LabelViewDialog>
<!-- 规则 --> <!-- 规则 -->
<LabelRuleDialog v-model="ruleVisible" :data="currentRow" v-if="ruleVisible && currentRow"></LabelRuleDialog> <LabelRuleDialog v-model="ruleVisible" :data="currentRow" v-if="ruleVisible && currentRow"></LabelRuleDialog>
<AISummaryDialog v-model="aiDialogVisible" v-if="aiDialogVisible"></AISummaryDialog>
</template> </template>
<style lang="scss"> <style lang="scss">
......
...@@ -125,3 +125,8 @@ export function syncMember() { ...@@ -125,3 +125,8 @@ export function syncMember() {
export function clearMember() { export function clearMember() {
return httpRequest.get('/api/lab/v1/experiment/member/clear') return httpRequest.get('/api/lab/v1/experiment/member/clear')
} }
// AI分析与总结
export function getAISummary(params: { member_id: string }) {
return httpRequest.get('/api/lab/v1/experiment/member/ai-one-person', { params })
}
<script setup>
import { getAISummary } from '../api'
const props = defineProps(['id'])
const content = ref('')
const isLoading = ref(false)
async function fetchAI() {
isLoading.value = true
const res = await getAISummary({ member_id: props.id })
content.value = res.data.result
isLoading.value = false
}
onMounted(() => {
fetchAI()
})
</script>
<template>
<el-dialog title="AI用户画像分析与建议">
<div v-loading="isLoading">
<el-input type="textarea" :rows="15" :value="content"></el-input>
</div>
<template #footer>
<el-row justify="center">
<el-button round @click="$emit('update:modelValue', false)">关闭</el-button>
</el-row>
</template>
</el-dialog>
</template>
...@@ -7,6 +7,8 @@ import Icon from '@/components/ConnectionIcon.vue' ...@@ -7,6 +7,8 @@ import Icon from '@/components/ConnectionIcon.vue'
const ViewEvent = defineAsyncComponent(() => import('@/components/ViewEvent.vue')) const ViewEvent = defineAsyncComponent(() => import('@/components/ViewEvent.vue'))
const ViewLabel = defineAsyncComponent(() => import('@/components/ViewLabel.vue')) const ViewLabel = defineAsyncComponent(() => import('@/components/ViewLabel.vue'))
const ViewGroup = defineAsyncComponent(() => import('@/components/ViewGroup.vue')) const ViewGroup = defineAsyncComponent(() => import('@/components/ViewGroup.vue'))
const AISummaryDialog = defineAsyncComponent(() => import('../components/AISummaryDialog.vue'))
const aiDialogVisible = ref(false)
const route = useRoute() const route = useRoute()
...@@ -20,7 +22,7 @@ let data = $ref<ImageProp>() ...@@ -20,7 +22,7 @@ let data = $ref<ImageProp>()
let fieldsList = $ref<MemberFieldsProp[]>([]) let fieldsList = $ref<MemberFieldsProp[]>([])
onMounted(() => { onMounted(() => {
// 画像 // 画像
getMemberImage({ id: userId.value, 'per-page': 100 }).then(res => { getMemberImage({ id: userId.value, 'per-page': 100 }).then((res) => {
data = res.data data = res.data
getFields(res.data) getFields(res.data)
}) })
...@@ -28,7 +30,7 @@ onMounted(() => { ...@@ -28,7 +30,7 @@ onMounted(() => {
}) })
const getFields = function (data: { fields: any }) { const getFields = function (data: { fields: any }) {
getMemberFieldsList().then(res => { getMemberFieldsList().then((res) => {
fieldsList = res.data.map((item: MemberFieldsProp) => { fieldsList = res.data.map((item: MemberFieldsProp) => {
if (data.fields[item.id]) { if (data.fields[item.id]) {
item.value = data.fields[item.id] item.value = data.fields[item.id]
...@@ -86,7 +88,12 @@ async function fetchEvent(isReset = false) { ...@@ -86,7 +88,12 @@ async function fetchEvent(isReset = false) {
if (isReset) { if (isReset) {
Object.assign(event, { page: 1, total: 0, list: [] }) Object.assign(event, { page: 1, total: 0, list: [] })
} }
const { data } = await getMemberImage({ id: userId.value, connection_id: currentConnection.value, page: event.page, 'per-page': 20 }) const { data } = await getMemberImage({
id: userId.value,
connection_id: currentConnection.value,
page: event.page,
'per-page': 20,
})
Object.assign(event, { page: event.page + 1, total: data.events.total, list: [...event.list, ...data.events.list] }) Object.assign(event, { page: event.page + 1, total: data.events.total, list: [...event.list, ...data.events.list] })
} }
watch(currentConnection, () => { watch(currentConnection, () => {
...@@ -100,7 +107,11 @@ watch(currentConnection, () => { ...@@ -100,7 +107,11 @@ watch(currentConnection, () => {
<div class="info-name" style="min-width: 300px"> <div class="info-name" style="min-width: 300px">
<div class="tx"> <div class="tx">
<img <img
:src="data.gender === '1' ? 'https://webapp-pub.ezijing.com/pages/assa/dml_boy.png' : 'https://webapp-pub.ezijing.com/pages/assa/dml_girl.png'" /> :src="
data.gender === '1'
? 'https://webapp-pub.ezijing.com/pages/assa/dml_boy.png'
: 'https://webapp-pub.ezijing.com/pages/assa/dml_girl.png'
" />
<!-- https://webapp-pub.ezijing.com/pages/assa/dml_boy.png --> <!-- https://webapp-pub.ezijing.com/pages/assa/dml_boy.png -->
<!-- <el-icon :size="50" color="#fff"><UserFilled /></el-icon> --> <!-- <el-icon :size="50" color="#fff"><UserFilled /></el-icon> -->
</div> </div>
...@@ -117,12 +128,16 @@ watch(currentConnection, () => { ...@@ -117,12 +128,16 @@ watch(currentConnection, () => {
<el-form label-suffix=":" label-width="110px"> <el-form label-suffix=":" label-width="110px">
<el-form-item label="用户ID">{{ data.id }} </el-form-item> <el-form-item label="用户ID">{{ data.id }} </el-form-item>
<el-form-item label="状态"> <el-form-item label="状态">
<span :style="`color: ${data.status === '1' ? 'rgba(0,172,39,1)' : '#ba143e'}`">{{ data.status_name }}</span> <span :style="`color: ${data.status === '1' ? 'rgba(0,172,39,1)' : '#ba143e'}`">{{
data.status_name
}}</span>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-form label-suffix=":" label-width="110px"> <el-form label-suffix=":" label-width="110px">
<el-form-item label="最近活跃时间">{{ data.updated_time }} </el-form-item> <el-form-item label="最近活跃时间">{{ data.updated_time }} </el-form-item>
<el-form-item label="最近活跃时间" style="opacity: 0">{{ data.updated_time }} </el-form-item> <el-form-item label-width="0">
<el-button type="primary" @click="aiDialogVisible = true">AI分析与总结</el-button>
</el-form-item>
</el-form> </el-form>
</div> </div>
</div> </div>
...@@ -148,7 +163,14 @@ watch(currentConnection, () => { ...@@ -148,7 +163,14 @@ watch(currentConnection, () => {
<el-tabs class="demo-tabs"> <el-tabs class="demo-tabs">
<el-tab-pane label="当前标签"> <el-tab-pane label="当前标签">
<div class="scroll" v-if="data?.tags && data.tags.length"> <div class="scroll" v-if="data?.tags && data.tags.length">
<el-tag class="ml-2" type="success" v-for="(item, index) in data.tag_list" :key="index" @click="handleViewLabel(item)">{{ item.name }}</el-tag> <el-tag
class="ml-2"
type="success"
v-for="(item, index) in data.tag_list"
:key="index"
@click="handleViewLabel(item)"
>{{ item.name }}</el-tag
>
</div> </div>
<el-empty description="暂无数据" :image-size="80" v-else /> <el-empty description="暂无数据" :image-size="80" v-else />
</el-tab-pane> </el-tab-pane>
...@@ -156,7 +178,9 @@ watch(currentConnection, () => { ...@@ -156,7 +178,9 @@ watch(currentConnection, () => {
<el-tabs class="demo-tabs"> <el-tabs class="demo-tabs">
<el-tab-pane label="历史标签"> <el-tab-pane label="历史标签">
<div class="scroll" v-if="data?.history_tags && data.history_tags.length"> <div class="scroll" v-if="data?.history_tags && data.history_tags.length">
<el-tag class="ml-2" type="success" v-for="(item, index) in data.history_tags" :key="index">{{ item }}</el-tag> <el-tag class="ml-2" type="success" v-for="(item, index) in data.history_tags" :key="index">{{
item
}}</el-tag>
</div> </div>
<el-empty description="暂无数据" :image-size="80" v-else /> <el-empty description="暂无数据" :image-size="80" v-else />
</el-tab-pane> </el-tab-pane>
...@@ -166,9 +190,14 @@ watch(currentConnection, () => { ...@@ -166,9 +190,14 @@ watch(currentConnection, () => {
<el-tabs class="demo-tabs"> <el-tabs class="demo-tabs">
<el-tab-pane label="静态群组"> <el-tab-pane label="静态群组">
<div class="scroll" v-if="data?.static_groups && data.static_groups.length"> <div class="scroll" v-if="data?.static_groups && data.static_groups.length">
<el-tag class="ml-2" type="success" v-for="(item, index) in data.static_group_list" :key="index" @click="handleViewGroup(item)">{{ <el-tag
item.name class="ml-2"
}}</el-tag> type="success"
v-for="(item, index) in data.static_group_list"
:key="index"
@click="handleViewGroup(item)"
>{{ item.name }}</el-tag
>
</div> </div>
<el-empty description="暂无数据" :image-size="80" v-else /> <el-empty description="暂无数据" :image-size="80" v-else />
</el-tab-pane> </el-tab-pane>
...@@ -176,9 +205,14 @@ watch(currentConnection, () => { ...@@ -176,9 +205,14 @@ watch(currentConnection, () => {
<el-tabs class="demo-tabs"> <el-tabs class="demo-tabs">
<el-tab-pane label="动态群组"> <el-tab-pane label="动态群组">
<div class="scroll" v-if="data?.dynamic_groups && data.dynamic_groups.length"> <div class="scroll" v-if="data?.dynamic_groups && data.dynamic_groups.length">
<el-tag class="ml-2" type="success" v-for="(item, index) in data.dynamic_group_list" :key="index" @click="handleViewGroup(item)">{{ <el-tag
item.name class="ml-2"
}}</el-tag> type="success"
v-for="(item, index) in data.dynamic_group_list"
:key="index"
@click="handleViewGroup(item)"
>{{ item.name }}</el-tag
>
</div> </div>
<el-empty description="暂无数据" :image-size="80" v-else /> <el-empty description="暂无数据" :image-size="80" v-else />
</el-tab-pane> </el-tab-pane>
...@@ -187,7 +221,9 @@ watch(currentConnection, () => { ...@@ -187,7 +221,9 @@ watch(currentConnection, () => {
<AppCard class="card" title="用户行为轨迹"> <AppCard class="card" title="用户行为轨迹">
<div style="text-align: center"> <div style="text-align: center">
<el-radio-group v-model="currentConnection"> <el-radio-group v-model="currentConnection">
<el-radio-button :value="item.id" v-for="item in connectionList" :key="item.id">{{ item.type_name }}</el-radio-button> <el-radio-button :value="item.id" v-for="item in connectionList" :key="item.id">{{
item.type_name
}}</el-radio-button>
</el-radio-group> </el-radio-group>
</div> </div>
<template v-if="event.list.length"> <template v-if="event.list.length">
...@@ -214,11 +250,23 @@ watch(currentConnection, () => { ...@@ -214,11 +250,23 @@ watch(currentConnection, () => {
</AppCard> </AppCard>
</div> </div>
<!-- 事件详情 --> <!-- 事件详情 -->
<ViewEvent v-model="viewEventVisible" :event="currentViewEvent" :user="data" v-if="viewEventVisible && currentViewEvent"></ViewEvent> <ViewEvent
v-model="viewEventVisible"
:event="currentViewEvent"
:user="data"
v-if="viewEventVisible && currentViewEvent"></ViewEvent>
<!-- 查看标签 --> <!-- 查看标签 -->
<ViewLabel v-model="viewLabelVisible" :data="currentViewLabel" v-if="viewLabelVisible && currentViewLabel"></ViewLabel> <ViewLabel
v-model="viewLabelVisible"
:data="currentViewLabel"
v-if="viewLabelVisible && currentViewLabel"></ViewLabel>
<!-- 查看群组 --> <!-- 查看群组 -->
<ViewGroup v-model="viewGroupVisible" :data="currentViewGroup" v-if="viewGroupVisible && currentViewGroup"></ViewGroup> <ViewGroup
v-model="viewGroupVisible"
:data="currentViewGroup"
v-if="viewGroupVisible && currentViewGroup"></ViewGroup>
<AISummaryDialog v-model="aiDialogVisible" :id="userId" v-if="aiDialogVisible"></AISummaryDialog>
</template> </template>
<style lang="scss"> <style lang="scss">
.info-box { .info-box {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论