写点什么

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

作者:APICloud
  • 2022 年 1 月 21 日
  • 本文字数:12020 字

    阅读完需:约 39 分钟

使用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="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>登录接口链接:​​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="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)首页轮播图


轮播图链接:​​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: "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)首页列表


所有店铺链接:​​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]); }}
复制代码


发布于: 刚刚阅读数: 2
用户头像

APICloud

关注

一次编码多端运行,移动应用低代码开发平台 2020.12.22 加入

APICloud多端技术遵循标准 Web Components组件化思想,兼容Vue 、React语法特性,一次编码同时发布为Android 、iOS 、小程序、Html5(SPA)多端应用。

评论

发布
暂无评论
使用APICloud AVM框架开发预约应用