mirror of
https://github.com/bs-community/blessing-skin-server.git
synced 2025-01-18 13:54:01 +08:00
Add "upload" page
This commit is contained in:
parent
a573f0efe3
commit
733e694ba0
@ -25,7 +25,6 @@
|
|||||||
"@fortawesome/fontawesome-free": "^5.2.0",
|
"@fortawesome/fontawesome-free": "^5.2.0",
|
||||||
"admin-lte": "^2.4.2",
|
"admin-lte": "^2.4.2",
|
||||||
"bootstrap": "^3.3.7",
|
"bootstrap": "^3.3.7",
|
||||||
"bootstrap-fileinput": "^4.4.7",
|
|
||||||
"chart.js": "^2.7.1",
|
"chart.js": "^2.7.1",
|
||||||
"es6-promise": "^4.2.4",
|
"es6-promise": "^4.2.4",
|
||||||
"highlight.js": "^9.12.0",
|
"highlight.js": "^9.12.0",
|
||||||
@ -36,6 +35,7 @@
|
|||||||
"toastr": "^2.1.4",
|
"toastr": "^2.1.4",
|
||||||
"vue": "^2.5.16",
|
"vue": "^2.5.16",
|
||||||
"vue-good-table": "^2.12.2",
|
"vue-good-table": "^2.12.2",
|
||||||
|
"vue-upload-component": "^2.8.11",
|
||||||
"vuejs-paginate": "^2.0.1",
|
"vuejs-paginate": "^2.0.1",
|
||||||
"whatwg-fetch": "^2.0.4"
|
"whatwg-fetch": "^2.0.4"
|
||||||
},
|
},
|
||||||
|
@ -59,4 +59,9 @@ export default [
|
|||||||
component: () => import('./skinlib/List'),
|
component: () => import('./skinlib/List'),
|
||||||
el: '.content-wrapper'
|
el: '.content-wrapper'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'skinlib/upload',
|
||||||
|
component: () => import('./skinlib/Upload'),
|
||||||
|
el: '.content'
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
222
resources/assets/src/components/skinlib/Upload.vue
Normal file
222
resources/assets/src/components/skinlib/Upload.vue
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
<template>
|
||||||
|
<section class="content">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="box box-primary">
|
||||||
|
<div class="box-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name" v-t="'skinlib.upload.texture-name'"></label>
|
||||||
|
<input
|
||||||
|
v-model="name"
|
||||||
|
class="form-control"
|
||||||
|
type="text"
|
||||||
|
:placeholder="textureNameRule"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label v-t="'skinlib.upload.texture-type'"></label>
|
||||||
|
<br>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
v-model="type"
|
||||||
|
name="type"
|
||||||
|
value="steve"
|
||||||
|
checked
|
||||||
|
> Steve
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
v-model="type"
|
||||||
|
name="type"
|
||||||
|
value="alex"
|
||||||
|
> Alex
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
v-model="type"
|
||||||
|
name="type"
|
||||||
|
value="cape"
|
||||||
|
> {{ $t('general.cape') }}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="file" v-t="'skinlib.upload.select-file'"></label>
|
||||||
|
<div class="file-dnd">
|
||||||
|
<img v-if="hasFile" :src="texture">
|
||||||
|
<h3 v-else v-t="'skinlib.upload.dropZone'"></h3>
|
||||||
|
</div>
|
||||||
|
<file-upload
|
||||||
|
v-model="files"
|
||||||
|
ref="upload"
|
||||||
|
extensions="png"
|
||||||
|
accept="image/png,image/x-png"
|
||||||
|
drop=".file-dnd"
|
||||||
|
@input-file="inputFile"
|
||||||
|
>
|
||||||
|
<span class="btn btn-primary">
|
||||||
|
{{ $t('skinlib.upload.select-file') }}
|
||||||
|
</span>
|
||||||
|
</file-upload>
|
||||||
|
<button
|
||||||
|
v-show="hasFile"
|
||||||
|
class="btn btn-default pull-right"
|
||||||
|
@click="remove"
|
||||||
|
>
|
||||||
|
<i class="fas fa-trash-alt"></i>
|
||||||
|
{{ $t('skinlib.upload.remove') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="callout callout-info" v-if="isPrivate">
|
||||||
|
<p>{{ privacyNotice }}</p>
|
||||||
|
</div>
|
||||||
|
</div><!-- /.box-body -->
|
||||||
|
|
||||||
|
<div class="box-footer">
|
||||||
|
<label
|
||||||
|
for="private"
|
||||||
|
class="pull-right"
|
||||||
|
:title="$t('skinlib.upload.privacy-notice')"
|
||||||
|
data-placement="top"
|
||||||
|
data-toggle="tooltip"
|
||||||
|
>
|
||||||
|
<input v-model="isPrivate" type="checkbox"> {{ $t('skinlib.upload.set-as-private') }}
|
||||||
|
</label>
|
||||||
|
<button v-if="uploading" class="btn btn-primary" disabled>
|
||||||
|
<i class="fa fa-spinner fa-spin"></i> {{ $t('skinlib.uploading') }}
|
||||||
|
</button>
|
||||||
|
<button v-else @click="upload" class="btn btn-primary">
|
||||||
|
{{ $t('skinlib.upload.button') }}
|
||||||
|
</button>
|
||||||
|
{{ hasFile && $t('skinlib.upload.cost', { score: scoreCost }) }}
|
||||||
|
</div>
|
||||||
|
</div><!-- /.box -->
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<previewer
|
||||||
|
:skin="type !== 'cape' && texture"
|
||||||
|
:cape="type === 'cape' ? texture : ''"
|
||||||
|
></previewer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import FileUpload from 'vue-upload-component';
|
||||||
|
import toastr from 'toastr';
|
||||||
|
import { walkFetch } from '../../js/net';
|
||||||
|
import { swal } from '../../js/notify';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Upload',
|
||||||
|
components: {
|
||||||
|
Previewer: () => import('../common/Previewer'),
|
||||||
|
FileUpload
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
name: '',
|
||||||
|
type: 'steve',
|
||||||
|
isPrivate: false,
|
||||||
|
files: [],
|
||||||
|
texture: '',
|
||||||
|
uploading: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
textureNameRule: () => __bs_data__.rule,
|
||||||
|
privacyNotice: () => __bs_data__.privacyNotice,
|
||||||
|
scorePublic: () => __bs_data__.scorePublic,
|
||||||
|
scorePrivate: () => __bs_data__.scorePrivate,
|
||||||
|
scoreCost() {
|
||||||
|
const size = Math.round(this.files[0].size / 1024) || 1;
|
||||||
|
return size * (this.isPrivate ? this.scorePrivate : this.scorePublic);
|
||||||
|
},
|
||||||
|
hasFile() {
|
||||||
|
return this.files[0];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async upload() {
|
||||||
|
if (!this.hasFile) {
|
||||||
|
toastr.info(this.$t('skinlib.emptyUploadFile'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.name) {
|
||||||
|
toastr.info(this.$t('skinlib.emptyTextureName'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/image\/(x-)?png/.test(this.files[0].type)) {
|
||||||
|
toastr.info(this.$t('skinlib.fileExtError'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = new FormData();
|
||||||
|
data.append('name', this.name);
|
||||||
|
data.append('type', this.type);
|
||||||
|
data.append('file', this.files[0].file, this.files[0].name);
|
||||||
|
data.append('public', !this.isPrivate);
|
||||||
|
|
||||||
|
this.uploading = true;
|
||||||
|
const request = new Request(`${blessing.base_url}/skinlib/upload`, {
|
||||||
|
body: data,
|
||||||
|
credentials: 'same-origin',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json'
|
||||||
|
},
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
const { errno, msg, tid } = await walkFetch(request);
|
||||||
|
if (errno === 0) {
|
||||||
|
await swal({ type: 'success', text: msg });
|
||||||
|
toastr.info(this.$t('skinlib.redirecting'));
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location = (`${blessing.base_url}/skinlib/show/${tid}`);
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
await swal({ type: 'warning', text: msg });
|
||||||
|
this.uploading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inputFile(file) {
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
if (!this.name) {
|
||||||
|
const matched = /(.*)\.png$/i.exec(file.name);
|
||||||
|
this.name = matched ? matched[1] : file.name;
|
||||||
|
}
|
||||||
|
this.texture = URL.createObjectURL(file.file);
|
||||||
|
},
|
||||||
|
remove() {
|
||||||
|
this.$refs.upload.clear();
|
||||||
|
this.texture = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
.file-dnd {
|
||||||
|
min-height: 256px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 2px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,6 +1,5 @@
|
|||||||
import './jquery'; // jQuery first
|
import './jquery'; // jQuery first
|
||||||
import 'bootstrap';
|
import 'bootstrap';
|
||||||
import 'bootstrap-fileinput';
|
|
||||||
import 'admin-lte';
|
import 'admin-lte';
|
||||||
import 'icheck';
|
import 'icheck';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
@ -10,7 +10,7 @@ const init = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
async function walkFetch(request) {
|
export async function walkFetch(request) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(request);
|
const response = await fetch(request);
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
|
138
resources/assets/tests/components/skinlib/Upload.test.js
Normal file
138
resources/assets/tests/components/skinlib/Upload.test.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
import Vue from 'vue';
|
||||||
|
import { mount } from '@vue/test-utils';
|
||||||
|
import Upload from '@/components/skinlib/Upload';
|
||||||
|
import { flushPromises } from '../../utils';
|
||||||
|
import toastr from 'toastr';
|
||||||
|
import { swal } from '@/js/notify';
|
||||||
|
import { walkFetch } from '@/js/net';
|
||||||
|
|
||||||
|
jest.mock('toastr');
|
||||||
|
jest.mock('@/js/notify');
|
||||||
|
jest.mock('@/js/net');
|
||||||
|
|
||||||
|
window.__bs_data__ = {
|
||||||
|
textureNameRule: 'rule',
|
||||||
|
privacyNotice: 'privacyNotice',
|
||||||
|
scorePrivate: 10,
|
||||||
|
scorePublic: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
test('display drap and drop notice', () => {
|
||||||
|
const wrapper = mount(Upload, {
|
||||||
|
stubs: ['file-upload']
|
||||||
|
});
|
||||||
|
expect(wrapper.text()).toContain('skinlib.upload.dropZone');
|
||||||
|
wrapper.setData({ files: [{}] });
|
||||||
|
expect(wrapper.contains('img')).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('button for removing texture', () => {
|
||||||
|
const wrapper = mount(Upload, {
|
||||||
|
stubs: ['file-upload']
|
||||||
|
});
|
||||||
|
wrapper.vm.$refs = {
|
||||||
|
upload: {
|
||||||
|
clear: jest.fn()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const button = wrapper.find('.btn-default');
|
||||||
|
expect(button.isVisible()).toBeFalse();
|
||||||
|
wrapper.setData({ files: [{}] });
|
||||||
|
expect(button.isVisible()).toBeTrue();
|
||||||
|
button.trigger('click');
|
||||||
|
expect(wrapper.vm.texture).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('notice should be display if texture is private', () => {
|
||||||
|
const wrapper = mount(Upload, {
|
||||||
|
stubs: ['file-upload']
|
||||||
|
});
|
||||||
|
expect(wrapper.contains('.callout')).toBeFalse();
|
||||||
|
wrapper.find('[type=checkbox]').setChecked();
|
||||||
|
expect(wrapper.find('.callout').text()).toBe('privacyNotice');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('display score cost', () => {
|
||||||
|
const origin = Vue.prototype.$t;
|
||||||
|
Vue.prototype.$t = (key, args) => `${key}${JSON.stringify(args)}`;
|
||||||
|
|
||||||
|
const wrapper = mount(Upload, {
|
||||||
|
stubs: ['file-upload']
|
||||||
|
});
|
||||||
|
wrapper.find('[type=checkbox]').setChecked();
|
||||||
|
wrapper.setData({ files: [{ size: 1024 }] });
|
||||||
|
expect(wrapper.text()).toContain(JSON.stringify({ score: 10 }));
|
||||||
|
|
||||||
|
Vue.prototype.$t = origin;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('process input file', () => {
|
||||||
|
window.URL.createObjectURL = jest.fn().mockReturnValue('file-url');
|
||||||
|
const blob = new Blob;
|
||||||
|
const wrapper = mount(Upload, {
|
||||||
|
stubs: ['file-upload']
|
||||||
|
});
|
||||||
|
|
||||||
|
wrapper.vm.inputFile();
|
||||||
|
expect(wrapper.vm.name).toBe('');
|
||||||
|
|
||||||
|
wrapper.vm.inputFile({ file: blob, name: '123.png' });
|
||||||
|
expect(wrapper.vm.name).toBe('123');
|
||||||
|
|
||||||
|
wrapper.vm.inputFile({ file: blob, name: '456.png' });
|
||||||
|
expect(wrapper.vm.name).toBe('123');
|
||||||
|
|
||||||
|
wrapper.setData({ name: '' });
|
||||||
|
wrapper.vm.inputFile({ file: blob, name: '789' });
|
||||||
|
expect(wrapper.vm.name).toBe('789');
|
||||||
|
|
||||||
|
expect(wrapper.vm.texture).toBe('file-url');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('upload file', async () => {
|
||||||
|
window.Request = jest.fn();
|
||||||
|
walkFetch
|
||||||
|
.mockResolvedValueOnce({ errno: 1, msg: '1' })
|
||||||
|
.mockResolvedValueOnce({ errno: 0, msg: '0', tid: 1 });
|
||||||
|
jest.spyOn(toastr, 'info');
|
||||||
|
swal.mockReturnValue();
|
||||||
|
|
||||||
|
const wrapper = mount(Upload, {
|
||||||
|
stubs: ['file-upload']
|
||||||
|
});
|
||||||
|
const button = wrapper.find('.box-footer > button');
|
||||||
|
|
||||||
|
button.trigger('click');
|
||||||
|
expect(walkFetch).not.toBeCalled();
|
||||||
|
expect(toastr.info).toBeCalledWith('skinlib.emptyUploadFile');
|
||||||
|
|
||||||
|
wrapper.setData({ files: [{ file: {}, name: 't', type: 'image/jpeg' }] });
|
||||||
|
button.trigger('click');
|
||||||
|
expect(walkFetch).not.toBeCalled();
|
||||||
|
expect(toastr.info).toBeCalledWith('skinlib.emptyTextureName');
|
||||||
|
|
||||||
|
wrapper.find('[type=text]').setValue('t');
|
||||||
|
button.trigger('click');
|
||||||
|
expect(walkFetch).not.toBeCalled();
|
||||||
|
expect(toastr.info).toBeCalledWith('skinlib.fileExtError');
|
||||||
|
|
||||||
|
wrapper.setData({ files: [{ file: new Blob, name: 't.png', type: 'image/png' }] });
|
||||||
|
button.trigger('click');
|
||||||
|
await flushPromises();
|
||||||
|
expect(window.Request).toBeCalledWith('/skinlib/upload', {
|
||||||
|
body: expect.any(FormData),
|
||||||
|
credentials: 'same-origin',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json'
|
||||||
|
},
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
expect(walkFetch).toBeCalledWith(expect.any(window.Request));
|
||||||
|
expect(swal).toBeCalledWith({ type: 'warning', text: '1' });
|
||||||
|
|
||||||
|
button.trigger('click');
|
||||||
|
await flushPromises();
|
||||||
|
jest.runAllTimers();
|
||||||
|
expect(swal).toBeCalledWith({ type: 'success', text: '0' });
|
||||||
|
expect(toastr.info).toBeCalledWith('skinlib.redirecting');
|
||||||
|
});
|
@ -63,13 +63,22 @@ skinlib:
|
|||||||
emptyUploadFile: You have not uploaded any file.
|
emptyUploadFile: You have not uploaded any file.
|
||||||
encodingError: 'Error: Encoding of this file is not accepted.'
|
encodingError: 'Error: Encoding of this file is not accepted.'
|
||||||
fileExtError: 'Error: Textures should be PNG files.'
|
fileExtError: 'Error: Textures should be PNG files.'
|
||||||
upload: Upload
|
|
||||||
uploading: Uploading
|
uploading: Uploading
|
||||||
redirecting: Redirecting...
|
redirecting: Redirecting...
|
||||||
setAsPrivate: Set as Private
|
setAsPrivate: Set as Private
|
||||||
setAsPublic: Set as Public
|
setAsPublic: Set as Public
|
||||||
setPublicNotice: Sure to set this as public texture?
|
setPublicNotice: Sure to set this as public texture?
|
||||||
deleteNotice: Are you sure to delete this texture?
|
deleteNotice: Are you sure to delete this texture?
|
||||||
|
upload:
|
||||||
|
texture-name: Texture Name
|
||||||
|
texture-type: Texture Type
|
||||||
|
select-file: Select File
|
||||||
|
privacy-notice: Prevent it from being visible at skin library.
|
||||||
|
set-as-private: Make it Private
|
||||||
|
button: Upload
|
||||||
|
dropZone: Drop a file here
|
||||||
|
remove: Remove
|
||||||
|
cost: (It cost you about :score score)
|
||||||
|
|
||||||
user:
|
user:
|
||||||
signRemainingTime: 'Available after :time :unit'
|
signRemainingTime: 'Available after :time :unit'
|
||||||
|
@ -65,13 +65,22 @@ skinlib:
|
|||||||
emptyUploadFile: 你还没有上传任何文件哦
|
emptyUploadFile: 你还没有上传任何文件哦
|
||||||
encodingError: 错误:这张图片编码不对哦
|
encodingError: 错误:这张图片编码不对哦
|
||||||
fileExtError: 错误:皮肤文件必须为 PNG 格式
|
fileExtError: 错误:皮肤文件必须为 PNG 格式
|
||||||
upload: 确认上传
|
|
||||||
uploading: 上传中
|
uploading: 上传中
|
||||||
redirecting: 正在跳转...
|
redirecting: 正在跳转...
|
||||||
setAsPrivate: 设为隐私
|
setAsPrivate: 设为隐私
|
||||||
setAsPublic: 设为公开
|
setAsPublic: 设为公开
|
||||||
setPublicNotice: 要将此材质设置为公开吗?
|
setPublicNotice: 要将此材质设置为公开吗?
|
||||||
deleteNotice: 真的要删除此材质吗?
|
deleteNotice: 真的要删除此材质吗?
|
||||||
|
upload:
|
||||||
|
texture-name: 材质名称
|
||||||
|
texture-type: 材质类型
|
||||||
|
select-file: 选择文件
|
||||||
|
privacy-notice: 其他人将不会在皮肤库中看到此材质
|
||||||
|
set-as-private: 设置为私密材质
|
||||||
|
button: 确认上传
|
||||||
|
dropZone: 拖拽文件到这里
|
||||||
|
remove: 移除
|
||||||
|
cost: (这会消耗您约 :score 积分)
|
||||||
|
|
||||||
user:
|
user:
|
||||||
signRemainingTime: ':time :unit 后可签到'
|
signRemainingTime: ':time :unit 后可签到'
|
||||||
@ -290,86 +299,6 @@ general:
|
|||||||
last-modified: 修改时间
|
last-modified: 修改时间
|
||||||
|
|
||||||
vendor:
|
vendor:
|
||||||
fileinput:
|
|
||||||
fileSingle: 文件
|
|
||||||
filePlural: 个文件
|
|
||||||
browseLabel: 选择 …
|
|
||||||
removeLabel: 移除
|
|
||||||
removeTitle: 清除选中文件
|
|
||||||
cancelLabel: 取消
|
|
||||||
cancelTitle: 取消进行中的上传
|
|
||||||
uploadLabel: 上传
|
|
||||||
uploadTitle: 上传选中文件
|
|
||||||
msgNo: 没有
|
|
||||||
msgNoFilesSelected: ''
|
|
||||||
msgCancelled: 取消
|
|
||||||
msgPlaceholder: '选择 {files}...'
|
|
||||||
msgZoomModalHeading: 详细预览
|
|
||||||
msgFileRequired: 必须选择一个文件上传.
|
|
||||||
msgSizeTooSmall: '文件 "{name}" (<b>{size} KB</b>) 必须大于限定大小 <b>{minSize} KB</b>.'
|
|
||||||
msgSizeTooLarge: '文件 "{name}" (<b>{size} KB</b>) 超过了允许大小 <b>{maxSize} KB</b>.'
|
|
||||||
msgFilesTooLess: '你必须选择最少 <b>{n}</b> {files} 来上传. '
|
|
||||||
msgFilesTooMany: '选择的上传文件个数 <b>({n})</b> 超出最大文件的限制个数 <b>{m}</b>.'
|
|
||||||
msgFileNotFound: '文件 "{name}" 未找到!'
|
|
||||||
msgFileSecured: '安全限制,为了防止读取文件 "{name}".'
|
|
||||||
msgFileNotReadable: '文件 "{name}" 不可读.'
|
|
||||||
msgFilePreviewAborted: '取消 "{name}" 的预览.'
|
|
||||||
msgFilePreviewError: '读取 "{name}" 时出现了一个错误.'
|
|
||||||
msgInvalidFileName: '文件名 "{name}" 包含非法字符.'
|
|
||||||
msgInvalidFileType: '不正确的类型 "{name}". 只支持 "{types}" 类型的文件.'
|
|
||||||
msgInvalidFileExtension: '不正确的文件扩展名 "{name}". 只支持 "{extensions}" 的文件扩展名.'
|
|
||||||
msgFileTypes:
|
|
||||||
image: image
|
|
||||||
html: HTML
|
|
||||||
text: text
|
|
||||||
video: video
|
|
||||||
audio: audio
|
|
||||||
flash: flash
|
|
||||||
pdf: PDF
|
|
||||||
object: object
|
|
||||||
msgUploadAborted: 该文件上传被中止
|
|
||||||
msgUploadThreshold: 处理中...
|
|
||||||
msgUploadBegin: 正在初始化...
|
|
||||||
msgUploadEnd: 完成
|
|
||||||
msgUploadEmpty: 无效的文件上传.
|
|
||||||
msgUploadError: Error
|
|
||||||
msgValidationError: 验证错误
|
|
||||||
msgLoading: '加载第 {index} 文件 共 {files} …'
|
|
||||||
msgProgress: '加载第 {index} 文件 共 {files} - {name} - {percent}% 完成.'
|
|
||||||
msgSelected: '{n} {files} 选中'
|
|
||||||
msgFoldersNotAllowed: '只支持拖拽文件! 跳过 {n} 拖拽的文件夹.'
|
|
||||||
msgImageWidthSmall: '图像文件的"{name}"的宽度必须是至少{size}像素.'
|
|
||||||
msgImageHeightSmall: '图像文件的"{name}"的高度必须至少为{size}像素.'
|
|
||||||
msgImageWidthLarge: '图像文件"{name}"的宽度不能超过{size}像素.'
|
|
||||||
msgImageHeightLarge: '图像文件"{name}"的高度不能超过{size}像素.'
|
|
||||||
msgImageResizeError: 无法获取的图像尺寸调整。
|
|
||||||
msgImageResizeException: '调整图像大小时发生错误。<pre>{errors}</pre>'
|
|
||||||
msgAjaxError: '{operation} 发生错误. 请重试!'
|
|
||||||
msgAjaxProgressError: '{operation} 失败'
|
|
||||||
ajaxOperations:
|
|
||||||
deleteThumb: 删除文件
|
|
||||||
uploadThumb: 上传文件
|
|
||||||
uploadBatch: 批量上传
|
|
||||||
uploadExtra: 表单数据上传
|
|
||||||
dropZoneTitle: 拖拽文件到这里 …<br>支持多文件同时上传
|
|
||||||
dropZoneClickTitle: '<br>(或点击{files}按钮选择文件)'
|
|
||||||
fileActionSettings:
|
|
||||||
removeTitle: 删除文件
|
|
||||||
uploadTitle: 上传文件
|
|
||||||
uploadRetryTitle: Retry upload
|
|
||||||
zoomTitle: 查看详情
|
|
||||||
dragTitle: 移动 / 重置
|
|
||||||
indicatorNewTitle: 没有上传
|
|
||||||
indicatorSuccessTitle: 上传
|
|
||||||
indicatorErrorTitle: 上传错误
|
|
||||||
indicatorLoadingTitle: 上传 ...
|
|
||||||
previewZoomButtonTitles:
|
|
||||||
prev: 预览上一个文件
|
|
||||||
next: 预览下一个文件
|
|
||||||
toggleheader: 缩放
|
|
||||||
fullscreen: 全屏
|
|
||||||
borderless: 无边界模式
|
|
||||||
close: 关闭当前预览
|
|
||||||
datatable:
|
datatable:
|
||||||
search: 搜索
|
search: 搜索
|
||||||
rowsPerPage: 每页的项目数
|
rowsPerPage: 每页的项目数
|
||||||
|
@ -2,15 +2,6 @@
|
|||||||
|
|
||||||
@section('title', trans('skinlib.upload.title'))
|
@section('title', trans('skinlib.upload.title'))
|
||||||
|
|
||||||
@section('style')
|
|
||||||
<style>
|
|
||||||
label[for="type-skin"],
|
|
||||||
label[for="type-cape"] {
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@endsection
|
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<!-- Full Width Column -->
|
<!-- Full Width Column -->
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
@ -23,83 +14,16 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Main content -->
|
<!-- Main content -->
|
||||||
<section class="content">
|
<section class="content"></section><!-- /.content -->
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="box box-primary">
|
|
||||||
<div class="box-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="name">@lang('skinlib.upload.texture-name')</label>
|
|
||||||
@php
|
|
||||||
$regexp = option('texture_name_regexp');
|
|
||||||
@endphp
|
|
||||||
<input id="name" class="form-control" type="text" placeholder="{{
|
|
||||||
$regexp ? trans('skinlib.upload.name-rule-regexp', compact('regexp')) : trans('skinlib.upload.name-rule')
|
|
||||||
}}" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>@lang('skinlib.upload.texture-type')</label>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-4">
|
|
||||||
<label for="type-skin">
|
|
||||||
<input type="radio" name="type" id="type-skin" checked> @lang('general.skin')
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-8">
|
|
||||||
<select class="form-control" id="skin-type">
|
|
||||||
<option value="steve">@lang('skinlib.filter.steve-model')</option>
|
|
||||||
<option value="alex">@lang('skinlib.filter.alex-model')</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-6">
|
|
||||||
<label for="type-cape">
|
|
||||||
<input type="radio" name="type" id="type-cape"> @lang('general.cape')
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="file">@lang('skinlib.upload.select-file')</label>
|
|
||||||
<input id="file" type="file" data-show-upload="false" data-language="{{ config('app.locale') }}" class="file" accept="image/png" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="callout callout-info" id="msg" style="display: none;">
|
|
||||||
<p>@lang('skinlib.upload.private-score-notice', ['score' => option('private_score_per_storage')])</p>
|
|
||||||
</div>
|
|
||||||
</div><!-- /.box-body -->
|
|
||||||
|
|
||||||
<div class="box-footer">
|
|
||||||
<label for="private" class="pull-right" title="@lang('skinlib.upload.privacy-notice')" data-placement="top" data-toggle="tooltip">
|
|
||||||
<input id="private" type="checkbox"> @lang('skinlib.upload.set-as-private')
|
|
||||||
</label>
|
|
||||||
<button id="upload-button" onclick="upload()" class="btn btn-primary">@lang('skinlib.upload.button')</button>
|
|
||||||
</div>
|
|
||||||
</div><!-- /.box -->
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="box box-default">
|
|
||||||
@include('common.texture-preview')
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</section><!-- /.content -->
|
|
||||||
</div><!-- /.container -->
|
</div><!-- /.container -->
|
||||||
</div><!-- /.content-wrapper -->
|
</div><!-- /.content-wrapper -->
|
||||||
@endsection
|
|
||||||
|
|
||||||
@section('script')
|
|
||||||
<script>
|
<script>
|
||||||
initUploadListeners();
|
var __bs_data__ = {
|
||||||
registerAnimationController();
|
rule: "{{ option('texture_name_regexp') ? trans('skinlib.upload.name-rule-regexp', compact('regexp')) : trans('skinlib.upload.name-rule') }}",
|
||||||
registerWindowResizeHandler();
|
privacyNotice: "@lang('skinlib.upload.private-score-notice', ['score' => option('private_score_per_storage')])",
|
||||||
|
scorePublic: {{ option('score_per_storage') }},
|
||||||
|
scorePrivate: {{ option('private_score_per_storage') }}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
@endsection
|
@endsection
|
||||||
|
@ -16,7 +16,6 @@ const config = {
|
|||||||
style: [
|
style: [
|
||||||
'bootstrap/dist/css/bootstrap.min.css',
|
'bootstrap/dist/css/bootstrap.min.css',
|
||||||
'admin-lte/dist/css/AdminLTE.min.css',
|
'admin-lte/dist/css/AdminLTE.min.css',
|
||||||
'bootstrap-fileinput/css/fileinput.min.css',
|
|
||||||
'@fortawesome/fontawesome-free/css/fontawesome.min.css',
|
'@fortawesome/fontawesome-free/css/fontawesome.min.css',
|
||||||
'@fortawesome/fontawesome-free/css/regular.min.css',
|
'@fortawesome/fontawesome-free/css/regular.min.css',
|
||||||
'@fortawesome/fontawesome-free/css/solid.min.css',
|
'@fortawesome/fontawesome-free/css/solid.min.css',
|
||||||
|
17
yarn.lock
17
yarn.lock
@ -1535,13 +1535,6 @@ bootstrap-daterangepicker@^2.1.25:
|
|||||||
jquery ">=1.10"
|
jquery ">=1.10"
|
||||||
moment "^2.9.0"
|
moment "^2.9.0"
|
||||||
|
|
||||||
bootstrap-fileinput@^4.4.7:
|
|
||||||
version "4.4.7"
|
|
||||||
resolved "https://registry.yarnpkg.com/bootstrap-fileinput/-/bootstrap-fileinput-4.4.7.tgz#ea6df7f6919e8de777ab7900fd07eed0893163ef"
|
|
||||||
dependencies:
|
|
||||||
bootstrap ">= 3.0.0"
|
|
||||||
jquery ">= 1.9.0"
|
|
||||||
|
|
||||||
bootstrap-slider@^9.8.0:
|
bootstrap-slider@^9.8.0:
|
||||||
version "9.10.0"
|
version "9.10.0"
|
||||||
resolved "https://registry.yarnpkg.com/bootstrap-slider/-/bootstrap-slider-9.10.0.tgz#1103d6bc00cfbfa8cfc9a2599ab518c55643da3f"
|
resolved "https://registry.yarnpkg.com/bootstrap-slider/-/bootstrap-slider-9.10.0.tgz#1103d6bc00cfbfa8cfc9a2599ab518c55643da3f"
|
||||||
@ -1550,10 +1543,6 @@ bootstrap-timepicker@^0.5.2:
|
|||||||
version "0.5.2"
|
version "0.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/bootstrap-timepicker/-/bootstrap-timepicker-0.5.2.tgz#10ed9f2a2f0b8ccaefcde0fcf6a0738b919a3835"
|
resolved "https://registry.yarnpkg.com/bootstrap-timepicker/-/bootstrap-timepicker-0.5.2.tgz#10ed9f2a2f0b8ccaefcde0fcf6a0738b919a3835"
|
||||||
|
|
||||||
"bootstrap@>= 3.0.0":
|
|
||||||
version "4.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.0.0.tgz#ceb03842c145fcc1b9b4e15da2a05656ba68469a"
|
|
||||||
|
|
||||||
bootstrap@^3.3.7:
|
bootstrap@^3.3.7:
|
||||||
version "3.3.7"
|
version "3.3.7"
|
||||||
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-3.3.7.tgz#5a389394549f23330875a3b150656574f8a9eb71"
|
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-3.3.7.tgz#5a389394549f23330875a3b150656574f8a9eb71"
|
||||||
@ -4978,7 +4967,7 @@ jquery-ui@^1.12.1:
|
|||||||
version "1.12.1"
|
version "1.12.1"
|
||||||
resolved "https://registry.yarnpkg.com/jquery-ui/-/jquery-ui-1.12.1.tgz#bcb4045c8dd0539c134bc1488cdd3e768a7a9e51"
|
resolved "https://registry.yarnpkg.com/jquery-ui/-/jquery-ui-1.12.1.tgz#bcb4045c8dd0539c134bc1488cdd3e768a7a9e51"
|
||||||
|
|
||||||
"jquery@2 - 3", "jquery@>= 1.9.0", jquery@>=1.10, jquery@>=1.12.0, jquery@>=1.5, jquery@>=1.7, "jquery@>=1.7.1 <4.0.0", jquery@>=1.8, jquery@^3.2.1, jquery@^3.3.1:
|
"jquery@2 - 3", jquery@>=1.10, jquery@>=1.12.0, jquery@>=1.5, jquery@>=1.7, "jquery@>=1.7.1 <4.0.0", jquery@>=1.8, jquery@^3.2.1, jquery@^3.3.1:
|
||||||
version "3.3.1"
|
version "3.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
|
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
|
||||||
|
|
||||||
@ -8481,6 +8470,10 @@ vue-template-es2015-compiler@^1.6.0:
|
|||||||
version "1.6.0"
|
version "1.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18"
|
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18"
|
||||||
|
|
||||||
|
vue-upload-component@^2.8.11:
|
||||||
|
version "2.8.11"
|
||||||
|
resolved "https://registry.npmjs.org/vue-upload-component/-/vue-upload-component-2.8.11.tgz#7dd207144ba9502760aecf77d42e4ab072b94b07"
|
||||||
|
|
||||||
vue@^2.5.16:
|
vue@^2.5.16:
|
||||||
version "2.5.16"
|
version "2.5.16"
|
||||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.16.tgz#07edb75e8412aaeed871ebafa99f4672584a0085"
|
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.16.tgz#07edb75e8412aaeed871ebafa99f4672584a0085"
|
||||||
|
Loading…
Reference in New Issue
Block a user