kintone微信小程序SDK使用实例

cybozu发表于:2019年08月30日 14:50:33更新于:2019年09月17日 14:12:20

Index

介绍

大家在做移动端应用开发的时候都会不满足于APP客户端,小程序的应用也越来越广泛了。

现在我们开发了一套kintone在微信小程序上的SDK。基于这个SDK大家就可以做适用于自己的微信小程序了。

SDK做了些什么

这里不得不提到kintone的JS SDK,既可以支持Commonjs规范服务端nodejs开发,也可以支持AMD规范的浏览器端的js开发。

此微信小程序SDK就是基于kintone的JS SDK改造而得。使用过JS SDK的开发者相信很快就能上手。

这里面已经封装好了kintone的大部分REST-API,并调用了wx.request来发送请求到kintone。

  /**
   * request to URL
   * @param {String} methodName
   * @param {String} restAPIName
   * @param {Object} body
   * @return {Promise}
   */
  request(methodName, restAPIName, body) {
    const method = String(methodName).toUpperCase();
    const uri = this.getUri(restAPIName);
    // Set Header
    const headersRequest = {};
    // set header with credentials
    this.auth.createHeaderCredentials().forEach((httpHeaderObj) => {
      headersRequest[httpHeaderObj.getKey()] = httpHeaderObj.getValue();
    });
    this.headers.forEach((httpHeaderObj) => {
      const headerKey = httpHeaderObj.getKey();
      if (headerKey !== CONNECTION_CONST.BASE.USER_AGENT) {
        headersRequest[headerKey] = httpHeaderObj.getValue();
      }
    });
    // Set request options
    const requestOptions = this.options;
    requestOptions.method = method;
    requestOptions.url = uri;
    requestOptions.dataType = CONNECTION_CONST.REQUEST.DATATYPE_JSON;
    if (!requestOptions.hasOwnProperty(CONNECTION_CONST.REQUEST.RESPONSE_TYPE)
      || requestOptions[CONNECTION_CONST.REQUEST.RESPONSE_TYPE] !== CONNECTION_CONST.REQUEST.RESPONSE_TYPE_ARRAYBUFFER) {
      requestOptions[CONNECTION_CONST.REQUEST.RESPONSE_TYPE] = CONNECTION_CONST.REQUEST.RESPONSE_TYPE_TEXT;
    }
    if (requestOptions.method === 'GET') {
      delete requestOptions.data;
      if (this.isExceedLimitUri(uri, body)) {
        requestOptions.url += '?' + this.serializeParams({_method: method});
        requestOptions.method = 'POST';
        headersRequest[CONNECTION_CONST.BASE.X_HTTP_METHOD_OVERRIDE] = String(methodName).toUpperCase();
        requestOptions.data = body;
      } else {
        requestOptions.url += '?' + this.serializeParams(body);
      }
    } else {
      requestOptions.data = body;
    }
    if (!headersRequest.hasOwnProperty(CONNECTION_CONST.BASE.CONTENT_TYPE)) {
      // Set content-type to "application/html" if using GET method
      if (requestOptions.method === 'GET') {
        headersRequest[CONNECTION_CONST.BASE.CONTENT_TYPE] = CONNECTION_CONST.BASE.CONTENT_TYPE_HTML;
      } else {
        headersRequest[CONNECTION_CONST.BASE.CONTENT_TYPE] = CONNECTION_CONST.BASE.CONTENT_TYPE_JSON;
      }
    }
    requestOptions.header = headersRequest;
    // Execute request
    return new Promise((resolve, reject) => {
      requestOptions.success = res => {
        if (res.statusCode === 200) {
          resolve(res.data);
        } else {
          reject(res);
        }
      };
      requestOptions.fail = err => {
        reject(err);
      };
      this.requestTask = wx.request(requestOptions);
    });
  }

