docs: 完善项目说明和协作规则
This commit is contained in:
@@ -1,10 +1,18 @@
|
||||
import { http } from "@/utils/http";
|
||||
|
||||
/**
|
||||
* 门店权限后台的业务接口层。
|
||||
*
|
||||
* 页面只依赖这里导出的类型和方法,避免在视图组件中散落接口路径、
|
||||
* 响应结构和状态枚举。后端统一挂在 `/api` 前缀下,由 Vite 代理转发。
|
||||
*/
|
||||
const API_PREFIX = "/api";
|
||||
|
||||
/** 后端员工与门店共用的启停状态枚举。 */
|
||||
export type EmployeeStatus = "ACTIVE" | "INACTIVE";
|
||||
export type StoreStatus = "ACTIVE" | "INACTIVE";
|
||||
|
||||
/** 员工列表中展示的角色摘要。 */
|
||||
export interface RoleSummary {
|
||||
id: number;
|
||||
code: string;
|
||||
@@ -49,6 +57,7 @@ export interface Role extends RoleOption {
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
/** 员工列表是服务端分页,门店和角色当前由前端做轻量筛选。 */
|
||||
export interface EmployeeListParams {
|
||||
storeId?: number;
|
||||
status?: EmployeeStatus;
|
||||
@@ -83,6 +92,7 @@ export interface EmployeePayload {
|
||||
roleIds: number[];
|
||||
}
|
||||
|
||||
/** access-manage 后端普通响应包裹结构。 */
|
||||
export interface ApiResult<T> {
|
||||
success: boolean;
|
||||
data: T;
|
||||
@@ -100,6 +110,7 @@ export interface PaginatedData<T> {
|
||||
pagination: Pagination;
|
||||
}
|
||||
|
||||
/** 门店接口:管理门店基础资料,并给员工下拉选项提供数据源。 */
|
||||
export const listStores = (params?: StoreListParams) => {
|
||||
return http.request<ApiResult<Store[]>>("get", `${API_PREFIX}/stores`, {
|
||||
params
|
||||
@@ -130,6 +141,7 @@ export const deleteStore = (id: number) => {
|
||||
return http.request<void>("delete", `${API_PREFIX}/stores/${id}`);
|
||||
};
|
||||
|
||||
/** 角色接口:维护员工可绑定的权限角色。 */
|
||||
export const getRole = (id: number) => {
|
||||
return http.request<ApiResult<Role>>("get", `${API_PREFIX}/roles/${id}`);
|
||||
};
|
||||
@@ -150,6 +162,7 @@ export const deleteRole = (id: number) => {
|
||||
return http.request<void>("delete", `${API_PREFIX}/roles/${id}`);
|
||||
};
|
||||
|
||||
/** 员工接口:员工列表由后端分页,编辑时按 id 重新拉取完整角色绑定。 */
|
||||
export const listEmployees = (params: EmployeeListParams) => {
|
||||
return http.request<ApiResult<PaginatedData<Employee>>>(
|
||||
"get",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import type { FunctionalComponent } from "vue";
|
||||
|
||||
/**
|
||||
* 登录后默认打开的标签页。
|
||||
*
|
||||
* 这里要和 `src/router/modules/employees.ts` 的业务页面保持一致,
|
||||
* 否则登出重置标签页或首次进入后台时会出现空标签/错入口。
|
||||
*/
|
||||
export const routerArrays: Array<RouteConfigs> = [
|
||||
{
|
||||
path: "/stores",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
const Layout = () => import("@/layout/index.vue");
|
||||
|
||||
/**
|
||||
* 权限管理业务菜单。
|
||||
*
|
||||
* 三个子页面都是静态路由,菜单展示顺序由这里的 children 决定;
|
||||
* 默认访问该模块时进入员工管理,保证和登录/登出后的主工作流一致。
|
||||
*/
|
||||
export default {
|
||||
path: "/access",
|
||||
name: "AccessManagement",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
const Layout = () => import("@/layout/index.vue");
|
||||
|
||||
/**
|
||||
* pure-admin 模板要求保留 Home 根路由。
|
||||
*
|
||||
* 当前业务首页是员工管理,所以根路径直接重定向到 `/employees`;
|
||||
* `/welcome` 只作为隐藏工作台占位页保留。
|
||||
*/
|
||||
export default {
|
||||
path: "/",
|
||||
name: "Home",
|
||||
|
||||
@@ -34,10 +34,12 @@ defineOptions({
|
||||
name: "EmployeeManagement"
|
||||
});
|
||||
|
||||
/** 表单状态复用后端员工 payload,id 用来切换创建/更新接口。 */
|
||||
type EmployeeFormState = EmployeePayload & {
|
||||
id?: number;
|
||||
};
|
||||
|
||||
/** 员工手机号按中国大陆手机号做前端第一层校验,最终唯一性仍由后端保证。 */
|
||||
const phonePattern = /^1[3-9]\d{9}$/;
|
||||
const tableLoading = ref(false);
|
||||
const catalogLoading = ref(false);
|
||||
@@ -56,6 +58,7 @@ const query = reactive({
|
||||
pageSize: 20
|
||||
});
|
||||
|
||||
/** 员工列表由后端分页,这里只保存当前查询返回的分页摘要。 */
|
||||
const pagination = reactive({
|
||||
total: 0,
|
||||
totalPages: 0
|
||||
@@ -130,6 +133,7 @@ function resetFormState() {
|
||||
formRef.value?.clearValidate();
|
||||
}
|
||||
|
||||
/** 清理提交数据,避免把仅包含空格的备注写入后端。 */
|
||||
function buildPayload(): EmployeePayload {
|
||||
return {
|
||||
storeId: form.storeId,
|
||||
@@ -141,6 +145,7 @@ function buildPayload(): EmployeePayload {
|
||||
};
|
||||
}
|
||||
|
||||
/** 门店和角色是员工表单的基础字典,打开弹窗前必须先加载。 */
|
||||
async function fetchCatalog() {
|
||||
catalogLoading.value = true;
|
||||
try {
|
||||
@@ -157,6 +162,7 @@ async function fetchCatalog() {
|
||||
}
|
||||
}
|
||||
|
||||
/** 员工列表筛选、分页都交给后端,前端只传递当前查询条件。 */
|
||||
async function fetchEmployees() {
|
||||
tableLoading.value = true;
|
||||
try {
|
||||
@@ -207,6 +213,7 @@ function openCreateDialog() {
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
/** 编辑前重新拉详情,确保角色绑定不是来自列表摘要的过期数据。 */
|
||||
async function openEditDialog(row: Employee) {
|
||||
try {
|
||||
const result = await getEmployee(row.id);
|
||||
|
||||
@@ -25,10 +25,12 @@ defineOptions({
|
||||
name: "RoleManagement"
|
||||
});
|
||||
|
||||
/** 表单状态与角色 payload 对齐,id 只在编辑已有角色时存在。 */
|
||||
type RoleFormState = RolePayload & {
|
||||
id?: number;
|
||||
};
|
||||
|
||||
/** 角色编码用于权限判断,限制为稳定的小写标识,避免展示名变更影响授权逻辑。 */
|
||||
const codePattern = /^[a-z][a-z0-9_]*$/;
|
||||
const tableLoading = ref(false);
|
||||
const submitLoading = ref(false);
|
||||
@@ -65,6 +67,7 @@ const rules: FormRules<RoleFormState> = {
|
||||
]
|
||||
};
|
||||
|
||||
/** 角色列表当前不分页,搜索在前端完成,降低简单维护场景的交互成本。 */
|
||||
const filteredRoles = computed(() => {
|
||||
const keyword = query.keyword.trim().toLowerCase();
|
||||
|
||||
@@ -119,6 +122,7 @@ function resetFormState() {
|
||||
formRef.value?.clearValidate();
|
||||
}
|
||||
|
||||
/** 空说明统一转为 null,和后端可选字段语义保持一致。 */
|
||||
function buildPayload(): RolePayload {
|
||||
const description = form.description?.trim();
|
||||
|
||||
@@ -129,6 +133,7 @@ function buildPayload(): RolePayload {
|
||||
};
|
||||
}
|
||||
|
||||
/** 角色是员工绑定的基础字典,页面加载和保存后都需要刷新。 */
|
||||
async function fetchRoles() {
|
||||
tableLoading.value = true;
|
||||
try {
|
||||
|
||||
@@ -28,6 +28,7 @@ defineOptions({
|
||||
name: "StoreManagement"
|
||||
});
|
||||
|
||||
/** 表单状态与后端 payload 基本一致,额外用 id 区分新增和编辑。 */
|
||||
type StoreFormState = StorePayload & {
|
||||
id?: number;
|
||||
};
|
||||
@@ -62,6 +63,7 @@ const rules: FormRules<StoreFormState> = {
|
||||
status: [{ required: true, message: "请选择门店状态", trigger: "change" }]
|
||||
};
|
||||
|
||||
/** 门店数据量预计较小,列表一次性拉取后在前端做状态和关键词筛选。 */
|
||||
const filteredStores = computed(() => {
|
||||
const keyword = query.keyword.trim().toLowerCase();
|
||||
|
||||
@@ -117,6 +119,7 @@ function resetFormState() {
|
||||
formRef.value?.clearValidate();
|
||||
}
|
||||
|
||||
/** 提交前统一清理空字符串,保持后端收到的是 null 而不是空值。 */
|
||||
function buildPayload(): StorePayload {
|
||||
const address = form.address?.trim();
|
||||
const phone = form.phone?.trim();
|
||||
@@ -129,6 +132,7 @@ function buildPayload(): StorePayload {
|
||||
};
|
||||
}
|
||||
|
||||
/** 拉取包含停用门店的完整列表,便于后台直接做启停管理。 */
|
||||
async function fetchStores() {
|
||||
tableLoading.value = true;
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user