vue同后端搭建

16 阅读11分钟

本项目使用vscode作为主要工具,navicat作为数据库软件,使用vue
请确保你安装了前置文件!!!!!!!

一。开始项目搭建
1.创建一个文件夹(我命名为mangerment),在其(mangerment)下面新建一个文件夹,即后端文件夹,命名为server。
(前端文件夹在 九,会在集成终端中使用《npm create vite@latest web -- --template vue》)
选择server,右键,在集成终端中打开,npm init -y,进行项目的初始化。
2.接着,按照所需,安装对应的插件。(mac用户前缀sudo,win不用)

cnpm i express cors body-parser sequelize mysql2 <br>

共五个express(基于node.js的Web 应用程序开发框架,),cors(解决跨域请求),body-parser(处理中间件函数),sequelize(把关系型数据库表结构映射为javascript对象),mysql2(数据库管理系统)

打开package.js,可以看到main:index.js,这个就是我们的主文件。在server文件下创建index.js。 (可以更改为其他名字,但要一一对应)

附录:nodemon的安装 npm install -g nodemon(应该不用了)

二。index

在index.js中

const express = require('express')//导入express模块

//const是JavaScript中用于声明常量的关键字,常量是一个不能重新分配的变量。 //require是Node.js提供的一个函数,用于包含在单独文件中定义的模块。在这种情况下,它被用来导入express模块。
//express是Node.js的一个流行的web应用程序框架。它是为构建web应用程序和API而设计的。当与Node.js一起使用时,它有助于创建web服务器,并简化处理HTTP请求、定义路由和执行其他与web相关的任务的过程。

////在这一行中,模块express(第二个)被分配给一个常量变量express(第一个),这意味着express变量以后不能在同一范围内重新分配或声明。

const app = express()//创建  app实例(express应用程序实例)

//调用express框架中的函数,用于创建一个 Express 应用程序对象。
在这里,express 是一个指向 Express 模块所暴露出的函数的引用。因此,express 在这里既代表 Express 框架(因为你之前导入了 Express 模块),也代表这个框架中的一个函数。

////在这一行中,模块express被分配给一个常量变量app,这意味着express变量以后不能在同一范围内重新分配或声明。

app.listen(3000, () => {
    console.log('服务成功启动在 http://localhost:3000')
})

//app.listen函数用于绑定和侦听指定主机和端口上的连接(现在这个端口是3000),若成功,则log打印,

//箭头函数省去了function关键字,采用箭头=>来定义函数。函数的参数放在=>前面的括号中,函数体跟在=>后的花括号中。

//箭头函数没有自己的this,它会捕获自己在定义时(注意,是定义时,不是调用时)所处的外层执行环境的this,并继承这个this值。
所以,箭头函数中this的指向在它被定义的时候就已经确定了,之后永远不会改变。箭头函数没有自己的arguments对象。在箭头函数中访问arguments实际上获得的是外层局部(函数)执行环境中的值。

在集成终端中启动服务

nodemon .\index.js

//nodemon可在检测到目录中的文件更改时通过自动重新启动节点应用程序来帮助开发基于 node.js 的应用程序。

导入cors

const cors = require('cors')

使用cors

app.use(cors())

//app.use()方法将指定的中间件功能放到指定的路径下,当请求的路径地址(前端发过来的地址)与定义的路由(app.get/post)相同时,就会执行指定的中间件功能。

引入bodyParser

const bodyParser = require('body-parser')

三。数据库 在navicat中新建一个数据库

四。database

在server文件夹下新建一个文件夹config,新建文件database.js

const { Sequelize, DataTypes, Op } = require("sequelize")

//引入sequelize中的 Sequelize类, DataTypes数据类型, Op运算符合辑

const sequelize = new Sequelize('denglu', 'root', '12345678', {//'denglu’数据库名称,'root’数据库用户名,'12345678’密码
    host: 'localhost',//主机名(数据库服务器IP)
    dialect: 'mysql',//表示使用mysql数据库(驱动)
    port: 3306,//端口号
    logging: (sql) => { console.log(sql); }, //Sequelize执行的SQL语句输出到控制台
    dialectOptions: { logQueryParameters: true, },//// 显示实际参数值
    timezone: '+08:00', //设置为中国的时区(北京时间,UTC+8)
});

// 数据库提示信息,对于sequelize连接数据库测试连接是否正常

sequelize.authenticate()//对于sequelize连接数据库测试连接是否正常
    .then(() => {
        console.log('数据库连接成功')
    })
    .catch((err) => {
        console.log('数据库连接失败', err)
    });

// 导出实例

module.exports = { sequelize, DataTypes, Op }

五。userModel.js 在server下新建models文件夹,创建文件userModel.js

const { sequelize, DataTypes, Op } = require("../config/database")

/|\

const User = sequelize.define('t_user', {
    id: {
        type: DataTypes.INTEGER,
        primaryKey: true,
        autoIncrement: true,
        allowNull: false,
        comment: "主键"
    },
    username: {
        type: DataTypes.STRING(255),
        allowNull: true,
        defaultValue: null
    },
    password: {
        type: DataTypes.STRING(255),
        allowNull: true,
        defaultValue: null
    },
    avatar: {
        type: DataTypes.STRING(255),
        allowNull: true,
        defaultValue: null
    },
    sex: {
        type: DataTypes.TINYINT,
        allowNull: true,
        defaultValue: null,
        defaultValue: 1
    },
    city: {
        type: DataTypes.STRING(255),
        allowNull: true,
        defaultValue: null
    },
    introduce: {
       type: DataTypes.TEXT,
       allowNull: true,
       defaultValue: null
    }
}, {
    tableName: 't_user',
    timestamps: false,
})

module.exports = User

/|\

userRouter

新建routers文件夹,在其下面创建一个userRouter.js的文件夹

const express = require('express')	        // 1. 导入 express
const router = express.Router()			    // 2. 创建路由对象
const User = require('../models/userModel') //3.导入模型
const bodyParser = require('body-parser')   //配置post请求参数挂载在body上
const jsonParser = bodyParser.json()        //处理content-type是json的请求上

//列表查询
router.get('/list', (req, res) => {
    User.findAll().then(data => {//User
        res.send({
            code: 0,
            msg: "操作成功",
            data: data
        })
    }).catch((error) => {
        //  失败
        res.send({
            code: -1,
            msg: "操作出错",
            detail: error
        })
    })
})

//添加
router.post('/add', jsonParser, (req, res) => {
    User.create(req.body).then(data => {//User
        res.send({
            code: 0,
            msg: "操作成功",
            data: data
        })
    }).catch((error) => {
        //  失败
        res.send({
            code: -1,
            msg: "操作出错",
            detail: error
        })
    })
})

// 修改
router.post('/update', jsonParser, (req, res) => {
    User.update(req.body, {//User
        where: {
            id: req.body.id
        }
    }).then(() => {
        res.send({
            code: 0,
            msg: "更新成功"
        })
    }).catch((error) => {
        res.send({
            code: -1,
            msg: "更新失败",
            detail: error
        })
    })
})

//删除
router.get('/delete', jsonParser, (req, res) => {
    User.destroy({//User
        where: {
            id: req.query.id
        }
    }).then(() => {
        res.send({
            code: 0,
            msg: '删除成功'
        })
    }).catch((error) => {
        res.send({
            code: -1,
            msg: '删除失败',
            detail: error
        })
    })
})

// 4. 向外导出路由对象
module.exports = router//导出router

六。回到主函数index.js

导入路由并使用

const userRouter = require('./routers/userRouter')
//此行在app.use前
//此行在app.use后
app.use('/user',userRouter)

七。测试一下 右键server文件夹,在集成终端中打开,nodemon index.js
同时显示
1.服务成功启动在 http://localhost:3000
2.数据库连接成功 即证明完成

在浏览器中输入http://127.0.0.1:3000/user/list(注意标点需要英文),若能出现数据库中数据,即请求成功

(发出的信息主要是database.js,userModel.js,userRouter.js,index.js,组成的)!!!!! 1.database.js是找哪个数据库,并且登录 2.userModel.js主要是数据库中字段的解析,标记了具体是哪个表 3.userRouter.js主要是增删改查的功能 4.index.js主要是传出数据到端口

