豪翔天下

Change My World by Program

0%

官方中文文档

常用命令CLI

1
2
3
4
5
6
7
npm install --save-dev @types/node @types/validator
npm install sequelize reflect-metadata sequelize-typescript

npm install mysql2 --save

npm install --save-dev sequelize-cli # 安装命令行工具npx
npx sequelize-cli init # 初始化,会创建config, migrations, seeders, models目录

注意上一步创建的config目录默认是json格式的,我们一般会想从.env文件中读取配置,通常要将它改成config.js文件,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
require('dotenv').config()

module.exports = {
development: {
username: process.env.DB_USERNAME,
password: null,
database: 'database_development',
host: '127.0.0.1',
dialect: 'mysql',
},
test: {
username: 'root',
password: null,
database: 'database_test',
host: '127.0.0.1',
dialect: 'mysql',
},
production: {
username: 'root',
password: null,
database: 'database_production',
host: '127.0.0.1',
dialect: 'mysql',
},
};

数据库连接

1
2
3
4
5
6
7
var sequelize = new Sequelize('mysql://用户名:密码@HOST:3306/数据库', {
dialect: 'mysql', // 如果不指定这个参数,可能会报错Dialect needs to be explicitly supplied as of v4.0.0
logging: false // 默认会将sql查询都输出到console.log中,设置为false可以不用输出,不输出sql语句
})

// 直接执行SQL raw queries
const records = await sequelize.query("SELECT * FROM `users`", { type: QueryTypes.SELECT });

模型定义

  • 数据类型包括:
    • 字符串:STRING、STRING(1024)、STRING.BINARY、TEXT、TEXT(‘tiny’)、CITEXT(仅PostgreSQL和SQLite)、TSVECTOR(仅PostgreSQL)
    • 布尔:BOOLEAN
    • 数字:INTEGER、INTEGER.UNSIGNED、INTEGER.ZEROFILL、INTEGER.UNSIGNED.ZEROFILL、BIGING、BIGING(11)、FLOAT、FLOAT(11)、FLOAT(11, 10)、REAL(仅PostgreSQL)、REAL(11)(PostgreSQL)、REAL(11, 10)(仅PostgreSQL)、DOUBLE、DOUBLE(11)、DOUBLE(11, 10)、DECIMAL、DECIMAL(10, 2)
    • 日期:DATE、DATE(6)(仅MySQL)、DATEONLY
    • UUID:可以自动为字段生成UUID,type为UUID,defaultValue为UUIDV1或者UUIDV4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// 定义方式零,纯typescript的方式可以使用https://github.com/RobinBuschmann/sequelize-typescript
@Column(DataType.VIRTUAL)
accessToken: string; // 添加virtual额外的字段

@Column(DataType.JSON) data: any // JSON字段

@Column({field: 'user_id'})
userID: number; // 自定义字段名称

// 定义方式一,typescript方式
class PostModel extends Model {
// 定义一些值可以让其增加typescript声明
public id: number
public name: string

// 为typescript增加获取关联对象的方法声明
public getUser! BelongsToGetAssociationMixin<BandModel>

static initModel (sequelize: Sequelize): void {
id: {
autoIncrement: true,
primaryKey: true,
type: INTEGER
},
name: {
type: STRING,
allowNull: false,
defaultValue: 'test',
validate: { // 数据校验
is: /^[a-z]+$/i, // 满足某个正则
is: ["^[a-z]+$",'i'], // 同上,只是正则用数组来写
not: /^[a-z]+$/i,
not: ["^[a-z]+$",'i'],
isEmail: true,
isUrl: true,
isIP: true,
isIPv4: true,
isIPv6: true,
isAlpha: true,
isAlphanumeric: true,
isNumeric: true,
isInt: true,
isFloat: true,
isDecimal: true,
isLowercase: true,
isUppercase: true,
notNull: true,
isNull: true, // 只允许null
notEmpty: true, // 不能是空字符串
equals: 'specific value',
contains: 'foo',
notIn: [['foo', 'bar']],
isIn: [['foo', 'bar']],
notContains: 'bar',
len: [2,10],
isUUID: 4,
isDate: true,
isAfter: "2011-11-05",
isBefore: "2011-11-05",
max: 23,
min: 23,
isCreditCard: true, // 是否是信用卡数字
isEven(value) { // 自定义校验
if (parseInt(value) % 2 !== 0) {
throw new Error('Only even values are allowed!');
}
}
},
isIn: {
args: [['en', 'zh']],
msg: "Must be English or Chinese" // 上面的校验方式都能够自定义,但是min和max不知道为啥不行,如果是min或者max就改成用len吧
}
}
}

static relate (): void {
PostModel.belongsTo(UserModel, {
as: 'user',
foreignKey: 'user_id'
})
}

@AfterUpdate
static createUser(user: UserModel) {
console.log(user.id);
}

@AfterDestroy // 软删除也是这里
static async afterDestroy(use: UserModel) {}
}

// 定义方式二
const Post = sequelize.define('post', {
id: {
autoIncrement: true,
primaryKey: true,
type: INTEGER
},
name: {
type: STRING,
allowNull: false,
defaultValue: 'test'
},
firstName: {
type: STRING,
field: 'first_name' // 自定义列名称,
comment: '列注释' // 注释仅针对MySQL、MariaDB、PostgreSQL、MSSQL
},
fullName: {
type: VIRTUAL, // 定义virtual字段,即实际不存在数据库中的字段
get: function (this: UserModel) {
return this.firstName + this.name
},
set: function (val) {
this.setDataValue('name', val)
}
},
date: {
type: DATE,
defaultValue: NOW
},
// unique参数的值可以是不二值或者字符串,如果多格列具有相同的字符串unique,他们会组成一个复合唯一键
uniqueOne: { type: DataTypes.STRING, unique: 'compositeIndex' },
uniqueTwo: { type: DataTypes.INTEGER, unique: 'compositeIndex' },

data: {
type: JSON
},
created_at: {
type: DATE
}
}, {
indexes: [{ unique: true, fields: ['field1']}], // 也可以在最后创建索引
timestamps: true, // 是否自动添加createdAt和updatedAt
tableName: 'MyPosts' // 自定义table name,如果不提供,sequelize会根据模型名称自动以复数形式设置表名
paranoid: true, // 定义该表为偏执表,即自带软删除,使用destroy能自动软删除
deletedAt: 'mydelete', // 偏执表软删除字段默认为deletedAt,这里可以指定自定义的字段名
validate: { // 基于model的校验,可以同时校验多个字段
bothCoordsOrNone() {
if ((this.latitude === null) !== (this.longitude === null)) {
throw new Error('Either both latitude and longitude, or neither!');
}
}
}
}
);

// 定义模型关系
Post.associate = () => {
Post.User = Post.belongsTo(app.model.Post)
}

User.associate = () => {
User.hasMany(Post)
}

// 定义模型类方法
Post.customQuery = () => {}

// 定义模型实例方法
Post.prototype.customQuery = () => {}

关联关系定义

  • sequelize默认会给关联关系添加对应的读取方法,例如如果和user关联,那么会有getUser方法,而如果是一对多,或者多对多,那么会有getUsers方法,但是如果是typescript,就需要我们先将该方法声明一下

    1
    2
    3
    public getUsers: BelongsToManyGetAssociationsMixin<UserModel>	// 懒加载

    await fooInstance.$get('bar'); // sequelize-typescript不使用include手动获取关联对象

One to One一对一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 装饰器定义
@ForeignKey(() => Person)
@Column
authorId: number;
@BelongsTo(() => Person)
author: Person;

@BelongsTo(() => Person, 'person_id') // 指定外键
author: Person;

Post.User = Post.belongsTo(app.model.Post, { foreignKey: 'post_id', as: 'Post' }),
Post.PostOwn = User.belongsTo(app.model.Post, {'foreignKey': 'id', as: 'PostOwn'}) // 如果要与当前表自身做join等操作,那么也需要定义一个与自身的关联
PostModel.belongsTo(UserModel)

// hasOne自动添加的方法
fooInstance.getBar()
fooInstance.setBar()
fooInstance.createBar()

One to Many 一对多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const Foo = sequelize.define('foo', { name: DataTypes.STRING });
const Bar = sequelize.define('bar', { status: DataTypes.STRING });
Foo.hasMany(Bar, {
scope: { // 可以通过scope限制某个关联表的字段
status: 'open'
},
as: 'openBars'
});

// hasMany自动添加的方法
fooInstance.getBars()
fooInstance.countBars()
fooInstance.hasBar()
fooInstance.hasBars()
fooInstance.setBars()
fooInstance.addBar()
fooInstance.addBars()
fooInstance.removeBar()
fooInstance.removeBars()
fooInstance.createBar()

