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: `

Gemold

模具制造管理系统

{{ error }}
` }; const DashboardPage = { setup() { const material = ref('ABS'); const fileInput = ref(null); const triggerUpload = () => fileInput.value?.click(); const handleFile = async (e) => { const f = e.target.files[0]; if (!f) return; await store.uploadFile(f, material.value); e.target.value = ''; }; onMounted(() => { store.fetchTasks(); const t = setInterval(() => store.fetchTasks(), 10000); onUnmounted(() => clearInterval(t)); }); const { onUnmounted } = Vue; const formatSize = (s) => s ? (s > 1048576 ? (s/1048576).toFixed(1)+' MB' : (s/1024).toFixed(1)+' KB') : '-'; const statusLabel = (s) => ({ processing: '处理中', completed: '已完成', failed: '失败', pending: '等待中' }[s] || (s || '未知')); const statusStyle = (s) => { if (s === 'completed') return { background: '#d4edda', color: '#155724' }; if (s === 'failed') return { background: '#f8d7da', color: '#721c24' }; return { background: '#d1ecf1', color: '#0c5460' }; }; return { store, tasks: computed(() => store.tasks), uploading: computed(() => store.uploading), material, fileInput, triggerUpload, handleFile, formatSize, statusLabel, statusStyle }; }, template: `

模具分析

{{ store.user?.username || '' }}
上传 STP 文件:

处理任务 ({{ tasks.length }})

暂无任务,请上传 STP 文件开始分析
{{ t.filename || 'unknown' }} {{ statusLabel(t.status) }} {{ t.progress }}%
{{ formatSize(t.file_size) }} {{ t.created_at }}
{{ t.current_step }}
{{ t.error }}
查看分析结果
{{ t.llm_report }}
` }; const routes = [ { path: '/login', component: LoginPage }, { path: '/dashboard', component: DashboardPage, meta: { requiresAuth: true } }, { path: '/:pathMatch(.*)*', redirect: '/dashboard' }, ]; const router = createRouter({ history: createWebHashHistory(), routes }); router.beforeEach((to, from, next) => { if (to.meta.requiresAuth && !store.user) return next('/login'); if (to.path === '/login' && store.user) return next('/dashboard'); next(); }); const app = createApp({ template: '' }); app.use(router); app.mount('#app');