八。前端web开始(我将使用element plus去加快速度,可能你不喜欢这个组件?但你应该要知道如何使用,算是加熟练度)
1.右键mangerment文件夹(主文件夹),在集成终端中打开

安装vue cli (npm install -g @vue/cli)

输入 npm create vite@latest web -- --template vue,即在mangerment文件夹下用指令添加一个web文件夹,(指令中的web是文件名)(是vue vli中的指令)

右键web文件夹,cnpm i安装依赖

/|\

element plus

使用组件首先要进行引入,使用包管理器或者浏览器直接映入皆可 包管理器 element-plus.org/zh-CN/guide…

使用浏览器直接引入 element-plus.org/zh-CN/guide…

在web的集成终端中
引入element plus(这里是包管理器的方式)

npm install element-plus --save

呃呃呃,还是用cnpm吧

cnpm install element-plus --save(简单理解为加速即可)

/|\

axios

在web的集成终端中 再使用一下axios,( Axios是一个基于promise的HTTP库(类似于jQuery的Ajax,用于HTTP请求)

  • 可以用于浏览器和node.js(既可以用于客户端也可以用于node.js编写的服务端))
cnpm install axios

/|\

启动前端项目

web

在web的集成终端中

npm run dev

不知道你们是不是这个网址呢,(不过这个不重要,知道就行) http://localhost:5173/ 复制到浏览器打开

在浏览器页面中看到# Vite + Vue页面即为成功

/|\

修改vue主页面
删除web下src下App.vue中所有代码
将之替换为

<template>
  
</template>

<script setup>
import { ref, reactive } from 'vue'
</script>

<style scoped>

</style>

删除components下Helloworld.vue文件

将web下src下的main.js中css的引入删除

此时浏览器页面为空白即为成功

(想确保无问题的话,也可以在app.vue的template中写入字符asd(此处随意),在页面中输出,即为完成)

/|\

在element plus网站中点击组件,

在basic基础组件中选择icon图标,安装它的包管理器(等待完成)

cnpm install @element-plus/icons-vue

打开main.js,在import下写入

import * as ElementPlusIconsVue from '@element-plus/icons-vue'

const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}

此时配置完成icon

/|\

在element plus网站中点击指南,选择快速开始 复制

import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

到main.js中,import下面

复制(假的)

app.use(ElementPlus)

到app下面

此时完成配置

/|\

在main.js中

npm run dev重新启动

在navigation 导航中点击菜单,下翻,选择 侧栏

右下角复制代码,点击app.vue,将其中代码删除,粘贴(报错:找不到模块为正常现象)

最后main.js中的代码

import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import App from './App.vue'

const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}

app.use(ElementPlus).mount('#app')

此时页面中将显示element plus中侧栏的页面

/|\

删除第二个el-col(右页面),第一个el-col中span改为6(个人习惯审美)

/|\

在web下src下component下创建HeaderArea.vue(头部区域)和MenuArea.vue(页面区域)

将原来的app.vue拆分

将app.vue中script中内容迁移到MenuArea.vue处,删除报错的四个:string和:string[],

将HeaderArea.vue和MenuArea.vue引入到app.vue当中

 import HeaderArea from './components/HeaderArea.vue';
 import MenuArea from './components/MenuArea.vue';

等-等-等-等————————

此时的三个界面

//app.vue

<template>
      <HeaderArea/>
  <el-row class="tac">
    <el-col :span="6">
      <MenuArea/>
    </el-col>
    <el-col :span="18">
      hello
    </el-col>
  </el-row>
</template>

<script setup>
  import HeaderArea from './components/HeaderArea.vue'
  import MenuArea from './components/MenuArea.vue'
</script>

//HeaderArea.vue

<template>
    <h5 class="mb-2">Default colors</h5>
</template>

<script setup>
import { ref, reactive } from 'vue'
</script>

<style scoped></style>

//MenuArea.vue

