diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 061cee9..1b90fdf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@ant-design/icons-vue': specifier: ^7.0.1 version: 7.0.1(vue@3.5.13) + '@fortawesome/fontawesome-free': + specifier: ^6.7.2 + version: 6.7.2 '@tailwindcss/vite': specifier: ^4.1.4 version: 4.1.4(vite@6.3.3(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.88.0)) @@ -23,9 +26,6 @@ importers: ant-design-vue: specifier: ^4.1.1 version: 4.2.6(vue@3.5.13) - aos: - specifier: ^2.3.4 - version: 2.3.4 axios: specifier: ^1.6.2 version: 1.9.0 @@ -263,6 +263,10 @@ packages: cpu: [x64] os: [win32] + '@fortawesome/fontawesome-free@6.7.2': + resolution: {integrity: sha512-JUOtgFW6k9u4Y+xeIaEiLr3+cjoUPiAuLXoyKOJSia6Duzb7pq+A76P9ZdPDoAoxHdHzq6gE9/jKBGXlZT8FbA==} + engines: {node: '>=6'} + '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} @@ -632,9 +636,6 @@ packages: peerDependencies: vue: '>=3.2.0' - aos@2.3.4: - resolution: {integrity: sha512-zh/ahtR2yME4I51z8IttIt4lC1Nw0ktsFtmeDzID1m9naJnWXhCoARaCgNOGXb5CLy3zm+wqmRAEgMYB5E2HUw==} - array-tree-filter@2.1.0: resolution: {integrity: sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==} @@ -659,9 +660,6 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} - classlist-polyfill@1.2.0: - resolution: {integrity: sha512-GzIjNdcEtH4ieA2S8NmrSxv7DfEV5fmixQeyTmqmRmRJPGpRBaSnA2a0VrCjyT8iW8JjEdMbKzDotAJf+ajgaQ==} - combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -893,12 +891,6 @@ packages: lodash-es@4.17.21: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} - lodash.debounce@4.0.8: - resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} - - lodash.throttle@4.1.1: - resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} - lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -908,6 +900,7 @@ packages: lucide-vue-next@0.507.0: resolution: {integrity: sha512-n0AZRmez4xq5vu5ZOfnrhC5wKBpu5tpwDHA6Ue2+sKyJv3USAPxqIerXLbdYbkHRW1tVZ3U9GE1MACsu6X/Z7Q==} + deprecated: Package deprecated. Please use @lucide/vue instead. peerDependencies: vue: '>=3.0.1' @@ -1229,6 +1222,8 @@ snapshots: '@esbuild/win32-x64@0.25.3': optional: true + '@fortawesome/fontawesome-free@6.7.2': {} + '@jridgewell/sourcemap-codec@1.5.0': {} '@parcel/watcher-android-arm64@2.5.1': @@ -1547,12 +1542,6 @@ snapshots: vue-types: 3.0.2(vue@3.5.13) warning: 4.0.3 - aos@2.3.4: - dependencies: - classlist-polyfill: 1.2.0 - lodash.debounce: 4.0.8 - lodash.throttle: 4.1.1 - array-tree-filter@2.1.0: {} async-validator@4.2.5: {} @@ -1581,8 +1570,6 @@ snapshots: dependencies: readdirp: 4.1.2 - classlist-polyfill@1.2.0: {} - combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -1787,10 +1774,6 @@ snapshots: lodash-es@4.17.21: {} - lodash.debounce@4.0.8: {} - - lodash.throttle@4.1.1: {} - lodash@4.17.21: {} loose-envify@1.4.0: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..4f4a59b --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,10 @@ +allowBuilds: + '@parcel/watcher': true + core-js: true + esbuild: true + vue-demi: true +onlyBuiltDependencies: + - '@parcel/watcher' + - core-js + - esbuild + - vue-demi diff --git a/src/App.vue b/src/App.vue index 3602e60..72d9eab 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,10 +1,23 @@ - + + + + - + diff --git a/src/api/request/dashboard.js b/src/api/request/dashboard.js new file mode 100644 index 0000000..61eb4ea --- /dev/null +++ b/src/api/request/dashboard.js @@ -0,0 +1,5 @@ +import { get, post } from '../request.js' + +export function dashboardStatsApi() { + return get('dashboard/stats') +} diff --git a/src/api/request/feedback.js b/src/api/request/feedback.js new file mode 100644 index 0000000..81da3fd --- /dev/null +++ b/src/api/request/feedback.js @@ -0,0 +1,9 @@ +import { get, post } from '../request.js' + +export function feedbackListApi(params) { + return get('feedback/list', params) +} + +export function feedbackHandleApi(id) { + return post('feedback/handle', { id }) +} diff --git a/src/api/request/level.js b/src/api/request/level.js new file mode 100644 index 0000000..158fb39 --- /dev/null +++ b/src/api/request/level.js @@ -0,0 +1,9 @@ +import { get, post } from '../request.js' + +export function levelListApi() { + return get('level/list') +} + +export function levelSaveApi(data) { + return post('level/save', data) +} diff --git a/src/api/request/module.js b/src/api/request/module.js new file mode 100644 index 0000000..23c2991 --- /dev/null +++ b/src/api/request/module.js @@ -0,0 +1,9 @@ +import { get, post } from '../request.js' + +export function moduleListApi() { + return get('module/list') +} + +export function moduleSaveApi(data) { + return post('module/save', data) +} diff --git a/src/api/request/tool.js b/src/api/request/tool.js new file mode 100644 index 0000000..828db2a --- /dev/null +++ b/src/api/request/tool.js @@ -0,0 +1,13 @@ +import { get, post } from '../request.js' + +export function toolListApi(params) { + return get('tools/all', { page: 1, pageSize: 50, includeDisabled: true, ...params }) +} + +export function toolSaveApi(data) { + return post('tools/save', data) +} + +export function toolToggleApi(data) { + return post('tools/toggle', data) +} diff --git a/src/api/request/user.js b/src/api/request/user.js index 54a22dc..442ef2d 100644 --- a/src/api/request/user.js +++ b/src/api/request/user.js @@ -28,6 +28,20 @@ export function userListApi(credentials) { return get(`${prefix}/list`, credentials) } +/** + * 设置用户等级 + */ +export function userSetLevelApi(data) { + return post(`${prefix}/set-level`, data) +} + +/** + * 启停用户 + */ +export function userStatusApi(data) { + return post(`${prefix}/status`, data) +} + /** * 获取当前登录用户信息 * @param credentials diff --git a/src/components/BarRank.vue b/src/components/BarRank.vue new file mode 100644 index 0000000..41a9bf5 --- /dev/null +++ b/src/components/BarRank.vue @@ -0,0 +1,34 @@ + + + + + 暂无使用数据 + + {{ i + 1 }} + {{ it.name || it.tool_key }} + + + + {{ it.count }} + + + diff --git a/src/components/TrendChart.vue b/src/components/TrendChart.vue new file mode 100644 index 0000000..15cfbc8 --- /dev/null +++ b/src/components/TrendChart.vue @@ -0,0 +1,60 @@ + + + + + + + + + + + + + {{ c.count }} + + {{ c.date.slice(5) }} + + + {{ layout.max }} + + + diff --git a/src/layouts/AdminLayout.vue b/src/layouts/AdminLayout.vue new file mode 100644 index 0000000..1025c07 --- /dev/null +++ b/src/layouts/AdminLayout.vue @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + 工具百宝箱 + 管理后台 + + + + + + + + + {{ route.meta.title }} + + {{ nickname }} + 退出 + + + + + + + + diff --git a/src/layouts/Header.vue b/src/layouts/Header.vue deleted file mode 100644 index 5dfaf00..0000000 --- a/src/layouts/Header.vue +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - My App - - - - - 首页 - About - Services - Contact - - - - - - Personal Center - - - - - - - diff --git a/src/router/index.js b/src/router/index.js index 796771d..71e75c6 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,15 +1,24 @@ import { createRouter, createWebHistory } from 'vue-router' import { useUserStore } from '@/stores/user' -import MainLayout from '@/layouts/Header.vue' -// 在路由配置中添加个人中心路由 const routes = [ + { + path: '/login', + name: 'Login', + component: () => import('@/views/Login.vue'), + }, { path: '/', - component: MainLayout, + component: () => import('@/layouts/AdminLayout.vue'), children: [ - { path: '', name: 'Home', component: () => import('@/views/index/index.vue') }, - ] + { path: '', redirect: '/dashboard' }, + { path: 'dashboard', name: 'Dashboard', component: () => import('@/views/dashboard/Dashboard.vue'), meta: { title: '数据看板' } }, + { path: 'tools', name: 'ToolManage', component: () => import('@/views/tools/ToolManage.vue'), meta: { title: '工具管理' } }, + { path: 'modules', name: 'ModuleManage', component: () => import('@/views/modules/ModuleManage.vue'), meta: { title: '模块管理' } }, + { path: 'levels', name: 'LevelManage', component: () => import('@/views/levels/LevelManage.vue'), meta: { title: '等级管理' } }, + { path: 'users', name: 'UserList', component: () => import('@/views/users/UserList.vue'), meta: { title: '用户管理' } }, + { path: 'feedbacks', name: 'FeedbackList', component: () => import('@/views/feedbacks/FeedbackList.vue'), meta: { title: '反馈管理' } }, + ], }, ] @@ -18,21 +27,17 @@ const router = createRouter({ routes, scrollBehavior(to, from, savedPosition) { return savedPosition || { top: 0 } - } + }, }) -router.beforeEach((to, from) => { +router.beforeEach((to) => { const userStore = useUserStore() - if (to.meta.requiresAuth && !userStore.user) { + if (to.name !== 'Login' && !userStore.isAuthenticated) { return { name: 'Login', query: { redirect: to.fullPath } } } + if (to.name === 'Login' && userStore.isAuthenticated) { + return { path: '/' } + } }) export default router - -router.beforeEach((to, from) => { - const userStore = useUserStore() - if (to.meta.requiresAuth && !userStore.user) { - return { name: 'Login', query: { redirect: to.fullPath } } - } -}) diff --git a/src/style.css b/src/style.css index a461c50..187b126 100644 --- a/src/style.css +++ b/src/style.css @@ -1 +1,17 @@ -@import "tailwindcss"; \ No newline at end of file +@import "tailwindcss"; + +/* 「橘子汽水」全局底色:避免 layout 之外露出默认深色 */ +html, +body, +#app { + min-height: 100%; + background-color: #fff9f2; +} + +body { + margin: 0; + color: #3d2a1e; + font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'MiSans', 'HarmonyOS Sans', + 'Helvetica Neue', sans-serif; + -webkit-font-smoothing: antialiased; +} diff --git a/src/views/Login.vue b/src/views/Login.vue new file mode 100644 index 0000000..21c8efb --- /dev/null +++ b/src/views/Login.vue @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 工具百宝箱 + 管理后台 + + + + + + + + + + 记住我 + 登 录 + + + 开发环境种子账号:admin / admin123 + + + diff --git a/src/views/dashboard/Dashboard.vue b/src/views/dashboard/Dashboard.vue new file mode 100644 index 0000000..9803542 --- /dev/null +++ b/src/views/dashboard/Dashboard.vue @@ -0,0 +1,57 @@ + + + + 加载中… + + + + + + {{ c.label }} + + + + + {{ stats[c.key] }} + + + + + + 近 7 日使用趋势 + + + + + + 工具使用 TOP10 + + + + diff --git a/src/views/feedbacks/FeedbackList.vue b/src/views/feedbacks/FeedbackList.vue new file mode 100644 index 0000000..d7ec3b3 --- /dev/null +++ b/src/views/feedbacks/FeedbackList.vue @@ -0,0 +1,68 @@ + + + + + + 全部 + 待处理 + 已处理 + + + + + + + {{ record.status === 1 ? '已处理' : '待处理' }} + + + + 标记已处理 + + + + + diff --git a/src/views/index/index.vue b/src/views/index/index.vue deleted file mode 100644 index 38bc235..0000000 --- a/src/views/index/index.vue +++ /dev/null @@ -1,11 +0,0 @@ - - - -你好 - - - diff --git a/src/views/levels/LevelManage.vue b/src/views/levels/LevelManage.vue new file mode 100644 index 0000000..1c6f8af --- /dev/null +++ b/src/views/levels/LevelManage.vue @@ -0,0 +1,131 @@ + + + + + + 等级 = 一组功能模块授权;变更即时对小程序生效 + 新增等级 + + + + + + {{ record.name }} + + + + {{ moduleName(m) }} + + + + + {{ record.isEnabled === 1 ? '启用' : '停用' }} + + + + 编辑 + + + + + + + + + + + + + + + + + + + + + (form.isEnabled = v ? 1 : 0)" /> + + + + + + 保存 + + + + diff --git a/src/views/modules/ModuleManage.vue b/src/views/modules/ModuleManage.vue new file mode 100644 index 0000000..acb5290 --- /dev/null +++ b/src/views/modules/ModuleManage.vue @@ -0,0 +1,110 @@ + + + + + + 新增模块 + + + + + + + + {{ (record.name || '?').slice(0, 1) }} + + {{ record.name }} + + + + + {{ record.isEnabled === 1 ? '启用' : '停用' }} + + + + 编辑 + + + + + + + + + + + + + + + + + + + + + (form.isEnabled = v ? 1 : 0)" /> + + + 保存 + + + + diff --git a/src/views/tools/ToolManage.vue b/src/views/tools/ToolManage.vue new file mode 100644 index 0000000..c934a30 --- /dev/null +++ b/src/views/tools/ToolManage.vue @@ -0,0 +1,165 @@ + + + + + + + + + 新增工具 + + + + + + + + {{ (record.name || '?').slice(0, 1) }} + + {{ record.name }} + + + + { record.isHot = v ? 1 : 0; toggle(record, 'is_hot') }" /> + + + { record.isEnabled = v ? 1 : 0; toggle(record, 'is_enabled') }" /> + + + 编辑 + + + + + + + + + + + + + + + + + + + + + + + + (form.isEnabled = v ? 1 : 0)" /> + + + (form.isHot = v ? 1 : 0)" /> + + + 保存 + + + + diff --git a/src/views/users/UserList.vue b/src/views/users/UserList.vue new file mode 100644 index 0000000..2f87402 --- /dev/null +++ b/src/views/users/UserList.vue @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + {{ (record.nickname || '微').slice(0, 1) }} + + {{ record.nickname }} + + + + setLevel(record, v)" /> + + + { record.status = v ? 1 : 0; toggleStatus(record) }" /> + + + + + diff --git a/vite.config.js b/vite.config.js index df31b24..afee117 100644 --- a/vite.config.js +++ b/vite.config.js @@ -18,8 +18,9 @@ export default defineConfig({ port: 8888, proxy: { '/api': { - // target: 'http://127.0.0.1:18007/api/', - target: 'http://127.0.0.1:18001/api/', + // 注意:target 不要带 /api 路径后缀(http-proxy 会把 target 路径拼回请求前缀) + // GoFrame 路由是 /login、/user/list 等,rewrite 已去掉 /api 前缀 + target: 'http://127.0.0.1:8000', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') }