个人案例
- 码易呀嘿
一款可自定义内容,颜色,图片的二维码生成器
码易呀嘿扫码体验
- 全民乐抽
一款可抽奖的小程序
全民乐抽扫码体验
- 猿叔头像助手
一款可更换头像的小程序
猿叔头像助手扫码体验
- H5 跳转微信公众号链接 报错
Refused to frame 'https://mp.weixin.qq.com/' because an ancestor violates the following Content Security Policy directive: "frame-ancestors 'self'
2022-02-21 - 07.Taro框架获取用户手机号解决方案
流程图 [图片] 核心代码 PhoneAuth 组件 [代码]import Taro from '@tarojs/taro'; import React, { useCallback, useEffect, useState } from 'react'; import { View, Button } from '@tarojs/components'; import { PLATFORM_TYPE } from '@/constant/index'; import tools from '@/utils/tools'; import './index.less'; // 工具方法 const PhoneManager: TaroMiniApp.IUtilsPhoneManager = { // 老版本: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/deprecatedGetPhoneNumber.html async oldVersionGetPhoneNumber(evt: TaroMiniApp.TGetPhoneNumberEvtDetail): Promise<TaroMiniApp.TaroApiResult> { return Taro.login() .then((codeRes: Taro.login.SuccessCallbackResult) => { // console.log(codeRes, evt); return this.getPhoneNumber(evt, codeRes.code) }) }, // 新版本: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html async newVersionGetPhoneNumber(evt: TaroMiniApp.TGetPhoneNumberEvtDetail): Promise<any> { console.log(evt); return this.getPhoneNumber(evt); }, // 获取支付宝绑定的手机号 async handleGetAliPayPhoneNumber(): Promise<TaroMiniApp.TaroApiResult> { try { const res = await (Taro as any).getPhoneNumber(); const data = JSON.parse(res.response); if (data.response.code !== 0) { return Promise.reject({ code: data.response.code, errMsg: 'getPhoneNumber:fail', data: { error: data.response }, }) } // TODO: 支付宝 return this.getPhoneNumber(data); } catch (error) { return Promise.reject({ code: 10, errMsg: 'getPhoneNumber:fail', data: { error: error }, }) } }, // TODO: 对接后端服务 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html getPhoneNumber(evtDetail: TaroMiniApp.TGetPhoneNumberEvtDetail, code?: string): Promise<TaroMiniApp.TaroApiResult> { // console.log(evtDetail, code); return Promise.resolve({ code: 0, errMsg: evtDetail?.detail?.errMsg, data: { phone: `121212-${code}` }, }) }, // 处理获取手机号事件 async handleGetPhoneNumberEvt(evt?: TaroMiniApp.TGetPhoneNumberEvtDetail): Promise<TaroMiniApp.TGetPhoneNumberResult> { if (PLATFORM_TYPE === 'WEAPP') { if (evt?.detail?.errMsg.indexOf('deny') !== -1) { return Promise.reject({ errMsg: evt?.detail.errMsg, code: 10, }) } // 获取系统信息 const { SDKVersion } = tools.getSystemInfo(); // console.log(SDKVersion, tools.compareVersion(SDKVersion, '2.21.2')); // 版本号对比 if (tools.compareVersion(SDKVersion, '2.21.2') === -1) { return this.oldVersionGetPhoneNumber(evt); } // new version return this.newVersionGetPhoneNumber(evt); } if (PLATFORM_TYPE === 'ALIPAY') { return this.handleGetAliPayPhoneNumber(); } const res = { code: 10, errMsg: 'getPhoneNumber: noSupport', data: {}, }; return Promise.resolve(res); }, } const PhoneAuth = (props: TaroMiniApp.IComponentsPhoneAuthProps) => { const [phoneNumberAuth, setPhoneNumber] = useState(false); useEffect(() => { if (PLATFORM_TYPE === 'ALIPAY') { Taro.getSetting() .then((res: Taro.getSetting.SuccessCallbackResult) => { setPhoneNumber((res.authSetting as any).phoneNumber); }); } }, []); const handleGetPhoneNumber = useCallback(async (evt) => { if (PLATFORM_TYPE === 'WEAPP') { PhoneManager.handleGetPhoneNumberEvt(evt).then((res) => { props.onSuccess(res) }).catch((err) => { props.onFail(err) }) return; } if (PLATFORM_TYPE === 'ALIPAY') { PhoneManager.handleGetPhoneNumberEvt().then((res) => { props.onSuccess(res) }).catch((err) => { props.onFail(err) }) } }, []); const getManualPhoneNumber = useCallback(async () => { PhoneManager.handleGetPhoneNumberEvt().then((res) => { props.onSuccess(res) }).catch((err) => { props.onFail(err) }) }, []); const onAuthError = useCallback((err) => { props.onFail({ code: 11, errMsg: 'getPhoneNumber: deny', data: err.detail }) }, []); const btnTxt = props.children || props.btnText || '手机号登录'; if (PLATFORM_TYPE !== 'ALIPAY' && PLATFORM_TYPE !== 'WEAPP') { return <View>不支持</View> } return ( <View className="phoneAuthContainer"> { PLATFORM_TYPE === 'WEAPP' && ( <Button style={`${props.btnStyle}`} className={`${props.uesSlot ? 'resetBtn' : ''}`} openType="getPhoneNumber" onGetPhoneNumber={handleGetPhoneNumber} >{btnTxt}</Button> ) } {/* 支付宝已授权用户 */} { PLATFORM_TYPE === 'ALIPAY' && !phoneNumberAuth && ( <Button style={`${props.btnStyle}`} className={`${props.uesSlot ? 'resetBtn' : ''}`} openType="getAuthorize" scope='phoneNumber' onGetAuthorize={handleGetPhoneNumber} onError={onAuthError} >{btnTxt}</Button> ) } {/* 支付宝未授权用户 */} { PLATFORM_TYPE === 'ALIPAY' && phoneNumberAuth && ( <Button style={`${props.btnStyle}`} className={`${props.uesSlot ? 'resetBtn' : ''}`} onClick={getManualPhoneNumber} >{btnTxt}</Button> ) } </View> ); } export default PhoneAuth; [代码] 如何使用 [代码]import React, { useCallback } from 'react'; import { View, } from '@tarojs/components'; // 引入的 PhoneAuth 组件 import { PhoneAuth } from '@/components/index'; import './index.less'; export default function PhoneAuthDemo() { const handleSuccess = useCallback((res) => { console.log('success:', res); }, []); const handleFail = useCallback((err) => { console.log('fail:', err); }, []); const resetBtnStyle = "display: flex; background: #eee"; return ( <View className="page"> <View className='wrap'> <PhoneAuth onSuccess={handleSuccess} onFail={handleFail} /> </View> <View className='wrap'> <PhoneAuth onSuccess={handleSuccess} onFail={handleFail} btnText="自定义文本" /> </View> <View className='wrap'> <PhoneAuth onSuccess={handleSuccess} onFail={handleFail} btnStyle={resetBtnStyle} uesSlot> <View>我是自定义 slot</View> </PhoneAuth> </View> </View> ) } [代码]
2022-03-03 - 微信支付成功页下方展示小程序,请问如何设置?
[图片]
2022-02-23 - 目前为止项目中用到的一些校验
// 判断是否是json字符串 isJSON(str) { if (typeof str == 'string') { try { var obj=JSON.parse(str); if(typeof obj == 'object' && obj ){ return true; }else{ return false; } } catch(e) { return false; } } }, //验证手机号 checkPhone(phone){ var res = /^1[3456789]\d{9}$/; return res.test(phone);//返回true:手机号正确 false:手机号错误 }, // 国内座机 checkTel(phone){ var res = /\d{3}-\d{8}|\d{4}-\d{7}/; return res.test(phone);//返回true:正确 false:错误 }, // 验证邮箱(支持中文邮箱) checkEmail(email) { var res =/^[A-Za-z0-9\u4e00-\u9fa5]+([-_.][A-Za-z\d]+)*@([A-Za-z\d]+[-.])+[A-Za-z\d]{2,5}$/; return res.test(email);//返回true:邮箱正确 false:邮箱错误 }, // 验证中国大陆身份证号 checkIdcard(idcard) { var res = /(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0\d|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/; return res.test(idcard);//返回true:正确 false:错误 }, // 验证中国大陆一代身份证号 checkIdcard1(idcard){ var res =/^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$/; return res.test(idcard);//返回true:正确 false:错误 }, // 验证中国大陆二代身份证号 checkIdcard2(idcard){ var res = /^\d{6}(18|19|20)\d{2}(0\d|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$/; return res.test(idcard);//返回true:身份证正确 false:身份证错误 }, // 验证统一社会信用代码和组织机构代码 CheckSocialCreditCode(code){ var res = /^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$/; return res.test(code);//返回true:正确 false:错误 }, // 验证是否是图片链接 isImageUrl(str){ var res=/^https?:\/\/.*?(?:gif|png|jpg|jpeg|webp|svg|psd|bmp|tif)$/i; return res.test(str);//返回true:正确 false:错误 }, // 验证是否是视频链接 isVideoUrl(str){ var res=/^https?:\/\/.*?(?:swf|avi|flv|mpg|rm|mov|wav|asf|3gp|mkv|rmvb|mp4)$/i; return res.test(str);//返回true:正确 false:错误 }, // 是否是base64 isBase64(str){ var res=/^\s*data:(?:[a-z]+\/[a-z0-9-+.]+(?:;[a-z-]+=[a-z0-9-]+)?)?(?:;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*?)\s*$/i; return res.test(str);//返回true:正确 false:错误 }, // 验证银行卡号 isBankCard(str){ var res=/^[1-9]\d{9,29}$/; return res.test(str);//返回true:正确 false:错误 }, // 中文姓名 isChineseName(str){ var res=/^(?:[\u4e00-\u9fa5·]{2,16})$/; return res.test(str);//返回true:正确 false:错误 }, // 英文姓名 isEnglishName(str){ var res=/(^[a-zA-Z]{1}[a-zA-Z\s]{0,20}[a-zA-Z]{1}$)/; return res.test(str);//返回true:正确 false:错误 }, // 是否是新能源车牌号 isNewCarCard(str){ var res=/[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-HJ-NP-Z]{1}(([0-9]{5}[DF])|([DF][A-HJ-NP-Z0-9][0-9]{4}))$/; return res.test(str);//返回true:正确 false:错误 }, // 非新能源车牌号 isOldCarCard(str){ var res=/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-HJ-NP-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$/; return res.test(str);//返回true:正确 false:错误 }, // 车牌号(新能源+非新能源) isCarCard(str){ var res=/^(?:[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-HJ-NP-Z]{1}(?:(?:[0-9]{5}[DF])|(?:[DF](?:[A-HJ-NP-Z0-9])[0-9]{4})))|(?:[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9 挂学警港澳]{1})$/; return res.test(str);//返回true:正确 false:错误 }, // 护照 (包含香港、澳门) checkPassport(str){ var res=/(^[EeKkGgDdSsPpHh]\d{8}$)|(^(([Ee][a-fA-F])|([DdSsPp][Ee])|([Kk][Jj])|([Mm][Aa])|(1[45]))\d{7}$)/; return res.test(str);//返回true:正确 false:错误 }, // 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线组合) checkAccount(str){ var res=/^[a-zA-Z][a-zA-Z0-9_]{4,15}$/; return res.test(str);//返回true:正确 false:错误 }, // 纯中文汉字 checkChineseWords(str){ var res=/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])+$/; return res.test(str);//返回true:正确 false:错误 }, // 纯英文字母 checkEnglistWords(str){ var res=/^[a-zA-Z]+$/; return res.test(str);//返回true:正确 false:错误 }, // 是否是小数 isDecimal(str){ var res=/^\d+\.\d+$/; return res.test(str);//返回true:正确 false:错误 }, // 纯数字 isNumber(str){ var res=/^\d{1,}$/; return res.test(str);//返回true:正确 false:错误 }, // qq号 isQQNumber(str){ var res=/^[1-9][0-9]{4,10}$/; return res.test(str);//返回true:正确 false:错误 }, // 微信号 6至20位,以字母开头,字母,数字,减号,下划线 checkWxCode(str){ var res=/^[a-zA-Z][-_a-zA-Z0-9]{5,19}$/; return res.test(str);//返回true:正确 false:错误 }, // 中国邮政编码 checkChinaPostalCode(str){ var res=/^(0[1-7]|1[0-356]|2[0-7]|3[0-6]|4[0-7]|5[1-7]|6[1-7]|7[0-5]|8[013-6])\d{4}$/; return res.test(str);//返回true:正确 false:错误 }, // 判断字符串中是否含有表情 isEmojiCharacter(substring) { // if(isEmojiCharacter(str)){console.log('不能含有表情')} for (var i = 0; i < substring.length; i++) { var hs = substring.charCodeAt(i); if (0xd800 <= hs && hs <= 0xdbff) { if (substring.length > 1) { var ls = substring.charCodeAt(i + 1); var uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000; if (0x1d000 <= uc && uc <= 0x1f77f) { return true; } } } else if (substring.length > 1) { var ls = substring.charCodeAt(i + 1); if (ls == 0x20e3) { return true; } } else { if (0x2100 <= hs && hs <= 0x27ff) { return true; } else if (0x2B05 <= hs && hs <= 0x2b07) { return true; } else if (0x2934 <= hs && hs <= 0x2935) { return true; } else if (0x3297 <= hs && hs <= 0x3299) { return true; } else if (hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50) { return true; } } } }, // 判断字符串中是否含有特殊字符 isTeShuString(substring){ var reg = /[~#^$@%&!?%*]/gi; return reg.test(substring); }, 不足之处忘大家指正,非常感谢
2022-02-22 - 迁移公证?
我迁移资料,需要公证!但是我之前注册的公众号的单位已经在两年前注销了!请问如何盖公章和迁移到新的公众号上
2022-02-21 - 如何在小程序中接入微信默认表情?
微信默认表情是否允许接入,有没有版权相关的问题? 想在小程序中接入微信默认表情,请问这个有没有接入方式? 谢谢~ [图片]
2022-02-21 - 如何搭建用户成长体系?
前言 在之前的文章中我提到了关于做用户留存的签到模块,其实签到模块只是用户成长体系积分中的获取途径之一,今天我和大家聊聊用户成长体系。你可以理解上之前那篇是技术篇主要讲解的是怎么做,这篇是产品篇主要讲解为什么要这样做。 相信你在生活中见过太多的用户成长体系,比如QQ会员、音乐会员、视频会员等等。那么为什么要有用户成长体系呢? 用户成长体系 用户成长体系本质上是用户精细化运营的重要手段。它可以提高用户忠诚度、提升用户留存、促进用户活跃。用户成长体系对于用户来说更是身份的象征,通过显著的会员等级标记,以及不经意之间的露出炫耀,让用户有更多的尊贵感,满足用户的虚荣心。 让用户去完成一些任务,获取积分的过程中,顺便提升等级。这样把用户进行了筛选,从而达到针对不同用户做出不同的产品策略。那么如何搭建用户成长体系呢? 第一步:分析数据 可以通过常用的用户分层模型 RFM 来进行区分。 [图片] RFM模型是衡量客户价值和客户创造利益能力的重要工具和手段。通过一个客户的近期购买行为、购买的总体频率以及花了多少钱3项指标来描述该客户的价值状况。 最近一次消费 (Recency) 消费频率 (Frequency) 消费金额 (Monetary) 通过以上模型,分析出来以下 4 类用户需要重点关注: 重要价值客户:最近消费时间近、消费频次和消费金额都很高,必须VIP服务。 重要保持客户:最近消费时间较远,但消费频次和金额都很高,需主动和他保持联系。 重要发展客户:最近消费时间较近、消费金额高,但频次不高,忠诚度不高,很有潜力的用户,必须重点发展。 重要挽留客户:最近消费时间较远、消费频次不高,但消费金额高的用户,可能是将要流失或者已经要流失的用户,应当给予挽留措施。 第二步:制定规则 用户体系通常的形态包含:积分、成长值、登记、勋章等,这个可以根据自己的产品来选择需要的形态。在这些形态中就需要一套合理的规则,制定规则的时候需要注意以下两点: 等级要循序渐进 在计算各等级成长值区间时,要像我们玩游戏打怪一样,越到后面越难,不同登记需要不同的成长速度,千万不要平均分配。 以QQ等级为例: [图片] 要遵循先易后难,这样才有挑战,我们可以把等级大概分成三个阶段。 初级,先让用户尝到甜头,降低门槛留存用户。 中级,筛选用户到这个级别有一定难度,能到达都是核心用户。 高级,能到这个登记的,那是最高价值的用户群体,需要重点对待。 有付出就要有回报 当用户完成某些任务获取到了积分,那么久需要有个场景能让积分进行消费可以是各种奖品、权益、勋章、功能等。明确告诉用户“你做什么能得到什么”。 在这里需要注意的就是需要提前去思考成本与收益。用户将积分兑换为权益所产生的成本应当小于该用户贡献的价值,这样才能保证不亏本。 第三步:目标导向 我们肯定是为了达成某个目标采取做的成长体系,所以不要盲目的去跟风做成长体系。在做之前想清楚自己的用户指标是什么,不要为了做而做,没有意义,浪费没必要的成本。 比如你想拉新,任务规则是:你在邀请朋友后,可以获得积分,这样做的目的是为了提高“新增”;也可以让你在连续签到 3 天获得双倍积分,这种方式就是通过提升“留存”的方式提升整个产品的用户指标。 小结 本篇文章主要讲解了: 为什么要做用户成长体系?本质是为了做用户的精细化运营 怎么做成长体系? 分析数据:不同用户不同对待方式 制定规则:先以后难,付出要有回报 目标导向:根据用户指标制定任务类型
2022-02-09 - 微信的unionid会变么?
如果公司的公众号迁移,会不会出现微信unionid变化的情况?
2022-01-10 - 现在能从我的小程序直接跳到别人的小程序里吗?有开发文档吗?
因为需要,想在我的小程序里,能打开别人的小程序,小程序目前能实现吗?有没有开发文档?
2022-01-10 - 这种小程序个人主体可以开发吗?
[图片] 这种弹框收款码怎么实现的? [图片]
2022-01-10 - 微信小程序5个不常见,但很实用的配置。
一、微信小程序资料页客服按钮配置。 1、配置前: [图片] 2、配置后: [图片] 3、配置方法: [图片] [图片] 注意要点: ·有客户通过这个按钮咨询,客服微信会收到服务通知。但要切换成在线状态才能收到。 ·因咨询有时效性,尽量让客户加客服微信再继续深度沟通。 [图片] 二、微信小程序允许被搜索开关。 这个功能跟另一个“暂停服务设置”有点像,但搜索开关和暂停服务还是有很大区别。比如一些内部的管理小程序,内部会经常用到。但不希望被非相关人员搜索到。又或者要注销的小程序,也可以关闭搜索再注销。不然注销完成了,还是有搜索信息展示。 配置方法: 小程序后台 > 设置 > 功能设置 > 私隐设置 [图片] 三、微信小程序资料页关联小程序。 1、配置前: [图片] 2、配置后: [图片] 3、配置路径: 小程序后台 > 设置 > 功能设置 > 相关小程序 [图片] 四、微信小程序资料页关联公众号。 1、配置前: [图片] 2、配置后: [图片] 3、配置路径: 小程序后台 > 设置 > 功能设置 > 相关公众号 [图片] 五、微信小程序支付后关注公众号。 本来这一条应该放在第一位,但可惜去年因影响用户体验,2021年8月19日功能下线。 [图片] 为什么还把这条不能配置放进来呢?不是为了凑数,而是想提醒社区的每一位开发或运营不要滥用微信生态功能!自己就亲身经历了很多士多连锁,支付后就收到公众号推送的一大堆广告。 除了这5个微信小程序配置,还想知道哪个配置(非代码)?可以评论区回复。
2022-01-06 - 400元迁移微信公众号留言功能,迁移全过程
背景:2021年5月27日晚8点30分开始策划迁移公众号,迁移的目的为了是为了【留言】功能,因为从2018年开始注册的公众号都没有留言功能。 [图片] 为了不浪费朋友时间,我直接把他的管理员转移给我微信小号了,方便我来回各种扫码。进入主题,开始迁移公众号,先看官方流程图: [图片] 迁移流程总耗时5天(可以缩短到3天) 开始发起迁移: [图片] 管理员(我的微信小号)扫码验证后出现迁移协议,就是下面这个图 [图片] 输入目标账号原始ID(本公众号的原始ID)发送验证后,在目标账号管理员微信端(我的微信大号)允许即可 [图片] 同意后下一步下载迁移公函 [图片] 下载下来我们需要填写迁移双方基本信息,迁移公函需要双方盖章 [图片] 继续准备资料办理公证书:原公众号营业执照、原公众号后台截图、原法人身份证正反面、目标公众号营业执照、目标公众号后台截图、目标法人身份证正反面,公众号后台截图。 [图片] 准备好后开始办理公函公证书,可以在网上办理某宝1-2百左右。线下的话地图搜索:公证处,提前打电话问问能不能做公众号迁移公函公证书,问好了再去,大约3-5百左右。(下面是我的公证书) [图片] 然后我们上传这些资料去提交即可,最后支付300元。(如果资料传错了,有两次免费修改的机会,两次都错了那继续交300) [图片] 然后我们等着就好了,一般情况2、3天(工作日)就搞定了,期间会打电话确认,然后双方公众号管理员同意迁移,约1个工作日迁移成功。 [图片] 至此公众号迁移功能已完成,整理一下我准备的资料。 [图片] 最后做个timeline 2021年5月27日:和朋友沟通拿号 2021年5月28日:把朋友公众号管理员签到我的微信 2021年5月29日:开始准备迁移所有资料 2021年5月30日:TB线上办理公函公证书100,上传后支付300迁移费 2021年5月31日:迁移电话确认,双方管理员同意,迁移成功
2021-06-01 - 小程序介绍页如何配置相关公众号、小程序?(如图)
小程序介绍页如何配置相关公众号、小程序?(如图) [图片][图片] 如上图,更多资料 下面是没内容的;怎么配置下图红框的功能? [图片]
2022-01-04 - 从不同端进入微信小程序的方式
在开发过程中会经常遇到要从H5、APP、短信等各端进入小程序的功能。今天就来简单聊聊怎么实现从各端进入到微信小程序的现实和背景 短信进入到小程序 背景:短信作为一个重要的流量入口,经常会被用于唤醒用户。但是历史原因,短信一般都是内嵌网页链接,带给用户的体验较差,并且对此渠道的粘性态度,微信小程序的出现刚好一定程度的解决了这方面的问题,微信小程序的功能多样,并且有一些订阅功能,能够一定程度降低唤起成本,所以越来越多的渠道选择通过用短信拉起小程序作为一个入口 实现方式: 生成小程序的URL Scheme,进入小程序管理后台,选择“工具-生成URL Scheme”,这样 [图片] 添加需要跳转指定页面路径,是否需要传参,添加完成之后生成你的地址,这时候发送短信只要把这个地址带上,用户点开的时候就能直接跳转到对应的小程序了 [图片] Tips: 这种方式只能实现固定路径和参数的scheme,如果需要动态路径则需要考虑后端支持或者自己动手实现云函数支持了,这里就不多赘述了。 H5进入小程序 背景:H5作为用户增长投放最重要的一环,经常会被投放到各个app或者浏览器中,这种推广简单且无兼容问题,所以也是备受青睐的一种方式 实现:实现方式也是基于微信小程序的scheme,在H5页面跳转到地方放入scheme,跳转也只需要执行 window.location.href="weixin://dl/business/?t=cwlT2e6Avvq" 就可以,部分app或者浏览器不兼容可以使用 iframe的方式来实现 const iframe = document.createElement('iframe') iframe.style.display = 'none' iframe.src = scheme document.documentElement.appendChild(iframe) setTimeout(function () { if (iframe) { document.documentElement.removeChild(iframe) } }, 0) Tips: 一般做用户触发调用可以直接通达,有些浏览器会有拦截直接放到onload函数中的操作,这时候需要手动点击才可以打开。 APP进入小程序 背景:移动互联网时代的来临,每个人已经离不开手机,更离不开手机里的各种app。抖音快手微博网易等app占据了时间和精力,所以一写聪明的人又抓住了眼球,通过常用app里来打广告实现用户增长 实现方式:一些用户量大的APP一般都会暴露出一些Native方法供调用,不通的app有不同的接入方式,并且需要做一些注册能力等的功能可以按实际接入的方式来实现即可。
2021-12-30 - 多个小程序同步登录【已实现】
1、需求 有同一个主体的2个小程序,分别是小程序A和小程序B,现在需要小程序A登录后,点击某个按钮/页面跳转到小程序B,实现小程序B登录。 2、思路 小程序A通过[代码]wx.navigateToMiniProgram[代码]方法将[代码]extraData[代码]传递给小程序B,小程序B在[代码]app.js[代码]中的[代码]onLaunch[代码]里面接收到[代码]extraData[代码]的值,从而实现登录。 3、实现 小程序A中的[代码]wx.navigateToMiniProgram[代码]方法 [代码]function syncLogin(appId,userId){ wx.navigateToMiniProgram({ appId: appId, path: '', // 打开小程序B页面的路径,如果为空则打开首页 extraData: { usrId: userId, // 需要传递给小程序B的数据 }, envVersion: 'develop', //开发版 // envVersion: 'trial', //体验版 // envVersion: 'release', //正式版 }) } [代码] 小程序B中模拟测试 在小程序B开发工具中,点击编译,进入场景选择【1037:从小程序进入】,并将[代码]AppID[代码]和[代码]extraData[代码]填上,一般都是[代码]json[代码]字符串。 [图片] 在小程序B中的[代码]app.js[代码]中的[代码]onLaunch[代码]里面通过[代码]options[代码]接收到[代码]extraData[代码]的值,用[代码]console.log[代码]打印验证。 [代码]App({ // 公共配置 data: { }, // onLaunch:当小程序加载完毕后就执行的方法 onLaunch: function (options) { // 得到进入场景值 console.log('options.from:' + options.referrerInfo.extraData.userId); // ...根据用户ID,就可以实现【小程序A在登录状态下点击跳转到小程序B自动登录的效果】。。 } }) [代码]
2021-12-22 - 微信小程序动画实现方案
微信小程序动画实现方案 背景 基于商城业务需求背景下,实现加购动画;具体要求如下: 动画落点不准 体验优化,动画先行,每点击一次需要触发一次动画 减少动画掉帧,卡;避免动画延迟,解决加购卡顿问题 基于以上诉求,对微信小程序各种动画实现方案做了简单的对比分析 动画落点不准确 最开始实现方案,就是在dom ready 的时候去获取元素落点,缓存下来,后续复用改落点即可; [代码] onReady() { // 获取落点坐标 this.getBubblePos('#end-point'); }, [代码] 然而在商城当前业务中,这种方案,获取到的坐标频繁出现较大偏差。基于现状,延迟获取落点坐标,出现以下方案(timeout 和 nextTick 都尝试了,依然没能解决问题,于是综合了一下) [代码] onReady() { // 获取落点坐标 this.$nextTick(() => { setTimeout(() => { this.getBubblePos('#end-point'); }, xxxx); }) }, [代码] [代码]timeout[代码]方案尝试了不同的延迟时间,发现在1s内,依旧会出现 获取到的位置不准确,初步判断跟加购动画落点所在的结算条的条件渲染有关。timeout 解决不了问题。 最终在x大佬的指导下,参考了其他平台的实现方案,采取了点击时获取落点坐标,解决了该问题; [代码] async startAnimation(e) { //点击时 获取落点 并 缓存 await this.setStartPos(`#end-point`); // 开始动画 this.useAnimateApi(0); }, [代码] 快速点击下能连续触发,多个动画并存 需要有多个动画元素,保证快速点击的时候,动画不延迟,并且每次点击都能触发一个动画元素执行动画;动画元素要求能购复用,减少冗余dom 元素; 业务现状: 现有加购按钮跟落点所在的结算条位于不同的组件内;点击时获取到点击位置的坐标,然后需要将动画元素从点击位置 移动到落点; 解决方案: 每个页面初始化 [代码]n[代码] (n=10)个元素隐藏在 页面内部 css 隐藏到页面某个区域;为了回收复用动画元素 用一个队列来维护当前可使用动画元素; [代码] <!-- css 元素2个元素实现 --> <block wx:for="{{transition}}" wx:key="id"> <view class="bubblebox bubblebox_{{index}}" style="{{item.parent}}"> <view class="bubble bubble_{{index}}" bind:transitionend="transitionEnd" data-index="{{index}}" style="{{item.child}}"></view> </view> </block> [代码] [代码]// 维护动画元素队列,动画执行完成之后 回收当前动画元素 transitionEnd(e) { console.log(e); // 监听动画结束时间 清除动画 const { index = 0 } = e.currentTarget.dataset || {}; this.data.queue.push(Number(index)); this.data.transition.splice(index, 1, { id: index, parent: '', child: '' }); this.setData({ transition: this.data.transition, }); }, [代码] 掉帧 卡顿问题 针对该问题,对各个实现方案做了个对比 Note: 商城加购动画 最终实现方案采用 css transition 实现 [代码]wx.createAnimation[代码] [代码] useCreateAnimation(index) { const { x: x1, y: y1 } = this.data.startPos; const { x: x2, y: y2 } = this.data.dropPos; // 1. 移动到 起点 // duration 不能为0 最小为1 this.animation.translate(x1, y1).opacity(1).step({ duration: 1, delay: 0 }); // const parent = this.animation.export(); // // 2. 起点移动到 落点 this.animation.translate(x2, y2).step({ duration: 1000, delay: 1 }); // // // 3. 隐藏气泡 回到起点 this.animation.opacity(0).translate(0, 0).step({ duration: 1, delay: 1000 }); const child = this.animation.export(); this.data.transition.splice(index, 1, { id: index, parent: '', child }); this.setData({ transition: this.data.transition, }); }, [代码] [代码]this.animate[代码] 从小程序基础库 2.9.0 开始 [代码] useAnimateApi(index) { const { x: x1, y: y1 } = this.data.startPos; const { x: x2, y: y2 } = this.data.dropPos; console.log(`useAnimateApi`, index, x1, y1, x2, y2); // 2. 上 下 移动动画 this.animate(`#bubble_${index}`, [{ left: `${x1}px`, top: `${y1}px`, opacity: 1 }, { translate: [`${x2 - x1}px`, `${y2 - y1}px`] }], 500, () => { console.log('animationEnd'); this.clearAnimation(`#bubble_${index}`, () => {}); this.animationEnd(index); }); }, [代码] [代码]css3 transition[代码] [代码] useCssAnimate(index) { const { x: x1, y: y1 } = this.data.startPos; const { x: x2, y: y2 } = this.data.dropPos; const parent = `transition: transform 0ms 0ms linear;transform: translate(${x1}px,${y1}px)`; const child = `transition:opacity 0s 0s,transform 2s 0ms linear;transform: translate(${x2 - x1}px,${y2 - y1}px);opacity:1;`; this.data.transition.splice(index, 1, { id: index, parent, child }); this.setData({ transition: this.data.transition, }); }, [代码] [代码]wxs[代码] 相应事件 小程序双引擎设计,setData 是影响性能的关键因素,wxs 相应事件能减少 setData 次数,有助于提高动画性能; [代码]<wxs module="utils"> var transition = function(e, ownerInstance) { var index = 0 var parent = ownerInstance.selectComponent('#bubblebox_1_'+index) // 返回组件的实例 var child = ownerInstance.selectComponent('#bubble_1_'+index) // 返回组件的实例 var x1 = 300 var y1 =50 var x2 = 50 var y2 = 278 console.log(parent,child) parent.setStyle({ transition: 'transform 0ms 0ms linear', transform: 'translate(300px,50px)' }) child.setStyle({ transition:'opacity 0s 0s,transform 2s 0ms linear', transform: 'translate(-250px,238px);opacity:1' }) return false // 不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault } module.exports = { transition:transition } [代码] </details> [代码]css3 animation[代码] 待尝试,动画生成keyframes 如何绑定到 dom 上? [代码]css transition[代码] 细节实现 分析当前动画元素过程 css 元素从隐藏的位置(fixed到左上角,(0,0))移动到起点,并可见; [代码]transition:opacity 0s 0s,top 0s 0s,left 0s 0s,transform 2s 0ms linear; transform: translate(1px,1px);opacity:1; top:100px; left:100px; [代码] 通过top,left 移动元素到起点,并且 通过opacity 控制元素展示出来;执行 translate 动画 利用[代码]transition[代码] 可以同时为不同的 [代码]动画属性[代码] 指定不同的 [代码]duration[代码] [代码]timeFunction[代码] [代码]delay[代码] 动画结束 移除动画元素绑定的 style,动画元素回到起点;动画结束,维护可使用的动画元素队列;监听动画结束事件,回收当前元素入队。 [代码] <view bind:transitionend="transitionEnd"></view> [代码] [代码] transitionEnd(e) { console.log(e); const { index = 0 } = e.currentTarget.dataset || {}; // 动画元素 入队 this.data.queue.push(Number(index)); // 监听动画结束时间 清除动画 this.data.transition.splice(index, 1, { id: index, parent: '', child: '' }); this.setData({ transition: this.data.transition, }); }, [代码] 可以用1层dom,为何用2层dom ? 虽然元素已经脱离了文档流,top left 并不会触发 重绘 引起性能问题;但是不能充分利用css3 硬件加速的能力。 完美解决??? 该方案对 dom性能有较大提高,但是消耗的内存占用也不容小觑。目前商城业务高度复杂,内存占本来就高的情况下,改方案依旧会在内存占用过高的情况下[代码]闪现[代码],过渡态消失的情况。 抛物线方案 该方案 必须使用2层view元素实现 抛物线方案,改方案初步实现后在小程序里面不同机型上 效果差异较大,动画方案回退为直线; 具体步骤: 移动到起点 父级元素 向左边移动 同时子级元素先向上移动指定距离,再向下(时间函数控制曲线斜率) 回收元素 参考: [代码]// 1. 抛物线 const parent = `transition: top 0s 0s, left 0s 0s, opacity 0s 0s, visibility 0s 0s, transform 500ms 0ms ease-in;transform: translateX(${ x2 - x1 }px);left:${x1}px;top:${y1}px;opacity:1;visibility:visible;` const child = `transition: transform 300ms 200ms ease-in, margin-top 200ms 0ms ease-in-out,opacity 0ms 501ms linear,visibility 0ms 501ms linear;margin-top: -66px;transform: translateY(${ y2 - y1 + 66 }px);opacity:0;visibility:hidden;` [代码] Refer weixin miniprogram wxs 响应事件 浏览器的重绘和回流(Repaint & Reflow)
2021-12-13 - 请问这样的连接怎么弄的?
上面有视频号连接 然后分栏有视频号 而且分栏在上面怎么做的? [图片]
2021-12-10 - 如何使用卫星地图?
如何将地图做成卫星地图这种[图片]
2021-12-10 - 想问一下这种便利店小程序怎么做?
看了很多家都是同样的样式,是不是小程序有自带这种组件 [图片][图片]
2021-12-08 - 如何手动调取小程序自带的反馈与投诉页面?
页面上做一个按钮,想打开小程序菜单里的反馈与投诉,请问有相关的API吗? (图片标错了,应该是右边的反馈与投诉按钮 ) [图片]
2021-12-01 - 小程序除了web-wive方法,可以用内置的播放时或者标签实现VR全景图的查看和VR视频的播放吗?
目前有一个需求,就是在小程序里直接查看VR全景图或者观看VR视频,不使用外链的情况下能实现吗?
2021-11-30 - input 框能限制小数点后只能输入两位么和微信支付那样,超过小数点两位输入不显示么?
求大佬指点
2021-11-30 - 小程序支持屏幕录制么? 不是录像是录屏
小程序支持屏幕录制么? 不是录像是录屏
2020-07-21 - 微信小程序能不能把文件进行切片上传?
微信小程序可以将文件切片上传吗??需要上传大文件,现在没有解决方案??官方人员出来回答一下
2021-11-23 - 请问这个胶囊怎么透明?
[图片]
2021-11-18 - uniapp接入人身核验小程序
1,下载小程序SDK 2,解压放入项目微信组件目录中,并配置组件 [图片]3,在uniapp默认index页面调用小程序 [图片] 4,修改sdk main index room js文件跳转路径。指向组件对应页面 [图片] 5,组件页面调用小程序 [图片]
2021-11-18 - 微信小程序隐私指引填写范本
微信隐私升级后,很多人问我怎么写,其实,按实际情况写就行了,不会的,参考下面的就知道大概什么意思了 开发者处理的信息根据法律规定,开发者仅处理实现小程序功能所必要的信息。 为了分辨用户,开发者将在获取你的明示同意后,收集你的微信昵称、头像。为了显示店铺距离,开发者将在获取你的明示同意后,收集你的位置信息。为了保存海报,开发者将在获取你的明示同意后,使用你的相册(仅写入)权限。为了方便获取订单信息,开发者将在获取你的明示同意后,收集你的手机号。开发者 收集你的地址,用于获取订单位置信息。开发者 收集你选中的照片或视频信息,用于获取订单反馈信息。开发者 使用你的通讯录(仅写入)权限,用于获取订单联系信息。
2021-11-18 - 微信开发者工具卡顿,超级卡顿
做任何操作,切换文件也是
2021-11-15 - 无感支付对接要怎么对接?
停车场运营想接入微信的无感支付,有看到代扣服务,微信分停车服务,还有车主服务。现在到底要怎么接入无感支付,看到部分申请还需要联系什么微信bd,这又是怎么联系?
2021-11-12 - 小程序类目审核咨询我终于知道为什么很多人找不到人工客服了。。。
如果你一直问人工客服,会让你“请点击人工客服咨询”。。。问题是人工客服入口没有啊。。。 一直问一直答,死循环了。。。结果我打趣的回复了一句,“怎么点击人工客服”,却跳出来转人工客服的入口。。。 好吧,这差点把我搞蒙。。。 希望不要太多人看到,累坏小哥哥小姐姐 [图片]
2021-11-10 - 用户隐私保护指引的填写完成后还是一直提示需要填写,无法提审?
11月新规的用户隐私保护指引,我们今天在做版本更新时提示需要补充,填写完成后就只生成了指引,但是回到提审页刷新重新提交时还是提示需要设置补充,要怎么操作才能顺利提审? [图片][图片]
2021-11-03 - 小程序跳转H5,无法实现微信支付?
小程序跳转到H5后,点击H5的微信支付弹出不支持打开非业务域名,请重新配置。[图片]
2021-10-21 - wx.saveImageToPhotosAlbum 保存base64格式图片
您好,wx.saveImageToPhotosAlbum 这个接口可不可以保存base64 格式的图片?后端返回来的是base64 格式的图片
2018-09-29 - 传统原生支付用云开发实现(非云支付)
本文的代码已过时,请勿照抄。建议改用云支付。 本文的代码被论坛自动过滤了所有XML的标签,所以照抄是会出错的。需要代码的话,看以前的老版本: https://developers.weixin.qq.com/community/develop/doc/000620ec5acb482103b7bf41d51804?jumpto=comment&commentid=000ea67d7b4da8d6c47acd1e05b8 代码前提:只需要替换两个与自己相关的参数key和mch_id 1、小程序开通微信支付成功,去公众平台(https://mp.weixin.qq.com/); 成功后可以知道自己的mch_id,即商户号。 2、去这里:商户平台(https://pay.weixin.qq.com/),获取key = API密钥,如果是退款的话,还需要下载API证书。 [图片] 以下代码仅包含统一下单,以及小程序端拉起支付的代码。 小程序端: testWxCloudPay: function () { wx.cloud.callFunction({ name: 'getPay', // data: {body:"body",attach:"attach",total_fee:1}, // 可传入相关参数。 success: res => { console.log(res.result) if (!res.result.appId) return wx.requestPayment({ ...res.result, success: res => { console.log(res) } }) } }) }, 云函数getPay: const key = "ABC...XYZ" //换成你的商户key,32位 const mch_id = "1413092000" //换成你的商户号 //以下全部照抄即可 const cloud = require('wx-server-sdk') const rp = require('request-promise') const crypto = require('crypto') cloud.init() function getSign(args) { let sa = [] for (let k in args) sa.push(k + '=' + args[k]) sa.push('key=' + key) return crypto.createHash('md5').update(sa.join('&'), 'utf8').digest('hex').toUpperCase() } function getXml(args) { let sa = [] for (let k in args) sa.push('<' + k + '>' + args[k] + '') sa.push('' + getSign(args) + '') return '' + sa.join('') + '' } exports.main = async(event, context) => { const wxContext = cloud.getWXContext() const appId = appid = wxContext.APPID const openid = wxContext.OPENID const attach = 'attach' const body = 'body' const total_fee = 1 const notify_url = "https://mysite.com/notify" const spbill_create_ip = "118.89.40.200" const nonceStr = nonce_str = Math.random().toString(36).substr(2, 15) const timeStamp = parseInt(Date.now() / 1000) + '' const out_trade_no = "otn" + nonce_str + timeStamp const trade_type = "JSAPI" const xmlArgs = { appid, openid, attach, body, mch_id, nonce_str, notify_url, out_trade_no, spbill_create_ip, total_fee, trade_type } let xml = (await rp({ url: "https://api.mch.weixin.qq.com/pay/unifiedorder", method: 'POST', body: getXml(xmlArgs) })).toString("utf-8") if (xml.indexOf('prepay_id') < 0) return xml let prepay_id = xml.split("")[0] let payArgs = { appId, nonceStr, package: ('prepay_id=' + prepay_id), signType: 'MD5', timeStamp } return { ...payArgs, paySign: getSign(payArgs) } } packge.json: { "name": "getPay", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "zfe", "license": "ISC", "dependencies": { "wx-server-sdk": "latest", "crypto": "^1.0.1", "request-promise": "^4.2.2" } } 附:完整代码片段:如果你觉得在这上面花的时间超过一天了,就去下载代码片段吧。 [图片]
2020-10-20 - 云开发支付的代码,有需要的进。
真机测试已通过。你照抄就行,保证可通过。 最新完美版本可供参考: https://developers.weixin.qq.com/community/develop/article/doc/0004c4a50a03107eaa79f03cc56c13 小程序端: wx.cloud.callFunction({ name: 'getPay' , data: { total_fee: parseFloat(0.01).toFixed(2) * 100, attach: 'anything', body: 'whatever' } }) .then( res => { wx.requestPayment({ appId: res.result.appid, timeStamp: res.result.timeStamp, nonceStr: res.result.nonce_str, package: 'prepay_id=' + res.result.prepay_id, signType: 'MD5', paySign: res.result.paySign, success: res => { console.log(res) } }) }) 云函数:getPay getPay目录下共两个文件: 1、index.js 2、package.json index.js代码如下: const key = "YOURKEY1234YOURKEY1234YOURKEY123"//这是商户的key,不是小程序的密钥,32位。 const mch_id = "1413090000" //你的商户号 //将以上的两个参数换成你的,然后以下可以不用改一个字照抄 const rp = require('request-promise') const crypto = require('crypto') function paysign({ ...args }) { let sa = [] for (let k in args) sa.push( k + '=' + args[k]) sa.push( 'key=' + key) return crypto.createHash('md5').update(sa.join('&'), 'utf8').digest('hex').toUpperCase() } exports.main = async (event, context) => { const appid = event.userInfo.appId const openid = event.userInfo.openId const attach = event.attach const body = event.body const total_fee = event.total_fee const notify_url = "https://whatever.com/notify" const spbill_create_ip = "118.89.40.200" const nonce_str = Math.random().toString(36).substr(2, 15) const timeStamp = parseInt(Date.now() / 1000) + '' const out_trade_no = "otn" + nonce_str + timeStamp let formData = "<xml>" formData += "<appid>" + appid + "</appid>" formData += "<attach>" + attach + "</attach>" formData += "<body>" + body + "</body>" formData += "<mch_id>" + mch_id + "</mch_id>" formData += "<nonce_str>" + nonce_str + "</nonce_str>" formData += "<notify_url>" + notify_url + "</notify_url>" formData += "<openid>" + openid + "</openid>" formData += "<out_trade_no>" + out_trade_no + "</out_trade_no>" formData += "<spbill_create_ip>" + spbill_create_ip + "</spbill_create_ip>" formData += "<total_fee>" + total_fee + "</total_fee>" formData += "<trade_type>JSAPI</trade_type>" formData += "<sign>" + paysign({ appid, attach, body, mch_id, nonce_str, notify_url, openid, out_trade_no, spbill_create_ip, total_fee, trade_type: 'JSAPI' }) + "</sign>" formData += "</xml>" let res = await rp({ url: "https://api.mch.weixin.qq.com/pay/unifiedorder", method: 'POST',body: formData}) let xml = res.toString("utf-8") if (xml.indexOf('prepay_id')<0) return xml let prepay_id = xml.split("<prepay_id>")[1].split("</prepay_id>")[0].split('[')[2].split(']')[0] let paySign = paysign({ appId: appid, nonceStr: nonce_str, package: ('prepay_id=' + prepay_id), signType: 'MD5', timeStamp: timeStamp }) return { appid, nonce_str, timeStamp, prepay_id, paySign } } package.json 代码如下: { "name": "getPay", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "youself", "license": "ISC", "dependencies": { "crypto": "^1.0.1", "request-promise": "^4.2.2" } } 最后选择:上传和部署:云端安装依赖。
2019-12-14 - 几行代码实现小程序云开发提现功能
先看效果: [图片] 纯云开发实现,下面说使用步骤: 一:开通商户的企业付款到领取功能 说明地址: https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_1 使用条件 1、商户号(或同主体其他非服务商商户号)已入驻90日 2、截止今日回推30天,商户号(或同主体其他非服务商商户号)连续不间断保持有交易 使用条件是第一难,第二难在下面这里 [图片] 在网上找了很多,感觉是云开发这里的一个不完善地方,如果不填ip,会报这种错 [代码]{"errorCode":1,"errorMessage":"user code exception caught","stackTrace":"NO_AUTH"} [代码] [代码]<xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[此IP地址不允许调用接口,如有需要请登录微信支付商户平台更改配置]]></return_msg> <mch_appid><![CDATA[wx383426ad9ffe1111]]></mch_appid> <mchid><![CDATA[1536511111]]></mchid> <result_code><![CDATA[FAIL]]></result_code> <err_code><![CDATA[NO_AUTH]]></err_code> <err_code_des><![CDATA[此IP地址不允许调用接口,如有需要请登录微信支付商户平台更改配置]]></err_code_des> </xml> [代码] 云开发没有ip这个概念,所以这里有些无从下手,不过这里我采用了个替代方案,参考了社区帖子: https://developers.weixin.qq.com/community/develop/doc/00088cff3a40d87d80f7267b65b800 之后我也亲自验证了,基本上就是这几个,当然肯定不够,但是可以自己在逻辑上进行处理,ip以下: [代码]172.81.207.12 172.81.212.74 172.81.236.99 172.81.235.12 172.81.245.51 212.64.65.131 212.64.84.22 212.64.85.35 212.64.85.139 212.64.87.134 [代码] 接着,可以动手了 二、云开发部分 1、设置云存储 证书配置地址: [图片] 下载后有三个文件,我们只需要p12结尾的那个 [图片] 然后,将这个apiclient_cert.p12文件上传到你的云存储 [图片] 这里处理完了,我们只需要一个东西,就是fileID也就是常说的云存储ID(上图红框内容) 2、配置云函数 新建云函数ref云函数 [图片] 代码如下: [代码]const config = { appid: 'wx383426ad9ffe1111', //小程序Appid envName: 'zf-shcud', // 小程序云开发环境ID mchid: '1111111111', //商户号 partnerKey: '1111111111111111111111', //此处填服务商密钥 pfx: '', //证书初始化 fileID: 'cloud://zf-shcud.11111111111111111/apiclient_cert.p12' //证书云存储id }; const cloud = require('wx-server-sdk') cloud.init({ env: config.envName }) const db = cloud.database(); const tenpay = require('tenpay'); //支付核心模块 exports.main = async(event, context) => { //首先获取证书文件 const res = await cloud.downloadFile({ fileID: config.fileID, }) config.pfx = res.fileContent let pay = new tenpay(config,true) let result = await pay.transfers({ //这部分参数含义参考https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2 partner_trade_no: 'bookreflect' + Date.now() + event.num, openid: event.userinfo._openid, check_name: 'NO_CHECK', amount: parseInt(event.num) * 100, desc: '二手书小程序提现', }); if (result.result_code == 'SUCCESS') { //如果提现成功后的操作 //以下是进行余额计算 let re=await db.collection('user').doc(event.userinfo._id).update({ data: { parse: event.userinfo.parse - parseInt(event.num) } }); return re } } [代码] 需安装的依赖:wx-server-sdk、tenpay 这里只是实现了简单原始的提现操作,关于提现后,比如防止重复提交,提现限额这些,在开源二手书商城上有完整流程,地址: https://github.com/xuhuai66/used-book-pro 这种办法,不是每次都能成功提现,小概率遇到ip未在白名单情况,还是希望,云开发团队能尽快出一个更好的解决方案吧
2019-09-21 - 关于textarea/input层级太高,有内样式或元素对不齐,问题解决
1、如果是textarea或者input层级太高 解决方法:给标签加上always-embed属性(基础库需要2.10.4以上)[图片] 如果兼容低版本基础库,需要使用cover-view写样式覆盖(标签文档:https://developers.weixin.qq.com/miniprogram/dev/component/cover-view.html) 2、如果发现textarea和其它元素需要一排对齐,但是ios有内样式,无法和安卓一样对齐 解决方法:给标签加上disable-default-padding属性(基础库需要2.10.0以上) [图片] 如果需要兼容低版本基础库,需要判断当前是否为ios系统,如果是不加任何padding,否则给textarea加上padding样式,padding: 18rpx 10rpx具体可以根据真机观察给大小。 写文章不容易,有帮助希望点下赞,程序员不难为程序员。
2021-04-27 - 小技巧!CSS 整块文本溢出省略特性探究
今天的文章很有意思,讲一讲整块文本溢出省略打点的一些有意思的细节。 文本超长打点 我们都知道,到今天(2020/03/06),CSS 提供了两种方式便于我们进行文本超长的打点省略。 感兴趣的小伙伴点击链接,了解详情~ http://github.crmeb.net/u/yi 对于单行文本,使用单行省略: { width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } [图片] 而对于多行文本的超长省略,使用 [代码]-webkit-line-clamp[代码] 相关属性,兼容性也已经非常好了: { width: 200px; overflow : hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } [图片] CodePen Demo -- inline-block 实现整块的溢出打点 问题一:超长文本整块省略 基于上述的超长打点省略方案之下,会有一些变化的需求。譬如,我们有如下结构: Sb Coco FEUIUX Designer前端工程师 [图片] 对于上述超出的情况,我们希望对于超出文本长度的整一块 -- 前端工程师,整体被省略。 如果我们直接使用上述的方案,使用如下的 CSS,结果会是这样,并非我们期待的整块省略: .person-card__desc { width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } [图片] 将 [代码]display: inline[代码] 改为 [代码]display: inline-block[代码] 实现整块省略 这里,如果我们需要实现一整块的省略,只需要将包裹整块标签元素的 [代码]span[代码] 的 [代码]display[代码] 由 [代码]inline[代码] 改为 [代码]inline-block[代码] 即可。 .person-card__desc span { display: inline-block; } [图片] 这样,就可以实现,基于整块的内容的溢出省略了。完整的 Demo,你可以戳这里: CodePen Demo - 整块超长溢出打点省略 问题二:iOS 不支持整块超长溢出打点省略 然而,上述方案并非完美的。经过实测,上述方案在 iOS 和 Safari 下,没能生效,表现为这样: [图片] 查看规范 - CSS Basic User Interface Module Level 3 - text-overflow,究其原因,在于 [代码]text-overflow[代码] 只能对内联元素进行打点省略。(Chrome 对此可能做了一些优化,所以上述非 iOS 和 Safari 的场景是正常的) 所以猜测是因为经过了 [代码]display: inline-block[代码] 的转化后,已经不再是严格意义上的内联元素了。 解决方案,使用多行省略替代单行省略 当然,这里经过试验后,发现还是有解的,我们在开头还提到了一种多行省略的方案,我们将多行省略的代码替换单行省略,只是行数 [代码]-webkit-line-clamp: 2[代码] 改成一行即可 [代码]-webkit-line-clamp: 1[代码]。 .person-card__desc { width: 200px; white-space: normal; overflow : hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; } .person-card__desc span { display: inline-block; } 这样,在 iOS/Safari 下也能完美实现整块的超长打点省略: [图片] CodePen Demo -- iOS 下的整块超长溢出打点省略方案 值得注意的是,在使用 [代码] -webkit-line-clamp[代码] 的方案的时候,一定要配合 [代码]white-space: normal[代码] 允许换行,而不是不换行。这一点,非常重要。 这样,我们就实现了全兼容的整块的超长打点省略了。 当然,[代码] -webkit-line-clamp[代码] 本身也是存在一定的兼容性问题的,实际使用的时候还需要具体去取舍。 最后 好了,本文到此结束,一个简单的 CSS 小技巧,希望对你有帮助 :) 感兴趣的小伙伴点击链接,了解详情~ http://github.crmeb.net/u/yi 作者:chokcoco
2021-03-15 - 腾讯位置服务实现轨迹回放
前言 在地图接入使用中,很多开发者咨询我们腾讯位置服务是否支持轨迹回放功能,所以今天特意将我们JavaScript API GL的轨迹回放&小车移动示例放到我们本篇文章分享。 轨迹回放&小车移动 在JavaScript API GL中,使用MultiMarker(点标记)中的moveAlong()方法 ,可以方便的实现轨迹回放功能,而且您可以对样式进行各种想要的修改,比如修改小车图片、不显示路线或者改成您想要的颜色等。 [图片] 代码 [代码]//初始化地图 var map = new TMap.Map("container", { zoom: 15, center: new TMap.LatLng(39.984104, 116.307503) }); //小车移动路线 var path = [ new TMap.LatLng(39.98481500648338, 116.30571126937866), new TMap.LatLng(39.982266575222155, 116.30596876144409), new TMap.LatLng(39.982348784165886, 116.3111400604248), new TMap.LatLng(39.978813710266024, 116.3111400604248), new TMap.LatLng(39.978813710266024, 116.31699800491333) ]; //创建mareker(小车) var marker = new TMap.MultiMarker({ map, styles: { //样式设置 'car-down': new TMap.MarkerStyle({ 'width': 40, //小车图片宽度(像素) 'height': 40, //高度 'anchor': { //图片中心的像素位置(小车会保持车头朝前,会以中心位置进行转向) x: 20,y: 20, }, 'faceTo': 'map', //取’map’让小车贴于地面,faceTo取值说明请见下文图示 'rotate': 180, //初始小车朝向(正北0度,顺时针一周为360度,180为正南) 'src': './img/car.png', //小车图片(图中小车车头向上,即正北0度) }) }, geometries: [{ //小车marker的位置信息 id: 'car', //因MultiMarker支持包含多个点标记,因此要给小车一个id styleId: 'car-down', //绑定样式 position: new TMap.LatLng(39.98481500648338, 116.30571126937866),//初始坐标位置 }] }); //调用moveAlong,实现小车移动 marker.moveAlong({ "car": { //设置让"car"沿"path"移动,速度70公里/小时 path, speed: 70 } }, { autoRotation:true //车头始终向前(沿路线自动旋转) } ) [代码] 在线示例:https://lbs.qq.com/webDemoCenter/glAPI/glMarker/markerMoveAlong 关于MultiMarker的faceTo说明: JavascriptAPI GL为可倾斜旋转的3D地图,这就带来了图片是贴在地面,还是贴向屏幕的问题: [代码]faceTo[代码]: “[代码]map[代码]” 贴在地面,轨迹回放场景,车是要贴地的(左图) [代码]faceTo[代码]:“[代码]screen[代码]” 贴在屏幕,小车场景就不合适了,它会始终“立着”(中图),"[代码]sreen[代码]"适合于标注位置使用(右图) [图片] 视角跟随小车移动(近期推出,敬请期待) 小车延路线运动的同时,控制视角跟随小车运动,可以达到类似模拟导航、第三人称游戏视角的感觉,非常炫酷。 [图片]
2020-10-16 - 微信小程序AR——商品引流
微信小程序AR [图片] 第一步、AR商品识别 扫一扫产品,扫一扫LOGO,扫一扫海报,扫一扫门店 基于AR图片识别技术,自己上传识别图,自定义识别操作 [图片] 第二步、交互式三维展示 360度产品展示、合拍分享到社交媒体 基于PBR物理渲染引擎,动画,支持手势缩放/拖拉/旋转操作 [图片] 第三步、虚拟试戴、虚拟化妆 逼真试戴体验、引流到在线商城 适合web/小程序的轻量深度神经网络、快速加载,对接微商城/微信小商店 「PLAY2XR眼镜试戴」 [图片] 「PLAY2XR口红试色」 [图片] 「PLAY2XR戒指试戴」 [图片] 扫码体验 [图片]
2020-10-20 - 小程序自定义时间轴实现分享
1、最近在开发小程序过程中遇到一个订单报修进度的需求,然后需要时间轴进行显示,实现完成之后感觉还阔以,来给大家分享交流一下,希望大家多多支持提出意见。 2、首先给出成品图。 2.1列表页 2.2 时间轴页 [图片] [图片] 3、时间轴页面代码 <view class="page"> <view class='weui-cell-third'> <view class="page__title"> <view class="gjimg"> <image class="position" mode="asceptFit" src="/pages/images/equrepair/gj.png" /> </view> <view class="repairinfo" > <view class="reminder" bindtap="toReminder" data-id="{{id}}" >催单</view> <view class="bxsb"> <text>报修设备:{{equipmentname}}</text> </view> <view class="sbxh"> <span>报修时间:{{applyRepairDate}}</span> </view> <view class="bxyy"> <span>报修原因:{{errorDescription}}</span> </view> </view> <view> </view> </view> <view class="fgx"></view> <view class="wecentBor"> <block wx:for="{{operationlog}}" wx:key="*this"> <view class='weui-cell-list'> <view class='weui-cell-line'> <view class="cimg"> <image class="checkimg" mode="asceptFit" src="/pages/images/equrepair/check.png" /> </view> <view class='weui-cell-time floarLeft'>{{item.operationtimeX}}</view> <view class='weui-cell-event floarLeft'>{{item.taskstatusX}}</view> </view> </view> </block> </view> </view> </view> 4、时间轴页面wxss代码 page { height: 98%; background-color: #f5f5f5; } .weui-cell-third { min-height: 100%; background-color: #fff; margin: 3% 3% 0px 3%; } .page__title { padding: 16rpx; background-color: #ffffff; margin-bottom: 10rpx; } .page__title>view:first-child { text-align: center; font-size: 35rpx; color: #4da2fd; font-weight: 700; } .page__title>view:last-child { display: flex; justify-content: center; align-items: center; color: #999999; } .weui-cell-list { overflow: hidden; padding-left: 30rpx; padding-top: 20rpx; } .wecentBor { padding-top: 30rpx; background: #fff; } .weui-cell-line { margin-top: -19rpx; margin-left: 12px; float: left; border-left: 1px solid #4da2fd; min-height: 140rpx; margin-bottom: -1px; position: relative; } .weui-cell-list .cimg { position: absolute; left: -30rpx; /* top: 3.5rpx; */ background-color: #ffffff; border: 0; display: flex; justify-content: center; align-items: center; } .checkimg { width: 60rpx; height: 60rpx; z-index: 100; padding-top: 8rpx; padding-bottom: 8rpx; } .weui-cell-time { float: left; font-size: 14px; padding-left: 65rpx; width: 100%; color: #999999; } .weui-cell-event { padding-left: 65rpx; padding-bottom: 30rpx; font-size: 14px; color: #787878; } .weui-cell-list:last-child .weui-cell-line { border: 0; } .gjimg { float: left; padding-top: 8%; } .position { height: 40px; width: 40px; display: flex; justify-content: center; } .repairinfo { padding-left: 16%; padding-top: 10rpx; } .repairinfo .bxsb { width: 80%; font-size: 15px; font-weight: 550; } .reminder { font-size: 14px; border: 1px solid #3699ff; padding: 2px 15px; border-radius: 15px; color: #e7f2fe; background-color: #3699ff; z-index: 999; position: absolute; left: 78%; } .repairinfo .sbxh { font-size: 13px; color: #6d6d6d; padding-top: 6rpx; } .repairinfo .bxyy { min-height: 50px; font-size: 13px; color: #6d6d6d; /*设置多行超出进行隐藏*/ display: -webkit-box; word-break: break-all; text-overflow: ellipsis; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 3; /*设置超出三行进行隐藏*/ } .fgx { border: 0.5px solid #f1f1f1; }
2020-09-24 - 使用小程序css3实现帧动画组件
使用场景: UI和产品经常需要在小程序使用动画功能,有些动画又很复杂,如果直接使用gif会因为图片太大导致小程序加载慢,并占用太多内存。 实现核心: css3动画animation的steps功能、css变量的应用。 动画效果: [图片] 组件代码: https://developers.weixin.qq.com/s/4x3Z9mmY77kl 温馨提示: 不定期更新文章,有需要请关注哈!
2020-09-14 - vue接入腾讯地图(二)【标注&定位实战】
vue接入腾讯地图(一)【点击事件】请参考:https://developers.weixin.qq.com/community/develop/article/doc/000260cddccd60d91a3bdc67056813 1、【标注】 添加标注 [代码]var marker = new qq.maps.Marker({ position: myLatlng , map: map }); [代码] 文本标注 [代码]var marker = new qq.maps.Label({ position: myLatlng, map: map, content:'文本标注' }); [代码] 效果 [图片] 自定义标注图标 [代码] var anchor = new qq.maps.Point(300, 0), size = new qq.maps.Size(600, 680), origin = new qq.maps.Point(-150, 0), markerIcon = new qq.maps.MarkerImage( "/static/images/position.png", size, origin, anchor ); marker.setIcon(markerIcon); [代码] 预览 [图片] 定位logo [图片] 官网预览https://lbs.qq.com/javascript_v2/case-run.html#sample-overlay-addmarkerimage [图片] 为什么会有这么大区别呢,因为这是图片的问题一个大一个小,还需要稍加调整,展示官网定位图片 [图片] 2、【定位实战→qq.maps.Geolocation】 引入js包(注意:vue项目得在首页的index.html里面引入) [代码]<script charset="utf-8" src="https://map.qq.com/api/js?v=2.exp&key=yourkey"></script> //这个是固定的用这个链接就可以 <script src="https://3gimg.qq.com/lightmap/components/geolocation/geolocation.min.js"></script> [代码] 三步走 [代码]<template> <div id="container" style="width:600px;height:500px;"></div> </template> <script> export default{ name:'news', data() { return { longitude:0,//经度 latitude:0,//纬度 city:'' } }, methods:{ //第一部分 //定位获得当前位置信息 getMyLocation() { var geolocation = new qq.maps.Geolocation("yourkey", "yourkey名称"); geolocation.getIpLocation(this.showPosition, this.showErr); //geolocation.getLocation(this.showPosition, this.showErr);//或者用getLocation精确度比较高 }, showPosition(position) { console.log(position); this.latitude = position.lat; this.longitude = position.lng; this.city = position.city; this.setMap(); }, showErr() { console.log("定位失败"); this.getMyLocation();//定位失败再请求定位,测试使用 }, //第二部分 //位置信息在地图上展示 setMap() { //步骤:定义map变量 调用 qq.maps.Map() 构造函数 获取地图显示容器 //设置地图中心点 var myLatlng = new qq.maps.LatLng(this.latitude,this.longitude); //定义工厂模式函数 var myOptions = { zoom: 13, //设置地图缩放级别 center: myLatlng, //设置中心点样式 mapTypeId: qq.maps.MapTypeId.ROADMAP //设置地图样式详情参见MapType } // //获取dom元素添加地图信息 var map = new qq.maps.Map(document.getElementById("container"), myOptions); //第三部分 //给定位的位置添加图片标注 var marker = new qq.maps.Marker({ position: myLatlng, map: map }); //给定位的位置添加文本标注 var marker = new qq.maps.Label({ position: myLatlng, map: map, content:'这是我当前的位置,偏差有点大,哈哈' }); } }, mounted() { this.getMyLocation(); } } </script> [代码] [图片] 开发者用的时候可以一步一步去实现,保证每一步都实现后再进行下一步 [图片] [图片] 申请key地址 https://lbs.qq.com/console/mykey.html 官方文档 https://lbs.qq.com/tool/component-geolocation.html https://lbs.qq.com/javascript_v2/guide-start.html 以上内容转载自前端来入坑的文章《vue使用腾讯地图(三)标注》 链接:https://www.jianshu.com/p/eca4f20ee1cb 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2020-11-02