项目实战【vue,react,微信小程序】(1707B)

目录

一、transition过渡动画

二、比较两个对象是否相等

三、图片懒加载使用gif图做loading

四、图片懒加载使用动画做loading

五、forEach

六、vuex getters

七、详情页对象变更检查注意事项

八、对话框组件

九、自定义alert,全局函数使用

十、瀑布流

十一、reduce和immutable.js

十二、使用immutable实现对象深拷贝

十三、路由懒加载

十四、小程序自定义对话框组件

十五、小程序案例

github源代码:


 

 

 

 

 

 

 

 

 

 

 

 

 

 

一、transition过渡动画

过渡:

<template>
  <div>
    <div>
      <router-link to="/index/home" class="m-nav-item">首页</router-link>
      <router-link to="/index/my_book" class="m-nav-item">书包</router-link>
      <router-link to="/index/news" class="m-nav-item">新闻</router-link>
    </div>
    <transition name="m-slide">
      <router-view class="m-router"></router-view>
    </transition>
  </div>
</template>

<script>
export default {
  watch: {
    $route(to, from) {
      console.log(to);
    }
  },
  methods: {

  }
};
</script>

<style>
.m-router {
  position: absolute;
  width: 100%;
}
/* 新路由的初始样式 */
.m-slide-enter {
  transform: scale(2, 2) rotate(-5deg) translate(100%, 0);
  opacity: 0;
}

/* 新路由的目标样式 */
.m-slide-enter-to {
  color: red!important;
}

/* 新路由动画 */
.m-slide-enter-active {
  transition: all 1s ease-in-out;
}

/* 老路由目标样式 */
.m-slide-leave-to {
  transform: translate(-100%, 0);
}

/* 老路由动画 */
.m-slide-leave-active {
  transition: all 1s ease-in-out;
}
</style>

动画:

<template>
  <div>
    <div>
      <router-link to="/index/home" class="m-nav-item">首页</router-link>
      <router-link to="/index/my_book" class="m-nav-item">书包</router-link>
      <router-link to="/index/news" class="m-nav-item">新闻</router-link>
    </div>
    <transition name="m-slide">
      <router-view class="m-router"></router-view>
    </transition>
  </div>
</template>

<script>
export default {
  watch: {
    $route(to, from) {
      console.log(to);
    }
  },
  methods: {

  }
};
</script>

<style>
.m-router {
  position: absolute;
  width: 100%;
}

/* 新路由动画 */
.m-slide-enter-active {
  animation: new-router 1s ease-in-out;
}

/* 老路由动画 */
.m-slide-leave-active {
  animation: old-router 1s ease-in-out;
}

@keyframes new-router {
  0% {
    transform: scale(2, 2) rotate(-5deg) translate(100%, 0);
    opacity: 0;
  }
  50% {
    color: red;
  }
  100% {

  }
}

@keyframes old-router {
  0% {
    transform: translate(100px)
  }
  100% {
    transform: translate(-100%, 0);
  }
}
</style>

二、比较两个对象是否相等

使用immutable:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>immutable.js实例</title>
  <style type="text/css">
  </style>
</head>

<body>
  <script src="https://cdn.bootcss.com/immutable/4.0.0-rc.12/immutable.js"></script>
  <script type="text/javascript">
    // 原来的写法
    var foo = { a: { b: 1 } };
    var bar = foo;
    bar.a.b = 2;
    console.log(foo.a.b);  // 打印 2
    console.log(foo === bar);  //  打印 true

    // 使用 immutable.js 后
    var foo = Immutable.fromJS({ a: { b: 1 } });
    var bar = foo.setIn(['a', 'b'], 2);   // 使用 setIn 赋值
    console.log(foo.getIn(['a', 'b']));  // 使用 getIn 取值,打印 1
    console.log(foo === bar);  //  打印 false     
    console.log(bar.getIn(['a', 'b']))  //2
    console.log(bar.toJS())  //转变成js对象

    let obj1 = Immutable.fromJS({
      a: {
        b: 1,
        c: [1, 2]
      }
    })

    let obj2 = Immutable.fromJS({
      a: {
        b: 1,
        c: [1, 2]
      }
    })

    //比较两个对象是否相等
    console.log(Immutable.is(obj1, obj2))
  </script>
