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",
|
||||
"admin-lte": "^2.4.2",
|
||||
"bootstrap": "^3.3.7",
|
||||
"bootstrap-fileinput": "^4.4.7",
|
||||
"chart.js": "^2.7.1",
|
||||
"es6-promise": "^4.2.4",
|
||||
"highlight.js": "^9.12.0",
|
||||
@ -36,6 +35,7 @@
|
||||
"toastr": "^2.1.4",
|
||||
"vue": "^2.5.16",
|
||||
"vue-good-table": "^2.12.2",
|
||||
"vue-upload-component": "^2.8.11",
|
||||
"vuejs-paginate": "^2.0.1",
|
||||
"whatwg-fetch": "^2.0.4"
|
||||
},
|
||||
|
@ -59,4 +59,9 @@ export default [
|
||||
component: () => import('./skinlib/List'),
|
||||
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 'bootstrap';
|
||||
import 'bootstrap-fileinput';
|
||||
import 'admin-lte';
|
||||
import 'icheck';
|
||||
import Vue from 'vue';
|
||||
|
@ -10,7 +10,7 @@ const init = {
|
||||
}
|
||||
};
|
||||
|
||||
async function walkFetch(request) {
|
||||
export async function walkFetch(request) {
|
||||
try {
|
||||
const response = await fetch(request);
|
||||
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.
|
||||
encodingError: 'Error: Encoding of this file is not accepted.'
|
||||
fileExtError: 'Error: Textures should be PNG files.'
|
||||
upload: Upload
|
||||
uploading: Uploading
|
||||
redirecting: Redirecting...
|
||||
setAsPrivate: Set as Private
|
||||
setAsPublic: Set as Public
|
||||
setPublicNotice: Sure to set this as public 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:
|
||||
signRemainingTime: 'Available after :time :unit'
|
||||
|
@ -65,13 +65,22 @@ skinlib:
|
||||
emptyUploadFile: 你还没有上传任何文件哦
|
||||
encodingError: 错误:这张图片编码不对哦
|
||||
fileExtError: 错误:皮肤文件必须为 PNG 格式
|
||||
upload: 确认上传
|
||||
uploading: 上传中
|
||||
redirecting: 正在跳转...
|
||||
setAsPrivate: 设为隐私
|
||||
setAsPublic: 设为公开
|
||||
setPublicNotice: 要将此材质设置为公开吗?
|
||||
deleteNotice: 真的要删除此材质吗?
|
||||
upload:
|
||||
texture-name: 材质名称
|
||||
texture-type: 材质类型
|
||||
select-file: 选择文件
|
||||
privacy-notice: 其他人将不会在皮肤库中看到此材质
|
||||
set-as-private: 设置为私密材质
|
||||
button: 确认上传
|
||||
dropZone: 拖拽文件到这里
|
||||
remove: 移除
|
||||
cost: (这会消耗您约 :score 积分)
|
||||
|
||||
user:
|
||||
signRemainingTime: ':time :unit 后可签到'
|
||||
@ -290,86 +299,6 @@ general:
|
||||
last-modified: 修改时间
|
||||
|
||||
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:
|
||||
search: 搜索
|
||||
rowsPerPage: 每页的项目数
|
||||
|
@ -2,15 +2,6 @@
|
||||
|
||||
@section('title', trans('skinlib.upload.title'))
|
||||
|
||||
@section('style')
|
||||
<style>
|
||||
label[for="type-skin"],
|
||||
label[for="type-cape"] {
|
||||
margin-top: 5px;
|
||||
}
|
||||
</style>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<!-- Full Width Column -->
|
||||
<div class="content-wrapper">
|
||||
@ -23,83 +14,16 @@
|
||||
</section>
|
||||
|
||||
<!-- Main content -->
|
||||
<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">@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 -->
|
||||
<section class="content"></section><!-- /.content -->
|
||||
</div><!-- /.container -->
|
||||
</div><!-- /.content-wrapper -->
|
||||
@endsection
|
||||
|
||||
@section('script')
|
||||
<script>
|
||||
initUploadListeners();
|
||||
registerAnimationController();
|
||||
registerWindowResizeHandler();
|
||||
var __bs_data__ = {
|
||||
rule: "{{ option('texture_name_regexp') ? trans('skinlib.upload.name-rule-regexp', compact('regexp')) : trans('skinlib.upload.name-rule') }}",
|
||||
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>
|
||||
@endsection
|
||||
|
@ -16,7 +16,6 @@ const config = {
|
||||
style: [
|
||||
'bootstrap/dist/css/bootstrap.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/regular.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"
|
||||
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:
|
||||
version "9.10.0"
|
||||
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"
|
||||
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:
|
||||
version "3.3.7"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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:
|
||||
version "2.5.16"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.16.tgz#07edb75e8412aaeed871ebafa99f4672584a0085"
|
||||
|
Loading…
Reference in New Issue
Block a user