mirror of
https://gh-proxy.org/https://github.com/klinsmaya/SQLBot.git
synced 2026-11-08 01:06:59 +08:00
feat: dashboard preview
This commit is contained in:
@@ -26,7 +26,8 @@ fileConfig(config.config_file_name)
|
||||
|
||||
# from apps.system.models.user import SQLModel # noqa
|
||||
# from apps.settings.models.setting_models import SQLModel
|
||||
from apps.chat.models.chat_model import SQLModel
|
||||
#from apps.chat.models.chat_model import SQLModel
|
||||
from apps.dashboard.models.dashboard import SQLModel
|
||||
from common.core.config import settings # noqa
|
||||
|
||||
target_metadata = SQLModel.metadata
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
"""core_dashboard
|
||||
|
||||
Revision ID: 804b08ac329d
|
||||
Revises: 1f077c30e476
|
||||
Create Date: 2025-06-03 14:40:25.419409
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlmodel.sql.sqltypes
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '804b08ac329d'
|
||||
down_revision = '1f077c30e476'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('core_dashboard',
|
||||
sa.Column('id', sa.String(length=50), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=True),
|
||||
sa.Column('pid', sa.String(length=50), nullable=True),
|
||||
sa.Column('workspace_id', sa.String(length=50), nullable=True),
|
||||
sa.Column('org_id', sa.String(length=50), nullable=True),
|
||||
sa.Column('level', sa.Integer(), nullable=True),
|
||||
sa.Column('node_type', sa.String(length=255), nullable=True),
|
||||
sa.Column('type', sa.String(length=50), nullable=True),
|
||||
sa.Column('canvas_style_data', sa.Text(), nullable=True),
|
||||
sa.Column('component_data', sa.Text(), nullable=True),
|
||||
sa.Column('mobile_layout', sa.SmallInteger(), nullable=True),
|
||||
sa.Column('status', sa.Integer(), nullable=True),
|
||||
sa.Column('self_watermark_status', sa.Integer(), nullable=True),
|
||||
sa.Column('sort', sa.Integer(), nullable=True),
|
||||
sa.Column('create_time', sa.BigInteger(), nullable=True),
|
||||
sa.Column('create_by', sa.String(length=255), nullable=True),
|
||||
sa.Column('update_time', sa.BigInteger(), nullable=True),
|
||||
sa.Column('update_by', sa.String(length=255), nullable=True),
|
||||
sa.Column('remark', sa.String(length=255), nullable=True),
|
||||
sa.Column('source', sa.String(length=255), nullable=True),
|
||||
sa.Column('delete_flag', sa.SmallInteger(), nullable=True),
|
||||
sa.Column('delete_time', sa.BigInteger(), nullable=True),
|
||||
sa.Column('delete_by', sa.String(length=255), nullable=True),
|
||||
sa.Column('version', sa.Integer(), nullable=True),
|
||||
sa.Column('content_id', sa.String(length=50), nullable=True),
|
||||
sa.Column('check_version', sa.String(length=50), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_table('core_dashboard')
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,18 @@
|
||||
import hashlib
|
||||
import os
|
||||
import uuid
|
||||
from typing import List
|
||||
from fastapi import APIRouter, File, UploadFile, HTTPException
|
||||
|
||||
from apps.dashboard.crud.dashboard_service import get_dashboard_list, preview_with_id
|
||||
from common.core.deps import SessionDep
|
||||
|
||||
router = APIRouter(tags=["dashboard"], prefix="/dashboard")
|
||||
|
||||
@router.get("/list")
|
||||
async def datasource_list(session: SessionDep):
|
||||
return get_dashboard_list(session=session)
|
||||
|
||||
@router.get("/preview_dashboard/{id}")
|
||||
async def preview_dashboard(session: SessionDep,id:str):
|
||||
return preview_with_id(session=session,dashboard_id=id)
|
||||
@@ -0,0 +1,13 @@
|
||||
from select import select
|
||||
|
||||
from apps.dashboard.models.dashboard_model import CoreDashboard
|
||||
from common.core.deps import SessionDep
|
||||
|
||||
|
||||
def get_dashboard_list(session: SessionDep):
|
||||
statement = select(CoreDashboard).order_by(CoreDashboard.create_time.desc())
|
||||
dashboard_list = session.exec(statement).fetchall()
|
||||
return dashboard_list
|
||||
|
||||
def preview_with_id(session: SessionDep, dashboard_id: str):
|
||||
return session.query(CoreDashboard).filter(CoreDashboard.id == id).first()
|
||||
@@ -0,0 +1,123 @@
|
||||
from sqlmodel import SQLModel, Field
|
||||
from sqlalchemy import String, Column, Text, SmallInteger, BigInteger, Integer
|
||||
|
||||
|
||||
class CoreDashboard(SQLModel, table=True):
|
||||
__tablename__ = "core_dashboard"
|
||||
|
||||
id: str = Field(
|
||||
sa_column=Column(String(50), nullable=False, primary_key=True)
|
||||
)
|
||||
name: str = Field(
|
||||
default=None,
|
||||
max_length=255,
|
||||
sa_column=Column(String(255), nullable=True)
|
||||
)
|
||||
pid: str = Field(
|
||||
default=None,
|
||||
max_length=50,
|
||||
sa_column=Column(String(50), nullable=True)
|
||||
)
|
||||
workspace_id: str = Field(
|
||||
default=None,
|
||||
max_length=50,
|
||||
sa_column=Column(String(50), nullable=True)
|
||||
)
|
||||
org_id: str = Field(
|
||||
default=None,
|
||||
max_length=50,
|
||||
sa_column=Column(String(50), nullable=True)
|
||||
)
|
||||
level: int = Field(
|
||||
default=None,
|
||||
sa_column=Column(Integer, nullable=True)
|
||||
)
|
||||
node_type: str = Field(
|
||||
default=None,
|
||||
max_length=255,
|
||||
sa_column=Column(String(255), nullable=True)
|
||||
)
|
||||
type: str = Field(
|
||||
default=None,
|
||||
max_length=50,
|
||||
sa_column=Column(String(50), nullable=True)
|
||||
)
|
||||
canvas_style_data: str = Field(
|
||||
default=None,
|
||||
sa_column=Column(Text, nullable=True)
|
||||
)
|
||||
component_data: str = Field(
|
||||
default=None,
|
||||
sa_column=Column(Text, nullable=True)
|
||||
)
|
||||
mobile_layout: int = Field(
|
||||
default=0,
|
||||
sa_column=Column(SmallInteger, nullable=True)
|
||||
)
|
||||
status: int = Field(
|
||||
default=1,
|
||||
sa_column=Column(Integer, nullable=True)
|
||||
)
|
||||
self_watermark_status: int = Field(
|
||||
default=0,
|
||||
sa_column=Column(Integer, nullable=True)
|
||||
)
|
||||
sort: int = Field(
|
||||
default=0,
|
||||
sa_column=Column(Integer, nullable=True)
|
||||
)
|
||||
create_time: int = Field(
|
||||
default=None,
|
||||
sa_column=Column(BigInteger, nullable=True)
|
||||
)
|
||||
create_by: str = Field(
|
||||
default=None,
|
||||
max_length=255,
|
||||
sa_column=Column(String(255), nullable=True)
|
||||
)
|
||||
update_time: int = Field(
|
||||
default=None,
|
||||
sa_column=Column(BigInteger, nullable=True)
|
||||
)
|
||||
update_by: str = Field(
|
||||
default=None,
|
||||
max_length=255,
|
||||
sa_column=Column(String(255), nullable=True)
|
||||
)
|
||||
remark: str = Field(
|
||||
default=None,
|
||||
max_length=255,
|
||||
sa_column=Column(String(255), nullable=True)
|
||||
)
|
||||
source: str = Field(
|
||||
default=None,
|
||||
max_length=255,
|
||||
sa_column=Column(String(255), nullable=True)
|
||||
)
|
||||
delete_flag: int = Field(
|
||||
default=0,
|
||||
sa_column=Column(SmallInteger, nullable=True)
|
||||
)
|
||||
delete_time: int = Field(
|
||||
default=None,
|
||||
sa_column=Column(BigInteger, nullable=True)
|
||||
)
|
||||
delete_by: str = Field(
|
||||
default=None,
|
||||
max_length=255,
|
||||
sa_column=Column(String(255), nullable=True)
|
||||
)
|
||||
version: int = Field(
|
||||
default=3,
|
||||
sa_column=Column(Integer, nullable=True)
|
||||
)
|
||||
content_id: str = Field(
|
||||
default='0',
|
||||
max_length=50,
|
||||
sa_column=Column(String(50), nullable=True)
|
||||
)
|
||||
check_version: str = Field(
|
||||
default='1',
|
||||
max_length=50,
|
||||
sa_column=Column(String(50), nullable=True)
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
export const dashboardApi = {
|
||||
list: () => request.get('/dashboard/list'),
|
||||
getDashboardInfo: (params:any) => request.post(`/dashboard/get_dashboard/${params.id}`,params),
|
||||
sqNameCheck: (data: any) => request.post('/dashboard/name_check',data),
|
||||
moveResource: (data: any) => request.post('/dashboard/move',data),
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11 11V2.64097C11 2.41809 11.0232 2.33727 11.0668 2.25579C11.1104 2.17431 11.1743 2.11036 11.2558 2.06678C11.3373 2.02321 11.4181 2 11.641 2H12.359C12.5819 2 12.6627 2.02321 12.7442 2.06678C12.8257 2.11036 12.8896 2.17431 12.9332 2.25579C12.9768 2.33727 13 2.41809 13 2.64097V11H21.359C21.5819 11 21.6627 11.0232 21.7442 11.0668C21.8257 11.1104 21.8896 11.1743 21.9332 11.2558C21.9768 11.3373 22 11.4181 22 11.641V12.359C22 12.5819 21.9768 12.6627 21.9332 12.7442C21.8896 12.8257 21.8257 12.8896 21.7442 12.9332C21.6627 12.9768 21.5819 13 21.359 13H13V21.359C13 21.5819 12.9768 21.6627 12.9332 21.7442C12.8896 21.8257 12.8257 21.8896 12.7442 21.9332C12.6627 21.9768 12.5819 22 12.359 22H11.641C11.4181 22 11.3373 21.9768 11.2558 21.9332C11.1743 21.8896 11.1104 21.8257 11.0668 21.7442C11.0232 21.6627 11 21.5819 11 21.359V13H2.64097C2.41809 13 2.33727 12.9768 2.25579 12.9332C2.17431 12.8896 2.11036 12.8257 2.06678 12.7442C2.02321 12.6627 2 12.5819 2 12.359V11.641C2 11.4181 2.02321 11.3373 2.06678 11.2558C2.11036 11.1743 2.17431 11.1104 2.25579 11.0668C2.33727 11.0232 2.41809 11 2.64097 11H11Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -10,15 +10,19 @@ export const dashboardStore = defineStore('dashboard', {
|
||||
curComponentId: null,
|
||||
canvasStyle: {},
|
||||
componentData: [],
|
||||
fullscreenFlag: false,
|
||||
dashboardInfo: {
|
||||
id: null,
|
||||
name: 'New Dashboard',
|
||||
name: null,
|
||||
pid: null,
|
||||
dataState: null,
|
||||
optType: null,
|
||||
workspaceId: null,
|
||||
status: null,
|
||||
type: null,
|
||||
mobileLayout: false
|
||||
creatorName: null,
|
||||
updateName: null,
|
||||
createTime: null,
|
||||
updateTime: null,
|
||||
contentId: null
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -32,6 +36,9 @@ export const dashboardStore = defineStore('dashboard', {
|
||||
this.curComponent = value
|
||||
this.curComponentId = value && value.id ? value.id : null
|
||||
},
|
||||
setDashboardInfo(value: any) {
|
||||
this.dashboardInfo = value
|
||||
},
|
||||
setComponentData(value: any) {
|
||||
this.componentData = value
|
||||
},
|
||||
@@ -44,6 +51,9 @@ export const dashboardStore = defineStore('dashboard', {
|
||||
setTabMoveInActiveId(tabId: any) {
|
||||
this.tabMoveInActiveId = tabId
|
||||
},
|
||||
canvasDataInit(){
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div class="info-card">
|
||||
<div class="info-title">
|
||||
Dashboard ID
|
||||
</div>
|
||||
<div class="info-content">{{ dashboardInfo.id }}</div>
|
||||
<div v-if="dashboardInfo.creatorName" class="info-title">Creator</div>
|
||||
<div v-if="dashboardInfo.creatorName" class="info-content">Creator Name</div>
|
||||
<div class="info-title">Create Time</div>
|
||||
<div class="info-content">{{ timestampFormatDate(dashboardInfo.createTime) }}</div>
|
||||
<div v-if="dashboardInfo.updateName" class="info-title">Updator</div>
|
||||
<div v-if="dashboardInfo.updateName" class="info-content">Updator Name</div>
|
||||
<div class="info-title">Update Time</div>
|
||||
<div v-if="dashboardInfo.updateTime" class="info-content">
|
||||
{{ timestampFormatDate(dashboardInfo.updateTime) }}
|
||||
</div>
|
||||
<div v-if="!dashboardInfo.updateTime" class="info-content">N/A</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {dashboardStoreWithOut} from "@/stores/dashboard/dashboard.ts";
|
||||
import {storeToRefs} from "pinia";
|
||||
|
||||
const dashboardStore = dashboardStoreWithOut()
|
||||
const {dashboardInfo} = storeToRefs(dashboardStore)
|
||||
|
||||
const timestampFormatDate = (value:any) => {
|
||||
if (!value) {
|
||||
return '-'
|
||||
}
|
||||
return new Date(value).toLocaleString()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.info-card {
|
||||
font-style: normal;
|
||||
padding-left: 4px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
|
||||
.info-title {
|
||||
color: #646a73;
|
||||
font-size: 14px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.info-content {
|
||||
color: #1f2329;
|
||||
font-size: 14px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,62 @@
|
||||
<script lang="ts" setup>
|
||||
import { type PropType } from 'vue'
|
||||
import nothingInput from '@/assets/img/nothing-input.png'
|
||||
import nothingSelect from '@/assets/img/nothing-select.png'
|
||||
import nothingTable from '@/assets/img/nothing-table.png'
|
||||
import none from '@/assets/img/none.png'
|
||||
import error from '@/assets/img/error.png'
|
||||
import nothingTree from '@/assets/img/nothing-tree.png'
|
||||
import nothingNone from '@/assets/img/nothing-none.png'
|
||||
defineProps({
|
||||
imgType: {
|
||||
type: String as PropType<
|
||||
'input' | 'select' | 'table' | 'none' | 'noneWhite' | 'tree' | 'error'
|
||||
>,
|
||||
default: 'table'
|
||||
},
|
||||
imageSize: {
|
||||
type: Number,
|
||||
default: 125
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const getAssetsFile = {
|
||||
input: nothingInput,
|
||||
select: nothingSelect,
|
||||
table: nothingTable,
|
||||
noneWhite: nothingNone,
|
||||
tree: nothingTree,
|
||||
error,
|
||||
none
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-empty
|
||||
class="empty-info"
|
||||
:imageSize="imageSize"
|
||||
:description="description"
|
||||
:image="getAssetsFile[imgType]"
|
||||
>
|
||||
<slot></slot>
|
||||
</el-empty>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.empty-info {
|
||||
height: 100%;
|
||||
}
|
||||
:deep(.ed-empty__description) {
|
||||
margin-top: 8px;
|
||||
|
||||
color: var(--N600, #646a73);
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,80 @@
|
||||
<script lang="ts" setup>
|
||||
import {Icon} from '@/components/icon-custom'
|
||||
import icon_more_outlined from '@/assets/svg/icon_more_outlined.svg'
|
||||
import type {Placement} from 'element-plus-secondary'
|
||||
|
||||
export interface Menu {
|
||||
svgName?: string
|
||||
label?: string
|
||||
command: string
|
||||
divided?: boolean
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
menuList: Menu[]
|
||||
placement?: Placement
|
||||
iconName?: any
|
||||
iconSize?: string
|
||||
inTable?: boolean
|
||||
}>(),
|
||||
{
|
||||
placement: 'bottom-end',
|
||||
iconSize: '16px',
|
||||
inTable: false
|
||||
}
|
||||
)
|
||||
|
||||
const handleCommand = (command: string | number | object) => {
|
||||
emit('handleCommand', command)
|
||||
}
|
||||
|
||||
const emit = defineEmits(['handleCommand'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dropdown
|
||||
popper-class="menu-more_popper"
|
||||
:placement="placement"
|
||||
:persistent="false"
|
||||
trigger="click"
|
||||
@command="handleCommand"
|
||||
>
|
||||
<el-icon class="hover-icon" :class="inTable && 'hover-icon-in-table'" @click.stop>
|
||||
<Icon>
|
||||
<component class="svg-icon" :is="iconName || icon_more_outlined"></component>
|
||||
</Icon>
|
||||
</el-icon>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu :persistent="false">
|
||||
<template v-for="ele in menuList"
|
||||
>
|
||||
<el-dropdown-item
|
||||
:divided="ele.divided"
|
||||
:command="ele.command"
|
||||
:disabled="ele.disabled"
|
||||
>
|
||||
<el-icon class="handle-icon" :style="{ fontSize: iconSize }" v-if="ele.svgName">
|
||||
<Icon>
|
||||
<component class="svg-icon" :is="ele.svgName"></component>
|
||||
</Icon>
|
||||
</el-icon>
|
||||
{{ ele.label }}
|
||||
</el-dropdown-item>
|
||||
</template>
|
||||
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.menu-more_popper {
|
||||
margin-top: -2px !important;
|
||||
|
||||
.handle-icon {
|
||||
color: #646a73;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,478 @@
|
||||
<script setup lang="ts">
|
||||
import icon_add_outlined from '@/assets/svg/icon_add_outlined.svg'
|
||||
import { treeDraggableChart } from '@/views/dashboard/utils/treeDraggableChart'
|
||||
import dvNewFolder from '@/assets/svg/dv-new-folder.svg'
|
||||
import icon_searchOutline_outlined from '@/assets/svg/icon_search-outline_outlined.svg'
|
||||
import dvFolder from '@/assets/svg/dv-folder.svg'
|
||||
import icon_operationAnalysis_outlined from '@/assets/svg/icon_operation-analysis_outlined.svg'
|
||||
import icon_edit_outlined from '@/assets/svg/icon_edit_outlined.svg'
|
||||
import { onMounted, reactive, ref, watch, nextTick, computed } from 'vue'
|
||||
import { ElIcon, ElScrollbar } from 'element-plus-secondary'
|
||||
import { Icon } from '@/components/icon-custom'
|
||||
import { type SQTreeNode } from '@/views/dashboard/utils/treeNode'
|
||||
import _ from 'lodash'
|
||||
import { treeParentWeight } from '@/views/dashboard/utils/treeSortUtils'
|
||||
import router from '@/router'
|
||||
import {dashboardStoreWithOut} from "@/stores/dashboard/dashboard.ts";
|
||||
import HandleMore from "@/views/dashboard/common/HandleMore.vue";
|
||||
const dashboardStore = dashboardStoreWithOut()
|
||||
|
||||
|
||||
defineProps({
|
||||
curCanvasType: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
showPosition: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: 'preview'
|
||||
},
|
||||
resourceTable: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: 'core'
|
||||
}
|
||||
})
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
disabled: (data: any) => data.extraFlag1 === 0
|
||||
}
|
||||
const mounted = ref(false)
|
||||
const rootManage = ref(false)
|
||||
const selectedNodeKey:any = ref(null)
|
||||
const filterText = ref(null)
|
||||
const expandedArray = ref([])
|
||||
const resourceListTree = ref()
|
||||
const returnMounted = ref(false)
|
||||
const state = reactive({
|
||||
pWeightMap: {},
|
||||
curSortType: 'time_desc',
|
||||
resourceTree: [] as SQTreeNode[],
|
||||
originResourceTree: [] as SQTreeNode[],
|
||||
folderMenuList: [
|
||||
],
|
||||
sortType: [
|
||||
],
|
||||
templateCreatePid: 0
|
||||
})
|
||||
|
||||
|
||||
|
||||
const resourceTypeList = computed(() => {
|
||||
return []
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
const { handleDrop, allowDrop, handleDragStart } = treeDraggableChart(
|
||||
state,
|
||||
'resourceTree',
|
||||
'dashboard'
|
||||
)
|
||||
|
||||
|
||||
const routerDashboardId = router.currentRoute.value.query.dashboardId
|
||||
if (routerDashboardId) {
|
||||
selectedNodeKey.value = routerDashboardId
|
||||
returnMounted.value = true
|
||||
}
|
||||
const nodeExpand = (data:any) => {
|
||||
if (data.id) {
|
||||
// @ts-ignore
|
||||
expandedArray.value.push(data.id)
|
||||
}
|
||||
}
|
||||
|
||||
const nodeCollapse = (data:any) => {
|
||||
if (data.id) {
|
||||
// @ts-ignore
|
||||
expandedArray.value.splice(expandedArray.value.indexOf(data.id), 1)
|
||||
}
|
||||
}
|
||||
|
||||
const filterNode = (value: string, data: SQTreeNode) => {
|
||||
if (!value) return true
|
||||
return data.name?.toLocaleLowerCase().includes(value.toLocaleLowerCase())
|
||||
}
|
||||
|
||||
const nodeClick = (data: SQTreeNode, node:any) => {
|
||||
dashboardStore.setCurComponent({ component: null, index: null })
|
||||
if (node.disabled) {
|
||||
nextTick(() => {
|
||||
const currentNode = resourceListTree.value.$el.querySelector('.is-current')
|
||||
if (currentNode) {
|
||||
currentNode.classList.remove('is-current')
|
||||
}
|
||||
return
|
||||
})
|
||||
} else {
|
||||
selectedNodeKey.value = data.id
|
||||
if (data.leaf) {
|
||||
emit('nodeClick', data)
|
||||
} else {
|
||||
resourceListTree.value.setCurrentKey(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getTree = async () => {
|
||||
state.originResourceTree = []
|
||||
afterTreeInit()
|
||||
}
|
||||
// @ts-ignore
|
||||
const flattedTree = computed<SQTreeNode[]>(() => {
|
||||
return _.filter(flatTree(state.resourceTree), node => node.leaf)
|
||||
})
|
||||
|
||||
const hasData = computed<boolean>(() => flattedTree.value.length > 0)
|
||||
|
||||
function flatTree(tree: SQTreeNode[]) {
|
||||
let result = _.cloneDeep(tree)
|
||||
_.forEach(tree, node => {
|
||||
if (node.children && node.children.length > 0) {
|
||||
result = _.union(result, flatTree(node.children))
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
const afterTreeInit = () => {
|
||||
state.pWeightMap = treeParentWeight(state.originResourceTree, rootManage.value ? 9 : 0)
|
||||
mounted.value = true
|
||||
if (selectedNodeKey.value && returnMounted.value) {
|
||||
// @ts-ignore
|
||||
expandedArray.value = getDefaultExpandedKeys()
|
||||
returnMounted.value = false
|
||||
}
|
||||
nextTick(() => {
|
||||
resourceListTree.value.setCurrentKey(selectedNodeKey.value)
|
||||
resourceListTree.value.filter(filterText.value)
|
||||
nextTick(() => {
|
||||
// @ts-ignore
|
||||
document.querySelector('.is-current')?.firstChild?.click()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const copyLoading = ref(false)
|
||||
const emit = defineEmits(['nodeClick'])
|
||||
|
||||
|
||||
function createNewObject() {
|
||||
}
|
||||
// @ts-ignore
|
||||
const resourceEdit = resourceId => {
|
||||
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const getParentKeys = (tree:any, targetKey:any, parentKeys = []) => {
|
||||
for (const node of tree) {
|
||||
if (node.id === targetKey) {
|
||||
return parentKeys
|
||||
}
|
||||
if (node.children) {
|
||||
const newParentKeys = [...parentKeys, node.id]
|
||||
// @ts-ignore
|
||||
const result = getParentKeys(node.children, targetKey, newParentKeys)
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const getDefaultExpandedKeys = () => {
|
||||
const parentKeys = getParentKeys(state.resourceTree, selectedNodeKey.value)
|
||||
if (parentKeys) {
|
||||
return [selectedNodeKey.value, ...parentKeys]
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
watch(filterText, val => {
|
||||
resourceListTree.value.filter(val)
|
||||
})
|
||||
|
||||
|
||||
const loadInit = () => {}
|
||||
onMounted(() => {
|
||||
loadInit()
|
||||
getTree()
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
const addOperation = ( cmd: string, data?: SQTreeNode, nodeType?: string, parentSelect?: boolean) => {
|
||||
|
||||
}
|
||||
|
||||
|
||||
defineExpose({
|
||||
rootManage,
|
||||
hasData,
|
||||
createNewObject,
|
||||
mounted
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="resource-tree">
|
||||
<div class="tree-header">
|
||||
<div class="icon-methods" v-show="showPosition === 'preview'">
|
||||
<span class="title"> Dashboard </span>
|
||||
<div v-if="rootManage" class="flex-align-center">
|
||||
<el-tooltip :content="'New Folder'" placement="top" effect="dark">
|
||||
<el-icon
|
||||
class="custom-icon btn"
|
||||
style="margin-right: 20px"
|
||||
>
|
||||
<Icon name="dv-new-folder"><dvNewFolder class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<el-input
|
||||
:placeholder="'Search'"
|
||||
v-model="filterText"
|
||||
clearable
|
||||
class="search-bar"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<Icon name="icon_search-outline_outlined"
|
||||
><icon_searchOutline_outlined class="svg-icon"
|
||||
/></Icon>
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<el-scrollbar class="custom-tree" v-loading="copyLoading">
|
||||
<el-tree
|
||||
menu
|
||||
ref="resourceListTree"
|
||||
:default-expanded-keys="expandedArray"
|
||||
:data="state.resourceTree"
|
||||
:props="defaultProps"
|
||||
node-key="id"
|
||||
highlight-current
|
||||
:expand-on-click-node="true"
|
||||
:filter-node-method="filterNode"
|
||||
@node-expand="nodeExpand"
|
||||
@node-collapse="nodeCollapse"
|
||||
@node-click="nodeClick"
|
||||
@node-drag-start="handleDragStart"
|
||||
@node-drop="handleDrop"
|
||||
draggable
|
||||
>
|
||||
<template #default="{ data }">
|
||||
<span class="custom-tree-node" :class="{ 'node-disabled-custom': data.extraFlag1 === 0 }">
|
||||
<el-icon style="font-size: 18px" v-if="!data.leaf">
|
||||
<Icon name="dv-folder"><dvFolder class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
<el-icon
|
||||
class="icon-screen-new color-dataV"
|
||||
:class="{ 'color-dataV': data.extraFlag1, 'color-dataV-disabled': !data.extraFlag1 }"
|
||||
style="font-size: 18px"
|
||||
v-else
|
||||
>
|
||||
<Icon name="icon_operation-analysis_outlined"
|
||||
><icon_operationAnalysis_outlined class="svg-icon"
|
||||
/></Icon>
|
||||
</el-icon>
|
||||
<div class="icon-more">
|
||||
<el-icon
|
||||
v-on:click.stop
|
||||
v-if="data.leaf"
|
||||
class="hover-icon"
|
||||
@click="resourceEdit(data.id)"
|
||||
>
|
||||
<Icon><icon_edit_outlined class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
<HandleMore
|
||||
@handle-command="
|
||||
cmd => addOperation(cmd, data, cmd === 'newFolder' ? 'folder' : 'leaf')
|
||||
"
|
||||
:menu-list="resourceTypeList"
|
||||
:icon-name="icon_add_outlined"
|
||||
placement="bottom-start"
|
||||
v-if="!data.leaf"
|
||||
></HandleMore>
|
||||
</div>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
.filter-icon-span {
|
||||
border: 1px solid #bbbfc4;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 4px;
|
||||
color: #1f2329;
|
||||
padding: 8px;
|
||||
margin-left: 8px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
.opt-icon:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
&:hover {
|
||||
background: #f5f6f7;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: #eff0f1;
|
||||
}
|
||||
}
|
||||
.resource-tree {
|
||||
padding: 16px 0 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.tree-header {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.icon-methods {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: var(--TextPrimary, #1f2329);
|
||||
padding-bottom: 16px;
|
||||
.title {
|
||||
margin-right: auto;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
}
|
||||
.custom-icon {
|
||||
font-size: 20px;
|
||||
&.btn {
|
||||
color: var(--ed-color-primary);
|
||||
}
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
.search-bar {
|
||||
padding-bottom: 10px;
|
||||
width: calc(100% - 40px);
|
||||
}
|
||||
}
|
||||
.title-area {
|
||||
margin-left: 6px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.title-area-outer {
|
||||
display: flex;
|
||||
flex: 1 1 0%;
|
||||
width: 0px;
|
||||
}
|
||||
.custom-tree-node-list {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
.father .child {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.father:hover .child {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
:deep(.ed-input__wrapper) {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.custom-tree {
|
||||
height: calc(100vh - 148px);
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.custom-tree-node {
|
||||
width: calc(100% - 30px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: content-box;
|
||||
padding-right: 4px;
|
||||
|
||||
.label-tooltip {
|
||||
width: 100%;
|
||||
margin-left: 8.75px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.icon-more {
|
||||
margin-left: auto;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.label-tooltip {
|
||||
width: calc(100% - 78px);
|
||||
}
|
||||
|
||||
.icon-more {
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-screen-new {
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
padding: 3px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.menu-outer-dv_popper {
|
||||
min-width: 140px;
|
||||
margin-top: -2px !important;
|
||||
|
||||
.ed-icon {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.sort-type-normal {
|
||||
i {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.sort-type-checked {
|
||||
color: var(--ed-color-primary);
|
||||
i {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.node-disabled-custom {
|
||||
color: rgba(187, 191, 196, 1);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.color-dataV-disabled {
|
||||
background: #bbbfc4 !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,134 @@
|
||||
<script setup lang="ts">
|
||||
import dvInfoSvg from '@/assets/svg/dv-info.svg'
|
||||
import icon_pc_outlined from '@/assets/svg/icon_pc_outlined.svg'
|
||||
import icon_edit_outlined from '@/assets/svg/icon_edit_outlined.svg'
|
||||
import DashboardDetailInfo from "@/views/dashboard/common/DashboardDetailInfo.vue";
|
||||
import {dashboardStoreWithOut} from "@/stores/dashboard/dashboard.ts";
|
||||
import {storeToRefs} from "pinia";
|
||||
const emit = defineEmits(['reload', 'download', 'downloadAsAppTemplate'])
|
||||
const dashboardStore = dashboardStoreWithOut()
|
||||
const {dashboardInfo} = storeToRefs(dashboardStore)
|
||||
const preview = () => {}
|
||||
const edit = () =>{}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="preview-head flex-align-center">
|
||||
<div class="canvas-name ellipsis">{{ dashboardInfo.name }}</div>
|
||||
<el-divider style="margin: 0 16px 0 7px" direction="vertical" />
|
||||
<div class="create-area flex-align-center">
|
||||
<span style="line-height: 22px"
|
||||
>Creator:{{ dashboardInfo.creatorName }}</span
|
||||
>
|
||||
<el-popover show-arrow :offset="8" placement="bottom" width="400" trigger="hover">
|
||||
<template #reference>
|
||||
<el-icon class="info-tips"
|
||||
><Icon name="dv-info"><dvInfoSvg class="svg-icon" /></Icon
|
||||
></el-icon>
|
||||
</template>
|
||||
<DashboardDetailInfo></DashboardDetailInfo>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="canvas-opt-button">
|
||||
<el-button secondary @click="preview">
|
||||
<template #icon>
|
||||
<icon name="icon_pc_outlined"><icon_pc_outlined class="svg-icon" /></icon>
|
||||
</template>
|
||||
Preview
|
||||
</el-button>
|
||||
<el-button class="custom-button" type="primary" @click="edit">
|
||||
<template #icon>
|
||||
<icon name="icon_edit_outlined"><icon_edit_outlined class="svg-icon" /></icon>
|
||||
</template>
|
||||
Edit</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.pad12 {
|
||||
.ed-dropdown-menu__item {
|
||||
padding: 5px 36px 5px 12px !important;
|
||||
|
||||
.ed-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.arrow-right_icon {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&:has(.arrow-right_icon) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.preview-head {
|
||||
width: 100%;
|
||||
min-width: 300px;
|
||||
height: 56px;
|
||||
padding: 16px 24px;
|
||||
border-bottom: 1px solid rgba(31, 35, 41, 0.15);
|
||||
.canvas-name {
|
||||
max-width: 200px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.canvas-have-update {
|
||||
background-color: rgba(52, 199, 36, 0.2);
|
||||
color: rgba(44, 169, 31, 1);
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
vertical-align: middle;
|
||||
padding: 0 4px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.custom-icon {
|
||||
cursor: pointer;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.create-area {
|
||||
color: #646a73;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
}
|
||||
.canvas-opt-button {
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
.head-more-icon {
|
||||
color: #1f2329;
|
||||
margin-left: 12px;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
&:hover {
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
left: -4px;
|
||||
border-radius: 4px;
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
background: #1f23291a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.info-tips {
|
||||
margin-left: 4px;
|
||||
font-size: 16px;
|
||||
color: #646a73;
|
||||
}
|
||||
|
||||
.custom-button {
|
||||
margin-left: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,246 @@
|
||||
<script setup lang="ts">
|
||||
import icon_add_outlined from '@/assets/svg/icon_add_outlined.svg'
|
||||
import {reactive, ref, toRefs, onBeforeMount, computed} from 'vue'
|
||||
import {initCanvasData} from '@/views/dashboard/utils/canvasUtils'
|
||||
import {Icon} from '@/components/icon-custom'
|
||||
import ResourceTree from "@/views/dashboard/common/ResourceTree.vue";
|
||||
import SQPreview from "@/views/dashboard/preview/SQPreview.vue";
|
||||
import SQPreviewHead from "@/views/dashboard/preview/SQPreviewHead.vue";
|
||||
import EmptyBackground from "@/views/dashboard/common/EmptyBackground.vue";
|
||||
import {dashboardStoreWithOut} from "@/stores/dashboard/dashboard.ts";
|
||||
|
||||
const dashboardStore = dashboardStoreWithOut()
|
||||
const previewCanvasContainer = ref(null)
|
||||
const dashboardPreview = ref(null)
|
||||
const slideShow = ref(true)
|
||||
const dataInitState = ref(true)
|
||||
const downloadStatus = ref(false)
|
||||
const state = reactive({
|
||||
canvasDataPreview: null,
|
||||
canvasStylePreview: null,
|
||||
canvasViewInfoPreview: null,
|
||||
dashboardInfo: null,
|
||||
showOffset: {
|
||||
top: 110,
|
||||
left: 280
|
||||
}
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
showPosition: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: 'preview'
|
||||
},
|
||||
noClose: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const {showPosition} = toRefs(props)
|
||||
|
||||
const resourceTreeRef = ref()
|
||||
|
||||
const hasTreeData = computed(() => {
|
||||
return resourceTreeRef.value?.hasData
|
||||
})
|
||||
|
||||
const rootManage = computed(() => {
|
||||
return resourceTreeRef.value?.rootManage
|
||||
})
|
||||
const mounted = computed(() => {
|
||||
return resourceTreeRef.value?.mounted
|
||||
})
|
||||
|
||||
|
||||
function createNew() {
|
||||
resourceTreeRef.value?.createNewObject()
|
||||
}
|
||||
|
||||
const loadCanvasData = (dashboardId: string) => {
|
||||
dataInitState.value = false
|
||||
//@ts-ignore
|
||||
initCanvasData({dashboardId},function ({dashboardInfo, canvasDataResult, canvasStyleResult, canvasViewInfoPreview }) {
|
||||
state.canvasDataPreview = canvasDataResult
|
||||
state.canvasStylePreview = canvasStyleResult
|
||||
state.canvasViewInfoPreview = canvasViewInfoPreview
|
||||
state.dashboardInfo = dashboardInfo
|
||||
dataInitState.value = true
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const slideOpenChange = () => {
|
||||
slideShow.value = !slideShow.value
|
||||
}
|
||||
|
||||
const getPreviewStateInfo = () => {
|
||||
return state
|
||||
}
|
||||
|
||||
const reload = (dashboardId:string) => {
|
||||
loadCanvasData(dashboardId)
|
||||
}
|
||||
|
||||
const resourceNodeClick = (data:any) => {
|
||||
loadCanvasData(data.id)
|
||||
}
|
||||
|
||||
const previewShowFlag = computed(() => !!dashboardStore.dashboardInfo?.name)
|
||||
|
||||
onBeforeMount(() => {
|
||||
if (showPosition.value === 'preview') {
|
||||
dashboardStore.canvasDataInit()
|
||||
}
|
||||
})
|
||||
const sideTreeStatus = ref(true)
|
||||
// @ts-ignore
|
||||
const changeSideTreeStatus = val => {
|
||||
sideTreeStatus.value = val
|
||||
}
|
||||
|
||||
const freezeStyle = computed(() => [
|
||||
{'--top-show-offset': state.showOffset.top},
|
||||
{'--left-show-offset': state.showOffset.left}
|
||||
])
|
||||
|
||||
defineExpose({
|
||||
getPreviewStateInfo
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dv-preview dv-teleport-query" :style="freezeStyle">
|
||||
<el-aside
|
||||
class="resource-area"
|
||||
:class="{ 'close-side': !slideShow, retract: !sideTreeStatus }"
|
||||
ref="node"
|
||||
style="width: 280px"
|
||||
>
|
||||
<resource-tree
|
||||
ref="resourceTreeRef"
|
||||
v-show="slideShow"
|
||||
:cur-canvas-type="'dashboard'"
|
||||
:show-position="showPosition"
|
||||
@node-click="resourceNodeClick"
|
||||
/>
|
||||
</el-aside>
|
||||
<el-container
|
||||
class="preview-area"
|
||||
:class="{ 'no-data': !state.dashboardInfo }"
|
||||
v-loading="!dataInitState"
|
||||
>
|
||||
<template v-if="previewShowFlag">
|
||||
<SQPreviewHead
|
||||
v-if="showPosition === 'preview'"
|
||||
@reload="reload"
|
||||
/>
|
||||
<div
|
||||
ref="previewCanvasContainer"
|
||||
class="content"
|
||||
id="sq-preview-content"
|
||||
>
|
||||
<SQPreview
|
||||
ref="dashboardPreview"
|
||||
v-if="state.canvasStylePreview && dataInitState"
|
||||
:dv-info="state.dashboardInfo"
|
||||
:component-data="state.canvasDataPreview"
|
||||
:canvas-style-data="state.canvasStylePreview"
|
||||
:canvas-view-info="state.canvasViewInfoPreview"
|
||||
:show-position="showPosition"
|
||||
:download-status="downloadStatus"
|
||||
:show-linkage-button="false"
|
||||
></SQPreview>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="hasTreeData && mounted">
|
||||
<EmptyBackground :description="'Please Select Resource'" img-type="select"/>
|
||||
</template>
|
||||
<template v-else-if="mounted">
|
||||
<EmptyBackground :description="'No Resource'" img-type="none">
|
||||
<el-button v-if="rootManage" @click="createNew" type="primary">
|
||||
<template #icon>
|
||||
<Icon name="icon_add_outlined">
|
||||
<icon_add_outlined class="svg-icon"/>
|
||||
</Icon>
|
||||
</template>
|
||||
Create Dashboard
|
||||
</el-button>
|
||||
</EmptyBackground>
|
||||
</template>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.dv-preview {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
background: #ffffff;
|
||||
position: relative;
|
||||
|
||||
.resource-area {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 279px;
|
||||
padding: 0;
|
||||
border-right: 1px solid #d7d7d7;
|
||||
overflow: visible;
|
||||
|
||||
&.retract {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
//transition: 0.5s;
|
||||
|
||||
&.no-data {
|
||||
background-color: rgba(245, 246, 247, 1);
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.close-side {
|
||||
width: 0px !important;
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.flexible-button-area {
|
||||
position: absolute;
|
||||
height: 60px;
|
||||
width: 16px;
|
||||
left: 0;
|
||||
top: calc(50% - 30px);
|
||||
background-color: #ffffff;
|
||||
border-radius: 0 4px 4px 0;
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-top: 1px solid #d7d7d7;
|
||||
border-right: 1px solid #d7d7d7;
|
||||
border-bottom: 1px solid #d7d7d7;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,24 @@
|
||||
import {dashboardApi} from "@/api/dashboard.ts";
|
||||
|
||||
export const initCanvasData = (params: any, callBack: Function) => {
|
||||
dashboardApi.getDashboardInfo(params).then((res: any) => {
|
||||
const canvasInfo = res.data;
|
||||
const dashboardInfo = {
|
||||
id: canvasInfo.id,
|
||||
name: canvasInfo.name,
|
||||
pid: canvasInfo.pid,
|
||||
workspaceId: canvasInfo.workspaceId,
|
||||
status: canvasInfo.status,
|
||||
type: canvasInfo.type,
|
||||
creatorName: canvasInfo.creatorName,
|
||||
updateName: canvasInfo.updateName,
|
||||
createTime: canvasInfo.createTime,
|
||||
updateTime: canvasInfo.updateTime,
|
||||
contentId: canvasInfo.contentId
|
||||
}
|
||||
const canvasDataResult = JSON.parse(canvasInfo.componentData)
|
||||
const canvasStyleResult = JSON.parse(canvasInfo.canvasStyleData)
|
||||
const canvasViewInfoPreview = canvasInfo.canvasViewInfo
|
||||
callBack({dashboardInfo, canvasDataResult, canvasStyleResult, canvasViewInfoPreview})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import {dashboardApi} from "@/api/dashboard.ts";
|
||||
const treeDraggableChart = (state:any, key:any, type:any) => {
|
||||
let dragNodeParentId = ''
|
||||
let dragNodeId = ''
|
||||
let dragNodeIndex = 0
|
||||
|
||||
let rawData:any[] = []
|
||||
|
||||
const dfsTreeNode = (arr:any, parentId:any) => {
|
||||
arr.forEach((element:any, index:any) => {
|
||||
if (element.id === dragNodeId) {
|
||||
dragNodeIndex = index
|
||||
dragNodeParentId = parentId
|
||||
}
|
||||
if (element.children?.length) {
|
||||
dfsTreeNode(element.children, element.id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const dfsTreeNodeSaveLevel = (arr:any[], idArr:any) => {
|
||||
arr.forEach((element:any) => {
|
||||
const index = idArr.findIndex((ele:any) => {
|
||||
return ele === element.id
|
||||
})
|
||||
if (index !== -1) {
|
||||
idArr.splice(index, 1)
|
||||
}
|
||||
if (element.children?.length && idArr.length === 2) {
|
||||
dfsTreeNodeSaveLevel(element.children, idArr)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const dfsTreeNodeBack = (arr:any, parentId:any, params:any) => {
|
||||
arr.forEach((element:any) => {
|
||||
if (element.id === params.id) {
|
||||
params.pid = parentId
|
||||
}
|
||||
if (element.children?.length) {
|
||||
dfsTreeNodeBack(element.children, element.id, params)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const dfsTreeNodeReset = (arr:any, node:any) => {
|
||||
arr.forEach((element:any) => {
|
||||
if (element.id === dragNodeParentId) {
|
||||
element.children.splice(dragNodeIndex, 0, node)
|
||||
}
|
||||
if (element.children?.length) {
|
||||
dfsTreeNodeReset(element.children, node)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const dfsTreeNodeDel = (arr:any, node:any) => {
|
||||
arr.forEach((element:any, index:any) => {
|
||||
if (element.id === node.id) {
|
||||
arr.splice(index, 1)
|
||||
}
|
||||
if (element.children?.length) {
|
||||
dfsTreeNodeDel(element.children, node)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleDragStart = (node:any) => {
|
||||
dragNodeId = node.data.id
|
||||
rawData = cloneDeep(state[key])
|
||||
dfsTreeNode(state[key], '0')
|
||||
}
|
||||
|
||||
const allowDrop = (dropNode:any) => {
|
||||
return !dropNode.data?.leaf
|
||||
}
|
||||
|
||||
const handleDrop = async (draggingNode:any, dropNode:any, dropType:any) => {
|
||||
const params = {
|
||||
id: draggingNode.data?.id,
|
||||
name: draggingNode.data?.name,
|
||||
nodeType: draggingNode.data?.leaf ? 'leaf' : 'folder',
|
||||
pid: '0',
|
||||
opt: 'move',
|
||||
type
|
||||
}
|
||||
if (dropType !== 'inner') {
|
||||
const idArr = [params.id, dropNode.data?.id]
|
||||
dfsTreeNodeSaveLevel(rawData, idArr)
|
||||
if (idArr.length === 0) {
|
||||
dfsTreeNodeDel(state[key], draggingNode.data)
|
||||
setTimeout(() => {
|
||||
if (dragNodeParentId === '0') {
|
||||
state[key].splice(dragNodeIndex, 0, draggingNode.data)
|
||||
} else {
|
||||
dfsTreeNodeReset(state[key], draggingNode.data)
|
||||
}
|
||||
}, 0)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await dashboardApi.sqNameCheck(params)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
// @ts-ignore
|
||||
delete params.opt
|
||||
|
||||
if (dropType === 'inner') {
|
||||
params.pid = dropNode.data?.id
|
||||
} else {
|
||||
dfsTreeNodeBack(state[key], '0', params)
|
||||
}
|
||||
|
||||
dashboardApi.moveResource(params)
|
||||
.then(() => {
|
||||
state.originResourceTree = cloneDeep(state[key])
|
||||
})
|
||||
.catch(() => {
|
||||
if (dragNodeParentId === '0') {
|
||||
state[key].splice(dragNodeIndex, 0, draggingNode.data)
|
||||
return
|
||||
}
|
||||
|
||||
dfsTreeNodeReset(state[key], draggingNode.data)
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
handleDrop,
|
||||
allowDrop,
|
||||
handleDragStart
|
||||
}
|
||||
}
|
||||
|
||||
export { treeDraggableChart }
|
||||
@@ -0,0 +1,19 @@
|
||||
export interface SQTreeNode {
|
||||
id: string | number
|
||||
pid: string | number
|
||||
name: string
|
||||
leaf?: boolean
|
||||
weight: number
|
||||
ext?: number
|
||||
extraFlag: number
|
||||
extraFlag1: number
|
||||
children?: SQTreeNode[]
|
||||
}
|
||||
|
||||
export interface SQTreeRequest {
|
||||
treeFlag?: string
|
||||
leaf?: boolean
|
||||
weight?: number
|
||||
sortType?: string
|
||||
resourceTable?: string
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import _ from 'lodash'
|
||||
import type {SQTreeNode} from "@/views/dashboard/utils/treeNode";
|
||||
|
||||
export function treeParentWeight(tree: SQTreeNode[], pWeight:any) {
|
||||
const pWeightResult = {}
|
||||
weightCheckCircle(tree, pWeightResult, pWeight)
|
||||
return pWeightResult
|
||||
}
|
||||
|
||||
export function weightCheckCircle(tree: SQTreeNode[], pWeightResult:any, pWeight:any) {
|
||||
_.forEach(tree, node => {
|
||||
pWeightResult[node.id] = pWeight
|
||||
if (node.children && node.children.length > 0) {
|
||||
weightCheckCircle(node.children, pWeightResult, node.weight)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export default function treeSort(tree: SQTreeNode[], sortType: string) {
|
||||
const result = _.cloneDeep(tree)
|
||||
sortCircle(result, sortType)
|
||||
return result
|
||||
}
|
||||
|
||||
export function sortCircle(tree: SQTreeNode[], sortType: string) {
|
||||
sortPer(tree, sortType)
|
||||
_.forEach(tree, node => {
|
||||
if (node.children && node.children.length > 0) {
|
||||
sortCircle(node.children, sortType)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const sortPer = (subTree: SQTreeNode[], sortType: string) => {
|
||||
if (sortType === 'name_desc') {
|
||||
subTree.sort((a, b) => b.name.localeCompare(a.name, 'zh-Hans-CN', { sensitivity: 'accent' }))
|
||||
} else if (sortType === 'name_asc') {
|
||||
subTree.sort((a, b) => a.name.localeCompare(b.name, 'zh-Hans-CN', { sensitivity: 'accent' }))
|
||||
} else if (sortType === 'time_asc') {
|
||||
subTree.reverse()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user