Files
ylst-pc/src/Layouts/BaseLayout.vue
2025-03-11 16:59:55 +08:00

799 lines
21 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>
<div class="header">
<div class="left">
<i class="iconfont icon-xiangzuo-moren" @click="backHome"></i>
<a-tooltip placement="right">
<template #title>
{{ project_name }}
</template>
<div class="question-name">{{ project_name }}</div>
</a-tooltip>
</div>
<div class="tab-container">
<div
class="tab-item"
style="font-size: 18px"
v-for="(tab, index) in tabs"
:key="index"
:class="[tab.link === currentPath ? 'active' : '']"
@click="toPage(tab.link, tab.title)"
>
<span :class="tab.link === currentPath ? 'click' : 'click2'">{{ index + 1 }}</span>
<span style="font-size: 16px">{{ tab.title }}</span>
<span
v-if="index != 3"
style="width: 27px; margin: 0 10px; border-top: 1px dashed #c4c0c0"
></span>
</div>
</div>
<!-- add by zhangweiwei 20250331_ai AI 质检 start -->
<div class="right" v-if="showAiInspection || showPreview || isRenderBtn || showShare">
<template v-if="!showLoading">
<div v-show="showAiInspection" class="preview-btn" @click="toAiInspection">
<img class="ai-inspection-btn" :src="require('@/assets/img/ai/ai-inspection-btn.png')" />
</div>
<!-- add by zhangweiwei 20250331_ai AI 质检 end -->
<div v-show="showPreview" class="preview-btn" @click="toPreview">
<i class="iconfont icon-yulan"></i>
<span style="margin-left: 6px; width: 29px">预览</span>
</div>
<a-button
class="publish-btn share-button"
style="display: flex; align-items: center"
@click.stop="clickEntrance"
>
<template #icon>
<i class="iconfont icon-fenxiang2 mr-6"></i>
<!-- <img class="download_img"
:src="require('@/assets/img/fenxiang.png')" /> -->
</template>
分享
</a-button>
</template>
<template v-else>
<div style="display: flex; align-items: center">
<a-spin size="small" style="padding-left: 40px" />
<span style="margin-left: 10px; width: 90px">正在保存中...</span>
</div>
</template>
<a-button
v-show="!showPublish && isRenderBtn"
v-if="!showLoading && isPermissionShowPublish"
type="primary"
class="publish-btn"
style="display: flex; align-items: center"
@click="openPublishModal"
>
<template #icon>
<i class="iconfont icon-fabu3 mr-6"></i>
<!-- <img class="download_img"
:src="require('@/assets/img/fabu.png')" /> -->
</template>
投放
</a-button>
<a-button
type="primary"
class="download-btn share-button"
style="display: flex; align-items: center"
@click.stop="toDownload"
v-if="showDownload && showxiazai"
>
<template #icon>
<!-- <img class="download_img"
:src="require('@/assets/img/download_center.png')" /> -->
<i class="iconfont icon-xiazaiqi mr-6"></i>
</template>
下载中心
</a-button>
<!-- <Avatar :onlyUserShow="false" /> -->
</div>
<div class="right" v-if="!showPreview && !isRenderBtn && !showShare">
<a-button
class="publish-btn share-button"
style="display: flex; align-items: center"
@click.stop="clickEntrance"
>
<template #icon>
<i class="iconfont icon-fenxiang2 mr-6"></i>
<!-- <img class="download_img"
:src="require('@/assets/img/fenxiang.png')" /> -->
</template>
分享
</a-button>
<a-button
type="primary"
class="download-btn share-button"
style="display: flex; align-items: center"
@click.stop="toDownload"
v-if="showDownload"
>
<template #icon>
<!-- <img class="download_img"
:src="require('@/assets/img/download_center.png')" /> -->
<i class="iconfont icon-xiazaiqi mr-6"></i>
</template>
下载中心
</a-button>
<!-- <Avatar :onlyUserShow="false" /> -->
</div>
</div>
<Group ref="groupInfo" :visible="visibleShare" :sn="sn" @backHome="backHome"></Group>
<div
class="big_box"
@click.stop="visibleShare = false"
v-show="visibleShare"
style="z-index: 1"
></div>
<a-modal v-model:visible="visible" @ok="handlePublish">
<div class="content">
<div class="tips">
<div class="iconfont icon-tixing"></div>
<div>提示!</div>
</div>
<div class="text">确定投放该问卷吗</div>
</div>
</a-modal>
<!-- 下载中心 -->
<DownloadCenter v-model:visible="downloadVisible" v-if="downloadVisible"></DownloadCenter>
<!-- add by zhangweiwei 20250331_ai AI 质检 start -->
<ai-loading
:visible="showAiLoading"
desc="我正在为您分析问卷内容,这个过程可能会多花一点点时间,不过马上就好,稍等哦~"
/>
<!-- add by zhangweiwei 20250331_ai AI 质检 end -->
</template>
<script setup>
import {
ref,
computed,
onMounted,
onBeforeUnmount,
onUnmounted,
createVNode,
nextTick,
watch
} from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { message } from 'ant-design-vue';
import useEmitter from '@/composables/useEmitter';
import { publishSurvey, getSurveyInfo } from '@/api/publish';
import { aiQualityInspection } from '@/api/ai';
import { useStore } from 'vuex';
import {
ExclamationCircleFilled,
ShareAltOutlined,
ExclamationCircleOutlined
} from '@ant-design/icons-vue';
import Group from './components/Group/Group.vue';
import Avatar from '@/views/Home/components/Avatar.vue';
import { tr } from 'element-plus/lib/locale';
import { Modal } from 'ant-design-vue';
import { cloneDeep } from 'lodash';
import DownloadCenter from '@/views/DownloadCenter/index.vue';
import { canPlanetPublish } from './utils';
import { checkShowInsightTab } from '@/views/DataAnalyse/insight/consts';
import { text } from 'cheerio/lib/static';
import AiLoading from '@/components/layout/loading/AiLoading.vue';
const router = useRouter();
const route = useRoute();
const store = useStore();
const emitter = useEmitter();
const publish_type = 0;
const sn = route.query.sn;
const tabs = ref([
{
title: '问卷设计',
link: '/survey/planet'
},
{
title: '问卷投放',
link: '/survey/publish'
},
{
title: '执行进度',
link: '/survey/schedule/recycle'
},
{
title: '数据分析',
link: '/survey/analyse'
}
]);
const getParentNode = () => {
return document.querySelector('.header');
};
const visible = ref(false);
const showAiLoading = ref(false);
const project_name = ref('');
const currentPath = computed(() => {
const matches = route.matched.map((item) => item.path);
if (!matches) {
return '';
}
let paths = cloneDeep(matches);
const links = tabs.value.map((item) => item.link);
let nearestPath = '';
while (paths.length > 0) {
const pop = paths.pop();
if (links.includes(pop)) {
nearestPath = pop;
}
}
return nearestPath;
});
const surveyInfo = ref({});
const isPermissionShowPublish = computed(() => surveyInfo.value.role_id !== 23);
const toJump = (url) => {
router.push(url);
};
const toTeamCenter = () => {
toJump('/team-manage/team-center');
};
const isRenderBtn = computed(() => {
// return currentPath.value.includes('design')
return (
route.matched.some((item) => item.meta?.showPublish)
&& (store.state.common.surveyStatus !== 1 || route.path !== '/survey/planet/answer-setting')
);
});
/** add by zhangweiwei AI 质检 start */
/**
* 是否展示 AI 质检
*/
const showAiInspection = computed(() => {
return route.matched.some((item) => item.meta?.showAiInspection);
});
/** add by zhangweiwei AI 质检 end */
const showPreview = computed(() => {
return route.matched.some((item) => item.meta?.showPreview);
});
const showDownload = computed(() => {
return route.matched.some((item) => item.meta?.showDownload);
});
const showShare = computed(() => {
return route.matched.some((item) => item.meta?.showShare);
});
const showPublish = computed(() => {
return store.state.common.surveyStatus;
});
const showLoading = computed(() => {
const sn = route.query.sn;
const isSurvey = store.state.polling.surveyStatus?.[sn] === 0;
const questionStatus = store.state.polling.questionStatus?.[sn] || {};
const isQuestion = Object.keys(questionStatus).some((key) => questionStatus[key] === 0);
return isSurvey || isQuestion;
});
const toPage = (path, title) => {
console.log(path, title);
if (
surveyInfo.value.team_role_id !== 28
&& surveyInfo.value.role_id === 23
&& ['/survey/publish', '/survey/schedule/recycle', '/survey/analyse'].includes(path)
) {
message.error('您没有此操作权限');
return;
}
showxiazai.value = null;
if (path !== '/survey/planet' && path !== '/survey/schedule/recycle') {
showxiazai.value = true;
}
if (
path.startsWith('/survey/analyse')
&& checkShowInsightTab({ templateType: store.state.common.surveyInfo?.template_type })
) {
path = '/survey/analyse/insight';
}
router.push({
path,
query: route.query
});
document.title = title;
};
/** add by zhangweiwei 20250331_ai AI 质检 start */
/**
* AI 质检
*/
const toAiInspection = () => {
const questionInfo = store.state.common.questionInfo;
console.log('questions', questionInfo.questions);
if (!questionInfo.questions || questionInfo.questions.length === 0) {
// 未设计问卷,不允许质检
message.error('您还没添加任何问题请添加后再进行AI质检。');
return;
}
const questions = questionInfo.questions;
showAiLoading.value = true;
aiQualityInspection(route.query.sn, { sn })
.then((res) => {
if (!res || !res.code === 0 || !res.data) {
// 质检接口调用失败
message.error(res.message ? res.message : 'AI问卷质检失败请稍后再试');
return;
}
// 1. 检查质检结果
const resultList = res.data.queResultList;
if (!resultList || resultList.length === 0) {
// 2. 无质检结果,清空问题列表中的质检结果
questions.forEach((question) => {
question.ai_inspection_result = null;
});
store.commit('common/M_COMMON_SET_QUESTION_INFO', questionInfo);
message.success('AI问卷质检完成');
return;
}
// 3. 有质检结果,先清空质检结果,再将质检结果合并到问题列表中
const questionMap = {};
questions.forEach((question) => {
question.ai_inspection_result = null;
questionMap[question.title] = question;
});
resultList.forEach((result) => {
const question = questionMap[result.title];
if (question) {
// 有匹配上的问题,将质检结果合并到问题中
question.ai_inspection_result = result.advice;
}
});
store.commit('common/M_COMMON_SET_QUESTION_INFO', questionInfo);
message.success('AI问卷质检完成');
})
.finally(() => {
showAiLoading.value = false;
});
};
/** add by zhangweiwei 20250331_ai AI 质检 end */
const toPreview = async () => {
var res = await canPlanetPublish(route.query.sn, 1);
if (res) {
router.push({
path: '/preview',
query: route.query
});
document.title = name;
}
};
const openPublishModal = async () => {
emitter.emit('app-loading', { visible: true, description: '正在生成问卷...请您稍候~' });
var res = await canPlanetPublish(route.query.sn);
if (res) {
// visible.value = true;
handlePublish();
return;
} else {
emitter.emit('app-loading', false);
}
// //console.log();
// // return;
// let isFb = true;
// let content = "";
// const qSteams = [];
// let type = 0;
// let title = "推荐提示";
// store.state.common.questionInfo.questions &&
// store.state.common.questionInfo.questions.forEach((s) => {
// if (s.question_type === 105 && s.config.design_version <= 0) {
// isFb = false;
// content = createVNode(<div>maxdiff未生成设计请先进行生成设计才能进行投放</div>);
// title = "推荐提示";
// type = 1;
// }
// if (s.question_type === 25 || s.question_type === 26) {
// if ((s.options?.[0]?.length || 0) <= 0 && (s?.associate?.length || 0) <= 0) {
// isFb = false;
// qSteams.push(`(${s.title})`);
// type = 2;
// }
// }
// });
// if (isFb === true) {
// visible.value = true;
// } else {
// if (type === 2) {
// const titleStr = qSteams.join(",");
// title = "添加选区";
// content = createVNode(<div>{titleStr} 未添加选区,请添加选区后进行投放</div>);
// }
// Modal.confirm({
// title: title,
// icon: createVNode(ExclamationCircleOutlined),
// content: content,
// onOk () { },
// width: "640px",
// height: "364px",
// onCancel () { },
// class: "test",
// });
// }
};
const handlePublish = () => {
publishSurvey({
sn,
publish_type
})
.then((res) => {
visible.value = false;
store.commit('common/M_COMMON_SET_SURVEY_STATUS', 1);
router.push({
path: '/survey/publish',
query: {
sn,
is_publish: true //添加标识
}
});
})
.finally(() => {
emitter.emit('app-loading', false);
});
};
const backHome = () => {
router.push('/home/project');
document.title = '智能洞察工具';
};
const fetchInfo = () => {
getSurveyInfo(sn).then((res) => {
surveyInfo.value = res.data || {};
noPermissionTo403(route.path);
project_name.value = res.data.project_name;
store.commit('common/M_COMMON_SET_SURVEY_NAME', res.data.project_name);
store.commit('common/M_COMMON_SET_SURVEY_STATUS', res.data.status);
store.commit('common/M_COMMON_SET_SURVEY_SCENEID', res.data.scene_code_info);
store.commit('common/M_COMMON_SET_SURVEY_SCENEPARENTID', res.data.scene_code);
store.commit('common/M_COMMON_SET_SURVEY_INFO', surveyInfo.value);
});
};
onBeforeUnmount(() => store.commit('common/M_COMMON_SET_SURVEY_INFO', {}));
watch(() => route.path, noPermissionTo403, { immediate: true });
function noPermissionTo403(path) {
if (
surveyInfo.value.team_role_id !== 28
&& surveyInfo.value.role_id === 23
&& ['/survey/publish', '/survey/schedule/recycle', '/survey/analyse'].some((i) =>
path.startsWith(i)
)
) {
router.replace({ path: '/error/403' });
}
}
const visibleShare = ref(false);
// 展示分享入口
const clickEntrance = () => {
console.log('展示入口');
visibleShare.value = true;
};
const groupInfo = ref(null);
const downloadVisible = computed(() => {
return store.state.downloadCenter.isShow;
});
// 下载中心
const toDownload = () => {
console.log('下载中心');
store.dispatch('downloadCenter/changeCenterUrl', route.path);
// downloadVisible.value = true
store.dispatch('downloadCenter/changeCenterShow', true);
// router.push({
// path: "/downloadCenter",
// query: { path: route.path, sn },
// });
};
const showxiazai = ref(true);
onMounted(() => {
if (route.path == '/survey/planet/design') {
showxiazai.value = false;
}
fetchInfo();
});
onMounted(() => store.dispatch('polling/pollingSurveyState', { sn, polling: true }));
onUnmounted(() => store.dispatch('polling/stopPollingSurveyState', { sn }));
</script>
<style lang="scss" scoped>
.header {
box-sizing: border-box;
padding: 0 34px;
height: 70px;
display: flex;
align-items: center;
background: #fff;
box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.06);
.left,
.right {
display: flex;
align-items: center;
width: 40%;
.question-name {
margin-left: 17px;
font-size: 16px;
font-family: PingFangSC-Regular, PingFang SC;
color: #434343;
width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
-webkit-line-clamp: 1;
}
.icon-xiangzuo-moren {
cursor: pointer;
font-size: 10px;
}
}
.tab-container {
width: 60%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
.tab-item {
position: relative;
width: auto;
height: 100%;
display: flex;
align-items: center;
font-size: 18px;
color: #434343;
white-space: nowrap;
font-family: PingFangSC-Regular, PingFang SC;
cursor: pointer;
&.active {
font-size: 20px;
color: #70b936;
font-weight: 500;
font-family: PingFangSC-Semibold, PingFang SC;
.click {
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
padding-top: 5px;
padding-bottom: 5px;
line-height: 10px;
border-radius: 60%;
background-color: #70b936;
color: #fff;
}
.click2 {
width: 20px;
height: 20px;
padding-top: 5px;
padding-bottom: 5px;
line-height: 10px;
border-radius: 60%;
background-color: #f5f5f5;
color: black;
text-align: center;
}
}
}
}
.right {
justify-content: flex-end;
}
.preview-btn {
display: flex;
align-items: center;
margin-left: 10px;
font-size: 16px;
color: #646a73;
cursor: pointer;
}
.preview-btn:hover {
color: #70b936;
}
.ai-inspection-btn {
width: 82px;
}
.publish-btn {
margin-left: 10px;
height: 32px;
line-height: 18px;
border-radius: 4px;
}
.download-btn {
margin-left: 32px;
// width: 88px;
height: 32px;
line-height: 18px;
border-radius: 4px;
}
.share-button {
margin-left: 10px;
font-size: 16px;
}
.head-portrait {
width: 32px;
height: 32px;
background: #70b936;
border-radius: 50%;
opacity: 1;
margin-left: 32px;
color: #fff;
font-size: 12px;
font-family: PingFang SC-中粗体, PingFang SC;
font-weight: normal;
text-align: center;
line-height: 32px;
}
&::v-deep .ant-dropdown-menu-item {
padding: 0;
}
}
.ant-dropdown-menu {
margin: 0;
}
.ant-dropdown-placement-bottomRight {
height: 398px;
}
.head-portrait-menu {
width: 360px;
height: 398px;
margin-top: 26px;
.wai {
width: auto;
// height: 398px;
.one {
width: 296px;
height: 124px;
border-bottom: 1px solid #e8e8e8;
margin-left: 32px;
display: flex;
.leftt {
width: 60px;
height: 60px;
border-radius: 50%;
background: #70b936;
opacity: 1;
margin-top: 32px;
text-align: center;
line-height: 60px;
color: #fff;
}
.rightt {
width: 60px;
height: 60px;
margin-left: 12px;
margin-top: 39px;
.wenzi {
font-size: 16px;
font-family: PingFang SC-中粗体, PingFang SC;
font-weight: 700;
}
}
}
.two {
width: 296px;
height: 124px;
border-bottom: 1px solid #e8e8e8;
margin-left: 32px;
overflow: hidden;
.homepage {
font-size: 16px;
margin-top: 32px;
font-weight: 700;
}
.team-members {
font-size: 16px;
margin-top: 32px;
// margin-bottom: 16px;
font-weight: 700;
}
}
.three {
width: 296px;
height: 124px;
margin-left: 32px;
// overflow: hidden;
.switch-team {
display: flex;
justify-content: space-between;
font-size: 16px;
margin-top: 32px;
font-weight: 700;
}
.log-out {
font-size: 16px;
margin-top: 32px;
font-weight: 700;
}
}
}
}
.tips {
display: flex;
align-items: center;
.icon-tixing {
margin-right: 8px;
color: #fbad13;
font-size: 14px;
font-weight: bold;
}
}
.text {
margin-top: 12px;
}
.big_box {
width: 100%;
height: calc(100% + 70px);
position: fixed;
top: 0;
// background-color: #000;
}
.iconfont {
transform-origin: right;
}
.icon-chexiao {
transform: rotateY(180deg);
}
.download_img {
width: 16px;
height: 16px;
margin-right: 6px;
}
.click {
width: 20px;
height: 20px;
padding-top: 5px;
padding-bottom: 5px;
line-height: 10px;
font-size: 12px;
border-radius: 60%;
background-color: #70b936;
color: #fff;
text-align: center;
margin-right: 8px;
}
.click2 {
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
padding-top: 5px;
padding-bottom: 5px;
line-height: 10px;
font-size: 12px;
border-radius: 60%;
background-color: #f5f5f5;
margin-right: 8px;
}
.mr-6 {
margin-right: 6px;
}
</style>