优化分页和筛选功能:实现服务端分页和筛选,修复总数不匹配问题
- 优化设备管理、版本管理、用户管理、在线实例、公告管理页面的分页功能 - 后端添加筛选参数支持(application_id, status, type, search等) - 前端移除冗余的前端筛选逻辑,改为服务端筛选 - 修复分页总数与筛选数据不匹配的问题 - 优化云端变量记录页面的时间筛选和搜索功能 - 修复DateTimePicker组件关闭后不自动更新的问题
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
package admin
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -28,6 +28,10 @@ func handleGetAllAnnouncements(c *gin.Context) {
|
||||
|
||||
page := c.DefaultQuery("page", "1")
|
||||
pageSize := c.DefaultQuery("page_size", "20")
|
||||
applicationIDFilter := c.Query("application_id")
|
||||
typeFilter := c.Query("type")
|
||||
statusFilter := c.Query("status")
|
||||
searchFilter := c.Query("search")
|
||||
|
||||
var total int64
|
||||
|
||||
@@ -59,7 +63,36 @@ func handleGetAllAnnouncements(c *gin.Context) {
|
||||
|
||||
log.Printf("[DEBUG] appNameMap: %v\n", appNameMap)
|
||||
|
||||
database.DB.Model(&model.Announcement{}).Where("application_id IN ?", appIDs).Count(&total)
|
||||
countQuery := database.DB.Model(&model.Announcement{}).Where("application_id IN ?", appIDs)
|
||||
if applicationIDFilter != "" {
|
||||
countQuery = countQuery.Where("application_id = ?", applicationIDFilter)
|
||||
}
|
||||
if typeFilter != "" {
|
||||
countQuery = countQuery.Where("type = ?", typeFilter)
|
||||
}
|
||||
if statusFilter != "" {
|
||||
countQuery = countQuery.Where("status = ?", statusFilter)
|
||||
}
|
||||
if searchFilter != "" {
|
||||
searchLower := strings.ToLower(searchFilter)
|
||||
countQuery = countQuery.Where("LOWER(title) LIKE ? OR LOWER(content) LIKE ?", "%"+searchLower+"%", "%"+searchLower+"%")
|
||||
}
|
||||
countQuery.Count(&total)
|
||||
|
||||
query := database.DB.Where("application_id IN ?", appIDs)
|
||||
if applicationIDFilter != "" {
|
||||
query = query.Where("application_id = ?", applicationIDFilter)
|
||||
}
|
||||
if typeFilter != "" {
|
||||
query = query.Where("type = ?", typeFilter)
|
||||
}
|
||||
if statusFilter != "" {
|
||||
query = query.Where("status = ?", statusFilter)
|
||||
}
|
||||
if searchFilter != "" {
|
||||
searchLower := strings.ToLower(searchFilter)
|
||||
query = query.Where("LOWER(title) LIKE ? OR LOWER(content) LIKE ?", "%"+searchLower+"%", "%"+searchLower+"%")
|
||||
}
|
||||
|
||||
var announcements []model.Announcement
|
||||
offset := 0
|
||||
@@ -72,7 +105,7 @@ func handleGetAllAnnouncements(c *gin.Context) {
|
||||
limit = pageSizeInt
|
||||
}
|
||||
|
||||
if err := database.DB.Where("application_id IN ?", appIDs).Order("is_top DESC, created_at DESC").Limit(limit).Offset(offset).Find(&announcements).Error; err != nil {
|
||||
if err := query.Order("is_top DESC, created_at DESC").Limit(limit).Offset(offset).Find(&announcements).Error; err != nil {
|
||||
response.Error(c, 500, "获取公告列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -722,6 +722,7 @@ func handleGetCloudVariableRecords(c *gin.Context) {
|
||||
userIDFilter := c.Query("user_id")
|
||||
startDate := c.Query("start_date")
|
||||
endDate := c.Query("end_date")
|
||||
searchQuery := c.Query("search")
|
||||
|
||||
var total int64
|
||||
query := database.DB.Model(&model.CloudVariableRecord{}).Where("cloud_variable_id = ?", variable.ID)
|
||||
@@ -734,6 +735,102 @@ func handleGetCloudVariableRecords(c *gin.Context) {
|
||||
if endDate != "" {
|
||||
query = query.Where("created_at <= ?", endDate+" 23:59:59")
|
||||
}
|
||||
|
||||
if searchQuery != "" {
|
||||
var allRecords []model.CloudVariableRecord
|
||||
if err := query.Order("created_at DESC").Find(&allRecords).Error; err != nil {
|
||||
response.Error(c, 500, "获取记录失败")
|
||||
return
|
||||
}
|
||||
|
||||
var filteredRecords []model.CloudVariableRecord
|
||||
searchLower := strings.ToLower(searchQuery)
|
||||
for _, record := range allRecords {
|
||||
if strings.Contains(strings.ToLower(fmt.Sprintf("%d", record.ID)), searchLower) {
|
||||
filteredRecords = append(filteredRecords, record)
|
||||
continue
|
||||
}
|
||||
|
||||
if record.AppUserID != nil {
|
||||
var appUser model.AppUser
|
||||
if err := database.DB.Select("username").First(&appUser, *record.AppUserID).Error; err == nil {
|
||||
if strings.Contains(strings.ToLower(appUser.Username), searchLower) {
|
||||
filteredRecords = append(filteredRecords, record)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Contains(strings.ToLower(record.CreatedAt), searchLower) {
|
||||
filteredRecords = append(filteredRecords, record)
|
||||
continue
|
||||
}
|
||||
|
||||
if record.Data != "" {
|
||||
if strings.Contains(strings.ToLower(record.Data), searchLower) {
|
||||
filteredRecords = append(filteredRecords, record)
|
||||
continue
|
||||
}
|
||||
|
||||
var parsedData interface{}
|
||||
if err := json.Unmarshal([]byte(record.Data), &parsedData); err == nil {
|
||||
if searchData(parsedData, searchLower) {
|
||||
filteredRecords = append(filteredRecords, record)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
total = int64(len(filteredRecords))
|
||||
start := (page - 1) * pageSize
|
||||
end := start + pageSize
|
||||
if start > len(filteredRecords) {
|
||||
filteredRecords = []model.CloudVariableRecord{}
|
||||
} else {
|
||||
if end > len(filteredRecords) {
|
||||
end = len(filteredRecords)
|
||||
}
|
||||
filteredRecords = filteredRecords[start:end]
|
||||
}
|
||||
|
||||
result := make([]gin.H, len(filteredRecords))
|
||||
for i, r := range filteredRecords {
|
||||
var parsedData interface{}
|
||||
if r.Data != "" {
|
||||
json.Unmarshal([]byte(r.Data), &parsedData)
|
||||
}
|
||||
if parsedData == nil && r.Data != "" {
|
||||
parsedData = r.Data
|
||||
}
|
||||
record := gin.H{
|
||||
"id": r.ID,
|
||||
"data": parsedData,
|
||||
"created_at": r.CreatedAt,
|
||||
}
|
||||
if r.AppUserID != nil {
|
||||
record["user_id"] = r.AppUserID
|
||||
var appUser model.AppUser
|
||||
if err := database.DB.Select("id, username").First(&appUser, *r.AppUserID).Error; err == nil {
|
||||
record["user"] = gin.H{
|
||||
"id": appUser.ID,
|
||||
"username": appUser.Username,
|
||||
}
|
||||
}
|
||||
}
|
||||
result[i] = record
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"records": result,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
"total_pages": (total + int64(pageSize) - 1) / int64(pageSize),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
query.Count(&total)
|
||||
|
||||
var records []model.CloudVariableRecord
|
||||
@@ -779,6 +876,36 @@ func handleGetCloudVariableRecords(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
func searchData(data interface{}, searchLower string) bool {
|
||||
switch v := data.(type) {
|
||||
case map[string]interface{}:
|
||||
for _, value := range v {
|
||||
if searchData(value, searchLower) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
case []interface{}:
|
||||
for _, item := range v {
|
||||
if searchData(item, searchLower) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
case string:
|
||||
if strings.Contains(strings.ToLower(v), searchLower) {
|
||||
return true
|
||||
}
|
||||
case float64, int, int64, float32:
|
||||
if strings.Contains(strings.ToLower(fmt.Sprintf("%v", v)), searchLower) {
|
||||
return true
|
||||
}
|
||||
case bool:
|
||||
if strings.Contains(strings.ToLower(fmt.Sprintf("%v", v)), searchLower) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func handleDeleteCloudVariableRecords(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
id := c.Param("id")
|
||||
|
||||
@@ -42,6 +42,8 @@ func handleGetDevices(c *gin.Context) {
|
||||
|
||||
userIDFilter := c.Query("user_id")
|
||||
deviceIDFilter := c.Query("device_id")
|
||||
applicationIDFilter := c.Query("application_id")
|
||||
statusFilter := c.Query("status")
|
||||
|
||||
var devices []model.UserDevice
|
||||
var appHeartbeatTimeoutMap map[uint]int
|
||||
@@ -95,6 +97,12 @@ func handleGetDevices(c *gin.Context) {
|
||||
if deviceIDFilter != "" {
|
||||
countQuery = countQuery.Where("device_id = ?", deviceIDFilter)
|
||||
}
|
||||
if applicationIDFilter != "" {
|
||||
countQuery = countQuery.Where("application_id = ?", applicationIDFilter)
|
||||
}
|
||||
if statusFilter != "" {
|
||||
countQuery = countQuery.Where("status = ?", statusFilter)
|
||||
}
|
||||
countQuery.Count(&total)
|
||||
|
||||
query := database.DB.Preload("User").Preload("Application").Where("application_id IN ?", appIDs)
|
||||
@@ -107,6 +115,14 @@ func handleGetDevices(c *gin.Context) {
|
||||
query = query.Where("device_id = ?", deviceIDFilter)
|
||||
}
|
||||
|
||||
if applicationIDFilter != "" {
|
||||
query = query.Where("application_id = ?", applicationIDFilter)
|
||||
}
|
||||
|
||||
if statusFilter != "" {
|
||||
query = query.Where("status = ?", statusFilter)
|
||||
}
|
||||
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
offset := (page - 1) * pageSize
|
||||
@@ -339,6 +355,11 @@ func handleGetSessions(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
deviceIDFilter := c.Query("device_id")
|
||||
appIDFilter := c.Query("app_id")
|
||||
usernameFilter := c.Query("username")
|
||||
searchFilter := c.Query("search")
|
||||
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
|
||||
var ownApps []model.Application
|
||||
if err := database.DB.Where("user_id = ?", userID).Find(&ownApps).Error; err != nil {
|
||||
@@ -377,7 +398,7 @@ func handleGetSessions(c *gin.Context) {
|
||||
}
|
||||
|
||||
if len(appIDs) == 0 {
|
||||
response.Success(c, gin.H{"sessions": []SessionWithDetails{}})
|
||||
response.Success(c, gin.H{"sessions": []SessionWithDetails{}, "total": 0, "page": page, "page_size": pageSize})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -391,8 +412,13 @@ func handleGetSessions(c *gin.Context) {
|
||||
query = query.Where("application_id = ?", appIDFilter)
|
||||
}
|
||||
|
||||
var total int64
|
||||
query.Count(&total)
|
||||
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
var sessions []model.DeviceSession
|
||||
if err := query.Find(&sessions).Error; err != nil {
|
||||
if err := query.Offset(offset).Limit(pageSize).Find(&sessions).Error; err != nil {
|
||||
response.Error(c, 500, "获取会话列表失败")
|
||||
return
|
||||
}
|
||||
@@ -418,6 +444,22 @@ func handleGetSessions(c *gin.Context) {
|
||||
timeoutThreshold := time.Now().Add(-time.Duration(heartbeatTimeout) * time.Second)
|
||||
isOnline := session.LastHeartbeat != nil && session.LastHeartbeat.After(timeoutThreshold)
|
||||
|
||||
if usernameFilter != "" && user.Username != usernameFilter {
|
||||
continue
|
||||
}
|
||||
|
||||
if searchFilter != "" {
|
||||
searchLower := strings.ToLower(searchFilter)
|
||||
instanceIDMatch := strings.Contains(strings.ToLower(session.InstanceID), searchLower)
|
||||
deviceIDMatch := strings.Contains(strings.ToLower(device.DeviceID), searchLower)
|
||||
usernameMatch := strings.Contains(strings.ToLower(user.Username), searchLower)
|
||||
deviceNameMatch := strings.Contains(strings.ToLower(device.DeviceName), searchLower)
|
||||
|
||||
if !instanceIDMatch && !deviceIDMatch && !usernameMatch && !deviceNameMatch {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
sessionsWithDetails = append(sessionsWithDetails, SessionWithDetails{
|
||||
DeviceSession: session,
|
||||
DeviceID: device.DeviceID,
|
||||
@@ -428,7 +470,7 @@ func handleGetSessions(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{"sessions": sessionsWithDetails})
|
||||
response.Success(c, gin.H{"sessions": sessionsWithDetails, "total": total, "page": page, "page_size": pageSize})
|
||||
}
|
||||
|
||||
func handleDeleteSession(c *gin.Context) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package admin
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
"verification-platform-backend/internal/database"
|
||||
"verification-platform-backend/internal/model"
|
||||
@@ -57,10 +58,14 @@ func handleGetUsers(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
log.Printf("[DEBUG] handleGetUsers called, userID: %d", userID)
|
||||
|
||||
var users []model.AppUser
|
||||
var appHeartbeatTimeoutMap map[uint]int
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
|
||||
applicationID := c.Query("application_id")
|
||||
statusFilter := c.Query("status")
|
||||
|
||||
var users []model.AppUser
|
||||
var appHeartbeatTimeoutMap map[uint]int
|
||||
if applicationID != "" {
|
||||
var appID uint
|
||||
if _, err := fmt.Sscanf(applicationID, "%d", &appID); err != nil {
|
||||
@@ -81,8 +86,13 @@ func handleGetUsers(c *gin.Context) {
|
||||
}
|
||||
appHeartbeatTimeoutMap[app.ID] = timeout
|
||||
|
||||
query := database.DB.Preload("Application").Where("application_id = ?", appID)
|
||||
if statusFilter != "" {
|
||||
query = query.Where("status = ?", statusFilter)
|
||||
}
|
||||
|
||||
if app.UserID == userID {
|
||||
if err := database.DB.Preload("Application").Where("application_id = ?", appID).Find(&users).Error; err != nil {
|
||||
if err := query.Find(&users).Error; err != nil {
|
||||
response.Error(c, 500, "获取用户列表失败")
|
||||
return
|
||||
}
|
||||
@@ -102,7 +112,11 @@ func handleGetUsers(c *gin.Context) {
|
||||
}
|
||||
|
||||
if len(cardUserIDs) > 0 {
|
||||
if err := database.DB.Preload("Application").Where("id IN ?", cardUserIDs).Find(&users).Error; err != nil {
|
||||
query = database.DB.Preload("Application").Where("id IN ?", cardUserIDs)
|
||||
if statusFilter != "" {
|
||||
query = query.Where("status = ?", statusFilter)
|
||||
}
|
||||
if err := query.Find(&users).Error; err != nil {
|
||||
response.Error(c, 500, "获取用户列表失败")
|
||||
return
|
||||
}
|
||||
@@ -137,7 +151,11 @@ func handleGetUsers(c *gin.Context) {
|
||||
}
|
||||
|
||||
if len(ownAppIDs) > 0 {
|
||||
if err := database.DB.Preload("Application").Where("application_id IN ?", ownAppIDs).Find(&users).Error; err != nil {
|
||||
query := database.DB.Preload("Application").Where("application_id IN ?", ownAppIDs)
|
||||
if statusFilter != "" {
|
||||
query = query.Where("status = ?", statusFilter)
|
||||
}
|
||||
if err := query.Find(&users).Error; err != nil {
|
||||
response.Error(c, 500, "获取用户列表失败")
|
||||
return
|
||||
}
|
||||
@@ -179,8 +197,12 @@ func handleGetUsers(c *gin.Context) {
|
||||
}
|
||||
|
||||
if len(allCardUserIDs) > 0 {
|
||||
query := database.DB.Preload("Application").Where("id IN ?", allCardUserIDs)
|
||||
if statusFilter != "" {
|
||||
query = query.Where("status = ?", statusFilter)
|
||||
}
|
||||
var appUsers []model.AppUser
|
||||
if err := database.DB.Preload("Application").Where("id IN ?", allCardUserIDs).Find(&appUsers).Error; err != nil {
|
||||
if err := query.Find(&appUsers).Error; err != nil {
|
||||
log.Printf("[DEBUG] Failed to get users: %v", err)
|
||||
} else {
|
||||
users = append(users, appUsers...)
|
||||
@@ -246,9 +268,21 @@ func handleGetUsers(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
offset := (page - 1) * pageSize
|
||||
end := offset + pageSize
|
||||
if offset > len(usersWithStatus) {
|
||||
offset = len(usersWithStatus)
|
||||
}
|
||||
if end > len(usersWithStatus) {
|
||||
end = len(usersWithStatus)
|
||||
}
|
||||
paginatedUsers := usersWithStatus[offset:end]
|
||||
|
||||
responseData := gin.H{
|
||||
"users": usersWithStatus,
|
||||
"users": paginatedUsers,
|
||||
"total": totalCount,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
"online_count": onlineCount,
|
||||
"offline_count": offlineCount,
|
||||
"banned_count": bannedCount,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package admin
|
||||
package admin
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
@@ -40,6 +40,10 @@ func handleGetAllVersions(c *gin.Context) {
|
||||
|
||||
page := c.DefaultQuery("page", "1")
|
||||
pageSize := c.DefaultQuery("page_size", "20")
|
||||
applicationIDFilter := c.Query("application_id")
|
||||
updateStrategyFilter := c.Query("update_strategy")
|
||||
updateMethodFilter := c.Query("update_method")
|
||||
searchFilter := c.Query("search")
|
||||
|
||||
var total int64
|
||||
|
||||
@@ -71,7 +75,36 @@ func handleGetAllVersions(c *gin.Context) {
|
||||
|
||||
log.Printf("[DEBUG] appNameMap: %v\n", appNameMap)
|
||||
|
||||
database.DB.Model(&model.Version{}).Where("application_id IN ?", appIDs).Count(&total)
|
||||
countQuery := database.DB.Model(&model.Version{}).Where("application_id IN ?", appIDs)
|
||||
if applicationIDFilter != "" {
|
||||
countQuery = countQuery.Where("application_id = ?", applicationIDFilter)
|
||||
}
|
||||
if updateStrategyFilter != "" {
|
||||
countQuery = countQuery.Where("update_strategy = ?", updateStrategyFilter)
|
||||
}
|
||||
if updateMethodFilter != "" {
|
||||
countQuery = countQuery.Where("update_method = ?", updateMethodFilter)
|
||||
}
|
||||
if searchFilter != "" {
|
||||
searchLower := strings.ToLower(searchFilter)
|
||||
countQuery = countQuery.Where("LOWER(version) LIKE ? OR LOWER(description) LIKE ?", "%"+searchLower+"%", "%"+searchLower+"%")
|
||||
}
|
||||
countQuery.Count(&total)
|
||||
|
||||
query := database.DB.Where("application_id IN ?", appIDs)
|
||||
if applicationIDFilter != "" {
|
||||
query = query.Where("application_id = ?", applicationIDFilter)
|
||||
}
|
||||
if updateStrategyFilter != "" {
|
||||
query = query.Where("update_strategy = ?", updateStrategyFilter)
|
||||
}
|
||||
if updateMethodFilter != "" {
|
||||
query = query.Where("update_method = ?", updateMethodFilter)
|
||||
}
|
||||
if searchFilter != "" {
|
||||
searchLower := strings.ToLower(searchFilter)
|
||||
query = query.Where("LOWER(version) LIKE ? OR LOWER(description) LIKE ?", "%"+searchLower+"%", "%"+searchLower+"%")
|
||||
}
|
||||
|
||||
var versions []model.Version
|
||||
offset := 0
|
||||
@@ -84,7 +117,7 @@ func handleGetAllVersions(c *gin.Context) {
|
||||
limit = pageSizeInt
|
||||
}
|
||||
|
||||
if err := database.DB.Where("application_id IN ?", appIDs).Order("created_at DESC").Limit(limit).Offset(offset).Find(&versions).Error; err != nil {
|
||||
if err := query.Order("created_at DESC").Limit(limit).Offset(offset).Find(&versions).Error; err != nil {
|
||||
response.Error(c, 500, "获取版本列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -57,6 +57,12 @@ function emitValue() {
|
||||
}
|
||||
}
|
||||
|
||||
watch(open, (isOpen) => {
|
||||
if (!isOpen && selectedDate.value) {
|
||||
emitValue()
|
||||
}
|
||||
})
|
||||
|
||||
function resetTime() {
|
||||
selectedDate.value = undefined
|
||||
selectedHour.value = '00'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { CheckCircle, Megaphone, Pin, Plus, XCircle } from 'lucide-vue-next'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { toast } from 'vue-sonner'
|
||||
@@ -32,6 +32,9 @@ const appFilter = ref<string>('')
|
||||
const typeFilter = ref<string>('')
|
||||
const statusFilter = ref<string>('')
|
||||
const searchFilter = ref<string>('')
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const total = ref(0)
|
||||
|
||||
const deleteDialogOpen = ref(false)
|
||||
const deleteTarget = ref<Announcement | null>(null)
|
||||
@@ -45,7 +48,7 @@ const applicationOptions = computed(() => {
|
||||
}))
|
||||
})
|
||||
|
||||
const typeOptions = computed(() => [
|
||||
const methodOptions = computed(() => [
|
||||
{ label: t('admin.announcements.types.info'), value: 'info' },
|
||||
{ label: t('admin.announcements.types.warning'), value: 'warning' },
|
||||
{ label: t('admin.announcements.types.error'), value: 'error' },
|
||||
@@ -57,36 +60,22 @@ const statusOptions = computed(() => [
|
||||
{ label: t('admin.announcements.statuses.inactive'), value: 'inactive' },
|
||||
])
|
||||
|
||||
const filteredAnnouncements = computed(() => {
|
||||
let result = announcements.value
|
||||
const activeCount = computed(() => announcements.value.filter(a => a.status === 'active').length)
|
||||
const inactiveCount = computed(() => announcements.value.filter(a => a.status === 'inactive').length)
|
||||
const topCount = computed(() => announcements.value.filter(a => a.is_top).length)
|
||||
|
||||
if (appFilter.value) {
|
||||
result = result.filter(a => String(a.application_id) === appFilter.value)
|
||||
}
|
||||
|
||||
if (typeFilter.value) {
|
||||
result = result.filter(a => a.type === typeFilter.value)
|
||||
}
|
||||
|
||||
if (statusFilter.value) {
|
||||
result = result.filter(a => a.status === statusFilter.value)
|
||||
}
|
||||
|
||||
if (searchFilter.value) {
|
||||
const search = searchFilter.value.toLowerCase()
|
||||
result = result.filter(a =>
|
||||
a.title?.toLowerCase().includes(search)
|
||||
|| a.content?.toLowerCase().includes(search)
|
||||
|| a.application_name?.toLowerCase().includes(search),
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
const activeCount = computed(() => filteredAnnouncements.value.filter(a => a.status === 'active').length)
|
||||
const inactiveCount = computed(() => filteredAnnouncements.value.filter(a => a.status === 'inactive').length)
|
||||
const topCount = computed(() => filteredAnnouncements.value.filter(a => a.is_top).length)
|
||||
const serverPagination = computed(() => ({
|
||||
page: currentPage.value,
|
||||
pageSize: pageSize.value,
|
||||
total: total.value,
|
||||
onPageChange: (newPage: number) => {
|
||||
currentPage.value = newPage
|
||||
},
|
||||
onPageSizeChange: (newPageSize: number) => {
|
||||
pageSize.value = newPageSize
|
||||
currentPage.value = 1
|
||||
},
|
||||
}))
|
||||
|
||||
async function fetchApplications() {
|
||||
try {
|
||||
@@ -101,8 +90,26 @@ async function fetchApplications() {
|
||||
async function fetchAnnouncements() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await api.get<{ announcements: Announcement[], total: number }>('/dev/announcements')
|
||||
const params = new URLSearchParams()
|
||||
params.append('page', String(currentPage.value))
|
||||
params.append('page_size', String(pageSize.value))
|
||||
|
||||
if (appFilter.value) {
|
||||
params.append('application_id', appFilter.value)
|
||||
}
|
||||
if (typeFilter.value) {
|
||||
params.append('type', typeFilter.value)
|
||||
}
|
||||
if (statusFilter.value) {
|
||||
params.append('status', statusFilter.value)
|
||||
}
|
||||
if (searchFilter.value) {
|
||||
params.append('search', searchFilter.value)
|
||||
}
|
||||
|
||||
const data = await api.get<{ announcements: Announcement[], total: number }>(`/dev/announcements?${params.toString()}`)
|
||||
announcements.value = data?.announcements || []
|
||||
total.value = data?.total || 0
|
||||
}
|
||||
catch (error) {
|
||||
console.error('加载公告失败:', error)
|
||||
@@ -209,6 +216,15 @@ onMounted(() => {
|
||||
appFilter.value = appParam
|
||||
}
|
||||
})
|
||||
|
||||
watch([currentPage, pageSize], () => {
|
||||
fetchAnnouncements()
|
||||
})
|
||||
|
||||
watch([appFilter, typeFilter, statusFilter, searchFilter], () => {
|
||||
currentPage.value = 1
|
||||
fetchAnnouncements()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -288,7 +304,8 @@ onMounted(() => {
|
||||
<DataTable
|
||||
ref="tableRef"
|
||||
:loading
|
||||
:data="filteredAnnouncements"
|
||||
:data="announcements"
|
||||
:server-pagination="serverPagination"
|
||||
:on-toggle-top="toggleTop"
|
||||
:on-edit="goToEdit"
|
||||
:on-delete="confirmDelete"
|
||||
|
||||
@@ -131,6 +131,14 @@ const dynamicKeys = computed(() => {
|
||||
return Array.from(keys).sort()
|
||||
})
|
||||
|
||||
const filteredRecords = computed(() => {
|
||||
return records.value
|
||||
})
|
||||
|
||||
const paginatedRecords = computed(() => {
|
||||
return records.value
|
||||
})
|
||||
|
||||
const columns = computed<ColumnDef<VariableRecord>[]>(() => {
|
||||
const cols: ColumnDef<VariableRecord>[] = [SelectColumn as ColumnDef<VariableRecord>]
|
||||
|
||||
@@ -278,9 +286,16 @@ watch([page, pageSize], () => {
|
||||
fetchRecords()
|
||||
})
|
||||
|
||||
watch(dateRange, () => {
|
||||
watch(searchQuery, () => {
|
||||
page.value = 1
|
||||
fetchRecords()
|
||||
})
|
||||
|
||||
watch(dateRange, (newVal, oldVal) => {
|
||||
if (newVal.from !== oldVal.from || newVal.to !== oldVal.to) {
|
||||
page.value = 1
|
||||
fetchRecords()
|
||||
}
|
||||
}, { deep: true })
|
||||
|
||||
async function fetchVariable() {
|
||||
@@ -306,9 +321,18 @@ async function fetchRecords() {
|
||||
const params = new URLSearchParams()
|
||||
params.append('page', String(page.value))
|
||||
params.append('page_size', String(pageSize.value))
|
||||
if (dateRange.value.from) params.append('start_date', dateRange.value.from)
|
||||
if (dateRange.value.to) params.append('end_date', dateRange.value.to)
|
||||
if (searchQuery.value) params.append('search', searchQuery.value)
|
||||
|
||||
if (dateRange.value.from) {
|
||||
const fromDate = dateRange.value.from.split('T')[0]
|
||||
params.append('start_date', fromDate)
|
||||
}
|
||||
if (dateRange.value.to) {
|
||||
const toDate = dateRange.value.to.split('T')[0]
|
||||
params.append('end_date', toDate)
|
||||
}
|
||||
if (searchQuery.value.trim()) {
|
||||
params.append('search', searchQuery.value.trim())
|
||||
}
|
||||
|
||||
const data = await api.get<{ records: VariableRecord[], total: number }>(`/dev/cloud-variables/${variable.value.id}/records?${params}`)
|
||||
records.value = data?.records || []
|
||||
@@ -374,11 +398,6 @@ async function handleBatchDelete() {
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
page.value = 1
|
||||
fetchRecords()
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await fetchVariable()
|
||||
if (variable.value) {
|
||||
@@ -472,7 +491,6 @@ onMounted(async () => {
|
||||
v-model="searchQuery"
|
||||
:placeholder="t('admin.cloudVariables.records.searchPlaceholder')"
|
||||
class="pl-8 h-8 w-[150px] lg:w-[250px]"
|
||||
@keydown.enter="handleSearch"
|
||||
/>
|
||||
</div>
|
||||
<DateTimePicker
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { Ban, CheckCircle, Layers, Monitor } from 'lucide-vue-next'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { toast } from 'vue-sonner'
|
||||
@@ -35,6 +35,9 @@ const users = ref<User[]>([])
|
||||
const appFilter = ref<string>('')
|
||||
const statusFilter = ref<string>('')
|
||||
const userIdFilter = ref<string>('')
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const total = ref(0)
|
||||
|
||||
const deleteDialogOpen = ref(false)
|
||||
const deleteTarget = ref<Device | null>(null)
|
||||
@@ -43,27 +46,9 @@ const batchDeleteIds = ref<(string | number)[]>([])
|
||||
const forceOfflineDialogOpen = ref(false)
|
||||
const forceOfflineTarget = ref<Device | null>(null)
|
||||
|
||||
const filteredDevices = computed(() => {
|
||||
let result = devices.value
|
||||
|
||||
if (appFilter.value) {
|
||||
result = result.filter(device => String(device.application_id) === appFilter.value)
|
||||
}
|
||||
|
||||
if (statusFilter.value) {
|
||||
result = result.filter(device => device.status === statusFilter.value)
|
||||
}
|
||||
|
||||
if (userIdFilter.value) {
|
||||
result = result.filter(device => String(device.user_id) === userIdFilter.value)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
const totalDevices = computed(() => filteredDevices.value.length)
|
||||
const onlineSessionCount = computed(() => filteredDevices.value.reduce((sum, d) => sum + (d.online_sessions || 0), 0))
|
||||
const bannedCount = computed(() => filteredDevices.value.filter(device => device.status === 'banned').length)
|
||||
const totalDevices = computed(() => total.value)
|
||||
const onlineSessionCount = computed(() => devices.value.reduce((sum, d) => sum + (d.online_sessions || 0), 0))
|
||||
const bannedCount = computed(() => devices.value.filter(device => device.status === 'banned').length)
|
||||
|
||||
const applicationOptions = computed(() => {
|
||||
return applications.value.map(app => ({
|
||||
@@ -110,20 +95,29 @@ async function fetchDevices() {
|
||||
const userId = route.query.user_id as string
|
||||
const deviceId = route.query.device_id as string
|
||||
|
||||
let url = '/dev/devices'
|
||||
const params: string[] = []
|
||||
const params = new URLSearchParams()
|
||||
params.append('page', String(currentPage.value))
|
||||
params.append('page_size', String(pageSize.value))
|
||||
|
||||
if (userId) {
|
||||
params.push(`user_id=${userId}`)
|
||||
params.append('user_id', userId)
|
||||
}
|
||||
if (deviceId) {
|
||||
params.push(`device_id=${deviceId}`)
|
||||
params.append('device_id', deviceId)
|
||||
}
|
||||
if (params.length > 0) {
|
||||
url += `?${params.join('&')}`
|
||||
if (appFilter.value) {
|
||||
params.append('application_id', appFilter.value)
|
||||
}
|
||||
if (statusFilter.value) {
|
||||
params.append('status', statusFilter.value)
|
||||
}
|
||||
if (userIdFilter.value) {
|
||||
params.append('user_id', userIdFilter.value)
|
||||
}
|
||||
|
||||
const data = await api.get<{ devices: Device[] }>(url)
|
||||
const data = await api.get<{ devices: Device[], total: number }>(`/dev/devices?${params.toString()}`)
|
||||
devices.value = Array.isArray(data?.devices) ? data.devices : []
|
||||
total.value = data?.total || 0
|
||||
}
|
||||
catch (error) {
|
||||
console.error('获取设备列表失败:', error)
|
||||
@@ -235,6 +229,15 @@ onMounted(() => {
|
||||
fetchUsers()
|
||||
fetchDevices()
|
||||
})
|
||||
|
||||
watch([currentPage, pageSize], () => {
|
||||
fetchDevices()
|
||||
})
|
||||
|
||||
watch([appFilter, statusFilter, userIdFilter], () => {
|
||||
currentPage.value = 1
|
||||
fetchDevices()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -307,7 +310,8 @@ onMounted(() => {
|
||||
<DataTable
|
||||
ref="tableRef"
|
||||
:loading
|
||||
:data="filteredDevices"
|
||||
:data="devices"
|
||||
:server-pagination="serverPagination"
|
||||
:on-toggle-status="toggleDeviceStatus"
|
||||
:on-delete="confirmDeleteDevice"
|
||||
:on-force-offline="confirmForceOffline"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { Boxes, Monitor, Users, Wifi } from 'lucide-vue-next'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { toast } from 'vue-sonner'
|
||||
@@ -28,6 +28,9 @@ const applications = ref<Application[]>([])
|
||||
const appFilter = ref<string>('')
|
||||
const searchFilter = ref<string>('')
|
||||
const usernameFilter = ref<string>('')
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const total = ref(0)
|
||||
|
||||
const forceOfflineDialogOpen = ref(false)
|
||||
const forceOfflineTarget = ref<Session | null>(null)
|
||||
@@ -50,32 +53,27 @@ const usernameOptions = computed(() => {
|
||||
const filteredSessions = computed(() => {
|
||||
let result = sessions.value.filter(s => s.is_online)
|
||||
|
||||
if (appFilter.value) {
|
||||
result = result.filter(session => String(session.application_id) === appFilter.value)
|
||||
}
|
||||
|
||||
if (usernameFilter.value) {
|
||||
result = result.filter(session => session.username === usernameFilter.value)
|
||||
}
|
||||
|
||||
if (searchFilter.value) {
|
||||
const search = searchFilter.value.toLowerCase()
|
||||
result = result.filter(session =>
|
||||
session.instance_id?.toLowerCase().includes(search)
|
||||
|| session.device_identifier?.toLowerCase().includes(search)
|
||||
|| session.username?.toLowerCase().includes(search)
|
||||
|| session.device_name?.toLowerCase().includes(search),
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
const totalSessions = computed(() => filteredSessions.value.length)
|
||||
const totalSessions = computed(() => total.value)
|
||||
const onlineApps = computed(() => new Set(filteredSessions.value.map(s => s.application_id)).size)
|
||||
const onlineDevices = computed(() => new Set(filteredSessions.value.filter(s => s.device_identifier).map(s => s.device_identifier)).size)
|
||||
const onlineUsers = computed(() => new Set(filteredSessions.value.filter(s => s.username).map(s => s.username)).size)
|
||||
|
||||
const serverPagination = computed(() => ({
|
||||
page: currentPage.value,
|
||||
pageSize: pageSize.value,
|
||||
total: total.value,
|
||||
onPageChange: (newPage: number) => {
|
||||
currentPage.value = newPage
|
||||
},
|
||||
onPageSizeChange: (newPageSize: number) => {
|
||||
pageSize.value = newPageSize
|
||||
currentPage.value = 1
|
||||
},
|
||||
}))
|
||||
|
||||
async function fetchApplications() {
|
||||
try {
|
||||
const data = await api.get<{ applications: Application[] }>('/dev/applications')
|
||||
@@ -89,8 +87,23 @@ async function fetchApplications() {
|
||||
async function fetchSessions() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await api.get<{ sessions: Session[] }>('/dev/sessions')
|
||||
const params = new URLSearchParams()
|
||||
params.append('page', String(currentPage.value))
|
||||
params.append('page_size', String(pageSize.value))
|
||||
|
||||
if (appFilter.value) {
|
||||
params.append('app_id', appFilter.value)
|
||||
}
|
||||
if (usernameFilter.value) {
|
||||
params.append('username', usernameFilter.value)
|
||||
}
|
||||
if (searchFilter.value) {
|
||||
params.append('search', searchFilter.value)
|
||||
}
|
||||
|
||||
const data = await api.get<{ sessions: Session[], total: number }>(`/dev/sessions?${params.toString()}`)
|
||||
sessions.value = data?.sessions || []
|
||||
total.value = data?.total || 0
|
||||
}
|
||||
catch (error: any) {
|
||||
console.error('获取会话列表失败:', error)
|
||||
@@ -133,6 +146,15 @@ onMounted(() => {
|
||||
fetchApplications()
|
||||
fetchSessions()
|
||||
})
|
||||
|
||||
watch([currentPage, pageSize], () => {
|
||||
fetchSessions()
|
||||
})
|
||||
|
||||
watch([appFilter, usernameFilter, searchFilter], () => {
|
||||
currentPage.value = 1
|
||||
fetchSessions()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -205,6 +227,7 @@ onMounted(() => {
|
||||
<DataTable
|
||||
:loading
|
||||
:data="filteredSessions"
|
||||
:server-pagination="serverPagination"
|
||||
:applications="applications"
|
||||
:app-filter="appFilter"
|
||||
:search-filter="searchFilter"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { Ban, CheckCircle, Clock, Plus, Users } from 'lucide-vue-next'
|
||||
import { computed, onActivated, onMounted, ref } from 'vue'
|
||||
import { computed, onActivated, onMounted, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { toast } from 'vue-sonner'
|
||||
@@ -34,6 +34,9 @@ const lastLoginStartDate = ref<string>('')
|
||||
const lastLoginEndDate = ref<string>('')
|
||||
const createdStartDate = ref<string>('')
|
||||
const createdEndDate = ref<string>('')
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const total = ref(0)
|
||||
|
||||
const deleteDialogOpen = ref(false)
|
||||
const deleteTarget = ref<User | null>(null)
|
||||
@@ -43,18 +46,10 @@ const batchDeleteIds = ref<(string | number)[]>([])
|
||||
const filteredUsers = computed(() => {
|
||||
let result = users.value
|
||||
|
||||
if (appFilter.value) {
|
||||
result = result.filter(user => String(user.application_id) === appFilter.value)
|
||||
}
|
||||
|
||||
if (statusFilter.value) {
|
||||
result = result.filter(user => user.online_status === statusFilter.value)
|
||||
}
|
||||
|
||||
if (accountStatusFilter.value) {
|
||||
result = result.filter(user => user.status === accountStatusFilter.value)
|
||||
}
|
||||
|
||||
if (lastLoginStartDate.value) {
|
||||
const fromDateTime = lastLoginStartDate.value.includes('T')
|
||||
? lastLoginStartDate.value.replace('T', ' ')
|
||||
@@ -94,9 +89,22 @@ const filteredUsers = computed(() => {
|
||||
return result
|
||||
})
|
||||
|
||||
const onlineCount = computed(() => filteredUsers.value.filter(user => user.online_status === 'online').length)
|
||||
const offlineCount = computed(() => filteredUsers.value.filter(user => user.online_status === 'offline').length)
|
||||
const bannedCount = computed(() => filteredUsers.value.filter(user => user.status === 'banned').length)
|
||||
const onlineCount = computed(() => users.value.filter(user => user.online_status === 'online').length)
|
||||
const offlineCount = computed(() => users.value.filter(user => user.online_status === 'offline').length)
|
||||
const bannedCount = computed(() => users.value.filter(user => user.status === 'banned').length)
|
||||
|
||||
const serverPagination = computed(() => ({
|
||||
page: currentPage.value,
|
||||
pageSize: pageSize.value,
|
||||
total: total.value,
|
||||
onPageChange: (newPage: number) => {
|
||||
currentPage.value = newPage
|
||||
},
|
||||
onPageSizeChange: (newPageSize: number) => {
|
||||
pageSize.value = newPageSize
|
||||
currentPage.value = 1
|
||||
},
|
||||
}))
|
||||
|
||||
const applicationOptions = computed(() => {
|
||||
return applications.value.map(app => ({
|
||||
@@ -128,8 +136,20 @@ async function fetchApplications() {
|
||||
async function fetchUsers() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await api.get<{ users: User[] }>('/dev/app-users')
|
||||
const params = new URLSearchParams()
|
||||
params.append('page', String(currentPage.value))
|
||||
params.append('page_size', String(pageSize.value))
|
||||
|
||||
if (appFilter.value) {
|
||||
params.append('application_id', appFilter.value)
|
||||
}
|
||||
if (accountStatusFilter.value) {
|
||||
params.append('status', accountStatusFilter.value)
|
||||
}
|
||||
|
||||
const data = await api.get<{ users: User[], total: number }>(`/dev/app-users?${params.toString()}`)
|
||||
users.value = Array.isArray(data?.users) ? data.users : []
|
||||
total.value = data?.total || 0
|
||||
}
|
||||
catch (error) {
|
||||
console.error('获取用户列表失败:', error)
|
||||
@@ -231,6 +251,15 @@ onMounted(() => {
|
||||
onActivated(() => {
|
||||
fetchUsers()
|
||||
})
|
||||
|
||||
watch([currentPage, pageSize], () => {
|
||||
fetchUsers()
|
||||
})
|
||||
|
||||
watch([appFilter, statusFilter, accountStatusFilter, lastLoginStartDate, lastLoginEndDate, createdStartDate, createdEndDate], () => {
|
||||
currentPage.value = 1
|
||||
fetchUsers()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -311,6 +340,7 @@ onActivated(() => {
|
||||
ref="tableRef"
|
||||
:loading
|
||||
:data="filteredUsers"
|
||||
:server-pagination="serverPagination"
|
||||
:on-edit="goToEdit"
|
||||
:on-toggle-status="toggleUserStatus"
|
||||
:on-delete="confirmDeleteUser"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { AlertTriangle, GitBranch, HardDrive, Package, Plus } from 'lucide-vue-next'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { toast } from 'vue-sonner'
|
||||
@@ -32,11 +32,17 @@ const appFilter = ref<string>('')
|
||||
const strategyFilter = ref<string>('')
|
||||
const methodFilter = ref<string>('')
|
||||
const searchFilter = ref<string>('')
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const total = ref(0)
|
||||
|
||||
const deleteDialogOpen = ref(false)
|
||||
const deleteTarget = ref<Version | null>(null)
|
||||
const batchDeleteDialogOpen = ref(false)
|
||||
const batchDeleteIds = ref<number[]>([])
|
||||
const exportDialogOpen = ref(false)
|
||||
const exportTarget = ref<Version | null>(null)
|
||||
const batchExportDialogOpen = ref(false)
|
||||
|
||||
const applicationOptions = computed(() => {
|
||||
return applications.value.map(app => ({
|
||||
@@ -55,38 +61,24 @@ const methodOptions = computed(() => [
|
||||
{ label: t('admin.versions.methods.auto'), value: 'auto' },
|
||||
])
|
||||
|
||||
const filteredVersions = computed(() => {
|
||||
let result = versions.value
|
||||
|
||||
if (appFilter.value) {
|
||||
result = result.filter(v => String(v.application_id) === appFilter.value)
|
||||
}
|
||||
|
||||
if (strategyFilter.value) {
|
||||
result = result.filter(v => v.update_strategy === strategyFilter.value)
|
||||
}
|
||||
|
||||
if (methodFilter.value) {
|
||||
result = result.filter(v => v.update_method === methodFilter.value)
|
||||
}
|
||||
|
||||
if (searchFilter.value) {
|
||||
const search = searchFilter.value.toLowerCase()
|
||||
result = result.filter(v =>
|
||||
v.version?.toLowerCase().includes(search)
|
||||
|| v.description?.toLowerCase().includes(search)
|
||||
|| v.application_name?.toLowerCase().includes(search),
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
const totalSize = computed(() => {
|
||||
return filteredVersions.value.reduce((sum, v) => sum + (v.file_size || 0), 0)
|
||||
return versions.value.reduce((sum, v) => sum + (v.file_size || 0), 0)
|
||||
})
|
||||
|
||||
const forcedCount = computed(() => filteredVersions.value.filter(v => v.update_strategy === 'forced').length)
|
||||
const forcedCount = computed(() => versions.value.filter(v => v.update_strategy === 'forced').length)
|
||||
|
||||
const serverPagination = computed(() => ({
|
||||
page: currentPage.value,
|
||||
pageSize: pageSize.value,
|
||||
total: total.value,
|
||||
onPageChange: (newPage: number) => {
|
||||
currentPage.value = newPage
|
||||
},
|
||||
onPageSizeChange: (newPageSize: number) => {
|
||||
pageSize.value = newPageSize
|
||||
currentPage.value = 1
|
||||
},
|
||||
}))
|
||||
|
||||
function formatFileSize(bytes: number): string {
|
||||
if (bytes === 0)
|
||||
@@ -110,8 +102,26 @@ async function fetchApplications() {
|
||||
async function fetchVersions() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await api.get<{ versions: Version[], total: number }>('/dev/versions')
|
||||
const params = new URLSearchParams()
|
||||
params.append('page', String(currentPage.value))
|
||||
params.append('page_size', String(pageSize.value))
|
||||
|
||||
if (appFilter.value) {
|
||||
params.append('application_id', appFilter.value)
|
||||
}
|
||||
if (strategyFilter.value) {
|
||||
params.append('update_strategy', strategyFilter.value)
|
||||
}
|
||||
if (methodFilter.value) {
|
||||
params.append('update_method', methodFilter.value)
|
||||
}
|
||||
if (searchFilter.value) {
|
||||
params.append('search', searchFilter.value)
|
||||
}
|
||||
|
||||
const data = await api.get<{ versions: Version[], total: number }>(`/dev/versions?${params.toString()}`)
|
||||
versions.value = data?.versions || []
|
||||
total.value = data?.total || 0
|
||||
}
|
||||
catch (error) {
|
||||
console.error('加载版本列表失败:', error)
|
||||
@@ -214,6 +224,15 @@ onMounted(() => {
|
||||
appFilter.value = appParam
|
||||
}
|
||||
})
|
||||
|
||||
watch([currentPage, pageSize], () => {
|
||||
fetchVersions()
|
||||
})
|
||||
|
||||
watch([appFilter, strategyFilter, methodFilter, searchFilter], () => {
|
||||
currentPage.value = 1
|
||||
fetchVersions()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -293,7 +312,8 @@ onMounted(() => {
|
||||
<DataTable
|
||||
ref="tableRef"
|
||||
:loading
|
||||
:data="filteredVersions"
|
||||
:data="versions"
|
||||
:server-pagination="serverPagination"
|
||||
:on-download="downloadVersion"
|
||||
:on-edit="goToEdit"
|
||||
:on-delete="confirmDelete"
|
||||
|
||||
Reference in New Issue
Block a user