优化问卷分析与界面展示

- 修复了图表数据为空时的渲染问题,添加了数据验证逻辑
- 优化了Analysis组件中的图表展示,增加了条件渲染
- 新增useScreen钩子函数用于响应式布局
- 改进了SurveyItem组件的标题样式,从h1调整为h3
- 优化了表格组件的宽度计算逻辑,提高了不同数据量下的展示效果
- 调整了多个组件的布局和样式,提升用户体验
- 清理了代码中的冗余注释和空白行,提高代码可读性
This commit is contained in:
Huangzhe
2025-05-21 16:12:58 +08:00
parent 0ae0bd0fdf
commit ef7463dc59
16 changed files with 108 additions and 66 deletions

View File

@@ -36,7 +36,9 @@ const index = ref(0);
// 当 keyword 变动的时候,标记脏数据
watch(
() => analysis.value,
async () => {
async (value) => {
// 排除空数据渲染图标步骤
tableData.value = {
...analysis.value,
option: getTableData(analysis.value)
@@ -46,6 +48,9 @@ watch(
series.value = formatData(dimension.value ? tableData.value : analysis.value, index.value);
const pieChart = useTemplateRef<HTMLSpanElement>('pieChart');
// console.log(`series value `, series.value);
if (!series.value?.data?.length) return;
useSetPieChart(pieChart, series, { title: false, legend: false });
},
{ immediate: true }
@@ -71,6 +76,7 @@ const changeChart = (i: number) => {
<!-- 图表部分 -->
<div
v-if="series?.data?.length"
class="charts"
:style="{
display: 'flex',
@@ -79,7 +85,13 @@ const changeChart = (i: number) => {
margin: '16px 0'
}"
>
<span ref="pieChart" style="width: 100%; height: v-bind(chartHeight + 'px')"></span>
<span
ref="pieChart"
:style="{
width: '100%',
height: series?.data?.length ? chartHeight + 'px' : ''
}"
></span>
</div>
</section>
</template>

View File

@@ -8,7 +8,7 @@ const survey = defineModel<object>('survey', { required: true });
<template>
<div>
<h1>{{ survey.project_name }}</h1>
<h3>{{ survey.project_name }}</h3>
<el-space spacer="|" direction="horizontal">
<section class="flex items-center pt-1">
<img :src="people" alt="" style="margin-right: 5px" />
@@ -25,8 +25,8 @@ const survey = defineModel<object>('survey', { required: true });
<style lang="scss" scoped module="item">
@use '@/assets/css/theme';
h1 {
margin: 17px 0 12px 0;
h3 {
margin: 10px 0 12px 0;
font-weight: 800;
line-height: 15px;
text-align: left;

View File

@@ -1,3 +1,7 @@
<script setup lang="ts">
const template = defineModel('template', { required: true });
</script>
<template>
<van-cell style="margin-bottom: 10px">
<template #extra>
@@ -18,6 +22,14 @@
</van-cell>
</template>
<script setup lang="ts">
const template = defineModel('template', { required: true });
</script>
<style lang="scss" scoped module="item">
@use '@/assets/css/theme';
h3 {
margin: 10px 0 12px 0;
font-weight: 800;
line-height: 15px;
text-align: left;
font-style: normal;
}
</style>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { RowAlign, type ColumnStyle } from 'element-plus';
import { type ColumnStyle } from 'element-plus';
const data = defineModel<unknown[]>('data', { default: [] });
const props = defineModel<TablePropsType[]>('props');

View File

@@ -1,6 +1,6 @@
type TablePropsType = {
prop: string;
label: string;
width?: string;
width?: string | number;
};

View File

@@ -0,0 +1,25 @@
import { onMounted, onUnmounted, ref } from 'vue';
export function screenLayout() {
// 当前视口的宽度
const width = ref(window.innerWidth);
// 当前视口的高度
const height = ref(window.innerHeight);
const observer = new ResizeObserver((entries) => {
entries.forEach((entry) => {
const { width: _width, height: _height } = entry.contentRect;
width.value = _width;
height.value = _height;
});
});
// 当页面挂载的时候开始监听元素
onMounted(() => observer.observe(document.body));
// 当页面卸载的时候停止监听元素
onUnmounted(() => observer.disconnect());
return {
width,
height
};
}

View File

@@ -1,4 +1,5 @@
<script setup>
import { fetchSurveys } from '@/hooks/request/useSurvey';
import CreateSurvey from './components/CreateSurvey/Index.vue';
import NewSurvey from './components/NewSurvey/index.vue';
import { onMounted, ref } from 'vue';
@@ -14,7 +15,8 @@ import MineTask from '@/views/Home/components/MineTask/Index.vue';
import router from '@/router';
const contentShow = ref(false);
// 获取我的问卷数据
const { surveys } = fetchSurveys();
const keyword = ref('');
onMounted(async () => {
if (appBridge.isInReactNative()) {
@@ -67,9 +69,8 @@ function handleSearchClick() {
<!--底部新建问卷-->
<NewSurvey />
<mine-task v-if="false" />
<mine-task :surveys="surveys" v-if="surveys?.length > 0" />
<home-recommend class="home_recommend" v-else />
<navigation />
</div>
</div>

View File

@@ -33,7 +33,7 @@ const props = ref<TablePropsType[]>([
<template>
<van-cell class="home_recommend">
<template #extra>
<div style="width: 88vw">
<div style="width: 90vw">
<yl-table :data="data?.surveyTrendDataVOS" :props="props" />
</div>
</template>

View File

@@ -1,26 +1,39 @@
<template>
<el-card shadow="never" :body-style="{ padding: 0 }">
<van-swipe :autoplay="5000" indicator-color="white">
<van-swipe-item v-for="banner in banners">
<el-image :src="banner.banner_address" fit="contain" @click="handleBannerClick(banner)" />
</van-swipe-item>
</van-swipe>
</el-card>
</template>
<script setup lang="ts">
import { banners } from '@/views/Home/components/ImageSlider/hooks/useSlider';
import { useRouter } from 'vue-router';
import { bannerInfo } from '@/views/AD/hooks/useAD';
const router = useRouter();
// const defineBanners = defineModel('banners');
// 如果定义了 banner 那么 banners 就不再初始化
// defineBanners.value && updateBanners(defineBanners.value);
const borderRadius = defineModel('borderRadius');
function handleBannerClick(banner: any) {
const router = useRouter();
// 把对应的信息给 AD 的 hooks
bannerInfo.value = banner;
router.push({ name: 'ad' });
}
</script>
<template>
<van-swipe :autoplay="5000" indicator-color="white">
<van-swipe-item v-for="banner in banners">
<el-image
class="img"
:style="{ borderRadius: borderRadius + 'px' }"
:src="banner.banner_address"
fit="contain"
@click="handleBannerClick(banner)"
/>
</van-swipe-item>
</van-swipe>
</template>
<style lang="scss" scoped>
@use '@/assets/css/theme' as *;
.img {
border-radius: $card-radius;
}
</style>

View File

@@ -1,9 +1,8 @@
<script setup lang="ts">
import { fetchSurveys } from '@/hooks/request/useSurvey';
import QuestionList from './components/QuestionList.vue';
import YlSwiper from '@/components/YlSwiper/Index.vue';
const { surveys } = fetchSurveys();
const surveys = defineModel('surveys', { required: true });
</script>
<template>

View File

@@ -1,13 +1,8 @@
<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');
@@ -16,16 +11,6 @@ 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>
@@ -80,15 +65,6 @@ const height = ref(`200px`);
.question-item-container {
width: 90vw;
// display: flex;
// justify-content: center;
// flex-flow: column nowrap;
// align-items: center;
.survey-item {
width: 95%;
}
.analysis-info {
}
}

View File

@@ -93,6 +93,9 @@ watch(keyword, async () => {
// await handleSearch();
// 重置状态
loading.value = false
// 清空 banners
banners.value = [];
});
// 索引变动之后,立刻进行搜索 (没有进行防抖)

View File

@@ -68,7 +68,6 @@ onUnmounted(() => {
@use '@/assets/css/theme';
.title {
overflow-wrap: break-word;
color: rgba(0, 0, 0, 1);
font-size: 14px;
font-family: PingFangSC-Medium;
@@ -76,7 +75,7 @@ onUnmounted(() => {
text-align: left;
white-space: nowrap;
line-height: 20px;
margin: 28px 297px 0 2px;
margin: 28px 0 0 0;
}
.search-container {
@@ -88,7 +87,7 @@ onUnmounted(() => {
}
.banner {
padding: theme.$gap;
padding: 0 theme.$gap;
}
// 搜索结果外部布局

View File

@@ -1,10 +1,5 @@
<script setup lang="ts">
import {
index,
surveys as _surveys,
lastIndex,
keyword
} from '@/views/HomeSearch/Hooks/useSurveySearch';
import { index, surveys as _surveys, keyword } from '@/views/HomeSearch/Hooks/useSurveySearch';
import SurveyItem from '@/components/SurveyItem/Index.vue';
import { useRouter } from 'vue-router';
import { computed } from 'vue';
@@ -53,5 +48,9 @@ const surveys = computed(() => {
.list-item {
margin-top: theme.$gap;
}
.list-item:first-child {
margin-top: 0;
}
}
</style>

View File

@@ -48,8 +48,7 @@ const { questionAnalysis } = useFetchAnalysis(sn.value);
:survey="currentSurvey as SurveyItem"
@post-analysis="postAnalysis(sn as string)"
/>
<!-- 弹窗组件 -->
<!-- 弹窗组件 -->
<el-dialog
:show-close="false"
:close-on-click-modal="false"

View File

@@ -24,7 +24,7 @@
v-if="analysis.head"
/>
</section>
<!-- <section v-else>
<!-- <section v-else>
<empty-container />
</section> -->
</div>
@@ -38,14 +38,17 @@ import ChartMsg from '@/components/Analysis/Index.vue';
import { getTableData } from './hooks/pieSeries';
import YlTable from '@/components/YlTable/Index.vue';
import { ref } from 'vue';
import { screenLayout } from '@/hooks/browser/useScreen';
// questionTypeMap 自己去对应
const showChart = ref([1, 2, 5, 106, 9, 10]);
// 接受上级传递的 questionAnalysis 数据
const questionAnalysis = defineModel<any[]>('questionAnalysis');
const { width } = screenLayout();
// 构建表头
const getTableHeadProps = (values: any[], option: any[]) => {
const getTableHeadProps = (values: any[], option: any[]): TablePropsType[] => {
const head = [];
if (values && values.length > 0) {
@@ -54,12 +57,13 @@ const getTableHeadProps = (values: any[], option: any[]) => {
head.push({
label: item.title,
prop: item.key,
width: 120
width: values.length < 3 ? width.value / values.length : 120
});
}
});
}
if (option && option.length > 0 && option[0].option) {
if (option.length > 0 && option[0].option) {
head.unshift({
label: '选项',
prop: 'option',