Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
m0_68066342
uni-app
提交
2462cfc0
U
uni-app
项目概览
m0_68066342
/
uni-app
与 Fork 源项目一致
Fork自
DCloud / uni-app
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
U
uni-app
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
2462cfc0
编写于
4月 22, 2019
作者:
fxy060608
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat(mp,h5):
uni://form-field
上级
404139f5
变更
15
隐藏空白更改
内联
并排
Showing
15 changed file
with
290 addition
and
36 deletion
+290
-36
build/rollup.config.js
build/rollup.config.js
+1
-0
package.json
package.json
+2
-1
packages/uni-app-plus/dist/index.js
packages/uni-app-plus/dist/index.js
+32
-6
packages/uni-app-plus/package.json
packages/uni-app-plus/package.json
+1
-1
packages/uni-mp-baidu/dist/index.js
packages/uni-mp-baidu/dist/index.js
+32
-6
packages/uni-mp-baidu/package.json
packages/uni-mp-baidu/package.json
+1
-1
packages/uni-mp-toutiao/dist/index.js
packages/uni-mp-toutiao/dist/index.js
+32
-6
packages/uni-mp-toutiao/package.json
packages/uni-mp-toutiao/package.json
+1
-1
packages/uni-mp-weixin/dist/index.js
packages/uni-mp-weixin/dist/index.js
+32
-6
packages/uni-mp-weixin/package.json
packages/uni-mp-weixin/package.json
+1
-1
src/core/runtime/wrapper/create-component.js
src/core/runtime/wrapper/create-component.js
+2
-2
src/core/runtime/wrapper/util.js
src/core/runtime/wrapper/util.js
+30
-4
src/core/view/plugins/behaviors/form-field.js
src/core/view/plugins/behaviors/form-field.js
+103
-0
src/core/view/plugins/behaviors/index.js
src/core/view/plugins/behaviors/index.js
+12
-0
src/core/view/plugins/index.js
src/core/view/plugins/index.js
+8
-1
未找到文件。
build/rollup.config.js
浏览文件 @
2462cfc0
...
...
@@ -41,6 +41,7 @@ module.exports = {
replace
({
__GLOBAL__
:
platform
.
prefix
,
__PLATFORM_TITLE__
:
platform
.
title
,
__PLATFORM_PREFIX__
:
JSON
.
stringify
(
platform
.
prefix
),
__PLATFORM__
:
JSON
.
stringify
(
process
.
env
.
UNI_PLATFORM
)
})
],
...
...
package.json
浏览文件 @
2462cfc0
...
...
@@ -76,7 +76,8 @@
"__PLATFORM__"
:
true
,
"__VERSION__"
:
true
,
"__GLOBAL__"
:
true
,
"__PLATFORM_TITLE__"
:
true
"__PLATFORM_TITLE__"
:
true
,
"__PLATFORM_PREFIX__"
:
true
},
"rules"
:
{
"no-tabs"
:
0
,
...
...
packages/uni-app-plus/dist/index.js
浏览文件 @
2462cfc0
...
...
@@ -402,8 +402,32 @@ function createObserver (name) {
}
}
function
getBehaviors
(
vueExtends
,
vueMixins
)
{
function
getBehaviors
(
vueOptions
)
{
const
vueBehaviors
=
vueOptions
[
'
behaviors
'
];
const
vueExtends
=
vueOptions
[
'
extends
'
];
const
vueMixins
=
vueOptions
[
'
mixins
'
];
let
vueProps
=
vueOptions
[
'
props
'
];
if
(
!
vueProps
)
{
vueOptions
[
'
props
'
]
=
vueProps
=
[];
}
const
behaviors
=
[];
if
(
Array
.
isArray
(
vueBehaviors
))
{
vueBehaviors
.
forEach
(
behavior
=>
{
behaviors
.
push
(
behavior
.
replace
(
'
uni://
'
,
`
${
"
wx
"
}
://`
));
if
(
behavior
===
'
uni://form-field
'
)
{
if
(
Array
.
isArray
(
vueProps
))
{
vueProps
.
push
(
'
name
'
);
vueProps
.
push
(
'
value
'
);
}
else
{
vueProps
[
'
name
'
]
=
String
;
vueProps
[
'
value
'
]
=
null
;
}
}
});
}
if
(
isPlainObject
(
vueExtends
)
&&
vueExtends
.
props
)
{
behaviors
.
push
(
Behavior
({
...
...
@@ -486,6 +510,11 @@ function getProperties (props, isBehavior = false, file = '') {
}
function
wrapper$1
(
event
)
{
// TODO 又得兼容 mpvue 的 mp 对象
try
{
event
.
mp
=
JSON
.
parse
(
JSON
.
stringify
(
event
));
}
catch
(
e
)
{}
event
.
stopPropagation
=
noop
;
event
.
preventDefault
=
noop
;
...
...
@@ -495,9 +524,6 @@ function wrapper$1 (event) {
event
.
detail
=
{};
}
// TODO 又得兼容 mpvue 的 mp 对象
event
.
mp
=
event
;
if
(
isPlainObject
(
event
.
detail
))
{
event
.
target
=
Object
.
assign
({},
event
.
target
,
event
.
detail
);
}
...
...
@@ -887,9 +913,9 @@ function initVm$2 (VueComponent) {
function
createComponent
(
vueOptions
)
{
vueOptions
=
vueOptions
.
default
||
vueOptions
;
const
properties
=
getProperties
(
vueOptions
.
props
,
false
,
vueOptions
.
__file
);
const
behaviors
=
getBehaviors
(
vueOptions
);
const
behaviors
=
getBehaviors
(
vueOptions
[
'
extends
'
],
vueOptions
[
'
mixins
'
]
);
const
properties
=
getProperties
(
vueOptions
.
props
,
false
,
vueOptions
.
__file
);
const
VueComponent
=
Vue
.
extend
(
vueOptions
);
...
...
packages/uni-app-plus/package.json
浏览文件 @
2462cfc0
{
"name"
:
"@dcloudio/uni-app-plus"
,
"version"
:
"0.0.22
7
"
,
"version"
:
"0.0.22
8
"
,
"description"
:
"uni-app app-plus"
,
"main"
:
"dist/index.js"
,
"scripts"
:
{
...
...
packages/uni-mp-baidu/dist/index.js
浏览文件 @
2462cfc0
...
...
@@ -535,8 +535,32 @@ function createObserver (name) {
}
}
function
getBehaviors
(
vueExtends
,
vueMixins
)
{
function
getBehaviors
(
vueOptions
)
{
const
vueBehaviors
=
vueOptions
[
'
behaviors
'
];
const
vueExtends
=
vueOptions
[
'
extends
'
];
const
vueMixins
=
vueOptions
[
'
mixins
'
];
let
vueProps
=
vueOptions
[
'
props
'
];
if
(
!
vueProps
)
{
vueOptions
[
'
props
'
]
=
vueProps
=
[];
}
const
behaviors
=
[];
if
(
Array
.
isArray
(
vueBehaviors
))
{
vueBehaviors
.
forEach
(
behavior
=>
{
behaviors
.
push
(
behavior
.
replace
(
'
uni://
'
,
`
${
"
swan
"
}
://`
));
if
(
behavior
===
'
uni://form-field
'
)
{
if
(
Array
.
isArray
(
vueProps
))
{
vueProps
.
push
(
'
name
'
);
vueProps
.
push
(
'
value
'
);
}
else
{
vueProps
[
'
name
'
]
=
String
;
vueProps
[
'
value
'
]
=
null
;
}
}
});
}
if
(
isPlainObject
(
vueExtends
)
&&
vueExtends
.
props
)
{
behaviors
.
push
(
Behavior
({
...
...
@@ -635,6 +659,11 @@ function getProperties (props, isBehavior = false, file = '') {
}
function
wrapper$1
(
event
)
{
// TODO 又得兼容 mpvue 的 mp 对象
try
{
event
.
mp
=
JSON
.
parse
(
JSON
.
stringify
(
event
));
}
catch
(
e
)
{}
event
.
stopPropagation
=
noop
;
event
.
preventDefault
=
noop
;
...
...
@@ -654,9 +683,6 @@ function wrapper$1 (event) {
}
}
// TODO 又得兼容 mpvue 的 mp 对象
event
.
mp
=
event
;
if
(
isPlainObject
(
event
.
detail
))
{
event
.
target
=
Object
.
assign
({},
event
.
target
,
event
.
detail
);
}
...
...
@@ -1070,9 +1096,9 @@ function initVm$2 (VueComponent) {
function
createComponent
(
vueOptions
)
{
vueOptions
=
vueOptions
.
default
||
vueOptions
;
const
properties
=
getProperties
(
vueOptions
.
props
,
false
,
vueOptions
.
__file
);
const
behaviors
=
getBehaviors
(
vueOptions
);
const
behaviors
=
getBehaviors
(
vueOptions
[
'
extends
'
],
vueOptions
[
'
mixins
'
]
);
const
properties
=
getProperties
(
vueOptions
.
props
,
false
,
vueOptions
.
__file
);
const
VueComponent
=
Vue
.
extend
(
vueOptions
);
...
...
packages/uni-mp-baidu/package.json
浏览文件 @
2462cfc0
{
"name"
:
"@dcloudio/uni-mp-baidu"
,
"version"
:
"0.0.82
4
"
,
"version"
:
"0.0.82
5
"
,
"description"
:
"uni-app mp-baidu"
,
"main"
:
"dist/index.js"
,
"scripts"
:
{
...
...
packages/uni-mp-toutiao/dist/index.js
浏览文件 @
2462cfc0
...
...
@@ -619,8 +619,32 @@ function createObserver (name) {
}
}
function
getBehaviors
(
vueExtends
,
vueMixins
)
{
function
getBehaviors
(
vueOptions
)
{
const
vueBehaviors
=
vueOptions
[
'
behaviors
'
];
const
vueExtends
=
vueOptions
[
'
extends
'
];
const
vueMixins
=
vueOptions
[
'
mixins
'
];
let
vueProps
=
vueOptions
[
'
props
'
];
if
(
!
vueProps
)
{
vueOptions
[
'
props
'
]
=
vueProps
=
[];
}
const
behaviors
=
[];
if
(
Array
.
isArray
(
vueBehaviors
))
{
vueBehaviors
.
forEach
(
behavior
=>
{
behaviors
.
push
(
behavior
.
replace
(
'
uni://
'
,
`
${
"
tt
"
}
://`
));
if
(
behavior
===
'
uni://form-field
'
)
{
if
(
Array
.
isArray
(
vueProps
))
{
vueProps
.
push
(
'
name
'
);
vueProps
.
push
(
'
value
'
);
}
else
{
vueProps
[
'
name
'
]
=
String
;
vueProps
[
'
value
'
]
=
null
;
}
}
});
}
if
(
isPlainObject
(
vueExtends
)
&&
vueExtends
.
props
)
{
behaviors
.
push
(
Behavior
({
...
...
@@ -703,6 +727,11 @@ function getProperties (props, isBehavior = false, file = '') {
}
function
wrapper$1
(
event
)
{
// TODO 又得兼容 mpvue 的 mp 对象
try
{
event
.
mp
=
JSON
.
parse
(
JSON
.
stringify
(
event
));
}
catch
(
e
)
{}
event
.
stopPropagation
=
noop
;
event
.
preventDefault
=
noop
;
...
...
@@ -712,9 +741,6 @@ function wrapper$1 (event) {
event
.
detail
=
{};
}
// TODO 又得兼容 mpvue 的 mp 对象
event
.
mp
=
event
;
if
(
isPlainObject
(
event
.
detail
))
{
event
.
target
=
Object
.
assign
({},
event
.
target
,
event
.
detail
);
}
...
...
@@ -1078,9 +1104,9 @@ function initVm$2 (VueComponent) {
function
createComponent
(
vueOptions
)
{
vueOptions
=
vueOptions
.
default
||
vueOptions
;
const
properties
=
getProperties
(
vueOptions
.
props
,
false
,
vueOptions
.
__file
);
const
behaviors
=
getBehaviors
(
vueOptions
);
const
behaviors
=
getBehaviors
(
vueOptions
[
'
extends
'
],
vueOptions
[
'
mixins
'
]
);
const
properties
=
getProperties
(
vueOptions
.
props
,
false
,
vueOptions
.
__file
);
const
VueComponent
=
Vue
.
extend
(
vueOptions
);
...
...
packages/uni-mp-toutiao/package.json
浏览文件 @
2462cfc0
{
"name"
:
"@dcloudio/uni-mp-toutiao"
,
"version"
:
"0.0.32
2
"
,
"version"
:
"0.0.32
3
"
,
"description"
:
"uni-app mp-toutiao"
,
"main"
:
"dist/index.js"
,
"scripts"
:
{
...
...
packages/uni-mp-weixin/dist/index.js
浏览文件 @
2462cfc0
...
...
@@ -419,8 +419,32 @@ function createObserver (name) {
}
}
function
getBehaviors
(
vueExtends
,
vueMixins
)
{
function
getBehaviors
(
vueOptions
)
{
const
vueBehaviors
=
vueOptions
[
'
behaviors
'
];
const
vueExtends
=
vueOptions
[
'
extends
'
];
const
vueMixins
=
vueOptions
[
'
mixins
'
];
let
vueProps
=
vueOptions
[
'
props
'
];
if
(
!
vueProps
)
{
vueOptions
[
'
props
'
]
=
vueProps
=
[];
}
const
behaviors
=
[];
if
(
Array
.
isArray
(
vueBehaviors
))
{
vueBehaviors
.
forEach
(
behavior
=>
{
behaviors
.
push
(
behavior
.
replace
(
'
uni://
'
,
`
${
"
wx
"
}
://`
));
if
(
behavior
===
'
uni://form-field
'
)
{
if
(
Array
.
isArray
(
vueProps
))
{
vueProps
.
push
(
'
name
'
);
vueProps
.
push
(
'
value
'
);
}
else
{
vueProps
[
'
name
'
]
=
String
;
vueProps
[
'
value
'
]
=
null
;
}
}
});
}
if
(
isPlainObject
(
vueExtends
)
&&
vueExtends
.
props
)
{
behaviors
.
push
(
Behavior
({
...
...
@@ -503,6 +527,11 @@ function getProperties (props, isBehavior = false, file = '') {
}
function
wrapper$1
(
event
)
{
// TODO 又得兼容 mpvue 的 mp 对象
try
{
event
.
mp
=
JSON
.
parse
(
JSON
.
stringify
(
event
));
}
catch
(
e
)
{}
event
.
stopPropagation
=
noop
;
event
.
preventDefault
=
noop
;
...
...
@@ -512,9 +541,6 @@ function wrapper$1 (event) {
event
.
detail
=
{};
}
// TODO 又得兼容 mpvue 的 mp 对象
event
.
mp
=
event
;
if
(
isPlainObject
(
event
.
detail
))
{
event
.
target
=
Object
.
assign
({},
event
.
target
,
event
.
detail
);
}
...
...
@@ -907,9 +933,9 @@ function initVm$2 (VueComponent) {
function
createComponent
(
vueOptions
)
{
vueOptions
=
vueOptions
.
default
||
vueOptions
;
const
properties
=
getProperties
(
vueOptions
.
props
,
false
,
vueOptions
.
__file
);
const
behaviors
=
getBehaviors
(
vueOptions
);
const
behaviors
=
getBehaviors
(
vueOptions
[
'
extends
'
],
vueOptions
[
'
mixins
'
]
);
const
properties
=
getProperties
(
vueOptions
.
props
,
false
,
vueOptions
.
__file
);
const
VueComponent
=
Vue
.
extend
(
vueOptions
);
...
...
packages/uni-mp-weixin/package.json
浏览文件 @
2462cfc0
{
"name"
:
"@dcloudio/uni-mp-weixin"
,
"version"
:
"0.0.94
6
"
,
"version"
:
"0.0.94
7
"
,
"description"
:
"uni-app mp-weixin"
,
"main"
:
"dist/index.js"
,
"scripts"
:
{
...
...
src/core/runtime/wrapper/create-component.js
浏览文件 @
2462cfc0
...
...
@@ -43,9 +43,9 @@ function initVm (VueComponent) {
export
function
createComponent
(
vueOptions
)
{
vueOptions
=
vueOptions
.
default
||
vueOptions
const
properties
=
getProperties
(
vueOptions
.
props
,
false
,
vueOptions
.
__file
)
const
behaviors
=
getBehaviors
(
vueOptions
)
const
behaviors
=
getBehaviors
(
vueOptions
[
'
extends
'
],
vueOptions
[
'
mixins
'
]
)
const
properties
=
getProperties
(
vueOptions
.
props
,
false
,
vueOptions
.
__file
)
const
VueComponent
=
Vue
.
extend
(
vueOptions
)
...
...
src/core/runtime/wrapper/util.js
浏览文件 @
2462cfc0
...
...
@@ -64,8 +64,32 @@ function createObserver (name) {
}
}
export
function
getBehaviors
(
vueExtends
,
vueMixins
)
{
export
function
getBehaviors
(
vueOptions
)
{
const
vueBehaviors
=
vueOptions
[
'
behaviors
'
]
const
vueExtends
=
vueOptions
[
'
extends
'
]
const
vueMixins
=
vueOptions
[
'
mixins
'
]
let
vueProps
=
vueOptions
[
'
props
'
]
if
(
!
vueProps
)
{
vueOptions
[
'
props
'
]
=
vueProps
=
[]
}
const
behaviors
=
[]
if
(
Array
.
isArray
(
vueBehaviors
))
{
vueBehaviors
.
forEach
(
behavior
=>
{
behaviors
.
push
(
behavior
.
replace
(
'
uni://
'
,
`
${
__PLATFORM_PREFIX__
}
://`
))
if
(
behavior
===
'
uni://form-field
'
)
{
if
(
Array
.
isArray
(
vueProps
))
{
vueProps
.
push
(
'
name
'
)
vueProps
.
push
(
'
value
'
)
}
else
{
vueProps
[
'
name
'
]
=
String
vueProps
[
'
value
'
]
=
null
}
}
})
}
if
(
isPlainObject
(
vueExtends
)
&&
vueExtends
.
props
)
{
behaviors
.
push
(
Behavior
({
...
...
@@ -164,6 +188,11 @@ export function getProperties (props, isBehavior = false, file = '') {
}
function
wrapper
(
event
)
{
// TODO 又得兼容 mpvue 的 mp 对象
try
{
event
.
mp
=
JSON
.
parse
(
JSON
.
stringify
(
event
))
}
catch
(
e
)
{}
event
.
stopPropagation
=
noop
event
.
preventDefault
=
noop
...
...
@@ -183,9 +212,6 @@ function wrapper (event) {
}
}
// TODO 又得兼容 mpvue 的 mp 对象
event
.
mp
=
event
if
(
isPlainObject
(
event
.
detail
))
{
event
.
target
=
Object
.
assign
({},
event
.
target
,
event
.
detail
)
}
...
...
src/core/view/plugins/behaviors/form-field.js
0 → 100644
浏览文件 @
2462cfc0
/**
* uni://form-field
*/
import
{
hasOwn
}
from
'
uni-shared
'
import
{
emitter
}
from
'
uni-mixins
'
function
created
()
{
this
.
$dispatch
(
'
Form
'
,
'
uni-form-group-update
'
,
{
type
:
'
add
'
,
vm
:
this
})
}
function
beforeDestroy
()
{
this
.
$dispatch
(
'
Form
'
,
'
uni-form-group-update
'
,
{
type
:
'
remove
'
,
vm
:
this
})
}
export
default
{
name
:
'
uni://form-field
'
,
init
(
options
,
vm
)
{
if
(
!
vm
.
constructor
.
options
.
props
.
name
||
!
vm
.
constructor
.
options
.
props
.
value
)
{
// 未初始化 props
if
(
!
vm
.
constructor
.
options
.
props
.
name
)
{
vm
.
constructor
.
options
.
props
.
name
=
options
.
props
.
name
=
{
type
:
String
}
}
if
(
!
vm
.
constructor
.
options
.
props
.
value
)
{
vm
.
constructor
.
options
.
props
.
value
=
options
.
props
.
value
=
{
type
:
null
}
}
}
if
(
!
options
.
propsData
)
{
options
.
propsData
=
{}
}
const
$vnode
=
vm
.
$vnode
if
(
$vnode
&&
$vnode
.
data
&&
$vnode
.
data
.
attrs
)
{
if
(
hasOwn
(
$vnode
.
data
.
attrs
,
'
name
'
))
{
options
.
propsData
.
name
=
$vnode
.
data
.
attrs
.
name
}
if
(
hasOwn
(
$vnode
.
data
.
attrs
,
'
value
'
))
{
options
.
propsData
.
value
=
$vnode
.
data
.
attrs
.
value
}
}
if
(
!
vm
.
constructor
.
options
.
methods
||
!
vm
.
constructor
.
options
.
methods
.
_getFormData
)
{
// 未初始化 methods
if
(
!
vm
.
constructor
.
options
.
methods
)
{
vm
.
constructor
.
options
.
methods
=
{}
}
if
(
!
options
.
methods
)
{
options
.
methods
=
{}
}
const
formMethods
=
{
_getFormData
()
{
return
this
.
name
?
{
key
:
this
.
name
,
value
:
this
.
value
}
:
{}
},
_resetFormData
()
{
this
.
value
=
''
}
}
Object
.
assign
(
vm
.
constructor
.
options
.
methods
,
formMethods
)
Object
.
assign
(
options
.
methods
,
formMethods
)
// add $dispatch
Object
.
assign
(
vm
.
constructor
.
options
.
methods
,
emitter
.
methods
)
Object
.
assign
(
options
.
methods
,
emitter
.
methods
)
const
createdHooks
=
options
[
'
created
'
]
vm
.
constructor
.
options
[
'
created
'
]
=
options
[
'
created
'
]
=
createdHooks
?
[].
concat
(
created
,
createdHooks
)
:
[
created
]
const
beforeDestroyHooks
=
options
[
'
beforeDestroy
'
]
vm
.
constructor
.
options
[
'
beforeDestroy
'
]
=
options
[
'
beforeDestroy
'
]
=
beforeDestroyHooks
?
[].
concat
(
beforeDestroy
,
beforeDestroyHooks
)
:
[
beforeDestroy
]
}
}
}
src/core/view/plugins/behaviors/index.js
0 → 100644
浏览文件 @
2462cfc0
import
formField
from
'
./form-field
'
const
behaviors
=
{
[
formField
.
name
]:
formField
}
export
default
function
initBehaviors
(
options
,
vm
)
{
options
.
behaviors
.
forEach
(
name
=>
{
const
behavior
=
behaviors
[
name
]
behavior
&&
behavior
.
init
(
options
,
vm
)
})
}
src/core/view/plugins/index.js
浏览文件 @
2462cfc0
...
...
@@ -7,6 +7,8 @@ import {
processEvent
}
from
'
./events
'
import
initBehaviors
from
'
./behaviors
'
function
pageMounted
()
{
// 通知 Service,View 层已 ready
UniViewJSBridge
.
publishHandler
(
'
onPageReady
'
,
{},
this
.
$page
.
id
)
...
...
@@ -34,13 +36,18 @@ export default {
}
}
$event
=
processEvent
.
call
(
this
,
$event
.
type
,
$event
,
{},
target
||
$event
.
target
,
$event
.
currentTarget
)
}
}
return
$event
}
Vue
.
mixin
({
beforeCreate
()
{
const
options
=
this
.
$options
if
(
options
.
behaviors
&&
options
.
behaviors
.
length
)
{
initBehaviors
(
options
,
this
)
}
if
(
isPage
(
this
))
{
options
.
mounted
=
options
.
mounted
?
[].
concat
(
pageMounted
,
options
.
mounted
)
:
[
pageMounted
]
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录