The AVM framework has a programming experience that tends to be native, and after more than a year of development, it has become one of the mainstream multi-terminal development frameworks in China. This article will review a developer's contribution and detail how to develop an application using APICloud's AVM framework.
Taking the development of an application with the "appointment" function as an example, this article will introduce the application development process from four aspects: application structure, application prototype, application front-end development, and application background construction.
01
Comb through the application structure
First of all, you need to sort out the structure and core functions of the "reservation" application, and draw the basic structure of the application through mind mapping.

02
Prototype your app
According to the application structure, the functional display of each page of the application is further refined, and the prototype of the "pre-code" application is drawn using the pre-code prototype tool.
The prototype link can be viewed here.
03
Application front-end development
The "Appointment" application front-end is developed using the APICloud AVM multi-terminal development framework, including swiper carousel diagram, network request encapsulation and other key points. Developed using APICloud multi-terminal technology, you can achieve a set of code multi-terminal operation, support compilation into Android & iOS apps and WeChat mini programs.
1. APICloud usage steps
(1) Download APICloud Studio 3 as a development tool. Download address: Click here to download.
(2) After registering an account, create an app in the console, console address: Click here to view.
Check out the working directory
(5) Modify or submit the project source code, and compile a custom Loader for the current project cloud for real machine synchronous debugging preview.
Use AppLoader for real machine synchronization debugging preview, the background automatically downloads to the mobile phone with Loader, after installation, click the small circle, enter the IP address: 192.168.2.152 port: 10916, after connecting the real machine synchronization, you can see the results just created.
2. The use of AVM framework
The main advantages of AVM are as follows:
Easy to use: With Vue and React foundations, it can be quickly started, and it is equipped with a dedicated development tool APICloud Studio3.
Multi-terminal: one development, multi-terminal rendering, one technology stack to get the mobile terminal development.
Rich functional APIs: Provide 1k+ modules and 2W+ APIs that can be called directly, and are unlimited for industry and scenario.
(1)UI
Official Documentation: Click here to view
Many UIs are designed by themselves, and you can also refer to the AUI framework made by developer Wandering Man. Font icon with ali font icon.
The APICloud website assembles a set of vant components, which can be viewed here.
(2) AJAX network interaction
Use the code below to implement it.
// 表单方式提交数据或文件
api.ajax({
url: 'http://192.168.1.101:3101/upLoad',
method: 'post',
data: {
values: {
name: 'haha'
},
files: {
file: 'fs://a.gif'
}
}
}, function(ret, err) {
if (ret) {
api.alert({ msg: JSON.stringify(ret) });
} else {
api.alert({ msg: JSON.stringify(err) });
}
});
// 提交JSON数据
api.ajax({
url: 'http://192.168.1.101:3101/upLoad',
method: 'post',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
data: {
body: {
name: 'haha'
}
}
}, function(ret, err) {
if (ret) {
api.alert({ msg: JSON.stringify(ret) });
} else {
api.alert({ msg: JSON.stringify(err) });
}
});
(3)vue指令使用(v-for v-show v-if v-else v-for v-on v-bind v-model等)
1. Data binding
Using The Mustache syntax:
<text text={{msg}}></text>
Using the v-bind directive:
<text v-bind:text="msg"></text>
2. Event binding
<template>
<text onclick="doThis('avm');">Click me!</text>
</template>
<script>
export default {
name: 'test',
methods: {
doThis(msg){
api.alert({
msg: msg
});
}
}
}
</script>
(4) Registration and login
1. Register interface link: Click here to view.
Registration code
<template name='tpl'>
<view class="page">
<div class="page1">
<safe-area class="header">
<!-- <text class="header__title">APICloud</text> -->
</safe-area>
<scroll-view class="main">
<view>
<image src="https://baodinglingqian.oss-cn-beijing.aliyuncs.com/1.png" class="touxiang " />
</view>
<div class="zhanghao">
<input placeholder="请输入账号" v-model="zhanghao" maxlength="10" autofocus />
</div>
<div class="mima">
<input type="password" placeholder="请输入密码" v-model="password" />
</div>
<text class="zhuce" @click="reg()">注册</text>
<text class="denglu" @click="handleClick()">登录</text>
</scroll-view>
</div>
<image class="originImage" mode="scaleToFill" src={src}></image>
</view>
</template>
<script>
export default {
name: "tpl",
apiready() {
api.setStatusBarStyle({
style: "light",
color: "-"
});
},
data() {
return {
zhanghao: '',
password: '',
src: "https://baodinglingqian.oss-cn-beijing.aliyuncs.com/bg.png"
};
},
computed: {
},
methods: {
handleClick(e) {
api.openWin({
name: 'main',
url: './main.stml',
pageParam: {
name: 'test'
}
});
},
reg() {
var _this = this;
// 提交JSON数据
api.ajax({
url: 'http://yy.deui.cn/api.php/index/re',
method: 'get',
// headers: {
// 'Content-Type': 'application/json;charset=utf-8'
// },
data: {
body: {
username: _this.zhanghao,
password: _this.password
}
}
}, function (ret, err) {
console.log(JSON.stringify(ret))
if (ret.msg == '返回成功') {
api.toast({
msg: '注册成功',
location: "middle"
});
} else {
api.alert({ msg: JSON.stringify(err) });
}
});
}
}
};
</script>
<style>
.denglu {
margin-top: 10px;
font-size: 14px;
}
.touxiang {
margin-top: 10%;
width: 150px;
height: 150px;
border-radius: 100px;
margin: 0 auto;
}
html {
width: 100%;
height: 100%;
}
body {
width: 100%;
height: 100%;
}
.originImage {
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
z-index: 1;
}
.page {
/* position:fixed; */
position: relative;
z-index: 9;
width: 100%;
height: 100%;
}
.page1 {
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
z-index: 2;
}
input {
padding-left: 10px;
line-height: 35px;
height: 35px;
border-radius: 5px;
}
.zhanghao {
display: block;
margin: 0 auto;
margin-top: 15%;
margin-bottom: 15px;
height: 60px;
}
.mima {
display: block;
margin: 0 auto;
margin-top: 25px;
}
.zhuce {
width: 100%;
height: 35px;
line-height: 35px;
background-color: coral;
text-align: center;
color: #fff;
margin-top: 25px;
}
.page {
height: 100%;
background-color: white;
}
.header {
background: #81a9c3;
justify-content: center;
align-items: center;
}
.header__title {
color: #fff;
font-size: 18px;
font-weight: bold;
height: 50px;
line-height: 50px;
}
.main {
flex: 1;
padding: 15px;
}
.h1 {
font-size: 24px;
}
.item {
flex-direction: row;
padding: 10px 0;
}
.item__text {
color: #333;
white-space: nowrap;
}
.item__value {
margin-left: 5px;
}
.footer {
background: #81a9c3;
flex-direction: row;
justify-content: center;
align-items: center;
}
.footer__text {
color: #fff;
font-size: 14px;
height: 30px;
line-height: 30px;
}
</style>
2. Login interface link: Click here to view.
Login code
<template name='tpl'>
<view class="page">
<div class="page1">
<safe-area class="header">
<!-- <text class="header__title">APICloud</text> -->
</safe-area>
<scroll-view class="main">
<view>
<image src="https://baodinglingqian.oss-cn-beijing.aliyuncs.com/1.png" class="touxiang " />
</view>
<div class="zhanghao">
<input placeholder="请输入账号" v-model="zhanghao" maxlength="10" autofocus />
</div>
<div class="mima">
<input type="password" placeholder="请输入mima" v-model="password" />
</div>
<text class="zhuce" @click="login()">登录</text>
<text class="denglu" @click="handleClick()">注册</text>
</scroll-view>
</div>
<image class="originImage" mode="scaleToFill" src={src}></image>
</view>
</template>
<script>
export default {
name: "tpl",
apiready() {
api.setStatusBarStyle({
style: "light",
color: "-"
});
var value = localStorage.getItem('uid');
api.openWin({
name: 'home',
url: './home.stml'
});
},
data() {
return {
zhanghao: '',
password: '',
src: "https://baodinglingqian.oss-cn-beijing.aliyuncs.com/bg.png"
};
},
computed: {
},
methods: {
handleClick(e) {
api.openWin({
name: 'region',
url: './region.stml',
pageParam: {
name: 'test'
}
});
// api.toast({
// msg: this.data.text,
// location: "middle"
// });
},
login() {
var _this = this;
api.ajax({
url: 'http://yy.deui.cn/api.php/index/login',
method: 'get',
// headers: {
// 'Content-Type': 'application/json;charset=utf-8'
// },
data: {
body: {
username: _this.zhanghao,
password: _this.password
}
}
}, function (ret, err) {
console.log(JSON.stringify(ret))
localStorage.setItem('uid', ret.data.data[0]['id']);
if (ret.msg == '返回成功') {
api.toast({
msg: '登录成功',
location: "middle"
});
api.openWin({
name: 'home',
url: './home.stml'
});
} else {
api.alert({ msg: JSON.stringify(err) });
}
});
}
}
};
</script>
<style>
.denglu {
margin-top: 10px;
font-size: 14px;
}
.touxiang {
margin-top: 10%;
width: 150px;
height: 150px;
border-radius: 100px;
margin: 0 auto;
}
html {
width: 100%;
height: 100%;
}
body {
width: 100%;
height: 100%;
}
.originImage {
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
z-index: 1;
}
.page {
/* position:fixed; */
position: relative;
z-index: 9;
width: 100%;
height: 100%;
}
.page1 {
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
z-index: 2;
}
input {
padding-left: 10px;
line-height: 35px;
height: 35px;
border-radius: 5px;
}
.zhanghao {
display: block;
margin: 0 auto;
margin-top: 15%;
margin-bottom: 15px;
height: 60px;
}
.mima {
display: block;
margin: 0 auto;
margin-top: 25px;
}
.zhuce {
width: 100%;
height: 35px;
line-height: 35px;
background-color: coral;
text-align: center;
color: #fff;
margin-top: 25px;
}
.page {
height: 100%;
background-color: white;
}
.header {
background: #81a9c3;
justify-content: center;
align-items: center;
}
.header__title {
color: #fff;
font-size: 18px;
font-weight: bold;
height: 50px;
line-height: 50px;
}
.main {
flex: 1;
padding: 15px;
}
.h1 {
font-size: 24px;
}
.item {
flex-direction: row;
padding: 10px 0;
}
.item__text {
color: #333;
white-space: nowrap;
}
.item__value {
margin-left: 5px;
}
.footer {
background: #81a9c3;
flex-direction: row;
justify-content: center;
align-items: center;
}
.footer__text {
color: #fff;
font-size: 14px;
height: 30px;
line-height: 30px;
}
</style>
(5) Home page carousel diagram
Carousel link: Click here to view.
<template name='tpl'>
<view class="page">
<div class="page1">
<safe-area class="header">
<text class="header__title">首页</text>
</safe-area>
<scroll-view class="main" scroll-y>
<swiper class="swiper" id="customSwiper" autoplay circular indicator-dots indicator-color="#ddd"
indicator-active-color="#f0f">
<swiper-item class="swiper-item" v-for="(_item,_index) in bannerlist">
<!-- <text class="desc">{_item.image}</text> -->
<image mode="scaleToFill" src={_item.image}></image>
</swiper-item>
</swiper>
<div>
<a-cell-group>
<a-cell v-bind:title="_item.name" v-bind:value="_item.content" v-bind:label="_item.address"
arrow-direction="right" @click="godetial(_item)" v-for="(_item,_index) in shangjialist" />
<!-- <a-cell title="单元格" value="内容" label="描述信息" /> -->
</a-cell-group>
</div>
</scroll-view>
</div>
<image class="originImage" mode="scaleToFill" src={src}></image>
</view>
</template>
<script>
import ACellGroup from "../../components/act/a-cell-group";
import ACell from "../../components/act/a-cell";
export default {
name: 'test',
data() {
return {
shangjialist: [],
bannerlist: [],
current: 0,
src: "https://baodinglingqian.oss-cn-beijing.aliyuncs.com/bg.png"
}
},
methods: {
apiready() {
this.banner()
this.allstores()
// var customSwiper = document.getElementById('customSwiper');
// customSwiper.load({
// data: this.data.dataList
// });
},
onchange(e) {
this.data.current = e.detail.current;
},
godetial(item) {
console.log(JSON.stringify(item))
api.openWin({
name: 'detial',
url: './detial.stml',
pageParam: {
id: item.id
}
});
},
banner() {
var _this = this;
api.ajax({
url: 'http://yy.deui.cn/api.php/index/banner',
method: 'get',
}, function (ret, err) {
if (ret.msg == '返回成功') {
_this.data.bannerlist = ret.data.data
// var customSwiper = document.getElementById('customSwiper');
// customSwiper.load({
// data: _this.data.bannerlist
// });
// console.log(JSON.stringify(_this.bannerlist))
} else {
api.alert({ msg: JSON.stringify(err) });
}
});
},
allstores() {
var _this = this;
api.ajax({
url: 'http://yy.deui.cn/api.php/index/stores',
method: 'get',
}, function (ret, err) {
if (ret.msg == '返回成功') {
console.log(1)
console.log(JSON.stringify(ret.data.data))
console.log(1)
var obj = ret.data.data
for (let index = 0; index < obj.length; index++) {
const element = obj[index];
element['content'] = element['content'].substring(0, 9) + '...'
}
console.log(JSON.stringify(obj))
_this.data.shangjialist = obj
} else {
api.alert({ msg: JSON.stringify(err) });
}
});
}
}
}
</script>
<style>
.denglu {
margin-top: 10px;
font-size: 14px;
}
.touxiang {
margin-top: 10%;
width: 150px;
height: 150px;
border-radius: 100px;
margin: 0 auto;
}
html {
width: 100%;
height: 100%;
}
body {
width: 100%;
height: 100%;
}
.originImage {
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
z-index: 1;
}
.page {
/* position:fixed; */
position: relative;
z-index: 9;
width: 100%;
height: 100%;
}
.page1 {
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
z-index: 2;
}
input {
padding-left: 10px;
line-height: 35px;
height: 35px;
border-radius: 5px;
}
.zhanghao {
display: block;
margin: 0 auto;
margin-top: 15%;
margin-bottom: 15px;
height: 60px;
}
.mima {
display: block;
margin: 0 auto;
margin-top: 25px;
}
.zhuce {
width: 100%;
height: 35px;
line-height: 35px;
background-color: coral;
text-align: center;
color: #fff;
margin-top: 25px;
}
.page {
height: 100%;
background-color: white;
}
.header {
background: #81a9c3;
justify-content: center;
align-items: center;
}
.header__title {
color: #fff;
font-size: 18px;
font-weight: bold;
height: 50px;
line-height: 50px;
}
.main {
flex: 1;
padding: 0px;
}
.h1 {
font-size: 24px;
}
.item {
flex-direction: row;
padding: 10px 0;
}
.item__text {
color: #333;
white-space: nowrap;
}
.item__value {
margin-left: 5px;
}
.footer {
background: #81a9c3;
flex-direction: row;
justify-content: center;
align-items: center;
}
.footer__text {
color: #fff;
font-size: 14px;
height: 30px;
line-height: 30px;
}
.main {
width: 100%;
height: 100%;
}
.swiper {
width: 100%;
height: 190px;
/* background-color: blue; */
}
.swiper-item {
justify-content: center;
}
.title {
padding: 10px 0;
font-size: 20px;
}
.desc {
width: 100%;
text-align: center;
}
.container {
width: 100%;
height: 200px;
}
.indicator {
flex-direction: row;
justify-content: center;
position: absolute;
width: 100%;
height: 20px;
bottom: 8px;
}
.indicator-item {
width: 15px;
height: 8px;
margin: 3px;
}
.indicator-item-normal {
background-color: #ddd;
}
.indicator-item-active {
background-color: red;
}
</style>
(6) Homepage list
All store links: Click here to view.
<a-cell-group>
<a-cell v-bind:title="_item.name" v-bind:value="_item.content" v-bind:label="_item.address" arrow-direction="right" @click="godetial(_item)" v-for="(_item,_index) in shangjialist" />
</a-cell-group>
shangjialist: [],
allstores() {
var _this = this;
api.ajax({
url: 'http://yy.deui.cn/api.php/index/stores',
method: 'get',
}, function (ret, err) {
if (ret.msg == '返回成功') {
console.log(1)
console.log(JSON.stringify(ret.data.data))
console.log(1)
var obj = ret.data.data
for (let index = 0; index < obj.length; index++) {
const element = obj[index];
element['content'] = element['content'].substring(0, 9) + '...'
}
console.log(JSON.stringify(obj))
_this.data.shangjialist = obj
} else {
api.alert({ msg: JSON.stringify(err) });
}
});
}
(7) Page reference to get details
api.openWin({
name: 'detial',
url: './detial.stml',
pageParam: {
id: 123
}
});
apiready() {//like created
if (api.pageParam.id) {
this.data.id = api.pageParam.id
}
console.log(this.data.id)
}
Store Details: Click here to view.
<template>
<view class="page">
<view>
<a-nav-bar v-bind:title="title" left-text="返回" left-arrow @click-left="onClickLeft"
@click-right="onClickRight" />
</view>
<view>
<a-cell-group>
<a-cell title="名称" v-bind:value="store.name" />
<a-cell title="电话" v-bind:value="store.phone" />
<a-cell title="城市" v-bind:value="store.city" />
<a-cell title="地址" v-bind:value="store.address" />
<a-cell title="内容" v-bind:value="store.content" />
</a-cell-group>
<!-- <text text={{store.name}}></text>
<text text={{store.city}}></text>
<text text={{store.content}}></text>
<text text={{store.address}}></text>
<text text={{store.phone}}></text> -->
</view>
<view>
<a-button type="success">产品</a-button>
</view>
<view>
<a-cell-group>
<a-cell v-bind:title="_item.name" v-bind:value="_item.content" label="点击可预约" v-for="(_item,_index) in product" @click="yuyuproduct(_item)" />
</a-cell-group>
</view>
</view>
</template>
<script>
import ACellGroup from "../../components/act/a-cell-group";
import ACell from "../../components/act/a-cell";
import ANavBar from "../../components/act/a-nav-bar";
import AButton from "../../components/act/a-button";
export default {
name: 'detial',
apiready() {//like created
if (api.pageParam.id) {
this.data.id = api.pageParam.id
}
console.log(this.data.id)
this.init()
},
data() {
return {
title: "详情",
id: 1,
store: {},
product:[]
}
},
methods: {
init() {
var _this = this;
api.ajax({
url: 'http://yy.deui.cn/api.php/index/searchstore',
method: 'get',
data: {
body: {
id: _this.data.id
}
}
}, function (ret, err) {
if (ret.msg == '返回成功') {
console.log(1)
console.log(JSON.stringify(ret.data.product))
console.log(1)
_this.data.store = ret.data.data[0]
_this.data.product = ret.data.product
} else {
api.alert({ msg: JSON.stringify(err) });
}
});
},
yuyuproduct(_item){
console.log(JSON.stringify(_item))
},
onClickLeft() {
api.closeWin();
},
onClickRight() {
}
}
}
</script>
<style>
.page {
height: 100%;
}
</style>
(8) Navigation bar components
import ANavBar from ".. /.. /components/act/a-nav-bar";
<a-nav-bar v-bind:title="title" left-text="返回" left-arrow @click-left="onClickLeft"
@click-right="onClickRight" />
Note The navigation bar component is used by references in the document
import ACell from".. /.. /components/act/a-nav-bar.stml";
In use it is recommended to remove the stml suffix, import ACell from": /.. /components/act/a-nav-bar";
(9) LocalStorage object use
This in main.stml is the object used
localStorage.setItem('uid', ret.data.data[0]['id']);
Here's how localStorage is used
// 设置存储.
sessionStorage.setItem('key', 'value');
// 获取存储.
var value = sessionStorage.getItem('key');
// 移除存储
sessionStorage.removeItem('key');
// 清除所有存储项
sessionStorage.clear();
// 获取已有存储项数
var length = sessionStorage.length;
// 根据存储项索引获取存储键名
var keyName = sessionStorage.key(index);
(10) The use of APICloud components and modules
Add modules to "modules", if it is H5, after downloading, put it into their own code; if it is a native module, you need to add it to your own application and use it. There is a special tutorial on the Internet to introduce this piece, and those who are not clear can search for it.
04
Application background setup
The background of the "Appointment" application is the php background written in the tp5 framework.
The fastadmin framework under tp5 can generate a simple background according to fastadmin, and the database file is:
Background interface code
<?php
namespace app\api\controller;
use app\common\controller\Api;
use think\Db;
/**
* 首页接口
*/
class Index extends Api
{
protected $noNeedLogin = ['*'];
protected $noNeedRight = ['*'];
/**
* 首页
*
*/
public function index()
{
$this->success('请求成功');
}
//注册
public function re(){
$username = $this->request->request("username");
$password = md5($this->request->request("password"));
$sql = " INSERT INTO `yuyuuser` (`id`, `group_id`, `username`, `nickname`, `password`, `salt`, `email`, `mobile`, `avatar`, `level`, `gender`, `birthday`, `bio`, `money`, `score`, `successions`, `maxsuccessions`, `prevtime`, `logintime`, `loginip`, `loginfailure`, `joinip`, `jointime`, `createtime`, `updatetime`, `token`, `status`, `verification`) VALUES (NULL, '0', '', '".$username ."', '".$password."', '', '', '', '', '0', '0', NULL, '', '0.00', '0', '1', '1', NULL, NULL, '', '0', '', NULL, NULL, NULL, '', '', '')";
$rst = Db::query($sql);
$data =1;
$this->success('返回成功', ['data' => $data]);
}
//登录
public function login(){
$username = $this->request->request("username");
$password = md5($this->request->request("password"));
$sql = "SELECT nickname,id FROM yuyuuser where nickname='".$username."' and password='".$password."' order by id DESC LIMIT 1";
$rst = Db::query($sql);
$this->success('返回成功', ['data' => $rst]);
}
//获取轮播图
public function banner()
{
$sql = "SELECT * FROM `yuyubanner`";
$rst = Db::query($sql);
$this->success('返回成功', ['data' => $rst]);
}
//获取网络协议
public function xieyi()
{
$sql = "SELECT * FROM `yuyuxieyi`";
$rst = Db::query($sql);
$this->success('返回成功', ['data' => $rst]);
}
//所有类型
public function leixing()
{
$sql = " SELECT * FROM `yuyutype`";
$rst = Db::query($sql);
$this->success('返回成功', ['data' => $rst]);
}
//所有店面
public function stores()
{
$sql = " SELECT * FROM `yuyustore`";
$rst = Db::query($sql);
$this->success('返回成功', ['data' => $rst]);
}
//店铺信息
public function searchstore()
{
$id = $this->request->request("id");
$sql = " SELECT * FROM `yuyustore` where id = ".$id;
$sql1 = "SELECT * FROM `yuyuproduct` where store_id = ".$id;
$rst = Db::query($sql);
$rst1 = Db::query($sql1);
$this->success('返回成功', ['data' => $rst,'product'=>$rst1]);
}
}
Using the AVM framework to develop applications can greatly shorten the development time and improve the development efficiency. Interested partners can come to learn to try the next ~