refactor(backend): 重构动物相关 API 接口
- 更新了动物数据结构和相关类型定义 - 优化了动物列表、详情、创建、更新和删除接口 - 新增了更新动物状态接口 - 移除了与认领记录相关的接口 -调整了 API 响应结构
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
NODE_ENV=development
|
||||
|
||||
# API配置
|
||||
VITE_API_BASE_URL=http://localhost:3000/api/v1
|
||||
VITE_API_BASE_URL=http://localhost:3100/api/v1
|
||||
VITE_API_TIMEOUT=30000
|
||||
|
||||
# 功能开关
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<meta name="theme-color" content="#1890ff">
|
||||
|
||||
<!-- 移动端优化 -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<meta name="apple-mobile-web-app-title" content="<%- VITE_APP_NAME %>">
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ const appStore = useAppStore()
|
||||
|
||||
onMounted(() => {
|
||||
// 初始化应用
|
||||
appStore.initializeApp()
|
||||
appStore.initialize()
|
||||
|
||||
// 开发环境调试信息
|
||||
if (import.meta.env.DEV) {
|
||||
|
||||
@@ -1,110 +1,89 @@
|
||||
import { request } from '@/api'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
import { request } from '.'
|
||||
|
||||
// 动物类型
|
||||
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
|
||||
species: string
|
||||
breed: string
|
||||
age: number
|
||||
price: number
|
||||
status: AnimalStatus
|
||||
image_url: string
|
||||
gender: string
|
||||
description: string
|
||||
image: string
|
||||
merchant_id: number
|
||||
merchant_name: string
|
||||
price: number
|
||||
status: 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
|
||||
limit?: number
|
||||
keyword?: string
|
||||
type?: AnimalType
|
||||
status?: AnimalStatus
|
||||
species?: string
|
||||
status?: string
|
||||
merchant_id?: number
|
||||
start_date?: string
|
||||
end_date?: string
|
||||
}
|
||||
|
||||
// 认领记录查询参数
|
||||
export interface ClaimQueryParams {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
keyword?: string
|
||||
status?: ClaimStatus
|
||||
export interface AnimalCreateData {
|
||||
name: string
|
||||
species: string
|
||||
breed: string
|
||||
age: number
|
||||
gender: string
|
||||
description: string
|
||||
image: string
|
||||
merchant_id: number
|
||||
price: number
|
||||
status?: string
|
||||
}
|
||||
|
||||
// API响应结构
|
||||
export interface ApiResponse<T> {
|
||||
success: boolean
|
||||
code: number
|
||||
message: string
|
||||
data: T
|
||||
pagination?: {
|
||||
current: number
|
||||
pageSize: number
|
||||
total: number
|
||||
totalPages: number
|
||||
}
|
||||
export interface AnimalUpdateData {
|
||||
name?: string
|
||||
species?: string
|
||||
breed?: string
|
||||
age?: number
|
||||
gender?: string
|
||||
description?: string
|
||||
image?: string
|
||||
merchant_id?: number
|
||||
price?: number
|
||||
status?: string
|
||||
}
|
||||
|
||||
// 获取动物列表
|
||||
export const getAnimals = async (params?: AnimalQueryParams): Promise<ApiResponse<Animal[]>> => {
|
||||
return request.get<ApiResponse<Animal[]>>('/animals', { params })
|
||||
}
|
||||
export const getAnimals = (params?: AnimalQueryParams) =>
|
||||
request.get<{ success: boolean; code: number; message: string; data: { animals: Animal[]; pagination: any } }>('/animals', { params })
|
||||
|
||||
// 获取动物详情
|
||||
export const getAnimal = async (id: number): Promise<ApiResponse<Animal>> => {
|
||||
return request.get<ApiResponse<Animal>>(`/animals/${id}`)
|
||||
}
|
||||
export const getAnimal = (id: number) =>
|
||||
request.get<{ success: boolean; code: number; message: string; data: { animal: Animal } }>(`/animals/${id}`)
|
||||
|
||||
// 创建动物
|
||||
export const createAnimal = async (animalData: Partial<Animal>): Promise<ApiResponse<Animal>> => {
|
||||
return request.post<ApiResponse<Animal>>('/animals', animalData)
|
||||
}
|
||||
export const createAnimal = (data: AnimalCreateData) =>
|
||||
request.post<{ success: boolean; code: number; message: string; data: { animal: Animal } }>('/animals', data)
|
||||
|
||||
// 更新动物
|
||||
export const updateAnimal = async (id: number, animalData: Partial<Animal>): Promise<ApiResponse<Animal>> => {
|
||||
return request.put<ApiResponse<Animal>>(`/animals/${id}`, animalData)
|
||||
}
|
||||
export const updateAnimal = (id: number, data: AnimalUpdateData) =>
|
||||
request.put<{ success: boolean; code: number; message: string; data: { animal: Animal } }>(`/animals/${id}`, data)
|
||||
|
||||
// 删除动物
|
||||
export const deleteAnimal = async (id: number): Promise<ApiResponse<void>> => {
|
||||
return request.delete<ApiResponse<void>>(`/animals/${id}`)
|
||||
}
|
||||
export const deleteAnimal = (id: number) =>
|
||||
request.delete<{ success: boolean; code: number; message: string }>(`/animals/${id}`)
|
||||
|
||||
// 获取认领记录列表
|
||||
export const getAnimalClaims = async (params?: ClaimQueryParams): Promise<ApiResponse<AnimalClaim[]>> => {
|
||||
return request.get<ApiResponse<AnimalClaim[]>>('/animals/claims', { params })
|
||||
}
|
||||
// 更新动物状态
|
||||
export const updateAnimalStatus = (id: number, status: string) =>
|
||||
request.put<{ success: boolean; code: number; message: string }>(`/animals/${id}/status`, { status })
|
||||
|
||||
// 审核动物认领(通过)
|
||||
export const approveAnimalClaim = async (id: number): Promise<ApiResponse<void>> => {
|
||||
return request.post<ApiResponse<void>>(`/animals/claims/${id}/approve`)
|
||||
}
|
||||
|
||||
// 拒绝动物认领
|
||||
export const rejectAnimalClaim = async (id: number, reason: string): Promise<ApiResponse<void>> => {
|
||||
return request.post<ApiResponse<void>>(`/animals/claims/${id}/reject`, { reason })
|
||||
export default {
|
||||
getAnimals,
|
||||
getAnimal,
|
||||
createAnimal,
|
||||
updateAnimal,
|
||||
deleteAnimal,
|
||||
updateAnimalStatus
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { message } from 'ant-design-vue'
|
||||
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||
|
||||
// API基础配置
|
||||
const baseURL = import.meta.env.VITE_API_BASE_URL || '/api'
|
||||
const baseURL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3100/api'
|
||||
const timeout = parseInt(import.meta.env.VITE_API_TIMEOUT || '10000')
|
||||
|
||||
// 创建axios实例
|
||||
@@ -17,7 +17,7 @@ const api: AxiosInstance = axios.create({
|
||||
|
||||
// 请求拦截器
|
||||
api.interceptors.request.use(
|
||||
(config: AxiosRequestConfig) => {
|
||||
(config) => {
|
||||
// 添加认证token
|
||||
const token = localStorage.getItem('admin_token')
|
||||
if (token && config.headers) {
|
||||
@@ -124,18 +124,18 @@ export const authAPI = {
|
||||
success: boolean
|
||||
data: {
|
||||
token: string
|
||||
user: any
|
||||
admin: any
|
||||
}
|
||||
}>('/auth/admin/login', credentials),
|
||||
}>('/admin/login', credentials),
|
||||
|
||||
// 获取当前用户信息
|
||||
getCurrentUser: () =>
|
||||
request.get<{
|
||||
success: boolean
|
||||
data: {
|
||||
user: any
|
||||
admin: any
|
||||
}
|
||||
}>('/auth/me'),
|
||||
}>('/admin/profile'),
|
||||
|
||||
// 刷新token
|
||||
refreshToken: () =>
|
||||
@@ -159,4 +159,13 @@ export * from './order'
|
||||
export * from './promotion'
|
||||
export * from './system'
|
||||
|
||||
// 为避免命名冲突,单独导出模块
|
||||
export { default as userAPI } from './user'
|
||||
export { default as merchantAPI } from './merchant'
|
||||
export { default as travelAPI } from './travel'
|
||||
export { default as animalAPI } from './animal'
|
||||
export { default as orderAPI } from './order'
|
||||
export { default as promotionAPI } from './promotion'
|
||||
export { default as systemAPI } from './system'
|
||||
|
||||
export default api
|
||||
@@ -1,73 +1,88 @@
|
||||
import { request } from '@/api'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
import { request } from '.'
|
||||
|
||||
// 商家类型
|
||||
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
|
||||
business_license: string
|
||||
legal_representative: string
|
||||
contact_person: string
|
||||
contact_phone: string
|
||||
status: MerchantStatus
|
||||
contact_email: string
|
||||
address: string
|
||||
business_scope: string
|
||||
status: string
|
||||
remark: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
// 商家查询参数
|
||||
export interface MerchantQueryParams {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
keyword?: string
|
||||
type?: MerchantType
|
||||
status?: MerchantStatus
|
||||
limit?: number
|
||||
business_name?: string
|
||||
contact_person?: string
|
||||
contact_phone?: string
|
||||
status?: string
|
||||
start_date?: string
|
||||
end_date?: string
|
||||
}
|
||||
|
||||
// API响应结构
|
||||
export interface ApiResponse<T> {
|
||||
success: boolean
|
||||
code: number
|
||||
message: string
|
||||
data: T
|
||||
pagination?: {
|
||||
current: number
|
||||
pageSize: number
|
||||
total: number
|
||||
totalPages: number
|
||||
}
|
||||
export interface MerchantCreateData {
|
||||
business_name: string
|
||||
business_license: string
|
||||
legal_representative: string
|
||||
contact_person: string
|
||||
contact_phone: string
|
||||
contact_email: string
|
||||
address: string
|
||||
business_scope: string
|
||||
status?: string
|
||||
remark?: string
|
||||
}
|
||||
|
||||
export interface MerchantUpdateData {
|
||||
business_name?: string
|
||||
business_license?: string
|
||||
legal_representative?: string
|
||||
contact_person?: string
|
||||
contact_phone?: string
|
||||
contact_email?: string
|
||||
address?: string
|
||||
business_scope?: string
|
||||
status?: string
|
||||
remark?: string
|
||||
}
|
||||
|
||||
// 获取商家列表
|
||||
export const getMerchants = async (params?: MerchantQueryParams): Promise<ApiResponse<Merchant[]>> => {
|
||||
return request.get<ApiResponse<Merchant[]>>('/merchants', { params })
|
||||
}
|
||||
export const getMerchants = (params?: MerchantQueryParams) =>
|
||||
request.get<{ success: boolean; code: number; message: string; data: { merchants: Merchant[]; pagination: any } }>('/merchants', { params })
|
||||
|
||||
// 获取商家详情
|
||||
export const getMerchant = async (id: number): Promise<ApiResponse<Merchant>> => {
|
||||
return request.get<ApiResponse<Merchant>>(`/merchants/${id}`)
|
||||
}
|
||||
export const getMerchant = (id: number) =>
|
||||
request.get<{ success: boolean; code: number; message: string; data: { merchant: Merchant } }>(`/merchants/${id}`)
|
||||
|
||||
// 审核商家(通过)
|
||||
export const approveMerchant = async (id: number): Promise<ApiResponse<void>> => {
|
||||
return request.post<ApiResponse<void>>(`/merchants/${id}/approve`)
|
||||
}
|
||||
// 创建商家
|
||||
export const createMerchant = (data: MerchantCreateData) =>
|
||||
request.post<{ success: boolean; code: number; message: string; data: { merchant: Merchant } }>('/merchants', data)
|
||||
|
||||
// 拒绝商家入驻申请
|
||||
export const rejectMerchant = async (id: number, reason: string): Promise<ApiResponse<void>> => {
|
||||
return request.post<ApiResponse<void>>(`/merchants/${id}/reject`, { reason })
|
||||
}
|
||||
// 更新商家
|
||||
export const updateMerchant = (id: number, data: MerchantUpdateData) =>
|
||||
request.put<{ success: boolean; code: number; message: string; data: { merchant: Merchant } }>(`/merchants/${id}`, data)
|
||||
|
||||
// 禁用商家
|
||||
export const disableMerchant = async (id: number): Promise<ApiResponse<void>> => {
|
||||
return request.post<ApiResponse<void>>(`/merchants/${id}/disable`)
|
||||
}
|
||||
// 删除商家
|
||||
export const deleteMerchant = (id: number) =>
|
||||
request.delete<{ success: boolean; code: number; message: string }>(`/merchants/${id}`)
|
||||
|
||||
// 启用商家
|
||||
export const enableMerchant = async (id: number): Promise<ApiResponse<void>> => {
|
||||
return request.post<ApiResponse<void>>(`/merchants/${id}/enable`)
|
||||
// 更新商家状态
|
||||
export const updateMerchantStatus = (id: number, status: string) =>
|
||||
request.put<{ success: boolean; code: number; message: string }>(`/merchants/${id}/status`, { status })
|
||||
|
||||
export default {
|
||||
getMerchants,
|
||||
getMerchant,
|
||||
createMerchant,
|
||||
updateMerchant,
|
||||
deleteMerchant,
|
||||
updateMerchantStatus
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { request } from '@/api'
|
||||
import { request } from '.'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
|
||||
// 订单状态
|
||||
@@ -13,31 +13,42 @@ export interface Order {
|
||||
order_no: string
|
||||
user_id: number
|
||||
user_name: string
|
||||
user_phone: string
|
||||
amount: number
|
||||
status: OrderStatus
|
||||
payment_method: PaymentMethod
|
||||
merchant_id: number
|
||||
merchant_name: string
|
||||
product_id: number
|
||||
product_name: string
|
||||
product_type: string
|
||||
quantity: number
|
||||
unit_price: number
|
||||
total_amount: number
|
||||
status: string
|
||||
payment_method: string
|
||||
payment_time: string
|
||||
refund_status: string
|
||||
remark: string
|
||||
created_at: string
|
||||
paid_at: string
|
||||
shipped_at: string
|
||||
completed_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
// 订单查询参数
|
||||
export interface OrderQueryParams {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
limit?: number
|
||||
order_no?: string
|
||||
status?: OrderStatus
|
||||
orderTime?: [string, string]
|
||||
user_name?: string
|
||||
merchant_name?: string
|
||||
product_type?: string
|
||||
status?: string
|
||||
payment_method?: string
|
||||
start_date?: string
|
||||
end_date?: string
|
||||
}
|
||||
|
||||
// 统计数据
|
||||
export interface OrderStatistics {
|
||||
today_orders: number
|
||||
today_sales: number
|
||||
month_orders: number
|
||||
month_sales: number
|
||||
// 订单更新参数
|
||||
export interface OrderUpdateData {
|
||||
status?: string
|
||||
refund_status?: string
|
||||
remark?: string
|
||||
}
|
||||
|
||||
// API响应结构
|
||||
@@ -55,41 +66,29 @@ export interface ApiResponse<T> {
|
||||
}
|
||||
|
||||
// 获取订单列表
|
||||
export const getOrders = async (params?: OrderQueryParams): Promise<ApiResponse<Order[]>> => {
|
||||
return request.get<ApiResponse<Order[]>>('/orders', { params })
|
||||
}
|
||||
export const getOrders = (params?: OrderQueryParams) =>
|
||||
request.get<{ success: boolean; code: number; message: string; data: { orders: Order[]; pagination: any } }>('/orders', { params })
|
||||
|
||||
// 获取订单详情
|
||||
export const getOrder = async (id: number): Promise<ApiResponse<Order>> => {
|
||||
return request.get<ApiResponse<Order>>(`/orders/${id}`)
|
||||
}
|
||||
export const getOrder = (id: number) =>
|
||||
request.get<{ success: boolean; code: number; message: string; data: { order: Order } }>(`/orders/${id}`)
|
||||
|
||||
// 更新订单
|
||||
export const updateOrder = (id: number, data: OrderUpdateData) =>
|
||||
request.put<{ success: boolean; code: number; message: string; data: { order: Order } }>(`/orders/${id}`, data)
|
||||
|
||||
// 删除订单
|
||||
export const deleteOrder = (id: number) =>
|
||||
request.delete<{ success: boolean; code: number; message: string }>(`/orders/${id}`)
|
||||
|
||||
// 更新订单状态
|
||||
export const updateOrderStatus = async (id: number, status: OrderStatus): Promise<ApiResponse<void>> => {
|
||||
return request.put<ApiResponse<void>>(`/orders/${id}/status`, { status })
|
||||
}
|
||||
export const updateOrderStatus = (id: number, status: string) =>
|
||||
request.put<{ success: boolean; code: number; message: string }>(`/orders/${id}/status`, { status })
|
||||
|
||||
// 发货
|
||||
export const shipOrder = async (id: number): Promise<ApiResponse<void>> => {
|
||||
return request.post<ApiResponse<void>>(`/orders/${id}/ship`)
|
||||
}
|
||||
|
||||
// 完成订单
|
||||
export const completeOrder = async (id: number): Promise<ApiResponse<void>> => {
|
||||
return request.post<ApiResponse<void>>(`/orders/${id}/complete`)
|
||||
}
|
||||
|
||||
// 取消订单
|
||||
export const cancelOrder = async (id: number): Promise<ApiResponse<void>> => {
|
||||
return request.post<ApiResponse<void>>(`/orders/${id}/cancel`)
|
||||
}
|
||||
|
||||
// 退款
|
||||
export const refundOrder = async (id: number): Promise<ApiResponse<void>> => {
|
||||
return request.post<ApiResponse<void>>(`/orders/${id}/refund`)
|
||||
}
|
||||
|
||||
// 获取订单统计数据
|
||||
export const getOrderStatistics = async (): Promise<ApiResponse<OrderStatistics>> => {
|
||||
return request.get<ApiResponse<OrderStatistics>>('/orders/statistics')
|
||||
export default {
|
||||
getOrders,
|
||||
getOrder,
|
||||
updateOrder,
|
||||
deleteOrder,
|
||||
updateOrderStatus
|
||||
}
|
||||
@@ -1,5 +1,81 @@
|
||||
import { request } from '@/api'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
import { request } from '.'
|
||||
|
||||
// 定义促销相关类型
|
||||
export interface Promotion {
|
||||
id: number
|
||||
title: string
|
||||
description: string
|
||||
type: string
|
||||
discount_value: number
|
||||
start_date: string
|
||||
end_date: string
|
||||
status: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface PromotionQueryParams {
|
||||
page?: number
|
||||
limit?: number
|
||||
title?: string
|
||||
type?: string
|
||||
status?: string
|
||||
start_date?: string
|
||||
end_date?: string
|
||||
}
|
||||
|
||||
export interface PromotionCreateData {
|
||||
title: string
|
||||
description: string
|
||||
type: string
|
||||
discount_value: number
|
||||
start_date: string
|
||||
end_date: string
|
||||
status?: string
|
||||
}
|
||||
|
||||
export interface PromotionUpdateData {
|
||||
title?: string
|
||||
description?: string
|
||||
type?: string
|
||||
discount_value?: number
|
||||
start_date?: string
|
||||
end_date?: string
|
||||
status?: string
|
||||
}
|
||||
|
||||
// 获取促销列表
|
||||
export const getPromotions = (params?: PromotionQueryParams) =>
|
||||
request.get<{ success: boolean; code: number; message: string; data: { promotions: Promotion[]; pagination: any } }>('/promotions', { params })
|
||||
|
||||
// 获取促销详情
|
||||
export const getPromotion = (id: number) =>
|
||||
request.get<{ success: boolean; code: number; message: string; data: { promotion: Promotion } }>(`/promotions/${id}`)
|
||||
|
||||
// 创建促销
|
||||
export const createPromotion = (data: PromotionCreateData) =>
|
||||
request.post<{ success: boolean; code: number; message: string; data: { promotion: Promotion } }>('/promotions', data)
|
||||
|
||||
// 更新促销
|
||||
export const updatePromotion = (id: number, data: PromotionUpdateData) =>
|
||||
request.put<{ success: boolean; code: number; message: string; data: { promotion: Promotion } }>(`/promotions/${id}`, data)
|
||||
|
||||
// 删除促销
|
||||
export const deletePromotion = (id: number) =>
|
||||
request.delete<{ success: boolean; code: number; message: string }>(`/promotions/${id}`)
|
||||
|
||||
// 更新促销状态
|
||||
export const updatePromotionStatus = (id: number, status: string) =>
|
||||
request.put<{ success: boolean; code: number; message: string }>(`/promotions/${id}/status`, { status })
|
||||
|
||||
export default {
|
||||
getPromotions,
|
||||
getPromotion,
|
||||
createPromotion,
|
||||
updatePromotion,
|
||||
deletePromotion,
|
||||
updatePromotionStatus
|
||||
}
|
||||
|
||||
// 推广活动状态
|
||||
export type PromotionStatus = 'active' | 'upcoming' | 'ended' | 'paused'
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { request } from '@/api'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
import { request } from '.'
|
||||
|
||||
// 服务类型
|
||||
export type ServiceType = 'database' | 'cache' | 'mq'
|
||||
@@ -9,112 +8,79 @@ export type ServiceStatus = 'running' | 'stopped'
|
||||
|
||||
// 系统服务数据结构
|
||||
export interface Service {
|
||||
id: number
|
||||
id: string
|
||||
name: string
|
||||
type: ServiceType
|
||||
status: ServiceStatus
|
||||
host: string
|
||||
port: number
|
||||
description: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface ServiceQueryParams {
|
||||
page?: number
|
||||
limit?: number
|
||||
type?: ServiceType
|
||||
status?: ServiceStatus
|
||||
}
|
||||
|
||||
export interface ServiceUpdateData {
|
||||
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<T> {
|
||||
success: boolean
|
||||
code: number
|
||||
message: string
|
||||
data: T
|
||||
}
|
||||
|
||||
// 获取系统服务列表
|
||||
export const getServices = async (): Promise<ApiResponse<Service[]>> => {
|
||||
return request.get<ApiResponse<Service[]>>('/system/services')
|
||||
export const getServices = (params?: ServiceQueryParams) =>
|
||||
request.get<{ success: boolean; code: number; message: string; data: { services: Service[]; pagination: any } }>('/system/services', { params })
|
||||
|
||||
// 更新系统服务状态
|
||||
export const updateServiceStatus = (id: string, data: ServiceUpdateData) =>
|
||||
request.put<{ success: boolean; code: number; message: string }>(`/system/services/${id}/status`, data)
|
||||
|
||||
// 定义系统配置相关类型
|
||||
export interface SystemConfig {
|
||||
id: string
|
||||
name: string
|
||||
value: string
|
||||
type: string
|
||||
group: string
|
||||
description: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
// 启动服务
|
||||
export const startService = async (id: number): Promise<ApiResponse<void>> => {
|
||||
return request.post<ApiResponse<void>>(`/system/services/${id}/start`)
|
||||
export interface SystemConfigQueryParams {
|
||||
page?: number
|
||||
limit?: number
|
||||
group?: string
|
||||
}
|
||||
|
||||
// 停止服务
|
||||
export const stopService = async (id: number): Promise<ApiResponse<void>> => {
|
||||
return request.post<ApiResponse<void>>(`/system/services/${id}/stop`)
|
||||
export interface SystemConfigUpdateData {
|
||||
value: string
|
||||
}
|
||||
|
||||
// 获取系统信息
|
||||
export const getSystemInfo = async (): Promise<ApiResponse<SystemInfo>> => {
|
||||
return request.get<ApiResponse<SystemInfo>>('/system/info')
|
||||
}
|
||||
|
||||
// 获取数据库状态
|
||||
export const getDatabaseStatus = async (): Promise<ApiResponse<DatabaseStatus>> => {
|
||||
return request.get<ApiResponse<DatabaseStatus>>('/system/database')
|
||||
}
|
||||
|
||||
// 获取缓存状态
|
||||
export const getCacheStatus = async (): Promise<ApiResponse<CacheStatus>> => {
|
||||
return request.get<ApiResponse<CacheStatus>>('/system/cache')
|
||||
}
|
||||
|
||||
// 获取系统配置
|
||||
export const getSystemSettings = async (): Promise<ApiResponse<SystemSettings>> => {
|
||||
return request.get<ApiResponse<SystemSettings>>('/system/settings')
|
||||
}
|
||||
// 获取系统配置列表
|
||||
export const getSystemConfigs = (params?: SystemConfigQueryParams) =>
|
||||
request.get<{ success: boolean; code: number; message: string; data: { configs: SystemConfig[]; pagination: any } }>('/system-configs', { params })
|
||||
|
||||
// 更新系统配置
|
||||
export const updateSystemSettings = async (settings: SystemSettings): Promise<ApiResponse<void>> => {
|
||||
return request.put<ApiResponse<void>>('/system/settings', settings)
|
||||
}
|
||||
export const updateSystemConfig = (id: string, data: SystemConfigUpdateData) =>
|
||||
request.put<{ success: boolean; code: number; message: string }>(`/system-configs/${id}`, data)
|
||||
|
||||
// 获取操作日志
|
||||
export const getOperationLogs = async (params?: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
search?: string
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
}): Promise<ApiResponse<any[]>> => {
|
||||
return request.get<ApiResponse<any[]>>('/system/logs', { params })
|
||||
}
|
||||
// 获取系统统计信息
|
||||
export const getSystemStats = () =>
|
||||
request.get<{ success: boolean; code: number; message: string; data: any }>('/system/stats')
|
||||
|
||||
// 清理缓存
|
||||
export const clearCache = async (): Promise<ApiResponse<void>> => {
|
||||
return request.post<ApiResponse<void>>('/system/cache/clear')
|
||||
}
|
||||
// 获取系统日志
|
||||
export const getSystemLogs = (params?: { page?: number; limit?: number; level?: string }) =>
|
||||
request.get<{ success: boolean; code: number; message: string; data: { logs: any[]; pagination: any } }>('/system/logs', { params })
|
||||
|
||||
// 系统健康检查
|
||||
export const healthCheck = async (): Promise<ApiResponse<any>> => {
|
||||
return request.get<ApiResponse<any>>('/system/health')
|
||||
}
|
||||
export default {
|
||||
getServices,
|
||||
updateServiceStatus,
|
||||
getSystemConfigs,
|
||||
updateSystemConfig,
|
||||
getSystemStats,
|
||||
getSystemLogs
|
||||
}
|
||||
|
||||
@@ -1,68 +1,82 @@
|
||||
import { request } from '@/api'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
import { request } from '.'
|
||||
|
||||
// 旅行计划状态
|
||||
export type TravelStatus = 'recruiting' | 'full' | 'completed' | 'cancelled'
|
||||
|
||||
// 旅行计划数据结构
|
||||
export interface TravelPlan {
|
||||
// 定义结伴游相关类型
|
||||
export interface Travel {
|
||||
id: number
|
||||
destination: string
|
||||
title: string
|
||||
description: string
|
||||
start_date: string
|
||||
end_date: string
|
||||
budget: number
|
||||
max_members: number
|
||||
current_members: number
|
||||
status: TravelStatus
|
||||
creator: string
|
||||
destination: string
|
||||
max_participants: number
|
||||
current_participants: number
|
||||
price: number
|
||||
status: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
// 旅行计划查询参数
|
||||
export interface TravelQueryParams {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
limit?: number
|
||||
title?: string
|
||||
destination?: string
|
||||
status?: TravelStatus
|
||||
travelTime?: [string, string]
|
||||
status?: string
|
||||
start_date?: string
|
||||
end_date?: string
|
||||
}
|
||||
|
||||
// API响应结构
|
||||
export interface ApiResponse<T> {
|
||||
success: boolean
|
||||
code: number
|
||||
message: string
|
||||
data: T
|
||||
pagination?: {
|
||||
current: number
|
||||
pageSize: number
|
||||
total: number
|
||||
totalPages: number
|
||||
}
|
||||
export interface TravelCreateData {
|
||||
title: string
|
||||
description: string
|
||||
start_date: string
|
||||
end_date: string
|
||||
destination: string
|
||||
max_participants: number
|
||||
price: number
|
||||
status?: string
|
||||
}
|
||||
|
||||
// 获取旅行计划列表
|
||||
export const getTravelPlans = async (params?: TravelQueryParams): Promise<ApiResponse<TravelPlan[]>> => {
|
||||
return request.get<ApiResponse<TravelPlan[]>>('/travel/plans', { params })
|
||||
export interface TravelUpdateData {
|
||||
title?: string
|
||||
description?: string
|
||||
start_date?: string
|
||||
end_date?: string
|
||||
destination?: string
|
||||
max_participants?: number
|
||||
price?: number
|
||||
status?: string
|
||||
}
|
||||
|
||||
// 获取旅行计划详情
|
||||
export const getTravelPlan = async (id: number): Promise<ApiResponse<TravelPlan>> => {
|
||||
return request.get<ApiResponse<TravelPlan>>(`/travel/plans/${id}`)
|
||||
}
|
||||
// 获取结伴游列表
|
||||
export const getTravels = (params?: TravelQueryParams) =>
|
||||
request.get<{ success: boolean; code: number; message: string; data: { travels: Travel[]; pagination: any } }>('/travels', { params })
|
||||
|
||||
// 审核旅行计划
|
||||
export const approveTravelPlan = async (id: number): Promise<ApiResponse<void>> => {
|
||||
return request.post<ApiResponse<void>>(`/travel/plans/${id}/approve`)
|
||||
}
|
||||
// 获取结伴游详情
|
||||
export const getTravel = (id: number) =>
|
||||
request.get<{ success: boolean; code: number; message: string; data: { travel: Travel } }>(`/travels/${id}`)
|
||||
|
||||
// 拒绝旅行计划
|
||||
export const rejectTravelPlan = async (id: number, reason: string): Promise<ApiResponse<void>> => {
|
||||
return request.post<ApiResponse<void>>(`/travel/plans/${id}/reject`, { reason })
|
||||
}
|
||||
// 创建结伴游
|
||||
export const createTravel = (data: TravelCreateData) =>
|
||||
request.post<{ success: boolean; code: number; message: string; data: { travel: Travel } }>('/travels', data)
|
||||
|
||||
// 关闭旅行计划
|
||||
export const closeTravelPlan = async (id: number): Promise<ApiResponse<void>> => {
|
||||
return request.post<ApiResponse<void>>(`/travel/plans/${id}/close`)
|
||||
// 更新结伴游
|
||||
export const updateTravel = (id: number, data: TravelUpdateData) =>
|
||||
request.put<{ success: boolean; code: number; message: string; data: { travel: Travel } }>(`/travels/${id}`, data)
|
||||
|
||||
// 删除结伴游
|
||||
export const deleteTravel = (id: number) =>
|
||||
request.delete<{ success: boolean; code: number; message: string }>(`/travels/${id}`)
|
||||
|
||||
// 更新结伴游状态
|
||||
export const updateTravelStatus = (id: number, status: string) =>
|
||||
request.put<{ success: boolean; code: number; message: string }>(`/travels/${id}/status`, { status })
|
||||
|
||||
export default {
|
||||
getTravels,
|
||||
getTravel,
|
||||
createTravel,
|
||||
updateTravel,
|
||||
deleteTravel,
|
||||
updateTravelStatus
|
||||
}
|
||||
@@ -1,81 +1,109 @@
|
||||
import { request } from '@/api'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
import { request } from '.'
|
||||
|
||||
// 用户状态类型
|
||||
export type UserStatus = 'active' | 'inactive' | 'banned'
|
||||
|
||||
// 用户等级类型
|
||||
export type UserLevel = number
|
||||
|
||||
// 用户数据结构
|
||||
// 定义用户相关类型
|
||||
export interface User {
|
||||
id: number
|
||||
openid: string
|
||||
username: string
|
||||
nickname: string
|
||||
email: string
|
||||
phone: string
|
||||
avatar: string
|
||||
gender: string
|
||||
birthday: string
|
||||
phone: string
|
||||
email: string
|
||||
status: UserStatus
|
||||
level: UserLevel
|
||||
points: number
|
||||
level: number
|
||||
balance: number
|
||||
travel_count: number
|
||||
animal_adopt_count: number
|
||||
flower_order_count: number
|
||||
status: string
|
||||
remark: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
last_login_at: string
|
||||
}
|
||||
|
||||
// 用户查询参数
|
||||
export interface UserQueryParams {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
keyword?: string
|
||||
status?: UserStatus
|
||||
registerTime?: [string, string]
|
||||
limit?: number
|
||||
username?: string
|
||||
nickname?: string
|
||||
phone?: string
|
||||
status?: string
|
||||
start_date?: string
|
||||
end_date?: string
|
||||
}
|
||||
|
||||
// API响应结构
|
||||
export interface ApiResponse<T> {
|
||||
export interface UserCreateData {
|
||||
username: string
|
||||
password: string
|
||||
nickname?: string
|
||||
email?: string
|
||||
phone?: string
|
||||
gender?: string
|
||||
birthday?: string
|
||||
status?: string
|
||||
remark?: string
|
||||
}
|
||||
|
||||
export interface UserUpdateData {
|
||||
username?: string
|
||||
password?: string
|
||||
nickname?: string
|
||||
email?: string
|
||||
phone?: string
|
||||
gender?: string
|
||||
birthday?: string
|
||||
status?: string
|
||||
remark?: string
|
||||
}
|
||||
|
||||
export interface ApiResponse<T = any> {
|
||||
success: boolean
|
||||
code: number
|
||||
message: string
|
||||
data: T
|
||||
pagination?: {
|
||||
current: number
|
||||
pageSize: number
|
||||
total: number
|
||||
totalPages: number
|
||||
}
|
||||
}
|
||||
|
||||
// 获取用户列表
|
||||
export const getUsers = async (params?: UserQueryParams): Promise<ApiResponse<User[]>> => {
|
||||
return request.get<ApiResponse<User[]>>('/users', { params })
|
||||
}
|
||||
export const getUsers = (params?: UserQueryParams) =>
|
||||
request.get<ApiResponse<{ users: User[]; pagination: any }>>('/users', { params })
|
||||
|
||||
// 获取用户详情
|
||||
export const getUser = async (id: number): Promise<ApiResponse<User>> => {
|
||||
return request.get<ApiResponse<User>>(`/users/${id}`)
|
||||
}
|
||||
export const getUser = (id: number) =>
|
||||
request.get<ApiResponse<User>>(`/users/${id}`)
|
||||
|
||||
// 创建用户
|
||||
export const createUser = async (userData: Partial<User>): Promise<ApiResponse<User>> => {
|
||||
return request.post<ApiResponse<User>>('/users', userData)
|
||||
}
|
||||
export const createUser = (data: UserCreateData) =>
|
||||
request.post<ApiResponse<User>>('/users', data)
|
||||
|
||||
// 更新用户
|
||||
export const updateUser = async (id: number, userData: Partial<User>): Promise<ApiResponse<User>> => {
|
||||
return request.put<ApiResponse<User>>(`/users/${id}`, userData)
|
||||
}
|
||||
export const updateUser = (id: number, data: UserUpdateData) =>
|
||||
request.put<ApiResponse<User>>(`/users/${id}`, data)
|
||||
|
||||
// 删除用户
|
||||
export const deleteUser = async (id: number): Promise<ApiResponse<void>> => {
|
||||
return request.delete<ApiResponse<void>>(`/users/${id}`)
|
||||
}
|
||||
export const deleteUser = (id: number) =>
|
||||
request.delete<ApiResponse<void>>(`/users/${id}`)
|
||||
|
||||
// 批量操作用户
|
||||
export const batchUsers = async (
|
||||
ids: number[],
|
||||
action: 'delete' | 'ban' | 'activate'
|
||||
): Promise<ApiResponse<void>> => {
|
||||
return request.post<ApiResponse<void>>('/users/batch', { ids, action })
|
||||
// 批量删除用户
|
||||
export const batchDeleteUsers = (ids: number[]) =>
|
||||
request.post<ApiResponse<void>>('/users/batch-delete', { ids })
|
||||
|
||||
// 更新用户状态
|
||||
export const updateUserStatus = (id: number, status: string) =>
|
||||
request.put<ApiResponse<User>>(`/users/${id}/status`, { status })
|
||||
|
||||
// 重置用户密码
|
||||
export const resetUserPassword = (id: number, password: string) =>
|
||||
request.put<ApiResponse<User>>(`/users/${id}/password`, { password })
|
||||
|
||||
export default {
|
||||
getUsers,
|
||||
getUser,
|
||||
createUser,
|
||||
updateUser,
|
||||
deleteUser,
|
||||
batchDeleteUsers,
|
||||
updateUserStatus,
|
||||
resetUserPassword
|
||||
}
|
||||
@@ -69,6 +69,7 @@ import { useRouter } from 'vue-router'
|
||||
import { UserOutlined, LockOutlined } from '@ant-design/icons-vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { authAPI } from '@/api'
|
||||
|
||||
interface FormState {
|
||||
username: string
|
||||
@@ -91,25 +92,22 @@ const onFinish = async (values: FormState) => {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
// 模拟登录过程
|
||||
console.log('登录信息:', values)
|
||||
|
||||
// TODO: 调用真实登录接口
|
||||
// const response = await authAPI.login(values)
|
||||
|
||||
// 模拟登录成功
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
// 调用真实登录接口
|
||||
const response = await authAPI.login(values)
|
||||
|
||||
// 保存token
|
||||
localStorage.setItem('admin_token', 'mock_token_123456')
|
||||
if (response?.data?.token) {
|
||||
localStorage.setItem('admin_token', response.data.token)
|
||||
} else {
|
||||
throw new Error('登录响应中缺少token')
|
||||
}
|
||||
|
||||
// 更新用户状态
|
||||
appStore.setUser({
|
||||
id: 1,
|
||||
username: values.username,
|
||||
nickname: '管理员',
|
||||
role: 'admin'
|
||||
})
|
||||
if (response?.data?.admin) {
|
||||
appStore.setUser(response.data.admin)
|
||||
} else {
|
||||
throw new Error('登录响应中缺少用户信息')
|
||||
}
|
||||
|
||||
message.success('登录成功!')
|
||||
|
||||
@@ -117,9 +115,10 @@ const onFinish = async (values: FormState) => {
|
||||
const redirect = router.currentRoute.value.query.redirect as string
|
||||
router.push(redirect || '/dashboard')
|
||||
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error('登录失败:', error)
|
||||
message.error('登录失败,请检查用户名和密码')
|
||||
const errorMessage = error.message || '登录失败,请检查用户名和密码'
|
||||
message.error(errorMessage)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -174,10 +173,11 @@ const onFinishFailed = (errorInfo: any) => {
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.login-footer p {
|
||||
.login-footer {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:deep(.ant-input-affix-wrapper) {
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ref, reactive, onMounted, h } from 'vue'
|
||||
import { message, Modal } from 'ant-design-vue'
|
||||
import type { TableProps } from 'ant-design-vue'
|
||||
import {
|
||||
@@ -150,7 +150,7 @@ import {
|
||||
StopOutlined,
|
||||
PlayCircleOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import { getMerchants, approveMerchant, rejectMerchant, disableMerchant, enableMerchant } from '@/api/merchant'
|
||||
import { getMerchants, getMerchant, approveMerchant, rejectMerchant, disableMerchant, enableMerchant } from '@/api/merchant'
|
||||
import type { Merchant } from '@/api/merchant'
|
||||
|
||||
interface SearchForm {
|
||||
@@ -314,8 +314,33 @@ const handleTableChange: TableProps['onChange'] = (pag) => {
|
||||
loadMerchants()
|
||||
}
|
||||
|
||||
const handleView = (record: Merchant) => {
|
||||
message.info(`查看商家: ${record.business_name}`)
|
||||
const handleView = async (record: Merchant) => {
|
||||
try {
|
||||
const response = await getMerchant(record.id)
|
||||
Modal.info({
|
||||
title: '商家详情',
|
||||
width: 600,
|
||||
content: h('div', { class: 'merchant-detail-modal' }, [
|
||||
h('a-descriptions', {
|
||||
column: 1,
|
||||
bordered: true
|
||||
}, [
|
||||
h('a-descriptions-item', { label: '商家ID' }, response.data.id),
|
||||
h('a-descriptions-item', { label: '商家名称' }, response.data.business_name),
|
||||
h('a-descriptions-item', { label: '商家类型' }, getTypeText(response.data.merchant_type)),
|
||||
h('a-descriptions-item', { label: '联系人' }, response.data.contact_person),
|
||||
h('a-descriptions-item', { label: '联系电话' }, response.data.contact_phone),
|
||||
h('a-descriptions-item', { label: '状态' }, [
|
||||
h('a-tag', { color: getStatusColor(response.data.status) }, getStatusText(response.data.status))
|
||||
]),
|
||||
h('a-descriptions-item', { label: '入驻时间' }, response.data.created_at),
|
||||
h('a-descriptions-item', { label: '更新时间' }, response.data.updated_at)
|
||||
])
|
||||
])
|
||||
})
|
||||
} catch (error) {
|
||||
message.error('获取商家详情失败')
|
||||
}
|
||||
}
|
||||
|
||||
const handleApprove = async (record: Merchant) => {
|
||||
|
||||
@@ -1,162 +1,241 @@
|
||||
<template>
|
||||
<div class="order-management">
|
||||
<a-page-header title="订单管理" sub-title="管理花束订单和交易记录">
|
||||
<a-page-header
|
||||
title="订单管理"
|
||||
sub-title="管理系统订单信息"
|
||||
>
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-button @click="handleRefresh">
|
||||
<template #icon><ReloadOutlined /></template>
|
||||
<template #icon>
|
||||
<ReloadOutlined />
|
||||
</template>
|
||||
刷新
|
||||
</a-button>
|
||||
<a-button type="primary" @click="showStats">
|
||||
<template #icon><BarChartOutlined /></template>
|
||||
销售统计
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-page-header>
|
||||
|
||||
<a-tabs v-model:activeKey="activeTab" @change="handleTabChange">
|
||||
<a-tab-pane key="orders" tab="订单列表">
|
||||
<!-- 统计卡片 -->
|
||||
<a-row :gutter="16" class="stats-row">
|
||||
<a-col :span="6">
|
||||
<a-card>
|
||||
<div class="search-container">
|
||||
<a-form layout="inline" :model="searchForm">
|
||||
<a-form-item label="订单号">
|
||||
<a-input v-model:value="searchForm.order_no" placeholder="输入订单号" allow-clear />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="状态">
|
||||
<a-select v-model:value="searchForm.status" placeholder="全部状态" style="width: 120px" allow-clear>
|
||||
<a-select-option value="pending">待支付</a-select-option>
|
||||
<a-select-option value="paid">已支付</a-select-option>
|
||||
<a-select-option value="shipped">已发货</a-select-option>
|
||||
<a-select-option value="completed">已完成</a-select-option>
|
||||
<a-select-option value="cancelled">已取消</a-select-option>
|
||||
<a-select-option value="refunded">已退款</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="下单时间">
|
||||
<a-range-picker v-model:value="searchForm.orderTime" :placeholder="['开始时间', '结束时间']" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
<a-button type="primary" @click="handleSearch">
|
||||
<template #icon><SearchOutlined /></template>
|
||||
搜索
|
||||
</a-button>
|
||||
<a-button style="margin-left: 8px" @click="handleReset">重置</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:columns="orderColumns"
|
||||
:data-source="orderList"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
:row-key="record => record.id"
|
||||
@change="handleTableChange"
|
||||
<a-statistic
|
||||
title="今日订单"
|
||||
:value="statistics.today_orders"
|
||||
:precision="0"
|
||||
suffix="单"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'order_no'">
|
||||
<a-typography-text copyable>{{ record.order_no }}</a-typography-text>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.key === 'amount'">¥{{ record.amount }}</template>
|
||||
|
||||
<template v-else-if="column.key === 'status'">
|
||||
<a-tag :color="getStatusColor(record.status)">{{ getStatusText(record.status) }}</a-tag>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.key === 'payment_method'">
|
||||
<span>{{ getPaymentMethodText(record.payment_method) }}</span>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.key === 'actions'">
|
||||
<a-space :size="8">
|
||||
<a-button size="small" @click="handleViewOrder(record)">
|
||||
<EyeOutlined />详情
|
||||
</a-button>
|
||||
|
||||
<template v-if="record.status === 'paid'">
|
||||
<a-button size="small" type="primary" @click="handleShip(record)">
|
||||
<CarOutlined />发货
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<template v-if="record.status === 'shipped'">
|
||||
<a-button size="small" type="primary" @click="handleComplete(record)">
|
||||
<CheckCircleOutlined />完成
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<template v-if="['pending', 'paid'].includes(record.status)">
|
||||
<a-button size="small" danger @click="handleCancel(record)">
|
||||
<CloseOutlined />取消
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<template v-if="record.status === 'paid'">
|
||||
<a-button size="small" danger @click="handleRefund(record)">
|
||||
<RollbackOutlined />退款
|
||||
</a-button>
|
||||
</template>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #prefix>
|
||||
<ShoppingCartOutlined />
|
||||
</template>
|
||||
</a-table>
|
||||
</a-statistic>
|
||||
</a-card>
|
||||
</a-tab-pane>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-card>
|
||||
<a-statistic
|
||||
title="今日销售额"
|
||||
:value="statistics.today_sales"
|
||||
:precision="2"
|
||||
suffix="元"
|
||||
:value-style="{ color: '#cf1322' }"
|
||||
>
|
||||
<template #prefix>
|
||||
<MoneyCollectOutlined />
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-card>
|
||||
<a-statistic
|
||||
title="本月订单"
|
||||
:value="statistics.month_orders"
|
||||
:precision="0"
|
||||
suffix="单"
|
||||
>
|
||||
<template #prefix>
|
||||
<CalendarOutlined />
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-card>
|
||||
<a-statistic
|
||||
title="本月销售额"
|
||||
:value="statistics.month_sales"
|
||||
:precision="2"
|
||||
suffix="元"
|
||||
:value-style="{ color: '#cf1322' }"
|
||||
>
|
||||
<template #prefix>
|
||||
<DollarOutlined />
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-tab-pane key="statistics" tab="销售统计">
|
||||
<a-card title="销售数据概览">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="6">
|
||||
<a-statistic title="今日订单" :value="statistics.today_orders" :precision="0" :value-style="{ color: '#3f8600' }">
|
||||
<template #prefix><ShoppingOutlined /></template>
|
||||
</a-statistic>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-statistic title="今日销售额" :value="statistics.today_sales" :precision="2" prefix="¥" :value-style="{ color: '#cf1322' }" />
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-statistic title="本月订单" :value="statistics.month_orders" :precision="0" :value-style="{ color: '#1890ff' }" />
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-statistic title="本月销售额" :value="statistics.month_sales" :precision="2" prefix="¥" :value-style="{ color: '#722ed1' }" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
<a-card>
|
||||
<!-- 搜索区域 -->
|
||||
<div class="search-container">
|
||||
<a-form layout="inline" :model="searchForm">
|
||||
<a-form-item label="订单号">
|
||||
<a-input
|
||||
v-model:value="searchForm.order_no"
|
||||
placeholder="请输入订单号"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="状态">
|
||||
<a-select
|
||||
v-model:value="searchForm.status"
|
||||
placeholder="全部状态"
|
||||
style="width: 120px"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option value="pending">待支付</a-select-option>
|
||||
<a-select-option value="paid">已支付</a-select-option>
|
||||
<a-select-option value="shipped">已发货</a-select-option>
|
||||
<a-select-option value="completed">已完成</a-select-option>
|
||||
<a-select-option value="cancelled">已取消</a-select-option>
|
||||
<a-select-option value="refunded">已退款</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="下单时间">
|
||||
<a-range-picker
|
||||
v-model:value="searchForm.orderTime"
|
||||
:placeholder="['开始时间', '结束时间']"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
<a-button type="primary" @click="handleSearch">
|
||||
<template #icon>
|
||||
<SearchOutlined />
|
||||
</template>
|
||||
搜索
|
||||
</a-button>
|
||||
<a-button style="margin-left: 8px" @click="handleReset">
|
||||
重置
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
|
||||
<a-card title="销售趋势" style="margin-top: 16px;">
|
||||
<div style="height: 300px;">
|
||||
<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: #999;">
|
||||
<BarChartOutlined style="font-size: 48px; margin-right: 12px;" />
|
||||
<span>销售趋势图表开发中</span>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<!-- 订单表格 -->
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="orderList"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
:row-key="record => record.id"
|
||||
@change="handleTableChange"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'amount'">
|
||||
<span class="amount-text">¥{{ record.amount }}</span>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.key === 'status'">
|
||||
<a-tag :color="getStatusColor(record.status)">
|
||||
{{ getStatusText(record.status) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.key === 'actions'">
|
||||
<a-space :size="8">
|
||||
<a-button size="small" @click="handleView(record)">
|
||||
<EyeOutlined />
|
||||
查看
|
||||
</a-button>
|
||||
|
||||
<template v-if="record.status === 'pending'">
|
||||
<a-button size="small" type="primary" @click="handlePay(record)">
|
||||
<PayCircleOutlined />
|
||||
支付
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<template v-else-if="record.status === 'paid'">
|
||||
<a-button size="small" type="primary" @click="handleShip(record)">
|
||||
<SendOutlined />
|
||||
发货
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<template v-else-if="record.status === 'shipped'">
|
||||
<a-button size="small" type="primary" @click="handleComplete(record)">
|
||||
<CheckCircleOutlined />
|
||||
完成
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<a-dropdown>
|
||||
<a-button size="small">
|
||||
更多
|
||||
<DownOutlined />
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item
|
||||
v-if="['pending', 'paid'].includes(record.status)"
|
||||
@click="handleCancel(record)"
|
||||
>
|
||||
<CloseCircleOutlined />
|
||||
取消
|
||||
</a-menu-item>
|
||||
<a-menu-item
|
||||
v-if="['paid', 'shipped', 'completed'].includes(record.status)"
|
||||
@click="handleRefund(record)"
|
||||
>
|
||||
<RedoOutlined />
|
||||
退款
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ref, reactive, onMounted, h } from 'vue'
|
||||
import { message, Modal } from 'ant-design-vue'
|
||||
import type { TableProps } from 'ant-design-vue'
|
||||
import {
|
||||
ReloadOutlined,
|
||||
ShoppingCartOutlined,
|
||||
MoneyCollectOutlined,
|
||||
CalendarOutlined,
|
||||
DollarOutlined,
|
||||
SearchOutlined,
|
||||
BarChartOutlined,
|
||||
ReloadOutlined,
|
||||
EyeOutlined,
|
||||
CarOutlined,
|
||||
PayCircleOutlined,
|
||||
SendOutlined,
|
||||
CheckCircleOutlined,
|
||||
CloseOutlined,
|
||||
RollbackOutlined,
|
||||
ShoppingOutlined
|
||||
CloseCircleOutlined,
|
||||
RedoOutlined,
|
||||
DownOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import { getOrders, updateOrderStatus, getOrderStatistics } from '@/api/order'
|
||||
import {
|
||||
getOrders,
|
||||
getOrder,
|
||||
getOrderStatistics,
|
||||
updateOrderStatus,
|
||||
shipOrder,
|
||||
completeOrder,
|
||||
cancelOrder,
|
||||
refundOrder
|
||||
} from '@/api/order'
|
||||
import type { Order, OrderStatistics } from '@/api/order'
|
||||
|
||||
interface SearchForm {
|
||||
@@ -165,8 +244,13 @@ interface SearchForm {
|
||||
orderTime: any[]
|
||||
}
|
||||
|
||||
const activeTab = ref('orders')
|
||||
const loading = ref(false)
|
||||
const statistics = ref<OrderStatistics>({
|
||||
today_orders: 0,
|
||||
today_sales: 0,
|
||||
month_orders: 0,
|
||||
month_sales: 0
|
||||
})
|
||||
|
||||
const searchForm = reactive<SearchForm>({
|
||||
order_no: '',
|
||||
@@ -174,13 +258,6 @@ const searchForm = reactive<SearchForm>({
|
||||
orderTime: []
|
||||
})
|
||||
|
||||
const statistics = reactive<OrderStatistics>({
|
||||
today_orders: 0,
|
||||
today_sales: 0,
|
||||
month_orders: 0,
|
||||
month_sales: 0
|
||||
})
|
||||
|
||||
const orderList = ref<Order[]>([])
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
@@ -191,25 +268,66 @@ const pagination = reactive({
|
||||
showTotal: (total: number) => `共 ${total} 条记录`
|
||||
})
|
||||
|
||||
const orderColumns = [
|
||||
{ title: '订单号', key: 'order_no', width: 160 },
|
||||
{ title: '用户', dataIndex: 'user_name', key: 'user_name', width: 100 },
|
||||
{ title: '联系电话', dataIndex: 'user_phone', key: 'user_phone', width: 120 },
|
||||
{ title: '金额', key: 'amount', width: 100, align: 'center' },
|
||||
{ title: '状态', key: 'status', width: 100, align: 'center' },
|
||||
{ title: '支付方式', key: 'payment_method', width: 100, align: 'center' },
|
||||
{ title: '下单时间', dataIndex: 'created_at', key: 'created_at', width: 150 },
|
||||
{ title: '操作', key: 'actions', width: 200, align: 'center' }
|
||||
const columns = [
|
||||
{
|
||||
title: '订单号',
|
||||
dataIndex: 'order_no',
|
||||
key: 'order_no',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '用户',
|
||||
dataIndex: 'user_name',
|
||||
key: 'user_name',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '联系电话',
|
||||
dataIndex: 'user_phone',
|
||||
key: 'user_phone',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '金额',
|
||||
key: 'amount',
|
||||
width: 100,
|
||||
align: 'right'
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'status',
|
||||
width: 100,
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '支付方式',
|
||||
dataIndex: 'payment_method',
|
||||
key: 'payment_method',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '下单时间',
|
||||
dataIndex: 'created_at',
|
||||
key: 'created_at',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 200,
|
||||
align: 'center'
|
||||
}
|
||||
]
|
||||
|
||||
// 状态映射
|
||||
const getStatusColor = (status: string) => {
|
||||
const colors = {
|
||||
pending: 'orange',
|
||||
paid: 'blue',
|
||||
shipped: 'green',
|
||||
completed: 'purple',
|
||||
shipped: 'purple',
|
||||
completed: 'green',
|
||||
cancelled: 'red',
|
||||
refunded: 'default'
|
||||
refunded: 'red'
|
||||
}
|
||||
return colors[status as keyof typeof colors] || 'default'
|
||||
}
|
||||
@@ -226,21 +344,24 @@ const getStatusText = (status: string) => {
|
||||
return texts[status as keyof typeof texts] || '未知'
|
||||
}
|
||||
|
||||
// 支付方式映射
|
||||
const getPaymentMethodText = (method: string) => {
|
||||
const texts = {
|
||||
wechat: '微信支付',
|
||||
alipay: '支付宝',
|
||||
bank: '银行卡',
|
||||
bank: '银行转账',
|
||||
balance: '余额支付'
|
||||
}
|
||||
return texts[method as keyof typeof texts] || '未知'
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
loadOrders()
|
||||
loadStatistics()
|
||||
})
|
||||
|
||||
// 方法
|
||||
const loadOrders = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
@@ -263,20 +384,12 @@ const loadOrders = async () => {
|
||||
const loadStatistics = async () => {
|
||||
try {
|
||||
const response = await getOrderStatistics()
|
||||
Object.assign(statistics, response.data)
|
||||
statistics.value = response.data
|
||||
} catch (error) {
|
||||
message.error('加载统计数据失败')
|
||||
}
|
||||
}
|
||||
|
||||
const handleTabChange = (key: string) => {
|
||||
if (key === 'orders') {
|
||||
loadOrders()
|
||||
} else if (key === 'statistics') {
|
||||
loadStatistics()
|
||||
}
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
pagination.current = 1
|
||||
loadOrders()
|
||||
@@ -293,11 +406,8 @@ const handleReset = () => {
|
||||
}
|
||||
|
||||
const handleRefresh = () => {
|
||||
if (activeTab.value === 'orders') {
|
||||
loadOrders()
|
||||
} else {
|
||||
loadStatistics()
|
||||
}
|
||||
loadOrders()
|
||||
loadStatistics()
|
||||
message.success('数据已刷新')
|
||||
}
|
||||
|
||||
@@ -307,17 +417,60 @@ const handleTableChange: TableProps['onChange'] = (pag) => {
|
||||
loadOrders()
|
||||
}
|
||||
|
||||
const handleViewOrder = (record: Order) => {
|
||||
message.info(`查看订单: ${record.order_no}`)
|
||||
const handleView = async (record: Order) => {
|
||||
try {
|
||||
const response = await getOrder(record.id)
|
||||
Modal.info({
|
||||
title: '订单详情',
|
||||
width: 600,
|
||||
content: h('div', { class: 'order-detail-modal' }, [
|
||||
h('a-descriptions', {
|
||||
column: 1,
|
||||
bordered: true
|
||||
}, [
|
||||
h('a-descriptions-item', { label: '订单号' }, response.data.order_no),
|
||||
h('a-descriptions-item', { label: '用户' }, response.data.user_name),
|
||||
h('a-descriptions-item', { label: '联系电话' }, response.data.user_phone),
|
||||
h('a-descriptions-item', { label: '订单金额' }, `¥${response.data.amount}`),
|
||||
h('a-descriptions-item', { label: '状态' }, [
|
||||
h('a-tag', { color: getStatusColor(response.data.status) }, getStatusText(response.data.status))
|
||||
]),
|
||||
h('a-descriptions-item', { label: '支付方式' }, getPaymentMethodText(response.data.payment_method)),
|
||||
h('a-descriptions-item', { label: '下单时间' }, response.data.created_at),
|
||||
h('a-descriptions-item', { label: '支付时间' }, response.data.paid_at || '-'),
|
||||
h('a-descriptions-item', { label: '发货时间' }, response.data.shipped_at || '-'),
|
||||
h('a-descriptions-item', { label: '完成时间' }, response.data.completed_at || '-')
|
||||
])
|
||||
])
|
||||
})
|
||||
} catch (error) {
|
||||
message.error('获取订单详情失败')
|
||||
}
|
||||
}
|
||||
|
||||
const handlePay = async (record: Order) => {
|
||||
Modal.confirm({
|
||||
title: '确认支付',
|
||||
content: `确定要标记订单 "${record.order_no}" 为已支付状态吗?`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
await updateOrderStatus(record.id, 'paid')
|
||||
message.success('订单状态已更新')
|
||||
loadOrders()
|
||||
} catch (error) {
|
||||
message.error('操作失败')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleShip = async (record: Order) => {
|
||||
Modal.confirm({
|
||||
title: '确认发货',
|
||||
content: `确定要发货订单 "${record.order_no}" 吗?`,
|
||||
content: `确定要标记订单 "${record.order_no}" 为已发货状态吗?`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
await updateOrderStatus(record.id, 'shipped')
|
||||
await shipOrder(record.id)
|
||||
message.success('订单已发货')
|
||||
loadOrders()
|
||||
} catch (error) {
|
||||
@@ -330,10 +483,10 @@ const handleShip = async (record: Order) => {
|
||||
const handleComplete = async (record: Order) => {
|
||||
Modal.confirm({
|
||||
title: '确认完成',
|
||||
content: `确定要完成订单 "${record.order_no}" 吗?`,
|
||||
content: `确定要标记订单 "${record.order_no}" 为已完成状态吗?`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
await updateOrderStatus(record.id, 'completed')
|
||||
await completeOrder(record.id)
|
||||
message.success('订单已完成')
|
||||
loadOrders()
|
||||
} catch (error) {
|
||||
@@ -349,7 +502,7 @@ const handleCancel = async (record: Order) => {
|
||||
content: `确定要取消订单 "${record.order_no}" 吗?`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
await updateOrderStatus(record.id, 'cancelled')
|
||||
await cancelOrder(record.id)
|
||||
message.success('订单已取消')
|
||||
loadOrders()
|
||||
} catch (error) {
|
||||
@@ -362,11 +515,11 @@ const handleCancel = async (record: Order) => {
|
||||
const handleRefund = async (record: Order) => {
|
||||
Modal.confirm({
|
||||
title: '确认退款',
|
||||
content: `确定要退款订单 "${record.order_no}" 吗?退款金额: ¥${record.amount}`,
|
||||
content: `确定要为订单 "${record.order_no}" 办理退款吗?`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
await updateOrderStatus(record.id, 'refunded')
|
||||
message.success('退款申请已提交')
|
||||
await refundOrder(record.id)
|
||||
message.success('订单已退款')
|
||||
loadOrders()
|
||||
} catch (error) {
|
||||
message.error('操作失败')
|
||||
@@ -374,15 +527,14 @@ const handleRefund = async (record: Order) => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const showStats = () => {
|
||||
activeTab.value = 'statistics'
|
||||
loadStatistics()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.order-management {
|
||||
.stats-row {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
margin-bottom: 16px;
|
||||
padding: 16px;
|
||||
@@ -393,5 +545,15 @@ const showStats = () => {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.amount-text {
|
||||
font-weight: 500;
|
||||
color: #cf1322;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-table-thead > tr > th) {
|
||||
background: #fafafa;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@@ -236,8 +236,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { message, Modal, type FormInstance } from 'ant-design-vue'
|
||||
import { ref, reactive, computed, onMounted, h } from 'vue'
|
||||
import { message, Modal, type FormInstance, TableProps } from 'ant-design-vue'
|
||||
import {
|
||||
UserAddOutlined,
|
||||
SearchOutlined,
|
||||
@@ -248,10 +248,10 @@ import {
|
||||
StopOutlined,
|
||||
PlayCircleOutlined,
|
||||
WarningOutlined,
|
||||
DownOutlined
|
||||
DownOutlined,
|
||||
type TableProps
|
||||
} from '@ant-design/icons-vue'
|
||||
import type { TableProps } from 'ant-design-vue'
|
||||
import { getUsers, updateUser, createUser, deleteUser } from '@/api/user'
|
||||
import { getUsers, getUser, updateUser, createUser, deleteUser } from '@/api/user'
|
||||
import type { User } from '@/api/user'
|
||||
|
||||
interface SearchForm {
|
||||
@@ -473,9 +473,44 @@ const showCreateModal = () => {
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
const handleView = (record: User) => {
|
||||
// TODO: 跳转到用户详情页
|
||||
message.info(`查看用户: ${record.nickname}`)
|
||||
const handleView = async (record: User) => {
|
||||
try {
|
||||
const response = await getUser(record.id)
|
||||
Modal.info({
|
||||
title: '用户详情',
|
||||
width: 600,
|
||||
content: h('div', { class: 'user-detail-modal' }, [
|
||||
h('a-descriptions', {
|
||||
column: 1,
|
||||
bordered: true
|
||||
}, [
|
||||
h('a-descriptions-item', { label: '用户ID' }, response.data.id),
|
||||
h('a-descriptions-item', { label: '用户名' }, response.data.username),
|
||||
h('a-descriptions-item', { label: '昵称' }, response.data.nickname),
|
||||
h('a-descriptions-item', { label: '头像' }, [
|
||||
h('a-avatar', {
|
||||
src: response.data.avatar,
|
||||
size: 64,
|
||||
shape: 'square'
|
||||
})
|
||||
]),
|
||||
h('a-descriptions-item', { label: '性别' }, response.data.gender),
|
||||
h('a-descriptions-item', { label: '生日' }, response.data.birthday),
|
||||
h('a-descriptions-item', { label: '手机号' }, response.data.phone),
|
||||
h('a-descriptions-item', { label: '邮箱' }, response.data.email),
|
||||
h('a-descriptions-item', { label: '状态' }, [
|
||||
h('a-tag', { color: getStatusColor(response.data.status) }, getStatusText(response.data.status))
|
||||
]),
|
||||
h('a-descriptions-item', { label: '等级' }, `Lv.${response.data.level}`),
|
||||
h('a-descriptions-item', { label: '积分' }, response.data.points),
|
||||
h('a-descriptions-item', { label: '注册时间' }, response.data.created_at),
|
||||
h('a-descriptions-item', { label: '更新时间' }, response.data.updated_at)
|
||||
])
|
||||
])
|
||||
})
|
||||
} catch (error) {
|
||||
message.error('获取用户详情失败')
|
||||
}
|
||||
}
|
||||
|
||||
const handleEdit = (record: User) => {
|
||||
|
||||
@@ -1,127 +1,80 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
interface AppState {
|
||||
// 应用配置
|
||||
name: string
|
||||
version: string
|
||||
// 用户信息
|
||||
user: any
|
||||
// 系统状态
|
||||
loading: boolean
|
||||
error: string | null
|
||||
// 功能开关
|
||||
features: {
|
||||
analytics: boolean
|
||||
debug: boolean
|
||||
}
|
||||
}
|
||||
import { reactive } from 'vue'
|
||||
import { authAPI } from '@/api'
|
||||
import type { Admin } from '@/types/user'
|
||||
|
||||
export const useAppStore = defineStore('app', () => {
|
||||
// 状态
|
||||
const state = ref<AppState>({
|
||||
name: import.meta.env.VITE_APP_NAME || '结伴客后台管理系统',
|
||||
version: import.meta.env.VITE_APP_VERSION || '1.0.0',
|
||||
user: null,
|
||||
const state = reactive({
|
||||
user: null as Admin | null,
|
||||
loading: false,
|
||||
error: null,
|
||||
features: {
|
||||
analytics: import.meta.env.VITE_FEATURE_ANALYTICS === 'true',
|
||||
debug: import.meta.env.VITE_FEATURE_DEBUG === 'true'
|
||||
}
|
||||
initialized: false
|
||||
})
|
||||
|
||||
// Getters
|
||||
const isAuthenticated = () => !!state.value.user
|
||||
const isLoading = () => state.value.loading
|
||||
const hasError = () => state.value.error !== null
|
||||
|
||||
// Actions
|
||||
const initializeApp = async () => {
|
||||
state.value.loading = true
|
||||
try {
|
||||
// 初始化应用配置
|
||||
console.log('🚀 初始化应用:', state.value.name, state.value.version)
|
||||
|
||||
// 检查用户登录状态
|
||||
await checkAuthStatus()
|
||||
|
||||
// 初始化分析工具(如果启用)
|
||||
if (state.value.features.analytics) {
|
||||
initAnalytics()
|
||||
}
|
||||
|
||||
state.value.error = null
|
||||
} catch (error) {
|
||||
state.value.error = error instanceof Error ? error.message : '初始化失败'
|
||||
console.error('❌ 应用初始化失败:', error)
|
||||
} finally {
|
||||
state.value.loading = false
|
||||
}
|
||||
// 设置用户信息
|
||||
const setUser = (user: Admin) => {
|
||||
state.user = user
|
||||
}
|
||||
|
||||
const checkAuthStatus = async () => {
|
||||
// 清除用户信息
|
||||
const clearUser = () => {
|
||||
state.user = null
|
||||
localStorage.removeItem('admin_token')
|
||||
}
|
||||
|
||||
// 初始化应用
|
||||
const initialize = async () => {
|
||||
if (state.initialized) return
|
||||
|
||||
state.loading = true
|
||||
try {
|
||||
// 检查本地存储的token
|
||||
const token = localStorage.getItem('admin_token')
|
||||
if (token) {
|
||||
// TODO: 验证token有效性
|
||||
// const user = await authAPI.verifyToken(token)
|
||||
// state.value.user = user
|
||||
console.log('✅ 用户已登录')
|
||||
} else {
|
||||
console.log('ℹ️ 用户未登录')
|
||||
// 获取用户信息
|
||||
const response = await authAPI.getCurrentUser()
|
||||
// 修复数据结构访问问题
|
||||
state.user = response.data.admin
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('⚠️ 认证状态检查失败:', error)
|
||||
// 清除无效的token
|
||||
localStorage.removeItem('admin_token')
|
||||
console.error('初始化失败:', error)
|
||||
clearUser()
|
||||
} finally {
|
||||
state.loading = false
|
||||
state.initialized = true
|
||||
}
|
||||
}
|
||||
|
||||
const setUser = (user: any) => {
|
||||
state.value.user = user
|
||||
}
|
||||
|
||||
const setLoading = (loading: boolean) => {
|
||||
state.value.loading = loading
|
||||
}
|
||||
|
||||
const setError = (error: string | null) => {
|
||||
state.value.error = error
|
||||
}
|
||||
|
||||
const clearError = () => {
|
||||
state.value.error = null
|
||||
// 登录
|
||||
const login = async (credentials: { username: string; password: string }) => {
|
||||
state.loading = true
|
||||
try {
|
||||
const response = await authAPI.login(credentials)
|
||||
|
||||
// 保存token
|
||||
localStorage.setItem('admin_token', response.data.token)
|
||||
|
||||
// 设置用户信息
|
||||
state.user = response.data.admin
|
||||
|
||||
return response
|
||||
} finally {
|
||||
state.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
const logout = () => {
|
||||
state.value.user = null
|
||||
localStorage.removeItem('admin_token')
|
||||
// TODO: 调用退出接口
|
||||
}
|
||||
|
||||
// 私有方法
|
||||
const initAnalytics = () => {
|
||||
if (state.value.features.analytics) {
|
||||
console.log('📊 初始化分析工具')
|
||||
// TODO: 集成分析工具
|
||||
}
|
||||
clearUser()
|
||||
}
|
||||
|
||||
return {
|
||||
// 状态
|
||||
state,
|
||||
// Getters
|
||||
isAuthenticated,
|
||||
isLoading,
|
||||
hasError,
|
||||
// Actions
|
||||
initializeApp,
|
||||
setUser,
|
||||
setLoading,
|
||||
setError,
|
||||
clearError,
|
||||
clearUser,
|
||||
initialize,
|
||||
login,
|
||||
logout
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
export type { Admin }
|
||||
@@ -1,8 +1,10 @@
|
||||
export { useAppStore } from './app'
|
||||
// 导出其他store模块
|
||||
// export { useUserStore } from './user'
|
||||
// export { useTravelStore } from './travel'
|
||||
// export { useAnimalStore } from './animal'
|
||||
// export { useMerchantStore } from './merchant'
|
||||
// export { useOrderStore } from './order'
|
||||
// export { usePromotionStore } from './promotion'
|
||||
// 导出其他store模块
|
||||
export { useUserStore } from './modules/user.js'
|
||||
export { useMerchantStore } from './modules/merchant.js'
|
||||
export { useOrderStore } from './modules/order.js'
|
||||
export { useAnimalStore } from './modules/animal.js'
|
||||
export { useActivityStore } from './modules/activity.js'
|
||||
export { useContentStore } from './modules/content.js'
|
||||
export { useStatisticsStore } from './modules/statistics.js'
|
||||
export { usePermissionStore } from './modules/permission.js'
|
||||
249
admin-system/src/stores/modules/activity.ts
Normal file
249
admin-system/src/stores/modules/activity.ts
Normal file
@@ -0,0 +1,249 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import type { Activity, ActivityQueryParams, ActivityStatus, ActivityType, ActivityStatistics } from '../../types/activity'
|
||||
|
||||
interface ActivityState {
|
||||
activities: Activity[]
|
||||
currentActivity: Activity | null
|
||||
loading: boolean
|
||||
totalCount: number
|
||||
queryParams: ActivityQueryParams
|
||||
}
|
||||
|
||||
export const useActivityStore = defineStore('activity', () => {
|
||||
// 状态
|
||||
const activities = ref<Activity[]>([])
|
||||
const currentActivity = ref<Activity | null>(null)
|
||||
const loading = ref(false)
|
||||
const totalCount = ref(0)
|
||||
const queryParams = ref<ActivityQueryParams>({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: '',
|
||||
status: undefined,
|
||||
activityType: undefined,
|
||||
organizerId: undefined,
|
||||
startDate: '',
|
||||
endDate: ''
|
||||
})
|
||||
|
||||
// 获取活动列表
|
||||
const fetchActivities = async (params?: ActivityQueryParams) => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 模拟数据
|
||||
const mockActivities: Activity[] = [
|
||||
{
|
||||
id: 1,
|
||||
title: '周末郊游',
|
||||
activityType: 'travel',
|
||||
description: '周末一起去郊游,享受大自然',
|
||||
organizerId: 101,
|
||||
organizerName: '张组织者',
|
||||
location: '北京市朝阳公园',
|
||||
startTime: '2024-01-20 09:00:00',
|
||||
endTime: '2024-01-20 17:00:00',
|
||||
maxParticipants: 20,
|
||||
currentParticipants: 15,
|
||||
status: 'active',
|
||||
price: 50.00,
|
||||
createTime: '2024-01-15 14:30:00',
|
||||
images: ['/images/activity1.jpg'],
|
||||
tags: ['郊游', '户外', '社交']
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '电影之夜',
|
||||
activityType: 'movie',
|
||||
description: '一起观看最新上映的电影',
|
||||
organizerId: 102,
|
||||
organizerName: '李组织者',
|
||||
location: '北京市万达影城',
|
||||
startTime: '2024-01-22 19:00:00',
|
||||
endTime: '2024-01-22 22:00:00',
|
||||
maxParticipants: 10,
|
||||
currentParticipants: 8,
|
||||
status: 'active',
|
||||
price: 35.00,
|
||||
createTime: '2024-01-16 10:15:00',
|
||||
images: ['/images/activity2.jpg'],
|
||||
tags: ['电影', '娱乐', '社交']
|
||||
}
|
||||
]
|
||||
|
||||
activities.value = mockActivities
|
||||
totalCount.value = mockActivities.length
|
||||
|
||||
if (params) {
|
||||
queryParams.value = { ...queryParams.value, ...params }
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取活动列表失败:', error)
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取活动详情
|
||||
const fetchActivityDetail = async (activityId: number) => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 模拟数据
|
||||
const mockActivity: Activity = {
|
||||
id: activityId,
|
||||
title: `活动${activityId}`,
|
||||
activityType: 'travel',
|
||||
description: '活动描述',
|
||||
organizerId: 100 + activityId,
|
||||
organizerName: '组织者',
|
||||
location: '地点',
|
||||
startTime: '2024-01-20 10:00:00',
|
||||
endTime: '2024-01-20 18:00:00',
|
||||
maxParticipants: 20,
|
||||
currentParticipants: 10,
|
||||
status: 'active',
|
||||
price: 50.00,
|
||||
createTime: '2024-01-15 14:30:00',
|
||||
images: ['/images/activity.jpg'],
|
||||
tags: ['标签1', '标签2']
|
||||
}
|
||||
|
||||
currentActivity.value = mockActivity
|
||||
return mockActivity
|
||||
} catch (error) {
|
||||
console.error('获取活动详情失败:', error)
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 更新活动状态
|
||||
const updateActivityStatus = async (activityId: number, status: ActivityStatus) => {
|
||||
try {
|
||||
// 模拟状态更新
|
||||
const activity = activities.value.find(a => a.id === activityId)
|
||||
if (activity) {
|
||||
activity.status = status
|
||||
}
|
||||
|
||||
if (currentActivity.value && currentActivity.value.id === activityId) {
|
||||
currentActivity.value.status = status
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('更新活动状态失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 审核活动
|
||||
const auditActivity = async (activityId: number, auditStatus: ActivityStatus, auditRemark?: string) => {
|
||||
try {
|
||||
// 模拟审核操作
|
||||
const activity = activities.value.find(a => a.id === activityId)
|
||||
if (activity) {
|
||||
activity.status = auditStatus
|
||||
activity.auditRemark = auditRemark
|
||||
activity.auditTime = new Date().toISOString()
|
||||
}
|
||||
|
||||
if (currentActivity.value && currentActivity.value.id === activityId) {
|
||||
currentActivity.value.status = auditStatus
|
||||
currentActivity.value.auditRemark = auditRemark
|
||||
currentActivity.value.auditTime = new Date().toISOString()
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('审核活动失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索活动
|
||||
const searchActivities = async (keyword: string) => {
|
||||
queryParams.value.keyword = keyword
|
||||
return fetchActivities(queryParams.value)
|
||||
}
|
||||
|
||||
// 重置查询参数
|
||||
const resetQueryParams = () => {
|
||||
queryParams.value = {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: '',
|
||||
status: undefined,
|
||||
activityType: undefined,
|
||||
organizerId: undefined,
|
||||
startDate: '',
|
||||
endDate: ''
|
||||
}
|
||||
}
|
||||
|
||||
// 获取活动统计
|
||||
const getActivityStatistics = async () => {
|
||||
try {
|
||||
// 模拟统计数据
|
||||
return {
|
||||
totalActivities: 89,
|
||||
activeActivities: 67,
|
||||
completedActivities: 22,
|
||||
cancelledActivities: 5,
|
||||
totalParticipants: 456,
|
||||
totalRevenue: 12800.50,
|
||||
averageParticipation: 5.12,
|
||||
activityTypeDistribution: {
|
||||
travel: 25,
|
||||
movie: 18,
|
||||
dinner: 15,
|
||||
game: 12,
|
||||
other: 19
|
||||
},
|
||||
statusDistribution: {
|
||||
pending: 8,
|
||||
active: 67,
|
||||
completed: 22,
|
||||
cancelled: 5
|
||||
},
|
||||
popularActivities: activities.value.slice(0, 5)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取活动统计失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 导出活动数据
|
||||
const exportActivities = async () => {
|
||||
try {
|
||||
// 模拟导出功能
|
||||
const csvContent = activities.value.map(activity =>
|
||||
`${activity.id},${activity.title},${activity.organizerName},${activity.activityType},${activity.status},${activity.currentParticipants}`
|
||||
).join('\n')
|
||||
|
||||
return csvContent
|
||||
} catch (error) {
|
||||
console.error('导出活动数据失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
activities,
|
||||
currentActivity,
|
||||
loading,
|
||||
totalCount,
|
||||
queryParams,
|
||||
fetchActivities,
|
||||
fetchActivityDetail,
|
||||
updateActivityStatus,
|
||||
auditActivity,
|
||||
searchActivities,
|
||||
resetQueryParams,
|
||||
getActivityStatistics,
|
||||
exportActivities
|
||||
}
|
||||
})
|
||||
245
admin-system/src/stores/modules/animal.ts
Normal file
245
admin-system/src/stores/modules/animal.ts
Normal file
@@ -0,0 +1,245 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import type { Animal, AnimalQueryParams, AnimalStatus, AnimalType, MedicalRecord, AnimalAdoptionRecord } from '../../types/animal'
|
||||
|
||||
interface AnimalState {
|
||||
animals: Animal[]
|
||||
currentAnimal: Animal | null
|
||||
loading: boolean
|
||||
totalCount: number
|
||||
queryParams: AnimalQueryParams
|
||||
}
|
||||
|
||||
export const useAnimalStore = defineStore('animal', () => {
|
||||
// 状态
|
||||
const animals = ref<Animal[]>([])
|
||||
const currentAnimal = ref<Animal | null>(null)
|
||||
const loading = ref(false)
|
||||
const totalCount = ref(0)
|
||||
const queryParams = ref<AnimalQueryParams>({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: '',
|
||||
status: undefined,
|
||||
animalType: undefined,
|
||||
farmId: undefined,
|
||||
startDate: '',
|
||||
endDate: ''
|
||||
})
|
||||
|
||||
// 获取动物列表
|
||||
const fetchAnimals = async (params?: AnimalQueryParams) => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 模拟数据
|
||||
const mockAnimals: Animal[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: '小白',
|
||||
animalType: 'sheep',
|
||||
breed: '绵羊',
|
||||
age: 2,
|
||||
gender: 'male',
|
||||
status: 'available',
|
||||
farmId: 1,
|
||||
farmName: '快乐农场',
|
||||
price: 299.00,
|
||||
description: '温顺可爱的小绵羊',
|
||||
images: ['/images/sheep1.jpg'],
|
||||
healthStatus: 'excellent',
|
||||
adoptionCount: 5,
|
||||
createTime: '2024-01-10 09:00:00',
|
||||
features: ['温顺', '亲人', '爱吃草']
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '小花',
|
||||
animalType: 'cow',
|
||||
breed: '奶牛',
|
||||
age: 3,
|
||||
gender: 'female',
|
||||
status: 'adopted',
|
||||
farmId: 1,
|
||||
farmName: '快乐农场',
|
||||
price: 499.00,
|
||||
description: '产奶量高的奶牛',
|
||||
images: ['/images/cow1.jpg'],
|
||||
healthStatus: 'good',
|
||||
adoptionCount: 3,
|
||||
createTime: '2024-01-12 14:30:00',
|
||||
features: ['产奶量高', '健康', '温顺']
|
||||
}
|
||||
]
|
||||
|
||||
animals.value = mockAnimals
|
||||
totalCount.value = mockAnimals.length
|
||||
|
||||
if (params) {
|
||||
queryParams.value = { ...queryParams.value, ...params }
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取动物列表失败:', error)
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取动物详情
|
||||
const fetchAnimalDetail = async (animalId: number) => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 模拟数据
|
||||
const mockAnimal: Animal = {
|
||||
id: animalId,
|
||||
name: `动物${animalId}`,
|
||||
animalType: 'sheep',
|
||||
breed: '品种',
|
||||
age: 2,
|
||||
gender: 'male',
|
||||
status: 'available',
|
||||
farmId: 1,
|
||||
farmName: '测试农场',
|
||||
price: 199.00,
|
||||
description: '动物描述',
|
||||
images: ['/images/animal.jpg'],
|
||||
healthStatus: 'excellent',
|
||||
adoptionCount: 0,
|
||||
createTime: '2024-01-15 10:00:00',
|
||||
features: ['特征1', '特征2']
|
||||
}
|
||||
|
||||
currentAnimal.value = mockAnimal
|
||||
return mockAnimal
|
||||
} catch (error) {
|
||||
console.error('获取动物详情失败:', error)
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 更新动物状态
|
||||
const updateAnimalStatus = async (animalId: number, status: AnimalStatus) => {
|
||||
try {
|
||||
// 模拟状态更新
|
||||
const animal = animals.value.find((a: Animal) => a.id === animalId)
|
||||
if (animal) {
|
||||
animal.status = status
|
||||
}
|
||||
|
||||
if (currentAnimal.value && currentAnimal.value.id === animalId) {
|
||||
currentAnimal.value.status = status
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('更新动物状态失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 添加新动物
|
||||
const addAnimal = async (animalData: Omit<Animal, 'id' | 'createTime'>) => {
|
||||
try {
|
||||
// 模拟添加操作
|
||||
const newAnimal: Animal = {
|
||||
...animalData,
|
||||
id: Date.now(),
|
||||
createTime: new Date().toISOString()
|
||||
}
|
||||
|
||||
animals.value.unshift(newAnimal)
|
||||
totalCount.value += 1
|
||||
|
||||
return newAnimal
|
||||
} catch (error) {
|
||||
console.error('添加动物失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 更新动物信息
|
||||
const updateAnimal = async (animalId: number, updateData: Partial<Animal>) => {
|
||||
try {
|
||||
// 模拟更新操作
|
||||
const animal = animals.value.find((a: Animal) => a.id === animalId)
|
||||
if (animal) {
|
||||
Object.assign(animal, updateData)
|
||||
}
|
||||
|
||||
if (currentAnimal.value && currentAnimal.value.id === animalId) {
|
||||
Object.assign(currentAnimal.value, updateData)
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('更新动物信息失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索动物
|
||||
const searchAnimals = async (keyword: string) => {
|
||||
queryParams.value.keyword = keyword
|
||||
return fetchAnimals(queryParams.value)
|
||||
}
|
||||
|
||||
// 重置查询参数
|
||||
const resetQueryParams = () => {
|
||||
queryParams.value = {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: '',
|
||||
status: undefined,
|
||||
animalType: undefined,
|
||||
farmId: undefined,
|
||||
startDate: '',
|
||||
endDate: ''
|
||||
}
|
||||
}
|
||||
|
||||
// 获取动物统计
|
||||
const getAnimalStatistics = async () => {
|
||||
try {
|
||||
// 模拟统计数据
|
||||
return {
|
||||
totalAnimals: 156,
|
||||
availableAnimals: 89,
|
||||
adoptedAnimals: 67,
|
||||
totalAdoptions: 245,
|
||||
averageAdoptionPrice: 256.80,
|
||||
animalTypeDistribution: {
|
||||
sheep: 45,
|
||||
cow: 32,
|
||||
pig: 28,
|
||||
chicken: 51
|
||||
},
|
||||
statusDistribution: {
|
||||
available: 89,
|
||||
adopted: 67
|
||||
},
|
||||
topAnimals: animals.value.slice(0, 5)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取动物统计失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
animals,
|
||||
currentAnimal,
|
||||
loading,
|
||||
totalCount,
|
||||
queryParams,
|
||||
fetchAnimals,
|
||||
fetchAnimalDetail,
|
||||
updateAnimalStatus,
|
||||
addAnimal,
|
||||
updateAnimal,
|
||||
searchAnimals,
|
||||
resetQueryParams,
|
||||
getAnimalStatistics
|
||||
}
|
||||
})
|
||||
439
admin-system/src/stores/modules/content.ts
Normal file
439
admin-system/src/stores/modules/content.ts
Normal file
@@ -0,0 +1,439 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import type {
|
||||
Content,
|
||||
ContentQueryParams,
|
||||
ContentStatus,
|
||||
ContentType,
|
||||
ContentStatistics,
|
||||
ContentAuditStatus,
|
||||
ContentCategory
|
||||
} from '../../types/content'
|
||||
|
||||
// 模拟数据 - 内容列表
|
||||
const mockContents: Content[] = [
|
||||
{
|
||||
id: 1,
|
||||
title: '西藏自驾游攻略分享',
|
||||
content: '这次西藏之行非常难忘,沿途风景美不胜收...',
|
||||
contentType: 'article',
|
||||
authorId: 101,
|
||||
authorName: '旅行达人小王',
|
||||
publishTime: '2024-01-15 10:30:00',
|
||||
status: 'published',
|
||||
viewCount: 1250,
|
||||
likeCount: 89,
|
||||
commentCount: 45,
|
||||
shareCount: 23,
|
||||
auditStatus: 'approved',
|
||||
category: 'travel',
|
||||
tags: ['西藏', '自驾', '攻略'],
|
||||
images: ['/images/tibet1.jpg', '/images/tibet2.jpg'],
|
||||
isPublic: true,
|
||||
allowComments: true,
|
||||
allowSharing: true,
|
||||
editCount: 2
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '认养小羊的快乐时光',
|
||||
content: '今天去农场看望了我认养的小羊,它长得真快...',
|
||||
contentType: 'post',
|
||||
authorId: 102,
|
||||
authorName: '爱心人士小李',
|
||||
publishTime: '2024-01-14 15:20:00',
|
||||
status: 'published',
|
||||
viewCount: 890,
|
||||
likeCount: 67,
|
||||
commentCount: 32,
|
||||
shareCount: 18,
|
||||
auditStatus: 'approved',
|
||||
category: 'animal',
|
||||
tags: ['认养', '小羊', '农场'],
|
||||
images: ['/images/sheep1.jpg'],
|
||||
isPublic: true,
|
||||
allowComments: true,
|
||||
allowSharing: true,
|
||||
editCount: 1
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '周末电影聚会邀请',
|
||||
content: '本周末组织电影观看活动,欢迎喜欢电影的朋友参加...',
|
||||
contentType: 'activity',
|
||||
authorId: 103,
|
||||
authorName: '电影爱好者小张',
|
||||
publishTime: '2024-01-16 09:00:00',
|
||||
status: 'draft',
|
||||
viewCount: 120,
|
||||
likeCount: 15,
|
||||
commentCount: 8,
|
||||
shareCount: 5,
|
||||
auditStatus: 'pending',
|
||||
category: 'entertainment',
|
||||
tags: ['电影', '聚会', '周末'],
|
||||
isPublic: true,
|
||||
allowComments: true,
|
||||
allowSharing: true,
|
||||
editCount: 3
|
||||
}
|
||||
]
|
||||
|
||||
// 模拟数据 - 内容统计
|
||||
const mockContentStats: ContentStatistics = {
|
||||
totalContents: 1560,
|
||||
publishedContents: 1200,
|
||||
draftContents: 200,
|
||||
deletedContents: 160,
|
||||
totalViews: 125000,
|
||||
totalLikes: 8900,
|
||||
totalComments: 4500,
|
||||
totalShares: 2300,
|
||||
averageEngagementRate: 12.5,
|
||||
contentTypeDistribution: {
|
||||
article: 600,
|
||||
post: 800,
|
||||
activity: 120,
|
||||
comment: 40,
|
||||
review: 200
|
||||
},
|
||||
categoryDistribution: {
|
||||
travel: 450,
|
||||
animal: 380,
|
||||
entertainment: 280,
|
||||
food: 200,
|
||||
sports: 150,
|
||||
study: 100,
|
||||
other: 0
|
||||
},
|
||||
dailyGrowth: 25,
|
||||
topContents: mockContents.slice(0, 3),
|
||||
authorDistribution: {
|
||||
totalAuthors: 350,
|
||||
activeAuthors: 280,
|
||||
newAuthorsThisMonth: 45,
|
||||
topAuthors: [
|
||||
{ authorId: 101, authorName: '旅行达人小王', contentCount: 45, totalViews: 12000 },
|
||||
{ authorId: 102, authorName: '爱心人士小李', contentCount: 38, totalViews: 8900 },
|
||||
{ authorId: 103, authorName: '电影爱好者小张', contentCount: 32, totalViews: 7600 }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export const useContentStore = defineStore('content', () => {
|
||||
// 状态
|
||||
const contents = ref<Content[]>(mockContents)
|
||||
const currentContent = ref<Content | null>(null)
|
||||
const contentStats = ref<ContentStatistics>(mockContentStats)
|
||||
const loading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
// 获取内容列表
|
||||
const fetchContents = async (params: ContentQueryParams) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
// 模拟API调用
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
|
||||
// 简单的过滤和分页逻辑
|
||||
let filteredContents = mockContents
|
||||
|
||||
if (params.keyword) {
|
||||
filteredContents = filteredContents.filter(content =>
|
||||
content.title.includes(params.keyword!) ||
|
||||
content.content.includes(params.keyword!)
|
||||
)
|
||||
}
|
||||
|
||||
if (params.status) {
|
||||
filteredContents = filteredContents.filter(content => content.status === params.status)
|
||||
}
|
||||
|
||||
if (params.contentType) {
|
||||
filteredContents = filteredContents.filter(content => content.contentType === params.contentType)
|
||||
}
|
||||
|
||||
if (params.auditStatus) {
|
||||
filteredContents = filteredContents.filter(content => content.auditStatus === params.auditStatus)
|
||||
}
|
||||
|
||||
if (params.authorId) {
|
||||
filteredContents = filteredContents.filter(content => content.authorId === params.authorId)
|
||||
}
|
||||
|
||||
if (params.startTime && params.endTime) {
|
||||
filteredContents = filteredContents.filter(content =>
|
||||
content.publishTime >= params.startTime! &&
|
||||
content.publishTime <= params.endTime!
|
||||
)
|
||||
}
|
||||
|
||||
// 分页
|
||||
const start = (params.page - 1) * params.pageSize
|
||||
const paginatedContents = filteredContents.slice(start, start + params.pageSize)
|
||||
|
||||
contents.value = paginatedContents
|
||||
|
||||
return {
|
||||
contents: paginatedContents,
|
||||
total: filteredContents.length,
|
||||
page: params.page,
|
||||
pageSize: params.pageSize
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = '获取内容列表失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取内容详情
|
||||
const fetchContentDetail = async (id: number) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 300))
|
||||
|
||||
const content = mockContents.find((c: Content) => c.id === id)
|
||||
if (!content) {
|
||||
throw new Error('内容不存在')
|
||||
}
|
||||
|
||||
currentContent.value = content
|
||||
return content
|
||||
} catch (err) {
|
||||
error.value = '获取内容详情失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 审核内容
|
||||
const auditContent = async (id: number, auditStatus: 'approved' | 'rejected', remark?: string) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
|
||||
const contentIndex = mockContents.findIndex((c: Content) => c.id === id)
|
||||
if (contentIndex === -1) {
|
||||
throw new Error('内容不存在')
|
||||
}
|
||||
|
||||
mockContents[contentIndex].auditStatus = auditStatus
|
||||
if (remark) {
|
||||
mockContents[contentIndex].auditRemark = remark
|
||||
}
|
||||
mockContents[contentIndex].auditTime = new Date().toISOString()
|
||||
|
||||
// 更新本地状态
|
||||
if (currentContent.value?.id === id) {
|
||||
currentContent.value.auditStatus = auditStatus
|
||||
currentContent.value.auditRemark = remark
|
||||
currentContent.value.auditTime = new Date().toISOString()
|
||||
}
|
||||
|
||||
return mockContents[contentIndex]
|
||||
} catch (err) {
|
||||
error.value = '审核内容失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 更新内容状态
|
||||
const updateContentStatus = async (id: number, status: 'published' | 'draft' | 'deleted') => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 300))
|
||||
|
||||
const contentIndex = mockContents.findIndex((c: Content) => c.id === id)
|
||||
if (contentIndex === -1) {
|
||||
throw new Error('内容不存在')
|
||||
}
|
||||
|
||||
mockContents[contentIndex].status = status
|
||||
|
||||
// 更新本地状态
|
||||
if (currentContent.value?.id === id) {
|
||||
currentContent.value.status = status
|
||||
}
|
||||
|
||||
return mockContents[contentIndex]
|
||||
} catch (err) {
|
||||
error.value = '更新内容状态失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 删除内容
|
||||
const deleteContent = async (id: number) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 300))
|
||||
|
||||
const contentIndex = mockContents.findIndex((c: Content) => c.id === id)
|
||||
if (contentIndex === -1) {
|
||||
throw new Error('内容不存在')
|
||||
}
|
||||
|
||||
mockContents.splice(contentIndex, 1)
|
||||
|
||||
// 清除当前内容
|
||||
if (currentContent.value?.id === id) {
|
||||
currentContent.value = null
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (err) {
|
||||
error.value = '删除内容失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取内容统计
|
||||
const fetchContentStatistics = async () => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 800))
|
||||
|
||||
// 模拟实时数据更新
|
||||
const updatedStats = {
|
||||
...mockContentStats,
|
||||
totalContents: mockContents.length,
|
||||
publishedContents: mockContents.filter(c => c.status === 'published').length,
|
||||
draftContents: mockContents.filter(c => c.status === 'draft').length,
|
||||
totalViews: mockContents.reduce((sum, c) => sum + c.viewCount, 0),
|
||||
totalLikes: mockContents.reduce((sum, c) => sum + c.likeCount, 0),
|
||||
totalComments: mockContents.reduce((sum, c) => sum + c.commentCount, 0),
|
||||
totalShares: mockContents.reduce((sum, c) => sum + c.shareCount, 0)
|
||||
}
|
||||
|
||||
contentStats.value = updatedStats
|
||||
return updatedStats
|
||||
} catch (err) {
|
||||
error.value = '获取内容统计失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索内容
|
||||
const searchContents = async (keyword: string, filters?: Partial<ContentQueryParams>) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 400))
|
||||
|
||||
let results = mockContents.filter(content =>
|
||||
content.title.includes(keyword) ||
|
||||
content.content.includes(keyword) ||
|
||||
content.tags?.some((tag: string) => tag.includes(keyword))
|
||||
)
|
||||
|
||||
if (filters?.status) {
|
||||
results = results.filter(content => content.status === filters.status)
|
||||
}
|
||||
|
||||
if (filters?.contentType) {
|
||||
results = results.filter(content => content.contentType === filters.contentType)
|
||||
}
|
||||
|
||||
return results
|
||||
} catch (err) {
|
||||
error.value = '搜索内容失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 批量操作
|
||||
const batchOperation = async (ids: number[], operation: 'publish' | 'draft' | 'delete' | 'approve' | 'reject') => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 600))
|
||||
|
||||
const results = []
|
||||
|
||||
for (const id of ids) {
|
||||
const contentIndex = mockContents.findIndex(c => c.id === id)
|
||||
if (contentIndex !== -1) {
|
||||
switch (operation) {
|
||||
case 'publish':
|
||||
mockContents[contentIndex].status = 'published'
|
||||
break
|
||||
case 'draft':
|
||||
mockContents[contentIndex].status = 'draft'
|
||||
break
|
||||
case 'delete':
|
||||
mockContents.splice(contentIndex, 1)
|
||||
break
|
||||
case 'approve':
|
||||
mockContents[contentIndex].auditStatus = 'approved'
|
||||
break
|
||||
case 'reject':
|
||||
mockContents[contentIndex].auditStatus = 'rejected'
|
||||
break
|
||||
}
|
||||
results.push({ id, success: true })
|
||||
} else {
|
||||
results.push({ id, success: false, error: '内容不存在' })
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
} catch (err) {
|
||||
error.value = '批量操作失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 重置错误
|
||||
const clearError = () => {
|
||||
error.value = null
|
||||
}
|
||||
|
||||
return {
|
||||
// 状态
|
||||
contents,
|
||||
currentContent,
|
||||
contentStats,
|
||||
loading,
|
||||
error,
|
||||
|
||||
// 操作
|
||||
fetchContents,
|
||||
fetchContentDetail,
|
||||
auditContent,
|
||||
updateContentStatus,
|
||||
deleteContent,
|
||||
fetchContentStatistics,
|
||||
searchContents,
|
||||
batchOperation,
|
||||
clearError
|
||||
}
|
||||
})
|
||||
232
admin-system/src/stores/modules/merchant.ts
Normal file
232
admin-system/src/stores/modules/merchant.ts
Normal file
@@ -0,0 +1,232 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import type { Merchant, MerchantQueryParams, MerchantStatus, MerchantAuditStatus } from '../../types/merchant'
|
||||
|
||||
interface MerchantState {
|
||||
merchants: Merchant[]
|
||||
currentMerchant: Merchant | null
|
||||
loading: boolean
|
||||
totalCount: number
|
||||
queryParams: MerchantQueryParams
|
||||
}
|
||||
|
||||
export const useMerchantStore = defineStore('merchant', () => {
|
||||
// 状态
|
||||
const merchants = ref<Merchant[]>([])
|
||||
const currentMerchant = ref<Merchant | null>(null)
|
||||
const loading = ref(false)
|
||||
const totalCount = ref(0)
|
||||
const queryParams = ref<MerchantQueryParams>({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: '',
|
||||
status: undefined,
|
||||
auditStatus: undefined,
|
||||
startDate: '',
|
||||
endDate: ''
|
||||
})
|
||||
|
||||
// 获取商家列表
|
||||
const fetchMerchants = async (params?: MerchantQueryParams) => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 模拟数据
|
||||
const mockMerchants: Merchant[] = [
|
||||
{
|
||||
id: 1,
|
||||
userId: 101,
|
||||
shopName: '鲜花坊',
|
||||
businessLicense: '1234567890',
|
||||
contactPerson: '张老板',
|
||||
contactPhone: '13800138001',
|
||||
contactEmail: 'flower@example.com',
|
||||
address: '北京市朝阳区',
|
||||
description: '专业鲜花销售',
|
||||
status: 'active',
|
||||
auditStatus: 'approved',
|
||||
auditTime: '2024-01-10 14:30:00',
|
||||
registerTime: '2024-01-05 09:00:00',
|
||||
category: 'flower',
|
||||
serviceScore: 4.8,
|
||||
orderCount: 156,
|
||||
totalRevenue: 12800.50
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
userId: 102,
|
||||
shopName: '快乐农场',
|
||||
businessLicense: '0987654321',
|
||||
contactPerson: '李农场主',
|
||||
contactPhone: '13900139001',
|
||||
contactEmail: 'farm@example.com',
|
||||
address: '上海市浦东新区',
|
||||
description: '动物认领和农场体验',
|
||||
status: 'active',
|
||||
auditStatus: 'approved',
|
||||
auditTime: '2024-01-12 16:20:00',
|
||||
registerTime: '2024-01-08 10:30:00',
|
||||
category: 'farm',
|
||||
serviceScore: 4.9,
|
||||
orderCount: 89,
|
||||
totalRevenue: 8900.00
|
||||
}
|
||||
]
|
||||
|
||||
// 应用搜索过滤
|
||||
let filteredMerchants = mockMerchants
|
||||
if (queryParams.value.keyword) {
|
||||
filteredMerchants = mockMerchants.filter((m: Merchant) => {
|
||||
return m.shopName.toLowerCase().includes(queryParams.value.keyword.toLowerCase()) ||
|
||||
m.contactPerson.toLowerCase().includes(queryParams.value.keyword.toLowerCase())
|
||||
})
|
||||
}
|
||||
|
||||
merchants.value = filteredMerchants
|
||||
totalCount.value = filteredMerchants.length
|
||||
|
||||
if (params) {
|
||||
queryParams.value = { ...queryParams.value, ...params }
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取商家列表失败:', error)
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取商家详情
|
||||
const fetchMerchantDetail = async (merchantId: number) => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 模拟数据
|
||||
const mockMerchant: Merchant = {
|
||||
id: merchantId,
|
||||
userId: 100 + merchantId,
|
||||
shopName: `商家${merchantId}`,
|
||||
businessLicense: `LICENSE${merchantId}`,
|
||||
contactPerson: '联系人',
|
||||
contactPhone: '13800138000',
|
||||
contactEmail: `merchant${merchantId}@example.com`,
|
||||
address: '地址',
|
||||
description: '商家描述',
|
||||
status: 'active',
|
||||
auditStatus: 'approved',
|
||||
auditTime: '2024-01-15 10:00:00',
|
||||
registerTime: '2024-01-01 09:00:00',
|
||||
category: 'flower',
|
||||
serviceScore: 4.5,
|
||||
orderCount: 100,
|
||||
totalRevenue: 10000.00
|
||||
}
|
||||
|
||||
currentMerchant.value = mockMerchant
|
||||
return mockMerchant
|
||||
} catch (error) {
|
||||
console.error('获取商家详情失败:', error)
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 审核商家
|
||||
const auditMerchant = async (merchantId: number, auditStatus: MerchantAuditStatus, auditRemark?: string) => {
|
||||
try {
|
||||
// 模拟审核操作
|
||||
const merchant = merchants.value.find(m => m.id === merchantId)
|
||||
if (merchant) {
|
||||
merchant.auditStatus = auditStatus
|
||||
merchant.auditTime = new Date().toISOString()
|
||||
}
|
||||
|
||||
if (currentMerchant.value && currentMerchant.value.id === merchantId) {
|
||||
currentMerchant.value.auditStatus = auditStatus
|
||||
currentMerchant.value.auditTime = new Date().toISOString()
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('审核商家失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 更新商家状态
|
||||
const updateMerchantStatus = async (merchantId: number, status: MerchantStatus) => {
|
||||
try {
|
||||
// 模拟状态更新
|
||||
const merchant = merchants.value.find(m => m.id === merchantId)
|
||||
if (merchant) {
|
||||
merchant.status = status
|
||||
}
|
||||
|
||||
if (currentMerchant.value && currentMerchant.value.id === merchantId) {
|
||||
currentMerchant.value.status = status
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('更新商家状态失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索商家
|
||||
const searchMerchants = async (keyword: string) => {
|
||||
queryParams.value.keyword = keyword
|
||||
return fetchMerchants(queryParams.value)
|
||||
}
|
||||
|
||||
// 重置查询参数
|
||||
const resetQueryParams = () => {
|
||||
queryParams.value = {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: '',
|
||||
status: undefined,
|
||||
auditStatus: undefined,
|
||||
startDate: '',
|
||||
endDate: ''
|
||||
}
|
||||
}
|
||||
|
||||
// 获取商家统计
|
||||
const getMerchantStatistics = async () => {
|
||||
try {
|
||||
// 模拟统计数据
|
||||
return {
|
||||
totalMerchants: 156,
|
||||
activeMerchants: 142,
|
||||
pendingAudit: 8,
|
||||
rejected: 6,
|
||||
totalRevenue: 256800.50,
|
||||
averageScore: 4.7,
|
||||
categoryDistribution: {
|
||||
flower: 45,
|
||||
farm: 32,
|
||||
activity: 23,
|
||||
other: 56
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取商家统计失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
merchants,
|
||||
currentMerchant,
|
||||
loading,
|
||||
totalCount,
|
||||
queryParams,
|
||||
fetchMerchants,
|
||||
fetchMerchantDetail,
|
||||
auditMerchant,
|
||||
updateMerchantStatus,
|
||||
searchMerchants,
|
||||
resetQueryParams,
|
||||
getMerchantStatistics
|
||||
}
|
||||
})
|
||||
223
admin-system/src/stores/modules/order.ts
Normal file
223
admin-system/src/stores/modules/order.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import type { Order, OrderQueryParams, OrderStatus } from '../../types/order'
|
||||
|
||||
|
||||
|
||||
export const useOrderStore = defineStore('order', () => {
|
||||
// 状态
|
||||
const orders = ref<Order[]>([])
|
||||
const currentOrder = ref<Order | null>(null)
|
||||
const loading = ref(false)
|
||||
const totalCount = ref(0)
|
||||
const queryParams = ref<OrderQueryParams>({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: '',
|
||||
status: undefined,
|
||||
orderType: undefined,
|
||||
startDate: '',
|
||||
endDate: ''
|
||||
})
|
||||
|
||||
// 获取订单列表
|
||||
const fetchOrders = async (params?: OrderQueryParams) => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 模拟数据
|
||||
const mockOrders: Order[] = [
|
||||
{
|
||||
id: 1,
|
||||
orderNo: 'ORDER202401150001',
|
||||
userId: 101,
|
||||
username: 'user1',
|
||||
merchantId: 1,
|
||||
shopName: '鲜花坊',
|
||||
orderType: 'flower',
|
||||
productName: '玫瑰花束',
|
||||
quantity: 1,
|
||||
unitPrice: 198.00,
|
||||
totalAmount: 198.00,
|
||||
status: 'completed',
|
||||
createTime: '2024-01-15 10:30:00',
|
||||
completeTime: '2024-01-15 14:00:00',
|
||||
recipientName: '李四',
|
||||
recipientPhone: '13900139000',
|
||||
deliveryAddress: '北京市朝阳区',
|
||||
message: '生日快乐!'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
orderNo: 'ORDER202401140001',
|
||||
userId: 102,
|
||||
username: 'user2',
|
||||
merchantId: 2,
|
||||
shopName: '快乐农场',
|
||||
orderType: 'animal',
|
||||
productName: '小羊认领',
|
||||
quantity: 1,
|
||||
unitPrice: 299.00,
|
||||
totalAmount: 299.00,
|
||||
status: 'pending',
|
||||
createTime: '2024-01-14 16:20:00',
|
||||
recipientName: '王五',
|
||||
recipientPhone: '13800138001',
|
||||
deliveryAddress: '上海市浦东新区'
|
||||
}
|
||||
]
|
||||
|
||||
orders.value = mockOrders
|
||||
totalCount.value = mockOrders.length
|
||||
|
||||
if (params) {
|
||||
queryParams.value = { ...queryParams.value, ...params }
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取订单列表失败:', error)
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取订单详情
|
||||
const fetchOrderDetail = async (orderId: number) => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 模拟数据
|
||||
const mockOrder: Order = {
|
||||
id: orderId,
|
||||
orderNo: `ORDER20240115${orderId.toString().padStart(4, '0')}`,
|
||||
userId: 100 + orderId,
|
||||
username: `user${orderId}`,
|
||||
merchantId: orderId,
|
||||
shopName: `商家${orderId}`,
|
||||
orderType: 'flower',
|
||||
productName: '商品',
|
||||
quantity: 1,
|
||||
unitPrice: 100.00,
|
||||
totalAmount: 100.00,
|
||||
status: 'pending',
|
||||
createTime: '2024-01-15 10:00:00',
|
||||
recipientName: '收件人',
|
||||
recipientPhone: '13800138000',
|
||||
deliveryAddress: '地址'
|
||||
}
|
||||
|
||||
currentOrder.value = mockOrder
|
||||
return mockOrder
|
||||
} catch (error) {
|
||||
console.error('获取订单详情失败:', error)
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 更新订单状态
|
||||
const updateOrderStatus = async (orderId: number, status: OrderStatus) => {
|
||||
try {
|
||||
// 模拟状态更新
|
||||
const order = orders.value.find((o: Order) => o.id === orderId)
|
||||
if (order) {
|
||||
order.status = status
|
||||
if (status === 'completed') {
|
||||
order.completeTime = new Date().toISOString()
|
||||
}
|
||||
}
|
||||
|
||||
if (currentOrder.value && currentOrder.value.id === orderId) {
|
||||
currentOrder.value.status = status
|
||||
if (status === 'completed') {
|
||||
currentOrder.value.completeTime = new Date().toISOString()
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('更新订单状态失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索订单
|
||||
const searchOrders = async (keyword: string) => {
|
||||
queryParams.value.keyword = keyword
|
||||
return fetchOrders(queryParams.value)
|
||||
}
|
||||
|
||||
// 重置查询参数
|
||||
const resetQueryParams = () => {
|
||||
queryParams.value = {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: '',
|
||||
status: undefined,
|
||||
orderType: undefined,
|
||||
startDate: '',
|
||||
endDate: ''
|
||||
}
|
||||
}
|
||||
|
||||
// 获取订单统计
|
||||
const getOrderStatistics = async () => {
|
||||
try {
|
||||
// 模拟统计数据
|
||||
return {
|
||||
totalOrders: 1256,
|
||||
pendingOrders: 45,
|
||||
completedOrders: 987,
|
||||
cancelledOrders: 224,
|
||||
totalRevenue: 156800.50,
|
||||
averageOrderValue: 124.68,
|
||||
dailyOrderCount: 89,
|
||||
orderTypeDistribution: {
|
||||
flower: 456,
|
||||
animal: 312,
|
||||
activity: 288,
|
||||
travel: 200
|
||||
},
|
||||
statusDistribution: {
|
||||
pending: 45,
|
||||
paid: 156,
|
||||
shipped: 89,
|
||||
completed: 987,
|
||||
cancelled: 224
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取订单统计失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 导出订单数据
|
||||
const exportOrders = async () => {
|
||||
try {
|
||||
// 模拟导出功能
|
||||
const csvContent = orders.value.map(order =>
|
||||
`${order.orderNo},${order.username},${order.shopName},${order.productName},${order.totalAmount},${order.status}`
|
||||
).join('\n')
|
||||
|
||||
return csvContent
|
||||
} catch (error) {
|
||||
console.error('导出订单数据失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
orders,
|
||||
currentOrder,
|
||||
loading,
|
||||
totalCount,
|
||||
queryParams,
|
||||
fetchOrders,
|
||||
fetchOrderDetail,
|
||||
updateOrderStatus,
|
||||
searchOrders,
|
||||
resetQueryParams,
|
||||
getOrderStatistics,
|
||||
exportOrders
|
||||
}
|
||||
})
|
||||
721
admin-system/src/stores/modules/permission.ts
Normal file
721
admin-system/src/stores/modules/permission.ts
Normal file
@@ -0,0 +1,721 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import type {
|
||||
Permission,
|
||||
Role,
|
||||
UserRole,
|
||||
PermissionQueryParams,
|
||||
RoleQueryParams,
|
||||
PermissionStatistics,
|
||||
AuditLog
|
||||
} from '../../types/permission'
|
||||
|
||||
// 模拟数据 - 权限列表
|
||||
const mockPermissions: Permission[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: '用户管理',
|
||||
code: 'user:manage',
|
||||
description: '管理用户信息的权限',
|
||||
type: 'module',
|
||||
parentId: null,
|
||||
level: 1,
|
||||
status: 'active',
|
||||
createTime: '2024-01-01 10:00:00',
|
||||
updateTime: '2024-01-01 10:00:00',
|
||||
children: [
|
||||
{
|
||||
id: 101,
|
||||
name: '查看用户',
|
||||
code: 'user:view',
|
||||
description: '查看用户列表和详情的权限',
|
||||
type: 'action',
|
||||
parentId: 1,
|
||||
level: 2,
|
||||
status: 'active',
|
||||
createTime: '2024-01-01 10:00:00',
|
||||
updateTime: '2024-01-01 10:00:00'
|
||||
},
|
||||
{
|
||||
id: 102,
|
||||
name: '编辑用户',
|
||||
code: 'user:edit',
|
||||
description: '编辑用户信息的权限',
|
||||
type: 'action',
|
||||
parentId: 1,
|
||||
level: 2,
|
||||
status: 'active',
|
||||
createTime: '2024-01-01 10:00:00',
|
||||
updateTime: '2024-01-01 10:00:00'
|
||||
},
|
||||
{
|
||||
id: 103,
|
||||
name: '删除用户',
|
||||
code: 'user:delete',
|
||||
description: '删除用户的权限',
|
||||
type: 'action',
|
||||
parentId: 1,
|
||||
level: 2,
|
||||
status: 'active',
|
||||
createTime: '2024-01-01 10:00:00',
|
||||
updateTime: '2024-01-01 10:00:00'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '活动管理',
|
||||
code: 'activity:manage',
|
||||
description: '管理活动信息的权限',
|
||||
type: 'module',
|
||||
parentId: null,
|
||||
level: 1,
|
||||
status: 'active',
|
||||
createTime: '2024-01-01 10:00:00',
|
||||
updateTime: '2024-01-01 10:00:00',
|
||||
children: [
|
||||
{
|
||||
id: 201,
|
||||
name: '查看活动',
|
||||
code: 'activity:view',
|
||||
description: '查看活动列表和详情的权限',
|
||||
type: 'action',
|
||||
parentId: 2,
|
||||
level: 2,
|
||||
status: 'active',
|
||||
createTime: '2024-01-01 10:00:00',
|
||||
updateTime: '2024-01-01 10:00:00'
|
||||
},
|
||||
{
|
||||
id: 202,
|
||||
name: '创建活动',
|
||||
code: 'activity:create',
|
||||
description: '创建新活动的权限',
|
||||
type: 'action',
|
||||
parentId: 2,
|
||||
level: 2,
|
||||
status: 'active',
|
||||
createTime: '2024-01-01 10:00:00',
|
||||
updateTime: '2024-01-01 10:00:00'
|
||||
},
|
||||
{
|
||||
id: 203,
|
||||
name: '审核活动',
|
||||
code: 'activity:audit',
|
||||
description: '审核活动内容的权限',
|
||||
type: 'action',
|
||||
parentId: 2,
|
||||
level: 2,
|
||||
status: 'active',
|
||||
createTime: '2024-01-01 10:00:00',
|
||||
updateTime: '2024-01-01 10:00:00'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '动物管理',
|
||||
code: 'animal:manage',
|
||||
description: '管理动物信息的权限',
|
||||
type: 'module',
|
||||
parentId: null,
|
||||
level: 1,
|
||||
status: 'active',
|
||||
createTime: '2024-01-01 10:00:00',
|
||||
updateTime: '2024-01-01 10:00:00',
|
||||
children: [
|
||||
{
|
||||
id: 301,
|
||||
name: '查看动物',
|
||||
code: 'animal:view',
|
||||
description: '查看动物列表和详情的权限',
|
||||
type: 'action',
|
||||
parentId: 3,
|
||||
level: 2,
|
||||
status: 'active',
|
||||
createTime: '2024-01-01 10:00:00',
|
||||
updateTime: '2024-01-01 10:00:00'
|
||||
},
|
||||
{
|
||||
id: 302,
|
||||
name: '编辑动物',
|
||||
code: 'animal:edit',
|
||||
description: '编辑动物信息的权限',
|
||||
type: 'action',
|
||||
parentId: 3,
|
||||
level: 2,
|
||||
status: 'active',
|
||||
createTime: '2024-01-01 10:00:00',
|
||||
updateTime: '2024-01-01 10:00:00'
|
||||
},
|
||||
{
|
||||
id: 303,
|
||||
name: '认养管理',
|
||||
code: 'animal:adopt',
|
||||
description: '管理动物认养流程的权限',
|
||||
type: 'action',
|
||||
parentId: 3,
|
||||
level: 2,
|
||||
status: 'active',
|
||||
createTime: '2024-01-01 10:00:00',
|
||||
updateTime: '2024-01-01 10:00:00'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
// 模拟数据 - 角色列表
|
||||
const mockRoles: Role[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: '超级管理员',
|
||||
code: 'super_admin',
|
||||
description: '拥有系统所有权限的最高权限角色',
|
||||
type: 'system',
|
||||
status: 'active',
|
||||
permissionIds: [1, 101, 102, 103, 2, 201, 202, 203, 3, 301, 302, 303],
|
||||
userCount: 3,
|
||||
createTime: '2024-01-01 10:00:00',
|
||||
updateTime: '2024-01-01 10:00:00',
|
||||
permissions: mockPermissions.flatMap(p => p.children || []).filter(p =>
|
||||
[101, 102, 103, 201, 202, 203, 301, 302, 303].includes(p.id)
|
||||
)
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '内容管理员',
|
||||
code: 'content_admin',
|
||||
description: '负责内容审核和管理的角色',
|
||||
type: 'system',
|
||||
status: 'active',
|
||||
permissionIds: [2, 201, 203, 3, 301],
|
||||
userCount: 5,
|
||||
createTime: '2024-01-01 10:00:00',
|
||||
updateTime: '2024-01-01 10:00:00',
|
||||
permissions: mockPermissions.flatMap(p => p.children || []).filter(p =>
|
||||
[201, 203, 301].includes(p.id)
|
||||
)
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '用户管理员',
|
||||
code: 'user_admin',
|
||||
description: '负责用户管理和支持的角色',
|
||||
type: 'system',
|
||||
status: 'active',
|
||||
permissionIds: [1, 101, 102],
|
||||
userCount: 8,
|
||||
createTime: '2024-01-01 10:00:00',
|
||||
updateTime: '2024-01-01 10:00:00',
|
||||
permissions: mockPermissions.flatMap(p => p.children || []).filter(p =>
|
||||
[101, 102].includes(p.id)
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
// 模拟数据 - 用户角色关联
|
||||
const mockUserRoles: UserRole[] = [
|
||||
{ id: 1, userId: 1, roleId: 1, assignTime: '2024-01-01 10:00:00', assignBy: 'system' },
|
||||
{ id: 2, userId: 2, roleId: 2, assignTime: '2024-01-01 10:00:00', assignBy: 'admin' },
|
||||
{ id: 3, userId: 3, roleId: 2, assignTime: '2024-01-01 10:00:00', assignBy: 'admin' },
|
||||
{ id: 4, userId: 4, roleId: 3, assignTime: '2024-01-01 10:00:00', assignBy: 'admin' }
|
||||
]
|
||||
|
||||
// 模拟数据 - 权限统计
|
||||
const mockPermissionStats: PermissionStatistics = {
|
||||
totalPermissions: 12,
|
||||
activePermissions: 12,
|
||||
inactivePermissions: 0,
|
||||
totalRoles: 3,
|
||||
activeRoles: 3,
|
||||
inactiveRoles: 0,
|
||||
totalUsersWithRoles: 4,
|
||||
permissionUsage: {
|
||||
'user:view': 15,
|
||||
'user:edit': 8,
|
||||
'user:delete': 3,
|
||||
'activity:view': 25,
|
||||
'activity:create': 12,
|
||||
'activity:audit': 18,
|
||||
'animal:view': 20,
|
||||
'animal:edit': 10,
|
||||
'animal:adopt': 15
|
||||
},
|
||||
roleDistribution: {
|
||||
'super_admin': 3,
|
||||
'content_admin': 5,
|
||||
'user_admin': 8
|
||||
},
|
||||
userPermissionStats: {
|
||||
averagePermissionsPerUser: 6.5,
|
||||
maxPermissionsPerUser: 9,
|
||||
minPermissionsPerUser: 2,
|
||||
usersWithExcessivePermissions: 1
|
||||
},
|
||||
auditLogs: {
|
||||
totalLogs: 156,
|
||||
permissionChanges: 45,
|
||||
roleChanges: 32,
|
||||
userRoleChanges: 79
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟数据 - 审计日志
|
||||
const mockAuditLogs: AuditLog[] = [
|
||||
{
|
||||
id: 1,
|
||||
action: 'assign_role',
|
||||
targetType: 'user',
|
||||
targetId: 2,
|
||||
targetName: 'user2',
|
||||
performerId: 1,
|
||||
performerName: 'admin',
|
||||
details: '分配角色: content_admin',
|
||||
ipAddress: '192.168.1.100',
|
||||
userAgent: 'Mozilla/5.0',
|
||||
timestamp: '2024-01-15 14:30:00',
|
||||
status: 'success'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
action: 'update_permission',
|
||||
targetType: 'permission',
|
||||
targetId: 101,
|
||||
targetName: 'user:view',
|
||||
performerId: 1,
|
||||
performerName: 'admin',
|
||||
details: '更新权限描述',
|
||||
ipAddress: '192.168.1.100',
|
||||
userAgent: 'Mozilla/5.0',
|
||||
timestamp: '2024-01-15 14:25:00',
|
||||
status: 'success'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
action: 'create_role',
|
||||
targetType: 'role',
|
||||
targetId: 3,
|
||||
targetName: 'user_admin',
|
||||
performerId: 1,
|
||||
performerName: 'admin',
|
||||
details: '创建新角色',
|
||||
ipAddress: '192.168.1.100',
|
||||
userAgent: 'Mozilla/5.0',
|
||||
timestamp: '2024-01-15 14:20:00',
|
||||
status: 'success'
|
||||
}
|
||||
]
|
||||
|
||||
export const usePermissionStore = defineStore('permission', () => {
|
||||
// 状态
|
||||
const permissions = ref<Permission[]>(mockPermissions)
|
||||
const roles = ref<Role[]>(mockRoles)
|
||||
const userRoles = ref<UserRole[]>(mockUserRoles)
|
||||
const permissionStats = ref<PermissionStatistics>(mockPermissionStats)
|
||||
const auditLogs = ref<AuditLog[]>(mockAuditLogs)
|
||||
const loading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
// 获取权限列表
|
||||
const fetchPermissions = async (params: PermissionQueryParams) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
|
||||
let filteredPermissions = mockPermissions.flat()
|
||||
|
||||
if (params.keyword) {
|
||||
filteredPermissions = filteredPermissions.filter(permission =>
|
||||
permission.name.includes(params.keyword!) ||
|
||||
permission.code.includes(params.keyword!) ||
|
||||
permission.description.includes(params.keyword!)
|
||||
)
|
||||
}
|
||||
|
||||
if (params.status) {
|
||||
filteredPermissions = filteredPermissions.filter(permission => permission.status === params.status)
|
||||
}
|
||||
|
||||
if (params.type) {
|
||||
filteredPermissions = filteredPermissions.filter(permission => permission.type === params.type)
|
||||
}
|
||||
|
||||
if (params.parentId !== undefined) {
|
||||
filteredPermissions = filteredPermissions.filter(permission => permission.parentId === params.parentId)
|
||||
}
|
||||
|
||||
// 分页
|
||||
const start = (params.page - 1) * params.pageSize
|
||||
const paginatedPermissions = filteredPermissions.slice(start, start + params.pageSize)
|
||||
|
||||
permissions.value = paginatedPermissions
|
||||
|
||||
return {
|
||||
permissions: paginatedPermissions,
|
||||
total: filteredPermissions.length,
|
||||
page: params.page,
|
||||
pageSize: params.pageSize
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = '获取权限列表失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取角色列表
|
||||
const fetchRoles = async (params: RoleQueryParams) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
|
||||
let filteredRoles = mockRoles
|
||||
|
||||
if (params.keyword) {
|
||||
filteredRoles = filteredRoles.filter(role =>
|
||||
role.name.includes(params.keyword!) ||
|
||||
role.code.includes(params.keyword!) ||
|
||||
role.description.includes(params.keyword!)
|
||||
)
|
||||
}
|
||||
|
||||
if (params.status) {
|
||||
filteredRoles = filteredRoles.filter(role => role.status === params.status)
|
||||
}
|
||||
|
||||
if (params.type) {
|
||||
filteredRoles = filteredRoles.filter(role => role.type === params.type)
|
||||
}
|
||||
|
||||
// 分页
|
||||
const start = (params.page - 1) * params.pageSize
|
||||
const paginatedRoles = filteredRoles.slice(start, start + params.pageSize)
|
||||
|
||||
roles.value = paginatedRoles
|
||||
|
||||
return {
|
||||
roles: paginatedRoles,
|
||||
total: filteredRoles.length,
|
||||
page: params.page,
|
||||
pageSize: params.pageSize
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = '获取角色列表失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取用户角色
|
||||
const fetchUserRoles = async (userId: number) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 300))
|
||||
|
||||
const userRoles = mockUserRoles.filter(ur => ur.userId === userId)
|
||||
const roleIds = userRoles.map(ur => ur.roleId)
|
||||
const userRolesWithDetails = mockRoles
|
||||
.filter(role => roleIds.includes(role.id))
|
||||
.map(role => ({
|
||||
...role,
|
||||
assignTime: userRoles.find(ur => ur.roleId === role.id)?.assignTime,
|
||||
assignBy: userRoles.find(ur => ur.roleId === role.id)?.assignBy
|
||||
}))
|
||||
|
||||
return userRolesWithDetails
|
||||
} catch (err) {
|
||||
error.value = '获取用户角色失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 分配用户角色
|
||||
const assignUserRole = async (userId: number, roleId: number, assignBy: string) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 400))
|
||||
|
||||
// 检查是否已分配
|
||||
const existingAssignment = mockUserRoles.find(ur => ur.userId === userId && ur.roleId === roleId)
|
||||
if (existingAssignment) {
|
||||
throw new Error('用户已拥有该角色')
|
||||
}
|
||||
|
||||
const newAssignment: UserRole = {
|
||||
id: Math.max(...mockUserRoles.map(ur => ur.id)) + 1,
|
||||
userId,
|
||||
roleId,
|
||||
assignTime: new Date().toISOString(),
|
||||
assignBy
|
||||
}
|
||||
|
||||
mockUserRoles.push(newAssignment)
|
||||
|
||||
// 更新角色用户计数
|
||||
const role = mockRoles.find(r => r.id === roleId)
|
||||
if (role) {
|
||||
role.userCount += 1
|
||||
}
|
||||
|
||||
return newAssignment
|
||||
} catch (err) {
|
||||
error.value = '分配用户角色失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 移除用户角色
|
||||
const removeUserRole = async (userId: number, roleId: number) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 400))
|
||||
|
||||
const index = mockUserRoles.findIndex(ur => ur.userId === userId && ur.roleId === roleId)
|
||||
if (index === -1) {
|
||||
throw new Error('用户角色关联不存在')
|
||||
}
|
||||
|
||||
mockUserRoles.splice(index, 1)
|
||||
|
||||
// 更新角色用户计数
|
||||
const role = mockRoles.find(r => r.id === roleId)
|
||||
if (role && role.userCount > 0) {
|
||||
role.userCount -= 1
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (err) {
|
||||
error.value = '移除用户角色失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 创建角色
|
||||
const createRole = async (roleData: Omit<Role, 'id' | 'createTime' | 'updateTime'>) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
|
||||
const newRole: Role = {
|
||||
...roleData,
|
||||
id: Math.max(...mockRoles.map(r => r.id)) + 1,
|
||||
createTime: new Date().toISOString(),
|
||||
updateTime: new Date().toISOString(),
|
||||
permissions: mockPermissions.flatMap(p => p.children || []).filter(p =>
|
||||
roleData.permissionIds.includes(p.id)
|
||||
)
|
||||
}
|
||||
|
||||
mockRoles.push(newRole)
|
||||
roles.value = [...mockRoles]
|
||||
|
||||
return newRole
|
||||
} catch (err) {
|
||||
error.value = '创建角色失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 更新角色
|
||||
const updateRole = async (roleId: number, roleData: Partial<Omit<Role, 'id' | 'createTime'>>) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
|
||||
const index = mockRoles.findIndex(r => r.id === roleId)
|
||||
if (index === -1) {
|
||||
throw new Error('角色不存在')
|
||||
}
|
||||
|
||||
mockRoles[index] = {
|
||||
...mockRoles[index],
|
||||
...roleData,
|
||||
updateTime: new Date().toISOString(),
|
||||
permissions: roleData.permissionIds
|
||||
? mockPermissions.flatMap(p => p.children || []).filter(p =>
|
||||
roleData.permissionIds!.includes(p.id)
|
||||
)
|
||||
: mockRoles[index].permissions
|
||||
}
|
||||
|
||||
roles.value = [...mockRoles]
|
||||
|
||||
return mockRoles[index]
|
||||
} catch (err) {
|
||||
error.value = '更新角色失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 删除角色
|
||||
const deleteRole = async (roleId: number) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 400))
|
||||
|
||||
const index = mockRoles.findIndex(r => r.id === roleId)
|
||||
if (index === -1) {
|
||||
throw new Error('角色不存在')
|
||||
}
|
||||
|
||||
// 检查是否有用户使用该角色
|
||||
const usersWithRole = mockUserRoles.filter(ur => ur.roleId === roleId)
|
||||
if (usersWithRole.length > 0) {
|
||||
throw new Error('无法删除有用户使用的角色')
|
||||
}
|
||||
|
||||
mockRoles.splice(index, 1)
|
||||
roles.value = [...mockRoles]
|
||||
|
||||
return true
|
||||
} catch (err) {
|
||||
error.value = '删除角色失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取权限统计
|
||||
const fetchPermissionStatistics = async () => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 800))
|
||||
|
||||
// 模拟实时数据更新
|
||||
const stats = {
|
||||
...mockPermissionStats,
|
||||
totalUsersWithRoles: mockUserRoles.length,
|
||||
userPermissionStats: {
|
||||
...mockPermissionStats.userPermissionStats,
|
||||
averagePermissionsPerUser: Math.round(
|
||||
mockUserRoles.reduce((sum, ur) => {
|
||||
const role = mockRoles.find(r => r.id === ur.roleId)
|
||||
return sum + (role ? role.permissionIds.length : 0)
|
||||
}, 0) / mockUserRoles.length
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
permissionStats.value = stats
|
||||
return stats
|
||||
} catch (err) {
|
||||
error.value = '获取权限统计失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取审计日志
|
||||
const fetchAuditLogs = async (page: number, pageSize: number) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 600))
|
||||
|
||||
const start = (page - 1) * pageSize
|
||||
const paginatedLogs = mockAuditLogs.slice(start, start + pageSize)
|
||||
|
||||
auditLogs.value = paginatedLogs
|
||||
|
||||
return {
|
||||
logs: paginatedLogs,
|
||||
total: mockAuditLogs.length,
|
||||
page,
|
||||
pageSize
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = '获取审计日志失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 检查用户权限
|
||||
const checkUserPermission = async (userId: number, permissionCode: string) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 200))
|
||||
|
||||
const userRoles = mockUserRoles.filter(ur => ur.userId === userId)
|
||||
const roleIds = userRoles.map(ur => ur.roleId)
|
||||
|
||||
const hasPermission = mockRoles.some(role =>
|
||||
roleIds.includes(role.id) &&
|
||||
role.permissions.some(p => p.code === permissionCode)
|
||||
)
|
||||
|
||||
return hasPermission
|
||||
} catch (err) {
|
||||
error.value = '检查用户权限失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 重置错误
|
||||
const clearError = () => {
|
||||
error.value = null
|
||||
}
|
||||
|
||||
return {
|
||||
// 状态
|
||||
permissions,
|
||||
roles,
|
||||
userRoles,
|
||||
permissionStats,
|
||||
auditLogs,
|
||||
loading,
|
||||
error,
|
||||
|
||||
// 操作
|
||||
fetchPermissions,
|
||||
fetchRoles,
|
||||
fetchUserRoles,
|
||||
assignUserRole,
|
||||
removeUserRole,
|
||||
createRole,
|
||||
updateRole,
|
||||
deleteRole,
|
||||
fetchPermissionStatistics,
|
||||
fetchAuditLogs,
|
||||
checkUserPermission,
|
||||
clearError
|
||||
}
|
||||
})
|
||||
668
admin-system/src/stores/modules/stats.ts
Normal file
668
admin-system/src/stores/modules/stats.ts
Normal file
@@ -0,0 +1,668 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import type {
|
||||
SystemStats,
|
||||
UserStats,
|
||||
ActivityStats,
|
||||
AnimalStats,
|
||||
OrderStats,
|
||||
RevenueStats,
|
||||
TimeRange,
|
||||
StatsComparison,
|
||||
DashboardMetrics
|
||||
} from '../../types/stats'
|
||||
|
||||
// 模拟数据 - 系统统计
|
||||
const mockSystemStats: SystemStats = {
|
||||
totalUsers: 12560,
|
||||
activeUsers: 8560,
|
||||
newUsersToday: 125,
|
||||
newUsersThisWeek: 890,
|
||||
newUsersThisMonth: 3450,
|
||||
totalActivities: 2560,
|
||||
activeActivities: 1250,
|
||||
completedActivities: 890,
|
||||
totalAnimals: 560,
|
||||
adoptedAnimals: 320,
|
||||
availableAnimals: 240,
|
||||
totalOrders: 4560,
|
||||
pendingOrders: 120,
|
||||
completedOrders: 4320,
|
||||
cancelledOrders: 120,
|
||||
totalRevenue: 125600,
|
||||
todayRevenue: 2560,
|
||||
weekRevenue: 17890,
|
||||
monthRevenue: 56780,
|
||||
systemUptime: '99.95%',
|
||||
averageResponseTime: 120,
|
||||
errorRate: 0.05,
|
||||
serverLoad: 45.2
|
||||
}
|
||||
|
||||
// 模拟数据 - 用户统计
|
||||
const mockUserStats: UserStats = {
|
||||
totalUsers: 12560,
|
||||
activeUsers: 8560,
|
||||
newUsers: {
|
||||
today: 125,
|
||||
yesterday: 110,
|
||||
thisWeek: 890,
|
||||
lastWeek: 780,
|
||||
thisMonth: 3450,
|
||||
lastMonth: 2980
|
||||
},
|
||||
userGrowthRate: 15.8,
|
||||
userRetentionRate: 78.5,
|
||||
userChurnRate: 5.2,
|
||||
userDemographics: {
|
||||
ageDistribution: {
|
||||
'18-24': 25.6,
|
||||
'25-34': 38.9,
|
||||
'35-44': 20.1,
|
||||
'45-54': 10.2,
|
||||
'55+': 5.2
|
||||
},
|
||||
genderDistribution: {
|
||||
male: 58.3,
|
||||
female: 40.1,
|
||||
other: 1.6
|
||||
},
|
||||
locationDistribution: {
|
||||
'北京': 18.5,
|
||||
'上海': 15.2,
|
||||
'广州': 12.8,
|
||||
'深圳': 11.5,
|
||||
'其他': 42.0
|
||||
}
|
||||
},
|
||||
userActivity: {
|
||||
dailyActiveUsers: 8560,
|
||||
weeklyActiveUsers: 23450,
|
||||
monthlyActiveUsers: 56780,
|
||||
averageSessionDuration: 12.5,
|
||||
averageSessionsPerUser: 3.2
|
||||
},
|
||||
userBehavior: {
|
||||
averageActivitiesPerUser: 2.1,
|
||||
averageAnimalsAdopted: 0.8,
|
||||
averageOrdersPerUser: 3.6,
|
||||
conversionRate: 28.5,
|
||||
engagementRate: 45.2
|
||||
},
|
||||
userLifetimeValue: 156.8,
|
||||
userAcquisitionCost: 25.4,
|
||||
userRetentionCost: 12.3
|
||||
}
|
||||
|
||||
// 模拟数据 - 活动统计
|
||||
const mockActivityStats: ActivityStats = {
|
||||
totalActivities: 2560,
|
||||
activeActivities: 1250,
|
||||
completedActivities: 890,
|
||||
cancelledActivities: 120,
|
||||
pendingActivities: 300,
|
||||
activityGrowthRate: 22.5,
|
||||
participationStats: {
|
||||
totalParticipants: 12560,
|
||||
averageParticipantsPerActivity: 4.9,
|
||||
repeatParticipants: 45.2,
|
||||
participationRate: 68.9
|
||||
},
|
||||
revenueStats: {
|
||||
totalRevenue: 125600,
|
||||
averageRevenuePerActivity: 49.1,
|
||||
revenueGrowthRate: 28.3,
|
||||
refundRate: 3.2
|
||||
},
|
||||
categoryDistribution: {
|
||||
travel: 35.6,
|
||||
movie: 18.9,
|
||||
dinner: 15.2,
|
||||
game: 12.8,
|
||||
sports: 8.5,
|
||||
study: 6.3,
|
||||
other: 2.7
|
||||
},
|
||||
locationDistribution: {
|
||||
'北京': 22.5,
|
||||
'上海': 18.9,
|
||||
'广州': 15.6,
|
||||
'深圳': 12.8,
|
||||
'其他': 30.2
|
||||
},
|
||||
timeDistribution: {
|
||||
'00-06': 5.2,
|
||||
'06-12': 25.6,
|
||||
'12-18': 35.8,
|
||||
'18-24': 33.4
|
||||
},
|
||||
organizerPerformance: {
|
||||
topOrganizers: [
|
||||
{ organizerId: 101, name: '旅行达人小王', activityCount: 45, revenue: 12500 },
|
||||
{ organizerId: 102, name: '电影爱好者小李', activityCount: 38, revenue: 9800 },
|
||||
{ organizerId: 103, name: '美食家小张', activityCount: 32, revenue: 7600 }
|
||||
],
|
||||
averageRating: 4.5,
|
||||
completionRate: 89.2,
|
||||
cancellationRate: 5.8
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟数据 - 动物统计
|
||||
const mockAnimalStats: AnimalStats = {
|
||||
totalAnimals: 560,
|
||||
adoptedAnimals: 320,
|
||||
availableAnimals: 240,
|
||||
adoptionRate: 57.1,
|
||||
adoptionGrowthRate: 18.9,
|
||||
animalTypes: {
|
||||
dog: 35.6,
|
||||
cat: 28.9,
|
||||
sheep: 15.2,
|
||||
cow: 8.5,
|
||||
chicken: 6.3,
|
||||
other: 5.5
|
||||
},
|
||||
adoptionStats: {
|
||||
totalAdoptions: 320,
|
||||
averageAdoptionTime: 3.2,
|
||||
repeatAdoptions: 12.5,
|
||||
adoptionSuccessRate: 92.8
|
||||
},
|
||||
careStats: {
|
||||
totalCareRecords: 12560,
|
||||
averageCareFrequency: 2.5,
|
||||
medicalCheckups: 456,
|
||||
vaccinationRate: 89.5
|
||||
},
|
||||
locationDistribution: {
|
||||
'北京农场': 25.6,
|
||||
'上海牧场': 18.9,
|
||||
'广州养殖场': 15.2,
|
||||
'深圳农场': 12.8,
|
||||
'其他': 27.5
|
||||
},
|
||||
popularAnimals: [
|
||||
{ animalId: 1, name: '小白', type: 'dog', adoptionCount: 15, viewCount: 1250 },
|
||||
{ animalId: 2, name: '小花', type: 'cat', adoptionCount: 12, viewCount: 980 },
|
||||
{ animalId: 3, name: '小羊', type: 'sheep', adoptionCount: 10, viewCount: 760 }
|
||||
],
|
||||
revenueStats: {
|
||||
totalAdoptionRevenue: 45600,
|
||||
averageAdoptionFee: 142.5,
|
||||
careProductRevenue: 12500,
|
||||
totalRevenue: 58100
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟数据 - 订单统计
|
||||
const mockOrderStats: OrderStats = {
|
||||
totalOrders: 4560,
|
||||
pendingOrders: 120,
|
||||
completedOrders: 4320,
|
||||
cancelledOrders: 120,
|
||||
orderGrowthRate: 25.8,
|
||||
revenueStats: {
|
||||
totalRevenue: 125600,
|
||||
averageOrderValue: 27.5,
|
||||
revenueGrowthRate: 32.1,
|
||||
refundAmount: 2560,
|
||||
netRevenue: 123040
|
||||
},
|
||||
orderTypeDistribution: {
|
||||
activity: 45.6,
|
||||
adoption: 28.9,
|
||||
flower: 15.2,
|
||||
product: 8.5,
|
||||
other: 2.8
|
||||
},
|
||||
paymentStats: {
|
||||
paymentMethods: {
|
||||
wechat: 58.9,
|
||||
alipay: 35.2,
|
||||
bank: 4.2,
|
||||
other: 1.7
|
||||
},
|
||||
paymentSuccessRate: 98.5,
|
||||
averagePaymentTime: 2.1,
|
||||
refundRate: 3.2
|
||||
},
|
||||
customerStats: {
|
||||
repeatCustomers: 45.6,
|
||||
averageOrdersPerCustomer: 3.2,
|
||||
customerLifetimeValue: 156.8,
|
||||
acquisitionCost: 25.4
|
||||
},
|
||||
timeDistribution: {
|
||||
'00-06': 8.5,
|
||||
'06-12': 22.6,
|
||||
'12-18': 35.8,
|
||||
'18-24': 33.1
|
||||
},
|
||||
locationDistribution: {
|
||||
'北京': 20.5,
|
||||
'上海': 18.2,
|
||||
'广州': 15.8,
|
||||
'深圳': 13.5,
|
||||
'其他': 32.0
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟数据 - 收入统计
|
||||
const mockRevenueStats: RevenueStats = {
|
||||
totalRevenue: 125600,
|
||||
revenueGrowthRate: 32.1,
|
||||
dailyRevenue: 2560,
|
||||
weeklyRevenue: 17890,
|
||||
monthlyRevenue: 56780,
|
||||
revenueSources: {
|
||||
activity: 45.6,
|
||||
adoption: 28.9,
|
||||
flower: 15.2,
|
||||
product: 8.5,
|
||||
other: 2.8
|
||||
},
|
||||
revenueTrends: {
|
||||
last7Days: [1250, 1380, 1560, 1420, 1680, 1920, 2560],
|
||||
last30Days: [
|
||||
1200, 1250, 1380, 1420, 1560, 1480, 1620, 1780, 1920, 2050,
|
||||
2180, 2250, 2380, 2450, 2560, 2680, 2750, 2850, 2920, 3050,
|
||||
3120, 3250, 3350, 3420, 3560, 3650, 3780, 3850, 3920, 4050
|
||||
]
|
||||
},
|
||||
customerMetrics: {
|
||||
averageRevenuePerUser: 156.8,
|
||||
averageOrderValue: 27.5,
|
||||
purchaseFrequency: 3.2,
|
||||
customerLifetimeValue: 456.2
|
||||
},
|
||||
geographicDistribution: {
|
||||
'北京': 22.5,
|
||||
'上海': 19.8,
|
||||
'广州': 16.2,
|
||||
'深圳': 13.8,
|
||||
'其他': 27.7
|
||||
},
|
||||
productPerformance: {
|
||||
topProducts: [
|
||||
{ productId: 1, name: '西藏自驾游', revenue: 12500, orders: 256 },
|
||||
{ productId: 2, name: '小羊认养', revenue: 9800, orders: 189 },
|
||||
{ productId: 3, name: '玫瑰花束', revenue: 7600, orders: 152 }
|
||||
],
|
||||
averageProfitMargin: 35.2,
|
||||
returnOnInvestment: 45.8
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟数据 - 仪表板指标
|
||||
const mockDashboardMetrics: DashboardMetrics = {
|
||||
keyMetrics: {
|
||||
totalUsers: 12560,
|
||||
activeUsers: 8560,
|
||||
totalActivities: 2560,
|
||||
totalOrders: 4560,
|
||||
totalRevenue: 125600,
|
||||
adoptionRate: 57.1,
|
||||
conversionRate: 28.5,
|
||||
engagementRate: 45.2,
|
||||
retentionRate: 78.5
|
||||
},
|
||||
growthMetrics: {
|
||||
userGrowth: 15.8,
|
||||
activityGrowth: 22.5,
|
||||
orderGrowth: 25.8,
|
||||
revenueGrowth: 32.1
|
||||
},
|
||||
performanceMetrics: {
|
||||
systemUptime: '99.95%',
|
||||
averageResponseTime: 120,
|
||||
errorRate: 0.05,
|
||||
conversionRate: 28.5
|
||||
},
|
||||
recentActivities: [
|
||||
{ type: 'user', action: '注册', count: 125, time: '今天' },
|
||||
{ type: 'activity', action: '创建', count: 45, time: '今天' },
|
||||
{ type: 'order', action: '完成', count: 89, time: '今天' },
|
||||
{ type: 'adoption', action: '认养', count: 12, time: '今天' }
|
||||
],
|
||||
alerts: [
|
||||
{ level: 'warning', message: '系统负载较高', time: '2小时前' },
|
||||
{ level: 'info', message: '新版本发布可用', time: '5小时前' }
|
||||
],
|
||||
trends: {
|
||||
userTrend: [1250, 1380, 1560, 1420, 1680, 1920, 2560],
|
||||
revenueTrend: [1200, 1250, 1380, 1420, 1560, 1480, 1620],
|
||||
activityTrend: [45, 52, 48, 56, 62, 58, 65],
|
||||
orderTrend: [89, 92, 85, 96, 102, 98, 112]
|
||||
},
|
||||
forecasts: {
|
||||
userForecast: [2800, 2950, 3100, 3250, 3400],
|
||||
revenueForecast: [4200, 4500, 4800, 5100, 5400],
|
||||
activityForecast: [70, 75, 80, 85, 90],
|
||||
orderForecast: [120, 130, 140, 150, 160]
|
||||
},
|
||||
comparisons: {
|
||||
vsPreviousPeriod: {
|
||||
userGrowth: { current: 15.8, previous: 12.5, growthRate: 26.4, trend: 'up', timeRange: 'month' },
|
||||
revenueGrowth: { current: 32.1, previous: 28.3, growthRate: 13.4, trend: 'up', timeRange: 'month' }
|
||||
},
|
||||
vsIndustryAverage: {
|
||||
userGrowth: { current: 15.8, previous: 10.2, growthRate: 54.9, trend: 'up', timeRange: 'month' },
|
||||
revenueGrowth: { current: 32.1, previous: 25.6, growthRate: 25.4, trend: 'up', timeRange: 'month' }
|
||||
},
|
||||
vsCompetitors: {
|
||||
userGrowth: { current: 15.8, previous: 14.2, growthRate: 11.3, trend: 'up', timeRange: 'month' },
|
||||
revenueGrowth: { current: 32.1, previous: 30.5, growthRate: 5.2, trend: 'up', timeRange: 'month' }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const useStatsStore = defineStore('stats', () => {
|
||||
// 状态
|
||||
const systemStats = ref<SystemStats>(mockSystemStats)
|
||||
const userStats = ref<UserStats>(mockUserStats)
|
||||
const activityStats = ref<ActivityStats>(mockActivityStats)
|
||||
const animalStats = ref<AnimalStats>(mockAnimalStats)
|
||||
const orderStats = ref<OrderStats>(mockOrderStats)
|
||||
const revenueStats = ref<RevenueStats>(mockRevenueStats)
|
||||
const dashboardMetrics = ref<DashboardMetrics>(mockDashboardMetrics)
|
||||
const loading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
// 获取系统统计
|
||||
const fetchSystemStats = async (timeRange?: TimeRange) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 800))
|
||||
|
||||
// 模拟根据时间范围筛选数据
|
||||
let stats = { ...mockSystemStats }
|
||||
|
||||
if (timeRange) {
|
||||
// 这里可以根据时间范围调整统计数据
|
||||
const growthFactor = timeRange === 'today' ? 1 : timeRange === 'week' ? 7 : 30
|
||||
stats.newUsersToday = Math.round(stats.newUsersToday * (timeRange === 'today' ? 1 : growthFactor / 30))
|
||||
stats.todayRevenue = Math.round(stats.todayRevenue * (timeRange === 'today' ? 1 : growthFactor / 30))
|
||||
}
|
||||
|
||||
systemStats.value = stats
|
||||
return stats
|
||||
} catch (err) {
|
||||
error.value = '获取系统统计失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取用户统计
|
||||
const fetchUserStats = async (timeRange?: TimeRange) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 600))
|
||||
|
||||
let stats = { ...mockUserStats }
|
||||
|
||||
if (timeRange) {
|
||||
const growthFactor = timeRange === 'today' ? 1 : timeRange === 'week' ? 7 : 30
|
||||
stats.newUsers.today = Math.round(stats.newUsers.today * (timeRange === 'today' ? 1 : growthFactor / 30))
|
||||
stats.newUsers.thisWeek = Math.round(stats.newUsers.thisWeek * (timeRange === 'week' ? 1 : growthFactor / 7))
|
||||
}
|
||||
|
||||
userStats.value = stats
|
||||
return stats
|
||||
} catch (err) {
|
||||
error.value = '获取用户统计失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取活动统计
|
||||
const fetchActivityStats = async (timeRange?: TimeRange) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 700))
|
||||
|
||||
let stats = { ...mockActivityStats }
|
||||
|
||||
if (timeRange) {
|
||||
const growthFactor = timeRange === 'today' ? 1 : timeRange === 'week' ? 7 : 30
|
||||
stats.totalActivities = Math.round(stats.totalActivities * growthFactor / 30)
|
||||
}
|
||||
|
||||
activityStats.value = stats
|
||||
return stats
|
||||
} catch (err) {
|
||||
error.value = '获取活动统计失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取动物统计
|
||||
const fetchAnimalStats = async (timeRange?: TimeRange) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
|
||||
let stats = { ...mockAnimalStats }
|
||||
|
||||
if (timeRange) {
|
||||
const growthFactor = timeRange === 'today' ? 1 : timeRange === 'week' ? 7 : 30
|
||||
stats.totalAnimals = Math.round(stats.totalAnimals * growthFactor / 30)
|
||||
}
|
||||
|
||||
animalStats.value = stats
|
||||
return stats
|
||||
} catch (err) {
|
||||
error.value = '获取动物统计失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取订单统计
|
||||
const fetchOrderStats = async (timeRange?: TimeRange) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 600))
|
||||
|
||||
let stats = { ...mockOrderStats }
|
||||
|
||||
if (timeRange) {
|
||||
const growthFactor = timeRange === 'today' ? 1 : timeRange === 'week' ? 7 : 30
|
||||
stats.totalOrders = Math.round(stats.totalOrders * growthFactor / 30)
|
||||
}
|
||||
|
||||
orderStats.value = stats
|
||||
return stats
|
||||
} catch (err) {
|
||||
error.value = '获取订单统计失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取收入统计
|
||||
const fetchRevenueStats = async (timeRange?: TimeRange) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 800))
|
||||
|
||||
let stats = { ...mockRevenueStats }
|
||||
|
||||
if (timeRange) {
|
||||
const growthFactor = timeRange === 'today' ? 1 : timeRange === 'week' ? 7 : 30
|
||||
stats.dailyRevenue = Math.round(stats.dailyRevenue * (timeRange === 'today' ? 1 : growthFactor / 30))
|
||||
}
|
||||
|
||||
revenueStats.value = stats
|
||||
return stats
|
||||
} catch (err) {
|
||||
error.value = '获取收入统计失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取仪表板指标
|
||||
const fetchDashboardMetrics = async () => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
|
||||
// 模拟实时数据更新
|
||||
const metrics = {
|
||||
...mockDashboardMetrics,
|
||||
keyMetrics: {
|
||||
...mockDashboardMetrics.keyMetrics,
|
||||
totalUsers: mockSystemStats.totalUsers,
|
||||
activeUsers: mockSystemStats.activeUsers,
|
||||
totalRevenue: mockSystemStats.totalRevenue
|
||||
}
|
||||
}
|
||||
|
||||
dashboardMetrics.value = metrics
|
||||
return metrics
|
||||
} catch (err) {
|
||||
error.value = '获取仪表板指标失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取统计对比
|
||||
const fetchStatsComparison = async (type: 'users' | 'activities' | 'orders' | 'revenue', timeRange: TimeRange) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 600))
|
||||
|
||||
let current: number
|
||||
let previous: number
|
||||
let growthRate: number
|
||||
|
||||
switch (type) {
|
||||
case 'users':
|
||||
current = mockUserStats.newUsers[timeRange === 'today' ? 'today' : timeRange === 'week' ? 'thisWeek' : 'thisMonth']
|
||||
previous = mockUserStats.newUsers[timeRange === 'today' ? 'yesterday' : timeRange === 'week' ? 'lastWeek' : 'lastMonth']
|
||||
break
|
||||
case 'activities':
|
||||
current = mockActivityStats.totalActivities
|
||||
previous = Math.round(current * 0.85)
|
||||
break
|
||||
case 'orders':
|
||||
current = mockOrderStats.totalOrders
|
||||
previous = Math.round(current * 0.8)
|
||||
break
|
||||
case 'revenue':
|
||||
current = mockRevenueStats[timeRange === 'today' ? 'dailyRevenue' : timeRange === 'week' ? 'weeklyRevenue' : 'monthlyRevenue']
|
||||
previous = Math.round(current * 0.75)
|
||||
break
|
||||
default:
|
||||
current = 0
|
||||
previous = 0
|
||||
}
|
||||
|
||||
growthRate = previous > 0 ? ((current - previous) / previous) * 100 : 0
|
||||
|
||||
const comparison: StatsComparison = {
|
||||
current,
|
||||
previous,
|
||||
growthRate,
|
||||
trend: growthRate >= 0 ? 'up' : 'down',
|
||||
timeRange
|
||||
}
|
||||
|
||||
return comparison
|
||||
} catch (err) {
|
||||
error.value = '获取统计对比失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 导出统计数据
|
||||
const exportStats = async (type: 'users' | 'activities' | 'animals' | 'orders' | 'revenue', format: 'csv' | 'excel') => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 1200))
|
||||
|
||||
// 模拟导出操作
|
||||
const data = {
|
||||
users: mockUserStats,
|
||||
activities: mockActivityStats,
|
||||
animals: mockAnimalStats,
|
||||
orders: mockOrderStats,
|
||||
revenue: mockRevenueStats
|
||||
}[type]
|
||||
|
||||
return {
|
||||
filename: `${type}_stats_${new Date().toISOString().split('T')[0]}.${format}`,
|
||||
data,
|
||||
format,
|
||||
exportedAt: new Date().toISOString()
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = '导出统计数据失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 重置错误
|
||||
const clearError = () => {
|
||||
error.value = null
|
||||
}
|
||||
|
||||
return {
|
||||
// 状态
|
||||
systemStats,
|
||||
userStats,
|
||||
activityStats,
|
||||
animalStats,
|
||||
orderStats,
|
||||
revenueStats,
|
||||
dashboardMetrics,
|
||||
loading,
|
||||
error,
|
||||
|
||||
// 操作
|
||||
fetchSystemStats,
|
||||
fetchUserStats,
|
||||
fetchActivityStats,
|
||||
fetchAnimalStats,
|
||||
fetchOrderStats,
|
||||
fetchRevenueStats,
|
||||
fetchDashboardMetrics,
|
||||
fetchStatsComparison,
|
||||
exportStats,
|
||||
clearError
|
||||
}
|
||||
})
|
||||
162
admin-system/src/stores/modules/user.ts
Normal file
162
admin-system/src/stores/modules/user.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import type { User, UserQueryParams, UserStatus } from '../../types/user'
|
||||
|
||||
|
||||
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
// 状态
|
||||
const users = ref<User[]>([])
|
||||
const currentUser = ref<User | null>(null)
|
||||
const loading = ref(false)
|
||||
const totalCount = ref(0)
|
||||
const queryParams = ref<UserQueryParams>({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: '',
|
||||
status: undefined,
|
||||
startDate: '',
|
||||
endDate: ''
|
||||
})
|
||||
|
||||
// 获取用户列表
|
||||
const fetchUsers = async (params?: UserQueryParams) => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 这里应该是API调用,暂时用模拟数据
|
||||
const mockUsers: User[] = [
|
||||
{
|
||||
id: 1,
|
||||
username: 'user1',
|
||||
nickname: '用户1',
|
||||
avatar: '',
|
||||
phone: '13800138000',
|
||||
email: 'user1@example.com',
|
||||
status: 'active',
|
||||
registerTime: '2024-01-01 10:00:00',
|
||||
lastLoginTime: '2024-01-15 15:30:00',
|
||||
userType: 'normal'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
username: 'merchant1',
|
||||
nickname: '商家1',
|
||||
avatar: '',
|
||||
phone: '13900139000',
|
||||
email: 'merchant1@example.com',
|
||||
status: 'active',
|
||||
registerTime: '2024-01-02 09:00:00',
|
||||
lastLoginTime: '2024-01-14 14:20:00',
|
||||
userType: 'merchant'
|
||||
}
|
||||
]
|
||||
|
||||
users.value = mockUsers
|
||||
totalCount.value = mockUsers.length
|
||||
|
||||
if (params) {
|
||||
queryParams.value = { ...queryParams.value, ...params }
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取用户列表失败:', error)
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取用户详情
|
||||
const fetchUserDetail = async (userId: number) => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 模拟API调用
|
||||
const mockUser: User = {
|
||||
id: userId,
|
||||
username: `user${userId}`,
|
||||
nickname: `用户${userId}`,
|
||||
avatar: '',
|
||||
phone: '13800138000',
|
||||
email: `user${userId}@example.com`,
|
||||
status: 'active',
|
||||
registerTime: '2024-01-01 10:00:00',
|
||||
lastLoginTime: '2024-01-15 15:30:00',
|
||||
userType: 'normal'
|
||||
}
|
||||
|
||||
currentUser.value = mockUser
|
||||
return mockUser
|
||||
} catch (error) {
|
||||
console.error('获取用户详情失败:', error)
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 更新用户状态
|
||||
const updateUserStatus = async (userId: number, status: UserStatus) => {
|
||||
try {
|
||||
// 模拟API调用
|
||||
const user = users.value.find((u: User) => u.id === userId)
|
||||
if (user) {
|
||||
user.status = status
|
||||
}
|
||||
|
||||
if (currentUser.value && currentUser.value.id === userId) {
|
||||
currentUser.value.status = status
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('更新用户状态失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索用户
|
||||
const searchUsers = async (keyword: string) => {
|
||||
queryParams.value.keyword = keyword
|
||||
return fetchUsers(queryParams.value)
|
||||
}
|
||||
|
||||
// 重置查询参数
|
||||
const resetQueryParams = () => {
|
||||
queryParams.value = {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: '',
|
||||
status: undefined,
|
||||
startDate: '',
|
||||
endDate: ''
|
||||
}
|
||||
}
|
||||
|
||||
// 导出用户数据
|
||||
const exportUsers = async () => {
|
||||
try {
|
||||
// 模拟导出功能
|
||||
const csvContent = users.value.map(user =>
|
||||
`${user.id},${user.username},${user.nickname},${user.phone},${user.email},${user.status}`
|
||||
).join('\n')
|
||||
|
||||
return csvContent
|
||||
} catch (error) {
|
||||
console.error('导出用户数据失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
users,
|
||||
currentUser,
|
||||
loading,
|
||||
totalCount,
|
||||
queryParams,
|
||||
fetchUsers,
|
||||
fetchUserDetail,
|
||||
updateUserStatus,
|
||||
searchUsers,
|
||||
resetQueryParams,
|
||||
exportUsers
|
||||
}
|
||||
})
|
||||
274
admin-system/src/types/activity.ts
Normal file
274
admin-system/src/types/activity.ts
Normal file
@@ -0,0 +1,274 @@
|
||||
// 活动类型
|
||||
export type ActivityType = 'travel' | 'movie' | 'dinner' | 'game' | 'sports' | 'study' | 'other'
|
||||
|
||||
// 活动状态类型
|
||||
export type ActivityStatus = 'pending' | 'active' | 'completed' | 'cancelled' | 'full'
|
||||
|
||||
// 活动审核状态类型
|
||||
export type ActivityAuditStatus = 'pending' | 'approved' | 'rejected'
|
||||
|
||||
// 活动基本信息接口
|
||||
export interface Activity {
|
||||
id: number
|
||||
title: string
|
||||
activityType: ActivityType
|
||||
description: string
|
||||
organizerId: number
|
||||
organizerName: string
|
||||
location: string
|
||||
startTime: string
|
||||
endTime: string
|
||||
maxParticipants: number
|
||||
currentParticipants: number
|
||||
status: ActivityStatus
|
||||
price: number
|
||||
createTime: string
|
||||
// 扩展信息
|
||||
images?: string[]
|
||||
tags?: string[]
|
||||
requirements?: string
|
||||
// 审核信息
|
||||
auditStatus?: ActivityAuditStatus
|
||||
auditRemark?: string
|
||||
auditTime?: string
|
||||
// 统计信息
|
||||
viewCount?: number
|
||||
likeCount?: number
|
||||
shareCount?: number
|
||||
// 地理位置
|
||||
latitude?: number
|
||||
longitude?: number
|
||||
// 费用说明
|
||||
feeIncludes?: string[]
|
||||
feeExcludes?: string[]
|
||||
// 取消政策
|
||||
cancellationPolicy?: string
|
||||
// 联系方式
|
||||
contactPhone?: string
|
||||
contactWechat?: string
|
||||
}
|
||||
|
||||
// 活动查询参数接口
|
||||
export interface ActivityQueryParams {
|
||||
page: number
|
||||
pageSize: number
|
||||
keyword?: string
|
||||
status?: ActivityStatus
|
||||
activityType?: ActivityType
|
||||
organizerId?: number
|
||||
minPrice?: number
|
||||
maxPrice?: number
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
location?: string
|
||||
sortField?: string
|
||||
sortOrder?: 'asc' | 'desc'
|
||||
}
|
||||
|
||||
// 活动统计信息接口
|
||||
export interface ActivityStatistics {
|
||||
totalActivities: number
|
||||
activeActivities: number
|
||||
completedActivities: number
|
||||
cancelledActivities: number
|
||||
totalParticipants: number
|
||||
totalRevenue: number
|
||||
averageParticipation: number
|
||||
activityTypeDistribution: Record<ActivityType, number>
|
||||
statusDistribution: Record<ActivityStatus, number>
|
||||
monthlyGrowth: number
|
||||
popularActivities: Activity[]
|
||||
participantDemographics: {
|
||||
ageDistribution: Record<string, number>
|
||||
genderDistribution: Record<string, number>
|
||||
locationDistribution: Record<string, number>
|
||||
}
|
||||
}
|
||||
|
||||
// 活动参与记录接口
|
||||
export interface ActivityParticipation {
|
||||
id: number
|
||||
activityId: number
|
||||
activityTitle: string
|
||||
userId: number
|
||||
username: string
|
||||
joinTime: string
|
||||
status: 'confirmed' | 'waiting' | 'cancelled'
|
||||
paymentStatus: 'paid' | 'pending' | 'refunded'
|
||||
paymentAmount: number
|
||||
paymentTime?: string
|
||||
// 评价信息
|
||||
rating?: number
|
||||
review?: string
|
||||
reviewTime?: string
|
||||
// 取消信息
|
||||
cancelReason?: string
|
||||
cancelTime?: string
|
||||
// 参与信息
|
||||
participantsCount?: number
|
||||
groupMembers?: number[]
|
||||
}
|
||||
|
||||
// 活动评价接口
|
||||
export interface ActivityReview {
|
||||
id: number
|
||||
activityId: number
|
||||
activityTitle: string
|
||||
userId: number
|
||||
username: string
|
||||
organizerId: number
|
||||
organizerName: string
|
||||
rating: number
|
||||
review: string
|
||||
reviewTime: string
|
||||
isAnonymous: boolean
|
||||
// 回复信息
|
||||
reply?: string
|
||||
replyTime?: string
|
||||
// 有用性统计
|
||||
helpfulCount: number
|
||||
reportCount: number
|
||||
status: 'normal' | 'hidden' | 'deleted'
|
||||
}
|
||||
|
||||
// 活动组织者接口
|
||||
export interface ActivityOrganizer {
|
||||
id: number
|
||||
username: string
|
||||
nickname: string
|
||||
avatar: string
|
||||
organizerLevel: number
|
||||
totalActivities: number
|
||||
completedActivities: number
|
||||
cancellationRate: number
|
||||
averageRating: number
|
||||
totalParticipants: number
|
||||
joinTime: string
|
||||
// 认证信息
|
||||
isVerified: boolean
|
||||
verificationLevel: number
|
||||
// 联系方式
|
||||
contactPhone?: string
|
||||
contactEmail?: string
|
||||
// 组织者描述
|
||||
description?: string
|
||||
specialties?: string[]
|
||||
}
|
||||
|
||||
// 活动类型统计接口
|
||||
export interface ActivityTypeStats {
|
||||
activityType: ActivityType
|
||||
count: number
|
||||
totalParticipants: number
|
||||
totalRevenue: number
|
||||
averageRating: number
|
||||
completionRate: number
|
||||
}
|
||||
|
||||
// 活动时间统计接口
|
||||
export interface ActivityTimeStats {
|
||||
date: string
|
||||
activityCount: number
|
||||
participantCount: number
|
||||
revenue: number
|
||||
peakHours: number[]
|
||||
}
|
||||
|
||||
// 活动地点统计接口
|
||||
export interface ActivityLocationStats {
|
||||
location: string
|
||||
activityCount: number
|
||||
participantCount: number
|
||||
averageRating: number
|
||||
totalRevenue: number
|
||||
}
|
||||
|
||||
// 活动审核记录接口
|
||||
export interface ActivityAuditRecord {
|
||||
id: number
|
||||
activityId: number
|
||||
activityTitle: string
|
||||
auditStatus: ActivityAuditStatus
|
||||
auditRemark?: string
|
||||
auditor: string
|
||||
auditTime: string
|
||||
previousStatus?: ActivityStatus
|
||||
// 审核详情
|
||||
auditDetails?: {
|
||||
contentCheck: boolean
|
||||
safetyCheck: boolean
|
||||
complianceCheck: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// 活动取消记录接口
|
||||
export interface ActivityCancellationRecord {
|
||||
id: number
|
||||
activityId: number
|
||||
activityTitle: string
|
||||
cancelReason: string
|
||||
cancelTime: string
|
||||
cancelledBy: 'organizer' | 'system' | 'participant'
|
||||
refundStatus: 'pending' | 'processed' | 'completed'
|
||||
refundAmount: number
|
||||
affectedParticipants: number
|
||||
// 取消详情
|
||||
weatherConditions?: string
|
||||
safetyConcerns?: string
|
||||
otherReasons?: string
|
||||
}
|
||||
|
||||
// 活动推荐参数接口
|
||||
export interface ActivityRecommendationParams {
|
||||
userId?: number
|
||||
location?: string
|
||||
preferredTypes?: ActivityType[]
|
||||
budgetRange?: [number, number]
|
||||
timeRange?: [string, string]
|
||||
participantCount?: number
|
||||
excludeAttended?: boolean
|
||||
}
|
||||
|
||||
// 活动导出参数接口
|
||||
export interface ActivityExportParams {
|
||||
format: 'csv' | 'excel'
|
||||
fields: string[]
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
status?: ActivityStatus
|
||||
activityType?: ActivityType
|
||||
organizerId?: number
|
||||
}
|
||||
|
||||
// 活动参与统计接口
|
||||
export interface ActivityParticipationStats {
|
||||
date: string
|
||||
participationCount: number
|
||||
newParticipants: number
|
||||
repeatParticipants: number
|
||||
participationRate: number
|
||||
cancellationRate: number
|
||||
averageGroupSize: number
|
||||
}
|
||||
|
||||
// 活动收入统计接口
|
||||
export interface ActivityRevenueStats {
|
||||
date: string
|
||||
revenue: number
|
||||
activityCount: number
|
||||
averageRevenuePerActivity: number
|
||||
refundAmount: number
|
||||
netRevenue: number
|
||||
}
|
||||
|
||||
// 活动运营指标接口
|
||||
export interface ActivityOperationMetrics {
|
||||
date: string
|
||||
newActivities: number
|
||||
completedActivities: number
|
||||
participantSatisfaction: number
|
||||
organizerSatisfaction: number
|
||||
activityCompletionRate: number
|
||||
revenuePerParticipant: number
|
||||
marketingEffectiveness: number
|
||||
}
|
||||
231
admin-system/src/types/animal.ts
Normal file
231
admin-system/src/types/animal.ts
Normal file
@@ -0,0 +1,231 @@
|
||||
// 动物类型
|
||||
export type AnimalType = 'sheep' | 'cow' | 'pig' | 'chicken' | 'goat' | 'duck' | 'other'
|
||||
|
||||
// 动物状态类型
|
||||
export type AnimalStatus = 'available' | 'adopted' | 'reserved' | 'inactive'
|
||||
|
||||
// 动物性别类型
|
||||
export type AnimalGender = 'male' | 'female' | 'unknown'
|
||||
|
||||
// 健康状态类型
|
||||
export type HealthStatus = 'excellent' | 'good' | 'fair' | 'poor' | 'critical'
|
||||
|
||||
// 动物基本信息接口
|
||||
export interface Animal {
|
||||
id: number
|
||||
name: string
|
||||
animalType: AnimalType
|
||||
breed: string
|
||||
age: number
|
||||
gender: AnimalGender
|
||||
status: AnimalStatus
|
||||
farmId: number
|
||||
farmName: string
|
||||
price: number
|
||||
description: string
|
||||
images: string[]
|
||||
healthStatus: HealthStatus
|
||||
adoptionCount: number
|
||||
createTime: string
|
||||
// 扩展信息
|
||||
features?: string[]
|
||||
personality?: string
|
||||
favoriteFood?: string
|
||||
// 医疗记录
|
||||
vaccinationRecords?: VaccinationRecord[]
|
||||
medicalHistory?: MedicalRecord[]
|
||||
// 认养信息
|
||||
currentAdopter?: string
|
||||
adoptionStartTime?: string
|
||||
adoptionEndTime?: string
|
||||
// 位置信息
|
||||
location?: string
|
||||
gpsCoordinates?: string
|
||||
}
|
||||
|
||||
// 动物查询参数接口
|
||||
export interface AnimalQueryParams {
|
||||
page: number
|
||||
pageSize: number
|
||||
keyword?: string
|
||||
status?: AnimalStatus
|
||||
animalType?: AnimalType
|
||||
farmId?: number
|
||||
minAge?: number
|
||||
maxAge?: number
|
||||
gender?: AnimalGender
|
||||
minPrice?: number
|
||||
maxPrice?: number
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
sortField?: string
|
||||
sortOrder?: 'asc' | 'desc'
|
||||
}
|
||||
|
||||
// 疫苗接种记录接口
|
||||
export interface VaccinationRecord {
|
||||
id: number
|
||||
animalId: number
|
||||
vaccineName: string
|
||||
vaccinationDate: string
|
||||
nextVaccinationDate: string
|
||||
veterinarian: string
|
||||
notes?: string
|
||||
}
|
||||
|
||||
// 医疗记录接口
|
||||
export interface MedicalRecord {
|
||||
id: number
|
||||
animalId: number
|
||||
diagnosis: string
|
||||
treatment: string
|
||||
treatmentDate: string
|
||||
veterinarian: string
|
||||
cost: number
|
||||
notes?: string
|
||||
followUpDate?: string
|
||||
}
|
||||
|
||||
// 动物统计信息接口
|
||||
export interface AnimalStatistics {
|
||||
totalAnimals: number
|
||||
availableAnimals: number
|
||||
adoptedAnimals: number
|
||||
totalAdoptions: number
|
||||
averageAdoptionPrice: number
|
||||
animalTypeDistribution: Record<AnimalType, number>
|
||||
statusDistribution: Record<AnimalStatus, number>
|
||||
healthStatusDistribution: Record<HealthStatus, number>
|
||||
monthlyAdoptions: number
|
||||
adoptionGrowthRate: number
|
||||
topAdoptedAnimals: Animal[]
|
||||
}
|
||||
|
||||
// 动物类型统计接口
|
||||
export interface AnimalTypeStats {
|
||||
animalType: AnimalType
|
||||
count: number
|
||||
adoptionCount: number
|
||||
averagePrice: number
|
||||
adoptionRate: number
|
||||
}
|
||||
|
||||
// 农场动物统计接口
|
||||
export interface FarmAnimalStats {
|
||||
farmId: number
|
||||
farmName: string
|
||||
totalAnimals: number
|
||||
availableAnimals: number
|
||||
adoptedAnimals: number
|
||||
totalAdoptions: number
|
||||
averageAdoptionPrice: number
|
||||
}
|
||||
|
||||
// 动物健康统计接口
|
||||
export interface AnimalHealthStats {
|
||||
date: string
|
||||
excellentCount: number
|
||||
goodCount: number
|
||||
fairCount: number
|
||||
poorCount: number
|
||||
criticalCount: number
|
||||
vaccinationRate: number
|
||||
}
|
||||
|
||||
// 动物认养记录接口
|
||||
export interface AnimalAdoptionRecord {
|
||||
id: number
|
||||
animalId: number
|
||||
animalName: string
|
||||
userId: number
|
||||
username: string
|
||||
startTime: string
|
||||
endTime: string
|
||||
duration: number
|
||||
totalCost: number
|
||||
status: 'active' | 'completed' | 'cancelled'
|
||||
paymentStatus: 'paid' | 'pending' | 'refunded'
|
||||
adoptionType: 'individual' | 'group'
|
||||
groupMembers?: number[]
|
||||
}
|
||||
|
||||
// 动物成长记录接口
|
||||
export interface AnimalGrowthRecord {
|
||||
id: number
|
||||
animalId: number
|
||||
recordDate: string
|
||||
weight: number
|
||||
height: number
|
||||
condition: string
|
||||
notes?: string
|
||||
photos?: string[]
|
||||
}
|
||||
|
||||
// 动物行为记录接口
|
||||
export interface AnimalBehaviorRecord {
|
||||
id: number
|
||||
animalId: number
|
||||
recordDate: string
|
||||
behaviorType: string
|
||||
duration: number
|
||||
notes?: string
|
||||
videoUrl?: string
|
||||
}
|
||||
|
||||
// 动物喂养记录接口
|
||||
export interface AnimalFeedingRecord {
|
||||
id: number
|
||||
animalId: number
|
||||
feedingTime: string
|
||||
foodType: string
|
||||
quantity: number
|
||||
feeder: string
|
||||
notes?: string
|
||||
}
|
||||
|
||||
// 动物活动记录接口
|
||||
export interface AnimalActivityRecord {
|
||||
id: number
|
||||
animalId: number
|
||||
activityType: string
|
||||
startTime: string
|
||||
endTime: string
|
||||
duration: number
|
||||
caloriesBurned?: number
|
||||
notes?: string
|
||||
}
|
||||
|
||||
// 动物导出参数接口
|
||||
export interface AnimalExportParams {
|
||||
format: 'csv' | 'excel'
|
||||
fields: string[]
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
status?: AnimalStatus
|
||||
animalType?: AnimalType
|
||||
farmId?: number
|
||||
}
|
||||
|
||||
// 动物认养统计接口
|
||||
export interface AnimalAdoptionStats {
|
||||
date: string
|
||||
adoptionCount: number
|
||||
totalRevenue: number
|
||||
averageAdoptionDuration: number
|
||||
popularAnimalTypes: string[]
|
||||
peakAdoptionHours: number[]
|
||||
}
|
||||
|
||||
// 动物健康预警接口
|
||||
export interface AnimalHealthAlert {
|
||||
id: number
|
||||
animalId: number
|
||||
animalName: string
|
||||
alertType: 'vaccination' | 'medical' | 'nutrition' | 'behavior'
|
||||
alertLevel: 'low' | 'medium' | 'high' | 'critical'
|
||||
alertMessage: string
|
||||
triggerTime: string
|
||||
resolved: boolean
|
||||
resolveTime?: string
|
||||
resolveNotes?: string
|
||||
}
|
||||
313
admin-system/src/types/content.ts
Normal file
313
admin-system/src/types/content.ts
Normal file
@@ -0,0 +1,313 @@
|
||||
// 内容类型
|
||||
export type ContentType = 'article' | 'post' | 'activity' | 'comment' | 'review'
|
||||
|
||||
// 内容状态类型
|
||||
export type ContentStatus = 'published' | 'draft' | 'deleted' | 'archived'
|
||||
|
||||
// 内容审核状态类型
|
||||
export type ContentAuditStatus = 'pending' | 'approved' | 'rejected'
|
||||
|
||||
// 内容分类类型
|
||||
export type ContentCategory = 'travel' | 'animal' | 'entertainment' | 'food' | 'sports' | 'study' | 'other'
|
||||
|
||||
// 内容基本信息接口
|
||||
export interface Content {
|
||||
id: number
|
||||
title: string
|
||||
content: string
|
||||
contentType: ContentType
|
||||
authorId: number
|
||||
authorName: string
|
||||
publishTime: string
|
||||
status: ContentStatus
|
||||
viewCount: number
|
||||
likeCount: number
|
||||
commentCount: number
|
||||
shareCount: number
|
||||
auditStatus: ContentAuditStatus
|
||||
auditRemark?: string
|
||||
auditTime?: string
|
||||
// 分类信息
|
||||
category: ContentCategory
|
||||
tags?: string[]
|
||||
// 多媒体内容
|
||||
images?: string[]
|
||||
videos?: string[]
|
||||
// 地理位置
|
||||
location?: string
|
||||
latitude?: number
|
||||
longitude?: number
|
||||
// 关联信息
|
||||
relatedActivityId?: number
|
||||
relatedAnimalId?: number
|
||||
// 隐私设置
|
||||
isPublic: boolean
|
||||
allowComments: boolean
|
||||
allowSharing: boolean
|
||||
// 编辑信息
|
||||
lastEditTime?: string
|
||||
editCount: number
|
||||
// SEO信息
|
||||
metaDescription?: string
|
||||
metaKeywords?: string[]
|
||||
}
|
||||
|
||||
// 内容查询参数接口
|
||||
export interface ContentQueryParams {
|
||||
page: number
|
||||
pageSize: number
|
||||
keyword?: string
|
||||
status?: ContentStatus
|
||||
contentType?: ContentType
|
||||
auditStatus?: ContentAuditStatus
|
||||
authorId?: number
|
||||
category?: ContentCategory
|
||||
startTime?: string
|
||||
endTime?: string
|
||||
sortField?: string
|
||||
sortOrder?: 'asc' | 'desc'
|
||||
// 高级筛选
|
||||
minViews?: number
|
||||
minLikes?: number
|
||||
minComments?: number
|
||||
hasImages?: boolean
|
||||
hasVideos?: boolean
|
||||
// 地理位置筛选
|
||||
location?: string
|
||||
radius?: number
|
||||
}
|
||||
|
||||
// 内容统计信息接口
|
||||
export interface ContentStatistics {
|
||||
totalContents: number
|
||||
publishedContents: number
|
||||
draftContents: number
|
||||
deletedContents: number
|
||||
totalViews: number
|
||||
totalLikes: number
|
||||
totalComments: number
|
||||
totalShares: number
|
||||
averageEngagementRate: number
|
||||
contentTypeDistribution: Record<ContentType, number>
|
||||
categoryDistribution: Record<ContentCategory, number>
|
||||
dailyGrowth: number
|
||||
topContents: Content[]
|
||||
authorDistribution: {
|
||||
totalAuthors: number
|
||||
activeAuthors: number
|
||||
newAuthorsThisMonth: number
|
||||
topAuthors: Array<{
|
||||
authorId: number
|
||||
authorName: string
|
||||
contentCount: number
|
||||
totalViews: number
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
// 内容审核记录接口
|
||||
export interface ContentAuditRecord {
|
||||
id: number
|
||||
contentId: number
|
||||
contentTitle: string
|
||||
auditStatus: ContentAuditStatus
|
||||
auditRemark?: string
|
||||
auditor: string
|
||||
auditTime: string
|
||||
previousStatus?: ContentStatus
|
||||
// 审核详情
|
||||
auditDetails?: {
|
||||
contentQuality: number
|
||||
complianceScore: number
|
||||
safetyCheck: boolean
|
||||
spamCheck: boolean
|
||||
}
|
||||
// 违规信息
|
||||
violationType?: string
|
||||
violationLevel?: 'minor' | 'major' | 'critical'
|
||||
penaltyApplied?: string
|
||||
}
|
||||
|
||||
// 内容举报接口
|
||||
export interface ContentReport {
|
||||
id: number
|
||||
contentId: number
|
||||
contentTitle: string
|
||||
reporterId: number
|
||||
reporterName: string
|
||||
reportReason: string
|
||||
reportTime: string
|
||||
reportStatus: 'pending' | 'processing' | 'resolved' | 'dismissed'
|
||||
// 举报详情
|
||||
reportDetails?: {
|
||||
violationType: string
|
||||
evidence?: string[]
|
||||
additionalComments?: string
|
||||
}
|
||||
// 处理信息
|
||||
handledBy?: string
|
||||
handleTime?: string
|
||||
handleResult?: string
|
||||
penaltyApplied?: string
|
||||
// 反馈信息
|
||||
reporterNotified: boolean
|
||||
feedbackProvided?: string
|
||||
}
|
||||
|
||||
// 内容分类统计接口
|
||||
export interface ContentCategoryStats {
|
||||
category: ContentCategory
|
||||
totalContents: number
|
||||
publishedContents: number
|
||||
averageViews: number
|
||||
averageLikes: number
|
||||
averageComments: number
|
||||
engagementRate: number
|
||||
topAuthors: Array<{
|
||||
authorId: number
|
||||
authorName: string
|
||||
contentCount: number
|
||||
}>
|
||||
popularTags: string[]
|
||||
growthTrend: number
|
||||
}
|
||||
|
||||
// 内容作者统计接口
|
||||
export interface ContentAuthorStats {
|
||||
authorId: number
|
||||
authorName: string
|
||||
totalContents: number
|
||||
publishedContents: number
|
||||
totalViews: number
|
||||
totalLikes: number
|
||||
totalComments: number
|
||||
averageEngagement: number
|
||||
followerCount: number
|
||||
joinDate: string
|
||||
lastActivity: string
|
||||
// 内容质量指标
|
||||
contentQualityScore: number
|
||||
complianceRate: number
|
||||
// 认证信息
|
||||
isVerified: boolean
|
||||
verificationLevel: number
|
||||
// 作者分类分布
|
||||
categoryDistribution: Record<ContentCategory, number>
|
||||
}
|
||||
|
||||
// 内容时间统计接口
|
||||
export interface ContentTimeStats {
|
||||
date: string
|
||||
contentCount: number
|
||||
viewCount: number
|
||||
likeCount: number
|
||||
commentCount: number
|
||||
shareCount: number
|
||||
newAuthors: number
|
||||
peakHours: number[]
|
||||
engagementRate: number
|
||||
}
|
||||
|
||||
// 内容标签统计接口
|
||||
export interface ContentTagStats {
|
||||
tag: string
|
||||
usageCount: number
|
||||
averageViews: number
|
||||
averageLikes: number
|
||||
averageComments: number
|
||||
relatedTags: string[]
|
||||
popularityTrend: number
|
||||
// 分类关联
|
||||
associatedCategories: ContentCategory[]
|
||||
// 作者使用情况
|
||||
topAuthors: Array<{
|
||||
authorId: number
|
||||
authorName: string
|
||||
usageCount: number
|
||||
}>
|
||||
}
|
||||
|
||||
// 内容推荐参数接口
|
||||
export interface ContentRecommendationParams {
|
||||
userId?: number
|
||||
preferredCategories?: ContentCategory[]
|
||||
preferredTags?: string[]
|
||||
location?: string
|
||||
timeRange?: [string, string]
|
||||
contentType?: ContentType
|
||||
minQualityScore?: number
|
||||
excludeViewed?: boolean
|
||||
limit?: number
|
||||
}
|
||||
|
||||
// 内容导出参数接口
|
||||
export interface ContentExportParams {
|
||||
format: 'csv' | 'excel' | 'json'
|
||||
fields: string[]
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
status?: ContentStatus
|
||||
contentType?: ContentType
|
||||
category?: ContentCategory
|
||||
authorId?: number
|
||||
}
|
||||
|
||||
// 内容审核参数接口
|
||||
export interface ContentAuditParams {
|
||||
contentId: number
|
||||
auditStatus: ContentAuditStatus
|
||||
auditRemark?: string
|
||||
violationType?: string
|
||||
violationLevel?: 'minor' | 'major' | 'critical'
|
||||
penalty?: string
|
||||
notifyAuthor: boolean
|
||||
}
|
||||
|
||||
// 内容批量操作参数接口
|
||||
export interface ContentBatchOperationParams {
|
||||
contentIds: number[]
|
||||
operation: 'publish' | 'draft' | 'delete' | 'approve' | 'reject'
|
||||
auditRemark?: string
|
||||
notifyAuthors: boolean
|
||||
}
|
||||
|
||||
// 内容搜索参数接口
|
||||
export interface ContentSearchParams {
|
||||
query: string
|
||||
filters?: {
|
||||
contentType?: ContentType
|
||||
category?: ContentCategory
|
||||
status?: ContentStatus
|
||||
authorId?: number
|
||||
dateRange?: [string, string]
|
||||
minViews?: number
|
||||
minLikes?: number
|
||||
}
|
||||
sortBy?: 'relevance' | 'date' | 'views' | 'likes' | 'comments'
|
||||
sortOrder?: 'asc' | 'desc'
|
||||
page?: number
|
||||
pageSize?: number
|
||||
}
|
||||
|
||||
// 内容分析报告接口
|
||||
export interface ContentAnalysisReport {
|
||||
period: string
|
||||
totalContents: number
|
||||
totalViews: number
|
||||
totalEngagement: number
|
||||
averageQualityScore: number
|
||||
topPerformingContents: Content[]
|
||||
topAuthors: ContentAuthorStats[]
|
||||
categoryPerformance: ContentCategoryStats[]
|
||||
tagAnalysis: ContentTagStats[]
|
||||
timeAnalysis: ContentTimeStats[]
|
||||
recommendations: string[]
|
||||
// 合规性指标
|
||||
complianceRate: number
|
||||
auditPassRate: number
|
||||
reportResolutionRate: number
|
||||
// 增长指标
|
||||
growthRate: number
|
||||
retentionRate: number
|
||||
churnRate: number
|
||||
}
|
||||
163
admin-system/src/types/merchant.ts
Normal file
163
admin-system/src/types/merchant.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
// 商家状态类型
|
||||
export type MerchantStatus = 'active' | 'inactive' | 'banned'
|
||||
|
||||
// 商家审核状态类型
|
||||
export type MerchantAuditStatus = 'pending' | 'approved' | 'rejected'
|
||||
|
||||
// 商家分类类型
|
||||
export type MerchantCategory = 'flower' | 'farm' | 'activity' | 'travel' | 'other'
|
||||
|
||||
// 商家基本信息接口
|
||||
export interface Merchant {
|
||||
id: number
|
||||
userId: number
|
||||
shopName: string
|
||||
businessLicense: string
|
||||
contactPerson: string
|
||||
contactPhone: string
|
||||
contactEmail: string
|
||||
address: string
|
||||
description: string
|
||||
status: MerchantStatus
|
||||
auditStatus: MerchantAuditStatus
|
||||
auditTime?: string
|
||||
auditRemark?: string
|
||||
registerTime: string
|
||||
category: MerchantCategory
|
||||
// 统计信息
|
||||
serviceScore: number
|
||||
orderCount: number
|
||||
totalRevenue: number
|
||||
// 扩展信息
|
||||
businessHours?: string
|
||||
deliveryRange?: string
|
||||
minimumOrder?: number
|
||||
// 资质文件
|
||||
licenseImage?: string
|
||||
idCardImage?: string
|
||||
// 银行信息
|
||||
bankAccount?: string
|
||||
bankName?: string
|
||||
accountHolder?: string
|
||||
}
|
||||
|
||||
// 商家查询参数接口
|
||||
export interface MerchantQueryParams {
|
||||
page: number
|
||||
pageSize: number
|
||||
keyword?: string
|
||||
status?: MerchantStatus
|
||||
auditStatus?: MerchantAuditStatus
|
||||
category?: MerchantCategory
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
sortField?: string
|
||||
sortOrder?: 'asc' | 'desc'
|
||||
}
|
||||
|
||||
// 商家审核参数接口
|
||||
export interface MerchantAuditParams {
|
||||
merchantId: number
|
||||
auditStatus: MerchantAuditStatus
|
||||
auditRemark?: string
|
||||
}
|
||||
|
||||
// 商家统计信息接口
|
||||
export interface MerchantStatistics {
|
||||
totalMerchants: number
|
||||
activeMerchants: number
|
||||
pendingAudit: number
|
||||
rejected: number
|
||||
totalRevenue: number
|
||||
averageScore: number
|
||||
categoryDistribution: Record<MerchantCategory, number>
|
||||
monthlyGrowth: number
|
||||
dailyActiveMerchants: number
|
||||
topMerchants: Merchant[]
|
||||
}
|
||||
|
||||
// 商家收入统计接口
|
||||
export interface MerchantRevenueStats {
|
||||
date: string
|
||||
revenue: number
|
||||
orderCount: number
|
||||
averageOrderValue: number
|
||||
}
|
||||
|
||||
// 商家服务评分统计接口
|
||||
export interface MerchantScoreStats {
|
||||
date: string
|
||||
averageScore: number
|
||||
reviewCount: number
|
||||
fiveStarCount: number
|
||||
oneStarCount: number
|
||||
}
|
||||
|
||||
// 商家分类统计接口
|
||||
export interface MerchantCategoryStats {
|
||||
category: MerchantCategory
|
||||
count: number
|
||||
totalRevenue: number
|
||||
averageScore: number
|
||||
}
|
||||
|
||||
// 商家审核记录接口
|
||||
export interface MerchantAuditRecord {
|
||||
id: number
|
||||
merchantId: number
|
||||
shopName: string
|
||||
auditStatus: MerchantAuditStatus
|
||||
auditRemark?: string
|
||||
auditor: string
|
||||
auditTime: string
|
||||
previousStatus?: MerchantAuditStatus
|
||||
}
|
||||
|
||||
// 商家操作日志接口
|
||||
export interface MerchantOperationLog {
|
||||
id: number
|
||||
merchantId: number
|
||||
shopName: string
|
||||
operationType: string
|
||||
operationDetail: string
|
||||
operationTime: string
|
||||
operator: string
|
||||
result: 'success' | 'failure'
|
||||
}
|
||||
|
||||
// 商家资质文件接口
|
||||
export interface MerchantQualification {
|
||||
id: number
|
||||
merchantId: number
|
||||
fileType: 'license' | 'idCard' | 'bank' | 'other'
|
||||
fileName: string
|
||||
fileUrl: string
|
||||
uploadTime: string
|
||||
status: 'verified' | 'pending' | 'rejected'
|
||||
verifyRemark?: string
|
||||
verifyTime?: string
|
||||
}
|
||||
|
||||
// 商家银行信息接口
|
||||
export interface MerchantBankInfo {
|
||||
id: number
|
||||
merchantId: number
|
||||
bankName: string
|
||||
bankAccount: string
|
||||
accountHolder: string
|
||||
branchName?: string
|
||||
status: 'verified' | 'pending' | 'rejected'
|
||||
verifyRemark?: string
|
||||
verifyTime?: string
|
||||
}
|
||||
|
||||
// 商家导出参数接口
|
||||
export interface MerchantExportParams {
|
||||
format: 'csv' | 'excel'
|
||||
fields: string[]
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
status?: MerchantStatus
|
||||
auditStatus?: MerchantAuditStatus
|
||||
category?: MerchantCategory
|
||||
}
|
||||
209
admin-system/src/types/order.ts
Normal file
209
admin-system/src/types/order.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
// 订单状态类型
|
||||
export type OrderStatus = 'pending' | 'paid' | 'shipped' | 'completed' | 'cancelled' | 'refunded'
|
||||
|
||||
// 订单类型
|
||||
export type OrderType = 'flower' | 'animal' | 'activity' | 'travel' | 'other'
|
||||
|
||||
// 支付方式类型
|
||||
export type PaymentMethod = 'wechat' | 'alipay' | 'bank' | 'balance'
|
||||
|
||||
// 支付状态类型
|
||||
export type PaymentStatus = 'pending' | 'paid' | 'failed' | 'refunded'
|
||||
|
||||
// 订单基本信息接口
|
||||
export interface Order {
|
||||
id: number
|
||||
orderNo: string
|
||||
userId: number
|
||||
username: string
|
||||
merchantId: number
|
||||
shopName: string
|
||||
orderType: OrderType
|
||||
productName: string
|
||||
quantity: number
|
||||
unitPrice: number
|
||||
totalAmount: number
|
||||
status: OrderStatus
|
||||
createTime: string
|
||||
payTime?: string
|
||||
completeTime?: string
|
||||
cancelTime?: string
|
||||
// 支付信息
|
||||
paymentMethod?: PaymentMethod
|
||||
paymentStatus?: PaymentStatus
|
||||
transactionNo?: string
|
||||
// 收货信息
|
||||
recipientName: string
|
||||
recipientPhone: string
|
||||
deliveryAddress: string
|
||||
deliveryTime?: string
|
||||
// 附加信息
|
||||
message?: string
|
||||
remark?: string
|
||||
// 评价信息
|
||||
rating?: number
|
||||
review?: string
|
||||
reviewTime?: string
|
||||
// 退款信息
|
||||
refundAmount?: number
|
||||
refundReason?: string
|
||||
refundTime?: string
|
||||
// 物流信息
|
||||
trackingNo?: string
|
||||
shippingCompany?: string
|
||||
}
|
||||
|
||||
// 订单查询参数接口
|
||||
export interface OrderQueryParams {
|
||||
page: number
|
||||
pageSize: number
|
||||
keyword?: string
|
||||
status?: OrderStatus
|
||||
orderType?: OrderType
|
||||
merchantId?: number
|
||||
userId?: number
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
sortField?: string
|
||||
sortOrder?: 'asc' | 'desc'
|
||||
}
|
||||
|
||||
// 订单统计信息接口
|
||||
export interface OrderStatistics {
|
||||
totalOrders: number
|
||||
pendingOrders: number
|
||||
completedOrders: number
|
||||
cancelledOrders: number
|
||||
totalRevenue: number
|
||||
averageOrderValue: number
|
||||
dailyOrderCount: number
|
||||
orderTypeDistribution: Record<OrderType, number>
|
||||
statusDistribution: Record<OrderStatus, number>
|
||||
monthlyGrowth: number
|
||||
topProducts: string[]
|
||||
revenueTrend: Array<{
|
||||
date: string
|
||||
revenue: number
|
||||
orderCount: number
|
||||
}>
|
||||
}
|
||||
|
||||
// 订单收入统计接口
|
||||
export interface OrderRevenueStats {
|
||||
date: string
|
||||
revenue: number
|
||||
orderCount: number
|
||||
averageOrderValue: number
|
||||
refundAmount: number
|
||||
netRevenue: number
|
||||
}
|
||||
|
||||
// 订单类型统计接口
|
||||
export interface OrderTypeStats {
|
||||
orderType: OrderType
|
||||
count: number
|
||||
totalRevenue: number
|
||||
averageOrderValue: number
|
||||
completionRate: number
|
||||
}
|
||||
|
||||
// 订单状态统计接口
|
||||
export interface OrderStatusStats {
|
||||
status: OrderStatus
|
||||
count: number
|
||||
percentage: number
|
||||
}
|
||||
|
||||
// 订单支付统计接口
|
||||
export interface OrderPaymentStats {
|
||||
paymentMethod: PaymentMethod
|
||||
count: number
|
||||
totalAmount: number
|
||||
successRate: number
|
||||
}
|
||||
|
||||
// 订单退款统计接口
|
||||
export interface OrderRefundStats {
|
||||
date: string
|
||||
refundCount: number
|
||||
refundAmount: number
|
||||
refundRate: number
|
||||
mainReasons: string[]
|
||||
}
|
||||
|
||||
// 订单操作记录接口
|
||||
export interface OrderOperationLog {
|
||||
id: number
|
||||
orderId: number
|
||||
orderNo: string
|
||||
operationType: string
|
||||
operationDetail: string
|
||||
operationTime: string
|
||||
operator: string
|
||||
result: 'success' | 'failure'
|
||||
remark?: string
|
||||
}
|
||||
|
||||
// 订单导出参数接口
|
||||
export interface OrderExportParams {
|
||||
format: 'csv' | 'excel'
|
||||
fields: string[]
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
status?: OrderStatus
|
||||
orderType?: OrderType
|
||||
merchantId?: number
|
||||
}
|
||||
|
||||
// 订单退款申请接口
|
||||
export interface OrderRefundApplication {
|
||||
id: number
|
||||
orderId: number
|
||||
orderNo: string
|
||||
userId: number
|
||||
username: string
|
||||
refundAmount: number
|
||||
refundReason: string
|
||||
status: 'pending' | 'approved' | 'rejected' | 'completed'
|
||||
applyTime: string
|
||||
processTime?: string
|
||||
processRemark?: string
|
||||
processor?: string
|
||||
}
|
||||
|
||||
// 订单评价接口
|
||||
export interface OrderReview {
|
||||
id: number
|
||||
orderId: number
|
||||
userId: number
|
||||
username: string
|
||||
merchantId: number
|
||||
shopName: string
|
||||
rating: number
|
||||
review: string
|
||||
reviewTime: string
|
||||
isAnonymous: boolean
|
||||
reply?: string
|
||||
replyTime?: string
|
||||
helpfulCount: number
|
||||
reportCount: number
|
||||
status: 'normal' | 'hidden' | 'deleted'
|
||||
}
|
||||
|
||||
// 订单物流信息接口
|
||||
export interface OrderShippingInfo {
|
||||
id: number
|
||||
orderId: number
|
||||
orderNo: string
|
||||
trackingNo: string
|
||||
shippingCompany: string
|
||||
shippingTime: string
|
||||
estimatedDelivery: string
|
||||
actualDelivery?: string
|
||||
shippingStatus: 'pending' | 'shipped' | 'in_transit' | 'delivered' | 'failed'
|
||||
shippingCost: number
|
||||
recipientName: string
|
||||
recipientPhone: string
|
||||
deliveryAddress: string
|
||||
deliveryNotes?: string
|
||||
}
|
||||
402
admin-system/src/types/permission.ts
Normal file
402
admin-system/src/types/permission.ts
Normal file
@@ -0,0 +1,402 @@
|
||||
// 权限状态类型
|
||||
export type PermissionStatus = 'active' | 'inactive' | 'deprecated'
|
||||
|
||||
// 权限类型
|
||||
export type PermissionType = 'module' | 'menu' | 'button' | 'action' | 'api'
|
||||
|
||||
// 角色类型
|
||||
export type RoleType = 'system' | 'custom' | 'guest'
|
||||
|
||||
// 角色状态类型
|
||||
export type RoleStatus = 'active' | 'inactive' | 'pending'
|
||||
|
||||
// 权限基本信息接口
|
||||
export interface Permission {
|
||||
id: number
|
||||
name: string
|
||||
code: string
|
||||
description: string
|
||||
type: PermissionType
|
||||
parentId: number | null
|
||||
level: number
|
||||
status: PermissionStatus
|
||||
createTime: string
|
||||
updateTime: string
|
||||
// 扩展信息
|
||||
icon?: string
|
||||
route?: string
|
||||
component?: string
|
||||
sortOrder?: number
|
||||
// 子权限
|
||||
children?: Permission[]
|
||||
// 权限控制
|
||||
isPublic?: boolean
|
||||
requiresAuth?: boolean
|
||||
// 元数据
|
||||
metadata?: Record<string, any>
|
||||
}
|
||||
|
||||
// 角色基本信息接口
|
||||
export interface Role {
|
||||
id: number
|
||||
name: string
|
||||
code: string
|
||||
description: string
|
||||
type: RoleType
|
||||
status: RoleStatus
|
||||
permissionIds: number[]
|
||||
userCount: number
|
||||
createTime: string
|
||||
updateTime: string
|
||||
// 扩展信息
|
||||
permissions?: Permission[]
|
||||
// 角色属性
|
||||
isDefault?: boolean
|
||||
isSystem?: boolean
|
||||
// 访问控制
|
||||
maxUsers?: number
|
||||
expirationDate?: string
|
||||
// 审批流程
|
||||
requiresApproval?: boolean
|
||||
approvalWorkflow?: string
|
||||
// 元数据
|
||||
metadata?: Record<string, any>
|
||||
}
|
||||
|
||||
// 用户角色关联接口
|
||||
export interface UserRole {
|
||||
id: number
|
||||
userId: number
|
||||
roleId: number
|
||||
assignTime: string
|
||||
assignBy: string
|
||||
// 扩展信息
|
||||
expirationTime?: string
|
||||
// 审批信息
|
||||
approvedBy?: string
|
||||
approveTime?: string
|
||||
approvalStatus?: 'pending' | 'approved' | 'rejected'
|
||||
// 权限委托
|
||||
delegatedFrom?: number
|
||||
delegationReason?: string
|
||||
// 元数据
|
||||
metadata?: Record<string, any>
|
||||
}
|
||||
|
||||
// 权限查询参数接口
|
||||
export interface PermissionQueryParams {
|
||||
page: number
|
||||
pageSize: number
|
||||
keyword?: string
|
||||
status?: PermissionStatus
|
||||
type?: PermissionType
|
||||
parentId?: number | null
|
||||
level?: number
|
||||
// 高级筛选
|
||||
isPublic?: boolean
|
||||
requiresAuth?: boolean
|
||||
// 排序
|
||||
sortField?: string
|
||||
sortOrder?: 'asc' | 'desc'
|
||||
}
|
||||
|
||||
// 角色查询参数接口
|
||||
export interface RoleQueryParams {
|
||||
page: number
|
||||
pageSize: number
|
||||
keyword?: string
|
||||
status?: RoleStatus
|
||||
type?: RoleType
|
||||
// 高级筛选
|
||||
isDefault?: boolean
|
||||
isSystem?: boolean
|
||||
// 用户数量范围
|
||||
minUsers?: number
|
||||
maxUsers?: number
|
||||
// 排序
|
||||
sortField?: string
|
||||
sortOrder?: 'asc' | 'desc'
|
||||
}
|
||||
|
||||
// 权限统计信息接口
|
||||
export interface PermissionStatistics {
|
||||
totalPermissions: number
|
||||
activePermissions: number
|
||||
inactivePermissions: number
|
||||
totalRoles: number
|
||||
activeRoles: number
|
||||
inactiveRoles: number
|
||||
totalUsersWithRoles: number
|
||||
permissionUsage: Record<string, number>
|
||||
roleDistribution: Record<string, number>
|
||||
userPermissionStats: {
|
||||
averagePermissionsPerUser: number
|
||||
maxPermissionsPerUser: number
|
||||
minPermissionsPerUser: number
|
||||
usersWithExcessivePermissions: number
|
||||
// 权限分布
|
||||
permissionDistribution?: Record<string, number>
|
||||
// 风险指标
|
||||
permissionConflictRate?: number
|
||||
permissionRedundancyRate?: number
|
||||
}
|
||||
auditLogs: {
|
||||
totalLogs: number
|
||||
permissionChanges: number
|
||||
roleChanges: number
|
||||
userRoleChanges: number
|
||||
// 安全事件
|
||||
securityIncidents?: number
|
||||
complianceViolations?: number
|
||||
}
|
||||
// 性能指标
|
||||
permissionCheckPerformance?: number
|
||||
roleResolutionTime?: number
|
||||
// 合规性指标
|
||||
complianceScore?: number
|
||||
auditPassRate?: number
|
||||
}
|
||||
|
||||
// 审计日志接口
|
||||
export interface AuditLog {
|
||||
id: number
|
||||
action: string
|
||||
targetType: string
|
||||
targetId: number
|
||||
targetName: string
|
||||
performerId: number
|
||||
performerName: string
|
||||
details: string
|
||||
ipAddress: string
|
||||
userAgent: string
|
||||
timestamp: string
|
||||
status: 'success' | 'failure' | 'warning'
|
||||
// 扩展信息
|
||||
resourceType?: string
|
||||
resourceId?: number
|
||||
// 变更详情
|
||||
oldValue?: any
|
||||
newValue?: any
|
||||
// 安全信息
|
||||
securityLevel?: 'low' | 'medium' | 'high' | 'critical'
|
||||
riskScore?: number
|
||||
// 地理位置
|
||||
location?: string
|
||||
deviceInfo?: string
|
||||
// 调查信息
|
||||
investigationStatus?: 'open' | 'in_progress' | 'resolved' | 'closed'
|
||||
investigationNotes?: string
|
||||
}
|
||||
|
||||
// 权限分配请求接口
|
||||
export interface PermissionAssignmentRequest {
|
||||
userId: number
|
||||
permissionIds: number[]
|
||||
assignBy: string
|
||||
// 有效期
|
||||
expirationTime?: string
|
||||
// 审批信息
|
||||
requiresApproval?: boolean
|
||||
approvalWorkflow?: string
|
||||
// 委托信息
|
||||
delegatedFrom?: number
|
||||
delegationReason?: string
|
||||
}
|
||||
|
||||
// 角色分配请求接口
|
||||
export interface RoleAssignmentRequest {
|
||||
userId: number
|
||||
roleId: number
|
||||
assignBy: string
|
||||
// 有效期
|
||||
expirationTime?: string
|
||||
// 审批信息
|
||||
requiresApproval?: boolean
|
||||
approvalWorkflow?: string
|
||||
// 委托信息
|
||||
delegatedFrom?: number
|
||||
delegationReason?: string
|
||||
}
|
||||
|
||||
// 权限检查请求接口
|
||||
export interface PermissionCheckRequest {
|
||||
userId: number
|
||||
permissionCode: string
|
||||
// 上下文信息
|
||||
resourceType?: string
|
||||
resourceId?: number
|
||||
// 环境信息
|
||||
ipAddress?: string
|
||||
userAgent?: string
|
||||
// 安全验证
|
||||
sessionId?: string
|
||||
token?: string
|
||||
}
|
||||
|
||||
// 权限检查响应接口
|
||||
export interface PermissionCheckResponse {
|
||||
hasPermission: boolean
|
||||
// 详细权限信息
|
||||
permission?: Permission
|
||||
// 权限来源
|
||||
grantedBy: 'role' | 'direct' | 'inheritance' | 'delegation'
|
||||
role?: Role
|
||||
// 有效期
|
||||
validUntil?: string
|
||||
// 限制信息
|
||||
restrictions?: string[]
|
||||
// 审计信息
|
||||
auditId?: number
|
||||
}
|
||||
|
||||
// 权限继承配置接口
|
||||
export interface PermissionInheritanceConfig {
|
||||
enabled: boolean
|
||||
inheritanceRules: Array<{
|
||||
sourceType: string
|
||||
sourceId: number
|
||||
targetType: string
|
||||
targetId: number
|
||||
inheritanceType: 'full' | 'partial' | 'conditional'
|
||||
conditions?: Record<string, any>
|
||||
// 有效期
|
||||
validFrom?: string
|
||||
validUntil?: string
|
||||
// 优先级
|
||||
priority: number
|
||||
}>
|
||||
// 冲突解决策略
|
||||
conflictResolution: 'deny' | 'allow' | 'highest_priority' | 'most_specific'
|
||||
// 性能优化
|
||||
cachingEnabled: boolean
|
||||
cacheTTL: number
|
||||
// 审计配置
|
||||
auditInheritance: boolean
|
||||
}
|
||||
|
||||
// 权限委托配置接口
|
||||
export interface PermissionDelegationConfig {
|
||||
enabled: boolean
|
||||
maxDelegationDepth: number
|
||||
delegationTimeLimit: number
|
||||
allowedDelegators: number[]
|
||||
restrictedPermissions: number[]
|
||||
// 审批流程
|
||||
requiresApproval: boolean
|
||||
approvalWorkflow: string
|
||||
// 审计要求
|
||||
auditDelegation: boolean
|
||||
// 通知设置
|
||||
notifyOriginalOwner: boolean
|
||||
notifyDelegator: boolean
|
||||
notifyDelegatee: boolean
|
||||
}
|
||||
|
||||
// 权限审计配置接口
|
||||
export interface PermissionAuditConfig {
|
||||
enabled: boolean
|
||||
auditLevel: 'minimal' | 'standard' | 'detailed' | 'comprehensive'
|
||||
retentionPeriod: number
|
||||
// 审计事件
|
||||
auditedActions: string[]
|
||||
// 敏感操作
|
||||
sensitiveOperations: Array<{
|
||||
action: string
|
||||
sensitivityLevel: 'low' | 'medium' | 'high' | 'critical'
|
||||
additionalAuditing: boolean
|
||||
}>
|
||||
// 实时监控
|
||||
realTimeMonitoring: boolean
|
||||
alertThresholds: Record<string, number>
|
||||
// 合规要求
|
||||
complianceRequirements: string[]
|
||||
regulatoryStandards: string[]
|
||||
}
|
||||
|
||||
// 权限导出参数接口
|
||||
export interface PermissionExportParams {
|
||||
format: 'csv' | 'excel' | 'json'
|
||||
include: Array<'permissions' | 'roles' | 'user_roles' | 'audit_logs'>
|
||||
filters?: {
|
||||
status?: PermissionStatus | RoleStatus
|
||||
type?: PermissionType | RoleType
|
||||
dateRange?: [string, string]
|
||||
}
|
||||
// 数据范围
|
||||
dataScope: 'all' | 'active' | 'recent'
|
||||
// 安全选项
|
||||
encryptData: boolean
|
||||
passwordProtect: boolean
|
||||
// 元数据
|
||||
includeMetadata: boolean
|
||||
}
|
||||
|
||||
// 权限导入参数接口
|
||||
export interface PermissionImportParams {
|
||||
file: File
|
||||
format: 'csv' | 'excel' | 'json'
|
||||
importType: 'permissions' | 'roles' | 'assignments'
|
||||
// 冲突处理
|
||||
conflictResolution: 'skip' | 'overwrite' | 'merge' | 'rename'
|
||||
// 验证选项
|
||||
validateData: boolean
|
||||
dryRun: boolean
|
||||
// 通知设置
|
||||
notifyOnCompletion: boolean
|
||||
notifyOnError: boolean
|
||||
// 审计配置
|
||||
auditImport: boolean
|
||||
}
|
||||
|
||||
// 权限同步配置接口
|
||||
export interface PermissionSyncConfig {
|
||||
enabled: boolean
|
||||
syncFrequency: number
|
||||
syncDirection: 'bidirectional' | 'source_to_target' | 'target_to_source'
|
||||
// 数据源配置
|
||||
sourceSystem: string
|
||||
sourceConfig: Record<string, any>
|
||||
targetSystem: string
|
||||
targetConfig: Record<string, any>
|
||||
// 映射规则
|
||||
fieldMappings: Record<string, string>
|
||||
valueTransformations: Record<string, (value: any) => any>
|
||||
// 冲突解决
|
||||
conflictResolution: 'source_wins' | 'target_wins' | 'manual'
|
||||
// 性能优化
|
||||
batchSize: number
|
||||
parallelProcessing: boolean
|
||||
// 监控配置
|
||||
monitorSync: boolean
|
||||
alertOnFailure: boolean
|
||||
}
|
||||
|
||||
// 权限健康检查接口
|
||||
export interface PermissionHealthCheck {
|
||||
timestamp: string
|
||||
status: 'healthy' | 'degraded' | 'unhealthy'
|
||||
// 组件状态
|
||||
components: Array<{
|
||||
name: string
|
||||
status: 'up' | 'down' | 'degraded'
|
||||
responseTime: number
|
||||
errorRate: number
|
||||
}>
|
||||
// 性能指标
|
||||
performanceMetrics: {
|
||||
permissionCheckTime: number
|
||||
roleResolutionTime: number
|
||||
cacheHitRate: number
|
||||
memoryUsage: number
|
||||
}
|
||||
// 安全指标
|
||||
securityMetrics: {
|
||||
failedAttempts: number
|
||||
suspiciousActivities: number
|
||||
complianceViolations: number
|
||||
}
|
||||
// 建议措施
|
||||
recommendations: string[]
|
||||
// 历史趋势
|
||||
historicalTrend: Record<string, number>
|
||||
}
|
||||
488
admin-system/src/types/stats.ts
Normal file
488
admin-system/src/types/stats.ts
Normal file
@@ -0,0 +1,488 @@
|
||||
// 时间范围类型
|
||||
export type TimeRange = 'today' | 'yesterday' | 'week' | 'month' | 'quarter' | 'year'
|
||||
|
||||
// 统计对比接口
|
||||
export interface StatsComparison {
|
||||
current: number
|
||||
previous: number
|
||||
growthRate: number
|
||||
trend: 'up' | 'down' | 'stable'
|
||||
timeRange: TimeRange
|
||||
// 详细信息
|
||||
unit?: string
|
||||
significance?: 'high' | 'medium' | 'low'
|
||||
confidence?: number
|
||||
}
|
||||
|
||||
// 系统统计接口
|
||||
export interface SystemStats {
|
||||
totalUsers: number
|
||||
activeUsers: number
|
||||
newUsersToday: number
|
||||
newUsersThisWeek: number
|
||||
newUsersThisMonth: number
|
||||
totalActivities: number
|
||||
activeActivities: number
|
||||
completedActivities: number
|
||||
totalAnimals: number
|
||||
adoptedAnimals: number
|
||||
availableAnimals: number
|
||||
totalOrders: number
|
||||
pendingOrders: number
|
||||
completedOrders: number
|
||||
cancelledOrders: number
|
||||
totalRevenue: number
|
||||
todayRevenue: number
|
||||
weekRevenue: number
|
||||
monthRevenue: number
|
||||
systemUptime: string
|
||||
averageResponseTime: number
|
||||
errorRate: number
|
||||
serverLoad: number
|
||||
// 扩展信息
|
||||
databaseSize?: number
|
||||
cacheHitRate?: number
|
||||
apiCalls?: number
|
||||
concurrentUsers?: number
|
||||
// 性能指标
|
||||
pageLoadTime?: number
|
||||
apiResponseTime?: number
|
||||
databaseQueryTime?: number
|
||||
// 资源使用
|
||||
memoryUsage?: number
|
||||
cpuUsage?: number
|
||||
diskUsage?: number
|
||||
networkUsage?: number
|
||||
}
|
||||
|
||||
// 用户统计接口
|
||||
export interface UserStats {
|
||||
totalUsers: number
|
||||
activeUsers: number
|
||||
newUsers: {
|
||||
today: number
|
||||
yesterday: number
|
||||
thisWeek: number
|
||||
lastWeek: number
|
||||
thisMonth: number
|
||||
lastMonth: number
|
||||
}
|
||||
userGrowthRate: number
|
||||
userRetentionRate: number
|
||||
userChurnRate: number
|
||||
userDemographics: {
|
||||
ageDistribution: Record<string, number>
|
||||
genderDistribution: Record<string, number>
|
||||
locationDistribution: Record<string, number>
|
||||
// 扩展信息
|
||||
educationDistribution?: Record<string, number>
|
||||
occupationDistribution?: Record<string, number>
|
||||
incomeDistribution?: Record<string, number>
|
||||
}
|
||||
userActivity: {
|
||||
dailyActiveUsers: number
|
||||
weeklyActiveUsers: number
|
||||
monthlyActiveUsers: number
|
||||
averageSessionDuration: number
|
||||
averageSessionsPerUser: number
|
||||
// 行为指标
|
||||
pagesPerSession?: number
|
||||
bounceRate?: number
|
||||
returnRate?: number
|
||||
}
|
||||
userBehavior: {
|
||||
averageActivitiesPerUser: number
|
||||
averageAnimalsAdopted: number
|
||||
averageOrdersPerUser: number
|
||||
conversionRate: number
|
||||
engagementRate: number
|
||||
// 参与度指标
|
||||
contentCreationRate?: number
|
||||
socialInteractionRate?: number
|
||||
referralRate?: number
|
||||
}
|
||||
// 用户生命周期
|
||||
userLifetimeValue: number
|
||||
userAcquisitionCost: number
|
||||
userRetentionCost: number
|
||||
// 用户满意度
|
||||
userSatisfactionScore?: number
|
||||
netPromoterScore?: number
|
||||
customerEffortScore?: number
|
||||
}
|
||||
|
||||
// 活动统计接口
|
||||
export interface ActivityStats {
|
||||
totalActivities: number
|
||||
activeActivities: number
|
||||
completedActivities: number
|
||||
cancelledActivities: number
|
||||
pendingActivities: number
|
||||
activityGrowthRate: number
|
||||
participationStats: {
|
||||
totalParticipants: number
|
||||
averageParticipantsPerActivity: number
|
||||
repeatParticipants: number
|
||||
participationRate: number
|
||||
// 参与度详情
|
||||
maleParticipants?: number
|
||||
femaleParticipants?: number
|
||||
ageDistribution?: Record<string, number>
|
||||
locationDistribution?: Record<string, number>
|
||||
}
|
||||
revenueStats: {
|
||||
totalRevenue: number
|
||||
averageRevenuePerActivity: number
|
||||
revenueGrowthRate: number
|
||||
refundRate: number
|
||||
// 收入详情
|
||||
ticketRevenue?: number
|
||||
serviceRevenue?: number
|
||||
productRevenue?: number
|
||||
commissionRevenue?: number
|
||||
}
|
||||
categoryDistribution: Record<string, number>
|
||||
locationDistribution: Record<string, number>
|
||||
timeDistribution: Record<string, number>
|
||||
organizerPerformance: {
|
||||
topOrganizers: Array<{
|
||||
organizerId: number
|
||||
name: string
|
||||
activityCount: number
|
||||
revenue: number
|
||||
rating?: number
|
||||
completionRate?: number
|
||||
}>
|
||||
averageRating: number
|
||||
completionRate: number
|
||||
cancellationRate: number
|
||||
// 组织者指标
|
||||
organizerSatisfaction?: number
|
||||
organizerRetention?: number
|
||||
organizerGrowth?: number
|
||||
}
|
||||
// 活动质量指标
|
||||
activityQualityScore?: number
|
||||
participantSatisfaction?: number
|
||||
safetyIncidents?: number
|
||||
// 营销效果
|
||||
marketingROI?: number
|
||||
acquisitionCost?: number
|
||||
conversionRate?: number
|
||||
}
|
||||
|
||||
// 动物统计接口
|
||||
export interface AnimalStats {
|
||||
totalAnimals: number
|
||||
adoptedAnimals: number
|
||||
availableAnimals: number
|
||||
adoptionRate: number
|
||||
adoptionGrowthRate: number
|
||||
animalTypes: Record<string, number>
|
||||
adoptionStats: {
|
||||
totalAdoptions: number
|
||||
averageAdoptionTime: number
|
||||
repeatAdoptions: number
|
||||
adoptionSuccessRate: number
|
||||
// 收养详情
|
||||
adoptionReasons?: Record<string, number>
|
||||
adoptionDuration?: Record<string, number>
|
||||
adoptionFrequency?: number
|
||||
}
|
||||
careStats: {
|
||||
totalCareRecords: number
|
||||
averageCareFrequency: number
|
||||
medicalCheckups: number
|
||||
vaccinationRate: number
|
||||
// 护理详情
|
||||
healthIssues?: number
|
||||
treatmentCost?: number
|
||||
careQuality?: number
|
||||
}
|
||||
locationDistribution: Record<string, number>
|
||||
popularAnimals: Array<{
|
||||
animalId: number
|
||||
name: string
|
||||
type: string
|
||||
adoptionCount: number
|
||||
viewCount: number
|
||||
likeCount?: number
|
||||
shareCount?: number
|
||||
}>
|
||||
revenueStats: {
|
||||
totalAdoptionRevenue: number
|
||||
averageAdoptionFee: number
|
||||
careProductRevenue: number
|
||||
totalRevenue: number
|
||||
// 收入详情
|
||||
donationRevenue?: number
|
||||
sponsorshipRevenue?: number
|
||||
merchandiseRevenue?: number
|
||||
}
|
||||
// 动物福利指标
|
||||
animalWelfareScore?: number
|
||||
healthIndex?: number
|
||||
happinessIndex?: number
|
||||
// 可持续发展
|
||||
environmentalImpact?: number
|
||||
communityEngagement?: number
|
||||
educationalValue?: number
|
||||
}
|
||||
|
||||
// 订单统计接口
|
||||
export interface OrderStats {
|
||||
totalOrders: number
|
||||
pendingOrders: number
|
||||
completedOrders: number
|
||||
cancelledOrders: number
|
||||
orderGrowthRate: number
|
||||
revenueStats: {
|
||||
totalRevenue: number
|
||||
averageOrderValue: number
|
||||
revenueGrowthRate: number
|
||||
refundAmount: number
|
||||
netRevenue: number
|
||||
// 收入构成
|
||||
productRevenue?: number
|
||||
serviceRevenue?: number
|
||||
taxAmount?: number
|
||||
shippingCost?: number
|
||||
}
|
||||
orderTypeDistribution: Record<string, number>
|
||||
paymentStats: {
|
||||
paymentMethods: Record<string, number>
|
||||
paymentSuccessRate: number
|
||||
averagePaymentTime: number
|
||||
refundRate: number
|
||||
// 支付详情
|
||||
fraudRate?: number
|
||||
chargebackRate?: number
|
||||
paymentCost?: number
|
||||
}
|
||||
customerStats: {
|
||||
repeatCustomers: number
|
||||
averageOrdersPerCustomer: number
|
||||
customerLifetimeValue: number
|
||||
acquisitionCost: number
|
||||
// 客户行为
|
||||
purchaseFrequency?: number
|
||||
basketSize?: number
|
||||
crossSellRate?: number
|
||||
}
|
||||
timeDistribution: Record<string, number>
|
||||
locationDistribution: Record<string, number>
|
||||
// 订单质量指标
|
||||
orderAccuracy?: number
|
||||
deliveryPerformance?: number
|
||||
customerSatisfaction?: number
|
||||
// 运营效率
|
||||
fulfillmentTime?: number
|
||||
inventoryTurnover?: number
|
||||
returnRate?: number
|
||||
}
|
||||
|
||||
// 收入统计接口
|
||||
export interface RevenueStats {
|
||||
totalRevenue: number
|
||||
revenueGrowthRate: number
|
||||
dailyRevenue: number
|
||||
weeklyRevenue: number
|
||||
monthlyRevenue: number
|
||||
revenueSources: Record<string, number>
|
||||
revenueTrends: {
|
||||
last7Days: number[]
|
||||
last30Days: number[]
|
||||
// 扩展趋势
|
||||
last90Days?: number[]
|
||||
last365Days?: number[]
|
||||
seasonalPatterns?: Record<string, number>
|
||||
}
|
||||
customerMetrics: {
|
||||
averageRevenuePerUser: number
|
||||
averageOrderValue: number
|
||||
purchaseFrequency: number
|
||||
customerLifetimeValue: number
|
||||
// 客户价值
|
||||
retentionValue?: number
|
||||
acquisitionValue?: number
|
||||
referralValue?: number
|
||||
}
|
||||
geographicDistribution: Record<string, number>
|
||||
productPerformance: {
|
||||
topProducts: Array<{
|
||||
productId: number
|
||||
name: string
|
||||
revenue: number
|
||||
orders: number
|
||||
profit?: number
|
||||
margin?: number
|
||||
}>
|
||||
averageProfitMargin: number
|
||||
returnOnInvestment: number
|
||||
// 产品指标
|
||||
productPopularity?: number
|
||||
productSatisfaction?: number
|
||||
productRetention?: number
|
||||
}
|
||||
// 财务指标
|
||||
grossProfit?: number
|
||||
operatingProfit?: number
|
||||
netProfit?: number
|
||||
cashFlow?: number
|
||||
// 预算与实际
|
||||
budgetVariance?: number
|
||||
forecastAccuracy?: number
|
||||
// 税务信息
|
||||
taxLiability?: number
|
||||
taxCompliance?: number
|
||||
}
|
||||
|
||||
// 仪表板指标接口
|
||||
export interface DashboardMetrics {
|
||||
keyMetrics: {
|
||||
totalUsers: number
|
||||
activeUsers: number
|
||||
totalActivities: number
|
||||
totalOrders: number
|
||||
totalRevenue: number
|
||||
adoptionRate: number
|
||||
// 核心指标
|
||||
conversionRate?: number
|
||||
engagementRate?: number
|
||||
retentionRate?: number
|
||||
}
|
||||
growthMetrics: {
|
||||
userGrowth: number
|
||||
activityGrowth: number
|
||||
orderGrowth: number
|
||||
revenueGrowth: number
|
||||
// 增长详情
|
||||
organicGrowth?: number
|
||||
paidGrowth?: number
|
||||
referralGrowth?: number
|
||||
}
|
||||
performanceMetrics: {
|
||||
systemUptime: string
|
||||
averageResponseTime: number
|
||||
errorRate: number
|
||||
conversionRate: number
|
||||
// 性能详情
|
||||
pageSpeed?: number
|
||||
apiAvailability?: number
|
||||
databasePerformance?: number
|
||||
}
|
||||
recentActivities: Array<{
|
||||
type: string
|
||||
action: string
|
||||
count: number
|
||||
time: string
|
||||
// 活动详情
|
||||
details?: string
|
||||
priority?: 'high' | 'medium' | 'low'
|
||||
}>
|
||||
alerts: Array<{
|
||||
level: 'critical' | 'warning' | 'info' | 'success'
|
||||
message: string
|
||||
time: string
|
||||
// 警报详情
|
||||
source?: string
|
||||
affectedComponents?: string[]
|
||||
resolution?: string
|
||||
}>
|
||||
// 趋势数据
|
||||
trends: {
|
||||
userTrend: number[]
|
||||
revenueTrend: number[]
|
||||
activityTrend: number[]
|
||||
orderTrend: number[]
|
||||
}
|
||||
// 预测数据
|
||||
forecasts: {
|
||||
userForecast: number[]
|
||||
revenueForecast: number[]
|
||||
activityForecast: number[]
|
||||
orderForecast: number[]
|
||||
}
|
||||
// 比较数据
|
||||
comparisons: {
|
||||
vsPreviousPeriod: Record<string, StatsComparison>
|
||||
vsIndustryAverage: Record<string, StatsComparison>
|
||||
vsCompetitors: Record<string, StatsComparison>
|
||||
}
|
||||
}
|
||||
|
||||
// 统计导出参数接口
|
||||
export interface StatsExportParams {
|
||||
type: 'users' | 'activities' | 'animals' | 'orders' | 'revenue' | 'system'
|
||||
format: 'csv' | 'excel' | 'json' | 'pdf'
|
||||
timeRange: TimeRange
|
||||
fields: string[]
|
||||
filters?: Record<string, any>
|
||||
sortBy?: string
|
||||
sortOrder?: 'asc' | 'desc'
|
||||
}
|
||||
|
||||
// 统计查询参数接口
|
||||
export interface StatsQueryParams {
|
||||
metric: string
|
||||
dimensions?: string[]
|
||||
filters?: Record<string, any>
|
||||
timeRange: TimeRange
|
||||
compareWith?: TimeRange
|
||||
granularity?: 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'
|
||||
limit?: number
|
||||
offset?: number
|
||||
}
|
||||
|
||||
// 统计响应接口
|
||||
export interface StatsResponse {
|
||||
data: any[]
|
||||
total: number
|
||||
timeRange: TimeRange
|
||||
generatedAt: string
|
||||
metadata: {
|
||||
query: StatsQueryParams
|
||||
processingTime: number
|
||||
dataFreshness: string
|
||||
confidence: number
|
||||
}
|
||||
}
|
||||
|
||||
// 统计预警配置接口
|
||||
export interface StatsAlertConfig {
|
||||
metric: string
|
||||
threshold: number
|
||||
condition: 'above' | 'below' | 'equal' | 'change'
|
||||
timeRange: TimeRange
|
||||
severity: 'critical' | 'warning' | 'info'
|
||||
notificationChannels: string[]
|
||||
recipients: string[]
|
||||
cooldownPeriod: number
|
||||
// 高级配置
|
||||
aggregation?: string
|
||||
windowSize?: number
|
||||
minimumSamples?: number
|
||||
}
|
||||
|
||||
// 统计报告接口
|
||||
export interface StatsReport {
|
||||
id: string
|
||||
title: string
|
||||
type: 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'yearly' | 'custom'
|
||||
period: string
|
||||
metrics: Record<string, any>
|
||||
insights: string[]
|
||||
recommendations: string[]
|
||||
generatedAt: string
|
||||
// 报告详情
|
||||
executiveSummary?: string
|
||||
detailedAnalysis?: string
|
||||
visualizations?: any[]
|
||||
attachments?: string[]
|
||||
// 分发信息
|
||||
recipients?: string[]
|
||||
deliveryStatus?: 'pending' | 'sent' | 'delivered' | 'failed'
|
||||
// 安全信息
|
||||
accessLevel?: 'public' | 'internal' | 'confidential'
|
||||
retentionPolicy?: string
|
||||
}
|
||||
130
admin-system/src/types/user.ts
Normal file
130
admin-system/src/types/user.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
// 用户状态类型
|
||||
export type UserStatus = 'active' | 'inactive' | 'banned'
|
||||
|
||||
// 用户类型
|
||||
export type UserType = 'normal' | 'merchant' | 'admin'
|
||||
|
||||
// 用户基本信息接口
|
||||
export interface User {
|
||||
id: number
|
||||
username: string
|
||||
nickname: string
|
||||
avatar: string
|
||||
phone: string
|
||||
email: string
|
||||
status: UserStatus
|
||||
registerTime: string
|
||||
lastLoginTime: string
|
||||
userType: UserType
|
||||
// 扩展字段
|
||||
gender?: 'male' | 'female' | 'unknown'
|
||||
birthday?: string
|
||||
interests?: string[]
|
||||
location?: string
|
||||
// 商家特有字段
|
||||
shopName?: string
|
||||
businessLicense?: string
|
||||
// 统计信息
|
||||
travelCount?: number
|
||||
animalAdoptionCount?: number
|
||||
orderCount?: number
|
||||
// 安全相关
|
||||
isVerified?: boolean
|
||||
verificationLevel?: number
|
||||
}
|
||||
|
||||
// 用户查询参数接口
|
||||
export interface UserQueryParams {
|
||||
page: number
|
||||
pageSize: number
|
||||
keyword?: string
|
||||
status?: UserStatus
|
||||
userType?: UserType
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
sortField?: string
|
||||
sortOrder?: 'asc' | 'desc'
|
||||
}
|
||||
|
||||
// 用户统计信息接口
|
||||
export interface UserStatistics {
|
||||
totalUsers: number
|
||||
activeUsers: number
|
||||
newUsersToday: number
|
||||
newUsersThisWeek: number
|
||||
newUsersThisMonth: number
|
||||
userGrowthRate: number
|
||||
userTypeDistribution: {
|
||||
normal: number
|
||||
merchant: number
|
||||
admin: number
|
||||
}
|
||||
statusDistribution: {
|
||||
active: number
|
||||
inactive: number
|
||||
banned: number
|
||||
}
|
||||
dailyActiveUsers: Array<{
|
||||
date: string
|
||||
count: number
|
||||
}>
|
||||
}
|
||||
|
||||
// 用户操作记录接口
|
||||
export interface UserOperationLog {
|
||||
id: number
|
||||
userId: number
|
||||
username: string
|
||||
operationType: string
|
||||
operationDetail: string
|
||||
operationTime: string
|
||||
ipAddress: string
|
||||
userAgent: string
|
||||
result: 'success' | 'failure'
|
||||
}
|
||||
|
||||
// 用户注册统计接口
|
||||
export interface UserRegistrationStats {
|
||||
date: string
|
||||
count: number
|
||||
}
|
||||
|
||||
// 用户登录统计接口
|
||||
export interface UserLoginStats {
|
||||
date: string
|
||||
count: number
|
||||
}
|
||||
|
||||
// 用户行为统计接口
|
||||
export interface UserBehaviorStats {
|
||||
date: string
|
||||
travelPublishCount: number
|
||||
animalAdoptionCount: number
|
||||
orderCount: number
|
||||
messageCount: number
|
||||
shareCount: number
|
||||
}
|
||||
|
||||
// 用户导出参数接口
|
||||
export interface UserExportParams {
|
||||
format: 'csv' | 'excel'
|
||||
fields: string[]
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
status?: UserStatus
|
||||
userType?: UserType
|
||||
}
|
||||
|
||||
// 管理员信息接口
|
||||
export interface Admin {
|
||||
id: number
|
||||
username: string
|
||||
email: string
|
||||
nickname: string
|
||||
avatar: string
|
||||
role: string
|
||||
status: number
|
||||
lastLoginTime?: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
@@ -11,10 +11,10 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
server: {
|
||||
port: 3001,
|
||||
port: 3150,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:3000',
|
||||
target: 'http://localhost:3100',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, '/api/v1')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user