shiny: 跟國外大神教程學(xué)網(wǎng)頁制作

搞出來一個加拿大人民喝過的酒的交互可視化統(tǒng)計


NOTE ON Building Shiny apps - an interactive tutorial

YOU DO NOT NEED TO KNOW ANY HTML/CSS/JavaScript

https://deanattali.com/blog/building-shiny-apps-tutorial/

1. Shiny app basics

Every Shiny app is composed of 2 parts:

  • a web page
  • a computer

In Shiny terminology,

  • UI (user interface)
  • server

UI

  • a web document that the user gets to see

  • responsible for

    • creating the layout of the app
    • telling Shiny exactly where things go

server

  • responsible for the logic of the app
  • the set of instructions that tell the web page what to show

2. An empty Shiny app

library(shiny)
ui <- fluidPage()
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

All Shiny apps follow the same template ??

A few things you should keep in mind:

  • It is very important that the name of the file is app.R, otherwise it would not be recognized as a Shiny app.
  • You should not have any R code after the shinyApp(ui = ui, server = server) line. That line needs to be the last line in your file.
  • It is good practice to place this app in its own folder, and not in a folder that already has other R scripts or files, unless those other files are used by your app.
runApp()
Listening on http://127.0.0.1:5360

Click the stop button to stop the app, or press the Escape key.

2.1 Alternate way: separate UI and server files

When the app is complex and involves more code,

separate the UI and server code into two files:

  • ui.R

  • server.R

When RStudio sees these two files in the same folder, it will know you’re writing a Shiny app.

Do not need to include the shinyApp(ui = ui, server = server) line.

2.2 Let RStudio fill out a Shiny app template

#
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
#    http://shiny.rstudio.com/
#

library(shiny)

# Define UI for application that draws a histogram
ui <- fluidPage(

    # Application title
    titlePanel("Old Faithful Geyser Data"),

    # Sidebar with a slider input for number of bins 
    sidebarLayout(
        sidebarPanel(
            sliderInput("bins",
                        "Number of bins:",
                        min = 1,
                        max = 50,
                        value = 30)
        ),

        # Show a plot of the generated distribution
        mainPanel(
           plotOutput("distPlot")
        )
    )
)

# Define server logic required to draw a histogram
server <- function(input, output) {

    output$distPlot <- renderPlot({
        # generate bins based on input$bins from ui.R
        x    <- faithful[, 2]
        bins <- seq(min(x), max(x), length.out = input$bins + 1)

        # draw the histogram with the specified number of bins
        hist(x, breaks = bins, col = 'darkgray', border = 'white')
    })
}

# Run the application 
shinyApp(ui = ui, server = server)

3. Load the dataset

就直接用作者的測試數(shù)據(jù)吧 raw data, 大概就是想從國企報表里康一康大家喝了多少酒什么酒花了多少錢這個亞子。

bcl <- read.csv("bcl-data.csv", stringsAsFactors = FALSE)
## you will see a summary of the dataset which should let you know that the dataset was indeed loaded correctly
print(str(bcl))
# 'data.frame': 6132 obs. of  7 variables:
#  $ Type           : chr  "WINE" "WINE" "WINE" "WINE" ...
#  $ Subtype        : chr  "TABLE WINE RED" "TABLE WINE WHITE" "TABLE WINE RED" "TABLE WINE WHITE" ...
#  $ Country        : chr  "CANADA" "CANADA" "CANADA" "CANADA" ...
#  $ Name           : chr  "COPPER MOON - MALBEC" "DOMAINE D'OR - DRY" "SOMMET ROUGE" "MISSION RIDGE - PREMIUM DRY WHITE" ...
#  $ Alcohol_Content: num  14 11.5 12 11 13.5 11 12.5 12 11.5 40 ...
#  $ Price          : num  31 33 30 34 37 ...
#  $ Sweetness      : int  0 0 0 1 0 0 0 0 0 NA ...
# NULL

做個練習(xí):

Exercise: Load the data file into R and get a feel for what’s in it. How big is it, what variables are there, what are the normal price ranges, etc.

dim(bcl)
# [1] 6132    7

## Alcohol Content
alc_mean <- mean(bcl$Alcohol_Content)
alc_mean
# [1] 17.16615
alc_max <- max(bcl$Alcohol_Content)
alc_max
# [1] 75.5
alc_min <- min(bcl$Alcohol_Content)
alc_min
# [1] 2.5

寫個函數(shù)求眾數(shù):

getthemost <- function(x) {
  tail(sort(table(x)), n=1)
}
alc_mode <- getthemost(bcl$Alcohol_Content)
alc_mode
#  13 
# 799
## Price
price_mean <- mean(bcl$Price, na.rm = TRUE)
price_mean
# [1] 141.4914
price_max <- max(bcl$Price, na.rm = TRUE)
price_max
# [1] 30250
price_min <- min(bcl$Price, na.rm = TRUE)
price_min
# [1] 1.99
price_mode <- getthemost(bcl$Price)
price_mode
# 14.99 
#   217 
getthetops <- function(x) {
  tail(sort(table(x)), n=5)
}
## Country
topcountries <- getthetops(bcl$Country)
topcountries
# x
#                AUSTRALIA                    ITALY 
#                      364                      570 
# UNITED STATES OF AMERICA                   FRANCE 
#                      703                     1357 
#                   CANADA 
#                     1374 
## Type 后面發(fā)現(xiàn)其實一共也就這四種
toptypes <- getthetops(bcl$Type)
toptypes
# x
# REFRESHMENT        BEER     SPIRITS        WINE 
#         111         683        1147        4191 
## Subtype
topsubts <- getthetops(bcl$Subtype)
topsubts
# x
# SPARKLING WINE WHITE        SCOTCH - MALT                 # BEER 
#                  181                  208                  689 
#     TABLE WINE WHITE       TABLE WINE RED 
#                 1119                 2564 

看了半天第煮,回到 shiny

4. Build the basic UI

4.1 Add plain text to the UI

The first thing you do when writing a Shiny app - add elements to the UI.

Place R strings inside fluidPage() to render text:

ui <- fluidPage("BC Liquor Store", "prices")
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

一步兩步、

ui <- fluidPage("BC Liquor Store", "prices", "Alcohol Content")
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

4.2 Add formatted text and other HTML elements

  • h1() : a top-level header
  • h2() : a secondary header
  • strong() : make text bold
  • em() : make text italicized
  • br() : a line break
  • img() : an image
  • a() : a hyperlink
ui <- fluidPage(h1("My app"),
                "BC",
                "Liquor",
                br(),
                "Store",
                strong("prices"))
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

4.3 Add a title

Shiny also has a special function titlePanel() .

Using titlePanel() not only adds a visible big title-like text to the top of the page, but it also sets the “official” title of the web page.

ui <- fluidPage(titlePanel("BC Liquor Store prices", 
                           windowTitle = "BC Liquor Store prices"))
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

4.4 Add a layout

Use sidebarLayout() to add a simple structure.

ui <- fluidPage(titlePanel("BC Liquor Store prices", 
                           windowTitle = "BC Liquor Store prices"), 
                sidebarLayout(
                  sidebarPanel("our inputs will go here"),
                  mainPanel("the results will go here")
                ))
server <- function(input, output) {}
shinyApp(ui = ui, server = server)
## 隨便試一下另外兩個函數(shù)
ui <- fluidPage(titlePanel("BC Liquor Store prices", 
                           windowTitle = "BC Liquor Store prices"), 
                sidebarLayout(
                  sidebarPanel("our inputs will go here"),
                  mainPanel("the results will go here")),
                column(width = 5, "5_1", offset = 3),
                column(width = 4, "4_1", offset = 3),
                fluidRow(
                  column(width = 5, "5_2"),
                  column(width = 4, "4 offset 2", offset = 2)
                ))
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

5. Add inputs to the UI

Inputs are what gives users a way to interact with a Shiny app.

  • textInput() : enter text
  • numericInput() : select a number
  • dateInput() : selecta date
  • selectInput() : create a select box (aka a dropdown menu)

All input functions have the same first two arguments: inputId and label.

  • The inputId will be the name. Every input must have a unique inputId.
  • The label argument specifies the text in the display label that goes along with the input widget.
## 隨便瞎試一下
ui <- fluidPage(titlePanel("BC Liquor Store prices", 
                           windowTitle = "BC Liquor Store prices"),
                sidebarLayout(
                  sidebarPanel(
                    numericInput("Price",
                                 "in USD",
                                 "1.99",
                                 min = 0,
                                 max = NA)),
                  mainPanel("the results will go here")))

5.1 Input for price

The first input we want to have is for specifying a price range (minimum and maximum price). The most sensible types of input for this are either numericInput() or sliderInput().

  • numericInput(), two inputs, one for the minimum value and one for the maximum.
  • sliderInput(), by supplying a vector of length two as the value argument, it can be used to specify a range rather than a single number.

說了這么多牵素,就是 sliderInput() 的效果更好请琳,拋棄前面的 numericInput().

前面求出最貴的酒是30250刀俄精,顯然買不起竖慧,買不起我可以假裝看不見,所以把最大值設(shè)為100——當然砍的,從四分位數(shù)也可以看出??

fivenum(bcl$Price)
# [1]     1.99    14.99    24.99    62.99 30250.00

最大值設(shè)為100是很合理的。

ui <- fluidPage(titlePanel("BC Liquor Store prices", 
                           windowTitle = "BC Liquor Store prices"),
                sidebarLayout(
                  sidebarPanel(
                    sliderInput("priceInput", "Price", 
                            min = 0, 
                            max = 100,
                            value = c(25, 40), 
                            pre = "$")),
                  mainPanel("the result will go here")))

5.2 Input for product type

We could either use radio buttons or a select box for our purpose. Let’s use radio buttons for now since there are only a few options.

ui <- fluidPage(titlePanel("BC Liquor Store prices", 
                           windowTitle = "BC Liquor Store prices"),
                sidebarLayout(
                  sidebarPanel(
                    sliderInput("priceInput", "Price", 
                                min = 0, 
                                max = 100,
                                value = c(25, 40), 
                                pre = "$"),
                    radioButtons("typeInput", "Product type",
                                 choices = c("BEER", "REFRESHMENT", 
                                             "SPIRITS", "WINE"),
                                 selected = "WINE")),
                  mainPanel("the result will go here")))

5.3 Input for country

紅酒就要喝 fà國 的榄审。

selectInput() : create a select box.

ui <- fluidPage(titlePanel("BC Liquor Store prices", 
                           windowTitle = "BC Liquor Store prices"),
                sidebarLayout(
                  sidebarPanel(
                    sliderInput("priceInput", "Price", 
                            min = 0, 
                            max = 100,
                            value = c(25, 40), 
                            pre = "$"),
                    radioButtons("typeInput", "Product type",
                                          choices = c("BEER", "REFRESHMENT", 
                                                      "SPIRITS", "WINE"),
                                          selected = "WINE"),
                    selectInput("countryInput", "Country",
                            choices = c("CANADA", "FRANCE", "UNITED STATES OF AMERICA",
                                        "ITALY", "AUSTRALIA"))),
                  mainPanel("the result will go here")))

