初步实现登录功能
This commit is contained in:
BIN
public/img/icons/tuichu.png
Normal file
BIN
public/img/icons/tuichu.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.3 KiB |
12
src/App.vue
12
src/App.vue
@@ -20,7 +20,8 @@
|
||||
<the-macro-prompt />
|
||||
</template>
|
||||
<the-select-printer-dialog v-else-if="instancesDB !== 'moonraker'" />
|
||||
<the-connecting-dialog v-else />
|
||||
<the-connecting-dialog v-else @goLogin="goLogins" />
|
||||
<openLogin ref="loginRef" />
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
@@ -45,6 +46,7 @@ import TheScrewsTiltAdjustDialog from '@/components/dialogs/TheScrewsTiltAdjustD
|
||||
import { setAndLoadLocale } from './plugins/i18n'
|
||||
import TheMacroPrompt from '@/components/dialogs/TheMacroPrompt.vue'
|
||||
import { AppRoute } from '@/routes'
|
||||
import openLogin from './components/login.vue'
|
||||
|
||||
Component.registerHooks(['metaInfo'])
|
||||
|
||||
@@ -63,6 +65,7 @@ Component.registerHooks(['metaInfo'])
|
||||
TheManualProbeDialog,
|
||||
TheBedScrewsDialog,
|
||||
TheScrewsTiltAdjustDialog,
|
||||
openLogin,
|
||||
},
|
||||
})
|
||||
export default class App extends Mixins(BaseMixin, ThemeMixin) {
|
||||
@@ -390,12 +393,19 @@ export default class App extends Mixins(BaseMixin, ThemeMixin) {
|
||||
doc.style.setProperty('--app-height', window.innerHeight + 'px')
|
||||
})
|
||||
}
|
||||
|
||||
goLogins() {
|
||||
this.$refs.loginRef.goLogin()
|
||||
}
|
||||
|
||||
mounted(): void {
|
||||
this.drawFavicon(this.print_percent)
|
||||
this.appHeight()
|
||||
window.addEventListener('resize', this.appHeight)
|
||||
window.addEventListener('orientationchange', this.appHeight)
|
||||
if (!localStorage.getItem('token')) {
|
||||
this.$refs.loginRef.showDialog=true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@@ -104,6 +104,7 @@ export default class TheConnectingDialog extends Mixins(BaseMixin, ThemeMixin) {
|
||||
this.counter++
|
||||
this.$store.dispatch('socket/setData', { connectingFailed: false })
|
||||
this.$socket.connect()
|
||||
this.$emit('goLogin',1)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@@ -56,6 +56,7 @@
|
||||
<the-notification-menu />
|
||||
<the-settings-menu />
|
||||
<the-top-corner-menu />
|
||||
<img class="outImg" @click="dialog = true" src="../../public/img/icons/tuichu.png" />
|
||||
</v-app-bar>
|
||||
<v-snackbar v-model="uploadSnackbar.status" :timeout="-1" fixed right bottom>
|
||||
<strong>{{ $t('App.TopBar.Uploading') }} {{ uploadSnackbar.filename }}</strong>
|
||||
@@ -70,6 +71,25 @@
|
||||
</template>
|
||||
</v-snackbar>
|
||||
<emergency-stop-dialog :show-dialog="showEmergencyStopDialog" @close="showEmergencyStopDialog = false" />
|
||||
<v-dialog v-model="dialog" max-width="290">
|
||||
<v-card color="white" style="color: black;">
|
||||
<v-card-title class="headings">
|
||||
Prompt
|
||||
</v-card-title>
|
||||
<v-card-text style="color: black;">
|
||||
Are you sure you want to log out?
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn text style="color: black;" @click="dialog = false">
|
||||
NO
|
||||
</v-btn>
|
||||
<v-btn text style="color: black;" @click="goLogot">
|
||||
YES
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -229,6 +249,18 @@ export default class TheTopbar extends Mixins(BaseMixin, ThemeMixin) {
|
||||
this.naviDrawer = this.$vuetify.breakpoint.lgAndUp
|
||||
}
|
||||
}
|
||||
|
||||
dialog: boolean = false;
|
||||
goLogot() {
|
||||
this.dialog = false;
|
||||
this.$services.post('/access/logout').then((response) => {
|
||||
localStorage.removeItem('token');
|
||||
this.$toast.success('logout successful', {
|
||||
position: 'top'
|
||||
});
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
btnEmergencyStop() {
|
||||
const confirmOnEmergencyStop = this.$store.state.gui.uiSettings.confirmOnEmergencyStop
|
||||
@@ -348,4 +380,9 @@ export default class TheTopbar extends Mixins(BaseMixin, ThemeMixin) {
|
||||
z-index: 8 !important;
|
||||
}
|
||||
}
|
||||
.outImg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 12px;
|
||||
}
|
||||
</style>
|
||||
|
59
src/components/login.vue
Normal file
59
src/components/login.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<v-dialog ref="openDialog" v-model="showDialog" persistent :width="450">
|
||||
<panel :title="`You need to log in first`" card-class="the-connection-dialog" :margin-bottom="false">
|
||||
<v-card-text class="pt-5">
|
||||
<v-text-field v-model="username" label="account" placeholder="Please enter account name"></v-text-field>
|
||||
<v-text-field v-model="password" label="password" type="password"
|
||||
placeholder="please enter the password"></v-text-field>
|
||||
</v-card-text>
|
||||
<v-card-actions class="justify-center" style="margin-bottom:20px">
|
||||
<v-btn class="black" ton @click="goLogin()" variant="tonal">login</v-btn>
|
||||
</v-card-actions>
|
||||
</panel>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import BaseMixin from '@/components/mixins/base';
|
||||
import ThemeMixin from '@/components/mixins/theme';
|
||||
|
||||
export default {
|
||||
mixins: [BaseMixin, ThemeMixin],
|
||||
data() {
|
||||
return {
|
||||
showDialog: false,
|
||||
counter: 0,
|
||||
username: 'my_user',
|
||||
password: 'my_password',
|
||||
source: 'moonraker'
|
||||
};
|
||||
},
|
||||
created() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
goLogin() {
|
||||
const data = {
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
source: this.source
|
||||
};
|
||||
this.$services.post('/access/login', data).then((res) => {
|
||||
if (res.result.token) {
|
||||
localStorage.setItem('token', res.result.token);
|
||||
this.showDialog = false;
|
||||
window.location.reload()
|
||||
this.$toast.success('login is successful', {
|
||||
position: 'top'
|
||||
});
|
||||
} else {
|
||||
this.$toast.error('login failed', {
|
||||
position: 'top'
|
||||
});
|
||||
this.showDialog = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
@@ -59,13 +59,13 @@ export default class WebrtcGo2rtc extends Mixins(BaseMixin, WebcamMixin) {
|
||||
|
||||
get url() {
|
||||
let urlSearch = ''
|
||||
let url = new URL(location.href)
|
||||
let baseURL = new URL(this.camSettings?.stream_url, this.printerUrl)
|
||||
|
||||
try {
|
||||
urlSearch = new URL(this.camSettings.stream_url).search.toString()
|
||||
url = new URL('api/ws' + urlSearch, this.camSettings.stream_url)
|
||||
urlSearch = new URL(baseURL).search.toString()
|
||||
url = new URL('api/ws' + urlSearch, baseURL)
|
||||
} catch (e) {
|
||||
this.log('invalid url', this.camSettings.stream_url)
|
||||
this.log('invalid url', baseURL)
|
||||
}
|
||||
|
||||
// create media types array
|
||||
|
17
src/main.ts
17
src/main.ts
@@ -37,6 +37,12 @@ import 'vue-resize/dist/vue-resize.css'
|
||||
import VueResize from 'vue-resize'
|
||||
import { defaultMode } from './store/variables'
|
||||
|
||||
|
||||
import axios from 'axios'
|
||||
import services from '@/utils/services'
|
||||
|
||||
Vue.prototype.$services = services
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
Vue.directive('observe-visibility', ObserveVisibility)
|
||||
@@ -86,10 +92,6 @@ const initLoad = async () => {
|
||||
window.console.error('Failed to load config.json')
|
||||
window.console.error(e)
|
||||
}
|
||||
|
||||
const url = store.getters['socket/getWebsocketUrl']
|
||||
Vue.use(WebSocketPlugin, { url, store })
|
||||
if (store?.state?.instancesDB === 'moonraker') Vue.$socket.connect()
|
||||
}
|
||||
|
||||
initLoad().then(() =>
|
||||
@@ -98,6 +100,13 @@ initLoad().then(() =>
|
||||
router,
|
||||
store,
|
||||
i18n,
|
||||
mounted() {
|
||||
const url = this.$store.getters['socket/getWebsocketUrl'];
|
||||
Vue.use(WebSocketPlugin, { url: url, store: this.$store });
|
||||
if (this.$store?.state?.instancesDB === 'moonraker') {
|
||||
Vue.$socket.connect();
|
||||
}
|
||||
},
|
||||
render: (h) => h(App),
|
||||
}).$mount('#app')
|
||||
)
|
||||
|
@@ -21,11 +21,18 @@ export const actions: ActionTree<ServerState, RootState> = {
|
||||
|
||||
// identify client
|
||||
try {
|
||||
let token = null
|
||||
if (localStorage.getItem('token')) {
|
||||
token = localStorage.getItem('token')
|
||||
} else {
|
||||
token = null
|
||||
}
|
||||
const connection = await Vue.$socket.emitAndWait('server.connection.identify', {
|
||||
client_name: 'mainsail',
|
||||
version: rootState.packageVersion,
|
||||
type: 'web',
|
||||
url: 'https://github.com/mainsail-crew/mainsail',
|
||||
access_token: token
|
||||
})
|
||||
commit('setConnectionId', connection.connection_id)
|
||||
} catch (e: any) {
|
||||
|
@@ -23,6 +23,14 @@ export const getters: GetterTree<ServerUpdateManagerState, any> = {
|
||||
})
|
||||
})
|
||||
|
||||
state.zip_repos.forEach((repo) => {
|
||||
output.push({
|
||||
name: repo.name,
|
||||
type: 'zip',
|
||||
data: { ...repo },
|
||||
})
|
||||
})
|
||||
|
||||
return caseInsensitiveSort(output, 'name')
|
||||
},
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@ export const getDefaultState = (): ServerUpdateManagerState => {
|
||||
github_limit_reset_time: null,
|
||||
git_repos: [],
|
||||
web_repos: [],
|
||||
zip_repos: [],
|
||||
system: {
|
||||
package_count: 0,
|
||||
package_list: [],
|
||||
|
@@ -5,6 +5,7 @@ export interface ServerUpdateManagerState {
|
||||
github_limit_reset_time: number | null
|
||||
git_repos: ServerUpdateManagerStateGitRepo[]
|
||||
web_repos: ServerUpdateManagerStateGitRepo[]
|
||||
zip_repos: ServerUpdateManagerStateGitRepo[]
|
||||
system: {
|
||||
package_count: number
|
||||
package_list: string[]
|
||||
@@ -67,6 +68,6 @@ export interface ServerUpdateManagerStateGitRepoGroupedCommits {
|
||||
|
||||
export interface ServerUpdateManagerStateGuiList {
|
||||
name: string
|
||||
type: 'git' | 'web'
|
||||
type: 'git' | 'web' | 'zip'
|
||||
data: ServerUpdateManagerStateGitRepo
|
||||
}
|
||||
|
52
src/utils/services.ts
Normal file
52
src/utils/services.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import axios from 'axios'
|
||||
import Vue from 'vue'
|
||||
import { VSnackbar } from 'vuetify/lib'
|
||||
|
||||
// 创建 axios 实例
|
||||
const service = axios.create({
|
||||
baseURL: 'http://d600pro2hs-d7e7.lan:7125',
|
||||
// baseURL: process.env.VUE_APP_BASE_API, // 从环境变量获取基础URL
|
||||
timeout: 10000 // 请求超时时间
|
||||
})
|
||||
|
||||
// 请求拦截器
|
||||
service.interceptors.request.use(
|
||||
config => {
|
||||
const token = localStorage.getItem('token')
|
||||
if (token) {
|
||||
config.headers['Authorization'] = `Bearer ${token}`,
|
||||
config.headers['Content-Type'] = 'application/json'
|
||||
}
|
||||
return config
|
||||
},
|
||||
error => {
|
||||
Vue.prototype.$loading.hide()
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
service.interceptors.response.use(
|
||||
response => {
|
||||
const res = response.data
|
||||
return res
|
||||
},
|
||||
error => {
|
||||
let message = ''
|
||||
if (error.response) {
|
||||
message = error.response.statusText
|
||||
Vue.prototype.$toast.error(message, {
|
||||
position: 'top'
|
||||
});
|
||||
return Promise.reject(error.response.data)
|
||||
} else {
|
||||
message = error.message
|
||||
}
|
||||
if (error.code === 'ECONNABORTED' && error.message.includes('timeout')) {
|
||||
Vue.prototype.$toast.error('Request timed out. Please refresh and try again', {
|
||||
position: 'top'
|
||||
});
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
)
|
||||
|
||||
export default service
|
Reference in New Issue
Block a user