Browse Source

数据资产模块

master
xueyinfei 3 months ago
parent
commit
991d4a5da4
  1. 110
      vue-fastapi-frontend/src/api/dataAsset/directory.js
  2. 336
      vue-fastapi-frontend/src/views/dataAsset/assetDetail/index.vue
  3. 171
      vue-fastapi-frontend/src/views/dataAsset/directory/components/AssetMoveDialog.vue
  4. 227
      vue-fastapi-frontend/src/views/dataAsset/directory/components/FormDialog.vue
  5. 175
      vue-fastapi-frontend/src/views/dataAsset/directory/components/MergerDialog.vue
  6. 173
      vue-fastapi-frontend/src/views/dataAsset/directory/components/MoveDialog.vue
  7. 592
      vue-fastapi-frontend/src/views/dataAsset/directory/index.vue

110
vue-fastapi-frontend/src/api/dataAsset/directory.js

@ -0,0 +1,110 @@
import request from '@/utils/request'
export function getDirectoryTree(params) {
return request({
url: '/default-api/system/data_catalog/list',
method: 'get',
params,
})
}
export function getDirectory(id) {
return request({
url: `/default-api/system/data_catalog/${id}`,
method: 'get',
})
}
export function addDirectory(data) {
return request({
url: '/default-api/system/data_catalog',
method: 'post',
data,
})
}
export function updateDirectory(data) {
return request({
url: `/default-api/system/data_catalog/edit`,
method: 'put',
data,
})
}
export function delDirectory(id) {
return request({
url: `/default-api/system/data_catalog/${id}`,
method: 'delete',
})
}
export function addDirectoryCollection(data) {
return request({
url: '/default-api/system/data_catalog/bookmark ',
method: 'post',
data,
})
}
export function cancelDirectoryCollection(id) {
return request({
url: `/default-api/system/data_catalog/bookmark/${id}`,
method: 'delete',
})
}
export function delDirectoryCollection(data) {
return request({
url: '/default-api/system/delete_data_asset_collection',
method: 'delete',
data,
})
}
export function moveDirectory(data) {
return request({
url: '/default-api/system/data_catalog/moved',
method: 'put',
data,
})
}
export function mergeDirectory(data) {
return request({
url: '/default-api/system/data_catalog/merge',
method: 'put',
data,
})
}
export function delDirectoryAsset(data) {
return request({
url: '/default-api/system/data_catalog/removerel',
method: 'put',
data,
})
}
export function moveDirectoryAsset(data) {
return request({
url: '/default-api/system/data_catalog/moverel',
method: 'put',
data,
})
}
export function getDirectoryAsset(params) {
return request({
url: '/default-api/system/data_catalog/atree',
method: 'get',
params,
})
}
export function getHtmlString(params) {
return request({
url: '/default-api/system/data_catalog/indx/list',
method: 'get',
params,
})
}

336
vue-fastapi-frontend/src/views/dataAsset/assetDetail/index.vue

@ -0,0 +1,336 @@
<template>
<div class="app-container">
<el-row :gutter="16">
<el-col :span="5">
<el-card shadow="never">
<el-input
v-model="filterText"
style="width: 100%"
placeholder="搜索系统名称"
>
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
<el-tree
style="margin-top: 10px"
ref="treeRef"
node-key="id"
:default-expand-all="true"
:highlight-current="true"
:expand-on-click-node="false"
:data="treeData"
:props="defaultProps"
:filter-node-method="filterNode"
:current-node-key="currentNode.id"
@node-click="handleNodeClick"
>
<template #default="{ node, data }">
<el-space :size="2">
<el-icon v-if="!data.isLeaf"><Folder /></el-icon>
<el-icon v-else><Document /></el-icon>
<span>{{ node.label }}</span>
</el-space>
</template>
</el-tree>
</el-card>
</el-col>
<el-col :span="19">
<el-form
:model="queryParams"
ref="queryRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="名称">
<el-input
v-model="queryParams.name"
placeholder="请输入名称"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="类型">
<el-input
v-model="queryParams.name"
placeholder="请输入类型"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="标签">
<el-input
v-model="queryParams.name"
placeholder="请输入标签"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery"
>搜索</el-button
>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Plus"
@click="handleAdd"
v-hasPermi="['dataAsset:assetDetail:add']"
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="Edit"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['dataAsset:assetDetail:edit']"
>修改</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['dataAsset:assetDetail:remove']"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermi="['dataAsset:assetDetail:export']"
>导出</el-button
>
</el-col>
<right-toolbar
v-model:showSearch="showSearch"
@queryTable="getList"
:columns="columns"
></right-toolbar>
</el-row>
<el-table
v-loading="loading"
:data="list"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="50" align="center" />
<el-table-column
label="编号"
align="center"
key=""
prop=""
v-if="columns[0].visible"
/>
<el-table-column
label="来源系统"
align="center"
key=""
prop=""
v-if="columns[1].visible"
/>
<el-table-column
label="英文名称"
align="center"
key=""
prop=""
v-if="columns[2].visible"
/>
<el-table-column
label="中文名称"
align="center"
key=""
prop=""
v-if="columns[3].visible"
/>
<el-table-column
label="类型"
align="center"
key=""
prop=""
v-if="columns[4].visible"
/>
<el-table-column
label="描述"
align="center"
key=""
prop=""
v-if="columns[5].visible"
/>
<el-table-column
label="标签"
align="center"
key=""
prop=""
v-if="columns[6].visible"
/>
<el-table-column
label="负责人"
align="center"
key=""
prop=""
v-if="columns[7].visible"
/>
<el-table-column
label="建立时间"
align="center"
key=""
prop=""
v-if="columns[8].visible"
/>
<el-table-column
label="更新时间"
align="center"
key=""
prop=""
v-if="columns[9].visible"
/>
<el-table-column
label="操作"
align="center"
width="100"
class-name="small-padding fixed-width"
>
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button
link
type="primary"
icon="Edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['dataAsset:assetDetail:edit']"
></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button
link
type="primary"
icon="Delete"
@click="handleDelete(scope.row)"
v-hasPermi="['dataAsset:assetDetail:remove']"
></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</el-col>
</el-row>
</div>
</template>
<script setup name="AssetDetail">
const { proxy } = getCurrentInstance()
const defaultProps = {
children: 'children',
label: 'name',
}
const currentNode = ref({})
const treeData = ref([
{
catalogId: '-1',
name: '系统',
isLeaf: 0,
children: [
{
parentId: '-1',
id: '1',
name: 'wind',
isLeaf: 1,
},
{
parentId: '-1',
id: '2',
name: 'O32',
isLeaf: 1,
},
],
},
])
const handleNodeClick = (data) => {
currentNode.value = data
}
const filterText = ref(undefined)
const treeRef = ref(null)
watch(filterText, (val) => {
treeRef.value.filter(val)
})
const filterNode = (value, data) => {
if (!value) {
return true
}
return data.catalogName.includes(value)
}
const columns = ref([
{ key: 0, label: `编号`, visible: true },
{ key: 1, label: `来源系统`, visible: true },
{ key: 2, label: `英文名称`, visible: true },
{ key: 3, label: `中文名称`, visible: true },
{ key: 4, label: `类型`, visible: true },
{ key: 5, label: `描述`, visible: true },
{ key: 6, label: `标签`, visible: true },
{ key: 7, label: `负责人`, visible: true },
{ key: 8, label: `建立时间`, visible: true },
{ key: 9, label: `更新时间`, visible: true },
])
const loading = ref(false)
const list = ref([])
const total = ref(0)
const showSearch = ref(true)
const queryParams = ref({
pageNum: 1,
pageSize: 10,
name: undefined,
})
const getList = () => {}
const handleQuery = () => {
queryParams.value.pageNum = 1
getList()
}
const resetQuery = () => {
proxy.resetForm('queryRef')
handleQuery()
}
const handleAdd = () => {}
const handleUpdate = (row) => {}
const handleDelete = (row) => {}
const handleExport = () => {}
const ids = ref([])
const single = ref(true)
const multiple = ref(true)
const handleSelectionChange = (selection) => {
ids.value = selection.map((item) => item.userId)
single.value = selection.length != 1
multiple.value = !selection.length
}
</script>

171
vue-fastapi-frontend/src/views/dataAsset/directory/components/AssetMoveDialog.vue

@ -0,0 +1,171 @@
<template>
<el-dialog width="800px" append-to-body :title="title" v-model="open">
<el-form label-width="100px" ref="formRef" :model="form" :rules="rules">
<el-row :gutter="16">
<el-col :span="11">
<el-form-item label="当前资产" prop="dataAstCnName">
<el-input :disabled="true" v-model="form.dataAstCnName" />
</el-form-item>
<el-form-item label="当前资产简介" prop="dataAstDesc">
<el-input
placeholder="自动带入"
type="textarea"
:disabled="true"
:rows="8"
v-model="form.dataAstDesc"
/>
</el-form-item>
</el-col>
<el-col :span="2">
<div class="arrow">
<span>········</span>
<el-icon><Right /></el-icon>
</div>
</el-col>
<el-col :span="11">
<el-form-item label="目标目录" prop="contentOnumAfter">
<el-tree-select
check-strictly
value-key="contentOnum"
placeholder="选择目标目录"
:default-expand-all="true"
:disabled="disabled"
:clearable="true"
:data="localDirectoryTree"
:props="{
value: 'contentOnum',
label: 'contentName',
children: 'children',
}"
v-model="form.contentOnumAfter"
@node-click="handleTargetCatalogNodeClick"
/>
</el-form-item>
<el-form-item label="目标目录简介" prop="contentIntrAfter">
<el-input
placeholder="自动带入"
type="textarea"
:disabled="true"
:rows="8"
v-model="form.contentIntrAfter"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" :disabled="disabled" @click="submitForm"
>确定</el-button
>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { nextTick } from 'vue'
import { moveDirectoryAsset } from '@/api/dataAsset/directory'
const props = defineProps({
directoryTree: {
type: Array,
required: true,
},
})
const filterTree = (tree, conditionFn) => {
return tree
.map((node) => {
//
const filteredChildren = node.children
? filterTree(node.children, conditionFn)
: []
//
if (conditionFn(node)) {
//
return {
...node,
children: filteredChildren,
}
}
// null
return null
})
.filter(Boolean) // null
}
const localDirectoryTree = computed(() => {
const tree = props.directoryTree
return filterTree(tree, (node) => node.contentOnum && !node.astOnum) //
})
const title = ref('')
const open = ref(false)
const disabled = ref(false)
const { proxy } = getCurrentInstance()
const form = ref({})
const rules = ref({
targetContentOnum: [
{ required: true, message: '目标目录不能为空', trigger: 'blur' },
],
})
const formRef = ref(null)
const openDialog = (row) => {
open.value = true
form.value = {
relaOnum: undefined,
contentOnum: undefined,
contentOnumAfter: undefined,
}
if (row.relaOnum) {
form.value = {
...form.value,
...row,
}
}
nextTick(() => {
formRef.value.clearValidate()
})
}
const handleTargetCatalogNodeClick = (data) => {
form.value = {
...form.value,
contentIntrAfter: data.contentIntr,
}
}
const emit = defineEmits(['onSuccess'])
const submitForm = () => {
formRef.value.validate((valid) => {
if (valid) {
moveDirectoryAsset(form.value).then((response) => {
proxy.$modal.msgSuccess('移动成功')
open.value = false
emit('onSuccess')
})
}
})
}
const cancel = () => {
open.value = false
}
defineExpose({ title, disabled, openDialog })
</script>
<style lang="scss" scoped>
.arrow {
display: flex;
font-size: 18px;
text-align: center;
margin: 8px auto;
span {
line-height: 18px;
}
}
</style>

227
vue-fastapi-frontend/src/views/dataAsset/directory/components/FormDialog.vue

@ -0,0 +1,227 @@
<template>
<el-dialog width="600px" append-to-body :title="title" v-model="open">
<el-form label-width="100px" ref="formRef" :model="form" :rules="rules">
<el-form-item label="目录名称" prop="contentName">
<el-input
placeholder="请输入目录名称"
:disabled="disabled"
v-model="form.contentName"
/>
</el-form-item>
<el-form-item label="上级目录" prop="suprContentOnum">
<el-tree-select
check-strictly
value-key="contentOnum"
placeholder="请选择上级目录"
:default-expand-all="true"
:disabled="disabled"
:clearable="true"
:data="localDirectoryTree"
:props="{
value: 'contentOnum',
label: 'contentName',
children: 'children',
}"
v-model="form.suprContentOnum"
>
</el-tree-select>
</el-form-item>
<el-form-item label="负责人" prop="contentPic">
<el-input
placeholder="请输入负责人"
:disabled="disabled"
v-model="form.contentPic"
/>
</el-form-item>
<el-form-item label="关联资产" prop="assets">
<el-tree-select
filterable
multiple
show-checkbox
value-key="id"
placeholder="请选择关联资产"
:default-expand-all="true"
:render-after-expand="false"
:disabled="disabled"
:clearable="true"
:data="assetTree"
:props="{
value: 'id',
label: 'dataAssetCatalogName',
children: 'children',
}"
v-model="form.assets"
>
<template #default="{ data }">
<div class="custom-tree-node">
<span>{{
data.dataAssetCatalogName || data.dataAssetSysName
}}</span>
</div>
</template>
</el-tree-select>
</el-form-item>
<el-form-item label="目录简介" prop="contentIntr">
<el-input
placeholder="请输入目录简介"
type="textarea"
:disabled="disabled"
:rows="8"
v-model="form.contentIntr"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" :disabled="disabled" @click="submitForm"
>确定</el-button
>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { computed, nextTick } from 'vue'
import {
getDirectory,
addDirectory,
updateDirectory,
getDirectoryAsset,
} from '@/api/dataAsset/directory'
const props = defineProps({
directoryTree: {
type: Array,
required: true,
},
})
const filterTree = (tree, conditionFn) => {
return tree
.map((node) => {
//
const filteredChildren = node.children
? filterTree(node.children, conditionFn)
: []
//
if (conditionFn(node)) {
//
return {
...node,
children: filteredChildren,
}
}
// null
return null
})
.filter(Boolean) // null
}
const localDirectoryTree = computed(() => {
const tree = props.directoryTree
return filterTree(tree, (node) => node.contentOnum && !node.astOnum) //
})
const title = ref('')
const open = ref(false)
const disabled = ref(false)
const { proxy } = getCurrentInstance()
const form = ref({})
const rules = ref({
contentName: [
{ required: true, message: '目录名称不能为空', trigger: 'blur' },
],
suprContentOnum: [
{ required: true, message: '上级目录不能为空', trigger: 'blur' },
],
})
const formRef = ref(null)
const openDialog = (row) => {
open.value = true
setAssetTree()
form.value = {
contentName: undefined,
suprContentOnum: undefined,
contentPic: undefined,
contentStat: '1', // 0-1-2-
contentIntr: undefined,
children: [],
}
if (row.contentOnum || row.suprContentOnum) {
form.value = {
...form.value,
...row,
assets:
row.children &&
row.children.length &&
row.children.find((i) => i.astOnum)
? [...row.children].map((i) => i.astOnum)
: [], //
}
}
nextTick(() => {
formRef.value.clearValidate()
})
}
const addTreeNodeId = (tree) => {
return tree.map((node, index) => {
return {
...node,
id: node.dataAssetCatalogAstno || index,
children:
node.children && node.children.length
? addTreeNodeId(node.children)
: [],
}
})
}
const assetTree = ref([])
const setAssetTree = () => {
getDirectoryAsset().then(({ data }) => {
assetTree.value = addTreeNodeId(data)
})
}
const emit = defineEmits(['onSuccess'])
const submitForm = () => {
formRef.value.validate((valid) => {
if (valid) {
const children = form.value.assets.reduce((arr, cur) => {
const item = form.value.children.find((i) => i.astOnum === cur)
if (!item) {
arr.push({
contentOnum: form.value.contentOnum,
astOnum: cur,
})
} else {
arr.push(item)
}
return arr
}, [])
form.value = {
...form.value,
children,
}
const request = form.value.contentOnum ? updateDirectory : addDirectory
request(form.value).then((response) => {
proxy.$modal.msgSuccess(
form.value.contentOnum ? '修改成功' : '新增成功'
)
open.value = false
emit('onSuccess')
})
}
})
}
const cancel = () => {
open.value = false
}
defineExpose({ title, disabled, openDialog })
</script>

175
vue-fastapi-frontend/src/views/dataAsset/directory/components/MergerDialog.vue

@ -0,0 +1,175 @@
<template>
<el-dialog width="800px" append-to-body :title="title" v-model="open">
<el-form label-width="100px" ref="formRef" :model="form" :rules="rules">
<el-row :gutter="16">
<el-col :span="11">
<el-form-item label="当前目录" prop="contentName">
<el-input :disabled="true" v-model="form.contentName" />
</el-form-item>
<el-form-item label="当前目录简介" prop="contentIntr">
<el-input
placeholder="自动带入"
type="textarea"
:disabled="true"
:rows="8"
v-model="form.contentIntr"
/>
</el-form-item>
</el-col>
<el-col :span="2">
<div class="arrow">
<span>········</span>
<el-icon><Right /></el-icon>
</div>
</el-col>
<el-col :span="11">
<el-form-item label="目标目录" prop="contentOnumAfter">
<el-tree-select
check-strictly
value-key="contentOnum"
placeholder="选择目标目录"
:default-expand-all="true"
:disabled="disabled"
:clearable="true"
:data="localDirectoryTree"
:props="{
value: 'contentOnum',
label: 'contentName',
children: 'children',
}"
v-model="form.contentOnumAfter"
@node-click="handleTargetCatalogNodeClick"
/>
</el-form-item>
<el-form-item label="目标目录简介" prop="contentIntrAfter">
<el-input
placeholder="自动带入"
type="textarea"
:disabled="true"
:rows="8"
v-model="form.contentIntrAfter"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" :disabled="disabled" @click="submitForm"
>确定</el-button
>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { nextTick } from 'vue'
import { mergeDirectory } from '@/api/dataAsset/directory'
const props = defineProps({
directoryTree: {
type: Array,
required: true,
},
})
const filterTree = (tree, conditionFn) => {
return tree
.map((node) => {
//
const filteredChildren = node.children
? filterTree(node.children, conditionFn)
: []
//
if (conditionFn(node)) {
//
return {
...node,
children: filteredChildren,
}
}
// null
return null
})
.filter(Boolean) // null
}
const localDirectoryTree = computed(() => {
const tree = props.directoryTree
return filterTree(tree, (node) => node.contentOnum && !node.astOnum) //
})
const title = ref('')
const open = ref(false)
const disabled = ref(false)
const { proxy } = getCurrentInstance()
const form = ref({})
const rules = ref({
contentOnumAfter: [
{ required: true, message: '目标目录不能为空', trigger: 'blur' },
],
})
const formRef = ref(null)
const openDialog = (row) => {
open.value = true
form.value = {
contentOnum: undefined,
suprContentOnum: undefined,
contentIntr: undefined,
contentOnumAfter: undefined,
suprContentOnumAfter: undefined,
contentIntrAfter: undefined,
}
if (row.contentOnum) {
form.value = {
...form.value,
...row,
}
}
nextTick(() => {
formRef.value.clearValidate()
})
}
const handleTargetCatalogNodeClick = (data) => {
form.value = {
...form.value,
suprContentOnumAfter: data.suprContentOnum,
contentIntrAfter: data.contentIntr,
}
}
const emit = defineEmits(['onSuccess'])
const submitForm = () => {
formRef.value.validate((valid) => {
if (valid) {
mergeDirectory(form.value).then((response) => {
proxy.$modal.msgSuccess('合并成功')
open.value = false
emit('onSuccess')
})
}
})
}
const cancel = () => {
open.value = false
}
defineExpose({ title, disabled, openDialog })
</script>
<style lang="scss" scoped>
.arrow {
display: flex;
font-size: 18px;
text-align: center;
margin: 8px auto;
span {
line-height: 18px;
}
}
</style>

173
vue-fastapi-frontend/src/views/dataAsset/directory/components/MoveDialog.vue

@ -0,0 +1,173 @@
<template>
<el-dialog width="800px" append-to-body :title="title" v-model="open">
<el-form label-width="100px" ref="formRef" :model="form" :rules="rules">
<el-row :gutter="16">
<el-col :span="11">
<el-form-item label="当前目录" prop="contentName">
<el-input :disabled="true" v-model="form.contentName" />
</el-form-item>
<el-form-item label="当前目录简介" prop="contentIntr">
<el-input
placeholder="自动带入"
type="textarea"
:disabled="true"
:rows="8"
v-model="form.contentIntr"
/>
</el-form-item>
</el-col>
<el-col :span="2">
<div class="arrow">
<span>········</span>
<el-icon><Right /></el-icon>
</div>
</el-col>
<el-col :span="11">
<el-form-item label="目标目录" prop="suprContentOnumAfter">
<el-tree-select
check-strictly
value-key="contentOnum"
placeholder="选择目标目录"
:default-expand-all="true"
:disabled="disabled"
:clearable="true"
:data="localDirectoryTree"
:props="{
value: 'contentOnum',
label: 'contentName',
children: 'children',
}"
v-model="form.suprContentOnumAfter"
@node-click="handleTargetCatalogNodeClick"
/>
</el-form-item>
<el-form-item label="目标目录简介" prop="contentIntrAfter">
<el-input
placeholder="自动带入"
type="textarea"
:disabled="true"
:rows="8"
v-model="form.contentIntrAfter"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" :disabled="disabled" @click="submitForm"
>确定</el-button
>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { nextTick } from 'vue'
import { moveDirectory } from '@/api/dataAsset/directory'
const props = defineProps({
directoryTree: {
type: Array,
required: true,
},
})
const filterTree = (tree, conditionFn) => {
return tree
.map((node) => {
//
const filteredChildren = node.children
? filterTree(node.children, conditionFn)
: []
//
if (conditionFn(node)) {
//
return {
...node,
children: filteredChildren,
}
}
// null
return null
})
.filter(Boolean) // null
}
const localDirectoryTree = computed(() => {
const tree = props.directoryTree
return filterTree(tree, (node) => node.contentOnum && !node.astOnum) //
})
const title = ref('')
const open = ref(false)
const disabled = ref(false)
const { proxy } = getCurrentInstance()
const form = ref({})
const rules = ref({
suprContentOnumAfter: [
{ required: true, message: '目标目录不能为空', trigger: 'blur' },
],
})
const formRef = ref(null)
const openDialog = (row) => {
open.value = true
form.value = {
contentOnum: undefined,
contentIntr: undefined,
suprContentOnum: undefined,
suprContentOnumAfter: undefined,
contentIntrAfter: undefined,
}
if (row.contentOnum) {
form.value = {
...form.value,
...row,
}
}
nextTick(() => {
formRef.value.clearValidate()
})
}
const handleTargetCatalogNodeClick = (data) => {
form.value = {
...form.value,
contentIntrAfter: data.contentIntr,
}
}
const emit = defineEmits(['onSuccess'])
const submitForm = () => {
formRef.value.validate((valid) => {
if (valid) {
moveDirectory(form.value).then((response) => {
proxy.$modal.msgSuccess('移动成功')
open.value = false
emit('onSuccess')
})
}
})
}
const cancel = () => {
open.value = false
}
defineExpose({ title, disabled, openDialog })
</script>
<style lang="scss" scoped>
.arrow {
display: flex;
font-size: 18px;
text-align: center;
margin: 8px auto;
span {
line-height: 18px;
}
}
</style>

592
vue-fastapi-frontend/src/views/dataAsset/directory/index.vue

@ -0,0 +1,592 @@
<template>
<div class="app-container">
<el-row :gutter="16">
<el-col :span="5">
<el-card shadow="never">
<el-input
v-model="filterText"
style="width: 100%"
placeholder="搜索目录名称"
>
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
<div class="tree-box">
<el-tree
class="tree"
ref="treeRef"
node-key="tempId"
:default-expand-all="true"
:highlight-current="true"
:expand-on-click-node="false"
:data="directoryTree"
:props="defaultProps"
:filter-node-method="filterNode"
:current-node-key="currentNode.tempId"
@node-click="handleNodeClick"
>
<template #default="{ data }">
<div class="custom-tree-node">
<el-space :size="2">
<el-icon v-if="data.contentOnum && !data.astOnum"
><Folder
/></el-icon>
<el-icon v-else><Document /></el-icon>
<span>{{ data.contentName || data.dataAstCnName }}</span>
</el-space>
<div
v-if="!isCollectionDirectory(data)"
class="tree-node__action"
>
<template v-if="isAsset(data)">
<el-button
v-if="!isCollected(data)"
link
type="warning"
icon="Star"
@click="(e) => handleCollect(data, e)"
></el-button>
<el-button
v-else
link
type="warning"
style="margin-right: -2px"
@click="(e) => handleCollectionCancel(data, e)"
>
<el-icon slot="icon" size="18" color="#E6A23C">
<StarFilled />
</el-icon>
</el-button>
</template>
<el-dropdown
v-if="
!isCollection(data) &&
(isDirectory(data) || isRoot(data)) &&
hasPermiOr([
'dataAsset:directory:add',
'dataAsset:directory:edit',
'dataAsset:directory:remove',
'dataAsset:directory:move',
'dataAsset:directory:merge',
])
"
placement="right-start"
@command="(command) => handleCommand(command, data)"
>
<el-button
style="margin-left: 4px"
link
type="primary"
icon="Menu"
></el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-if="
isRoot(data) &&
hasPermiOr(['dataAsset:directory:add'])
"
command="handleAddDialogOpen"
>
新增目录
</el-dropdown-item>
<template v-if="isDirectory(data)">
<el-dropdown-item
v-if="hasPermiOr(['dataAsset:directory:add'])"
command="handleAddDialogOpen"
>
新增目录
</el-dropdown-item>
<el-dropdown-item
v-if="hasPermiOr(['dataAsset:directory:edit'])"
command="handleEditDialogOpen"
>
修改目录
</el-dropdown-item>
<el-dropdown-item
v-if="hasPermiOr(['dataAsset:directory:remove'])"
command="handleDelete"
>
删除目录
</el-dropdown-item>
<el-dropdown-item
v-if="hasPermiOr(['dataAsset:directory:move'])"
command="handleMoveDialogOpen"
>
移动目录
</el-dropdown-item>
<el-dropdown-item
v-if="hasPermiOr(['dataAsset:directory:merge'])"
command="handleMergerDialogOpen"
>
合并目录
</el-dropdown-item>
</template>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-dropdown
v-if="
!isCollection(data) &&
isAsset(data) &&
hasPermiOr([
'dataAsset:asset:remove',
'dataAsset:asst:move',
])
"
placement="right-start"
@command="(command) => handleCommand(command, data)"
>
<el-button
style="margin-left: 4px"
link
type="primary"
icon="Menu"
></el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-if="hasPermiOr(['dataAsset:asset:remove'])"
command="handleAssetDelete"
>
删除资产
</el-dropdown-item>
<el-dropdown-item
v-if="hasPermiOr(['dataAsset:asst:move'])"
command="handleAssetMoveDialogOpen"
>
移动资产
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</template>
</el-tree>
</div>
</el-card>
</el-col>
<el-col :span="19">
<el-card shadow="never">
<template v-if="currentNode.contentOnum && !currentNode.astOnum">
<el-descriptions
:title="currentNode.contentName"
:column="3"
border
>
<el-descriptions-item label="简介">
{{ currentNode.contentIntr }}
</el-descriptions-item>
</el-descriptions>
<el-row style="margin-top: 20px" :gutter="20">
<el-col :span="12">
<el-table :data="directoryTableData" border style="width: 100%">
<el-table-column
prop="contentName"
width="160"
:label="`${tableHeaderLabel}名称`"
>
<template #default="{ row }">
{{ row.contentName || row.dataAstCnName }}
</template>
</el-table-column>
<el-table-column
prop="contentIntr"
:label="`${tableHeaderLabel}简介`"
>
<template #default="{ row }">
{{ row.contentIntr || row.dataAstDesc }}
</template>
</el-table-column>
</el-table>
</el-col>
<el-col :span="12">
<el-card class="html-box" shadow="never">
<iframe
ref="iframe"
:srcdoc="htmlContent"
:style="iframeStyle"
></iframe>
</el-card>
</el-col>
</el-row>
</template>
<template v-if="currentNode.astOnum">
<el-descriptions
:title="currentNode.dataAstCnName"
:column="3"
border
>
<el-descriptions-item label="简介">
{{ currentNode.dataAstDesc }}
</el-descriptions-item>
</el-descriptions>
<el-tabs style="margin-top: 8px" v-model="activeName">
<el-tab-pane label="资产字段" name="1">
<el-table :data="[]" border>
<el-table-column type="index" label="序号" width="60" />
<el-table-column prop="" label="字段中文名" />
<el-table-column prop="" label="字段英文名" />
<el-table-column prop="" label="字段类型" />
<el-table-column prop="" label="枚举" />
<el-table-column prop="" label="有值率" />
<el-table-column prop="" label="说明" />
</el-table>
</el-tab-pane>
<el-tab-pane label="样例数据" name="2">
<el-table :data="[]" border>
<el-table-column type="index" label="序号" width="60" />
<el-table-column prop="" label="股票代码" />
<el-table-column prop="" label="股票名称" />
<el-table-column prop="" label="股票价格" />
</el-table>
</el-tab-pane>
<el-tab-pane label="常见问题" name="3">
<div class="faq">
<el-text>{{ faq }}</el-text>
</div>
</el-tab-pane>
</el-tabs>
</template>
</el-card>
</el-col>
</el-row>
<FormDialog
ref="formDialogRef"
:directoryTree="directoryTree"
@onSuccess="setDirectoryTree"
/>
<MoveDialog
ref="moveDialogRef"
:directoryTree="directoryTree"
@onSuccess="setDirectoryTree"
/>
<MergerDialog
ref="mergerDialogRef"
:directoryTree="directoryTree"
@onSuccess="setDirectoryTree"
/>
<AssetMoveDialog
ref="assetMoveDialogRef"
:directoryTree="directoryTree"
@onSuccess="setDirectoryTree"
/>
</div>
</template>
<script setup name="Directory">
import { ElMessage, ElMessageBox } from 'element-plus'
import {
getDirectoryTree,
delDirectory,
getDirectory,
getHtmlString,
delDirectoryAsset,
addDirectoryCollection,
cancelDirectoryCollection,
} from '@/api/dataAsset/directory'
import auth from '@/plugins/auth'
import FormDialog from './components/FormDialog.vue'
import MoveDialog from './components/MoveDialog.vue'
import MergerDialog from './components/MergerDialog.vue'
import AssetMoveDialog from './components/AssetMoveDialog.vue'
import useUserStore from '@/store/modules/user'
import { nextTick } from 'vue'
const { proxy } = getCurrentInstance()
const { hasPermiOr } = auth
const userStore = useUserStore()
const defaultProps = {
children: 'children',
label: 'contentName',
}
const directoryTree = ref([])
const currentNode = ref({})
const directoryTableData = ref([])
const tableHeaderLabel = computed(() => {
if (currentNode.value.leafNodeFlag === 1) {
return '资产'
}
return '目录'
})
/** 增加临时ID作为树节点的唯一键值 */
const addTreeNodeId = (tree) => {
return tree.map((node) => {
return {
...node,
tempId: node.astOnum || node.contentOnum,
children:
node.children && node.children.length
? addTreeNodeId(node.children)
: [],
}
})
}
const htmlContent = ref('')
const setHtmlContent = async (data) => {
return getHtmlString(data).then((res) => {
htmlContent.value = res
})
}
const setDirectoryTree = () => {
return getDirectoryTree({
pageSize: 999,
}).then(({ rows }) => {
directoryTree.value = addTreeNodeId(rows)
})
}
setDirectoryTree().then(async () => {
if (directoryTree.value.length) {
currentNode.value = directoryTree.value[0]
directoryTableData.value = directoryTree.value[0].children || []
await setHtmlContent(currentNode.value)
setTimeout(() => {
setIframeSize()
}, 300)
}
})
const filterText = ref(undefined)
const treeRef = ref(null)
watch(filterText, (val) => {
treeRef.value.filter(val)
})
const filterNode = (value, data) => {
if (!value) {
return true
}
if (data.contentName) {
return data.contentName.includes(value)
}
if (data.dataAstCnName) {
return data.dataAstCnName.includes(value)
}
}
//
const isRoot = (data) => {
return data.contentOnum === 1
}
//
const isCollectionDirectory = (data) => {
return data.contentName === '我的收藏'
}
//
const isCollection = (data) => {
return false
}
//
const isCollected = (data) => {
return data.bookmarkFlag === 1
}
//
const isDirectory = (data) => {
return data.contentOnum && !isRoot(data) && !data.astOnum
}
//
const isAsset = (data) => {
return data.astOnum
}
const activeName = ref('1')
const handleNodeClick = async (data) => {
if (isCollectionDirectory(data)) {
return
}
activeName.value = '1'
currentNode.value = {
...data,
}
directoryTableData.value = data.children
if (!data.astOnum) {
await setHtmlContent(data)
setTimeout(() => {
setIframeSize()
}, 300)
}
}
const handleCollect = (data, e) => {
e.stopPropagation()
addDirectoryCollection({
dataAstNo: String(data.dataAstNo),
userId: String(userStore.id),
}).then(() => {
proxy.$modal.msgSuccess('收藏成功')
setDirectoryTree()
})
}
const handleCollectionCancel = (data, e) => {
e.stopPropagation()
cancelDirectoryCollection(data.relaOnum).then(() => {
proxy.$modal.msgSuccess('取消成功')
setDirectoryTree()
})
}
const formDialogRef = ref(null)
const handleAddDialogOpen = (data) => {
formDialogRef.value.title = '新增目录'
formDialogRef.value.openDialog({
suprContentOnum: data.contentOnum,
})
}
const handleEditDialogOpen = (data) => {
formDialogRef.value.title = '修改目录'
formDialogRef.value.openDialog(data)
}
const handleDelete = (data) => {
ElMessageBox.confirm(
`确定删除 ${data.contentName} 目录及其资产关系吗?`,
'目录删除',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
delDirectory(data.contentOnum).then(() => {
proxy.$modal.msgSuccess('删除成功')
setDirectoryTree()
})
})
}
const moveDialogRef = ref(null)
const handleMoveDialogOpen = (data) => {
moveDialogRef.value.title = '移动目录'
moveDialogRef.value.openDialog(data)
}
const mergerDialogRef = ref(null)
const handleMergerDialogOpen = (data) => {
mergerDialogRef.value.title = '合并目录'
mergerDialogRef.value.openDialog(data)
}
const handleAssetDelete = (data) => {
ElMessageBox.confirm(`确定删除 ${data.dataAstCnName} 资产吗?`, '资产删除', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
delDirectoryAsset({
...data,
relaStatus: '0', // 0-1-
}).then(() => {
proxy.$modal.msgSuccess('删除成功')
setDirectoryTree()
})
})
}
const assetMoveDialogRef = ref(null)
const handleAssetMoveDialogOpen = (data) => {
assetMoveDialogRef.value.title = '移动资产'
assetMoveDialogRef.value.openDialog(data)
}
const handleCommand = (command, data) => {
const strategy = {
handleAddDialogOpen: handleAddDialogOpen,
handleEditDialogOpen: handleEditDialogOpen,
handleDelete: handleDelete,
handleMoveDialogOpen: handleMoveDialogOpen,
handleMergerDialogOpen: handleMergerDialogOpen,
handleAssetDelete: handleAssetDelete,
handleAssetMoveDialogOpen: handleAssetMoveDialogOpen,
}
strategy[command](data)
}
const faq = `1、常见问题1\n2、常见问题2\n3、常见问题3`
const iframeStyle = ref({
width: '100%',
height: '100%',
})
const iframe = ref(null)
const setIframeSize = () => {
const content =
iframe.value.contentDocument || iframe.value.contentWindow.document
const width = Math.max(
content.body.scrollWidth,
content.documentElement.scrollWidth,
content.body.offsetWidth,
content.documentElement.offsetWidth,
content.body.clientWidth,
content.documentElement.clientWidth
)
const height = Math.max(
content.body.scrollHeight,
content.documentElement.scrollHeight,
content.body.offsetHeight,
content.documentElement.offsetHeight,
content.body.clientHeight,
content.documentElement.clientHeight
)
console.log('width', width)
console.log('height', height)
iframeStyle.value = {
width: `${width}px`,
height: `${height}px`,
}
}
</script>
<style lang="scss" scoped>
.tree-box {
overflow: auto;
}
.tree {
margin-top: 10px;
min-width: 260px;
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
.tree-node__action {
padding: 0 8px;
display: flex;
justify-content: flex-end;
}
}
:deep(
.el-descriptions__body
.el-descriptions__table.is-bordered
.el-descriptions__cell
) {
width: 80px !important;
}
.faq {
white-space: pre-wrap;
}
.html-box {
:deep(.el-card__body) {
overflow: auto;
}
}
iframe {
border: none;
}
</style>
Loading…
Cancel
Save