使用APICloud AVM框架开发预约应用

前段时间跟朋友一起搞了一个预约的项目,前端用的APICloud的AVM框架做的,后端用的php开发的,用的tp5框架,没几天就搞出来了。简单跟大家分享一下开发中的一些功能点的实现吧。也欢迎大家一起探讨。

一、整理项目脑图

二、项目原型图

原型是用的码前做的,大家可以通过​​www.devbefore.com/protomobile/47942983009476608​​查看

三、项目前端

本项目中前端采用APICloud AVM多端开发技术进行开发,要点包括 swiper 轮播图、网络请求封装等。使用 APICloud 多端技术进行开发,实现一套代码多端运行,支持编译成 Android & iOS App 以及微信小程序。

1、APICloud使用步骤:

(1)下载 APICloud Studio 3 作为开发工具。下载地址:​​​www.apicloud.com/studio3​​​

(2)注册账号后在控制台创建app,控制台地址:​​www.apicloud.com/console​​

(3)设置证书,一键创建安卓证书

(4)APICloud Studio3 拉取代码,点击项目,导出云端检出

检出

检出后工作目录

(5)修改或者提交项目源码,并为当前项目云编译自定义 Loader 进行真机同步调试预览。

使用 AppLoader 进行真机同步调试预览,后台自动以loader下载到手机端,安装后,点击小圆圈,输入ip地址:192.168.2.152 端口:10916,连接后真机同步,可以看到刚创建后的结果。

2、AVM框架的使用

为什么选择AVM?

  • 易用:有 Vue、React 基础,可快速上手,配套专用的开发工具APICloud Studio3。
  • 多端:一次开发,多端渲染,一个技术栈搞定移动端开发。
  • 功能 API 丰富:提供 1千+ 模块和 2万+ API 可直接调用,面向行业和场景无限制)

(1)UI

官方文档:​​https://docs.apicloud.com/apicloud3/#/component/view?index=4&subIndex=0​​

原来有一个叫流浪男的做的AUI框架,还不错,后来就是用一些简单的大公司的框架,这个看自己了。好多UI都是自己设计,自己写一些UI。字体图标用的阿里字体图标。CSS框架,要是没啥选择推荐可以用AUI,这个框架有CSS基础的一看就知道写的啥,而且特别好修改。实在不行了直接改源码。

APICloud官网组装了一套vant的,​​https://docs.apicloud.com/Client-API/AVM-Components/readme.md​​

(2)ajax网络交互

// 表单方式提交数据或文件

// 表单方式提交数据或文件

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>数据绑定

使用 Mustache 语法:

<text text={{msg}}></text>

使用v-bind指令:

<text v-bind:text="msg"></text>

2>事件绑定

<template>

<text onclick="doThis('avm');">Click me!</text>

</template>

<script>

export default {

name: 'test',

methods: {

doThis(msg){

api.alert({

msg: msg

});

}

}

}

</script>

(4)注册、登录

1>注册接口链接:​​http://showdoc.deui.cn/web/#/27?page_id=365​​

注册代码

<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="/wp-content/uploads/aaacj/20220327vue1234/112.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: "/wp-content/uploads/aaacj/20220327vue1234/bg13.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>登录接口链接:​​http://showdoc.deui.cn/web/#/27?page_id=364​​

登录代码

<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="/wp-content/uploads/aaacj/20220327vue1234/112.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: "/wp-content/uploads/aaacj/20220327vue1234/bg13.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)首页轮播图

轮播图链接:​​http://showdoc.deui.cn/web/#/27?page_id=366​​

<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: "/wp-content/uploads/aaacj/20220327vue1234/bg13.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)首页列表

所有店铺链接:​​http://showdoc.deui.cn/web/#/27?page_id=369​​

上面的5的代码里面含有了。

<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)页面传参获取详情

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)

}

店铺详情:​​http://showdoc.deui.cn/web/#/27?page_id=370​​

<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)导航栏组件

import ANavBar from "../../components/act/a-nav-bar";

<view>

<a-nav-bar v-bind:title="title" left-text="返回" left-arrow @click-left="onClickLeft"

@click-right="onClickRight" />

</view>

注意:导航栏组件的使用,文档中的引用

import ACell from"../../components/act/a-nav-bar.stml";

使用中建议去掉stml后缀,import ACell from"../../components/act/a-nav-bar";

(9)localStorage 对象使用

main.stml里面的这个,就是用的这个对象

localStorage.setItem('uid', ret.data.data[0]['id']);

下面是localStorage的用法

// 设置存储.

sessionStorage.setItem('key', 'value');

// 获取存储.

var value = sessionStorage.getItem('key');

// 移除存储

sessionStorage.removeItem('key');

// 清除所有存储项

sessionStorage.clear();

// 获取已有存储项数

var length = sessionStorage.length;

// 根据存储项索引获取存储键名

var keyName = sessionStorage.key(index);

(10)APICloud组件、模块的使用

模块中添加模块,如果是H5的需要下载后,放到自己的代码中;如果是原生的模块,需要添加到自己应用中,去require去使用。网上有专门介绍这块的教程,不清楚的可以去搜搜。

四、项目后台

这块是自己写的php后台,用的tp5框架。

tp5下的fastadmin框架,可以根据fastadmin一键生成简单后台,数据库文件为:

后台接口代码

<?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]);

}

}

以上是 使用APICloud AVM框架开发预约应用 的全部内容, 来源链接: utcz.com/z/267794.html

回到顶部