Fix for dropdown menu in admin panel

This commit is contained in:
Pig Fang 2018-09-07 22:26:34 +08:00
parent f20e2bcfe3
commit 544b30c830
7 changed files with 140 additions and 59 deletions

View File

@ -14,13 +14,28 @@
styleClass="vgt-table striped"
>
<template slot="table-row" slot-scope="props">
<span v-if="props.column.field === 'uid'">
<span v-if="props.column.field === 'player_name'">
{{ props.formattedRow[props.column.field] }}
<a @click="changeName(props.row)" :title="$t('admin.changePlayerName')" data-test="name">
<i class="fas fa-edit btn-edit"></i>
</a>
</span>
<span v-else-if="props.column.field === 'uid'">
<a
:href="`${baseUrl}/admin/users?uid=${props.row.uid}`"
:title="$t('admin.inspectHisOwner')"
data-toggle="tooltip"
data-placement="right"
>{{ props.formattedRow[props.column.field] }}</a>
<a @click="changeOwner(props.row)" :title="$t('admin.changeOwner')" data-test="owner">
<i class="fas fa-edit btn-edit"></i>
</a>
</span>
<span v-else-if="props.column.field === 'preference'">
{{ props.formattedRow[props.column.field] }}
<a @click="togglePreference(props.row)" :title="$t('admin.changePreference')" data-test="preference">
<i class="fas fa-edit btn-edit"></i>
</a>
</span>
<span v-else-if="props.column.field === 'preview'">
<a
@ -43,32 +58,13 @@
</a>
</span>
<span v-else-if="props.column.field === 'operations'">
<div class="btn-group">
<button
class="btn btn-default dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>{{ $t('admin.changeTexture') }} <span class="caret"></span></button>
<ul class="dropdown-menu" data-test="change-texture">
<li><a @click="changeTexture(props.row, 'steve')" href="#steve">Steve</a></li>
<li><a @click="changeTexture(props.row, 'alex')" href="#alex">Alex</a></li>
<li><a @click="changeTexture(props.row, 'cape')" v-t="'general.cape'" href="#cape"></a></li>
</ul>
</div>
<div class="btn-group">
<button
class="btn btn-default dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>{{ $t('general.more') }} <span class="caret"></span></button>
<ul class="dropdown-menu" data-test="operations">
<li><a @click="changeName(props.row)" v-t="'admin.changePlayerName'" href="#"></a></li>
<li><a @click="togglePreference(props.row)" v-t="'admin.changePreference'" href="#"></a></li>
<li><a @click="changeOwner(props.row)" v-t="'admin.changeOwner'" href="#"></a></li>
</ul>
</div>
<button
class="btn btn-default"
data-toggle="modal"
data-target="#modal-change-texture"
v-t="'admin.changeTexture'"
@click="textureChanges.originalIndex = props.row.originalIndex"
></button>
<button
class="btn btn-danger"
v-t="'admin.deletePlayer'"
@ -78,6 +74,55 @@
<span v-else v-text="props.formattedRow[props.column.field]" />
</template>
</vue-good-table>
<div
id="modal-change-texture"
class="modal fade"
tabindex="-1"
role="dialog"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" v-t="'admin.changeTexture'"></h4>
</div>
<div class="modal-body">
<div class="form-group">
<label v-t="'admin.textureType'" />
<select class="form-control" v-model="textureChanges.model">
<option value="steve">Steve</option>
<option value="alex">Alex</option>
<option value="cape" v-t="'general.cape'"></option>
</select>
</div>
<div class="form-group">
<label>TID</label>
<input
class="form-control"
type="text"
:placeholder="$t('admin.pidNotice')"
v-model.number="textureChanges.tid"
>
</div>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-default"
data-dismiss="modal"
v-t="'general.close'"
></button>
<a @click="changeTexture" class="btn btn-primary" v-t="'general.submit'"></a>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div>
</section>
</template>
@ -131,6 +176,11 @@ export default {
ofLabel: this.$t('vendor.datatable.of')
}
},
textureChanges: {
originalIndex: -1,
model: 'steve',
tid: '',
}
};
},
beforeMount() {
@ -162,23 +212,18 @@ export default {
this.serverParams.search = params.searchTerm;
this.fetchData();
},
async changeTexture(player, model) {
const { dismiss, value } = await swal({
text: this.$t('admin.pidNotice'),
input: 'number',
inputValue: player[`tid_${model}`]
});
if (dismiss) {
return;
}
async changeTexture() {
const player = this.players[this.textureChanges.originalIndex];
const { model, tid } = this.textureChanges;
const { errno, msg } = await this.$http.post(
'/admin/players?action=texture',
{ pid: player.pid, model, tid: value }
{ pid: player.pid, model, tid }
);
if (errno === 0) {
player[`tid_${model}`] = value;
player[`tid_${model}`] = tid;
toastr.success(msg);
$('.modal').modal('hide');
} else {
toastr.warning(msg);
}

View File

@ -14,7 +14,25 @@
styleClass="vgt-table striped"
>
<template slot="table-row" slot-scope="props">
<span v-if="props.column.field === 'players_count'">
<span v-if="props.column.field === 'email'">
{{ props.formattedRow[props.column.field] }}
<a @click="changeEmail(props.row)" :title="$t('admin.changeEmail')" data-test="email">
<i class="fas fa-edit btn-edit"></i>
</a>
</span>
<span v-else-if="props.column.field === 'nickname'">
{{ props.formattedRow[props.column.field] }}
<a @click="changeNickName(props.row)" :title="$t('admin.changeNickName')" data-test="nickname">
<i class="fas fa-edit btn-edit"></i>
</a>
</span>
<span v-else-if="props.column.field === 'score'">
{{ props.formattedRow[props.column.field] }}
<a @click="changeScore(props.row)" :title="$t('admin.changeScore')" data-test="score">
<i class="fas fa-edit btn-edit"></i>
</a>
</span>
<span v-else-if="props.column.field === 'players_count'">
<a
:href="props.row | playersLink"
:title="$t('admin.inspectHisPlayers')"
@ -28,6 +46,17 @@
<span v-else-if="props.column.field === 'verified'">
<span v-if="props.row.verified" v-t="'admin.verified'"></span>
<span v-else v-t="'admin.unverified'"></span>
<a
@click="toggleVerification(props.row)"
:title="$t('admin.toggleVerification')"
data-test="verification"
>
<i
class="fas btn-edit"
:class="{ 'fa-toggle-on': props.row.verified, 'fa-toggle-off': !props.row.verified }"
style="font-size: 18px;"
></i>
</a>
</span>
<div v-else-if="props.column.field === 'operations'">
<div class="btn-group">
@ -37,12 +66,8 @@
aria-haspopup="true"
aria-expanded="false"
>{{ $t('general.more') }} <span class="caret"></span></button>
<ul class="dropdown-menu operations-menu">
<li><a @click="changeEmail(props.row)" v-t="'admin.changeEmail'" href="#"></a></li>
<li><a @click="toggleVerification(props.row)" v-t="'admin.toggleVerification'" href="#"></a></li>
<li><a @click="changeNickName(props.row)" v-t="'admin.changeNickName'" href="#"></a></li>
<ul class="dropdown-menu operations-menu" :class="{ 'row-at-bottom': users.length - props.index < 3 }">
<li><a @click="changePassword(props.row)" v-t="'admin.changePassword'" href="#"></a></li>
<li><a @click="changeScore(props.row)" v-t="'admin.changeScore'" href="#"></a></li>
<template v-if="props.row.permission < 2">
<li class="divider"></li>
<li v-if="props.row.operations >= 2 && props.row.permission > -1">
@ -321,4 +346,8 @@ export default {
.operations-menu {
margin-left -35px
}
.row-at-bottom {
margin-top -100px
}
</style>

View File

@ -32,3 +32,8 @@ td {
color: #3c8dbc;
}
}
.btn-edit {
font-size: 12px;
color: #367fa9;
}

View File

@ -47,21 +47,20 @@ test('update tables', async () => {
});
test('change texture', async () => {
window.$ = jest.fn(() => ({ modal() {} }));
Vue.prototype.$http.get.mockResolvedValue({ data: [
{ pid: 1, tid_steve: 0 }
] });
Vue.prototype.$http.post
.mockResolvedValueOnce({ errno: 1, msg: '1' })
.mockResolvedValueOnce({ errno: 0, msg: '0' });
swal.mockResolvedValueOnce({ dismiss: 1 })
.mockResolvedValue({ value: 5 });
const wrapper = mount(Players);
await wrapper.vm.$nextTick();
const button = wrapper.find('[data-test="change-texture"] > li:nth-child(1) > a');
const button = wrapper.find('.btn-primary');
wrapper.find('.btn-default').trigger('click');
button.trigger('click');
expect(Vue.prototype.$http.post).not.toBeCalled();
wrapper.find('.modal-body').find('input').setValue('5');
button.trigger('click');
await wrapper.vm.$nextTick();
@ -73,6 +72,7 @@ test('change texture', async () => {
button.trigger('click');
await flushPromises();
expect(wrapper.text()).toContain('5');
expect(window.$).toBeCalledWith('.modal');
});
test('change player name', async () => {
@ -91,7 +91,7 @@ test('change player name', async () => {
const wrapper = mount(Players);
await wrapper.vm.$nextTick();
const button = wrapper.find('[data-test="operations"] > li:nth-child(1) > a');
const button = wrapper.find('[data-test="name"]');
button.trigger('click');
expect(Vue.prototype.$http.post).not.toBeCalled();
@ -118,7 +118,7 @@ test('toggle preference', async () => {
const wrapper = mount(Players);
await wrapper.vm.$nextTick();
const button = wrapper.find('[data-test="operations"] > li:nth-child(2) > a');
const button = wrapper.find('[data-test="preference"]');
button.trigger('click');
expect(Vue.prototype.$http.post).toBeCalledWith(
@ -147,7 +147,7 @@ test('change owner', async () => {
const wrapper = mount(Players);
await wrapper.vm.$nextTick();
const button = wrapper.find('[data-test="operations"] > li:nth-child(3) > a');
const button = wrapper.find('[data-test="owner"]');
button.trigger('click');
expect(Vue.prototype.$http.post).not.toBeCalled();

View File

@ -308,7 +308,7 @@ test('change email', async () => {
const wrapper = mount(Users);
await wrapper.vm.$nextTick();
const button = wrapper.find('.operations-menu > li:nth-child(1) > a');
const button = wrapper.find('[data-test="email"]');
button.trigger('click');
expect(Vue.prototype.$http.post).not.toBeCalled();
@ -336,7 +336,7 @@ test('toggle verification', async () => {
const wrapper = mount(Users);
await wrapper.vm.$nextTick();
const button = wrapper.find('.operations-menu > li:nth-child(2) > a');
const button = wrapper.find('[data-test="verification"');
button.trigger('click');
await wrapper.vm.$nextTick();
@ -366,7 +366,7 @@ test('change nickname', async () => {
const wrapper = mount(Users);
await wrapper.vm.$nextTick();
const button = wrapper.find('.operations-menu > li:nth-child(3) > a');
const button = wrapper.find('[data-test="nickname"]');
button.trigger('click');
expect(Vue.prototype.$http.post).not.toBeCalled();
@ -398,7 +398,7 @@ test('change password', async () => {
const wrapper = mount(Users);
await wrapper.vm.$nextTick();
const button = wrapper.find('.operations-menu > li:nth-child(4) > a');
const button = wrapper.find('.operations-menu > li:nth-child(1) > a');
button.trigger('click');
expect(Vue.prototype.$http.post).not.toBeCalled();
@ -430,7 +430,7 @@ test('change score', async () => {
const wrapper = mount(Users);
await wrapper.vm.$nextTick();
const button = wrapper.find('.operations-menu > li:nth-child(5) > a');
const button = wrapper.find('[data-test="score"]');
button.trigger('click');
expect(Vue.prototype.$http.post).not.toBeCalled();
@ -458,7 +458,7 @@ test('toggle admin', async () => {
const wrapper = mount(Users);
await wrapper.vm.$nextTick();
const button = wrapper.find('.operations-menu > li:nth-child(7) > a');
const button = wrapper.find('.operations-menu > li:nth-child(3) > a');
button.trigger('click');
await wrapper.vm.$nextTick();
@ -487,7 +487,7 @@ test('toggle ban', async () => {
const wrapper = mount(Users);
await wrapper.vm.$nextTick();
const button = wrapper.find('.operations-menu > li:nth-child(8) > a');
const button = wrapper.find('.operations-menu > li:nth-child(4) > a');
button.trigger('click');
await wrapper.vm.$nextTick();

View File

@ -257,6 +257,7 @@ admin:
changePlayerName: Change Player Name
changePreference: Toggle Preference
changeOwner: Change Owner
textureType: Texture Type
deletePlayer: Delete
changePlayerOwner: 'Please enter the id of user which this player should be transferred to:'
deletePlayerNotice: Are you sure to delete this player? It' permanent.

View File

@ -255,6 +255,7 @@ admin:
changeOwner: 更换角色拥有者
deletePlayer: 删除
changePlayerOwner: 请输入此角色要让渡至的用户 UID
textureType: 材质类型
deletePlayerNotice: 真的要删除此角色吗?此操作不可恢复
targetUser: '目标用户::nickname'
noSuchUser: 没有这个用户哦~