diff --git a/webapp/packages/supersonic-fe/config/defaultSettings.ts b/webapp/packages/supersonic-fe/config/defaultSettings.ts index 8a6b84a8a..02aa837b4 100644 --- a/webapp/packages/supersonic-fe/config/defaultSettings.ts +++ b/webapp/packages/supersonic-fe/config/defaultSettings.ts @@ -21,6 +21,6 @@ const Settings: ProLayoutProps & { // }, }; export const publicPath = '/webapp/'; -export const basePath = '/'; +export const basePath = '/webapp/'; export default Settings; diff --git a/webapp/packages/supersonic-fe/src/components/BatchCtrlDropDownButton/index.tsx b/webapp/packages/supersonic-fe/src/components/BatchCtrlDropDownButton/index.tsx index d0d3578e7..7219e5eeb 100644 --- a/webapp/packages/supersonic-fe/src/components/BatchCtrlDropDownButton/index.tsx +++ b/webapp/packages/supersonic-fe/src/components/BatchCtrlDropDownButton/index.tsx @@ -65,6 +65,30 @@ const BatchCtrlDropDownButton: FC = ({ icon: , disabled: disabledList?.includes('batchSensitiveLevel'), }, + batchDimensionValueBlackList: { + key: 'batchDimensionValueBlackList', + label: '批量导入黑名单', + icon: , + disabled: disabledList?.includes('batchDimensionValueBlackList'), + }, + batchDimensionValueWhiteList: { + key: 'batchDimensionValueWhiteList', + label: '批量导入白名单', + icon: , + disabled: disabledList?.includes('batchDimensionValueWhiteList'), + }, + batchRemoveDimensionValueBlackList: { + key: 'batchRemoveDimensionValueBlackList', + label: '批量移除黑名单', + icon: , + disabled: disabledList?.includes('batchRemoveDimensionValueBlackList'), + }, + batchRemoveDimensionValueWhiteList: { + key: 'batchRemoveDimensionValueWhiteList', + label: '批量移除白名单', + icon: , + disabled: disabledList?.includes('batchRemoveDimensionValueWhiteList'), + }, }; const extenderButtonList: any[] = extenderList.reduce((list: any[], key) => { diff --git a/webapp/packages/supersonic-fe/src/components/FormHelper/index.less b/webapp/packages/supersonic-fe/src/components/FormHelper/index.less index d9902a030..891e4a209 100644 --- a/webapp/packages/supersonic-fe/src/components/FormHelper/index.less +++ b/webapp/packages/supersonic-fe/src/components/FormHelper/index.less @@ -1,6 +1,8 @@ .title { font-family: var(--tencent-font-family); // margin-bottom: 5px; + color: #667085; + font-size: 16px; } .subTitleContainer { diff --git a/webapp/packages/supersonic-fe/src/components/TableHeaderFilter/index.less b/webapp/packages/supersonic-fe/src/components/TableHeaderFilter/index.less index 6d1c23529..8c28436c9 100644 --- a/webapp/packages/supersonic-fe/src/components/TableHeaderFilter/index.less +++ b/webapp/packages/supersonic-fe/src/components/TableHeaderFilter/index.less @@ -1,5 +1,4 @@ .tableHeaderTitle { - margin-left: 10px; .headerTitleLabel { color: #667085; font-size: 14px; diff --git a/webapp/packages/supersonic-fe/src/components/TableHeaderFilter/index.tsx b/webapp/packages/supersonic-fe/src/components/TableHeaderFilter/index.tsx index 7df4c7820..0d41a0290 100644 --- a/webapp/packages/supersonic-fe/src/components/TableHeaderFilter/index.tsx +++ b/webapp/packages/supersonic-fe/src/components/TableHeaderFilter/index.tsx @@ -5,7 +5,7 @@ import styles from './index.less'; type Props = { components: { - label: string; + label?: string; component: ReactNode; }[]; }; @@ -16,7 +16,8 @@ const TableHeaderFilter: React.FC = ({ components }) => { {components.map(({ label, component }) => ( - {label}: + {label && {label}:} + {component} ))} diff --git a/webapp/packages/supersonic-fe/src/pages/Agent/AgentForm.tsx b/webapp/packages/supersonic-fe/src/pages/Agent/AgentForm.tsx index ef2659f42..6c4c00e60 100644 --- a/webapp/packages/supersonic-fe/src/pages/Agent/AgentForm.tsx +++ b/webapp/packages/supersonic-fe/src/pages/Agent/AgentForm.tsx @@ -135,7 +135,7 @@ const AgentForm: React.FC = ({ editAgent, onSaveAgent, onCreateToolBtnCli const layout = { labelCol: { span: 8 }, - wrapperCol: { span: 12 }, + wrapperCol: { span: 16 }, }; const onOk = async () => { @@ -187,10 +187,10 @@ const AgentForm: React.FC = ({ editAgent, onSaveAgent, onCreateToolBtnCli > - + - + {/* = ({ editAgent, onSaveAgent, onCreateToolBtnCli label="开启精简模式" tooltip="精简模式下不可调整查询条件、不显示调试信息、不显示可视化组件" valuePropName="checked" + htmlFor="" > @@ -218,6 +219,7 @@ const AgentForm: React.FC = ({ editAgent, onSaveAgent, onCreateToolBtnCli hidden={formData?.toolConfig?.simpleMode === true} tooltip="包含Schema映射、SQL生成每阶段的关键信息" valuePropName="checked" + htmlFor="" > @@ -275,7 +277,7 @@ const AgentForm: React.FC = ({ editAgent, onSaveAgent, onCreateToolBtnCli {/* */} {/* */} -
+
{modelTypeOptions.map((item) => { return (
= ({ editAgent, onSaveAgent, onCreateToolBtnCli label={item.label} valuePropName="checked" tooltip={item.description} + htmlFor="" > @@ -300,7 +303,7 @@ const AgentForm: React.FC = ({ editAgent, onSaveAgent, onCreateToolBtnCli
{/* */} -
+
{modelTypeOptions.map((item) => { return (
= ({}) => { - const domainModel = useModel('SemanticModel.domainData'); - const modelModel = useModel('SemanticModel.modelData'); - const { selectDomainId } = domainModel; - const { selectModelId: modelId } = modelModel; - - const [createModalVisible, setCreateModalVisible] = useState(false); - const [tagItem, setTagItem] = useState(); - const [selectedRowKeys, setSelectedRowKeys] = useState([]); - const [tableData, setTableData] = useState([]); - const [loading, setLoading] = useState(false); - const defaultPagination = { - current: 1, - pageSize: 20, - total: 0, - }; - const [pagination, setPagination] = useState(defaultPagination); - - const [filterParams, setFilterParams] = useState>({}); - - const [tagValueSettingModalVisible, setTagValueSettingModalVisible] = useState(false); - - const actionRef = useRef(); - - const queryBatchUpdateStatus = async (ids: React.Key[], status: StatusEnum) => { - if (Array.isArray(ids) && ids.length === 0) { - return; - } - const { code, msg } = await batchUpdateTagStatus({ - ids, - status, - }); - if (code === 200) { - queryTagList({ ...filterParams, ...defaultPagination }); - return; - } - message.error(msg); - }; - - useEffect(() => { - queryTagList({ ...filterParams, ...defaultPagination }); - }, [filterParams]); - - const queryTagList = async (params: any) => { - setLoading(true); - const { code, data, msg } = await getTagList({ - ...pagination, - ...params, - modelIds: [modelId], - }); - setLoading(false); - const { list, pageSize, pageNum, total } = data || {}; - if (code === 200) { - setPagination({ - ...pagination, - pageSize: Math.min(pageSize, 100), - current: pageNum, - total, - }); - setTableData(list); - } else { - message.error(msg); - setTableData([]); - } - }; - - const columnsConfig = ColumnsConfig(); - - const columns: ProColumns[] = [ - { - dataIndex: 'id', - title: 'ID', - width: 80, - fixed: 'left', - search: false, - }, - { - dataIndex: 'name', - title: '标签', - width: 280, - fixed: 'left', - // width: '30%', - search: false, - render: columnsConfig.indicatorInfo.render, - }, - { - dataIndex: 'key', - title: '标签搜索', - hideInTable: true, - }, - { - dataIndex: 'sensitiveLevel', - title: '敏感度', - width: 160, - valueEnum: SENSITIVE_LEVEL_ENUM, - render: columnsConfig.sensitiveLevel.render, - }, - - { - dataIndex: 'description', - title: '描述', - width: 300, - search: false, - render: columnsConfig.description.render, - }, - { - dataIndex: 'status', - title: '状态', - width: 160, - search: false, - render: columnsConfig.state.render, - }, - { - dataIndex: 'createdBy', - title: '创建人', - width: 150, - search: false, - }, - { - dataIndex: 'updatedAt', - title: '更新时间', - width: 180, - search: false, - render: (value: any) => { - return value && value !== '-' ? moment(value).format('YYYY-MM-DD HH:mm:ss') : '-'; - }, - }, - { - title: '操作', - dataIndex: 'x', - valueType: 'option', - width: 150, - render: (_, record) => { - return ( - - - - {record.status === StatusEnum.ONLINE ? ( - - ) : ( - - )} - { - const { code, msg } = await deleteTag(record.id); - if (code === 200) { - setTagItem(undefined); - queryTagList({ ...filterParams, ...defaultPagination }); - } else { - message.error(msg); - } - }} - > - - - - ); - }, - }, - ]; - - const rowSelection = { - onChange: (selectedRowKeys: React.Key[]) => { - setSelectedRowKeys(selectedRowKeys); - }, - }; - - const onMenuClick = (key: string) => { - switch (key) { - case 'batchStart': - queryBatchUpdateStatus(selectedRowKeys, StatusEnum.ONLINE); - break; - case 'batchStop': - queryBatchUpdateStatus(selectedRowKeys, StatusEnum.OFFLINE); - break; - default: - break; - } - }; - - return ( - <> - { - setFilterParams((preState) => { - return { - ...preState, - key: value, - }; - }); - }} - /> - ), - }, - { - label: '敏感度', - component: ( - { - setDimensionList(undefined); - setMetricList(undefined); - const modelItem = modelList.find((item) => item.id === val); - setSelectedModelItem(modelItem); - }} - options={modelList.map((item) => { - return { label: item.name, value: item.id }; - })} - /> - - } - dimensionList={dimensionList} - metricList={metricList} - // tagList={tagList} - modelItem={selectedModelItem} - viewItem={viewItem} - ref={configTableRef} - /> + + + 切换模型: + { + setImportValues(values); + }} + /> + + +
+ + + { + return false; + }} + scroll={{ y: 500 }} + toolBarRender={() => { + return [ + { + onMenuClick?.(key, selectedRowKeys); + }} + />, + ]; + }} + headerTitle={ + { + const data = dataSource + .filter((item) => { + return item.includes(value); + }) + .map((item) => { + return { + value: item, + }; + }); + setTableData(data); + }} + /> + ), + }, + ]} + /> + } + rowSelection={{ + type: 'checkbox', + onChange: (selectedRowKeys: React.Key[]) => { + setSelectedRowKeys(selectedRowKeys); + }, + }} + sticky={{ offsetHeader: 0 }} + options={false} + /> +
+ + ); +}; + +export default DimensionValueFilterModal; diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionValueSettingForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionValueSettingForm.tsx index bfd0e7844..40a347763 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionValueSettingForm.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionValueSettingForm.tsx @@ -3,9 +3,7 @@ import { Form, Switch, Space, Button, Tooltip, message, Select } from 'antd'; import FormItemTitle from '@/components/FormHelper/FormItemTitle'; import { useModel } from '@umijs/max'; import { RedoOutlined, InfoCircleOutlined } from '@ant-design/icons'; -import DisabledWheelNumberInput from '@/components/DisabledWheelNumberInput'; import { formLayout } from '@/components/FormHelper/utils'; -import { ProCard } from '@ant-design/pro-components'; import { DictTaskState, KnowledgeConfigTypeEnum, @@ -13,32 +11,30 @@ import { KnowledgeConfigTypeWordingMap, } from '../../enum'; import { - searchKnowledgeConfigQuery, searchDictLatestTaskList, createDictTask, editDictConfig, - createDictConfig, deleteDictTask, - queryMetric, - getModelList, - getMetricData, } from '../../service'; import type { ISemantic } from '../../data'; import type { StateType } from '../../model'; import styles from '../style.less'; -import CommonEditList from '../../components/CommonEditList'; type Props = { - dataItem: ISemantic.IDimensionItem | ISemantic.ITagItem; + dataItem: ISemantic.IDimensionItem; type?: KnowledgeConfigTypeEnum; + knowledgeConfig?: ISemantic.IDictKnowledgeConfigItem; onSubmit?: () => void; + onVisibleChange?: (visible: KnowledgeConfigStatusEnum) => void; }; const FormItem = Form.Item; const DimensionValueSettingForm: React.FC = ({ dataItem, + knowledgeConfig, type = KnowledgeConfigTypeEnum.DIMENSION, + onVisibleChange, }) => { const [form] = Form.useForm(); const domainModel = useModel('SemanticModel.domainData'); @@ -47,46 +43,32 @@ const DimensionValueSettingForm: React.FC = ({ const [taskItemState, setTaskItemState] = useState(); const [saveLoading, setSaveLoading] = useState(false); const [refreshLoading, setRefreshLoading] = useState(false); - const [knowledgeConfig, setKnowledgeConfig] = useState(); - const [modelList, setModelList] = useState([]); + const [deleteLoading, setDeleteLoading] = useState(false); const [importDictState, setImportDictState] = useState(false); - const [metricList, setMetricList] = useState(); - const defaultKnowledgeConfig: ISemantic.IDictKnowledgeConfigItemConfig = { - blackList: [], - whiteList: [], - ruleList: [], - }; useEffect(() => { - searchKnowledgeConfig(); + // searchKnowledgeConfig(); queryDictLatestTaskList(); }, []); useEffect(() => { - if (!selectDomainId) { + if (!knowledgeConfig) { return; } - queryModelList(selectDomainId); - }, [selectDomainId]); - const queryModelList = async (domainId: number) => { - const { code, data } = await getModelList(domainId); - if (code === 200) { - setModelList(data); + const configItem = knowledgeConfig; + // if (configItem) { + const { status, config } = configItem; + if (status === KnowledgeConfigStatusEnum.ONLINE) { + setDimensionVisible(true); } else { - message.error('获取模型列表失败!'); + setDimensionVisible(false); } - }; - - const queryMetricList = async (modelId: number) => { - const { code, data, msg } = await queryMetric({ modelId }); - if (code === 200 && Array.isArray(data?.list)) { - setMetricList(data.list); - } else { - message.error(msg); - } - }; + form.setFieldsValue({ + ...config, + }); + }, [knowledgeConfig]); const taskRender = () => { if (taskItemState?.taskStatus) { @@ -99,42 +81,6 @@ const DimensionValueSettingForm: React.FC = ({ return '--'; }; - const searchKnowledgeConfig = async () => { - setRefreshLoading(true); - const { code, data } = await searchKnowledgeConfigQuery({ - type, - itemId: dataItem.id, - }); - - setRefreshLoading(false); - if (code !== 200) { - message.error('获取字典导入配置失败!'); - return; - } - const configItem = data[0]; - if (configItem) { - const { status, config } = configItem; - if (status === KnowledgeConfigStatusEnum.ONLINE) { - setDimensionVisible(true); - } else { - setDimensionVisible(false); - } - form.setFieldsValue({ - ...config, - }); - setKnowledgeConfig(configItem); - const { metricId } = config; - if (metricId) { - queryMetricData(metricId); - } - } else { - form.setFieldsValue({ - ...defaultKnowledgeConfig, - }); - createDictConfigQuery(dataItem, defaultKnowledgeConfig); - } - }; - const queryDictLatestTaskList = async () => { setRefreshLoading(true); const { code, data } = await searchDictLatestTaskList({ @@ -155,35 +101,6 @@ const DimensionValueSettingForm: React.FC = ({ } }; - const queryMetricData = async (metricId: string) => { - const { code, data, msg } = await getMetricData(metricId); - if (code === 200) { - const { modelId } = data; - queryMetricList(modelId); - form.setFieldValue('modelId', modelId); - return; - } - message.error(msg); - }; - - const createDictConfigQuery = async ( - dimension: ISemantic.IDimensionItem, - config: ISemantic.IDictKnowledgeConfigItemConfig, - ) => { - const { code, data } = await createDictConfig({ - type, - itemId: dimension.id, - config, - status: 1, - }); - - if (code !== 200) { - message.error('字典导入配置创建失败!'); - return; - } - setKnowledgeConfig(data); - }; - const createDictTaskQuery = async (dimension: ISemantic.IDimensionItem) => { setImportDictState(true); const { code } = await createDictTask({ @@ -218,10 +135,10 @@ const DimensionValueSettingForm: React.FC = ({ }); setSaveLoading(false); if (code === 200) { - message.success('字典导入配置保存成功!'); + message.success('维度值设置保存成功!'); return; } - message.error('字典导入配置保存失败!'); + message.error('维度值设置保存失败!'); }; const deleteDictTaskQuery = async (dimension: ISemantic.IDimensionItem) => { @@ -242,23 +159,13 @@ const DimensionValueSettingForm: React.FC = ({
{ - if (value.modelId === undefined && values.modelId == undefined) { - setMetricList([]); - form.setFieldValue('metricId', undefined); - return; - } - if (value.modelId) { - form.setFieldValue('metricId', undefined); - queryMetricList(value.modelId); - } - }} + onValuesChange={(value, values) => {}} > = ({ size="small" checked={dimensionVisible} onChange={(value) => { - editDictTaskQuery( - value - ? KnowledgeConfigStatusEnum.ONLINE - : KnowledgeConfigStatusEnum.OFFLINE, - ); + const state = value + ? KnowledgeConfigStatusEnum.ONLINE + : KnowledgeConfigStatusEnum.OFFLINE; + editDictTaskQuery(state); setDimensionVisible(value); + onVisibleChange?.(state); }} /> @@ -323,7 +230,7 @@ const DimensionValueSettingForm: React.FC = ({ > 导入状态 - : {taskRender(dataItem)} + : {taskRender()} @@ -353,120 +260,6 @@ const DimensionValueSettingForm: React.FC = ({ )} - {dimensionVisible && ( - <> - - 指标设置 - - } - bordered - extra={ - - } - style={{ marginBottom: 20 }} - > - - } - > - - - { - return { - value: item.id, - label: item.name, - }; - })} - /> - - - - - } - > - - -
-
- {KnowledgeConfigTypeWordingMap[type]}值过滤 -
- - - - - - - - -
-
- - )}
); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/RecommendedQuestionsSection.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/RecommendedQuestionsSection.tsx new file mode 100644 index 000000000..62455885c --- /dev/null +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/RecommendedQuestionsSection.tsx @@ -0,0 +1,87 @@ +import { message } from 'antd'; +import React, { useState, useEffect } from 'react'; +import { useModel } from '@umijs/max'; +import type { StateType } from '../../model'; +import { getDomainExtendConfig, addDomainExtend, editDomainExtend } from '../../service'; +import { ProCard } from '@ant-design/pro-components'; + +import TextAreaCommonEditList from '../../components/CommonEditList/TextArea'; + +type Props = {}; + +const RecommendedQuestionsSection: React.FC = ({}) => { + const modelModel = useModel('SemanticModel.modelData'); + const { selectModelId: modelId } = modelModel; + + const [questionData, setQuestionData] = useState([]); + const [currentRecordId, setCurrentRecordId] = useState(0); + + const queryThemeListData: any = async () => { + const { code, data } = await getDomainExtendConfig({ + modelId, + }); + + if (code === 200) { + const target = data?.[0] || {}; + if (Array.isArray(target.recommendedQuestions)) { + setQuestionData( + target.recommendedQuestions.map((item: { question: string }) => { + return item.question; + }), + ); + setCurrentRecordId(target.id || 0); + } else { + setQuestionData([]); + setCurrentRecordId(0); + } + return; + } + + message.error('获取问答设置信息失败'); + }; + + const saveEntity = async (list: string[]) => { + let saveDomainExtendQuery = addDomainExtend; + if (currentRecordId) { + saveDomainExtendQuery = editDomainExtend; + } + const { code, msg } = await saveDomainExtendQuery({ + recommendedQuestions: list.map((question: string) => { + return { question }; + }), + id: currentRecordId, + modelId, + }); + + if (code === 200) { + return; + } + message.error(msg); + }; + + const initPage = async () => { + queryThemeListData(); + }; + + useEffect(() => { + if (!modelId) { + return; + } + initPage(); + }, [modelId]); + + return ( +
+ + { + saveEntity(list); + setQuestionData(list); + }} + /> + +
+ ); +}; +export default RecommendedQuestionsSection; diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/style.less b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/style.less index bdcf1b7cc..96527bfae 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/style.less +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/style.less @@ -442,3 +442,10 @@ +.dimensionValueFilterTable { + :global { + .ant-pro-card-body { + padding:0 + } + } +} \ No newline at end of file diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/enum.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/enum.ts index 2fd7d6eec..5723ea2dc 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/enum.ts +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/enum.ts @@ -93,3 +93,9 @@ export enum DatePeriod { MONTH = 'MONTH', YEAR = 'YEAR', } + +export enum DimensionValueListType { + BLACK_LIST = 'blackList', + WHITE_LIST = 'whiteList', + RULE_LIST = 'ruleList', +} diff --git a/webapp/packages/supersonic-fe/src/utils/utils.ts b/webapp/packages/supersonic-fe/src/utils/utils.ts index fe546943d..bf48da88f 100644 --- a/webapp/packages/supersonic-fe/src/utils/utils.ts +++ b/webapp/packages/supersonic-fe/src/utils/utils.ts @@ -498,3 +498,7 @@ export function decryptPassword(encryptPassword: string) { }); return CryptoJS.enc.Utf8.stringify(decrypt).toString(); } + +export function uniqueArray(arr: any[]) { + return Array.from(new Set(arr)); +}