feat(admin): implement initial admin dashboard and member management
This commit introduces the foundational structure for the admin dashboard, focusing on the member management feature. Key additions include: - A new page at `/admin/manage/members` to display and manage members. - An `AddModal` component with a comprehensive form for creating new members, featuring validation with Zod and input masking. - A reusable `PhoneInput` component with country code selection, backed by a new `useCountries` composable and a full country dataset. - A custom, user-friendly global error page (`error.vue`) to handle application errors gracefully. - Updated dashboard sidebar navigation to include the new member management section. - Added recommended VS Code extensions and settings to improve developer experience.
This commit is contained in:
76
app/composables/useCountries.ts
Normal file
76
app/composables/useCountries.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { countries, type Country } from "~/data/countries";
|
||||
|
||||
/**
|
||||
* 🌍 useCountries composable
|
||||
* 提供国家相关的搜索、过滤、分组、查找等功能
|
||||
*/
|
||||
export const useCountries = () => {
|
||||
/**
|
||||
* 获取全部国家
|
||||
*/
|
||||
const getAll = (): Country[] => countries;
|
||||
|
||||
/**
|
||||
* 按洲分组
|
||||
*/
|
||||
const groupedByContinent = computed(() => {
|
||||
const groups: Record<string, Country[]> = {};
|
||||
for (const c of countries) {
|
||||
const key = c.continent || "Unknown";
|
||||
if (!groups[key]) groups[key] = [];
|
||||
groups[key].push(c);
|
||||
}
|
||||
return groups;
|
||||
});
|
||||
|
||||
/**
|
||||
* 按名称搜索(支持多语言字段)
|
||||
* @param query 搜索关键字
|
||||
*/
|
||||
const search = (query: string) => {
|
||||
if (!query) return countries;
|
||||
const q = query.toLowerCase();
|
||||
return countries.filter((c) =>
|
||||
Object.values(c.name).some((name) => name.toLowerCase().includes(q))
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据国家代码查找
|
||||
* @param code ISO Alpha-2 或 Alpha-3 代码
|
||||
*/
|
||||
const findByCode = (code: string) => {
|
||||
const upper = code.toUpperCase();
|
||||
return countries.find((c) => c.code === upper || c.iso3 === upper);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取特定语言的显示名称
|
||||
* @param country 国家对象
|
||||
* @param lang 语言代码(默认为 en)
|
||||
*/
|
||||
const getDisplayName = (
|
||||
country: Country,
|
||||
lang: keyof Country["name"] = "en"
|
||||
) => {
|
||||
return country.name?.[lang] || country.name.en;
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据洲名筛选
|
||||
* @param continent 洲名(如 'Asia'、'Europe')
|
||||
*/
|
||||
const filterByContinent = (continent: string) => {
|
||||
const key = continent.trim().toLowerCase();
|
||||
return countries.filter((c) => c.continent.toLowerCase() === key);
|
||||
};
|
||||
|
||||
return {
|
||||
getAll,
|
||||
groupedByContinent,
|
||||
search,
|
||||
findByCode,
|
||||
filterByContinent,
|
||||
getDisplayName,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user