可视化大屏系列(2):核心模块 - 入场登记、人脸识别与权限控制

可视化大屏系列(2):核心模块 - 入场登记、人脸识别与权限控制

在这里插入图片描述

在智慧工地管理系统中,入场登记、人脸识别与权限控制是保证工地安全与管理效率的重要环节。通过集成人脸识别技术、智能权限管理以及灵活的入场登记方式,可以有效提高工地管理的智能化水平。本节将介绍如何实现这些核心功能模块,包括用户信息管理、人脸抓拍与对接、角色权限控制以及基于 Element 的自适应表单和表格设计。


目录:

  • 一、入场登记系统设计
    • 入场登记表单设计
    • 自适应表单布局
    • 数据录入与验证
  • 二、人脸识别技术集成
    • 人脸抓拍技术原理
    • 与人脸识别接口对接
    • 实现工地人员入场验证
  • 三、角色权限控制(RBAC)
    • 角色权限管理(RBAC 原理)
    • 基于角色的权限控制实现
    • 权限校验与操作限制
  • 四、用户信息管理与展示
    • 用户信息的录入与更新
    • 基于 Element 表格展示用户信息
    • 数据分页与搜索功能
  • 五、总结与优化建议
    • 系统扩展与优化
    • 面临的挑战与解决方案

一、入场登记系统设计

入场登记系统是智慧工地管理系统中的重要组成部分,用于管理人员进出工地的记录。系统需要支持工作人员的基本信息录入、照片上传、人脸识别等功能,以确保工地的安全性和人员的合法性。本节将详细讲解如何设计一个高效、便捷的入场登记系统,包括表单设计、表单布局的自适应调整以及数据的录入和验证。


1.1 入场登记表单设计
入场登记表单结构

以下是一个典型的入场登记表单,它包含了姓名、工号、入场时间和照片上传等字段。

<template>
  <el-form :model="entryForm" ref="entryForm" label-width="100px">
    <!-- 姓名字段 -->
    <el-form-item label="姓名" prop="name" :rules="[{ required: true, message: '请输入姓名', trigger: 'blur' }]">
      <el-input v-model="entryForm.name" placeholder="请输入姓名" />
    </el-form-item>

    <!-- 工号字段 -->
    <el-form-item label="工号" prop="id" :rules="[{ required: true, message: '请输入工号', trigger: 'blur' }]">
      <el-input v-model="entryForm.id" placeholder="请输入工号" />
    </el-form-item>

    <!-- 入场时间字段 -->
    <el-form-item label="入场时间" prop="entryTime" :rules="[{ required: true, message: '请选择入场时间', trigger: 'blur' }]">
      <el-date-picker v-model="entryForm.entryTime" type="datetime" placeholder="请选择入场时间" />
    </el-form-item>

    <!-- 人脸照片上传字段 -->
    <el-form-item label="照片上传" prop="photo" :rules="[{ required: true, message: '请上传人脸照片', trigger: 'change' }]">
      <el-upload
        class="upload-demo"
        drag
        action="#"
        :on-success="handlePhotoSuccess"
        :before-upload="beforeUpload"
      >
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">点击或拖动文件上传</div>
      </el-upload>
    </el-form-item>

    <!-- 提交按钮 -->
    <el-form-item>
      <el-button type="primary" @click="submitEntryForm">提交</el-button>
    </el-form-item>
  </el-form>
</template>

<script setup>
import { ref } from 'vue';
import { ElForm, ElFormItem, ElInput, ElDatePicker, ElUpload, ElButton } from 'element-plus';

// 表单数据
const entryForm = ref({
  name: '',
  id: '',
  entryTime: '',
  photo: null
});

// 提交表单
const submitEntryForm = () => {
  // 表单提交逻辑
  console.log(entryForm.value);
};

// 上传图片的回调
const handlePhotoSuccess = (res, file) => {
  entryForm.value.photo = file;
};

// 图片上传之前的验证
const beforeUpload = (file) => {
  const isImage = file.type.startsWith('image/');
  if (!isImage) {
    alert('只能上传图片文件');
  }
  return isImage;
};
</script>

功能说明:

  • 表单的每个输入框都使用了 el-input 组件,确保了用户能够方便地输入数据。
  • el-date-picker 用于选择入场时间,它支持日期和时间的选择。
  • el-upload 组件用于上传人脸照片,通过 before-upload 方法验证上传的文件类型,并通过 on-success 处理上传成功后的操作。
  • el-button 用于提交表单,提交时可以将表单数据发送到后端进行处理。

1.2 自适应表单布局

为了确保表单在不同设备上的适配性,我们需要使用响应式设计。使用 Element Plus 的表单组件时,可以通过 CSS 媒体查询和 flexbox 布局来使表单自适应不同屏幕大小。

自适应布局的实现

以下是一个简单的自适应布局的 CSS 示例:

<style scoped>
/* 表单最大宽度 */
.el-form {
  max-width: 600px;
  margin: 0 auto;
}

/* 响应式布局:当屏幕宽度小于 768px 时,表单宽度为 100% */
@media (max-width: 768px) {
  .el-form {
    width: 100%;
    padding: 20px;
  }

  .el-form-item {
    margin-bottom: 10px;
  }
}
</style>

解释:

  • 默认情况下,表单宽度设置为 600px 并居中显示。
  • 当屏幕宽度小于 768px 时,表单宽度自动调整为 100%,并且添加了适当的内边距,确保表单控件在手机端或平板端的展示效果良好。

1.3 数据录入与验证

数据录入与验证是确保表单数据合法性的重要环节。在入场登记表单中,我们需要对用户的姓名、工号、入场时间和照片上传等字段进行验证。为了增强表单的准确性和安全性,我们可以使用 Element Plus 提供的验证功能进行数据校验。

数据验证与提交

在表单提交前,Element Plus 提供了 rules 属性来定义每个字段的验证规则。通过设置规则,可以确保用户输入的数据符合预期格式,如工号和姓名不能为空,入场时间必须选择等。

示例:验证规则的实现

<el-form-item label="姓名" prop="name" :rules="[{ required: true, message: '请输入姓名', trigger: 'blur' }]">
  <el-input v-model="entryForm.name" placeholder="请输入姓名" />
</el-form-item>

<el-form-item label="工号" prop="id" :rules="[{ required: true, message: '请输入工号', trigger: 'blur' }]">
  <el-input v-model="entryForm.id" placeholder="请输入工号" />
</el-form-item>

<el-form-item label="入场时间" prop="entryTime" :rules="[{ required: true, message: '请选择入场时间', trigger: 'blur' }]">
  <el-date-picker v-model="entryForm.entryTime" type="datetime" placeholder="请选择入场时间" />
</el-form-item>

<el-form-item label="照片上传" prop="photo" :rules="[{ required: true, message: '请上传人脸照片', trigger: 'change' }]">
  <el-upload
    class="upload-demo"
    drag
    action="#"
    :on-success="handlePhotoSuccess"
    :before-upload="beforeUpload"
  >
    <i class="el-icon-upload"></i>
    <div class="el-upload__text">点击或拖动文件上传</div>
  </el-upload>
</el-form-item>

验证说明:

  • required: true 表示该字段是必填项。
  • message 是验证失败时的提示信息。
  • trigger 属性指定何时触发验证,blur 表示当控件失去焦点时触发。
表单提交时的验证

在用户点击提交按钮时,我们可以通过 this.$refs.entryForm.validate 方法触发表单验证。如果验证通过,则执行表单提交操作;如果验证失败,则显示相应的错误提示。

const submitEntryForm = () => {
  // 验证表单
  this.$refs.entryForm.validate((valid) => {
    if (valid) {
      // 表单数据验证通过,执行提交逻辑
      console.log('表单提交数据:', entryForm.value);
    } else {
      console.log('表单验证失败');
      return false;
    }
  });
};

二、人脸识别技术集成

详细介绍人脸抓拍技术原理、如何与人脸识别接口对接,并实现工地人员的入场验证功能。

2.1 人脸抓拍技术原理

人脸抓拍技术主要基于摄像头采集的图像,通过算法提取人脸特征,并与数据库中已存储的人员特征进行对比,从而判断该人脸是否为授权人员。该技术的原理通常包括以下几个步骤:

  1. 人脸检测:摄像头拍摄的图像中,识别出可能的人脸区域。
  2. 人脸对齐:对检测到的人脸进行旋转、裁剪和标准化处理,使人脸特征更加突出。
  3. 特征提取:使用深度学习模型或传统算法提取出人脸特征,如眼距、鼻梁、下巴等。
  4. 人脸比对:将提取的人脸特征与数据库中存储的特征进行比对,判断是否匹配。

2.2 与人脸识别接口对接

为了集成人脸识别功能,我们可以使用一些第三方人脸识别 API 服务,如百度AI、腾讯云人脸识别等,这些平台提供了强大的接口,能够处理人脸抓拍和比对操作。在本节中,我们将以腾讯云人脸识别 API 为例,讲解如何进行对接。

集成腾讯云人脸识别 API

首先,你需要在腾讯云平台上申请人脸识别 API 的相关凭证(如 SecretId 和 SecretKey),并且安装腾讯云 SDK。

  1. 安装腾讯云 SDK:
npm install tencentcloud-sdk-nodejs
  1. 在项目中引入并配置腾讯云人脸识别 SDK:
// src/utils/faceRecognition.js
import * as tencentcloud from 'tencentcloud-sdk-nodejs';

const clientConfig = {
  credential: {
    secretId: 'YOUR_SECRET_ID',
    secretKey: 'YOUR_SECRET_KEY',
  },
  region: 'ap-guangzhou',
  profile: {
    httpProfile: {
      endpoint: 'face.tencentcloudapi.com',
    },
  },
};

const FaceClient = tencentcloud.iai.v20200303.Client;
const client = new FaceClient(clientConfig);

export const compareFaces = async (image1, image2) => {
  try {
    const params = {
      ImageA: image1, // 第一个图片的 Base64 编码
      ImageB: image2, // 第二个图片的 Base64 编码
    };
    const result = await client.CompareFace(params);
    return result;
  } catch (error) {
    console.error('人脸比对失败:', error);
    return null;
  }
};

在上面的代码中,CompareFace 接口接受两个参数(ImageAImageB),分别是两个人脸图像的 Base64 编码。通过该接口,腾讯云会返回一个比对结果,其中包括相似度得分。


2.3 实现工地人员入场验证

具体流程如下:

  1. 拍摄实时人脸图像:使用设备上的摄像头拍摄人员的实时人脸图像。
  2. 将图像转为 Base64 编码:将拍摄的图像转为 Base64 格式,以便传输至后端进行比对。
  3. 与存储的图像比对:将实时拍摄的图像与数据库中该人员的存储图像进行比对。
  4. 返回验证结果:根据比对结果,判断人员是否为授权入场人员。
前端代码:实时人脸抓拍与验证
<template>
  <div>
    <h3>人脸识别入场验证</h3>
    <el-button @click="startCamera">启动摄像头</el-button>
    <video ref="video" width="320" height="240" autoplay></video>
    <el-button @click="captureFace" :disabled="isCapturing">抓拍</el-button>

    <el-dialog title="验证结果" :visible.sync="dialogVisible">
      <p>{{ verificationResult }}</p>
      <el-button @click="dialogVisible = false">关闭</el-button>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { ElButton, ElDialog } from 'element-plus';

const videoRef = ref(null);
const isCapturing = ref(false);
const dialogVisible = ref(false);
const verificationResult = ref('');

// 启动摄像头
const startCamera = () => {
  const video = videoRef.value;
  if (navigator.mediaDevices.getUserMedia) {
    navigator.mediaDevices.getUserMedia({ video: true })
      .then((stream) => {
        video.srcObject = stream;
      })
      .catch((err) => {
        console.error("无法访问摄像头:", err);
      });
  } else {
    alert('您的浏览器不支持摄像头');
  }
};

// 人脸抓拍
const captureFace = async () => {
  isCapturing.value = true;
  const video = videoRef.value;
  const canvas = document.createElement('canvas');
  canvas.width = video.videoWidth;
  canvas.height = video.videoHeight;
  const ctx = canvas.getContext('2d');
  ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

  // 转为 Base64 编码
  const base64Image = canvas.toDataURL('image/jpeg');

  // 调用后端 API 进行人脸识别比对
  const result = await verifyFace(base64Image);
  verificationResult.value = result ? '人脸匹配成功,允许入场' : '人脸不匹配,禁止入场';
  dialogVisible.value = true;

  isCapturing.value = false;
};

// 调用后端接口进行人脸比对
const verifyFace = async (imageBase64) => {
  // 模拟调用后端人脸识别 API (实际需要调用后端接口)
  const response = await fetch('/api/verify-face', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ image: imageBase64 }),
  });
  const data = await response.json();
  return data.isMatch;  // 后端返回是否匹配
};
</script>

功能说明:

  • startCamera 方法通过浏览器的 navigator.mediaDevices.getUserMedia API 启动摄像头,实时显示视频。
  • captureFace 方法从摄像头画面中抓取一帧图片并将其转为 Base64 格式,然后通过 verifyFace 方法向后端发送请求进行人脸比对。
  • 后端收到图像后,将其与数据库中存储的人脸数据进行比对,返回比对结果,并在前端显示验证结果。
后端代码:人脸比对
// src/routes/verify-face.js (Node.js 示例)
const express = require('express');
const { compareFaces } = require('../utils/faceRecognition');  // 引入腾讯云人脸识别 SDK

const router = express.Router();

router.post('/verify-face', async (req, res) => {
  const { image } = req.body;
  
  try {
    // 从数据库获取已存储的人脸图像
    const storedImage = await getStoredFaceImage(req.user.id);  // 假设已登录并存有 ID
    const result = await compareFaces(storedImage, image);

    if (result && result.Similarity > 80) {
      return res.json({ isMatch: true });
    } else {
      return res.json({ isMatch: false });
    }
  } catch (error) {
    console.error('人脸比对失败:', error);
    return res.status(500).json({ error: '人脸比对失败' });
  }
});

module.exports = router;

功能说明:

  • 后端接收前端发送的 Base64 编码的图像。
  • 通过腾讯云的 compareFaces 方法,将前端图像与数据库中存储的图像进行比对。
  • 如果相似度大于 80%,则认为人脸匹配成功,允许入场。

三、角色权限控制(RBAC)

RBAC(Role-Based Access Control,基于角色的访问控制)是一种常见的权限管理模型,它将用户与角色绑定,再通过角色赋予不同的权限。通过 RBAC 机制,可以确保不同角色的用户仅能访问自己有权限的资源,避免权限滥用,提高系统安全性。


3.1 角色权限管理(RBAC 原理)

RBAC 的基本原理是基于角色对用户进行权限控制。具体来说,RBAC 包括三个基本要素:

  1. 用户(User):系统中的实际用户。每个用户都可以被分配一个或多个角色。
  2. 角色(Role):一组权限的集合,角色对应着特定的职责和功能。系统中的权限是与角色关联的,而不是直接与用户关联。用户通过角色获得访问某些资源的权限。
  3. 权限(Permission):可以执行的操作,如访问某些页面、读取某些数据、执行特定的任务等。

RBAC 的工作流程如下:

  1. 创建不同的角色(如管理员、操作员、审计员等)。
  2. 为每个角色分配特定的权限。
  3. 将用户分配到相应的角色上。
  4. 用户通过其角色获得权限,执行相应的操作。

RBAC 的优势在于:通过角色来组织和管理权限,避免了单独为每个用户设置权限的复杂性,便于管理和扩展。


3.2 基于角色的权限控制实现

在系统中,角色权限控制需要通过前后端结合来实现。前端通过用户的角色信息,限制用户可以访问的页面和功能;后端则根据角色来验证用户的权限,确保敏感操作和数据只有具备相应权限的用户才能访问。

1. 后端角色与权限设计

在后端,首先需要设计角色和权限的数据库表结构:

-- 角色表
CREATE TABLE roles (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  description TEXT
);

-- 权限表
CREATE TABLE permissions (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  description TEXT
);

