koa2+vue2+mysql 全栈开发记录

基于想要自己制作一个个人项目为由,于是有了这么一个开发记录(梳理开发过程也是一个知识巩固的过程)

koa2+vue2+mysql 个人的一个通用DEMO(本篇文章的范例)

前端工具

vue

vue-router

vuex

axios

element ui 页面UI组件

echartsjs 百度强大的图表展示

vue-admin-template 花裤衩大佬的一个实用管理后台模版 配套教程

vue-i18n 国际化

scss

后端工具

koa

koa-bodyparser 解析 PUT / POST 请求中的 body

koa-convert

koa-json

koa-jwt jwt鉴权

koa-logger

koa-mysql-session

koa-onerror

koa-router

koa-session-minimal

koa-static

koa-views

koa2-cors 处理跨域

md5 加密

moment 时间处理

mysql

前端篇

前端这边其实没什么好写的,主要是在vue-admin-template基础上做了一些修改

src/utils/request.js的修改

request拦截器

// request拦截器

service.interceptors.request.use(

config => {

if (store.getters.token) {

// config.headers['X-Token'] = getToken()

// 因为是jwt方式鉴权 所以在header头中改为如下写法

config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改

}

return config

},

error => {

// Do something with request error

console.log(error) // for debug

Promise.reject(error)

}

)

response 拦截器

// response 拦截器

service.interceptors.response.use(

response => {

/**

* code为非0是抛错 可结合自己业务进行修改

*/

const res = response.data

if (res.code !== 0) { // 因为后台返回值为0则是成功,所以将原来的20000改为了0

Message({

message: res.message,

type: 'error',

duration: 5 * 1000

})

// 70002:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;

if (res.code === 70002 || res.code === 50012 || res.code === 50014) {

MessageBox.confirm(

'你已被登出,可以取消继续留在该页面,或者重新登录',

'确定登出',

{

confirmButtonText: '重新登录',

cancelButtonText: '取消',

type: 'warning'

}

).then(() => {

store.dispatch('FedLogOut').then(() => {

location.reload() // 为了重新实例化vue-router对象 避免bug

})

})

}

return Promise.reject('error')

} else {

return response.data

}

},

error => {

console.log('err' + error) // for debug

Message({

message: error.message,

type: 'error',

duration: 5 * 1000

})

return Promise.reject(error)

}

)

封装了一个Echart组件

后端篇

构建项目目录

npm install -g koa-generator

koa2 /server && cd /server

npm install

安装组件

npm i jsonwebtoken koa-jwt koa-mysql-session koa-session-minimal koa2-cors md5 moment mysql save --save

配置app.js

const Koa = require('koa')

const jwt = require('koa-jwt')

const app = new Koa()

const views = require('koa-views')

const json = require('koa-json')

const onerror = require('koa-onerror')

const bodyparser = require('koa-bodyparser')

const logger = require('koa-logger')

const convert = require('koa-convert');

var session = require('koa-session-minimal')

var MysqlStore = require('koa-mysql-session')

var config = require('./config/default.js')

var cors = require('koa2-cors')

const users = require('./routes/users')

const account = require('./routes/account')

// error handler

onerror(app)

// 配置jwt错误返回

app.use(function(ctx, next) {

return next().catch(err => {

if (401 == err.status) {

ctx.status = 401

ctx.body = ApiErrorNames.getErrorInfo(ApiErrorNames.INVALID_TOKEN)

// ctx.body = {

// // error: err.originalError ? err.originalError.message : err.message

// }

} else {

throw err

}

})

})

// Unprotected middleware

app.use(function(ctx, next) {

if (ctx.url.match(/^\/public/)) {

ctx.body = 'unprotected\n'

} else {

return next()

}

})

// Middleware below this line is only reached if JWT token is valid

app.use(

jwt({ secret: config.secret, passthrough: true }).unless({

path: [/\/register/, /\/user\/login/]

})

)

// middlewares

app.use(convert(bodyparser({

enableTypes:['json', 'form', 'text']

})))

app.use(convert(json()))

app.use(convert(logger()))

app.use(require('koa-static')(__dirname + '/public'))

app.use(views(__dirname + '/views', {

extension: 'pug'

}))

// logger

app.use(async (ctx, next) => {

const start = new Date()

await next()

const ms = new Date() - start

console.log(`${ctx.method} ${ctx.url} - ${ms}ms`)

})

