Merge pull request #3470 from dataease/pr@dev@feat_app-connect

Pr@dev@feat_app-connect
This commit is contained in:
xuwei-fit2cloud 2022-10-27 11:29:47 +08:00 committed by GitHub
commit 9bf6c82289
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 1914 additions and 810 deletions

View File

@ -86,7 +86,7 @@ public class DatasourceController {
@PostMapping("/get/{id}")
public DatasourceDTO getDatasource(@PathVariable String id) throws Exception {
DatasourceUnionRequest request = new DatasourceUnionRequest();
request.setUserId(String.valueOf(AuthUtils.getUser().getUserId()));
request.setUserId("1");
request.setId(id);
List<DatasourceDTO> datasourceList = datasourceService.getDatasourceList(request);
return CollectionUtils.isNotEmpty(datasourceList) ? datasourceList.get(0) : null;

View File

@ -21,24 +21,31 @@ import java.util.List;
@RestController
@Api(tags = "应用市场:应用日志")
@ApiSupport(order = 220)
@RequestMapping("/app/log")
@RequestMapping("app/log")
public class AppLogController {
@Resource
private AppLogService applogService;
private AppLogService appLogService;
@I18n
@ApiOperation("查询日志")
@PostMapping("/logGrid/{goPage}/{pageSize}")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "path", name = "goPage", value = "页码", required = true, dataType = "Integer"),
@ApiImplicitParam(paramType = "path", name = "pageSize", value = "页容量", required = true, dataType = "Integer"),
@ApiImplicitParam(name = "request", value = "查询条件", required = true)
@ApiImplicitParam(paramType = "path", name = "goPage", value = "页码", required = true, dataType = "Integer"),
@ApiImplicitParam(paramType = "path", name = "pageSize", value = "页容量", required = true, dataType = "Integer"),
@ApiImplicitParam(name = "request", value = "查询条件", required = true)
})
public Pager<List<AppLogGridDTO>> logGrid(@PathVariable int goPage, @PathVariable int pageSize,
@RequestBody KeyGridRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
return PageUtils.setPageInfo(page, applogService.query(request));
return PageUtils.setPageInfo(page, appLogService.query(request));
}
@ApiOperation("删除日志和资源")
@PostMapping("/deleteLog")
public void deleteLogAndResource(@RequestBody AppLogGridDTO request) throws Exception {
appLogService.deleteLogAndResource(request);
}
}

View File

@ -203,4 +203,18 @@ public class PanelGroupController {
result.setResponseSource("appApply");
return result;
}
@PostMapping("/appEdit")
public void appEdit(@RequestBody PanelAppTemplateApplyRequest request) throws Exception{
panelGroupService.appEdit(request);
}
@GetMapping("/findOneWithParent/{panelId}")
public PanelGroupDTO findOneWithParent(@PathVariable String panelId) throws Exception{
PanelGroupDTO result = findOne(panelId);
result.setParents(authService.parentResource(panelId,"panel"));
result.setRequestId(UUIDUtil.getUUIDAsString());
result.setResponseSource("appApply");
return result;
}
}

View File

@ -18,4 +18,6 @@ public class DataSetGroupRequest extends DatasetGroup {
private String userId;
@ApiModelProperty("ID集合")
private Set<String> ids;
@ApiModelProperty("排除的ID")
private String excludedId;
}

View File