// belongsToMany自动添加的方法
fooInstance.getBars()
fooInstance.countBars()
fooInstance.hasBar()
fooInstance.hasBars()
fooInstance.setBars()
fooInstance.addBar()
fooInstance.addBars()
fooInstance.removeBar()
fooInstance.removeBars()
fooInstance.createBar()

Many to Many 多对多

多态多对多
  • 有中间表,且使用target_idtarget_type来表示关联的表的类型
  • 除了我下面这个例子,还可以参考Sequelize中文文档
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 例如一个用户有多篇文章,多辆车,一篇文章或者一辆车也能同时属于多个用户,那么就有这么几张表: Car, Post, User, UserThings关联表
CarModel.belongsToMany(UserModel, {
through: {
model: UserThingModel, // 中间表
unique: false, // 如果unique为true,那么表示只有一个
scope: {
targetType: 'car', // 当关联的是car时,其`target_type`字段为car
},
foreighKey: 'target_id',
constraints: false
}
})

PostModel.belongsToMany(UserModel, {
through: {
model: UserThingModel, // 中间表
unique: false, // 如果unique为true,那么表示只有一个
scope: {
targetType: 'post', // 当关联的是post时,其`target_type`字段为post
},
foreighKey: 'target_id',
constraints: false
}
})

增删改查

创建操作

1
2
3
4
5
6
7
8
9
10
11
12
const user = await User.create({ firstName: "Jane", lastName: "Doe" });

// 批量创建
const captains = await Captain.bulkCreate([
{ name: 'Jack Sparrow' },
{ name: 'Davy Jones' }
]);

await User.findOrCreate({
where:{}, // 比较的字段
defaults: {} // 填入的字段
})
阅读全文 »

常用语法

基本标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 转义,显示原始html内容
{!! $name !!}

# 转义花括号,因为花括号是特殊字符,如果要直接显示{{}}等内容需要在前面加入@符号
@{{ $name }} // 会直接显示{{ $name }}

# 使用or简化三目运算符
{{ $name or 'Default'}} // {{ isset($name) ? $name : 'Default' }}

# 时间格式转换
{{ $user->created_at->format('d/m/Y') }}

# 获取当前路由
{{ url()->current() == route('/user') }}

# 获取路由参数
{{ request()->get('abc') }}

# 带路由参数的路由生成
{{ route('/users', $id)}}
{{ route('/users', [$id])}}
{{ route('/users', ['id' => $id])}}

{{-- 模板的注释语法 --}}
阅读全文 »

Request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 获取请求指定字段
$request->name; // 直接获取
$request->input('name');
$request->input('name', 'Sally'); // 指定默认值
$request->input('user.name'); // 可以直接用点号获取JSON格式的请求体
$request->input('products.0.name'); // 如果请求数据是数组可以用这个方法获取数组内部元素
$request->input('products.*.name'); // 同上
$request->query('name'); // 获取查询参数
$request->query('name', 'Helen'); // 带默认值
$request->all(); // 获取所有请求参数为一个数组
$request->input(); // 同上
$request->query(); // 所有查询参数转换为数组
$request->boolean('archived'); // 获取布尔值,能够自动判断1/"1"/true/"true"/"on"/"yes",6.x开始
$request->only(['username', 'password']); // 仅获取指定字段的请求
$request->except(['credit_card']); // 仅排除指定字段的请求

// 判断请求是否包含某个key
$request->has('name');
$request->has(['name', 'email']);
$request->hasAny(['name', 'email']);
$request->filled('name'); // 是否包含并且不为空
$request->missing('name'); // 不包含

// 获取请求地址
$request->path(); // 获取请求路径,例如https://domain.com/foo/bar就会返回foo/bar
$request->is('admin/*'); // 正则匹配请求路径
$request->fullUrl(); // 包含请求参数的完整url,例如https://dmoain.com/foo/bar?abc=def
$request->url(); // 不包含请求参数的完整url,例如https://dmoain.com/foo/bar
$request->root(); // 获取域名部分,包括http,例如https://domain.com
$request()->getHost(); // 获取纯域名部分,例如domain.com

// 获取请求方法
$request->method();
$request->isMethod('post');// 判断请求方法

$request->route(); # 通过request获取Route对象

$request->cookie('name'); // 获取cookie,同Cookie::get('name');

// 动态改变或新增request的值
$request->merge([
'keyword' => $request->search,
'page' => 2
]);

// 判断请求类型
request()->ajax(); // 判断请求是否是ajax请求
request()->expectsJson(); // 判断客户端是否希望得到JSON响应

