metaMask login

This commit is contained in:
hujiabin 2024-06-29 16:52:33 +08:00
parent 5fce327ce6
commit a32a4e4e39
7 changed files with 767 additions and 568 deletions

View File

@ -1,114 +1,115 @@
{ {
"name": "vue-element-admin", "name": "vue-element-admin",
"version": "4.4.0", "version": "4.4.0",
"description": "A magical vue admin. An out-of-box UI solution for enterprise applications. Newest development stack of vue. Lots of awesome features", "description": "A magical vue admin. An out-of-box UI solution for enterprise applications. Newest development stack of vue. Lots of awesome features",
"author": "Pan <panfree23@gmail.com>", "author": "Pan <panfree23@gmail.com>",
"scripts": { "scripts": {
"dev": "vue-cli-service serve", "dev": "vue-cli-service serve",
"lint": "eslint --ext .js,.vue src", "lint": "eslint --ext .js,.vue src",
"build:prod": "vue-cli-service build", "build:prod": "vue-cli-service build",
"build:stage": "vue-cli-service build --mode staging", "build:stage": "vue-cli-service build --mode staging",
"preview": "node build/index.js --preview", "preview": "node build/index.js --preview",
"new": "plop", "new": "plop",
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml", "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
"test:unit": "jest --clearCache && vue-cli-service test:unit", "test:unit": "jest --clearCache && vue-cli-service test:unit",
"test:ci": "npm run lint && npm run test:unit" "test:ci": "npm run lint && npm run test:unit"
}, },
"dependencies": { "dependencies": {
"axios": "0.18.1", "axios": "0.18.1",
"clipboard": "2.0.4", "clipboard": "2.0.4",
"codemirror": "5.45.0", "codemirror": "5.45.0",
"core-js": "3.6.5", "core-js": "3.6.5",
"driver.js": "0.9.5", "driver.js": "0.9.5",
"dropzone": "5.5.1", "dropzone": "5.5.1",
"echarts": "4.2.1", "echarts": "4.2.1",
"element-ui": "2.13.2", "element-ui": "2.13.2",
"file-saver": "2.0.1", "file-saver": "2.0.1",
"fuse.js": "3.4.4", "fuse.js": "3.4.4",
"js-cookie": "2.2.0", "js-cookie": "2.2.0",
"jsonlint": "1.6.3", "jsonlint": "1.6.3",
"jszip": "3.2.1", "jszip": "3.2.1",
"normalize.css": "7.0.0", "normalize.css": "7.0.0",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"path-to-regexp": "2.4.0", "path-to-regexp": "2.4.0",
"pinyin": "2.9.0", "pinyin": "2.9.0",
"screenfull": "4.2.0", "screenfull": "4.2.0",
"script-loader": "0.7.2", "script-loader": "0.7.2",
"sortablejs": "1.8.4", "sortablejs": "1.8.4",
"tui-editor": "1.3.3", "tui-editor": "1.3.3",
"vue": "2.6.10", "vue": "2.6.10",
"vue-count-to": "1.0.13", "vue-count-to": "1.0.13",
"vue-i18n": "7.3.2", "vue-i18n": "7.3.2",
"vue-router": "3.0.2", "vue-router": "3.0.2",
"vue-splitpane": "1.0.4", "vue-splitpane": "1.0.4",
"vuedraggable": "2.20.0", "vuedraggable": "2.20.0",
"vuex": "3.1.0", "vuex": "3.1.0",
"xlsx": "0.14.1" "web3": "^1.7.4",
}, "xlsx": "0.14.1"
"devDependencies": { },
"@vue/cli-plugin-babel": "4.4.4", "devDependencies": {
"@vue/cli-plugin-eslint": "4.4.4", "@vue/cli-plugin-babel": "4.4.4",
"@vue/cli-plugin-unit-jest": "4.4.4", "@vue/cli-plugin-eslint": "4.4.4",
"@vue/cli-service": "4.4.4", "@vue/cli-plugin-unit-jest": "4.4.4",
"@vue/test-utils": "1.0.0-beta.29", "@vue/cli-service": "4.4.4",
"autoprefixer": "9.5.1", "@vue/test-utils": "1.0.0-beta.29",
"babel-eslint": "10.1.0", "autoprefixer": "9.5.1",
"babel-jest": "23.6.0", "babel-eslint": "10.1.0",
"babel-plugin-dynamic-import-node": "2.3.3", "babel-jest": "23.6.0",
"chalk": "2.4.2", "babel-plugin-dynamic-import-node": "2.3.3",
"chokidar": "2.1.5", "chalk": "2.4.2",
"connect": "3.6.6", "chokidar": "2.1.5",
"eslint": "6.7.2", "connect": "3.6.6",
"eslint-plugin-react": "^7.33.2", "eslint": "6.7.2",
"eslint-plugin-vue": "6.2.2", "eslint-plugin-react": "^7.33.2",
"html-webpack-plugin": "3.2.0", "eslint-plugin-vue": "6.2.2",
"husky": "1.3.1", "html-webpack-plugin": "3.2.0",
"lint-staged": "8.1.5", "husky": "1.3.1",
"mockjs": "1.0.1-beta3", "lint-staged": "8.1.5",
"plop": "2.3.0", "mockjs": "1.0.1-beta3",
"runjs": "4.3.2", "plop": "2.3.0",
"sass": "1.26.2", "runjs": "4.3.2",
"sass-loader": "8.0.2", "sass": "1.26.2",
"script-ext-html-webpack-plugin": "2.1.3", "sass-loader": "8.0.2",
"serve-static": "1.13.2", "script-ext-html-webpack-plugin": "2.1.3",
"svg-sprite-loader": "4.1.3", "serve-static": "1.13.2",
"svgo": "1.2.0", "svg-sprite-loader": "4.1.3",
"vue-template-compiler": "2.6.10" "svgo": "1.2.0",
}, "vue-template-compiler": "2.6.10"
"browserslist": [ },
"> 1%", "browserslist": [
"last 2 versions" "> 1%",
], "last 2 versions"
"bugs": { ],
"url": "https://github.com/PanJiaChen/vue-element-admin/issues" "bugs": {
}, "url": "https://github.com/PanJiaChen/vue-element-admin/issues"
"engines": { },
"node": ">=8.9", "engines": {
"npm": ">= 3.0.0" "node": ">=8.9",
}, "npm": ">= 3.0.0"
"keywords": [ },
"vue", "keywords": [
"admin", "vue",
"dashboard", "admin",
"element-ui", "dashboard",
"boilerplate", "element-ui",
"admin-template", "boilerplate",
"management-system" "admin-template",
], "management-system"
"license": "MIT", ],
"lint-staged": { "license": "MIT",
"src/**/*.{js,vue}": [ "lint-staged": {
"eslint --fix", "src/**/*.{js,vue}": [
"git add" "eslint --fix",
] "git add"
}, ]
"husky": { },
"hooks": { "husky": {
"pre-commit": "lint-staged" "hooks": {
} "pre-commit": "lint-staged"
}, }
"repository": { },
"type": "git", "repository": {
"url": "git+https://github.com/PanJiaChen/vue-element-admin.git" "type": "git",
} "url": "git+https://github.com/PanJiaChen/vue-element-admin.git"
} }
}