這時候的網(wǎng)頁已經(jīng)有點人樣了(霧,輸入部分已完成篮撑,把所有模塊整理好應(yīng)該是這樣的:

bcl <- read.csv("bcl-data.csv", stringsAsFactors = FALSE)
library(shiny)
ui <- fluidPage(titlePanel("BC Liquor Store prices", 
                           windowTitle = "BC Liquor Store prices"),
                sidebarLayout(
                  sidebarPanel(
                    sliderInput("priceInput", "Price", 
                            min = 0, 
                            max = 100,
                            value = c(25, 40), 
                            pre = "$"),
                    radioButtons("typeInput", "Product type",
                                          choices = c("BEER", "REFRESHMENT", 
                                                      "SPIRITS", "WINE"),
                                          selected = "WINE"),
                    selectInput("countryInput", "Country",
                            choices = c("CANADA", "FRANCE", "UNITED STATES OF AMERICA",
                                        "ITALY", "AUSTRALIA"))),
                  mainPanel("the result will go here")))
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

6. Add placeholders for outputs

Outputs can be any object that R creates and that we want to display in our app - such as a plot, a table, or text.

All the ouput functions have a outputId argument that is used to identify each output, and this argument must be unique for each output.

這時候可以把"the result will go here"用代碼替換了。

We’ll have a plot showing some visualization of the results.

We will have a table that shows all the results. To get a table, we use the tableOutput() function.

所以 mainPanel() 內(nèi)部是這樣的:

mainPanel(
    plotOutput("coolplot"),
    br(), br(),
    tableOutput("results"))

7. Implement server logic to create outputs

Now we have to write the server function, which will be responsible for listening to changes to the inputs and creating outputs to show in the app.

Server function is always defined with two arguments: input and output.

  • input : is a list you will read values from and will contain the values of all the different inputs at any given time.

  • output is a list you will write values to, and it is where you will save output objects (such as tables and plots) to display in your app.

There are three rules to build an output in Shiny.

  1. Save the output object into the output list (remember the app template - every server function has an output argument)
  2. Build the object with a render* function, where * is the type of output
  3. Access input values using the input list (every server function has an inputargument)

7.1 Making an output react to an input

2 rules:

  1. we’re creating a plot inside the renderPlot()function, and assigning it to coolplot in the output list.
  2. Remember that every output created in the UI must have a unique ID, now we see why. In order to attach an R object to an output with ID x, we assign the R object to output$x.
server <- function(input, output) {
  output$coolplot <- renderPlot({
    plot(rnorm(input$priceInput[1]))
  })
}

The variable input contains a list of all the inputs that are defined in the UI. input$priceInput return a vector of length 2 containing the miminimum and maximum price.

We are saving to the output list (output$coolplot <-), we are using a render* function to build the output (renderPlot({})), and we are accessing an input value (input$priceInput[1]).

7.2 Building the plot output

上面的只是試手纸型,正式的——用 ggplot2 畫一個酒精含量的直方圖狰腌。

# library(ggplo2)
server <- function(input, output) {
  output$coolplot <- renderPlot({
    ggplot(bcl, aes(Alcohol_Content)) + 
      geom_histogram()
  })
}

這時候的圖是不隨旁邊的輸入數(shù)據(jù)改變的,所以 the next step is to actually filter the dataset based on the inputs.

# library(ggplot2)
# library(dplyr)

server <- function(input, output) {
  output$coolplot <- renderPlot({
    filtered <- 
      bcl %>% 
      filter(Price >= input$priceInput[1],
             Price <= input$priceInput[2],
             Type == input$typeInput,
             Country == input$countryInput)
    ggplot(filtered, aes(Alcohol_Content)) +
      geom_histogram()
  })
}

7.3 Building the table output

The other output we have was called results (as defined in the UI) and should be a table of all the products that match the filters.

We should use the renderTable()function. We’ll do the exact same filtering on the data, and then simply return the data as a data.frame.

server <- function(input, output) {}下加上:

output$results <- renderTable({
    filtered <- 
      bcl %>% 
      filter(Price >= input$priceInput[1],
             Price <= input$priceInput[2],
             Type == input$typeInput,
             Country == input$countryInput)

8. Reactivity

Shiny uses a concept called reactive programming. This is what enables your outputs to react to changes in inputs.

Only reactive variables behave this way, and in Shiny all inputs are automatically reactive.

8.1 Creating and accessing reactive variables

One very important thing to remember about reactive variables (such as the input list) is that they can only be used inside reactive contexts.

Any render* function is a reactive context, so you can always use input$x or any other reactive variable inside render functions.

2 other common reactive contexts:

  • reactive({})
  • observe({})

observe({}) statement depends on input$priceInput.

Remember to wrap the cat(input$x) or print(input$x) by an observe({}).

server <- function(input, output) {}下加上:

observe({ print(input$priceInput) })

You can also create your own reactive variables using the reactive({}) function.

The difference between reactive({}) and observe({}) is that reactive({}) returns a value.

Create a variable called priceDiff that will be the difference between the maximum and minimum price selected.

server <- function(input, output) {} 下加上:

priceDiff <- reactive({
  diff(input$priceInput)
})
observe({print(priceDiff())})

If you want to access a reactive variable defined with reactive({}), you must add parentheses after the variable name, as if it’s a function.

8.2 Using reactive variables to reduce code duplication

again, 上面的都是試手。

we have the exact same code filtering the dataset in two places, once in each render function. We can solve that problem by defining a reactive variable that will hold the filtered dataset, and use that variable in the render functions.

server <- function(input, output) {} 下加上:

filtered <- reactive({
  bcl %>%
    filter(Price >= input$priceInput[1],
           Price <= input$priceInput[2],
           Type == input$typeInput,
           Country == input$countryInput
    )
})

7.27.3 的代碼換成:

output$coolplot <- renderPlot({
    ggplot(filtered(), aes(Alcohol_Content)) +
      geom_histogram()
})

output$results <- renderTable({
    filtered()
})

9. Using uiOutput() to create UI elements dynamically

9.1 Basic example of uiOutput()

One of the output functions you can add in the UI is uiOutput(), this is an output used to render more UI. It’s usually used to create inputs (or any other UI) from the server.

ui <- fluidPage(
  numericInput("num", "Maximum slider value", 5),
  uiOutput("slider")
)

server <- function(input, output) {
  output$slider <- renderUI({
    sliderInput("slider" , "Slider", min = 0,
                max = input$num , value = 0)
  })
}

shinyApp(ui = ui, server = server)

slider 的最大值是隨著 input 變動的尸诽。

9.2 Use uiOutput() in our app to populate the countries

現(xiàn)在通過 uiOutput() renderUI({}) 這個組合把對國家的選擇放在 server <- function(input, output) {} 下甥材。

ui <- fluidPage() 下的 selectInput("countryInput", ...) 換成:

uiOutput("countryOutput")

server <- function(input, output) {} 下加上:

output$countryOutput <- renderUI({
    selectInput("countryInput", "Country",
                sort(unique(bcl$Country)),
                selected = "CANADA")

這樣就可以選擇所有國家了。

9.3 Errors showing up and quickly disappearing

  1. The problem is that when the app initializes, filtered is trying to access the country input, but the country input hasn’t been created yet.

    Inside the filtered reactive function, we should check if the country input exists, and if not then just return NULL.

  2. The ggplot function will not work with a NULL dataset, so we also need to make a similar check in the renderPlot() function.

修復(fù)一閃而過的報錯逊谋,在 filtered <- reactive({}) 下加上:

if (is.null(input$countryInput)) {
    return(NULL)
}    

output$coolplot <- renderPlot({}) 下加上:

if (is.null(filtered())) {
      return()
}

FINALLY

大致粗糙做完了擂达,現(xiàn)在這個網(wǎng)頁長這樣:

ui.R 長這樣:

## bc_liquor_store_ui

bcl <- read.csv("bcl-data.csv", stringsAsFactors = FALSE)
library(shiny)

ui <- fluidPage(titlePanel("BC Liquor Store prices", 
                           windowTitle = "BC Liquor Store prices"),
                sidebarLayout(
                  sidebarPanel(
                    sliderInput("priceInput", "Price", 
                                min = 0, 
                                max = 100,
                                value = c(25, 40), 
                                pre = "$"),
                    radioButtons("typeInput", "Product type",
                                 choices = c("BEER", "REFRESHMENT", 
                                             "SPIRITS", "WINE"),
                                 selected = "WINE"),
                    uiOutput("countryOutput")),
                  mainPanel(plotOutput("coolplot"),
                            br(), br(),
                            tableOutput("results")
                  )))

server.R 長這樣:

## bc_liquor_store_server

library(ggplot2)
library(dplyr)

server <- function(input, output) {
  filtered <- reactive({
    if (is.null(input$countryInput)) {
      return(NULL)
    }    
    bcl %>% 
      filter(Price >= input$priceInput[1],
             Price <= input$priceInput[2],
             Type == input$typeInput,
             Country == input$countryInput)
  })
  
  output$coolplot <- renderPlot({
    if (is.null(filtered())) {
      return()
    }
    ggplot(filtered(), aes(Alcohol_Content)) +
      geom_histogram()
  })
  
  output$results <- renderTable({
    filtered()
  })
  
  output$countryOutput <- renderUI({
    selectInput("countryInput", "Country",
                sort(unique(bcl$Country)),
                selected = "CANADA")
  })
}

最后究恤,向大家隆重推薦生信技能樹的一系列干貨理张!

  1. 生信技能樹全球公益巡講:https://mp.weixin.qq.com/s/E9ykuIbc-2Ja9HOY0bn_6g
  2. B站公益74小時生信工程師教學(xué)視頻合輯:https://mp.weixin.qq.com/s/IyFK7l_WBAiUgqQi8O7Hxw
  3. 招學(xué)徒:https://mp.weixin.qq.com/s/KgbilzXnFjbKKunuw7NVfw
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末暂幼,一起剝皮案震驚了整個濱河市邪媳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌顿肺,老刑警劉巖讼昆,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碧查,死亡現(xiàn)場離奇詭異稻扬,居然都是意外死亡困后,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門蜕猫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事∧窈眨” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我赴叹,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任石窑,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己暇矫,他們只是感情好几睛,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著顺献,像睡著了一般纽窟。 火紅的嫁衣襯著肌膚如雪兼吓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天森枪,我揣著相機與錄音视搏,去河邊找鬼审孽。 笑死,一個胖子當著我的面吹牛浑娜,可吹牛的內(nèi)容都是我干的佑力。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼筋遭,長吁一口氣:“原來是場噩夢啊……” “哼打颤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起漓滔,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤编饺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后响驴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體透且,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年豁鲤,在試婚紗的時候發(fā)現(xiàn)自己被綠了秽誊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡琳骡,死狀恐怖锅论,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情楣号,我是刑警寧澤最易,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站竖席,受9級特大地震影響耘纱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜毕荐,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一束析、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧憎亚,春花似錦员寇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至什往,卻和暖如春扳缕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工躯舔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留驴剔,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓粥庄,卻偏偏與公主長得像丧失,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子惜互,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353