Vue开发一个电商全栈项目

var router = express.Router();

var Classify = require(‘…/models/classify’);

//添加商品分类 /classify/add

router.post(‘/add’, function(req,res){

//接收参数

let {name} = req.body

//保存到数据库

Classify.create({name}).then(result=>{

if(result){

//添加成功后的响应

res.json({

code: 200, //自定义标识,不是状态码

msg: ‘添加成标识功’

})

}else{

res.json({

code: 300,

msg: ‘添加失败’

})

}

}).catch(err=>{

res.json({

code: 400,

msg: ‘添加时出现异常’

})

})

})

//修改分类

router.post(‘/update’, function(req,res){

let C = req.body

if(!c._id){

res.json({

code:300,

msg:‘id参数不能为空!’

})

return

}

crud.update(Classify,{_id:c._id}, {name: c.name,no: c.no},res )

})

//删除分类

router.get(‘/de1’ , function(req,res){

let {_id} = req.query

if(!_id){

res.json({

code:300,

msg:‘id参数不能为空!’

})

return

}

crud.del(Classify,{_id},res)

})

//分页查询

router.get(‘/find’, function(req,res){

//let {page,pagesize,kw,s} = req.query

//let where = {}

//if(kw){

// where = {name: {$regex: kw}}

//}

//let sort = {}

//if(s == 1){

// sort = {no: -1}

//}else if(s == 2){

// sort = {no: 1}

//}

//crud-find(classify,page, pegeSize,where,sart ,res)

let {page,pagesize,sort = 1} = req.query //sort 排序 1降序 2升序,有值的话按值,没值的话按默认值

//s为排序的条件对象

let s = {}

if(sort == 1){

s = {no: -1}

}else if(sort == 2){

s = {no: 1}

}

crud.find(Classify,page, pegeSize,{},s ,res)

})

//非分页查询

router.get(‘/query’, function(req,res){

let {sort = 1} = req.query //sort 排序 1降序 2升序,有值的话按值,没值的话按默认值

//s为排序的条件对象

let s = {}

if(sort == 1){

s = {no: -1}

}else if(sort == 2){

s = {no: 1}

}

crud.query(Classify,{},s ,res)

})

module.exports = router;

在这里插入图片描述

第二步:

app.js 入口文件引入路由,示例代码如下:

//引入路由文件

var classifyRouter = require(‘./routes/classify’);

//配置一个路由

app.use(‘/classify’, classifyRouter);

第三步:在 postmain 工具中测试路由是否可用:

在这里插入图片描述

请求的路由地址

http://localhost:3000/classify/add

请求参数

{

“name”: “男装”

}

添加成功的响应结果

{

code: 200,

msg: ‘添加成功’

}

在这里插入图片描述

在这里插入图片描述

3.4、封装CRUD(封装好的可以直接拿出来用)

公共方法,写一遍不用动直接就可以用

crud/index.js 中封装增删改查的方法,示例代码如下:

添加的功能方法:

/**

  • 用于添加数据的公共方法(文档注释 可以一键生成API文档 ,两个*加@ 是竹节)【服务端才有】

  • @param {*} model 数据模型对象

  • @param {*} params 要添加的数据对象

  • @param {*} res 响应对象

*/

function add(model,params,res){

model.create(params).then(result=>{

if(result){

//添加成功后的响应

res.json({

code: 200,

msg: ‘添加成功’

})

}else{

res.json({

code: 300,

msg: ‘添加失败’

})

}

}).catch(err=>{

res.json({

code: 400,

msg: ‘添加时出现异常’

})

})

}

修改的公共方法:

/**

  • 用于修改的公共方法

  • @param {*} model 数据模型对象

  • @param {*} where 要修改的条件

  • @param {*} params 修改后的新对象

  • @param {*} res 响应对象

*/

function update(model,where,params,res){

// 修改的时候传入两个条件(修改的条件,修改的值) 返回一个result

model.updateOne(where,params).then(result=>{

//result 里面放的是修改结果的对象

//result里面有个对象 n ,n>0 证明修改成功了很多条,n是几就是几条

//else 就是修改失败

//catch 就是修改时出现异常

if(result.n > 0){

res.json({

code: 200,

msg: ‘修改成功’

})

}else{

res.json({

code: 300,

msg: ‘修改失败’

})

}

}).catch(err=>{

res.json({

code: 400,

msg: ‘修改时出现异常’

})

})

}

删除的公共方法:

/**

  • 删除的公共方法

  • @param {*} model 数据模型对象

  • @param {*} where 要删除的条件

  • @param {*} res 响应对象

*/

function del(model,where,res){

model.findOneAndDelete(where).then(result=>{

if(result){

//添加成功后的响应

res.json({

code: 200,

msg: ‘删除成功’

})

}else{

res.json({

code: 300,

msg: ‘删除失败’

})

}

}).catch(err=>{

res.json({

code: 400,

msg: ‘删除时出现异常’

})

})

}

分页查询公共方法:(与上面不分页查询按需要选其一即可)

/**

  • 用于分页查询的公共方法

  • @param {*} model 模型对象

  • @param {*} page 当前页码

  • @param {*} pageSize 每页条数

  • @param {*} where 查询条件

  • @param {*} sort 排序条件

  • @param {*} res 响应对象

*/

async function find(model,page,pageSize,where,sort,res){

//判断page是否存在

if(!page){

page = 1

}else{

page = parseInt(page)

if(isNaN(page)){ //非数字

page = 1

}else{ //是数字

if(page < 1){

page = 1

}

}

}

//判断pageSize是否为空

if(!pageSize){

pageSize = 10

}else{

//默认每页10条,如果进行赋值,每页最低每5条换页,最多不超过30条换页

pageSize = parseInt(pageSize)

if(isNaN(pageSize)){ //非数字

pageSize = 10

}else{ //是数字

if(pageSize < 5){

pageSize = 5

}else if(pageSize > 30){

pageSize = 30

}meyiye

}

}

//计算总页数,总页数 = Math.ceil(总记录数 ÷ 每页条数)

let count = 0

await model.find(where).countDocuments().then(result=>{

count = result

})

// await model.find(where).count().then(result=>{

// count = result

// })

//如果上一步还没执行完,就执行下一步,就会出错,所以要把异步变同步,所以要在上一步 和 执行查询操作 前面加上 await

//总页数 totalPage

let totalPage = Math.ceil(count/pageSize)

//判断当前页码是否大于总页数

//如果一条都没有的话,count为0 所以totalPage也会为0(0/每页条数 还为0)

if(totalPage > 0 && page > totalPage){

page = totalPage

}

//skip查询的起始位置,起始位置 = (当前页码 - 1)× 每页条数

let start = (page - 1)*pageSize

//执行查询操作

//跳过多少条 skip从哪里开始看 limit看多少条

await model.find(where).sort(sort).skip(start).limit(pageSize).then(result=>{

if(result && result.length > 0){

res.json({

code: 200,

msg: ‘查询成功’,

data: result,

page,

pageSize,

count,

totalPage

})

}else{

res.json({

code: 300,

msg: ‘没有查询到数据’,

data: [],

page,

pageSize,

count,

totalPage

})

}

}).catch(err=>{

res.json({

code: 400,

msg: ‘查询时出现异常’

})

})

}

非分页查询公共方法:(与下面分页查询按需要选其一即可)

/**

  • 用于分页查询的公共方法

  • @param {*} model 模型对象

  • @param {*} where 查询条件

  • @param {*} sort 排序条件

  • @param {*} res 响应对象

*/

function query(model,where,sort,res){

model.query(where).sort(sort).then(result=>{

if(result && result.length > 0){

res.json({

code: 200,

msg: ‘查询成功’,

data: result

})

}else{

res.json({

code: 300,

msg: ‘没有查询到数据’,

data: []

})

}

}).catch(err=>{

res.json({

code: 400,

msg: ‘查询时出现异常’

})

})

}

需要在 crud/index.js 文件中抛出以上函数:

module.exports = {

add, //添加

update, //修改

del, //删除

find, //分页查询

query //非分页查询

}

3.5、 将来实现一个功能模块(上面理解,这个要会用)

