频道筛选:text_message 表增加 (channel_id, created_at) 复合索引,主页增加频道筛选输入框,服务端筛选+防抖重载,版本号 backend 1.1.0 / frontend 0.2.0
This commit is contained in:
@@ -439,7 +439,7 @@ type TextMessageRecord struct {
|
||||
Text *string `gorm:"column:text"`
|
||||
PayloadHex *string `gorm:"column:payload_hex"`
|
||||
Topic string `gorm:"column:topic;not null"`
|
||||
ChannelID *string `gorm:"column:channel_id"`
|
||||
ChannelID *string `gorm:"column:channel_id;index:idx_text_message_channel_id_created_at,priority:1"`
|
||||
GatewayID *string `gorm:"column:gateway_id"`
|
||||
PacketID *int64 `gorm:"column:packet_id;index:idx_text_message_packet_id"`
|
||||
PacketTo *string `gorm:"column:packet_to"`
|
||||
@@ -458,7 +458,7 @@ type TextMessageRecord struct {
|
||||
MQTTRemoteHost *string `gorm:"column:mqtt_remote_host"`
|
||||
MQTTRemotePort *string `gorm:"column:mqtt_remote_port"`
|
||||
ContentJSON string `gorm:"column:content_json;not null"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;index:idx_text_message_from_num_created_at,priority:2;index:idx_text_message_created_at"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;index:idx_text_message_from_num_created_at,priority:2;index:idx_text_message_created_at;index:idx_text_message_channel_id_created_at,priority:2"`
|
||||
}
|
||||
|
||||
func (TextMessageRecord) TableName() string {
|
||||
@@ -707,7 +707,7 @@ func (s *Store) migrate() error {
|
||||
model any
|
||||
indexes []string
|
||||
}{
|
||||
{label: "text_message", model: &TextMessageRecord{}, indexes: []string{"idx_text_message_from_num_created_at", "idx_text_message_created_at", "idx_text_message_packet_id"}},
|
||||
{label: "text_message", model: &TextMessageRecord{}, indexes: []string{"idx_text_message_from_num_created_at", "idx_text_message_created_at", "idx_text_message_packet_id", "idx_text_message_channel_id_created_at"}},
|
||||
{label: "bot_direct_messages", model: &BotDirectMessageRecord{}, indexes: []string{"idx_bot_dm_bot_peer", "idx_bot_dm_bot_created_at"}},
|
||||
{label: "llm_message_queue", model: &LLMMessageQueueRecord{}, indexes: []string{"idx_llm_queue_bot_created"}},
|
||||
} {
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ func NewRouter(cfg configpkg.WebConfig, consoleLog bool, store *storepkg.Store,
|
||||
return r
|
||||
}
|
||||
|
||||
const BackendVersion = "1.0.0"
|
||||
const BackendVersion = "1.1.0"
|
||||
|
||||
var CommitVersion = "dev"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import { adminLogout, createNodeBlockingRule, deleteNode, deleteTextMessage, getAdminMe, getHealth, getMapReportViewport, getNodeInfo, getPositions, getTextMessages, purgeNode } from './api'
|
||||
import AdminBlockingManagement from './components/AdminBlockingManagement.vue'
|
||||
import AdminBot from './components/AdminBot.vue'
|
||||
@@ -69,6 +69,7 @@ const mapReportTotal = ref(0)
|
||||
const mapSources = ref<PublicMapTileSource[]>([fallbackMapSource])
|
||||
const mapSource = ref<PublicMapTileSource>(fallbackMapSource)
|
||||
const nodeFilter = ref('')
|
||||
const channelFilter = ref('')
|
||||
const pendingDeleteAction = ref<PendingDeleteAction | null>(null)
|
||||
type DeletableTextMessage = TextMessage & { mergedCount?: number; mergedMessages?: TextMessage[] }
|
||||
type NodeActionRequest = { nodeId: string; nodeNum: number | null; message?: DeletableTextMessage }
|
||||
@@ -95,6 +96,7 @@ const nodesById = computed<NodeInfoById>(() => {
|
||||
})
|
||||
|
||||
const normalizedNodeFilter = computed(() => nodeFilter.value.trim().toLowerCase())
|
||||
const normalizedChannelFilter = computed(() => channelFilter.value.trim())
|
||||
|
||||
function nodeMatchesFilterByInfo(node: NodeInfo | null | undefined, keyword: string): boolean {
|
||||
if (!keyword) {
|
||||
@@ -298,7 +300,7 @@ function clearSelectedNode() {
|
||||
}
|
||||
|
||||
async function loadInitialChatMessages() {
|
||||
const response = await getTextMessages(chatPageSize, 0)
|
||||
const response = await getTextMessages(chatPageSize, 0, normalizedChannelFilter.value ? { channelId: normalizedChannelFilter.value } : '')
|
||||
messages.value = toChronological(response.items)
|
||||
chatHasMore.value = response.items.length === chatPageSize
|
||||
chatInitialized.value = true
|
||||
@@ -311,7 +313,7 @@ async function loadOlderMessages() {
|
||||
|
||||
chatLoadingOlder.value = true
|
||||
try {
|
||||
const response = await getTextMessages(chatPageSize, messages.value.length)
|
||||
const response = await getTextMessages(chatPageSize, messages.value.length, normalizedChannelFilter.value ? { channelId: normalizedChannelFilter.value } : '')
|
||||
messages.value = mergeMessages(messages.value, toChronological(response.items))
|
||||
chatHasMore.value = response.items.length === chatPageSize
|
||||
} catch (err) {
|
||||
@@ -322,10 +324,23 @@ async function loadOlderMessages() {
|
||||
}
|
||||
|
||||
async function pollLatestMessages() {
|
||||
const response = await getTextMessages(chatPageSize, 0)
|
||||
const response = await getTextMessages(chatPageSize, 0, normalizedChannelFilter.value ? { channelId: normalizedChannelFilter.value } : '')
|
||||
messages.value = mergeMessages(messages.value, toChronological(response.items))
|
||||
}
|
||||
|
||||
let channelFilterTimer: number | undefined
|
||||
watch(normalizedChannelFilter, () => {
|
||||
if (channelFilterTimer !== undefined) {
|
||||
window.clearTimeout(channelFilterTimer)
|
||||
}
|
||||
channelFilterTimer = window.setTimeout(() => {
|
||||
messages.value = []
|
||||
chatHasMore.value = true
|
||||
chatInitialized.value = false
|
||||
loadInitialChatMessages()
|
||||
}, 400)
|
||||
})
|
||||
|
||||
async function loadNodePage(page: number, showLoading = true) {
|
||||
if (showLoading) {
|
||||
nodePageLoading.value = true
|
||||
@@ -676,6 +691,9 @@ onBeforeUnmount(() => {
|
||||
if (mapBoundsTimer !== undefined) {
|
||||
window.clearTimeout(mapBoundsTimer)
|
||||
}
|
||||
if (channelFilterTimer !== undefined) {
|
||||
window.clearTimeout(channelFilterTimer)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -739,6 +757,20 @@ onBeforeUnmount(() => {
|
||||
@click="nodeFilter = ''"
|
||||
>清除</button>
|
||||
</div>
|
||||
<div class="topbar-filter">
|
||||
<input
|
||||
type="search"
|
||||
class="topbar-filter-input"
|
||||
v-model="channelFilter"
|
||||
placeholder="筛选频道"
|
||||
/>
|
||||
<button
|
||||
v-if="normalizedChannelFilter"
|
||||
type="button"
|
||||
class="topbar-filter-clear"
|
||||
@click="channelFilter = ''"
|
||||
>清除</button>
|
||||
</div>
|
||||
<a class="topbar-link" href="/signed">签到列表</a>
|
||||
<a class="topbar-link" href="/help">使用帮助</a>
|
||||
<a class="topbar-link" href="/admin">管理</a>
|
||||
|
||||
@@ -1 +1 @@
|
||||
export const FRONTEND_VERSION = '0.1.2'
|
||||
export const FRONTEND_VERSION = '0.2.0'
|
||||
Reference in New Issue
Block a user