feat: 优化组件和页面功能
- 新增 YlSwiper 轮播组件,基于 swiper 库实现,支持自定义渲染和多种配置项 - 优化 YlTable 组件,提升表格渲染性能和使用体验 - 优化 LogicInfo 组件,修复数据为空时的显示问题,使用 currentTabs 替代重复的计算属性 - 优化 AnalysisInfo 组件,移除冗余类型转换 - 新增问卷列表组件 QuestionList,用于展示任务相关问卷 - 更新 vite 配置,支持 swiper 自定义元素 - 添加 swiper 依赖包
This commit is contained in:
@@ -32,6 +32,7 @@
|
|||||||
"regenerator-runtime": "^0.14.1",
|
"regenerator-runtime": "^0.14.1",
|
||||||
"shrinkpng": "^1.2.0-beta.1",
|
"shrinkpng": "^1.2.0-beta.1",
|
||||||
"sortablejs": "^1.15.6",
|
"sortablejs": "^1.15.6",
|
||||||
|
"swiper": "^11.2.7",
|
||||||
"tailwindcss": "^4.1.6",
|
"tailwindcss": "^4.1.6",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^11.1.0",
|
||||||
"vant": "^4.9.17",
|
"vant": "^4.9.17",
|
||||||
|
|||||||
286
src/components/YlSwiper/Index.vue
Normal file
286
src/components/YlSwiper/Index.vue
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { register } from 'swiper/element/bundle';
|
||||||
|
import { ref, computed, defineEmits, onMounted, defineModel, useSlots } from 'vue';
|
||||||
|
import { ArrowLeft, ArrowRight } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
|
// 定义 Swiper 元素类型
|
||||||
|
interface SwiperEvent {
|
||||||
|
detail: [any, any?];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SwiperElement extends HTMLElement {
|
||||||
|
swiper: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注册 Swiper 元素
|
||||||
|
register();
|
||||||
|
|
||||||
|
// Swiper 实例引用
|
||||||
|
const swiperRef = ref<SwiperElement | null>(null);
|
||||||
|
const swiperInstance = ref<any>(null);
|
||||||
|
|
||||||
|
// 使用 defineModel 定义可双向绑定的属性
|
||||||
|
// 轮播图数据
|
||||||
|
const slides = defineModel<any[]>('slides', { default: () => [] });
|
||||||
|
// 每个幻灯片的渲染函数
|
||||||
|
const renderSlide = defineModel<Function | null>('renderSlide', { default: null });
|
||||||
|
// 每页显示的幻灯片数
|
||||||
|
const slidesPerView = defineModel<number | string>('slidesPerView', { default: 1 });
|
||||||
|
// 幻灯片之间的间距
|
||||||
|
const spaceBetween = defineModel<number | string>('spaceBetween', { default: 10 });
|
||||||
|
// 是否居中显示
|
||||||
|
const centeredSlides = defineModel<boolean>('centeredSlides', { default: false });
|
||||||
|
// 是否循环播放
|
||||||
|
const loop = defineModel<boolean>('loop', { default: false });
|
||||||
|
// 是否自动播放
|
||||||
|
const autoplay = defineModel<boolean | object>('autoplay', { default: false });
|
||||||
|
// 是否显示分页器
|
||||||
|
const pagination = defineModel<boolean | object>('pagination', { default: false });
|
||||||
|
// 是否显示导航按钮
|
||||||
|
const navigation = defineModel<boolean | object>('navigation', { default: false });
|
||||||
|
// 断点配置
|
||||||
|
const breakpoints = defineModel<object>('breakpoints', { default: () => ({}) });
|
||||||
|
// 初始幻灯片索引
|
||||||
|
const initialSlide = defineModel<number>('initialSlide', { default: 0 });
|
||||||
|
// 高度
|
||||||
|
const height = defineModel<string>('height', { default: '' });
|
||||||
|
// 宽度
|
||||||
|
const width = defineModel<string>('width', { default: '100%' });
|
||||||
|
// 是否允许触摸滑动(设置为 false 则禁用滑动)
|
||||||
|
const allowTouchMove = defineModel<boolean>('allowTouchMove', { default: true });
|
||||||
|
|
||||||
|
// 定义事件
|
||||||
|
const emit = defineEmits([
|
||||||
|
'progress',
|
||||||
|
'slideChange',
|
||||||
|
'slideChangeTransitionStart',
|
||||||
|
'slideChangeTransitionEnd',
|
||||||
|
'update'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 计算属性:自动播放配置
|
||||||
|
const autoplayConfig = computed(() => {
|
||||||
|
if (autoplay.value === true) {
|
||||||
|
return { delay: 3000, disableOnInteraction: false };
|
||||||
|
}
|
||||||
|
return autoplay.value || false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算属性:分页器配置
|
||||||
|
const paginationConfig = computed(() => {
|
||||||
|
// 始终返回 false,禁用原生分页指示器
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取插槽
|
||||||
|
const slots = useSlots();
|
||||||
|
|
||||||
|
// 计算属性:是否有自定义导航按钮
|
||||||
|
const hasCustomNavButtons = computed(() => {
|
||||||
|
return !!(slots.prevButton || slots.nextButton);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算属性:导航按钮配置
|
||||||
|
const navigationConfig = computed(() => {
|
||||||
|
// 如果有自定义导航按钮,则禁用原生导航按钮
|
||||||
|
if (hasCustomNavButtons.value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 否则使用配置的导航按钮设置
|
||||||
|
if (navigation.value === true) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return navigation.value || false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 事件处理函数
|
||||||
|
const onProgress = (e: SwiperEvent) => {
|
||||||
|
const [swiper, progress] = e.detail;
|
||||||
|
emit('progress', { swiper, progress });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSlideChange = (e: SwiperEvent) => {
|
||||||
|
const [swiper] = e.detail;
|
||||||
|
emit('slideChange', { swiper, activeIndex: swiper.activeIndex });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSlideChangeTransitionStart = (e: SwiperEvent) => {
|
||||||
|
const [swiper] = e.detail;
|
||||||
|
emit('slideChangeTransitionStart', { swiper });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSlideChangeTransitionEnd = (e: SwiperEvent) => {
|
||||||
|
const [swiper] = e.detail;
|
||||||
|
emit('slideChangeTransitionEnd', { swiper });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化 Swiper
|
||||||
|
onMounted(() => {
|
||||||
|
// 使用 setTimeout 确保 DOM 已完全渲染
|
||||||
|
setTimeout(() => {
|
||||||
|
if (swiperRef.value && swiperRef.value.swiper) {
|
||||||
|
swiperInstance.value = swiperRef.value.swiper;
|
||||||
|
emit('update', swiperInstance.value);
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 提供方法:切换到下一张幻灯片
|
||||||
|
const slideNext = () => {
|
||||||
|
if (swiperInstance.value && typeof swiperInstance.value.slideNext === 'function') {
|
||||||
|
swiperInstance.value.slideNext();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提供方法:切换到上一张幻灯片
|
||||||
|
const slidePrev = () => {
|
||||||
|
if (swiperInstance.value && typeof swiperInstance.value.slidePrev === 'function') {
|
||||||
|
swiperInstance.value.slidePrev();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提供方法:切换到指定幻灯片
|
||||||
|
const slideTo = (index: number) => {
|
||||||
|
if (swiperInstance.value && typeof swiperInstance.value.slideTo === 'function') {
|
||||||
|
swiperInstance.value.slideTo(index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 暴露方法给父组件
|
||||||
|
defineExpose({
|
||||||
|
slideNext,
|
||||||
|
slidePrev,
|
||||||
|
slideTo,
|
||||||
|
swiper: swiperInstance
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="yl-swiper" :style="{ width, height }">
|
||||||
|
<!-- 导航按钮(左) -->
|
||||||
|
<div v-if="navigation" class="yl-swiper-button-prev" @click="slidePrev">
|
||||||
|
<slot name="prevButton">
|
||||||
|
<el-icon :size="24"><arrow-left /></el-icon>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<swiper-container
|
||||||
|
ref="swiperRef"
|
||||||
|
:slides-per-view="slidesPerView"
|
||||||
|
:space-between="spaceBetween"
|
||||||
|
:centered-slides="centeredSlides"
|
||||||
|
:initial-slide="initialSlide"
|
||||||
|
:loop="loop"
|
||||||
|
:autoplay="autoplayConfig"
|
||||||
|
:pagination="paginationConfig"
|
||||||
|
:navigation="navigationConfig"
|
||||||
|
:breakpoints="breakpoints"
|
||||||
|
:allow-touch-move="allowTouchMove"
|
||||||
|
@swiperprogress="onProgress"
|
||||||
|
@swiperslidechange="onSlideChange"
|
||||||
|
@sliderslidetransitionstart="onSlideChangeTransitionStart"
|
||||||
|
@sliderslidetransitionend="onSlideChangeTransitionEnd"
|
||||||
|
>
|
||||||
|
<!-- 使用传入的渲染函数渲染幻灯片 -->
|
||||||
|
<template v-if="renderSlide && slides && slides.length">
|
||||||
|
<swiper-slide v-for="(item, index) in slides" :key="index">
|
||||||
|
<component :is="renderSlide(item, index)" />
|
||||||
|
</swiper-slide>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 使用默认插槽 -->
|
||||||
|
<template v-else>
|
||||||
|
<slot></slot>
|
||||||
|
</template>
|
||||||
|
</swiper-container>
|
||||||
|
|
||||||
|
<!-- 导航按钮(右) -->
|
||||||
|
<div v-if="navigation" class="yl-swiper-button-next" @click="slideNext">
|
||||||
|
<slot name="nextButton">
|
||||||
|
<el-icon :size="24"><arrow-right /></el-icon>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 自定义分页指示器 -->
|
||||||
|
<div v-if="pagination" class="yl-swiper-pagination">
|
||||||
|
<slot name="pagination">
|
||||||
|
<div
|
||||||
|
v-for="(_, index) in slides && slides.length ? slides : (swiperInstance?.value?.slides || [])"
|
||||||
|
:key="index"
|
||||||
|
class="yl-swiper-pagination-bullet"
|
||||||
|
:class="{ 'is-active': swiperInstance?.value?.activeIndex === index }"
|
||||||
|
@click="slideTo(index)"
|
||||||
|
></div>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.yl-swiper {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.yl-swiper-button-prev,
|
||||||
|
.yl-swiper-button-next {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
z-index: 10;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yl-swiper-button-prev {
|
||||||
|
left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yl-swiper-button-next {
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yl-swiper-button-prev,
|
||||||
|
.yl-swiper-button-next {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);
|
||||||
|
color: #409EFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yl-swiper-pagination {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
|
.yl-swiper-pagination-bullet {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
background-color: #409EFF;
|
||||||
|
width: 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { RowAlign, type ColumnStyle } from 'element-plus';
|
import { RowAlign, type ColumnStyle } from 'element-plus';
|
||||||
import type { CSSProperties } from 'vue';
|
|
||||||
|
|
||||||
const data = defineModel<unknown[]>('data', { default: [] });
|
const data = defineModel<unknown[]>('data', { default: [] });
|
||||||
const props = defineModel<
|
const props = defineModel<
|
||||||
@@ -16,6 +15,9 @@ const singleLine = defineModel<boolean>('singleLine', { default: false });
|
|||||||
const rowStyle = defineModel<ColumnStyle<any>>('rowStyle', {
|
const rowStyle = defineModel<ColumnStyle<any>>('rowStyle', {
|
||||||
default: {}
|
default: {}
|
||||||
});
|
});
|
||||||
|
// 显示表格的高度, 默认的高度是 200px
|
||||||
|
const tableHeight = defineModel<string | number>('height', { default: '300px' });
|
||||||
|
|
||||||
const headerStyle = defineModel<ColumnStyle<any>>('headerStyle', {
|
const headerStyle = defineModel<ColumnStyle<any>>('headerStyle', {
|
||||||
default: {
|
default: {
|
||||||
background: 'red'
|
background: 'red'
|
||||||
@@ -34,6 +36,7 @@ function setStripeColor(rowData: { row: any; rowIndex: number }): string {
|
|||||||
<template>
|
<template>
|
||||||
<div :style="{ borderRadius: rounded ? '10px' : '0', overflow: 'hidden' }">
|
<div :style="{ borderRadius: rounded ? '10px' : '0', overflow: 'hidden' }">
|
||||||
<el-table
|
<el-table
|
||||||
|
:max-height="tableHeight"
|
||||||
:header-cell-style="{ background: '#f2f8ee' }"
|
:header-cell-style="{ background: '#f2f8ee' }"
|
||||||
:row-class-name="setStripeColor"
|
:row-class-name="setStripeColor"
|
||||||
:data="data"
|
:data="data"
|
||||||
@@ -49,7 +52,7 @@ function setStripeColor(rowData: { row: any; rowIndex: number }): string {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped module="table">
|
||||||
:deep(.even-row) {
|
:deep(.even-row) {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ import '@/style/utils.scss';
|
|||||||
import appBridge from '@/assets/js/appBridge';
|
import appBridge from '@/assets/js/appBridge';
|
||||||
import VConsole from 'vconsole';
|
import VConsole from 'vconsole';
|
||||||
import './assets/css/main.scss';
|
import './assets/css/main.scss';
|
||||||
|
// 引入 swiper 样式
|
||||||
|
import 'swiper/css';
|
||||||
|
import 'swiper/css/navigation';
|
||||||
|
import 'swiper/css/pagination';
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
if (import.meta.env.VITE_APP_ENV !== 'production') {
|
if (import.meta.env.VITE_APP_ENV !== 'production') {
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ service.interceptors.request.use(
|
|||||||
let plantToken = localStorage.getItem('plantToken');
|
let plantToken = localStorage.getItem('plantToken');
|
||||||
// 如果 token 不存在, 试图尝试让用户输入 token ,然后放入本地 token 内容中
|
// 如果 token 不存在, 试图尝试让用户输入 token ,然后放入本地 token 内容中
|
||||||
// 仅限开发环境中
|
// 仅限开发环境中
|
||||||
if (!plantToken && !import.meta.env.PROD) {
|
// if (!plantToken && !import.meta.env.PROD) {
|
||||||
|
if (!plantToken) {
|
||||||
plantToken = prompt('token 不存在,请输入 plant token');
|
plantToken = prompt('token 不存在,请输入 plant token');
|
||||||
plantToken && localStorage.setItem('plantToken', plantToken);
|
plantToken && localStorage.setItem('plantToken', plantToken);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ function handleSearchClick() {
|
|||||||
<div class="container-body">
|
<div class="container-body">
|
||||||
<!-- 搜索栏 -->
|
<!-- 搜索栏 -->
|
||||||
<section class="search">
|
<section class="search">
|
||||||
<search-bar placeholder="请输入关键字" :value="keyword" @click="handleSearchClick" />
|
<search-bar placeholder="请输入关键词" :value="keyword" @click="handleSearchClick" />
|
||||||
</section>
|
</section>
|
||||||
<!-- 首页轮播图 -->
|
<!-- 首页轮播图 -->
|
||||||
<section class="slider">
|
<section class="slider">
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ const createdQuestion = (item) => {
|
|||||||
remarks: '为优化活动服务品质,烦请完成问卷,感谢配合',
|
remarks: '为优化活动服务品质,烦请完成问卷,感谢配合',
|
||||||
scene_code: item.parentCode,
|
scene_code: item.parentCode,
|
||||||
scene_code_info: item.code,
|
scene_code_info: item.code,
|
||||||
// 很迷茫 模板新增 tag 空数组 非模板 就是k
|
|
||||||
tags: ''
|
tags: ''
|
||||||
};
|
};
|
||||||
if (createdNewPage.value) {
|
if (createdNewPage.value) {
|
||||||
@@ -116,7 +115,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="create_survey">
|
<div class="create_survey">
|
||||||
<div class="create_survey_title" style="color: #000; text-align: left">新建问卷</div>
|
<div class="create_survey_title" style="color: #000; text-align: left">新建任务</div>
|
||||||
<div class="home-pen">
|
<div class="home-pen">
|
||||||
<img :src="homePen" alt="" />
|
<img :src="homePen" alt="" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,35 +1,57 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import SurveyAnalysis from '@/views/Survey/views/Analysis/Index.vue';
|
|
||||||
import { fetchSurveys } from '@/hooks/request/useSurvey';
|
import { fetchSurveys } from '@/hooks/request/useSurvey';
|
||||||
import { cellWithoutPadding } from '@/utils/theme/cell';
|
import QuestionList from './components/QuestionList.vue';
|
||||||
|
import YlSwiper from '@/components/YlSwiper/Index.vue';
|
||||||
|
|
||||||
const { surveys } = fetchSurveys();
|
const { surveys } = fetchSurveys();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<van-cell :style="cellWithoutPadding" class="swipe-container">
|
<div class="carousel-container">
|
||||||
<template #extra>
|
<!-- 方式一:使用默认插槽,手动添加 swiper-slide 元素 -->
|
||||||
<van-swipe class="my-swipe" indicator-color="white" :loop="false">
|
<yl-swiper
|
||||||
<van-swipe-item :key="survey.sn" v-for="survey in surveys">
|
:slides-per-view="1"
|
||||||
<section style="width: 90vw">
|
:centered-slides="true"
|
||||||
<h3 style="margin: 10px 0 -10px 10px">我的任务</h3>
|
:pagination="true"
|
||||||
<survey-analysis :sn="survey.sn" :disable-search="true" :disable-insight="true" />
|
:navigation="true"
|
||||||
</section>
|
:loop="false"
|
||||||
</van-swipe-item>
|
:space-between="0"
|
||||||
<!-- 指示器 -->
|
:allow-touch-move="false"
|
||||||
<!-- <template #indicator="{ active, total }">
|
>
|
||||||
<div class="custom-indicator">{{ active + 1 }}/{{ total }}</div>
|
<swiper-slide v-for="question in surveys" :key="question.sn">
|
||||||
</template> -->
|
<question-list :survey="question" style="max-width: 100vw; overflow: hidden" />
|
||||||
</van-swipe>
|
</swiper-slide>
|
||||||
</template>
|
</yl-swiper>
|
||||||
</van-cell>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@use '@/assets/css/theme';
|
@use '@/assets/css/theme';
|
||||||
|
|
||||||
.swipe-container {
|
.carousel-container {
|
||||||
|
background-color: #fff;
|
||||||
|
overflow: hidden;
|
||||||
margin-top: theme.$gap;
|
margin-top: theme.$gap;
|
||||||
border-radius: theme.$card-radius;
|
border-radius: theme.$card-radius;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
.carousel-item {
|
||||||
|
.swipe-container {
|
||||||
|
margin-top: theme.$gap;
|
||||||
|
border-radius: theme.$card-radius;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-content {
|
||||||
|
height: 150px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import YlSwiper from '@/components/YlSwiper/Index.vue';
|
||||||
|
import { useFetchAnalysis } from '@/hooks/request/useSurvey';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import SurveyItem from '@/views/Survey/components/SurveyItem.vue';
|
||||||
|
import LogicInfo from '@/views/Survey/views/Analysis/components/LogicInfo/Index.vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import Wait from '@/views/Survey/views/Analysis/components/Wait/Index.vue';
|
||||||
|
import AnalysisInfo from '@/views/Survey/views/Analysis/components/AnalysisInfo/Index.vue';
|
||||||
|
import SearchBar from '@/components/Search/Index.vue';
|
||||||
|
import { fetchSingleSurvey } from '@/hooks/request/useSurvey';
|
||||||
|
|
||||||
|
const survey = defineModel<SurveyItem>('survey');
|
||||||
|
// 获取问卷分析数据
|
||||||
|
const { questionAnalysis } = useFetchAnalysis(survey.value?.sn as string);
|
||||||
|
const { currentSurvey } = fetchSingleSurvey(survey.value?.sn as string);
|
||||||
|
|
||||||
|
const disableInsight = ref(true);
|
||||||
|
const aiInsightsConfig = ref({
|
||||||
|
visible: false,
|
||||||
|
message: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const postAnalysis = (sn: string) => {
|
||||||
|
aiInsightsConfig.value.visible = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const height = ref(`200px`);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<section class="question-item-container">
|
||||||
|
<section class="survey-item">
|
||||||
|
<!-- 问卷详情部分 -->
|
||||||
|
<survey-item
|
||||||
|
v-if="currentSurvey"
|
||||||
|
:is-analysis="disableInsight"
|
||||||
|
:disable-action-button="true"
|
||||||
|
:survey="currentSurvey as SurveyItem"
|
||||||
|
></survey-item>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- 问卷分析 -->
|
||||||
|
<section class="analysis-info">
|
||||||
|
<!-- van swiper -->
|
||||||
|
<van-swipe>
|
||||||
|
<van-swipe-item v-for="analysis in questionAnalysis">
|
||||||
|
<analysis-info :sn="survey?.sn" :questionAnalysis="[analysis]" />
|
||||||
|
</van-swipe-item>
|
||||||
|
</van-swipe>
|
||||||
|
|
||||||
|
<!-- <swiper navigation :slides-per-view="3" :modules="modules" :space-between="50">
|
||||||
|
<swiper-slide v-for="analysis in questionAnalysis">
|
||||||
|
{{ analysis }}
|
||||||
|
</swiper-slide>
|
||||||
|
</swiper> -->
|
||||||
|
|
||||||
|
<!-- swiper -->
|
||||||
|
<!-- <yl-swiper> -->
|
||||||
|
<!-- <swiper-slide v-for="item in questionAnalysis" :key="item.stem"> -->
|
||||||
|
<!-- 为 item 解决内部数据错误的问题 -->
|
||||||
|
<!-- <analysis-info :sn="survey?.sn" :questionAnalysis="[item]" /> -->
|
||||||
|
<!-- </swiper-slide> -->
|
||||||
|
<!-- </yl-swiper> -->
|
||||||
|
|
||||||
|
<!-- el carousel -->
|
||||||
|
<!-- <el-carousel arrow="always" :loop="false" :autoplay="false"> -->
|
||||||
|
<!-- <el-carousel-item v-for="item in questionAnalysis"> -->
|
||||||
|
<!-- 为 item 解决内部数据错误的问题 -->
|
||||||
|
<!-- <analysis-info :sn="survey?.sn" :questionAnalysis="[item]" /> -->
|
||||||
|
<!-- </el-carousel-item> -->
|
||||||
|
<!-- </el-carousel> -->
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@use '@/assets/css/theme';
|
||||||
|
|
||||||
|
.question-item-container {
|
||||||
|
width: 90vw;
|
||||||
|
|
||||||
|
// display: flex;
|
||||||
|
// justify-content: center;
|
||||||
|
// flex-flow: column nowrap;
|
||||||
|
// align-items: center;
|
||||||
|
|
||||||
|
.survey-item {
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-info {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div style="width: 100%">
|
<div style="width: 100%">
|
||||||
<section v-for="analysis in questionAnalysis" :key="analysis.stem" class="mt10">
|
<!-- 优先去上级传递的数值 -->
|
||||||
|
<section v-for="analysis in analysis" :key="analysis.stem" class="mt10">
|
||||||
<!-- {{ analysis }} -->
|
<!-- {{ analysis }} -->
|
||||||
<!-- 问题标题 -->
|
<!-- 问题标题 -->
|
||||||
<el-tag type="success" size="small">{{
|
<el-tag type="success" size="small">{{
|
||||||
@@ -17,16 +18,21 @@
|
|||||||
|
|
||||||
<!-- 问题表格部分 -->
|
<!-- 问题表格部分 -->
|
||||||
<yl-table
|
<yl-table
|
||||||
class="mt10"
|
class="mt10"
|
||||||
:props="getTableHeadProps(analysis.head, analysis.option)"
|
:props="getTableHeadProps(analysis.head, analysis.option)"
|
||||||
:data="getTableData(analysis)"
|
:data="getTableData(analysis)"
|
||||||
v-if="analysis.head"
|
v-if="analysis.head"
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
|
<!-- <section v-else>
|
||||||
|
<empty-container />
|
||||||
|
</section> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
// 空白容器
|
||||||
|
import EmptyContainer from '@/views/Survey/components/EmptyContainer.vue';
|
||||||
import { useFetchAnalysis } from '../../hooks/useAnalysis';
|
import { useFetchAnalysis } from '../../hooks/useAnalysis';
|
||||||
import { questionTypeMap } from '@/utils/question/typeMapping';
|
import { questionTypeMap } from '@/utils/question/typeMapping';
|
||||||
import ChartMsg from '@/components/Analysis/Index.vue';
|
import ChartMsg from '@/components/Analysis/Index.vue';
|
||||||
@@ -35,12 +41,20 @@ import YlTable from '@/components/YlTable/Index.vue';
|
|||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
// questionTypeMap 自己去对应
|
// questionTypeMap 自己去对应
|
||||||
const showChart = ref([1, 2, 5, 106, 9, 10]);
|
const showChart = ref([1, 2, 5, 106, 9, 10]);
|
||||||
const sn = defineModel('sn');
|
const sn = defineModel('sn', { required: true });
|
||||||
const { questionAnalysis } = useFetchAnalysis(sn.value as string);
|
|
||||||
// const showTable = ref([1,2,4])
|
// 接受上级传递的 questionAnalysis 数据
|
||||||
|
const analysis = defineModel<any[]>('questionAnalysis');
|
||||||
|
|
||||||
|
// 如果没有接收到数据,那么就去请求
|
||||||
|
if (!analysis.value) {
|
||||||
|
console.log('repeat fetch analysis');
|
||||||
|
const { questionAnalysis } = useFetchAnalysis(sn.value as string);
|
||||||
|
analysis.value = questionAnalysis.value;
|
||||||
|
}
|
||||||
|
|
||||||
// 构建表头
|
// 构建表头
|
||||||
const getTableHeadProps = (values: any[], option) => {
|
const getTableHeadProps = (values: any[], option: any[]) => {
|
||||||
const head = [];
|
const head = [];
|
||||||
|
|
||||||
if (values && values.length > 0) {
|
if (values && values.length > 0) {
|
||||||
@@ -63,7 +77,6 @@ const getTableHeadProps = (values: any[], option) => {
|
|||||||
}
|
}
|
||||||
return head;
|
return head;
|
||||||
};
|
};
|
||||||
// 构建表格数据
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.mt10 {
|
.mt10 {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export const series = ref({
|
|||||||
name: 'yl form chart',
|
name: 'yl form chart',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: ['30%', '50%'],
|
radius: ['30%', '50%'],
|
||||||
center: ['50%', '30%'],
|
center: ['50%', '35%'],
|
||||||
data: [
|
data: [
|
||||||
{ value: 1048, name: 'Search Engine' },
|
{ value: 1048, name: 'Search Engine' },
|
||||||
{ value: 735, name: 'Direct' },
|
{ value: 735, name: 'Direct' },
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ sn.value = sn.value ?? (route.query.sn as string);
|
|||||||
const { quota, random, cycle, loading } = fetchLogicInfo(sn.value);
|
const { quota, random, cycle, loading } = fetchLogicInfo(sn.value);
|
||||||
|
|
||||||
const activeTab = ref(0);
|
const activeTab = ref(0);
|
||||||
const tabs = ref<LogicInfoTab[]>([
|
|
||||||
|
const tabs = computed(() => [
|
||||||
{
|
{
|
||||||
title: '逻辑配额',
|
title: '逻辑配额',
|
||||||
props: [
|
props: [
|
||||||
@@ -23,7 +24,7 @@ const tabs = ref<LogicInfoTab[]>([
|
|||||||
{ prop: 'sample_number', label: '样本量', width: 90 },
|
{ prop: 'sample_number', label: '样本量', width: 90 },
|
||||||
{ prop: 'percent', label: '进度', width: 90 }
|
{ prop: 'percent', label: '进度', width: 90 }
|
||||||
],
|
],
|
||||||
data: quota.value as any
|
data: quota.value
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '随机题组配额',
|
title: '随机题组配额',
|
||||||
@@ -33,7 +34,7 @@ const tabs = ref<LogicInfoTab[]>([
|
|||||||
{ prop: 'sample_num', label: '样本量', width: 120 },
|
{ prop: 'sample_num', label: '样本量', width: 120 },
|
||||||
{ prop: 'precent', label: '进度', width: 120 }
|
{ prop: 'precent', label: '进度', width: 120 }
|
||||||
],
|
],
|
||||||
data: random.value as any
|
data: random.value
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '循环题组配额',
|
title: '循环题组配额',
|
||||||
@@ -43,25 +44,23 @@ const tabs = ref<LogicInfoTab[]>([
|
|||||||
{ prop: 'sample_num', label: '样本量', width: 80 },
|
{ prop: 'sample_num', label: '样本量', width: 80 },
|
||||||
{ prop: 'precent', label: '进度', width: 120 }
|
{ prop: 'precent', label: '进度', width: 120 }
|
||||||
],
|
],
|
||||||
data: cycle.value as any
|
data: cycle.value
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// 自动计算是否有 card 区域,只显示有数据的标签页
|
||||||
const currentTabs = computed(() => {
|
const currentTabs = computed(() => {
|
||||||
return tabs.value.filter((item) => {
|
return tabs.value.filter((item) => {
|
||||||
return item.data.length > 0;
|
return item.data && item.data.length > 0;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 自动计算是否有 card 区域
|
// 调试信息
|
||||||
const visibleQuestionConfig = computed(() => {
|
const debug = ref(false);
|
||||||
return tabs.value.filter((item) => {
|
|
||||||
return item.data.length > 0;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<van-cell class="logic-info" v-if="visibleQuestionConfig.length > 0">
|
<van-cell class="logic-info" v-if="currentTabs.length > 0">
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<section style="width: 86vw" v-loading="loading">
|
<section style="width: 86vw" v-loading="loading">
|
||||||
<!-- tabs 选项列表 -->
|
<!-- tabs 选项列表 -->
|
||||||
|
|||||||
@@ -69,7 +69,14 @@ export default defineConfig(({ mode }) => {
|
|||||||
cacheDir: '.tmp',
|
cacheDir: '.tmp',
|
||||||
plugins: [
|
plugins: [
|
||||||
vueDevTools(),
|
vueDevTools(),
|
||||||
vue(),
|
vue({
|
||||||
|
template: {
|
||||||
|
compilerOptions: {
|
||||||
|
// 将 swiper 相关标签注册为自定义元素
|
||||||
|
isCustomElement: (tag) => tag.startsWith('swiper-')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
vueJsx(),
|
vueJsx(),
|
||||||
AutoImport({ resolvers: [VantResolver(), ElementPlusResolver()] }),
|
AutoImport({ resolvers: [VantResolver(), ElementPlusResolver()] }),
|
||||||
Components({ resolvers: [VantResolver(), ElementPlusResolver()] }),
|
Components({ resolvers: [VantResolver(), ElementPlusResolver()] }),
|
||||||
|
|||||||
Reference in New Issue
Block a user