Optimize closet
1. Use jqPaginator 2. Use CSR for closet 3. Use AJAX for closet 4. Just type to search instead of pressing ENTER key 5. Link to skin library is according to current category when closet is empty 6. Texture indicator shows category of texture
This commit is contained in:
parent
7607beebaf
commit
f53bb7acb6
|
|
@ -25,43 +25,40 @@ class ClosetController extends Controller
|
|||
$this->closet = new Closet(session('uid'));
|
||||
}
|
||||
|
||||
public function index(Request $request)
|
||||
public function index()
|
||||
{
|
||||
return view('user.closet')->with('user', app('user.current'));
|
||||
}
|
||||
|
||||
public function getClosetData(Request $request)
|
||||
{
|
||||
$category = $request->input('category', 'skin');
|
||||
$page = $request->input('page', 1);
|
||||
$page = $page <= 0 ? 1 : $page;
|
||||
$page = abs($request->input('page', 1));
|
||||
$q = $request->input('q', null);
|
||||
|
||||
$items = [];
|
||||
|
||||
if ($q) {
|
||||
foreach (['skin', 'cape'] as $category) {
|
||||
// do search
|
||||
foreach ($this->closet->getItems($category) as $item) {
|
||||
if (strstr($item->name, $q)) {
|
||||
$items[$category][] = $item;
|
||||
}
|
||||
// do search
|
||||
foreach ($this->closet->getItems($category) as $item) {
|
||||
if (stristr($item->name, $q)) {
|
||||
$items[] = $item;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$items['skin'] = $this->closet->getItems('skin');
|
||||
$items['cape'] = $this->closet->getItems('cape');
|
||||
$items = $this->closet->getItems($category);
|
||||
}
|
||||
|
||||
// pagination
|
||||
$total_pages = [];
|
||||
$total_pages = ceil(count($items) / 6);
|
||||
|
||||
foreach ($items as $key => $value) {
|
||||
$total_pages[] = ceil(count($items[$key]) / 6);
|
||||
$items[$key] = array_slice($value, ($page-1)*6, 6);
|
||||
}
|
||||
$items = array_slice($items, ($page - 1) * 6, 6);
|
||||
|
||||
return view('user.closet')->with('items', $items)
|
||||
->with('page', $page)
|
||||
->with('q', $q)
|
||||
->with('category', $category)
|
||||
->with('total_pages', $total_pages ? max($total_pages) : 0)
|
||||
->with('user', app('user.current'));
|
||||
return response()->json([
|
||||
'category' => $category,
|
||||
'items' => $items,
|
||||
'total_pages' => $total_pages
|
||||
]);
|
||||
}
|
||||
|
||||
public function info()
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ Route::group(['middleware' => 'auth', 'prefix' => 'user'], function ()
|
|||
|
||||
// Closet
|
||||
Route::get ('/closet', 'ClosetController@index');
|
||||
Route::get ('/closet-data', 'ClosetController@getClosetData');
|
||||
Route::post('/closet/add', 'ClosetController@add');
|
||||
Route::post('/closet/remove', 'ClosetController@remove');
|
||||
Route::post('/closet/rename', 'ClosetController@rename');
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@
|
|||
"font-awesome": "Font-Awesome#^4.6.3",
|
||||
"bootstrap-fileinput": "^4.3.3",
|
||||
"bootstrap": "^3.3.6",
|
||||
"sweetalert2": "^4.1.6"
|
||||
"sweetalert2": "^4.1.6",
|
||||
"jqPaginator": "^1.2.0",
|
||||
"lodash": "^4.17.4"
|
||||
},
|
||||
"resolutions": {
|
||||
"jquery": "1.9.1 - 3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ var srcPath = 'resources/assets/src/';
|
|||
var distPath = 'resources/assets/dist/';
|
||||
|
||||
var vendorScripts = [
|
||||
'lodash/dist/lodash.min.js',
|
||||
'jquery/dist/jquery.min.js',
|
||||
'bootstrap/dist/js/bootstrap.min.js',
|
||||
'AdminLTE/dist/js/app.min.js',
|
||||
|
|
@ -35,6 +36,7 @@ var vendorScripts = [
|
|||
'toastr/toastr.min.js',
|
||||
'es6-promise/es6-promise.auto.min.js',
|
||||
'sweetalert2/dist/sweetalert2.min.js',
|
||||
'jqPaginator/dist/jqPaginator.min.js',
|
||||
'resources/assets/dist/scripts/general.js',
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -18,13 +18,13 @@ $('body').on('click', '#preview-switch', () => {
|
|||
TexturePreview.previewType == '3D' ? TexturePreview.show2dPreview() : TexturePreview.show3dPreview();
|
||||
});
|
||||
|
||||
var selected = [];
|
||||
let skinSelected = false, capeSelected = false;
|
||||
|
||||
$('body').on('click', '.item-body', function () {
|
||||
$('.item-selected').parent().removeClass('item-selected');
|
||||
$(this).parent().addClass('item-selected');
|
||||
|
||||
let tid = $(this).parent().attr('tid');
|
||||
const tid = $(this).parent().attr('tid');
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
|
|
@ -33,24 +33,95 @@ $('body').on('click', '.item-body', function () {
|
|||
success: (json) => {
|
||||
if (json.type == "cape") {
|
||||
MSP.changeCape('../textures/' + json.hash);
|
||||
selected['cape'] = tid;
|
||||
capeSelected = true;
|
||||
} else {
|
||||
MSP.changeSkin('../textures/' + json.hash);
|
||||
selected['skin'] = tid;
|
||||
skinSelected = true;
|
||||
}
|
||||
|
||||
selected.length = 0;
|
||||
|
||||
['skin', 'cape'].forEach((key) => {
|
||||
if (selected[key] !== undefined) selected.length++;
|
||||
|
||||
$('#textures-indicator').html(selected.length);
|
||||
});
|
||||
if (skinSelected && capeSelected)
|
||||
$('#textures-indicator').text(`${trans('general.skin')} & ${trans('general.cape')}`);
|
||||
else if (skinSelected)
|
||||
$('#textures-indicator').text(trans('general.skin'));
|
||||
else if (capeSelected)
|
||||
$('#textures-indicator').text(trans('general.cape'));
|
||||
},
|
||||
error: showAjaxError
|
||||
});
|
||||
});
|
||||
|
||||
$('body').on('click', '.category-switch', () => {
|
||||
const category = $('a[href="#skin-category"]').parent().hasClass('active') ? 'cape' : 'skin';
|
||||
const page = parseInt($('#closet-paginator').attr(`last-${category}-page`));
|
||||
const search = $('input[name=q]').val();
|
||||
reloadCloset(category, page, search);
|
||||
});
|
||||
|
||||
function renderClosetItemComponent(item, rootPath) {
|
||||
return `
|
||||
<div class="item" tid="${item.tid}">
|
||||
<div class="item-body">
|
||||
<img src="${rootPath}/preview/${item.tid}.png">
|
||||
</div>
|
||||
<div class="item-footer">
|
||||
<p class="texture-name">
|
||||
<span title="${item.name}">${item.name} <small>(${item.type})</small></span>
|
||||
</p>
|
||||
|
||||
<a href="${rootPath}/skinlib/show/${item.tid}" title="${trans('user.viewInSkinlib')}" class="more" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-share"></i></a>
|
||||
<span title="${trans('general.more')}" class="more" data-toggle="dropdown" aria-haspopup="true" id="more-button"><i class="fa fa-cog"></i></span>
|
||||
|
||||
<ul class="dropup dropdown-menu" aria-labelledby="more-button">
|
||||
<li><a href="javascript:renameClosetItem(${item.tid}, '${item.name}');">${trans('user.renameItem')}</a></li>
|
||||
<li><a href="javascript:removeFromCloset(${item.tid});">${trans('user.removeItem')}</a></li>
|
||||
<li><a href="javascript:setAsAvatar(${item.tid});">${trans('user.setAsAvatar')}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function renderCloset(items, category) {
|
||||
const rootPath = /(^https?.*)\/user\/closet/.exec(window.location.href)[1];
|
||||
const search = $('input[name=q]').val();
|
||||
let container = $(`#${category}-category`);
|
||||
container.html('');
|
||||
if (items.length === 0) {
|
||||
$('#closet-paginator').hide();
|
||||
if (search === '') {
|
||||
container.html(`<div class="empty-msg">
|
||||
${trans('user.emptyClosetMsg', { url: rootPath + '/skinlib?filter=' + category })}</div>`);
|
||||
} else {
|
||||
container.html(`<div class="empty-msg">${trans('general.noResult')}</div>`);
|
||||
}
|
||||
} else {
|
||||
$('#closet-paginator').show();
|
||||
for (const item of items) {
|
||||
container.append(renderClosetItemComponent(item, rootPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function reloadCloset(category, page, search) {
|
||||
Promise.resolve($.ajax({
|
||||
type: 'GET',
|
||||
url: /(^https?.*)\/user\/closet/.exec(window.location.href)[1] + '/user/closet-data',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
category: category,
|
||||
page: page,
|
||||
q: search
|
||||
}
|
||||
})).then(result => {
|
||||
renderCloset(result.items, result.category);
|
||||
let paginator = $('#closet-paginator');
|
||||
paginator.attr(`last-${result.category}-page`, page);
|
||||
paginator.jqPaginator('option', {
|
||||
currentPage: page,
|
||||
totalPages: result.total_pages
|
||||
});
|
||||
}).catch(error => showAjaxError);
|
||||
}
|
||||
|
||||
function showPlayerTexturePreview(pid) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
|
|
@ -179,6 +250,43 @@ $(document).ready(function() {
|
|||
confirmButtonText: trans('general.confirm'),
|
||||
cancelButtonText: trans('general.cancel')
|
||||
});
|
||||
|
||||
if (window.location.pathname.includes('/user/closet')) {
|
||||
Promise.resolve($.ajax({
|
||||
type: 'GET',
|
||||
url: /(^https?.*)\/user\/closet/.exec(window.location.href)[1] + '/user/closet-data',
|
||||
dataType: 'json'
|
||||
})).then(result => {
|
||||
renderCloset(result.items, result.category);
|
||||
$('#closet-paginator').jqPaginator({
|
||||
totalPages: result.total_pages,
|
||||
visiblePages: 5,
|
||||
currentPage: 1,
|
||||
first: '<li><a style="cursor: pointer;">«</a></li>',
|
||||
prev: '<li><a style="cursor: pointer;">‹</a></li>',
|
||||
next: '<li><a style="cursor: pointer;">›</a></li>',
|
||||
last: '<li><a style="cursor: pointer;">»</a></li>',
|
||||
page: '<li><a style="cursor: pointer;">{{page}}</a></li>',
|
||||
wrapper: '<ul class="pagination pagination-sm no-margin"></ul>',
|
||||
onPageChange: page => {
|
||||
reloadCloset(
|
||||
$('#skin-category').hasClass('active') ? 'skin' : 'cape',
|
||||
page,
|
||||
$('input[name=q]').val()
|
||||
);
|
||||
}
|
||||
});
|
||||
}).catch(error => showAjaxError);
|
||||
|
||||
$('input[name=q]').on('input', _.debounce(() => {
|
||||
const category = $('#skin-category').hasClass('active') ? 'skin' : 'cape';
|
||||
reloadCloset(
|
||||
category,
|
||||
1,
|
||||
$('input[name=q]').val()
|
||||
);
|
||||
}, 350));
|
||||
}
|
||||
});
|
||||
|
||||
function setTexture() {
|
||||
|
|
|
|||
|
|
@ -68,6 +68,11 @@
|
|||
signInRemainingTime: 'Available after :time hours',
|
||||
|
||||
// Closet
|
||||
emptyClosetMsg: '<p>Nothing in your closet...</p><p>Why not explore the <a href=":url">Skin Library</a> for a while?</p>',
|
||||
renameItem: 'Rename item',
|
||||
removeItem: 'Remove from closet',
|
||||
setAsAvatar: 'Set as avatar',
|
||||
viewInSkinlib: 'View in skin library',
|
||||
switch2dPreview: 'Switch to 2D Preview',
|
||||
switch3dPreview: 'Switch to 3D Preview',
|
||||
removeFromClosetNotice: 'Sure to remove this texture from your closet?',
|
||||
|
|
@ -153,10 +158,14 @@
|
|||
extracting: 'Extracting update package..'
|
||||
},
|
||||
general: {
|
||||
skin: 'Skin',
|
||||
cape: 'Cape',
|
||||
fatalError: 'Fatal Error (Please contact the author)',
|
||||
confirmLogout: 'Sure to log out?',
|
||||
confirm: 'OK',
|
||||
cancel: 'Cancel'
|
||||
cancel: 'Cancel',
|
||||
more: 'More',
|
||||
noResult: 'No result.'
|
||||
}
|
||||
};
|
||||
})(window.jQuery);
|
||||
|
|
|
|||
|
|
@ -28,13 +28,11 @@ score-intro:
|
|||
closet:
|
||||
upload: Upload Texture
|
||||
search: Search Texture
|
||||
type-to-search: Type to search
|
||||
switch-category: Switch Category
|
||||
view: View in skin library
|
||||
more: More
|
||||
set-avatar: Set as avatar
|
||||
empty-msg: |
|
||||
<p>Nothing in your closet...</p>
|
||||
<p>Why not explore the <a href=":url">Skin Library</a> for a while?</p>
|
||||
|
||||
use-as:
|
||||
button: Apply...
|
||||
|
|
|
|||
|
|
@ -68,6 +68,11 @@
|
|||
signInRemainingTime: ':time 小时后可签到',
|
||||
|
||||
// Closet
|
||||
emptyClosetMsg: '<p>衣柜里啥都没有哦~</p><p>去<a href=":url">皮肤库</a>看看吧~</p>',
|
||||
renameItem: '重命名物品',
|
||||
removeItem: '从衣柜中移除',
|
||||
setAsAvatar: '设为头像',
|
||||
viewInSkinlib: '在皮肤库中查看',
|
||||
switch2dPreview: '切换 2D 预览',
|
||||
switch3dPreview: '切换 3D 预览',
|
||||
removeFromClosetNotice: '确定要从衣柜中移除此材质吗?',
|
||||
|
|
@ -153,10 +158,14 @@
|
|||
extracting: '正在解压更新包'
|
||||
},
|
||||
general: {
|
||||
skin: '皮肤',
|
||||
cape: '披风',
|
||||
fatalError: '严重错误(请联系作者)',
|
||||
confirmLogout: '确定要登出吗?',
|
||||
confirm: '确定',
|
||||
cancel: '取消'
|
||||
cancel: '取消',
|
||||
more: '更多',
|
||||
noResult: '无结果'
|
||||
},
|
||||
vendor: {
|
||||
datatables: {
|
||||
|
|
|
|||
|
|
@ -35,13 +35,11 @@ score-intro:
|
|||
closet:
|
||||
upload: 上传材质
|
||||
search: 搜索材质
|
||||
type-to-search: 输入即搜索
|
||||
switch-category: 切换分类
|
||||
view: 在皮肤库中查看
|
||||
more: 更多
|
||||
set-avatar: 设为头像
|
||||
empty-msg: |
|
||||
<p>衣柜里啥都没有哦~</p>
|
||||
<p>去<a href=":url">皮肤库</a>看看吧~</p>
|
||||
|
||||
use-as:
|
||||
button: 使用...
|
||||
|
|
|
|||
|
|
@ -26,51 +26,30 @@
|
|||
<div class="nav-tabs-custom">
|
||||
<!-- Tabs within a box -->
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#skin-category" data-toggle="tab">{{ trans('general.skin') }}</a></li>
|
||||
<li><a href="#cape-category" data-toggle="tab">{{ trans('general.cape') }}</a></li>
|
||||
<li class="active"><a href="#skin-category" class="category-switch" data-toggle="tab">{{ trans('general.skin') }}</a></li>
|
||||
<li><a href="#cape-category" class="category-switch" data-toggle="tab">{{ trans('general.cape') }}</a></li>
|
||||
|
||||
<div style="padding: 7px;">
|
||||
<li class="pull-right" style="padding: 7px;">
|
||||
<div class="has-feedback pull-right">
|
||||
<form method="get" action="" class="user-search-form">
|
||||
<input type="text" name="q" class="form-control input-sm" placeholder="输入,回车搜索" value="{{ $q }}">
|
||||
<div class="user-search-form">
|
||||
<input type="text" name="q" class="form-control input-sm" placeholder="{{ trans('user.closet.type-to-search') }}">
|
||||
<span class="glyphicon glyphicon-search form-control-feedback"></span>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content no-padding">
|
||||
<div class="tab-pane active box-body" id="skin-category">
|
||||
@include('vendor.closet-items', ['items' => (array)array_get($items, 'skin')])
|
||||
</div>
|
||||
|
||||
<div class="tab-pane box-body" id="cape-category">
|
||||
@include('vendor.closet-items', ['items' => (array)array_get($items, 'cape')])
|
||||
</div>
|
||||
<div class="tab-pane active box-body" id="skin-category"></div>
|
||||
<div class="tab-pane box-body" id="cape-category"></div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<ul class="pagination pagination-sm no-margin pull-right">
|
||||
<?php $base_url = $q ? "?q=$q&" : "?"; ?>
|
||||
|
||||
<li><a href="{{ $base_url }}page=1">«</a></li>
|
||||
@if ($page != 1)
|
||||
<li><a href="{{ $base_url }}page={{ $page-1 }}">{{ $page - 1 }}</a></li>
|
||||
@endif
|
||||
|
||||
<li><a href="{{ $base_url }}page={{ $page }}" class="active">{{ $page }}</a></li>
|
||||
|
||||
@if ($total_pages > $page)
|
||||
<li><a href="{{ $base_url }}page={{ $page+1 }}">{{ $page+1 }}</a></li>
|
||||
@endif
|
||||
|
||||
<li><a href="{{ $base_url }}page={{ $total_pages }}">»</a></li>
|
||||
</ul>
|
||||
<p class="pull-right">{{ trans('general.pagination', ['page' => $page, 'total' => $total_pages]) }}</p>
|
||||
<div class="pull-right" id="closet-paginator" last-skin-page="1" last-cape-page="1"></div>
|
||||
</div>
|
||||
</div><!-- /.nav-tabs-custom -->
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Left col -->
|
||||
<!-- Right col -->
|
||||
<div class="col-md-4">
|
||||
|
||||
<div class="box box-default">
|
||||
|
|
|
|||
30
resources/views/vendor/closet-items.tpl
vendored
30
resources/views/vendor/closet-items.tpl
vendored
|
|
@ -1,30 +0,0 @@
|
|||
@forelse ($items as $item)
|
||||
<div class="item" tid="{{ $item->tid }}">
|
||||
<div class="item-body">
|
||||
<img src="{{ url('preview/'.$item->tid) }}.png">
|
||||
</div>
|
||||
<div class="item-footer">
|
||||
<p class="texture-name">
|
||||
<span title="{{ $item->name }}">{{ $item->name }} <small>({{ $item->type }})</small></span>
|
||||
</p>
|
||||
|
||||
<a href="{{ url('skinlib/show/'.$item->tid) }}" title="{{ trans('user.closet.view') }}" class="more" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-share"></i></a>
|
||||
<span title="{{ trans('user.closet.more') }}" class="more" data-toggle="dropdown" aria-haspopup="true" id="more-button"><i class="fa fa-cog"></i></span>
|
||||
|
||||
<ul class="dropup dropdown-menu" aria-labelledby="more-button">
|
||||
<li><a href="javascript:renameClosetItem({{ $item->tid }}, '{{ $item->name }}');">{{ trans('user.closet.rename.title') }}</a></li>
|
||||
<li><a href="javascript:removeFromCloset({{ $item->tid }});">{{ trans('user.closet.remove.title') }}</a></li>
|
||||
<li><a href="javascript:setAsAvatar({{ $item->tid }});">{{ trans('user.closet.set-avatar') }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<div class="empty-msg">
|
||||
@if($q)
|
||||
{{ trans('skinlib.general.no-result') }}
|
||||
@else
|
||||
{!! trans('user.closet.empty-msg', ['url' => url('skinlib')]) !!}
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@endforelse
|
||||
Loading…
Reference in New Issue
Block a user