@ -13,12 +13,18 @@ import java.util.List;
@Data
public class PanelAppTemplateApplyRequest {
private String logId;
private String panelId;
private String panelGroupPid;
private String panelName;
private String datasetGroupId;
private String datasetGroupPid;
private String datasetGroupName;
private String appTemplateId;

View File

@ -16,4 +16,18 @@ public class AppLogGridDTO extends PanelAppTemplateLog implements Serializable {
private String panelName;
private String panelGroupPid;
private String datasourceType;
private String datasetGroupPid;
private Boolean deleteResource;
private String datasetPrivileges;
private String panelPrivileges;
private String datasourcePrivileges;
}

View File

@ -7,12 +7,38 @@
<result column="panel_name" property="panelName"/>
<result column="dataset_group_name" property="datasetGroupName"/>
<result column="datasource_name" property="datasourceName"/>
<result column="panel_group_pid" property="panelGroupPid"/>
<result column="dataset_group_pid" property="datasetGroupPid"/>
<result column="datasource_type" property="datasourceType"/>
<result column="dataset_privileges" property="datasetPrivileges"/>
<result column="panel_privileges" property="panelPrivileges"/>
<result column="datasource_privileges" property="datasourcePrivileges"/>
</resultMap>
<select id="query" parameterType="io.dataease.service.panel.applog.AppLogQueryParam" resultMap="BaseResultMapDTO">
select * from
select
logInfo.*,
get_auths(logInfo.dataset_group_id,'dataset',#{userId}) as `dataset_privileges`,
get_auths(logInfo.panel_id,'panel',#{userId}) as `panel_privileges`,
get_auths(logInfo.datasource_id,'link',#{userId}) as `datasource_privileges`
from
(select * from
(SELECT
panel_app_template_log.*,
panel_app_template_log.id,
panel_app_template_log.app_template_id,
panel_app_template_log.app_template_name,
datasource.id as datasource_id,
panel_app_template_log.source_datasource_name,
dataset_group.id as dataset_group_id,
panel_app_template_log.source_dataset_group_name,
panel_group.id as panel_id,
panel_app_template_log.source_panel_name,
panel_app_template_log.apply_time,
panel_app_template_log.apply_persion,
panel_app_template_log.is_success,
panel_app_template_log.remark,
panel_group.pid as panel_group_pid,
datasource.type as datasource_type,
dataset_group.pid as dataset_group_pid,
IFNULL(panel_app_template.name,CONCAT(panel_app_template_log.app_template_name,'(Deleted)')) as app_name,
IFNULL(panel_group.name,CONCAT(panel_app_template_log.source_panel_name,'(Deleted)')) as panel_name,
IFNULL(dataset_group.name,CONCAT(panel_app_template_log.source_dataset_group_name,'(Deleted)')) as dataset_group_name,

View File

@ -68,6 +68,10 @@
<if test="createTime != null">
and dataset_group.create_time = #{createTime,jdbcType=BIGINT}
</if>
<if test="excludedId != null">
and dataset_group.id != #{excludedId,jdbcType=VARCHAR}
</if>
<if test="ids != null and ids.size() > 0">
and id in
<foreach collection="ids" item="item" separator="," open="(" close=")">
@ -79,6 +83,6 @@
</select>
<select id="searchIds" resultType="java.util.Map">
select GET_V_AUTH_MODEL_WITH_CHILDREN(#{id},#{type}) ids
select GET_V_AUTH_MODEL_WITH_CHILDREN(#{id}, #{type}) ids
</select>
</mapper>

View File

@ -5,6 +5,7 @@ import io.dataease.commons.constants.CommonConstants;
import io.dataease.commons.constants.PanelConstants;
import io.dataease.commons.utils.AuthUtils;
import io.dataease.commons.utils.BeanUtils;
import io.dataease.controller.datasource.request.UpdataDsRequest;
import io.dataease.controller.request.dataset.DataSetTableRequest;
import io.dataease.controller.request.panel.PanelAppTemplateApplyRequest;
import io.dataease.controller.request.panel.PanelAppTemplateRequest;
@ -270,15 +271,36 @@ public class PanelAppTemplateService {
return chartViewFieldsRealMap;
}
public void nameCheck(PanelAppTemplateApplyRequest request) {
panelGroupService.checkPanelName(request.getPanelName(), request.getPanelId(), PanelConstants.OPT_TYPE_INSERT, null, "panel");
DatasetGroup datasetGroup = new DatasetGroup();
datasetGroup.setPid(request.getDatasetGroupId());
datasetGroup.setName(request.getDatasetGroupName());
dataSetGroupService.checkName(datasetGroup);
request.getDatasourceList().stream().forEach(datasource -> {
datasourceService.checkName(datasource.getName(), datasource.getType(), null);
});
public void nameCheck(PanelAppTemplateApplyRequest request, String optType) {
if ("add".equals(optType)) {
panelGroupService.checkPanelName(request.getPanelName(), request.getPanelGroupPid(), PanelConstants.OPT_TYPE_INSERT, null, "panel");
DatasetGroup datasetGroup = new DatasetGroup();
datasetGroup.setPid(request.getDatasetGroupPid());
datasetGroup.setName(request.getDatasetGroupName());
dataSetGroupService.checkName(datasetGroup);
request.getDatasourceList().stream().forEach(datasource -> {
datasourceService.checkName(datasource.getName(), datasource.getType(), null);
});
} else {
DatasetGroup datasetGroup = new DatasetGroup();
datasetGroup.setPid(request.getDatasetGroupPid());
datasetGroup.setName(request.getDatasetGroupName());
datasetGroup.setId(request.getDatasetGroupId());
dataSetGroupService.checkName(datasetGroup);
request.getDatasourceList().stream().forEach(datasource -> {
datasourceService.checkName(datasource.getName(), datasource.getType(), datasource.getId());
});
}
}
@Transactional(rollbackFor = Exception.class)
public void editDatasource(List<Datasource> updateDatasourceList) throws Exception {
for (int i = 0; i < updateDatasourceList.size(); i++) {
UpdataDsRequest updataDsRequest = new UpdataDsRequest();
BeanUtils.copyBean(updataDsRequest, updateDatasourceList.get(i));
datasourceService.updateDatasource(updataDsRequest);
}
}
}

View File

@ -127,9 +127,9 @@ public class PanelGroupService {
@Resource
private PanelAppTemplateLogService appTemplateLogService;
@Resource
private ChartGroupService chartGroupService;
@Resource
private DataSetGroupService dataSetGroupService;
@Resource
private DatasetGroupMapper datasetGroupMapper;
public List<PanelGroupDTO> tree(PanelGroupRequest panelGroupRequest) {
String userId = String.valueOf(AuthUtils.getUser().getUserId());
@ -232,6 +232,25 @@ public class PanelGroupService {
return panelId;
}
public void move(PanelGroupRequest request){
PanelGroupWithBLOBs panelInfo = panelGroupMapper.selectByPrimaryKey(request.getId());
if (panelInfo.getPid().equalsIgnoreCase(request.getPid())) {
DataEaseException.throwException(Translator.get("i18n_select_diff_folder"));
}
// 移动校验
if (StringUtils.isNotEmpty(request.getName())) {
checkPanelName(request.getName(), request.getPid(), PanelConstants.OPT_TYPE_INSERT, request.getId(), panelInfo.getNodeType());
}
PanelGroupWithBLOBs record = new PanelGroupWithBLOBs();
record.setName(request.getName());
record.setId(request.getId());
record.setPid(request.getPid());
record.setUpdateTime(request.getUpdateTime());
record.setUpdateBy(request.getUpdateBy());
panelGroupMapper.updateByPrimaryKeySelective(record);
DeLogUtils.save(SysLogConstants.OPERATE_TYPE.MODIFY, sourceType, request.getId(), panelInfo.getPid(), request.getPid(), sourceType);
}
public void checkPanelName(String name, String pid, String optType, String id, String nodeType) {
PanelGroupExample groupExample = new PanelGroupExample();
@ -811,12 +830,12 @@ public class PanelGroupService {
@Transactional(rollbackFor = Exception.class)
public String appApply(PanelAppTemplateApplyRequest request) throws Exception{
//仪表板名称校验数据集分组名称校验数据源名称校验
panelAppTemplateService.nameCheck(request);
panelAppTemplateService.nameCheck(request,"add");
String newPanelId = UUIDUtil.getUUIDAsString();
// 新建数据集分组
DatasetGroup newDatasetGroup = new DatasetGroup();
newDatasetGroup.setPid(request.getDatasetGroupId());
newDatasetGroup.setPid(request.getDatasetGroupPid());
newDatasetGroup.setName(request.getDatasetGroupName());
newDatasetGroup.setType("group");
DataSetGroupDTO resultDatasetGroup = dataSetGroupService.save(newDatasetGroup);
@ -853,7 +872,7 @@ public class PanelGroupService {
panelAppTemplateService.applyViewsField(chartViewFieldsInfo,chartViewsRealMap,datasetsRealMap,datasetFieldsRealMap);
panelAppTemplateService.applyPanel(panelInfo,chartViewsRealMap,newPanelId, request.getPanelName(), request.getPanelId());
panelAppTemplateService.applyPanel(panelInfo,chartViewsRealMap,newPanelId, request.getPanelName(), request.getPanelGroupPid());
panelAppTemplateService.applyPanelView(panelViewsInfo,chartViewsRealMap,newPanelId);
@ -873,4 +892,42 @@ public class PanelGroupService {
appTemplateLogService.newAppApplyLog(templateLog);
return newPanelId;
}
@Transactional(rollbackFor = Exception.class)
public void appEdit(PanelAppTemplateApplyRequest request) throws Exception{
long currentTime = System.currentTimeMillis();
String userName = AuthUtils.getUser().getUsername();
//名称校验数据集分组名称校验数据源名称校验
panelAppTemplateService.nameCheck(request,"update");
//仪表板移动更新名称
PanelGroup panelHistoryInfo = panelGroupMapper.selectByPrimaryKey(request.getPanelId());
String panelHistoryPid = panelHistoryInfo.getPid();
if(panelHistoryPid.equals(request.getPanelGroupPid())){
// 未移动
checkPanelName(request.getPanelName(), request.getPanelGroupPid(), PanelConstants.OPT_TYPE_UPDATE, request.getPanelId(), "panel");
}else{
checkPanelName(request.getPanelName(), request.getPanelGroupPid(), PanelConstants.OPT_TYPE_INSERT, null, "panel");
}
panelHistoryInfo.setName(request.getPanelName());
panelHistoryInfo.setPid(request.getPanelGroupPid());
panelHistoryInfo.setUpdateBy(userName);
panelHistoryInfo.setUpdateTime(currentTime);
panelGroupMapper.updateByPrimaryKey(panelHistoryInfo);
//数据集分组移动,变更
DatasetGroup datasetGroupHistoryInfo = datasetGroupMapper.selectByPrimaryKey(request.getDatasetGroupId());
DatasetGroup datasetGroup = new DatasetGroup();
datasetGroup.setName(request.getDatasetGroupName());
datasetGroup.setId(request.getDatasetGroupId());
if(datasetGroupHistoryInfo.getPid().equals(request.getDatasetGroupPid())){
datasetGroup.setPid(request.getDatasetGroupPid());
}
dataSetGroupService.checkName(datasetGroup);
datasetGroupHistoryInfo.setName(request.getDatasetGroupName());
datasetGroupHistoryInfo.setPid(request.getDatasetGroupPid());
datasetGroupMapper.updateByPrimaryKey(datasetGroupHistoryInfo);
//数据源变更
panelAppTemplateService.editDatasource(request.getDatasourceList());
}
}

View File

@ -7,5 +7,6 @@ import java.util.List;
@Data
public class AppLogQueryParam extends GridExample {
private String userId;
}

View File

@ -1,13 +1,20 @@
package io.dataease.service.panel.applog;
import com.google.gson.Gson;
import io.dataease.commons.utils.AuthUtils;
import io.dataease.controller.sys.request.KeyGridRequest;
import io.dataease.dto.SysLogDTO;
import io.dataease.dto.appTemplateMarket.AppLogGridDTO;
import io.dataease.ext.ExtAppLogMapper;
import io.dataease.ext.query.GridExample;
import io.dataease.plugins.common.base.mapper.PanelAppTemplateLogMapper;
import io.dataease.service.dataset.DataSetGroupService;
import io.dataease.service.datasource.DatasourceService;
import io.dataease.service.panel.PanelGroupService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
@ -19,12 +26,19 @@ public class AppLogService {
private PanelAppTemplateLogMapper appLogMapper;
@Resource
private ExtAppLogMapper extAppLogMapper;
@Resource
private PanelGroupService panelGroupService;
@Resource
private DataSetGroupService dataSetGroupService;
@Resource
private DatasourceService datasourceService;
public List<AppLogGridDTO> query(KeyGridRequest request) {
GridExample gridExample = request.convertExample();
gridExample.setExtendCondition(request.getKeyWord());
AppLogQueryParam logQueryParam = gson.fromJson(gson.toJson(gridExample), AppLogQueryParam.class);
logQueryParam.setUserId(String.valueOf(AuthUtils.getUser().getUserId()));
List<AppLogGridDTO> voLogs = extAppLogMapper.query(logQueryParam);
return voLogs;
}
@ -34,4 +48,19 @@ public class AppLogService {
}
@Transactional
public void deleteLogAndResource(AppLogGridDTO request) throws Exception {
if (request.getDeleteResource()) {
if (StringUtils.isNotEmpty(request.getPanelId())) {
panelGroupService.deleteCircle(request.getPanelId());
}
if (StringUtils.isNotEmpty(request.getDatasetGroupId())) {
dataSetGroupService.delete(request.getDatasetGroupId());
}
if (StringUtils.isNotEmpty(request.getDatasourceId())) {
datasourceService.deleteDatasource(request.getDatasourceId());
}
}
appLogMapper.deleteByPrimaryKey(request.getId());
}
}

View File

@ -32,6 +32,17 @@ WHERE (`msg_type_id` = 6);
ALTER TABLE `sys_user_assist`
ADD COLUMN `larksuite_id` VARCHAR(255) NULL DEFAULT NULL AFTER `lark_id`;
INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`,
`path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`,
`update_time`)
VALUES (41, 1, 1, 1, '应用管理', 'system-app-template', 'panel/appTemplate/index', 13, 'sys-param',
'panel/appTemplate/index', 0, 0, 0, NULL, NULL, NULL, NULL, 1620444227389);
INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`,
`path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`,
`update_time`)
VALUES (203, 0, 0, 1, '应用', 'app-template-market', 'panel/appTemplateMarket/index', 6, 'dashboard',
'/appTemplateMarket', 0, 0, 0, NULL, NULL, NULL, NULL, 1620444227389);
ALTER TABLE `dataset_table_field` CHANGE COLUMN `type` `type` VARCHAR(255) NOT NULL COMMENT '原始字段类型' ;
INSERT INTO `my_plugin` (`name`, `store`, `free`, `cost`, `category`, `descript`, `version`, `creator`, `load_mybatis`,
@ -42,3 +53,17 @@ VALUES ('Apache Kylin 数据源插件', 'default', '0', '0', 'datasource', 'Apac
INSERT INTO `sys_msg_channel` (`msg_channel_id`, `channel_name`, `service_name`) VALUES ('6', 'webmsg.channel_larksuite_msg', 'sendLarksuite');
INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (204, 203, 0, 2, '删除记录', NULL, NULL, 999, NULL, NULL, 0, 0, 0, 'appLog:del', NULL, NULL, 1614930903502, 1614930903502);
INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (205, 203, 0, 2, '编辑记录', NULL, NULL, 999, NULL, NULL, 0, 0, 0, 'appLog:edit', NULL, NULL, 1614930935529, 1614930935529);
INSERT INTO `sys_auth` (`id`, `auth_source`, `auth_source_type`, `auth_target`, `auth_target_type`, `auth_time`, `auth_details`, `auth_user`, `update_time`, `copy_from`, `copy_id`) VALUES ('46e4e2cb-1349-40c3-a72d-7b0b30ab5d14', '203', 'menu', '1', 'role', 1666840141866, NULL, 'admin', NULL, NULL, NULL);
INSERT INTO `sys_auth` (`id`, `auth_source`, `auth_source_type`, `auth_target`, `auth_target_type`, `auth_time`, `auth_details`, `auth_user`, `update_time`, `copy_from`, `copy_id`) VALUES ('6e22ad53-d737-447f-9686-5041e122b4dc', '205', 'menu', '1', 'role', 1666840141468, NULL, 'admin', NULL, NULL, NULL);
INSERT INTO `sys_auth` (`id`, `auth_source`, `auth_source_type`, `auth_target`, `auth_target_type`, `auth_time`, `auth_details`, `auth_user`, `update_time`, `copy_from`, `copy_id`) VALUES ('da17fcfe-7875-4aaf-983b-d750d71f36d2', '204', 'menu', '1', 'role', 1666840141658, NULL, 'admin', NULL, NULL, NULL);
INSERT INTO `sys_auth_detail` (`id`, `auth_id`, `privilege_name`, `privilege_type`, `privilege_value`, `privilege_extend`, `remark`, `create_user`, `create_time`, `update_time`, `copy_from`, `copy_id`) VALUES ('b4fe2e52-55a4-11ed-bf84-0242ac130005', '6e22ad53-d737-447f-9686-5041e122b4dc', 'i18n_auth_grant', 15, 0, 'grant', '基础权限-授权', 'admin', 1666840141000, NULL, NULL, NULL);
INSERT INTO `sys_auth_detail` (`id`, `auth_id`, `privilege_name`, `privilege_type`, `privilege_value`, `privilege_extend`, `remark`, `create_user`, `create_time`, `update_time`, `copy_from`, `copy_id`) VALUES ('b4fe3215-55a4-11ed-bf84-0242ac130005', '6e22ad53-d737-447f-9686-5041e122b4dc', 'i18n_auth_use', 1, 1, 'use', '基础权限-使用', 'admin', 1666840141000, NULL, NULL, NULL);
INSERT INTO `sys_auth_detail` (`id`, `auth_id`, `privilege_name`, `privilege_type`, `privilege_value`, `privilege_extend`, `remark`, `create_user`, `create_time`, `update_time`, `copy_from`, `copy_id`) VALUES ('b51affbf-55a4-11ed-bf84-0242ac130005', 'da17fcfe-7875-4aaf-983b-d750d71f36d2', 'i18n_auth_grant', 15, 0, 'grant', '基础权限-授权', 'admin', 1666840141000, NULL, NULL, NULL);
INSERT INTO `sys_auth_detail` (`id`, `auth_id`, `privilege_name`, `privilege_type`, `privilege_value`, `privilege_extend`, `remark`, `create_user`, `create_time`, `update_time`, `copy_from`, `copy_id`) VALUES ('b51b0473-55a4-11ed-bf84-0242ac130005', 'da17fcfe-7875-4aaf-983b-d750d71f36d2', 'i18n_auth_use', 1, 1, 'use', '基础权限-使用', 'admin', 1666840141000, NULL, NULL, NULL);
INSERT INTO `sys_auth_detail` (`id`, `auth_id`, `privilege_name`, `privilege_type`, `privilege_value`, `privilege_extend`, `remark`, `create_user`, `create_time`, `update_time`, `copy_from`, `copy_id`) VALUES ('b53a1ad7-55a4-11ed-bf84-0242ac130005', '46e4e2cb-1349-40c3-a72d-7b0b30ab5d14', 'i18n_auth_grant', 15, 0, 'grant', '基础权限-授权', 'admin', 1666840142000, NULL, NULL, NULL);
INSERT INTO `sys_auth_detail` (`id`, `auth_id`, `privilege_name`, `privilege_type`, `privilege_value`, `privilege_extend`, `remark`, `create_user`, `create_time`, `update_time`, `copy_from`, `copy_id`) VALUES ('b53a1dfd-55a4-11ed-bf84-0242ac130005', '46e4e2cb-1349-40c3-a72d-7b0b30ab5d14', 'i18n_auth_use', 1, 1, 'use', '基础权限-使用', 'admin', 1666840142000, NULL, NULL, NULL);

View File

@ -11,7 +11,7 @@ export function logGrid(page, size, data) {
export function opTypes() {
return request({
url: '/api/log/opTypes',
url: '/app/log/opTypes',
method: 'post',
loading: true
})
@ -19,10 +19,19 @@ export function opTypes() {
export function exportExcel(data) {
return request({
url: '/api/log/export',
url: '/app/log/export',
method: 'post',
loading: true,
responseType: 'blob',
data
})
}
export function deleteLogAndResource(data) {
return request({
url: '/app/log/deleteLog',
method: 'post',
data,
loading: true
})
}

View File

@ -313,7 +313,33 @@ export function appApply(data) {
return request({
url: 'panel/group/appApply',
method: 'post',
loading: true,
loading: false,
data
})
}
export function appEdit(data) {
return request({
url: 'panel/group/appEdit',
method: 'post',
loading: false,
data
})
}
export function editApply(data) {
return request({
url: 'panel/group/appApply',
method: 'post',
loading: false,
data
})
}
export function findOneWithParent(panelId) {
return request({
url: 'panel/group/findOneWithParent/' + panelId,
method: 'get',
loading: false
})
}

View File

@ -42,7 +42,7 @@ export default {
return backPath || backName || backTo
},
needInnerPadding() {
return ['sys-identification', 'sys-abutment', 'sys-task-email', 'system-dept', 'system-dept-form', 'system-auth', 'sys-appearance', 'system-param', 'system-template', 'sys-task-dataset', 'sys-msg-web-all', 'system-plugin'].includes(this.$route.name)
return ['system-app-template', 'sys-identification', 'sys-abutment', 'sys-task-email', 'system-dept', 'system-dept-form', 'system-auth', 'sys-appearance', 'system-param', 'system-template', 'sys-task-dataset', 'sys-msg-web-all', 'system-plugin'].includes(this.$route.name)
}
}
}

View File

@ -2625,6 +2625,16 @@ export default {
search_by_keyword: 'Search by keyword',
apply_logs: 'Apply logs',
app_group_delete_tips: 'Are you sure to delete this application category?',
app_group_delete_content: 'After deletion, all application templates in this category will also be deleted.'
app_group_delete_content: 'After deletion, all application templates in this category will also be deleted.',
panel_position: 'Panel position',
panel_name: 'Panel name',
dataset_group_position: 'Dataset group position',
dataset_group_name: 'Dataset name',
datasource_info: 'Datasource info',
datasource: 'Datasource',
dataset_group: 'Dataset group',
panel: 'Panel',
log_delete_tips: 'Are you sure to delete this application record?',
log_resource_delete_tips: 'Delete related resources (irrecoverable after deletion)'
}
}

View File

@ -2626,6 +2626,16 @@ export default {
search_by_keyword: '通過關鍵字搜索',
apply_logs: '應用記錄',
app_group_delete_tips: '確定刪除該應用分類嗎?',
app_group_delete_content: '刪除後,該分類中所有的應用模闆也將被刪除。'
app_group_delete_content: '刪除後,該分類中所有的應用模板也將被刪除。',
panel_position: '儀表板位置',
panel_name: '儀表板名稱',
dataset_group_position: '數據集分組位置',
dataset_group_name: '數據集分組名稱',
datasource_info: '數據源信息',
datasource: '數據源',
dataset_group: '數據集分組',
panel: '儀表板',
log_delete_tips: '確定刪除該條應用記錄嗎?',
log_resource_delete_tips: '刪除相關資源(刪除後不可恢復)'
}
}

View File

@ -2626,6 +2626,16 @@ export default {
search_by_keyword: '通过关键字搜索',
apply_logs: '应用记录',
app_group_delete_tips: '确定删除该应用分类吗?',
app_group_delete_content: '删除后,该分类中所有的应用模板也将被删除。'
app_group_delete_content: '删除后,该分类中所有的应用模板也将被删除。',
panel_position: '仪表板位置',
panel_name: '仪表板名称',
dataset_group_position: '数据集分组位置',
dataset_group_name: '数据集分组名称',
datasource_info: '数据源信息',
datasource: '数据源',
dataset_group: '数据集分组',
panel: '仪表板',
log_delete_tips: '确定删除该条应用记录吗?',
log_resource_delete_tips: '删除相关资源(删除后不可恢复)'
}
}

View File

@ -0,0 +1,534 @@
<template>
<div style="width: 100%;height: 100%">
<div class="de-template">
<div
v-loading="$store.getters.loadingMap[$store.getters.currentPath]"
class="tabs-container flex-tabs"
>
<div class="de-tabs-left">
<template-list
ref="templateList"
:template-type="currentTemplateType"
:template-list="templateList"
:show-position="showPosition"
@templateDelete="templateListDelete"
@templateEdit="templateEdit"
@showCurrentTemplate="showCurrentTemplate"
@templateImport="templateImport"
@showTemplateEditDialog="showTemplateEditDialog"
/>
</div>
<div class="de-tabs-right">
<div
v-if="currentTemplateLabel"
class="active-template"
>
{{ currentTemplateLabel }}&nbsp;&nbsp;({{
currentTemplateShowList.length
}})
<deBtn
v-if="showPositionCheck('system-setting')"
type="primary"
icon="el-icon-upload2"
@click="templateImport(currentTemplateId)"
>
{{ $t('app_template.app_upload') }}
</deBtn>
</div>
<el-empty
v-if="!currentTemplateShowList.length"
:image="noneImg"
:description="$t('app_template.no_apps')"
/>
<div
v-show="currentTemplateId !== ''"
id="template-box"
class="template-box"
>
<template-item
v-for="item in currentTemplateShowList"
:key="item.id"
:width="templateCurWidth"
:model="item"
:show-position="showPosition"
@applyNew="applyNew(item)"
@previewApp="previewApp(item)"
@command="(key) => handleCommand(key, item)"
/>
</div>
</div>
</div>
</div>
<el-dialog
:title="dialogTitle"
:visible.sync="editTemplate"
append-to-body
class="de-dialog-form"
width="600px"
destroy-on-close="true"
>
<el-form
ref="templateEditForm"
class="de-form-item"
:model="templateEditForm"
:rules="templateEditFormRules"
>
<el-form-item
:label="dialogTitleLabel"
prop="name"
>
<el-input v-model="templateEditForm.name" />
</el-form-item>
<el-form-item
:label="$t('app_template.app_group_icon')"
prop="icon"
>
<el-col style="width: 148px!important;height: 148px!important;overflow: hidden">
<el-upload
action=""
accept=".jpeg,.jpg,.png,.gif,.svg"
class="avatar-uploader"
list-type="picture-card"
:class="{disabled:uploadDisabled}"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove"
:http-request="upload"
:file-list="fileList"
>
<i class="el-icon-plus" />
</el-upload>
<el-dialog
top="25vh"
width="600px"
:append-to-body="true"
:destroy-on-close="true"
:visible.sync="dialogVisible"
>
<img
width="100%"
:src="dialogImageUrl"
>
</el-dialog>
</el-col>
</el-form-item>
</el-form>
<div
slot="footer"
class="dialog-footer"
>
<deBtn
secondary
@click="close()"
>{{ $t('commons.cancel') }}
</deBtn>
<deBtn
type="primary"
@click="saveTemplateEdit(templateEditForm)"
>{{ $t('commons.confirm') }}
</deBtn>
</div>
</el-dialog>
<!--导入templatedialog-->
<el-dialog
:title="templateDialog.title"
:visible.sync="templateDialog.visible"
:show-close="true"
class="de-dialog-form"
width="600px"
>
<template-import
v-if="templateDialog.visible"
:pid="templateDialog.pid"
:opt-type="templateOptType"
:app-template-info="currentAppTemplateInfo"
@refresh="showCurrentTemplate(currentTemplateId,
currentTemplateLabel)"
@closeEditTemplateDialog="closeEditTemplateDialog"
/>
</el-dialog>
</div>
</template>
<script>
import TemplateList from './component/TemplateList'
import TemplateItem from './component/TemplateItem'
import TemplateImport from './component/TemplateImport'
import { save, update, templateDelete, find } from '@/api/system/appTemplate'
import elementResizeDetectorMaker from 'element-resize-detector'
import msgCfm from '@/components/msgCfm/index'
import { uploadFileResult } from '@/api/staticResource/staticResource'
import { imgUrlTrans } from '@/components/canvas/utils/utils'
export default {
name: 'AppTemplateContent',
components: { TemplateList, TemplateItem, TemplateImport },
mixins: [msgCfm],
props: {
showPosition: {
type: String,
required: false,
default: 'system-setting'
}
},
data() {
return {
templateOptType: 'add',
currentAppTemplateInfo: null,
fileList: [],
dialogImageUrl: '',
dialogVisible: false,
uploadDisabled: false,
showShare: false,
currentTemplateShowList: [],
noneImg: require('@/assets/None.png'),
currentPid: '',
currentTemplateType: 'self',
templateEditFormRules: {
name: [
{ required: true, trigger: 'blur', validator: this.roleValidator },
{
required: true,
message: this.$t('commons.input_content'),
trigger: 'blur'
},
{
max: 50,
message: this.$t('commons.char_can_not_more_50'),
trigger: 'change'
}
],
icon: [
{
required: true,
message: '请选择文件',
trigger: 'change'
}
]
},
templateEditForm: {},
editTemplate: false,
dialogTitle: '',
dialogTitleLabel: '',
currentTemplateLabel: '',
currentTemplateId: '',
templateList: [],
templateMiniWidth: 286,
templateCurWidth: 286,
formType: '',
originName: '',
templateDialog: {
title: '导入应用',
visible: false,
pid: ''
}
}
},
computed: {
nameList() {
const { nodeType } = this.templateEditForm || {}
if (nodeType === 'template') {
return this.currentTemplateShowList.map((ele) => ele.name)
}
if (nodeType === 'folder') {
return this.templateList.map((ele) => ele.name)
}
return []
}
},
mounted() {
this.getTree()
const _this = this
const erd = elementResizeDetectorMaker()
const templateMainDom = document.getElementById('template-box')
// div
erd.listenTo(templateMainDom, (element) => {
_this.$nextTick(() => {
const curSeparator = Math.trunc(
templateMainDom.offsetWidth / _this.templateMiniWidth
)
_this.templateCurWidth =
Math.trunc(templateMainDom.offsetWidth / curSeparator) - 24 - curSeparator
})
})
},
methods: {
previewApp(item) {
this.$emit('previewApp', item)
},
applyNew(item) {
this.$emit('applyNew', item)
},
showPositionCheck(requiredPosition) {
return this.showPosition === requiredPosition
},
roleValidator(rule, value, callback) {
if (this.nameRepeat(value)) {
const { nodeType } = this.templateEditForm || {}
callback(
new Error(
this.$t(
`system_parameter_setting.${
nodeType === 'folder'
? 'name_already_exists_type'
: 'the_same_category'
}`
)
)
)
} else {
callback()
}
},
nameRepeat(value) {
if (!this.nameList || this.nameList.length === 0) {
return false
}
//
if (this.formType === 'edit' && this.originName === value) {
return false
}
return this.nameList.some((name) => name === value)
},
handleCommand(key, data) {
switch (key) {
case 'rename':
this.templateEdit(data)
break
case 'delete':
this.templateDeleteConfirm(data)
break
case 'update':
this.updateAppTemplate(data)
break
default:
break
}
},
updateAppTemplate(data) {
this.templateOptType = 'update'
this.templateDialog.visible = true
this.currentAppTemplateInfo = data
this.templateDialog.pid = data.pid
},
templateDeleteConfirm(template) {
const options = {
title: '是否卸载当前应用?',
type: 'primary',
cb: () => this.templateDelete(template.id)
}
this.handlerConfirm(options, '卸载')
},
handleClick(tab, event) {
this.getTree()
},
showCurrentTemplate(pid, name) {
this.currentTemplateId = pid
this.currentTemplateLabel = name
if (this.currentTemplateId) {
find({ pid: this.currentTemplateId }).then((response) => {
this.currentTemplateShowList = response.data
})
}
},
templateListDelete(id) {
if (id) {
templateDelete(id).then((response) => {
this.openMessageSuccess('commons.delete_success')
this.getTree()
})
}
},
templateDelete(id) {
if (id) {
templateDelete(id).then((response) => {
this.openMessageSuccess('commons.delete_success')
this.showCurrentTemplate(this.currentTemplateId, this.currentTemplateLabel)
})
}
},
showTemplateEditDialog(type, templateInfo) {
this.templateEditForm = null
this.formType = type
if (type === 'edit') {
if (templateInfo.icon) {
this.fileList.push({ url: imgUrlTrans(templateInfo.icon) })
}
this.templateEditForm = JSON.parse(JSON.stringify(templateInfo))
this.dialogTitle = this.$t(
`system_parameter_setting.${
this.templateEditForm.nodeType === 'folder'
? 'edit_classification'
: 'edit_template'
}`
)
this.originName = this.templateEditForm.label
} else {
this.fileList = []
this.dialogTitle = this.$t('panel.add_app_category')
this.templateEditForm = {
name: '',
nodeType: 'folder',
templateType: this.currentTemplateType,
icon: '',
level: 0,
pid: 0
}
}
this.dialogTitleLabel = this.$t(
`system_parameter_setting.${
this.templateEditForm.nodeType === 'folder'
? 'classification_name'
: 'template_name'
}`
)
this.editTemplate = true
},
templateEdit(templateInfo) {
this.showTemplateEditDialog('edit', templateInfo)
},
saveTemplateEdit(templateEditForm) {
this.$refs['templateEditForm'].validate((valid) => {
if (valid) {
const method = templateEditForm.id ? update : save
method(templateEditForm).then((response) => {
this.close()
this.openMessageSuccess(
`system_parameter_setting.${
this.templateEditForm.id
? 'rename_succeeded'
: 'added_successfully'
}`
)
this.getTree()
})
} else {
return false
}
})
},
close() {
this.$refs['templateEditForm'].resetFields()
this.editTemplate = false
this.handleRemove()
},
getTree() {
const request = {
templateType: this.currentTemplateType,
pid: '0'
}
find(request).then((res) => {
this.templateList = res.data
this.showFirst()
})
},
showFirst() {
//
if (this.templateList && this.templateList.length > 0) {
let showFirst = true
this.templateList.forEach((template) => {
if (template.id === this.currentTemplateId) {
showFirst = false
}
})
if (showFirst) {
this.$nextTick().then(() => {
const [obj = {}] = this.templateList
this.$refs.templateList.nodeClick(obj)
})
} else {
this.showCurrentTemplate(this.currentTemplateId, this.currentTemplateLabel)
}
} else {
this.currentTemplateShowList = []
}
},
closeEditTemplateDialog() {
this.templateDialog.visible = false
},
templateImport(pid) {
this.templateOptType = 'new'
this.currentAppTemplateInfo = null
this.templateDialog.visible = true
this.templateDialog.pid = pid
},
handleRemove(file, fileList) {
this.uploadDisabled = false
this.templateEditForm.icon = null
this.fileList = []
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url
this.dialogVisible = true
},
upload(file) {
const _this = this
uploadFileResult(file.file, (fileUrl) => {
_this.templateEditForm.icon = fileUrl
})
}
}
}
</script>
<style lang="scss" scoped>
.de-template {
height: 100%;
background-color: var(--MainBG, #f5f6f7);
.tabs-container {
height: 100%;
background: var(--ContentBG, #ffffff);
overflow-x: auto;
}
.flex-tabs {
display: flex;
background: #f5f6f7;
}
.de-tabs-left {
background: #fff;
width: 269px;
border-right: 1px solid rgba(31, 35, 41, 0.15);
padding: 24px;
}
.de-tabs-right {
flex: 1;
background: #fff;
padding: 24px 0 24px 24px;
overflow: hidden;
.template-box {
display: flex;
flex-wrap: wrap;
overflow-y: auto;
box-sizing: border-box;
align-content: flex-start;
height: calc(100% - 10px);
width: 100%;
padding-bottom: 24px;
}
.active-template {
margin: 4px 0 20px 0;
padding-right: 24px;
font-family: "PingFang SC";
font-style: normal;
font-weight: 500;
font-size: 16px;
display: flex;
align-items: center;
justify-content: space-between;
color: var(--deTextPrimary, #1f2329);
}
}
}
::v-deep .container-wrapper {
padding: 0px !important;
}
</style>

View File

@ -0,0 +1,57 @@
<template>
<el-drawer
v-closePress
:title="'应用模板'"
:visible.sync="applyDrawer"
custom-class="de-user-drawer"
size="600px"
direction="rtl"
>
<ds-form
v-if="applyDrawer"
:reference-position="'appMarket'"
:outer-params="outerParams"
@closeDraw="close"
/>
</el-drawer>
</template>
<script>
import DsForm from '@/views/system/datasource/DsForm'
export default {
name: 'AppTemplateApply',
components: {
DsForm
},
data() {
return {
outerParams: {},
applyDrawer: false
}
},
computed: {
},
mounted() {
},
methods: {
search() {
this.applyDrawer = false
this.$emit('search', this.formatCondition(), this.formatText())
},
init(params) {
this.applyDrawer = true
this.outerParams = params
},
close() {
this.$emit('closeDraw')
this.applyDrawer = false
}
}
}
</script>
<style scoped>
::v-deep .el-drawer__body{
padding: 0px 0px!important;
}
</style>

View File

@ -12,7 +12,10 @@
alt=""
>
</div>
<div class="card-info">
<div
v-if="showPositionCheck('system-setting')"
class="card-info"
>
<el-tooltip
class="item"
effect="dark"
@ -34,22 +37,60 @@
<slot>
<el-dropdown-item command="update">
<i class="el-icon-edit" />
{{ $t("commons.update") }}
{{ $t('commons.update') }}
</el-dropdown-item>
<el-dropdown-item command="delete">
<i class="el-icon-delete" />
{{ $t("commons.uninstall") }}
{{ $t('commons.uninstall') }}
</el-dropdown-item>
</slot>
</el-dropdown-menu>
</el-dropdown>
</div>
<div
v-if="showPositionCheck('market-manage')"
class="card-info-apply"
>
<el-row>
<el-row>
<el-tooltip
class="item"
effect="dark"
:content="model.name"
placement="top"
>
<span class="de-model-text-market">{{ model.name }}</span>
</el-tooltip>
</el-row>
<el-row class="market-button-area">
<el-button
size="small"
style="width: 45%"
@click="templatePreview"
>{{ $t('panel.preview') }}
</el-button>
<el-button
size="small"
style="width: 45%"
type="primary"
@click="apply"
>{{ $t('panel.apply') }}
</el-button>
</el-row>
</el-row>
</div>
</div>
</template>
<script>
export default {
props: {
showPosition: {
type: String,
required: false,
default: 'system-setting'
},
model: {
type: Object,
default: () => {
@ -74,6 +115,15 @@ export default {
}
},
methods: {
templatePreview() {
this.$emit('previewApp')
},
apply() {
this.$emit('applyNew')
},
showPositionCheck(requiredPosition) {
return this.showPosition === requiredPosition
},
handleCommand(key) {
this.$emit('command', key)
}
@ -83,6 +133,7 @@ export default {
<style lang="scss">
.de-card-model {
position: relative;
box-sizing: border-box;
background: #ffffff;
border: 1px solid var(--deCardStrokeColor, #dee0e3);
@ -132,20 +183,67 @@ export default {
}
}
.de-model-text {
font-family: "PingFang SC";
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 22px;
color: #1f2329;
display: inline-block;
width: 90%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
margin-right: 10px;
&:hover .card-info-apply {
height: 92px;
}
&:hover .market-button-area {
display: block;
}
.market-button-area {
text-align: center;
margin-top: 4px;
display: none;
}
.card-info-apply {
background: #ffffff;
width: 100%;
height: 48px;
position: absolute;
bottom: 0px;
left: 0px;
transition: height 0.3s ease-out;
align-items: center;
justify-content: space-between;
padding: 12px 12px 12px 12px;
box-sizing: border-box;
border-radius: 0 0 4px 4px;
border-top: 1px solid var(--deCardStrokeColor, #dee0e3);
overflow-y: hidden;
}
}
.de-model-text {
font-family: "PingFang SC";
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 22px;
color: #1f2329;
display: inline-block;
width: 90%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
margin-right: 10px;
}
.de-model-text-market {
font-family: "PingFang SC";
font-style: normal;
color: #1f2329;
display: inline-block;
width: 90%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
margin-right: 10px;
font-weight: 500;
font-size: 16px;
line-height: 24px;
}
.de-card-model:hover {

View File

@ -29,12 +29,15 @@
:class="[{ select: activeTemplate === ele.id }]"
@click="nodeClick(ele)"
>
<svg-icon
style="margin-right: 9px"
icon-class="scene"
/>
<img
:src="iconImgRul(ele.icon)"
style="margin-right: 8px;border-radius: 4px"
width="24"
height="24"
>
<span>{{ ele.name }}</span>
<span
v-if="showPositionCheck('system-setting')"
class="more"
@click.stop
>
@ -74,7 +77,7 @@
</li>
</ul>
<deBtn
v-if="templateFilterText === ''"
v-if="templateFilterText === '' && showPositionCheck('system-setting')"
style="width: 100%"
icon="el-icon-plus"
secondary
@ -87,12 +90,18 @@
<script>
import msgCfm from '@/components/msgCfm/index'
import { imgUrlTrans } from '@/components/canvas/utils/utils'
export default {
name: 'TemplateList',
components: {},
mixins: [msgCfm],
props: {
showPosition: {
type: String,
required: false,
default: 'system-setting'
},
templateType: {
type: String,
default: ''
@ -114,13 +123,6 @@ export default {
},
computed: {
templateListComputed() {
// if (!this.templateFilterText)
// return [
// ...this.templateList,
// ...this.templateList,
// ...this.templateList,
// ...this.templateList,
// ];
if (!this.templateFilterText) return [...this.templateList]
return this.templateList.filter((ele) =>
ele.name.includes(this.templateFilterText)
@ -128,6 +130,12 @@ export default {
}
},
methods: {
showPositionCheck(requiredPosition) {
return this.showPosition === requiredPosition
},
iconImgRul(iconUrl) {
return imgUrlTrans(iconUrl)
},
clickMore(type, data) {
switch (type) {
case 'edit':

View File

@ -1,513 +1,15 @@
<template>
<de-layout-content>
<div class="de-template">
<div
v-loading="$store.getters.loadingMap[$store.getters.currentPath]"
class="tabs-container flex-tabs"
>
<div class="de-tabs-left">
<template-list
ref="templateList"
:template-type="currentTemplateType"
:template-list="templateList"
@templateDelete="templateListDelete"
@templateEdit="templateEdit"
@showCurrentTemplate="showCurrentTemplate"
@templateImport="templateImport"
@showTemplateEditDialog="showTemplateEditDialog"
/>
</div>
<div class="de-tabs-right">
<div
v-if="currentTemplateLabel"
class="active-template"
>
{{ currentTemplateLabel }}&nbsp;&nbsp;({{
currentTemplateShowList.length
}})
<deBtn
type="primary"
icon="el-icon-upload2"
@click="templateImport(currentTemplateId)"
>
{{ $t('app_template.app_upload') }}
</deBtn>
</div>
<el-empty
v-if="!currentTemplateShowList.length"
:image="noneImg"
:description="$t('app_template.no_apps')"
/>
<div
v-show="currentTemplateId !== ''"
id="template-box"
class="template-box"
>
<template-item
v-for="item in currentTemplateShowList"
:key="item.id"
:width="templateCurWidth"
:model="item"
@command="(key) => handleCommand(key, item)"
/>
</div>
</div>
</div>
</div>
<el-dialog
:title="dialogTitle"
:visible.sync="editTemplate"
append-to-body
class="de-dialog-form"
width="600px"
destroy-on-close="true"
>
<el-form
ref="templateEditForm"
class="de-form-item"
:model="templateEditForm"
:rules="templateEditFormRules"
>
<el-form-item
:label="dialogTitleLabel"
prop="name"
>
<el-input v-model="templateEditForm.name" />
</el-form-item>
<el-form-item
:label="$t('app_template.app_group_icon')"
prop="icon"
>
<el-col style="width: 148px!important;height: 148px!important;overflow: hidden">
<el-upload
action=""
accept=".jpeg,.jpg,.png,.gif,.svg"
class="avatar-uploader"
list-type="picture-card"
:class="{disabled:uploadDisabled}"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove"
:http-request="upload"
:file-list="fileList"
>
<i class="el-icon-plus" />
</el-upload>
<el-dialog
top="25vh"
width="600px"
:append-to-body="true"
:destroy-on-close="true"
:visible.sync="dialogVisible"
>
<img
width="100%"
:src="dialogImageUrl"
>
</el-dialog>
</el-col>
</el-form-item>
</el-form>
<div
slot="footer"
class="dialog-footer"
>
<deBtn
secondary
@click="close()"
>{{ $t("commons.cancel") }}</deBtn>
<deBtn
type="primary"
@click="saveTemplateEdit(templateEditForm)"
>{{ $t("commons.confirm") }}
</deBtn>
</div>
</el-dialog>
<!--导入templatedialog-->
<el-dialog
:title="templateDialog.title"
:visible.sync="templateDialog.visible"
:show-close="true"
class="de-dialog-form"
width="600px"
>
<template-import
v-if="templateDialog.visible"
:pid="templateDialog.pid"
:opt-type="templateOptType"
:app-template-info="currentAppTemplateInfo"
@refresh="showCurrentTemplate(currentTemplateId,
currentTemplateLabel)"
@closeEditTemplateDialog="closeEditTemplateDialog"
/>
</el-dialog>
<app-template-content :show-position="'system-setting'" />
</de-layout-content>
</template>
<script>
import AppTemplateContent from '@/views/panel/appTemplate/AppTemplateContent'
import DeLayoutContent from '@/components/business/DeLayoutContent'
import TemplateList from './component/TemplateList'
import TemplateItem from './component/TemplateItem'
import TemplateImport from './component/TemplateImport'
import { save, update, templateDelete, find } from '@/api/system/appTemplate'
import elementResizeDetectorMaker from 'element-resize-detector'
import msgCfm from '@/components/msgCfm/index'
import { uploadFileResult } from '@/api/staticResource/staticResource'
import { imgUrlTrans } from '@/components/canvas/utils/utils'
export default {
name: 'AppTemplate',
components: { DeLayoutContent, TemplateList, TemplateItem, TemplateImport },
mixins: [msgCfm],
data() {
return {
templateOptType: 'add',
currentAppTemplateInfo: null,
fileList: [],
dialogImageUrl: '',
dialogVisible: false,
uploadDisabled: false,
showShare: false,
currentTemplateShowList: [],
noneImg: require('@/assets/None.png'),
currentPid: '',
currentTemplateType: 'self',
templateEditFormRules: {
name: [
{ required: true, trigger: 'blur', validator: this.roleValidator },
{
required: true,
message: this.$t('commons.input_content'),
trigger: 'blur'
},
{
max: 50,
message: this.$t('commons.char_can_not_more_50'),
trigger: 'change'
}
],
icon: [
{
required: true,
message: '请选择文件',
trigger: 'change'
}
]
},
templateEditForm: {},
editTemplate: false,
dialogTitle: '',
dialogTitleLabel: '',
currentTemplateLabel: '',
currentTemplateId: '',
templateList: [],
templateMiniWidth: 286,
templateCurWidth: 286,
formType: '',
originName: '',
templateDialog: {
title: '导入应用',
visible: false,
pid: ''
}
}
},
computed: {
nameList() {
const { nodeType } = this.templateEditForm || {}
if (nodeType === 'template') {
return this.currentTemplateShowList.map((ele) => ele.name)
}
if (nodeType === 'folder') {
return this.templateList.map((ele) => ele.name)
}
return []
}
},
mounted() {
this.getTree()
const _this = this
const erd = elementResizeDetectorMaker()
const templateMainDom = document.getElementById('template-box')
// div
erd.listenTo(templateMainDom, (element) => {
_this.$nextTick(() => {
const curSeparator = Math.trunc(
templateMainDom.offsetWidth / _this.templateMiniWidth
)
_this.templateCurWidth =
Math.trunc(templateMainDom.offsetWidth / curSeparator) - 24 - curSeparator
})
})
},
methods: {
roleValidator(rule, value, callback) {
if (this.nameRepeat(value)) {
const { nodeType } = this.templateEditForm || {}
callback(
new Error(
this.$t(
`system_parameter_setting.${
nodeType === 'folder'
? 'name_already_exists_type'
: 'the_same_category'
}`
)
)
)
} else {
callback()
}
},
nameRepeat(value) {
if (!this.nameList || this.nameList.length === 0) {
return false
}
//
if (this.formType === 'edit' && this.originName === value) {
return false
}
return this.nameList.some((name) => name === value)
},
handleCommand(key, data) {
switch (key) {
case 'rename':
this.templateEdit(data)
break
case 'delete':
this.templateDeleteConfirm(data)
break
case 'update':
this.updateAppTemplate(data)
break
default:
break
}
},
updateAppTemplate(data) {
this.templateOptType = 'update'
this.templateDialog.visible = true
this.currentAppTemplateInfo = data
this.templateDialog.pid = data.pid
},
templateDeleteConfirm(template) {
const options = {
title: '是否卸载当前应用?',
type: 'primary',
cb: () => this.templateDelete(template.id)
}
this.handlerConfirm(options, '卸载')
},
handleClick(tab, event) {
this.getTree()
},
showCurrentTemplate(pid, name) {
this.currentTemplateId = pid
this.currentTemplateLabel = name
if (this.currentTemplateId) {
find({ pid: this.currentTemplateId }).then((response) => {
this.currentTemplateShowList = response.data
})
}
},
templateListDelete(id) {
if (id) {
templateDelete(id).then((response) => {
this.openMessageSuccess('commons.delete_success')
this.getTree()
})
}
},
templateDelete(id) {
if (id) {
templateDelete(id).then((response) => {
this.openMessageSuccess('commons.delete_success')
this.showCurrentTemplate(this.currentTemplateId, this.currentTemplateLabel)
})
}
},
showTemplateEditDialog(type, templateInfo) {
this.templateEditForm = null
this.formType = type
if (type === 'edit') {
if (templateInfo.icon) {
this.fileList.push({ url: imgUrlTrans(templateInfo.icon) })
}
this.templateEditForm = JSON.parse(JSON.stringify(templateInfo))
this.dialogTitle = this.$t(
`system_parameter_setting.${
this.templateEditForm.nodeType === 'folder'
? 'edit_classification'
: 'edit_template'
}`
)
this.originName = this.templateEditForm.label
} else {
this.fileList = []
this.dialogTitle = this.$t('panel.add_app_category')
this.templateEditForm = {
name: '',
nodeType: 'folder',
templateType: this.currentTemplateType,
icon: '',
level: 0,
pid: 0
}
}
this.dialogTitleLabel = this.$t(
`system_parameter_setting.${
this.templateEditForm.nodeType === 'folder'
? 'classification_name'
: 'template_name'
}`
)
this.editTemplate = true
},
templateEdit(templateInfo) {
this.showTemplateEditDialog('edit', templateInfo)
},
saveTemplateEdit(templateEditForm) {
this.$refs['templateEditForm'].validate((valid) => {
if (valid) {
const method = templateEditForm.id ? update : save
method(templateEditForm).then((response) => {
this.close()
this.openMessageSuccess(
`system_parameter_setting.${
this.templateEditForm.id
? 'rename_succeeded'
: 'added_successfully'
}`
)
this.getTree()
})
} else {
return false
}
})
},
close() {
this.$refs['templateEditForm'].resetFields()
this.editTemplate = false
this.handleRemove()
},
getTree() {
const request = {
templateType: this.currentTemplateType,
pid: '0'
}
find(request).then((res) => {
this.templateList = res.data
this.showFirst()
})
},
showFirst() {
//
if (this.templateList && this.templateList.length > 0) {
let showFirst = true
this.templateList.forEach((template) => {
if (template.id === this.currentTemplateId) {
showFirst = false
}
})
if (showFirst) {
this.$nextTick().then(() => {
const [obj = {}] = this.templateList
this.$refs.templateList.nodeClick(obj)
})
} else {
this.showCurrentTemplate(this.currentTemplateId, this.currentTemplateLabel)
}
} else {
this.currentTemplateShowList = []
}
},
closeEditTemplateDialog() {
this.templateDialog.visible = false
},
templateImport(pid) {
this.templateOptType = 'new'
this.currentAppTemplateInfo = null
this.templateDialog.visible = true
this.templateDialog.pid = pid
},
handleRemove(file, fileList) {
this.uploadDisabled = false
this.templateEditForm.icon = null
this.fileList = []
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url
this.dialogVisible = true
},
upload(file) {
const _this = this
uploadFileResult(file.file, (fileUrl) => {
_this.templateEditForm.icon = fileUrl
})
}
}
components: { DeLayoutContent, AppTemplateContent }
}
</script>
<style lang="scss" scoped>
.de-template {
height: 100%;
background-color: var(--MainBG, #f5f6f7);
.tabs-container {
height: 100%;
background: var(--ContentBG, #ffffff);
overflow-x: auto;
}
.flex-tabs {
display: flex;
background: #f5f6f7;
}
.de-tabs-left {
background: #fff;
width: 269px;
border-right: 1px solid rgba(31, 35, 41, 0.15);
padding: 24px;
}
.de-tabs-right {
flex: 1;
background: #fff;
padding: 24px 0 24px 24px;
overflow: hidden;
.template-box {
display: flex;
flex-wrap: wrap;
overflow-y: auto;
box-sizing: border-box;
align-content: flex-start;
height: calc(100% - 10px);
width: 100%;
padding-bottom: 24px;
}
.active-template {
margin: 4px 0 20px 0;
padding-right: 24px;
font-family: "PingFang SC";
font-style: normal;
font-weight: 500;
font-size: 16px;
display: flex;
align-items: center;
justify-content: space-between;
color: var(--deTextPrimary, #1f2329);
}
}
}
::v-deep .container-wrapper {
padding: 0px !important;
}
</style>

View File

@ -0,0 +1,125 @@
<template>
<el-row>
<el-row style="height: 56px">
<el-col
:span="12"
style="text-align: left;line-height: 56px"
>
<svg-icon
icon-class="icon_left_outlined"
class="toolbar-icon-active icon20 margin-left20"
@click="closePreview"
/>
<span class="text16 margin-left12">
{{ templateInfo.name }}
</span>
</el-col>
<el-col
:span="12"
style="text-align: right;line-height: 56px;padding-right: 24px"
>
<el-button
size="small"
type="primary"
width="80px"
@click="appApply"
>{{ $t('panel.apply') }}
</el-button>
</el-col>
</el-row>
<el-row class="img-main">
<img
width="100%"
:src="templateInfo.snapshot"
alt=""
>
</el-row>
</el-row>
</template>
<script>
export default {
name: 'AppMarketPreview',
components: { },
props: {
templateInfo: {
type: Object,
required: true
}
},
data() {
return {
noneImg: require('@/assets/None.png')
}
},
computed: {
},
watch: {
},
mounted() {
},
methods: {
appApply() {
this.$emit('appApply')
},
closePreview() {
this.$emit('closePreview')
}
}
}
</script>
<style lang="scss" scoped>
.img-main{
border-radius: 4px;
overflow-x: auto;
overflow-y: auto;
text-align: center;
height: calc(100vh - 113px)!important;
}
.toolbar-icon-active {
cursor: pointer;
transition: .1s;
border-radius: 3px;
&:active {
color: #000;
border-color: #3a8ee6;
background-color: red;
outline: 0;
}
&:hover {
background-color: rgba(31, 35, 41, 0.1);
color: #3a8ee6;
}
}
.icon20 {
font-size: 20px;
color: var(--TextPrimary, #1F2329);
}
.icon16 {
font-size: 16px!important;
color: var(--TextPrimary, #1F2329);
}
.text16 {
font-size: 16px;
font-weight: 500;
line-height: 24px;
color: var(--TextPrimary, #1F2329);
}
.margin-left12 {
margin-left: 12px !important;
}
.margin-left20 {
margin-left: 12px !important;
}
</style>

View File

@ -1,12 +1,11 @@
<template>
<el-row class="outer-body">
<!--预览模式-->
<MarketPreview
<AppMarketPreview
v-if="previewModel"
:preview-id="templatePreviewId"
:current-app="currentApp"
:template-info="previewItem"
@closePreview="previewModel=false"
@templateApply="templateApply"
@appApply="appApply"
/>
<!--列表模式-->
<el-row
@ -32,42 +31,16 @@
</el-tabs>
</el-row>
<el-row v-show="marketActiveTab==='apps'">
<el-row
v-show="hasResult"
id="template-main"
v-loading="$store.getters.loadingMap[$store.getters.currentPath]"
class="template-main"
>
<el-col
v-for="(templateItem) in currentAppShowList"
:key="templateItem.id"
style="text-align: center;padding: 24px 12px 0 12px"
:style="{width: templateSpan}"
>
<app-template-item
:template="templateItem"
:base-url="baseUrl"
:width="templateCurWidth"
@appPreview="appPreview"
/>
</el-col>
</el-row>
<el-row
v-show="!hasResult"
class="custom-position template-main"
>
<div style="text-align: center">
<svg-icon
icon-class="no_result"
style="font-size: 75px;margin-bottom: 16px"
/>
<br>
<span>{{ $t('commons.no_result') }}</span>
</div>
</el-row>
<app-template-content
:ref="'appTemplateContent'"
class="template-main-content"
:show-position="'market-manage'"
@previewApp="previewApp"
@applyNew="applyNew"
/>
</el-row>
<el-row
v-show="marketActiveTab==='apply_logs'"
v-if="marketActiveTab==='apply_logs'"
class="main-log-area template-main"
>
<app-template-log class="log-area" />
@ -123,15 +96,23 @@
<el-button
size="mini"
@click="folderSelectShow=false"
>{{ $t('commons.cancel') }}</el-button>
>{{ $t('commons.cancel') }}
</el-button>
<el-button
size="mini"
type="primary"
:disabled="!panelForm.name || !panelForm.pid"
@click="apply"
>{{ $t('commons.confirm') }}</el-button>
>{{ $t('commons.confirm') }}
</el-button>
</div>
</el-dialog>
<keep-alive>
<app-template-apply
ref="templateApply"
/>
</keep-alive>
</el-row>
</template>
@ -139,16 +120,18 @@
import { searchAppTemplate } from '@/api/appTemplateMarket'
import { groupTree, panelSave } from '@/api/panel/panel'
import { DEFAULT_COMMON_CANVAS_STYLE_STRING } from '@/views/panel/panel'
import MarketPreview from '@/views/panel/appTemplateMarket/component/MarketPreview'
import elementResizeDetectorMaker from 'element-resize-detector'
import AppTemplateItem from '@/views/panel/appTemplateMarket/component/AppTemplateItem'
import AppTemplateLog from '@/views/panel/appTemplateMarket/log'
import AppTemplateContent from '@/views/panel/appTemplate/AppTemplateContent'
import AppMarketPreview from '@/views/panel/appTemplateMarket/component/AppMarketPreview'
import AppTemplateApply from '@/views/panel/appTemplate/component/AppTemplateApply'
export default {
name: 'AppTemplateMarket',
components: { AppTemplateLog, AppTemplateItem, MarketPreview },
components: { AppTemplateApply, AppMarketPreview, AppTemplateContent, AppTemplateLog },
data() {
return {
previewItem: null,
hasResult: true,
templateMiniWidth: 330,
templateCurWidth: 310,
@ -194,11 +177,8 @@ export default {
}
}
},
computed: {
},
watch: {
},
computed: {},
watch: {},
mounted() {
this.initMarketTemplate()
this.getGroupTree()
@ -215,6 +195,24 @@ export default {
})
},
methods: {
applyNew(item) {
const datasourceInfo = JSON.parse(item.datasourceInfo)[0]
const param = {
datasourceType: datasourceInfo.type,
appTemplateId: item.id,
appTemplateName: item.name,
panelName: item.name,
datasetGroupName: item.name
}
this.$refs.templateApply.init(param)
},
appApply() {
this.applyNew(this.previewItem)
},
previewApp(item) {
this.previewModel = true
this.previewItem = item
},
initMarketTemplate() {
searchAppTemplate({ nodeType: 'folder' }).then(rsp => {
this.currentAppShowList = rsp.data
@ -280,109 +278,120 @@ export default {
</script>
<style lang="scss" scoped>
.template-main{
text-align: center;
border-radius: 4px;
padding: 0 12px 24px 12px;
height: calc(100vh - 190px)!important;
overflow-x: hidden;
overflow-y: auto;
background-color: var(--ContentBG,#ffffff);
}
.market-main{
padding:24px
}
.title-left{
float: left;
font-size: 20px;
font-weight: 500;
line-height: 28px;
color: var(--TextPrimary, #1F2329);
}
.title-right{
float: right;
width: 320px;
}
.dialog-footer-self{
text-align: right;
}
.search-button-self{
text-align: left;
padding-left: 10px;
}
.template-main-content {
height: calc(100vh - 190px) !important;
}
.topbar-icon-active {
cursor: pointer;
transition: .1s;
border-radius: 3px;
font-size: 22px;
background-color: rgb(245, 245, 245);
.template-main {
text-align: center;
border-radius: 4px;
padding: 0 12px 24px 12px;
height: calc(100vh - 190px) !important;
overflow-x: hidden;
overflow-y: auto;
background-color: var(--ContentBG, #ffffff);
}
.market-main {
padding: 24px
}
.title-left {
float: left;
font-size: 20px;
font-weight: 500;
line-height: 28px;
color: var(--TextPrimary, #1F2329);
}
.title-right {
float: right;
width: 320px;
}
.dialog-footer-self {
text-align: right;
}
.search-button-self {
text-align: left;
padding-left: 10px;
}
.topbar-icon-active {
cursor: pointer;
transition: .1s;
border-radius: 3px;
font-size: 22px;
background-color: rgb(245, 245, 245);
&:active {
color: #000;
border-color: #3a8ee6;
background-color: red;
outline: 0;
}
color: #000;
border-color: #3a8ee6;
background-color: red;
outline: 0;
}
&:hover {
background-color: rgba(31, 35, 41, 0.1);
color: #3a8ee6;
}
background-color: rgba(31, 35, 41, 0.1);
color: #3a8ee6;
}
.custom-position {
height: 80vh;
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
flex-flow: row nowrap;
color: #646A73;
font-weight: 400;
}
.outer-body{
width: 100%;
height: calc(100vh - 56px);
background-color: var(--MainBG,#f5f6f7);
}
.custom-position {
height: 80vh;
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
flex-flow: row nowrap;
color: #646A73;
font-weight: 400;
}
.outer-body {
width: 100%;
height: calc(100vh - 56px);
background-color: var(--MainBG, #f5f6f7);
}
.market-dialog-css {
::v-deep .el-form-item__label {
width: 100% !important;
text-align: left;
}
.market-dialog-css{
::v-deep .el-form-item__label {
width: 100% !important;
text-align: left;
}
::v-deep
.el-form-item.is-required:not(.is-no-asterisk)
> .el-form-item__label:before {
display: none;
}
::v-deep
.el-form-item.is-required:not(.is-no-asterisk)
> .el-form-item__label::after {
content: "*";
color: #f54a45;
margin-left: 2px;
}
::v-deep .el-form-item__content {
margin-left: 0 !important;
}
::v-deep .vue-treeselect__input{
vertical-align:middle;
}
::v-deep
.el-form-item.is-required:not(.is-no-asterisk)
> .el-form-item__label:before {
display: none;
}
.main-log-area{
::v-deep
.el-form-item.is-required:not(.is-no-asterisk)
> .el-form-item__label::after {
content: "*";
color: #f54a45;
margin-left: 2px;
}
::v-deep .el-form-item__content {
margin-left: 0 !important;
}
::v-deep .vue-treeselect__input {
vertical-align: middle;
}
}
.main-log-area {
position: relative;
padding: 24px;
}
.log-area{
height: calc(100vh - 240px);
}
.log-area {
height: calc(100vh - 240px);
}
</style>

View File

@ -10,7 +10,8 @@
type="primary"
icon="el-icon-plus"
@click="applyNew()"
>{{ $t('commons.create') }}</deBtn>
>{{ $t('commons.create') }}
</deBtn>
<span>&nbsp;</span>
</el-col>
<el-col
@ -33,10 +34,12 @@
:plain="!!cacheCondition.length"
icon="iconfont icon-icon-filter"
@click="filterShow"
>{{ $t("user.filter")
}}<template v-if="filterTexts.length">
({{ cacheCondition.length }})
</template>
>{{
$t('user.filter')
}}
<template v-if="filterTexts.length">
({{ cacheCondition.length }})
</template>
</deBtn>
</el-col>
</el-row>
@ -45,7 +48,7 @@
class="filter-texts"
>
<span class="sum">{{ paginationConfig.total }}</span>
<span class="title">{{ $t("user.result_one") }}</span>
<span class="title">{{ $t('user.result_one') }}</span>
<el-divider direction="vertical" />
<i
v-if="showScroll"
@ -74,7 +77,8 @@
class="clear-btn"
icon="el-icon-delete"
@click="clearFilter"
>{{ $t("user.clear_filter") }}</el-button>
>{{ $t('user.clear_filter') }}
</el-button>
</div>
<div
id="resize-for-filter"
@ -82,6 +86,7 @@
:class="[filterTexts.length ? 'table-container-filter' : '']"
>
<grid-table
:ref="'grid-table'"
v-loading="$store.getters.loadingMap[$store.getters.currentPath]"
:table-data="data"
:columns="[]"
@ -92,23 +97,37 @@
>
<el-table-column
show-overflow-tooltip
prop="opType"
:label="'数据源'"
prop="datasourceName"
:label="$t('app_template.datasource')"
>
<template #default="{ row }">
<span>{{ row.datasourceName }}</span>
<span
v-if="row.datasourceId && hasDataPermission('use',row.datasourcePrivileges)"
class="link-span"
@click="goToDatasource(row)"
>{{ row.datasourceName }}</span>
<span v-else>{{ row.datasourceName }}</span>
</template>
</el-table-column>
<el-table-column
show-overflow-tooltip
prop="datasetGroupName"
:label="'数据集分组'"
:label="$t('app_template.dataset_group')"
/>
<el-table-column
show-overflow-tooltip
prop="panelName"
:label="'仪表板'"
/>
:label="$t('app_template.panel')"
>
<template #default="{ row }">
<span
v-if="row.panelId && hasDataPermission('use',row.panelPrivileges)"
class="link-span"
@click="goPanel(row)"
>{{ row.panelName }}</span>
<span v-else>{{ row.panelName }}</span>
</template>
</el-table-column>
<el-table-column
show-overflow-tooltip
prop="appName"
@ -124,6 +143,31 @@
<span>{{ scope.row.applyTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column
v-if="optShow"
slot="__operation"
:label="$t('commons.operating')"
fixed="right"
:width="operateWidth"
>
<template slot-scope="scope">
<el-button
v-permission="['appLog:edit']"
class="de-text-btn mr2"
type="text"
@click="editApply(scope.row)"
>{{ $t('commons.edit') }}
</el-button>
<el-button
v-if="scope.row.id !== 1"
v-permission="['appLog:del']"
class="de-text-btn"
type="text"
@click="del(scope.row)"
>{{ $t('commons.delete') }}
</el-button>
</template>
</el-table-column>
</grid-table>
</div>
<keep-alive>
@ -132,6 +176,39 @@
@search="filterDraw"
/>
</keep-alive>
<keep-alive>
<app-template-apply
ref="templateEditApply"
@closeDraw="closeDraw"
/>
</keep-alive>
<!--导入templatedialog-->
<el-dialog
:title="$t('app_template.log_delete_tips')"
:visible.sync="deleteConfirmDialog"
:show-close="true"
width="420px"
>
<el-row>
<el-checkbox
v-model="deleteItemInfo.deleteResource"
:disabled="!(hasDataPermission('manage',deleteItemInfo.panelPrivileges) &&hasDataPermission('manage',deleteItemInfo.datasetPrivileges) &&hasDataPermission('manage',deleteItemInfo.datasourcePrivileges))"
/>
{{ $t('app_template.log_resource_delete_tips') }}
</el-row>
<span slot="footer">
<el-button
size="mini"
@click="closeDel"
>{{ $t('commons.cancel') }}</el-button>
<el-button
type="danger"
size="mini"
@click="confirmDel"
>{{ $t('commons.confirm') }}</el-button>
</span>
</el-dialog>
</el-row>
</template>
@ -144,10 +221,13 @@ import {
addOrder,
formatOrders
} from '@/utils/index'
import { logGrid } from '@/api/appTemplateMarket/log'
import { deleteLogAndResource, logGrid } from '@/api/appTemplateMarket/log'
import { findOneWithParent } from '@/api/panel/panel'
import AppTemplateApply from '@/views/panel/appTemplate/component/AppTemplateApply'
export default {
name: 'AppTemplateLog',
components: { GridTable, filterUser },
components: { AppTemplateApply, GridTable, filterUser },
mixins: [keyEnter],
props: {
appTemplateId: {
@ -162,6 +242,12 @@ export default {
},
data() {
return {
optShow: false,
deleteConfirmDialog: false,
deleteItemInfo: {
deleteResource: false
},
operateWidth: 168,
columns: [],
paginationConfig: {
currentPage: 1,
@ -194,6 +280,56 @@ export default {
this.resizeObserver()
},
methods: {
closeDel() {
this.deleteItemInfo = {
deleteResource: false
}
this.deleteConfirmDialog = false
},
confirmDel() {
deleteLogAndResource(this.deleteItemInfo).then(() => {
this.closeDel()
this.search()
})
},
closeDraw() {
this.search()
},
editApply(item) {
const param = {
datasourceType: item.datasourceType,
logId: item.id,
panelId: item.panelId,
panelGroupPid: item.panelGroupPid,
datasourceId: item.datasourceId,
datasetGroupPid: item.datasetGroupPid,
datasetGroupId: item.datasetGroupId,
datasetGroupName: item.datasetGroupName,
panelName: item.panelName,
datasourcePrivileges: item.datasourcePrivileges,
panelPrivileges: item.panelPrivileges,
datasetPrivileges: item.datasetPrivileges
}
this.$refs.templateEditApply.init(param)
},
goToDatasource(row) {
},
goPanel(row) {
findOneWithParent(row.panelId).then(rsp => {
this.$router.push({ name: 'panel', params: rsp.data })
})
},
edit() {
},
del(item) {
this.deleteItemInfo = {
...item,
deleteResource: false
}
this.deleteConfirmDialog = true
},
applyNew() {
this.$emit('applyNew')
},
@ -289,6 +425,11 @@ export default {
logGrid(currentPage, pageSize, param).then((response) => {
this.data = response.data.listObject
this.paginationConfig.total = response.data.itemCount
const _this = this
_this.optShow = false
this.$nextTick(() => {
_this.optShow = true
})
})
}
}
@ -302,4 +443,13 @@ export default {
.table-container-filter {
height: calc(100% - 110px);
}
.link-span {
color: #3370FF;
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
</style>

View File

@ -118,6 +118,10 @@
icon="el-icon-picture-outline"
@click.native="downloadAsImage"
>{{ $t('panel.export_to_img') }}</el-dropdown-item>
<el-dropdown-item
icon="el-icon-s-data"
@click.native="downLoadToApp"
>{{ $t('panel.export_to_app') }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</span>
@ -304,6 +308,7 @@ export default {
},
data() {
return {
canvasInfoTemp: 'preview-temp-canvas-main',
canvasId: 'canvas-main',
showMain: true,
pdfTemplateSelectedIndex: 0,
@ -405,7 +410,7 @@ export default {
saveToTemplate() {
this.dataLoading = true
setTimeout(() => {
html2canvas(document.getElementById('canvasInfoTemp')).then(canvas => {
html2canvas(document.getElementById(this.canvasInfoTemp)).then(canvas => {
this.templateSaveShow = true
this.dataLoading = false
const snapshot = canvas.toDataURL('image/jpeg', 0.1) // 0.2
@ -430,7 +435,7 @@ export default {
_this.dataLoading = true
try {
_this.findStaticSource(function(staticResource) {
html2canvas(document.getElementById('canvasInfoTemp')).then(canvas => {
html2canvas(document.getElementById(_this.canvasInfoTemp)).then(canvas => {
_this.dataLoading = false
const snapshot = canvas.toDataURL('image/jpeg', 0.1) // 0.1
if (snapshot !== '') {
@ -458,9 +463,9 @@ export default {
_this.dataLoading = true
try {
_this.findStaticSource(function(staticResource) {
html2canvas(document.getElementById('canvasInfoTemp')).then(canvas => {
html2canvas(document.getElementById(_this.canvasInfoTemp)).then(canvas => {
_this.dataLoading = false
const snapshot = canvas.toDataURL('image/jpeg', 0.1) // 0.1
const snapshot = canvas.toDataURL('image/jpeg', 1) // 0.1
if (snapshot !== '') {
const panelInfo = {
name: _this.$store.state.panel.panelInfo.name,
@ -528,7 +533,7 @@ export default {
setTimeout(() => {
this.exporting = true
setTimeout(() => {
const canvasID = document.getElementById('canvasInfoTemp')
const canvasID = document.getElementById(this.canvasInfoTemp)
const a = document.createElement('a')
html2canvas(canvasID).then(canvas => {
this.exporting = false
@ -559,7 +564,7 @@ export default {
setTimeout(() => {
this.exporting = true
setTimeout(() => {
html2canvas(document.getElementById('canvasInfoTemp')).then(canvas => {
html2canvas(document.getElementById(this.canvasInfoTemp)).then(canvas => {
const snapshot = canvas.toDataURL('image/jpeg', 1) //
this.dataLoading = false
this.exporting = false
@ -573,7 +578,7 @@ export default {
},
refreshTemplateInfo() {
this.templateInfo = {}
html2canvas(document.getElementById('canvasInfoTemp')).then(canvas => {
html2canvas(document.getElementById(this.canvasInfoTemp)).then(canvas => {
const snapshot = canvas.toDataURL('image/jpeg', 0.1) // 0.2
if (snapshot !== '') {
this.templateInfo = {
@ -623,7 +628,9 @@ export default {
if (this.showType === 1 && this.shareUserId !== null) {
const param = { userId: this.shareUserId }
proxyInitPanelData(this.panelInfo.id, param, null)
} else { initPanelData(this.panelInfo.id, false) }
} else {
initPanelData(this.panelInfo.id, false)
}
},
changePublishState() {
if (this.panelInfo.status === 'publish') {
@ -642,72 +649,76 @@ export default {
</script>
<style>
.view-list {
height: 100%;
width: 20%;
min-width: 180px;
max-width: 220px;
border: 1px solid #E6E6E6;
border-left: 0 solid;
overflow-y: auto;
}
.view-list {
height: 100%;
width: 20%;
min-width: 180px;
max-width: 220px;
border: 1px solid #E6E6E6;
border-left: 0 solid;
overflow-y: auto;
}
.view-list-thumbnails-outline {
height: 100%;
overflow-y: auto;
}
.view-list-thumbnails-outline {
height: 100%;
overflow-y: auto;
}
.view-list-thumbnails {
width: 100%;
padding: 0px 15px 15px 0px;
}
.view-list-thumbnails {
width: 100%;
padding: 0px 15px 15px 0px;
}
.panel-design {
min-height: 400px;
height: 100%;
min-width: 500px;
overflow-y: hidden;
border-top: 1px solid #E6E6E6;
}
.panel-design {
min-height: 400px;
height: 100%;
min-width: 500px;
overflow-y: hidden;
border-top: 1px solid #E6E6E6;
}
.panel-design-head {
height: 40px;
background-color: var(--SiderBG, white);
padding: 0 10px;
line-height: 40px;
}
.panel-share-head {
height: auto !important;
}
.blackTheme .panel-design-head {
color: var(--TextActive);
}
.panel-design-head {
height: 40px;
background-color: var(--SiderBG, white);
padding: 0 10px;
line-height: 40px;
}
.panel-design-preview {
width: 100%;
height: calc(100% - 40px);
overflow-x: hidden;
overflow-y: auto;
/*padding: 5px;*/
}
.panel-share-head {
height: auto !important;
}
.custom-position {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
flex-flow: row nowrap;
color: #9ea6b2;
}
.blackTheme .panel-design-head {
color: var(--TextActive);
}
.dialog-css2 ::v-deep .el-dialog__title {
font-size: 14px!important;
}
.dialog-css2 ::v-deep .el-dialog__header {
padding: 20px 20px 0!important;
}
.dialog-css2 ::v-deep .el-dialog__body {
padding: 0px 20px!important;
}
.panel-design-preview {
width: 100%;
height: calc(100% - 40px);
overflow-x: hidden;
overflow-y: auto;
/*padding: 5px;*/
}
.custom-position {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
flex-flow: row nowrap;
color: #9ea6b2;
}
.dialog-css2 ::v-deep .el-dialog__title {
font-size: 14px !important;
}
.dialog-css2 ::v-deep .el-dialog__header {
padding: 20px 20px 0 !important;
}
.dialog-css2 ::v-deep .el-dialog__body {
padding: 0px 20px !important;
}
</style>

View File

@ -1,9 +1,12 @@
<template>
<div
v-loading="formLoading"
class="de-ds-form"
:class="positionCheck('datasource')?'de-ds-form':'de-ds-form-app'"
>
<div class="de-ds-top">
<div
v-if="positionCheck('datasource')"
class="de-ds-top"
>
<span class="name">
<i
class="el-icon-arrow-left"
@ -17,7 +20,7 @@
!canEdit
? $t('datasource.show_info')
: formType == 'add'
? `${$t('commons.create') + typeMap }${ $t('commons.datasource')}`
? `${$t('commons.create') + typeMap}${$t('commons.datasource')}`
: $t('datasource.modify')
}}
</span>
@ -63,6 +66,73 @@
<div class="de-ds-cont">
<div class="de-ds-inner">
<div class="w600">
<el-form
v-if="positionCheck('appMarket')"
ref="attachParamsForm"
:model="attachForm"
:rules="attachRule"
class="de-form-item"
label-width="180px"
label-position="right"
>
<div
class="de-row-rules"
style="margin: 0 0 16px 0;"
>
<span>{{ $t('datasource.basic_info') }}</span>
</div>
<el-form-item
:label="$t('app_template.panel_position')"
prop="panelGroupPid"
>
<treeselect
v-model="attachForm.panelGroupPid"
:disabled="!(formType === 'add' ? true : hasDataPermission('manage', outerParams.panelPrivileges))"
:clearable="false"
:options="panelGroupList"
:normalizer="normalizer"
:placeholder="$t('chart.select_group')"
: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')"
/>
</el-form-item>
<el-form-item
:label="$t('app_template.panel_name')"
prop="panelName"
>
<el-input
v-model="attachForm.panelName"
:disabled="!(formType === 'add' ? true : hasDataPermission('manage', outerParams.panelPrivileges))"
/>
</el-form-item>
<el-form-item
:label="$t('app_template.dataset_group_position')"
prop="datasetGroupPid"
>
<treeselect
v-model="attachForm.datasetGroupPid"
:disabled="!(formType === 'add' ? true : hasDataPermission('manage', outerParams.datasetPrivileges))"
:clearable="false"
:options="datasetGroupList"
:normalizer="normalizer"
:placeholder="$t('chart.select_group')"
: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')"
/>
</el-form-item>
<el-form-item
:label="$t('app_template.dataset_group_name')"
prop="datasetGroupName"
>
<el-input
v-model="attachForm.datasetGroupName"
:disabled="!(formType === 'add' ? true : hasDataPermission('manage', outerParams.panelPrivileges))"
/>
</el-form-item>
</el-form>
<el-form
ref="dsForm"
:model="form"
@ -80,7 +150,9 @@
label-position="right"
>
<div class="de-row-rules">
<span>{{ $t('datasource.basic_info') }}</span>
<span>{{
positionCheck('appMarket') ? $t('app_template.datasource_info') : $t('datasource.basic_info')
}}</span>
</div>
<el-form-item
:label="$t('datasource.display_name')"
@ -92,6 +164,24 @@
:placeholder="$t('commons.input_name')"
/>
</el-form-item>
<el-form-item
:label="$t('datasource.type')"
prop="type"
>
<el-select
v-model="form.type"
:placeholder="$t('datasource.please_choose_type')"
style="width: 100%"
disabled
>
<el-option
v-for="item in dsTypes"
:key="item.type"
:label="item.name"
:value="item.type"
/>
</el-select>
</el-form-item>
<el-form-item
:label="$t('commons.description')"
prop="desc"
@ -146,6 +236,52 @@
</div>
</div>
</div>
<div
v-if="positionCheck('appMarket')"
class="de-ds-bottom"
>
<div
class="apply"
style="width: 100%"
>
<template v-if="canEdit">
<deBtn
secondary
@click="closeDraw"
>{{ $t('commons.cancel') }}
</deBtn>
<deBtn
v-if="
formType === 'add' ||
hasDataPermission('manage', params.privileges)
"
secondary
@click="validaDatasource"
>{{ $t('commons.validate') }}
</deBtn>
<deBtn
v-if="
formType === 'add' ||
hasDataPermission('manage', params.privileges)
"
type="primary"
@click="save"
>{{ $t('commons.save') }}
</deBtn>
</template>
<template v-else>
<deBtn
v-if="
formType === 'add'
? true
: hasDataPermission('manage', params.privileges)
"
@click="validaDatasource"
>{{ $t('commons.validate') }}
</deBtn>
</template>
</div>
</div>
</div>
</template>
@ -167,6 +303,10 @@ import PluginCom from '@/views/system/plugin/PluginCom'
import { listDatasourceType, listDatasource } from '@/api/system/datasource'
import deTextarea from '@/components/deCustomCm/deTextarea.vue'
import msgCfm from '@/components/msgCfm'
import { dsGroupTree } from '@/api/dataset/dataset'
import { appApply, appEdit, groupTree } from '@/api/panel/panel'
import { deepCopy } from '@/components/canvas/utils/utils'
export default {
name: 'DsForm',
components: {
@ -175,8 +315,51 @@ export default {
deTextarea
},
mixins: [msgCfm],
props: {
referencePosition: {
type: String,
default: 'datasource'
},
outerParams: {
type: Object,
request: false
}
},
data() {
return {
appMarketEdit: true,
attachRule: {
panelName: [
{
required: true,
min: 2,
max: 25,
message: i18n.t('datasource.input_limit_2_25', [2, 25]),
trigger: 'blur'
}
],
datasetGroupName: [
{
required: true,
min: 2,
max: 25,
message: i18n.t('datasource.input_limit_2_25', [2, 25]),
trigger: 'blur'
}
],
datasetGroupPid: [{ required: true, message: i18n.t('chart.select_group'), trigger: 'blur' }],
panelGroupPid: [{ required: true, message: i18n.t('chart.select_group'), trigger: 'blur' }]
},
panelGroupList: [],
datasetGroupList: [],
attachForm: {
appTemplateId: '',
panelGroupPid: null,
panelName: '',
datasetGroupPid: null,
datasetGroupId: null,
datasetGroupName: ''
},
disabled: false,
form: {
configuration: {
@ -402,8 +585,30 @@ export default {
async created() {
await this.datasourceTypes()
this.queryTreeData()
const { id, showModel, type, name } = this.$route.query
let { id, showModel, type, name } = this.$route.query
this.params = this.$route.query
if (this.positionCheck('appMarket')) {
id = this.outerParams.datasourceId
showModel = this.outerParams.showModel
type = this.outerParams.datasourceType
name = this.outerParams.name
this.attachForm.appTemplateId = this.outerParams.appTemplateId
this.attachForm.panelGroupPid = this.outerParams.panelGroupPid
this.attachForm.panelId = this.outerParams.panelId
this.attachForm.panelName = this.outerParams.panelName
this.attachForm.datasetGroupPid = this.outerParams.datasetGroupPid ? this.outerParams.datasetGroupPid : '0'
this.attachForm.datasetGroupId = this.outerParams.datasetGroupId
this.attachForm.datasetGroupName = this.outerParams.datasetGroupName
this.params = {
id: this.outerParams.datasourceId,
showModel: this.outerParams.showModel,
type: this.outerParams.datasourceType,
name: this.outerParams.name,
privileges: this.outerParams.datasourcePrivileges
}
this.getPanelGroupTree()
this.getDatasetGroupTree()
}
if (id) {
await this.getDatasourceDetail(id, showModel)
this.edit(this.params)
@ -420,6 +625,36 @@ export default {
this.disabled = Boolean(id) && showModel === 'show' && !this.canEdit
},
methods: {
normalizer(node) {
// children=null
if (node.children === null || node.children === 'null') {
delete node.children
}
},
getDatasetGroupTree() {
dsGroupTree({ nodeType: 'group', excludedId: this.attachForm.datasetGroupId }).then(res => {
this.datasetGroupList = [{
id: '0',
name: this.$t('dataset.dataset_group'),
label: this.$t('dataset.dataset_group'),
pid: '0',
privileges: 'grant,manage,use',
type: 'group',
children: res.data
}]
})
},
getPanelGroupTree() {
groupTree({ nodeType: 'folder' }).then(res => {
this.panelGroupList = res.data
if (!this.attachForm.panelGroupPid && this.panelGroupList && this.panelGroupList.length > 0) {
this.attachForm.panelGroupPid = this.panelGroupList[0].id
}
})
},
positionCheck(referencePosition) {
return this.referencePosition === referencePosition
},
datasourceTypes() {
return listDatasourceType().then((res) => {
this.dsTypes = res.data || []
@ -569,7 +804,7 @@ export default {
if (
configuration.host === this.form.configuration.host &&
configuration.dataBase ===
this.form.configuration.dataBase &&
this.form.configuration.dataBase &&
configuration.port === this.form.configuration.port
) {
repeat = true
@ -584,7 +819,7 @@ export default {
if (
configuration.host === this.form.configuration.host &&
configuration.dataBase ===
this.form.configuration.dataBase &&
this.form.configuration.dataBase &&
configuration.port === this.form.configuration.port &&
configuration.schema === this.form.configuration.schema
) {
@ -621,7 +856,7 @@ export default {
configuration.schema === this.form.configuration.schema &&
configuration.host === this.form.configuration.host &&
configuration.dataBase ===
this.form.configuration.dataBase &&
this.form.configuration.dataBase &&
configuration.port === this.form.configuration.port
) {
repeat = true
@ -631,7 +866,7 @@ export default {
if (
configuration.host === this.form.configuration.host &&
configuration.dataBase ===
this.form.configuration.dataBase &&
this.form.configuration.dataBase &&
configuration.port === this.form.configuration.port
) {
repeat = true
@ -656,11 +891,20 @@ export default {
if (!status) {
return
}
if (this.positionCheck('appMarket')) {
this.$refs.attachParamsForm.validate(valid => {
if (!valid) {
return false
}
}
)
}
this.$refs.dsForm.validate((valid) => {
if (!valid) {
return false
}
const method = this.formType === 'add' ? addDs : editDs
let method = this.formType === 'add' ? addDs : editDs
const form = JSON.parse(JSON.stringify(this.form))
if (form.type === 'api') {
if (this.form.apiConfiguration.length < 1) {
@ -674,17 +918,29 @@ export default {
} else {
form.configuration = JSON.stringify(form.configuration)
}
const isAppMarket = this.positionCheck('appMarket')
let appApplyForm
if (isAppMarket) {
if (typeof form.desc === 'object') {
form.desc = ''
}
appApplyForm = {
...this.attachForm,
datasourceList: [deepCopy(form)]
}
method = this.formType === 'add' ? appApply : appEdit
}
if (
this.formType === 'modify' &&
this.originConfiguration !== form.configuration
) {
if (repeat) {
if (repeat && !isAppMarket) {
$confirm(
i18n.t('datasource.repeat_datasource_msg') +
'[' +
repeatDsName.join(',') +
'], ' +
i18n.t('datasource.confirm_save'),
'[' +
repeatDsName.join(',') +
'], ' +
i18n.t('datasource.confirm_save'),
() => {
$confirm(i18n.t('datasource.edit_datasource_msg'), () => {
this.method(method, form)
@ -693,27 +949,40 @@ export default {
)
} else {
$confirm(i18n.t('datasource.edit_datasource_msg'), () => {
this.method(method, form)
isAppMarket ? this.appApplyMethod(method, appApplyForm) : this.method(method, form)
})
}
return
}
if (repeat) {
if (repeat && !isAppMarket) {
$confirm(
i18n.t('datasource.repeat_datasource_msg') +
'[' +
repeatDsName.join(',') +
'], ' +
i18n.t('datasource.confirm_save'),
'[' +
repeatDsName.join(',') +
'], ' +
i18n.t('datasource.confirm_save'),
() => {
this.method(method, form)
}
)
} else {
this.method(method, form)
isAppMarket ? this.appApplyMethod(method, appApplyForm) : this.method(method, form)
}
})
},
appApplyMethod(method, form) {
this.formLoading = true
method(form).then((res) => {
this.$success(i18n.t('commons.save_success'))
if (this.formType === 'add') {
this.$router.push({ name: 'panel', params: res.data })
} else {
this.closeDraw()
}
}).finally(() => {
this.formLoading = false
})
},
method(method, form) {
this.formLoading = true
method(form).then((res) => {
@ -767,6 +1036,14 @@ export default {
if (!status) {
return
}
if (this.positionCheck('appMarket')) {
this.$refs.attachParamsForm.validate(valid => {
if (!valid) {
return false
}
}
)
}
this.$refs.dsForm.validate((valid) => {
if (valid) {
const data = JSON.parse(JSON.stringify(this.form))
@ -844,6 +1121,10 @@ export default {
backToList() {
this.$router.push('/datasource/index')
},
closeDraw() {
this.$emit('closeDraw')
},
logOutTips() {
const options = {
title: 'role.tips',
@ -967,21 +1248,47 @@ export default {
}
})
},
handleClick(tab, event) {}
handleClick(tab, event) {
}
}
}
</script>
<style lang="scss" scoped>
.de-ds-form {
width: 100vw;
height: 100vh;
.de-ds-top {
.de-ds-form-app {
width: 100%;
height: 100%;
.de-ds-cont {
display: flex;
width: 100%;
height: calc(100% - 56px);
overflow-y: auto;
overflow-x: hidden;
padding: 12px 24px 24px 24px;
.de-ds-inner {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.w600 {
width: 600px;
height: 100%;
}
}
.de-ds-bottom {
display: flex;
text-align: right;
align-items: center;
justify-content: space-between;
height: 56px;
padding: 12px 24px;
box-shadow: 0px 2px 4px rgba(31, 35, 41, 0.08);
box-shadow: 2px 2px 4px rgba(31, 35, 41, 0.08);
.name {
font-family: 'PingFang SC';
font-style: normal;
@ -990,16 +1297,46 @@ export default {
line-height: 24px;
color: var(--deTextPrimary, #1f2329);
}
i {
cursor: pointer;
}
}
}
.de-ds-form {
width: 100vw;
height: 100vh;
.de-ds-top {
display: flex;
align-items: center;
justify-content: space-between;
height: 56px;
padding: 12px 24px;
box-shadow: 0px 2px 4px rgba(31, 35, 41, 0.08);
.name {
font-family: 'PingFang SC';
font-style: normal;
font-weight: 500;
font-size: 16px;
line-height: 24px;
color: var(--deTextPrimary, #1f2329);
}
i {
cursor: pointer;
}
}
.de-ds-cont {
display: flex;
width: 100%;
height: calc(100% - 56px);
padding: 12px 24px 24px 24px;
background: #f5f6f7;
.de-ds-inner {
width: 100%;
height: 100;
@ -1009,6 +1346,7 @@ export default {
justify-content: center;
overflow-y: auto;
}
.w600 {
width: 600px;
height: 100%;