nodejs crud功能(orm 七牛 mysql..)

nodejs crud功能

地址

https://github.com/unknowSir/nodejs-crud.git

功能

一个页面数据的crud 具有分页 模糊查询 图片上传七牛 前端预览

前端 ajax+formData+html5-require原生验证+FileReader前端图片预览+bootstrap+bootstrap-bootstrap paginator+art-template

文件存储 前端直传七牛云(原生formdata直传)

后台 nodejs+express+xtemplate+mysql+orm2

前言,就是想要做一个crud的demo,把上面的技术都走一走(主要是要走通nodejs+express+orm+xtemplate+FormData+ajax+FileReader)

界面

这里写图片描述

安装

构建数据库

这里写图片描述

sql 代码
/*
Navicat MySQL Data Transfer

Source Server         : localhost_3306
Source Server Version : 50617
Source Host           : localhost:3306
Source Database       : cso2017

Target Server Type    : MYSQL
Target Server Version : 50617
File Encoding         : 65001

Date: 2017-12-19 22:39:54
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for `media`
-- ----------------------------
DROP TABLE IF EXISTS `media`;
CREATE TABLE `media` (
  `id` tinyint(4) NOT NULL AUTO_INCREMENT,
  `title` varchar(50) DEFAULT '',
  `src` text,
  `des` text,
  `createTime` varchar(50) DEFAULT '',
  `type` int(11) DEFAULT '-1' COMMENT '媒体类型\r\n-1 默认\r\n0 轮播图\r\n1 优秀标兵\r\n2 状态\r\n3 动态',
  `isDelete` tinyint(1) unsigned zerofill DEFAULT '0' COMMENT '0 false 1 true',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=latin1;

-- ----------------------------
-- Records of media
-- ----------------------------
INSERT INTO `media` VALUES ('1', '11', '1111', '111111', '11111111', '-1', '1');
INSERT INTO `media` VALUES ('2', '2', '22', '222', '22222', '-1', '1');
INSERT INTO `media` VALUES ('3', '1', 'uploads/37094-106.jpg', '3333', '1513678617873', '0', '0');
INSERT INTO `media` VALUES ('4', '2', 'uploads/295727-106.jpg', '333', '1513678633962', '1', '0');
INSERT INTO `media` VALUES ('5', '3', 'uploads/21420-106.jpg', '3333', '1513678676016', '-1', '0');
INSERT INTO `media` VALUES ('6', '4', 'uploads/37094-106.jpg', '111111', '1513678755337', '-1', '0');
INSERT INTO `media` VALUES ('7', '5', 'uploads/41077-106.jpg', '444', '1513678769503', '-1', '0');
INSERT INTO `media` VALUES ('8', '6', 'uploads/37094-106.jpg', '777', '1513679508958', '-1', '0');
INSERT INTO `media` VALUES ('9', '7', 'uploads/21420-106.jpg', '888', '1513679540648', '-1', '0');
INSERT INTO `media` VALUES ('10', '888', 'uploads/350947-106.jpg', '8888', '1513691658007', '2', '0');
INSERT INTO `media` VALUES ('11', '99', 'uploads/127678-106.jpg', '999', '1513691885585', '-1', '0');
INSERT INTO `media` VALUES ('12', '10', 'uploads/129643-106.jpg', '10', '1513691925412', '-1', '0');
INSERT INTO `media` VALUES ('13', '11', 'uploads/268223-106.jpg', '11', '1513691939124', '-1', '0');
INSERT INTO `media` VALUES ('14', '12', 'uploads/37094-106.jpg', '12', '1513692236017', '-1', '0');

安装nodejs后台环境

这里选择的搭配是nodejs+express 4.x + orm +xtemplate

传送门express

通过如下命令安装:

npm install express-generator -g

例如,下面的示例就是在当前工作目录下创建一个命名为 myapp 的应用。

express myapp

然后安装所有依赖包:

cd myapp 
npm install

Windows 平台使用如下命令:

> set DEBUG=myapp & npm start

然后在浏览器中打开 http://localhost:3000/ 网址就可以看到这个应用了。

还要安装其他依赖

orm mysql 对象关系模型框架

传送门orm

npm i orm --save

安装 xtemplate

npm i xtemplate --save

项目主要文件目录结构

这里写图片描述

后台app.js文件

// 引入orm模块
var orm = require('orm');
// 设置静态文件目录
app.use(express.static('public'));
// 修改视图引擎jade改为xtemplate- 不喜欢jade怪异的模样
app.set('view engine', 'xtpl');

// 修改上传文件的大小 默认上传太小了 超过1MB都会报错 所以提前修改  报错信息提示:payload 提示太大
app.use(bodyParser.json({limit:'50mb'}));
app.use(bodyParser.urlencoded({limit:'50mb',extended:true}));

// 定义数据模型 连接mysql 将模型绑定到 req对象上
app.use(orm.express("mysql://root@localhost/cso2017", {
  define: function (db, models, next) {
    var media= db.define("media", {
      // 字段都是和mysql中对应的
      id: { type: 'serial', key: true },
      title: { type: 'text' },
      src: { type: 'text' },
      des: { type: 'text' },
      createTime: { type: 'text' },
      type: { type: 'number' },
      isDelete: { type: 'number' }
    });
    models.media=media;
    next();
  }
}));

// 用来指定路由处理的
app.use('/', index);
app.use('/users', users);

后台 七牛 router/qiuniu.js

传送门七牛

var express = require('express');
var qiniu = require('qiniu');
var router = express.Router();

// 我的七牛的accessKey-此处修改为自己的即可
var accessKey = 'xxxxxxx';
// 我的七牛的 secretKey - 此处修改为自己的即可
var secretKey = 'xxxxx';
var options = {
  // 空间名字 buket
  scope: "xxxx",
};
var putPolicy = new qiniu.rs.PutPolicy(options);
var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
// 上传凭证
var uploadToken = putPolicy.uploadToken(mac);

router.get("/token", function (req, res, next) {
  console.log(uploadToken);
  res.send({
    "uptoken": uploadToken
  });
})
module.exports = router;

后台 router/user.js

var express = require('express');
var router = express.Router();
// 中间件 某则 req中获取不到上传参数(因为express 4.x 改变了 3.x获取参数的形式)
var multipart = require('connect-multiparty');
var multipartMiddleware = multipart();
var fs = require('fs');
var orm = require('orm');
router.get('/', function (req, res, next) {
  res.render('user', { title: '我来拉' });
});

// 获取分页数据
router.get("/getList", function (req, res, next) {
  // 获取分页数据
  var page = +req.query.page;
  var rows = +req.query.rows;
  var title = req.query.title;
  // 注意要传入的类型是数字
  // limit 限制一共取几条 rows
  // offset 往后偏移几条   下一页的条数
  // mysql查询参数
  var queryObj = { isDelete: "0", title: orm.like('%' + title + '%') };
  req.models.media.find(queryObj).all().limit(rows).offset((page - 1) * rows).run(function (error0, medias) {
    if (error0) res.send(error0);

    // 计算分页后的总数据
    req.models.media.count(queryObj, function (error1, count) {
      if (error1) res.send(error1);
      // medias.total = count;
      var obj = {
        data: medias,
        total: count,
        page: page,
        rows: rows
      };
      res.send(obj);
    });
  })
});

// 删除
router.get("/del", function (req, res, next) {
  var id = req.query.id;
  req.models.media.get(id, function (err, oldData) {
    if (err) throw err;
    oldData.isDelete = 1;
    oldData.save(function (err1) {
      if (err1) throw err1;
      res.send({ status: 0, msg: "删除成功" });
    })
  })
})

/* post 添加 */
router.post("/add", multipartMiddleware, function (req, res, next) {
  var file = req.files.src;
  var formBody = req.body;
  formBody.src = formBody.src;
  formBody.createTime = Date.now();
  formBody.isDelete = 0;
  req.models.media.create(formBody, function (err) {
    if (err) throw err;
    // 正常执行
    res.send({ status: 0, msg: "新增成功" });
  })

})

