Button组件和Checkbox组件(五)

博客写的比较细致, 基本是一个小功能把展示 实现 样式都粘贴到博客中了, 一步步完成的, 想看实现可以直接看代码地址: Button & Checkbox

这次的目标是希望实现一个下面这样的 Table 组件, 看起来很美观, 功能基本满足日常开发, 后面我们会弄一个用例图, 一个个功能实现

  • 基本的表格渲染
  • 可以自由渲染数据
  • 可以多选数据
  • 可以排序
  • 可以展开行
  • etc...
表格

在实现上面的功能之前, 我们先搭建开发环境, 完善基础组件 ButtonCheckbox 组件,

  1. 技术栈: React + Hook + TS + Scss
  2. 环境: Vite脚手架

搭建开发环境

Vite 是一种新型前端构建工具, 构建我们开发所需要的语言环境

如何使用 Vite 搭建 React + Ts 模板的脚手架

# vite 后面跟项目名称
# template 后面跟 需要的模板
yarn create vite 项目名称 --template react-ts
yarn create vite demo --template react-ts

# 进入项目 .scss .less .styl
yarn add sass/less/stylus # 内置了 css 预处理器

搭建我们的目录结构

.
├── App.scss
├── App.tsx // 展示组件
├── index.scss
├── lib // 组件源代码
│   └── Button
├── main.tsx // 入口
└── vite-env.d.ts

初始化项目 & Button 组件

  • button 代码组织
// lib/Button/button.tsx
import { FC } from "react";

interface ButtonProps {}

const Button: FC<ButtonProps> = (props) => {
  return <div>Button</div>;
};

export default Button;
  • 显示组件
// App.tsx 显示组件样式
import { Button } from "./lib/index";

const App = () => {
  return (
    <div className="App">
      <Button />
    </div>
  );
};

export default App;

Button用例图 & 使用Button

项目已经初始化, 我们思考一下用户如何使用我们的组件, 既简单又上手, 以及我们如何设计 props, 可以让用户方便

  1. 有一个基础样式, 比默认的好看 => hover/focus/active 效果
  2. 可以有不同的类型展示
  3. 是否可点击

实现用例1: 默认按钮变成一个好看的按钮

// lib/Button/button.tsx
import { FC } from "react";
import "./button.scss";

interface ButtonProps {}

const Button: FC<ButtonProps> = (props) => {
  return <button className="g-button g-button-default">按钮</button>;
};

export default Button;
.g-button {
  padding: 8px 12px;
  font-size: 14px;
  border-radius: 6px;
  border: none;
  cursor: pointer;
  

  &:focus {
    outline: none;
  }

  // 默认样式
  &.g-button-default {
    color: #575757;
    background: #f7f7fa;
    &:hover {
      background: #e5e5ea;
    }
    &:active {
      background: #d9d9d9;
    }
  }
}

实现用例2: 按钮展示不同的类型

展示不同的类型, 其实就是添加不同的 class, 给元素不同的展现

import { Button } from "./lib/index";

const App = () => {
  return (
    <div className="App">
      <Button /> 
      <Button type="primary" /> 
      <Button type="danger" /> 
    </div>
  );
};

export default App;
// 添加 type 属性
import { ButtonHTMLAttributes, FC } from "react";
import classnames from "classnames";
import "./button.scss";

interface ButtonProps extends Omit<ButtonHTMLAttributes<HTMLElement>, "type"> {
  type?: "primary" | "danger" | "default";
}

const Button: FC<ButtonProps> = (props) => {
  const { type = "default", ...restProps } = props;
  const classes = {
    [`g-button-${type}`]: type,
  };
  return (
    <button className={classnames("g-button", classes)} {...restProps}>
      按钮
    </button>
  );
};

export default Button;
.g-button {
  padding: 8px 12px;
  font-size: 14px;
  border-radius: 6px;
  border: none;
  cursor: pointer;
  

  &:focus {
    outline: none;
  }

  // 默认样式
  &.g-button-default {
    color: #575757;
    background: #f7f7fa;
    &:hover {
      background: #e5e5ea;
    }
    &:active {
      background: #d9d9d9;
    }
  }

  // 主要颜色
  &.g-button-primary {
    color: #fff;
    background: #3498ff;
    &:hover {
      background: #2589f5;
    }
    &:active {
      background: #1675e0;
    }
  }

  // 危险颜色
  &.g-button-danger {
    color: #fff;
    background: #ff7875;
    &:hover {
      background: #e4383a;
    }
    &:active {
      background: #d42926;
    }
  }
}

实现用例3: 按钮是否可点击

同理按钮是否可点击, 可以设置不同的样式, 并阻止点击事件触发

