当今的移动应用市场已经成为了一个日趋竞争激烈的领域,而开发一个既能在多个平台上运行,又能够高效、可维护的应用则成为了一个急需解决的问题。

在这个领域中,Vue3 + TypeScript + Uniapp 的组合已经成为了一种受欢迎的选择,特别是在开发小程序方面。Vue3 作为一个现代的、灵活的前端框架,它通过组件化的方式帮助开发者构建复杂的用户界面。
同时,TypeScript 作为一种强类型语言,可以提高代码的可维护性和可读性,并在开发过程中提供更好的开发体验。而 Uniapp 作为一个基于 Vue3 的跨平台应用开发框架,则提供了开发小程序所需的各种工具和功能,使得开发者可以更加高效地开发和发布小程序。
本文将全面介绍使用 Vue3 + TypeScript + Uniapp 开发小程序的方法和技巧,帮助读者掌握这一领域的核心技术,从而在开发过程中更加得心应手。
Vue3 + TypeScript + Uniapp 开发小程序【完整案例 一篇文章精通系列】
- 一、项目搭建
- 1、安装基本依赖
- 2、在vs-code当中打开项目
- 3、打开终端,安装依赖:`npm install`
- 4、运行发布
- 5、在微信开发者工具当中打开项目
- 二、搭建项目主界面、路由配置
- 1、完善页面
-
- 2、配置底部导航
- 3、完善页面
- 1)完善首页,设置背景图片在背景最顶端(自定义导航栏样式)
- 2)设置导航和胶囊对其
- 3)指定背景图片
- 4)设置轮播图
- 4、对请求接口进行封装
- 5、完善首页请求信息
- 1)完善首页,疫苗预约
- 2)首页第二项数据:挂号和体检
- 3)热门挂号
- 4)健康自测
- 四、新冠疫苗
- 1、创建新页面并实现页面跳转
- 2、源代码下载:[https://download.csdn.net/download/qq_44757034/87787707](https://download.csdn.net/download/qq_44757034/87787707)
一、项目搭建
在使用 Vue3 + TypeScript + Uniapp 进行小程序开发之前,需要先安装一些必要的开发环境和工具。
首先,你需要安装 Vue3 脚手架工具 vue-cli 或者 Vite,这两个工具可以帮助你快速创建和搭建 Vue3 项目,并且提供了一些常用的开发工具和插件,例如调试工具和代码热更新等。
其次,你需要安装 TypeScript,它是一种强类型的编程语言,可以提高代码的可读性和可维护性,同时也可以减少开发过程中的错误。
最后,你需要一个强大的编辑器来编写代码和管理项目,推荐使用 Visual Studio Code,它是一个功能强大的开源编辑器,提供了丰富的插件和扩展功能,可以帮助你更加高效地编写和调试代码。
在安装好这些必要的开发环境和工具后,你就可以开始使用 Vue3 + TypeScript + Uniapp 进行小程序开发了。
1、安装基本依赖
全局安装vue-cli
| npm install -g @vue/cli@4 |
复制

创建以typescript开发的工程
| npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project |
复制

拉去成功

修改一下对应的文件夹名称

2、在vs-code当中打开项目

3、打开终端,安装依赖:npm install

安装成功

4、运行发布
复制

生成dist文件,其中dev为对应打包好的微信小程序的代码

5、在微信开发者工具当中打开项目
导入刚刚生成的dist文件



运行成功

二、搭建项目主界面、路由配置
1、完善页面
1)首页

| <template> |
| <view> |
| 首页 |
| </view> |
| </template> |
| |
| <script setup lang="ts"> |
| import { ref } from 'vue' |
| const title = ref('Hello') |
| </script> |
| |
| <style scoped> |
| |
| </style> |
| |
复制
2)创建其他页面
挂号页面

| <template> |
| <view> |
| 挂号 |
| </view> |
| </template> |
| |
| <script setup lang="ts"> |
| import { ref } from 'vue' |
| const title = ref('Hello') |
| </script> |
| |
| <style scoped> |
| |
| </style> |
| |
复制
短视频页面

