uniapp Vue3+cli+pinia+ts 主题切换

直接拉取项目 
npm i
运行
npm run dev:h5
自行cli搭建步骤
安装
npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project
npm i
npm i pinia -S --legacy-peer-deps
npm i pinia-plugin-persistedstate -S --legacy-peer-deps
npm i sass -D --legacy-peer-deps
npm i sass-loader -D --legacy-peer-deps
npm i vk-uview-ui --legacy-peer-deps
运行报错
Uncaught SyntaxError: The requested module '/node_modules/pinia/node_modules/vue-demi/lib/index.mjs?v=f43e2f61' does not provide an export named 'hasInjectionContext' 
解决:降低pinia版本:npm install pinia@2.0.36 --legacy-peer-deps
运行报错
Uncaught TypeError: Cannot destructure property 'proxy' of 'getCurrentInstance(...)' as it is null.
解决:全局挂载的方法,在页面中使用,ts/js文件中还是用导入模块/函数的方式
一、安装完成后
配置uView Vue3.0
//main.ts
import { createSSRApp } from "vue";
import App from "./App.vue";

// 引入 uView UI
+ import uView from 'vk-uview-ui';

export function createApp() {
  const app = createSSRApp(App);

  // 使用 uView
+  app.use(uView)

  return {
    app
  };
}
//pages.json
+ "easycom": {
+ 	"autoscan": true,
+ 	"custom": {
+ 		"^u-(.*)": "vk-uview-ui/components/u-$1/u-$1.vue"
+ 	}
+ },
//App.vue
<style lang="scss">
+ @import "vk-uview-ui/index.scss";
</style>
//uni.scss
@import "vk-uview-ui/theme.scss";
配置pinia
//main.ts
import { createSSRApp } from "vue";
import App from "./App.vue";
// 导入 pinia 实例
+ import pinia from './stores'

export function createApp() {
  const app = createSSRApp(App);
  // 使用 pinia
+  app.use(pinia)
  return {
    app,
  };
}
//store/index.ts
import * as Pinia from 'pinia';
import persist from 'pinia-plugin-persistedstate'
// 创建 pinia 实例
const pinia = Pinia.createPinia();
// 使用持久化存储插件
pinia.use(persist)
// 默认导出,给 main.ts 使用
export default pinia
导入主题样式文件
 # 相关文件
 
 src/common
 src/static/common/common.scss
//App.vue
<style lang="scss">
// 主题样式
@import '@/common/theme/css/theme.scss';
/* uni.css - 通用组件、模板样式库,可以当作一套ui库应用 */
@import './common/uni.css';
</style>
存储主题信息,开启持久化
//store/theme.ts
import { defineStore } from 'pinia';
interface themeStoreType {
	color: string;
	index: number|string;
}
interface themeStoreState {
	appTheme: string;
	appThemeIndex: number|string;
	theme: string | themeStoreType;
}

const themeStore = defineStore('themeStore', {
	state: (): themeStoreState => ({
		// appTheme: localStorage.getSto('appTheme') ? localStorage.getSto('appTheme') : 'index',
		// appThemeIndex: localStorage.getSto('appThemeIndex') ? localStorage.getSto('appThemeIndex') : 1,
		appTheme: 'index',
		appThemeIndex: 1,
		theme: '',
	}),
	persist: {
		storage: {
			getItem: uni.getStorageSync,
			setItem: uni.setStorageSync
		}
	},
	getters: {

	},
	actions: {
		//你可以传入一个颜色参数(需要上面公共css中含有,如果不传入默认)
		setTheme(data?: themeStoreType) {
			//你可以传入一个颜色参数(需要上面公共css中含有,如果不传入默认)
			this.$patch({
				appTheme: data.color,
				appThemeIndex: data.index,
				theme: data
			});
			console.log('修改', this.theme);
		}
	}
});

export default themeStore;
定义修改主题颜色的方法并导出
// src/utils/theme.ts
import themeStore from '@/stores/theme';
//在ts/js文件中,需要引入实例store,才能使用模块
import store from '@/stores/index'
const theme = themeStore(store);