</body>

</html>

 使用原生js(核心代码):

    function compare2Objects(x, y) {
      for (let p in x) {
        // if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
        //   return false;
        // } else if (typeof y[p] !== typeof x[p]) {
        //   return false;
        // }
        
        switch (typeof (x[p])) {
          case 'object':
            if (!compare2Objects(x[p], y[p])) {
              return false;
            }
            break;
          default:
            if (x[p] !== y[p]) {
              return false;
            }
            break;
        }
      }

      return true;
    }

    let obj3 = {
      a: {
        b: 1,
        c: [1, 2]
      }
    }

    let obj4 = {
      a: {
        b: 1,
        c: [1, 3]
      }
    }

    console.log(compare2Objects(obj3, obj4))

 

详细代码请参考:

https://www.jianshu.com/p/90ed8b728975

三、图片懒加载使用gif图做loading

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import lazyload from 'vue-lazyload'
import './index.css'
import loading from './images/loading.gif'

Vue.use(lazyload, {
  loading: loading
})

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')
    <div v-for="item in list" :key="item.id" class="m-list-item">
      <div class="m-img-wrap">
        <img v-lazy="item.avatar" class="m-img">
      </div>
      <div class="m-info">
        {{item.title}}
      </div>
    </div>
.m-list-item{display: flex;margin: 5px;}
.m-img-wrap{display: flex; width: 122px;height: 150px;background: #dddddd;}
.m-img{width: 122px;height: 150px;}
.m-img[lazy=loading]{margin: auto; width: 38px;height: 38px;}
.m-info{flex:1}

四、图片懒加载使用动画做loading

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import lazyload from 'vue-lazyload'
import './index.css'


Vue.use(lazyload)

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')
    <div v-for="item in list" :key="item.id" class="m-list-item">
      <div class="m-img-wrap">
        <img v-lazy="item.avatar" class="m-img">
      </div>
      <div class="m-info">
        {{item.title}}
      </div>
    </div>
.m-list-item{display: flex;margin: 5px;}
.m-img-wrap{display: flex; width: 122px;height: 150px;background: #dddddd;}
.m-img-wrap::before{content: '';margin: auto;width: 38px;height: 38px;background-image: url(./images/loading.png);animation: loading 1s linear infinite;}
.m-img{position: absolute; width: 122px;height: 150px;}
.m-img[lazy=loading]{margin: auto; width: 38px;height: 38px;}
.m-info{flex:1}

@keyframes loading {
  0% {transform: rotate(0);}
  100%{transform: rotate(360deg);}
}

五、forEach

在forEach中,不能使用 continue 和 break ,可以使用 return 或 return false 跳出循环,效果与 for 中 continue 一样。注意该方法无法一次结束所有循环,需要一次性结束所有循环,还是老老实实使用for方法。

var arr = ['a', 'b', 'c', 'd', 'e'];
arr.forEach(function(item, index) {
    if (item === 'b') {
        return true;  //相当于continue
    }
    if (item === 'c') {
        return false;  //相当于continue
    }
    console.log(index + ':' + item);
});

六、vuex getters

通过属性访问:

  getters: {
    getCurrentList(state) {
      return state.currentList
    }
  },
export default {
  computed: {
    list() {
      return this.$store.getters.getCurrentList
    }
  }
}

通过方法访问:

  getters: {
    getTaskList(state) {
      return (type, search) => {
        return state.taskList.filter(item => {
          if (type) {
            return item.type === type
          } else {
            return true
          }
        }).filter(item => {
          if (search) {
            return item.name.includes(search)
          } else {
            return true
          }
        })
      }
    }
  },
  methods: {
    handleTask(type) {
      this.taskList = this.$store.getters.getTaskList(type, this.search)
      this.type = type
      console.log(this.taskList)
    },
    handleSearch() {
      this.taskList = this.$store.getters.getTaskList(this.type, this.search)
      console.log(this.taskList)
    }
  },

七、详情页对象变更检查注意事项

 

<template>
  <div>
    <img :src="detail.avatar" />
    <div>
      {{detail.summary}}
    </div>
    <div>
      <button v-if="detail.is_in_my_book">已收藏</button>
      <button v-else @click="handleAdd(detail)">收藏</button>
    </div>
  </div>
</template>

<script>
import Api from '../api'

export default {
  data() {
    return {
      detail: {}
    };
  },
  methods: {
    handleAdd(detail) {
      detail.count = 1
      Api.add({
        book: detail
      }).then(res => {
        if(res.code === 200) {
          //this.detail.is_in_my_book = true   //不推荐的写法
          //this.$set(this.detail, 'is_in_my_book', true)   //推荐的写法
          //this.detail = {...this.detail, is_in_my_book: true} //推荐的写法
          this.detail = Object.assign({}, this.detail, {is_in_my_book: true})  //推荐的写法
        }
      })
    }
  },
  mounted() {
    let { id } = this.$route.params;
    Api.getDetail(`?id=${id}`).then(res => {
      if (res.code === 200) {
        this.detail = res.data;
      }
    });
  }
};
</script>

<style>
</style>

参考链接:

https://cn.vuejs.org/v2/guide/list.html#%E5%AF%B9%E8%B1%A1%E5%8F%98%E6%9B%B4%E6%A3%80%E6%B5%8B%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9

 

八、对话框组件

Dialog.vue:

<template>
  <div class="m-dialog-wrap" :class="{active: visible}">
    <div class="m-dialog">
      <div class="m-dialog-title">{{title}}</div>
      <div class="m-dialog-content">
        <slot></slot>
      </div>
      <div class="m-dialog-footer">
        <slot name="footer"></slot>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: ['visible', 'title']
}
</script>

<style>

</style>

css:

.m-dialog-wrap{display: none;position: fixed;top: 0;left: 0;right: 0;bottom: 0;background: rgba(0, 0, 0, 0.2);}
.m-dialog-wrap.active{display: flex;}
.m-dialog{display: flex;flex-direction: column; margin: auto;padding: 10px; min-width: 200px;min-height: 150px; background: #ffffff;border-radius: 5px;}
.m-dialog-title{line-height: 50px;font-weight: bold;}
.m-dialog-content{flex: 1;}
.m-dialog-footer{line-height: 50px; text-align: right;}

使用:

    <Dialog :visible="visible" title="提示">
      <div>你确定要删除选中的图书吗?</div>
      <div>...</div>
      <template v-slot:footer>
        <div>
          <button @click="handleHideDialog">取消</button>
          <button @click="handleDeleteSelected">确定</button>
        </div>
      </template>
    </Dialog>

 

九、自定义alert,全局函数使用

参考链接:

https://www.jb51.net/article/159958.htm

vue.extend():

https://cn.vuejs.org/v2/api/#Vue-extend

 

Vue.extend()  参数是一个vue组件,使用脚手架开发时即.vue的文件,返回值是一个构造函数,这个构造函数使用new创建一个组件实例

 

Alert.vue:

<template>
  <div class="m-dialog-wrap" :class="{active: visible}">
    <div class="m-dialog">
      <div class="m-dialog-title">{{title}}</div>
      <div class="m-dialog-content">
        {{message}}
      </div>
      <div class="m-dialog-footer">
        <button @click="handleHideDialog">确定</button>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      title: '',
      message: '',
      visible: true
    }
  },
  methods: {
    handleHideDialog() {
      this.visible = false
    }
  }
}
</script>

