Vue + CPP项目

创建一个完整的企业级项目案例:一个简单的员工管理系统。前端使用Vue 3,后端使用C++(基于Drogon框架)。

项目结构

复制代码
employee-management/
├── frontend/                 # Vue前端
│   ├── src/
│   │   ├── components/
│   │   ├── views/
│   │   ├── api/
│   │   ├── App.vue
│   │   └── main.js
│   └── package.json
└── backend/                  # C++后端
    ├── src/
    │   ├── main.cpp
    │   ├── EmployeeController.h
    │   ├── EmployeeController.cpp
    │   ├── Employee.h
    │   └── Employee.cpp
    ├── CMakeLists.txt
    └── config.json

后端实现(C++ with Drogon)

1. CMakeLists.txt

cmake 复制代码
cmake_minimum_required(VERSION 3.16)
project(employee_backend)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Drogon REQUIRED)
find_package(jsoncpp REQUIRED)

add_executable(employee_server
    src/main.cpp
    src/Employee.cpp
    src/EmployeeController.cpp
)

target_link_libraries(employee_server
    Drogon::Drogon
    jsoncpp
)

2. config.json

json 复制代码
{
    "listeners": [
        {
            "address": "0.0.0.0",
            "port": 8080,
            "https": false
        }
    ],
    "app": {
        "document_root": "./",
        "static_files_headers": [
            {
                "uri": "/",
                "headers": {
                    "Access-Control-Allow-Origin": "*"
                }
            }
        ]
    },
    "db_clients": []
}

3. Employee.h

cpp 复制代码
#ifndef EMPLOYEE_H
#define EMPLOYEE_H

#include <string>
#include <json/json.h>

class Employee {
private:
    int id;
    std::string name;
    std::string department;
    std::string position;
    double salary;

public:
    Employee() : id(0), salary(0.0) {}
    Employee(int id, const std::string& name, const std::string& department,
             const std::string& position, double salary)
        : id(id), name(name), department(department), position(position), salary(salary) {}

    // Getters
    int getId() const { return id; }
    std::string getName() const { return name; }
    std::string getDepartment() const { return department; }
    std::string getPosition() const { return position; }
    double getSalary() const { return salary; }

    // Setters
    void setId(int id) { this->id = id; }
    void setName(const std::string& name) { this->name = name; }
    void setDepartment(const std::string& department) { this->department = department; }
    void setPosition(const std::string& position) { this->position = position; }
    void setSalary(double salary) { this->salary = salary; }

    // JSON序列化
    Json::Value toJson() const {
        Json::Value json;
        json["id"] = id;
        json["name"] = name;
        json["department"] = department;
        json["position"] = position;
        json["salary"] = salary;
        return json;
    }

    // JSON反序列化
    static Employee fromJson(const Json::Value& json) {
        Employee emp;
        if (json.isMember("id")) emp.setId(json["id"].asInt());
        if (json.isMember("name")) emp.setName(json["name"].asString());
        if (json.isMember("department")) emp.setDepartment(json["department"].asString());
        if (json.isMember("position")) emp.setPosition(json["position"].asString());
        if (json.isMember("salary")) emp.setSalary(json["salary"].asDouble());
        return emp;
    }
};

#endif // EMPLOYEE_H

4. EmployeeController.h

cpp 复制代码
#ifndef EMPLOYEE_CONTROLLER_H
#define EMPLOYEE_CONTROLLER_H

#include <drogon/HttpController.h>
#include <vector>
#include <mutex>
#include "Employee.h"

using namespace drogon;

class EmployeeController : public HttpController<EmployeeController> {
private:
    std::vector<Employee> employees;
    std::mutex employeesMutex;
    int nextId;

    // 模拟数据库操作
    void initSampleData();

public:
    EmployeeController() : nextId(1) {
        initSampleData();
    }

    METHOD_LIST_BEGIN
    // 定义路由
    ADD_METHOD_TO(EmployeeController::getAllEmployees, "/api/employees", Get, Options);
    ADD_METHOD_TO(EmployeeController::getEmployee, "/api/employees/{1}", Get, Options);
    ADD_METHOD_TO(EmployeeController::createEmployee, "/api/employees", Post, Options);
    ADD_METHOD_TO(EmployeeController::updateEmployee, "/api/employees/{1}", Put, Options);
    ADD_METHOD_TO(EmployeeController::deleteEmployee, "/api/employees/{1}", Delete, Options);
    METHOD_LIST_END

