vue使用elementUI组件提交表单(带图片)到node后台

发布于 2021-09-16  2,160 次阅读


1、方法一(图片与表单分开,请求2次)

1.1 前台代码

// elementUI表单
<el-form ref="form" class="forms" :model="form">
            <div class="title">
              <el-input
                type="text"
                placeholder="请在这里输入标题"
                v-model="form.formtitle"
                maxlength="48"
                show-word-limit
              >
              </el-input>
            </div>
            <div class="con">
              <div class="toolbar clearfix">
                <div class="toolbar-tip"></div>
                <ul class="toolbar-group">
                  <el-upload
                    action="http://localhost:5000/newtravel/upload"
                    :before-upload="beforeupload"
                    :on-change="handleChange"
                    :show-file-list="false"
                    :data="form"
                  >
                    <li>图片</li>
                  </el-upload>
                </ul>
              </div>
              <div class="inputcon">
                <img v-if="form.imageUrl" :src="form.imageUrl" class="avatar" />
                <el-input
                  type="textarea"
                  :rows="5"
                  placeholder="请输入内容"
                  v-model="form.formconent"
                >
                </el-input>
              </div>
              <div class="btns">
                <div class="user_accept">
                  <el-checkbox v-model="isaccept"
                    >我已阅读并同意
                    <a href="" title="《马蜂窝游记协议》">《马蜂窝游记协议》</a>
                  </el-checkbox>
                </div>
                <el-button type="submit" class="btn_submit" @click="onSumbit"
                  >发表</el-button
                >
                <span class="cont">或者</span>
                <span class="btn_save">保存草稿</span>
                <div class="sync">
                  <span>同步到:</span>
                  <ul class="clearfix">
                    <li>
                      <a href="javascript:void(0);" title="微博"></a>
                    </li>
                    <li>
                      <a href="javascript:void(0);" title="空间"></a>
                    </li>
                    <li>
                      <a href="javascript:void(0);" title="人人"></a>
                    </li>
                    <li>
                      <a href="javascript:void(0);" title="腾讯"></a>
                    </li>
                  </ul>
                </div>
              </div>
            </div>
          </el-form>
// elementUI表单方法:
data() {
    return {
      form: {
        formtitle: "",
        formconent: "",
        imageUrl: "",
      },
    };
  },
  methods: {
    handleChange(file) {
      if (file) {
        this.form.imageUrl = URL.createObjectURL(file.raw);
      }
    },
    beforeupload(file) {
      const isImage = file.type.includes("image");
      if (!isImage) {
        this.$message.error("上传文件类型必须是图片!");
      }
      const isLt2M = file.size / 1024 / 1024 < 2;
      if (!isLt2M) {
        this.$message.error("上传图片大小不能超过 2MB!");
      }
      return isImage && isLt2M;
    },
    onSumbit() {
      function settime(time = +new Date()) {
        var date = new Date(time + 8 * 3600 * 1000);
        return date.toJSON().substr(0, 19).replace("T", " ");
      }
      let formData = {
        formtitle: this.form.formtitle,
        formconent: this.form.formconent,
        createtime: settime()
      };
      this.$axios.post("/newtravel", formData).then((res) => {
        console.log(res);
      });
    },
  },

1.2 node 后台代码

// app.js
const express = require('express');
const path = require("path");
const travelnotes_query = require('./query/travelnotes_query');
const app = express();
app.all('*', function (req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
    res.header("X-Powered-By", ' 3.2.1')
    //方便返回json
    res.header("Content-Type", "application/json;charset=utf-8");
    if (req.method == 'OPTIONS') {
        //让options请求快速返回
        res.sendStatus(200);
    } else {
        next();
    }
});

app.use(express.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, 'static')));

// 定义写游记接口
app.post('/newtravel', (req, res, next) => {
    let data = req.body
    travelnotes_query.addNewTravelData(data, function (rec) {
        res.json(rec).end();
    });
})

// 定义写游记上传图片接口
var multer = require('multer')
var fs = require('fs')
const upload = multer({ dest: __dirname + '/static/upload' }) // 定义图片文件存储位置
app.post('/newtravel/upload', upload.single('file'), async (req, res) => {
    let file = req.file;
    let basePath = __dirname + '/static/upload/'; // 设置初始路径
    let oldFileName = file.filename // 获取文件名
    let newFileName = Date.now() + '.png' // 将文件名设置成时间戳png格式
    let filePath = basePath + oldFileName; // 源文件路径
    let newFilePath = basePath + newFileName; // 新文件路径
    if (fs.existsSync(basePath)) { // 判断该目录是否存在,若存在执行文件重命名操作
        fs.rename(filePath, newFilePath, (err) => {
            if (err) throw err
        })
    }
    let imgurl = 'static/upload/' + newFileName // 设置文件相对路径用于存入数据库
    let data2 = {
        imgurl: imgurl
    }
    travelnotes_query.addNewTravelImage(data2, function (rec) {
        res.json(rec).end();
    });
})

const port = process.env.PORT || 5000;
app.listen(port, function () { console.log(`监听端口: ${port}`) });

// travelnotes_query.js
const query = require("../dbconn.js");
// 写游记添加数据
exports.addNewTravelData = function (data, callback) {
  console.log('获取的数据:', data);
  var arr = [];
  arr.push(data.formtitle);
  arr.push(data.formconent);
  arr.push(data.createtime);
  query('INSERT INTO `travelnotes` (formtitle,formconent,createtime) VALUES (?,?,?)', arr, function (err, results, fields) {
    if (err) throw err;
    callback(results);
  });
}

