add application attachments
This commit is contained in:
parent
135f3362bb
commit
4986ccd6c7
@ -9,16 +9,13 @@ import javax.validation.constraints.Max;
|
|||||||
import javax.validation.constraints.Min;
|
import javax.validation.constraints.Min;
|
||||||
import me.chyxion.tigon.model.ViewModel;
|
import me.chyxion.tigon.model.ViewModel;
|
||||||
import me.chyxion.tigon.model.ListResult;
|
import me.chyxion.tigon.model.ListResult;
|
||||||
|
import com.pudonghot.ambition.crm.model.*;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import com.pudonghot.ambition.crm.model.User;
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import com.pudonghot.ambition.crm.model.Application;
|
|
||||||
import org.hibernate.validator.constraints.NotBlank;
|
import org.hibernate.validator.constraints.NotBlank;
|
||||||
import com.pudonghot.ambition.crm.service.UserService;
|
import com.pudonghot.ambition.crm.service.UserService;
|
||||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||||
import com.pudonghot.ambition.crm.model.CustomerProperty;
|
|
||||||
import com.pudonghot.ambition.crm.model.ApplicationImage;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import com.pudonghot.ambition.crm.service.ApplicationService;
|
import com.pudonghot.ambition.crm.service.ApplicationService;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
@ -28,6 +25,8 @@ import com.pudonghot.ambition.crm.form.create.ApplicationFormForCreate;
|
|||||||
import com.pudonghot.ambition.crm.form.update.ApplicationFormForUpdate;
|
import com.pudonghot.ambition.crm.form.update.ApplicationFormForUpdate;
|
||||||
import com.pudonghot.ambition.crm.form.create.ApplicationImageFormForCreate;
|
import com.pudonghot.ambition.crm.form.create.ApplicationImageFormForCreate;
|
||||||
import com.pudonghot.ambition.crm.form.update.ApplicationImageFormForUpdate;
|
import com.pudonghot.ambition.crm.form.update.ApplicationImageFormForUpdate;
|
||||||
|
import com.pudonghot.ambition.crm.form.create.ApplicationAttachmentFormForCreate;
|
||||||
|
import com.pudonghot.ambition.crm.form.update.ApplicationAttachmentFormForUpdate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Shaun Chyxion <br>
|
* @author Shaun Chyxion <br>
|
||||||
@ -129,6 +128,26 @@ public class ApplicationController
|
|||||||
((ApplicationService) queryService).updateImage(form);
|
((ApplicationService) queryService).updateImage(form);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = "/add-attachment", method = RequestMethod.POST)
|
||||||
|
public ViewModel<ApplicationAttachment> addAttachment(
|
||||||
|
@Valid ApplicationAttachmentFormForCreate form) {
|
||||||
|
Assert.state(!form.getAttachment().isEmpty(), "Image content is empty");
|
||||||
|
form.setAdmin(getAuthUser().getUser().getData().isAdmin());
|
||||||
|
return new ViewModel<>(((ApplicationService) queryService).addAttachment(form));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = "/remove-attachment", method = RequestMethod.POST)
|
||||||
|
public void removeAttachment(@NotBlank @RequestParam("id") String id) {
|
||||||
|
final User user = getAuthUser().getUser().getData();
|
||||||
|
((ApplicationService) queryService).removeAttachment(id, user.getId(), user.isAdmin());
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = "/update-attachment", method = RequestMethod.POST)
|
||||||
|
public void updateImage(@Valid ApplicationAttachmentFormForUpdate form) {
|
||||||
|
form.setAdmin(getAuthUser().getUser().getData().isAdmin());
|
||||||
|
((ApplicationService) queryService).updateAttachment(form);
|
||||||
|
}
|
||||||
|
|
||||||
@RequiresRoles(User.ROLE_ADMIN)
|
@RequiresRoles(User.ROLE_ADMIN)
|
||||||
@RequestMapping(value = "/delete", method = RequestMethod.POST)
|
@RequestMapping(value = "/delete", method = RequestMethod.POST)
|
||||||
public void delete(@NotBlank @RequestParam("id") String id) {
|
public void delete(@NotBlank @RequestParam("id") String id) {
|
||||||
|
@ -2,13 +2,16 @@ package com.pudonghot.ambition.crm.service;
|
|||||||
|
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import com.pudonghot.ambition.crm.model.Application;
|
import com.pudonghot.ambition.crm.model.Application;
|
||||||
import com.pudonghot.ambition.crm.model.ApplicationImage;
|
|
||||||
import org.hibernate.validator.constraints.NotBlank;
|
import org.hibernate.validator.constraints.NotBlank;
|
||||||
import me.chyxion.tigon.service.BaseCrudByFormService;
|
import me.chyxion.tigon.service.BaseCrudByFormService;
|
||||||
|
import com.pudonghot.ambition.crm.model.ApplicationImage;
|
||||||
|
import com.pudonghot.ambition.crm.model.ApplicationAttachment;
|
||||||
import com.pudonghot.ambition.crm.form.update.ApplicationFormForUpdate;
|
import com.pudonghot.ambition.crm.form.update.ApplicationFormForUpdate;
|
||||||
import com.pudonghot.ambition.crm.form.create.ApplicationFormForCreate;
|
import com.pudonghot.ambition.crm.form.create.ApplicationFormForCreate;
|
||||||
import com.pudonghot.ambition.crm.form.create.ApplicationImageFormForCreate;
|
import com.pudonghot.ambition.crm.form.create.ApplicationImageFormForCreate;
|
||||||
import com.pudonghot.ambition.crm.form.update.ApplicationImageFormForUpdate;
|
import com.pudonghot.ambition.crm.form.update.ApplicationImageFormForUpdate;
|
||||||
|
import com.pudonghot.ambition.crm.form.create.ApplicationAttachmentFormForCreate;
|
||||||
|
import com.pudonghot.ambition.crm.form.update.ApplicationAttachmentFormForUpdate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Shaun Chyxion <br>
|
* @author Shaun Chyxion <br>
|
||||||
@ -37,5 +40,26 @@ public interface ApplicationService
|
|||||||
* @param form form
|
* @param form form
|
||||||
*/
|
*/
|
||||||
void updateImage(@Valid ApplicationImageFormForUpdate form);
|
void updateImage(@Valid ApplicationImageFormForUpdate form);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add attachment
|
||||||
|
* @param form form
|
||||||
|
*/
|
||||||
|
ApplicationAttachment addAttachment(@Valid ApplicationAttachmentFormForCreate form);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove attachment
|
||||||
|
* @param id attachment id
|
||||||
|
* @param userId user id
|
||||||
|
* @param admin admin
|
||||||
|
*/
|
||||||
|
void removeAttachment(@NotBlank String id, String userId, boolean admin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update attachment
|
||||||
|
* @param form form
|
||||||
|
*/
|
||||||
|
void updateAttachment(@Valid ApplicationAttachmentFormForUpdate form);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,10 +7,13 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import me.chyxion.tigon.mybatis.Search;
|
import me.chyxion.tigon.mybatis.Search;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import me.chyxion.tigon.model.ViewModel;
|
import me.chyxion.tigon.model.ViewModel;
|
||||||
import com.pudonghot.ambition.crm.model.*;
|
import com.pudonghot.ambition.crm.model.*;
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
import com.pudonghot.ambition.crm.mapper.*;
|
import com.pudonghot.ambition.crm.mapper.*;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -24,8 +27,11 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import com.pudonghot.ambition.crm.service.CustomerPermissionService;
|
import com.pudonghot.ambition.crm.service.CustomerPermissionService;
|
||||||
import com.pudonghot.ambition.crm.form.update.ApplicationFormForUpdate;
|
import com.pudonghot.ambition.crm.form.update.ApplicationFormForUpdate;
|
||||||
import com.pudonghot.ambition.crm.form.create.ApplicationFormForCreate;
|
import com.pudonghot.ambition.crm.form.create.ApplicationFormForCreate;
|
||||||
|
import com.pudonghot.ambition.crm.form.update.ApplicationFileFormForUpdate;
|
||||||
import com.pudonghot.ambition.crm.form.create.ApplicationImageFormForCreate;
|
import com.pudonghot.ambition.crm.form.create.ApplicationImageFormForCreate;
|
||||||
import com.pudonghot.ambition.crm.form.update.ApplicationImageFormForUpdate;
|
import com.pudonghot.ambition.crm.form.update.ApplicationImageFormForUpdate;
|
||||||
|
import com.pudonghot.ambition.crm.form.create.ApplicationAttachmentFormForCreate;
|
||||||
|
import com.pudonghot.ambition.crm.form.update.ApplicationAttachmentFormForUpdate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Shaun Chyxion <br>
|
* @author Shaun Chyxion <br>
|
||||||
@ -41,6 +47,8 @@ public class ApplicationServiceSupport
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ApplicationImageMapper imageMapper;
|
private ApplicationImageMapper imageMapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
private ApplicationAttachmentMapper attachmentMapper;
|
||||||
|
@Autowired
|
||||||
private DiskFileApi fileApi;
|
private DiskFileApi fileApi;
|
||||||
@Autowired
|
@Autowired
|
||||||
private CustomerMapper customerMapper;
|
private CustomerMapper customerMapper;
|
||||||
@ -62,7 +70,8 @@ public class ApplicationServiceSupport
|
|||||||
final Date now = new Date();
|
final Date now = new Date();
|
||||||
application.setDateUpdated(now);
|
application.setDateUpdated(now);
|
||||||
|
|
||||||
uploadImages(id, 1, form.getImages(), form.getImageTitles(), form.getCreatedBy());
|
uploadFiles(id, 1, form.getImages(), form.getImageTitles(), form.getCreatedBy(), ApplicationImage::new, imageMapper::insert);
|
||||||
|
uploadFiles(id, 1, form.getAttachments(), form.getAttachmentTitles(), form.getCreatedBy(), ApplicationAttachment::new, attachmentMapper::insert);
|
||||||
mapper.insert(application);
|
mapper.insert(application);
|
||||||
return toViewModel(application);
|
return toViewModel(application);
|
||||||
}
|
}
|
||||||
@ -117,11 +126,13 @@ public class ApplicationServiceSupport
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ViewModel<Application> findViewModel(final String id) {
|
public ViewModel<Application> findViewModel(final String id) {
|
||||||
final List<ApplicationImage> images = imageMapper.list(
|
final Search search =
|
||||||
new Search(ApplicationImage.APPLICATION_ID, id)
|
new Search(ApplicationImage.APPLICATION_ID, id)
|
||||||
.asc(ApplicationImage.SORT));
|
.asc(ApplicationImage.SORT);
|
||||||
|
|
||||||
return super.findViewModel(id)
|
return super.findViewModel(id)
|
||||||
.setAttr("images", images);
|
.setAttr("images", imageMapper.list(search))
|
||||||
|
.setAttr("attachments", attachmentMapper.list(search));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -132,11 +143,21 @@ public class ApplicationServiceSupport
|
|||||||
final String applicationId = form.getApplicationId();
|
final String applicationId = form.getApplicationId();
|
||||||
final String createdBy = form.getCreatedBy();
|
final String createdBy = form.getCreatedBy();
|
||||||
validatePerm(applicationId, createdBy, form.isAdmin());
|
validatePerm(applicationId, createdBy, form.isAdmin());
|
||||||
return uploadImages(applicationId,
|
return uploadFiles(applicationId,
|
||||||
imageMapper.nextSort(applicationId),
|
imageMapper.nextSort(applicationId),
|
||||||
new MultipartFile[] {form.getImage()},
|
new MultipartFile[] {form.getImage()},
|
||||||
new String[] {form.getNote()},
|
new String[] {form.getNote()},
|
||||||
createdBy).iterator().next();
|
createdBy,
|
||||||
|
ApplicationImage::new,
|
||||||
|
imageMapper::insert).iterator().next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void updateImage(final ApplicationImageFormForUpdate form) {
|
||||||
|
updateFile(form, imageMapper::find, imageMapper::listSort, imageMapper::update);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -145,23 +166,128 @@ public class ApplicationServiceSupport
|
|||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Throwable.class)
|
@Transactional(rollbackFor = Throwable.class)
|
||||||
public void removeImage(final String id, final String userId, final boolean admin) {
|
public void removeImage(final String id, final String userId, final boolean admin) {
|
||||||
final ApplicationImage appImage = imageMapper.find(id);
|
removeFile(id, userId, admin, imageMapper::find, imageMapper::delete, imageMapper::updateSort);
|
||||||
Assert.state(appImage != null, "No application image [" + id + "] found");
|
|
||||||
final String applicationId = appImage.getApplicationId();
|
|
||||||
validatePerm(applicationId, userId, admin);
|
|
||||||
fileApi.delete(imageFolder(applicationId) + "/" + id);
|
|
||||||
imageMapper.delete(id);
|
|
||||||
imageMapper.updateSort(applicationId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void updateImage(final ApplicationImageFormForUpdate form) {
|
public ApplicationAttachment addAttachment(final ApplicationAttachmentFormForCreate form) {
|
||||||
|
final String applicationId = form.getApplicationId();
|
||||||
|
final String createdBy = form.getCreatedBy();
|
||||||
|
validatePerm(applicationId, createdBy, form.isAdmin());
|
||||||
|
return uploadFiles(applicationId,
|
||||||
|
attachmentMapper.nextSort(applicationId),
|
||||||
|
new MultipartFile[] {form.getAttachment()},
|
||||||
|
new String[] {form.getNote()},
|
||||||
|
createdBy,
|
||||||
|
ApplicationAttachment::new,
|
||||||
|
attachmentMapper::insert).iterator().next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void updateAttachment(final ApplicationAttachmentFormForUpdate form) {
|
||||||
|
updateFile(form, attachmentMapper::find, attachmentMapper::listSort, attachmentMapper::update);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Throwable.class)
|
||||||
|
public void removeAttachment(final String id, final String userId, final boolean admin) {
|
||||||
|
removeFile(id, userId, admin, attachmentMapper::find, attachmentMapper::delete, attachmentMapper::updateSort);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Throwable.class)
|
||||||
|
public int delete(final String id) {
|
||||||
|
log.info("Delete application [{}].", id);
|
||||||
|
final Application app = find(id);
|
||||||
|
Assert.state(app != null, "No application [" + id + "] found");
|
||||||
|
Assert.state(!app.isEnabled(), "Application [" + id + "] is enabled");
|
||||||
|
Assert.state(customerApplicationMapper.count(
|
||||||
|
new Search(CustomerApplication.APPLICATION_ID, id)) == 0,
|
||||||
|
"Application [" + id + "] is in using");
|
||||||
|
final Search appFileSearch = new Search(ApplicationImage.APPLICATION_ID, id);
|
||||||
|
imageMapper.list(appFileSearch).forEach(
|
||||||
|
image -> fileApi.deleteById(image.getFileId()));
|
||||||
|
imageMapper.delete(appFileSearch);
|
||||||
|
|
||||||
|
attachmentMapper.list(appFileSearch).forEach(
|
||||||
|
attachment -> fileApi.deleteById(attachment.getFileId()));
|
||||||
|
attachmentMapper.delete(appFileSearch);
|
||||||
|
return mapper.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends ApplicationFile> List<T> uploadFiles(
|
||||||
|
final String applicationId,
|
||||||
|
int sort,
|
||||||
|
final MultipartFile[] files,
|
||||||
|
final String[] titles,
|
||||||
|
final String createdBy,
|
||||||
|
Supplier<T> constructor,
|
||||||
|
Consumer<T> insert) {
|
||||||
|
|
||||||
|
final List<T> appFiles = new ArrayList<>();
|
||||||
|
final Date now = new Date();
|
||||||
|
final String fileFolder = fileFolder(applicationId);
|
||||||
|
int i = 0;
|
||||||
|
for (final MultipartFile file : files) {
|
||||||
|
if (!file.isEmpty()) {
|
||||||
|
final String originalFilename = file.getOriginalFilename();
|
||||||
|
try (final InputStream ins = file.getInputStream()) {
|
||||||
|
final String fileId = idSeq.get();
|
||||||
|
final T appFile = constructor.get();
|
||||||
|
final FileInfo fileInfo = fileApi.upload(ins,
|
||||||
|
file.getSize(),
|
||||||
|
fileFolder,
|
||||||
|
fileId,
|
||||||
|
file.getContentType(),
|
||||||
|
FilenameUtils.getExtension(originalFilename),
|
||||||
|
originalFilename);
|
||||||
|
appFile.setId(fileId);
|
||||||
|
appFile.setApplicationId(applicationId);
|
||||||
|
appFile.setFileId(fileInfo.getId());
|
||||||
|
appFile.setUrl(fileInfo.getUrl());
|
||||||
|
appFile.setSort(sort++);
|
||||||
|
appFile.setEnabled(true);
|
||||||
|
appFile.setNote(StringUtils.defaultIfBlank(titles[i], originalFilename));
|
||||||
|
appFile.setCreatedBy(createdBy);
|
||||||
|
appFile.setDateCreated(now);
|
||||||
|
// insert
|
||||||
|
insert.accept(appFile);
|
||||||
|
appFiles.add(appFile);
|
||||||
|
}
|
||||||
|
catch (final IOException e) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Read upload file [" + originalFilename + "] error cased", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log.info("Application file [{}] is empty, ignore.", file);
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
return appFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends ApplicationFile> void updateFile(
|
||||||
|
final ApplicationFileFormForUpdate form,
|
||||||
|
final Function<String, T> finder,
|
||||||
|
final Function<String, List<T>> listSort,
|
||||||
|
final Consumer<T> updater) {
|
||||||
|
|
||||||
final String id = form.getId();
|
final String id = form.getId();
|
||||||
final ApplicationImage appImage = imageMapper.find(id);
|
final T appImage = finder.apply(id);
|
||||||
Assert.state(appImage != null, "No application image [" + id + "] found");
|
Assert.state(appImage != null, "No application file [" + id + "] found");
|
||||||
final String applicationId = appImage.getApplicationId();
|
final String applicationId = appImage.getApplicationId();
|
||||||
final String updatedBy = form.getUpdatedBy();
|
final String updatedBy = form.getUpdatedBy();
|
||||||
validatePerm(applicationId, updatedBy, form.isAdmin());
|
validatePerm(applicationId, updatedBy, form.isAdmin());
|
||||||
@ -180,75 +306,31 @@ public class ApplicationServiceSupport
|
|||||||
appImage.setNote(form.getNote());
|
appImage.setNote(form.getNote());
|
||||||
appImage.setDateUpdated(new Date());
|
appImage.setDateUpdated(new Date());
|
||||||
appImage.setUpdatedBy(updatedBy);
|
appImage.setUpdatedBy(updatedBy);
|
||||||
imageMapper.update(appImage);
|
|
||||||
|
updater.accept(appImage);
|
||||||
|
|
||||||
if (sortUpdated) {
|
if (sortUpdated) {
|
||||||
imageMapper.listSort(applicationId).forEach(imageMapper::update);
|
listSort.apply(applicationId).forEach(updater);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private <T extends ApplicationFile> void removeFile(
|
||||||
* {@inheritDoc}
|
final String id,
|
||||||
*/
|
final String userId,
|
||||||
@Override
|
final boolean admin,
|
||||||
@Transactional(rollbackFor = Throwable.class)
|
final Function<String, T> finder,
|
||||||
public int delete(final String id) {
|
final Function<String, Integer> deleter,
|
||||||
log.info("Delete application [{}].", id);
|
final Function<String, Integer> sortUpdater) {
|
||||||
final Application app = find(id);
|
final T appFile = finder.apply(id);
|
||||||
Assert.state(app != null, "No application [" + id + "] found");
|
Assert.state(appFile != null, "No application file [" + id + "] found");
|
||||||
Assert.state(!app.isEnabled(), "Application [" + id + "] is enabled");
|
final String applicationId = appFile.getApplicationId();
|
||||||
Assert.state(customerApplicationMapper.count(
|
validatePerm(applicationId, userId, admin);
|
||||||
new Search(CustomerApplication.APPLICATION_ID, id)) == 0,
|
fileApi.delete(fileFolder(applicationId) + "/" + id);
|
||||||
"Application [" + id + "] is in using");
|
deleter.apply(id);
|
||||||
final Search appImageSearch = new Search(ApplicationImage.APPLICATION_ID, id);
|
sortUpdater.apply(applicationId);
|
||||||
imageMapper.list(appImageSearch).forEach(
|
|
||||||
image -> fileApi.deleteById(image.getFileId()));
|
|
||||||
imageMapper.delete(appImageSearch);
|
|
||||||
return mapper.delete(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ApplicationImage> uploadImages(
|
private String fileFolder(final String appId) {
|
||||||
final String applicationId,
|
|
||||||
int sort,
|
|
||||||
final MultipartFile[] images,
|
|
||||||
final String[] titles,
|
|
||||||
final String createdBy) {
|
|
||||||
|
|
||||||
final List<ApplicationImage> imagesRtn = new ArrayList<>();
|
|
||||||
final Date now = new Date();
|
|
||||||
final String imageFolder = imageFolder(applicationId);
|
|
||||||
int i = 0;
|
|
||||||
for (final MultipartFile image : images) {
|
|
||||||
if (!image.isEmpty()) {
|
|
||||||
try (final InputStream ins = image.getInputStream()) {
|
|
||||||
final String imageId = idSeq.get();
|
|
||||||
final ApplicationImage appImage = new ApplicationImage();
|
|
||||||
final FileInfo fileInfo = fileApi.uploadImage(ins, imageFolder, imageId);
|
|
||||||
appImage.setId(imageId);
|
|
||||||
appImage.setApplicationId(applicationId);
|
|
||||||
appImage.setFileId(fileInfo.getId());
|
|
||||||
appImage.setUrl(fileInfo.getUrl());
|
|
||||||
appImage.setSort(sort++);
|
|
||||||
appImage.setEnabled(true);
|
|
||||||
appImage.setNote(titles[i]);
|
|
||||||
appImage.setCreatedBy(createdBy);
|
|
||||||
appImage.setDateCreated(now);
|
|
||||||
imageMapper.insert(appImage);
|
|
||||||
imagesRtn.add(appImage);
|
|
||||||
}
|
|
||||||
catch (final IOException e) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Read upload image [" + image.getOriginalFilename() + "] error cased", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.info("Application image [{}] is empty, ignore.", image);
|
|
||||||
}
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
return imagesRtn;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String imageFolder(final String appId) {
|
|
||||||
return "app/" + appId;
|
return "app/" + appId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,4 +19,5 @@ spring.http.multipart.max-request-size=1024MB
|
|||||||
|
|
||||||
# File
|
# File
|
||||||
file.base-path=http://127.0.0.1:1217/lm-f/
|
file.base-path=http://127.0.0.1:1217/lm-f/
|
||||||
file.base-dir=/Users/chyxion/Workspaces/ambition-crm/files/
|
# file.base-dir=/Users/chyxion/Workspaces/ambition-crm/files/
|
||||||
|
file.base-dir=/Users/donghuang/Documents/Workspaces/ambition-crm/files/
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.pudonghot.ambition.crm.mapper;
|
||||||
|
|
||||||
|
import me.chyxion.tigon.mybatis.BaseMapper;
|
||||||
|
import com.pudonghot.ambition.crm.model.ApplicationAttachment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Shaun Chyxion <br>
|
||||||
|
* chyxion@163.com <br>
|
||||||
|
* Mar 11, 2018 11:39:49
|
||||||
|
*/
|
||||||
|
public interface ApplicationAttachmentMapper
|
||||||
|
extends ApplicationFileMapper<ApplicationAttachment>,
|
||||||
|
BaseMapper<String, ApplicationAttachment> {
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
/**
|
||||||
|
* @author Donghuang <br>
|
||||||
|
* donghuang@wacai.com <br>
|
||||||
|
* May 17, 2018 17:09:24
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
<!DOCTYPE mapper PUBLIC
|
||||||
|
"-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.pudonghot.ambition.crm.mapper.ApplicationAttachmentMapper">
|
||||||
|
|
||||||
|
<select id="nextSort" resultType="int">
|
||||||
|
<include refid="com.pudonghot.ambition.crm.mapper.ApplicationFileMapper.nextSort" />
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<update id="updateSort">
|
||||||
|
<include refid="com.pudonghot.ambition.crm.mapper.ApplicationFileMapper.updateSort" />
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<select id="listSort" resultType="com.pudonghot.ambition.crm.model.ApplicationAttachment">
|
||||||
|
<include refid="com.pudonghot.ambition.crm.mapper.ApplicationFileMapper.listSort" />
|
||||||
|
</select>
|
||||||
|
</mapper>
|
@ -0,0 +1,38 @@
|
|||||||
|
package com.pudonghot.ambition.crm.mapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.hibernate.validator.constraints.NotBlank;
|
||||||
|
import com.pudonghot.ambition.crm.model.ApplicationFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Shaun Chyxion <br>
|
||||||
|
* chyxion@163.com <br>
|
||||||
|
* Mar 11, 2018 11:39:49
|
||||||
|
*/
|
||||||
|
interface ApplicationFileMapper<T extends ApplicationFile> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* find next sort
|
||||||
|
* @param applicationId application id
|
||||||
|
* @return next sort
|
||||||
|
*/
|
||||||
|
int nextSort(@NotBlank @Param("applicationId") String applicationId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update sort
|
||||||
|
* @param applicationId application id
|
||||||
|
* @return effected rows
|
||||||
|
*/
|
||||||
|
int updateSort(@NotBlank @Param("applicationId") String applicationId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list application files
|
||||||
|
* @param applicationId
|
||||||
|
* @return images
|
||||||
|
*/
|
||||||
|
List<T> listSort(
|
||||||
|
@NotBlank
|
||||||
|
@Param("applicationId")
|
||||||
|
String applicationId);
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
/**
|
||||||
|
* @author Shaun Chyxion <br>
|
||||||
|
* chyxion@163.com <br>
|
||||||
|
* May 17, 2018 17:29:10
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
<!DOCTYPE mapper PUBLIC
|
||||||
|
"-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.pudonghot.ambition.crm.mapper.ApplicationFileMapper">
|
||||||
|
|
||||||
|
<sql id="nextSort">
|
||||||
|
select if (application_id, max(sort) + 1, 1)
|
||||||
|
from <include refid="table" />
|
||||||
|
where application_id = #{applicationId}
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<sql id="updateSort">
|
||||||
|
update <include refid="table" /> a
|
||||||
|
join (
|
||||||
|
select id, @cur_row := @cur_row + 1 sort
|
||||||
|
from <include refid="table" />
|
||||||
|
join (select @cur_row := 0) r
|
||||||
|
where application_id = #{applicationId}
|
||||||
|
order by <include refid="table" />.sort) s
|
||||||
|
on a.id = s.id
|
||||||
|
set a.sort = s.sort
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<sql id="listSort">
|
||||||
|
select id, file_id,
|
||||||
|
url, application_id,
|
||||||
|
created_by, date_created,
|
||||||
|
updated_by, date_updated,
|
||||||
|
enabled, note,
|
||||||
|
@cur_row := @cur_row + 1 sort
|
||||||
|
from <include refid="table" />
|
||||||
|
join (select @cur_row := 0) r
|
||||||
|
where application_id = #{applicationId}
|
||||||
|
order by <include refid="table" />.sort
|
||||||
|
</sql>
|
||||||
|
</mapper>
|
@ -1,9 +1,6 @@
|
|||||||
package com.pudonghot.ambition.crm.mapper;
|
package com.pudonghot.ambition.crm.mapper;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import me.chyxion.tigon.mybatis.BaseMapper;
|
import me.chyxion.tigon.mybatis.BaseMapper;
|
||||||
import org.apache.ibatis.annotations.Param;
|
|
||||||
import org.hibernate.validator.constraints.NotBlank;
|
|
||||||
import com.pudonghot.ambition.crm.model.ApplicationImage;
|
import com.pudonghot.ambition.crm.model.ApplicationImage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -11,29 +8,7 @@ import com.pudonghot.ambition.crm.model.ApplicationImage;
|
|||||||
* chyxion@163.com <br>
|
* chyxion@163.com <br>
|
||||||
* Mar 11, 2018 11:39:49
|
* Mar 11, 2018 11:39:49
|
||||||
*/
|
*/
|
||||||
public interface ApplicationImageMapper extends BaseMapper<String, ApplicationImage> {
|
public interface ApplicationImageMapper
|
||||||
|
extends ApplicationFileMapper<ApplicationImage>,
|
||||||
/**
|
BaseMapper<String, ApplicationImage> {
|
||||||
* find next sort
|
|
||||||
* @param applicationId application id
|
|
||||||
* @return next sort
|
|
||||||
*/
|
|
||||||
int nextSort(@NotBlank @Param("applicationId") String applicationId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* update sort
|
|
||||||
* @param applicationId application id
|
|
||||||
* @return effected rows
|
|
||||||
*/
|
|
||||||
int updateSort(@NotBlank @Param("applicationId") String applicationId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list application images
|
|
||||||
* @param applicationId
|
|
||||||
* @return images
|
|
||||||
*/
|
|
||||||
List<ApplicationImage> listSort(
|
|
||||||
@NotBlank
|
|
||||||
@Param("applicationId")
|
|
||||||
String applicationId);
|
|
||||||
}
|
}
|
||||||
|
@ -12,33 +12,14 @@
|
|||||||
<mapper namespace="com.pudonghot.ambition.crm.mapper.ApplicationImageMapper">
|
<mapper namespace="com.pudonghot.ambition.crm.mapper.ApplicationImageMapper">
|
||||||
|
|
||||||
<select id="nextSort" resultType="int">
|
<select id="nextSort" resultType="int">
|
||||||
select if (application_id, max(sort) + 1, 1)
|
<include refid="com.pudonghot.ambition.crm.mapper.ApplicationFileMapper.nextSort" />
|
||||||
from <include refid="table" />
|
|
||||||
where application_id = #{applicationId}
|
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<update id="updateSort">
|
<update id="updateSort">
|
||||||
update crm_application_image a
|
<include refid="com.pudonghot.ambition.crm.mapper.ApplicationFileMapper.updateSort" />
|
||||||
join (
|
|
||||||
select id, @cur_row := @cur_row + 1 sort
|
|
||||||
from crm_application_image
|
|
||||||
join (select @cur_row := 0) r
|
|
||||||
where application_id = #{applicationId}
|
|
||||||
order by crm_application_image.sort) s
|
|
||||||
on a.id = s.id
|
|
||||||
set a.sort = s.sort
|
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
<select id="listSort" resultType="com.pudonghot.ambition.crm.model.ApplicationImage">
|
<select id="listSort" resultType="com.pudonghot.ambition.crm.model.ApplicationImage">
|
||||||
select id, file_id,
|
<include refid="com.pudonghot.ambition.crm.mapper.ApplicationFileMapper.listSort" />
|
||||||
url, application_id,
|
|
||||||
created_by, date_created,
|
|
||||||
updated_by, date_updated,
|
|
||||||
enabled, note,
|
|
||||||
@cur_row := @cur_row + 1 sort
|
|
||||||
from <include refid="table" />
|
|
||||||
join (select @cur_row := 0) r
|
|
||||||
where application_id = #{applicationId}
|
|
||||||
order by crm_application_image.sort
|
|
||||||
</select>
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
@ -22,7 +22,18 @@
|
|||||||
(select group_concat(note order by sort separator 0x1d) from
|
(select group_concat(note order by sort separator 0x1d) from
|
||||||
crm_application_image
|
crm_application_image
|
||||||
where application_id = a.id
|
where application_id = a.id
|
||||||
group by application_id) image_titles
|
group by application_id) image_titles,
|
||||||
|
|
||||||
|
(select group_concat(url order by sort separator 0x1d) from
|
||||||
|
crm_application_attachment
|
||||||
|
where application_id = a.id
|
||||||
|
group by application_id) attachments,
|
||||||
|
|
||||||
|
(select group_concat(note order by sort separator 0x1d) from
|
||||||
|
crm_application_attachment
|
||||||
|
where application_id = a.id
|
||||||
|
group by application_id) attachment_titles
|
||||||
|
|
||||||
from
|
from
|
||||||
<include refid="table" /> a
|
<include refid="table" /> a
|
||||||
<include refid="Tigon.search" />
|
<include refid="Tigon.search" />
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns:context="http://www.springframework.org/schema/context"
|
|
||||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||||
http://www.springframework.org/schema/context
|
|
||||||
http://www.springframework.org/schema/context/spring-context.xsd">
|
|
||||||
<!--<context:component-scan base-package="cn.com.flaginfo.umsapp.ota" /> -->
|
|
||||||
</beans>
|
</beans>
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.pudonghot.ambition.crm.form.create;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import me.chyxion.tigon.form.FC2;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import org.hibernate.validator.constraints.NotBlank;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Donghuang <br>
|
||||||
|
* donghuang@wacai.com <br>
|
||||||
|
* May 17, 2018 17:12:56
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class ApplicationAttachmentFormForCreate extends FC2<String> {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
// current user is admin
|
||||||
|
private boolean admin;
|
||||||
|
@NotBlank
|
||||||
|
private String applicationId;
|
||||||
|
@NotNull
|
||||||
|
private MultipartFile attachment;
|
||||||
|
}
|
@ -30,4 +30,6 @@ public class ApplicationFormForCreate extends FC2<String> {
|
|||||||
private String owner;
|
private String owner;
|
||||||
private String[] imageTitles;
|
private String[] imageTitles;
|
||||||
private MultipartFile[] images;
|
private MultipartFile[] images;
|
||||||
|
private String[] attachmentTitles;
|
||||||
|
private MultipartFile[] attachments;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.pudonghot.ambition.crm.form.update;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Donghuang <br>
|
||||||
|
* donghuang@wacai.com <br>
|
||||||
|
* May 17, 2018 17:12:46
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class ApplicationAttachmentFormForUpdate extends ApplicationFileFormForUpdate {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.pudonghot.ambition.crm.form.update;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import me.chyxion.tigon.form.FU2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Shaun Chyxion <br>
|
||||||
|
* chyxion@163.com <br>
|
||||||
|
* May 17, 2018 17:50:22
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class ApplicationFileFormForUpdate extends FU2<String, String> {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
// current user is admin
|
||||||
|
private boolean admin;
|
||||||
|
private float sort;
|
||||||
|
}
|
@ -2,7 +2,6 @@ package com.pudonghot.ambition.crm.form.update;
|
|||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import me.chyxion.tigon.form.FU2;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @version 0.0.1
|
* @version 0.0.1
|
||||||
@ -13,10 +12,6 @@ import me.chyxion.tigon.form.FU2;
|
|||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public class ApplicationImageFormForUpdate extends FU2<String, String> {
|
public class ApplicationImageFormForUpdate extends ApplicationFileFormForUpdate {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
// current user is admin
|
|
||||||
private boolean admin;
|
|
||||||
private float sort;
|
|
||||||
}
|
}
|
||||||
|
@ -32,4 +32,8 @@ public class Application extends M3<String, String> {
|
|||||||
private String images;
|
private String images;
|
||||||
@Transient
|
@Transient
|
||||||
private String imageTitles;
|
private String imageTitles;
|
||||||
|
@Transient
|
||||||
|
private String attachments;
|
||||||
|
@Transient
|
||||||
|
private String attachmentTitles;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.pudonghot.ambition.crm.model;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import me.chyxion.tigon.mybatis.Table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Shaun Chyxion <br>
|
||||||
|
* chyxion@163.com <br>
|
||||||
|
* May 17, 2018 17:02:51
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Table("crm_application_attachment")
|
||||||
|
public class ApplicationAttachment extends ApplicationFile {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.pudonghot.ambition.crm.model;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import me.chyxion.tigon.model.M3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Donghuang <br>
|
||||||
|
* donghuang@wacai.com <br>
|
||||||
|
* May 17, 2018 17:23:39
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class ApplicationFile extends M3<String, String> {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
// Column Names
|
||||||
|
public static final String FILE_ID = "file_id";
|
||||||
|
public static final String URL = "url";
|
||||||
|
public static final String APPLICATION_ID = "application_id";
|
||||||
|
public static final String SORT = "sort";
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
private String fileId;
|
||||||
|
private String url;
|
||||||
|
private String applicationId;
|
||||||
|
private float sort;
|
||||||
|
}
|
@ -2,7 +2,6 @@ package com.pudonghot.ambition.crm.model;
|
|||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import me.chyxion.tigon.model.M3;
|
|
||||||
import me.chyxion.tigon.mybatis.Table;
|
import me.chyxion.tigon.mybatis.Table;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -13,18 +12,6 @@ import me.chyxion.tigon.mybatis.Table;
|
|||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@Table("crm_application_image")
|
@Table("crm_application_image")
|
||||||
public class ApplicationImage extends M3<String, String> {
|
public class ApplicationImage extends ApplicationFile {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
// Column Names
|
|
||||||
public static final String FILE_ID = "file_id";
|
|
||||||
public static final String URL = "url";
|
|
||||||
public static final String APPLICATION_ID = "application_id";
|
|
||||||
public static final String SORT = "sort";
|
|
||||||
|
|
||||||
// Properties
|
|
||||||
private String fileId;
|
|
||||||
private String url;
|
|
||||||
private String applicationId;
|
|
||||||
private float sort;
|
|
||||||
}
|
}
|
||||||
|
51
web/app/components/file-input.js
Normal file
51
web/app/components/file-input.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import Ember from 'ember';
|
||||||
|
import Component from '@ember/component';
|
||||||
|
import { computed } from '@ember/object';
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
file: {},
|
||||||
|
value: computed.alias('file.file'),
|
||||||
|
'no-file': 'Choose file',
|
||||||
|
'btn-choose': 'Choose file',
|
||||||
|
didReceiveAttrs() {
|
||||||
|
const me = this;
|
||||||
|
me._super(...arguments);
|
||||||
|
},
|
||||||
|
didInsertElement() {
|
||||||
|
let me = this;
|
||||||
|
me._super(...arguments);
|
||||||
|
|
||||||
|
me.$('input[type=file]').ace_file_input({
|
||||||
|
no_file: me.get('no-file'),
|
||||||
|
btn_choose: me.get('btn-choose'),
|
||||||
|
btn_change: null,
|
||||||
|
droppable: true,
|
||||||
|
style: 'well',
|
||||||
|
no_icon: 'ace-icon fa fa-files-o',
|
||||||
|
|
||||||
|
// thumbnail: 'large',
|
||||||
|
// allowExt: ['jpg', 'jpeg', 'png', 'gif'],
|
||||||
|
// allowMime: ['image/jpg', 'image/jpeg', 'image/png', 'image/gif'],
|
||||||
|
|
||||||
|
before_change: function() {
|
||||||
|
let filename = me.$(this).val();
|
||||||
|
me.set('value', filename);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
before_remove: function() {
|
||||||
|
let filename = me.$(this).val();
|
||||||
|
me.set('value', null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}).on('file.error.ace', function(e, info) {
|
||||||
|
if (me.$(this)[0] === e.target) {
|
||||||
|
if (info.error_count['ext'] > 0 || info.error_count['mime'] > 0) {
|
||||||
|
me.get('message').warn('Invalid file');
|
||||||
|
}
|
||||||
|
else if(info.error_count['size'] > 0) {
|
||||||
|
me.get('message').warn('File size too large');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
@ -15,17 +15,22 @@ export default BaseRoute.extend({
|
|||||||
return RSVP.hash({
|
return RSVP.hash({
|
||||||
enabled: true,
|
enabled: true,
|
||||||
images: [{}],
|
images: [{}],
|
||||||
|
attachments: [{}],
|
||||||
users: this.get('store').ajaxGet('user/list-for-select')
|
users: this.get('store').ajaxGet('user/list-for-select')
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
addImage() {
|
addImage() {
|
||||||
const me = this;
|
this.get('controller.model.images').pushObject({});
|
||||||
me.get('controller.model.images').pushObject({});
|
|
||||||
},
|
},
|
||||||
removeImage(image) {
|
removeImage(image) {
|
||||||
const me = this;
|
this.get('controller.model.images').removeObject(image);
|
||||||
me.get('controller.model.images').removeObject(image);
|
|
||||||
},
|
},
|
||||||
|
addAttachment() {
|
||||||
|
this.get('controller.model.attachments').pushObject({});
|
||||||
|
},
|
||||||
|
removeAttachment(attachment) {
|
||||||
|
this.get('controller.model.attachments').removeObject(attachment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -23,6 +23,7 @@ export default BaseEditRoute.extend({
|
|||||||
},
|
},
|
||||||
submitAddImage() {
|
submitAddImage() {
|
||||||
const me = this;
|
const me = this;
|
||||||
|
me.set('controller.errors', {});
|
||||||
if (me.get('controller.model.image.file')) {
|
if (me.get('controller.model.image.file')) {
|
||||||
me.set('controller.errors', {});
|
me.set('controller.errors', {});
|
||||||
me.get('store').ajaxPost('application/add-image',
|
me.get('store').ajaxPost('application/add-image',
|
||||||
@ -49,30 +50,79 @@ export default BaseEditRoute.extend({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
moveUp(image) {
|
moveImageUp(image) {
|
||||||
let me = this;
|
const me = this;
|
||||||
let images = me.get('controller.model.images');
|
me.moveUp(me.get('controller.model.images'), image, 'updateImage');
|
||||||
if (images && images.length > 1) {
|
},
|
||||||
let index = images.indexOf(image);
|
moveImageDown(image) {
|
||||||
images.removeObject(image);
|
const me = this;
|
||||||
images.insertAt(index - 1, image);
|
me.moveDown(me.get('controller.model.images'), image, 'updateImage');
|
||||||
--image.sort;
|
},
|
||||||
me.updateImage(image);
|
// attachments
|
||||||
|
addAttachment() {
|
||||||
|
const me = this;
|
||||||
|
me.set('controller.model.addAttachment', true);
|
||||||
|
me.set('controller.model.attachment', {});
|
||||||
|
},
|
||||||
|
submitAddAttachment() {
|
||||||
|
const me = this;
|
||||||
|
me.set('controller.errors', {});
|
||||||
|
if (me.get('controller.model.attachment.file')) {
|
||||||
|
me.get('store').ajaxPost('application/add-attachment',
|
||||||
|
new FormData($('#form_' + me.get('controller.model.id'))[0]))
|
||||||
|
.then(attachment => {
|
||||||
|
me.get('controller.model.attachments').pushObject(attachment);
|
||||||
|
me.set('controller.model.addAttachment', false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
me.set('controller.errors.attachment', ['No attachment file selected']);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
moveDown(image) {
|
closeAddAttachment() {
|
||||||
let me = this;
|
const me = this;
|
||||||
let images = me.get('controller.model.images');
|
me.set('controller.model.addAttachment', false);
|
||||||
if (images && images.length > 1) {
|
},
|
||||||
let index = images.indexOf(image);
|
removeAttachment(attachment) {
|
||||||
images.removeObject(image);
|
const me = this;
|
||||||
images.insertAt(index + 1, image);
|
me.get('dialog').confirm('Are you sure to remove attachment?', () => {
|
||||||
++image.sort;
|
me.get('store').ajaxPost('application/remove-attachment', attachment).then(() => {
|
||||||
me.updateImage(image);
|
me.get('message').alert('Attachment removed');
|
||||||
}
|
me.get('controller.model.attachments').removeObject(attachment);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
moveAttachmentUp(attachment) {
|
||||||
|
const me = this;
|
||||||
|
me.moveUp(me.get('controller.model.attachments'), attachment, 'updateAttachment');
|
||||||
|
},
|
||||||
|
moveAttachmentDown(attachment) {
|
||||||
|
const me = this;
|
||||||
|
me.moveDown(me.get('controller.model.attachments'), attachment, 'updateAttachment');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateImage(image) {
|
updateImage(image) {
|
||||||
this.get('ajax').doPost('application/update-image', image, false);
|
this.get('ajax').doPost('application/update-image', image, false);
|
||||||
|
},
|
||||||
|
updateAttachment(attachment) {
|
||||||
|
this.get('ajax').doPost('application/update-attachment', attachment, false);
|
||||||
|
},
|
||||||
|
moveUp(files, file, update) {
|
||||||
|
if (files && files.length > 1) {
|
||||||
|
let index = files.indexOf(file);
|
||||||
|
files.removeObject(file);
|
||||||
|
files.insertAt(index - 1, file);
|
||||||
|
--file.sort;
|
||||||
|
this[update](file);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
moveDown(files, file, update) {
|
||||||
|
if (files && files.length > 1) {
|
||||||
|
let index = files.indexOf(file);
|
||||||
|
files.removeObject(file);
|
||||||
|
files.insertAt(index + 1, file);
|
||||||
|
++file.sort;
|
||||||
|
this[update](file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -38,6 +38,22 @@
|
|||||||
<div class="space-12"></div>
|
<div class="space-12"></div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/with}}
|
{{/with}}
|
||||||
|
<hr />
|
||||||
|
{{#with (customer-application-images model.attachments model.attachmentTitles) as |attachments|}}
|
||||||
|
{{#if attachments.length}}
|
||||||
|
<div class="row">
|
||||||
|
{{#each attachments as |attachment index|}}
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<a href="{{attachment.image}}" title="{{attachment.title}}" target="_blank">
|
||||||
|
<i class="ace-icon fa fa-paperclip bigger-120 grey"></i>
|
||||||
|
{{if attachment.title attachment.title 'Attachment'}}
|
||||||
|
</a>
|
||||||
|
<div class="space-2"></div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/with}}
|
||||||
</div>
|
</div>
|
||||||
{{/modal-dialog}}
|
{{/modal-dialog}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
1
web/app/templates/components/file-input.hbs
Normal file
1
web/app/templates/components/file-input.hbs
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{input type='file' name=name}}
|
@ -1,5 +1,5 @@
|
|||||||
<label for="{{if name name idField}}" class="{{get this 'label-class'}} control-label no-padding-right"> {{label}} </label>
|
<label for="{{if name name idField}}" class="{{label-class}} control-label no-padding-right"> {{label}} </label>
|
||||||
<div class="{{get this 'input-class'}}">
|
<div class="{{input-class}}">
|
||||||
|
|
||||||
{{#if hasBlock}}
|
{{#if hasBlock}}
|
||||||
{{yield}}
|
{{yield}}
|
||||||
|
@ -40,6 +40,34 @@
|
|||||||
</div>
|
</div>
|
||||||
{{/form-input}}
|
{{/form-input}}
|
||||||
|
|
||||||
|
{{#form-input name='attachment' label='Attachments'}}
|
||||||
|
<div class="widget-box transparent">
|
||||||
|
<div class="widget-header widget-header-small">
|
||||||
|
<div class="widget-toolbar action-buttons">
|
||||||
|
<a href="#" data-rel="tooltip" title="Add Attachment" {{action (route-action 'addAttachment')}}>
|
||||||
|
<i class="ace-icon fa fa-plus-circle green"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="widget-body">
|
||||||
|
<div class="widget-main padding-4">
|
||||||
|
{{#each model.attachments as |attachment i|}}
|
||||||
|
<div class="col-xs-6 no-padding-left">
|
||||||
|
<div class="space-4"></div>
|
||||||
|
{{input name=(concat 'attachmentTitles[' i ']') class='width-80' placeholder='Attachment name' value=image.title}}
|
||||||
|
|
||||||
|
<a href="#" class="red" data-rel="tooltip" title="Remove Attachment" {{action (route-action 'removeAttachment' attachment)}}>
|
||||||
|
<i class="ace-icon fa fa-times bigger-125"></i>
|
||||||
|
</a>
|
||||||
|
<div class="space-2"></div>
|
||||||
|
{{!input type='file'}}
|
||||||
|
{{file-input name=(concat 'attachments[' i ']') file=attachment}}
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/form-input}}
|
||||||
<hr />
|
<hr />
|
||||||
{{form-footer-buttons}}
|
{{form-footer-buttons}}
|
||||||
{{/form-content}}
|
{{/form-content}}
|
||||||
|
@ -71,12 +71,83 @@
|
|||||||
<i class="ace-icon fa fa-trash-o"></i>
|
<i class="ace-icon fa fa-trash-o"></i>
|
||||||
</a>
|
</a>
|
||||||
{{#if (not-eq model.images.firstObject.id image.id)}}
|
{{#if (not-eq model.images.firstObject.id image.id)}}
|
||||||
<a class="btn btn-xs btn-info" data-rel="tooltip" title="Move Up" {{action (route-action 'moveUp' image)}}>
|
<a class="btn btn-xs btn-info" data-rel="tooltip" title="Move Up" {{action (route-action 'moveImageUp' image)}}>
|
||||||
<i class="ace-icon fa fa-arrow-up bigger-90"></i>
|
<i class="ace-icon fa fa-arrow-up bigger-90"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (not-eq model.images.lastObject.id image.id)}}
|
{{#if (not-eq model.images.lastObject.id image.id)}}
|
||||||
<a class="btn btn-xs btn-success" data-rel="tooltip" title="Move Down" {{action (route-action 'moveDown' image)}}>
|
<a class="btn btn-xs btn-success" data-rel="tooltip" title="Move Down" {{action (route-action 'moveImageDown' image)}}>
|
||||||
|
<i class="ace-icon fa fa-arrow-down bigger-90"></i>
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/form-input}}
|
||||||
|
|
||||||
|
{{#form-input label='Attachments' name='attachments'}}
|
||||||
|
<div class="widget-box transparent" style="opacity: 1;">
|
||||||
|
<!-- #section:custom/widget-box.options -->
|
||||||
|
<div class="widget-header">
|
||||||
|
<div class="widget-toolbar action-buttons">
|
||||||
|
<a href="#" data-rel="tooltip" title="Add Attachment" {{action (route-action 'addAttachment')}}>
|
||||||
|
<i class="ace-icon fa fa-plus-circle green"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- /section:custom/widget-box.options -->
|
||||||
|
<div class="widget-body">
|
||||||
|
<div class="widget-main no-padding">
|
||||||
|
<table class="table table-striped table-bordered table-hover">
|
||||||
|
<thead class="thin-border-bottom">
|
||||||
|
<tr>
|
||||||
|
<th style="width: 86px;">
|
||||||
|
<i class="ace-icon fa fa-paperclip bigger-110 hidden-480"></i>
|
||||||
|
Attachment
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<i class="ace-icon fa fa-sticky-note-o bigger-110 hidden-480"></i>
|
||||||
|
Description
|
||||||
|
</th>
|
||||||
|
<th style="width: 106px;">
|
||||||
|
<i class="ace-icon fa fa-cogs bigger-110 hidden-480"></i>
|
||||||
|
Settings
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{{#each model.attachments as |attachment|}}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{!image-previews images=image.url}}
|
||||||
|
<a href="{{attachment.url}}" target="_blank">
|
||||||
|
<i class="ace-icon fa fa-download bigger-110 grey"></i>
|
||||||
|
Download
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{editable-cell model=attachment field='note' post-url='application/update-attachment'}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="btn-group">
|
||||||
|
<a class="btn btn-xs btn-danger" data-rel="tooltip" title="Remove" {{action (route-action 'removeAttachment' attachment)}}>
|
||||||
|
<i class="ace-icon fa fa-trash-o"></i>
|
||||||
|
</a>
|
||||||
|
{{#if (not-eq model.images.firstObject.id attachment.id)}}
|
||||||
|
<a class="btn btn-xs btn-info" data-rel="tooltip" title="Move Up" {{action (route-action 'moveAttachmentUp' attachment)}}>
|
||||||
|
<i class="ace-icon fa fa-arrow-up bigger-90"></i>
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
{{#if (not-eq model.images.lastObject.id attachment.id)}}
|
||||||
|
<a class="btn btn-xs btn-success" data-rel="tooltip" title="Move Down" {{action (route-action 'moveAttachmentDown' attachment)}}>
|
||||||
<i class="ace-icon fa fa-arrow-down bigger-90"></i>
|
<i class="ace-icon fa fa-arrow-down bigger-90"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@ -109,3 +180,17 @@
|
|||||||
{{/form-content}}
|
{{/form-content}}
|
||||||
{{/modal-dialog}}
|
{{/modal-dialog}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if model.addAttachment}}
|
||||||
|
{{#modal-dialog title='Add Attachment' no-cancel=true submit=(route-action 'submitAddAttachment') on-close=(route-action 'closeAddAttachment') close-to-parent=false}}
|
||||||
|
{{#form-content form-id=(concat 'form_' model.id)}}
|
||||||
|
{{input type='hidden' name='applicationId' value=model.id}}
|
||||||
|
{{#form-input name='note' label='Description'}}
|
||||||
|
{{input name='note' value=model.attachment.note class='col-xs-12'}}
|
||||||
|
{{/form-input}}
|
||||||
|
{{#form-input name='attachment' label='Attachment'}}
|
||||||
|
{{file-input name='attachment' file=model.attachment}}
|
||||||
|
{{/form-input}}
|
||||||
|
{{/form-content}}
|
||||||
|
{{/modal-dialog}}
|
||||||
|
{{/if}}
|
||||||
|
24
web/tests/integration/components/file-input-test.js
Normal file
24
web/tests/integration/components/file-input-test.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { moduleForComponent, test } from 'ember-qunit';
|
||||||
|
import hbs from 'htmlbars-inline-precompile';
|
||||||
|
|
||||||
|
moduleForComponent('file-input', 'Integration | Component | file input', {
|
||||||
|
integration: true
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it renders', function(assert) {
|
||||||
|
// Set any properties with this.set('myProperty', 'value');
|
||||||
|
// Handle any actions with this.on('myAction', function(val) { ... });
|
||||||
|
|
||||||
|
this.render(hbs`{{file-input}}`);
|
||||||
|
|
||||||
|
assert.equal(this.$().text().trim(), '');
|
||||||
|
|
||||||
|
// Template block usage:
|
||||||
|
this.render(hbs`
|
||||||
|
{{#file-input}}
|
||||||
|
template block text
|
||||||
|
{{/file-input}}
|
||||||
|
`);
|
||||||
|
|
||||||
|
assert.equal(this.$().text().trim(), 'template block text');
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user