小程序的开始
developers.weixin.qq.com/miniprogram...
js
全新的邮箱注册;
AppID;
成员
开发者工具创建项目
- AppID: 可以先用 测试号 进行体验
- 后端服务:不使用云服务
- 模板选择:不使用模板
项目目录介绍
arduino
project-root/
├── pages/ // 页面目录
│ ├── index/ // 首页
│ │ ├── index.js // 页面逻辑
│ │ ├── index.json // 页面配置
│ │ ├── index.wxml // 页面结构
│ │ └── index.wxss // 页面样式
│ └── logs/ // 日志页
│ ├── logs.js
│ ├── logs.json
│ ├── logs.wxml
│ └── logs.wxss
├── animal/ // 分包animal
│ └── pages/
│ ├── cat/
│ │ ├── cat.js
│ │ ├── cat.json
│ │ ├── cat.wxml
│ │ └── cat.wxss
├── utils/ // 工具类目录
│ └── util.js // 工具函数
├── app.js // 小程序逻辑
├── app.json // 小程序公共配置
├── app.wxss // 小程序公共样式
├── sitemap.json // 小程序搜索索引配置
└── project.config.json // 项目配置文件
app.js 小程序的主逻辑文件,定义小程序的全局生命周期函数和全局变量。
js
// app.js
import {
sleep
} from './utils/index'
App({
onLaunch(options) {
// 初始化应用 或者 小程序销毁 冷启动时执行的钩子
console.log('App---onLaunch', options);
this.getUserInfo()
},
onShow(options) {
// 热启动 冷启动时执行 + 后台切到前台执行
console.log('App---onShow', options);
},
onHide() {
// 前台切到后台执行
console.log('App---onHide');
},
onError(msg) {
console.log('App---onError', msg);
},
// 获取用户信息并返回 Promise
getUserInfo() {
if (!this.globalData.userInfoPromise) {
this.globalData.userInfoPromise = async () => {
// 模拟请求接口
await sleep(2000)
return {
id: "7979",
userName: "zs"
}
}
return this.globalData.userInfoPromise;
}
},
globalData: { // 整个应用公用的数据
age: 19,
user: {},
userInfoPromise: null
},
randomId() { // 整个应用公用的方法
},
})
// 其他页面使用
const app = getApp()
Page({
data: {
num: 0,
user: {},
},
onShow(){
console.log('[index]:onShow---start.globalData', app.globalData);
console.log('[index]:onShow---start---randomId', app.randomId());
this.getUser()
},
async getUser(){
const res = await app.globalData.userInfoPromise()
this.setData({ user: res })
}
});
小程序JSON 配置
app.json
developers.weixin.qq.com/miniprogram...
小程序的所有页面路径、界面表现、网络超时时间、底部 tab 等
json
{
"pages": [
// 应用根目录的页面路径
"pages/index/index"
],
"subPackages": [
// 分包结构配置
{
"root": "animal", // 分包根目录
"pages": ["pages/cat/cat"]
},
{
"root": "fruit",
"pages": ["pages/apple/apple"]
}
],
"tabBar": {
// 底部 tab 栏的表现
"list": [
{
"pagePath": "pages/index/index",
"text": "首页"
},
{
"pagePath": "pages/logs/index",
"text": "日志"
}
]
},
"window": {
// 全局的默认窗口表现
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "",
"navigationBarTextStyle": "black"
},
"usingComponents": {
// 全局自定义组件配置
"dialog": "/component/dialog/dialog",
"van-toast": "@vant/weapp/toast/index"
},
"networkTimeout": {
// 网络超时时间
"request": 10000,
"downloadFile": 10000
},
"requiredPrivateInfos": ["getLocation"],
"debug": true
}
project.config.json 项目配置
developers.weixin.qq.com/miniprogram...
json
"appid": "xxx",
"setting":{ // 可以开发者工具-详情-本地设置可以对应上
"urlCheck": false, // 是否检查安全域名和 TLS 版本
"es6": true, // 是否启用 es6 转 es5
"enhance": true, // es6 和 enhance 需同时为 true/false,对应于 将 JS 编译成 ES5
"postcss": true, // 上传代码时样式是否自动补全
}
page.json 页面配置
developers.weixin.qq.com/miniprogram...
js
{
"navigationStyle": "custom", // 自定义导航栏,只保留右上角胶囊按钮。
"enablePullDownRefresh": true, // 开启当前页面下拉刷新
}
// 自定义导航栏返回和title盒子可能需要 获取菜单按钮(右上角胶囊按钮)的布局位置信息 以保持底部对齐
const { height, top } = wx.getMenuButtonBoundingClientRect()
height + top 即右上角胶囊按钮的位置
WXML 模板
基础组件
developers.weixin.qq.com/miniprogram...
js
view
text [selectable]
button [type size plain disabled loading]
image [src mode lazy-load]
picker 普通选择器 | 多列选择器 | 时间选择器 | 日期选择器 | 省市区选择器
navigator [url open-type]
swiper swiper-item
scroll-view
web-view
事件 bindxxx="" catchxxx=""
developers.weixin.qq.com/miniprogram...
js
<view id="tapTest" data-hi="Weixin" bindtap="tapName"> Click me! </view>
Page({
tapName: function(event) {
console.log(event)
}
})
与 bind 不同, catch 会阻止事件向上冒泡
小程序语法
developers.weixin.qq.com/miniprogram...
- 数据绑定
- 列表渲染
- 条件渲染
js
<block wx:for="{{list}}" wx:key="index">
<view bindtap='onTap' data-item="{{item}}">
{{index}} --- {{item.text}}
</view>
</block>
<view wx:if="{{view == 'WEBVIEW'}}"> WEBVIEW </view>
<view wx:elif="{{view == 'APP'}}"> APP </view>
<view wx:else="{{view == 'MINA'}}"> MINA </view>
Page({
data: {
view: "APP",
list:[
{id:"11", text:"zs"},
{id:"13", text:"ls"},
{id:"14", text:"ww"},
]
},
onTap(event){
console.log('onTap---event', event)
const {item} = event.currentTarget.dataset
console.log('onTap---item', item) // {id:"14", text:"ww"}
},
});
- 更改数据 Page.prototype.setData(Object data, Function callback)
developers.weixin.qq.com/miniprogram...
js
<view>{{num}}</view>
<button bindtap="changeNum"> Change normal num </button>
<view>{{array[0].text}}</view>
<button bindtap="changeItemInArray"> Change Array data </button>
<view>{{object.text}}</view>
<button bindtap="changeItemInObject"> Change Object data </button>
<view>{{newField.text}}</view>
<button bindtap="addNewField"> Add new data </button>
// index.js
Page({
data: {
num: 0,
array: [{
text: 'init data'
}],
object: {
text: 'init data'
}
},
changeNum: function () {
this.data.num = 1
this.setData({
num: this.data.num
})
},
changeItemInArray: function () {
// 对于对象或数组字段,可以直接修改一个其下的子字段,这样做通常比修改整个对象或数组更好
this.setData({
'array[0].text': 'changed data'
})
},
changeItemInObject: function () {
this.setData({
'object.text': 'changed data'
});
},
addNewField: function () {
// 并且不需要在 this.data 中预先定义
this.setData({
'newField.text': 'new data'
})
}
});
WXSS
developers.weixin.qq.com/miniprogram...
- 尺寸单位 rpx(responsive pixel): 可以根据屏幕宽度进行自适应
- 样式导入
css
@import "common.wxss"; // @import后跟需要导入的外联样式表的相对路径,用;表示语句结束
.middle-p {
padding:15px;
}
- 内联样式
css
<view style="color:{{color}};" />
- 选择器 类 id 标签 并集
小程序运行机制
developers.weixin.qq.com/miniprogram...
特性 | 冷启动 | 热启动 |
---|---|---|
启动条件 | 首次启动或销毁后重新启动 | 从后台切换到前台 |
资源加载 | 重新下载代码包和资源文件 | 无需重新加载资源 |
生命周期 | 触发 onLaunch onLoad onShow |
仅触发 onShow (app和页面) |
运行状态 | 完全重新初始化 | 保留之前的运行状态 |
启动速度 | 较慢 | 较快 |
适用场景 | 首次打开或长时间未使用后重新打开 | 从后台切换回小程序 |
-
切后台的方式包括但不限于以下几种:
点击右上角胶囊按钮离开小程序
iOS 从屏幕左侧右滑离开小程序
安卓点击返回键离开小程序
小程序前台运行时直接把微信切后台(手势或 Home 键)
小程序前台运行时直接锁屏
自定义组件
developers.weixin.qq.com/miniprogram...
组件基础
- 组件的声明与使用
js
在 组件名.json 中声明组件:
{
"component": true
}
- 组件的属性(Properties) 在微信小程序中,不能直接将一个方法作为属性值传递给子组件。属性值只能是基本类型(如字符串、数字、布尔值)或对象/数组等 JSON 兼容的数据结构,而不能直接传递函数。
js
<slot name="before"></slot>
<view>{{ title }}</view>
<view>{{ count }}</view>
<slot name="after"></slot>
Component({
behaviors:[require('../../behaviors/my-behavior.js')], // 类似mixins
options: {
multipleSlots: true // 在组件定义时的选项中启用多slot支持
},
properties: { // 接收父组件传递的属性
title: {
type: String, // 属性类型
value: '默认标题', // 默认值
},
count: {
type: Number,
value: 0,
observer(newValue, oldValue){ // 数据变化时触发
console.log('count---observer---newValue', newValue, oldValue);
}
},
},
data: { // 组件内部数据
kid: "97"
},
observers: { // 监听数据发生变化
'kid'(newValue) {
console.log('observers---kid', newValue);
}
},
lifetimes: { // 组件生命周期
attached() {
console.log('组件已挂载');
},
detached() {
console.log('组件已卸载');
},
},
methods: { // 组件内部方法
onChangeKid() {
console.log('myBehaviorData', this.data.myBehaviorData); // 获取 behaviors 中data数据
this.sharedMethod() // 调用 behaviors 中的方法
this.setData({
kid: 100
})
},
onTap() {
const myEventDetail = {
age: 19
}
this.triggerEvent('myevent', myEventDetail)
},
_buttonMet() {
console.log('_buttonMet---start');
}
},
});
- 组件的插槽(Slot)
js
// my-component 组件 默认插槽
<view>
<slot></slot>
</view>
// 父组件
<my-component>
<view>插入的内容</view>
</my-component>
// my-component 组件 具名插槽
<view>
<slot name="header"></slot>
<slot name="footer"></slot>
</view>
// 父组件
<my-component>
<view slot="header">头部内容</view>
<view slot="footer">底部内容</view>
</my-component>
- 组件的数据监听器 observers
- 组件的行为复用 behaviors
js
// my-Behavior.js
module.exports = Behavior({
behaviors: [],
properties: {
myBehaviorProperty: {
type: String
}
},
data: {
myBehaviorData: {
age:6
}
},
attached: function(){},
methods: {
sharedMethod() {
console.log('共享方法');
},
}
})
- 组件的外部样式 externalClasses
- 组件的样式隔离
js
options: {
styleIsolation: 'isolated', // 样式隔离
},
组件的通信
developers.weixin.qq.com/miniprogram...
- 父组件向子组件传递数据 [属性传递 + 父组件获取子组件实例并调用子组件内的方法]
- 子组件向父组件传递数据 [定义自定义事件并 triggerEvent]
js
// 父组件
<my-button user="{{user}}" count="{{count}}" bind:myevent="onMyevent" id="my-button"></my-button>
<button bindtap="onDataToChild">获取子组件实例并调用子实例方法传递数据</button>
const app = getApp()
Page({
data: {
count: 1,
user: { age: 19, name: "bwf"},
},
onMyevent(event){
// 父组件自定义事件接收子组件传递的数据
console.log('onMyevent---detail', event.detail); // { age: 999, name: "zs" }
},
onDataToChild(){
// selectComponent() 获取子组件实例对象 这样就可以直接访问组件的任意数据和方法
const child = this.selectComponent('#my-button');
child.onShow(this.data.user)
},
});
// 子组件
<view>user.name:{{user.name}}</view>
<view>count:{{count}}</view>
<view bind:tap="onTap">向父组件传递数据</view>
Component({
options: {
multipleSlots: true // 在组件定义时的选项中启用多slot支持
},
properties: { // 组件的属性列表
user: {
type: Object,
observer(newValue, oldValue) {
console.log('user---observer---newValue', newValue, oldValue);
}
},
count: {
type: Number,
},
},
methods: {
onTap() {
const myEventDetail = { age: 999, name: "zs" }
// triggerEvent触发父组件自定义的事件,并传递数据
this.triggerEvent('myevent', myEventDetail)
},
onShow(data){
console.log('onShow---data', data) // { age: 19, name: "bwf" }
}
},
})
app.js 和 页面xx.js 和 组件.js 配置上的区别
- app.js App 是整个小程序的入口,用于定义全局的配置和行为
js
App({
onLaunch(options) { ... }, // 小程序初始化时触发
onShow(options) { ... }, // 小程序启动或从后台进入前台时触发
onHide() { ... }, // 小程序从前台进入后台时触发
globalData: { ... }, // 全局数据
aFn(){}, // 自定义全局方法
});
- 页面xx.js Page 用于定义单个页面的配置和行为。
js
Page({
data: { ... }, // 页面数据
onLoad(options) { ... }, // 页面加载时触发
onShow() { ... }, // 页面显示时触发
onReady() { ... }, // 页面初次渲染完成时触发
onHide() { ... }, // 页面隐藏时触发
onUnload() { ... }, // 页面卸载时触发
customMethod() { ... }, // 自定义方法
});
- 组件xx.js Component
js
Component({
properties: { ... }, // 组件接收父传递过来的属性
data: { ... }, // 组件内部数据
methods: { ... }, // 组件方法(必须放在 methods 中)
lifetimes: { // 组件生命周期
created() {}, // 组件实例刚被创建时触发(不能调用 setData)
attached() {}, // 组件实例进入页面节点树时触发
ready() {}, // 组件布局完成(类似页面的 onReady)
moved() {}, // 组件实例被移动到节点树另一个位置时触发
detached() {}, // 组件实例从页面节点树移除时触发
},
pageLifetimes: { // 页面生命周期(仅在组件所在页面触发)
show() {}, // 页面显示时触发
hide() {}, // 页面隐藏时触发
},
});
网络服务
网络请求 API:wx.request
js
wx.request({
url: 'https://api.example.com/data', // 请求地址
method: 'GET', // 请求方法(GET、POST 等)
data: { // 请求参数
key1: 'value1',
key2: 'value2',
},
header: { // 请求头
'Content-Type': 'application/json',
},
success(res) { // 请求成功回调
console.log(res.data); // 返回的数据
},
fail(err) { // 请求失败回调
console.error('请求失败', err);
},
complete() { // 请求完成回调(无论成功或失败)
console.log('请求完成');
},
});
封装网路请求
js
// utils/request.js
const request = (url, method = 'GET', data = {}, header = {}) => {
// 请求拦截:添加 token
const token = wx.getStorageSync('token');
if (token) {
header.Authorization = `Bearer ${token}`;
}
return new Promise((resolve, reject) => {
wx.request({
url: `https://api.example.com${url}`,
method,
data,
header: {
'Content-Type': 'application/json',
...header,
},
success(res) {
// '0000' 为自定义的业务成功码,可根据实际情况替换
if (res.statusCode === 200 && res.data.code === '0000') {
resolve(res.data.data);
} else {
reject(res.data);
}
},
fail(err) {
reject(err);
},
});
});
};
// GET 请求封装
const get = (url, data = {}) => {
return request(url, 'GET', data);
};
// POST 请求封装
const post = (url, data = {}) => {
return request(url, 'POST', data);
};
module.exports = { get, post };
文件上传
js
wx.uploadFile({
url: 'https://api.example.com/upload',
filePath: '文件路径',
name: 'file',
formData: { // 其他表单数据
key: 'value',
},
header: {},
success(res) {
console.log(res.data);
},
fail(err) {
console.error('上传失败', err);
},
});
注意点
域名限制:小程序只能请求已配置到 request 合法域名列表中的地址。 HTTPS:小程序要求请求的地址必须是 HTTPS。 并发限制:小程序最多同时发起 10 个网络请求。 在小程序后台配置合法域名,否则无法发起网络请求。 开发阶段:在开发者工具中勾选「不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书」
rust
微信公众平台 「开发」->「开发设置」 在「服务器域名」中配置 request 合法域名
存储
developers.weixin.qq.com/miniprogram...
-
存储 对本地缓存进行读写和清理 wx.setStorage/wx.setStorageSync、wx.getStorage/wx.getStorageSync、wx.clearStorage/wx.clearStorageSync,wx.removeStorage/wx.removeStorageSync
-
隔离策略 同一个微信用户,同一个小程序 storage 上限为 10MB。storage 以用户维度隔离,同一台设备上,A 用户无法读取到 B 用户的数据;不同小程序之间也无法互相读写数据。
分包
developers.weixin.qq.com/miniprogram...
页面间跳转
- wx.navigateTo
js
用途:保留当前页面,跳转到新页面(页面栈新增一层)。[当前页面的数据并不会被清空重置]
特点:可通过返回按钮回到原页面。
wx.navigateTo({
url: '/pages/detail/detail?id=123&name=bwf'
})
// detail接收参数
Page({
onLoad(options) {
const { id, name } = options; // 123 bwf
}
})
- wx.redirectTo
js
用途:关闭当前页面,跳转到新页面(替换当前页面栈)。
特点:无法返回原页。
- wx.redirectTo
js
用途:关闭当前页面,跳转到新页面(替换当前页面栈)。
特点:无法返回原页。
- wx.switchTab
js
用途:跳转到 tabBar 页面(需在 app.json 中定义)。
特点:会清空页面栈,只能跳转 tabBar 页面。
- wx.reLaunch
js
用途:关闭所有页面,打开新页面(重置页面栈)。
场景:如用户登录后重置整个应用状态。
- wx.navigateBack
js
用途:返回上一页面或多层页面。
参数:通过 delta 指定返回层数。
wx.navigateBack({
delta: 1 // 返回上一层
})
导航栏与交互
js
// 设置导航栏标题 ,在页面 .json 文件中设置
{
"navigationBarTitleText": "详情页"
}
// 动态修改:使用 wx.setNavigationBarTitle
wx.setNavigationBarTitle({
title: '新标题'
})
// 自定义返回按钮
{
"navigationStyle": "custom"
}
第三方UI vant-weapp
vant-ui.github.io/vant-weapp/...
npm 安装
kotlin
如果项目没有 package.json,先运行 npm init -y 初始化
npm install @vant/weapp
构建 npm 包
css
打开微信开发者工具,点击菜单栏的 工具 -> 构建 npm。
确保项目根目录下生成 miniprogram_npm 文件夹,里面包含 @vant。
app.json 或 页面.json 中注册组件
js
{
"usingComponents": {
"van-button": "@vant/weapp/button/index"
}
}
使用 Vant 组件
js
<van-button type="primary" bind:click="onClick">点击我</van-button>
<van-toast id="van-toast" />
onClick(){
console.log('onClick');
Toast('我是提示文案,建议不超过十五字~');
},
web-view
developers.weixin.qq.com/miniprogram... 在微信小程序中使用 web-view 组件可以嵌入网页内容
- 配置业务域名
js
在小程序后台 开发管理 -> 开发设置 -> 业务域名 中添加网页的 HTTPS 域名(需下载校验文件并部署到域名根目录)。
本地调试时,可在开发者工具中勾选 不校验合法域名(仅限开发环境)。
- 在 .wxml 文件中嵌入网页:
js
<web-view src="https://example.com?user_id=123&token=abc" bindmessage="onWebMessage"></web-view>
Page({
onWebMessage(e) { // 在特定时机触发并收到消息:小程序后退、组件销毁、分享、复制链接
console.log('收到网页消息:', e.detail.data); // data是多次 postMessage 的参数组成的数组
}
});
// h5网页内代码
document.title = '新标题'; // h5网页设置小程序标题
import wx from 'weixin-js-sdk'
wx.miniProgram.postMessage({ data: { action: 'login' } });
- 网页向小程序发送消息
css
小程序 向 h5页面 传递数据:通过url后缀参数,h5通过地址栏解析参数获取
h5页面 向 小程序 传递数据:h5通过postMessage, 小程序通过 bindmessage 获取
数据共享
- 在 app.js 中定义 globalData,通过 getApp() 获取全局数据
js
// app.js
App({
globalData: {
userInfo: null,
},
});
// page.js
const app = getApp();
Page({
onLoad() {
console.log(app.globalData.userInfo);
},
});
- 本地存储(Storage)wx.setStorageSync 和 wx.getStorageSync 进行数据存储和读取
- 页面间通信 过路由跳转时传递参数(wx.navigateTo 或 wx.redirectTo)。
js
// 页面 A
wx.navigateTo({
url: '/pages/pageB?data=' + JSON.stringify(data),
});
// 页面 B
Page({
onLoad(options) {
const data = JSON.parse(options.data);
},
});
- 组件间通信 通过 【父向子】属性+子组件实例;【子向父】自定义方法 + triggerEvent
- 第三方库MobX
第三方库MobX
js
npm i mobx-miniprogram mobx-miniprogram-bindings
// store.js
import { observable, action } from 'mobx-miniprogram';
export const useUserStore = observable({
count: 2,
user: null,
increment: action(function () {
this.count++;
}),
setUser: action(function (user) {
this.user = user
}),
});
// page.js
import {
createStoreBindings
} from 'mobx-miniprogram-bindings';
import {
useUserStore
} from '../../stores/useUserStore';
Page({
data: { },
onLoad(options) {
this.storeBindings = createStoreBindings(this, {
store: useUserStore,
fields: ['count', "user"],
actions: ['increment', 'setUser'],
});
},
onUnload() {
// 解除绑定
this.storeBindings.destoryStoreBindings()
},
onAdd() {
console.log('onAdd', this.data.count); // store中的count
this.increment()
},
onSetUser() {
this.setUser({ name:"zs", id:"99" })
},
});
//page.wxml
<view>
<view>index.wxml</view>
<view>store---count:{{count}}</view>
<button bind:tap="onAdd">add count</button>
<view>store---user.id:{{user.id}}</view>
<button bind:tap="onSetUser">set user</button>
</view>
vConsole
点击屏幕右上角的按钮打开的菜单里选择「打开调试」。此时小程序/小游戏会退出,重新打开后右下角会出现一个 vConsole 按钮。点击 vConsole 按钮可以打开日志面板。
核心api
fail can only be invoked by user TAP gesture:须用户授权才可以调用此api [如点击弹窗中的确认才可以获取]
碰到api官方文档提示用户授权 :则需要 wx.getSetting 和弹窗确认后 wx.openSetting({}) 打开对应的权限开关,才可调用正常 developers.weixin.qq.com/miniprogram...
异步 API 支持 callback & promise 两种调用方式。当接口参数 Object 对象中不包含 success/fail/complete 时将默认返回 promise,否则仍按回调方式执行,无返回值。【碰到授权情况,不可使用 await 等模式,尽量使用 callback 模式】
网络请求
wx.request
上传文件
wx.uploadFile
数据存储
wx.setStorage / wx.setStorageSync wx.removeStorage / wx.removeStorageSync wx.clearStorage / wx.clearStorageSync
界面交互
- wx.showToast
js
wx.showToast({
title: '操作成功',
icon: 'success',
duration: 2000,
});
- wx.showModal
js
async onTap() {
const res = await wx.showModal({
title: '提示',
content: '确定删除吗?',
});
console.log('res', res);
if (res.confirm) {
console.log('用户点击确定');
}
},
- wx.showLoading / wx.hideLoading
js
wx.showLoading({ title: '加载中' });
setTimeout(() => { wx.hideLoading(); }, 2000);
- wx.showActionSheet 显示操作菜单
js
async onTap() {
const res = await wx.showActionSheet({
itemList: ['A', 'B', 'C'],
});
console.log('res', res.tapIndex);
},
路由与导航
wx.navigateTo wx.redirectTo wx.switchTab wx.navigateBack
wx.onNetworkStatusChange
监听网络状态变化
媒体与文件
- wx.chooseMedia 从相册或相机选择图片
- wx.previewImage 预览图片
- wx.saveImageToPhotosAlbum 下载保存到相册
- 以下示例:上传/预览/下载
js
<view>
<view>index.wxml</view>
<image src="{{src}}" mode="" bind:tap="onPreview"/>
<button bind:tap="onChooseImage">chooseImage</button>
<button bind:tap="onSaveImageToPhotosAlbum">saveImageToPhotosAlbum</button>
</view>
Page({
data: {
src: ""
},
async onChooseImage() {
const res = await wx.chooseMedia()
this.setData({
src: res.tempFiles[0].tempFilePath
})
},
onPreview() {
wx.previewImage({urls: [this.data.src],});
},
async onSaveImageToPhotosAlbum() {
const res = await wx.saveImageToPhotosAlbum({
filePath: this.data.src
}).catch(async error => {
console.error('error', error);
const { authSetting } = await wx.getSetting()
if (!authSetting['scope.writePhotosAlbum']) {
wx.showModal({
title: '是否授权图片保存到相册',
content: '请在设置中打开授权',
success: async res => {
if (res.confirm) {
// openSetting需要放到showModal里面,否则无法打开设置权限页面
const { authSetting } = await wx.openSetting({})
console.log('openSetting---authSetting', authSetting);
if (authSetting['scope.writePhotosAlbum']) {
await wx.saveImageToPhotosAlbum({
filePath: this.data.src
})
}
}else{
wx.showToast({
title: '此操作需要摄像头权限',
icon:"none"
})
}
}
})
}
throw new Error(error)
})
console.log('saveImageToPhotosAlbum---end---res', res);
}
});
map openLocation 位置与地图
js
<map id="myMap" bindtap="onMap" latitude="{{latitude}}" longitude="{{longitude}}" markers="{{markers}}" bindmarkertap="onMarkerTap" style="width: 100%; height: 500px;"></map>
Page({
data: {
latitude: 39.90469,
longitude: 116.40717,
markers: [ // 标记点数组
{
id: 1,
latitude: 39.90469,
longitude: 116.40717,
name: '北京',
// iconPath: '/images/marker.png',
},
],
polyline: [
{
points: [ // 路线数组
{ latitude: 39.90469, longitude: 116.40717 },
{ latitude: 39.90469, longitude: 116.41370 },
],
color: '#FF0000',
width: 2,
},
],
},
onMarkerTap(e) {
console.log('标记点被点击', e.markerId);
},
onReady() {
const mapCtx = wx.createMapContext('myMap');
mapCtx.moveToLocation();
},
onMap(){
wx.openLocation({ // 打开地图
latitude: this.data.latitude,
longitude: this.data.longitude,
})
},
});
login getUserProfile 用户信息
js
<button bindtap="onGetUser">user</button>
Page({
data: {},
async onGetUser() {
const { code } = await wx.login({});
console.log('code', code);
// fail can only be invoked by user TAP gesture 须用户点击弹窗中的确认才可以获取
wx.showModal({
title: '提示',
content: '用于完善会员资料',
success: async(res) => {
if (res.confirm) {
const {
userInfo
} = await wx.getUserProfile({
desc: '用于完善会员资料',
});
console.log('userInfo', userInfo);
}
}
})
}
});
scanCode 扫码
js
wx.scanCode({
success(res) {
const productId = res.result; // 获取商品 ID
// wx.navigateTo({
// url: `/pages/productDetail/productDetail?id=${productId}`,
// });
},
});
拍照 camera takePhoto uploadFile
js
<view class="container">
<!-- 摄像头 -->
<camera device-position="back" flash="off" binderror="error" style="width: 100%; height: 80vh;"></camera>
<!-- 按钮 -->
<view class="button-container">
<view class="progress-ring" style="{{progressStyle}}"></view>
<view class="button" bindtouchstart="onTouchStart" bindtouchend="onTouchEnd">按住拍照</view>
</view>
</view>
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
}
.button-container {
position: relative;
width: 100px;
height: 100px;
}
.progress-ring {
position: absolute;
width: 100px;
height: 100px;
border-radius: 50%;
background: transparent;
border: 10px solid rgba(0, 0, 0, 0.1); /* 默认背景 */
border-top: 10px solid #4caf50; /* 进度条颜色 */
animation: none; /* 动画初始化 */
}
.button {
position: absolute;
top: 10px;
left: 10px;
width: 100px;
height: 100px;
border-radius: 50%;
background-color: #ffffff;
color: #4caf50;
font-size: 14px;
line-height: 100px;
text-align: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
Page({
data: {
progressStyle: '', // 控制进度条样式
timer: null, // 定时器
progress: 0, // 当前进度
},
onTouchStart() {
this.setData({ progress: 0 }); // 初始化进度
this.startProgress(); // 开始进度条动画
},
onTouchEnd() {
this.stopProgress(); // 停止进度条动画
},
startProgress() {
const MAX_PROGRESS = 360; // 总进度
const DURATION = 4000; // 总时间(4秒)
let currentProgress = this.data.progress;
this.setData({
timer: setInterval(() => {
if (currentProgress >= MAX_PROGRESS) {
this.stopProgress(); // 达到最大进度,停止
this.takePhoto(); // 拍照
} else {
currentProgress += (MAX_PROGRESS / (DURATION / 100)); // 每次增加的角度
this.setData({
progressStyle: `transform: rotate(${currentProgress}deg);`,
});
}
}, 100), // 每100ms更新一次
});
},
stopProgress() {
clearInterval(this.data.timer); // 清除定时器
this.setData({ timer: null, progressStyle: '' }); // 重置样式
},
takePhoto() {
const ctx = wx.createCameraContext();
ctx.takePhoto({
quality: 'high',
success: (res) => {
console.log('照片路径:', res.tempImagePath);
this.uploadFile(res.tempImagePath); // 上传图片
},
fail: (err) => {
console.error('拍照失败:', err);
},
});
},
uploadFile(filePath) {
wx.uploadFile({
url: 'https://example.com/upload', // 替换为你的服务器地址
filePath: filePath,
name: 'file',
success: (res) => {
console.log('上传成功:', res);
},
fail: (err) => {
console.error('上传失败:', err);
},
});
},
});