Skip to content

Instantly share code, notes, and snippets.

@Ding-Fan
Last active March 10, 2019 11:06
Show Gist options
  • Save Ding-Fan/ef160941e83129097ecf6f13cf4e7dec to your computer and use it in GitHub Desktop.
Save Ding-Fan/ef160941e83129097ecf6f13cf4e7dec to your computer and use it in GitHub Desktop.
写小程序 wepy 的常用 snippet 。

app.wpy 是入口文件,很多东西例如 globalData intercept onLaunch 都需要在这里配置。

其中 config 属性对应原生的 app.json 文件,build 编译时会根据 config 属性自动生成 app.json 文件,如果需要修改 config 中的内容,请使用微信提供的相关 API。

参考 wepy 文档 小程序文档

在 app.wpy 中,在 constructor 里的 intercept 里的 config 里,通过 url 是否开头含有 http:// 或 https:// 判断是否需要对通过 wepy.request 的请求添加 apiDomain ,起到类似 axios baseURL 的作用

if (!(/^https?:\/\//.test(p.url))) {
  p.url = env.apiDomain + p.url;
}

当调用 requestPayment 时,在 callback (无论是 successfail 、还是 complete )里使用 reLaunch ?报错 fail can not invoke reLaunch in background 。 2018-07-17 是的。这是微信的 bug 。想别的办法吧,例如使用 switchTabredirectTo

在 app.wpy 的 config 里配 pages 时,二级目录需要排在一级目录的后面。 2018-07-17

/* 这样就会报错 */
...
"pages/menu/cart/index",
"pages/menu/index",
...

/* 这样就没问题 */
...
"pages/menu/index",
"pages/menu/cart/index",
...
// 每次添加新页面后,需要重新 `npm run dev` 一次,否则可能报错

当使用“远程调试”提示编译异常时,试试 rm -r dist/rm .wepycache (即删除 dist 文件夹)

在调用 requestPayment 的上一行里调用 removeTabBarBadge 会失效。更新:我把 removeTabBarBadge 放入 requestPayment 的 callback 里,在“远程调试”和“开发者工具”里都成功了,但是在体验版和“预览”里都失败。。。我不想再写这些坑了。。。我¥%@¥#@#¥%#!%&……#¥#@¥ 2018-07-28 卒

在 iOS 里, align-self: stretch; 会失效。 2018-07-28

在 iOS 里,transform: perspective(99999px) translateZ(1px); 会穿透 z-index 比它高的 view 。 2018-07-28 我打算试试用这个代替 z-index 来着。。。

当需要阻止点击冒泡时,可以使用 @tap.stop 。当需要阻止滑动冒泡时,可以使用 @touchmove.stop

...
<view @tap.stop="stopBubble">
  ...
</view>
...

...
<view @touchmove.stop="stopBubble">
  ...
</view>
...
  stopBubble() {
    return
  }

当出现 textarea input 等层级过高现象时

可以使用 cover-view (慎用)。cover-view 对 flex 布局不友好。

textarea 穿透弹窗遮罩(又叫蒙版又叫 mask ),可在弹窗出现时隐藏 textarea ,并用另一个 view 用来占位。

清空 input 可以用 wx:if 切成 false 再切成 true 。。。

使用 font face 从链接载入的字体,可能在第一次打开项目时载入失败。刷新一下就好了。

使用本地图片时,图片名称不能是中文。

onLaunch 里放 debugger 可能一下子报四十多个错。。。

预览生成的二维码时可能出现缓存现象导致预览到的二维码和放在那儿的二维码不是同一个。

为了防止 button 出现默认样式,通常这样写

<button
  id="machine"
  form-type="submit"
>
</button>
<label for="machine">
  <view class="option">
    <view class="icon">
      <image src="nier_automata.png" />
    </view>
    <view class="text">
      好耶
    </view>
  </view>
</label>
/* 需要用到 button 时使用,清除自带样式 */
button,
.button-hover {
  all: unset !important;
}
button::before,
button::after {
  content: none;
}
<!-- Modal 示例 -->
<template>
<view class="page">
<view class="hahahaha">
<view class="header"></view>
<view class="content">
<view class="content-header"></view>
<view class="content-body">
<view class="open-modal"
@tap="openVanilla">普通提示弹窗</view>
<view class="open-modal"
@tap="openOneBtn">一个按钮弹窗</view>
<view class="open-modal"
@tap="openTwoBtn">两个按钮弹窗</view>
<view class="open-modal"
@tap="openContact">联系方式弹窗</view>
</view>
<view class="content-footer"></view>
</view>
<view class="footer"></view>
</view>
<view class="vanilla-modal"
wx:if="{{vanillaModal}}">
<view class="modal-content">
<view class="content-header">
<view class="title"></view>
<view class="close"
@tap="closeVanilla">
<image src="../../assets/images/ic_common_close.png" />
</view>
</view>
<view class="content-body">
下班到我办公室来
</view>
<view class="content-footer"></view>
</view>
<view class="g-modal-overlay"
@tap="closeVanilla"></view>
</view>
<view class="one-btn-modal"
wx:if="{{oneBtnModal}}">
<view class="modal-content">
<view class="content-header">
<view class="title">通知</view>
<view class="close"
@tap="closeOneBtn">
<image src="../../assets/images/ic_common_close.png" />
</view>
</view>
<view class="content-body">
下班到我办公室来
</view>
<view class="content-footer">
<view class="action"
@tap="closeOneBtn">
好的
</view>
</view>
</view>
<view class="g-modal-overlay"
@tap="closeOneBtn"></view>
</view>
<view class="two-btn-modal"
wx:if="{{twoBtnModal}}">
<view class="modal-content">
<view class="content-header">
<view class="title">通知</view>
<view class="close"
@tap="closeTwoBtn">
<image src="../../assets/images/ic_common_close.png" />
</view>
</view>
<view class="content-body">
下班到我办公室来
</view>
<view class="content-footer">
<view class="actions">
<view class="cancel"
@tap="cancelTwoBtn">偏不</view>
<view class="confirm"
@tap="confirmTwoBtn">好的</view>
</view>
</view>
</view>
<view class="g-modal-overlay"
@tap="closeTwoBtn"></view>
</view>
<view class="contact-modal"
wx:if="{{contactModal}}">
<view class="modal-content">
<view class="content-header">
<view class="title">联系方式</view>
<view class="close"
@tap="closeContact">
<image src="../../assets/images/ic_common_close.png" />
</view>
</view>
<view class="content-body">
<input type="number"
maxlength="11"
@input="handleInput({{'cellNumber'}})"
placeholder="请输入联系方式" />
</view>
<view class="content-footer">
<view @tap="confirmContact"
:class="{'action': true, 'active': cellNumberValidated}">
好的
</view>
</view>
</view>
<view class="g-modal-overlay"
@tap="closeContact"></view>
</view>
</view>
</template>
<script>
import wepy from 'wepy';
export default class Hahahaha extends wepy.page {
config = {
navigationBarTitleText: "哈哈哈哈",
};
data = {
vanillaModal: false,
oneBtnModal: false,
twoBtnModal: false,
contactModal: false,
cellNumber: '',
cellNumberValidated: false
};
watch = {
cellNumber(newValue) {
// TODO: 添加 debounce (去抖)
this.cellNumberValidated = !!this.handler().cellNumberValidator(newValue);
// 没想到吧!在 watch 里也要 $apply
this.$apply();
},
};
methods = {
openVanilla() {
this.vanillaModal = true;
},
closeVanilla() {
this.vanillaModal = false;
},
openOneBtn() {
this.oneBtnModal = true;
},
closeOneBtn() {
this.oneBtnModal = false;
},
openTwoBtn() {
this.twoBtnModal = true;
},
closeTwoBtn() {
this.twoBtnModal = false;
},
cancelTwoBtn() {
this.twoBtnModal = false;
},
confirmTwoBtn() {
this.twoBtnModal = false;
},
openContact() {
this.contactModal = true;
},
closeContact() {
this.cellNumber = '';
this.contactModal = false;
},
confirmContact() {
if (!this.cellNumberValidated) {
return;
}
this.contactModal = false;
},
handleInput(key, event) {
this[key] = event.detail.value.trim();
},
};
network() {
return {
foo: () => { }
}
}
handler() {
return {
cellNumberValidator(cellNumber) {
// https://blog.csdn.net/voidmain_123/article/details/78962164 正则来源
const validationRegExp = RegExp('^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\\d{8}$');
return validationRegExp.test(cellNumber);
}
}
}
computed = {};
onLoad() { };
onShow() { };
}
</script>
<style lang='less'>
.g-modal-overlay {
/* 通常前缀为 g- 的都放在 app.wpy 里 */
z-index: 1000;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.6);
}
.page {
.hahahaha {
.header {
}
.content {
.content-header {
}
.content-body {
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
height: 50vh;
width: 100%;
.open-modal {
background-color: pink;
border-radius: 999rpx;
padding: 10rpx 50rpx;
color: #fff;
font-size: 60rpx;
}
}
.content-footer {
}
}
.footer {
}
}
.vanilla-modal {
display: flex;
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
.modal-content {
z-index: 1001;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 500rpx;
border-radius: 8rpx;
background-color: #fff;
display: flex;
flex-direction: column;
overflow: hidden;
.content-header {
.title {
}
.close {
position: absolute;
top: 30rpx;
right: 30rpx;
image {
width: 35rpx;
height: 35rpx;
}
}
}
.content-body {
height: 250rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 32rpx;
font-weight: bold;
}
.content-footer {
}
}
}
.one-btn-modal {
display: flex;
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
.modal-content {
z-index: 1001;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 500rpx;
border-radius: 8rpx;
background-color: #fff;
display: flex;
flex-direction: column;
overflow: hidden;
font-size: 32rpx;
.content-header {
.title {
height: 90rpx;
line-height: 90rpx;
text-align: center;
border-bottom: 2rpx solid #f7f7f7;
font-weight: bold;
}
.close {
position: absolute;
top: 20rpx;
right: 30rpx;
image {
width: 35rpx;
height: 35rpx;
}
}
}
.content-body {
height: 250rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.content-footer {
.action {
font-weight: bold;
color: #fff;
height: 90rpx;
background-color: orange;
// border-radius: 8rpx;
text-align: center;
line-height: 90rpx;
}
}
}
}
.two-btn-modal {
display: flex;
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
.modal-content {
z-index: 1001;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 500rpx;
border-radius: 8rpx;
background-color: #fff;
display: flex;
flex-direction: column;
overflow: hidden;
font-size: 32rpx;
.content-header {
.title {
height: 90rpx;
line-height: 90rpx;
text-align: center;
border-bottom: 2rpx solid #f7f7f7;
font-weight: bold;
}
.close {
position: absolute;
top: 20rpx;
right: 30rpx;
image {
width: 35rpx;
height: 35rpx;
}
}
}
.content-body {
height: 250rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.content-footer {
.actions {
border-top: 2rpx solid #c2c2c2;
height: 90rpx;
display: flex;
.cancel {
text-align: center;
line-height: 90rpx;
flex: 1;
border-right: 2rpx solid #c2c2c2;
}
.confirm {
text-align: center;
line-height: 90rpx;
flex: 1;
}
}
}
}
}
.contact-modal {
display: flex;
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
.modal-content {
z-index: 1001;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 500rpx;
border-radius: 8rpx;
background-color: #fff;
display: flex;
flex-direction: column;
overflow: hidden;
font-size: 32rpx;
.content-header {
.title {
height: 90rpx;
line-height: 90rpx;
text-align: center;
border-bottom: 2rpx solid #f7f7f7;
font-weight: bold;
}
.close {
position: absolute;
top: 20rpx;
right: 30rpx;
image {
width: 35rpx;
height: 35rpx;
}
}
}
.content-body {
height: 250rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
input {
text-align: center;
font-weight: bold;
}
}
.content-footer {
.action {
color: #fff;
height: 90rpx;
background-color: #d2d2d2;
// border-radius: 8rpx;
text-align: center;
line-height: 90rpx;
&.active {
background-color: orange;
}
}
}
}
}
}
</style>
/* 需要让页面占满整屏时使用 */
.page {
background-color: #fff;
min-height: 100vh;
}
/* 底部固定按钮 */
/* 记得给页面加底部 padding-bottom ,防止按钮遮挡页面内容*/
.footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #4d6aff;
font-size: 32rpx;
height: 90rpx;
line-height: 90rpx;
color: #fff;
text-align: center;
}
<!-- 通用页面基本结构 -->
<template>
<view class="page">
<!-- 这里放 modal 之类的 -->
<!-- 由于 wepy 里出现的奇怪现象,只有把 modal 之类的放在用到的 custom component 之前,否则会报错。所以放在这里 -->
<view class="main">
<view class="header">
<!-- header 放不属于本页面特有内容的部分,方便复用和布局 -->
</view>
<view class="content">
<view class="content-header">
</view>
<view class="content-body">
<view class="section">
</view>
</view>
<view class="content-footer">
</view>
</view>
<view class="footer">
<!-- footer 放不属于本页面特有内容的部分,方便复用和布局 -->
</view>
</view>
</view>
</template>
methods = {
goTo(url) {
// 点击页面元素发生页面跳转时使用
this.$navigate({
url
})
},
handleInput(key, event) {
// wepy 没有 v-model ,所以用这种方式获取 input 的内容
// 传入的 key 与 data 中的名称对应
this[key] = event.detail.value.trim()
// 这里可能需要 this.$apply(); ,解决 input 无法获取值的问题。
},
};
network() {
return {
foo: async () => {
let {data, statusCode} = await foo();
if (statusCode == 200) {
// 这里用 == ,因为后端风格各不相同,有的可能给你 String
} else {
wepy.showToast({
title: data.error.message, //提示的内容,
icon: 'none', //图标,
duration: 2000, //延迟时间,
mask: true, //显示透明蒙层,防止触摸穿透,
success: res => {}
});
}
this.$apply();
}
}
}
<!-- 星级选择组件 -->
<!-- 用法
<star :count="starCount" :touchAble.twoWay="starAvailable"></star>
data = {
starCount:2,//星级数
starAvailable:true //是否可点击
};
components = {
star
}; -->
<template>
<view class="star-rank">
<repeat for="{{starInputs}}" key="index" index="index" item="starInput">
<view class="star-input" @tap="rankIt('{{index}}')">
<image src="https://your-star-picture-here/images/{{starInput}}" />
</view>
</repeat>
</view>
</template>
<script>
import wepy from 'wepy';
export default class StarRank extends wepy.component {
data = {
baseStars: [
'ic_star.png',
'ic_star.png',
'ic_star.png',
'ic_star.png',
'ic_star.png'
],
star: 0
};
props = {
count: {
type: Number,
default: 0,
twoWay: true
},
touchAble: {
type: Boolean,
default: false
}
};
computed = {
starInputs() {
let result = this.baseStars.slice();
console.log(result,'result')
for (let i = 0; i < parseInt(this.count); i++) {
result.unshift('ic_star_light.png');
}
return result.slice(0, 5);
}
};
methods = {
rankIt(index) {
if (!this.touchAble) {
return;
}
this.count = index + 1;
console.log(index);
}
};
}
</script>
<style lang="less">
.star-rank {
display: flex;
align-items: center;
.star-input {
display: flex;
align-items: center;
margin-right: 10rpx;
image {
height: 34rpx;
width: 34rpx;
}
}
}
</style>
<!-- Tab 示例 -->
<template>
<view class="page">
<view class="order-management">
<view class="header"></view>
<view class="content">
<view class="content-header">
<view class="tabs">
<block
wx:for="{{tabsArray}}"
wx:key="index"
>
<view
:class="{'tab': true, 'active': activeTab === item.index}"
@tap="tapTab({{item.index}})"
>
{{item.text}}
</view>
</block>
</view>
<view class="decorator">
<view
class="box"
style="margin-left: {{activeTab * 25 }}%"
>
<view class="bar"></view>
</view>
</view>
</view>
<view class="content-body">
<block
wx:for="{{ordersList}}"
wx:key="index"
wx:for-index="index"
wx:for-item="orderItem"
>
<view class="order">
<view class="order-header">
<view class="icon"></view>
<view class="store-name"></view>
<view class="order-status"></view>
</view>
<view class="order-body">
<block
wx:for="{{orderItem.productsList}}"
wx:key="index"
wx:for-index="index"
wx:for-item="productItem"
>
</block>
</view>
<view class="order-footer"></view>
</view>
</block>
</view>
<view class="content-footer"></view>
</view>
<view class="footer"></view>
</view>
</view>
</template>
<script>
import wepy from 'wepy';
export default class Hahahaha extends wepy.page {
config = {
navigationBarTitleText: "哈哈哈哈",
};
data = {
tabsArray: [
{
index: 0, // index 用于 view 层显示与控制
text: '全部',
value: 0 // value 用于 调用接口
},
{
index: 1,
text: '待支付',
value: 1
},
{
index: 2,
text: '待自提',
value: 2
},
{
index: 3,
text: '已完成',
value: 3
},
],
activeTab: 0
};
watch = {};
computed = {
imgDomain() {
return this.$parent.globalData.imgDomain
}
};
methods = {
tapTab(tab) {
this.activeTab = tab;
// this.network().getOrder();
},
};
onLoad() { };
onShow() { };
}
</script>
<style lang='less'>
.page {
background-color: #fff;
min-height: 100vh;
.order-management {
.header {
}
.content {
.content-header {
position: fixed;
top: 0;
left: 0;
right: 0;
background-color: #fff;
box-shadow: 0 2rpx 0 0 #e5e5e5;
z-index: 1;
.tabs {
// background-color: #fff;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
.tab {
flex: 1;
height: 80rpx;
line-height: 80rpx;
text-align: center;
color: #8b8d8c;
font-size: 28rpx;
transition: 0.15s all;
&.active {
color: pink;
font-weight: bold;
}
}
}
.decorator {
width: 100vw;
.box {
width: 25vw;
transition: 0.15s all;
display: flex;
justify-content: center;
align-items: center;
.bar {
width: 5vw;
height: 6rpx;
background-color: pink;
}
}
}
}
.content-body {
padding-top: 80rpx;
}
.content-footer {
}
}
.footer {
}
}
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment