Optimize form validation
This commit is contained in:
parent
0d005707bd
commit
a4d4dcfa9c
|
|
@ -18,6 +18,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:placeholder="$t('auth.captcha')"
|
:placeholder="$t('auth.captcha')"
|
||||||
|
required
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
type="email"
|
type="email"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:placeholder="$t('auth.email')"
|
:placeholder="$t('auth.email')"
|
||||||
|
required
|
||||||
>
|
>
|
||||||
<span class="glyphicon glyphicon-envelope form-control-feedback" />
|
<span class="glyphicon glyphicon-envelope form-control-feedback" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -14,7 +15,6 @@
|
||||||
<captcha ref="captcha" />
|
<captcha ref="captcha" />
|
||||||
|
|
||||||
<div class="callout callout-success" :class="{ hide: !successMsg }">{{ successMsg }}</div>
|
<div class="callout callout-success" :class="{ hide: !successMsg }">{{ successMsg }}</div>
|
||||||
<div class="callout callout-info" :class="{ hide: !infoMsg }">{{ infoMsg }}</div>
|
|
||||||
<div class="callout callout-warning" :class="{ hide: !warningMsg }">{{ warningMsg }}</div>
|
<div class="callout callout-warning" :class="{ hide: !warningMsg }">{{ warningMsg }}</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
@ -59,7 +59,6 @@ export default {
|
||||||
data: () => ({
|
data: () => ({
|
||||||
email: '',
|
email: '',
|
||||||
successMsg: '',
|
successMsg: '',
|
||||||
infoMsg: '',
|
|
||||||
warningMsg: '',
|
warningMsg: '',
|
||||||
pending: false,
|
pending: false,
|
||||||
}),
|
}),
|
||||||
|
|
@ -67,30 +66,16 @@ export default {
|
||||||
async submit() {
|
async submit() {
|
||||||
const { email } = this
|
const { email } = this
|
||||||
|
|
||||||
if (!email) {
|
|
||||||
this.infoMsg = this.$t('auth.emptyEmail')
|
|
||||||
this.$refs.email.focus()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!/\S+@\S+\.\S+/.test(email)) {
|
|
||||||
this.infoMsg = this.$t('auth.invalidEmail')
|
|
||||||
this.$refs.email.focus()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pending = true
|
this.pending = true
|
||||||
const { code, message } = await this.$http.post(
|
const { code, message } = await this.$http.post(
|
||||||
'/auth/forgot',
|
'/auth/forgot',
|
||||||
{ email, captcha: await this.$refs.captcha.execute() }
|
{ email, captcha: await this.$refs.captcha.execute() }
|
||||||
)
|
)
|
||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
this.infoMsg = ''
|
|
||||||
this.warningMsg = ''
|
this.warningMsg = ''
|
||||||
this.successMsg = message
|
this.successMsg = message
|
||||||
this.pending = false
|
this.pending = false
|
||||||
} else {
|
} else {
|
||||||
this.infoMsg = ''
|
|
||||||
this.warningMsg = message
|
this.warningMsg = message
|
||||||
this.pending = false
|
this.pending = false
|
||||||
this.$refs.captcha.refresh()
|
this.$refs.captcha.refresh()
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
v-model="identification"
|
v-model="identification"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:placeholder="$t('auth.identification')"
|
:placeholder="$t('auth.identification')"
|
||||||
|
required
|
||||||
>
|
>
|
||||||
<span class="glyphicon glyphicon-envelope form-control-feedback" />
|
<span class="glyphicon glyphicon-envelope form-control-feedback" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -16,13 +17,13 @@
|
||||||
type="password"
|
type="password"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:placeholder="$t('auth.password')"
|
:placeholder="$t('auth.password')"
|
||||||
|
required
|
||||||
>
|
>
|
||||||
<span class="glyphicon glyphicon-lock form-control-feedback" />
|
<span class="glyphicon glyphicon-lock form-control-feedback" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<captcha v-if="tooManyFails" ref="captcha" />
|
<captcha v-if="tooManyFails" ref="captcha" />
|
||||||
|
|
||||||
<div class="callout callout-info" :class="{ hide: !infoMsg }">{{ infoMsg }}</div>
|
|
||||||
<div class="callout callout-warning" :class="{ hide: !warningMsg }">{{ warningMsg }}</div>
|
<div class="callout callout-warning" :class="{ hide: !warningMsg }">{{ warningMsg }}</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
@ -78,7 +79,6 @@ export default {
|
||||||
tooManyFails: blessing.extra.tooManyFails,
|
tooManyFails: blessing.extra.tooManyFails,
|
||||||
recaptcha: blessing.extra.recaptcha,
|
recaptcha: blessing.extra.recaptcha,
|
||||||
invisible: blessing.extra.invisible,
|
invisible: blessing.extra.invisible,
|
||||||
infoMsg: '',
|
|
||||||
warningMsg: '',
|
warningMsg: '',
|
||||||
pending: false,
|
pending: false,
|
||||||
}
|
}
|
||||||
|
|
@ -89,18 +89,6 @@ export default {
|
||||||
identification, password, remember,
|
identification, password, remember,
|
||||||
} = this
|
} = this
|
||||||
|
|
||||||
if (!identification) {
|
|
||||||
this.infoMsg = this.$t('auth.emptyIdentification')
|
|
||||||
this.$refs.identification.focus()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!password) {
|
|
||||||
this.infoMsg = this.$t('auth.emptyPassword')
|
|
||||||
this.$refs.password.focus()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pending = true
|
this.pending = true
|
||||||
const {
|
const {
|
||||||
code, message, data: { login_fails: loginFails } = { login_fails: 0 },
|
code, message, data: { login_fails: loginFails } = { login_fails: 0 },
|
||||||
|
|
@ -131,7 +119,6 @@ export default {
|
||||||
}
|
}
|
||||||
this.tooManyFails = true
|
this.tooManyFails = true
|
||||||
}
|
}
|
||||||
this.infoMsg = ''
|
|
||||||
this.warningMsg = message
|
this.warningMsg = message
|
||||||
this.pending = false
|
this.pending = false
|
||||||
this.$refs.captcha.refresh()
|
this.$refs.captcha.refresh()
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
type="email"
|
type="email"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:placeholder="$t('auth.email')"
|
:placeholder="$t('auth.email')"
|
||||||
|
required
|
||||||
>
|
>
|
||||||
<span class="glyphicon glyphicon-envelope form-control-feedback" />
|
<span class="glyphicon glyphicon-envelope form-control-feedback" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -17,6 +18,9 @@
|
||||||
type="password"
|
type="password"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:placeholder="$t('auth.password')"
|
:placeholder="$t('auth.password')"
|
||||||
|
required
|
||||||
|
minlength="8"
|
||||||
|
maxlength="32"
|
||||||
>
|
>
|
||||||
<span class="glyphicon glyphicon-lock form-control-feedback" />
|
<span class="glyphicon glyphicon-lock form-control-feedback" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -27,6 +31,9 @@
|
||||||
type="password"
|
type="password"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:placeholder="$t('auth.repeat-pwd')"
|
:placeholder="$t('auth.repeat-pwd')"
|
||||||
|
required
|
||||||
|
minlength="8"
|
||||||
|
maxlength="32"
|
||||||
>
|
>
|
||||||
<span class="glyphicon glyphicon-log-in form-control-feedback" />
|
<span class="glyphicon glyphicon-log-in form-control-feedback" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -44,6 +51,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:placeholder="$t('auth.player-name')"
|
:placeholder="$t('auth.player-name')"
|
||||||
|
required
|
||||||
>
|
>
|
||||||
<span class="glyphicon glyphicon-pencil form-control-feedback" />
|
<span class="glyphicon glyphicon-pencil form-control-feedback" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -60,6 +68,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:placeholder="$t('auth.nickname')"
|
:placeholder="$t('auth.nickname')"
|
||||||
|
required
|
||||||
>
|
>
|
||||||
<span class="glyphicon glyphicon-pencil form-control-feedback" />
|
<span class="glyphicon glyphicon-pencil form-control-feedback" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -125,48 +134,12 @@ export default {
|
||||||
email, password, confirm, playerName, nickname,
|
email, password, confirm, playerName, nickname,
|
||||||
} = this
|
} = this
|
||||||
|
|
||||||
if (!email) {
|
|
||||||
this.infoMsg = this.$t('auth.emptyEmail')
|
|
||||||
this.$refs.email.focus()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!/\S+@\S+\.\S+/.test(email)) {
|
|
||||||
this.infoMsg = this.$t('auth.invalidEmail')
|
|
||||||
this.$refs.email.focus()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!password) {
|
|
||||||
this.infoMsg = this.$t('auth.emptyPassword')
|
|
||||||
this.$refs.password.focus()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password.length < 8 || password.length > 32) {
|
|
||||||
this.infoMsg = this.$t('auth.invalidPassword')
|
|
||||||
this.$refs.password.focus()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password !== confirm) {
|
if (password !== confirm) {
|
||||||
this.infoMsg = this.$t('auth.invalidConfirmPwd')
|
this.infoMsg = this.$t('auth.invalidConfirmPwd')
|
||||||
this.$refs.confirm.focus()
|
this.$refs.confirm.focus()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.requirePlayer && !playerName) {
|
|
||||||
this.infoMsg = this.$t('auth.emptyPlayerName')
|
|
||||||
this.$refs.playerName.focus()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.requirePlayer && !nickname) {
|
|
||||||
this.infoMsg = this.$t('auth.emptyNickname')
|
|
||||||
this.$refs.nickname.focus()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pending = true
|
this.pending = true
|
||||||
const { code, message } = await this.$http.post(
|
const { code, message } = await this.$http.post(
|
||||||
'/auth/register',
|
'/auth/register',
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@
|
||||||
type="password"
|
type="password"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:placeholder="$t('auth.password')"
|
:placeholder="$t('auth.password')"
|
||||||
|
required
|
||||||
|
minlength="8"
|
||||||
|
maxlength="32"
|
||||||
>
|
>
|
||||||
<span class="glyphicon glyphicon-lock form-control-feedback" />
|
<span class="glyphicon glyphicon-lock form-control-feedback" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -17,6 +20,9 @@
|
||||||
type="password"
|
type="password"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:placeholder="$t('auth.repeat-pwd')"
|
:placeholder="$t('auth.repeat-pwd')"
|
||||||
|
required
|
||||||
|
minlength="8"
|
||||||
|
maxlength="32"
|
||||||
>
|
>
|
||||||
<span class="glyphicon glyphicon-log-in form-control-feedback" />
|
<span class="glyphicon glyphicon-log-in form-control-feedback" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -65,18 +71,6 @@ export default {
|
||||||
async reset() {
|
async reset() {
|
||||||
const { password, confirm } = this
|
const { password, confirm } = this
|
||||||
|
|
||||||
if (!password) {
|
|
||||||
this.infoMsg = this.$t('auth.emptyPassword')
|
|
||||||
this.$refs.password.focus()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password.length < 8 || password.length > 32) {
|
|
||||||
this.infoMsg = this.$t('auth.invalidPassword')
|
|
||||||
this.$refs.password.focus()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password !== confirm) {
|
if (password !== confirm) {
|
||||||
this.infoMsg = this.$t('auth.invalidConfirmPwd')
|
this.infoMsg = this.$t('auth.invalidConfirmPwd')
|
||||||
this.$refs.confirm.focus()
|
this.$refs.confirm.focus()
|
||||||
|
|
|
||||||
|
|
@ -20,19 +20,9 @@ test('submit forgot form', async () => {
|
||||||
.mockResolvedValueOnce({ code: 0, message: 'ok' })
|
.mockResolvedValueOnce({ code: 0, message: 'ok' })
|
||||||
const wrapper = mount(Forgot, { stubs: { Captcha } })
|
const wrapper = mount(Forgot, { stubs: { Captcha } })
|
||||||
const form = wrapper.find('form')
|
const form = wrapper.find('form')
|
||||||
const info = wrapper.find('.callout-info')
|
|
||||||
const warning = wrapper.find('.callout-warning')
|
const warning = wrapper.find('.callout-warning')
|
||||||
const success = wrapper.find('.callout-success')
|
const success = wrapper.find('.callout-success')
|
||||||
|
|
||||||
form.trigger('submit')
|
|
||||||
expect(Vue.prototype.$http.post).not.toBeCalled()
|
|
||||||
expect(info.text()).toBe('auth.emptyEmail')
|
|
||||||
|
|
||||||
wrapper.find('[type="email"]').setValue('a')
|
|
||||||
form.trigger('submit')
|
|
||||||
expect(Vue.prototype.$http.post).not.toBeCalled()
|
|
||||||
expect(info.text()).toBe('auth.invalidEmail')
|
|
||||||
|
|
||||||
wrapper.find('[type="email"]').setValue('a@b.c')
|
wrapper.find('[type="email"]').setValue('a@b.c')
|
||||||
form.trigger('submit')
|
form.trigger('submit')
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
|
|
|
||||||
|
|
@ -28,18 +28,9 @@ test('login', async () => {
|
||||||
.mockResolvedValueOnce({ code: 0, message: 'ok' })
|
.mockResolvedValueOnce({ code: 0, message: 'ok' })
|
||||||
const wrapper = mount(Login, { stubs: { Captcha } })
|
const wrapper = mount(Login, { stubs: { Captcha } })
|
||||||
const form = wrapper.find('form')
|
const form = wrapper.find('form')
|
||||||
const info = wrapper.find('.callout-info')
|
|
||||||
const warning = wrapper.find('.callout-warning')
|
const warning = wrapper.find('.callout-warning')
|
||||||
|
|
||||||
form.trigger('submit')
|
|
||||||
expect(Vue.prototype.$http.post).not.toBeCalled()
|
|
||||||
expect(info.text()).toBe('auth.emptyIdentification')
|
|
||||||
|
|
||||||
wrapper.find('input').setValue('a@b.c')
|
wrapper.find('input').setValue('a@b.c')
|
||||||
form.trigger('submit')
|
|
||||||
expect(Vue.prototype.$http.post).not.toBeCalled()
|
|
||||||
expect(info.text()).toBe('auth.emptyPassword')
|
|
||||||
|
|
||||||
wrapper.find('[type="password"]').setValue('123')
|
wrapper.find('[type="password"]').setValue('123')
|
||||||
form.trigger('submit')
|
form.trigger('submit')
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
|
||||||
|
|
@ -34,32 +34,9 @@ test('register', async () => {
|
||||||
const info = wrapper.find('.callout-info')
|
const info = wrapper.find('.callout-info')
|
||||||
const warning = wrapper.find('.callout-warning')
|
const warning = wrapper.find('.callout-warning')
|
||||||
|
|
||||||
form.trigger('submit')
|
|
||||||
expect(Vue.prototype.$http.post).not.toBeCalled()
|
|
||||||
expect(info.text()).toBe('auth.emptyEmail')
|
|
||||||
|
|
||||||
wrapper.find('[type="email"]').setValue('a')
|
|
||||||
form.trigger('submit')
|
|
||||||
expect(Vue.prototype.$http.post).not.toBeCalled()
|
|
||||||
expect(info.text()).toBe('auth.invalidEmail')
|
|
||||||
|
|
||||||
wrapper.find('[type="email"]').setValue('a@b.c')
|
wrapper.find('[type="email"]').setValue('a@b.c')
|
||||||
form.trigger('submit')
|
|
||||||
expect(Vue.prototype.$http.post).not.toBeCalled()
|
|
||||||
expect(info.text()).toBe('auth.emptyPassword')
|
|
||||||
|
|
||||||
wrapper.findAll('[type="password"]').at(0)
|
|
||||||
.setValue('123456')
|
|
||||||
form.trigger('submit')
|
|
||||||
expect(Vue.prototype.$http.post).not.toBeCalled()
|
|
||||||
expect(info.text()).toBe('auth.invalidPassword')
|
|
||||||
|
|
||||||
wrapper.findAll('[type="password"]').at(0)
|
wrapper.findAll('[type="password"]').at(0)
|
||||||
.setValue('12345678')
|
.setValue('12345678')
|
||||||
form.trigger('submit')
|
|
||||||
expect(Vue.prototype.$http.post).not.toBeCalled()
|
|
||||||
expect(info.text()).toBe('auth.invalidConfirmPwd')
|
|
||||||
|
|
||||||
wrapper.findAll('[type="password"]').at(1)
|
wrapper.findAll('[type="password"]').at(1)
|
||||||
.setValue('123456')
|
.setValue('123456')
|
||||||
form.trigger('submit')
|
form.trigger('submit')
|
||||||
|
|
@ -68,10 +45,6 @@ test('register', async () => {
|
||||||
|
|
||||||
wrapper.findAll('[type="password"]').at(1)
|
wrapper.findAll('[type="password"]').at(1)
|
||||||
.setValue('12345678')
|
.setValue('12345678')
|
||||||
form.trigger('submit')
|
|
||||||
expect(Vue.prototype.$http.post).not.toBeCalled()
|
|
||||||
expect(info.text()).toBe('auth.emptyNickname')
|
|
||||||
|
|
||||||
wrapper.findAll('[type="text"]').at(0)
|
wrapper.findAll('[type="text"]').at(0)
|
||||||
.setValue('abc')
|
.setValue('abc')
|
||||||
form.trigger('submit')
|
form.trigger('submit')
|
||||||
|
|
@ -99,17 +72,11 @@ test('register with player name', async () => {
|
||||||
Vue.prototype.$http.post.mockResolvedValue({ code: 0, message: 'ok' })
|
Vue.prototype.$http.post.mockResolvedValue({ code: 0, message: 'ok' })
|
||||||
const wrapper = mount(Register, { stubs: { Captcha } })
|
const wrapper = mount(Register, { stubs: { Captcha } })
|
||||||
const form = wrapper.find('form')
|
const form = wrapper.find('form')
|
||||||
const info = wrapper.find('.callout-info')
|
|
||||||
wrapper.find('[type="email"]').setValue('a@b.c')
|
wrapper.find('[type="email"]').setValue('a@b.c')
|
||||||
wrapper.findAll('[type="password"]').at(0)
|
wrapper.findAll('[type="password"]').at(0)
|
||||||
.setValue('12345678')
|
.setValue('12345678')
|
||||||
wrapper.findAll('[type="password"]').at(1)
|
wrapper.findAll('[type="password"]').at(1)
|
||||||
.setValue('12345678')
|
.setValue('12345678')
|
||||||
|
|
||||||
form.trigger('submit')
|
|
||||||
expect(Vue.prototype.$http.post).not.toBeCalled()
|
|
||||||
expect(info.text()).toBe('auth.emptyPlayerName')
|
|
||||||
|
|
||||||
wrapper.findAll('[type="text"]').at(0)
|
wrapper.findAll('[type="text"]').at(0)
|
||||||
.setValue('abc')
|
.setValue('abc')
|
||||||
form.trigger('submit')
|
form.trigger('submit')
|
||||||
|
|
|
||||||
|
|
@ -15,22 +15,8 @@ test('reset password', async () => {
|
||||||
const info = wrapper.find('.callout-info')
|
const info = wrapper.find('.callout-info')
|
||||||
const warning = wrapper.find('.callout-warning')
|
const warning = wrapper.find('.callout-warning')
|
||||||
|
|
||||||
form.trigger('submit')
|
|
||||||
expect(Vue.prototype.$http.post).not.toBeCalled()
|
|
||||||
expect(info.text()).toBe('auth.emptyPassword')
|
|
||||||
|
|
||||||
wrapper.findAll('[type="password"]').at(0)
|
|
||||||
.setValue('123456')
|
|
||||||
form.trigger('submit')
|
|
||||||
expect(Vue.prototype.$http.post).not.toBeCalled()
|
|
||||||
expect(info.text()).toBe('auth.invalidPassword')
|
|
||||||
|
|
||||||
wrapper.findAll('[type="password"]').at(0)
|
wrapper.findAll('[type="password"]').at(0)
|
||||||
.setValue('12345678')
|
.setValue('12345678')
|
||||||
form.trigger('submit')
|
|
||||||
expect(Vue.prototype.$http.post).not.toBeCalled()
|
|
||||||
expect(info.text()).toBe('auth.invalidConfirmPwd')
|
|
||||||
|
|
||||||
wrapper.findAll('[type="password"]').at(1)
|
wrapper.findAll('[type="password"]').at(1)
|
||||||
.setValue('123456')
|
.setValue('123456')
|
||||||
form.trigger('submit')
|
form.trigger('submit')
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
- Push notifications to queue for performance.
|
- Push notifications to queue for performance.
|
||||||
- Optimized exception stack of Ajax error.
|
- Optimized exception stack of Ajax error.
|
||||||
|
- Optimized validating forms of pages like logining and registering.
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
- 发送通知时将任务推送到队列
|
- 发送通知时将任务推送到队列
|
||||||
- 优化 Ajax 中的错误显示
|
- 优化 Ajax 中的错误显示
|
||||||
|
- 优化登录、注册等页面的表单校验
|
||||||
|
|
||||||
## 修复
|
## 修复
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user