Koa & Mongoose & Vue實現先後端分離--10更新用戶信息

上節回顧

  • JWT相關

工做內容

  • 更新用戶數據

準備工做

css

業務邏輯

頁面調用用戶信息

//更新文件:client/src/views/personal-panel/index.vue
...
<script>
import http from '@/utils/http'

export default {
  data () {
    return {
      loginer: {}
    }
  },
  async created () {
    const { id } = this.$store.state.loginer
    const res = await http.get(`/users/${id}`)
    this.loginer = res.data
  }
}
</script>

在登陸的時候,已經將登陸用戶信息存儲到vuex中,經過用戶ID查詢用戶詳細信息。html

服務端查詢用戶服務

// 更新文件: server/router/users.js
...
const { list, get, register, login, update } = require('../control/users');
...
  {
    path: '/:id',
    method: 'GET',
    handle: get
  },
...
// 更新文件:server/control/users.js
async function get (ctx) {
  const { id } = ctx.params;
  try {
    const user = await userModel.findOne({
      _id: id
    }).select('+telephone +email'); //新增查詢除默認外的字段
    if (user) {
      ctx.body = {
        code: '200',
        data: user,
        msg: '成功'
      }
      return;
    }
    throw('請覈對參數')
  } catch (err) {
    ctx.body = {
      code: '403',
      data: null,
      msg: err.message
    }
  }
}
...
module.exports = {
...
  get,
...
}
  • 其中,userModel.findOne({_id: id})默認查出的是Schema沒有設置select: false的字段,若是須要其它字段的話,須要userModel.findOne({_id: id}).select('+telephone +email');新增字段
  • 經過ctx.params獲取匹配路由:id參數。

前端展現佈局

// 更新文件:client/src/views/personal-panel/index.vue
<template>
  <div class="panel-container">
    <div class="panel-avatar">
      <el-upload
        class="avatar-uploader"
        v-bind="uploadForm">
        <img v-if="imageUrl" :src="imageUrl" class="avatar">
        <i v-else class="el-icon-user"></i>
      </el-upload>
    </div>
    <div class="panel-main">
      <h3 class="panel-title">{{loginer.alias || loginer.account}}</h3>
      <ul class="panel-content">
        <li class="panel-item">
          <label class="panel-item-lanel">賬號:</label>
          <span class="panel-item-value">{{loginer.account}}</span>
        </li>
        <li class="panel-item">
          <label class="panel-item-lanel">郵箱:</label>
          <span class="panel-item-value">{{loginer.email}}</span>
        </li>
        <li class="panel-item">
          <label class="panel-item-lanel">電話:</label>
          <span class="panel-item-value">{{loginer.telephone}}</span>
        </li>
      </ul>
      <div class="panel-controls">
        <el-button class="btn-edit" type="default" @click="openFormDialog">編輯</el-button>
      </div>
    </div>
  </div>
</template>
<script>
import http from '@/utils/http'

export default {
  methods: {
    openFormDialog () {

    }
  },
  data () {
    return {
      uploadForm: {
        action: ''
      },
      imageUrl: '',
      loginer: {}
    }
  },
  async created () {
    const { id } = this.$store.state.loginer
    const res = await http.get(`/users/${id}`)
    this.loginer = res.data
  }
}
</script>
<style lang="scss" scoped>
@import '~@/stylesheets/layout.scss';
@import './index.scss';
</style>
// 新建文件:client/src/views/personal-panel/index.scss
.panel-container {
  @include flex($item: flex-start);

  .panel-avatar {
    width: 160px;
  }
  .panel-main {
    flex: 1;
    color: #525975;
    line-height: 2;
    padding: 0 40px;
    font-size: 14px;

    .panel-title {
      color: #2e3242;
      font-size: 24px;
    }
  }
  .panel-controls {
    margin-top: 20px;
  }
}
.avatar-uploader{
  /deep/ {
    .el-upload {
      border: 1px dashed #d9d9d9;
      border-radius: 100px;
      cursor: pointer;
      position: relative;
      overflow: hidden;
    }

    .el-upload:hover {
      border-color: #409EFF;
    }

    .el-icon-user {
      font-size: 70px;
      color: #8c939d;
      width: 160px;
      height: 160px;
      line-height: 160px;
      text-align: center;
    }

    .avatar {
      width: 160px;
      height: 160px;
      display: block;
    }
  }
}

展現效果
image.png前端

前端編輯用戶信息