实现一个用户有关的模块 (模型对象,路由在app中的引用,封装好的,写完不用再改,唯一修改的就是,有新的功能写一个新的二级路由)

第一步:创建模型、在models文件下创建一个users.js模块

我们要设计一个用户模块,就创建 models/users.js 文件,示例代码如下:

(与其他模块不一样的地方,需要改的就是schema里面的属性;集合的名称 users 改成对应的以及抛出对象的名称)

//引入

var mongoose = require(‘mongoose’)

/**

  • 创建用户的模型对象

*/

let schema = new mongoose.Schema({ // schema 用于定义和数据库中字段相同的属性名称和数据类型

username: String , //用户的名称

pwd: String //用户密码

})

// users 将来数据库中叫的名字 最后一个字母必须s

let Users= mongoose.model(‘users’, schema)

//抛出

module.exports = Users

第二步:创建路由、

routes文件下创建一个users.js文件

//通过 express 导入一个路由

var express = require(‘express’);

var router = express.Router();

//抛出一个路由

module.exports = router;

app.js 入口文件引入路由,示例代码如下:

//引入路由文件

var usersRouter = require(‘./routes/users’);

//配置一级路由

app.use(‘/users’, usersRouter );//确定有没有引入成功,可以按住 Ctrl 鼠标放在 usersRouter 上呈现小手状,点击直接跳转到路由界面,能定位到,说明引入成功

第三步:功能接口,在routes/users.js中(如果其他模块直接改掉 Users 对应部分)

//var express = require(‘express’); 这是第一步的内容保留,这里只是为了展示写入位置

//var router = express.Router(); 这是第一步的内容保留,这里只是为了展示写入位置

//保存到数据库里面,导入Users

var Users = require(‘…/models/users’);

//存储数据库的方法全在 crud 里封装着

var crud = require(‘…/crud’);

//与crud里面对应

//添加用户

//通过 post 或 get , /users 后面 /add 找到这个方法

//服务端路由,就是通过 url地址与一个 函数 之间的 映射

router.post(‘/add’, function(req,res){

//接收参数 只要是post请求 就用body封装

let {username,pwd} = req.body

//调用自己封装的方法 .add( 放现在操作的模型,添加的数据就是{username=username 直接写username, pwd=pwd直接写pwd },res响应)

crud.add(Users,{username,pwd},res) //可以按住 Ctrl 鼠标放在 add 上呈现小手状,点击直接跳转查看封装思维

//或者写成

//let user = req.body

//crud.add(Users,user,res)

})

//修改用户

router.post(‘/update’, function(req,res){

//接收参数 只要是post请求 就用body

//user 整个对象

let user = req.body

//数组里只有ID是唯一修改的条件

crud.update(Users,{_id:user._id},{

username:user.username,

pwd:user.pwd

},res)

})

//删除用户

router.get(‘/del’,function(req ,res){

let {_id} = req.query

crud.del(Users,{_id},res)

})

//分页查询

router.get(‘/find’ , function(req,res){

let {page,pageSize} = req.query

//目前没有 查询条件, 排序 不需要 所以{}

crud.find(Users,page,pageSize,{},{},res)

})

//module.exports = router; 这是第一步的内容保留,这里只是为了展示写入位置

3.6、接口规范

全局配置:

服务器IP与端口(根据实际更改)

baseURL: http://localhost:3000

3.6.1、分类管理模块

添加分类:

接口地址(路由,发请求):/classify/add

请求方法:POST

返回数据格式:JSON

请求参数:

| 参数名称 | 参数类型 | 是否必填 | 参数描述 |

| — | — | — | — |

| name | String | 是 | 分类的名称 |

| no | Number | 否,默认值0 | 分类的排序 |

响应参数:

| 参数名称 | 参数类型 | 参数示例 | 参数描述 |

| — | — | — | — |

| code | Number | 200 | 自定义,请求的结果状态,200为成功,300为失败,400为服务端异常 |

| msg | String | “添加成功” | 请求的结果描述 |

修改分类:

接口地址(路由,发请求):/classify/update

请求方法:POST

返回数据格式:JSON

请求参数:

| 参数名称 | 参数类型 | 是否必填 | 参数描述 |

| — | — | — | — |

