chore: bootstrap storage migrations and frontend

This commit is contained in:
zwt13703
2026-07-01 20:25:37 +08:00
parent 393a170d3a
commit beec8cf5bb
25 changed files with 2817 additions and 101 deletions
+3
View File
@@ -0,0 +1,3 @@
<template>
<RouterView />
</template>
+14
View File
@@ -0,0 +1,14 @@
import axios from "axios";
export const apiClient = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8000/api",
timeout: 15000,
headers: {
"Content-Type": "application/json",
},
});
apiClient.interceptors.response.use(
(response) => response,
(error) => Promise.reject(error),
);
+17
View File
@@ -0,0 +1,17 @@
import { createApp } from "vue";
import { createPinia } from "pinia";
import Antd from "ant-design-vue";
import "ant-design-vue/dist/reset.css";
import App from "./App.vue";
import router from "./router";
import "./styles/variables.css";
import "./styles/global.css";
const app = createApp(App);
app.use(createPinia());
app.use(router);
app.use(Antd);
app.mount("#app");
+16
View File
@@ -0,0 +1,16 @@
import { createRouter, createWebHistory } from "vue-router";
import TemplateCenter from "@/views/TemplateCenter.vue";
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: "/",
name: "template-center",
component: TemplateCenter,
},
],
});
export default router;
+8
View File
@@ -0,0 +1,8 @@
import { defineStore } from "pinia";
export const useAppStore = defineStore("app", {
state: () => ({
projectName: "AI 文档模板系统",
saveStatus: "已保存",
}),
});
+23
View File
@@ -0,0 +1,23 @@
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
min-width: 1024px;
min-height: 100vh;
color: var(--c-text);
background: var(--c-bg);
font-family: var(--font-app);
font-size: 13px;
-webkit-font-smoothing: antialiased;
}
button,
input,
textarea,
select {
font: inherit;
}
+19
View File
@@ -0,0 +1,19 @@
:root {
--c-primary: #5b5bd6;
--c-primary-hover: #4a4ac0;
--c-primary-soft: #eeeefb;
--c-bg: #f5f6f8;
--c-surface: #ffffff;
--c-surface-soft: #f0f1f3;
--c-border: #e0e2e6;
--c-border-light: #eaecef;
--c-text: #1a1d24;
--c-text-secondary: #5b626e;
--c-text-muted: #9aa1ad;
--c-success: #1a8c4a;
--c-warn: #d48a00;
--radius-sm: 4px;
--radius-md: 6px;
--shadow-sm: 0 1px 2px rgb(0 0 0 / 6%);
--font-app: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif;
}
+214
View File
@@ -0,0 +1,214 @@
<script setup lang="ts">
import { computed } from "vue";
import { FileTextOutlined, PlusOutlined } from "@ant-design/icons-vue";
import { useAppStore } from "@/stores/appStore";
const appStore = useAppStore();
const statusClass = computed(() => (appStore.saveStatus === "已保存" ? "saved" : "dirty"));
</script>
<template>
<main class="template-center">
<aside class="sidebar">
<div class="brand">
<div class="brand-mark">AI</div>
<span>{{ appStore.projectName }}</span>
</div>
<nav class="nav-list">
<button class="nav-item active" type="button">
<FileTextOutlined />
模板中心
</button>
</nav>
</aside>
<section class="workspace">
<header class="toolbar">
<div>
<div class="breadcrumb">模板管理 / 模板中心</div>
<h1>模板中心</h1>
</div>
<div class="toolbar-actions">
<span class="save-status" :class="statusClass">{{ appStore.saveStatus }}</span>
<a-button type="primary">
<template #icon><PlusOutlined /></template>
上传模板
</a-button>
</div>
</header>
<section class="content">
<div class="empty-panel">
<FileTextOutlined class="empty-icon" />
<h2>前端基础工程已就绪</h2>
<p>Vue 3ViteAnt Design VueRouterPiniaAxios 与全局样式变量已接入</p>
</div>
</section>
</section>
</main>
</template>
<style scoped>
.template-center {
display: flex;
height: 100vh;
overflow: hidden;
}
.sidebar {
width: 220px;
flex: 0 0 220px;
border-right: 1px solid var(--c-border);
background: var(--c-surface);
}
.brand {
display: flex;
align-items: center;
gap: 8px;
height: 52px;
padding: 0 16px;
border-bottom: 1px solid var(--c-border);
font-weight: 600;
}
.brand-mark {
display: grid;
width: 26px;
height: 26px;
place-items: center;
border-radius: 5px;
color: #fff;
background: var(--c-primary);
font-size: 13px;
font-weight: 700;
}
.nav-list {
padding: 8px 0;
}
.nav-item {
position: relative;
display: flex;
align-items: center;
width: 100%;
gap: 8px;
padding: 8px 16px;
border: 0;
color: var(--c-text-secondary);
background: transparent;
cursor: pointer;
text-align: left;
}
.nav-item.active {
color: var(--c-primary);
background: var(--c-primary-soft);
font-weight: 500;
}
.nav-item.active::before {
position: absolute;
top: 4px;
bottom: 4px;
left: 0;
width: 3px;
border-radius: 0 2px 2px 0;
background: var(--c-primary);
content: "";
}
.workspace {
display: flex;
min-width: 0;
flex: 1;
flex-direction: column;
}
.toolbar {
display: flex;
align-items: center;
justify-content: space-between;
height: 64px;
padding: 0 24px;
border-bottom: 1px solid var(--c-border);
background: var(--c-surface);
}
.breadcrumb {
color: var(--c-text-muted);
font-size: 12px;
}
h1 {
margin: 2px 0 0;
font-size: 18px;
line-height: 1.3;
}
.toolbar-actions {
display: flex;
align-items: center;
gap: 12px;
}
.save-status {
display: inline-flex;
align-items: center;
gap: 6px;
color: var(--c-text-secondary);
font-size: 12px;
}
.save-status::before {
width: 7px;
height: 7px;
border-radius: 50%;
content: "";
}
.save-status.saved::before {
background: var(--c-success);
}
.save-status.dirty::before {
background: var(--c-warn);
}
.content {
flex: 1;
padding: 24px;
overflow: auto;
}
.empty-panel {
display: grid;
min-height: 320px;
place-items: center;
align-content: center;
gap: 8px;
border: 1px dashed var(--c-border);
border-radius: var(--radius-md);
background: var(--c-surface);
color: var(--c-text-secondary);
box-shadow: var(--shadow-sm);
}
.empty-icon {
color: var(--c-primary);
font-size: 30px;
}
.empty-panel h2 {
margin: 4px 0 0;
color: var(--c-text);
font-size: 16px;
}
.empty-panel p {
margin: 0;
color: var(--c-text-muted);
}
</style>
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />