From fe9a80405dd1e34dfc4509807f3e0f9a72b07965 Mon Sep 17 00:00:00 2001 From: yanghao05 Date: Tue, 1 Apr 2025 17:51:50 +0800 Subject: [PATCH] feat: add prime vue --- frontend/admin/bun.lock | 22 + frontend/admin/package.json | 4 + frontend/admin/src/App.vue | 79 +-- frontend/admin/src/api/apiClient.js | 45 ++ frontend/admin/src/api/statsApi.js | 132 +++++ frontend/admin/src/components/HelloWorld.vue | 43 -- frontend/admin/src/main.js | 23 +- frontend/admin/src/pages/HomePage.vue | 178 +++---- frontend/admin/src/pages/MediasPage.vue | 481 ------------------- frontend/admin/src/pages/PostsPage.vue | 341 ------------- frontend/admin/src/router.js | 10 - frontend/admin/src/style.css | 4 +- 12 files changed, 339 insertions(+), 1023 deletions(-) create mode 100644 frontend/admin/src/api/apiClient.js create mode 100644 frontend/admin/src/api/statsApi.js delete mode 100644 frontend/admin/src/components/HelloWorld.vue delete mode 100644 frontend/admin/src/pages/MediasPage.vue delete mode 100644 frontend/admin/src/pages/PostsPage.vue diff --git a/frontend/admin/bun.lock b/frontend/admin/bun.lock index 303a575..2b26690 100644 --- a/frontend/admin/bun.lock +++ b/frontend/admin/bun.lock @@ -4,10 +4,14 @@ "": { "name": "admin", "dependencies": { + "@primeuix/themes": "^1.0.0", "@tailwindcss/vite": "^4.0.17", "axios": "^1.8.4", "daisyui": "^5.0.9", + "primeicons": "^7.0.0", + "primevue": "^4.3.3", "tailwindcss": "^4.0.17", + "tailwindcss-primeui": "^0.6.1", "vue": "^3.5.13", "vue-router": "4", }, @@ -78,6 +82,18 @@ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], + "@primeuix/styled": ["@primeuix/styled@0.5.0", "https://registry.npmmirror.com/@primeuix/styled/-/styled-0.5.0.tgz", { "dependencies": { "@primeuix/utils": "^0.5.0" } }, "sha512-k5CTQ+10cXIXxZTep7sktmYe8lJkjmUaFVDAc1OCsWTJR+bhBy/s6zWIatGljVtuf3RmTSxtlrHQeFLjPmdUNQ=="], + + "@primeuix/styles": ["@primeuix/styles@1.0.0", "https://registry.npmmirror.com/@primeuix/styles/-/styles-1.0.0.tgz", { "dependencies": { "@primeuix/styled": "^0.5.0" } }, "sha512-j/TlbqihLNMP37zFNjxac5dTRaQEf5Ldrv0P7NwKigCCc/+MI5j4MddxDw1LnxkGhWCJ1Gjbt9uwyQteWtSv7A=="], + + "@primeuix/themes": ["@primeuix/themes@1.0.0", "https://registry.npmmirror.com/@primeuix/themes/-/themes-1.0.0.tgz", { "dependencies": { "@primeuix/styled": "^0.5.0" } }, "sha512-fxUgcAP9H6FeytbE8c4QvRt8aBnoyZJqvtnnVwHT8PHr1dNSnC1nYKGrXpebcx3SpNy9Hp9oVidGsl6u61+pXQ=="], + + "@primeuix/utils": ["@primeuix/utils@0.5.2", "https://registry.npmmirror.com/@primeuix/utils/-/utils-0.5.2.tgz", {}, "sha512-fHL0DGnyhL/9toBoV0cO6L+Xg/uaxmOHJW4SrDNMq6GQ7cDXR3Y0vDLvX5j/8kIsaC7LB3329sQLNHgtEETnWw=="], + + "@primevue/core": ["@primevue/core@4.3.3", "https://registry.npmmirror.com/@primevue/core/-/core-4.3.3.tgz", { "dependencies": { "@primeuix/styled": "^0.5.0", "@primeuix/utils": "^0.5.1" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-kSkN5oourG7eueoFPIqiNX3oDT/f0I5IRK3uOY/ytz+VzTZp5yuaCN0Nt42ZQpVXjDxMxDvUhIdaXVrjr58NhQ=="], + + "@primevue/icons": ["@primevue/icons@4.3.3", "https://registry.npmmirror.com/@primevue/icons/-/icons-4.3.3.tgz", { "dependencies": { "@primeuix/utils": "^0.5.1", "@primevue/core": "4.3.3" } }, "sha512-ouQaxHyeFB6MSfEGGbjaK5Qv9efS1xZGetZoU5jcPm090MSYLFtroP1CuK3lZZAQals06TZ6T6qcoNukSHpK5w=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.37.0", "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.37.0.tgz", { "os": "android", "cpu": "arm" }, "sha512-l7StVw6WAa8l3vA1ov80jyetOAEo1FtHvZDbzXDO/02Sq/QVvqlHkYoFwDJPIMj0GKiistsBudfx5tGFnwYWDQ=="], "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.37.0", "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.37.0.tgz", { "os": "android", "cpu": "arm64" }, "sha512-6U3SlVyMxezt8Y+/iEBcbp945uZjJwjZimu76xoG7tO1av9VO691z8PkhzQ85ith2I8R2RddEPeSfcbyPfD4hA=="], @@ -264,6 +280,10 @@ "postcss": ["postcss@8.5.3", "https://registry.npmmirror.com/postcss/-/postcss-8.5.3.tgz", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="], + "primeicons": ["primeicons@7.0.0", "https://registry.npmmirror.com/primeicons/-/primeicons-7.0.0.tgz", {}, "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw=="], + + "primevue": ["primevue@4.3.3", "https://registry.npmmirror.com/primevue/-/primevue-4.3.3.tgz", { "dependencies": { "@primeuix/styled": "^0.5.0", "@primeuix/styles": "^1.0.0", "@primeuix/utils": "^0.5.1", "@primevue/core": "4.3.3", "@primevue/icons": "4.3.3" } }, "sha512-nooYVoEz5CdP3EhUkD6c3qTdRmpLHZh75fBynkUkl46K8y5rksHTjdSISiDijwTA5STQIOkyqLb+RM+HQ6nC1Q=="], + "proxy-from-env": ["proxy-from-env@1.1.0", "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], "rollup": ["rollup@4.37.0", "https://registry.npmmirror.com/rollup/-/rollup-4.37.0.tgz", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.37.0", "@rollup/rollup-android-arm64": "4.37.0", "@rollup/rollup-darwin-arm64": "4.37.0", "@rollup/rollup-darwin-x64": "4.37.0", "@rollup/rollup-freebsd-arm64": "4.37.0", "@rollup/rollup-freebsd-x64": "4.37.0", "@rollup/rollup-linux-arm-gnueabihf": "4.37.0", "@rollup/rollup-linux-arm-musleabihf": "4.37.0", "@rollup/rollup-linux-arm64-gnu": "4.37.0", "@rollup/rollup-linux-arm64-musl": "4.37.0", "@rollup/rollup-linux-loongarch64-gnu": "4.37.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.37.0", "@rollup/rollup-linux-riscv64-gnu": "4.37.0", "@rollup/rollup-linux-riscv64-musl": "4.37.0", "@rollup/rollup-linux-s390x-gnu": "4.37.0", "@rollup/rollup-linux-x64-gnu": "4.37.0", "@rollup/rollup-linux-x64-musl": "4.37.0", "@rollup/rollup-win32-arm64-msvc": "4.37.0", "@rollup/rollup-win32-ia32-msvc": "4.37.0", "@rollup/rollup-win32-x64-msvc": "4.37.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-iAtQy/L4QFU+rTJ1YUjXqJOJzuwEghqWzCEYD2FEghT7Gsy1VdABntrO4CLopA5IkflTyqNiLNwPcOJ3S7UKLg=="], @@ -272,6 +292,8 @@ "tailwindcss": ["tailwindcss@4.0.17", "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-4.0.17.tgz", {}, "sha512-OErSiGzRa6rLiOvaipsDZvLMSpsBZ4ysB4f0VKGXUrjw2jfkJRd6kjRKV2+ZmTCNvwtvgdDam5D7w6WXsdLJZw=="], + "tailwindcss-primeui": ["tailwindcss-primeui@0.6.1", "https://registry.npmmirror.com/tailwindcss-primeui/-/tailwindcss-primeui-0.6.1.tgz", { "peerDependencies": { "tailwindcss": ">=3.1.0" } }, "sha512-T69Rylcrmnt8zy9ik+qZvsLuRIrS9/k6rYJSIgZ1trnbEzGDDQSCIdmfyZknevqiHwpSJHSmQ9XT2C+S/hJY4A=="], + "tapable": ["tapable@2.2.1", "https://registry.npmmirror.com/tapable/-/tapable-2.2.1.tgz", {}, "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="], "vite": ["vite@6.2.2", "https://registry.npmmirror.com/vite/-/vite-6.2.2.tgz", { "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ=="], diff --git a/frontend/admin/package.json b/frontend/admin/package.json index 9558e78..489033a 100644 --- a/frontend/admin/package.json +++ b/frontend/admin/package.json @@ -9,10 +9,14 @@ "preview": "vite preview" }, "dependencies": { + "@primeuix/themes": "^1.0.0", "@tailwindcss/vite": "^4.0.17", "axios": "^1.8.4", "daisyui": "^5.0.9", + "primeicons": "^7.0.0", + "primevue": "^4.3.3", "tailwindcss": "^4.0.17", + "tailwindcss-primeui": "^0.6.1", "vue": "^3.5.13", "vue-router": "4" }, diff --git a/frontend/admin/src/App.vue b/frontend/admin/src/App.vue index 1939366..0dde5b2 100644 --- a/frontend/admin/src/App.vue +++ b/frontend/admin/src/App.vue @@ -1,42 +1,53 @@ - - \ No newline at end of file diff --git a/frontend/admin/src/api/apiClient.js b/frontend/admin/src/api/apiClient.js new file mode 100644 index 0000000..5b1ee6b --- /dev/null +++ b/frontend/admin/src/api/apiClient.js @@ -0,0 +1,45 @@ +import axios from 'axios'; + +// Create a base axios instance +export const apiClient = axios.create({ + baseURL: import.meta.env.VITE_API_BASE_URL || '', + timeout: 10000, + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } +}); + +// Request interceptor +apiClient.interceptors.request.use( + (config) => { + // You can add auth tokens here if needed + const token = localStorage.getItem('auth_token'); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; + }, + (error) => { + return Promise.reject(error); + } +); + +// Response interceptor +apiClient.interceptors.response.use( + (response) => { + return response; + }, + (error) => { + // Handle common errors (e.g., unauthorized, server errors) + if (error.response) { + // Handle specific status codes + if (error.response.status === 401) { + // Handle unauthorized + console.error('Unauthorized request'); + // You might want to redirect to login page + } + } + return Promise.reject(error); + } +); diff --git a/frontend/admin/src/api/statsApi.js b/frontend/admin/src/api/statsApi.js new file mode 100644 index 0000000..915df7e --- /dev/null +++ b/frontend/admin/src/api/statsApi.js @@ -0,0 +1,132 @@ +import { apiClient } from './apiClient'; + +// Environment detection +let isDevelopment = false; // Default to development mode + +// Try different ways to detect environment +try { + if (typeof process !== 'undefined' && process.env) { + console.log('Detected process.env, NODE_ENV:', process.env.NODE_ENV); + isDevelopment = process.env.NODE_ENV === 'development'; + } else if (typeof import.meta !== 'undefined' && import.meta.env) { + console.log('Detected import.meta.env, MODE:', import.meta.env.MODE); + isDevelopment = import.meta.env.MODE === 'development'; + } +} catch (error) { + console.error('Error detecting environment:', error); +} +setTimeout(() => { + console.log('%cCurrent environment: ' + (isDevelopment ? 'DEVELOPMENT' : 'PRODUCTION'), + 'background: #222; color: #bada55; font-size: 16px; padding: 4px;'); +}, 0); + +// Mock service implementation +const mockService = { + /** + * Mock implementation for getting media count + * @returns {Promise<{count: number}>} + */ + getMediaCount: async () => { + // Simulate network delay + await new Promise(resolve => setTimeout(resolve, 800)); + + // Return mock data + return { + data: { + count: Math.floor(Math.random() * 500) + 100 // Random count between 100-600 + } + }; + }, + + /** + * Mock implementation for getting article count + * @returns {Promise<{count: number}>} + */ + getArticleCount: async () => { + // Simulate network delay + await new Promise(resolve => setTimeout(resolve, 600)); + + // Return mock data + return { + data: { + count: Math.floor(Math.random() * 200) + 50 // Random count between 50-250 + } + }; + } +}; + +// Real API implementation +const realApiService = { + /** + * Real implementation for getting media count + * @returns {Promise<{count: number}>} + */ + getMediaCount: async () => { + try { + return await apiClient.get('/api/media/count'); + } catch (error) { + console.error('Error fetching media count:', error); + throw error; + } + }, + + /** + * Real implementation for getting article count + * @returns {Promise<{count: number}>} + */ + getArticleCount: async () => { + try { + return await apiClient.get('/api/articles/count'); + } catch (error) { + console.error('Error fetching article count:', error); + throw error; + } + } +}; + +// Log which service we're using +console.log(`Using ${isDevelopment ? 'MOCK' : 'REAL'} API service for stats`); + +// Use the appropriate service based on environment +const apiService = isDevelopment ? mockService : realApiService; + +export const statsApi = { + /** + * Get the total count of media items + * @returns {Promise<{count: number}>} The media count + */ + getMediaCount: async () => { + const response = await apiService.getMediaCount(); + return response.data; + }, + + /** + * Get the total count of articles + * @returns {Promise<{count: number}>} The article count + */ + getArticleCount: async () => { + const response = await apiService.getArticleCount(); + return response.data; + }, + + /** + * Get all statistics in a single call + * @returns {Promise<{mediaCount: number, articleCount: number}>} + */ + getAllStats: async () => { + try { + const [mediaResponse, articleResponse] = await Promise.all([ + apiService.getMediaCount(), + apiService.getArticleCount() + ]); + + return { + mediaCount: mediaResponse.data.count, + articleCount: articleResponse.data.count + }; + } catch (error) { + console.error('Error fetching all stats:', error); + throw error; + } + } +}; diff --git a/frontend/admin/src/components/HelloWorld.vue b/frontend/admin/src/components/HelloWorld.vue deleted file mode 100644 index 546ebbc..0000000 --- a/frontend/admin/src/components/HelloWorld.vue +++ /dev/null @@ -1,43 +0,0 @@ - - - - - diff --git a/frontend/admin/src/main.js b/frontend/admin/src/main.js index df3c224..a6c3c57 100644 --- a/frontend/admin/src/main.js +++ b/frontend/admin/src/main.js @@ -1,6 +1,11 @@ +import Aura from '@primeuix/themes/aura'; +import PrimeVue from 'primevue/config'; import { createApp } from 'vue'; import App from './App.vue'; -import { router } from './router'; +import { router } from './router.js'; + +// Import only the required PrimeVue styles +import 'primeicons/primeicons.css'; // Icons import './style.css'; // Log environment information during app initialization @@ -13,4 +18,18 @@ if (typeof import.meta !== 'undefined') { } console.log('============================='); -createApp(App).use(router).mount('#app'); +const app = createApp(App) +app.use(router); +app.use(PrimeVue, { + theme: { + preset: Aura, + }, + ripple: true, + options: { + darkModeSelector: '.my-app-dark', + } +}) + +// Remove global component registrations + +app.mount('#app'); diff --git a/frontend/admin/src/pages/HomePage.vue b/frontend/admin/src/pages/HomePage.vue index fac9492..5c8402a 100644 --- a/frontend/admin/src/pages/HomePage.vue +++ b/frontend/admin/src/pages/HomePage.vue @@ -1,135 +1,91 @@ - - - + + + + + + + + + diff --git a/frontend/admin/src/pages/MediasPage.vue b/frontend/admin/src/pages/MediasPage.vue deleted file mode 100644 index bc7375e..0000000 --- a/frontend/admin/src/pages/MediasPage.vue +++ /dev/null @@ -1,481 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/admin/src/pages/PostsPage.vue b/frontend/admin/src/pages/PostsPage.vue deleted file mode 100644 index a879735..0000000 --- a/frontend/admin/src/pages/PostsPage.vue +++ /dev/null @@ -1,341 +0,0 @@ - - - \ No newline at end of file diff --git a/frontend/admin/src/router.js b/frontend/admin/src/router.js index a686a8f..36f96e2 100644 --- a/frontend/admin/src/router.js +++ b/frontend/admin/src/router.js @@ -7,16 +7,6 @@ const routes = [ name: 'Home', component: () => import('./pages/HomePage.vue') }, - { - path: '/posts', - name: 'Posts', - component: () => import('./pages/PostsPage.vue') - }, - { - path: '/medias', - name: 'Medias', - component: () => import('./pages/MediasPage.vue') - } ]; // Create the router instance diff --git a/frontend/admin/src/style.css b/frontend/admin/src/style.css index 74d8662..c376756 100644 --- a/frontend/admin/src/style.css +++ b/frontend/admin/src/style.css @@ -1,2 +1,4 @@ @import "tailwindcss"; -@plugin "daisyui"; \ No newline at end of file +@import "tailwindcss-primeui"; + +@custom-variant dark (&:where(.my-app-dark, .my-app-dark *)); \ No newline at end of file