feat(layout): 优化首页布局和样式

- 调整了首页的整体布局结构
- 优化了问卷创建、最新问卷和模板市场的样式
- 添加了背景图片和一些样式细节
- 重构了部分组件以提高复用性
This commit is contained in:
陈昱达
2025-03-18 17:02:31 +08:00
parent dcac0348c5
commit ad1455fd73
17 changed files with 352 additions and 104 deletions

View File

@@ -22,6 +22,9 @@
--status-bar-height: 20px;
--sticky-top-height: calc(var(--status-bar-height) + calc(var(--van-nav-bar-height) + 13px));
--van-radius-sm: 16px;
--van-tabs-line-height: 30px;
--van-tabs-bottom-bar-height: 2px;
--van-tabs-bottom-bar-width: 28px;
}
/* semantic color variables for this project */

View File

@@ -17,6 +17,20 @@
background: #f2f2f2;
}
.van-tabs__nav--line.van-tabs__nav--shrink,
.van-tabs__nav--line.van-tabs__nav--complete {
padding-left: 0;
}
.van-tab--grow {
margin: 0 20px 0 0;
padding: 0;
}
.van-tab--grow:last-child {
margin-right: 0;
}
.van-radio-group {
& .van-radio {
.van-radio__icon--checked {

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -138,6 +138,6 @@
content: '\e6a0';
}
.mobilefont-fasong:before {
.mobilefont-fasong::before {
content: '\e647';
}

View File

@@ -3,7 +3,7 @@
<!-- title 标题和搜索栏 -->
<header class="header">
<van-nav-bar
class="navbar-header"
class=""
:title="$route.meta.title"
left-arrow
safe-area-inset-top
@@ -11,7 +11,7 @@
@click-left="goBack"
>
<template #left>
<van-icon name="left-long" class-prefix="mobilefont" size="18" style="color: #fff" />
<van-icon name="left-long" class-prefix="mobilefont" size="18" style="color: #000" />
</template>
</van-nav-bar>
</header>
@@ -36,16 +36,28 @@ function goBack() {
</script>
<style scoped lang="scss">
.common-layout {
background-color: white;
//background-size: contain; /* 或者使用具体的尺寸,如 100% 100% */
//background-position: center; /* 确保图片居中显示 */
//background-repeat: no-repeat;
background-image: url('@/assets/img/home/home-pen.png');
//background-color: white;
color: #333;
}
.van-nav-bar {
//background-color: transparent;
background-image: url('@/assets/img/home/home-pen.png');
}
.header {
position: sticky;
top: 0;
z-index: 1000;
background-color: #a5d380;
//background-color: #a5d380;
//background-image: url('@/assets/img/home/home-pen.png');
.title {
display: flex;
align-items: end;

60
src/layouts/redirect.vue Normal file
View File

@@ -0,0 +1,60 @@
<template>
<div class="common-layout">
<!-- title 标题和搜索栏 -->
<header class="header">
<van-nav-bar
class="navbar-header"
:title="$route.meta.title"
left-arrow
safe-area-inset-top
:border="false"
@click-left="goBack"
>
<template #left>
<van-icon name="left-long" class-prefix="mobilefont" size="18" style="color: #fff" />
</template>
</van-nav-bar>
</header>
<!-- content -->
<RouterView />
</div>
</template>
<script setup>
import { RouterView, useRouter, useRoute } from 'vue-router';
import appBridge from '@/assets/js/appBridge';
const route = useRoute();
const router = useRouter();
function goBack() {
if (window.history.length > 1 && route.name !== 'home') {
router.go(-1);
} else {
appBridge.navigateBack();
}
}
</script>
<style scoped lang="scss">
.common-layout {
background-color: white;
color: #333;
}
.header {
position: sticky;
top: 0;
z-index: 1000;
background-color: #a5d380;
.title {
display: flex;
align-items: end;
justify-content: center;
}
.back-icon {
width: 18px;
height: 18px;
}
}
</style>

View File

@@ -30,13 +30,13 @@ function showModal(options) {
* @param {*} data
* @returns
*/
const canPlanetPublishPSM = function (data) {
const canPlanetPublishPSM = function(data) {
let isFb = true;
let message = '';
let title = '题目设置未完成';
const incompleteQuestionList = [];
data.questions &&
data.questions.forEach((s) => {
data.questions
&& data.questions.forEach((s) => {
if (s.question_type === 101 && s.config.price_gradient.length <= 0) {
isFb = false;
message = 'psm题目未完成设置请设置价格区间后投放';
@@ -60,15 +60,15 @@ const canPlanetPublishPSM = function (data) {
* @param {*} data
* @returns
*/
const canPlanetPublishMxdAndHotArea = function (data) {
const canPlanetPublishMxdAndHotArea = function(data) {
let isFb = true;
let message = '';
const qSteams = [];
const incompleteQuestionList = [];
let type = 0;
let title = '题目设置未完成';
data.questions &&
data.questions.forEach((s) => {
data.questions
&& data.questions.forEach((s) => {
if (s.question_type === 105 && s.config.design_version <= 0) {
isFb = false;
message = 'maxdiff题目未完成设置请生成设计后投放';
@@ -107,14 +107,14 @@ const canPlanetPublishMxdAndHotArea = function (data) {
* @param {*} data
* @returns
*/
const canPlanetPublish3D = function (data) {
const canPlanetPublish3D = function(data) {
{
let canFB = true;
let message = '';
const qSteams = [];
let title = '';
data.questions &&
data.questions.forEach((s) => {
data.questions
&& data.questions.forEach((s) => {
if (QUESTION_TYPE.contains(s.question_type)) {
try {
if (s.config.is_three_dimensions && !s.config.scene) {
@@ -144,15 +144,15 @@ const canPlanetPublish3D = function (data) {
let message = '';
const qSteams = [];
let title = '';
data.questions &&
data.questions.forEach((s) => {
data.questions
&& data.questions.forEach((s) => {
if (QUESTION_TYPE.contains(s.question_type)) {
try {
if (s.config.is_three_dimensions && s.config.is_binding_goods) {
const wares = [];
const _sceneInformation = s.config.scene_information;
const sceneInformation =
typeof _sceneInformation === 'string'
const sceneInformation
= typeof _sceneInformation === 'string'
? JSON.parse(_sceneInformation)
: _sceneInformation;
sceneInformation.shelves.forEach((shelf) => {
@@ -202,14 +202,14 @@ const canPlanetPublish3D = function (data) {
* @param {*} data
* @returns
*/
const canPlanetPublishImage = function (data) {
const canPlanetPublishImage = function(data) {
{
let canFB = true;
let message = '';
const qSteams = [];
let title = '';
data.questions &&
data.questions.forEach((s) => {
data.questions
&& data.questions.forEach((s) => {
if (s.question_type === 13) {
try {
if (s.options.length <= 0 || s.options.some((y) => y.length <= 0)) {
@@ -304,8 +304,8 @@ function canPublishRandom(data, publishType) {
if (!isValidated) {
errors.push({
message:
field.message ||
`请填写"${random.title}"中第${index + 1}组"随机题组"的"${field.name}"`
field.message
|| `请填写"${random.title}"中第${index + 1}组"随机题组"的"${field.name}"`
});
}
});
@@ -384,11 +384,11 @@ function isLoopingLogicValid(data, publishType) {
if (
(data?.cycle_pages || []).every((i) => {
return (
i.question_index &&
i.relation_type !== undefined &&
i.relation_type !== null &&
i.first_page &&
i.last_page
i.question_index
&& i.relation_type !== undefined
&& i.relation_type !== null
&& i.first_page
&& i.last_page
);
})
) {
@@ -412,7 +412,7 @@ function isLoopingLogicValid(data, publishType) {
* @param sn
* @param publishType undefined投放null投放0投放1预览2投放3测试
*/
export const canPlanetPublish = async function (sn, publishType) {
export const canPlanetPublish = async function(sn, publishType) {
const parsedPublishType = !publishType ? 2 : publishType;
const num = window.location.href.indexOf('code=');
let code;

View File

@@ -1,6 +1,7 @@
import { createRouter, createWebHistory } from 'vue-router';
import layout from '@/layouts/index.vue';
import Design from '@/views/Design/Index.vue';
import Redirect from '@/layouts/redirect.vue';
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
@@ -20,7 +21,18 @@ const router = createRouter({
title: '伊调研'
},
component: () => import('../views/Home/Index.vue')
},
}
]
},
{
path: '/redirect',
name: '/redirect',
component: Redirect,
redirect: '/survey',
meta: {
title: '伊调研'
},
children: [
{
path: '/survey',
name: 'survey',

View File

@@ -46,7 +46,7 @@ import { ref } from 'vue';
import RateCharacter from './RateCharacter.vue';
const isPreview = defineModel('isPreview', { default: false, type: Boolean });
/*const props = */ defineProps({
/* const props = */ defineProps({
index: {
type: Number,
default: 0

View File

@@ -9,7 +9,7 @@ import { showFailToast } from 'vant';
const contentShow = ref(false);
const show = ref(false);
onMounted(async() => {
onMounted(async () => {
if (utils.getSessionStorage('xToken')) {
const appToken = utils.getSessionStorage('xToken');
getUserInfo(appToken)
@@ -40,16 +40,21 @@ function create() {
</script>
<template>
<div v-if="contentShow" class="container">
<create-survey :createdNewPage="false" />
<!-- 最新问卷 -->
<last-survey />
<!-- 模板市场 -->
<Market />
<div class="new_survey">
<van-button type="primary" block color="#70B937" @click="create">
<span class="fw-bold">+</span> 新建问卷
</van-button>
<div v-if="contentShow" class="container-home">
<!-- <div class="home-pen">-->
<!-- <img :src="homePen" alt="" />-->
<!-- </div>-->
<div class="container-body">
<create-survey :createdNewPage="false" />
<!-- 最新问卷 -->
<last-survey />
<!-- 模板市场 -->
<Market />
<div class="new_survey">
<van-button type="primary" block color="#70B937" @click="create">
<span class="fw-bold">+</span> 新建问卷
</van-button>
</div>
</div>
</div>
<van-popup v-model:show="show" round closeable position="bottom">
@@ -58,15 +63,47 @@ function create() {
</template>
<style scoped lang="scss">
.container {
padding: 30px 10px 80px;
background: linear-gradient(0deg, #f5f5f5 0%, #f5f5f5 84%, #a5d380 100%);
.container-home {
overflow: hidden;
width: 100%;
//background: #f2f2f2;
//position: relative;
.home-pen {
//height: 200px;
position: absolute;
top: -10px;
left: 0;
z-index: 8;
//width: 100%;
background: #fff;
//background: linear-gradient(180deg, rgba(242, 242, 242, 0) 0%, #ebffe9 100%);
img {
width: 100%;
//height: 200px;
}
}
}
.container-body {
padding: 0 10px 80px;
//background: linear-gradient(0deg, #f5f5f5 0%, #f5f5f5 84%, #a5d380 100%);
& > :first-child {
display: flex;
justify-content: space-around;
border-radius: 6px;
background-color: white;
position: relative;
z-index: 10;
//background-color: white;
margin-top: 10px;
//display: flex;
//justify-content: space-around;
border-radius: 16px;
& > div {
display: flex;
@@ -77,7 +114,7 @@ function create() {
// background-color: #B9F8CF;
padding: 20px 10px;
//padding: 20px 10px;
}
}

View File

@@ -19,8 +19,9 @@ const createdNewPage = defineModel('createdNewPage', {
const createdQuestion = (item) => {
const query = {
group_id: 0,
source: 1,
project_name: `${item.title}问卷 `,
remarks: '',
remarks: '为优化活动服务品质,烦请完成问卷,感谢配合',
scene_code: item.parentCode,
scene_code_info: item.code,
// 很迷茫 模板新增 tag 空数组 非模板 就是k
@@ -104,10 +105,13 @@ onMounted(() => {
</script>
<template>
<van-cell class="create_survey">
<div class="create_survey_title" style="color: #000; text-align: left">请选择新建类型</div>
<van-row>
<van-col
<!-- <div class="home-pen">-->
<!-- <img :src="homePen" alt="" />-->
<!-- </div>-->
<div class="create_survey">
<div class="create_survey_title" style="color: #000; text-align: left">新建问卷</div>
<div class="flex align-center space-between warp">
<div
v-for="survey in surveys"
:key="survey.title"
span="6"
@@ -116,30 +120,68 @@ onMounted(() => {
>
<img :src="survey.image" alt="" width="40" />
<span>{{ survey.title }}</span>
</van-col>
</van-row>
</van-cell>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.home-pen {
//height: 200px;
position: absolute;
top: -10px;
left: 0;
z-index: 8;
//width: 100%;
background: #fff;
img {
width: 100%;
//height: 200px;
}
}
.create_survey {
padding: 15px;
overflow: hidden;
border-radius: 16px;
background-image: url('@/assets/img/home/home-back.png');
background-position: center; /* 确保图片居中显示 */
background-size: contain; /* 或者使用具体的尺寸,如 100% 100% */
background-repeat: no-repeat;
//padding: 15px;
color: #000;
.create_survey_title {
margin-bottom: 15px;
margin: 16px;
color: #000;
font-weight: bold;
font-size: 15px;
font-family: PingFangSC, 'PingFang SC';
}
& .warp {
flex-wrap: wrap;
}
}
.survey {
display: flex;
//margin-right: 30px;
flex-direction: column;
align-items: center;
justify-content: space-around;
//flex: 1;
width: 25%;
//margin: 0 16px;
text-align: center;
//justify-content: space-around;
span {
margin-top: 8px;

View File

@@ -2,16 +2,18 @@
<!-- 问卷 -->
<div class="last-survey">
<div class="survey_header">
<div class="flex space-between">
<div class="flex space-between align-center">
<div class="flex align-center">
<p class="fw-bold">最新问卷</p>
<p class="survey_header_tag">NEW</p>
</div>
<p @click="$router.push('/survey')">全部问卷 ></p>
<p class="survey-all" @click="$router.push('/survey')">
全部问卷 <van-icon name="arrow"></van-icon>
</p>
</div>
</div>
<div class="survey_con">
<div class="flex">
<div class="flex align-center space-between mb10">
<div>
<div class="survey_con_title flex align-center">
<p class="mr-10 fw-bold">{{ survey.project_name }}</p>
@@ -37,9 +39,14 @@
</div>
</div>
</div>
<img src="" alt="" />
<div class="survey_item_status">
<img v-if="survey.status === 0" :src="editPng" alt="" />
<img v-else-if="survey.status === 1" :src="publishPng" alt="" />
<img v-else-if="survey.status === 2" :src="endPng" alt="" />
<!-- <span class="survey_item_info_status_text">-{{ item.status_txt }}-</span>-->
</div>
</div>
<div class="survey_remark">{{ survey.remarks }}</div>
<div v-if="survey.remarks" class="survey_remark">{{ survey.remarks }}</div>
</div>
</div>
</template>
@@ -47,11 +54,14 @@
<script setup>
import { ref, onMounted } from 'vue';
import { getSurveysPage } from '@/api/home/index.js';
import editPng from '@/assets/img/publish/edit.png';
import publishPng from '@/assets/img/publish/publish.png';
import endPng from '@/assets/img/publish/end.png';
const survey = ref({
project_name: ''
});
const fetchSurveys = async() => {
const fetchSurveys = async () => {
const params = {
page: 1,
per_page: 10,
@@ -83,35 +93,55 @@ onMounted(() => {
<style scoped lang="scss">
.last-survey {
margin-top: 6px;
padding: 10px 3px 3px;
border-radius: 10px;
margin-top: 10px;
padding: 9px 3px 3px;
border-radius: 16px;
background-color: #27d6ac;
color: #fff;
.survey_header {
padding: 0 10px;
padding: 0 15px;
font-size: 15px;
& .survey-all {
font-size: 13px;
}
.survey_header_tag {
width: 32px;
//width: 32px;
height: 15px;
margin-left: 3px;
border-radius: 4px;
margin-left: 7px;
border-radius: 5px;
background-color: #fff;
color: #27d6ac;
font-size: 10px;
font-size: 11px;
line-height: 15px;
text-align: center;
}
}
.survey_con {
margin-top: 10px;
position: relative;
margin-top: 5px;
padding-bottom: 5px;
border-radius: 10px;
background: #fff;
color: #000;
.survey_item_status {
position: absolute;
top: 0;
right: -15px;
overflow: hidden;
width: 85px;
height: 80px;
& img {
width: 100%;
height: 100%;
}
}
img {
height: 12px;
margin-right: 3px;
@@ -138,18 +168,19 @@ onMounted(() => {
.survey_con_label {
margin-left: 10px;
color: #666;
font-size: 10px !important;
div {
& div {
margin-right: 5px;
font-size: 11px;
}
}
.survey_remark {
box-sizing: border-box;
margin: 10px 0 0 10px;
padding: 10px 5px;
border-radius: 10px;
margin: 0 10px 1px;
padding: 8px;
border-radius: 8px;
background: #f6f7f8;
color: #828282;
font-weight: 400;
font-style: normal;
@@ -157,4 +188,8 @@ onMounted(() => {
}
}
}
.mb10 {
margin-bottom: 10px;
}
</style>

View File

@@ -3,9 +3,10 @@
<div class="market">
<div class="market_title fw-bold">模板市场</div>
<van-tabs v-model:active="active" @change="getMarketInfo">
<van-tab v-for="item in marketList" :key="item.title" :title="item.title"> </van-tab>
<van-tab v-for="item in marketList" :key="item.title" :title="item.title">
<market-item :info="marketInfo" />
</van-tab>
</van-tabs>
<market-item :info="marketInfo"></market-item>
</div>
<div class="more">
<p>-更多模板期待您的探索-</p>
@@ -70,11 +71,12 @@ onMounted(() => {
.market {
margin-top: 10px;
padding: 15px 15px 0;
border-radius: 10px;
padding: 10px 12px 0;
border-radius: 16px;
background: #fff;
.market_title {
margin-bottom: 5px;
color: #000;
font-size: 15px;
}

View File

@@ -26,11 +26,12 @@
style="color: #c1c1c1"
/>
</div>
<div class="desc flex space-between">
<div class="desc flex space-between align-center">
<div>
<p>创建人</p>
<p class="fw-bold">{{ item.created_user }}</p>
</div>
<div class="line"></div>
<div>
<p>引用次数</p>
<p class="fw-bold">{{ item.quote_nums }}</p>
@@ -93,23 +94,36 @@ onMounted(() => {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 10px 0;
}
.market-item {
//margin-right: 4%;
flex: 1;
box-sizing: border-box;
width: 49%;
margin-bottom: 12px;
padding: 10px;
border-radius: 8px;
background: #fff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
max-width: 50%;
margin-bottom: 5px;
margin-left: 7px;
padding: 15px 12px;
border: 1px solid #fff;
border-radius: 10px;
background: rgba(241, 241, 241, 0.39);
// 偶数项的 margin-right 设置为 0
&:nth-child(even) {
&:nth-child(n-1) {
margin-right: 7px;
margin-left: 0;
}
&:nth-child(2n) {
margin-right: 0;
}
//width: 49%;
//margin-bottom: 12px;
//padding: 10px;
//border-radius: 8px;
//background: #fff;
//box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
.content {
.title {
display: flex;
@@ -137,6 +151,13 @@ onMounted(() => {
color: #666;
font-size: 12px;
.line {
width: 1px;
height: 20px;
background: #979797;
opacity: 0.1;
}
div p:last-child {
color: #000;
}

View File

@@ -1,10 +1,10 @@
<template>
<div class="survey-search">
<van-search
v-model="searchValue"
class="theme-background"
:border="false"
background="#71b73c"
v-model="searchValue"
@blur="blurs"
@search="blurs"
></van-search>
@@ -68,20 +68,26 @@
<div class="survey_item_action">
<!-- <el-space direction="horizontal">-->
<div>
<el-button @click="deleteItem(item)"> 删除</el-button>
<el-button @click="copyItem(item)"> 复制</el-button>
<el-button :disabled="item.source === 0" @click="deleteItem(item)"> 删除</el-button>
<el-button :disabled="item.source === 0" @click="copyItem(item)"> 复制</el-button>
<el-button style="border: 2px solid #71b73c" @click="toPreview(item)">
<el-text style="color: #71b73c">预览</el-text>
</el-button>
<el-button color="#6fb937" @click="toPublish(item)">
<el-button color="#6fb937" :disabled="item.source === 0" @click="toPublish(item)">
<el-text style="color: white">开启投放</el-text>
</el-button>
</div>
<el-dropdown placement="top-end" trigger="click" active-color="#ee0a24">
<Io5EllipsisHorizontalSharp />
<el-dropdown
v-if="item.source === 1"
placement="top-end"
trigger="click"
active-color="#71b73c"
>
<!-- <Io5EllipsisHorizontalSharp />-->
<van-icon class-prefix="mobilefont" name="gengduo" size="0.7rem"></van-icon>
<template #dropdown>
<el-dropdown-menu
active-color="#ee0a24"
active-color="#71b73c"
:close-on-click-overlay="false"
:close-on-click-outside="false"
>
@@ -100,7 +106,6 @@
<script setup>
import { ref, onMounted } from 'vue';
import { getSurveysPage, copySurveys, deleteSurveys, saveTemplates } from '@/api/home/index.js';
import { Io5EllipsisHorizontalSharp } from 'vue-icons-plus/io5';
import { showDialog, showConfirmDialog, showFailToast, showSuccessToast, showToast } from 'vant';
import { useRouter } from 'vue-router';
const router = useRouter();
@@ -281,7 +286,7 @@ onMounted(() => {
//background: linear-gradient(to bottom, $theme-color 200px, #f2f2f2 300px);
.new-survey_item {
margin: 0 10px 10px 10px;
margin: 0 10px 10px;
padding: 10px 0 8px 7px;
border-radius: 8px;
background-color: white;
@@ -373,7 +378,7 @@ onMounted(() => {
}
.new-survey_item + .new-survey_item {
margin: 0 10px 10px 10px;
margin: 0 10px 10px;
}
}
</style>

View File

@@ -360,6 +360,7 @@ import { basicQuesTypeList } from '@/utils/common';
import { useRoute, useRouter } from 'vue-router';
import YLPicker from '@/components/YLPicker.vue';
import { getPages } from '@/utils/public';
import { showConfirmDialog } from 'vant';
// 获取 Store 实例
const counterStore = useCounterStore();
@@ -565,6 +566,10 @@ watch(
// 保存 目前没有任何逻辑可以执行所有保存
const saveAs = () => {
// 保存所有
showConfirmDialog({
message: '问卷保存成功',
showCancelButton: false
});
};
// 投放
const publishQuestion = () => {