- 使用button组件客服contact功能,为什么用其他微信号扫描二维码进行真机测试不成功?
小程序没有上线发布,用的是真实AppID.第一个微信号是用来开发小程序的管理员账号,第二个普通微信号作为小程序的用户对使用button组件客服contact功能进行真机测试,扫描二维码之后,微信开发者工具上面的真机测试二维码没有任何变化,一直在等待设备,而第二个普通微信号扫描真机测试二维码后则是一直白屏,将第二个普通微信号添加到体验成员也没有用?这是为什么?
2020-09-25 - navigateTo with an already exist webviewId 26?
使用navigator页面实现跳转,但是微信开发者工具报错? 如图[图片]
2020-09-25 - 小程序富文本能力的深入研究与应用
前言 在开发小程序的过程中,很多时候会需要使用富文本内容,然而现有的方案都有着或多或少的缺陷,如何更好的显示富文本将是一个值得继续探索的问题。 [图片] 现有方案 WxParse [代码]WxParse[代码] 作为一个应用最为应用最广泛的富文本插件,在很多时候是大家的首选,但其也明显的存在许多问题。 格式不正确时标签会被原样显示 很多人可能都见到过这种情况,当标签里的内容出现格式上的错误(如冒号不匹配等),在[代码]WxParse[代码]中都会被认为是文本内容而原样输出,例如:[代码]<span style="font-family:"宋体"">Hello World!</span> [代码] 这是由于[代码]WxParse[代码]的解析脚本中,是通过正则匹配的方式进行解析,一旦格式不正确,就将导致无法匹配而被直接认为是文本[代码]//WxParse的匹配模式 var startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/, endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/, attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g; [代码] 然而,[代码]html[代码] 对于格式的要求并不严格,一些诸如冒号不匹配之类的问题是可以被浏览器接受的,因此需要在解析脚本的层面上提高容错性。 超过限定层数时无法显示 这也是一个让许多人十分苦恼的问题,[代码]WxParse[代码] 通过 [代码]template[代码] 迭代的方式进行显示,当节点的层数大于设定的 [代码]template[代码] 数时就会无法显示,自行增加过多的层数又会大大增加空间大小,因此对于 [代码]wxml[代码] 的渲染方式也需要改进。 对于表格、列表等复杂内容支持性差 [代码]WxParse[代码] 对于 [代码]table[代码]、[代码]ol[代码]、[代码]ul[代码] 等支持性较差,类似于表格单元格合并,有序列表,多层列表等都无法渲染 rich-text [代码]rich-text[代码] 组件作为官方的富文本组件,也是很多人选择的方案,但也存在着一些不足之处 一些常用标签不支持 [代码]rich-text[代码] 支持的标签较少,一些常用的标签(比如 [代码]section[代码])等都不支持,导致其很难直接用于显示富文本内容 ps:最新的 2.7.1 基础库已经增加支持了许多语义化标签,但还是要考虑低版本兼容问题 不能实现图片和链接的点击 [代码]rich-text[代码] 组件中会屏蔽所有结点事件,这导致无法实现图片点击预览,链接点击效果等操作,较影响体验 不支持音视频 音频和视频作为富文本的重要内容,在 [代码]rich-text[代码] 中却不被支持,这也严重影响了使用体验 共同问题 不支持解析 [代码]style[代码] 标签 现有的方案中都不支持对 [代码]style[代码] 标签中的内容进行解析和匹配,这将导致一些标签样式的不正确 [图片] 方案构建 因此要解决上述问题,就得构建一个新的方案来实现 渲染方式 对于该节点下没有图片、视频、链接等的,直接使用 [代码]rich-text[代码] 显示(可以减少标签数,提高渲染效果),否则则继续进行深入迭代,例如: [图片] 对于迭代的方式,有以下两种方案: 方案一 像 [代码]WxParse[代码] 那样通过 [代码]template[代码] 进行迭代,对于小于 20 层的内容,通过 [代码]template[代码] 迭代的方式进行显示,超过 20 层时,用 [代码]rich-text[代码] 组件兜底,避免无法显示,这也是一开始采用的方案[代码]<!--超过20层直接使用rich-text--> <template name='rich-text-floor20'> <block wx:for='{{nodes}}' wx:key> <rich-text nodes="{{item}}" /> </block> </template> [代码] 方案二 添加一个辅助组件 [代码]trees[代码],通过组件递归的方式显示,该方式实现了没有层数的限制,且避免了多个重复性的 [代码]template[代码] 占用空间,也是最终采取的方案[代码]<!--继续递归--> <trees wx:else id="node" class="{{item.name}}" style="{{item.attrs.style}}" nodes="{{item.children}}" controls="{{controls}}" /> [代码] 解析脚本 从 [代码]htmlparser2[代码] 包进行改写,其通过状态机的方式取代了正则匹配,有效的解决了容错性问题,且大大提升了解析效率 [代码]//不同状态各通过一个函数进行判断和状态跳转 for (; this._index < this._buffer.length; this._index++) this[this._state](this._buffer[this._index]); [代码] 兼容 [代码]rich-text[代码] 为了解析结果能同时在 [代码]rich-text[代码] 组件上显示,需要对一些 [代码]rich-text[代码]不支持的组件进行转换[代码]//以u标签为例 case 'u': name = 'span'; attrs.style = 'text-decoration:underline;' + attrs.style; break; [代码] 适配渲染需要 在渲染过程中,需要对节点下含有图片、视频、链接等不能由 [代码]rich-text[代码]直接显示的节点继续迭代,否则直接使用 [代码]rich-text[代码] 组件显示;因此需要在解析过程中进行标记,遇到 [代码]img[代码]、[代码]video[代码]、[代码]a[代码] 等标签时,对其所有上级节点设置一个 [代码]continue[代码] 属性用于区分[代码]case 'a': attrs.style = 'color:#366092;display:inline;word-break:break-all;overflow:auto;' + attrs.style; element.continue = true; //冒泡:对上级节点设置continue属性 this._bubbling(); break; [代码] 处理style标签 解析方式 方案一 正则匹配[代码]var classes = style.match(/[^\{\}]+?\{([^\{\}]*?({[\s\S]*?})*)*?\}/g); [代码] 缺陷: 当 [代码]style[代码] 字符串较长时,可能出现栈溢出的问题 对于一些复杂的情况,可能出现匹配失败的问题 方案二 状态机的方式,类似于 [代码]html[代码] 字符串的处理方式,对于 [代码]css[代码] 的规则进行了调整和适配,也是目前采取的方案 匹配方式 方案一 将 [代码]style[代码] 标签解析为一个形如 [代码]{key:content}[代码] 的结构体,对于每个标签,通过访问结构体的相应属性即可知晓是否匹配成功[代码]if (this._style[name]) attrs.style += (';' + this._style[name]); if (this._style['.' + attrs.class]) attrs.style += (';' + this._style['.' + attrs.class]); if (this._style['#' + attrs.id]) attrs.style += (';' + this._style['#' + attrs.id]); [代码] 优点:匹配效率高,适合前端对于时间和空间的要求 缺点:对于多层选择器等复杂情况无法处理 因此在前端组件包中采取的是这种方式进行匹配 方案二 将 [代码]style[代码] 标签解析为一个数组,每个元素是形如 [代码]{key,list,content,index}[代码] 的结构体,主要用于多层选择器的匹配,内置了一个数组 [代码]list[代码] 存储各个层级的选择器,[代码]index[代码] 用于记录当前的层数,匹配成功时,[代码]index++[代码],匹配成功的标签出栈时,[代码]index--[代码];通过这样的方式可以匹配多层选择器等更加复杂的情况,但匹配过程比起方案一要复杂的多。 [图片] 遇到的问题 [代码]rich-text[代码] 组件整体的显示问题 在显示过程中,需要把 [代码]rich-text[代码] 作为整体的一部分,在一些情况下会出现问题,例如: [代码]Hello <rich-text nodes="<div style='display:inline-block'>World!</div>"/> [代码] 在这种情况下,虽然对 [代码]rich-text[代码] 中的顶层 [代码]div[代码] 设置了 [代码]display:inline-block[代码],但没有对 [代码]rich-text[代码] 本身进行设置的情况下,无法实现行内元素的效果,类似的还有 [代码]float[代码]、[代码]width[代码](设置为百分比时)等情况 解决方案 方案一 用一个 [代码]view[代码] 包裹在 [代码]rich-text[代码] 外面,替代最外层的标签[代码]<view style="{{item.attrs.style}}"> <rich-text nodes="{{item.children}}" /> </view> [代码] 缺陷:当该标签为 [代码]table[代码]、[代码]ol[代码] 等功能性标签时,会导致错误 方案二 对 [代码]rich-text[代码] 组件使用最外层标签的样式[代码]<rich-text nodes="{{item}}" style="{{item.attrs.style}}" /> [代码] 缺陷:当该标签的 [代码]style[代码] 中含有 [代码]margin[代码]、[代码]padding[代码] 等内容时会被缩进两次 方案三 通过 [代码]wxs[代码] 脚本将顶层标签的 [代码]display[代码]、[代码]float[代码]、[代码]width[代码] 等样式提取出来放在 [代码]rich-text[代码] 组件的 [代码]style[代码] 中,最终解决了这个问题[代码]var res = ""; var reg = getRegExp("float\s*:\s*[^;]*", "i"); if (reg.test(style)) res += reg.exec(style)[0]; reg = getRegExp("display\s*:\s*([^;]*)", "i"); if (reg.test(style)) { var info = reg.exec(style); res += (';' + info[0]); display = info[1]; } else res += (';display:' + display); reg = getRegExp("[^;\s]*width\s*:\s*[^;]*", "ig"); var width = reg.exec(style); while (width) { res += (';' + width[0]); width = reg.exec(style); } return res; [代码] 图片显示的问题 在 [代码]html[代码] 中,若 [代码]img[代码] 标签没有设置宽高,则会按照原大小显示;设置了宽或高,则按比例进行缩放;同时设置了宽高,则按设置的宽高进行显示;在小程序中,若通过 [代码]image[代码] 组件模拟,需要通过 [代码]bindload[代码] 来获取图片宽高,再进行 [代码]setData[代码],当图片数量较大时,会大大降低性能;另外,许多图片的宽度会超出屏幕宽度,需要加以限制 解决方案 用 [代码]rich-text[代码] 中的 [代码]img[代码] 替代 [代码]image[代码] 组件,实现更加贴近 [代码]html[代码] 的方式 ;对 [代码]img[代码] 组件设置默认的效果 [代码]max-width:100%;[代码] 视频显示的问题 当一个页面出现过多的视频时,同时进行加载可能导致页面卡死 解决方案 在解析过程中进行计数,若视频数量超过3个,则用一个 [代码]wxss[代码] 绘制的图片替代 [代码]video[代码] 组件,当受到点击时,再切换到 [代码]video[代码] 组件并设置 [代码]autoplay[代码] 以模拟正常效果,实现了一个类似懒加载的功能 [代码]<!--视频--> <view wx:if="{{item.attrs.id>'media3'&&!controls[item.attrs.id].play}}" class="video" data-id="{{item.attrs.id}}" bindtap="_loadVideo"> <view class="triangle_border_right"></view> </view> <video wx:else src='{{controls[item.attrs.id]?item.attrs.source[controls[item.attrs.id].index]:item.attrs.src}}' id="{{item.attrs.id}}" autoplay="{{item.attrs.autoplay||controls[item.attrs.id].play}}" /> [代码] 文本复制的问题 小程序中只有 [代码]text[代码] 组件可以通过设置 [代码]selectable[代码] 属性来实现长按复制,在富文本组件中实现这一功能就存在困难 解决方案 在顶层标签上加上 [代码]user-select:text;-webkit-user-select[代码] [图片] 实现更加丰富的功能 在此基础上,还可以实现更多有用的功能 自动设置页面标题 在浏览器中,会将 [代码]title[代码] 标签中的内容设置到页面标题上,在小程序中也同样可以实现这样的功能[代码]if (res.title) { wx.setNavigationBarTitle({ title: res.title }) } [代码] 多资源加载 由于平台差异,一些媒体文件在不同平台可能兼容性有差异,在浏览器中,可以通过 [代码]source[代码] 标签设置多个源,当一个源加载失败时,用下一个源进行加载和播放,在本插件中同样可以实现这样的功能[代码]errorEvent(e) { //尝试加载其他源 if (!this.data.controls[e.currentTarget.dataset.id] && e.currentTarget.dataset.source.length > 1) { this.data.controls[e.currentTarget.dataset.id] = { play: false, index: 1 } } else if (this.data.controls[e.currentTarget.dataset.id] && e.currentTarget.dataset.source.length > (this.data.controls[e.currentTarget.dataset.id].index + 1)) { this.data.controls[e.currentTarget.dataset.id].index++; } this.setData({ controls: this.data.controls }) this.triggerEvent('error', { target: e.currentTarget, message: e.detail.errMsg }, { bubbles: true, composed: true }); }, [代码] 添加加载提示 可以在组件的插槽中放入加载提示信息或动画,在加载完成后会将 [代码]slot[代码] 的内容渐隐,将富文本内容渐显,提高用户体验,避免在加载过程中一片空白。 最终效果 经过一个多月的改进,目前实现了这个功能丰富,无层数限制,渲染效果好,轻量化(30.0KB),效率高,前后端通用的富文本插件,体验小程序的用户数已经突破1k啦,欢迎使用和体验 [图片] github 地址 npm 地址 总结 以上就是我在开发这样一个富文本插件的过程大致介绍,希望对大家有所帮助;本人在校学生,水平所限,不足之处欢迎提意见啦! [图片]
2020-12-27 - 微信小程序云开发教程-WXML入门-常用组件和属性
同学们大家好,我是小伊同学,今天继续学习WXML部分,我们来介绍一下常用的组件和属性。 在上一节教程中,我们已经知道了什么是组件什么是属性。通常情况下,组件和属性都是配合使用,同时出现。 [图片] 组件和属性一起使用的基本格式在我们上一小节讲到的结构中添加了属性名称等于属性值的结构。这样的结构根据我们所需要的效果可以写很多,相互之间是平级的,共同起作用。 需要注意的是所有组件与属性都是小写,以连字符-连接,每一种组件和每一种属性都代表一种“独特”的功能。 在我们进行小程序开发的过程中,我们编写wxml的步骤主要就是两步: Step1:选择合适的组件Step2:为组件配备相应的属性 下面我们来介绍一下组件的通用属性,包括id、class、style、hidden、data-*和bind*。 [图片] 其中我们最常用的主要是前三个和第五个。具体的使用方法同学们可以查阅微信开发文档,也可以观看我们的视频教程,我们将在实操部分具体为大家讲解该怎么使用。 下面,我们来介绍一些我们后面实操会用到的几个基本组件: [图片] View是小程序中最常用的组件,主要功能就是组件容器,大家可以把他近似理解成一个盒子,将其他组件放到这个盒子中,再用几个盒子组成这整个页面,这样能够使得页面更加整齐有序。 同时,在盒子的内部,我也可以很方便地设置一定的规律顺序排列组件,这样的设置不会影响到其他组件。同时,class指定的样式会作用于view内部的组件或文本内容。 Text组件主要用于展示文字文本,是一种基础组件。我们可以在这个组件的起止标签内部书写文字,当然我们直接把文字放在View中也是没有问题的。 [图片] 媒体组件中,我们常用的就是image组件了,通过src属性,我们可以设置图片位置,使得程序页面上加载出图片来。 [图片] Button组件顾名思义就表示按钮啦,通过这一组件,我们可以实现在页面上添加按钮。 [图片] Input组件是页面出现输入框,程序可以获取到用户输入。 当然,其实还有很多组件,同学们可以通过微信开发者文档进行自学。 [图片] 打开微信开发者文档,选择组件标签,左侧列出了所有组件,点击后右侧会显示该组件的专用属性及说明,里面还会有示例代码供大家参考学习。
2020-08-21 - 未能保存" app.json": 文件的内容较新 请将你的版本与此文件内容进行比较?
未能保存" app.json": 文件的内容较新 请将你的版本与此文件内容进行比较 是什么意思?如图[图片] [图片]
2020-09-24 - 微信小程序为啥成了很多人手上一块“弃之可惜,食之无味的鸡肋”?又怎样让它变成宝?
你好!尊敬的腾讯官方客服:无诚信开发服务商欺骗微信小程序用户已在严重制约着小程序的健康发展,究竟由谁来监督谁来管? 咱从2018年就想定制或购买一款微信小程序创业。先后近一年内咱花钱花时间用心体验了三家微信小程序,三家小程序系统各有各的优势,也各有各的短板,至今咱仍没有找到能够让咱确定能真正放心去投入运营的小程序,为什么?因为咱委托微信小程序第三方开发服务商所定制开发上线的小程序以及后期的客服服务态度越来越让咱感觉象是被坑骗了;例如其中的:广东与众不同信息科技有限公司为咱这(本地便民商圈)微信小程序所定制开发的多商户入驻商城版小程序架构根本就没有能进行实际运营的功能;商城运营者(圈主)居然没有手机端后台管理操作面版,商城运营主和入驻商户申请开通的店铺虽然有手机端后台管理操作面板,但是也根本没有能够进行实际运营管理的微信小程序店铺该有的营销操作功能,到访本地便民商圈的微信用户网友打开已入驻商圈商户店铺中的商品分类后居然没有商品显示,所显示的分类也莫名其妙的居然不是店主本人所设置的分类;咱点开家人微信号在便民商圈小程开通的店铺点击分类按钮也有测试过,并且点击全部商品的按钮后也显示不了所有商品;至今包括咱自营店铺和入驻商户店铺手机端和电脑端居然都没有上传轮播图的设置,店主和普通微信客户打开店铺居然没有主页功能模块,店铺后台也是有名而无实际营销功能。 例如: 一、店铺管理中心 1、店铺管理中心:没有线上客户导航到店自提货地址信息库录入,也没有客户退换货门店收件地址库录入; 2、店铺管理中心:没有导航到店自提货时段设置功能,没有门店员工或同城骑手派送的自定义设置和默认设置功能,没有物流快递承运自定义设置和默认设置; 二、商品管理中心: 1、商品管理中心:最关键的商品编辑发布和商品编辑修改功能居然也没有,因此店铺商品价格和库存量就无法进行修改调整;例如,库存除了客户线上下单后到线下门店自提的数据门店系统能够实时扣减库存量,但是门店内线下销量在线仓库存量是不能及时自动扣减的,因此店主就需要及时修改调整库存量或价格,以免造成不必要的超量下单产生的销售麻烦;而连这个微信小程序店铺最基础的商品管理功能操作面板都是一个有其名无其实的摆设,只能对在售商品进行删除与下架和再上架的操作,这叫什么商品管理系统?小程就是这样的赋能吗?还是你们故意扣着以此要挟咱们再另行付费?? 2、商品管理中心:没有拼团、秒杀和优惠券等营销工具,这样的微信小程序店铺如何真正赋能有效运营起来? 三、商户店铺主页图片轮播功能和底部导航栏居然至今仍然没有上线动作,更别谈视频营销、直播营销这些功能了;在当今线上店铺如果没有视频内容介绍,产品没更直观的视频说明,店主又如何能有效运营线上店铺?而且这入驻商户他们的客户如果进入线下门店消费时打开(本地便民商圈)微信小程序个人中心中的“不用扫码当面支付”功能给店主当面微信支付货款后,店主店铺中这单记录不但没有,而且资产余额中也没有这项进帐;【这就是广东与众不同信息科技有限公司为咱开发交付给咱并已持续一年多维护更新服务的商城微信小程序】 四、电脑PC端商品编辑发布界面至今也没有发货方式勾选入口。因此,针对不同商品、不同区域和差异化的营销发货或自提方式无法灵活运用,那么微信小程序店铺最关键的线下实体店社区营销服务就无法顺畅运行,而且商品编辑发布功能在手机端至今仍然没有嫁接上线;(针对咱提出的这些基础功能咱要求开发服务商广东与众不同信息科技有限公司尽快更新完善时而客服居然回复对咱说:“这边技术产品看了您的需求,目前并不是大多数用户所需要的,暂时还不更新的”);这样类似推诿已有多次,而他们这样的说法就根本上不是大部分客户需要不需要的问题,而是这些基础功能傻子都能看得出来,是每个商家店铺运营都需要的功能,而他们却自欺欺人的昧着良心对待咱们这些微信小程序推广运营者们; [图片] 尊敬的腾讯官方客服:象这样不诚信的开发服务商已严重制约着微信小程序的健康发展,究竟由谁来监督谁来管? [图片] 咱一直被这样坑骗着而无法能真正运营,店铺根本就没有微信小程序店铺该有的营销赋能 因此,今年的微信公众号也还没有心思去做认证 [图片] [图片] 尊敬的腾讯官方客服:广东与众不同信息科技有限公司给咱这便民商圈微信小程序圈主运营者系统后台也没有可为入驻商户店铺开通相关营销功能的操作,那么谁又愿意和咱一起来参与微信小程序运营呢?又怎么能尽快让更多人用上微信小程序赋能更多实体店呢?以上一些问题咱一年多来发现所谓的开发服务商的业务对接人员和客服人员前后态度差距太大,咱遇到这些需要调整或完善的地方他们根本没有诚意给你及时解决,交付给的商城小程序店铺营销能力根本不是他们之前所描述的那样;咱亲朋好友入驻店铺运营能力与咱圈主自营店铺营销功能差别太大,咱系统后台仍没有为他们开通相关营销工具的操作,他们店铺根本就没有微信小程序店铺线上应有的运营能力;因此,咱再好的朋友谁又愿意参与运营使用呢?如果就咱个人小程序自营单店运营又能又多少资源起步发展运行微信小程序呢?何况咱定制开发的也不是单店小程序;他们如此的不守诚信当初交付给咱的商城小程序根本就没达到当初对接时所描述的样子,而咱等待了一年多他们根本就没有什么维护更新优化,至今仍没有达到一个商城商圈微信小程序应有的营销能力,而淘宝、京东、拼多那么成熟的平台咱不上去,人家为什么要花钱、花时间和精力来投入微信小程序运营?因为咱们看中的是微信小程它所具有的真实能力。。。作为开发服务商请看一下当初你们业务员是对咱们如何描述和承诺的,难道都忘了吗?请你们真诚的对待把信任交给你们的微信用户吧!! 每人的忍耐都是有底线的。。。 咱们把身份证、营业执照、个人资料、甚至是银行卡这些资料全都信任的提供给了你们,你们好意思借微信小程序第三方开发服务商的名头唬弄咱们吗?唬弄咱一年多,使咱(本地便民商圈)微信小程序一直不能真正对外推广运营,而且前不久在5月份还以关停咱小程序系统后台管理登录来要挟咱续费让他们继续为咱提供这有其名无其实的维护更新服务,这是什么强盗逻辑!! [图片] 这上面是他们与咱对接开发时的承诺,而后来实际上给咱的产品和服务根本就不相符 [图片] 这上面是他们与咱对接开发时的承诺,而后来实际上给咱的产品和服务根本就不相符,如果迟迟不能更新达到这小程序该有的功能,那么他们这完全就是在借微信小程序开发服务商之名欺诈微信用户 。[图片] 这上面截图是2020年5月27日小程序客服与咱的对话 [图片] [图片] [图片] 这上方截图是咱首次把在社区这发表的准备投诉内容转发给他们后,当时感觉小程序客服似乎态度有些好转,但是又经过咱这一个多月的耐心等待感觉他们仍然是没有诚意为咱这小程序处理问题,而且似乎这一个多月来他们又可能公关了腾讯方面的相关人脉,现在却变得更加肆无忌惮了。。。 [图片][图片] [图片] [图片] [图片] [图片] 这上面是2020年6月15日他们对咱的态度,而下面这张是他们与咱达成交易前的承诺,这活脱脱的暴露了他们的欺诈行为 [图片] 这是他们与咱对接开发时的承诺,而实际上给咱的产品和后来服务根本就不相符 这不是欺诈交易吗?而且消耗了咱一年的时间等待和精力,如今小程序仍然是这样 如何让更多普通实体商户参与微信小程序运营,让更多民众使用微信支付,让微信小程序真正成为人们手中的宝? 首先,应意识到在当今互联网竟争异常激烈的形势下微信生态治理应该是迫在眉睫!! 尊敬的腾讯官方客服:在当今现实中一些微信小程序开发服务商,例如,先前对咱们承诺保证后期免费为咱小程序不断优化更新升级,确保咱微信息小程序处于最新最佳运行状态,但是后来经咱们不断验证后他们当初的所谓承诺保证事实上已经成为了一个欺诈事实;咱们付费开发上线后的微信小程序在遇到一些问题后向开发服务商客服反馈时,他们先前是与咱们打哈哈绕湾湾或者否定你的建议和要求,最后甚至是爱理不搭闪人;他们开始都是把自己的小程序和服务讲的多么多么的牛,事实上很多功能并没有达到那个水准,而又很少主动与别人家的小程序进行比较进行相互取长补短,来站在小程序运营者和店主的角度去完善更新后台和店铺运营操作能力,他们平台中也没有用户反馈改进建议的官方路径渠道,又如何更新打造出适合用户实际需求的应用小程序来?系统技术维护和开发人员不能随时获得真实的用户反馈和建议,开发服务商又怎么可打磨出受欢迎的微信小程序来? 而一些所谓的小程序客服和售后所建立的微信群大多数也实际上只是一个小程序推销叫卖群而已,他们根本无动力积极有效的听取用户所反馈的问题和建议,所谓客服和最高级代理在他们各自的群里也都只注重怎么把手上的这小程序象“车子”一样批发给代理商,代理商也只追求如何把“车”批给分销商,而不注重“分销商”和最终的运营用户以及消费者的客户体验,不注重把关“车子”质量的好坏,不追求“车子”性能的不断完善升级和维护,那么这个品牌又怎么可能有长久的竞争优势!!一味的只注重售卖小程序而不注重付费运营小程序用户的产出效益,不注重最终商户店铺消费者的便捷体验度,那么又有多少人愿意购买或定制小程序呢?即使稀里糊涂定制或购买了,又有多少人愿意再继续傻傻的投入更多的时间和精力去运营微信小程序呢? 难道就没有更好的方式让微信小程序开发服务商和小程序运营者们产生共赢的模式,让微信小程序更好更快的赋能更多商户和企业了吗? 首先要明白其实定制或购买来的小程序再好,它本身也只是一个空架而已,没有人投入运营是不可能产生收益的;就比如租赁或定制购买了一辆“客车”,如果不投入财力和人力运营上路跑起来,把车放在家里是不会产生收益的,客户也不会去你家里上车子坐一下就给你钱,小程序运营道理也一样,何况它开始还只是一个虚拟的“客车”;而且这个“客车”如果是付费租赁来的,即使“车”主或制造商也已承诺三包服务随时可为“客车”更换新的零部件以确保车辆运行处于最佳的状态,但这开发后的微信小程这部“车”“钥匙”却是一把远程遥控“钥匙”并且是牢牢的掌控在车主或“制造商”手里,同时这个“车”的每一个部件和功能其开发“制造商”都是可通过远程编码遥控操作的,他可以随时更新功能也可以随时给你关闭某一个系统功能或者远程给你拆掉某个关键零部件,想象一下这样的“车子”你敢再继续使用下去吗? 因此,想要让更多微信用户能够放心踏实参与微信小程序推广运营并使用起来,那微信开发服务商这些“制造商”或“租赁公司”必须要把自己的租赁与维护收益和小程序运营者们的收益挂钩,咱们推广运营者们才能放心参与;更何况目前世面上所谓定制开发购买的小程序大多数其实就是租赁性质;为什么这么说,因为只要系统更新升级是依赖开发服务商的并且每年要续费的实质上就是租赁。因此,象这样以“出售”而实质是“租赁”小程序的开发服务商,其动机就是不诚实的;开发服务商当初讲小程序后期所有维护以及新功能都免费更新升级,但是有些开发服务商虽然小程序系统功能后期做得很好,但是依然还是在走着一些电商平台卖营销工具的套路,所谓的免费更新维护却了一个谎子;即使现在有人稀里糊涂定制或购买了,又有多少人愿意再继续傻傻的投入更多的时间和精力去运营微信小程序呢? 就目前微信小程序生态发展中所存在的系列严重问题,如果腾讯不尽快的加以改革,那么这些开发服务商就会制约微信小程序的快速健康发展,而后续负面影响也会越来越严重,同样也会制约开发服务商自身业务的长远发展,因此,微信小程序开发服务商也要主动积极配合改革,把微信小程序开发后期更新维护服务费与小程序运者们的收益挂钩,小程序运营管理者、小小程序商户如没有产生收益,开发服务商就没有服务收入,这样才能倒逼服务商们升级打造出越来越好的微信小程序营销能力,就能快速产生社会效益;开发服务商的服务费还必须得由微信官方从运营者微信支付商户号钱包中为他们代收,这样才能有效保障微信小程序生态健康快速的发展,才能让更多人喜欢上微信小程序。但是,咱经过一年的体验发现大部分小程序开发服务商的业务员、程序员和售后服务人员都存在收钱后或小程序交付后就会出现爱理不搭极不诚信的服务表现,让很多个人中小投资者准备投入运营微信小程序过程中产生忧虑而不敢继续实质性的投入运营,而造成目前微信小程序却成了很多人手上一块“弃之可惜,食之无味的鸡肋”,因此微信官方想要尽快改变现状:1、必须尽快改革服务费收取方式才能有效规避上面的现象发生;2、辟开所谓的第三方开发服务商,直接以腾讯官方微信小程序团队为主体开发出一个通用的微信电商小程序系统发布上线,增加公信力;前期通过免费开放多城市多商户入驻型微信小程序商圈模板,组建首批微信电商小程序启动运营合伙人机制,就能将微信电商小程序迅速裂变式普及推广打开市场; 咱注册的两个小程序虽然以所谓的定制委托微信小程序服务商开发了,但至今咱也仍没有最终确定选用哪一家为咱开发的小程序,最有可能尽快优化完善至是咱们能够真正放心去运营的微信小程序;咱期待着微信小程序官方能尽快打造开发出一款通用的微信电商小程序上线;其实运营多商户入驻的商城商圈型小程序更应给予扶持,因为商城圈主不仅仅是在自营,也是在引导整合更多商户入驻使用小程序,这才能加速微信小程序的普及推广,才能让更多人使用微信支付。 近一年来就咱所试运行的每个小程序都有能够操作的后台管理系统,并且也都开通了独立微信支付帐户,每个小程序咱也是以所谓的定制或购买的方式花了一些钱,但是,并没有能正常的产生收益,服务商却每年还是要咱支付所谓的续费系统更新维护服务,可是有的小程序服务商在开发交付后第一年免费服务中就并有给咱小程序有实质性的更新维护升级服务,咱们这心里始终就会有些被忽悠的感觉而不敢续费由他们继续服务;又因为,现在看来对咱们一开始所讲的小程序定制开发或购买的说法,实际上绝大多数就是用咱们注册的微信小程序绑定他们已成形的小程模板租赁使用而已,并非是定制也不是购买,从这一点来看一开始他们就是不诚实的欺骗了咱们;不是真正意义上的个性化定制开发的小程序,后期个性化的一些需求就根本很难实现,因为他们给咱每个人的并不是所谓定制开发的独立小程序,而是同一个系统,只不过行业模板不同而已;给不了个性化的更新却又要你支付更多的资金升级购买高级别的系统营销工具,如果不支付所谓有其名而无其实的更新维护服务费,他们就会强制关停你的小程序后台系统登录权限以此要挟咱们续费让他们继续那样所谓的坑人服务;而且有时往往所谓的一些付费工具升级也没有体验期,这就会让很多人支付续费升级后发现仍不是自己想要的模式,就会感觉又一次被骗;因此,现实中咱们退一步说,即使咱们认同是定制开发买来的小程序,但是后期所谓的售后服务中,有些不诚信的客服和技术员平时还是会用这样那样的方式以敷衍的或是爱理不撘的态度对待咱们,根本没有心思为用户解决问题,收了钱前之后态度完全不同,差距很大,因为无论你运营好还不好,反正你每年得向他们支付续费服务,否则就让你小程序无法使用。因此,一些正常的微信小程序店铺营销系统更新维护升,他们却会整出很多花样的所谓营销工具,然后要你付费才能使用。所以有些能正常使用的功能却被整成你不付费购买,一旦系统更新后你原本的工具都不能正常使用了,让你原本的一系列能操作功能也无法运营。为什么会这样,因为他们的收益没有与运营管理者们的收益关联挂钩,就会整出很多收费工具来谋取更多收益。就会让一个极简便的微信小程序变得越来越复杂臃肿,近期体验到有些微信小程序开发服务商整出来的付费工具达上百种之多。 其实最好的方式就是微信官方开发出不同需求的微信小程序系统和可灵活运用的模板来,让大家根据自己的能力和需求象征性付费或免费注册使用或参与运营推广,以微信小程序使用者和运营者的实际运营收益提取系统使用和维护服务费,无收益不收费,一年仍没有交易或运营收益的关停使用,这样才能迅速让人们放心大胆的使用微信小程序,才能让以微信小程序创业的人们积极参与小程序的推广普及,才能迅速让更多人在各个方面使用上微信支付,才能让微信小程序商业化产生社会价值,才能让腾讯摆脱游戏真正过度到电商发展的道路上来,发挥创造出更多的社会价值。 而目前现实中咱们大部分人的小程序委托给别人或微信小程序第三方开发服务商开发上线后,真正把微信小程序运营准备当事业来做的人,往往一开始是不可能都立马真正投入运营的,都是要试运行一段时间,以便真正运营起来不让参与的商户和消费者造成不好的体验;但是,现实中很多开发服务商后期他们连咱们试“运行”体验中就已发现的一些需要调整或改进的问题都不能及时有效的给予解决,那..后面谁还敢再继续花时间和大把的精力投入真式“运营”小程序创业呢?开发服务商他们又错误的认为给咱们开发了小程序,就象是给了咱们一块黄金一样;其实,即使咱们委托朋友或是自己独立开发出的真正意义上的独立小程序也只是“一幢空荡的建筑物”而已,而后期要使其发挥价值成为有经营效益的“商城”“商圈”或店铺,咱们后面还要有99%的精力需要持续充实内容并投入运营才有可能创造出微信小程序其真正的价值收益;而何况,咱们大多数人本身又不是专业程序员,那咱们所委托开发的这些小程序开发服务商就更必须得是诚信可靠的主,否则任何人也不敢再继续傻乎乎的投入,不能运营那么这一张“纸黄金”也只能是一张废设置图纸而已。因此,现实中很多人经历一段时间的周折都会有感觉被骗而无奈的放弃了微信小程序创业梦想;为什么会这样呢?关建就是小程序服务商的后期服务费没有紧紧的与微信小程序运营者们的收益挂钩,并且后期有的服务商一系列系统更新升级和一些服务工具又要另行变着花样让你付费才能使用,否则让你的小程序无法适应新的营销需求,你花钱花时间精力投入的小程序就会成为“一块弃之可惜,食之无味的鸡肋”。例如目前微信小程序服务商应主动为运营者更新接入的微信官方视频直播营销系统插件,而有的服务商却变相每年收取其功能插件使用费;其实包括微信官方的基础功能插件和第三方开发服务商日常开发更新的其它运营工具都不能以一次性的卖工具形式谋利,而应该以适当提升服务费率为导向,让开发服务商的后期系统更新维护服务费紧紧的与运营者的收益比例挂钩,才能保证系统真正的始终处于最佳新潮状态,各方也才能真正形成互利共赢的利益共同体;最终小程序入驻商户店铺运营功能好了、消费者的使用体验好了,那么小程序购买运营者就有好的效益,运营者效益好了那么第三方开发服务商的服务收益也就能同步跟着高,微信支付体系也才能不断提升市场份额;唯有这样微信小程序也才能真正步入良性发展商业化的轨道上来;也因此,服务商才会主动设身处地的去重视运营者在运营中的小程序所有反馈和建议,甚至他们会主动去发现问题并及时解决处理好;只有这样才能让更多人放心大胆无后顾之忧的投入微信小程序运营创业,也唯有这样微信小程序才能真正健康发展壮大起来。然而,很显然目前一些无诚信的小程序开发服务商的坑骗欺诈行为正在严重制约着微信小程序的健康快速发展。就连微信官方小程序直播插件接入服务居然他们也都能狮子大张口要讹诈运营者微信用户1000元,更何况微信小程序直播还有真正普及推广起来呢,正需要人们去运营推广,而他们却在设卡。 再例如,咱们体验的这三家其中的手边微信小程序是咱异地一位村淘同行兄弟为咱一手经办注册开通的专业版小程序,但当时咱想要做的是多城市多商户入驻型的商城商圈小程序,可我那同行兄弟也不知是误解了我的意思还是我猜测的其他原因,给我做的只是一个专业版商户型小程序,这位兄弟由他1999元的市场零售价给咱打折去除了他售卖小程序的利润部分只收了该付给开发服务商的成本399元,这其中应该是没有赚我什么钱;但是,从一开始至今在咱小程序一些页面和商品详情页底部仍然都始终插有他代理售卖小程序的连接按钮,这就让咱运营产生了很大的担忧和运营的顾虑,因为如果咱花精力开拓发展的粉丝客户从咱这里注册购买了他们的小程序自行运营小程序了,不但对咱会形成不同程度的区域竟争,而且这笔在咱店铺产生的小程序交易咱不仅没有任何佣金分成,又失去了一位客户,甚至是这个客户他一个圈子或一个区域的潜在粉丝用户,这样一来到最后咱的店铺岂不是白忙活又成了小程第三方开发服务商和销售小程序代理商们的免费打市场的推广位了吗?这样谁会还有思心去运营这样的小程序呢?提供资料给他为咱注册的这样一个小程序,也是咱一直没有真正运营的最大顾虑所在。开发服务商应该把售买小程序做成象有分销设置的商品一样,给到我们或下级代理本着自愿的原则为平台有针对性的向目标运营用户去推广分销小程序才对,这样大家也才愿意参与微信小程序的应用和推广,这也是唯一的上上策,因为坑骗糊弄是不会长久的,即使现在有人没有意识发现到这个套路,但是,一旦只要是花了时间和精力运行一段时间后发现这些套路的人,就会醒悟、就会很正常的对其小程序产生一些反向宣传。。。。(目前,仍有小程序开发服务商在运营用户小程序里面插有这样的坑杀套路链接,还自欺欺人的把运营用户当傻子,其实他们真的想反了,这样如何能快速发展壮大微信小程序应用的商业价值呢?而他们又怎么可能有长期的发展前途!) 就咱同行兄弟,为咱小程序在附近显示功能审核至今仍没有审核开通,而我明明有线下实体店,小程序微信公众号也明明早就微信认证过,而且,为了开通附近小程序功能今年又多次反复上传了营业执照和相关资料,可每次审核反馈给我的信息都是说经过审核无线下实体店,咱明明有,为啥说没有?咱不想在这做猜测了;小程序微信支付功能也是经历了近一年,在今年3月份才开通,因此,咱感觉这小程序平台后期服务方面不能及时落实到位拖延时间太长。(不过我还是比较看中广东与众不同和柚安米手边小程序的未来,包括随手逛商圈微信小程序,希望这三家当中能有帅先改革起来,当然还有更牛的开发服务商也许还没有被咱们发现;比如随手逛商圈微信小程序,他们如果一直能保持2018年9月份之前四五个月的更新频率不放弃运营,到今天应该能成为很受中小企业和所有线上和线下实体商户欢迎的小程序,且较为实用完美的航母级微信小程序了,因为,随手逛商圈层级模式架构清晰,手机端操作管理既简单但功能又比较全面,几乎所有操作在手机端都能随时随地的完成,不懂互联网的农村大哥大姐都能用得上,因为在手机端员工管理中可以随时进入微信好友列表添加指定好友为运营管理员代为运营管理,也可以随时随地通过一键删减运营者而解除代运营关系;可是不知是啥原因“随手逛”从2018年9月19日在公众号发布:高级圈主可手动为商圈商城内店铺升级的通知后,至今公众服务号中就再也没任何动静了。。。因此,近两年来一些新的功能:如,视频直播、直播社团营销等也就没有人再为随手逛进行更新和维护,也让人们不敢真正运营使用) [图片] 鉴于微信小程序无需下载也不用安装又有极简单的操作体验能力,咱很看重微信生态小程序这个应用;另外,个人认为“抖音”她的价值是泛娱乐型广告传递而电商是走不长的。 所以咱在2019年从称是微信公众服务号助手上联系上了一家官方小程序开发公司的业务员,他对咱说以正好公司有活动,可以按当时的活动优惠价给咱定制开发本地便民商圈小程序,但是咱从付款不久就有些后悔,因为付了款后面的制作开发和安排工作就是乎没付款之前那么热情积极了,而且程序员在交付小程序的时候也并没有真正按其公司交付流程,而是只让你以一个普通客户的身份体一下验简单的小程序网购场景而已,强行让你跟着走完交付流程,根本没有给到系统后台操作管理方面的真实体验,在交付时以各种借口说后台管理操作系统交付后才能实行操作体验,让咱这小程序所有者根本无法在交付前真正以管理员的身份体验系统架构后台操作管理到底是什么样的,最后只能无奈地收下这半生不熟的“西瓜”在手上,苦心经营充实一些内容等待,希望他们在后期能真正更新完善起来,可是非但没有完善增加或升级功能反而发现给咱限制了好些地方,咱将当时接手时下载的小程序资料包解压和现在的进行过对比发现少了几十条内容;另外,就连之前承诺给咱淘宝联盟(淘宝客)和快递员收件这两个连接也没有在交付前为咱接入,小程序在附近强制显示的功能至今也没开通,咱公众号运营服务他们一直根本就从来没有做过,咱一年多的苦苦等待很多地方让咱仍然是很失望,甚至想要投诉他们。(下面有咱对接时的部分聊天记录截图) 最后在这咱想说:微信小程序有着宠大的微信用户群体,为什么微信视频通话入口不接入微信小程序直播?完全可以做到比抖音更实用火爆的啊!再不尽快进行改革真的就晚了!!另外,还有一点就是微信小程序刚刚上线处于推广前期,蜂拥而上的游戏类小程序无节制的过度上线,并在微信群中泛滥充斥着人们的视觉神经,也造成了人们对微信小程序的错误认知,咱们近一两年在很多微信群中发现只要有人在微信中转发小程序就会有人反感,人们误认为小程序就是坑人的游戏产品。这一点也给微信小程序运营推广造成一定负面影响,这就要有象咱们这些运营者们做更多的正面宣传推广。 综上所反馈的的一些方方面面的问题和建议咱并不想刻意投诉谁,只是希望微信小程序开发服务商能够诚信运营和腾讯官方一起有所重视当下的现实,让微信小程序能有一个健康安全快速发展的环境。 ----本人初小毕业没有什么文化,许多地方字句有错,请见谅! 谢谢! [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] 上面这一张截图是交易达成付款环节 [图片] [图片] [图片] [图片] [图片] [图片] 这下方截图是咱首次把在社区这发表的准备投诉内容转发给他们后,当时感觉小程序客服似乎态度有些好转,但是又经过咱这一个多月的耐心等待感觉他们仍然是没有诚意为咱这小程序处理问题,而是似乎这一个多月来他们又可能公关了腾讯方面的相关人脉,而现在变得更加肆无忌惮了 [图片] 这一年多几乎没有处理任何问题 [图片] [图片] [图片] [图片] [图片] 如果真是腾讯官方指定授权的微信小程序第三方开发服务商应尽快自行整治,否则腾讯更应从严追究。 咱希望广东与众不同信息科有限公司是真正的腾讯官方授权指定的小程序第三方开发服务商,能尽快诚信对待参与小程序运营者们 尊敬的腾讯官方客服:象这样没有诚信的开发服务商已严重制约着微信小程序的健康发展,究竟该由谁来监督谁来管?
2021-01-27 - 微信开发者工具 1.03.2006090 Stable 更新说明
下载地址Windows 64 、Windows 32、macOS 此版本是 1.03.20006091 RC 在灰度 1w 用户后并携带以下 bugfix 的稳定版本, [代码]F[代码] 修复 Mock 的规则无法删除的问题 反馈详情[代码]F[代码] 修复 自定义预览前预处理命令失败后,再次预览无响应的问题 反馈详情[代码]F[代码] 修复 新创建的小游戏项目第一次编译可能提示 [代码]__virtualDOM__ is not defined[代码] 的问题[代码]F[代码] 修复 project.config.json 内容不正确时,无法新建自定义编译条件的问题[代码]F[代码] 修复 project.config.json 中 watchOptions.ignore 删除部分配置后,重启工具无法生效的问题 基于 nwjs: 0.39.3, chromium: 75 的内核版本,主要的更新内容如下: 1. 新增终端面板 工具的面板可以切换并展示 “终端” 功能。 [图片] 2. 开发者工具进程管理 通过点击菜单栏【微信开发者工具】【查看进程管理】,可以检查并结束由开发者工具、终端创建的正在运行的进程。 [图片] 3. 云开发静态资源托管 静态网站托管是云开发为开发者提供的 Web 资源托管服务,网站的静态资源(HTML、JavaScript、CSS、图片、音频、视频等)可以托管在该服务上,并享有以下能力: 默认域名:获得对应云环境的唯一专属默认域名,通过域名可访问静态资源,域名可以用于测试或线上使用 小程序 webview:小程序不用配置业务域名即可在 <web-view> 打开云开发静态网站托管的域名(仅支持能够使用 <web-view> 标签的小程序) CDN 加速 详细文档:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/staticstorage/introduction.html 4. 订阅消息开关 模拟器设置页面支持配置是否接收订阅消息,以及相关模板消息的订阅状态。 5. 支持小程序/小游戏收藏事件调试 开发者工具新增支持模拟小程序收藏功能,详细文档: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/favorite.html 6. 通用设置新增 - 快速打开文件类型 对通用设置进行了样式改版,且新增设置项 【快速打开文件】 [图片] 该设置项可设置点击模拟器状态栏路径时默认打开的页面对应后缀的文件(右键可以查看到可选的后缀类型)。 [图片] 7. 通用设置新增 - 项目关闭时 在通用设置中新增设置项 【项目关闭时】,该选项将控制当前项目关闭时的表现,默认是项目关闭时打开项目列表页,用户可根据需求自行设置。 [图片] 8. 新增搜索回调调试插件 CallbackDebug 是小程序商品数据实时更新接口调试能力。基于该能力,小程序的开发者可以获取小程序商品数据实时更新接口的返回结果。 详细文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/merchant_callback.html [图片] 9. 新增小游戏脚本录制插件 微信小游戏测试框架提供了一套操作录制回放工具。可以在微信开发者工具中录制你在模拟器中的操作,并生成录制文件。将该文件打包提交,即可在云测环境中回放录制的操作。详细文档: https://developers.weixin.qq.com/minigame/dev/guide/best-practice/tool/record.html 10. 模拟器更多模拟 开发者工具通过模拟内存警告等真机异常,可以触发wx.onMemoryWarning 等API,帮助开发者收集异常数据、优化小程序。 [图片] [图片] 11. 支持音视频合成调试 音视频合成相关 API 过去只能在真机上预览进行调试,现在可以通过开发者工具直接调试,音视频合成 API 详细文档:https://developers.weixin.qq.com/miniprogram/dev/api/media/video-processing/wx.createMediaContainer.html 12. 上传代码 sourcemap 下载 过去,开发者只能到 mp.weixin.qq.com 上获取已上线的版本的 sourcemap 文件,现在能通过开发者工具获得每次上传的版本的 sourcemap 了。 [图片]
2020-06-22 - OpenSDK支持FileProvider方式分享文件到微信
各位开发者: 最新版本(7.0.13)的微信 SDK 已经适配使用FileProvider的方式来进行消息分享。 如果分享的消息中涉及文件路径(如图片类型消息),建议开发者针对Android 7.0版本及以上设备,判断微信版本支持(判断方法见后文【微信版本支持】一节)的情况下,更新为FileProvider的方式进行分享。以下是具体的适配说明。 使用FileProvider1. 配置在项目的[代码]AndroidManifest.xml[代码]添加相关配置,示例如下: <provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_provider_paths" /> </provider> // ${applicationId}为你的应用包名 在[代码]res/xml[代码]目录(如果没有[代码]xml[代码]目录,则新建一个)下,添加文件[代码]file_provider_paths.xml[代码],内容如下: <?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="sharedata" path="shareData/"/> </paths> [代码]external-files-path[代码]表示通过 [代码]Context.getExternalFilesDir(null)[代码] 接口获取到的目录下的文件才可被共享,其他未配置的路径均不可被分享。同样的节点可以配置多个,以支持多个不同的子目录,如下所示: <?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="sharedata" path="shareData/"/> <external-files-path name="sharedata2" path="shareData2/"/> </paths> [代码]paths[代码]内部还支持节点配置其他的路径,比如: [代码]files-path[代码],对应于 [代码]Context.getFilesDir()[代码] 获取到的目录[代码]cache-path[代码],对应于 [代码]Context.getCacheDir()[代码] 获取到的目录 ...还有一些其他可配置的路径,开发者可自行了解使用。 2. 使用FileProvider接口将路径通过FileProvider的接口转换成[代码]content://URI[代码]形式,示例如下: public void shareToWechat(Context context) { // ... String filePath = context.getExternalFilesDir(null) + "/shareData/test.png"; // 该filePath对应于xml/file_provider_paths里的第一行配置:,因此才可被共享 File file = new File(filePath); String contentPath = getFileUri(context, file); // 使用contentPath作为文件路径进行分享 // ... } public String getFileUri(Context context, File file) { if (file == null || !file.exists()) { return null; } Uri contentUri = FileProvider.getUriForFile(context, "com.example.app.fileprovider", // 要与`AndroidManifest.xml`里配置的`authorities`一致,假设你的应用包名为com.example.app file); // 授权给微信访问路径 context.grantUriPermission("com.tencent.mm", // 这里填微信包名 contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); return contentUri.toString(); // contentUri.toString() 即是以"content://"开头的用于共享的路径 } 微信版本支持OpenSDK版本:必须大于或等于 5.4.3 版本,建议开发者升级至最新版本 5.5.8微信版本:当且仅当通过 IWXAPI.getWXAppSupportAPI() 接口获取到的值 >= 0x27000D00,才能支持FileProvider的方式进行分享。示例代码如下:private IWXAPI api; // api的初始化这里省略 public void shareToWechat(Context context) { // ... if (checkVersionValid(context) && checkAndroidNotBelowN()) { String filePath = context.getExternalFilesDir(null) + "/shareData/test.png"; File file = new File(filePath); String contentPath = WXOpenSDKFileProviderHelper.getFileUri(context, file); // 使用contentPath作为文件路径进行分享 // ... } else { // 使用原有方式传递文件路径进行分享 // ... } } // 判断微信版本是否为7.0.13及以上 public boolean checkVersionValid(Context context) { return api.getWXAppSupportAPI() >= 0x27000D00; } // 判断Android版本是否7.0及以上 public boolean checkAndroidNotBelowN() { return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N; } 详细参考 OpenSDK 消息分享文档:https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Share_and_Favorites/Android.html 微信团队 2020年03月31日
2020-03-31 - 客服功能使用常见问题
1、微信公众平台客服功能与多客服功能有何区别? 新版的客服功能,在原有多客服的基础上进行了改进,为公众号运营者和客服人员提供一个网页版客服聊天工具。与旧版电脑客户端多客服相比,新版客服功能将会是网页聊天的形式,使用微信扫码登录,支持多人同时为一个公众号提供客服功能,实时回复粉丝的咨询。目前新版客服功能和旧版最大的区别是暂不支持手机端使用,并且不支持下载聊天记录,给您带来的不便请您谅解。 2、已经有多客服功能了,为什么还要推出新版的客服功能? 多客服功能是微信公众平台较早前推出的公众号客服电脑客户端软件,支持多个客服同时为一个公众号服务。由于推出的时间较久,维护和迭代的成本较高,因此我们推出了新版的客服功能。请使用多客服功能的公众号运营者尽快升级到新版客服功能。 3、开通客服功能的条件是什么? 通过微信认证的公众号即可申请使用客服功能。 4、如果我已经使用了旧版多客服,要如何升级成新版客服功能呢? 请联系公众号的运营者,登录微信公众平台后,在“功能-多客服”页面,点击“升级”按钮,进入升级流程。请注意,升级过程中,需要将多客服的所有客服帐号逐一绑定微信号,如果存在不需要绑定的帐号,请将其删除。只有全部客服帐号绑定完毕后,才能升级成功。升级成功后,公众号和粉丝互动的消息将推送到新版网页客服功能,旧版多客服客户端将失效。 5、从旧版多客服升级到新版客服功能后,还可以使用旧版电脑客户端吗?可以回退到之前版本吗? 升级成功后,旧版客户端将失效,使用客服功能需通过客服功能页面进行登录,使用新版客服功能。升级成功后,不可回退。 温馨提示:升级完成后,旧版多客服仍可正常登录,可以查看之前的会话;但无法使用旧版多客服收发消息。新的粉丝互动消息只能在网页版客服功能查看。旧版多客服聊天记录不会同步。 6、新版客服功能聊天记录保存多久? 客服功能会在云端保存30天的聊天记录。点击某个对话,向上翻动即可看到较早之前的聊天记录。目前暂不支持下载及导出。 7、新版客服每次可以接入多少用户? 目前没有接入用户上限。 8、新版多客服是否支持手机端使用 暂不支持,还请您后续留意。 9、新版客服是否支持给用户发送语音消息? 暂不支持,还请您后续留意。 温馨提示:升级完成后,原手机端多客服会失效,给您带来的不便请您谅解。 10、点击文件夹标志后无法发送文件,提示上传失败的原因是什么? 目前仅支持发送图片,其他类型文件请您后续留意。
2019-12-26 - 微信开发者工具 1.02.2003121 RC 更新说明
下载地址Windows 64 、 Windows 32 、 macOS 1、支持 API Mock 新增API Mock功能支持模拟 API 的返回内容,让开发者更方便开发小程序,更多详情可移步至:API Mock文档。 [图片] 2、编辑器支持重命名多个文件编辑器支持在同级目录下同时重命名多个同名文件,方便对 Page/Component 文件进行重命名。 [图片] 3、支持显示灰度中的基础库、下发测试基础库新增显示灰度中的基础库以及基础库支持的客户端版本。 [图片] [图片] 同时新增推送按钮,将选定版本的基础库下发到客户端上,推送结果可以在开发版小程序的调试面板中查看。 [图片] 微信客户端对开发版的小程序打开调试,可以看到测试版基础库的生效时间。 [图片] [图片] 注意: 该功能只能推送到登录到开发者工具的微信号的手机上。 会影响到手机上所有的小程序。4、模拟器支持终止模拟器是工具的主要功能之一,如果小程序/小游戏的业务代码中出现死循环、复杂运算、频繁调用某些 API 的情况下都会导致工具出现卡顿或者 CPU 占用比较高的情况。模拟器新增终止按钮,点击后可以暂时终止模拟器运行,节省系统资源占用。 [图片] 5、打开项目时展示 Loading 状态工具增加开启加载 loading 弹窗,显示加载状态情况。 [图片] 6、CLI/HTTP V2 更新CLI & HTTP 接口升级 v2 版本,在 v2 版本中,旧版命令仍然可以使用,但已废弃并会在未来移除,请使用 v2 命令。v2 版本增加了云开发管理操作支持、优化命令形式、增加细致状态提示、支持长时间命令执行、支持国际化(中英文选择)等。详细文档。 [图片] 7、优化云控制台用户访问统计和监控图表的数据展示支持按照近 7 天、近 30 天以及自定义时间段来筛选 DAU。 [图片] 8、数据库备份回档云开发已自动开启数据库备份,并于每日凌晨自动进行一次数据备份,最长保存 7 天的备份数据。如有需要,开发者可在云控制台上通过新建回档任务将集合回档(还原)至指定时间点。详情。 [图片] 9、优化模拟器工具栏展示工具栏机型及显示比例菜单合并,网络模拟调整到模拟操作下。 [图片] 10、编辑器支持小游戏项目的 API 代码补全在小游戏项目,可以看到为小游戏提供的代码补全(部分 Canvas API 尚未提供)。 [图片]
2020-03-13 - (19)文件系统能力
文件系统能力 文件系统能力可便于用户在客户端保存文件资源,并在下次启动客户端之后可以使用已保存的文件。 只要用户不主动删除小程序或小游戏,并保持一定的使用频率,文件都可以一直被保留。 合理的使用文件系统能力来缓存资源文件,可以给开发者更好的使用体验。 今天,我们来分享文件系统能力的小故事。 1 文件系统的演进历史 小程序在最早发布的版本中就已提供了最基础的文件存储和删除接口:wx.saveFile、 wx.removeSavedFile ; 对于绝大部分的小程序来说,这两个接口已经能够满足开发者的需求。但对于小游戏来说,需要更完整的能力来做支撑。 因此,发布小游戏的时候我们便提供了一套更完整的文件管理系统:FileSystemManager,其中主要包含了目录管理、文件内容读写等能力。 2 文件系统的设计背景 文件系统能力是应小游戏开发需求的迭代而逐步增强的。在小程序的场景下,很多时候只是需要把一个图片或视频资源缓存起来便可继续使用,文件内容与文件存储的目录结构都不是开发者所关心的。 但是在小游戏场景下情况则不同—— 一方面,小游戏除了有图片和视频文件、还有游戏引擎生成的配置文件,游戏需要能够去读取并理解配置文件的具体内容; 另一方面,游戏使用的资源文件会比普通小程序更多,若没有内容目录管理的功能,维护成本会变高。 除此之外,由于小游戏代码包大小限制只有4MB (加上分包最多8MB),对于一些偏重的游戏,资源甚至容易超100MB。 因此在此大背景下,我们给文件系统主要增加了目录管理、文件内容读写等两项接口—— 目录管理的需求场景是在使用游戏引擎时需要按目录来管理资源文件,文件内容读写的需求场景是在使用游戏引擎时需要读取配置文件;同时,我们对小游戏类目的本地存储容量的规范限制扩容到50MB。 开发者可能会疑惑,为什么在小程序的文件系统中会有一些功能相接近的接口?例如,想缓存一个文件,可以用 saveFile 或 copyFile ;再比如 removeSavedFile和 unlink 都可以用来删除一个文件。 上述情况的原因是我们在早期便提供了基础的文件存储接口 saveFile 和removeFile ,但不提供自定义目录相关的能力,开发者调用 saveFile 之后只能得到微信返回到的一个随机文件名。 小游戏应运而生的同时也增强了对文件系统能力扩展的需求,为了保证向后兼容,我们保留了这批基础接口,并在这个基础上增加了目录管理接口以及对应的文件操作接口。因此,便出现了上述一些相似接口的情况。 3 文件系统的优势—存储隔离 有不少开发者询问过关于文件存储的问题,他们担心文件内容被其他小程序读取到,也担心多个登录用户之间的文件内容会互相影响。为了保证用户的隐私安全,也为了保证小程序的数据安全,本地文件存储的一个重要规则便是保证隔离。 文件被存储到本地后,会以小程序账号和用户账号两个不同的维度来区分和隔离。即:同个微信用户使用不同小程序之间的文件存储会互相隔离;不同微信用户(在同一台手机中)使用同个小程序时,不同用户间的文件存储也会互相隔离。 [图片] 4 适当的存储容量 考虑到存储的问题,我们规范了小游戏文件存储的容量。普通小程序是10MB,小游戏则是50MB,当文件存储超出限制时,写入的文件会失败。 功能上线以后,我们曾收到过若干宝贵意见与反馈,希望能提高容量限制。但在经过反复论证与评估后,我们认为如果将文件存储的容量再往上提,就会有用户新增需要管理或清理手机存储空间的需求,小程序和小游戏将会变得不再“小”了。对于资源文件超过上述标准限制的小程序与小游戏,应该合理地管理本地文件,及时清理不常用的文件,这样在大多数情况下,手机存储空间便能保证顺畅。 更多关于小程序文件系统能力的信息,可查阅 接口文档 。
2018-08-21 - 一文读懂微信小程序开发工具、项目结构、全局配置文件及页面跳转
微信小程序是近些年一项新的事物。互联网的发展过程中,总会不断出现新的事物,因为逆水行舟不进则退,创新才是互联网科技发展的原动力。 大家想一下,这种开发模式对Android和IOS肯定会造成一些冲击,因为这样微信等于自己成为了一个操作系统的平台,里面可以有各种各样的应用。 另外听说支付宝最近也有小程序了,说明这是今后的一个趋势。 抱着一定的好奇,另外最近闲暇时间也在做自己的小程序,所以边学编边写,供大家参考。 那么小程序开发怎么入门?今天主要介绍下开发工具、项目结构、全局配置文件及页面跳转等。 小程序账号的注册、开放工具的下载比较简单,此处不在赘述。 一、开发工具 开发工具的名字就叫“微信开发者工具”,非常直白利于记忆。 启动之后,我们来看一下它的长相,开发工具界面如下: [图片] 可以看到,界面有三个区域:模拟预览区、编辑代码区、调试区。这和我们开发其他软件的必备的操作区域是一样的。 这三个开发区域,分别对应左上角三个绿色按钮,点击它们就可以进行对不同区域的隐藏,非常方便。 建议大家,初学期间,先下载一个demo项目,打开之后,边看边学,在开发工具上边点边试,效率最高。 下面我们就以这个“石头剪刀布”小游戏为例进行介绍。 二、项目四大文件类型 微信小程序的项目,共有四种文件类型,这是最最基础的入门知识了。 1.wxml:微信模板文件,类似网页开发的html文件。 2.wxss:微信样式表文件,用于定于页面样式。 3.js:脚本文件,代码逻辑写在这。 4.json:静态数据配置文件。 三、 项目结构 整体结构图如下: [图片] 可以看出: (一)有一个page文件夹:page文件夹种的子文件夹就代表一个个页面,每个页面(文件夹)信息都至少包括3个文件,json文件不是必须的 一般情况下我们只需要用全局配置的 app.json文件配置就可以了。 (二)有一个utils的文件夹:可以看出utils和pages文件是并列的,所以他就不代表页面的信息了,里面只有一个js文件,这个js文件其实就是把代码模块化了,这里面是将日期的转换封装好了,直接引入使用就可以了。 (三)还有一系列app开头的全局配置文件,pages里面的代表的仅仅是某个界面的配置,而app系列文件的代表的是整个项目的配置。 本篇重点介绍这些全局配置文件 四、全局配置文件 1.文件app.json—小程序全局配置文件 代码示例如下 [代码]{ "pages": [ "pages/index/index" "pages/logs/logs" ], "window": { "backgroundTextStyle": "light", "navigationBarBackgroundColor": "#363527", "navigationBarTitleText": "石头剪刀布", "navigationBarTextStyle": "white" }, "sitemapLocation": "sitemap.json" } [代码] 此文件主要管理小程序的全局配置,包括小程序页面路径,界面风格(标题,背景色,字体色),网络超时时间,底部tab等; page配置项说明: 提供页面入口和页面控制功能,后面会介绍到。 window配置项说明: 这个配置项是用来设置小程序的状态栏、导航条、标题、窗口背景色。六个属性功能如下: navigationBarBackgroundColor 它是用来设置导航栏背景颜色,这个属性要输入的是十六进制颜色值. navigationBarTextStyle 它是用来导航栏标题颜色的,它的输入类型是字符串(String),但是现在官网给出的文档目前仅支持(black和white)这两种颜色. navigationBarTitleText 这个属性是显示导航栏标题内容的,开发者可以根据自己的需要来进行设置. - backgroundColor 这个属性是设置窗口的背景色的,它需要输入的也是十六进制颜色值的. backgroundTextStyle 这个属性我的理解是设置窗口内容的样式的,官方给出的标准说明是下拉背景字体、loading 图的样式,这个属性 navigationBarTextStyle属性一样目前仅支持两种颜色(dark和light). enablePullDownRefresh 这个属性是设置是否开启下拉刷新,默认是开启的,注意: 在这个配置文件(app.json)中如果关闭了下拉刷新,当你在你自己开发的页面中想要设置下拉刷新也是行不通的,也就是说如果你想要在以后页面中使用下拉刷新这个功能,就必须保证这个配置文件中的这一项设置是打开的。 官方文档中解释如下: [图片] 注意:每个文件夹中的json中也有windows配置项,定义了文件夹内的wxml的界面风格,可覆盖app.json中的全局风格 其他配置项含义如下: [图片] 2.project.config.json—开发者工具配置 此文件用于定义开发者工具的个性化定制,比如界面颜色,编译配置等等。 project.config.json保存了你对于开发者工具的相关配置,如果你换电脑了,你可以通过此文件直接还原你最初习惯的开发设置。 3.app.js app.js文件是项目的入口文件 onLaunch : function(){}:调用API从本地缓存中获取数据 onLoad:function(options){}:页面初始化 options 为页面跳转所传递过来的参数 onReady:function(){}:页面渲染完成 onShow:function(){}:页面显示 onHide:function(){}:页面隐藏 onUnload:function(){}:页面关闭 还可以在 app.js里面定义一些全局的方法: 例如:获取用户信息的方法(wx.getSetting)和属性(globalData): 示例如下 [代码]//app.js App({ onLaunch: function () { //调用API从本地缓存中获取数据 var logs = wx.getStorageSync('logs') || [] logs.unshift(Date.now()) wx.setStorageSync('logs', logs) }, getUserInfo:function(cb){ var that = this if(this.globalData.userInfo){ typeof cb == "function" && cb(this.globalData.userInfo) }else{ //调用登录接口 wx.login({ success: function () { wx.getUserInfo({ success: function (res) { that.globalData.userInfo = res.userInfo typeof cb == "function" && cb(that.globalData.userInfo) } }) } }) } }, globalData:{ userInfo:null } }) [代码] 五、页面跳转 1.首次进入程序,入口由文件app.json控制,如下所示,入口为pages/index/index.wxml文件 [代码]{ "pages": [ "pages/index/index",(说明:第一行的这个页面代表小程序的入口) "pages/logs/logs" ], [代码] 2.微信小程序页面之间怎么跳转 方法一: 在pages/index/index.wxml文件中设置一个按钮,给按钮绑定一个跳转事件,则点击跳转到另一个页面。 index.wxml文件,代码示例 [代码] <navigator url="../ts/ts" hover-class="navigator-hover" open-type="switchTab">我是个按钮demo </navigator> [代码] 方法二: 在小程序底部加选项卡控件,点击不同选项卡进入不同页面 [图片] 代码就是在app.json里放一个list就可以了,list中将页面和选项卡绑定,示例如下: [代码]"list": [ { "pagePath": "pages/xxx/xxx", "text": "选项卡一", "iconPath": "image/xxx.png", "selectedIconPath": "image/xxx.png" }, { "pagePath": "pages/yyy/yyy", "text": "选项卡二", "iconPath": "image/yyy.png", "selectedIconPath": "image/yyy.png" }, { "pagePath": "pages/zzz/zzz", "text": "选项卡三", "iconPath": "image/zzz.png", "selectedIconPath": "image/zzz.png" }, { "pagePath": "pages/ddd/ddd", "text": "使用说明", "iconPath": "image/ddd.png", "selectedIconPath": "image/ddd.png" } ] [代码] 六、新增页面 最佳做法是: 1.直接在app.json文件的pages数组中新增一条页面路径 2.按ctrl+s,保存,pages文件夹会自动生成一个新的页面,这样做更加方便和保险。
2021-02-27 - 微信第三方平台:若干项能力优化提升
微信团队近期对第三方基础能力进行了若干项优化,并对服务平台提供的能力及服务进行了迭代,详情可见微信第三方平台开发文档和微信服务平台,优化内容具体如下: 优化业务流程 1.第三方代注册小程序,可快速设置登录邮箱和密码 第三方平台使用快速创建小程序接口代商家创建小程序后,商家将收到设置小程序登录邮箱和密码的微信消息提醒,管理员可点击提醒前往设置,或直接搜索「小程序助手」进行邮箱密码设置。 具体操作指引详见第三方代注册小程序支持快速设置登录邮箱和密码。 2.第三方代注册小程序,支持更多类目上线 第三方调用接口快速创建小程序现已支持更多类目,以往只支持创建线下类目的小程序,目前除了境外主体的类目、内测类目、以及个别敏感类目暂不支持外,大部分类目均与“微信官方文档-小程序开放的服务类目”对齐。快速创建小程序接口-类目参考表已更新。 提高开发效率 1. 完善第三方开发文档 近期在微信开放平台上,陆续补充、完善第三方开发文档,其中数据分析、小程序直播两个接口文档已上线。 后续将继续完善、输出更多面向第三方平台的接口能力与文档。 2. 第三方平台服务器域名设置 1)提升服务器域名数量上限:Request域名、Socket域名、Uploadfile域名、Download域名、Udp域名的设置数量均最大支持200个; 2)服务器域名的每月修改次数上限,提高至50次。 3. 第三方平台业务域名设置 业务域名数量上限提升至100个。 4. 第三方平台小程序模板库数量限制 小程序模板库数量上限,从50个提升至200个。 5. 上传小程序代码时,ext.json对插件的支持 通过commit接口上传小程序代码时,可通过ext.json完成对Plugins的配置,该配置会覆盖模板中app.json中的Plugins配置。 6. 小程序直播插件在ext.json的配置优化 小程序直播插件,可直接通过Plugins进行配置;不再需要额外配置recompile:true才会生效。 7. 第三方平台调用头像修改接口的报错说明 修改头像接口的返回错误码47001,含义为“数据格式错误”。需注意,此接口入参中,x,y参数均需为字符串类型。 8. 第三方平台模板保存的优化建议 当第三方平台模板较大时,保存模板容易触发每秒30M的上传流量限制,建议开发者注意调用频率和控制模板大小。 提供更多能力 微信服务平台,是给微信公众号、小程序运营者提供优质服务的官方平台,提供小程序开发、小程序插件、接口能力等开发服务,以及直播、视频等运营内容服务。 1. 提供AI、安全、地图、内容等近50项接口、插件能力 提供来自微信团队、腾讯云、腾讯音乐在内的近30项接口、插件能力,例如OCR识别、珊瑚内容安全、人脸检测与分析等,可供开发者在开发小程序过程中使用,实现基于接口的服务能力。 [图片] 2. 提供直播、视频、图文等内容代运营服务 MCN内容服务专区现已正式对外开放,帮助合作伙伴为商家提供直播、视频、图文等内容服务,满足商家直播方案策划、达人代播、公众号代运营、图文视频制作等需求。 商家可在内容服务专区选择服务商,下单并联系合作。 想要入驻专区的机构可以查看MCN服务商入驻要求后,与平台联系。 [图片] 平台将继续与各位合作伙伴共同建设更为完善的第三方平台生态,共同创造出更多优质小程序。
2020-07-15 - 关于服务器域名绑定限制
对于每个需要配置域名的接口,分别可以配置最多 20 个域名;对于每个域名,分别限制最多被20个帐号绑定。 更多关于域名配置请参考文档说明。 第三方平台是授权的小程序共用一套域名,第三方平台代表一个帐号。
2019-11-25 - 不在以下 request 合法域名列表中?
[代码]wx.request({[代码][代码] [代码][代码]url: [代码][代码]'https://api.remove.bg/v1.0/removebg.com'[代码][代码], [代码][代码] [代码][代码]data: {[代码][代码] [代码][代码]image_file: fs,[代码][代码] [代码][代码]size: [代码][代码]'auto'[代码][代码],[代码][代码] [代码][代码]},[代码][代码] [代码][代码]header: {[代码][代码] [代码][代码]'content-type'[代码][代码]: [代码][代码]'application/json'[代码][代码], [代码][代码]// 默认值[代码][代码] [代码] [代码] [代码][代码]},[代码]这个接口不合法吗
2019-08-06 - 正确使用JavaScript数组
首先,我们可以简单地认为缩进就是代码复杂性的指标(尽管很粗略)。因为缩进越多代表我们的嵌套越多,因此代码就越复杂。今天就拿数组来做具体的例子,来展示以下如何抛弃循环,减少缩进,正确地使用JavaScript数组。 “…a loop is an imperative control structure that’s hard to reuse and difficult to plug in to other operations. In addition, it implies code that’s constantly changing or mutating in response to new iterations.” -Luis Atencio 循环 我们都知道,循环结构就是会无形地提高代码的复杂性。那我们现在看看在JavaScript上的循环是如何工作的。 在JavaScript上至少有四五种循环的方式,其中最基础的就是[代码]while[代码]循环了。讲例子前,先设定一个函数和数组: [代码]// oodlify :: String -> String function oodlify(s) { return s.replace(/[aeiou]/g, 'oodle'); } const input = [ 'John', 'Paul', 'George', 'Ringo', ]; [代码] 那么,如果我们现在要使用[代码]oodlify[代码]函数操作一下数组里每个元素的话,如果我们使用[代码]while[代码]循环的话,是这样子的: [代码]let i = 0; const len = input.length; let output = []; while (i < len) { let item = input[i]; let newItem = oodlify(item); output.push(newItem); i = i + 1; } [代码] 这里就有许多无谓的,但是又不得不做的工作。比如用[代码]i[代码]这个计数器来记住当前循环的位置,而且需要把[代码]i[代码]初始化成0,每次循环还要加一;比如要拿[代码]i[代码]和数组的长度[代码]len[代码]对比,这样才知道循环到什么时候停止。 这时为了让清晰一点,我们可以使用JavaScript为我们提供的[代码]for[代码]循环: [代码]const len = input.length; let output = []; for (let i = 0; i < len; i = i + 1) { let item = input[i]; let newItem = oodlify(item); output.push(newItem); } [代码] [代码]for[代码]循环的好处就是把与业务代码无关的计数逻辑放在了括号里面了。 对比起[代码]while[代码]循环虽有一定改进,但是也会发生类似忘记给计数器[代码]i[代码]加一而导致死循环的情况。 现在回想一下我们的最初目的:就只是给数组的每一个元素执行一下[代码]oodlify[代码]函数而已。其实我们真的不想关什么计数器。 因此,[代码]ES2015[代码]就为我们提供了一个全新的可以让我们忽略计数器的循环结构- [代码]for...of[代码]循环 : [代码]let output = []; for (let item of input) { let newItem = oodlify(item); output.push(newItem); } [代码] 这个方式是不是简单多了!我们可以注意到,计数器和对比语句都没了。 如果我们这就满足的话,我们的目标也算完成了,代码的确是简洁了不少。 但是其实,我们可以对JavaScript的数组再深入挖掘一下,更上一层楼。 Mapping [代码]for...of[代码]循环的确比[代码]for[代码]循环简洁不少,但是我们仍然写了一些不必要的初始化代码,比如[代码]output[代码]数组,以及把每个操作过后的值push进去。 其实我们有办法写得更简单明了一点的。不过,现在我们来放大一下这个问题先: 如果我们有两个数组需要使用[代码]oodlify[代码]函数操作的话呢? [代码]const fellowship = [ 'frodo', 'sam', 'gandalf', 'aragorn', 'boromir', 'legolas', 'gimli', ]; const band = [ 'John', 'Paul', 'George', 'Ringo', ]; [代码] 很明显,我们就要这样循环两个数组: [代码]let bandoodle = []; for (let item of band) { let newItem = oodlify(item); bandoodle.push(newItem); } let floodleship = []; for (let item of fellowship) { let newItem = oodlify(item); floodleship.push(newItem); } [代码] 这的确可以完成我们的目标,但是这样写得有点累赘。我们可以重构一下以减少重复的代码。因此我们可以创建一个函数: [代码]function oodlifyArray(input) { let output = []; for (let item of input) { let newItem = oodlify(item); output.push(newItem); } return output; } let bandoodle = oodlifyArray(band); let floodleship = oodlifyArray(fellowship); [代码] 这样是不是好看多了。但是问题来了,如果我们要使用其他函数来操作这个数组的话呢? [代码]function izzlify(s) { return s.replace(/[aeiou]+/g, 'izzle'); } [代码] 这时,我们前面创建的[代码]oodlifyArray[代码]函数帮不了我们了。不过如果我们这时创建[代码]izzlifyArray[代码]函数的话,代码不就又有许多重复的部分了吗? [代码]function oodlifyArray(input) { let output = []; for (let item of input) { let newItem = oodlify(item); output.push(newItem); } return output; } function izzlifyArray(input) { let output = []; for (let item of input) { let newItem = izzlify(item); output.push(newItem); } return output; } [代码] 这两个函数是不是及其相似呢。 如果此时我们将其抽象成一个模式的话呢:我们希望传入一个数组和一个函数,然后映射每个数组元素,最后输出一个数组。这个模式就称为[代码]mapping[代码]: [代码]function map(f, a) { let output = []; for (let item of a) { output.push(f(item)); } return output; } [代码] 其实我们并不需要自己手动写[代码]mapping[代码]函数,因为JavaScript提供了内置的[代码]map[代码]函数给我们使用,此时我们的代码是这样的: [代码]let bandoodle = band.map(oodlify); let floodleship = fellowship.map(oodlify); let bandizzle = band.map(izzlify); let fellowshizzle = fellowship.map(izzlify); [代码] Reducing 此时[代码]map[代码]是很方便了,但是并不能覆盖我们所有的循环需要。 如果此时我们需要累计数组中的所有数组呢。我们假设有一个这样的数组: [代码]const heroes = [ {name: 'Hulk', strength: 90000}, {name: 'Spider-Man', strength: 25000}, {name: 'Hawk Eye', strength: 136}, {name: 'Thor', strength: 100000}, {name: 'Black Widow', strength: 136}, {name: 'Vision', strength: 5000}, {name: 'Scarlet Witch', strength: 60}, {name: 'Mystique', strength: 120}, {name: 'Namora', strength: 75000}, ]; [代码] 如果我们要找到[代码]strength[代码]最大的那个的元素的话,使用[代码]for...of[代码]循环是这样的: [代码]let strongest = {strength: 0}; for (hero of heroes) { if (hero.strength > strongest.strength) { strongest = hero; } } [代码] 如果此时我们想累计一下所有的[代码]strength[代码]的话,循环里面就是这样的了: [代码]let combinedStrength = 0; for (hero of heroes) { combinedStrength += hero.strength; } [代码] 这两个例子我们都需要初始化一个变量来配合我们的操作。合并两个例子的话就是这样的: [代码]function greaterStrength(champion, contender) { return (contender.strength > champion.strength) ? contender : champion; } function addStrength(tally, hero) { return tally + hero.strength; } // 例子 1 const initialStrongest = {strength: 0}; let working = initialStrongest; for (hero of heroes) { working = greaterStrength(working, hero); } const strongest = working; // 例子 2 const initialCombinedStrength = 0; working = initialCombinedStrength; for (hero of heroes) { working = addStrength(working, hero); } const combinedStrength = working; [代码] 此时我们可以抽象成这样一个函数: [代码]function reduce(f, initialVal, a) { let working = initialVal; for (item of a) { working = f(working, item); } return working; } [代码] 其实这个方法JavaScript也提供了内置函数,就是[代码]reduce[代码]函数。这时代码是这样的: [代码]const strongestHero = heroes.reduce(greaterStrength, {strength: 0}); const combinedStrength = heroes.reduce(addStrength, 0); [代码] Filtering 前面的[代码]map[代码]函数是将数组的全部元素执行同个操作之后输出一个同样大小的数组; [代码]reduce[代码]则是将数组的全部值执行操作之后,最终输出一个值。 如果此时我们只是需要提取几个元素到一个数组内呢?为了更好得解释,我们来扩充一下之前的例子: [代码]const heroes = [ {name: 'Hulk', strength: 90000, sex: 'm'}, {name: 'Spider-Man', strength: 25000, sex: 'm'}, {name: 'Hawk Eye', strength: 136, sex: 'm'}, {name: 'Thor', strength: 100000, sex: 'm'}, {name: 'Black Widow', strength: 136, sex: 'f'}, {name: 'Vision', strength: 5000, sex: 'm'}, {name: 'Scarlet Witch', strength: 60, sex: 'f'}, {name: 'Mystique', strength: 120, sex: 'f'}, {name: 'Namora', strength: 75000, sex: 'f'}, ]; [代码] 现在假设我们要做的两件事: 找到[代码]sex = f[代码]的元素 找到[代码]strength > 500[代码]的元素 如果使用[代码]for...of[代码]循环的话,是这样的: [代码]let femaleHeroes = []; for (let hero of heroes) { if (hero.sex === 'f') { femaleHeroes.push(hero); } } let superhumans = []; for (let hero of heroes) { if (hero.strength >= 500) { superhumans.push(hero); } } [代码] 由于有重复的地方,那么我们就把不同的地方抽取出来: [代码]function isFemaleHero(hero) { return (hero.sex === 'f'); } function isSuperhuman(hero) { return (hero.strength >= 500); } let femaleHeroes = []; for (let hero of heroes) { if (isFemaleHero(hero)) { femaleHeroes.push(hero); } } let superhumans = []; for (let hero of heroes) { if (isSuperhuman(hero)) { superhumans.push(hero); } } [代码] 此时就可以抽象成JavaScript内置的[代码]filter[代码]函数: [代码]function filter(predicate, arr) { let working = []; for (let item of arr) { if (predicate(item)) { working = working.concat(item); } } } const femaleHeroes = filter(isFemaleHero, heroes); const superhumans = filter(isSuperhuman, heroes); [代码] Finding [代码]filter[代码]搞定了,那么如果我们只要找到一个元素呢。 的确,我们同样可以使用[代码]filter[代码]函数完成这个目标,比如: [代码]function isBlackWidow(hero) { return (hero.name === 'Black Widow'); } const blackWidow = heroes.filter(isBlackWidow)[0]; [代码] 当然我们也同样会发现,这样的效率并不高。因为[代码]filter[代码]函数会过滤所有的元素,尽管在前面已经找到了应该要找到的元素。因此我们可以写一个这样的查找函数: [代码]function find(predicate, arr) { for (let item of arr) { if (predicate(item)) { return item; } } } const blackWidow = find(isBlackWidow, heroes); [代码] 正如大家所预期那样,JavaScript也同样提供了内置方法[代码]find[代码]给我们,因此我们最终的代码是这样的: [代码]const blackWidow = heroes.find(isBlackWidow); [代码] 总结 这些JavaScript内置的数组函数就是很好的例子,让我们学会了如何去抽象提取共同部分,以创造一个可以复用的函数。 现在我们可以用内置函数完成几乎所有的数组操作。分析一下,我们可以看出每个函数都有以下特点: 摒弃了循环的控制结构,使代码更容易阅读。 通过使用适当的方法名称描述我们正在使用的方法。 减少了处理整个数组的问题,只需要关注我们的业务代码。 在每种情况下,JavaScript的内置函数都已经将问题分解为使用小的纯函数的解决方案。通过学习这几种内置函数能让我们消除几乎所有的循环结构,这是因为我们写的几乎所有循环都是在处理数组或者构建数组或者两者都有。因此使用内置函数不仅让我们在消除循环的同时,也为我们的代码增加了不少地可维护性。 本文翻译自:JavaScript Without Loops
2020-03-11 - 关于tabBar,在js和json中都可以设置,实际运行的时候会不会有差异?
比如在app.js中 wx.setTabBarStyle({ color: '#999', selectedColor: '#FF0000' }) 和在app.js中写入 "tabBar": { "color": "#999", "selectedColor":"#FF0000", 在json中和在js中设置看上去是一样的效果,具体在程序运行上有没有差异? 两种写法会否对小程序的运行速度有影响,哪一种更好? 如果有其他影响,具体有哪些?
2020-09-21 - 初识选择器、颜色和字体
初步认识“作为WXML和WXSS桥梁”的选择器,以及颜色与字体。 名片初始代码,点此领取:https://share.weiyun.com/5ASBOw3 [视频]
2021-11-26 - 首页开发实战
带你实战腾讯课堂首页开发,详解布局王者flex。 本节结束代码:https://share.weiyun.com/5Eqz7TV [视频]
2021-11-26 - JavaScript函数与拓展
JavaScript进阶内容学习:函数与小程序特有的JS。 课程最终代码,点此领取:https://share.weiyun.com/5Kb7U6O [视频]
2021-11-26 - JavaScript基础语法
JavaScript从零到入门,本节带你学习编程语言常见的概念:变量、数据类型、条件语句、循环,并通过JavaScript编程来优化课堂首页开发。 [视频]
2021-11-26 - 公众号JS接口安全域名额度调整
为满足公众号的业务需求,方便开发者更灵活地使用和管理相关功能,2020年8月4日起,平台将公众号的JS接口安全域名额度由3个提升为5个,一个自然月内最多可修改次数由3次提升为5次。公众号开发者可在配置的域名下调用微信开放的JS接口。 功能入口:MP后台-设置-公众号设置-功能设置-JS接口安全域名。 [图片]
2020-08-04 - 获取用户信息方案介绍
背景 小程序一个比较重要的能力就是获取用户信息,也就是使用 [代码]wx.getUserInfo[代码] 接口。我们发现几乎所有的小程序都会调用这个接口。虽然我们在设计文档上有提出最好的设计是在真正要用户信息的情况下才去获取用户信息,不过很多开发者并没有按照我们的期望去做,导致用户在使用的时候有很多困扰。 归结起来有几点: 开发者在首页直接调用 [代码]wx.getUserInfo[代码] 进行授权,弹框有会使得一部分用户放弃小程序的使用。 开发者没有处理用户拒绝弹框的情况,有部分小程序强制要求用户授权头像昵称等信息才能继续使用小程序。 用户没有很好的方式重新授权,虽然在前几个版本我们增加了[代码]设置[代码]页面可以让用户选择重新授权,但是操作还是不够便捷。 开发者希望进到首页就获取到用户的[代码]unionId[代码],以便和之前已经关注了公众号的用户画像关联起来。 开发者默认将 [代码]wx.login[代码] 和 [代码]wx.getUserInfo[代码] 绑定使用,这个是由于我们一开始的设计缺陷和实例代码导致: [代码]getUserInfo[代码]必须通过[代码]wx.login[代码] 在后台生成[代码]session_key[代码] 后才能调用。 为了解决以上几点,我们更新了三个能力: 使用组件来获取用户信息,用户拒绝授权后也可以重新弹窗再次授权 若用户满足一定条件(下文有详细介绍),则可以用[代码]wx.login[代码] 获取到的code直接换到[代码]unionId[代码] [代码]wx.getUserInfo[代码] 不依赖 [代码]wx.login[代码] 就能调用得到数据。 获取用户信息组件介绍[代码][代码] 组件变化: [代码]open-type[代码] 属性增加 [代码]getUserInfo[代码] :用户点击时候会触发 [代码]bindgetuserinfo[代码] 事件。 新增事件 [代码]bindgetuserinfo[代码] :当 [代码]open-type[代码] 为 [代码]getUserInfo[代码] 时,用户点击会触发。可以从事件返回参数的[代码]detail[代码]字段中获取到和[代码]wx.getUserInfo[代码] 返回参数相同的数据。 示例: [代码]<[代码][代码]button[代码] [代码]open-type[代码][代码]=[代码][代码]"getUserInfo"[代码] [代码]bindgetuserinfo[代码][代码]=[代码][代码]"userInfoHandler"[代码][代码]> Click me </[代码][代码]button[代码][代码]>[代码]和 [代码]wx.getUserInfo[代码] 不同之处在于: API [代码]wx.getUserInfo[代码] 只会弹一次框,用户拒绝授权之后,再次调用将不会弹框 组件 [代码][代码][代码][代码] 由于是用户主动触发,不受弹框次数限制,只要用户没有授权,都会再次弹框 直接获取unionId考虑很多场景下,业务方申请userinfo授权主要为了获取unionid。我们鼓励开发者在不骚扰用户的情况下合理获得unionid,而仅在必要时才向用户弹窗申请使用昵称头像。为此,凡使用“获取用户信息组件”获取用户昵称头像的小程序,在满足以下全部条件时,将可以静默获得unionid。 在微信开放平台下存在同主体的App、公众号、小程序。 用户关注了某个相同主体公众号,或曾经在某个相同主体App、公众号上进行过微信登录授权。 getUserInfo 和 login很多开发者会把login和getUserInfo捆绑调用当成登录使用,其实login已经可以完成登录,可以建立账号体系了,getUserInfo只是获取额外的用户信息。 在login获取到code,然后发送到开发者后端,开发者后端再通过接口去微信后端换取到openid和sessionKey(并且现在会将unionid也一并返回)之后,然后把3rd_session返回给前端,就已经完成登录行为。而login行为是静默,不必授权的,不会对用户造成骚扰。 getUserInfo只是为了提供更优质的服务而存在,比如展示头像昵称,判断性别,通过unionId和其他公众号上已有的用户画像结合起来提供历史数据。所以不必在刚刚进入小程序的时候就强制要求授权。 推荐使用方法调用[代码]wx.login[代码] 获取[代码]code[代码],然后从微信后端换取到[代码]sessionKey[代码],用于解密[代码]getUserInfo[代码]返回的敏感数据。 使用[代码]wx.getSetting[代码] 获取用户的授权情况 如果用户已经授权,直接调用 API [代码]wx.getUserInfo[代码] 获取用户最新的信息 用户未授权,在界面中显示一个按钮提示用户登入,当用户点击并授权后就获取到用户的最新信息。 获取到用户数据后可以进行展示或者发送给自己的后端。 文档中的quickStart已经更新 特别注意为了给用户提供更好的小程序环境,我们约定在一段时间后(具体时间会做通知),若还出现以下情况(包括但不限于),将无法通过审核 初次打开小程序就弹框授权用户信息 未处理用户拒绝授权的情况 强制要求用户授权 已经上线的小程序不会受到影响。 FAQ Q: 除了 UserInfo 呢,比如说位置信息 --- ’风の诺言 . A: 其他授权信息不像用户信息那么高频繁,也基本是在使用时候才申请授权,所以没有同 UserInfo 一起给出。我们会先看看 UserInfo 的使用情况再结合具体场景我们会给出相应的方案 Q: 后台要维护用户信息 --- Azleal 我们的小程序业务是功能都需要授权才能使用的(也就是必须拿到unionid获取用户信息) --- elemeNT 我在小程序与服务号的数据需要互通,通过unionId来确定用户的唯一性,如果在用户进入小程序后不强制他授权,单凭一个openid来存储他的用户数据,在用户下次从服务号进入时。不就会产生重复数据吗?就没做到数据互通了 --- ﺭ并向你吐了趴口水ﺭ五年. 另外看到官方提到 要强制推行,我想说我们目前所有用户是通过unionid注册的。那么这些用户就不得不使用 openid重新登录 、注册一遍。更重要的是,之前他们的相关数据都会对应不上(因为你们也不允许强制用户登录授权) --- 羊毛 现在这种方案,不能满足我们的需求,我们的小程序,必须一进入就要获取他的信息,然后加载他的数据; --- 韩文 A: 调用`wx.login`已经可以获取到用户的登录态,已经可以做用户账号的管理。 UserInfo 中带的 UnionId 是额外的信息,没有它完全可以完成登录 对于需要和开发平台绑定的业务进行数据互通的情况,一个新用户进来没有互通数据的情况下也是可以体验到所有业务,那么对于没有授权unionId的用户,可以将其当成是新用户,当真正授权unionId之后再做绑定完全是可以的 Q: 我需要确保用户的唯一性,这样就必须取unionID,否则用户删除了小程序,或者换了设备, 下次再进来这个小程序,该用什么来区分是上次来过的用户呢?? --- WEI+ A: 如果你本身没有其他公众号、App、小程序,那么也就没必要拿到unionid,因为unionid是打通你在开放平台下所有应用的标识 如果只有一个小程序,用 openid 足以, openid 是一个用户对于一个小程序的标识,永远不变 Q: wx.getUserInfo 是网络请求,如果使用了 open-type = "getUserInfo",是否每次点击都会调接口? --- SouthernBox A: 是的,open-type="getUserInfo" 的作用以及内部实现基本和 wx.getUserInfo 一样 区别是一个开发者主动(拒绝一次不再弹窗),一个是用户主动(拒绝任意次都可以重新弹窗) Q: 比如有一个创建按钮,用户点击一次授权了,我已经获取到用户信息,再次点击就没必要再调用 getUserInfo 去网络请求了。 --- SouthernBox A: 可以参考文中 quickStart 的做法,如果已经授权了,那就可以把按钮隐藏,之后的授权直接用API wx.getUserInfo 调用(因为已经授权,所以也不会弹窗),用户也不会再点了 Q: 小程序是不是必须要用微信自带的授权才可以登录 ,能否不使用授权方式登录,用自己系统的api接口数据实现?这个会不会涉及到审核不过的问题??麻烦解答一下 谢谢了。 --- WEI+ A: 自己做登录不会涉及到审核问题。 不过不建议在没有原有账号体系的情况下让用户在小程序内注册,过重的行为会损失用户。 Q: 在小程序中有一个"我的"页面,这是属于会员页,如果用户要进入这个页面就必须授权。交互方式就是在用户未授权情况下整个页面只显示一个授权获取用户信息的button 按钮,这个需要用户自己去触发,算不算强制授权? --- ﺭ并向你吐了趴口水ﺭ五年. A: 强制授权是说如果用户如果不授权基本信息,连最基础的浏览功能都不提供(当然这个也是要分具体的业务场景,不会限制得太死板) 可以有更好的交互,参考下主流App,在未登录的时候点击【我的】页面,也不会直接要求登录,而是展示了一定的页面结构,同时给一个登录按钮(例如【携程】【京东】等),之后再在这个页面做操作的话可以弹一个登录页面或按钮提示用户登录是完全可以的。 上述所说的登录只是用户感知上的登录,从业务逻辑上用户其实在 wx.login 的时候已经完成登录了。 Q: 看了很多评论,有些人还是不知道为什么官方要这样做,我作为一个商家角度来说下。 --- Mr.J 1. 比如我们要做一些户外推广的二唯码,用户只看到了你的图片宣传单,扫描二唯码一打开就提示“需要获取你的个人信息,您是否允许”,你不要当自己是开发者当自己是一个正常人,看到这个提示我相信很多人的第一反应就是拒绝。如果第一步已经把你拒之门外,谈何营销? 2. 没有小程序之前,我们在公众号有很多用户,都绑定了unionid,有小程序之后我们考虑怎么让用户接受小程序,可以静默登录我觉得非常好,从公众号过来的用户可以直接就登录了,没有任何提示,完美的对接,这是一个很好的体验。 A: 说得很好,我们的这些改造不仅是为了开发者,同时也是为了这个生态下的用户考虑。希望开发者们也能站在用户的角度去思考怎么做一个产品。 Q: 我不明白为什么login 给多个unionid 为什么不行? unionid也不能算是个人信息吧,给多个unionid可以更方便开发者,而且很多情况下就不用调用getUserInfo了 --- candyTong 我们提个建议,能否直接开放unionid呢?这样也许会有许多小程序不需要再弹窗了。既一定程度保障了用户体验,也照顾到了我们开发者的体验。 --- 羊毛 A: 如果直接开放了unionid,就会出现这种情况:当你作为一个用户进入一个小程序,这个小程序并没要求你授权就直接把你的头像昵称显示出来(它之前把unionId对应的头像昵称都存了下来),但是这个小程序主体(open平台主体和公众平台主体并不相同)相关的任何一个应用你从来没用过,你会不会觉得很奇怪并且很不舒服,觉得自己在微信内的用户信息没有丝毫的保障? Q: 那有推荐的比较好的例子么?对于必须使用用户头像、昵称这些信息的小程序而言 --- 亚里士朱德 A: 首先,没有什么逻辑是一定要使用用户的头像、昵称才能work的。对于这个case,完全可以先用默认头像、匿名昵称先做替代,用户点击默认头像后就可以弹出授权信息,非常的水到渠成。 Q: 之前看了这个帖子一直在思考,如果是一进去需要回去用户的地理位置信息显示到地图上的呢?这样算不算是一进去就弹窗授权获取用户信息? --- 吴俊绩🤔 A: 地图的情况和获取用户信息不同,我们目前还没对地图的授权请求有所调整。当前不受上述策略的影响 Q: 对于开发者而言,小程序与公众号是同级的,只是不同的入口 但是这样的设计,公众号与小程序成了主从关系咯 --- log琥珀① A: 并无什么主从关系,只是多一个渠道让开发者可以更方便的获取到已经是该主体下用户的unionId
2017-08-07 - 老白之精磨小程序登陆bindgetuserinfo
前提.现在小程序必须要用户触发才可以实现授权与否的操作,且不允许在用户未充分了解风险的情况下,上来就抛给用户一个获取授权的登陆button. 所以,当用户决定使用,进而授权的时候,往往是引导用户到专门的授权页操作,完成授权后再返回来继续操作;这就把授权和其他的业务逻辑割裂开来,个人感觉用户体验不是很好 需求:以用户留言为例.如果用户未授权,则弹窗提示->获取授权信息->业务处理; 如果授权,则业务处理; [图片] 老白对着下面的图,好好磨一磨这个过程 1.用户点击button后,首先触发弹窗 [图片] 2.弹窗操作(拒绝或者允许)后,才执行函数体里的内容.这是和别的函数特别不一样的地方,小白一定要注意(划重点)... 2.1.如果用户拒绝: -> line10 处 log 不到内容 -> get不到授权信息 -> line25 给出用户拒绝授权后要进行的操作,如额外的 toast 或 modal 或导航到其他 page 2.2.如果用户同意: -> line10 处实际上就已经能够 log 到用户信息了.但是老白为了和业务逻辑合在一起,并没有在这里获取用户信息,而是通过获取用户的授权范围scope(line15)来获取用户信息(line18) -> 最后处理用户的留言业务逻辑(略) [图片] 3.button 可以像其他事件一样,添加 data- 属性 4.个人感觉这样做的话,用户体验还是不错的.很多时候,我们可能只对某一块感兴趣,我们不需要一个充分的,认真的,正规的登陆页面 5.老白很努力,大家多指教!
2020-03-15 - 请教编写js文件关于JavaScript的问题?
代码来源:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserInfo.html Page({ data: { canIUse: wx.canIUse('button.open-type.getUserInfo') }, onLoad: function() { // 查看是否授权 wx.getSetting({ success (res){ if (res.authSetting['scope.userInfo']) { // 已经授权,可以直接调用 getUserInfo 获取头像昵称 wx.getUserInfo({ success: function(res) { console.log(res.userInfo) } }) } } }) }, bindGetUserInfo (e) { console.log(e.detail.userInfo) } }) 问题: 1. data: { canIUse: wx.canIUse('button.open-type.getUserInfo') }, 其中data是对象,canIUse表示属性?wx.canIUse('button.open-type.getUserInfo')表示属性对应的值? 这段代码的具体含义是什么?能够实现什么功能? 2.bindGetUserInfo (e) { console.log(e.detail.userInfo)? ①bindGetUserInfo是函数名?e是参数?如果是参数,是实参还是形参? ②为什么一定要用e? ③e.detail又是什么意思? ④有种说法这是回调函数?可以解释一下具体含义或者出处? ⑤为什么看了JavaScript,感觉还是有点不太理解小程序js文件部分,下一步应该怎么学习?可以提供参考书目或者学习文章或者系统性的学习路径和方案吗? 尤其是是以上这两点问题 请大佬解答!
2020-09-17 - 在线答题小程序如新建题库
本文背景我开发的答题小程序经常需要对题库信息进行维护,本文主要整理具体的一个维护过程 本文内容本文以 考前练习册 小程序为示例,讲解具体如何创建一个新题库,在创建新题库之前,需要您确定下题库信息维护的集合名字,比如在 考前练习册 这个小程序里面 题库信息维护在 category 集合里面 在具体本文操作之前,请首先确定好题库信息维护的集合名字,不同的小程序,这个集合名字是不一样的 第一步:打开云开发控制台,切换到数据库面板,找到category,点击导出按钮,会得到一个json文件 [图片] 第二步:用文本编辑器打开这个json文件,windows建议安装notepad++,通过notepad++来打开,也可以用下面的推荐软件 推荐软件一:vscode, 下载地址为https://code.visualstudio.com/ 推荐软件二:notepad++ https://notepad-plus-plus.org/ f [图片] 第三步:删掉第二行、第三行,只保留第一行 [图片] 第四步:将第一行的001改为004,题库名字按照具体场景改为对应的名字,(假设目前新增的题库是第四个,编号规则建议采用三位数字,004) [图片] 第五步:打开云开发控制台,点击导入按钮,将刚才的json文件导入好 [图片] [图片] 第六步:导入完成后,会在集合category看到新增了一条记录004,恭喜您,您已完成题库的创建工作 [图片] f 本文总结本文具体以考前练习册小程序为例,描述了如何新增题库的操作过程,希望给使用该小程序的用户一个指引。
2020-09-03 - 小程序关联公众号策略调整
各位开发者,大家好。 目前,小程序需要与公众号关联,才可被使用在公众号自定义菜单、模板消息、客服消息等场景中。而公众号关联小程序时,需要小程序管理员确认,该环节增加了开发者之间的沟通成本。 为了降低公众号与小程序间的合作门槛,我们将调整小程序关联公众号策略如下: 公众号关联小程序将无需小程序管理员确认。 取消“小程序最多关联500个公众号”的限制。 若希望小程序在被关联时保留管理员确认环节,可前往“小程序管理后台-设置-基本设置-关联公众号设置”修改设置项。 公众号文章中可直接使用小程序素材,无需关联小程序。 开发者可在“小程序管理后台-设置-关联设置”中管理已关联的公众号。 微信团队 2019.04.04
2019-04-08 - 微信开放社区运营规范
微信开放社区运营规范 微信团队一直致力于将微信打造成一个强大的、全方位的服务工具。通过全面开放的能力,连接更多的开发者和用户。微信开放社区是微信为用户提供的内容问答社区。使用微信开放社区服务(以下简称“微信开放社区”或本“功能”),微信开放社区用户(以下简称“你”)必须阅读并遵守《微信公众平台服务协议》、《微信小程序平台服务条款》、《微信小程序平台运营规范》以及腾讯为此制定的专项规则等。 本《微信开放社区运营规范》(以下简称,本“运营规范”)是在上述协议及规则基础上进行解释和说明,相关内容和举例旨在帮助开发者更加清晰地理解和遵守相关协议和规则,以便能够更加顺利地在微信开放社区进行运营,而不是修改或变更上述协议及规则中的任何条款。 一、发布内容规范: 1. 你不得在微信开放社区发布、分享传播国家法律法规禁止的以下内容: 1.1 反对宪法所确定基本原则,危害国家安全、泄露国家秘密、颠覆国家政权、破坏国家统一、损害国家荣誉和利益。 1.2 反政府、反社会,或存在煽动性的涉政言论、散布谣言,扰乱社会秩序,破坏社会稳定。 1.3 煽动民族仇恨、民族歧视、破坏民族团结、破坏国家宗教政策、宣扬邪教和封建迷信。 1.4 展示人或动物被杀戮、致残、枪击、针刺或其他伤害的真实图片,描述暴力或虐待儿童的,或包含宣扬暴力血腥。 1.5 淫秽、色情或低俗信息,包括但不限于: 1.5.1 应用中含有淫秽、色情内容,如招嫖、寻找一夜情、性伴侣等内容; 1.5.2 传播以色情为目的的情色文字、情色视频、情色漫画等形式的内容; 1.5.3 传播非法色情交易的信息; 1.5.4 直接或隐晦表现性行为、具有挑逗性或者侮辱性内容,或以带有性暗示、性挑逗的语言描述性行为、性过程、性方式的; 1.5.5 应用中传播非法性药品、性保健品、性用品和性病治疗营销信息等相关内容的; 1.5.6 应用中传播相关部门禁止传播的色情和有伤社会风化的文字、音视频内容的。 1.6 赌博、竞猜和抽奖类信息,包括但不限于: 1.6.1 应用中传播以虚拟货币或真实货币直接进行押输赢、竞猜、参与赌博等内容的; 1.6.2 应用运营过程中可将游戏分数或金币等兑换成真实货币或实物奖励的; 1.6.3 应用运营过程中可根据玩家输赢结果进行抽水、分成等后果的; 1.6.4 其他被认定为宣扬赌博色彩的行为。 1.7 含有虚假、欺诈或冒充类内容,包括但不限于虚假红包、虚假活动、虚假宣传,仿冒腾讯官方或他人业务,可能造成微信用户混淆等内容的。 1.8 任何召集、鼓动犯罪或有明显违背社会善良风俗行为的内容。 1.9 任何违反《计算机信息网络国际联网安全保护管理办法》、《互联网信息服务管理办法》、《互联网电子公告服务管理规定》、《维护互联网安全的决定》或其他国家法律法规规定的内容。 2. 你不得在微信开放社区发布、分享和传播侵犯他人合法权利的信息: 2.1 包含公然侮辱或者诽谤他人,损害他人名誉或商誉内容的。 2.2 包含使用或揭露他人身份信息、照片、隐私,侵害他人肖像权、隐私权合法权益的。 2.3 未经授权,擅自使用他人商标、著作权以及其他侵犯他人知识产权内容的。 2.4 未经授权,擅自使用他人拥有商标权的标识、图像等内容。 2.5 未经授权,使用或传播他人的原创图文消息或其他有合法著作权权利的内容,侵犯他人知识产权。 2.6 依靠抄袭、模仿等手段使用他人拥有商标权或著作权权益的内容,侵犯他人权益的。 3. 你不得在微信开放社区发布、分享和传播违反平台相关规则的信息: 3.1 内容主要为营销、互推或广告用途,包括但不限于空白广告位、招商广告位或作为第三方平台通过应用中的营销、广告模块盈利等。 3.2 内容包含多级分销信息,发布分销信息诱导用户进行分享、传播或直接参与。 3.3 对用户产生误导、严重破坏用户体验,损害用户利益的谣言类内容。 3.4 传播骚扰信息、恶意营销和垃圾信息等内容。 3.5 其他涉及违法违规或违反平台相关协议、规则的内容。 二、使用行为规范 你不得在微信开放社区从事以下行为: 1. 恶意破坏微信开放社区正常秩序,包括但不限于恶意灌水、踩贴、刷流量、刷评论、利用自定义栏目或其他形式传播病毒、垃圾广告、非法信息等。 2. 未经腾讯许可或授权,擅自转载、或爬取微信开放社区文章和内容。 3. 重复发布干扰正常用户体验的内容。包括但不限于: 3.1 重复发表同一文章的; 3.2 重复的回答内容多次发布在不同问题下的; 3.3 频繁发布难以辨识涵义影响阅读体验的字符、数字等无意义乱码的; 3.4 骚扰他人,以评论、@他人、私信等方式对他人反复发送重复或者相似的诉求。 4. 发表不符合版面主题,或者无内容的灌水内容、或者发表色情,猥亵,谩骂、包含人身攻击,诽谤等的内容。 5. 使用不雅或不恰当ID和昵称,头像,个性签名等。 6. 从事违反与腾讯签订的、任何形式的服务协议、平台协议、功能协议的行为。 7. 从事违反腾讯为相关软件、服务、功能等而制定的管理、运营规范、规则的行为。 8. 从事非法商业活动或任何违反国家法律法规的行为。 三、服务商规范 1. 服务平台入驻规则 1.1 服务平台服务商,指为微信小程序提供模版开发、定制化开发、插件、借口能力等服务的服务提供方,包括但不限于具有第三方平台的平台型服务商和定制型服务商。 1.2 任何有意愿为小程序提供开发服务的开发者/企业都可以申请成为小程序服务商。目前阶段,满足以下条件的服务商,经平台审核后可接入微信开放社区-服务平台,小程序用户即可在服务平台中获取关服务商信息: (1)符合上述1.1条定义; (2)在微信开放社区中具有关联第三方平台的企业主页; (3)两个自然周内,单个服务商累计7天符合要求,可在下两个自然周内被搜出。 上述条件中,“第三方平台”[1]为全网发布并经审核通过,并应满足以下条件: (1)服务商业务活跃性:短期内授权一定数量的小程序,并发布上线; (2)小程序活跃度:已上线的小程序保证持续用户活跃; (3)服务商小程序预审能力:无不良审核记录。 1.3 平台有权根据产品策略和业务发展需求,对上述服务商接入的要求和规范进行不时地调整。对于不满足条件的服务商,微信有权进行动态调整。 2. 服务商行为规范 2.1 服务商入驻微信开放社区服务平台,不视为微信向你提供任何授权或进行任何合作。你不得以微信、微信代理商或微信官方合作伙伴的名义从事任何经营活动。你保证,不得以微信或微信代理商的名义拓展合作伙伴或代理商,未经微信同意,不得对外使用“微信”、“微信公众平台”、“微信开放平台”和“微信开放社区”的品牌和LOGO,或进行任何造成或可能造成微信用户混淆的对外宣传行为。 2.2 服务商不得从事或间接协助从事以下任一活动,否则平台可能会对你作出处理,终止合作,追究服务商的法律责任: 2.2.1 在企业主页等位置存在夸大、虚假、承诺性、绝对化等违反广告法等法律法规的描述的; 2.2.2 以微信、微信代理商或微信官方合作伙伴的名义从事经营活动或对外宣传的; 2.2.3 将你在平台的技术资源等转包、分包、转让、有偿或无偿提供给其他主体,或者以代理、加盟等形式拓展合作伙伴并收取费用的; 2.2.4 把服务商资源用于《微信开放平台公众帐号第三方平台开发者服务协议》和本规则约定范围以外的用途的; 2.2.5 服务态度恶劣,陈述有误引起小程序商户误解,或未向商户提供经承诺的服务的; 2.2.6 虚构事实,隐瞒真相,出现违规行为时不配合或阻碍平台正常调查的; 2.3 服务商不得在主页等位置,发布任何干扰微信公众平台正常运营,或侵犯其他用户或第三方合法权益的内容,否则平台可能会对你作出处理。 2.4 服务商使用服务平台向小程序用户提供服务,须严格遵守适用的法律法规。 2.5 服务商入驻服务平台,不视为服务商获得平台的任何授权,也不代表平台对服务商的能力、商誉进行任何形式、任何种类的明示或暗示的推荐或担保,包括但不限于商业适售性、特定用途适用性等。服务商对于其在服务平台向小程序用户提供服务的行为必须自行承担相应法律责任和风险。 四、违规处理 1. 如你违反上述使用规范,微信开放社区有权视开发者的违规程度给予警告、删帖、暂停账号使用、注销帐号等处罚措施,并依法追究你的法律责任。 2. 如服务商违反上述服务商规范,在平台收到对你的违规投诉或平台知悉你存在违规情形后,平台将向你发送电子邮件或站内信或通过其他有效通知方式,服务商应在平台发出通知之日起3个工作日内提供书面回复,如服务商认为违规行为不成立或已整改,应提供相应证明;如服务商未能在限定期间内书面回复,或虽提出异议但未能提供充分证据,视为违规行为成立。平台有权依据相关事实及服务商书面回复内容(如有)进行独立判断,并以电子邮件形式将认定结果进行告知。[T1] 五、免责声明 1. 微信开放社区不对你发表的内容、回答或评论的正确性进行保证。你在微信开放社区发表的内容仅表明其个人的立场和观点,并不代表微信的立场或观点。作为内容的发表者,你需自行对所发表内容负责,因所发表内容引发的一切纠纷,由你承担全部法律及连带责任。微信不承担任何法律及连带责任。 2. 微信开放社区知识库内容(即带有微信官方标识的内容)为微信开放社区官方发布内容。你清楚了解并同意,该等内容为微信开放社区根据平台运营情况或相关行业经验,在特定时间针对某具体问题提供的参考性内容,该等内容非实时更新,亦可能过期失效,仅供用户参考。未经腾讯许可或授权,用户不得擅自转载或爬取微信开放社区知识库内容。 3. 服务商入驻服务平台,不视为服务商获得平台的任何授权,也不代表平台对服务商的能力、商誉进行任何形式、任何种类的明示或暗示的推荐或担保,包括但不限于商业适售性、特定用途适用性等。服务商必须自行对其通过服务平台提供的服务承担相应法律责任和风险。 4. 你理解并同意,在使用微信开放社区时,需自行承担如下腾讯不可掌控的风险,包括但不限于: 4.1 由于受到计算机病毒、木马或其他恶意程序、黑客攻击的破坏等不可抗拒因素可能引 起的信息丢失、泄漏等损失和风险; 4.2 用户或腾讯的电脑软件、系统、硬件和通信线路出现故障导致的服务终端、数据丢失以及其他的损失和风险; 4.3 用户操作不当或通过非腾讯授权的方式使用本服务带来的损失和风险; 4.4 用户发布的内容被他人转发、分享,因此等传播可能带来的风险和责任; 4.5 由于网络信号不稳定、网络服务中断或其他原因所引起的无法使用微信开放社区、页面打开速度慢等风险; 4.6 其他腾讯无法控制或合理预见的情形。 六、遵守当地法律监管 1. 你在使用微信开放社区服务的过程中应当遵守当地相关的法律法规,并尊重当地的道德和风俗习惯。如果你的行为违反了当地法律法规或道德风俗,你应当为此独立承担责任。 2. 你应避免因使用本服务而使腾讯卷入政治和公共事件,否则腾讯有权暂停或终止对你的服务。 七、动态文档 这是一份动态更新的文档,我们会根据新出现的问题、相关法律法规更新或产品运营的需要来对其内容进行修改并更新,制定新的规则,保证微信用户的体验。你应能反复查看以便获得最新信息,请定期了解更新情况。 微信团队
2019-12-27 - (6)微信登录能力优化
小程序和小游戏内的用户登录,我们推荐使用以下两种方式获取用户信息: ▷ 按钮组件的登录方式,用户主动点击按钮可以拉起用户授权弹框,获取用户头像、昵称等信息; ▷ 在不获取用户信息的情况下,可展示用户头像昵称。 用户在没有任何操作的情况直接弹出授权的登录方式将逐渐不再支持,受影响的有 wx.getUserInfo 接口,以及 wx.authorize 接口传入 scope="scope.userInfo" 的情况。 1 为什么平台要做接口调整? 我们提供了 wx.login 和 wx.getUserInfo 接口,用于获取用户的 openID 和基本信息。 推出这两个接口的初衷是希望:当用户使用小程序时,只有访问到真正需要登录的页面,才需要授权并登录。 但在实际应用中,我们发现很多开发者在打开小程序时直接弹出授权框,如果用户点击拒绝授权,无法使用小程序。 在没有任何提示和背景的情况下,直接弹框想要获得用户信息授权,用户脑子里可能会闪过几个哲学问题: 你是谁? 我在哪里? 我为什么要同意? …… 相当一部分用户下意识会点击“拒绝“授权——这样不合理的登录流程既造成了用户的困扰,还使得用户流失。 用户通过小程序可以快速获取服务,因此在访问小程序的第一个页面非常重要。 对于一个互联网产品而言,第一个页面决定了用户对这个产品的认知,用户会选择是否继续使用这个产品。 一个优秀的互联网产品,能够给用户留下一个好的第一印象,用户可以快速了解你的产品,接收到你想要传递的服务信息,从而产生相应的操作行为。 一个优秀的小程序会吸引用户在小程序里进行探索,完成你期望他们去做的事,比如会员注册、商品购买等。 试想一下如果一个品牌的商品官网,一进入要求用户登录才能查看产品信息是什么感觉呢? 因此良好的用户登录体验非常重要。 2 如何设计登录流程? 用户打开小程序时,看第一眼的时候,开发者需要专注以下两个目标: ▷ 精准快速地传达产品理念,开发者要让用户能够快速了解自己的产品和服务; ▷ 将用户流量进行转化,让用户能方便操作或者交易。 一般而言,用户打开小程序后看到的第一个页面,先不要直接弹出授权框,第一个页面可以包含以下内容: ▷ 展示你的小程序功能(如产品、服务、活动等) ,让用户清晰地知道小程序是做什么用的,这些内容可以是你的精选内容; ▷ 激发用户的探索欲,通过描述或者图片吸引用户注意力; ▷ 按照自己的产品目标,给用户提供清晰明确的下一步操作(查看详情、购买等)。 如果某些特殊小程序在使用前一定需要用户登录,或者已经进行到需要用户登录的操作时,可以将 button 组件(其中 open-type 属性指定为 getUserInfo)放置到页面中,页面上可以大致说明以下要点: 为什么需要我授权? 需要我什么信息? 授权后我得到什么好处呢? 接下来在页面上放置一个明显的登录按钮, 建议这个页面上不要有额外的点击区域,以免分散用户注意力,让用户专注于登录这件事情。 3 简单的开发建议 1 当用户打开小程序时访问第一个页面时,先通过 wx.login,获取用户 openID 。这时无需弹框授权,开发者拿到 openID 可以建立自身的帐号 ID。 2 在第一步中,拿到 openID 后,判断是新用户还是老用户。如果是老用户,可以直接登录;如果是新用户,可先在小程序首页展示你的信息服务,让用户对这个小程序有大概的了解,再引导用户进行下一步的操作。 3 当需要获取用户头像昵称的时候,对用户展示一个登录页面,这个页面只有一个最重要的操作,引导用户进行登录。 小程序中,在页面中加入一个 button 按钮,并将 open-type 属性设置为 getUserInfo 。 以小程序为例: 微信登录 对于功能较简单的小程序或者小游戏而言,如果不是必须要获得用户的头像昵称,建议可先通过wx.login 拿到 openID 后,使用 open-data 方式或者开放数据域的方式展示用户信息,整个过程都无需用户授权。 Tips: 1、在用户登录后,开发者需要存储用户的 unionID,而且建议只把 unionID 作为互通的用户标识,不要直接使用 unionID 作为用户 ID。因为一旦小程序迁移到其他的开放平台下,unionID 是会改变的,而 openID 是不变的。 2、用 button 组件的方式获得用户授权后,调用 wx.getUserInfo 就可以直接获取用户信息。这个的意义在于获取过一次之后,用户有可能改昵称头像,因此为了及时同步,最好是定期获取用户信息。 这里两个小提示: ▷ 定期使用 wx.getUserInfo 获取并更新用户的信息; ▷ 如果用户授权过一次之后,又在设置中关掉了授权(或者本地删除了小程序),那这时再调用 wx.getUserInfo 也是不会成功的,需要重新获得授权。 相关开发文档参考: ▷ 小程序 1、小程序 wx.login 2、button 组件,并将 open-type 指定为 getUserInfo 类型,获取用户基本信息 3、open-data 展示用户基本信息 ▷ 小游戏 1、小游戏 wx.login 2、用户信息按钮 UserInfoButton 3、开放数据域下的展示用户信息
2018-08-17 - (4)获取用户信息
背景 我们发现大部分小程序都会使用 [代码]wx.getUserInfo[代码] 接口,来获取用户信息。原本设计这个接口时,我们希望开发者在真正需要用户信息的情况下才去调取这个接口,但很多开发者会直接调用这个接口,导致用户在使用小程序的时候产生困扰,归结起来有几点: 开发者在小程序首页直接调用 [代码]wx.getUserInfo[代码] 进行授权,弹框获取用户信息,会使得一部分用户点击“拒绝”按钮。 在开发者没有处理用户拒绝弹框的情况下,用户必须授权头像昵称等信息才能继续使用小程序,会导致某些用户放弃使用该小程序。 用户没有很好的方式重新授权,尽管我们增加了[代码]设置[代码]页面,可以让用户选择重新授权,但很多用户并不知道可以这么操作。 此外,我们发现开发者默认将 [代码]wx.login[代码] 和 [代码]wx.getUserInfo[代码] 绑定使用,这个是由于我们一开始的设计缺陷和实例代码导致的([代码]wx.getUserInfo[代码] 必须通过 [代码]wx.login[代码] 在后台生成 [代码]session_key[代码]后才能调用)。同时,我们收到开发者的反馈,希望用户进入小程序首页便能获取到用户的 [代码]unionId[代码],以便识别到用户是否以前关注了同主体公众号或使用过同主体的App 。 为了解决以上问题,针对获取用户信息我们更新了三个能力: 1.使用组件来获取用户信息 2.若用户满足一定条件,则可以用[代码]wx.login[代码] 获取到的[代码]code[代码]直接换到[代码]unionId[代码] 3.[代码]wx.getUserInfo[代码] 不需要依赖 [代码]wx.login[代码] 就能调用得到数据 获取用户信息组件介绍 [代码][代码] 组件变化: [代码]open-type [代码]属性增加 [代码]getUserInfo[代码] :用户点击时候会触发 [代码]bindgetuserinfo[代码] 事件。 新增事件 [代码]bindgetuserinfo[代码] :当 [代码]open-type[代码]为 [代码]getUserInfo[代码] 时,用户点击会触发。可以从事件返回参数的 [代码]detail[代码] 字段中获取到和 [代码]wx.getUserInfo[代码] 返回参数相同的数据。 示例: [代码]<button open-type="getUserInfo" bindgetuserinfo="userInfoHandler"> Click me button>[代码]和 [代码]wx.getUserInfo[代码] 不同之处在于: 1.API [代码]wx.getUserInfo[代码] 只会弹一次框,用户拒绝授权之后,再次调用将不会弹框; 2.组件 [代码][代码][代码][代码] 由于是用户主动触发,不受弹框次数限制,只要用户没有授权,都会再次弹框。 通过获取用户信息的组件,就可以解决用户再次授权的问题。 直接获取unionId开发者申请 [代码]userinfo[代码] 授权主要为了获取 [代码]unionid[代码],我们鼓励开发者在不骚扰用户的情况下合理获得[代码]unionid[代码],而仅在必要时才向用户弹窗申请使用昵称头像。为此,凡使用“获取用户信息组件”获取用户昵称头像的小程序,在满足以下全部条件时,将可以静默获得 [代码]unionid[代码]: 1.在微信开放平台下存在同主体的App、公众号、小程序。 2.用户关注了某个相同主体公众号,或曾经在某个相同主体App、公众号上进行过微信登录授权。 这样可让其他同主体的App、公众号、小程序的开发者快速获得已有用户的数据。 不依赖登录的用户信息获取某些工具类的轻量小程序不需要登录行为,但是也想获取用户信息,那么就可以在 [代码]wx.getUserInfo[代码] 的时候加一个参数 [代码]withCredentials: false[代码] 直接获取到用户信息,可以少一次网络请求。 这样可以在不给用户弹窗授权的情况下直接展示用户的信息。 最佳实践 1.调用 [代码]wx.login[代码] 获取 [代码]code[代码],然后从微信后端换取到 [代码]session_key[代码],用于解密 [代码]getUserInfo[代码]返回的敏感数据。 2.使用 [代码]wx.getSetting[代码] 获取用户的授权情况 1) 如果用户已经授权,直接调用 API [代码]wx.getUserInfo[代码] 获取用户最新的信息; 2) 用户未授权,在界面中显示一个按钮提示用户登入,当用户点击并授权后就获取到用户的最新信息。 3.获取到用户数据后可以进行展示或者发送给自己的后端。 One More Thing 除了获取用户方案介绍之外,再聊一聊很多初次接触微信小程序的开发者所不容易理解的一些概念: 1.关于OpenId和UnionId [代码]OpenId[代码] 是一个用户对于一个小程序/公众号的标识,开发者可以通过这个标识识别出用户。 [代码]UnionId[代码] 是一个用户对于同主体微信小程序/公众号/APP的标识,开发者需要在微信开放平台下绑定相同账号的主体。开发者可通过[代码]UnionId[代码],实现多个小程序、公众号、甚至APP 之间的数据互通了。 同一个用户的这两个 ID 对于同一个小程序来说是永久不变的,就算用户删了小程序,下次用户进入小程序,开发者依旧可以通过后台的记录标识出来。 2.关于 getUserInfo 和 login 很多开发者会把 [代码]login[代码] 和 [代码]getUserInfo[代码] 捆绑调用当成登录使用,其实 [代码]login[代码] 已经可以完成登录,[代码]getUserInfo[代码] 只是获取额外的用户信息。 在 [代码]login[代码] 获取到 [代码]code[代码] 后,会发送到开发者后端,开发者后端通过接口去微信后端换取到 [代码]openid[代码] 和[代码]sessionKey[代码](现在会将 [代码]unionid[代码] 也一并返回)后,把自定义登录态 [代码]3rd_session[代码]返回给前端,就已经完成登录行为了。而 [代码]login[代码] 行为是静默,不必授权的,用户不会察觉。 [代码]getUserInfo[代码] 只是为了提供更优质的服务而存在,比如展示头像昵称,判断性别,开发者可通过 [代码]unionId[代码] 和其他公众号上已有的用户画像结合来提供历史数据。因此开发者不必在用户刚刚进入小程序的时候就强制要求授权。 可以在官方的文档中看到 [代码]login[代码] 的最佳实践: [图片] Q & A Q1: 为什么 login 的时候不直接返回 openid,而是要用这么复杂的方式来经过后台好几层处理之后才能拿到? A: 为了防止坏人在网络链路上做手脚,所以小程序端请求开发者服务器的的请求都需要二次验证才是可信的。因为我们采取了小程序端只给 [代码]code[代码] ,由服务器端拿着 [代码]code[代码] 和 [代码]AppSecrect[代码] 去微信服务器请求的方式,才会给到开发者对应的[代码]openId[代码] 和用于加解密的 [代码]session_key。[代码] Q2: 既然用户的[代码]openId[代码] 是永远不变的,那么开发者可以使用[代码]openId[代码] 作为用户的登录态么? A: 不行,这是非常危险的行为。因为 [代码]openId[代码] 是不变的,如果有坏人拿着别人的 [代码]openId[代码] 来进行请求,那么就会出现冒充的情况。所以我们建议开发者可以自己在后台生成一个拥有有效期的 [代码]第三方session[代码] 来做登录态,用户每隔一段时间都需要进行更新以保障数据的安全性。 Q3: 是不是用户每次打开小程序都需要重新[代码]login[代码]? A: 不必,可以将登录态存入[代码]storage[代码]中,用户再次登录就可以拿[代码]storage[代码] 里的登录态做正常的业务请求,只有当登录态过期了之后才需要重新[代码]login[代码] 。这样子做一则可以减少用户等待时间,二则可以减少网络带宽。 目前微信的[代码]session_key[代码] 有效期是三天,所以建议开发者设置的登录态有效期要小于这个值。
2018-08-17 - 对weui的新认识
从不同的角度对weui有不同的认识,比如你问一个做小程序的,她会说weiui不就是一套小程序UI组件? 你问一个前端开发,她会说weui不就是一套H5 UI样式 如果您能认识到这一点,说明已经跟我达成一致认识了, weui https://github.com/Tencent/weui/wiki 小程序Weui https://developers.weixin.qq.com/miniprogram/dev/extended/weui/quickstart.html 在前几天我试图尝试用half-screen-dialog,但是里面内容是定制的,当时就是通过样式的形式实现的,因为小程序weui不支持定制内容。 认识这一点非常重要
2020-05-19 - weui-css如何修改样式,有没有详细的文档
我现在用weui-css写界面,照例子抄确实很方便,但是有时候想改一下组件的尺寸但不知道从何下手(像这些weui-form \weui-cells__group weui-cells__group_form\weui-cell weui-cell_active)用官方文档的源码修改,有时候总是莫名其妙的空出一大块,只有试着margin 负值才能消掉,但这也不办法呀,怕产生连锁反应。求高手赐教。微信/qq:309867165
2020-06-05 - 微信小程序UI组件库合集
UI组件库合集,大家有遇到好的组件库,欢迎留言评论然后加入到文档里。 第一款: 官方WeUI组件库,地址 https://developers.weixin.qq.com/miniprogram/dev/extended/weui/ 预览码: [图片] 第二款: ColorUI:地址 https://github.com/weilanwl/ColorUI 预览码: [图片] 第三款: vantUI(又名:ZanUI):地址 https://youzan.github.io/vant-weapp/#/intro 预览码: [图片] 第四款: MinUI: 地址 https://meili.github.io/min/docs/minui/index.html 预览码: [图片] 第五款: iview-weapp:地址 https://weapp.iviewui.com/docs/guide/start 预览码: [图片] 第六款: WXRUI:暂无地址 预览码: [图片] 第七款: WuxUI:地址https://www.wuxui.com/#/introduce 预览码: [图片] 第八款: WussUI:地址 https://phonycode.github.io/wuss-weapp/quickstart.html 预览码: [图片] 第九款: TouchUI:地址 https://github.com/uileader/touchwx 预览码: [图片] 第十款: Hello UniApp: 地址 https://m3w.cn/uniapp 预览码: [图片] 第十一款: TaroUI:地址 https://taro-ui.jd.com/#/docs/introduction 预览码: [图片] 第十二款: Thor UI: 地址 https://thorui.cn/doc/ 预览码: [图片] 第十三款: GUI:https://github.com/Gensp/GUI 预览码: [图片] 第十四款: QyUI:暂无地址 预览码: [图片] 第十五款: WxaUI:暂无地址 预览码: [图片] 第十六款: kaiUI: github地址 https://github.com/Chaunjie/kai-ui 组件库文档:https://chaunjie.github.io/kui/dist/#/start 预览码: [图片] 第十七款: YsUI:暂无地址 预览码: [图片] 第十八款: BeeUI:git地址 http://ued.local.17173.com/gitlab/wxc/beeui.git 预览码: [图片] 第十九款: AntUI: 暂无地址 预览码: [图片] 第二十款: BleuUI:暂无地址 预览码: [图片] 第二十一款: uniydUI:暂无地址 预览码: [图片] 第二十二款: RovingUI:暂无地址 预览码: [图片] 第二十三款: DojayUI:暂无地址 预览码: [图片] 第二十四款: SkyUI:暂无地址 预览码: [图片] 第二十五款: YuUI:暂无地址 预览码: [图片] 第二十六款: wePyUI:暂无地址 预览码: [图片] 第二十七款: WXDUI:暂无地址 预览码: [图片] 第二十八款: XviewUI:暂无地址 预览码: [图片] 第二十九款: MinaUI:暂无地址 预览码: [图片] 第三十款: InyUI:暂无地址 预览码: [图片] 第三十一款: easyUI:地址 https://github.com/qq865738120/easyUI 预览码: [图片] 第三十二款 Kbone-UI: 地址 https://wechat-miniprogram.github.io/kboneui/ui/#/ 暂无预览码 第三十三款 VtuUi: 地址 https://github.com/jisida/VtuWeapp 预览码: [图片] 第三十四款 Lin-UI 地址:http://doc.mini.talelin.com/ 预览码: [图片] 第三十五款 GraceUI 地址: http://grace.hcoder.net/ 这个是收费的哦~ 预览码: [图片] 第三十六款 anna-remax-ui npm:https://www.npmjs.com/package/anna-remax-ui/v/1.0.12 anna-remax-ui 地址: https://annasearl.github.io/anna-remax-ui/components/general/button 预览码 [图片] 第三十七款 Olympus UI 地址:暂无 网易严选出品。 预览码 [图片] 第三十八款 AiYunXiaoUI 地址暂无 预览码 [图片] 第三十九款 visionUI npm:https://www.npmjs.com/package/vision-ui 预览码: [图片] 第四十款 AnimaUI(灵动UI) 地址:https://github.com/AnimaUI/wechat-miniprogram 预览码: [图片] 第四十一款 uView 地址:http://uviewui.com/components/quickstart.html 预览码: [图片] 第四十二款 firstUI 地址:https://www.firstui.cn/ 预览码: [图片]
2023-01-10 - weui内置扩展库使用步骤
更新最新的 nightly 版开发者工具 在app.json里新增 “useExtendedLib”: { “weui”: true } 在使用的页面json文件应用组件,比如在index.json里 { “navigationStyle”:“custom”, “usingComponents”: { “mp-navigation-bar”:“weui-miniprogram/navigation-bar/navigation-bar” } } wxml文件使用组件,比如在index.wxml里 <mp-navigation-bar title=“首页”></mp-navigation-bar> 验证有无生效。 示例代码片段:https://developers.weixin.qq.com/s/zB6mFrmc7elr 备注: 文档里说目前暂不支持在分包中引用,但据代码测试,分包也能使用。 各位觉得有用的话,给个赞呗。
2020-10-28 - WeUI官方组件库:助力小程序高效设计与开发
提起 WeUI,相信大家都不陌生,WeUI 是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信内网页和微信小程序量身设计,令用户的使用感知更加统一。 不过,对于 WeUI 样式库,开发者就有疑问了。 [图片] [图片] 我们来看看 WeUI 组件库到底有什么可用的 UI 组件呢?WeUI 样式库有的各个元素,WeUI 组件库是基于 WeUI 样式库做了组件化处理,开发者可以便捷的使用,无需考虑组件层面的逻辑问题。 [图片] 有了心动的组件之后,大家肯定想知道 WeUI 组件库是怎么使用的。 [图片] 要使用 WeUI,首先要把 WeUI 引入我们的小程序项目,引入 WeUI 的方式有以下两种,使用其中一种即可~ 方法一:通过 useExtendedLib 扩展库 的方式引入,这种方式引入的组件将不会计入代码包大小。(推荐👍) 方法二:可以通过 npm方式下载构建,npm 包名为 weui-miniprogram。 与方法一不同,npm 引入的方式需要多操作一步,在 app.wxss 中引用 weui.wxss。 // app.wxss @import '/miniprogram_npm/weui-miniprogram/weui-wxss/dist/style/weui.wxss'; [图片] 引入之后,我们就要开始来使用了,WeUI 组件库是基于小程序自定义组件构建的,所以使用是以自定义组件的形式来使用。 下面通过几个例子来感受下 WeUI 组件库的使用。 由于是自定义组件的形式,所以使用组件都需要在页面配置中引入,像这样: // page.json { "usingComponents": { "mp-half-screen-dialog": "weui-miniprogram/half-screen-dialog/half-screen-dialog", "mp-searchbar": "weui-miniprogram/searchbar/searchbar" } } 引入组件之后,就可以直接在 wxml 中使用了,当然,为了让开发者接入更加简便,我们也加入了做了一些常见的实用性功能。 半屏弹窗 小程序提供了 wx.showModal、wx.showToast 供开发者进行页面交互,在开发过程中,可能需要自定义按钮相关的内容,所以 WeUI 提供了半屏弹窗让开发者可以有更多的自定义空间。 我们来看下代码,使用很简单,直接使用 mp-half-screen-dialog,配置相关属性即可。 // page.wxml <mp-half-screen-dialog bindbuttontap="buttontap" show="{{show}}" mask-closable="{{false}}" title="测试标题B" sub-title="测试标题B的副标题" desc="辅助描述内容,可根据实际需要安排" tips="辅助提示内容,可根据实际需要安排" buttons="{{buttons}}"> </mp-half-screen-dialog> // page.js data配置 buttons: [ { type: 'default', className: '', text: '辅助操作', value: 0 }, { type: 'primary', className: '', text: '主操作', value: 1 } ] 来看下半屏弹窗的效果~ u1s1,这交互体验真的爱了😍 [图片] Form 表单校验 Form 表单这里,除了基础的的功能之外,WeUI 组件库还提供了表单校验的能力,通过 rules 规则的配置(支持长度、手机号码、电子邮件、url 链接地址等),轻松解决表单校验问题。 [图片] 左滑删除 相比 Web 端,手机端的操作按钮更多的是通过⬅️左滑等来实现,考虑到左滑删除的普遍性,WeUI 组件库也是支持的。 在 mp-slideview 组件中设置 buttons属性即可。 [图片] 搜索组件 同样是基本功能的搜索,WeUI 组件库也封装了搜索组件,开发者只需配置搜索结果即可拥有搜索功能~ [图片] 除了这些组件之外,WeUI 组件库还提供了很多实用的组件,包括基础的 icon、loading,表单的 uploader、cell 等等。 [图片] 伴随客户端、小程序对 DarkMode 的支持,WeUI 组件库也同步适配 DarkMode 的模式,让 WeUI 组件库的使用同学可以快速适配 DarkMode。 在根结点(或组件的外层结点)增加属性 data-weui-theme="dark" ,即可把 WeUI 组件切换到 DarkMode 的表现,如: <view data-weui-theme="dark"> ... </view> [图片] 最后,如果想体验 WeUI 组件库的效果,欢迎点击下方小程序示例体验👏及接入使用,使用过程中如有建议或者疑问,欢迎到微信开放社区与我们交流。 [图片]
2020-05-21 - 基于云开发的商城小程序开发之路系列之准备篇
本系列文章主要记录基于云开发 的商城小程序的开发过程以及开发过程中遇到的问题 写在之前:正式接触微云开发是在19年10月份,刚开始只是把它当做一个新的知识去了解,偶尔也会写个代码片段测试单个功能。随着了解的深入慢慢的喜欢上了这种新的开发模式,然后就按照商城的需求一点一点的找云开发的替代方案。我记得当时遇到的问题包括:定时发送模板消息 、支付回调地址 、商城后台管理等等这一些列问题,总终这些问题都一一得到解决。故而在新项目开始之际记录开发 过程中的点点滴滴,一来重新梳理思路 逻辑,二来供广大开发者参考以便指点雅正。 开发准备1,新建小程序填入APPID 使用云开发 [图片] 2,开通 云开发 [图片] [图片] 创建环境,等待初始化 [图片] 3,回调IDE 删除无用文件 [图片] 移除多余文件后空白项目 [图片] 4,添加小程序常用工具了,搭建简单的开发框架 [图片] config.js const userUtils = require("../utils/userUtils.js") const user = require("../mode/user.js") module.exports = { no_data: "cloud://xxxxx/no_data.png", faild: "cloud://xxxxx/net_error.png", net_error: "cloud://xxxxx/net_error.png", no_order: "cloud://xxxxx/res/no_order.png", pageSize: 10, minClickTime: 500, order_out_time: 60 * 1000 * 30, ok: "cloud.callFunction:ok", env: "xxxxx",//环境id tmpid_order_cancel: "xxxxx", //订单取消模板消息id tmpid_order_send: "xxxxx", //订单发货 } user .js module.exports = { USER_ID: "user_id", HEIGHT: "height", MAIN_HEIGHT: "main_height", USER_NAME: "user_name", AVATAR: "avatar", SX: "sx", } file.js let path="" function setPath(locationPath) { path=locationPath } function uploadFile(fileList, index, afterUpSu, posi) { const imgItem = fileList[index] if (imgItem.is_up) { toNext(fileList, index, afterUpSu, posi) } else { if (!imgItem.location_path) { toNext(fileList, index, afterUpSu, posi) } else { wx.compressImage({ src: imgItem.location_path, // 图片路径 quality: 70, // 压缩质量 success: function(res) { imgItem.temppath = res.tempFilePath upImgToCloud(fileList, index, afterUpSu, posi) }, fail: function(res) { upImgToCloud(fileList, index, afterUpSu, posi) }, complete: function(res) {}, }) } } } function upImgToCloud(fileList, index, afterUpSu,posi) { const imgItem = fileList[index] const cloudPath = path+ getMadomChart(10) + new Date().getTime() + ".jpg" wx.cloud.uploadFile({ cloudPath: cloudPath, filePath: imgItem.temppath || imgItem.location_path, // 文件路径 success: res => { // get resource ID console.log("上传图片 success==" + JSON.stringify(res)) const fileID = res.fileID imgItem.file_id = fileID imgItem.is_up = true wx.cloud.getTempFileURL({ fileList: [{ fileID: fileID }] }).then(urlRes => { console.log("getTempFileURL success==" + JSON.stringify(urlRes)) imgItem.url = urlRes.fileList[0].tempFileURL toNext(fileList, index, afterUpSu, posi) }).catch(error => { console.log("getTempFileURL catch==" + JSON.stringify(error)) toNext(fileList, index, afterUpSu, posi) }) }, fail: err => { // handle error console.log("上传图片 err==" + JSON.stringify(err)) toNext(fileList, index, afterUpSu, posi) } }) } function getMadomChart(length) { const chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" let result = ''; for (let i = length; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)]; return result; } /** * 下一张 */ function toNext(fileList, index, afterUpSu, posi) { let nextIndex = 0 if (index < fileList.length - 1) { nextIndex = index + 1 uploadFile(fileList, nextIndex, afterUpSu, posi) } else { afterUpSu(fileList, posi) } } module.exports = { setPath: setPath, uploadFile: uploadFile, } userUtils.js let utils = require("util.js") const user = require("../mode/user.js") const log = require("../utils/log.js") const config = require("../config/config.js") let userModel = {} /** * 获取窗口高度以及 sx(像素与rpx的比) */ function getHeight() { let oldHight let mainHeight const that = this console.log("mainHeight getHeight==") try { oldHight = getDataByKey(user.HEIGHT) mainHeight = getDataByKey(user.MAIN_HEIGHT) } catch (e) { oldHight = 0 mainHeight = 0 } if (oldHight > mainHeight && mainHeight > 0) { return } wx.getSystemInfo({ success: function(res) { let height = res.windowHeight console.log("mainHeight==" + mainHeight) console.log("height==" + height) if (mainHeight > 0) { } else { setDataBykey(user.MAIN_HEIGHT, height) } let width = res.windowWidth let sx = width / 750 setDataBykey("sx", sx) if (height > oldHight) { setDataBykey(user.HEIGHT, height) } }, }) } function setDataBykey(key, value) { userModel[key] = value try { wx.setStorageSync(key, value) } catch (e) { wx.setStorage({ key: key, data: value }) } } function getDataByKey(key) { if (userModel[key]) { return userModel[key] } else { try { return wx.getStorageSync(key) } catch (e) { wx.getStorage({ key: key, success(res) { return res.data } }) } return "" } } function saveUserMsg(res, page, doWhat) { const detail = res.detail if (detail && detail.errMsg != "getUserInfo:fail auth deny") { setDataBykey(user.AVATAR, detail.userInfo.avatarUrl || "") setDataBykey(user.USER_NAME, detail.userInfo.nickName || "") page.setData({ avatar: detail.userInfo.avatarUrl, nickName: detail.userInfo.nickName }) wx.cloud.callFunction({ name: "updata_userinfo", data: detail.userInfo }) if (doWhat) { doWhat() } } else { wx.showToast({ title: '用户信息获取失败', icon: "none" }) } } function login() { console.log("login") const data = {} data.req_type = "login" data.data = {} wx.cloud.callFunction({ name: "user_manage", data: data }).then(res => { if (res.errMsg == config.ok) { if (res.result.code == 200) { const openid = res.result.data.openid const unionid = res.result.data.unionid userUtils.setDataBykey(user.USER_ID, openid) userUtils.setDataBykey(user.UNIONID, unionid) } } }) } module.exports = { getHeight: getHeight, saveUserMsg: saveUserMsg, getDataByKey: getDataByKey, setDataBykey: setDataBykey, login: login, } utils.js const config=require("../config/config.js") let imgPath = "" function formatNumber(n) { n = n.toString() return n[1] ? n : '0' + n } function formatTime(date, format) { var formateArr = ['YY', 'MM', 'DD', 'hh', 'mm', 'ss']; var returnArr = []; returnArr.push(date.getFullYear()); returnArr.push(formatNumber(date.getMonth() + 1)); returnArr.push(formatNumber(date.getDate())); returnArr.push(formatNumber(date.getHours())); returnArr.push(formatNumber(date.getMinutes())); returnArr.push(formatNumber(date.getSeconds())); for (var i in returnArr) { format = format.replace(formateArr[i], returnArr[i]); } return format; } /** * 判断权限 * * scope 权限名字 * contentMsg 权限被拒绝时提示文字 * doWhat 授权成功后续操作 * */ function getAuthorize(scope, contentMsg, doWhat) { var that = this if (wx.getSetting) { wx.getSetting({ success(res) { if (!res.authSetting[scope]) { wx.authorize({ scope: scope, success() { // 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问 doWhat() }, fail: function(error) { wx.showModal({ title: '提示', content: contentMsg + "权限未开启", confirmText: "去打开", cancelText: "暂不", success: function(res) { if (res.confirm) { wx.navigateTo({ url: '../openAuthorize/index?auth=' + contentMsg, }) } } }) } }) } else { doWhat() } }, }) } } /** * 设置图片下载地址 */ function setImgPath(downLoadPath) { imgPath = downLoadPath } function saveImg() { this.getAuthorize('scope.writePhotosAlbum', "相册", downLoadPhoto) } /** * 下载图片 */ function downLoadPhoto() { if (wx.showLoading) { wx.showLoading({ title: '正在保存', mask: true }) } var that = this wx.saveImageToPhotosAlbum({ filePath: imgPath, success: function (res) { if (wx.hideLoading) { wx.hideLoading() } wx.showModal({ title: '提示', content: '图片已保存', confirmText: "我知道了" }) }, fail: function (error) { if (wx.hideLoading) { wx.hideLoading() } } }) } /** * 比较两个版本 */ function compareVersion(v1, v2) { v1 = v1.split('.') v2 = v2.split('.') var len = Math.max(v1.length, v2.length) while (v1.length < len) { v1.push('0') } while (v2.length < len) { v2.push('0') } for (var i = 0; i < len; i++) { var num1 = parseInt(v1[i]) var num2 = parseInt(v2[i]) if (num1 > num2) { return 1 } else if (num1 < num2) { return -1 } } return 0 } /** * 检查更新 */ function checkUpData() { if (wx.getUpdateManager) { const updateManager = wx.getUpdateManager() updateManager.onCheckForUpdate(function(res) { // 请求完新版本信息的回调 console.log(res.hasUpdate) }) updateManager.onUpdateReady(function() { wx.showModal({ title: '更新提示', content: '新版本已经准备好,是否重启应用?', success: function(res) { if (res.confirm) { // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启 updateManager.applyUpdate() } } }) }) updateManager.onUpdateFailed(function() { // 新的版本下载失败 }) } } function isPhone(phone) { if (!(/^1[3456789]\d{9}$/.test(phone))) { return false; } return true } function initCloud() { if (!wx.cloud) { console.error('请使用 2.2.3 或以上的基础库以使用云能力') } else { wx.cloud.init({ // env 参数说明: // env 参数决定接下来小程序发起的云开发调用(wx.cloud.xxx)会默认请求到哪个云环境的资源 // 此处请填入环境 ID, 环境 ID 可打开云控制台查看 // 如不填则使用默认环境(第一个创建的环境) env: config.env, traceUser: true, success:function(res) { } }) } } module.exports = { formatTime: formatTime, initCloud: initCloud, getAuthorize: getAuthorize, compareVersion: compareVersion, checkUpData: checkUpData, setImgPath: setImgPath, saveImg: saveImg, isPhone: isPhone, } 5,建立集合 [图片] 6,小程序初始化 //app.js const utils = require("utils/util.js") const userUtils = require("utils/userUtils.js") const user = require("mode/user.js") const config = require("config/config.js") App({ onLaunch: function () { userUtils.getHeight() utils.initCloud() utils.checkUpData() this.globalData = {} userUtils.login() }, }) 到此小程序基本框架搭建完毕,下篇聊一下云开发之后台管理
2020-01-11 - 基于云开发的商城小程序开发之路系列之后台管理
经过上篇文章的准备工作,现在就可以正式开始写商城了 后台管理 后台管理分以下几大块 : 用户 、 商品管理 、 订单管理、优惠券及设置。由于小程序云函数有最多50个的限制,故而需要把云函数按照木块综合处理(刚开始的时候没注意,写着写着方法数量超了,没办法只好又把所有的方法汇总分模块了) 1,用户管理,用户管理包括 用户登录注册、更新用户信息(getuserInfo提供的信息,可根据情况保存) ,获取用户列表,获取打印员(本小程序接入快递助手),更新打印员,添加虚拟用户,删除虚拟用户,获取虚拟用户 , 创建名字为 user_manage的函数, req_type对应函数可根据需求实现 user_manage // 云函数入口函数 exports.main = async(event, context) => { const wxContext = cloud.getWXContext() const result={} const req_type = event.req_type const data = event.data switch (req_type) { case "login": return await login(wxContext) case "up_data_msg": return await updataUserMsg(wxContext, data) case "get_user": return await getUserList(data) case "get_printer": return await getPrinter() case "up_printer": return await upPrinter(data) case "add_invented_user": for (let key in data) { if (!keyIsInPara(key, data[key])) { result.code = 202 result.msg = "参数错误" return result } } return await addUser(data) case "del_invented_user": for (let key in data) { if (!keyIsInPara(key, data[key])) { result.code = 202 result.msg = "参数错误" return result } } return await delUser(data) case "get_invented_user": for (let key in data) { if (!keyIsInPara(key, data[key])) { result.code = 202 result.msg = "参数错误" return result } } return await getInventedUser(data) case "cleant_invented_user": for (let key in data) { if (!keyIsInPara(key, data[key])) { result.code = 202 result.msg = "参数错误" return result } } return await cleanUser(data) default : result.code = 203 result.msg = "req_type 不存在" return result } } 2,商品管理 商品管理包括 商品的增删改查复制等等 创建名字为 goods_manage的函数, req_type对应函数可根据需求实现 // 云函数入口函数 exports.main = async (event, context) => { const wxContext = cloud.getWXContext() const result = {} const req_type = event.req_type const data = event.data switch (req_type) { case "add_clazz"://添加分类 return await addClazz(data) case "del_clazz"://删除分类 return await delClazz(data) case "edit_clazz"://编辑分类 return await editClazz(data) case "get_clazz"://获取分类列表 return await getClazz(data) case "get_clazz_item"://获取单个分类信息 return await getClazzItem(data) // case "clean_clazz": // return await cleanClazz() case "add_goods"://添加商品 return await addGoods(data) case "del_goods"://删除商品 return await delGoods(data) case "edit_goods"://编辑商品 return await editGoods(data) case "get_goods_web"://获取商品列表(后台管理用) return await getGoodsForWeb(data) case "get_goods_wechat"://获取商品列表(小程序理用) return await getGoodsForWechat() case "get_goods_by_clazz"://根据分类获取商品列表 return await getGoodsBylazz(data) case "get_goods_info"://商品详情 return await getGoodsInfo(data) case "copy_goods"://复制商品 return await copyGoods(data) //添加评论开始 case "add_comment_web"://添加商品评论(后台管理用) return await addCommentWeb(data) case "get_comment_web"://获取商品评论(后台管理用) return await getCommentWeb(data) case "del_comment"://删除评论 return await delComment(data) default: result.code = 203 result.msg = "req_type 不存在" return result } } 3,订单管理(暂未写到后续补上) 4,设置 设置包括优惠券 规则图文 支付方式 发货地址 等等一些操作 创建名为setting_manage的函数,req_type对应函数可根据需求实现 // 云函数入口函数 exports.main = async(event, context) => { const result = {} const req_type = event.req_type const data = event.data switch (req_type) { case "add_banner"://添加首页banner return await addBanner(data) case "del_banner"://删除banner return await delBanner(data) case "edit_banner"://编辑banner return await editBanner(data) case "get_banner"://获取banner列表 return await getBanner() case "get_banner_item"://获取单个banner信息 return await getBannerItem(data) case "clean_banner"://请空banner return await cleanBanner() // 一下为图文规则接口 case "add_rule"://添加图文规则 return await addUseingHelp(data) case "del_rule"://删除图文规则 return await delUseingHelp(data) case "edit_rule"://修改图文规则 return await editUseingHelp(data) case "get_rule"://获取图文规则 return await getUseingHelp() case "get_rule_by_type"://根据type获取图文规则 return await getUseingHelpItemByType(data) case "get_rule_item"://获取单个图文规则 return await getUseingHelpItem(data) case "clean_rule"://清空图文规则 return await cleanUseingHelp() //发货地址(快递助手发货时用) case "get_send_address"://获取发货地址 return await getSendAddress() case "set_send_address"://设置发货地址 return await editSendAddress(data) //支付方式 case "get_pay_way"://获取支付方式(发货方式需小程序集合内置几种发货方式,后台只可控制起是否可用) return await getPayWay() case "edit_pay_way": return await editPayWay(data) //运费模板 case "get_postage":// return await getPostageList() case "add_postage": return await addPostage(data) case "del_postage": return await delPostage(data) //优惠券 case "add_coupon": return await addCoupon(data) case "get_coupon_list": return await getCouponList(data) case "get_coupon": return await getCoupon(data) case "del_coupon": return await delCoupon(data) case "edit_coupon": return await editCoupon(data) case "get_coupon_user": return await getCouponUser(data) default: result.code = 203 result.msg = "req_type 不存在" return result } } 后台管理调小程序云函数详见 http调用云函数API文档
2020-01-11 - 微信小程序云开发教程-WXSS入门-常用样式
同学们大家好,我是小伊同学,今天我们一起来学习一些常用样式,让我们的组件更加美观漂亮。 在前面几节我们说了很多语法规则,大家已经对wxss有了一个基本的认识。用好wxss能够让我们的页面变得美观漂亮。那么大家一定很好奇,都能对组件设置哪些样式呢?下面我们就来介绍一下常用的样式属性。 [图片] 首先是设置组件的宽度,其属性名称是width,属性值的单位我们也已经讲过了。其次就是高度,他的属性名称是height。然后是颜色,其属性名称是color,这里的颜色表示的是组件内字体的颜色,如果想要设置背景颜色,那么就需要使用backgroundcolor。颜色的属性值可以使用颜色的英文,前提是编译器能够识别出这种颜色,常见的orange、red等都可以直接写在上面。当然,这只有固定的几种颜色,我们还可以使用十六进制的颜色值对颜色属性进行设置,大家可以百度搜索css在线取色器工具进行获取。最后是字体大小,其属性名称是font-size,同样也能够使用rpx的尺寸单位。 此外还有很多属性,比如边框、投影等,这些属性和css是一样的,大家可以通过菜鸟教程,也就是右边的第一个链接学习更多内容。 对于前端页面,有一个非常重要的概念:盒子模型。所有HTML元素都可以当做是一个盒子,可以分为4部分:实际内容,内边距,边框,外边距。 [图片] 实际内容Content,是指盒子装的内容,也就是我们可以看到的文本或者图像,比如说,这里的内容可以是一段话“天气好美”。 内边距Padding,是指距离内容周围的区域,也就是指内容距离边框的距离。比如说,我们这里可以设置内容必须距离上边框10px。 边框Border,是指围绕在内边距和内容外的边框,可以设定宽度、材质、颜色。比如说,我们可以这个边框宽5px,实线,红色。 外边距Margin,是指边框外的区域,也就是指其它元素必须距离该边框的距离,比如说,我们可以设置,其它元素必须距离该元素的上边框10px。 好了,对于wxss的基本内容同学们已经了解的差不多了,最后,让我们从全局的角度看一下如何使用wxss。ppt上展示了三张截图,分别是wxml文件、wxss文件和页面的运行效果。 [图片] 在wxss中对代码进行注释只需要在代码或代码段的开头加上右斜杠和星号,在结尾加上星号和右斜杠,中间部分就会被注释掉了。对于自动生成的空白页面包括wxml、wxss和js文件,会自动在第一行以注释的方式生成页面路径及页面名称。 这三张图展示了一个基本的wxml和wxss配合搭建简单页面的结果。可以看到,我们在wxml中对不同组件加了样式,将对应的样式内容写在了wxss中,最终展示出的效果和我们对组件设置的样式属性是一致的。 在本节的最后,顺便说一下代码风格,在wxml和wxss中代码规范并不限制空行怎么处理,但是我们更推荐大家使用如图所示的代码风格,包括缩进等格式,在wxss中每一个声明写一行,两个声明块之间空一行,这种是我们比较推荐的一种编码风格,看起来也简洁美观。如果觉得手动去调整代码缩进并不方便,我们可以在编译器中使用Alt+shift+F三个按键同时按自动格式化代码。 好了,本节的内容就到这里,到此,我们已经完成了小程序视图层知识的学习,下一节我们将学习小程序的最后一种语言js语言。
2020-08-23 - 微信小程序云开发教程-WXSS入门-基本语法
同学们大家好,我是小伊同学,今天进入WXSS部分的学习,首先我们还是先来讲解一下基本语法。 本部分我们将要学习的知识点如下所示: [图片] 经过之前的课程,相信各位同学已经对wxss有了初步的印象,那么本节课,我们就来具体地学习一下wxss。 [图片] WXSS的全称为WeiXinStyle Sheets,是一套样式语言,用于描述WXML的组件样式。它是由CSS语言,全称为CascadingStyle Sheets,修改扩充而来,因而具备了CSS的大部分特性。如果说css是一种用来变美的语言,那么wxss也同样如此,它决定了wxml中的组件应该如何显示,通过wxss的代码,我们可以对组件设置字体、颜色、位置、大小等样式,也能够添加动画效果,使页面呈现出最佳的视觉效果。 那么,我们如何写一段wxss代码呢?下面我们就来学习一下wxss的语法,基本上和css是一致的。 [图片] 一段wxss代码首先是选择器,选择器的作用是指明针对哪些wxml组件使用此样式,选择器后为一个花括号,括号内部为属性和属性值。属性表明了我要设置这个组件哪个方面的样式,是字体大小还是颜色什么的,不同属性名称的含义不同。冒号后面跟着属性值,表明对于该属性我要设置成什么样子,如果是颜色,这里就填颜色值,如果是字体大小,那么这里就是字号。我们把属性和属性值称作一条声明,使用分号作为结束符。一个花括号中可以包含多条声明。所有这些组合起来成为声明块,代表了一种样式格式。 下面的例子是一段简单的wxss代码,点txt是这个样式格式的名字,也是选择器,表示在wxml中的样式属性被设置为txt的组件使用wxss文件中的此样式进行展示。整个声明块中有三条声明,分别对三个属性进行了设置:字体大小设置为50rpx,颜色设置为橙色,背景颜色设置为小麦色。 之前我们说到wxss是在css上修改而来的,那么wxss与css的区别在哪呢,下面我们就要介绍在wxss中特有的尺寸单位,细心的同学可能已经发现了,在上一页ppt的字体大小中,我设置为了50rpx,这里的rpx就是一种特殊的尺寸单位。 [图片] 做过网页前端的可能都知道,在网页上如何衡量尺寸呢?比如组件的长和宽、字体大小等很多地方都需要用到尺寸。一般在网页前端,为了避免因为显示器的大小及分辨率等因素的差异带来前端展示效果的破坏,我们使用的是px,它代表了像素,是一种在所有电脑上都能使用的单位。 但是我们知道,网页是有滑动条的,如果页面超出窗口大小,可以很方便地向右或者向下滑动的,而像小程序这样,如果无法在一个屏幕上展示全部页面无疑用户体验会非常不好,这就要求小程序开发者必须知道手机屏幕的长和宽到底有多少像素,以确定组件的大小不会超出。 但是每个手机的屏幕大小也不尽相同,因此为了更好的用户体验,避免用户手机屏幕物理尺寸和分辨率的问题,微信在px的基础上使用了一种新的单位,这就是rpx,它可以根据屏幕宽度进行自适应。 具体来说是如何做到自适应的呢?这里微信规定了,一个页面的宽度就是750rpx,开发者使用这一单位进行开发,能够保证页面大小不会超出,而对rpx和真实大小的映射由微信统一完成,在不同手机上只是映射关系不同,而对rpx单位来说大家都是750rpx。 例如,在iPhone6上,屏幕宽度为375px,共有750个物理像素,则750rpx= 375px = 750物理像素,1rpx= 0.5px = 1物理像素,开发者设定多少rpx会被计算折合与该手机对应的实际大小而被显示。 因此,在微信小程序的开发中,如果需要在样式上用到尺寸单位,我们尽量使用rpx。需要说明的是px单位也是可以使用的,但不能保证在所有手机上都能使展示效果与开发者在预览的时候一致。而当样式错乱以后,页面往往都会非常难看,所以使用rpx单位就成为了一种小程序代码的编写习惯。此外,我们有时也会使用百分比来表示尺寸,他表示占上一层容器尺寸的百分比。 好了,本节课的内容就到这里,我们明天继续。如果大家有什么问题,欢迎在文末留言。
2020-08-23 - 微信小程序相关图片素材的几个尺寸
微信小程序相关素材的几个尺寸 像我一个人运营几十个小程序的人,经常换小程序图片以及经常设置底部导航图片,对这种尺寸是要熟记于心的。 1、小程序头像的图片尺寸大小 144px*144px [图片] 占位 2、小程序菜单icon尺寸大小,81px*81px [图片] 占位 [图片] 3、 另外分享下我经常寻找图片素材的几个网站 1、https://www.iconfont.cn/ 2、花瓣网,https://huaban.com/
2020-02-24 - [有点炫]自定义navigate+分包+自定义tabbar
自定义navigate+分包+自定义tabbar,有需要的可以拿去用用,可能会存在一些问题,根据自己的业务改改吧 大家也可以多多交流 代码片段:在这里 {"version":"1.1.5","update":[{"title":"修复 [复制代码片段提示] 无法使用的问题","date":"2020-06-15 09:20","imgs":[]}]} 更新日志: 2019-11-25 自定义navigate 也可以调用wx.showNavigationBarLoading 和 wx.hideNavigationBarLoading 2019-11-25 页面滚动条显示在自定义navigate 和 自定义tabbar上面的问题(点击“体验custom Tabbar” [图片] [图片] 其他demo: 云开发之微信支付:代码片段
2020-06-15 - 自定义tabbar 【恋爱小清单开发总结】
看官方demo的小伙伴知道,自定义tabbar需要在小程序根目录底下建一个名叫custom-tab-bar的组件(我有试过,如果放在components目录里面小程序会识别不了),目前我自己实现的效果是:通过在配置可以切换tab,也可以点击tab后重定向到新页面,支持隐藏tabbar,同时也可以显示右上角文本和小红点。 官方demo里面用的是cover-view,我改成view,因为如果页面有弹窗的话我希望可以盖住tabbar 总结一下有以下注意点: 1、tabbar组件的目录命名需要是custom-tab-bar 2、app.json增加自定义tabbar配置 3、wx.navigateTo不允许跳转到tabb页面 4、进入tab页面时,需要调用tabbar.js手动切换tab 效果图: [图片] 可以扫码体验 [图片] 代码目录如下: [图片] 代码如下: app.json增加自定义tabbar配置 "tabBar": { "custom": true, "color": "#7A7E83", "selectedColor": "#3cc51f", "borderStyle": "black", "backgroundColor": "#ffffff", "list": [ { "pagePath": "pages/love/love", "text": "首页" }, { "pagePath": "pages/tabbar/empty", "text": "礼物说" }, { "pagePath": "pages/tabbar/empty", "text": "恋人圈" }, { "pagePath": "pages/me/me", "text": "我" } ] }, 自定义tabbar组件代码如下 index.js //api.js是我自己对微信接口的一些封装 const api = require('../utils/api.js'); //获取应用实例 const app = getApp(); Component({ data: { isPhoneX: false, selected: 0, hide: false, list: [{ showRedDot: false, showBadge: false, badgeText: "", pagePath: "/pages/love/love", iconPath: "/images/tabbar/home.png", selectedIconPath: "/images/tabbar/home-select.png", text: "首页" }, { showRedDot: false, showBadge: false, badgeText: "", pagePath: "/pages/tabbar/empty", navigatePath: "/pages/gifts/giftList", iconPath: "/images/tabbar/gift.png", selectedIconPath: "/images/tabbar/gift-select.png", text: "礼物说", hideTabBar: true }, { showRedDot: false, showBadge: false, badgeText: "", pagePath: "/pages/tabbar/empty", navigatePath: "/pages/moments/moments", iconPath: "/images/tabbar/lover-circle.png", selectedIconPath: "/images/tabbar/lover-circle-select.png", text: "恋人圈", hideTabBar: true }, { showRedDot: false, showBadge: false, badgeText: "", pagePath: "/pages/me/me", iconPath: "/images/tabbar/me.png", selectedIconPath: "/images/tabbar/me-select.png", text: "我" }] }, ready() { // console.error("custom-tab-bar ready"); this.setData({ isPhoneX: app.globalData.device.isPhoneX }) }, methods: { switchTab(e) { const data = e.currentTarget.dataset; console.log("tabBar参数:", data); api.vibrateShort(); if (data.hideTabBar) { api.navigateTo(data.navigatePath); } else { /*this.setData({ selected: data.index }, function () { wx.switchTab({url: data.path}); });*/ /** * 改为直接跳转页面, * 因为发现如果先设置selected的话, * 对应tab图标会先选中,然后页面再跳转, * 会出现图标变成未选中然后马上选中的过程 */ wx.switchTab({url: data.path}); } }, /** * 显示tabbar * @param e */ showTab(e){ this.setData({ hide: false }, function () { console.log("showTab执行完毕"); }); }, /** * 隐藏tabbar * @param e */ hideTab(e){ this.setData({ hide: true }, function () { console.log("hideTab执行完毕"); }); }, /** * 显示小红点 * @param index */ showRedDot(index, success, fail) { try { const list = this.data.list; list[index].showRedDot = true; this.setData({ list }, function () { typeof success == 'function' && success(); }) } catch (e) { typeof fail == 'function' && fail(); } }, /** * 隐藏小红点 * @param index */ hideRedDot(index, success, fail) { try { const list = this.data.list; list[index].showRedDot = false; this.setData({ list }, function () { typeof success == 'function' && success(); }) } catch (e) { typeof fail == 'function' && fail(); } }, /** * 显示tab右上角文本 * @param index * @param text */ showBadge(index, text, success, fail) { try { const list = this.data.list; Object.assign(list[index], {showBadge: true, badgeText: text}); this.setData({ list }, function () { typeof success == 'function' && success(); }) } catch (e) { typeof fail == 'function' && fail(); } }, /** * 隐藏tab右上角文本 * @param index */ hideBadge(index, success, fail) { try { const list = this.data.list; Object.assign(list[index], {showBadge: false, badgeText: ""}); this.setData({ list }, function () { typeof success == 'function' && success(); }) } catch (e) { typeof fail == 'function' && fail(); } } } }); index.html <view class="footer-tool-bar flex-center {{isPhoneX? 'phx_68':''}}" hidden="{{hide}}"> <view class="tab flex-full {{selected === index ? 'focus':''}}" wx:for="{{list}}" wx:key="index" data-path="{{item.pagePath}}" data-index="{{index}}" data-navigate-path="{{item.navigatePath}}" data-hide-tab-bar="{{item.hideTabBar}}" data-open-ext-mini-program="{{item.openExtMiniProgram}}" data-ext-mini-program-app-id="{{item.extMiniProgramAppId}}" bindtap="switchTab"> <view class="text"> <view class="dot" wx:if="{{item.showRedDot}}"></view> <view class="badge" wx:if="{{item.showBadge}}">{{item.badgeText}}</view> <image class="icon" src="{{item.selectedIconPath}}" hidden="{{selected !== index}}"></image> <image class="icon" src="{{item.iconPath}}" hidden="{{selected === index}}"></image> </view> </view> </view> index.json { "component": true, "usingComponents": {} } index.wxss @import "/app.wxss"; .footer-tool-bar{ background-color: #fff; height: 100rpx; width: 100%; position: fixed; bottom: 0; z-index: 100; text-align: center; font-size: 24rpx; transition: transform .3s; border-radius: 30rpx 30rpx 0 0; /*padding-bottom: env(safe-area-inset-bottom);*/ box-shadow:0rpx 0rpx 18rpx 8rpx rgba(212, 210, 211, 0.35); } .footer-tool-bar .tab{ color: #242424; height: 100%; line-height: 100rpx; } .footer-tool-bar .focus{ color: #f96e49; font-weight: 500; } .footer-tool-bar .icon{ width: 44rpx; height: 44rpx; margin: 18rpx auto; } .footer-tool-bar .text{ line-height: 80rpx; height: 80rpx; position: relative; display: inline-block; padding: 0rpx 40rpx; box-sizing: border-box; margin: 10rpx auto; } .footer-tool-bar .dot{ position: absolute; top: 16rpx; right: 16rpx; height: 16rpx; width: 16rpx; border-radius: 50%; background-color: #f45551; } .footer-tool-bar .badge{ position: absolute; top: 8rpx; right: 8rpx; height: 30rpx; width: 30rpx; line-height: 30rpx; border-radius: 50%; background-color: #f45551; color: #fff; text-align: center; font-size: 20rpx; font-weight: 450; } .hide{ transform: translateY(100%); } app.wxss(这里的样式文件是我用来存放一些公共样式) /**app.wxss**/ page { background-color: #f5f5f5; height: 100%; -webkit-overflow-scrolling: touch; } .container { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: space-between; box-sizing: border-box; } .blur { filter: blur(80rpx); opacity: 0.65; } .flex-center { display: flex; align-items: center; justify-content: center; } .flex-column { display: flex; /*垂直居中*/ align-items: center; /*水平居中*/ justify-content: center; flex-direction: column; } .flex-start-horizontal{ display: flex; justify-content: flex-start; } .flex-end-horizontal{ display: flex; justify-content: flex-end; } .flex-start-vertical{ display: flex; align-items: flex-start; } .flex-end-vertical{ display: flex; align-items: flex-end; } .flex-wrap { display: flex; flex-wrap: wrap; } .flex-full { flex: 1; } .reset-btn:after { border: none; } .reset-btn { background-color: #ffffff; border-radius: 0; margin: 0; padding: 0; overflow: auto; } .loading{ opacity: 0; transition: opacity 1s; } .load-over{ opacity: 1; } .phx_68{ padding-bottom: 68rpx; } .phx_34{ padding-bottom: 34rpx; } 另外我还对tabbar的操作做了简单的封装: tabbar.js const api = require('/api.js'); /** * 切换tab * @param me * @param index */ const switchTab = function (me, index) { if (typeof me.getTabBar === 'function' && me.getTabBar()) { console.log("切换tab:", index); me.getTabBar().setData({ selected: index }) } }; /** * 显示 tabBar 某一项的右上角的红点 * @param me * @param index */ const showRedDot = function (me, index, success, fail) { if (typeof me.getTabBar === 'function' && me.getTabBar()) { me.getTabBar().showRedDot(index, success, fail); } }; /** * 隐藏 tabBar 某一项的右上角的红点 * @param me * @param index */ const hideRedDot = function (me, index, success, fail) { if (typeof me.getTabBar === 'function' && me.getTabBar()) { me.getTabBar().hideRedDot(index, success, fail); } }; /** * 显示tab右上角文本 * @param me * @param index * @param text */ const showBadge = function (me, index, text, success, fail) { if (typeof me.getTabBar === 'function' && me.getTabBar()) { me.getTabBar().showBadge(index, text, success, fail); } }; /** * 隐藏tab右上角文本 * @param me * @param index */ const hideBadge = function (me, index, success, fail) { if (typeof me.getTabBar === 'function' && me.getTabBar()) { me.getTabBar().hideBadge(index, success, fail); } }; /** * 显示tabbar * @param me * @param success */ const showTab = function(me, success){ if (typeof me.getTabBar === 'function' && me.getTabBar()) { me.getTabBar().showTab(success); } }; /** * 隐藏tabbar * @param me * @param success */ const hideTab = function(me, success){ if (typeof me.getTabBar === 'function' && me.getTabBar()) { me.getTabBar().hideTab(success); } }; module.exports = { switchTab, showRedDot, hideRedDot, showBadge, hideBadge, showTab, hideTab }; 最后,进入到tab对应页面的时候要手动调用一下swichTab接口,然tabbar聚焦到当前tab /** * 生命周期函数--监听页面显示 */ onShow: function () { tabbar.switchTab(this, this.data.tabIndex);//tabIndex是当前tab的索引 }
2021-11-09 - tabBar里面的icon的颜色如何设置或者改变?
tabBar里面的icon的颜色如何设置或者改变? app.json关于tabBar的配置如下图 [图片] "iconPath": "/images/images/home_page.png", "selectedIconPath":"/images/images/home_page_selected.png" 因为在"iconPath" 和"selectedIconPath"路径下面 分别放置了两张自己找的图片 “home_page.png” [图片] 和“home_page_selected.png" [图片], 有什么办法可以设置或者改变icon的颜色?特别是"selectedIconPath"路径下面icon的颜色?比如绿色或者红色? 或者不用放置自己找的图片,有其他方法设置icon样式和icon的颜色?
2020-08-23 - 为什么引入weui.wxss 之后不显示图标(icon)?
一、操作步骤 https://github.com/wechat-miniprogram/weui-miniprogram 将WeUI下载后,将weui-wxss-master/dist/路径下的style文件夹复制粘贴到微信小程序的新建项目里 再将WeUI的tabbarde的wxml,wxss,js源代码分别复制粘贴到微信小程序的新建项目pages/index/路径下对应的index.wxml,index.wxss,index.js里面 然后在app.wxss中 @import 'style/weui.wxss‘, 然后编译,刷新 二、出现问题 1.编译后出现的tabbar(底部导航条)文字(微信,通讯录,发现,我)和右上角的红色带圈数字和小红点都和WeUI,但是文字(微信,通讯录,发现,我)上方的icon(图标)没有出现是什么原因? 2.调试器报错内容如下: VM1571:1 Failed to load local image resource /pages/images/icon_tabbar.png the server responded with a status of 500 (HTTP/1.1 500 Internal Server Error) console.error @ VM1571:1 (anonymous) @ VM1596:2 3截图如下: [图片] 左下角的tabbar文字(微信,通讯录,发现,我)上方的icon(图标)缺失是什么原因? 请指点迷津
2020-08-11 - WeUI组件快速开发和WeUI组件库示例代码不一致?2个问题!
根据微信小程序官方开发文档提供两种方式复制代码到微信开发者工具,一个报错,一个不报错? 1.WeUI 快速上手 https://developers.weixin.qq.com/miniprogram/dev/extended/weui/quickstart.html [图片] 2.WeUI 示例代码 https://developers.weixin.qq.com/miniprogram/dev/extended/weui/dialog.html [图片] 第一,按照1.WeUI 快速上手的方式把代码复制到微信开发者工具不会报错? 1.引入组件通过 useExtendedLib 扩展库 的方式引入,这种方式引入的组件将不会计入代码包大小。而且没有通过npm方式下载构建 "/miniprogram_npm/weui-miniprogram/dialog/dialog" 为什么还会是/miniprogram_npm/weui-miniprogram/这个路径而且可以成功? [图片] 第二,按照2.WeUI 示例代码的方式复制到微信开发者工具就会报错? [图片] 请解惑
2020-08-09