export const toggleAppTheme = (color, index) => {
	var data = {
		color: color,
		index: index
	};

	theme.setTheme(data);
	uni.hideLoading();
	if (color == 'index') {
		return '1';
	} else if (color == 'black') {
		return '2';
	} else if (color == 'blue') {
		return '3';
	} else if (color == 'grey') {
		uni.hideLoading();
		return '4';
	} else if (color == 'green') {
		uni.hideLoading();
		return '5';
	} else if (color == 'yellow') {
		return '6';
	} else {
		return '1';
	}
};
全局挂载主题色和修改主题方法
//main.ts
import { createSSRApp } from "vue";
import App from "./App.vue";

// 主题切换
+ import useThemeStore from '@/stores/theme';
+ const theme = useThemeStore();
+ import { toggleAppTheme } from '@/utils/theme';

export function createApp() {
  const app = createSSRApp(App);
  // 全局挂载
+ app.config.globalProperties.$appThemeName = theme; //主题颜色
+ app.config.globalProperties.$changeAppTheme = toggleAppTheme; //改变主题色事件
    
  return {
    app
  };
}                                                            
渲染主题
//App.vue
<script setup lang="ts">
import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
//引入API
+ import { getCurrentInstance } from 'vue';
+ const { proxy } = getCurrentInstance() as any;
//定义全局挂载的主题色及方法
+ const themeStore = proxy.$appThemeName;
+ const toggleAppTheme = proxy.$changeAppTheme;
onLaunch(() => {
	console.log("App Launch");
});
onShow(() => {
	console.log("App Show");
//使用定义的全局挂载的主题色及方法
+	if (themeStore.appThemeIndex) {
+		console.log('app.vue', themeStore.appTheme, themeStore.appThemeIndex);
+		toggleAppTheme(themeStore.appTheme, themeStore.appThemeIndex);
+	} else {
+		toggleAppTheme('index', '1');
+	}
});
onHide(() => {
	console.log("App Hide");
});

</script>
<style lang="scss">
// 主题样式
@import '@/common/theme/css/theme.scss';
/* uni.css - 通用组件、模板样式库,可以当作一套ui库应用 */
@import './common/uni.css';
</style>
页面路径集中管理
//utils/routes.ts
export const index_route = '/pages/index/index';
export const mine_route = '/pages/mine/mine';
导入自定义组件,并全局挂载
# 相关文件

# 二次封装uView自定义顶部导航栏
src/components/ownNavbar/ownNavbar.vue
# 自定义底部导航栏
src/components/tabBar/tabBar.vue 
//ownNavbar.vue
<template>
	<view class="">
		<u-navbar
			:back-text="backText"
			:title-width="titleWidth"
			:title="title"
			:background="background"
			:title-color="color"
			:back-icon-color="color"
			:is-back="isBack"
			:border-bottom="borderbottom"
		>
			<view slot="right">
				<slot name="rights"></slot>
			</view>
		</u-navbar>
	</view>
</template>

<script lang="ts" setup>
// import themeMxins from "@/utils/mixins/themeMxins.js"
import { ref, computed,getCurrentInstance } from 'vue';
const { proxy } = getCurrentInstance() as any;
const themeStore = proxy.$appThemeName;
const toggleAppTheme = proxy.$changeAppTheme;

