diff --git a/webapp/packages/supersonic-fe/package.json b/webapp/packages/supersonic-fe/package.json index 8aa1191c7..df1cc8181 100644 --- a/webapp/packages/supersonic-fe/package.json +++ b/webapp/packages/supersonic-fe/package.json @@ -16,6 +16,7 @@ "dev:inner": "npm run start:dev", "no:dev:os": "NODE_OPTIONS=--openssl-legacy-provider npm run start:osdev", "no:dev:inner": "NODE_OPTIONS=--openssl-legacy-provider npm run start:dev", + "no:build:inner": "cross-env NODE_OPTIONS=--openssl-legacy-provider REACT_APP_ENV=prod APP_TARGET=inner umi build", "gh-pages": "gh-pages -d dist", "i18n-remove": "pro i18n-remove --locale=zh-CN --write", "postinstall": "umi g tmp", diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/DataSourceCreateForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/DataSourceCreateForm.tsx index 3bcaf5343..8f26d54d5 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/DataSourceCreateForm.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/DataSourceCreateForm.tsx @@ -5,11 +5,13 @@ import DataSourceFieldForm from './DataSourceFieldForm'; import { formLayout } from '@/components/FormHelper/utils'; import { EnumDataSourceType } from '../constants'; import styles from '../style.less'; -import { updateModel, createModel, getColumns } from '../../service'; +import { updateModel, createModel, getColumns, getUnAvailableItem } from '../../service'; import type { Dispatch } from 'umi'; import type { StateType } from '../../model'; import { connect } from 'umi'; import { ISemantic, IDataSource } from '../../data'; +import { isArrayOfValues } from '@/utils/utils'; +import EffectDimensionAndMetricTipsModal from './EffectDimensionAndMetricTipsModal'; export type CreateFormProps = { domainManger: StateType; @@ -54,6 +56,12 @@ const DataSourceCreateForm: React.FC = ({ const [saveLoading, setSaveLoading] = useState(false); const [hasEmptyNameField, setHasEmptyNameField] = useState(false); const [formDatabaseId, setFormDatabaseId] = useState(); + const [queryParamsState, setQueryParamsState] = useState({}); + const [effectTipsModalOpenState, setEffectTipsModalOpenState] = useState(false); + const [effectTipsData, setEffectTipsData] = useState< + (ISemantic.IDimensionItem | ISemantic.IMetricItem)[] + >([]); + const formValRef = useRef(initFormVal as any); const [form] = Form.useForm(); const { databaseConfigList, selectModelId: modelId, selectDomainId } = domainManger; @@ -84,6 +92,58 @@ const DataSourceCreateForm: React.FC = ({ const forward = () => setCurrentStep(currentStep + 1); const backward = () => setCurrentStep(currentStep - 1); + const checkAvailableItem = async (fields: string[] = []) => { + if (!modelItem) { + return false; + } + const originalFields = modelItem.modelDetail?.fields || []; + const hasRemoveFields = originalFields.reduce( + (fieldList: string[], item: IDataSource.IDataSourceDetailFieldsItem) => { + const { fieldName } = item; + if (!fields.includes(fieldName)) { + fieldList.push(fieldName); + } + return fieldList; + }, + [], + ); + if (!isArrayOfValues(hasRemoveFields)) { + return false; + } + const { code, data, msg } = await getUnAvailableItem({ + modelId: modelItem?.id, + fields: hasRemoveFields, + }); + if (code === 200 && data) { + const { metricResps = [], dimensionResps = [] } = data; + if (!isArrayOfValues(metricResps) && !isArrayOfValues(dimensionResps)) { + return false; + } + setEffectTipsData([...metricResps, ...dimensionResps]); + setEffectTipsModalOpenState(true); + return true; + } + message.error(msg); + return false; + }; + + const saveModel = async (queryParams: any) => { + setSaveLoading(true); + const queryDatasource = isEdit ? updateModel : createModel; + const { code, msg, data } = await queryDatasource(queryParams); + setSaveLoading(false); + if (code === 200) { + message.success('保存模型成功!'); + onSubmit?.({ + ...queryParams, + ...data, + resData: data, + }); + return; + } + message.error(msg); + }; + const getFieldsClassify = (fieldsList: any[]) => { const classify = fieldsList.reduce( (fieldsClassify, item: any) => { @@ -129,6 +189,7 @@ const DataSourceCreateForm: React.FC = ({ case EnumDataSourceType.PRIMARY: fieldsClassify.identifiers.push({ bizName: fieldName, + isCreateDimension, name, type, entityNames, @@ -156,6 +217,7 @@ const DataSourceCreateForm: React.FC = ({ ); return classify; }; + const handleNext = async (saveState: boolean = false) => { const fieldsValue = await form.validateFields(); @@ -175,7 +237,6 @@ const DataSourceCreateForm: React.FC = ({ if (!saveState && currentStep < 1) { forward(); } else { - setSaveLoading(true); const { dbName, tableName } = submitForm; const queryParams = { ...submitForm, @@ -190,19 +251,12 @@ const DataSourceCreateForm: React.FC = ({ sqlQuery: sql, }, }; - const queryDatasource = isEdit ? updateModel : createModel; - const { code, msg, data } = await queryDatasource(queryParams); - setSaveLoading(false); - if (code === 200) { - message.success('保存模型成功!'); - onSubmit?.({ - ...queryParams, - ...data, - resData: data, - }); + setQueryParamsState(queryParams); + const checkState = await checkAvailableItem(fieldColumns.map((item) => item.nameEn)); + if (checkState) { return; } - message.error(msg); + saveModel(queryParams); } }; @@ -484,6 +538,17 @@ const DataSourceCreateForm: React.FC = ({ {renderContent()} {children} + + { + setEffectTipsModalOpenState(false); + }} + onOk={() => { + saveModel(queryParamsState); + }} + /> ); }; diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/DataSourceFieldForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/DataSourceFieldForm.tsx index 388059740..936a6edbf 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/DataSourceFieldForm.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/DataSourceFieldForm.tsx @@ -302,6 +302,7 @@ const DataSourceFieldForm: React.FC = ({ fields, sql, onFieldChange, onSq dataSource={fields} columns={columns} rowKey="bizName" + virtual pagination={false} scroll={{ y: 500 }} /> diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/EffectDimensionAndMetricTipsModal.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/EffectDimensionAndMetricTipsModal.tsx new file mode 100644 index 000000000..87209f908 --- /dev/null +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/EffectDimensionAndMetricTipsModal.tsx @@ -0,0 +1,153 @@ +import React, { useState, useEffect } from 'react'; +import { Form, Input, Modal, Table, Tag, message } from 'antd'; +import TransTypeTag from '../../components/TransTypeTag'; +import { SemanticNodeType } from '../../enum'; +import { ISemantic } from '../../data'; +import styles from '../../components/style.less'; +import { StatusEnum } from '../../enum'; +import { batchUpdateMetricStatus, batchUpdateDimensionStatus } from '../../service'; + +type Props = { + open: boolean; + tableDataSource: (ISemantic.IDimensionItem | ISemantic.IMetricItem)[]; + onOk?: () => void; + onCancel?: () => void; +}; + +const EffectDimensionAndMetricTipsModal: React.FC = ({ + open, + tableDataSource, + onOk, + onCancel, +}) => { + const [loading, setLoading] = useState(false); + useEffect(() => {}, []); + + const queryBatchUpdateDimensionStatus = async (ids: React.Key[], status: StatusEnum) => { + if (Array.isArray(ids) && ids.length === 0) { + return; + } + setLoading(true); + const { code, msg } = await batchUpdateDimensionStatus({ + ids, + status, + }); + setLoading(false); + if (code === 200) { + return; + } + message.error(msg); + }; + + const queryBatchUpdateMetricStatus = async (ids: React.Key[], status: StatusEnum) => { + if (Array.isArray(ids) && ids.length === 0) { + return; + } + const { code, msg } = await batchUpdateMetricStatus({ + ids, + status, + }); + if (code === 200) { + return; + } + message.error(msg); + }; + + const updateState = async () => { + const dimensionIds: React.Key[] = []; + const metricIds: React.Key[] = []; + tableDataSource.forEach((item) => { + if (item.typeEnum === SemanticNodeType.DIMENSION) { + dimensionIds.push(item.id); + } + if (item.typeEnum === SemanticNodeType.METRIC) { + metricIds.push(item.id); + } + }); + if (dimensionIds.length > 0) { + await queryBatchUpdateDimensionStatus(dimensionIds, StatusEnum.UNAVAILABLE); + } + if (metricIds.length > 0) { + await queryBatchUpdateMetricStatus(metricIds, StatusEnum.UNAVAILABLE); + } + onOk?.(); + }; + + const columns = [ + { + title: '名称', + dataIndex: 'name', + width: 100, + }, + { + title: '英文名称', + dataIndex: 'bizName', + width: 100, + }, + { + title: '类型', + dataIndex: 'typeEnum', + width: 80, + render: (transType: SemanticNodeType) => { + return ; + }, + }, + { + title: '创建人', + width: 100, + dataIndex: 'createdBy', + }, + ]; + + return ( + { + updateState(); + }} + // footer={renderFooter()} + onCancel={() => { + onCancel?.(); + }} + > +

+ 检测到模型信息变更会对以下 + + 指标 + + 和 + + 维度 + + 产生影响。如确认保存,将会自动置为 + + 不可用状态 + + 。 +

+ + + ); +}; + +export default EffectDimensionAndMetricTipsModal; diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DimensionMetricRelationTableTransfer.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DimensionMetricRelationTableTransfer.tsx index 97fc4a121..47ebe191f 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DimensionMetricRelationTableTransfer.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DimensionMetricRelationTableTransfer.tsx @@ -62,7 +62,7 @@ const DimensionMetricRelationTableTransfer: React.FC = ({ return { ...item, transType, - disabled: checkedMap[id]?.inheritFromModel, + disabled: checkedMap[id]?.inheritedFromModel, key: `${id}`, }; }); @@ -73,6 +73,7 @@ const DimensionMetricRelationTableTransfer: React.FC = ({ if (!Array.isArray(relationsInitialValue)) { return; } + console.log(relationsInitialValue, 'relationsInitialValue'); const ids = relationsInitialValue.map((item) => `${item.dimensionId}`); const relationMap = relationsInitialValue.reduce((relationCheckedMap, item: any) => { const { dimensionId } = item; @@ -116,7 +117,7 @@ const DimensionMetricRelationTableTransfer: React.FC = ({ (relationList: ISemantic.IDrillDownDimensionItem[], dimensionId: string) => { const target = relationCheckedMap[dimensionId]; if (target) { - if (target.inheritFromModel === true && !target.necessary) { + if (target.inheritedFromModel === true && !target.necessary) { return relationList; } relationList.push(target); @@ -124,7 +125,7 @@ const DimensionMetricRelationTableTransfer: React.FC = ({ relationList.push({ dimensionId: Number(dimensionId), necessary: false, - inheritFromModel: false, + inheritedFromModel: false, }); } return relationList; @@ -238,7 +239,7 @@ const DimensionMetricRelationTableTransfer: React.FC = ({ }, selectedRowKeys: listSelectedKeys, renderCell: function (checked, record, index, originNode) { - if (checkedMap[record.id]?.inheritFromModel === true) { + if (checkedMap[record.id]?.inheritedFromModel === true) { return ( @@ -256,7 +257,7 @@ const DimensionMetricRelationTableTransfer: React.FC = ({ dataSource={filteredItems as any} size="small" rowClassName={(record) => { - if (checkedMap[record.id]?.inheritFromModel) { + if (checkedMap[record.id]?.inheritedFromModel) { return 'inherit-from-model-row'; } return ''; diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricFieldFormTable.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricFieldFormTable.tsx index 8ab098e46..c287bd7e4 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricFieldFormTable.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricFieldFormTable.tsx @@ -9,7 +9,7 @@ import { ISemantic } from '../data'; type Props = { typeParams: ISemantic.IFieldTypeParams; - fieldList: string[]; + fieldList: ISemantic.IFieldTypeParamsItem[]; onFieldChange: (fields: ISemantic.IFieldTypeParamsItem[]) => void; onSqlChange: (sql: string) => void; }; @@ -85,10 +85,15 @@ const MetricFieldFormTable: React.FC = ({ search: { placeholder: '请输入字段名称', onSearch: (value: string) => { + if (!value) { + setTableData(fieldList); + return; + } + setTableData( - fieldList.reduce((data: ISemantic.IFieldTypeParamsItem[], fieldName) => { - if (fieldName.includes(value)) { - data.push({ fieldName }); + fieldList.reduce((data: ISemantic.IFieldTypeParamsItem[], item) => { + if (item.fieldName.includes(value)) { + data.push(item); } return data; }, []), diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricInfoCreateForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricInfoCreateForm.tsx index 3f9056040..7f273f1f2 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricInfoCreateForm.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricInfoCreateForm.tsx @@ -100,7 +100,7 @@ const MetricInfoCreateForm: React.FC = ({ const [defineType, setDefineType] = useState(METRIC_DEFINE_TYPE.MEASURE); const [createNewMetricList, setCreateNewMetricList] = useState([]); - const [fieldList, setFieldList] = useState([]); + const [fieldList, setFieldList] = useState([]); const [isPercentState, setIsPercentState] = useState(false); const [isDecimalState, setIsDecimalState] = useState(false); const [hasMeasuresState, setHasMeasuresState] = useState(true); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricMeasuresFormTable.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricMeasuresFormTable.tsx index a97c3a3ee..0cc2013be 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricMeasuresFormTable.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricMeasuresFormTable.tsx @@ -131,8 +131,16 @@ const MetricMeasuresFormTable: React.FC = ({ search: { placeholder: '请输入度量名称', onSearch: (value: string) => { + const datasource = + datasourceId && Array.isArray(measuresList) + ? measuresList.filter((item) => item.datasourceId === datasourceId) + : measuresList; + if (!value) { + setTableData(datasource); + return; + } setTableData( - [...tableData].reduce((data: ISemantic.IMeasure[], item: ISemantic.IMeasure) => { + [...datasource].reduce((data: ISemantic.IMeasure[], item: ISemantic.IMeasure) => { if (item.bizName.includes(value)) { data.push(item); } diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricTableColumnRender.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricTableColumnRender.tsx index 6068dd655..6b17ffb2d 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricTableColumnRender.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricTableColumnRender.tsx @@ -282,6 +282,12 @@ export const ColumnsConfig = { label: '已删除', }; break; + case StatusEnum.UNAVAILABLE: + tagProps = { + color: 'default', + label: '不可用', + }; + break; default: break; } diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/data.d.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/data.d.ts index ae38f9e78..6a4d0db67 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/data.d.ts +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/data.d.ts @@ -234,7 +234,7 @@ export declare namespace ISemantic { interface IDrillDownDimensionItem { dimensionId: number; - inheritFromModel?: boolean; + inheritedFromModel?: boolean; necessary?: boolean; } diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/enum.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/enum.ts index 7029c5ae5..65f19d404 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/enum.ts +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/enum.ts @@ -28,11 +28,12 @@ export enum DictTaskState { } export enum StatusEnum { + UNKNOWN = -1, INITIALIZED = 0, ONLINE = 1, OFFLINE = 2, DELETED = 3, - UNKNOWN = -1, + UNAVAILABLE = 4, } export enum OperatorEnum { diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/service.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/service.ts index 2a10330dd..9ea62d61f 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/service.ts +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/service.ts @@ -426,6 +426,13 @@ export function deleteModel(modelId: number): Promise { }); } +export function getUnAvailableItem(data: any): Promise { + return request(`${process.env.API_BASE_URL}model/getUnAvailableItem`, { + method: 'POST', + data, + }); +} + export function getModelDetail(data: any): Promise { return request.get(`${process.env.API_BASE_URL}model/getModel/${data.modelId}`); } diff --git a/webapp/packages/supersonic-fe/webapp_1225.zip b/webapp/packages/supersonic-fe/webapp_1225.zip deleted file mode 100644 index 396f5cfdf..000000000 Binary files a/webapp/packages/supersonic-fe/webapp_1225.zip and /dev/null differ