From 89fa43d5fa43dd3823bbcba4e2710861b39c8874 Mon Sep 17 00:00:00 2001 From: bwstudio Date: Thu, 21 May 2026 21:34:09 +0800 Subject: [PATCH] feat(admin): add Vue3 admin frontend --- api/admin/index.html | 12 ++++ api/admin/package.json | 21 +++++++ api/admin/src/App.vue | 20 ++++++ api/admin/src/api/auth.js | 3 + api/admin/src/api/category.js | 10 +++ api/admin/src/api/joke.js | 9 +++ api/admin/src/api/request.js | 27 ++++++++ api/admin/src/layout/index.vue | 63 +++++++++++++++++++ api/admin/src/main.js | 12 ++++ api/admin/src/router/index.js | 37 +++++++++++ api/admin/src/stores/auth.js | 20 ++++++ api/admin/src/stores/joke.js | 34 ++++++++++ api/admin/src/views/category/Crowd.vue | 63 +++++++++++++++++++ api/admin/src/views/category/Type.vue | 63 +++++++++++++++++++ api/admin/src/views/dashboard/index.vue | 67 ++++++++++++++++++++ api/admin/src/views/joke/Edit.vue | 78 +++++++++++++++++++++++ api/admin/src/views/joke/List.vue | 68 ++++++++++++++++++++ api/admin/src/views/login/index.vue | 84 +++++++++++++++++++++++++ api/admin/vite.config.js | 21 +++++++ 19 files changed, 712 insertions(+) create mode 100644 api/admin/index.html create mode 100644 api/admin/package.json create mode 100644 api/admin/src/App.vue create mode 100644 api/admin/src/api/auth.js create mode 100644 api/admin/src/api/category.js create mode 100644 api/admin/src/api/joke.js create mode 100644 api/admin/src/api/request.js create mode 100644 api/admin/src/layout/index.vue create mode 100644 api/admin/src/main.js create mode 100644 api/admin/src/router/index.js create mode 100644 api/admin/src/stores/auth.js create mode 100644 api/admin/src/stores/joke.js create mode 100644 api/admin/src/views/category/Crowd.vue create mode 100644 api/admin/src/views/category/Type.vue create mode 100644 api/admin/src/views/dashboard/index.vue create mode 100644 api/admin/src/views/joke/Edit.vue create mode 100644 api/admin/src/views/joke/List.vue create mode 100644 api/admin/src/views/login/index.vue create mode 100644 api/admin/vite.config.js diff --git a/api/admin/index.html b/api/admin/index.html new file mode 100644 index 0000000..f368c55 --- /dev/null +++ b/api/admin/index.html @@ -0,0 +1,12 @@ + + + + + + 笑话大全 - 后台管理 + + +
+ + + diff --git a/api/admin/package.json b/api/admin/package.json new file mode 100644 index 0000000..332766d --- /dev/null +++ b/api/admin/package.json @@ -0,0 +1,21 @@ +{ + "name": "joke-admin", + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "vue": "^3.4.0", + "vue-router": "^4.2.5", + "pinia": "^2.1.7", + "element-plus": "^2.5.0", + "axios": "^1.6.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.0", + "vite": "^5.0.0" + } +} diff --git a/api/admin/src/App.vue b/api/admin/src/App.vue new file mode 100644 index 0000000..4dee7af --- /dev/null +++ b/api/admin/src/App.vue @@ -0,0 +1,20 @@ + + + + + diff --git a/api/admin/src/api/auth.js b/api/admin/src/api/auth.js new file mode 100644 index 0000000..25be045 --- /dev/null +++ b/api/admin/src/api/auth.js @@ -0,0 +1,3 @@ +import request from './request' + +export const login = data => request.post('/auth/login', data) diff --git a/api/admin/src/api/category.js b/api/admin/src/api/category.js new file mode 100644 index 0000000..0f1a50a --- /dev/null +++ b/api/admin/src/api/category.js @@ -0,0 +1,10 @@ +import request from './request' + +export const getTypes = () => request.get('/types') +export const getCrowds = () => request.get('/crowds') +export const createType = data => request.post('/admin/types', data) +export const updateType = (id, data) => request.put(`/admin/types/${id}`, data) +export const deleteType = id => request.delete(`/admin/types/${id}`) +export const createCrowd = data => request.post('/admin/crowds', data) +export const updateCrowd = (id, data) => request.put(`/admin/crowds/${id}`, data) +export const deleteCrowd = id => request.delete(`/admin/crowds/${id}`) diff --git a/api/admin/src/api/joke.js b/api/admin/src/api/joke.js new file mode 100644 index 0000000..a8ea938 --- /dev/null +++ b/api/admin/src/api/joke.js @@ -0,0 +1,9 @@ +import request from './request' + +export const getJokes = params => request.get('/admin/jokes', { params }) +export const getJoke = id => request.get(`/admin/jokes/${id}`) +export const createJoke = data => request.post('/admin/jokes', data) +export const updateJoke = (id, data) => request.put(`/admin/jokes/${id}`, data) +export const deleteJoke = id => request.delete(`/admin/jokes/${id}`) +export const batchApprove = ids => request.put('/admin/jokes/batch-approve', ids) +export const getStats = () => request.get('/admin/stats') diff --git a/api/admin/src/api/request.js b/api/admin/src/api/request.js new file mode 100644 index 0000000..9b68832 --- /dev/null +++ b/api/admin/src/api/request.js @@ -0,0 +1,27 @@ +import axios from 'axios' + +const request = axios.create({ + baseURL: '/api', + timeout: 10000 +}) + +request.interceptors.request.use(config => { + const token = localStorage.getItem('token') + if (token) { + config.headers.Authorization = `Bearer ${token}` + } + return config +}) + +request.interceptors.response.use({ + successHandler: res => res.data, + errorHandler: err => { + if (err.response?.status === 401) { + localStorage.removeItem('token') + window.location.href = '/login' + } + return Promise.reject(err) + } +}) + +export default request diff --git a/api/admin/src/layout/index.vue b/api/admin/src/layout/index.vue new file mode 100644 index 0000000..c9808e6 --- /dev/null +++ b/api/admin/src/layout/index.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/api/admin/src/main.js b/api/admin/src/main.js new file mode 100644 index 0000000..6760c11 --- /dev/null +++ b/api/admin/src/main.js @@ -0,0 +1,12 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import ElementPlus from 'element-plus' +import 'element-plus/dist/index.css' +import App from './App.vue' +import router from './router' + +const app = createApp(App) +app.use(createPinia()) +app.use(router) +app.use(ElementPlus) +app.mount('#app') diff --git a/api/admin/src/router/index.js b/api/admin/src/router/index.js new file mode 100644 index 0000000..3b7c63f --- /dev/null +++ b/api/admin/src/router/index.js @@ -0,0 +1,37 @@ +import { createRouter, createWebHistory } from 'vue-router' + +const routes = [ + { + path: '/login', + name: 'Login', + component: () => import('@/views/login/index.vue') + }, + { + path: '/', + component: () => import('@/layout/index.vue'), + children: [ + { path: '', redirect: '/dashboard' }, + { path: 'dashboard', name: 'Dashboard', component: () => import('@/views/dashboard/index.vue') }, + { path: 'jokes', name: 'JokeList', component: () => import('@/views/joke/List.vue') }, + { path: 'jokes/edit/:id?', name: 'JokeEdit', component: () => import('@/views/joke/Edit.vue') }, + { path: 'category/type', name: 'CategoryType', component: () => import('@/views/category/Type.vue') }, + { path: 'category/crowd', name: 'CategoryCrowd', component: () => import('@/views/category/Crowd.vue') }, + ] + } +] + +const router = createRouter({ + history: createWebHistory(), + routes +}) + +router.beforeEach((to, from, next) => { + const token = localStorage.getItem('token') + if (to.path !== '/login' && !token) { + next('/login') + } else { + next() + } +}) + +export default router diff --git a/api/admin/src/stores/auth.js b/api/admin/src/stores/auth.js new file mode 100644 index 0000000..837e147 --- /dev/null +++ b/api/admin/src/stores/auth.js @@ -0,0 +1,20 @@ +import { defineStore } from 'pinia' +import { login } from '@/api/auth' + +export const useAuthStore = defineStore('auth', { + state: () => ({ + token: localStorage.getItem('token') || '' + }), + actions: { + async login(username, password) { + const res = await login({ username, password }) + this.token = res.access_token + localStorage.setItem('token', res.access_token) + return res + }, + logout() { + this.token = '' + localStorage.removeItem('token') + } + } +}) diff --git a/api/admin/src/stores/joke.js b/api/admin/src/stores/joke.js new file mode 100644 index 0000000..98f86fd --- /dev/null +++ b/api/admin/src/stores/joke.js @@ -0,0 +1,34 @@ +import { defineStore } from 'pinia' +import { getJokes, createJoke, updateJoke, deleteJoke, batchApprove } from '@/api/joke' + +export const useJokeStore = defineStore('joke', { + state: () => ({ + jokes: [], + total: 0, + loading: false + }), + actions: { + async fetchJokes(params) { + this.loading = true + try { + const res = await getJokes(params) + this.jokes = res.items + this.total = res.total + } finally { + this.loading = false + } + }, + async create(data) { + return await createJoke(data) + }, + async update(id, data) { + return await updateJoke(id, data) + }, + async remove(id) { + return await deleteJoke(id) + }, + async approve(ids) { + return await batchApprove(ids) + } + } +}) diff --git a/api/admin/src/views/category/Crowd.vue b/api/admin/src/views/category/Crowd.vue new file mode 100644 index 0000000..4fa6bc9 --- /dev/null +++ b/api/admin/src/views/category/Crowd.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/api/admin/src/views/category/Type.vue b/api/admin/src/views/category/Type.vue new file mode 100644 index 0000000..90060ae --- /dev/null +++ b/api/admin/src/views/category/Type.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/api/admin/src/views/dashboard/index.vue b/api/admin/src/views/dashboard/index.vue new file mode 100644 index 0000000..96833f3 --- /dev/null +++ b/api/admin/src/views/dashboard/index.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/api/admin/src/views/joke/Edit.vue b/api/admin/src/views/joke/Edit.vue new file mode 100644 index 0000000..39e951f --- /dev/null +++ b/api/admin/src/views/joke/Edit.vue @@ -0,0 +1,78 @@ + + + diff --git a/api/admin/src/views/joke/List.vue b/api/admin/src/views/joke/List.vue new file mode 100644 index 0000000..22bda57 --- /dev/null +++ b/api/admin/src/views/joke/List.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/api/admin/src/views/login/index.vue b/api/admin/src/views/login/index.vue new file mode 100644 index 0000000..bcc76d0 --- /dev/null +++ b/api/admin/src/views/login/index.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/api/admin/vite.config.js b/api/admin/vite.config.js new file mode 100644 index 0000000..0ff83d7 --- /dev/null +++ b/api/admin/vite.config.js @@ -0,0 +1,21 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import path from 'path' + +export default defineConfig({ + plugins: [vue()], + resolve: { + alias: { + '@': path.resolve(__dirname, './src') + } + }, + server: { + port: 3001, + proxy: { + '/api': { + target: 'http://localhost:8000', + changeOrigin: true + } + } + } +})