6326d5ed by lxs

全景地图地址修改

2 parents b0d49d5c 3625e1e3
Showing 53 changed files with 15878 additions and 56 deletions
......@@ -83,6 +83,9 @@ VITE_APP_ATTACHMENT_TEMPLATE = ms-daop-personel-service
#人员服务
VITE_APP_PERSONAL_URL = 'ms-daop-personel-service'
#元数据标准
VITE_APP_STANDARD_URL = 'ms-daop-meta-standard-service'
#流通平台地址
VITE_APP_CIRCULATION = https://daop-lt-test.zgsjzc.com/
# 测试环境访问地址
......@@ -91,7 +94,6 @@ VITE_APP_CIRCULATION = https://daop-lt-test.zgsjzc.com/
#数据加工交付
VITE_APP_DATA_DELIVERY = https://daop-jgjf-test.zgsjzc.com/
# 本地访问地址
# VITE_API_CIRCULATION_URL = http://localhost:9000/circulation
......
......@@ -110,6 +110,9 @@ VITE_API_ASSET_BASEURL = ms-swzl-data-dam-service
#数据同步接口地址
VITE_API_DATA_SYNC = ms-swzl-data-sync-service
#元数据标准
VITE_APP_STANDARD_URL = 'ms-daop-meta-standard-service'
#消息接口
VITE_API_MESSAGE = ms-daop-message-service
......
......@@ -27,6 +27,7 @@ declare module '@vue/runtime-core' {
FileUpload: typeof import('./src/components/FileUpload/index.vue')['default']
FixedActionBar: typeof import('./src/components/FixedActionBar/index.vue')['default']
Form: typeof import('./src/components/Form/index.vue')['default']
GraphTopbar: typeof import('./src/components/RelationNetwork/graphTopbar.vue')['default']
Hour: typeof import('./src/components/Schedule/component/hour.vue')['default']
ImagePreview: typeof import('./src/components/ImagePreview/index.vue')['default']
ImagesUpload: typeof import('./src/components/ImagesUpload/index.vue')['default']
......@@ -41,6 +42,7 @@ declare module '@vue/runtime-core' {
PageNav: typeof import('./src/components/PageNav/index.vue')['default']
PcasCascader: typeof import('./src/components/PcasCascader/index.vue')['default']
Popover: typeof import('./src/components/Popover/index.vue')['default']
RelationNetwork: typeof import('./src/components/RelationNetwork/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Schedule: typeof import('./src/components/Schedule/index.vue')['default']
......
......@@ -551,4 +551,9 @@ export const getContractDetail = (params) => request({
url: `https://zcgl.zgsjzc.com/circulation/api/ms-data-circulation-tx-mgr-service/data-contract/detail`,
method: 'get',
params
})
// 获取所有字典
export const getDictAllList = () => request({
url: `${import.meta.env.VITE_APP_CONFIG_URL}/dict/data/get-all`
})
\ No newline at end of file
......
......@@ -248,4 +248,298 @@ export const syncChangeDetail = (guid) => request({
export const delMetaDataSource = (databaseGuid) => request({
url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-collect-task/del-by-database-guid?databaseGuid=${databaseGuid}`,
method: 'delete',
})
/** 元数据-标准代码分页查询 */
export const getStandardCodeList = (params) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/page-list`,
method: 'post',
data: params
})
/** 元数据-标准代码新增 */
export const saveStandardCode = (params) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/save`,
method: 'post',
data: params
})
/** 元数据-标准代码修改 */
export const updateStandardCode = (params) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/update`,
method: 'put',
data: params
})
/** 元数据-标准代码详情 */
export const getStandardCodeDetail = (guid) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/detail?guid=${guid}`,
method: 'get',
})
/** 元数据-标准代码删除 */
export const deleteStandardCode = (params) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/delete`,
method: 'delete',
data: params
})
/** 元数据-查询标准列表 */
export const getStandardCodeStandard = (standardTypeCode) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/get-standard?standardTypeCode=${standardTypeCode}`,
method: 'get'
})
/** 元数据-查询代码列表 */
export const getStandardCodeDataList = (params) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/data/get-data`,
method: 'post',
data: params
})
/** 元数据-查询代码字段数据 */
export const getStandardCodeFields = (standardGuid) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/get-field?standardGuid=${standardGuid}`,
method: 'get'
})
/** 元数据-保存标准代码数据 */
export const saveStandardCodeFieldsData = (params) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/data/save-data`,
method: 'post',
data: params
})
/** 元数据-删除标准代码数据 */
export const deleteStandardCodeFieldsData = (params) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/data/remove-data`,
method: 'post',
data: params
})
/** 元数据-导出 */
export const exportStandardCodeData = (params) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/data/data-export`,
method: 'post',
data: params,
responseType: 'blob'
})
/** 元数据标准-树形列表 */
export const getMetaStandardTree = () => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/list-tree`,
method: 'get'
})
/** 元数据标准-新增 */
export const saveMetaStandard = (params) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/save`,
method: 'post',
data: params
})
/** 元数据标准-修改 */
export const updateMetaStandard = (params) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/update`,
method: 'put',
data: params
})
/** 元数据标准-删除 */
export const deleteMetaStandard = (params) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/delete`,
method: 'delete',
data: params
})
/** 元数据标准-详情 */
export const getMetaStandardDetail = (guid) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/detail?guid=${guid}`,
method: 'get'
})
/** 元数据标准-标准数据 */
export const getMetaStandardDataList = (params) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/data/page-list`,
method: 'post',
data: params
})
/** 元数据标准-标准字段 */
export const getMetaStandardDataFields = (metaStandardGuid) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/get-standard-field?metaStandardGuid=${metaStandardGuid}`,
method: 'get'
})
/** 元数据标准-标准字段保存 */
export const saveMetaStandardDataFields = (params) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/data/save-or-update`,
method: 'post',
data: params
})
/** 元数据标准-标准字段删除 */
export const deleteMetaStandardDataFields = (params) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/data/del`,
method: 'delete',
data: params
})
/** 元数据标准-导出 */
export const exportMetaStandardData = (params) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/data/data-export`,
method: 'post',
data: params,
responseType: 'blob'
})
/** 标准代码-树形表 */
export const getStandardCodeTree = () => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/code-tree`,
method: 'get'
})
/** 元数据标准树形列表查询 */
export const getMetaStandardTreeList = (guid) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/list-tree?metaStandardGuid=${guid}`,
method: 'get'
})
/** 元数据标准guid查询只展示的字段 */
export const getMetaStandardField = (guid) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/data/list-by-meta-standard-guid?metaStandardGuid=${guid}`,
method: 'get'
})
/** 元数据标准字段详情 */
export const getMetaStandardFieldsDetail = (guid) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/data/detail?guid=${guid}`,
method: 'get'
})
/** 根据元数据标准展示字段去获取未展示的详情信息 */
export const getMetaStandardFieldDetail = (guid) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/data/convert-detail?guid=${guid}`,
method: 'get'
})
/** 获取桑基图数据 */
export const getSankeyData = (guid) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/sankey-data?metaStandardGuid=${guid}`,
method: 'get'
})
/** 数仓目录树列表查询 */
export const getDataWareCatalogList = (params) => request({
// url: `${import.meta.env.VITE_APP_DATA_DELIVERY}delivery/ms-daop-data-plan-service/data-catalog-directory/tree-list`,
url: `${import.meta.env.VITE_API_BASEURL}delivery/api/ms-daop-data-plan-service/data-catalog-directory/tree-list`,
method: 'post',
data: params
})
export const getAllFlowData = (dictType) => request({
url: `${import.meta.env.VITE_APP_CONFIG_URL}/dict/data/get-by-dictType?dictType=${dictType}`,
method: 'get',
})
/** 元数据标准guid查询新建数据集字段 */
export const getMetaStandardDsField = (guid) => request({
url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/get-standard?metaStandardGuid=${guid}`,
method: 'get'
})
/** 表分类类型 */
export const tableCategoryList = [
{
value: 1,
label: "明细表",
},
{
value: 2,
label: "汇总表",
},
{
value: 3,
label: "应用表",
},
{
value: 6,
label: "业务表",
},
// {
// value: 4,
// label: "维度表",
// },
// {
// value: 5,
// label: "缓慢变化维",
// }
];
/** 同步策略 */
export const syncPolicys = [
{
value: 1,
label: "实时",
},
{
value: 2,
label: "增量",
},
{
value: 3,
label: "全量",
},
{
value: 4,
label: "增量加更新",
},
];
/** 维表类型 */
export const dimTypeList = [{
label: "列表",
value: 1,
},
{
label: "层级",
value: 2,
},
{
label: "螺旋",
value: 3,
},
{
label: "通用",
value: 4,
}];
/** 表模型分类 */
export const tableModels = [
{
label: "主键模型",
value: 1,
},
{
label: "聚合模型",
value: 2,
},
{
label: "明细模型",
value: 3,
}
];
/** 聚合方式 */
export const aggMethodList = [{
value: 'SUM',
}, {
value: 'MAX'
}, {
value: 'MIN'
}, {
value: 'REPLACE'
}, {
value: 'REPLACE_IF_NOT_NULL'
}, {
value: 'HLL_UNION'
}, {
value: 'BITMAP_UNION'
}];
/** 保存主题表设置,直接入库 */
export const saveSubjectTable = (params) => request({
url: `${import.meta.env.VITE_API_BASEURL}delivery/api/ms-daop-data-plan-service/data-catalog-subject/add`,
method: 'post',
data: params
})
/** 获取字典列表 */
export const getDictionary = (params) => request({
url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/data-dictionary-general/list-all?state=1`,
method: 'post'
})
/** 获取维度列表 */
export const getDimList = () => request({
url: `${import.meta.env.VITE_API_BASEURL}delivery/api/ms-daop-data-plan-service/data-catalog-subject/get-dim-list`,
method: 'get'
})
\ No newline at end of file
......
import request from "@/utils/request";
//获取需求表树形列表
export const getDemandTreeList = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-menu/page-list`,
method: "post",
data: params,
});
};
//获取所有需求表列表
export const getDemandAll = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-menu/get-list-data`,
method: "get",
params
});
};
//新增需求列表
export const saveDemandTree = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-menu/save`,
method: "post",
data: params,
});
};
//修改需求列表
export const updateDemandTree = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-menu/update`,
method: "post",
data: params,
});
};
// 删除需求列表
export const deleteDemandTree = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-menu/delete`,
method: "delete",
data: params,
});
};
//获取需求表
export const getDemandList = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-table/page-list`,
method: "post",
data: params,
});
};
//获取需求表详情
export const getDemandDetail = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-table/detail`,
method: "get",
params,
});
};
//新增需求表
export const saveDemand = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-table/save`,
method: "post",
data: params,
});
};
//修改需求表
export const updateDemand = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-table/update`,
method: "post",
data: params,
});
};
// 删除需求表
export const deleteDemand = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-table/delete`,
method: "delete",
data: params,
});
};
// 获取疾病列表
export const getDiseaseList = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/disease-manage/page-list`,
method: "post",
data: params,
});
};
//获取所有疾病列表
export const getDiseaseAll = () => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/disease-manage/get-list-data`,
method: "post"
});
};
// 获取疾病详情
export const getDiseaseDetail = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/disease-manage/detail`,
method: "get",
params,
});
};
// 新增疾病
export const saveDisease = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/disease-manage/save`,
method: "post",
data: params,
});
};
// 修改疾病
export const updateDisease = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/disease-manage/update`,
method: "post",
data: params,
});
};
// 删除疾病
export const deleteDisease = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/disease-manage/delete`,
method: "delete",
data: params,
});
};
// 获取定价配置
export const getConfigureList = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-configure/page-list`,
method: "post",
data: params,
});
};
// 获取配置详情
export const getConfigureDetail = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-configure/detail`,
method: "get",
params,
});
};
// 新增配置
export const saveConfigure = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-configure/save`,
method: "post",
data: params,
});
};
// 修改配置
export const updateConfigure = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-configure/update`,
method: "post",
data: params,
});
};
// 删除配置
export const deleteConfigure = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-configure/delete`,
method: "delete",
data: params,
});
};
// 复制配置
export const addCopyConfigure = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-configure/copy`,
method: "post",
data: params,
});
};
// 获取数据定价
export const getPriceList = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-data/page-list`,
method: "post",
data: params,
});
};
// 获取数据定价详情
export const getPriceDetail = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-data/detail`,
method: "get",
params,
});
};
// 新增数据定价
export const savePrice = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-data/save`,
method: "post",
data: params,
});
};
// 修改数据定价
export const updatePrice = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-data/update`,
method: "post",
data: params,
});
};
// 获取数据定价结果
export const getPriceResult = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-data/pricing-price`,
method: "post",
data: params,
});
};
// 删除数据定价
export const deletePrice = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-data/delete`,
method: "delete",
data: params,
});
};
// 获取数据资源目录
export const getDamCatalogList = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/dam-catalog-table/get-table-select-new`,
method: "get",
params,
});
};
// 获取模型相关需求表
export const getModelDemand = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-data/pricing-model`,
method: "get",
params,
});
};
// 获取质量模型评分
export const getModelScore = (params) => {
return request({
url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-analysis-report/get-quality-score-by-dam-guid-v2`,
method: "get",
params,
});
};
export const exportModelScore = (params) => {
return request({
url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-data/download-template`,
method: "post",
data: params,
responseType: 'blob'
});
};
......@@ -303,3 +303,10 @@ export const getCurrentUserInfo = (params) => {
params
});
};
/** 下载文件模板 */
export const exportTemplate = (params) => request({
url: `${import.meta.env.VITE_APP_ADD_FILE}/import-config/export-template?bizGuid=${params.bizGuid}&importType=${params.importType}`,
method: 'post',
responseType: 'blob'
});
......
......@@ -1182,3 +1182,12 @@
max-width: 100%;
}
}
.el-drawer__title {
font-size: 18px;
color: #212121;
}
.el-form-item--default .el-form-item__label {
height: 26px;
line-height: 26px;
}
\ No newline at end of file
......
......@@ -44,6 +44,7 @@ const emits = defineEmits([
"uploadBtnClick",
"beforeUPload",
"uploadFile",
'deleteFile',
"onUpload",
"scheduleChange",
"cascaderChange",
......@@ -668,7 +669,7 @@ defineExpose({
<template v-else-if="con.type.indexOf('upload') > -1">
<UploadFiles ref="formUploadRef" :upload-info="con.uploadInfo" @onUpload="onUpload"
@beforeUPload="beforeUPload" @uploadFile="uploadFile" @uploadBtnClick="uploadBtnClick"
@cascaderChange="cascaderChange" @selectChange="uploadSelectChange" />
@cascaderChange="cascaderChange" @selectChange="uploadSelectChange" @deleteFile="() => emits('deleteFile')"/>
</template>
<template v-else>
<div v-if="con.formInfo.tools">
......
......@@ -458,3 +458,7 @@ const drawerClose = () => {
}
}
</style>
<style lang="scss">
</style>
......
......@@ -70,7 +70,15 @@ const onMouseOver = (str) => {
</script>
<template>
<el-tooltip class="item" placement="top" effect="light" :popper-class="popperClass ?? 'table_tooltip'" :disabled="isShowTooltip" :open-delay="400" :content="content">
<el-tooltip
class="item"
placement="top"
effect="light"
:popper-class="popperClass ?? 'table_tooltip'"
:disabled="isShowTooltip"
:open-delay="400"
:content="content"
>
<p class="over-flow" :class="className" @mouseover="onMouseOver(refName)">
<span :ref="(el) => setRefMap(el, refName)" v-html="content || '-'"></span>
</p>
......@@ -91,4 +99,10 @@ const onMouseOver = (str) => {
p {
margin: 0;
}
</style>
<style lang="scss">
.el-popper.tree-popper {
max-width: 280px!important;
}
</style>
\ No newline at end of file
......
......@@ -741,6 +741,7 @@ onMounted(() => {
toolRef.value.isFull = true
}
}, 500)
return;
}
const container: any = containerRef.value;
const width = container.clientWidth;
......
<script lang="ts" setup name="topbar">
import { ref, watch } from 'vue';
const props = defineProps({
isGraphDisplay: {
type: Boolean,
default: true
},
});
const isGraph = ref(false);
watch(() => props.isGraphDisplay, (val) => {
isGraph.value = val;
}, {
immediate: true
})
const emits = defineEmits(["displaySwitchChange"]);
const switchChange = (val) => {
isGraph.value = val
emits('displaySwitchChange', val);
}
</script>
<template>
<div className='g6-component-topbar-content'>
<div :class="isGraph ? 'selected g6-component-topbar-item' : 'g6-component-topbar-item'" @click="switchChange(true)">
关系网
</div>
<div :class="!isGraph ? 'selected g6-component-topbar-item' : 'g6-component-topbar-item'" @click="switchChange(false)">
桑基图
</div>
</div>
</template>
<style scoped lang="scss">
.g6-component-topbar-content {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background: #fff;
border: 1px solid var(--el-color-primary);
border-radius: 32px;
padding: 4px;
width: 138px;
height: 32px;
}
.g6-component-topbar-item {
width: 50%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
color: #999999;
cursor: pointer;
&.selected {
background: #4FA1A4;
border-radius: 32px;
color: #fff;
}
}
</style>
\ No newline at end of file
<template>
<div ref="containerRef" className='canvas-wrapper'>
<div class="main" ref="tooltip1Ref" style="display: none;position: absolute;" v-loading="detailLoading">
<div class="title">{{ detailInfoLabel }}</div>
<div class="row" v-for="item in Object.keys(detailInfo)">
<span>{{ item + ':' }}</span>
<span>{{ detailInfo[item] == null || detailInfo[item] == '' ? '-' : detailInfo[item] }}</span>
</div>
</div>
</div>
</template>
<script lang="ts" setup name="RelationNetwork">
import { ref, onMounted } from 'vue'
import G6 from '@antv/g6';
import insertCss from 'insert-css';
import {
getMetaStandardFieldDetail
} from '@/api/modules/dataMetaService';
import { ElMessage } from 'element-plus';
const props = defineProps({
treeData: {
type: Object,
default: {}
},
noContextMenu: { //关系查看页面,显示右键菜单定位到详情。标准查询页面,显示引用标准新建数据集
type: Boolean,
default: false
}
})
const emits = defineEmits([
'nodeItemClick',
'contextMenu'
]);
const { proxy } = getCurrentInstance() as any;
const maxChineseCount = ref(16);
const maxEnglishCount = ref(26);
const detailLoading = ref(false);
const detailInfo: any = ref({});
const detailInfoLabel = ref('');
const containerRef = ref();
const graphRef = ref();
watch(() => props.treeData, (val) => {
if (!graphRef.value && val?.guid) {
nextTick(() => {
initGraph();
})
return;
}
if (!graphRef.value) {
return;
}
tooltip1Ref.value.style.display = 'none';
if (lastSelectNode.value) {
graphRef.value.updateItem(lastSelectNode.value, {
labelCfg: {
color: '#212121'
},
style: {
stroke: '#4fa1a4',
fill: '#ebf6f7',
cursor: 'pointer'
}
});
}
lastSelectNode.value = null;
if (val) {
renderGraph(graphRef.value, val);
}
})
const renderGraph = (graph: any, lineageData: any) => {
if (!graph || !lineageData) return;
graph.setMinZoom(1);
graph.setMaxZoom(1);
graph.data(lineageData);
graph.render();
graph.fitView(40, { direction: 'both' });
graph.fitCenter();
graph.setMinZoom(0.5);
graph.setMaxZoom(5);
};
const detectLanguage = (text) => {
if (!text) {
return 'English';
}
let chineseCount = 0;
let englishCount = 0;
for (let char of text) {
if (/[\u4e00-\u9fa5]/.test(char)) {
chineseCount++;
} else if (/[a-zA-Z]/.test(char)) {
englishCount++;
}
}
if (chineseCount > englishCount) {
return 'Chinese';
} else if (englishCount > chineseCount) {
return 'English';
}
return 'English';
}
const handleLabelLength = (label: string) => {
if (detectLanguage(label) == 'English') {
return label?.length > maxEnglishCount.value ? label.slice(0, maxEnglishCount.value) + '...' : label;
}
let arr = label.split('\n');
let maxLen = Math.max(...arr.map(s => s.length));
return maxLen > maxChineseCount.value ? label.slice(0, maxChineseCount.value) + '...' : label;
};
insertCss(`
.g6-component-contextmenu {
padding: 8px 0px;
background-color: #fff;
}
.context-menu {
display: flex;
flex-direction: column;
}
.menu-item {
line-height: 32px;
color: #212121;
cursor: pointer;
padding: 0 12px;
}
.menu-item:hover {
background-color: #f5f5f5;
}
`);
const tooltip = ref();
const initGraph = () => {
const container: any = containerRef.value;
const width = container.clientWidth;
const height = container.clientHeight - 10;
tooltip.value = new G6.Tooltip({
offsetX: 10,
offsetY: 10,
trigger: 'mouseenter',
// 允许出现 tooltip 的 item 类型
itemTypes: ['node'],
// 自定义 tooltip 内容
shouldBegin: (evt: any) => {
const { item, target } = evt;
const currentAnchor = target.get('name');
const name = item._cfg.model?.label;
if (currentAnchor == 'text-shape') {
if (detectLanguage(name) == 'English') {
return name?.length > maxEnglishCount.value;
}
let arr = name.split('\n');
let maxLen = Math.max(...arr.map(s => s.length));
return maxLen > maxChineseCount.value;
}
return false;
},
getContent: (e: any) => {
const { item, target } = e;
const currentAnchor = target.get('name');
const outDiv = document.createElement('div');
outDiv.className = 'node';
outDiv.style.width = 'fit-content';
const name = item._cfg.model.isField ? item._cfg.model.metaStandardId : item._cfg.model.standardName;
if (currentAnchor == 'text-shape') {
outDiv.innerHTML = `<h4>${name}</h4>`
}
return outDiv;
},
});
const contextMenu = new G6.Menu({
getContent(evt: any) {
const { item, target } = evt;
let model = item._cfg.model;
if (props.noContextMenu) {
return `
<div class='context-menu'>
<span class='menu-item'>查看标准管理</span>
</div>`
} else {
return `
<div class='context-menu'>
<span class='menu-item'>引用标准新建数据集</span>
</div>`
}
},
shouldBegin: (evt: any) => {
const { item, target } = evt;
let model = item._cfg.model;
if (props.noContextMenu) {
// return false;
if (model && !model.isField) {
return true;
}
return false;
}
if (model && !model.isField && !model.children?.length) {
return true;
}
if (model.children?.length && model.children[0].isField) {
return true;
}
return false;
},
handleMenuClick: (target, item: any) => {
let model = item._cfg?.model;
if (!model) {
return;
}
emits('contextMenu', model);
},
// offsetX and offsetY include the padding of the parent container
// 需要加上父级容器的 padding-left 16 与自身偏移量 10
offsetX: 16,
// 需要加上父级容器的 padding-top 24 、画布兄弟元素高度、与自身偏移量 10
offsetY: 0,
// the types of items that allow the menu show up
// 在哪些类型的元素上响应
itemTypes: ['node'],
});
const graph = new G6.TreeGraph({
container: container,
width,
height,
// animate: false,
plugins: [contextMenu, tooltip.value],
fitCenter: true,
fitView: true,
fitViewPadding: 40,
minZoom: 0.7,
maxZoom: 1,
modes: {
default: [
{
type: 'collapse-expand',
onChange: (item, collapsed) => {
if (!item) {
return;
}
const data = item.getModel();
data.collapsed = collapsed;
setTimeout(() => {
graph.focusItem(item, true, {
duration: 400 // 动画时长为500ms
});
}, 500)
return true;
},
shouldBegin: (e) => {
// 若当前操作的节点 id 为 'node1',则不发生 collapse-expand
if (e.item && e.item.getModel().isLoading == true) return false;
return true;
},
},
'drag-canvas',
'zoom-canvas',
],
},
defaultNode: {
size: 16,
anchorPoints: [
[0, 0.5],
[1, 0.5],
],
style: {
// stroke: '#4fa1a4',
fill: '#ebf6f7',
cursor: 'pointer'
},
},
defaultEdge: {
type: 'cubic-horizontal',
},
layout: {
type: 'compactBox',
direction: 'LR',
getId: function getId(d) {
return d.id;
},
getHeight: function getHeight() {
return 16;
},
getWidth: function getWidth() {
return 16;
},
getVGap: function getVGap(node) {
if (node.isField) {
return 6;
}
return 20;
},
getHGap: function getHGap() {
return 120;
},
},
});
graphRef.value = graph;
graph.node((node) => {
return {
size: node.isField ? 11 : 16,
id: node.guid as string,
label: handleLabelLength((node.isField ? node.metaStandardId : node.standardName) as string),
collapsed: node.children?.length ? false : true,
labelCfg: {
offset: node.isField ? 4 : 7,
style: {
fontSize: 13,
fill: '#212121',
fontWeight: 500,
cursor: 'pointer'
},
position: !node.isField ? 'left' : 'right', //只有字段是最后一层级,不需要展开
},
style: {
stroke: '#4fa1a4',
cursor: 'pointer'
}
};
});
// data不是数组,第一级是根节点
props.treeData?.guid && graph.data(props.treeData);
graph.render();
graph.fitView(40, { direction: 'both' });
graph.fitCenter();
graph.setMinZoom(0.5);
graph.setMaxZoom(5);
bindEvents();
observeResize();
}
onMounted(() => {
nextTick(() => {
if (!graphRef.value && props.treeData?.guid) {
initGraph();
}
})
})
const observeResize = () => {
window.addEventListener('resize', (e) => {
let domWidth = document.documentElement.clientWidth;
if (lastSelectNode.value) {
tooltip1Ref.value.style.display = 'none';
graphRef.value.updateItem(lastSelectNode.value, {
labelCfg: {
style: {
fill: '#212121',
},
},
style: {
stroke: '#4fa1a4',
fill: '#ebf6f7',
cursor: 'pointer'
}
});
}
lastSelectNode.value = null;
if (domWidth < 992) {//根据setting.ts里的设置,小于992,会隐藏左边的菜单栏,
setTimeout(() => {
const container: any = containerRef.value;
const width = container.clientWidth;
const height = container.clientHeight - 10;
if (!width) {//会把隐藏的给消失。
return;
}
graphRef.value.changeSize(width, height);
graphRef.value.setMinZoom(1);
graphRef.value.setMaxZoom(1);
graphRef.value.fitView(40, { direction: 'both' });
graphRef.value.fitCenter();
graphRef.value.setMinZoom(0.5);
graphRef.value.setMaxZoom(5);
}, 500)
return;
}
const container: any = containerRef.value;
const width = container.clientWidth;
const height = container.clientHeight - 10;
if (!width) {//会把隐藏的给消失。
return;
}
graphRef.value.changeSize(width, height);
graphRef.value.setMinZoom(1);
graphRef.value.setMaxZoom(1);
graphRef.value.fitView(40, { direction: 'both' });
graphRef.value.fitCenter();
graphRef.value.setMinZoom(0.5);
graphRef.value.setMaxZoom(5);
});
}
const tooltip1Ref = ref();
// 更新tooltip的位置
function updateTooltipPosition(evt) {
var width = graphRef.value.get("width");
var height = graphRef.value.get("height");
var offsetX = 10;
var offsetY = 10;
var point = graphRef.value.getPointByClient(evt.clientX, evt.clientY);
var _a2 = graphRef.value.getCanvasByPoint(point.x, point.y), x4 = _a2.x, y4 = _a2.y;
var graphContainer = graphRef.value.getContainer();
var res = {
x: x4 + graphContainer.offsetLeft + offsetX,
y: y4 + graphContainer.offsetTop + offsetY
};
let bbox = tooltip1Ref.value.getBoundingClientRect();
let bboxHeight = bbox.height;
if (x4 + 320 + offsetX > width) {
res.x -= 320 + offsetX;
}
if (y4 + bboxHeight + offsetY > height) {
res.y -= bboxHeight + offsetY;
if (res.y < 0) {
res.y = 0;
}
}
tooltip1Ref.value.style.left = `${res.x}px`;
tooltip1Ref.value.style.top = `${res.y}px`;
tooltip1Ref.value.style.display = 'block';
}
const lastSelectNode = ref(null);
const bindEvents = () => {
let graph = graphRef.value;
if (!graph) {
return;
}
graph.on('node:click', function (evt) {
const item = evt.item;
if (!item) {
return;
}
const nodeId = item.get('guid');
const model = <any>item.getModel();
if (lastSelectNode.value) {
if (lastSelectNode.value == item) {
return;
}
tooltip1Ref.value.style.display = 'none';
graphRef.value.updateItem(lastSelectNode.value, {
labelCfg: {
style: {
fill: '#212121',
}
},
style: {
stroke: '#4fa1a4',
fill: '#ebf6f7',
cursor: 'pointer'
}
});
lastSelectNode.value = null;
}
const children = model.children;
if (children?.length && !model.isField) {
return;
}
if (model.isField) { //是字段级别,就不需要再展开了
graphRef.value.updateItem(item, {
labelCfg: {
style: {
fill: '#4fa1a4'
}
},
style: {
stroke: '#4fa1a4',
fill: '#4fa1a4',
cursor: 'pointer'
}
});
lastSelectNode.value = item;
detailInfo.value = {};
// detailInfo.value.guid = model.guid;
detailInfoLabel.value = model.metaStandardId || {};
detailLoading.value = true;
nextTick(() => {
tooltip1Ref.value.style.display = 'block';
updateTooltipPosition(evt);
getMetaStandardFieldDetail(model.guid).then((res: any) => {
if (res?.code == proxy.$passCode) {
detailInfo.value = res.data?.metaStandardValue || {};
detailInfoLabel.value = model.metaStandardId;
detailLoading.value = false;
nextTick(() => {
tooltip1Ref.value.style.display = 'block';
updateTooltipPosition(evt);
})
} else {
ElMessage.error(res.msg);
}
})
})
return;
}
evt.preventDefault();
evt.stopPropagation();
model.isLoading = true;
emits('nodeItemClick', graph, item);
});
graph.on('dragstart', (evt: any) => {
if (evt.item?.getType() == 'node') {
return;
}
// 清除状态
if (lastSelectNode.value) {
tooltip1Ref.value.style.display = 'none';
graphRef.value.updateItem(lastSelectNode.value, {
labelCfg: {
style: {
fill: '#212121',
},
},
style: {
stroke: '#4fa1a4',
fill: '#ebf6f7',
cursor: 'pointer'
}
});
}
lastSelectNode.value = null;
})
graph.on('click', (evt: any) => {
if (evt.item?.getType() == 'node') {
return;
}
// 清除状态
if (lastSelectNode.value) {
tooltip1Ref.value.style.display = 'none';
graphRef.value.updateItem(lastSelectNode.value, {
labelCfg: {
style: {
fill: '#212121',
},
},
style: {
stroke: '#4fa1a4',
fill: '#ebf6f7',
cursor: 'pointer'
}
});
}
lastSelectNode.value = null;
});
graph.on('canvas:click', (evt: any) => {
// 清除状态
if (lastSelectNode.value) {
tooltip1Ref.value.style.display = 'none';
graphRef.value.updateItem(lastSelectNode.value, {
labelCfg: {
style: {
fill: '#212121',
},
},
style: {
stroke: '#4fa1a4',
fill: '#ebf6f7',
cursor: 'pointer'
}
});
}
lastSelectNode.value = null;
});
}
const destroy = () => {
graphRef.value?.destroy();
}
defineExpose({
destroy,
});
</script>
<style lang="scss" scoped>
.canvas-wrapper {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
}
.canvas-wrapper:-webkit-full-screen {
background-color: white;
}
.canvas-wrapper:-moz-full-screen {
background-color: white;
}
.canvas-wrapper:-ms-fullscreen {
background-color: white;
}
.canvas-wrapper:fullscreen {
background-color: white;
}
.main {
padding: 16px;
box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.08);
background-color: #fff;
width: 320px;
min-height: 100px;
max-height: 400px;
overflow-y: auto;
.title {
font-size: 14px;
color: #212121;
line-height: 24px;
font-weight: 600;
}
.row {
margin-top: 8px;
font-size: 12px;
color: #666666;
word-break: break-all;
line-height: 18px;
}
}
</style>
\ No newline at end of file
......@@ -248,6 +248,7 @@ onMounted(() => {
:show-header="props.tableInfo.showHeader ?? true" stripe :border="props.tableInfo.border
?? true" :height="props.tableInfo.height === null ? null : (props.tableInfo.height ?? '100%')"
:max-height="maxHeight" :row-key="rowKey" :current-row-key="currentRowKey" :row-class-name="rowClassName"
:tree-props="props.tableInfo.treeProps" :default-expand-all="props.tableInfo.expandAll ?? false"
:expand-row-keys="props.tableInfo.expandedKey" v-loading="tableDataLoading" @row-click="rowClick"
@row-dblclick="rowDblClick" @selection-change="selectionChange" @select="tableCheckboxSelectChange"
@select-all="tableCheckboxAllSelectChange" style="width: 100%; display: inline-block"
......@@ -375,7 +376,7 @@ onMounted(() => {
<template #default="scope" v-else-if="item.type == 'approveTag'">
<el-tag v-if="scope.row[item.field]" :type="tagType(scope.row.approveVO, 'approveState')">{{
tagMethod(scope.row.approveVO, 'approveState')
}}</el-tag>
}}</el-tag>
<span v-else>{{ '--' }}</span>
</template>
<template #default="scope" v-else-if="item.type == 'tooltip'">
......@@ -480,7 +481,7 @@ onMounted(() => {
</template>
<template #default="scope" v-else-if="item.type == 'stepsBar'">
<div :class="item.className" v-if="item.stepsInfo(scope)">
<StepBar :steps-info="item.stepsInfo(scope)" />
<StepBar :steps-info="item.stepsInfo(scope)" />
</div>
<span v-else>--</span>
</template>
......
......@@ -42,20 +42,20 @@ const checkKeys = computed(() => {
return props.treeInfo.checkedKey ?? []
});
const currentNodeKey = computed(() => {
// if (props.treeInfo.currentNodeKey) {
// nextTick(() => {
// let domItems = treeRef.value?.$el.getElementsByClassName('el-tree-node');
// let clientHeight = treeRef.value?.$el.clientHeight;
// for (const item of domItems) {
// if (item.getAttribute('data-key') == props.treeInfo.currentNodeKey) {
// if (item.offsetTop > clientHeight) {
// item.scrollIntoView({ block: "end", inline: "nearest" });
// }
// break;
// }
// }
// })
// }
if (props.treeInfo.currentNodeKey) {
nextTick(() => {
let domItems = treeRef.value?.$el.getElementsByClassName('el-tree-node');
let clientHeight = treeRef.value?.$el.clientHeight;
for (const item of domItems) {
if (item.getAttribute('data-key') == props.treeInfo.currentNodeKey) {
if (item.offsetTop > clientHeight) {
item.scrollIntoView({ block: "end", inline: "nearest" });
}
break;
}
}
})
}
return props.treeInfo.currentNodeKey ?? ''
});
const customInfo = computed(() => {
......@@ -70,6 +70,9 @@ const editTreeItem = computed(() => {
const expendAll = computed(() => {
return props.treeInfo.expendAll ?? false
})
const ellipsis = computed(() => {
return props.treeInfo.ellipsis ?? true
})
interface Tree {
[key: string]: any;
......@@ -182,6 +185,21 @@ const appendChildren = (key, data) => {
const visibleNodes = ref({}); //解决点击树item,下拉菜单不消失的问题,原因是树的点击事件阻止冒泡了。
function formatNodeLabel (node) {
let sliceLength = 8
switch (node.level) {
case 2:
sliceLength = 9
break
case 3:
sliceLength = 6
break
case 4:
sliceLength = 6
break
}
return node.label.slice(0, sliceLength)
}
defineExpose({
getCheckedNodes,
getCheckedKeys,
......@@ -225,8 +243,14 @@ defineExpose({
</span>
<template v-else-if="editTreeItem && data.showEdit !== false">
<span class="list-item-text">
<ellipsis-tooltip v-if="!customInfo" :content="node.label" class-name="w100f"
<template v-if="!ellipsis">
<span>{{ formatNodeLabel(node) }}</span>
</template>
<template v-else>
<ellipsis-tooltip v-if="!customInfo" :content="node.label" class-name="w100f" popper-class="tree-popper"
:refName="'tooltipOver' + node.id"></ellipsis-tooltip>
</template>
</span>
<div class="tags-list-right" :class="visibleNodes[node.data.guid] ? 'active' : ''">
<el-popover v-model:visible="visibleNodes[node.data.guid]" placement="bottom" width="96" trigger="click"
......
......@@ -4,7 +4,7 @@ import { ElMessage, genFileId } from "element-plus";
import { Plus, Download, Upload, } from "@element-plus/icons-vue";
import type { UploadRawFile } from 'element-plus'
const emits = defineEmits(["onUpload", "beforeUPload", "uploadFile", "uploadBtnClick", "cascaderChange", "selectChange"]);
const emits = defineEmits(["onUpload", "beforeUPload", "uploadFile", "uploadBtnClick", "cascaderChange", "selectChange", 'deleteFile']);
const props = defineProps({
uploadInfo: {
type: Object,
......@@ -75,6 +75,7 @@ const btnClick = (file, type) => {
if (type == 'remove') {
const fileUpload = fileUploadRef.value[0] || fileUploadRef.value
fileUpload?.handleRemove(file)
emits('deleteFile')
} else if (type == 'upload') {
file.show = false
emits('uploadFile', file)
......@@ -111,13 +112,16 @@ defineExpose({
<p class="item_title">{{ item.title }}</p>
<div class="form_item" v-if="item.type == 'select'">
<span class="item_label" :class="{ required_mark: item.selectInfo.required }">{{ item.selectInfo.label
}}:</span>
}}:</span>
<el-select v-model="extraParams[item.selectInfo.field]" :placeholder="item.selectInfo.placeholder"
:disabled="item.selectInfo.disabled" @change="selectChange">
<el-option v-for="opts in item.selectInfo.options" :label="opts.label" :value="opts.value" />
:disabled="item.selectInfo.disabled" @change="selectChange" :filterable="item.selectInfo.filterable" :style="item.selectInfo.style">
<el-option v-for="opts in item.selectInfo.options"
:label="item.selectInfo.props?.label ? opts[item.selectInfo.props.label] : opts.label"
:value="item.selectInfo.props?.value ? opts[item.selectInfo.props.value] : opts.value"
:disabled="opts.disabled" />
</el-select>
</div>
<div class="form_item" v-if="item.type == 'tree-select'">
<div class="form_item" v-else-if="item.type == 'tree-select'">
<span class="item_label" :class="{ required_mark: item.selectInfo.required }">{{ item.selectInfo.label
}}:</span>
<el-tree-select
......@@ -176,7 +180,7 @@ defineExpose({
</el-button>
</div>
<el-upload v-else ref="fileUploadRef" class="upload_panel" :class="[props.uploadInfo.col]"
v-model:file-list="fileList" :action="item.uploadInfo.action" :auto-upload="item.uploadInfo.auto ?? ''"
v-model:file-list="fileList" :action="item.uploadInfo.action ?? ''" :auto-upload="item.uploadInfo.auto ?? ''"
:drag="item.uploadInfo.drag ?? false" :accept="item.uploadInfo.accept" :limit="item.uploadInfo.limit ?? 1"
:on-change="onUpload" :on-exceed="val => exceedFile(val, item.uploadInfo.cover ?? false)"
:on-error="handleError" :on-success="handleSuccess" :before-upload="beforeUPload"
......
......@@ -4,6 +4,7 @@ import { useRouter } from "vue-router";
import useUserStore from "@/store/modules/user";
import useKeepAliveStore from '@/store/modules/keepAlive'
import { ElMessageBox, ElMessage } from "element-plus";
import { isEqual } from "lodash-es";
const router = useRouter();
const route = useRoute();
......@@ -26,7 +27,7 @@ watch(
if (tab.fullPath.includes(jionPath)) {
pathIndex.value = index
}
return tab.fullPath === newRouter.fullPath;
return tab.fullPath === newRouter.fullPath || (tab.path == newRouter.path && isEqual(tab.query, newRouter.query));
});
if (isExist.length == 0) {
if (pathIndex.value != -1 && routerLength > 3) {
......@@ -45,11 +46,8 @@ watch(
}
}
list.map(item => {
item.fullPath = item.fullPath;
})
tabbarList.value = list;
tabbarActive.value = newRouter.fullPath;
tabbarActive.value = isExist[0]?.fullPath || newRouter.fullPath;
userStore.setTabbar(tabbarList.value);
userStore.setActiveTabbar(combPath, newRouter.fullPath);
},
......@@ -58,7 +56,7 @@ watch(
const changeTab = (pane: any, ev: any) => {
const tabIndex = Number(pane.index);
const paneData: any = tabbarList.value[tabIndex];
if (paneData.name == 'budgetDataIndex' || paneData.name == 'iframePage') {
if (paneData.name == 'budgetDataIndex' || paneData.name == 'iframePage' || paneData.name == 'portraitMap' || paneData.name == 'portraitMaps') {
router.push(paneData.fullPath);
} else {
router.push({
......
......@@ -144,7 +144,7 @@ const routes: RouteRecordRaw[] = [
{
path: 'budgetDataIndex',
name: 'budgetDataIndex',
component: () => import('@/views/data_catalog/budgetDataIndex.vue'),
component: () => import('@/views/data_meta/budgetDataIndex.vue'),
meta: {
title: '',
// sidebar: false,
......@@ -155,7 +155,7 @@ const routes: RouteRecordRaw[] = [
}
]
},
{
{
path: '/data-meta/reports',
component: Layout,
meta: {
......@@ -166,7 +166,7 @@ const routes: RouteRecordRaw[] = [
{
path: 'iframePage',
name: 'iframePage',
component: () => import('@/views/data_catalog/budgetDataIndex.vue'),
component: () => import('@/views/data_meta/budgetDataIndex.vue'),
meta: {
title: '',
sidebar: true,
......@@ -175,6 +175,19 @@ const routes: RouteRecordRaw[] = [
reuse: true,
activeMenu: '/data-meta/reports/iframePage',
},
},
{
path: 'portraitMaps',
name: 'portraitMaps',
component: () => import('@/views/data_meta/portraitMap.vue'),
meta: {
title: '全景地图',
sidebar: true,
breadcrumb: false,
cache: true,
reuse: true,
activeMenu: '/data-meta/reports/portraitMaps',
},
}
]
},
......@@ -199,6 +212,119 @@ const routes: RouteRecordRaw[] = [
},
}
]
},
{
path: '/data-meta/metadata-standard',
component: Layout,
meta: { title: '元数据标准', icon: 'ep:grid' },
children: [
{
path: '',
name: 'metadataStandard',
component: () => import('@/views/data_meta/standard.vue'),
meta: {
title: '元数据标准管理',
breadcrumb: false,
cache: true
}
},
{
path: 'standard-query',
name: 'metadataStandardQuery',
component: () => import('@/views/data_meta/standard-query.vue'),
meta: {
title: '元数据标准查询',
breadcrumb: false,
cache: true
}
},
{
path: 'standard-query-view',
name: 'metadataStandardQueryView',
component: () => import('@/views/data_meta/standard-query-view.vue'),
meta: {
title: '元数据标准查看',
breadcrumb: false,
cache: true,
activeMenu: '/data-meta/metadata-standard'
},
beforeEnter: (to, from) => {
if (to.query.name) {
to.meta.title = `元数据标准查看-${to.query.name}`;
}
}
},
{
path: 'table-create-manual',
name: 'tableCreateManual',
component: () => import('@/views/data_meta/tableCreateManual.vue'),
meta: {
title: '新建表',
sidebar: false,
breadcrumb: false,
cache: true,
reuse: true,
activeMenu: '/data-meta/metadata-standard/standard-query'
},
beforeEnter: (to, from) => {
if (to.query.domainName) {
to.meta.title = `新建表(${to.query.domainName})-${to.query.standardName}`;
to.meta.editPage = true;
}
}
},
{
path: 'dim-table-create-manual',
name: 'dimTableCreateManual',
component: () => import('@/views/data_meta/dimTableCreateManual.vue'),
meta: {
title: '新建表',
sidebar: false,
breadcrumb: false,
cache: true,
reuse: true,
activeMenu: '/data-meta/metadata-standard/standard-query'
},
beforeEnter: (to, from) => {
if (to.query.domainName) {
to.meta.title = `新建表(${to.query.domainName})-${to.query.standardName}`;
to.meta.editPage = true;
}
}
},
{
path: 'standard-codetable',
name: 'metadataStandardCodetable',
component: () => import('@/views/data_meta/standard-codetable.vue'),
meta: {
title: '标准代码表',
breadcrumb: false,
cache: true
}
},
{
path: 'standard-import',
name: 'metadataStandardImport',
component: () => import('@/views/data_meta/standard-import.vue'),
meta: {
title: '标准代码导入',
breadcrumb: false,
cache: true,
activeMenu: '/data-meta/metadata-standard/standard-codetable'
}
},
{
path: 'standard-meta-import',
name: 'standardMetaImport',
component: () => import('@/views/data_meta/standard-meta-import.vue'),
meta: {
title: '元数据标准导入',
breadcrumb: false,
cache: true,
activeMenu: '/data-meta/metadata-standard'
}
}
]
}
]
......
import type { RouteRecordRaw } from 'vue-router'
function Layout() {
return import('@/layouts/index.vue')
}
const routes: RouteRecordRaw[] = [
{
path: '/data-pricing/pricing-manage',
component: Layout,
meta: {
title: '定价管理',
icon: 'ep:grid',
},
children: [
{
path: 'demand-manage',
name: 'demandManage',
component: () => import('@/views/data_pricing/demandManage.vue'),
meta: {
title: '需求表管理',
breadcrumb: false,
cache: true
},
},
{
path: 'import-file-demand-manage',
name: 'importFileDemandManage',
component: () => import('@/views/importFile.vue'),
meta: {
title: '导入数据-需求表管理',
sidebar: false,
breadcrumb: false,
cache: true,
reuse: true,
activeMenu: '/data-pricing/pricing-manage/demand-manage'
}
},
{
path: 'disease-manage',
name: 'diseaseManage',
component: () => import('@/views/data_pricing/diseaseManage.vue'),
meta: {
title: '疾病管理',
breadcrumb: false,
cache: true
},
},
{
path: 'import-file-disease',
name: 'importFileDisease',
component: () => import('@/views/importFile.vue'),
meta: {
title: '导入数据-疾病管理',
sidebar: false,
breadcrumb: false,
cache: true,
reuse: true,
activeMenu: '/data-pricing/pricing-manage/disease-manage'
}
},
{
path: 'price-config',
name: 'priceConfig',
component: () => import('@/views/data_pricing/priceConfig.vue'),
meta: {
title: '定价配置',
breadcrumb: false,
cache: true
},
},
{
path: 'price-model',
name: 'priceModel',
component: () => import('@/views/data_pricing/priceModel.vue'),
meta: {
title: '新增配置',
sidebar: false,
breadcrumb: false,
cache: true,
reuse: true,
editPage: true,
activeMenu: '/data-pricing/pricing-manage/price-config'
},
beforeEnter: (to, from) => {
if (to.query.guid) {
to.meta.title = `编辑-${to.query.name}`;
} else {
to.meta.title = `新增配置`;
}
}
},
{
path: 'price-calculate',
name: 'priceCalculate',
component: () => import('@/views/data_pricing/priceCalculate.vue'),
meta: {
title: '数据定价',
breadcrumb: false,
cache: true
}
},
{
path: 'calculate-config',
name: 'calculateConfig',
component: () => import('@/views/data_pricing/calculateConfig.vue'),
meta: {
title: '新增数据定价',
sidebar: false,
breadcrumb: false,
cache: true,
reuse: true,
editPage: true,
activeMenu: '/data-pricing/pricing-manage/price-calculate'
},
beforeEnter: (to, from) => {
if (to.query.guid) {
to.meta.title = `编辑-${to.query.name}`;
} else {
to.meta.title = `新增数据定价`;
}
}
},
],
},
]
export default routes
......@@ -10,6 +10,7 @@ import DataTrustedSpace from './modules/dataTrustedSpace';
import DataAssetRegistry from './modules/dataAssetRegistry';
import DataEntry from './modules/dataEntry';
import SecurityMenu from './modules/securityMenu';
import DataPricing from './modules/dataPricing';
import useSettingsStore from '@/store/modules/settings'
......@@ -110,9 +111,10 @@ const asyncRoutes: RouteRecordRaw[] = [
...DataEntry,
...SecurityMenu,
...DataMeta,
...DataQuality,
...DataQuality,
...DataInventory,
...DataTrustedSpace
...DataTrustedSpace,
...DataPricing
]
const constantRoutesByFilesystem = generatedRoutes.filter((item) => {
......
......@@ -174,7 +174,10 @@ const useMenuStore = defineStore(
else {
// 如果是 string 类型,则认为是路由,需要查找对应的主导航索引
const findIndex = allMenus.value.findIndex(item => item.children.some(r => {
if ((data== "/data-meta/reports/iframePage" || data== "/data-meta/report/budgetDataIndex") && r.path?.includes('/data-meta/report')) {
if ((data== "/data-meta/reports/iframePage" || data == '/data-meta/reports/portraitMaps' || data== "/data-meta/report/budgetDataIndex") && r.path?.includes('/data-meta/report')) {
return true;
}
if (data== "/data-meta/portraitMap" && r.path?.includes('/data-meta/portraitMap')) {
return true;
}
if (data== "/data-meta/portraitMap" && r.path?.includes('/data-meta/portraitMap')) {
......
......@@ -157,7 +157,7 @@ const useRouteStore = defineStore(
};
}
r = routes.find((route: any) => {
return route.path === path || route.path === m.path || `/${route.path}` === m.path || ((path.includes('budgetDataIndex') && path.includes('/data-meta/report') && route.path.includes('/data-meta/report')) || (path.includes('iframePage') && path.includes('/data-meta/report')) || (path.includes('/data-meta/portraitMap') && route.path.includes('/data-meta/portraitMap')));
return route.path === path || route.path === m.path || `/${route.path}` === m.path || ((path.includes('budgetDataIndex') && path.includes('/data-meta/report') && route.path.includes('/data-meta/report')) || (path.includes('iframePage') && path.includes('/data-meta/report')) || (path.includes('portraitMaps') && path.includes('/data-meta/report')) || (path.includes('/data-meta/portraitMap') && route.path.includes('/data-meta/portraitMap')));
});
if (r && (path.includes('budgetDataIndex') || path.includes('iframePage')) || path.includes('portraitMap')) {
r.path = path;
......
......@@ -27,6 +27,7 @@ declare module '@vue/runtime-core' {
FileUpload: typeof import('./../components/FileUpload/index.vue')['default']
FixedActionBar: typeof import('./../components/FixedActionBar/index.vue')['default']
Form: typeof import('./../components/Form/index.vue')['default']
GraphTopbar: typeof import('./../components/RelationNetwork/graphTopbar.vue')['default']
Hour: typeof import('./../components/Schedule/component/hour.vue')['default']
ImagePreview: typeof import('./../components/ImagePreview/index.vue')['default']
ImagesUpload: typeof import('./../components/ImagesUpload/index.vue')['default']
......@@ -41,6 +42,7 @@ declare module '@vue/runtime-core' {
PageNav: typeof import('./../components/PageNav/index.vue')['default']
PcasCascader: typeof import('./../components/PcasCascader/index.vue')['default']
Popover: typeof import('./../components/Popover/index.vue')['default']
RelationNetwork: typeof import('./../components/RelationNetwork/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Schedule: typeof import('./../components/Schedule/index.vue')['default']
......
......@@ -120,6 +120,9 @@ export const filterCascaderData = (data, field, val, type = 'disabled') => {
}
// 数字千分位 保留两位小数
export const changeNum = (num, fixed = 0, round = false) => {
if(num === '' || num === null || num === undefined){
return '';
}
num = parseFloat(num);
if (round) {
let parts = num.toFixed(fixed).split(".")
......
......@@ -884,7 +884,7 @@ const matchEnValue = ref({
<div class="data-label">
<div class="container_wrap full flex">
<div class="aside_wrap">
<div class="aside_title">分类分级目录</div>
<div class="aside_title">分类分级模板</div>
<Tree :treeInfo="treeInfo" @nodeClick="nodeClick" @itemMenuClick="handleTreeItemMenuClick" />
</div>
<div class="main_wrap">
......
......@@ -1293,6 +1293,7 @@ const loadDrawer = async () => {
drawerInfo.value.footer.btns.at(-2).visible = false
drawerInfo.value.footer.btns.at(-3).visible = false
drawerInfo.value.visible = true
console.log('table', formTable.value)
};
const batching = (type) => {
......
<template>
<div ref="containerRef" class='canvas-wrapper'>
</div>
</template>
<script lang="ts" setup name="topbar">
import { ref, watch } from 'vue';
import * as echarts from "echarts";
const props = defineProps({
treeData: {
type: Array<any>,
default: [
{
source: 'a',
target: 'a1',
value: 5
},
{
source: 'a',
target: 'a2',
value: 3
},
{
source: 'b',
target: 'b1',
value: 8
},
{
source: 'a',
target: 'b1',
value: 3
},
{
source: 'b1',
target: 'a1',
value: 1
},
{
source: 'b1',
target: 'c',
value: 2
}
]
},
names: {
type: Array<any>,
default: [
{
name: 'a'
},
{
name: 'b'
},
{
name: 'a1'
},
{
name: 'a2'
},
{
name: 'b1'
},
{
name: 'c'
}
]
}
})
watch(() => props.treeData, (val) => {
setChartsOption();
})
const sankeyInstance: any = ref();
const containerRef = ref();
const setChartsOption = () => {
let option = {
tooltip: {
trigger: 'item',
formatter: (params) => {
if (params.data.name) {
return null;
}
return params.data.source + ' > ' + params.data.target
}
},
color: ["#3DBCBE", "#6b67d1", "#7BBCE0", "#2B8EF3", "#51dca2", "#E19D46"],
series: [
{
type: 'sankey',
top: 60,
bottom: 20,
draggable: false,
left: 50,
emphasis: {
focus: 'trajectory'
},
labelLayout: {
hideOverlap: true,
},
right: 100,
label: {
fontSize: 10,
},
data: props.names?.map(n => {
if (n.isLast) {
return {
...n,
label: {
width: 97,
overflow: 'breakAll'
}
}
}
return n;
}),
links: props.treeData,
lineStyle: {
color: 'source',
curveness: 0.5
},
}
]
}
sankeyInstance.value.setOption(option);
sankeyInstance.value.resize();
}
const resizeObserver = ref();
const observeResize = () => {
window.addEventListener('resize', (e) => {
sankeyInstance.value?.resize();
});
}
onMounted(() => {
nextTick(() => {
sankeyInstance.value = echarts.init(containerRef.value);
setChartsOption();
observeResize();
})
})
</script>
<style lang="scss" scoped>
.canvas-wrapper {
width: 100%;
height: 100%;
position: relative;
}
</style>
\ No newline at end of file
<route lang="yaml">
name: dictFileds
</route>
<script lang="ts" setup name="dictFileds">
import { ref } from 'vue'
import router from '@/router'
import { ElMessage, ElMessageBox } from "element-plus";
import Table from '@/components/Table/index.vue'
// import Dialog from '@/components/Dialog/index.vue'
import useCatchStore from "@/store/modules/catch";
import { chunk } from '@/utils/common'
import { getStandardCodeDataList, getStandardCodeFields,
saveStandardCodeFieldsData, deleteStandardCodeFieldsData,
exportStandardCodeData
} from '@/api/modules/dataMetaService'
import { download } from '@/utils/common'
import {
saveDictionaryData,
getDictionaryFileds,
deleteDictionaryData,
checkDictionaryData,
showDictionary,
getDictionaryRuleData
} from '@/api/modules/dataInventory';
const emits = defineEmits(["exportData"])
const { proxy } = getCurrentInstance() as any;
const tableEl = ref()
const cacheStore = useCatchStore()
const standardGuid = ref("")
const standardName = ref('')
const tableSearchInput = ref('')
const tableFields: any = ref([])
const orginData: any = ref([])
const currTableData: any = ref<Object>({});
const selectRowData = ref([])
const page = ref({
limit: 50,
curr: 1,
sizes: [
{ label: "10", value: 10 },
{ label: "50", value: 50 },
{ label: "100", value: 100 },
{ label: "200", value: 200 },
{ label: "300", value: 300 },
],
});
const tableChunkData: any = ref([])
const tableData: any = ref([])
const tableInfo: any = ref({
id: 'data-fileds-table',
multiple: true,
fields: [],
data: [],
page: {
type: "normal",
rows: 0,
...page.value,
},
// showPage: false,
actionInfo: {
label: "操作",
type: "btn",
width: 92,
btns: (scope) => {
return [
{ label: "编辑", value: "edit", visible: scope.row['STATE'] !== 'Running' },
{ label: "删除", value: "remove", visible: scope.row['STATE'] !== 'Running' },
// { label: "保存", value: "save", visible: scope.row['STATE'] === 'Running' },
{ label: "取消", value: "cancel", visible: scope.row['STATE'] === 'Running' },
]
},
},
editInfo: {},
loading: false
})
const uploadFiles = ref([])
const uploadInfo = ref({
type: 'upload',
title: '',
col: '',
uploadInfo: {
id: 'upload-file-form',
type: 'panel',
action: '',
auto: false,
cover: true,
fileList: [],
accept: '.xlsx, .xls',
triggerBtn: {
label: '导入',
value: 'import_file',
icon: 'Upload',
},
tips: '当前支持xls、xlsx文件,默认使用第一个sheet'
},
})
const dialogInfo = ref({
visible: false,
size: 640,
direction: "column",
header: {
title: "新建",
},
type: '',
contents: [
uploadInfo.value
],
footer: {
visible: true,
btns: [
{ type: "default", label: "取消", value: "cancel" },
{ type: "primary", label: "开始导入", value: "submit" },
],
},
})
const getFirstPageData = () => {
page.value.curr = 1;
toSearch({})
}
const toSearch = (val: any, clear: boolean = false) => {
let params = {
pageIndex: page.value.curr,
pageSize: page.value.limit,
standardGuid : standardGuid.value
}
getTableData(params);
};
const getTableData = (params) => {
tableInfo.value.loading = true
Promise.all([getStandardCodeFields(standardGuid.value), getStandardCodeDataList(params)]).then((resList:any) => {
console.log(resList)
let schemaDataVOS = resList[0].data || []
let jsonArray = resList[1].data.records || []
let data = resList[1].data
tableInfo.value.page.limit = data.pageSize
tableInfo.value.page.curr = data.pageIndex
tableInfo.value.page.rows = data.totalRows
setUploadDataInfo({schemaDataVOS,jsonArray}, true)
}).finally(() => tableInfo.value.loading = false)
};
const tableSelectionChange = (val, tId) => {
selectRowData.value = val;
};
const tablePageChange = (info) => {
console.log('pageChange')
const toChange = checkSave()
const changeCont = () => {
page.value.curr = Number(info.curr)
page.value.limit = Number(info.limit)
toSearch({})
}
if (!toChange) {
ElMessageBox.confirm(
'存在未保存的数据,切换后会丢失,是否确定切换',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
changeCont()
})
} else {
changeCont()
}
};
const saveDisabled = ref(true)
const toolBtnClick = (btn) => {
const type = btn.value
if (type == 'export') {
exportData()
} else if (type == 'import') {
const info = [
{ standardGuid: standardGuid.value, standardName: standardName.value }
]
cacheStore.setCatch('uploadSetting', info)
nextTick(() => {
router.push({
path: '/data-meta/metadata-standard/standard-import',
query: { bizGuid: standardGuid.value }
});
})
} else if (type == 'submit') {
saveData()
} else if (type == 'add_row') {
let rowInfo: any = {}
tableFields.value.map(item => {
rowInfo[item.field] = ''
})
rowInfo.guid = undefined;
rowInfo.STATE = 'Running'
rowInfo.STATUS = 'edit'
rowInfo.ROWID = `upload_${tableData.value.length}`
tableData.value.unshift(rowInfo)
orginData.value.unshift(rowInfo)
// tableInfo.value.page.rows = tableData.value.length
saveDisabled.value = false
// 表格滚动到第一行
let $tableEl = tableEl.value.tableRef
$tableEl.setCurrentRow(rowInfo)
let table = $tableEl.layout.table.refs
let tableScrollEle =
table.bodyWrapper.firstElementChild.firstElementChild
tableScrollEle.scrollTop = 0
}
}
const tableBtnClick = (scope, btn) => {
const type = btn.value;
let row = scope.row;
currTableData.value = row;
if (type == "edit") {
row.STATE = 'Running'
row.STATUS = 'edit'
tableData.value[scope.$index] = row
saveDisabled.value = false
} else if (type == 'save') {
saveData(scope)
} else if (type == 'cancel') {
if (row.guid != undefined) {
// row = orginData.value[(page.value.curr - 1) * page.value.limit + scope.$index]
row = JSON.parse(JSON.stringify(orginData.value[scope.$index]))
row.STATE = ''
row.STATUS = ''
tableData.value[scope.$index] = row
} else {
tableData.value.splice(scope.$index, 1)
orginData.value.splice(scope.$index, 1)
}
// tableInfo.value.page.rows = tableData.value.length
let haveState = tableData.value.some(item => item.STATE)
saveDisabled.value = !haveState
} else if (type == 'remove') {
open("是否确定删除所选数据?", "warning");
}
};
const onUpload = (file, fileList) => {
uploadFiles.value = fileList
}
const uploadBtnClick = (btn) => {
exportData('model')
}
const exportData = (type: any = null) => {
// emits('exportData', type)
let body = {
standardCodeGuid: standardGuid.value,
standardCodeDataGuids: selectRowData.value.map(v => v.guid)
}
exportStandardCodeData(body).then((res:any) => {
if (res && !res.msg) {
download(res, `${standardName.value}.xlsx`, 'excel')
} else {
res?.msg && ElMessage.error(res?.msg);
}
})
}
const importData = (file: any = null) => {
let params = new FormData()
if (file) {
params.append("file", file.raw);
} else {
if (uploadFiles.value.length) {
uploadFiles.value.forEach((item: any, index: number) => {
params.append("file", item.raw);
});
}
}
params.append("standardGuid", standardGuid.value);
showDictionary(params).then((res: any) => {
if (res.code == proxy.$passCode) {
dialogInfo.value.visible = false
let data = res.data ?? []
// data.map((item: any, i) => {
// item.index = tableData.value.length + i
// })
const tData = { jsonArray: data }
setUploadDataInfo(tData)
// saveData(null, tData)
} else {
ElMessage({
type: "error",
message: res.msg,
});
// dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
}
}).catch(() => {
// dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
});
}
const checkSave = () => {
const toSaveData = tableData.value.filter(item => item.STATE === 'Running')
return toSaveData.length == 0 ? true : false
}
const checkParamsData = (scope: any = null) => {
let addJsonArray: any = [], upJsonArray: any = [], jsonArray: any = [], pass = true
let passArr = scope ? [scope.row] : tableData.value
passArr.map((item, index) => {
const obj = JSON.parse(JSON.stringify(item))
delete obj.STATUS
delete obj.NOTES
delete obj.STATE
delete obj.ROWID
if (obj.guid !== undefined) {
upJsonArray.push(obj)
} else {
addJsonArray.push(obj)
}
if (scope) {
// obj.index = scope.$index
jsonArray.push(obj)
} else {
// obj.index = index
jsonArray.push(obj)
}
})
return { pass, addJsonArray, upJsonArray, jsonArray }
}
const loading = ref(false)
const saveData = async (scope: any = null, checkParamData: any = null) => {
let passInfo: any = {}
if (checkParamData) {
passInfo = checkParamData
} else {
passInfo = await checkParamsData(scope)
console.log('passInfo', passInfo)
if (!passInfo.pass) {
ElMessage({
type: 'error',
message: '请填写所有数据项'
})
return
}
}
let params = passInfo.jsonArray.map(item => {
let standardCodeValue = {}
tableFields.value.forEach(fieldObj => {
standardCodeValue[fieldObj.field] = item[fieldObj.field]
})
let obj = {
standardGuid: standardGuid.value,
standardCodeValue,
// guid: item.guid || null,
// standardCodeId: item.standardCodeId || null,
// parentId: item.parentId || null
}
if (item.guid) {
obj.guid = item.guid
}
if (item.standardCodeId) {
obj.standardCodeId = item.standardCodeId
}
if (item.parentId) {
obj.parentId = item.parentId
}
return obj
})
loading.value = true
tableInfo.value.loading = true
saveStandardCodeFieldsData(params).then((res: any) => {
if (res.code == proxy.$passCode) {
getFirstPageData();
ElMessage({
type: 'success',
message: '保存成功'
})
} else {
ElMessage({
type: 'error',
message: res.msg,
})
tableInfo.value.loading = false
}
}).finally(() => loading.value = false)
}
const addColumn = (info: any = null) => {
const fields = tableFields.value
const existIndex = fields.findIndex(item => item.field == 'NOTES')
if (info) {
if (existIndex == -1) {
fields.push({
label: '备注',
field: 'NOTES',
width: 200
})
}
for (var d in info) {
tableData.value[d].NOTES = info[d].join(',')
}
} else {
if (existIndex > -1) {
fields.splice(existIndex, 1)
}
}
}
// 生成表头
const setUploadDataFields = (data) => {
let fields: any = [], editInfo: any = {}
data.map(item => {
let fieldItem: any = {
label: item.fieldName, field: item.fieldName, width: 140
}
fieldItem.type = 'edit'
fieldItem.columClass = 'edit-colum'
editInfo[item.fieldName] = {
label: '',
type: 'input',
placeholder: '',
maxlength: 50,
field: item.fieldName,
default: '',
disabled: item.codeRuleGuid ? true : false,
clearable: true,
}
fields.push(fieldItem)
})
tableFields.value = fields
tableInfo.value.fields = tableFields.value
tableInfo.value.editInfo = editInfo
}
// 生成表数据
const setUploadDataInfo = async (info, setField = false) => {
if (setField) {
const fields = info.schemaDataVOS ?? []
await setUploadDataFields(fields)
}
let data = info.jsonArray ?? []
// 设置表数据
// data.map((item, i) => {
// item.ROWID = `upload_${tableData.value.length + i}`
// })
data.forEach(item => {
Object.keys(item.standardCodeValue).forEach(key => {
item[key] = item.standardCodeValue[key]
})
})
if (setField) {
tableData.value = data
} else {
tableData.value = [...tableData.value, ...data]
}
orginData.value = JSON.parse(JSON.stringify(tableData.value))
tableInfo.value.data = tableData.value
// tableInfo.value.page.rows = tableData.value.length
// if (setField) {
// orginData.value = data
// } else {
// orginData.value = [...orginData.value, ...data]
// }
// chunkData()
}
const chunkData = () => {
const data = orginData.value
tableChunkData.value = chunk(data, page.value.limit)
tableData.value = tableChunkData.value[page.value.curr - 1]
tableInfo.value.data = tableData.value
tableInfo.value.page.limit = page.value.limit
tableInfo.value.page.curr = page.value.curr
tableInfo.value.page.rows = orginData.value.length
}
const batching = (type) => {
if (type == 'delete') {
if (selectRowData.value.length == 0) {
ElMessage({
type: 'error',
message: '请选择需要删除的数据',
})
return
}
open("是否确定删除所选数据?", "warning", true);
}
};
const open = (msg, type, isBatch = false) => {
ElMessageBox.confirm(msg, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: type,
}).then(() => {
let guids: any = []
if (isBatch) {
const list = selectRowData.value.filter((item: any) => item.guid !== undefined)
if (list.length) {
guids = list.map((l: any) => l.guid)
}
const newRows = selectRowData.value.filter((item: any) => item.guid == undefined)
newRows.map((n: any, r) => {
const existIndex = tableData.value.findIndex(t => t.ROWID == n.ROWID)
if (existIndex > -1) {
tableData.value.splice(existIndex, 1)
orginData.value.splice(existIndex, 1)
}
// const existIndex = orginData.value.findIndex(t => t.id == n.id)
// existIndex > -1 && orginData.value.splice(existIndex, 1)
// if (r == newRows.length - 1) {
// page.value.curr = 1
// chunkData()
// }
})
tableInfo.value.page.rows = tableData.value.length
} else {
guids = [currTableData.value.guid]
}
if (guids.length) {
const params = {
guid: standardGuid.value,
delGuids: guids
}
// console.log(params)
// return
deleteStandardCodeFieldsData(guids).then((res: any) => {
if (res.code == proxy.$passCode) {
getFirstPageData();
ElMessage({
type: "success",
message: "删除成功",
});
} else {
ElMessage({
type: "error",
message: res.msg,
});
}
});
}
});
};
const dialogBtnClick = (btn, info) => {
if (btn.value == 'submit') {
// dialogInfo.value.footer.btns.map((item: any) => item.disabled = true)
importData()
} else if (btn.value == 'cancel') {
// dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
nextTick(() => {
dialogInfo.value.visible = false;
})
}
};
defineExpose({
standardGuid,
standardName,
getFirstPageData,
checkSave
});
</script>
<template>
<div class="container_wrap full flex">
<div class="main_wrap">
<div class="table_tool_wrap">
<div class="tools_btns">
<el-button type="primary" @click="toolBtnClick({ value: 'add_row' })" v-preReClick :disabled="loading">新增行</el-button>
<el-button type="primary" plain @click="toolBtnClick({ value: 'submit' })" v-preReClick :disabled="saveDisabled || loading">保存数据</el-button>
<el-button @click="batching('delete')" v-preReClick :disabled="loading">批量删除</el-button>
<el-button @click="toolBtnClick({ value: 'import' })" v-preReClick :disabled="loading">导入数据</el-button>
<el-button @click="toolBtnClick({ value: 'export' })" v-preReClick :disabled="loading">导出数据</el-button>
<el-button @click="getFirstPageData" v-preReClick :disabled="loading">刷新结果</el-button>
</div>
<!-- <el-input class="table_search_input" v-model.trim="tableSearchInput" placeholder="输入名称搜索" :suffix-icon="Search" clearable
@change="val => toSearch({})" /> -->
</div>
<div class="table_panel_wrap full">
<Table
ref="tableEl"
:tableInfo="tableInfo"
@tableBtnClick="tableBtnClick"
@tableSelectionChange="tableSelectionChange"
@tablePageChange="tablePageChange"
/>
</div>
</div>
<!-- <Dialog :dialogInfo="dialogInfo" @btnClick="dialogBtnClick" @onUpload="onUpload" @uploadBtnClick="uploadBtnClick" /> -->
</div>
</template>
<style lang="scss" scoped>
.container_wrap {
width: calc(100% - 200px);
.main_wrap {
width: 100%;
.table_panel_wrap {
height: calc(100% - 44px);
}
}
}
</style>
<script lang="ts" setup name="Drawer">
import { computed } from "vue";
import { ClickOutside as vClickOutside } from "element-plus";
import type { FormInstance } from "element-plus";
import { Download, Upload, CirclePlus } from "@element-plus/icons-vue";
import Table from "@/components/Table/index.vue";
import Form from "@/components/Form/index.vue";
import Tree from "@/components/Tree/index.vue";
import ListPanel from "@/components/ListPanel/index.vue";
import UploadFiles from "@/components/Upload/index.vue";
const emits = defineEmits([
"drawerBtnClick",
"radioGroupChange",
"drawerSelectChange",
"drawerTableSelectChange",
"drawerTableBtnClick",
"drawerTableToolBtnClick",
"drawerFormBtnClick",
"listItemClick",
"drawerTableInputChange",
"drawerToolBtnClick",
"drawerTableSelectionChange",
"drawerTablePageChange",
"uploadBtnClick",
"beforeUPload",
"uploadFile",
"onUpload",
"onClickOutside",
]);
const props = defineProps({
drawerInfo: {
type: Object,
default: {},
},
});
const drawerTableRef = ref();
const drawerFormRef = ref();
const selectRowData = ref([]);
const formListRef = ref();
const formTreeRef = ref();
const uploadRef = ref();
const drawerVisible = computed(() => {
nextTick(() => {
if (props.drawerInfo.visible) {
drawerOpen();
}
})
return props.drawerInfo.visible;
});
const drawerDirection = computed(() => {
return props.drawerInfo.direction;
});
const drawerModal = computed(() => {
return props.drawerInfo.modal ?? true;
});
const modalClose = computed(() => {
return props.drawerInfo.modalClose ?? false;
});
const drawerModalClass = computed(() => {
nextTick(() => {
drawerOpen();
})
return props.drawerInfo.modalClass ?? "";
});
const drawerSize = computed(() => {
return props.drawerInfo.size ?? "30%";
});
const drawerType = computed(() => {
return props.drawerInfo.type ?? "form";
});
const drawerTitle = computed(() => {
return props.drawerInfo.header.title;
});
const selectOptions = computed(() => {
return props.drawerInfo.container.relateOptions ?? [];
});
const formItems = computed(() => {
return props.drawerInfo.container.formItems ?? [];
});
const formRules = computed(() => {
return props.drawerInfo.container.rules ?? {};
});
const selectData = computed(() => {
return props.drawerInfo.container.selectData ?? [];
});
const contents = computed(() => {
return props.drawerInfo.container.contents ?? [];
});
const footer = computed(() => {
return props.drawerInfo.footer;
});
const onClickOutside = (e: any) => {
emits("onClickOutside");
};
const getDrawerConRef = (refName) => {
console.log(refName, '----------')
if (refName == 'drawerTableRef') {
const dtf = drawerTableRef.value[0] || drawerTableRef.value
return dtf?.tableRef
}
// const drawerForm = drawerFormRef.value[0] || drawerFormRef.value;
if (refName == 'drawerFormRef') {
const drawerForm = drawerFormRef.value?.[0] || drawerFormRef.value;
return drawerForm
}
}
defineExpose({
// selections,
getDrawerConRef
});
const toolBtnClick = (btn, type: any = null) => {
if (type && type == "table") {
let selectData = [];
if (drawerTableRef.value) {
const drawerTable = drawerTableRef.value[0] || drawerTableRef.value;
selectData = drawerTable.tableRef.getSelectionRows();
}
emits("drawerTableToolBtnClick", btn, selectData);
} else {
emits("drawerToolBtnClick", btn);
}
};
const tableSelectionChange = (val, tId) => {
selectRowData.value = val;
emits("drawerTableSelectionChange", val, tId);
};
const tableSelectChange = (val, scope) => {
emits("drawerTableSelectChange", val, scope);
};
const tableInputChange = (val, scope) => {
emits("drawerTableInputChange", val, scope);
};
const tablePageChange = (info) => {
emits("drawerTablePageChange", info);
}
const tableBtnClick = (scope, btn) => {
emits("drawerTableBtnClick", scope, btn);
};
const submitForm = async (formEl: FormInstance | undefined, btn) => {
if (!formEl) return;
await formEl.validate((valid, fields) => {
const drawerForm = drawerFormRef.value[0] || drawerFormRef.value;
if (valid) {
const formInfo = drawerForm.formInline;
emits("drawerBtnClick", btn, formInfo);
} else {
var obj = fields && Object.keys(fields);
obj?.[0] && formEl?.scrollToField(obj?.[0])
console.log("error submit!", fields);
}
});
};
const btnClick = (btn, type) => {
if (btn.disabled) return;
if (
btn.value != "cancel" &&
(btn.value.indexOf("submit") > -1 || btn.value.indexOf("save") > -1)
) {
if (drawerFormRef.value) {
const drawerForm = drawerFormRef.value[0] || drawerFormRef.value;
const formRef = drawerForm.ruleFormRef;
submitForm(formRef, btn);
} else {
emits("drawerBtnClick", btn);
}
} else {
emits("drawerBtnClick", btn);
}
};
const radioGroupChange = (val, info) => {
emits("radioGroupChange", val, info);
};
const formSelectChange = (val, row, info) => {
emits("drawerSelectChange", val, row, info);
};
const formBtnClick = (btn) => {
emits("drawerFormBtnClick", btn);
};
const listItemClick = (row) => {
emits("listItemClick", row);
};
const onUpload = (file, fileList) => {
emits("onUpload", file, fileList);
};
const beforeUPload = (file) => {
emits("beforeUPload", file);
};
const uploadFile = (file) => {
emits("uploadFile", file);
};
const uploadBtnClick = (btn) => {
emits("uploadBtnClick", btn);
};
const drawerOpen = () => {
const wrap: any = drawerModalClass.value && document.getElementsByClassName(drawerModalClass.value)[0];
if (wrap) {
wrap.style.width = `${drawerSize.value + 1}px`
} else {
const dom: any = document.getElementsByClassName('el-overlay')[0];
dom && (dom.style.width = '100%');
}
};
const drawerClose = () => {
btnClick({ value: "cancel" }, null);
if (drawerFormRef.value) {
const drawerForm = drawerFormRef.value[0] || drawerFormRef.value;
const formRef = drawerForm.ruleFormRef;
drawerForm?.resetForm(formRef);
}
};
</script>
<template>
<el-drawer v-model="drawerVisible" :direction="drawerDirection" :size="drawerSize" :modal="drawerModal"
:close-on-click-modal="modalClose" :close-on-press-escape="modalClose" :modal-class="drawerModalClass"
destroy-on-close :z-index="props.drawerInfo.zIndex ?? null" @close="drawerClose">
<template #header>
<span class="title">{{ drawerTitle }}</span>
</template>
<template #default>
<div class="drawer-body-loading" v-if="drawerInfo.loading ?? false" v-loading="drawerInfo.loading ?? false"></div>
<div v-else class="drawer_panel" :class="[con.col]" :style="con.style" v-for="con in contents">
<div class="panel_title" v-if="con.title">{{ con.title }}</div>
<template v-if="con.type && con.type.indexOf('table') > -1">
<div class="table_tool float-right">
<template v-for="bar in con.tableTool.btns.slice(1)">
<el-popover v-if="bar.popover" :visible="bar.popover.visible" :title="bar.popover.title"
:popper-class="bar.popover.class ?? ''" placement="bottom-start" :width="bar.popover.width ?? 200"
trigger="click">
<template #reference>
<el-button :type="bar.type" @click="toolBtnClick(bar, con.type)" v-click-outside="onClickOutside"
v-preReClick>{{ bar.label }}</el-button>
</template>
<template #default>
<span v-html="bar.popover.content"></span>
</template>
</el-popover>
<el-button :type="bar.type" :plain="bar.plain" v-else @click="toolBtnClick(bar, 'table')" v-preReClick>{{
bar.label
}}</el-button>
</template>
</div>
<div class="table_panel_wrap" :style="con.tableInfo.style">
<Table ref="drawerTableRef" :class="[con.tableInfo.col]" :tableInfo="con.tableInfo"
@tableSelectChange="tableSelectChange" @tableBtnClick="tableBtnClick"
@tableSelectionChange="tableSelectionChange" @tableInputChange="tableInputChange"
@tablePageChange="tablePageChange" />
</div>
<div class="table_tool" :class="[con.tableTool.col]" v-if="con.tableTool && (con.tableTool.visible ?? true)">
<template v-for="bar in con.tableTool.btns.slice(0, 1)">
<!-- <el-popover v-if="bar.popover" :visible="bar.popover.visible" :title="bar.popover.title"
:popper-class="bar.popover.class ?? ''" placement="bottom-start" :width="bar.popover.width ?? 200"
trigger="click">
<template #reference>
<el-button :type="bar.type" @click="toolBtnClick(bar, con.type)" v-click-outside="onClickOutside"
v-preReClick>{{ bar.label }}</el-button>
</template>
<template #default>
<span v-html="bar.popover.content"></span>
</template>
</el-popover> -->
<!-- <el-button :type="bar.type" :plain="bar.plain" @click="toolBtnClick(bar, 'table')" v-preReClick>{{
bar.label
}}</el-button> -->
<div class="operation-icon" @click="toolBtnClick(bar, 'table')" v-preReClick>
<el-icon color="#4fa1a4">
<CirclePlus />
</el-icon>
<span>{{ bar.label }}</span>
</div>
</template>
</div>
</template>
<template v-else-if="con.type && con.type.indexOf('tree') > -1">
<div class="list_tree" v-if="con.type.indexOf('list') > -1">
<ListPanel ref="formListRef" :listInfo="con.listInfo" @itemClick="listItemClick" />
<Tree ref="formTreeRef" :treeInfo="con.treeInfo" />
</div>
<Tree ref="formTreeRef" :treeInfo="con.treeInfo" v-else />
</template>
<template v-else-if="con.type && con.type == 'field-list'">
<div class="field_list_panel" v-if="con.listInfo.data.length > 0">
<div class="list_item" v-for="item in con.listInfo.data">
<span class="item_field">{{ item[con.listInfo.field] }}</span>
<span class="item_text">{{ item[con.listInfo.label] }}</span>
</div>
</div>
<div class="field_list_panel">
<div class="empty_tips">暂无数据</div>
</div>
</template>
<template v-else-if="con.type && con.type.indexOf('upload') > -1">
<div class="upload_tool">
<UploadFiles ref="uploadRef" :upload-info="con.uploadInfo" @onUpload="onUpload" @beforeUPload="beforeUPload"
@uploadFile="uploadFile" @uploadBtnClick="uploadBtnClick" />
<div class="tool_btns" v-if="con.tools && con.tools.visible">
<template v-for="btn in con.tools.btns">
<el-button v-if="btn.visible ?? true" :type="btn.type" :plain="btn.plain"
@click="toolBtnClick(btn, 'table')" v-preReClick>
<el-icon v-if="btn.icon && btn.icon == 'Upload'">
<Upload />
</el-icon>
<el-icon v-else-if="btn.icon && btn.icon == 'Download'">
<Download />
</el-icon>
<span>{{ btn.label }}</span>
</el-button>
</template>
</div>
</div>
<div class="upload_table_panel_wrap" v-if="con.tableInfo && Object.keys(con.tableInfo).length">
<Table ref="drawerTableRef" :class="[con.tableInfo.col]" :tableInfo="con.tableInfo"
@tableSelectChange="tableSelectChange" @tableBtnClick="tableBtnClick"
@tableSelectionChange="tableSelectionChange" @tableInputChange="tableInputChange"
@tablePageChange="tablePageChange" />
</div>
</template>
<template v-else>
<Form ref="drawerFormRef" :itemList="con.formInfo.items" :formId="con.formInfo.id" :rules="con.formInfo.rules"
:col="con.formInfo.col" :readonly="con.formInfo.readonly" @radioGroupChange="radioGroupChange"
@selectChange="formSelectChange" @btnClick="formBtnClick">
</Form>
<!-- 插槽内容 -->
<slot></slot>
</template>
</div>
</template>
<template #footer v-if="footer.visible ?? true">
<div style="flex: auto">
<template v-for="btn in footer.btns">
<el-button v-if="btn.visible ?? true" :type="btn.type" :disabled="btn.disabled ?? false"
@click="btnClick(btn, null)" v-preReClick :loading="btn.loading ?? false">{{ btn.label }}</el-button>
</template>
</div>
</template>
</el-drawer>
</template>
<style lang="scss" scoped>
.operation-icon {
display: flex;
align-items: center;
cursor: pointer;
> span {
margin-left: 4px;
color: #4fa1a4;
}
}
.drawer-body-loading {
height: 100%;
}
.drawer_panel {
position: relative;
&.no-margin {
margin-top: 0;
}
&.height_auto {
.table_panel_wrap {
.table_panel {
min-height: unset;
}
}
}
}
.panel_title {
line-height: 40px;
font-size: 14px;
color: var(--el-color-regular);
font-weight: 600;
}
.table_tool {
height: 40px;
display: flex;
align-items: center;
&.float-right {
position: absolute;
top: 0;
right: 0;
}
}
.table_panel_wrap {
height: auto!important;
.table_panel {
padding: 0;
min-height: auto;
&.auto-height {
min-height: unset;
}
}
}
.list_tree {
max-height: 400px;
display: flex;
justify-content: space-between;
box-shadow: 0 0 0 1px #d9d9d9;
.list_panel_wrap {
width: 158px;
box-shadow: 1px 0 0 0 #d9d9d9;
}
.tree_panel {
width: calc(100% - 159px);
:deep(.el-tree) {
margin: 0;
height: 100%;
overflow: hidden auto;
}
}
}
.field_list_panel {
width: 100%;
height: calc(100vh - 70px);
position: relative;
.list_item {
display: flex;
font-size: 12px;
color: var(--el-color-regular);
margin: 4px 0;
.item_field {
width: 130px;
color: var(--el-text-color-regular);
}
.item_text {
width: calc(100% - 130px);
word-wrap: break-word;
}
}
.empty_tips {
text-align: center;
color: var(--el-disabled-text-color);
position: absolute;
top: 50px;
left: 50%;
transform: translate(-50%);
}
}
.upload_tool {
margin-bottom: 8px;
position: relative;
.tool_btns {
position: absolute;
top: 0;
right: 0;
}
}
.upload_table_panel_wrap {
.table_panel {
min-height: unset;
}
}
</style>
<script lang="ts" setup name="existingTableSelect">
import { ref } from "vue";
import Dialog from "@/components/Dialog/index.vue";
const emits = defineEmits([
"expandValueChange"
]);
const props = defineProps({
tableCreateInfo: {
type: Object,
default: {},
},
partitionAttribute: {
type: Object,
default: {},
},
isLook: {
type: Boolean,
default: false
}
});
const expandProperties: any = computed(() => {
let partitionAttribute = props.tableCreateInfo.partitionAttribute;
return {
partitionMode: partitionAttribute?.partitionMode || 'dynamic',
staticPartitionType: partitionAttribute?.staticPartitionType || 'Range',
partitionCol: partitionAttribute?.partitionCol || "",
partitionTimeUnit: partitionAttribute?.partitionTimeUnit || "DAY",
dynamicPartitionEnd: partitionAttribute?.dynamicPartitionEnd == null ? 3 : partitionAttribute?.dynamicPartitionEnd,
staticPartitionRange: partitionAttribute?.staticPartitionRangeBegin ? [partitionAttribute?.staticPartitionRangeBegin,
partitionAttribute?.staticPartitionRangeEnd] : null,
dynamicPartitionHistory: partitionAttribute?.dynamicPartitionHistory === "Y",
dynamicPartitionHistoryNum: partitionAttribute?.dynamicPartitionHistoryNum,
staticPartitionEnum: partitionAttribute?.staticPartitionEnum,
}
})
const formItems: any = ref([
{
label: "分区模式",
type: "radio-panel",
placeholder: "",
field: "partitionMode",
default: "dynamic",
options: [
{ label: "动态分区", value: "dynamic" },
{ label: "静态分区", value: "static" },
],
children: [],
required: true,
block: true,
col: "no-wrap col2",
},
{
label: "分区类型",
type: "select",
placeholder: "请选择",
options: [
{
label: "Range",
value: "Range",
},
{
label: "List",
value: "List",
},
],
field: "staticPartitionType",
default: 'Range',
required: true,
visible: false,
},
{
label: "分区字段",
type: "select",
placeholder: "请选择",
options: [],
field: "partitionCol",
default: "",
props: {
label: 'chName',
value: 'enName'
},
tooltip: true,
tooltipContent: '分区字段为日期、日期时间类型且为主键的字段。',
clearable: true,
required: true,
visible: true,
},
{
label: "分区单位",
type: "select",
placeholder: "请选择",
options: [
{
value: "DAY",
label: "按天",
},
{
value: "WEEK",
label: "按星期",
},
{
value: "MONTH",
label: "按月",
},
{
value: "YEAR",
label: "按年",
},
],
default: "DAY",
field: "partitionTimeUnit",
required: true,
visible: false,
},
{
type: "select-group",
field: "dynamicPartitionTimeUnit",
children: [
{
label: "分区单位",
type: "select",
placeholder: "请选择",
options: [
{
value: "HOUR",
label: "按小时",
},
{
value: "DAY",
label: "按天",
},
{
value: "WEEK",
label: "按星期",
},
{
value: "MONTH",
label: "按月",
},
{
value: "YEAR",
label: "按年",
},
],
default: "DAY",
field: "partitionTimeUnit",
required: true,
visible: true,
},
{
label: " ",
type: "input",
placeholder: "结束偏移量",
default: 3,
field: "dynamicPartitionEnd",
required: true,
visible: true,
},
],
col: "col2",
visible: true
},
{
label: "分区范围",
type: "date-picker",
field: "staticPartitionRange",
default: null,
placeholder: "开始时间~截止时间",
clearable: true,
required: true,
visible: false,
},
{
type: "checkbox-input",
placeholder: "创建历史分区",
field: "dynamicPartitionHistory",
default: false,
children: [
{
label: "",
type: "input",
placeholder: "历史分区数量",
field: "dynamicPartitionHistoryNum",
default: 1,
required: false,
visible: false,
},
],
class: "dialog-checkbox-input",
visible: true,
required: false,
},
{
label: "分区枚举值",
type: "textarea",
placeholder: "请使用“,”号分隔",
field: "staticPartitionEnum",
default: "",
clearable: true,
required: true,
block: true,
visible: false,
},
]);
const formRules = ref({
staticPartitionType: [
{
validator: (rule: any, value: any, callback: any) => {
if (!value) {
callback(new Error("分区类型不为空"));
} else {
callback();
}
},
trigger: "blur",
},
],
partitionCol: [{
validator: (rule: any, value: any, callback: any) => {
if (!value) {
callback(new Error("分区字段不为空"));
} else {
callback();
}
},
trigger: "blur",
}],
staticPartitionRange: [{
validator: (rule: any, value: any, callback: any) => {
if (!value?.length) {
callback(new Error("分区范围不为空"));
} else {
callback();
}
},
trigger: "blur",
}],
staticPartitionEnum: [{
trigger: "blur",
required: true,
message: '分区枚举值不能为空'
}],
dynamicPartitionHistoryNum: {
validator: (rule: any, value: any, callback: any) => {
const r = /(^[0-9]([0-9]*)$|^[0-9]$)/; // 正整数(可以以0打头)
if (value && !r.test(value)) {
callback(new Error('请填写大于或等于零整数'));
return;
}
if ((value + '').length > 6) {
callback(new Error('请填写小于7位的整数'));
return;
}
callback();
},
trigger: "blur",
},
dynamicPartitionEnd: {
validator: (rule: any, value: any, callback: any) => {
const r = /(^[0-9]([0-9]*)$|^[0-9]$)/; // 正整数(可以以0打头)
if (value && !r.test(value)) {
callback(new Error('请填写大于或等于零整数'));
return;
}
if ((value + '').length > 6) {
callback(new Error('请填写小于7位的整数'));
return;
}
callback();
},
trigger: "blur",
},
});
const expandPropertyDialogInfo = ref({
readonly: false,
visible: false,
size: 700,
height: "270px",
header: {
title: "扩展属性",
},
direction: "column",
type: "",
contents: [
{
type: "form",
title: "",
formInfo: {
readonly: false,
id: "edit-expand-property",
items: formItems.value,
rules: formRules.value,
},
},
],
footer: {
visible: true,
btns: [
{ type: "default", label: "取消", value: "cancel" },
{ type: "primary", label: "确定", value: "submit" },
],
},
});
/** 记录对话框编辑过程中的扩展属性。提交之后才会记录在expandProperties */
const editExpandProperties: any = ref({});
/** 扩展属性弹出对话框 */
const handleClickExpand = () => {
expandPropertyDialogInfo.value.visible = true;
if (props.isLook || props.tableCreateInfo.isCreate) {
expandPropertyDialogInfo.value.contents[0].formInfo.readonly = true;
expandPropertyDialogInfo.value.footer.visible = false;
} else {
expandPropertyDialogInfo.value.contents[0].formInfo.readonly = false;
expandPropertyDialogInfo.value.footer.visible = true;
}
if (expandProperties.value.partitionMode === 'dynamic') {
formItems.value[1].visible = false;
formItems.value[2].visible = true;
formItems.value[3].visible = false;
formItems.value[4].visible = true;
formItems.value[5].visible = false;
formItems.value[6].visible = true;
formItems.value[7].visible = false;
formItems.value[6].children[0].visible = expandProperties.value.dynamicPartitionHistory;
formItems.value[2].options = props.tableCreateInfo.tableFields?.filter(field => field.isPrimary === 'Y' && (field.dataType === 'date' || field.dataType === 'datetime')) || [];
formItems.value[2].tooltipContent = '分区字段为日期、日期时间类型且为主键的字段。';
} else {
formItems.value[1].visible = true;
formItems.value[2].visible = true;
formItems.value[3].visible = true;
formItems.value[4].visible = false;
formItems.value[5].visible = true;
formItems.value[6].visible = false;
formItems.value[7].visible = false;
let val = expandProperties.value.staticPartitionType;
formItems.value[3].visible = val !== 'List';
formItems.value[5].visible = val !== 'List';
formItems.value[7].visible = val === 'List';
if (val !== 'List') {
formItems.value[2].options = props.tableCreateInfo.tableFields?.filter(field => field.isPrimary === 'Y' && (field.dataType === 'date' || field.dataType === 'datetime')) || [];
formItems.value[2].tooltipContent = '分区字段为日期、日期时间类型且为主键的字段。';
} else {
formItems.value[2].options = props.tableCreateInfo.tableFields?.filter(field => field.isPrimary === 'Y') || [];
formItems.value[2].tooltipContent = '分区字段为主键字段。';
}
}
expandPropertyDialogInfo.value.contents[0].formInfo.items = formItems.value;
editExpandProperties.value = Object.assign({}, expandProperties.value);
setFormItems(editExpandProperties.value);
};
/** 重置formItems的值 */
const setFormItems = (row: any = null) => {
formItems.value.forEach(item => {
if (item.field === 'dynamicPartitionTimeUnit') {
item.children.forEach(child => {
child.default = row[child.field];
});
} else if (item.field === 'dynamicPartitionHistory') {
item.default = row[item.field];
item.children.forEach(child => {
child.default = row[child.field];
});
} else {
item.default = row[item.field];
}
})
}
const radioGroupChange = (val, info) => {
formItems.value[0].default = val;
if (val == "dynamic") {
formItems.value[1].visible = false;
formItems.value[2].visible = true;
formItems.value[3].visible = false;
formItems.value[4].visible = true;
formItems.value[5].visible = false;
formItems.value[6].visible = true;
formItems.value[7].visible = false;
} else if (val == "static") {
formItems.value[1].visible = true;
formItems.value[2].visible = true;
formItems.value[3].visible = true;
formItems.value[4].visible = false;
formItems.value[5].visible = true;
formItems.value[6].visible = false;
formItems.value[7].visible = false;
}
expandPropertyDialogInfo.value.contents[0].formInfo.items = formItems.value;
editExpandProperties.value = Object.assign({}, editExpandProperties.value, info)
setFormItems(editExpandProperties.value);
};
const dialogCheckboxChange = (val, info) => {
let opts: any = formItems.value[6].children;
opts[0].visible = val;
editExpandProperties.value = Object.assign({}, editExpandProperties.value, info)
setFormItems(editExpandProperties.value);
};
const dialogSelectChange = (val, row, info) => {
if (row.field == 'staticPartitionType') {
formItems.value[3].visible = val !== 'List';
formItems.value[5].visible = val !== 'List';
formItems.value[7].visible = val === 'List';
if (val !== 'List') {
formItems.value[2].options = props.tableCreateInfo.tableFields?.filter(field => field.isPrimary === 'Y' && (field.dataType === 'date' || field.dataType === 'datetime')) || [];
formItems.value[2].tooltipContent = '分区字段为日期、日期时间类型且为主键的字段。';
} else {
formItems.value[2].options = props.tableCreateInfo.tableFields?.filter(field => field.isPrimary === 'Y') || [];
formItems.value[2].tooltipContent = '分区字段为主键字段。';
}
}
editExpandProperties.value = Object.assign({}, editExpandProperties.value, info)
setFormItems(editExpandProperties.value);
}
const expandPropertyDialogBtnClick = (btn, info) => {
if (btn.value == 'submit') {
emits('expandValueChange', info);
expandPropertyDialogInfo.value.visible = false;
} else if (btn.value == 'cancel') {
expandPropertyDialogInfo.value.visible = false;
}
};
defineExpose({
handleClickExpand
});
</script>
<template>
<Dialog ref="expandPropertyDialogRef" :dialogInfo="expandPropertyDialogInfo" @radioGroupChange="radioGroupChange"
@checkboxChange="dialogCheckboxChange" @selectChange="dialogSelectChange" @btnClick="expandPropertyDialogBtnClick" />
</template>
<style scoped lang="scss"></style>
<template>
<el-drawer
v-model="visible"
:title="title"
size="760px"
class="standard-modal"
:close-on-click-modal="false"
>
<el-form
:rules="formRules" :model="form" ref="formEl"
require-asterisk-position="right"
v-loading="loading"
>
<el-row>
<el-col :span="12" style="padding-right:10px">
<el-form-item label="元数据标准名称" prop="standardName">
<el-input v-model="form.standardName" placeholder="请输入" maxlength="50"/>
</el-form-item>
</el-col>
<el-col :span="12" style="padding-right:10px">
<el-form-item label="上级标准" prop="parentGuid">
<el-tree-select
v-model="form.parentGuid"
:data="treeSelectData"
:props="standardProps"
node-key="guid"
:highlight-current="true"
check-strictly
placeholder="请选择"
:disabled="treeSelectDisabled"
clearable
@check="parentGuidCheck"
@clear="parentGuidClear"
@nodeClick="parentGuidCheck"
/>
<!-- <el-cascader
v-model="form.parentGuid"
:options="standardOptions"
:props="standardProps"
:show-all-levels="false"
style="width:100%"
clearable
@change="parentGuidChange"
/> -->
</el-form-item>
</el-col>
<el-col :span="12" style="padding-right:10px">
<el-form-item label="标准编号" prop="standardCode">
<el-input v-model="form.standardCode" placeholder="请输入" maxlength="20"/>
</el-form-item>
</el-col>
<el-col :span="12" style="padding-right:10px">
<el-form-item label="排序" prop="orderNum">
<el-input v-model="form.orderNum" placeholder="请输入" type="number" maxlength="2"/>
</el-form-item>
</el-col>
<el-col :span="12" style="padding-right:10px">
<el-form-item label="发布单位" prop="publishingUnitCode">
<el-select v-model="form.publishingUnitCode" placeholder="请选择">
<el-option v-for="item in publishUnitOptions" :label="item.label" :value="item.value" :key="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="描述" prop="description">
<el-input
v-model="form.description"
placeholder="请输入"
type="textarea" maxlength="200"
rows="4"
show-word-limit
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="自定义字段选择" v-if="fieldsTableShow" required>
<div class="table-form">
<div class="table-form-wrapper" v-for="item,index in form.fieldRQVOS" :key="index">
<div class="table-form-item">
<el-select v-model="item.fileNameCode" style="width:160px" filterable clearable :disabled="fieldsDisabled">
<el-option v-for="item in fieldOptions" :label="item.label" :value="item.value" :key="item.value"></el-option>
</el-select>
</div>
<div class="table-form-item">
<el-select v-model="item.isNotnull" style="width: 96px" placeholder="是否必填" clearable :disabled="fieldsDisabled">
<el-option v-for="item in isBooleanOptions" :label="item.label" :value="item.value" :key="item.value"></el-option>
</el-select>
</div>
<div class="table-form-item">
<el-select v-model="item.isDisplay" style="width:96px" placeholder="是否展示" clearable :disabled="fieldsDisabled">
<el-option v-for="item in isBooleanOptions" :label="item.label" :value="item.value" :key="item.value"></el-option>
</el-select>
</div>
<div class="table-form-item">
<el-select v-model="item.inputTypeCode" style="width:130px" clearable :disabled="fieldsDisabled"
@change="v => inputTypeChange(v, item)">
<el-option v-for="item in inputOptions" :label="item.label" :value="item.value" :key="item.value"></el-option>
</el-select>
</div>
<div class="table-form-item">
<el-select
v-if="item.inputTypeCode == 2"
v-model="item.dataTypeCode"
style="width:160px" filterable clearable
:disabled="fieldsDisabled">
<el-option v-for="item in allDictOptions" :label="item.dictTypeName" :value="item.dictTypeName" :key="item.guid"></el-option>
</el-select>
<el-input
v-else-if="item.inputTypeCode == 3"
v-model="item.validateExpression"
:disabled="fieldsDisabled"
placeholder="请输入"
></el-input>
</div>
<div class="table-form-operation" v-if="!fieldsDisabled">
<!-- <el-icon color="#4fa1a4" @click="() => addTableItem(index)">
<CirclePlus />
</el-icon> -->
<el-icon color="#b2b2b2" @click="() => deleteTableItem(index)" :size="22" class="custom-icon" style="margin-top:3px"><Delete /></el-icon>
</div>
</div>
<div class="table-form-add" v-if="!fieldsDisabled">
<el-icon color="#4fa1a4" @click="() => addTableItem(index)" :size="20" class="custom-icon">
<CirclePlus />
</el-icon>
<span @click="() => addTableItem(index)" style="cursor: pointer;">添加字段</span>
</div>
</div>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div style="text-align:right">
<el-button @click="visible = false" :disabled="confirmLoading">取消</el-button>
<el-button type="primary" @click="confirm" :loading="confirmLoading">确认</el-button>
</div>
</template>
</el-drawer>
</template>
<script setup lang="ts">
// import { cloneDeep } from 'lodesh-es'
import { watch } from 'vue'
import { Search, CirclePlus, Delete } from '@element-plus/icons-vue'
import { saveMetaStandard, deleteMetaStandard,
updateMetaStandard, getMetaStandardDetail,
getMetaStandardTree
} from '@/api/modules/dataMetaService'
import {
getParamsList, getDictAllList
} from '@/api/modules/dataAsset'
import { ElMessage } from "element-plus";
const { proxy } = getCurrentInstance() as any;
const props = defineProps({
modelValue: Boolean,
standardOptions: {
type: Array,
default: () => ([])
},
type: {
type: String,
default: 'add'
},
guid: {
type: String,
default: ''
},
currentNode: {
type: Object,
default: () => ({})
}
})
const emit = defineEmits(['update:modelValue', 'success', 'confirm']);
const visible = computed({
get() {
return props.modelValue;
},
set(val) {
emit('update:modelValue', val);
}
})
const title = computed(() => {
return props.type === 'add' ? '新增元数据标准' : '编辑元数据标准'
})
/**
* 配置项列表
*/
const publishUnitOptions = ref([])
const standardProps = {
label: 'standardName',
value: 'guid',
checkStrictly: true,
emitPath: false
}
const standardTreeProps = {
label: 'standardName',
value: 'guid',
}
const fieldOptions = ref([])
const isBooleanOptions = [
{ label: '是', value: 'Y' },
{ label: '否', value: 'N' },
]
const inputOptions = ref([])
const allDictOptions = ref([])
/**表单 */
const formEl = ref()
const formTpl = {
standardName: '',
standardCode: '',
orderNum: '',
publishingUnitCode: '',
parentGuid: '',
description: '',
fieldRQVOS: null
}
const form = ref({...formTpl})
const formRules = {
standardName: { required: true, message: '请填写元数据标准名称' },
orderNum: { required: true, message: '请填写排序' },
publishingUnitCode: { required: true, message: '请选择发布的单位' }
}
function parentGuidChange (val) {
console.log(val)
if (!val) {
form.value.fieldRQVOS = []
return
}
const isFirst = props.standardOptions.find(item => item.guid === val)
if (isFirst) {
form.value.fieldRQVOS = [{...tableFormTpl}]
}
}
/** 上级标准树选择 */
const treeSelectData = ref([])
const treeCurrentNode = ref({})
const treeSelectDisabled = ref(false)
const fieldsDisabled = ref(false)
const fieldsTableShow = ref(false)
watch(
() => treeCurrentNode,
(currentNode:any) => {
console.log('currentNode', currentNode.value)
let { level, isHaveData } = currentNode.value
fieldsTableShow.value = level ? true : false
fieldsDisabled.value = false
if (!level) {
form.value.fieldRQVOS = null
return
}
if (props.type === 'add') {
if (level == 1) {
// 所选为一级节点,当前二级节点
form.value.fieldRQVOS = [{...tableFormTpl}]
}
if (level >= 2) {
// 所选为二级节点,当前三级节点以下,只读
fieldsDisabled.value = true
}
} else if (props.type === 'edit') {
if (level == 1) {
// 所选为一级节点,当前二级节点
form.value.fieldRQVOS = [{...tableFormTpl}]
fieldsDisabled.value = false
}
if (level >= 2) {
fieldsDisabled.value = true
}
}
},
{ deep: true}
)
function parentGuidCheck (node, {checkedKeys}) {
console.log('node', node)
// fieldsControlDisabled.value = false
treeCurrentNode.value = node
// if (checkedKeys.length === 0) {
// treeCurrentNode.value = {}
// return
// }
if (node.level == 1) return
// 上级标准带出标准编号和发布单位
// loading.value = true
getMetaStandardDetail(node.guid).then((res:any) => {
if (res.code === proxy.$passCode) {
const data = res.data
if (!form.value.standardCode) {
form.value.standardCode = data.standardCode
}
if (!form.value.publishingUnitCode) {
form.value.publishingUnitCode = data.publishingUnitCode
}
form.value.fieldRQVOS = data.fieldRSVOS
}
})
}
function parentGuidClear () {
treeCurrentNode.value = {}
}
const tableFormTpl = {
fileNameCode: '',
isNotnull: 'N',
isDisplay: 'N',
inputTypeCode: '',
dataTypeCode: null,
validateExpression: null
}
function addTableItem (index) {
const tableObj = { ...tableFormTpl }
// form.value.fieldRQVOS.splice(index + 1, 0, tableObj)
form.value.fieldRQVOS.push(tableObj)
nextTick(() => {
let scrollBody = document.querySelector('.el-drawer__body')
scrollBody.scrollTop = scrollBody.scrollHeight
})
}
function deleteTableItem (index) {
form.value.fieldRQVOS.splice(index, 1)
}
function inputTypeChange (val, item) {
console.log(val)
if (val == 2) {
// 下拉框
item.validateExpression = null
} else if (val == 3) {
item.dataTypeCode = null
} else {
item.validateExpression = null
item.dataTypeCode = null
}
}
const loading = ref(false)
function getDetail () {
loading.value = true
return getMetaStandardDetail(props.guid).then((res:any) => {
if (res.code === proxy.$passCode) {
const data = res.data
data.fieldRQVOS = data.fieldRSVOS
form.value = { ...data }
let { level, isChildHaveData, children } = treeCurrentNode.value
if (level == 1) {
// 所选为一级节点,当前二级节点
fieldsTableShow.value = false
}
if (level == 2) {
fieldsDisabled.value = isChildHaveData === 'Y' ? true : false
}
if (level >= 3) {
fieldsDisabled.value = true
}
if ((children && children.length) || isChildHaveData === 'Y') {
treeSelectDisabled.value = true
}
}
}).finally(() => loading.value = false)
}
const confirmLoading = ref(false)
function confirm () {
formEl.value.validate(valid => {
if (!valid) return
let body = { ...form.value }
if (!body.parentGuid) {
body.parentGuid = null
}
delete body.fieldRSVOS
// return
const request = props.type === 'add' ? saveMetaStandard : updateMetaStandard
confirmLoading.value = true
request(body).then((res:any) => {
if (res.code === proxy.$passCode) {
ElMessage.success('操作成功')
emit('success', props.type === 'add' ? res.data : body.guid)
visible.value = false
return
}
ElMessage.error(res.msg)
}).finally(() => confirmLoading.value = false)
})
}
function getTreeSelectOptions () {
return getMetaStandardTree().then((res:any) => {
if (res.code === proxy.$passCode) {
let data = res.data || []
data.forEach(item => {
item.showEdit = true
item.level = 1
})
treeSelectData.value = data
}
})
}
watch(
() => visible.value,
(v) => {
if (!v) return
// console.log(props.currentNode)
// 上级标准:不能选自己,有数据的标准不能选
// treeSelectData.value = JSON.parse(JSON.stringify(props.standardOptions))
treeSelectDisabled.value = false
if (props.type === 'edit') {
// fieldsControlDisabled.value = false
getTreeSelectOptions().then(() => {
getDetail().then(() => formatOptions(treeSelectData.value))
})
treeCurrentNode.value = props.currentNode
} else {
// fieldsControlDisabled.value = true
form.value = { ...formTpl }
getTreeSelectOptions().then(() => {
formatOptions(treeSelectData.value)
})
treeCurrentNode.value = {}
}
setTimeout(() => {
formEl.value.clearValidate()
}, 100)
}
)
function formatOptions (options, level = 1) {
options.forEach((item:any) => {
let disabled = form.value.guid === item.guid ? true : false
if (item.isHaveData === 'Y') {
disabled = true
}
item.disabled = disabled
item.level = level
if (item.children) {
formatOptions(item.children, level + 1)
}
})
}
onBeforeMount(() => {
getParamsList({ dictType: '发布单位' }).then((res:any) => {
if (res.code === proxy.$passCode) {
const data = res.data || []
publishUnitOptions.value = data
}
})
getParamsList({ dictType: '字段名代码' }).then((res:any) => {
if (res.code === proxy.$passCode) {
const data = res.data || []
fieldOptions.value = data
}
})
getParamsList({ dictType: '输入框类型' }).then((res:any) => {
if (res.code === proxy.$passCode) {
const data = res.data || []
inputOptions.value = data
}
})
getDictAllList().then((res:any) => {
if (res.code === proxy.$passCode) {
const data = res.data || []
allDictOptions.value = data
}
})
})
</script>
<style lang="scss">
.standard-modal {
.el-form-item {
flex-direction: column;
.el-form-item__label {
justify-content: flex-start;
}
}
.el-drawer__footer {
padding: 10px;
justify-content: flex-end;
}
.el-dialog__body {
height: 500px;
overflow: auto;
}
.table-form-wrapper {
display: flex;
margin-bottom: 5px;
.table-form-item {
padding-right: 10px;
}
.table-form-operation {
flex: 0 0 30px;
// padding-left: 6px;
}
}
.table-form-add {
display: flex;
align-items: center;
color: #4fa1a4;
> span {
margin-left: 4px;
}
}
}
</style>
\ No newline at end of file
<template>
<el-drawer
v-model="visible"
:title="title"
size="520px"
class="standard-meta-modal"
:close-on-click-modal="false"
>
<el-form
:model="form"
:rules="formRules"
ref="formEl"
style="min-height: 200px;"
require-asterisk-position="right"
v-loading="loading"
>
<el-row>
<el-col v-for="item,index in fields" :key="index" :span="12" style="padding-right:10px;">
<el-form-item :label="item.fileNameCodeName" :prop="item.fileNameCode">
<el-input
v-if="item.inputTypeCode == '1' || item.inputTypeCode == '3'"
v-model="form[item.fileNameCode]"
placeholder="请输入"
/>
<el-select
v-else-if="item.inputTypeCode == '2'"
v-model="form[item.fileNameCode]"
filterable
clearable
placeholder="请选择"
>
<el-option v-for="op in formOptions[item.fileNameCode]" :label="op.label" :value="op.value" :key="op.value"></el-option>
</el-select>
<el-tree-select
v-else-if="item.inputTypeCode == '4'"
v-model="form[item.fileNameCode]"
:data="standardCodeTree"
:props="treeSelectProps"
placeholder="请选择"
filterable
clearable
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div style="text-align:right">
<el-button @click="visible = false" :disabled="confirmLoading">取消</el-button>
<el-button type="primary" @click="confirm" :loading="confirmLoading">确认</el-button>
</div>
</template>
</el-drawer>
</template>
<script setup lang="ts">
import { watch } from 'vue'
import { ElMessage } from "element-plus";
import { getParamsList } from '@/api/modules/dataAsset'
import { saveMetaStandardDataFields, getMetaStandardFieldsDetail, getStandardCodeTree } from '@/api/modules/dataMetaService'
const { proxy } = getCurrentInstance() as any;
const props = defineProps({
modelValue: Boolean,
fields: {
type: Array,
default: () => ([])
},
metaStandardGuid: {
type: String,
default: ''
},
type: {
type: String,
default: 'add'
},
data: {
type: Object,
default: () => ({})
}
})
const emit = defineEmits(['update:modelValue', 'success', 'confirm'])
const visible = computed({
get() {
return props.modelValue;
},
set(val) {
emit('update:modelValue', val);
}
})
const title = computed(() => {
return props.type === 'add' ? '新增字段标准' : '编辑字段标准'
})
const formEl = ref()
const form = ref({})
const formRules = ref({})
const formOptions = ref({})
const detail = ref({})
async function initForm () {
const { fields, type, data } = props
console.log(data)
if (!fields) return
let formData = {}
let formRuleData = {}
let formOptionData = {}
let detailData:any = {}
if (type === 'edit') {
detailData = await getDetail()
detail.value = detailData
}
fields.forEach(async (item:any) => {
formData[item.fileNameCode] = type === 'add' ? '' : detailData.metaStandardValue[item.fileNameCode]
formRuleData[item.fileNameCode] = {
required: item.isNotnull === 'Y' ? true : false,
message: `缺少${item.fileNameCodeName}`
}
// formOptionData[item.fileNameCode] = await getOptions(item.dataTypeCode)
if (item.inputTypeCode == '2') {
formOptions.value[item.fileNameCode] = await getOptions(item.dataTypeCode)
}
})
// formOptions.value = formOptionData
form.value = formData
formRules.value = formRuleData
nextTick(() => formEl.value.clearValidate())
setTimeout(() => {
formEl.value.clearValidate()
}, 100)
}
function getOptions (dictType) {
return new Promise((resolve, reject) => {
getParamsList({ dictType }).then((res:any) => {
if (res.code === proxy.$passCode) {
resolve(res.data)
}
})
})
}
const loading = ref(false)
function getDetail () {
return new Promise((resolve) => {
loading.value = true
getMetaStandardFieldsDetail(props.data.guid).then((res:any) => {
if (res.code === proxy.$passCode) {
resolve(res.data)
}
}).finally(() => loading.value = false)
})
}
const confirmLoading = ref(false)
function confirm () {
console.log(form.value)
formEl.value.validate(valid => {
if (!valid) return
let body = {
metaStandardGuid: props.type === 'edit' ? detail.value.metaStandardGuid : props.metaStandardGuid,
metaStandardValue: { ...form.value }
}
if (props.type === 'edit') {
body.guid = props.data.guid
}
confirmLoading.value = true
saveMetaStandardDataFields(body).then((res:any) => {
if (res.code === proxy.$passCode) {
ElMessage.success('操作成功')
emit('success')
visible.value = false
} else {
ElMessage.error(res.msg)
}
}).finally(() => confirmLoading.value = false)
})
}
const standardCodeTree = ref([])
const treeSelectProps = {
label: 'name',
value: 'guid',
isLeaf: 'isCode'
}
function getStandardCodeTreeList () {
getStandardCodeTree().then((res:any) => {
if (res.code === proxy.$passCode) {
const data = res.data
data.forEach(item => {
if (item.children) {
item.children.forEach(subItem => {
// 二级的标准名字作为key
subItem.guid = subItem.name
// subItem.value = subItem.name
})
}
})
standardCodeTree.value = data
}
})
}
watch(
() => visible.value,
(v) => {
if (!v) return
initForm()
}
)
onBeforeMount(() => {
getStandardCodeTreeList()
})
</script>
<style lang="scss">
.standard-meta-modal {
.el-form-item {
flex-direction: column;
.el-form-item__label {
justify-content: flex-start;
}
}
.el-drawer__footer {
padding: 10px;
justify-content: flex-end;
}
.table-form-wrapper {
display: flex;
margin-bottom: 5px;
.table-form-item {
padding-right: 10px;
}
.table-form-operation {
flex: 0 0 70px;
padding-left: 6px;
}
}
}
</style>
\ No newline at end of file
<template>
<el-select v-if="!readonly && isEdit && isSelectType(dbType, scope)" v-model="scope.row['defaultValue']" placeholder="请选择" collapse-tags-tooltip
filterable allow-create default-first-option :reserve-keyword="false">
<el-option v-for="opt in optionsConfig[dataType]" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
</el-select>
<el-date-picker v-else-if="!readonly && isEdit && dataType === 'date'" v-model="scope.row['defaultValue']" type="date"
format="YYYY-MM-DD" value-format="YYYY-MM-DD" placeholder="请选择" />
<el-date-picker v-else-if="!readonly && isEdit && dbType === 'mysql' && dataType === 'datetime'"
v-model="scope.row['defaultValue']" placeholder="请选择" type="datetime" format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss" />
<el-input v-else-if="!readonly && isEdit" v-model.trim="scope.row['defaultValue']" placeholder="请填写"
@input="(val) => inputChange(val, scope.row.dataType, scope)" />
<span v-else>{{ scope.row["defaultValue"] == null ? '-' : (isSelectType(dbType, scope) ?
(optionsConfig[dataType].find(s => s.value == scope.row["defaultValue"])?.label ??
scope.row["defaultValue"]) : scope.row["defaultValue"]) }}</span>
</template>
<script lang="ts" setup name="tableDefaultValue">
import { ref } from "vue";
import { useDefault } from "@/hooks/useDefault";
const { optionsConfig, inputChange, isSelectType } = useDefault()
const props = defineProps({
dbType: {
type: String,
default: ''
},
scope: {
type: Object,
default: {}
},
readonly: {
type: Boolean,
default: false
}
})
const isEdit = computed(() => {
return props.scope.row['isEdit'];
});
const dataType = computed(() => {
return props.scope.row['dataType'];
});
</script>
\ No newline at end of file
<route lang="yaml">
name: dimTableCreateManual
</route>
<script lang="ts" setup name="dimTableCreateManual">
import { ref } from "vue";
import { useRouter, useRoute } from "vue-router";
import { ElMessage, ElMessageBox, ElTable } from "element-plus";
import { QuestionFilled } from "@element-plus/icons-vue";
import {
getDatabase,
saveSubjectTable,
dimTypeList,
syncPolicys,
tableModels,
aggMethodList,
getMetaStandardDsField,
getAllFlowData
} from "@/api/modules/dataMetaService";
import useUserStore from "@/store/modules/user";
import expandPropertyDialog from "./components/expandPropertyDialog.vue";
import tableDefaultValue from "./components/tableDefaultValue.vue";
import { useDefault } from "@/hooks/useDefault"
import { getCamundaDeploymentId } from "@/api/modules/workFlowService"
const userStore = useUserStore();
const { checkDefault } = useDefault()
const router = useRouter();
const route = useRoute();
const fullPath = route.fullPath;
const isLook = <any>route.query.isLook == 1;
const subjectDomainGuid: any = ref(route.query.domainGuid);
const tableCreateInfoLoading = ref(false);
const { proxy } = getCurrentInstance() as any;
const fieldStandardTableRef = ref<InstanceType<typeof ElTable>>();
/** 表模型,只有doris数据库才有 */
const dbType = ref("");
const databaseList: any = ref([]);
const tableFieldsLoading = ref(false);
//记录当前正在编辑的表创建信息。
const tableCreateInfo: Ref<any> = ref({
guid: "",
isCreate: false,
inputNameValue: '',
tableCreateType: 1,
tableData: [
{
//数据库表信息。
dataSourceGuid: '',
dataServerName: "",
dataServerChName: "",
enName: "",
chName: "",
subjectDomain: route.query.domainName,
subjectDomainGuid: subjectDomainGuid.value,
dimType: 1,
codeColumn: '',
codeName: '',
syncPolicy: 3,
tableCategory: 4,
characterSet: 'utf8mb3',
tableModel: 1, //若是聚合模型,下方出现一列聚合方式选择。处了主键列,其余列都需要选择。每个表里都要有主键。
description: "",
},
],
partitionAttribute: {},
tableFields: [], // 字段标准数组。
});
const selectTableFieldRows = ref([]);
//字段类型
const fieldTypes: any = ref([]);
//字符集
const characterList: any = ref([]);
//是否列表
const isNotList = ref([
{
label: "Y",
value: "Y",
},
{
label: "N",
value: "N",
},
]);
const fullscreenLoading = ref(false);
/** 表里有数据时删除字段时提示。 */
const hasSubjectData = ref(false);
const expandProperties = ref({});
const deploymentId = ref('');
const flowExpand = ref(true);
onBeforeMount(() => {
getDatabaseList();
getFieldTypeList();
getCharacterListData();
if (route.query.metaStandard) {
tableCreateInfo.value.tableData[0].chName = route.query.standardName;
fullscreenLoading.value = true;
getMetaStandardDsField(route.query.metaStandard).then((res: any) => {
fullscreenLoading.value = false;
if (res.code == proxy.$passCode) {
tableCreateInfo.value.tableFields = res.data?.map((d, i) => {
d.orderNum = i;
d.isEdit = true;
return d;
}) || [];
} else {
proxy.$ElMessage.error(res.msg);
}
})
}
getCamundaDeploymentId('10025').then((res: any) => {
if (res.code == proxy.$passCode) {
deploymentId.value = res.data;
} else {
proxy.$ElMessage.error(res.msg);
}
})
})
onMounted(() => {
})
onActivated(() => {
});
const getDatabaseList = () => {
getDatabase({ connectStatus: 1 }).then((res: any) => {
databaseList.value = [];
if (res.code == proxy.$passCode) {
databaseList.value = res.data || [];
} else {
ElMessage.error(res.msg);
}
})
};
const getFieldTypeList = () => {
getAllFlowData('字段类型').then((res: any) => {
fieldTypes.value = [];
if (res.code == proxy.$passCode) {
fieldTypes.value = res.data || [];
} else {
ElMessage.error(res.msg);
}
})
}
const getCharacterListData = () => {
getAllFlowData('Mysql字符集').then((res: any) => {
characterList.value = [];
if (res.code == proxy.$passCode) {
characterList.value = res.data || [];
} else {
ElMessage.error(res.msg);
}
})
}
/** 限制长度输入框只能输入整型数字,表,字段英文名称,限制输入字符,数字和下划线。 */
const inputLengthKeyUp = (regexp, scope, field, max: any = null, min: any = null) => {
scope.row[field] = scope.row[field].replace(regexp, '');
if (field == 'fieldLength' && scope.row.dataType == 'decimal') {
max = 65;
}
/** 最大值设置2000 */
if (max && scope.row[field] > max) {
scope.row[field] = max;
}
if (min !== null && scope.row[field] != '' && scope.row[field] <= min) {
scope.row[field] = min;
}
}
/** 保存表 */
const saveTable = () => {
let tableData = tableCreateInfo.value.tableData[0];
if (!tableData.chName) {
ElMessage({
type: "error",
message: "主题表名称不能为空",
});
return;
}
if (!tableData.dataServerName) {
ElMessage({
type: "error",
message: "数据源不能为空",
});
return;
}
if (tableData.dataServerName.indexOf('-') > -1) {
ElMessage.error('数据库表名称不能包含中划线,可以改为下划线');
return;
}
if (!tableCreateInfo.value.inputNameValue) {
ElMessage({
type: "error",
message: "主题表名称不能为空",
});
return;
}
if (!tableData.codeColumn) {
ElMessage({
type: "error",
message: "编码字段不能为空",
});
return;
}
if (!tableData.codeName) {
ElMessage({
type: "error",
message: "编码名称不能为空",
});
return;
}
let tableFields = tableCreateInfo.value.tableFields;
if (!tableFields.length) {
ElMessage({
type: "error",
message: "表字段不能为0行",
});
return;
}
// 若开启了字段标准,则不能为空。
// 必须含有主键。若是聚合模型,则除了主键必须有聚合方式。
let isSumModel = tableData.tableModel === 2;
let hasPrimary = false;
let enNames: any = [];
let chNames: any = [];
const regex = /^[a-zA-Z]/;
for (const field of tableFields) {
if (!field.enName) {
ElMessage.error(`第 ${field.orderNum} 个字段的英文名称不能为空`);
return;
}
if (!regex.test(field.enName)) {
ElMessage.error(`第 ${field.orderNum} 个字段的英文名称必须以英文字符开头`);
return;
}
if (!field.dataType) {
ElMessage.error(`第 ${field.orderNum} 个字段的字段类型不能为空`);
return;
}
if (enNames.indexOf(field.enName) > -1) {
ElMessage.error(`字段的英文名称 ${field.enName} 不能重复`);
return;
}
if (chNames.indexOf(field.chName) > -1) {
ElMessage.error(`字段的中文名称 ${field.chName} 不能重复`);
return;
}
if (field.dataType === "decimal" && (!field.fieldPrecision && field.fieldPrecision != 0)) {
ElMessage.error(`第 ${field.orderNum} 个字段的字段类型为浮点型时,精度不能为空`);
return;
}
if (field.dataType === "varchar" && (!field.fieldLength && field.fieldLength != 0)) {
ElMessage.error(`第 ${field.orderNum} 个字段的字段类型为字符型时,长度不能为空`);
return;
}
if (field.dataType === "char" && (!field.fieldLength && field.fieldLength != 0)) {
ElMessage.error(`第 ${field.orderNum} 个字段的字段类型为单字符型时,长度不能为空`);
return;
}
if (field.dataType === "decimal" && (!field.fieldLength && field.fieldLength != 0)) {
ElMessage.error(`第 ${field.orderNum} 个字段的字段类型为浮点符型时,长度不能为空`);
return;
}
if (field.isPrimary === 'Y') {
hasPrimary = true;
if (field.notNull != 'Y') {
ElMessage.error(`第 ${field.orderNum} 个字段为主键,应设置为必填`);
return;
}
if (field.dataType == 'text') {
ElMessage.error(`第 ${field.orderNum} 个字段为主键,字段类型不能设置为‘大字段型’`);
return;
}
if (field.dataType == 'json') {
ElMessage.error(`第 ${field.orderNum} 个字段为主键,字段类型不能设置为‘JSON类型’`);
return;
}
if (field.dataType == 'bit') {
ElMessage.error(`第 ${field.orderNum} 个字段为主键,字段类型不能设置为‘布尔类型’`);
return;
}
} else {
if (!field.aggWay && isSumModel) {
ElMessage.error(`聚合模型的非主键字段必须设置聚合方式`);
return;
}
}
if (field.isEdit) {
if (checkDefault[field.dataType]) {
if (!field.fieldLength) { }
if (!checkDefault[field.dataType]({ row: field })) {
return;
}
}
}
enNames.push(field.enName);
chNames.push(field.chName);
field.fieldStandardGuid = route.query.metaStandard;
}
if (!hasPrimary) {
ElMessage.error(`字段至少有一个主键字段!`);
return;
}
let addInfo = Object.assign({}, tableCreateInfo.value.tableData[0], {
enName: tableCreateInfo.value.inputNameValue,
tableCreateType: tableCreateInfo.value.tableCreateType,
saveFlag: 1,
dbType: dbType.value,
dataState: 1,
partitionAttribute: !Object.keys(tableCreateInfo.value.partitionAttribute).length ? null : Object.assign({}, tableCreateInfo.value.partitionAttribute, {
dynamicPartitionHistory: tableCreateInfo.value.partitionAttribute ? "Y" : 'N'
}),
subjectFieldAddDTOS: tableCreateInfo.value.tableFields.map((field, i) => {
return Object.assign({}, field, { orderNum: i + 1 });
}),
})
if (!tableCreateInfo.value.guid) { //添加
fullscreenLoading.value = true;
saveSubjectTable(addInfo).then((res: any) => {
fullscreenLoading.value = false;
if (res.code == proxy.$passCode) {
ElMessage.success('新建表提交成功!');
router.push({
name: 'metadataStandardQuery'
});
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
} else {
ElMessage.error(res.msg);
}
}).catch((err) => {
ElMessage.error(err);
fullscreenLoading.value = false;
});
}
};
//数据库选择改变,对应的表名称是否需要变化。需要根据此属性带出表名前缀,以及是否是doris数据库。
const selectDatabaseChange = (val) => {
let oldDbType = dbType.value;
let d = databaseList.value.find(d => d.guid === val);
if (d) {
dbType.value = d.databaseType;
tableCreateInfo.value.tableData[0].dataSourceGuid = d.guid;
tableCreateInfo.value.tableData[0].dataServerName = d.databaseNameEn;
} else {
dbType.value = "";
tableCreateInfo.value.tableData[0].dataSourceGuid = '';
tableCreateInfo.value.tableData[0].dataServerName = '';
}
if (oldDbType != dbType.value) {
tableCreateInfo.value.tableFields.forEach((tableField: any) => {
if (tableField['dataType'] === "datetime") {
tableField.defaultValue = ""
}
})
}
};
/** 添加字段标准 */
const addField = () => {
let len = tableCreateInfo.value.tableFields.length;
tableCreateInfo.value.tableFields.push({
orderNum: len + 1,
isDim: "N",
isPrimary: "N",
notNull: "N",
isEdit: true
});
//设置选中表格当前新增行。
fieldStandardTableRef.value?.setCurrentRow(
tableCreateInfo.value.tableFields[tableCreateInfo.value.tableFields.length - 1]
);
nextTick(() => {
let bodyWrapper = fieldStandardTableRef.value?.$el.querySelector('.el-table__body');
let domScroll = bodyWrapper.parentElement.parentElement;
let rect = domScroll.getBoundingClientRect();
let maxNum = len + 1;
if (maxNum * 36 > rect.height + domScroll.scrollTop) {
fieldStandardTableRef.value?.setScrollTop(maxNum * 36 - rect.height + 2)
}
})
};
/** 勾选字段标准选中变化。 */
const selectionFieldsChange = (val) => {
selectTableFieldRows.value = val;
};
/**
* 上移规则:
* 勾选多个时先从最上面开始逐个上移一行,若已经移到最上面一行,则不处理。
*/
const moveUp = () => {
let selectRows = fieldStandardTableRef.value?.getSelectionRows();
if (!selectRows.length) {
ElMessage.error('请先选择需要勾选的数据进行上移');
return;
}
let data = tableCreateInfo.value.tableFields;
let selectRowIndexs: number[] = [];
let minNum: number = 0;
selectRows.forEach((row, i) => {
let orderNum = data.findIndex(d => d === row) + 1;
if (orderNum == 1) {
selectRowIndexs.push(orderNum);
minNum = orderNum;
return;
}
if (selectRowIndexs.includes(orderNum - 1)) {
//下一行也是选中的,则不做转换。
return;
}
let topNum = orderNum - 1;
if (i === 0) {
minNum = topNum;
}
row.orderNum = topNum;
let changeRow = data[topNum - 1];
changeRow.orderNum = orderNum;
selectRowIndexs.push(topNum);
data[topNum] = changeRow;
data[topNum - 1] = row;
});
nextTick().then(() => {
let bodyWrapper = fieldStandardTableRef.value?.$el.querySelector('.el-table__body');
let domScroll = bodyWrapper.parentElement.parentElement;
if ((minNum * 36 - 36 - 2) < domScroll.scrollTop) {
fieldStandardTableRef.value?.setScrollTop((domScroll.scrollTop - 36 - 2) < 0 ? 0 : (domScroll.scrollTop - 36 - 2))
}
});
}
/**
* 下移规则:
* 勾选多个时先从最下面开始逐个下移一行,若已经移到最下面一行,则不处理。
*/
const moveDown = () => {
let selectRows = fieldStandardTableRef.value?.getSelectionRows();
if (!selectRows.length) {
ElMessage.error('请先选择需要勾选的数据进行下移');
return;
}
let data = tableCreateInfo.value.tableFields;
let selectRowIndexs: number[] = [];
let maxNum: number = 0;
selectRows.slice(0).reverse().forEach((row, i) => {
let orderNum = data.findIndex(d => d === row) + 1;
if (orderNum === data.length) {
maxNum = orderNum;
selectRowIndexs.push(orderNum);
return;
}
if (selectRowIndexs.includes(orderNum + 1)) {
//下一行也是选中的,则不做转换。
return;
}
let bottomNum = orderNum + 1;
row.orderNum = bottomNum;
if (i === 0) {
maxNum = bottomNum;
}
let changeRow = data[bottomNum - 1];
changeRow.orderNum = orderNum;
selectRowIndexs.push(bottomNum);
data[orderNum - 1] = changeRow;
data[bottomNum - 1] = row;
});
nextTick(() => {
let bodyWrapper = fieldStandardTableRef.value?.$el.querySelector('.el-table__body');
let domScroll = bodyWrapper.parentElement.parentElement;
let rect = domScroll.getBoundingClientRect();
if (maxNum * 36 > rect.height + domScroll.scrollTop) {
fieldStandardTableRef.value?.setScrollTop(maxNum * 36 - rect.height + 2)
}
})
}
/** 批量删除字段标准 */
const delFeilds = () => {
if (selectTableFieldRows.value.length == 0) {
ElMessage({
type: "info",
message: "请选择需要删除的字段",
});
return;
}
if (tableCreateInfo.value.isCreate) {
if (selectTableFieldRows.value.find((row: any) => row.isPrimary === 'Y')) {
ElMessage.error('已建表不能删除主键字段');
return;
}
}
let hasCreateField = selectTableFieldRows.value.some((row: any) => row.isCreate === 'Y');
ElMessageBox.confirm(hasCreateField && hasSubjectData.value ? '已选择的字段中含有已创建的表字段且有数据,此操作将永久删除,是否继续?' : "此操作将永久删除,是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
//此删除是直接从库里删除,还是点击保存后再删除呢??如果是入库删除,后,调用接口获取数据。
let tableFields = tableCreateInfo.value.tableFields;
let tableData = tableCreateInfo.value.tableData[0];
selectTableFieldRows.value.forEach((r: any) => {
let index = tableFields.findIndex((t: any) => t.orderNum === r.orderNum);
if (index !== -1) {
let row = tableFields[index];
tableFields.splice(index, 1);
if (tableData.codeName == row.enName) {
tableData.codeName = "";
}
if (tableData.codeColumn == row.enName) {
tableData.codeColumn = "";
}
}
});
fieldStandardTableRef.value?.clearSelection();
tableCreateInfo.value.tableFields.forEach((field, i) => {
field.orderNum = i + 1;
});
ElMessage({
type: "success",
message: "删除成功",
});
})
.catch(() => {
ElMessage({
type: "info",
message: "已取消删除",
});
});
};
//点击编辑按钮
const handleFieldClickEdit = (scope) => {
scope.row['isEdit'] = true;
};
//点击保存按钮
const handleFieldClickSave = (scope) => {
if (!scope.row.fieldStandardCode) {
ElMessage({
type: "error",
message: "该主题域开启了字段标准,当前行字段标准不能为空!",
});
return;
}
if (!scope.row.enName) {
ElMessage({
type: "error",
message: "字段英文名不能为空!",
});
return;
}
scope.row['isEdit'] = false;
};
const handleFieldDelete = (scope) => {
ElMessageBox.confirm(scope.row['isCreate'] === 'Y' && hasSubjectData.value ? '该字段已被创建且表中有数据,此操作将永久删除,是否继续?' : "此操作将永久删除,是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
let tableFields = tableCreateInfo.value.tableFields;
tableFields.splice(scope.$index, 1);
tableCreateInfo.value.tableFields.forEach((field, i) => {
field.orderNum = i + 1;
});
tableCreateInfo.value.tableFields.forEach((field, i) => {
field.orderNum = i + 1;
});
let row = scope.row;
let tableData = tableCreateInfo.value.tableData[0];
if (tableData.codeName == row.enName) {
tableData.codeName = "";
}
if (tableData.codeColumn == row.enName) {
tableData.codeColumn = "";
}
ElMessage({
type: "success",
message: "删除成功",
});
})
.catch(() => {
ElMessage({
type: "info",
message: "已取消删除",
});
});
}
const dataTypeChange = (val, scope) => {
scope.row['defaultValue'] = ''
scope.row['fieldLength'] = undefined
scope.row['fieldPrecision'] = undefined
}
/** 扩展属性弹出对话框 */
const expandPropertyDialogRef = ref();
/** 扩展属性弹出对话框 */
const handleClickExpand = () => {
expandPropertyDialogRef.value?.handleClickExpand();
}
const expandDialogValueChange = (val) => {
tableCreateInfo.value.partitionAttribute = val;
expandProperties.value = val;
}
const tableSelectFields = computed(() => {
return tableCreateInfo.value.tableFields.filter(t => !!t.enName);
})
</script>
<template>
<div class="table_tool_wrap" v-loading="fullscreenLoading">
<div class="tools_btns">
<!-- <el-button type="primary" :disabled="isLook" @click="saveDraftTable" v-preReClick>保存为草稿</el-button> -->
<el-button type="primary" :disabled="isLook" @click="saveTable" v-preReClick>提交</el-button>
</div>
<el-table ref="tableRef" :data="tableCreateInfo.tableData" v-loading="tableCreateInfoLoading"
:highlight-current-row="true" stripe border height="100%" tooltip-effect="light" row-key="guid" :style="{
width: '100%',
height: 'auto',
display: 'inline-block',
}">
<el-table-column prop="dataSourceGuid" label="数据源" width="200px" align="left" show-overflow-tooltip>
<template #header>
<span>数据源</span>
<span v-if="!isLook" style="color:red;margin-left: 2px;">*</span>
</template>
<template #default="scope">
<el-select v-if="!isLook" v-model="scope.row['dataSourceGuid']" placeholder="请选择"
:disabled="tableCreateInfo.isCreate" @change="(val) => selectDatabaseChange(val)" clearable filterable>
<el-option v-for="opt in databaseList" :key="opt['guid']" :label="opt['databaseNameZh']"
:value="opt['guid']" />
</el-select>
<span v-else>{{ scope.row["dataServerChName"] }}</span>
</template>
</el-table-column>
<el-table-column prop="enName" label="数据库表" width="200px" align="left" show-overflow-tooltip>
<template #header>
<span>数据库表</span>
<span v-if="!isLook" style="color:red;margin-left: 2px;">*</span>
</template>
<template #default="scope">
<template v-if="!isLook">
<div class="prefix-or-suffix-cell">
<el-input :disabled="tableCreateInfo.isCreate" v-model.trim="tableCreateInfo.inputNameValue"
:maxlength="50" placeholder="必填" />
</div>
</template>
<span v-else>{{ scope.row["enName"] }}</span>
</template>
</el-table-column>
<el-table-column prop="chName" label="主题表名称" width="200px" align="left" show-overflow-tooltip>
<template #header>
<span>主题表名称</span>
<span v-if="!isLook" style="color:red;margin-left: 2px;">*</span>
</template>
<template #default="scope">
<el-input v-if="!isLook" v-model.trim="scope.row['chName']" placeholder="必填" :maxlength="50" />
<span v-else>{{ scope.row["chName"] }}</span>
</template>
</el-table-column>
<el-table-column prop="subjectDomain" label="主题域" width="180px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-input v-if="!isLook" disabled v-model.trim="scope.row['subjectDomain']" />
<span v-else>{{ scope.row["subjectDomain"] }}</span>
</template>
</el-table-column>
<el-table-column prop="tableModel" label="表模型" :width="isLook ? '100px' : '150px'" align="left"
show-overflow-tooltip>
<template #default="scope">
<el-select v-if="dbType == 'doris' && !isLook" v-model="scope.row['tableModel']" placeholder="请选择"
:disabled="tableCreateInfo.isCreate">
<el-option v-for="opt in tableModels" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
</el-select>
<span v-else-if="dbType == 'doris'">{{tableModels.find(t => t.value === scope.row["tableModel"])?.label ||
'-'
}}</span>
<span v-else>--</span>
</template>
</el-table-column>
<el-table-column prop="dimType" label="维表类型" :width="isLook ? '100px' : '150px'" align="left"
show-overflow-tooltip>
<template #default="scope">
<el-select v-if="!isLook" v-model="scope.row['dimType']" placeholder="请选择">
<el-option v-for="opt in dimTypeList" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
</el-select>
<span v-else>{{dimTypeList.find(t => t.value === scope.row["dimType"])?.label || '-'}}</span>
</template>
</el-table-column>
<el-table-column prop="codeColumn" label="编码字段" :width="isLook ? '100px' : '150px'" align="left"
show-overflow-tooltip>
<template #header>
<div class="header_title">
<span>编码字段</span>
<span v-if="!isLook" style="color:red;margin-left: 2px;margin-right: 8px;">*</span>
<el-tooltip placement="top" effect="light" popper-class="table_tooltip">
<template #content>
<div style="max-width: 236px;">
下拉列表来自于该表的字段。
</div>
</template>
<el-icon>
<QuestionFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<template #default="scope">
<el-select v-if="!isLook" v-model="scope.row['codeColumn']" placeholder="请选择">
<el-option v-for="opt in tableSelectFields" :key="opt['enName']" :label="opt['chName']"
:value="opt['enName']" />
</el-select>
<span v-else>{{ scope.row["codeColumn"] || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="codeName" label="编码名称" :width="isLook ? '100px' : '150px'" align="left"
show-overflow-tooltip>
<template #header>
<div class="header_title">
<span>编码名称</span>
<span v-if="!isLook" style="color:red;margin-left: 2px;margin-right: 8px;">*</span>
<el-tooltip placement="top" effect="light" popper-class="table_tooltip">
<template #content>
<div style="max-width: 236px;">
下拉列表来自于该表的字段。
</div>
</template>
<el-icon>
<QuestionFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<template #default="scope">
<el-select v-if="!isLook" v-model="scope.row['codeName']" placeholder="请选择">
<el-option v-for="opt in tableSelectFields" :key="opt['enName']" :label="opt['chName']"
:value="opt['enName']" />
</el-select>
<span v-else>{{ scope.row["codeName"] || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="syncPolicy" label="同步策略" :width="isLook ? '100px' : '150px'" align="left"
show-overflow-tooltip>
<template #default="scope">
<el-select v-if="!isLook" v-model="scope.row['syncPolicy']" placeholder="请选择">
<el-option v-for="opt in syncPolicys" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
</el-select>
<span v-else>{{syncPolicys.find(s => s.value === scope.row["syncPolicy"])?.label || '-'}}</span>
</template>
</el-table-column>
<el-table-column prop="characterSet" label="字符集" :width="isLook ? '100px' : '150px'" align="left"
show-overflow-tooltip>
<template #default="scope">
<el-select v-if="!isLook" v-model="scope.row['characterSet']" placeholder="请选择">
<el-option v-for="opt in characterList" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
</el-select>
<span v-else>{{characterList.find(c => c.value === scope.row["characterSet"])?.label || '-'
}}</span>
</template>
</el-table-column>
<el-table-column prop="description" label="描述" width="220px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-input v-if="!isLook" v-model.trim="scope.row['description']" />
<span v-else>{{ scope.row["description"] || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="100px" align="left" fixed="right" show-overflow-tooltip>
<template #default="scope">
<span class="text_btn" @click="handleClickExpand()" v-preReClick>扩展属性</span>
</template>
</el-table-column>
</el-table>
<div class="tools_btns">
<el-button type="primary" :disabled="isLook" @click="addField">新增</el-button>
<el-button @click="moveUp" :disabled="isLook">上移</el-button>
<el-button @click="moveDown" :disabled="isLook">下移</el-button>
<el-button @click="delFeilds" :disabled="isLook">批量删除</el-button>
</div>
<div class="table_panel">
<el-table ref="fieldStandardTableRef" :data="tableCreateInfo.tableFields" v-loading="tableFieldsLoading"
:highlight-current-row="true" stripe border height="100%" row-key="guid"
@selection-change="selectionFieldsChange" tooltip-effect="light" :style="{
width: '100%',
'max-height': 'calc(100% - 16px)',
display: 'inline-block',
}">
<el-table-column type="selection" v-if="!isLook" :width="32" align="center" />
<el-table-column label="排序" type="index" width="56px" align="center" show-overflow-tooltip>
</el-table-column>
<el-table-column prop="chName" label="字段中文名称" width="150px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-input v-if="scope.row['isEdit']" placeholder="请输入" v-model.trim="scope.row['chName']" />
<span v-else>{{ scope.row["chName"] || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="enName" label="字段英文名" width="150px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-input v-if="scope.row['isEdit'] && !(scope.row['guid'] && tableCreateInfo.isCreate)"
v-model.trim="scope.row['enName']" placeholder="必填"
@input="inputLengthKeyUp(/[^a-zA-Z0-9_]/g, scope, 'enName')" />
<span v-else>{{ scope.row["enName"] || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="dataType" label="字段类型" width="120px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-select v-if="scope.row['isEdit'] && !(tableCreateInfo.isCreate && scope.row['isCreate'] === 'Y')"
v-model="scope.row['dataType']" placeholder="请选择" @change="(val) => dataTypeChange(val, scope)">
<el-option v-for="opt in fieldTypes" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
</el-select>
<span v-else>{{fieldTypes.find(f => f.value === scope.row["dataType"])?.label || '-'}}</span>
</template>
</el-table-column>
<el-table-column prop="fieldLength" label="长度" width="115px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-input
v-if="scope.row['isEdit'] && !(tableCreateInfo.isCreate && scope.row['isCreate'] === 'Y') && (scope.row['dataType'] == 'varchar' || scope.row['dataType'] == 'decimal' || scope.row['dataType'] == 'char')"
v-model.trim="scope.row['fieldLength']" placeholder="必填"
@input="inputLengthKeyUp(/\D/g, scope, 'fieldLength', 2000, 1)" />
<span v-else>{{ scope.row["fieldLength"] == null ? '-' : scope.row["fieldLength"] }}</span>
</template>
</el-table-column>
<el-table-column prop="fieldPrecision" label="精度" width="115px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-input
v-if="scope.row['isEdit'] && !(tableCreateInfo.isCreate && scope.row['isCreate'] === 'Y') && scope.row['dataType'] == 'decimal'"
v-model.trim="scope.row['fieldPrecision']" placeholder="必填"
@input="inputLengthKeyUp(/\D/g, scope, 'fieldPrecision', 30, 1)" />
<span v-else>{{ scope.row["fieldPrecision"] == null ? '-' : scope.row["fieldPrecision"] }}</span>
</template>
</el-table-column>
<el-table-column prop="isPrimary" label="是否主键" width="90px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-select v-if="scope.row['isEdit'] && !tableCreateInfo.isCreate" v-model="scope.row['isPrimary']"
placeholder="请选择">
<el-option v-for="opt in isNotList" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
</el-select>
<span v-else>{{ scope.row["isPrimary"] || '-' }}</span>
</template>
</el-table-column>
<el-table-column v-if="tableCreateInfo.tableData[0].tableModel == 2" prop="aggWay" label="聚合方式" width="120px"
align="left" show-overflow-tooltip>
<template #default="scope">
<el-select v-if="scope.row['isEdit']" v-model="scope.row['aggWay']" placeholder="请选择">
<el-option v-for="opt in aggMethodList" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
</el-select>
<span v-else>{{ scope.row["aggWay"] || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="notNull" label="是否必填" width="90px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-select v-if="scope.row['isEdit'] && !(scope.row.isCreate == 'Y' && scope.row['notNull'] == 'N')"
v-model="scope.row['notNull']" placeholder="请选择">
<el-option v-for="opt in isNotList" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
</el-select>
<span v-else>{{ scope.row["notNull"] || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="defaultValue" label="默认值" width="205px" align="left" show-overflow-tooltip>
<template #default="scope">
<tableDefaultValue :scope="scope" :dbType="dbType"
:readonly="dbType == 'doris' && scope.row.isCreate == 'Y'"></tableDefaultValue>
</template>
</el-table-column>
<el-table-column v-if="!isLook" label="操作" width="100px" align="left" fixed="right" show-overflow-tooltip>
<template #default="scope">
<template v-if="!(tableCreateInfo.isCreate && scope.row['isPrimary'] === 'Y')">
<span class="text_btn" v-if="!scope.row['isEdit']" @click="handleFieldClickEdit(scope)"
v-preReClick>编辑</span>
<span class="text_btn" v-else @click="handleFieldClickSave(scope)" v-preReClick>保存</span>
<el-divider direction="vertical" />
<span class="text_btn" @click="handleFieldDelete(scope)">删除</span>
</template>
<span v-else>--</span>
</template>
</el-table-column>
</el-table>
</div>
<ContentWrap title="流程审批" description="" :isExpand="flowExpand" :expand-swicth="true" class="mb16"
@expand="(v) => flowExpand = v">
<ApprovalProcess v-if="deploymentId" :deploymentId="deploymentId" :definitionId="''">
</ApprovalProcess>
</ContentWrap>
<expandPropertyDialog ref="expandPropertyDialogRef" :is-look="isLook" :partitionAttribute="expandProperties"
:table-create-info="tableCreateInfo" @expandValueChange="expandDialogValueChange" />
</div>
</template>
<style lang="scss" scoped>
.table_tool_wrap {
width: 100%;
height: 100%;
padding: 0 16px;
overflow: hidden auto;
.tools_btns {
padding: 8px 0;
}
:deep(.el-table) {
.cell {
.prefix-or-suffix-cell {
display: inline-flex;
align-items: center;
}
}
}
.table_panel {
height: 268px;
:deep(.el-table) {
& td.el-table__cell {
padding: 2px 0;
height: 36px;
}
}
}
}
:deep(.el-dialog) {
.dialog-form-inline {
.checkbox_input {
display: flex;
flex-direction: column;
.input_panel {
margin: 0;
}
}
.select_group {
.el-form-item__content>.el-input {
margin-top: 21px;
}
}
.radio_panel {
.panel_content {
display: none;
}
}
}
}
:deep(.batchDialog) {
.el-tree-node__content {
padding-left: 8px !important;
.el-icon {
display: none;
}
}
}
.header_title {
margin-left: 8px;
display: flex;
align-items: center;
.el-icon {
color: #b2b2b2;
width: 16px;
height: 16px;
}
}
</style>
\ No newline at end of file
......@@ -26,8 +26,8 @@ function encrypt(txt) {
}
const userStore = useUserStore()
const userInfoData = JSON.parse(localStorage.userData)
// const url: any = 'https://scm.cs4pl.com/portraitBMap?fUrl=portraitMap';
const url: any = 'http://localhost:8086/portraitBMap?fUrl=portraitMap';
const url: any = 'https://scm-operation-test.csbr.cn/portraitBMap?fUrl=portraitMap&tUrl=portraitBMap';
// const url: any = 'http://localhost:8086/portraitBMap?fUrl=portraitMap&tUrl=portraitBMap';
const link = ref('')
const loading = ref(true);
......
<route lang="yaml">
name: metadataStandardCodetable
</route>
<script lang="ts" setup name="metadataStandardCodetable">
import { ref } from 'vue'
import { ElMessage, ElMessageBox } from "element-plus";
import { Search } from '@element-plus/icons-vue'
import Tree from '@/components/Tree/index.vue'
import Table from '@/components/Table/index.vue'
import Drawer from './components/drawer.vue'
import DictFileds from './components/dictFileds.vue'
import useCatchStore from "@/store/modules/catch";
import { download } from '@/utils/common'
import { getParamsList } from '@/api/modules/dataAsset'
import { getStandardCodeList, saveStandardCode,
updateStandardCode, getStandardCodeDetail,
deleteStandardCode, getStandardCodeStandard, exportStandardCodeData,
getStandardCodeDataList, getStandardCodeTree
} from '@/api/modules/dataMetaService'
import {
addDictionary,
deleteDictionary,
getDictionary,
updateDictionary,
getDictionaryTree,
getDictionaryDetail,
exportDictionary,
showDictionary,
getDataBaseList,
getCoderuleList,
saveDictionaryData,
getDictionaryFileds,
updateDictionaryState,
getDictionaryRuleData,
exportDictionaryFileds,
checkDeleteDictionary,
checkDeleteDictionaryScheme,
checkDictionaryData,
getNewDataTypeList
} from '@/api/modules/dataInventory';
import router from '@/router'
import { TableColumnWidth } from '@/utils/enum';
const { proxy } = getCurrentInstance() as any;
const cacheStore = useCatchStore()
// 禁用字段
const forbidFields = [
'guid',
'sharding_flag',
'create_user_id',
'create_user_name',
'update_user_id',
'update_user_name',
'create_time',
'update_time',
'is_deleted'
]
const showFiledsPage = ref(false)
const dictFiledsRef = ref()
const dataTypeList = ref([])
const dataBaseList = ref([])
const codeRuleList = ref([])
const dictTreeRef = ref()
const dictType = ref(-1)
const dictGuid = ref('')
const expandedKey: any = ref([])
const currentNodeKey = ref('')
const showLoading = ref(true)
const treeData = ref([])
const treeInfo = ref({
id: "data-pickup-tree",
filter: true,
queryValue: "",
queryPlaceholder: "请输入关键字搜索",
props: {
label: "label",
value: "value",
isLeaf: "isLeaf",
},
// lazy: true,
nodeKey: 'value',
expandedKey: ['01'],
currentNodeKey: '01',
data: [],
expandOnNodeClick: false,
loading: false,
currentObj: {}
});
const standardOptions = ref([])
const publishingUnitCodeOptions = ref([])
const tableSearchInput = ref('')
const currTableData: any = ref<Object>({});
const page = ref({
limit: 50,
curr: 1,
sizes: [
{ label: "10", value: 10 },
{ label: "50", value: 50 },
{ label: "100", value: 100 },
{ label: "150", value: 150 },
{ label: "200", value: 200 },
],
});
const selectRowData = ref([])
const selectedRowData = ref([])
const tableInfo: any = ref({
id: 'data-source-table',
multiple: true,
fixedSelection: true,
fields: [
{ label: "序号", type: "index", width: 56, align: "center" },
{ label: '代码名称', field: 'codeName', width: 140 },
// { label: '代码编码', field: 'code', width: 140 },
{ label: '标准号', field: 'standard', width: 140 },
{ label: '标准名称', field: 'standardName', width: 140 },
// { label: '启用状态', field: 'bizState', type: 'switch', activeText: '启用', inactiveText: '停用', activeValue: 1, inactiveValue: 0, switchWidth: 56, width: 100, align: 'center' },
{ label: '启用状态', field: 'bizState', type: 'tag', width: 100, align: 'center',getName: (scope) => {
let status = scope.row.bizState;
return status == 'Y' ? '启用' : '停用';
} },
{ label: '创建时间', field: 'createTime', width: TableColumnWidth }
],
data: [],
page: {
type: "normal",
rows: 0,
...page.value,
},
actionInfo: {
label: "操作",
type: "btn",
width: 172, //不要刚好90.缩小浏览器会因为小数点的差距而换行,
fixed: 'right',
btns: [
{ label: '查看', value: 'detail'},
{ label: "编辑", value: "edit" },
{ label: '复制', value: 'copy' },
{ label: "删除", value: "delete" },
],
},
loading: false
})
const dictionaryType = ref(1)
const orginOptions = [
{ label: 'code', value: 'code' },
{ label: 'name', value: 'name' },
]
let codeOptions = ref([])
const formItems: any = ref([
{
label: '标准类型',
type: 'select',
placeholder: '请选择',
field: 'standardTypeCode',
default: '',
options: standardOptions,
clearable: true,
required: true
},
{
label: '代码名称',
type: 'input',
placeholder: '请输入',
field: 'codeName',
default: '',
maxlength: 30,
clearable: true,
required: true
},
{
label: '代码编码',
type: 'input',
placeholder: '请输入',
field: 'code',
default: '',
maxlength: 30,
clearable: true,
// required: true
},
{
label: '标准号',
type: 'input',
placeholder: '请输入',
field: 'standard',
default: '',
maxlength: 50,
clearable: true,
required: true
},
{
label: '标准名称',
type: 'input',
placeholder: '请输入',
field: 'standardName',
default: '',
maxlength: 30,
clearable: true,
required: true
},
{
label: '发布单位',
type: 'select',
placeholder: '请选择',
field: 'publishingUnitCode',
default: '',
options: publishingUnitCodeOptions,
clearable: true,
required: true
},
{
label: '排序',
type: 'input',
placeholder: '请输入',
field: 'orderNum',
default: '',
maxlength: 2,
clearable: true,
required: true
},
{
label: '启用状态',
type: 'switch',
field: 'bizState',
default: 'Y',
activeValue: 'Y',
inactiveValue: 'N'
},
{
label: '代码类型',
type: 'radio-panel',
placeholder: '请选择',
field: 'typeCode',
default: '1',
block: true,
options: [
{ label: '列表结构', value: '1' },
{ label: '层级结构', value: '2' }
],
clearable: true,
required: true
},
{
label: '编码字段',
type: 'select',
placeholder: '请选择',
field: 'codeFields',
default: [],
options: codeOptions,
clearable: true,
required: true,
multiple: true,
tagsTooltip: true,
collapse: true,
filterable: true,
visible: true
},
{
label: '编码名称',
type: 'select',
placeholder: '请选择',
field: 'codeFieldName',
default: '',
options: codeOptions,
clearable: true,
required: true,
visible: true
},
{
label: '层级结构',
type: 'input',
placeholder: '请输入',
field: 'hierarchy',
default: '',
clearable: true,
visible: false
}
])
const formRules: any = ref({
standardTypeCode: { required: true, message: '请选择标准类型' },
codeName: { required: true, message: '请输入代码名称' },
// code: { required: true, message: '请输入代码编码' },
standard: { required: true, message: '请输入标准号' },
standardName: { required: true, message: '请输入标准名称' },
publishingUnitCode: { required: true, message: '请选择发布单位' },
orderNum: {
required: true,
validator: (rule, value, callback) => {
if (value === 0) {
callback();
return;
}
if (!value) {
callback(new Error('请填写排序'));
return;
}
const r = /(^[0-9]([0-9]*)$|^[0-9]$)/; // 正整数(可以以0打头)
if (value && !r.test(value)) {
callback(new Error('请填写大于或等于零整数'));
return;
}
callback();
}
},
typeCode: { required: true, message: '请选择代码类型' },
codeFields: { required: true, message: '请选择编码字段' },
codeFieldName: { required: true, message: '请选择编码名称' }
})
const formInfo = ref({
type: 'form',
title: '',
col: 'span',
formInfo: {
id: 'add-dict-form',
col: 'col2',
readonly: false,
items: formItems.value,
rules: formRules.value
}
})
const orginData = [
{
ROWID: 'field_0',
fieldName: '代码',
description: '编码字段',
fieldType: 'varchar',
fieldLength: '10',
fieldPrecision: '',
// orderNum: '1',
codeRuleGuid: '',
STATUS: 'edit',
STATE: 'Running'
}, {
ROWID: 'field_1',
fieldName: '名称',
description: '编码名称',
fieldType: 'varchar',
fieldLength: '20',
fieldPrecision: '',
// orderNum: '2',
codeRuleGuid: '',
STATUS: 'edit',
STATE: 'Running'
}
]
const orginFields = [
{ label: "序号", type: "index", width: 56, align: "center" },
{ label: "字段名称", field: "fieldName", width: 140 }
]
const formTableData: any = ref([])
const formTableSelectRowData: any = ref([])
const editTableInfo = ref({
fieldName: {
label: '字段名称',
type: 'input',
maxlength: 50,
placeholder: '',
field: 'fieldName',
default: '',
clearable: true,
required: true,
}
})
const formTable = ref({
type: "table",
title: "定义表结构",
col: 'no-margin',
style: {
// height: 'calc(100% - 234px)'
height: 'auto'
},
tableInfo: {
id: "role-authority-table",
multiple: true,
minHeight: 'unset',
maxHeight: '100%',
fields: [],
data: [],
showPage: false,
actionInfo: {
show: true,
label: "操作",
type: "btn",
width: 60,
fixed: 'right',
btns: [
{ label: "删除", value: "remove" },
]
},
editInfo: {},
readonly: false,
// col: 'auto-height',
style: {
// height: 'calc(100% - 40px)'
},
loading: false
},
tableTool: {
// col: 'float-right',
visible: false,
btns: [
{ label: "新增行", value: "add-row", type: 'primary' },
{ label: "批量删除", value: "remove_batch" },
]
},
})
const fieldSheetInfo: any = ref({})
const uploadTableData: any = ref([])
const uploadSelectRowData: any = ref([])
const uploadTableFields: any = ref([])
const uploadTableInfo = ref({
id: "role-authority-table",
multiple: true,
// minHeight: 'unset',
// maxHeight: '100%',
fields: [],
data: [],
showPage: false,
actionInfo: {
label: "操作",
type: "btn",
width: 60,
btns: [
{ label: "删除", value: "remove" },
],
},
editInfo: {},
readonly: false,
loading: false
})
const uploadFiles: any = ref([])
const uploadInfo = ref({
type: 'upload',
title: '添加表数据',
col: 'row-reverse',
uploadInfo: {
id: 'upload-file-form',
action: '',
auto: false,
fileList: [],
accept: '.xlsx, .xls',
cover: true,
triggerBtn: {
label: '导入文件',
value: 'import_file',
icon: 'Upload',
plain: true,
},
toolBar: [
{ label: '下载模板', value: 'export_model', plain: true, icon: 'Download' },
]
// showList: false,
},
tableInfo: {},
tools: {
col: 'right',
visible: true,
btns: [
{ label: '树形显示', value: 'show_tree', type: 'primary', plain: true, visible: false },
{ label: '新增行', value: 'add_row', type: 'primary', plain: true },
{ label: '批量删除', value: 'remove_batch', plain: true },
]
}
})
const fieldTableInfo = ref({
type: 'field-table',
title: '表数据',
tableInfo: {
id: 'dict-field-table',
minHeight: 'unset',
maxHeight: '100%',
fields: [],
data: [],
loading: false,
showPage: false,
actionInfo: {
show: false
},
col: 'auto-height'
}
})
const contents = ref({
add: [
formInfo.value,
formTable.value,
],
upload: [
formInfo.value,
uploadInfo.value,
],
sheet: [
formInfo.value,
formTable.value,
],
field: [
formInfo.value,
fieldTableInfo.value,
]
})
const drawerRef = ref()
const drawerInfo: any = ref({
visible: false,
direction: "rtl",
modalClass: "",
modalClose: false,
modal: true,
size: 700,
header: {
title: "新增",
},
type: '',
container: {
contents: [],
},
footer: {
btns: [
{ type: 'default', label: '取消', value: 'cancel' },
{ type: 'primary', label: '保存', value: 'submit', visible: true },
{ type: 'primary', label: '保存并添加数据', value: 'saveAndAdd', visible: true },
]
},
})
const setFormItems = (row: any = null) => {
// formItems.value = JSON.parse(JSON.stringify(orginItems))
formItems.value.map(item => {
item.default = row ? row[item.field] : ''
})
formInfo.value.formInfo.readonly = drawerInfo.value.type == 'detail'
formInfo.value.formInfo.items = formItems.value
// for(var e in editTableInfo.value){
// const editItem = editTableInfo.value[e]
// if(editItem.field != 'description' && editItem.field != 'codeRuleGuid'){
// editItem.disabled = drawerInfo.value.type != 'add'
// }
// }
}
// 获取数据类型列表
const getDataType = () => {
let params = {
dictType: "字段类型"
}
getNewDataTypeList(params).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data
editTableInfo.value['fieldType'].options = data
} else {
ElMessage({
type: 'error',
message: res.msg,
})
}
})
}
// 获取数据库列表
const getDataBase = () => {
const params = {
pageIndex: 1,
connectStatus: '1'
}
getDataBaseList(params).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data ?? []
data.map(item => {
item.label = item.databaseNameZh
item.value = item.guid
})
dataBaseList.value = data
} else {
ElMessage({
type: 'error',
message: res.msg,
})
}
})
}
// 获取编码规则列表
const getCodeRuleData = () => {
let params = {}
getCoderuleList(params).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data
data?.map(item => {
item.label = item.ruleName
item.value = item.guid
})
codeRuleList.value = data
editTableInfo.value['codeRuleGuid'].options = data
} else {
ElMessage({
type: 'error',
message: res.msg,
})
}
})
}
const treePromise = ref();
const getTreeData = (needClick = false, currData = {}) => {
return getParamsList({ dictType: '标准类型'}).then((res:any) => {
if (res.code === proxy.$passCode) {
const data = res.data || []
data.forEach(item => {
item.treeLevel = 1
})
treeInfo.value.data = data
standardOptions.value = data
}
})
}
const getFirstPageData = () => {
page.value.curr = 1;
toSearch({})
}
const toSearch = (val: any, clear: boolean = false) => {
let params: any = Object.keys(val).length ? { ...val } : {}
let { currentNodeKey, currentObj } = treeInfo.value
console.log('currentObj', currentObj)
params.pageIndex = page.value.curr;
params.pageSize = page.value.limit;
params.keyWord = tableSearchInput.value
if (currentObj.treeLevel === 1) {
params.standardTypeCode = currentNodeKey
} else if (currentObj.treeLevel === 2) {
params.standardTypeCode = currentObj.standardTypeCode
params.standardName = currentObj.value
}
getTableData(params);
};
const getTableData = (params) => {
tableInfo.value.loading = true
getStandardCodeList(params).then((res:any) => {
if (res.code === proxy.$passCode) {
let data = res.data
let list = res.data.records || []
list = list.map((item,index) => {
item.label = item.codeName
item.value = item.guid
item.index = index
return item
})
tableInfo.value.data = list
tableInfo.value.page.limit = data.pageSize
tableInfo.value.page.curr = data.pageIndex
tableInfo.value.page.rows = data.totalRows
}
tableInfo.value.loading = false
}).catch(xhr => {
tableInfo.value.loading = false
})
};
const tableSelectionChange = (val, tId) => {
val.sort((pre, cur) => pre.index - cur.index)
if (drawerInfo.value.visible) {
if (formItems.value.length == 2) {
uploadSelectRowData.value = val
} else {
formTableSelectRowData.value = val
}
} else {
selectRowData.value = val.map((item) => item.guid);
selectedRowData.value = val
}
};
const tablePageChange = (info) => {
page.value.curr = Number(info.curr);
page.value.limit = Number(info.limit);
toSearch({});
};
const tableInputChange = (val, scope) => {
if (forbidFields.indexOf(val) > -1) {
ElMessage({
type: "error",
message: '该名称已存在,请填写其他名称',
});
return
}
setCodeOptions()
}
const setCodeOptions = () => {
let opts: any = []
formTableData.value.map(item => {
if (item.fieldName) {
const row = JSON.parse(JSON.stringify(item))
row.label = item.fieldName
row.value = item.fieldName
opts.push(row)
}
})
codeOptions.value = opts
}
const toolBtnClick = (btn, data) => {
console.log(formItems.value.length)
const type = btn.value
if (data) {
if (type.indexOf('add') > -1) {
if (formItems.value.length == 2) {
const params = {
guid: fieldSheetInfo.value.guid
}
uploadTableInfo.value.loading = true
getDictionaryRuleData(params).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data ?? {}
let rowInfo: any = {}
uploadTableFields.value.map(item => {
rowInfo[item.field] = data[item.field] ?? ''
})
rowInfo.STATUS = 'edit'
rowInfo.STATE = 'Running'
rowInfo.ROWID = `upload_${uploadTableData.value.length}`
uploadTableData.value.push(rowInfo)
uploadTableInfo.value.data = uploadTableData.value
scrollTable(rowInfo)
} else {
ElMessage({
type: 'error',
message: res.msg
})
}
uploadTableInfo.value.loading = false
}).catch(() => {
uploadTableInfo.value.loading = false
})
} else {
const rowInfo = {
ROWID: `formData_${formTableData.value.length}`,
fieldName: '',
description: '',
fieldType: '',
fieldLength: '',
fieldPrecision: '',
orderNum: '',
codeRuleGuid: '',
STATUS: 'edit',
STATE: 'Running'
}
let list: any = formTableData.value
list.push(rowInfo)
formTable.value.tableInfo.data = list
scrollTable(rowInfo)
}
} else if (type.indexOf('remove') > -1) {
if (formItems.value.length == 2) {
if (uploadSelectRowData.value.length == 0) {
ElMessage({
type: "error",
message: '请选择需要删除的数据',
});
return
}
uploadSelectRowData.value.map(item => {
const existIndex = uploadTableData.value.findIndex(s => s.ROWID == item.ROWID)
existIndex > -1 && uploadTableData.value.splice(existIndex, 1)
})
} else {
if (formTableSelectRowData.value.length == 0) {
ElMessage({
type: "error",
message: '请选择需要删除的数据',
});
return
}
const removeRows = () => {
formTableSelectRowData.value.map(item => {
const existIndex = formTableData.value.findIndex(s => s.ROWID == item.ROWID)
existIndex > -1 && formTableData.value.splice(existIndex, 1)
})
}
if (drawerInfo.value.type == 'edit') {
const editRows = formTableSelectRowData.value.filter(item => item.guid !== undefined)
if (editRows.length) {
// formTable.value.tableInfo.loading = true
checkDelete().then((res: any) => {
if (res) {
ElMessageBox.confirm("数据字典有数据, 确定是否继续删除?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: 'warning',
}).then(() => {
removeRows()
})
} else {
removeRows()
}
// formTable.value.tableInfo.loading = false
}).catch(xhr => {
ElMessage({
type: 'error',
message: xhr
})
formTable.value.tableInfo.loading = false
})
} else {
removeRows()
}
} else {
removeRows()
}
}
}
} else {
if (type == 'export_model') {
exportData('model')
} else if (type == 'import_file') {
importData()
}
}
}
const tableSwitchBeforeChange = (scope, field, callback) => {
const msg = '确定修改状态?'
ElMessageBox.confirm(
msg,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
const state = scope.row[field] == 1 ? 0 : 1
const result = tableSwitchChange(state, scope, field)
callback(result)
}).catch(() => {
callback(false)
})
}
const tableSwitchChange = (val, scope, field) => {
return new Promise((resolve, reject) => {
let params = {
guid: scope.row.guid,
dictionaryState: val
}
updateDictionaryState(params).then((res: any) => {
if (res.code == proxy.$passCode && res.data) {
getFirstPageData()
ElMessage({
type: "success",
message: '状态修改成功',
});
resolve(true)
} else {
ElMessage({
type: "error",
message: res.msg,
});
reject(false)
}
}).catch(() => {
reject(false)
})
})
}
const tableBtnClick = (scope, btn) => {
const type = btn.value;
const row = scope.row;
if (type == "edit" || type === 'copy') {
fieldTableInfo.value.tableInfo.fields = []
fieldTableInfo.value.tableInfo.data = []
drawerInfo.value.header.title = type == 'edit' ? "编辑标准代码" : "复制标准代码";
// drawerInfo.value.modalClass = type == 'edit' ? '' : 'wrap_width_auto'
drawerInfo.value.type = type
drawerInfo.value.footer.btns.forEach(item => {
if (item.value === 'submit' || item.value === 'saveAndAdd') {
item.visible = type === 'detail' ? false : true
}
})
drawerInfo.value.visible = true;
drawerInfo.value.loading = true;
formTable.value.tableInfo.actionInfo.show = type === 'detail' ? false : true
formTable.value.tableTool.visible = type === 'detail' ? false : true
formTable.value.tableInfo.loading = true
getStandardCodeDetail(row.guid).then((res: any) => {
if (res.code == proxy.$passCode && res.data) {
let data = res.data
currTableData.value = data;
setDetailInfo(type)
if (type === 'edit') {
checkDelete().then((res:any) => {
formItems.value.find(v => v.field === 'typeCode')['disabled'] = res ? true : false
})
}
} else {
ElMessage({
type: "error",
message: res.msg,
});
}
formTable.value.tableInfo.loading = false
drawerInfo.value.loading = false;
}).catch(() => {
formTable.value.tableInfo.loading = false
drawerInfo.value.loading = false;
})
// 代码有数据后不允许编辑代码类型
} else if (type === 'remove') {
let removeRows = () => {
const existIndex = formTableData.value.findIndex(s => {
if (s.ROWID) {
return s.ROWID == row.ROWID
}
return s.guid == row.guid
})
existIndex > -1 && formTableData.value.splice(existIndex, 1)
}
if (drawerInfo.value.type == 'edit') {
checkDelete().then((res:any) => {
if (res) {
ElMessageBox.confirm("标准代码有数据, 确定是否继续删除?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: 'warning',
}).then(() => {
removeRows()
})
} else {
removeRows()
}
})
} else {
removeRows()
}
}
else if (type == "delete") {
currTableData.value = row;
tableInfo.value.loading = true
open("此操作将永久删除, 是否继续?", "warning");
tableInfo.value.loading = false
} else if (type == 'detail') {
showFiledsPage.value = true
nextTick(() => {
dictFiledsRef.value.standardGuid = row.guid
dictFiledsRef.value.standardName = row.standardName
treeCurrentNodeKey.value = row.guid
treeInfo.value.currentNodeKey = row.guid
dictFiledsRef.value.getFirstPageData()
})
}
}
const checkDelete = (isBatch: any = false) => {
return new Promise((resolve, reject) => {
let params: any = {
pageIndex: 1,
pageSize: -1,
standardGuid: currTableData.value.guid
}
getStandardCodeDataList(params).then((res: any) => {
if (res.code == proxy.$passCode) {
if (res.data.records.length) {
resolve(res.data)
} else {
resolve(null)
}
} else {
reject(res.msg)
}
}).catch((xhr: any) => {
reject(xhr.msg)
})
})
}
const open = (msg, type, isBatch = false) => {
ElMessageBox.confirm(msg, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: type,
}).then(() => {
let guids = [currTableData.value.guid]
if (isBatch) {
guids = selectRowData.value
}
tableInfo.value.loading = true
deleteStandardCode(guids).then((res: any) => {
if (res.code == proxy.$passCode) {
initTree().then(() => getFirstPageData())
// getFirstPageData()
ElMessage({
type: "success",
message: "删除成功",
});
} else {
ElMessage({
type: "error",
message: res.msg,
});
}
tableInfo.value.loading = false
}).catch(() => {
tableInfo.value.loading = false
});
});
};
const onUpload = (file, fileList) => {
uploadFiles.value = fileList
}
const uploadFile = (file) => {
importData(file)
}
const exportData = (type: any = null) => {
let body = {
standardCodeGuid: treeCurrentNodeKey.value,
standardCodeDataGuids: []
}
exportStandardCodeData(body).then((res:any) => {
if (res && !res.msg) {
download(res, '标准代码表.xlsx', 'excel')
} else {
res?.msg && ElMessage.error(res?.msg);
}
})
return
}
const importData = (file: any = null) => {
let params = new FormData()
if (file) {
params.append("file", file.raw);
} else {
if (uploadFiles.value.length) {
uploadFiles.value.forEach((item: any, index: number) => {
params.append("file", item.raw);
});
}
}
params.append("dictionaryGuid", fieldSheetInfo.value.guid);
showDictionary(params).then((res: any) => {
if (res.code == proxy.$passCode) {
let data = res.data ?? []
setUploadDataInfo(data)
} else {
ElMessage({
type: "error",
message: res.msg,
});
}
})
}
// 生成表头
const setUploadDataFields = (data) => {
let fields: any = [], editInfo: any = {}
data.map(item => {
let fieldItem: any = {
label: item.description, field: item.fieldName, width: 140
}
if (drawerInfo.value.type != 'detail') {
fieldItem.type = 'edit'
fieldItem.columClass = 'edit-colum'
editInfo[item.fieldName] = {
label: '',
type: 'input',
placeholder: '',
field: item.fieldName,
default: '',
disabled: item.codeRuleGuid ? true : false,
clearable: true,
}
}
fields.push(fieldItem)
})
uploadTableFields.value = fields
if (drawerInfo.value.type == 'detail') {
fieldTableInfo.value.tableInfo.fields = uploadTableFields.value
} else {
uploadTableInfo.value.fields = uploadTableFields.value
uploadTableInfo.value.editInfo = editInfo
}
}
// 生成表数据
const setUploadDataInfo = async (info) => {
let data = info
if (drawerInfo.value.type == 'detail') {
data = info.jsonArray ?? []
const fields = info.schemaDataVOS ?? []
setUploadDataFields(fields)
}
if (drawerInfo.value.type == 'detail') {
fieldTableInfo.value.tableInfo.data = data
drawerInfo.value.container.contents = contents.value['field']
} else {
// 设置表数据
data.map((item, i) => {
item.STATUS = 'edit'
item.STATE = 'Running'
item.ROWID = `upload_${i}`
})
uploadTableData.value = data
uploadTableInfo.value.data = uploadTableData.value
}
}
const loadDrawer = async () => {
drawerInfo.value.visible = true;
drawerInfo.value.type = 'add';
drawerInfo.value.header.title = '新增标准代码';
drawerInfo.value.footer.visible = true;
// drawerInfo.value.header.title = '新增数据字典'
// drawerInfo.value.type = 'add'
// drawerInfo.value.modalClass = '';
// await setFormItems()
let fields = JSON.parse(JSON.stringify(orginFields))
fields.map((item: any) => {
if (!item.type || item.type != 'index') {
item.type = 'edit'
item.columClass = 'edit-colum'
}
})
formTable.value.tableInfo.fields = fields
formItems.value.forEach(item => {
if (item.field === 'bizState') {
item.default = 'Y'
} else if (item.field === 'typeCode') {
item.default = '1'
} else if (item.field === 'hierarchy') {
item.default = ''
item.visible = false
}
else {
item.default = ''
}
item.disabled = false
})
formInfo.value.formInfo.items = formItems.value
formTableData.value = JSON.parse(JSON.stringify(orginData))
formTable.value.tableInfo.data = formTableData.value
formTable.value.tableInfo.editInfo = JSON.parse(JSON.stringify(editTableInfo.value))
formTable.value.tableInfo.readonly = false
formTable.value.tableInfo.multiple = true
formTable.value.tableInfo.actionInfo.show = true
formTable.value.tableTool.visible = true
drawerInfo.value.container.contents = contents.value['add']
drawerInfo.value.footer.btns.forEach(item => {
item.visible = true
})
drawerInfo.value.visible = true
console.log('table', formTable.value)
console.log('formInfo', formInfo)
setCodeOptions()
};
const treeCurrentNodeKey = ref('')
const batching = (type) => {
if (type == 'delete') {
if (selectRowData.value.length == 0) {
ElMessage({
type: 'error',
message: '请选择需要删除的数据',
})
return
}
tableInfo.value.loading = true
open("此操作将永久删除, 是否继续?", "warning", true);
tableInfo.value.loading = false
}
if (type == 'export') {
if (selectRowData.value.length == 0) {
ElMessage({
type: 'error',
message: '请选择代码名称',
})
return
}
if (selectRowData.value.length > 30) {
ElMessage.error('选择的代码名称不能超过30个')
return
}
// console.log(selectedRowData)
let uploadSetting = selectedRowData.value.map(item => {
return {
standardName: item.codeName,
standardGuid: item.guid
}
})
cacheStore.setCatch('uploadSetting', uploadSetting)
router.push({
path: '/data-meta/metadata-standard/standard-import',
});
}
};
const nodeClick = (data) => {
console.log('treeNodeClick', data)
drawerInfo.value.visible = false
const changeCont = () => {
nextTick(() => {
treeInfo.value.currentNodeKey = data.value
treeInfo.value.currentObj = data
if (data.isLeaf) {
showFiledsPage.value = true
nextTick(() => {
dictFiledsRef.value.standardGuid = data.value
dictFiledsRef.value.standardName = data.label
treeCurrentNodeKey.value = data.value
dictFiledsRef.value.getFirstPageData()
})
} else {
showFiledsPage.value = false
getFirstPageData()
}
})
}
if (showFiledsPage.value) {
const toChange = dictFiledsRef.value.checkSave()
if (!toChange) {
ElMessageBox.confirm(
'存在未保存的数据,切换后会丢失,是否确定切换',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
changeCont()
}).catch(() => {
treeInfo.value.currentNodeKey = dictGuid.value
})
} else {
changeCont()
}
} else {
changeCont()
}
}
function loadTreeNode (node, resolve) {
console.log('node', node)
if (node.isLeaf) {
return resolve([]);
}
if (node.level === 1) {
let standardTypeCode = node.data.value
getStandardCodeStandard(standardTypeCode).then((res:any) => {
if (res.code === proxy.$passCode) {
let data = res.data || []
let uniqList = Array.from(new Set(data)).map(item => {
return {
label: item,
value: item,
treeLevel: 2,
standardTypeCode
}
})
resolve(uniqList)
}
})
}
if (node.level === 2) {
let params = {
pageIndex: 1,
pageSize: -1,
standardTypeCode: node.data.standardTypeCode,
standardName: node.data.value
}
getStandardCodeList(params).then((res:any) => {
if (res.code === proxy.$passCode) {
const list = res.data.records || []
list.forEach(item => {
item.isLeaf = true
item.label = item.codeName
item.value = item.guid
})
resolve(list)
}
})
}
}
// 设置详情信息
const setDetailInfo = (type) => {
console.log('type', type)
const row = JSON.parse(JSON.stringify(currTableData.value))
formItems.value.forEach(item => {
item.default = row[item.field] || ''
item.disabled = type === 'detail' ? true : false
})
formInfo.value.formInfo.items = formItems.value
formTableData.value = row.standardCodeFields.map(item => {
item.STATUS = 'edit'
return item
})
let fields = JSON.parse(JSON.stringify(orginFields))
fields.map((item: any) => {
if (item.type != 'index') {
item.type = 'edit'
item.columClass = 'edit-colum'
}
})
formTable.value.tableInfo.fields = fields
formTable.value.tableInfo.data = formTableData.value
formTable.value.tableInfo.editInfo = JSON.parse(JSON.stringify(editTableInfo.value))
formTable.value.tableInfo.readonly = type === 'detail' ? true : false
formTable.value.tableInfo.multiple = true
// formTable.value.tableInfo.actionInfo.show = true
// formTable.value.tableTool.visible = true
drawerInfo.value.container.contents = contents.value['add']
drawerInfo.value.visible = true
// if (row.typeCode)
formItems.value.find(v => v.field === 'hierarchy')['visible'] = row.typeCode == 1 ? false : true
setCodeOptions()
}
const saveData = async (params, btnValue) => {
// const passInfo = await checkParamsData()
console.log('params', params)
let request = drawerInfo.value.type === 'edit' ? updateStandardCode : saveStandardCode
request(params).then((res:any) => {
if (res.code === proxy.$passCode) {
ElMessage.success('操作成功')
initTree().then(() => {
drawerInfo.value.visible = false
if (btnValue === 'saveAndAdd') {
// 跳转代码详情
toStandardCodeDetail(res.data)
} else {
getFirstPageData()
}
})
} else {
ElMessage.error(res.msg)
}
}).finally(() => {
drawerInfo.value.footer.btns.map((item: any) => item.disabled = false)
})
}
function toStandardCodeDetail (guid) {
if (drawerInfo.value.type === 'add' || drawerInfo.value.type === 'copy') {
getStandardCodeDetail(guid).then((res:any) => {
if (res.code == proxy.$passCode) {
let { guid, codeName } = res.data
showFiledsPage.value = true
nextTick(() => {
dictFiledsRef.value.standardGuid = guid
dictFiledsRef.value.standardName = codeName
treeCurrentNodeKey.value = guid
treeInfo.value.currentNodeKey = guid
dictFiledsRef.value.getFirstPageData()
})
}
})
} else {
console.log('currentData', currTableData.value)
let { guid, codeName } = currTableData.value
showFiledsPage.value = true
nextTick(() => {
dictFiledsRef.value.standardGuid = guid
dictFiledsRef.value.standardName = codeName
treeCurrentNodeKey.value = guid
treeInfo.value.currentNodeKey = guid
dictFiledsRef.value.getFirstPageData()
})
}
}
const scrollTable = (rowInfo) => {
nextTick(() => {
const drawerBody = document.getElementsByClassName('el-drawer__body')[0];
const tableListRef = drawerRef.value.getDrawerConRef("drawerTableRef");
if (!tableListRef) return;
//设置选中表格当前新增行。
tableListRef.setCurrentRow(rowInfo);
drawerBody.scrollTop = drawerBody.scrollHeight;
let table = tableListRef.layout.table.refs;
// 获取表格滚动元素
let tableScrollEle =
table.bodyWrapper.firstElementChild.firstElementChild;
// 设置表格滚动的位置
tableScrollEle.scrollTop = tableScrollEle.scrollHeight;
});
};
const drawerBtnClick = (btn, info) => {
console.log('btn', btn, info)
console.log('table', formTable.value)
let params = {
standardCodeFields: formTable.value.tableInfo.data.map(item => {
let obj = {
fieldName: item.fieldName,
guid: item.guid || null,
standardGuid: item.standardGuid || null
}
return obj
}),
...info
}
if (params.typeCode === '1' || !params.hierarchy) {
delete params.hierarchy
}
if (drawerInfo.value.type === 'edit') {
params.guid = currTableData.value.guid
}
if (btn.value == 'submit' || btn.value == 'saveAndAdd') {
let uniqFieldNameList = Array.from(new Set(formTable.value.tableInfo.data.map(item => item.fieldName)))
if (uniqFieldNameList.length !== formTable.value.tableInfo.data.length) {
ElMessage.error('表结构字段名称需唯一,不允许重名')
return
}
let standardCodeFields = params.standardCodeFields.map(v => v.fieldName).join()
let codeFieldName = params.codeFieldName
let codeFields = params.codeFields
let fieldValidate = false
if (standardCodeFields.includes(codeFieldName) && codeFields.every(v => standardCodeFields.includes(v))) {
fieldValidate = true
}
if (!fieldValidate) {
ElMessage.error('所选的编码字段和编码名称必须在字段名称中有')
return
}
drawerInfo.value.footer.btns.map((item: any) => item.disabled = true)
saveData(params,btn.value)
} else {
drawerInfo.value.footer.btns.map((item: any) => delete item.disabled)
nextTick(() => {
drawerInfo.value.visible = false
})
}
}
const radioGroupChange = async (val, info) => {
// console.log(val, info)
await setFormItems(info)
// setGroup()
if (val == 1) {
// 列表结构
formItems.value.find(v => v.field === 'hierarchy')['visible'] = false
} else {
formItems.value.find(v => v.field === 'hierarchy')['visible'] = true
}
}
function initTree () {
return Promise.all([getParamsList({ dictType: '标准类型'}), getStandardCodeTree()]).then((resList:any) => {
let treeRoot = resList[0].data || []
let treeData = resList[1].data || []
// console.log('treeRoot', treeRoot)
// console.log('treeData', treeData)
treeData.forEach(item => {
if (item.children) {
item.children.forEach(subItem => {
// 二级标准name 为 value
subItem.treeLevel = 2
subItem.label = subItem.name
subItem.value = subItem.name
if (subItem.children) {
subItem.children.forEach(threeItem => {
threeItem.label = threeItem.name
threeItem.value = threeItem.guid
threeItem.isLeaf = threeItem.isCode
})
}
})
}
})
let tree = treeRoot.map(item => {
item.treeLevel = 1
let target = treeData.find(v => v.guid === item.value)
item.children = target ? target.children : null
return item
})
// console.log('treeData', treeData)
// console.log('tree', tree)
standardOptions.value = treeRoot
treeInfo.value.data = tree
})
}
onBeforeMount(() => {
// getDataType()
initTree().then(() => {
nodeClick(treeInfo.value.data[0])
})
// getTreeData().then(() => {
// // 默认展开第一个
// let data = treeInfo.value.data
// if (data.length === 0) return
// let params = {
// pageIndex: 1,
// pageSize: 50,
// standardTypeCode: data[0].value,
// // codeName: '标准类型'
// }
// getStandardCodeList(params).then((res:any) => {
// if (res.code === proxy.$passCode) {
// let data = res.data
// data.records && data.records.forEach((item,index) => item.index = index)
// tableInfo.value.data = data.records
// tableInfo.value.page.limit = data.pageSize
// tableInfo.value.page.curr = data.pageIndex
// tableInfo.value.page.rows = data.totalRows
// }
// })
// })
getParamsList({ dictType: '发布单位' }).then((res:any) => {
if (res.code === proxy.$passCode) {
const data = res.data || []
publishingUnitCodeOptions.value = data
}
})
})
onMounted(() => {
});
</script>
<template>
<div class="container_wrap full flex">
<div class="aside_wrap">
<div class="aside_title">标准代码列表</div>
<!-- <Tree ref="dictTreeRef" :treeInfo="treeInfo" @nodeClick="nodeClick" @loadNode="loadTreeNode"/> -->
<Tree ref="dictTreeRef" :treeInfo="treeInfo" @nodeClick="nodeClick" />
</div>
<DictFileds ref="dictFiledsRef" v-if="showFiledsPage" @exportData="exportData" />
<div class="main_wrap" v-else>
<div class="table_tool_wrap">
<div class="tools_btns">
<el-button type="primary" @click="loadDrawer" v-preReClick>新建</el-button>
<el-button @click="batching('export')" v-preReClick>批量导入</el-button>
<el-button @click="batching('delete')" v-preReClick>批量删除</el-button>
</div>
<el-input class="table_search_input" v-model.trim="tableSearchInput" placeholder="请输入关键字搜索"
:suffix-icon="Search" clearable @change="val => getFirstPageData()" />
</div>
<div class="table_panel_wrap full">
<Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" @tableSelectionChange="tableSelectionChange"
@tablePageChange="tablePageChange" @tableSwitchBeforeChange="tableSwitchBeforeChange" />
</div>
</div>
<Drawer ref="drawerRef"
class="table-no-scorll"
:drawerInfo="drawerInfo" @drawerBtnClick="drawerBtnClick"
@radioGroupChange="radioGroupChange" @drawerTableBtnClick="tableBtnClick"
@drawerTableSelectionChange="tableSelectionChange" @drawerTableToolBtnClick="toolBtnClick"
@drawerTableInputChange="tableInputChange" @drawerToolBtnClick="toolBtnClick" @onUpload="onUpload"
@uploadFile="uploadFile" @uploadBtnClick="toolBtnClick" />
</div>
</template>
<style lang="scss" scoped>
.container_wrap {
.aside_wrap {
width: 200px;
}
}
.tree_panel {
height: 100%;
padding-top: 0;
:deep(.el-tree) {
margin: 0;
height: calc(100% - 68px);
overflow: hidden auto;
}
}
</style>
<style lang="scss">
.table-no-scorll {
.el-scrollbar__bar.is-vertical {
display: none!important;
}
}
/* .el-scrollbar__bar.is-vertical {
display: none!important;
} */
</style>
<route lang="yaml">
name: metadataStandardCodetable
</route>
<script lang="ts" setup name="metadataStandardCodetable">
import { ref, reactive } from 'vue';
import { ElMessage } from "element-plus";
import Tree from '@/components/Tree/index.vue';
import TableTools from '@/components/Tools/table_tools.vue';
import { Search } from '@element-plus/icons-vue'
import { useRouter, useRoute } from "vue-router";
import { TableColumnWidth } from '@/utils/enum';
import { commonPageConfig } from '@/components/PageNav/index';
import { getParamsList } from '@/api/modules/dataAsset'
import { getStandardCodeList, saveStandardCode, updateStandardCode } from '@/api/modules/dataMetaService'
const { proxy } = getCurrentInstance() as any;
const router = useRouter();
const treeInfo:any = reactive({
id: "data-pickup-tree",
filter: true,
queryValue: "",
queryPlaceholder: "请输入关键字搜索",
props: {
label: "label",
value: "value",
},
nodeKey: 'value',
expandedKey: ['01'],
currentNodeKey: '01',
expandOnNodeClick: false,
data: [],
loading: false
});
function nodeClick (data) {
}
/** 分页及搜索传参信息配置。 */
const tableSearchInput = ref('')
function getFirstPageData () {
page.value.curr = 1
toSearch({})
}
function toSearch (val: any, clear: boolean = false) {
let params: any = Object.keys(val).length ? { ...val } : {}
params.pageIndex = page.value.curr;
params.pageSize = page.value.limit;
params.name = tableSearchInput.value
getTableData(params);
}
const page = ref({
...commonPageConfig,
collectTaskName: '',
dataSourceGuid: '',
taskState: null
});
const tableInfo = ref({
id: 'data-source-table',
fields: [
{ label: '序号', type: 'index', width: TableColumnWidth.INDEX, align: 'center' },
{ label: '代码名称', field: '1', width: 140 },
{ label: '标准号', field: '2', width: 140 },
{ label: '标准名称', field: '3', width: 140 },
{ label: '启用状态', field: '4', width: 140 },
{ label: '创建时间', field: '5', width: 140 }
],
data: [],
page: {
type: 'normal',
rows: 0,
...page.value
},
actionInfo: {
label: '操作',
type: 'btn',
width: 160,
btns: scope => {
let row = scope.row
return [
{ label: '编辑', value: 'edit' },
{ label: '删除', value: 'delete' },
]
}
},
loading: false
})
function tableBtnClick (scope, btn) {
}
function tablePageChange (info) {
page.value.curr = Number(info.curr);
page.value.limit = Number(info.limit);
// getTableData();
}
function getTableData (params) {
}
function handleCreate () {
drawerInfo.value.visible = true;
drawerInfo.value.type = 'add';
drawerInfo.value.header.title = '新增标准代码';
drawerInfo.value.footer.visible = true;
}
// drawer form
const standardOptions = ref([])
const publishingUnitCodeOptions = ref([])
const formRef = ref()
const formItems: any = ref([
{
label: '标准类型',
type: 'select',
placeholder: '请选择',
field: 'standardTypeCode',
default: '',
options: standardOptions,
clearable: true,
required: true
},
{
label: '代码名称',
type: 'input',
placeholder: '请输入',
field: 'codeName',
default: '',
maxlength: 20,
clearable: true,
required: true
},
{
label: '标准号',
type: 'input',
placeholder: '请输入',
field: 'standard',
default: '',
maxlength: 50,
clearable: true,
required: true
},
{
label: '标准名称',
type: 'input',
placeholder: '请输入',
field: 'standardName',
default: '',
maxlength: 20,
clearable: true,
required: true
},
{
label: '发布单位',
type: 'select',
placeholder: '请输入',
field: 'publishingUnitCode',
default: '',
options: publishingUnitCodeOptions,
clearable: true,
required: true
},
{
label: '排序',
type: 'inputNumber',
placeholder: '请输入',
field: 'orderNum',
default: '',
maxlength: 2,
clearable: true,
required: true
},
{
label: '启用状态',
type: 'switch',
field: 'bizState',
default: false
},
{
label: '代码类型',
type: 'select',
placeholder: '请选择',
field: 'typeCode',
default: '1',
options: [
{ label: '列表结构', value: '1' },
{ label: '层级结构', value: '2' }
],
clearable: true,
required: true
},
{
label: '编码字段',
type: 'select',
placeholder: '请选择',
field: 'codeFields',
default: [],
options: [
{ label: 'code', value: 'code' },
{ label: 'name', value: 'name' }
],
clearable: true,
required: true,
multiple: true
},
{
label: '编码名称',
type: 'select',
placeholder: '请选择',
field: 'codeFieldName',
default: '',
options: [
{ label: 'code', value: 'code' },
{ label: 'name', value: 'name' }
],
clearable: true,
required: true
},
{
label: '层级结构',
type: 'input',
placeholder: '请输入',
field: 'hierarchy',
default: '',
clearable: true,
required: true
}
])
const formRules = ref({
})
const formInfo = ref({
type: 'form',
title: '',
col: 'span',
formInfo: {
id: 'add-dict-form',
col: 'col2',
items: formItems.value,
formRules: formRules.value
}
})
const formTable = reactive({
type: "table",
title: "定义表结构",
col: 'no-margin',
style: {
height: 'calc(100% - 234px)'
},
tableInfo: {
id: "role-authority-table",
multiple: true,
minHeight: 'unset',
maxHeight: '100%',
fields: [
{ label: '序号', type: 'index', width: 56, align: 'center' },
{ label: '字段名称', field: 'fieldName', width: 140, type: 'edit', customClass: 'edit-colum'}
],
data: [{ fieldName: '1', index: 0}],
showPage: false,
actionInfo: {
show: true,
label: "操作",
type: "btn",
width: 60,
fixed: 'right',
btns: [{ label: "删除", value: "remove" }]
},
editInfo: {
fieldName: {
field: 'fieldName',
label: '字段名称',
type: 'input',
clearable: true,
maxlength: 20
}
},
readonly: false,
col: 'auto-height',
style: { height: 'calc(100% - 40px)'},
loading: false
},
tableTool: {
col: 'float-right',
visible: true,
btns: [
{ label: "新增行", value: "tableAdd", type: 'primary' },
{ label: "批量删除", value: "tableDelete" },
]
},
})
// 新增drawer
const drawerInfo = ref({
visible: false,
direction: 'rtl',
size: 550,
header: {
title: '新增标准代码',
},
type: '',
container: {
contents: [formInfo.value, formTable],
},
footer: {
visible: true,
btns: [
{ type: 'default', label: '取消', value: 'cancel' },
{ type: 'primary', label: '保存', value: 'save' },
{ type: 'primary', label: '保存并添加数据', value: 'saveAndAdd', visible: true }
]
}
})
async function drawerBtnClick (btn, info) {
}
function drawerTableToolBtnClick (btn, data) {
console.log(btn, data)
if (btn.value === 'tableAdd') {
// 定义表结构新增行
let rowInfo = { fieldName: '' }
formTable.tableInfo.data.push(rowInfo)
}
}
onMounted(() => {
getParamsList({ dictType: '标准类型'}).then((res:any) => {
if (res.code === proxy.$passCode) {
const data = res.data || []
treeInfo.data = data
standardOptions.value = data
// 默认展开第一个
if (data.length === 0) return
let params = {
pageIndex: 1,
pageSize: 100,
standardTypeCode: data[0].value,
codeName: '标准类型'
}
getStandardCodeList(params)
}
})
getParamsList({ dictType: '发布单位' }).then((res:any) => {
if (res.code === proxy.$passCode) {
const data = res.data || []
publishingUnitCodeOptions.value = data
}
})
})
</script>
<template>
<div class="container_wrap full flex">
<div class="aside_wrap">
<div class="aside_title">标准代码列表</div>
<Tree :treeInfo="treeInfo" @nodeClick="nodeClick" />
</div>
<div class="main_wrap">
<div class="table_tool_wrap">
<div class="tools_btns">
<el-button type="primary" @click="handleCreate">新建</el-button>
<el-button>批量删除</el-button>
<el-button>批量导入</el-button>
</div>
<el-input class="table_search_input" v-model.trim="tableSearchInput" placeholder="代码名称搜索"
:suffix-icon="Search" clearable @change="val => getFirstPageData()" />
</div>
<div class="table_panel_wrap">
<Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" @tablePageChange="tablePageChange"
/>
</div>
</div>
<Drawer
:drawerInfo="drawerInfo"
@drawerBtnClick="drawerBtnClick"
ref="drawerRef"
@drawerTableToolBtnClick="drawerTableToolBtnClick"
>
<!-- <Form ref="formRef"
:itemList="formItems"
formId="basic-info-form"
:rules="formRules"
/> -->
</Drawer>
</div>
</template>
<style scoped lang="scss">
.container_wrap {
.aside_wrap {
width: 200px;
}
.main_wrap {
padding: 10px 20px;
:deep(.el-tabs) {
height: 100%;
.el-tabs__header {
margin-bottom: 0;
}
.el-tabs__item {
height: 32px;
padding: 0px;
width: 144px;
&:last-child {
width: 130px;
}
}
.el-tabs__content {
height: calc(100% - 32px);
}
.el-tab-pane {
padding: 0px 16px;
height: 100%;
}
}
}
}
.tree_panel {
height: calc(100% - 36px);
padding-top: 0;
:deep(.el-tree) {
margin: 0;
overflow: hidden auto;
}
}
.table_tool_wrap {
display: flex;
flex-direction: column;
.el-input {
width: 230px;
height: 32px;
}
:deep(.el-input) {
.el-input__suffix-inner {
flex-direction: row-reverse;
-webkit-flex-direction: row-reverse;
display: flex;
}
}
}
.table_panel_wrap {
height: calc(100% - 44px);
}
</style>
\ No newline at end of file
<route lang="yaml">
name: metadataStandardImport
</route>
<script lang="ts" setup name="metadataStandardImport">
import { ref } from "vue";
import { useRoute, useRouter } from "vue-router"
import useUserStore from "@/store/modules/user";
import { ElMessage, ElMessageBox } from "element-plus";
import Tabs from '@/components/Tabs/index.vue'
import Table from '@/components/Table/index.vue'
import Dialog from '@/components/Dialog/index.vue'
import useCatchStore from "@/store/modules/catch";
import { download, downFileByBob, downFile } from '@/utils/common'
import {
addImportData,
deleteImportData,
getImportData,
exportDictionary,
exportCollectTask,
// getImageContent
} from '@/api/modules/queryService';
import {
parseAndDecodeUrl,
getDownFileSignByUrl,
obsDownloadRequest
} from '@/api/modules/obsService';
import {
getDictionaryTree
} from '@/api/modules/dataInventory';
import { commonPageConfig } from '@/utils/enum';
import * as XLSX from 'xlsx';
const { proxy } = getCurrentInstance() as any;
const userStore = useUserStore()
const route = useRoute()
const router = useRouter()
/** 2表示资产目录的。3是主数据; 4是元数据导入 */
const isfileImport = route.query.isfileImport
const userData = JSON.parse(userStore.userData)
const cacheStore = useCatchStore()
const standardSetList = ref([])
const standardSetGuid = ref('')
const dictionaryList = ref([])
const dictionaryGuid = ref('')
const tabsActiveName = ref('')
const uploadSetting: any = ref([])
const importType = ref('')
const defaulttabs = [
// { label: '标准集导入', name: 'standard' },
// { label: '字段标准导入', name: 'field' },
// { label: '命名标准导入', name: 'naming' },
{ label: '数据字典导入', name: 'dictionary' },
// { label: '质量模型导入', name: 'qualityModelGroup' },
// { label: '质量规则导入', name: 'qualityRule' },
]
const importTabs = [
{ label: '导入文件数据', name: 'importFile' },
// { label: '质量模型导入', name: 'qualityModelGroup' },
// { label: '质量规则导入', name: 'qualityRule' },
]
const tabsInfo = ref({
activeName: '',
tabs: isfileImport ? importTabs : defaulttabs
})
const currTableData: any = ref<Object>({});
const page = ref(commonPageConfig);
const selectRowData = ref([])
const tableInfo = ref({
id: 'data-source-table',
multiple: true,
fields: [
{ label: "序号", type: "index", width: 56, align: "center" },
{ label: "文件名称", field: "fileName", width: 240, },
{ label: "状态", field: "importState", type: 'tag', width: 110, align: 'center' },
{ label: "导入结果", field: "importMessage", width: 280 },
{ label: "导入时间", field: "createTime", width: 180 },
],
data: [],
page: {
type: "normal",
rows: 0,
...page.value,
},
actionInfo: {
label: "操作",
type: "btn",
width: 220,
fixed: 'right',
btns: (scope) => {
const row = scope.row
let btnsArr = [
{ label: '下载文件', value: 'export_file' },
{ label: '删除', value: 'delete' }
]
if (row.importState != 0 && row.importState != 1) {
btnsArr.splice(1, 0, { label: '下载异常数据', value: 'export_abnormal_data' })
}
return btnsArr
},
},
loading: false
})
const uploadFiles = ref([])
const uploadSteps: any = ref([])
const uploadInfo = ref({
type: 'upload',
title: '',
col: '',
uploadInfo: {
id: 'upload-file-form',
type: 'panel',
steps: [],
extraParams: {dictionaryGuid: 'xx'},
},
})
const dialogInfo: any = ref({
visible: false,
size: 560,
direction: "column",
header: {
title: "新建",
},
type: 'upload',
contents: [
uploadInfo.value
],
footer: {
visible: true,
btns: [
{ type: "default", label: "取消", value: "cancel" },
{ type: "primary", loading: false, label: "开始导入", value: "submit" },
],
},
})
// // 获取所有数据字典
const getDictList = () => {
const params = {
paramCode: '数据字典类型'
}
getDictionaryTree(params).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data ?? []
const treeList = data.filter(item => item.children && item.children.length)
dictionaryList.value = treeList
} else {
ElMessage({
type: 'error',
message: res.msg,
})
}
})
}
const tabsChange = (name) => {
tabsActiveName.value = name
let info: any = {
type: name
}
if (tabsActiveName.value == 'field' || tabsActiveName.value == 'naming') {
info.standardSetGuid = standardSetGuid.value
} else if (tabsActiveName.value == 'dictionary') {
info.dictionaryGuid = dictionaryGuid.value
} else if (tabsActiveName.value == 'importFile') {
}
cacheStore.setCatch('uploadSetting', info)
setUploadInfo()
}
const getFirstPageData = () => {
page.value.curr = 1
toSearch({})
console.log('store', cacheStore.getCatch('uploadSetting'))
}
const toSearch = (val: any, clear: boolean = false) => {
let params: any = Object.keys(val).length ? { ...val } : {}
params.pageIndex = page.value.curr;
params.pageSize = page.value.limit;
params.staffGuid = userData.staffGuid;
params.importType = importType.value;
params.bizGuid = route.query.bizGuid || ''
getTableData(params);
};
const getTableData = (params) => {
tableInfo.value.loading = true
getImportData(params).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || {}
tableInfo.value.data = data.records || []
tableInfo.value.page.limit = data.pageSize
tableInfo.value.page.curr = data.pageIndex
tableInfo.value.page.rows = data.totalRows
} else {
ElMessage({
type: 'error',
message: res.msg,
})
}
tableInfo.value.loading = false
}).catch(xhr => {
tableInfo.value.loading = false
})
};
const tableSelectionChange = (val) => {
selectRowData.value = val.map(item => item.guid);
};
const tablePageChange = (info) => {
page.value.curr = Number(info.curr);
page.value.limit = Number(info.limit);
toSearch({});
};
const tableBtnClick = async (scope, btn) => {
const type = btn.value;
const row = scope.row;
currTableData.value = row;
if (type == "export_file") {
const refSignInfo: any = await getDownFileSignByUrl(parseAndDecodeUrl(row.filePath).fileName);
if (!refSignInfo?.data) {
refSignInfo?.msg && ElMessage.error(refSignInfo?.msg);
return;
}
obsDownloadRequest(refSignInfo?.data).then((res: any) => {
if (res && !res.msg) {
downFileByBob(res, row.fileName);
} else {
res?.msg && ElMessage.error(res?.msg);
}
});
//downFile(row.filePath, row.fileName)
} else if (type == 'export_abnormal_data') {
//downFile(row.errorFilePath, '')
const refSignInfo: any = await getDownFileSignByUrl(parseAndDecodeUrl(row.errorFilePath).fileName);
if (!refSignInfo?.data) {
refSignInfo?.msg && ElMessage.error(refSignInfo?.msg);
return;
}
obsDownloadRequest(refSignInfo?.data).then((res: any) => {
if (res && !res.msg) {
let name = row.errorFilePath;
let fileName = name ? name.substring(name.lastIndexOf('/') + 1) : ''
downFileByBob(res, fileName);
} else {
res?.msg && ElMessage.error(res?.msg);
}
});
} else if (type == "delete") {
open("此操作将永久删除, 是否继续?", "warning");
}
};
const batching = (type) => {
if (type == 'import') {
dialogInfo.value.header.title = '导入数据'
dialogInfo.value.type = 'upload'
dialogInfo.value.size = 560
uploadFiles.value = []
// if (tabsActiveName.value == 'field' || tabsActiveName.value == 'naming') {
// uploadSteps.value[0].selectInfo.options = standardSetList.value
// } else if (tabsActiveName.value == 'dictionary') {
// uploadSteps.value[0].cascaderInfo.options = dictionaryList.value
// }
uploadInfo.value.uploadInfo.steps = uploadSteps.value
uploadSetting.value.forEach(item => item.value = '')
sheetNameList.value = []
const content: any = [uploadInfo.value]
dialogInfo.value.contents = content
dialogInfo.value.visible = true
} else if (type == 'delete') {
if (selectRowData.value.length == 0) {
ElMessage({
type: 'error',
message: '请选择需要删除的数据',
})
return
}
open("此操作将永久删除, 是否继续?", "warning", true);
} else if (type === 'importFile') {
if (isfileImport == '2' || isfileImport == '4') {
dialogInfo.value.header.title = '导入数据'
dialogInfo.value.type = 'upload'
dialogInfo.value.size = isfileImport == '4' ? 560 : 560;
uploadFiles.value = []
uploadInfo.value.uploadInfo.steps = uploadSteps.value
const content: any = [uploadInfo.value]
dialogInfo.value.contents = content
dialogInfo.value.visible = true
} else {
router.push({
name: "importData",
query: route.query
})
}
}
};
const open = (msg, type, isBatch = false) => {
ElMessageBox.confirm(msg, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: type,
}).then(() => {
let guids = [currTableData.value.guid]
if (isBatch) {
guids = selectRowData.value
}
deleteImportData(guids).then((res: any) => {
if (res.code == proxy.$passCode) {
getFirstPageData();
ElMessage({
type: "success",
message: "删除成功",
});
} else {
ElMessage({
type: "error",
message: res.msg,
});
}
});
});
};
const sheetNameList = ref([])
const onUpload = (file, fileList) => {
// console.log('file', file)
uploadFiles.value = fileList
const reader = new FileReader()
reader.onload = function (e) {
let data = new Uint8Array(e.target.result)
let wb = XLSX.read(data, { type: 'array', raw: false, cellDates: true })
console.log('wb', wb)
sheetNameList.value = wb.SheetNames
}
reader.readAsArrayBuffer(file.raw)
}
const uploadBtnClick = (btn) => {
exportData()
}
const cascaderChange = (val) => {
dictionaryGuid.value = val ? val.at(-1) : ''
}
const selectChange = (val) => {
standardSetGuid.value = val
}
const exportData = (ids: any = null) => {
if (tabsActiveName.value == 'standard') {
const fieldTemplate = "/files/set.xlsx";
downFile(fieldTemplate, '标准集模板.xlsx')
} else if (tabsActiveName.value == 'field') {
const fieldTemplate = "/files/field.xlsx";
downFile(fieldTemplate, '字段标准模板.xlsx')
} else if (tabsActiveName.value == 'naming') {
const namingTemplate = "/files/naming.xlsx";
downFile(namingTemplate, '命名标准模板.xlsx')
} else if (tabsActiveName.value == 'dictionary') {
const params = {
guid: dictionaryGuid.value
}
exportDictionary(params).then((res: any) => {
if (res && !res.msg) {
download(res, '数据字典模板.xlsx', 'excel');
} else {
res?.msg && ElMessage.error(res?.msg);
}
});
} else if (tabsActiveName.value == 'importFile' && isfileImport == '4') {
exportCollectTask({
importTypes: [
"0042"
]
}).then((res: any) => {
if (res && !res.msg) {
download(res, '元数据模板.xlsx', 'excel');
} else {
res?.msg && ElMessage.error(res?.msg);
}
});
}
}
const importData = (info) => {
let params = new FormData()
if (uploadFiles.value.length == 0) {
ElMessage({
type: 'error',
message: '请选择上传文件'
})
// dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
return
}
let sheetPass = uploadSetting.value.some(item => item.value)
if (!sheetPass) {
ElMessage.error('导入失败,没有导入数据')
dialogInfo.value.visible = false;
return
}
let sheetNum = uploadSetting.value.filter(item => item.value)['length']
if (sheetNum > 30) {
ElMessage.error('最多只能导入30个sheet页的数据。')
return
}
let paramUrl = '';
uploadFiles.value.forEach((item: any, index: number) => {
params.append("file", item.raw);
});
let sheetMaps = {}
uploadSetting.value.forEach(item => {
if (item.value) {
sheetMaps[item.value] = item.standardGuid
}
})
sheetMaps = encodeURIComponent(JSON.stringify(sheetMaps))
paramUrl = `${import.meta.env.VITE_APP_ADD_FILE}/import-data/import-batch-common?importType=${importType.value}&staffGuid=${userData.staffGuid}&tenantGuid=${userData.tenantGuid}&sheetMaps=${sheetMaps}`
dialogInfo.value.footer.btns[1].loading = true;
addImportData(paramUrl, params).then((res: any) => {
dialogInfo.value.footer.btns[1].loading = false;
if (res.code == proxy.$passCode) {
getFirstPageData();
ElMessage({
type: "success",
message: '导入成功',
});
dialogInfo.value.visible = false;
} else {
ElMessage({
type: "error",
message: res.msg,
});
// dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
}
}).catch(() => {
dialogInfo.value.footer.btns[1].loading = false;
})
}
const dialogBtnClick = (btn, info) => {
if (btn.value == 'submit') {
// dialogInfo.value.footer.btns.map((item: any) => item.disabled = true)
if (dialogInfo.value.type == 'upload') {
if (tabsActiveName.value == 'dictionary') {
importData({ bizGuid: dictionaryGuid.value })
} else {
importData(info)
}
}
} else if (btn.value == 'cancel') {
// dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
nextTick(() => {
dialogInfo.value.visible = false;
})
};
}
const setUploadInfo = () => {
importType.value = '0101'
tabsInfo.value.activeName = tabsActiveName.value
getFirstPageData()
uploadSteps.value = [
{
title: '1、选择准备好的文件导入',
type: 'btn_upload',
uploadInfo: {
action: '',
auto: false,
cover: true,
fileList: [],
accept: '.xlsx, .xls',
tips: '当前支持xls、xlsx文件,支持一个文件多个sheet批量导入,一次最多只能导入30个sheet页数据。'
}
}
]
}
function deleteFile () {
sheetNameList.value = []
uploadSetting.value.forEach(item => item.value = null)
}
onActivated(() => {
let list = cacheStore.getCatch('uploadSetting') || []
uploadSetting.value = list.map(item => {
item.value = null
return item
})
console.log('uploadSetting', uploadSetting.value)
setUploadInfo()
})
</script>
<template>
<div class="container_wrap">
<!-- <Tabs v-if="!isfileImport" :tabs-info="tabsInfo" @tabChange="tabsChange" /> -->
<div class="table_tool_wrap">
<div class="tools_btns">
<el-button type="primary" @click="batching('import')" v-if="tabsActiveName !== 'importFile'"
v-preReClick>批量导入</el-button>
<el-button type="primary" @click="batching('importFile')" v-if="tabsActiveName == 'importFile'"
v-preReClick>文件导入</el-button>
<el-button @click="batching('delete')" v-preReClick>批量删除</el-button>
<el-button @click="getFirstPageData" v-preReClick>刷新结果</el-button>
</div>
<span class="tips_text">请及时刷新查看最终结果</span>
</div>
<div class="table_panel_wrap" :style="{ height: !isfileImport ? 'calc(100% - 71px)' : 'calc(100% - 44px)' }">
<Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" @tableSelectionChange="tableSelectionChange"
@tablePageChange="tablePageChange" />
</div>
<Dialog :dialogInfo="dialogInfo" @btnClick="dialogBtnClick" @onUpload="onUpload" @uploadBtnClick="uploadBtnClick"
@cascaderChange="cascaderChange" @selectChange="selectChange" @deleteFile="deleteFile">
<div style="overflow: auto;">
<div class="title" style="color:#333;margin:20px 0 10px">2、导入前请先导入文件的sheet与标准做对应</div>
<el-table :data="uploadSetting" border height="310">
<el-table-column type="index" label="序号" width="55" align="center"/>
<el-table-column label="代码名称" prop="standardName"></el-table-column>
<el-table-column label="选择sheet页">
<template #default="scope">
<el-select v-model="scope.row.value" placeholder="请选择" style="width:200px" clearable>
<el-option v-for="item,i in sheetNameList" :label="item" :value="item" :key="i"></el-option>
</el-select>
</template>
</el-table-column>
</el-table>
</div>
</Dialog>
</div>
</template>
<style lang="scss" scoped>
.container_wrap {
padding: 0;
:deep(.el-tabs) {
.el-tabs__header {
margin-bottom: 0;
}
.el-tabs__item {
height: 32px;
&:nth-child(2) {
padding-left: 16px;
}
&:last-child {
padding-right: 16px;
}
&::after {
content: '';
width: 100%;
height: 2px;
background-color: transparent;
position: absolute;
left: 0;
bottom: 0;
}
&.is-active {
&::after {
background-color: var(--el-color-primary);
}
}
}
.el-tabs__active-bar {
display: none;
}
}
.table_tool_wrap {
padding: 0 16px;
display: flex;
align-items: center;
.tips_text {
margin-left: 16px;
font-size: 14px;
color: #b2b2b2;
}
}
.table_panel_wrap {
padding: 0 16px;
height: calc(100% - 71px);
}
}
</style>
<style lang="scss">
.upload_panel_wrap .upload_panel .file_panel .file_item .file_btn {
word-break: keep-all;
}
.upload_panel_wrap .upload_step_panel .upload_panel {
padding-top: 42px!important;
}
</style>
<route lang="yaml">
name: standardMetaImport
</route>
<script lang="ts" setup name="standardMetaImport">
import { ref } from "vue";
import { useRoute, useRouter } from "vue-router"
import useUserStore from "@/store/modules/user";
import { ElMessage, ElMessageBox } from "element-plus";
import Tabs from '@/components/Tabs/index.vue'
import Table from '@/components/Table/index.vue'
import Dialog from '@/components/Dialog/index.vue'
import useCatchStore from "@/store/modules/catch";
import { download, downFileByBob, downFile } from '@/utils/common'
import {
addImportData,
deleteImportData,
getImportData,
exportDictionary,
exportCollectTask,
// getImageContent
} from '@/api/modules/queryService';
import {
parseAndDecodeUrl,
getDownFileSignByUrl,
obsDownloadRequest
} from '@/api/modules/obsService';
import {
getDictionaryTree
} from '@/api/modules/dataInventory';
import { commonPageConfig } from '@/utils/enum';
import * as XLSX from 'xlsx';
const { proxy } = getCurrentInstance() as any;
const userStore = useUserStore()
const route = useRoute()
const router = useRouter()
/** 2表示资产目录的。3是主数据; 4是元数据导入 */
const isfileImport = route.query.isfileImport
const userData = JSON.parse(userStore.userData)
const cacheStore = useCatchStore()
const standardSetList = ref([])
const standardSetGuid = ref('')
const dictionaryList = ref([])
const dictionaryGuid = ref('')
const tabsActiveName = ref('')
const uploadSetting: any = ref([])
const importType = ref('')
const defaulttabs = [
// { label: '标准集导入', name: 'standard' },
// { label: '字段标准导入', name: 'field' },
// { label: '命名标准导入', name: 'naming' },
{ label: '数据字典导入', name: 'dictionary' },
// { label: '质量模型导入', name: 'qualityModelGroup' },
// { label: '质量规则导入', name: 'qualityRule' },
]
const importTabs = [
{ label: '导入文件数据', name: 'importFile' },
// { label: '质量模型导入', name: 'qualityModelGroup' },
// { label: '质量规则导入', name: 'qualityRule' },
]
const tabsInfo = ref({
activeName: '',
tabs: isfileImport ? importTabs : defaulttabs
})
const currTableData: any = ref<Object>({});
const page = ref(commonPageConfig);
const selectRowData = ref([])
const tableInfo = ref({
id: 'data-source-table',
multiple: true,
fields: [
{ label: "序号", type: "index", width: 56, align: "center" },
{ label: "文件名称", field: "fileName", width: 240, },
{ label: "状态", field: "importState", type: 'tag', width: 110, align: 'center' },
{ label: "导入结果", field: "importMessage", width: 280 },
{ label: "导入时间", field: "createTime", width: 180 },
],
data: [],
page: {
type: "normal",
rows: 0,
...page.value,
},
actionInfo: {
label: "操作",
type: "btn",
width: 220,
fixed: 'right',
btns: (scope) => {
const row = scope.row
let btnsArr = [
{ label: '下载文件', value: 'export_file' },
{ label: '删除', value: 'delete' }
]
if (row.importState != 0 && row.importState != 1) {
btnsArr.splice(1, 0, { label: '下载异常数据', value: 'export_abnormal_data' })
}
return btnsArr
},
},
loading: false
})
const uploadFiles = ref([])
const uploadSteps: any = ref([])
const uploadInfo = ref({
type: 'upload',
title: '',
col: '',
uploadInfo: {
id: 'upload-file-form',
type: 'panel',
steps: [],
extraParams: {dictionaryGuid: 'xx'},
},
})
const dialogInfo: any = ref({
visible: false,
size: 560,
direction: "column",
header: {
title: "新建",
},
type: 'upload',
contents: [
uploadInfo.value
],
footer: {
visible: true,
btns: [
{ type: "default", label: "取消", value: "cancel" },
{ type: "primary", loading: false, label: "开始导入", value: "submit" },
],
},
})
// // 获取所有数据字典
const getDictList = () => {
const params = {
paramCode: '数据字典类型'
}
getDictionaryTree(params).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data ?? []
const treeList = data.filter(item => item.children && item.children.length)
dictionaryList.value = treeList
} else {
ElMessage({
type: 'error',
message: res.msg,
})
}
})
}
const tabsChange = (name) => {
tabsActiveName.value = name
let info: any = {
type: name
}
if (tabsActiveName.value == 'field' || tabsActiveName.value == 'naming') {
info.standardSetGuid = standardSetGuid.value
} else if (tabsActiveName.value == 'dictionary') {
info.dictionaryGuid = dictionaryGuid.value
} else if (tabsActiveName.value == 'importFile') {
}
cacheStore.setCatch('uploadSetting', info)
setUploadInfo()
}
const getFirstPageData = () => {
page.value.curr = 1
toSearch({})
console.log('store', cacheStore.getCatch('uploadSetting'))
}
const toSearch = (val: any, clear: boolean = false) => {
let params: any = Object.keys(val).length ? { ...val } : {}
params.pageIndex = page.value.curr;
params.pageSize = page.value.limit;
params.staffGuid = userData.staffGuid;
params.importType = importType.value;
params.bizGuid = route.query.bizGuid || ''
getTableData(params);
};
const getTableData = (params) => {
tableInfo.value.loading = true
getImportData(params).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || {}
tableInfo.value.data = data.records || []
tableInfo.value.page.limit = data.pageSize
tableInfo.value.page.curr = data.pageIndex
tableInfo.value.page.rows = data.totalRows
} else {
ElMessage({
type: 'error',
message: res.msg,
})
}
tableInfo.value.loading = false
}).catch(xhr => {
tableInfo.value.loading = false
})
};
const tableSelectionChange = (val) => {
selectRowData.value = val.map(item => item.guid);
};
const tablePageChange = (info) => {
page.value.curr = Number(info.curr);
page.value.limit = Number(info.limit);
toSearch({});
};
const tableBtnClick = async (scope, btn) => {
const type = btn.value;
const row = scope.row;
currTableData.value = row;
if (type == "export_file") {
const refSignInfo: any = await getDownFileSignByUrl(parseAndDecodeUrl(row.filePath).fileName);
if (!refSignInfo?.data) {
refSignInfo?.msg && ElMessage.error(refSignInfo?.msg);
return;
}
obsDownloadRequest(refSignInfo?.data).then((res: any) => {
if (res && !res.msg) {
downFileByBob(res, row.fileName);
} else {
res?.msg && ElMessage.error(res?.msg);
}
});
//downFile(row.filePath, row.fileName)
} else if (type == 'export_abnormal_data') {
//downFile(row.errorFilePath, '')
const refSignInfo: any = await getDownFileSignByUrl(parseAndDecodeUrl(row.errorFilePath).fileName);
if (!refSignInfo?.data) {
refSignInfo?.msg && ElMessage.error(refSignInfo?.msg);
return;
}
obsDownloadRequest(refSignInfo?.data).then((res: any) => {
if (res && !res.msg) {
let name = row.errorFilePath;
let fileName = name ? name.substring(name.lastIndexOf('/') + 1) : ''
downFileByBob(res, fileName);
} else {
res?.msg && ElMessage.error(res?.msg);
}
});
} else if (type == "delete") {
open("此操作将永久删除, 是否继续?", "warning");
}
};
const batching = (type) => {
if (type == 'import') {
dialogInfo.value.header.title = '导入数据'
dialogInfo.value.type = 'upload'
dialogInfo.value.size = 560
uploadFiles.value = []
// if (tabsActiveName.value == 'field' || tabsActiveName.value == 'naming') {
// uploadSteps.value[0].selectInfo.options = standardSetList.value
// } else if (tabsActiveName.value == 'dictionary') {
// uploadSteps.value[0].cascaderInfo.options = dictionaryList.value
// }
uploadInfo.value.uploadInfo.steps = uploadSteps.value
uploadSetting.value.forEach(item => item.value = '')
sheetNameList.value = []
const content: any = [uploadInfo.value]
dialogInfo.value.contents = content
dialogInfo.value.visible = true
} else if (type == 'delete') {
if (selectRowData.value.length == 0) {
ElMessage({
type: 'error',
message: '请选择需要删除的数据',
})
return
}
open("此操作将永久删除, 是否继续?", "warning", true);
} else if (type === 'importFile') {
if (isfileImport == '2' || isfileImport == '4') {
dialogInfo.value.header.title = '导入数据'
dialogInfo.value.type = 'upload'
dialogInfo.value.size = isfileImport == '4' ? 560 : 560;
uploadFiles.value = []
uploadInfo.value.uploadInfo.steps = uploadSteps.value
const content: any = [uploadInfo.value]
dialogInfo.value.contents = content
dialogInfo.value.visible = true
} else {
router.push({
name: "importData",
query: route.query
})
}
}
};
const open = (msg, type, isBatch = false) => {
ElMessageBox.confirm(msg, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: type,
}).then(() => {
let guids = [currTableData.value.guid]
if (isBatch) {
guids = selectRowData.value
}
deleteImportData(guids).then((res: any) => {
if (res.code == proxy.$passCode) {
getFirstPageData();
ElMessage({
type: "success",
message: "删除成功",
});
} else {
ElMessage({
type: "error",
message: res.msg,
});
}
});
});
};
const sheetNameList = ref([])
const onUpload = (file, fileList) => {
// console.log('file', file)
uploadFiles.value = fileList
const reader = new FileReader()
reader.onload = function (e) {
let data = new Uint8Array(e.target.result)
let wb = XLSX.read(data, { type: 'array', raw: false, cellDates: true })
console.log('wb', wb)
sheetNameList.value = wb.SheetNames
}
reader.readAsArrayBuffer(file.raw)
}
const uploadBtnClick = (btn) => {
exportData()
}
const cascaderChange = (val) => {
dictionaryGuid.value = val ? val.at(-1) : ''
}
const selectChange = (val) => {
standardSetGuid.value = val
}
const exportData = (ids: any = null) => {
if (tabsActiveName.value == 'standard') {
const fieldTemplate = "/files/set.xlsx";
downFile(fieldTemplate, '标准集模板.xlsx')
} else if (tabsActiveName.value == 'field') {
const fieldTemplate = "/files/field.xlsx";
downFile(fieldTemplate, '字段标准模板.xlsx')
} else if (tabsActiveName.value == 'naming') {
const namingTemplate = "/files/naming.xlsx";
downFile(namingTemplate, '命名标准模板.xlsx')
} else if (tabsActiveName.value == 'dictionary') {
const params = {
guid: dictionaryGuid.value
}
exportDictionary(params).then((res: any) => {
if (res && !res.msg) {
download(res, '数据字典模板.xlsx', 'excel');
} else {
res?.msg && ElMessage.error(res?.msg);
}
});
} else if (tabsActiveName.value == 'importFile' && isfileImport == '4') {
exportCollectTask({
importTypes: [
"0042"
]
}).then((res: any) => {
if (res && !res.msg) {
download(res, '元数据模板.xlsx', 'excel');
} else {
res?.msg && ElMessage.error(res?.msg);
}
});
}
}
const importData = (info) => {
let params = new FormData()
if (uploadFiles.value.length == 0) {
ElMessage({
type: 'error',
message: '请选择上传文件'
})
// dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
return
}
let sheetPass = uploadSetting.value.some(item => item.value)
if (!sheetPass) {
ElMessage.error('导入失败,没有导入数据')
dialogInfo.value.visible = false;
return
}
let sheetNum = uploadSetting.value.filter(item => item.value)['length']
if (sheetNum > 30) {
ElMessage.error('最多只能导入30个sheet页的数据。')
return
}
let paramUrl = '';
uploadFiles.value.forEach((item: any, index: number) => {
params.append("file", item.raw);
});
let sheetMaps = {}
uploadSetting.value.forEach(item => {
if (item.value) {
sheetMaps[item.value] = item.standardGuid
}
})
sheetMaps = encodeURIComponent(JSON.stringify(sheetMaps))
paramUrl = `${import.meta.env.VITE_APP_ADD_FILE}/import-data/import-batch-common?importType=${importType.value}&staffGuid=${userData.staffGuid}&tenantGuid=${userData.tenantGuid}&sheetMaps=${sheetMaps}`
dialogInfo.value.footer.btns[1].loading = true;
addImportData(paramUrl, params).then((res: any) => {
dialogInfo.value.footer.btns[1].loading = false;
if (res.code == proxy.$passCode) {
getFirstPageData();
ElMessage({
type: "success",
message: '导入成功',
});
dialogInfo.value.visible = false;
} else {
ElMessage({
type: "error",
message: res.msg,
});
// dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
}
}).catch(() => {
dialogInfo.value.footer.btns[1].loading = false;
})
}
const dialogBtnClick = (btn, info) => {
if (btn.value == 'submit') {
// dialogInfo.value.footer.btns.map((item: any) => item.disabled = true)
if (dialogInfo.value.type == 'upload') {
if (tabsActiveName.value == 'dictionary') {
importData({ bizGuid: dictionaryGuid.value })
} else {
importData(info)
}
}
} else if (btn.value == 'cancel') {
// dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
nextTick(() => {
dialogInfo.value.visible = false;
})
};
}
const setUploadInfo = () => {
importType.value = '0102'
tabsInfo.value.activeName = tabsActiveName.value
getFirstPageData()
uploadSteps.value = [
{
title: '1、选择准备好的文件导入',
type: 'btn_upload',
uploadInfo: {
action: '',
auto: false,
cover: true,
fileList: [],
accept: '.xlsx, .xls',
tips: '当前支持xls、xlsx文件,支持一个文件多个sheet批量导入,一次最多只能导入30个sheet页数据。'
}
}
]
}
function deleteFile () {
sheetNameList.value = []
uploadSetting.value.forEach(item => item.value = null)
}
onActivated(() => {
let list = cacheStore.getCatch('uploadSetting') || []
uploadSetting.value = list.map(item => {
item.value = null
return item
})
console.log('uploadSetting', uploadSetting.value)
setUploadInfo()
})
</script>
<template>
<div class="container_wrap">
<!-- <Tabs v-if="!isfileImport" :tabs-info="tabsInfo" @tabChange="tabsChange" /> -->
<div class="table_tool_wrap">
<div class="tools_btns">
<el-button type="primary" @click="batching('import')" v-if="tabsActiveName !== 'importFile'"
v-preReClick>批量导入</el-button>
<el-button type="primary" @click="batching('importFile')" v-if="tabsActiveName == 'importFile'"
v-preReClick>文件导入</el-button>
<el-button @click="batching('delete')" v-preReClick>批量删除</el-button>
<el-button @click="getFirstPageData" v-preReClick>刷新结果</el-button>
</div>
<span class="tips_text">请及时刷新查看最终结果</span>
</div>
<div class="table_panel_wrap" :style="{ height: !isfileImport ? 'calc(100% - 71px)' : 'calc(100% - 44px)' }">
<Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" @tableSelectionChange="tableSelectionChange"
@tablePageChange="tablePageChange" />
</div>
<Dialog :dialogInfo="dialogInfo" @btnClick="dialogBtnClick" @onUpload="onUpload" @uploadBtnClick="uploadBtnClick"
@cascaderChange="cascaderChange" @selectChange="selectChange" @deleteFile="deleteFile">
<div style="overflow: auto;">
<div class="title" style="color:#333;margin:20px 0 10px">2、导入前请先导入文件的sheet与标准做对应</div>
<el-table :data="uploadSetting" border height="310">
<el-table-column type="index" label="序号" width="55" align="center"/>
<el-table-column label="标准名称" prop="standardName"></el-table-column>
<el-table-column label="选择sheet页">
<template #default="scope">
<el-select v-model="scope.row.value" placeholder="请选择" style="width:200px" clearable>
<el-option v-for="item,i in sheetNameList" :label="item" :value="item" :key="i"></el-option>
</el-select>
</template>
</el-table-column>
</el-table>
</div>
</Dialog>
</div>
</template>
<style lang="scss" scoped>
.container_wrap {
padding: 0;
:deep(.el-tabs) {
.el-tabs__header {
margin-bottom: 0;
}
.el-tabs__item {
height: 32px;
&:nth-child(2) {
padding-left: 16px;
}
&:last-child {
padding-right: 16px;
}
&::after {
content: '';
width: 100%;
height: 2px;
background-color: transparent;
position: absolute;
left: 0;
bottom: 0;
}
&.is-active {
&::after {
background-color: var(--el-color-primary);
}
}
}
.el-tabs__active-bar {
display: none;
}
}
.table_tool_wrap {
padding: 0 16px;
display: flex;
align-items: center;
.tips_text {
margin-left: 16px;
font-size: 14px;
color: #b2b2b2;
}
}
.table_panel_wrap {
padding: 0 16px;
height: calc(100% - 71px);
}
}
</style>
<style lang="scss">
.upload_panel_wrap .upload_panel .file_panel .file_item .file_btn {
word-break: keep-all;
}
.upload_panel_wrap .upload_step_panel .upload_panel {
padding-top: 42px!important;
}
</style>
<route lang="yaml">
name: metadataStandardQueryView
</route>
<template>
<div class="main_wrap" v-loading="graphDataLoading">
<div className='g6-component-topbar'>
<graphTopbar ref="topBarRef" @displaySwitchChange="displaySwitchChange" :isGraphDisplay="isGraphDisplay" />
</div>
<RelationNetwork v-show="graphTreeData?.guid && isGraphDisplay" ref="relationNetworkRef" :tree-data="graphTreeData"
:noContextMenu="true" @nodeItemClick="handleNodeItemClick" @contextMenu="handleContextMenu">
</RelationNetwork>
<Sankey v-show="!isGraphDisplay && (sankeyNames?.length || sankeyDataLoading)" v-loading="sankeyDataLoading"
:tree-data="sankeyData" :names="sankeyNames">
</Sankey>
<div v-show="(isGraphDisplay ? !graphTreeData?.guid : (!sankeyDataLoading && !sankeyNames?.length))"
class="main-placeholder">
<img src="../../assets/images/no-data.png" :style="{ width: '96px', height: '96px' }" />
<div class="empty-text">暂无数据</div>
</div>
</div>
</template>
<script lang="ts" setup name="metadataStandardQueryView">
import {
getMetaStandardField,
getSankeyData,
getMetaStandardTreeList
} from '@/api/modules/dataMetaService';
import Sankey from './components/Sankey.vue';
import { ElMessage } from 'element-plus';
const router = useRouter();
const route = useRoute()
const metaGuid = ref(route.query.guid);
const { proxy } = getCurrentInstance() as any;
const relationNetworkRef = ref();
const graphDataLoading = ref(false);
/** 关系网树形数据 */
const graphTreeData: any = ref({});
onBeforeUnmount(() => {
relationNetworkRef.value.destroy();
})
const sankeyDataLoading = ref(false);
const sankeyData: any = ref([]);
const sankeyNames: any = ref([]);
const isGraphDisplay = ref(true);
const displaySwitchChange = (val) => {
if (val == isGraphDisplay.value) {
return;
}
isGraphDisplay.value = val;
if (!val) {
getSankeyDataList();
}
}
const getSankeyDataList = () => {
sankeyDataLoading.value = true;
getSankeyData(metaGuid.value).then((res: any) => {
sankeyDataLoading.value = false;
if (res?.code == proxy.$passCode) {
sankeyData.value = res.data?.links || [];
sankeyNames.value = res.data?.data || [];
} else {
ElMessage.error(res.msg);
}
})
}
const handleNodeItemClick = (graph, nodeItem) => {
const nodeId = nodeItem.get('id');
let parentData = graph.findDataById(nodeId);
if (!parentData.children) {
parentData.children = [];
}
if (parentData.isHaveData == 'N') {
ElMessage.warning('没有可展开的下级字段');
return;
}
// graph.updateConfig({ animate: false });
// graph.refresh();
nodeItem.getModel().collapsed = false;
parentData.collapsed = false;
graphDataLoading.value = true;
getMetaStandardField(nodeId).then((res: any) => {
graphDataLoading.value = false;
if (res?.code == proxy.$passCode) {
parentData = graph.findDataById(nodeId);
const data = res.data || [];
parentData.children = [];
if (!data?.length) {
parentData.isHaveData = 'N';
ElMessage.warning('没有可展开的下级字段');
return;
}
data.forEach(d => {
parentData.children.push(d);
})
parentData.isLoading = false;
nodeItem.getModel().collapsed = false;
parentData.collapsed = false;
graph.updateItem(nodeItem, {
...nodeItem.getModel(),
collapsed: false
});
graph.layout();
setTimeout(() => {
// graph.updateConfig({ animate: true });
// graph.refresh();
graph.updateItem(nodeItem, {
...nodeItem.getModel(),
collapsed: false
});
graph.setMinZoom(1);
graph.setMaxZoom(1);
graph.layout();
graph.setMinZoom(0.5);
graph.setMaxZoom(5);
graph.focusItem(nodeItem, true, {
duration: 400 // 动画时长为500ms
});
}, 500);
} else {
parentData.isLoading = false;
ElMessage.error(res.msg);
}
})
}
const handleContextMenu = (model) => {
router.push({
name: 'metadataStandard',
query: {
standardGuid: model.guid,
name: model.standardName
}
});
}
onBeforeMount(() => {
graphDataLoading.value = true
getMetaStandardTreeList(metaGuid.value).then((res: any) => {
graphDataLoading.value = false;
if (res?.code == proxy.$passCode) {
const data = res.data || [];
let resultData = data?.[0] || {};
if (!resultData?.children?.length && resultData.isHaveData == 'Y') {
graphDataLoading.value = true;
getMetaStandardField(resultData.guid).then((res: any) => {
graphDataLoading.value = false;
if (res?.code == proxy.$passCode) {
resultData.children = res.data || [];
graphTreeData.value = resultData;
} else {
graphTreeData.value = resultData;
ElMessage.error(res.msg);
}
});
} else {
graphTreeData.value = resultData;
}
} else {
ElMessage.error(res.msg);
}
})
})
</script>
<style lang="scss" scoped>
.main_wrap {
height: 100%;
width: 100%;
position: relative;
:deep(.canvas-wrapper) {
background-color: #f7f7f9;
}
.main-placeholder {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.empty-text {
font-size: 14px;
color: #b2b2b2;
}
}
}
.g6-component-topbar {
position: absolute;
left: 24px;
bottom: unset;
top: 14px;
padding: 0;
text-align: center;
z-index: 999;
}
</style>
\ No newline at end of file
<route lang="yaml">
name: metadataStandardQuery
</route>
<script lang="ts" setup name="metadataStandardQuery">
import { ref } from 'vue';
import { ElMessage, ElMessageBox } from "element-plus";
import Sankey from './components/Sankey.vue';
import Tree from '@/components/Tree/index.vue';
import RelationNetwork from '@/components/RelationNetwork/index.vue';
import {
getDataWareCatalogList,
getMetaStandardTreeList,
getMetaStandardField,
getSankeyData
} from '@/api/modules/dataMetaService';
import { useRouter, useRoute } from "vue-router";
import useDataMetaStore from "@/store/modules/dataMeta"
import { cloneDeep } from 'lodash-es';
import { useValidator } from '@/hooks/useValidator';
const { required } = useValidator();
const router = useRouter();
const route = useRoute()
const { proxy } = getCurrentInstance() as any;
const relationNetworkRef = ref();
const treeInfo = ref({
id: "data-meta-standard-tree",
filter: true,
queryValue: "",
queryPlaceholder: "请输入关键字搜索",
props: {
label: "standardName",
value: "guid",
isLeaf: "isLeaf",
},
nodeKey: 'guid',
expandedKey: [],
currentNodeKey: '',
expandOnNodeClick: false,
data: <any>[],
loading: false
});
/** 获取左侧树数据. */
const getTreeData = async () => {
treeInfo.value.loading = true
getMetaStandardTreeList('').then((res: any) => {
treeInfo.value.loading = false;
if (res?.code == proxy.$passCode) {
const data = res.data || [];
data.forEach(d => {
d.disabled = true;
})
treeInfo.value.data = data;
if (data.length) {
treeInfo.value.currentNodeKey = data[0]?.children?.[0]?.guid;
treeInfo.value.expandedKey = <any>[data[0].guid];
nodeClick(treeInfo.value.data[0]?.children?.[0])
}
} else {
ElMessage.error(res.msg);
}
})
}
/** 左侧树的的组件引用. */
const treeInfoRef = ref();
/** 当前选中的树节点数据data */
const lastClickNode: any = ref({});
const treeDataLoading = ref(false);
/** 点击左侧树节点,更新对应的血缘关系图. */
const nodeClick = (data) => {
const ele = <HTMLElement>document.querySelector(".g6-component-contextmenu")
if (ele) {
ele.style.visibility = "hidden"
}
if (data.disabled) {
treeInfo.value.currentNodeKey = data.guid;
nextTick(() => {
treeInfo.value.currentNodeKey = lastClickNode.value.guid;
});
// lastClickNode.value = {};
return;
}
treeInfo.value.currentNodeKey = data.guid;
treeInfo.value.expandedKey = <any>[data.guid];
lastClickNode.value = cloneDeep(data);
if (!isGraphDisplay.value) {
getSankeyDataList();
}
}
/** 选中树节点后自动滚动到可视范围内. */
const scrollToNode = (nodeId) => {
nextTick(() => {
const nodeElement = treeInfoRef.value.treeRef.$el.querySelector(`[data-key="${nodeId}"]`);
if (nodeElement) {
nodeElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
});
}
/** 处理从详情处跳转而来的默认展示. */
const processRouter = () => {
const { guid, databaseName, tableName, databaseChName, databaseGuid, fieldGuid, fieldEnName, set } = useDataMetaStore()
let isFL = useDataMetaStore().isFieldLineage;
if (fieldGuid) {//查看字段血缘的
nextTick(() => {
treeInfo.value.expandedKey = <any>[databaseGuid, guid];
treeInfo.value.currentNodeKey = fieldGuid as string;
scrollToNode(fieldGuid);
treeInfoRef.value.setCurrentKey(fieldGuid);
})
lastClickNode.value = { guid: fieldGuid, tableGuid: guid, databaseGuid: databaseGuid, enName: fieldEnName, tableName, databaseName, type: 4, isLeaf: true, databaseChName };
// getTableFieldLineageMap();
set()
} else if (guid) {
treeInfo.value.currentNodeKey = guid as string;
treeInfo.value.expandedKey = <any>[databaseGuid, guid];
lastClickNode.value = { guid: guid, tableName, databaseName, type: 3, databaseChName };
scrollToNode(guid);
// if (isFL) {
// getAllTableFieldLineageMap();
// } else {
// getTableLineageMap()
// }
set()
}
}
onActivated(() => {
//processRouter();
});
onBeforeMount(async () => {
await getTreeData()
// processRouter();
})
onMounted(() => { })
const sankeyDataLoading = ref(false);
const sankeyData: any = ref([]);
const sankeyNames: any = ref([]);
const isGraphDisplay = ref(true);
const displaySwitchChange = (val) => {
if (val == isGraphDisplay.value) {
return;
}
isGraphDisplay.value = val;
if (!val) {
getSankeyDataList();
}
}
const getSankeyDataList = () => {
sankeyDataLoading.value = true;
getSankeyData(lastClickNode.value.guid).then((res: any) => {
sankeyDataLoading.value = false;
if (res?.code == proxy.$passCode) {
sankeyData.value = res.data?.links || [];
sankeyNames.value = res.data?.data || [];
} else {
ElMessage.error(res.msg);
}
})
}
const handleNodeItemClick = (graph, nodeItem) => {
const nodeId = nodeItem.get('id');
const parentData = graph.findDataById(nodeId);
if (!parentData.children) {
parentData.children = [];
}
if (parentData.noData) {
ElMessage.warning('没有可展开的下级字段');
return;
}
treeDataLoading.value = true;
getMetaStandardField(nodeId).then((res: any) => {
treeDataLoading.value = false;
if (res?.code == proxy.$passCode) {
const data = res.data || [];
parentData.children = [];
if (!data?.length) {
parentData.noData = true;
ElMessage.warning('没有可展开的下级字段');
return;
}
data.forEach(d => {
parentData.children.push(d);
})
parentData.isLoading = false;
parentData.collapsed = false;
graph.layout();
setTimeout(() => {
graph.updateItem(nodeItem, {
...nodeItem.getModel(),
collapsed: false
});
graph.setMinZoom(1);
graph.setMaxZoom(1);
graph.layout();
graph.setMinZoom(0.5);
graph.setMaxZoom(5);
graph.focusItem(nodeItem, true, {
duration: 500 // 动画时长为500ms
});
}, 500);
} else {
parentData.isLoading = false;
ElMessage.error(res.msg);
}
})
}
const handleContextMenu = (model) => {
if (model.isHaveData == 'N') {
ElMessage.warning('当前标准下无字段,请先添加字段');
return;
}
contextNodeData.value = model;
dialogInfo.value.visible = true;
formItems.value[0].default = '';
getDataWareCatalogList({}).then((res: any) => {
if (res?.code == proxy.$passCode) {
dataCatalogList.value = res.data || [];
formItems.value[0].options = dataCatalogList.value;
} else {
ElMessage.error(res.msg);
}
})
}
onBeforeUnmount(() => {
relationNetworkRef.value.destroy();
})
/** 数仓目录树形列表 */
const dataCatalogList = ref([]);
const formItems = ref([{
label: "数仓目录",
type: "tree-select",
placeholder: "请选择",
field: "domainGuid",
default: '',
options: dataCatalogList.value,
props: {
label: 'name',
value: 'guid'
},
showAllLevels: false,
checkStrictly: false,//只能选择叶子节点。
lazy: false,
filterable: true,
clearable: true,
required: true,
}]);
const formRules = ref({
domainGuid: [required('请选择数仓目录')],
});
const dialogInfo = ref({
visible: false,
size: 400,
direction: "column",
header: {
title: "引用标准新建数据集",
},
type: '',
contents: [
{
type: 'form',
title: '',
formInfo: {
id: 'select-subject-domain-list',
items: formItems.value,
rules: formRules.value
}
}
],
footer: {
btns: [
{ type: "default", label: "取消", value: "cancel" },
{ type: "primary", label: "确定", value: "submit", loading: false },
],
},
});
const contextNodeData: any = ref({});
const selectDataCatalogNodeObj: any = ref({});
const handleTreeSelectNodeChange = (node, item, nodeObj) => {
selectDataCatalogNodeObj.value = nodeObj;
}
const dialogBtnClick = (btn, info) => {
if (btn.value == 'submit') {
dialogInfo.value.visible = false;
if (selectDataCatalogNodeObj.value.parent.data.layereAttribute == 2) { //维度
router.push({
name: 'dimTableCreateManual',
query: {
domainGuid: info.domainGuid,
domainName: selectDataCatalogNodeObj.value.data.name,
metaStandard: contextNodeData.value.guid,
standardName: contextNodeData.value.label
}
});
} else {
if (selectDataCatalogNodeObj.value.parent.data.layereAttribute == 4) {
router.push({
name: 'tableCreateManual',
query: {
domainGuid: info.domainGuid,
domainName: selectDataCatalogNodeObj.value.data.name,
metaStandard: contextNodeData.value.guid,
layereAttribute: selectDataCatalogNodeObj.value.parent.data.layereAttribute,
standardName: contextNodeData.value.label
}
});
} else {
router.push({
name: 'tableCreateManual',
query: {
domainGuid: info.domainGuid,
domainName: selectDataCatalogNodeObj.value.data.name,
metaStandard: contextNodeData.value.guid,
standardName: contextNodeData.value.label
}
});
}
}
} else if (btn.value == 'cancel') {
dialogInfo.value.visible = false;
}
}
</script>
<template>
<div class="container_wrap full flex">
<div class="aside_wrap">
<div class="aside_title">元数据标准列表</div>
<Tree ref="treeInfoRef" :treeInfo="treeInfo" @nodeClick="nodeClick" />
</div>
<div class="main_wrap">
<div v-show="lastClickNode?.guid" className='g6-component-topbar'>
<graphTopbar ref="topBarRef" @displaySwitchChange="displaySwitchChange" :isGraphDisplay="isGraphDisplay" />
</div>
<RelationNetwork v-show="lastClickNode?.guid && isGraphDisplay" ref="relationNetworkRef"
:tree-data="lastClickNode" v-loading="treeDataLoading" @nodeItemClick="handleNodeItemClick"
@contextMenu="handleContextMenu">
</RelationNetwork>
<Sankey v-show="lastClickNode?.guid && !isGraphDisplay && (sankeyNames?.length || sankeyDataLoading)"
v-loading="sankeyDataLoading" :tree-data="sankeyData" :names="sankeyNames">
</Sankey>
<div
v-show="(!lastClickNode?.guid || !treeInfo.data?.length) && !treeInfo.loading || (!isGraphDisplay && !sankeyDataLoading && !sankeyNames?.length)"
class="main-placeholder">
<img src="../../assets/images/no-data.png" :style="{ width: '96px', height: '96px' }" />
<div class="empty-text">{{ treeInfo.data.length && !lastClickNode?.guid ? '请选中二级及以下级别标准查看' : '暂无数据' }}</div>
</div>
</div>
<Dialog :dialogInfo="dialogInfo" @btnClick="dialogBtnClick" @treeSelectNodeChange="handleTreeSelectNodeChange" />
</div>
</template>
<style scoped lang="scss">
.container_wrap {
.aside_wrap {
width: 200px;
margin-right: 1px;
}
.main_wrap {
position: relative;
:deep(.canvas-wrapper) {
background-color: #f7f7f9;
}
.main-placeholder {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.empty-text {
font-size: 14px;
color: #b2b2b2;
}
}
}
}
.g6-component-topbar {
position: absolute;
left: 24px;
bottom: unset;
top: 14px;
padding: 0;
text-align: center;
z-index: 999;
}
.container_wrap.flex .main_wrap {
padding: 0px;
}
.tree_panel {
height: calc(100% - 36px);
padding-top: 0;
:deep(.el-tree) {
margin: 0;
overflow: hidden auto;
}
}
.card-noData {
height: 100%;
width: 100%;
background: #fafafa;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #909399;
font-size: 14px;
}
:deep(.el-form .el-form-item) {
width: calc(100%);
// margin-right: 8px;
}
:deep(.el-message) {
position: fixed;
/* 使用fixed或absolute定位 */
z-index: 10000;
/* 设置一个较高的z-index值确保在最上层显示 */
}
</style>
<route lang="yaml">
ame: metadataStandard
</route>
<script lang="ts" setup name="metadataStandard">
import { ref, reactive, computed } from 'vue'
import { ElMessage, ElMessageBox } from "element-plus";
import { Search, CirclePlus } from '@element-plus/icons-vue'
import Tree from '@/components/Tree/index.vue'
import Table from '@/components/Table/index.vue'
import Drawer from '@/components/Drawer/index.vue'
import DictFileds from './components/dictFileds.vue'
import useCatchStore from "@/store/modules/catch";
import { download } from '@/utils/common'
import { getParamsList } from '@/api/modules/dataAsset'
import { getMetaStandardTree, deleteMetaStandard,
getMetaStandardDataList, getMetaStandardDataFields,
deleteMetaStandardDataFields, exportMetaStandardData
} from '@/api/modules/dataMetaService'
import router from '@/router'
import { TableColumnWidth } from '@/utils/enum';
import StandardDialog from './components/standardDialog.vue'
import StandardFieldsDialog from './components/standardFieldsDialog.vue'
function getAssetsImages(name) {
return new URL(`../../assets/images/${name}`, import.meta.url).href;
}
const { proxy } = getCurrentInstance() as any;
const route = useRoute();
const cacheStore = useCatchStore()
const showFiledsPage = ref(false)
// 树菜单
const dictTreeRef = ref()
const treeInfo = ref({
id: "data-pickup-tree",
filter: true,
queryValue: "",
queryPlaceholder: "请输入关键字搜索",
props: {
label: "standardName",
value: "guid",
isLeaf: "isLeaf",
},
// lazy: true,
nodeKey: 'guid',
expandedKey: [],
currentNodeKey: '',
data: [],
expandOnNodeClick: false,
loading: false,
currentObj: {},
editTreeItem: true,
// className: 'tree-list',
ellipsis: false
})
function nodeClick (data) {
console.log('nodeData', data)
treeInfo.value.currentObj = data
if (data.level == 1) return
getTableFields()
getFirstPageData()
}
function treeCustomClick (node, type) {
console.log(node, type)
if (type === 'edit') {
// 编辑
standardDialog.type = 'edit'
standardDialog.guid = node.data.guid
node.data.level = node.level
standardDialog.currentNode = node.data
standardDialog.visible = true
return
}
if (type === 'delete') {
// 删除
ElMessageBox.confirm('确定删除吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => deleteTreeNode(node.data.guid))
}
}
function getTree (refresh = false, initLocate = false) {
treeInfo.value.loading = true;
getMetaStandardTree().then((res:any) => {
treeInfo.value.loading = false;
if (res.code === proxy.$passCode) {
let data = res.data || []
data.forEach(item => {
item.showEdit = true
item.level = 1
})
treeInfo.value.data = data
if (!initLocate) {
// treeInfo.value.expandedKey = [data[0].guid]
// treeInfo.value.currentNodeKey = data[0].guid
console.log('data', data)
let firstNode = data[0]
if (firstNode.children) {
firstNode = firstNode.children[0]
}
treeInfo.value.expandedKey = [firstNode.guid]
treeInfo.value.currentNodeKey = firstNode.guid
nodeClick(firstNode)
}
}
})
}
function refreshTree (treeGuid) {
treeInfo.value.loading = true;
getMetaStandardTree().then((res:any) => {
treeInfo.value.loading = false;
if (res.code === proxy.$passCode) {
let data = res.data || []
data.forEach(item => {
item.showEdit = true
item.level = 1
})
treeInfo.value.data = data
if (treeGuid) {
setTreeCurrentNode(treeGuid)
}
}
})
}
function refreshTreeAndPage () {
refreshTree()
getFirstPageData()
}
function setTreeCurrentNode (treeGuid) {
treeInfo.value.currentNodeKey = treeGuid
let { data } = treeInfo.value
const format = (list) => {
list.forEach(item => {
if (item.guid === treeGuid) {
nodeClick(item)
return
}
if (item.children) {
format(item.children)
}
})
}
format(data)
}
function deleteTreeNode (guid) {
deleteMetaStandard([guid]).then((res:any) => {
if (res.code === proxy.$passCode) {
ElMessage.success('删除成功')
getTree()
} else {
ElMessage.error(res.msg)
}
})
}
const tableSearchInput = ref('')
const currTableData: any = ref<Object>({});
const page = ref({
limit: 50,
curr: 1,
sizes: [
{ label: "10", value: 10 },
{ label: "50", value: 50 },
{ label: "100", value: 100 },
{ label: "150", value: 150 },
{ label: "200", value: 200 },
]
})
const selectRowData = ref([])
const selectedRowData = ref([])
const tableInfo: any = ref({
id: 'data-source-table',
multiple: true,
fixedSelection: true,
fields: [
{ label: "序号", type: "index", width: 56, align: "center" },
// { label: '数据源标识符', field: 'codeName', width: 140 },
// { label: '数据元名称', field: 'standard', width: 140 },
// { label: '定义', field: 'standardName', width: 140 },
// { label: '数据类型', field: '', width: 120 },
// { label: '表示格式', field: 'createTime', width: TableColumnWidth }
],
data: [],
page: {
type: "normal",
rows: 0,
...page.value,
},
actionInfo: {
label: "操作",
type: "btn",
width: 92, //不要刚好90.缩小浏览器会因为小数点的差距而换行,
fixed: 'right',
btns: [
// { label: '查看', value: 'detail'},
{ label: "编辑", value: "edit" },
{ label: "删除", value: "delete" },
],
},
loading: false
})
const standardFields = ref([])
function getFirstPageData () {
page.value.curr = 1
toSearch({})
}
function toSearch (val: any, clear: boolean = false) {
let params: any = Object.keys(val).length ? { ...val } : {}
let { currentNodeKey, currentObj } = treeInfo.value
params.pageIndex = page.value.curr;
params.pageSize = page.value.limit;
params.metaStandardGuid = currentObj.guid
params.keyWords = tableSearchInput.value
getTable(params)
}
function getTable (params) {
tableInfo.value.loading = true
getMetaStandardDataList(params).then((res:any) => {
if (res.code === proxy.$passCode) {
let data = res.data
let list = res.data.records || []
list.forEach((item:any) => {
Object.keys(item.metaStandardValue).forEach(key => {
item[key] = item.metaStandardValue[key]
})
})
tableInfo.value.data = list
tableInfo.value.page.limit = data.pageSize
tableInfo.value.page.curr = data.pageIndex
tableInfo.value.page.rows = data.totalRows
}
}).finally(() => tableInfo.value.loading = false)
}
function getTableFields () {
let { currentObj } = treeInfo.value
const indexField = { label: '序号', type: 'index', width: 56, align: 'center' }
getMetaStandardDataFields(currentObj.guid).then((res:any) => {
if (res.code === proxy.$passCode && res.data) {
const data = res.data
standardFields.value = data
const fields = data.map(item => {
return {
label: item.fileNameCodeName,
field: item.fileNameCode,
width: 140
}
})
fields.unshift(indexField)
tableInfo.value.fields = fields
} else {
tableInfo.value.fields = [indexField]
}
})
}
function tablePageChange (info) {
page.value.curr = Number(info.curr)
page.value.limit = Number(info.limit)
toSearch({})
}
function tableBtnClick (scope, btn) {
console.log(scope, btn)
const type = btn.value
const row = scope.row
if (type === 'edit') {
openStandardFieldsDialog(type, row)
} else if (type === 'delete') {
ElMessageBox.confirm('确定删除吗?', '提示', {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: 'warning',
}).then(() => deleteStandardFields(row))
}
}
function deleteStandardFields (row) {
deleteMetaStandardDataFields([row.guid]).then((res:any) => {
if (res.code === proxy.$passCode) {
ElMessage.success('删除成功')
refreshTreeAndPage()
} else {
ElMessage.error(res.msg)
}
})
}
const formItems: any = ref([
{
label: '中文名称',
type: 'input',
placeholder: '请输入',
field: 'codeName',
default: '',
maxlength: 20,
clearable: true,
required: true
},
{
label: '英文生成规则',
type: 'select',
placeholder: '请选择',
field: 'publishingUnitCode',
default: '',
options: [],
clearable: true,
required: true
},
{
label: '英文名称',
type: 'input',
placeholder: '请输入',
field: 'orderNum',
default: '',
maxlength: 2,
clearable: true,
required: true
},
{
label: '标准编号',
type: 'input',
placeholder: '请选择',
field: 'codeFieldName',
default: '',
clearable: true,
required: true,
visible: true
},
{
label: '标准集',
type: 'Select',
placeholder: '请选择',
field: 'hierarchy',
default: '',
options: [],
clearable: true,
visible: false
},
{
label: '字段类型',
type: 'Select',
placeholder: '请选择',
field: 'hierarchy',
default: '',
options: [],
clearable: true,
visible: false
},
{
label: '数据分类',
type: 'Select',
placeholder: '请选择',
field: 'hierarchy',
default: '',
options: [],
clearable: true,
visible: false
},
{
label: '数据加密等级',
type: 'Select',
placeholder: '请选择',
field: 'hierarchy',
default: '',
options: [],
clearable: true,
visible: false
},
{
label: '关联数据字典',
type: 'checkbox',
placeholder: '请选择',
field: 'hierarchy',
default: '',
options: [],
clearable: true,
visible: false
},
{
label: '业务定义',
type: 'textarea',
placeholder: '请选择',
field: 'hierarchy',
default: '',
options: [],
clearable: true,
visible: false
}
])
const formRules: any = ref({
standardTypeCode: { required: true, message: '请选择标准类型' },
codeName: { required: true, message: '请输入代码名称' },
standard: { required: true, message: '请输入标准号' },
standardName: { required: true, message: '请输入标准名称' },
publishingUnitCode: { required: true, message: '请选择发布单位' },
orderNum: { required: true, message: '请输入排序' },
typeCode: { required: true, message: '请选择代码类型' },
codeFields: { required: true, message: '请选择编码字段' },
codeFieldName: { required: true, message: '请选择编码名称' }
})
const formInfo = ref({
type: 'form',
title: '',
col: 'span',
formInfo: {
id: 'add-dict-form',
col: 'col2',
readonly: false,
items: formItems.value,
rules: formRules.value
}
})
// 元标准dialog
const standardDialog = reactive({
visible: false,
type: 'add',
guid: null,
currentNode: null
})
function openStandardDialog () {
standardDialog.type = 'add'
standardDialog.visible = true
}
// 元标准数据dialog
const standardFieldsDialog = reactive({
visible: false,
type: 'add',
metaStandardGuid: null,
data: {}
})
function openStandardFieldsDialog (type, data = {}) {
standardFieldsDialog.type = type
standardFieldsDialog.metaStandardGuid = treeInfo.value.currentObj.guid
standardFieldsDialog.data = data
standardFieldsDialog.visible = true
}
function importData () {
let currentTreeObj:any = treeInfo.value.currentObj
console.log('currentTree', currentTreeObj)
let uploadSetting:any = []
if (currentTreeObj.children) {
formatChildrenLastItem(currentTreeObj.children, uploadSetting)
} else {
uploadSetting = [{
standardName: currentTreeObj.standardName,
standardGuid: currentTreeObj.guid
}]
}
cacheStore.setCatch('uploadSetting', uploadSetting)
router.push({
path: '/data-meta/metadata-standard/standard-meta-import',
});
}
function formatChildrenLastItem (list, returnList = []) {
list.forEach((item:any) => {
if (!item.children) {
returnList.push({
standardName: item.standardName,
standardGuid: item.guid
})
} else {
formatChildrenLastItem(item.children, returnList)
}
})
}
function exportData () {
let body = [treeInfo.value.currentObj.guid]
exportMetaStandardData(body).then((res:any) => {
if (res && !res.msg) {
download(res, '元数据标准表.xlsx', 'excel')
} else {
res?.msg && ElMessage.error(res?.msg);
}
})
}
function tableSelectionChange (list) {
selectedRowData.value = list
}
function batchDelete () {
// console.log(selectedRowData.value)
let selectRowGuids = selectedRowData.value.map(v => v.guid)
if (selectRowGuids.length === 0) {
ElMessage.error('请选择需要删除的数据')
return
}
ElMessageBox.confirm('确定删除吗?', '提示', {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: 'warning',
}).then(() => {
deleteMetaStandardDataFields(selectRowGuids).then((res:any) => {
if (res.code === proxy.$passCode) {
ElMessage.success('删除成功')
refreshTreeAndPage()
} else {
ElMessage.error(res.msg)
}
})
})
}
/** 从关系网定位打开此页面,需定位到对应的树节点。 route.query.standardGuid */
onActivated(() => {
if (route.query.standardGuid) {
treeInfo.value.currentNodeKey = route.query.standardGuid as string;
let obj = {
guid: route.query.standardGuid,
standardName: route.query.name
}
treeInfo.value.currentObj = obj;
nodeClick(obj);
}
})
onBeforeMount(() => {
getTree(false, route.query.standardGuid != null)
})
const viewGraph = () => {
router.push({
name: 'metadataStandardQueryView',
query: {
guid: treeInfo.value.currentObj?.guid,
name: treeInfo.value.currentObj?.standardName,
}
});
}
</script>
<template>
<div class="container_wrap full flex standard">
<div class="aside_wrap">
<div class="aside_title">
元数据标准列表
<el-icon color="#4fa1a4" @click="openStandardDialog" :size="20" class="custom-icon">
<CirclePlus />
</el-icon>
</div>
<Tree ref="dictTreeRef" :treeInfo="treeInfo" @nodeClick="nodeClick" @itemMenuClick="treeCustomClick">
<template #default="{ node, data }">
{{ node.label }}
</template>
</Tree>
</div>
<div class="main_wrap">
<template v-if="treeInfo.currentObj.level != 1">
<div class="header" style="font-size:16px;font-weight:bold;margin-top:8px;color:#212121">{{ treeInfo.currentObj.standardName }}</div>
<div class="table_tool_wrap">
<div class="tools_btns">
<el-button type="primary"
@click="() => openStandardFieldsDialog('add')"
:disabled="treeInfo.currentObj.children"
v-preReClick>新建</el-button>
<el-button @click="importData" v-preReClick>导入</el-button>
<el-button @click="exportData" v-preReClick>导出</el-button>
<el-button @click="viewGraph" v-preReClick>查看</el-button>
<el-button @click="batchDelete">批量删除</el-button>
</div>
<el-input class="table_search_input" v-model.trim="tableSearchInput" placeholder="请输入关键字搜索"
:suffix-icon="Search" clearable @change="val => getFirstPageData()" />
</div>
<div class="table_panel_wrap full" style="height:calc(100% - 84px)">
<Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" @tableSelectionChange="tableSelectionChange"
@tablePageChange="tablePageChange"/>
</div>
</template>
<div v-else class="no-content">
<img :src="getAssetsImages('no-data.png')" :style="{ width: '96px', height: '96px' }" />
<span>请选择标准表</span>
</div>
</div>
<StandardDialog
v-model="standardDialog.visible"
:standardOptions="treeInfo.data"
:type="standardDialog.type"
:guid="standardDialog.guid"
:currentNode="standardDialog.currentNode"
@success="refreshTree"
/>
<StandardFieldsDialog
v-model="standardFieldsDialog.visible"
:fields="standardFields"
:type="standardFieldsDialog.type"
:metaStandardGuid="standardFieldsDialog.metaStandardGuid"
:data="standardFieldsDialog.data"
@success="refreshTreeAndPage"
/>
</div>
</template>
<style lang="scss">
</style>
<style lang="scss" scoped>
.no-content {
height: 96%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.container_wrap {
.aside_wrap {
width: 200px;
}
.aside_title {
display: flex;
justify-content: space-between;
align-items: center;
}
}
.tree_panel {
height: 100%;
padding-top: 0;
:deep(.el-tree) {
margin: 0;
height: calc(100% - 68px);
overflow: hidden auto;
}
}
</style>
<style lang="scss">
.standard {
.custom-icon {
svg {
width: auto;
height: auto;
}
}
}
.tree_panel .el-tree .el-tree-node__content {
position: relative;
.list-item-text {
width: calc(100% - 58px)!important;
}
.tags-list-right {
position: absolute;
right: 2px;
top:8px;
}
}
</style>
<route lang="yaml">
name: tableCreateManual
</route>
<script lang="ts" setup name="tableCreateManual">
import { ref } from "vue";
import { useRouter, useRoute } from "vue-router";
import { ElMessage, ElMessageBox, ElTable } from "element-plus";
import { QuestionFilled } from "@element-plus/icons-vue";
import {
getDatabase,
saveSubjectTable,
syncPolicys,
tableModels,
tableCategoryList,
getDimList,
getDictionary,
aggMethodList,
getMetaStandardDsField,
getAllFlowData
} from "@/api/modules/dataMetaService";
import useUserStore from "@/store/modules/user";
import expandPropertyDialog from "./components/expandPropertyDialog.vue";
import tableDefaultValue from "./components/tableDefaultValue.vue";
import { useDefault } from "@/hooks/useDefault"
import { getCamundaDeploymentId } from "@/api/modules/workFlowService"
const userStore = useUserStore();
const { checkDefault } = useDefault()
const router = useRouter();
const route = useRoute();
const flowExpand = ref(true);
const deploymentId = ref('');
const fullPath = route.fullPath;
const isLook = <any>route.query.isLook == 1;
const subjectDomainGuid: any = ref(route.query.domainGuid);
const { proxy } = getCurrentInstance() as any;
const fieldStandardTableRef = ref<InstanceType<typeof ElTable>>();
/** 表模型,只有doris数据库才有 */
const dbType = ref("");
const databaseList: any = ref([]);
const tableFieldsLoading = ref(false);
const tableCreateInfoLoading = ref(false);
//记录当前正在编辑的表创建信息。
const tableCreateInfo: Ref<any> = ref({
guid: "",
isCreate: false,
inputNameValue: '',
tableCreateType: 1,
tableData: [
{
//数据库表信息。
dataSourceGuid: '',
dataServerName: "",
dataServerChName: "",
enName: "",
chName: "",
subjectDomain: route.query.domainName,
subjectDomainGuid: subjectDomainGuid.value,
tableCategory: route.query.layereAttribute == '3' ? 6 : 1,
syncPolicy: 3,
characterSet: 'utf8mb3',
tableModel: 1, //若是聚合模型,下方出现一列聚合方式选择。处了主键列,其余列都需要选择。每个表里都要有主键。
description: "",
},
],
partitionAttribute: {},
tableFields: [], // 字段标准数组。
});
const selectTableFieldRows = ref([]);
//字段类型
const fieldTypes: any = ref([]);
//字符集
const characterList: any = ref([]);
//是否列表
const isNotList = ref([
{
label: "Y",
value: "Y",
},
{
label: "N",
value: "N",
},
]);
//字典列表
const dictionaryList: any = ref([]);
// 可选择的关联维度的列表。
const dimListData: any = ref([]);
const fullscreenLoading = ref(false);
/** 表里有数据时不能修改字段类型,长度,精度 */
const hasSubjectData = ref(false);
const expandProperties = ref({});
onBeforeMount(() => {
getDatabaseList();
getFieldTypeList();
getCharacterListData();
if (route.query.metaStandard) {
tableCreateInfo.value.tableData[0].chName = route.query.standardName;
fullscreenLoading.value = true;
getMetaStandardDsField(route.query.metaStandard).then((res: any) => {
fullscreenLoading.value = false;
if (res.code == proxy.$passCode) {
tableCreateInfo.value.tableFields = res.data?.map((d, i) => {
d.orderNum = i;
d.isEdit = true;
return d;
}) || [];
} else {
proxy.$ElMessage.error(res.msg);
}
})
}
getCamundaDeploymentId('10025').then((res: any) => {
if (res.code == proxy.$passCode) {
deploymentId.value = res.data;
} else {
proxy.$ElMessage.error(res.msg);
}
})
})
onActivated(() => {
console.log("activated");
getDictionaryList();
getDimListData();
});
const getDatabaseList = () => {
getDatabase({ connectStatus: 1 }).then((res: any) => {
databaseList.value = [];
if (res.code == proxy.$passCode) {
databaseList.value = res.data || [];
} else {
ElMessage.error(res.msg);
}
})
};
const getDictionaryList = () => {
getDictionary({}).then((res: any) => {
dictionaryList.value = [];
if (res.code == proxy.$passCode) {
dictionaryList.value = res.data || [];
dimOrDictList.value[0].children = dictionaryList.value;
dimOrDictList.value[0].disabled = !dictionaryList.value.length;
} else {
ElMessage.error(res.msg);
}
})
};
const getDimListData = () => {
getDimList().then((res: any) => {
dimListData.value = [];
if (res.code == proxy.$passCode) {
dimListData.value = res.data?.map(r => {
return {
guid: r.guid,
chName: r.chName,
subjectDomainName: r.subjectDomainName,
parentGuid: '2'
}
}) || [];
dimOrDictList.value[1].children = dimListData.value;
dimOrDictList.value[1].disabled = !dimListData.value.length;
} else {
ElMessage.error(res.msg);
}
})
};
const getFieldTypeList = () => {
getAllFlowData('字段类型').then((res: any) => {
fieldTypes.value = [];
if (res.code == proxy.$passCode) {
fieldTypes.value = res.data || [];
} else {
ElMessage.error(res.msg);
}
})
}
const getCharacterListData = () => {
getAllFlowData('Mysql字符集').then((res: any) => {
characterList.value = [];
if (res.code == proxy.$passCode) {
characterList.value = res.data || [];
} else {
ElMessage.error(res.msg);
}
})
}
/** 限制长度输入框只能输入整型数字,表,字段英文名称,限制输入字符,数字和下划线。 */
const inputLengthKeyUp = (regexp, scope, field, max: any = null, min: any = null) => {
scope.row[field] = scope.row[field].replace(regexp, '');
if (field == 'fieldLength' && scope.row.dataType == 'decimal') {
max = 65;
}
/** 最大值设置2000 */
if (max && scope.row[field] > max) {
scope.row[field] = max;
}
if (min !== null && scope.row[field] != '' && scope.row[field] <= min) {
scope.row[field] = min;
}
}
/** 保存表 */
const saveTable = () => {
let tableData = tableCreateInfo.value.tableData[0];
if (!tableData.chName) {
ElMessage({
type: "error",
message: "主题表名称不能为空",
});
return;
}
if (!tableData.dataServerName) {
ElMessage({
type: "error",
message: "数据源不能为空",
});
return;
}
if (tableData.dataServerName.indexOf('-') > -1) {
ElMessage.error('数据库表名称不能包含中划线,可以改为下划线');
return;
}
if (!tableCreateInfo.value.inputNameValue) {
ElMessage({
type: "error",
message: "主题表名称不能为空",
});
return;
}
let tableFields = tableCreateInfo.value.tableFields;
if (!tableFields.length) {
ElMessage({
type: "error",
message: "表字段不能为0行",
});
return;
}
// 若开启了字段标准,则不能为空。
// 必须含有主键。若是聚合模型,则除了主键必须有聚合方式。
let isSumModel = tableData.tableModel === 2;
let hasPrimary = false;
let enNames: any = [];
let chNames: any = [];
const regex = /^[a-zA-Z]/;
for (const field of tableFields) {
if (!field.enName) {
ElMessage.error(`第 ${field.orderNum} 个字段的英文名称不能为空`);
return;
}
if (!regex.test(field.enName)) {
ElMessage.error(`第 ${field.orderNum} 个字段的英文名称必须以英文字符开头`);
return;
}
if (!field.dataType) {
ElMessage.error(`第 ${field.orderNum} 个字段的字段类型不能为空`);
return;
}
if (enNames.indexOf(field.enName) > -1) {
ElMessage.error(`字段的英文名称 ${field.enName} 不能重复`);
return;
}
if (chNames.indexOf(field.chName) > -1) {
ElMessage.error(`字段的中文名称 ${field.chName} 不能重复`);
return;
}
if (field.dataType === "decimal" && (!field.fieldPrecision && field.fieldPrecision != 0)) {
ElMessage.error(`第 ${field.orderNum} 个字段的字段类型为浮点型时,精度不能为空`);
return;
}
if (field.dataType === "varchar" && (!field.fieldLength && field.fieldLength != 0)) {
ElMessage.error(`第 ${field.orderNum} 个字段的字段类型为字符型时,长度不能为空`);
return;
}
if (field.dataType === "char" && (!field.fieldLength && field.fieldLength != 0)) {
ElMessage.error(`第 ${field.orderNum} 个字段的字段类型为单字符型时,长度不能为空`);
return;
}
if (field.dataType === "decimal" && (!field.fieldLength && field.fieldLength != 0)) {
ElMessage.error(`第 ${field.orderNum} 个字段的字段类型为浮点符型时,长度不能为空`);
return;
}
if (field.isPrimary === 'Y') {
hasPrimary = true;
if (field.notNull != 'Y') {
ElMessage.error(`第 ${field.orderNum} 个字段为主键,应设置为必填`);
return;
}
if (field.dataType == 'text') {
ElMessage.error(`第 ${field.orderNum} 个字段为主键,字段类型不能设置为‘大字段型’`);
return;
}
if (field.dataType == 'json') {
ElMessage.error(`第 ${field.orderNum} 个字段为主键,字段类型不能设置为‘JSON类型’`);
return;
}
if (field.dataType == 'bit') {
ElMessage.error(`第 ${field.orderNum} 个字段为主键,字段类型不能设置为‘布尔类型’`);
return;
}
} else {
if (!field.aggWay && isSumModel) {
ElMessage.error(`聚合模型的非主键字段必须设置聚合方式`);
return;
}
}
if (field.isEdit) {
if (checkDefault[field.dataType]) {
if (!field.fieldLength) { }
if (!checkDefault[field.dataType]({ row: field })) {
return;
}
}
}
field.fieldStandardGuid = route.query.metaStandard;
enNames.push(field.enName);
chNames.push(field.chName);
}
if (!hasPrimary) {
ElMessage.error(`字段至少有一个主键字段!`);
return;
}
let addInfo = Object.assign({}, tableCreateInfo.value.tableData[0], {
enName: tableCreateInfo.value.inputNameValue,
tableCreateType: tableCreateInfo.value.tableCreateType,
saveFlag: 1,
layereAttribute: route.query.layereAttribute,
dbType: dbType.value,
dataState: 1,
immediateApprove: true,
partitionAttribute: !Object.keys(tableCreateInfo.value.partitionAttribute).length ? null : Object.assign({}, tableCreateInfo.value.partitionAttribute, {
dynamicPartitionHistory: tableCreateInfo.value.partitionAttribute ? "Y" : 'N'
}),
subjectFieldAddDTOS: tableCreateInfo.value.tableFields.map((field, i) => {
return Object.assign({}, field, { orderNum: i + 1 });
})
})
if (!tableCreateInfo.value.guid) { //添加
fullscreenLoading.value = true;
saveSubjectTable(addInfo).then((res: any) => {
fullscreenLoading.value = false;
if (res.code == proxy.$passCode) {
ElMessage.success('新建表提交成功!');
router.push({
name: 'metadataStandardQuery'
});
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
} else {
ElMessage.error(res.msg);
}
});
}
};
//数据库选择改变,对应的表名称是否需要变化。需要根据此属性带出表名前缀,以及是否是doris数据库。
const selectDatabaseChange = (val) => {
let d = databaseList.value.find(d => d.guid === val);
if (d) {
dbType.value = d.databaseType;
tableCreateInfo.value.tableData[0].dataSourceGuid = d.guid;
tableCreateInfo.value.tableData[0].dataServerName = d.databaseNameEn;
} else {
dbType.value = "";
tableCreateInfo.value.tableData[0].dataSourceGuid = '';
tableCreateInfo.value.tableData[0].dataServerName = '';
}
tableCreateInfo.value.tableFields.forEach((tableField: any) => {
if (tableField['dataType'] === "datetime") {
tableField.defaultValue = ""
}
})
};
/** 添加字段标准 */
const addField = () => {
let len = tableCreateInfo.value.tableFields.length;
tableCreateInfo.value.tableFields.push({
orderNum: len + 1,
isDim: "N",
isPrimary: "N",
notNull: "N",
isEdit: true
});
//设置选中表格当前新增行。
fieldStandardTableRef.value?.setCurrentRow(
tableCreateInfo.value.tableFields[tableCreateInfo.value.tableFields.length - 1]
);
nextTick(() => {
let bodyWrapper = fieldStandardTableRef.value?.$el.querySelector('.el-table__body');
let domScroll = bodyWrapper.parentElement.parentElement;
let rect = domScroll.getBoundingClientRect();
let maxNum = len + 1;
if (maxNum * 36 > rect.height + domScroll.scrollTop) {
fieldStandardTableRef.value?.setScrollTop(maxNum * 36 - rect.height + 2)
}
})
};
/** 勾选字段标准选中变化。 */
const selectionFieldsChange = (val) => {
selectTableFieldRows.value = val;
};
/**
* 上移规则:
* 勾选多个时先从最上面开始逐个上移一行,若已经移到最上面一行,则不处理。
*/
const moveUp = () => {
let selectRows = fieldStandardTableRef.value?.getSelectionRows();
if (!selectRows.length) {
ElMessage.error('请先选择需要勾选的数据进行上移');
return;
}
let data = tableCreateInfo.value.tableFields;
let selectRowIndexs: number[] = [];
let minNum: number = 0;
selectRows.forEach((row, i) => {
let orderNum = data.findIndex(d => d === row) + 1;
if (orderNum == 1) {
selectRowIndexs.push(orderNum);
minNum = orderNum;
return;
}
let topNum = orderNum - 1;
if (selectRowIndexs.includes(topNum)) {
//下一行也是选中的,则不做转换。
return;
}
if (i === 0) {
minNum = topNum;
}
row.orderNum = topNum;
let changeRow = data[topNum - 1];
changeRow.orderNum = orderNum;
selectRowIndexs.push(topNum);
data[topNum] = changeRow;
data[topNum - 1] = row;
});
nextTick().then(() => {
let bodyWrapper = fieldStandardTableRef.value?.$el.querySelector('.el-table__body');
let domScroll = bodyWrapper.parentElement.parentElement;
if ((minNum * 36 - 38) < domScroll.scrollTop) {
let top = domScroll.scrollTop - 38;
fieldStandardTableRef.value?.setScrollTop(top < 0 ? 0 : top)
}
});
}
/**
* 下移规则:
* 勾选多个时先从最下面开始逐个下移一行,若已经移到最下面一行,则不处理。
*/
const moveDown = () => {
let selectRows = fieldStandardTableRef.value?.getSelectionRows();
if (!selectRows.length) {
ElMessage.error('请先选择需要勾选的数据进行下移');
return;
}
let data = tableCreateInfo.value.tableFields;
let selectRowIndexs: number[] = [];
let maxNum: number = 0;
selectRows.slice(0).reverse().forEach((row, i) => {
let orderNum = data.findIndex(d => d === row) + 1;
if (orderNum === data.length) {
maxNum = orderNum;
selectRowIndexs.push(orderNum);
return;
}
if (selectRowIndexs.includes(orderNum + 1)) {
//下一行也是选中的,则不做转换。
return;
}
let bottomNum = orderNum + 1;
row.orderNum = bottomNum;
if (i === 0) {
maxNum = bottomNum;
}
let changeRow = data[bottomNum - 1];
changeRow.orderNum = orderNum;
selectRowIndexs.push(bottomNum);
data[orderNum - 1] = changeRow;
data[bottomNum - 1] = row;
});
nextTick(() => {
let bodyWrapper = fieldStandardTableRef.value?.$el.querySelector('.el-table__body');
let domScroll = bodyWrapper.parentElement.parentElement;
let rect = domScroll.getBoundingClientRect();
if (maxNum * 36 > rect.height + domScroll.scrollTop) {
fieldStandardTableRef.value?.setScrollTop(maxNum * 36 - rect.height + 2)
}
})
}
/** 批量删除字段标准 */
const delFeilds = () => {
if (selectTableFieldRows.value.length == 0) {
ElMessage({
type: "info",
message: "请选择需要删除的字段",
});
return;
}
if (tableCreateInfo.value.isCreate) {
if (selectTableFieldRows.value.find((row: any) => row.isPrimary === 'Y')) {
ElMessage.error('已建表不能删除主键字段');
return;
}
}
let hasCreateField = selectTableFieldRows.value.some((row: any) => row.isCreate === 'Y');
ElMessageBox.confirm(hasCreateField && hasSubjectData.value ? '已选择的字段中含有已创建的表字段且有数据,此操作将永久删除,是否继续?' : "此操作将永久删除,是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
//此删除是直接从库里删除,还是点击保存后再删除呢??如果是入库删除,后,调用接口获取数据。
let tableFields = tableCreateInfo.value.tableFields;
selectTableFieldRows.value.forEach((r: any) => {
let index = tableFields.findIndex((t: any) => t.orderNum === r.orderNum);
if (index !== -1) {
tableFields.splice(index, 1);
}
});
fieldStandardTableRef.value?.clearSelection();
tableCreateInfo.value.tableFields.forEach((field, i) => {
field.orderNum = i + 1;
});
ElMessage({
type: "success",
message: "删除成功",
});
})
.catch(() => {
ElMessage({
type: "info",
message: "已取消删除",
});
});
};
//点击编辑按钮
const handleFieldClickEdit = (scope) => {
scope.row['isEdit'] = true;
};
//点击保存按钮
const handleFieldClickSave = (scope) => {
if (!scope.row.enName) {
ElMessage({
type: "error",
message: "字段英文名不能为空!",
});
return;
}
if (checkDefault[scope.row.dataType]) {
if (!scope.row.fieldLength) { }
if (!checkDefault[scope.row.dataType](scope)) {
return
}
}
scope.row['isEdit'] = false;
};
const handleFieldDelete = (scope) => {
ElMessageBox.confirm(scope.row['isCreate'] === 'Y' && hasSubjectData.value ? '该字段已被创建且表中有数据,此操作将永久删除,是否继续?' : "此操作将永久删除,是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
//此删除是直接从库里删除,还是点击保存后再删除呢??如果是入库删除,后,调用接口获取数据。
let tableFields = tableCreateInfo.value.tableFields;
tableFields.splice(scope.$index, 1);
tableCreateInfo.value.tableFields.forEach((field, i) => {
field.orderNum = i + 1;
});
tableCreateInfo.value.tableFields.forEach((field, i) => {
field.orderNum = i + 1;
});
ElMessage({
type: "success",
message: "删除成功",
});
})
.catch(() => {
ElMessage({
type: "info",
message: "已取消删除",
});
});
}
const dataTypeChange = (val, scope) => {
scope.row['defaultValue'] = ''
scope.row['fieldLength'] = undefined
scope.row['fieldPrecision'] = undefined
}
/*** 以下是处理数据字典或维表的树形选择框。 */
const dimOrDictList: any = ref([{
guid: '1',
chName: '数据字典',
children: dictionaryList.value,
isLeaf: false,
disabled: !dictionaryList.value.length,
}, {
guid: '2',
chName: '维度',
isLeaf: false,
disabled: !dimListData.value.length,
children: dimListData.value
}]);
const dimOrDictInputFilterMethod = (v, data) => {
return data.label?.includes(v) || data.chName?.includes(v);
};
const dimOrDictSelectRef = ref();
const dimOrDictSelectNode = ref();
const handleDictSelectNodeChange = (node) => {
dimOrDictSelectNode.value = node;
}
const handleDictionaryChange = (val, scope) => {
if (!val) {
scope.row.dictionaryGuid = '';
scope.row.dimGuid = '';
scope.row.dictionaryChName = '';
scope.row.dimChName = '';
return;
}
let info = dimOrDictSelectNode.value;
if (!info) {
return;
}
if (info.parentGuid == '2') {
scope.row.dimGuid = val;
scope.row.dimChName = dimListData.value.find(d => d.guid === val)?.chName;
scope.row.dictionaryGuid = '';
scope.row.dictionaryChName = '';
} else {
scope.row.dictionaryGuid = val;
scope.row.dictionaryChName = dictionaryList.value.find(d => d.guid === val)?.chName;
scope.row.dimGuid = '';
scope.row.dimChName = '';
}
}
/** 扩展属性弹出对话框 */
const expandPropertyDialogRef = ref();
/** 扩展属性弹出对话框 */
const handleClickExpand = () => {
expandPropertyDialogRef.value?.handleClickExpand();
}
const expandDialogValueChange = (val) => {
tableCreateInfo.value.partitionAttribute = val;
expandProperties.value = val;
}
</script>
<template>
<div class="table_tool_wrap" v-loading="fullscreenLoading">
<div class="tools_btns">
<!-- <el-button type="primary" :disabled="isLook" @click="saveDraftTable" v-preReClick>保存为草稿</el-button> -->
<el-button type="primary" :disabled="isLook" @click="saveTable" v-preReClick>提交</el-button>
</div>
<el-table ref="tableRef" :data="tableCreateInfo.tableData" v-loading="tableCreateInfoLoading"
:highlight-current-row="true" stripe border height="100%" tooltip-effect="light" row-key="guid" :style="{
width: '100%',
height: 'auto',
display: 'inline-block',
}">
<el-table-column prop="dataSourceGuid" label="数据源" width="200px" align="left" show-overflow-tooltip>
<template #header>
<span>数据源</span>
<span v-if="!isLook" style="color:red;margin-left: 2px;">*</span>
</template>
<template #default="scope">
<el-select v-if="!isLook" v-model="scope.row['dataSourceGuid']" placeholder="请选择"
:disabled="tableCreateInfo.isCreate" @change="(val) => selectDatabaseChange(val)" clearable filterable>
<el-option v-for="opt in databaseList" :key="opt['guid']" :label="opt['databaseNameZh']"
:value="opt['guid']" />
</el-select>
<span v-else>{{ scope.row["dataServerChName"] }}</span>
</template>
</el-table-column>
<el-table-column prop="enName" label="数据库表" width="200px" align="left" show-overflow-tooltip>
<template #header>
<span>数据库表</span>
<span v-if="!isLook" style="color:red;margin-left: 2px;">*</span>
</template>
<template #default="scope">
<template v-if="!isLook">
<div class="prefix-or-suffix-cell">
<el-input :disabled="tableCreateInfo.isCreate" v-model.trim="tableCreateInfo.inputNameValue"
:maxlength="50" placeholder="必填" />
</div>
</template>
<span v-else>{{ scope.row["enName"] }}</span>
</template>
</el-table-column>
<el-table-column prop="chName" label="主题表名称" width="200px" align="left" show-overflow-tooltip>
<template #header>
<span>主题表名称</span>
<span v-if="!isLook" style="color:red;margin-left: 2px;">*</span>
</template>
<template #default="scope">
<el-input v-if="!isLook" v-model.trim="scope.row['chName']" placeholder="必填" :maxlength="50" />
<span v-else>{{ scope.row["chName"] }}</span>
</template>
</el-table-column>
<el-table-column prop="subjectDomain" label="主题域" width="180px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-input v-if="!isLook" disabled v-model="scope.row['subjectDomain']" />
<span v-else>{{ scope.row["subjectDomain"] }}</span>
</template>
</el-table-column>
<el-table-column prop="tableModel" label="表模型" :width="isLook ? '100px' : '150px'" align="left"
show-overflow-tooltip>
<template #default="scope">
<el-select v-if="dbType == 'doris' && !isLook" v-model="scope.row['tableModel']" placeholder="请选择"
:disabled="tableCreateInfo.isCreate">
<el-option v-for="opt in tableModels" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
</el-select>
<span v-else-if="dbType == 'doris'">{{tableModels.find(t => t.value === scope.row["tableModel"])?.label ||
'-'
}}</span>
<span v-else>--</span>
</template>
</el-table-column>
<el-table-column prop="tableCategory" label="表分类" :width="isLook ? '100px' : '150px'" align="left"
show-overflow-tooltip>
<template #default="scope">
<el-select v-if="!isLook" v-model="scope.row['tableCategory']" placeholder="请选择">
<el-option v-for="opt in tableCategoryList" :key="opt['value']" :label="opt['label']"
:value="opt['value']" />
</el-select>
<span v-else>{{tableCategoryList.find(t => t.value === scope.row["tableCategory"])?.label || '-'}}</span>
</template>
</el-table-column>
<el-table-column prop="syncPolicy" label="同步策略" :width="isLook ? '100px' : '150px'" align="left"
show-overflow-tooltip>
<template #default="scope">
<el-select v-if="!isLook" v-model="scope.row['syncPolicy']" placeholder="请选择">
<el-option v-for="opt in syncPolicys" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
</el-select>
<span v-else>{{syncPolicys.find(s => s.value === scope.row["syncPolicy"])?.label || '-'}}</span>
</template>
</el-table-column>
<el-table-column prop="characterSet" label="字符集" :width="isLook ? '100px' : '150px'" align="left"
show-overflow-tooltip>
<template #default="scope">
<el-select v-if="!isLook" v-model="scope.row['characterSet']" placeholder="请选择"
:disabled="tableCreateInfo.isCreate">
<el-option v-for="opt in characterList" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
</el-select>
<span v-else>{{characterList.find(c => c.value === scope.row["characterSet"])?.label || '-'}}</span>
</template>
</el-table-column>
<el-table-column prop="description" label="描述" width="220px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-input v-if="!isLook" v-model.trim="scope.row['description']" />
<span v-else>{{ scope.row["description"] || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="100px" align="left" fixed="right" show-overflow-tooltip>
<template #default="scope">
<span class="text_btn" @click="handleClickExpand()" v-preReClick>扩展属性</span>
</template>
</el-table-column>
</el-table>
<div class="tools_btns">
<el-button type="primary" :disabled="isLook" @click="addField">新增</el-button>
<el-button @click="moveUp" :disabled="isLook">上移</el-button>
<el-button @click="moveDown" :disabled="isLook">下移</el-button>
<el-button @click="delFeilds" :disabled="isLook">批量删除</el-button>
</div>
<div class="table_panel">
<el-table ref="fieldStandardTableRef" :data="tableCreateInfo.tableFields" v-loading="tableFieldsLoading"
:highlight-current-row="true" stripe border height="100%" row-key="guid"
@selection-change="selectionFieldsChange" tooltip-effect="light" :style="{
width: '100%',
'max-height': 'calc(100% - 16px)',
display: 'inline-block',
}">
<el-table-column type="selection" v-if="!isLook" :width="32" align="center" />
<el-table-column label="排序" type="index" width="56px" align="center" show-overflow-tooltip>
</el-table-column>
<el-table-column prop="chName" label="字段中文名称" width="150px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-input v-if="scope.row['isEdit']" :placeholder="'请输入'" v-model.trim="scope.row['chName']" />
<span v-else>{{ scope.row["chName"] || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="enName" label="字段英文名" width="150px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-input v-if="scope.row['isEdit'] && !(scope.row['guid'] && tableCreateInfo.isCreate)"
v-model.trim="scope.row['enName']" placeholder="必填"
@input="inputLengthKeyUp(/[^a-zA-Z0-9_]/g, scope, 'enName')" />
<span v-else>{{ scope.row["enName"] || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="dataType" label="字段类型" width="120px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-select v-if="scope.row['isEdit'] && !(tableCreateInfo.isCreate && scope.row['isCreate'] === 'Y')"
v-model="scope.row['dataType']" placeholder="请选择" @change="(val) => dataTypeChange(val, scope)">
<el-option v-for="opt in fieldTypes" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
</el-select>
<span v-else>{{fieldTypes.find(f => f.value === scope.row["dataType"])?.label || '-'}}</span>
</template>
</el-table-column>
<el-table-column prop="fieldLength" label="长度" width="115px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-input
v-if="scope.row['isEdit'] && !(tableCreateInfo.isCreate && scope.row['isCreate'] === 'Y') && (scope.row['dataType'] == 'varchar' || scope.row['dataType'] == 'decimal' || scope.row['dataType'] == 'char')"
v-model.trim="scope.row['fieldLength']" placeholder="必填"
@input="inputLengthKeyUp(/\D/g, scope, 'fieldLength', 2000, 1)" />
<span v-else>{{ scope.row["fieldLength"] == null ? '-' : scope.row["fieldLength"] }}</span>
</template>
</el-table-column>
<el-table-column prop="fieldPrecision" label="精度" width="115px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-input
v-if="scope.row['isEdit'] && !(tableCreateInfo.isCreate && scope.row['isCreate'] === 'Y') && scope.row['dataType'] == 'decimal'"
v-model.trim="scope.row['fieldPrecision']" placeholder="必填"
@input="inputLengthKeyUp(/\D/g, scope, 'fieldPrecision', 30, 1)" />
<span v-else>{{ scope.row["fieldPrecision"] == null ? '-' : scope.row["fieldPrecision"] }}</span>
</template>
</el-table-column>
<el-table-column prop="dimOrdictionaryGuid" label="关联维度/字典" width="130px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-tree-select ref="dimOrDictSelectRef" v-if="scope.row['isEdit']" filterable clearable
:data="dimOrDictList" v-model="scope.row['dimOrdictionaryGuid']" node-key="guid"
:default-expanded-keys="scope.row['dictionaryGuid'] ? ['1'] : (scope.row['dimGuid'] ? ['2'] : [])"
placeholder="请选择" :filter-node-method="dimOrDictInputFilterMethod" :props="{
label: 'chName',
value: 'guid',
children: 'children',
isLeaf: 'isLeaf'
}" @change="(v) => handleDictionaryChange(v, scope)" @current-change="handleDictSelectNodeChange">
<template #default="{ node, data }">
<template v-if="node.level > 1 && data.parentGuid == '2'">
<span>{{ data["chName"] + `(${data["subjectDomainName"]})` }}</span>
</template>
<span v-else>{{ data['chName'] }}</span>
</template>
</el-tree-select>
<span v-else>{{ (scope.row['dictionaryGuid'] ? scope.row["dictionaryChName"] : (scope.row['dimGuid'] ?
scope.row['dimChName'] : '-')) || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="isPrimary" label="是否主键" width="90px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-select v-if="scope.row['isEdit'] && !tableCreateInfo.isCreate" v-model="scope.row['isPrimary']"
placeholder="请选择">
<el-option v-for="opt in isNotList" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
</el-select>
<span v-else>{{ scope.row["isPrimary"] || '-' }}</span>
</template>
</el-table-column>
<el-table-column v-if="tableCreateInfo.tableData[0].tableModel == 2" prop="aggWay" label="聚合方式" width="120px"
align="left" show-overflow-tooltip>
<template #default="scope">
<el-select v-if="scope.row['isEdit']" v-model="scope.row['aggWay']" placeholder="请选择">
<el-option v-for="opt in aggMethodList" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
</el-select>
<span v-else>{{ scope.row["aggWay"] || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="notNull" label="是否必填" width="90px" align="left" show-overflow-tooltip>
<template #default="scope">
<el-select v-if="scope.row['isEdit'] && !(scope.row.isCreate == 'Y' && scope.row['notNull'] == 'N')"
v-model="scope.row['notNull']" placeholder="请选择">
<el-option v-for="opt in isNotList" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
</el-select>
<span v-else>{{ scope.row["notNull"] || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="defaultValue" label="默认值" width="205px" align="left" show-overflow-tooltip>
<template #default="scope">
<tableDefaultValue :scope="scope" :dbType="dbType"
:readonly="dbType == 'doris' && scope.row.isCreate == 'Y'"></tableDefaultValue>
</template>
</el-table-column>
<el-table-column v-if="!isLook" label="操作" width="100px" align="left" fixed="right" show-overflow-tooltip>
<template #default="scope">
<template v-if="!(tableCreateInfo.isCreate && scope.row['isPrimary'] === 'Y')">
<span class="text_btn" v-if="!scope.row['isEdit']" @click="handleFieldClickEdit(scope)"
v-preReClick>编辑</span>
<span class="text_btn" v-else @click="handleFieldClickSave(scope)" v-preReClick>保存</span>
<el-divider direction="vertical" />
<span class="text_btn" @click="handleFieldDelete(scope)">删除</span>
</template>
<span v-else>--</span>
</template>
</el-table-column>
</el-table>
</div>
<ContentWrap title="流程审批" description="" :isExpand="flowExpand" :expand-swicth="true" class="mb16"
@expand="(v) => flowExpand = v">
<ApprovalProcess v-if="deploymentId" :deploymentId="deploymentId" :definitionId="''">
</ApprovalProcess>
</ContentWrap>
<expandPropertyDialog ref="expandPropertyDialogRef" :is-look="isLook" :partitionAttribute="expandProperties"
:table-create-info="tableCreateInfo" @expandValueChange="expandDialogValueChange" />
</div>
</template>
<style lang="scss" scoped>
.table_tool_wrap {
width: 100%;
height: 100%;
padding: 0 16px;
overflow: hidden auto;
.tools_btns {
padding: 8px 0;
}
:deep(.el-table) {
.cell {
.prefix-or-suffix-cell {
display: inline-flex;
align-items: center;
}
}
}
.table_panel {
height: 268px;
:deep(.el-table) {
& td.el-table__cell {
padding: 2px 0;
height: 36px;
}
}
}
}
:deep(.el-dialog) {
.dialog-form-inline {
.checkbox_input {
display: flex;
flex-direction: column;
.input_panel {
margin: 0;
}
}
.select_group {
.el-form-item__content>.el-input {
margin-top: 21px;
}
}
.radio_panel {
.panel_content {
display: none;
}
}
}
}
</style>
<route lang="yaml">
name: calculateConfig
</route>
<script lang="ts" setup name="calculateConfig">
import { ref, onMounted } from "vue";
import { useRouter, useRoute } from "vue-router";
import { ElMessage, ElMessageBox } from "element-plus";
import useUserStore from "@/store/modules/user";
import useDataAssetStore from "@/store/modules/dataAsset";
import { getAllFlowData } from '@/api/modules/queryService';
import { getDamCatalogList } from "@/api/modules/dataPricing";
import { getRegisterCatalogDetail, getRegisterCatalogTableDetail } from "@/api/modules/dataAsset";
import { download } from '@/utils/common'
import {
getConfigureList,
getConfigureDetail,
getDiseaseAll,
getPriceDetail,
getDemandList,
getModelScore,
savePrice,
getModelDemand,
getPriceResult,
exportModelScore
} from '@/api/modules/dataPricing';
import { changeNum } from "@/utils/common";
const { proxy } = getCurrentInstance() as any;
const router = useRouter();
const route = useRoute();
const userStore = useUserStore();
const assetStore = useDataAssetStore();
const fullPath = route.fullPath;
const userData = JSON.parse(localStorage.userData);
const guid = route.query.guid;
const priceName = route.query.name;
const loading = ref(false);
const flowDetail: any = ref({});
const typeMap: any = ref({});
const expand1 = ref(true)
const expand2 = ref(true)
const expand3 = ref(true)
const demandTableList: any = ref([]);
const pricingTargetList: any = ref([]);
const demandTableFieldAllNum = ref(0);
const resourceTableAllNum = ref(0);
const resourceTableFieldAllNum = ref(0);
const modelData: any = ref({});
const pricingDimensionalityData: any = ref([]);
const dictionaryData: any = ref([]);
const diseaseData: any = ref([]);
const qualityScoreData: any = ref({});
const disScore: any = ref([]);
const exportData: any = ref([]);
const dataUsage = ref('');
// 基础设置
const baseConfigFormRef = ref();
const baseConfigFormItems: any = ref([
{
label: '模型名称',
type: 'select',
placeholder: '请选择',
field: 'modelGuid',
default: '',
options: [],
props: {
label: "modelName",
value: "guid",
},
clearable: true,
filterable: true,
required: true
},
{
label: '数据资源',
type: 'select',
placeholder: '请选择',
field: 'dataResourceGuid',
default: '',
options: [],
props: {
label: "damName",
value: "guid",
},
clearable: true,
filterable: true,
required: true,
},
{
label: '所属主体',
type: 'input',
placeholder: '',
field: 'belongingEntityGuid',
default: '',
options: [],
clearable: true,
disabled: true
},
{
label: '所属主题',
type: 'tree-select',
placeholder: '请选择',
field: 'belongingTheme',
default: '',
options: [],
showAllLevels: false,
checkStrictly: false,//只能选择叶子节点。
lazy: false,
props: {
label: "label",
value: "value",
children: 'childDictList'
},
filterable: true,
clearable: true,
disabled: true
},
])
const baseConfigFormRules: any = ref({
modelGuid: [
{ required: true, trigger: 'change', message: "请选择模型名称" }
],
dataResourceGuid: [
{ required: true, trigger: 'change', message: "请选择数据资源" }
],
});
const baseConfigForm = ref({
items: baseConfigFormItems.value,
rules: baseConfigFormRules.value,
})
const tableFields: any = ref([
{ label: '需求表', field: 'demandTableName', type: 'input', width: 200, disabled: true },
{ label: '数据资源表', field: 'dataTableGuid', type: 'select', width: 200 },
{ label: '表描述', field: 'tableDescription', type: 'input', width: 200, disabled: true },
{ label: '需求表权重(%)', field: 'weightDemandTable', type: 'input', width: 140, disabled: true },
])
const expendTableRef = ref();
const tableData: any = ref([]);
const tableLoading = ref(false);
const dataTransactionPrice: any = ref('');
const setFormItems = (info = null) => {
let datas: any = info || flowDetail.value || {};
const dData = datas.dictionaryJson ? JSON.parse(datas.dictionaryJson) : {};
datas = { ...datas, ...dData };
baseConfigFormItems.value.map(item => {
item.default = datas[item.field] || '';
})
nextTick(() => {
baseConfigFormRef.value.ruleFormRef?.clearValidate();
})
}
/**
* 传入多个promise对象,当全部结束时取消Loading
* @param promises 传入多个promise对象,当全部结束时取消Loading
*/
const promiseList = (...promises: Promise<void>[]) => {
// loading方法全局封装成一个组件
!guid && (loading.value = true);
try {
Promise.all(promises).then(res => {
loading.value = false;
});
} catch (e) {
loading.value = false;
} finally {
!guid && (loading.value = false);
}
};
// 获取模型
const getModel = () => {
getConfigureList({ pageSize: -1, pageIndex: 1, bizState: 'Y' }).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data.records || [];
typeMap.value.modelGuid = JSON.parse(JSON.stringify(data));
let item = baseConfigFormItems.value.find(item => item.field == 'modelGuid');
item && (item.options = data);
}
})
}
// 获取所有疾病数据
const getDiseaseData = () => {
getDiseaseAll().then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || [];
typeMap.value.diseaseGuid = JSON.parse(JSON.stringify(data));
let item = baseConfigFormItems.value.find(item => item.field == 'diseaseGuid');
if (item) {
item.options = typeMap.value['diseaseGuid'];
if (guid) {
const diseaseData = typeMap.value.diseaseGuid.find(m => m.guid == flowDetail.value.diseaseGuid);
if (!diseaseData) {
item.options.unshift({
guid: flowDetail.value.diseaseGuid,
diseaseName: flowDetail.value.diseaseName
});
}
}
}
}
})
}
// 获取数据资源
const getDataCatalog = () => {
return getDamCatalogList({ dataType: userData.superTubeFlag == 'Y' ? "P" : "D", sceneType: "D" }).then((res: any) => {
if (res.code == proxy.$passCode) {
let data = res.data || [];
data.map(item => item.damGuid = item.guid);
typeMap.value.dataResourceGuid = JSON.parse(JSON.stringify(data));
let item = baseConfigFormItems.value.find(item => item.field == 'dataResourceGuid');
if (item) {
item.options = data;
if (guid) {
const rItem = typeMap.value.dataResourceGuid.find(m => m.damGuid == flowDetail.value.dataResourceGuid);
if (!rItem) {
const rtem = { damGuid: flowDetail.value.dataResourceGuid, damName: flowDetail.value.dataResourceName };
item.options.unshift(rtem);
typeMap.value.dataResourceGuid.unshift(rtem);
}
}
}
}
})
}
// 获取数据资源主题
const getSourceThem = (dictType, fieldName) => {
return getAllFlowData({ dictType }).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || [];
typeMap.value[fieldName] = JSON.parse(JSON.stringify(data));
let item = baseConfigFormItems.value.find(item => item.field == fieldName);
item && (item.options = data);
} else {
proxy.$ElMessage.error(res.msg);
}
})
}
// 获取数据字典
const getDataType = (dictType, fieldName) => {
getAllFlowData({ dictType }).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || [];
typeMap.value[fieldName] = JSON.parse(JSON.stringify(data));
let item = baseConfigFormItems.value.find(item => item.field == fieldName);
item && (item.options = data);
} else {
proxy.$ElMessage.error(res.msg);
}
})
}
// 获取详情
const getDetail = () => {
loading.value = true;
getPriceDetail({ guid }).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || {};
flowDetail.value = data;
dataTransactionPrice.value = flowDetail.value.dataTransactionPrice;
dataUsage.value = data.dataUsage || '';
const mItem = typeMap.value.modelGuid.find(m => m.guid == flowDetail.value.modelGuid);
if (!mItem) {
const mtem = { guid: flowDetail.value.modelGuid, modelName: flowDetail.value.modelName };
typeMap.value.modelGuid.unshift(mtem);
baseConfigFormItems.value[0].options.unshift(mtem);
};
getModelInfo(flowDetail.value.modelGuid);
getDataTypeList()
}
}).catch(() => {
loading.value = false;
})
}
const getDataTypeList = () => {
if (guid) {
promiseList(
getDataCatalog(),
getSourceThem('数据资产目录主题名称', 'belongingTheme'),
getQuilityModelScore(flowDetail.value.dataResourceGuid)
)
} else {
promiseList(
getDataCatalog(),
getSourceThem('数据资产目录主题名称', 'belongingTheme'),
)
}
}
const setFormItemData = () => {
let dictionaryList: any = [], diseaseList: any = [];
pricingTargetList.value.map(item => {
switch (item.targetType) {
case '2':
item.functionName == '2' && diseaseList.push(item);
break;
case '3':
dictionaryList.push(item);
break;
default:
break;
}
})
dictionaryData.value = dictionaryList;
diseaseData.value = diseaseList;
if (diseaseList.length) {
const diseaseName = flowDetail.value.diseaseName || '';
const modelGuid = flowDetail.value.modelGuid || '';
// 获取疾病得分
if (diseaseName && modelGuid) {
getTargetNum({ diseaseName, guid: modelGuid });
}
}
baseConfigFormItems.value.splice(4);
for (var r in baseConfigFormRules.value) {
if (r != 'modelGuid' && r != 'dataResourceGuid') {
delete baseConfigFormRules.value[r];
}
}
// 添加所属疾病
if (diseaseList.length > 0) {
baseConfigFormItems.value.push({
label: '所属疾病',
type: 'cascader',
placeholder: '请选择',
field: 'diseaseGuid',
default: '',
options: [],
showAllLevels: false,
props: {
checkStrictly: true,
label: "diseaseName",
value: "guid",
children: 'childList',
emitPath: false
},
filterable: true,
clearable: true,
required: true,
});
baseConfigFormRules.value.diseaseGuid = { required: true, trigger: 'change', message: "请选择所属疾病" };
if (typeMap.value['diseaseGuid'] == undefined) {
getDiseaseData();
} else {
let item = baseConfigFormItems.value.find(item => item.field == 'diseaseGuid');
if (item) {
item.options = typeMap.value['diseaseGuid'];
const diseaseData = typeMap.value.diseaseGuid.find(m => m.guid == flowDetail.value.diseaseGuid);
if (!diseaseData) {
item.options.unshift({
guid: flowDetail.value.diseaseGuid,
diseaseName: flowDetail.value.diseaseName
});
}
}
}
}
// 添加数据字典
dictionaryList.map(d => {
const dictName = d.dictionaryName;
const dictField = `dict_${d.guid}`;
baseConfigFormItems.value.push({
label: dictName,
type: 'select',
placeholder: '请输入',
field: dictField,
default: '',
options: [],
clearable: true,
filterable: true,
required: true,
});
baseConfigFormRules.value[dictField] = { required: true, trigger: 'change', message: `请选择${dictName}` };
(() => {
if (typeMap.value[dictField] == undefined) {
getDataType(dictName, dictField)
} else {
let item = baseConfigFormItems.value.find(item => item.field == dictField);
item && (item.options = typeMap.value[dictField]);
}
})()
})
setTimeout(() => {
baseConfigFormRef.value.ruleFormRef?.clearValidate();
}, 100)
}
const setdemandTableData = (mGuid = '') => {
const tList = flowDetail.value.dataPricingDemandmatchingRQVOS || demandTableList.value || [];
let tDatas: any = [];
if (guid) {
if (mGuid) {
tDatas = mGuid == flowDetail.value.modelGuid ? tList : demandTableList.value || [];
} else {
tDatas = tList;
}
} else {
tDatas = tList;
}
setTableData(JSON.parse(JSON.stringify(tDatas)))
}
const setTableData = (dataArr) => {
tableData.value.splice(0);
dataArr.map((item, i) => {
const demInfo = pricingTargetList.value.find(t => t.demandTableGuid == (item.demandTableGuid || item.guid));
const demWeight = demInfo?.weight || '';
tableData.value.push({
...item,
demandTableName: item.demandTableName || item.menuName,
dataTableGuid: item.dataTableGuid || '',
tableDescription: item.tableDescription || '',
weightDemandTable: item.weightDemandTable ? parseFloat(item.weightDemandTable).toFixed(2) : (demWeight ? parseFloat(demWeight).toFixed(2) : ''),
dataFields: item.pricingDemandFieldRQVOS || [],
dataFieldsNum: item.dataFieldsNum || 0,
})
if ((item.demandTableGuid || item.guid)) {
const rGuid = item.demandTableGuid || item.guid;
const rIndex = i;
if (!guid || (guid && rGuid != (demInfo?.demandTableGuid || ''))) {
(() => {
getDemandField(rGuid, rIndex);
})()
}
}
})
resourceTableFieldAllNum.value = tableData.value.reduce((accumulator, currentValue) => {
return accumulator + Number(currentValue.dataFieldsNum);
}, 0);
setTimeout(() => {
tableData.value.map(t => {
expendTableRef.value.toggleRowExpansion(t);
})
}, 200)
}
// 获取模型配置信息
const getModelConfig = (mGuid) => {
return getModelDemand({ guid: mGuid }).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || [];
demandTableList.value = data.pricingDemandMenuRSVOS || [];
pricingTargetList.value = data.pricingTargetRSVOS || [];
demandTableFieldAllNum.value = data.fieldCount || 0;
}
})
}
// 获取模型详情
const getModelDetail = (mGuid) => {
return getConfigureDetail({ guid: mGuid }).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || [];
modelData.value = data;
const pricingDimensionality = data.pricingDimensionalityRSVOS || [];
let tData: any = [];
pricingDimensionality.map(p => {
p.pricingTargetRSVOS.map(t => {
tData.push({ ...p, ...t })
})
})
pricingDimensionalityData.value = tData;
}
})
}
// 获取资源详情
const getResourceDetail = (sGuid, toPromise = true) => {
const detailData = getRegisterCatalogDetail({ guid: sGuid }).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || {};
baseConfigFormItems.value.map(item => {
if (item.field == 'belongingEntityGuid') {
item.default = data.rightMainName || '';
} else if (item.field == 'belongingTheme') {
item.default = data.subjectDomain || '';
}
})
const damCatalogTableInfo = data.damCatalogTableInfo || [];
const damOptions = damCatalogTableInfo.map(item => {
return {
...item,
label: item.tableName,
value: item.guid
}
})
tableData.value.map((item, i) => {
item.damDataTable = JSON.parse(JSON.stringify(damOptions));
if (guid && sGuid == flowDetail.value.dataResourceGuid) {
const sData = flowDetail.value.dataPricingDemandmatchingRQVOS?.find(s => s.demandTableGuid == item.demandTableGuid);
if (sData) {
item.dataTableGuid = sData.dataTableGuid;
item.dataFields.map(f => {
const fData = sData.pricingDemandFieldRQVOS.find(t => t.guid == f.guid);
f.enName = fData?.enName || '';
f.chName = fData?.chName || '';
});
item.tableDescription = sData.tableDescription || damOptions.find(t => t.guid == sData.dataTableGuid)?.tableDescription || '';
item.dataFieldsNum = item.dataFields.filter(item => item.chName != '' && item.chName != null).length;
resourceTableFieldAllNum.value = tableData.value.reduce((accumulator, currentValue) => {
return accumulator + Number(currentValue.dataFieldsNum);
}, 0);
}
} else {
item.dataTableGuid = '';
item.dataFields.map(f => { f.enName = ''; f.chName = '' });
item.dataFieldsNum = 0;
item.tableDescription = '';
resourceTableFieldAllNum.value = 0;
}
const dGuid = item.dataTableGuid;
const rIndex = i;
(() => {
!toPromise && dGuid && setTableRowData(dGuid, rIndex)
})()
})
resourceTableAllNum.value = tableData.value.filter(item => item.dataTableGuid != '' && item.dataTableGuid != null).length;
}
});
if (toPromise) {
return detailData;
} else {
(() => detailData)()
}
}
// 获取质量模型评分
const getQuilityModelScore = (sGuid) => {
return getModelScore({ damGuid: sGuid }).then((res: any) => {
if (res.code === proxy.$passCode) {
const data = res.data || {};
qualityScoreData.value = data;
} else {
proxy.$ElMessage.error(res.msg);
}
})
}
// 獲取模型相关信息
const getModelInfo = (mGuid) => {
const promises: any = [
getModelConfig(mGuid),
getModelDetail(mGuid)
];
try {
loading.value = true;
Promise.all(promises).then(res => {
loading.value = false;
setFormItemData();
if (guid && mGuid == flowDetail.value.modelGuid) {
dataTransactionPrice.value = flowDetail.value.dataTransactionPrice;
setTimeout(() => {
getResourceDetail(flowDetail.value.dataResourceGuid, false);
setFormItems();
setdemandTableData(mGuid);
}, 200);
} else {
setdemandTableData(mGuid);
}
});
} catch (e) {
loading.value = false;
}
}
// 获取数据资源管理信息
const getResourceInfo = (sGuid) => {
const promises: any = [getResourceDetail(sGuid), getQuilityModelScore(sGuid)];
try {
loading.value = true;
Promise.all(promises).then(res => {
loading.value = false;
});
} catch (e) {
loading.value = false;
}
}
const setTableRowData = (dGuid, rIndex) => {
let rowData = tableData.value[rIndex];
if (guid && dGuid == rowData.dataTableGuid) {
const sourceTableField = flowDetail.value.dataPricingDemandmatchingRQVOS?.find(s => dGuid == s.dataTableGuid);
const pricingDemandField = sourceTableField?.pricingDemandFieldRQVOS || [];
rowData.dataFields.map(f => {
f.chName = pricingDemandField.find(s => f.guid == s.guid)?.chName || ''
})
} else {
rowData.dataFields.map(f => f.chName = '')
}
const damData = rowData.damDataTable.find(item => item.guid == dGuid);
rowData.tableDescription = damData?.tableDescription || '';
rowData.dataFieldsNum = rowData.dataFields.filter(item => item.chName != '' && item.chName != null).length;
resourceTableFieldAllNum.value = tableData.value.reduce((accumulator, currentValue) => {
return accumulator + Number(currentValue.dataFieldsNum);
}, 0);
resourceTableAllNum.value = tableData.value.filter(item => item.dataTableGuid != '' && item.dataTableGuid != null).length;
if (dGuid) {
tableLoading.value = true;
getRegisterCatalogTableDetail(dGuid).then((res: any) => {
tableLoading.value = false;
if (res.code == proxy.$passCode) {
const data = res.data || {};
const damTableField = data.damCatalogTableField || [];
const damFieldOptions = damTableField.map(d => {
return {
...d,
label: d.chName || '',
value: d.chName || ''
}
})
rowData.dataFields.map(t => {
t.damFieldTable = JSON.parse(JSON.stringify(damFieldOptions));
})
// console.log('rowData', rowData)
} else {
proxy.$ElMessage.error(res.msg);
}
}).catch(() => {
tableLoading.value = false;
})
}
}
const changeDatasource = () => {
baseConfigFormItems.value.map(item => {
if (item.field == 'belongingEntityGuid') {
item.default = '';
} else if (item.field == 'belongingTheme') {
item.default = '';
}
})
}
const cascaderChange = (val) => {
disScore.value = [];
if (val) {
const baseConfigFormObj = baseConfigFormRef.value;
const baseConfigFormInfo = baseConfigFormObj.formInline;
const parentsData = baseConfigFormObj.getCascaderCheckedData();
const diseaseName = parentsData[0]?.label || '';
const modelGuid = baseConfigFormInfo.modelGuid;
// 获取疾病得分
getTargetNum({ diseaseName, guid: modelGuid });
}
}
const selectChange = async (val, row, info) => {
dataTransactionPrice.value = '';
if (row.field == 'modelGuid') {
tableData.value = [];
demandTableFieldAllNum.value = 0;
resourceTableAllNum.value = 0;
resourceTableFieldAllNum.value = 0;
await setFormItems(info);
val && getModelInfo(val);
qualityScoreData.value = {};
baseConfigFormItems.value[1].default = '';
changeDatasource();
} else if (row.field == 'dataResourceGuid') {
await setFormItems(info);
qualityScoreData.value = {};
resourceTableAllNum.value = 0;
resourceTableFieldAllNum.value = 0;
if (val) {
getResourceInfo(val);
} else {
changeDatasource();
}
} else if (row.field == 'dataTableGuid') {
setTableRowData(val, info.$index)
} else if (row.field == 'chName') {
let tData = info.row;
if (val) {
const damData = tData.dataFields[row.index].damFieldTable.find(item => item.chName == val);
tData.dataFields[row.index].enName = damData?.enName || '';
} else {
tData.dataFields[row.index].enName = '';
}
tData.dataFieldsNum = tData.dataFields.filter(item => item.chName != '' && item.chName != null).length;
resourceTableFieldAllNum.value = tableData.value.reduce((accumulator, currentValue) => {
return accumulator + Number(currentValue.dataFieldsNum);
}, 0);
} else {
setFormItems(info);
}
}
// 获取需求表字段
const getDemandField = (rGuid, rIndex) => {
getDemandList({
pageSize: -1,
pageIndex: 1,
relationMenuGuid: rGuid,
bizState: 'Y'
}).then((res: any) => {
tableLoading.value = false;
if (res.code == proxy.$passCode) {
const data = res.data || {};
const fData = data.records || [];
const tFields = tableData.value[rIndex].dataFields;
const tData = fData.map(item => {
const iData = tFields.find(t => t.demandFieldGuid == item.guid) || {};
return {
...item,
fieldName: item.fieldName,
isRequired: item.isRequired,
chName: item.chName || '',
enName: item.enName || '',
...iData
}
});
tableData.value[rIndex].dataFields = tData;
} else {
proxy.$ElMessage.error(res.msg);
}
}).catch(() => {
tableLoading.value = false;
})
}
const toPath = () => {
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
assetStore.set(true);
router.push({
name: 'priceCalculate',
})
}
// 获取维度公式计算结果
const getSignatory = (row) => {
let formulaVal = 0;
const pricingTargetData = row.pricingTargetRSVOS || [];
if (!row.computationalFormula || row.computationalFormula == 'custom') {
let formula = row.customize;
// 遍历数组,检查 customize 是否包含对应的 targetName,若包含则替换为 tNum
pricingTargetData.forEach((item) => {
if (formula.includes(item.targetName)) {
formula = formula.replace(new RegExp(item.targetName, 'g'), item.tNum);
}
});
// 使用 eval 计算公式结果(注意:eval 存在安全风险,仅适用于受控环境)
try {
formulaVal = eval(formula);
} catch (error) {
console.error('公式计算错误:', error);
}
} else {
const formula = pricingTargetData.map(item => item.tNum);
if (row.computationalFormula == '3') {
formulaVal = formula.reduce((accumulator, currentValue) => parseFloat(accumulator) * parseFloat(currentValue), 1); // 初始值为1
} else {
formulaVal = formula.reduce((accumulator, currentValue) => parseFloat(accumulator) + parseFloat(currentValue), 0); // 初始值为0
}
}
return (Math.round(formulaVal * 100) / 100).toFixed(2);
};
const getTargetNum = (params) => {
// loading.value = true;
getPriceResult(params).then((res: any) => {
// loading.value = false;
if (res.code === proxy.$passCode) {
const data = res.data || [];
disScore.value = data;
} else {
proxy.$ElMessage.error(res.msg);
}
}).catch(() => {
// loading.value = false;
});
}
// 生成报告内容
const reporting = (formInfo) => {
let resultInfo: any = [];
const signatoryData = JSON.parse(JSON.stringify(modelData.value.pricingDimensionalityRSVOS || '[]'));
signatoryData.map((sign, s) => {
resultInfo.push({
dimensionalityName: sign.dimensionalityName,
computationalFormula: sign.computationalFormula,
customize: sign.customize,
pricingTargetRSVOS: []
});
const targets = sign.pricingTargetRSVOS || [];
const signTargets = targets.map(t => {
let tNum: any = 0, tCustomize = '';
if (t.targetType == '3') { // 指标类型-数据字典
const tName = dictionaryData.value.find(d => d.guid == t.guid) ? `dict_${t.guid}` : '';
if (tName) {
const pVal = typeMap.value[tName].find(t => t.value == formInfo[tName]);
const dictionary = t.dictionaryJson.find(d => d.name == pVal.label);
if (sign.computationalFormula == '1') {// 加权平均
tNum = parseFloat(t.weight) / 100 * parseFloat(dictionary?.value || t.defaultValue || 0);
tCustomize = `权重${parseFloat(t.weight) / 100} * 因子/默认值${parseFloat(dictionary?.value || t.defaultValue || 0)}`;
} else { // 其他
tNum = parseFloat(dictionary?.value || t.defaultValue || 0);
tCustomize = `默认值${parseFloat(dictionary?.value || t.defaultValue || 0)}`;
}
t.dictionaryName == '数据用途' && (dataUsage.value = pVal.value || '');
}
} else if (t.targetType == '2') {// 指标类型-系统功能
if (t.functionName == '1') { // 功能名称-质量评价模型
const score = parseFloat(qualityScoreData.value.qualityScore || 0);
tNum = parseFloat(t.weight || 1) / 100 * score;
tCustomize = `权重${parseFloat(t.weight) / 100} * 模型评分${score}`;
} else if (t.functionName == '2') { // 功能名称-疾病管理
if (sign.computationalFormula == '1') {// 加权平均
const score = parseFloat(disScore.value.find(d => d.guid == t.guid)?.factor || 0);
tNum = parseFloat(t.weight) / 100 * score;
tCustomize = `权重${parseFloat(t.weight) / 100} * 疾病得分${score}`;
} else { //其他
tNum = parseFloat(disScore.value.find(d => d.guid == t.guid)?.factor || 0);
tCustomize = `疾病得分${tNum}`;
}
} else if (t.functionName == '3') {// 功能名称-需求表管理
const tData = tableData.value.find(f => f.demandTableGuid == t.demandTableGuid || f.guid == t.demandTableGuid);
if (tData) {
if (sign.computationalFormula == '1') {// 加权平均
tNum = parseFloat(t.weight) / 100 * (parseFloat(tData.dataFieldsNum) / tData.dataFields.length || parseFloat(t.defaultValue || 0));
tCustomize = `权重${parseFloat(t.weight) / 100} * 匹配率/默认值${parseFloat(tData.dataFieldsNum) / tData.dataFields.length || parseFloat(t.defaultValue || 0)}`;
} else { //其他
tNum = parseFloat(tData.dataFieldsNum) / tData.dataFields.length || parseFloat(t.defaultValue || 0);
tCustomize = `匹配率/默认值${parseFloat(tData.dataFieldsNum) / tData.dataFields.length || parseFloat(t.defaultValue || 0)}`;
}
}
}
} else { // 指标类型-系统内置
if (sign.computationalFormula == '1') {// 加权平均
tNum = parseFloat(t.weight) / 100 * parseFloat(t.defaultValue || 0);
tCustomize = `权重${parseFloat(t.weight) / 100} * 默认值${parseFloat(t.defaultValue || 0)}`;
} else { //其他
tNum = parseFloat(t.defaultValue || 0);
tCustomize = `默认值${parseFloat(t.defaultValue || 0)}`;
}
}
t.tNum = (Math.round(parseFloat(tNum) * 100) / 100).toFixed(2);
resultInfo[s].pricingTargetRSVOS.push({
targetName: t.targetName,
targetType: t.targetType,
functionName: t.functionName,
customize: tCustomize,
tNum: t.tNum,
})
return t;
})
sign.pricingTargetRSVOS = signTargets;
sign.sNum = getSignatory(sign);
resultInfo[s].sNum = sign.sNum;
})
// exportData.value = resultInfo;
return { signatoryData, resultInfo };
}
// 计算价格
const calculatePrice = (pData) => {
let modelFormula = modelData.value.modelFormula;
// 1. 移除所有干扰的引号(确保是数学表达式)
modelFormula = modelFormula.replace(/["']/g, "");
// 1. 提取变量名(中文、英文、数字、下划线)
const variableRegex = /[\u4e00-\u9fa5a-zA-Z_][\u4e00-\u9fa5a-zA-Z0-9_]*/g;
const variableNames = modelFormula.match(variableRegex) || [];
// 2. 去重
const uniqueVariables = [...new Set(variableNames)];
// 3. 构建变量映射 { 销售额: 2000, 成本: 500.5, ... }
const variables = {};
uniqueVariables.forEach(name => {
const dim = pData.find(d => d.dimensionalityName === name);
variables[name] = dim ? parseFloat(dim.sNum) : 0; // 找不到则默认为 0
});
// 4. 替换变量为数值(不加引号,确保是数字运算)
let expression = modelFormula;
uniqueVariables.forEach(name => {
expression = expression.replace(new RegExp(name, 'g'), variables[name]);
});
// 5. 安全计算(推荐 math.js,或 new Function)
try {
//如果用 eval,确保表达式格式正确
const resultNum = eval(expression);
dataTransactionPrice.value = (Math.round(parseFloat(resultNum) * 100) / 100).toFixed(2);
} catch (error) {
console.error('公式计算错误:', error);
return NaN;
}
};
// 计算结果和提交
const checkForm = (type) => {
const baseConfigFormObj = baseConfigFormRef.value;
const baseConfigFormEl = baseConfigFormObj.ruleFormRef;
const baseConfigFormInfo = baseConfigFormObj.formInline;
baseConfigFormEl.validate((valid, errorItem) => {
if (valid) {
if (type == 'calculate') {
const { signatoryData, resultInfo } = reporting(baseConfigFormInfo);
exportData.value = resultInfo;
calculatePrice(signatoryData);
} else if (type == 'export') {
const { signatoryData, resultInfo } = reporting(baseConfigFormInfo);
exportData.value = resultInfo;
!dataTransactionPrice.value && calculatePrice(signatoryData);
loading.value = true;
let exportOut: any = {};
// 估值对象信息
const damName = typeMap.value.dataResourceGuid.find(f => f.damGuid == baseConfigFormInfo.dataResourceGuid)?.damName || '';
exportOut.one = `${baseConfigFormInfo.belongingEntityGuid}拟了解其所持有的\"${damName}\"相关数据资源的价格,为此需对该行为涉及的数据资源在不同应用场景下,基于数据资源持有单位的性质、信息化程度、数据稀缺性、需求匹配等情况下,为上述经济行为提供定价参考依据。`;
exportOut.two = `估值对象:${baseConfigFormInfo.belongingEntityGuid}持有的\"${damName}\"`;
// 估值范围信息
const damNames = demandTableList.value.map(item => item.menuName)
let rangStr = `包含${damNames.join('、')}等${damNames.length}张表单,${damNames.length}张表共计${demandTableFieldAllNum.value}个字段`;
const dataTimeliness = pricingTargetList.value.find(p => p.dictionaryName == '时效性');
const dataTimelinessStr = dataTimeliness ? typeMap.value[`dict_${dataTimeliness.guid}`].find(f => f.value == baseConfigFormInfo[`dict_${dataTimeliness.guid}`])?.label || '' : '';
rangStr += dataTimelinessStr ? `,时间跨度为${dataTimelinessStr}的数据` : `的数据`;
damNames.length && (exportOut.two = `${exportOut.two}\n估值范围:${rangStr}`);
// 字典
let dictList: any = [], hasModelScore = false;
const dictStr = exportData.value.map(e => {
// 检查是否有质量模型评分
hasModelScore = hasModelScore || e.pricingTargetRSVOS.some(
t => t.targetType === '2' && t.functionName === '1'
);
// 只有当维度指标数大于1时才处理明细
if (e.pricingTargetRSVOS.length > 1) {
const targetStr = e.pricingTargetRSVOS
.map(t => `${t.targetName}为${changeNum(t.tNum, 2)}`)
.join('、');
dictList.push(`${e.dimensionalityName}为${changeNum(e.sNum, 2)},其中${targetStr}`);
}
return `${e.dimensionalityName}为${changeNum(e.sNum, 2)}`;
})
let dictListStr = `${dictStr.join(',')}。\n${dictList.join(';\n')}`
// 质量模型
if (hasModelScore) {
const { largeCategoryScoreList = [], qualityScore = 0 } = qualityScoreData.value;
const qualityParts = [
`数据的总体质量得分为${changeNum(qualityScore, 2)}`
];
if (largeCategoryScoreList.length) {
const categoryScores = largeCategoryScoreList.map(
q => `${q.largeCategoryName}方面得分为${changeNum(q.largeCategoryScore || 0, 2)}`
);
qualityParts.push(`其中${categoryScores.join(',')}`);
}
dictListStr += `;\n${qualityParts.join('。')}`;
}
exportOut.three = `${baseConfigFormInfo.belongingEntityGuid}持有的"${damName}"的数据(患者人次)单价为${changeNum(dataTransactionPrice.value, 2)}元${dictListStr ? `;其中${dictListStr}` : '。'}`;
exportModelScore(exportOut).then((res: any) => {
loading.value = false;
if (res && !res.msg) {
ElMessage({
type: "success",
message: '下载报告成功',
});
download(res, `数据定价报告.doc`, 'word');
} else {
res?.msg && ElMessage.error(res?.msg);
}
}).catch(() => {
loading.value = false;
ElMessage({
type: "error",
message: '下载报告请求失败',
});
})
} else {
const { signatoryData, resultInfo } = reporting(baseConfigFormInfo);
exportData.value = resultInfo;
!dataTransactionPrice.value && calculatePrice(signatoryData);
const modelName = typeMap.value.modelGuid.find(d => d.guid == baseConfigFormInfo.modelGuid)?.modelName || '';
const dataResourceName = typeMap.value.dataResourceGuid.find(d => d.damGuid == baseConfigFormInfo.dataResourceGuid)?.damName || '';
const diseaseGuid = baseConfigFormInfo.diseaseGuid || '';
let params: any = {
tenantGuid: userData.tenantGuid,
dataTransactionPrice: dataTransactionPrice.value,
modelGuid: baseConfigFormInfo.modelGuid,
modelName,
dataResourceGuid: baseConfigFormInfo.dataResourceGuid,
dataResourceName,
belongingEntityGuid: baseConfigFormInfo.belongingEntityGuid,
belongingTheme: baseConfigFormInfo.belongingTheme,
diseaseGuid,
diseaseName: '',
dataUsage: dataUsage.value
};
if (diseaseGuid) {
const parentsData = baseConfigFormObj.getCascaderCheckedData();
params.diseaseName = parentsData[0]?.label || '';
}
let dictionaryJson = {};
for (var b in baseConfigFormInfo) {
if (b.indexOf('dict_') > -1) {
dictionaryJson[b] = baseConfigFormInfo[b];
}
}
params.dictionaryJson = Object.keys(dictionaryJson).length ? JSON.stringify(dictionaryJson) : '';
let demandMatchingData: any = [];
tableData.value.map(item => {
demandMatchingData.push({
demandTableName: item.demandTableName,
demandTableGuid: item.demandTableGuid || item.guid, // 需求表guid
dataTableGuid: item.dataTableGuid, // 数据资源表guid
weightDemandTable: item.weightDemandTable,
dataFieldsNum: item.dataFieldsNum,
pricingDemandFieldRQVOS: item.dataFields.map(d => {
return {
demandFieldGuid: d.demandFieldGuid || d.guid, // 资源表字段guid
fieldName: d.fieldName,
enName: d.enName,
chName: d.chName,
isRequired: d.isRequired
}
})
})
});
params.dataPricingDemandmatchingRQVOS = demandMatchingData;
guid && (params.guid = guid);
loading.value = true;
savePrice(params).then((res: any) => {
loading.value = false;
if (res.code == proxy.$passCode) {
ElMessage({
type: "success",
message: guid ? '编辑数据定价成功' : '新增数据定价成功',
});
toPath()
} else {
proxy.$ElMessage.error(res.msg);
}
}).catch(() => {
loading.value = false;
});
}
} else {
expand1.value = true;
var obj = Object.keys(errorItem);
baseConfigFormEl.scrollToField(obj[0]);
}
})
}
const btnClick = async (btn, row: any = null) => {
const type = btn.value;
if (type == 'dim') {
baseConfigFormItems.value.at(-1).default += btn.name;
} else if (type == 'del-signatory') {
open('确定要删除该条维度数据吗?', 'warning');
} else if (type == 'expend') {
expendTableRef.value.toggleRowExpansion(row);
} else if (type == 'calculate' || type == 'submit') {
if (type == 'submit') {
ElMessageBox.confirm(dataTransactionPrice.value === '' ? '是否直接计算价格并提交' : '请确认当前数据交易价格是否为最新计算结果', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
checkForm(type);
}).catch(() => {
ElMessage.info('已取消提交操作');
});
} else {
checkForm(type);
}
} else if (type == 'export') {
ElMessageBox.confirm(dataTransactionPrice.value === '' ? '是否直接计算价格并下载' : '请确认当前数据交易价格是否为最新计算结果', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
checkForm(type);
}).catch(() => {
ElMessage.info('已取消下载操作');
});
} else if (type == 'cancel') {
ElMessageBox.confirm(
"当前页面尚未保存,确定关闭吗?",
"提示",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}
).then(() => {
toPath()
}).catch(() => {
ElMessage({
type: "info",
message: "已取消",
});
});
}
}
onActivated(() => {
let tab: any = userStore.tabbar.find((tab: any) => tab.fullPath === router.currentRoute.value.fullPath);
if (tab) {
switch (route.query.type) {
case 'create':
tab.meta.title = `新增数据定价`;
break;
case 'edit':
tab.meta.title = `编辑-${priceName}`;
break;
case 'detail':
tab.meta.title = `详情-${priceName}`;
break;
}
}
getModel()
})
onBeforeMount(() => {
if (guid) {
getDetail();
} else {
getDataTypeList();
getModel()
}
})
onMounted(() => {
})
</script>
<template>
<div class="container_wrap full" v-loading="loading">
<div class="content_main panel">
<ContentWrap id="contract-content-wrap" title="输入参数" expandSwicth style="margin-top: 15px" :isExpand="expand1"
@expand="(v) => expand1 = v">
<Form ref="baseConfigFormRef" formId="contract-content-form" :itemList="baseConfigForm.items"
:rules="baseConfigForm.rules" col="col3" @selectChange="selectChange" @cascaderChange="cascaderChange" />
</ContentWrap>
<ContentWrap id="contract-signatory-wrap" title="需求匹配" expandSwicth style="margin-top: 15px" :isExpand="expand2"
@expand="(v) => expand2 = v">
<div class="table_panel_wrap">
<div class="table_tool">
<div class="tool_title">
<div class="title_desc">
<span>需求表数量:</span>
<span class="text-num">{{ demandTableList.length }}</span>
<span>张,字段数:</span>
<span class="text-num">{{ demandTableFieldAllNum }}</span>
<span>匹配表数量:</span>
<span class="text-num">{{ resourceTableAllNum }}</span>
<span>张,字段数:</span>
<span class="text-num">{{ resourceTableFieldAllNum }}</span>
</div>
</div>
</div>
<div class="table_panel" v-loading="tableLoading">
<el-table ref="expendTableRef" border :data="tableData" row-key="demandTableName" tooltip-effect="light"
style="height: 100%;">
<el-table-column type="expand">
<template #default="props">
<div class="expand_panel">
<div class="table_tool">
<div class="tool_title">
<div class="title_desc">
<span>需求字段数:</span>
<span class="text-num">{{ props.row.dataFields.length }}</span>
<span>个,匹配字段数:</span>
<span class="text-num">{{ props.row.dataFieldsNum }}</span>
</div>
</div>
</div>
<el-table :data="props.row.dataFields" border>
<el-table-column label="序号" type="index" width="56" align="center" />
<el-table-column label="需求字段中文" prop="fieldName" class-name="edit-col">
<template #default="scope">
<el-input v-model.trim="scope.row.fieldName" placeholder="请输入" disabled />
</template>
</el-table-column>
<el-table-column label="匹配字段中文" prop="chName" class-name="edit-col">
<template #default="scope">
<el-select v-model="scope.row.chName" clearable filterable
@change="val => selectChange(val, { field: 'chName', index: scope.$index }, props)">
<el-option v-for="(opt, o) in scope.row.damFieldTable" :label="opt.label" :value="opt.value"
:key="o" />
</el-select>
</template>
</el-table-column>
<el-table-column label="匹配字段英文" prop="enName" class-name="edit-col">
<template #default="scope">
<el-input v-model.trim="scope.row.enName" placeholder="请输入" disabled />
</template>
</el-table-column>
<el-table-column label="是否必需字段" prop="isRequired" class-name="edit-col">
<template #default="scope">
<el-select v-model="scope.row.isRequired" disabled>
<el-option label="是" value="Y" />
<el-option label="否" value="N" />
</el-select>
</template>
</el-table-column>
</el-table>
</div>
</template>
</el-table-column>
<el-table-column label="序号" type="index" width="56" align="center" />
<el-table-column v-for="item in tableFields" :key="item.field" :label="item.label" :prop="item.field"
:width="item.width" :align="item.align" class-name="edit-col">
<template #default="scope">
<el-select v-if="item.type == 'select'" v-model="scope.row[item.field]" clearable filterable
@change="val => selectChange(val, item, scope)">
<el-option v-for="(opt, o) in scope.row.damDataTable" :label="opt.label" :value="opt.value"
:key="o" />
</el-select>
<el-input v-else v-model.trim="scope.row[item.field]" :disabled="item.disabled" placeholder="请输入"
clearable />
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="100">
<template #default="scope">
<el-button type="primary" link @click="btnClick({ value: 'expend' }, scope.row)">字段映射</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</ContentWrap>
<ContentWrap id="contract-content-wrap" title="输出结构" expandSwicth style="margin-top: 15px" :isExpand="expand3"
@expand="(v) => expand3 = v">
<el-form class="result-form">
<el-form-item class="flex-column" label="数据交易价格(元)">
<el-input v-model="dataTransactionPrice" placeholder="" disabled style="display: none;" />
<div class="result-price">{{ changeNum(dataTransactionPrice, 2) }}</div>
</el-form-item>
<el-form-item class="align-end" style="margin-bottom: 14px;">
<el-button type="primary" @click="btnClick({ value: 'calculate' })">开始计算</el-button>
<el-button @click="btnClick({ value: 'export' })">下载报告</el-button>
<span style="margin-left: 8px">输出结果报告查看</span>
</el-form-item>
</el-form>
</ContentWrap>
</div>
<div class="tool_btns">
<div class="btns">
<el-button @click="btnClick({ value: 'cancel' })">取消</el-button>
<el-button type="primary" @click="btnClick({ value: 'submit' })">提交</el-button>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.container_wrap {
overflow: hidden;
.content_main {
height: calc(100% - 45px);
overflow: hidden auto;
&.panel {
padding: 0 16px 16px;
}
:deep(.el-card) {
&#contract-signatory-wrap {
.card-body-content {
padding: 8px 16px;
}
}
}
.signatory-tags {
margin-bottom: 11px;
}
.table_panel_wrap {
margin-bottom: 4px;
.table_tool {
height: 36px;
display: flex;
justify-content: space-between;
align-items: center;
.tool_title {
width: 100%;
display: flex;
justify-content: start;
}
.title_desc {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
.text-num {
color: var(--el-color-primary);
margin: 0 8px;
}
}
}
.table_panel {
margin-bottom: 4px;
height: 392px;
:deep(.el-table) {
.el-table__cell {
&.edit-col {
padding: 4px 0;
.cell {
padding: 0 4px;
.el-cascader {
width: 100%;
height: 28px;
}
.el-input {
height: 28px;
}
}
}
.expand-icon {
color: #888;
margin-right: 8px;
vertical-align: text-bottom;
cursor: pointer;
}
}
.el-input.is-disabled .el-input__wrapper {
background-color: var(--el-disabled-bg-color);
}
.el-select__wrapper.is-disabled {
background-color: var(--el-disabled-bg-color);
}
}
.expand_panel {
padding: 6px;
margin: -6px 0;
background: #fff;
}
}
}
}
.btn-block {
width: 100%;
margin: 16px 0 8px;
}
.tool_btns {
height: 44px;
margin: 0 -8px;
display: flex;
justify-content: center;
align-items: center;
border-top: 1px solid #d9d9d9;
}
}
:deep(.el-form) {
&.result-form {
display: flex;
.el-form-item {
&.flex-column {
width: calc(33.33% - 6px);
margin-right: 8px;
display: flex;
flex-direction: column;
align-items: self-start;
.el-form-item__content {
width: 100%;
}
.result-price {
width: 100%;
height: 32px;
line-height: 32px;
padding: 1px 11px;
border-radius: 4px;
cursor: not-allowed;
color: var(--el-disabled-text-color);
background-color: var(--el-disabled-bg-color);
box-shadow: 0 0 0 1px var(--el-disabled-border-color) inset;
}
}
&.align-end {
align-self: flex-end;
}
}
}
.el-select__wrapper.is-disabled {
background-color: var(--el-disabled-bg-color);
}
}
</style>
<route lang="yaml">
name: demandManage
</route>
<script lang="ts" setup name="demandManage">
import { ref } from 'vue';
import { TableColumnWidth, commonPageConfig } from '@/utils/enum';
import { ElMessage, ElMessageBox } from "element-plus";
import { CirclePlus, Search } from "@element-plus/icons-vue";
import { useRouter, useRoute } from "vue-router";
import { changeNum, getCurrentTime } from '@/utils/common';
import useUserStore from "@/store/modules/user";
import useCatchStore from "@/store/modules/catch";
import {
getDemandAll,
saveDemandTree,
updateDemandTree,
deleteDemandTree,
getDemandList,
saveDemand,
updateDemand,
deleteDemand,
} from '@/api/modules/dataPricing';
const router = useRouter();
const userStore = useUserStore()
const userData = JSON.parse(userStore.userData)
const { proxy } = getCurrentInstance() as any;
const cacheStore = useCatchStore()
/** 左侧标签列表管理 */
const treeInfoRef = ref();
const treeData = ref([]);
const treeInfo = ref({
id: "demand-tree",
filter: true,
editTreeItem: true,
queryValue: "",
loading: false,
className: 'tree-list-manage',
queryPlaceholder: "输入关键字搜索",
props: {
label: "menuName",
value: "guid",
children: 'childList',
},
prefix: {
type: 'prefixIcon'
},
lazy: false,
nodeKey: 'guid',
expendAll: true,
currentNodeKey: '',
expandOnNodeClick: false,
data: [],
});
const catalogData = ref([]);
const searchItemList = ref([
{
type: "select",
label: "",
field: "isRequired",
placeholder: "是否必选",
default: "",
options: [
{ label: '是', value: 'Y' },
{ label: '否', value: 'N' }
],
clearable: true,
style: {
width: '140px'
}
},
{
type: "input",
label: "",
field: "fieldName",
default: "",
placeholder: "字段名称",
clearable: true,
style: {
width: '230px'
}
},
]);
const lastClickNode: any = ref({});
const showTable = ref(false)
/** 记录正在提交审批流程的api,防止重复提交 */
const addDataPromise: any = ref();
const tableRef = ref();
const tableData: any = ref([]);
const selectRowData: any = ref([])
const currTableData: any = ref({});
const page: any = ref({
...commonPageConfig,
fieldName: '',
isRequired: '',
});
const tableFields: any = [
{ label: "字段名称", field: "fieldName" },
{ label: "是否必选", field: "isRequired", type: 'select' },
{ label: "排序", field: "orderNum", type: 'defaultValue' },
{ label: '启用状态', field: 'bizState', type: 'switch', activeText: '启用', inactiveText: '停用', activeValue: 'Y', inactiveValue: 'N' },
{ label: "创建时间", field: "createTime", type: 'defaultValue' },
]
const tableInfo: any = ref({
id: 'api-data-table',
rowKey: 'guid',
loading: false,
multiple: true,
fields: [
{ label: "字段名称", field: "fieldName", width: 200, align: "left", type: 'edit' },
{
label: "是否必选", field: "isRequired", width: 140, type: 'edit', getName: (scope) => {
return scope.row.isRequired == 'Y' ? '是' : '否'
}
},
{ label: "排序", field: "orderNum", width: 120, type: 'edit', dataTypeName: 'fieldType' },
{
label: '启用状态', field: 'bizState', type: 'edit', activeText: '启用', inactiveText: '停用', activeValue: 'Y', inactiveValue: 'N', switchWidth: 56, width: 96, align: 'center',
},
{ label: "创建时间", field: "createTime", width: TableColumnWidth.DATETIME, type: 'edit', dataTypeName: 'dateType' },
],
data: tableData.value,
editInfo: [],
page: {
type: "normal",
rows: 0,
...page.value,
},
actionInfo: {
label: "操作",
type: "btn",
isMore: false,
width: 120,
btns: (scope) => {
let btns: any[] = [];
if (scope.row.STATUS == 'edit') {
btns.push({
value: 'save', label: '保存', click: (scope) => {
if (addDataPromise.value) {
return;
}
// 调用新增数据的接口。
let params = { ...scope.row, tenantGuid: userData.tenantGuid };
params.relationMenuGuid = lastClickNode.value.guid;
delete params['ROWID'];
delete params['STATUS'];
delete params['STATE'];
delete params.dateType;
delete params.fieldType;
if (params.guid) {
addDataPromise.value = updateDemand(params).then((res: any) => {
addDataPromise.value = null;
if (res.code == proxy.$passCode) {
page.value.curr = 1;
getTableData();
proxy.$ElMessage.success('编辑数据保存成功');
} else {
proxy.$ElMessage.error(res.msg);
}
});
} else {
addDataPromise.value = saveDemand(params).then((res: any) => {
addDataPromise.value = null;
if (res.code == proxy.$passCode) {
page.value.curr = 1;
getTableData();
proxy.$ElMessage.success('新增数据保存成功');
} else {
proxy.$ElMessage.error(res.msg);
}
});
}
}
});
btns.push({
value: 'del', label: '删除', click: (scope) => {
proxy.$openMessageBox('确定要删除该条数据吗?', () => {
tableData.value.splice(0, 1);
proxy.$ElMessage.success("删除成功");
}, () => {
proxy.$ElMessage.info("已取消删除");
})
}
});
return btns;
}
btns.push({ value: 'edit', label: '编辑' });
btns.push({ value: 'del', label: '删除' });
return btns;
}
}
});
/** ---- 新增修改标签对话框配置------------- */
const formItems: any = ref([
{
label: '需求表名称',
type: 'input',
placeholder: '请输入',
field: 'menuName',
default: '',
maxlength: 20,
clearable: true,
required: true,
},
{
label: '是否为目录',
type: 'select',
field: 'isCatalog',
default: 'N',
options: [
{ label: '是', value: 'Y' },
{ label: '否', value: 'N' }
],
required: true,
},
{
label: '所属上级',
type: 'cascader',
placeholder: '请选择',
field: "parentGuid",
default: [],
options: [],
showAllLevels: false,
props: {
checkStrictly: true,
label: "menuName",
value: "guid",
children: 'childList',
emitPath: false
},
clearable: true,
filterable: true,
block: true
},
{
label: '排序',
type: 'input',
placeholder: '请输入',
field: 'orderNum',
default: '',
inputType: 'integerNumber',
maxlength: 6,
clearable: true,
},
])
const formRules = ref({
menuName: [
{ required: true, message: '请填写需求表名称', trigger: 'blur' }
],
isCatalog: [
{ required: true, message: '请选择是否为目录', trigger: 'change' }
]
});
/** 新建api标签对话框 */
const demScheduleLabelDialogInfo = ref({
visible: false,
size: 480,
direction: "column",
header: {
title: "",
},
type: '',
contents: [
{
type: 'form',
title: '',
formInfo: {
id: 'add-staff-form',
items: formItems.value,
rules: formRules.value
}
}
],
footer: {
btns: [
{ type: "default", label: "取消", value: "cancel" },
{ type: "primary", label: "确定", value: "submit" },
],
},
});
// 获取需求表树形数据
const getTreeData = () => {
getDemandAll({ isCatalog: '' }).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || [];
treeData.value = JSON.parse(JSON.stringify(data));
treeInfo.value.data = JSON.parse(JSON.stringify(data));
treeInfo.value.currentNodeKey = data[0]?.guid || "";
lastClickNode.value = data[0] || {};
nextTick(() => {
treeInfoRef.value?.setCurrentKey(treeInfo.value.currentNodeKey);
if (lastClickNode.value.isCatalog != 'Y') {
showTable.value = true;
page.value.curr = 1;
getTableData();
} else {
showTable.value = false;
}
})
} else {
proxy.$ElMessage.error(res.msg);
}
})
getCatalog();
}
const getCatalog = () => {
getDemandAll({ isCatalog: 'Y' }).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || [];
catalogData.value = JSON.parse(JSON.stringify(data));
}
})
}
/** 点击左侧节点 */
const nodeClick = (data) => {
lastClickNode.value = data || {};
if (data.isCatalog != 'Y') {
showTable.value = true;
page.value.curr = 1;
getTableData();
} else {
showTable.value = false;
}
}
const setRowData = (info: any = null) => {
const rowData: any = {
STATUS: 'edit',
STATE: "Running",
ROWID: '1',
dateType: 'datetime',
fieldType: 'int',
fieldName: info?.fieldName || '',
isRequired: info?.isRequired || 'N',
bizState: info?.bizState || 'Y',
createTime: info?.createTime || getCurrentTime(),
orderNum: info?.orderNum !== undefined ? info?.orderNum : tableData.value.length + 1
}
info && (rowData.guid = info.guid || '');
return rowData;
}
// 新增字段
const addField = () => {
if (tableData.value[0]?.STATUS == 'edit') {
proxy.$ElMessage.warning('请先保存正在编辑的行数据');
return;
}
// 支持新增数据,直接添加一行进行编辑。
const rowData = setRowData();
tableData.value.unshift(rowData);
nextTick(() => {
tableRef?.value?.tableRef?.setScrollTop(0);
})
}
// 批量删除
const batching = (type) => {
if (type == 'delete') {
if (selectRowData.value.length == 0) {
ElMessage({
type: 'error',
message: '请选择需要删除的数据',
})
return
}
open("此操作将永久删除, 是否继续?", "warning", true);
} else if (type == 'import') {
const info = {
dictionaryGuid: ''
}
cacheStore.setCatch('uploadSetting', info)
router.push(
{
name: 'importFileDemandManage',
query: {
isfileImport: '7'
}
}
);
}
}
const setFormItems = (info = null, type = '') => {
const datas: any = info || {};
formItems.value.map(item => {
if (item.field == 'isCatalog') {
item.default = datas[item.field] || 'N';
} else {
item.default = datas[item.field] || '';
}
})
}
const setCataLog = (dataArr, id, parentDisabled = false) => {
return dataArr.map(item => {
// 当前节点是否被禁选(自身匹配或父节点已禁选)
const isDisabled = parentDisabled || item.guid === id;
// 新节点基础属性
const newItem = {
...item,
disabled: isDisabled
};
// 递归处理子节点,并传递当前节点的禁选状态
if (item.childList && item.childList.length > 0) {
newItem.childList = setCataLog(item.childList, id, isDisabled);
}
return newItem;
})
}
/** 处理标签的操作按钮,编辑,删除 */
const handleTreeItemMenuClick = async (node, type) => {
let data = node.data;
lastClickNode.value = data;
if (type == "edit") {
await setFormItems(data);
if (data.isCatalog == 'Y') {
formItems.value[1].disabled = data.childList && data.childList.length > 0;
} else {
formItems.value[1].disabled = tableData.value && tableData.value.length > 0;
}
let cata = setCataLog(catalogData.value, data.guid);
formItems.value.at(-2).options = cata;
demScheduleLabelDialogInfo.value.contents[0].formInfo.items = formItems.value;
demScheduleLabelDialogInfo.value.header.title = "编辑需求表";
demScheduleLabelDialogInfo.value.type = 'edit';
demScheduleLabelDialogInfo.value.visible = true;
} else if (type == "delete") {
proxy.$openMessageBox('确定要删除该需求表吗?', () => {
deleteDemandTree([lastClickNode.value.guid]).then((res: any) => {
if (res.code == proxy.$passCode) {
getTreeData();
proxy.$ElMessage.success("删除该需求表成功");
} else {
proxy.$ElMessage.error(res.msg);
}
});
}, () => {
proxy.$ElMessage.info("已取消");
})
}
}
/** 新建分组对话框确定。 */
const demScheduleLabelDialogBtnClick = (btn, info) => {
if (btn.value == 'submit') {
let params = { ...info, parentGuid: info.parentGuid || '', tenantGuid: userData.tenantGuid };
if (demScheduleLabelDialogInfo.value.type == 'add') {
saveDemandTree(params).then((res: any) => {
if (res.code == proxy.$passCode) {
getTreeData();
proxy.$ElMessage({
type: 'success',
message: '新增需求表成功'
})
demScheduleLabelDialogInfo.value.visible = false;
} else {
proxy.$ElMessage({
type: 'error',
message: res.msg,
})
}
})
} else {
params.guid = lastClickNode.value.guid;
updateDemandTree(params).then((res: any) => {
if (res.code == proxy.$passCode) {
getTreeData();
proxy.$ElMessage({
type: 'success',
message: '编辑需求表成功'
})
demScheduleLabelDialogInfo.value.visible = false;
} else {
proxy.$ElMessage({
type: 'error',
message: res.msg,
})
}
})
}
} else if (btn.value == 'cancel') {
demScheduleLabelDialogInfo.value.visible = false;
}
};
const addTreeNode = async () => {
await setFormItems();
demScheduleLabelDialogInfo.value.header.title = '新建需求表';
formItems.value[1].disabled = false;
formItems.value.at(-2).options = catalogData.value;
demScheduleLabelDialogInfo.value.contents[0].formInfo.items = formItems.value;
demScheduleLabelDialogInfo.value.type = 'add';
demScheduleLabelDialogInfo.value.visible = true;
}
// 设置需求表表头
const setTableField = () => {
const editInfo = {};
tableFields.forEach(f => {
if (f.type == 'switch') {
editInfo[f.field] = {
label: f.label,
type: f.type,
field: f.field,
default: false,
activeText: '启用',
inactiveText: '停用',
activeValue: 'Y',
inactiveValue: 'N',
};
} else if (f.type == 'select') {
editInfo[f.field] = {
label: f.label,
type: f.type,
field: f.field,
default: '',
options: [
{ label: '是', value: 'Y' },
{ label: '否', value: 'N' }
],
clearable: true
}
} else if (f.type == 'defaultValue') {
editInfo[f.field] = {
label: f.label,
type: f.type,
field: f.field,
default: '',
clearable: true,
disabled: true,
}
} else {
editInfo[f.field] = {
label: f.label,
type: 'input',
field: f.field,
placeholder: f.isPrimary == 'Y' ? '主键自动生成' : '请输入',
default: '',
clearable: true
}
}
})
tableInfo.value.editInfo = editInfo;
}
const toSearch = (val: any = null, clear: boolean = false) => {
page.value.curr = 1;
if (clear) {
searchItemList.value.map((item) => (item.default = ""));
page.value.fieldName = '';
page.value.isRequired = '';
} else {
let info = val ? { ...val } : {};
searchItemList.value.map(item => {
info[item.field] = item.default;
})
page.value.fieldName = info.fieldName || '';
page.value.isRequired = info.isRequired || '';
}
getTableData();
};
// 获取表格数据
const getTableData = () => {
if (!lastClickNode.value?.guid) {
return;
}
tableInfo.value.loading = true;
getDemandList({
pageSize: page.value.limit,
pageIndex: page.value.curr,
relationMenuGuid: lastClickNode.value.guid,
fieldName: page.value.fieldName,
isRequired: page.value.isRequired,
}).then((res: any) => {
tableInfo.value.loading = false;
if (res.code == proxy.$passCode) {
const data = res.data || {};
tableData.value = data.records || [];
tableInfo.value.data = tableData.value;
tableInfo.value.page.limit = data.pageSize
tableInfo.value.page.curr = data.pageIndex
tableInfo.value.page.rows = data.totalRows
} else {
proxy.$ElMessage.error(res.msg);
}
})
}
const tablePageChange = (info) => {
page.value.curr = Number(info.curr);
page.value.limit = Number(info.limit);
getTableData();
};
const tableSelectionChange = (val) => {
selectRowData.value = val;
};
const tableSwitchBeforeChange = (scope, field, callBack) => {
if (scope.row.STATUS == 'edit') {
callBack(true);
} else {
// proxy.$openMessageBox(`确定要更新该字段值吗?`, () => {
// const state = scope.row[field] == 'Y' ? 'N' : 'Y';
// let mdmModelData: any = {};
// mdmModelData[field] = state;
// let pkValue = scope.row[mergeFormItems.value[0].field];
// mdmModelData[mergeFormItems.value[0].field] = pkValue;
// updateMdmModelData({
// mdmModelTableGuid: props.modelConfigInfo.guid,
// mdmModelData: mdmModelData,
// pkValues: [pkValue]
// }).then((res: any) => {
// if (res.code == proxy.$passCode) {
// proxy.$ElMessage.success('该字段值更新成功');
// let firstData = tableData.value[0];
// getMainModelData().then((res: any) => {
// if (firstData && firstData.STATUS == 'edit') {
// tableData.value.unshift(firstData);
// }
// });
// callBack(true)
// } else {
// proxy.$ElMessage.error(res.msg);
// callBack(false)
// }
// });
// }, () => {
// callBack(false);
// })
}
}
const tableSwitchChange = (val, scope, field) => {
if (scope.row.STATUS == 'edit') {
tableData.value[scope.$index][field] = val;
}
}
const tableBtnClick = (scope, btn) => {
const type = btn.value;
const row = scope.row;
currTableData.value = row;
if (type === 'edit') { // 编辑
if (tableData.value.find(t => t.STATUS == 'edit')) {
proxy.$ElMessage.warning('请先保存正在编辑的行数据');
return;
}
const rowData = setRowData(row);
tableData.value[scope.$index] = rowData;
} else if (type === 'del') { // 删除
open('确定要删除该条数据吗?', 'warning');
}
};
const open = (msg, type, isBatch = false) => {
ElMessageBox.confirm(msg, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: type,
}).then(() => {
let guids = [currTableData.value.guid]
if (isBatch) {
guids = selectRowData.value.map(item => item.guid);
}
deleteDemand(guids).then((res: any) => {
if (res.code == proxy.$passCode) {
getTableData();
ElMessage({
type: "success",
message: "删除成功",
});
} else {
ElMessage({
type: "error",
message: res.msg,
});
}
});
});
};
onBeforeMount(() => {
getTreeData();
setTableField();
});
onActivated(() => {
})
</script>
<template>
<div class="container_wrap full flex">
<div class="aside_wrap">
<div class="aside_title">需求表列表</div>
<el-icon class="icon-add" color="#4fa1a4" @click="addTreeNode()">
<CirclePlus />
</el-icon>
<Tree ref="treeInfoRef" :treeInfo="treeInfo" @nodeClick="nodeClick" @itemMenuClick="handleTreeItemMenuClick" />
</div>
<div class="main_wrap">
<template v-if="showTable">
<div class="table_tool_wrap">
<div class="tools_btns">
<el-button type="primary" @click="addField" v-preReClick>添加</el-button>
<el-button @click="batching('import')" v-preReClick>批量导入</el-button>
<el-button @click="batching('delete')" v-preReClick>批量删除</el-button>
</div>
<div class="tools_serach">
<template v-for="(item, i) in searchItemList" :key="'s_' + i">
<el-select v-if="item.type == 'select'" v-model="item.default" :placeholder="item.placeholder"
:clearable="item.clearable" :style="item.style" @change="val => toSearch()">
<el-option v-for="(opt, o) in item.options" :label="opt.label" :value="opt.value" :key="o" />
</el-select>
<el-input v-else v-model.trim="item.default" :placeholder="item.placeholder" :suffix-icon="Search"
:clearable="item.clearable" :style="item.style" @change="val => toSearch()" />
</template>
</div>
</div>
<div class="table_panel_wrap">
<Table ref="tableRef" :tableInfo="tableInfo" @tableSelectionChange="tableSelectionChange"
@tablePageChange="tablePageChange" @tableBtnClick="tableBtnClick" />
</div>
</template>
<div class="card-noData" v-else>
<img src="@/assets/images/no-data.png" :style="{ width: '96px', height: '96px' }" />
<p>请选择需求表</p>
</div>
</div>
<!-- 添加编辑需求表的对话框 -->
<Dialog :dialogInfo="demScheduleLabelDialogInfo" @btnClick="demScheduleLabelDialogBtnClick" />
</div>
</template>
<style lang="scss" scoped>
.container_wrap {
padding: 0;
display: flex;
justify-content: space-between;
.aside_wrap {
width: 199px;
border-right: 1px solid #d9d9d9;
box-shadow: none;
.aside_title {
width: calc(100% - 32px);
display: inline-block;
}
.icon-add.el-icon {
width: 24px;
height: 24px;
vertical-align: middle;
cursor: pointer;
svg {
width: 24px;
height: 24px;
}
}
:deep(.tree_panel) {
height: 100%;
padding-top: 0;
.el-tree {
margin: 0;
height: calc(100% - 68px);
overflow: hidden auto;
}
}
}
.main_wrap {
padding: 0 8px;
height: auto;
flex: 1;
.table_tool_wrap {
width: 100%;
padding: 0 8px;
display: flex;
justify-content: space-between;
align-items: center;
.tools_btns {
padding: 0px 0 0;
}
.tools_serach {
width: 378px;
display: flex;
justify-content: space-between;
}
}
.table_panel_wrap {
width: 100%;
height: calc(100% - 44px);
padding: 0px 8px 0;
}
.card-noData {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
}
}
}
.row-dialog-content {
height: 300px;
.tools_btns {
margin-bottom: 8px;
}
}
:deep(.el-form) {
.select-input-long {
.el-form-item:first-child {
width: 67%;
margin-right: 0px;
}
.el-form-item:last-child {
width: 33%;
margin-right: 0px;
.item-label {
white-space: pre;
.required_mark::after {
content: '';
}
}
}
}
.select-input-long-reverse {
.el-form-item:first-child {
width: 100px;
margin-right: 0px;
}
.el-form-item:last-child {
width: calc(100% - 100px);
margin-right: 0px;
.item-label {
white-space: pre;
.required_mark::after {
content: '';
}
}
}
}
}
</style>
<route lang="yaml">
name: diseaseManage
</route>
<script lang="ts" setup name="diseaseManage">
import { ref } from 'vue';
import TableTools from '@/components/Tools/table_tools.vue';
import { TableColumnWidth, commonPageConfig } from '@/utils/enum';
import { ElMessage, ElMessageBox } from "element-plus";
import { CirclePlus } from "@element-plus/icons-vue";
import { useRouter, useRoute } from "vue-router";
import useUserStore from "@/store/modules/user";
import useCatchStore from "@/store/modules/catch";
import {
getDiseaseAll,
getDiseaseList,
saveDisease,
updateDisease,
deleteDisease,
} from '@/api/modules/dataPricing';
import { changeNum } from '@/utils/common';
const router = useRouter();
const userStore = useUserStore()
const userData = JSON.parse(userStore.userData)
const { proxy } = getCurrentInstance() as any;
const cacheStore = useCatchStore()
const searchItemList = ref([
{
type: "input",
label: "",
field: "diseaseName",
default: "",
placeholder: "疾病名称",
clearable: true,
},
{
type: "select",
label: "",
field: "isChildrenDisease",
placeholder: "是否儿童",
default: "",
options: [
{ label: '是', value: 'Y' },
{ label: '否', value: 'N' }
],
clearable: true,
}
]);
const tableData: any = ref([]);
const selectRowData: any = ref([])
const currTableData: any = ref({});
const selectionTreeTableRef = ref();
const page: any = ref({
...commonPageConfig,
diseaseName: '',
isChildrenDisease: ''
});
const tableInfo = ref({
id: 'api-data-table',
rowKey: 'guid',
loading: false,
multiple: true,
fields: [
{ label: "疾病名称", field: "diseaseName", width: 200 },
{
label: "是否儿童疾病", field: "isChildrenDisease", width: 120, getName: (scope) => {
return scope.row.isChildrenDisease == 'Y' ? '是' : '否';
}
},
{ label: "发病率/患病率", field: "incidenceRate", width: 120 },
{
label: "因子", field: "factor", width: 100, align: 'right', getName: (scope) => {
return (scope.row.factor !== '' && scope.row.factor !== null) ? changeNum(scope.row.factor, 2) : '-';
}
},
{
label: "启用状态", field: "bizState", type: 'tag', width: 120, align: 'center', getName: (scope) => {
return scope.row.bizState == 'Y' ? '启用' : '停用';
}, tagType: (scope) => {
return scope.row.bizState == 'Y' ? 'success' : 'info';
}
},
{ label: "排序", field: "orderNum", width: 100, align: 'right', type: 'chnum' },
{ label: "创建时间", field: "createTime", width: TableColumnWidth.DATETIME },
],
data: tableData.value,
treeProps: {
children: 'childList',
hasChildren: 'hasChildren',
checkStrictly: false,
},
page: {
type: "normal",
rows: 0,
...page.value,
},
actionInfo: {
label: "操作",
type: "btn",
isMore: false,
width: 120,
btns: [
{ label: "编辑", value: "edit" },
{ label: "删除", value: "del" }
]
}
});
/** ---- 新增修改标签对话框配置------------- */
const catalogData = ref([]);
const formItems: any = ref([
{
label: '疾病名称',
type: 'input',
placeholder: '请输入',
field: 'diseaseName',
default: '',
maxlength: 50,
required: true,
clearable: true,
},
{
label: '所属上级',
type: 'cascader',
placeholder: '请选择',
field: "parentGuid",
default: [],
options: [],
showAllLevels: false,
props: {
checkStrictly: true,
label: "diseaseName",
children: 'childList',
value: "guid",
emitPath: false
},
clearable: true,
filterable: true,
},
{
label: '是否儿童疾病',
type: 'select',
placeholder: '请选择',
field: 'isChildrenDisease',
default: '',
options: [
{ label: '是', value: 'Y' },
{ label: '否', value: 'N' }
],
required: true,
clearable: true,
},
{
label: '启用状态',
type: 'switch',
field: 'bizState',
default: 'Y',
placeholder: '请选择',
activeValue: 'Y',
inactiveValue: 'N',
switchWidth: 32,
},
{
label: '发病率/患病率',
type: 'input',
placeholder: '请输入',
field: 'incidenceRate',
default: '',
maxlength: 20,
clearable: true,
required: true,
},
{
label: '因子',
type: 'input-group',
placeholder: '请输入',
field: 'in',
default: '',
children: [
{
label: '',
type: 'input',
placeholder: '请输入',
field: 'factor',
default: '',
inputType: 'factorNumber',
maxlength: 10,
clearable: true,
},
{
label: '',
type: 'checkbox',
placeholder: '下级同时修改',
desc: '',
field: 'juniorTogetherModify',
default: "N",
trueValue: 'Y',
falseValue: 'N',
}
],
},
{
label: '排序',
type: 'input',
placeholder: '由数字组成',
field: 'orderNum',
inputType: 'integerNumber',
default: '',
maxlength: 6,
clearable: true,
},
])
const formRules = ref({
diseaseName: [
{ required: true, message: '请填写疾病名称', trigger: 'blur' }
],
isChildrenDisease: [
{ required: true, message: '请选择是否儿童疾病', trigger: 'change' }
],
bizState: [
{ required: true, message: '请选择启用状态', trigger: 'change' }
],
incidenceRate: [
{ requires: true, message: '请填写发病率/患病率', trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => {
if (!value) {
callback(new Error("请填写发病率/患病率"));
} else {
if (drawerInfo.value.type.indexOf('edit') > -1 && currTableData.value.incidenceRate == value) {
callback();
return
}
let eMsg = '';
// 验证格式:数字、小数或分数
if (!/^[0-9]*\.?[0-9]*(\/[0-9]+\.?[0-9]*)?$/.test(value)) {
eMsg = '请输入有效的数字、小数或分数(如0.5或1/2)';
}
let numericValue;
// 处理分数
if (value.includes('/')) {
const parts = value.split('/');
if (parts.length !== 2) {
eMsg = '请输入有效的分数格式(如1/2)';
}
const numerator = parseFloat(parts[0]);
const denominator = parseFloat(parts[1]);
if (isNaN(numerator) || isNaN(denominator)) {
eMsg = '请输入有效的分数格式(如1/2)';
}
if (denominator === 0) {
eMsg = '请输入有效的分数格式(如1/2)';
}
numericValue = numerator / denominator;
} else {// 处理普通数字
numericValue = parseFloat(value);
if (isNaN(numericValue)) {
eMsg = '请输入有效的数字';
}
}
if (numericValue < 0) {
eMsg = '发病率必须大于等于0';
}
if (numericValue > 1) {
eMsg = '发病率必须小于或等于1';
}
if (eMsg) {
callback(new Error(eMsg));
} else {
callback();
}
}
},
trigger: "blur",
},
]
});
const formInfo = ref({
type: "form",
title: "",
col: "span",
formInfo: {
id: "add-disease-form",
readonly: false,
items: formItems.value,
rules: formRules.value,
},
});
const drawerInfo = ref({
visible: false,
direction: "rtl",
size: 480,
header: {
title: "新增",
},
type: "",
container: {
contents: [
formInfo.value
],
},
footer: {
visible: true,
btns: [
{ type: "default", label: "取消", value: "cancel" },
{ type: "primary", label: "保存", value: "submit" },
],
},
});
// 获取所有疾病数据
const getDiseaseData = () => {
getDiseaseAll().then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || [];
catalogData.value = JSON.parse(JSON.stringify(data));
}
})
}
const toSearch = (val: any, clear: boolean = false) => {
page.value.curr = 1;
if (clear) {
searchItemList.value.map((item) => (item.default = ""));
page.value.diseaseName = '';
page.value.isChildrenDisease = '';
} else {
page.value.diseaseName = val.diseaseName || '';
page.value.isChildrenDisease = val.isChildrenDisease || '';
}
getTableData();
};
const getTableData = () => {
tableInfo.value.loading = true;
getDiseaseList({
tenantGuid: userData.tenantGuid,
pageSize: page.value.limit,
pageIndex: page.value.curr,
diseaseName: page.value.diseaseName,
isChildrenDisease: page.value.isChildrenDisease,
}).then((res: any) => {
tableInfo.value.loading = false;
if (res.code == proxy.$passCode) {
const data = res.data || {};
tableData.value = data.records || [];
tableInfo.value.data = data.records || []
tableInfo.value.page.limit = data.pageSize
tableInfo.value.page.curr = data.pageIndex
tableInfo.value.page.rows = data.totalRows
// formItems.value[1].options = data.records || [];
} else {
proxy.$ElMessage.error(res.msg);
}
})
}
const tablePageChange = (info) => {
page.value.curr = Number(info.curr);
page.value.limit = Number(info.limit);
tableInfo.value.page.limit = page.value.limit;
tableInfo.value.page.curr = page.value.curr;
getTableData();
};
// 处理表格数据选中
const setSelect = (data, flag) => {
data.forEach((row: any) => {
selectionTreeTableRef.value.tableRef.toggleRowSelection(row, flag);
row.childList && setSelect(row.childList, flag);
});
}
const setCataLog = (dataArr, id, parentDisabled = false) => {
return dataArr.map(item => {
// 当前节点是否被禁选(自身匹配或父节点已禁选)
const isDisabled = parentDisabled || item.guid === id;
// 新节点基础属性
const newItem = {
...item,
disabled: isDisabled
};
// 递归处理子节点,并传递当前节点的禁选状态
if (item.childList && item.childList.length > 0) {
newItem.childList = setCataLog(item.childList, id, isDisabled);
}
return newItem;
})
}
const tableCheckboxSelectChange = (selectRows, row) => {
const check = selectRows.find(item => item.guid === row.guid);
row.childList && setSelect(row.childList, check ? true : false);
}
const checkAll = ref(false)
const tableCheckboxAllSelectChange = (selectRows) => {
checkAll.value = !checkAll.value;
setSelect(tableData.value, checkAll.value);
}
const tableSelectionChange = (val) => {
selectRowData.value = val;
};
const tableBtnClick = (scope, btn) => {
const type = btn.value;
const row = scope.row;
currTableData.value = row;
if (type === 'edit') { // 编辑
drawerInfo.value.visible = true;
drawerInfo.value.header.title = '编辑疾病';
let cata = setCataLog(catalogData.value, row.guid);
formItems.value.map(item => {
if (item.field == 'parentGuid') {
item.options = cata;
}
item.default = row[item.field] || '';
if (item.children) {
item.children.map(child => {
child.default = row[child.field] || '';
if (child.field == 'factor') {
child.default = (row[child.field] !== '' && row[child.field] !== null) ? changeNum(row[child.field], 2) : '';
}
})
}
if (item.inputType == 'integerNumber') {
item.default = typeof row[item.field] == 'number' ? row[item.field] : '';
}
});
drawerInfo.value.type = type;
} else if (type === 'del') { // 删除
open('确定要删除该条数据吗?', 'warning');
}
};
const open = (msg, type, isBatch = false) => {
ElMessageBox.confirm(msg, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: type,
}).then(() => {
let guids = [currTableData.value.guid]
if (isBatch) {
guids = [];
// 处理表格数据选中
const setGuids = (data) => {
data.forEach((row: any) => {
guids.push(row.guid);
row.childList && setGuids(row.childList);
});
}
setGuids(selectRowData.value);
}
deleteDisease(guids).then((res: any) => {
if (res.code == proxy.$passCode) {
getTableData();
getDiseaseData();
ElMessage({
type: "success",
message: "删除成功",
});
} else {
proxy.$ElMessage.error(res.msg);
}
});
});
};
const addDisease = () => {
formItems.value.map(item => {
if (item.field == 'bizState') {
item.default = 'Y';
} else {
if (item.field == 'parentGuid') {
item.options = catalogData.value;
}
item.default = '';
if (item.children) {
item.children.map(child => {
child.default = '';
})
}
}
});
drawerInfo.value.header.title = '新增疾病';
drawerInfo.value.type = 'add';
drawerInfo.value.visible = true;
}
// 批量删除
const batching = (type) => {
if (type == 'delete') {
if (selectRowData.value.length == 0) {
ElMessage({
type: 'error',
message: '请选择需要删除的数据',
})
return
}
open("此操作将永久删除, 是否继续?", "warning", true);
} else if (type == 'import') {
const info = {
dictionaryGuid: ''
}
cacheStore.setCatch('uploadSetting', info)
router.push(
{
name: 'importFileDisease',
query: {
isfileImport: '8'
}
}
);
}
}
const drawerBtnClick = async (btn, info) => {
if (btn.value == "submit") {
drawerInfo.value.footer.btns.map((item: any) => (item.disabled = true));
let params = { ...info, tenantGuid: userData.tenantGuid };
delete params.in;
if (drawerInfo.value.type == "add") {
saveDisease(params)
.then((res: any) => {
if (res.code == proxy.$passCode) {
getTableData();
getDiseaseData();
ElMessage({
type: "success",
message: "新增疾病成功",
});
drawerInfo.value.visible = false;
} else {
proxy.$ElMessage.error(res.msg);
drawerInfo.value.footer.btns.map(
(item: any) => delete item.disabled
);
}
})
.catch(() => {
drawerInfo.value.footer.btns.map((item: any) => delete item.disabled);
});
} else {
params.guid = currTableData.value.guid;
updateDisease(params)
.then((res: any) => {
if (res.code == proxy.$passCode) {
getTableData();
getDiseaseData();
ElMessage({
type: "success",
message: "编辑疾病成功",
});
drawerInfo.value.visible = false;
} else {
proxy.$ElMessage.error(res.msg);
drawerInfo.value.footer.btns.map(
(item: any) => delete item.disabled
);
}
})
.catch(() => {
drawerInfo.value.footer.btns.map((item: any) => delete item.disabled);
});
}
} else {
drawerInfo.value.footer.btns.map((item: any) => delete item.disabled);
nextTick(() => {
drawerInfo.value.visible = false;
});
}
};
onBeforeMount(() => {
getDiseaseData();
});
onActivated(() => {
})
</script>
<template>
<div class="container_wrap">
<div class="table_tool_wrap">
<TableTools :searchItems="searchItemList" :searchId="'data-source-search'" @search="toSearch" />
<div class="tools_btns">
<el-button type="primary" @click="addDisease" v-preReClick>新增</el-button>
<el-button @click="batching('import')" v-preReClick>批量导入</el-button>
<el-button @click="batching('delete')" v-preReClick>批量删除</el-button>
</div>
</div>
<div class="table_panel_wrap">
<Table ref="selectionTreeTableRef" :tableInfo="tableInfo" @tableCheckboxSelectChange="tableCheckboxSelectChange"
@tableCheckboxAllSelectChange="tableCheckboxAllSelectChange" @tableSelectionChange="tableSelectionChange"
@tablePageChange="tablePageChange" @tableBtnClick="tableBtnClick" />
</div>
<Drawer :drawerInfo="drawerInfo" @drawerBtnClick="drawerBtnClick" />
</div>
</template>
<style lang="scss" scoped>
.table_tool_wrap {
width: 100%;
height: 84px !important;
padding: 0 8px;
.tools_btns {
padding: 0px 0 0;
}
}
.table_panel_wrap {
width: 100%;
height: calc(100% - 84px);
padding: 0px 8px 0;
}
</style>
<route lang="yaml">
name: priceCalculate
</route>
<script lang="ts" setup name="priceCalculate">
import { ref } from 'vue';
import TableTools from '@/components/Tools/table_tools.vue';
import { TableColumnWidth, commonPageConfig } from '@/utils/enum';
import { ElMessage, ElMessageBox } from "element-plus";
import { CirclePlus } from "@element-plus/icons-vue";
import { useRouter, useRoute } from "vue-router";
import useUserStore from "@/store/modules/user";
import useDataAssetStore from "@/store/modules/dataAsset";
import { getAllFlowData } from '@/api/modules/queryService';
import { changeNum } from "@/utils/common";
import {
getDiseaseAll,
getPriceList,
deletePrice,
} from '@/api/modules/dataPricing';
const router = useRouter();
const userStore = useUserStore()
const assetStore = useDataAssetStore();
const userData = JSON.parse(userStore.userData)
const { proxy } = getCurrentInstance() as any;
const searchItemList = ref([
{
type: "input",
label: "",
field: "dataResourceName",
default: "",
placeholder: "数据资源名称",
clearable: true,
},
{
type: "cascader",
label: "",
field: "diseaseName",
placeholder: "疾病名称",
default: "",
options: [],
showAllLevels: false,
props: {
checkStrictly: true,
label: "diseaseName",
value: "guid",
children: 'childList',
emitPath: false
},
filterable: true,
clearable: true,
}
]);
const typeMap: any = ref({});
const selectRowData: any = ref([])
const currTableData: any = ref({});
const page: any = ref({
...commonPageConfig,
dataResourceName: '',
diseaseName: ''
});
const tableField: any = ref([
{ label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
{ label: "定价模型名称", field: "modelName", width: 200 },
{ label: "数据资源名称", field: "dataResourceName", width: 200 },
{ label: "疾病名称", field: "diseaseName", width: 200 },
{
label: "交易价格(元)", field: "dataTransactionPrice", width: 120, align: 'right', getName: (scope) => {
return scope.row.dataTransactionPrice ? changeNum(parseFloat(scope.row.dataTransactionPrice), 2) : '-';
}
},
{ label: "创建人", field: "createUserName", width: 120 },
{ label: "创建时间", field: "createTime", width: TableColumnWidth.DATETIME },
]);
const tableInfo = ref({
id: 'api-data-table',
rowKey: 'guid',
loading: false,
fields: tableField.value,
data: [],
page: {
type: "normal",
rows: 0,
...page.value,
},
actionInfo: {
label: "操作",
type: "btn",
isMore: false,
width: 120,
btns: [
{ label: "编辑", value: "edit" },
{ label: "删除", value: "del" }
]
}
});
const setTableField = () => {
tableField.value.splice(4, 0, {
label: "交易用途", field: "dataUsage", width: 120, getName: (scope) => {
return typeMap.value['dataUsage'].find((item) => item.value == scope.row['dataUsage'])?.label || '-';
}
});
tableInfo.value.fields = tableField.value;
}
// 获取所有疾病数据
const getDiseaseData = () => {
getDiseaseAll().then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || [];
searchItemList.value[1].options = data;
}
})
}
// 获取数据字典
const getDataType = (dictType, fieldName) => {
getAllFlowData({ dictType }).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || [];
typeMap.value[fieldName] = JSON.parse(JSON.stringify(data));
setTableField()
} else {
proxy.$ElMessage.error(res.msg);
}
})
}
const toSearch = (val: any, clear: boolean = false) => {
page.value.curr = 1;
if (clear) {
searchItemList.value.map((item) => (item.default = ""));
page.value.dataResourceName = ''
page.value.diseaseName = '';
} else {
page.value.dataResourceName = val.dataResourceName || '';
page.value.diseaseName = val.diseaseName || '';
}
getTableData();
};
const getTableData = () => {
tableInfo.value.loading = true;
getPriceList({
pageSize: page.value.limit,
pageIndex: page.value.curr,
dataResourceName: page.value.dataResourceName,
diseaseName: page.value.diseaseName,
}).then((res: any) => {
tableInfo.value.loading = false;
if (res.code == proxy.$passCode) {
const data = res.data || {};
tableInfo.value.data = data.records || [];
tableInfo.value.page.limit = data.pageSize
tableInfo.value.page.curr = data.pageIndex
tableInfo.value.page.rows = data.totalRows
} else {
proxy.$ElMessage.error(res.msg);
}
})
}
const tablePageChange = (info) => {
page.value.curr = Number(info.curr);
page.value.limit = Number(info.limit);
tableInfo.value.page.limit = page.value.limit;
tableInfo.value.page.curr = page.value.curr;
getTableData();
};
const tableBtnClick = (scope, btn) => {
const type = btn.value;
const row = scope.row;
currTableData.value = row;
if (type === 'edit') { // 编辑
router.push(
{
name: 'calculateConfig',
query: {
guid: row.guid,
name: row.modelName,
type: 'edit'
}
}
);
} else if (type === 'del') { // 删除
open('确定要删除该条数据吗?', 'warning');
}
};
const open = (msg, type, isBatch = false) => {
ElMessageBox.confirm(msg, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: type,
}).then(() => {
let guids = [currTableData.value.guid]
if (isBatch) {
guids = selectRowData.value
}
deletePrice(guids).then((res: any) => {
if (res.code == proxy.$passCode) {
getTableData();
ElMessage({
type: "success",
message: "删除成功",
});
} else {
proxy.$ElMessage.error(res.msg);
}
});
});
};
const addDisease = () => {
router.push(
{
name: 'calculateConfig',
query: {
type: 'create'
}
}
);
}
onBeforeMount(() => {
getDiseaseData();
getDataType('数据用途', 'dataUsage')
});
onActivated(() => {
if (assetStore.isRefresh) {//如果是首次加载,则不需要调用
toSearch(null, true);
assetStore.set(false);
}
})
</script>
<template>
<div class="container_wrap">
<div class="table_tool_wrap">
<TableTools :searchItems="searchItemList" :searchId="'data-source-search'" @search="toSearch" />
<div class="tools_btns">
<el-button type="primary" @click="addDisease" v-preReClick>新增</el-button>
</div>
</div>
<div class="table_panel_wrap">
<Table :tableInfo="tableInfo" @tablePageChange="tablePageChange" @tableBtnClick="tableBtnClick" />
</div>
</div>
</template>
<style lang="scss" scoped>
.table_tool_wrap {
width: 100%;
height: 84px !important;
padding: 0 8px;
.tools_btns {
padding: 0px 0 0;
}
}
.table_panel_wrap {
width: 100%;
height: calc(100% - 84px);
padding: 0px 8px 0;
}
</style>
<route lang="yaml">
name: priceConfig
</route>
<script lang="ts" setup name="priceConfig">
import { ref } from 'vue';
import TableTools from '@/components/Tools/table_tools.vue';
import { TableColumnWidth, commonPageConfig } from '@/utils/enum';
import { ElMessage, ElMessageBox } from "element-plus";
import { CirclePlus } from "@element-plus/icons-vue";
import { useRouter, useRoute } from "vue-router";
import useUserStore from "@/store/modules/user";
import useDataAssetStore from "@/store/modules/dataAsset";
import { getAllFlowData } from '@/api/modules/queryService';
import {
getConfigureList,
deleteConfigure,
getConfigureDetail,
addCopyConfigure
} from '@/api/modules/dataPricing';
const router = useRouter();
const userStore = useUserStore()
const assetStore = useDataAssetStore();
const userData = JSON.parse(userStore.userData)
const { proxy } = getCurrentInstance() as any;
const searchItemList = ref([
{
type: "input",
label: "",
field: "modelName",
default: "",
placeholder: "定价模型名称",
clearable: true,
},
{
type: "select",
label: "",
field: "institutionType",
placeholder: "机构类型",
default: "",
options: [],
clearable: true,
}
]);
const typeMap = ref({});
const tableData: any = ref([]);
const selectRowData: any = ref([])
const currTableData: any = ref({});
const page: any = ref({
...commonPageConfig,
modelName: '',
institutionType: ''
});
const tableInfo = ref({
id: 'api-data-table',
rowKey: 'guid',
loading: false,
fields: [
{ label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
{ label: "定价模型名称", field: "modelName", width: 200 },
{
label: "机构类型", field: "institutionType", width: 120, getName: (scope) => {
return filterVal(scope.row.institutionType, 'institutionType')
}
},
{
label: "启用状态", field: "bizState", type: 'tag', width: 120, align: 'center', getName: (scope) => {
return scope.row.bizState == 'Y' ? '启用' : '停用';
}, tagType: (scope) => {
return scope.row.bizState == 'Y' ? 'success' : 'info';
}
},
{ label: "创建人", field: "createUserName", width: 120 },
{ label: "创建时间", field: "createTime", width: TableColumnWidth.DATETIME },
],
data: tableData.value,
page: {
type: "normal",
rows: 0,
...page.value,
},
actionInfo: {
label: "操作",
type: "btn",
isMore: false,
width: 160,
btns: [
{ label: "编辑", value: "edit" },
{ label: "复制", value: "copy" },
{ label: "删除", value: "del" }
]
}
});
// 过滤
const filterVal = (val, name) => {
if (typeMap.value[name]) {
const data = typeMap.value[name].find(item => item.value == val);
return data?.label || '--';
}
};
// 获取需求类型
const getDataType = (dictType, fieldName) => {
getAllFlowData({ dictType }).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || [];
typeMap.value[fieldName] = JSON.parse(JSON.stringify(data));
let item = searchItemList.value.find(item => item.field == fieldName);
item && (item.options = data);
} else {
proxy.$ElMessage.error(res.msg);
}
})
}
const toSearch = (val: any, clear: boolean = false) => {
page.value.curr = 1;
if (clear) {
searchItemList.value.map((item) => (item.default = ""));
page.value.modelName = '';
page.value.institutionType = '';
} else {
page.value.modelName = val.modelName || '';
page.value.institutionType = val.institutionType || '';
}
getTableData();
};
const getTableData = () => {
tableInfo.value.loading = true;
getConfigureList({
pageSize: page.value.limit,
pageIndex: page.value.curr,
modelName: page.value.modelName,
institutionType: page.value.institutionType,
}).then((res: any) => {
tableInfo.value.loading = false;
if (res.code == proxy.$passCode) {
const data = res.data || {};
tableInfo.value.data = data.records || []
tableInfo.value.page.limit = data.pageSize
tableInfo.value.page.curr = data.pageIndex
tableInfo.value.page.rows = data.totalRows
} else {
proxy.$ElMessage.error(res.msg);
}
})
}
const tablePageChange = (info) => {
page.value.curr = Number(info.curr);
page.value.limit = Number(info.limit);
tableInfo.value.page.limit = page.value.limit;
tableInfo.value.page.curr = page.value.curr;
getTableData();
};
const tableBtnClick = (scope, btn) => {
const type = btn.value;
const row = scope.row;
currTableData.value = row;
if (type === 'edit') { // 编辑
router.push(
{
name: 'priceModel',
query: {
guid: row.guid,
name: row.modelName,
type
}
}
);
} else if (type == 'copy') { //复制
ElMessageBox.confirm('是否确定复制该模型?', "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: 'warning',
}).then(() => {
getConfigureDetail({ guid: row.guid }).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || {};
let params = { ...data };
delete params.createTime;
delete params.createUserName;
delete params.pricingDimensionalityRSVOS;
const pricingDimensionalityRSVOS = data.pricingDimensionalityRSVOS || [];
pricingDimensionalityRSVOS.map(p => {
p.pricingTargetRQVOS = p.pricingTargetRSVOS || [];
delete p.pricingTargetRSVOS;
});
params.pricingDimensionalityRQVOS = pricingDimensionalityRSVOS;
addCopyConfigure(params).then((res: any) => {
if (res.code == proxy.$passCode) {
ElMessage({
type: "success",
message: "复制定价配置成功",
});
getTableData();
} else {
proxy.$ElMessage.error(res.msg);
}
}).catch(() => {
});
}
}).catch(() => {
})
});
} else if (type === 'del') { // 删除
open('确定要删除该条数据吗?', 'warning');
}
};
const open = (msg, type, isBatch = false) => {
ElMessageBox.confirm(msg, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: type,
}).then(() => {
let guids = [currTableData.value.guid]
if (isBatch) {
guids = selectRowData.value
}
deleteConfigure(guids).then((res: any) => {
if (res.code == proxy.$passCode) {
getTableData();
ElMessage({
type: "success",
message: "删除成功",
});
} else {
proxy.$ElMessage.error(res.msg);
}
});
});
};
const addDisease = () => {
router.push(
{
name: 'priceModel',
query: {
type: 'create'
}
}
);
}
onBeforeMount(() => {
getDataType('机构类型', 'institutionType')
});
onActivated(() => {
if (assetStore.isRefresh) {//如果是首次加载,则不需要调用
toSearch(null, true);
assetStore.set(false);
}
})
</script>
<template>
<div class="container_wrap">
<div class="table_tool_wrap">
<TableTools :searchItems="searchItemList" :searchId="'data-source-search'" @search="toSearch" />
<div class="tools_btns">
<el-button type="primary" @click="addDisease" v-preReClick>新增</el-button>
</div>
</div>
<div class="table_panel_wrap">
<Table :tableInfo="tableInfo" @tablePageChange="tablePageChange" @tableBtnClick="tableBtnClick" />
</div>
</div>
</template>
<style lang="scss" scoped>
.table_tool_wrap {
width: 100%;
height: 84px !important;
padding: 0 8px;
.tools_btns {
padding: 0px 0 0;
}
}
.table_panel_wrap {
width: 100%;
height: calc(100% - 84px);
padding: 0px 8px 0;
}
</style>
<route lang="yaml">
name: priceModel
</route>
<script lang="ts" setup name="priceModel">
import { ref, onMounted } from "vue";
import { useRouter, useRoute } from "vue-router";
import { ElMessage, ElMessageBox, rowProps } from "element-plus";
import { Plus, CirclePlus, Guide } from "@element-plus/icons-vue";
import useUserStore from "@/store/modules/user";
import useDataAssetStore from "@/store/modules/dataAsset";
import { getAllFlowData } from '@/api/modules/queryService';
import { changeNum } from "@/utils/common";
import {
getDemandTreeList,
getConfigureDetail,
saveConfigure,
updateConfigure
} from '@/api/modules/dataPricing';
const { proxy } = getCurrentInstance() as any;
const router = useRouter();
const route = useRoute();
const userStore = useUserStore();
const assetStore = useDataAssetStore();
const fullPath = route.fullPath;
const userData = JSON.parse(localStorage.userData);
const guid = route.query.guid;
const modelName = route.query.name;
const loading = ref(false);
const flowDetail: any = ref({});
const typeMap: any = ref({});
const expand1 = ref(true)
const expand2 = ref(true)
// 基础设置
const baseConfigFormRef = ref();
const baseConfigFormItems: any = ref([
{
label: '模型名称',
type: 'input',
placeholder: '请输入',
field: 'modelName',
default: '',
clearable: true,
required: true
},
{
label: '机构类型',
type: 'select',
placeholder: '请选择',
field: 'institutionType',
default: '',
options: [],
clearable: true,
required: true,
},
{
label: '启用状态',
type: 'switch',
field: 'bizState',
default: 'Y',
placeholder: '请选择',
activeValue: 'Y',
inactiveValue: 'N',
switchWidth: 32,
},
{
label: '模型公式',
type: 'input',
placeholder: '请输入',
field: 'modelFormula',
default: '',
maxlength: 500,
clearable: true,
required: true,
block: true,
},
])
const baseConfigFormRules = ref({
modelName: [
{ required: true, trigger: 'blur', message: "请填写模型名称" }
],
institutionType: [
{ required: true, trigger: 'change', message: "请选择机构类型" }
],
modelFormula: [
{ required: true, trigger: 'change', message: "请填写模型公式" },
{
validator: (rule, value, callback) => {
validateFormula(value, callback);
},
trigger: 'blur'
}
],
});
const baseConfigForm = ref({
items: baseConfigFormItems.value,
rules: baseConfigFormRules.value,
})
// 校验模型公式
const validateFormula = (value, callback) => {
const baseConfigFormObj = baseConfigFormRef.value;
const baseConfigFormInfo = baseConfigFormObj.formInline;
const formula = value || baseConfigFormInfo.modelFormula;
if (!formula.trim()) {
callback(new Error('请输入模型公式'));
return;
}
const dimensions = signatoryTableList.value.map(s => s.dimensionalityName);
/* 第一步:检查维度是否存在 */
const dimMatches = [...formula.matchAll(/"([^"]+)"/g)];
for (const match of dimMatches) {
const dim = match[1]; // 获取引号内的内容
if (!dimensions.includes(dim)) {
callback(new Error(`维度"${dim}"不存在`));
return;
}
}
/* 第二步:检查维度间是否有运算符 */
if (/"([^"]+)"\s*"([^"]+)"/.test(formula)) {
callback(new Error('维度之间必须使用运算符连接'));
return;
}
/* 第三步:检查运算符使用是否合法 */
// 检查连续运算符
if (/([+\-*\/^]\s*){2,}/.test(formula.replace(/\s+/g, ''))) {
callback(new Error('不允许连续使用运算符'));
return;
}
// 检查运算符后无参数
if (/[+\-*\/^]\s*["(]*(\)|$)/.test(formula.replace(/\s+/g, ''))) {
callback(new Error('公式不完整'));
return;
}
/* 第四步:替换变量并校验公式结构 */
const dimMap = new Map();
dimMatches.forEach((match, i) => {
dimMap.set(match[0], `a${i + 1}`); // 创建维度到变量的映射
});
let formulaWithVars = formula;
dimMap.forEach((varName, dim) => {
formulaWithVars = formulaWithVars.split(dim).join(varName);
});
// 基础字符检查
const validChars = /^[\sa\d+\-*\/^().]+$/;
if (!validChars.test(formulaWithVars)) {
callback(new Error('公式包含非法字符'));
return;
}
/* 增强的括号匹配检查(支持多种括号类型)*/
const bracketPairs = {
'(': ')',
'[': ']',
'{': '}',
'<': '>'
};
const stack = [];
for (const char of formulaWithVars) {
if (bracketPairs[char]) {
stack.push(char);
} else if (Object.values(bracketPairs).includes(char)) {
if (stack.length === 0 || bracketPairs[stack.pop()] !== char) {
callback(new Error('括号使用不匹配或未正确闭合'));
return;
}
}
}
if (stack.length > 0) {
callback(new Error('括号使用不匹配或未正确闭合'));
return;
}
callback(); // 所有检查通过
};
// 维度设置
const currTableData: any = ref({});
const currSignatoryData: any = ref({});
const signatoryTableList: any = ref([]);
const pricingDimensionality: any = ref([]);
// 维度表单
const signatoryFormItems: any = ref([
{
label: '维度名称',
type: 'input',
placeholder: '请输入',
field: 'dimensionalityName',
default: '',
maxlength: 50,
clearable: true,
required: true,
},
{
label: '计算公式',
type: 'select',
placeholder: '请选择',
field: 'computationalFormula',
default: '',
options: [],
clearable: true,
required: true,
},
{
label: '权重(%)',
type: 'input',
placeholder: '请输入',
field: 'weight',
default: '',
inputType: 'factorNumber',
maxlength: 10,
clearable: true,
},
{
label: '启用状态',
type: 'switch',
field: 'invocationStatus',
default: 'Y',
placeholder: '请选择',
activeValue: 'Y',
inactiveValue: 'N',
switchWidth: 32,
},
{
label: '维度说明',
type: 'textarea',
placeholder: '请输入',
field: 'remark',
default: '',
maxlength: 500,
clearable: true,
block: true,
},
{
label: '自定义公式',
type: 'textarea',
placeholder: '请输入',
field: 'customize',
default: '',
maxlength: 500,
clearable: true,
block: true,
visible: false,
},
])
const signatoryFormRules = ref({
dimensionalityName: [
{
validator: (rule: any, value: any, callback: any) => {
if (!value) {
callback(new Error("请填写维度名称"));
} else {
if (drawerInfo.value.type.indexOf('edit') > -1 && currSignatoryData.value.dimensionalityName == value) {
callback();
return
}
const dimName = signatoryTableList.value.map(item => item.dimensionalityName);
if (dimName.indexOf(value) != -1) {
callback(new Error("该名称已存在"));
} else {
callback();
}
}
},
trigger: "blur",
},
],
computationalFormula: [
{ required: true, message: '请选择计算公式', trigger: 'change' }
],
});
// 指标表单
const treeData = ref([]);
const targetFormItems: any = ref([
{
label: '指标名称',
type: 'input',
placeholder: '请输入',
field: 'targetName',
default: '',
maxlength: 50,
clearable: true,
required: true,
},
{
label: '指标类型',
type: 'select',
placeholder: '请选择',
field: 'targetType',
default: '',
options: [],
clearable: true,
required: true,
},
{
label: '权重(%)',
type: 'input',
placeholder: '请输入',
field: 'weight',
default: '',
inputType: 'factorNumber',
maxlength: 10,
clearable: true,
required: true,
visible: false,
},
{
label: '是否展示',
type: 'switch',
field: 'isShow',
default: 'Y',
placeholder: '请选择',
activeValue: 'Y',
inactiveValue: 'N',
switchWidth: 32,
disabled: false,
style: {
width: 'calc(25% - 4px)',
'margin-left': '8px'
}
},
{
label: '是否输入参数',
type: 'switch',
field: 'isInputParameter',
default: 'Y',
placeholder: '请选择',
activeValue: 'Y',
inactiveValue: 'N',
switchWidth: 32,
style: {
width: 'calc(25% - 4px)'
}
},
{
label: '默认值',
type: 'input',
placeholder: '请输入',
field: 'defaultValue',
default: '',
inputType: 'factorNumber',
maxlength: 18,
clearable: true,
required: false
},
{
label: '选择字典',
type: 'select',
placeholder: '请选择',
field: 'dictionaryType',
default: '',
options: [],
clearable: true,
required: true,
visible: false,
},
{
label: '功能名称',
type: 'select',
placeholder: '请选择',
field: 'functionName',
default: '',
options: [],
clearable: true,
required: true,
visible: false,
},
{
label: '选择需求表',
type: 'select',
placeholder: '请选择',
field: 'demandTableGuid',
default: '',
options: [],
props: {
label: "menuName",
value: "guid",
},
filterable: true,
clearable: true,
required: true,
visible: false,
},
{
label: '公式设置',
type: 'textarea',
placeholder: '请输入',
field: 'formulaSetting',
default: '',
resize: 'vertical',
maxlength: 500,
clearable: true,
block: true,
required: true,
},
])
const targetFormRules = ref({
targetName: [
{
validator: (rule: any, value: any, callback: any) => {
if (!value) {
callback(new Error("请填写指标名称"));
} else {
if (drawerInfo.value.type.indexOf('edit') > -1 && currTableData.value.targetName == value) {
callback();
return
}
const dimName = currSignatoryData.value.tableInfo.data.map(item => item.targetName);
if (dimName.indexOf(value) != -1) {
callback(new Error("该名称已存在"));
} else {
callback();
}
}
},
trigger: "blur",
},
],
targetType: [
{ required: true, message: '请选择指标类型', trigger: 'change' }
],
dictionaryType: [
{ required: true, message: '请选择数据字典', trigger: 'change' }
],
functionName: [
{ required: true, message: '请选择功能名称', trigger: 'change' }
],
formulaSetting: [
{ required: true, message: '请填写公式设置', trigger: 'blur' }
],
demandTableGuid: [
{ required: true, message: '请选择需求表', trigger: 'change' }
],
weight: [
{ required: true, message: '请填写权重', trigger: 'change' },
{
validator: (rule: any, value: any, callback: any) => {
if (!value) {
callback(new Error("请填写权重"));
} else {
if (drawerInfo.value.type.indexOf('edit') > -1 && currTableData.value.weight == value) {
callback();
return
}
setTimeout(() => {
let formula: any = [];
if (drawerInfo.value.type.indexOf('edit') > -1) {
formula = currSignatoryData.value.tableInfo.data.map(p => p.targetName == currTableData.value.targetName ? value : p.weight || 0);
} else {
formula = currSignatoryData.value.tableInfo.data.map(p => p.weight || 0);
formula.push(value);
}
const isOver = formula.reduce((accumulator, currentValue) => parseFloat(accumulator) + parseFloat(currentValue), 0); // 初始值为0
if (isOver > 100) {
callback(new Error("该维度权重和已超过100%,请重新填写"));
} else {
callback();
}
}, 200);
}
},
trigger: "blur",
},
],
defaultValue: [
{ required: true, message: '请填写默认值', trigger: 'blur' }
],
});
const dictionaryType = ref('');
const showFactorTable = ref(false);
const mergeRowCount: any = ref({});
const rowCount = {
level: { index: 0, rowspan: [] },
};
const mergeTableData = [
{ id: '1-1', level: '一级', grade: '甲等', factor: '' },
{ id: '1-2', level: '一级', grade: '乙等', factor: '' },
{ id: '1-3', level: '一级', grade: '丙等', factor: '' },
{ id: '2-1', level: '二级', grade: '甲等', factor: '' },
{ id: '2-2', level: '二级', grade: '乙等', factor: '' },
{ id: '2-3', level: '二级', grade: '丙等', factor: '' },
{ id: '3-1', level: '三级', grade: '甲等', factor: '' },
{ id: '3-2', level: '三级', grade: '乙等', factor: '' },
{ id: '3-3', level: '三级', grade: '丙等', factor: '' },
];
const tableData: any = ref([]);
// 抽屉内容
const contents = ref({
target: {
type: "form",
title: "",
col: "span",
formInfo: {
id: "add-dict-form",
readonly: false,
items: targetFormItems.value,
rules: targetFormRules.value,
},
},
signatory: {
type: "form",
title: "",
col: "span",
formInfo: {
id: "add-dict-form",
readonly: false,
items: signatoryFormItems.value,
rules: signatoryFormRules.value,
},
}
})
const drawerInfo: any = ref({
visible: false,
direction: "rtl",
size: 480,
header: {
title: "新增",
},
type: "",
container: {
contents: [],
},
footer: {
visible: true,
btns: [
{ type: "default", label: "取消", value: "cancel" },
{ type: "primary", label: "保存", value: "submit" },
],
},
});
const setTableField = (data) => {
tableData.value = [];
const dictionaryName = typeMap.value['dictionaryType'].find(item => item.value == dictionaryType.value)?.label || '';
const dictionaryJson = dictionaryName && dictionaryName == currTableData.value.dictionaryName ? (currTableData.value.dictionaryJson || []) : [];
if (dictionaryType.value == '1') {
const tData = JSON.parse(JSON.stringify(mergeTableData));
if (dictionaryJson.length) {
tData.map((item, i) => {
item.factor = dictionaryJson[i]?.value || '';
})
}
tableData.value = tData;
getMergeRow();
} else {
if (dictionaryJson.length) {
dictionaryJson.map(item => {
tableData.value.push({
label: item.name,
factor: item.value || '',
})
})
} else {
data.map((item: any) => {
tableData.value.push({
label: item.label,
factor: '',
})
})
}
}
}
const getDictionaryRuleData = () => {
const dictType: any = typeMap.value['dictionaryType'].find(item => item.value == dictionaryType.value)?.label || '';
getAllFlowData({ dictType }).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || [];
setTableField(data);
} else {
proxy.$ElMessage.error(res.msg);
}
})
}
// 设置表单信息
const setFormItems = (info = null, type = '') => {
const datas: any = info || flowDetail.value || {};
if (type == 'target') {
targetFormItems.value.map(item => {
item.default = datas[item.field] || '';
if (item.field == 'functionName') {
item.visible = datas.targetType == '2'
} else if (item.field == 'dictionaryType') {
dictionaryType.value = datas[item.field] || '';
item.visible = datas.targetType == '3'
showFactorTable.value = datas.targetType == '3' && datas[item.field]
showFactorTable.value && getDictionaryRuleData();
} else if (item.field == 'formulaSetting') {
item.visible = datas.targetType == '2' && datas.functionName == '2'
} else if (item.field == 'demandTableGuid') {
item.visible = datas.targetType == '2' && datas.functionName == '3'
} else if (item.field == 'isShow' || item.field == 'isInputParameter') {
item.default = datas[item.field] || 'Y';
} else if (item.field == 'isShow') {
item.disabled = datas.targetType == '1'
} else if (item.field == 'defaultValue') {
item.required = datas.targetType == '1';
targetFormRules.value.defaultValue[0].required = datas.targetType == '1';
}
})
} else if (type == 'signatory') {
signatoryFormItems.value.map(item => {
if (item.field == 'invocationStatus') {
item.default = datas[item.field] || 'Y';
} else if (item.field == 'customize') {
item.default = datas[item.field] || '';
item.visible = datas.computationalFormula == 'custom'
} else {
item.default = datas[item.field] || '';
}
})
} else {
baseConfigFormItems.value.map(item => {
if (item.field == 'modelFormula') {
item.default = datas[item.field] || '';
} else {
item.default = datas[item.field] || '';
}
})
}
}
// 过滤
const setVal = (type, row) => {
let formulaText = '';
if (!row[type] || row[type] == 'custom') {
formulaText = row.customize;
} else {
const pricingTargetData = row.pricingTargetRSVOS || row.tableInfo?.data || [];
let formula: any = [];
if (pricingTargetData.length) {
pricingTargetData.map(item => {
if (row[type] == '1') {
formula.push(`${item.targetName}*${item.weight}%`)
} else {
formula.push(`${item.targetName}`)
}
})
if (row[type] == '3') {
formulaText = `公式=${formula.join('*')}`;
} else {
formulaText = `公式=${formula.join('+')}`;
}
}
}
row.customize = formulaText;
pricingDimensionality.value.find(item => {
if (item.dimensionalityName == row.dimensionalityName) {
item.customize = formulaText;
}
})
return formulaText;
};
/**
* 传入多个promise对象,当全部结束时取消Loading
* @param promises 传入多个promise对象,当全部结束时取消Loading
*/
const promiseList = (...promises: Promise<void>[]) => {
// loading方法全局封装成一个组件
loading.value = true;
try {
Promise.all(promises).then(res => {
if (guid) {
setFormItems();
setSignatoryData();
}
});
} catch (e) {
loading.value = false;
} finally {
loading.value = false;
}
};
// 获取数据字典
const getDataType = (dictType, fieldName) => {
return getAllFlowData({ dictType }).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || [];
if (fieldName == 'computationalFormula') {
data.push({ label: '自定义', value: 'custom' })
typeMap.value[fieldName] = JSON.parse(JSON.stringify(data));
let item = signatoryFormItems.value.find(item => item.field == fieldName);
item && (item.options = data);
} else {
typeMap.value[fieldName] = JSON.parse(JSON.stringify(data));
if (fieldName == 'institutionType') {
let item = baseConfigFormItems.value.find(item => item.field == fieldName);
item && (item.options = data);
} else {
let item = targetFormItems.value.find(item => item.field == fieldName);
item && (item.options = data);
}
}
} else {
proxy.$ElMessage.error(res.msg);
}
})
}
// 初始化维度表格信息
const setSignatoryTableInfo = (row, isEdit = false) => {
const pricingTarget = row.pricingTargetRSVOS || [];
const fieldList: any = [
{ label: "指标名称", field: "targetName", width: 200 },
{
label: "指标类型", field: "targetType", width: 120, getName: (scope) => {
return typeMap.value['targetType'].find(item => item.value == scope.row.targetType)?.label || '--';
}
},
{
label: "默认值", field: "defaultValue", width: 180, align: 'right', getName: (scope) => {
return changeNum(scope.row.defaultValue, 2) || '-'
}
},
{
label: "是否展示", field: "isShow", width: 100, getName: (scope) => {
return scope.row.isShow == 'Y' ? '是' : '否';
}
},
{
label: "是否输入参数", field: "isInputParameter", width: 140, getName: (scope) => {
return scope.row.isInputParameter == 'Y' ? '是' : '否';
}
}
];
row.computationalFormula == '1' && fieldList.splice(2, 0, { label: "权重(%)", field: "weight", width: 100 });
if (isEdit) {
const rIndex = signatoryTableList.value.findIndex(item => item.dimensionalityName == currSignatoryData.value.dimensionalityName);
rIndex != -1 && signatoryTableList.value.splice(rIndex, 1, { ...currSignatoryData.value, ...row, tableInfo: { ...currSignatoryData.value.tableInfo, fields: fieldList } });
const sIndex = pricingDimensionality.value.findIndex(item => item.dimensionalityName == currSignatoryData.value.dimensionalityName);
sIndex != -1 && pricingDimensionality.value.splice(sIndex, 1, { ...currSignatoryData.value, ...row, tableInfo: { ...currSignatoryData.value.tableInfo, fields: fieldList } });
} else {
const tData = {
id: `signatory-${signatoryTableList.value.length}`,
rowKey: '',
fields: fieldList,
data: pricingTarget,
showPage: false,
actionInfo: {
label: "操作",
type: "btn",
width: 120,
btns: [
{ label: "编辑", value: "edit" },
{ label: "删除", value: "del" }
]
}
};
signatoryTableList.value.push({
...row,
tableInfo: tData
});
}
}
// 设置维度指标数据
const setSignatoryData = () => {
const pricingData = flowDetail.value.pricingDimensionalityRSVOS || [];
pricingDimensionality.value = pricingData;
pricingData.map(item => {
const rows = item;
(() => {
setSignatoryTableInfo(rows);
})()
})
}
// 获取详情
const getModelDetail = () => {
loading.value = true;
getConfigureDetail({ guid }).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || {};
flowDetail.value = data;
getDataTypeList()
}
}).catch(() => {
loading.value = false;
})
}
const getDataTypeList = () => {
promiseList(
getTreeData(),
getDataType('机构类型', 'institutionType'),
getDataType('维度计算公式', 'computationalFormula'),
getDataType('维度指标类型', 'targetType'),
getDataType('系统功能', 'functionName'),
getDataType('选择字典', 'dictionaryType'),
)
}
// 获取需求表树形数据
const getTreeData = () => {
return getDemandTreeList({ pageIndex: 1, pageSize: 100000, isCatalog: 'N' }).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data?.records || [];
treeData.value = JSON.parse(JSON.stringify(data));
const item = targetFormItems.value.find(item => item.field == 'demandTableGuid');
item && (item.options = data);
} else {
proxy.$ElMessage.error(res.msg);
}
})
}
const selectChange = async (val, row, info) => {
if (row.field == 'computationalFormula') {
setFormItems(info, 'signatory');
} else if (row.field == 'targetType' || row.field == 'dictionaryType' || row.field == 'functionName') {
const tInfo = { ...currTableData.value, ...info };
await setFormItems(tInfo, 'target');
if (row.field == 'targetType') {
targetFormItems.value[3].default = val == '1' ? 'N' : 'Y';
}
}
}
const inputChange = (val, scope, field) => {
let row = scope.row;
let strArr = val.split(".");
if (strArr.length > 1) {
let right = strArr[1];
if (right === "" || right.length < 3) {
row[field] = val = parseFloat(val || 0).toFixed(3);
}
} else {
row[field] = val = parseFloat(val || 0).toFixed(3);
}
}
/** 输入框输入触发事件 */
const inputEventChange = (val, scope, field) => {
let row = scope.row;
row[field] = row[field].toString().replace(/[^\d.]/g, "")
row[field] = row[field].toString().replace(/\.{2,}/g, ".")
row[field] = row[field].toString().replace(".", "$#$").replace(/\./g, "").replace("$#$", ".")
row[field] = row[field].toString().replace(/^(\-)*(\d+)\.(\d\d\d\d\d\d).*$/, "$1$2.$3")
row[field] = row[field].toString().replace(/^\D*(\d{0,12}(?:\.\d{0,3})?).*$/g, "$1")
}
// 设置表格合并下标
const getMergeRow = () => {
mergeRowCount.value = JSON.parse(JSON.stringify(rowCount));
let list = tableData.value;
for (var i = 0; i < list.length; i++) {
if (i === 0) {
//第一个数据 默认合并1行,开始位置下标默认为0
for (var m in mergeRowCount.value) {
mergeRowCount.value[m].rowspan.push(1);
mergeRowCount.value[m].index = 0;
}
} else {
// 根据拥有子级数量进行合并1
for (var m in mergeRowCount.value) {
let mergeRow = mergeRowCount.value[m];
if (list[i][m] && list[i][m] === list[i - 1][m]) {
mergeRow.rowspan[mergeRow.index] += 1;
mergeRow.rowspan.push(0);
} else {
mergeRow.rowspan.push(1);
mergeRow.index = i;
}
}
}
}
}
// 表格行合并
const tableSpanMethod = ({ row, column, rowIndex, columnIndex }) => {
if (columnIndex === 0) {
const mergeInfo = mergeRowCount.value.level;
const rowspan = mergeInfo.rowspan[rowIndex];
if (rowspan > 0) {
return {
rowspan,
colspan: 1,
};
} else {
return {
rowspan: 0,
colspan: 0,
};
}
}
}
const toPath = () => {
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
assetStore.set(true);
router.push({
name: 'priceConfig',
})
}
const switchChange = (val, index) => {
pricingDimensionality.value[index].invocationStatus = val;
}
const btnClick = async (btn, row: any = null) => {
const type = btn.value;
if (type == 'dim') {
const baseConfigFormObj = baseConfigFormRef.value;
const baseConfigFormEl = baseConfigFormObj.ruleFormRef;
const baseConfigFormInfo = baseConfigFormObj.formInline;
await setFormItems(baseConfigFormInfo);
baseConfigFormItems.value.at(-1).default = baseConfigFormInfo.modelFormula ? `${baseConfigFormInfo.modelFormula}"${row.dimensionalityName}"` : `"${row.dimensionalityName}"`;
// 触发校验
setTimeout(() => {
baseConfigFormEl.validateField('modelFormula');
}, 200);
} else if (type == 'add-target') {
currSignatoryData.value = row;
currTableData.value = {};
const tData = currSignatoryData.value.tableInfo.data;
await setFormItems(null, 'target');
drawerInfo.value.type = type;
drawerInfo.value.header.title = '新增指标';
targetFormItems.value[2].visible = row.computationalFormula == '1';
targetFormItems.value.at(-2).options.map(o => {
const fData = tData.filter(t => t.targetType == '2' && t.functionName == '3');
o.disabled = fData.find(f => f.demandTableGuid == o.guid) ? true : false;
});
drawerInfo.value.container.contents[0] = contents.value.target;
tableData.value = [];
drawerInfo.value.visible = true;
} else if (type == 'add-signatory') {
currSignatoryData.value = {};
currTableData.value = {};
showFactorTable.value = false;
await setFormItems(null, 'signatory');
drawerInfo.value.type = type;
drawerInfo.value.header.title = '新增维度';
drawerInfo.value.container.contents[0] = contents.value.signatory;
drawerInfo.value.visible = true;
} else if (type == 'edit-signatory') {
currSignatoryData.value = row;
currTableData.value = {};
showFactorTable.value = false;
await setFormItems(row, 'signatory');
drawerInfo.value.type = type;
drawerInfo.value.header.title = '编辑维度';
drawerInfo.value.container.contents[0] = contents.value.signatory;
drawerInfo.value.visible = true;
} else if (type == 'del-signatory') {
currSignatoryData.value = row;
open('确定要删除该条维度数据吗?', 'warning', 'signatory');
} else if (type == 'submit') {
const baseConfigFormObj = baseConfigFormRef.value;
const baseConfigFormEl = baseConfigFormObj.ruleFormRef;
const baseConfigFormInfo = baseConfigFormObj.formInline;
baseConfigFormEl.validate((valid, errorItem) => {
if (valid) {
const pricingDimensionalityData = pricingDimensionality.value.map((item, i) => {
item.orderNum = i + 1;
const pricingTargetData = item.pricingTargetRSVOS || item.pricingTargetRQVOS || [];
item.pricingTargetRQVOS = pricingTargetData.map((targetItem, t) => {
targetItem.orderNum = t + 1;
delete targetItem.tableInfo;
return targetItem;
})
delete item.pricingTargetRSVOS;
return item;
})
loading.value = true;
let params = {
...baseConfigFormInfo,
tenantGuid: userData.tenantGuid,
pricingDimensionalityRQVOS: pricingDimensionalityData,
};
if (guid) {
params.guid = guid
updateConfigure(params).then((res: any) => {
loading.value = false;
if (res.code == proxy.$passCode) {
ElMessage({
type: "success",
message: guid ? "编辑定价配置成功" : "新增定价配置成功",
});
toPath()
} else {
proxy.$ElMessage.error(res.msg);
}
}).catch(() => {
loading.value = false;
});
} else {
saveConfigure(params).then((res: any) => {
loading.value = false;
if (res.code == proxy.$passCode) {
ElMessage({
type: "success",
message: guid ? "编辑定价配置成功" : "新增定价配置成功",
});
toPath()
} else {
proxy.$ElMessage.error(res.msg);
}
}).catch(() => {
loading.value = false;
});
}
} else {
expand1.value = true;
var obj = Object.keys(errorItem);
baseConfigFormEl.scrollToField(obj[0]);
}
});
} else if (type == 'cancel') {
ElMessageBox.confirm(
"当前页面尚未保存,确定关闭吗?",
"提示",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}
).then(() => {
toPath()
}).catch(() => {
ElMessage({
type: "info",
message: "已取消",
});
});
}
}
const tableBtnClick = async (scope, btn, lData: any = null) => {
const type = btn.value;
const row = scope.row;
currTableData.value = row;
if (type === 'edit') { // 编辑
currSignatoryData.value = lData;
const tData = currSignatoryData.value.tableInfo.data;
await setFormItems(row, 'target');
drawerInfo.value.type = 'edit-target';
drawerInfo.value.header.title = '编辑指标';
targetFormItems.value[2].visible = lData.computationalFormula == '1';
targetFormItems.value.at(-2).options.map(o => {
const fData = tData.filter(t => t.targetType == '2' && t.functionName == '3');
o.disabled = fData.find(f => f.demandTableGuid == o.guid) ? true : false;
});
drawerInfo.value.container.contents[0] = contents.value.target;
drawerInfo.value.visible = true;
} else if (type === 'del') { // 删除
currSignatoryData.value = lData;
open('确定要删除该条数据吗?', 'warning', 'target');
}
};
const open = (msg, type, target) => {
ElMessageBox.confirm(msg, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: type,
}).then(() => {
if (target == 'signatory') {
const tIndex = signatoryTableList.value.findIndex(item => item.dimensionalityName == currSignatoryData.value.dimensionalityName);
tIndex != -1 && signatoryTableList.value.splice(tIndex, 1);
const sIndex = pricingDimensionality.value.findIndex(item => item.dimensionalityName == currSignatoryData.value.dimensionalityName);
sIndex != -1 && pricingDimensionality.value.splice(sIndex, 1);
} else if (target == 'target') {
const signatoryRow = signatoryTableList.value.find(item => item.dimensionalityName == currSignatoryData.value.dimensionalityName);
if (signatoryRow) {
const tIndex = signatoryRow.tableInfo.data.findIndex(t => t.targetName == currTableData.value.targetName);
tIndex != -1 && signatoryRow.tableInfo.data.splice(tIndex, 1);
}
const signatoryParams = pricingDimensionality.value.find(item => item.dimensionalityName == currSignatoryData.value.dimensionalityName);
if (signatoryParams) {
const sIndex = signatoryParams.pricingTargetRSVOS.findIndex(s => s.targetName == currTableData.value.targetName);
sIndex != -1 && signatoryParams.pricingTargetRSVOS.splice(sIndex, 1);
}
}
});
};
const drawerBtnClick = async (btn, info) => {
if (btn.value == "submit") {
let params = { ...info };
if (drawerInfo.value.type == 'add-signatory') {// 新增维度
setSignatoryTableInfo(params);
pricingDimensionality.value.push({
...params,
pricingTargetRSVOS: []
})
} else if (drawerInfo.value.type == 'edit-signatory') {// 编辑维度
setSignatoryTableInfo(params, true);
} else if (drawerInfo.value.type == 'add-target') {// 新增指标
let factorFull = true;
const dictionaryData = tableData.value.map((d: any) => {
if (dictionaryType.value && !d.factor) {
factorFull = false;
}
if (dictionaryType.value == '1') {
return { name: `${d.level}${d.grade}`, value: d.factor || '' }
} else {
return { name: `${d.label}`, value: d.factor || '' }
}
})
if (dictionaryType.value && !factorFull) {
ElMessage({
type: "error",
message: '请完善字典因子',
});
return;
}
const dictionaryName = typeMap.value['dictionaryType'].find(item => item.value == params.dictionaryType)?.label || '';
const sArr = signatoryTableList.value.map(item => {
if (item.dimensionalityName == currSignatoryData.value.dimensionalityName) {
item.tableInfo.data.push({ ...params, dictionaryJson: dictionaryData, dictionaryName });
}
return item;
})
signatoryTableList.value = sArr;
let signatoryParams = pricingDimensionality.value.find(item => item.dimensionalityName == currSignatoryData.value.dimensionalityName);
if (signatoryParams) {
const sData = signatoryParams.pricingTargetRSVOS.find(s => s.targetName == params.targetName)
!sData && signatoryParams.pricingTargetRSVOS.push({ ...params, dictionaryJson: dictionaryData, dictionaryName })
}
} else if (drawerInfo.value.type == 'edit-target') {// 编辑指标
let factorFull = true;
const dictionaryData = tableData.value.map((d: any) => {
if (dictionaryType.value && !d.factor) {
factorFull = false;
}
if (dictionaryType.value == '1') {
return { name: `${d.level}${d.grade}`, value: d.factor || '' }
} else {
return { name: `${d.label}`, value: d.factor || '' }
}
})
if (dictionaryType.value && !factorFull) {
ElMessage({
type: "error",
message: '请完善字典因子',
});
return;
}
const dictionaryName = typeMap.value['dictionaryType'].find(item => item.value == params.dictionaryType)?.label || '';
const sArr = signatoryTableList.value.map(item => {
if (item.dimensionalityName == currSignatoryData.value.dimensionalityName) {
const tIndex = item.tableInfo.data.findIndex(t => t.targetName == currTableData.value.targetName)
tIndex != -1 && item.tableInfo.data.splice(tIndex, 1, { ...params, dictionaryJson: dictionaryData, dictionaryName })
}
return item;
})
signatoryTableList.value = sArr;
let signatoryParams = pricingDimensionality.value.find(item => item.dimensionalityName == currSignatoryData.value.dimensionalityName);
if (signatoryParams) {
const sIndex = signatoryParams.pricingTargetRSVOS.findIndex(s => s.targetName == currTableData.value.targetName)
sIndex != -1 && signatoryParams.pricingTargetRSVOS.splice(sIndex, 1, { ...params, dictionaryJson: dictionaryData, dictionaryName })
}
}
drawerInfo.value.visible = false;
} else {
nextTick(() => {
drawerInfo.value.visible = false;
});
}
};
onActivated(() => {
let tab: any = userStore.tabbar.find((tab: any) => tab.fullPath === router.currentRoute.value.fullPath);
if (tab) {
switch (route.query.type) {
case 'create':
tab.meta.title = `新增定价配置`;
break;
case 'edit':
tab.meta.title = `编辑-${modelName}`;
break;
case 'detail':
tab.meta.title = `详情-${modelName}`;
break;
}
}
})
onBeforeMount(() => {
if (guid) {
getModelDetail();
} else {
getDataTypeList();
}
})
onMounted(() => {
})
</script>
<template>
<div class="container_wrap full" v-loading="loading">
<div class="content_main panel">
<ContentWrap id="contract-content-wrap" title="基础设置" expandSwicth style="margin-top: 15px" :isExpand="expand1"
@expand="(v) => expand1 = v">
<Form ref="baseConfigFormRef" formId="contract-content-form" :itemList="baseConfigForm.items"
:rules="baseConfigForm.rules" col="col3" />
<div class="signatory-tags" v-if="signatoryTableList.length">
<template v-for="(item, index) in signatoryTableList" :key="'tag_' + index">
<el-button v-if="item.invocationStatus == 'Y'" type="primary" @click="btnClick({ value: 'dim' }, item)">{{
item.dimensionalityName }}</el-button>
</template>
</div>
<div class="tips-text" style="color: #666; margin-bottom: 4px; font-size: 12px;">
备注:模型公式是维度名称与运算符组合,如("维度1"+"维度2")/("维度3"+"维度4")*10,维度名称要用英文引号(")包裹。维度名称在添加维度后才会在公式下展示!
</div>
</ContentWrap>
<ContentWrap id="contract-signatory-wrap" title="维度设置" expandSwicth style="margin-top: 15px" :isExpand="expand2"
@expand="(v) => expand2 = v">
<div class="table_panel_wrap" v-for="(item, index) in signatoryTableList" :key="'wrap_' + index">
<div class="table_tool">
<div class="tool_title">
<div class="title_text">{{ item.dimensionalityName }}</div>
<div class="title_desc"><ellipsis-tooltip :content="setVal('computationalFormula', item)"
refName="tooltipOver"></ellipsis-tooltip></div>
</div>
<div class="tool_btns">
<el-switch v-model="item.invocationStatus" inline-prompt active-value="Y" inactive-value="N"
active-text="启用" inactive-text="停用" @change="val => switchChange(val, index)"
style="margin-right: 8px;" />
<el-button type="primary" @click="btnClick({ value: 'edit-signatory' }, item)">编辑</el-button>
<el-button @click="btnClick({ value: 'del-signatory' }, item)">删除</el-button>
</div>
</div>
<div class="table_panel">
<Table :tableInfo="item.tableInfo" @tableBtnClick="(scope, btn) => tableBtnClick(scope, btn, item)" />
</div>
<el-button type="primary" :icon="CirclePlus" link
@click="btnClick({ value: 'add-target' }, item)">新增指标</el-button>
</div>
<el-button class="btn-block" plain size="large" @click="btnClick({ value: 'add-signatory' })">新增维度</el-button>
</ContentWrap>
</div>
<div class="tool_btns">
<div class="btns">
<el-button @click="btnClick({ value: 'cancel' })">取消</el-button>
<el-button type="primary" @click="btnClick({ value: 'submit' })">提交</el-button>
</div>
</div>
<Drawer :drawerInfo="drawerInfo" @drawerBtnClick="drawerBtnClick" @drawerSelectChange="selectChange">
<template v-if="showFactorTable">
<span class="required_mark" style="line-height: 21px;">字典值对应因子</span>
<div class="table_panel">
<el-table border :data="tableData" :span-method="tableSpanMethod" tooltip-effect="light" style="height: 100%;"
v-if="dictionaryType == '1'">
<el-table-column label="医院等级">
<el-table-column prop="level" label="级别" width="100" />
<el-table-column prop="grade" label="等次" width="100" />
</el-table-column>
<el-table-column prop="factor" label="因子" class-name="edit-col">
<template #default="scope">
<el-input v-model="scope.row.factor" placeholder="请输入"
@change="(val) => inputChange(val, scope, 'factor')"
@input="(val) => inputEventChange(val, scope, 'factor')" />
</template>
</el-table-column>
</el-table>
<el-table border :data="tableData" tooltip-effect="light" style="height: 100%;" v-else>
<el-table-column label="字典名称" prop="label" width="140" />
<el-table-column prop="factor" label="因子" class-name="edit-col">
<template #default="scope">
<el-input v-model="scope.row.factor" placeholder="请输入"
@change="(val) => inputChange(val, scope, 'factor')"
@input="(val) => inputEventChange(val, scope, 'factor')" />
</template>
</el-table-column>
</el-table>
</div>
</template>
</Drawer>
</div>
</template>
<style scoped lang="scss">
.container_wrap {
overflow: hidden;
.content_main {
height: calc(100% - 45px);
overflow: hidden auto;
&.panel {
padding: 0 16px 16px;
}
:deep(.el-card) {
&#contract-signatory-wrap {
.card-body-content {
padding: 8px 16px;
}
}
}
.signatory-tags {
margin-top: 2px;
margin-bottom: 11px;
}
.table_panel_wrap {
margin-bottom: 4px;
.table_tool {
height: 36px;
display: flex;
justify-content: space-between;
align-items: center;
.tool_title {
display: flex;
justify-content: start;
flex: 1;
}
.title_text {
font-weight: 600;
margin-right: 8px;
}
.title_desc {
color: #b2b2b2;
flex: 1;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.tool_btns {
margin: 0;
border: none;
}
}
.table_panel {
margin-bottom: 4px;
height: 212px;
}
}
}
.btn-block {
width: 100%;
margin: 16px 0 8px;
}
.tool_btns {
height: 44px;
margin: 0 -8px;
display: flex;
justify-content: center;
align-items: center;
border-top: 1px solid #d9d9d9;
}
}
:deep(.el-drawer) {
.table_panel {
th {
text-align: center;
}
.el-table__cell {
&.edit-col {
padding: 4px 0;
.cell {
padding: 0 4px;
.el-input {
height: 28px;
}
}
}
}
}
}
</style>
......@@ -25,12 +25,13 @@ import {
getDatabase,
getRuleTypeList
} from '@/api/modules/dataQuality';
import { getDamCatalogList } from '@/api/modules/dataPricing';
import useDataQualityStore from "@/store/modules/dataQuality";
import { useValidator } from '@/hooks/useValidator';
import { TableColumnWidth } from '@/utils/enum';
const dataQualityStore = useDataQualityStore();
const userData = JSON.parse(localStorage.userData);
const { proxy } = getCurrentInstance() as any;
const { orderNum, description } = useValidator();
......@@ -39,6 +40,8 @@ const cacheStore = useCatchStore();
/** 可选择的质量规则列表。 */
const ruleTypeList: any = ref([]);
const productList: any = ref([]);
/** 质量规则集表对象。 */
const qualityModelTreeRef = ref();
/** 树选中不同层级的,代表的类型, model, group, table */
......@@ -281,7 +284,22 @@ const formItems: any = ref([
default: '',
maxlength: 6,
required: true
}, {
},
{
label: '数据产品',
type: 'select',
field: 'damGuid',
default: '',
placeholder: '请选择',
options: productList.value,
props: {
value: 'guid',
label: 'damName'
},
clearable: true,
filterable: true
},
{
label: '描述',
type: 'textarea',
placeholder: '请输入',
......@@ -929,6 +947,17 @@ onActivated(async () => {
}
})
// 获取数据产品列表
const getProducts = () => {
getDamCatalogList({ dataType: userData.superTubeFlag == 'Y' ? "P" : "D", sceneType: "Z" }).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || [];
productList.value = data;
formItems.value.at(-2).options = data;
}
})
}
onBeforeMount(() => {
getQualityGroupTreeData();
getGroupTableData();
......@@ -945,6 +974,7 @@ onBeforeMount(() => {
ElMessage.error(res.msg);
}
})
getProducts();
})
</script>
......
......@@ -18,7 +18,7 @@ import {
getImportData,
exportDictionary,
exportCollectTask,
// getImageContent
exportTemplate
} from '@/api/modules/queryService';
import {
parseAndDecodeUrl,
......@@ -29,6 +29,7 @@ import {
getDictionaryTree
} from '@/api/modules/dataInventory';
import { commonPageConfig } from '@/utils/enum';
import { getDemandTreeList, getDiseaseAll } from '@/api/modules/dataPricing';
const { proxy } = getCurrentInstance() as any;
......@@ -156,6 +157,29 @@ const getDictList = () => {
})
}
// 获取需求表树形数据
const getTreeData = () => {
if (isfileImport == '7') {
getDemandTreeList({ pageIndex: 1, pageSize: 100000, isCatalog: 'N' }).then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data?.records || [];
dictionaryList.value = data;
} else {
proxy.$ElMessage.error(res.msg);
}
})
} else {
getDiseaseAll().then((res: any) => {
if (res.code == proxy.$passCode) {
const data = res.data || [];
dictionaryList.value = data;
} else {
proxy.$ElMessage.error(res.msg);
}
})
}
}
const tabsChange = (name) => {
tabsActiveName.value = name
let info: any = {
......@@ -283,11 +307,16 @@ const batching = (type) => {
}
open("此操作将永久删除, 是否继续?", "warning", true);
} else if (type === 'importFile') {
if (isfileImport == '2' || isfileImport == '4') {
if (isfileImport == '2' || isfileImport == '4' || isfileImport == '7' || isfileImport == '8') {
dialogInfo.value.header.title = '导入数据'
dialogInfo.value.type = 'upload'
dialogInfo.value.size = isfileImport == '4' ? 600 : 500;
uploadFiles.value = []
if (isfileImport == '7') {
uploadSteps.value[0].selectInfo.options = dictionaryList.value;
} else if (isfileImport == '8') {
uploadSteps.value[0].cascaderInfo.options = dictionaryList.value;
}
uploadInfo.value.uploadInfo.steps = uploadSteps.value
const content: any = [uploadInfo.value]
dialogInfo.value.contents = content
......@@ -377,6 +406,20 @@ const exportData = (ids: any = null) => {
res?.msg && ElMessage.error(res?.msg);
}
});
} else if (tabsActiveName.value == 'importFile' && isfileImport == '7') {
exportTemplate({
bizGuid: '',
importType: "0046"
}).then((res: any) => {
download(res, '需求表管理导入模板.xlsx', 'excel')
});
} else if (tabsActiveName.value == 'importFile' && isfileImport == '8') {
exportTemplate({
bizGuid: '',
importType: "0047"
}).then((res: any) => {
download(res, '疾病管理导入模板.xlsx', 'excel')
});
}
}
......@@ -390,11 +433,11 @@ const importData = (info) => {
// dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
return
}
uploadFiles.value.forEach((item: any, index: number) => {
params.append("file", item.raw);
});
let paramUrl = '';
if (isfileImport == '2') {
uploadFiles.value.forEach((item: any, index: number) => {
params.append("file", item.raw);
});
paramUrl = `ms-daop-zcgl-asset-dam-service/dam-catalog-table/excel-by-subject-guid?staffGuid=${userData.staffGuid}&subjectGuid=${route.query.bizGuid}`
} else if (isfileImport == '4') {
if (!info.databaseNameZh) {
......@@ -411,14 +454,17 @@ const importData = (info) => {
})
return;
}
uploadFiles.value.forEach((item: any, index: number) => {
params.append("uploadFile", item.raw);
});
paramUrl = `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-collect-task/meta-collect-import?staffGuid=${userData.staffGuid}&databaseNameZh=${info.databaseNameZh}&databaseNameEn=${info.databaseNameEn}&isCover=${info.isCover}`
} else if (isfileImport == '7') {
info.bizGuid = '0046';
paramUrl = `${import.meta.env.VITE_APP_ADD_FILE}/import-data/import-common?importType=${importType.value}&staffGuid=${userData.staffGuid}&tenantGuid=${userData.tenantGuid}`
paramUrl += `&extendFields=${encodeURIComponent(JSON.stringify(info))}`
} else if (isfileImport == '8') {
info.bizGuid = '0047';
paramUrl = `${import.meta.env.VITE_APP_ADD_FILE}/import-data/import-common?importType=${importType.value}&staffGuid=${userData.staffGuid}&tenantGuid=${userData.tenantGuid}`
paramUrl += `&extendFields=${encodeURIComponent(JSON.stringify(info))}`
} else {
uploadFiles.value.forEach((item: any, index: number) => {
params.append("file", item.raw);
});
paramUrl = `${import.meta.env.VITE_APP_ADD_FILE}/import-data/import-common?importType=${importType.value}&staffGuid=${userData.staffGuid}&tenantGuid=${userData.tenantGuid}&dataType=2`
if (info && Object.keys(info).length) {
paramUrl += `&extendFields=${encodeURIComponent(JSON.stringify(info))}`
......@@ -452,6 +498,10 @@ const dialogBtnClick = (btn, info) => {
if (dialogInfo.value.type == 'upload') {
if (tabsActiveName.value == 'dictionary') {
importData({ bizGuid: dictionaryGuid.value })
} else if (tabsActiveName.value == 'importFile' && isfileImport == '7') {
importData(info)
} else if (tabsActiveName.value == 'importFile' && isfileImport == '8') {
importData(info)
} else {
importData(info)
}
......@@ -478,6 +528,10 @@ const setUploadInfo = () => {
importType.value = '0034';
} else if (isfileImport == '4') {
importType.value = '0042';
} else if (isfileImport == '7') {
importType.value = '0046';
} else if (isfileImport == '8') {
importType.value = '0047';
} else {
importType.value = '0033';
}
......@@ -641,6 +695,94 @@ const setUploadInfo = () => {
}
]
uploadInfo.value.uploadInfo.extraParams = { isCover: 'Y' }
} else if (tabsActiveName.value == 'importFile' && isfileImport == '7') {
uploadSteps.value = [
{
title: '1、导入前请先录入以下内容',
type: 'select',
selectInfo: {
label: '需求表名称',
placeholder: '请选择',
field: 'menuGuid',
default: '',
options: [],
props: {
label: "menuName",
value: "guid",
},
filterable: true,
clearable: true,
required: true,
style: {
width: '191px',
}
}
},
{
title: '2、请下载最新的模板,并按照模板格式准备需要导入的数据',
type: 'btn_down'
},
{
title: '3、选择准备好的文件导入',
type: 'btn_upload',
uploadInfo: {
action: '',
auto: false,
cover: true,
fileList: [],
accept: '.xlsx, .xls',
tips: '当前支持xls、xlsx文件,默认使用第一个sheet'
}
}
]
dictionaryGuid.value = uploadSetting.value?.dictionaryGuid || ''
uploadInfo.value.uploadInfo.extraParams = {
menuGuid: dictionaryGuid.value
}
} else if (tabsActiveName.value == 'importFile' && isfileImport == '8') {
uploadSteps.value = [
{
title: '1、导入前请先录入以下内容',
type: 'cascader',
cascaderInfo: {
label: '疾病名称',
placeholder: '',
field: 'parentGuid',
options: [],
default: '',
showAllLevels: false,
props: {
checkStrictly: true,
label: "diseaseName",
value: "guid",
children: 'childList',
emitPath: false
},
filterable: true,
clearable: true,
}
},
{
title: '2、请下载最新的模板,并按照模板格式准备需要导入的数据',
type: 'btn_down'
},
{
title: '3、选择准备好的文件导入',
type: 'btn_upload',
uploadInfo: {
action: '',
auto: false,
cover: true,
fileList: [],
accept: '.xlsx, .xls',
tips: '当前支持xls、xlsx文件,默认使用第一个sheet'
}
}
]
dictionaryGuid.value = uploadSetting.value?.dictionaryGuid || ''
uploadInfo.value.uploadInfo.extraParams = {
parentGuid: dictionaryGuid.value
}
} else {
uploadSteps.value = [
{
......@@ -674,7 +816,8 @@ onActivated(() => {
if (tabsActiveName.value == 'dictionary') {
getDictList();
}
setUploadInfo()
setUploadInfo();
(isfileImport == '7' || isfileImport == '8') && getTreeData();
})
</script>
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!