| _id | String | 是 | 要修改的分类id |

| name | String | 否 | 分类的新名称 |

| no | Number | 否,默认值0 | 分类的新排序 |

响应参数:

| 参数名称 | 参数类型 | 参数示例 | 参数描述 |

| — | — | — | — |

| code | Number | 200 | 自定义,请求的结果状态,200为成功,300为失败,400为服务端异常 |

| msg | String | “修改成功” | 请求的结果描述 |

删除分类:

接口地址(路由,发请求):/classify/del

请求方法:GET

返回数据格式:JSON

请求参数:

| 参数名称 | 参数类型 | 是否必填 | 参数描述 |

| — | — | — | — |

| _id | String | 是 | 分类的id |

响应参数:

| 参数名称 | 参数类型 | 参数示例 | 参数描述 |

| — | — | — | — |

| code | Number | 200 | 自定义,请求的结果状态,200为成功,300为失败,400为服务端异常 |

| msg | String | “删除成功” | 请求的结果描述 |

分页查询分类:

接口地址:/classify/find

请求方法:GET

返回数据格式:JSON

请求参数:

| 参数名称 | 参数类型 | 是否必填 | 参数描述 |

| — | — | — | — |

| page | Number | 否,默认值1 | 当前页码 |

| pageSize | Number | 否,默认值10 | 每页查询条数,最少5条,最多30条 |

| sort | Number | 否,默认值1 | 按no字段的排序规则,1为降序,2为升序 |

响应参数:

| 参数名称 | 参数类型 | 参数示例 | 参数描述 |

| — | — | — | — |

| code | Number | 200 | 请求的结果状态,200为成功,300为失败,400为服务端异常 |

| msg | String | “查询成功” | 请求的结果描述 |

| data | any | [{name:’’,no:’’},{}] | 请求成功返回的数据 |

| page | Number | 1 | 当前访问的页码 |

| pageSize | Number | 10 | 每页返回条数 |

| count | Number | 2000 | 总记录数 |

| totalPage | Number | 50 | 总页数 |

非分页查询所有分类:

接口地址:/classify/query

请求方法:GET

返回数据格式:JSON

请求参数:

| 参数名称 | 参数类型 | 是否必填 | 参数描述 |

| — | — | — | — |

| sort | Number | 否,默认值1 | 按no字段的排序规则,1为降序,2为升序 |

响应参数:

| 参数名称 | 参数类型 | 参数示例 | 参数描述 |

| — | — | — | — |

| code | Number | 200 | 请求的结果状态,200为成功,300为失败,400为服务端异常 |

| msg | String | “查询成功” | 请求的结果描述 |

| data | any | [{name:’’,no:’’},{}] | 请求成功返回的数据 |

3.6.2、商品管理模块

同上

3.6.3、数据统计模块

同上

4、后台管理系统开发


4.1、设计路由

router/index.js 中设计路由

import Vue from ‘vue’

import VueRouter from ‘vue-router’

import routes from ‘./routes’ //将路由的模块进行封装

Vue.use(VueRouter)

const router = new VueRouter({

mode: ‘history’,

base: process.env.BASE_URL,

routes

})

export default router

router/routes.js 文件代码示例:

//路由对象

const routes = [

{

path: ‘/’,

component: ()=>import(‘@/views/Main’),

redirect: ‘/home’, //默认到home

children: [

{

path: ‘/home’, //控制台 数据统计

component: ()=>import(‘@/views/Home’)

},

{

path: ‘/classify’, //分类管理

component: ()=>import(‘@/views/Classify’)

},

{

path: ‘/goods/add’, //发布商品

component: ()=>import(‘@/views/Goods/add.vue’)

},

{

path: ‘/goods/list/:state’, //当前的状态,1上架,2下架

component: ()=>import(‘@/views/Goods/list.vue’)

}

]

}

]

export default routes

在这里插入图片描述

封装全局的前置守卫,router/beforeEach.js 文件代码示例:

import router from ‘./index’

/**

  • 路由的前置守卫

*/

export default ()=>{

router.beforeEach((to,from,next)=>{

console.log(‘beforeEach…’)

next()

})

}

