Merge branch 'dev-v2' into pr@dev-v2_dzz

This commit is contained in:
dataeaseShu 2023-12-25 10:52:42 +08:00
commit bc2f8ac020
32 changed files with 1169 additions and 172 deletions

View File

@ -67,6 +67,8 @@ public class ChartDataManage {
@Resource
private CorePermissionManage corePermissionManage;
public static final String START_END_SEPARATOR = "_START_END_SPLIT";
private static Logger logger = LoggerFactory.getLogger(ChartDataManage.class);
public ChartViewDTO calcData(ChartViewDTO view) throws Exception {
@ -246,7 +248,8 @@ public class ChartDataManage {
List<SqlVariableDetails> sqlVariables = datasetGroupManage.getSqlParams(Arrays.asList(view.getTableId()));
if (CollectionUtil.isNotEmpty(sqlVariables)) {
for (SqlVariableDetails parameter : Optional.ofNullable(request.getParameters()).orElse(new ArrayList<>())) {
if (sqlVariables.stream().map(SqlVariableDetails::getId).collect(Collectors.toList()).contains(parameter.getId())) {
String parameterId = StringUtils.endsWith(parameter.getId(), START_END_SEPARATOR) ? parameter.getId().split(START_END_SEPARATOR)[0] : parameter.getId();
if (sqlVariables.stream().map(SqlVariableDetails::getId).collect(Collectors.toList()).contains(parameterId)) {
hasParameters = true;
}
}

View File

@ -20,6 +20,7 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static io.dataease.chart.manage.ChartDataManage.START_END_SEPARATOR;
import static org.apache.calcite.sql.SqlKind.*;
public class SqlparserUtils {
@ -166,7 +167,11 @@ public class SqlparserUtils {
return "'" + String.join("','", sqlVariableDetails.getValue()) + "'";
} else if (sqlVariableDetails.getOperator().equals("between")) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(sqlVariableDetails.getType().size() > 1 ? (String) sqlVariableDetails.getType().get(1).replace("DD", "dd") : "YYYY");
return simpleDateFormat.format(new Date(Long.parseLong((String) sqlVariableDetails.getValue().get(0))));
if (StringUtils.endsWith(sqlVariableDetails.getId(), START_END_SEPARATOR)) {
return simpleDateFormat.format(new Date(Long.parseLong((String) sqlVariableDetails.getValue().get(1))));
} else {
return simpleDateFormat.format(new Date(Long.parseLong((String) sqlVariableDetails.getValue().get(0))));
}
} else {
return (String) sqlVariableDetails.getValue().get(0);
}

View File

@ -416,9 +416,6 @@ public class ExcelUtils {
}
List<String[]> data = new ArrayList<>(noModelDataListener.getData());
if (isPreview) {
if (data.size() > 100) {
data = data.subList(0, 100);
}
for (int i = 0; i < data.size(); i++) {
for (int j = 0; j < data.get(i).length; j++) {
if (j < fields.size()) {
@ -426,6 +423,9 @@ public class ExcelUtils {
}
}
}
if (data.size() > 100) {
data = data.subList(0, 100);
}
}
for (int i = 0; i < fields.size(); i++) {
@ -472,6 +472,9 @@ public class ExcelUtils {
}
}
}
if (data.size() > 100) {
data = data.subList(0, 100);
}
}
for (int i = 0; i < fields.size(); i++) {
if (StringUtils.isEmpty(fields.get(i).getFieldType())) {

View File

@ -17,4 +17,14 @@ public interface ExtVisualizationTemplateMapper{
List<TemplateManageDTO> findBaseTemplateList();
Long checkCategoryMap(@Param("categoryId") String categoryId);
Long checkRepeatTemplateId(@Param("categoryId") String categoryId, @Param("templateId") String templateId);
void deleteCategoryMapByTemplate(@Param("templateName") String templateName, @Param("templateId") String templateId);
Long checkCategoryTemplateName(@Param("templateName") String templateName,@Param("categories") List<String> categories);
List<String> findTemplateCategories(@Param("templateId") String templateId);
}

View File

@ -3,6 +3,7 @@ package io.dataease.template.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.dataease.api.template.TemplateManageApi;
import io.dataease.api.template.dto.TemplateManageDTO;
import io.dataease.api.template.request.TemplateManageBatchRequest;
import io.dataease.api.template.request.TemplateManageRequest;
import io.dataease.api.template.vo.VisualizationTemplateVO;
import io.dataease.constant.CommonConstants;
@ -56,16 +57,16 @@ public class TemplateManageService implements TemplateManageApi {
request.setWithBlobs("N");
List<TemplateManageDTO> templateList = extTemplateMapper.findTemplateList(request);
if (request.getWithChildren()) {
getTreeChildren(templateList,request.getLeafDvType());
getTreeChildren(templateList, request.getLeafDvType());
}
return templateList;
}
public void getTreeChildren(List<TemplateManageDTO> parentTemplateList,String dvType) {
public void getTreeChildren(List<TemplateManageDTO> parentTemplateList, String dvType) {
Optional.ofNullable(parentTemplateList).ifPresent(parent -> parent.forEach(parentTemplate -> {
List<TemplateManageDTO> panelTemplateDTOChildren = extTemplateMapper.findTemplateList(new TemplateManageRequest(parentTemplate.getId(),dvType));
List<TemplateManageDTO> panelTemplateDTOChildren = extTemplateMapper.findTemplateList(new TemplateManageRequest(parentTemplate.getId(), dvType));
parentTemplate.setChildren(panelTemplateDTOChildren);
getTreeChildren(panelTemplateDTOChildren,dvType);
getTreeChildren(panelTemplateDTOChildren, dvType);
}));
}
@ -96,17 +97,21 @@ public class TemplateManageService implements TemplateManageApi {
DEException.throwException("名称已存在");
}
VisualizationTemplateCategory templateCategory = new VisualizationTemplateCategory();
BeanUtils.copyBean(templateCategory,request);
BeanUtils.copyBean(templateCategory, request);
templateCategoryMapper.insert(templateCategory);
} else {//模板插入 同名的模板进行覆盖(先删除)
// 分类映射删除
extTemplateMapper.deleteCategoryMapByTemplate(request.getName(),null);
// 模版删除
QueryWrapper<VisualizationTemplate> wrapper = new QueryWrapper<>();
wrapper.eq("name",request.getName());
wrapper.eq("name", request.getName());
templateMapper.delete(wrapper);
VisualizationTemplate template = new VisualizationTemplate();
BeanUtils.copyBean(template,request);
BeanUtils.copyBean(template, request);
templateMapper.insert(template);
// 插入分类关系
request.getCategories().forEach(categoryId ->{
request.getCategories().forEach(categoryId -> {
VisualizationTemplateCategoryMap categoryMap = new VisualizationTemplateCategoryMap();
categoryMap.setId(UUID.randomUUID().toString());
categoryMap.setCategoryId(categoryId);
@ -122,16 +127,27 @@ public class TemplateManageService implements TemplateManageApi {
DEException.throwException("名称已存在");
}
VisualizationTemplateCategory templateCategory = new VisualizationTemplateCategory();
BeanUtils.copyBean(templateCategory,request);
BeanUtils.copyBean(templateCategory, request);
templateCategoryMapper.updateById(templateCategory);
}else{
} else {
String nameCheckResult = this.nameCheck(CommonConstants.OPT_TYPE.UPDATE, request.getName(), request.getId());
if (CommonConstants.CHECK_RESULT.EXIST_ALL.equals(nameCheckResult)) {
DEException.throwException("名称已存在");
}
VisualizationTemplate template = new VisualizationTemplate();
BeanUtils.copyBean(template,request);
BeanUtils.copyBean(template, request);
templateMapper.updateById(template);
//更新分类
// 分类映射删除
extTemplateMapper.deleteCategoryMapByTemplate(null,request.getId());
// 插入分类关系
request.getCategories().forEach(categoryId -> {
VisualizationTemplateCategoryMap categoryMap = new VisualizationTemplateCategoryMap();
categoryMap.setId(UUID.randomUUID().toString());
categoryMap.setCategoryId(categoryId);
categoryMap.setTemplateId(request.getId());
categoryMapMapper.insert(categoryMap);
});
}
}
@ -141,14 +157,14 @@ public class TemplateManageService implements TemplateManageApi {
return templateManageDTO;
}
//名称检查
public String nameCheck(String optType, String name,String id) {
//模版名称检查
public String nameCheck(String optType, String name, String id) {
QueryWrapper<VisualizationTemplate> wrapper = new QueryWrapper<>();
if (CommonConstants.OPT_TYPE.INSERT.equals(optType)) {
wrapper.eq("name",name);
wrapper.eq("name", name);
} else if (CommonConstants.OPT_TYPE.UPDATE.equals(optType)) {
wrapper.eq("name",name);
wrapper.ne("id",id);
wrapper.eq("name", name);
wrapper.ne("id", id);
}
List<VisualizationTemplate> templateList = templateMapper.selectList(wrapper);
if (CollectionUtils.isEmpty(templateList)) {
@ -158,14 +174,25 @@ public class TemplateManageService implements TemplateManageApi {
}
}
//名称检查
//分类下模版名称检查
@Override
public String categoryTemplateNameCheck(TemplateManageRequest request) {
Long result = extTemplateMapper.checkCategoryTemplateName(request.getName(), request.getCategories());
if (result == 0) {
return CommonConstants.CHECK_RESULT.NONE;
} else {
return CommonConstants.CHECK_RESULT.EXIST_ALL;
}
}
//分类名称检查
public String categoryNameCheck(String optType, String name, String id) {
QueryWrapper<VisualizationTemplateCategory> wrapper = new QueryWrapper<>();
if (CommonConstants.OPT_TYPE.INSERT.equals(optType)) {
wrapper.eq("name",name);
wrapper.eq("name", name);
} else if (CommonConstants.OPT_TYPE.UPDATE.equals(optType)) {
wrapper.eq("name",name);
wrapper.ne("id",id);
wrapper.eq("name", name);
wrapper.ne("id", id);
}
List<VisualizationTemplateCategory> templateList = templateCategoryMapper.selectList(wrapper);
if (CollectionUtils.isEmpty(templateList)) {
@ -174,31 +201,59 @@ public class TemplateManageService implements TemplateManageApi {
return CommonConstants.CHECK_RESULT.EXIST_ALL;
}
}
@Override
public String nameCheck(TemplateManageRequest request) {
return nameCheck(request.getOptType(), request.getName(), request.getId());
}
@Override
public void delete(String id) {
public void delete(String id, String categoryId) {
Assert.notNull(id, "id cannot be null");
templateMapper.deleteById(id);
Assert.notNull(categoryId, "categoryId cannot be null");
QueryWrapper<VisualizationTemplateCategoryMap> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("template_id", id);
queryWrapper.eq("category_id", categoryId);
categoryMapMapper.delete(queryWrapper);
// 如何是最后一个 则实际模版需要删除
Long result = extTemplateMapper.checkRepeatTemplateId(categoryId, id);
if (result == 0) {
templateMapper.deleteById(id);
}
}
@Override
public void deleteCategory(String id) {
public String deleteCategory(String id) {
Assert.notNull(id, "id cannot be null");
templateCategoryMapper.deleteById(id);
// 该分类下是否有其他分类公用的模版
Long checkResult = extTemplateMapper.checkCategoryMap(id);
if (checkResult == 0) {
templateCategoryMapper.deleteById(id);
QueryWrapper<VisualizationTemplateCategoryMap> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("category_id", id);
categoryMapMapper.delete(queryWrapper);
return "success";
} else {
return "repeat";
}
}
@Override
public VisualizationTemplateVO findOne(String templateId) {
VisualizationTemplate template = templateMapper.selectById(templateId);
if(template != null){
if (template != null) {
VisualizationTemplateVO templateVO = new VisualizationTemplateVO();
BeanUtils.copyBean(templateVO,template);
BeanUtils.copyBean(templateVO, template);
//查找分类
List<String> categories = extTemplateMapper.findTemplateCategories(templateId);
templateVO.setCategories(categories);
return templateVO;
}else{
} else {
return null;
}
}
@Override
public List<TemplateManageDTO> find(TemplateManageRequest request) {
return extTemplateMapper.findTemplateList(request);
@ -208,4 +263,39 @@ public class TemplateManageService implements TemplateManageApi {
public List<TemplateManageDTO> findCategories(TemplateManageRequest request) {
return extTemplateMapper.findCategories(request);
}
@Override
public void batchUpdate(TemplateManageBatchRequest request) {
request.getTemplateIds().forEach(templateId ->{
// 分类映射删除
extTemplateMapper.deleteCategoryMapByTemplate(null,templateId);
// 插入分类关系
request.getCategories().forEach(categoryId -> {
VisualizationTemplateCategoryMap categoryMap = new VisualizationTemplateCategoryMap();
categoryMap.setId(UUID.randomUUID().toString());
categoryMap.setCategoryId(categoryId);
categoryMap.setTemplateId(templateId);
categoryMapMapper.insert(categoryMap);
});
});
}
@Override
public void batchDelete(TemplateManageBatchRequest request) {
request.getTemplateIds().forEach(templateId ->{
request.getCategories().forEach(categoryId -> {
QueryWrapper<VisualizationTemplateCategoryMap> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("template_id", templateId);
queryWrapper.eq("category_id", categoryId);
categoryMapMapper.delete(queryWrapper);
// 如何是最后一个 则实际模版需要删除
Long result = extTemplateMapper.checkRepeatTemplateId(categoryId, templateId);
if (result == 0) {
templateMapper.deleteById(templateId);
}
});
});
}
}

View File

@ -3,24 +3,24 @@
<mapper namespace="io.dataease.template.dao.ext.ExtVisualizationTemplateMapper">
<resultMap id="BaseResultMap" type="io.dataease.api.template.vo.VisualizationTemplateVO">
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="pid" jdbcType="VARCHAR" property="pid" />
<result column="level" jdbcType="INTEGER" property="level" />
<result column="dv_type" jdbcType="VARCHAR" property="dvType" />
<result column="node_type" jdbcType="VARCHAR" property="nodeType" />
<result column="create_by" jdbcType="VARCHAR" property="createBy" />
<result column="create_time" jdbcType="BIGINT" property="createTime" />
<result column="template_type" jdbcType="VARCHAR" property="templateType" />
<result column="snapshot" jdbcType="LONGVARCHAR" property="snapshot" />
<result column="template_style" jdbcType="LONGVARCHAR" property="templateStyle" />
<result column="template_data" jdbcType="LONGVARCHAR" property="templateData" />
<result column="dynamic_data" jdbcType="LONGVARCHAR" property="dynamicData" />
<id column="id" jdbcType="VARCHAR" property="id"/>
<result column="name" jdbcType="VARCHAR" property="name"/>
<result column="pid" jdbcType="VARCHAR" property="pid"/>
<result column="level" jdbcType="INTEGER" property="level"/>
<result column="dv_type" jdbcType="VARCHAR" property="dvType"/>
<result column="node_type" jdbcType="VARCHAR" property="nodeType"/>
<result column="create_by" jdbcType="VARCHAR" property="createBy"/>
<result column="create_time" jdbcType="BIGINT" property="createTime"/>
<result column="template_type" jdbcType="VARCHAR" property="templateType"/>
<result column="snapshot" jdbcType="LONGVARCHAR" property="snapshot"/>
<result column="template_style" jdbcType="LONGVARCHAR" property="templateStyle"/>
<result column="template_data" jdbcType="LONGVARCHAR" property="templateData"/>
<result column="dynamic_data" jdbcType="LONGVARCHAR" property="dynamicData"/>
</resultMap>
<resultMap id="BaseResultMapDTO" type="io.dataease.api.template.dto.TemplateManageDTO"
extends="BaseResultMap">
<result column="recent_use_time" jdbcType="BIGINT" property="recentUseTime" />
<result column="recent_use_time" jdbcType="BIGINT" property="recentUseTime"/>
<result column="label" jdbcType="VARCHAR" property="label"/>
<result column="childrenCount" jdbcType="VARCHAR" property="childrenCount"/>
</resultMap>
@ -33,57 +33,55 @@
</resultMap>
<sql id="Base_Column_List">
vt.id, vt.`name`, vt.pid, vt.`level`,vt.`dv_type`, vt.node_type, vt.create_by, vt.create_time, vt.template_type, vt.snapshot
</sql>
vt
.
id
, vt.`name`, vt.pid, vt.`level`,vt.`dv_type`, vt.node_type, vt.create_by, vt.create_time, vt.template_type, vt.snapshot
</sql>
<sql id="Blob_Column_List">
,vt.template_style, vt.template_data, vt.dynamic_data
</sql>
,vt.template_style, vt.template_data, vt.dynamic_data
</sql>
<select id="findBaseTemplateList" resultMap="BaseResultMapCollectionDTO">
SELECT
vt.id,
vt.`name`,
vt.pid,
vt.`level`,
vt.`dv_type`,
vt.node_type,
vt.create_by,
vt.create_time,
vt.template_type,
vt.SNAPSHOT,
vtcm.category_id,
cor.time as 'recent_use_time'
FROM
visualization_template vt
LEFT JOIN visualization_template_category_map vtcm ON vt.id = vtcm.template_id
left JOIN core_opt_recent cor on cor.resource_type=6 and vt.id= cor.resource_name
ORDER BY
vt.create_time DESC
SELECT vt.id,
vt.`name`,
vt.pid,
vt.`level`,
vt.`dv_type`,
vt.node_type,
vt.create_by,
vt.create_time,
vt.template_type,
vt.SNAPSHOT,
vtcm.category_id,
cor.time as 'recent_use_time'
FROM visualization_template vt
LEFT JOIN visualization_template_category_map vtcm ON vt.id = vtcm.template_id
left JOIN core_opt_recent cor on cor.resource_type = 6 and vt.id = cor.resource_name
ORDER BY vt.create_time DESC
</select>
<select id="findCategories" resultMap="BaseResultMapDTO">
SELECT
id,
`name`,
`name` as lable,
pid,
`level`,
`dv_type`,
node_type,
create_by,
create_time,
template_type,
SNAPSHOT
FROM
visualization_template_category
SELECT id,
`name`,
`name` as lable,
pid,
`level`,
`dv_type`,
node_type,
create_by,
create_time,
template_type,
SNAPSHOT
FROM visualization_template_category
order by visualization_template_category.create_time desc
</select>
<select id="findTemplateList" resultMap="BaseResultMapDTO">
SELECT
<include refid="Base_Column_List" />
<include refid="Base_Column_List"/>
<if test='withBlobs == "Y"'>
<include refid="Blob_Column_List" />
<include refid="Blob_Column_List"/>
</if>
FROM
visualization_template vt
@ -91,4 +89,51 @@
where vtcm.category_id=#{categoryId}
order by vt.create_time desc
</select>
<select id="checkCategoryMap" resultType="Long">
SELECT count(1)
FROM visualization_template_category_map cm
WHERE cm.category_id = #{categoryId}
</select>
<select id="checkRepeatTemplateId" resultType="Long">
SELECT count(1)
FROM visualization_template_category_map cm
where cm.category_id != #{categoryId}
and cm.template_id =#{templateId}
</select>
<select id="checkCategoryTemplateName" resultType="Long">
SELECT
count(1)
FROM
visualization_template vt
LEFT JOIN visualization_template_category_map vtcm ON vt.id = vtcm.template_id
WHERE
vt.NAME = #{templateName}
AND vtcm.category_id IN
<foreach collection="categories" item="categoryId" index="index" open="(" close=")" separator=",">
#{categoryId}
</foreach>
</select>
<delete id="deleteCategoryMapByTemplate">
delete from visualization_template_category_map tcm
<where>
<if test="templateName">
tcm.template_id in (
select id from visualization_template vt where vt.name = #{templateName})
</if>
<if test="templateId">
and tcm.template_id = #{templateId}
</if>
</where>
</delete>
<select id="findTemplateCategories" resultType="String">
select category_id
from visualization_template_category_map
where template_id = #{templateId}
</select>
</mapper>

View File

@ -7,9 +7,9 @@ export function save(data) {
loading: true
})
}
export function templateDelete(id) {
export function templateDelete(id, categoryId) {
return request.post({
url: '/templateManage/delete/' + id
url: '/templateManage/delete/' + id + '/' + categoryId
})
}
@ -54,3 +54,24 @@ export function nameCheck(data) {
data: data
})
}
export function categoryTemplateNameCheck(data) {
return request.post({
url: '/templateManage/categoryTemplateNameCheck',
data: data
})
}
export function batchDelete(data) {
return request.post({
url: '/templateManage/batchDelete',
data: data
})
}
export function batchUpdate(data) {
return request.post({
url: '/templateManage/batchUpdate',
data: data
})
}

View File

@ -49,6 +49,7 @@
:style="getPointStyle(item)"
@mousedown="handleMouseDownOnPoint(item, $event)"
></div>
<div class="shape-shadow" v-show="batchOptStatus" @mousedown="batchSelected"></div>
<template v-if="boardMoveActive">
<div
v-show="!element.editing"
@ -517,6 +518,15 @@ const selectCurComponent = e => {
}
}
const batchSelected = e => {
if (dvMainStore.batchOptStatus) {
componentEditBarRef.value.batchOptCheckOut()
e.stopPropagation()
e.preventDefault()
return
}
}
const handleMouseDownOnPoint = (point, e) => {
dashboardActive.value && emit('onStartResize', e)
dvMainStore.setInEditorStatus(true)
@ -833,6 +843,15 @@ onMounted(() => {
position: absolute;
}
.shape-shadow {
top: 0;
left: 0;
width: 100%;
height: 100%;
position: absolute;
background-size: 100% 100% !important;
}
.shape-inner {
width: 100%;
height: 100%;

View File

@ -10,6 +10,8 @@ import { useEmitt } from '@/hooks/web/useEmitt'
import AboutPage from '@/views/about/index.vue'
import LangSelector from './LangSelector.vue'
import router from '@/router'
import { useCache } from '@/hooks/web/useCache'
const { wsCache } = useCache()
const userStore = useUserStoreWithOut()
const { t } = useI18n()
@ -30,6 +32,16 @@ const linkLoaded = items => {
items.forEach(item => linkList.value.push(item))
linkList.value.sort(compare('id'))
}
const xpackLinkLoaded = items => {
let len = linkList.value.length
while (len--) {
if (linkList.value[len]?.id === 2 && linkList.value[len]?.link === '/modify-pwd/index') {
linkList.value.splice(len, 1)
}
}
items.forEach(item => linkList.value.push(item))
linkList.value.sort(compare('id'))
}
const compare = (property: string) => {
return (a, b) => a[property] - b[property]
@ -68,6 +80,10 @@ const openPopover = () => {
if (uid.value === '1') {
linkLoaded([{ id: 4, link: '/sys-setting/parameter', label: t('commons.system_setting') }])
const desktop = wsCache.get('app.desktop')
if (!desktop) {
linkLoaded([{ id: 2, link: '/modify-pwd/index', label: t('user.change_password') }])
}
}
</script>
@ -138,7 +154,7 @@ if (uid.value === '1') {
</el-popover>
<AboutPage />
<XpackComponent jsname="dWNlbnRlci1oYW5kbGVy" @loaded="linkLoaded" />
<XpackComponent jsname="dWNlbnRlci1oYW5kbGVy" @loaded="xpackLinkLoaded" />
</template>
<style lang="less">

View File

@ -1354,7 +1354,7 @@ export default {
pls_input_filename: '请输入文件名称',
calc_tips: {
tip1: '表达式语法请遵循calcite语法',
tip2: '数据集中不支持聚合运算',
tip2: '聚合运算仅能在图表中生效',
tip3: '引用字段以 "[" 开始 "]" 结束',
tip4: '请勿修改引用内容否则将引用失败',
tip5: '若输入与引用字段相同格式的内容将被当作引用字段处理',

View File

@ -96,6 +96,22 @@ export const routes: AppRouteRecordRaw[] = [
hidden: true,
meta: {},
component: () => import('@/custom-component/rich-text/DeRichTextView.vue')
},
{
path: '/modify-pwd',
name: 'modify-pwd',
hidden: true,
meta: {},
component: () => import('@/layout/index.vue'),
children: [
{
path: 'index',
name: 'mpi',
hidden: true,
component: () => import('@/views/system/modify-pwd/index.vue'),
meta: { hidden: true }
}
]
}
]

View File

@ -554,6 +554,7 @@ onMounted(() => {
<el-checkbox
:disabled="!formatterEditable"
v-model="curSeriesFormatter.show"
:effect="themes"
size="small"
label="quota"
@change="changeTooltipAttr('seriesTooltipFormatter', true)"

View File

@ -0,0 +1,140 @@
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { cloneDeep } from 'lodash-es'
import request from '@/config/axios'
import { rsaEncryp } from '@/utils/encryption'
import { ElMessage } from 'element-plus-secondary'
import { logoutHandler } from '@/utils/logout'
import { CustomPassword } from '@/components/custom-password'
const { t } = useI18n()
const defaultForm = {
pwd: '',
newPwd: '',
confirm: ''
}
const pwdForm = reactive(cloneDeep(defaultForm))
const validatePwd = (_: any, value: any, callback: any) => {
if (value === pwdForm.pwd) {
callback(new Error('新旧密码不能相同'))
}
const pattern =
/^.*(?=.{6,20})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[~!@#$%^&*()_+\-\={}|":<>?`[\];',.\/])[a-zA-Z0-9~!@#$%^&*()_+\-\={}|":<>?`[\];',.\/]*$/
const regep = new RegExp(pattern)
if (!regep.test(value)) {
const msg = t('user.pwd_pattern_error')
callback(new Error(msg))
} else {
callback()
}
}
const validateConfirmPwd = (_: any, value: any, callback: any) => {
if (value !== pwdForm.newPwd) {
callback(new Error('两次输入的密码不一致'))
} else {
callback()
}
}
const rule = {
pwd: [
{
required: true,
message: t('common.require'),
trigger: 'blur'
},
{
min: 6,
max: 20,
message: t('commons.input_limit', [6, 20]),
trigger: 'blur'
}
],
newPwd: [
{
required: true,
message: t('common.require'),
trigger: 'blur'
},
{ validator: validatePwd, trigger: 'blur' }
],
confirm: [
{
required: true,
message: t('common.require'),
trigger: 'blur'
},
{
min: 6,
max: 20,
message: t('commons.input_limit', [6, 20]),
trigger: 'blur'
},
{ validator: validateConfirmPwd, trigger: 'blur' }
]
}
const updatePwdForm = ref()
const save = () => {
updatePwdForm.value.validate(val => {
if (val) {
const pwd = rsaEncryp(pwdForm.pwd)
const newPwd = rsaEncryp(pwdForm.newPwd)
request.post({ url: '/user/modifyPwd', data: { pwd, newPwd } }).then(() => {
ElMessage.success('修改成功,请重新登录')
logoutHandler()
})
}
})
}
</script>
<template>
<el-form
ref="updatePwdForm"
require-asterisk-position="right"
:model="pwdForm"
:rules="rule"
class="mt16"
label-width="80px"
label-position="top"
>
<el-form-item label="原始密码" prop="pwd">
<CustomPassword
v-model="pwdForm.pwd"
show-password
type="password"
placeholder="请输入原始密码"
/>
</el-form-item>
<el-form-item label="新密码" prop="newPwd">
<CustomPassword
v-model="pwdForm.newPwd"
show-password
type="password"
placeholder="请输入新密码"
/>
</el-form-item>
<el-form-item label="确认密码" prop="confirm">
<CustomPassword
v-model="pwdForm.confirm"
show-password
type="password"
placeholder="请输入确认密码"
/>
</el-form-item>
<el-button style="margin-top: 12px" @click="save" type="primary">
{{ t('common.save') }}
</el-button>
</el-form>
</template>
<style lang="less" scoped>
.mt16 {
margin-top: 16px;
}
</style>

View File

@ -0,0 +1,198 @@
<template>
<div class="user-center flex-align-center">
<div class="user-center-container">
<div class="user-tabs">
<div class="tabs-title flex-align-center">用户中心</div>
<el-divider />
<div class="list-item_primary active">
{{ t('user.change_password') }}
</div>
</div>
<div class="user-info">
<div class="base-info">
<div class="info-title flex-align-center">
<span class="title">
{{ t('user.change_password') }}
</span>
</div>
<update-pwd />
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import UpdatePwd from './UpdatePwd.vue'
import { useI18n } from '@/hooks/web/useI18n'
const { t } = useI18n()
</script>
<style lang="less" scoped>
.user-center {
width: 100%;
flex-direction: column;
padding-top: 24px;
.user-center-container {
display: flex;
font-family: PingFang SC;
font-style: normal;
}
.user-tabs {
width: 200px;
height: 201px;
border-radius: 4px;
background: #fff;
padding: 16px;
.list-item_primary {
padding: 9px 8px;
}
.ed-divider {
margin: 4px 0;
border-color: rgba(31, 35, 41, 0.15);
}
.tabs-title {
padding-left: 8px;
color: #8d9199;
font-family: PingFang SC;
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: 22px;
height: 40px;
}
}
.user-info {
margin-left: 16px;
width: 864px;
height: 326px;
.base-info {
& + .base-info {
margin-top: 12px;
.bind-info {
margin-top: 16px;
display: flex;
align-items: center;
width: 100%;
padding: 16px 24px 16px 24px;
border-radius: 4px;
border: 1px solid #dee0e3;
.bind {
font-size: 48px;
margin-right: 12px;
}
.info {
font-family: PingFang SC;
font-style: normal;
font-weight: 400;
width: 80%;
.name {
color: #1f2329;
font-size: 16px;
line-height: 24px;
font-weight: 500;
width: 100%;
}
.tip {
color: #646a73;
font-size: 14px;
line-height: 22px;
max-width: 600px;
white-space: pre-wrap;
}
}
.delete {
margin-left: auto;
}
}
}
padding: 20px 24px 24px 24px;
border-radius: 4px;
background: #fff;
.role {
& + .role {
margin-left: 4px;
}
display: inline-flex;
height: 20px;
padding: 0 6px;
align-items: center;
font-size: 12px;
color: #2b5fd9;
border-radius: 2px;
background: rgba(51, 112, 255, 0.2);
}
.info-title {
.ed-button {
margin-left: auto;
}
.title {
color: #1f2329;
font-size: 16px;
font-weight: 500;
line-height: 24px;
}
}
}
}
.base-info-item {
margin-top: 16px;
font-size: 14px;
font-weight: 400;
line-height: 22px;
width: 100%;
.label {
color: #646a73;
}
.value {
margin-top: 4px;
color: #1f2329;
}
}
.mr12 {
margin-top: 12px;
}
}
</style>
<style lang="less">
.qr-code-dialog {
font-family: PingFang SC;
font-style: normal;
.ed-dialog__body {
display: flex;
flex-direction: column;
align-items: center;
}
.qr-code-img {
margin-top: 16px;
display: inline-block;
padding: 8px 12px;
img {
width: 184px;
height: 184px;
}
border-radius: 8px;
border: 1px solid #bbbfc4;
}
.refresh-login {
margin: 0 auto;
margin-top: 9px;
color: #646a73;
font-size: 14px;
font-weight: 400;
line-height: 22px;
.ed-icon {
margin-left: 4px;
}
}
}
</style>

View File

@ -0,0 +1,86 @@
<template>
<div class="template-import">
<el-form
ref="templateImportForm"
class="de-form-item"
:model="state.templateInfo"
:rules="state.templateInfoRules"
label-position="top"
>
<el-form-item :label="'选择分类'" prop="categories" style="margin-top: 16px">
<el-select v-model="state.templateInfo.categories" multiple style="width: 100%">
<el-option
v-for="option in templateCategories"
:key="option.id"
:label="option.name"
:value="option.id"
/>
</el-select>
</el-form-item>
</el-form>
<el-row> </el-row>
<el-row class="de-root-class">
<el-button secondary @click="cancel()">{{ t('commons.cancel') }}</el-button>
<el-button type="primary" @click="saveChange()">{{ t('commons.confirm') }}</el-button>
</el-row>
</div>
</template>
<script lang="ts" setup>
import { onMounted, reactive, ref } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { batchUpdate } from '@/api/template'
const emits = defineEmits(['closeBatchEditTemplateDialog', 'refresh'])
const { t } = useI18n()
const props = defineProps({
templateCategories: {
type: Array,
required: true
},
templateIds: {
type: Array,
required: true
}
})
const state = reactive({
templateInfo: {
categories: []
},
categories: [],
templateInfoRules: {
categories: [
{
required: true,
message: t('commons.input_content'),
trigger: 'change'
}
]
}
})
onMounted(() => {
// showCurrentTemplate(props.pid)
})
const cancel = () => {
emits('closeBatchEditTemplateDialog')
}
const saveChange = () => {
const params = {
templateIds: props.templateIds,
categories: state.templateInfo.categories
}
batchUpdate(params).then(rsp => {
emits('refresh')
emits('closeBatchEditTemplateDialog')
})
}
</script>
<style scoped lang="less">
.de-root-class {
justify-content: flex-end;
}
</style>

View File

@ -23,11 +23,7 @@
/>
</div>
</el-form-item>
<el-row
v-show="!!state.importTemplateInfo.snapshot"
class="preview"
:style="classBackground"
/>
<el-row v-show="!!state.templateInfo.snapshot" class="preview" :style="classBackground" />
<el-form-item :label="'选择分类'" prop="categories" style="margin-top: 16px">
<el-select v-model="state.templateInfo.categories" multiple style="width: 100%">
<el-option
@ -36,6 +32,16 @@
:label="option.name"
:value="option.id"
/>
<div class="custom-option-line"></div>
<div>
<el-button
@click="doAddCategory"
icon="Plus"
text
style="width: 100%; justify-content: flex-start"
>添加分类</el-button
>
</div>
</el-select>
</el-form-item>
</el-form>
@ -48,12 +54,12 @@
</template>
<script lang="ts" setup>
import { save, nameCheck, find } from '@/api/template'
import { save, nameCheck, find, findOne, categoryTemplateNameCheck } from '@/api/template'
import { computed, onMounted, reactive, ref } from 'vue'
import { imgUrlTrans } from '@/utils/imgUtils'
import { ElMessage } from 'element-plus-secondary'
import { ElMessage, ElMessageBox } from 'element-plus-secondary'
import { useI18n } from '@/hooks/web/useI18n'
const emits = defineEmits(['closeEditTemplateDialog', 'refresh'])
const emits = defineEmits(['closeEditTemplateDialog', 'refresh', 'addCategoryInfo'])
const { t } = useI18n()
const filesRef = ref(null)
const props = defineProps({
@ -64,6 +70,15 @@ const props = defineProps({
templateCategories: {
type: Array,
required: true
},
optType: {
type: String,
required: true,
default: 'insert'
},
templateId: {
type: String,
required: false
}
})
@ -105,9 +120,9 @@ const state = reactive({
})
const classBackground = computed(() => {
if (state.importTemplateInfo.snapshot) {
if (state.templateInfo.snapshot) {
return {
background: `url(${imgUrlTrans(state.importTemplateInfo.snapshot)}) no-repeat`
background: `url(${imgUrlTrans(state.templateInfo.snapshot)}) no-repeat`
}
} else {
return {}
@ -119,6 +134,7 @@ const showCurrentTemplate = pid => {
state.nameList = response.data
})
}
const cancel = () => {
emits('closeEditTemplateDialog')
}
@ -138,40 +154,63 @@ const saveTemplate = () => {
return false
}
if (props.optType === 'insert') {
importTemplate()
} else {
editTemplate()
}
}
const editTemplate = () => {
const nameCheckRequest = {
pid: state.templateInfo.pid,
name: state.templateInfo.name,
categories: state.templateInfo.categories,
optType: 'insert'
optType: props.optType
}
//
nameCheck(nameCheckRequest).then(response => {
if (response.data.indexOf('exist') > -1) {
ElMessage.warning(t('当前模版名称已经存在'))
// const options = {
// title: 'commons.prompt',
// content: 'system_parameter_setting.to_overwrite_them',
// type: 'primary',
// cb: () =>
// save(state.templateInfo).then(response => {
// ElMessage.success('')
// emits('refresh')
// emits('closeEditTemplateDialog')
// }),
// confirmButtonText: t('template.override')
// }
// handlerConfirm(options)
} else {
save(state.templateInfo).then(response => {
ElMessage.success(t('导入成功'))
emits('refresh')
emits('closeEditTemplateDialog')
})
}
save(state.templateInfo).then(response => {
ElMessage.success(t('编辑成功'))
emits('refresh')
emits('closeEditTemplateDialog')
})
})
}
const handlerConfirm = option => {
// do handlerConfirm
const importTemplate = () => {
const nameCheckRequest = {
pid: state.templateInfo.pid,
name: state.templateInfo.name,
categories: state.templateInfo.categories,
optType: props.optType
}
categoryTemplateNameCheck(nameCheckRequest).then(response => {
if (response.data.indexOf('exist') > -1) {
ElMessageBox.confirm('提示?', {
tip: '当前分类存在相同模版名称,是否覆盖?',
confirmButtonType: 'danger',
type: 'warning',
autofocus: false,
showClose: false
}).then(() => {
save(state.templateInfo).then(response => {
ElMessage.success(t('覆盖成功'))
emits('refresh')
emits('closeEditTemplateDialog')
})
})
} else {
//
nameCheck(nameCheckRequest).then(response => {
save(state.templateInfo).then(response => {
ElMessage.success(t('导入成功'))
emits('refresh')
emits('closeEditTemplateDialog')
})
})
}
})
}
const handleFileChange = e => {
@ -195,9 +234,19 @@ const goFile = () => {
filesRef.value.click()
}
const doAddCategory = () => {
emits('addCategoryInfo')
}
onMounted(() => {
showCurrentTemplate(props.pid)
// showCurrentTemplate(props.pid)
})
if (props.templateId) {
findOne(props.templateId).then(rsp => {
state.templateInfo = rsp.data
})
}
</script>
<style scoped lang="less">
@ -245,4 +294,11 @@ onMounted(() => {
flex: 1;
}
}
.custom-option-line {
width: calc(100% -8px);
margin: 4px;
height: 1px;
background-color: rgba(31, 35, 41, 0.15);
}
</style>

View File

@ -1,19 +1,15 @@
<template>
<div :style="classBackground" class="de-card-model">
<div
:style="classBackground"
class="de-card-model"
:class="{ 'de-card-model-active': batchState, 'de-card-model-checked': model.checked }"
>
<el-checkbox class="custom-item-checkbox" v-model="model.checked" />
<div class="card-img-model" :style="classImg">
<img :src="imgUrlTrans(model.snapshot)" alt="" />
</div>
<div class="card-info">
<div style="display: flex; align-items: center; width: calc(100% - 24px)">
<el-tooltip class="item" effect="dark" :content="dvTypeName" placement="top">
<el-icon style="font-size: 18px" v-if="model.dvType === 'dashboard'">
<Icon name="dv-dashboard-spine"></Icon>
</el-icon>
<el-icon class="icon-screen-new" style="font-size: 18px" v-else>
<Icon name="icon_operation-analysis_outlined"></Icon>
</el-icon>
</el-tooltip>
<el-tooltip class="item" effect="dark" :content="model.name" placement="top">
<span class="de-model-text">{{ model.name }}</span>
</el-tooltip>
@ -24,7 +20,7 @@
<template #dropdown>
<el-dropdown-menu class="de-card-dropdown">
<slot>
<el-dropdown-item command="rename">
<el-dropdown-item command="templateEdit">
<el-icon><EditPen /></el-icon>
编辑
</el-dropdown-item>
@ -42,7 +38,7 @@
<script setup lang="ts">
import { imgUrlTrans } from '@/utils/imgUtils'
import { computed } from 'vue'
import { computed, toRefs } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
const { t } = useI18n()
const emits = defineEmits(['command'])
@ -53,9 +49,14 @@ const props = defineProps({
},
width: {
type: Number
},
batchState: {
type: Boolean
}
})
const { model, width, batchState } = toRefs(props)
const dvTypeName = computed(() => {
return props.model.dvType === 'dashboard' ? '仪表板' : '数据大屏'
})
@ -79,13 +80,28 @@ const handleCommand = key => {
</script>
<style lang="less">
.de-card-model-checked {
border: 1px solid #3370ff !important;
}
.de-card-model-active {
.custom-item-checkbox {
display: inline !important;
}
}
.de-card-model {
box-sizing: border-box;
position: relative;
background: #ffffff;
border: 1px solid var(--deCardStrokeColor, #dee0e3);
border: 1px solid #dee0e3;
border-radius: 4px;
margin: 0 24px 25px 0;
overflow: hidden;
.custom-item-checkbox {
position: absolute;
display: none;
right: 8px;
top: 8px;
}
.card-img-model {
border-bottom: 1px solid var(--deCardStrokeColor, #dee0e3);
height: 144px;
@ -106,7 +122,7 @@ const handleCommand = key => {
display: flex;
align-items: center;
justify-content: space-between;
padding: 4px 12px 9px 12px;
padding: 4px 12px 9px 0px;
box-sizing: border-box;
.el-icon-more {
@ -147,6 +163,12 @@ const handleCommand = key => {
}
}
.de-card-model:hover {
.custom-item-checkbox {
display: inline;
}
}
.de-card-model:hover {
box-shadow: 0px 6px 24px rgba(31, 35, 41, 0.08);
}

View File

@ -105,7 +105,7 @@ const add = () => {
}
const categoryDelete = template => {
ElMessageBox.confirm('确定删除该分类吗?', {
tip: '删除后,该分类下的所有模版也将删除。',
tip: '',
confirmButtonType: 'danger',
type: 'warning',
autofocus: false,

View File

@ -62,10 +62,22 @@
v-for="item in currentTemplateShowListComputed"
:key="item.id"
:width="state.templateCurWidth"
:batch-state="batchState > 0"
:model="item"
@command="key => handleCommand(key, item)"
/>
</div>
<div v-show="batchState" class="batch-opt-area">
<el-button @click="batchUpdate" type="danger" plain style="margin-left: 24px"
>修改分类</el-button
>
<el-button @click="batchPreDelete" type="danger" plain>批量删除</el-button>
<span style="margin-left: 24px; font-size: 14px">已选 {{ batchState }} </span>
<el-button @click="batchFullSelect" style="margin-left: 16px" text
>全选 {{ currentTemplateShowListComputed.length }} </el-button
>
<el-button @click="batchClear" text>清空</el-button>
</div>
</div>
</div>
</div>
@ -74,16 +86,17 @@
v-model="state.editTemplate"
append-to-body
class="de-dialog-form"
width="600px"
width="420px"
>
<el-form
ref="templateEditFormRef"
label-position="top"
class="de-form-item"
:model="state.templateEditForm"
:rules="state.templateEditFormRules"
>
<el-form-item :label="state.dialogTitleLabel" prop="name">
<el-input v-model="state.templateEditForm.name" />
<el-input :placeholder="'请输入分类名称'" v-model="state.templateEditForm.name" />
</el-form-item>
</el-form>
<template #footer>
@ -100,28 +113,77 @@
:title="state.templateDialog.title"
v-model="state.templateDialog.visible"
:show-close="true"
:destroy-on-close="true"
class="de-dialog-form"
width="600px"
>
<de-template-import
v-if="state.templateDialog.visible"
:pid="state.templateDialog.pid"
:template-id="state.templateDialog.templateId"
:opt-type="state.templateDialog.optType"
:template-categories="state.templateCategories"
@addCategoryInfo="showTemplateEditDialog('new', null)"
@refresh="showCurrentTemplate(state.currentTemplateId, state.currentTemplateLabel)"
@closeEditTemplateDialog="closeEditTemplateDialog"
/>
</el-dialog>
<!--导入templateDialog-->
<el-dialog
:title="state.templateDialog.title"
v-model="state.templateDialog.visible"
:show-close="true"
:destroy-on-close="true"
class="de-dialog-form"
width="600px"
>
<de-template-import
v-if="state.templateDialog.visible"
:pid="state.templateDialog.pid"
:template-id="state.templateDialog.templateId"
:opt-type="state.templateDialog.optType"
:template-categories="state.templateCategories"
@refresh="showCurrentTemplate(state.currentTemplateId, state.currentTemplateLabel)"
@closeEditTemplateDialog="closeEditTemplateDialog"
/>
</el-dialog>
<!--导入templateDialog-->
<el-dialog
:title="'修改分类'"
v-model="state.batchOptDialogShow"
:show-close="true"
:destroy-on-close="true"
class="de-dialog-form"
width="600px"
>
<de-category-change
v-if="state.batchOptDialogShow"
:template-ids="batchTemplateIds"
:template-categories="state.templateCategories"
@refresh="showCurrentTemplate(state.currentTemplateId, state.currentTemplateLabel)"
@closeBatchEditTemplateDialog="closeBatchOptDialog"
></de-category-change>
</el-dialog>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { save, templateDelete, find, findCategories, deleteCategory } from '@/api/template'
import {
save,
templateDelete,
find,
findCategories,
deleteCategory,
batchDelete
} from '@/api/template'
import elementResizeDetectorMaker from 'element-resize-detector'
import { computed, nextTick, onMounted, reactive, ref } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { ElMessage } from 'element-plus-secondary'
import { ElMessage, ElMessageBox } from 'element-plus-secondary'
import DeTemplateList from '@/views/template/component/DeTemplateList.vue'
const { t } = useI18n()
const templateEditFormRef = ref(null)
@ -129,6 +191,7 @@ const templateListRef = ref(null)
import NoneImage from '@/assets/none.png'
import DeTemplateImport from '@/views/template/component/DeTemplateImport.vue'
import DeTemplateItem from '@/views/template/component/DeTemplateItem.vue'
import DeCategoryChange from '@/views/template/component/DeCategoryChange.vue'
const roleValidator = (rule, value, callback) => {
if (nameRepeat(value)) {
@ -148,6 +211,8 @@ const roleValidator = (rule, value, callback) => {
}
const state = reactive({
batchOptDialogShow: false,
batchOptList: [],
templateFilterText: '',
showShare: false,
currentTemplateShowList: [],
@ -183,11 +248,54 @@ const state = reactive({
templateDialog: {
title: t('visualization.import_template'),
visible: false,
templateId: null,
optType: 'insert',
pid: '',
categories: []
}
})
const batchUpdate = () => {
state.batchOptDialogShow = true
}
const batchPreDelete = () => {
ElMessageBox.confirm(`确定删除${batchState.value}个模板吗?`, {
confirmButtonType: 'danger',
type: 'warning',
autofocus: false,
showClose: false
}).then(() => {
const params = {
templateIds: batchTemplateIds.value,
categories: [state.currentTemplateId]
}
batchDelete(params).then(rsp => {
showCurrentTemplate(state.currentTemplateId, state.currentTemplateLabel)
})
})
}
const batchFullSelect = () => {
currentTemplateShowListComputed.value.forEach(item => {
item.checked = true
})
}
const batchClear = () => {
currentTemplateShowListComputed.value.forEach(item => {
item.checked = false
})
}
const batchState = computed(() => {
return currentTemplateShowListComputed.value.filter(ele => ele.checked).length
})
const batchTemplateIds = computed(() => {
return currentTemplateShowListComputed.value.filter(ele => ele.checked).map(item => item.id)
})
const currentTemplateShowListComputed = computed(() => {
if (!state.templateFilterText) return [...state.currentTemplateShowList]
return state.currentTemplateShowList.filter(ele =>
@ -220,7 +328,7 @@ const nameRepeat = value => {
const handleCommand = (key, data) => {
switch (key) {
case 'rename':
case 'templateEdit':
templateEdit(data)
break
case 'delete':
@ -256,24 +364,44 @@ const showCurrentTemplate = (pid, label) => {
const categoryDelete = id => {
if (id) {
deleteCategory(id).then(response => {
ElMessage({
message: t('commons.delete_success'),
type: 'success',
showClose: true
})
getTree()
if (response.data === 'success') {
ElMessage({
message: t('commons.delete_success'),
type: 'success',
showClose: true
})
getTree()
} else {
ElMessageBox.confirm('无法删除分类?', {
tip: '请移除该分类下所有模版再进行删除分类操作',
confirmButtonText: '知道了',
confirmButtonType: 'default',
showCancelButton: false,
type: 'warning',
autofocus: false,
showClose: false
})
}
})
}
}
const templateDeleteInfo = id => {
if (id) {
templateDelete(id).then(response => {
ElMessage({
message: t('commons.delete_success'),
type: 'success',
showClose: true
ElMessageBox.confirm('确定删除该模版吗?', {
tip: '',
confirmButtonType: 'danger',
type: 'warning',
autofocus: false,
showClose: false
}).then(() => {
templateDelete(id, state.currentTemplateId).then(response => {
ElMessage({
message: t('commons.delete_success'),
type: 'success',
showClose: true
})
showCurrentTemplate(state.currentTemplateId, state.currentTemplateLabel)
})
showCurrentTemplate(state.currentTemplateId, state.currentTemplateLabel)
})
}
}
@ -283,7 +411,7 @@ const showTemplateEditDialog = (type, templateInfo) => {
state.formType = type
if (type === 'edit') {
state.templateEditForm = JSON.parse(JSON.stringify(templateInfo))
state.dialogTitle = state.templateEditForm['nodeType'] === 'folder' ? '编辑分类' : '编辑模板'
state.dialogTitle = state.templateEditForm['nodeType'] === 'folder' ? '重命名' : '编辑模板'
state.originName = state.templateEditForm['label']
} else {
state.dialogTitle = t('visualization.add_category')
@ -299,7 +427,10 @@ const showTemplateEditDialog = (type, templateInfo) => {
}
const templateEdit = templateInfo => {
showTemplateEditDialog('edit', templateInfo)
state.templateDialog.visible = true
state.templateDialog.title = '编辑模版'
state.templateDialog.optType = 'update'
state.templateDialog.templateId = templateInfo.id
}
const categoryClick = params => {
@ -356,12 +487,18 @@ const showFirst = () => {
}
}
const closeBatchOptDialog = () => {
state.batchOptDialogShow = false
}
const closeEditTemplateDialog = () => {
state.templateDialog.visible = false
}
const templateImport = pid => {
state.templateDialog.visible = true
state.templateDialog.templateId = null
state.templateDialog.optType = 'insert'
state.templateDialog.pid = pid
}
@ -404,7 +541,7 @@ onMounted(() => {
flex: 1;
overflow: hidden;
background: rgba(239, 240, 241, 1);
position: relative;
.template-box {
display: flex;
flex-wrap: wrap;
@ -430,6 +567,15 @@ onMounted(() => {
background: #fff;
border-bottom: 1px solid rgba(31, 35, 41, 0.15);
}
.batch-opt-area {
background-color: #ffffff;
position: absolute;
display: flex;
align-items: center;
height: 48px;
width: 100%;
bottom: 0;
}
}
}

@ -1 +1 @@
Subproject commit dfef649cb7d7d2490891ba7acb0ecfb342673a7b
Subproject commit 08ae0222ed43c0b19caab179e0ef5a96c4a69f94

View File

@ -4,7 +4,6 @@ target=$2
DE_BASE=/opt
DE_RUNNING_BASE=${DE_BASE}/dataease2.0
DE_PORT=8100
need_init_apisix=false
compose_files="-f docker-compose.yml"
compose_cmd="docker-compose"

View File

@ -185,9 +185,6 @@ if [[ -d images ]]; then
docker load -i images/$i 2>&1 | tee -a ${CURRENT_DIR}/install.log
done
else
log "拉取镜像"
cd ${DE_RUN_BASE} && docker-compose $compose_files pull 2>&1
DEVERSION=$(cat ${CURRENT_DIR}/dataease/templates/version)
curl -sfL https://resource.fit2cloud.com/installation-log.sh | sh -s de ${INSTALL_TYPE} ${DEVERSION}
cd -
@ -244,5 +241,9 @@ log "启动服务"
dectl start | tee -a ${CURRENT_DIR}/install.log
dectl status 2>&1 | tee -a ${CURRENT_DIR}/install.log
access_port=$DE_PORT
if [[ $DE_INSTALL_MODE != "community" ]];then
access_port=9080
fi
echo -e "======================= 安装完成 =======================\n" 2>&1 | tee -a ${CURRENT_DIR}/install.log
echo -e "系统登录信息如下:\n 用户名: admin\n 初始密码: DataEase@123456" 2>&1 | tee -a ${CURRENT_DIR}/install.log
echo -e "系统登录信息如下:\n 访问地址: http://服务器IP:$access_port\n 用户名: admin\n 初始密码: DataEase@123456" 2>&1 | tee -a ${CURRENT_DIR}/install.log

41
installer/uninstall.sh Normal file
View File

@ -0,0 +1,41 @@
#!/bin/bash
DE_BASE=/opt
read -r -p "即将卸载 DataEase 服务,包括删除运行目录、数据及相关镜像,是否继续? [Y/n] " input
case $input in
[yY][eE][sS]|[yY])
echo "Yes"
;;
[nN][oO]|[nN])
echo "No"
exit 1
;;
*)
echo "无效输入..."
exit 1
;;
esac
if [ -f /usr/bin/dectl ]; then
# 获取已安装的 DataEase 的运行目录
DE_BASE=$(grep "^DE_BASE=" /usr/bin/dectl | cut -d'=' -f2)
fi
echo "停止 DataEase 服务"
dectl stop
# 清理 DataEase 相关镜像
if test ! -z "$(docker images -f dangling=true -q)"; then
echo "清理虚悬镜像"
docker rmi $(docker images -f dangling=true -q)
fi
if test -n "$(docker images | grep 'registry.cn-qingdao.aliyuncs.com/dataease')"; then
echo "清理 DataEase 镜像"
docker rmi $(docker images | grep "registry.cn-qingdao.aliyuncs.com/dataease" | awk -F' ' '{print $1":"$2}')
fi
# 清理 DataEase 运行目录及命令行工具 dectl
rm -rf ${DE_BASE}/dataease2.0 /usr/bin/dectl

View File

@ -3,6 +3,7 @@ package io.dataease.api.chart;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.dataease.api.chart.dto.ChartViewDTO;
import io.dataease.api.chart.request.ChartExcelRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.PathVariable;
@ -18,13 +19,15 @@ import java.util.List;
@Tag(name = "视图管理:数据")
@ApiSupport(order = 989)
public interface ChartDataApi {
@Operation(summary = "获取视图数据")
@PostMapping("getData")
ChartViewDTO getData(@RequestBody ChartViewDTO chartViewDTO) throws Exception;
@Operation(summary = "导出数据")
@PostMapping("innerExportDetails")
void innerExportDetails(@RequestBody ChartExcelRequest request, HttpServletResponse response) throws Exception;
@Operation(summary = "获取字段值")
@PostMapping("getFieldData/{fieldId}/{fieldType}")
List<String> getFieldData(@RequestBody ChartViewDTO view, @PathVariable Long fieldId, @PathVariable String fieldType) throws Exception;
}

View File

@ -3,6 +3,7 @@ package io.dataease.api.chart;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.dataease.api.chart.dto.ChartViewDTO;
import io.dataease.api.chart.dto.ChartViewFieldDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@ -18,18 +19,23 @@ import java.util.Map;
@Tag(name = "视图管理:查看")
@ApiSupport(order = 988)
public interface ChartViewApi {
@Operation(summary = "查询视图详情并同时计算数据", hidden = true)
@PostMapping("getChart/{id}")
ChartViewDTO getData(@PathVariable Long id) throws Exception;
@Operation(summary = "获取视图字段")
@PostMapping("listByDQ/{id}/{chartId}")
Map<String, List<ChartViewFieldDTO>> listByDQ(@PathVariable Long id, @PathVariable Long chartId);
@Operation(summary = "保存视图")
@PostMapping("save")
ChartViewDTO save(@RequestBody ChartViewDTO dto) throws Exception;
@Operation(summary = "检查是否同数据集")
@GetMapping("/checkSameDataSet/{viewIdSource}/{viewIdTarget}")
String checkSameDataSet(@PathVariable String viewIdSource, @PathVariable String viewIdTarget);
@Operation(summary = "查询视图详情")
@PostMapping("getDetail/{id}")
ChartViewDTO getDetail(@PathVariable Long id);
}

View File

@ -5,6 +5,7 @@ import io.dataease.api.dataset.dto.DatasetTableDTO;
import io.dataease.api.dataset.dto.PreviewSqlDTO;
import io.dataease.api.dataset.union.DatasetGroupInfoDTO;
import io.dataease.dto.dataset.DatasetTableFieldDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@ -18,21 +19,27 @@ import java.util.Map;
@Tag(name = "数据集管理:数据")
@ApiSupport(order = 978)
public interface DatasetDataApi {
@Operation(summary = "预览数据")
@PostMapping("previewData")
Map<String, Object> previewData(@RequestBody DatasetGroupInfoDTO datasetGroupInfoDTO) throws Exception;
@Operation(summary = "获取数据集节点字段")
@PostMapping("tableField")
List<DatasetTableFieldDTO> tableField(@RequestBody DatasetTableDTO datasetTableDTO) throws Exception;
@Operation(summary = "SQL预览")
@PostMapping("previewSql")
Map<String, Object> previewSql(@RequestBody PreviewSqlDTO dto) throws Exception;
@Operation(summary = "sql片段校验", hidden = true)
@PostMapping("previewSqlCheck")
Map<String, Object> previewSqlCheck(@RequestBody PreviewSqlDTO dto) throws Exception;
@Operation(summary = "获取字段枚举值")
@PostMapping("enumValue")
List<String> getFieldEnum(@RequestBody List<Long> ids) throws Exception;
@Operation(summary = "获取数据集总数据量", hidden = true)
@PostMapping("getDatasetCount")
Long getDatasetCount(@RequestBody DatasetGroupInfoDTO datasetGroupInfoDTO) throws Exception;
}

View File

@ -4,6 +4,7 @@ import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.dataease.api.dataset.dto.MultFieldValuesRequest;
import io.dataease.api.dataset.engine.SQLFunctionDTO;
import io.dataease.dto.dataset.DatasetTableFieldDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@ -27,31 +28,39 @@ public interface DatasetTableApi {
* @return
* @throws Exception
*/
@Operation(summary = "保存字段")
@PostMapping("save")
DatasetTableFieldDTO save(@RequestBody DatasetTableFieldDTO datasetTableFieldDTO) throws Exception;
@Operation(summary = "查询字段")
@PostMapping("get/{id}")
DatasetTableFieldDTO get(@PathVariable Long id);
@Operation(summary = "获取数据集字段")
@PostMapping("listByDatasetGroup/{id}")
List<DatasetTableFieldDTO> listByDatasetGroup(@PathVariable Long id);
@Operation(summary = "删除字段")
@PostMapping("delete/{id}")
void delete(@PathVariable Long id);
@Operation(summary = "获取字段分组")
@PostMapping("listByDQ/{id}")
Map<String, List<DatasetTableFieldDTO>> listByDQ(@PathVariable Long id);
@GetMapping ("listWithPermissions/{id}")
@Operation(summary = "获取字段")
@GetMapping("listWithPermissions/{id}")
List<DatasetTableFieldDTO> listFieldsWithPermissions(@PathVariable Long id);
@Operation(summary = "获取枚举值")
@PostMapping("multFieldValuesForPermissions")
List<String> multFieldValuesForPermissions(@RequestBody MultFieldValuesRequest multFieldValuesRequest) throws Exception;
@Operation(summary = "获取计算字段函数")
@PostMapping("getFunction")
List<SQLFunctionDTO> getFunction();
@Operation(summary = "删除视图计算字段", hidden = true)
@PostMapping("deleteByChartId/{id}")
void deleteByChartId(@PathVariable Long id);
}

View File

@ -10,6 +10,7 @@ import io.dataease.auth.DeApiPath;
import io.dataease.auth.DePermit;
import io.dataease.model.BusiNodeRequest;
import io.dataease.model.BusiNodeVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@ -19,6 +20,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
import static io.dataease.constant.AuthResourceEnum.DATASET;
@Tag(name = "数据集管理:树")
@ApiSupport(order = 979)
@DeApiPath(value = "/datasetTree", rt = DATASET)
@ -31,10 +33,12 @@ public interface DatasetTreeApi {
* @return
* @throws Exception
*/
@Operation(summary = "保存数据集", hidden = true)
@DePermit({"#p0.id+':manage'"})
@PostMapping("save")
DatasetNodeDTO save(@RequestBody DatasetGroupInfoDTO dto) throws Exception;
@Operation(summary = "重命名数据集")
@DePermit({"#p0.id+':manage'"})
@PostMapping("rename")
DatasetNodeDTO rename(@RequestBody DatasetGroupInfoDTO dto) throws Exception;
@ -46,36 +50,46 @@ public interface DatasetTreeApi {
* @return
* @throws Exception
*/
@Operation(summary = "创建数据集")
@DePermit({"#p0.pid+':manage'"})
@PostMapping("create")
DatasetNodeDTO create(@RequestBody DatasetGroupInfoDTO dto) throws Exception;
@Operation(summary = "移动数据集")
@DePermit({"#p0.id+':manage'", "#p0.pid+':manage'"})
@PostMapping("move")
DatasetNodeDTO move(@RequestBody DatasetGroupInfoDTO dto) throws Exception;
@Operation(summary = "删除数据集")
@DePermit({"#p0+':manage'"})
@PostMapping("delete/{id}")
void delete(@PathVariable("id") Long id);
@Operation(summary = "查询文件夹以及数据集tree")
@PostMapping("tree")
List<BusiNodeVO> tree(@RequestBody BusiNodeRequest request);
@Operation(summary = "查询数据集对应用户信息")
@GetMapping("/barInfo/{id}")
DataSetBarVO barInfo(@PathVariable("id") Long id);
@Operation(summary = "查询数据集")
@PostMapping("get/{id}")
DatasetGroupInfoDTO get(@PathVariable("id") Long id) throws Exception;
@Operation(summary = "获取数据集详情")
@PostMapping("details/{id}")
DatasetGroupInfoDTO details(@PathVariable("id") Long id) throws Exception;
@Operation(summary = "获取数据集详情")
@PostMapping("dsDetails")
List<DatasetTableDTO> panelGetDsDetails(@RequestBody List<Long> ids) throws Exception;
@Operation(summary = "获取SQL参数")
@PostMapping("getSqlParams")
List<SqlVariableDetails> getSqlParams(@RequestBody List<Long> ids) throws Exception;
@Operation(summary = "带权限查询数据集详情")
@PostMapping("detailWithPerm")
List<DatasetTableDTO> detailWithPerm(@RequestBody List<Long> ids) throws Exception;
}

View File

@ -1,6 +1,7 @@
package io.dataease.api.template;
import io.dataease.api.template.dto.TemplateManageDTO;
import io.dataease.api.template.request.TemplateManageBatchRequest;
import io.dataease.api.template.request.TemplateManageRequest;
import io.dataease.api.template.vo.VisualizationTemplateVO;
import org.springframework.web.bind.annotation.*;
@ -14,11 +15,11 @@ public interface TemplateManageApi {
@PostMapping("/save")
TemplateManageDTO save(@RequestBody TemplateManageRequest request);
@PostMapping("/delete/{id}")
void delete(@PathVariable String id);
@PostMapping("/delete/{id}/{categoryId}")
void delete(@PathVariable String id,@PathVariable String categoryId);
@PostMapping("/deleteCategory/{id}")
void deleteCategory(@PathVariable String id);
String deleteCategory(@PathVariable String id);
@GetMapping("/findOne/{templateId}")
VisualizationTemplateVO findOne(@PathVariable String templateId) throws Exception;
@ -32,4 +33,13 @@ public interface TemplateManageApi {
@PostMapping("/nameCheck")
String nameCheck(@RequestBody TemplateManageRequest request);
@PostMapping("/categoryTemplateNameCheck")
String categoryTemplateNameCheck(@RequestBody TemplateManageRequest request);
@PostMapping("/batchUpdate")
void batchUpdate(@RequestBody TemplateManageBatchRequest request);
@PostMapping("/batchDelete")
void batchDelete(@RequestBody TemplateManageBatchRequest request);
}

View File

@ -18,6 +18,8 @@ public class TemplateManageDTO extends VisualizationTemplateVO {
private Long recentUseTime;
private Boolean checked = false;
private List<TemplateManageDTO> children;
private List<String> categories;

View File

@ -0,0 +1,21 @@
package io.dataease.api.template.request;
import io.dataease.api.template.vo.VisualizationTemplateVO;
import lombok.Data;
import java.util.List;
/**
* Author: wangjiahao
* Description:
*/
@Data
public class TemplateManageBatchRequest {
private String optType;
private List<String> templateIds;
private List<String> categories;
}

View File

@ -2,6 +2,8 @@ package io.dataease.api.template.vo;
import lombok.Data;
import java.util.List;
/**
* @author : WangJiaHao
* @date : 2023/11/7 13:22
@ -73,4 +75,9 @@ public class VisualizationTemplateVO {
* 预存数据
*/
private String dynamicData;
/**
* 分类
*/
private List<String> categories;
}