// 编辑
router.post("/edit", multipartMiddleware, function (req, res, next) {
  /* 
  1 有修改了图片文件的 重新保存和提交
  2 没有修改图片的 只修改字段即可

  3 改用七牛云 直接存储字段即可
   */
  var formBody = req.body;
  req.models.media.get(formBody.id, function (err, oldData) {
    oldData.title = formBody.title;
    oldData.des = formBody.des;
    oldData.type = formBody.type;
    oldData.src = formBody.src;
    // 保存数据 同步保存数据
    oldData.save(function (err) {
      if (err) throw err;
      res.send({ status: 0, msg: "编F辑成功" });
    });
  });

})
/**
 * 
 * @param {*要保存的文件} file 
 * @param {*文件保存的全路径} path  abc/
 * @param {*回调函数} callback 参数 imgPath 图片全路径
 */
function saveFile(file, path, callback) {
  fs.readFile(file.path, function (err, data) {
    if (err) {
      console.log(err);
    } else {
      // 存储文件
      var imgPath = "uploads/" + path + Date.now() + file.originalFilename;
      fs.writeFile("public/" + imgPath, data, function (err1) {
        if (err1) {
          console.log(err1);
        } else {
          // 存入数据库
          callback(imgPath);
        }
      });
    }
  });
}
module.exports = router;