View File

@ -1,23 +1,38 @@
import request from '@/utils/request' import request from '@/utils/request'
export function login(data) { export function login(data) {
return request({ return request({
url: '/user/login', url: '/user/login',
method: 'post', method: 'post',
data data
}) })
} }
export function metamaskLogin(data) {
export function getInfo(token) { return request({
return request({ url: '/user/metamask-login',
url: '/user/info', method: 'post',
method: 'get' data
}) })
} }
export function logout() { export function getInfo(token) {
return request({ return request({
url: '/user/logout', url: '/user/info',
method: 'get' method: 'get'
}) })
} }
export function logout() {
return request({
url: '/user/logout',
method: 'get'
})
}
export function getNonce(account) {
return request({
url: '/user/getNonce?account=' + account,
method: 'get'
})
}

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718869327969" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5949" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M517.77536 972.8c133.98016-30.19776 211.70176-70.42048 301.2608-156.3136 29.32736-28.11904 174.04928-63.68256 190.4128-100.83328 23.72608-53.8624-58.2144-115.72224-58.2144-178.40128 0-240.54784-194.06848-435.5584-433.4592-435.5584-239.38048 0-433.44896 195.01056-433.44896 435.5584 0 68.21888-86.13888 124.14976-58.3168 181.62688 15.28832 31.56992 164.1472 73.216 189.51168 97.60768C305.08032 902.68672 383.55968 942.60224 517.77536 972.8z" fill="#FFD797" p-id="5950"></path><path d="M908.77952 65.5872c-0.88064 1.40288 79.0528 276.7872 32.68608 379.32032a439.0912 439.0912 0 0 1 9.76896 92.34432c0 62.6688 81.94048 124.54912 58.2144 178.40128-1.71008 3.8912-4.88448 7.74144-9.216 11.55072-146.59584-39.44448-297.6768-4.31104-453.25312 105.41056l-5.64224 4.00384H494.592C336.24064 723.2 182.55872 686.92992 33.536 727.78752c-3.54304-3.0208-6.11328-5.9904-7.5264-8.9088-27.82208-57.47712 58.3168-113.408 58.3168-181.62688 0-27.4944 2.53952-54.39488 7.38304-80.47616-59.62752-90.97216 27.136-389.7344 26.22464-391.18848 81.39776-14.41792 159.06816 8.76544 233.03168 69.5296a430.40768 430.40768 0 0 1 166.8096-33.42336 430.44864 430.44864 0 0 1 160.94208 31.01696C751.77984 73.728 828.4672 51.36384 908.77952 65.5872z" fill="#FF6F24" p-id="5951"></path><path d="M283.1872 315.51488c31.10912-104.9088-23.97184-186.24512-165.25312-244.0192 1.024 1.65888-77.7216 340.93056 28.89728 383.11936-3.51232 2.92864 41.94304-43.42784 136.35584-139.10016z m461.48608 0c-31.10912-104.9088 23.97184-186.24512 165.24288-244.0192-1.024 1.65888 77.7216 340.93056-28.89728 383.11936 3.52256 2.92864-41.9328-43.42784-136.3456-139.10016z" fill="#922101" opacity=".574" p-id="5952"></path><path d="M647.69024 629.00224c-0.21504-60.29312 44.56448-83.94752 134.3488-70.95296-2.78528 3.072 6.78912 44.93312-40.51968 69.15072-11.264 5.76512-42.53696 6.36928-93.82912 1.80224z m-251.46368 0c0.22528-60.29312-44.55424-83.94752-134.33856-70.95296 2.78528 3.072-6.79936 44.93312 40.50944 69.15072 11.264 5.76512 42.53696 6.36928 93.82912 1.80224z" fill="#350000" p-id="5953"></path><path d="M743.10656 565.72928l0.04096 0.1536c0.78848 3.42016 1.20832 6.99392 1.20832 10.6496 0 23.4496-17.14176 42.82368-39.424 46.00832-3.42016 0.1536-7.2192 0.21504-11.39712 0.18432a45.93664 45.93664 0 0 1-36.7104-26.61376c10.25024-24.832 39.0144-34.95936 86.28224-30.38208z m-459.84768 0l-0.04096 0.1536a47.13472 47.13472 0 0 0-1.20832 10.6496c0 23.4496 17.14176 42.82368 39.424 46.00832 3.40992 0.1536 7.2192 0.21504 11.39712 0.18432a45.93664 45.93664 0 0 0 36.7104-26.61376c-10.26048-24.832-39.0144-34.95936-86.28224-30.38208z" fill="#923320" p-id="5954"></path><path d="M328.6528 574.72c-4.12672 10.24-6.25664 17.43872-6.38976 21.58592-0.23552 6.89152 1.8944 12.5952 6.38976 17.1008 0.768 0.54272 6.44096-5.30432 6.144-17.87904-0.1024-3.72736-2.1504-10.6496-6.144-20.80768z m372.72576 0c-4.12672 10.24-6.2464 17.43872-6.38976 21.58592-0.22528 6.89152 1.90464 12.5952 6.38976 17.1008 0.77824 0.54272 6.44096-5.30432 6.144-17.87904-0.09216-3.72736-2.14016-10.6496-6.144-20.80768z" fill="#FFFFFF" opacity=".632" p-id="5955"></path><path d="M520.42752 863.8464c37.24288 0 67.4304-27.81184 67.4304-62.1056 0-34.304-134.8608-34.304-134.8608 0s30.19776 62.1056 67.4304 62.1056z" fill="#040C12" p-id="5956"></path><path d="M520.15104 152.02304c8.86784 146.97472 40.57088 238.83776 95.10912 275.57888l2.19136 1.4336-0.24576 0.07168c-6.42048 2.53952-71.46496 73.5744-96.80896 138.2912-0.0512 0.08192-0.09216 0-0.12288-0.26624-0.04096 0.256-0.08192 0.34816-0.12288 0.26624-25.35424-64.7168-90.38848-135.75168-96.8192-138.2912l-0.24576-0.07168 2.2016-1.4336c53.71904-36.1984 85.1968-125.87008 94.45376-269.03552l0.4096-6.5536z" fill="#C74D22" p-id="5957"></path><path d="M735.15008 721.8688c49.55136 0 89.72288-17.17248 89.72288-38.3488s-40.17152-38.3488-89.72288-38.3488c-49.5616 0-89.72288 17.17248-89.72288 38.3488s40.17152 38.3488 89.72288 38.3488z m-443.89376 0c49.5616 0 89.72288-17.17248 89.72288-38.3488s-40.17152-38.3488-89.72288-38.3488-89.72288 17.17248-89.72288 38.3488 40.17152 38.3488 89.72288 38.3488z" fill="#C53028" fill-opacity=".3" p-id="5958"></path></svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -1,16 +1,17 @@
const getters = { const getters = {
sidebar: state => state.app.sidebar, sidebar: state => state.app.sidebar,
language: state => state.app.language, language: state => state.app.language,
size: state => state.app.size, size: state => state.app.size,
device: state => state.app.device, device: state => state.app.device,
visitedViews: state => state.tagsView.visitedViews, visitedViews: state => state.tagsView.visitedViews,
cachedViews: state => state.tagsView.cachedViews, cachedViews: state => state.tagsView.cachedViews,
token: state => state.user.token, token: state => state.user.token,
name: state => state.user.name, name: state => state.user.name,
introduction: state => state.user.introduction, nonce: state => state.user.nonce,
roles: state => state.user.roles, introduction: state => state.user.introduction,
permission_routes: state => state.permission.routes, roles: state => state.user.roles,
errorLogs: state => state.errorLog.logs, permission_routes: state => state.permission.routes,
email: state => state.emailView.email errorLogs: state => state.errorLog.logs,
} email: state => state.emailView.email
export default getters }
export default getters

