初步实现登录功能

This commit is contained in:
593415420
2025-06-11 16:33:48 +08:00
committed by Lyn Lee
parent dd479b7fdc
commit 97cb80e556
12 changed files with 195 additions and 10 deletions

BIN
public/img/icons/tuichu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -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>

View File

@@ -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>

View File

@@ -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
View 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>

View File

@@ -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

View File

@@ -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')
)

View File

@@ -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) {

View File

@@ -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')
},
}

View File

@@ -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: [],

View File

@@ -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
View 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