mirror of
https://github.com/dataease/dataease.git
synced 2025-02-24 19:42:56 +08:00
Merge remote-tracking branch 'origin/dev-v2' into dev-v2
This commit is contained in:
commit
3049d42372
105
core/core-frontend/src/components/rich-text/TinymceEditor.vue
Normal file
105
core/core-frontend/src/components/rich-text/TinymceEditor.vue
Normal file
@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div class="de-tinymce-container ed-textarea__inner">
|
||||
<editor class="de-tinymce-content" v-model="myValue" :id="tinymceId" :init="init" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, toRefs, watch } from 'vue'
|
||||
import { formatDataEaseBi } from '@/utils/url'
|
||||
import tinymce from 'tinymce/tinymce' // tinymce默认hidden,不引入不显示
|
||||
import Editor from '@tinymce/tinymce-vue' // 编辑器引入
|
||||
import 'tinymce/themes/silver/theme' // 编辑器主题
|
||||
import 'tinymce/icons/default' // 引入编辑器图标icon,不引入则不显示对应图标
|
||||
// 引入编辑器插件(基本免费插件都在这儿了)
|
||||
import 'tinymce/plugins/advlist' // 高级列表
|
||||
import 'tinymce/plugins/autolink' // 自动链接
|
||||
import 'tinymce/plugins/link' // 超链接
|
||||
import 'tinymce/plugins/image' // 插入编辑图片
|
||||
import 'tinymce/plugins/lists' // 列表插件
|
||||
import 'tinymce/plugins/charmap' // 特殊字符
|
||||
import 'tinymce/plugins/media' // 插入编辑媒体
|
||||
import 'tinymce/plugins/wordcount' // 字数统计
|
||||
import 'tinymce/plugins/table' // 表格
|
||||
import 'tinymce/plugins/contextmenu' // contextmenu
|
||||
import 'tinymce/plugins/directionality'
|
||||
import 'tinymce/plugins/nonbreaking'
|
||||
import 'tinymce/plugins/pagebreak'
|
||||
const props = defineProps({
|
||||
modelValue: String
|
||||
})
|
||||
const myValue = ref()
|
||||
const { modelValue } = toRefs(props)
|
||||
myValue.value = modelValue
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
newValue => {
|
||||
myValue.value = newValue
|
||||
}
|
||||
)
|
||||
|
||||
const emits = defineEmits(['update:modelValue', 'change'])
|
||||
watch(
|
||||
() => myValue.value,
|
||||
newValue => {
|
||||
emits('update:modelValue', newValue)
|
||||
emits('change', newValue)
|
||||
}
|
||||
)
|
||||
|
||||
const tinymceId = 'tinymce-view-pf'
|
||||
const init = ref({
|
||||
selector: '#' + tinymceId,
|
||||
toolbar_items_size: 'small',
|
||||
language_url: formatDataEaseBi('/tinymce-dataease-private/langs/zh_CN.js'), // 汉化路径是自定义的,一般放在public或static里面
|
||||
language: 'zh_CN',
|
||||
skin_url: formatDataEaseBi('/tinymce-dataease-private/skins/ui/oxide'), // 皮肤
|
||||
content_css: formatDataEaseBi('/tinymce-dataease-private/skins/content/default/content.css'),
|
||||
plugins:
|
||||
'advlist autolink link image lists charmap media wordcount table contextmenu directionality pagebreak', // 插件
|
||||
// 工具栏
|
||||
toolbar:
|
||||
'undo redo |fontselect fontsizeselect |forecolor backcolor bold italic |underline strikethrough link| formatselect |' +
|
||||
'alignleft aligncenter alignright | bullist numlist |' +
|
||||
' blockquote subscript superscript removeformat | table image media | fullscreen ' +
|
||||
'| bdmap indent2em lineheight formatpainter axupimgs',
|
||||
toolbar_location: '/',
|
||||
font_formats:
|
||||
'阿里巴巴普惠体=阿里巴巴普惠体 3.0 55 Regular L3;微软雅黑=Microsoft YaHei;宋体=SimSun;黑体=SimHei;仿宋=FangSong;华文黑体=STHeiti;华文楷体=STKaiti;华文宋体=STSong;华文仿宋=STFangsong;Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings',
|
||||
fontsize_formats: '12px 14px 16px 18px 20px 22px 24px 28px 32px 36px 48px 56px 72px', // 字体大小
|
||||
menubar: false,
|
||||
placeholder: '',
|
||||
outer_placeholder: '双击输入文字',
|
||||
inline: true,
|
||||
branding: false
|
||||
})
|
||||
|
||||
tinymce.init({})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.de-tinymce-container {
|
||||
--ed-input-text-color: var(--ed-text-color-regular);
|
||||
--ed-input-border: var(--ed-border);
|
||||
--ed-input-hover-border: var(--ed-border-color-hover);
|
||||
--ed-input-focus-border: var(--ed-color-primary);
|
||||
--ed-input-transparent-border: 0 0 0 1px transparent inset;
|
||||
--ed-input-border-color: var(--ed-border-color);
|
||||
--ed-input-border-radius: var(--ed-border-radius-base);
|
||||
--ed-input-bg-color: var(--ed-fill-color-blank);
|
||||
--ed-input-icon-color: var(--ed-text-color-placeholder);
|
||||
--ed-input-placeholder-color: var(--ed-text-color-placeholder);
|
||||
--ed-input-hover-border-color: var(--ed-border-color-hover);
|
||||
--ed-input-clear-hover-color: var(--ed-text-color-secondary);
|
||||
--ed-input-focus-border-color: var(--ed-color-primary);
|
||||
--ed-input-width: 100%;
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
.de-tinymce-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
outline: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -2136,7 +2136,9 @@ export default {
|
||||
dsIntervalTime: '数据源检测时间间隔',
|
||||
dsExecuteTime: '数据源检测频率',
|
||||
frontTimeOut: '请求超时时间(秒)',
|
||||
logLiveTime: '日志保留时间(天)'
|
||||
logLiveTime: '日志保留时间(天)',
|
||||
platformOid: '第三方平台用户组织',
|
||||
platformRid: '第三方平台用户角色'
|
||||
},
|
||||
template_manage: {
|
||||
name_already_exists_type: '分类名称已存在',
|
||||
|
@ -6,6 +6,8 @@ import { listDatasources } from '@/api/datasource'
|
||||
import type { BusiTreeRequest, BusiTreeNode } from '@/models/tree/TreeNode'
|
||||
import { pathValid } from '@/store/modules/permission'
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
import { useAppStoreWithOut } from '@/store/modules/app'
|
||||
const appStore = useAppStoreWithOut()
|
||||
const { wsCache } = useCache()
|
||||
export interface InnerInteractive {
|
||||
rootManage: boolean
|
||||
@ -47,7 +49,7 @@ export const interactiveStore = defineStore('interactive', {
|
||||
actions: {
|
||||
async setInteractive(param: BusiTreeRequest) {
|
||||
const flag = busiFlagMap.findIndex(item => item === param.busiFlag)
|
||||
if (!hasMenuAuth(flag) && !window.DataEaseBi) {
|
||||
if (!hasMenuAuth(flag) && !window.DataEaseBi && !appStore.getIsIframe) {
|
||||
const tempData: InnerInteractive = {
|
||||
rootManage: false,
|
||||
anyManage: false,
|
||||
|
@ -6,6 +6,7 @@ import { shortcutOption } from '@/views/workbranch/ShortcutOption'
|
||||
import { useRouter } from 'vue-router'
|
||||
import Workbranch from '@/views/mobile/components/Workbranch.vue'
|
||||
import request from '@/config/axios'
|
||||
import nothingNone from '@/assets/img/none.png'
|
||||
import VanTabs from 'vant/es/tabs'
|
||||
import VanNavBar from 'vant/es/nav-bar'
|
||||
import VanTab from 'vant/es/tab'
|
||||
@ -19,6 +20,7 @@ const router = useRouter()
|
||||
const { t } = useI18n()
|
||||
|
||||
const activeTab = ref('recent')
|
||||
const emptyTips = ref('')
|
||||
const state = reactive({
|
||||
tableData: [],
|
||||
curTypeList: []
|
||||
@ -35,9 +37,22 @@ const loadTableData = () => {
|
||||
})
|
||||
.finally(() => {
|
||||
emits('setLoading', false)
|
||||
setEmptyTips()
|
||||
})
|
||||
}
|
||||
|
||||
const setEmptyTips = () => {
|
||||
emptyTips.value = state.tableData.length
|
||||
? ''
|
||||
: `暂无${
|
||||
{
|
||||
recent: '数据',
|
||||
store: '收藏',
|
||||
share: '分享'
|
||||
}[activeTab.value]
|
||||
}`
|
||||
}
|
||||
|
||||
const loadShareTableData = () => {
|
||||
emits('setLoading', true)
|
||||
request
|
||||
@ -50,6 +65,7 @@ const loadShareTableData = () => {
|
||||
})
|
||||
.finally(() => {
|
||||
emits('setLoading', false)
|
||||
setEmptyTips()
|
||||
})
|
||||
}
|
||||
|
||||
@ -133,6 +149,12 @@ const formatterTime = val => {
|
||||
:time="formatterTime(ele.lastEditTime || ele.time)"
|
||||
/>
|
||||
</div>
|
||||
<div class="empty-img-mobile" v-if="!!emptyTips">
|
||||
<img width="125" height="125" :src="nothingNone" alt="" />
|
||||
<div class="empty-tips">
|
||||
{{ emptyTips }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -145,5 +167,23 @@ const formatterTime = val => {
|
||||
height: calc(100vh - 142px);
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.empty-img-mobile {
|
||||
position: absolute;
|
||||
top: 33%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
|
||||
img {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -67,12 +67,7 @@ const onClickLeft = () => {
|
||||
<template>
|
||||
<div class="dv-common-layout-mobile">
|
||||
<van-sticky>
|
||||
<van-nav-bar
|
||||
:title="state.dvInfo.name"
|
||||
left-text="返回"
|
||||
left-arrow
|
||||
@click-left="onClickLeft"
|
||||
/>
|
||||
<van-nav-bar :title="state.dvInfo.name" left-arrow @click-left="onClickLeft" />
|
||||
</van-sticky>
|
||||
<de-preview
|
||||
ref="dashboardPreview"
|
||||
@ -92,5 +87,11 @@ const onClickLeft = () => {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
overflow-y: auto;
|
||||
--van-nav-bar-height: 44px;
|
||||
--van-nav-bar-arrow-size: 20px;
|
||||
--van-nav-bar-icon-color: #1f2329;
|
||||
--van-nav-bar-title-text-color: #1f2329;
|
||||
--van-font-bold: 500;
|
||||
--van-nav-bar-title-font-size: 17px;
|
||||
}
|
||||
</style>
|
||||
|
@ -18,7 +18,9 @@ const state = reactive({
|
||||
dsExecuteTime: 'minute',
|
||||
frontTimeOut: '30'
|
||||
}),
|
||||
settingList: []
|
||||
settingList: [],
|
||||
orgOptions: [],
|
||||
roleOptions: []
|
||||
})
|
||||
|
||||
const rule = reactive<FormRules>({
|
||||
@ -36,7 +38,10 @@ const buildSettingList = () => {
|
||||
const pkey = item.pkey.startsWith('basic.') ? item.pkey : `basic.${item.pkey}`
|
||||
const sort = item.sort
|
||||
const type = item.type
|
||||
const pval = state.form[item.pkey]
|
||||
let pval = state.form[item.pkey]
|
||||
if (Array.isArray(pval)) {
|
||||
pval = pval.join(',')
|
||||
}
|
||||
return { pkey, pval, type, sort }
|
||||
})
|
||||
}
|
||||
@ -99,7 +104,9 @@ const closeLoading = () => {
|
||||
loadingInstance.value?.close()
|
||||
}
|
||||
|
||||
const edit = list => {
|
||||
const edit = (list, orgOptions, roleOptions) => {
|
||||
state.orgOptions = orgOptions || []
|
||||
state.roleOptions = roleOptions || []
|
||||
state.settingList = list.map(item => {
|
||||
const pkey = item.pkey
|
||||
if (pkey === 'basic.logLiveTime') {
|
||||
@ -114,11 +121,62 @@ const edit = list => {
|
||||
item['label'] = `setting_${pkey}`
|
||||
item['pkey'] = pkey.split('.')[1]
|
||||
let pval = item.pval
|
||||
if (item.pkey.includes('platformRid') && pval?.length) {
|
||||
pval = pval.split(',')
|
||||
if (!rule['platformRid']) {
|
||||
rule['platformRid'] = [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
if (item.pkey.includes('platformOid')) {
|
||||
if (!rule['platformOid']) {
|
||||
rule['platformOid'] = [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
state.form[item['pkey']] = pval || state.form[item['pkey']]
|
||||
return item
|
||||
})
|
||||
dialogVisible.value = true
|
||||
}
|
||||
const loadRoleOptions = async () => {
|
||||
const oid = state.form['platformOid']
|
||||
if (!oid) {
|
||||
return
|
||||
}
|
||||
const res = await request.get({ url: `/role/queryWithOid/${oid}` })
|
||||
const data = res.data
|
||||
const map = groupBy(data)
|
||||
state.roleOptions[0].children = map.get(false)
|
||||
state.roleOptions[1].children = map.get(true)
|
||||
}
|
||||
const groupBy = list => {
|
||||
const map = new Map()
|
||||
list.forEach(item => {
|
||||
const readonly = item.readonly
|
||||
let arr = map.get(readonly)
|
||||
if (!arr) {
|
||||
arr = []
|
||||
}
|
||||
arr.push({ value: item.id, label: item.name, disabled: false })
|
||||
map.set(readonly, arr)
|
||||
})
|
||||
return map
|
||||
}
|
||||
const oidChange = () => {
|
||||
state.form['platformRid'] = []
|
||||
loadRoleOptions()
|
||||
}
|
||||
defineExpose({
|
||||
edit
|
||||
})
|
||||
@ -200,6 +258,29 @@ defineExpose({
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="item.pkey === 'platformOid'">
|
||||
<el-tree-select
|
||||
class="edit-all-line"
|
||||
v-model="state.form[item.pkey]"
|
||||
:data="state.orgOptions"
|
||||
check-strictly
|
||||
:render-after-expand="false"
|
||||
@change="oidChange"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="item.pkey === 'platformRid'">
|
||||
<el-tree-select
|
||||
class="edit-all-line"
|
||||
v-model="state.form[item.pkey]"
|
||||
:data="state.roleOptions"
|
||||
:highlight-current="true"
|
||||
multiple
|
||||
:render-after-expand="false"
|
||||
:placeholder="$t('common.please_select') + $t('user.role')"
|
||||
show-checkbox
|
||||
check-on-click-node
|
||||
/>
|
||||
</div>
|
||||
<v-else />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
@ -18,29 +18,77 @@ import request from '@/config/axios'
|
||||
import { SettingRecord } from '@/views/system/common/SettingTemplate'
|
||||
import { reactive } from 'vue'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
const { t } = useI18n()
|
||||
const editor = ref()
|
||||
const infoTemplate = ref()
|
||||
const tooltips = [
|
||||
{
|
||||
key: 'setting_basic.frontTimeOut',
|
||||
val: '请求超时时间(单位:秒,注意:保存后刷新浏览器生效)'
|
||||
},
|
||||
{
|
||||
key: 'setting_basic.platformOid',
|
||||
val: '作用域包括认证设置和平台对接'
|
||||
},
|
||||
{
|
||||
key: 'setting_basic.platformRid',
|
||||
val: '作用域包括认证设置和平台对接'
|
||||
}
|
||||
]
|
||||
const state = reactive({
|
||||
templateList: [] as SettingRecord[]
|
||||
templateList: [] as SettingRecord[],
|
||||
orgOptions: [],
|
||||
roleOptions: [
|
||||
{
|
||||
value: 'admin',
|
||||
label: t('role.org_admin'),
|
||||
children: null,
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
value: 'readonly',
|
||||
label: t('role.average_role'),
|
||||
children: null,
|
||||
disabled: false
|
||||
}
|
||||
]
|
||||
})
|
||||
let originData = []
|
||||
const selectedOid = ref('')
|
||||
const selectedOName = ref('')
|
||||
const selectedRid = ref<string[]>([])
|
||||
const selectedRName = ref<string[]>([])
|
||||
const search = cb => {
|
||||
const url = '/sysParameter/basic/query'
|
||||
originData = []
|
||||
state.templateList = []
|
||||
request.get({ url }).then(res => {
|
||||
request.get({ url }).then(async res => {
|
||||
originData = cloneDeep(res.data)
|
||||
const data = res.data
|
||||
for (let index = 0; index < data.length; index++) {
|
||||
const item = data[index]
|
||||
if (item.pkey === 'basic.autoCreateUser') {
|
||||
item.pval = item.pval === 'true' ? '开启' : '未开启'
|
||||
} else if (item.pkey === 'basic.platformOid') {
|
||||
selectedOid.value = item.pval
|
||||
await loadOrgOptions()
|
||||
item.pval = selectedOName.value || '默认组织'
|
||||
} else if (item.pkey === 'basic.platformRid') {
|
||||
const pval = item.pval
|
||||
if (pval?.length) {
|
||||
const pvalArray = pval.split(',')
|
||||
selectedRid.value = pvalArray
|
||||
await loadRoleOptions()
|
||||
if (selectedRName.value.length) {
|
||||
item.pval = selectedRName.value.join(',')
|
||||
} else {
|
||||
item.pval = '普通角色'
|
||||
}
|
||||
} else {
|
||||
selectedRid.value = []
|
||||
item.pval = '普通角色'
|
||||
}
|
||||
} else {
|
||||
item.pval = item.pval
|
||||
}
|
||||
@ -58,6 +106,56 @@ const refresh = () => {
|
||||
refresh()
|
||||
|
||||
const edit = () => {
|
||||
editor?.value.edit(cloneDeep(originData))
|
||||
editor?.value.edit(
|
||||
cloneDeep(originData),
|
||||
cloneDeep(state.orgOptions),
|
||||
cloneDeep(state.roleOptions)
|
||||
)
|
||||
}
|
||||
const loadOrgOptions = async () => {
|
||||
const res = await request.post({ url: '/org/mounted', data: {} })
|
||||
const data = res.data
|
||||
formatOrg(data)
|
||||
state.orgOptions = data
|
||||
}
|
||||
const loadRoleOptions = async () => {
|
||||
const res = await request.get({ url: `/role/queryWithOid/${selectedOid.value}` })
|
||||
const data = res.data
|
||||
const map = groupBy(data)
|
||||
state.roleOptions[0].children = map.get(false)
|
||||
state.roleOptions[1].children = map.get(true)
|
||||
}
|
||||
const formatOrg = list => {
|
||||
const stack = [...list]
|
||||
while (stack.length) {
|
||||
const item = stack.pop()
|
||||
if (item.id === selectedOid.value) {
|
||||
selectedOName.value = item.name
|
||||
}
|
||||
item.value = item.id
|
||||
item.label = item.name
|
||||
item.disabled = item.readOnly
|
||||
if (item.children?.length) {
|
||||
item.children.forEach(kid => stack.push(kid))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const groupBy = list => {
|
||||
const map = new Map()
|
||||
selectedRName.value = []
|
||||
list.forEach(item => {
|
||||
if (selectedRid.value.includes(item.id)) {
|
||||
selectedRName.value.push(item.name)
|
||||
}
|
||||
const readonly = item.readonly
|
||||
let arr = map.get(readonly)
|
||||
if (!arr) {
|
||||
arr = []
|
||||
}
|
||||
arr.push({ value: item.id, label: item.name, disabled: false })
|
||||
map.set(readonly, arr)
|
||||
})
|
||||
return map
|
||||
}
|
||||
</script>
|
||||
|
2
de-xpack
2
de-xpack
@ -1 +1 @@
|
||||
Subproject commit d9d2d9a8a9eb67c4d3c68027d80fb4c5459c5c25
|
||||
Subproject commit a1c4b52372679fde98c2635264ef0b48ac64a50f
|
2
pom.xml
2
pom.xml
@ -13,7 +13,7 @@
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<dataease.version>2.3.0</dataease.version>
|
||||
<dataease.version>2.4.0</dataease.version>
|
||||
<java.version>17</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
|
||||
|
@ -8,6 +8,7 @@ import io.dataease.api.permissions.role.vo.RoleVO;
|
||||
import io.dataease.auth.DeApiPath;
|
||||
import io.dataease.auth.DePermit;
|
||||
import io.dataease.model.KeywordRequest;
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
@ -90,4 +91,8 @@ public interface RoleApi {
|
||||
@Operation(summary = "查询组织内角色")
|
||||
@PostMapping("/byCurOrg")
|
||||
List<RoleVO> byCurOrg(@RequestBody KeywordRequest request);
|
||||
|
||||
@Hidden
|
||||
@GetMapping("/queryWithOid/{oid}")
|
||||
List<RoleVO> queryWithOid(@PathVariable("oid") Long oid);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user