Drupal 8 与微信公众号、小程序开发

主要讲解使用Drupal8开发微信公众号(服务号),微信小程序。探讨最新的Drupal8、前端技术、微信小程序技术。

   我们的示例,主要以亚艾元象棋网为例,讲解Drupal8网站的搭建,Drupal8下面微信模块的使用,如何快速的配置出来一个微信服务号、如何制作一个微信小程序。

   网站:亚艾元象棋网

   微信服务号:亚艾元象棋

   微信小程序:亚艾元象棋

 

   中国象棋棋谱播放器,微信小程序,下载地址:

https://github.com/g089h515r806/we_cn_chess

   亚艾元象棋微信小程序,下载地址:

 

Drupal版本:

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

使用Drupal8 + wechat模块,配置微信服务号

  作者: 老葛 亚艾元软件

我们这里主要讲解,微信服务号“亚艾元象棋”的配置。“亚艾元象棋”的前身是“亚艾元培训”,再前面是“老葛的Drupal培训班”,因为名字的原因,从最初的名字改为了“亚艾元培训”,最近将其改为“亚艾元象棋”。我们想做一个网站,能够充分的说明我们的技术,同时又具有实用性。加上我本人一直是一名象棋的爱好者,所以以象棋棋谱网站为例,作为Drupal制作网站的示例。

 

首先,前期的准备工作,一个是需要申请一个微信服务号,https://mp.weixin.qq.com/,微信公众号的注册申请,我们这里就略过了;申请完了以后,要通过认证,说白了是给微信缴纳300元的认证费,如果不认证的话,基本上是没有接口权限的,后面的工作就做不了。

 

认证完了以后,我们需要制作一个Drupal网站,注意这里是Drupal,而且是Drupal8。如果你现在刚开始学习Drupal,建议你学习Drupal8,因为Drupal7还是10年前的技术,整体架构已经落伍了不少;从Drupal7Drupal8的升级很痛苦,但是从Drupal8Drupal9的升级会很平滑。我们的亚艾元象棋网,已经初步搭建完毕。运行的环境:nginxphp7.1mysqlcentos7

我们从drupal官网上下载微信模块wechat模块,下载地址:

https://www.drupal.org/project/wechat,注意下载8.x-1.0-beta3或者更高版本。

下载完毕,将其解压缩,传到网站的modules目录。导航到:admin/modules页面,找到微信模块,在前面的复选框中,将对应模块选中。

8-1-1.png 

                       8-1-1

    这里有3个模块,wechatwechat menuwechat default config,熟悉了以后,其实第3个模块可以不用启用的,自己配置即可。wechat default config只是为了降低初学者的配置成本,给出来的一个默认配置。wechat default config模块依赖于image url formatter模块。

 

导航到admin/wechat/config/default

8-1-2.png 

                        8-1-2

 

这里需要设置Token AppIDAppSecret,将来还需要设置EncodingAESKey,在后面的版本中,将会增加这个配置选项。这些配置选项,除了Token ,是自己的设定的以外,AppIDAppSecret都是微信公众平台提供的。

登陆微信公众平台,https://mp.weixin.qq.com/,点击右边菜单的“开发”---“基本设置”

 

 

8-1-3.png 

                             8-1-3

    我们会看到如下所示的界面

8-1-4.png 

 

 

8-1-4

从这里获取AppIDAppSecret。注意这里的IP白名单设置,需要将我们网站的公网IP地址,填到这里。

 

 

 8-1-5.png

 

                          8-1-5

IP白名单,是微信后面新增的一个安全限制,开始的时候没有。微信是一个一致在进入的系统。

8-1-6.png 

                          8-1-6

如果这里没有启用的话,需要启用,并填写或者修改这里的配置。注意服务器地址:

http://chess.yaiyuan.com/wechat,域名+内部路径,我们的内部路径为wechatDrupal端,对应配置配置好后,这里的配置也做对应修改。TokenDrupal端,微信公众号后台,需要保持一致。注意,wechat模块,暂时只支持明文模式,后续版本将会支持安全模式。所以目前只能选择明文模式。

这里保存设置,保存成功表示Drupal端与微信公众号后端通信成功;如果失败,请检查白名单是否设置,已经各项设置是否正确。


Drupal版本:

B,微信菜单的配置

  作者: 老葛 亚艾元软件

我们导航到admin/wechat/config/menu,这是我的配置:

8-1-7.png

                         8-1-7

 

微信菜单:最多两级,一级菜单最多3个,一菜单下属二级菜单最多5个。

   点击上面的添加按钮,进入页面admin/wechat/config/menu/add

 

 8-1-8.png

8-1-8

 

如果是一级菜单的话,只需要输入名字,选中父按钮即可。

8-1-9.png 

8-1-9

     如果是二级菜单的话,需要选择所属的父级菜单:

8-1-10.png 

8-1-10

 

对于菜单类型,包含以下选项:

8-1-11.png 

8-1-11

点击推事件、跳转URL、扫码推事件、扫码推事件且弹出“消息接收中”提示框、弹出系统拍照发图、弹出拍照或者相册发图、弹出微信相册发图器、弹出地理位置选择器。常用的是点击推事件、跳转URL

