blessing-skin-server/resources/assets/src/views/skinlib/Show.vue
2019-11-26 17:59:14 +08:00

393 lines
11 KiB
Vue

<template>
<div class="row">
<div class="col-md-8">
<previewer
:skin="type !== 'cape' && textureUrl"
:cape="type === 'cape' ? textureUrl : ''"
:model="type"
:init-position-z="60"
>
<template #footer>
<button
v-if="anonymous"
class="btn btn-outline-secondary"
disabled
:title="$t('skinlib.show.anonymous')"
>
{{ $t('skinlib.addToCloset') }}
</button>
<div v-else class="d-flex justify-content-between">
<div>
<button
v-if="liked"
class="btn btn-outline-success mr-2"
data-toggle="modal"
data-target="#modal-use-as"
@click="fetchPlayersList"
>
{{ $t('skinlib.apply') }}
</button>
<button
v-if="liked"
class="btn btn-outline-primary mr-2"
data-test="removeFromCloset"
@click="removeFromCloset"
>
{{ $t('skinlib.removeFromCloset') }}
</button>
<button
v-else
class="btn btn-outline-primary mr-2"
data-test="addToCloset"
@click="addToCloset"
>
{{ $t('skinlib.addToCloset') }}
</button>
<button
v-if="type !== 'cape'"
class="btn btn-outline-info mr-2"
data-test="setAsAvatar"
@click="setAsAvatar"
>
{{ $t('user.setAsAvatar') }}
</button>
<button
v-if="canBeDownloaded"
class="btn btn-outline-info mr-2"
data-test="download"
@click="download"
>
{{ $t('skinlib.show.download') }}
</button>
<button
class="btn btn-outline-info mr-2"
data-test="report"
@click="report"
>
{{ $t('skinlib.report.title') }}
</button>
</div>
<div
class="pt-2 likes"
:class="[liked ? 'text-red' : 'text-gray']"
:title="$t('skinlib.show.likes')"
>
<i class="fas fa-heart" />
<span>{{ likes }}</span>
</div>
</div>
</template>
</previewer>
</div>
<div class="col-md-4">
<div class="card card-primary">
<div class="card-header">
<h3 v-t="'skinlib.show.detail'" class="card-title" />
</div>
<div class="card-body">
<table class="table">
<tbody>
<tr>
<td v-t="'skinlib.show.name'" />
<td>
{{ name.length > 15 ? `${name.slice(0, 15)}...` : name }}
<small v-if="hasEditPermission">
<a v-t="'skinlib.show.edit'" href="#" @click="changeTextureName" />
</small>
</td>
</tr>
<tr>
<td v-t="'skinlib.show.model'" />
<td>
<span v-if="type === 'cape'">{{ $t('general.cape') }}</span>
<span v-else>{{ type }}</span>
<small v-if="hasEditPermission">
<a v-t="'skinlib.show.edit'" href="#" @click="changeModel" />
</small>
</td>
</tr>
<tr>
<td>Hash</td>
<td>
<span :title="hash">{{ hash.slice(0, 15) }}...</span>
</td>
</tr>
<tr>
<td v-t="'skinlib.show.size'" />
<td>{{ size }} KB</td>
</tr>
<tr>
<td v-t="'skinlib.show.uploader'" />
<td v-if="uploaderNickName !== null">
<a
:href="`${baseUrl}skinlib?filter=${type === 'cape' ? 'cape' : 'skin'}&uploader=${uploader}`"
>{{ uploaderNickName }}</a>
</td>
<td v-else><span v-t="'general.unexistent-user'" /></td>
</tr>
<tr>
<td v-t="'skinlib.show.upload-at'" />
<td>{{ uploadAt }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div v-if="hasEditPermission" class="card card-warning">
<div class="card-header">
<h3 v-t="'admin.operationsTitle'" class="card-title" />
</div>
<div class="card-body">
<p v-t="'skinlib.show.manage-notice'" />
</div>
<div class="card-footer">
<div class="container d-flex justify-content-between">
<button class="btn btn-warning" @click="togglePrivacy">
{{ $t(togglePrivacyText) }}
</button>
<button class="btn btn-danger" @click="deleteTexture">
{{ $t('skinlib.show.delete-texture') }}
</button>
</div>
</div>
</div>
</div>
<apply-to-player-dialog
ref="useAs"
:allow-add="false"
:skin="type !== 'cape' ? tid : 0"
:cape="type === 'cape' ? tid : 0"
/>
</div>
</template>
<script>
import setAsAvatar from '../../components/mixins/setAsAvatar'
import addClosetItem from '../../components/mixins/addClosetItem'
import removeClosetItem from '../../components/mixins/removeClosetItem'
import emitMounted from '../../components/mixins/emitMounted'
import ApplyToPlayerDialog from '../../components/ApplyToPlayerDialog.vue'
export default {
name: 'Show',
components: {
ApplyToPlayerDialog,
Previewer: () => import('../../components/Previewer.vue'),
},
mixins: [
emitMounted,
addClosetItem,
removeClosetItem,
setAsAvatar,
],
props: {
baseUrl: {
type: String,
default: document.baseURI,
},
},
data() {
return {
tid: +this.$route[1],
name: '',
type: 'steve',
likes: 0,
hash: '',
uploader: 0,
size: 0,
uploadAt: '',
public: true,
liked: blessing.extra.inCloset,
canBeDownloaded: blessing.extra.download,
currentUid: blessing.extra.currentUid,
admin: blessing.extra.admin,
uploaderNickName: blessing.extra.nickname,
reportScore: blessing.extra.report,
}
},
computed: {
anonymous() {
return !this.currentUid
},
hasEditPermission() {
return this.uploader === this.currentUid || this.admin
},
togglePrivacyText() {
return this.public ? 'skinlib.setAsPrivate' : 'skinlib.setAsPublic'
},
textureUrl() {
return `${this.baseUrl}textures/${this.hash}`
},
},
beforeMount() {
this.fetchData()
},
methods: {
async fetchData() {
const { data = {} } = await this.$http.get(`/skinlib/info/${this.tid}`)
Object.assign(this.$data, data)
this.uploadAt = data.upload_at
},
async addToCloset() {
this.$once('like-toggled', () => {
this.liked = true
this.likes += 1
})
await this.addClosetItem()
},
async removeFromCloset() {
this.$once('item-removed', () => {
this.liked = false
this.likes -= 1
})
await this.removeClosetItem()
},
download() {
const a = document.createElement('a')
a.href = `${this.baseUrl}raw/${this.tid}.png`
a.download = `${this.name}.png`
const event = new MouseEvent('click')
a.dispatchEvent(event)
},
async changeTextureName() {
let value
try {
({ value } = await this.$prompt(this.$t('skinlib.setNewTextureName'), {
inputValue: this.name,
inputValidator: name => !!name || this.$t('skinlib.emptyNewTextureName'),
}))
} catch {
return
}
const { code, message } = await this.$http.post(
'/skinlib/rename',
{ tid: this.tid, new_name: value },
)
if (code === 0) {
this.name = value
this.$message.success(message)
} else {
this.$message.error(message)
}
},
async changeModel() {
const h = this.$createElement
const vnode = h('div', null, [
h('span', null, this.$t('skinlib.setNewTextureModel')),
h('select', { attrs: { selectedIndex: 0 } }, [
h('option', { attrs: { value: 'steve' } }, 'Steve'),
h('option', { attrs: { value: 'alex' } }, 'Alex'),
h('option', { attrs: { value: 'cape' } }, this.$t('general.cape')),
]),
])
try {
await this.$msgbox({
message: vnode,
showCancelButton: true,
})
} catch {
return
}
const value = ['steve', 'alex', 'cape'][vnode.children[1].elm.selectedIndex]
const { code, message } = await this.$http.post(
'/skinlib/model',
{ tid: this.tid, model: value },
)
if (code === 0) {
this.type = value
this.$message.success(message)
} else {
this.$message.warning(message)
}
},
async togglePrivacy() {
try {
await this.$confirm(
this.public
? this.$t('skinlib.setPrivateNotice')
: this.$t('skinlib.setPublicNotice'),
{ type: 'warning' },
)
} catch {
return
}
const { code, message } = await this.$http.post(
'/skinlib/privacy',
{ tid: this.tid },
)
if (code === 0) {
this.$message.success(message)
this.public = !this.public
} else {
this.$message.warning(message)
}
},
async deleteTexture() {
try {
await this.$confirm(
this.$t('skinlib.deleteNotice'),
{ type: 'warning' },
)
} catch {
return
}
const { code, message } = await this.$http.post(
'/skinlib/delete',
{ tid: this.tid },
)
if (code === 0) {
this.$message.success(message)
setTimeout(() => (window.location = `${this.baseUrl}skinlib`), 1000)
} else {
this.$message.warning(message)
}
},
async report() {
const prompt = (() => {
if (this.reportScore > 0) {
return this.$t('skinlib.report.positive', { score: this.reportScore })
} else if (this.reportScore < 0) {
return this.$t('skinlib.report.negative', { score: -this.reportScore })
}
return ''
})()
let reason
try {
({ value: reason } = await this.$prompt(prompt, {
title: this.$t('skinlib.report.title'),
inputPlaceholder: this.$t('skinlib.report.reason'),
}))
} catch {
return
}
const { code, message } = await this.$http.post(
'/skinlib/report',
{ tid: this.tid, reason },
)
if (code === 0) {
this.$message.success(message)
} else {
this.$message.warning(message)
}
},
fetchPlayersList() {
this.$refs.useAs.fetchList()
},
},
}
</script>
<style lang="stylus">
.table > tbody > tr > td
border-top 0
</style>