// 去掉路由参数
$request->route()->forgetParameter('param');
阅读全文 »

  • 默认启用了TrimStringsConvertEmptyStringsToNull两个中间件的,一个自动去除前后空白,一个将空字符串转换为null

直接验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
$validatedData = $request->validate([
'title' => 'required|unique:posts|max:255',
'title' => ['required', 'unique:posts', 'max:255'], // 也可以写成数组的形式
'person.*.email' => 'email|unique:users', // 校验数组
'person.*.first_name' => 'required_with:person.*.last_name'
'title' => ['required', function ($attribute, $value, $fail) { // 简单的自定义验证规则可以不用建验证类,直接用匿名函数
if ($value === 'foo') {
$fail($attribute.' is invalid.');
}
},],
]);

// 或者这样创建
$validator = Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);

// 判断请求类型,如果是ajax请求,那么返回json数据和422,如果非ajax那么重定向刷新页面
if ($validator->fails()) {
if ($request->ajax()) {
return response()->json($validator->messages(), Response::HTTP_BAD_REQUEST);
} else {
return redirect('post/create')
->withErrors($validator) // 刷新session中存储的错误信息,可用在view中
->withInput();
}
}

// 同样可以自定义错误信息
$messages = [
'required' => 'The :attribute field is required.',
];
$validator = Validator::make($input, $rules, $messages);

// 满足某个条件时才验证,例如下面当游戏>=100的时候才验证指定字段
$v->sometimes('reason', 'required|max:500', function ($input) {
return $input->games >= 100;
});
$v->sometimes(['reason', 'cost'], 'required', function ($input) {
return $input->games >= 100;
});
阅读全文 »

帮助方法

  • Laravel中的帮助方法无法重写,如果要重写,只能自己单独写放在app/helpers.php里面去,还需要在composer.json中添加配置:

    1
    2
    3
    4
    5
    6
    "autoload": {
    ...
    "files": [
    "app/helpers.php"
    ]
    }

数组和对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# 返回第一个或最后一个元素
head($array);
last($array);

# 合并多个数组为一个单一的数组
Arr::collapse([[1,2,3], [4,5,6], [7,8,9]]); // [1,2,3,4,5,6,7,8,9]

# 从数组/字典移除指定的key
$array = ['name' => 'Desk', 'price' => 100, 'products' => ['desk' => ['price' => 100]]];
Arr::except($array, ['price']); // ['name' => 'Desk']
# 还可以通过点号的方式来移出多级key
Arr::forget($array 'products.desk'); // ['name' => 'Desk', 'price' => 100]
# 使用点号获取多级的值,第三个参数为默认值
$array = ['products' => ['desk' => ['price' => 100]]];
Arr::get($array, 'products.desk.price', $defaultValue); // 100
data_get($array, 'products.desk.price', $defaultValue); // 同上
data_get($array, '*.name');// 设置支持通配符
# 可使用has来查看是否有某个key,或多个key
Arr::has($array, ['product.price', 'product.discount']);
# 查看是否包含多个key中的一个
Arr::hasAny($array, ['product.name', 'product.discount']);
# 给数组添加元素
Arr::prepend($array, 'zero');
Arr::prepend($array, 'Desk', 'name'); // 会添加一个'name' => 'Desk'
# 获取并移除一个元素
Arr::pull($array, 'name', $defaultValue)

# 检查数组是否存在某个key
Arr::exists($array, 'key');

# 查找第一个回调函数返回true的元素,第三个参数可以指定默认值
Arr::first($array, function($value, $key) {return $value >= 2;}, $defaultValue);

# 仅返回指定key组成的数组
$array = ['name' => 'Desk', 'price' => 100, 'orders' => 10];
Arr::only($array, ['name', 'price']); // ['name' => 'Desk', 'price' => 100]

# 将数组/字典所有的value组成一个单一的数组
$array = ['name' => 'Joe', 'languages' => ['PHP', 'Ruby']];
$flattened = Arr::flatten($array); // ['Joe', 'PHP', 'Ruby']
# 将指定的key的value组成一个单一的数组
$array = [
['developer' => ['id' => 1, 'name' => 'Taylor']],
['developer' => ['id' => 2, 'name' => 'Abigail']],
];
Arr::pluck($array, 'developer.name'); // ['Taylor', 'Abigail']
Arr::pluck($array, 'developer.name', 'developer.id'); // [1 => 'Taylor', 2 => 'Abigail']

