feat(web): I1 shell and I2 customer/project UI

Vue 3 + Element Plus layout with JWT login, RBAC routes, axios 401
handling with token restore, and Customers/Projects views wired to
platform APIs.

Made-with: Cursor
This commit is contained in:
2026-04-06 21:05:02 +08:00
parent 3f577b34d5
commit 65eb983035
18 changed files with 2939 additions and 0 deletions
@@ -0,0 +1,46 @@
<template>
<el-card>
<el-alert title="I1JWT + 布局壳;后续迭代挂载 M1M11" type="info" show-icon :closable="false" />
<p class="meta">用户{{ auth.displayName }}角色{{ auth.roles.join(", ") || "—" }}</p>
<el-button type="primary" :loading="pingLoading" @click="ping">Bearer 调用 /api/v1/ping</el-button>
<pre v-if="pingBody">{{ pingBody }}</pre>
</el-card>
</template>
<script setup>
import { ref, onMounted } from "vue";
import axios from "axios";
import { useAuthStore } from "../stores/auth";
const auth = useAuthStore();
const pingBody = ref("");
const pingLoading = ref(false);
onMounted(() => auth.restoreAxiosAuth());
async function ping() {
pingLoading.value = true;
pingBody.value = "";
try {
const { data } = await axios.get("/api/v1/ping");
pingBody.value = JSON.stringify(data, null, 2);
} catch (e) {
pingBody.value = e.response?.data ? JSON.stringify(e.response.data) : String(e.message);
} finally {
pingLoading.value = false;
}
}
</script>
<style scoped>
.meta {
margin: 12px 0;
}
pre {
margin-top: 12px;
background: #1e1e1e;
color: #d4d4d4;
padding: 12px;
border-radius: 4px;
}
</style>