# load packages
library(shiny)
library(dplyr)
library(ggplot2)
library(shinythemes)
custom_css <- "
.record-table {
margin-top: 20px;
}
"
# 定义 UI 组件
ui <- bootstrapPage(
theme = shinytheme("flatly"),
headerPanel("服务费用计算器"),
sidebarPanel(
textInput(inputId = "name", label = "客户名字:", value = ""),
selectInput(inputId = "service", label = "服务类型:",
choices = c("日常清洁", "深层清洁", "搬出/搬入")),
checkboxGroupInput(inputId = "addone", label = "附加服务:",
choices = c("烤箱" = "烤箱", "窗户" = "窗户", "墙和天花板" = "墙和天花板")),
numericInput(inputId = "oven_num", label = "烤箱数量:", value = 1, min = 1),
numericInput(inputId = "window_num", label = "窗户数量:", value = 1, min = 1),
textInput(inputId = "time", label = "服务时间(小时):", value = "1"),
dateInput(inputId = "date", label = "服务日期:",
value = Sys.Date()),
actionButton(inputId = "add_gst_button", label = "添加 GST(15%)"),
actionButton(inputId = "remove_gst_button", label = "取消 GST(15%)"),
actionButton(inputId = "add", label = "添加服务记录"),
actionButton(inputId = "plot_button", label = "生成图表")
),
mainPanel(
h3("计算结果:"),
textOutput(outputId = "total"),
downloadButton(outputId = "download", label = "下载服务记录"),
tags$head(tags$style(HTML(custom_css))),
fluidRow(
column(12,
plotOutput(outputId = "plot")
),
column(12,
div(class = "record-table",
tableOutput(outputId = "records")
)
)
)
),
)
# 定义 server 组件
server <- function(input, output, session) {
gst_active <- reactiveVal(FALSE)
observeEvent(input$add_gst_button, {
gst_active(TRUE)
})
observeEvent(input$remove_gst_button, {
gst_active(FALSE)
})
service_records <- reactiveValues(data = data.frame(Date = character(),
Name = character(),
Service = character(),
Time = numeric(),
Cost = numeric(),
Deleted = logical(),
stringsAsFactors = FALSE))
# 计算总费用
total_cost <- reactive({
if(input$service == "日常清洁") {
cost_per_hour_service <- 40
} else if(input$service == "深层清洁") {
cost_per_hour_service <- 100
} else {
cost_per_hour_service <- 200
}
# 初始化 cost_per_hour_addone 变量
cost_per_hour_addone <- 0
if("烤箱" %in% input$addone) {
cost_per_hour_addone <- cost_per_hour_addone + (60 * input$oven_num)
}
if("窗户" %in% input$addone) {
cost_per_hour_addone <- cost_per_hour_addone + (30 * input$window_num)
}
if("墙和天花板" %in% input$addone) {
cost_per_hour_addone <- cost_per_hour_addone + 120
}
total <- (cost_per_hour_service) * as.numeric(input$time) + cost_per_hour_addone
# 应用 GST
if (gst_active()) {
total <- total * 1.15
}
return(total)
})
# 添加服务记录
observeEvent(input$add, {
if(input$name != "") {
record <- data.frame(Date = format(input$date, "%m/%d/%y"),
Name = input$name,
Service = paste(input$service, "和", paste(input$addone, collapse = ",")),
Time = as.numeric(input$time),
Cost = total_cost(),
stringsAsFactors = FALSE)
service_records$data <- rbind(service_records$data, record)
}
})
# 显示计算结果
output$total <- renderText({
if(input$name != "") {
cost <- total_cost()
paste0(input$name, " 需要支付 $", round(cost, 2), " 的服务费用。")
} else {
"请输入客户名字。"
}
})
# 绘制时间序列图
output$plot <- renderPlot({
if(input$plot_button > 0) {
plot_data <- service_records$data %>%
mutate(Date = as.Date(Date, "%m/%d/%y")) %>%
group_by(Date) %>%
summarize(TotalCost = sum(Cost))
ggplot(plot_data, aes(x = Date, y = TotalCost)) +
geom_line() +
geom_text(aes(label = round(TotalCost, 2)), vjust = -0.5, size = 4) +
labs(title = "服务费用时间序列图", x = "日期", y = "服务费用") +
theme_minimal()
}
})
# 下载服务记录
output$download <- downloadHandler(
filename = function() {
paste("service_records_", Sys.Date(), ".csv", sep = "")
},
content = function(file) {
req(service_records$data)
write.csv(service_records$data, file, row.names = FALSE)
}
)
# 显示服务记录
output$records <- renderTable({
service_records$data %>%
arrange(desc(Date))
})
}
# 运行应用程序
shinyApp(ui, server)
Introduction to Shiny
Shiny is an R package for creating interactive web applications directly from R without requiring web development knowledge. A Shiny app generally consists of a user interface (UI) and a server component that contains the app’s logic and computations.
Purpose of this app
This Shiny app is a service fee calculator designed to calculate and track service fees based on various parameters, such as customer name, service type, add-on services, and service time. The app also allows users to apply or remove a 15% Goods and Services Tax (GST) on the total cost.
User Interface (UI)
The UI is divided into three main sections:
Header Panel: Contains the app’s title “服务费用计算器” (Service Fee Calculator).
Sidebar Panel: Includes input fields for customer name, service type, add-on services, service time, and service date. It also has action buttons for adding/removing GST, adding a service record, and generating a chart.
Main Panel: Displays the calculated total cost, a table of service records, a time series plot of service costs, and a download button for exporting the service records as a CSV file.
Server Component
The server component handles the app’s logic and computations, including:
Calculating the total cost: Determines the total cost based on service type, add-on services, and service time.
Applying/removing GST: Applies or removes a 15% GST on the total cost based on user input.
Managing service records: Stores service records in a reactive data frame, updates the records when a new entry is added, and arranges the records in descending order by date.
Generating a time series plot: Creates a time series plot of service costs using ggplot2 when the “生成图表” (Generate Chart) button is clicked.
Downloading service records: Allows users to download service records as a CSV file, with a filename containing the current date.
In summary, this Shiny app is a user-friendly service fee calculator that helps users input various parameters, calculate the total cost, apply or remove GST, store and display service records, and generate a time series plot of service costs.