feat-实现AI洞察功能
This commit is contained in:
@@ -60,3 +60,4 @@ See [Configuration Reference](https://cli.vuejs.org/config/).
|
|||||||
- keepAlive: 是否页面使用 keepAlive 缓存
|
- keepAlive: 是否页面使用 keepAlive 缓存
|
||||||
- shared: 是否页面为分享功能页面,不需要登录即可访问
|
- shared: 是否页面为分享功能页面,不需要登录即可访问
|
||||||
- showAiInspection: 问卷设计是否显示AI质检按钮
|
- showAiInspection: 问卷设计是否显示AI质检按钮
|
||||||
|
- showAiInsight: 数据分析是否显示AI洞察按钮
|
||||||
|
|||||||
17
src/App.vue
17
src/App.vue
@@ -26,12 +26,12 @@
|
|||||||
<script>
|
<script>
|
||||||
import locale from 'ant-design-vue/es/locale/zh_CN';
|
import locale from 'ant-design-vue/es/locale/zh_CN';
|
||||||
import 'moment/locale/zh-cn';
|
import 'moment/locale/zh-cn';
|
||||||
import { watch, onMounted, ref } from 'vue';
|
import { watch, onMounted,onUnmounted, ref } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
import Loading from '@/components/layout/loading/Loading.vue';
|
import Loading from '@/components/layout/loading/Loading.vue';
|
||||||
import useEmitter from '@/composables/useEmitter';
|
import useEmitter from '@/composables/useEmitter';
|
||||||
|
import aiTaskManager from '@/utils/ai/ai-task-manager'; // 引入AI任务管理器
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Loading
|
Loading
|
||||||
@@ -76,13 +76,12 @@ export default {
|
|||||||
// window.localStorage.plantId = data?.id
|
// window.localStorage.plantId = data?.id
|
||||||
// window.localStorage.plantUserInfo = JSON.stringify(data)
|
// window.localStorage.plantUserInfo = JSON.stringify(data)
|
||||||
// }
|
// }
|
||||||
// onMounted(()=>{
|
onMounted(() => {
|
||||||
// router.beforeEach((to, from) => {
|
aiTaskManager.runTasks();
|
||||||
// if(to.path!= "/answer"){
|
});
|
||||||
// getNewToken()
|
onUnmounted(() => {
|
||||||
// }
|
aiTaskManager.saveTasks();
|
||||||
// })
|
});
|
||||||
// })
|
|
||||||
const clickAction = (e) => {
|
const clickAction = (e) => {
|
||||||
if (e.target.getAttribute('data-linkType') === '1') {
|
if (e.target.getAttribute('data-linkType') === '1') {
|
||||||
show.value = true;
|
show.value = true;
|
||||||
|
|||||||
@@ -26,11 +26,12 @@
|
|||||||
></span>
|
></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- add by zhangweiwei 20250331_ai AI 质检 start -->
|
|
||||||
<div class="right" v-if="showAiInspection || showPreview || isRenderBtn || showShare">
|
<div class="right" v-if="showAiInspection || showPreview || isRenderBtn || showShare">
|
||||||
<template v-if="!showLoading">
|
<template v-if="!showLoading">
|
||||||
|
<!-- add by zhangweiwei 20250331_ai AI 质检 start -->
|
||||||
<div v-show="showAiInspection" class="preview-btn" @click="toAiInspection">
|
<div v-show="showAiInspection" class="preview-btn" @click="toAiInspection">
|
||||||
<img class="ai-inspection-btn" :src="require('@/assets/img/ai/ai-inspection-btn.png')" />
|
<img class="ai-btn" :src="require('@/assets/img/ai/ai-inspection-btn.png')" />
|
||||||
</div>
|
</div>
|
||||||
<!-- add by zhangweiwei 20250331_ai AI 质检 end -->
|
<!-- add by zhangweiwei 20250331_ai AI 质检 end -->
|
||||||
<div v-show="showPreview" class="preview-btn" @click="toPreview">
|
<div v-show="showPreview" class="preview-btn" @click="toPreview">
|
||||||
@@ -88,7 +89,12 @@
|
|||||||
</a-button>
|
</a-button>
|
||||||
<!-- <Avatar :onlyUserShow="false" /> -->
|
<!-- <Avatar :onlyUserShow="false" /> -->
|
||||||
</div>
|
</div>
|
||||||
<div class="right" v-if="!showPreview && !isRenderBtn && !showShare">
|
<div class="right" v-if="(!showPreview && !isRenderBtn && !showShare) || showAiInsight">
|
||||||
|
<!-- add by zhangweiwei 20250331_ai AI 洞察 start -->
|
||||||
|
<div v-show="showAiInsight" class="preview-btn" @click="toAiInsight">
|
||||||
|
<img class="ai-btn" :src="require('@/assets/img/ai/data-analyse-ai-insight-btn.png')" />
|
||||||
|
</div>
|
||||||
|
<!-- add by zhangweiwei 20250331_ai AI 洞察 end -->
|
||||||
<a-button
|
<a-button
|
||||||
class="publish-btn share-button"
|
class="publish-btn share-button"
|
||||||
style="display: flex; align-items: center"
|
style="display: flex; align-items: center"
|
||||||
@@ -138,10 +144,7 @@
|
|||||||
<!-- 下载中心 -->
|
<!-- 下载中心 -->
|
||||||
<DownloadCenter v-model:visible="downloadVisible" v-if="downloadVisible"></DownloadCenter>
|
<DownloadCenter v-model:visible="downloadVisible" v-if="downloadVisible"></DownloadCenter>
|
||||||
<!-- add by zhangweiwei 20250331_ai AI 质检 start -->
|
<!-- add by zhangweiwei 20250331_ai AI 质检 start -->
|
||||||
<ai-loading
|
<ai-loading :visible="showAiLoading" :desc="aiLoadingDesc" />
|
||||||
:visible="showAiLoading"
|
|
||||||
desc="我正在为您检查问卷内容,大概需要20-30s,请稍等哦~"
|
|
||||||
/>
|
|
||||||
<!-- add by zhangweiwei 20250331_ai AI 质检 end -->
|
<!-- add by zhangweiwei 20250331_ai AI 质检 end -->
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -160,6 +163,7 @@ import { useRouter, useRoute } from 'vue-router';
|
|||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import useEmitter from '@/composables/useEmitter';
|
import useEmitter from '@/composables/useEmitter';
|
||||||
import { publishSurvey, getSurveyInfo, aiQualityInspection } from '@/api/publish';
|
import { publishSurvey, getSurveyInfo, aiQualityInspection } from '@/api/publish';
|
||||||
|
import { aiInsight } from '@/api/data-analyse';
|
||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
import {
|
import {
|
||||||
ExclamationCircleFilled,
|
ExclamationCircleFilled,
|
||||||
@@ -176,6 +180,8 @@ import { canPlanetPublish } from './utils';
|
|||||||
import { checkShowInsightTab } from '@/views/DataAnalyse/insight/consts';
|
import { checkShowInsightTab } from '@/views/DataAnalyse/insight/consts';
|
||||||
import { text } from 'cheerio/lib/static';
|
import { text } from 'cheerio/lib/static';
|
||||||
import AiLoading from '@/components/layout/loading/AiLoading.vue';
|
import AiLoading from '@/components/layout/loading/AiLoading.vue';
|
||||||
|
import aiTaskManager from '@/utils/ai/ai-task-manager'; // 引入AI任务管理器
|
||||||
|
import { TASK_TYPE, Task } from '@/utils/ai/ai-task';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@@ -244,14 +250,24 @@ const isRenderBtn = computed(() => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
/** add by zhangweiwei AI 质检 start */
|
/** add by zhangweiwei AI 质检/洞察 start */
|
||||||
/**
|
/**
|
||||||
* 是否展示 AI 质检
|
* 是否展示 AI 质检
|
||||||
*/
|
*/
|
||||||
const showAiInspection = computed(() => {
|
const showAiInspection = computed(() => {
|
||||||
return route.matched.some((item) => item.meta?.showAiInspection);
|
return route.matched.some((item) => item.meta?.showAiInspection);
|
||||||
});
|
});
|
||||||
/** add by zhangweiwei AI 质检 end */
|
|
||||||
|
const showAiInsight = computed(() => {
|
||||||
|
return route.matched.some((item) => item.meta?.showAiInsight);
|
||||||
|
});
|
||||||
|
|
||||||
|
const aiLoadingDesc = computed(() => {
|
||||||
|
return showAiInspection.value
|
||||||
|
? '我正在为您检查问卷内容,大概需要20-30s,请稍等哦~'
|
||||||
|
: '正在激活AI显做镜,为您解析问卷背后的情感温度与数据密码,大约需要1-2min,请您先忙,一会记得刷新页面查看结果哦~';
|
||||||
|
});
|
||||||
|
/** add by zhangweiwei AI 质检/洞察 end */
|
||||||
|
|
||||||
const showPreview = computed(() => {
|
const showPreview = computed(() => {
|
||||||
return route.matched.some((item) => item.meta?.showPreview);
|
return route.matched.some((item) => item.meta?.showPreview);
|
||||||
@@ -362,6 +378,40 @@ const toAiInspection = () => {
|
|||||||
};
|
};
|
||||||
/** add by zhangweiwei 20250331_ai AI 质检 end */
|
/** add by zhangweiwei 20250331_ai AI 质检 end */
|
||||||
|
|
||||||
|
/** add by zhangweiwei 20250331_ai AI 洞察 start */
|
||||||
|
/**
|
||||||
|
* AI 洞察
|
||||||
|
*/
|
||||||
|
const toAiInsight = () => {
|
||||||
|
const task = aiTaskManager.findTask(TASK_TYPE.INSIGHT, sn);
|
||||||
|
if (task) {
|
||||||
|
// 已经在AI洞察中
|
||||||
|
message.warn(
|
||||||
|
'正在为您进行AI洞察,这次需要多分析一会儿才能确保洞察精准度,认真工作的我马上带着结果回来哦~'
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
aiInsight(route.query.sn, { sn })
|
||||||
|
.then(() => {
|
||||||
|
showAiLoading.value = true;
|
||||||
|
// 10s后关闭
|
||||||
|
setTimeout(() => {
|
||||||
|
// 开启轮询模式
|
||||||
|
aiTaskManager.addTask(new Task(TASK_TYPE.INSIGHT, sn));
|
||||||
|
showAiLoading.value = false;
|
||||||
|
}, 10000);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
if (error.data?.code === 10015) {
|
||||||
|
// 正在AI洞察中
|
||||||
|
message.warn(error.data?.message);
|
||||||
|
} else {
|
||||||
|
message.error(error.data?.message || '服务器错误');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
/** add by zhangweiwei 20250331_ai AI 洞察 end */
|
||||||
|
|
||||||
const toPreview = async () => {
|
const toPreview = async () => {
|
||||||
var res = await canPlanetPublish(route.query.sn, 1);
|
var res = await canPlanetPublish(route.query.sn, 1);
|
||||||
if (res) {
|
if (res) {
|
||||||
@@ -614,7 +664,7 @@ onUnmounted(() => store.dispatch('polling/stopPollingSurveyState', { sn }));
|
|||||||
.preview-btn:hover {
|
.preview-btn:hover {
|
||||||
color: #70b936;
|
color: #70b936;
|
||||||
}
|
}
|
||||||
.ai-inspection-btn {
|
.ai-btn {
|
||||||
width: 82px;
|
width: 82px;
|
||||||
}
|
}
|
||||||
.publish-btn {
|
.publish-btn {
|
||||||
|
|||||||
@@ -207,4 +207,28 @@ export function cancelAiMark(sn, data) {
|
|||||||
data
|
data
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/** add by zhangweiwei 20250331_ai AI 样本标记 end */
|
/** add by zhangweiwei 20250331_ai AI 样本标记 end */
|
||||||
|
|
||||||
|
/** add by zhangweiwei 20250331_ai AI 洞察 start */
|
||||||
|
/**
|
||||||
|
* 数据分析:AI 洞察
|
||||||
|
* @param {String} sn 问卷编号
|
||||||
|
* @param {Object} data
|
||||||
|
*/
|
||||||
|
export function aiInsight(sn, data) {
|
||||||
|
return request({
|
||||||
|
url: `/console/surveys/${sn}/analysis_insights`,
|
||||||
|
method: 'post',
|
||||||
|
toastError: false,
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function queryAiStatus(sn, data) {
|
||||||
|
return request({
|
||||||
|
url: `/console/surveys/${sn}/status`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/** add by zhangweiwei 20250331_ai AI 洞察 end */
|
||||||
|
|||||||
BIN
src/assets/img/ai/ai-loading-transparent.png
Normal file
BIN
src/assets/img/ai/ai-loading-transparent.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.3 KiB |
BIN
src/assets/img/ai/data-analyse-ai-insight-btn.png
Normal file
BIN
src/assets/img/ai/data-analyse-ai-insight-btn.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.7 KiB |
BIN
src/assets/img/ai/data-analyse-ai-insight-icon.png
Normal file
BIN
src/assets/img/ai/data-analyse-ai-insight-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/img/ai/data-analyse-ai-insight-result.png
Normal file
BIN
src/assets/img/ai/data-analyse-ai-insight-result.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
@@ -157,7 +157,7 @@ const constantRoutes = [
|
|||||||
showPublish: true,
|
showPublish: true,
|
||||||
showDownload: true
|
showDownload: true
|
||||||
},
|
},
|
||||||
/** add by zhangweiwei 20250331_ai AI 质检 新增 showAiInspection属性 end */
|
/** add by zhangweiwei 20250331_ai AI 质检 新增 showAiInspection属性 end */
|
||||||
component: () =>
|
component: () =>
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: "planet" */ '../views/planetDesign/Design/DesignContent.vue'
|
/* webpackChunkName: "planet" */ '../views/planetDesign/Design/DesignContent.vue'
|
||||||
@@ -260,8 +260,9 @@ const constantRoutes = [
|
|||||||
{
|
{
|
||||||
path: 'diagram',
|
path: 'diagram',
|
||||||
name: 'Diagram',
|
name: 'Diagram',
|
||||||
meta: { keepAlive: true, showDownload: true },
|
/** add by zhangweiwei 20250331_ai AI 洞察 新增 showAiInsight 属性 start */
|
||||||
|
meta: { keepAlive: true, showDownload: true, showAiInsight: true },
|
||||||
|
/** add by zhangweiwei 20250331_ai AI 洞察 新增 showAiInsight 属性 start */
|
||||||
component: () =>
|
component: () =>
|
||||||
import(/* webpackChunkName: "analyse" */ '@/views/DataAnalyse/diagram/test')
|
import(/* webpackChunkName: "analyse" */ '@/views/DataAnalyse/diagram/test')
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -18,3 +18,34 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
top: -2px;
|
top: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-notification-notice {
|
||||||
|
padding: 6px 10px 10px !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
background-color: #f9fff5 !important;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
font-size: 14px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-close{
|
||||||
|
top: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-message {
|
||||||
|
margin-left: 22px !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
color: #000000 !important;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
&-with-icon &-description {
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-left: 0 !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
color: #6d6d6d !important;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
45
src/utils/ai/ai-task-manager.js
Normal file
45
src/utils/ai/ai-task-manager.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { ref } from 'vue';
|
||||||
|
export class TaskManager {
|
||||||
|
constructor() {
|
||||||
|
this.tasks = ref(this.loadTasks());
|
||||||
|
}
|
||||||
|
|
||||||
|
loadTasks() {
|
||||||
|
const storedTasks = localStorage.getItem('ai-tasks');
|
||||||
|
return storedTasks ? JSON.parse(storedTasks) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
saveTasks() {
|
||||||
|
localStorage.setItem('tasks', JSON.stringify(this.tasks.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
addTask(task) {
|
||||||
|
this.tasks.value.push(task);
|
||||||
|
this.saveTasks();
|
||||||
|
task.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTask(taskId) {
|
||||||
|
this.tasks.value = this.tasks.value.filter((task) => task.id !== taskId);
|
||||||
|
this.saveTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
findTask(aiType,sn) {
|
||||||
|
return this.tasks.value.find((task) => task.id === `${aiType}-${sn}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
runTasks() {
|
||||||
|
if (this.tasks.value.length === 0) {
|
||||||
|
console.log('No ai tasks to run');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.tasks.value.forEach((task) => {
|
||||||
|
if (task.isRunning) {
|
||||||
|
task.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskManager = new TaskManager();
|
||||||
|
export default taskManager;
|
||||||
89
src/utils/ai/ai-task.js
Normal file
89
src/utils/ai/ai-task.js
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { queryAiStatus } from '@/api/data-analyse'; // 引入查询AI状态的API函数
|
||||||
|
import taskManager from './ai-task-manager'; // 引入任务管理器
|
||||||
|
import { notification } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import InfoIcon from '@/assets/img/ai/data-analyse-ai-insight-icon.png'; // 导入你的自定义图标
|
||||||
|
|
||||||
|
export const TASK_TYPE = {
|
||||||
|
MARK: 'mark',
|
||||||
|
INSIGHT: 'insight'
|
||||||
|
};
|
||||||
|
|
||||||
|
const TYPE_CODE = {
|
||||||
|
[TASK_TYPE.MARK]: 1,
|
||||||
|
[TASK_TYPE.INSIGHT]: 2
|
||||||
|
};
|
||||||
|
|
||||||
|
const TASK_SUCCESS_NOTICE = {
|
||||||
|
[TASK_TYPE.MARK]: 'AI小助手已经完成了数据洞察工作,请前往数据分析-基础分析页面进行查看吧',
|
||||||
|
[TASK_TYPE.INSIGHT]: 'AI小助手已经完成了无效样本标记工作,请前往数据分析-明细数据页面进行查看吧'
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Task {
|
||||||
|
constructor(aiType, sn) {
|
||||||
|
this.id = `${aiType}-${sn}`;
|
||||||
|
this.sn = sn;
|
||||||
|
this.aiType = aiType;
|
||||||
|
this.interval = 15000;
|
||||||
|
this.maxRetries = 3;
|
||||||
|
this.status = null;
|
||||||
|
this.isPolling = true;
|
||||||
|
this.retryCount = 0;
|
||||||
|
this.timer = null;
|
||||||
|
}
|
||||||
|
run() {
|
||||||
|
const poll = async () => {
|
||||||
|
if (this.status === 1 || this.status === 2) {
|
||||||
|
this.stopPolling();
|
||||||
|
}
|
||||||
|
if (!this.isPolling) return;
|
||||||
|
try {
|
||||||
|
// type 1=AI样本标记;2=AI洞察
|
||||||
|
const response = await queryAiStatus(this.sn, {
|
||||||
|
surveySn: this.sn,
|
||||||
|
type: TYPE_CODE[this.aiType]
|
||||||
|
});
|
||||||
|
console.log('response--->', response);
|
||||||
|
// 标记状态:0=标记中,1=标记完成,2=标记失败
|
||||||
|
this.status = response.data?.status;
|
||||||
|
// 如果状态为成功,停止轮询
|
||||||
|
if (this.status === 1 || this.status === 2) {
|
||||||
|
this.stopPolling();
|
||||||
|
taskManager.removeTask(this.id); // 从任务管理器中移除任务
|
||||||
|
this.showNotice(); // 显示通知
|
||||||
|
} else {
|
||||||
|
this.retryCount = 0; // 重置重试计数
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Polling error:', error);
|
||||||
|
// 当发生错误时,增加重试计数
|
||||||
|
this.retryCount++;
|
||||||
|
// 如果超过最大重试次数,停止轮询
|
||||||
|
if (this.retryCount >= this.maxRetries) {
|
||||||
|
console.log('已达到最大重试次数,Stopping polling.');
|
||||||
|
this.stopPolling();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
poll();
|
||||||
|
this.timer = setInterval(() => {
|
||||||
|
poll(); // 定时执行任务
|
||||||
|
}, this.interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
stopPolling() {
|
||||||
|
this.isPolling = false;
|
||||||
|
clearInterval(this.timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
showNotice() {
|
||||||
|
notification.info({
|
||||||
|
message: '提示',
|
||||||
|
duration: null,
|
||||||
|
key: this.aiType,
|
||||||
|
icon: <img src={InfoIcon} alt="" style="width:12px; height: 12px " />,
|
||||||
|
description: TASK_SUCCESS_NOTICE[this.aiType]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/views/DataAnalyse/diagram/components/AiInsightResult.vue
Normal file
46
src/views/DataAnalyse/diagram/components/AiInsightResult.vue
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<!-- add by zhangweiwei 20250331_ai AI 洞察 start -->
|
||||||
|
<template>
|
||||||
|
<div class="ai-insight-item">
|
||||||
|
<div>
|
||||||
|
<img
|
||||||
|
class="ai-insight-item_icon"
|
||||||
|
:src="require('@/assets/img/ai/data-analyse-ai-insight-icon.png')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="ai-insight-item_result" v-html="result"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
result: {
|
||||||
|
type: String,
|
||||||
|
default: () => ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.ai-insight-item {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
padding: 4px 9px;
|
||||||
|
background: linear-gradient(124deg, #fdfaff 0%, #fbf9ff 1%, #f8f9ff 100%);
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
&_icon {
|
||||||
|
width: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&_result {
|
||||||
|
margin-left: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #333333;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<!-- add by zhangweiwei 20250331_ai AI 洞察 end -->
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
<!-- add by zhangweiwei 20250331_ai AI 洞察 start -->
|
||||||
|
<template>
|
||||||
|
<div class="ai-insight" :class="{ 'loading-height': loading }">
|
||||||
|
<img
|
||||||
|
class="ai-insight_icon"
|
||||||
|
:src="require('@/assets/img/ai/data-analyse-ai-insight-result.png')"
|
||||||
|
/>
|
||||||
|
<div v-if="loading" class="ai-insight_loading">
|
||||||
|
<img
|
||||||
|
class="ai-insight_loading--img"
|
||||||
|
:src="require('@/assets/img/ai/ai-loading-transparent.png')"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span class="ai-insight_loading--text">AI洞察加速中...</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="ai-insight_result" v-html="result"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
result: {
|
||||||
|
type: String,
|
||||||
|
default: () => ''
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: () => false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.ai-insight {
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 0 32px 10px;
|
||||||
|
background: linear-gradient(124deg, #fdfaff 0%, #fbf9ff 1%, #f8f9ff 100%);
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
&_icon {
|
||||||
|
width: 38px;
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&_loading {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
top: 36px;
|
||||||
|
bottom: 36px;
|
||||||
|
|
||||||
|
&--img {
|
||||||
|
width: 96px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--text {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #000000;
|
||||||
|
line-height: 17px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&_result {
|
||||||
|
padding: 15px 28px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #333333;
|
||||||
|
line-height: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.loading-height {
|
||||||
|
height: 150px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<!-- add by zhangweiwei 20250331_ai AI 洞察 end -->
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
|
<AiInsightResult
|
||||||
|
v-if="data.question_conclusion"
|
||||||
|
:result="data.question_conclusion"
|
||||||
|
/>
|
||||||
<a-table
|
<a-table
|
||||||
:align="'center'"
|
:align="'center'"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
@@ -37,6 +41,7 @@ import { defineComponent, ref, watch, inject, computed } from 'vue';
|
|||||||
import { getFileExtension } from '@/utils/file';
|
import { getFileExtension } from '@/utils/file';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import { downloadFile } from '@/views/DataAnalyse/composables/downloadFile';
|
import { downloadFile } from '@/views/DataAnalyse/composables/downloadFile';
|
||||||
|
import AiInsightResult from '@/views/DataAnalyse/diagram/components/AiInsightResult';
|
||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { downloadAllFile, downloadFileSheet, getDiagramAnalysis } from '@/api/data-analyse';
|
import { downloadAllFile, downloadFileSheet, getDiagramAnalysis } from '@/api/data-analyse';
|
||||||
@@ -57,7 +62,7 @@ export default defineComponent({
|
|||||||
default: ''
|
default: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: { ImagePreview },
|
components: { ImagePreview, AiInsightResult },
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
|
<AiInsightResult
|
||||||
|
v-if="data.question_conclusion"
|
||||||
|
:result="data.question_conclusion"
|
||||||
|
/>
|
||||||
<a-table
|
<a-table
|
||||||
:align="'center'"
|
:align="'center'"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
@@ -21,6 +25,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, watch, inject } from 'vue';
|
import { computed, ref, watch, inject } from 'vue';
|
||||||
import RenderTableTitle from '@/views/DataAnalyse/components/RenderTableTitle';
|
import RenderTableTitle from '@/views/DataAnalyse/components/RenderTableTitle';
|
||||||
|
import AiInsightResult from '@/views/DataAnalyse/diagram/components/AiInsightResult';
|
||||||
import { getDiagramAnalysis } from '@/api/data-analyse';
|
import { getDiagramAnalysis } from '@/api/data-analyse';
|
||||||
import { convertQueryToString } from '@/utils/httpFormat';
|
import { convertQueryToString } from '@/utils/httpFormat';
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
/>
|
/>
|
||||||
<div class="chart-container">
|
<div class="chart-container">
|
||||||
<ChartAction @update:action="handleUpdateAction" :chartTypeOptions="chartTypeOptions" />
|
<ChartAction @update:action="handleUpdateAction" :chartTypeOptions="chartTypeOptions" />
|
||||||
|
<AiInsightResult
|
||||||
|
v-if="data.question_conclusion"
|
||||||
|
:result="data.question_conclusion"
|
||||||
|
/>
|
||||||
<div class="chart-wrapper">
|
<div class="chart-wrapper">
|
||||||
<pieChart :chart-option="option" ref="chart" />
|
<pieChart :chart-option="option" ref="chart" />
|
||||||
</div>
|
</div>
|
||||||
@@ -39,6 +43,7 @@ import pieChart from '@/components/chart/PieChart';
|
|||||||
import ChartAction from '@/views/DataAnalyse/diagram/components/ChartAction';
|
import ChartAction from '@/views/DataAnalyse/diagram/components/ChartAction';
|
||||||
import RenderTableTitle from '@/views/DataAnalyse/components/RenderTableTitle';
|
import RenderTableTitle from '@/views/DataAnalyse/components/RenderTableTitle';
|
||||||
import useMultiChartOption from '@/views/DataAnalyse/diagram/composables/useMultiChartOption';
|
import useMultiChartOption from '@/views/DataAnalyse/diagram/composables/useMultiChartOption';
|
||||||
|
import AiInsightResult from '@/views/DataAnalyse/diagram/components/AiInsightResult';
|
||||||
import { generateTableCustomRender } from './renderTableGroup';
|
import { generateTableCustomRender } from './renderTableGroup';
|
||||||
|
|
||||||
function clearData(data) {
|
function clearData(data) {
|
||||||
@@ -68,7 +73,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
RenderTableTitle,
|
RenderTableTitle,
|
||||||
ChartAction
|
ChartAction,
|
||||||
|
AiInsightResult
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const chartInstance = inject('chartInstance');
|
const chartInstance = inject('chartInstance');
|
||||||
@@ -379,7 +385,8 @@ export default defineComponent({
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.chart-container {
|
.chart-container {
|
||||||
height: 300px;
|
min-height: 300px;
|
||||||
|
padding-bottom: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="chart-container">
|
<div class="chart-container">
|
||||||
<ChartAction @update:action="handleUpdateAction" :chartTypeOptions="chartTypeOptions" />
|
<ChartAction @update:action="handleUpdateAction" :chartTypeOptions="chartTypeOptions" />
|
||||||
|
<AiInsightResult
|
||||||
|
v-if="data.question_conclusion"
|
||||||
|
:result="data.question_conclusion"
|
||||||
|
/>
|
||||||
<div class="chart-wrapper">
|
<div class="chart-wrapper">
|
||||||
<pieChart :chart-option="option" ref="chart" />
|
<pieChart :chart-option="option" ref="chart" />
|
||||||
</div>
|
</div>
|
||||||
@@ -33,6 +37,7 @@ import ChartAction from '@/views/DataAnalyse/diagram/components/ChartAction';
|
|||||||
import RenderTableTitle from '@/views/DataAnalyse/components/RenderTableTitle';
|
import RenderTableTitle from '@/views/DataAnalyse/components/RenderTableTitle';
|
||||||
import RenderTableTitleLo from '@/views/DataAnalyse/components/RenderTableTitleLo';
|
import RenderTableTitleLo from '@/views/DataAnalyse/components/RenderTableTitleLo';
|
||||||
import useChartOption from '@/views/DataAnalyse/diagram/composables/useChartOption';
|
import useChartOption from '@/views/DataAnalyse/diagram/composables/useChartOption';
|
||||||
|
import AiInsightResult from '@/views/DataAnalyse/diagram/components/AiInsightResult';
|
||||||
import * as cheerio from 'cheerio';
|
import * as cheerio from 'cheerio';
|
||||||
import { generateTableCustomRender } from './renderTableGroup';
|
import { generateTableCustomRender } from './renderTableGroup';
|
||||||
|
|
||||||
@@ -77,7 +82,8 @@ export default defineComponent({
|
|||||||
components: {
|
components: {
|
||||||
RenderTableTitle,
|
RenderTableTitle,
|
||||||
ChartAction,
|
ChartAction,
|
||||||
pieChart
|
pieChart,
|
||||||
|
AiInsightResult
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const chartInstance = inject('chartInstance');
|
const chartInstance = inject('chartInstance');
|
||||||
@@ -211,7 +217,8 @@ export default defineComponent({
|
|||||||
.container {
|
.container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
.chart-container {
|
.chart-container {
|
||||||
height: 300px;
|
min-height: 300px;
|
||||||
|
padding-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-container {
|
.table-container {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import Header from '@/views/DataAnalyse/diagram/components/Layouts/Header';
|
|||||||
import Main from '@/views/DataAnalyse/diagram/components/Layouts/Main';
|
import Main from '@/views/DataAnalyse/diagram/components/Layouts/Main';
|
||||||
import Footer from '@/views/DataAnalyse/diagram/components/Layouts/Footer';
|
import Footer from '@/views/DataAnalyse/diagram/components/Layouts/Footer';
|
||||||
import useQuestionInfo from '@/views/DataAnalyse/diagram/composables/useQuestionInfo';
|
import useQuestionInfo from '@/views/DataAnalyse/diagram/composables/useQuestionInfo';
|
||||||
|
import AiInsightResult from '@/views/DataAnalyse/diagram/components/AiInsightResult';
|
||||||
import spliceChart from '../../components/spliceChart';
|
import spliceChart from '../../components/spliceChart';
|
||||||
import generatorTable from './generatorTable';
|
import generatorTable from './generatorTable';
|
||||||
import { exportChart } from '@/api/data-analyse';
|
import { exportChart } from '@/api/data-analyse';
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="chart-container">
|
<div class="chart-container">
|
||||||
<ChartAction @update:action="handleUpdateAction" :chartTypeOptions="chartTypeOptions" />
|
<ChartAction @update:action="handleUpdateAction" :chartTypeOptions="chartTypeOptions" />
|
||||||
|
<AiInsightResult v-if="data.question_conclusion" :result="data.question_conclusion" />
|
||||||
<div class="chart-wrapper">
|
<div class="chart-wrapper">
|
||||||
<pieChart :chart-option="option" ref="chart" />
|
<pieChart :chart-option="option" ref="chart" />
|
||||||
</div>
|
</div>
|
||||||
@@ -31,6 +32,7 @@ import pieChart from '@/components/chart/PieChart';
|
|||||||
import ChartAction from '@/views/DataAnalyse/diagram/components/ChartAction';
|
import ChartAction from '@/views/DataAnalyse/diagram/components/ChartAction';
|
||||||
import RenderTableTitle from '@/views/DataAnalyse/components/RenderTableTitle';
|
import RenderTableTitle from '@/views/DataAnalyse/components/RenderTableTitle';
|
||||||
import useConstantOption from '@/views/DataAnalyse/diagram/composables/useConstantOption';
|
import useConstantOption from '@/views/DataAnalyse/diagram/composables/useConstantOption';
|
||||||
|
import AiInsightResult from '@/views/DataAnalyse/diagram/components/AiInsightResult';
|
||||||
|
|
||||||
function clearData(data) {
|
function clearData(data) {
|
||||||
return data.filter((item) => item.avg !== '0.00');
|
return data.filter((item) => item.avg !== '0.00');
|
||||||
@@ -49,7 +51,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
RenderTableTitle,
|
RenderTableTitle,
|
||||||
ChartAction
|
ChartAction,
|
||||||
|
AiInsightResult,
|
||||||
// pieChart,
|
// pieChart,
|
||||||
},
|
},
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
@@ -168,7 +171,8 @@ export default defineComponent({
|
|||||||
.container {
|
.container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
.chart-container {
|
.chart-container {
|
||||||
height: 300px;
|
min-height: 300px;
|
||||||
|
padding-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-container {
|
.table-container {
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<AiInsightResult v-if="data.question_conclusion" :result="data.question_conclusion" />
|
||||||
<div id="location-map" class="location-map" ref="locationMap"></div>
|
<div id="location-map" class="location-map" ref="locationMap"></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent, ref, onMounted, watchEffect, computed } from 'vue';
|
import { defineComponent, ref, onMounted, watchEffect, computed } from 'vue';
|
||||||
|
import AiInsightResult from '@/views/DataAnalyse/diagram/components/AiInsightResult';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -13,6 +15,9 @@ export default defineComponent({
|
|||||||
default: () => {}
|
default: () => {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
components: {
|
||||||
|
AiInsightResult
|
||||||
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
const map = ref(null);
|
const map = ref(null);
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
<div style="height: 32px">
|
<div style="height: 32px">
|
||||||
<CascaderSelect :data="data.optionAll" :max="3" @onOptionIndexChange="getData"></CascaderSelect>
|
<CascaderSelect :data="data.optionAll" :max="3" @onOptionIndexChange="getData"></CascaderSelect>
|
||||||
</div>
|
</div>
|
||||||
|
<AiInsightResult
|
||||||
|
v-if="data.question_conclusion"
|
||||||
|
:result="data.question_conclusion"
|
||||||
|
/>
|
||||||
<pieChart :chart-option="option" ref="chart" />
|
<pieChart :chart-option="option" ref="chart" />
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<a-table
|
<a-table
|
||||||
@@ -32,6 +36,7 @@ import { useRoute } from 'vue-router';
|
|||||||
import useChartOption from '@/views/DataAnalyse/diagram/composables/useChartOption';
|
import useChartOption from '@/views/DataAnalyse/diagram/composables/useChartOption';
|
||||||
import RenderTableTitle from '@/views/DataAnalyse/components/RenderTableTitle';
|
import RenderTableTitle from '@/views/DataAnalyse/components/RenderTableTitle';
|
||||||
import RenderTableTitleLo from '@/views/DataAnalyse/components/RenderTableTitleLo';
|
import RenderTableTitleLo from '@/views/DataAnalyse/components/RenderTableTitleLo';
|
||||||
|
import AiInsightResult from '@/views/DataAnalyse/diagram/components/AiInsightResult';
|
||||||
|
|
||||||
function clearData(data) {
|
function clearData(data) {
|
||||||
return data.filter((item) => item.number !== 0);
|
return data.filter((item) => item.number !== 0);
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
<div style="height: 32px">
|
<div style="height: 32px">
|
||||||
<CascaderSelect :data="university" :max="2" @onOptionIndexChange="getData"></CascaderSelect>
|
<CascaderSelect :data="university" :max="2" @onOptionIndexChange="getData"></CascaderSelect>
|
||||||
</div>
|
</div>
|
||||||
|
<AiInsightResult
|
||||||
|
v-if="data.question_conclusion"
|
||||||
|
:result="data.question_conclusion"
|
||||||
|
/>
|
||||||
<pieChart :chart-option="option" ref="chart" />
|
<pieChart :chart-option="option" ref="chart" />
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<a-table
|
<a-table
|
||||||
@@ -33,6 +37,7 @@ import useChartOption from '@/views/DataAnalyse/diagram/composables/useChartOpti
|
|||||||
import RenderTableTitle from '@/views/DataAnalyse/components/RenderTableTitle';
|
import RenderTableTitle from '@/views/DataAnalyse/components/RenderTableTitle';
|
||||||
import RenderTableTitleLo from '@/views/DataAnalyse/components/RenderTableTitleLo';
|
import RenderTableTitleLo from '@/views/DataAnalyse/components/RenderTableTitleLo';
|
||||||
import university from '@/views/Answer/questions/json/university.json';
|
import university from '@/views/Answer/questions/json/university.json';
|
||||||
|
import AiInsightResult from '@/views/DataAnalyse/diagram/components/AiInsightResult';
|
||||||
|
|
||||||
function clearData(data) {
|
function clearData(data) {
|
||||||
return data.filter((item) => item.number !== 0);
|
return data.filter((item) => item.number !== 0);
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
<div style="height: 32px">
|
<div style="height: 32px">
|
||||||
<CascaderSelect :data="major" :max="1" @onOptionIndexChange="getData"></CascaderSelect>
|
<CascaderSelect :data="major" :max="1" @onOptionIndexChange="getData"></CascaderSelect>
|
||||||
</div>
|
</div>
|
||||||
|
<AiInsightResult
|
||||||
|
v-if="data.question_conclusion"
|
||||||
|
:result="data.question_conclusion"
|
||||||
|
/>
|
||||||
<pieChart :chart-option="option" ref="chart" />
|
<pieChart :chart-option="option" ref="chart" />
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<a-table
|
<a-table
|
||||||
@@ -33,6 +37,7 @@ import useChartOption from '@/views/DataAnalyse/diagram/composables/useChartOpti
|
|||||||
import RenderTableTitle from '@/views/DataAnalyse/components/RenderTableTitle';
|
import RenderTableTitle from '@/views/DataAnalyse/components/RenderTableTitle';
|
||||||
import RenderTableTitleLo from '@/views/DataAnalyse/components/RenderTableTitleLo';
|
import RenderTableTitleLo from '@/views/DataAnalyse/components/RenderTableTitleLo';
|
||||||
import major from '@/views/Answer/questions/json/major.json';
|
import major from '@/views/Answer/questions/json/major.json';
|
||||||
|
import AiInsightResult from '@/views/DataAnalyse/diagram/components/AiInsightResult';
|
||||||
|
|
||||||
function clearData(data) {
|
function clearData(data) {
|
||||||
return data.filter((item) => item.number !== 0);
|
return data.filter((item) => item.number !== 0);
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
<div style="height: 32px">
|
<div style="height: 32px">
|
||||||
<CascaderSelect :data="cityes" :max="1" @onOptionIndexChange="getData"></CascaderSelect>
|
<CascaderSelect :data="cityes" :max="1" @onOptionIndexChange="getData"></CascaderSelect>
|
||||||
</div>
|
</div>
|
||||||
|
<AiInsightResult
|
||||||
|
v-if="data.question_conclusion"
|
||||||
|
:result="data.question_conclusion"
|
||||||
|
/>
|
||||||
<pieChart :chart-option="option" ref="chart" />
|
<pieChart :chart-option="option" ref="chart" />
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<a-table
|
<a-table
|
||||||
@@ -33,6 +37,7 @@ import useChartOption from '@/views/DataAnalyse/diagram/composables/useChartOpti
|
|||||||
import RenderTableTitle from '@/views/DataAnalyse/components/RenderTableTitle';
|
import RenderTableTitle from '@/views/DataAnalyse/components/RenderTableTitle';
|
||||||
import RenderTableTitleLo from '@/views/DataAnalyse/components/RenderTableTitleLo';
|
import RenderTableTitleLo from '@/views/DataAnalyse/components/RenderTableTitleLo';
|
||||||
import cityes from '@/views/Answer/questions/json/cityes.json';
|
import cityes from '@/views/Answer/questions/json/cityes.json';
|
||||||
|
import AiInsightResult from '@/views/DataAnalyse/diagram/components/AiInsightResult';
|
||||||
|
|
||||||
function clearData(data) {
|
function clearData(data) {
|
||||||
return data.filter((item) => item.number !== 0);
|
return data.filter((item) => item.number !== 0);
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
<div style="height: 32px">
|
<div style="height: 32px">
|
||||||
<CascaderSelect :data="cityes" :max="2" @onOptionIndexChange="getData"></CascaderSelect>
|
<CascaderSelect :data="cityes" :max="2" @onOptionIndexChange="getData"></CascaderSelect>
|
||||||
</div>
|
</div>
|
||||||
|
<AiInsightResult
|
||||||
|
v-if="data.question_conclusion"
|
||||||
|
:result="data.question_conclusion"
|
||||||
|
/>
|
||||||
<pieChart :chart-option="option" ref="chart" />
|
<pieChart :chart-option="option" ref="chart" />
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<a-table
|
<a-table
|
||||||
@@ -33,6 +37,7 @@ import useChartOption from '@/views/DataAnalyse/diagram/composables/useChartOpti
|
|||||||
import RenderTableTitle from '@/views/DataAnalyse/components/RenderTableTitle';
|
import RenderTableTitle from '@/views/DataAnalyse/components/RenderTableTitle';
|
||||||
import RenderTableTitleLo from '@/views/DataAnalyse/components/RenderTableTitleLo';
|
import RenderTableTitleLo from '@/views/DataAnalyse/components/RenderTableTitleLo';
|
||||||
import cityes from '@/views/Answer/questions/json/cityes.json';
|
import cityes from '@/views/Answer/questions/json/cityes.json';
|
||||||
|
import AiInsightResult from '@/views/DataAnalyse/diagram/components/AiInsightResult';
|
||||||
|
|
||||||
function clearData(data) {
|
function clearData(data) {
|
||||||
return data.filter((item) => item.number !== 0);
|
return data.filter((item) => item.number !== 0);
|
||||||
|
|||||||
@@ -32,6 +32,13 @@
|
|||||||
></newSearch>
|
></newSearch>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="diagramList.length > 0">
|
<div v-if="diagramList.length > 0">
|
||||||
|
<!-- add by zhangweiwei 20250331_ai AI 洞察 start -->
|
||||||
|
<ai-insight-result-overall
|
||||||
|
v-if="aiInsightResult || (typeof aiInsightLoading !== 'undefined')"
|
||||||
|
:result="aiInsightResult"
|
||||||
|
:loading="aiInsightLoading"
|
||||||
|
/>
|
||||||
|
<!-- add by zhangweiwei 20250331_ai AI 洞察 end -->
|
||||||
<div class="diagram-container">
|
<div class="diagram-container">
|
||||||
<BasicDiagramItem
|
<BasicDiagramItem
|
||||||
@remove-chart="onRemove"
|
@remove-chart="onRemove"
|
||||||
@@ -75,21 +82,25 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import 'element-plus/theme-chalk/el-loading.css';
|
import 'element-plus/theme-chalk/el-loading.css';
|
||||||
import { onMounted, ref, computed, provide, nextTick } from 'vue';
|
import { onMounted, ref, computed, provide, nextTick, watch } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import BasicDiagramItem from '@/views/DataAnalyse/diagram/components/diagram-item-new.vue';
|
import BasicDiagramItem from '@/views/DataAnalyse/diagram/components/diagram-item-new.vue';
|
||||||
import SubTitle from '../components/SubTitle.vue';
|
import SubTitle from '../components/SubTitle.vue';
|
||||||
import search from '../components/diagram/search.vue';
|
import search from '../components/diagram/search.vue';
|
||||||
|
import AiInsightResultOverall from '@views/DataAnalyse/diagram/components/AiInsightResultOverall.vue';
|
||||||
import FullScreenModal from '@views/DataAnalyse/diagram/components/FullScreenModal';
|
import FullScreenModal from '@views/DataAnalyse/diagram/components/FullScreenModal';
|
||||||
import newSearch from '@/views/DataAnalyse/components/diagram/newSearch';
|
import newSearch from '@/views/DataAnalyse/components/diagram/newSearch';
|
||||||
|
|
||||||
import { getDiagramAnalysis } from '@/api/data-analyse';
|
import { aiInsight, getDiagramAnalysis } from '@/api/data-analyse';
|
||||||
import { getAuthRoles } from '@/api/role';
|
import { getAuthRoles } from '@/api/role';
|
||||||
import { convertQueryToString } from '@/utils/httpFormat';
|
import { convertQueryToString } from '@/utils/httpFormat';
|
||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
import useEmitter from '@/composables/useEmitter';
|
import useEmitter from '@/composables/useEmitter';
|
||||||
import { chartList } from '@/views/DataAnalyse/diagram/composables/useInstance';
|
import { chartList } from '@/views/DataAnalyse/diagram/composables/useInstance';
|
||||||
import newModal from '@/views/DataAnalyse/components/diagram/newModal';
|
import newModal from '@/views/DataAnalyse/components/diagram/newModal';
|
||||||
|
import taskManager from '@/utils/ai/ai-task-manager';
|
||||||
|
import { TASK_TYPE } from '@/utils/ai/ai-task';
|
||||||
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
const emitter = useEmitter();
|
const emitter = useEmitter();
|
||||||
|
|
||||||
@@ -107,6 +118,12 @@ provide('exportList', exportList);
|
|||||||
|
|
||||||
const diagramItem = ref(null);
|
const diagramItem = ref(null);
|
||||||
|
|
||||||
|
/** add by zhangweiwei 20250331_ai AI 洞察 start */
|
||||||
|
const aiInsightResult = ref('');
|
||||||
|
const aiInsightLoading = ref(undefined);
|
||||||
|
// const tasks = taskManager.tasks.value;
|
||||||
|
/** add by zhangweiwei 20250331_ai AI 洞察 end */
|
||||||
|
|
||||||
function getPermissionInfo() {
|
function getPermissionInfo() {
|
||||||
getAuthRoles({
|
getAuthRoles({
|
||||||
flag: 2,
|
flag: 2,
|
||||||
@@ -167,6 +184,7 @@ function getDiagramData() {
|
|||||||
total.value = _total;
|
total.value = _total;
|
||||||
per_page.value = res.meta.per_page;
|
per_page.value = res.meta.per_page;
|
||||||
dataCount.value = res.other.answerCount;
|
dataCount.value = res.other.answerCount;
|
||||||
|
aiInsightResult.value = res.other.overall_conclusion;
|
||||||
hasElementAfterRemove.value = _total === 0 ? true : false;
|
hasElementAfterRemove.value = _total === 0 ? true : false;
|
||||||
|
|
||||||
let response_data = res.data
|
let response_data = res.data
|
||||||
@@ -347,6 +365,18 @@ onMounted(async () => {
|
|||||||
spinning.value = false;
|
spinning.value = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => taskManager.tasks.value,
|
||||||
|
(newTasks) => {
|
||||||
|
console.log('任务列表发生变化:', newTasks);
|
||||||
|
aiInsightLoading.value = taskManager.findTask(TASK_TYPE.INSIGHT, sn)?.isPolling;
|
||||||
|
if (aiInsightLoading.value) {
|
||||||
|
aiInsightResult.value = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true }
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@@ -648,17 +648,14 @@ function aiMark(sn) {
|
|||||||
aiSampleMark(sn, {
|
aiSampleMark(sn, {
|
||||||
surveySn: sn
|
surveySn: sn
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then(() => {
|
||||||
showAiLoading.value = true;
|
showAiLoading.value = true;
|
||||||
// 5s后关闭
|
// 10s后关闭
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
showAiLoading.value = false;
|
showAiLoading.value = false;
|
||||||
// getData();
|
|
||||||
// resetSelection();
|
|
||||||
}, 10000);
|
}, 10000);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('----->', error);
|
|
||||||
if (error.data?.code === 10014) {
|
if (error.data?.code === 10014) {
|
||||||
// 正在AI标记中
|
// 正在AI标记中
|
||||||
message.warn(error.data?.message);
|
message.warn(error.data?.message);
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
<!-- add by zhangweiwei 20250331_ai AI 质检 新增 质检结果组件 start -->
|
<!-- add by zhangweiwei 20250331_ai AI 质检 新增 质检结果组件 start -->
|
||||||
<template>
|
<template>
|
||||||
<div class="ai-inspection-result">
|
<div class="ai-inspection-result">
|
||||||
<img :src="require('@/assets/img/ai/ai-inspection-result-icon.png')" class="result-icon" />
|
<div>
|
||||||
|
<img class="result-icon" :src="require('@/assets/img/ai/ai-inspection-result-icon.png')" />
|
||||||
|
</div>
|
||||||
<div class="result-wrap">
|
<div class="result-wrap">
|
||||||
<div class="result-item" v-for="(advice, index) in result" :key="index">
|
<div class="result-item" v-for="(advice, index) in result" :key="index">
|
||||||
<span class="result-item__index" v-if="result.length > 1">{{ index + 1 }}</span>
|
<span class="result-item__index" v-if="result.length > 1">{{ index + 1 }}</span>
|
||||||
@@ -35,7 +36,6 @@ const props = defineProps({
|
|||||||
.result-icon {
|
.result-icon {
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
margin-top: 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-wrap {
|
.result-wrap {
|
||||||
|
|||||||
Reference in New Issue
Block a user