feat: 完善首页布局并添加组件
1. 添加首页轮播图组件 ImageSlider。 2. 添加我的任务组件 MineTask,展示用户任务事项。 3. 调整首页组件结构,优化页面展示效果。 4. 更新 TypeScript 版本至 5.8.3。 5. 将 tsconfig.app.json 中的 module 修改为 ESNext,适配新的模块加载方式。 6. 在文档中强调使用 Vue3 的 `<script setup>` 语法。 7. 添加 Echarts依赖
This commit is contained in:
@@ -4,4 +4,5 @@ trigger: always_on
|
||||
|
||||
1. always use chinese to response
|
||||
2. 尽量使用 element-plus 的组件内容
|
||||
3. 开发的时候尽量少使用 css 内容,能不用尽量不用
|
||||
3. 开发的时候尽量少使用 css 内容,能不用尽量不用
|
||||
4. 使用 vue3 的 <script setup> 语法, 拒绝使用以前的语法
|
||||
@@ -22,6 +22,7 @@
|
||||
"core-js": "^3.41.0",
|
||||
"cos-js-sdk-v5": "^1.8.7",
|
||||
"dotenv": "^16.4.7",
|
||||
"echarts": "^5.6.0",
|
||||
"element-plus": "^2.7.8",
|
||||
"js-base64": "^3.7.7",
|
||||
"lodash": "^4.17.21",
|
||||
@@ -62,7 +63,7 @@
|
||||
"postcss-pxtorem": "^6.1.0",
|
||||
"sass": "^1.85.1",
|
||||
"sass-loader": "^16.0.5",
|
||||
"typescript": "~5.4.0",
|
||||
"typescript": "^5.8.3",
|
||||
"unplugin-auto-import": "^0.18.6",
|
||||
"unplugin-vue-components": "^0.27.5",
|
||||
"vite": "^6.0.0",
|
||||
@@ -77,4 +78,4 @@
|
||||
"Android >= 4.4",
|
||||
"iOS >= 8"
|
||||
]
|
||||
}
|
||||
}
|
||||
48
src/hooks/chart/data/pie.ts
Normal file
48
src/hooks/chart/data/pie.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
const option = {
|
||||
// title: {
|
||||
// text: 'Referer of a Website',
|
||||
// subtext: 'Fake Data',
|
||||
// left: 'center'
|
||||
// },
|
||||
// tooltip: {
|
||||
// trigger: 'item'
|
||||
// },
|
||||
// legend: {
|
||||
// orient: 'vertical',
|
||||
// left: 'left'
|
||||
// },
|
||||
series: [
|
||||
{
|
||||
name: 'Access From',
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: [
|
||||
{ value: 1048, name: 'Search Engine' },
|
||||
{ value: 735, name: 'Direct' },
|
||||
{ value: 580, name: 'Email' },
|
||||
{ value: 484, name: 'Union Ads' },
|
||||
{ value: 300, name: 'Video Ads' }
|
||||
],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const pieOption = ref<Partial<typeof option>>(option);
|
||||
|
||||
// 删除左侧的预览图
|
||||
export function deleteLegend() {
|
||||
delete pieOption.value.legend;
|
||||
}
|
||||
|
||||
export function deleteTitle() {
|
||||
delete pieOption.value.title;
|
||||
}
|
||||
6
src/hooks/chart/types/index.d.ts
vendored
Normal file
6
src/hooks/chart/types/index.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
type optsType =
|
||||
| {
|
||||
title: boolean;
|
||||
legend: boolean;
|
||||
}
|
||||
| {};
|
||||
64
src/hooks/chart/usePieChart.ts
Normal file
64
src/hooks/chart/usePieChart.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { onMounted, ref, type ShallowRef, watch } from 'vue';
|
||||
import type { ECOption } from '@/utils/echarts';
|
||||
import { chart } from '@/utils/echarts';
|
||||
import { deleteLegend, deleteTitle, pieOption } from './data/pie';
|
||||
|
||||
type dataOption = Partial<ECOption['data']>;
|
||||
|
||||
const pieChart = ref();
|
||||
/**
|
||||
* 定义数据集
|
||||
*/
|
||||
const series = ref<dataOption[]>([
|
||||
{
|
||||
name: 'Access From',
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: [
|
||||
{ value: 1048, name: 'Search Engine' },
|
||||
{ value: 735, name: 'Direct' },
|
||||
{ value: 580, name: 'Email' },
|
||||
{ value: 484, name: 'Union Ads' },
|
||||
{ value: 300, name: 'Video Ads' }
|
||||
],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
/**
|
||||
* 饼图的 option
|
||||
*/
|
||||
const option = ref(pieOption);
|
||||
|
||||
function useSetPieChart(
|
||||
dom: Readonly<ShallowRef<HTMLSpanElement | null>>,
|
||||
data: any[],
|
||||
opts: optsType = {}
|
||||
): void {
|
||||
for (let item in opts) {
|
||||
if (item === 'legend') !opts[item] && deleteLegend();
|
||||
else if (item === 'title') !opts[item] && deleteTitle();
|
||||
}
|
||||
|
||||
// 检测边界范围 dom 和 data 是否存在
|
||||
onMounted(() => {
|
||||
console.log(dom);
|
||||
if (!dom || data.length === 0) return;
|
||||
// 在 dom 挂载之后,显示饼图
|
||||
pieChart.value = chart.init(dom.value);
|
||||
pieChart.value.setOption(option.value, opts);
|
||||
});
|
||||
|
||||
// 如果 data 变动,重新生成图表
|
||||
watch(series, (value) => {
|
||||
pieChart.value.setOption(value, opts);
|
||||
});
|
||||
}
|
||||
|
||||
export { useSetPieChart };
|
||||
@@ -14,7 +14,6 @@ import '@/style/utils.scss';
|
||||
import appBridge from '@/assets/js/appBridge';
|
||||
import VConsole from 'vconsole';
|
||||
import './assets/css/main.scss';
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
if (import.meta.env.VITE_APP_ENV !== 'production') {
|
||||
|
||||
54
src/utils/echarts/index.ts
Normal file
54
src/utils/echarts/index.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
// 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。
|
||||
import type { ComposeOption } from 'echarts/core';
|
||||
import * as echarts from 'echarts/core';
|
||||
import type { PieSeriesOption } from 'echarts/charts';
|
||||
// 引入 饼状图
|
||||
import { PieChart } from 'echarts/charts';
|
||||
// 组件类型的定义后缀都为 ComponentOption
|
||||
import type {
|
||||
DatasetComponentOption,
|
||||
GridComponentOption,
|
||||
TitleComponentOption,
|
||||
TooltipComponentOption,
|
||||
LegendComponentOption
|
||||
} from 'echarts/components';
|
||||
// 引入标题,提示框,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
|
||||
import {
|
||||
DatasetComponent,
|
||||
GridComponent,
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
TransformComponent,
|
||||
LegendComponent
|
||||
} from 'echarts/components';
|
||||
|
||||
// 标签自动布局、全局过渡动画等特性
|
||||
import { LabelLayout, UniversalTransition } from 'echarts/features';
|
||||
// 使用 SVG 渲染器
|
||||
import { SVGRenderer } from 'echarts/renderers';
|
||||
|
||||
// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
|
||||
type ECOption = ComposeOption<
|
||||
| PieSeriesOption
|
||||
| TitleComponentOption
|
||||
| TooltipComponentOption
|
||||
| GridComponentOption
|
||||
| DatasetComponentOption
|
||||
| LegendComponentOption
|
||||
>;
|
||||
|
||||
// 注册必须的组件
|
||||
echarts.use([
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
DatasetComponent,
|
||||
TransformComponent,
|
||||
LabelLayout,
|
||||
UniversalTransition,
|
||||
SVGRenderer,
|
||||
PieChart,
|
||||
LegendComponent
|
||||
]);
|
||||
|
||||
export { echarts as chart, type ECOption };
|
||||
@@ -8,6 +8,9 @@ import utils from '@/assets/js/common';
|
||||
import { getUserInfo } from '@/api/common/index.js';
|
||||
import { showFailToast } from 'vant';
|
||||
import appBridge from '@/assets/js/appBridge';
|
||||
import ImageSlider from './components/ImageSlider/Index.vue';
|
||||
import MineTask from '@/views/Home/components/MineTask/Index.vue';
|
||||
|
||||
const contentShow = ref(false);
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -42,12 +45,16 @@ onMounted(async () => {
|
||||
<div v-if="contentShow" class="container-home">
|
||||
<div class="container-body">
|
||||
<create-survey :createdNewPage="false" />
|
||||
<!-- 首页轮播图 -->
|
||||
<image-slider />
|
||||
<!-- 最新问卷 -->
|
||||
<last-survey />
|
||||
<!-- <last-survey />-->
|
||||
<!-- 模板市场 -->
|
||||
<Market />
|
||||
<!-- <Market />-->
|
||||
<!--底部新建问卷-->
|
||||
<NewSurvey></NewSurvey>
|
||||
<NewSurvey />
|
||||
|
||||
<mine-task />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
43
src/views/Home/components/ImageSlider/Index.vue
Normal file
43
src/views/Home/components/ImageSlider/Index.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<el-carousel height="160px" :interval="1000" arrow="never">
|
||||
<el-carousel-item v-for="item in sliderItems" :key="item.id">
|
||||
<el-row :gutter="20" justify="space-between">
|
||||
<el-col :span="16">
|
||||
<el-row >
|
||||
<el-col :span="4">
|
||||
<el-icon :size="24" color="#409EFF"><DataAnalysis /></el-icon>
|
||||
</el-col>
|
||||
<el-col :span="20">
|
||||
<h3>{{ item.title }}</h3>
|
||||
<p>{{ item.subtitle }}</p>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</el-card>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { DataAnalysis } from '@element-plus/icons-vue';
|
||||
|
||||
const sliderItems = ref<SliderItem[]>([
|
||||
{
|
||||
id: 1,
|
||||
title: '伊调研功能上新',
|
||||
subtitle: '从问卷投放到数据报分析一站搞定!',
|
||||
image: '/src/assets/slider/slider1.png'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '数据分析功能',
|
||||
subtitle: '强大的数据分析能力,助您洞察市场趋势',
|
||||
image: '/src/assets/slider/slider2.png'
|
||||
}
|
||||
]);
|
||||
</script>
|
||||
6
src/views/Home/components/ImageSlider/types/slider.d.ts
vendored
Normal file
6
src/views/Home/components/ImageSlider/types/slider.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
interface SliderItem {
|
||||
id: number;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
image: string;
|
||||
}
|
||||
146
src/views/Home/components/MineTask/Index.vue
Normal file
146
src/views/Home/components/MineTask/Index.vue
Normal file
@@ -0,0 +1,146 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, useTemplateRef } from 'vue';
|
||||
import { showToast } from 'vant';
|
||||
import { useSetPieChart } from '@/hooks/chart/usePieChart';
|
||||
|
||||
// 饼图 dom 结构
|
||||
const pieChart = useTemplateRef<HTMLSpanElement>('pieChart');
|
||||
|
||||
useSetPieChart(pieChart, [1], { title: false, ledge: false });
|
||||
|
||||
// 任务数据
|
||||
const taskData = ref({
|
||||
title: '问卷A',
|
||||
progress: 100,
|
||||
deadline: '2025-03-31至04-01',
|
||||
creator: '张三',
|
||||
creationMethod: '移动端',
|
||||
creationTime: '2025-03-04',
|
||||
responseCount: 10,
|
||||
responseRate: '10%',
|
||||
submissionRate: '10%',
|
||||
status: '草稿中'
|
||||
});
|
||||
|
||||
// 图表数据
|
||||
const chartData = ref([
|
||||
{ name: '选项1', value: 66.77, color: '#F56C6C' },
|
||||
{ name: '选项2', value: 33.33, color: '#F7BA2A' },
|
||||
{ name: '选项3', value: 0, color: '#67C23A' },
|
||||
{ name: '选项4', value: 0, color: '#409EFF' }
|
||||
]);
|
||||
|
||||
// 导航按钮点击事件
|
||||
const handlePrev = () => {
|
||||
showToast('点击了上一个问题');
|
||||
};
|
||||
|
||||
const handleNext = () => {
|
||||
showToast('点击了下一个问题');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<van-card class="task-card">
|
||||
<!-- 状态标签 -->
|
||||
<template #tags>
|
||||
<el-tag type="success" effect="plain">{{ taskData.status }}</el-tag>
|
||||
</template>
|
||||
|
||||
<!-- 标题部分 -->
|
||||
<template #title>
|
||||
<van-space>
|
||||
<span style="font-size: 18px; font-weight: bold">{{ taskData.title }}</span>
|
||||
<span style="color: #409eff">{{ taskData.progress }}%</span>
|
||||
<span style="color: #606266; font-size: 14px">| {{ taskData.deadline }}</span>
|
||||
</van-space>
|
||||
</template>
|
||||
|
||||
<!-- 创建者信息 -->
|
||||
<template #desc>
|
||||
<el-space spacer="|">
|
||||
<span>{{ taskData.creator }}</span>
|
||||
<span>{{ taskData.creationMethod }}</span>
|
||||
<span>创建时间:{{ taskData.creationTime }}</span>
|
||||
</el-space>
|
||||
</template>
|
||||
|
||||
<template #price>
|
||||
<!-- 空的价格区域 -->
|
||||
</template>
|
||||
|
||||
<!-- 卡片内容 -->
|
||||
<template #bottom>
|
||||
<van-space direction="vertical" fill style="width: 100%; padding-top: 16px">
|
||||
<!-- 任务统计 -->
|
||||
<van-grid :column-num="3">
|
||||
<van-grid-item>
|
||||
<van-space direction="vertical" fill>
|
||||
<span style="color: #67c23a; font-size: 18px; font-weight: bold">{{
|
||||
taskData.responseCount
|
||||
}}</span>
|
||||
<span style="font-size: 14px; color: #909399">回收数量</span>
|
||||
</van-space>
|
||||
</van-grid-item>
|
||||
<van-grid-item>
|
||||
<van-space direction="vertical" fill>
|
||||
<span style="color: #67c23a; font-size: 18px; font-weight: bold">{{
|
||||
taskData.responseRate
|
||||
}}</span>
|
||||
<span style="font-size: 14px; color: #909399">回收数量进度</span>
|
||||
</van-space>
|
||||
</van-grid-item>
|
||||
<van-grid-item>
|
||||
<van-space direction="vertical" fill>
|
||||
<span style="color: #67c23a; font-size: 18px; font-weight: bold">{{
|
||||
taskData.submissionRate
|
||||
}}</span>
|
||||
<span style="font-size: 14px; color: #909399">投放时间进度</span>
|
||||
</van-space>
|
||||
</van-grid-item>
|
||||
</van-grid>
|
||||
|
||||
<!-- 问题部分 -->
|
||||
<van-divider />
|
||||
|
||||
<p>1. 能描述您为自己或家人购买这款产品A的可能性吗?(单选)</p>
|
||||
|
||||
<!-- 图表部分 -->
|
||||
<div style="display: flex; justify-content: center; margin: 16px 0">
|
||||
<span ref="pieChart" style="width: 100%; height: 300px"></span>
|
||||
</div>
|
||||
|
||||
<!-- 选项列表 -->
|
||||
<van-cell-group inset>
|
||||
<van-cell v-for="(item, index) in chartData" :key="index">
|
||||
<template #title>
|
||||
<van-tag :color="item.value > 0 ? item.color : '#dcdfe6'" plain>
|
||||
{{ item.name }}
|
||||
</van-tag>
|
||||
</template>
|
||||
<template #value>
|
||||
<span>{{ item.value.toFixed(2) }}%</span>
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
</van-space>
|
||||
<!-- 导航按钮 左右按钮-->
|
||||
<!-- <div style="position: fixed; left: 16px; top: 50%; transform: translateY(-50%)">-->
|
||||
<!-- <van-button round icon="arrow-left" @click="handlePrev" />-->
|
||||
<!-- </div>-->
|
||||
|
||||
<!-- <div style="position: fixed; right: 16px; top: 50%; transform: translateY(-50%)">-->
|
||||
<!-- <van-button round icon="arrow" @click="handleNext" />-->
|
||||
<!-- </div>-->
|
||||
</template>
|
||||
</van-card>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.task-card {
|
||||
//margin: 16px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
</style>
|
||||
@@ -15,7 +15,8 @@
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"allowJs": true,
|
||||
"baseUrl": ".",
|
||||
"module": "commonjs",
|
||||
"module": "ESNext",
|
||||
// "module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
|
||||
Reference in New Issue
Block a user