// props 的使用
import { Button } from "./lib/index";
import "./App.scss";

const App = () => {
  return (
    <div className="App">
      <Button>普通按钮</Button>
      <Button type="primary">主要按钮</Button>
      <Button type="danger">危险按钮</Button>
      <br />
      <Button disabled>不可点击按钮</Button>
      <Button type="primary" disabled>
        不可点击主要按钮
      </Button>
      <Button type="danger" disabled>
        不可点击危险按钮
      </Button>
    </div>
  );
};

export default App;

// 实现 props disabled
import { ButtonHTMLAttributes, FC } from "react";
import classnames from "classnames";
import "./button.scss";

interface ButtonProps extends Omit<ButtonHTMLAttributes<HTMLElement>, "type"> {
  type?: "primary" | "danger" | "default";
}

const Button: FC<ButtonProps> = (props) => {
  const { type = "default", disabled = false, children, ...restProps } = props;
  // 加了一个 class 的判断
  const cn = {
    [`g-button-${type}`]: type,
    [`g-button-disabled`]: disabled,
  };

  return (
    <button className={classnames("g-button", cn)} {...restProps}>
      {children}
    </button>
  );
};

export default Button;
.g-button {
  padding: 8px 12px;
  font-size: 14px;
  border-radius: 6px;
  border: none;
  user-select: none;
  cursor: pointer;
  margin-right: 8px;
  margin-top: 20px;

  &:focus {
    outline: none;
  }

  // 默认样式
  &.g-button-default {
    color: #575757;
    background: #f7f7fa;
    &:hover {
      background: #e5e5ea;
    }
    &:active {
      background: #d9d9d9;
    }

    &.g-button-disabled {
      color: #c5c6c7;
      pointer-events: none;
    }
  }

  // 主要颜色
  &.g-button-primary {
    color: #fff;
    background: #3498ff;
    &:hover {
      background: #2589f5;
    }
    &:active {
      background: #1675e0;
    }
    &.g-button-disabled {
      background: #cce9ff;
      pointer-events: none;
    }
  }

  // 危险颜色
  &.g-button-danger {
    color: #fff;
    background: #ff7875;
    &:hover {
      background: #e4383a;
    }
    &:active {
      background: #d42926;
    }
    &.g-button-disabled {
      background: #eeb4b3;
      pointer-events: none;
    }
  }
}

上面代码完成, 显示效果

button.png
show1.png

实现一个 Checkbox 组件

首先在项目里面添加 Checkbox 文件夹

.
├── README.md
├── index.html
├── package.json
├── src
│   ├── App.scss
│   ├── App.tsx
│   ├── index.scss
│   ├── lib
│   │   ├── Button
│   │   ├── Checkbox
│   │   └── index.ts
│   ├── main.tsx
│   └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── yarn.lock

用例图 & props 使用

  1. 一个基本好看的样式, 用户可以切换勾选
  2. 不可点击状态
  3. 触发事件通知外面

实现用例1: 好看的勾选框 & 可以切换状态

我们默认的勾选框比较小, 我们可以设置默认的不可见, 写一个勾选框来覆盖默认样式, 来模拟 checkbox 的行为, 为了点击文字也能触发 checkbox, 使用label 标签包裹元素, 并触发 change 事件, 改变 checkbox 的状态

import { FC, useState } from "react";
import classNames from "classnames";
import "./checkbox.scss";

interface CheckboxProps {
  checked?: boolean;
}

const Checkbox: FC<CheckboxProps> = (props) => {
  const { checked = false } = props;
  // 当前是否被选中
  const [currentChecked, setCurrentChecked] = useState(checked);

  const classes = {
    "g-checkbox-checked": currentChecked,
  };

  const handleChange = () => {
    setCurrentChecked(!currentChecked);
  };

  return (
    <label className="g-checkbox-wrapper">
      <span className="g-checkbox">
        <span className={classNames("g-checkbox-inner", classes)}></span>
        <input
          className="g-checkbox-input"
          type="checkbox"
          checked={currentChecked}
          onChange={handleChange}
        />
      </span>
      <span className="g-checkobox-label">选择框</span>
    </label>
  );
};

export default Checkbox;
.g-checkbox-wrapper {
  display: inline-flex;
  align-items: center;
  user-select: none;
  cursor: pointer;
  .g-checkbox {
    padding: 10px;
    &-inner {
      position: relative;
      display: block;
      width: 16px;
      height: 16px;
      border: 1px solid #d9d9d9;
      border-radius: 3px;
      background: #fff;
      transition: all 0.3s;

      &.g-checkbox-checked {
        background: #3498ff;
        border: 1px solid #3498ff;
        &::after {
          content: "";
          position: absolute;
          top: 1px;
          left: 5px;
          width: 5px;
          height: 10px;
          transform: rotate(45deg);
          border: 2px solid #fff;
          border-top: none;
          border-left: none;
        }
      }
    }

    &-input {
      position: absolute;
      opacity: 0;
      box-sizing: border-box;
    }
  }
}