// 将数组作为url的查询参数
$array = ['name' => 'Taylor', 'order' => ['column' => 'created_at', 'direction' => 'desc']];
Arr::query($array); // name=Taylor&order[column]=created_at&order[direction]=desc

// 从数组中随机取一个元素
Arr::random($array);
Arr::random($array, 2); // 第二个参数表示随机取N个元素

// 数组排序
Arr::sort($array);
array_values(Arr::sort($array, function ($value) { // 如果是字段需要指定排序方式
return $value['name'];
}));

// 返回符合条件的元素组成的数组,类似于js里面的filter
Arr::where($array, function ($value, $key) {
return is_string($value);
});
阅读全文 »

前两天团队接到一个新项目,我需要为其配置Circle CI,于是就又折腾了一下,不过还好,毕竟之前做过运维的,CI的基本原理是知道的,所以很快就弄好了,这里也简单的记录一下。

1. 为项目开启Circle CI

这一步需要在circleci后台项目列表里面为指定的项目开启配置Set Up Project,因为第一步已经编写好了配置文件,所以这里可以直接选择start building。这一步circleci会自动在对应的项目里面添加Deploy Key并且会自动配置到circleci的项目管理里面,如下:

阅读全文 »

禁用csrf认证

全局禁用csrf

app/Http/Kernel.php中,$middleware表示全局中间件,而$routeMiddleware表示针对某个路由的中间件,所以只需要把csrf在$middleware中注释掉,然后在$routeMiddleware中添加'csrf' => 'App\Http\Middleware\VerifyCsrfToken'
如果要在某个路由上使用就这样:

