主要讲解使用Drupal8开发微信公众号(服务号),微信小程序。探讨最新的Drupal8、前端技术、微信小程序技术。
我们的示例,主要以亚艾元象棋网为例,讲解Drupal8网站的搭建,Drupal8下面微信模块的使用,如何快速的配置出来一个微信服务号、如何制作一个微信小程序。
网站:亚艾元象棋网
微信服务号:亚艾元象棋
微信小程序:亚艾元象棋
中国象棋棋谱播放器,微信小程序,下载地址:
https://github.com/g089h515r806/we_cn_chess
亚艾元象棋微信小程序,下载地址:
作者: 老葛 亚艾元软件
微信小程序,正式版刚出来的时候,甚是激动。激动了不到一个月之后,发现里面限制太多,所以很快就转为观望状态。张小龙是一个让人尊敬的大神,由于近一年来,微信小程序,逐步的开放越来越多的接口,各种功能也愈加完善,所以决定,开始尝试微信小程序。
这是我写的第一个微信小程序,代码在:
https://github.com/g089h515r806/we_cn_chess
我尝试在微信小程序上面写一个象棋棋谱的播放器,花了一个周末的时间,终于成功了。这让我对于微信小程序,增加了十分的信心。
从微信小程序里面,我们可以看到Angularjs, react, vue,这些当今流行JS框架的影子。我熟悉Angular 1.0, 4.0,看了小程序里面的js代码以后,发现两者十分相似。
对于Drupal来讲,无头(Headless) Drupal是一个方向,特别是对于移动互联网,现在各种前端框架,不断涌现。 IOS、android原生应用蓬勃发展,只有将Drupal和移动互联网结合起来,才能壮大Drupal的版图。当然,Drupal仅仅是后台框架的一种,除了Drupal以外,PHP的laravel、nodejs、JAVA、python等,都是流行的后端框架,与后者相比,Drupal还是小众技术。
Drupal8是面向移动设计,内置Rest API,可以方便的将Drupal各种数据,提供给前端框架。比如新兴的Json API模块,极大的方便的和前端框架的数据交互。增强了Drupal作为一个框架的吸引力。
学习微信小程序,我的经验,首先是看官方文档,文档地址:
https://mp.weixin.qq.com/debug/wxadoc/dev/
图8-2-1
把整个文档好好的看一遍,这才是权威的资料。
注册一个微信小程序账号,行动起来。已经认证的微信服务号、订阅号,可以使用原来的资料创建一个小程序账号。我使用的就是原来认证过的微信服务号。微信小程序账号的创建文档,参考官方文档。
下载开发者工具:
https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html
图8-2-2
根据你的系统,选择对应的版本。现在万事俱备,只欠动手了。
作者:老葛 亚艾元软件
打开安装好的微信web开发者工具程序:
图8-2-2-1
双击以后,我们进入这样的界面,需要扫码:
图8-2-2-2
扫码成功后,看到了我的信息:
图8-2-2-3
这里选择本地小程序项目。我们进入一个添加项目页面:
图8-2-2-4
在这里输入项目名称,项目目录,注意项目目录文件夹里面为空:
图8-2-2-5
选中在当前目录中创建quick start项。最后点击“确定”按钮。我们进入了微信小城的示例界面:
图8-2-2-6
接下来,需要熟悉微信开发者工具的使用,就是将它的每个菜单选项都熟悉一下,点点看看。
图8-2-2-7
打开示例程序所在目录:
图8-2-2-8
这个目录下面包含4个文件:
app.json: 配置信息
app.js: 逻辑代码
app.wxss: 微信小程序的CSS代码。
project.config:项目配置文件
展开pages,我们看到里面包含index,logs两个目录:
图8-2-2-9
图8-2-2-10
每个页面大概包含四个部分:
js :逻辑代码
wxml:模板文件
wxss:微信样式
json:配置文件
这里的代码,没有多少,我建议大家手动用笔抄写一遍。我对于学习新的技术,一贯的态度就是动动手,其中对于第一次看到的代码,都是用手抄写一遍,以加强记忆。
我们来看一下,index页面的逻辑代码:
const app = getApp()
Page({
data: {
motto: 'Hello World',
userInfo: {},
hasUserInfo: false,
canIUse: wx.canIUse('button.open-type.getUserInfo')
},
//事件处理函数
bindViewTap: function() {
wx.navigateTo({
url: '../logs/logs'
})
},
onLoad: function () {
if (app.globalData.userInfo) {
this.setData({
userInfo: app.globalData.userInfo,
hasUserInfo: true
})
} else if (this.data.canIUse){
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
// 所以此处加入 callback 以防止这种情况
app.userInfoReadyCallback = res => {
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
} else {
// 在没有 open-type=getUserInfo 版本的兼容处理
wx.getUserInfo({
success: res => {
app.globalData.userInfo = res.userInfo
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
}
},
getUserInfo: function(e) {
console.log(e)
app.globalData.userInfo = e.detail.userInfo
this.setData({
userInfo: e.detail.userInfo,
hasUserInfo: true
})
}
})
对应的模板文件:
<!--index.wxml-->
<view>
<view>
<button wx:if="{{!hasUserInfo && canIUse}}" open-type="getUserInfo" bindgetuserinfo="getUserInfo"> 获取头像昵称 </button>
<block wx:else>
<image bindtap="bindViewTap" src="{{userInfo.avatarUrl}}" background-size="cover"></image>
<text>{{userInfo.nickName}}</text>
</block>
</view>
<view>
<text>{{motto}}</text>
</view>
</view>
在页面的js逻辑里面,定义一个Page, 里面data部门定义的数据,可以传递到wxml模板文件中直接使用。bindViewTap是定义的一个事件处理函数,我们可以根据需要定义多个这样的函数。onLoad,类似于Drupal里面的钩子函数,由微信小程序系统本身定义,当一个微信小程序页面加载时,会调用对应页面的onLoad函数。在这里,我们调用 app.getUserInfo,成功后,将返回的用户信息,通过setData,设置为data里面的userInfo。
模板文件里面,直接调用了逻辑代码里面,data部分的定义的数据。
{{userInfo.nickName}}
{{motto}}
采用两个大括号,表示模板里面的变量。如果你用过angular、vue的话,你会发现,和angular的很类似。
这里面,使用了类似数据绑定的概念,js里面data数据,会传递给模板文件。
常用框架的数据传递方式:
Jquery : 操纵 DOM
Angularjs、Vue : 数据绑定来代替
React :虚拟DOM,
微信小程序: 数据绑定, 双向绑定,单向绑定
Onload:微信小程序定义的接口,类似于Drupal里面的钩子,或者事件分发。
在小程序显示的时候,隐藏的时候,退出的时候,都有对应的系统函数可供调用,我们可以在这里面,实现自己的逻辑。
setData的使用
That.setData 最好这样设置
That.data.motto = ‘123’ 这样的设置,有问题。
模板文件中的逻辑指令:
wx:if
wx:else
类似的执行有很多,可以参考官方文档。
对于页面里面元素,点击操作,我们可以为它绑定一个响应事件,这里是通过:bindtap="bindViewTap"
完成的。
{{userInfo.avatarUrl}}
{{}}
其它需要注意:
这个示例,用到了小程序的缓存,获取缓存,设置缓存:
var logs = wx.getStorageSync('logs') || []
logs.unshift(Date.now())
wx.setStorageSync('logs', logs)
每个小程序缓存有个大小限制,印象中是 20M,大体够用。
rpx, 这里的r: response,响应式,自适应。
程序跑起来了,我们需要改动一下,比如把首页面里面的文字做以下修改:
Hello word: 你好 Demo
Wechat : Demo Title
找到修改的地方,完成这个任务。说明我们能够在示例的基础之上,对代码修改修改。很多时候,我们只是代码的搬运工。
最初的工具版本,开始有个配置,在线版本工具里面,通过点击“工具”>> “项目详情”,看到项目设置:
图8-2-2-11
下面的四个复选框,开始我都是选中的。特别是最后一个。后来开发好了以后,上传到微信小程序后台,才发现,需要使用https。这里特别强调的一点事,最终我们是需要配置https的。
作者:老葛 亚艾元软件
这是我开发好的效果:
图8-2-3-1
代码可以在github上面下载。
前面我们已经熟悉了微信小程序的基本结构,我打算先写一个微信小程序下面的象棋播放器,作为练手。我花了整整一个周末的时间,终于搞定了。现在我介绍一下大致的流程:
首先,需要熟悉微信小程序有关canvas的文档
https://mp.weixin.qq.com/debug/wxadoc/dev/component/canvas.html#canvas
图8-2-3-2
微信文档里面给出了例子,我们可以让它的代码跑起来。
画一个象棋棋盘:
让你画一个象棋棋盘,你一定开始觉得无从下手。如果让你在canvas上面画一条直线,这应该不会是一个难的问题。实际上,一个棋盘,就是由这样的一条一条的线构成,我们把它一条一条的画出来,最终就会画成我们的棋盘。
我开发小程序棋谱播放器的时候,就是按照这样的思路,我在小程序的canvas上面画了一条线,成功了。Github上面,有多个象棋棋谱播放器的程序,我也下载了很多,作为参考。
画一条直线:
function lineDrawing(context, mx, my, lx, ly) {
context.beginPath()
context.moveTo(mx, my)
context.lineTo(lx, ly)
context.stroke()
}
开始的时候,没有对代码进行封装,后来改造为了一个函数。
画一个棋盘:
function qipanDrawing(context, offsetX, offsetY, cellSize) {
context.setStrokeStyle("brown")
context.setLineWidth(2)
//context.rect(3, 3, 320, 360)
//context.stroke()
for(var i= 1; i< 8; i++){
lineDrawing(context,cellSize * i + offsetX, offsetY, cellSize * i + offsetX, cellSize * 4 + offsetY)
lineDrawing(context,cellSize * i + offsetX, cellSize * 5 + offsetY, cellSize * i + offsetX, cellSize * 9 + offsetY)
}
//棋盘行
for(var i= 0; i<10; i++){
lineDrawing(context,offsetX, i * cellSize + offsetY, cellSize * 8 + offsetX, i * cellSize + offsetY)
}
lineDrawing(context,offsetX, offsetY, offsetX, cellSize * 9 + offsetY)
lineDrawing(context,cellSize * 8 + offsetX, offsetY, cellSize * 8 + offsetX, cellSize * 9 + offsetY)
lineDrawing(context, cellSize * 3 + offsetX, offsetY, cellSize * 5 + offsetX, cellSize * 2 + offsetY)
//lineDrawing(context, cellSize * 3 + offsetX, offsetY, cellSize * 5 + offsetX, cellSize * 2 + offsetY)
lineDrawing(context, cellSize * 5 + offsetX, offsetY, cellSize * 3 + offsetX, cellSize * 2 + offsetY)
lineDrawing(context, cellSize * 3 + offsetX, cellSize * 9 + offsetY, cellSize * 5 + offsetX, cellSize * 7 + offsetY)
lineDrawing(context, cellSize * 5 + offsetX, cellSize * 9 + offsetY, cellSize * 3 + offsetX, cellSize * 7 + offsetY)
var unit = 4;
//var centers = [];
centerDrawing(context,cellSize * 1 + offsetX, cellSize * 2 + offsetY, unit)
centerDrawing(context,cellSize * 7 + offsetX, cellSize * 2 + offsetY, unit)
centerDrawing(context,cellSize * 0 + offsetX, cellSize * 3 + offsetY, unit)
centerDrawing(context,cellSize * 2 + offsetX, cellSize * 3 + offsetY, unit)
centerDrawing(context,cellSize * 4 + offsetX, cellSize * 3 + offsetY, unit)
centerDrawing(context,cellSize * 6 + offsetX, cellSize * 3 + offsetY, unit)
centerDrawing(context,cellSize * 8 + offsetX, cellSize * 3 + offsetY, unit)
centerDrawing(context,cellSize * 0 + offsetX, cellSize * 6 + offsetY, unit)
centerDrawing(context,cellSize * 2 + offsetX, cellSize * 6 + offsetY, unit)
centerDrawing(context,cellSize * 4 + offsetX, cellSize * 6 + offsetY, unit)
centerDrawing(context,cellSize * 4 + offsetX, cellSize * 6 + offsetY, unit)
centerDrawing(context,cellSize * 8 + offsetX, cellSize * 6 + offsetY, unit)
centerDrawing(context,cellSize * 1 + offsetX, cellSize * 7 + offsetY, unit)
centerDrawing(context,cellSize * 7 + offsetX, cellSize * 7 + offsetY, unit)
context.setFillStyle("#000000");
context.setFontSize(cellSize * 0.75)
//console.log(cellSize *1 + 5 + offsetX)
//console.log( cellSize *5 - 10 + offsetY)
offsetX = offsetX +15
context.fillText("汉", cellSize *1 + 5 + offsetX, cellSize *5 - 10 + offsetY);
//context.fillText("汉", 65, 210);
context.fillText("界", cellSize *2 + 5 + offsetX, cellSize *5 - 10 + offsetY);
context.fillText("楚", cellSize *5 + 5 + offsetX, cellSize *5 - 10 + offsetY);
context.fillText("河", cellSize *6 + 5 + offsetX, cellSize *5 - 10 + offsetY);
offsetX = offsetX -10
context.setFontSize(cellSize * 0.3)
context.fillText("亚", cellSize * 3 + 5 + offsetX, cellSize * 4.5 -5 + offsetY);
context.fillText("艾", cellSize * 4 - 5 + offsetX, cellSize * 4.5 -5 + offsetY);
context.fillText("元", cellSize * 4.5 +5 + offsetX, cellSize * 4.5 -5 + offsetY);
context.fillText("象", cellSize * 3 + 5 + offsetX, cellSize *5 - 5 + offsetY);
context.fillText("棋", cellSize * 4 - 5 + offsetX, cellSize *5 - 5 + offsetY);
context.fillText("网", cellSize * 4.5 +5 + offsetX, cellSize *5 - 5 + offsetY);
}
其中centerDrawing用来画象棋棋盘里面的中心点,对应函数:
function centerDrawing(context, x, y, unit) {
//中心点
x =x;
y = y;
//左上
if(x - unit > 20){
lineDrawing(context, x - unit, y - 3 * unit, x - unit, y - unit);
lineDrawing(context, x - unit, y - unit, x - 3 * unit, y - unit);
}
//右上
if(x + unit < unit * 80){
lineDrawing(context, x + unit, y - 3 * unit, x + unit, y - unit);
lineDrawing(context, x + unit, y - unit, x + 3 * unit, y - unit);
}
//左下
if(x - unit > 20){
lineDrawing(context, x - unit, y + 3 * unit, x - unit, y + unit);
lineDrawing(context, x - unit, y + unit, x - 3 * unit, y + unit);
}
//右下
if(x + unit < unit * 80){
lineDrawing(context, x + unit, y + 3 * unit, x + unit, y + unit);
lineDrawing(context, x + unit, y + unit, x + 3 * unit, y + unit);
}
}
context.fillText用来在棋盘上面写字,我们知道棋盘上面通常有楚河汉界四个字。
context.fillText("汉", cellSize *1 + 5 + offsetX, cellSize *5 - 10 + offsetY);
在这四个字之间,我增加了“亚艾元象棋网”6个小字。
画一个棋子:
棋盘画出来以后,在棋盘上面加个棋子,其实就是在指定位置画一个圈,加个背景色,然后再写个字。先画一个棋子,然后将它总结成为函数:
function qiziDrawing(context, x, y, qizi_text, isred) {
context.setLineWidth(2)
context.beginPath()
context.arc(x+1, y+1, 18, 0, 2 * Math.PI)
context.setFillStyle("rgba(250, 240, 240,1)")
context.fill();
var stroke_style = isred ? "red" : "#000000";
context.setStrokeStyle(stroke_style)
context.stroke()
//context.stroke();
context.setFillStyle(stroke_style);
context.setTextAlign('center')
context.setFontSize(30)
context.fillText(qizi_text, x+1, y+11);
}
画一个局面:
在象棋里面,通常使用一个fen字符串来表示象棋的当前局面,有关fen字符串的更详细的内容,可以参考象棋百科全书里面的介绍。这里简单加以介绍。
为方便表示,中国象棋的棋子名称除了用汉字以外,还可以用字母,字母可从国际象棋中稍加改动得到,而数字是为了方便棋谱的输入(以便用在数字小键盘上)(见表一):
红方黑方字母相当于国际象棋中的棋子数字帅将KKing(王)1仕士AAdvisor(没有可比较的棋子)2相象B[1]Bishop(象)3马马N[2]Knight(马)4车车RRook(车)5炮炮CCannon(没有可比较的棋子)6兵卒PPawn(兵)7
表一 中国象棋棋子代号[1] 世界象棋联合会推荐的字母代号为E(Elephant)[2] 世界象棋联合会推荐的字母代号为H(Horse)
|
其中红方用大写,黑方用小写。
另外,对于棋谱招法,我们采用ICCS格式:
ICCS是中国象棋互联网服务器(Internet Chinese Chess Server)的缩写。在网络对弈服务器处理着法时,把着法表示成起点和终点的坐标是最方便的 ,因此这种格式最早在计算机上使用。
1. H2-E2(炮二平五) H7-E7(炮8平5)2. E2-E6(炮五进四) D9-E8(士4进5)3. H0-G2(马二进三) H9-G7(马8进7)4. B2-E2(炮八平五) B9-C7(马2进3)5. E6-E4(前炮退二) I9-H9(车9平8)6. ……(如右图)
在“中国象棋通用引擎协议”(UCCI协议)中,坐标格式得到进一步简化,例如H2-E2记作h2e2,把符号限制在一个32位数据中,处理起来速度更快。 |
图8-2-3-3
|
采用ICCS简写的形式。
"rnbakab1r/9/1c4nc1/p1p1p1p1p/9/9/P1P1P1P1P/1C2C4/9/RNBAKABNR w"
局面分两部分:"rnbakab1r/9/1c4nc1/p1p1p1p1p/9/9/P1P1P1P1P/1C2C4/9/RNBAKABNR”, “w"。第一部分,表示每个位置上面的棋子,第二部分表示当前该有哪方走棋。
对于第一部分,这就是一个字符串。里面的字母用来表示对应的棋子。数字表示棋盘这一行,连续多少个位置上面没有棋子。从上面到下面共有10行,9表示这一行一个棋子也没有。
从这里,我们可以看出,一个局面包含了多个棋子,并且包含了这些棋子的位置信息。我们将局面的fen字符串,循环处理,分别画出单个棋子。最终就组成了一个当前局面。
function fenDrawing(context, fen, offsetX, offsetY, cellSize) {
if(fen == ''){
fen = "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w";
}
var fen_array =new Array();
var qiju = new Array();
fen_array = fen.split("/");
//console.log(fen_array);
//console.log(fen);
//var length = arr.length;
for (var i = 0; i < 10; i++) {
var fen_item = fen_array[i] || '';
var k = 0;
if(fen_item == '' || fen_item == '9'){
continue;
}else{
for(var j=0;j<fen_item.length; j++){
if(k >8){
break;
}
var qizi_name = fen_item.charAt(j);
if(/^\d+$/.test(qizi_name)){
k = parseInt(qizi_name) + k;
//console.log(k);
//console.log(qizi_name);
}else{
var qizi_text = convertMachineToReadName(qizi_name);
var isred = (qizi_name === qizi_name.toUpperCase());
qiziDrawing(context, cellSize * k + offsetX, cellSize * i + offsetY, qizi_text, isred);
k++;
}
}
}
}
context.draw()
}
同样我将一个局面分装成了一个函数。先手动的一个一个画出一个局面,最后再做的封装。
招法与局面的转换
一个棋谱,有一个初始局面,还有一个招法列表。每走一步,就是一个新的局面,为了方便,我根据初始局面,还有招法,生成一个局面数组。这个数组里面包含了这个棋谱的所有局面,每一步的局面。前面我们能够画出,一个局面的canvas表示,每到一个局面,我们都给它重新画一遍。 这些代码,写好以后,对齐做以下封装,这样方便调用。
function getFenList(fen, moves) {
var fen_list = new Array();
fen_list[0] = fen;
var total_steps = Math.floor(moves.length / 4);
var current_fen = fen;
for(var i = 1; i<= total_steps ; i++){
var move = moves.substring((i-1) * 4, i * 4);
fen_list[i] = nextFen(current_fen, move);
current_fen = fen_list[i];
}
return fen_list;
}
function nextFen(fen, move) {
var x_array = new Array();
x_array["a"] = "0";
x_array["b"] = "1";
x_array["c"] = "2";
x_array["d"] = "3";
x_array["e"] = "4";
x_array["f"] = "5";
x_array["g"] = "6";
x_array["h"] = "7";
x_array["i"] = "8";
if(fen == ''){
fen = "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w";
}
var fen_twoparts = fen.split(" ");
var fen_first = fen_twoparts[0];
var fen_second = fen_twoparts[1] || 'w';
var fen_array =new Array();
fen_array = fen_first.split("/");
var from_x = move.charAt(0);
var from_y = move.charAt(1);
var from_fen_item = fen_array[9-from_y];
var to_x = move.charAt(2);
var to_y = move.charAt(3);
var to_fen_item = fen_array[9-to_y];
if(from_y == to_y){
var from_row_array = fenItemToRowArray(from_fen_item);
//todo
var from_x_index = x_array[from_x];
var qizi_name = from_row_array[from_x_index];
from_row_array[from_x_index] = '';
//from_fen_item = rowArrayToFenItem(from_row_array);
var to_x_index = x_array[to_x];
from_row_array[to_x_index] = qizi_name;
fen_array[9-from_y] = rowArrayToFenItem(from_row_array);
}else{
var from_row_array = fenItemToRowArray(from_fen_item);
//todo
var from_x_index = x_array[from_x];
var qizi_name = from_row_array[from_x_index];
from_row_array[from_x_index] = '';
//from_fen_item = rowArrayToFenItem(from_row_array);
fen_array[9-from_y] = rowArrayToFenItem(from_row_array);
var to_row_array = fenItemToRowArray(to_fen_item);
//todo
var to_x_index = x_array[to_x];
to_row_array[to_x_index] = qizi_name;
//from_fen_item = rowArrayToFenItem(from_row_array);
fen_array[9-to_y] = rowArrayToFenItem(to_row_array);
}
fen_first = fen_array.join("/");
if(fen_second == 'w'){
fen_second = 'b';
}else{
fen_second = 'w';
}
var next_fen = fen_first + ' ' + fen_second;
return next_fen;
}
function fenItemToRowArray(fen_item) {
var row_array = new Array();
var k = 0;
for(var j=0;j<fen_item.length; j++){
if(k >8){
break;
}
var qizi_name = fen_item.charAt(j);
if(/^\d+$/.test(qizi_name)){
var g = 0;
while(g < qizi_name){
row_array[k] = '';
k++;
g++;
if(k >8){
break;
}
}
}else{
row_array[k] = qizi_name;
k++;
}
}
return row_array;
}
function rowArrayToFenItem(row_array) {
var fen_item ="";
var k = 0;
for(var i = 0; i < row_array.length; i++){
if(row_array[i] != ''){
if(k == 0){
fen_item = fen_item + row_array[i];
}else{
fen_item = fen_item + k + row_array[i];
k = 0;
}
}else{
k++;
}
}
if(k > 0){
fen_item = fen_item + k;
}
return fen_item;
}
这样,当一个棋谱加载的时候,初始局面的canvas表示,我们就可以轻松实现了。
var util = require('../../utils/util.js')
Page({
data: {
current_step: 0,
total_steps: 0,
fen_list: [],
auto_play_text:'自动',
auto_play:false,
qipu: {}
},
onReady: function (e) {
// 使用 wx.createContext 获取绘图上下文 context
var context = wx.createCanvasContext('qipuCanvas')
var offsetX = 20
var offsetY = 20
var cellSize = 40;
var qipu = {
//fen: "rnbakab1r/9/1c4nc1/p1p1p1p1p/9/9/P1P1P1P1P/1C2C4/9/RNBAKABNR w",
title:'王天一 先胜 赵国荣',
event:'“腾讯棋牌”2017年全国象棋男子甲级联赛',
fen:'',
//moves: 'h0g2i9h9i0h0c6c5g3g4b9c7b0a2a6a5b2c2c7b5a0a1c9e7h0h6d9e8a1d1b5a3d1b1h7i7h6g6b7c7c2c1i7i8g4g5i8g8g6f6g8g5g2f4c7c6f6f8a9d9b1b6c6c8f8f6h9h4f4e6g7e6e2e6a3c2g0e2h4d4f0e1c2e3f6f3e3d5e6e5c8c9e0f0d9d7b6b9e9d9c1c2d5c7b9a9d4e4c2d2d7d2e5e8d2d0e1d0f9e8d0e1d9e9f3f5a5a4c3c4c5c4a9a4e4e3i3i4e3e4a4a7g5g7a7a3e4i4a3g3c7e6f5f6i4e4g3g4e4g4e2g4e6c5f6i6c4d4'
moves:'g0e2b7d7a0a1b9c7a1d1f9e8b0a2a9b9a3a4b9b5d1d4b5f5a2b4c6c5h2h1h7h5h1c1c7d5d4h4d5f4c1f1f4h3h4h3d7h7h3h5f5h5h0g2g9e7b4a6h5h1f1f4g6g5f4i4h9i7f0e1i6i5i4d4i7h5d4d3e6e5b2b5i9i6a6b4h1h2d3d2i6e6b4d3h5g3d3e5h7g7i0f0h2h6c3c4e6f6f0i0h6h2g2f0h2h6c4c5e7c5f0g2c5e7e5c4h6g6b5b6f6b6c4b6g6b6i0h0b6g6h0h4g5g4e2g4g7g4g2i1g4g5h4g4g3h1i1h3h1i3g4g0i5i4h3f4g6h6f4d5i4h4c0e2g5e5g0g3i3h5e3e4e5e2e1f2h6e6d5c3e6d6d2d5e2b2d5e5e9f9g3f3d6f6e5f5f9e9c3d5f6d6e4e5b2b5d5c3b5f5f3f5h5g7f5f4h4h3c3d5g7i6d0e1i6g5a4a5h3g3a5b5g5h3d5b6d6c6e0d0e8d7e5d5d9e8f4d4g3f3d5d6c6c0d0d1c0c1d1d0c1c0d0d1f3f2d4h4c0c1d1d0c1c3h4h9e8f9b6d7e9d9h9f9d9d8f9f8d8d9f8e8c3c0d0d1c0c1d1d0c1c0d0d1c0c1d1d0c1c7b5c5f2e2c5c6e2e1c6c7h3f2e8e9d9d8c7c8'
}
var init_fen= "rnbakab1r/9/1c4nc1/p1p1p1p1p/9/9/P1P1P1P1P/1C2C4/9/RNBAKABNR w";
//fen = "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w";
//fen = "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C2C4/9/RNBAKABNR b";
//fen = "rnbakab1r/9/1c4nc1/p1p1p1p1p/9/9/P1P1P1P1P/1C2C4/9/RNBAKABNR w";
//h2e2 h9g7
var moves = 'h0g2i9h9i0h0c6c5g3g4b9c7b0a2a6a5b2c2c7b5a0a1c9e7h0h6d9e8a1d1b5a3d1b1h7i7h6g6b7c7c2c1i7i8g4g5i8g8g6f6g8g5g2f4c7c6f6f8a9d9b1b6c6c8f8f6h9h4f4e6g7e6e2e6a3c2g0e2h4d4f0e1c2e3f6f3e3d5e6e5c8c9e0f0d9d7b6b9e9d9c1c2d5c7b9a9d4e4c2d2d7d2e5e8d2d0e1d0f9e8d0e1d9e9f3f5a5a4c3c4c5c4a9a4e4e3i3i4e3e4a4a7g5g7a7a3e4i4a3g3c7e6f5f6i4e4g3g4e4g4e2g4e6c5f6i6c4d4';
//var moves = 'h0g2i9h9i0h0c6c5';
var fen_list = util.getFenList(qipu.fen, qipu.moves)
//this.setData({
// fen_list:fen_list
//})
var total_steps = Math.floor(qipu.moves.length / 4);
this.setData({
qipu:qipu,
fen_list:fen_list,
total_steps:total_steps
})
console.log(fen_list);
util.qipanDrawing(context,offsetX, offsetY, cellSize)
util.fenDrawing(context, fen_list[this.data.current_step], offsetX, offsetY, cellSize)
this.gotoInit()
}
})
<button type="default" size="mini" plain="true" bindtap="gotoPrevious" hover-class="qipu-button-hover"> 后退 </button>
实现控制按钮:
在模板文件里面添加:
<view>
<!-- canvas.wxml -->
<canvas style="width: 360px; height: 400px;" canvas-id="qipuCanvas"></canvas>
<!-- 当使用绝对定位时,文档流后边的 canvas 的显示层级高于前边的 canvas -->
<view>
<button type="default" size="mini" plain="true" disabled="true" hover-class="qipu-button-hover">{{current_step}}/{{total_steps}}</button>
<button type="default" size="mini" plain="true" bindtap="gotoPrevious" hover-class="qipu-button-hover"> 后退 </button>
<button type="default" size="mini" plain="true" bindtap="gotoNext" hover-class="qipu-button-hover"> 前进 </button>
</view>
<view>
<button type="default" size="mini" plain="true" bindtap="gotoInit" hover-class="qipu-button-hover"> 初始 </button>
<button type="default" size="mini" plain="true" bindtap="gotoEnd" hover-class="qipu-button-hover"> 终局 </button>
<button type="default" size="mini" plain="true" bindtap="autoPlay" hover-class="qipu-button-hover"> {{auto_play_text}} </button>
</view>
<text>{{qipu.title}}</text>
<text>{{qipu.event}}</text>
</view>
为为初始、中局、前进、后退,添加对应的代码:
gotoEnd: function (e) {
this.setData({
current_step:this.data.total_steps
})
var fen_list = this.data.fen_list
var context = wx.createCanvasContext('qipuCanvas')
var offsetX = 20
var offsetY = 20
var cellSize = 40;
util.qipanDrawing(context,offsetX, offsetY, cellSize)
util.fenDrawing(context, fen_list[this.data.current_step], offsetX, offsetY, cellSize)
},
gotoInit: function (e) {
this.setData({
current_step:0
})
var fen_list = this.data.fen_list
var context = wx.createCanvasContext('qipuCanvas')
var offsetX = 20
var offsetY = 20
var cellSize = 40;
util.qipanDrawing(context,offsetX, offsetY, cellSize)
util.fenDrawing(context, fen_list[this.data.current_step], offsetX, offsetY, cellSize)
},
gotoNext: function (e) {
var next_step = this.data.current_step +1
if(next_step <= this.data.total_steps){
this.setData({
current_step:next_step
})
var fen_list = this.data.fen_list
var context = wx.createCanvasContext('qipuCanvas')
var offsetX = 20
var offsetY = 20
var cellSize = 40;
util.qipanDrawing(context,offsetX, offsetY, cellSize)
util.fenDrawing(context, fen_list[this.data.current_step], offsetX, offsetY, cellSize)
}
},
gotoPrevious: function (e) {
var previous_step = this.data.current_step - 1
if(previous_step >= 0){
this.setData({
current_step:previous_step
})
var fen_list = this.data.fen_list
var context = wx.createCanvasContext('qipuCanvas')
var offsetX = 20
var offsetY = 20
var cellSize = 40;
util.qipanDrawing(context,offsetX, offsetY, cellSize)
util.fenDrawing(context, fen_list[this.data.current_step], offsetX, offsetY, cellSize)
}
},
自动播放
我们需要使用setInterval, 以前我用过这个函数,但是第一个参数都一个一个完整的函数,而这次则是使用this.interval = setInterval(this.autoMove,2000);有所区别,试了一下,正常。注意播放完了以后,使用clearInterval清除这个定时器。对应代码:
autoMove: function (e) {
this.gotoNext();
if(this.data.current_step == this.data.total_steps){
clearInterval(this.interval);
this.setData({
auto_play_text:'自动',
auto_play:false
})
}
},
autoPlay: function (e) {
if(this.data.auto_play == false){
this.setData({
auto_play_text:'暂停',
auto_play:true
})
this.interval = setInterval(this.autoMove,2000);
}else{
clearInterval(this.interval);
this.setData({
auto_play_text:'自动',
auto_play:false
})
}
var previous_step = this.data.current_step - 1
if(previous_step >= 0){
this.setData({
current_step:previous_step
})
var fen_list = this.data.fen_list
var context = wx.createCanvasContext('qipuCanvas')
var offsetX = 20
var offsetY = 20
var cellSize = 40;
util.qipanDrawing(context,offsetX, offsetY, cellSize)
util.fenDrawing(context, fen_list[this.data.current_step], offsetX, offsetY, cellSize)
}
},
当前局面所在的步数,这个小功能,是后来加的。代码都分散在前面几个函数里面。
实际上,在这个棋谱播放器里面,我犯了一个错误,就是红方用的是“兵”,不是“卒”,结果我弄了一个红“卒”出来,有时候眼花,过了好长一段时间,我才发现这个问题。这是因为我在网页版的棋谱播放器里面也是用的红“卒”,我使用程序为每个棋谱动态的生成gif文件的时候,中间检查文件,偶然发现的。
还有就是棋谱默认宽度360px,算上两边的边框400px,在有的手机上显示有问题。
作者: 老葛 亚艾元软件
先出页面,对应的文件都创建出来。逻辑层js里面的代码,空着先,模板文件里面写上对应文字。比如对于新闻动态页面,我们是这样创建的:
news.js
Page({
data: {
items: []
},
onLoad: function () {
console.log('onLoad')
var that = this
}
})
news.wxml:
<view>
<text>动图棋谱-象甲蒋川飞刀喋血 王天一超级逆转1</text>
<text>动图棋谱-象甲蒋川飞刀喋血 王天一超级逆转1</text>
<text>动图棋谱-象甲蒋川飞刀喋血 王天一超级逆转1</text>
<text>动图棋谱-象甲蒋川飞刀喋血 王天一超级逆转1</text>
<text>动图棋谱-象甲蒋川飞刀喋血 王天一超级逆转1</text>
</view>
news.wxss,里面代码为空,只有注释:
/**news.wxss**/
这样的话,我们可以快速的创建多个静态的小程序页面,将这些页面在app.json里面注册一下:
{
"pages":[
"pages/index/index",
"pages/qipus/qipus",
"pages/qipus/qipuitem",
"pages/players/players",
"pages/news/news"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "亚艾元象棋",
"navigationBarTextStyle":"black"
}
}
其它小程序页面,前期也是这么创建,先搭建一个骨架出来,然后再往里面写内容。这个时候,我们调整index页面的模板文件,调整的内容如下:
<!--index.wxml-->
<view>
<view>
<view>请输入与查找棋谱相关的关键字:</view>
<input placeholder="王天一" type="text"/>
<button>搜索</button>
</view>
<view>
<navigator url="/pages/qipus/qipus" hover-class="navigator-hover">最新棋谱</navigator>
<navigator url="/pages/opening/opening" hover-class="navigator-hover">开局导航</navigator>
<navigator url="/pages/players/players" hover-class="navigator-hover">棋手风采</navigator>
</view>
<view>
<navigator url="/pages/news/news?type=1" hover-class="navigator-hover">象棋动态</navigator>
<navigator url="/pages/news/news?type=2" hover-class="navigator-hover">象棋赛事</navigator>
<navigator url="/pages/news/news?type=3" hover-class="navigator-hover">象棋典故</navigator>
</view>
</view>
这里面,我们使用了微信小程序的组件navigator,实现了从小程序页面之间的跳转。
接下来要做的时候实现里面的具体功能。我们以新闻列表为例,首先先实现一个简单的版本。这里面,我们要做的工作包含两部分,小程序端的,Drupal端的。在小程序端,我们需要发起一个http请求,获取象棋新闻的最新数据,获取后,将其展现出来。Drupal端,我们需要配置rest结构,为小程序提供数据。
加了http请求的news.js代码:
Page({
data: {
items: []
},
onLoad: function () {
console.log('onLoad')
var that = this
wx.request({
url: 'http://chess.yaiyuan.com/jsonnews/' + options.type + '?page=' + that.data.page,
method: 'GET',
success:function(res) {
that.setData({
items: res.data
})
console.log(that.data.items)
}
})
}
})
微信小程序,发起http请求,使用 wx.request,向其提供URL路径,使用的方法“GET”,请求成功后,调用 success:function(res) 回调函数。在成功回调函数里面,我们将http请求返回的数据res.data赋值给页面数据的items。为了调试的方便,我这里面加了console.log。另外,需要注意的是var that =this这句代码。这主要是方便wx.request里面调用当前页面的setData函数。我的理解是,wx.request的成功回调函数里面,this所指的对象已经不是当前页面,为了引用当前页面,在范围外,需要将它设置为that。
对应的模板文件:
<view>
<view wx:for="{{items}}" >
<navigator url="/pages/news/newsitem?nid={{item.nid}}" >
<view>{{item.title}}</view>
</navigator>
</view>
</view>
Drupal端,使用views输出,注意显示类型选择REST export,做以下配置:
图8-2-4-1
我的象棋新闻分三类:动态、赛事、典故,所以这里面,使用了上下文过滤器,传递一个分类术语id。字段的话,我们输出字段、标题、内容ID。如果以前使用过Drupal7下面的service的话,输出的json的字段的机读名字可以在字段的label里面设置,但是在Drupal8里面,是在这个地方设置:
图8-2-4-2
点击这类的设置,我们看到这样的弹出框:
图8-2-4-3
这里面,可以设置字段的别名。如果不修改的话,输出这样的格式:
[{"field_image":"","title":"2017\u5e74\u5168\u56fd\u8c61\u68cb\u4e2a\u4eba\u8d5b\u6fc0\u6218\u6b63\u9163 \u7537\u5b50\u5341\u516d\u5f3a\u4ea7\u751f","nid":"89605"},
修改后:
[{"img_url":"","title":"2017\u5e74\u5168\u56fd\u8c61\u68cb\u4e2a\u4eba\u8d5b\u6fc0\u6218\u6b63\u9163 \u7537\u5b50\u5341\u516d\u5f3a\u4ea7\u751f","nid":"89605"},
我是后来才发现这样配置的,所以前期我用的默认配置,后来都沿用了默认配置。
另外,这里还用到了小程序页面之间参数的传递,就目前我了解的,小程序,只支持通过参数传递。我们通过这样的URL格式传递参数:
/pages/news/news
/pages/news/news?type=1
在参数的接收页面
onLoad: function (options) {
console.log('onLoad')
var that = this;
var type = options.type;
Onload参数options里面,包含了参数的信息,我们可以这样取到参数的值。
作者: 老葛 亚艾元软件
在前面的代码里面,我们成功的将小程序和Drupal结合了起来。但是我们的这个列表,还有所不足,只去了前面10条数据。如果用户想看后面的数据怎么办,这就涉及到了一个分页的问题。不过在手机上面,我们不准备实现一个和Drupal类似的分页器功能,实现的要具有小程序(手机端)特色。这就是手机屏幕滚动到最下面的时候,自动加载下10条记录。Drupal端,我们需要提供分页的支持,我们选用的是完整分页。一页10条记录。
在微信小程序端,我们将代码做以下修改:
Page({
data: {
title: '',
page: 0,
pageSize: 10,
hasMoreData: true,
type:'',
items: []
},
getArticles: function (message) {
var that = this;
wx.request({
url: 'http://chess.yaiyuan.com/jsonnews/' + that.data.type + '?page=' + that.data.page,
method: 'GET',
success:function(res) {
var nodelist = res.data
var itemsTem = that.data.items
if (nodelist.length < that.data.pageSize) {
that.setData({
hasMoreData: false,
items: itemsTem.concat(nodelist)
})
}else{
that.setData({
hasMoreData: true,
page: that.data.page + 1,
items: itemsTem.concat(nodelist)
})
}
//console.log(that.data.items)
}
})
},
onReachBottom: function() {
// Do something when page reach bottom.
//this.getArticles();
if (this.data.hasMoreData) {
this.getArticles('加载更多数据')
} else {
wx.showToast({
title: '没有更多数据',
})
}
},
onLoad: function (options) {
console.log('onLoad')
var that = this;
var type = options.type;
var title = "";
if(type == 1){
title = '象棋动态';
}
if(type == 2){
title = '象棋赛事';
}
if(type == 3){
title = '象棋典故';
}
that.setData({
title: title,
type:type
})
that.getArticles();
}
})
在这里,在页面的data里面,我们增加了:
page: 0,
pageSize: 10,
hasMoreData: true,
这三个参数是用来做分页的,page用来标识当前的页码;pageSize表示一次多少条;hasMoreData,布尔值,用来标识当前还有没有更多数据。
我们将获取数据的任务封装成为了一个getArticles函数,里面包含了向Drupal端发起http请求。里面的代码我们已经熟悉了。我们这里做了对应修改,主要是处理分页的逻辑。
var itemsTem = that.data.items
if (nodelist.length < that.data.pageSize) {
that.setData({
hasMoreData: false,
items: itemsTem.concat(nodelist)
})
}else{
that.setData({
hasMoreData: true,
page: that.data.page + 1,
items: itemsTem.concat(nodelist)
})
}
如果当前返回的数据的条数,小于pageSize,表示后面没有更多数据了。否则的话,表示还有更多数据。
微信小程序提供了接口:onReachBottom ,我们通过实现这个接口,
onReachBottom: function() {
// Do something when page reach bottom.
//this.getArticles();
if (this.data.hasMoreData) {
this.getArticles('加载更多数据')
} else {
wx.showToast({
title: '没有更多数据',
})
}
},
这样在到达底部的时候,如果还有更多数据,我们继续加载;如果没有更多数据了,我们提示一下用户,信息提供的对话框,使用wx.showToast。
在onload接口里面,我们初始化一下数据,然后调用getArticles。
作者: 老葛 亚艾元软件
当我们第一次实现新闻详细页面的时候,效果如下:
图8-2-6-1
对应代码:
newsitem.js
Page({
data: {
node: {}
},
onLoad: function (options) {
//console.log('onLoad')
var that = this
wx.request({
url: 'http://chess.yaiyuan.com/jsonnewsitem/' + options.nid,
method: 'GET',
success:function(res) {
that.setData({
node: res.data[0]
})
}
})
}
})
newsitem.wxml
<view>
<view>{{node.title}}</view>
<view>{{node.body}}</view>
</view>
正文部分是乱的。
经过搜索和研究,发现Wxparse可以解决这个问题。还有一个办法是只提供文本,对于图片,单独给出。
项目地址:https://github.com/icindy/wxParse
我按照Wxparse提供的文档,试了一下,果然可以解决这个问题。
将它下载下来,放到和pages并列的目录:
图8-2-6-2
图8-2-6-3
修改象棋新闻的对应代码:
var WxParse = require('../../wxParse/wxParse.js');
Page({
data: {
node: {}
},
onLoad: function (options) {
//console.log('onLoad')
var that = this
wx.request({
url: 'http://chess.yaiyuan.com/jsonnewsitem/' + options.nid,
method: 'GET',
success:function(res) {
that.setData({
node: res.data[0]
})
var article = res.data[0].body;
WxParse.wxParse('article', 'html', article, that, 5);
}
})
}
})
修改模板文件:
<view>
<view>{{node.title}}</view>
<view>
<import src="../../wxParse/wxParse.wxml"/>
<template is="wxParse" data="{{wxParseData:article.nodes}}"/>
</view>
<!--<view>{{node.body}}</view> -->
</view>
修改后的显示效果:
图8-2-6-4
当然个别地方,还是有点小问题,不过这个HTML转义的问题就这样解决了。
作者:老葛 亚艾元软件
开始的样式比较丑,丑到家了。我仅仅实现了功能,这是最初的样式:
图8-2-7-1
图8-2-7-2
经过搜索查找,微信官方团队提供的weui,作为一个UI框架,效果不错。经过对比,我决定,采用weui提供的CSS代码,作为一个起点。
我用的是weui针对小程序的版本,项目地址:https://github.com/Tencent/weui-wxss/
可以在https://weui.io/在线的查看效果。我先是看了一下weui提供的所有示例,然后对照我自己的小程序,看可以用到哪个效果。有点使用bootstrap这类框架的意思。找好了以后,套上去,个别地方加上自己的CSS调整。
引入weui.wxss,将下载下来的weui-wxss里面的weui.wxss文件复制到我们的项目的根目录下面:
图8-2-7-3
修改app.wxss文件,在头部增加以下代码:
@import 'weui.wxss';
page{
background-color: #F8F8F8;
font-size: 16px;
font-family: -apple-system-font,Helvetica Neue,Helvetica,sans-serif;
}
.page__hd {
padding: 5px;
text-align:center;
}
.page__bd {
padding-bottom: 40px;
}
.page__bd_spacing {
padding-left: 15px;
padding-right: 15px;
}
.page__ft{
padding-bottom: 10px;
text-align: center;
}
.page__title {
text-align: center;
font-size: 20px;
font-weight: 400;
}
.page__desc {
margin-top: 5px;
color: #888888;
text-align: left;
font-size: 14px;
}
.weui-cells{
margin-top:0px;
}
.container {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 200rpx 0;
box-sizing: border-box;
}
我把示例里面的wxss代码部分也都拷贝了进来。
参考微信UI的示例代码,改造。Wxml文件。
以 news.wxml为例:
原来:
<view>
<view wx:for="{{items}}">
<navigator url="/pages/news/newsitem?nid={{item.nid}}" >
<view>{{item.title}}</view>
</navigator>
</view>
</view>
改进后:
<view>
<view>
<view>{{title}}</view>
</view>
<view>
<view class="weui-cells weui-cells_after-title">
<view wx:for="{{items}}" class="weui-cell weui-cell_access" hover-class="weui-cell_active">
<navigator url="/pages/news/newsitem?nid={{item.nid}}" >
<view>{{item.title}}</view>
</navigator>
</view>
</view>
</view>
</view>
这是改进后的效果:
图8-2-7-4
样式满意了,可能不完美,但是对于我这样的程序员,来说,够用就好。
将其它页面,也都改为weui提供的结构,比如新闻详细页面:
newsitem.wxml:
<view>
<view>
<view>{{node.title}}</view>
</view>
<view>
<view>
<import src="../../wxParse/wxParse.wxml"/>
<template is="wxParse" data="{{wxParseData:article.nodes}}"/>
</view>
</view>
</view>
作者: 老葛 亚艾元软件
首页有一个搜索框,这里有一个重要的知识点,是获取用户的输入数据并跳转到搜索页面。
首页部分,搜索模板:
<view>
<view>
<view>
<view>
<!--<icon type="search" size="14"></icon>-->
<input type="text" bindinput="inputTyping" bindconfirm="search" placeholder="棋谱搜索" value="{{inputVal}}" />
</view>
</view>
<button type="default" size="mini" plain="true" bindtap="search" hover-class="qipu-button-hover">
搜索
</button>
</view>
</view>
这里重点部分:
<input type="text" bindinput="inputTyping" bindconfirm="search" placeholder="棋谱搜索" value="{{inputVal}}" />
对应的js:
var app = getApp()
Page({
data: {
inputShowed: false,
inputVal: ""
},
showInput: function () {
this.setData({
inputShowed: true
});
},
showInput: function () {
this.setData({
inputShowed: true
});
},
hideInput: function () {
this.setData({
inputVal: "",
inputShowed: false
});
},
clearInput: function () {
this.setData({
inputVal: ""
});
},
inputTyping: function (e) {
this.setData({
inputVal: e.detail.value
});
//console.log(e.detail.value)
},
search: function () {
wx.navigateTo({
url: '/pages/search/search?keys=' + this.data.inputVal
})
},
onLoad: function () {
console.log('onLoad')
var that = this
}
})
这段代码,也是从示例中复制过来的,我做了相应的修改。搜索框里面,用户输入的信息,可以通过bindinput="inputTyping",传递给逻辑层。当按了Enter键的时候, 调用逻辑层的search方法。如果用户点击了搜索按钮,也是调用逻辑层的search方法。
在搜索页面,这是对应的实现:
Search.js:
Page({
data: {
keys:'',
page: 0,
pageSize: 10,
hasMoreData: true,
items: []
},
inputTyping: function (e) {
this.setData({
keys: e.detail.value
});
//console.log(e.detail.value)
},
search: function () {
var that = this;
console.log(that.data.keys)
wx.request({
url: 'http://chess.yaiyuan.com/jsonsearch/' + that.data.keys + '?page=' + that.data.page,
method: 'GET',
success:function(res) {
var nodelist = res.data
var itemsTem = that.data.items
if (nodelist.length < that.data.pageSize) {
that.setData({
hasMoreData: false,
items: itemsTem.concat(nodelist)
})
}else{
that.setData({
hasMoreData: true,
page: that.data.page + 1,
items: itemsTem.concat(nodelist)
})
}
//console.log(that.data.items)
}
})
},
searchNew: function () {
this.setData({
page: 0,
pageSize: 10,
hasMoreData: true,
items: []
})
this.search()
},
onReachBottom: function() {
// Do something when page reach bottom.
//this.getArticles();
if (this.data.hasMoreData) {
this.search()
} else {
wx.showToast({
title: '没有更多数据',
})
}
},
onLoad: function (options) {
//console.log('onLoad')
//var keys = options.keys || '';
this.setData({
keys:options.keys
})
if(this.data.keys != ''){
this.search();
}
}
})
对应模板文件:
<view>
<view>
<view>
<view>
<view>
<input type="text" bindinput="inputTyping" bindconfirm="searchNew" placeholder="棋谱搜索" value="{{keys}}" />
</view>
</view>
<button type="default" size="mini" plain="true" bindtap="searchNew" hover-class="qipu-button-hover">
搜索
</button>
</view>
<view class="weui-cells searchbar-result" wx:if="{{keys.length > 0}}">
<block wx:for="{{items}}">
<navigator url="/pages/qipus/qipuitem?uuid={{item.uuid}}" hover-class="weui-cell_active">
<view class="weui-media-box__hd weui-media-box__hd_in-appmsg">
<image src="{{item.img_url}}" />
</view>
<view class="weui-media-box__bd weui-media-box__bd_in-appmsg">
<view>{{item.title}}</view>
<view>{{item.event}}</view>
</view>
</navigator>
</block>
</view>
</view>
</view>
模板文件中,搜索框部分的代码,和首页类似,其它部分,就是根据用户的输入,动态的向服务器发起请求,获取数据,并展示。
在这里,微信发起http请求的代码,在测试的事后是可以工作的,但是上传到微信端,成为体验版本,在真实的微信上面运行却是不能工作:
'http://chess.yaiyuan.com/jsonsearch/' + that.data.keys + '?page=' + that.data.page,
搜索不能生效,我经过测试定位,找到了这里,正确的代码应该这样写:
wx.request({
url: 'https://chess.yaiyuan.com/jsonsearch/' + encodeURIComponent(that.data.keys) + '?page=' + that.data.page,
需要使用encodeURIComponent处理一下用户输入,这样才能解决中文字符问题。
作者: 老葛 亚艾元软件
棋手列表,棋手详细信息展示,棋谱列表、棋谱详细页面,这些功能,和新闻列表、新闻详细类似,对于棋谱详细页面,前面专门讲过,这里只需要改进一下,把棋谱的获取改为动态,从服务器下载即可。
我们先在Drupal端,准备好json数据,这是我的配置:
图8-2-9-1
这里,棋谱开局列表,相对复杂一些,主要是在小程序的逻辑层,写了一点稍微复杂的代码。
Opening.js里面包含了获取象棋开局分类的代码:
Page({
data: {
terms: []
},
reOrganizeTerms: function (terms) {
var pterms = [];
var subterms = {};
for (var i = 0; i < terms.length; i++) {
var term = terms[i] || {};
var ptid = term.ptid || '';
if( ptid == ''){
pterms.push(term);
}else{
var temp_subterms = subterms[ptid] || [];
temp_subterms.push(term);
subterms[ptid] = temp_subterms;
}
}
for (var j = 0; j < pterms.length; j++) {
var pterm = pterms[j] || {};
var tid = pterm.tid || '';
//var subs = subterms[tid] || [];
pterm.subs = subterms[tid] || [];
pterms[j] = pterm;
}
return pterms;
},
onLoad: function (options) {
//console.log('onLoad')
var that = this;
wx.request({
url: 'http://chess.yaiyuan.com/jsonopenings',
method: 'GET',
success:function(res) {
var termsTmp = res.data;
var terms = that.reOrganizeTerms(termsTmp);
that.setData({
terms: terms
})
//console.log(that.data.terms)
}
})
}
})
对应模板文件:opening.wxml,代码如下:
<view>
<view>
<view>布局导航</view>
</view>
<view>
<block wx:for="{{terms}}" wx:for-index="idx" wx:for-item="term">
<view>
<view>
<navigator url="/pages/opening/openinglist?tid={{term.tid}}">
{{term.name}}
</navigator>
</view>
<view>
<view class="weui-media-box weui-media-box_small-appmsg">
<view class="weui-cells weui-cells_in-small-appmsg">
<block wx:for="{{term.subs}}" wx:for-index="idx1" wx:for-item="sub">
<navigator url="/pages/opening/openinglist?tid={{sub.tid}}" class="weui-cell weui-cell_access" hover-class="weui-cell_active">
<view class="weui-cell__bd weui-cell_primary">
<view>{{sub.name}}</view>
</view>
<view class="weui-cell__ft weui-cell__ft_in-access"></view>
</navigator>
</block>
</view>
</view>
</view>
</view>
</block>
</view>
</view>
开局包含两级分类,从Drupal端获取到的json数据,格式没有明确包含层级关系,但是也自带的层级关系。为了让它满足微信小程序端的需要,我自己写了一个reOrganizeTerms函数,重新组织了层级关系。
在模板文件中,使用了两边wx:for循环:
<block wx:for="{{terms}}" wx:for-index="idx" wx:for-item="term">
<block wx:for="{{term.subs}}" wx:for-index="idx1" wx:for-item="sub">
这里面就这么多的道道。
图8-2-9-2
作者: 老葛 亚艾元软件
微信开发者工具里面允许我们将小程序代码,传递到微信小程序的对应服务器上,在新版本里面,这样操作:
图8-2-10-1
点击最右边的箭头:
图8-2-10-2
点击这里的上传:
图8-2-10-3
此时会让填写版本好,项目备注。填写好了以后,点击上传即可。
上传过后,登陆小程序。在开发管理界面,我们看到,我们提交的代码,
图8-2-10-4
还在开发版本里面,这个时候我们只能体验一吧先,审核需要时间,所以就体验了一下。通过体验,发现了这样的错误,首先是需要https配置,配置好了以后,发现搜索不能工作,经过检查,发现需要在请求URL里面使用encodeURIComponent才能正常。
这些问题解决了以后,可以让更多人体验一下小程序,我将小程序放到QQ群里面,发现体验者没有权限,这个时候,可以在小程序后台管理界面的用户身份里面,为特定用户添加体验权限:
图8-2-10-5
从提交审核申请,到审核批准,大约花了四个小时,还是比较快的。我是中午提交的。审核通过以后,还需要自己提交发布一下,这样我们的小程序,终于和大家见面了。
作者: 老葛 亚艾元软件
我们的网站使用的是Nginx,服务器用的阿里云,网上有比较多的参考文档。
http://www.jianshu.com/p/e3d945fa82b4
首先需要购买证书,我购买的是免费版本的Symantec的DV SSL。进入阿里云的控制台,在左边的导航里面,找到“安全(云盾)” 》 “CA证书(数据安全)”,点击进入。
图8-2-11-1
购买后,补全信息,完成域名验证,这是成功后的样子:
图8-2-11-2
点击下载,可以看到有多个选择:
图8-2-11-3
按照官方文档安装即可:
文件说明:
1. 证书文件214222535920882.pem,包含两段内容,请不要删除任何一段内容。
2. 如果是证书系统创建的CSR,还包含:证书私钥文件214222535920882.key。
( 1 ) 在Nginx的安装目录下创建cert目录,并且将下载的全部文件拷贝到cert目录中。如果申请证书时是自己创建的CSR文件,请将对应的私钥文件放到cert目录下并且命名为214222535920882.key;
( 2 ) 打开 Nginx 安装目录下 conf 目录中的 nginx.conf 文件,找到:
# HTTPS server
# #server {
# listen 443;
# server_name localhost;
# ssl on;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_timeout 5m;
# ssl_protocols SSLv2 SSLv3 TLSv1;
# ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
# ssl_prefer_server_ciphers on;
# location / {
#
#
#}
#}
( 3 ) 将其修改为 (以下属性中ssl开头的属性与证书配置有直接关系,其它属性请结合自己的实际情况复制或调整) :
server {
listen 443;
server_name localhost;
ssl on;
root html;
index index.html index.htm;
ssl_certificate cert/214222535920882.pem;
ssl_certificate_key cert/214222535920882.key;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
location / {
root html;
index index.html index.htm;
}
}
保存退出。
( 4 )重启 Nginx。
( 5 ) 通过 https 方式访问您的站点,测试站点证书的安装配置。
配置成功后,访问:https://chess.yaiyuan.com,成功。
图8-2-11-4
作者: 老葛 亚艾元软件
我们前面写的棋谱播放器,是固定宽度大小的。宽360px,高400px。我在我的手机上测试,这个宽度没有问题。不过上线了以后,发现有的手机上,屏幕宽度不足360px,这样棋谱的显示,就不全。
为了解决这个问题,我们需要动态的获取当前手机屏幕的宽度,然后根据当前手机屏幕的宽度,来设置棋盘的宽度和长度。如何获取屏幕的宽度呢?微信小程序提供了wx.getSystemInfo这个接口,可以获取以下系统参数:
参数 |
说明 |
最低版本 |
brand |
手机品牌 |
|
model |
手机型号 |
|
pixelRatio |
设备像素比 |
|
screenWidth |
屏幕宽度 |
|
screenHeight |
屏幕高度 |
|
windowWidth |
可使用窗口宽度 |
|
windowHeight |
可使用窗口高度 |
|
language |
微信设置的语言 |
|
version |
微信版本号 |
|
system |
操作系统版本 |
|
platform |
客户端平台 |
|
fontSizeSetting |
用户字体大小设置。以“我-设置-通用-字体大小”中的设置为准,单位:px |
|
SDKVersion |
客户端基础库版本 |
官方的示例代码:
wx.getSystemInfo({
success: function(res) {
console.log(res.model)
console.log(res.pixelRatio)
console.log(res.windowWidth)
console.log(res.windowHeight)
console.log(res.language)
console.log(res.version)
console.log(res.platform)
}
})
除了getSystemInfo接口以外,还有wx.getSystemInfoSync()可用,后者是同步接口,前者是异步接口。我们在这里是用的前者。
在我们的qipuitem.js里面,代码做如下修改:
data: {
current_step: 0,
total_steps: 0,
fen_list: [],
auto_play_text:'自动',
auto_play:false,
qipu: {},
cell_size: 40,
qipan_width: 360,
qipan_height: 400,
uuid: ''
},
...
onLoad: function(options) {
this.setData({
uuid: options.uuid,
})
var that = this;
wx.getSystemInfo({
success: function(res) {
var win_width = res.windowWidth;
var cellSize = parseInt((win_width - 10) /9);
if(cellSize > 50){
cellSize = 50;
}
that.setData({
cell_size: cellSize,
qipan_width: cellSize *9,
qipan_height: cellSize *10,
})
//console.log(res.model)
//console.log(res.pixelRatio)
//console.log(res.windowWidth)
//console.log(res.windowHeight)
//console.log(res.language)
//console.log(res.version)
}
})
},
....
onReady: function (e) {
// 使用 wx.createContext 获取绘图上下文 context
var context = wx.createCanvasContext('qipuCanvas')
//var offsetX = 20
//var offsetY = 20
var cellSize = this.data.cell_size;
var offsetX = parseInt(cellSize/2);
var offsetY = parseInt(cellSize/2);
...
在这里,我们获取了屏幕的宽度,然后根据它的大小计算出来棋盘里面一个格子的大小,最后计算出来棋盘的宽度和高度。象棋棋盘,九条竖线,十条横线。修改与棋谱播放器的相关函数,使其支持棋盘大小的自适应。
对应的模板文件qipuitem.wxml部分,也做了调整:
<view>
<!-- canvas.wxml -->
<canvas style="width:{{qipan_width}}px; height:{{qipan_height}}px; margin:5px;" canvas-id="qipuCanvas"></canvas>
<!-- 当使用绝对定位时,文档流后边的 canvas 的显示层级高于前边的 canvas -->
开始的时候,我尝试在CSS,或者在代码里面,设置小程序canvas的宽度、高度,但是都没有成功。只有在模板文件中,设置大小才能工作。这个和H5下面的canvas还是有区别的,纯H5下面的canvas,可以使用js设置canvas大小。另外,这里的css规则也不一样。纯H5下面这样写:
<canvas width=”360” height=”400” canvas-id="qipuCanvas"></canvas>
小程序里面,放到了style里面。
代码写好以后,调试、测试以后,在微信开发者工具里面,使用多个分辨率测试,都正常。
图8-2-12-1
作者: 老葛 亚艾元软件
我后来,试用了以前其它小程序,大部分小程序,底部都有一个好用的tab导航。特别是参考了微信小程序数据助手的导航设计。对比一下,原来的首页几个图标的设计,已经太丑了,而且当时那几个图标,也都是用的同一个,非常不美观。
新的改版,其中的重要的改进就是增加底部的tab导航,还有顶部的tab导航。这是改进后的样子:
图8-2-13-1
实现底部的导航,我们需要修改小程序的app.json文件:
{
"pages":[
"pages/qipus/qipus",
"pages/index/index",
"pages/search/search",
"pages/qipus/qipuitem",
"pages/players/players",
"pages/players/playeritem",
"pages/opening/opening",
"pages/opening/openinglist",
"pages/events/events",
"pages/events/eventlist",
"pages/my/my",
"pages/my/aboutus",
"pages/my/myshares",
"pages/my/myfavorites",
"pages/my/myreads",
"pages/my/mysettings",
"pages/news/news",
"pages/news/newsitem"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "亚艾元象棋",
"navigationBarTextStyle":"black"
},
"tabBar": {
"color": "#6e6d6b",
"selectedColor": "#0C8EE9",
"borderStyle": "white",
"backgroundColor": "#fff",
"box-shadow":"0 0 6px 0",
"font-size":"20px",
"list": [{
"pagePath": "pages/qipus/qipus",
"iconPath": "images/nav/chip-off.png",
"selectedIconPath": "images/nav/chip-on.png",
"text": "棋谱"
}, {
"pagePath": "pages/news/news",
"iconPath": "images/nav/dynamic-off.png",
"selectedIconPath": "images/nav/dynamic-on.png",
"text": "动态"
},{
"pagePath": "pages/search/search",
"iconPath": "images/nav/search-off.png",
"selectedIconPath": "images/nav/search-on.png",
"text": "搜索"
}, {
"pagePath": "pages/my/my",
"iconPath": "images/nav/my-off.png",
"selectedIconPath": "images/nav/my-on.png",
"text": "我的"
}]
},
"networkTimeout": {
"request": 10000,
"downloadFile": 10000
}
}
这里使用tabBar参数,设置底部导航,是小程序推荐的方式。在这个参数下面,有这么一些参数可供使用:
"color": "#6e6d6b",
"selectedColor": "#0C8EE9",
"borderStyle": "white",
"backgroundColor": "#fff",
"box-shadow":"0 0 6px 0",
"font-size":"20px",
用来控制底部导航的样式,比如颜色、选中颜色、背景颜色、字体大小、边框样式、阴影效果.
里面的list包含一个数组。里面包含每个tab的设置。对于每个tab,我们可以设置下面几个选项:
"pagePath": "pages/qipus/qipus",
"iconPath": "images/nav/chip-off.png",
"selectedIconPath": "images/nav/chip-on.png",
"text": "棋谱"
pagePath:小程序页面路径
Text:tab标签文本
iconPath:图标路径
selectedIconPath:选中图标路径。
为了让我们的图标更漂亮,我们还制作了一组图标:
图8-2-13-2
图标分两种,一种是未选中的样子,一种是选中后的样子,通常选中后的要突出显示。
个人页面的之后,我们后面再介绍,先把这个tab实现了。
作者:老葛 亚艾元软件
底部tab实现了以后,我们来看一下,顶部的tabs的实现,这是我实现后的效果:
图8-2-14-1
图8-2-14-2
我们先以动态部分的顶部tab为例。首先在news.wxml里面,添加四个导航navigator链接。
<view>
<view>
<view>
<navigator url="/pages/news/news" open-type="reLaunch" class="tab-list {{type=='all' ? 'active' : ''}}" hover-class="navigator-hover">全部</navigator>
<navigator url="/pages/news/news?type=1" open-type="reLaunch" class="tab-list {{type=='1' ? 'active' : ''}}" hover-class="navigator-hover">动态</navigator>
<navigator url="/pages/news/news?type=2" open-type="reLaunch" class="tab-list {{type=='2' ? 'active' : ''}}" hover-class="navigator-hover">赛事</navigator>
<navigator url="/pages/news/news?type=3" open-type="reLaunch" class="tab-list {{type=='3' ? 'active' : ''}}" hover-class="navigator-hover">典故</navigator>
</view>
</view>
<view>
<view class="weui-cells weui-cells_after-title">
<view wx:for="{{items}}" wx:key="*this" class="weui-cell weui-cell_access" hover-class="weui-cell_active">
<navigator url="/pages/news/newsitem?nid={{item.nid}}" >
<view>{{item.title}}</view>
</navigator>
</view>
</view>
</view>
</view>
在news.js,做以下修改:
onLoad: function (options) {
//console.log('onLoad')
var that = this;
var type = options.type || "";
var title = "";
if(type == 1){
title = '象棋动态';
}
if(type == 2){
title = '象棋赛事';
}
if(type == 3){
title = '象棋典故';
}
if(type == ""){
type = 'all';
title = '象棋动态';
}
that.setData({
title: title,
type:type
})
that.getArticles();
就是设置了全部显示的情况。新闻动态这里面,上面的4个tab,对应的微信小程序页面,都是这个"pages/news/news",只不过传递的参数不一样。
对于我们增加的navigator,有两部需要注意:
open-type="reLaunch" class="tab-list {{type=='2' ? 'active' : ''}}"
打开类型,我们选择了reLaunch,表示重新加载。对于class,我们使用三位运算符,根据参数type的不同,选择输出active。
调整样,在news.wxss里面增加以下代码:
.btn-area navigator{
width:24.9%;
float:left;
}
.btn-area{
height:30px;
clear:both;
}
.btn-area navigator.navigator-hover,
.btn-area navigator.active{
border-bottom: 2px solid blue;
}
这样做出来的效果,就如同我们前面看到的那样:
图8-2-14-3
棋谱部分,顶部的四个tab的实现,和这里类似,只不过,四个tab对应四个不同的页面。我们以里面的布局tab为例:
图8-2-14-4
首先在对应页面的模板opening.wxml中,增加导航链接:
<view>
<view>
<view>
<view>
<navigator url="/pages/qipus/qipus" open-type="reLaunch" hover-class="navigator-hover">最新</navigator>
<navigator url="/pages/opening/opening" class="tab-list active" hover-class="navigator-hover">布局</navigator>
<navigator url="/pages/events/events" hover-class="navigator-hover">赛事</navigator>
<navigator url="/pages/players/players" hover-class="navigator-hover">棋手</navigator>
</view>
</view>
</view>
<view>
...
然后调整对应的样式opening.wxss:
.btn-area navigator{
width:24.9%;
float:left;
}
.btn-area{
height:30px;
clear:both;
}
.btn-area navigator.navigator-hover,
.btn-area navigator.active{
border-bottom: 2px solid blue;
}
依次对其它3个页面的模板文件做同样的操作。由于我们分别在4个模板文件中添加导航链接,每个模板中navigator,哪个是active的,我们可以单独设置,我们将当前页面所在的navigator设置为active即可。对应的样式代码,也是一样,我们拷贝到对应页面的样式文件中即可。
这里面,我们只讲qipus页面的navigator的打开类型设置为了reLaunch,一般情况下,这是没事的,不过上线以后,发现了一个小问题,这样我我对小程序里面的页面管理,有了更深一步的认识。
作者: 老葛 亚艾元软件
我们这里主要讲解,微信服务号“亚艾元象棋”的配置。“亚艾元象棋”的前身是“亚艾元培训”,再前面是“老葛的Drupal培训班”,因为名字的原因,从最初的名字改为了“亚艾元培训”,最近将其改为“亚艾元象棋”。我们想做一个网站,能够充分的说明我们的技术,同时又具有实用性。加上我本人一直是一名象棋的爱好者,所以以象棋棋谱网站为例,作为Drupal制作网站的示例。
首先,前期的准备工作,一个是需要申请一个微信服务号,https://mp.weixin.qq.com/,微信公众号的注册申请,我们这里就略过了;申请完了以后,要通过认证,说白了是给微信缴纳300元的认证费,如果不认证的话,基本上是没有接口权限的,后面的工作就做不了。
认证完了以后,我们需要制作一个Drupal网站,注意这里是Drupal,而且是Drupal8。如果你现在刚开始学习Drupal,建议你学习Drupal8,因为Drupal7还是10年前的技术,整体架构已经落伍了不少;从Drupal7到Drupal8的升级很痛苦,但是从Drupal8到Drupal9的升级会很平滑。我们的亚艾元象棋网,已经初步搭建完毕。运行的环境:nginx、php7.1、mysql,centos7。
我们从drupal官网上下载微信模块wechat模块,下载地址:
https://www.drupal.org/project/wechat,注意下载8.x-1.0-beta3或者更高版本。
下载完毕,将其解压缩,传到网站的modules目录。导航到:admin/modules页面,找到微信模块,在前面的复选框中,将对应模块选中。
图8-1-1
这里有3个模块,wechat、 wechat menu、wechat default config,熟悉了以后,其实第3个模块可以不用启用的,自己配置即可。wechat default config只是为了降低初学者的配置成本,给出来的一个默认配置。wechat default config模块依赖于image url formatter模块。
导航到admin/wechat/config/default
图8-1-2
这里需要设置Token 、AppID、 AppSecret,将来还需要设置EncodingAESKey,在后面的版本中,将会增加这个配置选项。这些配置选项,除了Token ,是自己的设定的以外,AppID、 AppSecret都是微信公众平台提供的。
登陆微信公众平台,https://mp.weixin.qq.com/,点击右边菜单的“开发”---“基本设置”
图8-1-3
我们会看到如下所示的界面
图8-1-4
从这里获取AppID、AppSecret。注意这里的IP白名单设置,需要将我们网站的公网IP地址,填到这里。
图8-1-5
IP白名单,是微信后面新增的一个安全限制,开始的时候没有。微信是一个一致在进入的系统。
图8-1-6
如果这里没有启用的话,需要启用,并填写或者修改这里的配置。注意服务器地址:
http://chess.yaiyuan.com/wechat,域名+内部路径,我们的内部路径为wechat。Drupal端,对应配置配置好后,这里的配置也做对应修改。Token在Drupal端,微信公众号后台,需要保持一致。注意,wechat模块,暂时只支持明文模式,后续版本将会支持安全模式。所以目前只能选择明文模式。
这里保存设置,保存成功表示Drupal端与微信公众号后端通信成功;如果失败,请检查白名单是否设置,已经各项设置是否正确。
作者: 老葛 亚艾元软件
我们导航到admin/wechat/config/menu,这是我的配置:
图8-1-7
微信菜单:最多两级,一级菜单最多3个,一级菜单下属二级菜单最多5个。
点击上面的添加按钮,进入页面admin/wechat/config/menu/add:
图8-1-8
如果是一级菜单的话,只需要输入名字,选中父按钮即可。
图8-1-9
如果是二级菜单的话,需要选择所属的父级菜单:
图8-1-10
对于菜单类型,包含以下选项:
图8-1-11
点击推事件、跳转URL、扫码推事件、扫码推事件且弹出“消息接收中”提示框、弹出系统拍照发图、弹出拍照或者相册发图、弹出微信相册发图器、弹出地理位置选择器。常用的是点击推事件、跳转URL。
点击推事件的示例配置:
图8-1-12
跳转URL的示例配置:
图8-1-13
跳转URL,就是输入一个URL的绝对路径,这里需要注意的是,路径里面参数的值带有网址的话,需要url_encode一下,进行转义,最初不转义也可以工作,现在不行了。
我们使用权重,控制菜单项的相对位置,从小到大排列。在微信菜单的管理界面,我们没有实现可以拖拽调整顺序的效果,只能通过每个菜单项的权重进行调整。
这里,需要着说明的是,点击推事件的配置:
图8-1-14
Drupal里面处理列表都是用的视图(Views),对于微信里面的图文消息,wechat模块使用views对它进行相应,所以这里需要输入视图的机读名字、显示的机读名字,还有参数。
微信菜单推事件、微信默认搜索,这两个地方默认使用views进行响应。我们在wechat模块里面,实现了wechat response这个显示类型。这里我们看一下,我们views的具体配置,对于他们含义的详细讲解,参考后面的微信搜索的配置。
图8-1-15
我们需要构建一个entity的绝对路径,一种方式是使用wechat模块提供的:
Global: Entity absolute url
图8-1-16
一种是views自带的,覆写输出的形式:
图8-1-17
视图参数,我们用的分类术语,传递的是术语的tid,导航到分类页面,找到对应的分类术语,鼠标移动上去,地址里面的/taxonomy/term/[tid],这里可以获得tid。就是向views传递的参数。
图8-1-18
就是微信菜单项里面对应的参数。
图8-1-19
作者: 老葛 亚艾元软件
在微信的配置界面,找到“微信默认搜索”配置项:
图8-1-20
点击,进入页面:admin/wechat/config/default_search,我们看到
图8-1-21
这里我们需要弄明白,每个配置项的含义,如果你熟悉views的话,那么很简单。
l Views name: 视图的机读名字
l Views display: 显示的机读名字
l 过滤器标识符: 全文搜索对应字段的标识(机读名字)
admin/structure/views views的管理界面,找到上面的配置对应的视图
图8-1-22
Views的机读名字:
图8-1-23
可以在路径里面获取。
图8-1-24
再打开的视图的配置界面,点击上面的“wechat response”。
Display的名字,也可以在URL里面找到:
图8-1-25
也可以在views的高级配置里面找到:
图8-1-26
前面这两个相对简单一点,过滤器标识符,需要特别熟悉Drupal的人才会知道,我们这里给出它对应的位置,看这里:
图8-1-27
点击过滤条件“Search:全文搜索”, 或者你使用的搜索条件。我们看到这样的弹出框。
图8-1-28
这个还是比较熟悉的,我们往下拉,看到下面的信息:
图8-1-29
过滤器标识符就在这里,它其实是URL参数里面的key。
Views里面,全文搜索字段,必须是选中这个复选框的:“把这个过滤条件显示给网站的访问者,允许他们可以更改这个过滤条件。”
微信端的输入,最终传递给了这个过滤条件,这个过滤条件,用来检索用。
我们这里面使用的是solr, search API, search api solr, 注意安装的时候使用composer, composer 很多时候需要科学上网。
微信模块自带的默认配置,走的是Drupal核心自带搜索,需要启用核心search模块。
图8-1-30
默认搜索,对于中文来说,下面的配置“索引关键词最小字数”,从3改为1
图8-1-31
Drupal8下面,改进了一点,不改为1可能也能搜索出来。
需要注意几点:
1) display type 必须选择使用 wechat response:
图8-1-32
2) 必须做好Views字段与微信消息字段之间的映射关系。点击显示里面的设置。
图8-1-33
我们看到这样的弹出框。
图8-1-34
这里的label名字,是微信官方需要我们提供的。下拉列表里面的字段,是对应views的可用字段。
图8-1-35
这里的字段,是在views配置的FIelds里面添加的:
图8-1-36
这是另一个映射示例:
图8-1-37
总之这里的映射关系,需要配置好。
3)
显示的项目数量不能超过8。微信的限制:以前是10, 后来改成8了。推荐使用5,
图8-1-38
我们以工匠的精神,持续钻研Drupal有11年之久。我们总能帮助客户解决Drupal项目中的实际问题,很多客户头疼的实际问题,我们能在较短的时间内搞定。十年的持续积累和客户支持认可让我们很快成为了中国知名的Drupal开发商。我们将Drupal和小程序结合起来,开发出来了多款微信小程序产品,积累了丰富的微信小程序开发经验,为帮助用户掌握微信小程序开发,我们特别准备了微信小程序开发培训班。
与先进的互联网技术相结合,为用户提供更为优质的服务,搭建针对性的微应用,是目前高校信息化、图书馆信息化、政府机构信息化、企事业单位信息化的热点。小程序以微信9.6亿用户为载体,为用户提供了优质的体验。
我们的微信小程序开发培训班,结合业务实际,以基于小程序的“意见反馈”、“在线测试”、“讲座预定”为案例,以原生的PHP/MYSQL为基础,帮助学员学以致用、举一反三。助力学员将本单位的特色服务与微信小程序相结合,让用户满意、领导放心,抢先一步,还能发论文、评职称。
微信小程序开发培训班第一天内容
时间 |
主讲人 |
|
上午 |
崔克俊 葛红儒 |
培训班开班介绍、小程序发展介绍(崔克俊) 微信小程序概述,组件、API 微信小程序官方示例代码解析 问题与答疑 |
下午 |
葛红儒 |
在小程序里面动态展示列表数据 PHP端如何为小程序准备json数据 在小程序端解决下拉无限滚动 使用Wxparse实现文章详细页面的图文展示 问题与答疑 |
微信小程序开发培训班第二天内容
时间 |
主讲人 |
|
上午 |
葛红儒 |
WeUI概述、常用样式组件介绍 如何在项目中套用WeUI样式 使用tabBar实现底部导航、如何实现顶部tabs导航 问题与答疑 |
下午 |
葛红儒 |
实现小程序中页面转发(分享)功能 小程序搜索功能的实现 问题与答疑 |
微信小程序开发培训班第三天内容
时间 |
主讲人 |
|
上午 |
葛红儒 |
应用微信小程序缓存API 使用小程序Canvas制作动画,以棋谱播放为例 如何配置https 问题与答疑 |
下午 |
葛红儒 |
从小程序登陆PHP 从小程序向PHP提交数据 从小程序向PHP上传图片 问题与答疑 |
微信小程序开发教程,以免费的形式,完全发布到亚艾元官方博客thinkindrupal.com上面。与开发教程相关的小程序源代码,我们将以开源的方式发布到github上。
微信小程序开发培训班,我们采用实体的形式,现场教学,而非网络的形式,3天总共900元。
参加微信小程序开发培训班后,我们将会将用户加入我们的学员微信群里面。我们的小程序产品,对于有兴趣的学员单位,我们提供免费试用,后续只收维护费、定制开发费。
第1届 2017.12.16-18上海基于Drupal 8 的微信小程序开发培训班