├── demo ├── demo.en.png └── demo.zh.png ├── src ├── deps.ts ├── plugins │ ├── zip.png │ └── upgrade.icon.svg ├── icons │ ├── favicon.png │ ├── link.icon.svg │ ├── i18n.icon.svg │ ├── run.svg │ ├── favicon.italic.svg │ ├── logos │ │ ├── iotpro.svg │ │ └── shark.svg │ └── settings.icon.svg ├── overview │ ├── offline.png │ ├── online.png │ └── icons │ │ ├── stop.icon.svg │ │ ├── start.icon.svg │ │ ├── refresh.icon.svg │ │ ├── overview.icon.svg │ │ └── disk.icon.svg ├── dashboard │ ├── components │ │ ├── index.sass │ │ ├── Doc.tsx │ │ ├── SaveComfirmModal.tsx │ │ └── EchartsComponent.tsx │ ├── SettingsPanel │ │ ├── index.scss │ │ ├── SettingsPanel.tsx │ │ └── CanvasSetting.tsx │ ├── Charts │ │ ├── Chart │ │ │ └── index.scss │ │ ├── CompositeGraph │ │ │ ├── index.scss │ │ │ └── constant.ts │ │ ├── Variables │ │ │ ├── index.scss │ │ │ └── index.tsx │ │ ├── DashboardEditor │ │ │ └── index.sass │ │ ├── Gauge │ │ │ └── index.scss │ │ ├── Configuration │ │ │ └── index.sass │ │ ├── Descriptions │ │ │ └── index.scss │ │ ├── Table │ │ │ └── index.scss │ │ └── RichText │ │ │ └── index.sass │ ├── constant.ts │ ├── ChartFormFields │ │ ├── components │ │ │ ├── Tooltip.tsx │ │ │ ├── Title.tsx │ │ │ ├── DataZoom.tsx │ │ │ ├── Wrapper.tsx │ │ │ └── SplitLine.tsx │ │ ├── EditorFields.tsx │ │ ├── index.scss │ │ └── constant.ts │ ├── Share │ │ └── index.sass │ ├── DataSource │ │ ├── DataView.tsx │ │ └── InsertVariableBtn.tsx │ └── Overview.sass ├── components │ ├── DraggableModal │ │ ├── index.scss │ │ └── index.tsx │ ├── copy │ │ ├── copied.icon.svg │ │ ├── CopyIconButton.scss │ │ ├── copy.icon.svg │ │ └── CopyIconButton.tsx │ ├── TableOperations │ │ ├── index.scss │ │ └── index.tsx │ ├── ReadonlyEditor │ │ ├── index.scss │ │ └── index.tsx │ ├── RefreshButton │ │ └── index.tsx │ ├── Editor │ │ └── index.sass │ ├── BackButton.tsx │ ├── GlobalErrorBoundary.sass │ ├── BottomFixedFooter │ │ ├── index.scss │ │ └── index.tsx │ ├── DDBHeader │ │ ├── CompileAndRefresh.tsx │ │ ├── HostSelect.tsx │ │ ├── Logo.tsx │ │ └── LanguageSelect.tsx │ ├── Unlogin.tsx │ ├── icons │ │ ├── arrow.down.icon.svg │ │ └── export.icon.svg │ ├── tags │ │ ├── index.scss │ │ └── index.tsx │ ├── node-select │ │ └── index.tsx │ ├── StringColorPicker │ │ └── index.tsx │ ├── BoolRadioGroup │ │ └── index.tsx │ ├── DDBTable │ │ ├── index.sass │ │ └── index.tsx │ ├── StringTimePicker.tsx │ ├── StringDatePicker │ │ └── index.tsx │ ├── TableCellDetail │ │ └── index.tsx │ └── FormDependencies │ │ └── index.tsx ├── data-collection │ ├── components │ │ ├── create-connection-modal │ │ │ └── index.scss │ │ ├── view-log-modal │ │ │ ├── index.scss │ │ │ └── index.tsx │ │ ├── create-parser-template-modal │ │ │ └── index.scss │ │ ├── connection-detail │ │ │ ├── index.scss │ │ │ ├── parser-template-view-modal.tsx │ │ │ └── delete-describe-modal.tsx │ │ ├── create-subscribe-modal │ │ │ └── index.scss │ │ └── delete-connections-modal │ │ │ └── index.tsx │ ├── index.sass │ ├── utils.ts │ ├── ParserTemplates.sass │ └── icons │ │ ├── data-collection.icon.svg │ │ ├── parser-template.icon.svg │ │ └── connection.icon.svg ├── access │ ├── hooks │ │ ├── use-users.ts │ │ ├── use-groups.ts │ │ ├── use-access.ts │ │ └── use-access-objs.ts │ ├── components │ │ └── RevokeConfirm.tsx │ ├── types.ts │ ├── icons │ │ ├── access.icon.svg │ │ ├── user.icon.svg │ │ └── group.icon.svg │ └── utils │ │ ├── filter-access-options.ts │ │ └── handle-access.ts ├── shell │ ├── QueryGuide │ │ ├── init.ts │ │ ├── model.ts │ │ ├── type.ts │ │ ├── utils.ts │ │ └── components │ │ │ ├── EnumSelect.tsx │ │ │ ├── EnumAutoComplete.tsx │ │ │ └── ColSelectTransfer.tsx │ ├── AccessModal.sass │ ├── icons │ │ ├── column.icon.svg │ │ ├── git.icon.svg │ │ ├── column-root.icon.svg │ │ ├── add-column.icon.svg │ │ ├── create-table.icon.svg │ │ ├── vector.icon.svg │ │ ├── access.icon.svg │ │ ├── partitions.icon.svg │ │ ├── partition-file.icon.svg │ │ ├── database.icon.svg │ │ ├── variable.icon.svg │ │ ├── matrix.icon.svg │ │ ├── query-guide.icon.svg │ │ ├── table.icon.svg │ │ └── catalog.icon.svg │ ├── git │ │ ├── git-provider.ts │ │ └── Git.tsx │ ├── CreateTableModal.scss │ ├── DataView.tsx │ └── ExecuteAction.tsx ├── guide │ ├── iot-guide │ │ ├── index.sass │ │ ├── SimpleVersion │ │ │ ├── index.scss │ │ │ └── SimpleFirstStep.tsx │ │ ├── AdvancedVersion │ │ │ └── index.scss │ │ ├── index.tsx │ │ └── type.ts │ ├── utils.ts │ ├── finance-guide │ │ ├── components │ │ │ ├── index.scss │ │ │ └── ExistedDBSelect.tsx │ │ ├── type.ts │ │ ├── constant.ts │ │ └── index.scss │ ├── components │ │ ├── GuideFailResultPage.tsx │ │ ├── GuideSuccessResultPage.tsx │ │ ├── UploadConfigModal.tsx │ │ ├── DownloadConfigModal.tsx │ │ ├── CodeViewStep.tsx │ │ └── UploadFileField.tsx │ ├── constant.ts │ └── icons │ │ ├── iot.icon.svg │ │ └── finance.icon.svg ├── inspection │ ├── utils.ts │ ├── pages │ │ ├── AddInspectionPage.tsx │ │ └── EditInspectionPage.tsx │ ├── constants.ts │ ├── components │ │ ├── DeletePlansModal.tsx │ │ └── DeleteReportsModal.tsx │ ├── index.tsx │ ├── modals │ │ ├── LogModal.tsx │ │ └── AddParamsModal.tsx │ └── icons │ │ └── inspection.icon.svg ├── streaming │ ├── StreamingError.tsx │ ├── index.sass │ ├── hooks │ │ └── use-streaming.ts │ └── types.ts ├── window.sass ├── test │ └── index.sass ├── utils.common.ts ├── config │ ├── icons │ │ ├── group.icon.svg │ │ ├── config.icon.svg │ │ └── controller.config.icon.svg │ ├── type.ts │ └── utils.ts ├── computing │ ├── icons │ │ ├── table.icon.svg │ │ └── engine.icon.svg │ ├── CEPComputing │ │ ├── api.ts │ │ └── components │ │ │ └── CEPEngineList.tsx │ ├── index.dos │ └── index.sass ├── streaming-graph │ ├── Configuration.tsx │ ├── index.tsx │ └── flow.icon.svg ├── antd-table.sass ├── hooks.ts ├── settings │ ├── index.sass │ └── index.tsx ├── login │ └── index.sass ├── index.shf.sass ├── variables.sass ├── job │ └── index.sass └── log │ └── index.sass ├── .gitignore ├── eslint.config.js ├── .vscode ├── extensions.json └── settings.template.json ├── i18n └── index.ts ├── test └── index.ts ├── global.d.ts └── nginx.conf /demo/demo.en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dolphindb/web/HEAD/demo/demo.en.png -------------------------------------------------------------------------------- /demo/demo.zh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dolphindb/web/HEAD/demo/demo.zh.png -------------------------------------------------------------------------------- /src/deps.ts: -------------------------------------------------------------------------------- 1 | export * as AntdProComponents from '@ant-design/pro-components' 2 | -------------------------------------------------------------------------------- /src/plugins/zip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dolphindb/web/HEAD/src/plugins/zip.png -------------------------------------------------------------------------------- /src/icons/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dolphindb/web/HEAD/src/icons/favicon.png -------------------------------------------------------------------------------- /src/overview/offline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dolphindb/web/HEAD/src/overview/offline.png -------------------------------------------------------------------------------- /src/overview/online.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dolphindb/web/HEAD/src/overview/online.png -------------------------------------------------------------------------------- /src/dashboard/components/index.sass: -------------------------------------------------------------------------------- 1 | .dashboard-echarts-component 2 | width: 100% 3 | height: 100% -------------------------------------------------------------------------------- /src/components/DraggableModal/index.scss: -------------------------------------------------------------------------------- 1 | .draggable-modal-title { 2 | width: 100%; 3 | cursor: move; 4 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | /out/ 4 | 5 | /i18n/untranslateds.json 6 | 7 | /.vscode/settings.json 8 | /.vscode/launch.json 9 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import { xlint_config } from 'xshell/xlint.js' 2 | 3 | // eslint-disable-next-line no-restricted-exports 4 | export default [xlint_config] 5 | -------------------------------------------------------------------------------- /src/data-collection/components/create-connection-modal/index.scss: -------------------------------------------------------------------------------- 1 | .create-connection-modal { 2 | .ant-input-number { 3 | width: 300px; 4 | } 5 | } -------------------------------------------------------------------------------- /src/dashboard/SettingsPanel/index.scss: -------------------------------------------------------------------------------- 1 | .reset-config-btn { 2 | margin-top: 16px; 3 | margin-left: 16px; 4 | } 5 | 6 | .reset-tip { 7 | padding: 12px 8 | } -------------------------------------------------------------------------------- /src/dashboard/Charts/Chart/index.scss: -------------------------------------------------------------------------------- 1 | .chart-radio-group { 2 | margin-bottom: 12px; 3 | } 4 | 5 | .chart-wrapper { 6 | padding: 8px; 7 | background-color: #282828; 8 | } -------------------------------------------------------------------------------- /src/dashboard/Charts/CompositeGraph/index.scss: -------------------------------------------------------------------------------- 1 | .series_match_type_tip { 2 | margin-left: 8px; 3 | } 4 | 5 | .collapse-label { 6 | display: flex; 7 | justify-content: space-between; 8 | } -------------------------------------------------------------------------------- /src/overview/icons/stop.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/access/hooks/use-users.ts: -------------------------------------------------------------------------------- 1 | import useSWR from 'swr' 2 | 3 | import { access } from '../model.ts' 4 | 5 | export function use_users () { 6 | return useSWR('users', async () => access.get_user_list()) 7 | } 8 | -------------------------------------------------------------------------------- /src/shell/QueryGuide/init.ts: -------------------------------------------------------------------------------- 1 | import { model } from '../../model.js' 2 | 3 | import code from './guide.dos' 4 | 5 | 6 | export async function init_dbms_query_guide () { 7 | await model.ddb.eval(code) 8 | } 9 | -------------------------------------------------------------------------------- /src/access/hooks/use-groups.ts: -------------------------------------------------------------------------------- 1 | import useSWR from 'swr' 2 | 3 | import { access } from '../model.ts' 4 | 5 | export function use_groups () { 6 | return useSWR('groups', async () => access.get_group_list()) 7 | } 8 | -------------------------------------------------------------------------------- /src/shell/AccessModal.sass: -------------------------------------------------------------------------------- 1 | .access-table 2 | .ant-table-wrapper .ant-table-cell, .ant-table-wrapper .ant-table-thead>tr>th 3 | padding: 0 4 | .ant-collapse>.ant-collapse-item >.ant-collapse-header 5 | padding: 5px -------------------------------------------------------------------------------- /src/components/copy/copied.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/TableOperations/index.scss: -------------------------------------------------------------------------------- 1 | .ddb-table-operations { 2 | display: flex; 3 | column-gap: 12px; 4 | 5 | .more-operations { 6 | color: var(--ant-color-primary); 7 | cursor: pointer; 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/data-collection/components/view-log-modal/index.scss: -------------------------------------------------------------------------------- 1 | .log-list { 2 | .ant-list-item { 3 | border: none !important; 4 | 5 | &:nth-child(even) { 6 | background-color: rgba(0, 0, 0, 0.02); 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /src/guide/iot-guide/index.sass: -------------------------------------------------------------------------------- 1 | .view-card.iot-guide 2 | padding-top: 10px !important 3 | 4 | .warning-result 5 | height: 100% 6 | display: flex 7 | align-items: center 8 | justify-content: center 9 | flex-direction: column 10 | -------------------------------------------------------------------------------- /src/inspection/utils.ts: -------------------------------------------------------------------------------- 1 | import dayjs from 'dayjs' 2 | 3 | export function parse_minute (minute_str: string) { 4 | const [hour, minute] = minute_str.slice(0, -1).split(':') 5 | return dayjs().set('hour', Number(hour)).set('minute', Number(minute)) 6 | } 7 | -------------------------------------------------------------------------------- /src/inspection/pages/AddInspectionPage.tsx: -------------------------------------------------------------------------------- 1 | import { InspectionForm } from '@/inspection/components/InspectionForm.tsx' 2 | 3 | export function AddInspectionPage () { 4 | return
5 | 6 |
7 | } 8 | -------------------------------------------------------------------------------- /src/components/copy/CopyIconButton.scss: -------------------------------------------------------------------------------- 1 | .copy-icon-button { 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | padding: 12px; 6 | box-sizing: border-box; 7 | 8 | &:hover { 9 | border: 1px solid #E2E2E2; 10 | } 11 | } -------------------------------------------------------------------------------- /src/data-collection/index.sass: -------------------------------------------------------------------------------- 1 | .data-connection 2 | width: 100% 3 | height: 100% 4 | 5 | .data-connection, .parser-template 6 | .ant-result.init 7 | padding-top: 150px !important 8 | 9 | .data-collection-spin 10 | width: 100% 11 | margin-top: 50vh 12 | 13 | -------------------------------------------------------------------------------- /src/dashboard/Charts/Variables/index.scss: -------------------------------------------------------------------------------- 1 | .variable-title { 2 | font-weight: 500; 3 | margin-bottom: 16px; 4 | } 5 | 6 | .variable-chart-wrapper { 7 | margin-bottom: 0px; 8 | } 9 | 10 | .variable-config-form { 11 | .ant-form-item { 12 | margin-bottom: 8px; 13 | } 14 | } -------------------------------------------------------------------------------- /src/dashboard/constant.ts: -------------------------------------------------------------------------------- 1 | import { DdbForm } from 'dolphindb/browser.js' 2 | 3 | import { t } from '@i18n' 4 | 5 | export const DATA_SOURCE_TYPE_MAP = { 6 | [DdbForm.table]: t('表格'), 7 | [DdbForm.matrix]: t('矩阵') 8 | } 9 | 10 | 11 | export const DASHBOARD_SHARED_SEARCH_KEY = 'ids' 12 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "MS-CEINTL.vscode-language-pack-zh-hans", 4 | "mhutchie.git-graph", 5 | "eamodio.gitlens", 6 | "syler.sass-indented", 7 | "dbaeumer.vscode-eslint" 8 | ], 9 | "unwantedRecommendations": [] 10 | } -------------------------------------------------------------------------------- /src/dashboard/components/Doc.tsx: -------------------------------------------------------------------------------- 1 | import { t } from '@i18n' 2 | 3 | export function Doc () { 4 | return 9 | {t('文档')} 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/components/ReadonlyEditor/index.scss: -------------------------------------------------------------------------------- 1 | .readonly-editor { 2 | position: relative; 3 | 4 | border: 1px solid #D9D9D9; 5 | 6 | .copy-btn { 7 | position: absolute; 8 | border-radius: 4px; 9 | top: 12px; 10 | right: 32px; 11 | z-index: 1000; 12 | } 13 | } -------------------------------------------------------------------------------- /src/streaming/StreamingError.tsx: -------------------------------------------------------------------------------- 1 | import { type ErrorType } from './types.js' 2 | import './index.sass' 3 | 4 | export function StreamingError ({ error }: { error: ErrorType }) { 5 | if (error.appear) 6 | return
{error.msg}
7 | 8 | return null 9 | } 10 | -------------------------------------------------------------------------------- /src/shell/icons/column.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/components/RefreshButton/index.tsx: -------------------------------------------------------------------------------- 1 | import { Button, type ButtonProps } from 'antd' 2 | import { ReloadOutlined } from '@ant-design/icons' 3 | 4 | import { t } from '@i18n' 5 | 6 | export function RefreshButton (props: ButtonProps) { 7 | return 10 | } 11 | -------------------------------------------------------------------------------- /src/window.sass: -------------------------------------------------------------------------------- 1 | html, body 2 | margin: 0 3 | padding: 0 4 | height: 100% 5 | overscroll-behavior-y: contain 6 | color: #000000 7 | 8 | * 9 | box-sizing: inherit 10 | 11 | .root 12 | height: 100% 13 | box-sizing: border-box 14 | 15 | font-size: 20px 16 | 17 | .app 18 | height: 100% 19 | -------------------------------------------------------------------------------- /src/data-collection/utils.ts: -------------------------------------------------------------------------------- 1 | import { model } from '@model' 2 | 3 | export async function request (func: string, params?: any) { 4 | const res = await model.ddb.invoke(func, params ? [JSON.stringify(params)] : undefined) 5 | if (typeof res === 'string') 6 | return JSON.parse(res) as T 7 | else 8 | return res as T 9 | } 10 | -------------------------------------------------------------------------------- /src/icons/link.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/components/Editor/index.sass: -------------------------------------------------------------------------------- 1 | .monaco-editor-container 2 | min-height: 0px 3 | 4 | .light 5 | .monaco-editor 6 | --vscode-editorLineNumber-foreground: #888888 !important 7 | --vscode-editorLineNumber-activeForeground: #444444 !important 8 | --vscode-editor-background: #ffffff !important 9 | --vscode-editorGutter-background: #ffffff !important 10 | -------------------------------------------------------------------------------- /src/components/BackButton.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from 'antd' 2 | import { ArrowLeftOutlined } from '@ant-design/icons' 3 | 4 | import { model } from '@model' 5 | 6 | export function BackButton ({ to }: { to: string }) { 7 | return 20 | } 21 | -------------------------------------------------------------------------------- /src/components/Unlogin.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Result } from 'antd' 2 | 3 | import { t } from '@i18n' 4 | 5 | import { model } from '@model' 6 | 7 | 8 | export function Unlogin ({ info }: { info: string }) { 9 | return { await model.goto_login() }}> 15 | {t('去登录')} 16 | 17 | } 18 | /> 19 | } 20 | -------------------------------------------------------------------------------- /src/dashboard/DataSource/DataView.tsx: -------------------------------------------------------------------------------- 1 | import { Obj } from '@/obj.tsx' 2 | 3 | import { model } from '@model' 4 | import { dashboard } from '@/dashboard/model.ts' 5 | 6 | 7 | export function DataView () { 8 | const { result } = dashboard.use(['result']) 9 | const { options, product_name } = model.use(['options', 'product_name']) 10 | 11 | return
{ 12 | result ? : null 13 | }
14 | } 15 | -------------------------------------------------------------------------------- /src/shell/icons/column-root.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/config/type.ts: -------------------------------------------------------------------------------- 1 | export type NodeType = 'data' | 'agent' | 'controller' | 'computing' 2 | 3 | export type ControllerConfig = { 4 | id: string 5 | name: string 6 | value: string 7 | } 8 | 9 | export type ClusterNode = { 10 | id: string 11 | host: string 12 | port: string 13 | alias: string 14 | mode: NodeType 15 | computeGroup?: string 16 | zone?: string 17 | } 18 | 19 | export type NodesConfig = { 20 | key: string 21 | category?: string 22 | qualifier: string 23 | name: string 24 | value: string 25 | } 26 | -------------------------------------------------------------------------------- /src/dashboard/ChartFormFields/components/DataZoom.tsx: -------------------------------------------------------------------------------- 1 | import { Form } from 'antd' 2 | 3 | import { t } from '@i18n' 4 | import { BoolRadioGroup } from '../../../components/BoolRadioGroup/index.js' 5 | 6 | export function DataZoomFields () { 7 | return <> 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/access/components/RevokeConfirm.tsx: -------------------------------------------------------------------------------- 1 | import { Typography, Popconfirm } from 'antd' 2 | 3 | import { t } from '@i18n' 4 | 5 | export function RevokeConfirm ({ on_confirm }: { on_confirm: () => Promise }) { 6 | return 12 | 13 | {t('撤销')} 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/guide/utils.ts: -------------------------------------------------------------------------------- 1 | import { safe_json_parse } from '../dashboard/utils.ts' 2 | import { model } from '../model.js' 3 | 4 | export async function request (func: string, params: any) { 5 | const res = await model.ddb.call(func, [JSON.stringify(params)]) 6 | return safe_json_parse(res?.value) as T 7 | } 8 | 9 | 10 | export function check_tb_valid (name: string) { 11 | // 仅支持中英文开头 12 | if (!/^[a-zA-Z\u4e00-\u9fa5]/.test(name)) 13 | return false 14 | // 仅支持中英文、数字以及下划线 15 | if (!/[\u4e00-\u9fa5A-Za-z0-9_]+$/g.test(name)) 16 | return false 17 | return true 18 | } 19 | -------------------------------------------------------------------------------- /src/components/icons/arrow.down.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/components/tags/index.scss: -------------------------------------------------------------------------------- 1 | .ddb-tag { 2 | font-weight: bold; 3 | background-color: #E6F7FF; 4 | color: #003A8C; 5 | } 6 | 7 | .ddb-success-tag { 8 | color: #216E4E; 9 | background-color: #DCFFF1; 10 | font-weight: bold; 11 | } 12 | 13 | .ddb-error-tag { 14 | color: #AE2E24; 15 | background-color: #FFECEB; 16 | font-weight: bold; 17 | } 18 | 19 | .ddb-partial-success-tag { 20 | color: #873800; 21 | background-color: #FFE7BA; 22 | font-weight: bold; 23 | } 24 | 25 | .ddb-processing-tag { 26 | background-color: #E6F7FF; 27 | color: #003A8C; 28 | font-weight: bold; 29 | } -------------------------------------------------------------------------------- /src/guide/finance-guide/components/index.scss: -------------------------------------------------------------------------------- 1 | .common-filter-cols-wrapper { 2 | width: 100%; 3 | padding: 16px; 4 | border-radius: 8px; 5 | background-color: var(--ant-color-bg-layout); 6 | margin-bottom: 24px; 7 | h4 { 8 | margin: 0px 0px 16px 0px; 9 | } 10 | 11 | .common-filter-col { 12 | display: flex; 13 | margin-bottom: 12px; 14 | width: 100%; 15 | justify-content: space-between; 16 | align-items: center; 17 | 18 | .ant-form-item { 19 | width: 45%; 20 | margin-bottom: 0px; 21 | } 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/dashboard/Charts/CompositeGraph/constant.ts: -------------------------------------------------------------------------------- 1 | import { DdbType } from 'dolphindb/browser.js' 2 | 3 | export const TIME_TYPES = [ 4 | DdbType.date, 5 | DdbType.month, 6 | DdbType.time, 7 | DdbType.minute, 8 | DdbType.second, 9 | DdbType.datetime, 10 | DdbType.timestamp, 11 | DdbType.nanotime, 12 | DdbType.nanotimestamp, 13 | DdbType.datehour 14 | ] 15 | 16 | 17 | 18 | export const VALUE_TYPES = [ 19 | DdbType.short, 20 | DdbType.int, 21 | DdbType.long, 22 | DdbType.float, 23 | DdbType.double, 24 | DdbType.decimal32, 25 | DdbType.decimal64, 26 | DdbType.decimal128 27 | ] 28 | 29 | -------------------------------------------------------------------------------- /src/shell/QueryGuide/model.ts: -------------------------------------------------------------------------------- 1 | import { Model } from 'react-object-model' 2 | 3 | import { model } from '../../model.js' 4 | 5 | import { type IQueryInfos } from './type.js' 6 | import query_guide_code from './guide.dos' 7 | 8 | export class GuideQueryModel extends Model { 9 | code: string 10 | query_values: IQueryInfos 11 | 12 | query_guide_defined = false 13 | 14 | async define_query_guide () { 15 | if (this.query_guide_defined) 16 | return 17 | await model.ddb.eval(query_guide_code) 18 | this.set({ query_guide_defined: true }) 19 | } 20 | } 21 | 22 | 23 | export let guide_query_model = new GuideQueryModel() 24 | -------------------------------------------------------------------------------- /src/components/DDBHeader/HostSelect.tsx: -------------------------------------------------------------------------------- 1 | import { Select } from 'antd' 2 | import type { SizeType } from 'antd/es/config-provider/SizeContext.js' 3 | 4 | import { envs, model } from '@model' 5 | 6 | 7 | export function HostSelect ({ size = 'small' }: { size?: SizeType }) { 8 | return ({ label: item, value: item })) as DefaultOptionType[]} 20 | {...others} 21 | /> 22 | } 23 | -------------------------------------------------------------------------------- /src/shell/CreateTableModal.scss: -------------------------------------------------------------------------------- 1 | .create-table-form { 2 | .no-partition-scheme { 3 | color: rgba(0, 0, 0, 0.25); 4 | margin: 0; 5 | text-align: center; 6 | } 7 | } 8 | 9 | .create-table-preview-code { 10 | position: relative; 11 | 12 | &-copy { 13 | position: absolute; 14 | top: 8px; 15 | right: 8px; 16 | } 17 | 18 | &-editor { 19 | border: 1px solid #DDD; 20 | height: 400px; 21 | } 22 | 23 | &-action { 24 | margin-top: 24px; 25 | 26 | display: flex; 27 | justify-content: flex-end; 28 | gap: 32px; 29 | } 30 | } 31 | 32 | .create-table-result__error { 33 | .ant-result-subtitle { 34 | color: #333; 35 | text-align: left; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/guide/finance-guide/type.ts: -------------------------------------------------------------------------------- 1 | export interface IDatabaseInfo { 2 | isExist: 0 | 1 3 | name: string 4 | // 日新增数据量 5 | dailyTotalNum?: { 6 | // 自定义和分阶段,两者二选一 7 | custom?: number 8 | gap?: number 9 | } 10 | engine?: 'OLAP' | 'TSDB' 11 | } 12 | 13 | export interface ITableInfo { 14 | name: string 15 | schema: Array<{ 16 | colName: string 17 | dataType: string 18 | }> 19 | // 时间列 20 | timeCol?: string 21 | // 标的列 22 | hashCol?: string 23 | partitionCols?: string[] 24 | filterCols: Array<{ 25 | colName: string 26 | // 唯一值数量 27 | uniqueNum: number 28 | }> 29 | } 30 | 31 | 32 | export interface IFinanceInfo { 33 | database?: IDatabaseInfo 34 | table?: ITableInfo 35 | code?: string 36 | } 37 | -------------------------------------------------------------------------------- /src/inspection/constants.ts: -------------------------------------------------------------------------------- 1 | import { t } from '@i18n' 2 | 3 | export const InspectionFrequencyOptions = [ 4 | { 5 | label: t('每日'), 6 | value: 'D', 7 | }, 8 | { 9 | label: t('每周'), 10 | value: 'W', 11 | }, 12 | { 13 | label: t('每月'), 14 | value: 'M', 15 | }, 16 | ] 17 | 18 | export const WeekDays = [t('周日'), t('周一'), t('周二'), t('周三'), t('周四'), t('周五'), t('周六')] 19 | 20 | export const ReportLables = { 21 | user: t('提交人'), 22 | startTime: t('开始时间'), 23 | endTime: t('结束时间'), 24 | runningTime: t('运行时间'), 25 | desc: t('巡检描述'), 26 | } 27 | 28 | export const MetricGroups = [t('集群基础信息'), t('集群运行状态'), t('服务器运行状态')] 29 | 30 | export const EmailConfigMessages = [t('正常'), t('存在未安装 httpClient 插件的节点。'), t('httpClient 插件版本与 server 版本的前3位不一致。')] 31 | -------------------------------------------------------------------------------- /src/access/icons/access.icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/guide/iot-guide/SimpleVersion/index.scss: -------------------------------------------------------------------------------- 1 | .iot-guide { 2 | .btn-group { 3 | display: flex; 4 | justify-content: end; 5 | } 6 | 7 | .simple-version-wrapper { 8 | height: 100%; 9 | display: flex; 10 | flex-direction: column; 11 | align-items: center; 12 | justify-content: center; 13 | .guide-step { 14 | width: 600px; 15 | margin: 32px 0px; 16 | } 17 | 18 | 19 | .simple-version-form { 20 | width: 1000px; 21 | } 22 | 23 | .ant-input-number { 24 | width: 100%; 25 | } 26 | } 27 | 28 | .apply-config-wrapper { 29 | width: 1000px; 30 | text-align: end; 31 | margin-bottom: 16px; 32 | } 33 | } 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /.vscode/settings.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "typescript.preferences.preferTypeOnlyAutoImports": true, 4 | "typescript.preferences.importModuleSpecifierEnding": "js", 5 | "files.trimTrailingWhitespace": false, 6 | "files.eol": "\n", 7 | "editor.trimAutoWhitespace": false, 8 | "editor.formatOnSave": false, 9 | "editor.formatOnPaste": false, 10 | "editor.tabSize": 4, 11 | "editor.comments.ignoreEmptyLines": false, 12 | "editor.codeActionsOnSave": { 13 | "source.fixAll": "always", 14 | "source.organizeImports": "never" 15 | }, 16 | "gitlens.integrations.enabled": true, 17 | "gitlens.remotes": [{ "domain": "dolphindb.net", "type": "GitLab" }], 18 | "git.pruneOnFetch": true, 19 | "git-graph.dialog.fetchRemote.prune": true 20 | } 21 | -------------------------------------------------------------------------------- /src/components/BottomFixedFooter/index.tsx: -------------------------------------------------------------------------------- 1 | import './index.scss' 2 | import type { HTMLAttributes } from 'react' 3 | 4 | import { model } from '@model' 5 | import { sider_collapsed_width, sider_uncollapsed_width } from '@utils' 6 | 7 | /** 页面里底部固定按钮,比如页面表单很长,然后表单滚动,按钮固定 */ 8 | export function BottomFixedFooter (props: HTMLAttributes) { 9 | const { collapsed } = model.use(['collapsed']) 10 | 11 | const sider_width = `${collapsed ? sider_collapsed_width : sider_uncollapsed_width}px` 12 | return <> 13 |
14 |
21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/computing/icons/table.icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/shell/icons/vector.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 编组 11 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/streaming-graph/Configuration.tsx: -------------------------------------------------------------------------------- 1 | import { Descriptions, Empty } from 'antd' 2 | 3 | import { t } from '@i18n' 4 | 5 | import { sgraph } from './model.ts' 6 | 7 | 8 | export function Configuration () { 9 | const { graph: { graph: { config } } } = sgraph.use(['graph']) 10 | 11 | if (!config || !Object.keys(config).length) 12 | return 13 | 14 | return
15 | 16 | {Object.entries(config) 17 | .map(([key, value]) => 18 | 19 | {typeof value === 'object' ? JSON.stringify(value) : String(value)} 20 | )} 21 | 22 |
23 | } 24 | -------------------------------------------------------------------------------- /src/dashboard/ChartFormFields/components/Wrapper.tsx: -------------------------------------------------------------------------------- 1 | import { Form, InputNumber } from 'antd' 2 | 3 | import { t } from '@i18n' 4 | 5 | export function WrapperFields () { 6 | return <> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/shell/QueryGuide/type.ts: -------------------------------------------------------------------------------- 1 | export enum QueryGuideType { 2 | QUERY_GUIDE, 3 | SQL 4 | } 5 | 6 | 7 | export interface IColumn { 8 | name: string 9 | data_type: string 10 | } 11 | 12 | 13 | export type Query = Array<{ 14 | // 列 15 | col: string 16 | // 数据类型 17 | dataType?: string 18 | // 运算符 19 | opt: number 20 | // 对比值,时间用string 21 | value: number | string 22 | }> 23 | 24 | export interface IQueryInfos { 25 | // 库名 26 | dbName: string 27 | // 表名 28 | tbName: string 29 | // 查询列 30 | queryCols: Array 31 | // 分区列查询条件 32 | partitionColQuerys: Query 33 | // 查询条件块 34 | querys: Array 35 | } 36 | 37 | 38 | export enum ExportStatus { 39 | LOADING, 40 | SUCCESS, 41 | FAILED = -1 42 | } 43 | 44 | 45 | export const ENUM_TYPES = ['STRING', 'INT', 'SYMBOL', 'CHAR', 'BOOL'] 46 | -------------------------------------------------------------------------------- /src/components/StringColorPicker/index.tsx: -------------------------------------------------------------------------------- 1 | import { ColorPicker, type ColorPickerProps } from 'antd' 2 | import { type Color } from 'antd/lib/color-picker' 3 | import { useCallback } from 'react' 4 | 5 | interface IProps extends Omit { 6 | value?: string 7 | onChange?: (str: string) => void 8 | } 9 | 10 | export function StringColorPicker (props: IProps) { 11 | const { onChange, ...others } = props 12 | 13 | const on_color_change = useCallback(color => { 14 | onChange?.(color.toHexString()) 15 | }, [ ]) 16 | 17 | return { onChange(null) }} 25 | /> 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/components/BoolRadioGroup/index.tsx: -------------------------------------------------------------------------------- 1 | import { Radio, type RadioChangeEvent, type RadioGroupProps } from 'antd' 2 | import { useCallback } from 'react' 3 | 4 | import { t } from '@i18n' 5 | 6 | interface IBoolRadioGroupProps extends Omit { 7 | value?: boolean 8 | onChange?: (val: boolean) => void 9 | } 10 | 11 | export function BoolRadioGroup (props: IBoolRadioGroupProps) { 12 | const { value, onChange: onChangeProp, ...otherProps } = props 13 | 14 | const onChange = useCallback( 15 | (e: RadioChangeEvent) => { 16 | onChangeProp?.(e?.target?.value) 17 | }, 18 | [onChangeProp] 19 | ) 20 | 21 | return 22 | {t('是')} 23 | {t('否')} 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/components/DDBTable/index.sass: -------------------------------------------------------------------------------- 1 | .ddb-table 2 | .ant-table-title 3 | padding: 0px !important 4 | width: 100% 5 | 6 | .ddb-table-filter-form 7 | margin-bottom: 10px 8 | 9 | .ddb-table-header 10 | display: flex 11 | justify-content: space-between 12 | align-items: center 13 | 14 | width: 100% 15 | 16 | .ddb-table-title 17 | display: flex 18 | column-gap: 8px 19 | align-items: center 20 | 21 | padding: 10px 0px 22 | 23 | font-size: 18px 24 | 25 | &.big-title 26 | font-size: 1.5em 27 | 28 | .text 29 | font-weight: bold 30 | 31 | .help-icon 32 | cursor: pointer 33 | color: #888888 34 | 35 | -------------------------------------------------------------------------------- /src/data-collection/components/connection-detail/parser-template-view-modal.tsx: -------------------------------------------------------------------------------- 1 | import './index.scss' 2 | import NiceModal, { useModal } from '@ebay/nice-modal-react' 3 | 4 | import { Modal } from 'antd' 5 | 6 | import { ReadonlyEditor } from '../../../components/ReadonlyEditor/index.js' 7 | import type { IParserTemplate } from '../../type.js' 8 | 9 | interface IProps { 10 | template: IParserTemplate 11 | } 12 | 13 | export const TemplateViewModal = NiceModal.create(({ template }: IProps) => { 14 | const modal = useModal() 15 | 16 | return { modal.remove() }} 20 | onCancel={async () => modal.hide()} 21 | footer={null} 22 | width={800} 23 | > 24 | 25 | 26 | }) 27 | -------------------------------------------------------------------------------- /src/access/utils/filter-access-options.ts: -------------------------------------------------------------------------------- 1 | import { ACCESS_OPTIONS } from '../constants.tsx' 2 | import type { AccessCategory } from '../types.ts' 3 | 4 | export function filter_access_options ( 5 | category: AccessCategory, 6 | role: 'user' | 'group', 7 | isAdmin: boolean, 8 | type?: string 9 | ) { 10 | let options = ACCESS_OPTIONS[category] 11 | 12 | if (category === 'script') 13 | if (role === 'user' && isAdmin) 14 | // 对于当前用户是管理员,不能赋予 VIEW_OWNER 权限 15 | options = options.filter(item => item !== 'VIEW_OWNER') 16 | else if (role === 'group' || type === 'deny') 17 | // QUERY_RESULT_MEM_LIMIT 和 TASK_GROUP_MEM_LIMIT 暂不支持组 18 | // deny 不支持 QUERY_RESULT_MEM_LIMIT 和 TASK_GROUP_MEM_LIMIT 19 | options = options.filter(item => item !== 'QUERY_RESULT_MEM_LIMIT' && item !== 'TASK_GROUP_MEM_LIMIT') 20 | 21 | return options 22 | } 23 | -------------------------------------------------------------------------------- /src/data-collection/icons/data-collection.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/guide/constant.ts: -------------------------------------------------------------------------------- 1 | export const TIME_TYPES = ['DATE', 'DATETIME', 'TIMESTAMP', 'NANOTIMESTAMP'] 2 | 3 | export const ENUM_TYPES = ['STRING', 'SYMBOL', 'CHAR' ] 4 | 5 | 6 | export const BASIC_DATA_TYPES = ['BOOL', 'CHAR', 'SHORT', 'INT', 'LONG', 'DATE', 'MONTH', 'TIME', 'MINUTE', 'SECOND', 'DATETIME', 'TIMESTAMP', 'NANOTIME', 'NANOTIMESTAMP', 'FLOAT', 'DOUBLE', 'SYMBOL', 'STRING', 'UUID', 'DATEHOUR', 'IPADDR', 'INT128', 'COMPLEX', 'POINT', 'DECIMAL32', 'DECIMAL64', 'DECIMAL128'] 7 | 8 | export const ARRAY_VECTOR_DATA_TYPES = ['BOOL[]', 'CHAR[]', 'SHORT[]', 'INT[]', 'FLOAT[]', 'DOUBLE[]', 'LONG[]', 'DATE[]', 'MONTH[]', 'TIME[]', 9 | 'MINUTE[]', 'SECOND[]', 'DATETIME[]', 'TIMESTAMP[]', 'NANOTIME[]', 'NANOTIMESTAMP[]', 'DATEHOUR[]', 'DECIMAL32[]', 'DECIMAL64[]', 'DECIMAL128[]', 'IPADDR[]', 'INT128[]', 'POINT[]', 'COMPLEX[]', 'UUID[]'] 10 | 11 | 12 | export const LOW_VERSION_DATA_TYPES = BASIC_DATA_TYPES.filter(item => !item.includes('DECIMAL')) 13 | -------------------------------------------------------------------------------- /src/dashboard/Charts/Configuration/index.sass: -------------------------------------------------------------------------------- 1 | .configuration-diagram 2 | height: 100% 3 | 4 | .actions 5 | position: absolute 6 | 7 | z-index: 5 8 | 9 | bottom: 0px 10 | left: 0px 11 | right: 0px 12 | 13 | margin: 8px 14 | 15 | display: flex 16 | gap: 8px 17 | 18 | align-items: center 19 | 20 | height: 28px 21 | 22 | .time 23 | width: 180px 24 | 25 | .slider 26 | flex: 1 27 | margin: 8px 8px 8px 16px 28 | 29 | .player 30 | padding: 0px 4px 31 | 32 | .player-icon 33 | color: #282828 34 | font-size: 18px 35 | 36 | .realtime 37 | padding: 0px 4px 38 | 39 | .rate 40 | width: 80px 41 | height: 23px 42 | -------------------------------------------------------------------------------- /src/guide/finance-guide/components/ExistedDBSelect.tsx: -------------------------------------------------------------------------------- 1 | import { Select } from 'antd' 2 | import { type SelectProps } from 'antd/lib' 3 | import { useId, useState } from 'react' 4 | 5 | import useSWR from 'swr' 6 | 7 | import { model } from '../../../model.js' 8 | import { t } from '@i18n' 9 | 10 | export function ExistDBSelect (props: SelectProps) { 11 | 12 | const [options, set_options] = useState([ ]) 13 | const id = useId() 14 | 15 | const { isLoading } = useSWR( 16 | ['getClusterDFSDatabases', id], 17 | async () => model.ddb.eval('getClusterDFSDatabases()'), 18 | { onSuccess: (data: any) => { 19 | set_options(data?.value?.map(item => ({ label: item.slice(6), value: item.slice(6) }))) 20 | } } 21 | ) 22 | 23 | return 16 | 17 | 18 | 19 | 20 |
21 | }, [ ]) 22 | 23 | return 31 | } 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/dashboard/Charts/Descriptions/index.scss: -------------------------------------------------------------------------------- 1 | .color-item { 2 | width: 100%; 3 | display: flex; 4 | align-items: center; 5 | 6 | 7 | .ant-space-item:first-child { 8 | width: 80%; 9 | } 10 | .color-item-delete-icon { 11 | cursor: pointer; 12 | margin-bottom: 8px; 13 | } 14 | .ant-form-item { 15 | margin-bottom: 8px; 16 | } 17 | } 18 | 19 | .value-color-values { 20 | margin-bottom: 12px; 21 | } 22 | 23 | .my-descriptions { 24 | height: 100%; 25 | overflow: auto; 26 | background-color: #282828; 27 | &.ant-descriptions .ant-descriptions-row >th, .ant-descriptions .ant-descriptions-row >td { 28 | padding-bottom: 0px; 29 | } 30 | } 31 | 32 | .description-setting-form { 33 | .ant-form-item{ 34 | margin-bottom: 8px; 35 | &:last-child { 36 | margin-bottom: 0px; 37 | } 38 | } 39 | } 40 | 41 | .description-pagination { 42 | margin-top: 12px; 43 | } -------------------------------------------------------------------------------- /src/hooks.ts: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react' 2 | 3 | import type { editor } from 'monaco-editor' 4 | import { useMonaco } from '@monaco-editor/react' 5 | 6 | export function use_monaco_insert (editor: editor.IStandaloneCodeEditor) { 7 | const monaco = useMonaco() 8 | 9 | const on_monaco_insert = useCallback( 10 | (content: string) => { 11 | const position = editor?.getPosition?.() 12 | editor.executeEdits('', [ 13 | { 14 | range: new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column), 15 | text: content 16 | } 17 | ]) 18 | 19 | editor.setPosition({ 20 | lineNumber: position.lineNumber, 21 | column: position.column + content.length 22 | }) 23 | 24 | editor.focus() 25 | }, 26 | [editor] 27 | ) 28 | 29 | return { on_monaco_insert } 30 | } 31 | -------------------------------------------------------------------------------- /src/icons/favicon.italic.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/shell/QueryGuide/components/EnumSelect.tsx: -------------------------------------------------------------------------------- 1 | import { Select } from 'antd' 2 | import useSWR from 'swr' 3 | 4 | import { useCallback, useEffect, useState } from 'react' 5 | 6 | import { query_enums } from '../utils.ts' 7 | import { t } from '@i18n' 8 | 9 | interface IProps { 10 | table: string 11 | database: string 12 | col: string 13 | } 14 | 15 | export function EnumSelect (props: IProps) { 16 | const { table, database, col, ...others } = props 17 | 18 | const [options, set_options] = useState([ ]) 19 | 20 | const get_options = useCallback(async () => { 21 | const opt = await query_enums({ dbName: database, tbName: table, col }) 22 | set_options(opt) 23 | }, [database, table, col ]) 24 | 25 | useEffect(() => { 26 | get_options() 27 | }, [ database, table, col ]) 28 | 29 | 30 | return 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/shell/QueryGuide/components/EnumAutoComplete.tsx: -------------------------------------------------------------------------------- 1 | import { AutoComplete, type AutoCompleteProps } from 'antd' 2 | 3 | import { useCallback, useEffect, useState } from 'react' 4 | 5 | import { t } from '@i18n' 6 | import { query_enums } from '../utils.ts' 7 | 8 | interface IProps extends AutoCompleteProps { 9 | table: string 10 | database: string 11 | col: string 12 | } 13 | 14 | export function EnumAutoComplete (props: IProps) { 15 | const { table, database, col, options: custom_options, ...others } = props 16 | 17 | const [options, set_options] = useState(custom_options) 18 | 19 | const get_options = useCallback(async () => { 20 | const opt = await query_enums({ dbName: database, tbName: table, col }) 21 | set_options(opt) 22 | }, [database, table, col]) 23 | 24 | useEffect(() => { 25 | if (!custom_options?.length) 26 | get_options() 27 | }, [ col, database, table]) 28 | 29 | return 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/login/index.sass: -------------------------------------------------------------------------------- 1 | .login 2 | display: flex 3 | flex-direction: column 4 | align-items: center 5 | 6 | flex: 1 7 | 8 | .logo 9 | margin: 160px 0px 30px 0px 10 | 11 | height: 32px 12 | 13 | display: flex 14 | align-items: center 15 | 16 | &.shf 17 | height: 32px 18 | 19 | .suffix 20 | padding-bottom: 6px 21 | 22 | &.dolphinx 23 | padding-bottom: unset 24 | 25 | .dolphindb, .line, .suffix 26 | height: 100% 27 | 28 | .line 29 | font-size: 26px 30 | line-height: 1 31 | margin: 0px 8px 32 | 33 | .suffix 34 | padding-bottom: 6px 35 | 36 | &.dolphinx 37 | padding-bottom: unset 38 | 39 | .login-form 40 | width: 300px 41 | 42 | .login-button, .oauth-button 43 | width: 100% 44 | 45 | .oauth-button .ant-btn-icon 46 | margin-top: 1px 47 | margin-right: 6px 48 | -------------------------------------------------------------------------------- /src/index.shf.sass: -------------------------------------------------------------------------------- 1 | .root .app.shf 2 | --header-background: #ffffff 3 | --header-background-light: #ffffff 4 | --header-color: #000000 5 | --header-section-padding: 4px 6 | 7 | .shell .tabs 8 | background-color: #ffffff 9 | 10 | .tab 11 | background-color: unset 12 | 13 | .shell .content .top .editor-resizable .shell-editor .toolbar 14 | border-top: 1px solid #dddddd 15 | 16 | .shell .left-panels .panel .type 17 | background-color: unset 18 | box-shadow: unset 19 | border: 1px solid #dddddd 20 | 21 | .shell .git .block-title 22 | background-color: unset 23 | box-shadow: unset 24 | border: 1px solid #dddddd 25 | 26 | .ddb-header 27 | border-bottom: 1px solid #e0e0e0 28 | height: 40px 29 | 30 | .logo .line 31 | font-size: 18px 32 | 33 | .compile-and-refresh 34 | font-size: 14px 35 | 36 | .host-select.ant-select .ant-select-selector .ant-select-selection-item 37 | font-size: 14px 38 | 39 | .license, .node-info 40 | font-size: 14px 41 | -------------------------------------------------------------------------------- /src/inspection/modals/LogModal.tsx: -------------------------------------------------------------------------------- 1 | import NiceModal, { useModal } from '@ebay/nice-modal-react' 2 | import { t } from '@i18n' 3 | import { Editor } from '@monaco-editor/react' 4 | import { Modal } from 'antd' 5 | import useSWR from 'swr' 6 | 7 | import { inspection } from '@/inspection/model.ts' 8 | 9 | interface LogModalProps { 10 | node?: string 11 | report_id: string 12 | } 13 | 14 | 15 | export const LogModal = NiceModal.create(({ report_id, node }: LogModalProps) => { 16 | const modal = useModal() 17 | 18 | const { data: logs, isLoading } = useSWR(['get_logs', report_id, node], async () => inspection.get_logs(report_id, node)) 19 | return 28 | 41 | 42 | }) 43 | -------------------------------------------------------------------------------- /src/components/copy/copy.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/inspection/modals/AddParamsModal.tsx: -------------------------------------------------------------------------------- 1 | import NiceModal, { useModal } from '@ebay/nice-modal-react' 2 | import { t } from '@i18n' 3 | import { Modal } from 'antd' 4 | 5 | import { useState } from 'react' 6 | 7 | 8 | import type { MetricsWithStatus } from '@/inspection/type.ts' 9 | import { MetricTable } from '@/inspection/components/MetricTable.tsx' 10 | 11 | interface AddParamsModalProps { 12 | checked_metrics: MetricsWithStatus[] 13 | set_checked_metrics: (metrics: MetricsWithStatus[]) => void 14 | } 15 | 16 | export const AddParamsModal = NiceModal.create(({ 17 | checked_metrics, 18 | set_checked_metrics 19 | }: AddParamsModalProps) => { 20 | const modal = useModal() 21 | 22 | const [footer, setFooter] = useState(null) 23 | 24 | return 33 | 39 | 40 | }) 41 | 42 | -------------------------------------------------------------------------------- /src/dashboard/Charts/Variables/index.tsx: -------------------------------------------------------------------------------- 1 | import './index.scss' 2 | 3 | import { VariableForm } from '../../GraphItem/VariableForm.js' 4 | import type { GraphComponentProps } from '@/dashboard/graphs.js' 5 | 6 | interface IVariableConfig { 7 | title?: string 8 | title_size: number 9 | variable_ids?: string[] 10 | variable_cols: number 11 | with_search_btn: boolean 12 | search_btn_label?: string 13 | variable_form_label_col?: number 14 | } 15 | 16 | 17 | export function Variables ({ widget }: GraphComponentProps) { 18 | const config = widget.config as IVariableConfig 19 | 20 | return <> 21 | { 22 | config?.title && 23 |
24 | {config.title} 25 |
26 | } 27 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/components/StringTimePicker.tsx: -------------------------------------------------------------------------------- 1 | import { TimePicker, type TimePickerProps } from 'antd' 2 | import dayjs from 'dayjs' 3 | import { useCallback, useMemo } from 'react' 4 | interface IProps extends Omit { 5 | onChange?: (time: string) => void 6 | value?: string 7 | submitSuffix?: string 8 | } 9 | 10 | export function StringTimePicker (props: IProps) { 11 | const { format = 'HH:mm:ss', onChange, value, submitSuffix, ...others } = props 12 | 13 | const on_value_change = useCallback((value, time) => { 14 | if (!value) { 15 | onChange(null) 16 | return 17 | } 18 | if (submitSuffix) 19 | onChange(time + submitSuffix) 20 | else 21 | onChange(time) 22 | }, [submitSuffix]) 23 | 24 | const val = useMemo(() => { 25 | if (!value || !dayjs(value, format as string).isValid()) 26 | return null 27 | let time = submitSuffix ? value.replace(submitSuffix, '') : value 28 | return time ? dayjs(value, format as string) : null 29 | }, [submitSuffix, value, format]) 30 | 31 | 32 | return 38 | } 39 | -------------------------------------------------------------------------------- /src/variables.sass: -------------------------------------------------------------------------------- 1 | $color: var(--vscode-editor-foreground, #000000) 2 | $border-color: var(--vscode-tree-tableColumnsBorder, #60606020) 3 | $background-color: var(--vscode-editor-background, #ffffff) 4 | $icon-color: var(--vscode-editorGroupHeader-tabsBackground, #888888) 5 | $hover-color: var(--vscode-sideBar-background, #f8f8f8) 6 | $table-head-color: var(--vscode-sideBar-background, #eeeeee) 7 | $font-color: var(--vscode-editor-foreground, #000000) 8 | 9 | $dropdown-background-color: var(--vscode-dropdown-background, #ffffff) 10 | $dropdown-foreground-color: var(--vscode-dropdown-foreground, #000000d9) 11 | $dropdown-border-color: var(--vscode-dropdown-border, #ffffff) 12 | $dropdown-active-background-color: var(--vscode-inputOption-hoverBackground, #f5f5f5) 13 | $dropdown-selected-background-color: var(--vscode-editor-selectionBackground, #eeeeee) 14 | 15 | $input-background-color: var(--vscode-input-background, #ffffff) 16 | $input-foreground-color: var(--vscode-input-foreground, #000000d9) 17 | 18 | $line-number-foreground-color: var(--vscode-editorLineNumber-foreground, #000000d9) 19 | $line-number-active-foreground-color: var(--vscode-editorLineNumber-activeForeground, #000000) 20 | 21 | $tree-node-hover-color: var(--vscode-inputOption-hoverBackground, var(--ant-color-primary)) 22 | 23 | $border: 1px solid #e0e0e0 24 | -------------------------------------------------------------------------------- /src/guide/components/UploadConfigModal.tsx: -------------------------------------------------------------------------------- 1 | import NiceModal, { useModal } from '@ebay/nice-modal-react' 2 | import { Form, Modal } from 'antd' 3 | import { useCallback } from 'react' 4 | 5 | import { safe_json_parse } from '../../dashboard/utils.ts' 6 | 7 | import { t } from '@i18n' 8 | 9 | import { UploadFileField } from './UploadFileField.js' 10 | 11 | interface IProps { 12 | apply: (info: any) => void 13 | } 14 | 15 | export const UploadConfigModal = NiceModal.create((props: IProps) => { 16 | const { apply } = props 17 | const [form] = Form.useForm() 18 | 19 | const modal = useModal() 20 | 21 | const on_apply = useCallback(async () => { 22 | try { 23 | const { file } = await form.validateFields() 24 | const config = safe_json_parse(await file.file.text()) 25 | apply(config) 26 | await modal.hide() 27 | } catch { } 28 | }, [ apply ]) 29 | 30 | return 38 |
39 | 40 | 41 |
42 | }) 43 | -------------------------------------------------------------------------------- /src/shell/icons/partition-file.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/TableOperations/index.tsx: -------------------------------------------------------------------------------- 1 | import { EllipsisOutlined } from '@ant-design/icons' 2 | import './index.scss' 3 | import { Popover, Space } from 'antd' 4 | import { toArray } from 'lodash' 5 | 6 | interface IProps extends React.HTMLAttributes { 7 | /** 最多展示的表单操作项,不传则展示全部 */ 8 | max_show_count?: number 9 | } 10 | 11 | export function TableOperations (props: IProps) { 12 | 13 | const { max_show_count, children, ...others } = props 14 | const child_nodes = toArray(children).filter(Boolean) 15 | 16 | const ellipsis = max_show_count ? child_nodes.length > max_show_count : false 17 | 18 | const visible_items = ellipsis ? child_nodes.slice(0, max_show_count) : children 19 | 20 | return
21 | {visible_items} 22 | {ellipsis && 29 | {child_nodes.slice(max_show_count)} 30 | 31 | }> 32 | 33 | } 34 |
35 | } 36 | -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.icon.svg' { 2 | const component: React.ComponentType> 3 | export default component 4 | } 5 | 6 | declare module '*.svg' { 7 | const text: string 8 | export default text 9 | } 10 | 11 | declare module '*.png' { 12 | const text: string 13 | export default text 14 | } 15 | 16 | declare module '*.jpg' { 17 | const text: string 18 | export default text 19 | } 20 | 21 | declare module '*.ico' { 22 | const text: string 23 | export default text 24 | } 25 | 26 | declare module '*.csv' { 27 | const text: string 28 | export default text 29 | } 30 | 31 | declare module '*.txt' { 32 | const text: string 33 | export default text 34 | } 35 | 36 | declare module '*.dos' { 37 | const text: string 38 | export default text 39 | } 40 | 41 | declare module '*.sass' { 42 | const classes: { readonly [key: string]: string } 43 | export default classes 44 | } 45 | 46 | interface Window { 47 | model?: import('react-object-model').Model 48 | shell?: import('react-object-model').Model 49 | dashboard?: import('react-object-model').Model 50 | } 51 | 52 | interface HTMLCollection { 53 | [Symbol.iterator](): ArrayIterator; 54 | } 55 | 56 | declare const WEB_VERSION: string 57 | 58 | declare var PRODUCTION: boolean 59 | -------------------------------------------------------------------------------- /src/dashboard/DataSource/InsertVariableBtn.tsx: -------------------------------------------------------------------------------- 1 | import { Button, type MenuProps, type ButtonProps, Dropdown } from 'antd' 2 | import { useMemo } from 'react' 3 | import { PlusSquareOutlined } from '@ant-design/icons' 4 | 5 | import { variables } from '../Variable/variable.js' 6 | import { t } from '@i18n' 7 | 8 | interface IProps extends ButtonProps { 9 | on_insert: (val: string) => void 10 | } 11 | 12 | export function InsertVariableBtn (props: IProps) { 13 | const { on_insert, ...others } = props 14 | 15 | 16 | const { variable_infos = [ ] } = variables.use(['variable_infos']) 17 | 18 | const items = useMemo(() => { 19 | return variable_infos.map(item => ({ 20 | label: { e.preventDefault() }} onClick={e => { 21 | e.preventDefault() 22 | try { 23 | on_insert?.(`{{${item.name}}}`) 24 | } catch { } 25 | }}>{item.name}, 26 | key: item.id 27 | })) 28 | }, [variable_infos, on_insert]) 29 | 30 | return !!variable_infos?.length && 31 | 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/icons/logos/iotpro.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/plugins/upgrade.icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/shell/DataView.tsx: -------------------------------------------------------------------------------- 1 | import { type DdbObj, type DdbValue } from 'dolphindb/browser.js' 2 | 3 | import { Obj, type DdbObjRef } from '@/obj.tsx' 4 | 5 | import { model } from '@model' 6 | 7 | import { ExportCsv } from '@components/ExportCsv.tsx' 8 | import { LineageGraph } from '@/lineage/index.tsx' 9 | 10 | import { shell } from './model.ts' 11 | 12 | 13 | export function DataView () { 14 | const { result } = shell.use(['result']) 15 | const { options, product_name } = model.use(['options', 'product_name']) 16 | 17 | return
{ 18 | (() => { 19 | if (!result) 20 | return 21 | 22 | const { type } = result 23 | 24 | if (type === 'lineage') 25 | return 26 | 27 | const { data } = result 28 | 29 | return } 37 | : 38 | { objref: data as DdbObjRef }} 39 | /> 40 | })() 41 | }
42 | } 43 | -------------------------------------------------------------------------------- /src/data-collection/icons/connection.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/data-collection/components/create-subscribe-modal/index.scss: -------------------------------------------------------------------------------- 1 | .create-subscribe-modal { 2 | 3 | 4 | .ant-modal-body { 5 | max-height: 640px; 6 | overflow: auto; 7 | } 8 | 9 | .parser-template-params { 10 | padding: 16px; 11 | background-color: rgba(0, 0, 0, 0.02); 12 | margin-bottom: 16px; 13 | 14 | h4 { 15 | margin-top: 0px; 16 | } 17 | 18 | .parser-template-header-icon { 19 | margin-left: 4px; 20 | } 21 | } 22 | 23 | 24 | .ant-input-number { 25 | width: 300px; 26 | } 27 | } 28 | 29 | .parser-template-label { 30 | display: flex; 31 | justify-content: space-between; 32 | align-items: center; 33 | } 34 | 35 | .kafka-params-list-wrapper { 36 | padding: 16px; 37 | background-color: rgba(0, 0, 0, 0.02); 38 | margin-bottom: 16px; 39 | 40 | .kafka-params-title { 41 | font-weight: 600; 42 | margin-bottom: 16px; 43 | 44 | .kafka-link-doc { 45 | display: inline-block; 46 | margin-right: 4px; 47 | } 48 | } 49 | 50 | .kafka-params-item { 51 | width: 100%; 52 | display: flex; 53 | justify-content: space-around; 54 | 55 | .delete-icon-btn { 56 | padding: 4px !important; 57 | } 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /src/components/DDBHeader/Logo.tsx: -------------------------------------------------------------------------------- 1 | import { model } from '@model' 2 | 3 | import dolphindb from '@/icons/logos/dolphindb.svg' 4 | import dolphinx from '@/icons/logos/dolphinx.svg' 5 | import iotbasic from '@/icons/logos/iotbasic.svg' 6 | import iotpro from '@/icons/logos/iotpro.svg' 7 | import octopus from '@/icons/logos/octopus.svg' 8 | import orca from '@/icons/logos/orca.svg' 9 | import shark from '@/icons/logos/shark.svg' 10 | import swordfish from '@/icons/logos/swordfish.svg' 11 | 12 | import dolphindb_shf from '@/icons/logos/dolphindb.shf.svg' 13 | import dolphindb_color from '@/icons/logos/dolphindb.color.svg' 14 | 15 | 16 | export function Logo ({ header }: { header: boolean }) { 17 | const { shf, product } = model.use(['shf', 'product']) 18 | 19 | return
20 | 21 | { product !== 'dolphindb' && <> 22 |
|
23 | 27 | } 28 |
29 | } 30 | 31 | 32 | const logos = { 33 | dolphindb, 34 | dolphinx, 35 | iotbasic, 36 | iotpro, 37 | octopus, 38 | orca, 39 | shark, 40 | swordfish 41 | } 42 | -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | # 不推荐通过 nginx 代理 dolphindb 网页,主要是性能方面的考量 2 | # 使用 nginx 进行转发时,它需要处理从客户端到服务器的请求和响应。这个额外的处理过程可能导致性能的轻微降低,尤其是在高负载情况下。 3 | # 虽然 nginx 通常是一个高效的服务器,但在某些情况下,直接将流量发送到目标服务器可能更为高效。 4 | # nginx 不适合 WebSocket 长连接,对于 WebSocket 的支持可能不如其他服务器,因为 nginx 在设计上更侧重于短连接的高性能 5 | 6 | 7 | http { 8 | map $http_upgrade $connection_upgrade { 9 | default upgrade; 10 | '' close; 11 | } 12 | 13 | server { 14 | # dolphindb web 目前只支持部署到 / 或者 /dolphindb/ 这两个路径下面 15 | # dolphindb web currently only supports deployment to / or /dolphindb/ under these two paths 16 | location /dolphindb/ { 17 | proxy_pass http://192.168.1.111:8848/; 18 | 19 | proxy_http_version 1.1; 20 | 21 | proxy_set_header Host $host; 22 | proxy_set_header X-Real-IP $remote_addr; 23 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 24 | 25 | 26 | proxy_set_header Upgrade $http_upgrade; 27 | proxy_set_header Connection $connection_upgrade; 28 | 29 | proxy_connect_timeout 30000; 30 | proxy_read_timeout 7d; 31 | proxy_send_timeout 7d; 32 | 33 | client_max_body_size 2g; 34 | } 35 | } 36 | } 37 | 38 | # ref: 39 | # https://www.jianshu.com/p/6205c8769e3c 40 | # http://nginx.org/en/docs/http/websocket.html 41 | -------------------------------------------------------------------------------- /src/data-collection/components/view-log-modal/index.tsx: -------------------------------------------------------------------------------- 1 | import './index.scss' 2 | 3 | import NiceModal, { useModal } from '@ebay/nice-modal-react' 4 | import { List, Modal, Typography } from 'antd' 5 | 6 | import useSWR from 'swr' 7 | 8 | import { t } from '@i18n' 9 | import { request } from '../../utils.ts' 10 | import type { Protocol } from '../../type.js' 11 | 12 | interface IProps { 13 | protocol: Protocol 14 | } 15 | 16 | export const ViewLogModal = NiceModal.create(({ protocol }: IProps) => { 17 | const modal = useModal() 18 | 19 | const { data = [ ], isLoading } = useSWR( 20 | ['dcp_getLog', protocol], 21 | async () => request('dcp_getLog', { protocol }) 22 | ) 23 | 24 | return 32 | 40 | 41 | {item} 42 | 43 | } 44 | /> 45 | 46 | }) 47 | -------------------------------------------------------------------------------- /src/icons/logos/shark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/shell/ExecuteAction.tsx: -------------------------------------------------------------------------------- 1 | import { Popconfirm } from 'antd' 2 | import { CaretRightOutlined, LoadingOutlined } from '@ant-design/icons' 3 | 4 | import { t } from '@i18n' 5 | 6 | import { model } from '../model.js' 7 | 8 | import { shell } from './model.js' 9 | 10 | 11 | export function ExecuteAction () { 12 | const { executing, show_executing } = shell.use(['executing', 'show_executing']) 13 | 14 | return { 20 | await model.ddb.cancel() 21 | model.message.success(t('取消作业指令发送成功')) 22 | }} 23 | > 24 | { 28 | try { 29 | // shell 上的 executing 状态才是最新的 30 | if (!shell.executing) 31 | await shell.execute('all') 32 | } catch (error) { 33 | // 忽略用户执行脚本的报错 34 | } 35 | }} 36 | > 37 | {executing && show_executing ? : } 38 | {executing && show_executing ? t('执行中') : t('执行')} 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/overview/icons/overview.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | jiqun备份 4 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/shell/icons/database.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/shell/QueryGuide/components/ColSelectTransfer.tsx: -------------------------------------------------------------------------------- 1 | import { Transfer } from 'antd' 2 | 3 | import { useCallback, useMemo, useState } from 'react' 4 | import { CheckCircleOutlined, CloseCircleOutlined, MinusCircleOutlined } from '@ant-design/icons' 5 | 6 | import { type IColumn } from '../type.js' 7 | import { t } from '@i18n' 8 | 9 | interface IProps { 10 | cols: IColumn[] 11 | onChange?: (keys: string[]) => void 12 | value?: string[] 13 | } 14 | 15 | export function ColSelectTransfer (props: IProps) { 16 | const { cols, value = [ ], onChange } = props 17 | 18 | const options = useMemo(() => cols.map(item => ({ title: item.name, key: item.name })), [cols]) 19 | 20 | const on_value_change = useCallback((keys: string[]) => { 21 | onChange(keys) 22 | }, [ ]) 23 | 24 | return 27 | 28 | {t('非查询列')} 29 |
, 30 |
31 | 32 | {t('查询列')} 33 |
34 | ]} 35 | className='col-select-transfer' 36 | showSearch 37 | onChange={on_value_change} 38 | targetKeys={value ?? [ ]} 39 | render={item => item.title} 40 | dataSource={options} 41 | operations={[t('增加查询列'), t('删除查询列')]} 42 | /> 43 | } 44 | -------------------------------------------------------------------------------- /src/dashboard/ChartFormFields/index.scss: -------------------------------------------------------------------------------- 1 | .field-wrapper { 2 | padding: 8px 0px; 3 | 4 | .ant-space { 5 | width: 100%; 6 | .ant-space-item:first-child { 7 | flex-grow: 1; 8 | } 9 | } 10 | } 11 | 12 | .axis-wrapper { 13 | .ant-form-item{ 14 | margin-bottom: 8px; 15 | &:last-child { 16 | margin-bottom: 0px; 17 | } 18 | } 19 | } 20 | 21 | .divider { 22 | margin: 4px 0px; 23 | } 24 | 25 | 26 | .delete-icon { 27 | margin-left: 4px; 28 | } 29 | 30 | .yasix-collapse-wrapper { 31 | .add-yaxis-btn { 32 | margin-top: 12px; 33 | } 34 | } 35 | 36 | collapse-label { 37 | display: flex; 38 | justify-content: space-between; 39 | } 40 | 41 | 42 | .add-series-btn { 43 | margin-top: 12px; 44 | } 45 | 46 | .ant-picker-dropdown .ant-picker-time-panel-column { 47 | width: 100px; 48 | } 49 | 50 | .col-select-space { 51 | width: 100%; 52 | .ant-space-item { 53 | width: 50%; 54 | } 55 | } 56 | 57 | 58 | 59 | .threshold-list { 60 | background-color:rgb(20,20,20); 61 | padding: 8px; 62 | 63 | .threshold-item { 64 | display: flex; 65 | justify-content: space-between; 66 | margin-bottom: 8px; 67 | .ant-form-item { 68 | margin-bottom: 0px !important; 69 | } 70 | } 71 | 72 | .threshold-add-btn { 73 | text-align: right; 74 | .threshold-add-icon { 75 | margin-right: 4px; 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /src/components/StringDatePicker/index.tsx: -------------------------------------------------------------------------------- 1 | import { DatePicker, type DatePickerProps } from 'antd' 2 | import dayjs, { type Dayjs } from 'dayjs' 3 | import { useCallback, useMemo } from 'react' 4 | 5 | interface IProps extends Omit { 6 | value?: string 7 | onChange?: (val: string) => void 8 | submitFormat?: string 9 | showTime?: boolean 10 | submitSuffix?: string 11 | } 12 | 13 | export function StringDatePicker (props: IProps) { 14 | // 使用 submitFormat 是因为 ddb 内时间格式固定将年月日以 . 连接,但是实际在组件内展示的标准时间格式是以 - 连接,所以提交表单的格式与展示格式不一致 15 | const { onChange, value, submitFormat = 'YYYY.MM.DD', submitSuffix, format, ...others } = props 16 | 17 | const on_date_change = useCallback((value: Dayjs) => { 18 | if (!value) { 19 | onChange(null) 20 | return 21 | } 22 | if (submitSuffix) 23 | onChange(value.format(submitFormat) + submitSuffix) 24 | else 25 | onChange(value.format(submitFormat)) 26 | }, [ submitSuffix ]) 27 | 28 | const val = useMemo(() => { 29 | if (!value || !dayjs(value, format as string).isValid()) 30 | return null 31 | let time = value 32 | if (submitSuffix) 33 | time = submitSuffix ? time.replace(submitSuffix, '') : time 34 | return dayjs(time, format as string) 35 | }, [ value, format ]) 36 | 37 | return 38 | } 39 | -------------------------------------------------------------------------------- /src/components/TableCellDetail/index.tsx: -------------------------------------------------------------------------------- 1 | import { Typography } from 'antd' 2 | import { type ParagraphProps } from 'antd/lib/typography/Paragraph.js' 3 | 4 | import { model } from '../../model.js' 5 | import { t } from '@i18n' 6 | 7 | 8 | interface IProps extends Omit { 9 | title: string 10 | content: string 11 | detail_text?: string 12 | max_line?: number 13 | } 14 | 15 | 16 | export function TableCellDetail (props: IProps) { 17 | const { title, 18 | content, 19 | detail_text = t('详细'), 20 | max_line = 2, 21 | ...otherProps } = props 22 | 23 | function detail () { 24 | model.modal.info({ 25 | title, 26 | content, 27 | width: '80%' 28 | }) 29 | } 30 | return { 39 | event.stopPropagation() 40 | detail() 41 | }} 42 | > 43 | {detail_text} 44 | 45 | ) 46 | }} 47 | > 48 | {content} 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/job/index.sass: -------------------------------------------------------------------------------- 1 | .job 2 | height: 100% 3 | overflow-y: auto 4 | 5 | .actions 6 | padding-top: 10px 7 | 8 | display: flex 9 | 10 | .title 11 | font-size: 1.5em 12 | font-weight: bold 13 | margin-right: 20px 14 | 15 | .search 16 | margin-right: 20px 17 | flex: 1 18 | 19 | .spin-container 20 | height: 100% 21 | display: flex 22 | justify-content: center 23 | align-items: center 24 | 25 | 26 | .cjobs 27 | margin-bottom: 20px 28 | 29 | 30 | 31 | .nojobs 32 | margin-bottom: 20px 33 | 34 | 35 | .ellipsis 36 | display: -webkit-box 37 | -webkit-box-orient: vertical 38 | -webkit-line-clamp: 2 39 | overflow: hidden 40 | 41 | 42 | .action-col 43 | display: flex 44 | white-space: nowrap 45 | gap: 12px 46 | 47 | .job-message 48 | overflow: auto 49 | 50 | .job-message-modal 51 | .ant-modal-content 52 | overflow: auto 53 | max-height: 80vh 54 | display: flex 55 | flex-flow: column 56 | .ant-modal-body 57 | flex:1 58 | height: 0 59 | display: flex 60 | flex-flow: column 61 | overflow: auto 62 | .job-message 63 | height:80vh 64 | padding-bottom: 5px 65 | .copy-button 66 | margin-top: 8px -------------------------------------------------------------------------------- /src/streaming/hooks/use-streaming.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { DDB, type StreamingMessage } from 'dolphindb/browser.js' 3 | 4 | export function use_streaming ( 5 | { 6 | url, 7 | username, 8 | password, 9 | table 10 | }: { 11 | url: string | undefined 12 | username: string 13 | password: string 14 | table: string 15 | }, 16 | { onSuccess, onError, onReceivedStreamingData }: Record any) | undefined> 17 | ) { 18 | useEffect(() => { 19 | // 订阅流数据 20 | const rddb = new DDB(url, { 21 | autologin: Boolean(username), 22 | username, 23 | password, 24 | streaming: { 25 | table: table, 26 | // 订阅流表 27 | handler (message: StreamingMessage) { 28 | // 收集数据(新数据放到后面) 29 | if (message.error) { 30 | console.error(message.error) 31 | return 32 | } 33 | onReceivedStreamingData && onReceivedStreamingData(message) 34 | } 35 | } 36 | }) 37 | ;(async () => { 38 | try { 39 | await rddb.connect() 40 | onSuccess && onSuccess() 41 | } catch (e) { 42 | onError && onError(e) 43 | } 44 | })() 45 | return () => { 46 | rddb.disconnect() 47 | } 48 | }, [url, table, password, username]) 49 | } 50 | -------------------------------------------------------------------------------- /src/guide/components/DownloadConfigModal.tsx: -------------------------------------------------------------------------------- 1 | import NiceModal, { useModal } from '@ebay/nice-modal-react' 2 | import { Form, Input, Modal } from 'antd' 3 | import { useCallback } from 'react' 4 | 5 | import { t } from '@i18n' 6 | 7 | interface IProps { 8 | config: any 9 | } 10 | 11 | export const DownloadConfigModal = NiceModal.create((props: IProps) => { 12 | const { config } = props 13 | const modal = useModal() 14 | const [form] = Form.useForm() 15 | 16 | const on_download_config = useCallback(async () => { 17 | try { 18 | const { name } = await form.validateFields() 19 | let a = document.createElement('a') 20 | a.download = `${name}.json` 21 | a.href = URL.createObjectURL( 22 | new Blob([JSON.stringify(config, null, 4)], { type: 'application/json' }) 23 | ) 24 | document.body.appendChild(a) 25 | a.click() 26 | document.body.removeChild(a) 27 | modal.hide() 28 | } catch { } 29 | }, [config]) 30 | 31 | 32 | 33 | return 40 |
41 | 42 | 43 | 44 |
45 |
46 | }) 47 | -------------------------------------------------------------------------------- /src/computing/CEPComputing/api.ts: -------------------------------------------------------------------------------- 1 | import { safe_json_parse, sql_formatter } from '../../dashboard/utils.ts' 2 | import { model } from '../../model.js' 3 | 4 | import { type ICEPEngineDetail, type CEPEngineItem, type DataViewEngineItem, type IServerEngineDetail } from './type.js' 5 | 6 | 7 | export async function get_cep_engine_list () { 8 | const res = await model.ddb.eval('getStreamEngineStat().CEPEngine') 9 | return res?.value ? sql_formatter(res) as CEPEngineItem[] : [ ] 10 | } 11 | 12 | 13 | export async function get_cep_engine_detail (name: string) { 14 | const res = await model.ddb.invoke('getCEPEngineStat', [name]) 15 | 16 | 17 | return { 18 | ...res, 19 | eventSchema: res?.eventSchema?.map(item => ({ 20 | eventValuesTypeIntList: item.fieldTypeId, 21 | eventType: item.eventType, 22 | eventField: item.eventField ? item.eventField.split(',') : [ ], 23 | eventValuesTypeStringList: item.fieldType ? item.fieldType.split(',') : [ ], 24 | eventFormIdList: item.fieldFormId, 25 | })) 26 | } as ICEPEngineDetail 27 | } 28 | 29 | 30 | export async function get_dataview_info (engine_name: string, dataview_name: string) { 31 | const table = await model.ddb.invoke[]>('getDataViewEngine', [engine_name, dataview_name]) 32 | const engine_detail = await get_cep_engine_detail(engine_name) 33 | const key_cols = engine_detail?.dataViewEngines?.find(item => item.name === dataview_name)?.keyColumns?.split(',') 34 | 35 | return { table, key_cols } 36 | } 37 | -------------------------------------------------------------------------------- /src/dashboard/SettingsPanel/CanvasSetting.tsx: -------------------------------------------------------------------------------- 1 | import { type CollapseProps, Form, Input, Collapse } from 'antd' 2 | 3 | import { t } from '@i18n' 4 | 5 | export function CanvasSetting () { 6 | interface SettingOptions { 7 | title: string 8 | column: number 9 | row: number 10 | } 11 | 12 | const canvas_collapsse: CollapseProps['items'] = [ 13 | { 14 | key: '1', 15 | label: t('基础'), 16 | children: ( 17 | label={t('标题')} name='title'> 18 | 19 | 20 | ) 21 | }, 22 | { 23 | key: '2', 24 | label: t('画布样式'), 25 | children: ( 26 |
27 | label={t('列数')} name='column'> 28 | 29 | 30 | label={t('行数')} name='row'> 31 | 32 | 33 |
34 | ) 35 | } 36 | ] 37 | 38 | return
47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/streaming-graph/flow.icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/shell/icons/variable.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | variable.icon 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/access/icons/group.icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/FormDependencies/index.tsx: -------------------------------------------------------------------------------- 1 | import { useMount, useUnmount, useUpdate, useUpdateEffect } from 'ahooks' 2 | import { Form, type FormInstance } from 'antd' 3 | 4 | import type { NamePath } from 'antd/es/form/interface' 5 | 6 | interface IInternalFormWatchProps { 7 | name: NamePath 8 | onMount?: (value: any) => void 9 | onChange?: (value: any) => void 10 | onUnmount?: () => void 11 | form: FormInstance 12 | preserve?: boolean 13 | } 14 | 15 | function InternalFormWatch (props: IInternalFormWatchProps) { 16 | const { name, onChange, onMount, onUnmount, form, preserve } = props 17 | const value = Form.useWatch(name, { 18 | form, 19 | preserve 20 | }) 21 | 22 | useMount(() => { 23 | onMount?.(value) 24 | }) 25 | 26 | useUnmount(() => { 27 | onUnmount?.() 28 | }) 29 | 30 | useUpdateEffect(() => { 31 | onChange?.(value) 32 | }, [value]) 33 | 34 | return null 35 | } 36 | 37 | 38 | export function FormDependencies ({ dependencies, form: propForm, children }: { 39 | dependencies: NamePath[] 40 | form?: FormInstance 41 | children: (values: any, form: FormInstance) => React.ReactNode 42 | }) { 43 | const contextForm = Form.useFormInstance() 44 | const form = propForm || contextForm 45 | 46 | return <> 47 | {dependencies.map(dep => 48 | )} 54 | {children(form.getFieldsValue(dependencies), form)} 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/config/utils.ts: -------------------------------------------------------------------------------- 1 | import { t } from '@i18n' 2 | 3 | import { node_configs } from './model.ts' 4 | 5 | import { type NodeType, type ControllerConfig, type ClusterNode } from './type.js' 6 | 7 | export const strs_2_controller_configs = (strs: string[]): ControllerConfig[] => 8 | strs.map(str => { 9 | const iequal = str.indexOf('=') 10 | const name = str.slice(0, iequal) 11 | const value = str.slice(iequal + 1) 12 | return { 13 | id: str, 14 | name, 15 | value 16 | } 17 | }) 18 | 19 | 20 | /** 节点配置项转字符串 21 | @param {ControllerConfig[] | ClusterNode[]} items - 节点配置 22 | @return {string[]} 转换的字符串,拼接形成的一行字符串,是以逗号分隔的节点的全部信息 */ 23 | export const _2_strs = (items: ControllerConfig[] | ClusterNode[]): string[] => 24 | items.map(i => i.id) 25 | 26 | 27 | export const strs_2_nodes = (strs: string[]): ClusterNode[] => 28 | strs.map(str => { 29 | const [rest, mode, group = '', zone = ''] = str.split(',') 30 | const [host, port, alias] = rest.split(':') 31 | return { 32 | id: str, 33 | host, 34 | port, 35 | alias, 36 | mode: mode as NodeType, 37 | computeGroup: group, 38 | zone 39 | } 40 | }) 41 | 42 | 43 | export function get_category (name: string) { 44 | for (let category_name in node_configs) 45 | if (node_configs[category_name].includes(name)) 46 | return category_name 47 | 48 | return t('其它') 49 | } 50 | 51 | export const filter_config = (input: string, option?: { label: string, options: any }) => 52 | (option?.label ?? '').toLowerCase().includes(input.toLowerCase()) 53 | -------------------------------------------------------------------------------- /src/dashboard/components/EchartsComponent.tsx: -------------------------------------------------------------------------------- 1 | import './index.sass' 2 | import * as echarts from 'echarts' 3 | import { useEffect, useRef } from 'react' 4 | import { useSize } from 'ahooks' 5 | 6 | import chart_config from '@/dashboard/chart.config.json' with { type: 'json' } 7 | 8 | 9 | interface IProps { 10 | options: echarts.EChartsOption 11 | on_chart_ready?: (chart: echarts.ECharts) => void 12 | not_merge?: boolean 13 | lazy_update?: boolean 14 | replace_merge?: string | string[] 15 | } 16 | 17 | 18 | export function DashboardEchartsComponent (props: IProps) { 19 | const { options, on_chart_ready, not_merge, lazy_update, replace_merge } = props 20 | const div_ref = useRef(null) 21 | 22 | const wrapper_size = useSize(div_ref) 23 | 24 | const chart_ref = useRef(null) 25 | 26 | /** echarts 父级元素高度不定会使得图表无法填满整个空间,需要监听父元素高度,resize 图表 */ 27 | useEffect(() => { 28 | chart_ref?.current?.resize() 29 | }, [wrapper_size]) 30 | 31 | 32 | useEffect(() => { 33 | if (!chart_ref.current) { 34 | chart_ref.current = echarts.init(div_ref.current, chart_config.themeName) 35 | on_chart_ready?.(chart_ref.current) 36 | chart_ref.current.setOption(options) 37 | } else 38 | chart_ref.current.setOption( 39 | options, 40 | { 41 | replaceMerge: replace_merge, 42 | notMerge: not_merge, 43 | lazyUpdate: lazy_update 44 | } 45 | ) 46 | }, [options]) 47 | 48 | return
49 | } 50 | -------------------------------------------------------------------------------- /src/data-collection/components/connection-detail/delete-describe-modal.tsx: -------------------------------------------------------------------------------- 1 | import './index.scss' 2 | import NiceModal, { useModal } from '@ebay/nice-modal-react' 3 | import { Modal, Form, Switch, Button } from 'antd' 4 | 5 | import { useCallback } from 'react' 6 | 7 | 8 | import { t } from '@i18n' 9 | 10 | import { request } from '@/data-collection/utils.ts' 11 | 12 | interface IProps { 13 | ids: string[] 14 | refresh: () => void 15 | } 16 | 17 | 18 | export const DeleteDescribeModal = NiceModal.create(({ ids = [ ], refresh }: IProps) => { 19 | const modal = useModal() 20 | 21 | const on_delete = useCallback(async (params: { dropUseTable: boolean }) => { 22 | await request('dcp_deleteSubscribe', { ...params, ids }) 23 | modal.hide() 24 | refresh() 25 | }, [ids, refresh]) 26 | 27 | 28 | return 35 |
{ on_delete(values) } }> 36 | 43 | 44 | 45 | 46 | 47 | 48 |
49 |
50 | }) 51 | -------------------------------------------------------------------------------- /src/guide/iot-guide/AdvancedVersion/index.scss: -------------------------------------------------------------------------------- 1 | .iot-guide { 2 | height: 100%; 3 | .advanced-version-wrapper { 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | justify-content: center; 9 | 10 | .advanced-step-panel { 11 | width: 1000px; 12 | } 13 | 14 | .guide-step { 15 | width: 600px; 16 | margin: 32px 0px; 17 | } 18 | 19 | .ant-input-number { 20 | width: 100%; 21 | } 22 | 23 | .sort-cols-wrapper { 24 | margin-top: 40px; 25 | background-color: #EFEFEF; 26 | border-radius: 8px; 27 | padding: 16px; 28 | 29 | h4 { 30 | margin-top: 0px; 31 | margin-bottom: 16px; 32 | } 33 | 34 | .sort-col-item { 35 | display: flex; 36 | align-items: center; 37 | justify-content: space-between; 38 | margin-bottom: 16px; 39 | 40 | .ant-form-item { 41 | width: 48%; 42 | margin-bottom: 0px; 43 | } 44 | 45 | } 46 | 47 | } 48 | } 49 | 50 | .btn-group { 51 | display: flex; 52 | flex-direction: end; 53 | } 54 | 55 | .other-sortkey-tip { 56 | display: inline-block; 57 | margin-bottom: 24px; 58 | } 59 | 60 | .apply-config-wrapper { 61 | width: 1000px; 62 | text-align: end; 63 | margin-bottom: 16px; 64 | } 65 | } -------------------------------------------------------------------------------- /src/components/copy/CopyIconButton.tsx: -------------------------------------------------------------------------------- 1 | import './CopyIconButton.scss' 2 | 3 | import React, { useEffect, useState } from 'react' 4 | import cn from 'classnames' 5 | 6 | import { Button, type ButtonProps, Tooltip } from 'antd' 7 | import copy from 'copy-to-clipboard' 8 | 9 | import { t } from '@i18n' 10 | 11 | import SVGCopyIcon from './copy.icon.svg' 12 | import SVGCopiedIcon from './copied.icon.svg' 13 | 14 | 15 | interface CopyIconButtonProps extends Omit { 16 | text: string 17 | onCopy?: () => void 18 | copyOptions?: Parameters[1] 19 | copyTooltips?: [React.ReactNode, React.ReactNode] 20 | } 21 | 22 | const COPY_TOOLTIPS = [t('复制'), t('复制成功')] 23 | 24 | export function CopyIconButton (props: CopyIconButtonProps) { 25 | const { onCopy: on_copy_props, text, copyOptions: copy_options, copyTooltips = COPY_TOOLTIPS, ...button_props } = props 26 | const [copied, set_copied] = useState(false) 27 | 28 | function onCopy () { 29 | copy(text, copy_options) 30 | set_copied(true) 31 | on_copy_props?.() 32 | } 33 | 34 | useEffect(() => { 35 | if (copied) { 36 | const timeout = setTimeout(() => { 37 | set_copied(false) 38 | }, 2000) 39 | return () => { clearTimeout(timeout) } 40 | } 41 | }, [copied]) 42 | 43 | return 44 | 48 | 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/dashboard/Charts/RichText/index.sass: -------------------------------------------------------------------------------- 1 | .quill 2 | height: 95% 3 | .ql-toolbar .ql-stroke 4 | fill: none 5 | stroke: #d7d7d7 6 | 7 | .ql-toolbar .ql-fill 8 | fill: #d7d7d7 9 | stroke: none 10 | 11 | .ql-toolbar .ql-picker 12 | color: #d7d7d7 13 | 14 | .ql-container 15 | height: 80% 16 | background-color: inherit 17 | font-size: 16px 18 | 19 | .ql-picker-options 20 | background-color: #282828 21 | 22 | .rich-text 23 | width: 60% !important 24 | height: 600px 25 | .ant-modal-content 26 | height: 100% 27 | .ant-modal-body 28 | height: 95% 29 | .ant-modal-footer 30 | margin: 0 31 | 32 | .ant-modal-close 33 | top: 30px 34 | inset-inline-end: 30px 35 | 36 | .rich-text-container 37 | display: flex 38 | flex-direction: column 39 | justify-content: center 40 | align-items: center 41 | border: none !important 42 | .edit-rich-text 43 | display: none 44 | 45 | .empty-area,.display-area 46 | width: 100% 47 | height: 100% 48 | color: #fff 49 | 50 | .empty-area 51 | display: flex 52 | justify-content: center 53 | align-items: center 54 | 55 | .display-area 56 | background-color: #282828 57 | font-size:16px 58 | 59 | 60 | .grid-stack-item 61 | &:hover 62 | .edit-rich-text-hover 63 | cursor: pointer 64 | font-size: 14px 65 | color: #fff 66 | display: block 67 | position: absolute 68 | top: -7px 69 | left: 0px 70 | z-index: 100000 71 | 72 | .edit-rich-text-icon 73 | margin-right: 4px 74 | 75 | 76 | .rich-text-insert-btn 77 | margin-bottom: 12px -------------------------------------------------------------------------------- /src/shell/icons/query-guide.icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/data-collection/components/delete-connections-modal/index.tsx: -------------------------------------------------------------------------------- 1 | import NiceModal, { useModal } from '@ebay/nice-modal-react' 2 | import { t } from '@i18n' 3 | import { Button, Form, Modal, Switch } from 'antd' 4 | 5 | import { request } from '@/data-collection/utils.ts' 6 | import { model } from '@model' 7 | 8 | interface IProps { 9 | ids: string[] 10 | after_delete?: () => Promise 11 | name?: string 12 | } 13 | export const DeleteConnectionModal = NiceModal.create((props: IProps) => { 14 | const { ids, after_delete, name } = props 15 | 16 | const modal = useModal() 17 | 18 | async function on_delete (values) { 19 | await request('dcp_deleteConnect', { ids, ...values }) 20 | model.message.success(t('删除成功')) 21 | await after_delete() 22 | modal.hide() 23 | } 24 | 25 | 26 | return 37 |
38 | 45 | 46 | 47 | 48 | 49 | 50 |
51 |
52 | }) 53 | -------------------------------------------------------------------------------- /src/shell/git/Git.tsx: -------------------------------------------------------------------------------- 1 | import './index.sass' 2 | 3 | import { useState } from 'react' 4 | 5 | import { Resizable } from 're-resizable' 6 | 7 | import { Repos } from './Repos.tsx' 8 | import { Files } from './Files.tsx' 9 | import { Commit } from './Commit.tsx' 10 | 11 | export function Git () { 12 | const [selected_repo, set_selected_repo] = useState(undefined) 13 | const [selected_repo_name, set_selected_repo_name] = useState(undefined) 14 | const [selected_branch, set_selected_branch] = useState(undefined) 15 | 16 | function handle_repo_change (id: string, title: string) { 17 | set_selected_repo(id) 18 | set_selected_repo_name(title) 19 | } 20 | 21 | function handle_branch_change (id: string) { 22 | set_selected_branch(id) 23 | } 24 | 25 | return
26 | 27 | 44 | 45 | 46 | 47 |
48 | } 49 | -------------------------------------------------------------------------------- /src/shell/icons/table.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | icon_table备份 6 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/log/index.sass: -------------------------------------------------------------------------------- 1 | .log 2 | padding: unset !important 3 | 4 | flex: 1 5 | min-height: 0 6 | 7 | display: flex 8 | flex-direction: column 9 | 10 | .list 11 | flex: 1 12 | 13 | display: flex 14 | flex-direction: column 15 | 16 | min-height: 0 17 | 18 | /* x偏移量 | y偏移量 | 阴影模糊半径 | 阴影扩散半径 | 阴影颜色 */ 19 | box-shadow: 2px 2px 2px 3px rgba(230, 230, 230, 0.568) 20 | 21 | .log-title 22 | background: #fafafa 23 | padding: 8px 30px 8px 30px 24 | display: flex 25 | justify-content: space-between 26 | align-items: center 27 | font-size: 1.1em 28 | font-weight: bold 29 | 30 | .refresh-button 31 | margin-left: 20px 32 | 33 | .log-login-interceptor 34 | flex: 1 35 | display: flex 36 | align-items: center 37 | justify-content: center 38 | flex-direction: column 39 | 40 | .log-block 41 | flex: 1 42 | min-height: 0 43 | overflow-y: auto 44 | 45 | .log-line 46 | padding: 8px 0px 8px 30px 47 | display: flex 48 | align-items: center 49 | 50 | .log-line:nth-child(even) 51 | background: #fafafa 52 | 53 | .log-pagination 54 | padding-top: 16px 55 | padding-bottom: 16px 56 | display: flex 57 | justify-content: center 58 | box-shadow: 0px -1px 0px 0px #e5e5e5 59 | z-index: 1 60 | 61 | .ant-pagination-item-active 62 | border-color: #444444 63 | 64 | a 65 | color: #444444 66 | -------------------------------------------------------------------------------- /src/dashboard/ChartFormFields/constant.ts: -------------------------------------------------------------------------------- 1 | import { t } from '@i18n' 2 | import { WidgetChartType, WidgetType } from '../model.js' 3 | 4 | import { AxisType, ILineType, ITimeFormat, MarkPresetType, Position } from './type.js' 5 | 6 | export const axis_type_options = [{ 7 | label: t('数据轴'), 8 | value: AxisType.VALUE 9 | }, 10 | { 11 | label: t('类目轴'), 12 | value: AxisType.CATEGORY 13 | }, 14 | { 15 | label: t('时间轴'), 16 | value: AxisType.TIME, 17 | }, 18 | { 19 | label: t('对数'), 20 | value: AxisType.LOG 21 | }] 22 | 23 | export const axis_position_options = [ 24 | { value: Position.LEFT, label: t('左侧') }, 25 | { value: Position.RIGHT, label: t('右侧') } 26 | ] 27 | 28 | export const mark_point_options = [ 29 | { 30 | value: MarkPresetType.MAX, 31 | label: t('最大值') 32 | }, 33 | { 34 | value: MarkPresetType.MIN, 35 | label: t('最小值') 36 | } 37 | ] 38 | 39 | export const mark_line_options = [...mark_point_options, { 40 | value: MarkPresetType.AVERAGE, 41 | label: t('平均值') 42 | }] 43 | 44 | 45 | export const line_type_options = [ 46 | { 47 | value: ILineType.SOLID, 48 | label: t('实线') 49 | }, 50 | { 51 | value: ILineType.DASHED, 52 | label: t('虚线') 53 | }, 54 | { 55 | value: ILineType.DOTTED, 56 | label: t('点线') 57 | }, 58 | 59 | ] 60 | 61 | 62 | export const chart_type_options = [ 63 | { 64 | label: t(WidgetType.BAR), 65 | value: WidgetChartType.BAR 66 | }, 67 | { 68 | label: t(WidgetType.LINE), 69 | value: WidgetChartType.LINE 70 | }, 71 | { 72 | label: t(WidgetType.SCATTER), 73 | value: WidgetChartType.SCATTER 74 | } 75 | ] 76 | 77 | 78 | 79 | 80 | export const format_time_options = Object.values(ITimeFormat).map(val => ({ 81 | label: val, 82 | value: val 83 | })) 84 | -------------------------------------------------------------------------------- /src/computing/icons/engine.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/guide/iot-guide/index.tsx: -------------------------------------------------------------------------------- 1 | import { Radio, Result, Spin } from 'antd' 2 | import { useId, useState } from 'react' 3 | 4 | import useSWR from 'swr' 5 | 6 | import { t } from '@i18n' 7 | 8 | 9 | import { model, NodeType } from '../../model.js' 10 | 11 | import finance_guide_code from '../finance.dos' 12 | import iot_guide_code from '../iot.dos' 13 | 14 | import './index.sass' 15 | 16 | 17 | 18 | import { Unlogin } from '@/components/Unlogin.tsx' 19 | 20 | import { GuideType } from './type.js' 21 | import { SimpleVersion } from './SimpleVersion/index.js' 22 | import { AdvancedVersion } from './AdvancedVersion/index.js' 23 | 24 | const VersionMap = { 25 | [GuideType.SIMPLE]: , 26 | [GuideType.ADVANCED]: 27 | } 28 | 29 | export function CreateGuide () { 30 | const { logined, node_type } = model.use(['logined', 'node_type']) 31 | const [type, set_type] = useState(GuideType.SIMPLE) 32 | const id = useId() 33 | const { isLoading } = useSWR( 34 | ['load_code', id], 35 | async () => { 36 | await model.ddb.eval(finance_guide_code) 37 | await model.ddb.eval(iot_guide_code) 38 | } 39 | ) 40 | 41 | if (!logined) 42 | return 43 | 44 | if (node_type === NodeType.controller) 45 | return 50 | 51 | return 52 | { set_type(e.target.value) }}> 53 | {t('简易版')} 54 | {t('进阶版')} 55 | 56 | 57 | {VersionMap[type]} 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/components/DDBHeader/LanguageSelect.tsx: -------------------------------------------------------------------------------- 1 | import { Dropdown } from 'antd' 2 | 3 | import { t } from '@i18n' 4 | 5 | import { useState } from 'react' 6 | 7 | import { TranslationOutlined } from '@ant-design/icons' 8 | 9 | import { model, storage_keys } from '@model' 10 | 11 | 12 | export function LanguageSelect () { 13 | const [selected, set_selected] = useState(localStorage.getItem(storage_keys.language) || 'auto') 14 | 15 | return { 32 | if (key === 'auto') { 33 | localStorage.removeItem(storage_keys.language) 34 | model.set_query('language', null) 35 | } else { 36 | model.set_query('language', key) 37 | localStorage.setItem(storage_keys.language, key) 38 | } 39 | location.reload() 40 | set_selected(key) 41 | }, 42 | onSelect: ({ key }) => { 43 | set_selected(key) 44 | }, 45 | selectedKeys: [selected], 46 | }} 47 | placement='bottomRight' 48 | trigger={['hover', 'click']} 49 | arrow 50 | className='header-settings' 51 | > 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/guide/icons/finance.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 16 | 18 | 20 | 22 | 23 | -------------------------------------------------------------------------------- /src/guide/components/CodeViewStep.tsx: -------------------------------------------------------------------------------- 1 | import './index.scss' 2 | 3 | import { Button, Popconfirm, Space } from 'antd' 4 | import { useCallback } from 'react' 5 | 6 | import NiceModal from '@ebay/nice-modal-react' 7 | 8 | import { model } from '../../model.js' 9 | 10 | 11 | import { ExecuteResult } from '../iot-guide/type.js' 12 | import { ReadonlyEditor } from '../../components/ReadonlyEditor/index.js' 13 | import { t } from '@i18n' 14 | 15 | import { BottomFixedFooter } from '@/components/BottomFixedFooter/index.js' 16 | 17 | import { DownloadConfigModal } from './DownloadConfigModal.js' 18 | 19 | interface IProps { 20 | code: string 21 | back: () => void 22 | config: any 23 | go: (infos: any) => void 24 | } 25 | 26 | export function CodeViewStep (props: IProps) { 27 | const { code, back, config, go } = props 28 | 29 | const execute_code = useCallback(async () => { 30 | try { 31 | await model.ddb.eval(code) 32 | go({ result: ExecuteResult.SUCCESS } ) 33 | } catch (e) { 34 | go({ result: ExecuteResult.FAILED, error_msg: e.toString() }) 35 | } 36 | }, [code, go]) 37 | 38 | 39 | 40 | const on_download = useCallback(async () => 41 | NiceModal.show(DownloadConfigModal, { config }) 42 | , [config]) 43 | 44 | 45 | return
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
59 | } 60 | -------------------------------------------------------------------------------- /src/guide/iot-guide/type.ts: -------------------------------------------------------------------------------- 1 | export enum GuideType { 2 | SIMPLE = 'simple', 3 | ADVANCED = 'advanced' 4 | } 5 | 6 | export interface ServerRecommendInfo { 7 | partitionInfo?: { 8 | context: string 9 | partitionNum: number 10 | } 11 | sortColumnInfo?: { 12 | context: string 13 | maxOtherSortKeyNum: number 14 | } 15 | 16 | } 17 | 18 | export interface RecommendInfo extends ServerRecommendInfo { 19 | hasAdvancedInfo: boolean 20 | } 21 | 22 | export interface BasicInfoFormValues { 23 | dbName: string 24 | tbName: string 25 | isFreqIncrease: 0 | 1 26 | dailyTotalNum?: { 27 | gap?: number 28 | custom?: number 29 | } 30 | totalNum?: { 31 | gap?: number 32 | custom?: number 33 | } 34 | 35 | pointNum?: number 36 | schema: Array<{ 37 | colName: string 38 | dataType: string 39 | }> 40 | } 41 | 42 | export enum CommonQueryDuration { 43 | HOUR = 'hour', 44 | DAILY = 'daily', 45 | MONTH = 'month' 46 | } 47 | 48 | export enum KeepDuplicates { 49 | ALL, 50 | FIRST, 51 | LAST 52 | } 53 | export interface SecondStepInfo { 54 | // 分区列 55 | partitionColumn: string[] 56 | actomic: 0 | 1 57 | KeepDuplicates 58 | // 常用筛选列 59 | otherSortKeys: Array<{ 60 | // 列名 61 | colName: string 62 | // 降维桶数,可能有 63 | hashMapNum: number 64 | }> 65 | // 常用查询时间跨度 66 | // commQueryDuration: CommonQueryDuration 67 | keepDuplicates: KeepDuplicates 68 | } 69 | 70 | 71 | export interface SimpleSecondStepInfo { 72 | partitionColumn: string[] 73 | } 74 | 75 | 76 | export interface SimpleInfos { 77 | first?: BasicInfoFormValues 78 | code?: string 79 | } 80 | 81 | export interface AdvancedInfos { 82 | first?: BasicInfoFormValues 83 | second?: SecondStepInfo 84 | code?: string 85 | } 86 | 87 | 88 | export enum ExecuteResult { 89 | FAILED, 90 | SUCCESS 91 | } 92 | 93 | -------------------------------------------------------------------------------- /src/overview/icons/disk.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/config/icons/controller.config.icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/guide/components/UploadFileField.tsx: -------------------------------------------------------------------------------- 1 | import './index.scss' 2 | import { CloudUploadOutlined, DeleteOutlined, FileOutlined } from '@ant-design/icons' 3 | import { Form, Typography, Upload } from 'antd' 4 | import { useState } from 'react' 5 | 6 | import { t } from '@i18n' 7 | 8 | interface IProps { 9 | onChange?: (file: File) => void 10 | tip?: string 11 | accept?: string 12 | } 13 | 14 | export function UploadFileField (props: IProps) { 15 | const { tip, accept } = props 16 | const [file, set_file] = useState() 17 | 18 | return { 24 | if (!file) 25 | return Promise.reject(t('请选择文件')) 26 | } }]} 27 | valuePropName='file' 28 | getValueFromEvent={e => Array.isArray(e) ? e[0] : e } 29 | > 30 | { 31 | file ? 32 |
33 |
34 | 35 | {file.name} 36 |
37 | { set_file(null) }} 39 | className='delete-icon' /> 40 |
41 | : { 44 | set_file(file) 45 | return false 46 | }}> 47 |
48 | 49 |
{t('点击或将文件拖拽到此区域')}
50 | {tip && {tip} } 51 |
52 |
53 | } 54 |
55 | } 56 | -------------------------------------------------------------------------------- /src/computing/CEPComputing/components/CEPEngineList.tsx: -------------------------------------------------------------------------------- 1 | import { Col, Row, Typography } from 'antd' 2 | import classNames from 'classnames' 3 | import { RedoOutlined } from '@ant-design/icons' 4 | 5 | import { t } from '@i18n' 6 | 7 | import { type ICEPEngineDetail, type CEPEngineItem } from '../type.js' 8 | 9 | 10 | interface IProps { 11 | on_select: (name: string) => void 12 | current: ICEPEngineDetail 13 | data: CEPEngineItem[] 14 | on_refresh: () => void 15 | } 16 | 17 | 18 | export function CEPEngineList (props: IProps) { 19 | const { current, on_select, data = [ ], on_refresh } = props 20 | 21 | return
22 |
23 |

{t('CEP 引擎')}

24 | 25 | 26 | {t('刷新')} 27 | 28 |
29 | 30 |
31 | {data.map(item =>
{ on_select(item.name) } } 35 | > 36 |
{item.name}
37 | 38 | 39 | 40 | {t('创建人:{{name}}', { name: item.user }) } 41 | 42 | 43 | 44 | 45 | {t('子引擎数:{{num}}', { num: item.numOfSubEngine }) } 46 | 47 | 48 | 49 |
)} 50 |
51 |
52 | } 53 | -------------------------------------------------------------------------------- /src/shell/icons/catalog.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/components/DraggableModal/index.tsx: -------------------------------------------------------------------------------- 1 | import './index.scss' 2 | 3 | import { Modal, type ModalProps } from 'antd' 4 | import { useRef, useState } from 'react' 5 | import Draggable, { type DraggableData, type DraggableEvent } from 'react-draggable' 6 | 7 | export function DraggableModal (props: ModalProps) { 8 | const { title, ...others } = props 9 | 10 | const [disabled, setDisabled] = useState(true) 11 | const [bounds, setBounds] = useState({ left: 0, top: 0, bottom: 0, right: 0 }) 12 | const draggleRef = useRef(null) 13 | 14 | function onStart (_event: DraggableEvent, uiData: DraggableData) { 15 | const { clientWidth, clientHeight } = window.document.documentElement 16 | const targetRect = draggleRef.current?.getBoundingClientRect() 17 | if (!targetRect) 18 | return 19 | setBounds({ 20 | left: -targetRect.left + uiData.x, 21 | right: clientWidth - (targetRect.right - uiData.x), 22 | top: -targetRect.top + uiData.y, 23 | bottom: clientHeight - (targetRect.bottom - uiData.y), 24 | }) 25 | } 26 | 27 | 28 | 29 | return { onStart(event, uiData) }} 37 | > 38 |
{modal}
39 |
40 | } 41 | title={
{ 44 | if (disabled) 45 | setDisabled(false) 46 | 47 | }} 48 | onMouseOut={() => { 49 | setDisabled(true) 50 | }} 51 | onFocus={() => { }} 52 | onBlur={() => { }} 53 | > 54 | {title} 55 |
} 56 | /> 57 | } 58 | -------------------------------------------------------------------------------- /src/components/DDBTable/index.tsx: -------------------------------------------------------------------------------- 1 | import './index.sass' 2 | 3 | import { QuestionCircleOutlined } from '@ant-design/icons' 4 | import { Space, Table, Tooltip, type TableProps } from 'antd' 5 | import { type ReactNode } from 'react' 6 | 7 | 8 | export interface DDBTableProps extends Omit, 'title'> { 9 | title?: ReactNode 10 | 11 | /** 页面模块只有一个表格,表格标题作为模块大标题的情况 */ 12 | big_title?: boolean 13 | 14 | /** 选传,对于表格的解释说明 */ 15 | help?: string 16 | 17 | /** 表格操作 */ 18 | buttons?: ReactNode 19 | 20 | filter_form?: ReactNode 21 | } 22 | 23 | 24 | export function DDBTable ({ 25 | title, help, buttons, filter_form, pagination, big_title, className, 26 | ...others 27 | }: DDBTableProps) { 28 | return <> 33 |
34 | {title ? 35 |
36 |
{title}
37 | { help && 38 | 39 | } 40 |
41 | : 42 | null 43 | } 44 | {buttons && {buttons}} 45 |
46 | 47 | {filter_form &&
{filter_form}
} 48 | 49 | : 50 | undefined 51 | } 52 | 53 | pagination={ 54 | pagination 55 | ? { 56 | ...pagination, 57 | size: 'small' 58 | } 59 | : false 60 | } 61 | 62 | {...others} 63 | /> 64 | } 65 | -------------------------------------------------------------------------------- /src/streaming/types.ts: -------------------------------------------------------------------------------- 1 | export type Context = 'page' | 'webview' | 'window' | 'embed' 2 | 3 | export interface ErrorType { 4 | appear: boolean 5 | msg: string 6 | } 7 | export interface ConfigType { 8 | url?: string 9 | table: string 10 | username: string 11 | password: string 12 | } 13 | export interface LineConfigType extends ConfigType { 14 | time_variable: string 15 | properties: Array 16 | duration: number 17 | height?: number 18 | width?: number 19 | } 20 | 21 | export interface TableConfigType extends ConfigType { 22 | properties: Array 23 | column?: number 24 | layout?: 'vertical' | 'horizontal' 25 | } 26 | 27 | export interface HeatMapConfigType extends ConfigType { 28 | properties: string[] 29 | max?: number 30 | min?: number 31 | sort?: 'ASC' | 'DESC' 32 | column?: number 33 | } 34 | 35 | export interface SortBarConfigType extends ConfigType { 36 | properties: string[] 37 | sort?: 'ASC' | 'DESC' 38 | animationDuration?: number 39 | height?: number 40 | width?: number 41 | } 42 | 43 | export interface KLineConfigType extends ConfigType { 44 | time_variable: string 45 | duration: number 46 | opening_price_variable: string 47 | closing_price_variable: string 48 | maximum_price_variable: string 49 | minimum_price_variable: string 50 | height?: number 51 | width?: number 52 | } 53 | 54 | export interface ScatterConfigType extends ConfigType { 55 | x_variable: string 56 | y_variable: string 57 | x_type?: 'TIMESTAMP' | 'NUMBER' | 'STRING' 58 | y_type?: 'NUMBER' | 'STRING' 59 | size_variable?: string 60 | color_variable?: string 61 | height?: number 62 | width?: number 63 | } 64 | 65 | // 定义折线图节点类型(必须要包含一个time属性) 66 | export type LineNodeType = { 67 | time: number 68 | [key: string]: number | string 69 | } 70 | // 定义K线图节点类型 71 | export type KLineNodeType = { 72 | time: number 73 | opening_price: number 74 | closing_price: number 75 | maximum_price: number 76 | minimum_price: number 77 | } 78 | -------------------------------------------------------------------------------- /src/settings/index.tsx: -------------------------------------------------------------------------------- 1 | import './index.sass' 2 | 3 | import { ApartmentOutlined, default as Icon } from '@ant-design/icons' 4 | 5 | import { t } from '@i18n' 6 | 7 | import { model } from '@model' 8 | 9 | 10 | import SvgFinance from '@/guide/icons/finance.icon.svg' 11 | import SvgIot from '@/guide/icons/iot.icon.svg' 12 | import SvgStreamingGraph from '@/streaming-graph/flow.icon.svg' 13 | 14 | import { Card } from './Card.tsx' 15 | 16 | 17 | export function Settings () { 18 | const { admin, license } = model.use(['admin', 'license']) 19 | 20 | return admin &&
21 |
{t('可选功能')}
22 | 23 | } 26 | label={t('金融库表向导')} 27 | description={t('此功能专为金融用户设计。用户通过此功能无需设计分区方案或编写复杂的 SQL 语句,只需输入库表名称、列名等基本信息,并通过列表选择参数,即可快速生成建库建表脚本,进而完成库表创建。')} 28 | /> 29 | 30 | } 33 | label={t('物联网库表向导')} 34 | description={t('此功能专为物联网用户设计。用户通过此功能无需设计分区方案或编写复杂的 SQL 语句,只需输入库表名称、列名等基本信息,并通过列表选择参数,即可快速生成建库建表脚本,进而完成库表创建。')} 35 | /> 36 | 37 | { license.product_key !== 'ORCA' && model.v3 && <> 38 | } 41 | label={t('流图监控')} 42 | description={t('查看流计算图相关信息,通过流图监控模块,用户可以直观监控通过 Orca 创建的流图任务及其运行状态,帮助用户实时掌握各流图的任务数量、执行次数和当前状态等。点击流图名称跳转至详情页面,查看具体任务结构与执行情况。')} 43 | /> 44 | 45 | } 48 | label={t('数据血缘')} 49 | description={t('查看数据库表、计算引擎的血缘关系,清晰地展示节点的上游拓扑结构,帮助用户直观地理解数据流动路径,从而快速定位问题。')} 50 | /> 51 | } 52 |
53 | } 54 | --------------------------------------------------------------------------------