library(shinyjs) library(shinyAce) ## ===== 统一入口===== output$quickgen_chat <- renderUI({ tagList( useShinyjs(), stat_tab_panel( menu = i18n$t("Oneclick generation > AI assistance"), tool = i18n$t("AI chat guidance"), tool_ui = "chat_main_ui", output_panels = tabPanel( title = i18n$t("Chat history"), value = "chat_panel", uiOutput("chat_history_area") ) ) ) }) ## ===== 左侧区域===== output$chat_main_ui <- renderUI({ tagList( useShinyjs(), wellPanel( div(style = "font-weight:bold; color:#1976d2; margin-bottom:10px;", icon("database"), " ", i18n$t("Dataset Fields Information") ), uiOutput("field_info_display"), tags$hr(), div(style = "color:#666; font-size:0.9em; margin-top:5px;", i18n$t("The current dataset's field information is automatically passed to the AI assistant.") ), style = "max-height:450px; overflow-y:auto; background-color:#f8f9fa;" ), help_and_report( modal_title = i18n$t("AI chat guidance"), fun_name = "quickgen_chat", help_file = inclMD(file.path(getOption("radiant.path.quickgen"), "app/tools/help/quickgen_chat.md")), lic = "by-sa" ) ) }) ## ===== 右侧区域===== output$chat_history_area <- renderUI({ field_info_encoded <- get_field_info() if (is.null(field_info_encoded)) { return(create_no_data_ui()) } dify_base_url <- "http://180.169.131.147:8078/chat/tfjTZpJDgjQpBeTl" dify_url <- paste0( dify_base_url, "?showSidebar=true", "&field_info=", field_info_encoded ) tagList( div( id = "chat_box", style = "height:700px; overflow-y:auto; border:1px solid #ddd; border-radius:4px; padding:0; background:#fff; margin-bottom:10px;", tags$iframe( src = dify_url, style = "width: 100%; height: 100%; border: 0;", frameborder = 0, allow = "microphone" ) ) ) }) # 生成左侧展示的格式化字段文本 output$field_info_display <- renderUI({ if (is.null(input$dataset) || !exists("r_data")) { return(tags$pre( i18n$t("Please select a dataset in another page first"), style = "margin:0; background-color:#f9f9f9; border:1px solid #ddd; padding:10px; font-size:0.9em;" )) } df <- tryCatch({ get(input$dataset, envir = r_data) }, error = function(e) NULL) if (is.null(df) || !is.data.frame(df) || nrow(df) == 0) { return(tags$pre( i18n$t("Current dataset is empty"), style = "margin:0; background-color:#f9f9f9; border:1px solid #ddd; padding:10px; font-size:0.9em;" )) } # 生成带换行的格式 field_lines <- sprintf('"%s": "%s"', names(df), sapply(df, function(x) class(x)[1])) formatted_text <- paste(field_lines, collapse = ",\n") tags$pre( formatted_text, style = "margin:0; background-color:#f9f9f9; border:1px solid #ddd; padding:10px; font-size:0.9em; line-height:1.5; white-space:pre-wrap;" ) }) # 获取并编码字段信息 get_field_info <- function() { if (is.null(input$dataset) || !exists("r_data")) return(NULL) df <- tryCatch(get(input$dataset, envir = r_data), error = function(e) NULL) if (is.null(df) || !is.data.frame(df) || nrow(df) == 0) return(NULL) fields_summary <- list() for (col in names(df)) { x <- df[[col]] cls <- class(x)[1] # 提取非 NA 的值 valid_x <- x[!is.na(x)] if (cls %in% c("numeric", "double")) { if (length(valid_x) == 0) { fields_summary[[col]] <- list(type = cls) } else { fields_summary[[col]] <- list( type = cls, min = round(min(valid_x), 3), max = round(max(valid_x), 3), mean = round(mean(valid_x), 3) ) } } else if (cls == "integer") { if (length(valid_x) == 0) { fields_summary[[col]] <- list(type = cls, levels = list(), n_levels = 0L) } else { unique_vals <- sort(unique(valid_x)) n_unique <- length(unique_vals) if (n_unique <= 10) { levs_out <- unique_vals } else { levs_out <- c(unique_vals[1:10], "...") } fields_summary[[col]] <- list( type = cls, levels = levs_out, n_levels = n_unique ) } } else { # factor 或 character if (length(valid_x) == 0) { fields_summary[[col]] <- list(type = cls, levels = list(), n_levels = 0L) } else { if (is.factor(x)) { # 只取实际出现的值 unique_vals <- sort(unique(as.character(valid_x))) } else { unique_vals <- sort(unique(as.character(valid_x))) } n_unique <- length(unique_vals) if (n_unique <= 10) { levs_out <- unique_vals } else { levs_out <- c(unique_vals[1:10], "...") } fields_summary[[col]] <- list( type = cls, levels = levs_out, n_levels = n_unique ) } } } json_struct <- list( dataset_name = input$dataset, n_rows = nrow(df), fields = fields_summary ) URLencode(jsonlite::toJSON(json_struct, auto_unbox = TRUE, digits = 3), reserved = TRUE) } # 无数据时的UI create_no_data_ui <- function() { tagList( div( id = "chat_box", style = "height:700px; overflow-y:auto; border:1px solid #ddd; border-radius:4px; padding:20px; background:#fff; margin-bottom:10px;", p(i18n$t("请选择数据集"), style = "text-align:center; margin-top:50px; color:#888;") ) ) }