diff --git a/admin-system/package-lock.json b/admin-system/package-lock.json index 880fbb8..6f623c4 100644 --- a/admin-system/package-lock.json +++ b/admin-system/package-lock.json @@ -25,6 +25,7 @@ "@vue/tsconfig": "^0.4.0", "eslint": "^8.22.0", "eslint-plugin-vue": "^9.0.0", + "less": "^4.4.1", "prettier": "^2.8.0", "typescript": "~5.0.0", "vite": "^4.3.0", @@ -1334,6 +1335,18 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/core-js": { "version": "3.45.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.45.1.tgz", @@ -1475,6 +1488,19 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -2066,6 +2092,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "optional": true + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -2126,6 +2159,19 @@ "he": "bin/he" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2135,6 +2181,19 @@ "node": ">= 4" } }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -2224,6 +2283,12 @@ "node": ">=0.10.0" } }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2274,6 +2339,38 @@ "json-buffer": "3.0.1" } }, + "node_modules/less": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/less/-/less-4.4.1.tgz", + "integrity": "sha512-X9HKyiXPi0f/ed0XhgUlBeFfxrlDP3xR4M7768Zl+WXLUViuL9AOPPJP4nCV0tgRWvTYvpNmN0SFhZOQzy16PA==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/less/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -2337,6 +2434,30 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -2367,6 +2488,19 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -2444,6 +2578,23 @@ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true }, + "node_modules/needle": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", + "dev": true, + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -2524,6 +2675,15 @@ "node": ">=6" } }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", @@ -2583,6 +2743,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/pinia": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz", @@ -2673,6 +2843,13 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -2781,6 +2958,20 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "optional": true + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "dev": true, + "optional": true + }, "node_modules/scroll-into-view-if-needed": { "version": "2.2.31", "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz", @@ -2836,6 +3027,16 @@ "node": ">=8" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", diff --git a/admin-system/package.json b/admin-system/package.json index fbe7014..c7e89f2 100644 --- a/admin-system/package.json +++ b/admin-system/package.json @@ -11,14 +11,14 @@ "type-check": "vue-tsc --noEmit" }, "dependencies": { - "vue": "^3.3.0", - "vue-router": "^4.2.0", - "pinia": "^2.1.0", + "@ant-design/icons-vue": "^6.1.0", "ant-design-vue": "^4.0.0", "axios": "^1.4.0", - "@ant-design/icons-vue": "^6.1.0", "dayjs": "^1.11.0", - "lodash-es": "^4.17.21" + "lodash-es": "^4.17.21", + "pinia": "^2.1.0", + "vue": "^3.3.0", + "vue-router": "^4.2.0" }, "devDependencies": { "@types/lodash-es": "^4.17.0", @@ -26,16 +26,17 @@ "@vitejs/plugin-vue": "^4.2.0", "@vue/eslint-config-typescript": "^11.0.0", "@vue/tsconfig": "^0.4.0", - "typescript": "~5.0.0", - "vite": "^4.3.0", - "vue-tsc": "^1.4.0", "eslint": "^8.22.0", "eslint-plugin-vue": "^9.0.0", - "prettier": "^2.8.0" + "less": "^4.4.1", + "prettier": "^2.8.0", + "typescript": "~5.0.0", + "vite": "^4.3.0", + "vue-tsc": "^1.4.0" }, "browserslist": [ "> 1%", "last 2 versions", "not dead" ] -} \ No newline at end of file +} diff --git a/admin-system/src/api/animal.ts b/admin-system/src/api/animal.ts new file mode 100644 index 0000000..56852fe --- /dev/null +++ b/admin-system/src/api/animal.ts @@ -0,0 +1,110 @@ +import { request } from '@/api' +import type { AxiosResponse } from 'axios' + +// 动物类型 +export type AnimalType = 'alpaca' | 'dog' | 'cat' | 'rabbit' + +// 动物状态 +export type AnimalStatus = 'available' | 'claimed' | 'reserved' + +// 认领状态 +export type ClaimStatus = 'pending' | 'approved' | 'rejected' | 'completed' + +// 动物数据结构 +export interface Animal { + id: number + name: string + type: AnimalType + breed: string + age: number + price: number + status: AnimalStatus + image_url: string + description: string + created_at: string + updated_at: string +} + +// 动物认领记录 +export interface AnimalClaim { + id: number + animal_id: number + animal_name: string + animal_image: string + user_name: string + user_phone: string + status: ClaimStatus + applied_at: string + processed_at: string +} + +// 动物查询参数 +export interface AnimalQueryParams { + page?: number + pageSize?: number + keyword?: string + type?: AnimalType + status?: AnimalStatus +} + +// 认领记录查询参数 +export interface ClaimQueryParams { + page?: number + pageSize?: number + keyword?: string + status?: ClaimStatus +} + +// API响应结构 +export interface ApiResponse { + success: boolean + code: number + message: string + data: T + pagination?: { + current: number + pageSize: number + total: number + totalPages: number + } +} + +// 获取动物列表 +export const getAnimals = async (params?: AnimalQueryParams): Promise> => { + return request.get>('/animals', { params }) +} + +// 获取动物详情 +export const getAnimal = async (id: number): Promise> => { + return request.get>(`/animals/${id}`) +} + +// 创建动物 +export const createAnimal = async (animalData: Partial): Promise> => { + return request.post>('/animals', animalData) +} + +// 更新动物 +export const updateAnimal = async (id: number, animalData: Partial): Promise> => { + return request.put>(`/animals/${id}`, animalData) +} + +// 删除动物 +export const deleteAnimal = async (id: number): Promise> => { + return request.delete>(`/animals/${id}`) +} + +// 获取认领记录列表 +export const getAnimalClaims = async (params?: ClaimQueryParams): Promise> => { + return request.get>('/animals/claims', { params }) +} + +// 审核动物认领(通过) +export const approveAnimalClaim = async (id: number): Promise> => { + return request.post>(`/animals/claims/${id}/approve`) +} + +// 拒绝动物认领 +export const rejectAnimalClaim = async (id: number, reason: string): Promise> => { + return request.post>(`/animals/claims/${id}/reject`, { reason }) +} \ No newline at end of file diff --git a/admin-system/src/api/index.ts b/admin-system/src/api/index.ts index 6670e19..ac4677e 100644 --- a/admin-system/src/api/index.ts +++ b/admin-system/src/api/index.ts @@ -151,189 +151,12 @@ export const authAPI = { request.post('/auth/logout') } -// 用户管理API -export const userAPI = { - // 获取用户列表 - getUsers: (params?: { - page?: number - pageSize?: number - search?: string - status?: string - }) => request.get('/users', { params }), - - // 获取用户详情 - getUser: (id: number) => request.get(`/users/${id}`), - - // 创建用户 - createUser: (data: any) => request.post('/users', data), - - // 更新用户 - updateUser: (id: number, data: any) => request.put(`/users/${id}`, data), - - // 删除用户 - deleteUser: (id: number) => request.delete(`/users/${id}`), - - // 批量操作 - batchUsers: (ids: number[], action: string) => - request.post('/users/batch', { ids, action }) -} - -// 商家管理API -export const merchantAPI = { - // 获取商家列表 - getMerchants: (params?: { - page?: number - pageSize?: number - search?: string - status?: string - type?: string - }) => request.get('/merchants', { params }), - - // 获取商家详情 - getMerchant: (id: number) => request.get(`/merchants/${id}`), - - // 审核商家 - approveMerchant: (id: number, data: any) => request.post(`/merchants/${id}/approve`, data), - - // 拒绝商家 - rejectMerchant: (id: number, reason: string) => request.post(`/merchants/${id}/reject`, { reason }), - - // 禁用商家 - disableMerchant: (id: number) => request.post(`/merchants/${id}/disable`), - - // 启用商家 - enableMerchant: (id: number) => request.post(`/merchants/${id}/enable`) -} - -// 旅行管理API -export const travelAPI = { - // 获取旅行计划列表 - getTravelPlans: (params?: { - page?: number - pageSize?: number - search?: string - status?: string - destination?: string - }) => request.get('/travel/plans', { params }), - - // 获取旅行计划详情 - getTravelPlan: (id: number) => request.get(`/travel/plans/${id}`), - - // 审核旅行计划 - approveTravelPlan: (id: number) => request.post(`/travel/plans/${id}/approve`), - - // 拒绝旅行计划 - rejectTravelPlan: (id: number, reason: string) => request.post(`/travel/plans/${id}/reject`, { reason }), - - // 关闭旅行计划 - closeTravelPlan: (id: number) => request.post(`/travel/plans/${id}/close`) -} - -// 动物管理API -export const animalAPI = { - // 获取动物列表 - getAnimals: (params?: { - page?: number - pageSize?: number - search?: string - status?: string - type?: string - }) => request.get('/animals', { params }), - - // 获取动物详情 - getAnimal: (id: number) => request.get(`/animals/${id}`), - - // 创建动物 - createAnimal: (data: any) => request.post('/animals', data), - - // 更新动物 - updateAnimal: (id: number, data: any) => request.put(`/animals/${id}`, data), - - // 删除动物 - deleteAnimal: (id: number) => request.delete(`/animals/${id}`), - - // 审核动物认领 - approveAnimalClaim: (id: number) => request.post(`/animals/claims/${id}/approve`), - - // 拒绝动物认领 - rejectAnimalClaim: (id: number, reason: string) => request.post(`/animals/claims/${id}/reject`, { reason }) -} - -// 订单管理API -export const orderAPI = { - // 获取订单列表 - getOrders: (params?: { - page?: number - pageSize?: number - search?: string - status?: string - type?: string - startDate?: string - endDate?: string - }) => request.get('/orders', { params }), - - // 获取订单详情 - getOrder: (id: number) => request.get(`/orders/${id}`), - - // 更新订单状态 - updateOrderStatus: (id: number, status: string) => request.put(`/orders/${id}/status`, { status }), - - // 导出订单 - exportOrders: (params: any) => request.get('/orders/export', { - params, - responseType: 'blob' - }) -} - -// 推广管理API -export const promotionAPI = { - // 获取推广数据 - getPromotionStats: () => request.get('/promotion/stats'), - - // 获取推广记录 - getPromotionRecords: (params?: { - page?: number - pageSize?: number - search?: string - status?: string - }) => request.get('/promotion/records', { params }), - - // 审核提现申请 - approveWithdrawal: (id: number) => request.post(`/promotion/withdrawals/${id}/approve`), - - // 拒绝提现申请 - rejectWithdrawal: (id: number, reason: string) => request.post(`/promotion/withdrawals/${id}/reject`, { reason }), - - // 导出推广数据 - exportPromotionData: (params: any) => request.get('/promotion/export', { - params, - responseType: 'blob' - }) -} - -// 系统管理API -export const systemAPI = { - // 获取系统配置 - getConfig: () => request.get('/system/config'), - - // 更新系统配置 - updateConfig: (data: any) => request.put('/system/config', data), - - // 获取操作日志 - getOperationLogs: (params?: { - page?: number - pageSize?: number - search?: string - action?: string - startDate?: string - endDate?: string - }) => request.get('/system/logs', { params }), - - // 清理缓存 - clearCache: () => request.post('/system/cache/clear'), - - // 系统健康检查 - healthCheck: () => request.get('/system/health') -} +export * from './user' +export * from './merchant' +export * from './travel' +export * from './animal' +export * from './order' +export * from './promotion' +export * from './system' export default api \ No newline at end of file diff --git a/admin-system/src/api/merchant.ts b/admin-system/src/api/merchant.ts new file mode 100644 index 0000000..c3865ad --- /dev/null +++ b/admin-system/src/api/merchant.ts @@ -0,0 +1,73 @@ +import { request } from '@/api' +import type { AxiosResponse } from 'axios' + +// 商家类型 +export type MerchantType = 'flower_shop' | 'activity_organizer' | 'farm_owner' + +// 商家状态 +export type MerchantStatus = 'pending' | 'approved' | 'rejected' | 'disabled' + +// 商家数据结构 +export interface Merchant { + id: number + business_name: string + merchant_type: MerchantType + contact_person: string + contact_phone: string + status: MerchantStatus + created_at: string + updated_at: string +} + +// 商家查询参数 +export interface MerchantQueryParams { + page?: number + pageSize?: number + keyword?: string + type?: MerchantType + status?: MerchantStatus +} + +// API响应结构 +export interface ApiResponse { + success: boolean + code: number + message: string + data: T + pagination?: { + current: number + pageSize: number + total: number + totalPages: number + } +} + +// 获取商家列表 +export const getMerchants = async (params?: MerchantQueryParams): Promise> => { + return request.get>('/merchants', { params }) +} + +// 获取商家详情 +export const getMerchant = async (id: number): Promise> => { + return request.get>(`/merchants/${id}`) +} + +// 审核商家(通过) +export const approveMerchant = async (id: number): Promise> => { + return request.post>(`/merchants/${id}/approve`) +} + +// 拒绝商家入驻申请 +export const rejectMerchant = async (id: number, reason: string): Promise> => { + return request.post>(`/merchants/${id}/reject`, { reason }) +} + +// 禁用商家 +export const disableMerchant = async (id: number): Promise> => { + return request.post>(`/merchants/${id}/disable`) +} + +// 启用商家 +export const enableMerchant = async (id: number): Promise> => { + return request.post>(`/merchants/${id}/enable`) +} \ No newline at end of file diff --git a/admin-system/src/api/order.ts b/admin-system/src/api/order.ts new file mode 100644 index 0000000..eb05929 --- /dev/null +++ b/admin-system/src/api/order.ts @@ -0,0 +1,95 @@ +import { request } from '@/api' +import type { AxiosResponse } from 'axios' + +// 订单状态 +export type OrderStatus = 'pending' | 'paid' | 'shipped' | 'completed' | 'cancelled' | 'refunded' + +// 支付方式 +export type PaymentMethod = 'wechat' | 'alipay' | 'bank' | 'balance' + +// 订单数据结构 +export interface Order { + id: number + order_no: string + user_id: number + user_name: string + user_phone: string + amount: number + status: OrderStatus + payment_method: PaymentMethod + created_at: string + paid_at: string + shipped_at: string + completed_at: string +} + +// 订单查询参数 +export interface OrderQueryParams { + page?: number + pageSize?: number + order_no?: string + status?: OrderStatus + orderTime?: [string, string] +} + +// 统计数据 +export interface OrderStatistics { + today_orders: number + today_sales: number + month_orders: number + month_sales: number +} + +// API响应结构 +export interface ApiResponse { + success: boolean + code: number + message: string + data: T + pagination?: { + current: number + pageSize: number + total: number + totalPages: number + } +} + +// 获取订单列表 +export const getOrders = async (params?: OrderQueryParams): Promise> => { + return request.get>('/orders', { params }) +} + +// 获取订单详情 +export const getOrder = async (id: number): Promise> => { + return request.get>(`/orders/${id}`) +} + +// 更新订单状态 +export const updateOrderStatus = async (id: number, status: OrderStatus): Promise> => { + return request.put>(`/orders/${id}/status`, { status }) +} + +// 发货 +export const shipOrder = async (id: number): Promise> => { + return request.post>(`/orders/${id}/ship`) +} + +// 完成订单 +export const completeOrder = async (id: number): Promise> => { + return request.post>(`/orders/${id}/complete`) +} + +// 取消订单 +export const cancelOrder = async (id: number): Promise> => { + return request.post>(`/orders/${id}/cancel`) +} + +// 退款 +export const refundOrder = async (id: number): Promise> => { + return request.post>(`/orders/${id}/refund`) +} + +// 获取订单统计数据 +export const getOrderStatistics = async (): Promise> => { + return request.get>('/orders/statistics') +} \ No newline at end of file diff --git a/admin-system/src/api/promotion.ts b/admin-system/src/api/promotion.ts new file mode 100644 index 0000000..cb16f62 --- /dev/null +++ b/admin-system/src/api/promotion.ts @@ -0,0 +1,133 @@ +import { request } from '@/api' +import type { AxiosResponse } from 'axios' + +// 推广活动状态 +export type PromotionStatus = 'active' | 'upcoming' | 'ended' | 'paused' + +// 奖励类型 +export type RewardType = 'cash' | 'points' | 'coupon' + +// 奖励状态 +export type RewardStatus = 'pending' | 'issued' | 'failed' + +// 推广活动数据结构 +export interface PromotionActivity { + id: number + name: string + description: string + reward_type: RewardType + reward_amount: number + status: PromotionStatus + start_time: string + end_time: string + max_participants: number + current_participants: number + created_at: string + updated_at: string +} + +// 奖励记录 +export interface RewardRecord { + id: number + user_id: number + user_name: string + user_phone: string + activity_id: number + activity_name: string + reward_type: RewardType + reward_amount: number + status: RewardStatus + issued_at: string + created_at: string +} + +// 推广活动查询参数 +export interface PromotionQueryParams { + page?: number + pageSize?: number + name?: string + status?: PromotionStatus + activityTime?: [string, string] +} + +// 奖励记录查询参数 +export interface RewardQueryParams { + page?: number + pageSize?: number + user?: string + reward_type?: RewardType + status?: RewardStatus +} + +// 统计数据 +export interface PromotionStatistics { + total_activities: number + active_activities: number + total_rewards: number + issued_rewards: number + total_amount: number +} + +// API响应结构 +export interface ApiResponse { + success: boolean + code: number + message: string + data: T + pagination?: { + current: number + pageSize: number + total: number + totalPages: number + } +} + +// 获取推广活动列表 +export const getPromotionActivities = async (params?: PromotionQueryParams): Promise> => { + return request.get>('/promotion/activities', { params }) +} + +// 获取推广活动详情 +export const getPromotionActivity = async (id: number): Promise> => { + return request.get>(`/promotion/activities/${id}`) +} + +// 创建推广活动 +export const createPromotionActivity = async (activityData: Partial): Promise> => { + return request.post>('/promotion/activities', activityData) +} + +// 更新推广活动 +export const updatePromotionActivity = async (id: number, activityData: Partial): Promise> => { + return request.put>(`/promotion/activities/${id}`, activityData) +} + +// 删除推广活动 +export const deletePromotionActivity = async (id: number): Promise> => { + return request.delete>(`/promotion/activities/${id}`) +} + +// 暂停推广活动 +export const pausePromotionActivity = async (id: number): Promise> => { + return request.post>(`/promotion/activities/${id}/pause`) +} + +// 恢复推广活动 +export const resumePromotionActivity = async (id: number): Promise> => { + return request.post>(`/promotion/activities/${id}/resume`) +} + +// 获取奖励记录列表 +export const getRewardRecords = async (params?: RewardQueryParams): Promise> => { + return request.get>('/promotion/rewards', { params }) +} + +// 发放奖励 +export const issueReward = async (id: number): Promise> => { + return request.post>(`/promotion/rewards/${id}/issue`) +} + +// 获取推广统计数据 +export const getPromotionStatistics = async (): Promise> => { + return request.get>('/promotion/statistics') +} \ No newline at end of file diff --git a/admin-system/src/api/system.ts b/admin-system/src/api/system.ts new file mode 100644 index 0000000..4bd0571 --- /dev/null +++ b/admin-system/src/api/system.ts @@ -0,0 +1,120 @@ +import { request } from '@/api' +import type { AxiosResponse } from 'axios' + +// 服务类型 +export type ServiceType = 'database' | 'cache' | 'mq' + +// 服务状态 +export type ServiceStatus = 'running' | 'stopped' + +// 系统服务数据结构 +export interface Service { + id: number + name: string + type: ServiceType + description: string + status: ServiceStatus +} + +// 系统配置 +export interface SystemSettings { + systemName: string + systemVersion: string + maintenanceMode: boolean + sessionTimeout: number + pageSize: number + enableSwagger: boolean +} + +// 系统信息 +export interface SystemInfo { + version: string + environment: string + uptime: string + startTime: string +} + +// 数据库状态 +export interface DatabaseStatus { + status: ServiceStatus + type: string + connections: string + queriesPerMinute: number +} + +// 缓存状态 +export interface CacheStatus { + status: ServiceStatus + memoryUsage: string + hitRate: string + keyCount: number +} + +// API响应结构 +export interface ApiResponse { + success: boolean + code: number + message: string + data: T +} + +// 获取系统服务列表 +export const getServices = async (): Promise> => { + return request.get>('/system/services') +} + +// 启动服务 +export const startService = async (id: number): Promise> => { + return request.post>(`/system/services/${id}/start`) +} + +// 停止服务 +export const stopService = async (id: number): Promise> => { + return request.post>(`/system/services/${id}/stop`) +} + +// 获取系统信息 +export const getSystemInfo = async (): Promise> => { + return request.get>('/system/info') +} + +// 获取数据库状态 +export const getDatabaseStatus = async (): Promise> => { + return request.get>('/system/database') +} + +// 获取缓存状态 +export const getCacheStatus = async (): Promise> => { + return request.get>('/system/cache') +} + +// 获取系统配置 +export const getSystemSettings = async (): Promise> => { + return request.get>('/system/settings') +} + +// 更新系统配置 +export const updateSystemSettings = async (settings: SystemSettings): Promise> => { + return request.put>('/system/settings', settings) +} + +// 获取操作日志 +export const getOperationLogs = async (params?: { + page?: number + pageSize?: number + search?: string + startDate?: string + endDate?: string +}): Promise> => { + return request.get>('/system/logs', { params }) +} + +// 清理缓存 +export const clearCache = async (): Promise> => { + return request.post>('/system/cache/clear') +} + +// 系统健康检查 +export const healthCheck = async (): Promise> => { + return request.get>('/system/health') +} \ No newline at end of file diff --git a/admin-system/src/api/travel.ts b/admin-system/src/api/travel.ts new file mode 100644 index 0000000..784116b --- /dev/null +++ b/admin-system/src/api/travel.ts @@ -0,0 +1,68 @@ +import { request } from '@/api' +import type { AxiosResponse } from 'axios' + +// 旅行计划状态 +export type TravelStatus = 'recruiting' | 'full' | 'completed' | 'cancelled' + +// 旅行计划数据结构 +export interface TravelPlan { + id: number + destination: string + start_date: string + end_date: string + budget: number + max_members: number + current_members: number + status: TravelStatus + creator: string + created_at: string + updated_at: string +} + +// 旅行计划查询参数 +export interface TravelQueryParams { + page?: number + pageSize?: number + destination?: string + status?: TravelStatus + travelTime?: [string, string] +} + +// API响应结构 +export interface ApiResponse { + success: boolean + code: number + message: string + data: T + pagination?: { + current: number + pageSize: number + total: number + totalPages: number + } +} + +// 获取旅行计划列表 +export const getTravelPlans = async (params?: TravelQueryParams): Promise> => { + return request.get>('/travel/plans', { params }) +} + +// 获取旅行计划详情 +export const getTravelPlan = async (id: number): Promise> => { + return request.get>(`/travel/plans/${id}`) +} + +// 审核旅行计划 +export const approveTravelPlan = async (id: number): Promise> => { + return request.post>(`/travel/plans/${id}/approve`) +} + +// 拒绝旅行计划 +export const rejectTravelPlan = async (id: number, reason: string): Promise> => { + return request.post>(`/travel/plans/${id}/reject`, { reason }) +} + +// 关闭旅行计划 +export const closeTravelPlan = async (id: number): Promise> => { + return request.post>(`/travel/plans/${id}/close`) +} \ No newline at end of file diff --git a/admin-system/src/api/user.ts b/admin-system/src/api/user.ts new file mode 100644 index 0000000..3f6365f --- /dev/null +++ b/admin-system/src/api/user.ts @@ -0,0 +1,81 @@ +import { request } from '@/api' +import type { AxiosResponse } from 'axios' + +// 用户状态类型 +export type UserStatus = 'active' | 'inactive' | 'banned' + +// 用户等级类型 +export type UserLevel = number + +// 用户数据结构 +export interface User { + id: number + openid: string + nickname: string + avatar: string + gender: string + birthday: string + phone: string + email: string + status: UserStatus + level: UserLevel + points: number + created_at: string + updated_at: string +} + +// 用户查询参数 +export interface UserQueryParams { + page?: number + pageSize?: number + keyword?: string + status?: UserStatus + registerTime?: [string, string] +} + +// API响应结构 +export interface ApiResponse { + success: boolean + code: number + message: string + data: T + pagination?: { + current: number + pageSize: number + total: number + totalPages: number + } +} + +// 获取用户列表 +export const getUsers = async (params?: UserQueryParams): Promise> => { + return request.get>('/users', { params }) +} + +// 获取用户详情 +export const getUser = async (id: number): Promise> => { + return request.get>(`/users/${id}`) +} + +// 创建用户 +export const createUser = async (userData: Partial): Promise> => { + return request.post>('/users', userData) +} + +// 更新用户 +export const updateUser = async (id: number, userData: Partial): Promise> => { + return request.put>(`/users/${id}`, userData) +} + +// 删除用户 +export const deleteUser = async (id: number): Promise> => { + return request.delete>(`/users/${id}`) +} + +// 批量操作用户 +export const batchUsers = async ( + ids: number[], + action: 'delete' | 'ban' | 'activate' +): Promise> => { + return request.post>('/users/batch', { ids, action }) +} \ No newline at end of file diff --git a/admin-system/src/pages/NotFound.vue b/admin-system/src/pages/NotFound.vue new file mode 100644 index 0000000..2d03f03 --- /dev/null +++ b/admin-system/src/pages/NotFound.vue @@ -0,0 +1,35 @@ + + + + + \ No newline at end of file diff --git a/admin-system/src/pages/animal/index.vue b/admin-system/src/pages/animal/index.vue index cf0d261..89cedd4 100644 --- a/admin-system/src/pages/animal/index.vue +++ b/admin-system/src/pages/animal/index.vue @@ -243,31 +243,8 @@ import { CheckOutlined, CloseOutlined } from '@ant-design/icons-vue' - -interface Animal { - id: number - name: string - type: string - breed: string - age: number - price: number - status: string - image_url: string - description: string - created_at: string -} - -interface AnimalClaim { - id: number - animal_id: number - animal_name: string - animal_image: string - user_name: string - user_phone: string - status: string - applied_at: string - processed_at: string -} +import { getAnimals, deleteAnimal, getAnimalClaims, approveAnimalClaim, rejectAnimalClaim } from '@/api/animal' +import type { Animal, AnimalClaim } from '@/api/animal' interface SearchForm { keyword: string @@ -295,52 +272,13 @@ const claimSearchForm = reactive({ status: '' }) -// 模拟数据 -const animalList = ref([ - { - id: 1, - name: '小白', - type: 'alpaca', - breed: '苏利羊驼', - age: 2, - price: 1000, - status: 'available', - image_url: 'https://api.dicebear.com/7.x/bottts/svg?seed=alpaca', - description: '温顺可爱的羊驼', - created_at: '2024-01-10' - }, - { - id: 2, - name: '旺财', - type: 'dog', - breed: '金毛寻回犬', - age: 1, - price: 800, - status: 'claimed', - image_url: 'https://api.dicebear.com/7.x/bottts/svg?seed=dog', - description: '活泼聪明的金毛', - created_at: '2024-02-15' - } -]) - -const claimList = ref([ - { - id: 1, - animal_id: 1, - animal_name: '小白', - animal_image: 'https://api.dicebear.com/7.x/bottts/svg?seed=alpaca', - user_name: '张先生', - user_phone: '13800138000', - status: 'pending', - applied_at: '2024-03-01', - processed_at: '' - } -]) +const animalList = ref([]) +const claimList = ref([]) const pagination = reactive({ current: 1, pageSize: 20, - total: 50, + total: 0, showSizeChanger: true, showQuickJumper: true, showTotal: (total: number) => `共 ${total} 条记录` @@ -349,7 +287,7 @@ const pagination = reactive({ const claimPagination = reactive({ current: 1, pageSize: 20, - total: 30, + total: 0, showSizeChanger: true, showQuickJumper: true, showTotal: (total: number) => `共 ${total} 条记录` @@ -459,7 +397,7 @@ const claimColumns = [ }, { title: '操作', -极速版 key: 'actions', + key: 'actions', width: 150, align: 'center' } @@ -537,7 +475,16 @@ onMounted(() => { const loadAnimals = async () => { loading.value = true try { - await new Promise(resolve => setTimeout(resolve, 500)) + const response = await getAnimals({ + page: pagination.current, + pageSize: pagination.pageSize, + keyword: searchForm.keyword, + type: searchForm.type, + status: searchForm.status + }) + + animalList.value = response.data + pagination.total = response.pagination?.total || 0 } catch (error) { message.error('加载动物列表失败') } finally { @@ -548,7 +495,15 @@ const loadAnimals = async () => { const loadClaims = async () => { claimLoading.value = true try { - await new Promise(resolve => setTimeout(resolve, 500)) + const response = await getAnimalClaims({ + page: claimPagination.current, + pageSize: claimPagination.pageSize, + keyword: claimSearchForm.keyword, + status: claimSearchForm.status + }) + + claimList.value = response.data + claimPagination.total = response.pagination?.total || 0 } catch (error) { message.error('加载认领记录失败') } finally { @@ -616,7 +571,7 @@ const handleEditAnimal = (record: Animal) => { message.info(`编辑动物: ${record.name}`) } -const handleDeleteAnimal = (record: Animal) => { +const handleDeleteAnimal = async (record: Animal) => { Modal.confirm({ title: '确认删除', content: `确定要删除动物 "${record.name}" 吗?`, @@ -624,6 +579,7 @@ const handleDeleteAnimal = (record: Animal) => { okType: 'danger', onOk: async () => { try { + await deleteAnimal(record.id) message.success('动物已删除') loadAnimals() } catch (error) { @@ -633,12 +589,13 @@ const handleDeleteAnimal = (record: Animal) => { }) } -const handleApproveClaim = (record: AnimalClaim) => { +const handleApproveClaim = async (record: AnimalClaim) => { Modal.confirm({ title: '确认通过', content: `确定要通过用户 "${record.user_name}" 的认领申请吗?`, onOk: async () => { try { + await approveAnimalClaim(record.id) message.success('认领申请已通过') loadClaims() } catch (error) { @@ -648,12 +605,13 @@ const handleApproveClaim = (record: AnimalClaim) => { }) } -const handleRejectClaim = (record: AnimalClaim) => { +const handleRejectClaim = async (record: AnimalClaim) => { Modal.confirm({ title: '确认拒绝', content: `确定要拒绝用户 "${record.user_name}" 的认领申请吗?`, onOk: async () => { try { + await rejectAnimalClaim(record.id, '拒绝原因') message.success('认领申请已拒绝') loadClaims() } catch (error) { diff --git a/admin-system/src/pages/merchant/index.vue b/admin-system/src/pages/merchant/index.vue index b29f4a8..3c90228 100644 --- a/admin-system/src/pages/merchant/index.vue +++ b/admin-system/src/pages/merchant/index.vue @@ -150,16 +150,8 @@ import { StopOutlined, PlayCircleOutlined } from '@ant-design/icons-vue' - -interface Merchant { - id: number - business_name: string - merchant_type: string - contact_person: string - contact_phone: string - status: string - created_at: string -} +import { getMerchants, approveMerchant, rejectMerchant, disableMerchant, enableMerchant } from '@/api/merchant' +import type { Merchant } from '@/api/merchant' interface SearchForm { keyword: string @@ -175,32 +167,11 @@ const searchForm = reactive({ status: '' }) -// 模拟商家数据 -const merchantList = ref([ - { - id: 1, - business_name: '鲜花坊', - merchant_type: 'flower_shop', - contact_person: '张经理', - contact_phone: '13800138000', - status: 'approved', - created_at: '2024-01-15' - }, - { - id: 2, - business_name: '阳光农场', - merchant_type: 'farm_owner', - contact_person: '李场主', - contact_phone: '13800138001', - status: 'pending', - created_at: '2024-02-20' - } -]) - +const merchantList = ref([]) const pagination = reactive({ current: 1, pageSize: 20, - total: 50, + total: 0, showSizeChanger: true, showQuickJumper: true, showTotal: (total: number) => `共 ${total} 条记录` @@ -300,8 +271,16 @@ onMounted(() => { const loadMerchants = async () => { loading.value = true try { - // TODO: 调用真实API - await new Promise(resolve => setTimeout(resolve, 500)) + const response = await getMerchants({ + page: pagination.current, + pageSize: pagination.pageSize, + keyword: searchForm.keyword, + type: searchForm.type, + status: searchForm.status + }) + + merchantList.value = response.data + pagination.total = response.pagination?.total || 0 } catch (error) { message.error('加载商家列表失败') } finally { @@ -345,6 +324,7 @@ const handleApprove = async (record: Merchant) => { content: `确定要通过商家 "${record.business_name}" 的入驻申请吗?`, onOk: async () => { try { + await approveMerchant(record.id) message.success('商家入驻申请已通过') loadMerchants() } catch (error) { @@ -360,6 +340,7 @@ const handleReject = async (record: Merchant) => { content: `确定要拒绝商家 "${record.business_name}" 的入驻申请吗?`, onOk: async () => { try { + await rejectMerchant(record.id, '拒绝原因') message.success('商家入驻申请已拒绝') loadMerchants() } catch (error) { @@ -375,6 +356,7 @@ const handleDisable = async (record: Merchant) => { content: `确定要禁用商家 "${record.business_name}" 吗?`, onOk: async () => { try { + await disableMerchant(record.id) message.success('商家已禁用') loadMerchants() } catch (error) { @@ -390,6 +372,7 @@ const handleEnable = async (record: Merchant) => { content: `确定要启用商家 "${record.business_name}" 吗?`, onOk: async () => { try { + await enableMerchant(record.id) message.success('商家已启用') loadMerchants() } catch (error) { diff --git a/admin-system/src/pages/order/index.vue b/admin-system/src/pages/order/index.vue index 5d10765..2f5a5e7 100644 --- a/admin-system/src/pages/order/index.vue +++ b/admin-system/src/pages/order/index.vue @@ -31,7 +31,7 @@ 已发货 已完成 已取消 - 已退款 + 已退款 @@ -92,11 +92,11 @@ -