    // API处理方法
    void getAllEmployees(const HttpRequestPtr& req,
                        std::function<void(const HttpResponsePtr&)>&& callback);
    void getEmployee(const HttpRequestPtr& req,
                    std::function<void(const HttpResponsePtr&)>&& callback,
                    int id);
    void createEmployee(const HttpRequestPtr& req,
                       std::function<void(const HttpResponsePtr&)>&& callback);
    void updateEmployee(const HttpRequestPtr& req,
                       std::function<void(const HttpResponsePtr&)>&& callback,
                       int id);
    void deleteEmployee(const HttpRequestPtr& req,
                       std::function<void(const HttpResponsePtr&)>&& callback,
                       int id);
};

#endif // EMPLOYEE_CONTROLLER_H

5. EmployeeController.cpp

cpp 复制代码
#include "EmployeeController.h"
#include <json/json.h>

void EmployeeController::initSampleData() {
    employees.push_back(Employee(1, "张三", "技术部", "高级工程师", 15000));
    employees.push_back(Employee(2, "李四", "市场部", "市场经理", 12000));
    employees.push_back(Employee(3, "王五", "人事部", "HR主管", 10000));
    nextId = 4;
}

void EmployeeController::getAllEmployees(
    const HttpRequestPtr& req,
    std::function<void(const HttpResponsePtr&)>&& callback) {
    
    // 处理CORS预检请求
    if (req->getMethod() == Options) {
        auto resp = HttpResponse::newHttpResponse();
        resp->addHeader("Access-Control-Allow-Origin", "*");
        resp->addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        resp->addHeader("Access-Control-Allow-Headers", "Content-Type");
        callback(resp);
        return;
    }

    std::lock_guard<std::mutex> lock(employeesMutex);
    
    Json::Value jsonArray(Json::arrayValue);
    for (const auto& emp : employees) {
        jsonArray.append(emp.toJson());
    }

    auto resp = HttpResponse::newHttpJsonResponse(jsonArray);
    resp->addHeader("Access-Control-Allow-Origin", "*");
    callback(resp);
}

void EmployeeController::getEmployee(
    const HttpRequestPtr& req,
    std::function<void(const HttpResponsePtr&)>&& callback,
    int id) {
    
    if (req->getMethod() == Options) {
        auto resp = HttpResponse::newHttpResponse();
        resp->addHeader("Access-Control-Allow-Origin", "*");
        resp->addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        resp->addHeader("Access-Control-Allow-Headers", "Content-Type");
        callback(resp);
        return;
    }

    std::lock_guard<std::mutex> lock(employeesMutex);
    
    auto it = std::find_if(employees.begin(), employees.end(),
        [id](const Employee& emp) { return emp.getId() == id; });

    if (it != employees.end()) {
        auto resp = HttpResponse::newHttpJsonResponse(it->toJson());
        resp->addHeader("Access-Control-Allow-Origin", "*");
        callback(resp);
    } else {
        Json::Value error;
        error["error"] = "Employee not found";
        auto resp = HttpResponse::newHttpJsonResponse(error);
        resp->setStatusCode(k404NotFound);
        resp->addHeader("Access-Control-Allow-Origin", "*");
        callback(resp);
    }
}

void EmployeeController::createEmployee(
    const HttpRequestPtr& req,
    std::function<void(const HttpResponsePtr&)>&& callback) {
    
    if (req->getMethod() == Options) {
        auto resp = HttpResponse::newHttpResponse();
        resp->addHeader("Access-Control-Allow-Origin", "*");
        resp->addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        resp->addHeader("Access-Control-Allow-Headers", "Content-Type");
        callback(resp);
        return;
    }

    auto json = req->getJsonObject();
    if (!json) {
        Json::Value error;
        error["error"] = "Invalid JSON";
        auto resp = HttpResponse::newHttpJsonResponse(error);
        resp->setStatusCode(k400BadRequest);
        resp->addHeader("Access-Control-Allow-Origin", "*");
        callback(resp);
        return;
    }

    std::lock_guard<std::mutex> lock(employeesMutex);
    
    Employee emp = Employee::fromJson(*json);
    emp.setId(nextId++);
    employees.push_back(emp);

    auto resp = HttpResponse::newHttpJsonResponse(emp.toJson());
    resp->setStatusCode(k201Created);
    resp->addHeader("Access-Control-Allow-Origin", "*");
    callback(resp);
}