实现用例2: 不可点击状态

不可点击状态和 Button 类似, 加一个不可点击状态的样式, 不能触发 checkbox 的 change 事件

  • 给父元素添加 disabled className 设置样式
  • change 事件 disabled 不可触发
  • 添加 checkbox value props, 可选属性
// 示例
import { Button, Checkbox } from "./lib/index";
import "./App.scss";

const App = () => {
  return (
    <div className="App">
      {/* 使用选择框 */}
      <Checkbox value="选择框" />
      <Checkbox disabled checked value="苹果" />
    </div>
  );
};

export default App;
// 如何实现

import { FC, useState } from "react";
import classNames from "classnames";
import "./checkbox.scss";

interface CheckboxProps {
  checked?: boolean;
  disabled?: boolean;
  value?: string;
}

const Checkbox: FC<CheckboxProps> = (props) => {
  const { checked = false, disabled = false, value = "" } = props;
  // 当前是否被选中
  const [currentChecked, setCurrentChecked] = useState(checked);

  const classes_inner = {
    "g-checkbox-checked": currentChecked,
  };

  const handleChange = () => {
    // 如果 disabeld 不能触发 change 事件
    if (disabled) return;
    setCurrentChecked(!currentChecked);
  };

  return (
    {/* 添加 disabled class */}
    <label
      className={classNames("g-checkbox-wrapper", {
        "g-checkbox-disabled": disabled,
      })}
    >
      <span className="g-checkbox">
        <span className={classNames("g-checkbox-inner", classes_inner)}></span>
        <input
          className="g-checkbox-input"
          type="checkbox"
          checked={currentChecked}
          onChange={handleChange}
          value={value}
        />
      </span>
      <span className="g-checkbox-label">{value}</span>
    </label>
  );
};

export default Checkbox;
.g-checkbox-wrapper {
  display: inline-flex;
  align-items: center;
  user-select: none;
  cursor: pointer;
  .g-checkbox {
    padding: 10px;
    &-inner {
      position: relative;
      display: block;
      width: 16px;
      height: 16px;
      border: 1px solid #d9d9d9;
      border-radius: 3px;
      background: #fff;
      transition: all 0.3s;

      &.g-checkbox-checked {
        background: #3498ff;
        border: 1px solid #3498ff;
        &::after {
          content: "";
          position: absolute;
          top: 1px;
          left: 5px;
          width: 5px;
          height: 10px;
          transform: rotate(45deg);
          border: 2px solid #fff;
          border-top: none;
          border-left: none;
        }
      }
    }

    &-input {
      position: absolute;
      opacity: 0;
      box-sizing: border-box;
    }
  }

  // disabled 样式
  &.g-checkbox-disabled {
    cursor: not-allowed;
    .g-checkbox-inner {
      background: #f7f7fa;
      border: none;
      &.g-checkbox-checked {
        background: #cce9ff;
      }
    }
    .g-checkbox-label {
      color: #c5c6c7;
    }
  }
}

实现用例3: change事件回调

  • 我们需要知道 checkbox 的状态, 传入 change事件
// 使用

import { Button, Checkbox } from "./lib/index";
import "./App.scss";
import { ChangeEvent } from "react";

const App = () => {
  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    // <input class="g-checkbox-input" type="checkbox" value="选择框">
    // 选择框
    // false/true
    console.log(e.target, e.target.value, e.target.checked);
  };

  return (
    <div className="App">
      <Checkbox value="选择框" onChange={handleChange} />
      <Checkbox disabled checked value="苹果" />
    </div>
  );
};

export default App;

// 实现
import { ChangeEvent, FC, useState } from "react";
import classNames from "classnames";
import "./checkbox.scss";

interface CheckboxProps {
  checked?: boolean;
  disabled?: boolean;
  value?: string;
  onChange?: (e: ChangeEvent<HTMLInputElement>) => void; // 传参
}