// cors

app.use(cors())

// routes

app.use(users.routes(), users.allowedMethods())

app.use(account.routes(), account.allowedMethods())

// error-handling

app.on('error', (err, ctx) => {

console.error('server error', err, ctx)

});

module.exports = app

新建config文件夹用于存放数据库连接等操作

default.js

// 数据库配置

const config = {

port: 3000,

database: {

DATABASE: 'xxx', //数据库

USERNAME: 'root', //用户

PASSWORD: 'xxx', //密码

PORT: '3306', //端口

HOST: '127.0.0.1' //服务ip地址

},

secret: 'jwt_secret'

}

module.exports = config

数据库相关(mysql)

createTables.js 一个简单的用户角色权限表 用户、角色、权限表的关系(mysql)

// 数据库表格创建

const createTable = {

users: `CREATE TABLE IF NOT EXISTS user_info (

id INT PRIMARY KEY NOT NULL AUTO_INCREMENT COMMENT '(自增长)',

user_id VARCHAR ( 100 ) NOT NULL COMMENT '账号',

user_name VARCHAR ( 100 ) NOT NULL COMMENT '用户名',

user_pwd VARCHAR ( 100 ) NOT NULL COMMENT '密码',

user_head VARCHAR ( 225 ) COMMENT '头像',

user_mobile VARCHAR ( 20 ) COMMENT '手机',

user_email VARCHAR ( 64 ) COMMENT '邮箱',

user_creatdata TIMESTAMP NOT NULL DEFAULT NOW( ) COMMENT '注册日期',

user_login_time TIMESTAMP DEFAULT NOW( ) COMMENT '登录时间',

user_count INT COMMENT '登录次数'

) ENGINE = INNODB charset = utf8;`,

role: `CREATE TABLE IF NOT EXISTS role_info (

id INT PRIMARY KEY NOT NULL AUTO_INCREMENT COMMENT '(自增长)',

role_name VARCHAR ( 20 ) NOT NULL COMMENT '角色名',

role_description VARCHAR ( 255 ) DEFAULT NULL COMMENT '描述'

) ENGINE = INNODB charset = utf8;`,

permission: `CREATE TABLE IF NOT EXISTS permission_info (

id INT PRIMARY KEY NOT NULL AUTO_INCREMENT COMMENT '(自增长)',

permission_name VARCHAR ( 20 ) NOT NULL COMMENT '权限名',

permission_description VARCHAR ( 255 ) DEFAULT NULL COMMENT '描述'

) ENGINE = INNODB charset = utf8;`,

userRole: `CREATE TABLE IF NOT EXISTS user_role (

id INT PRIMARY KEY NOT NULL AUTO_INCREMENT COMMENT '(自增长)',

user_id INT NOT NULL COMMENT '关联用户',

role_id INT NOT NULL COMMENT '关联角色',

KEY fk_user_role_role_info_1 ( role_id ),

KEY fk_user_role_user_info_1 ( user_id ),

CONSTRAINT fk_user_role_role_info_1 FOREIGN KEY ( role_id ) REFERENCES role_info ( id ) ON DELETE CASCADE ON UPDATE CASCADE,

CONSTRAINT fk_user_role_user_info_1 FOREIGN KEY ( user_id ) REFERENCES user_info ( id ) ON DELETE CASCADE ON UPDATE CASCADE

) ENGINE = INNODB charset = utf8;`,

rolePermission: `CREATE TABLE IF NOT EXISTS role_permission (

id INT PRIMARY KEY NOT NULL AUTO_INCREMENT COMMENT '(自增长)',

role_id INT NOT NULL COMMENT '关联角色',

permission_id INT NOT NULL COMMENT '关联权限',

KEY fk_role_permission_role_info_1 ( role_id ),

KEY fk_role_permission_permission_info_1 ( permission_id ),

CONSTRAINT fk_role_permission_role_info_1 FOREIGN KEY ( role_id ) REFERENCES role_info ( id ) ON DELETE CASCADE ON UPDATE CASCADE,

CONSTRAINT fk_role_permission_permission_info_1 FOREIGN KEY ( permission_id ) REFERENCES permission_info ( id ) ON DELETE CASCADE ON UPDATE CASCADE

) ENGINE = INNODB charset = utf8;`

}

module.exports = createTable

创建lib文件夹 用于存储数据库查询语句