<template>
    <el-menu default-active="2" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose">
        <el-sub-menu index="1">
            <template #title>
                <el-icon>
                    <location />
                </el-icon>
                <span>Navigator One</span>
            </template>
            <el-menu-item-group title="Group One">
                <el-menu-item index="1-1">item one</el-menu-item>
                <el-menu-item index="1-2">item two</el-menu-item>
            </el-menu-item-group>
            <el-menu-item-group title="Group Two">
                <el-menu-item index="1-3">item three</el-menu-item>
            </el-menu-item-group>
            <el-sub-menu index="1-4">
                <template #title>item four</template>
                <el-menu-item index="1-4-1">item one</el-menu-item>
            </el-sub-menu>
        </el-sub-menu>
        <el-menu-item index="2">
            <el-icon><icon-menu /></el-icon>
            <span>Navigator Two</span>
        </el-menu-item>
        <el-menu-item index="3" disabled>
            <el-icon>
                <document />
            </el-icon>
            <span>Navigator Three</span>
        </el-menu-item>
        <el-menu-item index="4">
            <el-icon>
                <setting />
            </el-icon>
            <span>Navigator Four</span>
        </el-menu-item>
    </el-menu>
</template>

<script setup>
import { ref, reactive } from 'vue'
import {
    Document,
    Menu as IconMenu,
    Location,
    Setting,
} from '@element-plus/icons-vue'
const handleOpen = (key, keyPath) => {
    console.log(key, keyPath)
}
const handleClose = (key, keyPath) => {
    console.log(key, keyPath)
}
</script>

<style scoped></style>

/|\

此时页面将重新出现 (以上全为页面搭建!!!) /|\

在element plus下basic下button处,挑选一个button Primary

在element plus下Data数据展示下table表格,选择一个基础表格即可 将table表格中数据(script中tableData)放到app.vue中的script中

此时网站页面当中会显示数据

/|\

在element plus下Data数据展示下pagination中找一个分页

<el-pagination background layout="prev, pager, next" :total="1000" />

放到table下

页面完成

app.vue

<template>
  <!-- <HeaderArea /> -->
  <el-row class="tac">
    <el-col :span="6">
      <MenuArea />
    </el-col>
    <el-col :span="18" style="padding-left: 20px;">
      <!-- 页面主体,表格之类的 -->
      <el-button type="primary">Primary</el-button>

      <el-table :data="tableData" style="width: 100%">
        <el-table-column prop="date" label="Date" width="180" />
        <el-table-column prop="name" label="Name" width="180" />
        <el-table-column prop="address" label="Address" />
      </el-table>

      <el-pagination background layout="prev, pager, next" :total="1000" />
    </el-col>
  </el-row>
</template>

<script setup>
import HeaderArea from './components/HeaderArea.vue'
import MenuArea from './components/MenuArea.vue'

