/* 应用外壳:左侧菜单 + 顶栏 + 内容区,最后加载并挂载
(ref / computed / createApp 等已在 util.js 里解构到全局) */
const App = {
template: `
{{ current.title }}
{{ current.sub }}
↻刷新本页
`,
setup() {
const pages = window.PAGES;
// 分组:按注册顺序把页面归到各自的分组里(总览 / 数据管理 / 统计分析)
const groups = [];
pages.forEach(p => {
const name = p.group || '其他';
let g = groups.find(x => x.name === name);
if (!g) { g = { name, items: [] }; groups.push(g); }
g.items.push(p);
});
const fallback = (pages[0] && pages[0].key) || '';
const currentKey = ref(location.hash.replace(/^#\/?/, '') || fallback);
// 刷新本页:改 key 让组件重新挂载,页面自己会重新拉数据
const reloadTick = ref(0);
const reload = () => { reloadTick.value++; };
const current = computed(() =>
pages.find(p => p.key === currentKey.value) || pages[0] || { title: '', sub: '', component: null });
const go = (key) => {
if (key === currentKey.value) { reload(); return; }
location.hash = '#/' + key;
};
// 地址栏直接改 hash / 浏览器前进后退也能切页
const onHash = () => {
const k = location.hash.replace(/^#\/?/, '');
if (k && k !== currentKey.value) currentKey.value = k;
};
onMounted(() => window.addEventListener('hashchange', onHash));
onUnmounted(() => window.removeEventListener('hashchange', onHash));
// 把地址栏补齐,刷新页面能停在当前页
if (!location.hash) location.hash = '#/' + currentKey.value;
// 前后端不同源了,/docs 这种站内相对路径会指到前端服务器上,必须用后端地址
const docsUrl = API_BASE + '/docs';
return { groups, currentKey, current, go, reload, reloadTick, icon, docsUrl };
},
};
const app = createApp(App);
// 模板里写错组件名之类的错误,直接在界面上弹出来,比只看控制台快
app.config.errorHandler = (err) => {
console.error(err);
toastErr(err && err.message ? err.message : '页面出错了,请按 F12 看控制台');
};
app.use(ElementPlus, { locale: ElementPlusLocaleZhCn });
app.mount('#app');