<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>09_form</title>
</head>
<body>
<div id="example"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
  /*
  需求: 自定义包含表单的组件
    1. 界面如下所示
    2. 输入用户名密码后, 点击登陆提示输入信息
    3. 不提交表单
  */
  class FormComp extends React.Component {
    constructor (props) {
      super(props)
      // 初始化状态
      this.state = {
        username: 'abc',
        pwd: '123'
      }
    }
    handleNameChange = (event) => {
      const username = event.target.value
      // 更新状态(username)
      this.setState({
        username
      })
    }
    handlePwdChange = (event) => {
      const pwd = event.target.value
      // 更新状态(pwd)
      this.setState({
        pwd
      })
    }
    login = (event) => {
      // 阻止默认行为
      event.preventDefault()
      const {username, pwd} = this.state
      alert(`发送登陆请求 ${username}--${pwd}`)
      // return false
    }
    render () {
      const {username, pwd} = this.state
      return (
        <form action="/test" onSubmit={this.login}>
          用户名: <input type="text" onChange={this.handleNameChange} value={username}/>
          密码: <input type="password" onChange={this.handlePwdChange} value={pwd}/> /*事件在输入过程中不断触发*/
          <input type="submit" value="登陆"/>
        </form>
      )
    }
  }
  ReactDOM.render(<FormComp/>,document.getElementById('example'))
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>09_form</title>
</head>
<body>
<div id="example"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
  /*
  需求: 自定义包含表单的组件
    1. 界面如下所示
    2. 输入用户名密码后, 点击登陆提示输入信息
    3. 不提交表单
  */
  class FormComp extends React.Component {
    login = (event) => {
      // 阻止默认行为
      event.preventDefault()
      const username = this.refs.username.value
      const pwd = this.refs.pwd.value
      alert(`发送登陆请求 ${username}--${pwd}`)
      // return false
    }
    render () {
      return (
        <form action="/test" onSubmit={this.login}>
          用户名: <input type="text" ref="username"/>
          密码: <input type="password" ref="pwd"/>
          <input type="submit" value="登陆"/>
        </form>
      )
    }
  }
  ReactDOM.render(<FormComp/>,document.getElementById('example'))
</script>
</body>
</html>
	//  数字转换位千分位 千分位转换位数字
    function commafy(num) {
      //1.先去除空格,判断是否空值和非数   
      num = num + "";
      num = num.replace(/[ ]/g, ""); //去除空格  
      if (num == "") {
        return;
      }
      if (isNaN(num)) {
        return num;
      }
      //2.针对是否有小数点,分情况处理   
      var index = num.indexOf(".");
      if (index == -1) {//无小数点   
        var reg = /(-?\d+)(\d{3})/;
        while (reg.test(num)) {
          num = num.replace(reg, "$1,$2");
        }
        num += ".00";//强制转为含角分   pcj
      } else {
        var intPart = num.substring(0, index);
        var pointPart = num.substring(index + 1, num.length);
        //如果输入小数位为1位,追加0
        if (pointPart.length == 1) {
          pointPart += "0";
        }
        var reg = /(-?\d+)(\d{3})/;
        while (reg.test(intPart)) {
          intPart = intPart.replace(reg, "$1,$2");
        }
        num = intPart + "." + pointPart;
      }
      return num;
    }
    console.log(commafy(123456))//123,456.00
    console.log(commafy(1233254235456))
    /**  
     * 去除千分位  
     *@param{Object}num  
     */
    function delcommafy(num) {
      num = num.toString();
      num = num.replace(/[ ]/g, "");//去除空格  
      num = num.replace(/,/gi, '');
      return Number(num);
    }
    console.log(delcommafy(commafy(123456)));
    console.log(delcommafy(commafy(1233254235456)));
```js
import Vue from 'vue'   Vue.filter('NumFormat', function (value) {
if (!value) return '0.00'
if (value === '0') return '0.00'
//  获取整数部分
// let intPart = Number(value).toString.split('.')
let temp = String(value).split('.')
// let dot = intPart.indexOf('.')
// console.log('测试', intPart)
// console.log('测试', dot)
let intPart = temp[0]
// var intPart = Number(value).toFixed(0)
//  将整数部分逢三一断
var intPartFormat = intPart.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,')
//  预定义小数部分
var floatPart = '.00'
var value2Array = value.split('.')
//  =2表示数据有小数位
if (value2Array.length === 2) {
  //  拿到小数部分
  floatPart = value2Array[1].toString()
  //  补0,实际上用不着
  if (floatPart.length === 1) {
    return intPartFormat + '.' + floatPart
  } else {
    return intPartFormat + '.' + floatPart
  }
} else {
  return intPartFormat
}   })
```
```js
————   export const status = (state) => {
switch (state) {
  case '0':
    return '未上传'
  case '1':
    return '未预估'
  case '2':
    return '正常'
}   }
export const send = (state) => { switch (state) { case ‘10A’: return ‘已下载’ case ‘10B’: return ‘未下载’ } }
export const gradeStatus = (state) => { switch (state) { case ‘10B’: return ‘未处理’ case ‘10A’: return ‘已处理’ } } ———— 1.引入import { status } from ‘../../filter/runGradeState’ 2.使用过滤 filters: { status } 3.写入表达式 ```
import Axios from 'axios'
import router from '../router'
import { Toast } from 'vant'
import store from '../store'
import getuuid from './getUUID'
import { LGet } from './storage'
import { format } from '../filter/date'
import judge from './device'
var logData = ''
var httpUuid = ''
var user = LGet('user') || ''
// 建立请求对象
const service = Axios
/* const service = Axios.create({
  // baseURL: process.env.API_HOST, // api的base_url
  timeout: 1000 // request timeout
}) */
// 请求拦截器
service.interceptors.request.use(
  config => {
    httpUuid = getuuid(16, 16) // 生成请求uuid
    let account = ''
    let userPhone = ''
    if (config.url.indexOf('user/userLogin') !== -1) { // 如果为登录接口
      account = config.params.account
      userPhone = config.params.account
    } else {
      account = user.userName
      userPhone = user.phone
    }
    logData = {
      serverType: 'vue',
      logUuid: httpUuid,
      userId: user.id,
      userName: account,
      realIp: '',
      reqUrl: config.url,
      reqParam: config.params ? JSON.stringify(config.params) : '',
      repUrl: '',
      repParam: '',
      repData: '',
      logType: '1', // 1请求,2系统
      logLevel: '',
      resultCode: '',
      resultMsg: '',
      logErrInfo: '',
      logCreateTime: format(new Date(), 'YYYY-MM-DD HH:mm:ss'),
      logUploadTime: '',
      mobile: userPhone,
      netWork: '',
      appUuid: '',
      appType: '',
      appVersion: '',
      phoneType: '',
      phoneVersion: '',
      phoneWebView: '',
      httpStartTime: new Date().getTime(),
      httpEndTime: '',
      httpDuration: '',
      httpServer: 'app-backend'
    }
    if (config.url.indexOf('upload/json/fileUpload') !== -1) { // 如果为文件上传服务
      logData.httpServer = 'lf-files'
    }
    // const url = config.url
    const token = store.state.user.token
    /* if (!token) {
      store.dispatch('user/getLocalUser')
    } */
    /* // 判断是否为第三方请求 比如阿里oss直传
    if (url.indexOf('http') === 0) {
      // console.log(config)
      return config
    } */
    if (config.timeout === 0) {
      config.timeout = 20000
    }
    // 头部请求添加token
    if (token !== '') {
      config.headers.token = token
    }
    config.headers.logUuid = httpUuid
    // 安全策略配置
    // config.withCredentials = true
    // 拼接请求主域名
    // config.url = DOMAIN + config.url
    // config.responseType = 'json'
    return config
  },
  err => {
    // 日志上传参数
    logData.logLevel = '1'
    logData.logErrInfo = JSON.stringify(err)
    logData.httpEndTime = new Date().getTime()
    logData.httpDuration = logData.httpEndTime - logData.httpStartTime
    // console.log(logData) // 调用原生上传日志方法
    if (judge !== null) {
      judge.uploadLog(logData)
    }
    // console.log(err)
    return Promise.reject(err)
  })
