JS:
requirejs(['../config'], function () {
require(['jquery', 'underscore', 'util', 'qiniu', 'bootbox', 'layzr', 'bootstrap.notify', 'user_profile', 'validate', 'product_favorite', 'jquery.dotdotdot', 'cropper'], function ($, _, util, Qiniu, bootbox, Layzr) {
var emptyContent = {
favorite : '<p class="text-center">您还没有相关喜欢</p>',
};
bootbox.setDefaults({
locale: 'zh_CN'
});
$.notifyDefaults({
type: 'danger',
delay: 4000
});
$(function () {
var minCount = 5,
archive = $('input[name=archive]').val(),
userId = $('input[name=user_id]').val(),
container = $('.product-list'),
listTemplate = _.template($('#product-list-template').html()),
previewUrl = $('input[name=preview_url]').val(),
offset = container.find('.product-item').length,
hasMore = true,
isLoading = false,
countItem = {
total : $('#total-publish-count'),
favorite : $('#total-favorite-count'),
},
layzr = new Layzr();
// project page
$('.product-list.published').on('click', 'figure, .product-name a, .unactive-edit', function(event) {
event.preventDefault();
$this = $(this);
var active = $this.closest('.product-item').data('active');
if (!active) {
$.notify('Oops... 本内容已被管理员锁定,有问题请联系管理员');
return;
};
window.location.href = $this .data('href') || $this.attr('href');
}).on('click', '.wechat', function(event) {
$('#qrcode').attr('src', $(this).data('src'));
$('#wechat-dialog').modal('show');
}).on('click', '.action-menu .action-edit, .action-menu .action-publish', function(e) {
var mobileUrl = $(this).data('mobile-url');
if ($(window).width() <= 720) {
e.preventDefault();
window.location.href = mobileUrl;
}
});
// product page
$('.product-list.favorited').on('click', 'figure', function(event) {
window.location.href = $(this) .data('href');
});
// cut out description
function initDescription() {
$('.published .dotdotdot-flag').dotdotdot({
height: 120,
after: 'a.view-more',
wrap: 'letter',
callback: function(isTruncated, orgContent) {
if (!isTruncated) {
$(this).find('a.view-more').remove();
};
},
}).removeClass('dotdotdot-flag');
$('.favorited .dotdotdot-flag').dotdotdot({
height: 90,
wrap: 'letter',
}).removeClass('dotdotdot-flag');
}
initDescription();
function loadCountData() {
var params = {
user_id: userId,
};
$.ajax({
url: '/service/user/product-count',
type: 'GET',
data: params,
}).done(function (response) {
if (response.status === 'SUCCESS') {
data = response.body;
countItem.total.html(data.totalPublishCount);
countItem.favorite.html(data.totalFavoriteCount);
}
});
}
function loadMore() {
if (!hasMore || isLoading) {
return;
};
var params = {
user_id: userId,
archive: archive,
offset: offset,
};
isLoading = true;
$.ajax({
url: '/service/user/products',
type: 'GET',
data: params,
}).done(function (response) {
if (response.status === 'SUCCESS') {
hasMore = response.body.hasMore;
products = response.body.products;
if (products && products.length <= 0) {
return false;
}
offset += products.length;
container.append(listTemplate({
products: products
}));
initDescription();
layzr.updateSelector();
layzr.update();
}
}).always(function () {
isLoading = false;
});
}
$(window).on('scroll', function () {
var $window = $(window);
if (!hasMore || isLoading) {
return;
}
if ($window.height() + $window.scrollTop() + 300 >= $(document).height()) {
loadMore();
}
});
function removeItem(item, message) {
item.remove();
loadCountData();
offset--;
if (offset <= minCount) {
loadMore();
};
if(container.find('.product-item').length == 0) {
container.append(message);
}
}
$('.self-favorited').on('unfavorite', '.favorite-icon', function () {
var currentItem = $(this).closest('.product-item');
currentItem.remove();
loadCountData();
offset--;
if (offset <= minCount) {
loadMore();
};
if(offset == 0) {
container.append(emptyContent.favorite);
}
});
$('.self-published').on('click', '.action-cancel', function () {
var $this = $(this);
currentItem = $this.closest('.product-item'),
productId = currentItem.data('product-id');
if (!productId) {
return false;
}
bootbox.confirm({
title: '取消发布',
message: '确认取消?',
size: 'small',
callback: function (result) {
if (!result) {
return;
}
$.ajax({
url: '/service/product/unpublish',
type: 'POST',
data: {
product_id: productId
}
}).done(function (response) {
if (response.status === 'SUCCESS') {
$this.removeClass('action-cancel').addClass('action-publish')
.html('发布').prop('href', previewUrl + productId)
.data('mobile-url', currentItem.data('mobile-publish-url'));
currentItem.find('figure').removeClass('banner-ribbon-published')
.addClass('banner-ribbon-draft');
}
});
}
});
return false;
}).on('click', '.action-delete', function () {
var $this = $(this);
bootbox.confirm({
title: '删除家藏',
message: '确认删除?',
size: 'small',
callback: function (result) {
if (!result) {
return;
}
var currentItem = $this.closest('.product-item'),
productId = currentItem.data('product-id');
if (!productId) {
return;
}
$.ajax({
url: '/service/product/delete',
type: 'POST',
data: {
product_id: productId
}
}).done(function (response) {
if (response.status === 'SUCCESS') {
currentItem.remove();
loadCountData();
offset--;
if (offset <= minCount) {
loadMore();
};
if (offset == 0) {
window.location.reload();
};
}
});
}
});
});
util.initGotoTop();
var $avatarImg = $('#avatar-img');
var options = {
viewMode: 1,
dragMode: 'move',
autoCrop: true,
aspectRatio: 1 / 1,
cropBoxMovable: false,
cropBoxResizable: false,
toggleDragModeOnDblclick: false,
/*minCropBoxWidth: 415,*/
rotatable: false,
guides: false,
crop: function(e) {
startX = Math.abs(Math.round(parseFloat($('.cropper-view-box>img').css('margin-left'))));
startY = Math.abs(Math.round(parseFloat($('.cropper-view-box>img').css('margin-top'))));
},
built: function () {
if ($(window).width() > 720) {
$avatarImg.cropper('setCropBoxData', {"left": 127, "top": 36, "width": 415, "height": 415});
zoomImg();
} else {
smallZoomImg();
}
}
};
var avatarKey = $avatarImg.data('avatar-key'),
domain = APP_CONFIG.cdn.domain,
currentAvatarSource = util.getCdnResource(avatarKey),
uploader = Qiniu.uploader({
runtimes: 'html5,flash,html4',
browse_button: 'avatar-img-button',
container: 'upload-container',
drop_element: 'avatar-img-button',
max_file_size: '4mb',
flash_swf_url: 'libs/plupload/Moxie.swf',
dragdrop: false,
chunk_size: '4mb',
uptoken_url: '/service/cdn/uptoken',
domain: APP_CONFIG.cdn.domain,
get_new_uptoken: true,
unique_names: true,
auto_start: true,
multi_selection: false,
filters: {
mime_types: [
{
title : 'Image files',
extensions : 'jpg,jpeg,gif,png'
}
]
},
init: {
FileUploaded: function (up, file, info) {
domain = up.getOption('domain');
var result = $.parseJSON(info),
sourceLink = 'http://' + domain + '/' + result.key;
avatarKey = result.key;
replaceImg(sourceLink);
},
Error: function (up, err, errTip) {
alert(errTip);
}
}
});
var replaceImg = function(sourceLink) {
$avatarImg.one('built.cropper', function () {
URL.revokeObjectURL(sourceLink);
}).cropper('reset').cropper('replace', sourceLink);
};
var smallZoomImg = function() {
var containerWidth = $avatarImg.cropper('getContainerData').width,
containerHeight = $avatarImg.cropper('getContainerData').height,
smallLength = containerWidth;
if (containerWidth > containerHeight) {
smallLength = containerHeight;
}
var cropBoxSize = Math.round(smallLength * 0.8),
left = Math.round((containerWidth - cropBoxSize) / 2),
top = Math.round((containerHeight - cropBoxSize) / 2),
canvasHeight = parseInt($('.cropper-canvas').css('height')),
canvasWidth = parseInt($('.cropper-canvas').css('width')),
canvasSmallLength = canvasHeight,
canvasLength = 'height';
if (canvasHeight > canvasWidth) {
canvasSmallLength = canvasWidth;
canvasLength = 'width'
}
while (canvasSmallLength < cropBoxSize) {
$avatarImg.cropper('zoom', 0.5);
canvasSmallLength = parseInt($('.cropper-canvas').css(canvasLength));
}
$avatarImg.cropper('setCropBoxData', {"left": left, "top": top, "width": cropBoxSize, "height": cropBoxSize});
};
var zoomImg = function() {
var canvasWidth = parseInt($('.cropper-canvas').css('width')),
left = 127;
/*if ($(window).width() <= 720) {
left = 78;
}*/
while (canvasWidth < 415) {
$avatarImg.cropper('zoom', 1);
canvasWidth = parseInt($('.cropper-canvas').css('width'));
}
$avatarImg.cropper('setCropBoxData', {"left": left, "top": 36, "width": 415, "height": 415});
};
$('#avatar-dialog').on('hidden.bs.modal', function () {
replaceImg(currentAvatarSource);
zoomImg();
avatarKey = $avatarImg.data('avatar-key');
})
var startX,
startY,
avatarHeight,
avatarWidth;
$('.cancel-set').on('click', function() {
$('#avatar-dialog').modal('hide');
});
$avatarImg.cropper(options);
if ($(window).width() <= 720) {
$('#avatar-dialog').on('shown.bs.modal', function (e) {
if (!currentAvatarSource) {
$avatarImg.cropper('destroy');
$avatarImg.cropper(options);
}
replaceImg(currentAvatarSource);
});
}
$('#submit-avatar').on('click', function() {
if (avatarKey) {
var height = Math.round($avatarImg.cropper('getCropBoxData').height),
zoomSize = 415 / height,
imageData = $avatarImg.cropper('getImageData');
avatarHeight = Math.round(parseFloat(imageData.height * zoomSize));
avatarWidth = Math.round(parseFloat(imageData.width * zoomSize));
startX = Math.abs(Math.round(parseFloat(startX * zoomSize)));
startY = Math.abs(Math.round(parseFloat(startY * zoomSize)));
var cropWidth = 415,
cropHeight = 415,
url = domain + '/' + avatarKey + '?imageMogr2/thumbnail/' + avatarWidth + 'x' + avatarHeight + '/crop/!' + cropWidth + 'x' + cropHeight + 'a' + startX + 'a' + startY;
$.ajax({
url: '/service/user/update-avatar',
type: 'POST',
data: {
key: avatarKey,
url: url
}
}).done(function (response) {
if (response.status === 'SUCCESS') {
var avatarImageUrl = util.getUrlByAliasName(response.body.avatar, 'avatar');
$('#user-avatar').attr('src', avatarImageUrl);
avatarKey = response.body.avatar;
replaceImg(avatarImageUrl);
$avatarImg.data('avatar-key', avatarKey);
currentAvatarSource = util.getCdnResource(response.body.avatar);
}
});
} else {
$.notify('请选择上传图片');
}
$('#avatar-dialog').modal('hide');
});
var updateInfo = function(domObj, url, flag) {
domObj.on('blur', function() {
var data = {};
if (flag === 'nickname') {
data = {
nickname: $(this).text()
}
}
if (flag === 'signature') {
data = {
signature: $(this).text()
}
}
if (flag === 'description') {
data = {
description: $(this).val()
}
}
if (isCurrentUser) {
$.ajax({
url: '/service/user/' + url,
type: 'POST',
data: data
}).done(function (response) {
if (response.status === 'SUCCESS') {
if (flag === 'description') {
var showDesc = response.body.show_description;
userDescDom.toggleClass('empty-desc', '' == showDesc);
showDesc = (showDesc == '') ? userDescDom.data('empty-desc') : showDesc;
domObj.addClass('d-none');
userDescDom.html(showDesc);
userDescDom.data('description', response.body.description);
if (userDescDom.height() > 70) {
userDescDom.html(response.body.show_description + toggleHtml);
}
createDes();
removeBr();
userDescDom.show();
}
} else if (response.status === 'INVALID') {
var errorMessage;
if (flag === 'nickname') {
errorMessage = response.errors.nickname;
}
if (flag === 'signature') {
errorMessage = response.errors.signature;
}
if (flag === 'description') {
errorMessage = response.errors.description;
}
$.notify(errorMessage[0]);
}
});
}
});
}
var userDescDom,
isCurrentUser = $('#current-user').val();
toggleHtml = '<a class="toggle" href="javascript:void(0)"><span class="read-more">查看全部</span><span class="pack-more">收起</span></a>';
if ($(window).width() > 720) {
userDescDom = $('#user-description');
} else {
userDescDom = $('#user-description-mobile');
}
$(window).resize(function() {
if ($(window).width() > 720) {
userDescDom = $('#user-description');
} else {
userDescDom = $('#user-description-mobile');
}
if (userDescDom.height() > 70) {
userDescDom.append(toggleHtml);
}
createDes();
removeBr();
});
function createDes() {
userDescDom.dotdotdot({
after: 'a.toggle',
height: 70,
wrap: 'letter'
});
}
function destroyDes() {
userDescDom.trigger('destroy');
}
function isTruncated() {
return userDescDom.triggerHandler('isTruncated');
}
function removeBr() {
var descContents = userDescDom.contents();
descContents.each(function(index, item) {
var _this = $(item);
// remove br before toggle link
if (_this.is('a.toggle') && $(descContents[index - 1]).is('br')) {
userDescDom.find('br').last().remove();
return false;
}
});
}
if (window.location.hash === '#showAll') {
userDescDom.toggleClass('opened');
destroyDes();
window.location.hash = '';
} else {
if (userDescDom.height() > 70) {
userDescDom.append(toggleHtml);
}
createDes();
removeBr();
}
if (window.location.hash === '#showMessage') {
$.notify('家藏致力于营造健康优美的生活方式,这里只刊登建筑师和手工艺人(造物者)的原创作品。通过他们展示的制作过程,让人对创作与工艺更怀尊敬之心……惜物如金,大象无形', {
type: 'success',
});
window.location.hash = '';
}
if ($(window).width() > 720) {
userDescDom.on('click', function(event) {
if (isCurrentUser) {
if (userDescDom.hasClass('opened')) {
userDescDom.toggleClass('opened');
createDes();
removeBr();
}
$('.desc-textarea').val($(this).data('description'));
$(this).hide();
$('.desc-textarea').removeClass('d-none').focus();
util.autoTextarea($('.desc-textarea')[0]);
}
});
}
userDescDom.on('click', '.toggle', function(event) {
userDescDom.toggleClass('opened');
if (userDescDom.hasClass('opened')) {
destroyDes();
} else {
createDes();
removeBr();
}
return false;
});
updateInfo($('.nickname'), 'update-nickname', 'nickname');
updateInfo($('.user-signature'), 'update-signature', 'signature');
updateInfo($('.desc-textarea'), 'update-description', 'description');
});
$('.nickname').keydown(function(event) {
if (event.keyCode == '13') {
return false;
}
});
$('.user-signature').keydown(function(event) {
if (event.keyCode == '13') {
return false;
}
});
// form submit
$(function() {
var createRow = $('.product-list .create-row');
createRow.on('click', 'form h1', function(event) {
$(this).closest('form').submit();
});
})
});
});
blade:
<?php $isCurrentUserProfile = $isCurrentUser && isset($isProfile) && $isProfile; ?>
<div class="user-profile row" data-user-id="{{ $user->user_id }}">
<div class="user-img">
@if ($avatar = data_get($user, 'avatar'))
@if ($isCurrentUser && isset($isProfile) && $isProfile)
<img id="user-avatar" class="avatar-img" data-toggle="modal" data-target="#avatar-dialog" src="{{ app('cdn')->getUrlByAliasName($avatar, 'avatar') }}" alt="头像">
@else
@if ((isset($is_follower) && $is_follower) || (isset($is_following) && $is_following))
<a href="{{ route('user_home', ['userId' => $userId]) }}" title="我的主页">
<img class="avatar-img" src="{{ app('cdn')->getUrlByAliasName($avatar, 'avatar') }}" alt="头像">
</a>
@else
<img class="avatar-img" src="{{ app('cdn')->getUrlByAliasName($avatar, 'avatar') }}" alt="头像">
@endif
@endif
@else
@if ($isCurrentUserProfile)
<img class="default-img avatar-img" id="user-avatar" data-toggle="modal" data-target="#avatar-dialog" src="{{ asset('static/image/headshot_250x250.png') }}">
@else
@if ((isset($is_follower) && $is_follower) || (isset($is_following) && $is_following))
<a href="{{ route('user_home', ['userId' => $userId]) }}" title="我的主页">
<img class="default-img avatar-img" src="{{ asset('static/image/headshot_250x250.png') }}">
</a>
@else
<img class="default-img avatar-img" src="{{ asset('static/image/headshot_250x250.png') }}">
@endif
@endif
@endif
@if (!$isCurrentUser)
<button type="button" class="btn btn-xs follow-btn
{{ $isFollowed ? 'js-unfollow-btn' : 'js-follow-btn' }}"
data-user-id="{{ $user->user_id }}">
{{ $isFollowed ? '已关注' : '+ 关注' }}
</button>
@endif
</div>
<div class="user-content">
<div class="name-group clearfix">
<div class="user-name pull-left">
@if ($isCurrentUserProfile)
<div class="nickname current-user-profile" placeholder="未命名"
tabindex="0" spellcheck="false" contenteditable="true"
>{{ $user->nickname }}</div>
@else
@if ((isset($is_follower) && $is_follower) || (isset($is_following) && $is_following))
<a href="{{ route('user_home', ['userId' => $userId]) }}" title="我的主页">
<div class="nickname" placeholder="未命名">{{ $user->nickname }}</div>
</a>
@else
<div class="nickname" placeholder="未命名">{{ $user->nickname }}</div>
@endif
@endif
<?php $userRating = $user['is_seller'] ? $user['seller_rating'] : $user['customer_rating']; ?>
<div class="user-rating pull-left {{ $userRating ? '' : 'hidden' }}">
{{ number_format($userRating ?: 0, 1) }}
</div>
<div class="user-status">
@if ($user->type === App\Models\User::TYPE_VERIFIED)
<span class="label label-info verify-label" title="{{ $verifyDescription }}">已认证</span>
@elseif ($isCurrentUser)
<a class="label label-default" href="{{ url('account/verify') }}">申请认证</a>
@else
<span class="label label-default noverify-label">未认证</span>
@endif
</div>
</div>
<div class="action-items pull-right">
@if ($isCurrentUserProfile)
<div class="dropdown pull-right">
<a class="dropdown-toggle publish-btn" data-toggle="dropdown" title="发布">
<img src="{{ asset('/static/image/icons_publish_34x34.png') }}" class="publish-btn">
</a>
<div class="dropdown-menu">
<div class="text-center project-item">
<form method="POST" action="{{ url('product/create?type=2') }}">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<h1>家</h1>
<p>发表一个作品</p>
</form>
</div>
<div class="text-center product-item">
<form method="POST" action="{{ url('product/create?type=1') }}">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<h1>藏</h1>
<p>发表一个器物</p>
</form>
</div>
</div>
</div>
@endif
</div>
</div>
<div class="user-rating-mobile">
<input class="rating-star" data-size="xs" data-min="0" data-max="5" data-step="0.1"
value="{{ number_format(($user['is_seller'] ? $user['seller_rating'] : $user['customer_rating']) ?: 0, 1) }}">
<span class="rating-score">
{{ number_format(($user['is_seller'] ? $user['seller_rating'] : $user['customer_rating']) ?: 0, 1) . '分' }}
</span>
</div>
<p class="follow-stats">
<a class="{{ isset($is_following) && $is_following ? 'active' : '' }} following"
href="{{ route('user_following', ['userId' => $userId]) }}"
>关注 <span class="following-count">{{ data_get($user, 'following_count', 0) }}</span></a> <span class="split-line">|</span>
<a class="{{ isset($is_follower) && $is_follower ? 'active' : '' }} follower"
href="{{ route('user_follower', ['userId' => $userId]) }}"
>粉丝 <span class="follower-count">{{ data_get($user, 'follower_count', 0) }}</span></a>
</p>
<input type="hidden" id="current-user" value="{{ $isCurrentUser }}">
<div class="user-signature {{ $isCurrentUserProfile ? 'current-user-profile' : '' }}" placeholder="{{ $isCurrentUserProfile ? '个性签名(点击可编辑)' : '' }}"
@if ($isCurrentUserProfile)
tabindex="1" spellcheck="false" contenteditable="true"
@endif
>{{ $user->signature }}</div>
<div class="user-description {{ $isCurrentUserProfile ? 'current-user-profile' : '' }}
{{ '' == $user->description ? 'empty-desc' : '' }}"
id="user-description" data-empty-desc="个人简介(点击可编辑)"
data-description="{{ $user->description }}">
@if ($isCurrentUserProfile && '' == $user->description)
{{ '个人简介(点击可编辑)' }}
@else
{!! $user['show_description'] !!}
@endif
</div>
<textarea class="desc-textarea d-none {{ $isCurrentUserProfile ? 'current-user-profile' : '' }}"
spellcheck="false"></textarea>
</div>
<div class="modal fade" id="avatar-dialog" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title">修改头像</h4>
</div>
<div class="modal-body avatar-body">
<div class="row" id="upload-container">
<button type="button" id="avatar-img-button" class="btn btn-default archist-submit-btn">选择图片</button>
</div>
<div class="row avatar-row">
<div class="col-sm-12 text-center pop-avatar-wrapper">
<img id="avatar-img" data-avatar-key="{{ data_get($user, 'avatar') ? data_get($user, 'avatar') : '' }}"
@if ($avatar = data_get($user, 'avatar'))
src="{{ app('cdn')->getResourceUrl($avatar) }}"
@else
src="{{ asset('/static/image/cover_image_1400x682.png') }}"
@endif
>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-default cancel-set archist-cancel-btn">取消</button>
<button class="btn btn-default archist-submit-btn" id="submit-avatar">确认</button>
</div>
</div>
</div>
</div>
</div>