前端 common.js

$(function () {

  // 往art-template中注册方法
  template.helper("toJson", function (str) {
    return JSON.stringify(str);
  });
  // 将数字转为 对应的多媒体类型
  template.helper("toType", function (num) {
    var str = "默认";
    switch (num) {
      case -1:
        break;
      case 0:
        str = "轮播图";
        break;
      case 1:
        str = "优秀标兵";
        break;
      case 2:
        str = "状态";
        break;
      case 3:
        str = "动态";
        break;
      default:
        break;
    }
    return str;
  })
})

前端 user.xtpl xtpl 是xtemplate特定的文件形式

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <!-- 注意 因为在app.js中设置了 app.use(express.static('public')); 所以路径忽略public文件夹,直接到子层即可 -->
  <link rel="stylesheet" href="lib/bootstrap/css/bootstrap.min.css">
  <link rel="stylesheet" href="css/user.css">
</head>

<body>
  <!-- 遮罩层 -->
  <div class="mask"></div>
  <div class="container">
    <div>
      <button class="btn btn-default" id="add">添加</button>
      <button class="btn btn-default" id="reflash">刷新</button>
      <div class="input-group">
        <input type="text" class="form-control searchInp" placeholder="Search for...">
        <span class="input-group-btn">
          <button class="btn btn-default searchBtn" type="button">搜索</button>
        </span>
      </div>
    </div>
    <div>
      <table class="table table-bordered"></table>
    </div>
    <div>
      <nav aria-label="Page navigation">
        <ul class="pagination"></ul>
      </nav>
    </div>
  </div>
  <!-- 模态框 -->
  <div id="myModal" class="modal fade" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <button type="button" class="close" data-dismiss="modal" aria-label="Close">
            <span aria-hidden="true">&times;</span>
          </button>
          <h4 class="modal-title">操作数据</h4>
        </div>
        <div class="modal-body">
          <form id="ff" class="form-horizontal" enctype="multipart/form-data">
            <input type="hidden" name="id">
            <div class="form-group">
              <label for="title" class="col-sm-2 control-label">请输入标题</label>
              <div class="col-sm-10">
                <input required type="text" class="form-control" name="title" id="title" placeholder="Email">
              </div>
            </div>
            <div class="form-group">
              <label for="fileInp" class="col-sm-2 control-label">上传文件</label>
              <div class="col-sm-10 " id="upParent">
                <input required type="file" class="form-control" id="fileInp" placeholder="Email">
                <input type="hidden" name="src" id="hiddenSrc">
              </div>
            </div>
            <div class="form-group">
              <div class="col-sm-10 col-sm-offset-2">
                <input type="image" width="100" src="images/default.png" name="srcImage" id="src">
              </div>
            </div>
            <div class="form-group">
              <label for="type" class="col-sm-2 control-label">类型</label>
              <div class="col-sm-10">
                <select name="type" class="form-control" id="type">
                  <option value="-1">默认</option>
                  <option value="0">轮播图</option>
                  <option value="1">优秀标兵</option>
                  <option value="2">状态</option>
                  <option value="3">动态</option>
                </select>
              </div>
            </div>
            <div class="form-group">
              <label for="inputEmail3" class="col-sm-2 control-label">描述</label>
              <div class="col-sm-10">
                <textarea placeholder="请输入详细描述" required name="des" id="des" class="form-control"></textarea>
              </div>
            </div>
          </form>
        </div>
        <div class="modal-footer">
          <button type="button" id="canBtn" class="btn btn-default" data-dismiss="modal">Close</button>
          <button type="submit" form="ff" id="subBtn" class="btn btn-primary">提交</button>
        </div>
      </div>
    </div>
  </div>

  <!-- 删除模态框 -->
  <div class="modal fade" id="deleteModal" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <button type="button" class="close" data-dismiss="modal" aria-label="Close">
            <span aria-hidden="true">&times;</span>
          </button>
          <h4 class="modal-title">警告</h4>
        </div>
        <div class="modal-body">
          你确定要删除吗
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
          <button type="button" class="btn btn-danger delSubBtn ">删除</button>
        </div>
      </div>
      <!-- /.modal-content -->
    </div>
    <!-- /.modal-dialog -->
  </div>
  <!-- /.modal -->

  <!-- art-template 模板引擎语法 -->
  <script type="text/html" id="tblTpl">
    {{%
    <tr>
      <td>序号</td>
      <td>标题</td>
      <td>路径</td>
      <td>描述</td>
      <td>创建时间</td>
      <td>类型</td>
      <td>操作</td>
    </tr>
    {{each data as value i}}
    <tr data-value="{{toJson(value)}}" >
      <td>{{(page-1)*rows+ i+1 }}</td>
      <td>{{ value.title }}</td>
      <td><img src="{{value.src}}" alt=""></td>
        <td>{{  value.des }}</td>
        <td>{{  value.createTime }}</td>
        <td>{{  toType(value.type) }}</td>
        <td><button class="btn btn-warning dataEditBtn " data-id="{{value.id}}" >编辑</button> <button  data-id="{{value.id}}" class="btn btn-danger dataDelBtn" >删除</button> </td>
        </tr>
      {{/each}}
      %}}
  </script>
  <script src="lib/jquery.min.js"></script>
  <script src="lib/bootstrap/js/bootstrap.min.js"></script>
  <!-- 分页插件 -->
  <script src="lib/bootstrap-paginator.min.js"></script>
  <script src="lib/template.js"></script>
  <script src="js/common.js"></script>
  <script src="js/user.js"></script>
