Vue移动端系列 => [07] 文章详情
七、文章详情
创建组件并配置路由
1、创建 views/article/index.vue
组件
<template><div class="article-container">文章详情</div>
</template><script>
export default {name: 'ArticleIndex',components: {},props: {articleId: {type: [Number, String],required: true}},data () {return {}},computed: {},watch: {},created () {},mounted () {},methods: {}
}
</script><style scoped lang="less"></style>
2、然后将该页面配置到根级路由
{path: '/article/:articleId',name: 'article',component: () => import('@/views/article'),// 将路由动态参数映射到组件的 props 中,更推荐这种做法props: true
}
3、通过props获取路由参数
props: {articleId: {type: [Number, String],required: true}}
官方文档:路由 props 传参
在我的article-list-item里面配置
这里面click直接点击没有用,需要click.native因为模板没有原生点击事件
<artilce-list-itemv-for="item in list":key="item.art_id":articleItem="item"@click.native="fn(item.art_id)"/>fn(id) {// console.log(id);this.$router.push(`/article/${id}`);},
页面布局
使用到的 Vant 中的组件:
NavBar 导航栏
Loading 加载
Cell 单元格
Button 按钮
Image 图片
Divider 分割线
Icon 图标
<template><div class="article-container"><!-- 导航栏 --><van-nav-barclass="page-nav-bar"left-arrowtitle="头条"></van-nav-bar><!-- /导航栏 --><div class="main-wrap"><!-- 加载中 --><div class="loading-wrap"><van-loadingcolor="#3296fa"vertical>加载中</van-loading></div><!-- /加载中 --><!-- 加载完成-文章详情 --><div class="article-detail"><!-- 文章标题 --><h1 class="article-title">这是文章标题</h1><!-- /文章标题 --><!-- 用户信息 --><van-cell class="user-info" center :border="false"><van-imageclass="avatar"slot="icon"roundfit="cover"src="https://img.yzcdn.cn/vant/cat.jpeg"/><div slot="title" class="user-name">头条号</div><div slot="label" class="publish-date">14小时前</div><van-buttonclass="follow-btn"type="info"color="#3296fa"roundsize="small"icon="plus">关注</van-button><!-- <van-buttonclass="follow-btn"roundsize="small">已关注</van-button> --></van-cell><!-- /用户信息 --><!-- 文章内容 --><div class="article-content">这是文章内容</div><van-divider>正文结束</van-divider></div><!-- /加载完成-文章详情 --><!-- 加载失败:404 --><div class="error-wrap"><van-icon name="failure" /><p class="text">该资源不存在或已删除!</p></div><!-- /加载失败:404 --><!-- 加载失败:其它未知错误(例如网络原因或服务端异常) --><div class="error-wrap"><van-icon name="failure" /><p class="text">内容加载失败!</p><van-button class="retry-btn">点击重试</van-button></div><!-- /加载失败:其它未知错误(例如网络原因或服务端异常) --></div><!-- 底部区域 --><div class="article-bottom"><van-buttonclass="comment-btn"type="default"roundsize="small">写评论</van-button><van-iconname="comment-o"info="123"color="#777"/><van-iconcolor="#777"name="star-o"/><van-iconcolor="#777"name="good-job-o"/><van-icon name="share" color="#777777"></van-icon></div><!-- /底部区域 --></div>
</template><script>
export default {name: 'ArticleIndex',components: {},props: {articleId: {type: [Number, String],required: true}},data () {return {}},computed: {},watch: {},created () {},mounted () {},methods: {}
}
</script><style scoped lang="less">
.article-container {.main-wrap {position: fixed;left: 0;right: 0;top: 92px;bottom: 88px;overflow-y: scroll;background-color: #fff;}.article-detail {.article-title {font-size: 40px;padding: 50px 32px;margin: 0;color: #3a3a3a;}.user-info {padding: 0 32px;.avatar {width: 70px;height: 70px;margin-right: 17px;}.van-cell__label {margin-top: 0;}.user-name {font-size: 24px;color: #3a3a3a;}.publish-date {font-size: 23px;color: #b7b7b7;}.follow-btn {width: 170px;height: 58px;}}.article-content {padding: 55px 32px;/deep/ p {text-align: justify;}}}.loading-wrap {padding: 200px 32px;display: flex;align-items: center;justify-content: center;background-color: #fff;}.error-wrap {padding: 200px 32px;display: flex;flex-direction: column;align-items: center;justify-content: center;background-color: #fff;.van-icon {font-size: 122px;color: #b4b4b4;}.text {font-size: 30px;color: #666666;margin: 33px 0 46px;}.retry-btn {width: 280px;height: 70px;line-height: 70px;border: 1px solid #c3c3c3;font-size: 30px;color: #666666;}}.article-bottom {position: fixed;left: 0;right: 0;bottom: 0;display: flex;justify-content: space-around;align-items: center;box-sizing: border-box;height: 88px;border-top: 1px solid #d8d8d8;background-color: #fff;.comment-btn {width: 282px;height: 46px;border: 2px solid #eeeeee;font-size: 30px;line-height: 46px;color: #a7a7a7;}.van-icon {font-size: 40px;.van-info {font-size: 16px;background-color: #e22829;}}}
}
</style>
获取文章详情数据
1、封装请求文章详情方法
/*** 文章接口模块*/
import request from '@/utils/request'/*** 获取频道的文章列表*/
export const getArticleById = articleId => {return request({method: 'GET',url: '/app/v1_0/articles/' + articleId})
}
2、引入方法
import { getArticleById } from '@/api/article.js'
3、定义加载数据方法
async loadArtcileInfo () {try {const res = await getArticleById(this.articleId)console.log(res)} catch (err) {this.$toast('获取失败')}
}
关于后端返回数据中的大数字问题
之所以请求文章详情返回 404 是因为我们请求发送的文章 ID (article.art_id)不正确。
JavaScript 能够准确表示的整数范围在-2^53
到2^53
之间(不含两个端点),超过这个范围,无法精确表示这个值,这使得 JavaScript 不适合进行科学和金融方面的精确计算。
Math.pow(2, 53) // 90071992547409929007199254740992 // 9007199254740992
9007199254740993 // 9007199254740992Math.pow(2, 53) === Math.pow(2, 53) + 1
// true
上面代码中,超出 2 的 53 次方之后,一个数就不精确了。
ES6 引入了Number.MAX_SAFE_INTEGER
和Number.MIN_SAFE_INTEGER
这两个常量,用来表示这个范围的上下限。
Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1
// true
Number.MAX_SAFE_INTEGER === 9007199254740991
// trueNumber.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER
// true
Number.MIN_SAFE_INTEGER === -9007199254740991
// true
上面代码中,可以看到 JavaScript 能够精确表示的极限。
后端返回的数据一般都是 JSON 格式的字符串。
'{ "id": 9007199254740995, "name": "Jack", "age": 18 }'
如果这个字符不做任何处理,你能方便的获取到字符串中的指定数据吗?非常麻烦。所以我们要把它转换为 JavaScript 对象来使用就很方便了。
幸运的是 axios 为了方便我们使用数据,它会在内部使用 JSON.parse()
把后端返回的数据转为 JavaScript 对象。
// { id: 9007199254740996, name: 'Jack', age: 18 }
JSON.parse('{ "id": 9007199254740995, "name": "Jack", "age": 18 }')
可以看到,超出安全整数范围的 id 无法精确表示,这个问题并不是 axios 的错。
了解了什么是大整数的概念,接下来的问题是如何解决?
利用json-bigint处理大数字问题
json-bigint 是一个第三方包,它可以帮我们很好的处理这个问题。
使用它的第一步就是把它安装到你的项目中。
npm i json-bigint
下面是使用它的一个简单示例。
const jsonStr = '{ "art_id": 1245953273786007552 }'console.log(JSON.parse(jsonStr)) // 1245953273786007600
// JSON.stringify()// JSONBig 可以处理数据中超出 JavaScript 安全整数范围的问题
console.log(JSONBig.parse(jsonStr)) // 把 JSON 格式的字符串转为 JavaScript 对象// 使用的时候需要把 BigNumber 类型的数据转为字符串来使用
console.log(JSONBig.parse(jsonStr).art_id.toString()) // 1245953273786007552console.log(JSON.stringify(JSONBig.parse(jsonStr)))console.log(JSONBig.stringify(JSONBig.parse(jsonStr))) // 把 JavaScript 对象 转为 JSON 格式的字符串转
json-bigint 会把超出 JS 安全整数范围的数字转为一个 BigNumber 类型的对象,对象数据是它内部的一个算法处理之后的,我们要做的就是在使用的时候转为字符串来使用。
通过 Axios 请求得到的数据都是 Axios 处理(JSON.parse)之后的,我们应该在 Axios 执行处理之前手动使用 json-bigint 来解析处理。Axios 提供了自定义处理原始后端返回数据的 API:transformResponse
。
import axios from 'axios'import jsonBig from 'json-bigint'var json = '{ "value" : 9223372036854775807, "v2": 123 }'console.log(jsonBig.parse(json))const request = axios.create({baseURL: 'http://ttapi.research.itcast.cn/', // 接口基础路径// transformResponse 允许自定义原始的响应数据(字符串)transformResponse: [function (data) {try {// 如果转换成功则返回转换的数据结果return jsonBig.parse(data)} catch (err) {// 如果转换失败,则包装为统一数据格式并返回return {data}}}]
})export default request
修改props类型
props: {articleId: {type: [Number, String, Object],required: true}}
扩展:ES2020 BigInt
ES2020 引入了一种新的数据类型 BigInt(大整数),来解决这个问题。BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。
参考链接:
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/BigInt
- http://es6.ruanyifeng.com/#docs/number#BigInt-%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B
展示文章详情
思路:
- 找到数据接口
- 封装请求方法
- 请求获取数据
- 模板绑定
一、请求并展示文章详情
1、在 api/article.js
中新增封装接口方法
/*** 根据 id 获取指定文章*/
export const getArticleById = articleId => {return request({method: 'GET',url: `/app/v1_0/articles/${articleId}`})
}
2、在组件中调用获取文章详情
+ import { getArticleById } from '@/api/article'export default {name: 'ArticlePage',components: {},props: {articleId: {type: String,required: true}},data () {return {+ article: {} // 文章详情}},computed: {},watch: {},created () {+ this.loadArticle()},mounted () {},methods: {+++ async loadArticle () {try {const { data } = await getArticleById(this.articleId)this.article = data.data} catch (err) {console.log(err)}}}
}
3、模板绑定
<!-- 加载完成-文章详情 --><div class="article-detail"><!-- 文章标题 --><h1 class="article-title">{{ article.title }}</h1><!-- /文章标题 --><!-- 用户信息 --><van-cell class="user-info" center :border="false"><van-imageclass="avatar"slot="icon"roundfit="cover":src="article.aut_photo"/><div slot="title" class="user-name">{{ article.aut_name }}</div><div slot="label" class="publish-date">{{ article.pubdate | relativeTime }}</div><van-buttonclass="follow-btn"type="info"color="#3296fa"roundsize="small"icon="plus">关注</van-button><!-- <van-buttonclass="follow-btn"roundsize="small">已关注</van-button> --></van-cell><!-- /用户信息 --><!-- 文章内容 --><div class="article-content" v-html="article.content"></div><van-divider>正文结束</van-divider></div><!-- /加载完成-文章详情 -->
处理内容加载状态
需求:
- 加载中,显示 loading
- 加载成功,显示文章详情
- 加载失败,显示错误提示
- 如果 404,提示资源不存在
- 其它的,提示加载失败,用户可以点击重试重新加载
<!-- 加载中 -->
<div class="loading-wrap" v-if="isLoading">
<!-- 加载完成-文章详情 -->
<div v-else-if="article.title" class="article-detail">
<!-- 加载失败:404 -->
<div v-else-if="errStatus === 404" class="error-wrap">
<!-- 加载失败:其它未知错误(例如网络原因或服务端异常) -->
<div v-else class="error-wrap">
async loadArticleInfo () {try {// 随机错误thorw Error()const { data } = await getArticleInfo(this.articleId)this.article = data.data} catch (err) {// 加载失败 404if (err.response && err.response.status === 404) {this.errStatus = 404}this.$toast('获取失败')}// 加载完成this.isLoading = false
}
点击重新加载
<!-- 加载失败:其它未知错误(例如网络原因或服务端异常) -->
<div v-else class="error-wrap"><van-icon name="failure" /><p class="text">内容加载失败!</p><van-button @click="loadArtcileInfo" class="retry-btn">点击重试</van-button>
</div>
在loadArtcileInfo中重新显示加载loading
关于文章正文的样式
文章正文包括各种数据:段落、标题、列表、链接、图片、视频等资源。
将 github-markdown-css 样式文件下载到项目中
导入
import './github-markdown.css'
添加类名
<div class="article-content markdown-body" v-html="article.content"></div>
配置不要转换样式文件中的字号
'postcss-pxtorem': {rootValue ({ file }) {return file.indexOf('vant') !== -1 ? 37.5 : 75},propList: ['*'],exclude: 'github-markdown'}
图片点击预览
一、ImagePreview 图片预览 的使用
1、导入
import { ImagePreview } from 'vant';
2、使用
ImagePreview({images: ['https://img.yzcdn.cn/vant/apple-1.jpg','https://img.yzcdn.cn/vant/apple-2.jpg'],// 预览图片的起始位置startPosition: 1,// 点击关闭onClose () {// do something}
})
二、处理图片点击预览
思路:140967 带图片
1、页面渲染完成从文章内容中获取到所有的 img DOM 节点
async loadArticleInfo () {try {thorwError()const { data } = await getArticleInfo(this.articleId)this.article = data.data// 数据加载完成setTimeout(() => {this.previewImg()}, 10)} catch (err) {if (err.response && err.response.status === 404) {this.errStatus = 404}this.$toast('获取失败')}this.isLoading = false}
previewImg () {const contentEl = this.$refs.contentRefconst allImg = contentEl.querySelectorAll('img')
}
2、获取文章内容中所有的图片地址
previewImg () {const contentEl = this.$refs.contentRefconst allImg = contentEl.querySelectorAll('img')console.log(allImg)
}
3、遍历所有 img 节点,给每个节点注册点击事件
previewImg () {const contentEl = this.$refs.contentRefconst allImg = contentEl.querySelectorAll('img')const images = []allImg.forEach((element, index) => {images.push(element.src)element.onclick = () => {}})
}
4、在 img 点击事件处理函数中,调用 ImagePreview 预览
previewImg () {const contentEl = this.$refs.contentRefconst allImg = contentEl.querySelectorAll('img')const images = []allImg.forEach((element, index) => {images.push(element.src)element.onclick = () => {ImagePreview({images,startPosition: index})}})
}
关注用户
思路:
- 给按钮注册点击事件
- 在事件处理函数中
- 如果已关注,则取消关注
- 如果没有关注,则添加关注
下面是具体实现。
视图处理
功能处理
- 找到数据接口
- 封装请求方法
- 请求调用
- 视图更新
1、在 api/user.js
中添加封装请求方法
/*** 添加关注*/
export const addFollow = userId => {return request({method: 'POST',url: '/app/v1_0/user/followings',data: {target: userId}})
}/*** 取消关注*/
export const deleteFollow = userId => {return request({method: 'DELETE',url: `/app/v1_0/user/followings/${userId}`})
}
2、给关注/取消关注按钮注册点击事件
3、在事件处理函数中
import { addFollow, deleteFollow } from '@/api/user'
async onFollow () {// 开启按钮的 loading 状态this.isFollowLoading = truetry {// 如果已关注,则取消关注const authorId = this.article.aut_idif (this.article.is_followed) {await deleteFollow(authorId)} else {// 否则添加关注await addFollow(authorId)}// 更新视图this.article.is_followed = !this.article.is_followed} catch (err) {console.log(err)this.$toast.fail('操作失败')}// 关闭按钮的 loading 状态this.isFollowLoading = false
}
最后测试。
loading 效果
两个作用:
- 交互反馈
- 防止网络慢用户多次点击按钮导致重复触发点击事件
组件封装
组件封装
1、封装组件
<template><van-buttonclass="follow-btn"type="info"color="#3296fa"roundv-if="!is_followed"size="small"icon="plus":loading="isFollowLoading"@click="follow">关注</van-button><van-buttonv-elseclass="follow-btn"round:loading="isFollowLoading"size="small"@click="follow">已关注</van-button>
</template><script>
import { addFollow, deleteFollow } from '@/api/user'
export default {name: 'follow-user',data () {return {isFollowLoading: false}},props: {is_followed: {type: Boolean,required: true},user_id: {type: [Number, String, Object],required: true}},created () {},methods: {// 关注用户async follow () {this.isFollowLoading = truetry {// 如果已关注,则取消关注const authorId = this.user_idif (this.is_followed) {await deleteFollow(authorId)} else {// 否则添加关注await addFollow(authorId)}// 更新视图this.$emit('update-follow', !this.is_followed)} catch (err) {console.dir(err)if (err.response && err.response.status === 400) {return this.$toast.fail('不能关注自己')}this.$toast.fail('操作失败')}// 关闭按钮的 loading 状态this.isFollowLoading = false}}
}
</script><style scoped lang='less'></style>
2、引入组件
import FollowUser from '@/components/follow-user'
components: {FollowUser
}
3、使用组件
<follow-user:is_followed="article.is_followed":user_id="article.aut_id"@update-follow="article.is_followed = $event"
</follow-user>
使用v-model
<follow-userv-model="article.is_followed":user_id="article.aut_id"></follow-user>
组件内部添加model
model: {prop: 'is_followed',event: 'update-follow'
}
文章收藏
该功能和关注用户的处理思路几乎一样,建议由学员自己编写。
封装组件
处理视图
1、封装组件
<template><van-iconcolor="#777"name="star-o"/>
</template><script>
export default {name: 'collectArticle',
}
</script><style scoped lang='less'></style>
2、使用组件
<van-iconname="comment-o"info="123"color="#777"/>
<CollectArticle />
<van-iconcolor="#777"name="good-job-o"/>
2、传递数据处理视图
<collect-article v-model="article.is_collected" />
<template><van-icon:color="value ? '#ffa500' : ''":name="value?'star':'star-o'"/>
</template><script>
export default {name: 'CollectArticle',props: {value: {type: Boolean,required: true}}
}
</script>
功能处理
思路:
- 给收藏按钮注册点击事件
- 如果已经收藏了,则取消收藏
- 如果没有收藏,则添加收藏
下面是具体实现。
1、在 api/article.js
添加封装数据接口
/*** 收藏文章*/
export const addCollect = target => {return request({method: 'POST',url: '/app/v1_0/article/collections',data: {target}})
}/*** 取消收藏文章*/
export const deleteCollect = target => {return request({method: 'DELETE',url: `/app/v1_0/article/collections/${target}`})
}
2、给收藏按钮注册点击事件
<van-icon
color="#ffa500"
:name="value?'star':'star-o'"
@click="onCollect"
:loading="loading"
/>
3、处理函数
async onCollect () {// 开始请求this.loading = truetry {// 是否收藏if (this.value) {// 父组件 传递articleIdawait deleteCollect(this.articleId)} else {await addCollect(this.articleId)}// 更新视图this.$emit('input', !this.value)this.$toast.success(this.value ? '取消收藏' : '收藏成功')} catch (err) {this.$toast('操作失败,请重试')}// 请求结束this.loading = false
}
4、 父组件传递文章id
<collect-article :article-id="article.art_id" v-model="article.is_followed" />
5、将底部区域调整到正文下面
文章点赞
1、封装组件
<template><van-icon:color="value === 1 ? 'red' : ''":name="value === 1?'good-job':'good-job-o'"/>
</template><script>
export default {data () {return {}},props: {value: {type: Number,required: true}},created () {},methods: {}
}
</script><style scoped lang='less'></style>
2、使用组件
import likeArticle from '@/components/like-article'components: {FollowUser,CollectArticle,likeArticle
}<likeArticle v-model="article.attitude" />
该功能和关注用户的处理思路几乎一样,建议由学员自己编写。
article 中的 attitude
表示用户对文章的态度
-1
无态度0
不喜欢1
已点赞
思路:
- 给点赞按钮注册点击事件
- 如果已经点赞,则请求取消点赞
- 如果没有点赞,则请求点赞
1、添加封装数据接口
/*** 点赞*/
export const addLike = articleId => {return request({method: 'POST',url: '/app/v1_0/article/likings',data: {target: articleId}})
}/*** 取消点赞*/
export const deleteLike = articleId => {return request({method: 'DELETE',url: `/app/v1_0/article/likings/${articleId}`})
}
2、给点赞按钮注册点击事件
3、处理函数
async onLike () {// 两个作用:1、交互提示 2、防止网络慢用户连续不断的点击按钮请求this.$toast.loading({duration: 0, // 持续展示 toastmessage: '操作中...',forbidClick: true // 是否禁止背景点击})try {// 如果已经点赞,则取消点赞if (this.article.attitude === 1) {await deleteLike(this.articleId)this.article.attitude = -1this.$toast.success('取消点赞')} else {// 否则添加点赞await addLike(this.articleId)this.article.attitude = 1this.$toast.success('点赞成功')}} catch (err) {console.log(err)this.$toast.fail('操作失败')}
}
Vue移动端系列 => [07] 文章详情相关推荐
- Vue移动端系列 => [06] 文章搜索
六.文章搜索 6.1 创建组件并配置路由 1.给搜索按钮添加to属性 <!-- 导航栏 --><van-nav-bar class="page-nav-bar" ...
- 基于SpringBoot + Vue的个人博客系统07——文章列表和文章详情
简介 由于本人不是专业的前端,所以写出来的界面可能会稍微有些丑陋,甚至有些地方的写法不是很专业,还请大家见谅 主界面 JS 部分 首先是 js 逻辑部分 我们先在@/http/request.js中定 ...
- 小案例:实现http://www.alloyteam.com/page/0/移动端效果,博客文章列表和文章详情页面
一:案例要求和数据: (1)移动端适配 (2)列表跳详情 (3)回到顶部 (4)上下页 (5)时间格式 (6)解决接口跨域请求访问问题 (7)路由跳转无bug 接口路径均以 http://118.19 ...
- InfoQ网站作者的文章列表文章详情获取-Java网络爬虫系统性学习与实战系列(13)
InfoQ网站作者的文章列表&文章详情获取-Java网络爬虫系统性学习与实战系列(13) 文章目录 联系方式 概述 分析 配置好Xpath规则 selenium工具类 获取InfoQ文章列表 ...
- Vue.js 实战系列之实现视频类WebApp的项目开发——6. 首页视频详情实现
如果想看该实战系列的其他内容,请移步至 Vue.js 实战系列之实现视频类WebApp的项目开发. 项目仓库地址,欢迎 Star 实现效果 功能实现 视频详情页基本实现 创建 InfoBar.vue ...
- VUE—移动端手机号正则验证,不正确显示‘请输入正确手机号’,若正确跳转到发送验证码(图文详情)
VUE-移动端手机号正则验证,不正确显示'请输入正确手机号',若正确跳转到发送验证码 先看效果图 第一步:写布局 <input type="text" placeholder ...
- Vue.js入门 0x13 实战:知乎日报项目开发-文章详情页
加载内容 右侧的文章内容区域封装成了一个组件.在components目录下新建daily-article.vue组件,它唯一接收唯一的一个prop:id,也就是文章的id,如果id变化了,就说明切换了 ...
- JavaScript - Vue经典教程系列-李游Leo-专题视频课程
JavaScript - Vue经典教程系列-1114人已学习 课程介绍 Vue.JS 是目前火的前端框架之一,是一个构建数据驱动的 web 界面的渐进式框架. Vue.JS 的目标是 ...
- 手摸手,带你用vue撸后台 系列一(基础篇) - 掘金
完整项目地址:vue-element-admin 系列文章: 手摸手,带你用 vue 撸后台 系列一(基础篇) 手摸手,带你用 vue 撸后台 系列二(登录权限篇) 手摸手,带你用 vue 撸后台 系 ...
最新文章
- 医疗影像处理:去除医疗影像中背景的影响2D/3D【numpy-code】| CSDN博文精选
- labview 随笔记录
- IntelliJ IDEA 2020.1 EAP2 发布:新增禅模式和 LightEdit 模式
- CSP认证201409-4	最优配餐[C++题解]:bfs、多源bfs、最短路、图论
- Serverless 时代 DevOps 的最佳打开方式
- 笔记-项目整体管理-监控项目工作主要做的工作
- LeetCode 16 最接近的三数之和
- JAVA语言程序设计课后习题----第四单元解析(仅供参考)
- 【渝粤题库】国家开放大学2021春1376机械制造装备及设计题目
- Cisco Nexus 1000V
- 主成分分析碎石图_ISLR读书笔记十九:主成分分析(PCA)
- 两个摄像头合成一路_小米手机成功开发出伸缩式摄像头,秒变单反,这次雷军又火了...
- 为什么你总感觉情绪低落心情颓废?
- java proguard 反混淆_JAVA之代码混淆proguard
- FC SAN、IP SAN、IB SAN
- Android 发短信功能实现
- vue开发必备神器:vue-devtools
- vue中views新建文件夹的代码规范
- 图像和音频格式解析一览
- 期权:短期交易日内波动为主 静待市场情绪拐点