微信小程序—魔镜 笔记&源码(微信开发者工具+JS+UI组件库+云开发)

效果展示

视频演示

经历了一次升级之后,我的小程序怎么样了?

文章目录

  • 微信小程序—魔镜 笔记&源码(微信开发者工具+JS+UI组件库+云开发)
      • 0.准备工作
      • 1.搭建环境
      • 2.引入WuxWeapp组件库或者VantUI组件库
      • 3.使用Wux组件库
      • 4.微信开发者工具配置
    • 正式开发
      • 1.自定义底部导航tabbar
      • 2.使用压屏窗
      • 3.使用动画组
      • ~~6.使用svg(小程序不支持)~~
      • 7.取景框拍照实现
      • 8.takePhoto页
        • 使用到的局部数据
        • 使用的全局数据
        • takePhoto页主要逻辑
      • 9.Scanning页
        • 引用的外部js文件
        • 人脸检测与皮肤分析主要逻辑
      • 10.云函数的使用
      • 11.Home页
        • 使用模板template
        • 使用fabButton浮动按钮
        • 卡片1
        • 卡片2
        • 卡片3
        • limit查询加载首页推荐列表
        • 浏览文章页
        • 半屏窗
      • 12.Shop购物页
      • 13.富文本编辑及提交存储

0.准备工作

Wux组件库官方文档:https://www.wuxui.com/点击转到文档
微信小程序官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/点击转到文档
微信开发者工具下载:https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html点击转到下载页

1.搭建环境

安装npm安装组件库

安装node
检查node版本 node -v
检查npm版本 npm -v

或者安装yarn安装组件库

npm install -g yarn

2.引入WuxWeapp组件库或者VantUI组件库

# Using npm install Wux UI
npm i wux-weapp -S --production
# 通过 npm 安装 vant UI
npm i @vant/weapp -S --production
#直接下载源码
git clone https://github.com/wux-weapp/wux-weapp.git D:\File\Project\Download
//‘cnpm‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件,则安装cnpm
//npm install -g cnpm --registry=https://registry.npm.taobao.org//cnpm -v//'cross-env' 不是内部或外部命令,也不是可运行的程序,则安装cross-env
//cnpm i cross-env --save-dev//'gulp'不是内部或者外部命令,也不是可运行的程序或批处理文件,则安装
//npm install gulp -g//提示try running:yarn add gulp,则执行
//yarn add gulp//提示error An unexpected error occurred: "ENOENT: no such file or directory, copyfile 'C:\\Users\\WangZhuo\\AppData\\Local\\Yarn\\Cache\\v6\\npm-shebang-regex-1.0.0-da42f49740c0b42db2ca9728571cb190c98efea3-integrity\\node_modules\\shebang-regex\\index.js' -> 'D:\\File\\Project\\Download\\wux-weapp\\node_modules\\cross-spawn\\node_modules\\shebang-regex\\index.js'". 则//删除国外代理
//npm config rm proxy
//npm config rm https-proxy
//更换成国内镜像
//yarn config set sass-binary-site http://npm.taobao.org/mirrors/node-sass安装依赖包
npm install在wux-weapp文件夹下,编译
npm run build -- --config ./config.custom.json --output ./build

3.修改app.json

将 app.json 中的 "style": "v2" 去除

4.修改project.config.json,在settings中添加

