About the project

This is a meal ordering system, which includes three functions: user ordering, business ordering and administrator management. This project was originally a practical training on campus, which needed to be written in Java, and I was responsible for part of it. But I don’t like to use Java, and I have enough time, so I made a copy for study. The functions and requirements of the project are based on the group discussion in the early stage, and basically all functions of Ele. me have been basically realized

Online address: (slow)47.93.254.91:3333

Source code address: Chihuobao

Login account: User :12345678910 Merchant :11112222333 Administrator :admin2 The login password is 123456Copy the code

Functional structure

The test and operation

npm install

npm run dev



CD server # open koA2 background, open port 3333

npm install

node bin/www

Copy the code
NPM run build # package

Cp dist/* server/public/ # place the packed files in the KOA2 static directory

Copy the code

Page screenshots

The overall analysis

Frameworks used, plug-ins, etc

  • Vue-cli scaffolding, VUE-Router, vuex
  • Use an Element-UI style framework
  • Send the request using Axios
  • Use KOA2 in the background, and use async and await directly in advanced Node versions
  • Connect mongodb database with Mongoose

Included features

  • Mobile phone registration, login, reset password
  • When the user orders, the merchant receives a message indicating a new order (implemented by polling)
  • Users view their orders, comment on them, delete them, etc
  • Modify their own information, apply to become a merchant, etc
  • Merchants manage orders, receive orders, etc
  • Count merchant orders, scores, etc. (monthly sales on the page is the total sales)
  • Merchants manage menus and view reviews
  • Administrators manage users, shops, categories, etc
  • The search function

The directory structure

The top layer is vue-CLI structure, mainly look at the front-end SRC and background server structure

├─ SRC ├─ common # │ ├─ Audio # │ ├─ images # │ ├─ API # ├─ Style # Public Style ├─ Components # Component ├─ Pages # │ ├─ admin │ ├── user │ ├─ index. Vue │ ├─ login. Vue │ ├─ router # ├─ index │ ├─ admin │ ├── user │ ├─ index.js ├─ app.vue ├─ main.jsCopy the code
├─ Server ├─ App ├─ Common # Tools ├── Controllers ├─ Models ├── Routes # Route ├─ app.js ├─ config.js # SMS API key relatedCopy the code

The development process

Using the vue – cli

I used React and didn’t use scaffolding (such as Yeoman) to familiarize myself with Webpack. I was impressed by the complexity of Babel and the complexity of webPack configuration. Using vuE-CLI is simply a word: cool, all kinds of complex configuration are ready, such as using sass after downloading in the style configuration is good, do not have to go to webpack configuration, these miscellaneous configuration should not repeat. Now that the Parceiljs package tool is out, we can develop it more quickly in the future

The feeling of VUE is really friendly to the novice, the official website tutorial is very complete, many examples, fast. The vuex + map helper function is very convenient to use. Here is an example of how to log in

# login.vue
Vuex is divided into three modules: User, Seller and admin. Vuex is divided into three modules: User, Seller and adminmethods: { ... mapAction('user',    [
      'saveUserInfo'
    ]
  ),
  login () {
    _loginApi(phone, pass).then(res => {
      this.saveUserInfo(res.data)
      this.$router.push('/home')
    })}}Copy the code
# user/actions.js
Create a client storage to localStorage and then to state
import { _saveUserInfo } from 'common/javascript/cache'

export function saveUserInfo ({commit, state}, info) {
  commit(types.SET_USER_INFO, _saveUserInfo(info))
}
Copy the code
# index.vue
Vuex mapGetters: vuex mapGetters: vuex mapGetters
<template>
  <user-header :userInfo='userInfo'></user-header>
</template>
<script>
export default {
  computed: {
. mapGetters(
      'user',
      [
        'userInfo',
        'reLogin'
      ]
    )}}</script>
Copy the code

The flow of data is one-way

Problems encountered in development

Vuex module modification

No sub-modules were originally written like this

# store.js
# 
export default new Vuex.Store({
  getter,
  state,
  mutations,
  actions
})

Component call, direct callcomputed: { ... mapGetters(['suggestion']) }, methods: { ... mapActions(['saveUserInfo']), ... mapMutations({    setCoordinate: 'SET_COORDINATE'})}Copy the code

Divided module writing method has a difference

# store.js
# Each module has its own state, getters, actions
# Module structure is self-defined, so you can define a common top layer, and then in the inner layer module
export default new Vuex.Store({
  modules: {
    user,
    seller,
    admin}})# component call
# call with module name, mapActions with different modules separatelycomputed: { ... mapGetters(    'user',
      [
        'suggestionList',
        'userInfo'
      ]) }, methods: { ... mapMutations({    setCoordinate: 'user/SET_COORDINATE'
  }),
  ...mapActions('user',
    [
      'saveInfo'
    ]
  ),
  ...mapActions('seller',
    [
      'saveSellerInfo'
    ])}Copy the code

Parent-child component communication

In the general parent-child component, the parent component passes data to the child component, and the child component displays data, and the data flows in one direction. When a child component needs to pass data to its parent component, it fires a function to pass data to the parent component in the form of arguments, just like react data

# the parent component
<food-card @addOne='addOne' :info='info'></food-card>

# child components
<p class='name'>{{info.dishName}}</p>
<span class='money'>${{info. DishPrice}}</span>
<div :class='_status' @click='addToCart'>Add to shopping cart</div>. props: { info: {Type: Object, # Define the data type passed by the parent component. Vue will warn if the incoming type does not match the defined data type
    default: {}
  }
},
methods: {
  addToCart () {
$emit('addOne', this.info) # use this.$emit to trigger the addOne function of the parent component}}Copy the code

In the example above, you cannot modify the data passed in by the parent component. If you want to modify the data, you need to copy the data before $emit and then modify it and pass it to the parent component. You can also use sync to implement bidirectional binding between the parent component and the parent component. Sync was removed in 2.0 because it broke one-way data flow, but 2.3 was introduced because there were scenarios where some components needed to be reused. Sync, however, is a bit different from previous implementations in that it is just a syntax sugar that is extended to a V-on listener that automatically updates the parent component’s properties. And the child component needs to display the trigger update: this.$emit(‘update:xx’, newVal)

# the parent component
<card-item :data.sync='item'></card-item># will be expanded to this<comp :data="item" @update:data="newVal => item = newVal"></comp>

# child components
<input class='commend' type="text" v-model='commend' placeholder="Write down your comments on this dish.">
  export default {
    data () {
      return {
        commend: ''
      }
    },
    watch: {
      commend (newC) {
        this.data.commend = newC
$emit('update:data', this.data) # this.$emit('update:data', this.data) # this
      }
    },
    props: {
      data: {
        type: Object,
        default: {}
      }
    }
  }

Copy the code

The element-UI setting style is invalid

Using the Element-UI style framework, they sometimes need to make style changes to their components. But it’s wrapped, so I have to look at the source code to know that the class or tag that it defines internally comes from the style, but it doesn’t work, for example

  <el-rate
    v-model="item.score"
    disabled
    show-text
    text-color="#ff9900">
  </el-rate>

<style scoped lang='sass'>The.el-rate # component comes with a class of the same name    div
      background: red
<style>

The code presented by Jsfiddle is fine, so I look for the differences and discover that the scoped style tag may limit the scope of the style. Just leave it out. Note that the style is global, so be careful with the class name
Copy the code

Listen for $route carefully

In the view shop page, you can choose different types of businesses, you can also search for businesses, you can have different implementation methods. You can keep the state all within the component or vuEX management, but then the state disappears after the refresh. So I chose to hash the url to hold the state and listen for route changes to load different data. The merchant list data is placed in vuex

# merchant page, place.vue
data () {
  return {
    pageNum: 1,
    totalPage: 1,
    keyword: '',
    loading: false
  }
},
created () {
    this.getList()
# Scroll to load next page
    window.onscroll = () => {
if (! this.loading && this.__getScrollHeight() <= (this.__getWindowHeight() + window.scrollY + 100)) {
        if (this.pageNum < this.totalPage) {
          this.loading = true
          this.pageNum++
          this.getList()
        }
      }
    }
  },
watch: {
  $route () {
    this.getList()
  }
},
methods: {
  changeTag (tag) {
    this.pageNum = 1
    this.shopType = tag
    this.keyword = ''
    # this.clearShopList()
    this.$router.push({path: '/place', query: {shopType: code, keyword: undefined}})
  },
  search (str) {
    this.keyword = str
    this.pageNum = 1
    this.shopType = 1
    # this.clearShopList()
    this.$router.push({path: '/place', query: {shopType: undefined, keyword: str}})
  },
  getList () {
    const { keyword, shopType } = this.$router.currentRoute.query
    this.loading = true
_getShopList(keyword, shopType, this.pagenum). Then (res => {# concat stores vuex in the merchant list
.
    })}}Copy the code

Later, I found a bug: when changing routes on other pages, duplicate data will be loaded. Therefore, only the routes that listen for route changes are valid on this page

watch: {
  $route () {
    if (this.$router.currentRoute.name === 'place') {
      this.getList()
    }}}Copy the code

Then I found a bug: when I came back from another page, I also loaded duplicate data, and the solution was to delete the original data when I left the component. I did this because I saved the data in VUex and felt it was unnecessary to save it in VUex..

beforeDestroy () { window.onscroll = null this.clearShopList() }, methods: { ... mapMutations({      clearShopList: 'user/CLEAR_SHOP_LIST'})}Copy the code

The request is abnormal and the login page is redirected

Sometimes the request needs to have an exception like 401, and you need to get the user to log in again, and I’m using Axios

# This is a request wrapper that returns an exception by calling all reLogin actions to return the login pageimport store from '.. /.. /store' export function basePOST (api, params) { return axios({    method: 'post',
    url: api,
    headers: {
      'content-Type': 'application/x-www-form-urlencoded'
    },
    data: config.toFormData({
. params
    })
  }).then(res => {
    return res.data
  }).catch(() => {
    store.dispatch('user/reLogin')})}Copy the code

Koa2 Basic configuration

Initialize the project using the KOA generator

npm install koa-generator -g
koa2 server
cd server && npm install
npm start
Copy the code

Join session Middleware

const session = require('koa-session2')
app.use(session({
  key: 'sessionid---'
}))
Copy the code

Set up static resource caching

# note that the time needs to be put in a variable, otherwise it will not be valid
var staticCache = require('koa-static-cache')
const cacheTime = 365 * 24 * 60 * 60
app.use(staticCache(path.join(__dirname, 'public'), {
  maxAge: cacheTime
}))
Copy the code

Transfer file compression

var compress = require('koa-compress')
app.use(compress({
  filter: function (contentType) {
    return /text/i.test(contentType)
  },
  threshold: 2048,
  flush: require('zlib').Z_SYNC_FLUSH
}))
Copy the code

Mongoose uses the pit it comes across

When constructing a table, pay attention to the data type. If the schema is defined as Number but stored as String, an error will be reported. User.find ({age: ’18’}); user.find({age: ’18’});

Moment fixes mongodb time zone issues

Mongodb uses the time in the middle zone, we are in the East 8 zone, so the time is always 8 hours late. Using the moment plugin to process the moment is used on the client, not stored. Since moment is used in many places, I put it directly into the prototype of Vue so that all Vue instances can get this method

#main.js
import Vue from 'vue'
import moment from 'moment'
Object.defineProperty(Vue.prototype, '$moment', {value: moment})

# component
<p class='fr date'>{{$moment(item.commentDate).format('YYYY-MM-DD HH:mm:ss')}}</p>
Copy the code

other

Transmission data format conversion

Post transmits data in X-www-form-urlencoded format, but the form has an array of objects:

{
  userId: 13546515,
  dishs: [{id: 4545, num: 2, price: 12}, {id: 1446, num: 1, price: 8}],
  ...
}
Copy the code

I use Node as the background and have no problem with this data, but the team and Java background can not pass object array strings like this. He needs to wrap it directly with a List<> and pass it that way

  let dishs = {}
  _dishs.forEach((item, index) => {
    for (let key in item) {
if (! dishs[`dishs[${index}]`]) {
        dishs[`dishs[${index}]`] = {}
      }
      dishs[`dishs[${index}]`][key] = item[key]
    }
  })
  let temp = {}
  let result = {}
  for (let i in dishs) {
    for (let j in dishs[i]) {
if (! temp[i]) temp[i] = {}
      result[`${i}.${j}`] = dishs[i][j]
    }
  }

Copy the code

Mongodb Import data

The server/db_vue path is the exported data that can be imported to your own mongodb database

# d is the database name and c is the collection name
mongoimport -d vue -c users --file vue/users.json
Copy the code

conclusion

Generally speaking, the project structure is fairly clear, but I am not very familiar with Vue, so I am not very good at using it. For example, WHEN I use Vuex, I store the data that needs to be shared by different components in store or store it locally at the same time. For the data in a single component, I feel it is not necessary to store it in store. I am not very familiar with KOA2, so I always forget to await and other small problems at the beginning. Although I have done the cache compression after writing, because it is not a back-end, the performance is still not good, because the online server may be quite stuck, because it is a student server. After writing this, I am more familiar with Vue. Next, I will continue to learn the principle of Vue and learn new things. The above is the summary of the project, please correct me if there is any mistake