View File

@ -1,127 +1,138 @@
import { login, logout, getInfo } from '@/api/user' import { login, logout, getInfo } from '@/api/user'
import { getToken, setToken, removeToken } from '@/utils/auth' import { getToken, setToken, removeToken } from '@/utils/auth'
import router, { resetRouter } from '@/router' import router, { resetRouter } from '@/router'
const state = { const state = {
token: getToken(), token: getToken(),
name: '', name: '',
introduction: '', introduction: '',
roles: [] roles: [],
} nonce: ''
}
const mutations = {
SET_TOKEN: (state, token) => { const mutations = {
state.token = token SET_TOKEN: (state, token) => {
}, state.token = token
SET_INTRODUCTION: (state, introduction) => { },
state.introduction = introduction SET_INTRODUCTION: (state, introduction) => {
}, state.introduction = introduction
SET_NAME: (state, name) => { },
state.name = name SET_NAME: (state, name) => {
}, state.name = name
SET_ROLES: (state, roles) => { },
state.roles = roles SET_ROLES: (state, roles) => {
} state.roles = roles
} },
SET_NONCE: (state, nonce) => {
const actions = { state.nonce = nonce
// user login }
login({ commit }, userInfo) { }
const { username, password } = userInfo
return new Promise((resolve, reject) => { const actions = {
login({ username: username.trim(), password: password }).then(response => { // user login
const { token } = response login({ commit }, userInfo) {
commit('SET_TOKEN', token) const { username, password } = userInfo
setToken(token) return new Promise((resolve, reject) => {
resolve() login({ username: username.trim(), password: password }).then(response => {
}).catch(error => { const { token } = response
reject(error) commit('SET_TOKEN', token)
}) setToken(token)
}) resolve()
}, }).catch(error => {
reject(error)
// get user info })
getInfo({ commit, state }) { })
return new Promise((resolve, reject) => { },
getInfo().then(response => {
const { data } = response // get user info
getInfo({ commit, state }) {
if (!data) { return new Promise((resolve, reject) => {
reject('Verification failed, please Login again.') getInfo().then(response => {
} const { data } = response
const { roles } = data if (!data) {
reject('Verification failed, please Login again.')
// roles must be a non-empty array }
if (!roles || roles.length <= 0) {
reject('getInfo: roles must be a non-null array!') const { roles } = data
}
// roles must be a non-empty array
commit('SET_ROLES', roles) if (!roles || roles.length <= 0) {
// commit('SET_NAME', name) reject('getInfo: roles must be a non-null array!')
// }
// commit('SET_INTRODUCTION', introduction)
resolve(data) commit('SET_ROLES', roles)
}).catch(error => { // commit('SET_NAME', name)
reject(error) //
}) // commit('SET_INTRODUCTION', introduction)
}) resolve(data)
}, }).catch(error => {
reject(error)
// user logout })
logout({ commit, state, dispatch }) { })
return new Promise((resolve, reject) => { },
logout(state.token).then(() => {
commit('SET_TOKEN', '') // user logout
commit('SET_ROLES', []) logout({ commit, state, dispatch }) {
removeToken() return new Promise((resolve, reject) => {
resetRouter() logout(state.token).then(() => {
commit('SET_TOKEN', '')
// reset visited views and cached views commit('SET_ROLES', [])
// to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485 removeToken()
dispatch('tagsView/delAllViews', null, { root: true }) resetRouter()
resolve() // reset visited views and cached views
}).catch(error => { // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
reject(error) dispatch('tagsView/delAllViews', null, { root: true })
})
}) resolve()
}, }).catch(error => {
reject(error)
// remove token })
resetToken({ commit }) { })
return new Promise(resolve => { },
commit('SET_TOKEN', '')
commit('SET_ROLES', []) // remove token
removeToken() resetToken({ commit }) {
resolve() return new Promise(resolve => {
}) commit('SET_TOKEN', '')
}, commit('SET_ROLES', [])
removeToken()
// dynamically modify permissions resolve()
async changeRoles({ commit, dispatch }, role) { })
const token = role + '-token' },
// get nonce
commit('SET_TOKEN', token) getNonce({ commit }, nonce) {
setToken(token) return new Promise(resolve => {
commit('SET_NONCE', nonce)
const { roles } = await dispatch('getInfo') resolve()
})
resetRouter() },
// generate accessible routes map based on roles // dynamically modify permissions
const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true }) async changeRoles({ commit, dispatch }, role) {
// dynamically add accessible routes const token = role + '-token'
router.addRoutes(accessRoutes)
commit('SET_TOKEN', token)
// reset visited views and cached views setToken(token)
dispatch('tagsView/delAllViews', null, { root: true })
} const { roles } = await dispatch('getInfo')
}
resetRouter()
export default {
namespaced: true, // generate accessible routes map based on roles
state, const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })
mutations, // dynamically add accessible routes
actions router.addRoutes(accessRoutes)
}
// reset visited views and cached views
dispatch('tagsView/delAllViews', null, { root: true })
}
}
export default {
namespaced: true,
state,
mutations,
actions
}

View File

@ -1,22 +1,58 @@
<template> <template>
<div class="social-signup-container"> <div class="social-signup-container">
<div class="sign-btn" @click="wechatHandleClick('wechat')"> <!-- <div class="sign-btn" @click="wechatHandleClick('wechat')">-->
<span class="wx-svg-container"><svg-icon icon-class="wechat" class="icon" /></span> <!-- <span class="wx-svg-container"><svg-icon icon-class="wechat" class="icon" /></span>-->
WeChat <!-- WeChat-->
</div> <!-- </div>-->
<div class="sign-btn" @click="tencentHandleClick('tencent')"> <!-- <div class="sign-btn" @click="tencentHandleClick('tencent')">-->
<span class="qq-svg-container"><svg-icon icon-class="qq" class="icon" /></span> <!-- <span class="qq-svg-container"><svg-icon icon-class="qq" class="icon" /></span>-->
QQ <!-- QQ-->
<!-- </div>-->
<div class="sign-btn" @click="marketHandleClick('MetaMask')">
<span class="qq-svg-container"><svg-icon icon-class="MetaMask" class="icon" /></span>
Market
</div> </div>
</div> </div>
</template> </template>
<script> <script>
// import openWindow from '@/utils/open-window' // import openWindow from '@/utils/open-window'
import Web3 from 'web3'
import { getNonce, metamaskLogin } from '@/api/user'
export default { export default {
name: 'SocialSignin', name: 'SocialSignin',
data() {
return {
provider: '',
web3: '',
chainId: '',
account: '',
redirect: undefined,
otherQuery: {}
}
},
watch: {
$route: {
handler: function(route) {
const query = route.query
if (query) {
this.redirect = query.redirect
this.otherQuery = this.getOtherQuery(query)
}
},
immediate: true
}
},
methods: { methods: {
getOtherQuery(query) {
return Object.keys(query).reduce((acc, cur) => {
if (cur !== 'redirect') {
acc[cur] = query[cur]
}
return acc
}, {})
},
wechatHandleClick(thirdpart) { wechatHandleClick(thirdpart) {
alert('ok') alert('ok')
// this.$store.commit('SET_AUTH_TYPE', thirdpart) // this.$store.commit('SET_AUTH_TYPE', thirdpart)
@ -32,6 +68,131 @@ export default {
// const redirect_uri = encodeURIComponent('xxx/redirect?redirect=' + window.location.origin + '/auth-redirect') // const redirect_uri = encodeURIComponent('xxx/redirect?redirect=' + window.location.origin + '/auth-redirect')
// const url = 'https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=' + client_id + '&redirect_uri=' + redirect_uri // const url = 'https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=' + client_id + '&redirect_uri=' + redirect_uri
// openWindow(url, thirdpart, 540, 540) // openWindow(url, thirdpart, 540, 540)
},
async marketHandleClick(thirdpart) {
if (!this.hasMetamask()) {
this.$message({
message: '请先安装MetaMask插件',
type: 'error',
duration: 1200
})
return
}
this.provider = await this.connectMetaMask()
if (!this.provider) {
return
}
this.web3 = new Web3(this.provider)
this.chainId = await this.web3.eth.getChainId()
const accounts = await this.web3.eth.getAccounts()
if (accounts && accounts.length > 0) {
this.account = accounts[0]
}
await getNonce(this.account).then(response => {
if (response.code === 0) {
this.$store.dispatch('user/getNonce', response.data)
} else {
this.$message({
message: response.message,
type: 'error',
duration: 1200
})
return
}
}).catch((err) => {
console.log('Api getNonce Error:' + err)
})
await this.metaMaskLogin()
},
hasMetamask() {
if (typeof window.ethereum !== 'undefined') {
return true
} else {
return false
}
},
connectMetaMask() {
var provider = window.ethereum
try {
provider.request({ method: 'eth_requestAccounts' })
} catch (error) {
if (error.code === -32002) {
throw new Error('MeatMask not login, Open MeatMask and login first')
} else {
throw new Error('User Rejected')
}
}
return provider
},
async metaMaskLogin() {
const account = this.account
const net_id = this.chainId
const nonce = this.$store.getters.nonce
const tips = 'This signature is only used for verify your account'
const signMsg = {
tips,
nonce
}
const EIP721_DOMAIN_DATA = [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' }
]
const signObj = {
types: {
EIP712Domain: EIP721_DOMAIN_DATA,
set: [
{ name: 'tips', type: 'string' },
{ name: 'nonce', type: 'string' }
]
},
primaryType: 'set',
domain: {
name: 'Auth',
version: '1'
},
message: signMsg
}
const signature = await this.signData(signObj, account)
const authData = {
account,
nonce,
signature,
tips,
net_id
}
metamaskLogin(authData).then(response => {
this.$router.push({ path: this.redirect || '/', query: this.otherQuery })
console.log(response)
}).catch((err) => {
console.log('Api getNonce Error:' + err)
})
},
async signData(signObj, signer) {
const msgParams = JSON.stringify(signObj)
const from = signer
const params = [from, msgParams]
const result = await this.sendCmd(
'eth_signTypedData_v4',
params,
from
)
return result.result
},
async sendCmd(method, params, from) {
return new Promise((resolve, reject) => {
this.web3.currentProvider.sendAsync({
method,
params,
from
}, async function(err, result) {
if (err) {
reject && reject(err)
return
}
resolve && resolve(result)
})
})
} }
} }
} }

View File

@ -1,281 +1,290 @@
<template> <template>
<div class="login-container"> <div class="login-container">
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" autocomplete="on" label-position="left"> <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" autocomplete="on" label-position="left">
<div class="title-container"> <div class="title-container">
<h3 class="title">Login Form</h3> <h3 class="title">Login Form</h3>
</div> </div>
<el-form-item prop="username"> <el-form-item prop="username">
<span class="svg-container"> <span class="svg-container">
<svg-icon icon-class="user" /> <svg-icon icon-class="user" />
</span> </span>
<el-input <el-input
ref="username" ref="username"
v-model="loginForm.username" v-model="loginForm.username"
placeholder="Username" placeholder="Username"
name="username" name="username"
type="text" type="text"
tabindex="1" tabindex="1"
autocomplete="on" autocomplete="on"
/> />
</el-form-item> </el-form-item>
<el-tooltip v-model="capsTooltip" content="Caps lock is On" placement="right" manual> <el-tooltip v-model="capsTooltip" content="Caps lock is On" placement="right" manual>
<el-form-item prop="password"> <el-form-item prop="password">
<span class="svg-container"> <span class="svg-container">
<svg-icon icon-class="password" /> <svg-icon icon-class="password" />
</span> </span>
<el-input <el-input
:key="passwordType" :key="passwordType"
ref="password" ref="password"
v-model="loginForm.password" v-model="loginForm.password"
:type="passwordType" :type="passwordType"
placeholder="Password" placeholder="Password"
name="password" name="password"
tabindex="2" tabindex="2"
autocomplete="on" autocomplete="on"
@keyup.native="checkCapslock" @keyup.native="checkCapslock"
@blur="capsTooltip = false" @blur="capsTooltip = false"
@keyup.enter.native="handleLogin" @keyup.enter.native="handleLogin"
/> />
<span class="show-pwd" @click="showPwd"> <span class="show-pwd" @click="showPwd">
<svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" /> <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
</span> </span>
</el-form-item> </el-form-item>
</el-tooltip> </el-tooltip>
<el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">Login</el-button> <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">Login</el-button>
</el-form> <div style="position:relative;margin-top: 30px">
<el-button class="thirdparty-button" type="primary" @click="showDialog=true">
</div> Or connect with
</template> </el-button>
</div>
<script> </el-form>
import { validUsername } from '@/utils/validate' <el-dialog title="Or connect with" :visible.sync="showDialog">
export default { <social-sign />
name: 'Login', </el-dialog>
components: {}, </div>
data() { </template>
const validateUsername = (rule, value, callback) => {
if (!validUsername(value)) { <script>
callback(new Error('Please enter the correct user name')) import { validUsername } from '@/utils/validate'
} else { import SocialSign from './components/SocialSignin'
callback()
} export default {
} name: 'Login',
const validatePassword = (rule, value, callback) => { components: { SocialSign },
if (value.length < 5) { data() {
callback(new Error('The password can not be less than 6 digits')) const validateUsername = (rule, value, callback) => {
} else { if (!validUsername(value)) {
callback() callback(new Error('Please enter the correct user name'))
} } else {
} callback()
return { }
loginForm: { }
username: 'admin', const validatePassword = (rule, value, callback) => {
password: 'admin' if (value.length < 5) {
}, callback(new Error('The password can not be less than 6 digits'))
loginRules: { } else {
username: [{ required: true, trigger: 'blur', validator: validateUsername }], callback()
password: [{ required: true, trigger: 'blur', validator: validatePassword }] }
}, }
passwordType: 'password', return {
capsTooltip: false, loginForm: {
loading: false, username: 'admin',
showDialog: false, password: 'admin'
redirect: undefined, },
otherQuery: {} loginRules: {
} username: [{ required: true, trigger: 'blur', validator: validateUsername }],
}, password: [{ required: true, trigger: 'blur', validator: validatePassword }]
watch: { },
$route: { passwordType: 'password',
handler: function(route) { capsTooltip: false,
const query = route.query loading: false,
if (query) { showDialog: false,
this.redirect = query.redirect redirect: undefined,
this.otherQuery = this.getOtherQuery(query) otherQuery: {}
} }
}, },
immediate: true watch: {
} $route: {
}, handler: function(route) {
created() { const query = route.query
if (query) {
}, this.redirect = query.redirect
mounted() { this.otherQuery = this.getOtherQuery(query)
if (this.loginForm.username === '') { }
this.$refs.username.focus() },
} else if (this.loginForm.password === '') { immediate: true
this.$refs.password.focus() }
} },
}, created() {
destroyed() {
},
}, mounted() {
methods: { if (this.loginForm.username === '') {
checkCapslock(e) { this.$refs.username.focus()
const { key } = e } else if (this.loginForm.password === '') {
this.capsTooltip = key && key.length === 1 && (key >= 'A' && key <= 'Z') this.$refs.password.focus()
}, }
showPwd() { },
if (this.passwordType === 'password') { destroyed() {
this.passwordType = ''
} else { },
this.passwordType = 'password' methods: {
} checkCapslock(e) {
this.$nextTick(() => { const { key } = e
this.$refs.password.focus() this.capsTooltip = key && key.length === 1 && (key >= 'A' && key <= 'Z')
}) },
}, showPwd() {
handleLogin() { if (this.passwordType === 'password') {
this.$refs.loginForm.validate(valid => { this.passwordType = ''
if (valid) { } else {
this.loading = true this.passwordType = 'password'
this.$store.dispatch('user/login', this.loginForm) }
.then(() => { this.$nextTick(() => {
this.$router.push({ path: this.redirect || '/', query: this.otherQuery }) this.$refs.password.focus()
this.loading = false })
}) },
.catch(() => { handleLogin() {
this.loading = false this.$refs.loginForm.validate(valid => {
}) if (valid) {
} else { this.loading = true
console.log('error submit!!') this.$store.dispatch('user/login', this.loginForm)
return false .then(() => {
} this.$router.push({ path: this.redirect || '/', query: this.otherQuery })
}) this.loading = false
}, })
getOtherQuery(query) { .catch(() => {
return Object.keys(query).reduce((acc, cur) => { this.loading = false
if (cur !== 'redirect') { })
acc[cur] = query[cur] } else {
} console.log('error submit!!')
return acc return false
}, {}) }
} })
} },
} getOtherQuery(query) {
</script> return Object.keys(query).reduce((acc, cur) => {
if (cur !== 'redirect') {
<style lang="scss"> acc[cur] = query[cur]
/* 修复input 背景不协调 和光标变色 */ }
/* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */ return acc
}, {})
$bg:#283443; }
$light_gray:#fff; }
$cursor: #fff; }
</script>
@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
.login-container .el-input input { <style lang="scss">
color: $cursor; /* 修复input 背景不协调 和光标变色 */
} /* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */
}
$bg:#283443;
/* reset element-ui css */ $light_gray:#fff;
.login-container { $cursor: #fff;
.el-input {
display: inline-block; @supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
height: 47px; .login-container .el-input input {
width: 85%; color: $cursor;
}
input { }
background: transparent;
border: 0px; /* reset element-ui css */
-webkit-appearance: none; .login-container {
border-radius: 0px; .el-input {
padding: 12px 5px 12px 15px; display: inline-block;
color: $light_gray; height: 47px;
height: 47px; width: 85%;
caret-color: $cursor;
input {
&:-webkit-autofill { background: transparent;
box-shadow: 0 0 0px 1000px $bg inset !important; border: 0px;
-webkit-text-fill-color: $cursor !important; -webkit-appearance: none;
} border-radius: 0px;
} padding: 12px 5px 12px 15px;
} color: $light_gray;
height: 47px;
.el-form-item { caret-color: $cursor;
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.1); &:-webkit-autofill {
border-radius: 5px; box-shadow: 0 0 0px 1000px $bg inset !important;
color: #454545; -webkit-text-fill-color: $cursor !important;
} }
} }
</style> }
<style lang="scss" scoped> .el-form-item {
$bg:#2d3a4b; border: 1px solid rgba(255, 255, 255, 0.1);
$dark_gray:#889aa4; background: rgba(0, 0, 0, 0.1);
$light_gray:#eee; border-radius: 5px;
color: #454545;
.login-container { }
min-height: 100%; }
width: 100%; </style>
background-color: $bg;
overflow: hidden; <style lang="scss" scoped>
$bg:#2d3a4b;
.login-form { $dark_gray:#889aa4;
position: relative; $light_gray:#eee;
width: 520px;
max-width: 100%; .login-container {
padding: 160px 35px 0; min-height: 100%;
margin: 0 auto; width: 100%;
overflow: hidden; background-color: $bg;
} overflow: hidden;
.tips { .login-form {
font-size: 14px; position: relative;
color: #fff; width: 520px;
margin-bottom: 10px; max-width: 100%;
padding: 160px 35px 0;
span { margin: 0 auto;
&:first-of-type { overflow: hidden;
margin-right: 16px; }
}
} .tips {
} font-size: 14px;
color: #fff;
.svg-container { margin-bottom: 10px;
padding: 6px 5px 6px 15px;
color: $dark_gray; span {
vertical-align: middle; &:first-of-type {
width: 30px; margin-right: 16px;
display: inline-block; }
} }
}
.title-container {
position: relative; .svg-container {
padding: 6px 5px 6px 15px;
.title { color: $dark_gray;
font-size: 26px; vertical-align: middle;
color: $light_gray; width: 30px;
margin: 0px auto 40px auto; display: inline-block;
text-align: center; }
font-weight: bold;
} .title-container {
} position: relative;
.show-pwd { .title {
position: absolute; font-size: 26px;
right: 10px; color: $light_gray;
top: 7px; margin: 0px auto 40px auto;
font-size: 16px; text-align: center;
color: $dark_gray; font-weight: bold;
cursor: pointer; }
user-select: none; }
}
.show-pwd {
.thirdparty-button { position: absolute;
position: absolute; right: 10px;
right: 0; top: 7px;
bottom: 6px; font-size: 16px;
} color: $dark_gray;
cursor: pointer;
@media only screen and (max-width: 470px) { user-select: none;
.thirdparty-button { }
display: none;
} .thirdparty-button {
} position: absolute;
} right: 0;
</style> bottom: 6px;
}
@media only screen and (max-width: 470px) {
.thirdparty-button {
display: none;
}
}
}
</style>