defineEmits
defineProps
<script setup>
const navigateToPostDetails = () => {
uni.navigateTo({
url: '/components/Post/PostDetails',
})
}
</script>
<template>
<view class="container" @click="navigateToPostDetails">
<!-- 评论组件 -->
<navigator url="/pages/index/Posts/PostDetails" class="reply-navigator">
<view class="reply">
<image class="reply-profile-image" src="/static/logo.png" />
<view class="reply-info">
<view class="reply-user">
<text class="reply-name">Qiuner</text>
<text class="reply-handle">22级 · 5天</text>
</view>
<text class="reply-text">
福信校园通是一个校园社交软件,在这里你可以分享你的想法、发现新事物、与志同道合的人互动。
</text>
</view>
</view>
</navigator>
<view class="post-image-container">
<view class="post-image" style="background-color: #1da1f2">
<!-- 蓝色背景替代图片部分 -->
</view>
</view>
<view class="post-footer"></view>
</view>
</template>
<style>
.container {
padding: 1rem;
margin-top: calc(env(safe-area-inset-top) + 2rem);
}
.reply {
display: flex;
padding: 1rem 0;
border-top: 0.0625rem solid #ddd;
}
.reply-profile-image {
width: 1.875rem;
height: 1.875rem;
border-radius: 50%;
}
.reply-info {
flex: 1;
margin-left: 0.625rem;
}
.reply-user {
display: flex;
align-items: center;
}
.reply-name {
font-weight: bold;
}
.reply-handle {
color: grey;
margin-left: 0.3125rem;
}
.reply-text {
margin-top: 0.3125rem;
}
.post-image-container {
display: flex;
justify-content: center;
}
.post-image {
width: 100%;
max-width: 100%;
height: 12rem;
border: 0.0625rem solid #ddd;
}
.post-footer {
color: grey;
}
.reply-navigator,
.post-image-navigator {
display: block;
}
</style>
“Image is not defined in this environment” 表示 Image 对象在当前环境中不可用
改动前
// 预加载图片
function preloadImage(src: string) {
if (typeof Image !== 'undefined') {
const img = new Image()
img.src = src
} else {
console.warn('Image is not defined in this environment')
}
}
改动后
// 预加载图片
function preloadImage(src: string) {
uni.getImageInfo({
src,
success: () => console.log(`Image loaded: ${src}`),
fail: (err) => console.warn(`Failed to load image: ${src}`, err),
})
}
总体代码
<template>
<view class="container">
<!-- 页面其他内容 -->
<image class="add-button" :src="buttonImage" @click="handleClick"></image>
</view>
</template>
<script setup lang="ts">
import { ref, computed, nextTick } from 'vue'
// 定义响应式变量
const isAddButton1 = ref(true)
// 切换图片和跳转页面的方法
const handleClick = () => {
isAddButton1.value = !isAddButton1.value
// 如果切换后的状态是 AddButton1,进行页面跳转
if (isAddButton1.value) {
nextTick(() => {
setTimeout(() => {
uni.navigateTo({
url: '/pages/writing/writing',
fail: (err) => {
console.error('Navigation Error:', err)
},
})
}, 100) // 延迟时间可以根据实际需要调整
})
}
}
// 预加载图片
function preloadImage(src: string) {
uni.getImageInfo({
src,
success: () => {
// 成功时不进行任何操作
},
fail: (err) => {
console.warn(`Failed to load image: ${src}`, err)
},
})
}
// 预加载图片
preloadImage('/static/add/AddButton1.png')
preloadImage('/static/add/AddButton2.png')
// 图片路径的计算属性
const buttonImage = computed(() =>
isAddButton1.value ? '/static/add/AddButton1.png' : '/static/add/AddButton2.png',
)
</script>
<style scoped>
.container {
position: relative;
width: 100%;
height: 100vh;
background-color: #f0f0f0;
}
.add-button {
position: fixed;
right: 5%; /* 调整按钮的水平位置 */
bottom: 5%; /* 调整按钮的位置,使其在导航栏上方 */
width: 20vw; /* 相对屏幕宽度的按钮宽度 */
height: 20vw; /* 相对屏幕宽度的按钮高度 */
cursor: pointer;
}
</style>
钩子名称 | 触发时机 | 用途 |
---|---|---|
onLoad | 页面首次加载时 | 页面初始化操作,如获取数据、设置页面初始状态 |
onShow | 页面显示时(包括从其他页面返回时) | 页面显示时的操作,如更新数据、记录用户行为 |
onReady | 页面初次渲染完成时 | 页面渲染完成后的操作,如初始化插件、开始动画 |
onHide | 页面隐藏时(如切换到其他页面或应用被切换到后台) | 保存页面状态、停止动画、清理定时器 |
onUnload | 页面卸载时(如退出页面或页面被销毁) | 页面销毁前的清理操作,如释放资源、清除缓存 |
onTabItemTap | 用户点击 TabBar 的某一项时 | 处理 TabBar 项的点击事件 |
钩子名称 | 触发时机 | 用途 |
---|---|---|
onBeforeMount | 组件挂载前 | 组件创建之前的准备工作 |
onMounted | 组件挂载后 | 组件挂载后的操作,如数据获取、DOM 操作 |
onBeforeUpdate | 组件数据更新前 | 数据更新之前进行操作 |
onUpdated | 组件数据更新后 | 数据更新之后执行操作,如操作新的 DOM 元素 |
onBeforeUnmount | 组件卸载前 | 组件卸载之前的清理工作 |
onUnmounted | 组件卸载后 | 组件卸载后的清理工作 |
<script setup lang="ts">
import { ref, onShow } from 'vue'
import AddButton from '@/components/AddButton.vue'
import Posts from '@/components/Post/Post.vue'
import TabHeader from '@/components/HeadPortrait/TabHeader.vue'
import '@/styles/IndexAndMessage.css'
const currentTab = ref(0)
// 切换选项卡的方法
const changeTab = (index: number) => {
currentTab.value = index
}
// 监听滑动事件,实现选项卡动态切换
const onSwiperChange = (e: any) => {
currentTab.value = e.detail.current
}
// 重置状态到默认值
onShow(() => {
currentTab.value = 0
})
</script>
<template>
<TabHeader />
<!-- 顶部导航栏 -->
<view class="header">
<view class="tab-container">
<view class="tab" :class="{ active: currentTab === 0 }" @click="changeTab(0)">
<text>为您推荐</text>
</view>
<view class="tab" :class="{ active: currentTab === 1 }" @click="changeTab(1)">
<text>正在关注</text>
</view>
</view>
</view>
<!-- 滑动内容区 -->
<swiper :current="currentTab" @change="onSwiperChange" style="height: 100vh">
<!-- 为您推荐内容 -->
<swiper-item>
<view class="content">
<Posts />
</view>
</swiper-item>
<!-- 正在关注内容 -->
<swiper-item>
<view class="content">正在关注的内容</view>
</swiper-item>
</swiper>
<!-- 添加按钮 -->
<AddButton />
</template>
onHide
钩子中清除缓存,在 onShow
钩子中重置状态:<script setup lang="ts">
import { ref } from 'vue'
import { onShow, onHide } from '@dcloudio/uni-app'
import AddButton from '@/components/AddButton.vue'
import Posts from '@/components/Post/Post.vue'
import TabHeader from '@/components/HeadPortrait/TabHeader.vue'
import '@/styles/IndexAndMessage.css'
const currentTab = ref(0)
// 切换选项卡的方法
const changeTab = (index: number) => {
currentTab.value = index
}
// 监听滑动事件,实现选项卡动态切换
const onSwiperChange = (e: any) => {
currentTab.value = e.detail.current
}
// 在页面隐藏时重置状态
onHide(() => {
uni.removeStorage({
key: 'currentTab',
})
})
// 在页面显示时恢复默认状态
onShow(() => {
uni.getStorage({
key: 'currentTab',
success: (res) => {
currentTab.value = res.data || 0
},
fail: () => {
currentTab.value = 0
},
})
})
</script>
<template>
<TabHeader />
<!-- 顶部导航栏 -->
<view class="header">
<view class="tab-container">
<view class="tab" :class="{ active: currentTab === 0 }" @click="changeTab(0)">
<text>为您推荐</text>
</view>
<view class="tab" :class="{ active: currentTab === 1 }" @click="changeTab(1)">
<text>正在关注</text>
</view>
</view>
</view>
<!-- 滑动内容区 -->
<swiper :current="currentTab" @change="onSwiperChange" style="height: 100vh">
<!-- 为您推荐内容 -->
<swiper-item>
<view class="content">
<Posts />
</view>
</swiper-item>
<!-- 正在关注内容 -->
<swiper-item>
<view class="content">正在关注的内容</view>
</swiper-item>
</swiper>
<!-- 添加按钮 -->
<AddButton />
</template>
安装
pnpm add pinia
配置
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')
创建实例
import { defineStore } from 'pinia'
export const useTabStore = defineStore('tab', {
state: () => ({
currentTab: 0 // 默认选中的标签页索引
}),
actions: {
setTab(index: number) {
this.currentTab = index
},
resetTab() {
this.currentTab = 0
}
}
})
在组件中使用……
import { createPinia } from 'pinia'
import persist from 'pinia-plugin-persistedstate'
// 创建pinia实例
const pinia = createPinia()
// 使用持久化插件
pinia.use(persist)
export default pinia
import { createSSRApp } from 'vue'
import App from './App.vue'
import pinia from './stores'
export function createApp() {
// vue实例
const app = createSSRApp(App)
// 使用pinia
app.use(pinia)
return {
app,
}
}
// src/stores/indexStore.ts
import { defineStore } from 'pinia'
export const useIndexStore = defineStore('indexStore', {
state: () => ({
currentTab: 0, // 0 表示为您推荐,1 表示正在关注
}),
actions: {
changeTab(index: number) {
this.currentTab = index
},
resetTab() {
this.currentTab = 0 // 重置为默认状态
},
},
})
<script setup lang="ts">
import AddButton from '@/components/AddButton.vue'
import Posts from '@/components/Post/Post.vue'
import TabHeader from '@/components/HeadPortrait/TabHeader.vue'
import '@/styles/IndexAndMessage.css'
import type { SwiperChangeEvent } from '@/types/index'
import { onShow } from '@dcloudio/uni-app'
import { useIndexStore } from '@/stores/modules/indexStore'
const indexStore = useIndexStore()
onShow(() => {
indexStore.resetTab()
})
</script>
<template>
<TabHeader />
<!-- 顶部导航栏 -->
<view class="header">
<view class="tab-container">
<view
class="tab"
:class="{ active: indexStore.currentTab === 0 }"
@click="indexStore.changeTab(0)"
>
<text>为您推荐</text>
</view>
<view
class="tab"
:class="{ active: indexStore.currentTab === 1 }"
@click="indexStore.changeTab(1)"
>
<text>正在关注</text>
</view>
</view>
</view>
<!-- 滑动内容区 -->
<swiper
:current="indexStore.currentTab"
@change="(e: SwiperChangeEvent) => indexStore.changeTab(e.detail.current)"
style="height: 100vh"
>
<!-- 为您推荐内容 -->
<swiper-item>
<view class="content">
<Posts />
</view>
</swiper-item>
<!-- 正在关注内容 -->
<swiper-item>
<view class="content">正在关注的内容</view>
</swiper-item>
</swiper>
<!-- 添加按钮 -->
<AddButton />
</template>
@change="(e: SwiperChangeEvent) => indexStore.changeTab(e.detail.current)"
因此这样写代码而不是
@change="(e) => indexStore.changeTab(e.detail.current)"
// src/types/swiper.d.ts
export interface SwiperChangeEvent {
detail: {
current: number
}
}
在这里插入图片描述
在这里插入图片描述
import { defineStore } from 'pinia'
export const useIndexAndMessageStore = defineStore('indexStoreAndMessage', {
state: () => ({
currentTab: 0, // 0 表示为您推荐,1 表示正在关注
}),
actions: {
changeTab(index: number) {
this.currentTab = index
},
resetTab() {
this.currentTab = 0 // 重置为默认状态
},
},
persist: {
// 调整为兼容多端的API
storage: {
getItem(key) {
return uni.getStorageSync(key)
},
setItem(key, value) {
uni.setStorageSync(key, value)
},
},
},
})
index
<script setup lang="ts">
import AddButton from '@/components/AddButton.vue'
import Posts from '@/components/Post/Post.vue'
import TabHeader from '@/components/HeadPortrait/TabHeader.vue'
import '@/styles/IndexAndMessage.css'
import { onShow } from '@dcloudio/uni-app'
import { useIndexAndMessageStore } from '@/stores/modules/indexAndMessageStore'
import type { SwiperChangeEvent } from '@/types/indexAndMessageStore'
// 使用 Pinia Store
const indexAndMessageStore = useIndexAndMessageStore()
// 页面显示时重置 tab 状态
onShow(() => {
indexAndMessageStore.resetTab()
})
</script>
<template>
<TabHeader />
<!-- 顶部导航栏 -->
<view class="header">
<view class="tab-container">
<view
class="tab"
:class="{ active: indexAndMessageStore.currentTab === 0 }"
@click="indexAndMessageStore.changeTab(0)"
>
<text>为您推荐</text>
</view>
<view
class="tab"
:class="{ active: indexAndMessageStore.currentTab === 1 }"
@click="indexAndMessageStore.changeTab(1)"
>
<text>正在关注</text>
</view>
</view>
</view>
<!-- 滑动内容区 -->
<swiper
:current="indexAndMessageStore.currentTab"
@change="(e: SwiperChangeEvent) => indexAndMessageStore.changeTab(e.detail.current)"
style="height: 100vh"
>
<!-- 为您推荐内容 -->
<swiper-item>
<view class="content">
<Posts />
</view>
</swiper-item>
<!-- 正在关注内容 -->
<swiper-item>
<view class="content">正在关注的内容</view>
</swiper-item>
</swiper>
<!-- 添加按钮 -->
<AddButton />
</template>
<style lang="scss"></style>
message
<script setup lang="ts">
import AddButton from '@/components/AddButton.vue'
import TabHeader from '@/components/HeadPortrait/TabHeader.vue'
import '@/styles/IndexAndMessage.css'
import { useIndexAndMessageStore } from '@/stores/modules/indexAndMessageStore'
import { onShow } from '@dcloudio/uni-app'
import type { SwiperChangeEvent } from '@/types/indexAndMessageStore'
// 使用 Pinia Store
const indexAndMessageStore = useIndexAndMessageStore()
// 页面显示时重置 tab 状态
onShow(() => {
indexAndMessageStore.resetTab()
})
</script>
<template>
<TabHeader />
<!-- 顶部导航栏 -->
<view class="header">
<view class="tab-container">
<view
class="tab"
:class="{ active: indexAndMessageStore.currentTab === 0 }"
@click="indexAndMessageStore.changeTab(0)"
>
<text>全部</text>
</view>
<view
class="tab"
:class="{ active: indexAndMessageStore.currentTab === 1 }"
@click="indexAndMessageStore.changeTab(1)"
>
<text>点赞</text>
</view>
<view
class="tab"
:class="{ active: indexAndMessageStore.currentTab === 2 }"
@click="indexAndMessageStore.changeTab(2)"
>
<text>回复</text>
</view>
</view>
</view>
<!-- 滑动内容区 -->
<swiper
:current="indexAndMessageStore.currentTab"
@change="(e: SwiperChangeEvent) => indexAndMessageStore.changeTab(e.detail.current)"
style="height: 100vh"
>
<!-- 全部内容 -->
<swiper-item>
<view class="content">
<!-- 这里是“全部”的内容 -->
<p>这是全部</p>
</view>
</swiper-item>
<!-- 点赞内容 -->
<swiper-item>
<view class="content">
<p>这是点赞</p>
</view>
</swiper-item>
<!-- 回复内容 -->
<swiper-item>
<view class="content">
<p>这是回复</p>
</view>
</swiper-item>
</swiper>
<!-- 添加按钮 -->
<AddButton />
</template>
<style lang="scss"></style>
indexAndMessageStore
// src/stores/indexStore.ts
import { defineStore } from 'pinia'
export const useIndexAndMessageStore = defineStore('indexStoreAndMessage', {
state: () => ({
currentTab: 0, // 0 表示为您推荐,1 表示正在关注
}),
actions: {
changeTab(index: number) {
this.currentTab = index
},
resetTab() {
this.currentTab = 0 // 重置为默认状态
},
},
})
indexAndMessageStore.d
// src/types/swiper.d.ts
export interface SwiperChangeEvent {
detail: {
current: number
}
}