"packNpmManually": true,"packNpmRelationList": [{"packageJsonPath": "./package.json","miniprogramNpmDistDir": "./"}

开发工具会默认在当前目录下创建miniprogram_npm的文件名,所以新版本的miniprogramNpmDistDir配置为’./'即可

5.构建npm包

点击 工具 -> 构建 npm,并勾选 使用 npm 模块 选项,构建完成后,即可引入组件

3.使用Wux组件库

全局使用
app.json配置
{"usingComponents": {"wux-button": "/miniprogram_npm/wux-weapp/button/index", //按钮"wux-icon": "/miniprogram_npm/wux-weapp/icon/index", //icon图标"wux-badge": "/miniprogram_npm/wux-weapp/badge/index", "wux-tabbar": "/miniprogram_npm/wux-weapp/tabbar/index", //底部触摸栏"wux-tabbar-item": "/miniprogram_npm/wux-weapp/tabbar-item/index"}
}

4.微信开发者工具配置

"checkSiteMap":false  //project.config.json里面去进行配置,有个checkSiteMap的配置项,默认为true,改成false就不会报sitemap的警告//2.18.1 加入了组件属性和对应数据的类型匹配检查,如果不匹配就会展示the type of property "xx" is illegal

5.魔镜主题色

@positive: #387ef5;
@calm: #6a9ef8;//75%@positive//#11c1f3
@balanced: #a5c5fb;//45%@positive//#33cd5f
@energized: #ffb9c1;//45%@royal//#ffc900
@assertive: #ff8b98;//75%@royal//#ef473a
@royal: #ff6476;//#886aea

6.项目目录结构

正式开发

1.自定义底部导航tabbar

①app.json

"tabBar": {"custom": true,"list": [{"pagePath": "pages/home/home"},{"pagePath": "pages/person/person"},{"pagePath": "pages/shop/shop"}]},"usingComponents": {}

②根目录下创建custom-tab-bar文件夹

在custom-tab-bar下创建四个文件

custom-tab-bar/index.js
custom-tab-bar/index.json
custom-tab-bar/index.wxml
custom-tab-bar/index.wxss

③在custom-tab-bar/index.js中写switchTap方法

Page({data: {// current: '0',pagePath:['/pages/home/home','----忽略----','/pages/shop/shop','/pages/person/person']},onChange(e) {// console.log('onChange', e.detail)const that=this;if(e.detail.key!==1){//非扫一扫var pageUrl=that.data.path[e.detail.key]wx.switchTab({url:pageUrl})}else{//扫一扫}},})

④在跳转的目标页.js中写getTabBar 获取当前页面下的自定义 tabBar 组件实例

并setData()更新current,tab栏icon变为选中

page({ onShow: function () {if (typeof this.getTabBar === 'function' &&this.getTabBar()) {this.getTabBar().setData({current: 0})}}
})

2.使用压屏窗

①app.json中引入

app.json
"usingComponents":{"wux-landscape": "./components/lib/landscape/index"
}

②在custom-tab-bar/index.wxml添加

<wux-landscape visible="{{ visible1 }}" bind:close="onClose1" maskClosable="{{true}}"><view class='SVGcontainer'><image class='SVG' src="{{bgImg}}" mode='aspectFit' style="height:140px;width:130px" /><wux-white-space body-style="height: 25px" /><wux-button block size="small" type="assertive" bind:click="navToCamera" data-way="takePhoto">拍摄照片</wux-button><wux-white-space body-style="height: 12px" /><wux-button block size="small" type="assertive" bind:click="navToCamera" data-way="chooseGallery">相册选择</wux-button></view>
</wux-landscape>

③在custom-tab-bar/index.wxss中添加

/**index.wxss**/
.SVG{top:-80px;position: absolute;left:41px;
}
.SVGcontainer{margin:40px auto 0 auto;height:200px;width: 220px;align-items: center;background-color: rgb(255, 255, 255);padding: 30px;box-sizing: border-box;border-radius: 15px;border:5px solid rgb(255, 197, 210);position: relative;
}

④在custom-tab-bar/index.js中增加

 data: {current: 0,//记录当前tabbar选中对象last: 0,//记录上次tabbar选中对象bgImg:'data:image/svg+xml;base64,77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjxzdmcgdmVyc2lvbj0iMS4xIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjE0NHB4IiBoZWlnaHQ9IjE0NHB4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPg0KICA8ZGVmcz4NCiAgICA8cGF0aCBkPSJNIDAgMCAgTCAxMjkzIDAgIEwgMTI5MyAxMDI0ICBMIDAgMTAyNCAgWiAiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZmlsbD0iYmxhY2siIGlkPSJwYXRoMjAiIC8+DQogICAgPGNsaXBQYXRoIGlkPSJjbGlwMjEiPg0KICAgICAgPHVzZSB4bGluazpocmVmPSIjcGF0aDIwIiAvPg0KICAgIDwvY2xpcFBhdGg+DQogICAgPGZpbHRlciB4PSItNTAuMDAlIiB5PSItNTAuMDAlIiB3aWR0aD0iMjAwLjAwJSIgaGVpZ2h0PSIyMDAuMDAlIiBmaWx0ZXJVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giIGlkPSJmaWx0ZXIyMiI+DQogICAgICA8ZmVDb2xvck1hdHJpeCB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMSAwIDAgMCAwICAwIDEgMCAwIDAgIDAgMCAxIDAgMCAgMCAwIDAgMSAwICAiIGluPSJTb3VyY2VHcmFwaGljIiAvPg0KICAgIDwvZmlsdGVyPg0KICAgIDxmaWx0ZXIgeD0iMTE1cHgiIHk9IjE0M3B4IiB3aWR0aD0iMTQ0cHgiIGhlaWdodD0iMTQ0cHgiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgaWQ9ImZpbHRlcjIzIj4NCiAgICAgIDxmZU9mZnNldCBkeD0iMCIgZHk9IjUiIGluPSJTb3VyY2VBbHBoYSIgcmVzdWx0PSJzaGFkb3dPZmZzZXRJbm5lciIgLz4NCiAgICAgIDxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjQiIGluPSJzaGFkb3dPZmZzZXRJbm5lciIgcmVzdWx0PSJzaGFkb3dHYXVzc2lhbiIgLz4NCiAgICAgIDxmZUNvbXBvc2l0ZSBpbjI9InNoYWRvd0dhdXNzaWFuIiBvcGVyYXRvcj0iYXRvcCIgaW49IlNvdXJjZUFscGhhIiByZXN1bHQ9InNoYWRvd0NvbXBvc2l0ZSIgLz4NCiAgICAgIDxmZUNvbG9yTWF0cml4IHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwIDAgMCAwIDAuMTkyMTU2ODYyNzQ1MDk4ICAwIDAgMCAwIDAuMTkyMTU2ODYyNzQ1MDk4ICAwIDAgMCAwIDAuMTkyMTU2ODYyNzQ1MDk4ICAwIDAgMCAwLjEyMTU2ODYyNzQ1MDk4IDAgICIgaW49InNoYWRvd0NvbXBvc2l0ZSIgLz4NCiAgICA8L2ZpbHRlcj4NCiAgICA8ZyBpZD0id2lkZ2V0MjQiPg0KICAgICAgPGcgdHJhbnNmb3JtPSJtYXRyaXgoMC4wOTg5OTQ1ODYyMzM1NjU0IDAgMCAwLjA5ODk5NDU4NjIzMzU2NTQgMTIzIDE1OS4zMTQ3NzE4NDg0MTUgKSIgY2xpcC1wYXRoPSJ1cmwoI2NsaXAyMSkiIGZpbHRlcj0idXJsKCNmaWx0ZXIyMikiPg0KICAgICAgICA8cGF0aCBkPSJNMjg3LjUyODM0NSAyNjAuNTgxMjU0bDQyLjg0NjMwNS0yMjMuNjYzMUExMi4xMjYzMTMgMTIuMTI2MzEzIDAgMCAxIDM0Mi4yMzE0ODkgMjYuOTQ3NjMxaDYwOS4wMTAzNjZhMTIuMTI2MzEzIDEyLjEyNjMxMyAwIDAgMSAxMS44NTY4MzkgOS45NzA1MjNsNDIuODQ2MzA0IDIyMy42NjMxeiIgZmlsbD0iI0ZEQ0E4OSIgcC1pZD0iNjEwMSIgZGF0YS1zcG0tYW5jaG9yLWlkPSJhMzEzeC43NzgxMDY5LjAuaTkiIGNsYXNzPSJzZWxlY3RlZCI+PC9wYXRoPg0KICAgICAgICA8cGF0aCBkPSJNOTM5LjExNTU0MiA1My44OTQ5OTJsMzQuMjIzMTQ5IDE3OS43Mzg5SDMyMC4xMzQ2NTNMMzU0LjM1NzgwMSA1My44OTQ5OTJoNTg0Ljc1Nzc0MW0xMi4zOTU3ODctNTMuODk0NzIzSDM0Mi4yMzE0ODlhMzkuMDczNjc0IDM5LjA3MzY3NCAwIDAgMC0zOC4yNjUyNTMgMzEuNzk3ODg3bC00OS4wNDQxOTggMjU1LjczMDQ1OWg3ODMuNjI5MjY4bC00OC43NzQ3MjQtMjU1LjczMDQ1OUEzOS4wNzM2NzQgMzkuMDczNjc0IDAgMCAwIDk1MS4yNDE4NTUgMC4wMDAyNjl6IiBmaWxsPSIjRkY2NDc2IiBwLWlkPSI2MTAyIiBkYXRhLXNwbS1hbmNob3ItaWQ9ImEzMTN4Ljc3ODEwNjkuMC5pNCIgY2xhc3M9InNlbGVjdGVkIj48L3BhdGg+DQogICAgICAgIDxwYXRoIGQ9Ik0yNi45NDczNjEgMTg4LjYzMTc5OWwxMjM5LjU3ODYyMSAwIDAgODA4LjQyMDg0LTEyMzkuNTc4NjIxIDAgMC04MDguNDIwODRaIiBmaWxsPSIjRkRBQ0JEIiBwLWlkPSI2MTAzIiBkYXRhLXNwbS1hbmNob3ItaWQ9ImEzMTN4Ljc3ODEwNjkuMC5pMCIgY2xhc3M9IiI+PC9wYXRoPg0KICAgICAgICA8cGF0aCBkPSJNMTIxMi42MzEyNiAyMTUuNTc5MTZhMjYuOTQ3MzYxIDI2Ljk0NzM2MSAwIDAgMSAyNi45NDczNjEgMjYuOTQ3MzYxdjcwMC42MzEzOTVhMjYuOTQ3MzYxIDI2Ljk0NzM2MSAwIDAgMS0yNi45NDczNjEgMjYuOTQ3MzYxSDgwLjg0MjA4NGEyNi45NDczNjEgMjYuOTQ3MzYxIDAgMCAxLTI2Ljk0NzM2MS0yNi45NDczNjFWMjQyLjUyNjUyMWEyNi45NDczNjEgMjYuOTQ3MzYxIDAgMCAxIDI2Ljk0NzM2MS0yNi45NDczNjFoMTEzMS43ODkxNzZtMC01My44OTQ3MjNIODAuODQyMDg0YTgwLjg0MjA4NCA4MC44NDIwODQgMCAwIDAtODAuODQyMDg0IDgwLjg0MjA4NHY3MDAuNjMxMzk1YTgwLjg0MjA4NCA4MC44NDIwODQgMCAwIDAgODAuODQyMDg0IDgwLjg0MjA4NGgxMTMxLjc4OTE3NmE4MC44NDIwODQgODAuODQyMDg0IDAgMCAwIDgwLjg0MjA4NC04MC44NDIwODRWMjQyLjUyNjUyMWE4MC44NDIwODQgODAuODQyMDg0IDAgMCAwLTgwLjg0MjA4NC04MC44NDIwODR6IiBmaWxsPSIjRkY2NDc2IiBwLWlkPSI2MTA0IiBkYXRhLXNwbS1hbmNob3ItaWQ9ImEzMTN4Ljc3ODEwNjkuMC5pMSIgY2xhc3M9IiI+PC9wYXRoPg0KICAgICAgICA8cGF0aCBkPSJNNjQ2LjczNjY3MiA4ODMuMzM0Nzc0QTI5MC40OTI1NTUgMjkwLjQ5MjU1NSAwIDEgMSA5MzcuMjI5MjI3IDU5Mi44NDIyMTkgMjkxLjAzMTUwMiAyOTEuMDMxNTAyIDAgMCAxIDY0Ni43MzY2NzIgODgzLjMzNDc3NHoiIGZpbGw9IiNGRkZGRkYiIHAtaWQ9IjYxMDUiPjwvcGF0aD4NCiAgICAgICAgPHBhdGggZD0iTTY0Ni43MzY2NzIgMzI5LjI5NzAyNUEyNjMuNTQ1MTk0IDI2My41NDUxOTQgMCAxIDEgMzgzLjE5MTQ3OCA1OTIuODQyMjE5IDI2My44MTQ2NjcgMjYzLjgxNDY2NyAwIDAgMSA2NDYuNzM2NjcyIDMyOS4yOTcwMjVtMC01My44OTQ3MjNBMzE3LjQzOTkxNiAzMTcuNDM5OTE2IDAgMSAwIDk2NC4xNzY1ODggNTkyLjg0MjIxOSAzMTcuNDM5OTE2IDMxNy40Mzk5MTYgMCAwIDAgNjQ2LjczNjY3MiAyNzUuNDAyMzAyeiIgZmlsbD0iI0ZGNjQ3NiIgcC1pZD0iNjEwNiIgZGF0YS1zcG0tYW5jaG9yLWlkPSJhMzEzeC43NzgxMDY5LjAuaTIiIGNsYXNzPSIiPjwvcGF0aD4NCiAgICAgICAgPHBhdGggZD0iTTY0Ni43MzY2NzIgNTkyLjg0MjIxOW0tMTIwLjE4NTIzMiAwYTEyMC4xODUyMzIgMTIwLjE4NTIzMiAwIDEgMCAyNDAuMzcwNDYzIDAgMTIwLjE4NTIzMiAxMjAuMTg1MjMyIDAgMSAwLTI0MC4zNzA0NjMgMFoiIGZpbGw9IiNGRjY0NzYiIHAtaWQ9IjYxMDciIGRhdGEtc3BtLWFuY2hvci1pZD0iYTMxM3guNzc4MTA2OS4wLmkxMCIgY2xhc3M9IiI+PC9wYXRoPg0KICAgICAgICA8cGF0aCBkPSJNNjQ2LjczNjY3MiA0OTkuNjA0MzQ5QTkzLjIzNzg3IDkzLjIzNzg3IDAgMSAxIDU1My40OTg4MDIgNTkyLjg0MjIxOSA5My4yMzc4NyA5My4yMzc4NyAwIDAgMSA2NDYuNzM2NjcyIDQ5OS42MDQzNDltMC01My44OTQ3MjNBMTQ3LjEzMjU5MyAxNDcuMTMyNTkzIDAgMSAwIDc5My44NjkyNjUgNTkyLjg0MjIxOSAxNDcuMTMyNTkzIDE0Ny4xMzI1OTMgMCAwIDAgNjQ2LjczNjY3MiA0NDUuNzA5NjI2eiIgZmlsbD0iI0ZGNjQ3NiIgcC1pZD0iNjEwOCIgZGF0YS1zcG0tYW5jaG9yLWlkPSJhMzEzeC43NzgxMDY5LjAuaTMiIGNsYXNzPSIiPjwvcGF0aD4NCiAgICAgICAgPHBhdGggZD0iTTEwMDcuMjkyMzY3IDI4MC43OTE3NzVsMTY5LjQ5ODkwMiAwIDAgODguOTI2MjkyLTE2OS40OTg5MDIgMCAwLTg4LjkyNjI5MloiIGZpbGw9IiNGRjY0NzYiIHAtaWQ9IjYxMDkiIGRhdGEtc3BtLWFuY2hvci1pZD0iYTMxM3guNzc4MTA2OS4wLmk2IiBjbGFzcz0iIj48L3BhdGg+DQogICAgICAgIDxwYXRoIGQ9Ik0xMTQ5Ljg0MzkwOCAzMDcuNzM5MTM2djM1LjAzMTU3aC0xMTUuNjA0MTh2LTM1LjAzMTU3aDExNS42MDQxOG00LjMxMTU3OC01My44OTQ3MjNoLTEyMy45NTc4NjJhNDkuNTgzMTQ1IDQ5LjU4MzE0NSAwIDAgMC00OS41ODMxNDUgNDkuNTgzMTQ1djQzLjM4NTI1MmE0OS41ODMxNDUgNDkuNTgzMTQ1IDAgMCAwIDQ5LjU4MzE0NSA0OS41ODMxNDVoMTIzLjk1Nzg2MmE0OS41ODMxNDUgNDkuNTgzMTQ1IDAgMCAwIDQ5LjU4MzE0NS00OS41ODMxNDV2LTQzLjM4NTI1MmE0OS41ODMxNDUgNDkuNTgzMTQ1IDAgMCAwLTQ5LjU4MzE0NS00OS41ODMxNDV6IiBmaWxsPSIjZmZmZmZmIiBwLWlkPSI2MTEwIiBkYXRhLXNwbS1hbmNob3ItaWQ9ImEzMTN4Ljc3ODEwNjkuMC5pNSIgY2xhc3M9IiI+PC9wYXRoPg0KICAgICAgPC9nPg0KICAgIDwvZz4NCiAgPC9kZWZzPg0KICA8ZyB0cmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAxIC0xMTUgLTE0MyApIj4NCiAgICA8dXNlIHhsaW5rOmhyZWY9IiN3aWRnZXQyNCIgZmlsdGVyPSJ1cmwoI2ZpbHRlcjIzKSIgLz4NCiAgICA8dXNlIHhsaW5rOmhyZWY9IiN3aWRnZXQyNCIgLz4NCiAgPC9nPg0KPC9zdmc+',//syg格式的粉色相机图片visible1: false,},
//显示压屏窗onOpen1() {this.setData({current: 1,visible1: true,})},//tabbar栏1号被点击chooseScan(){this.setData({last:this.data.current})this.onOpen1();console.log('-----可以忽略此问题-----');},//隐藏压屏窗onClose1() {const that=this;this.setData({current: that.data.last,visible1: false,})},//跳转到处理页面
async navToCamera(e){console.log(e.target.dataset['way'])var choice=e.target.dataset['way'];// await this.onClose1()//隐藏压屏窗if(choice==='takePhoto'){//调用相机拍照//跳转到拍摄页面await wx.navigateTo({url: '/pages/takePhoto/takePhoto',})}else if(choice==='chooseGallery'){//从相册选择wx.chooseImage({count: 1,sizeType: ['original'],sourceType: ['album', 'camera'],success (res) {// tempFilePath可以作为img标签的src属性显示图片const tempFilePaths = res.tempFilePathsconsole.log(tempFilePaths[0]);}})}}

3.使用动画组

wxml

Switch state<switch data-model="example.in" bindchange="switchChange" checked="{{ example.in }}" /><wux-animation-groupin="{{ example.in }}"enter="{{ true}}"exit="{{ true }}"class-names="wux-animate--fadeInDown"><view>微信小程序自定义组件 https://github.com/wux-weapp/wux-weapp</view></wux-animation-group>

js

  switchChange(e) {console.log(e.currentTarget.dataset);console.log(e.detail)const { model } = e.currentTarget.datasetthis.setData({[model]: e.detail.value,})}

6.使用svg(小程序不支持)

<view class="svg_outline"><image class="svg_outline-inner" src="/images/取景框/outline.svg"/>
</view>

7.取景框拍照实现

takePhoto.wxml

  <view class='cameraContainer'><wux-button class='loading' block loading type="light">相机加载中...</wux-button><camera class='camer' device-position="{{camera.position}}" flash="{{camera.flash}}" binderror="{{camera.binderror}}" frame-size="{{camera.framesize}}"  bindstop="{{camera.bindstop}}"></camera><!-- <view class='outline'> --><!-- <image class="outline" src='{{svgOutline}}'><image> --><view class="svg"><image class="svg_outline" src="{{svgOutline}}" type='aspectFill' style="width: 100%; height: 100%;" /></view><!-- <view class="svg_outline"><image class="svg_outline-inner" src="/images/取景框/outline.svg"/></view> --><!-- </view> --></view><view class='coverView'><view class='btnBar'><view class='littleBtn' bindtap="switchFlash"><wux-icon type="ios-flash" size="34" color="#fff"/></view></view><view class='btnBar'><view class='mainBtn' bindtap="takePhoto"><wux-icon type="ios-radio-button-off" size="70" color="#fff"/></view></view><view class='btnBar'><view class='littleBtn' bindtap="switchPosition"><wux-icon type="ios-sync" size="34" color="#fff"/></view></view></view><wux-toast id="wux-toast" />

takePhoto.js

data:{camera:{mode:"normal",resolution:"high",position:'back',flash:"off",framesize:"large",bindstop:"exceptionalStop",bindinitdone:"hideLoading",binderror:"grantFail"}
}//检测相机授权onShow: function () {wx.getSetting({success(res) {if (!res.authSetting['scope.camera']) {wx.authorize({scope: 'scope.camera',success () {console.log('相机授权成功')}})}}})},//切换闪光灯switchFlash(){if(this.data.camera.flash=='torch'){wx.showToast({title: '关闭闪光灯',icon:'none',})this.setData({['camera.flash']:'off',success:res=>{wx.hideToast({ })}})console.log(this.data.camera.flash)}else{wx.showToast({title: '打开闪光灯',icon:'none',})this.setData({['camera.flash']:'torch',success:res=>{wx.hideToast({ })}})console.log(this.data.camera.flash)}},//切换前后置摄像头switchPosition(){if(this.data.camera.position=='front'){wx.showToast({title: '切换后置相机',icon:'none',})this.setData({['camera.position']:'back',success:res=>{wx.hideToast({ })}})// console.log(this.data.camera.position)}else{wx.showToast({title: '切换前置相机',icon:'none',})this.setData({['camera.position']:'front',success:res=>{wx.hideToast({ })}})// console.log(this.data.camera.position)}},//相机授权失败处理grantFail:function(){wx.showModal({title: '提示',content: '请打开相机授权后继续',success (res) {if (res.confirm) {wx.openSetting({success (res) {console.log(res.authSetting)res.authSetting = {"scope.userInfo": true,"scope.camera": true}// that.onLoad();//重载takePhoto页面(测不可用)wx.redirectTo({ url: '/pages/takePhoto/takePhoto'});//重载takePhoto页面(测试可用)}})} else if (res.cancel) {wx.showToast({icon:'none',title:'授权失败'})wx.navigateBack({delta: 1,})}}})}

takePhoto.css

/*相机父容器 相对定位*/
.cameraContainer{box-sizing: border-box;width:100%; height:1260rpx;position: relative;/* align-items: center; */
}
/*加载中 绝对定位 index-0*/
.loading{position: absolute;width: 100%;top:240rpx;left:0;
}
/*相机画布 绝对定位 index-0*/
.camer{width: 100%;height: 100%;position: absolute;top: 0;left: 0;
}
/*取景框svg 绝对定位*/
.svg{width: 100%; height:100%;left: 0;top:0;display: flex;flex-direction: column;align-items: center;box-sizing: border-box;/* padding-top: 105rpx; */padding-left:115rpx;padding-right: 115rpx;/* padding-bottom: 115rpx; *//* position: fixed; */position: absolute;z-index: 10;
}/*底部按钮 固定定位*/
.coverView{width: 100%;position: fixed;bottom: 50rpx;display: flex;z-index: 20;
}
/*底部按钮父容器*/
.btnBar{width: 33.3%;display: flex;flex-direction: column;align-items: center;justify-content: center;
}
/*其他按钮*/
.littleBtn{width:70rpx;height: 70rpx;padding: 5rpx;display: flex;flex-direction: column;align-items: center;justify-content: center;border-radius: 50%;background-color: rgba(168, 114, 255, 0.685);
}
/*拍照按钮*/
.mainBtn{width:125rpx;height: 125rpx;display: flex;flex-direction: column;align-items: center;justify-content: center;border-radius: 50%;background-color: rgba(168, 114, 255, 0.685);
}

8.takePhoto页

使用到的局部数据

takePhoto.jsdata:{windowHeight:0,//屏幕高度windowWidth:0,//屏幕宽度screenMaxBright:0,//屏幕是否最大亮度showCamera:false,//渲染takePhoto页camera:{mode:"normal",//拍照模式resolution:"high",//拍照质量// position:getApp().globalData.camera.direction,flash:"off",//闪光灯framesize:"large",bindstop:"exceptionalStop",bindinitdone:"hideLoading",binderror:"grantFail"//相机授权失败时触发},
}

使用的全局数据

app.jsglobalData: {orignalBright:0.5,//屏幕初始亮度camera:{direction:'back'//前置\后置相机}}

takePhoto页主要逻辑

具体实现

1.页面加载,获取屏幕的宽高,将camera的父容器和camera本身的宽高设为屏幕宽高

onLoad(){this.getsize()//将camera的父容器和camera本身的宽高设为屏幕宽高
}
//getsize方法的实现
getsize(){let that=this;wx.getSystemInfo({success(res) {console.log("height="+ res.windowHeight)console.log("width="+res.windowWidth)that.setData({windowHeight:res.windowHeight,windowWidth:res.windowWidth})},})}

2.页面就绪,展示Modal“确认拍摄”,阻塞其他进程,等待用户确认

wx.showModal({cancelColor: '#FF4242',showCancel:true,content:'确认拍摄?',success:res=>{}})

success:res=>{ }

用户选择确认,showCamera置为true,渲染takePhoto.wxml页面,相机方向设为全局数据中保存的camera.direction相机方向

if(res.confirm){that.setData({showCamera:true,['camera.position']: getApp().globalData.camera.direction})}

用户选择取消,返回上一页

 if(res.cancel){wx.navigateBack({delta: 1,})}

4.切换前后置相机

1.在takePhoto页面onLoad()或者onReady()时,初始化this.data.camera.position为globalData.camera.direction
(恢复用户上次相机的(前置/后置)配置)
2.点击切换前后置事件
判断当前data.camera.position
如果是front前置则showToast"切换后置中..." 展示文字提示this.setData()'camera.position'设为'back' 后置然后hideToast
否则showToast"切换前置中..."setData 将 'camera.position'设为'front' 前置隐藏Toast 文字提示//切换前后置摄像头switchPosition(){if(this.data.camera.position=='front'){wx.showToast({title: '切换后置相机',icon:'none',})this.setData({['camera.position']:'back',success:res=>{wx.hideToast({ })}})console.log(this.data.camera.position)}else{wx.showToast({title: '切换前置相机',icon:'none',})this.setData({['camera.position']:'front',success:res=>{wx.hideToast({ })}})console.log(this.data.camera.position)}}3.takePhoto页面unLoad()卸载时,将当前镜头方向信息传给全局数据globalData.camera.direction(下次重新加载takePage页相机方向就会恢复成上次退出时的状态)
let app=getApp()app.globalData.camera.direction=that.data.camera.position//返回当前相机direction给全局对象

5.相机授权失败事件

展示对话框
选择确认则打开设置设置完成则重载takePhoto页wx.redirectTo({ url: '/pages/takePhoto/takePhoto'});(重载保证相机授权成功后显示出来)
选择取消则展示“授权失败”返回上一页wx.navigateBack({ delta: 1 })grantFail:function(){const pagePath=this.getCurrentPageFullPath;//返回当前页面全路径wx.showModal({title: '提示',content: '请打开相机授权后继续',success (res) {if (res.confirm) {wx.openSetting({success (res) {console.log(res.authSetting)res.authSetting = {"scope.userInfo": true,"scope.camera": true}wx.redirectTo({ url: '/pages/takePhoto/takePhoto'});//重载takePhoto页面}})// that.onReady();} else if (res.cancel) {wx.showToast({icon:'none',title:'授权失败'})wx.navigateBack({delta: 1,})}}})}
  1. 闪光灯/屏幕补光逻辑
1.在小程序onLaunch()启动时获取屏幕亮度,并保存到全局数据globalData.orignalBright中      wx.getScreenBrightness({success(res){getApp().globalData.orignalBright=res.value;}})2.takePhoto页面onLoad()加载时或者onReady()就绪时,初始化this.data.camera.position相机方向为全局数据globalData.camera.direction的值
that.setData({showCamera:true,['camera.position']: getApp().globalData.camera.direction})3.闪光灯点击事件
首先判断当前相机方向this.data.camera.position
如果是'back'后置如果闪光灯当前是'torch'状态则关闭闪光灯that.setData({['camera.flash']:'off'})否则打开闪光灯that.setData({['camera.flash']:'torch'})
否则是前置(前置情况下没有闪光灯,变为调节屏幕亮度)如果当前data.screenMaxBright==1是屏幕最大亮度则恢复手机原始亮度,将value设为全局数据getApp().globalData.orignalBright屏幕亮度的值,将data.screenMaxBright=0否则将屏幕调为最大亮度,将data.screenMaxBright=1wx.setScreenBrightness({value:1,success(){that.data.screenMaxBright=1}})4. takePhoto页面unLoad()卸载时,退出拍摄页,回归屏幕原来的亮度将当前屏幕亮度设为globalData.orignalBright原始亮度,data.screenMaxBright=0wx.setScreenBrightness({value: getApp().globalData.orignalBright,success(){that.data.screenMaxBright=0}})

7.拍摄事件

takePhoto() {console.log('拍照')const ctx = wx.createCameraContext()ctx.takePhoto({quality: 'high',success: (res) => {var tmpPath=res.tempImagePath;wx.navigateTo({url: '/pages/takePhoto/scanning/scanning',success: function(res) {// 通过eventChannel向被打开页面传送数据res.eventChannel.emit('acceptDataFromOpenerPage', { imagePath: tmpPath })}})}})}

9.Scanning页

扫描动画

wxml

<view class="container" class="scanning"><view class='logo'><image src="{{logoUrl}}" ></image></view><view class='processing'><progress percent="{{processValue}}" color="rgb(255, 100, 118)" active stroke-width="5" bindactiveend='onScanningFinish' /></view><text style="color: #ccc;">正在扫描生成结果...</text><view class='uploadPhoto'> <image src="{{facePhoto}}" mode="aspectFill"></image><canvas type="2d" id="canvas" style="width: {{canvasWidth}}px; height: {{canvasWidth}}px; position: absolute; border-width: 3px; border-style:dashed; border-color: pink;"></canvas></view>
</view>

css

.logo{width:300px;height:230px;display: flex;justify-content: center;align-items: center;margin-top: 20rpx;
}
.logo image{width:140rpx;height:140rpx;
}
.processing{width:300px;height:50px;margin-top: 10rpx;
}
.uploadPhoto{width:300px;height:300px;display: flex;justify-content: center;align-items: center;margin-top: 10rpx;position: relative;
}
.uploadPhoto image{width:30%;height:40%
}
.scanning{display: flex;flex-direction: column;align-items: center;
}

扫描动画

scanning.js(page({ }))

data

  data: {canvasWidth:150,//canvas画布宽度lineWidth:153,//线的宽度lineHeight:2,//线的高度},

onready()

onReady() {/***绘制动画*/this.position = {x: 1,//线的初始坐标y: this.data.canvasWidth+40,}// 通过 SelectorQuery 获取 Canvas 节点wx.createSelectorQuery().select('#canvas').fields({node: true,size: true,}).exec(this.init.bind(this))},
/*** 扫描动画*/init(res) {const width = res[0].widthconst height = res[0].heightconst canvas = res[0].nodeconst ctx = canvas.getContext('2d')const dpr = wx.getSystemInfoSync().pixelRatiocanvas.width = width * dprcanvas.height = height * dprctx.scale(dpr, dpr)const renderLoop = () => {this.render(canvas, ctx)canvas.requestAnimationFrame(renderLoop)}canvas.requestAnimationFrame(renderLoop)const img = canvas.createImage()img.onload = () => {this._img = img}img.src = '/images/检测界面/scan.png'},render(canvas, ctx) {ctx.clearRect(0, 0, this.data.canvasWidth+5, this.data.canvasWidth+30)this.drawCar(ctx)},drawCar(ctx) {if (!this._img) returnif (this.position.y < 0) {this.position.y = this.data.lineWidth+40}ctx.drawImage(this._img, this.position.x, this.position.y-=1, this.data.lineWidth, this.data.lineHeight)ctx.restore()},
  • 页面跳转并传送数据

    拍照页拍照并发送图片临时地址

    takePhoto.js

      console.log('拍照')const ctx = wx.createCameraContext()ctx.takePhoto({quality: 'high',success: (res) => {var tmpPath=res.tempImagePath;wx.navigateTo({url: '/pages/takePhoto/scanning/scanning',success: function(res) {// 通过eventChannel向被打开页面传送数据res.eventChannel.emit('acceptDataFromOpenerPage', { imagePath: tmpPath })}})}})

​ scanning.js

​ 接收页onLoad并接收图片临时地址

​ onLoad()中

   //接收传送的数据const eventChannel= this.getOpenerEventChannel()eventChannel.on('acceptDataFromOpenerPage', function (data) {console.log('图片路径:'+data.imagePath)that.setData({facePhotoUrl:data.imagePath})/*****图片转base64*****/})

​ eventChannel.on({})中

  • 图片转base64
      //图片转base64const fs=wx.getFileSystemManager()// 同步接口try {const res = fs.readFileSync(that.data.facePhotoUrl, 'base64', 0)console.log("scanning页成功获取到base64!")that.data.facePhotoBase64=res;} catch(e) {console('scanning页base64获取失败!')console.error(e)}

引用的外部js文件

const util=require("../../../utils/util");

util.js

数据库的操作

①查询

查询UserPrivateInfo的skinAnalyzeInspect//openId查询数据库
const  queryDbByOpenId = async (collectionName,openid) =>{return new Promise((resolve)=>db.collection(collectionName).where({_openid:_.eq(openid)}).get({success:res=>{resolve(res.data)}})).then((val)=>{return val})//.data//{success:res=>{console.log(res.data[0])}}
}

人脸检测与皮肤分析主要逻辑

app.js

    //初始化云开发wx.cloud.init({env: '',//cloud1-6g4fn1e1fc689993traceUser: true,})

scanning.js

一、余额查询与判断

全局数据:
freeTimes (免费次数)
remainingTimes (剩余购买的次数)
局部数据:事件逻辑
appLunch()时
查询免费次数并保存到globalData.freeTimes
查询剩余付费次数并保存到globalData.userInfo.remainingTimes
查询单价(元/次)并保存到globalData.chargelet chargeConfig=await util.queryDbById('Settings','charge_manage')// console.log("价格:"+chargeConfig.cost_each_time+"元/次")this.globalData.charge=chargeConfig.cost_each_time;// console.log('免费次数:'+chargeConfig.free_times)this.globalData.freeTimes=chargeConfig.free_times;统计用户检测次数amount(已保存的用户分析记录数)用户检测皮肤前进行判断
amount小于等于(免费次数+剩余付费次数),直接进行检测
大于(免费次数+剩余付费次数)询问支付(查询次数不足,是否前往付费?)
选择“否”,返回上一页
选择“是”,跳转到支付界面
选择支付套餐(次数*globalData.charge),选择支付数量
支付完成之后
更新数据库,更新全局数据globalData.userInfo.remainingTimes,更新页面数据
在用户个人主页显示可用次数
购买的总数+免费总数-已用总数

二、人脸检测API鉴权与请求

此处仅列出思路

1.查询settings中自己保存的的api相关请求token信息

util.js
// util.queryDbAll('Settings')
//数据库中获取到自己请求api的一些配置信息

2.api鉴权并请求人脸识别

getFaceResult()

3.人脸检测并反馈结果

faceDetect()

三、皮肤分析结果与显示

skinAnalyze()

数据库的查询

①费用相关

app.js

//初始化云开发
wx.cloud.init({env: '你的云开发环境id',//cloud1-6g4fn1e1fc689993traceUser: true,
})

util.js

const db=wx.cloud.database();//id查询数据库
const  queryDbById = async (collectionName,id) =>{ //async内有同步方法return (await db.collection(collectionName).doc(id).get()).data //await同步
}
module.exports = {queryDbById
}

引用页.js

const util=require('../../utils/util')//引用util.jsonReady:async function(){//同步let chargeConfig=await util.queryDbById('Settings','charge_manage')//同步查询,必须要await,否侧console.log(chargeConfig)为F{}空结果console.log(chargeConfig)
}

②API相关

util.js

//id更新数据库
const updateDbById = async (collectionName,id,keyvalue) =>{await db.collection(collectionName).doc(id).update({data:keyvalue})
}
  /*** 获得百度Access_Token* new promise 实现同步返回新的token*/const getAPIgrant = async function (key) {let that = this;var client_id = key.API_Key; //client_id: 必须参数,应用的API Key;var client_secret = key.Secret_Key; //client_secret: 必须参数,应用的Secret Key;var Access_Tokenreturn new Promise((resolve) => {wx.request({url: 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=' + client_id + '&client_secret=' + client_secret + '&',method: 'GET',dataType: 'json',success: res => {console.log('人脸检测API Access Token 请求成功!')const newTime = Math.round(Date.parse(res.header.Date)/1000) //Math.round(Date.parse(new Date())/1000);获取13位当前时间戳,/1000再向上取整,精确到秒//本次更新时间,保存到数据库that.updateDbById('Settings', 'bd_face_detect_token', {['lastUpdateTime']: newTime,['Access_Token']: res.data.access_token,['effictiveTime']: res.data.expires_in})Access_Token = res.data.access_token;console.log('新授权token:' + Access_Token);resolve(Access_Token)},fail: res => {console.log("人脸检测API Access Token请求失败:");console.log(res)}})}).then((value) => {getApp().globalData.API.Access_Token = value// console.log(value)return value})}
  /*** 判断是否允许直接进行检测*/const isDetectAllow = async ()=>{var num={}; var freeTimes=0; var paidTimes=0;return new Promise((resolve)=>{num = db.collection("FaceInfo").count();//FaceInfo集合(权限为仅创建者可读写)中当前用户的测肤记录数,也是最后一条测肤记录的编号resolve(num)}).then((value)=>{freeTimes = getApp().globalData.freeTimes//免费次数paidTimes = getApp().globalData.userInfo.reaminingTimes//购买的次数// console.log(value.total)return ( value.total <= (freeTimes+paidTimes) )? true : false;})

app.js

    //查询API相关信息var key=await util.queryDbById('Settings','bd_face_detect_token')// console.dir( util.queryDbAll('Settings'))getApp().globalData.API.Access_Token=key.Access_Tokenvar timestamp = Math.round(Date.parse(new Date())/1000);//获取13位当前时间戳,/1000再向上取整,精确到秒console.log("当前时间戳:"+timestamp)var gap=timestamp-key.lastUpdateTimeif(gap>=key.effictiveTime){//token超过有效期//重新获取access_token,更新全局数据global.data.API.Access_TokengetApp().globalData.API.Access_Token = (await util.getAPIgrant(key))// console.log(getApp().globalData.API.Access_Token)}

③用户皮肤分析余额相关

applunch()时 查询当前用户余额及检测次数并存至全局globalData.remaning_times
查询当前用户皮肤分析次数并存至global.skinAnalyzeAmount
如果查询到的结果为空,
则发起注册登录
思路一:
1.用户注册时,remaining_times自动置为free_times
2.用户分析皮肤时,判断remaining_times>0,如果True继续检测如果False,发起支付,支付(charge*数量)完成后,remainning_times+=数量,更新数据库,更新全局变量
3.分析完成后,将remaining_times-1,并更新数据库思路二:
1.用户注册时,remaining_times置为0
2.用户分析皮肤时,判断分析次数<=remaining_times+free_times,如果True继续检测如果False,发起支付,支付(charge*数量)后,remainning_times+=数量,更新数据库,更新全局变量
3.分析完成后,如果amount<=free_times,则免费次数剩余=free_times-amount,付费余额剩余remainning_times,否则免费次数=0,
免费次数剩余:次;付费次数剩余:次1.在app.js的onlunch()查询用户数据
如果找到,则更新globalData.userInfo.remainingTimes、globalData.userInfo.skinAnalyzeInspect
更新globalData.userInfo.detectAllow
如果没有找到,则将globalData.userInfo置为undefined//用户数据管理// let userPriInfo=await util.queryDbById('UserPrivateInfo','bb4c25156261752300d2ebbd49f05d7a')// let userPriInfo=await util.queryDbByOpenId('UserPrivateInfo','oid')//通过openid查询var userPriInfo =await util.queryDbAll('UserPrivateInfo')userPriInfo=userPriInfo[0];if(userPriInfo){console.log("剩余可用次数:"+userPriInfo.remaining_times)this.globalData.userInfo.reaminingTimes=userPriInfo.remaining_timesconsole.log("皮肤分析指标:"+userPriInfo.skinAnalyzeInspect)this.globalData.userInfo.skinAnalyzeInspect=userPriInfo.skinAnalyzeInspectthis.globalData.userInfo.detectAllow=await util.isDetectAllow()console.log("允许直接检测:"this.globalData.userInfo.detectAllow)}else{//用户还未使用过小程序,需要初始化账户this.globalData.userInfo=undefinedconsole.log("用户"+this.globalData.userInfo+"\n新用户需注册")}2.在scanning.js中
判断globalData.userInfo是否undefined
若是,则注册并初始化用户数据
若否则,直接使用globalData.userInfo的数据,进行下一步操作
3.判断globalData.userInfo.detectAllow
若为T,直接请求API
若为F,则询问并支付
(返回看思路二)

皮肤检测事件逻辑

皮肤分析结果页展示出来后,remaining_times–

否则不更新remaining_times

10.云函数的使用

在 project.config.json 文件中添加以下字段

//"cloudfunctionRoot": "cloudbase/"

新建云函数目录cloudfunctions

cloudfunctions下新建node.js云函数getopenid

index.js中编写云函数

// 云函数入口文件
const cloud = require('wx-server-sdk')cloud.init()// 云函数入口函数
exports.main = async (event, context) => {const wxContext = cloud.getWXContext()return {// event,openid: wxContext.OPENID,// appid: wxContext.APPID,// unionid: wxContext.UNIONID,}
}

config.json中配置触发器(可选)

{"permissions": {"openapi": []},"triggers": [{"name": "tomylove","type": "timer","config": "0  0  2  1  *  *  *"}]
}

最后右键保存部署到云环境

其它内容**详见微信官方云开发文档

11.Home页

使用模板template

实现多个页面引入topBar

模板页

/template/topBar.wxml

<template name="topBar"><view style="display: flex; flex-direction:column; align-items: center; font-size: smaller; padding:10rpx; position: relative; width:100%" class='wux-energized--bg wux-light'><view style='position: absolute;top:0rpx;left:0rpx;display: flex;align-items: center;height:100%; width:35%; overflow: scroll;'><wux-avatar wx:if="{{avatarUrl}}" bind:tap='{{openSettings}}' src='{{avatarUrl}}' style="margin: 10rpx;"></wux-avatar><wux-icon wx:else="{{avatarUrl}}" bindtap="{{initAccount}}" style="margin: 10rpx;" type="ios-contact" size="40" color="#f0f0f0" />{{nickName}}</view><view><wux-avatar style="margin: 10rpx 10rpx;" size='large' src='https://636c-cloud1-6g4fn1e1fc689993-1309475441.tcb.qcloud.la/photos/%E7%99%BD%E8%BE%B9%E5%9C%86%E8%A7%92logo.png?sign=e773d7f0b3b6337c6b572ce0a80dd8ba&t=1651971170'></wux-avatar>Magic Mirror</view></view>
</template>

引用页

以home页为例

home.wxml

<import src="/template/topBar" />
<template  is="topBar" data="{{...userInfo}}" ></template>

传入数据和绑定事件

home.js

  data: {userInfo:{avatarUrl:'',//https://636c-cloud1-6g4fn1e1fc689993-1309475441.tcb.qcloud.la/photos/%E7%99%BD%E8%BE%B9%E5%9C%86%E8%A7%92logo.png?sign=e773d7f0b3b6337c6b572ce0a80dd8ba&t=1651971170nickName:'',openSettings:'openPerson',initAccount:'initAccount'}},
page({openPerson:()=>{wx.showModal({title:'hello',})},initAccount:()=>{wx.showToast({title: 'hello',})}
})

魔镜用户默认头像base64 icon:

data:image/svg+xml;base64,PHN2ZyB0PSIxNjM2NjEzNDY0Mjg4IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjI0OTcwIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCI+PHBhdGggZD0iTTQyOS4zIDU0NC4xYy00MS4zLTI0LjEtNjUuNy01NS4xLTg0LjItMTA3bC0xLjQtMy41Yy0zLjUtNi45LTEwLjUtMTEuMS0xOC41LTExLjEtMi4yIDAuMS00LjIgMC41LTYuMSAxLjEtOS4xIDMuMi0xNC42IDEyLjYtMTMgMjIuN2wxLjMgNC40YzIxLjIgNTkuNyA1MS4xIDk3LjUgOTkuOSAxMjYuNCAwLjEgMCA3LjIgNC44IDExIDQuOGwyLjggMC4xYzEuOS0wLjEgMy45LTAuNCA1LjgtMS4xIDEwLjQtMy43IDE1LjgtMTUuMiAxMi4xLTI1LjctMS42LTUuMS01LjMtOS4yLTkuNy0xMS4xek0zMTcuOSA0MTIuOWM1LjQtMS44IDkuOC01LjUgMTIuNS0xMC43IDIuNi01LjEgMy0xMC45IDEuMy0xNi40LTIuOS04LjktMTEuMS0xNC44LTIwLjQtMTQuOC0yLjIgMC00LjQgMC40LTYuNyAxLjEtMTEuMiAzLjctMTcuNCAxNS44LTEzLjcgMjcgMi45IDguOSAxMS4xIDE0LjggMjAuNCAxNC44IDIuMiAwLjEgNC40LTAuMyA2LjYtMXoiIGZpbGw9IiNGRjY0NzYiIHAtaWQ9IjI0OTcxIiBkYXRhLXNwbS1hbmNob3ItaWQ9ImEzMTN4Ljc3ODEwNjkuMC5pMzgiIGNsYXNzPSJzZWxlY3RlZCI+PC9wYXRoPjxwYXRoIGQ9Ik02ODIuOCA2NjkuM2M5My45LTU3LjcgMTU2LjgtMTYwLjcgMTU2LjgtMjc4LjMgMC0xODAuNC0xNDcuNi0zMjcuMi0zMjguOS0zMjcuMlMxODEuOCAyMTAuNiAxODEuOCAzOTFjMCAxMTcuNiA2Mi45IDIyMC42IDE1Ni44IDI3OC4zbC0xMzQgMTcwLjFjLTkuMiAxNi41LTEwLjYgMzUtMTAuNiA0NC42IDAgNTAuOCAyNiA3NS43IDY2LjIgNzUuN2g1MDEuMWM0MC4yIDAgNjYuMi0yNC45IDY2LjItNzUuNyAwLTE3LjgtMy4yLTMxLjQtMTAuNC00NC4zTDY4Mi44IDY2OS4zek0yNDIuNyAzOTFjMC0xNDYuOCAxMjAuMi0yNjYuMyAyNjgtMjY2LjNzMjY4IDExOS40IDI2OCAyNjYuMy0xMjAuMiAyNjYuMy0yNjggMjY2LjMtMjY4LTExOS41LTI2OC0yNjYuM3ogbTQ5NC4xIDUwNi4xSDI4NC43Yy0xMy41IDAtMjIuNS0xMi4xLTIyLjUtMjQuMiAwLTQuOCAwLTcuMyAyLjMtMTIuMWwxMjcuNC0xNjUuMWMzNi45IDE0LjMgNzYuOSAyMi41IDExOC45IDIyLjVzODEuOS04LjEgMTE4LjktMjIuNUw3NTcgODYwLjhjMi4yIDIuNCAyLjIgNy4zIDIuMiAxMi4xIDAuMSAxMi4xLTguOSAyNC4yLTIyLjQgMjQuMnoiIGZpbGw9IiNmZmZmZmYiIHAtaWQ9IjI0OTcyIj48L3BhdGg+PHBhdGggZD0iTTYyOS42IDY5NS43Yy0zNi45IDE0LjMtNzYuOSAyMi41LTExOC45IDIyLjUtNDEuOSAwLTgxLjktOC4xLTExOC45LTIyLjVMMjY0LjQgODYwLjhjLTIuMyA0LjgtMi4zIDcuMy0yLjMgMTIuMSAwIDEyLjEgOSAyNC4yIDIyLjUgMjQuMmg0NTIuMWMxMy41IDAgMjIuNS0xMi4xIDIyLjUtMjQuMiAwLTQuOCAwLTkuNy0yLjItMTIuMUw2MjkuNiA2OTUuN3oiIGZpbGw9IiNmZmZmZmYiIHAtaWQ9IjI0OTczIj48L3BhdGg+PC9zdmc+

使用fabButton浮动按钮

更改

/components/lib/fab-button

.wux-fab-button{position:absolute;z-index:1020}

app.json

 "wux-fab-button": "./components/lib/fab-button/index"

wxml

<view style='height:100rpx;width:100%;position:fixed;bottom:135rpx;left:0'><view style="height:100%; width:100%; position:relative;"><wux-fab-buttonposition="center"theme="energized"hideShadow='true'scale='1.1'backdrop='true'direction="circle"sAngle="110"eAngle="250"buttons="{{ buttons }}"bind:change="onChange"bind:click="onClick"/></view>
</view>

js

data:

    buttons:[{// openType: '',// hoverClass:'wux-light wux-assertive--bg',label: 'Camera',icon:'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJpb25pY29uIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+PHRpdGxlPkNhbWVyYTwvdGl0bGU+PHBhdGggZD0iTTM1MC41NCAxNDguNjhsLTI2LjYyLTQyLjA2QzMxOC4zMSAxMDAuMDggMzEwLjYyIDk2IDMwMiA5NmgtOTJjLTguNjIgMC0xNi4zMSA0LjA4LTIxLjkyIDEwLjYybC0yNi42MiA0Mi4wNkMxNTUuODUgMTU1LjIzIDE0OC42MiAxNjAgMTQwIDE2MEg4MGEzMiAzMiAwIDAwLTMyIDMydjE5MmEzMiAzMiAwIDAwMzIgMzJoMzUyYTMyIDMyIDAgMDAzMi0zMlYxOTJhMzIgMzIgMCAwMC0zMi0zMmgtNTljLTguNjUgMC0xNi44NS00Ljc3LTIyLjQ2LTExLjMyeiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzMiIvPjxjaXJjbGUgY3g9IjI1NiIgY3k9IjI3MiIgcj0iODAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMzIiLz48cGF0aCBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjMyIiBkPSJNMTI0IDE1OHYtMjJoLTI0djIyIi8+PC9zdmc+',hideShadow:true,},{// openType: '',// hoverClass:'wux-light wux-assertive--bg',label: 'Image',icon:'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJpb25pY29uIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+PHRpdGxlPkltYWdlPC90aXRsZT48cmVjdCB4PSI0OCIgeT0iODAiIHdpZHRoPSI0MTYiIGhlaWdodD0iMzUyIiByeD0iNDgiIHJ5PSI0OCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMzIiLz48Y2lyY2xlIGN4PSIzMzYiIGN5PSIxNzYiIHI9IjMyIiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2Utd2lkdGg9IjMyIi8+PHBhdGggZD0iTTMwNCAzMzUuNzlsLTkwLjY2LTkwLjQ5YTMyIDMyIDAgMDAtNDMuODctMS4zTDQ4IDM1Mk0yMjQgNDMybDEyMy4zNC0xMjMuMzRhMzIgMzIgMCAwMTQzLjExLTJMNDY0IDM2OCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzMiIvPjwvc3ZnPg==',hideShadow:true},{// openType: '',// hoverClass:'wux-light wux-assertive--bg',label: 'Create',icon:'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJpb25pY29uIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+PHRpdGxlPkJydXNoPC90aXRsZT48cGF0aCBkPSJNNDUyLjM3IDU5LjYzaDBhNDAuNDkgNDAuNDkgMCAwMC01Ny4yNiAwTDE4NCAyOTQuNzRjMjMuMDggNC43IDQ2LjEyIDI3LjI5IDQ5LjI2IDQ5LjI2bDIxOS4xMS0yMjcuMTFhNDAuNDkgNDAuNDkgMCAwMDAtNTcuMjZ6TTEzOCAzMzZjLTI5Ljg4IDAtNTQgMjQuNS01NCA1NC44NiAwIDIzLjk1LTIwLjg4IDM2LjU3LTM2IDM2LjU3QzY0LjU2IDQ0OS43NCA5Mi44MiA0NjQgMTIwIDQ2NGMzOS43OCAwIDcyLTMyLjczIDcyLTczLjE0IDAtMzAuMzYtMjQuMTItNTQuODYtNTQtNTQuODZ6IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjMyIi8+PC9zdmc+',hideShadow:true}]

onClick事件

  onClick(e) {console.log('onClick', e.detail)switch(e.detail.index) {// wx.switchTab({// url: '/pages/index/index',// })case 0:this.camera()break;case 1:this.image()break;case 2:this.create()break;}},image:function(){wx.showModal({title:'image',cancelColor: '#000',})},camera:function(){wx.showModal({title:'camera',cancelColor: '#000',})},create:function(){wx.showModal({title:'create',cancelColor: '#000',})}

maddleBar

maddleBar.wxml

<template name="middleBar_logo"><view style="display: flex; flex-direction:column; align-items: center; font-size: smaller; padding:10rpx; position: relative; width:100%; font-size: larger; font-weight: bolder;" class='wux-royal '><view><wux-avatar style="margin: 10rpx 10rpx;" size='large' src='https://636c-cloud1-6g4fn1e1fc689993-1309475441.tcb.qcloud.la/photos/%E7%99%BD%E8%BE%B9%E5%9C%86%E8%A7%92logo.png?sign=e773d7f0b3b6337c6b572ce0a80dd8ba&t=1651971170'></wux-avatar>Magic Mirror</view></view>
</template>

person.wxml

<import src="/template/middleBar" />

卡片1

app.json

    "wux-card": "./components/lib/card/index","wux-wing-blank": "./components/lib/wing-blank/index","wux-grids": "./components/lib/grids/index","wux-grid": "./components/lib/grid/index"

引入页.js

   avatar:'/images/logo/logo.png',nickName:'孤勇者',free_times:0,remain_times:0,

引入页.wxml

    <wux-wing-blank size="default"><wux-cardtitle="{{nickName}}"extra="我的账户"thumb="{{avatar}}"thumb-style="border-radius: 50%;"><view slot="body"><wux-grids col="2"><!-- <wux-grid thumb="" label="follow" /><wux-grid thumb="" label="follwer" /><wux-grid thumb="" label="enshrine" /><wux-grid thumb="" label="shop" /> --><wux-grid thumb="" label="剩余免费{{free_times}}次" /><wux-grid thumb="" label="剩余付费{{remain_times}}次" /></wux-grids></view><view slot="footer"></view></wux-card></wux-wing-blank>

卡片2

<wux-wing-blank size="default"><wux-card extra=""><view slot="body"><!-- <wux-cell-group title="测肤设置"> --><wux-popup-select value="{{ value2 }}" options="{{ options2 }}" multiple data-index="2" bind:confirm="onConfirm" bind:valueChange="onValueChange"><wux-cell thumb="{{inspectOptions}}" title="测肤指标" label='肤色、光滑度、痣、皱纹、黑眼圈···' is-link extra="自定义"></wux-cell></wux-popup-select><wux-cell thumb="{{analyzeHistory}}" is-link extra="测肤记录"></wux-cell><!-- </wux-cell-group> --></view></wux-card>
</wux-wing-blank>

.js

data

    inspectOptions:'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJpb25pY29uIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+PHRpdGxlPk9wdGlvbnM8L3RpdGxlPjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMzIiIGQ9Ik0zNjggMTI4aDgwTTY0IDEyOGgyNDBNMzY4IDM4NGg4ME02NCAzODRoMjQwTTIwOCAyNTZoMjQwTTY0IDI1Nmg4MCIvPjxjaXJjbGUgY3g9IjMzNiIgY3k9IjEyOCIgcj0iMzIiIGZpbGw9Im5vbmUiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMzIiLz48Y2lyY2xlIGN4PSIxNzYiIGN5PSIyNTYiIHI9IjMyIiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjMyIi8+PGNpcmNsZSBjeD0iMzM2IiBjeT0iMzg0IiByPSIzMiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzMiIvPjwvc3ZnPg==',analyzeHistory:'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJpb25pY29uIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+PHRpdGxlPkFuYWx5dGljczwvdGl0bGU+PHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzMiIgZD0iTTM0NCAyODBsODgtODhNMjMyIDIxNmw2NCA2NE04MCAzMjBsMTA0LTEwNCIvPjxjaXJjbGUgY3g9IjQ1NiIgY3k9IjE2OCIgcj0iMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMzIiLz48Y2lyY2xlIGN4PSIzMjAiIGN5PSIzMDQiIHI9IjI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjMyIi8+PGNpcmNsZSBjeD0iMjA4IiBjeT0iMTkyIiByPSIyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzMiIvPjxjaXJjbGUgY3g9IjU2IiBjeT0iMzQ0IiByPSIyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzMiIvPjwvc3ZnPg==',value2:['001','002','003','004','005','006'],options2: [{title: '肤色',value: '001',},{title: ' 光滑度',value: '002',},{title: '痘、斑、痣',value: '003',},{title: '皱纹',value: '004',},{title: '黑眼圈、眼袋',value: '005',},{title: '毛孔、黑头',value: '006',}],

app.json

    "wux-popup-select": "./components/lib/popup-select/index","wux-cell-group": "./components/lib/cell-group/index","wux-cell": "./components/lib/cell/index"

卡片3

xml

<wux-wing-blank size="default"><wux-card title="" extra="分享社区" thumb=""><view slot="body"><wux-cell thumb="{{articleShare}}" is-link extra="我的贴子"></wux-cell><wux-cell thumb="{{articleEnshrine}}" is-link extra="我的收藏"></wux-cell></view><!-- <view slot="footer">尾部内容</view> --></wux-card></wux-wing-blank>

data

articleShare:'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJpb25pY29uIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+PHRpdGxlPkRvY3VtZW50IFRleHQ8L3RpdGxlPjxwYXRoIGQ9Ik00MTYgMjIxLjI1VjQxNmE0OCA0OCAwIDAxLTQ4IDQ4SDE0NGE0OCA0OCAwIDAxLTQ4LTQ4Vjk2YTQ4IDQ4IDAgMDE0OC00OGg5OC43NWEzMiAzMiAwIDAxMjIuNjIgOS4zN2wxNDEuMjYgMTQxLjI2YTMyIDMyIDAgMDE5LjM3IDIyLjYyeiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMzIiLz48cGF0aCBkPSJNMjU2IDU2djEyMGEzMiAzMiAwIDAwMzIgMzJoMTIwTTE3NiAyODhoMTYwTTE3NiAzNjhoMTYwIiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjMyIi8+PC9zdmc+',articleEnshrine:'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJpb25pY29uIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+PHRpdGxlPlN0YXI8L3RpdGxlPjxwYXRoIGQ9Ik00ODAgMjA4SDMwOEwyNTYgNDhsLTUyIDE2MEgzMmwxNDAgOTYtNTQgMTYwIDEzOC0xMDAgMTM4IDEwMC01NC0xNjB6IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzMiIvPjwvc3ZnPg==',

limit查询加载首页推荐列表

wx:for 循环来加载推荐列表

xml


<wux-wing-blank><!-- 文章推荐列表 --><block wx:for="{{articleList}}" wx:for-item="items"><view style="height:310rpx; margin-top: 10rpx; margin-bottom: 15rpx; border-radius: 12rpx; overflow: hidden; box-shadow: 0rpx 0rpx 30rpx rgb(216, 216, 216);"  bindtap="navToArticle" data-articleid='{{index}}'><!--卡片--><view style="height: 75%;" class="wux-light--bg"><!--配图 标题 描述--><view style="width: 40%;height:100%; ;float:left;display:flex;align-items: center;/*垂直居中*/justify-content: center;"><!-- <view style="width:80%;height:80%;background-color: chartreuse; "></view>配图 --><image style="width:85%;height:82%; ; border-radius: 15rpx" mode="aspectFill" src="{{items.layoutPicture?items.layoutPicture:'/images/logo/logo.png'}}"></image></view><view style="width: 60%;height:100%; ;float:right;"><!--标题 文字 display: flex;align-items: center;--><view style="height:50%; ; font-weight: bolder; margin-left: 10rpx; margin-right: 10rpx; display:flex; align-items: center;/*垂直居中*/"><!--标题--><view style="margin-top:10rpx;" class="wux-ellipsis--l2">{{items.title?items.title:"标题加载中"}}</view></view><view style="height:50%; ; margin-left: 10rpx; margin-right: 10rpx; display:flex; align-items: center;/*垂直居中*/ color:rgb(128, 132, 143)"><!--简介   background-clip: content-box; padding: 0.6em; padding-top: 0; --><view style="margin-bottom: 10rpx;" class="wux-ellipsis--l2">{{items.subTitle?items.subTitle:"简介加载中"}}</view></view></view></view><view style="height: 25%; background-color: rgb(255, 240, 243);" class="wux-energized--border" ><!--头像 点赞 评论 收藏border: 1rpx solid; border-left: 0; border-bottom: 0; border-right: 0; --><view style="width: 40%;height:100%; float:left"><!--头像昵称--><view style="width: 30%;height:100%; ;float:left;display:flex;align-items: center;/*垂直居中*/justify-content: center;"><wux-avatar src="{{items.userAvatar?items.userAvatar:'/images/logo/logo.png'}}"></wux-avatar><!--头像--></view><view style="width: 70%;height:100%; ;float:left; display: flex; align-items: center; font-weight: bolder;" class="wux-royal"><view class="wux-ellipsis">{{items.nickName?items.nickName:""}}</view></view><!--昵称  white-space: nowrap; text-overflow: ellipsis; overflow:hidden; -o-text-overflow:ellipsis;--></view><view style="width: 60%;height:100%; ; float:right; font-size: smaller; color:rgb(128, 132, 143)"><!--点赞 评论 转发--><view style="width: 34%;height:100%; ;float:left; display:flex;align-items: center;/*垂直居中*/ justify-content: center;/*水平居中*/"><!--点赞--><wux-icon type="md-thumbs-up" size="30"  class="wux-royal wux-margin-right--5" /><text>{{items.like?items.like:0}}</text></view><view style="width: 33%;height:100%; ;float:left; display:flex;align-items: center;/*垂直居中*/ justify-content: center;/*水平居中*/"><!--评论--><wux-icon type="md-text" size="30" class="wux-royal wux-margin-right--5 wux-margin-top--5" /><text>{{items.discuss?items.discuss:0}}</text></view><view style="width: 33%;height:100%; ;float:left; display:flex;align-items: center;/*垂直居中*/ justify-content: center;/*水平居中*/"><!--转发--><wux-icon type="md-share-alt" size="30" class="wux-royal wux-margin-right--5" /><text>{{items.share?items.share:0}}</text></view></view></view></view></block>
</wux-wing-blank>

util.js

//分页查询
const queryDBbyLimt=async function(collectionName,skipNum,limitNum,condition){return (await db.collection(collectionName).where(condition)// .field({//   name: true,//   price: true,// }).orderBy('like', 'desc').skip(skipNum).limit(limitNum).get()).data
}

home.js

const util=require('../../utils/util')
let app=getApp();
let begin=0;
const step=4;

data

    articleList:[]

onload()

    //加载四篇文章let aList=await util.queryDBbyLimt('Articles',begin,step,{visibility:true});// console.log(articleList);this.setData({articleList:aList})getApp().globalData.showArticleList=aList;//赋给全局数组console.log(this.data.articleList)

点击跳转事件

  navToArticle(e){// console.log(e.target);//点击的view// console.log(e.currentTarget);//有绑定事件的viewvar url='/pages/...';var articleid=e.currentTarget.dataset['articleid']// wx.showToast({//   icon:'none',//   title: data.detail.dataset,// })// wx.navigateTo({//   url: 'url?id=${articleid}',// })console.log(`${url}?id=${articleid}`)}

打开新页面展示文章

  navToArticle(e){// console.log(e.target);//点击的view// console.log(e.currentTarget);//有绑定事件的viewvar pageUrl='/pages/article/readArticle';var articleid=e.currentTarget.dataset['articleid']wx.navigateTo({url: `${pageUrl}?id=${articleid}`,})}

下拉触底加载更多

onReachBottom()

  /*** 页面上拉触底事件的处理函数*/onReachBottom:async function () {//跳跃加载wx.showToast({icon:'loading',title: 'loading',})begin+=step;var addList=await util.queryDBbyLimt('Articles',begin,step,{visibility:true});console.log(addList)wx.hideToast({})if(addList==false){wx.showToast({icon:'none',title: '暂无更多内容',duration:1500})}else{   // this.data.articleList.concat(addList);//追加数组getApp().globalData.showArticleList=getApp().globalData.showArticleList.concat(addList)this.setData({articleList:this.data.articleList.concat(addList)})}},

浏览文章页

xml

<view class="container" style="height:{{editorHeight}}px;"><editorid="editor"class="ql-container" placeholder="{{'正在加载...'}}" bindready="onEditorReady"read-only="{{true}}"></editor>
</view>

js

  /*** 生命周期函数--监听页面加载*/onLoad(options) {let app=getApp();// console.log(app.globalData.showArticleList[options.id]);//第i篇文章this.setData({article:app.globalData.showArticleList[options.id]})const { windowHeight, platform } = wx.getSystemInfoSync()let editorHeight = windowHeight //占满屏幕this.setData({ editorHeight })},
onEditorReady() {const that = thislet app=getApp()wx.createSelectorQuery().select('#editor').context(function (res) {that.editorCtx = res.context //必备初始化语句let article=that.data.article;if(article){var title=article.title;// that.editorCtx.insertText({html:`<h2>${title}</h2>`})that.editorCtx.setContents({// html:article.html,// text:article.text,delta:article.content,fail:res=>{console.log(res)}})}}).exec()},

wxss

@import "/components/editor/assets/iconfont.wxss";
@import "/components/common/lib/weui.wxss";
.container {position: absolute; top: 0; left: 0; width: 100%;
}
.ql-container {box-sizing: border-box;width: 100%;height: 100%;font-size: 16px;line-height: 1.5;overflow: auto;padding: 10px 10px 20px 10px;border: 1px solid #ECECEC;
}

半屏窗

template xml

<template name="middleBar_logo2"><view style="display: flex; flex-direction:column; align-items: center; font-size: smaller; position: relative; width:100%; font-weight: bolder;" class='wux-light '><view><wux-avatar style="margin: 5rpx 5rpx;" size='large' src='https://636c-cloud1-6g4fn1e1fc689993-1309475441.tcb.qcloud.la/photos/%E7%99%BD%E8%BE%B9%E5%9C%86%E8%A7%92logo.png?sign=e773d7f0b3b6337c6b572ce0a80dd8ba&t=1651971170'></wux-avatar>Magic Mirror</view></view>
</template>

wxss

.recordBtn{border-radius: 18rpx;/* border: 1rpx solid rgb(255, 100, 118);box-shadow: 1rpx 1rpx 10rpx rgb(255, 100, 118);color: rgb(255, 100, 118); */margin-top: 18rpx;width: 48%;font-size: 28rpx;padding-top: 18rpx;padding-bottom: 18rpx;background-color: #fff;text-align: center;font-weight: bolder;
}
.settingBtn{width: 100%;border-radius: 18rpx;margin-top: 18rpx;padding-top: 15rpx;padding-bottom: 15rpx;background-color: #fff;display: flex;justify-content: space-between;align-items: center;color:rgb(155, 155, 155);
}

app.json

  "useExtendedLib": {"weui": true},

usingcomponent

"mp-half-screen-dialog": "weui-miniprogram/half-screen-dialog/half-screen-dialog"

12.Shop购物页

json

    "wux-row": "./components/lib/row/index","wux-col": "./components/lib/col/index"

xml

<wux-row><wux-col span="6" wx:for="{{goodsList}}" wx:for-key="{{item._id}}"><!-- class="wux-energized--bg" class="wux-balanced--bg"--><view  style="height:350rpx;border-radius: 15rpx;overflow: hidden;margin: 15rpx 15rpx 15rpx 15rpx;box-shadow: 0 0 20rpx rgb(216, 209, 255);"><view  style="height:280rpx; ;bottom: 0rpx; background-image: url('{{item.imgurl}}'); background-repeat: no-repeat;background-size:contain;background-position: center"></view><!-- background-color: rgba(173, 86, 255, 0.828); --><view style="height:70rpx; background-color: white; bottom: 0rpx; color: gray;"><view style="width: 34%;height:100%; ;float:left; display:flex;align-items: center;/*垂直居中*/ justify-content: center;/*水平居中*/"><!--浏览--><wux-icon type="md-eye" size="22"  class="wux-calm wux-margin-right--5" /><text>{{item.watching}}</text></view><view style="width: 33%;height:100%; ;float:left; display:flex;align-items: center;/*垂直居中*/ justify-content: center;/*水平居中*/"><!--评论--><wux-icon type="md-text" size="22" class="wux-calm wux-margin-right--5" /><text>{{item.discussion}}</text></view><view style="width: 33%;height:100%;  ;float:left; display:flex;align-items: center;/*垂直居中*/ justify-content: center;/*水平居中*/"><!--收藏--><wux-icon type="md-star" size="22" class="wux-calm wux-margin-right--5" /><text>{{item.stars}}</text></view></view></view></wux-col></wux-row>

js

data

goodsList:[{_id:0,imgurl:'http://cdn.skyvow.cn/logo.png',navurl:'',goodsInfo:'',watching:0,discussion:0,stars:0},{_id:1,imgurl:'http://cdn.skyvow.cn/logo.png',navurl:'',goodsInfo:'',watching:0,discussion:0,stars:0},{_id:2,imgurl:'http://cdn.skyvow.cn/logo.png',navurl:'',goodsInfo:'',watching:0,discussion:0,stars:0},{_id:3,imgurl:'http://cdn.skyvow.cn/logo.png',navurl:'',goodsInfo:'',watching:0,discussion:0,stars:0},
]

onload()

  onLoad: async function (options) {let app=getApp();const db=wx.cloud.database()let goods=await db.collection('Goods').where({}).limit(8).get()goods=goods.dataconsole.log(goods)app.globalData.goods=goodsthis.setData({pageHeight:app.globalData.windowHeight,['userInfo.avatarUrl']:app.globalData.userInfo.avatarUrl,['userInfo.nickName']:app.globalData.userInfo.nickName,['userInfo.bgc']:app.globalData.presentTheme.lightBgColor,presentTheme:app.globalData.presentTheme,themeName:app.globalData.presentTheme.deepColor.match(/wux-(\S*)/)[1],goodsList:goods,})var j=0;var tabs1=[];var contents=[];for(var i=0;i<goods.length;i++){contents.push(goods[i].product_name)if((i+1)%3==0){tabs1[j]={key:"tab"+(j+1),title:"推荐"+(j+1),content:contents}console.log(contents)contents=[]j++}}if(i>=1){if(contents!=false){tabs1.push({ key:"tab"+(j+1),title:"推荐"+(j+1),content:contents})}this.setData({tabs1:tabs1,tabsHeight:tabs1.length*87<520?tabs1.length*87:520}) }},

13.富文本编辑及提交存储

程序流程逻辑

具体实现

wxss

@import "../common/lib/weui.wxss";
@import "./assets/iconfont.wxss";.container {position: absolute; top: 0; left: 0; width: 100%;
}.ql-container {box-sizing: border-box;width: 100%;height: 100%;font-size: 16px;line-height: 1.5;overflow: auto;padding: 10px 10px 20px 10px;border: 1px solid #ECECEC;
}.ql-active {color: #7044ffc4;
}.iconfont {display: inline-block;width: 30px;height: 30px;cursor: pointer;font-size: 20px;
}.toolbar {box-sizing: border-box;padding: 0 10px;height: 50px;width: 100%;position: fixed;left: 0;right: 100%;bottom: 0;display: flex;align-items: center;justify-content: space-between;border: 1px solid #ECECEC;border-left: none;border-right: none;
}

wxml

<view style="height:{{editorHeight}}px;"><editor id="editor" class="ql-container" placeholder="{{placeholder}}" bindstatuschange="onStatusChange" bindready="onEditorReady"><!--show-img-size='{{true}}' show-img-resize='{{true}}' --></editor><wux-wing-blank size="default"><wux-button block outline type="royal" bindtap="submit">提交</wux-button></wux-wing-blank><wux-backdrop id="wux-backdrop"  /> </view><view class="toolbar" catchtouchend="format" hidden="{{keyboardHeight > 0 ? false : true}}" show-img-toolbar="{{true}}" style="bottom: {{isIOS ? keyboardHeight : 0}}px"><i class="iconfont " bindtap="indentClick"></i><i class="iconfont icon-charutupian" catchtouchend="insertImage"></i><i class="iconfont icon-format-header-2 {{formats.header === 2 ? 'ql-active' : ''}}" data-name="header" data-value="{{2}}"></i><i class="iconfont icon-format-header-3 {{formats.header === 3 ? 'ql-active' : ''}}" data-name="header" data-value="{{3}}"></i><i class="iconfont icon-zitijiacu {{formats.bold ? 'ql-active' : ''}}" data-name="bold"></i><i class="iconfont icon-zitixieti {{formats.italic ? 'ql-active' : ''}}" data-name="italic"></i><i class="iconfont icon-zitixiahuaxian {{formats.underline ? 'ql-active' : ''}}" data-name="underline"></i><i class="iconfont icon-juzhongduiqi {{formats.align === 'center' ? 'ql-active' : ''}}" data-name="align" data-value="center"></i><i class="iconfont icon-zuoyouduiqi {{formats.align === 'justify' ? 'ql-active' : ''}}" data-name="align" data-value="justify"></i><i class="iconfont icon-youduiqi {{formats.align === 'right' ? 'ql-active' : ''}}" data-name="align" data-value="right"></i><i class="iconfont icon-youxupailie {{formats.list === 'ordered' ? 'ql-active' : ''}}" data-name="list" data-value="ordered"></i><i class="iconfont icon-wuxupailie {{formats.list === 'bullet' ? 'ql-active' : ''}}" data-name="list" data-value="bullet"></i><i class="iconfont icon-undo {{formats.redo === 'undo' ? 'ql-active' : ''}}" data-name='undo' bindtap="undo"></i>
</view>

js

const util=require('../../utils/util')
import { $wuxBackdrop } from '../../components/lib/index'
// var tmpimages=[];//图片数组路径
Page({data: {locks:0,show:false,formats: {},readOnly: false,placeholder: '分享你的护肤小妙招吧!',editorHeight: 300,keyboardHeight: 0,isIOS: false,tempimageid: 1,imageid: 1,image:[// {//   imageid:0,//   imageurl:'null'// }],result:[]//图片上传成功后的url数组},readOnlyChange() {this.setData({readOnly: !this.data.readOnly})},onLoad() {const platform = wx.getSystemInfoSync().platformthis.$wuxBackdrop = $wuxBackdrop()const isIOS = platform === 'ios'this.setData({ isIOS})const that = thisthis.updatePosition(0)let keyboardHeight = 0wx.onKeyboardHeightChange(res => {if (res.height === keyboardHeight) returnconst duration = res.height > 0 ? res.duration * 1000 : 0keyboardHeight = res.heightsetTimeout(() => {wx.pageScrollTo({scrollTop: 0,success() {that.updatePosition(keyboardHeight)that.editorCtx.scrollIntoView()}})}, duration)})},updatePosition(keyboardHeight) {const toolbarHeight = 50const { windowHeight, platform } = wx.getSystemInfoSync()let editorHeight = keyboardHeight > 0 ? (windowHeight - keyboardHeight - toolbarHeight-60) : (windowHeight-80)this.setData({ editorHeight, keyboardHeight })},calNavigationBarAndStatusBar() {const systemInfo = wx.getSystemInfoSync()const { statusBarHeight, platform } = systemInfoconst isIOS = platform === 'ios'const navigationBarHeight = isIOS ? 44 : 48return statusBarHeight + navigationBarHeight},onEditorReady() {const that = thislet app=getApp()wx.createSelectorQuery().select('#editor').context(function (res) {that.editorCtx = res.context //必备初始化语句let article=getApp().globalData.article;if(article){that.editorCtx.setContents({// html:article.html,// text:article.text,delta:article.delta,fail:res=>{console.log(res)}})}}).exec()},blur() {this.editorCtx.blur()},undo(){this.editorCtx.undo()},format(e) {let { name, value } = e.target.datasetif (!name) return// console.log('format', name, value)this.editorCtx.format(name, value)},onStatusChange(e) {const formats = e.detailthis.setData({ formats })},insertDivider() {this.editorCtx.insertDivider({success: function () {console.log('insert divider success')}})},clear() {let that=this;this.editorCtx.clear({success: function (res) {// html='<p><br></p>';// text='\r';// imagesTempPath=[];console.log("clear success")}})},removeFormat() {this.editorCtx.removeFormat()},insertDate() {const date = new Date()const formatDate = `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`this.editorCtx.insertText({text: formatDate})},insertImage() {const that = thisvar max=1//3wx.chooseImage({count: max,success: function (res) {let tmpId=that.data.tempimageid// console.log(res.tempFilePaths)var i = 0// for(; i < res.tempFilePaths.length ; i++){that.editorCtx.insertImage({src: res.tempFilePaths[i], //插入图片临时文件地址data: {id: tmpId, //临时图片id},width: '100%',success: function () {that.data.image.push({id:tmpId,url:res.tempFilePaths[i]})// imagesTempPath.push(res.tempFilePaths[i]);//追加到图片数组中//   tmpimages.push({id:tmpid,url:res.tempFilePaths[i],success:function(){//    tmpid+=1//临时图片id加1//  }})// console.log('insert image success '+(imagesTempPath.length-1)+":"+res.tempFilePaths[i])that.data.tempimageid=tmpId+1;console.log(tmpId+" "+res.tempFilePaths[i]);}})// }}})},/*提交*/submit: function(){let app=getApp()//   if(!app.globalData.hasUserInfo){wx.showToast({//     icon:'none',//     title: '请先登录后进行操作',//     duration:2000//   })//   return;// }let that=this; var time= util.formatTime(new Date());var visible=true;//可见性let title='';let subTitle='';// that.insertDate();that.editorCtx.getContents({success:async function(res){ //async await 问题// html = res.html;// text = res.text;if(!res.text.replace(/\s+/g, '').length ==0 ){ //&&!res.text.replace(/\s+/g, '\r') if (res.html !== constTxt) {// var flag=0;// console.log("检测到输入内容" ) //+ res.text// console.log(res.delta);//替换res.delta中的图片urllet images=that.data.image;console.log(images.length+"张临时图片")var length = res.delta.ops.length;wx.showLoading({title: '正在上传',})for (var i = 0; i < length; i++) {if(res.delta.ops[i].attributes){//筛选出里面的图片let imageId=that.data.imageid;  var _id =parseInt(res.delta.ops[i].attributes['data-custom'].trim().slice(3));//取出临时图片的idfor (var j = 0; j < images.length; j++){      if (_id === images[j].id){           //与实际图片地址的id比较,并替换图片地址// tmpimages[j].url=await util.uploadFile('image',tmpimages[j].url,'articlePictures/'+ (new Date().getTime())+'.png',{// id: imageId// });//设置图片id,需与临时图片id一致,所以初始化赋值时两个均为0即可res.delta.ops[i].insert.image = await util.uploadFile('image',images[j].url,'articlePictures/'+ (new Date().getTime())+'.png',{id: imageId});that.data.result.push(res.delta.ops[i].insert.image);console.log(_id+" "+that.data.result[imageId-1])that.data.imageid=imageId+1;break;}                          }        }}// console.log((that.data.imageid-1)+"张实际图片")console.log(that.data.result.length+"张实际图片")var strify = JSON.stringify(res.delta);//dealta转成字符串变成strifyconsole.log(strify);//提交审核//发布utils.addDB('Articles',{// 'articleID':'id' 文章ID'title':title, //标题'subTitle':subTitle, //子标题'images': that.data.result,//图片数组'content':res.delta,'time':time,// 发布时间'like':0,// 点赞数'discuss':0,// 评论数'collect':0,// 收藏数'visibility': visible//可见性});wx.hideLoading({success: (res) => {console.log('文章发布成功')},})var arrParse = JSON.parse(strify);//strify转成数组变回dealta,这里的arrParse是等于dealta的console.log(arrParse);} else {// console.log(res.html)wx.showModal({showCancel: false,title: '提示',content: '内容不能为空或仅包含图片'})return;}}});},onUnload: function () {let that=this;that.editorCtx.getContents({success: res=>{// html=res.html;// text=res.text;let content=res;if (!res.text.replace(/\s+/g, '').length ==0) {//res.html!==constTxt内容不为空wx.showModal({title: '要放弃编辑的内容吗?',// cancelColor: '#000000',confirmColor:'#f00',success (res) {if (res.confirm) {console.log('用户点击确认');that.clear();} else if (res.cancel) {console.log('用户点击取消');getApp().globalData.article=contentconsole.log(content)}}})}}});},//缩进indentClick(){this.editorCtx.insertText({text: "ㅤㅤ"})},retain() {this.$wuxBackdrop.retain()this.setData({locks: this.$wuxBackdrop.backdropHolds,show:true//显示组件})},release() {this.$wuxBackdrop.release()this.setData({locks: this.$wuxBackdrop.backdropHolds,show:false})}
})

以上是对本人前段时间开发魔镜微信小程序过程中所写的代码和笔记,上传到CSDN方便日后复习回顾。

从零开始编写一个微信小程序(微信开发者工具+JS+WuxUI组件库+云开发)万字整理,建议收藏!相关推荐

  1. 微信开发者工具 wxmi修改模版颜色_网站建设公司讲解:微信小程序的开发者工具界面...

    网站建设公司深圳市博纳网络信息技术有限公司()讲解:微信小程序的开发者工具界面 创建项目后,进入到微信开发者工具界面,界面大致可以分为6个区域:①菜单栏区域,②模拟器.编辑器.调试器显示与隐藏区域,③ ...

  2. 微信小程序在开发者工具和预览下边跳转都好好的真机预览就找不到页面,报错 {“errMsg“:“navigateTo:fail page \“***\“ is not found“}

    微信小程序开发有这么一种情况: 在开发者工具里边随便点随便跳转,页面都能找到 点击预览,手机扫码来回点来回跳,页面都能找到 点击真机预览,来回点来回跳,偶尔页面就找不到了 报错:(in promise ...

  3. 微信小程序在开发者工具中能收到发送消息,手机预览不能获取发送消息

    如题,开发微信小程序时,发现在开发者工具中能正常发送收到消息,而在真机中不打开调试模式的情况下不能发送获取消息,找了半天文档终于找到了原因: 服务器域名配置原因引起的 在没设置服务器域名的时候不开启上 ...

  4. 推荐微信小程序常用的几个UI组件库

    在微信小程序开发的过程中,自己不借助UI组件库开发出来的页面,不但要花费更多的时间,页面的美观度上也有一定差距. 所以在这里我给大家推荐几个好看,常用的几个UI组件库. WeUI WeUI 是微信官方 ...

  5. api 微信小程序组件库colorui_微信小程序常用的几个UI组件库

    1.WeUI WeUI 是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信 Web 开发量身设计,可以令用户的使用感知更加统一.包含button.cell.dialog. progre ...

  6. uniapp开发微信小程序,开发者工具、真机预览都没问题,唯独预览、体验版在手机上列表顺序错乱,用的localeCompare做的中文排序,终于找到问题并解决掉了,记录一下子

    中文转拼音资源地址:https://download.csdn.net/download/qq_38652871/10906193 排序方法记录地址:https://blog.csdn.net/qq_ ...

  7. 【微信小程序】一文带你读懂云开发

    前言 云开发(CloudBase)是一个已经存在了很多年的概念,但在过去未能真正成为主流.然而,由于云和软件即服务的宏观趋势的结合,以及技术的进步,如容器技术 Docker 和 Kubernetes, ...

  8. 用过1000个微信小程序,挑了8个好用又好玩的!建议收藏

    笔者由于工作原因,几乎坐在办公室的每个小时,都要跟小程序打交道,这些年过手的小程序没有八百也有一千,然而能入我法眼的,唯有这10款,特此安利,造福大众! 1 * 查地铁 2 * 车轮查违章 3 * 微 ...

  9. 微信小程序万能模板(tabBar\openid\授权登录\云开发之一个云函数实现云数据库增删查改!)

    Step1:新建小程序 使用自己的appid 勾选不使用云服务(后面可以在项目中再使用,这里若勾选会多出很多乱七八糟的东西) 选择不使用模板 Step2:搭建tabBar 从阿里巴巴图标库https: ...

最新文章

  1. 如何将PDF转换成JPEG图片?
  2. 配置spring-mvc + simple-spring-memcached
  3. python职能-python随机模块22个函数详解(下)
  4. 学python可以干嘛-学习Python可以做什么
  5. maven项目编译漏掉src/main/java下的xml配置文件
  6. python元组支持双向索引吗_2、Python列表和元组
  7. equals方法重写详解
  8. skcket编程实例
  9. 数组的foreach方法和jQuery中的each方法
  10. # 2017-2018-1 20155336《信息安全技术》实验二——Windows口令破解
  11. JAVASE1~5补充
  12. NPM ---- 安装yarn
  13. myeclipse与mysql连接_myeclipse 与 mysql 的连接
  14. html设置ie11兼容,ie11浏览器兼容性问题设置方法
  15. 充电w数测试软件,充电功率检测(cn.nowtool.battery) - 1.3.0 - 应用 - 酷安
  16. 腾讯云云服务器部署Davinci可视化
  17. JAVA钓鱼游戏_java实现小猫钓鱼游戏
  18. 处理linux centos7中登陆plsql后退格键上下键使用乱码问题
  19. 10个最佳iOS Photo App模板
  20. Mysql错误代码1045

热门文章

  1. [分割一切!] SegmentAnything真的太强了
  2. html怎么所有按钮没效果图,点击按钮没反应?所有按钮都没反应
  3. 实验室预约管理系统(Java+SSH+Web+MySQL+ofbiz系统)
  4. keil stm32标准库放在哪里_使用Keil MDK以及标准外设库创建STM32工程
  5. CSP-2019day1题解报告
  6. Android Study
  7. R语言并行计算spearman相关系数
  8. 微信支付 公众号关联商户号
  9. 程序和进程的关系程序
  10. Hadoop面试连环炮