ts快速了解

安装TS

npm i  -g  typescript

或者

yarn  global add  typescript 

基础类型

数字类型

let num1:number = 100;
let num2:number = 0b100;    // 2进制
let num3:number = 0o100;    // 8进制
let num4:number = 0x100;    // 16进制
let num5:number = 100.88;   // 小数也是数字类型

// 类型定义好以后,不可以错误赋值
// num5 = 'abc'

console.log(num1);      // 100
console.log(num2);      // 4
console.log(num3);      // 64
console.log(num4);      // 256
console.log(num5);      // 100.88

boolean类型

let flag:boolean = true;

flag = false;
flag = 20 >5;
flag = !!100;

console.log(flag);    // true

string类型

let one:string = 'www'
let dot:string = '.'
let two:string = 'baidu'
let three:string = `
  https//${one}${dot}${two}.com
`

console.log(three)    // https//www.baidu.com

数组

let arr:string[] = [];    // 声明字符串数组,不可以放其他类型

arr.push('123');
arr.push('abc');
// arr.push(123);   // 放数字类型会报错

console.log(arr);   // [ '123', 'abc' ]

其他特殊类型

let a: null = null // 声明了null类型,就只能赋值null,表示对象缺失
let b: undefined = undefined // 声明了undefined类型,就只能赋值undefined,用于初始化变量是未定义的值

let c: any // any表示任意类型,可以忽略类型限制,可以js一样了
let d // 声明变量不指定类型,默认为any类型
// 可以赋值任意类型的值
c = 10
c = 'abc'
c = true

let e: unknown // unknown表示未知类型,还不知道什么类型,但是安全的,具体意思看下面

e = 'www'
e = 100

let f: string
// e = f;      // 指定类型的变量不可以赋值给unknown类型

// unknown类型的可以赋值给unknown类型 和 any类型
let g: unknown
g = e
let h: any
h = e
e = h // any类型也可以赋值给unknown

// never 表示接口永远没有返回值
function fun(): never {
  while (true) {
    // ...接口一直执行循环,永远不会返回
    // 或者抛异常
    throw new Error('error')
  }
}

对象类型

// 推断的方式直接赋值了
let user = {
  name: '张三',
  age: 30
}
console.log(user.name);   // 张三

// 第二种方式声明对象类型限制
let userInfo: { name: string } // 先声明
userInfo = { name: '张三' } // 再赋值
// let userInfo: { name: string } = { name: '张三' } // 也可以边声明边赋值

console.log(userInfo.name) // 张三

// 当不知道对象后面有多少属性,也不知道都有哪些类型,这样写
let obj: { name: string; age: number; [propName: string]: any } // 添加了数组表示多个属性,不指定类型
obj = { name: 'abc', age: 12, gender: 'male', email: 'abc@163.com' }

运算符号

// | 符合等于拼接
let info: string | number // info可以是string和number两种数据类型
info = 'abc'
info = 123
// info = true        // 报错,因为info只能是string和number两种数据类型

let arr: (string | number | boolean)[] = ['a', 1, true]
arr.push('b')
arr.push(2)

// & 符号表示与关系,如下表示对象使用时必须要有name和age两个属性都赋值才行
let user: { name: string } & { age: number }

关键字

tuple 元组

// 声明数组
let array1: string[] = ['a', 'b', 'c']
// 声明只允许两种类型值的数组
let array2: (string | number)[] = ['a', 'b', 'c', 1, 2, 3]
// 指定数组长度的数组
let array3: [string, string, string, number] = ['a', 'b', 'c', 1]

enum 枚举

enum sex {
  Famale = '男',
  male = '女'
}
console.log(sex.Famale) // 男

type 别名

type mystate = 1 | 2 | 3 | 4 | 5
let state: mystate
state = 1
// state = 7    // 只能使用12345这5个数,所以赋值7会报错

// 示例
// 声明了别名
type myFun = (a: number, b: number) => number
// 使用别名,只能是这个别名的类型,
let testTypeFun: myFun = (a: number, b: number) => a * b
console.log(testTypeFun(2, 3))    // 6

类型断言

as

