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.
77 lines
1.8 KiB
TypeScript
77 lines
1.8 KiB
TypeScript
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,
|
||
};
|
||
};
|