hookehuyr

feat(Webview): 添加Webview页面及相关测试功能

refactor(BottomNav): 替换图标为SVG并添加样式过滤
style(Activities): 更新页面标题和导航栏样式
docs: 添加文件描述注释和最后编辑信息
......@@ -11,10 +11,8 @@ declare module 'vue' {
BottomNav: typeof import('./src/components/BottomNav.vue')['default']
GlassCard: typeof import('./src/components/GlassCard.vue')['default']
NavBar: typeof import('./src/components/navBar.vue')['default']
NutImage: typeof import('@nutui/nutui-taro')['Image']
NutButton: typeof import('@nutui/nutui-taro')['Button']
NutImagePreview: typeof import('@nutui/nutui-taro')['ImagePreview']
NutInput: typeof import('@nutui/nutui-taro')['Input']
NutToast: typeof import('@nutui/nutui-taro')['Toast']
Picker: typeof import('./src/components/time-picker-data/picker.vue')['default']
PointsCollector: typeof import('./src/components/PointsCollector.vue')['default']
PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default']
......
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1756303912062" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="21734" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M512 97.52381c228.912762 0 414.47619 185.563429 414.47619 414.47619s-185.563429 414.47619-414.47619 414.47619S97.52381 740.912762 97.52381 512 283.087238 97.52381 512 97.52381z m0 73.142857C323.486476 170.666667 170.666667 323.486476 170.666667 512s152.81981 341.333333 341.333333 341.333333 341.333333-152.81981 341.333333-341.333333S700.513524 170.666667 512 170.666667z m190.854095 160.572952l-86.186666 275.334095L341.333333 692.760381l86.211048-275.334095 275.309714-86.186667z m-216.941714 144.579048l-33.01181 105.374476 105.398858-32.987429-72.411429-72.411428z" p-id="21735"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1756302772088" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8533" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M549.61981 133.022476l319.683047 203.605334A70.851048 70.851048 0 0 1 902.095238 396.361143v434.883047A70.89981 70.89981 0 0 1 831.146667 902.095238h-282.819048l0.024381-218.112h-71.826286v218.087619L192.853333 902.095238A70.89981 70.89981 0 0 1 121.904762 831.24419V390.241524c0-24.527238 12.678095-47.299048 33.54819-60.220953l318.659048-197.485714a70.972952 70.972952 0 0 1 75.50781 0.487619zM828.952381 828.952381V397.214476L511.488 195.047619 195.047619 391.119238V828.952381h211.309714v-216.551619h212.187429v216.527238L828.952381 828.952381z" p-id="8534"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1756304253215" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16498" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M853.333333 896a42.666667 42.666667 0 0 1-42.666666-42.666667 298.666667 298.666667 0 0 0-6.186667-60.16 289.066667 289.066667 0 0 0-17.28-56.106666 292.266667 292.266667 0 0 0-27.52-50.773334 313.386667 313.386667 0 0 0-36.48-44.16 301.653333 301.653333 0 0 0-44.16-36.48 42.666667 42.666667 0 1 1 47.573333-70.613333 375.253333 375.253333 0 0 1 56.96 46.72 395.52 395.52 0 0 1 46.72 56.746667A379.733333 379.733333 0 0 1 865.706667 704 386.773333 386.773333 0 0 1 896 853.333333a42.666667 42.666667 0 0 1-42.666667 42.666667zM741.973333 272.64a228.48 228.48 0 0 0-13.866666-43.946667 225.28 225.28 0 0 0-21.333334-39.893333 236.16 236.16 0 0 0-28.586666-34.773333 234.666667 234.666667 0 0 0-34.773334-28.586667 234.666667 234.666667 0 0 0-178.346666-35.413333 240.213333 240.213333 0 0 0-84.053334 35.413333 234.666667 234.666667 0 0 0-34.773333 28.586667 236.16 236.16 0 0 0-28.586667 34.773333 241.28 241.28 0 0 0-21.333333 39.893333 243.84 243.84 0 0 0-13.653333 43.946667 241.28 241.28 0 0 0 0 94.72 228.48 228.48 0 0 0 13.866666 43.946667 225.28 225.28 0 0 0 21.333334 39.893333 42.666667 42.666667 0 0 0 70.826666-47.786667 131.84 131.84 0 0 1-13.866666-25.173333 149.333333 149.333333 0 0 1-8.746667-28.16 152.96 152.96 0 0 1 0-60.16 141.653333 141.653333 0 0 1 8.746667-27.946667 138.24 138.24 0 0 1 13.653333-25.386666 166.186667 166.186667 0 0 1 18.346667-21.333334 151.253333 151.253333 0 0 1 21.333333-18.346666 138.24 138.24 0 0 1 25.386667-13.653334 141.653333 141.653333 0 0 1 27.946666-8.746666 149.333333 149.333333 0 0 1 60.16 0 141.653333 141.653333 0 0 1 27.946667 8.746666 138.24 138.24 0 0 1 25.386667 13.653334 147.84 147.84 0 0 1 40.32 40.533333 131.84 131.84 0 0 1 13.866666 25.173333 149.333333 149.333333 0 0 1 8.746667 28.16 152.96 152.96 0 0 1 0 60.16 141.653333 141.653333 0 0 1-8.746667 27.946667 138.24 138.24 0 0 1-13.653333 25.386667A166.186667 166.186667 0 0 1 618.666667 426.666667a151.253333 151.253333 0 0 1-21.333334 18.346666 138.24 138.24 0 0 1-25.386666 13.653334 141.653333 141.653333 0 0 1-27.946667 8.746666A156.373333 156.373333 0 0 1 512 469.333333a386.773333 386.773333 0 0 0-149.333333 30.293334 384 384 0 0 0-64 35.413333 375.253333 375.253333 0 0 0-56.96 46.72 395.52 395.52 0 0 0-46.72 56.746667A379.733333 379.733333 0 0 0 158.293333 704 386.773333 386.773333 0 0 0 128 853.333333a42.666667 42.666667 0 0 0 85.333333 0 298.666667 298.666667 0 0 1 6.186667-60.16 289.066667 289.066667 0 0 1 17.28-56.106666 292.266667 292.266667 0 0 1 27.52-50.773334 313.386667 313.386667 0 0 1 36.48-44.16 301.653333 301.653333 0 0 1 44.16-36.48 292.266667 292.266667 0 0 1 50.773333-27.52 305.28 305.28 0 0 1 56.106667-17.493333A314.453333 314.453333 0 0 1 512 554.666667a248.746667 248.746667 0 0 0 47.146667-4.693334 240.213333 240.213333 0 0 0 84.053333-35.413333 234.666667 234.666667 0 0 0 34.773333-28.586667 236.16 236.16 0 0 0 28.586667-34.773333 241.28 241.28 0 0 0 21.333333-39.893333 243.84 243.84 0 0 0 13.653334-43.946667 241.28 241.28 0 0 0 0-94.72z" fill="#333333" p-id="16499"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1756303617166" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8875" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M584.655238 97.52381l7.753143 0.121904a127.024762 127.024762 0 0 1 109.592381 181.052953h152.064a73.142857 73.142857 0 0 1 73.142857 73.142857v86.625524a73.142857 73.142857 0 0 1-73.142857 73.142857h-4.510476v340.967619a73.142857 73.142857 0 0 1-73.142857 73.142857H223.963429a73.142857 73.142857 0 0 1-73.142858-73.142857l-0.024381-340.967619H146.285714a73.142857 73.142857 0 0 1-73.142857-73.142857v-86.625524a73.142857 73.142857 0 0 1 73.142857-73.142857h139.897905a127.024762 127.024762 0 0 1 109.568-181.077334L403.504762 97.52381a128.975238 128.975238 0 0 1 90.599619 37.010285A128.926476 128.926476 0 0 1 584.655238 97.52381zM223.963429 852.577524l237.397333-0.024381V511.609905H223.914667v340.967619z m552.472381-340.967619h-237.421715v340.943238h237.446095l-0.02438-340.943238zM461.336381 351.817143H146.285714v86.649905h4.486096v-2.218667l310.564571-0.024381V351.817143z m392.728381 0H538.989714v84.406857l310.588953 0.024381-0.024381 2.218667h4.510476v-86.625524zM403.504762 170.666667h-2.364952a53.881905 53.881905 0 0 0 0 107.763809h54.125714V226.937905c0-6.339048 0.463238-12.55619 1.340952-18.627048A56.246857 56.246857 0 0 0 403.504762 170.666667z m183.539809 0h-2.364952a56.295619 56.295619 0 0 0-53.126095 37.668571c0.902095 6.046476 1.365333 12.263619 1.365333 18.602667l-0.024381 51.492571h54.150095a53.881905 53.881905 0 0 0 53.76-50.029714l0.121905-3.852191c0-29.744762-24.137143-53.881905-53.881905-53.881904z" p-id="8876"></path></svg>
\ No newline at end of file
<!--
* @Date: 2025-08-27 17:44:10
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-08-27 23:10:28
* @FilePath: /lls_program/src/components/BottomNav.vue
* @Description: 文件描述
-->
<template>
<view class="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-100 flex justify-around py-2 z-50">
<view
......@@ -6,9 +13,9 @@
@click="navigate(item.path)"
:class="['flex flex-col items-center py-2 px-5', isActive(item.path) ? 'text-blue-600' : 'text-gray-500']"
>
<component :is="item.icon" size="24" />
<image :src="item.icon" :class="['w-6 h-6', isActive(item.path) ? 'filter-blue' : 'filter-gray']" />
<span class="text-xs mt-1">{{ item.label }}</span>
<view v-if="isActive(item.path)" class="absolute bottom-0 w-10 h-1 bg-blue-600 rounded-t-full"></view>
<!-- <view v-if="isActive(item.path)" class="absolute bottom-0 w-10 h-1 bg-blue-600 rounded-t-full"></view> -->
</view>
</view>
</template>
......@@ -16,13 +23,16 @@
<script setup>
import { computed, shallowRef } from 'vue';
import Taro from '@tarojs/taro';
import { Home, Find, Shop, My } from '@nutui/icons-vue-taro';
import homeIcon from '@/assets/images/icon/home.svg';
import rewardsIcon from '@/assets/images/icon/rewards.svg';
import activitiesIcon from '@/assets/images/icon/activities.svg';
import meIcon from '@/assets/images/icon/me.svg';
const navItems = shallowRef([
{ path: '/pages/Dashboard/index', icon: Home, label: '首页' },
{ path: '/pages/Activities/index', icon: Find, label: '活动' },
{ path: '/pages/Rewards/index', icon: Shop, label: '兑换' },
{ path: '/pages/Profile/index', icon: My, label: '我的' },
{ path: '/pages/Dashboard/index', icon: homeIcon, label: '首页' },
{ path: '/pages/Activities/index', icon: activitiesIcon, label: '活动' },
{ path: '/pages/Rewards/index', icon: rewardsIcon, label: '兑换' },
{ path: '/pages/Profile/index', icon: meIcon, label: '我的' },
]);
const currentPage = computed(() => {
......@@ -38,3 +48,13 @@ const navigate = (path) => {
Taro.reLaunch({ url: path });
};
</script>
<style>
.filter-blue {
filter: brightness(0) saturate(100%) invert(22%) sepia(100%) saturate(3441%) hue-rotate(229deg) brightness(101%) contrast(101%);
}
.filter-gray {
filter: brightness(0) saturate(100%) invert(91%) sepia(6%) saturate(394%) hue-rotate(201deg) brightness(50%) contrast(92%);
}
</style>
......
/*
* @Date: 2025-08-27 18:25:06
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-08-27 21:00:06
* @FilePath: /lls_program/src/pages/Activities/index.config.js
* @Description: 文件描述
*/
export default {
navigationBarTitleText: '首页'
navigationBarTitleText: '活动'
}
......
<template>
<view class="min-h-screen flex flex-col bg-white">
<AppHeader title="城市漫步" :showBack="false" />
<!-- Map container -->
<view class="flex-1 relative">
<Map
class="w-full h-full"
:longitude="121.47"
:latitude="31.23"
:scale="15"
:markers="markers"
@markertap="onMarkerTap"
/>
</view>
<!-- Check-in point detail modal -->
<view v-if="showModal && selectedPoint" class="fixed inset-0 bg-black bg-opacity-50 flex items-end justify-center z-20">
<view class="bg-white rounded-t-2xl w-full max-h-[80%] overflow-y-auto">
<view class="relative h-64 overflow-hidden">
<image :src="selectedPoint.image" :alt="selectedPoint.name" class="w-full h-full object-cover" />
<button class="absolute top-4 right-4 bg-white rounded-full p-2" @click="showModal = false">
<Failure size="24" />
</button>
</view>
<view class="p-4">
<h2 class="text-2xl font-bold mb-2">{{ selectedPoint.name }}</h2>
<p class="text-gray-600 mb-6">{{ selectedPoint.description }}</p>
<button @click="handleCheckIn" :disabled="selectedPoint.isCheckedIn" :class="['w-full py-3 rounded-full text-white font-medium', selectedPoint.isCheckedIn ? 'bg-gray-400' : 'bg-blue-500']">
{{ selectedPoint.isCheckedIn ? '已打卡' : '打卡' }}
</button>
</view>
</view>
</view>
<!-- Check-in success message -->
<view v-if="showSuccess" class="fixed inset-0 flex items-center justify-center z-30 pointer-events-none">
<view class="bg-black bg-opacity-70 text-white px-6 py-4 rounded-lg flex items-center">
<Check size="24" class="text-green-400 mr-2" />
<span>打卡成功!</span>
<view class="activities-container">
<!-- WebView内容 -->
<web-view
v-if="webUrl"
:src="webUrl"
class="web-view"
@message="handleMessage"
@load="handleLoad"
@error="handleError"
></web-view>
<!-- 加载状态 -->
<view v-if="loading" class="loading-container">
<view class="loading-spinner">⏳</view>
<view class="loading-text">加载中...</view>
</view>
<!---but态 -->
<view v-if="error" class="error-container">
<view class="error-icon">⚠️</view>
<view class="error-text">页面加载失败</view>
<nut-button type="primary" size="small" @click="handleRetry">重试</nut-button>
</view>
<!-- 底部导航 -->
<BottomNav />
</view>
</template>
<script setup>
import { ref, computed } from 'vue';
import AppHeader from '../../components/AppHeader.vue';
import BottomNav from '../../components/BottomNav.vue';
import { Failure, Check } from '@nutui/icons-vue-taro';
const selectedPoint = ref(null);
const showModal = ref(false);
const showSuccess = ref(false);
const checkInPoints = ref([
{
id: 'yuyuan',
name: '豫园',
description: '豫园始建于1559年,是明代著名的私家园林,现为国家重点文物保护单位。园内亭台楼阁布局精巧,景色优美,是上海著名的旅游景点。',
latitude: 31.2271,
longitude: 121.4875,
iconPath: '/static/images/activities/marker.png',
width: 30,
height: 30,
image: "/static/images/activities/citywalk2_1.png",
isCheckedIn: false
},
{
id: 'jiuquqiao',
name: '九曲桥',
description: '豫园九曲桥位于上海城隍庙豫园内,是上海的标志性建筑之一。桥因始建于明代嘉靖、万历年间,九曲桥与荷花池在那时就已存在,最初为木桥。清朝隆年间进行过修建,上世纪20年代因火灾改建为水泥桥,解放后又恢复了石桥形式。',
latitude: 31.2275,
longitude: 121.4895,
iconPath: '/static/images/activities/marker.png',
width: 30,
height: 30,
image: "/static/images/activities/citywalk2_1.png",
isCheckedIn: false
}
]);
const markers = computed(() => checkInPoints.value.map(p => ({
id: p.id,
latitude: p.latitude,
longitude: p.longitude,
iconPath: p.iconPath,
width: p.width,
height: p.height,
})));
const onMarkerTap = (e) => {
const point = checkInPoints.value.find(p => p.id === e.detail.markerId);
if (point) {
selectedPoint.value = point;
showModal.value = true;
}
};
const handleCheckIn = () => {
if (selectedPoint.value) {
const index = checkInPoints.value.findIndex(p => p.id === selectedPoint.value.id);
if (index !== -1) {
checkInPoints.value[index].isCheckedIn = true;
}
showModal.value = false;
showSuccess.value = true;
import { ref, onMounted } from 'vue'
import Taro from '@tarojs/taro'
import BottomNav from '../../components/BottomNav.vue'
/**
* 活动页面WebView组件
* 用于显示活动相关的外部网页内容
*/
// 页面状态
const webUrl = ref('https://oa.onwall.cn/f/map/#/checkin/?id=2400107') // TODO: 临时设置一个URL
const loading = ref(true)
const error = ref(false)
/**
* 处理WebView加载完成
*/
const handleLoad = (e) => {
console.log('WebView加载完成:', e)
loading.value = false
error.value = false
}
/**
* 处理WebView加载错误
*/
const handleError = (e) => {
console.error('WebView加载错误:', e)
loading.value = false
error.value = true
Taro.showToast({
title: '页面加载失败',
icon: 'none'
})
}
/**
* 处理WebView消息
*/
const handleMessage = (e) => {
console.log('WebView消息:', e)
// 可以在这里处理来自WebView的消息
}
/**
* 重试加载
*/
const handleRetry = () => {
loading.value = true
error.value = false
// 重新设置URL触发重新加载
const currentUrl = webUrl.value
webUrl.value = ''
setTimeout(() => {
showSuccess.value = false;
}, 2000);
}
};
webUrl.value = currentUrl
}, 100)
}
// 页面挂载时初始化
onMounted(() => {
// 这里可以根据需要动态设置URL
console.log('活动页面WebView初始化')
})
</script>
<style lang="less">
.activities-container {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
background-color: #fff;
position: relative;
}
.web-view {
flex: 1;
width: 100%;
// 确保webview高度在底部导航以上
height: calc(100vh - 120rpx); // 减去底部导航的高度
}
.loading-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 10;
}
.loading-spinner {
font-size: 48rpx;
margin-bottom: 16rpx;
animation: spin 1s linear infinite;
}
.loading-text {
font-size: 28rpx;
color: #666;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.error-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
z-index: 10;
}
.error-icon {
font-size: 80rpx;
margin-bottom: 24rpx;
}
.error-text {
font-size: 28rpx;
color: #666;
margin-bottom: 32rpx;
}
</style>
......
export default {
navigationBarTitleText: '网页预览',
navigationStyle: 'custom',
enablePullDownRefresh: false,
backgroundTextStyle: 'dark'
}
\ No newline at end of file
<template>
<view class="webview-container">
<!-- 导航栏 -->
<view class="nav-bar">
<view class="nav-left" @click="handleBack">
<view class="back-icon">←</view>
<text class="back-text">返回</text>
</view>
<view class="nav-title">{{ pageTitle }}</view>
<view class="nav-right"></view>
</view>
<!-- WebView内容 -->
<web-view
v-if="webUrl"
:src="webUrl"
class="web-view"
@message="handleMessage"
@load="handleLoad"
@error="handleError"
></web-view>
<!-- 加载状态 -->
<view v-if="loading" class="loading-container">
<view class="loading-spinner">⏳</view>
<view class="loading-text">加载中...</view>
</view>
<!-- 错误状态 -->
<view v-if="error" class="error-container">
<view class="error-icon">⚠️</view>
<view class="error-text">页面加载失败</view>
<nut-button type="primary" size="small" @click="handleRetry">重试</nut-button>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import Taro from '@tarojs/taro'
/**
* WebView页面组件
* 用于预览外部网页链接
*/
// 页面状态
const webUrl = ref('')
const pageTitle = ref('网页预览')
const loading = ref(true)
const error = ref(false)
/**
* 获取页面参数
*/
const getPageParams = () => {
const instance = Taro.getCurrentInstance()
const params = instance.router?.params || {}
if (params.url) {
webUrl.value = decodeURIComponent(params.url)
}
if (params.title) {
pageTitle.value = decodeURIComponent(params.title)
}
}
/**
* 处理返回按钮点击
*/
const handleBack = () => {
Taro.navigateBack()
}
/**
* 处理WebView加载完成
*/
const handleLoad = (e) => {
console.log('WebView加载完成:', e)
loading.value = false
error.value = false
}
/**
* 处理WebView加载错误
*/
const handleError = (e) => {
console.error('WebView加载错误:', e)
loading.value = false
error.value = true
Taro.showToast({
title: '页面加载失败',
icon: 'none'
})
}
/**
* 处理WebView消息
*/
const handleMessage = (e) => {
console.log('WebView消息:', e)
// 可以在这里处理来自WebView的消息
}
/**
* 重试加载
*/
const handleRetry = () => {
loading.value = true
error.value = false
// 重新设置URL触发重新加载
const currentUrl = webUrl.value
webUrl.value = ''
setTimeout(() => {
webUrl.value = currentUrl
}, 100)
}
// 页面挂载时获取参数
onMounted(() => {
getPageParams()
// 如果没有URL参数,显示错误
if (!webUrl.value) {
loading.value = false
error.value = true
Taro.showToast({
title: '缺少URL参数',
icon: 'none'
})
}
})
</script>
<script>
export default {
name: 'WebViewPage'
}
</script>
<style lang="less">
.webview-container {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
background-color: #fff;
}
.nav-bar {
display: flex;
align-items: center;
justify-content: space-between;
height: 88rpx;
padding: 0 32rpx;
background-color: #fff;
border-bottom: 1rpx solid #eee;
position: sticky;
top: 0;
z-index: 100;
}
.nav-left {
display: flex;
align-items: center;
cursor: pointer;
.back-icon {
font-size: 32rpx;
color: #333;
font-weight: bold;
}
.back-text {
margin-left: 8rpx;
font-size: 28rpx;
color: #333;
}
}
.nav-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
text-align: center;
flex: 1;
}
.nav-right {
width: 80rpx;
}
.web-view {
flex: 1;
width: 100%;
}
.loading-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.loading-spinner {
font-size: 48rpx;
margin-bottom: 16rpx;
animation: spin 1s linear infinite;
}
.loading-text {
font-size: 28rpx;
color: #666;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.error-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.error-icon {
font-size: 80rpx;
margin-bottom: 24rpx;
}
.error-text {
font-size: 28rpx;
color: #666;
margin-bottom: 32rpx;
}
</style>
\ No newline at end of file
export default {
navigationBarTitleText: 'WebView测试页面',
enablePullDownRefresh: false,
backgroundTextStyle: 'dark'
}
\ No newline at end of file
<template>
<view class="test-container">
<view class="test-header">
<text class="test-title">WebView页面测试</text>
</view>
<view class="test-content">
<view class="test-section">
<text class="section-title">测试链接:</text>
<nut-button
type="primary"
size="small"
@click="testWebView('https://www.baidu.com', '百度搜索')"
class="test-btn"
>
测试百度
</nut-button>
<nut-button
type="success"
size="small"
@click="testWebView('https://m.taobao.com', '淘宝网')"
class="test-btn"
>
测试淘宝
</nut-button>
<nut-button
type="warning"
size="small"
@click="testWebView('https://github.com', 'GitHub')"
class="test-btn"
>
测试GitHub
</nut-button>
</view>
<view class="test-section">
<text class="section-title">自定义URL测试:</text>
<nut-input
v-model="customUrl"
placeholder="请输入要测试的URL"
class="url-input"
/>
<nut-input
v-model="customTitle"
placeholder="请输入页面标题"
class="title-input"
/>
<nut-button
type="primary"
@click="testCustomUrl"
class="test-btn"
>
测试自定义URL
</nut-button>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import Taro from '@tarojs/taro'
/**
* WebView测试页面
*/
const customUrl = ref('')
const customTitle = ref('')
/**
* 测试WebView页面
* @param {string} url - 要测试的URL
* @param {string} title - 页面标题
*/
const testWebView = (url, title) => {
Taro.navigateTo({
url: `/pages/webview/index?url=${encodeURIComponent(url)}&title=${encodeURIComponent(title)}`
})
}
/**
* 测试自定义URL
*/
const testCustomUrl = () => {
if (!customUrl.value) {
Taro.showToast({
title: '请输入URL',
icon: 'none'
})
return
}
const url = customUrl.value.startsWith('http') ? customUrl.value : `https://${customUrl.value}`
const title = customTitle.value || '自定义页面'
testWebView(url, title)
}
</script>
<script>
export default {
name: 'WebViewTest'
}
</script>
<style lang="less">
.test-container {
padding: 32rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
.test-header {
text-align: center;
margin-bottom: 48rpx;
}
.test-title {
font-size: 36rpx;
font-weight: 600;
color: #333;
}
.test-content {
background-color: #fff;
border-radius: 16rpx;
padding: 32rpx;
}
.test-section {
margin-bottom: 48rpx;
&:last-child {
margin-bottom: 0;
}
}
.section-title {
display: block;
font-size: 28rpx;
font-weight: 600;
color: #333;
margin-bottom: 24rpx;
}
.test-btn {
margin-right: 16rpx;
margin-bottom: 16rpx;
}
.url-input,
.title-input {
margin-bottom: 16rpx;
}
</style>
\ No newline at end of file