const tableData = [
  {
    date: '2016-05-03',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-02',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
]
</script>

headerArea.vue
<template>
    <h5 class="mb-2">Default colors</h5>
</template>

<script setup>
import { ref, reactive } from 'vue'
</script>

<style scoped></style>

menuArea.vue
<template>
    <el-menu default-active="2" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose">
        <el-sub-menu index="1">
            <template #title>
                <el-icon>
                    <location />
                </el-icon>
                <span>Navigator One</span>
            </template>
            <el-menu-item-group title="Group One">
                <el-menu-item index="1-1">item one</el-menu-item>
                <el-menu-item index="1-2">item two</el-menu-item>
            </el-menu-item-group>
            <el-menu-item-group title="Group Two">
                <el-menu-item index="1-3">item three</el-menu-item>
            </el-menu-item-group>
            <el-sub-menu index="1-4">
                <template #title>item four</template>
                <el-menu-item index="1-4-1">item one</el-menu-item>
            </el-sub-menu>
        </el-sub-menu>
        <el-menu-item index="2">
            <el-icon><icon-menu /></el-icon>
            <span>Navigator Two</span>
        </el-menu-item>
        <el-menu-item index="3" disabled>
            <el-icon>
                <document />
            </el-icon>
            <span>Navigator Three</span>
        </el-menu-item>
        <el-menu-item index="4">
            <el-icon>
                <setting />
            </el-icon>
            <span>Navigator Four</span>
        </el-menu-item>
    </el-menu>
</template>

<script setup>
import { ref, reactive } from 'vue'
import {
    Document,
    Menu as IconMenu,
    Location,
    Setting,
} from '@element-plus/icons-vue'
const handleOpen = (key, keyPath) => {
    console.log(key, keyPath)
}
const handleClose = (key, keyPath) => {
    console.log(key, keyPath)
}
</script>

<style scoped></style>

/|
/|
/|\

开始连接 嗯,易知,table靠:data="tableData"同script下的tableData数据进行连接

于是将app.vue中的全部el-table-column修改为(此处是之前http://localhost:3000/user/list中数据库的相关修改)(还挑了个插槽和一个弹出框,不过脑子要坏了,先就这样吧)

<el-table-column prop="username" label="姓名" width="180" />
        <el-table-column prop="city" label="籍贯" width="180" />

        <el-table-column prop="sex" label="性别" width="180">
          <template #default="scope">
            {{ scope.row.sex == 1 ? '男' : '女' }}
          </template>
        </el-table-column>

        <el-table-column prop="avatar" label="头像" width="180">
          <template #default="scope">
            <el-avatar :size="50" :src="scope.row.avatar" />
          </template>
        </el-table-column>

        <el-table-column prop="introduce" label="简介" />

        <!-- 插槽 template(每行插入修改,删除) -->
        <el-table-column fixed="right" label="操作" width="120">
          <template #default="scope">
            <el-button link type="success" size="small" @click="handleClick">修改</el-button>
            <el-button link type="danger" size="small" @click="delUser(scope.row.id)">删除</el-button>
          </template>
        </el-table-column>



  <!-- 此处为弹窗 -->
  <el-dialog v-model="dialogFormVisible" title="新增用户">
    <el-form :model="userForm">

      <el-form-item label="姓名" :label-width="formLabelWidth">
        <el-input v-model="userForm.username" autocomplete="off" />
      </el-form-item>

      <el-form-item label="性别" :label-width="formLabelWidth">
        <el-radio-group v-model="userForm.sex" class="ml-4">
          <el-radio label="1" size="large"></el-radio>
          <el-radio label="0" size="large"></el-radio>
        </el-radio-group>
      </el-form-item>

      <el-form-item label="籍贯" :label-width="formLabelWidth">
        <el-select v-model="userForm.city" placeholder="请选择籍贯">
          <el-option label="合肥" value="合肥" />
          <el-option label="芜湖" value="芜湖" />
          <el-option label="上海" value="上海" />
          <el-option label="天津" value="天津" />
        </el-select>
      </el-form-item>

      <el-form-item label="简介">
        <el-input v-model="userForm.introduce" type="textarea" />
      </el-form-item>

      <el-form-item label="头像">
        <el-upload v-model:file-list="fileList" action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
          list-type="picture-card" :on-preview="handlePictureCardPreview" :on-remove="handleRemove">
          <el-icon>
            <Plus />
          </el-icon>
        </el-upload>
      </el-form-item>



    </el-form>
    <template #footer>

      <span class="dialog-footer">
        <el-button @click="dialogFormVisible = false">Cancel</el-button>
        <el-button type="primary" @click="dialogFormVisible = false">
          Confirm
        </el-button>
      </span>
    </template>

  </el-dialog>

/|
删除tableData中的数据,只保留const tableData = []

先引入axios import axios from 'axios'; 那么,请求数据库传递来的参数, function initData() { axios.get('http://localhost:3000/user/list') }

使用.then(res => { tableData = res.data.data console.log(res); })//获取响应

import { onMounted } from 'vue'

onMounted(() =>{ initData() }) //此时,已经可以获取到传递来的参数了,没有展示罢了

const 会报错,于是

import { ref,onMounted } from 'vue'

const tableData = ref([])

function initData() { axios.get('http://localhost:3000/user/list').then(res => { tableData.value = res.data.data console.log(res); }) }

检查服务是否启动

应该就没什么问题了

主要是数据获取,数据上传的问题