全局守卫封装好之后,需要在 main.js 入口文件调用守卫方法:

import Vue from ‘vue’

import App from ‘./App.vue’

import router from ‘./router’

import store from ‘./store’

import ElementUI from ‘element-ui’;

import ‘element-ui/lib/theme-chalk/index.css’;

import beforeEach from ‘./router/beforeEach’ //导入全局守卫模块

Vue.use(ElementUI);

Vue.config.productionTip = false

beforeEach() //调用守卫方法

new Vue({

router,

store,

render: h => h(App)

}).$mount(‘#app’)

4.2、封装axios请求

创建 src/http/index.js 文件,代码示例如下:

var axios = require(‘axios’)

//创建axios实例

var instance = axios.create({

baseURL: ‘http://localhost:3000’

})

//请求拦截

instance.interceptors.request.use(config=>{

// console.log(‘请求拦截…’,config)

return config

},err=>{

console.error(‘请求时异常’, err)

return

})

//响应拦截

instance.interceptors.response.use(res=>{

//请求成功对响应数据做处理

// console.log(‘响应拦截’, res)

return res //该返回对象会传到请求方法的响应对象中

},err=>{

// 响应错误处理

console.error(‘响应时异常’, err)

return

})

/**

  • 用于发送HTTP请求的方法

  • @param {*} option 发送请求的配置项

  • @returns 响应结果

*/

async function http({path,method,params}){

let result

//get、delete

if(method == ‘get’ || method == ‘delete’){

await instance[method](path,{

params

}).then(res=>{

result = res.data

}).catch(err=>{

result = err

})

}else if(method == ‘post’ || method == ‘put’ || method == ‘patch’){

await instancemethod.then(res=>{

result = res.data

}).catch(err=>{

result = err

})

}

return result

}

export default http

main.js 文件中引入 http 模块:

import Vue from ‘vue’

import App from ‘./App.vue’

import router from ‘./router’

import store from ‘./store’

import http from ‘./http’ //引入 http 模块

Vue.prototype.$http = http //将http函数挂载到Vue的实例上

new Vue({

router,

store,

render: h => h(App)

}).$mount(‘#app’)

在组件中使用 http 模块:

4.3、文件上传

4.3.1、设计服务端文件上传接口

安装相关依赖

cnpm i multer --save

创建 routes/upload.js 路由文件,示例代码如下:

var express = require(‘express’);

var router = express.Router();

var multer = require(‘multer’);

var fs = require(‘fs’);

var path = require(‘path’);

//使用表单上传

var upload = multer({

storage: multer.diskStorage({

//设置文件存储位置

destination: function(req, file, cb) {

let date = new Date();

let year = date.getFullYear();

let month = (date.getMonth() + 1).toString().padStart(2, ‘0’);

let day = date.getDate();

let dir = “./public/img/” + year + month + day;

//判断目录是否存在,没有则创建

if (!fs.existsSync(dir)) {

fs.mkdirSync(dir, {

recursive: true

});

}

//dir就是上传文件存放的目录

cb(null, dir);

},

//设置文件名称

filename: function(req, file, cb) {

let fileName = file.fieldname + ‘-’ + Date.now() + path.extname(file.originalname);

//fileName就是上传文件的文件名

cb(null, fileName);

}

})

})

//文件上传的路由

router.post(‘/img’, upload.single(“mainPic”), function(req,res){

let file = req.file

let path = file.path.replace(‘public’,“”)

// console.log(path)

res.json({

file: path

})

} )

module.exports = router;

app.js 入口文件中引入路由

var uploadRouter = require(‘./routes/upload’);

app.use(‘/upload’, uploadRouter);

服务端上传接口地址: http://localhost:3000/update/img

4.3.2、设计前端上传组件

前端以 elementui 为例。

使用 <el-upload> 上传组件,示例代码如下:

<el-upload action=“http://localhost:3000/upload/img”

name=“mainPic”

:on-success=“uploadSuccess”

:on-error=“uploadError”

:before-upload=“uploadBefore”

上传

数据结构与算法

这一块在笔试、面试的代码题中考核较多,其中常考的数据结构主要有:数组、链表、队列、栈、Set、Map、哈希表等,不同数据结构有不同的方法以及储存原理,这些算是技术岗的必备知识。算法部分主要分为两大块,排序算法与一些其他算法题

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

排序算法根据考频高低主要有:快速排序、归并排序、堆排序、冒泡排序、插入排序、选择排序、希尔排序、桶排序、基数排序、Timsort这十种,这类考核点要么是算法的时间、空间复杂度、稳定度,要么是直接手写代码,故在理解算法原理的同时,对JS语言版的排序算法代码也要加强记忆。

  • 二叉树层序遍历
  • B 树的特性,B 树和 B+树的区别
  • 尾递归
  • 如何写一个大数阶乘?递归的方法会出现什么问题?
  • 把多维数组变成一维数组的方法
  • 知道的排序算法 说一下冒泡快排的原理
  • Heap 排序方法的原理?复杂度?
  • 几种常见的排序算法,手写
  • 数组的去重,尽可能写出多个方法
  • 如果有一个大的数组,都是整型,怎么找出最大的前 10 个数
  • 知道数据结构里面的常见的数据结构
  • 找出数组中第 k 大的数组出现多少次,比如数组【1,2, 4,4,3,5】第二大的数字是 4,出现两次,所以返回 2
  • 合并两个有序数组
  • 给一个数,去一个已经排好序的数组中寻找这个数的位 置(通过快速查找,二分查找)

创建

if (!fs.existsSync(dir)) {

fs.mkdirSync(dir, {

recursive: true

});

}

//dir就是上传文件存放的目录

cb(null, dir);

},

//设置文件名称

filename: function(req, file, cb) {

let fileName = file.fieldname + ‘-’ + Date.now() + path.extname(file.originalname);

//fileName就是上传文件的文件名

cb(null, fileName);

}

})

})

