feat: update AigcPhotoCard, FaqHelpCard, MapNavigationCard, NoticeCard, ScenicImageCard with new layouts, styles, and mock data; enhance user interaction and visual appeal
This commit is contained in:
@@ -1,31 +1,27 @@
|
||||
<template>
|
||||
<CardShell class="aigc-photo-card p-16" variant="soft">
|
||||
<view class="aigc-photo-card__body flex flex-items-center gap-12">
|
||||
<view class="aigc-photo-card__icon flex flex-items-center flex-justify-center w-44 h-44 rounded-16 bg-CCFBF1 font-size-22 font-900">{{ data.icon }}</view>
|
||||
<view class="aigc-photo-card__content flex-full">
|
||||
<view class="aigc-photo-card__title font-size-16 font-900">{{ data.title }}</view>
|
||||
<view class="aigc-photo-card__subtitle color-0F766E font-size-11 font-700">{{ data.subtitle }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="aigc-photo-card__preview grid grid-cols-3 gap-8 mt-14">
|
||||
<MediaFrame
|
||||
v-for="(image, index) in images"
|
||||
:key="image || index"
|
||||
class="aigc-photo-card__image h-82 rounded-14"
|
||||
:src="image"
|
||||
<view
|
||||
class="aigc-photo-card relative rounded-24 overflow-hidden w-full"
|
||||
:class="{ 'is-disabled': disabled }"
|
||||
@click="handleAction"
|
||||
>
|
||||
<image
|
||||
class="aigc-photo-card__image block w-full"
|
||||
:src="data.cover"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="aigc-photo-card__shade"></view>
|
||||
|
||||
<view class="aigc-photo-card__play flex flex-items-center flex-justify-center rounded-full">
|
||||
<view class="aigc-photo-card__triangle"></view>
|
||||
</view>
|
||||
|
||||
<view class="aigc-photo-card__title color-white font-size-16 font-900">
|
||||
{{ data.title }}
|
||||
</view>
|
||||
<view class="aigc-photo-card__button mt-14 p-12 rounded-16 color-white bg-0F766E font-size-13 font-900 text-center" :class="{ 'is-disabled': disabled }" @click="handleAction">
|
||||
{{ data.buttonText }}
|
||||
</view>
|
||||
</CardShell>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from "vue";
|
||||
import CardShell from "../SharedVisual/CardShell.vue";
|
||||
import MediaFrame from "../SharedVisual/MediaFrame.vue";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
@@ -39,8 +35,6 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits(["select", "action"]);
|
||||
|
||||
const images = computed(() => props.data.images || []);
|
||||
|
||||
const handleAction = () => {
|
||||
if (props.disabled) return;
|
||||
emit("select", props.data);
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
export default {
|
||||
icon: "AI",
|
||||
title: "生成一张景区合照",
|
||||
subtitle: "上传照片后生成同框纪念照",
|
||||
buttonText: "开始生成",
|
||||
images: [
|
||||
"https://images.unsplash.com/photo-1500534314209-a25ddb2bd429?auto=format&fit=crop&w=400&q=80",
|
||||
"https://images.unsplash.com/photo-1529156069898-49953e39b3ac?auto=format&fit=crop&w=400&q=80",
|
||||
"https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=400&q=80",
|
||||
],
|
||||
id: "xiaoqikong-flying",
|
||||
title: "沉浸式飞越 · 小七孔",
|
||||
cover: "https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=1000&q=80",
|
||||
action: {
|
||||
type: "play",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,35 +1,54 @@
|
||||
.aigc-photo-card {
|
||||
background: linear-gradient(145deg, #ffffff 0%, #f0fdfa 100%);
|
||||
height: 272px;
|
||||
background: #0f172a;
|
||||
}
|
||||
|
||||
.aigc-photo-card__body {
|
||||
.aigc-photo-card:active {
|
||||
opacity: 0.86;
|
||||
}
|
||||
|
||||
.aigc-photo-card__icon {
|
||||
color: #0f766e;
|
||||
}
|
||||
|
||||
.aigc-photo-card__content {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.aigc-photo-card__title {
|
||||
color: #134e4a;
|
||||
}
|
||||
|
||||
.aigc-photo-card__subtitle {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.aigc-photo-card__preview {
|
||||
.aigc-photo-card.is-disabled {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.aigc-photo-card__image {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.aigc-photo-card__button {
|
||||
.aigc-photo-card__shade {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(15, 23, 42, 0.38);
|
||||
}
|
||||
|
||||
.aigc-photo-card__button.is-disabled {
|
||||
opacity: 0.55;
|
||||
.aigc-photo-card__play {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 76px;
|
||||
height: 76px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.48);
|
||||
background: rgba(255, 255, 255, 0.28);
|
||||
transform: translate(-50%, -50%);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.aigc-photo-card__triangle {
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: 6px;
|
||||
border-top: 17px solid transparent;
|
||||
border-bottom: 17px solid transparent;
|
||||
border-left: 23px solid #fff;
|
||||
}
|
||||
|
||||
.aigc-photo-card__title {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 72px;
|
||||
text-align: center;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0;
|
||||
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.32);
|
||||
}
|
||||
|
||||
@@ -1,36 +1,35 @@
|
||||
<template>
|
||||
<CardShell class="faq-help-card p-16" variant="soft">
|
||||
<view class="faq-help-card__header flex flex-justify-between gap-12">
|
||||
<view>
|
||||
<view class="faq-help-card__title color-1E293B font-size-16 font-900">{{ data.title }}</view>
|
||||
<view class="faq-help-card__subtitle color-94A3B8 font-size-11 font-700">{{ data.subtitle }}</view>
|
||||
<view class="faq-help-card bg-white rounded-24 w-full">
|
||||
<view class="faq-help-card__header flex flex-items-center">
|
||||
<view class="faq-help-card__icon flex flex-items-center flex-justify-center rounded-full font-size-16 font-900">
|
||||
{{ data.icon }}
|
||||
</view>
|
||||
<BadgePill v-if="data.badge" :label="data.badge" tone="blue" />
|
||||
<view class="faq-help-card__title color-1E293B font-size-20 font-900">
|
||||
{{ data.title }}
|
||||
</view>
|
||||
<view class="faq-help-card__list flex flex-col gap-8">
|
||||
</view>
|
||||
|
||||
<view class="faq-help-card__divider"></view>
|
||||
|
||||
<view class="faq-help-card__list">
|
||||
<view
|
||||
v-for="item in questions"
|
||||
:key="item.id || item.question"
|
||||
class="faq-help-card__item p-12 rounded-16 bg-F8FAFC"
|
||||
:class="{ 'is-open': openedId === item.id }"
|
||||
@click="toggle(item)"
|
||||
:key="item.id || item.text"
|
||||
class="faq-help-card__item flex flex-items-center"
|
||||
:class="{ 'is-disabled': disabled }"
|
||||
@click="handleSelect(item)"
|
||||
>
|
||||
<view class="faq-help-card__question flex flex-justify-between gap-12 color-1E293B font-size-13 font-900">
|
||||
<text>{{ item.question }}</text>
|
||||
<text class="faq-help-card__arrow color-2563EB">{{ openedId === item.id ? "−" : "+" }}</text>
|
||||
<view class="faq-help-card__question color-475569 font-size-18 font-900 ellipsis-1 flex-full">
|
||||
{{ item.text }}
|
||||
</view>
|
||||
<view v-if="openedId === item.id" class="faq-help-card__answer color-64748B font-size-12 font-700">
|
||||
{{ item.answer }}
|
||||
<view class="faq-help-card__arrow color-CBD5E1 font-size-22 font-900 flex-shrink-0">›</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</CardShell>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from "vue";
|
||||
import CardShell from "../SharedVisual/CardShell.vue";
|
||||
import BadgePill from "../SharedVisual/BadgePill.vue";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
@@ -45,12 +44,10 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits(["select"]);
|
||||
|
||||
const openedId = ref("");
|
||||
const questions = computed(() => props.data.questions || []);
|
||||
|
||||
const toggle = (item) => {
|
||||
const handleSelect = (item) => {
|
||||
if (props.disabled) return;
|
||||
openedId.value = openedId.value === item.id ? "" : item.id;
|
||||
emit("select", item);
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
export default {
|
||||
title: "常见问题",
|
||||
subtitle: "快速了解游览和服务信息",
|
||||
badge: "FAQ",
|
||||
id: "faq-help",
|
||||
icon: "?",
|
||||
title: "您可能还想了解",
|
||||
questions: [
|
||||
{ id: "q1", question: "景区几点停止入园?", answer: "通常闭园前 1 小时停止入园,节假日以现场公告为准。" },
|
||||
{ id: "q2", question: "可以携带宠物吗?", answer: "多数室内区域不支持宠物进入,导盲犬等服务犬除外。" },
|
||||
{ id: "q3", question: "下雨还能游玩吗?", answer: "小雨可游览,雨后水景更好,但请注意栈道湿滑。" },
|
||||
{
|
||||
id: "shuttle-time",
|
||||
text: "观光车末班车发车时间",
|
||||
},
|
||||
{
|
||||
id: "power-bank",
|
||||
text: "景区内哪里有共享充电宝",
|
||||
},
|
||||
{
|
||||
id: "pet-policy",
|
||||
text: "可以带宠物入园吗",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1,36 +1,54 @@
|
||||
.faq-help-card {
|
||||
padding: 34px 30px 26px;
|
||||
border: 1px solid rgba(226, 232, 240, 0.72);
|
||||
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.05);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.faq-help-card__header {
|
||||
margin-bottom: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.faq-help-card__icon {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
margin-right: 10px;
|
||||
border: 2px solid #10b981;
|
||||
color: #10b981;
|
||||
line-height: 18px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.faq-help-card__title {
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
.faq-help-card__subtitle {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.faq-help-card__list {
|
||||
.faq-help-card__divider {
|
||||
height: 1px;
|
||||
margin: 24px 0 18px;
|
||||
background: #f1f5f9;
|
||||
}
|
||||
|
||||
.faq-help-card__item {
|
||||
min-height: 42px;
|
||||
}
|
||||
|
||||
.faq-help-card__item.is-open {
|
||||
background: #eff6ff;
|
||||
.faq-help-card__item:active {
|
||||
opacity: 0.86;
|
||||
}
|
||||
|
||||
.faq-help-card__item.is-disabled {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.faq-help-card__question {
|
||||
min-width: 0;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.faq-help-card__arrow {
|
||||
font-size: 18px;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.faq-help-card__answer {
|
||||
margin-top: 8px;
|
||||
line-height: 20px;
|
||||
width: 22px;
|
||||
margin-left: 16px;
|
||||
text-align: right;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
@@ -1,24 +1,40 @@
|
||||
<template>
|
||||
<CardShell class="map-navigation-card" variant="soft">
|
||||
<view class="map-navigation-card__map">
|
||||
<MediaFrame class="map-navigation-card__image h-178" :src="data.mapImage" />
|
||||
<view class="map-navigation-card__pin color-334155 font-size-10 font-900">{{ data.pinText }}</view>
|
||||
<view class="map-navigation-card bg-white rounded-24 overflow-hidden w-full">
|
||||
<view class="map-navigation-card__media">
|
||||
<image
|
||||
class="map-navigation-card__image block w-full"
|
||||
:src="data.image"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="map-navigation-card__badge flex flex-items-center color-white font-size-12 font-900">
|
||||
<text class="map-navigation-card__badge-icon">{{ badge.icon }}</text>
|
||||
<text>{{ badge.text }}</text>
|
||||
</view>
|
||||
<view class="map-navigation-card__bar flex flex-items-center gap-12">
|
||||
</view>
|
||||
|
||||
<view class="map-navigation-card__content flex flex-items-center">
|
||||
<view class="map-navigation-card__info flex-full">
|
||||
<view class="map-navigation-card__title color-1E293B font-size-14 font-900 ellipsis-1">{{ data.title }}</view>
|
||||
<view class="map-navigation-card__distance color-94A3B8 font-size-11 font-800 ellipsis-1">{{ data.distance }}</view>
|
||||
<view class="map-navigation-card__title color-1E293B font-size-18 font-900 ellipsis-1">
|
||||
{{ data.title }}
|
||||
</view>
|
||||
<view class="map-navigation-card__distance color-94A3B8 font-size-14 font-900 ellipsis-1">
|
||||
{{ data.distance }}
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="map-navigation-card__button flex flex-items-center flex-justify-center bg-0F172A color-white font-size-18 font-900"
|
||||
:class="{ 'is-disabled': disabled }"
|
||||
@click="handleAction"
|
||||
>
|
||||
<text class="map-navigation-card__button-icon">{{ action.icon }}</text>
|
||||
<text>{{ action.text }}</text>
|
||||
</view>
|
||||
<view class="map-navigation-card__button rounded-50 color-white bg-0F172A font-size-12 font-900" :class="{ 'is-disabled': disabled }" @click="handleAction">
|
||||
{{ data.buttonText }}
|
||||
</view>
|
||||
</view>
|
||||
</CardShell>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import CardShell from "../SharedVisual/CardShell.vue";
|
||||
import MediaFrame from "../SharedVisual/MediaFrame.vue";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
@@ -33,6 +49,9 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits(["select", "action"]);
|
||||
|
||||
const badge = computed(() => props.data.badge || {});
|
||||
const action = computed(() => props.data.action || {});
|
||||
|
||||
const handleAction = () => {
|
||||
if (props.disabled) return;
|
||||
emit("select", props.data);
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
export default {
|
||||
mapImage: "https://images.unsplash.com/photo-1524661135-423995f22d0b?auto=format&fit=crop&w=900&q=80",
|
||||
pinText: "目的地",
|
||||
title: "前往翠谷瀑布",
|
||||
distance: "距你 500m · 步行约 8 分钟",
|
||||
buttonText: "导航",
|
||||
id: "xiaoqikong-bridge-nav",
|
||||
title: "前往小七孔桥",
|
||||
image: "https://images.unsplash.com/photo-1500534314209-a25ddb2bd429?auto=format&fit=crop&w=1000&q=80",
|
||||
distance: "步行约 450 米",
|
||||
badge: {
|
||||
icon: "⌖",
|
||||
text: "目的地",
|
||||
},
|
||||
action: {
|
||||
icon: "↗",
|
||||
text: "导航",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,22 +1,38 @@
|
||||
.map-navigation-card__map {
|
||||
.map-navigation-card {
|
||||
border: 1px solid rgba(226, 232, 240, 0.72);
|
||||
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.05);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.map-navigation-card__media {
|
||||
position: relative;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.map-navigation-card__image {
|
||||
height: 248px;
|
||||
}
|
||||
|
||||
.map-navigation-card__pin {
|
||||
.map-navigation-card__badge {
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
left: 18px;
|
||||
padding: 5px 9px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
left: 16px;
|
||||
bottom: 16px;
|
||||
height: 34px;
|
||||
padding: 0 12px;
|
||||
border-radius: 8px;
|
||||
background: rgba(15, 23, 42, 0.72);
|
||||
line-height: 34px;
|
||||
}
|
||||
|
||||
.map-navigation-card__bar {
|
||||
padding: 0 14px 14px;
|
||||
.map-navigation-card__badge-icon {
|
||||
margin-right: 6px;
|
||||
font-size: 15px;
|
||||
line-height: 15px;
|
||||
}
|
||||
|
||||
.map-navigation-card__content {
|
||||
min-height: 96px;
|
||||
padding: 20px 26px 22px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.map-navigation-card__info {
|
||||
@@ -24,16 +40,31 @@
|
||||
}
|
||||
|
||||
.map-navigation-card__title {
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.map-navigation-card__distance {
|
||||
margin-top: 4px;
|
||||
margin-top: 6px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.map-navigation-card__button {
|
||||
padding: 9px 14px;
|
||||
width: 108px;
|
||||
height: 58px;
|
||||
margin-left: 18px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.map-navigation-card__button:active {
|
||||
opacity: 0.86;
|
||||
}
|
||||
|
||||
.map-navigation-card__button.is-disabled {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.map-navigation-card__button-icon {
|
||||
margin-right: 8px;
|
||||
font-size: 19px;
|
||||
line-height: 19px;
|
||||
}
|
||||
|
||||
@@ -1,56 +1,74 @@
|
||||
<template>
|
||||
<view class="notice-card">
|
||||
<CardShell v-if="!activeNotice" class="notice-card__list p-16" variant="soft">
|
||||
<view class="notice-card__header flex flex-justify-between gap-12">
|
||||
<view>
|
||||
<view class="notice-card__title color-1E293B font-size-16 font-900">{{ data.title }}</view>
|
||||
<view class="notice-card__subtitle color-94A3B8 font-size-11 font-700">{{ data.subtitle }}</view>
|
||||
<view class="notice-card w-full">
|
||||
<view v-if="!detailOpen" class="notice-card__summary bg-FFFBEB rounded-24 w-full">
|
||||
<view class="notice-card__summary-title color-B45309 font-size-16 font-900">
|
||||
{{ summary.title }}
|
||||
</view>
|
||||
<BadgePill v-if="data.badge" :label="data.badge" tone="rose" />
|
||||
<view class="notice-card__summary-content color-D97706 font-size-14 font-900">
|
||||
{{ summary.content }}
|
||||
</view>
|
||||
<view class="notice-card__summary-footer flex flex-items-center flex-justify-between">
|
||||
<view class="notice-card__summary-time color-D97706 font-size-12 font-900">
|
||||
{{ summary.publishTime }}
|
||||
</view>
|
||||
<view
|
||||
v-for="item in notices"
|
||||
:key="item.id || item.title"
|
||||
class="notice-card__item flex gap-12 border-bottom-F1F5F9"
|
||||
class="notice-card__summary-link color-B45309 font-size-14 font-900"
|
||||
:class="{ 'is-disabled': disabled }"
|
||||
@click="openNotice(item)"
|
||||
@click="openDetail"
|
||||
>
|
||||
<view class="notice-card__item-body flex-full">
|
||||
<view class="notice-card__item-title color-1E293B font-size-13 font-900 ellipsis-1">{{ item.title }}</view>
|
||||
<view class="notice-card__item-desc color-94A3B8 font-size-11 font-700 ellipsis-2">{{ item.summary }}</view>
|
||||
{{ summary.actionText }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="notice-card__item-date color-E11D48 font-size-10 font-900">{{ item.date }}</view>
|
||||
</view>
|
||||
</CardShell>
|
||||
|
||||
<DetailShell
|
||||
v-else
|
||||
:title="activeNotice.title"
|
||||
:tag="activeNotice.type"
|
||||
tone="rose"
|
||||
@back="closeNotice"
|
||||
>
|
||||
<view class="notice-card__detail p-16">
|
||||
<MediaFrame v-if="activeNotice.cover" class="notice-card__cover h-154" :src="activeNotice.cover" />
|
||||
<view class="notice-card__meta color-94A3B8 font-size-11 font-800">{{ activeNotice.date }}</view>
|
||||
<view v-else class="notice-card__detail bg-white rounded-24 overflow-hidden w-full">
|
||||
<view class="notice-card__detail-head flex flex-items-center">
|
||||
<view
|
||||
v-for="(paragraph, index) in paragraphs"
|
||||
:key="index"
|
||||
class="notice-card__paragraph color-475569 font-size-13 font-700"
|
||||
class="notice-card__back flex flex-items-center flex-justify-center rounded-full flex-shrink-0"
|
||||
@click="closeDetail"
|
||||
>
|
||||
{{ paragraph }}
|
||||
‹
|
||||
</view>
|
||||
<view class="notice-card__head-title color-1E293B font-size-18 font-900">
|
||||
{{ detail.navTitle }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<image
|
||||
class="notice-card__image block w-full"
|
||||
:src="detail.image"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
|
||||
<view class="notice-card__detail-body">
|
||||
<view class="notice-card__detail-title color-1E293B font-size-20 font-900">
|
||||
{{ detail.title }}
|
||||
</view>
|
||||
<view class="notice-card__time-pill flex flex-items-center bg-F8FAFC color-475569 font-size-13 font-900">
|
||||
<text class="notice-card__time-icon">{{ detail.timeIcon }}</text>
|
||||
<text>{{ detail.publishTime }}</text>
|
||||
</view>
|
||||
<view class="notice-card__divider"></view>
|
||||
<view
|
||||
v-for="paragraph in paragraphs"
|
||||
:key="paragraph.id"
|
||||
class="notice-card__paragraph color-475569 font-size-14 font-800"
|
||||
>
|
||||
<text
|
||||
v-for="segment in paragraph.segments"
|
||||
:key="segment.text"
|
||||
:class="{ 'notice-card__strong': segment.strong }"
|
||||
>
|
||||
{{ segment.text }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</DetailShell>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from "vue";
|
||||
import CardShell from "../SharedVisual/CardShell.vue";
|
||||
import DetailShell from "../SharedVisual/DetailShell.vue";
|
||||
import BadgePill from "../SharedVisual/BadgePill.vue";
|
||||
import MediaFrame from "../SharedVisual/MediaFrame.vue";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
@@ -63,20 +81,22 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["select", "back"]);
|
||||
const emit = defineEmits(["select", "back", "action"]);
|
||||
|
||||
const activeNotice = ref(null);
|
||||
const notices = computed(() => props.data.notices || []);
|
||||
const paragraphs = computed(() => activeNotice.value?.paragraphs || []);
|
||||
const detailOpen = ref(false);
|
||||
const summary = computed(() => props.data.summary || {});
|
||||
const detail = computed(() => props.data.detail || {});
|
||||
const paragraphs = computed(() => detail.value.paragraphs || []);
|
||||
|
||||
const openNotice = (item) => {
|
||||
const openDetail = () => {
|
||||
if (props.disabled) return;
|
||||
activeNotice.value = item;
|
||||
emit("select", item);
|
||||
detailOpen.value = true;
|
||||
emit("select", props.data);
|
||||
emit("action", props.data);
|
||||
};
|
||||
|
||||
const closeNotice = () => {
|
||||
activeNotice.value = null;
|
||||
const closeDetail = () => {
|
||||
detailOpen.value = false;
|
||||
emit("back");
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,24 +1,49 @@
|
||||
export default {
|
||||
title: "景区公告",
|
||||
subtitle: "重要通知与运营提醒",
|
||||
badge: "公告",
|
||||
notices: [
|
||||
id: "wolongtan-trail-notice",
|
||||
summary: {
|
||||
title: "卧龙潭",
|
||||
content: "卧龙潭排队严重。",
|
||||
publishTime: "发布时间:2025年7月12日 09:30",
|
||||
actionText: "详情",
|
||||
},
|
||||
detail: {
|
||||
navTitle: "交通公告",
|
||||
image: "https://images.unsplash.com/photo-1500534314209-a25ddb2bd429?auto=format&fit=crop&w=1000&q=80",
|
||||
title: "卧龙潭至鸳鸯湖步道临时封闭通知",
|
||||
timeIcon: "◷",
|
||||
publishTime: "发布时间:2025年7月12日 09:30",
|
||||
paragraphs: [
|
||||
{
|
||||
id: "notice-1",
|
||||
title: "观光车运营时间调整",
|
||||
summary: "因道路维护,今日 16:30 后部分站点临时调整。",
|
||||
date: "今日",
|
||||
type: "运营",
|
||||
cover: "https://images.unsplash.com/photo-1500534314209-a25ddb2bd429?auto=format&fit=crop&w=900&q=80",
|
||||
paragraphs: ["今日 16:30 后,水上森林站点将临时调整至游客中心东侧。", "已购票游客可凭原票乘坐接驳车,请根据现场指引有序排队。"],
|
||||
id: "rain",
|
||||
segments: [
|
||||
{
|
||||
text: "因近日持续降雨导致景区内水位明显上升,为保障游客人身安全,景区管理部门决定自即日起临时封闭",
|
||||
},
|
||||
{
|
||||
id: "notice-2",
|
||||
title: "雨后栈道防滑提醒",
|
||||
summary: "部分亲水路段地面湿滑,请穿防滑鞋并照看儿童。",
|
||||
date: "昨天",
|
||||
type: "安全",
|
||||
paragraphs: ["雨后栈道区域湿滑,请勿奔跑或翻越护栏。", "如遇临时封闭,请服从现场工作人员安排。"],
|
||||
text: "卧龙潭至鸳鸯湖沉溪步道",
|
||||
strong: true,
|
||||
},
|
||||
{
|
||||
text: ",封闭时间视水位情况另行通知。",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "route",
|
||||
segments: [
|
||||
{
|
||||
text: "受影响游客可改走景区主干道绕行,全程增加约15分钟。观光车服务正常运营,建议优先乘车前往鸳鸯湖区域。",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "reopen",
|
||||
segments: [
|
||||
{
|
||||
text: "如水位下降恢复安全标准,景区将第一时间通过广播及公告重新开放步道,请游客留意景区实时公告。",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -2,59 +2,122 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.notice-card__list {
|
||||
.notice-card__summary,
|
||||
.notice-card__detail {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.notice-card__header {
|
||||
margin-bottom: 12px;
|
||||
.notice-card__summary {
|
||||
padding: 16px 20px 14px;
|
||||
border: 1px solid #fde68a;
|
||||
box-shadow: 0 8px 22px rgba(245, 158, 11, 0.08);
|
||||
}
|
||||
|
||||
.notice-card__title {
|
||||
.notice-card__summary-title {
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.notice-card__subtitle {
|
||||
margin-top: 4px;
|
||||
.notice-card__summary-content {
|
||||
margin-top: 6px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.notice-card__item {
|
||||
padding: 13px 0;
|
||||
.notice-card__summary-footer {
|
||||
min-width: 0;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.notice-card__item:last-child {
|
||||
border-bottom: none;
|
||||
.notice-card__summary-time {
|
||||
min-width: 0;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.notice-card__item.is-disabled {
|
||||
.notice-card__summary-link {
|
||||
margin-left: 16px;
|
||||
line-height: 18px;
|
||||
text-decoration: underline;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.notice-card__summary-link:active {
|
||||
opacity: 0.82;
|
||||
}
|
||||
|
||||
.notice-card__summary-link.is-disabled {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.notice-card__item-body {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.notice-card__item-title {
|
||||
}
|
||||
|
||||
.notice-card__item-desc {
|
||||
margin-top: 5px;
|
||||
line-height: 17px;
|
||||
}
|
||||
|
||||
.notice-card__item-date {
|
||||
}
|
||||
|
||||
.notice-card__detail {
|
||||
border: 1px solid rgba(226, 232, 240, 0.72);
|
||||
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
|
||||
.notice-card__cover {
|
||||
margin-bottom: 14px;
|
||||
.notice-card__detail-head {
|
||||
height: 68px;
|
||||
padding: 0 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.notice-card__meta {
|
||||
margin-bottom: 12px;
|
||||
.notice-card__back {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
margin-right: 14px;
|
||||
color: #64748b;
|
||||
font-size: 28px;
|
||||
line-height: 1;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.notice-card__head-title {
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.notice-card__image {
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
.notice-card__detail-body {
|
||||
padding: 22px 20px 28px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.notice-card__detail-title {
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.notice-card__time-pill {
|
||||
display: inline-flex;
|
||||
height: 34px;
|
||||
margin-top: 16px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 999px;
|
||||
line-height: 34px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.notice-card__time-icon {
|
||||
margin-right: 6px;
|
||||
color: #64748b;
|
||||
font-size: 15px;
|
||||
line-height: 15px;
|
||||
}
|
||||
|
||||
.notice-card__divider {
|
||||
height: 1px;
|
||||
margin: 26px 0 20px;
|
||||
background: #e5e7eb;
|
||||
}
|
||||
|
||||
.notice-card__paragraph {
|
||||
margin-bottom: 12px;
|
||||
line-height: 22px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.notice-card__paragraph + .notice-card__paragraph {
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.notice-card__strong {
|
||||
color: #1e293b;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,35 @@
|
||||
<template>
|
||||
<CardShell class="scenic-image-card" variant="soft" pressable :disabled="disabled" @select="handleAction">
|
||||
<view class="scenic-image-card__image-wrap relative">
|
||||
<MediaFrame class="scenic-image-card__image h-260" :src="data.image" />
|
||||
<view
|
||||
class="scenic-image-card relative rounded-24 overflow-hidden w-full"
|
||||
:class="{ 'is-disabled': disabled }"
|
||||
@click="handleSelect"
|
||||
>
|
||||
<image
|
||||
class="scenic-image-card__image block w-full"
|
||||
:src="data.image"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
|
||||
<view
|
||||
class="scenic-image-card__expand flex flex-items-center flex-justify-center rounded-full color-white font-size-18 font-900"
|
||||
@click.stop="handleAction"
|
||||
>
|
||||
{{ action.icon }}
|
||||
</view>
|
||||
|
||||
<view v-if="hasCaption" class="scenic-image-card__caption">
|
||||
<view class="scenic-image-card__caption-title color-white font-size-14 font-900">{{ data.caption.title }}</view>
|
||||
<view v-if="data.caption.subtitle" class="scenic-image-card__caption-subtitle font-size-11 font-700">
|
||||
{{ data.caption.subtitle }}
|
||||
<view v-if="caption.title" class="scenic-image-card__title color-white font-size-18 font-900 ellipsis-1">
|
||||
{{ caption.title }}
|
||||
</view>
|
||||
<view v-if="caption.subtitle" class="scenic-image-card__subtitle color-white font-size-14 font-900 ellipsis-1">
|
||||
{{ caption.subtitle }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="scenic-image-card__expand absolute flex flex-items-center flex-justify-center w-32 h-32 rounded-full color-white font-size-16 font-900">⛶</view>
|
||||
</view>
|
||||
</CardShell>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from "vue";
|
||||
import CardShell from "../SharedVisual/CardShell.vue";
|
||||
import MediaFrame from "../SharedVisual/MediaFrame.vue";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
@@ -31,11 +44,17 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits(["select", "action"]);
|
||||
|
||||
const hasCaption = computed(() => Boolean(props.data.caption?.title || props.data.caption?.subtitle));
|
||||
const caption = computed(() => props.data.caption || {});
|
||||
const action = computed(() => props.data.action || {});
|
||||
const hasCaption = computed(() => Boolean(caption.value.title || caption.value.subtitle));
|
||||
|
||||
const handleSelect = () => {
|
||||
if (props.disabled) return;
|
||||
emit("select", props.data);
|
||||
};
|
||||
|
||||
const handleAction = () => {
|
||||
if (props.disabled) return;
|
||||
emit("select", props.data);
|
||||
emit("action", props.data);
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
export default {
|
||||
export const scenicImageWithoutCaption = {
|
||||
id: "bridge-mist-plain",
|
||||
image: "https://images.unsplash.com/photo-1500534314209-a25ddb2bd429?auto=format&fit=crop&w=1000&q=80",
|
||||
caption: {
|
||||
title: "水上森林二号机位",
|
||||
subtitle: "下午逆光较弱,适合拍摄树影和水面反射",
|
||||
action: {
|
||||
icon: "↗",
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
id: "bridge-mist",
|
||||
image: "https://images.unsplash.com/photo-1500534314209-a25ddb2bd429?auto=format&fit=crop&w=1000&q=80",
|
||||
caption: {
|
||||
title: "古桥晨雾机位",
|
||||
subtitle: "清晨 6:30 雾气最浓,长焦压缩效果极佳",
|
||||
},
|
||||
action: {
|
||||
icon: "↗",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,8 +1,30 @@
|
||||
.scenic-image-card__image-wrap {
|
||||
.scenic-image-card {
|
||||
height: 324px;
|
||||
background: #e2e8f0;
|
||||
}
|
||||
|
||||
.scenic-image-card:active,
|
||||
.scenic-image-card__expand:active {
|
||||
opacity: 0.86;
|
||||
}
|
||||
|
||||
.scenic-image-card.is-disabled {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.scenic-image-card__image {
|
||||
border-radius: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.scenic-image-card__expand {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
z-index: 2;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: rgba(15, 23, 42, 0.36);
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.scenic-image-card__caption {
|
||||
@@ -10,21 +32,17 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 42px 16px 16px;
|
||||
padding: 72px 22px 20px;
|
||||
background: linear-gradient(180deg, rgba(15, 23, 42, 0) 0%, rgba(15, 23, 42, 0.72) 100%);
|
||||
}
|
||||
|
||||
.scenic-image-card__caption-title {
|
||||
line-height: 20px;
|
||||
.scenic-image-card__title {
|
||||
line-height: 24px;
|
||||
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.28);
|
||||
}
|
||||
|
||||
.scenic-image-card__caption-subtitle {
|
||||
margin-top: 3px;
|
||||
color: rgba(255, 255, 255, 0.72);
|
||||
}
|
||||
|
||||
.scenic-image-card__expand {
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
background: rgba(0, 0, 0, 0.32);
|
||||
.scenic-image-card__subtitle {
|
||||
margin-top: 4px;
|
||||
line-height: 18px;
|
||||
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.28);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user