| <template> |
| <view> |
| 短视频 |
| </view> |
| </template> |
| |
| <script setup lang="ts"> |
| import { ref } from 'vue' |
| const title = ref('Hello') |
| </script> |
| |
| <style scoped> |
| |
| </style> |
| |
复制
个人中心页面

| <template> |
| <view> |
| 个人中心 |
| </view> |
| </template> |
| |
| <script setup lang="ts"> |
| import { ref } from 'vue' |
| const title = ref('Hello') |
| </script> |
| |
| <style scoped> |
| |
| </style> |
| |
复制
2、配置底部导航

参考官方文档进行配置
https://uniapp.dcloud.net.cn/collocation/pages.html#tabbar


| { |
| "pages": [ |
| { |
| "path": "pages/index/index", |
| "style": { |
| "navigationBarTitleText": "首页" |
| } |
| }, |
| { |
| "path": "pages/registered/registered", |
| "style": { |
| "navigationBarTitleText": "挂号" |
| } |
| }, |
| { |
| "path": "pages/video/video", |
| "style": { |
| "navigationBarTitleText": "短视频" |
| } |
| }, |
| { |
| "path": "pages/mine/mine", |
| "style": { |
| "navigationBarTitleText": "个人中心" |
| } |
| } |
| ], |
| "globalStyle": { |
| "navigationBarTextStyle": "black", |
| "navigationBarTitleText": "uni-app", |
| "navigationBarBackgroundColor": "#F8F8F8", |
| "backgroundColor": "#F8F8F8" |
| }, |
| "tabBar": { |
| "color": "#7A7E83", |
| "selectedColor": "#d81e06", |
| "borderStyle": "white", |
| "backgroundColor": "#ffffff", |
| "list": [ |
| { |
| "pagePath": "pages/index/index", |
| "iconPath": "static/image/home.png", |
| "selectedIconPath": "static/image/home-h.png", |
| "text": "首页" |
| }, { |
| "pagePath": "pages/registered/registered", |
| "iconPath": "static/image/addition.png", |
| "selectedIconPath": "static/image/addition-h.png", |
| "text": "挂号" |
| }, { |
| "pagePath": "pages/video/video", |
| "iconPath": "static/image/pause.png", |
| "selectedIconPath": "static/image/pause-h.png", |
| "text": "短视频" |
| }, { |
| "pagePath": "pages/mine/mine", |
| "iconPath": "static/image/gr.png", |
| "selectedIconPath": "static/image/gr-h.png", |
| "text": "个人中心" |
| } |
| ] |
| } |
| } |
| |
复制
实现了点击页面切换

3、完善页面
1)完善首页,设置背景图片在背景最顶端(自定义导航栏样式)
首先先需要去掉原生导航栏的样式

| "navigationStyle":"custom" |
复制
在index.vue当中引入一张图片看看效果

| <template> |
| <view> |
| <image src="/static/image/indexbg.png" style="width: 100%;"></image> |
| </view> |
| </template> |
| |
| <script setup lang="ts"> |
| import { ref,onMounted } from 'vue' |
| |
| </script> |
| |
| <style scoped> |
| |
| </style> |
| |
复制
运行效果

2)设置导航和胶囊对其
在App.vue当中

| onLaunch(() => { |
| |
| const res = uni.getMenuButtonBoundingClientRect(); |
| let get_Menu = uni.getStorageSync('MenuButton') |
| if(!get_Menu){ |
| |
| uni.setStorageSync('MenuButton',res); |
| } |
| }); |
复制
保存成功

在index.vue当中,设置自定义导航栏
设置动态样式,使用刚刚获取到的胶囊的位置信息来动态的设置对应的标题信息与胶囊进行对其