<style>
  
</style>

index.js:

import Vue from 'vue'
import Alert from './Alert'

const AlertConstructor = Vue.extend(Alert)

const AlertFun = (options) => {
  let instance = new AlertConstructor({data: options}).$mount()
  document.body.appendChild(instance.$el)
}

export default AlertFun

vue入口函数在原型链上添加函数:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

import Alert from './components/Alert/index.js'

Vue.prototype.$MyAlert = Alert

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

在组件里使用:

this.$MyAlert({title: '标题', message: '请选择要删除的图书~'})

在拦截器里使用:

import MyAlert from '../components/Alert'

axios.defaults.baseURL = 'http://localhost:85'

axios.interceptors.response.use((res) => {
  if (res.data.code === 400) {
    MyAlert({title: '标题', message: res.data.message})
  }
  return res
})

十、瀑布流

Waterfall.vue:

<template>
  <div class="m-news-wrap">
    <div class="m-news-serarch">
      <input type="text" placeholder="请输入关键词" v-model="search" @keyup.enter="handleSearch" />
    </div>
    <div class="m-waterfall js-waterfall" @scroll="handleScroll">
      <div id="m-waterfall-top"></div>
      <div>
        <div v-for="item in list" :key="item.id" class="m-waterfall-item-wrap js-waterfall-item-wrap">
          <div class="m-waterfall-item">
            <img :src="item.image" class="m-waterfall-img" :height="imageHeight(item.image)" />
            <div class="m-waterfall-info">{{item.id}} {{item.name}}</div>
          </div>
        </div>
      </div>
      <div class="m-waterfall-end">{{end}}</div>
    </div>
    <el-backtop target=".js-waterfall"></el-backtop>
  </div>