mysql.js

const mysql = require('mysql')

const config = require('../config/default')

const createTables = require('../config/createTables.js')

var pool = mysql.createPool({

host: config.database.HOST,

user: config.database.USERNAME,

password: config.database.PASSWORD,

database: config.database.DATABASE

})

let query = function(sql, values) {

return new Promise((resolve, reject) => {

pool.getConnection(function(err, connection) {

if (err) {

resolve(err)

} else {

connection.query(sql, values, (err, rows) => {

if (err) {

reject(err)

} else {

resolve(rows)

}

connection.release()

})

}

})

})

}

let createTable = function(sql) {

return query(sql, [])

}

// 建表

// createTable(createTables.users)

// createTable(createTables.role)

// createTable(createTables.permission)

// createTable(createTables.userRole)

// createTable(createTables.rolePermission)

// 查询用户是否存在

let findUser = async function(id) {

let _sql = `

SELECT * FROM user_info where user_id="${id}" limit 1;

`

let result = await query(_sql)

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

result = result[0]

} else {

result = null

}

return result

}

// 查询用户以及用户角色

let findUserAndRole = async function(id) {

let _sql = `

SELECT u.*,r.role_name FROM user_info u,user_role ur,role_info r where u.id=(SELECT id FROM user_info where user_id="${id}" limit 1) and ur.user_id=u.id and r.id=ur.user_id limit 1;

`

let result = await query(_sql)

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

result = result[0]

} else {

result = null

}

return result

}

// 更新用户登录次数和登录时间

let UpdataUserInfo = async function(value) {

let _sql =

'UPDATE user_info SET user_count = ?, user_login_time = ? WHERE id = ?;'

return query(_sql, value)

}

module.exports = {

//暴露方法

createTable,

findUser,

findUserAndRole,

UpdataUserInfo,

getShopAndAccount

}

koa 路由配置

接口报错信息统一方法 创建error文件夹

ApiErrorNames.js

/**

* API错误名称

*/

var ApiErrorNames = {};

ApiErrorNames.UNKNOW_ERROR = "UNKNOW_ERROR";

ApiErrorNames.SUCCESS = "SUCCESS";

/* 参数错误:10001-19999 */

ApiErrorNames.PARAM_IS_INVALID = 'PARAM_IS_INVALID';

ApiErrorNames.PARAM_IS_BLANK = 'PARAM_IS_BLANK';

ApiErrorNames.PARAM_TYPE_BIND_ERROR = 'PARAM_TYPE_BIND_ERROR';

ApiErrorNames.PARAM_NOT_COMPLETE = 'PARAM_NOT_COMPLETE';

/* 用户错误:20001-29999*/

ApiErrorNames.USER_NOT_LOGGED_IN = 'USER_NOT_LOGGED_IN';

ApiErrorNames.USER_LOGIN_ERROR = 'USER_LOGIN_ERROR';

ApiErrorNames.USER_ACCOUNT_FORBIDDEN = 'USER_ACCOUNT_FORBIDDEN';

ApiErrorNames.USER_NOT_EXIST = 'USER_NOT_EXIST';

ApiErrorNames.USER_HAS_EXISTED = 'USER_HAS_EXISTED';

/* 业务错误:30001-39999 */

ApiErrorNames.SPECIFIED_QUESTIONED_USER_NOT_EXIST = 'SPECIFIED_QUESTIONED_USER_NOT_EXIST';

/* 系统错误:40001-49999 */

ApiErrorNames.SYSTEM_INNER_ERROR = 'SYSTEM_INNER_ERROR';

/* 数据错误:50001-599999 */

ApiErrorNames.RESULE_DATA_NONE = 'RESULE_DATA_NONE';

ApiErrorNames.DATA_IS_WRONG = 'DATA_IS_WRONG';

ApiErrorNames.DATA_ALREADY_EXISTED = 'DATA_ALREADY_EXISTED';

/* 接口错误:60001-69999 */

ApiErrorNames.INTERFACE_INNER_INVOKE_ERROR = 'INTERFACE_INNER_INVOKE_ERROR';

ApiErrorNames.INTERFACE_OUTTER_INVOKE_ERROR = 'INTERFACE_OUTTER_INVOKE_ERROR';

ApiErrorNames.INTERFACE_FORBID_VISIT = 'INTERFACE_FORBID_VISIT';