void EmployeeController::updateEmployee(
    const HttpRequestPtr& req,
    std::function<void(const HttpResponsePtr&)>&& callback,
    int id) {
    
    if (req->getMethod() == Options) {
        auto resp = HttpResponse::newHttpResponse();
        resp->addHeader("Access-Control-Allow-Origin", "*");
        resp->addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        resp->addHeader("Access-Control-Allow-Headers", "Content-Type");
        callback(resp);
        return;
    }

    auto json = req->getJsonObject();
    if (!json) {
        Json::Value error;
        error["error"] = "Invalid JSON";
        auto resp = HttpResponse::newHttpJsonResponse(error);
        resp->setStatusCode(k400BadRequest);
        resp->addHeader("Access-Control-Allow-Origin", "*");
        callback(resp);
        return;
    }

    std::lock_guard<std::mutex> lock(employeesMutex);
    
    auto it = std::find_if(employees.begin(), employees.end(),
        [id](const Employee& emp) { return emp.getId() == id; });

    if (it != employees.end()) {
        Employee updatedEmp = Employee::fromJson(*json);
        updatedEmp.setId(id);
        *it = updatedEmp;
        
        auto resp = HttpResponse::newHttpJsonResponse(updatedEmp.toJson());
        resp->addHeader("Access-Control-Allow-Origin", "*");
        callback(resp);
    } else {
        Json::Value error;
        error["error"] = "Employee not found";
        auto resp = HttpResponse::newHttpJsonResponse(error);
        resp->setStatusCode(k404NotFound);
        resp->addHeader("Access-Control-Allow-Origin", "*");
        callback(resp);
    }
}

void EmployeeController::deleteEmployee(
    const HttpRequestPtr& req,
    std::function<void(const HttpResponsePtr&)>&& callback,
    int id) {
    
    if (req->getMethod() == Options) {
        auto resp = HttpResponse::newHttpResponse();
        resp->addHeader("Access-Control-Allow-Origin", "*");
        resp->addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        resp->addHeader("Access-Control-Allow-Headers", "Content-Type");
        callback(resp);
        return;
    }

    std::lock_guard<std::mutex> lock(employeesMutex);
    
    auto it = std::find_if(employees.begin(), employees.end(),
        [id](const Employee& emp) { return emp.getId() == id; });

    if (it != employees.end()) {
        employees.erase(it);
        
        Json::Value success;
        success["message"] = "Employee deleted successfully";
        auto resp = HttpResponse::newHttpJsonResponse(success);
        resp->addHeader("Access-Control-Allow-Origin", "*");
        callback(resp);
    } else {
        Json::Value error;
        error["error"] = "Employee not found";
        auto resp = HttpResponse::newHttpJsonResponse(error);
        resp->setStatusCode(k404NotFound);
        resp->addHeader("Access-Control-Allow-Origin", "*");
        callback(resp);
    }
}

6. main.cpp

cpp 复制代码
#include <drogon/drogon.h>
#include "EmployeeController.h"

int main() {
    // 设置日志级别
    drogon::app().setLogLevel(trantor::Logger::kWarn);
    
    // 添加CORS支持
    drogon::app().registerPostHandlingAdvice([](const drogon::HttpRequestPtr& req,
                                                  const drogon::HttpResponsePtr& resp) {
        resp->addHeader("Access-Control-Allow-Origin", "*");
        resp->addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        resp->addHeader("Access-Control-Allow-Headers", "Content-Type");
    });
    
    // 加载配置文件
    drogon::app().loadConfigFile("config.json");
    
    // 注册控制器
    auto controller = std::make_shared<EmployeeController>();
    drogon::app().registerController(controller);
    
    // 启动服务器
    drogon::app().run();
    
    return 0;
}

前端实现(Vue 3)

1. package.json

json 复制代码
{
  "name": "employee-frontend",
  "version": "1.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "vue": "^3.3.0",
    "axios": "^1.4.0",
    "element-plus": "^2.3.0"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.2.0",
    "vite": "^4.3.0"
  }
}

2. vite.config.js

javascript 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  server: {
    port: 3000,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true
      }
    }
  }
})

3. index.html

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>员工管理系统</title>
</head>
<body>
  <div id="app"></div>
  <script type="module" src="/src/main.js"></script>
</body>
</html>

4. src/main.js

javascript 复制代码
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'

const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')

5. src/api/employee.js

javascript 复制代码
import axios from 'axios'

const API_BASE_URL = '/api'

const api = axios.create({
  baseURL: API_BASE_URL,
  timeout: 5000,
  headers: {
    'Content-Type': 'application/json'
  }
})

export const employeeApi = {
  // 获取所有员工
  getAllEmployees() {
    return api.get('/employees')
  },

  // 获取单个员工
  getEmployee(id) {
    return api.get(`/employees/${id}`)
  },

  // 创建员工
  createEmployee(employee) {
    return api.post('/employees', employee)
  },

  // 更新员工
  updateEmployee(id, employee) {
    return api.put(`/employees/${id}`, employee)
  },

  // 删除员工
  deleteEmployee(id) {
    return api.delete(`/employees/${id}`)
  }
}

6. src/App.vue

vue 复制代码
<template>
  <div id="app">
    <el-container>
      <el-header>
        <h1>员工管理系统</h1>
      </el-header>
      <el-main>
        <EmployeeList />
      </el-main>
    </el-container>
  </div>
</template>

<script>
import EmployeeList from './components/EmployeeList.vue'

export default {
  name: 'App',
  components: {
    EmployeeList
  }
}
</script>

<style>
#app {
  font-family: 'Arial', sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
}

.el-header {
  background-color: #409EFF;
  color: white;
  display: flex;
  align-items: center;
  padding: 0 20px;
}

.el-header h1 {
  margin: 0;
  font-size: 24px;
}

.el-main {
  padding: 20px;
}
</style>

7. src/components/EmployeeList.vue

vue 复制代码
<template>
  <div class="employee-list">
    <!-- 操作栏 -->
    <div class="toolbar">
      <el-button type="primary" @click="showAddDialog">
        <el-icon><Plus /></el-icon>
        添加员工
      </el-button>
    </div>

    <!-- 员工表格 -->
    <el-table
      :data="employees"
      style="width: 100%"
      border
      stripe
      v-loading="loading"
    >
      <el-table-column prop="id" label="ID" width="80" />
      <el-table-column prop="name" label="姓名" width="120" />
      <el-table-column prop="department" label="部门" width="150" />
      <el-table-column prop="position" label="职位" width="150" />
      <el-table-column prop="salary" label="薪资" width="120">
        <template #default="scope">
          ¥{{ scope.row.salary.toLocaleString() }}
        </template>
      </el-table-column>
      <el-table-column label="操作" width="200" fixed="right">
        <template #default="scope">
          <el-button size="small" @click="showEditDialog(scope.row)">
            编辑
          </el-button>
          <el-button
            size="small"
            type="danger"
            @click="deleteEmployee(scope.row.id)"
          >
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>

    <!-- 添加/编辑对话框 -->
    <el-dialog
      v-model="dialogVisible"
      :title="dialogTitle"
      width="500px"
      @close="resetForm"
    >
      <el-form
        ref="formRef"
        :model="form"
        :rules="rules"
        label-width="100px"
      >
        <el-form-item label="姓名" prop="name">
          <el-input v-model="form.name" placeholder="请输入姓名" />
        </el-form-item>
        <el-form-item label="部门" prop="department">
          <el-select v-model="form.department" placeholder="请选择部门">
            <el-option label="技术部" value="技术部" />
            <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="职位" prop="position">
          <el-input v-model="form.position" placeholder="请输入职位" />
        </el-form-item>
        <el-form-item label="薪资" prop="salary">
          <el-input-number
            v-model="form.salary"
            :min="0"
            :max="1000000"
            :step="1000"
            placeholder="请输入薪资"
          />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="dialogVisible = false">取消</el-button>
          <el-button type="primary" @click="submitForm">
            确定
          </el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>

<script>
import { ref, reactive, onMounted, computed } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import { employeeApi } from '../api/employee'

export default {
  name: 'EmployeeList',
  components: {
    Plus
  },
  setup() {
    const employees = ref([])
    const loading = ref(false)
    const dialogVisible = ref(false)
    const formRef = ref(null)
    const editingId = ref(null)
    
    const form = reactive({
      name: '',
      department: '',
      position: '',
      salary: 0
    })

    const rules = {
      name: [
        { required: true, message: '请输入姓名', trigger: 'blur' },
        { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
      ],
      department: [
        { required: true, message: '请选择部门', trigger: 'change' }
      ],
      position: [
        { required: true, message: '请输入职位', trigger: 'blur' }
      ],
      salary: [
        { required: true, message: '请输入薪资', trigger: 'blur' }
      ]
    }

    const dialogTitle = computed(() => {
      return editingId.value ? '编辑员工' : '添加员工'
    })

    // 获取所有员工
    const fetchEmployees = async () => {
      loading.value = true
      try {
        const response = await employeeApi.getAllEmployees()
        employees.value = response.data
      } catch (error) {
        ElMessage.error('获取员工列表失败:' + error.message)
      } finally {
        loading.value = false
      }
    }

    // 显示添加对话框
    const showAddDialog = () => {
      editingId.value = null
      resetForm()
      dialogVisible.value = true
    }

    // 显示编辑对话框
    const showEditDialog = (employee) => {
      editingId.value = employee.id
      form.name = employee.name
      form.department = employee.department
      form.position = employee.position
      form.salary = employee.salary
      dialogVisible.value = true
    }

    // 提交表单
    const submitForm = async () => {
      if (!formRef.value) return
      
      await formRef.value.validate(async (valid) => {
        if (valid) {
          try {
            const employeeData = {
              name: form.name,
              department: form.department,
              position: form.position,
              salary: form.salary
            }

            if (editingId.value) {
              await employeeApi.updateEmployee(editingId.value, employeeData)
              ElMessage.success('员工信息更新成功')
            } else {
              await employeeApi.createEmployee(employeeData)
              ElMessage.success('员工添加成功')
            }

            dialogVisible.value = false
            await fetchEmployees()
          } catch (error) {
            ElMessage.error('操作失败:' + error.message)
          }
        }
      })
    }

    // 删除员工
    const deleteEmployee = async (id) => {
      try {
        await ElMessageBox.confirm(
          '确定要删除该员工吗?',
          '警告',
          {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
          }
        )
        
        await employeeApi.deleteEmployee(id)
        ElMessage.success('员工删除成功')
        await fetchEmployees()
      } catch (error) {
        if (error !== 'cancel') {
          ElMessage.error('删除失败:' + error.message)
        }
      }
    }

    // 重置表单
    const resetForm = () => {
      form.name = ''
      form.department = ''
      form.position = ''
      form.salary = 0
      if (formRef.value) {
        formRef.value.resetFields()
      }
    }

    // 页面加载时获取数据
    onMounted(() => {
      fetchEmployees()
    })

    return {
      employees,
      loading,
      dialogVisible,
      formRef,
      form,
      rules,
      dialogTitle,
      showAddDialog,
      showEditDialog,
      submitForm,
      deleteEmployee,
      resetForm
    }
  }
}
</script>

<style scoped>
.employee-list {
  background: white;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}

.toolbar {
  margin-bottom: 20px;
}

.dialog-footer {
  display: flex;
  justify-content: flex-end;
}
</style>

编译和运行

安装vcpkg:

bash 复制代码
# 克隆vcpkg仓库
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg

# 运行bootstrap脚本
.\bootstrap-vcpkg.bat

# 集成到Visual Studio(可选)
.\vcpkg integrate install

后端编译运行:

bash 复制代码
.\vcpkg install drogon:x64-windows nlohmann-json:x64-windows
# 或者使用jsoncpp(Drogon默认依赖,已自动安装)
.\vcpkg install jsoncpp:x64-windows

# 编译
cd backend
mkdir build && cd build
cmake ..
make

# 运行
./employee_server

前端运行:

bash 复制代码
cd frontend
npm install
npm run dev

项目特点

  1. 完整的企业级架构:前后端分离,RESTful API设计
  2. 规范的代码结构:清晰的目录组织,模块化设计
  3. 完整的CRUD操作:增删改查功能齐全
  4. 错误处理:包含完善的错误处理和用户提示
  5. 线程安全:后端使用互斥锁保证数据安全
  6. CORS支持:处理跨域请求
  7. 表单验证:前端表单验证规则
  8. UI组件库:使用Element Plus提供专业的UI体验

这个项目虽然规模不大,但包含了企业级项目的核心要素,可以作为实际项目的基础模板进行扩展。

相关推荐
玖釉-1 小时前
Slang 和 HLSL 的区别与用法详解:现代图形渲染中的两种 Shader 编程语言
c++·算法·图形渲染
TechPioneer_lp1 小时前
就业指导|中九非科班毕业,华为 OD 做 Java 后端想转 C++,能找到深度学习挂钩的岗工作吗?
java·c++·华为od·华为·就业指导·校招指导
枕星而眠1 小时前
C++ String类精讲:从基础用法到进阶底层原理
开发语言·c++·后端·学习方法
江屿风1 小时前
【C++笔记】模板初阶流食般投喂
开发语言·c++·笔记
Shadow(⊙o⊙)1 小时前
qt信号和槽链接的接入与断开
开发语言·前端·c++·qt·学习
慕斯fuafua1 小时前
JS——DOM操作
前端·javascript·html
肩上风骋1 小时前
C++基本知识点积累之d指针,invokemethod函数(一)
开发语言·c++·d指针·invokemethod()
GISer_Jing1 小时前
深入解析 Three.js:从架构设计到 WebGPU 渲染革命
javascript·信息可视化·webgl
计算机安禾1 小时前
【c++面向对象编程】第45篇:萃取(Traits)技术与策略类:STL源码中的智慧
开发语言·c++