// 写游记添加图片
exports.addNewTravelImage = function (data, callback) {
  console.log('获取的数据:', data);
  var arr = [];
  arr.push(data.imgurl);
  query('INSERT INTO `travelnotes` (imgurl) VALUES (?)', arr, function (err, results, fields) {
    if (err) throw err;
    callback(results);
  });
}

// dbconn.js
//定义连接池
const mysql = require('mysql2');
const pool = mysql.createPool({
  host: '*****',
  user: '*****',
  password: '*****',
  database: '*****',
  dateStrings: true,
  waitForConnections: true,
  connectionLimit: 50,
  queueLimit: 0
});
//定义通用的查询接口并将其导出
var query=function(sql,values,callback){
    pool.getConnection(function(err,conn){
        if(err){
            console.log(err);
            callback(err,null,null);
        }else{
            conn.query(sql,values,function(qerr,results,fields){
                //释放连接
                conn.release();
                //事件驱动回调
                callback(qerr,results,fields);
            });
        }
    });
}; 
module.exports=query;

2、方法二(把表单放在图片里,请求1次)

2.1 vue 前台

// vue 的dom内容
<el-form ref="form" class="forms" :model="form">
            <div class="title">
              <el-input
                type="text"
                placeholder="请在这里输入标题"
                v-model="form.formtitle"
                maxlength="48"
                show-word-limit
              >
              </el-input>
            </div>
            <div class="con">
              <div class="toolbar clearfix">
                <div class="toolbar-tip"></div>
                <ul class="toolbar-group">
                  <el-upload
                    ref="upload"
                    action="http://localhost:5000/newtravel"
                    :auto-upload="false"
                    :before-upload="beforeupload"
                    :on-change="handleChange"
                    :show-file-list="false"
                    :data="form"
                  >
                    <li>图片</li>
                  </el-upload>
                </ul>
              </div>
              <div class="inputcon">
                <img v-if="form.imageUrl" :src="form.imageUrl" class="avatar" />
                <el-input
                  type="textarea"
                  :rows="5"
                  placeholder="请输入内容"
                  v-model="form.formconent"
                >
                </el-input>
              </div>
              <div class="btns">
                <div class="user_accept">
                  <el-checkbox v-model="isaccept"
                    >我已阅读并同意
                    <a href="" title="《马蜂窝游记协议》">《马蜂窝游记协议》</a>
                  </el-checkbox>
                </div>
                <el-button type="submit" class="btn_submit" @click="onSumbit('form')"
                  >发表</el-button
                >
                <span class="cont">或者</span>
                <span class="btn_save">保存草稿</span>
                <div class="sync">
                  <span>同步到:</span>
                  <ul class="clearfix">
                    <li>
                      <a href="javascript:void(0);" title="微博"></a>
                    </li>
                    <li>
                      <a href="javascript:void(0);" title="空间"></a>
                    </li>
                    <li>
                      <a href="javascript:void(0);" title="人人"></a>
                    </li>
                    <li>
                      <a href="javascript:void(0);" title="腾讯"></a>
                    </li>
                  </ul>
                </div>
              </div>
            </div>
          </el-form>

// vue 的data和methods
data() {
    return {
      form: {
        formtitle: "",
        formconent: "",
        imageUrl: "",
      },
      uploadFile: [],
    };
  },
  methods: {
    handleChange(file) {
      if (file) {
        this.form.imageUrl = URL.createObjectURL(file.raw);
        this.uploadFile.push(file.raw);
      }
    },
    beforeupload(file) {
      const isImage = file.type.includes("image");
      if (!isImage) {
        this.$message.error("上传文件类型必须是图片!");
      }
      const isLt2M = file.size / 1024 / 1024 < 2;
      if (!isLt2M) {
        this.$message.error("上传图片大小不能超过 2MB!");
      }
      return isImage && isLt2M;
    },
    onSumbit(form) {
      let vm = this;
      this.$refs[form].validate((valid)=>{
        if(valid){
          vm.$refs.upload.submit();
        }else{
          return false
        }
      })
    },
  },

2.2 node 后台

//app.js
// 定义写游记接口
var multer = require('multer')
var fs = require('fs')
const upload = multer({ dest: __dirname + '/static/upload' })
app.post('/newtravel', upload.single('file'), async (req, res) => {
    let file = req.file;
    let basePath = __dirname + '/static/upload/';
    let oldFileName = file.filename
    let newFileName = Date.now() + '.png'
    let filePath = basePath + oldFileName;
    let newFilePath = basePath + newFileName;
    if (fs.existsSync(basePath)) {
        fs.rename(filePath, newFilePath, (err) => {
            if (err) throw err
        })
    }
    let imgurl = 'static/upload/' + newFileName
    function settime(time = +new Date()) {
        var date = new Date(time + 8 * 3600 * 1000);
        return date.toJSON().substr(0, 19).replace("T", " ");
    }
    let formdata = {
        imgurl: imgurl,
        formtitle: req.body.formtitle,
        formconent: req.body.formconent,
        formtitle: req.body.formtitle,
        createtime: settime()
    }
    travelnotes_query.addNewTravelNote(formdata, function (rec) {
        res.json(rec).end();
    });
})

//travelnotes_query.js
exports.addNewTravelNote = function (data, callback) {
  var arr = [];
  arr.push(data.formtitle);
  arr.push(data.imgurl);
  arr.push(data.formconent);
  arr.push(data.createtime);
  query('INSERT INTO `travelnotes` (formtitle,imgurl,formconent,createtime) VALUES (?,?,?,?)', arr, function (err, results, fields) {
    if (err) throw err;
    callback(results);
  });
}

其他的内容一样

3、效果预览

效果预览


活的像诗一样