support searching players when applying textures

This commit is contained in:
Pig Fang 2020-01-13 12:01:13 +08:00
parent 9e792b9c3a
commit cf6f1aa547
4 changed files with 48 additions and 35 deletions

View File

@ -5,21 +5,25 @@
:title="$t('user.closet.use-as.title')" :title="$t('user.closet.use-as.title')"
:ok-button-text="$t('general.submit')" :ok-button-text="$t('general.submit')"
flex-footer flex-footer
center
> >
<template v-if="players.length !== 0"> <template v-if="players.length !== 0">
<div v-for="player in players" :key="player.pid" class="player-item"> <div class="form-group">
<label class="model-label" :for="player.pid">
<input <input
v-model="selected" v-model="search"
type="radio" type="text"
name="player" class="form-control"
:value="player.pid" :placeholder="$t('user.typeToSearch')"
> >
<img :src="avatarUrl(player)" width="35" height="35">
<span>{{ player.name }}</span>
</label>
</div> </div>
<button
v-for="player in filteredPlayers"
:key="player.pid"
class="btn btn-block btn-outline-info text-left"
@click="submit(player.pid)"
>
<img :src="avatarUrl(player)" width="45" height="45">&nbsp;
<span>{{ player.name }}</span>
</button>
</template> </template>
<p v-else v-t="'user.closet.use-as.empty'" /> <p v-else v-t="'user.closet.use-as.empty'" />
<template #footer> <template #footer>
@ -31,8 +35,8 @@
class="btn btn-default" class="btn btn-default"
href="#" href="#"
/> />
<button class="btn btn-primary" data-test="submit" @click="submit"> <button class="btn btn-default" data-dismiss="modal">
{{ $t('general.submit') }} {{ $t('general.cancel') }}
</button> </button>
</template> </template>
</modal> </modal>
@ -59,26 +63,29 @@ export default {
data() { data() {
return { return {
players: [], players: [],
selected: 0, search: '',
} }
}, },
computed: {
filteredPlayers() {
return this.players.filter(player => player.name.includes(this.search))
},
},
mounted() {
this.fetchList()
},
methods: { methods: {
async fetchList() { async fetchList() {
this.players = (await this.$http.get('/user/player/list')).data this.players = (await this.$http.get('/user/player/list')).data
}, },
async submit() { async submit(selected) {
if (!this.selected) {
toast.info(this.$t('user.emptySelectedPlayer'))
return
}
if (!this.skin && !this.cape) { if (!this.skin && !this.cape) {
toast.info(this.$t('user.emptySelectedTexture')) toast.info(this.$t('user.emptySelectedTexture'))
return return
} }
const { code, message } = await this.$http.post( const { code, message } = await this.$http.post(
`/user/player/set/${this.selected}`, `/user/player/set/${selected}`,
{ {
skin: this.skin || undefined, skin: this.skin || undefined,
cape: this.cape || undefined, cape: this.cape || undefined,
@ -92,13 +99,8 @@ export default {
} }
}, },
avatarUrl(player) { avatarUrl(player) {
return `${blessing.base_url}/avatar/${player.tid_skin}?3d&size=35` return `${blessing.base_url}/avatar/${player.tid_skin}?3d&size=45`
}, },
}, },
} }
</script> </script>
<style lang="stylus">
.player-item:not(:nth-child(1))
margin-top 10px
</style>

View File

@ -8,16 +8,13 @@ import ApplyToPlayerDialog from '@/components/ApplyToPlayerDialog.vue'
jest.mock('@/scripts/notify') jest.mock('@/scripts/notify')
test('submit applying texture', async () => { test('submit applying texture', async () => {
Vue.prototype.$http.get.mockResolvedValue({ data: [{ pid: 1 }] }) Vue.prototype.$http.get.mockResolvedValue({ data: [{ pid: 1, name: 'a' }] })
Vue.prototype.$http.post.mockResolvedValueOnce({ code: 1 }) Vue.prototype.$http.post.mockResolvedValueOnce({ code: 1 })
.mockResolvedValue({ code: 0, message: 'ok' }) .mockResolvedValue({ code: 0, message: 'ok' })
const wrapper = mount(ApplyToPlayerDialog) const wrapper = mount(ApplyToPlayerDialog)
const button = wrapper.find('[data-test=submit]') await flushPromises()
const button = wrapper.find('.btn-outline-info')
button.trigger('click')
expect(toast.info).toBeCalledWith('user.emptySelectedPlayer')
wrapper.setData({ selected: 1 })
button.trigger('click') button.trigger('click')
expect(toast.info).toBeCalledWith('user.emptySelectedTexture') expect(toast.info).toBeCalledWith('user.emptySelectedTexture')
@ -48,5 +45,17 @@ test('compute avatar URL', () => {
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
const wrapper = mount<Vue & { avatarUrl(player: { tid_skin: number }): string }>(ApplyToPlayerDialog) const wrapper = mount<Vue & { avatarUrl(player: { tid_skin: number }): string }>(ApplyToPlayerDialog)
const { avatarUrl } = wrapper.vm const { avatarUrl } = wrapper.vm
expect(avatarUrl({ tid_skin: 1 })).toBe('/avatar/1?3d&size=35') expect(avatarUrl({ tid_skin: 1 })).toBe('/avatar/1?3d&size=45')
})
test('search players', async () => {
Vue.prototype.$http.get.mockResolvedValue({ data: [{ pid: 1, name: 'abc' }] })
const wrapper = mount(ApplyToPlayerDialog)
await flushPromises()
wrapper.find('input').setValue('e')
expect(wrapper.find('.btn-outline-info').exists()).toBeFalse()
wrapper.find('input').setValue('b')
expect(wrapper.find('.btn-outline-info').exists()).toBeTrue()
}) })

View File

@ -18,6 +18,7 @@
- Added login with 3rd-party services. (GitHub and Microsoft Live are supported currently.) - Added login with 3rd-party services. (GitHub and Microsoft Live are supported currently.)
- Added support of character "§" for player name. (Under CJK mode.) - Added support of character "§" for player name. (Under CJK mode.)
- New password hash algorithm: Argon2i. - New password hash algorithm: Argon2i.
- Support searching players when applying textures.
## Tweaked ## Tweaked

View File

@ -18,6 +18,7 @@
- 第三方登录(目前仅支持 GitHub 和 Microsoft Live - 第三方登录(目前仅支持 GitHub 和 Microsoft Live
- 角色名支持字符「§」需开启「CJK」模式 - 角色名支持字符「§」需开启「CJK」模式
- 新的密码哈希算法Argon2i - 新的密码哈希算法Argon2i
- 将材质应用到角色时可进行搜索
## 调整 ## 调整