const props = defineProps({
	title: {
		type: String,
		default: ''
	},
	//标题宽度
	titleWidth: {
		type: String,
		default: 300
	},
	//标题宽度
	backText: {
		type: String,
		default: ''
	},
	/* 是否返回 */
	isBack: {
		type: Boolean,
		default: true
	},
	/* 底部线条 */
	borderbottom: {
		type: Boolean,
		default: true
	},
	color: {
		type: String,
		default: '#fff'
	}
});
const background = computed(() => {
	let type = toggleAppTheme(themeStore.appTheme, themeStore.appThemeIndex);
	console.log('ttttype',type)
	if (type == '1') {
		return {
			backgroundImage: 'linear-gradient(45deg, rgb(32, 171, 254), rgb(60, 199, 254))'
		};
	} else if (type == '2') {
		return {
			backgroundImage: 'linear-gradient(45deg, rgb(78, 78, 78), rgb(78, 78, 78))'
		};
	} else if (type == '3') {
		return {
			backgroundImage: 'linear-gradient(45deg, rgb(47, 166, 200), rgb(47, 166, 200))'
		};
	} else if (type == '4') {
		return {
			backgroundImage: 'linear-gradient(45deg, rgb(214, 214, 214), rgb(214, 214, 214))'
		};
	} else if (type == '5') {
		return {
			backgroundImage: 'linear-gradient(45deg, rgb(62, 130, 83), rgb(62, 130, 83))'
		};
	} else if (type == '6') {
		return {
			backgroundImage: 'linear-gradient(45deg, rgb(239, 150, 24), rgb(239, 150, 24))'
		};
	} else {
		return {
			backgroundImage: 'linear-gradient(45deg, rgb(32, 171, 254), rgb(60, 199, 254))'
		};
	}
});
const color = computed(() => {
	let type = toggleAppTheme(themeStore.appTheme, themeStore.appThemeIndex);
	if (type == '1') {
		return '#fff';
	} else if (type == '2') {
		return '#fff';
	} else if (type == '3') {
		return '#fff';
	} else if (type == '4') {
		return '#000';
	} else if (type == '5') {
		return '#fff';
	} else if (type == '6') {
		return '#fff';
	} else {
		return '#fff';
	}
});

</script>

<style></style>
//tabBar.vue
<template>
	<view class="tabbar" :data-theme="themeStore.appTheme" :style="{ 'padding-bottom': paddingBottomHeight + 'rpx' }">
		<view class="tabbar-item" v-for="(item, index) in tabList" :key="index" @click="tabbarChange(item.pagePath)">
			<view class="">
				<u-icon v-if="current == index" class="tab-icon-select" size="45" :name="item.selectedIconPath"
					custom-prefix="custom-icon"></u-icon>
				<u-icon v-else class="tab-icon" size="45" :name="item.iconPath" custom-prefix="custom-icon"></u-icon>
			</view>
			<view class="">
				<view class="tabbarActive" v-if="current == index">{{ item.text }}</view>
				<view class="tab-text" v-else>{{ item.text }}</view>
			</view>
		</view>
	</view>
</template>

<script lang="ts" setup>
import { ref, getCurrentInstance } from 'vue';
import { index_route, mine_route } from '@/utils/routes'
const { proxy } = getCurrentInstance() as any;
const themeStore = proxy.$appThemeName;


const props = defineProps({
	current: {
		type: Number,
		default: 0
	}
});
const tabList = ref([
	{
		iconPath: 'shouye',
		selectedIconPath: 'shouye-fill',
		text: '首页',
		count: 0, // 红色角标显示的数字,如果需要移除角标,配置此参数为0即可
		isDot: false, // 如果配置此值为true,那么角标将会以红点的形式显示
		customIcon: true, // 如果使用自定义扩展的图标库字体,需配置此值为true
		midButton: false, // 如果是凸起按钮项,需配置此值为true
		pagePath: index_route //路径需要以"/"开头
	},
	{
		iconPath: 'wode',
		selectedIconPath: 'wode-fill',
		text: '主题',
		isDot: false,
		customIcon: true,
		midButton: false,
		pagePath: mine_route
	}
]);
const paddingBottomHeight = ref(0);
const active = ref(props.current ? props.current : 0);
// uni.hideTabBar();
uni.getSystemInfo({
	success: function (res) {
		let model = ['X', 'XR', 'XS', '11', '12', '13', '14', '15'];
		model.forEach((item) => {
			//适配iphoneX以上的底部,给tabbar一定高度的padding-bottom
			if (res.model.indexOf(item) != -1 && res.model.indexOf('iPhone') != -1) {
				// that.paddingBottomHeight = 40;
			}
		});
	}
});