function demo01(n: number | string) {
  let l: number
  // n参数是字符串,就把长度赋值给l
  l = (n as string).length
  console.log(l) // undefined
}

demo01(1)

// 解构断言
function funTest() {
  let str: string = 'www.baidu.com '
  let fun = (a: number, b: number) => a + b
  return [str, fun]		// 返回2个值
}
// 断言这个函数返回类型
let [a, b] = funTest() as [string, Function]

console.log(b(1, 2)) // 3

as const

let arr01 = ['www', 123] as const
// arr01.push(456);   // 指定了为常量,不可以修改值
console.log(arr01)

let user = {
  name: '张三',
  age: 30
} as const
// user.name = '李四'   // 不可以修改常量的值

函数类型

// 声明函数,c是可选参数
function func01(a: number, b: number, c?: number): number {
  return a + b
}
console.log(func01(1, 2))

// 箭头函数声明,后面跟上返回值类型number
let func02: (a: number, b: number) => number
// 赋值
func02 = (a, b) => a * b
console.log(func02(1, 2))

// 声明别名
type myFun = (n: number, m: number) => number
// 声明函数,两个参数,第3个参数是回调函数,返回使用的,使用的是myFun类型
function calc(num1: number, num2: number, callback: myFun) {
  return callback(num1, num2)
}
// 使用函数
console.log(calc(1, 2, (n, m) => n + m)) // 3

// 声明赋值函数,参数b选填参数
const funcTest = function (a: number, b?: number): number {
  return 0
}
funcTest(1)
funcTest(1, 2)
funcTest(1, undefined)

// 参数b默认是2
const funcTest2 = function (a: number, b: number = 2) {
  return a + b
}

function fun3(...args: any[]) {
  console.log(args) // [ 1, 2, 3 ]
}
fun3(1, 2, 3)

// 函数重载写法,不写方法体
function addTest(a: number, b: number): number
function addTest(a: string, b: string): string
// 具体实现
function addTest(a: any, b: any): any {
  return a + b
}

console.log(addTest(1, 2))      // 3
console.log(addTest('a', 'b'))  // ab

定义类

class Person {
  name: string
  age: number

  // 构造方法
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }

  // 类的属性
  sayHello() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`)
  }
}

const p = new Person('张三', 30)
p.sayHello() // 输出:Hello, my name is 张三 and I am 30 years old.

继承

class Person {
  name: string
  age: number

  // 构造方法
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }

  // 类的属性
  sayHello() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`)
  }
}
// 声明Student类继承Person,子类只能继承 1个父类,父类可以有多个子类
class Student extends Person {
  school: string

  constructor(name: string, age: number, school: string) {
    super(name, age) // 调用父类的constructor(name: string, age: number)
    this.school = school
  }

  study() {
    console.log(`${this.name} is studying at ${this.school}.`)
  }
}

const s = new Student('张三', 30, '家里蹲')
s.sayHello() // 输出:Hello, my name is 张三 and I am 30 years old.
s.study() // 输出:张三 is studying at 家里蹲.

// 声明Teacher继承Student,Student继承了Person,所以Teacher也有Person属性,并且有Student属性
class Teacher extends Student {
  price: number

  constructor(name: string, age: number, school: string, price: number) {
    super(name, age, school)
    this.price = price
  }
  // 重写父类的study方法
  study() {
    console.log(`${this.name} is teaching at ${this.school} for ${this.price} dollars.`)
  }
}

const t = new Teacher('李四', 40, '加拿大', 1000)
t.sayHello() // 输出:Hello, my name is 李四 and I am 40 years old.
t.study() // 输出:李四 is teaching at 加拿大 for 1000 dollars.

封装

修饰符

TypeScript可以使用三种访问修饰符,分别是public, private 和 protected
readonly可以修饰属性可读

class Person {
  // 不写修饰符,默认是public
  private name: string
  readonly age: number