// 响应拦截器
service.interceptors.response.use(
  response => {
    // console.log(response)
    // 日志上传参数
    logData.repData = JSON.stringify(response.data)
    logData.resultCode = response.status
    logData.resultMsg = response.data.resultMessage
    logData.httpEndTime = new Date().getTime()
    logData.httpDuration = logData.httpEndTime - logData.httpStartTime
    // console.log(logData) // 调用原生上传日志方法
    if (judge !== null) {
      judge.uploadLog(logData)
    }
    // 响应处理 如果响应code码为token失效那么返回至登录页
    if (response.status === 200) {
      switch (response.data.resultCode) {
        case '0001':
          router.replace({
            path: '/login'
          })
          Toast(response.data.resultMessage)
          return response.data
        case '200':
          return response.data
        default:
          if (response.data.resultMessage) {
            Toast(response.data.resultMessage)
          }
          return response.data
      }
    }
  },
  error => {
    // 日志上传参数
    logData.logLevel = '1'
    logData.logErrInfo = JSON.stringify(error)
    logData.httpEndTime = new Date().getTime()
    logData.httpDuration = logData.httpEndTime - logData.httpStartTime
    // console.log(logData) // 调用原生上传日志方法
    // console.log(JSON.stringify(error))
    if (judge !== null) {
      judge.uploadLog(logData)
    }
    if (error && error.response) {
      switch (error.response.status) {
        case 400: error.message = '请求错误(400)'; break
        case 401: error.message = '未授权, 请重新登录(401)'; break
        case 403: error.message = '拒绝访问(403)'; break
        case 404: error.message = '请求出错(404)'; break
        case 408: error.message = '请求超时(408)'; break
        case 500: error.message = '服务器错误(500)'; break
        case 501: error.message = '服务未实现(501)'; break
        case 502: error.message = '网络错误(502)'; break
        case 503: error.message = '服务不可用(503)'; break
        case 504: error.message = '网络超时(504)'; break
        case 505: error.message = 'HTTP版本不受支持(505)'; break
      }
    } else {
      error.message = '网络异常,请稍后重试'
    }
    Toast(error.message)
    // 响应错误拦截
    return Promise.reject(error) // 返回接口返回的错误信息
  })
export default service
// 判断设备
/**
 * @function judge
 * @returns {object} object
 * @return {function} object.loginData 传登录后的数据
 * @return {function} object.phone 打电话
 * @return {function} object.clearCache 清除缓存
 * @return {function} object.href 跳转到链接
 * @return {function} object.dbNumber icon显示未读数
 */
