feat: add tenant and user management with migrations, handlers, and tests

- Implemented tenant and user management features including:
  - Tenant creation and management with associated migrations.
  - User creation and management with associated migrations.
  - Tenant membership management with associated migrations.
  - Platform user roles management with associated migrations.
  - Quota management with associated migrations.
  - Article and template management with associated migrations.
- Added HTTP handlers for templates and workspaces.
- Created tests for protected and public routes.
- Introduced a script to check tenant scope in SQL queries.
- Documented task plan for backend completion and frontend foundation.
This commit is contained in:
2026-04-01 00:58:42 +08:00
commit de30497f59
210 changed files with 23733 additions and 0 deletions
@@ -0,0 +1,81 @@
<script setup lang="ts">
withDefaults(
defineProps<{
eyebrow?: string;
title: string;
description: string;
}>(),
{
eyebrow: "模块页面",
},
);
</script>
<template>
<section class="page-hero">
<div class="page-hero__copy">
<p class="page-hero__eyebrow">{{ eyebrow }}</p>
<h1>{{ title }}</h1>
<p>{{ description }}</p>
</div>
<div class="page-hero__actions">
<slot name="actions" />
</div>
</section>
</template>
<style scoped>
.page-hero {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 24px;
padding: 28px 30px;
background: linear-gradient(135deg, #ffffff 0%, #f7fbff 100%);
border: 1px solid #e6edf5;
border-radius: 24px;
}
.page-hero__copy {
max-width: 760px;
}
.page-hero__eyebrow {
margin: 0 0 10px;
color: #5f6fff;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.page-hero h1 {
margin: 0;
font-size: 30px;
line-height: 1.08;
}
.page-hero p:last-child {
margin: 10px 0 0;
color: var(--muted);
line-height: 1.6;
}
.page-hero__actions {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 12px;
}
@media (max-width: 820px) {
.page-hero {
flex-direction: column;
}
.page-hero__actions {
justify-content: flex-start;
}
}
</style>