-- 角色与权限的关联表
CREATE TABLE role_permissions (
  role_id INT NOT NULL,
  permission_id INT NOT NULL,
  PRIMARY KEY(role_id, permission_id),
  FOREIGN KEY(role_id) REFERENCES roles(id),
  FOREIGN KEY(permission_id) REFERENCES permissions(id)
);

-- 用户与角色的关联表
CREATE TABLE user_roles (
  user_id INT NOT NULL,
  role_id INT NOT NULL,
  PRIMARY KEY(user_id, role_id),
  FOREIGN KEY(user_id) REFERENCES users(id),
  FOREIGN KEY(role_id) REFERENCES roles(id)
);
2. 后端 API 设计

通过创建和查询 API,用户可以被分配角色,角色可以被分配权限。例如,创建角色时,可以为该角色分配一组权限。

// 示例:为角色分配权限(Node.js + Express)

const express = require('express');
const { Role, Permission } = require('../models');  // 假设模型已创建

const router = express.Router();

// 创建角色并分配权限
router.post('/assign-role-permissions', async (req, res) => {
  const { roleId, permissionIds } = req.body;  // roleId 和一组权限 ID
  
  try {
    // 删除旧权限关系
    await Role.removePermissions(roleId);

    // 为角色分配新权限
    const permissions = await Permission.findAll({ where: { id: permissionIds } });
    await Role.addPermissions(roleId, permissions);

    res.status(200).json({ message: '权限分配成功' });
  } catch (error) {
    res.status(500).json({ error: '权限分配失败' });
  }
});

module.exports = router;
3. 前端权限控制

在前端,基于用户的角色来展示和限制操作。假设在登录时,后端返回了用户的角色信息,前端可以根据这些角色来控制页面和功能的展示。

<template>
  <div>
    <h3>智慧工地系统</h3>
    <!-- 仅管理员可见的按钮 -->
    <el-button v-if="hasPermission('admin')" @click="goToAdminPage">管理员页面</el-button>

    <!-- 所有用户可见的按钮 -->
    <el-button v-if="hasPermission('viewReports')" @click="viewReports">查看报告</el-button>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { ElButton } from 'element-plus';

const userRoles = ref(['admin']);  // 假设用户角色数据来自后端

// 判断当前用户是否有某个权限
const hasPermission = (permission) => {
  return userRoles.value.includes(permission);
};

const goToAdminPage = () => {
  // 跳转到管理员页面
};

const viewReports = () => {
  // 查看报告
};
</script>

功能说明:

  • hasPermission 方法根据用户角色数组判断用户是否拥有某个权限。
  • 只有拥有 admin 权限的用户才能看到“管理员页面”按钮,其他用户则无法看到。
  • 通过此方式,前端控制哪些功能对不同角色的用户可见。
4. 权限校验与操作限制

权限校验通常在后端进行,前端负责将操作请求发送到后端,后端再根据用户角色验证其权限。以操作某个资源(如创建工地项目)为例,后端需要根据用户角色来判断是否允许该操作。

// 示例:创建工地项目时权限校验(Node.js + Express)

const express = require('express');
const { Project, User } = require('../models');  // 假设模型已创建

const router = express.Router();

router.post('/create-project', async (req, res) => {
  const { userId, projectData } = req.body;
  
  try {
    // 获取用户角色
    const user = await User.findByPk(userId, { include: ['roles'] });
    const userRoles = user.roles;

    // 检查用户是否有创建项目的权限
    if (userRoles.some(role => role.permissions.includes('createProject'))) {
      const newProject = await Project.create(projectData);
      res.status(201).json({ message: '项目创建成功', project: newProject });
    } else {
      res.status(403).json({ error: '没有权限创建项目' });
    }
  } catch (error) {
    res.status(500).json({ error: '创建项目失败' });
  }
});

module.exports = router;

功能说明:

  • 后端通过用户的角色来验证其是否具有创建项目的权限。
  • 如果用户的角色具有权限,则允许创建项目;否则,返回无权限错误。

3.3 总结与优化

