chore: bootstrap storage migrations and frontend
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<RouterView />
|
||||
</template>
|
||||
@@ -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),
|
||||
);
|
||||
@@ -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");
|
||||
@@ -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;
|
||||
@@ -0,0 +1,8 @@
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
export const useAppStore = defineStore("app", {
|
||||
state: () => ({
|
||||
projectName: "AI 文档模板系统",
|
||||
saveStatus: "已保存",
|
||||
}),
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 3、Vite、Ant Design Vue、Router、Pinia、Axios 与全局样式变量已接入。</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>
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
Reference in New Issue
Block a user