Multiple live image upload (for portfolios)

remotes/origin/PR-39
ArturBaybulatov 9 years ago
parent b38ba547b5
commit 1aa50cccc9
  1. 126
      assets/index.js
  2. 1
      assets/lib/jquery-jgrowl/jquery.jgrowl.min.css
  3. 5
      common/views.py
  4. 39
      projects/forms.py
  5. 62
      projects/templates/contractor_portfolio_edit.html
  6. 1
      projects/templates/customer_project_edit.html
  7. 5
      projects/views.py
  8. 3
      templates/partials/base.html
  9. 4
      users/templates/contractor_office.html
  10. 4
      users/templates/contractor_profile.html
  11. 4
      users/templates/team_profile.html
  12. 10
      users/templates/user_financial_info_edit.html
  13. 10
      users/templates/user_profile_edit.html

@ -2,6 +2,8 @@
var API_PAGE_SIZE = 100
var LIVE_IMAGE_UPLOAD_URL = '/common/live-image-upload/create/'
var STUB_IMAGE_URL = '/static/img/profile.jpg'
@ -514,39 +516,98 @@ function loadAllPhotos(portfIds) {
// Live image upload ---------------------------------------
var $liveImageUploadContainer = $('.-live-image-upload-container').first()
var $avatarImage = $liveImageUploadContainer.find('.-avatar-image').first()
var $liveImageUpload = $liveImageUploadContainer.find('.-live-image-upload').first()
var $liveImageId = $liveImageUploadContainer.find('.-live-image-id').first()
var $liveImageDelete = $liveImageUploadContainer.find('.-live-image-delete').first()
var stubImageUrl = $liveImageUploadContainer.data('stubImageUrl')
$liveImageUpload.fileupload({
dataType: 'json',
;(function() {
var $liveImageUploadContainer = $('.-live-image-upload-container').first()
var $avatarImage = $liveImageUploadContainer.find('.-avatar-image').first()
var $liveImageUpload = $liveImageUploadContainer.find('.-live-image-upload').first()
var $liveImageId = $liveImageUploadContainer.find('.-live-image-id').first()
var $liveImageDelete = $liveImageUploadContainer.find('.-live-image-delete').first()
$avatarImage.attr('orig-src', $avatarImage.attr('src'))
done: function($evt, data) {
var image = data.result.files[0]
$liveImageUpload.fileupload({
url: LIVE_IMAGE_UPLOAD_URL,
dataType: 'json',
$avatarImage.attr('src', image.thumbnailUrl)
$liveImageId.val(image.id)
done: function($evt, data) {
var image = data.result
$avatarImage.attr('src', image.thumbnailUrl)
$liveImageId.val(image.id)
$liveImageDelete.data('url', image.deleteUrl)
$liveImageDelete.css('display', 'block')
}
})
$liveImageDelete.on('click', function($evt) {
var $that = $(this)
$liveImageDelete.data('url', image.deleteUrl)
$liveImageDelete.css('display', 'block')
}
})
$.post($that.data('url')).then(function(res) {
if (res.status == 'success') {
$avatarImage.attr('src', $avatarImage.attr('orig-src') || STUB_IMAGE_URL)
$liveImageId.val('')
$that.css('display', 'none')
}
})
})
}())
$liveImageDelete.on('click', function($evt) {
var $that = $(this)
// Live image upload (multiple) -----------------------------------
;(function() {
var $container = $('.-live-image-upload-multiple-container').first()
var $liveImageUpload = $container.find('.-live-image-upload').first()
var liveImageDeleteClass = '.-live-image-delete'
var templ = _.template($container.find('.-templ').first().html())
var $res = $container.find('.-res').first()
var images = []
$.post($that.data('url')).then(function(res) {
if (res.status == 'success') {
$avatarImage.attr('src', stubImageUrl)
$liveImageId.val('')
$that.css('display', 'none')
render()
$liveImageUpload.fileupload({
url: LIVE_IMAGE_UPLOAD_URL,
dataType: 'json',
done: function($evt, data) {
var image = data.result
images.push(image)
render()
}
})
})
$container.on('click', liveImageDeleteClass, function($evt) {
var $that = $(this)
var image = _.find({id: $that.data('imageId')}, images)
$.post(image.deleteUrl).then(function(res) {
if (res.status == 'success') {
remove(images, image)
render()
}
})
})
// Utils --------------------------------
function render() {
$res.html(templ({images: images}))
}
}())
@ -754,6 +815,10 @@ function getLocationTree(locId) {
// Utils -----------------------------------------------
var _call = Function.prototype.call
var _splice = _call.bind(Array.prototype.splice)
function humanFileSize(bytes, si) {
var thresh = si ? 1000 : 1024
@ -794,6 +859,12 @@ function format() {
}
function remove(coll, item) {
_splice(coll, _.indexOf(item, coll), 1)
return coll
}
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
@ -808,6 +879,3 @@ function getCookie(name) {
}
return cookieValue;
}

@ -0,0 +1 @@
.jGrowl{z-index:9999;color:#fff;font-size:12px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;position:fixed}.jGrowl.top-left{left:0;top:0}.jGrowl.top-right{right:0;top:0}.jGrowl.bottom-left{left:0;bottom:0}.jGrowl.bottom-right{right:0;bottom:0}.jGrowl.center{top:0;width:50%;left:25%}.jGrowl.center .jGrowl-closer,.jGrowl.center .jGrowl-notification{margin-left:auto;margin-right:auto}.jGrowl-notification{background-color:#000;opacity:.9;-ms-filter:alpha(90);filter:alpha(90);zoom:1;width:250px;padding:10px;margin:10px;text-align:left;display:none;border-radius:5px;min-height:40px}.jGrowl-notification .ui-state-highlight,.jGrowl-notification .ui-widget-content .ui-state-highlight,.jGrowl-notification .ui-widget-header .ui-state-highlight{border:1px solid #000;background:#000;color:#fff}.jGrowl-notification .jGrowl-header{font-weight:700;font-size:.85em}.jGrowl-notification .jGrowl-close{background-color:transparent;color:inherit;border:none;z-index:99;float:right;font-weight:700;font-size:1em;cursor:pointer}.jGrowl-closer{background-color:#000;opacity:.9;-ms-filter:alpha(90);filter:alpha(90);zoom:1;width:250px;padding:10px;margin:10px;display:none;border-radius:5px;padding-top:4px;padding-bottom:4px;cursor:pointer;font-size:.9em;font-weight:700;text-align:center}.jGrowl-closer .ui-state-highlight,.jGrowl-closer .ui-widget-content .ui-state-highlight,.jGrowl-closer .ui-widget-header .ui-state-highlight{border:1px solid #000;background:#000;color:#fff}@media print{.jGrowl{display:none}}

@ -92,15 +92,16 @@ class LiveImageUploadCreateView(NoCsrfMixin, LoginRequiredMixin, View):
live_img = LiveImageUpload.objects.create(file=image)
return JsonResponse({'files': [{
return JsonResponse({
'id': live_img.pk,
'name': live_img.file.name,
'size': live_img.file.size,
'url': live_img.file.url,
'thumbnailUrl': get_thumbnail(live_img.file, '235x224', crop='center').url,
'smallThumbnailUrl': get_thumbnail(live_img.file, '200x200', crop='center').url,
'deleteUrl': reverse('common:live-image-upload-delete', kwargs={'pk': live_img.pk}),
'deleteType': 'POST',
}]})
})
class LiveImageUploadDeleteView(NoCsrfMixin, LoginRequiredMixin, View):

@ -82,6 +82,8 @@ class ProjectFilterRealtyForm(forms.ModelForm):
class CustomerProjectEditForm(forms.ModelForm):
# Define a form field manually for a reverse model vield:
files = forms.ModelMultipleChoiceField(
queryset=ProjectFile.objects.none(),
widget=forms.CheckboxSelectMultiple,
@ -184,6 +186,43 @@ class PortfolioForm(forms.ModelForm):
}
class PortfolioEditForm(forms.ModelForm):
# Define a form field manually for a reverse model vield:
photos = forms.ModelMultipleChoiceField(
queryset=PortfolioPhoto.objects.none(),
widget=forms.CheckboxSelectMultiple,
required=False,
)
class Meta:
model = Portfolio
fields = (
'building_classification',
'construction_type',
'currency',
'name',
'photos',
'term_type',
)
# fields = '__all__'
#
# widgets = {
# 'construction_type': forms.Select(attrs={'class': 'selectpicker'}),
# 'building_classification': forms.Select(attrs={'class': 'selectpicker'}),
# 'currency': forms.Select(attrs={'class': 'selectpicker'}),
# 'term_type': forms.Select(attrs={'class': 'selectpicker'}),
# }
def __init__(self, *args, **kwargs):
# self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
self.fields['photos'].queryset = self.instance.photos.all()
class ProjectAnswerForm(forms.ModelForm):
text = forms.CharField(widget=forms.Textarea)

@ -1,6 +1,21 @@
{% extends 'partials/base.html' %}
{% load common_tags %}
{% load thumbnail %}
{% block head_css %}
<style>
.-live-image-upload-multiple-container .-position-relative-parent {position: relative}
.-live-image-upload-multiple-container .-image-delete,
.-live-image-upload-multiple-container .-live-image-delete {
position: absolute;
top: 0;
right: 0;
background-color: white;
}
</style>
{% endblock %}
{% block content %}
{% include 'partials/header.html' %}
@ -85,19 +100,52 @@
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<div class="textAreaBlock2 text-nn box-sizing disTab -live-image-upload-multiple-container">
<p>Фотографии</p>
{% for p in form.instance.photos.all %}
{% for photo in form.photos.field.queryset.all %}
<div class="col-lg-3">
<img src="{{ p }}" width="200" height="200"/>
<div class="-position-relative-parent" style="display: inline-block">
<a href="#" onclick="return false" class="btn close -image-delete">&times;</a>
{% thumbnail photo.img "200x200" crop="center" as img %}
<img src="{{ img.url }}">
{% endthumbnail %}
</div>
<input type="checkbox" name="{{ form.photos.html_name }}" value="{{ photo.pk }}" checked style='display: none'>
</div>
{% endfor %}
<script type="text/x-template" class="-templ">
<% images.forEach(function(image) { %>
<div class="col-lg-3">
<div class="-position-relative-parent" style="display: inline-block">
<a href="#" onclick="return false" data-image-id="<%- image.id %>" class="btn close -live-image-delete">&times;</a>
<img src="<%- image.smallThumbnailUrl %>">
</div>
<input type="hidden" name="live_images" value="<%- image.id %>">
</div>
<% }) %>
</script>
<div class="-res"></div>
<div class="col-xs-12">
<input type="file" name="image" multiple class="-live-image-upload" style="display: none">
<a href="#" onclick="$(this).closest('.-live-image-upload-multiple-container').find('.-live-image-upload').first().click(); return false" class="btn btn-default add_file_to_port">
Выберите файлы
</a>
</div>
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
{# <input type="file" name="{{ form.img.html_name }}" >#}
<input type="hidden" name="{{ form.user.html_name }}" value="{{ form.user.value }}" >
<input type="hidden" name="{{ form.images_ids.html_name }}" value="{{ form.user.value }}" >
</div>
<div class="searchF1 polsF1 polsFF links-filter">

@ -39,6 +39,7 @@
<div class="messageBlock box-sizing disTab">
<p>Дополнительно</p>
</div>
<div id="fileUploadContainer" class="col-lg-12 documentsChat">
<div class="upload">
<p id="fileUploadAddBtn" style="margin: 0">+ добавить файл (до 100 файлов)</p>

@ -45,6 +45,7 @@ from .forms import (
CustomerProjectEditForm,
CustomerProjectRestoreForm,
CustomerProjectTrashForm,
PortfolioEditForm,
PortfolioForm,
ProjectAnswerForm,
ProjectAnswerMessageForm,
@ -755,9 +756,9 @@ def contractor_portfolio_create(request): # TODO: pekopt: shit. rewrite using g
raise Http404
class ContractorPortfolioUpdateView(UpdateView):
class ContractorPortfolioUpdateView(BaseMixin, UpdateView):
model = Portfolio
form_class = PortfolioForm
form_class = PortfolioEditForm
template_name = 'contractor_portfolio_edit.html'
def get_success_url(self):

@ -24,7 +24,7 @@
<link rel='stylesheet' href='{% static "css/swiper.min.css" %}'>
<link rel='stylesheet' href='{% static "lib/jquery.fileupload/css/jquery.fileupload.css" %}'>
<link rel='stylesheet' href='{% static "js/magnific-popup.css" %}'>
{# <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jquery-jgrowl/1.4.1/jquery.jgrowl.min.css"> <!-- TODO: Download locally -->#}
<link rel="stylesheet" href='{% static "lib/jquery-jgrowl/jquery.jgrowl.min.css" %}'>
{% block head_css %}{% endblock %}
@ -70,7 +70,6 @@
<script src='{% static "lib/jquery.fileupload/js/jquery.fileupload-process.js" %}'></script>
<script src='{% static "js/jquery.magnific-popup.min.js" %}'></script>
<script src='{% static "lib/jquery.cookie/jquery.cookie.min.js" %}'></script>
<script src='{% static "lib/jquery-jgrowl/jquery.jgrowl.min.js" %}'></script>

@ -168,7 +168,7 @@
<div class="tab-content">
<div id="tab11" class="tab-pane fade in active">
<div class="galleryWork2 disTab col-lg-9 -portfolios-container">
<script type="text/template" class="-portfolio-item-templ">
<script type="text/x-template" class="-portfolio-item-templ">
<div class="col-lg-4">
<div class="insetCol box-sizing disTab">
<div class="imgGal" style="background:rgba(0, 0, 0, 0) url('<%- portfolio.photos[0].img %>') no-repeat scroll center center / cover">
@ -192,7 +192,7 @@
<div id="tab12" class="tab-pane fade">
<div class="galleryWork2 disTab -work-sells-container">
<script type="text/template" class="-work-sell-item-templ">
<script type="text/x-template" class="-work-sell-item-templ">
<div class="col-lg-4">
<div class="insetCol box-sizing disTab">
<div class="imgGal" style="background:rgba(0, 0, 0, 0) url('<%- workSell.photos[0].img %>') no-repeat scroll center center / cover ;">

@ -202,7 +202,7 @@
{% endif %}
<div class="galleryWork2 disTab -portfolios-container">
<script type="text/template" class="-portfolio-item-templ">
<script type="text/x-template" class="-portfolio-item-templ">
<div class="col-lg-4">
<div class="insetCol box-sizing disTab">
<div class="imgGal" style="background:rgba(0, 0, 0, 0) url('<%- portfolio.photos[0].img %>') no-repeat scroll center center / cover">
@ -281,7 +281,7 @@
{% endif %}
<div class="galleryWork2 disTab -work-sells-container">
<script type="text/template" class="-work-sell-item-templ">
<script type="text/x-template" class="-work-sell-item-templ">
<div class="col-lg-4">
<div class="insetCol box-sizing disTab">
<a href="#">

@ -115,7 +115,7 @@
<div class="tab-content">
<div id="tab11" class="tab-pane fade in active">
<div class="galleryWork2 disTab col-lg-9 -portfolios-container">
<script type="text/template" class="-portfolio-item-templ">
<script type="text/x-template" class="-portfolio-item-templ">
<div class="col-lg-4">
<div class="insetCol box-sizing disTab">
<div class="imgGal" style="background:rgba(0, 0, 0, 0) url('<%- portfolio.photos[0].img %>') no-repeat scroll center center / cover">
@ -139,7 +139,7 @@
<div id="tab12" class="tab-pane fade">
<div class="galleryWork2 disTab -work-sells-container">
<script type="text/template" class="-work-sell-item-templ">
<script type="text/x-template" class="-work-sell-item-templ">
<div class="col-lg-4">
<div class="insetCol box-sizing disTab">
<div class="imgGal" style="background:rgba(0, 0, 0, 0) url('<%- workSell.photos[0].img %>') no-repeat scroll center center / cover ;">

@ -5,9 +5,9 @@
{% block head_css %}
<style>
.-live-image-delete-parent {position: relative}
.-live-image-upload-container .-position-relative-parent {position: relative}
.-live-image-delete {
.-live-image-upload-container .-live-image-delete {
position: absolute;
top: 0;
right: 0;
@ -32,9 +32,9 @@
<div class="projectsBlock disTab">
<div class="col-lg-12">
<div class="col-lg-3 divCol3 -live-image-upload-container" data-stub-image-url="{% static 'img/profile.jpg' %}">
<div class="col-lg-3 divCol3 -live-image-upload-container">
<div class="avatar">
<div class="avatarInset -live-image-delete-parent">
<div class="avatarInset -position-relative-parent">
<a href="#" onclick="return false" class="btn close -live-image-delete" style="display: none">&times;</a>
{% if request.user.avatar %}
@ -49,7 +49,7 @@
<div class="menuUser upload-img disTab">
<div class="upload2 up-l1">
<input type="file" name="image" data-url="{% url 'common:live-image-upload-create' %}" class="-live-image-upload">
<input type="file" name="image" class="-live-image-upload">
<p>Загрузить фотографию</p>
</div>

@ -5,9 +5,9 @@
{% block head_css %}
<style>
.-live-image-delete-parent {position: relative}
.-live-image-upload-container .-position-relative-parent {position: relative}
.-live-image-delete {
.-live-image-upload-container .-live-image-delete {
position: absolute;
top: 0;
right: 0;
@ -32,9 +32,9 @@
<div class="projectsBlock disTab">
<div class="col-lg-12">
<div class="col-lg-3 divCol3 -live-image-upload-container" data-stub-image-url="{% static 'img/profile.jpg' %}">
<div class="col-lg-3 divCol3 -live-image-upload-container">
<div class="avatar">
<div class="avatarInset -live-image-delete-parent">
<div class="avatarInset -position-relative-parent">
<a href="#" onclick="return false" class="btn close -live-image-delete" style="display: none">&times;</a>
{% if request.user.avatar %}
@ -49,7 +49,7 @@
<div class="menuUser upload-img disTab">
<div class="upload2 up-l1">
<input type="file" name="image" data-url="{% url 'common:live-image-upload-create' %}" class="-live-image-upload">
<input type="file" name="image" class="-live-image-upload">
<p>Загрузить фотографию</p>
</div>

Loading…
Cancel
Save