Apicloud
创建第一个app
https://docs.apicloud.com/APICloud/creating-first-app
开发工具下载
https://www.apicloud.com/devtools
源码地址
https://github.com/apicloudcom
signature 源码
https://github.com/apicloudcom/APICloud-Modules/tree/master/signature/android/sourceCode
打包发布APP-云编译
注意项:
1. 如果装的话,需要将之前【调试的 loader】 先卸载后再装,否则提示“安装失败,安装包版本太低”等问题
步骤:
1. 修改 logo(png格式)
2. 修改启动页
3. 云编译,打包
资源:
拷贝模拟器中的文件?
在安卓手机 文件管理器 / $MuMu共享文件夹
APP运行的文件是存在于 /UZMap/APP目录 下
在电脑上有对应的文件夹与之对应
APICloud七天在线培训课教程
https://github.com/apicloudcom/APICloud-7Days-Online-Training-Tutorials
课程第一天 https://docs.apicloud.com/Seven/Day1
一周一APP 完整技术架构和流程
https://github.com/xiaoqiang730730/vue_apicloud/blob/master/doc/oneweekoneapp.md
AUI框架
https://github.com/liulangnan/aui
APICloud WiFi 真机预览效果
https://docs.apicloud.com/Dev-Tools/wifi-debug
与 Vue 整合开发的相关资料
方式一: vue-cli + vant
【运行方式】 启动 Vue 服务器,然后在 手机应用上同步才可以看到效果 【注意】 浏览器中是看不到效果的,因为创建 vue 组件是在 api_ready 下才成功创建的。
vue 脚手架开发
https://github.com/xiaoqiang730730/vue_apicloud/tree/master/doc
vant 移动端UI库
https://youzan.github.io/vant/#/zh-CN/quickstart
vue: command not found
【解决办法】 写全命令路径
/usr/local/Cellar/node/6.2.2/lib/node_modules/vue-cli/bin/vue init xiaoqiang730730/vue_apicloud ee-app-vant
Vue项目中如何使用APICloud的接口
直接调用即可,不过需要在模拟器中进行调用才行。在浏览器中,会提示找不到对象
例如,在点击函数中,双向数据绑定:
this.msg = api.systemType
知识点积累
路由跳转
this.$router.push('/register');
引入库
采用全局导入组件的方式【推荐】
cnpm i vant -S
在 src/main.js 全部导入 vant 的组件
import Vant from 'vant'; import 'vant/lib/vant-css/index.css'; Vue.use(Vant);
之前的babel方式,有点麻烦,【不推荐使用】
.barelrc 配置。当然安装依赖什么的,就直接参考文档安装即可。
vue init xiaoqiang730730/vue_apicloud my-project cnpm install cnpm i babel-plugin-import -D cnpm i vant -S cnpm run start
{ "presets": [ ["env", { "modules": false, "targets": { "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] } }], "stage-2" ], "plugins": ["transform-vue-jsx", "transform-runtime", ["import", { "libraryName": "vant", "libraryDirectory": "es", "style": true }]], "env": { "test": { "presets": ["env", "stage-2"], "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"] } } }
2. 使用组件
关闭elint验证, config/index.js, 配置选项:
useEslint: false,
<van-button type="default">清空</van-button> import { Button } from "vant"; export default { name: "HelloWorld", components: { [Button.name]: Button }, };
3. APICloud 源代码配置,采用热更新的方式,其中配置方式,可参考方式二中的 相关文章
主要步骤
(1)APIStudio 新建项目
(2)网页云控制台,模块/编译自定义 Loader
编辑 模板案例 中的 index.html 文件,修改结构 和 js 代码
<body> <div id="wrap"> <div id="header"> </div> <div id="main" class="flex-con"> </div> </div> </body> <script type="text/javascript" src="./script/api.js"></script> <script type="text/javascript"> window.apiready = function(){ var header = $api.byId('header'); //适配iOS 7+,Android 4.4+状态栏 $api.fixStatusBar(header); var headerPos = $api.offset(header); var main = $api.byId('main'); var mainPos = $api.offset(main); api.openFrame({ name: 'main', //url: 'dist/html/index.html', //url: 'html/main.html', //url: 'http://172.16.5.6:8010/index.html', url: 'http://172.16.5.6:8080', bounces: true, rect: { x: 0, y: headerPos.h, w: 'auto', h: mainPos.h } }); }; </script>
方式二: vue + sui
史上最快的开发方式,基于webpack2+vue2搭建的apicloud脚手架
https://community.apicloud.com/bbs/forum.php?mod=viewthread&tid=55876&extra=&page=1
SUI 淘宝的移动端UI框架
http://m.sui.taobao.org/getting-started/
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="maximum-scale=1.0,minimum-scale=1.0,user-scalable=0,width=device-width,initial-scale=1.0" /> <meta name="format-detection" content="telephone=no,email=no,date=no,address=no"> <title>入口页面</title> <!-- <link rel="stylesheet" href="./res/css/sm.min.css"> --> <link rel="stylesheet" href="//g.alicdn.com/msui/sm/0.6.2/css/sm.min.css"> <style> .bar-nav { background-color: #0099FF; } .bar-nav .title { color: #fff; } </style> </head> <body style="background-color: #fff"> <!-- <header class="bar bar-nav"> <a class="icon icon-me pull-left open-panel"></a> <h1 class="title">标题222</h1> </header> --> <div id="app"> <p> <!-- 使用 router-link 组件来导航. --> <!-- 通过传入 `to` 属性指定链接. --> <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 --> <router-link to="/foo">Go to Foo</router-link> <router-link to="/bar">Go to Bar</router-link> </p> <!-- 路由出口 --> <!-- 路由匹配到的组件将渲染在这里 --> <router-view></router-view> </div> </body> <!-- 引入 APICloud 库 --> <script src="./src/public/api.js"></script> <!-- 引入 Vue 和 Vue 路由 --> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> <script> window.apiready = function () { // 1. 定义 (路由) 组件。 // 可以从其他文件 import 进来 const Foo = { template: '<div>foo</div>' } const Bar = { template: '<div>bar</div>' } // 2. 定义路由 // 每个路由应该映射一个组件。 其中"component" 可以是 // 通过 Vue.extend() 创建的组件构造器, // 或者,只是一个组件配置对象。 // 我们晚点再讨论嵌套路由。 const routes = [ { path: '/foo', component: Foo }, { path: '/bar', component: Bar } ] // 3. 创建 router 实例,然后传 `routes` 配置 // 你还可以传别的配置参数, 不过先这么简单着吧。 const router = new VueRouter({ routes: routes // (缩写) 相当于 routes: routes }) // 4. 创建和挂载根实例。 // 记得要通过 router 配置参数注入路由, // 从而让整个应用都有路由功能 const app = new Vue({ router: router }).$mount('#app') }; </script> </html>
其他技术知识点
APP store 开发流程
https://github.com/xiaoqiang730730/vue_apicloud/blob/master/doc/appstore.md
vue中调用api的模块
https://community.apicloud.com/bbs/forum.php?mod=viewthread&tid=100823&highlight=vue
命令行工具
https://docs.apicloud.com/Dev-Tools/apicloud-cli
npm install -g apicloud-cli 找到安装目录 /usr/local/Cellar/node/6.2.2/bin/apicloud export PATH=/usr/local/Cellar/node/6.2.2/bin:$PATH 可以将PATH写到 系统的路径里 /etc/profile 也可以写到用户的 配置环境里 如果用的是bash 则在用户目录~/.bash_profile 添加路径 把修改后的文件用source命令生效下就行 也可以重启下系统 vi ~/.bash_profile source ~/.bash_profile
vue-cli启动
npm run wifistart
https://github.com/xiaoqiang730730/vue_apicloud/blob/master/doc/oneweekoneapp.md
_apiConfig.xml 可以把content改成http://xxxx:8080, 就能够运行vue的项目了。 在安装自定义loader之后,按照如图所示, 这个时候就需求apicloud-cli了,2个步骤 wifi开启同步 apicloud wifiStart --port 8686 或者 npm run wifistart wifi服务同步所有文件 apicloud wifiSync --project ./src --updateAll true --port 8686 或者 npm run wifiupdateall
参考模块
拍照模块 FNPhotograph
https://docs.apicloud.com/Client-API/Func-Ext/FNPhotograph
支持距离调整等
组件化,结合vue的实现代码
<template> <div class="camera"> <van-icon @click="takePhoto" name="photograph" class="btn" /> </div> </template> <script> /* 使用说明 <take-photo v-if="showCamera" v-on:before-close="closeCamera" :exam-student-id="'xxxx-xxx-xxx'" :exam-index="0" ></take-photo> this.showCamera = true; closeCamera: function(imagePath) { alert("closeCamera") alert(imagePath) this.showCamera = false; }, import TakePhoto from './TakePhoto' components: { TakePhoto }, */ export default { name: 'TakePhoto', props: [ 'examStudentId', 'examIndex', ], mounted() { alert("开始拍照!"); // 拍照 var FNPhotograph = api.require('FNPhotograph'); FNPhotograph.openCameraView({ rect: { x: 0, y: 0, w: 'auto', h: api.winHeight - 100 }, orientation: 'landscapeRight', fixedOn: api.frameName }, function(ret) { if (!ret.status) { alert('相机打开失败,请确认已经授权使用相机!'); } // alert('openCameraView ret:'+JSON.stringify(ret)); }) }, methods: { takePhoto: function() { var examPhotoPath = this.EXAM_PHOTO_PATH; examPhotoPath += this.examStudentId; examPhotoPath += '/' + this.examIndex; // 拍照 var FNPhotograph = api.require('FNPhotograph'); FNPhotograph.takePhoto({ quality: 'medium', path: examPhotoPath, album: false }, (ret) => { var imagePath = (ret.imagePath); FNPhotograph.closeCameraView((ret) => { if (ret) { alert("拍照成功!"); this.$emit("before-close", imagePath); } }); }); }, }, } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> .camera { position: absolute; z-index: 999; top: 0; left: 0; right: 0; bottom: 0; background: #000000; } .btn { position: absolute; bottom: 10px; left: 50%; margin-left: -30px; width: 60px; line-height: 50px; cursor: pointer; font-size:40px; text-align: center; color:white; border:2px solid white; border-radius:50%; padding:5px; } </style>
拍照旋转问题
【解决方法】将图片通过 css 样式旋转,即可纠正旋转的照片
style="transform:rotate(-90deg); "
获取旋转角度,注意要传入图片路径,官方代码中没有包含图片路径参数。
FNPhotograph.getRotateDegree({ imgPath: imagePath }, function(ret) { if (ret) { alert(JSON.stringify(ret)); } });
编译自定义Loader后,调用摄像头代码,如下:
var FNPhotograph = api.require('FNPhotograph'); FNPhotograph.open({ path: 'fs://savePath', album: false , quality: 'medium' // quality: 'low' }, function(ret){ if (ret.eventType == "show") { } else if (ret.eventType == "takePhoto") { alert(ret.imagePath); FNPhotograph.close(); } else if (ret.eventType == "close") { alert("close"); } else { alert(JSON.stringify(ret)); } });
拍照像素问题
设为低像素
FNPhotograph.takePhoto({ quality: 'low', qualityValue: 12, path: examPhotoPath,
自定义Frame操作
FNPhotograph.openCameraView({ rect: { x: 0, y: 0, w: 'auto', h: 'auto' }, orientation: 'landscapeLeft', fixedOn: api.frameName }, function(ret) { alert('sss'+JSON.stringify(ret)); FNPhotograph.takePhoto({ quality: 'medium', path: 'fs://FNPhotograph/01.png', album: false }, function(ret) { alert(ret.imagePath); FNPhotograph.close( function(ret) { if (ret) { alert(JSON.stringify(ret)); } }); }); });
参考:https://community.apicloud.com/bbs/forum.php?mod=viewthread&tid=73629&highlight=openCameraView
模块使用流程
1. 添加模块,同步代码
2. 编译自定义 loader,使用自定义Loader进行测试
前置摄像头
setCamera
设置前置/后置摄像头(注意此方法与openCameraView配合使用)
setCamera({params})
params
camera:
- 类型:字符串
- 描述:(可选项)摄像头
- 默认值:back
- 取值范围:
- front:前置摄像头
- back:后置摄像头
trans 转换模块的使用
转换图片编码等
var trans = api.require('trans'); trans.decodeImgToBase64({ imgPath: ret.imagePath }, (ret, err) => { if (ret.status) { // console.log(ret.base64Str); ...
socketManager 通讯模块
状态定义:
101 //创建成功
102 //连接成功
https://docs.apicloud.com/Client-API/Func-Ext/socketManager#3
创建连接、发送数据等操作
https://docs.apicloud.com/Client-API/Func-Ext/socketManager#3
var socketManager = api.require("socketManager"); socketManager.createSocket({ host: '192.168.10.1', port: 6001 }, function(ret, err) { if (ret) { alert(JSON.stringify(ret)); } else { alert(JSON.stringify(err)); } });
SocketManager发送16进制数据
https://community.apicloud.com/bbs/forum.php?mod=viewthread&tid=74184&extra=page%253D1
- 下载图片 base64 编码多了1个等于号 => 直接字符串替换掉
Mac 下 TCP/UDP 测试工具
https://www.jianshu.com/p/ca1910ac691a
ajax模块
上传多个文件
api.ajax({ url: url, method: 'post', timeout: 60*60*2, headers: { 'token': this.token }, data: { files: { "file": ['fs://1.jpg', 'fs://2.jpg'] } } }, (ret, err) => { if (ret) { ...
后台接收
@Login @PostMapping("question/uploadpic") @ApiOperation("上传1个考生正常考试抓取的图片") public R uploadpic(@RequestParam String examStudentID, @RequestParam("file") MultipartFile[] files) { ...
发送字符串数组
api.ajax({ url: url, method: 'post', timeout: 60*60*2, headers: { 'Content-Type': 'application/json;charset=utf-8', 'token': this.token }, data: { body: examPhotoList } }, (ret, err) => { if (ret) { ...
后台接收
@Login @PostMapping("question/uploadpic") @ApiOperation("上传1个考生正常考试抓取的图片") public R uploadpic(@RequestParam String examStudentID, @RequestBody List<Object> base64StrList) { ...
遇到的问题:
JsonMappingException: Can not deserialize instance of java.lang.Integer out of START_OBJECT token
【解决方法】 前端传递的是对象,而后端接受的是 List,类型不匹配
示例代码
简单登录效果实现
技术知识点
ajax 的封装
// 自定义的ajax接口 // obj 参数: // { // method: 'get' 'post' // url: '' // data: {}, // sucess: (ret) => {} // fail: (ret) => {} // } Vue.prototype.eeAjax = function (obj) { if (this.token == null) { this.$router.push("/") return ; } var prefix = "http://192.168.1.111:8080/ee-server"; obj.url = prefix + obj.url; if (obj.data) { var arr = []; for (var k in obj.data) { arr.push( k + "=" + obj.data[k]); } if (arr.length >= 0) { obj.url += "?"; obj.url += arr.join('&') } } // 表单方式提交数据或文件 api.ajax({ url: obj.url, method: 'get', headers: { 'Content-Type': 'application/json;charset=utf-8', 'token': this.token } }, (ret, err) => { if (ret) { if (ret.code == 0) { obj.success && obj.success(ret); } else if (ret.code == 403) { // token失效,请重新登录 alert(ret.msg); this.$router.push("/") } else { obj.fail && obj.fail(ret) } } }); }
全局变量实现
export default function install (Vue, options) { // 服务器信息记录 Vue.prototype.KEY_SERVER_INFO = "KEY_SERVER_INFO"; Vue.prototype.getGLibExamStudent = function () { return Vue.prototype.gLibExamStudent; } } // main.js import utils from './utils'; Vue.use(utils); //将全局函数当做插件来进行注册
系统参数存储
// 同步获取app参数 var dbIndex = api.getPrefs({ sync: true, key: 'dbIndex' }); // 设置app参数 if (!dbIndex) { api.setPrefs({ key: 'dbIndex', value: '1' }); }
获取摄像头图像或系统相册中的图片
https://docs.apicloud.com/Client-API/api#20
api.getPicture({ sourceType: 'library', encodingType: 'jpg', mediaValue: 'pic', destinationType: 'base64', allowEdit: false, quality: 50, targetWidth: 100, targetHeight: 100, saveToPhotoAlbum: false }, function(ret, err) { if (ret) { alert(JSON.stringify(ret)); } else { alert(JSON.stringify(err)); } });
写文件
var dbf = dbFilePath + "/" + dbIndex + "/" + (dbFileCount++); api.writeFile({ path: dbf, data: imgData }, function(ret, err) { if (ret.status) { alert("保存成功"); } else { alert("dbFilePath保存失败:" + JSON.stringify(err)); } });
数据库操作
https://docs.apicloud.com/Client-API/Func-Ext/db
https://www.cnblogs.com/mr-yuan/p/6696288.html
CSS 边框测试代码
border-bottom-color: #ff0000; border-bottom-style: solid; border-bottom-width: 5px;
Vue 默认图片显示
data: { noPhoto: 'this.src="' + require("../assets/StartExam/NoPhoto.gif") + '"', } <img v-bind:src="stu.photo" :onerror="noPhoto">
Java Base64图片编码
注意生成的图片前面有前缀表示
// 保存图片数据,转换成 Base64 编码 Byte[] photo = worker.getPhoto(); int n = photo.length; byte[] b = new byte[n]; for (int i = 0; i < n; i++) { b[i] = photo[i]; } // 保存 Base64 编码格式显示 examStudentForm.setPhoto("data:image/jpeg;base64," + Base64.encodeBase64String(b));
前端中的显示:
{ noPhoto: 'this.src="' + require("../assets/StartExam/NoPhoto.png") + '"', <img style="transform:rotate(-90deg); " v-bind:src="'data:image/jpg;base64,'+photo" :onerror="noPhoto">
问题及解决
该操作仅对使用 git-svn 检出的 SVN 项目有效.如果是标准 Git 项目,直接使用 push 命令即可.
重新用 apistudio 工具从网站检出之后,将检出的文件夹 替换 编译即可。
Vue 区分开发版本还是发布版本
下面2个表达式,可以在 vue 的组件总直接来使用
开发版本 process.env.NODE_ENV === 'development' 发布版本 process.env.NODE_ENV === 'production' // 组件中的使用 this.isDev = (process.env.NODE_ENV === 'development');
文件操作模块
fs 模块使用手册
https://docs.apicloud.com/Client-API/Func-Ext/fs#31
使用模块时,需要先添加模块,才能使用这些功能
var fs = api.require('fs'); var bakDir = "fs://exam/bak"; // var ret = fs.createDirSync({ // path: bakDir // }); // if (ret.status) { // alert('创建成功!'); // } else { // alert('创建失败!'); // } var ret = fs.existSync({ var ret = fs.openSync({ var ret = fs.writeSync({ fd: this.bak.fd, data: JSON.stringify(this.stuExamList), overwrite: true }); // if (ret.status) { // alert('保存成功!'); // } else { // alert('保存失败!'); // }
加密模块
signature
https://docs.apicloud.com/Client-API/Func-Ext/signature
添加模块
var signature = api.require('signature'); signature.desECB({ data: txt, key: '4OzEDGPUvkym9SkU' }, (ret, err) => { if (!ret.status) {
signature 源码
https://github.com/apicloudcom/APICloud-Modules/tree/master/signature/android/sourceCode
参照源代码
Java 端
import org.apache.commons.codec.binary.Base64; str = new String(Base64.decodeBase64(afterDecrypt.getBytes("UTF-8")));
【注意】测试时候的字符一定要准确
Wifi模块
https://docs.apicloud.com/Client-API/Device-Access/bgnWiFi
与apicloud配套使用的vant框架
环境搭建
cnpm install -g @vue/cli-init vue init xiaoqiang730730/vue_apicloud si-app-vant
去除eslint的验证
config/index.js
useEslint: false,
启动项目
npm run start
编译加上传
npm run build && gulp upload