Commit 1853393b authored by chenjiahao's avatar chenjiahao

数据质量-质量模板

parent 81b6a215
......@@ -3,10 +3,10 @@
<div class="toolbar">
<div class="tools">
<label for="dataSource">数据源</label>
<select id="dataSource">
<option value="option1">ARGODB</option>
<option value="option2">MYSQL</option>
<option value="option3">GBASE</option>
<select id="dataSource" v-model="selectedDataSource">
<option value="ARGODB">ARGODB</option>
<option value="MYSQL">MYSQL</option>
<option value="GBASE">GBASE</option>
</select>
<a-button @click="handleTemplateConversion">模板转换</a-button>
<a-button @click="handleBackward">后退</a-button>
......@@ -18,60 +18,68 @@
</div>
<div class="editor">
<textarea placeholder="请输入内容...">{{ sql }}</textarea>
<textarea v-model="sql" placeholder="请输入内容..."></textarea>
</div>
<div class="w-full">
<Card style="margin-bottom: 8px">
<Tabs defaultActiveKey="基本信息" @change="handleChangeTab">
<TabPane tab="基本信息" key="基本信息">
<BasicForm @register="registerInfoForm" />
<div style="height: 300px; overflow: auto">
<BasicForm @register="registerInfoForm" />
</div>
</TabPane>
<TabPane tab="参数配置" key="参数配置">
<BasicTable @register="registerTable" />
<div style="height: 300px; overflow: auto">
<BasicTable @register="registerTable" />
</div>
</TabPane>
<TabPane tab="SQL语法" key="SQL语法">
<p>质量检查的执行,是通过SQL查询有问题的数据实现,形如</p>
<pre><code>SELECT COUNT(*) FROM ${table_a} WHERE ${column_a} is NULL or length(${column_a}) &lt; {arg1}</code></pre>
<p
>整个SQL分为【Select部分】、【From部分】、【Where部分】组成,并能通过【参数】设置变量。</p
>
<p>对于SQL编写有如下要求:</p>
<ol>
<li>
【Select部分】格式固定为“SELECT COUNT(*)”不允许修改。查到0条代表没有质量问题。
<pre><code>SELECT COUNT(*) FROM ${table_a} WHERE ${column_a} is NULL or length(${column_a}) &lt; {arg1}</code></pre>
</li>
<li>
【From部分】用于限定质量检查数据范围,可是表或者子查询。
<pre><code>SELECT COUNT(*) FROM ${table_a} WHERE ${column_a} is NULL or length(${column_a}) &lt; {arg1}</code></pre>
</li>
<li>
【Where部分】用于过滤质量检查数据,如非空检查用“WHERE ${column_a} IS NULL”。
<pre><code>SELECT COUNT(*) FROM ${table_a} WHERE ${column_a} is NULL or length(${column_a}) &lt; {arg1}</code></pre>
</li>
<li>
【参数】在运行时会替换成对应的值,当前支持数据表、字段,在生成质量规则时选择对应表/字段,另外也支持字符串和数字等,生成规则时要输入对应值。
<pre><code>SELECT COUNT(*) FROM ${table_a} WHERE ${column_a} is NULL or length(${column_a}) &lt; {arg1}</code></pre>
</li>
<li>
若【From部分】使用子查询,则【Where部分】中的字段参数使用默认的“数据字段类”即“table.column”可能会报错;需给【Where部分】的数据字段类单独配置字段参数,且需勾选“仅字段”,即以“columnn”执行。
<pre><code>SELECT COUNT(*) FROM ( SELECT ${column_a1} FROM ${table_a} ) a where ${column_a2} is NULL</code></pre>
</li>
<li>
若【From部分】嵌套了主体类参数的子查询,可能会导致系统无法识别主键,无法统计问题数据或是报告图表。
<pre><code>SELECT COUNT(*) FROM ( SELECT * FROM ${subject_a} where ${column_a1} is not null ) a where ${column_a2} is NULL</code></pre>
</li>
<li>
【参数】公共代码类在值域检查时,默认为“字符型”检查,即SQL中使用单引号引用代码值,字符型检查SQL如下:
<pre><code>SELECT COUNT(*) FROM TABLE_A WHERE SEX NOT IN ('0', '1', '2', '3', '9')</code></pre>
如需进行“数值型”值域检查,请勾选“数值型”。数值型检查SQL如下:
<pre><code>SELECT COUNT(*) FROM TABLE_A WHERE SEX NOT IN (0, 1, 2, 3, 9)</code></pre>
</li>
</ol>
<div style="height: 300px; overflow: auto">
<p>质量检查的执行,是通过SQL查询有问题的数据实现,形如</p>
<pre><code>SELECT COUNT(*) FROM ${table_a} WHERE ${column_a} is NULL or length(${column_a}) &lt; {arg1}</code></pre>
<p
>整个SQL分为【Select部分】、【From部分】、【Where部分】组成,并能通过【参数】设置变量。</p
>
<p>对于SQL编写有如下要求:</p>
<ol>
<li>
【Select部分】格式固定为“SELECT COUNT(*)”不允许修改。查到0条代表没有质量问题。
<pre><code>SELECT COUNT(*) FROM ${table_a} WHERE ${column_a} is NULL or length(${column_a}) &lt; {arg1}</code></pre>
</li>
<li>
【From部分】用于限定质量检查数据范围,可是表或者子查询。
<pre><code>SELECT COUNT(*) FROM ${table_a} WHERE ${column_a} is NULL or length(${column_a}) &lt; {arg1}</code></pre>
</li>
<li>
【Where部分】用于过滤质量检查数据,如非空检查用“WHERE ${column_a} IS NULL”。
<pre><code>SELECT COUNT(*) FROM ${table_a} WHERE ${column_a} is NULL or length(${column_a}) &lt; {arg1}</code></pre>
</li>
<li>
【参数】在运行时会替换成对应的值,当前支持数据表、字段,在生成质量规则时选择对应表/字段,另外也支持字符串和数字等,生成规则时要输入对应值。
<pre><code>SELECT COUNT(*) FROM ${table_a} WHERE ${column_a} is NULL or length(${column_a}) &lt; {arg1}</code></pre>
</li>
<li>
若【From部分】使用子查询,则【Where部分】中的字段参数使用默认的“数据字段类”即“table.column”可能会报错;需给【Where部分】的数据字段类单独配置字段参数,且需勾选“仅字段”,即以“columnn”执行。
<pre><code>SELECT COUNT(*) FROM ( SELECT ${column_a1} FROM ${table_a} ) a where ${column_a2} is NULL</code></pre>
</li>
<li>
若【From部分】嵌套了主体类参数的子查询,可能会导致系统无法识别主键,无法统计问题数据或是报告图表。
<pre><code>SELECT COUNT(*) FROM ( SELECT * FROM ${subject_a} where ${column_a1} is not null ) a where ${column_a2} is NULL</code></pre>
</li>
<li>
【参数】公共代码类在值域检查时,默认为“字符型”检查,即SQL中使用单引号引用代码值,字符型检查SQL如下:
<pre><code>SELECT COUNT(*) FROM TABLE_A WHERE SEX NOT IN ('0', '1', '2', '3', '9')</code></pre>
如需进行“数值型”值域检查,请勾选“数值型”。数值型检查SQL如下:
<pre><code>SELECT COUNT(*) FROM TABLE_A WHERE SEX NOT IN (0, 1, 2, 3, 9)</code></pre>
</li>
</ol>
</div>
</TabPane>
</Tabs>
</Card>
</div>
<RunModal @register="registerRunModal" />
<SelectDatasourceModal @register="registerSelectDatasourceModal" />
</PageWrapper>
</template>
<script lang="ts" setup>
......@@ -82,7 +90,12 @@
import BasicTable from '@/components/Table/src/BasicTable.vue';
import { useTable } from '@/components/Table';
import { useRoute } from 'vue-router';
import { router } from '@/router';
import { tableList } from './mock';
import RunModal from '@/views/dataQuality/template/runModal.vue';
import { useModal } from '@/components/Modal';
import { useMessage } from '@/hooks/web/useMessage';
import SelectDatasourceModal from '@/views/dataQuality/template/selectDatasourceModal.vue';
const formItem = reactive({
templateName: '',
......@@ -92,6 +105,7 @@
const route = useRoute();
const businessId = ref();
const sql = ref('');
const selectedDataSource = ref('ARGODB');
const formSchemaTemplate: FormSchema[] = [
{
field: 'name',
......@@ -141,7 +155,7 @@
},
},
];
const { createMessage, createConfirm } = useMessage();
const [registerInfoForm, { setFieldsValue, updateSchema, resetFields, validate }] = useForm({
labelWidth: 100,
baseColProps: { lg: 24, md: 24 },
......@@ -196,44 +210,19 @@
},
];
const tableData = ref([
{
key: '1',
parameter: 'table_a',
type: '数据表类',
description: '数据表',
},
{
key: '2',
parameter: 'column_a',
type: '数据字段类',
description: '数据字段',
},
{
key: '3',
parameter: 'range_a',
type: '公共代码类',
description: '值域',
},
]);
const [
registerTable,
{ reload, updateTableDataRecord, getSearchInfo, getForm, getRowSelection },
] = useTable({
api: async (params) => {
const [registerTable] = useTable({
api: async () => {
const data = tableList.filter((item) => item.businessId == businessId.value);
const response = {
pageNu: '1',
pageSize: '10',
pages: '1',
total: tableData.value.length,
total: data[0].params.length,
code: '',
message: '',
data: [],
data: data[0].params,
};
var data = [];
data = tableData.value;
return { ...response, data: data };
return { ...response };
},
rowKey: 'key',
columns,
......@@ -244,10 +233,29 @@
},
});
const [registerRunModal, { openModal: openRunModal }] = useModal();
const [registerSelectDatasourceModal, { openModal: openSelectDatasourceModal }] = useModal();
function handleChangeTab(key: string) {
// 处理标签页切换逻辑...
}
function handleTemplateConversion() {
openSelectDatasourceModal(true, { currentDataSource: selectedDataSource.value });
}
function handleRun() {
const data = tableList.filter((item) => item.businessId == businessId.value);
openRunModal(true, {
...data[0],
});
}
function handleSave() {
createMessage.success('保存成功!');
router.back();
}
function saveTemplate() {
validate()
.then((values) => {
......
<template>
<BasicModal
width="60%"
v-bind="$attrs"
@register="registerModal"
title="查找路径"
@ok="handleSubmit"
>
<div class="path-selector">
<a-cascader
style="width: 60%"
v-model:value="value"
placeholder="Please select"
showSearch
change-on-select
:options="options"
@change="onChange"
/>
</div>
<template #footer>
<p> {{ text }} &nbsp; </p>
<a-button @click="handleCancel">取消</a-button>
<a-button type="primary" @click="handleSubmit">确定</a-button>
</template>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import { BasicModal, useModalInner } from '@/components/Modal';
import { useTable } from '@/components/Table';
import { Cascader as ACascader, CascaderProps } from 'ant-design-vue';
import { tableList } from './mock';
const emit = defineEmits(['success', 'register']);
const businessId = ref<number | undefined>();
// const popupVisible = ref(true);
// 初始化模态对话框
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
businessId.value = data.businessId;
setModalProps({ confirmLoading: false });
});
/** 确定按钮 */
async function handleSubmit() {
closeModal();
emit('success', text.value);
}
function handleCancel() {
closeModal();
}
const options: CascaderProps['options'] = [
{
value: '数据库对象资源',
label: '数据库对象资源',
children: [
{
value: '数据中台工作区01',
label: '数据中台工作区01',
children: [
{
value: 'ArgoDB_Dev01',
label: 'ArgoDB_Dev01',
children: [
{
value: 'dmtp01',
label: 'dmtp01',
children: [
{
value: 'eligible_graduates_list',
label: 'eligible_graduates_list',
},
{
value: 'graduates_info',
label: 'graduates_info',
},
{
value: 'graduates_info_id_masked',
label: 'graduates_info_id_masked',
},
{
value: 'landing_graduates_info',
label: 'landing_graduates_info',
},
{
value: 'landing_member_relationship_info',
label: 'landing_member_relationship_info',
},
{
value: 'lowincome_family_children_list',
label: 'lowincome_family_children_list',
},
{
value: 'lowincome_family_info',
label: 'lowincome_family_info',
},
{
value: 'lowincome_graduates_area_ratio',
label: 'lowincome_graduates_area_ratio',
},
{
value: 'lowincome_graduates_cnt',
label: 'lowincome_graduates_cnt',
},
{
value: 'lowincome_graduates_employment_cls',
label: 'lowincome_graduates_employment_cls',
},
{
value: 'lowincome_graduates_employment_rat',
label: 'lowincome_graduates_employment_rat',
},
{
value: 'test',
label: 'test',
},
],
},
],
},
],
},
],
},
];
const value = ref<string[]>([]);
const text = ref('');
const onChange: CascaderProps['onChange'] = (_value, selectedOptions) => {
text.value = selectedOptions.map((o) => o.label).join('/ ');
};
</script>
<style scoped>
.path-selector {
margin-bottom: 20px;
}
</style>
......@@ -31,6 +31,29 @@ export const tableList: any[] = [
'SELECT COUNT(*)\n' +
'FROM $(table a}\n' +
'WHERE ${column a}IS NOT NULL AND ${column a}NOT IN (${range a})',
params: [
{
key: '1',
parameter: 'table_a',
type: '数据表类',
description: '数据表',
value: '默认工作组/数据库a/数据表a',
},
{
key: '2',
parameter: 'column_a',
type: '数据字段类',
description: '数据字段',
value: '默认工作组/数据库a/数据表a/字段a',
},
{
key: '3',
parameter: 'range_a',
type: '公共代码类',
description: '值域',
value: '公共代码/数据范围a',
},
],
},
{
businessId: 3,
......
<template>
<BasicModal width="35%" v-bind="$attrs" @register="registerModal" title="运行" @ok="handleSubmit">
<BasicModal width="40%" v-bind="$attrs" @register="registerModal" title="运行" @ok="handleSubmit">
<p>请填写当前SQL中引用的参数的值</p>
<BasicTable @register="registerTable" />
<BasicTable @register="registerTable" style="height: 370px">
<template #value="{ text, record }">
<div class="control-group">
<AInput style="width: 220px; border-radius: 5px 0 0 5px" :value="text" />
<a-button type="primary" @click="handleChose()" style="border-radius: 0 5px 5px 0"
>选择</a-button
>
</div>
</template>
</BasicTable>
<div style="width: 100%; margin-top: 20px; height: 120px" v-show="isRun">
<span>运行结果:0</span>
</div>
<template #footer>
<a-button @click="handleCancel">取消</a-button>
<a-button type="primary" @click="handleRun">运行</a-button>
</template>
</BasicModal>
<FindPath @register="registerFindPathModal" @success="handleSuccess" />
</template>
<script lang="ts" setup>
import { ref, unref } from 'vue';
import { BasicModal, useModalInner } from '@/components/Modal';
import { ref, onMounted } from 'vue';
import { BasicModal, useModal, useModalInner } from '@/components/Modal';
import { BasicTable, useTable } from '@/components/Table';
import { tableList } from './mock';
import FindPath from '@/views/dataQuality/template/findPath.vue';
import { BasicForm, useForm } from '@/components/Form';
import { importFormSchema } from './tempalte.data';
const emit = defineEmits(['success', 'register']);
const isUpdate = ref(true);
const isMove = ref(false);
const rowId = ref('');
const businessId = ref();
const isRun = ref(false);
//初始化弹框
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields();
console.log(data);
businessId.value = data.businessId;
setModalProps({ confirmLoading: false });
isUpdate.value = !!data?.isUpdate;
isMove.value = !!data?.isMove;
if (unref(isUpdate)) {
// 获取行数据的id
rowId.value = data.record.businessId;
// 塞值
setFieldsValue({
...data.record,
});
}
});
const columns = [
......@@ -41,78 +50,37 @@
title: '参数类型',
dataIndex: 'type',
key: 'type',
edit: true,
// editable: true,
editComponent: 'Select',
editComponentProps: {
options: [
{
label: '数据表类',
value: '1',
},
{
label: '数据字段类',
value: '2',
},
{
label: '公共代码类',
value: '3',
},
{
label: '全局参数类',
value: '4',
},
],
},
},
{
title: '描述(选填)',
title: '模板描述',
dataIndex: 'description',
key: 'description',
edit: true,
editable: true,
editComponent: 'Input',
},
];
const tableData = ref([
{
key: '1',
parameter: 'table_a',
type: '数据表类',
description: '数据表',
},
{
key: '2',
parameter: 'column_a',
type: '数据字段类',
description: '数据字段',
},
{
key: '3',
parameter: 'range_a',
type: '公共代码类',
description: '值域',
title: '值',
dataIndex: 'value',
key: 'value',
// edit: true,
// editable: true,
// editComponent: 'Input',
width: 250,
slots: { customRender: 'value' },
},
]);
];
const [
registerTable,
{ reload, updateTableDataRecord, getSearchInfo, getForm, getRowSelection },
] = useTable({
api: async (params) => {
const [registerTable] = useTable({
api: async () => {
const data = tableList.filter((item) => item.businessId == businessId.value);
const response = {
pageNu: '1',
pageSize: '10',
pages: '1',
total: tableData.value.length,
total: data[0].params.length,
code: '',
message: '',
data: [],
data: data[0].params,
};
var data = [];
data = tableData.value;
return { ...response, data: data };
return { ...response };
},
rowKey: 'key',
columns,
......@@ -127,4 +95,28 @@
async function handleSubmit() {
closeModal();
}
function handleCancel() {
closeModal();
}
function handleRun() {
isRun.value = true;
}
const [registerFindPathModal, { openModal: openFindPathModal }] = useModal();
function handleChose() {
openFindPathModal(true);
// console.log('record', record);
}
const selectedPath = ref('');
function handleSuccess(path) {
selectedPath.value = path;
console.log('Selected Path:', selectedPath.value);
// 在这里处理接收到的路径信息
}
</script>
<style scoped>
.control-group {
display: flex;
align-items: center;
}
</style>
<template>
<BasicModal
width="500px"
v-bind="$attrs"
@register="registerModal"
title="选择数据源类型"
@ok="handleSubmit"
@cancel="handleCancel"
>
<div class="modal-content">
<p>当前数据源类型:</p>
<span>{{ currentDataSource }}</span>
<br />
<p>选择需要转换的数据类型</p>
<ACheckboxGroup v-model:value="selectedDataTypes" class="checkbox-group">
<ACheckbox :value="'DB2'">DB2</ACheckbox>
<ACheckbox :value="'HIVE'">HIVE</ACheckbox>
<ACheckbox :value="'IMPALA'">IMPALA</ACheckbox>
<ACheckbox :value="'INCEPTOR'">INCEPTOR</ACheckbox>
<ACheckbox :value="'MYSQL'">MYSQL</ACheckbox>
<ACheckbox :value="'ORACLE'">ORACLE</ACheckbox>
<ACheckbox :value="'SQL_SERVER'">SQL_SERVER</ACheckbox>
</ACheckboxGroup>
</div>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import { BasicModal, useModalInner } from '@/components/Modal';
import { Checkbox as ACheckbox, CheckboxGroup as ACheckboxGroup } from 'ant-design-vue';
// 定义发射的事件
const emit = defineEmits(['success', 'register']);
// 当前数据源类型
const currentDataSource = ref('ARGODB');
// 已选择的数据类型
const selectedDataTypes = ref<string[]>(['INCEPTOR', 'MYSQL']);
// 初始化模态框
const [registerModal, { closeModal, setModalProps }] = useModalInner((data) => {
if (data && data.currentDataSource) {
currentDataSource.value = data.currentDataSource;
}
setModalProps({ confirmLoading: false });
});
// 提交处理
const handleSubmit = () => {
// 发射 success 事件,并附带已选择的数据类型
emit('success', selectedDataTypes.value);
closeModal();
};
// 取消处理
const handleCancel = () => {
closeModal();
};
</script>
<style scoped>
.modal-content {
padding: 20px;
}
.checkbox-group {
display: flex;
flex-direction: column;
}
.checkbox-item {
margin-bottom: 10px;
}
</style>
......@@ -211,8 +211,6 @@ export const formSchemaTemplate: any = [
},
},
];
export const importFormSchema: any[] = [
{
field: 'fileMethods',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment