Drupal 8 + 微信小程序

  作者: 老葛 亚艾元软件

微信小程序,正式版刚出来的时候,甚是激动。激动了不到一个月之后,发现里面限制太多,所以很快就转为观望状态。张小龙是一个让人尊敬的大神,由于近一年来,微信小程序,逐步的开放越来越多的接口,各种功能也愈加完善,所以决定,开始尝试微信小程序。

 

这是我写的第一个微信小程序,代码在:

https://github.com/g089h515r806/we_cn_chess

 

我尝试在微信小程序上面写一个象棋棋谱的播放器,花了一个周末的时间,终于成功了。这让我对于微信小程序,增加了十分的信心。

 

从微信小程序里面,我们可以看到Angularjsreactvue,这些当今流行JS框架的影子。我熟悉Angular 1.04.0,看了小程序里面的js代码以后,发现两者十分相似。

 

对于Drupal来讲,无头(HeadlessDrupal是一个方向,特别是对于移动互联网,现在各种前端框架,不断涌现。 IOSandroid原生应用蓬勃发展,只有将Drupal和移动互联网结合起来,才能壮大Drupal的版图。当然,Drupal仅仅是后台框架的一种,除了Drupal以外,PHPlaravelnodejsJAVApython等,都是流行的后端框架,与后者相比,Drupal还是小众技术。

 

   Drupal8是面向移动设计,内置Rest API,可以方便的将Drupal各种数据,提供给前端框架。比如新兴的Json API模块,极大的方便的和前端框架的数据交互。增强了Drupal作为一个框架的吸引力。

 

学习微信小程序,我的经验,首先是看官方文档,文档地址:

https://mp.weixin.qq.com/debug/wxadoc/dev/

 

8-2-1.png 

8-2-1

    把整个文档好好的看一遍,这才是权威的资料。

 

注册一个微信小程序账号,行动起来。已经认证的微信服务号、订阅号,可以使用原来的资料创建一个小程序账号。我使用的就是原来认证过的微信服务号。微信小程序账号的创建文档,参考官方文档。

 

下载开发者工具:

https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html

8-2-2.png 

8-2-2

根据你的系统,选择对应的版本。现在万事俱备,只欠动手了。


Drupal版本:

Drupal8+微信小程序:2,把示例代码跑起来

                   作者:老葛 亚艾元软件      

  打开安装好的微信web开发者工具程序:

 

8-2-2-1.png 

8-2-2-1

 

双击以后,我们进入这样的界面,需要扫码:

8-2-2-2.png 

8-2-2-2

   扫码成功后,看到了我的信息:

8-2-2-3.png 

8-2-2-3

 

这里选择本地小程序项目。我们进入一个添加项目页面:

 

8-2-2-4.png 

8-2-2-4

在这里输入项目名称,项目目录,注意项目目录文件夹里面为空:

8-2-2-5.png 

8-2-2-5

 

选中在当前目录中创建quick start项。最后点击“确定”按钮。我们进入了微信小城的示例界面:

8-2-2-6.png 

8-2-2-6

 

接下来,需要熟悉微信开发者工具的使用,就是将它的每个菜单选项都熟悉一下,点点看看。

8-2-2-7.png 

8-2-2-7

 

打开示例程序所在目录:

8-2-2-8.png 

8-2-2-8

 

这个目录下面包含4个文件:

app.json: 配置信息

app.js: 逻辑代码

app.wxss: 微信小程序的CSS代码。

project.config:项目配置文件

展开pages,我们看到里面包含indexlogs两个目录:

8-2-2-9.png 

8-2-2-9

 

 

8-2-2-10.png 

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}}

    采用两个大括号,表示模板里面的变量。如果你用过angularvue的话,你会发现,和angular的很类似。

这里面,使用了类似数据绑定的概念,js里面data数据,会传递给模板文件。

 

常用框架的数据传递方式:

Jquery : 操纵 DOM

AngularjsVue : 数据绑定来代替

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, 这里的rresponse,响应式,自适应。

 

程序跑起来了,我们需要改动一下,比如把首页面里面的文字做以下修改:

Hello word:  你好 Demo

Wechat :  Demo Title

找到修改的地方,完成这个任务。说明我们能够在示例的基础之上,对代码修改修改。很多时候,我们只是代码的搬运工。

 

