2ae6fed9 by lihua

支持根据标准新建数据集

1 parent be01a38f
......@@ -404,7 +404,137 @@ export const getSankeyData = (guid) => request({
/** 数仓目录树列表查询 */
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_APP_DATA_DELIVERY}delivery/ms-daop-data-plan-service/data-catalog-directory/tree-list`,
url: `http://localhost:9000/delivery/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: `http://localhost:9000/delivery/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: `http://localhost:9000/delivery/ms-daop-data-plan-service/data-catalog-subject/get-dim-list`,
method: 'get'
})
\ No newline at end of file
......
......@@ -277,6 +277,44 @@ const routes: RouteRecordRaw[] = [
}
}
},
{
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',
......
<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-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;
tableCreateInfoLoading.value = true;
getMetaStandardDsField(route.query.metaStandard).then((res: any) => {
tableCreateInfoLoading.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.fullscreen.lock="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
......@@ -228,14 +228,7 @@ onBeforeUnmount(() => {
})
/** 数仓目录树形列表 */
const dataCatalogList = ref([{
name: '测试',
guid: '1',
children: [{
name: 'cesi',
guid: '1-1'
}]
}]);
const dataCatalogList = ref([]);
const formItems = ref([{
label: "数仓目录",
......@@ -299,12 +292,37 @@ const dialogBtnClick = (btn, info) => {
if (btn.value == 'submit') {
dialogInfo.value.visible = false;
if (selectDataCatalogNodeObj.value.parent.data.layereAttribute == 2) { //维度
window.open(`${import.meta.env.VITE_APP_DATA_DELIVERY}data-catalog/data-warehouse/dim-table-create-manual?domainGuid=${info.domainGuid}&domainName=${selectDataCatalogNodeObj.value.data.name}&metaStandard=${contextNodeData.value.guid}`);
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) {
window.open(`${import.meta.env.VITE_APP_DATA_DELIVERY}data-catalog/data-warehouse/table-create-manual?domainGuid=${info.domainGuid}&domainName=${selectDataCatalogNodeObj.value.data.name}&layereAttribute=${selectDataCatalogNodeObj.value.parent.data.layereAttribute}&metaStandard=${contextNodeData.value.guid}`);
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 {
window.open(`${import.meta.env.VITE_APP_DATA_DELIVERY}data-catalog/data-warehouse/table-create-manual?domainGuid=${info.domainGuid}&domainName=${selectDataCatalogNodeObj.value.data.name}&metaStandard=${contextNodeData.value.guid}`);
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') {
......
<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;
tableCreateInfoLoading.value = true;
getMetaStandardDsField(route.query.metaStandard).then((res: any) => {
tableCreateInfoLoading.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.fullscreen.lock="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>
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!