</template>

<script>
import Api from "../api";

let isUpdated = true;
export default {
  data() {
    return {
      list: [],
      page: 1,
      search: "",
      end: ""
    };
  },
  methods: {
    handleScroll(e) {
      console.log(
        e.target.clientHeight,
        e.target.scrollTop,
        e.target.scrollHeight
      );
      let { scrollTop, clientHeight, scrollHeight } = e.target;
      if (
        scrollTop + clientHeight + 200 > scrollHeight &&
        isUpdated &&
        this.end === ""
      ) {
        this.page++;
        isUpdated = false;
        Api.news(`?page=${this.page}&size=10&search=${this.search}`).then(
          res => {
            if (res.code === 200) {
              this.list = [...this.list, ...res.data];
              if (res.data.length < 10) {
                this.end = "我是有底线的~";
              }
            }
          }
        );
      }
    },
    handleSearch() {
      document.getElementById("m-waterfall-top").scrollIntoView(true);
      Api.news(`?page=1&size=10&search=${this.search}`).then(res => {
        if (res.code === 200) {
          this.list = res.data;
          this.page = 1;
          this.end = "";
        }
      });
    },
    waterFall() {
      let items = document.getElementsByClassName("js-waterfall-item-wrap");
      items = Array.from(items);
      items[0].style.top = 0; //第一个元素
      items[0].style.left = 0;
      items[1].style.top = 0; //第二个元素
      items[1].style.left = "50%";

      let arr = [];
      arr.push(items[0].offsetHeight); //第一个元素
      arr.push(items[1].offsetHeight); //第二个元素
      for (let i = 2; i < items.length; i++) {
        let minHeight = arr[0];
        let index = 0;
        for (let j = 0; j < arr.length; j++) {
          if (minHeight > arr[j]) {
            minHeight = arr[j];
            index = j;
          }
        }
        items[i].style.top = arr[index] + "px";
        items[i].style.left = items[index].offsetLeft + "px";
        arr[index] = arr[index] + items[i].offsetHeight;
      }
    },
    imageHeight(url) {
      let size = url.slice(url.lastIndexOf("/") + 1).split("x");
      //真实宽度 / 真实高度 = 原始宽度  / 原始高度
      //真实高度 = 真实宽度 / 原始宽度 * 原始高度
      let height =
        ((window.document.body.clientWidth / 2 - 20) / size[0]) * size[1];
      return height;
    }
  },
  updated() {
    isUpdated = true;
    this.waterFall();
  },
  mounted() {
    Api.news("?page=1&size=10").then(res => {
      if (res.code === 200) {
        this.list = res.data;
      }
    });
  }
};
</script>

<style>
</style>

css:

