- 小程序尝试开启quic不成功,是否有成功案例?
使用nginx-quic模块编译的nginx,使用chrome访问走的quic协议,使用小程序访问未走quic协议。使用caddy并开启experimental_http3,chrome访问也走的quic协议,小程序未走quic协议。小程序wx.request是否真正支持quic协议,还是我服务器配置有问题?
2021-05-13 - 如何使用scroll-view制作左右滚动导航条效果
最新:2020/06/13。修改为scroll-view与swiper联动效果,新增下拉刷新以及上拉加载效果。。具体效果查看代码片段,以下文章内容和就不改了 刚刚在社区里看到 有老哥在问如何做滚动的导航栏。这里简单给他写了个代码片段,需要的大哥拿去随便改改,先看效果图: [图片] 代码如下: wxml [代码]<scroll-view class="scroll-wrapper" scroll-x scroll-with-animation="true" scroll-into-view="item{{currentTab < 4 ? 0 : currentTab - 3}}" > <view class="navigate-item" id="item{{index}}" wx:for="{{taskList}}" wx:key="{{index}}" data-index="{{index}}" bindtap="handleClick"> <view class="names {{currentTab === index ? 'active' : ''}}">{{item.name}}</view> <view class="currtline {{currentTab === index ? 'active' : ''}}"></view> </view> </scroll-view> [代码] wxss [代码].scroll-wrapper { white-space: nowrap; -webkit-overflow-scrolling: touch; background: #FFF; height: 90rpx; padding: 0 32rpx; box-sizing: border-box; } ::-webkit-scrollbar { width: 0; height: 0; color: transparent; } .navigate-item { display: inline-block; text-align: center; height: 90rpx; line-height: 90rpx; margin: 0 16rpx; } .names { font-size: 28rpx; color: #3c3c3c; } .names.active { color: #00cc88; font-weight: bold; font-size: 34rpx; } .currtline { margin: -8rpx auto 0 auto; width: 100rpx; height: 8rpx; border-radius: 4rpx; } .currtline.active { background: #47CD88; transition: all .3s; } [代码] JS [代码]const app = getApp() Page({ data: { currentTab: 0, taskList: [{ name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, ] }, onLoad() { }, handleClick(e) { let currentTab = e.currentTarget.dataset.index this.setData({ currentTab }) }, }) [代码] 最后奉上代码片段: https://developers.weixin.qq.com/s/nkyp64mN7fim
2020-06-13 - SAUI-table组件
前提:现成的框架里,table的组件,都是很简单的。遇到复杂的业务时,很多都是不满足的。 - 支持表头和表格body分组(自定义配置) - 支持表格左右滑动(可自定义配置表格固定列) - 支持点击事件 /*==================mock start =====================*/ //简单的数据结构 const headData = [ { title: '截止日期', key: 'time', hasClick: true}, { title: '是否经过审计', key: 'isSJ', hasClick: true}, { title: '审计日期', key: 'sjTime', align: 'right', hasClick: true}, ] const data = [ {time: '2020/3/31', isSJ: '未审计', sjTime: '2020/02/02'}, {time: '2020/3/31', isSJ: '已审计', sjTime: '2020/02/02'}, {time: '2020/3/31', isSJ: '未审计', sjTime: '2020/02/02'}, ] // 固定列,左右滚动的table,可点击修改 const data2 = [ {name: '财务报表截止日期', column1: '2020/01/02', column2: '2020/01/02', column3: '2020/01/02', column4: '2020/01/02'}, {name: '是否经过审计', column1: '未审计', column2: '未审计', column3: '已审计', column4: '已审计'}, {name: '审计日期', column1: '2020/01/02', column2: '2020/01/02', column3: '2020/01/02', column4: '2020/01/02'}, {name: '财务报表类型', column1: '1', column2: '1', column3: '2', column4: '2'}, ] const headData2 = [ { title: { '@item': { title: [ { title: '截止日期' }, { title: '指标' } ], titleClass: 'lineTd' } }, key: 'name', width: '260', sticky: true, }, { title: '2020/01/02', key: 'column1', width: '200', hasClick: true }, { title: '2020/01/03', key: 'column2', width: '200', hasClick: true }, { title: '2020/01/04', key: 'column3', width: '200', hasClick: true }, { title: '2020/01/05', key: 'column4', width: '200', hasClick: true }, ] const mock2 = { headData: ["2021-12-31", "2021-12-30", "2021-12-29", "2021-12-29"], data: [ { code: 'a', name: '经营活动产生的现金流量', value: [], isLevel1: true, isEdit: false, }, { code: 'a1', name: '销售商品', value: ['a1v', 'a2v', 'a3v', 'a4v'], isLevel1: false, }, { code: 'a2', name: '提供劳务收到的现金', value: ['a1v', 'a2v', '提供劳务收到的现金', 'a4v'], isLevel1: false, }, { code: 'a3', name: '提供劳务收到的现金', value: ['a1v', 'a2v', 'a3v', 'a4v'], isLevel1: false, }, { code: 'b', name: '经营活动产生的现金流量', value: ['', '', '', ''], isLevel1: true, isEdit: true, }, { code: 'b1', name: '销售商品', value: ['b1v', null, 'b3v', 'b4v'], isLevel1: false, }, { code: 'b2', name: '提供劳务收到的现金', value: ['b1v', 'b2v', 'b3v', 'b4v'], isLevel1: false, }, { code: 'b3', name: '提供劳务收到的现金', value: ['b1v', 'b2v', 'b3v', 'b4v'], isLevel1: false, }, ] } /*==================mock end =====================*/ //tableInst, tableInst2 由aotoo封装的table组件调用方式(图一) 正常的table返回数据构成 //tableInst3, tableInst4 由wxml封装的table组件调用方式(图二) 正常的table返回数据构成 //tableInst5 纯wxml构成(图三) 非正常的table返回数据构成 Pager({ data: { tableInst: tableList({ headData: headData, data: data, tableClass: 'border mb-default', // isHideHeader: true, // unit: '%', autoWidth: true }), tableInst2: Pager.item({}), tableInst3: null, tableInst4: null, tableInst5: null, }, onReady() { const $tableInst2 = this.getElementsById('tableInst2') this.$modal = this.getElementsById('modal') this.vm = null const that = this const inst = tableList({ headData: headData2, data: data2, tableClass: 'auto border sticky mb-default', onItemClick(params) { that.vm = this // 组件内部的this that.openModal(params) }, }) $tableInst2.update(inst) this.setData({ tableInst3: { headData: headData, data: data, tableClass: 'border auto mb-default', }, tableInst4: { headData: headData3, data: data3, tableClass: 'border auto mb-default', onItemClick(params) { const {idx, key, value} = params const dt = this.data.data const abc = that.filterClas(dt, params) this.setData({data: abc}) that.vm1 = this that.openModal(params, 1) }, }, tableInst5: { headData: mock2.headData, data: mock2.data }, }) } }) 图一: [图片] 图二: [图片] 图三: [图片] 效果: [图片]
2022-04-02 - 打开会话可以拉互联企业的成员吗?
打开会话接口可以拉互联企业的成员吗? 如果要拉旗下某个子公司的成员,要如何实现呢?
2021-04-22 - ASP.NET Core 微信支付(一)【统一下单 APIV3】
官方参考资料签名:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml 统一下单接口:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_1.shtml 首先声明,我这个是为APP支付提供的接口!!! 一、签名 生成签名 参考资料里面讲的比较详细,也有官方的文档,不过文档不全,导致我的调试程序一直出现问题,请求微信的统一下单接口总是报400错误(Bad Request)。 签名生成参考官方代码,代码如下,里面有我标注的请求接口报400错误原因的代码 using System; using System.IO; using System.Net.Http; using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; namespace HttpHandlerDemo { // 使用方法 // HttpClient client = new HttpClient(new HttpHandler("{商户号}", "{商户证书序列号}")); // ... // var response = client.GetAsync("https://api.mch.weixin.qq.com/v3/certificates"); public class HttpHandler : DelegatingHandler { private readonly string merchantId; private readonly string serialNo; public HttpHandler(string merchantId, string merchantSerialNo) { InnerHandler = new HttpClientHandler(); this.merchantId = merchantId; this.serialNo = merchantSerialNo; } protected async override Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { var auth = await BuildAuthAsync(request); string value = $"WECHATPAY2-SHA256-RSA2048 {auth}"; request.Headers.Add("Authorization", value); request.Headers.Add("Accept", "application/json");//如果缺少这句代码就会导致下单接口请求失败,报400错误(Bad Request) request.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");//如果缺少这句代码就会导致下单接口请求失败,报400错误(Bad Request) return await base.SendAsync(request, cancellationToken); } protected async Task BuildAuthAsync(HttpRequestMessage request) { string method = request.Method.ToString(); string body = ""; if (method == "POST" || method == "PUT" || method == "PATCH") { var content = request.Content; body = await content.ReadAsStringAsync();//debug的时候在这里打个断点,看看body的值是多少,如果跟你传入的参数不一致,说明是有问题的,一定参考我的方法 } string uri = request.RequestUri.PathAndQuery; var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); string nonce = Path.GetRandomFileName(); string message = $"{method}\n{uri}\n{timestamp}\n{nonce}\n{body}\n"; string signature = Sign(message); return $"mchid=\"{merchantId}\",nonce_str=\"{nonce}\",timestamp=\"{timestamp}\",serial_no=\"{serialNo}\",signature=\"{signature}\""; } protected string Sign(string message) { // NOTE: 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY----- // 亦不包括结尾的-----END PRIVATE KEY----- string privateKey = "{你的私钥}"; byte[] keyData = Convert.FromBase64String(privateKey); using (CngKey cngKey = CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob)) using (RSACng rsa = new RSACng(cngKey)) { byte[] data = System.Text.Encoding.UTF8.GetBytes(message); return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)); } } } } 二、统一下单 使用方法 var url = "https://api.mch.weixin.qq.com/v3/pay/transactions/app"; var req = new GenerateOrderModelForWxPay { appid = WxPayConst.appid, mchid = WxPayConst.mchid, description = "商品名称", amount = new WxPayAmountModel { total = 1 }, out_trade_no = orderNumber, notify_url = "https://xxx.com/api/WxPayCallback" }; HttpClient client = new HttpClient(new HttpHandler("{商户号}", "{商户证书序列号}")); //GET 方式 var response = client.GetAsync("https://api.mch.weixin.qq.com/v3/certificates"); // POST 方式 var bodyJson = new StringContent(req.ToJson(), Encoding.UTF8, "application/json"); //一定要这样传递参数,不然在加密签名的时候获取到的参数就是\\u0这种形式的数据了,不是传递的这样的数据了,导致加密的结果不正确 var response = await client.PostAsync(url, bodyJson); // 读取统一下单之后的返回结果,这样读取出来的直接就是结果,或者错误原因,大家一定要这么搞啊!!!多么痛的领悟,会有具体的错误信息的。 var respStr = await response.Content.ReadAsStringAsync();//这里面就包含prepay_id了 三、注意 大家一定看接口规则里面的说明,我就是没有看,导致我搞了一天没有搞通,下面贴两个截图 [图片] [图片]
2021-04-10 - 电脑微信小程序,最小化后再次打开,重新进入主页。
电脑微信小程序,点击最小化后,重新从登陆口点击进入,虽然原小程序选的是 最小化 “—” ,但是还是重新从主页打开,并不记录原来操作页面了。 登陆口:[图片]
2020-12-18 - 在微信小程序中使用echarts,在手机微信预览可以显示,在pc端微信上预览不显示?
[图片]
2020-07-25 - Vue项目使用kbone打包成小程序的踩坑笔记
以下内容是初版kbone的笔记,现在kbone进行了大量的更新升级,以下配置方法大部分已经不适用新版kbone了,如需详细了解kbone的使用方法,请参考官方文档 kbone是大佬 @June 开发的一款Vue项目打包成小程序的工具(暂时只支持微信小程序) 已有的Vue项目只需通过简单的配置即可打包成小程序的代码,新项目也可以使用这个工具进行同步开发,项目中提供了很多示例供参考 Github连接:https://github.com/wechat-miniprogram/kbone 为什么选择kbone? 市面上有很多跨平台的框架,如: WePY、mpvue、uni-app、taro等 他们都有一些弊端和限制,现有的项目迁移不方便,对第三方组件库不兼容等 kbone作为一款工具,也可以说是一款webpack插件(Vue项目只需要引入 mp-webpack-plugin 插件,通过简单的webpack配置即可),无论是对新项目还是已有项目都是非常友好的,因为他完整的移植了Vue Runtime,通过webpack将Vue代码打包成小程序兼容的代码,通过适配器运行,所以大部分的第三方组件库是兼容的 如何使用? Github仓库中提供了很多示例,有兴趣的可以体验下,下面介绍下我的使用过程 创建项目 因为习惯了使用Vue Cli,所以我选择使用这个工具初始化一个项目,使用的版本是Vue Cli 3(这个工具提供了一套完全图形化的用户界面,这里就不介绍使用方法了,没有使用过的同学可以 点击这里 查看创建方法) 集成插件 kbone目前还没有提供Vue Cli的插件(大佬很忙,有能力的同学可以在Github上提PR),所以需要手动添加,具体的操作方式请参考QuickStart文档 安装 mp-webpack-plugin 插件 [代码]yarn add mp-webpack-plugin --dev # 或者 npm install mp-webpack-plugin --save-dev [代码] 在 src 目录中新增 main.mp.js 入口文件 [代码]import Vue from 'vue' import App from '@/App' import router from '@/router' import store from '@/store' // 需要将创建根组件实例的逻辑封装成方法 export default function createApp () { // 在小程序中如果要注入到 id 为 app 的 dom 节点上,需要主动创建 const container = document.createElement('div') container.id = 'app' document.body.appendChild(container) Vue.config.productionTip = false return new Vue({ router, store, render: h => h(App) }).$mount('#app') } [代码] 在根目录创建 miniprogram.config.js 文件,添加 mp-webpack-plugin 插件配置 [代码]module.exports = { // 页面 origin,默认是 https://miniprogram.default origin: '', // 填写项目中的图片资源地址,建议图片资源使用线上地址 // 入口页面路由,默认是 / entry: '/', // 页面路由,用于页面间跳转 router: { // 路由可以是多个值,支持动态路由 index: [] }, // 特殊路由跳转 redirect: { // 跳转遇到同一个 origin 但是不在 router 里的页面时处理方式,支持的值:webview - 使用 web-view 组件打开;error - 抛出异常;none - 默认值;什么都不做,router 配置项中的 key notFound: 'index', // 跳转到 origin 之外的页面时处理方式,值同 notFound accessDenied: 'index' }, // app 配置,同 https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#window app: { navigationStyle: 'custom' // 自定义navigation }, // 全局配置 global: {}, // 页面配置,可以为单个页面做个性化处理,覆盖全局配置 pages: {}, // 优化 optimization: { domSubTreeLevel: 5, // 将多少层级的 dom 子树作为一个自定义组件渲染,支持 1 - 5,默认值为 5 // 对象复用,当页面被关闭时会回收对象,但是如果有地方保留有对象引用的话,注意要关闭此项,否则可能出问题 elementMultiplexing: true, // element 节点复用 textMultiplexing: true, // 文本节点复用 commentMultiplexing: true, // 注释节点复用 domExtendMultiplexing: true, // 节点相关对象复用,如 style、classList 对象等 styleValueReduce: 5000, // 如果设置 style 属性时存在某个属性的值超过一定值,则进行删减 attrValueReduce: 5000 // 如果设置 dom 属性时存在某个属性的值超过一定值,则进行删减 }, // 项目配置,会被合并到 project.config.json projectConfig: { appid: '', // 填写小程序的AppId projectname: '' // 填写小程序的项目名称 }, // 包配置,会被合并到 package.json packageConfig: { name: '', // 项目名称 description: '', // 描述 author: '' // 作者信息 } } [代码] 在根目录创建 .env.mp 文件,添加 mp 环境变量 [代码]NODE_ENV = mp [代码] 修改 vue.config.js 文件,添加打包小程序的 webpack 配置 [代码]const path = require('path') function resolve (dir) { return path.join(__dirname, dir) } const webpack = require('webpack') const MpWebpackPlugin = require('mp-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') module.exports = { css: { extract: true }, outputDir: process.env.NODE_ENV === 'mp' ? './dist/mp/common' : './dist/web', configureWebpack: { resolve: { extensions: ['*', '.js', '.vue', '.json'], alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src') } } }, chainWebpack: config => { if (process.env.NODE_ENV === 'mp') { config .devtool('node') .entry('app').clear().add('./src/main.mp.js').end() .output.filename('[name].js') .library('createApp') .libraryExport('default') .libraryTarget('window').end() .target('web') .optimization.runtimeChunk(false) .splitChunks({ chunks: 'all', minSize: 1000, maxSize: 0, minChunks: 1, maxAsyncRequests: 100, maxInitialRequests: 100, automaticNameDelimiter: '~', name: true, cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10 }, default: { minChunks: 2, priority: -20, reuseExistingChunk: true } } }).end() .plugins.delete('copy').end() .plugin('define').use(new webpack.DefinePlugin({ 'process.env.isMiniprogram': process.env.isMiniprogram // 注入环境变量,用于业务代码判断 })).end() .plugin('extract-css').use(new MiniCssExtractPlugin({ filename: '[name].wxss', chunkFilename: '[name].wxss' })).end() .plugin('mp-webpack').use(new MpWebpackPlugin(require('./miniprogram.config.js'))).end() } } } [代码] 修改 package.json 中的 scripts 属性,添加用于开发和打包的任务 [代码]# 开发 "mp-serve": "vue-cli-service build --watch --mode mp" # 打包 "mp-build": "vue-cli-service build --mode mp" [代码] 配置完成,尽情玩耍吧! 目前已知的问题 小程序中无法支持 getComputedStyle 和 getBoundingClientRect 的同步接口,只能异步,所以依赖这两个方法的第三方库,需要进行改造 因为大佬很忙目前还处于开发阶段,很多组件还没有适配,有能力和有精力的同学欢迎去Github提PR 再次奉上Github连接:https://github.com/wechat-miniprogram/kbone
2019-12-10