</body>
</html>

前端 user.css

table img {
    width: 100%;
    max-width: 100px;
}

.mask {
    position: fixed;
    width: 100%;
    height: 100%;
    opacity: .5;
    background-color: #000;
    z-index: 100000;
}

.mask::after {
    display: block;
    content: "";
    position: absolute;
    width: 10px;
    height: 10px;
    border: 1px solid #fff;
    background-color: blue;
    border-radius: 50%;
    left: 50%;
    top: 50%;
    margin-left: -50px;
    margin-top: -50px;
    transform-origin: 45px 45px;
    animation: ani_loading .5s infinite linear;
}

@keyframes ani_loading {
    0% {}
    100% {
        transform: rotate(360deg);
    }
}

前端 user.js


$(function () {
  // 查询参数
  var QueryPage = {
    page: 1,
    rows: 5,
    title: ""
  };
  // 操作类型
  // 0 新增 1 编辑 2 删除 默认为0 新增
  var ManagerType = 0;
  // 总页数
  var TotalPages = 1;
  // 要删除数据的id
  var DelID = -1;
  getList(setPage);
  function getList(callback) {
    QueryPage.title = $.trim($(".searchInp").val());
    $.get("users/getList", QueryPage, function (result) {
      // 计算总页数
      TotalPages = Math.ceil(result.total / QueryPage.rows);
      var html = template("tblTpl", result);
      $("table").html(html);
      callback && callback();
    })
  }
  function setPage(params) {
    // bs 分页插件设置
    var options = {
      bootstrapMajorVersion: 3,
      currentPage: QueryPage.page,//当前页面  
      numberOfPages: QueryPage.rows,//一页显示几个按钮(在ul里面生成5个li)  
      totalPages: TotalPages,//总页数  
      onPageClicked: function (event, originalEvent, type, page) {
        QueryPage.page = page;
        getList(setPage);
      }
    }
    $(".pagination").bootstrapPaginator(options);
  }



  // 搜索
  $(".searchBtn").click(function (params) {
    QueryPage.page = 1;
    getList(setPage);
    // 点击搜索后 设置分页插件自动回到第一页
    $(".pagination").bootstrapPaginator("showFirst");
  })

  // 选择图片后,在前端即时预览
  $("#fileInp").change(function (e) {
    // console.log(e);
    var file = e.target.files[0];
    var fr = new FileReader();
    fr.readAsDataURL(file);
    fr.onload = function () {
      $("#src").attr("src", fr.result);
    }
  })

  // 编辑
  $("table ").on("click", ".dataEditBtn", function (e) {
    /* 
    0 存入当前操作提示 编辑 ManagerType=1 
    1 弹出对话框
    2 填充表单
    3 去除 文件上传按钮 required属性
     */
    ManagerType = 1;
    $("#myModal").modal("show");

    // 获取被操作的行上的元数据 data-value
    //parentsUntil  查找当前元素的所有的父辈元素,直到遇到匹配的那个元素为止。 包含了下面所有找到的父辈元素,但不包括那个选择器匹配到的元素。 
    var obj = $(e.target).parents("tr").data("value");

    // 2 填充数据
    $("input[name='title']").val(obj.title);
    $("input[name='id']").val(obj.id);
    // $("input[name='src']").val(obj.src);
    $("[name='des']").val(obj.des);
    $("[name='type']").val(obj.type);
    // 图片按钮
    $("input[name='srcImage']").attr("src", obj.src);

    // 3 
    // $("input[type='src']").removeAttr("required");
    // 可能存在编辑文件时 没重新提交图片 所以去除验证
    $("#fileInp").removeAttr("required");
  })

  // 新增
  $("#add").click(function (e) {
    /* 
    1 操作类型
    2 弹出对话框
    3 清空表单
    4 添加文件上传按钮 required属性
     */
    ManagerType = 0;
    $("#myModal").modal("show");
    $("input[name='src']").attr("required", "");
    $("#ff")[0].reset();
    // 清除图片路径的隐藏于
    $("input[name='src']").val("");
    // 清除图片按钮
    $("input[type='image']").attr("src", "");
    $("input[type='image']").val("");
  })

  // 提交
  $("#ff").submit(function (e) {
    /* 
    1 发送了3个ajax a 请求上传图片的token b 上传到七牛 获取图片链接 c 将数据插入到后台
     */
    e.preventDefault();

    // formData.append("src", $("input[name='src']").val());
    // 判断操作类型 0 为新增  1 为编辑 2 为删除
    if (ManagerType == 0) {
      // 获取最新的token
      $.get("/qiniu/token", function (token) {
        var file = $("#fileInp")[0].files[0];
        // 构造formdata 上传到七牛使用
        var f = new FormData();
        f.append("key", Date.now() + file.name);
        f.append("token", token.uptoken);
        f.append("file", file);
        $.ajax({
          url: "http://upload.qiniup.com/",
          type: "POST",
          data: f,
          cache: false,
          // 不用jq处理
          processData: false,
          contentType: false,
          success: function (r) {
            // console.log(r);
            // 外链名字
            var namepace = "http://p1935eslc.bkt.clouddn.com/";
            // 图片的完整名字
            var imgPath = namepace + r.key;
            // 构造参数,添加到后台
            $("input[name='src']").val(imgPath);
            var formData = new FormData($("#ff")[0]);
            $.ajax({
              url: "/users/add",
              type: "POST",
              data: formData,
              cache: false,
              // 不用jq处理
              processData: false,
              contentType: false,
              success: function (result) {
                // console.log(e);
                // 关闭对话框 刷新数据列表
                $("#myModal").modal("hide");
                QueryPage = {
                  page: 1,
                  rows: 5
                };
                getList(setPage);
              },
              error: function (e) {
                // console.log(e);
              }
            });
          }
        });
      });
    } else if (ManagerType == 1) {
      // 编辑
      var formData = new FormData($("#ff")[0]);
      $.ajax({
        url: "/users/edit",
        type: "POST",
        data: formData,
        cache: false,
        // 不用jq处理
        processData: false,
        contentType: false,
        success: function (result) {
          // console.log(e);
          // 关闭对话框 刷新数据列表
          $("#myModal").modal("hide");
          QueryPage = {
            page: 1,
            rows: 5
          };
          getList(setPage);
        },
        error: function (e) {
          // console.log(e);
        }
      })
    }
  });

  // 删除
  $("table").on("click", ".dataDelBtn", function (e) {
    var id = $(e.target).parents("tr").data("value").id;
    DelID = id;
    $("#deleteModal").modal("show");
  });
  // 提交删除
  $(".delSubBtn").click(function (e) {
    /* 
    1 发送删除请求
    2 关闭对话框
    3 刷新数据
     */
    $.get("users/del?id=" + DelID, function (result) {
      $("#deleteModal").modal("hide");
      if (result.status == 0) {
        // 成功
        QueryPage.page = 1;
        getList(setPage);
      }
    })
  })
})