| <template> |
| <!-- 自定义的导航 --> |
| <view class="nav-gation"> |
| <view class="nav-top"></view> |
| <view class="nav-height">某某省第一人民医院</view> |
| </view> |
| </template> |
| <script setup lang="ts"> |
| import { ref,onMounted } from 'vue' |
| //取出胶囊按钮数据 |
| let menu_top = ref<string>('') //胶囊按钮距离顶部的高度 |
| let menu_height = ref<string>('') //胶囊按钮高度 |
| onMounted(()=>{ |
| //获取胶囊按钮的坐标 |
| let MenuButton:any = uni.getStorageSync('MenuButton'); |
| menu_top.value = MenuButton.top + 'px' |
| menu_height.value = MenuButton.height + 'px' |
| }) |
| </script> |
| <style scoped> |
| .nav-gation{ |
| position: fixed; |
| top: 0; |
| left: 0; |
| right: 0; |
| z-index: 99; |
| } |
| .nav-top{ |
| height: v-bind('menu_top'); |
| } |
| .nav-height{ |
| height: v-bind('menu_height'); |
| line-height: v-bind('menu_height'); |
| } |
| </style> |
| |
复制

3)指定背景图片

| <view class="imgTopBg"> |
| <image src="/static/image/indexbg.png"></image> |
| </view> |
复制
| |
| .imgTopBg image{ |
| width: 100%; |
| } |
复制
4)设置轮播图
我们找到uniapp的官方文档
https://uniapp.dcloud.net.cn/component/swiper.html


| <!-- 轮播图 --> |
| <view class="uni-margin-wrap"> |
| <swiper class="swiper" circular :indicator-dots="indicatorDots" :autoplay="autoplay" :interval="interval" |
| :duration="duration"> |
| <swiper-item> |
| <image src="https://cdn-us.imgs.moe/2023/02/07/63e253ac01153.png"></image> |
| </swiper-item> |
| <swiper-item> |
| <image src="https://cdn-us.imgs.moe/2023/02/07/63e253ac01153.png"></image> |
| </swiper-item> |
| <swiper-item> |
| <image src="https://cdn-us.imgs.moe/2023/02/07/63e253ac01153.png"></image> |
| </swiper-item> |
| </swiper> |
| </view> |
复制
| .swiper{ |
| height: 400rpx; |
| width: 96%; |
| margin: auto; |
| } |
| |
| swiper-item image{ |
| width: 100%; |
| height: 100%; |
| border-radius: 15rpx; |
| } |
| .uni-margin-wrap{ |
| margin-top: -310rpx; |
| } |
复制

4、对请求接口进行封装
安装一下se64的依赖,
| npm install --save js-base64 |
复制
创建public文件夹
在public当中创建request.ts文件

| |
| const baseUrl = 'https://meituan.thexxdd.cn/api/' |
| |
| import { Base64 } from "js-base64" |
| function getToken():string{ |
| const token = uni.getStorageSync('wxuser').user_token || '' |
| const base64_token = Base64.encode(token + ':') |
| return 'Basic' + base64_token |
| } |
| |
| function request( |
| url:string, |
| method:'GET'|'POST', |
| data:string|object|ArrayBuffer |
| ) |
| { |
| return new Promise((resolve,reject) =>{ |
| uni.request({ |
| url:baseUrl + url, |
| method, |
| data, |
| header:{ Authorization:getToken() }, |
| success:(res:any)=>{ |
| |
| if(res.statusCode == 200){ |
| |
| resolve(res) |
| }else if(res.statusCode == 400){ |
| |
| reject(res) |
| }else if(res.statusCode == 401){ |
| |
| reject(res) |
| }else if(res.statusCode == 403){ |
| |
| reject(res) |
| }else if(res.statusCode == 500){ |
| uni.showToast({title:'服务器发生未知错误',icon:'none',duration:1000}) |
| |
| reject(res) |
| }else if(res.statusCode == 502){ |
| |
| }else if(res.statusCode == 504){ |
| |
| reject(res) |
| }else if(res.statusCode == 202){ |
| uni.showToast({title:'给你提示',icon:'none',duration:1000}) |
| reject(res) |
| }else{ |
| uni.showToast({title:'服务器发生未知错误',icon:'none',duration:1000}) |
| reject(res) |
| } |
| |
| }, |
| fail:(err:any)=>{ |
| uni.showToast({title:'服务器发生未知错误',icon:'none',duration:1000}) |
| reject(err) |
| } |
| }) |
| }) |
| } |
| |
| |
| const RequestApi = { |
| |
| FrontPage:()=>request('frontpage','GET',{}) |
| } |
| |
| export {RequestApi} |
复制

