feat: 优化首页和搜索页面样式及功能

1. 修复了搜索页面中的类型错误和构建问题
   - 修复了 v-model 的类型断言问题
   - 优化了搜索关键字的处理逻辑

2. 优化了首页布局和样式
   - 添加了渐变背景样式
   - 调整了搜索栏和轮播图的间距
   - 优化了卡片圆角和边距

3. 改进了广告页面(AD)的UI
   - 优化了布局和样式
   - 添加了响应式设计
   - 改进了按钮样式和交互

4. 重构了主题和样式
   - 提取了常用样式到 theme.scss
   - 使用 SCSS 变量管理颜色和尺寸
   - 优化了组件间的样式复用

5. 修复了路由和布局问题
   - 更新了路由配置
   - 优化了重定向布局
   - 修复了导航栏显示问题
This commit is contained in:
Huangzhe
2025-05-18 17:50:11 +08:00
parent 51a9fd085e
commit 6852188cf1
23 changed files with 228 additions and 78 deletions

View File

@@ -58,6 +58,7 @@ export function getSurveyTemplate(sn, params) {
params
});
}
export function getSurveysDetail(sn, params) {
return request({
url: `/console/surveys/${sn}`,
@@ -65,6 +66,7 @@ export function getSurveysDetail(sn, params) {
params
});
}
export function changeStatus(params) {
return request({
url: `/console/surveys/${params.sn}/status`,

View File

@@ -1 +0,0 @@
@import "tailwindcss";

View File

@@ -2,4 +2,8 @@ $theme-color: #71b73c;
$card-radius: 10px;
$gap: 10px;
$nav-header-color: #f2f2f2;
$nav-color: #EEFDE8;
$nav-color: #eefde8;
%seach-gradient {
background: linear-gradient(to bottom, #f8fef5, #f3f3f3);
}

View File

@@ -41,13 +41,13 @@ const changeChart = (i: Number) => {
<template>
<section>
<div v-if="dimension">
<!-- <div v-if="dimension">
<van-radio-group v-model="index" @change="changeChart">
<van-radio v-for="(item, index) in tableData.option" :name="index">{{
item.option
}}</van-radio>
</van-radio-group>
</div>
</div> -->
<!-- 图表部分 -->
<div

View File

@@ -40,6 +40,8 @@ function handleSearchActino() {
</script>
<style scoped lang="scss">
@use '@/assets/css/theme';
.search-container {
:deep(.van-search) {
padding: 0;
@@ -64,7 +66,7 @@ function handleSearchActino() {
display: grid;
grid-template-columns: 1fr 60px;
padding: 0 10px;
margin: 0 10px;
// margin: 0 10px;
overflow: hidden;
}
</style>

View File

@@ -7,10 +7,10 @@ const option = {
// tooltip: {
// trigger: 'item'
// },
// legend: {
// orient: 'vertical',
// left: 'left'
// },
legend: {
orient: 'horizontal',
bottom: 'left'
},
label: {
formatter: '{b},{c}'
},

View File

@@ -0,0 +1,4 @@
### 作用
这个文件夹是用来获取异步数据内容的,由于之前异步获取的变量都在 hooks 方法外的定义,相当于 pinia 的定义变量。
没有规范好,所以有了这个文件夹,异步获取的数据在这里重新定义使用。后续会持续更改重复请求到这里的。

View File

@@ -0,0 +1,40 @@
import { getSurveysDetail } from '@/api/design';
import { getSurveysPage } from '@/api/home';
import { ref, type Ref } from 'vue';
// 异步函数获取数据
function fetchSurveys({ page = 1, perPage = 3 } = {}) {
const surveys = ref<SurveyItem[]>([]);
const params = {
page: page,
per_page: perPage,
group_id: 0
};
getSurveysPage(params).then(({ data }) => {
if (data.code !== 0) return;
surveys.value = data.data;
});
return {
surveys
};
}
/**
* 通过 sn 获取单个问卷的数据
* @param sn {string}
* @returns { {currentSurvey: Ref<SurveyItem> }}
*/
function fetchSingleSurvey(sn: string) {
const currentSurvey = ref<SurveyItem>();
getSurveysDetail(sn).then(({ data }) => {
if (data.code !== 0) return;
currentSurvey.value = data.data;
});
return { currentSurvey };
}
export { fetchSurveys, fetchSingleSurvey };

View File

@@ -3,7 +3,7 @@
<!-- title 标题和搜索栏 -->
<header class="header">
<van-nav-bar
class="navbar-header"
:class="[$route.meta.pureBGC ? '' : 'navbar-header']"
:title="$route.meta.title"
left-arrow
safe-area-inset-top
@@ -16,7 +16,7 @@
</van-nav-bar>
</header>
<!-- content -->
<div class="redirect-body">
<div class="redirect-body" :style="{ background: $route.meta.pureBGC ? 'white' : '' }">
<RouterView />
</div>
</div>
@@ -27,6 +27,7 @@ import { RouterView, useRoute } from 'vue-router';
import appBridge from '@/assets/js/appBridge';
const route = useRoute();
console.log(route.meta.pureBGC);
// const router = useRouter();
function goBack() {

View File

@@ -13,12 +13,11 @@ import '@/style/utils.scss';
import appBridge from '@/assets/js/appBridge';
import VConsole from 'vconsole';
import './assets/css/main.scss';
import './assets/css/main.css';
const app = createApp(App);
if (import.meta.env.VITE_APP_ENV !== 'production') {
const vconsole = new VConsole();
app.use(vconsole);
// app.use(vconsole);
}
declare global {

View File

@@ -14,16 +14,7 @@ const router = createRouter({
meta: {
title: '伊调研'
},
children: [
{
path: '/home',
name: 'home',
meta: {
title: '伊调研'
},
component: () => import('../views/Home/Index.vue')
}
]
children: []
},
{
path: '/redirect',
@@ -34,6 +25,14 @@ const router = createRouter({
title: '伊调研'
},
children: [
{
path: '/home',
name: 'home',
meta: {
title: '伊调研'
},
component: () => import('../views/Home/Index.vue')
},
{
path: '/survey',
name: 'survey',
@@ -87,27 +86,34 @@ const router = createRouter({
component: () =>
import(
'@/views/Home/components/CreateSurvey/components/IntelligentGeneration/Index.vue'
)
}, {
)
},
{
path: '/search',
name: 'search',
meta: {},
component: () => import('@/views/HomeSearch/Index.vue')
}, {
},
{
path: '/template-market',
name: 'templateMarket',
component: () => import('@/views/Home/components/Market/Index.vue')
}, {
},
{
path: '/analysis',
name: 'analysis',
meta: {
title:"统计分析"
title: '统计分析'
},
component: () => import('@/views/Survey/views/Analysis/Index.vue')
}, {
},
{
path: '/ad',
name: 'ad',
meta: {},
meta: {
pureBGC: true,
title: "伊调研123123"
},
component: () => import('@/views/AD/Index.vue')
}
]

4
src/utils/theme/cell.ts Normal file
View File

@@ -0,0 +1,4 @@
export const cellWithoutPadding = {
'--van-cell-vertical-padding': 0,
'--van-cell-horizontal-padding': 0
};

View File

@@ -4,30 +4,59 @@ import { bannerInfo } from '@/views/AD/hooks/useAD';
<template>
<section class="banner-container">
<section>
<h2>{{ bannerInfo.title }}</h2>
<section class="msg-info">
<h3>{{ bannerInfo?.title }}</h3>
<el-space spacer="|">
<el-text>{{ bannerInfo.characters }}</el-text>
<el-text>{{ bannerInfo.publish_time }}</el-text>
<el-text type="success">{{ bannerInfo.characters }}</el-text>
<el-text>{{ bannerInfo.publish_time.split('-').join('.') }} 发布</el-text>
</el-space>
</section>
<article>
<section v-if="bannerInfo.type===0">{{ bannerInfo.synopsis }}</section>
<section v-if="bannerInfo.type===1">
<!-- 根据banner的类型使用不同的方式渲染 -->
<section v-if="bannerInfo?.type === 0">
<p>{{ bannerInfo.synopsis }}</p>
</section>
<section v-else-if="bannerInfo?.type === 1">
<el-image :src="bannerInfo.file_address"></el-image>
</section>
<section v-if="bannerInfo.type===2">
<section v-else-if="bannerInfo?.type === 2">
<video width="320" height="240" controls>
<source :src="bannerInfo.file_address" type="video/mp4">
<source :src="bannerInfo.file_address" type="video/mp4" />
</video>
</section>
</article>
<section v-if="bannerInfo.is_display_button" style="width:100vw;display:flex; justify-content: center; align-items:center; position: fixed; bottom: 0">
<section
v-if="bannerInfo?.is_display_button"
style="
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
position: fixed;
left: 0;
bottom: 10px;
"
>
<!-- 立即进入 -->
<el-button style="width:98vw">
{{ bannerInfo.button_name }}
<el-button style="width: 95%; height: 50px; border-radius: 15px" color="#70b937">
<el-text style="color: white">{{ bannerInfo.button_name }}</el-text>
</el-button>
</section>
</section>
</template>
<style lang="scss" scoped>
.banner-container {
padding: 20px;
.msg-info {
font-family: PingFangSC-Medium;
h2 {
font-size: 16px;
line-height: 22px;
font-weight: 500;
}
}
}
</style>

View File

@@ -1,5 +1,4 @@
<script setup>
import Market from './components/Market/Index.vue';
import CreateSurvey from './components/CreateSurvey/Index.vue';
import NewSurvey from './components/NewSurvey/index.vue';
import { onMounted, ref } from 'vue';
@@ -10,10 +9,12 @@ import appBridge from '@/assets/js/appBridge';
import ImageSlider from './components/ImageSlider/Index.vue';
import SearchBar from '@/components/Search/Index.vue';
import Navigation from '@/components/Navigation/Index.vue';
import MineTask from '@/views/Home/components/MineTask/Index.vue';
import router from '@/router';
const contentShow = ref(false);
const keyword = ref('');
onMounted(async () => {
if (appBridge.isInReactNative()) {
const appToken = utils.getSessionStorage('xToken');
@@ -50,9 +51,13 @@ function handleSearchClick() {
<div v-if="contentShow" class="container-home">
<div class="container-body">
<!-- 搜索栏 -->
<search-bar :value="''" @click="handleSearchClick" />
<section class="search">
<search-bar :value="keyword" @click="handleSearchClick" />
</section>
<!-- 首页轮播图 -->
<image-slider style="margin-top: 10px" />
<section class="slider">
<image-slider />
</section>
<create-survey :createdNewPage="false" />
<!-- 最新问卷 -->
<!--<last-survey/>-->
@@ -69,7 +74,21 @@ function handleSearchClick() {
</template>
<style scoped lang="scss">
@use '@/assets/css/theme';
.container-body {
padding: 0 10px 80px;
}
.search {
margin: 0 -10px 0 -10px;
padding: 10px;
// background: linear-gradient(to bottom, #f8fef5, #f3f3f3);
@extend %seach-gradient;
}
.slider {
overflow: hidden;
border-radius: theme.$card-radius;
}
</style>

View File

@@ -151,7 +151,7 @@ onMounted(() => {
.create_survey {
overflow: hidden;
margin-top: 25px;
margin-top: 10px;
border-radius: 16px;
background-image: url('@/assets/img/home/item-back.png');
background-position: center; /* 确保图片居中显示 */

View File

@@ -0,0 +1,31 @@
<script setup lang="ts">
import SurveyAnalysis from '@/views/Survey/views/Analysis/Index.vue';
import { fetchSurveys } from '@/hooks/request/useSurvey';
import { cellWithoutPadding } from '@/utils/theme/cell';
const { surveys } = fetchSurveys();
</script>
<template>
<van-cell :style="cellWithoutPadding" class="swipe-container">
<template #extra>
<van-swipe class="my-swipe" indicator-color="white" :loop="false">
<van-swipe-item :key="survey.sn" v-for="survey in surveys">
<section style="width: 90vw">
<h3 style="margin: 10px 0 -10px 10px">我的任务</h3>
<survey-analysis :sn="survey.sn" :disable-search="true" />
</section>
</van-swipe-item>
</van-swipe>
</template>
</van-cell>
</template>
<style lang="scss" scoped>
@use '@/assets/css/theme';
.swipe-container {
margin-top: theme.$gap;
border-radius: theme.$card-radius;
}
</style>

View File

@@ -1,9 +1,6 @@
<template>
<div class="new_survey">
<div v-if="$route.name !== 'home'">
<!-- <van-button type="primary" block color="#70B937" @click="create">
<span class="fw-bold">+</span> 新建问卷
</van-button> -->
<van-popup v-model:show="show" round closeable position="bottom" teleport="#app">
<create-question :createdNewPage="false"></create-question>
</van-popup>
@@ -27,13 +24,15 @@ const show = ref(false);
</script>
<style scoped lang="scss">
@use '@/assets/css/theme';
.new_survey {
position: fixed;
bottom: 50px;
left: 0;
box-sizing: border-box;
width: 100vw;
border-radius: 12px;
border-radius: theme.$card-radius;
background: #fff;
}
</style>

View File

@@ -10,6 +10,9 @@ import { onMounted } from 'vue';
import ImageSlider from '../Home/components/ImageSlider/Index.vue';
import { banners } from '@/views/HomeSearch/Hooks/useSurveySearch';
// 确保 keyword 是字符串类型,提供默认值
const searchKeyword = keyword.value || '';
onMounted(() => {
// 当页面取消挂载时,重置页面展示的状态
loading.value = false;
@@ -19,7 +22,7 @@ onMounted(() => {
<template>
<section class="search-container">
<div class="search">
<search :focus="true" v-model:value="keyword as string" :search="handleSearch" />
<search :focus="true" v-model:value="searchKeyword" :search="handleSearch" />
</div>
<!-- 广告区域 -->
<image-slider :banners="banners" v-if="banners?.length" />
@@ -64,9 +67,10 @@ onMounted(() => {
.search-container {
.search {
padding-top: 10px;
padding: 10px;
padding-bottom: 20px;
background: linear-gradient(to bottom, #f8fef5, #f3f3f3);
// background: linear-gradient(to bottom, #f8fef5, #f3f3f3);
@extend %seach-gradient;
}
// 搜索结果外部布局

View File

@@ -1,14 +1,7 @@
<template>
<div class="survey-search">
<!-- <van-search
v-model="searchValue"
placeholder="请输入关键词"
class="theme-background"
:border="false"
background="#fff"
@search="blurs"
></van-search> -->
<nav-search v-model:value="searchValue" @search="blurs" />
<!-- <nav-search v-model:value="searchValue" @search="blurs" /> -->
<nav-search v-model:value="searchValue" @click="() => $router.push({ name: 'search' })" />
</div>
<div class="new-survey-container">
<div style="margin-bottom: 80px">
@@ -73,11 +66,11 @@ onMounted(() => {
}
.survey-search {
@extend %seach-gradient;
padding: 0 10px;
position: sticky;
top: 0;
z-index: 1000;
width: 100%;
padding: 0;
background-color: theme.$nav-header-color;
}

View File

@@ -1,5 +1,4 @@
<script setup lang="ts">
import { currentSurvey, fetchSingleSurvey } from '@/views/Survey/hooks/useSurveyData';
import SurveyItem from '@/views/Survey/components/SurveyItem.vue';
import LogicInfo from '@/views/Survey/views/Analysis/components/LogicInfo/Index.vue';
import { useRoute } from 'vue-router';
@@ -11,20 +10,29 @@ import {
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 route = useRoute();
/**
* 如果当前问卷的数据不存在,重新获取数据
*/
if (!currentSurvey.value) fetchSingleSurvey(route.query.sn as string);
// 从上级传递的 sn 号码
const sn = defineModel<string>('sn');
// 是否禁用搜索
const disableSearch = defineModel<boolean>('disableSearch', { default: false });
// sn 的获取方式,默认优先从上级传递的,如果没有尝试从 router 获取
sn.value = sn.value ?? (route.query.sn as string);
const { currentSurvey } = fetchSingleSurvey(sn.value);
// 重置 message 信息
aiInsightsConfig.value.message = '';
useFetchAnalysis(route.query.sn as string);
useFetchAnalysis(sn.value);
</script>
<template>
<search-bar
v-if="!disableSearch"
:value="''"
style="margin: 15px 10px 5px 10px"
@click="() => $router.push({ name: 'search' })"
@@ -36,7 +44,7 @@ useFetchAnalysis(route.query.sn as string);
<survey-item
:is-analysis="true"
:survey="currentSurvey as SurveyItem"
@post-analysis="postAnalysis(route.query.sn as string)"
@post-analysis="postAnalysis(sn as string)"
/>
<!-- 弹窗组件 -->
@@ -46,18 +54,18 @@ useFetchAnalysis(route.query.sn as string);
</template>
</van-cell>
<!-- 逻辑配额随机题组配额循环体配额 表格部分-->
<logic-info />
<logic-info :sn="sn" />
<van-cell v-if="aiInsightsConfig.message.length > 0" class="ai-insight" :key="route">
<van-cell v-if="aiInsightsConfig.message.length > 0" class="ai-insight">
<template #extra>
<!-- ai 洞察部分内容 -->
<div v-html="aiInsightsConfig.message" />
</template>
</van-cell>
<!-- 图表分析部分 -->
<van-cell class="analysis">
<template #extra>
<!-- 图表分析部分 -->
<analysis-info />
</template>
</van-cell>

View File

@@ -1,24 +1,26 @@
<template>
<div>
<section v-for="analysis in questionAnalysis" :key="analysis.stem" class="mt10">
<!-- {{ analysis }} -->
<!-- 问题标题 -->
<el-tag type="success" size="small">{{
questionTypeMap.get(analysis.question_type as number)
}}</el-tag>
{{ analysis.stem }}
<!-- 问题图表部分 -->
<chart-msg
v-if="showChart.includes(analysis.question_type)"
:dimension="analysis.option && analysis.option[0].option"
:analysis="analysis"
/>
<!-- 问题表格部分 -->
<yl-table
:props="getTableHeadProps(analysis.head, analysis.option)"
:data="getTableData(analysis)"
v-if="analysis.head"
/>
<!-- {{ analysis }}-->
</section>
</div>
</template>

View File

@@ -12,7 +12,10 @@ import YlTable from '@/components/YlTable/Index.vue';
const route = useRoute();
updateDate(route.query.sn as string);
// 从上级传递的 sn 号码
const sn = defineModel<string>('sn');
// sn 的获取方式,默认优先从上级传递的,如果没有尝试从 router 获取
sn.value = sn.value ?? (route.query.sn as string);
const activeTab = ref(0);
const tabs = ref<LogicInfoTab[]>([

View File

@@ -1,7 +1,8 @@
import { surveyAnalysis } from '@/api/survey';
import { onMounted, ref } from 'vue';
import { ref } from 'vue';
import { analysisInsights, checkAnalysisStatus, getAnalysis } from '@/api/anslysis/aiInsight';
const currentSn = ref<string>('');
const aiAnalysisData = ref();
type AiInsightType = {
visible: boolean;
@@ -76,4 +77,4 @@ async function useFetchAnalysis(sn: string) {
// console.log(`question analysis`, questionAnalysis.value);
}
export { useFetchAnalysis, questionAnalysis, aiAnalysisData };
export { useFetchAnalysis, questionAnalysis, aiAnalysisData, currentSn };