// 显示进度条
$(window).ajaxStart(function () {
  $(".mask").show();
})
// 隐藏进度条
$(window).ajaxStop(function () {
  $(".mask").hide();
})

注意

整个demo download下后,需要修改的地方有

数据库连接代码 在app.js中

七牛的密钥等,在router/qiniu.js 中

环境: nodejs 8.3.0 express 4.x 其他 都是最新版本.

如有错误,联系方式 yeah126139163@163.com

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要安装和配置Node.js v13.14.0,您可以按照以下步骤操作: 1. 访问Node.js官方网站(https://nodejs.org),然后点击下载页面上的"LTS"按钮。LTS代表长期支持版本,这意味着它是一个稳定且经过测试的版本。 2. 在下载页面上,选择适合您操作系统的安装包。对于Windows用户,通常会选择msi或exe文件;对于macOS用户,则是pkg文件。 3. 下载完成后,运行安装程序并按照指示进行安装。在安装期间,您可以选择自定义选项,例如更改默认安装位置等。 4. 安装完成后,打开终端(对于Windows用户,是命令提示符或PowerShell),然后运行以下命令来验证Node.js是否正确安装: ``` node -v ``` 如果您看到输出版本号为v13.14.0,那么说明安装成功。 5. 配置Node.js的环境变量(Windows用户):在开始菜单中搜索"环境变量",然后选择"编辑系统环境变量"。在系统属性对话框中,点击"环境变量"按钮。在"系统变量"部分,找到名为"Path"的变量,并点击"编辑"。在弹出的编辑环境变量对话框中,点击"新建"并输入Node.js安装的路径(例如:"C:\Program Files\nodejs")。点击"确定"保存更改。 6. 配置Node.js的环境变量(macOS用户):打开终端,然后运行以下命令来编辑bash配置文件: ``` nano ~/.bash_profile ``` 在打开的文件中,添加以下行: ``` export PATH="/usr/local/bin:$PATH" ``` 使用Ctrl+X保存文件,然后按Y确认保存。 7. 现在,关闭并重新打开终端(或命令提示符/PowerShell),然后再次运行以下命令来验证环境变量是否正确配置: ``` node -v ``` 如果您看到输出版本号为v13.14.0,那么说明环境配置成功。 恭喜您成功安装和配置了Node.js v13.14.0!现在您可以开始使用Node.js进行开发了。如有任何问题,请随时向我提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值