目录

  • `一、前期准备`
  • `二、浙里办-单点登录功能`
  • `三、获取用户信息`
  • `四、埋点操作`
  • `五、JSBridge的引入及使用`

一、前期准备

  1. 由于浙里办的微应用是必须前后端分离,且前端文件需要部署到浙里办服务器上的,所以需要前端项目支持 rpm run build 命令,所以不能使用Hbuilder X直接构建项目,需要如下命令构建,选择默认模板即可,细节参考Uniapp官网。
// 安装脚手架
npm install -g @vue/cli
// 新建项目
vue create -p dcloudio/uni-preset-vue 项目名字
  1. 由于浙里办编译默认输出位置是build,但是uniapp的构建命令默认输出位置是dist;所以,第一种方式是修改uniapp的构建命令输出位置,将pakeage.json中的scripts属性进行如下修改:
"scripts": {"serve": "npm run dev:h5","build": "npm run build:h5",// 在"build:h5"中添加UNI_OUTPUT_DIR='build'即可"build:h5": "cross-env UNI_OUTPUT_DIR='build' NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build","dev:h5": "cross-env NODE_ENV=development UNI_PLATFORM=h5 vue-cli-service uni-serve"},
  • 或者在根目录下新建gbc.json文件向浙里办指定输出目录
// gbc.json
{"type":"build-config","version":"0.0.1","outputPath":"dist"
}
  1. 由于前端项目部署在浙里办服务器上,所以还需要对manifest.json文件进行如下修改
{"name" : "xxxxxxxx","appid" : "","description" : "","versionName" : "1.0.0","versionCode" : "100","transformPx" : false,"h5" : {"publicPath" : "./", // 修改1 不修改此处会出现应用白屏的情况"router" : {"base" : "./", // 修改2 不修改此处会出现图片拿不到的情况"mode" : "hash" // 修改3 浙里办只支持hash路由}}
}

二、浙里办-单点登录功能

  • 由于浙里办微应用需要对支付宝浙里办小程序与浙里办APP进行双端适配,而不同环境下的单点登录跳转链接也不同,所以需要进行应用环境的检测
  const sUserAgent = window.navigator.userAgent.toLowerCase()// 浙里办APPconst bIsDtDreamApp = sUserAgent.indexOf('dtdreamweb') > -1// 浙里办支付宝小程序const bIsAlipayMini = sUserAgent.indexOf('miniprogram') > -1 && sUserAgent.indexOf('alipay') > -1
  1. 主要代码实现
isLoad: () => {if (bIsAlipayMini) {window.location.href = "https://puser.zjzwfw.gov.cn/sso/alipay.do?action=ssoLogin&servicecode=【接入代码】&redirectUrl=【附带跳转地址,以sp参数返回】";} else {window.location.href = "https://puser.zjzwfw.gov.cn/sso/mobile.do?action=oauth&scope=1&servicecode=【接入代码】&redirectUrl=【附带跳转地址,以sp参数返回】";}// 或者使用replace()// window.location.replace('https://puser.zjzwfw.gov.cn/sso/alipay.do?action=ssoLogin&servicecode=【接入代码】&redirectUrl=【附带跳转地址,以sp参数返回】');
}

接入码是跟浙里办申请服务接入通过之后给的,redirectUrl参数是单点登录跳转的回调地址,如果不指定就以后台设置的sp参数作为回调地址跳转。(sp参数自己无权限修改,必须找服务接入对接人设置,且不设置单点登录登录成功将显示“无权访问业务系统”的提示字样)

Tips:新版使用AK/SK(servicecode 等同于 AccessKey(简称 AK),servicepwd 等同于 SecretKey(简称 SK)),获取地址:https://csss.zj.gov.cn/verifyComList/applyNew ,需政务内网访问。
2. 二次回退问题
使用pageshow事件解决二次回退问题

// 浙里办APP判断页面进入状态
watchApp: () => {window.onpageshow = (event) => {if (event.persisted || (window.performance && window.performance.navigation.type == 2)) {ZWJSBridge.close();}that.isLoad();}
},
// 支付宝浙里办小程序判断页面进入状态
watchApply: () => {window.onpageshow = (event) => {if (event.persisted || (window.performance && (window.performance.navigation.type == 1 || window.performance.navigation.type == 0))) {that.isLoad();} else {my.navigateBack();}}
}

使用vue router的守卫导航解决二次回退问题

// loading 中转页面
// 单点登录成功后调用 that.$router.push('/index')
beforeRouteEnter(to, from, next) {if(to.meta.name === 'loading' && from.meta.name === 'index') {if(bIsDtDreamApp){ZWJSBridge.close();} else {my.navigateBack();}}
}
// 路由配置
{path: '/index', component: () => {'./pages/index/index.vue'}, name: 'index', meta: {name: 'index'}}
{path: '/loading', component: () => {'./pages/index/loading.vue'}, name: 'loading', meta: {name: 'loading'}}
  1. 获取回调后的ticket参数
// 用法 ==> that.getQuery('ticket')
getQuery: (name) => {var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");var r = window.location.search.substr(1).match(reg);if (r != null) return unescape(r[2]);return null;
}

因为uniapp只能处理应用内的跳转及参数获取,应用内的参数可以在onLoad()及onInit()这两个生命周期函数获取,而单点登录成功的回调属于重定向,参数只能通过location对象获取。
4. 完整逻辑

init: () => {sUserAgent = window.navigator.userAgent.toLowerCase();bIsDtDreamApp = sUserAgent.indexOf("dtdreamweb") > -1; // 浙里办APPbIsAlipayMini = sUserAgent.indexOf("miniprogram") > -1 && sUserAgent.indexOf("alipay") > -1;if (that.getQuery('ticket') == null && that.ticket == "") {uni.showLoading({title: '登录中',mask: true});if (bIsDtDreamApp) {that.watchApp();} else if (bIsAlipayMini) {that.watchApply();}} else {that.ticket = that.getQuery('ticket');uni.reLaunch({url: `./index?ticket=${that.ticket}`})}
}
  1. 适配微信小程序单点登录
wxAPP: () => {if (ZWJSBridge.ssoTicket) {const ssoFlag = await ZWJSBridge.ssoTicket({});if (ssoFlag && ssoFlag.result === true) {//使用 IRS“浙里办”单点登录组件if (ssoFlag.ticketId) {that.ticket = ssoFlag.ticketId;} else {//当“浙里办”单点登录失败或登录态失效时调用 ZWJSBridge.openLink 方法重新获取 ticketId。ZWJSBridge.openLink({type: "reload"}).then(res => {that.ticket = res.ticketId;})}} else {that.init();}} else {that.init();}
}// !!!!!!非常重要的内容!!!!!!
// 微信小程序需要使用最新版本的ZWJSBridge-1.1.0
// <script type="text/javascript"src="//assets.zjzwfw.gov.cn/assets/ZWJSBridge/1.1.0/zwjsbridge.js"></script>

三、获取用户信息

Postman调用脚本见下文附件

Ⅰ、普通request调取用户数据

根据ticket获取token,POST方法queryParams传参
getTonken: () => {new Promise((resolve, reject) => {uni.request({url: `https://appapi.zjzwfw.gov.cn/sso/servlet/simpleauth?method=ticketValidation${that.queryParams(that.getTonkenAndUserInfoParams())}`,method: 'POST',header:{"Content-Type": "application/json"},success: (res) => {if (res.statusCode == 200 && res.data.result == 0) {resolve(res.data);} else {uni.showToast({title: res.data.result})reject(res);}},fail: (err) => {uni.showToast({title: '网络错误,请稍后重试!'})reject(err.errMsg);}})}
}

根据token获取用户信息

getUserInfo: () => {new Promise((resolve, reject) => {uni.request({url: `https://appapi.zjzwfw.gov.cn/sso/servlet/simpleauth?method=getUserInfo${that.queryParams(that.getTonkenAndUserInfoParams(that.token))}`,method: 'POST',header:{"Content-Type": "application/json"},success: (res) => {if (res.statusCode == 200 && res.data.result == 0) {resolve(res.data);} else {uni.showToast({title: res.data.result})reject(res);}},fail: (err) => {uni.showToast({title: '网络错误,请稍后重试!'})reject(err.errMsg);}})}
}

统一获取参数

getTonkenAndUserInfoParams: ( token = '') => {let mTime = that.timeFormat(new Date(), 'yyyymmddhhMMss');let data = {servicecode: 【接入码】,time: mTime,sign: md5Libs.md5(`${【接入码】}${【接入码密码】}${mTime}`),datatype: 'json'}if (token == '') {data.st = that.ticket;} else {data.token = token;}return data;
}

时间格式化

function timeFormat(dateTime = null, fmt = 'yyyy-mm-dd') {// 如果为null,则格式化当前时间if (!dateTime) dateTime = Number(new Date());// 如果dateTime长度为10或者13,则为秒和毫秒的时间戳,如果超过13位,则为其他的时间格式if (dateTime.toString().length == 10) dateTime *= 1000;let date = new Date(dateTime);let ret;let opt = {"y+": date.getFullYear().toString(), // 年"m+": (date.getMonth() + 1).toString(), // 月"d+": date.getDate().toString(), // 日"h+": date.getHours().toString(), // 时"M+": date.getMinutes().toString(), // 分"s+": date.getSeconds().toString() // 秒// 有其他格式化字符需求可以继续添加,必须转化成字符串};for (let k in opt) {ret = new RegExp("(" + k + ")").exec(fmt);if (ret) {fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0")))};};return fmt;
}

参数格式化

function queryParams(data = {}, isPrefix = true, arrayFormat = 'brackets') {let prefix = isPrefix ? '?' : ''let _result = []if (['indices', 'brackets', 'repeat', 'comma'].indexOf(arrayFormat) == -1) arrayFormat = 'brackets';for (let key in data) {let value = data[key]// 去掉为空的参数if (['', undefined, null].indexOf(value) >= 0) {continue;}// 如果值为数组,另行处理if (value.constructor === Array) {// e.g. {ids: [1, 2, 3]}switch (arrayFormat) {case 'indices':// 结果: ids[0]=1&ids[1]=2&ids[2]=3for (let i = 0; i < value.length; i++) {_result.push(key + '[' + i + ']=' + value[i])}break;case 'brackets':// 结果: ids[]=1&ids[]=2&ids[]=3value.forEach(_value => {_result.push(key + '[]=' + _value)})break;case 'repeat':// 结果: ids=1&ids=2&ids=3value.forEach(_value => {_result.push(key + '=' + _value)})break;case 'comma':// 结果: ids=1,2,3let commaStr = "";value.forEach(_value => {commaStr += (commaStr ? "," : "") + _value;})_result.push(key + '=' + commaStr)break;default:value.forEach(_value => {_result.push(key + '[]=' + _value)})}} else {_result.push(key + '=' + value)}}return _result.length ? prefix + _result.join('&') : ''
}

Tips 最新版本单点登录参数签名生成代码详见下文代码

这是在前端直接处理用户信息的方式,但是这样直接调用会引发另一个问题,就是浏览器的CROS跨域问题,这个时候就需要使用其他方式去实现功能,一种是将ticket传回后端,让后端获取用户信息然后回传前端;第二种方式就是使用浙里办提供的RPC网关去实现前端获取用户数据。

npm i --save @aligov/jssdk-mgop@3.0.0
// 代码中引用
import { mgop } from '@aligov/jssdk-mgop';

调用方法改造
Tips 最新版本单点登录参数签名生成代码详见下文第7小点代码

getTonkenAndUserInfo: (data) => {return new Promise((resolve, reject) => {mgop({api: '【API名称】',host: 'https://mapi.zjzwfw.gov.cn/',data: data,dataType: 'JSON',type: 'POST',appKey: '【应用APPKEY】',onSuccess: res => {if (res.data.result && res.data.result == 0) {resolve(res.data);}},onFail: err => {reject(err);}});})
},

// 安装插件HMAC-SHA256签名插件crypto-js
// npm install crypto-js
// 2022最新规范示例

// Tips 最新版本单点登录参数签名生成代码详见下文代码

import CryptoJS from 'crypto-js/crypto-js';
getTonkenAndUserInfo: (data, method = 'ticketValidation') => {let xTime = new Date().toGMTString();return new Promise((resolve, reject) => {mgop({api: '【API名称】',host: 'https://mapi.zjzwfw.gov.cn/',data: data,dataType: 'JSON',type: 'POST',appKey: '【应用APPKEY】',// getHeader()方法在下方,参数说明//获取Token填 自己的tokenheader: that.getHeader(), onSuccess: res => {if (res.data.result && res.data.result == 0) {resolve(res.data);}},onFail: err => {reject(err);}});})
},

getTonkenAndUserInfoParams: (method = 'ticketValidation', token = '') => {let mTime = that.timeFormat(new Date(), 'yyyymmddhhMMss');let data = {method: method,servicecode: 【接入码】,time: mTime,sign: md5Libs.md5(`${【接入码】}${【接入码密码】}${mTime}`),datatype: 'json'}if (token == '') {data.st = that.ticket;} else {data.token = token;}return data;
}

具体引用实现

that.getTonkenAndUserInfo(that.getTonkenAndUserInfoParams()).then(data => {return that.getTonkenAndUserInfo(that.getTonkenAndUserInfoParams('getUserInfo', data.token));
}).then(data => {that.userInfo = data;
}).catch(err => {console.log(err);
});

最新版本单点登录参数签名生成代码

let accessKey = "xxx"; //替换成自己的ak
let secret="xxxx"; //替换成自己的sklet path = pm.request.url.getPath();
let query=pm.request.url.query;
let queryArray=[];
for(index in query.members){let member= query.members[index];if(member["disabled"]==true){continue;}let value = member["value"];if(member["value"]==null){value = ""}let queryKeyValue = encodeURIComponent(member["key"])+"="+encodeURIComponent(value);queryArray.push(queryKeyValue);
}
queryArray.sort();
let queryString = queryArray.join("&");let date = (new Date()).toGMTString();
let singString = pm.request.method+"\n"+path+"\n"+queryString+"\n"+accessKey+"\n"+date+"\n";
let hash = CryptoJS.HmacSHA256(singString, secret);
let hashInBase64 = CryptoJS.enc.Base64.stringify(hash);pm.environment.set("X-BG-HMAC-ACCESS-KEY",accessKey);
pm.environment.set("X-BG-HMAC-SIGNATURE",hashInBase64);
pm.environment.set("X-BG-HMAC-ALGORITHM","hmac-sha256");
pm.environment.set("X-BG-DATE-TIME",date);

getHeader方法提取

getHeader: (subPath = '自己的Token') => {let accessKey = 【ak】; let secret = 【sk】; let path = `/restapi/prod/${subPath}/sso/servlet/simpleauth`;let queryString = "";let xTime = (new Date()).toGMTString(); let singString = "POST"+"\n"+path+"\n"+queryString+"\n"+accessKey+"\n"+xTime+"\n";let hash = CryptoJS.HmacSHA256(singString, secret);let hashInBase64 = CryptoJS.enc.Base64.stringify(hash);    return {'Content-Type': 'application/x-www-form-urlencoded','X-BG-HMAC-ACCESS-KEY': accessKey,'X-BG-HMAC-SIGNATURE': hashInBase64,'X-BG-HMAC-ALGORITHM': 'hmac-sha256','X-BG-DATE-TIME': xTime,};
}

如果遇到应用上架检测外链,请用以下写法,官方白名单

// 法人用户
window.location.href = 'https://esso.zjzwfw.gov.cn/opensso/spsaehandler/metaAlias/sp?spappurl=' + url;
window.location.href = 'https://essotest.zjzwfw.gov.cn/opensso/spsaehandler/metaAlias/sp?spappurl=' + url;
// 个人用户
window.location.href = 'https://puser.zjzwfw.gov.cn/sso/usp.do?action=zfRedirect&servicecode' + code;

四、埋点操作

Ⅰ、旧版本aplus.js埋点
1、首先在public/index.html中引入埋点的api脚本,为防止出现https://https://这种双协议头调用,除脚本自身外,还需先填写基础PV日志的埋点函数,如果不需要单点登录采集用户数据则不需要最后的BLOCK,其他均为固定值,照抄就行。

<script>(function(w, d, s, q, i) {w[q] = w[q] || [];var f = d.getElementsByTagName(s)[0],j = d.createElement(s);j.async = true;j.id = 'beacon-aplus';j.src = 'https://d.alicdn.com/alilog/mlog/aplus.js?id=自己的id';f.parentNode.insertBefore(j, f);})(window, document, 'script', 'aplus_queue');aplus_queue.push({action: 'aplus.setMetaInfo',arguments: ['aplus-waiting', 'MAN']});aplus_queue.push({action: 'aplus.setMetaInfo',arguments: ['aplus-rhost-v', 'alog.zjzwfw.gov.cn']});aplus_queue.push({action: 'aplus.setMetaInfo',arguments: ['aplus-rhost-g', 'alog.zjzwfw.gov.cn']});aplus_queue.push({action: 'aplus.setMetaInfo',arguments: ['appId', '60506758']});aplus_queue.push({action: 'aplus.setMetaInfo',arguments: ['_hold', 'BLOCK']});
</script>

2、页面PV日志采集,aplus函数在取得用户经纬度、用户数据、用户类型等数据后再进行调用。如没有单点登录获取用户数据,则仅需要留’aplus.sendPV’这一个。

aplus: () => {aplus_queue.push({'action': 'aplus.sendPV','arguments': [{is_auto: false}, {miniAppId: '【APPID】',miniAppName: '【APPNAME】',long: that.longitude,lati: that.latitude,userType: that.userType,}]});aplus_queue.push({action: "aplus.setMetaInfo",arguments: ["_user_nick", that.userInfo.username]});aplus_queue.push({action: "aplus.setMetaInfo",arguments: ["_user_id", that.userInfo.userid]});aplus_queue.push({action: 'aplus.setMetaInfo',arguments: ['_hold', 'START']});
}

Ⅱ、新版本版本zwlog.js埋点
1、首先在public/index.html中引入埋点的api脚本

<script type="text/javascript"src="//assets.zjzwfw.gov.cn/assets/zwlog/1.0.0/zwlog.js"></script>

2、创建并声明zwlog对象实例,在创建zwlog实例时下述参数为必传,如需在没有用户信息时进行埋点则使用当前应用信息,如有用户信息则可使用用户信息

// 在创建zwlog实例时下述参数为必传,如需在没有用户信息时进行埋点则使用当前应用信息,如有用户信息则可使用用户信息

initLog: () => {const zwlog = new ZwLog({_user_id: "用户 ID", // 或应用appId_user_nick: "用户昵称" // 或应用名称})
}

3、日志事件发送

zwlog: () => {zwlog.onReady(() => {// sendPV⽅法将发送⼀条页面⽇志zwlog.sendPV({miniAppId: 'IRS 服务侧应用 appid',Page_duration: '用户从进入到离开当前页面的时长',t2: '页面启动到加载完成的时间',t0: '页面启动到页面响应完成的时间',log_status: 'IRS 服务侧应用 appid'});// record⽅法将发送⼀条事件⽇志,它接收三个参数:// trackerEventCode:为注册的事件编码.当上报的事件为 PV 事件时,trackerEventCode 可传空值或'PageView'常量;// eventType:事件类型 取值为'EXP':⾃定义曝光事件,'CLK':⾃定义点击事件,'OTHER': 其他⾃定义事件;// eventParams: 为本次事件中上报的事件参数. 其取值为⼀个 JSON 对象(平铺的简单对象,不能多层嵌套);// JSON 中的 key 不能是以下保留属性:// uidaplus,spm-url,spm-pre,spm_cnt,pvid,_dev_id,_anony_id,_user_id,_user_nick,_session_idzwlog.record('yourTrackerEventCode', 'CLK', {Test1: '测试参数 1',});let { metaInfo } = zwlog; // SDK 元配置的当前设置}
}

五、JSBridge的引入及使用

在浙里办的微应用中,很多地方需要使用到浙里办的一些封装API功能,也就是JSBridge,且所有JSBridgeAPI均支持Promise回调,这里主要讲解下常用的几个API以及引入。

JSBriage JS的引入,在public/index.html中引入

<script type="text/javascript" src="//assets.zjzwfw.gov.cn/assets/ZWJSBridge/1.0.1/zwjsbridge.js"></script>

微信小程序需要使用最新版本的ZWJSBridge-1.1.0

<script type="text/javascript"src="//assets.zjzwfw.gov.cn/assets/ZWJSBridge/1.1.0/zwjsbridge.js"></script>

JSBridge JS初始化,在App.vue中进行全局初始化

onLaunch: function() {console.log('App Launch')ZWJSBridge.onReady(() => {console.log('初始化完成后,执行bridge方法')})
},

初始化完成以后,就可以在单个页面使用 ZWJSBridge.API名称 调用功能了
常用的API

// 设置单页面标题
ZWJSBridge.setTitle({"title": "标题"
}).then(res => {console.log(res)
}).catch(err => {console.log(err)
})
// 获取用户类型
ZWJSBridge.getUserType({}).then(res => {console.log(res)
}).catch(err => {console.log(err)
})
// 获取用户地理位置
ZWJSBridge.getLocation({}).then(res => {console.log(res)
}).catch(err => {console.log(err)
})
//调用扫一扫
ZWJSBridge.scancode({}).then(res => {console.log(res)
}).catch(err => {console.log(err)
})

Uniapp、Vue搭建浙里办微应用(单点登录、埋点)相关推荐

  1. 使用uniapp框架搭建浙里办微应用(单点登录、埋点、适老化、RPC网关)

    文章目录 前言 一.前期准备 二.浙里办-单点登录功能 三.获取用户信息 Ⅰ.普通request调取用户数据 Ⅱ.浙里办RPC网关调用的实现 Ⅲ.浙里办官方单点登录组件接口网关调用 四.埋点操作 Ⅰ. ...

  2. 浙里办接入 H5应用使用说明

    参考官网链接: https://www.yuque.com/docs/share/525e3e8a-ad52-421b-90da-2d76808e3050 说明接入方式有两种: 1.使用Rax框架 并 ...

  3. 浙里办前端H5对接小结(复盘自用)

    项目是老项目 之前上线在浙政钉上的 现在老板说要在浙里办也上线一版 刚转正的前端就开始一脸懵逼得瞎搞 现在整差不多了 复盘一下 目录 政务中台debug工具测试 下载及登录绑定 查看日志 调试工具 浙 ...

  4. 浙里办对接H5微应用遇到的问题

    最近在搞浙里办对接,在这个对接中遇到了以下问题,单点登录.埋点.二次回退问题 "浙里办"H5微应用接入文档:语雀 注意:我们做的是app对接 单点登录 由于浙里办微应用需要对支付宝 ...

  5. 新手小白H5微应用接入浙里办流程指南

    第一步:前期准备工作 1.联系业主单位于irs业主-工作台(操作角色:业主单位-IRS 应用管理员,操作平台:irs.zj.gov.cn)创建H5微应用,相应资料填写,开发商可协助. 2.由业主单位[ ...

  6. 浙里办完整开发流程(仅前端)

    开发之前一定要作的几点 1.添加浙里办钉钉对接群 群号 34143965(有什么不定的地方就大胆问群里的几个负责人) 2.问钉钉群里面得的老师要开发流程相关文件 和开发注意事项(浙里办适老化 对ui有 ...

  7. 浙里办(H5)上传流程

    "浙里办"H5微应用迁移流程 主要满足三点:(摘自官方文档) https://odynww.yuque.com/docs/share/caa2a215-bb8b-4739-a4e0 ...

  8. 浙里办H5应用开发中的注意事项

    浙里办H5应用开发 一.准备工作 1.从业主部门(需要开发应用的政府单位)申请浙政钉账号,最好是用自己的手机号去申请,因为浙政钉登录需要验证,如果用别人提供的账号,每次登录都很麻烦. 2.加入浙里办技 ...

  9. 浙里办微信小程序上架

    一.概述 本指南旨在为"浙里办"单点登录组件提供接入指南,"浙里办"单点登陆组件,上架在IRS,为上架在IRS的应用,提供统一的单点登录解决方案,现阶段仅支持微 ...

最新文章

  1. 个人怎么发表期刊具体细节
  2. Oracle XQuery 过滤XML查询SQL
  3. 细节决定成败(竞赛错题经验总结)
  4. C++实现学生成绩管理系统
  5. 大数据,只是为了赚钱么?
  6. pandas去重函数
  7. NYOJ 5767 装背包
  8. matlab的SVM简单的例子
  9. ACNet论文阅读笔记
  10. Nokia7610彩信设置
  11. 华为仿苹果字体_华为mate40系列再次霸榜DXO,网友:无敌是多么寂寞
  12. 统计mysql里每条SQL语句执行的时间
  13. Day3 01 枚举
  14. 阿里云发布ECS企业级产品家族 19款实例族涵盖173个应用场景
  15. Bloom Filter布隆过滤器
  16. CarMaker入门第二课-创建第一个TestRun
  17. 实现商品分类回显(笔记)
  18. 富爸爸穷爸爸--读书笔记
  19. python图片查看器
  20. JTA transaction unexpectedly rolled back (maybe due to a timeout)

热门文章

  1. 未来两周目标计划---C++ and Disassembly(不积跬步无以至千里,不积小流无以成江海)...
  2. 第二章:QML基础组件之菜单栏MenuBar
  3. 还在用原生Tensorflow吗?试试TF-Slim吧
  4. 澳门大学计算机语言博士生导师王珊,四年资助百余万!澳门大学人文学院招收博士生(第二批)...
  5. 【面试题】755- 104道 CSS 面试题,助你查漏补缺(上)
  6. 车站信息管理系统Android,客运站车无线交互系统
  7. 选完校花又选校草,张朝阳为什么对造星如此执念?
  8. 用docker搭建discuz论坛
  9. vue项目中使用trackingjs人脸识别
  10. 微信放大字体导致的样式错乱