ApiErrorNames.INTERFACE_ADDRESS_INVALID = 'INTERFACE_ADDRESS_INVALID';

ApiErrorNames.INTERFACE_REQUEST_TIMEOUT = 'INTERFACE_REQUEST_TIMEOUT';

ApiErrorNames.INTERFACE_EXCEED_LOAD = 'INTERFACE_EXCEED_LOAD';

/* 权限错误:70001-79999 */

ApiErrorNames.PERMISSION_NO_ACCESS = 'PERMISSION_NO_ACCESS';

ApiErrorNames.INVALID_TOKEN = 'INVALID_TOKEN';

/**

* API错误名称对应的错误信息

*/

const error_map = new Map();

error_map.set(ApiErrorNames.SUCCESS, { code: 0, message: '成功' });

error_map.set(ApiErrorNames.UNKNOW_ERROR, { code: -1, message: '未知错误' });

/* 参数错误:10001-19999 */

error_map.set(ApiErrorNames.PARAM_IS_INVALID, { code: 10001, message: '参数无效' });

error_map.set(ApiErrorNames.PARAM_IS_BLANK, { code: 10002, message: '参数为空' });

error_map.set(ApiErrorNames.PARAM_TYPE_BIND_ERROR, { code: 10003, message: '参数类型错误' });

error_map.set(ApiErrorNames.PARAM_NOT_COMPLETE, { code: 10004, message: '参数缺失' });

/* 用户错误:20001-29999*/

error_map.set(ApiErrorNames.USER_NOT_LOGGED_IN, { code: 20001, message: '用户未登录' });

error_map.set(ApiErrorNames.USER_LOGIN_ERROR, { code: 20002, message: '账号不存在或密码错误' });

error_map.set(ApiErrorNames.USER_ACCOUNT_FORBIDDEN, { code: 20003, message: '账号已被禁用' });

error_map.set(ApiErrorNames.USER_NOT_EXIST, { code: 20004, message: '用户不存在' });

error_map.set(ApiErrorNames.USER_HAS_EXISTED, { code: 20005, message: '用户已存在' });

/* 业务错误:30001-39999 */

error_map.set(ApiErrorNames.SPECIFIED_QUESTIONED_USER_NOT_EXIST, { code: 30001, message: '某业务出现问题' });

/* 系统错误:40001-49999 */

error_map.set(ApiErrorNames.SYSTEM_INNER_ERROR, { code: 40001, message: '系统繁忙,请稍后重试' });

/* 数据错误:50001-599999 */

error_map.set(ApiErrorNames.RESULE_DATA_NONE, { code: 50001, message: '数据未找到' });

error_map.set(ApiErrorNames.DATA_IS_WRONG, { code: 50002, message: '数据有误' });

error_map.set(ApiErrorNames.DATA_ALREADY_EXISTED, { code: 50003, message: '数据已存在' });

/* 接口错误:60001-69999 */

error_map.set(ApiErrorNames.INTERFACE_INNER_INVOKE_ERROR, { code: 60001, message: '内部系统接口调用异常' });

error_map.set(ApiErrorNames.INTERFACE_OUTTER_INVOKE_ERROR, { code: 60002, message: '外部系统接口调用异常' });

error_map.set(ApiErrorNames.INTERFACE_FORBID_VISIT, { code: 60003, message: '该接口禁止访问' });

error_map.set(ApiErrorNames.INTERFACE_ADDRESS_INVALID, { code: 60004, message: '接口地址无效' });

error_map.set(ApiErrorNames.INTERFACE_REQUEST_TIMEOUT, { code: 60005, message: '接口请求超时' });

error_map.set(ApiErrorNames.INTERFACE_EXCEED_LOAD, { code: 60006, message: '接口负载过高' });

/* 权限错误:70001-79999 */

error_map.set(ApiErrorNames.PERMISSION_NO_ACCESS, { code: 70001, message: '无访问权限' });

error_map.set(ApiErrorNames.INVALID_TOKEN, { code: 70002, message: '无效token' });

//根据错误名称获取错误信息

ApiErrorNames.getErrorInfo = (error_name) => {

var error_info;

if (error_name) {

error_info = error_map.get(error_name);

}

//如果没有对应的错误信息,默认'未知错误'

if (!error_info) {

error_name = UNKNOW_ERROR;

error_info = error_map.get(error_name);

}

return error_info;

}

//返回正确信息