const Checkbox: FC<CheckboxProps> = (props) => {
  const { checked = false, disabled = false, value = "", onChange } = props;
  // 当前是否被选中
  const [currentChecked, setCurrentChecked] = useState(checked);

  const classes_inner = {
    "g-checkbox-checked": currentChecked,
  };

  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    // 如果 disabeld 不能触发 change 事件
    if (disabled) return;
    setCurrentChecked(!currentChecked);
    // 回调
    onChange && onChange(e);
  };

  return (
    <label
      className={classNames("g-checkbox-wrapper", {
        "g-checkbox-disabled": disabled,
      })}
    >
      <span className="g-checkbox">
        <span className={classNames("g-checkbox-inner", classes_inner)}></span>
        <input
          className="g-checkbox-input"
          type="checkbox"
          checked={currentChecked}
          onChange={handleChange}
          value={value}
        />
      </span>
      <span className="g-checkbox-label">{value}</span>
    </label>
  );
};

export default Checkbox;

  • 有时候不知道这个元素的类型, 可以把鼠标放在元素上, 看一下什么类型
类型

实现 CheckboxGroup 组件

平常我们使用多选框大部分是把多个选择框放在一起, 成为一组, 选择多个, CheckboxGroup 组件就是对 Checkbox 组件包装一下, 当触发 chnage 事件时, 知道我们选择了哪些选择框, 而不用给每一个 Checkbox 添加一个 change事件

用例图 & 使用方式

  • 用户默认选中了哪几个
  • 用户改变选中, 返回选中值

下面我们看一下如何让用户使用我们的组件, 第一种方案是 让用户写入每一个 Checkbox 组件, 第二种方案是让用户通过数据的方式我们自己来渲染这些Checkbox, 这次我先尝试使用 第一种方式来实现 CheckboxGroup

// 1. 元素方式
<CheckboxGroup selected={["11", "33"]} onChange={handleChange}>
  <Checkbox value="11">苹果</Checkbox>
  <Checkbox value="22">香蕉</Checkbox>
  <Checkbox value="33" disabled>火龙果</Checkbox>
</CheckboxGroup>


// 2. 数据方式
const options = [
  {
    label: "电影",
    value: "1",
    disabled: true
  },
  {
    label: "电视剧",
    value: "2",
    disabled: false
  },
  {
    label: "做梦",
    value: "3",
    disabled: false
  }
];

<CheckboxGroup options={options} selected={["1", "3"]} onChange={handleChange} />
  • 第一种方式我们需要使用到 React.Children 这个 API, 我们来渲染我们的 Children, React.Children 提供了用于处理 this.props.children 不透明数据结构的实用方法
// 如何使用最基本的
import { Button, Checkbox, CheckboxGroup } from "./lib/index";
import "./App.scss";
import { ChangeEvent } from "react";

const App = () => {
  return (
    <div className="App">
      <CheckboxGroup>
        <Checkbox value="苹果" />
        <Checkbox value="香蕉" />
        <Checkbox value="梨子" disabled />
      </CheckboxGroup>
    </div>
  );
};

export default App;

// checkboxGroup.tsx
import React, { FC, ReactElement } from "react";
import Checkbox from "./checkbox";

interface GroupProps {
  children: Array<ReactElement>;
}

const CheckboxGroup: FC<GroupProps> = (props) => {
  const { children } = props;

  const childWithProps = React.Children.map(children, (child, index) => {
    // 确保每一个子元素都是 checkbox
    if (child.type !== Checkbox) {
      throw new Error("复选框组的子元素必须是 Checkbox");
    }

    // 返回每一个子元素并带有props
    return React.cloneElement(child, {
      ...child.props,
      key: index,
    });
  });

  return <div>{childWithProps}</div>;
};

export default CheckboxGroup;
  • 可以看到下面的我们可以正确渲染了 多选框组, 但是我们不知道我们选中了哪一个, 如果有默认选中的我们也不知道怎么设置, 下面添加两个属性
    1. selected props 用户初始是否有默认选中的值
    2. onChange 当用户触发子元素事件, 通知父元素选中一组中的哪几个
group.png

实现1: selected 数组

在父元素 CheckboxGroup 上添加 <CheckboxGroup selected={["11", "33"]}> 时, 把selected 传递给 Checkbox, 初始时判断value 是否在 selected 中, 如果在 checked 为 true, 不在 checked 为 false

// 使用 selected
import { Button, Checkbox, CheckboxGroup } from "./lib/index";
import "./App.scss";
import { ChangeEvent } from "react";

const App = () => {
  return (
    <div className="App" selected=["香蕉"]>
      <CheckboxGroup>
        <Checkbox value="苹果" />
        <Checkbox value="香蕉" />
        <Checkbox value="梨子" disabled />
      </CheckboxGroup>
    </div>
  );
};

export default App;
// checkboxGroup.tsx
import React, { FC, ReactElement } from "react";
import Checkbox from "./checkbox";