// const tabChange = (index, urls) => {
// 	if (index == active.value) {
// 		return;
// 	}
// 	active.value = index;
// 	console.log(index, active.value);
// 	uni.switchTab({
// 		url: urls
// 	});
// };
const tabbarChange = (urls: string) => {
	uni.redirectTo({
		url: urls
	});
};
</script>

<style lang="scss" scoped>
.tabbar {
	width: 100%;
	// height: 100rpx;
	border-top: 1px solid #c0c0c0;
	display: flex;
	position: fixed;
	justify-content: space-around;
	z-index: 99 !important;
	background-color: #ffffff;
	bottom: 0;
	padding-bottom: 0;
	padding-bottom: constant(safe-area-inset-bottom);
	padding-bottom: env(safe-area-inset-bottom);
	// padding: 20rpx 0;

	.tabbar-item {
		flex: 1;
		height: 100%;
		display: flex;
		flex-direction: column;
		justify-content: center;
		align-items: center;
		padding: 10rpx 0;

		.tab-text {
			font-size: 26rpx;
			color: #919191;
		}

		.tabbarActive {
			font-size: 26rpx;
			color: #67a9fd;
		}

		.tab-icon {
			color: #919191;
		}

		.tab-icon-select {
			color: #67a9fd;
		}
	}
}
</style>
全局挂载注册自定义组件
//main.ts
import { createSSRApp } from "vue";
import App from "./App.vue";

//引入自定义导航
+ import ownNavbar from '@/components/ownNavbar/ownNavbar.vue'; //导航栏
+ import tabBar from '@/components/tabBar/tabBar.vue'; //底部导航栏

export function createApp() {
  const app = createSSRApp(App);
    // 全局注册自定义组件
+  app.component('own-navbar', ownNavbar);
+  app.component('tab-bar', tabBar);
  return {
    app
  };
}

创建页面,关闭原生顶部导航
//index.vue
<template>
	<view class="bodys flex-column boxSizing">
		<own-navbar title="首页" :isBack="false"></own-navbar>

		<view class="content boxSizing">
			<button :data-theme="themeStore.appTheme" type="default" size="mini" style="margin: 30rpx">按钮</button>
			<button :data-theme="themeStore.appTheme" type="default" size="mini" style="margin: 30rpx">按钮</button>
			<button :data-theme="themeStore.appTheme" type="default" size="mini" style="margin: 30rpx">按钮</button>
		</view>
		<tab-bar :current="0"></tab-bar>
	</view>
</template>
<script lang="ts" setup>
import { getCurrentInstance } from 'vue';
const { proxy } = getCurrentInstance() as any;
const themeStore = proxy.$appThemeName
</script>

<style lang="scss" scoped>
.bodys {
	background-color: #e7e7e7;
	min-height: 100vh;
}
</style>
//mine.vue
<template>
	<view class="bodys boxSizing">
		<own-navbar title="主题设置" :isBack="false"></own-navbar>
		<view class="title" :data-theme="themeStore.appTheme">系统主题</view>
		<view class="content">
			<view class="theme_box flex-box boxSizing">
				<view v-for="(info, index) in themes" :key="index" class="theme boxSizing flex-center" :class="info.theme" @click="toggleAppTheme(info.theme, info.index)">
					<u-icon name="checkmark" color="#fff" size="48" v-if="themeStore.appThemeIndex == (index+1)"></u-icon>
				</view>
			</view>
		</view>
		<view class="bgColor" :data-theme="themeStore.appTheme" style="height: 300rpx; width: 80%; margin: 0 auto"></view>
		<tab-bar :current="1"></tab-bar>
	</view>
</template>

