🎉 创世提交

This commit is contained in:
柚子
2025-01-22 01:48:07 +08:00
commit 5d3481b9ea
92 changed files with 11705 additions and 0 deletions

60
src/lib/api/app.ts Normal file
View File

@@ -0,0 +1,60 @@
import { AppDetail, AppItem } from "@/types/app";
import { invoke } from "@tauri-apps/api/core";
import { retryOperation } from "../utils";
/**
* 获取应用详细信息
* @param category 应用分类
* @param pkgname 应用包名
* @returns Promise<AppInfoItem>
*/
export async function getAppInfo(category: string, pkgname: string): Promise<AppDetail> {
try {
return await retryOperation(async () => {
const appInfo = await invoke<AppDetail>("get_app_info", {
category,
pkgname,
});
return appInfo;
});
} catch (error) {
throw new Error(`获取应用信息失败: ${error}`);
}
}
/**
* 搜索所有应用
* @param query 搜索关键词
* @returns Promise<AppItem[]>
*/
export async function searchAllApps(query: string): Promise<AppItem[]> {
try {
// 从后端获取数据并转换字段名称
const rawApps = await retryOperation(async () => {
return await invoke<Array<{
More: string,
Name: string,
Pkgname: string,
Tags?: string,
Update: string,
icon?: string,
category?: string
}>>("search_all_apps", { query });
});
// 将后端返回的大写字段名转换为前端使用的小写格式
return rawApps.map(app => ({
more: app.More,
name: app.Name,
pkgname: app.Pkgname,
tags: app.Tags,
update: app.Update,
icon: app.icon,
category: app.category
}));
} catch (error) {
console.error("搜索应用失败:", error);
throw new Error(`搜索应用失败: ${error}`);
}
}

46
src/lib/api/category.ts Normal file
View File

@@ -0,0 +1,46 @@
import { invoke } from "@tauri-apps/api/core";
import { Category } from "@/types/category";
import { AppItem } from "@/types/app";
import { retryOperation } from "../utils";
export const getAllCategories = async (): Promise<Category[]> => {
try {
return await retryOperation(async () => {
return await invoke<Category[]>("get_all_categories");
});
} catch (error) {
console.error("获取分类列表失败:", error);
throw new Error("获取分类列表失败");
}
};
export const getCategoryApps = async (categoryId: string): Promise<AppItem[]> => {
// 从后端获取数据并转换字段名称
try {
const rawApps = await retryOperation(async () => {
return await invoke<Array<{
More: string,
Name: string,
Pkgname: string,
Tags?: string,
Update: string,
icon?: string,
category?: string
}>>("get_category_apps", { categoryId });
});
// 将后端返回的大写字段名转换为前端使用的小写格式
return rawApps.map(app => ({
more: app.More,
name: app.Name,
pkgname: app.Pkgname,
tags: app.Tags,
update: app.Update,
icon: app.icon,
category: app.category
}));
} catch (error) {
console.error("获取分类应用列表失败:", error);
throw new Error("获取分类应用列表失败");
}
};

21
src/lib/api/home.ts Normal file
View File

@@ -0,0 +1,21 @@
import { invoke } from "@tauri-apps/api/core";
import { retryOperation } from "../utils";
import { HomeLink, HomeLinkResponse } from "@/types/home";
export const getHomeLinks = async (): Promise<HomeLink[]> => {
try {
const links = await retryOperation(async () => {
const result = await invoke<HomeLinkResponse[]>("get_home_links");
// 将返回数据中的 url 字段转换为 linkUrl
return result.map(link => ({
...link,
linkUrl: link.url,
url: undefined
}));
});
return links;
} catch (error) {
console.error("获取主页链接列表失败:", error);
throw new Error("获取主页链接列表失败");
}
};

50
src/lib/api/server.ts Normal file
View File

@@ -0,0 +1,50 @@
import { invoke } from "@tauri-apps/api/core";
// 存储服务器相关的全局变量
let targetArchToStore: string;
let jsonServerUrl: string;
let imgServerUrl: string;
// 初始化函数,在应用启动时调用
export async function initServerConfig() {
targetArchToStore = await invoke('get_target_arch_to_store');
jsonServerUrl = await invoke('get_json_server_url');
imgServerUrl = await invoke('get_img_server_url');
}
export function getTargetArchToStore(): string {
if (!targetArchToStore) {
throw new Error('服务器配置未初始化,请先调用 initServerConfig');
}
return targetArchToStore;
}
/**
* 获取JSON服务器URL
* @returns 返回JSON服务器的完整URL
*/
export function getJsonServerUrl(): string {
if (!jsonServerUrl) {
throw new Error('服务器配置未初始化,请先调用 initServerConfig');
}
return jsonServerUrl;
}
/**
* 获取图片服务器URL
* @returns 返回图片服务器的完整URL
*/
export function getImgServerUrl(): string {
if (!imgServerUrl) {
throw new Error('服务器配置未初始化,请先调用 initServerConfig');
}
return imgServerUrl;
}
/**
* 获取 User-Agent
* @returns 返回应用的 User-Agent 字符串
*/
export async function getUserAgent(): Promise<string> {
return await invoke('get_user_agent');
}

18
src/lib/icon.ts Normal file
View File

@@ -0,0 +1,18 @@
import { Package, Code, Globe, Palette, MessageCircle, Gamepad2, Image, Music, FileText, Grid, type Icon } from 'lucide-solid';
const iconMap: Record<string, typeof Icon> = {
'code': Code,
'globe': Globe,
'palette': Palette,
'message-circle': MessageCircle,
'gamepad-2': Gamepad2,
'image': Image,
'music': Music,
'file-text': FileText,
'grid': Grid
};
export const getIconComponent = (iconName?: string): typeof Icon => {
if (!iconName) return Package;
return iconMap[iconName] || Package;
};

24
src/lib/share.ts Normal file
View File

@@ -0,0 +1,24 @@
import { invoke } from "@tauri-apps/api/core";
import { writeText } from '@tauri-apps/plugin-clipboard-manager';
export const getTargetArchToStore = async (): Promise<string> => {
return await invoke<string>("get_target_arch_to_store");
};
export const copy = async (text: string): Promise<void> => {
console.log(text);
await writeText(text);
};
export const generateShareLinks = async (category: string, pkgname: string) => {
const targetArch = await getTargetArchToStore();
const spkLink = `spk://${targetArch}/${category}/${pkgname}`;
const shareLink = `https://spk-resolv.spark-app.store/?spk=spk://${targetArch}/${category}/${pkgname}`;
const shareIframe = `<iframe src="https://spk-resolv.spark-app.store/?spk=${encodeURIComponent(`spk://${targetArch}/${category}/${pkgname}`)}" height="350" width="100%" border="0"></iframe>`;
return {
spkLink,
shareLink,
shareIframe
};
};

26
src/lib/utils.ts Normal file
View File

@@ -0,0 +1,26 @@
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
export const retryOperation = async <T>(
operation: () => Promise<T>,
maxRetries: number = 3
): Promise<T> => {
let lastError: any;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error;
if (attempt === maxRetries) break;
// 等待一小段时间后重试
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
throw lastError;
};