interface GroupProps {
  selected?: string[]; // group 使用, value 值的集合
  children: Array<ReactElement>;
}

const CheckboxGroup: FC<GroupProps> = (props) => {
  const { children, selected = [] } = props;

  const childWithProps = React.Children.map(children, (child, index) => {
    // 确保每一个子元素都是 checkbox
    if (child.type !== Checkbox) {
      throw new Error("复选框组的子元素必须是 Checkbox");
    }

    return React.cloneElement(child, {
      ...child.props,
      key: index,
      selected, // 添加的 selected props 传递给子元素
    });
  });

  return <div>{childWithProps}</div>;
};

export default CheckboxGroup;
// checkbox.tsx

import { ChangeEvent, FC, useEffect, useState } from "react";
import classNames from "classnames";
import "./checkbox.scss";

interface CheckboxProps {
  checked?: boolean;
  disabled?: boolean;
  value?: string;
  onChange?: (e: ChangeEvent<HTMLInputElement>) => void;

  // group 传的 props
  selected?: string[];
}

const Checkbox: FC<CheckboxProps> = (props) => {
  const {
    selected = [],
    checked = false,
    disabled = false,
    value = "",
    onChange,
  } = props;
  // 当前是否被选中
  const [currentChecked, setCurrentChecked] = useState(checked);

  // + 初始判断是否被选中
  useEffect(() => {
    if (selected.length > 0 && selected.indexOf(value) > -1) {
      setCurrentChecked(true);
    }
  }, []);

  const classes_inner = {
    "g-checkbox-checked": currentChecked,
  };

  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    // 如果 disabeld 不能触发 change 事件
    if (disabled) return;
    setCurrentChecked(!currentChecked);
    onChange && onChange(e);
  };

  return (
    <label
      className={classNames("g-checkbox-wrapper", {
        "g-checkbox-disabled": disabled,
      })}
    >
      <span className="g-checkbox">
        <span className={classNames("g-checkbox-inner", classes_inner)}></span>
        <input
          className="g-checkbox-input"
          type="checkbox"
          checked={currentChecked}
          onChange={handleChange}
          value={value}
        />
      </span>
      <span className="g-checkbox-label">{value}</span>
    </label>
  );
};

export default Checkbox;

实现2: change事件

上面我们实现了 selected, 现在当我们改变选中数组时告诉父元素哪几个兄弟被选中了, 所以我们需要根据 selected 的值来为初始值, 当改变子元素的 checked, 来改变选中的值

// 使用 onChange
import { Button, Checkbox, CheckboxGroup } from "./lib/index";
import "./App.scss";
import { ChangeEvent } from "react";

const App = () => {
  const handleChange = (values: string[]) => {
    setValues(values);
  };

  return (
    <div className="App" selected=["香蕉"] onChange={handleChange}>
      <CheckboxGroup>
        <Checkbox value="苹果" />
        <Checkbox value="香蕉" />
        <Checkbox value="梨子" disabled />
      </CheckboxGroup>
    </div>
  );
};

export default App;
// CheckboxGroup.tsx

import React, {
  ChangeEvent,
  FC,
  ReactElement,
  useEffect,
  useState,
} from "react";
import Checkbox from "./checkbox";

interface GroupProps {
  selected?: string[]; // group 使用, value 值的集合
  children: Array<ReactElement>;
  onChange?: (selected: string[]) => void;
}

const CheckboxGroup: FC<GroupProps> = (props) => {
  const { children, selected = [], onChange } = props;

  const [selectedValue, setSelectedValue] = useState(selected);

  // 变化时改变 值
  const handleGroupChange = (e: ChangeEvent<HTMLInputElement>) => {
    const { checked, value } = e.currentTarget;
    if (checked) {
      setSelectedValue([...selectedValue, value]);
    } else {
      setSelectedValue((arr) => arr.filter((i) => i !== value));
    }
  };

  // 值每次变化都暴露出去
  useEffect(() => {
    onChange && onChange(selectedValue);
  }, [selectedValue]);

  const childWithProps = React.Children.map(children, (child, index) => {
    // 确保每一个子元素都是 checkbox
    if (child.type !== Checkbox) {
      throw new Error("复选框组的子元素必须是 Checkbox");
    }

    return React.cloneElement(child, {
      ...child.props,
      key: index,
      selected,
      onChange: handleGroupChange, // 利用回调
    });
  });

  return <div>{childWithProps}</div>;
};

export default CheckboxGroup;
selected.png

上面完善了 Button组件和Checkbox组件的基本使用, 可以在后面我们写Table 组件的时候直接使用, 如果需要其它功能, 自己添加一下 props

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容