还封装了上传下载文件(wx.uploadFile,wx.downloadFile)的函数。能让大家在调用上传下载文件的时候,能返回给kintone所必须的fileKey。

  /**
   * upload file to kintone
   * @param {String} filePath
   * @return {Promise}
   */
  upload(filePath) {
    // Set Header
    const headersRequest = {};
    // set header with credentials
    this.auth.createHeaderCredentials().forEach((httpHeaderObj) => {
      headersRequest[httpHeaderObj.getKey()] = httpHeaderObj.getValue();
    });
    this.headers.forEach((httpHeaderObj) => {
      const headerKey = httpHeaderObj.getKey();
      if (headerKey !== CONNECTION_CONST.BASE.USER_AGENT) {
        headersRequest[headerKey] = httpHeaderObj.getValue();
      }
    });
    // Execute request
    return new Promise((resolve, reject) => {
      // Set request options
      const requestOptions = this.options;
      requestOptions.url = this.getUri('FILE');
      requestOptions.filePath = filePath;
      requestOptions.name = 'file';
      requestOptions.header = headersRequest;
      requestOptions.success = res => {
        if (res.statusCode === 200) {
          resolve(JSON.parse(res.data));
        } else {
          reject(res);
        }
      };
      requestOptions.fail = err => {
        reject(err);
      };
      this.uploadTask = wx.uploadFile(requestOptions);
    });
  }
}


范例

以下是利用这个SDK做的库存管理的微信小程序的范例。在手机端添加数据后,小程序和kintone的PC端都得到了同步更新。

0015d6e2f79b38816d7727610ad875a

部署

Step 1

首先,需要下载好微信开发者工具,并且创建一个JavaScript项目

0015d804d62a82e8022cae21b7ea6a8

微信开发者工具

Step 2

通过npm导入kintone-wechat-miniprogram-sdk库。

cd your-project-directory 
npm init 
npm install --save @kintone/kintone-wechat-miniprogram-sdk

Step 3

启用增强编译选项来支持async等。

0015d804355dd18d813dbd64569a51c

选择 “工具” => “构建npm” 来让微信小程序支持npm。

0015d804310d85056091ea4c9b6dc9b

部署完成了。以下是商品列表的演示画面:

0015d720b7e5deb67e78578991f248a

代码解析

库存管理

在库存管理应用里实现了

  • 商品列表

  • 商品入库

  • 商品添加

  • 商品出库

  • 商品检索

这几个基本的演示功能。

目录结构

以下是此商品库存管理的微信小程序的目录结构:

0015d6f2457056962cc1f11c8efc91d

其中:

  • common:

    放置静态资源和公用的函数。

    kintoneConfig.js定义了kintone的配置信息,kintone的字段代码等。

    utility.js定义了一些公用的kintone的连接函数、数据初始化、数据校验等函数。

  • miniprogram_npm:

    微信小程序自动编译的npm第三方库。

  • pages:

    库存管理具体的代码及页面。

  • weui-miniprogram:

    WEUI组件库

添加商品代码

以下是以具体添加商品作为范例来说明SDK的使用方法。

首先了解一下添加商品功能的页面的目录结构。

0015d6e0259d85306ab72481735bd86

  • add.js:逻辑层

    写具体的添加商品的逻辑代码。

    通过page构造器注册页面

    data:初始化数据

    onLoad:在页面初始化的时调用uploadFile函数,自动执行文件上传的API并获取file key。

    还有其他一些formInputChange、formDateChange等函数与前端add.wxml进行数据绑定,

    最后通过在函数submitForm里通过添加记录的API来将数据添加到kintone。

  • add.wxml:视图层

    这是页面模板的页,构建出页面的结构。

    这里选择日期,上传图片,提交表单,数据校验这些都是通过微信的WEUI组件库来实现的。他能带给用户类似于原生视觉体验的UI。

  • add.json:配置

    可以写调用的WEUI组件的信息,小程序的页面路径、界面表现、网络超时时间、底部 tab 等

  • add.wxss:样式

    该页面的样式信息

以下是add.js的代码范例:

const kintoneConfig = require('../../common/kintoneConfig');
const utility = require('../../common/utility');
Page({
  /**
   * 页面的初始数据
   */
  data: {
    warehouses: ["上海A", "上海B", "苏州", "无锡"],
    warehouseIndex: 0,
    warehousePositions: ["1-1", "1-2", "2-1", "2-2"],
    warehousePositionIndex: 0,
    inOutDate: `${utility.formatDate(new Date())}`,
    formData: {
      inOutDate: `${utility.formatDate(new Date())}`,
    },
    rules: [
      {
        name: 'inOutDate',
        rules: [{required: true, message: '日期必填'}],
      },
      { // 多个规则
        name: 'inOutAmount',
        rules: [{required: true, message: '数量必填'}, {
          validator: (rule, value, param, models) => {
            var regPos = /^\d+$/; // 非负整数
            if (!regPos.test(value)) {
              return rule.message;
            }
          },
          message: '数量格式不对',
          valid: true,
        }],
      },
      {
        name: 'commodityName',
        rules: [{required: true, message: '商品名称必填'}]
      },
    ],
    commodityNameTitle: kintoneConfig.field.commodityName.name,
    commodityNumberTitle: kintoneConfig.field.commodityNumber.name,
    inOutDateTitle: kintoneConfig.field.inOutDate.name.in,
    inOutAmountTitle: kintoneConfig.field.inOutAmount.name.in,
    unitTitle: kintoneConfig.field.unit.name,
    inOutNoteTitle: kintoneConfig.field.inOutNote.name,
    warehouseTitle: kintoneConfig.field.warehouse.name,
    warehousePositionTitle: kintoneConfig.field.warehousePosition.name,
    commoditySizeTitle: kintoneConfig.field.commoditySize.name,
    commodityPriceTitle: kintoneConfig.field.commodityPrice.name,
    imageTitle: kintoneConfig.field.image.name,
  },
  onLoad() {
    this.setData({
      uploadFile: this.uploadFile.bind(this)
    });
  },
  formDateChange: function(e) {
    this.setData({
      inOutDate: e.detail.value,
    });
    this.formInputChange(e);
  },
  formInputChange: function(e) {
    const {field} = e.currentTarget.dataset;
    this.setData({
      [`formData.${field}`]: e.detail.value
    });
  },
  warehouseChange: function(e) {
    this.setData({
      warehouseIndex: e.detail.value,
    });
  },
  warehousePositionChange: function(e) {
    this.setData({
      warehousePositionIndex: e.detail.value
    });
  },
  fileKey: '',
  uploadFile: function(files) {
    console.log('upload files', files);
    const kintoneFile = utility.kintoneFileModule();
    return kintoneFile.upload({filePath: files.tempFilePaths[0]}).then(rsp => {
      this.fileKey = rsp.fileKey;
      return {urls: files.tempFilePaths};
    }).catch(e => {
      console.log(e.get());
    });
  },
  deleteFile: function() {
    this.fileKey = '';
  },
  submitForm: function() {
    this.selectComponent('#form').validate((valid, errors) => {
      console.log('valid', valid, errors);
      if (!valid) {
        const firstError = Object.keys(errors);
        if (firstError.length) {
          this.setData({
            error: errors[firstError[0]].message
          });
        }
      } else {
        const kintoneRecord = utility.kintoneRecordModule();
        const record = {};
        record[kintoneConfig.field.commodityName.code] = {value: this.data.formData.commodityName};
        record[kintoneConfig.field.commodityNumber.code] = {value: this.data.formData.commodityNumber};
        record[kintoneConfig.field.warehouse.code] = {value: this.data.warehouses[this.data.warehouseIndex]};
        record[kintoneConfig.field.warehousePosition.code] = {value: this.data.warehousePositions[this.data.warehousePositionIndex]};
        record[kintoneConfig.field.commoditySize.code] = {value: this.data.formData.commoditySize};
        record[kintoneConfig.field.commodityPrice.code] = {value: this.data.formData.commodityPrice};
        record[kintoneConfig.field.unit.code] = {value: this.data.formData.unit};
        if (this.fileKey.length > 0) {
          record[kintoneConfig.field.image.code] = {value: [{fileKey: this.fileKey}]};
        }
        const value = {};
        value[kintoneConfig.field.inOutDate.code] = {value: this.data.formData.inOutDate};
        value[kintoneConfig.field.inOutAmount.code] = {value: parseInt(this.data.formData.inOutAmount)};
        value[kintoneConfig.field.inOutNote.code] = {value: this.data.formData.inOutNote};
        record[kintoneConfig.field.inOutTable.code] = {value: [{value: value}]};
        kintoneRecord.addRecord({app: kintoneConfig.appId, record: record}).then(rsp => {
          wx.redirectTo({url: `../commodity/detail?id=${rsp.id}`});
        }).catch(e => {
          console.log(e.get());
        });
      }
    });
  },
});


资源列表

SDK源码和文档:https://cybozudev.kf5.com/hc/kb/article/1307637

demo源码:https://github.com/kintone/SAMPLE-kintone-wechat-miniprogram-sdk-cn

总结

好了,小程序的SDK和范例就介绍到这里,希望能给大家在开发kintone的微信小程序的时候带来帮助,如果有什么疑问或者建议,可以在下面留言哦。

 注意事项

  • 本示例代码不保证其运行。

  • 我们不为本示例代码提供技术支持。

    注意:贴代码时请注意格式并使用"代码语言",与本文无关的问题请至“讨论社区”提问。
    您需要登录后才可以回复