//文件上传的路由

router.post(‘/img’, upload.single(“mainPic”), function(req,res){

let file = req.file

let path = file.path.replace(‘public’,“”)

// console.log(path)

res.json({

file: path

})

} )

module.exports = router;

app.js 入口文件中引入路由

var uploadRouter = require(‘./routes/upload’);

app.use(‘/upload’, uploadRouter);

服务端上传接口地址: http://localhost:3000/update/img

4.3.2、设计前端上传组件

前端以 elementui 为例。

使用 <el-upload> 上传组件,示例代码如下:

<el-upload action=“http://localhost:3000/upload/img”

name=“mainPic”

:on-success=“uploadSuccess”

:on-error=“uploadError”

:before-upload=“uploadBefore”

上传

数据结构与算法

这一块在笔试、面试的代码题中考核较多,其中常考的数据结构主要有:数组、链表、队列、栈、Set、Map、哈希表等,不同数据结构有不同的方法以及储存原理,这些算是技术岗的必备知识。算法部分主要分为两大块,排序算法与一些其他算法题

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

排序算法根据考频高低主要有:快速排序、归并排序、堆排序、冒泡排序、插入排序、选择排序、希尔排序、桶排序、基数排序、Timsort这十种,这类考核点要么是算法的时间、空间复杂度、稳定度,要么是直接手写代码,故在理解算法原理的同时,对JS语言版的排序算法代码也要加强记忆。

  • 二叉树层序遍历
  • B 树的特性,B 树和 B+树的区别
  • 尾递归
  • 如何写一个大数阶乘?递归的方法会出现什么问题?
  • 把多维数组变成一维数组的方法
  • 知道的排序算法 说一下冒泡快排的原理
  • Heap 排序方法的原理?复杂度?
  • 几种常见的排序算法,手写
  • 数组的去重,尽可能写出多个方法
  • 如果有一个大的数组,都是整型,怎么找出最大的前 10 个数
  • 知道数据结构里面的常见的数据结构
  • 找出数组中第 k 大的数组出现多少次,比如数组【1,2, 4,4,3,5】第二大的数字是 4,出现两次,所以返回 2
  • 合并两个有序数组
  • 给一个数,去一个已经排好序的数组中寻找这个数的位 置(通过快速查找,二分查找)

[外链图片转存中…(img-3x7avZy1-1714372782049)]

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值