add image preview

This commit is contained in:
Shaun Chyxion 2018-03-18 22:15:30 +08:00
parent 267fb19f2e
commit 2bf6f74181
37 changed files with 767 additions and 378 deletions

View File

@ -1,17 +1,20 @@
package com.pudonghot.ambition.crm.controller;
import java.util.Map;
import java.util.*;
import java.net.URLDecoder;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.lang.reflect.Array;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import me.chyxion.tigon.model.M1;
import org.apache.commons.io.IOUtils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import me.chyxion.tigon.mybatis.Search;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.util.TypeUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.StringUtils;
import java.io.UnsupportedEncodingException;
import org.apache.commons.lang3.CharEncoding;
@ -107,12 +110,11 @@ public abstract class AbstractBaseController {
*/
protected InputStream getInputStream(MultipartFile file) {
try {
return file == null || file.isEmpty() ?
null : file.getInputStream();
return file != null && !file.isEmpty() ? file.getInputStream() : null;
}
catch (IOException e) {
throw new IllegalStateException(
"Read Multipart File Stream Error Caused", e);
"Read multipart file stream error caused", e);
}
}
@ -123,13 +125,13 @@ public abstract class AbstractBaseController {
protected String decodeParam(String param) {
if (StringUtils.isNotBlank(param)) {
try {
log.debug("Decode Param [{}].", param);
log.debug("Decode param [{}].", param);
param = URLDecoder.decode(param, CharEncoding.UTF_8);
log.debug("Decode Param Result [{}].", param);
log.debug("Decode param result [{}].", param);
}
catch (UnsupportedEncodingException e) {
throw new IllegalStateException(
"Decode [" + param + "] Error Caused", e);
"Decode [" + param + "] error caused", e);
}
}
return param;
@ -139,14 +141,12 @@ public abstract class AbstractBaseController {
* @param start start
* @param limit limit
* @param search search
* @param strSorters sorters
* @return search
*/
protected Search search(final Integer start,
final Integer limit,
final String search,
final String strSorters) {
return search(null, start, limit, search, strSorters);
final String search) {
return search(null, start, limit, search, null, null, null);
}
/**
@ -155,14 +155,16 @@ public abstract class AbstractBaseController {
* @param start start
* @param limit limit
* @param strSearch search
* @param strSorters sorters
* @param strOrders orders
* @return search
*/
protected Search search(Search search,
final Integer start,
final Integer limit,
final String strSearch,
final String strSorters) {
final String strCriteria,
final String strFilters,
final String strOrders) {
if (search == null) {
search = new Search();
@ -175,27 +177,140 @@ public abstract class AbstractBaseController {
}
if (StringUtils.isNotBlank(strSearch)) {
final Search orSearch = new Search();
for (String col : searchCols()) {
for (final String col : searchCols()) {
orSearch.or(new Search().like(col, decodeLike(strSearch)));
}
search.and(orSearch);
}
if (StringUtils.isNotBlank(strSorters)) {
Map<String, String> sorterCols = sorterCols();
final JSONArray jaSorters;
criteria(search, strCriteria);
filters(search, strFilters);
order(search, strOrders) ;
return search;
}
protected Search criteria(final Search search, final String strCriteria) {
if (StringUtils.isBlank(strCriteria)) {
log.debug("No criteria given.");
return search;
}
final List<Object[]> criteria;
try {
criteria = JSON.parseArray(decodeParam(strCriteria), Object[].class);
}
catch (Exception e) {
throw new IllegalStateException(
"Invalid criteria [" + strCriteria + "]", e);
}
for (final Object[] criterion : criteria) {
if (criterion.length == 3) {
final String field = (String) criterion[0];
if (StringUtils.isNotBlank(field)) {
final Pair<String, Class<?>> colProp = criterionCol(field);
final String col = colProp.getKey();
if (StringUtils.isNotBlank(col)) {
final String op = (String) criterion[1];
final Object val = TypeUtils.castToJavaBean(criterion[2], colProp.getValue());
if (StringUtils.isNotBlank(op) &&
val != null && val instanceof String ?
StringUtils.isNotBlank((String) val) : true) {
onSearch(search, col);
if ("eq".equals(op)) {
search.eq(col, val);
}
else if ("gt".equals(op)) {
search.gt(col, val);
}
else if ("gte".equals(op)) {
search.gte(col, val);
}
else if ("lt".equals(op)) {
search.lt(col, val);
}
else if ("lte".equals(op)) {
search.lt(col, val);
}
else if ("ne".equals(op)) {
search.ne(col, val);
}
else if ("like".equals(op)) {
search.like(col, (String) val, true);
}
else {
log.warn("Unknown criterion op [{}], ignore.", op);
}
}
}
}
else {
log.warn("Criterion col is blank, ignore.");
}
}
else {
log.warn("Invalid criterion [{}], ignore.", criterion);
}
}
return search;
}
protected JSONObject filters(String filters) {
try {
return JSON.parseObject(decodeParam(filters));
}
catch (Exception e) {
throw new IllegalStateException(
"Invalid filters [" + filters + "]", e);
}
}
protected Search filters(final Search search, final String strFilters) {
if (StringUtils.isBlank(strFilters)) {
log.debug("No filters given, ignore.");
return defaultFilter(search);
}
final JSONObject joFilters = filters(strFilters);
for (Map.Entry<String, Object> filter : joFilters.entrySet()) {
final String field = filter.getKey();
final Pair<String, Class<?>> colProp = filterCol(filter.getKey());
if (colProp != null) {
final String col = colProp.getKey();
final Object filterVal = filter.getValue();
if (filterVal != null) {
onSearch(search, col);
// type convert
if (filterVal instanceof JSONArray) {
search.eq(col, joFilters.getObject(
field, Array.newInstance(colProp.getValue(), 0).getClass()));
}
else {
search.eq(col, filterVal);
}
}
}
return search;
}
return search;
}
protected Search order(final Search search, final String strOrders) {
if (StringUtils.isNotBlank(strOrders)) {
final JSONArray jaOrders;
try {
jaSorters = JSON.parseArray(decodeParam(strSorters));
jaOrders = JSON.parseArray(decodeParam(strOrders));
}
catch (JSONException e) {
throw new IllegalStateException(
"Invalid Sorters JSONArray Params [" + strSorters + "]", e);
"Invalid orders params [" + strOrders + "]", e);
}
for (Object objSorter : jaSorters) {
final JSONObject joSorter = (JSONObject) objSorter;
for (final Object objOrder : jaOrders) {
final JSONObject joSorter = (JSONObject) objOrder;
if (!joSorter.isEmpty()) {
final Map.Entry<String, Object> sortEntry =
joSorter.entrySet().iterator().next();
final String col = sorterCols.get(sortEntry.getKey());
final String col = orderCol(sortEntry.getKey());
if (StringUtils.isNotBlank(col)) {
final String dir = (String) sortEntry.getValue();
if ("asc".equalsIgnoreCase(dir)) {
@ -208,20 +323,48 @@ public abstract class AbstractBaseController {
}
}
}
else {
defaultOrder(search);
}
return search;
}
/**
* @return simple search cols
*/
protected String[] searchCols() {
return new String[]{ };
protected List<String> searchCols() {
return Collections.emptyList();
}
/**
* @return simple search cols
*/
protected Map<String, String> sorterCols() {
return Collections.emptyMap();
protected Search defaultFilter(final Search search) {
return search;
}
protected Search defaultOrder(final Search search) {
return search;
}
protected Pair<String, Class<?>> criterionCol(final String field) {
return null;
}
protected Pair<String, Class<?>> filterCol(final String field) {
return null;
}
protected String orderCol(final String field) {
return null;
}
protected void onSearch(final Search search, final String col) {
}
protected Map<String, String> defaultOrderCols() {
final Map<String, String> cols = new HashMap<>();
cols.put(M1.ID, M1.ID);
cols.put("dateCreated", M1.DATE_CREATED);
cols.put("dateUpdated", M1.DATE_UPDATED);
return cols;
}
}

View File

@ -1,5 +1,7 @@
package com.pudonghot.ambition.crm.controller;
import java.util.List;
import java.util.Arrays;
import javax.validation.Valid;
import me.chyxion.tigon.mybatis.Search;
import javax.validation.constraints.Max;
@ -8,14 +10,18 @@ import me.chyxion.tigon.model.ViewModel;
import me.chyxion.tigon.model.ListResult;
import org.apache.commons.lang3.StringUtils;
import com.pudonghot.ambition.crm.model.User;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Controller;
import com.pudonghot.ambition.crm.model.Application;
import com.pudonghot.ambition.crm.service.UserService;
import org.apache.shiro.authz.annotation.RequiresRoles;
import com.pudonghot.ambition.crm.model.CustomerProperty;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestParam;
import com.pudonghot.ambition.crm.service.ApplicationService;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.beans.factory.annotation.Autowired;
import com.pudonghot.ambition.crm.form.create.ApplicationFormForCreate;
import com.pudonghot.ambition.crm.form.update.ApplicationFormForUpdate;
@ -29,6 +35,14 @@ import com.pudonghot.ambition.crm.form.update.ApplicationFormForUpdate;
public class ApplicationController
extends BaseQueryController<Application> {
@Value("${file.base-path}")
private String fileBasePath;
@Autowired
private UserService userService;
private final List<String> SEARCH_COLS =
Arrays.asList(CustomerProperty.NAME,
CustomerProperty.NOTE);
@RequestMapping("/list")
public ListResult<ViewModel<Application>> list(
@Min(0)
@ -38,6 +52,8 @@ public class ApplicationController
@Max(2048)
@RequestParam(value = "limit", defaultValue = "16")
final int limit,
@RequestParam(value = "filters", required = false)
final String filters,
@RequestParam(value = "search", required = false)
final String strSearch) {
@ -47,8 +63,12 @@ public class ApplicationController
if (!user.isAdmin()) {
search.eq(Application.OWNER, user.getId());
}
return listViewModels(search, start, limit, strSearch, null);
final ListResult<ViewModel<Application>> result =
listViewModels(search, start, limit, strSearch, null, filters, null);
result.setAttr("fileBasePath", fileBasePath);
result.setAttr("users", userService.listViewModels(
new Search().asc(User.EMPLOYEE_ID)));
return result;
}
@RequestMapping("/list-for-select")
@ -63,7 +83,7 @@ public class ApplicationController
if (StringUtils.isNotBlank(enabled)) {
search.eq(CustomerProperty.ENABLED, Boolean.parseBoolean(enabled));
}
return listViewModels(search, null, null, strSearch, null);
return listViewModels(search, null, null, strSearch, null, null, null);
}
@RequiresRoles(User.ROLE_ADMIN)
@ -77,6 +97,7 @@ public class ApplicationController
@RequestMapping(value = "/update", method = RequestMethod.POST)
public void update(
@Valid ApplicationFormForUpdate form) {
form.setAdmin(getAuthUser().getUser().getData().isAdmin());
((ApplicationService) queryService).update(form);
}
@ -84,10 +105,15 @@ public class ApplicationController
* {@inheritDoc}
*/
@Override
protected String[] searchCols() {
return new String[] {
CustomerProperty.NAME,
CustomerProperty.NOTE
};
protected List<String> searchCols() {
return SEARCH_COLS;
}
/**
* {@inheritDoc}
*/
@Override
protected Pair<String, Class<?>> filterCol(final String field) {
return Application.OWNER.equals(field) ? Pair.of(field, String.class) : null;
}
}

View File

@ -1,11 +1,12 @@
package com.pudonghot.ambition.crm.controller;
import lombok.extern.slf4j.Slf4j;
import me.chyxion.tigon.mybatis.Search;
import me.chyxion.tigon.model.BaseModel;
import me.chyxion.tigon.model.ViewModel;
import me.chyxion.tigon.model.ListResult;
import javax.validation.constraints.NotNull;
import me.chyxion.tigon.service.BaseQueryService;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
@ -17,12 +18,31 @@ import org.springframework.web.bind.annotation.RequestMapping;
* chyxion@163.com <br>
* May 10, 2016 4:50:58 PM
*/
@Slf4j
public class BaseQueryController<Model extends BaseModel<String>>
extends BaseController {
@Autowired
protected BaseQueryService<String, Model> queryService;
/**
* find model by id
* @param id model id
* @return model
*/
@RequestMapping("/find")
public ViewModel<Model> find(@NotBlank @RequestParam("id") String id) {
return queryService.findViewModel(id);
}
/**
* @return models count
*/
@RequestMapping("/count")
public int count() {
return queryService.count(null);
}
/**
* list view models
* @param start start
@ -34,40 +54,48 @@ public class BaseQueryController<Model extends BaseModel<String>>
final Integer start,
final Integer limit,
final String paramSearch) {
return listViewModels(start, limit, paramSearch, null);
return listViewModels(start, limit, paramSearch, null, null, null);
}
/**
* list view models
* @param start start
* @param limit limit
* @param strSearch search
* @param strSorters sorters
* @return search
* @param strCriteria criteria
* @param strFilters filters
* @param strOrders orders
* @return list result
*/
protected ListResult<ViewModel<Model>> listViewModels(
final Integer start,
final Integer limit,
final String strSearch,
final String strSorters) {
return listViewModels(null, start, limit, strSearch, strSorters);
final String strCriteria,
final String strFilters,
final String strOrders) {
return listViewModels(null, start, limit, strSearch, strCriteria, strFilters, strOrders);
}
/**
* list view models
* @param search search
* @param start start
* @param limit limit
* @param strSearch search
* @return view models
* @param strCriteria criteria
* @param strFilters filters
* @param strOrders orders
* @return list result
*/
protected ListResult<ViewModel<Model>> listViewModels(
final Search search,
final Integer start,
final Integer limit,
final String strSearch,
final String strSorters) {
return listViewModels(search(search, start, limit, strSearch, strSorters));
final String strCriteria,
final String strFilters,
final String strOrders) {
return listViewModels(search(
search, start, limit, strSearch, strCriteria, strFilters, strOrders));
}
/**
@ -78,22 +106,4 @@ public class BaseQueryController<Model extends BaseModel<String>>
protected ListResult<ViewModel<Model>> listViewModels(Search search) {
return queryService.listViewModelsPage(search);
}
/**
* find model by id
* @param id model id
* @return model
*/
@RequestMapping("/find")
public ViewModel<Model> find(@NotNull @RequestParam("id") String id) {
return queryService.findViewModel(id);
}
/**
* @return models count
*/
@RequestMapping("/count")
public int count() {
return queryService.count(null);
}
}

View File

@ -2,9 +2,8 @@ package com.pudonghot.ambition.crm.controller;
import java.util.*;
import javax.validation.Valid;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import com.alibaba.fastjson.JSONObject;
import me.chyxion.tigon.mybatis.Search;
import me.chyxion.tigon.model.ViewModel;
import javax.validation.constraints.Max;
@ -13,6 +12,7 @@ import me.chyxion.tigon.model.ListResult;
import javax.validation.constraints.NotNull;
import org.apache.commons.lang3.StringUtils;
import com.pudonghot.ambition.crm.model.User;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Controller;
import com.pudonghot.ambition.crm.model.Customer;
import org.apache.shiro.authz.annotation.RequiresRoles;
@ -37,7 +37,7 @@ import com.pudonghot.ambition.crm.form.update.CustomerFormForUpdate;
public class CustomerController
extends BaseQueryController<Customer> {
private static final String[] SEARCH_COLS = new String[] {
private static final List<String> SEARCH_COLS = Arrays.asList(
"customer.id",
"customer.name",
"customer.city",
@ -48,26 +48,25 @@ public class CustomerController
"customer.salesperson",
"year",
// status text
"prop.name"
};
"prop.name");
private static final Map<String, String> SORT_COLS = new HashMap<>();
private static final Map<String, String> ORDER_COLS = new HashMap<>();
static {
SORT_COLS.put(Customer.ID, "customer.id");
SORT_COLS.put("dateAdded", "year(customer.date_added)");
SORT_COLS.put(CustomerYearToDateSale.YEAR, CustomerYearToDateSale.YEAR);
SORT_COLS.put("countryCode", "customer.country_code");
SORT_COLS.put(Customer.MS, "customer.ms");
SORT_COLS.put(Customer.REGION, "customer.region");
SORT_COLS.put(Customer.SALESPERSON, "customer.salesperson");
SORT_COLS.put("sumYtdSales", Customer.SUM_YTD_SALES);
SORT_COLS.put("status", "prop.sort");
SORT_COLS.put("application", "application_names");
ORDER_COLS.put(Customer.ID, "customer.id");
ORDER_COLS.put("dateAdded", "year(customer.date_added)");
ORDER_COLS.put(CustomerYearToDateSale.YEAR, CustomerYearToDateSale.YEAR);
ORDER_COLS.put("countryCode", "customer.country_code");
ORDER_COLS.put(Customer.MS, "customer.ms");
ORDER_COLS.put(Customer.REGION, "customer.region");
ORDER_COLS.put(Customer.SALESPERSON, "customer.salesperson");
ORDER_COLS.put("sumYtdSales", Customer.SUM_YTD_SALES);
ORDER_COLS.put("status", "prop.sort");
ORDER_COLS.put("application", "application_names");
}
private static final Map<String, String> CRITERION_COLS;
static {
CRITERION_COLS = new HashMap<>(SORT_COLS);
CRITERION_COLS = new HashMap<>(ORDER_COLS);
CRITERION_COLS.put(Customer.NAME, "customer.name");
CRITERION_COLS.put(Customer.STATE, "customer.state");
CRITERION_COLS.put(Customer.CITY, "customer.city");
@ -77,6 +76,48 @@ public class CustomerController
CRITERION_COLS.put("issue", "issue.artificial");
}
// filters
private static final Map<String, Pair<String, Class<?>>> FILTER_COLS = new HashMap<>();
static {
FILTER_COLS.put("dateAdded",
Pair.of(CRITERION_COLS.get("dateAdded"), Integer.class));
FILTER_COLS.put("year",
Pair.of(CRITERION_COLS.get("year"), String.class));
FILTER_COLS.put("ms",
Pair.of(CRITERION_COLS.get("ms"), String.class));
FILTER_COLS.put("region",
Pair.of(CRITERION_COLS.get("region"), String.class));
FILTER_COLS.put("countryCode",
Pair.of(CRITERION_COLS.get("countryCode"), String.class));
FILTER_COLS.put("state",
Pair.of(CRITERION_COLS.get("state"), String.class));
FILTER_COLS.put("city",
Pair.of(CRITERION_COLS.get("city"), String.class));
FILTER_COLS.put("salesperson",
Pair.of(CRITERION_COLS.get("city"), String.class));
FILTER_COLS.put("status",
Pair.of(CRITERION_COLS.get("status"), String.class));
FILTER_COLS.put("application",
Pair.of(CRITERION_COLS.get("application"), String.class));
FILTER_COLS.put("issue",
Pair.of(CRITERION_COLS.get("issue"), Boolean.class));
// filter(search, joFilters, "dateAdded", Integer[].class);
// filter(search, joFilters, "year");
//
// filter(search, joFilters, "ms");
// filter(search, joFilters, "region");
// filter(search, joFilters, "countryCode");
// filter(search, joFilters, "state");
// filter(search, joFilters, "city");
// filter(search, joFilters, "salesperson");
// filter(search, joFilters, "status");
// filter(search, joFilters, "application");
//
// filter(search, joFilters, "issue", Boolean[].class);
}
@RequestMapping("/list")
public ListResult<ViewModel<Customer>> list(
@Min(0)
@ -92,10 +133,10 @@ public class CustomerController
final String criteria,
@RequestParam(value = "filters", required = false)
final String filters,
@RequestParam(value = "sorters", required = false)
final String sorters) {
@RequestParam(value = "orders", required = false)
final String orders) {
final Search search = filters(new Search(), filters);
final Search search = new Search();
User user = getUser().getData();
// if (!user.isAdmin()) {
search.setAttr(User.ACCOUNT, user.getAccount());
@ -104,9 +145,7 @@ public class CustomerController
if (StringUtils.isNotBlank(strSearch)) {
search.setAttr("YTD_SALE", true);
}
final ListResult<ViewModel<Customer>> result =
listViewModels(search(criteria(search, criteria), start, limit, strSearch, sorters));
return result;
return listViewModels(search, start, limit, strSearch, criteria, filters, orders);
}
@RequiresRoles(User.ROLE_ADMIN)
@ -135,7 +174,7 @@ public class CustomerController
* {@inheritDoc}
*/
@Override
protected String[] searchCols() {
protected List<String> searchCols() {
return SEARCH_COLS;
}
@ -143,174 +182,42 @@ public class CustomerController
* {@inheritDoc}
*/
@Override
protected Map<String, String> sorterCols() {
return SORT_COLS;
protected Pair<String, Class<?>> filterCol(final String field) {
return FILTER_COLS.get(field);
}
private String criterionCol(String col) {
return CRITERION_COLS.get(col);
/**
* {@inheritDoc}
*/
protected Pair<String, Class<?>> criterionCol(final String col) {
return Pair.of(CRITERION_COLS.get(col), Object.class);
}
private Search criteria(final Search search, final String strCriteria) {
if (StringUtils.isBlank(strCriteria)) {
log.debug("No Criteria Given.");
return search;
}
final List<String[]> criteria;
try {
criteria = JSON.parseArray(decodeParam(strCriteria), String[].class);
}
catch (Exception e) {
throw new IllegalStateException(
"Invalid Criteria [" + strCriteria + "]", e);
}
for (String[] criterion : criteria) {
if (criterion.length == 3) {
final String col = criterion[0];
if (StringUtils.isNotBlank(col)) {
final String criterionCol = criterionCol(col);
if (StringUtils.isNotBlank(criterionCol)) {
// for search year
if (CustomerYearToDateSale.YEAR.equals(criterionCol) ||
CustomerYearToDateSale.YTD_SALE.equals(criterionCol)) {
search.setAttr("YTD_SALE", true);
}
final String op = criterion[1];
final String val = criterion[2];
if (StringUtils.isNotBlank(op) && StringUtils.isNotBlank(val)) {
if ("eq".equals(op)) {
search.eq(criterionCol, val);
}
else if ("gt".equals(op)) {
search.gt(criterionCol, val);
}
else if ("gte".equals(op)) {
search.gte(criterionCol, val);
}
else if ("lt".equals(op)) {
search.lt(criterionCol, val);
}
else if ("lte".equals(op)) {
search.lt(criterionCol, val);
}
else if ("ne".equals(op)) {
search.ne(criterionCol, val);
}
else if ("like".equals(op)) {
search.like(criterionCol, val, true);
}
else {
log.warn("Unknown Criterion Op [{}], Ignore.", op);
}
}
}
}
else {
log.warn("Criterion Col Is Blank, Ignore.");
}
}
else {
log.warn("Invalid Criterion [{}], Ignore.", criterion);
}
}
return search;
/**
* {@inheritDoc}
*/
@Override
protected String orderCol(final String field) {
return ORDER_COLS.get(field);
}
private Search filters(final Search search, final String strFilters) {
if (StringUtils.isBlank(strFilters)) {
log.debug("No filters given.");
defaultFilter(search, "status");
return search;
}
final JSONObject joFilters;
try {
joFilters = JSON.parseObject(decodeParam(strFilters));
}
catch (Exception e) {
throw new IllegalStateException(
"Invalid Filters [" + strFilters + "]", e);
}
filter(search, joFilters, "dateAdded", Integer[].class);
filter(search, joFilters, "year");
filter(search, joFilters, "ms");
filter(search, joFilters, "region");
filter(search, joFilters, "countryCode");
filter(search, joFilters, "state");
filter(search, joFilters, "city");
filter(search, joFilters, "salesperson");
filter(search, joFilters, "status");
filter(search, joFilters, "application");
filter(search, joFilters, "issue", Boolean[].class);
return search;
/**
* {@inheritDoc}
*/
@Override
protected Search defaultFilter(final Search search) {
return search.ne(CRITERION_COLS.get("status"), CustomerProperty.STATUS_NA_ID);
}
private Search filter(final Search search, final JSONObject joFilters, final String field) {
return filter(search, joFilters, field, String[].class);
}
private <T> Search filter(final Search search, final JSONObject joFilters, final String field, Class<T[]> clazz) {
final String col = CRITERION_COLS.get(field);
if (StringUtils.isNotBlank(col)) {
final T[] filters =
joFilters.getObject(field, clazz);
if (filters != null && filters.length > 0) {
log.info("Col [{}] filters [{}] found.", col, filters);
if ("application".equals(col)) {
search.setAttr("APPLICATIONS", filters);
}
else {
in(search, col, filters);
}
}
else {
defaultFilter(search, field);
}
// filter year
if (CustomerYearToDateSale.YEAR.equals(col)) {
search.setAttr("YTD_SALE", true);
}
}
return search;
}
private void defaultFilter(final Search search, final String field) {
// default no NA
if ("status".equals(field)) {
search.ne(CRITERION_COLS.get(field), CustomerProperty.STATUS_NA_ID);
}
}
private void in(final Search search, final String col, final Object[] values) {
final Set<Object> setValues = new HashSet<>(Arrays.asList(values));
final Iterator<Object> valIt = setValues.iterator();
if (setValues.size() == 1) {
final Object val = valIt.next();
search.eq(col, "null".equals(val) ? null : val);
}
else {
boolean nullValue = false;
while (valIt.hasNext()) {
final Object val = valIt.next();
if (val == null || "null".equals(val)) {
nullValue = true;
valIt.remove();
break;
}
}
if (nullValue) {
search.and(new Search(col, setValues).or(col, null));
}
else {
search.in(col, setValues);
}
/**
* {@inheritDoc}
*/
@Override
protected void onSearch(final Search search, final String col) {
// for search year
if (CustomerYearToDateSale.YEAR.equals(col) ||
CustomerYearToDateSale.YTD_SALE.equals(col)) {
search.setAttr("YTD_SALE", true);
}
}
}

View File

@ -1,5 +1,7 @@
package com.pudonghot.ambition.crm.controller;
import java.util.List;
import java.util.Arrays;
import javax.validation.Valid;
import javax.validation.constraints.Max;
import me.chyxion.tigon.model.ViewModel;
@ -24,6 +26,11 @@ import com.pudonghot.ambition.crm.form.update.CustomerIssueFormForUpdate;
public class CustomerIssueController
extends BaseQueryController<CustomerIssue> {
private static final List<String> SEARCH_COLS =
Arrays.asList(
CustomerIssue.ISSUE,
CustomerIssue.NOTE);
@RequestMapping("/list")
public ListResult<ViewModel<CustomerIssue>> list(
@Min(0)
@ -54,10 +61,7 @@ public class CustomerIssueController
* {@inheritDoc}
*/
@Override
protected String[] searchCols() {
return new String[] {
CustomerIssue.ISSUE,
CustomerIssue.NOTE
};
protected List<String> searchCols() {
return SEARCH_COLS;
}
}

View File

@ -21,6 +21,9 @@ import com.pudonghot.ambition.crm.service.CustomerPropertyService;
import com.pudonghot.ambition.crm.form.create.CustomerPropertyFormForCreate;
import com.pudonghot.ambition.crm.form.update.CustomerPropertyFormForUpdate;
import java.util.Arrays;
import java.util.List;
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
@ -31,6 +34,11 @@ import com.pudonghot.ambition.crm.form.update.CustomerPropertyFormForUpdate;
public class CustomerPropertyController
extends BaseQueryController<CustomerProperty> {
private static final List<String> SEARCH_COLS =
Arrays.asList(
CustomerProperty.NAME,
CustomerProperty.NOTE);
@RequestMapping("/list")
public ListResult<ViewModel<CustomerProperty>> list(
@Min(0)
@ -54,7 +62,7 @@ public class CustomerPropertyController
Boolean.parseBoolean(enabled));
}
((CustomerPropertyService) queryService).initSystemStatus(getUserId());
return listViewModels(search, start, limit, strSearch, null);
return listViewModels(search, start, limit, strSearch, null, null, null);
}
@RequestMapping("/app-list")
@ -70,7 +78,7 @@ public class CustomerPropertyController
if (StringUtils.isNotBlank(enabled)) {
search.eq(CustomerProperty.ENABLED, Boolean.parseBoolean(enabled));
}
return listViewModels(search, null, null, strSearch, null);
return listViewModels(search, null, null, strSearch, null, null, null);
}
@RequiresRoles(User.ROLE_ADMIN)
@ -100,10 +108,7 @@ public class CustomerPropertyController
* {@inheritDoc}
*/
@Override
protected String[] searchCols() {
return new String[] {
CustomerProperty.NAME,
CustomerProperty.NOTE
};
protected List<String> searchCols() {
return SEARCH_COLS;
}
}

View File

@ -19,6 +19,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
import com.pudonghot.ambition.crm.form.create.HomePageFormForCreate;
import com.pudonghot.ambition.crm.form.update.HomePageFormForUpdate;
import java.util.Arrays;
import java.util.List;
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
@ -29,10 +32,10 @@ import com.pudonghot.ambition.crm.form.update.HomePageFormForUpdate;
@RequestMapping("/home-page")
public class HomePageController
extends BaseQueryController<HomePage> {
private static final String[] SEARCH_COLS = new String[] {
HomePage.NAME,
HomePage.NOTE
};
private static final List<String> SEARCH_COLS =
Arrays.asList(
HomePage.NAME,
HomePage.NOTE);
@RequestMapping("/list")
@RequiresRoles(User.ROLE_ADMIN)
@ -47,7 +50,7 @@ public class HomePageController
@RequestParam(value = "search", required = false)
final String search) {
return listViewModels(start, limit, search, null);
return listViewModels(start, limit, search);
}
@RequiresRoles(User.ROLE_ADMIN)
@ -85,7 +88,7 @@ public class HomePageController
* {@inheritDoc}
*/
@Override
protected String[] searchCols() {
protected List<String> searchCols() {
return SEARCH_COLS;
}
}

View File

@ -1,6 +1,7 @@
package com.pudonghot.ambition.crm.controller;
import java.util.List;
import java.util.Arrays;
import lombok.extern.slf4j.Slf4j;
import me.chyxion.tigon.model.ViewModel;
import javax.validation.constraints.Max;
@ -23,6 +24,14 @@ import org.springframework.web.bind.annotation.RequestMapping;
public class ImportRecordController
extends BaseQueryController<ImportRecord> {
private final List<String> SEARCH_COLS =
Arrays.asList("r.type",
"r.note",
"u.name",
"u.en_name",
"u.account",
"u.employee_id" );
@RequestMapping("/list")
@RequiresRoles(User.ROLE_ADMIN)
public ViewModel<List<ViewModel<ImportRecord>>> list(
@ -36,7 +45,7 @@ public class ImportRecordController
@RequestParam(value = "search", required = false)
final String search) {
return listViewModels(search(start, limit, search, null)
return listViewModels(search(start, limit, search)
.table("r")
.desc(ImportRecord.DATE_CREATED));
}
@ -45,7 +54,8 @@ public class ImportRecordController
* {@inheritDoc}
* @return search cols
*/
protected String[] searchCols() {
return new String[] { "r.type", "r.note", "u.name", "u.en_name", "u.account", "u.employee_id" };
@Override
protected List<String> searchCols() {
return SEARCH_COLS;
}
}

View File

@ -1,17 +1,20 @@
package com.pudonghot.ambition.crm.controller;
import java.util.Map;
import java.util.List;
import java.util.Arrays;
import java.util.HashMap;
import javax.validation.Valid;
import me.chyxion.tigon.mybatis.Search;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import me.chyxion.tigon.model.ViewModel;
import me.chyxion.tigon.model.ListResult;
import org.apache.ibatis.annotations.Param;
import javax.validation.constraints.NotNull;
import com.pudonghot.ambition.crm.model.User;
import org.apache.ibatis.annotations.Param;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.stereotype.Controller;
import org.hibernate.validator.constraints.NotBlank;
import com.pudonghot.ambition.crm.service.UserService;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.RequestParam;
@ -31,22 +34,22 @@ import com.pudonghot.ambition.crm.form.update.UserFormForUpdatePassword;
public class UserController
extends BaseQueryController<User> {
private static final Map<String, String> SORT_COLS = new HashMap<>();
private static final String[] SEARCH_COLS = new String[] {
private static final List<String> SEARCH_COLS = Arrays.asList(
User.NAME,
User.EN_NAME,
User.ACCOUNT,
User.EMPLOYEE_ID,
User.MOBILE,
User.EMAIL,
User.NOTE
};
// User.MOBILE,
// User.EMAIL,
User.NOTE);
private static final Map<String, String> ORDER_COLS = new HashMap<>();
static {
SORT_COLS.put(User.ACCOUNT, User.ACCOUNT);
SORT_COLS.put("employeeId", User.EMPLOYEE_ID);
SORT_COLS.put(User.MOBILE, User.MOBILE);
SORT_COLS.put("enName", User.EN_NAME);
ORDER_COLS.put(User.ACCOUNT, User.ACCOUNT);
ORDER_COLS.put("employeeId", User.EMPLOYEE_ID);
ORDER_COLS.put(User.MOBILE, User.MOBILE);
ORDER_COLS.put("enName", User.EN_NAME);
}
@RequestMapping("/list")
@ -61,9 +64,14 @@ public class UserController
final int limit,
@RequestParam(value = "search", required = false)
final String search,
@RequestParam(value = "sorters", required = false)
final String sorters) {
return listViewModels(start, limit, search, sorters);
@RequestParam(value = "orders", required = false)
final String orders) {
return listViewModels(start, limit, search, null, null, orders);
}
@RequestMapping("/list-for-select")
public List<ViewModel<User>> listForSelect() {
return queryService.listViewModels(new Search().asc(User.EMPLOYEE_ID));
}
@RequiresRoles(User.ROLE_ADMIN)
@ -100,7 +108,7 @@ public class UserController
* {@inheritDoc}
*/
@Override
protected String[] searchCols() {
protected List<String> searchCols() {
return SEARCH_COLS;
}
@ -108,7 +116,7 @@ public class UserController
* {@inheritDoc}
*/
@Override
protected Map<String, String> sorterCols() {
return SORT_COLS;
protected String orderCol(final String field) {
return ORDER_COLS.get(field);
}
}

View File

@ -35,6 +35,12 @@ import com.pudonghot.ambition.crm.form.update.WeekGoalFormForUpdate;
public class WeekGoalController
extends BaseQueryController<WeekGoal> {
private static final List<String> SEARCH_COLS =
Arrays.asList("u.name",
"u.en_name",
"u.account",
"u.employee_id");
@RequestMapping("/list")
public ViewModel<List<ViewModel<WeekGoal>>> list(
@Min(0)
@ -50,7 +56,7 @@ public class WeekGoalController
final String strSearch) {
final List<String> employeeIds = employeeIdsFilter(filters);
return addYearSum(((WeekGoalService) queryService).listJoinUser(
listFilters(search(start, limit, strSearch, null), employeeIds)), employeeIds);
listFilters(search(start, limit, strSearch), employeeIds)), employeeIds);
}
@RequestMapping("/mine")
@ -116,8 +122,8 @@ public class WeekGoalController
* @return search cols
*/
@Override
protected String[] searchCols() {
return new String[] { "u.name", "u.en_name", "u.account", "u.employee_id" };
protected List<String> searchCols() {
return SEARCH_COLS;
}
/**

View File

@ -4,6 +4,7 @@ import java.util.Date;
import java.io.IOException;
import java.io.InputStream;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import me.chyxion.tigon.model.ViewModel;
import org.springframework.stereotype.Service;
import com.pudonghot.ambition.file.AmbitionFileApi;
@ -45,12 +46,31 @@ public class ApplicationServiceSupport
final Date now = new Date();
application.setDateUpdated(now);
final MultipartFile[] images = form.getImages();
uploadImages(id, 0, form.getImages(), form.getImageTitles(), form.getCreatedBy());
mapper.insert(application);
return toViewModel(application);
}
/**
* {@inheritDoc}
*/
@Override
public ViewModel<Application> update(final ApplicationFormForUpdate form) {
final String id = form.getId();
final Application application = find(id);
Assert.state(application != null, "No application [" + id + "] found");
final String updatedBy = form.getUpdatedBy();
Assert.state(form.isAdmin() || application.getOwner().equals(updatedBy),
"No permission to update application");
uploadImages(id, imageMapper.nextSort(id), form.getImages(), form.getImageTitles(), updatedBy);
return update(form.copy(application));
}
private void uploadImages(final String id, int sort, final MultipartFile[] images, final String[] titles, final String createdBy) {
if (images != null) {
final String createdBy = form.getCreatedBy();
int sort = 0;
final Date now = new Date();
final String imageFolder = imageFolder(id);
final String[] imageTitles = form.getImageTitles();
int i = 0;
for (final MultipartFile image : images) {
if (!image.isEmpty()) {
@ -61,7 +81,8 @@ public class ApplicationServiceSupport
appImage.setId(imageId);
appImage.setApplicationId(id);
appImage.setSort(sort++);
appImage.setNote(imageTitles[i]);
appImage.setEnabled(true);
appImage.setNote(titles[i]);
appImage.setCreatedBy(createdBy);
appImage.setDateCreated(now);
imageMapper.insert(appImage);
@ -74,16 +95,6 @@ public class ApplicationServiceSupport
++i;
}
}
mapper.insert(application);
return toViewModel(application);
}
/**
* {@inheritDoc}
*/
@Override
public ViewModel<Application> update(final ApplicationFormForUpdate form) {
return null;
}
private String imageFolder(final String appId) {

View File

@ -1,6 +1,8 @@
package com.pudonghot.ambition.crm.mapper;
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;
/**
@ -10,4 +12,10 @@ import com.pudonghot.ambition.crm.model.ApplicationImage;
*/
public interface ApplicationImageMapper extends BaseMapper<String, ApplicationImage> {
/**
* find next sort
* @param applicationId application id
* @return next sort
*/
int nextSort(@NotBlank @Param("applicationId") String applicationId);
}

View File

@ -10,4 +10,11 @@
"-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pudonghot.ambition.crm.mapper.ApplicationImageMapper">
<select id="nextSort" resultType="int">
select if (application_id, max(sort) + 1, 0)
from <include refid="table" />
where application_id = #{applicationId}
</select>
</mapper>

View File

@ -10,4 +10,22 @@
"-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pudonghot.ambition.crm.mapper.ApplicationMapper">
<select id="list" resultType="com.pudonghot.ambition.crm.model.Application">
select
<include refid="cols" />,
(select group_concat(id order by sort separator 0x1d) from
crm_application_image
where application_id = a.id
group by application_id) images,
(select group_concat(note order by sort separator 0x1d) from
crm_application_image
where application_id = a.id
group by application_id) image_titles
from
<include refid="table" /> a
<include refid="Tigon.search" />
</select>
</mapper>

View File

@ -21,6 +21,8 @@ import org.springframework.web.multipart.MultipartFile;
public class ApplicationFormForUpdate extends FU2<String, String> {
private static final long serialVersionUID = 1L;
// current user is admin
private boolean admin;
@NotBlank
@Length(max = 64)
private String name;

View File

@ -4,6 +4,7 @@ import lombok.Getter;
import lombok.Setter;
import me.chyxion.tigon.model.M3;
import me.chyxion.tigon.mybatis.Table;
import me.chyxion.tigon.mybatis.Transient;
/**
* @author Donghuang <br>
@ -25,4 +26,10 @@ public class Application extends M3<String, String> {
private String name;
private String content;
private String owner;
// Transient Props
@Transient
private String images;
@Transient
private String imageTitles;
}

View File

@ -0,0 +1,21 @@
import Component from '@ember/component';
import { htmlSafe } from '@ember/string';
export default Component.extend({
tagName: '',
didReceiveAttrs() {
const me = this;
me._super(...arguments);
me.set('content', htmlSafe(me.get('model.content')));
},
actions: {
show() {
let me = this;
me.set('show', true);
},
close() {
let me = this;
me.set('show', false);
}
}
});

View File

@ -32,6 +32,7 @@ export default BaseFormInput.extend({
allowClear: me.get('nullable'),
placeholder: me.get('placeholder') || me.get('label') || 'Please select...'
}).on('change', function(e) {
console.log('select2 change: ', e);
if (e.removed) {
me.unselect(me.findOption(e.removed.id));
}
@ -64,7 +65,7 @@ export default BaseFormInput.extend({
if (me.get('multiple')) {
const vals = me.getVal();
if (isArray(vals)) {
vals.forEach(v => me.select(me.findOption(v)));
vals.each(v => me.select(me.findOption(v)));
}
else {
me.setVal(me.getSelected());
@ -101,6 +102,6 @@ export default BaseFormInput.extend({
getSelected() {
const me = this;
const selected = me.get('options').filter(o => o.selected).mapBy(me.get('value-field'));
return me.get('multiple') ? selected : selected[0];
return selected.length > 1 ? selected : selected.length == 1 ? selected[0] : null;
}
});

View File

@ -1,18 +1,25 @@
import Ember from 'ember';
import Component from '@ember/component';
import { computed } from '@ember/object';
import $ from 'jquery'
export default Ember.Component.extend({
export default Component.extend({
tagName: 'span',
classNames: ['inline'],
index: 0,
images: computed.alias('previews'),
'image-height': 22,
imageHeight: computed.alias('image-height'),
'image-style': 'border-radius: 8%; border: 1px solid #DCDCDC; max-width: 32px;',
imageStyle: computed.alias('image-style'),
didReceiveAttrs() {
let me = this;
let previews = me.get('previews');
if (Ember.$.type(previews) === 'string') {
let images = me.get('images');
if (Ember.$.type(images) === 'string') {
let sep = me.get('separator');
me.set('previews', sep ? previews.split(sep) : [previews]);
me.set('images', sep ? images.split(sep) : [images]);
}
},
imageHeight: 22,
imageStyle: 'border-radius: 8%; border: 1px solid #DCDCDC; max-width: 32px;',
didInsertElement() {
let me = this;
me._super(...arguments);

View File

@ -1,16 +1,22 @@
import Ember from 'ember';
import Component from '@ember/component'
import BaseComponentMixin from '../mixins/components/base-component';
export default Ember.Component.extend(BaseComponentMixin, {
export default Component.extend(BaseComponentMixin, {
classNames: ['modal', 'fade'],
'init-modal': true,
transitionToParentRouteAfterClose: true,
'close-to-parent': true,
'cancel-text': 'Cancel',
'submit-text': 'Submit',
keyboard: true,
didInsertElement() {
let me = this;
me._super(...arguments);
if (me.get('init-modal')) {
me.$().modal().on('hidden.bs.modal', ()=> {
me.$() && me.get('transitionToParentRouteAfterClose') &&
me.$().modal({
backdrop: me.get('backdrop'),
keyboard: me.get('keyboard')
}).on('hidden.bs.modal', ()=> {
me.$() && me.get('close-to-parent') &&
me.get('router').transitionTo(
me.get('parentRouteName') ||
me.get('routeName').replace(/\.[^.]+$/, ''));

View File

@ -10,7 +10,8 @@ const OptionTextComponent = Component.extend({
me._super(...arguments);
let val = me.get('value');
if (val) {
const option = (me.get('options') || []).findBy(me.get('value-field'), val);
const valueField = me.get('value-field');
const option = (me.get('options') || []).findBy(valueField, val);
if (option) {
let text = option[me.get('text-field')];
let textExp = me.get('text-exp');

View File

@ -3,7 +3,7 @@ import BaseComponentMixin from '../mixins/components/base-component';
export default Ember.Component.extend(BaseComponentMixin, {
tagName: 'th',
order: Ember.computed('route.controller.sorters', function() {
order: Ember.computed('route.controller.orders', function() {
return this.getDir();
}),
sorting: Ember.computed.none('order'),
@ -14,44 +14,44 @@ export default Ember.Component.extend(BaseComponentMixin, {
getDir() {
let me = this;
let name = me.get('name');
let sorters = me.getSorters();
if (sorters && sorters.length) {
let sorter = sorters.find(sorter => sorter.hasOwnProperty(name));
if (sorter) {
return sorter[name];
let orders = me.getOrders();
if (orders && orders.length) {
let order = orders.find(order => order.hasOwnProperty(name));
if (order) {
return order[name];
}
}
},
getSorters() {
getOrders() {
let me = this;
let strSorters = me.get('route.controller.sorters');
if (strSorters) {
return JSON.parse(strSorters);
let strOrders = me.get('route.controller.orders');
if (strOrders) {
return JSON.parse(strOrders);
}
},
actions: {
sort() {
let me = this;
let sorters = me.getSorters() || [];
let orders = me.getOrders() || [];
let name = me.get('name');
if (sorters.length) {
sorters = sorters.filter(sorter => !sorter.hasOwnProperty(name));
if (orders.length) {
orders = orders.filter(order => !order.hasOwnProperty(name));
}
let sorter = {};
sorter[name] =
let order = {};
order[name] =
me.get('order') === 'asc' ?
'desc' : 'asc';
// prepend sort
sorters.unshift(sorter);
me.set('route.controller.sorters', JSON.stringify(sorters));
orders.unshift(order);
me.set('route.controller.orders', JSON.stringify(orders));
},
removeSort() {
let me = this;
let sorters = me.getSorters();
if (sorters) {
let orders = me.getOrders();
if (orders) {
let name = me.get('name');
me.set('route.controller.sorters',
JSON.stringify(sorters.filter(sorter => !sorter.hasOwnProperty(name))));
me.set('route.controller.orders',
JSON.stringify(orders.filter(order => !order.hasOwnProperty(name))));
}
}
}

View File

@ -0,0 +1,16 @@
import { helper } from '@ember/component/helper';
export function customerApplicationImages([fileBasePath, appId, images, titles]) {
if (images) {
titles = titles ? titles.split(String.fromCharCode(0x1d)) : [];
return images.split(String.fromCharCode(0x1d)).map((image, i) => {
return {
image: fileBasePath + 'app/' + appId + '/' + image,
title: titles[i]
}
});
}
return [];
}
export default helper(customerApplicationImages);

View File

@ -6,7 +6,10 @@ export default BaseRoute.extend({
search: {
refreshModel: true
},
sorters: {
filters: {
refreshModel: true
},
orders: {
refreshModel: true
}
},

View File

@ -1,6 +1,7 @@
import Ember from 'ember';
import BaseRoute from '../base';
import EmberObject, { computed } from '@ember/object';
import RSVP from 'rsvp';
export default BaseRoute.extend({
breadcrumbs: [{route: 'customer-application.list', params: 1, text: 'Customer Application'},
@ -11,9 +12,10 @@ export default BaseRoute.extend({
})
}),
model() {
return this.get('modelClass').create({
return RSVP.hash({
enabled: true,
images: [{}]
images: [{}],
users: this.get('store').ajaxGet('user/list-for-select')
});
},
actions: {

View File

@ -2,10 +2,23 @@ import Ember from 'ember';
import BaseEditRoute from '../base-edit';
export default BaseEditRoute.extend({
service: Ember.inject.service('customer-property.service'),
afterModel(model) {
const me = this;
me._super(...arguments);
model.images = [{}];
this.set('breadcrumbs',
[{route: 'customer-application.list', params: 1, text: 'Customer Application'},
{text: 'Edit Customer Application[' + model.name + ']'}]);
},
actions: {
addImage() {
const me = this;
me.get('controller.model.images').pushObject({});
},
removeImage(image) {
const me = this;
me.get('controller.model.images').removeObject(image);
},
}
});

View File

@ -6,4 +6,18 @@ export default BaseListRoute.extend({
// model(params) {
// return this.get('store').ajaxGet('customer-property/app-list', params);
// }
actions: {
showContent(app) {
this.get('dialog').dialog({
title: app.name,
message: `<p>${app.content}</p>`,
buttons: {
ok: {
label: 'OK',
className: 'btn-info'
}
}
});
}
}
});

View File

@ -65,9 +65,6 @@ export default BaseListRoute.extend({
queryParams: {
criteria: {
refreshModel: true
},
filters: {
refreshModel: true
}
},
breadcrumbs: [{text: 'Customers'}],

View File

@ -0,0 +1,18 @@
<button {{action 'show'}} class="btn btn-minier btn-info2" data-rel="tooltip" title="Content Preview">
<i class="fa fa-search" aria-hidden="true"></i>
</button>
{{#if show}}
{{#modal-dialog title=model.name no-cancel=true submit=(action 'close') on-close=(action 'close') close-to-parent=false}}
<div class="widget-main">
<div class="space-12"></div>
<p>{{content}}</p>
<hr />
{{#with (customer-application-images file-base-path model.id model.images model.imageTitles) as |images|}}
{{#each images as |image index|}}
{{image-previews cover-title=true image-height=192 image-style='border-radius: 2%; border: 1px solid #DCDCDC; max-width: 480px;' previews=images index=index}}
{{/each}}
{{/with}}
</div>
{{/modal-dialog}}
{{/if}}

View File

@ -1,12 +1,23 @@
{{#if previews}}
<a href="{{previews.firstObject}}" data-rel="{{elementId}}_preview">
<img height="{{imageHeight}}" style="{{imageStyle}}" src="{{previews.firstObject}}" />
</a>
<div class="dropdown-menu">
{{#each previews as |preview index|}}
{{#if (gt index 0)}}
<a href="{{preview}}" data-rel="{{elementId}}_preview"></a>
{{#if images}}
<ul class="ace-thumbnails clearfix0">
{{#each images as |image i|}}
{{#if (eq i index)}}
<li class="no-margin no-padding clearfix" style="float: inherit; overflow: inherit; border-width: 0">
<a href="{{if image.image image.image iamge}}"
title={{image.title}} data-rel="{{elementId}}_preview">
<img height="{{image-height}}" style="{{image-style}}"
src="{{if image.image image.image image}}"
title={{image.title}} />
{{#if (and cover-title image.title)}}
<div class="text">
<div class="inner">{{image.title}}</div>
</div>
{{/if}}
</a>
</li>
{{else}}
<a style="display: none" href="{{if image.image image.image image}}" data-rel="{{elementId}}_preview" title={{image.title}}></a>
{{/if}}
{{/each}}
</div>
</ul>
{{/if}}

View File

@ -1,25 +1,33 @@
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header" style="padding: 9px;">
{{#if (not (get this 'no-close'))}}
<button type="button" class="bootbox-close-button close" data-dismiss="modal" aria-hidden="true">
&times;
</button>
{{/if}}
<h4 class="blue">{{title}}</h4>
</div>
<div class="modal-body no-padding">
{{yield}}
</div>
{{#if (not (get this 'no-footer'))}}
<div class="modal-footer no-margin-top">
{{#if (not (get this 'no-cancel'))}}
<button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">
<i class="ace-icon fa fa-times"></i>
Cancel
{{cancel-text}}
</button>
<button type="button" class="btn btn-sm btn-primary" {{action (if submit submit (route-action 'submit'))}}>
{{/if}}
{{#if (not (get this 'no-submit'))}}
<button type="button" class="btn btn-sm btn-primary"
{{action (if submit submit (route-action 'submit'))}}>
<i class="ace-icon fa fa-check"></i>
OK
{{submit-text}}
</button>
{{/if}}
</div>
{{/if}}
</div>
</div>

View File

@ -11,8 +11,9 @@
col-width=12
label=(if label label text)
options=options
value-field=(get this 'value-field')
text-field=(get this 'text-field')
value-field=value-field
text-field=text-field
text-exp=text-exp
}}
</form>
</div>

View File

@ -1,5 +1,4 @@
{{#form-content}}
{{form-input type='hidden' name='type'}}
{{form-input name='name' label='Name'}}
{{#form-input name='content' label='Content'}}
{{wysiwyg-editor model=model name='content'}}
@ -16,7 +15,6 @@
</div>
<div class="widget-body">
<div class="widget-main padding-4">
<!-- #section:pages/profile.feed -->
{{#each model.images as |image i|}}
<div class="col-xs-6 no-padding-left">
<div class="space-4"></div>
@ -29,12 +27,20 @@
{{image-input name=(concat 'images[' i ']') image=image}}
</div>
{{/each}}
<!-- /section:pages/profile.feed -->
</div>
</div>
</div>
{{/form-input}}
{{form-input name='note' label='Remark'}}
{{form-input-select2
nullable=true
name='owner'
label='Owner'
options=model.users
value-field='id'
text-field='name'
text-exp='$.employeeId ($.name)'
}}
<hr />
{{form-footer-buttons}}
{{/form-content}}

View File

@ -1,7 +1,39 @@
{{#form-content}}
{{input type='hidden' name='id' value=model.id}}
{{form-input type='hidden' name='type'}}
{{form-input name='name' label='Name'}}
{{#form-input name='content' label='Content'}}
{{wysiwyg-editor model=model name='content'}}
{{/form-input}}
{{#form-input name='image' label='Images'}}
<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 Image" {{action (route-action 'addImage')}}>
<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.images as |image i|}}
<div class="col-xs-6 no-padding-left">
<div class="space-4"></div>
{{input name=(concat 'imageTitles[' i ']') class='width-80' placeholder='Image title' value=image.title}}
&nbsp;
<a href="#" class="red" data-rel="tooltip" title="Remove Image" {{action (route-action 'removeImage' image)}}>
<i class="ace-icon fa fa-times bigger-125"></i>
</a>
<div class="space-2"></div>
{{image-input name=(concat 'images[' i ']') image=image}}
</div>
{{/each}}
</div>
</div>
</div>
{{/form-input}}
{{form-input-enabled label='Enabled' enabledText='TRUE' disabledText='FALSE'}}
{{form-input name='note' label='Remark'}}
<hr />

View File

@ -22,20 +22,33 @@
<th>
Name
</th>
<th>
Content
</th>
<th>
<i class="ace-icon fa fa-sticky-note-o bigger-110 hidden-480"></i>
Remark
</th>
{{#if ajax.user.admin}}
<th>
<i class="ace-icon fa fa-id-badge bigger-110 hidden-480"></i>
{{th-filter name='owner'
text='Owner'
label='Owner Filter'
options=model.users
value-field='id'
text-exp='$.employeeId ($.name)'
}}
</th>
{{/if}}
<th>
<i class="ace-icon fa fa-exchange bigger-110 hidden-480"></i>
Enabled
</th>
{{#if ajax.user.admin}}
<th>
<i class="ace-icon fa fa-cogs bigger-110 hidden-480"></i>
Settings
</th>
{{/if}}
</tr>
</thead>
@ -46,16 +59,20 @@
{{it.name}}
</td>
<td>
{{#if ajax.user.admin}}
{{editable-cell model=it field='note'}}
{{else}}
{{it.note}}
{{/if}}
{{image-previews previews=(customer-application-images model.fileBasePath it.id it.images it.imageTitles)}}
{{customer-application/preview-btn model=it file-base-path=model.fileBasePath}}
</td>
<td>
{{editable-cell model=it field='note'}}
</td>
{{#if ajax.user.admin}}
<td>
{{option-text model.users it.owner 'id' 'name' '$.employeeId ($.name)'}}
</td>
{{/if}}
<td>
{{status-cell model=it enabledText='TRUE' disabledText='FALSE'}}
</td>
{{#if ajax.user.admin}}
<td>
<div class="hidden-sm hidden-xs btn-group">
{{status-toggle-button model=it}}
@ -111,7 +128,6 @@
</div>
</div>
</td>
{{/if}}
</tr>
{{/each}}
</tbody>

View File

@ -0,0 +1,24 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('customer-application/preview-btn', 'Integration | Component | customer application/preview btn', {
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`{{customer-application/preview-btn}}`);
assert.equal(this.$().text().trim(), '');
// Template block usage:
this.render(hbs`
{{#customer-application/preview-btn}}
template block text
{{/customer-application/preview-btn}}
`);
assert.equal(this.$().text().trim(), 'template block text');
});

View File

@ -0,0 +1,17 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('customer-application-images', 'helper:customer-application-images', {
integration: true
});
// Replace this with your real tests.
test('it renders', function(assert) {
this.set('inputValue', '1234');
this.render(hbs`{{customer-application-images inputValue}}`);
assert.equal(this.$().text().trim(), '1234');
});