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:
@@ -5,3 +5,4 @@ trigger: always_on
|
|||||||
1. always use chinese to response
|
1. always use chinese to response
|
||||||
2. 尽量使用 element-plus 的组件内容
|
2. 尽量使用 element-plus 的组件内容
|
||||||
3. 开发的时候尽量少使用 css 内容,能不用尽量不用
|
3. 开发的时候尽量少使用 css 内容,能不用尽量不用
|
||||||
|
4. 使用 vue3 的 <script setup> 语法, 拒绝使用以前的语法
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
"core-js": "^3.41.0",
|
"core-js": "^3.41.0",
|
||||||
"cos-js-sdk-v5": "^1.8.7",
|
"cos-js-sdk-v5": "^1.8.7",
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
|
"echarts": "^5.6.0",
|
||||||
"element-plus": "^2.7.8",
|
"element-plus": "^2.7.8",
|
||||||
"js-base64": "^3.7.7",
|
"js-base64": "^3.7.7",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
@@ -62,7 +63,7 @@
|
|||||||
"postcss-pxtorem": "^6.1.0",
|
"postcss-pxtorem": "^6.1.0",
|
||||||
"sass": "^1.85.1",
|
"sass": "^1.85.1",
|
||||||
"sass-loader": "^16.0.5",
|
"sass-loader": "^16.0.5",
|
||||||
"typescript": "~5.4.0",
|
"typescript": "^5.8.3",
|
||||||
"unplugin-auto-import": "^0.18.6",
|
"unplugin-auto-import": "^0.18.6",
|
||||||
"unplugin-vue-components": "^0.27.5",
|
"unplugin-vue-components": "^0.27.5",
|
||||||
"vite": "^6.0.0",
|
"vite": "^6.0.0",
|
||||||
|
|||||||
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 appBridge from '@/assets/js/appBridge';
|
||||||
import VConsole from 'vconsole';
|
import VConsole from 'vconsole';
|
||||||
import './assets/css/main.scss';
|
import './assets/css/main.scss';
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
if (import.meta.env.VITE_APP_ENV !== 'production') {
|
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 { getUserInfo } from '@/api/common/index.js';
|
||||||
import { showFailToast } from 'vant';
|
import { showFailToast } from 'vant';
|
||||||
import appBridge from '@/assets/js/appBridge';
|
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);
|
const contentShow = ref(false);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@@ -42,12 +45,16 @@ onMounted(async () => {
|
|||||||
<div v-if="contentShow" class="container-home">
|
<div v-if="contentShow" class="container-home">
|
||||||
<div class="container-body">
|
<div class="container-body">
|
||||||
<create-survey :createdNewPage="false" />
|
<create-survey :createdNewPage="false" />
|
||||||
|
<!-- 首页轮播图 -->
|
||||||
|
<image-slider />
|
||||||
<!-- 最新问卷 -->
|
<!-- 最新问卷 -->
|
||||||
<last-survey />
|
<!-- <last-survey />-->
|
||||||
<!-- 模板市场 -->
|
<!-- 模板市场 -->
|
||||||
<Market />
|
<!-- <Market />-->
|
||||||
<!--底部新建问卷-->
|
<!--底部新建问卷-->
|
||||||
<NewSurvey></NewSurvey>
|
<NewSurvey />
|
||||||
|
|
||||||
|
<mine-task />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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",
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"module": "commonjs",
|
"module": "ESNext",
|
||||||
|
// "module": "commonjs",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user