nuxt/auth

This commit is contained in:
ronger 2022-10-27 23:22:46 +08:00
parent 2b10747e97
commit a2f72f6d0a
46 changed files with 7378 additions and 7600 deletions

View File

@ -48,7 +48,7 @@ export default {
name: "UserList",
computed: {
...mapState({
userInfo: state => state.oauth
userInfo: state => state.auth.user
})
},
props: {

View File

@ -9,7 +9,7 @@
<el-col :xs="0" :sm="12" :md="8" :xl="8" style="text-align: center;">
<el-row type="flex" justify="center">
<el-col>
<el-menu :default-active="getActiveMenu" style="margin-top: -2px;border: 0;" mode="horizontal"
<el-menu :default-active="activeMenu" style="margin-top: -2px;border: 0;" mode="horizontal"
@select="handleSelectMenu">
<el-menu-item index="index">首页</el-menu-item>
<el-menu-item index="topic">专题</el-menu-item>
@ -19,8 +19,9 @@
</el-col>
<el-col :xs="16" :sm="8" :md="6" :xl="3" style="padding-top: 1rem;">
<client-only>
<el-col v-if="user" style="text-align: right;">
<el-link rel="nofollow" :underline="false" style="padding-left: 10px;padding-right: 10px;" href="/article/post">发帖
<el-col v-if="loggedIn" style="text-align: right;">
<el-link rel="nofollow" :underline="false" style="padding-left: 10px;padding-right: 10px;"
href="/article/post">发帖
</el-link>
<el-link rel="nofollow" :underline="false" style="padding-left: 10px;padding-right: 10px;">
<el-dropdown trigger="click" @command="handleCommand">
@ -37,15 +38,17 @@
</el-link>
<el-link rel="nofollow" :underline="false" style="margin-left: 10px;">
<el-dropdown trigger="click" @command="handleCommand">
<el-avatar v-if="avatarURL" size="small" :src="avatarURL"></el-avatar>
<el-avatar v-if="loggedIn" size="small" :src="user.avatarUrl"></el-avatar>
<el-avatar v-else size="small" src="https://static.rymcu.com/article/1578475481946.png"></el-avatar>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="user" style="align-items: center;">
<el-avatar class="mr-3" v-if="avatarURL" size="small" style="margin-top: 1rem;"
:src="avatarURL"></el-avatar>
<el-avatar class="mr-3" v-if="user.avatarUrl" size="small" style="margin-top: 1rem;"
:src="loggedIn"></el-avatar>
<el-avatar class="mr-3" v-else size="small" style="margin-top: 1rem;"
src="https://static.rymcu.com/article/1578475481946.png"></el-avatar>
<el-link rel="nofollow" :underline="false" style="margin-left: 10px;margin-bottom: 1rem;">{{ nickname }}</el-link>
<el-link rel="nofollow" :underline="false" style="margin-left: 10px;margin-bottom: 1rem;">
{{ user.nickname }}
</el-link>
</el-dropdown-item>
<el-dropdown-item v-if="hasPermissions" command="admin-dashboard">系统管理</el-dropdown-item>
<el-dropdown-item command="user-info">资料与账号</el-dropdown-item>
@ -70,203 +73,165 @@
</template>
<script>
import { isBrowser } from '~/environment'
import {mapState} from 'vuex';
const Cookie = process.client ? require('js-cookie') : undefined
export default {
name: "MobileHeader",
computed: {
getActiveMenu() {
return this.$store.state.activeMenu;
},
user() {
return this.$store.state.oauth;
},
avatarURL() {
let _ts = this;
if (isBrowser) {
if (!_ts.$store.state.userInfo) {
let user = localStorage.getItem('user');
if (user) {
_ts.$store.commit('setUser', JSON.parse(user))
}
}
}
return _ts.$store.state.userInfo?.avatarURL;
},
nickname() {
let _ts = this;
if (isBrowser) {
if (!_ts.$store.state.userInfo) {
let user = localStorage.getItem('user');
if (user) {
_ts.$store.commit('setUser', JSON.parse(user))
}
}
}
return _ts.$store.state.userInfo?.nickname;
},
account() {
let _ts = this;
if (isBrowser) {
if (!_ts.$store.state.userInfo) {
let user = localStorage.getItem('user');
if (user) {
_ts.$store.commit('setUser', JSON.parse(user))
}
}
}
return _ts.$store.state.userInfo?.account;
},
hasPermissions() {
return this.$store.getters.hasPermissions('blog_admin');
}
},
data() {
return {
restaurants: [],
state: '',
timeout: null,
show: false,
notifications: [],
notificationNumbers: ""
};
},
watch: {
user: function () {
this.getUnreadNotifications();
}
},
methods: {
handleSelectMenu(item) {
let _ts = this;
let activeMenu = _ts.$store.state.activeMenu;
if (activeMenu !== item) {
switch (item) {
case 'topic':
_ts.$router.push({
path: '/topic/news'
})
break;
case 'github':
window.open("https://github.com/rymcu");
return false;
break;
default:
_ts.$router.push(
{
path: '/'
}
)
}
}
},
handleCommand(item) {
let _ts = this;
export default {
name: "MobileHeader",
computed: {
...mapState({
activeMenu: state => state.activeMenu,
user: state => state.auth.user,
loggedIn: state => state.auth.loggedIn
}),
hasPermissions() {
return this.$auth.hasScope('blog_admin');
}
},
data() {
return {
restaurants: [],
state: '',
timeout: null,
show: false,
notifications: [],
notificationNumbers: ""
};
},
watch: {
user: function () {
this.getUnreadNotifications();
}
},
methods: {
handleSelectMenu(item) {
let _ts = this;
let activeMenu = _ts.$store.state.activeMenu;
if (activeMenu !== item) {
switch (item) {
case 'user':
case 'topic':
_ts.$router.push({
path: '/user/' + _ts.account
path: '/topic/news'
})
break;
case 'user-info':
_ts.$router.push({
path: '/user/settings/account'
})
break;
case 'logout':
Cookie.remove('auth')
_ts.$store.commit('setAuth', null)
item = 'login';
case 'github':
window.open("https://github.com/rymcu");
return false;
break;
default:
_ts.$router.push({
name: item
})
}
},
getUnreadNotifications() {
let _ts = this;
if (_ts.user) {
_ts.$axios.$get('/api/notification/unread').then(function (res) {
if (res) {
_ts.$set(_ts, 'notifications', res.notifications);
_ts.$set(_ts, 'notificationNumbers', res.pagination.total === 0 ? "" : res.pagination.total);
}
})
}
},
browserFingerprint() {
let _ts = this
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
let txt = 'https://rymcu.com/';
ctx.textBaseline = "top";
ctx.font = "14px 'Arial'";
ctx.textBaseline = "rymcu";
ctx.fillStyle = "#f60";
ctx.fillRect(125,1,62,20);
ctx.fillStyle = "#069";
ctx.fillText(txt, 2, 15);
ctx.fillStyle = "rgba(102, 204, 0, 0.7)";
ctx.fillText(txt, 4, 17);
let b64 = canvas.toDataURL().replace("data:image/png;base64,","");
let bin = atob(b64);
let fingerprint = _ts.bin2hex(bin.slice(-16,-12));
_ts.$store.commit('setFingerprint', fingerprint);
},
bin2hex(str) {
let _ts = this
let result = "";
for (let i = 0; i < str.length; i++) {
let c = str.charCodeAt(i);
//
result += _ts.byte2Hex(c >> 8 & 0xff);
//
result += _ts.byte2Hex(c & 0xff);
}
return result;
},
byte2Hex(b) {
if (b < 0x10) {
return "0" + b.toString(16);
} else {
return b.toString(16);
_ts.$router.push(
{
path: '/'
}
)
}
}
},
mounted() {
let _ts = this
let user = _ts.user;
if (user) {
_ts.getUnreadNotifications();
handleCommand(item) {
let _ts = this;
switch (item) {
case 'user':
_ts.$router.push({
path: '/user/' + _ts.user.account
})
break;
case 'user-info':
_ts.$router.push({
path: '/user/settings/account'
})
break;
case 'logout':
_ts.$auth.reset()
item = 'login';
item = 'login';
break;
default:
_ts.$router.push({
name: item
})
}
let fingerprint = _ts.$store.state.fingerprint
if (!fingerprint) {
_ts.browserFingerprint();
},
getUnreadNotifications() {
let _ts = this;
if (_ts.user) {
_ts.$axios.$get('/api/notification/unread').then(function (res) {
if (res) {
_ts.$set(_ts, 'notifications', res.notifications);
_ts.$set(_ts, 'notificationNumbers', res.pagination.total === 0 ? "" : res.pagination.total);
}
})
}
},
browserFingerprint() {
let _ts = this
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
let txt = 'https://rymcu.com/';
ctx.textBaseline = "top";
ctx.font = "14px 'Arial'";
ctx.textBaseline = "rymcu";
ctx.fillStyle = "#f60";
ctx.fillRect(125, 1, 62, 20);
ctx.fillStyle = "#069";
ctx.fillText(txt, 2, 15);
ctx.fillStyle = "rgba(102, 204, 0, 0.7)";
ctx.fillText(txt, 4, 17);
let b64 = canvas.toDataURL().replace("data:image/png;base64,", "");
let bin = atob(b64);
let fingerprint = _ts.bin2hex(bin.slice(-16, -12));
_ts.$store.commit('setFingerprint', fingerprint);
},
bin2hex(str) {
let _ts = this
let result = "";
for (let i = 0; i < str.length; i++) {
let c = str.charCodeAt(i);
//
result += _ts.byte2Hex(c >> 8 & 0xff);
//
result += _ts.byte2Hex(c & 0xff);
}
return result;
},
byte2Hex(b) {
if (b < 0x10) {
return "0" + b.toString(16);
} else {
return b.toString(16);
}
}
},
mounted() {
let _ts = this
let user = _ts.user;
if (user) {
_ts.getUnreadNotifications();
}
let fingerprint = _ts.$store.state.fingerprint
if (!fingerprint) {
_ts.browserFingerprint();
}
}
}
</script>
<style scoped>
.navbar-brand {
color: inherit;
margin-right: 1rem;
font-size: 1.25rem;
white-space: nowrap;
font-weight: 600;
padding: 0;
transition: .3s opacity;
line-height: 2rem;
}
.navbar-brand {
color: inherit;
margin-right: 1rem;
font-size: 1.25rem;
white-space: nowrap;
font-weight: 600;
padding: 0;
transition: .3s opacity;
line-height: 2rem;
}
.navbar-brand-img {
height: 2rem;
line-height: 2rem;
vertical-align: bottom;
margin-right: .5rem;
width: auto;
}
.navbar-brand-img {
height: 2rem;
line-height: 2rem;
vertical-align: bottom;
margin-right: .5rem;
width: auto;
}
</style>

View File

@ -20,7 +20,7 @@
<el-col :md="10" :span="10" :xs="16" style="line-height: 60px">
<client-only>
<el-col style="text-align: right;" v-if="user">
<el-col style="text-align: right;" v-if="loggedIn">
<el-popover
@show="handleShowPopover"
placement="bottom"
@ -76,17 +76,17 @@
</el-link>
<el-link :underline="false" rel="nofollow" style="margin-left: 10px;">
<el-dropdown @command="handleCommand" trigger="click">
<el-avatar :src="avatarURL" size="small" v-if="avatarURL"></el-avatar>
<el-avatar :src="user.avatarUrl" size="small" v-if="user.avatarUrl"></el-avatar>
<el-avatar size="small" src="https://static.rymcu.com/article/1578475481946.png" v-else></el-avatar>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item style="align-items: center;">
<el-avatar :src="avatarURL" class="mr-3" size="small" style="margin-top: 1rem;"
v-if="avatarURL"></el-avatar>
<el-avatar :src="user.avatarUrl" class="mr-3" size="small" style="margin-top: 1rem;"
v-if="user.avatarUrl"></el-avatar>
<el-avatar class="mr-3" size="small" src="https://static.rymcu.com/article/1578475481946.png"
style="margin-top: 1rem;"
v-else></el-avatar>
<el-link :underline="false" rel="nofollow" style="margin-left: 10px;margin-bottom: 1rem;">
{{ nickname }}
{{ user.nickname }}
</el-link>
</el-dropdown-item>
<el-dropdown-item command="user">个人中心</el-dropdown-item>
@ -138,251 +138,213 @@
</template>
<script>
import {mapState} from 'vuex';
import {isBrowser} from '~/environment';
// import sockClient from '~/plugins/sockjs';
import {mapState} from 'vuex';
// import sockClient from '~/plugins/sockjs';
const Cookie = process.client ? require('js-cookie') : undefined
export default {
name: "PcHeader",
computed: {
...mapState({
activeMenu: state => state.activeMenu,
user: state => state.oauth
}),
avatarURL() {
let _ts = this;
if (isBrowser) {
if (!_ts.$store.state.userInfo) {
let user = localStorage.getItem('user');
if (user) {
_ts.$store.commit('setUser', JSON.parse(user))
}
}
}
return _ts.$store.state.userInfo?.avatarURL;
},
nickname() {
let _ts = this;
if (isBrowser) {
if (!_ts.$store.state.userInfo) {
let user = localStorage.getItem('user');
if (user) {
_ts.$store.commit('setUser', JSON.parse(user))
}
}
}
return _ts.$store.state.userInfo?.nickname;
},
account() {
let _ts = this;
if (isBrowser) {
if (!_ts.$store.state.userInfo) {
let user = localStorage.getItem('user');
if (user) {
_ts.$store.commit('setUser', JSON.parse(user))
}
}
}
return _ts.$store.state.userInfo?.account;
},
hasPermissions() {
return this.$store.getters.hasPermissions('blog_admin');
}
export default {
name: "PcHeader",
computed: {
...mapState({
activeMenu: state => state.activeMenu,
user: state => state.auth.user,
loggedIn: state => state.auth.loggedIn
}),
hasPermissions() {
return this.$auth.hasScope('admin') || this.$auth.hasScope('blog_admin');
}
},
data() {
return {
queryString: '',
timeout: null,
show: false,
notifications: [],
notificationNumbers: "",
showPopover: false,
autofocus: false
};
},
watch: {
user: function () {
this.getUnreadNotifications();
}
},
methods: {
querySearchAsync() {
this.$router.push({
path: `/search?q=${this.queryString}`
})
this.$set(this, 'showPopover', false);
this.$set(this, 'queryString', '');
},
data() {
return {
queryString: '',
timeout: null,
show: false,
notifications: [],
notificationNumbers: "",
showPopover: false,
autofocus: false
};
handleShowPopover() {
setTimeout(function () {
document.getElementsByName("searchInput")[0].focus()
}, 500);
},
watch: {
user: function () {
this.getUnreadNotifications();
}
},
methods: {
querySearchAsync() {
this.$router.push({
path: `/search?q=${this.queryString}`
})
this.$set(this, 'showPopover', false);
this.$set(this, 'queryString', '');
},
handleShowPopover() {
setTimeout(function () {
document.getElementsByName("searchInput")[0].focus()
}, 500);
},
handleSelectMenu(item) {
let _ts = this;
let activeMenu = _ts.$store.state.activeMenu;
if (activeMenu !== item) {
switch (item) {
case 'topic':
_ts.$router.push({
path: '/topic/news?page=1'
})
break;
case 'portfolios':
_ts.$router.push({
path: '/portfolios?page=1'
})
break;
case 'products':
_ts.$router.push({
path: '/products?page=1'
})
break;
case 'github':
window.open("https://github.com/rymcu");
break;
case 'taobao':
window.open("https://rymcu.taobao.com?utm_source=rymcu.com");
break;
case 'open-data':
_ts.$router.push({
path: '/open-data'
})
break;
default:
_ts.$router.push(
{
path: '/'
}
)
}
}
},
handleCommand(item) {
let _ts = this;
handleSelectMenu(item) {
let _ts = this;
let activeMenu = _ts.$store.state.activeMenu;
if (activeMenu !== item) {
switch (item) {
case 'user':
case 'topic':
_ts.$router.push({
path: '/user/' + _ts.account
path: '/topic/news?page=1'
})
break;
case 'user-info':
case 'portfolios':
_ts.$router.push({
path: '/user/settings/account'
path: '/portfolios?page=1'
})
break;
case 'logout':
Cookie.remove('auth')
_ts.$store.commit('setAuth', null)
item = 'login';
case 'products':
_ts.$router.push({
path: '/products?page=1'
})
break;
case 'github':
window.open("https://github.com/rymcu");
break;
case 'taobao':
window.open("https://rymcu.taobao.com?utm_source=rymcu.com");
break;
case 'open-data':
_ts.$router.push({
path: '/open-data'
})
break;
default:
_ts.$router.push({
name: item
})
}
},
getUnreadNotifications() {
let _ts = this;
if (_ts.user) {
_ts.$axios.$get('/api/notification/unread').then(function (res) {
if (res) {
_ts.$set(_ts, 'notifications', res.list);
_ts.$set(_ts, 'notificationNumbers', res.total === 0 ? "" : res.total);
}
})
}
},
login() {
this.$router.push({
path: '/login',
query: {
historyUrl: window.location.href
}
})
},
browserFingerprint() {
let _ts = this
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
let txt = 'https://rymcu.com/';
ctx.textBaseline = "top";
ctx.font = "14px 'Arial'";
ctx.textBaseline = "rymcu";
ctx.fillStyle = "#f60";
ctx.fillRect(125, 1, 62, 20);
ctx.fillStyle = "#069";
ctx.fillText(txt, 2, 15);
ctx.fillStyle = "rgba(102, 204, 0, 0.7)";
ctx.fillText(txt, 4, 17);
let b64 = canvas.toDataURL().replace("data:image/png;base64,", "");
let bin = atob(b64);
let fingerprint = _ts.bin2hex(bin.slice(-16, -12));
_ts.$store.commit('setFingerprint', fingerprint);
},
bin2hex(str) {
let _ts = this
let result = "";
for (let i = 0; i < str.length; i++) {
let c = str.charCodeAt(i);
//
result += _ts.byte2Hex(c >> 8 & 0xff);
//
result += _ts.byte2Hex(c & 0xff);
}
return result;
},
byte2Hex(b) {
if (b < 0x10) {
return "0" + b.toString(16);
} else {
return b.toString(16);
_ts.$router.push(
{
path: '/'
}
)
}
}
},
mounted() {
handleCommand(item) {
let _ts = this;
let user = _ts.user;
if (user) {
_ts.getUnreadNotifications();
_ts.$store.dispatch('follow/fetchUserFollowerList');
_ts.$store.dispatch('follow/fetchUserFollowingList');
// sockClient.initSocket(this.$store.state.userInfo);
switch (item) {
case 'user':
_ts.$router.push({
path: '/user/' + _ts.user.account
})
break;
case 'user-info':
_ts.$router.push({
path: '/user/settings/account'
})
break;
case 'logout':
_ts.$auth.logout()
item = 'login';
break;
default:
_ts.$router.push({
name: item
})
}
let fingerprint = _ts.$store.state.fingerprint
if (!fingerprint) {
_ts.browserFingerprint();
},
getUnreadNotifications() {
let _ts = this;
if (_ts.user) {
_ts.$axios.$get('/api/notification/unread').then(function (res) {
if (res) {
_ts.$set(_ts, 'notifications', res.list);
_ts.$set(_ts, 'notificationNumbers', res.total === 0 ? "" : res.total);
}
})
}
},
login() {
this.$router.push({
path: '/login',
query: {
historyUrl: window.location.href
}
})
},
browserFingerprint() {
let _ts = this
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
let txt = 'https://rymcu.com/';
ctx.textBaseline = "top";
ctx.font = "14px 'Arial'";
ctx.textBaseline = "rymcu";
ctx.fillStyle = "#f60";
ctx.fillRect(125, 1, 62, 20);
ctx.fillStyle = "#069";
ctx.fillText(txt, 2, 15);
ctx.fillStyle = "rgba(102, 204, 0, 0.7)";
ctx.fillText(txt, 4, 17);
let b64 = canvas.toDataURL().replace("data:image/png;base64,", "");
let bin = atob(b64);
let fingerprint = _ts.bin2hex(bin.slice(-16, -12));
_ts.$store.commit('setFingerprint', fingerprint);
},
bin2hex(str) {
let _ts = this
let result = "";
for (let i = 0; i < str.length; i++) {
let c = str.charCodeAt(i);
//
result += _ts.byte2Hex(c >> 8 & 0xff);
//
result += _ts.byte2Hex(c & 0xff);
}
return result;
},
byte2Hex(b) {
if (b < 0x10) {
return "0" + b.toString(16);
} else {
return b.toString(16);
}
}
},
mounted() {
let _ts = this;
let user = _ts.user;
if (user) {
_ts.getUnreadNotifications();
_ts.$store.dispatch('follow/fetchUserFollowerList');
_ts.$store.dispatch('follow/fetchUserFollowingList');
// sockClient.initSocket(this.$store.state.auth.user);
}
let fingerprint = _ts.$store.state.fingerprint
if (!fingerprint) {
_ts.browserFingerprint();
}
}
}
</script>
<style scoped>
.navbar-brand {
color: inherit;
margin-right: 1rem;
font-size: 1.25rem;
white-space: nowrap;
font-weight: 600;
padding: 0;
transition: .3s opacity;
line-height: 3rem;
}
.navbar-brand {
color: inherit;
margin-right: 1rem;
font-size: 1.25rem;
white-space: nowrap;
font-weight: 600;
padding: 0;
transition: .3s opacity;
line-height: 3rem;
}
.navbar-brand-img {
height: 3rem;
line-height: 3rem;
vertical-align: top;
width: auto;
}
.navbar-brand-img {
height: 3rem;
line-height: 3rem;
vertical-align: top;
width: auto;
}
.search-result-box {
min-width: 20vw !important;
}
.search-result-box {
min-width: 20vw !important;
}
.search-result-type {
padding-right: 5px;
}
.search-result-type {
padding-right: 5px;
}
</style>

View File

@ -1,6 +0,0 @@
export default function ({ store, redirect }) {
// If the user is not authenticated
if (!store.state.oauth) {
return redirect('/login')
}
}

View File

@ -1,6 +0,0 @@
export default function ({ store, redirect }) {
// If the user is authenticated redirect to home page
if (store.state.oauth) {
return redirect('/')
}
}

View File

@ -70,9 +70,44 @@ export default {
modules: [
'@nuxtjs/axios',
'@nuxtjs/proxy',
'js-cookie',
'cookieparser'
'@nuxtjs/auth-next'
],
auth: {
redirect: {
login: '/login',
logout: false,
home: false
},
strategies: {
local: {
// scope: true,
scheme: 'refresh',
token: {
property: 'token',
global: true,
maxAge: 60 * 15,
// required: true,
type: false
},
refreshToken: {
property: 'refreshToken',
data: 'refreshToken',
maxAge: 60 * 60 * 2
},
user: {
property: 'user',
autoFetch: false
},
endpoints: {
login: { url: '/api/auth/login', method: 'post' },
logout: { url: '/api/auth/logout', method: 'post' },
refresh: { url: '/api/auth/refresh-token', method: 'post' },
user: { url: '/api/auth/user', method: 'get' }
},
autoLogout: false
}
}
},
axios: {
proxy: true // 开启proxy
},

View File

@ -11,13 +11,12 @@
},
"dependencies": {
"@chenfengyuan/vue-qrcode": "^1.0.2",
"@nuxtjs/auth-next": "^5.0.0-1648802546.c9880dc",
"@nuxtjs/axios": "^5.13.1",
"babel-plugin-lodash": "^3.3.4",
"cookieparser": "^0.1.0",
"echarts": "^4.9.0",
"element-ui": "^2.15.8",
"express": "^4.18.1",
"js-cookie": "^2.2.1",
"lodash": "^4.17.21",
"net": "^1.0.2",
"nuxt": "^2.15.8",

View File

@ -45,6 +45,7 @@ import _ from 'lodash'
export default {
name: "Admin",
middleware: 'auth',
data() {
return {
menus: [
@ -144,7 +145,7 @@ export default {
return this.$store.state.activeMenu;
},
hasPermissions() {
return this.$store.getters.hasPermissions('blog_admin');
return this.$auth.hasScope('admin') || this.$auth.hasScope('blog_admin');
}
},
methods: {

View File

@ -107,6 +107,7 @@ import EditTags from '~/components/widget/tags';
export default {
name: "articles",
middleware: 'auth',
components: {
EditTags
},

View File

@ -75,6 +75,7 @@ import Records from "../../components/common/bank/account/records";
export default {
name: "bank-accounts",
middleware: 'auth',
components: {Records},
fetch() {
let {store, params, error} = this.$nuxt.context

View File

@ -87,6 +87,7 @@ import {mapState} from 'vuex';
export default {
name: "banks",
middleware: 'auth',
fetch() {
let {store, params, error} = this.$nuxt.context
return Promise.all([

View File

@ -97,6 +97,7 @@ import {mapState} from 'vuex';
export default {
name: "comments",
middleware: 'auth',
fetch() {
let {store, params, error} = this.$nuxt.context
return Promise.all([

View File

@ -81,6 +81,7 @@ import {mapState} from 'vuex';
export default {
name: "currency-rules",
middleware: 'auth',
fetch() {
let {store, params, error} = this.$nuxt.context
return Promise.all([

View File

@ -248,6 +248,7 @@ import EditTags from '~/components/widget/tags';
Vue.prototype.$echarts = echarts;
export default {
name: "Dashboard",
middleware: 'auth',
components: {
EditTags
},

View File

@ -80,6 +80,7 @@ import {mapState} from 'vuex';
export default {
name: "products",
middleware: 'auth',
fetch() {
let {store, params, error} = this.$nuxt.context
return Promise.all([

View File

@ -91,6 +91,7 @@
export default {
name: "roles",
middleware: 'auth',
fetch() {
let {store, params, error} = this.$nuxt.context
return Promise.all([

View File

@ -126,6 +126,7 @@ import apiConfig from '~/config/api.config';
export default {
name: "PostTag",
middleware: 'auth',
components: {
VueCropper
},

View File

@ -48,6 +48,7 @@
export default {
name: "tags",
middleware: 'auth',
fetch() {
let {store, params, error} = this.$nuxt.context
return Promise.all([

View File

@ -71,6 +71,7 @@ import {mapState} from 'vuex';
export default {
name: "adminTopicDetail",
middleware: 'auth',
validate({params, store}) {
if (typeof params.topic_uri === 'undefined') {
return true;
@ -94,7 +95,7 @@ export default {
tags: state => state.topic.tags.data
}),
hasPermissions() {
return this.$store.getters.hasPermissions('topic');
return this.$auth.hasScope('topic');
}
},
methods: {

View File

@ -53,6 +53,7 @@ import {mapState} from 'vuex';
export default {
name: "adminTopicBindTag",
middleware: 'auth',
fetch({store, params, error}) {
let _ts = this;
return Promise.all([

View File

@ -124,6 +124,7 @@ import apiConfig from '~/config/api.config';
export default {
name: "adminTopicPost",
middleware: 'auth',
components: {
VueCropper
},

View File

@ -39,6 +39,7 @@
export default {
name: "topics",
middleware: 'auth',
fetch() {
let {store, params, error} = this.$nuxt.context
return Promise.all([

View File

@ -119,6 +119,7 @@ import {mapState} from 'vuex';
export default {
name: "users",
middleware: 'auth',
fetch() {
let {store, params, error} = this.$nuxt.context
return Promise.all([

View File

@ -62,6 +62,7 @@ import {mapState} from 'vuex';
export default {
name: "answer",
middleware: 'auth',
fetch({store, params, error}) {
return Promise.all([
store

View File

@ -82,7 +82,7 @@
<el-col v-if="article.portfolios && article.portfolios.length > 0">
<portfolios-widget :portfolios="article.portfolios"></portfolios-widget>
</el-col>
<el-col v-if="user">
<el-col v-if="loggedIn">
<el-tooltip class="item" effect="dark" content="酷" placement="top-start">
<el-button type="text" style="font-size: 1.2rem;" @click="thumbsUp">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
@ -192,11 +192,12 @@ export default {
article: state => state.article.detail.data,
isFetching: state => state.article.detail.fetching,
isMobile: state => state.global.isMobile,
user: state => state.oauth,
avatar: state => state.userInfo?.avatarURL
loggedIn: state => state.auth.loggedIn,
user: state => state.auth.user,
avatar: state => state.auth.user.avatarUrl
}),
hasPermissions() {
let account = this.$store.state.userInfo?.nickname;
let account = this.$store.state.auth.user?.nickname;
if (account) {
if (account === this.article.articleAuthor.userNickname) {
return true;
@ -205,7 +206,7 @@ export default {
return false;
},
isAdmin() {
return this.$store.getters.hasPermissions('blog_admin');
return this.$auth.hasScope('admin') || this.$auth.hasScope('blog_admin');
},
routeArticleId() {
return Number(this.$route.params.article_id);

View File

@ -63,6 +63,7 @@
export default {
name: "PostArticle",
middleware: 'auth',
validate({params, store}) {
if (typeof params.article_id === 'undefined') {
return true;
@ -80,7 +81,7 @@
article: state => state.article.detail.data
}),
hasPermissions() {
let account = this.$store.state.userInfo?.nickname;
let account = this.$store.state.auth.user?.nickname;
if (account) {
if (this.$route.params.article_id) {
if (account === this.article.articleAuthor.userNickname) {
@ -90,7 +91,7 @@
return true;
}
}
return this.$store.getters.hasPermissions('blog_admin');
return this.$auth.hasScope('blog_admin');
}
},
data() {

View File

@ -45,7 +45,7 @@ export default {
name: "Chat",
computed: {
...mapState({
user: state => state.userInfo
user: state => state.auth.user
})
},
data() {

View File

@ -71,6 +71,7 @@
export default {
name: "DraftDetail",
middleware: 'auth',
validate({params, store}) {
return params.draft_id && !isNaN(Number(params.draft_id))
},
@ -86,17 +87,17 @@
article: state => state.draft.detail.data,
isFetching: state => state.draft.detail.fetching,
isMobile: state => state.global.isMobile,
user: state => state.oauth,
avatar: state => state.userInfo?.avatarURL
user: state => state.auth.user,
avatar: state => state.auth.user?.avatarURL
}),
hasPermissions() {
let account = this.$store.state.userInfo?.nickname;
let account = this.$store.state.auth.user?.nickname;
if (account) {
if (account === this.article?.articleAuthor?.userNickname) {
return true;
}
}
return this.$store.getters.hasPermissions('blog_admin');
return this.$auth.hasScope('blog_admin');
},
routeArticleId() {
return Number(this.$route.params.draft_id)

View File

@ -13,6 +13,7 @@
export default {
name: "Drafts",
middleware: 'auth',
components: {
DraftList
},
@ -27,7 +28,7 @@
computed: {
...mapState({
articles: state => state.draft.list.data,
user: state => state.oauth
user: state => state.auth.user
})
},
mounted() {

View File

@ -54,10 +54,9 @@
<script>
import {mapState} from 'vuex';
const Cookie = process.client ? require('js-cookie') : undefined
export default {
name: "login",
middleware: 'notAuthenticated',
middleware: 'auth',
data() {
return {
user: {
@ -81,40 +80,25 @@ export default {
methods: {
login() {
let _ts = this;
_ts.$refs.user.validate((valid) => {
_ts.$refs.user.validate(async (valid) => {
if (valid) {
_ts.$set(_ts, 'loginLoading', true);
setTimeout(function () {
_ts.$set(_ts, 'loginLoading', false);
}, 10000);
let fingerprint = _ts.$store.state.fingerprint;
let data = {
account: _ts.user.account,
password: _ts.user.password
password: _ts.user.password,
fingerprint: fingerprint
}
_ts.$axios.$post('/api/console/login', data).then(function (res) {
_ts.$set(_ts, 'loginLoading', false);
if (res) {
if (res.message) {
_ts.$message(res.message);
return false;
}
let auth = {
accessToken: res.token,
idUser: res.idUser,
role: res.weights
}
let user = {
nickname: res.nickname,
avatarURL: res.avatarUrl || 'https://static.rymcu.com/article/1578475481946.png',
account: res.account
}
_ts.$store.commit('setAuth', auth) // mutating to store for client rendering
localStorage.setItem('user', JSON.stringify(user))
_ts.$store.commit('setUser', user) // mutating to store for client rendering
Cookie.set('auth', auth, { expires: 7 })
try {
let response = await _ts.$auth.loginWith('local', {data: data})
if (response.success) {
_ts.$auth.setUserToken(response.data.token, response.data.refreshToken);
if (_ts.historyUrl) {
window.location.href = _ts.historyUrl
} else {
@ -123,7 +107,9 @@ export default {
})
}
}
})
} catch (err) {
console.log(err)
}
} else {
return false;
}

View File

@ -13,7 +13,7 @@
export default {
name: "Notification",
middleware: 'authenticated',
middleware: 'auth',
components: {
NotificationList
},
@ -33,7 +33,7 @@
computed: {
...mapState({
notifications: state => state.notification.list.data,
user: state => state.oauth
user: state => state.auth.user
})
},
methods: {

View File

@ -83,11 +83,11 @@ export default {
articles: state => state.portfolio.articles,
isFetching: state => state.portfolio.detail.fetching,
isMobile: state => state.global.isMobile,
user: state => state.oauth,
avatar: state => state.userInfo?.avatarURL
user: state => state.auth.user,
avatar: state => state.auth.user?.avatarURL
}),
isAuthor() {
let account = this.$store.state.userInfo?.nickname;
let account = this.$store.state.auth.user?.nickname;
if (account) {
if (account === this.portfolio.portfolioAuthor.userNickname) {
return true;

View File

@ -78,7 +78,7 @@
portfolio: state => state.portfolio.detail.data
}),
isAuthor() {
let account = this.$store.state.userInfo?.nickname;
let account = this.$store.state.auth.user?.nickname;
if (account) {
if (account === this.portfolio.portfolioAuthorName) {
return true;

View File

@ -125,7 +125,7 @@ export default {
return {'X-Upload-Token': state.uploadHeaders}
},
isAuthor() {
let account = this.$store.state.userInfo?.nickname;
let account = this.$store.state.auth.user?.nickname;
if (account) {
if (this.$route.params.portfolio_id) {
if (account === this.portfolioDetail.portfolioAuthorName) {
@ -334,7 +334,7 @@ export default {
if (res) {
_ts.$set(_ts, 'notificationFlag', false);
_ts.$router.push({
path: '/user/' + _ts.$store.state.userInfo?.account
path: '/user/' + _ts.$store.state.auth.user?.account
})
}
})

View File

@ -61,8 +61,8 @@
</el-link>
</el-popover>
</div>
<div v-if="oauth">
<div v-if="oauth.idUser !== user.idUser">
<div v-if="auth.user">
<div v-if="auth.user.idUser !== user.idUser">
<el-button type="primary" v-if="isFollow" @click="cancelFollowUser(user.idUser)" plain>取消关注</el-button>
<el-button type="primary" v-else @click="followUser(user.idUser)" plain>关注</el-button>
<el-button v-show="false" @click="gotoChats" plain>聊天</el-button>
@ -176,7 +176,7 @@ export default {
portfolios: state => state.user.portfolios,
followers: state => state.user.followers,
followings: state => state.user.followings,
oauth: state => state.oauth
auth: state => state.auth.user
})
},
data() {
@ -227,7 +227,7 @@ export default {
},
followUser(idUser) {
let _ts = this;
if (_ts.oauth) {
if (_ts.auth) {
_ts.$axios.$post('/api/follow', {
followingId: idUser,
followingType: 0
@ -241,7 +241,7 @@ export default {
},
cancelFollowUser(idUser) {
let _ts = this;
if (_ts.oauth) {
if (_ts.auth) {
_ts.$axios.$post('/api/follow/cancel-follow', {
followingId: idUser,
followingType: 0
@ -265,7 +265,7 @@ export default {
mounted() {
let _ts = this;
this.$store.commit('setActiveMenu', 'user');
if (_ts.oauth) {
if (_ts.auth) {
_ts.$axios.$get('/api/follow/is-follow', {
params: {
followingId: _ts.user.idUser,

View File

@ -47,12 +47,13 @@
<script>
export default {
name: "Settings",
middleware: 'auth',
computed: {
getActiveMenu () {
return this.$store.state.activeMenu;
},
isLogin () {
return this.$store.state.oauth;
return this.$store.state.auth.user;
}
},
data() {

View File

@ -65,9 +65,10 @@
export default {
name: "account",
middleware: 'auth',
computed: {
...mapState({
idUser: state => state.oauth.idUser
idUser: state => state.auth.user.idUser
})
},
data() {

View File

@ -81,6 +81,7 @@ import 'cropperjs/dist/cropper.css';
const {generateRandomAvatar} = require('~/plugins/avataaars/generator/generateAvatar');
export default {
name: "avatar",
middleware: 'auth',
components: {
Avataaars,
VueCropper
@ -90,7 +91,7 @@ export default {
uploadHeaders: state => {
return {'X-Upload-Token': state.uploadHeaders}
},
idUser: state => state.oauth.idUser
idUser: state => state.auth.user.idUser
})
},
data() {

View File

@ -12,10 +12,11 @@ import LoginRecords from "@/components/common/user/login-records";
export default {
name: "loginRecord",
middleware: 'auth',
components: {LoginRecords},
computed: {
...mapState({
idUser: state => state.oauth.idUser
idUser: state => state.auth.user.idUser
})
},
data() {

View File

@ -79,7 +79,7 @@ export default {
name: "security",
computed: {
...mapState({
idUser: state => state.oauth.idUser
idUser: state => state.auth.user.idUser
})
},
data() {

View File

@ -1,17 +1,22 @@
<template>
<el-row class="wrapper">
<el-col>
<h1>账户信息</h1>
<el-col v-if="bankAccount">
<el-col>
<h1>账户信息</h1>
</el-col>
<el-col class="bank-account-item">
<span style="font-size: 24px;"> 账号:</span> <span style="color: red;">{{ bankAccount.bankAccount }}</span>
</el-col>
<el-col class="bank-account-item">
<span style="font-size: 24px;"> 余额:</span> <span style="color: red;">{{ bankAccount.accountBalance }}</span> <span
style="font-size: 24px;">巴旦木</span>
</el-col>
<el-col>
<records :records="records" :bankAccount="bankAccount.bankAccount" @currentChange="handleCurrentChange" @searchTransactionRecord="searchTransactionRecord"></records>
</el-col>
</el-col>
<el-col class="bank-account-item">
<span style="font-size: 24px;"> 账号:</span> <span style="color: red;">{{ bankAccount.bankAccount }}</span>
</el-col>
<el-col class="bank-account-item">
<span style="font-size: 24px;"> 余额:</span> <span style="color: red;">{{ bankAccount.accountBalance }}</span> <span
style="font-size: 24px;">巴旦木</span>
</el-col>
<el-col>
<records :records="records" :bankAccount="bankAccount.bankAccount" @currentChange="handleCurrentChange" @searchTransactionRecord="searchTransactionRecord"></records>
<el-col v-else style="text-align: center;margin-top: 10vh;">
<el-button type="primary">开通钱包账号</el-button>
</el-col>
</el-row>
</template>
@ -26,10 +31,10 @@ export default {
fetch({store, error}) {
return Promise.all([
store
.dispatch('wallet/fetchDetail', {idUser: store.state.oauth.idUser})
.dispatch('wallet/fetchDetail')
.catch(err => error({statusCode: 404})),
store
.dispatch('wallet/fetchTransactionRecordList', {idUser: store.state.oauth.idUser})
.dispatch('wallet/fetchTransactionRecordList')
.catch(err => error({statusCode: 404}))
])
},
@ -47,7 +52,7 @@ export default {
handleCurrentChange(search) {
let _ts = this;
_ts.$store.dispatch('wallet/fetchTransactionRecordList', {
idUser: _ts.$store.state.oauth.idUser,
idUser: _ts.$store.state.auth.user.idUser,
startDate: search.startDate,
endDate: search.endDate,
page: search.page
@ -58,7 +63,7 @@ export default {
let startDate = dates[0]
let endDate = dates[1]
_ts.$store.dispatch('wallet/fetchTransactionRecordList', {
idUser: _ts.$store.state.oauth.idUser,
idUser: _ts.$store.state.auth.user.idUser,
startDate: startDate,
endDate: endDate
})

View File

@ -1,18 +1,11 @@
import {Message} from 'element-ui'
const Cookie = process.client ? require('js-cookie') : undefined
export default function ({app, $axios, store, redirect}) {
$axios.onRequest(config => {
let fingerprint = store.state.fingerprint;
if (fingerprint) {
config.headers['fingerprint'] = fingerprint
}
let token = store.state.oauth?.accessToken;
if (token) {
// if (!(config.url.indexOf('console') > -1 || config.url.indexOf('comments') > -1)) {
// }
config.headers['Authorization'] = token
}
})
$axios.onResponse(response => {
return new Promise((resolve, reject) => {
@ -29,13 +22,9 @@ export default function ({app, $axios, store, redirect}) {
if (response.data.code === 0) {
Message.error(message ? message : '服务异常')
} else if (response.data.code === 401) {
Cookie.remove('auth');
store.commit('setAuth', null);
window.location.reload()
app.$auth.logout()
} else if (response.data.code === 402) {
Cookie.remove('auth');
store.commit('setAuth', null);
window.location.reload()
app.$auth.strategy.token.reset()
} else if (response.data.code === 404) {
Message.error('操作失败,请稍后再试......')
} else if (response.data.code === 500) {
@ -47,6 +36,7 @@ export default function ({app, $axios, store, redirect}) {
reject(response);
})
});
$axios.onError(error => {
const code = parseInt(error.response && error.response.status)
if (code === 400) {

View File

@ -26,7 +26,6 @@ export const mutations = {
state.list.fetching = action
},
updateListData(state, action) {
console.log(action)
state.list.data = action
},
// 文章详情

View File

@ -1,7 +1,5 @@
import { isServer } from '~/environment'
const cookieParser = isServer ? require('cookieparser') : undefined
export const state = () => {
return {
activeMenu: 'index',
@ -14,28 +12,12 @@ export const state = () => {
}
export const mutations = {
setAuth (state, auth) {
state.oauth = auth
},
setUser (state, data) {
state.userInfo = data
},
setActiveMenu (state, activeMenu) {
state.activeMenu = activeMenu
},
setUploadHeaders(state, data){
state.uploadHeaders = data
},
setUserInfo(state, data) {
state.userInfo.avatarURL = data.avatarUrl;
state.userInfo.nickname = data.nickname;
let user = {
nickname: data.nickname,
avatarURL: data.avatarUrl,
account: this.state.userInfo.account
}
localStorage.setItem('user', JSON.stringify(user))
},
setFingerprint (state, fingerprint) {
state.fingerprint = fingerprint
}
@ -65,17 +47,6 @@ export const actions = {
// 移动端
store.commit('global/updateMobileState', true)
}
let auth = null
if (req.headers.cookie) {
const parsed = cookieParser.parse(req.headers.cookie)
try {
auth = JSON.parse(parsed.auth)
} catch (err) {
// No valid cookie found
console.log(err);
}
store.commit('setAuth', auth)
}
const initFetchAppData = [
// 内容数据
@ -86,36 +57,3 @@ export const actions = {
return Promise.all(initFetchAppData)
}
}
export const getters = {
hasPermissions: (state) => (scenes) => {
let hasPermissions = false;
const role = state.oauth?.role
if (role) {
switch (scenes) {
case 'user':
hasPermissions = role < 5;
break;
case 'role':
hasPermissions = role < 2;
break;
case 'topic':
hasPermissions = role < 3;
break;
case 'tag':
hasPermissions = role < 3;
break;
case 'admin':
hasPermissions = role < 2;
break;
case 'blog_admin':
hasPermissions = role < 3;
break;
default:
hasPermissions = false;
this.commit('logout');
}
}
return hasPermissions;
}
}

View File

@ -39,10 +39,10 @@ export const mutations = {
export const actions = {
// 获取账户详情
fetchDetail({commit}, params = {}) {
fetchDetail({commit}) {
commit('updateDetailFetching', true)
return this.$axios
.$get(`${WALLET_API_PATH}/${params.idUser}`)
.$get(`${WALLET_API_PATH}/detail`)
.then(response => {
return new Promise(resolve => {
commit('updateDetailData', response)
@ -64,7 +64,6 @@ export const actions = {
return this.$axios
.$get(`${WALLET_API_PATH}/transaction-records`, {
params: {
idUser: params.idUser,
startDate: params.startDate,
endDate: params.endDate,
page: params.page || 1

13913
yarn.lock

File diff suppressed because it is too large Load Diff