diff --git a/admin-frontend-vue/.gitignore b/admin-frontend-vue/.gitignore new file mode 100644 index 00000000..b673f63c --- /dev/null +++ b/admin-frontend-vue/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +dist/ +.env* +!.env.example +test-results/ +vite-dev.log diff --git a/admin-frontend-vue/README.md b/admin-frontend-vue/README.md new file mode 100644 index 00000000..68526fdb --- /dev/null +++ b/admin-frontend-vue/README.md @@ -0,0 +1,39 @@ +# Admin Frontend Vue + +独立的后台管理前端工程,与客户端 `frontend-vue` 完全分离。 + +## 本地开发 + +```powershell +npm install +$env:VITE_API_TARGET = "http://127.0.0.1:18080" +npm run dev +``` + +开发入口:`http://localhost:5174/admin-vue/` + +## 构建 + +```powershell +npm run build +npm test +``` + +构建产物在 `dist/`,部署到 Nginx 的 `/admin-vue/` 根目录。该工程不写入客户端的 `new_web_source`,也不打进 Java JAR。 + +## 运行时契约 + +- 登录态:复用同源 Cookie/Session。 +- 当前用户:`GET /api/admin/current-user`。 +- 菜单树:`GET /api/admin/current-user/menus`,Java 返回树形菜单。 +- 退出登录:`POST /api/admin/logout`。 +- 前端路由:History 模式,基准路径 `/admin-vue/`。 +- 权限粒度:菜单/页面级,不定义按钮级权限。 + +## 首批页面 + +- `/admin-vue/account/users` +- `/admin-vue/account/menus` +- `/admin-vue/account/groups` + +当前首批页面已接入 Java 现有用户、菜单和数据权限分组 API;Java 菜单接口完成树形响应和 Flyway 菜单迁移后,路由权限守卫即可按最终菜单树工作。 diff --git a/admin-frontend-vue/index.html b/admin-frontend-vue/index.html new file mode 100644 index 00000000..9f5de7fe --- /dev/null +++ b/admin-frontend-vue/index.html @@ -0,0 +1,13 @@ + + + + + + + 数富AI - 后台管理 + + +
+ + + diff --git a/backend-java/src/main/resources/static/static/logo.jpg b/admin-frontend-vue/public/assets/logo.jpg similarity index 100% rename from backend-java/src/main/resources/static/static/logo.jpg rename to admin-frontend-vue/public/assets/logo.jpg diff --git a/admin-frontend-vue/src/App.vue b/admin-frontend-vue/src/App.vue new file mode 100644 index 00000000..a78fb6fd --- /dev/null +++ b/admin-frontend-vue/src/App.vue @@ -0,0 +1,7 @@ + + + diff --git a/admin-frontend-vue/src/api/http.ts b/admin-frontend-vue/src/api/http.ts index 92f21795..b2b836a4 100644 --- a/admin-frontend-vue/src/api/http.ts +++ b/admin-frontend-vue/src/api/http.ts @@ -14,7 +14,7 @@ function redirectToLogin(requestUrl?: string): void { // 已在登录页或失败请求即登录端点时不再跳转,避免登录页循环与吞掉登录失败反馈。 if (!shouldRedirectUnauthorized(window.location.pathname, requestUrl)) return const target = loginRedirectTarget(window.location) - window.location.assign(`/login?redirect=${target}`) + window.location.assign('/admin-vue/login?redirect=' + target) } http.interceptors.response.use( diff --git a/admin-frontend-vue/src/api/users-dto.ts b/admin-frontend-vue/src/api/users-dto.ts index ed572237..768cd1c1 100644 --- a/admin-frontend-vue/src/api/users-dto.ts +++ b/admin-frontend-vue/src/api/users-dto.ts @@ -1,7 +1,7 @@ /** 用户管理列表/分页 DTO(任务 41):纯逻辑,无框架依赖,与 Java AdminUserController 对齐。 */ import type { AdminUser } from '../types/admin' -export const USER_PAGE_DEFAULT_SIZE = 15 +export const USER_PAGE_DEFAULT_SIZE = 10 export const USER_PAGE_MIN_PAGE = 1 export const USER_PAGE_MAX_SIZE = 200 diff --git a/admin-frontend-vue/src/layout/topbar-model.ts b/admin-frontend-vue/src/layout/topbar-model.ts index 5b2ae3a5..965498cf 100644 --- a/admin-frontend-vue/src/layout/topbar-model.ts +++ b/admin-frontend-vue/src/layout/topbar-model.ts @@ -1,4 +1,5 @@ -import type { AdminUser } from '../types/admin' +import type { AdminUser } from '../types/admin.ts' +import { roleLabel } from '../types/admin.ts' /** 无 meta.title 时的壳层缺省标题。 */ export const ADMIN_DEFAULT_TITLE = '管理后台' @@ -18,5 +19,5 @@ export interface TopbarUserViewModel { /** 顶部栏用户模型:用户信息或未登录空态统一为可渲染视图模型。 */ export function topbarUserOf(user: AdminUser | null | undefined): TopbarUserViewModel { if (!user) return { username: '当前用户', role: '', hasUser: false } - return { username: user.username || '当前用户', role: user.role || '', hasUser: true } + return { username: user.username || '当前用户', role: roleLabel(user.role) || user.role || '', hasUser: true } } diff --git a/admin-frontend-vue/src/pages/account/GroupsPage.vue b/admin-frontend-vue/src/pages/account/GroupsPage.vue index 8f9cc5ad..789032d2 100644 --- a/admin-frontend-vue/src/pages/account/GroupsPage.vue +++ b/admin-frontend-vue/src/pages/account/GroupsPage.vue @@ -18,14 +18,19 @@ const isSuperAdmin = computed(() => session.isSuperAdmin) const currentUserId = computed(() => session.user?.id ?? null) // 客户端分页:全量拉取后按页切片展示。 const page = ref(1) -const pageSize = 10 +const pageSize = ref(10) const total = computed(() => rows.value.length) -const pagedRows = computed(() => rows.value.slice((page.value - 1) * pageSize, page.value * pageSize)) +const pagedRows = computed(() => rows.value.slice((page.value - 1) * pageSize.value, page.value * pageSize.value)) function changePage(p: number) { page.value = p } +function changeSize(size: number) { + pageSize.value = size + page.value = 1 +} + function canEditOf(group: ShopGroupItem): boolean { if (isSuperAdmin.value) return true return currentUserId.value != null && group.leaderUserId != null && currentUserId.value === group.leaderUserId @@ -75,7 +80,6 @@ onMounted(loadGroups)

数据权限分组

管理店铺数据访问分组和组员范围。组长不可更改,组员为普通账号。

- 新建分组 @@ -96,8 +100,10 @@ onMounted(loadGroups) +
+ 新建分组 +
- @@ -123,7 +129,7 @@ onMounted(loadGroups)
([]) // 客户端分页:列表展示统一带分页组件。 const page = ref(1) -const pageSize = 10 +const pageSize = ref(10) const total = computed(() => rows.value.length) -const pagedRows = computed(() => rows.value.slice((page.value - 1) * pageSize, page.value * pageSize)) +const pagedRows = computed(() => rows.value.slice((page.value - 1) * pageSize.value, page.value * pageSize.value)) function changePage(p: number) { page.value = p } + +function changeSize(size: number) { + pageSize.value = size + page.value = 1 +} const createVisible = ref(false) const saving = ref(false) const editingNode = ref(null) @@ -186,10 +191,12 @@ onMounted(loadMenus)

菜单管理

维护后台菜单树和页面路由。权限粒度仅到菜单/页面。

- 新增菜单 +
+ 新增菜单 +
- +
+ 新建用户 +
-