在实际应用中,可以考虑以下优化:

  1. 动态权限管理:根据用户行为、部门需求等动态调整权限。
  2. 权限细化:将权限分解为更小的粒度(如查看、编辑、删除权限),从而进行更精细的控制。
  3. 权限缓存:提高权限校验性能,可以在用户登录后缓存其权限,减少频繁的数据库查询。

四、用户信息管理与展示

4.1 用户信息的录入与更新(简化)

<!-- UserForm.vue -->
<template>
  <el-form :model="model" :rules="rules" ref="formRef" label-width="100px">
    <el-row :gutter="20">
      <el-col :span="12">
        <el-form-item label="姓名" prop="name">
          <el-input v-model="model.name" />
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item label="手机号" prop="phone">
          <el-input v-model="model.phone" />
        </el-form-item>
      </el-col>
    </el-row>
    <el-form-item label="角色" prop="role">
      <el-select v-model="model.role" placeholder="选择角色">
        <el-option label="工人" value="worker" />
        <el-option label="管理员" value="admin" />
        <el-option label="安全员" value="safety" />
      </el-select>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="submit">提交</el-button>
    </el-form-item>
  </el-form>
</template>

<script setup>
import { ref } from 'vue'

const props = defineProps({ model: Object })
const emit = defineEmits(['submit'])

const formRef = ref()
const rules = {
  name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
  phone: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
  role: [{ required: true, message: '请选择角色', trigger: 'change' }]
}

const submit = () => {
  formRef.value.validate((valid) => {
    if (valid) emit('submit')
  })
}
</script>

4.2 封装通用表格组件(带分页与插槽操作)

<!-- components/BaseTable.vue -->
<template>
  <div>
    <el-table :data="paginatedData" border style="width: 100%">
      <el-table-column v-for="col in columns" :key="col.prop" v-bind="col" />
      <el-table-column v-if="$slots.actions" label="操作" width="180">
        <template #default="scope">
          <slot name="actions" :row="scope.row" />
        </template>
      </el-table-column>
    </el-table>

    <el-pagination
      v-if="showPagination"
      :total="filteredData.length"
      v-model:current-page="currentPage"
      :page-size="pageSize"
      layout="prev, pager, next"
      class="mt-4 text-center"
    />
  </div>
</template>

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

const props = defineProps({
  data: Array,
  columns: Array,
  searchText: String,
  showPagination: {
    type: Boolean,
    default: true
  },
  pageSize: {
    type: Number,
    default: 10
  }
})

const currentPage = ref(1)

const filteredData = computed(() => {
  if (!props.searchText) return props.data
  return props.data.filter(item =>
    Object.values(item).some(val =>
      String(val).toLowerCase().includes(props.searchText.toLowerCase())
    )
  )
})

const paginatedData = computed(() => {
  const start = (currentPage.value - 1) * props.pageSize
  return filteredData.value.slice(start, start + props.pageSize)
})
</script>

4.3 页面调用与使用示例

<template>
  <el-input v-model="search" placeholder="搜索姓名 / 手机号" class="mb-4" />

  <base-table
    :data="userList"
    :columns="columns"
    :search-text="search"
    :page-size="5"
  >
    <template #actions="{ row }">
      <el-button size="small" @click="editUser(row)">编辑</el-button>
      <el-button size="small" type="danger" @click="deleteUser(row)">删除</el-button>
    </template>
  </base-table>
</template>

<script setup>
import BaseTable from '@/components/BaseTable.vue'

const search = ref('')
const userList = ref([
  { name: '张三', phone: '13800001111', role: 'worker' },
  { name: '李四', phone: '13900002222', role: 'admin' },
  // ...其他用户
])

const columns = [
  { prop: 'name', label: '姓名' },
  { prop: 'phone', label: '手机号' },
  { prop: 'role', label: '角色' }
]

const editUser = (row) => console.log('编辑', row)
const deleteUser = (row) => console.log('删除', row)
</script>

五、总结与优化建议

通过合理设计入场登记系统、人脸识别技术与权限控制模块,智慧工地管理系统能够有效地提高工地的管理效率与安全性。未来,可以根据用户需求进一步优化数据展示与权限管理,结合更多智能技术实现工地管理的智能化与自动化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈探索者chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值