1
2
3
Route::group(['middleware' => 'csrf'], function(){     // csrf保护的接口
Route::get('/', 'HomeController@index');
}
针对某几个接口单独禁用csrf

可以在app/Http/Middleware/VerifyCsrfToken$except添加,但是这里的添加只能以正则的方式来匹配,不能使用路由别名,如果路由中有参数可以用星号代替

1
2
3
4
protected $except = [
'webhook/*',
'users/*/profile'
];

一个页面调用多个接口如何传递CSRF Token

由于csrf_token是存储于session的,依照laravel的实现机制,同一时间只能有一个_token,所以无法实现一个页面设置多个csrf token,要解决这个问题要么将非必要的接口忽略csrf,要么每次请求api后从后台生成并返回一个新的token

阅读全文 »

在需要这个效果的时候首先在npm仓库找到了vue-dragscroll库,但是应用在我们自己项目上的时候拖动起来却非常慢,元素跟不上鼠标的移动速度,无奈,就自己简单的实现了一个拖拽指令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import Vue from 'vue'

Vue.directive('dragscroll', function (el) {
el.onmousedown = function (ev) {
const disX = ev.clientX
const disY = ev.clientY
const originalScrollLeft = el.scrollLeft
const originalScrollTop = el.scrollTop
const originalScrollBehavior = el.style['scroll-behavior']
const originalPointerEvents = el.style['pointer-events']
el.style['scroll-behavior'] = 'auto'
// 鼠标移动事件是监听的整个document,这样可以使鼠标能够在元素外部移动的时候也能实现拖动
document.onmousemove = function (ev) {
ev.preventDefault()
const distanceX = ev.clientX - disX
const distanceY = ev.clientY - disY
el.scrollTo(originalScrollLeft - distanceX, originalScrollTop - distanceY)
// 由于我们的图片本身有点击效果,所以需要在鼠标拖动的时候将点击事件屏蔽掉
el.style['pointer-events'] = 'none'
}
document.onmouseup = function () {
document.onmousemove = null
document.onmouseup = null
el.style['scroll-behavior'] = originalScrollBehavior
el.style['pointer-events'] = originalPointerEvents
}
}
})

由于我们项目使用了是Nuxtjs,如果完全由后端渲染,是无法在document上进行事件监听的,所以在nuxt.config.js中这样定义插件:

1
2
3
plugins: [
{ src: '@/plugins/dragscroll', ssr: false }
],

最后可以在任何元素上应用该指令:

1
<div dragscroll><img /></div>

最终的结果类似这样

布局

栅格布局

  • el-rowel-col可以搭配实现24格的栅格布局

  • el-row支持如下属性

参数 含义 说明 可选值 默认值
gutter 每两个栅格之间的间隔(单位是px) number - 0
type 布局模式 string - -
justify flex布局下的水平排列方式 string start/end/center/space-around/space-between start
align flex布局下的垂直排列方式 string top/middle/bottom top
tag 自定义元素标签 string * div
  • el-col支持如下属性
参数 含义 说明 可选值 默认值
span 栅格占据的列数 number - 24
offset 栅格左侧的间隔格数 number - 0
push 栅格向右移动格数 number - 0
pull 栅格向左移动格数 number - 0
xs <768px 响应式栅格数或者栅格属性对象 number/object (例如: {span: 4, offset: 4})
sm ≥768px 响应式栅格数或者栅格属性对象 number/object (例如: {span: 4, offset: 4})
md ≥992px 响应式栅格数或者栅格属性对象 number/object (例如: {span: 4, offset: 4})
lg ≥1200px 响应式栅格数或者栅格属性对象 number/object (例如: {span: 4, offset: 4})
xl ≥1920px 响应式栅格数或者栅格属性对象 number/object (例如: {span: 4, offset: 4}) - -
tag 自定义元素标签 string * Div

栅格布局一般会根据实际的各个分辨率下的情况来设置每个布局的宽度,如果想要直接换行,也可以某一种分辨率之和超过24,这样只要两个col超过24就会换行,例如:

1
2
3
4
5
6
<el-row :gutter="24">
<el-col :xs="20" :sm="6" :md="4" :lg="3" :xl="1"><div></div></el-col>
<el-col :xs="20" :sm="6" :md="8" :lg="9" :xl="11"><div></div></el-col>
<el-col :xs="20" :sm="6" :md="8" :lg="9" :xl="11"><div</div></el-col>
<el-col :xs="20" :sm="6" :md="4" :lg="3" :xl="1"><div</div></el-col>
</el-row>
阅读全文 »

这个周末有点闲暇时间,老婆在我身后准备期末考试(回老家一人一张书桌),所以我准备做个年终总结。因为疫情、老婆怀孕、换工作,所以年初的计划基本全部被打乱了。

工作方面

万万没想到在上家公司的最后半年会这么累,不过也还好,这让我蓄谋已久的离职也变得顺理成章了。本来上家公司的工资一直很低,但好在工作清闲,所以虽然我一直说要离职,但始终没有下定决心。不过由于公司内部调整,阴差阳错地进入了一个业务部门。我实在无法适应大公司的业务部门,仅用半个月的时间就超过了我过去三年所有的加班时间,并且周末还得时刻神经紧绷,因为公司的问题电话会随时打给你,哪怕问题和你根本没半毛钱关系,你也得拿出电脑去解决。那种感觉是我再也不想经历的。所以,在请完婚假后第一天就去提交离职申请了。年初我就计算过,今年产检、年假、婚假、产检、离职、疫情,可能往后几十年,就数今年的假期最多了,不过假期多也没什么用,因为老婆假期不多,即使婚假也得在家复习。所以我过了所有人都无法想象的婚假: 在家撸代码。

技术方面

去年年底由于工作安排,强迫学习了Java,基本达到了能熟练开发Java项目的地步。不过由于那边业务比较单一历史框架死板,所以也没遇到多少技术难题,加班主要是因为任务太紧。6月底至今在新公司,全栈开发岗位,不过大部分是前端Vuejs + Nodejs,所以前面两周也很累的,每天下班后都需要恶补一下前端知识。但远程工作好的地方是不加班,以后我会详细介绍远程工作的爽与不爽。几周过去,也算是入门前端了,基本能够配合框架快速实现想要的效果了,也渐渐地开始尝试看Vue源码,因为总感觉自己的前端代码写得有点low。不出意外,下半年在技术上主要是针对前端和AWS的深入学习了。对了,新公司基本是用英语交流的,所以英语学习也至关重要;据老板说转正后每周有和外国友人直接语音交流的机会,不过在一群高手面前,转正还得继续努力💪。

生活方面

一方面因为换到了远程工作且新工作不给买社保,所以上半年一直在研究社保和个人所得税方面的问题,现在的我自我感觉算是半个这方面的专家了。现在,我是以个人身份参加了职工医保和养老保险,算下来一个月一千多,不用说,肯定公司帮忙交划算,不过个人如果不用其它险种和公积金,这样交才是最划算的。

另一方面,家里要迎来新成员了。做了好多的计划,买了好多的东西,本来6月份因为多干了一些活儿多几千块收入,但是618买一波母婴用品花得干干净净的。第一次知道他们为啥被称为“四脚吞金兽”了。唯一让我压力小点的就是有极大的可能是女儿,我那时相当高兴的呀。虽然我积攒已久的育儿经验都得改一下,但是女孩子的话是真的真的好轻松,感谢老婆!