最初的工具版本,开始有个配置,在线版本工具里面,通过点击“工具”>> “项目详情”,看到项目设置:

 

8-2-2-11.png 

8-2-2-11

 

   下面的四个复选框,开始我都是选中的。特别是最后一个。后来开发好了以后,上传到微信小程序后台,才发现,需要使用https。这里特别强调的一点事,最终我们是需要配置https的。

 

 


Drupal版本:

Drupal8+微信小程序:3,实现象棋棋谱播放器

作者:老葛 亚艾元软件

这是我开发好的效果:

8-2-3-1.png 

8-2-3-1

   代码可以在github上面下载。

   前面我们已经熟悉了微信小程序的基本结构,我打算先写一个微信小程序下面的象棋播放器,作为练手。我花了整整一个周末的时间,终于搞定了。现在我介绍一下大致的流程:

   首先,需要熟悉微信小程序有关canvas的文档

https://mp.weixin.qq.com/debug/wxadoc/dev/component/canvas.html#canvas

8-2-3-2.png 

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.png

图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,在有的手机上显示有问题。

 



Drupal版本:

Drupal8+微信小程序:4,实现小程序下面的动态列表

作者: 老葛 亚艾元软件

先出页面,对应的文件都创建出来。逻辑层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.png 

8-2-4-1

 

   我的象棋新闻分三类:动态、赛事、典故,所以这里面,使用了上下文过滤器,传递一个分类术语id。字段的话,我们输出字段、标题、内容ID。如果以前使用过Drupal7下面的service的话,输出的json的字段的机读名字可以在字段的label里面设置,但是在Drupal8里面,是在这个地方设置:

8-2-4-2.png 

8-2-4-2

 

点击这类的设置,我们看到这样的弹出框:

8-2-4-3.png 

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版本:

Drupal8+微信小程序:5,分页问题(滚动加载更多)的实现

 作者: 老葛 亚艾元软件

在前面的代码里面,我们成功的将小程序和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


Drupal版本:

Drupal8+微信小程序:6,使用Wxparse实现图文展示

 作者: 老葛 亚艾元软件

当我们第一次实现新闻详细页面的时候,效果如下:

8-2-6-1.png 

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.png 

8-2-6-2

 

8-2-6-3.png 

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.png 

8-2-6-4

 

当然个别地方,还是有点小问题,不过这个HTML转义的问题就这样解决了。


Drupal版本:

Drupal8+微信小程序:7,使用weUI改进样式

作者:老葛 亚艾元软件

开始的样式比较丑,丑到家了。我仅仅实现了功能,这是最初的样式:

8-2-7-1.png 

8-2-7-1

8-2-7-2.png 

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.png 

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.png 

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>

 


Drupal版本:

Drupal8+微信小程序:8,棋谱搜索功能的实现

作者: 老葛 亚艾元软件

首页有一个搜索框,这里有一个重要的知识点,是获取用户的输入数据并跳转到搜索页面。

   首页部分,搜索模板:

    <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版本:

Drupal8+微信小程序:9,象棋棋谱开局分类导航的实现

作者: 老葛 亚艾元软件

棋手列表,棋手详细信息展示,棋谱列表、棋谱详细页面,这些功能,和新闻列表、新闻详细类似,对于棋谱详细页面,前面专门讲过,这里只需要改进一下,把棋谱的获取改为动态,从服务器下载即可。

我们先在Drupal端,准备好json数据,这是我的配置:

8-2-9-1.png 

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.png

8-2-9-2

 


Drupal版本:

Drupal8+微信小程序:10,上传我们微信小程序

 作者: 老葛 亚艾元软件

微信开发者工具里面允许我们将小程序代码,传递到微信小程序的对应服务器上,在新版本里面,这样操作:

8-2-10-1.png 

8-2-10-1

   点击最右边的箭头:

8-2-10-2.png 

8-2-10-2

 

点击这里的上传:

8-2-10-3.png 

8-2-10-3

 

此时会让填写版本好,项目备注。填写好了以后,点击上传即可。

 

   上传过后,登陆小程序。在开发管理界面,我们看到,我们提交的代码,

8-2-10-4.png 