.m-waterfall{position: relative; flex:1; overflow-y: auto;overflow-x: hidden; background: #eeeeee;}
.m-waterfall-item-wrap{display: inline-block;position: absolute; width: 50%;padding: 10px;box-sizing: border-box;vertical-align: top;}
.m-waterfall-item-wrap:last-child{margin: 0 0 50px 0;}
.m-waterfall-item{border-radius: 10px;}
.m-waterfall-img{width: 100%; border-top-left-radius: 10px;border-top-right-radius: 10px;}
.m-waterfall-info{margin: -4px 0 0;padding: 5px; height: 100px;background: #ffffff;border-bottom-right-radius: 10px;border-bottom-left-radius: 10px;}
.m-waterfall-end{position: fixed;width: 100%;line-height: 50px; bottom: 0; text-align: center;color: #aaaaaa;}

mock数据:

const news = Mock.mock({
	'list|500': [{
		'id|+1': 1,
		'name': '@cname',
		//'image': Mock.Random.image(null, '#ff0000', '#ffff00', 'hello'),
		'image': '@image()',
		'title': '@ctitle',
		'paragraph': '@cparagraph',
		'datetime': '@datetime'
	}]
}).list

分页+搜索接口:

app.get('/api/news', (req, res) => {
  let { page, size, search = '' } = req.query
  let newsSearchResult = news.filter(item => item.name.includes(search))
  let start = (page - 1) * size
  let end = start + size * 1
  res.send({
    code: 200,
    data: newsSearchResult.slice(start, end),
    message: '新闻'
  })
})

十一、reduce和immutable.js

Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。Immutable 实现的原理是 Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable 使用了Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。请看下面动画:

1. 降低 Mutable 带来的复杂度

2. 节省内存空间

3. Undo/Redo,Copy/Paste,随意穿越!

4. 拥抱函数式编程

不要修改 state:

https://www.redux.org.cn/docs/basics/Reducers.html

https://segmentfault.com/a/1190000017294051

immutable官网:

https://immutable-js.github.io/immutable-js/

为啥要使用immutable:

http://yunlaiwu.github.io/blog/2016/12/01/react+redux+immutablejs/#2

简介:

Facebook 工程师 Lee Byron 花费 3 年时间打造!

https://www.jianshu.com/p/0fa8c7456c15

 

import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { fromJS } from 'immutable'

const defaultState = fromJS({
  listAll: [],
  currentId: 0,
  myBook: []
})

const reducer = (state = defaultState, action) => {
  switch (action.type) {
    case 'SET_STATE':
      // let newState = JSON.parse(JSON.stringify(state))
      // newState[action.key] = action.value
      //state是一个immutable对象,调用setIn方法并不会修改state的值,因为state是immutable对象
      //newState是基于state和action创建出来的新immutable对象,这个创建过程并不是深拷贝,深拷贝性能很低
      //这个创建过程会共享没有改变的部分,避免深拷贝把所有节点都复制一遍带来的性能损耗
      //对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享
      let newState = state.setIn(action.key, fromJS(action.value))
      console.log(state.toJS())
      console.log(newState.toJS())
      return newState
    default: 
      return state
  }
}

const store = createStore(reducer, applyMiddleware(thunk))

store.subscribe(() => {
  //console.log(store.getState().toJS())
})

export default store
const mapStateToProps = (state) => {
  state = state.toJS()
  return {
    navList: state.navList,
    currentId: state.currentId,
    currentList: state.currentList
  }
}

const mapDispatchToProps = (dispatch) => {
  return ({
    setState(key, value) {
      dispatch({ type: 'SET_STATE', key, value })
    }
  })
}

export default connect(mapStateToProps, mapDispatchToProps)(List)

十二、使用immutable实现对象深拷贝

<!DOCTYPE html>
<html>

<head>
</head>

<body>
  <div>
    <script src="https://cdn.bootcss.com/immutable/4.0.0-rc.12/immutable.js"></script>
    <script>
      let obj = {
        a: 1,
        b: {
          c: '1'
        },
        fun() {
          console.log(1)
        },
        time: new Date(),
        d: undefined
      }
      //有兼容性问题,https://www.cnblogs.com/sweet-ice/p/10583192.html
      let objClone = JSON.parse(JSON.stringify(obj))
      console.log(objClone)
      //使用immutable可以深拷贝值为undefined、函数和日期的key
      let objClone2 = Immutable.fromJS(obj).toJS()
      console.log(objClone2)
      objClone2.a = 2
      console.log(obj.a)  //1
      console.log(objClone2.a)  //2

      
    </script>
  </div>


</body>

</html>

 

 

十三、路由懒加载

import React, { Component, Suspense, lazy } from 'react'
import { Switch, Route } from 'react-router-dom'
import Header from '../components/Header'
import Footer from '../components/Footer'
import Home from './Home'
//import MyBook from './MyBook'  //无懒加载
import Me from './Me'
//const MyBook = lazy(() => import('./MyBook'))  //有懒加载
import Loading from '../components/Loading'

//懒加载延时1
// const MyBook = lazy(async () => {
//   return await new Promise((resolve, reject) => {
//     setTimeout(() => {
//       resolve(import('./MyBook'))
//     }, 500)
//   })
// })

//懒加载延时2
const MyBook = lazy(async () => {
  let component
  await new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(import('./MyBook'))
    }, 500)
  }).then(res => {
    component = res
  })

  return component
})