5、完善首页请求信息
1)完善首页,疫苗预约

| import { RequestApi } from '@/public/request' |
复制
| async function pageData(){ |
| const res:any = await RequestApi.FrontPage() |
| console.log(res) |
| } |
| |
复制
回到微信小程序查看,我们成功的拿到了数据

将数据渲染到首页
创建decl-type.d.ts文件

| |
| |
| |
| |
| |
| export interface Vaccine{ |
| image:string; |
| title:string; |
| } |
复制

| import type {Vaccine} from '@/public/decl-type' |
复制
| |
| |
| let vaccine = ref<Vaccine[]>([]) |
复制

| const res:any = await RequestApi.FrontPage() |
| vaccine.value = res.data.data[0].vaccine |
复制
设置页面样式

| .gongge { |
| width:90%; |
| margin: auto; |
| } |
| .gongge view{ |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| font-size: 28rpx; |
| float: left; |
| margin: 25rpx; |
| text-align: center; |
| margin-top: 30rpx; |
| } |
| .gongge image{ |
| width: 80rpx; |
| height:80rpx; |
| } |
复制

2)首页第二项数据:挂号和体检

| |
| |
| export interface Phydata{ |
| describe:string; |
| image:string; |
| title:string; |
| } |
复制

| <view class="re-me-ex"> |
| <view class="re-me-ex-view card" v-for="(item,index) in phydata" :key="index"> |
| <image mode="widthFix" :src="item.image"></image> |
| <view style="margin-top: 10%;"> |
| <view class="re-me-ex-title" >{{ item.title }}</view> |
| <view class="re-me-ex-lable">{{ item.describe }}</view> |
| </view> |
| <view style="height: 30rpx;"></view> |
| </view> |
| </view> |
复制

| import type {Vaccine,Phydata} from '@/public/decl-type' |
复制
| |
| let phydata = ref<Phydata[]>([]) |
复制
| phydata.value = res.data.data[1].reserve |
复制

| |
| .re-me-ex-view{ |
| margin: auto; |
| float: left; |
| width: 45%; |
| border-width: 1rpx; |
| border-radius: 8rpx; |
| margin: 1%; |
| padding: 1%; |
| } |
| .re-me-ex image{ |
| float: left; |
| width: 140rpx; |
| } |
| .re-me-ex-view view{ |
| padding-left: 1%; |
| float: left; |
| width: 180rpx; |
| font-size: 25rpx; |
| text-align: right; |
| } |
| .re-me-ex-title{ |
| font-weight: bold; |
| margin-top: 10rpx; |
| } |
| .card { |
| box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); |
| transition: 0.3s; |
| border-radius: 5px; |
| } |
| .card:hover { |
| box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2); |
| } |
复制

3)热门挂号

| |
| export interface Registered{ |
| background:string; |
| dep_id:string; |
| image:string; |
| title:string; |
| } |
复制

| <view style="height: 10rpx;"></view> |
| <view class="online-reg"> |
| <view v-for="(item,index) in registered" :key="index" class="card" :style="{backgroundColor:item.background}"> |
| <text> |
| {{ item.title }} |
| </text> |
| <image mode="aspectFit" :src="item.image"></image> |
| </view> |
| </view> |
复制