ApiErrorNames.getSuccessInfo = (data) => {

var success_info;

let name = 'SUCCESS';

success_info = error_map.get(name);

if (data) {

success_info.data = data

}

return success_info;

}

module.exports = ApiErrorNames;

创建controller文件夹

对路由users进行逻辑编写

const mysqlModel = require('../lib/mysql') //引入数据库方法

const jwt = require('jsonwebtoken')

const config = require('../config/default.js')

const ApiErrorNames = require('../error/ApiErrorNames.js')

const moment = require('moment')

/**

* 普通登录

*/

exports.login = async (ctx, next) => {

const { body } = ctx.request

try {

const user = await mysqlModel.findUser(body.username)

if (!user) {

// ctx.status = 401

ctx.body = ApiErrorNames.getErrorInfo(ApiErrorNames.USER_NOT_EXIST)

return

}

let bodys = await JSON.parse(JSON.stringify(user))

// 匹配密码是否相等

if ((await user.user_pwd) === body.password) {

let data = {

user: user.user_id,

// 生成 token 返回给客户端

token: jwt.sign(

{

data: user.user_id,

// 设置 token 过期时间

exp: Math.floor(Date.now() / 1000) + 60 * 60 // 60 seconds * 60 minutes = 1 hour

},

config.secret

)

}

ctx.body = ApiErrorNames.getSuccessInfo(data)

} else {

ctx.body = ApiErrorNames.getErrorInfo(ApiErrorNames.USER_LOGIN_ERROR)

}

} catch (error) {

ctx.throw(500)

}

}

/**

* 获取用户信息

*/

exports.info = async (ctx, next) => {

const { body } = ctx.request

// console.log(body)

try {

const token = ctx.header.authorization

let payload

if (token) {

payload = await jwt.verify(token.split(' ')[1], config.secret) // 解密,获取payload

const user = await mysqlModel.findUserAndRole(payload.data)

if (!user) {

ctx.body = ApiErrorNames.getErrorInfo(ApiErrorNames.USER_NOT_EXIST)

} else {

let cont = user.user_count + 1

let updateInfo = [

cont,

moment().format('YYYY-MM-DD HH:mm:ss'),

user.id

]

await mysqlModel

.UpdataUserInfo(updateInfo)

.then(res => {

let data = {

avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',

name: user.user_id,

// roles: [user.user_admin === 0 ? 'admin' : '']

roles: [user.role_name]

}

ctx.body = ApiErrorNames.getSuccessInfo(data)

})

.catch(err => {

ctx.body = ApiErrorNames.getErrorInfo(ApiErrorNames.DATA_IS_WRONG)

})

}

} else {

ctx.body = ApiErrorNames.getErrorInfo(ApiErrorNames.INVALID_TOKEN)

}

} catch (error) {

ctx.throw(500)

}

}

/**

* 退出登录

*/

exports.logout = async (ctx, next) => {

try {

// ctx.status = 200

ctx.body = ApiErrorNames.getSuccessInfo()

} catch (error) {

ctx.throw(500)

}

}

routes中的users.js

const router = require('koa-router')() //引入路由函数

const userControl = require('../controller/users') //引入逻辑

// const config = require('../config/default.js')

router.get('/', async (ctx, next) => {

'use strict'

ctx.redirect('/user/login')

})

// 路由中间间,页面路由到/,就是端口号的时候,(网址),页面指引到//user/login

router.get('/user/info', userControl.info)

router.post('/user/logout', userControl.logout)

router.post('/user/login', userControl.login)

module.exports = router

//将页面暴露出去

备注

ctx.request 获取post请求中的body

ctx.query.xx 获取get请求中的参数

router.prefix('/account') 给router实例添加前缀