<script lang="ts" setup>
import { ref, getCurrentInstance } from 'vue';
const { proxy } = getCurrentInstance() as any;
const themeStore = proxy.$appThemeName
const toggleAppTheme = proxy.$changeAppTheme
const themes = ref([
	{
		index: 1,
		theme: 'index',
		name: '默认色'
	},
	{
		index: 2,
		theme: 'black',
		name: '极致黑'
	},
	{
		index: 3,
		theme: 'blue',
		name: '天青蓝'
	},
	{
		index: 4,
		theme: 'grey',
		name: '素雅灰'
	},
	{
		index: 5,
		theme: 'green',
		name: '国网绿'
	},
	{
		index: 6,
		theme: 'yellow',
		name: '深桔黄'
	}
]);

</script>

<style lang="scss" scoped>
.bodys {
	min-height: 100vh;
	background: #f4f4f4;

	.title {
		padding: 20rpx;
	}

	.content {
		.flex-box {
			display: flex;
		}
		.theme_box {
			background-color: #fff;
			flex-wrap: wrap;
			padding: 20rpx 15rpx;

			.theme {
				width: 30%;
				height: 100rpx !important;
				margin-bottom: 20rpx;
				font-size: 30rpx;
				line-height: 100rpx;
				text-align: center;
				color: #fff;
				position: relative;
				box-shadow: 3px 3px 5px #d0d0d3;
				margin: 0 10rpx 15rpx 10rpx;

				text {
					z-index: 9;
				}

				.dian {
					width: 20rpx;
					height: 20rpx;
					position: absolute;
					background-color: #fff;
					border-radius: 50%;
					top: 3px;
					right: 3px;
					border: 1px solid #000000;
					z-index: 99 !important;
				}
			}

			.theme:last-child {
				margin-right: 0;
			}
		}

		.theme_box_check {
			border: 2px solid #007aff;
		}

		.index {
			background-color: #67a9fd;
		}

		.black {
			background-color: #4e4e4e;
		}

		.blue {
			background-color: #2fa6c8;
		}

		.grey {
			background-color: #dddddd;
		}

		.green {
			background-color: #3e8253;
		}

		.yellow {
			background-color: #ef9618;
		}
	}
}
</style>
配置关闭原生顶部导航
//pages.json
"pages": [
		//pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
		{
			"path": "pages/index/index",
			"style": {
				"navigationBarTitleText": "uni-app",
                //关闭原生顶部导航
+				"navigationStyle": "custom"
			}
		},
		{
			"path": "pages/mine/mine",
			"style": {
				"navigationBarTitleText": "mine",
+				"navigationStyle": "custom"
			}
		}
	]
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
uniapp是一个基于Vue.js的跨平台开发框架,可以用于开发多种类型的应用程序,包括Web、iOS、Android等。Vue3是Vue.js的最新版本,它引入了一些新的特性和改进,使开发更加高效和灵活。而ts表示使用TypeScript进行开发。 要在uniapp中使用Vue3和TypeScript,可以按照以下步骤进行操作: 1. 在命令行中使用Vue CLI创建一个uniapp项目。可以通过以下命令进行安装: ``` vue create -p dcloudio/uni-preset-vue my-project ``` 2. 在项目根目录下进入命令行,并执行以下命令以进入项目: ``` cd my-project ``` 3. 在项目中安装Vue3和TypeScript依赖。可以使用以下命令进行安装: ``` npm install vue@next npm install typescript ts-loader -D ``` 4. 在项目的`src`目录下创建一个`.ts`或`.tsx`文件,这将成为您的Vue组件文件。 5. 在您的组件中,您可以使用Vue3的新特性和语法进行开发,例如使用`<script setup>`语法来简化组件的编写。 6. 您可以使用pinia框架来进行状态管理。可以通过以下步骤安装和使用pinia: - 在项目中安装pinia: ``` npm install pinia ``` - 在您的组件中实例化和使用pinia来管理应用程序的状态。 7. 在开发过程中,您可以使用Vue3的其他特性,如组合式API、Teleport、Suspense等来提高开发效率和应用程序的性能。 通过以上步骤,您就可以在uniapp中使用Vue3和TypeScript进行开发了。如果您需要进一步了解如何使用uniappVue3和TypeScript进行开发,可以参考uniapp官方文档和Vue3官方文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值