diff --git a/widget/looker/.gitignore b/widget/looker/.gitignore new file mode 100644 index 0000000..129d522 --- /dev/null +++ b/widget/looker/.gitignore @@ -0,0 +1,3 @@ +build/bin +node_modules +frontend/dist diff --git a/widget/looker/app.go b/widget/looker/app.go index 5efbe7e..ba11b44 100644 --- a/widget/looker/app.go +++ b/widget/looker/app.go @@ -35,6 +35,14 @@ type BidItem struct { UpdatedAt string `json:"updatedAt"` } +// AiRecommendation AI推荐结构体 +type AiRecommendation struct { + ID string `json:"id"` + Title string `json:"title"` + Confidence int `json:"confidence"` + CreatedAt string `json:"createdAt"` +} + // App struct type App struct { ctx context.Context @@ -212,3 +220,59 @@ func (a *App) GetPinnedBidItems() ([]BidItem, error) { return items, nil } + +// GetAiRecommendations 获取 AI 推荐数据 +func (a *App) GetAiRecommendations() ([]AiRecommendation, error) { + dsn := a.GetDatabaseDSN() + if dsn == "" { + return nil, fmt.Errorf("数据库配置未加载") + } + + db, err := sql.Open("mysql", dsn) + if err != nil { + return nil, fmt.Errorf("连接数据库失败: %v", err) + } + defer db.Close() + + // 测试连接 + if err := db.Ping(); err != nil { + return nil, fmt.Errorf("数据库连接测试失败: %v", err) + } + + // 查询 ai_recommendations 表 + query := `SELECT id, title, confidence, createdAt + FROM ai_recommendations + ORDER BY createdAt DESC` + + rows, err := db.Query(query) + if err != nil { + return nil, fmt.Errorf("查询失败: %v", err) + } + defer rows.Close() + + var items []AiRecommendation + for rows.Next() { + var item AiRecommendation + var createdAt time.Time + + err := rows.Scan( + &item.ID, + &item.Title, + &item.Confidence, + &createdAt, + ) + if err != nil { + return nil, fmt.Errorf("扫描行失败: %v", err) + } + + item.CreatedAt = createdAt.Format("2006-01-02 15:04:05") + + items = append(items, item) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("遍历行失败: %v", err) + } + + return items, nil +} diff --git a/widget/looker/frontend/src/App.vue b/widget/looker/frontend/src/App.vue index 0e82db4..4a9184f 100644 --- a/widget/looker/frontend/src/App.vue +++ b/widget/looker/frontend/src/App.vue @@ -1,13 +1,47 @@ @@ -82,6 +117,26 @@ body { padding: 20px; } +.refresh-button { + margin-left: auto; + padding: 8px 16px; + background: #3498db; + color: #fff; + border: none; + border-radius: 4px; + font-size: 14px; + cursor: pointer; + transition: all 0.2s ease; +} + +.refresh-button:hover { + background: #2980b9; +} + +.refresh-button:active { + transform: scale(0.98); +} + .placeholder { display: flex; align-items: center; diff --git a/widget/looker/frontend/src/components/AiRecommendations.vue b/widget/looker/frontend/src/components/AiRecommendations.vue new file mode 100644 index 0000000..4a1176c --- /dev/null +++ b/widget/looker/frontend/src/components/AiRecommendations.vue @@ -0,0 +1,161 @@ + + + + + diff --git a/widget/looker/frontend/src/components/PinnedBids.vue b/widget/looker/frontend/src/components/PinnedBids.vue index b67c803..5191d0b 100644 --- a/widget/looker/frontend/src/components/PinnedBids.vue +++ b/widget/looker/frontend/src/components/PinnedBids.vue @@ -38,6 +38,10 @@ const openUrl = (url: string) => { onMounted(() => { loadPinnedBids() }) + +defineExpose({ + loadPinnedBids +})