点击推事件的示例配置:

8-1-12.png 

8-1-12

 

跳转URL的示例配置:

8-1-13.png 

8-1-13

 

跳转URL,就是输入一个URL的绝对路径,这里需要注意的是,路径里面参数的值带有网址的话,需要url_encode一下,进行转义,最初不转义也可以工作,现在不行了。

 

我们使用权重,控制菜单项的相对位置,从小到大排列。在微信菜单的管理界面,我们没有实现可以拖拽调整顺序的效果,只能通过每个菜单项的权重进行调整。

 

这里,需要着说明的是,点击推事件的配置:

8-1-14.png 

8-1-14

Drupal里面处理列表都是用的视图(Views),对于微信里面的图文消息,wechat模块使用views对它进行相应,所以这里需要输入视图的机读名字、显示的机读名字,还有参数。

微信菜单推事件、微信默认搜索,这两个地方默认使用views进行响应。我们在wechat模块里面,实现了wechat response这个显示类型。这里我们看一下,我们views的具体配置,对于他们含义的详细讲解,参考后面的微信搜索的配置。

8-1-15.png 

8-1-15

我们需要构建一个entity的绝对路径,一种方式是使用wechat模块提供的:

Global: Entity absolute url

 

8-1-16.png 

8-1-16

一种是views自带的,覆写输出的形式:

8-1-17.png 

8-1-17

视图参数,我们用的分类术语,传递的是术语的tid,导航到分类页面,找到对应的分类术语,鼠标移动上去,地址里面的/taxonomy/term/[tid],这里可以获得tid。就是向views传递的参数。

 

8-1-18.png 

8-1-18

 

  就是微信菜单项里面对应的参数。

8-1-19.png 

8-1-19


Drupal版本:

C,搜索的实现

  作者: 老葛 亚艾元软件

在微信的配置界面,找到“微信默认搜索”配置项:

8-1-20.png 

8-1-20

  点击,进入页面:admin/wechat/config/default_search,我们看到

 

8-1-21.png 

8-1-21

这里我们需要弄明白,每个配置项的含义,如果你熟悉views的话,那么很简单。

Views name: 视图的机读名字

Views display: 显示的机读名字

过滤器标识符: 全文搜索对应字段的标识(机读名字)

 

admin/structure/views views的管理界面,找到上面的配置对应的视图

 

8-1-22.png 

8-1-22

 

Views的机读名字:

8-1-23.png 

8-1-23

可以在路径里面获取。

 

8-1-24.png 

8-1-24

再打开的视图的配置界面,点击上面的wechat response”。

Display的名字,也可以在URL里面找到:

 8-1-25.png

8-1-25

也可以在views的高级配置里面找到:

8-1-26.png 

8-1-26

前面这两个相对简单一点,过滤器标识符,需要特别熟悉Drupal的人才会知道,我们这里给出它对应的位置,看这里:

8-1-27.png 

8-1-27

  点击过滤条件Search:全文搜索”, 或者你使用的搜索条件。我们看到这样的弹出框。

8-1-28.png 

8-1-28

这个还是比较熟悉的,我们往下拉,看到下面的信息:

8-1-29.png 

8-1-29

   过滤器标识符就在这里,它其实是URL参数里面的key

Views里面,全文搜索字段,必须是选中这个复选框的:“把这个过滤条件显示给网站的访问者,允许他们可以更改这个过滤条件。”

微信端的输入,最终传递给了这个过滤条件,这个过滤条件,用来检索用。

 

我们这里面使用的是solrsearch APIsearch api solr, 注意安装的时候使用composercomposer 很多时候需要科学上网。

 

微信模块自带的默认配置,走的是Drupal核心自带搜索,需要启用核心search模块。

 

8-1-30.png 

8-1-30

默认搜索,对于中文来说,下面的配置“索引关键词最小字数”,从3改为1

 

8-1-31.png 

8-1-31

Drupal8下面,改进了一点,不改为1可能也能搜索出来。

 

 

需要注意几点:

 1) display type 必须选择使用 wechat response:

8-1-32.png 

8-1-32

2) 必须做好Views字段与微信消息字段之间的映射关系。点击显示里面的设置。

8-1-33.png

8-1-33

我们看到这样的弹出框。

 

 8-1-34.png 

8-1-34

这里的label名字,是微信官方需要我们提供的。下拉列表里面的字段,是对应views的可用字段。

8-1-35.png 

8-1-35

这里的字段,是在views配置的FIelds里面添加的:

8-1-36.png 

8-1-36

 

   这是另一个映射示例:

 

 

8-1-37.png 

8-1-37

总之这里的映射关系,需要配置好。

3)

显示的项目数量不能超过8。微信的限制:以前是10, 后来改成8了。推荐使用5

8-1-38.png 

 

8-1-38


Drupal版本:

微信小程序开发培训班

    举办单位: 北京亚艾元软件

 

     我们以工匠的精神,持续钻研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 的微信小程序开发培训班