koa2 mysql_koa2+vue+mysql 全栈开发记录相关推荐

  1. 【全栈之巅】Node.js + Vue.js 全栈开发王者荣耀手机端官网和管理后台学习笔记(4.1-4.10)

    [全栈之巅]Node.js + Vue.js 全栈开发王者荣耀手机端官网和管理后台学习笔记(4.1-4.10) 本项目是 学习Bilibili 全栈之巅 视频教程相关源码和体会 https://git ...

  2. Flask Vue.js全栈开发

    Flask Vue.js全栈开发的 最新完整代码 及使用方式 本系列的最新代码及使用方式将持续更新到: http://www.madmalls.com/blog/post/latest-code/ 1 ...

  3. spring boot + vue + element-ui全栈开发入门——基于Electron桌面应用开发

     前言 Electron是由Github开发,用HTML,CSS和JavaScript来构建跨平台桌面应用程序的一个开源库. Electron通过将Chromium和Node.js合并到同一个运行时环 ...

  4. spring boot + vue + element-ui全栈开发入门

    今天想弄弄element-ui  然后就在网上找了个例子 感觉还是可以用的  第一步是完成了  果断 拿过来  放到我这里这  下面直接是连接  点进去 就可以用啊 本想着不用vue   直接导入连接 ...

  5. Node.js+Vue.js全栈开发王者荣耀手机端官网和管理后台(一)

    文章目录 [全栈之巅]Node.js+Vue.js全栈开发王者荣耀手机端官网和管理后台(一) 工具安装和环境搭建 初始化项目 基于ElementUI的后台管理基础界面搭建 创建分类(客户端) 创建分类 ...

  6. Python全栈开发记录_第一篇(循环练习及杂碎的知识点)

    Python全栈开发记录只为记录全栈开发学习过程中一些难和重要的知识点,还有问题及课后题目,以供自己和他人共同查看.(该篇代码行数大约:300行) 知识点1:优先级:not>and 短路原则:a ...

  7. 【全栈之巅】Node.js + Vue.js 全栈开发王者荣耀手机端官网和管理后台学习笔记(3.11-3.12)

    [全栈之巅]Node.js + Vue.js 全栈开发王者荣耀手机端官网和管理后台学习笔记(3.11-3.12) 本项目是 学习Bilibili 全栈之巅 视频教程相关源码和体会 https://gi ...

  8. Node.js + Vue.js 全栈开发王者荣耀手机端官网和管理后台

    前言 最近在跟着Johnny的全栈之巅系列视频教程学习使用NodeJS+Express+Element-UI+MongoDB等开发王者荣耀,服务端server,移动端web,admin,学到了不少东西 ...

  9. 学习【全栈之巅】Node.js + Vue.js 全栈开发王者荣耀手机端官网和管理后台笔记(2.10-2.12)

    [全栈之巅]Node.js + Vue.js 全栈开发王者荣耀手机端官网和管理后台 本项目是 学习Bilibili 全栈之巅 视频教程相关源码和体会 https://gitee.com/blaunic ...

最新文章

  1. 提升城镇化质量 有利于激活智慧城市潜能
  2. docker 常用操作
  3. python基础语法手册format-Python format 格式化函数
  4. matlab径向分布函数作图_常见的概率分布(matlab作图)
  5. mindspore学习之使用obsutil工具向桶中上传数据
  6. Bootstrap的响应式,当文字超过div长度,换行问题的处理!
  7. 当代国人绝难做到的10件平常事
  8. c语言江宝钏实验六答案,C语言程序设计江宝钏著实验六答案
  9. jsp的九大内置对象和作用域
  10. C#调用VB进行简繁转换
  11. Python助力性能测试——报文批量生成
  12. 2020-09-02 微机原理与接口课程复习题
  13. nanopi制作个人服务器,NanoPi K2 服务器系统镜像
  14. c# 微信开发 《生成带参数的关注二维码》
  15. python 打开txt_python编程之文件操作
  16. [C#] TestHttpPost:测试Http的POST方法的小工具
  17. Nmap内网扫描端口
  18. Linux加密框架 crypto 哈希算法说明 同步哈希shash_alg | 异步哈希 ahash_alg | 通用部分抽象 hash_alg_common
  19. JS之如何判断数据类型
  20. Mac 下使用ISIS 处理数据 -CTX,HiRISE,LRO

热门文章

  1. JDK1.5提供的原子类原理及使用
  2. [CQOI2011]放棋子 题解(dp+组合数学)
  3. centos7安装Nginx 配置及反向代理
  4. ubantu的下载和在虚拟机VM中的安装
  5. idea 和 maven学习
  6. 结对项目开发之电梯调度问题
  7. jpa 数据库方言_使用Hibernate和Oracle 10g方言,如何用JPA生成我的id?
  8. html5 拖拽滚动条,js 简易版滚动条实例(适用于移动端H5开发)
  9. 数据上报java_通过Jaeger上报Java应用数据
  10. mysql索引的概念和作用_索引的基本概念及作用