export default class Index extends Component {
  render() {
    return (
      <div className="m-wrap">
        <Header></Header>
        <Suspense fallback={<div className="m-main"><Loading lazyLoading={true}></Loading></div>}>
          <Switch>
            <Route path="/index/home" component={Home}></Route>
            <Route path="/index/my_book" component={MyBook}></Route>
            <Route path="/index/me" component={Me}></Route>
          </Switch>
        </Suspense>
        <Footer></Footer>
        <Loading></Loading>
      </div>
    )
  }
}

十四、小程序自定义对话框组件

dialog.js:

// components/dialog/dialog.js
Component({
  options: {
    styleIsolation: 'apply-shared', //isolatiion apply-shared shared
    multipleSlots: true
  },
  /**
   * 组件的属性列表
   */
  properties: {
    visible: Boolean,
    title: String
  },

  /**
   * 组件的初始数据
   */
  data: {

  },

  /**
   * 组件的方法列表
   */
  methods: {
    //小程序阻止遮罩层下的页面滚动
    handleMove(e) {

    }
  }
})

dialog.wxml:

除 bind 外,也可以用 catch 来绑定事件。与 bind 不同, catch 会阻止事件向上冒泡。

<!--components/dialog/dialog.wxml-->
<view class="m-dialog-wrap {{visible ? 'active' : ''}}" catch:touchmove="handleMove">
  <view class="m-dialog">
    <view class="m-dialog-header">{{title}}</view>
    <view class="m-dialog-content">
      <slot name="content"></slot>
    </view>
    <view class="m-dialog-footer">
      <slot name="footer"></slot>
    </view>
  </view>
</view>

dialog.wxss:

/* components/dialog/dialog.wxss */
.m-dialog-wrap{display: none; position: fixed;top: 0;left: 0;right: 0;bottom: 0;background: rgba(0, 0, 0, 0.5);z-index: 99;}
.m-dialog-wrap.active{display: flex;}
.m-dialog{display: flex;flex-direction: column; margin: auto;padding: 10rpx; min-width: 600rpx;min-height: 400rpx;background: #ffffff;border-radius: 10rpx;}
.m-dialog-header{height: 80rpx;font-weight: bold;}
.m-dialog-content{flex: 1;}
.m-dialog-footer{height: 80rpx;text-align: right;}



使用dialog组件:

  <dialog visible="{{visible}}" title="添加">
    <view slot="content" class="m-count-wrap">
      <view class="m-count">
        <text class="m-text-btn" bind:tap="handleSub">-</text>
        <input class="m-input" placeholder="请输入" value="{{item.count}}"></input>
        <text class="m-text-btn" bind:tap="handleAdd">+</text>
      </view>
    </view>
    <view slot="footer">
      <button size="mini" class="m-btn" bind:tap="handleHideDialog">取消</button>
      <button size="mini" class="m-btn" bind:tap="handleAddToMyBook">确定</button>
    </view>
  </dialog>

 

十五、小程序案例

 

 

 

 

 

 

 

 

 

 

github源代码:

https://github.com/baweireact/m-apps/tree/master/m-app-1707B

 

 

 

 

 

 

 

 

posted @ 2019-11-29 08:37  徐同保  阅读(33)  评论(0编辑  收藏  举报