This commit is contained in:
2025-03-10 10:50:06 +08:00
commit 81c79275aa
65 changed files with 3648 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
export const config = {
appListUrl: 'http://localhost:4005/api/router?path=app&key=public-list',
};

View File

@@ -0,0 +1,45 @@
export const checkIsElectron = () => {
return typeof window !== 'undefined' && typeof window.electron === 'object';
};
export const getElectron = () => {
return window.electron;
};
export const getAppList = async () => {
const check = checkIsElectron();
if (!check) {
console.log('not electron');
return [];
}
const electron = getElectron();
console.log('electron', electron);
const appList = await electron.ipcRenderer.invoke('get-app-list');
console.log('appList', appList);
return appList;
};
export const installApp = async (app) => {
const check = checkIsElectron();
if (!check) {
console.log('not electron');
return [];
}
const electron = getElectron();
console.log('installApp', app);
const result = await electron.ipcRenderer.invoke('install-app', app);
console.log('installApp result', result);
return result;
};
export const uninstallApp = async (app) => {
const check = checkIsElectron();
if (!check) {
console.log('not electron');
return [];
}
const electron = getElectron();
console.log('uninstallApp', app);
const result = await electron.ipcRenderer.invoke('uninstall-app', app);
console.log('uninstallApp result', result);
return result;
};

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Package Manager</title>
<link rel="stylesheet" href="./style.css">
<script type="module" src="./electron.js"></script>
</head>
<body>
<div id="app">
<h1>Package Manager</h1>
<div id="package-list" class="package-list"></div>
</div>
<script type="module" src="./main.js"></script>
</body>
</html>

View File

@@ -0,0 +1,158 @@
import { config } from './config.js';
import { getAppList, installApp, uninstallApp } from './electron.js';
let installedPackages = [];
let allPackages = [];
// Store for installed packages
// const installedPackages = [
// { user: 'test', key: 'test-key', version: '1.0.0' },
// { user: 'demo', key: 'demo-package', version: '1.2.0' },
// ];
// Function to fetch packages from API
async function fetchPackages() {
try {
// Currently using mock data
// TODO: Uncomment the following code when API is ready
const response = await fetch(config.appListUrl);
const result = await response.json();
if (result.code === 200) {
return result.data;
}
throw new Error('Failed to fetch packages');
} catch (error) {
console.error('Error fetching packages:', error);
return [];
}
}
// Mock data for testing
const mockPackages = [
{
id: '1',
title: 'Demo Package 1',
description: 'A test package for demonstration',
version: '1.0.0',
user: 'test',
key: 'test-key',
},
{
id: '2',
title: 'Demo Package 2',
description: 'Another test package with updates',
version: '2.0.0',
user: 'demo',
key: 'demo-package',
},
{
id: '3',
title: 'New Package',
description: "A package that hasn't been installed yet",
version: '1.0.0',
user: 'demo',
key: 'new-package',
},
];
// Function to check if a package is installed
async function getPackageStatus(pkg) {
const installed = installedPackages.find((p) => p.user === pkg.user && p.key === pkg.key);
if (!installed) return 'not-installed';
if (installed.version !== pkg.version) return 'update-available';
return 'installed';
}
// Function to create a package card
async function createPackageCard(pkg) {
const status = await getPackageStatus(pkg);
const card = document.createElement('div');
card.className = 'package-card';
card.innerHTML = `
<h2>${pkg.title}</h2>
<p class="description">${pkg.description}</p>
<div class="package-info">
<span>Version: ${pkg.version}</span>
<span>User: ${pkg.user}</span>
</div>
<div class="actions">
${getActionButton(status, pkg)}
${status !== 'not-installed' ? `<button class="button button-uninstall" onclick="handleUninstall('${pkg.id}')">Uninstall</button>` : ''}
</div>
`;
return card;
}
// Function to get the appropriate action button based on status
function getActionButton(status, pkg) {
switch (status) {
case 'not-installed':
return `<button class="button button-install" onclick="handleInstall('${pkg.id}')">Install</button>`;
case 'update-available':
return `<button class="button button-update" onclick="handleUpdate('${pkg.id}')">Update</button>`;
case 'installed':
return `<button class="button button-reinstall" onclick="handleReinstall('${pkg.id}')">Reinstall</button>`;
}
}
// Action handlers
window.handleInstall = async (id) => {
console.log('Installing package:', id);
const pkg = allPackages.find((p) => p.id === id);
if (pkg) {
await installApp(pkg);
renderPackages();
}
};
window.handleUpdate = async (id) => {
console.log('Updating package:', id);
const pkg = allPackages.find((p) => p.id === id);
if (pkg) {
await installApp(pkg);
renderPackages();
}
};
window.handleReinstall = async (id) => {
console.log('Reinstalling package:', id);
const pkg = allPackages.find((p) => p.id === id);
if (pkg) {
await installApp(pkg);
renderPackages();
}
};
window.handleUninstall = async (id) => {
console.log('Uninstalling package:', id);
// const pkg = mockPackages.find((p) => p.id === id);
const pkg = allPackages.find((p) => p.id === id);
if (pkg) {
// TODO: Replace with actual API call
const index = installedPackages.findIndex((p) => p.user === pkg.user && p.key === pkg.key);
await uninstallApp(pkg);
if (index !== -1) {
installedPackages.splice(index, 1);
renderPackages();
}
}
};
// Render packages
async function renderPackages() {
const packageList = document.getElementById('package-list');
packageList.innerHTML = '';
const installed = await getAppList();
installedPackages = installed;
for (const pkg of allPackages) {
packageList.appendChild(await createPackageCard(pkg));
}
}
// Initialize the application
document.addEventListener('DOMContentLoaded', async () => {
const packages = await fetchPackages();
allPackages = packages;
renderPackages();
});

View File

@@ -0,0 +1,115 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
background-color: #fff8e1;
color: #213547;
}
body {
margin: 0;
min-width: 320px;
min-height: 100vh;
}
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
}
h1 {
text-align: center;
color: #ff8f00;
}
.package-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1rem;
padding: 1rem;
}
.package-card {
background: white;
border-radius: 8px;
padding: 1.5rem;
box-shadow: 0 2px 4px rgba(255, 143, 0, 0.1);
border: 1px solid #ffe0b2;
}
.package-card h2 {
margin: 0 0 0.5rem 0;
color: #f57c00;
}
.package-card .description {
color: #666;
margin-bottom: 1rem;
font-size: 0.9rem;
display: -webkit-box;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.5;
max-height: 6em;
}
.package-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
font-size: 0.9rem;
color: #666;
}
.button {
padding: 0.5rem 1rem;
border-radius: 4px;
border: none;
cursor: pointer;
font-weight: 500;
transition: background-color 0.2s;
}
.button-install {
background-color: #ffa000;
color: white;
}
.button-update {
background-color: #ff8f00;
color: white;
}
.button-reinstall {
background-color: #ffb300;
color: white;
}
.button-uninstall {
background-color: #ff6f00;
color: white;
}
.button:hover {
opacity: 0.9;
}
.button:disabled {
background-color: #ffe0b2;
cursor: not-allowed;
}
.error-message {
text-align: center;
color: #ff6f00;
padding: 2rem;
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(255, 143, 0, 0.1);
grid-column: 1 / -1;
}