let object = {}
isWeiXin()
if (window.android && !isWeiXin()) {
  // 传登录后的数据
  object.loginData = (data) => {
    window.android.loginData(JSON.stringify(data))
  }
  // 打电话
  object.phone = (phone) => {
    // 这么调用的原因是因为 如果赋值给一个新的变量原生就无法监听了
    window.android.phone(phone)
  }
  // 清理缓存
  object.clearCache = () => {
    window.android.clearCache()
  }
  // 跳转到链接
  object.href = (url) => {
    window.android.href(url)
  }
  // icon显示未读数
  object.dbNumber = (number) => {
    window.android.dbNumber(number)
  }
  // 下载图片
  object.saveImg = () => {
    window.android.saveImg()
  }
  // 点击立即升级 跳转页面
  object.BeginUpgrade = (url) => {
    window.android.BeginUpgrade(url)
  }
  // 无网络时,点击重新加载
  object.noData = () => {
    window.android.noData()
  }
  // 分享工单
  object.shareGD = url => {
    window.android.shareGD(url)
  }
  // 日志上传
  object.uploadLog = (data) => {
    window.android.uploadLog(JSON.stringify(data))
  }
  // 退出登录
  object.quitLogin = () => {
    window.android.quitLogin()
  }
  // 获取缓存文件
  object.getCacheSize = () => {
    window.android.getCacheSize()
  }
} else if (window.webkit && !isWeiXin()) {
  // 传登录后的数据
  object.loginData = (data) => {
    window.webkit.messageHandlers.loginData.postMessage(data)
  }
  // 打电话
  object.phone = (phone) => {
    // window.webkit.messageHandlers.phone.postMessage(phonephone)
    window.webkit.messageHandlers.phone.postMessage(phone)
  }
  // 清理缓存
  object.clearCache = () => {
    window.webkit.messageHandlers.clearCache.postMessage(null)
  }
  // 跳转到链接
  object.href = (url) => {
    window.webkit.messageHandlers.href.postMessage(url)
  }
  // icon显示未读数
  object.dbNumber = (number) => {
    window.webkit.messageHandlers.dbNumber.postMessage(number)
  }
  // 下载图片
  object.saveImg = () => {
    window.webkit.messageHandlers.saveImg.postMessage(null)
  }
  // 点击立即升级 跳转页面
  object.BeginUpgrade = (url) => {
    window.webkit.messageHandlers.BeginUpgrade.postMessage(url)
  }
  // 无网络时,点击重新加载
  object.noData = () => {
    window.webkit.messageHandlers.noData.postMessage(null)
  }
  // 分享工单
  object.shareGD = (url) => {
    window.webkit.messageHandlers.shareGD.postMessage(url)
  }
  // 日志上传
  object.uploadLog = (data) => {
    window.webkit.messageHandlers.uploadLog.postMessage(data)
  }
  // 退出登录
  object.quitLogin = () => {
    window.webkit.messageHandlers.quitLogin.postMessage(null)
  }
  // 获取缓存
  object.getCacheSize = () => {
    window.webkit.messageHandlers.getCacheSize.postMessage(null)
  }
} else {
  // 怀疑android JS 注入有延迟
  object = null
}
function isWeiXin () {
  var ua = window.navigator.userAgent.toLowerCase()
  if (ua.indexOf('micromessenger') !== -1) {
    return true
  } else {
    return false
  }
}
export default object
import Vue from 'vue'
//1.引入vue-router
import Router from 'vue-router'
import find from '../components/find'
import mall from '../components/mall'
import microshop from '../components/microshop'
import cloud from '../components/cloud'
import personalcenter from '../components/personalcenter'
//2.使用vue-router
Vue.use(Router)
//3.实例化router这个构造函数
let router = new Router({
//去掉地址中的哈希#
mode:"history",
//5.映射,什么样的地址跳转到什么样的page
routes:[
{
//根目录
path:'/',
name:'首页',
component:mall
},
{
//发现page
path:'/find',
name:'发现',
component:find
},
{
//微店page
path:'/microshop',
name:'购物袋',
component:microshop
},
{
//云仓page
path:'/cloud',
name:'云仓',
component:cloud
},
{
//我的page
path:'/personalcenter',
name:'我的',
component:personalcenter
}
]
});
//page跳转后title改变
router.afterEach(function (to) {
document.title = to.name
})
export default router
// import Msite from '../pages/Msite/Msite.vue'
// import Order from '../pages/Order/Order.vue'
// import Search from '../pages/Search/Search.vue'
// import Profile from '../pages/Profile/Profile.vue'
// 打包时进行代码分割, 只有当需要时才从后台请求获取
const Msite=()=>import('../pages/Msite/Msite.vue')
const Order=()=>import('../pages/Order/Order.vue')
const Search=()=>import('../pages/Search/Search.vue')
const Profile=()=>import('../pages/Profile/Profile.vue')
下载less和less-loader,在build中webpack.base.conf.js的module里配置
module.exports = {
  context: path.resolve(__dirname, '../'),
  entry: {
    app: './src/main.js'
  },
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
  },
  module: {
    rules: [
      ...(config.dev.useEslint ? [createLintingRule()] : []),
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('media/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      },
      + {
        test: /\.less$/,
        loader: ['style-loader','css-loader','less-loader'],
      + },
    ]
  },
}
想控制哪个组件就在哪个组件上加 v-show="$route.meta.isShow"
在router/index中配置
routes: [
    {
      path: '/',
      component: HelloWorld,
     + meta:{
        isShow:true//默认为false显示,true为不显示
     + }
    },
]
<template>
  <footer class="footer_guide border-1px">
    <a href="javascript:;" class="guide_item " :class="{on:$route.path==='/msite'}" @click="goTo('/msite')">
      <span class="item_icon">
        <i class="iconfont icon-waimai"></i>
      </span>
      <span>外卖</span>
    </a>
    <a href="javascript:;" class="guide_item" :class="{on:$route.path==='/search'}" @click="goTo('/search')">
      <span class="item_icon">
        <i class="iconfont icon-search"></i>
      </span>
      <span>搜索</span>
    </a>
    <a href="javascript:;" class="guide_item" :class="{on:$route.path==='/order'}" @click="goTo('/order')">
      <span class="item_icon">
        <i class="iconfont icon-dingdan"></i>
      </span>
      <span>订单</span>
    </a>
    <a href="javascript:;" class="guide_item" :class="{on:$route.path==='/profile'}" @click="goTo('/profile')">
      <span class="item_icon">
        <i class="iconfont icon-geren"></i>
      </span>
      <span>我的</span>
    </a>
  </footer>
</template>
1)methods里定义函数
methods:{
      goTo(path){
          this.$router.replace(path)
      }
    }
2)动态控制类名
 .guide_item
      display flex
      flex 1
      text-align center
      flex-direction column
      align-items center
      margin 5px
      color #999999
      &.on
        color #02a774
1)定义工具函数文件
/*
数据存储的工具模块
问题: 向外暴露一个函数(一个功能)还是一个对象(多个功能)?
 */
const TODOS_KEY='todos_key'
export default {
  saveTodos(todos){//保存数据
    window.localStorage.setItem(TODOS_KEY,JSON.stringify(todos))
  },
  readTodos(){
    return JSON.parse(window.localStorage.getItem(TODOS_KEY)||'[]')
  }
}
2)引入调用使用
App中读取
import storgeUtil from './util/storgeUtil'
export default{
    data(){
      return{
        todos:storgeUtil.readTodos()
      }
    },
    computed: {
      completedCount () { // 完成的数量
        return this.todos.reduce((preTotal, todo) => preTotal + (todo.completed?1:0), 0)
      },