  // 构造方法
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }

  // 类的属性
  sayHello() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`)
  }
}

const person = new Person('Alice', 25)
// console.log(person.name);   // 不能访问属性,只能在类的内部
console.log(person.age) // 输出:25 能访问属性,因为age是只读的
// person.age = 18;     // 无法修改属性,因为age是只读的

封装特性和存储器

class Person {
  name: string
  private _age: number // 私有属性定义一遍使用 _ 下划线

  // 手写get和set方法
  setAge(age: number) {
    this._age = age
  }
  getAge() {
    return this._age
  }

  // 手写存取器
  set age(age: number) {
    this._age = age
  }
  get age() {
    return this._age
  }

  // 构造方法
  constructor(name: string, age: number) {
    this.name = name
    this._age = age
  }
}

const p = new Person('张三', 18)
p.setAge(28) // 调用setAge方法赋值属性
console.log(p.getAge()) // 28;

p.age = 38 // 使用存取器
console.log(p.age)		// 38

静态 static

class Student {
  static school: string = '家里蹲' // 静态属性
  name: string

  constructor(name: string) {
    this.name = name
  }
  static study() {
    // 静态方法
    console.log('学习')
  }
  say() {
    console.log('说话')
  }
}

const s = new Student('张三')
const s2 = new Student('李四')
const s3 = new Student('王五')

s.say()
// s.study()     // 不可以通过对象调用静态方法
Student.study() // 通过类调用静态方法
console.log(Student.school) // 静态属性

抽象类

// 声明抽象类,要有抽象方法,必须是抽象类
abstract class Person {
  name: string

  // 构造方法
  constructor(name: string) {
    this.name = name
  }

  // 抽象方法
  abstract run(): void
}

// 定义子类,如果不重写抽象方法,子类必须也是抽象类
abstract class Demo extends Person {}

// 实现抽象方法,定义普通类
class Demo2 extends Person {
  // 重写抽象方法
  run(): void {
    console.log('run')
  }
}

接口

// 声明接口
interface Demo {
  hello: string // 接口可以定义属性

  // 接口中的抽象方法
  fun(): void
}

interface Demo2 {
  fun2(): void
}

// 接口可以多继承
interface Demo3 extends Demo, Demo2 {
  fun3(): void
}

// 实现接口,必须要实现所有抽象方法和属性
class Demo4 implements Demo3 {
  hello: string = 'hello'

  fun3(): void {
    throw new Error('Method not implemented.')
  }
  fun(): void {
    throw new Error('Method not implemented.')
  }
  fun2(): void {
    throw new Error('Method not implemented.')
  }
}

// 使用别名灵活性
type myObj1 = { name: string } | { age: number }
type myObj2 = { name: string } & { age: number }

const obj1: myObj1 = { name: 'Alice' }
const obj2: myObj1 = { age: 30 }
const obj3: myObj2 = { name: 'Alice', age: 30 }

// 使用接口灵活性,同名的可以合并属性
interface A1 {
  name: string
}
interface A1 {
  age: number
}

const obj4: A1 = {
  name: 'Alice',
  age: 30
}

多态

interface USB {
  start(): void
  run(): void
  end(): void
}
// 接口可以作为参数传递
function demo(u: USB) {
  u.start()
  u.run()
  u.end()
}
// 定义子类实现USB接口
class Mouse implements USB {
  start(): void {
    console.log('鼠标开始工作')
  }
  run(): void {
    console.log('鼠标运行')
  }
  end(): void {
    console.log('鼠标停止工作')
  }
}
// 定义子类实现USB接口
class Keyboard implements USB {
  start(): void {
    console.log('键盘开始工作')
  }
  run(): void {
    console.log('键盘运行')
  }
  end(): void {
    console.log('键盘停止工作')
  }
}
// 调用demo函数,子类可以作为参数传递
demo(new Mouse())
demo(new Keyboard())

联合类型 |

let num: 类型1 | 类型2 | 类型3 .... = 初始值
let num:number | string = 1 // 可以写多个类型 

// 1|'2' 在这里的1 和 '2‘是类型, 常量,表示numAndStr2的值只能是 1 或 '2'
let numAndStr2: 1|'2' = 1
let numAndStr3: 1|'2' = '2'

// 表明obj必须要有a 或 b属性,并且a的类型为1,或b的类型为'3'(可以a、b都有)
let obj:{a:1}|{b:'3'} = {a: 1};

交叉类型 &

是指把几种类型合并起来

// 交叉类型 
let obj:{name: string, age:number} & {height: number};
obj = {name: 'll', age: 19, height: 20} // 属性必须全部要有,少了一个都报错

// 如果一个属性出现多次类型的设置,必须都要满足才行
let obj1:{name: string, age:number} & {height: number, age: 18};
// obj1 = {name: 'xiaotian', age: 19, height: 20} // 报错, age不满足18
obj1 = {name: 'll', age: 18, height: 20}

复用type定义的类型

type Point = {
  x: number;
  y: number;
};

type Coordinate = Point & {
  z: number;
};

复用interface定义的类型

interface Point {
  x: number;
  y: number;
};

interface Coordinate extends Point {
  z: number;
}

interface复用type定义的类型

type Point = {
  x: number;
  y: number;
};

interface Coordinate extends Point {
  z: number;
}

type复用interface定义的类型

interface Point {
  x: number;
  y: number;
};

type Coordinate = Point & {
  z: number;
};

泛型

// 泛型,传入T类型
function fun<T>(arg1: T, arg2: T, arg3: T): T {
  return arg1
}
fun<number>(10, 20, 30)
fun<string>('abc', 'def', 'ghi')
fun<Function>( // 泛型是函数类型
  new Function(),
  () => {}, // 匿名函数
  () => {}
)
// fun<number>(10, 20, "abc")     // 报错,必须传同一类型

// 可以传入多个泛型,返回一个泛型
function fun2<T, E, W>(arg1: T, arg2: E, arg3: W): T {
  return arg1
}
// 使用传入多个泛型,数字、字符、对象 3种类型
fun2<number, string, { name: string }>(10, 'abc', { name: 'abc' })
// 泛型使用
interface IPerson<T1, T2> {
  name: T1
  age: T2
}
const person: IPerson<string, number> = {
  name: '张三',
  age: 18
}

interface IPerson2<T1 = string, T2 = number> {
  name: T1
  age: T2
}
const person2: IPerson2 = {
  name: '张三',
  age: 18
}

// 对象使用泛型
class Person<T1, T2> {
  name: T1
  age: T2
  sex: T1

  constructor(name: T1, age: T2, sex: T1) {
    this.name = name
    this.age = age
    this.sex = sex
  }
}

const p = new Person('张三', 18, '男')
const p2 = new Person<string, number>('张三', 18, '男')
const p3: Person<string, number> = new Person('张三', 18, '男')

复用指定属性和排除指定属性的定义

type Person = {
    name: string;
    age: number;
    id: number;
}

Pick 挑选出指定属性,生成新对象类型
type UserInfo = Pick<Person, 'name' | 'age'>; // 挑选出 { name: string; age: number }

Omit 排除指定的属性,生成新的对象类型
type UserInfo2 = Omit<Person, 'id'>; // 排除 id,生成 { name: string; age: number }

Partial 将对象所有属性变为可选
type PartialPerson = Partial<Person>; // { name?: string; age?: number; id?: number }

Readonly 将对象所有属性变为只读
type ReadonlyPerson = Readonly<Person>; // { readonly name: string; readonly age: number; readonly id: number }

Record 生成对象类型,例如
type PersonMap = Record<number, Person>; // { [index: number]: Person }

Exclude 排除一些联合类型
type UserInfoKeys = Exclude<keyof Person, 'id'>; // 'name' | 'age'

Webpack打包TS文件

创建test目录来测试一下,vscode打开这个目录

npm init -y创建package.json文件

安装WebPack:
npm i webpack webpack-cli webpack-dev-server -D

-D是安装开发环境,只用于开发环境

安装好以后查看package.json
image
安装好了这3个工具,然后需要创建webpack.config.js
image

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');		// 需要下载安装插件

module.exports = {
  entry: './src/index.ts',  // 入口文件

  output: {   // 输出到哪
    path: path.resolve(__dirname, "build"),  // 输出目录
    filename: 'bundle.js',  // 输出文件名
  },

  module: {
    rules: [
      {
        test: /\.ts$/,    // 正则匹配要转换的文件,ts结尾文件
        exclude: /node_modules/,    // 不转换的目录
        use: 'ts-loader'  // 使用ts-loader转换
      }
    ]
  },


  resolve: {
    extensions: ['.ts', '.js']   // 解析扩展名,哪种文件可以打包
  },

  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'   // 模板文件
    })
  ],

  mode: 'development',   // 开发模式
}

通过配置文件中的入口文件可以看到入口文件的位置
所以创建src目录,下面创建index.ts文件
image

function sum(a: number, b: number): number {
  return a + b;
}

console.log(sum(1, 2)); // 输出 3

安装html开发插件npm i html-webpack-plugin -D
可以开发中在浏览器中测试
创建一个对应的html文件
image

执行tsc --init
生成ts的配置tsconfig.json文件,可以先不修改,就默认的就行
image

安装tsc和ts-loader
npm i typescript ts-loader -D

-D是安装开发环境,只用于开发环境,上线时候不用这个插件

执行打包命令:webpack
image

测试编译好的js文件
image

如果在浏览器中测试查看,终端窗口使用命令webpack serve
这是开发环境的命令,热加载的效果,会及时生效
执行后浏览器打开http://localhost:8080/
然后查看浏览器的控制台查看效果
这个命令只在开发时使用,上线还是webpack

命名空间

编写代码时候经常会有同名的函数,可以通过命名空间区别开

// 导入其他ts或js文件,Three.ts文件在test目录下
import { Three } from './test'

namespace One {
  // 命名空间外部要使用的,必须导出,使用export
  export function add(a: number, b: number): number {
    return a + b
  }

  export const hello: string = 'hello'

  // 命名空间内部使用自己的函数,导出和不导出都可以使用
  console.log(add(1, 2))
}

namespace Two {
  export function add(a: string, b: string): string {
    return a + b
  }
}

// 测试当前文件命名空间
console.log(One.hello)
console.log(One.add(5, 5)) // 命名空间外部要使用,必须导出
console.log(Two.add('hello', ' world'))
// 测试其他文件命名空间
console.log(Three.sub(10, 5)) // 5
// 要想其他文件中使用,要声明导出
export namespace Three {
  export function sub(a: number, b: number): number {
    return a - b
  }
}

描述文件

开发中经常引入第三方组件,有ts和js文件,调用api时候会有问题和冲突,可以通过.d.ts后缀的文件区分处理
项目中不管在哪个位置写的以.d.ts结尾的文件,都是描述文件,作用就是开发过程中保证编译不出错

实例:
随便创建一个js文件,模拟当成第三方包

let ew_host = 'http://localhost:3000';
const ew_hello = 'hello world';

function shop(a, b) {
  return a + b;
}

let Person = {
  this.name = name;
  this.age = age;
}

比如使用的时候:报编译错误
image

console.log(ew_host)
console.log(ew_hello)

console.log(shop(1, 2))

const p = new Person('张三', 20)
console.log(p.name)

创建一个.d.ts文件

declare let ew_host: string
declare const ew_hello: string

declare function shop(n: number, m: number): number

declare class Person {
  name: string
  age: number

  constructor(name: string, age: number): void
}

ts文件中使用,就不会报编译错误了
image

更多其他的描述可以参考一下:
image

Vue3和TS结合

先随便创建一个目录,比如test目录,在这个目录的命令行窗口执行npm i -g @vue/cli安装vue3
如果之前已经安装了vue3版本,可以执行命令vue -V查看版本
image

确认安装了vue客户端,就可以创建vue3项目:vue create vue_ts

然后使用上下键选择:手动选择组件
image

然后可以进行选择可选的组件
image

下面的操作就可以根据提示慢慢来了
知道操作结束,自动会生成一个vue3的包含ts的项目
进入到项目的目录下,参考package.json的配置文件启动项目测试

vue3支持ts的组件

参考这个HomeView.vue的路由组件,以这个为父组件,
image

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
    <!-- 给子组件传入属性 字符类型和对象类型-->
    <MyComponent hello="hello MyComponent" :title="{ value: 'abc', color: 'red' }"></MyComponent>
    <MyComponent2 hello="hello MyComponent" :title="{ value: 'abc', color: 'green' }"></MyComponent2>
  </div>
</template>

// 设置成ts
<script lang="ts">
import MyComponent from '@/components/MyComponent.vue';
import MyComponent2 from '@/components/MyComponent2.vue';
import { defineComponent } from 'vue'; // 导入vue的ts组件

export default defineComponent({
  name: 'HomeView',
  components: {
    MyComponent,
    MyComponent2
  },
});
</script>

然后编写子组件
image

<template>
  <div>
    <h1>MyComponent</h1>
    num = {{ num }}
    <br>
    <div v-for="item in goods" :key="item.id">
      {{ item.id }}. {{ item.name }} - {{ item.price }}
    </div>

    <!-- 使用父组件传入进来的值,也就是下面props里面接收的 -->
    <h1 :style="{ color: title.color }">{{ hello }} ==> {{ title.value }}</h1>

  </div>
</template>

<script lang="ts">
// 导入vue的ts组件
import { PropType, defineComponent, ref } from 'vue';
// 导入定义好的模块
import { IProduct, titleInfo } from '../types';


export default defineComponent({
  name: 'MyComponent',

  // 接收父组件传来的值
  props: {
    hello: {
      type: String,     // 父组件传来的类型必须是字符串
      required: true    // 必传
    },
    title: {
      type: Object as PropType<titleInfo>,  // 父组件传来的类型必须是对象,使用PropType是为了确保类型安全,泛型的效果,然后断言
      default() {
        return {
          value: '默认值',
          color: 'red'
        }
      }
    }
  },

  setup() {
    let num = ref(10);  // 引入的值 10
    let goods = ref([] as IProduct[]);  // 断言引入的类型

    // 模拟数据
    goods.value = [
      { id: 1, name: '商品1', price: 100 },
      { id: 2, name: '商品2', price: 200 },
      { id: 3, name: '商品3', price: 300 }
    ];

    return {
      num,
      goods,
    }
  },

})

</script>
<template>
  <div>
    <h1>MyComponent2</h1>
    num = {{ num }}
    <br>
    <div v-for="item in goods" :key="item.id">
      {{ item.id }}. {{ item.name }} - {{ item.price }}
    </div>

    <h1 :style="{ color: title.color }">{{ hello }} ==> {{ title.value }}</h1>

  </div>
</template>

<!-- 指定域为setup,就可以不用写 setup() 的配置了 -->
<script lang="ts" setup>

import { PropType, defineProps, ref } from 'vue';
import { IProduct, titleInfo } from '../types';

// defineProps 和 props 配置一个意思,是接收父组件传来的值
defineProps({
  hello: {
    type: String,     // 父组件传来的类型必须是字符串
    required: true    // 必传
  },
  title: {
    type: Object as PropType<titleInfo>,  // 父组件传来的类型必须是对象
    default() {
      return {
        value: '默认值',
        color: '#000'
      }
    }
  }
});

let num = ref(10);
let goods = ref([] as IProduct[]);

goods.value = [
  { id: 1, name: '商品1', price: 100 },
  { id: 2, name: '商品2', price: 200 },
  { id: 3, name: '商品3', price: 300 }
];

// 因为不是写在配置的 setup() 里,所以也不用写return返回了

</script>

要使用的对象定义成了两个模块
image

最终效果
image

Vue3 + TS + Element-Plus

Element-Plus官网地址:https://element-plus.gitee.io/zh-CN/

参考官网文档,先安装element-plus组件:npm install element-plus --save

然后使用,在main.ts引入使用组件element-plus和样式
image

根据官网推荐的按需引入的自动导入
安装组件:npm install -D unplugin-vue-components unplugin-auto-import

我的项目上webpack打包的,就用webpack打包方式
编写webpack.config.js

const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')

module.exports = {
  // ...
  plugins: [
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
}

然后启动项目npm run serve

使用element-plus组件
修改这个组件内容,查看效果
image

比如把按钮组件的代码复制过来
image
覆盖在AboutView.vue文件中

然后去页面看效果
点到对应的页面路由
image

效果出来
image

posted @ 2024-02-24 10:04  aBiu--  阅读(7)  评论(0编辑  收藏  举报