8-2-10-4

 

    还在开发版本里面,这个时候我们只能体验一吧先,审核需要时间,所以就体验了一下。通过体验,发现了这样的错误,首先是需要https配置,配置好了以后,发现搜索不能工作,经过检查,发现需要在请求URL里面使用encodeURIComponent才能正常。

   这些问题解决了以后,可以让更多人体验一下小程序,我将小程序放到QQ群里面,发现体验者没有权限,这个时候,可以在小程序后台管理界面的用户身份里面,为特定用户添加体验权限:

8-2-10-5.png 

8-2-10-5

 

   从提交审核申请,到审核批准,大约花了四个小时,还是比较快的。我是中午提交的。审核通过以后,还需要自己提交发布一下,这样我们的小程序,终于和大家见面了。


Drupal版本:

Drupal8+微信小程序:11, Nginx下面 https配置

作者: 老葛 亚艾元软件

我们的网站使用的是Nginx,服务器用的阿里云,网上有比较多的参考文档。

  http://www.jianshu.com/p/e3d945fa82b4

  

首先需要购买证书,我购买的是免费版本的SymantecDV SSL。进入阿里云的控制台,在左边的导航里面,找到“安全(云盾)” 》 “CA证书(数据安全)”,点击进入。

8-2-11-1.png 

8-2-11-1

购买后,补全信息,完成域名验证,这是成功后的样子:

8-2-11-2.png 

8-2-11-2

 

点击下载,可以看到有多个选择:

8-2-11-3.png 

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.png
8-2-11-4


Drupal版本:

Drupal8+微信小程序:12,使用getSystemInfo解决棋盘大小自适应问题

作者: 老葛 亚艾元软件

我们前面写的棋谱播放器,是固定宽度大小的。宽360px,高400px。我在我的手机上测试,这个宽度没有问题。不过上线了以后,发现有的手机上,屏幕宽度不足360px,这样棋谱的显示,就不全。

为了解决这个问题,我们需要动态的获取当前手机屏幕的宽度,然后根据当前手机屏幕的宽度,来设置棋盘的宽度和长度。如何获取屏幕的宽度呢?微信小程序提供了wx.getSystemInfo这个接口,可以获取以下系统参数:

参数

说明

最低版本

brand

手机品牌

1.5.0

model

手机型号


pixelRatio

设备像素比


screenWidth

屏幕宽度

1.1.0

screenHeight

屏幕高度

1.1.0

windowWidth

可使用窗口宽度


windowHeight

可使用窗口高度


language

微信设置的语言


version

微信版本号


system

操作系统版本


platform

客户端平台


fontSizeSetting

用户字体大小设置。以“我-设置-通用-字体大小”中的设置为准,单位:px

1.5.0

SDKVersion

客户端基础库版本

1.1.0

官方的示例代码:

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.png 

8-2-12-1


Drupal版本:

Drupal8+微信小程序:13, 使用tabBar实现底部tab导航

作者: 老葛 亚艾元软件

我后来,试用了以前其它小程序,大部分小程序,底部都有一个好用的tab导航。特别是参考了微信小程序数据助手的导航设计。对比一下,原来的首页几个图标的设计,已经太丑了,而且当时那几个图标,也都是用的同一个,非常不美观。

新的改版,其中的重要的改进就是增加底部的tab导航,还有顶部的tab导航。这是改进后的样子:

8-2-13-1.png 

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:小程序页面路径

   Texttab标签文本

   iconPath:图标路径

   selectedIconPath:选中图标路径。

为了让我们的图标更漂亮,我们还制作了一组图标:

8-2-13-2.png 

8-2-13-2

 

   图标分两种,一种是未选中的样子,一种是选中后的样子,通常选中后的要突出显示。

   个人页面的之后,我们后面再介绍,先把这个tab实现了。


Drupal版本:

Drupal8+微信小程序:14,顶部tabs的实现

作者:老葛 亚艾元软件

底部tab实现了以后,我们来看一下,顶部的tabs的实现,这是我实现后的效果:

8-2-14-1.png 

8-2-14-1

8-2-14-2.png  

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();

就是设置了全部显示的情况。新闻动态这里面,上面的4tab,对应的微信小程序页面,都是这个"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.png 

8-2-14-3

 

   棋谱部分,顶部的四个tab的实现,和这里类似,只不过,四个tab对应四个不同的页面。我们以里面的布局tab为例:

8-2-14-4.png 

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版本: