diff --git a/backend/src/main/java/io/dataease/controller/dataset/DataSetTableController.java b/backend/src/main/java/io/dataease/controller/dataset/DataSetTableController.java index c75cef5f5e..54841a4805 100644 --- a/backend/src/main/java/io/dataease/controller/dataset/DataSetTableController.java +++ b/backend/src/main/java/io/dataease/controller/dataset/DataSetTableController.java @@ -97,7 +97,7 @@ public class DataSetTableController { @ApiOperation("查询预览数据") @PostMapping("getPreviewData/{page}/{pageSize}") public Map getPreviewData(@RequestBody DataSetTableRequest dataSetTableRequest, @PathVariable Integer page, @PathVariable Integer pageSize) throws Exception { - return dataSetTableService.getPreviewData(dataSetTableRequest, page, pageSize); + return dataSetTableService.getPreviewData(dataSetTableRequest, page, pageSize, null); } @ApiOperation("根据sql查询预览数据") diff --git a/backend/src/main/java/io/dataease/controller/dataset/DataSetTableFieldController.java b/backend/src/main/java/io/dataease/controller/dataset/DataSetTableFieldController.java index e10f7b84c7..7d66eac976 100644 --- a/backend/src/main/java/io/dataease/controller/dataset/DataSetTableFieldController.java +++ b/backend/src/main/java/io/dataease/controller/dataset/DataSetTableFieldController.java @@ -4,25 +4,30 @@ import com.auth0.jwt.JWT; import com.auth0.jwt.interfaces.DecodedJWT; import com.github.xiaoymin.knife4j.annotations.ApiSupport; import io.dataease.auth.filter.F2CLinkFilter; +import io.dataease.base.domain.DatasetTable; import io.dataease.base.domain.DatasetTableField; +import io.dataease.commons.exception.DEException; +import io.dataease.controller.request.dataset.DataSetTableRequest; import io.dataease.controller.request.dataset.MultFieldValuesRequest; import io.dataease.controller.response.DatasetTableField4Type; +import io.dataease.i18n.Translator; import io.dataease.service.dataset.DataSetFieldService; import io.dataease.service.dataset.DataSetTableFieldsService; +import io.dataease.service.dataset.DataSetTableService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; +import cn.hutool.core.collection.CollectionUtil; + import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.TreeSet; +import java.util.*; import java.util.stream.Collectors; /** @@ -40,6 +45,9 @@ public class DataSetTableFieldController { @Autowired private DataSetFieldService dataSetFieldService; + @Resource + private DataSetTableService dataSetTableService; + @ApiOperation("查询表下属字段") @PostMapping("list/{tableId}") public List list(@PathVariable String tableId) { @@ -74,6 +82,15 @@ public class DataSetTableFieldController { @PostMapping("save") public DatasetTableField save(@RequestBody DatasetTableField datasetTableField) { dataSetTableFieldsService.checkFieldName(datasetTableField); + try { + // 执行一次sql,确保数据集中所有字段均能正确执行 + DatasetTable datasetTable = dataSetTableService.get(datasetTableField.getTableId()); + DataSetTableRequest dataSetTableRequest = new DataSetTableRequest(); + BeanUtils.copyProperties(datasetTable, dataSetTableRequest); + dataSetTableService.getPreviewData(dataSetTableRequest, 1, 1, Collections.singletonList(datasetTableField)); + } catch (Exception e) { + DEException.throwException(Translator.get("i18n_calc_field_error")); + } return dataSetTableFieldsService.save(datasetTableField); } @@ -85,8 +102,10 @@ public class DataSetTableFieldController { @ApiOperation("多字段值枚举") @PostMapping("linkMultFieldValues") - public List linkMultFieldValues(@RequestBody MultFieldValuesRequest multFieldValuesRequest) throws Exception { - HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + public List linkMultFieldValues(@RequestBody MultFieldValuesRequest multFieldValuesRequest) + throws Exception { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) + .getRequest(); String linkToken = request.getHeader(F2CLinkFilter.LINK_TOKEN_KEY); DecodedJWT jwt = JWT.decode(linkToken); Long userId = jwt.getClaim("userId").asLong(); @@ -99,18 +118,21 @@ public class DataSetTableFieldController { public List multFieldValues(@RequestBody MultFieldValuesRequest multFieldValuesRequest) throws Exception { List results = new ArrayList<>(); for (String fieldId : multFieldValuesRequest.getFieldIds()) { - results.addAll(dataSetFieldService.fieldValues(fieldId, multFieldValuesRequest.getUserId())); + List fieldValues = dataSetFieldService.fieldValues(fieldId, multFieldValuesRequest.getUserId()); + if (CollectionUtil.isNotEmpty(fieldValues)) { + results.addAll(fieldValues); + } + } ArrayList list = results.stream().collect( Collectors.collectingAndThen( Collectors.toCollection( () -> new TreeSet<>(Comparator.comparing(t -> { - if (ObjectUtils.isEmpty(t)) return ""; + if (ObjectUtils.isEmpty(t)) + return ""; return t.toString(); - })) - ), ArrayList::new - ) - ); + }))), + ArrayList::new)); return list; } } diff --git a/backend/src/main/java/io/dataease/plugins/server/ThemeServer.java b/backend/src/main/java/io/dataease/plugins/server/ThemeServer.java index 08e2d10374..99dc4c5fec 100644 --- a/backend/src/main/java/io/dataease/plugins/server/ThemeServer.java +++ b/backend/src/main/java/io/dataease/plugins/server/ThemeServer.java @@ -4,12 +4,13 @@ import java.util.List; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import io.dataease.commons.exception.DEException; +import io.dataease.commons.utils.LogUtil; import io.dataease.plugins.config.SpringContextUtil; import io.dataease.plugins.xpack.theme.dto.ThemeDto; import io.dataease.plugins.xpack.theme.dto.ThemeItem; @@ -20,10 +21,8 @@ import io.dataease.plugins.xpack.theme.service.ThemeXpackService; @RestController public class ThemeServer { - - @PostMapping("/themes") - public List themes(){ + public List themes() { ThemeXpackService themeXpackService = SpringContextUtil.getBean(ThemeXpackService.class); return themeXpackService.themes(); @@ -36,15 +35,22 @@ public class ThemeServer { } @PostMapping("/save") - public void save(@RequestPart("request") ThemeRequest request, @RequestPart(value = "file", required = false) MultipartFile bodyFile) { + public void save(@RequestPart("request") ThemeRequest request, + @RequestPart(value = "file", required = false) MultipartFile bodyFile) { ThemeXpackService themeXpackService = SpringContextUtil.getBean(ThemeXpackService.class); - themeXpackService.save(request, bodyFile); + try { + themeXpackService.save(request, bodyFile); + } catch (Exception e) { + LogUtil.error(e.getMessage(), e); + DEException.throwException(e); + } + } @PostMapping("/delete/{themeId}") - public void save(@PathVariable("themeId") int themeId) { + public void delete(@PathVariable("themeId") int themeId) { ThemeXpackService themeXpackService = SpringContextUtil.getBean(ThemeXpackService.class); themeXpackService.deleteTheme(themeId); } - + } diff --git a/backend/src/main/java/io/dataease/plugins/server/XEmailTaskServer.java b/backend/src/main/java/io/dataease/plugins/server/XEmailTaskServer.java index 78dfecc793..9ecd645704 100644 --- a/backend/src/main/java/io/dataease/plugins/server/XEmailTaskServer.java +++ b/backend/src/main/java/io/dataease/plugins/server/XEmailTaskServer.java @@ -34,7 +34,7 @@ public class XEmailTaskServer { @PostMapping("/queryTasks/{goPage}/{pageSize}") public Pager> queryTask(@PathVariable int goPage, @PathVariable int pageSize, - @RequestBody XpackGridRequest request) { + @RequestBody XpackGridRequest request) { EmailXpackService emailXpackService = SpringContextUtil.getBean(EmailXpackService.class); Page page = PageHelper.startPage(goPage, pageSize, true); List tasks = emailXpackService.taskGrid(request); @@ -87,7 +87,7 @@ public class XEmailTaskServer { try { fileId = emailXpackService.print(url, token, buildPixel(request.getPixel())); } catch (Exception e) { - LogUtil.error(e); + LogUtil.error(e.getMessage(), e); DEException.throwException("预览失败,请联系管理员"); } String imageUrl = "/system/ui/image/" + fileId; @@ -116,7 +116,7 @@ public class XEmailTaskServer { @PostMapping("/queryInstancies/{goPage}/{pageSize}") public Pager> instancesGrid(@PathVariable int goPage, @PathVariable int pageSize, - @RequestBody XpackGridRequest request) { + @RequestBody XpackGridRequest request) { EmailXpackService emailXpackService = SpringContextUtil.getBean(EmailXpackService.class); Page page = PageHelper.startPage(goPage, pageSize, true); List instances = emailXpackService.taskInstanceGrid(request); diff --git a/backend/src/main/java/io/dataease/provider/datasource/JdbcProvider.java b/backend/src/main/java/io/dataease/provider/datasource/JdbcProvider.java index df88db99c1..48a2af205f 100644 --- a/backend/src/main/java/io/dataease/provider/datasource/JdbcProvider.java +++ b/backend/src/main/java/io/dataease/provider/datasource/JdbcProvider.java @@ -2,6 +2,7 @@ package io.dataease.provider.datasource; import com.alibaba.druid.filter.Filter; import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.pool.DruidPooledConnection; import com.alibaba.druid.wall.WallFilter; import com.google.gson.Gson; import io.dataease.commons.constants.DatasourceTypes; @@ -18,6 +19,7 @@ import javax.annotation.PostConstruct; import java.beans.PropertyVetoException; import java.io.File; import java.io.IOException; +import java.lang.reflect.Method; import java.net.URL; import java.sql.*; import java.util.*; @@ -143,6 +145,10 @@ public class JdbcProvider extends DatasourceProvider { } List list = new LinkedList<>(); try (Connection connection = getConnectionFromPool(datasourceRequest)) { + if (datasourceRequest.getDatasource().getType().equalsIgnoreCase("oracle")) { + Method setRemarksReporting = extendedJdbcClassLoader.loadClass("oracle.jdbc.driver.OracleConnection").getMethod("setRemarksReporting",boolean.class); + setRemarksReporting.invoke(((DruidPooledConnection) connection).getConnection(), true); + } DatabaseMetaData databaseMetaData = connection.getMetaData(); ResultSet resultSet = databaseMetaData.getColumns(null, "%", datasourceRequest.getTable(), "%"); while (resultSet.next()) { diff --git a/backend/src/main/java/io/dataease/service/dataset/DataSetTableService.java b/backend/src/main/java/io/dataease/service/dataset/DataSetTableService.java index 60ec16233b..b6eace9983 100644 --- a/backend/src/main/java/io/dataease/service/dataset/DataSetTableService.java +++ b/backend/src/main/java/io/dataease/service/dataset/DataSetTableService.java @@ -459,16 +459,16 @@ public class DataSetTableService { List roleIds = new ArrayList<>(); Long deptId = null; - if(user == null && userId == null ){ + if (user == null && userId == null) { return datasetRowPermissions; } - if(user != null && userId != null ){ + if (user != null && userId != null) { return datasetRowPermissions; } - if(user != null){ - if(user.getIsAdmin()){ + if (user != null) { + if (user.getIsAdmin()) { return datasetRowPermissions; } userId = user.getUserId(); @@ -476,9 +476,9 @@ public class DataSetTableService { roleIds = user.getRoles().stream().map(CurrentRoleDto::getId).collect(Collectors.toList()); } - if(userId != null){ + if (userId != null) { SysUserEntity userEntity = authUserService.getUserById(userId); - if(userEntity.getIsAdmin()){ + if (userEntity.getIsAdmin()) { return datasetRowPermissions; } deptId = userEntity.getDeptId(); @@ -514,7 +514,7 @@ public class DataSetTableService { List customFilter = new ArrayList<>(); for (DatasetRowPermissions datasetRowPermissions : rowPermissions(datasetTable.getId(), user)) { ChartFieldCustomFilterDTO dto = new ChartFieldCustomFilterDTO(); - if(StringUtils.isEmpty(datasetRowPermissions.getDatasetFieldId())){ + if (StringUtils.isEmpty(datasetRowPermissions.getDatasetFieldId())) { continue; } DatasetTableField field = getFieldById(fields, datasetRowPermissions.getDatasetFieldId()); @@ -525,7 +525,7 @@ public class DataSetTableService { dto.setId(field.getId()); dto.setFilterType(datasetRowPermissions.getFilterType()); if (datasetRowPermissions.getFilterType().equalsIgnoreCase("logic")) { - if(StringUtils.isEmpty(datasetRowPermissions.getFilter())){ + if (StringUtils.isEmpty(datasetRowPermissions.getFilter())) { continue; } List lists = JSONObject.parseArray(datasetRowPermissions.getFilter(), ChartCustomFilterItemDTO.class); @@ -536,7 +536,7 @@ public class DataSetTableService { dto.setLogic(datasetRowPermissions.getLogic()); customFilter.add(dto); } else { - if(StringUtils.isEmpty(datasetRowPermissions.getEnumCheckField())){ + if (StringUtils.isEmpty(datasetRowPermissions.getEnumCheckField())) { continue; } dto.setEnumCheckField(Arrays.asList(datasetRowPermissions.getEnumCheckField().split(",").clone())); @@ -546,10 +546,13 @@ public class DataSetTableService { return customFilter; } - public Map getPreviewData(DataSetTableRequest dataSetTableRequest, Integer page, Integer pageSize) throws Exception { + public Map getPreviewData(DataSetTableRequest dataSetTableRequest, Integer page, Integer pageSize, List extFields) throws Exception { Map map = new HashMap<>(); DatasetTableField datasetTableField = DatasetTableField.builder().tableId(dataSetTableRequest.getId()).checked(Boolean.TRUE).build(); List fields = dataSetTableFieldsService.list(datasetTableField); + if (CollectionUtils.isNotEmpty(extFields)) { + fields.addAll(extFields); + } if (CollectionUtils.isEmpty(fields)) { map.put("fields", fields); map.put("data", new ArrayList<>()); @@ -578,7 +581,7 @@ public class DataSetTableService { if (ObjectUtils.isEmpty(ds)) { throw new RuntimeException(Translator.get("i18n_datasource_delete")); } - if(StringUtils.isNotEmpty(ds.getStatus()) && ds.getStatus().equalsIgnoreCase("Error")){ + if (StringUtils.isNotEmpty(ds.getStatus()) && ds.getStatus().equalsIgnoreCase("Error")) { throw new Exception(Translator.get("i18n_invalid_ds")); } DatasourceProvider datasourceProvider = ProviderFactory.getProvider(ds.getType()); @@ -646,7 +649,7 @@ public class DataSetTableService { if (ObjectUtils.isEmpty(ds)) { throw new RuntimeException(Translator.get("i18n_datasource_delete")); } - if(StringUtils.isNotEmpty(ds.getStatus()) && ds.getStatus().equalsIgnoreCase("Error")){ + if (StringUtils.isNotEmpty(ds.getStatus()) && ds.getStatus().equalsIgnoreCase("Error")) { throw new Exception(Translator.get("i18n_invalid_ds")); } DatasourceProvider datasourceProvider = ProviderFactory.getProvider(ds.getType()); diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties index 2d31e92209..04edcb89a8 100644 --- a/backend/src/main/resources/i18n/messages_en_US.properties +++ b/backend/src/main/resources/i18n/messages_en_US.properties @@ -300,3 +300,4 @@ i18n_invalid_ds=Invalid Datasource 定时报告=Regular report i18n_rp_exist=Row permission of the same type already exists i18n_field_name_repeat=Field name can't repeat +i18n_calc_field_error=Field expression error diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties index 60f075497f..fe61bae269 100644 --- a/backend/src/main/resources/i18n/messages_zh_CN.properties +++ b/backend/src/main/resources/i18n/messages_zh_CN.properties @@ -299,3 +299,4 @@ i18n_invalid_ds=无效数据源 定时报告=定时报告 i18n_rp_exist=已有同类型的行权限存在 i18n_field_name_repeat=字段名不能重复 +i18n_calc_field_error=字段表达式语法错误 diff --git a/backend/src/main/resources/i18n/messages_zh_TW.properties b/backend/src/main/resources/i18n/messages_zh_TW.properties index 0e25c2bf28..529af64b07 100644 --- a/backend/src/main/resources/i18n/messages_zh_TW.properties +++ b/backend/src/main/resources/i18n/messages_zh_TW.properties @@ -302,3 +302,4 @@ i18n_invalid_ds=無效數據源 定时报告=定時報告 i18n_rp_exist=已有同類型餓行權限存在 i18n_field_name_repeat=字段名不能重復 +i18n_calc_field_error=字段表達式語法錯誤 diff --git a/frontend/src/components/canvas/utils/utils.js b/frontend/src/components/canvas/utils/utils.js index d5d4fa84fa..dbc4161bd4 100644 --- a/frontend/src/components/canvas/utils/utils.js +++ b/frontend/src/components/canvas/utils/utils.js @@ -3,6 +3,10 @@ import { HYPERLINKS } from '@/components/canvas/custom-component/component-list' +import { + ApplicationContext +} from '@/utils/ApplicationContext' + export function deepCopy(target) { if (typeof target === 'object') { const result = Array.isArray(target) ? [] : {} @@ -62,25 +66,10 @@ export function mobile2MainCanvas(mainSource, mobileSource) { export function panelInit(componentDatas) { componentDatas.forEach(item => { if (item.component && item.component === 'de-date') { - if (item.serviceName === 'timeDateWidget' && item.options.attrs && !item.options.attrs.default) { - item.options.attrs.default = { - isDynamic: false, - dkey: 0, - dynamicPrefix: 1, - dynamicInfill: 'day', - dynamicSuffix: 'before' - } - } - if (item.serviceName === 'timeDateRangeWidget' && item.options.attrs && !item.options.attrs.default) { - item.options.attrs.default = { - isDynamic: false, - dkey: 0, - sDynamicPrefix: 1, - sDynamicInfill: 'day', - sDynamicSuffix: 'before', - eDynamicPrefix: 1, - eDynamicInfill: 'day', - eDynamicSuffix: 'after' + if (item.options.attrs && !item.options.attrs.default) { + const widget = ApplicationContext.getService(item.serviceName) + if (widget && widget.defaultSetting) { + item.options.attrs.default = widget.defaultSetting() } } } diff --git a/frontend/src/components/widget/DeWidget/DeDate.vue b/frontend/src/components/widget/DeWidget/DeDate.vue index 39176372d6..403accd14e 100644 --- a/frontend/src/components/widget/DeWidget/DeDate.vue +++ b/frontend/src/components/widget/DeWidget/DeDate.vue @@ -72,9 +72,7 @@ export default { this.setCondition() }, 'defaultValueStr': function(value, old) { - if ((this.element.serviceName === 'timeDateWidget' || this.element.serviceName === 'timeDateRangeWidget') && - this.element.options.attrs.default.isDynamic) { - // 如果设置了动态时间 不做任何操作 + if (this.element.options.attrs.default.isDynamic) { return } if (value === old) return @@ -82,13 +80,10 @@ export default { this.dateChange(value) }, 'defaultoptions': function(val, old) { - // console.log('default chaneg') - if (this.element.serviceName !== 'timeDateWidget' || this.element.serviceName === 'timeDateRangeWidget') { - if (!this.element.options.attrs.default.isDynamic) { - this.values = this.fillValueDerfault() - this.dateChange(this.values) - return - } + if (!this.element.options.attrs.default.isDynamic) { + this.values = this.fillValueDerfault() + this.dateChange(this.values) + return } if (val === old) return const widget = ApplicationContext.getService(this.element.serviceName) @@ -97,9 +92,7 @@ export default { } }, created() { - if ((this.element.serviceName === 'timeDateWidget' || this.element.serviceName === 'timeDateRangeWidget') && this - .element.options.attrs.default && this.element.options - .attrs.default.isDynamic) { + if (this.element.options.attrs.default && this.element.options.attrs.default.isDynamic) { if (this.element.options.attrs.default) { const widget = ApplicationContext.getService(this.element.serviceName) this.values = widget.dynamicDateFormNow(this.element) diff --git a/frontend/src/components/widget/serviceImpl/TimeDateRangeServiceImpl.js b/frontend/src/components/widget/serviceImpl/TimeDateRangeServiceImpl.js index 34edf26be4..5e3083c3ee 100644 --- a/frontend/src/components/widget/serviceImpl/TimeDateRangeServiceImpl.js +++ b/frontend/src/components/widget/serviceImpl/TimeDateRangeServiceImpl.js @@ -80,6 +80,9 @@ class TimeDateRangeServiceImpl extends WidgetService { return field['deType'] === 1 }) } + defaultSetting() { + return dialogPanel.options.attrs.default + } getStartDayOfWeek() { var now = new Date() // 当前日期 var nowDayOfWeek = now.getDay() @@ -159,18 +162,10 @@ class TimeDateRangeServiceImpl extends WidgetService { const nowYear = now.getFullYear() const nowDate = now.getDate() - const tarYear = nowYear if (dynamicSuffix === 'before') { - const deffMonth = nowMonth - dynamicPrefix - let diffYear = deffMonth / 12 - if (deffMonth < 0) { - diffYear -= 1 - } - return new Date(tarYear + diffYear, nowMonth - dynamicPrefix % 12, nowDate).getTime() + return new Date(nowYear, nowMonth - dynamicPrefix, nowDate).getTime() } else { - const deffMonth = nowMonth + dynamicPrefix - const diffYear = deffMonth / 12 - return new Date(tarYear + diffYear, deffMonth % 12, nowDate).getTime() + return new Date(nowYear, nowMonth + dynamicPrefix, nowDate).getTime() } } if (dynamicInfill === 'year') { diff --git a/frontend/src/components/widget/serviceImpl/TimeDateServiceImpl.js b/frontend/src/components/widget/serviceImpl/TimeDateServiceImpl.js index 98bfcc7af6..d7f6725af9 100644 --- a/frontend/src/components/widget/serviceImpl/TimeDateServiceImpl.js +++ b/frontend/src/components/widget/serviceImpl/TimeDateServiceImpl.js @@ -1,4 +1,6 @@ -import { WidgetService } from '../service/WidgetService' +import { + WidgetService +} from '../service/WidgetService' const leftPanel = { icon: 'iconfont icon-ri', @@ -19,7 +21,23 @@ const dialogPanel = { dkey: 0, dynamicPrefix: 1, dynamicInfill: 'day', - dynamicSuffix: 'before' + dynamicSuffix: 'before', + radioOptions: [{ value: false, text: 'dynamic_time.fix' }, { value: true, text: 'dynamic_time.dynamic' }], + relativeOptions: [ + { value: 0, text: 'dynamic_time.today' }, + { value: 1, text: 'dynamic_time.yesterday' }, + { value: 2, text: 'dynamic_time.firstOfMonth' }, + { value: 3, text: 'dynamic_time.custom' } + ], + custom: { + unitsOptions: [ + { value: 'day', text: 'dynamic_time.date' }, + { value: 'week', text: 'dynamic_time.week' }, + { value: 'month', text: 'dynamic_time.month' }, + { value: 'year', text: 'dynamic_time.year' } + ], + limits: [1, 12] + } } }, value: '' @@ -45,7 +63,9 @@ const drawPanel = { class TimeDateServiceImpl extends WidgetService { constructor(options = {}) { - Object.assign(options, { name: 'timeDateWidget' }) + Object.assign(options, { + name: 'timeDateWidget' + }) super(options) this.filterDialog = true this.showSwitch = false @@ -71,6 +91,9 @@ class TimeDateServiceImpl extends WidgetService { return field['deType'] === 1 }) } + defaultSetting() { + return dialogPanel.options.attrs.default + } dynamicDateFormNow(element) { if (element.options.attrs.default === null || typeof element.options.attrs.default === 'undefined' || !element.options.attrs.default.isDynamic) return null @@ -111,18 +134,10 @@ class TimeDateServiceImpl extends WidgetService { const nowYear = now.getFullYear() const nowDate = now.getDate() - const tarYear = nowYear if (dynamicSuffix === 'before') { - const deffMonth = nowMonth - dynamicPrefix - let diffYear = deffMonth / 12 - if (deffMonth < 0) { - diffYear -= 1 - } - return new Date(tarYear + diffYear, nowMonth - dynamicPrefix % 12, nowDate).getTime() + return new Date(nowYear, nowMonth - dynamicPrefix, nowDate).getTime() } else { - const deffMonth = nowMonth + dynamicPrefix - const diffYear = deffMonth / 12 - return new Date(tarYear + diffYear, deffMonth % 12, nowDate).getTime() + return new Date(nowYear, nowMonth + dynamicPrefix, nowDate).getTime() } } if (dynamicInfill === 'year') { @@ -136,5 +151,7 @@ class TimeDateServiceImpl extends WidgetService { } } } -const timeDateServiceImpl = new TimeDateServiceImpl({ name: 'timeDateWidget' }) +const timeDateServiceImpl = new TimeDateServiceImpl({ + name: 'timeDateWidget' +}) export default timeDateServiceImpl diff --git a/frontend/src/components/widget/serviceImpl/TimeMonthServiceImpl.js b/frontend/src/components/widget/serviceImpl/TimeMonthServiceImpl.js index 45f77dd99d..8a09aafc4f 100644 --- a/frontend/src/components/widget/serviceImpl/TimeMonthServiceImpl.js +++ b/frontend/src/components/widget/serviceImpl/TimeMonthServiceImpl.js @@ -13,7 +13,27 @@ const dialogPanel = { placeholder: 'deyearmonth.placeholder', viewIds: [], fieldId: '', - dragItems: [] + dragItems: [], + default: { + isDynamic: false, + dkey: 0, + dynamicPrefix: 1, + dynamicInfill: 'month', + dynamicSuffix: 'before', + radioOptions: [{ value: false, text: 'dynamic_month.fix' }, { value: true, text: 'dynamic_month.dynamic' }], + relativeOptions: [ + { value: 0, text: 'dynamic_month.current' }, + { value: 1, text: 'dynamic_month.last' }, + { value: 2, text: 'dynamic_month.firstOfYear' }, + { value: 3, text: 'dynamic_time.custom' } + ], + custom: { + unitsOptions: [ + { value: 'month', text: 'dynamic_time.month' } + ], + limits: [0, 10] + } + } }, value: '' }, @@ -64,6 +84,39 @@ class TimeMonthServiceImpl extends WidgetService { return field['deType'] === 1 }) } + defaultSetting() { + return dialogPanel.options.attrs.default + } + dynamicDateFormNow(element) { + const now = new Date() + const nowMonth = now.getMonth() + const nowYear = now.getFullYear() + const nowDate = now.getDate() + if (element.options.attrs.default === null || typeof element.options.attrs.default === 'undefined' || !element.options.attrs.default.isDynamic) return null + + if (element.options.attrs.default.dkey === 0) { + return Date.now() + } + + if (element.options.attrs.default.dkey === 1) { + return new Date(nowYear, nowMonth - 1, nowDate).getTime() + } + + if (element.options.attrs.default.dkey === 2) { + return new Date(nowYear, 0, 1).getTime() + } + + if (element.options.attrs.default.dkey === 3) { + const dynamicPrefix = parseInt(element.options.attrs.default.dynamicPrefix) + const dynamicSuffix = element.options.attrs.default.dynamicSuffix + + if (dynamicSuffix === 'before') { + return new Date(nowYear, nowMonth - dynamicPrefix, nowDate).getTime() + } else { + return new Date(nowYear, nowMonth + dynamicPrefix, nowDate).getTime() + } + } + } } const timeMonthServiceImpl = new TimeMonthServiceImpl() export default timeMonthServiceImpl diff --git a/frontend/src/components/widget/serviceImpl/TimeYearServiceImpl.js b/frontend/src/components/widget/serviceImpl/TimeYearServiceImpl.js index 2beb3d6935..900b681bcb 100644 --- a/frontend/src/components/widget/serviceImpl/TimeYearServiceImpl.js +++ b/frontend/src/components/widget/serviceImpl/TimeYearServiceImpl.js @@ -13,7 +13,26 @@ const dialogPanel = { placeholder: 'deyear.placeholder', viewIds: [], fieldId: '', - dragItems: [] + dragItems: [], + default: { + isDynamic: false, + dkey: 0, + dynamicPrefix: 1, + dynamicInfill: 'year', + dynamicSuffix: 'before', + radioOptions: [{ value: false, text: 'dynamic_year.fix' }, { value: true, text: 'dynamic_year.dynamic' }], + relativeOptions: [ + { value: 0, text: 'dynamic_year.current' }, + { value: 1, text: 'dynamic_year.last' }, + { value: 2, text: 'dynamic_time.custom' } + ], + custom: { + unitsOptions: [ + { value: 'year', text: 'dynamic_time.year' } + ], + limits: [0, 10] + } + } }, value: '' }, @@ -65,6 +84,36 @@ class TimeYearServiceImpl extends WidgetService { return field['deType'] === 1 }) } + defaultSetting() { + return dialogPanel.options.attrs.default + } + dynamicDateFormNow(element) { + if (element.options.attrs.default === null || typeof element.options.attrs.default === 'undefined' || !element.options.attrs.default.isDynamic) return null + + if (element.options.attrs.default.dkey === 0) { + return Date.now() + } + + if (element.options.attrs.default.dkey === 1) { + const now = new Date() + const nowYear = now.getFullYear() + const nowMonth = now.getMonth() + const nowDate = now.getDate() + return new Date(nowYear - 1, nowMonth, nowDate).getTime() + } + + if (element.options.attrs.default.dkey === 2) { + const dynamicPrefix = parseInt(element.options.attrs.default.dynamicPrefix) + const dynamicSuffix = element.options.attrs.default.dynamicSuffix + + const now = new Date() + const nowMonth = now.getMonth() + const nowYear = now.getFullYear() + const nowDate = now.getDate() + + return new Date(dynamicSuffix === 'before' ? (nowYear - dynamicPrefix) : (nowYear + dynamicPrefix), nowMonth, nowDate).getTime() + } + } } const timeYearServiceImpl = new TimeYearServiceImpl() export default timeYearServiceImpl diff --git a/frontend/src/lang/en.js b/frontend/src/lang/en.js index 405308d002..25101156d9 100644 --- a/frontend/src/lang/en.js +++ b/frontend/src/lang/en.js @@ -131,6 +131,7 @@ export default { default_login: 'Normal' }, commons: { + apply: 'Apply', search: 'Search', folder: 'Folder', no_target_permission: 'No permission', @@ -437,7 +438,10 @@ export default { background: 'Background color', custom: 'Custom color', otherSave: 'Theme Save as', - info: 'Theme info' + info: 'Theme info', + add: 'Add Theme', + please_input_name: 'Please enter a name', + name_repeat: 'Name already exists' }, tagsView: { refresh: 'Refresh', @@ -1759,6 +1763,19 @@ export default { cquarter: 'This Quarter', cyear: 'This Year' }, + dynamic_year: { + fix: 'Fixed Year', + dynamic: 'Dynamic Year', + current: 'This Year', + last: 'Last Year' + }, + dynamic_month: { + fix: 'Fixed Month', + dynamic: 'Dynamic Month', + current: 'This Month', + last: 'Last Month', + firstOfYear: 'First month of this year' + }, wizard: { welcome_title: 'Welcome To Use DataEase', welcome_hint: 'Open source data visual analysis tool available to everyone', diff --git a/frontend/src/lang/tw.js b/frontend/src/lang/tw.js index def92763b9..abe2e65799 100644 --- a/frontend/src/lang/tw.js +++ b/frontend/src/lang/tw.js @@ -131,6 +131,7 @@ export default { default_login: '普通登錄' }, commons: { + apply: '應用', search: '搜索', folder: '目錄', no_target_permission: '沒有權限', @@ -437,7 +438,10 @@ export default { background: '背景顏色', custom: '自定義顏色', otherSave: '主題另存為', - info: '主題信息' + info: '主題信息', + add: '新增主題', + please_input_name: '請輸入名稱', + name_repeat: '名稱已存在' }, tagsView: { refresh: '刷新', @@ -1771,6 +1775,19 @@ export default { cquarter: '本季', cyear: '本年' }, + dynamic_year: { + fix: '固定年份', + dynamic: '動態年份', + current: '當年', + last: '去年' + }, + dynamic_month: { + fix: '固定年月', + dynamic: '動態年月', + current: '本月', + last: '上月', + firstOfYear: '當年首月' + }, wizard: { welcome_title: '欢迎使用DataEase', welcome_hint: '人人可用的开源数据可视化分析工具', diff --git a/frontend/src/lang/zh.js b/frontend/src/lang/zh.js index 1869dc18e7..1445db33d2 100644 --- a/frontend/src/lang/zh.js +++ b/frontend/src/lang/zh.js @@ -131,6 +131,7 @@ export default { default_login: '普通登录' }, commons: { + apply: '应用', search: '搜索', folder: '目录', no_target_permission: '没有权限', @@ -438,7 +439,10 @@ export default { background: '背景颜色', custom: '自定义颜色', otherSave: '主题另存为', - info: '主题信息' + info: '主题信息', + add: '新增主题', + please_input_name: '请输入名称', + name_repeat: '名称已存在' }, tagsView: { refresh: '刷新', @@ -1781,6 +1785,19 @@ export default { cquarter: '本季', cyear: '本年' }, + dynamic_year: { + fix: '固定年份', + dynamic: '动态年份', + current: '当年', + last: '去年' + }, + dynamic_month: { + fix: '固定年月', + dynamic: '动态年月', + current: '当月', + last: '上月', + firstOfYear: '当年首月' + }, wizard: { welcome_title: '欢迎使用DataEase', welcome_hint: '人人可用的开源数据可视化分析工具', diff --git a/frontend/src/views/chart/chart/chart.js b/frontend/src/views/chart/chart/chart.js index 2db3f6c550..e0f36d1f81 100644 --- a/frontend/src/views/chart/chart/chart.js +++ b/frontend/src/views/chart/chart/chart.js @@ -13,11 +13,11 @@ export const DEFAULT_SIZE = { barDefault: true, barWidth: 40, barGap: 0.4, - lineWidth: 1, + lineWidth: 2, lineType: 'solid', lineSymbol: 'circle', lineSymbolSize: 4, - lineSmooth: false, + lineSmooth: true, lineArea: false, pieInnerRadius: 0, pieOuterRadius: 80, diff --git a/frontend/src/views/chart/chart/liquid/liquid.js b/frontend/src/views/chart/chart/liquid/liquid.js index d317e06232..2d61bc2066 100644 --- a/frontend/src/views/chart/chart/liquid/liquid.js +++ b/frontend/src/views/chart/chart/liquid/liquid.js @@ -5,7 +5,7 @@ import { DEFAULT_SIZE } from '@/views/chart/chart/chart' export function baseLiquid(plot, container, chart) { let value = 0 const colors = [] - let max, radius, outlineBorder, outlineDistance, waveLength, waveCount, bgColor, shape, labelContent, title + let max, radius, bgColor, shape, labelContent, title if (chart.data) { if (chart.data.series.length > 0) { value = chart.data.series[0].data[0] @@ -26,10 +26,6 @@ export function baseLiquid(plot, container, chart) { const size = JSON.parse(JSON.stringify(customAttr.size)) max = size.liquidMax ? size.liquidMax : DEFAULT_SIZE.liquidMax radius = parseFloat((size.liquidSize ? size.liquidSize : DEFAULT_SIZE.liquidSize) / 100) - outlineBorder = parseInt(size.liquidOutlineBorder ? size.liquidOutlineBorder : DEFAULT_SIZE.liquidOutlineBorder) - outlineDistance = parseInt((size.liquidOutlineDistance || size.liquidOutlineDistance === 0) ? size.liquidOutlineDistance : DEFAULT_SIZE.liquidOutlineDistance) - waveLength = parseInt(size.liquidWaveLength ? size.liquidWaveLength : DEFAULT_SIZE.liquidWaveLength) - waveCount = parseInt(size.liquidWaveCount ? size.liquidWaveCount : DEFAULT_SIZE.liquidWaveCount) shape = size.liquidShape ? size.liquidShape : DEFAULT_SIZE.liquidShape } // label @@ -86,14 +82,6 @@ export function baseLiquid(plot, container, chart) { percent: (parseFloat(value) / parseFloat(max)), radius: radius, shape: shape, - outline: { - border: outlineBorder, - distance: outlineDistance - }, - wave: { - length: waveLength, - count: waveCount - }, statistic: { // title: title, content: labelContent diff --git a/frontend/src/views/chart/components/normal/LabelNormal.vue b/frontend/src/views/chart/components/normal/LabelNormal.vue index 27f728943c..616692f810 100644 --- a/frontend/src/views/chart/components/normal/LabelNormal.vue +++ b/frontend/src/views/chart/components/normal/LabelNormal.vue @@ -6,7 +6,7 @@ id="label-content" :style="content_class" > - +

{{ item.data[0] }}

diff --git a/frontend/src/views/chart/components/shape-attr/LabelSelector.vue b/frontend/src/views/chart/components/shape-attr/LabelSelector.vue index e94542193d..3a5acfe09f 100644 --- a/frontend/src/views/chart/components/shape-attr/LabelSelector.vue +++ b/frontend/src/views/chart/components/shape-attr/LabelSelector.vue @@ -17,7 +17,7 @@ - + @@ -84,27 +84,35 @@ export default { labelForm: JSON.parse(JSON.stringify(DEFAULT_LABEL)), fontSize: [], isSetting: false, - labelPosition: [ + labelPosition: [], + labelPositionPie: [ { name: this.$t('chart.inside'), value: 'inside' }, - { name: this.$t('chart.outside'), value: 'outside' }, - { name: this.$t('chart.center'), value: 'center' }, - { name: this.$t('chart.text_pos_top'), value: 'top' }, - { name: this.$t('chart.text_pos_bottom'), value: 'bottom' }, + { name: this.$t('chart.outside'), value: 'outside' } + ], + labelPositionH: [ { name: this.$t('chart.text_pos_left'), value: 'left' }, + { name: this.$t('chart.center'), value: 'inside' }, { name: this.$t('chart.text_pos_right'), value: 'right' } ], + labelPositionV: [ + { name: this.$t('chart.text_pos_top'), value: 'top' }, + { name: this.$t('chart.center'), value: 'inside' }, + { name: this.$t('chart.text_pos_bottom'), value: 'bottom' } + ], predefineColors: COLOR_PANEL } }, watch: { 'chart': { handler: function() { + this.initOptions() this.initData() } } }, mounted() { this.init() + this.initOptions() this.initData() }, methods: { @@ -140,6 +148,18 @@ export default { this.isSetting = false } this.$emit('onLabelChange', this.labelForm) + }, + initOptions() { + const type = this.chart.type + if (type) { + if (type.includes('horizontal') || type === 'funnel') { + this.labelPosition = this.labelPositionH + } else if (type.includes('pie')) { + this.labelPosition = this.labelPositionPie + } else { + this.labelPosition = this.labelPositionV + } + } } } } diff --git a/frontend/src/views/chart/components/shape-attr/LabelSelectorAntV.vue b/frontend/src/views/chart/components/shape-attr/LabelSelectorAntV.vue index 52c90ecdf3..50142634b7 100644 --- a/frontend/src/views/chart/components/shape-attr/LabelSelectorAntV.vue +++ b/frontend/src/views/chart/components/shape-attr/LabelSelectorAntV.vue @@ -17,7 +17,7 @@ - + @@ -62,29 +62,35 @@ export default { labelForm: JSON.parse(JSON.stringify(DEFAULT_LABEL)), fontSize: [], isSetting: false, - labelPosition: [ - { name: this.$t('chart.inside'), value: 'middle' }, - { name: this.$t('chart.outside'), value: 'outside' }, - { name: this.$t('chart.center'), value: 'center' }, - { name: this.$t('chart.text_pos_top'), value: 'top' }, - { name: this.$t('chart.text_pos_bottom'), value: 'bottom' }, + labelPosition: [], + labelPositionPie: [ + { name: this.$t('chart.inside'), value: 'inner' }, + { name: this.$t('chart.outside'), value: 'outer' } + ], + labelPositionH: [ { name: this.$t('chart.text_pos_left'), value: 'left' }, + { name: this.$t('chart.center'), value: 'middle' }, { name: this.$t('chart.text_pos_right'), value: 'right' } ], + labelPositionV: [ + { name: this.$t('chart.text_pos_top'), value: 'top' }, + { name: this.$t('chart.center'), value: 'middle' }, + { name: this.$t('chart.text_pos_bottom'), value: 'bottom' } + ], predefineColors: COLOR_PANEL } }, watch: { 'chart': { handler: function() { - this.initLabelPosition() + this.initOptions() this.initData() } } }, mounted() { this.init() - this.initLabelPosition() + this.initOptions() this.initData() }, methods: { @@ -121,22 +127,16 @@ export default { } this.$emit('onLabelChange', this.labelForm) }, - initLabelPosition() { - if (this.chart && this.chart.type && this.chart.type.includes('pie')) { - this.labelPosition = [ - { name: this.$t('chart.inside'), value: 'inner' }, - { name: this.$t('chart.outside'), value: 'outer' } - ] - } else { - this.labelPosition = [ - { name: this.$t('chart.inside'), value: 'middle' }, - { name: this.$t('chart.outside'), value: 'outside' }, - { name: this.$t('chart.center'), value: 'center' }, - { name: this.$t('chart.text_pos_top'), value: 'top' }, - { name: this.$t('chart.text_pos_bottom'), value: 'bottom' }, - { name: this.$t('chart.text_pos_left'), value: 'left' }, - { name: this.$t('chart.text_pos_right'), value: 'right' } - ] + initOptions() { + const type = this.chart.type + if (type) { + if (type.includes('horizontal') || type === 'funnel') { + this.labelPosition = this.labelPositionH + } else if (type.includes('pie')) { + this.labelPosition = this.labelPositionPie + } else { + this.labelPosition = this.labelPositionV + } } } } diff --git a/frontend/src/views/chart/components/shape-attr/SizeSelector.vue b/frontend/src/views/chart/components/shape-attr/SizeSelector.vue index 362b8d15ab..892ea3bd90 100644 --- a/frontend/src/views/chart/components/shape-attr/SizeSelector.vue +++ b/frontend/src/views/chart/components/shape-attr/SizeSelector.vue @@ -128,9 +128,6 @@ - - {{ $t('chart.show') }} - diff --git a/frontend/src/views/chart/components/shape-attr/SizeSelectorAntV.vue b/frontend/src/views/chart/components/shape-attr/SizeSelectorAntV.vue index bedb4a527d..ad36ad6972 100644 --- a/frontend/src/views/chart/components/shape-attr/SizeSelectorAntV.vue +++ b/frontend/src/views/chart/components/shape-attr/SizeSelectorAntV.vue @@ -125,9 +125,6 @@ - - {{ $t('chart.show') }} - @@ -244,18 +241,6 @@ - - - - - - - - - - - - diff --git a/frontend/src/views/chart/group/Group.vue b/frontend/src/views/chart/group/Group.vue index e7df267002..f9cb7d2c9b 100644 --- a/frontend/src/views/chart/group/Group.vue +++ b/frontend/src/views/chart/group/Group.vue @@ -196,9 +196,9 @@ :options="chartGroupTreeAvailable" :normalizer="normalizer" :placeholder="$t('chart.select_group')" - :noChildrenText="$t('commons.treeselect.no_children_text')" - :noOptionsText="$t('commons.treeselect.no_options_text')" - :noResultsText="$t('commons.treeselect.no_results_text')" + :no-children-text="$t('commons.treeselect.no_children_text')" + :no-options-text="$t('commons.treeselect.no_options_text')" + :no-results-text="$t('commons.treeselect.no_results_text')" /> @@ -773,6 +773,7 @@ export default { view.customFilter = JSON.stringify([]) view.drillFields = JSON.stringify([]) view.extBubble = JSON.stringify([]) + this.setChartDefaultOptions(view) const _this = this post('/chart/view/save', view).then(response => { this.closeCreateChart() @@ -788,6 +789,33 @@ export default { }) }, + setChartDefaultOptions(view) { + const type = view.type + const attr = JSON.parse(view.customAttr) + if (type.includes('pie')) { + if (view.render === 'echarts') { + attr.label.position = 'inside' + } else { + attr.label.position = 'inner' + } + } else if (type.includes('line')) { + attr.label.position = 'top' + } else if (type.includes('treemap')) { + if (view.render === 'echarts') { + attr.label.position = 'inside' + } else { + attr.label.position = 'middle' + } + } else { + if (view.render === 'echarts') { + attr.label.position = 'inside' + } else { + attr.label.position = 'middle' + } + } + view.customAttr = JSON.stringify(attr) + }, + getTable(table) { this.table = JSON.parse(JSON.stringify(table)) }, diff --git a/frontend/src/views/chart/view/ChartEdit.vue b/frontend/src/views/chart/view/ChartEdit.vue index 363a93bd7f..e76b1c5fd5 100644 --- a/frontend/src/views/chart/view/ChartEdit.vue +++ b/frontend/src/views/chart/view/ChartEdit.vue @@ -141,7 +141,7 @@ class="render-select" style="width: 100px" size="mini" - @change="calcData(true,'chart',true,true)" + @change="changeChartType()" > @@ -230,11 +230,11 @@ :options="places" :placeholder="$t('chart.select_map_range')" :normalizer="normalizer" + :no-children-text="$t('commons.treeselect.no_children_text')" + :no-options-text="$t('commons.treeselect.no_options_text')" + :no-results-text="$t('commons.treeselect.no_results_text')" @input="calcData" @deselect="calcData" - :noChildrenText="$t('commons.treeselect.no_children_text')" - :noOptionsText="$t('commons.treeselect.no_options_text')" - :noResultsText="$t('commons.treeselect.no_results_text')" />
@@ -1420,6 +1420,7 @@ export default { const view = this.buildParam(true, 'chart', false, false) if (!view) return post('/chart/view/save', view).then(response => { + this.getChart(response.data.id) this.hasEdit = false this.refreshGroup(view) this.closeChangeChart() @@ -2065,6 +2066,36 @@ export default { reset() { this.getData(this.param.id) + }, + + changeChartType() { + this.setChartDefaultOptions() + this.calcData(true, 'chart', true, true) + }, + + setChartDefaultOptions() { + const type = this.view.type + if (type.includes('pie')) { + if (this.view.render === 'echarts') { + this.view.customAttr.label.position = 'inside' + } else { + this.view.customAttr.label.position = 'inner' + } + } else if (type.includes('line')) { + this.view.customAttr.label.position = 'top' + } else if (type.includes('treemap')) { + if (this.view.render === 'echarts') { + this.view.customAttr.label.position = 'inside' + } else { + this.view.customAttr.label.position = 'middle' + } + } else { + if (this.view.render === 'echarts') { + this.view.customAttr.label.position = 'inside' + } else { + this.view.customAttr.label.position = 'middle' + } + } } } } diff --git a/frontend/src/views/panel/filter/defaultValue/DeDateDefault.vue b/frontend/src/views/panel/filter/defaultValue/DeDateDefault.vue index f6ce1acaa3..c83e0ff172 100644 --- a/frontend/src/views/panel/filter/defaultValue/DeDateDefault.vue +++ b/frontend/src/views/panel/filter/defaultValue/DeDateDefault.vue @@ -1,43 +1,89 @@