| |
| |
| let vaccine = ref<Vaccine[]>([]) |
| |
| let phydata = ref<Phydata[]>([]) |
| |
| let registered = ref<Registered[]>([]) |
| |
| onMounted(()=>{ |
| |
| let MenuButton:any = uni.getStorageSync('MenuButton'); |
| menu_top.value = MenuButton.top + 'px' |
| menu_height.value = MenuButton.height + 'px' |
| pageData() |
| }) |
| |
| async function pageData(){ |
| const res:any = await RequestApi.FrontPage() |
| vaccine.value = res.data.data[0].vaccine |
| phydata.value = res.data.data[1].reserve |
| registered.value = res.data.data[2].popular |
| } |
复制

| .online-reg{ |
| margin: auto; |
| font-size: 30rpx; |
| font-weight: 900; |
| width: 98%; |
| } |
| .online-reg view { |
| padding: 1%; |
| float: left; |
| width: 29%; |
| margin: 1%; |
| height: 100rpx; |
| } |
| .online-reg view image { |
| width: 30px; |
| height: 30px; |
| float: right; |
| } |
复制

4)健康自测

| |
| export interface Selftest{ |
| describe: string; |
| image: string; |
| minute: number; |
| name: string; |
| number_of_people: number; |
| question: number; |
| } |
复制

| let selftest = ref<Selftest[]>([]) |
| 在这里插入代码片 |
复制
| selftest.value = res.data.data[3].self_test |
复制

| <view class="health"> |
| <view class="card-foot"> |
| <view class="re-me-ex-title" > |
| <view style="padding-left: 40rpx;">{{ selftest[1].name }}</view> |
| </view> |
| <view style="clear: both;"> |
| <view style="width:40%;height: 180rpx;float: left;font-size: 25rpx;"> |
| <view> |
| <span> {{ selftest[1].minute }}题/{{ selftest[1].minute }}分钟</span> |
| </view> |
| <view> |
| <span> {{ selftest[1].number_of_people }}人通过测试</span> |
| </view> |
| </view> |
| <view style="width: 40%;height: 180rpx;float: left;"> |
| <image style="height: 150rpx;width: 150rpx;;" :src="selftest[1].image"></image> |
| </view> |
| </view> |
| </view> |
| <view class="card-foot"> |
| <view class="re-me-ex-title" > |
| <view style="padding-left: 40rpx;">{{ selftest[2].name }}</view> |
| </view> |
| <view style="clear: both;"> |
| <view style="width:40%;height: 180rpx;float: left;font-size: 25rpx;"> |
| <view> |
| <span> {{ selftest[2].minute }}题/{{ selftest[2].minute }}分钟</span> |
| </view> |
| <view> |
| <span> {{ selftest[2].number_of_people }}人通过测试</span> |
| </view> |
| </view> |
| <view style="width: 40%;height: 180rpx;float: left;"> |
| <image style="height: 150rpx;width: 150rpx;;" :src="selftest[2].image"></image> |
| </view> |
| </view> |
| </view> |
| </view> |
复制

四、新冠疫苗
1、创建新页面并实现页面跳转
创建newapptime.vue

| <template> |
| <view> |
| 新冠疫苗 |
| </view> |
| </template> |
| |
| <script setup lang="ts"> |
| import { ref } from 'vue' |
| const title = ref('Hello') |
| </script> |
| |
| <style scoped> |
| |
| </style> |
| |
复制
实现页面跳转
设置路由

| { |
| "path": "pages/newapptime/newapptime", |
| "style": { |
| "navigationBarTitleText": "新冠疫苗" |
| } |
| } |
复制

| <view class="card-border" v-for="(item,index) in vaccine" :key="index" @click="jumpToPage(index)"> |
| <image :src="item.image"></image> |
| <text>{{ item.title }}</text> |
| </view> |
复制

| function jumpToPage(index:number){ |
| if(index == 0){ |
| uni.navigateTo({ |
| url:"/pages/newapptime/newapptime" |
| }) |
| } |
| if(index == 1){ |
| |
| } |
| if(index == 2){ |
| |
| } |
| if(index == 3){ |
| |
| } |
| } |
复制


2、源代码下载:https://download.csdn.net/download/qq_44757034/87787707
)