// 更新文件:client/src/views/personal-panel/index.vue
<template>
  <div class="panel-container">
    <div class="panel-avatar">
      <el-upload
        class="avatar-uploader"
        v-bind="uploadForm">
        <img v-if="imageUrl" :src="imageUrl" class="avatar">
        <i v-else class="el-icon-user"></i>
      </el-upload>
    </div>
    <div class="panel-main">
      <h3 class="panel-title">{{loginer.alias || loginer.account}}</h3>
      <ul class="panel-content">
        <li class="panel-item">
          <label class="panel-item-lanel">賬號:</label>
          <span class="panel-item-value">{{loginer.account}}</span>
        </li>
        <li class="panel-item">
          <label class="panel-item-lanel">郵箱:</label>
          <span class="panel-item-value">{{loginer.email}}</span>
        </li>
        <li class="panel-item">
          <label class="panel-item-lanel">電話:</label>
          <span class="panel-item-value">{{loginer.telephone}}</span>
        </li>
      </ul>
      <div class="panel-controls">
        <el-button class="btn-edit" type="default" @click="openFormDialog">編輯</el-button>
      </div>
    </div>
    <el-dialog :title="dialogForm.title" :visible.sync="dialogForm.visible">
      <el-form :ref="dialogForm.formRef" :model="dialogForm.form" :rules="dialogForm.rules">
        <el-form-item label="賬號" prop="account">
          <el-input v-model="dialogForm.form.account"></el-input>
        </el-form-item>
        <el-form-item label="暱稱" prop="alias">
          <el-input v-model="dialogForm.form.alias"></el-input>
        </el-form-item>
        <el-form-item label="郵箱" prop="email">
          <el-input type="email" v-model="dialogForm.form.email"></el-input>
        </el-form-item>
        <el-form-item label="電話" prop="telephone">
          <el-input type="telephone" v-model="dialogForm.form.telephone"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogForm.visible = false">取 消</el-button>
        <el-button type="primary" @click="submitForm">確 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import http from '@/utils/http'

export default {
  methods: {
    openFormDialog () {
      this.dialogForm.visible = true
    },
    async submitForm () {
      try {
        const valid = await this.$refs[this.dialogForm.formRef].validate()
        if (valid) {
          const res = await http.patch(
            `/users/${this.userId}`,
            {
              ...this.dialogForm.form
            }
          )
          if (res.code === '200') {
            const { account, alias } = res.data
            this.$store.commit(
              'putLoginer',
              {
                account,
                alias
              }
            ) // 更新vuex狀態
            this.loginer = Object.assign(
              {},
              this.loginer,
              {
                ...res.data
              }
            ) // 更新展現的默認值
            this.$set(this.dialogForm, 'form', Object.assign(
              {},
              this.dialogForm.form,
              {
                ...res.data
              }
            )) // 更新彈窗默認值
            this.dialogForm.visible = false
          } else {
            this.$message({
              type: 'error',
              message: res.msg
            })
          }
        }
      } catch (err) {
        console.error(err)
      }
    }
  },
  data () {
    return {
      uploadForm: {
        action: ''
      },
      imageUrl: '',
      loginer: {},
      dialogForm: {
        visible: false,
        title: '編輯',
        formRef: 'dialogForm',
        form: {
          account: '',
          alias: '',
          email: '',
          telephone: ''
        },
        rules: {
          account: [
            { required: true, message: '請輸入賬號', trigger: 'blur' },
            { min: 5, message: '長度至少5個字符', trigger: 'blur' }
          ],
          alias: [
            { required: true, message: '請輸入暱稱', trigger: 'blur' },
            { min: 1, max: 15, message: '長度不能超過十五個字符', trigger: 'blur'}
          ],
          email: [
            { type: 'email', required: true, message: '請輸入郵箱', trigger: 'blur' }
          ],
          telephone: [
            { required: true, message: '請輸入手機號碼', trigger: 'blur' },
            {
              validator: (rule, value, cb) => {
                if (/^1[0-9]{10}$/.test(value)) {
                  cb()
                } else {
                  cb(new Error('手機號碼格式錯誤'))
                }
              },
              trigger: 'blur'
            }
          ]
        }
      }
    }
  },
  computed: {
    userId () {
      return this.$store.state.loginer.id
    }
  },
  async created () {
    // 調用完初始化接口時,須要賦展現初始值
    const res = await http.get(`/users/${this.userId}`)
    if (res.code === '200') {
      this.loginer = res.data
      this.dialogForm.form = {...res.data}
      this.imageUrl = res.data.avatar
    } else {
      this.$message({
        type: 'error',
        message: '獲取用戶信息失敗'
      })
    }
  }
}
</script>
  • get方法調用/users/:id接口後,須要給用戶設初始值。
  • patch方法調用/users/:id接口,更新用戶信息後,要更新vuex存儲的狀態,更新展現的用戶信息,更新彈窗初始值。

展現效果:
image.pngvue

服務端更新用戶服務

// 更新文件:server/control/users.js
...
async function update (ctx) {
  const { id } = ctx.params;
  const payload = ctx.request.body;
  try {
    await userModel.updateOne(
      {
        _id: id
      },
      {
        ...payload
      }
    );
    ctx.body = {
      code: '200',
      data: {
        id,
        ...payload
      },
      msg: '更新成功'
    }
  } catch (err) {
    ctx.body = {
      code: '403',
      data: null,
      msg: '請覈查參數'
    }
  }
}
...

測試結果:
update.gifvuex

參考文檔

Model操做api

相關文章
相關標籤/搜索