diff --git a/static/vue-app.js b/static/vue-app.js new file mode 100644 index 0000000..904e097 --- /dev/null +++ b/static/vue-app.js @@ -0,0 +1,188 @@ +const { createApp, ref, reactive, computed, onMounted } = Vue; +const { createRouter, createWebHashHistory } = VueRouter; + +function api(method, url, body) { + const opts = { method, headers: { 'Content-Type': 'application/json' } }; + const t = localStorage.getItem('gemold_token'); + if (t) opts.headers.Authorization = 'Bearer ' + t; + if (body) opts.body = JSON.stringify(body); + return fetch(url, opts).then(r => { + if (r.status === 401) { localStorage.removeItem('gemold_token'); localStorage.removeItem('gemold_user'); router.push('/login'); } + if (!r.ok) return r.json().then(e => Promise.reject(e)); + const ct = r.headers.get('content-type') || ''; + return ct.includes('application/json') ? r.json() : r.text(); + }); +} + +function apiForm(url, form) { + const opts = { method: 'POST', body: form }; + const t = localStorage.getItem('gemold_token'); + if (t) opts.headers = { Authorization: 'Bearer ' + t }; + return fetch(url, opts).then(r => { + if (r.status === 401) { localStorage.removeItem('gemold_token'); localStorage.removeItem('gemold_user'); router.push('/login'); } + if (!r.ok) return r.json().then(e => Promise.reject(e)); + return r.json(); + }); +} + +const store = reactive({ + user: JSON.parse(localStorage.getItem('gemold_user') || 'null'), + tasks: [], + uploading: false, + async login(username, password) { + const form = new FormData(); + form.append('username', username); + form.append('password', password); + const r = await apiForm('/api/auth/login', form); + store.user = r.user; + localStorage.setItem('gemold_token', r.access_token); + localStorage.setItem('gemold_user', JSON.stringify(r.user)); + return r; + }, + logout() { + localStorage.removeItem('gemold_token'); + localStorage.removeItem('gemold_user'); + store.user = null; + store.tasks = []; + router.push('/login'); + }, + async fetchTasks() { + const r = await api('GET', '/api/debug/tasks'); + store.tasks = Object.values(r.tasks || {}).sort((a, b) => { + const da = a.created_at || a.upload_time || ''; + const db = b.created_at || b.upload_time || ''; + return db.localeCompare(da); + }); + }, + async uploadFile(file, material) { + store.uploading = true; + try { + const form = new FormData(); + form.append('file', file); + if (material) form.append('material', material); + const r = await apiForm('/api/upload', form); + await store.fetchTasks(); + return r; + } finally { + store.uploading = false; + } + }, +}); + +const LoginPage = { + setup() { + const username = ref(''); + const password = ref(''); + const error = ref(''); + const loading = ref(false); + const doLogin = async () => { + loading.value = true; error.value = ''; + try { await store.login(username.value, password.value); router.push('/dashboard'); } + catch (e) { error.value = e.detail || '登录失败'; } + finally { loading.value = false; } + }; + return { username, password, error, loading, doLogin }; + }, + template: ` +
模具制造管理系统
+