mirror of
https://github.com/NetEase/tango.git
synced 2026-08-31 09:30:28 +08:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c240c1824b | |||
| d63517ecb9 | |||
| 1a17ee1e94 | |||
| e1bab66819 | |||
| 89bcd45728 | |||
| e86a6d7cd9 | |||
| 46b54459ce | |||
| 85898dee19 | |||
| e429610606 | |||
| b6814d6484 |
@@ -14,7 +14,7 @@ export default {
|
||||
],
|
||||
devServer: {
|
||||
host: 'local.netease.com',
|
||||
port: 7007,
|
||||
port: 8008,
|
||||
https: {
|
||||
key: path.resolve(__dirname, 'local.netease.com-key.pem'),
|
||||
cert: path.resolve(__dirname, 'local.netease.com.pem'),
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
],
|
||||
"scripts": {
|
||||
"build": "COMPRESS=none umi build",
|
||||
"start": "HTTPS=1 umi dev"
|
||||
"start": "umi dev"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^4.8.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Row, Switch } from 'antd';
|
||||
import { observer } from '@music163/tango-designer';
|
||||
import { observer } from '@music163/tango-context';
|
||||
|
||||
const OtherPanel = observer(({ autoRemove, setAutoRemove }: any) => {
|
||||
const onChange = (checked: boolean) => {
|
||||
|
||||
@@ -44,7 +44,7 @@ const tangoConfigJson = {
|
||||
resources: ['https://unpkg.com/moment@{{version}}/moment.js'],
|
||||
},
|
||||
'@music163/tango-boot': {
|
||||
version: '0.2.0',
|
||||
version: '0.2.1',
|
||||
library: 'TangoBoot',
|
||||
type: 'baseDependency',
|
||||
resources: ['https://unpkg.com/@music163/tango-boot@{{version}}/dist/boot.js'],
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
@import '~antd/dist/antd.less'; // 引入官方提供的 less 样式入口文件
|
||||
|
||||
@primary-color: #2f54eb;
|
||||
@border-radius-base: 2px;
|
||||
@border-radius-base: 2px;
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
DndQuery,
|
||||
} from '@music163/tango-designer';
|
||||
import { createEngine, Workspace } from '@music163/tango-core';
|
||||
import { Logo, ProjectDetail } from './share';
|
||||
import { Logo, ProjectDetail, bootHelperVariables } from './share';
|
||||
import { sampleFiles } from '../mock/project';
|
||||
import './index.less';
|
||||
import {
|
||||
@@ -50,7 +50,14 @@ export default function App() {
|
||||
const [menuLoading, setMenuLoading] = useState(true);
|
||||
const [menuData, setMenuData] = useState(false);
|
||||
return (
|
||||
<Designer engine={engine} sandboxQuery={sandboxQuery}>
|
||||
<Designer
|
||||
engine={engine}
|
||||
config={{
|
||||
customActionVariables: bootHelperVariables,
|
||||
customExpressionVariables: bootHelperVariables,
|
||||
}}
|
||||
sandboxQuery={sandboxQuery}
|
||||
>
|
||||
<DesignerPanel
|
||||
logo={<Logo />}
|
||||
description={<ProjectDetail />}
|
||||
@@ -58,6 +65,7 @@ export default function App() {
|
||||
<Box px="l">
|
||||
<Toolbar>
|
||||
<Toolbar.Item key="routeSwitch" placement="left" />
|
||||
<Toolbar.Item key="preview" placement="left" />
|
||||
<Toolbar.Item key="modeSwitch" placement="right" />
|
||||
<Toolbar.Item key="togglePanel" placement="right" />
|
||||
<Toolbar.Separator />
|
||||
|
||||
@@ -6,6 +6,36 @@ import { registerSetter } from '@music163/tango-designer';
|
||||
import type { ComponentPrototypeType } from '@music163/tango-helpers';
|
||||
import { FooSetter } from '../components';
|
||||
|
||||
export const bootHelperVariables = [
|
||||
{
|
||||
key: '$helpers',
|
||||
title: '工具函数',
|
||||
children: [
|
||||
{
|
||||
title: 'setStoreValue',
|
||||
key: '() => tango.setStoreValue("variableName", "variableValue")',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
title: 'getStoreValue',
|
||||
key: '() => tango.getStoreValue("variableName")',
|
||||
type: 'function',
|
||||
},
|
||||
{ title: 'openModal', key: '() => tango.openModal("")', type: 'function' },
|
||||
{ title: 'closeModal', key: '() => tango.closeModal("")', type: 'function' },
|
||||
{ title: 'navigateTo', key: '() => tango.navigateTo("/")', type: 'function' },
|
||||
{ title: 'showToast', key: '() => tango.showToast("hello")', type: 'function' },
|
||||
{ title: 'formatDate', key: '() => tango.formatDate("2022-12-12")', type: 'function' },
|
||||
{ title: 'formatNumber', key: '() => tango.formatDate(9999)', type: 'function' },
|
||||
{
|
||||
title: 'copyToClipboard',
|
||||
key: '() => tango.copyToClipboard("hello")',
|
||||
type: 'function',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// folder-name
|
||||
// 物料列表定义
|
||||
const bizToggleButtonPrototype: ComponentPrototypeType = {
|
||||
@@ -133,7 +163,7 @@ export function ProjectDetail() {
|
||||
return (
|
||||
<Box display="flex" alignItems="center" columnGap="l">
|
||||
<Box className="ProjectName" fontSize="18px" fontWeight="bold">
|
||||
lc-online-test
|
||||
community-test
|
||||
</Box>
|
||||
<Box className="BranchName" as="code" fontSize="14px">
|
||||
<BranchesOutlined /> feature/list
|
||||
|
||||
@@ -10,11 +10,20 @@
|
||||
|
||||
项目使用的 npm 包依赖信息,用于定义依赖的类型以及外置依赖资源。该配置项为一个 map,key 为对应的 npm 包名,value 为该依赖的配置信息。
|
||||
|
||||
- version 包的版本号,必选
|
||||
- library UMD 包中的全局变量名,必选
|
||||
- type 依赖的类型
|
||||
- baseDependency 基础依赖包
|
||||
- bizDependency 业务组件依赖包
|
||||
- dependency 其他依赖包
|
||||
- resources 外置依赖资源列表
|
||||
- designerResources 在设计器视图载入的外置资源列表,用来需要区别加载时使用
|
||||
- description 描述信息
|
||||
|
||||
依赖的类型 `type` 可以是 `baseDependency` 即项目的基础依赖,用于获取项目基础物料列表的信息;也可以是 `bizDependency` 即业务组件依赖,用于获取合并到物料面板业务组件菜单中的业务组件列表;也可以是 `depenency` 即项目的其他依赖,只用于提供 external 信息与版本信息等,不会和物料面板产生关系。
|
||||
|
||||
当配置了外置依赖资源 `resources` 与其对应的全局变量 `library` 时,当这些依赖被 import 时,相关资源不会被打包到 bundle 中,而是在运行时再去外部获取这些扩展依赖,其等效于之前的 `sandbox.externalResources` 与 `sandbox.externals` 配置项。此外,可以将 `resource` 中的版本号替换为 `{{version}}`,其会在平台上构建时替换为版本号。特殊的,可以定义 `designerResources` 用于适配平台的设计器视图。
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"react": {
|
||||
|
||||
@@ -6,7 +6,7 @@ Tango 是一个用于快速构建低代码平台的低代码设计器框架,
|
||||
|
||||
## 引擎架构概览
|
||||
|
||||
Tango 低代码引擎在实现上进行了分层解藕,使得上层的低代码平台与底层的低代码引擎可以独立开发和维护,快速集成部署。另一方面,Tango 低代码引擎定义了一套开放的物料生态体系,开发者可以自由的贡献扩展组件配置能力的属性设置器,以及扩展低代码物料的二方三方业务组件。
|
||||
Tango 低代码引擎在实现上进行了分层解耦,使得上层的低代码平台与底层的低代码引擎可以独立开发和维护,快速集成部署。另一方面,Tango 低代码引擎定义了一套开放的物料生态体系,开发者可以自由的贡献扩展组件配置能力的属性设置器,以及扩展低代码物料的二方三方业务组件。
|
||||
|
||||
如下图所示:
|
||||
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@
|
||||
"release": "yarn eslint && yarn build && yarn run ver && lerna publish from-git",
|
||||
"release:beta": "yarn eslint && yarn build && yarn run ver && lerna publish from-package --dist-tag beta",
|
||||
"up": "yarn upgrade-interactive --latest",
|
||||
"postinstall": "husky install"
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.19.3",
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.2.2](https://github.com/netease/tango/compare/@music163/tango-context@0.2.1...@music163/tango-context@0.2.2) (2023-10-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- refactor expSetter ([#41](https://github.com/netease/tango/issues/41)) ([d63517e](https://github.com/netease/tango/commit/d63517ecb936e4227e70c33e610664316625f4f4))
|
||||
|
||||
## [0.2.1](https://github.com/netease/tango/compare/@music163/tango-context@0.2.0...@music163/tango-context@0.2.1) (2023-09-22)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-context",
|
||||
"version": "0.2.1",
|
||||
"version": "0.2.2",
|
||||
"description": "react context for tango-apps",
|
||||
"keywords": [
|
||||
"react",
|
||||
|
||||
@@ -1,11 +1,25 @@
|
||||
import type { Engine } from '@music163/tango-core';
|
||||
import { createContext } from '@music163/tango-helpers';
|
||||
|
||||
interface CustomVariableData {
|
||||
key: string;
|
||||
title: string;
|
||||
children?: CustomVariableData[];
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface ITangoEngineContext {
|
||||
/**
|
||||
* 低代码引擎
|
||||
*/
|
||||
engine: Engine;
|
||||
/**
|
||||
* 自定义配置数据
|
||||
*/
|
||||
config: {
|
||||
customActionVariables: CustomVariableData[];
|
||||
customExpressionVariables: CustomVariableData[];
|
||||
};
|
||||
}
|
||||
|
||||
const [TangoEngineProvider, useTangoEngine] = createContext<ITangoEngineContext>({
|
||||
@@ -22,31 +36,8 @@ export const useDesigner = () => {
|
||||
return useTangoEngine()?.engine.designer;
|
||||
};
|
||||
|
||||
const builtinHelpers = [
|
||||
{
|
||||
title: 'setStoreValue',
|
||||
key: '() => tango.setStoreValue("variableName", "variableValue")',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
title: 'getStoreValue',
|
||||
key: '() => tango.getStoreValue("variableName")',
|
||||
type: 'function',
|
||||
},
|
||||
{ title: 'openModal', key: '() => tango.openModal("")', type: 'function' },
|
||||
{ title: 'closeModal', key: '() => tango.closeModal("")', type: 'function' },
|
||||
{ title: 'navigateTo', key: '() => tango.navigateTo("/")', type: 'function' },
|
||||
{ title: 'showToast', key: '() => tango.showToast("hello")', type: 'function' },
|
||||
{ title: 'formatDate', key: '() => tango.formatDate("2022-12-12")', type: 'function' },
|
||||
{ title: 'formatNumber', key: '() => tango.formatDate(9999)', type: 'function' },
|
||||
{
|
||||
title: 'copyToClipboard',
|
||||
key: '() => tango.copyToClipboard("hello")',
|
||||
type: 'function',
|
||||
},
|
||||
];
|
||||
|
||||
export const useWorkspaceData = () => {
|
||||
const ctx = useTangoEngine();
|
||||
const workspace = useWorkspace();
|
||||
const modelVariables: any[] = []; // 绑定变量列表
|
||||
const storeActionVariables: any[] = []; // 模型中的所有 actions
|
||||
@@ -111,20 +102,29 @@ export const useWorkspaceData = () => {
|
||||
value: item.path,
|
||||
}));
|
||||
|
||||
let actionVariables: CustomVariableData[] = [
|
||||
buildVariableOptions('数据模型', '$stores', storeActionVariables),
|
||||
buildVariableOptions('服务函数', '$services', serviceVariables),
|
||||
];
|
||||
|
||||
if (ctx.config?.customActionVariables) {
|
||||
actionVariables = actionVariables.concat(ctx.config?.customActionVariables);
|
||||
}
|
||||
|
||||
let expressionVariables: CustomVariableData[] = [
|
||||
buildVariableOptions('数据模型', '$stores', storeVariables),
|
||||
buildVariableOptions('服务函数', '$services', serviceVariables),
|
||||
];
|
||||
if (ctx.config?.customExpressionVariables) {
|
||||
expressionVariables = expressionVariables.concat(ctx.config?.customExpressionVariables);
|
||||
}
|
||||
|
||||
return {
|
||||
modelVariables: [buildVariableOptions('数据模型', 'stores', modelVariables)],
|
||||
actionVariables: [
|
||||
buildVariableOptions('数据模型', 'stores', storeActionVariables),
|
||||
buildVariableOptions('服务函数', 'services', serviceVariables),
|
||||
buildVariableOptions('工具函数', 'helpers', builtinHelpers),
|
||||
],
|
||||
actionVariables,
|
||||
storeVariables,
|
||||
serviceVariables,
|
||||
expressionVariables: [
|
||||
buildVariableOptions('数据模型', 'stores', storeVariables),
|
||||
buildVariableOptions('服务函数', 'services', serviceVariables),
|
||||
buildVariableOptions('工具函数', 'helpers', builtinHelpers),
|
||||
],
|
||||
expressionVariables,
|
||||
routeOptions,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3,6 +3,23 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.5.0](https://github.com/netease/tango/compare/@music163/tango-designer@0.4.1...@music163/tango-designer@0.5.0) (2023-10-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- refactor expSetter ([#41](https://github.com/netease/tango/issues/41)) ([d63517e](https://github.com/netease/tango/commit/d63517ecb936e4227e70c33e610664316625f4f4))
|
||||
|
||||
### Features
|
||||
|
||||
- add umd resources config when modifying dependency ([#36](https://github.com/netease/tango/issues/36)) ([e1bab66](https://github.com/netease/tango/commit/e1bab66819cf9a0bd7b87b72b2962498dfd51b2c))
|
||||
|
||||
## [0.4.1](https://github.com/netease/tango/compare/@music163/tango-designer@0.4.0...@music163/tango-designer@0.4.1) (2023-10-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- always switch to design mode after preview is on ([85898de](https://github.com/netease/tango/commit/85898dee19b6031016679e33a4ce9edbd1089713))
|
||||
- update export entry ([e429610](https://github.com/netease/tango/commit/e4296106066ad290ac838135fe6a57bc50ad2680))
|
||||
|
||||
# [0.4.0](https://github.com/netease/tango/compare/@music163/tango-designer@0.3.0...@music163/tango-designer@0.4.0) (2023-09-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-designer",
|
||||
"version": "0.4.0",
|
||||
"version": "0.5.0",
|
||||
"description": "lowcode designer",
|
||||
"keywords": [
|
||||
"react"
|
||||
@@ -33,12 +33,12 @@
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^4.8.0",
|
||||
"@music163/request": "^0.1.0",
|
||||
"@music163/tango-context": "^0.2.1",
|
||||
"@music163/tango-context": "^0.2.2",
|
||||
"@music163/tango-core": "^0.2.1",
|
||||
"@music163/tango-helpers": "^0.1.3",
|
||||
"@music163/tango-sandbox": "^0.1.6",
|
||||
"@music163/tango-setting-form": "^0.2.3",
|
||||
"@music163/tango-ui": "^0.1.4",
|
||||
"@music163/tango-setting-form": "^0.2.5",
|
||||
"@music163/tango-ui": "^0.1.6",
|
||||
"antd": "^4.24.2",
|
||||
"cash-dom": "^8.1.2",
|
||||
"classnames": "^2.3.2",
|
||||
|
||||
@@ -11,10 +11,15 @@ import {
|
||||
Empty,
|
||||
Popconfirm,
|
||||
Tooltip,
|
||||
Alert,
|
||||
} from 'antd';
|
||||
import { Box, Text, css, Link } from 'coral-system';
|
||||
import { PlusOutlined, FunctionOutlined, DeleteOutlined } from '@ant-design/icons';
|
||||
import { Box, Text, css } from 'coral-system';
|
||||
import {
|
||||
PlusOutlined,
|
||||
FunctionOutlined,
|
||||
DeleteOutlined,
|
||||
CopyOutlined,
|
||||
EyeOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import {
|
||||
filterTreeData,
|
||||
isFunction,
|
||||
@@ -96,10 +101,6 @@ export interface EditableVariableTreeProps {
|
||||
* 选择列表项时的回调
|
||||
*/
|
||||
onSelect?: (data: IVariableTreeNode) => void;
|
||||
/**
|
||||
* 点击使用此变量按钮时的回调
|
||||
*/
|
||||
onUse?: (data: IVariableTreeNode) => void;
|
||||
/**
|
||||
* 保存结点定义的回调
|
||||
*/
|
||||
@@ -124,10 +125,6 @@ export interface EditableVariableTreeProps {
|
||||
* 搜索框的后置结点
|
||||
*/
|
||||
searchAddonAfter?: React.ReactNode;
|
||||
/**
|
||||
* 是否展示预览值的提示信息
|
||||
*/
|
||||
showPreviewAlert?: boolean;
|
||||
}
|
||||
|
||||
const previewOptions = [
|
||||
@@ -145,11 +142,9 @@ export function EditableVariableTree({
|
||||
onDeleteStore = noop,
|
||||
getPreviewValue = noop,
|
||||
onSelect = noop,
|
||||
onUse = noop,
|
||||
onSave = noop,
|
||||
modes = ['add', 'preview', 'define'],
|
||||
defaultMode = 'preview',
|
||||
showPreviewAlert = true,
|
||||
}: EditableVariableTreeProps) {
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const [node, setNode] = useState<IVariableTreeNode>();
|
||||
@@ -184,11 +179,16 @@ export function EditableVariableTree({
|
||||
</Box>
|
||||
<VariableTree
|
||||
dataSource={treeData}
|
||||
showViewIcon
|
||||
onSelect={(item) => {
|
||||
setNode(item);
|
||||
mode === 'add' && setMode(defaultMode);
|
||||
onSelect(item);
|
||||
}}
|
||||
onView={(item) => {
|
||||
setNode(item);
|
||||
mode === 'add' && setMode(defaultMode);
|
||||
}}
|
||||
onAdd={(item) => {
|
||||
setNode(item);
|
||||
setMode('add');
|
||||
@@ -225,33 +225,15 @@ export function EditableVariableTree({
|
||||
borderRadius="m"
|
||||
position="sticky"
|
||||
top="0"
|
||||
bodyProps={{ px: 'l' }}
|
||||
bodyProps={{ px: 'm' }}
|
||||
>
|
||||
{mode === 'preview' && (
|
||||
<>
|
||||
<Box mb="m" display={showPreviewAlert ? 'block' : 'none'}>
|
||||
<Alert
|
||||
message={
|
||||
<Space>
|
||||
<Text>
|
||||
<Text as="strong">变量路径:</Text>tango.{node.key}
|
||||
</Text>
|
||||
<Link onClick={() => onUse(node)}>使用此变量</Link>
|
||||
<CopyClipboard text={`tango.${node.key.replaceAll('.', '?.')}`}>
|
||||
{(copied) => <Link>{copied ? '已复制' : '复制变量路径'}</Link>}
|
||||
</CopyClipboard>
|
||||
</Space>
|
||||
}
|
||||
type="info"
|
||||
/>
|
||||
</Box>
|
||||
<ValuePreview
|
||||
value={getPreviewValue(node)}
|
||||
onSelect={(valuePath) => {
|
||||
return ['tango', node.key.replaceAll('.', '?.'), valuePath].join('.');
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
<ValuePreview
|
||||
value={getPreviewValue(node)}
|
||||
onSelect={(valuePath) => {
|
||||
return ['tango', node.key.replaceAll('.', '?.'), valuePath].join('.');
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{mode === 'define' && (
|
||||
<NodeDefineForm
|
||||
@@ -308,7 +290,7 @@ export function EditableVariableTreeModal({
|
||||
{React.cloneElement(trigger, { onClick: on })}
|
||||
<Modal
|
||||
title={title}
|
||||
visible={visible}
|
||||
open={visible}
|
||||
onCancel={off}
|
||||
okButtonProps={{
|
||||
disabled: !node,
|
||||
@@ -345,6 +327,11 @@ const varTreeStyle = css`
|
||||
.ant-tree-indent-unit {
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
.anticon-function {
|
||||
margin-left: 4px;
|
||||
color: var(--tango-colors-text2);
|
||||
}
|
||||
`;
|
||||
|
||||
interface VariableTreeProps {
|
||||
@@ -352,7 +339,10 @@ interface VariableTreeProps {
|
||||
onSelect?: (data: IVariableTreeNode) => void;
|
||||
onAdd?: (data: IVariableTreeNode) => void;
|
||||
onRemove?: (data: IVariableTreeNode) => void;
|
||||
deletable?: boolean;
|
||||
onCopy?: (data: IVariableTreeNode) => void;
|
||||
onView?: (data: IVariableTreeNode) => void;
|
||||
showDeleteIcon?: boolean;
|
||||
showViewIcon?: boolean;
|
||||
}
|
||||
|
||||
export function VariableTree({
|
||||
@@ -360,11 +350,15 @@ export function VariableTree({
|
||||
onSelect = noop,
|
||||
onAdd = noop,
|
||||
onRemove = noop,
|
||||
deletable = false,
|
||||
onCopy = noop,
|
||||
onView = noop,
|
||||
showDeleteIcon = false,
|
||||
showViewIcon = false,
|
||||
}: VariableTreeProps) {
|
||||
return (
|
||||
<Box className="VariableTree" css={varTreeStyle}>
|
||||
<Tree.DirectoryTree
|
||||
<Tree
|
||||
blockNode
|
||||
showIcon={false}
|
||||
defaultExpandAll
|
||||
treeData={dataSource}
|
||||
@@ -372,28 +366,18 @@ export function VariableTree({
|
||||
onSelect(detail.node);
|
||||
}}
|
||||
titleRender={(node) => {
|
||||
// 只有叶子结点支持删除
|
||||
const showRemoveIcon = (!node.children && node.showDeleteIcon) ?? deletable;
|
||||
const showAddIcon = node.showAddChildIcon ?? false;
|
||||
if (showRemoveIcon || showAddIcon) {
|
||||
const isLeaf = !node.children;
|
||||
|
||||
if (isLeaf) {
|
||||
const isDeletable = node.showDeleteIcon ?? showDeleteIcon;
|
||||
return (
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center" pr="l">
|
||||
<Text>{node.title}</Text>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Text>
|
||||
{node.title}
|
||||
{node.type === 'function' && <FunctionOutlined />}
|
||||
</Text>
|
||||
<Box>
|
||||
{showAddIcon && (
|
||||
<Tooltip title={`向 ${node.title} 中添加变量`}>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onAdd(node);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{showRemoveIcon && (
|
||||
{isDeletable && (
|
||||
<Popconfirm
|
||||
title="确认删除吗?该操作会导致引用此模型的代码报错,请谨慎操作!"
|
||||
onConfirm={() => onRemove(node)}
|
||||
@@ -408,14 +392,61 @@ export function VariableTree({
|
||||
/>
|
||||
</Popconfirm>
|
||||
)}
|
||||
<CopyClipboard text={`tango.${node.key.replaceAll('.', '?.')}`}>
|
||||
{({ copied, onClick }) => {
|
||||
const label = copied ? '已复制' : '复制变量路径';
|
||||
return (
|
||||
<Tooltip title={label}>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<CopyOutlined />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onClick();
|
||||
onCopy(node);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
}}
|
||||
</CopyClipboard>
|
||||
{showViewIcon && (
|
||||
<Tooltip title="查看变量详情">
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<EyeOutlined />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onView(node);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
<Text mr="m">{node.title}</Text>
|
||||
{node.type === 'function' && <FunctionOutlined />}
|
||||
<Box>
|
||||
{node.showAddChildIcon && (
|
||||
<Tooltip title={`向 ${node.title} 中添加变量`}>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onAdd(node);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}}
|
||||
|
||||
@@ -27,6 +27,7 @@ export interface DesignerProps extends IDesignerContext, ITangoEngineContext {
|
||||
export function Designer(props: DesignerProps) {
|
||||
const {
|
||||
engine,
|
||||
config,
|
||||
theme: themeProp,
|
||||
sandboxQuery,
|
||||
remoteServices = {},
|
||||
@@ -42,7 +43,7 @@ export function Designer(props: DesignerProps) {
|
||||
return (
|
||||
<SystemProvider theme={theme} prefix="--tango">
|
||||
<ConfigProvider locale={zhCN}>
|
||||
<TangoEngineProvider value={{ engine }}>
|
||||
<TangoEngineProvider value={{ engine, config }}>
|
||||
<DesignerProvider value={{ sandboxQuery, remoteServices }}>{children}</DesignerProvider>
|
||||
</TangoEngineProvider>
|
||||
</ConfigProvider>
|
||||
|
||||
@@ -11,6 +11,7 @@ export * from './sandbox';
|
||||
export * from './sidebar';
|
||||
export * from './toolbar';
|
||||
export * from './selection-menu';
|
||||
export * from './widgets';
|
||||
|
||||
export { register as registerSetter } from '@music163/tango-setting-form';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { Box, css } from 'coral-system';
|
||||
import { Alert, Modal, Tabs } from 'antd';
|
||||
import { Modal } from 'antd';
|
||||
import { value2code, isValidExpressionCode } from '@music163/tango-core';
|
||||
import {
|
||||
isVariableString,
|
||||
@@ -9,12 +9,12 @@ import {
|
||||
useBoolean,
|
||||
getValue,
|
||||
} from '@music163/tango-helpers';
|
||||
import { CloseCircleFilled, ExpandAltOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { IconButton, Panel, InputCode, ChatInput } from '@music163/tango-ui';
|
||||
import { CloseCircleFilled, ExpandAltOutlined } from '@ant-design/icons';
|
||||
import { IconButton, Panel, InputCode } from '@music163/tango-ui';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
import { useWorkspace, useWorkspaceData } from '@music163/tango-context';
|
||||
import { EditableVariableTree, IVariableTreeNode } from '../components';
|
||||
import { useRemoteServices, useSandboxQuery } from '../context';
|
||||
import { useSandboxQuery } from '../context';
|
||||
|
||||
export const expressionValueValidate = (value: string) => {
|
||||
if (isVariableString(value)) {
|
||||
@@ -190,38 +190,24 @@ export function ExpressionModal({
|
||||
[workspace],
|
||||
);
|
||||
const sandbox = useSandboxQuery();
|
||||
const remoteServices = useRemoteServices();
|
||||
const { expressionVariables } = useWorkspaceData();
|
||||
const evaluateContext = sandbox.window;
|
||||
const gptService = remoteServices?.GptService;
|
||||
|
||||
const handleExpInputChange = (val: string) => {
|
||||
setExp(val?.trim());
|
||||
};
|
||||
return (
|
||||
<Modal
|
||||
title={title}
|
||||
closable={false}
|
||||
destroyOnClose
|
||||
width="70%"
|
||||
open={visible}
|
||||
onCancel={onCancel}
|
||||
onOk={() => {
|
||||
onOk(exp);
|
||||
}}
|
||||
destroyOnClose
|
||||
>
|
||||
<Panel
|
||||
title="绑定的表达式"
|
||||
subTitle={subTitle}
|
||||
extra={
|
||||
<IconButton
|
||||
icon={<QuestionCircleOutlined />}
|
||||
tooltip="你可以直接编辑下面的表达式,表达式需要使用 { } 进行包裹,点击此图标可以查看详细文档。"
|
||||
href="https://music-doc.st.netease.com/st/tango-docs/docs/guide/javascript/overview"
|
||||
/>
|
||||
}
|
||||
shape="solid"
|
||||
borderRadius="m"
|
||||
>
|
||||
<Panel title={`将 ${title} 设置为引用变量或自定义表达式`} subTitle={subTitle} shape="solid">
|
||||
<InputCode
|
||||
shape="inset"
|
||||
minHeight="56px"
|
||||
@@ -233,91 +219,49 @@ export function ExpressionModal({
|
||||
autoCompleteOptions={autoCompleteOptions}
|
||||
/>
|
||||
</Panel>
|
||||
<Box mt="m" maxHeight={400} overflow="auto">
|
||||
<Tabs type="card" size="small">
|
||||
<Tabs.TabPane key="list" tab="从变量列表中选择">
|
||||
<Box my="m">
|
||||
<Alert
|
||||
type="info"
|
||||
message="你可以直接从下方的列表中选择已定义的变量"
|
||||
banner
|
||||
closable
|
||||
/>
|
||||
</Box>
|
||||
<EditableVariableTree
|
||||
// height={400}
|
||||
dataSource={dataSource || expressionVariables}
|
||||
onUse={(node) => {
|
||||
let str;
|
||||
if (/^(stores|services)\./.test(node.key)) {
|
||||
str = `{tango.${node.key.replaceAll('.', '?.')}}`;
|
||||
} else {
|
||||
str = `{${node.key}}`;
|
||||
}
|
||||
setExp(str);
|
||||
}}
|
||||
onAddVariable={(storeName, data) => {
|
||||
onAction('addStoreState', [storeName, data.name, data.initialValue]);
|
||||
}}
|
||||
onDeleteVariable={(storeName, stateName) => {
|
||||
onAction('removeStoreState', [storeName, stateName]);
|
||||
}}
|
||||
onDeleteStore={(storeName) => {
|
||||
onAction('removeStoreModule', [storeName]);
|
||||
}}
|
||||
onSave={(code, node) => {
|
||||
onAction('updateModuleCodeByVariablePath', [node.key, code]);
|
||||
}}
|
||||
getPreviewValue={(node) => {
|
||||
if (!node || !node.key) {
|
||||
return;
|
||||
}
|
||||
<Panel title="从变量列表中选中" shape="solid" borderTop="0" maxHeight={400} overflow="auto">
|
||||
<EditableVariableTree
|
||||
dataSource={dataSource || expressionVariables}
|
||||
onSelect={(node) => {
|
||||
if (!node.key) {
|
||||
return;
|
||||
}
|
||||
if (node.key.split('.').length < 2) {
|
||||
return;
|
||||
}
|
||||
let str;
|
||||
if (/^(stores|services)\./.test(node.key)) {
|
||||
str = `{tango.${node.key.replaceAll('.', '?.')}}`;
|
||||
} else {
|
||||
str = `{${node.key}}`;
|
||||
}
|
||||
setExp(str);
|
||||
}}
|
||||
onAddVariable={(storeName, data) => {
|
||||
onAction('addStoreState', [storeName, data.name, data.initialValue]);
|
||||
}}
|
||||
onDeleteVariable={(storeName, stateName) => {
|
||||
onAction('removeStoreState', [storeName, stateName]);
|
||||
}}
|
||||
onDeleteStore={(storeName) => {
|
||||
onAction('removeStoreModule', [storeName]);
|
||||
}}
|
||||
onSave={(code, node) => {
|
||||
onAction('updateModuleCodeByVariablePath', [node.key, code]);
|
||||
}}
|
||||
getPreviewValue={(node) => {
|
||||
if (!node || !node.key) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.type === 'function') {
|
||||
return node.raw;
|
||||
}
|
||||
if (node.type === 'function') {
|
||||
return node.raw;
|
||||
}
|
||||
|
||||
return getValue(evaluateContext['tango'], node.key);
|
||||
}}
|
||||
/>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane key="ai" tab="使用 AI 助手生成" disabled={!gptService?.textCompletion}>
|
||||
<ChatInput
|
||||
placeholder="例如:实现一个函数,从给定的数组中过滤出name为bob的数据"
|
||||
onGenerate={(text) =>
|
||||
gptService
|
||||
.textCompletion({
|
||||
prompt: getExpressionCode(text),
|
||||
model: 'gpt-3.5-turbo',
|
||||
temperature: 0.5,
|
||||
topP: 1,
|
||||
presencePenalty: 0,
|
||||
frequencyPenalty: 0,
|
||||
mode: 'chat',
|
||||
})
|
||||
.then((res: any) => {
|
||||
return res?.detail?.choices?.[0]?.text;
|
||||
})
|
||||
}
|
||||
onApply={(preview) => {
|
||||
setExp(`{${preview}}`);
|
||||
}}
|
||||
showCloseIcon={false}
|
||||
/>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</Box>
|
||||
return getValue(evaluateContext['tango'], node.key);
|
||||
}}
|
||||
/>
|
||||
</Panel>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
function getExpressionCode(userInput: string) {
|
||||
return `你是一个代码生成助手。你使用的编程语言是 JavaScript。不要解释代码,只返回代码块本身,代码必须包裹在 {} 内部。
|
||||
标准的输出格式如下:
|
||||
1. {this.a}
|
||||
2. {() => {}}
|
||||
3. {tango.stores.user.name}
|
||||
|
||||
${userInput}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -123,7 +123,6 @@ export function ModelSetter({ value, onChange }: FormItemComponentProps) {
|
||||
onBlur={onInputBlur}
|
||||
suffix={
|
||||
<EditableVariableTreeModal
|
||||
showPreviewAlert={false}
|
||||
title="同步到的变量"
|
||||
trigger={
|
||||
<Tooltip title="从模型列表选择" placement="topRight">
|
||||
|
||||
@@ -148,7 +148,7 @@ const DataSourceView = observer(({ onAdd, onUpdate, onDelete }: DataServiceViewP
|
||||
</Box>
|
||||
<VariableTree
|
||||
dataSource={dataSource}
|
||||
deletable
|
||||
showDeleteIcon
|
||||
onSelect={(item) => {
|
||||
setServiceData({ key: item.key, ...workspace.getServiceFunction(item.key) });
|
||||
}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { observer, useWorkspace } from '@music163/tango-context';
|
||||
import {
|
||||
Button,
|
||||
@@ -9,15 +9,17 @@ import {
|
||||
Tooltip,
|
||||
Popconfirm,
|
||||
Radio,
|
||||
Select,
|
||||
message,
|
||||
Tag,
|
||||
Checkbox,
|
||||
ModalProps,
|
||||
} from 'antd';
|
||||
import { FormListProps } from 'antd/lib/form';
|
||||
import { Box, Text } from 'coral-system';
|
||||
import semverLt from 'semver/functions/lt';
|
||||
import semverValid from 'semver/functions/valid';
|
||||
import { ConfigGroup, ConfigItem } from '@music163/tango-ui';
|
||||
import { PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { MinusCircleOutlined, PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { useBoolean } from '@music163/tango-helpers';
|
||||
import { isUndefined } from 'lodash-es';
|
||||
import { Workspace } from '@music163/tango-core';
|
||||
@@ -177,18 +179,9 @@ function RenderItem({
|
||||
onUpgrade,
|
||||
showTags = true,
|
||||
}: RenderItemProps) {
|
||||
const [version, setVersion] = useState<string>();
|
||||
const [versionList, setVersionList] = useState<Array<{ label: string; value: string }>>([]);
|
||||
const [open, { on, off }] = useBoolean(false);
|
||||
|
||||
const workspace = useWorkspace() as Workspace;
|
||||
// const remoteServices = useRemoteServices();
|
||||
|
||||
// useEffect(() => {
|
||||
// remoteServices?.DependencyService?.listPackageVersions({ packageName: record.name }).then(
|
||||
// (data: any) => setVersionList((data || []).map((v: string) => ({ label: v, value: v }))),
|
||||
// );
|
||||
// }, [remoteServices, record]);
|
||||
|
||||
const basePackage = useMemo(() => {
|
||||
if (type !== DependencyItemType.基础包 || !templateBaseDependencies) return undefined;
|
||||
@@ -198,9 +191,29 @@ function RenderItem({
|
||||
/**
|
||||
* 处理依赖升级的逻辑
|
||||
*/
|
||||
function onDepUpgrade() {
|
||||
function onDepUpgrade(values: Record<string, any>) {
|
||||
// 根据是否有 packages 字段,判断是否已升级为新的规范
|
||||
const hasUpgraded = !!workspace.tangoConfigJson?.getValue('packages');
|
||||
const { version, umd, library, resources, designerResources } = values || {};
|
||||
|
||||
let packageConfig = record?.package;
|
||||
if (umd === false && packageConfig) {
|
||||
packageConfig = {
|
||||
...packageConfig,
|
||||
version,
|
||||
library: undefined,
|
||||
resources: undefined,
|
||||
designerResources: undefined,
|
||||
};
|
||||
} else {
|
||||
packageConfig = {
|
||||
...packageConfig,
|
||||
version,
|
||||
library,
|
||||
resources,
|
||||
designerResources,
|
||||
};
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case DependencyItemType.业务组件:
|
||||
@@ -210,7 +223,7 @@ function RenderItem({
|
||||
version,
|
||||
hasUpgraded
|
||||
? {
|
||||
package: record?.package,
|
||||
package: packageConfig,
|
||||
}
|
||||
: {},
|
||||
);
|
||||
@@ -225,7 +238,7 @@ function RenderItem({
|
||||
basePackage?.version || 'latest',
|
||||
hasUpgraded
|
||||
? {
|
||||
package: record?.package,
|
||||
package: packageConfig,
|
||||
}
|
||||
: {},
|
||||
);
|
||||
@@ -295,7 +308,6 @@ function RenderItem({
|
||||
}
|
||||
|
||||
function onCloseModal() {
|
||||
setVersion(undefined);
|
||||
off();
|
||||
}
|
||||
|
||||
@@ -317,10 +329,8 @@ function RenderItem({
|
||||
const { resources } = packageInfo || {};
|
||||
const dependencies = workspace.packageJson?.getValue('dependencies');
|
||||
const hasUmd = !!resources?.length;
|
||||
const unsafeUmd =
|
||||
hasUmd && resources.some((e: string) => /\/\/unpkg\.f[nt]\.netease\.com\//.test(e));
|
||||
const packageJsonOnly = dependencies?.[name] && !packageInfo;
|
||||
const tangoConfigJsonOnly = !dependencies?.[name] && packageInfo;
|
||||
const packageJsonIncluded = !!dependencies?.[name];
|
||||
const tangoConfigJsonIncluded = !!packageInfo;
|
||||
|
||||
return [
|
||||
hasUmd && (
|
||||
@@ -330,21 +340,14 @@ function RenderItem({
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
),
|
||||
unsafeUmd && (
|
||||
<Tooltip title="该依赖的 UMD 资源来自自建 unpkg 服务,稳定性不可靠,建议修改 tango.config.json 更换为正式 cdn 资源地址">
|
||||
<Tag key="unsafeUmd" color="orange">
|
||||
非 CDN
|
||||
</Tag>
|
||||
packageJsonIncluded && (
|
||||
<Tooltip title="此依赖在 package.json 中存在">
|
||||
<Tag key="packageJson">package</Tag>
|
||||
</Tooltip>
|
||||
),
|
||||
packageJsonOnly && (
|
||||
<Tooltip title="此依赖只在 package.json 中存在">
|
||||
<Tag key="packageJsonOnly">package.json</Tag>
|
||||
</Tooltip>
|
||||
),
|
||||
tangoConfigJsonOnly && (
|
||||
<Tooltip title="此依赖只在 tango.config.json 中存在">
|
||||
<Tag key="packageJsonOnly">tango.config.json</Tag>
|
||||
tangoConfigJsonIncluded && (
|
||||
<Tooltip title="此依赖在 tango.config.json 中存在">
|
||||
<Tag key="tangoConfigJson">tango.config</Tag>
|
||||
</Tooltip>
|
||||
),
|
||||
].filter((e) => e);
|
||||
@@ -356,7 +359,7 @@ function RenderItem({
|
||||
type !== DependencyItemType.基础包
|
||||
? [
|
||||
<a key="upgrade" onClick={on}>
|
||||
升级
|
||||
修改
|
||||
</a>,
|
||||
<Popconfirm onConfirm={onDepDelete} title={`确认删除依赖 ${record.name}?`}>
|
||||
<a key="delete">删除</a>
|
||||
@@ -366,7 +369,7 @@ function RenderItem({
|
||||
? [
|
||||
<Popconfirm
|
||||
title={`确认升级 ${record.name} 到 ${basePackage.version} 吗?`}
|
||||
onConfirm={onDepUpgrade}
|
||||
onConfirm={() => onDepUpgrade({ version: basePackage.version })}
|
||||
>
|
||||
<a key="upgrade">升级</a>
|
||||
</Popconfirm>,
|
||||
@@ -394,28 +397,12 @@ function RenderItem({
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Modal
|
||||
<DependencyConfigModal
|
||||
open={open}
|
||||
title={`升级依赖:${record.name}`}
|
||||
record={record}
|
||||
onCancel={onCloseModal}
|
||||
onOk={onDepUpgrade}
|
||||
>
|
||||
<Text mr="6px">
|
||||
选择依赖版本号
|
||||
<Tooltip title="只提供正式版本列表,Beta版本依赖需要手动升级">
|
||||
<QuestionCircleOutlined />
|
||||
</Tooltip>
|
||||
</Text>
|
||||
<Select
|
||||
style={{ width: 160 }}
|
||||
showSearch
|
||||
defaultValue={record.version}
|
||||
options={versionList || []}
|
||||
onChange={(value: string) => setVersion(value)}
|
||||
placeholder="请选择要升级的版本号"
|
||||
optionFilterProp="value"
|
||||
/>
|
||||
</Modal>
|
||||
onOk={(event, { values }) => onDepUpgrade(values)}
|
||||
/>
|
||||
</List.Item>
|
||||
);
|
||||
}
|
||||
@@ -447,111 +434,114 @@ interface DependencyModalProps {
|
||||
templateBaseDependencies: Record<string, any>;
|
||||
onBizDependencyAdd: Function;
|
||||
onBaseDependencyAdd: Function;
|
||||
record?: Record<string, any>;
|
||||
}
|
||||
|
||||
function AddDependencyModal({
|
||||
templateBaseDependencies,
|
||||
onBizDependencyAdd,
|
||||
onBaseDependencyAdd,
|
||||
}: DependencyModalProps) {
|
||||
record,
|
||||
...rest
|
||||
}: DependencyModalProps & ModalProps) {
|
||||
// 依赖类型
|
||||
const [type, setType] = useState<DependencyItemType>(DependencyItemType.其他依赖);
|
||||
const [open, { on, off }] = useBoolean();
|
||||
|
||||
const [form] = Form.useForm();
|
||||
const workspace = useWorkspace();
|
||||
const sandbox = useSandboxQuery();
|
||||
const hasUpgraded = !!workspace.tangoConfigJson?.getValue('packages');
|
||||
|
||||
const onFinishCallback = (name?: string) => {
|
||||
name && message.success(`${name} 添加成功`);
|
||||
// FIXME: 修复沙箱添加组件后 HMR 失效的 bug,添加完刷新
|
||||
sandbox.reload();
|
||||
off();
|
||||
form.resetFields();
|
||||
};
|
||||
|
||||
const onOk = () => {
|
||||
form.validateFields().then((values) => {
|
||||
const { name, version } = values;
|
||||
// 根据是否有 packages 字段,判断是否已升级为新的规范
|
||||
const hasUpgraded = !!workspace.tangoConfigJson?.getValue('packages');
|
||||
const onOk: DependencyConfigModalProps['onOk'] = (event, { values }) => {
|
||||
const { type, name, version, umd, library, resources, designerResources } = values;
|
||||
|
||||
switch (type) {
|
||||
case DependencyItemType.其他依赖: {
|
||||
workspace.updateDependency(name, version);
|
||||
break;
|
||||
}
|
||||
// case DependencyItemType.业务组件: {
|
||||
// const targetBizInfo = (bizList as any)?.find(
|
||||
// (biz: { packageName: string }) => biz.packageName === name,
|
||||
// );
|
||||
// onBizDependencyAdd &&
|
||||
// onBizDependencyAdd({
|
||||
// ...values,
|
||||
// detail: targetBizInfo,
|
||||
// });
|
||||
// break;
|
||||
// }
|
||||
case DependencyItemType.基础包: {
|
||||
const basePackage = templateBaseDependencies[name];
|
||||
// 添加 package.json
|
||||
workspace.updateDependency(
|
||||
name,
|
||||
basePackage.version || 'latest',
|
||||
hasUpgraded
|
||||
? {
|
||||
package: basePackage.package,
|
||||
}
|
||||
: {},
|
||||
);
|
||||
|
||||
// 配置已经合并为新版,不需要走旧的逻辑
|
||||
if (!hasUpgraded) {
|
||||
// 更新 tango.config.json baseDependencies
|
||||
workspace.tangoConfigJson.setValue('baseDependencies', (targetValue) => {
|
||||
const bases = targetValue || [];
|
||||
(bases as string[]).push(name);
|
||||
return bases;
|
||||
});
|
||||
// 更新 tango.config.json externals
|
||||
workspace.tangoConfigJson.setValue('sandbox.externals', (targetValue) => {
|
||||
targetValue[name] = basePackage?.global;
|
||||
return targetValue;
|
||||
});
|
||||
// 更新 tango.config.json externalResources
|
||||
workspace.tangoConfigJson
|
||||
.setValue('sandbox.externalResources', (targetValue) => {
|
||||
basePackage.resources.forEach((resource: string) => {
|
||||
(targetValue as string[]).push(
|
||||
resource.replace('{latest_version}', basePackage.version),
|
||||
);
|
||||
});
|
||||
return targetValue;
|
||||
})
|
||||
.update();
|
||||
}
|
||||
onBaseDependencyAdd && onBaseDependencyAdd(basePackage);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
switch (type) {
|
||||
case DependencyItemType.其他依赖: {
|
||||
workspace.updateDependency(
|
||||
name,
|
||||
version,
|
||||
hasUpgraded && umd
|
||||
? {
|
||||
package: {
|
||||
type: 'dependency',
|
||||
...record?.package,
|
||||
version,
|
||||
library,
|
||||
resources,
|
||||
designerResources,
|
||||
},
|
||||
}
|
||||
: {},
|
||||
);
|
||||
break;
|
||||
}
|
||||
onFinishCallback(values?.name);
|
||||
});
|
||||
};
|
||||
// case DependencyItemType.业务组件: {
|
||||
// const targetBizInfo = (bizList as any)?.find(
|
||||
// (biz: { packageName: string }) => biz.packageName === name,
|
||||
// );
|
||||
// onBizDependencyAdd &&
|
||||
// onBizDependencyAdd({
|
||||
// ...values,
|
||||
// detail: targetBizInfo,
|
||||
// });
|
||||
// break;
|
||||
// }
|
||||
case DependencyItemType.基础包: {
|
||||
const basePackage = templateBaseDependencies[name];
|
||||
// 添加 package.json
|
||||
workspace.updateDependency(
|
||||
name,
|
||||
basePackage.version || 'latest',
|
||||
hasUpgraded
|
||||
? {
|
||||
package: basePackage.package,
|
||||
}
|
||||
: {},
|
||||
);
|
||||
|
||||
const onValuesChange = ({ type: depType }: { type: DependencyItemType; name: string }) => {
|
||||
if (depType) {
|
||||
// type 变更触发
|
||||
form.resetFields(['name', 'version']);
|
||||
setType(depType);
|
||||
// 配置已经合并为新版,不需要走旧的逻辑
|
||||
if (!hasUpgraded) {
|
||||
// 更新 tango.config.json baseDependencies
|
||||
workspace.tangoConfigJson.setValue('baseDependencies', (targetValue) => {
|
||||
const bases = targetValue || [];
|
||||
(bases as string[]).push(name);
|
||||
return bases;
|
||||
});
|
||||
// 更新 tango.config.json externals
|
||||
workspace.tangoConfigJson.setValue('sandbox.externals', (targetValue) => {
|
||||
targetValue[name] = basePackage?.global;
|
||||
return targetValue;
|
||||
});
|
||||
// 更新 tango.config.json externalResources
|
||||
workspace.tangoConfigJson
|
||||
.setValue('sandbox.externalResources', (targetValue) => {
|
||||
basePackage.resources.forEach((resource: string) => {
|
||||
(targetValue as string[]).push(
|
||||
resource.replace('{latest_version}', basePackage.version),
|
||||
);
|
||||
});
|
||||
return targetValue;
|
||||
})
|
||||
.update();
|
||||
}
|
||||
onBaseDependencyAdd && onBaseDependencyAdd(basePackage);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
onFinishCallback(values?.name);
|
||||
};
|
||||
|
||||
const onCloseModal = () => {
|
||||
function onCloseModal() {
|
||||
off();
|
||||
form.resetFields(['name', 'version']);
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<Box px="l" py="m">
|
||||
@@ -559,31 +549,155 @@ function AddDependencyModal({
|
||||
<PlusOutlined />
|
||||
添加依赖
|
||||
</Button>
|
||||
<Modal
|
||||
width={560}
|
||||
<DependencyConfigModal
|
||||
open={open}
|
||||
title="添加依赖"
|
||||
title={record ? `修改依赖:${record.name}` : '添加依赖'}
|
||||
onOk={onOk}
|
||||
onCancel={onCloseModal}
|
||||
destroyOnClose
|
||||
{...rest}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
type DependencyConfigModalProps = Omit<ModalProps, 'onOk'> & {
|
||||
record?: Record<string, any>;
|
||||
onOk: (
|
||||
event: React.MouseEvent<HTMLElement, MouseEvent>,
|
||||
data: {
|
||||
values: Record<string, any>;
|
||||
hasUpgraded: boolean;
|
||||
},
|
||||
) => any;
|
||||
};
|
||||
|
||||
function ResourceFormList(props: Omit<FormListProps, 'children'>) {
|
||||
return (
|
||||
<Form.List {...props}>
|
||||
{(fields, { add, remove }, { errors }) => (
|
||||
<>
|
||||
{fields.map((field) => (
|
||||
<Form.Item key={field.key}>
|
||||
<Box display="flex" alignItems="center">
|
||||
<Form.Item
|
||||
label="资源地址"
|
||||
{...field}
|
||||
rules={[{ required: true, whitespace: true, type: 'url' }]}
|
||||
noStyle
|
||||
>
|
||||
<Input
|
||||
placeholder="资源地址中的版本号可用 {{version}} 替代"
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Box width="20px" fontSize="18px" ml="6px" textAlign="center" flex="none">
|
||||
<a onClick={() => remove(field.name)}>
|
||||
<MinusCircleOutlined />
|
||||
</a>
|
||||
</Box>
|
||||
</Box>
|
||||
</Form.Item>
|
||||
))}
|
||||
<Button type="dashed" onClick={() => add()} block>
|
||||
添加
|
||||
</Button>
|
||||
<Form.ErrorList errors={errors} />
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
);
|
||||
}
|
||||
|
||||
function DependencyConfigModal({
|
||||
record,
|
||||
open,
|
||||
onOk,
|
||||
onCancel,
|
||||
...rest
|
||||
}: DependencyConfigModalProps) {
|
||||
// 依赖类型
|
||||
const [type, setType] = useState<DependencyItemType>(DependencyItemType.其他依赖);
|
||||
const [form] = Form.useForm();
|
||||
const workspace = useWorkspace();
|
||||
// 根据是否有 packages 字段,判断是否已升级为新的规范
|
||||
const hasUpgraded = !!workspace.tangoConfigJson?.getValue('packages');
|
||||
// const [versionList, setVersionList] = useState<Array<{ label: string; value: string }>>([]);
|
||||
|
||||
// const remoteServices = useRemoteServices();
|
||||
|
||||
// useEffect(() => {
|
||||
// remoteServices?.DependencyService?.listPackageVersions({ packageName: record.name }).then(
|
||||
// (data: any) => setVersionList((data || []).map((v: string) => ({ label: v, value: v }))),
|
||||
// );
|
||||
// }, [remoteServices, record]);
|
||||
|
||||
useEffect(() => {
|
||||
if (open && form) {
|
||||
form.resetFields();
|
||||
if (record) {
|
||||
setType(record?.type);
|
||||
form.setFieldsValue({
|
||||
...record,
|
||||
...record?.package,
|
||||
umd: !!record?.package?.resources?.length,
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [record, open, form]);
|
||||
|
||||
const onSubmit = (event: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||
form.validateFields().then((values) => {
|
||||
if (onOk) {
|
||||
onOk(event, {
|
||||
values,
|
||||
hasUpgraded,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onValuesChange = ({ type: depType }: { type: DependencyItemType; name: string }) => {
|
||||
if (depType) {
|
||||
// type 变更触发
|
||||
form.resetFields();
|
||||
form.setFieldValue('type', depType);
|
||||
setType(depType);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
width={560}
|
||||
open={open}
|
||||
title={record ? `修改依赖:${record.name}` : '添加依赖'}
|
||||
onOk={onSubmit}
|
||||
onCancel={onCancel}
|
||||
destroyOnClose
|
||||
{...rest}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
autoComplete="off"
|
||||
initialValues={{ type: DependencyItemType.业务组件 }}
|
||||
initialValues={{ type }}
|
||||
onValuesChange={onValuesChange}
|
||||
>
|
||||
<Form.Item label="依赖类型" name="type">
|
||||
<Radio.Group value={type}>
|
||||
{/* <Radio.Button value={DependencyItemType.业务组件}>业务组件</Radio.Button> */}
|
||||
<Radio.Button value={DependencyItemType.基础包}>基础包</Radio.Button>
|
||||
<Radio.Button value={DependencyItemType.其他依赖}>其他依赖</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="npm包名" name="name" rules={[{ required: true }]}>
|
||||
<Input placeholder="输入依赖名" />
|
||||
</Form.Item>
|
||||
{!record && (
|
||||
<>
|
||||
<Form.Item label="依赖类型" name="type" rules={[{ required: true }]}>
|
||||
<Radio.Group>
|
||||
{/* <Radio.Button value={DependencyItemType.业务组件}>业务组件</Radio.Button> */}
|
||||
<Radio.Button value={DependencyItemType.基础包}>基础包</Radio.Button>
|
||||
<Radio.Button value={DependencyItemType.其他依赖}>其他依赖</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="npm 包名" name="name" rules={[{ required: true }]}>
|
||||
<Input placeholder="输入依赖名" />
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
<Form.Item
|
||||
label="版本号"
|
||||
name="version"
|
||||
@@ -591,8 +705,43 @@ function AddDependencyModal({
|
||||
>
|
||||
<Input placeholder="输入版本号" />
|
||||
</Form.Item>
|
||||
{hasUpgraded && (
|
||||
<Form.Item name="umd" valuePropName="checked">
|
||||
<Checkbox>使用预构建 UMD 资源</Checkbox>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item noStyle shouldUpdate>
|
||||
{() =>
|
||||
form.getFieldValue('umd') && (
|
||||
<>
|
||||
<Form.Item label="全局变量名" name="library">
|
||||
<Input placeholder="输入 UMD 资源挂载在全局变量下的变量名" />
|
||||
</Form.Item>
|
||||
<Form.Item label="资源地址" required>
|
||||
<ResourceFormList
|
||||
name="resources"
|
||||
rules={[
|
||||
{
|
||||
validator: async (rule, value) => {
|
||||
if (!value?.length) {
|
||||
throw new Error('资源地址不能为空');
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
{form.getFieldValue('type') === DependencyItemType.基础包 && (
|
||||
<Form.Item label="设计器资源地址">
|
||||
<ResourceFormList name="designerResources" />
|
||||
</Form.Item>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,11 @@ export const PreviewTool = observer(() => {
|
||||
shape="ghost"
|
||||
selected={designer.isPreview}
|
||||
onClick={() => {
|
||||
designer.toggleIsPreview(!designer.isPreview);
|
||||
const nextIsPreview = !designer.isPreview;
|
||||
designer.toggleIsPreview(nextIsPreview);
|
||||
if (nextIsPreview) {
|
||||
designer.setActiveView('design');
|
||||
}
|
||||
}}
|
||||
tip="预览"
|
||||
>
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.2.5](https://github.com/netease/tango/compare/@music163/tango-setting-form@0.2.4...@music163/tango-setting-form@0.2.5) (2023-10-12)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-setting-form
|
||||
|
||||
## [0.2.4](https://github.com/netease/tango/compare/@music163/tango-setting-form@0.2.3...@music163/tango-setting-form@0.2.4) (2023-10-09)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-setting-form
|
||||
|
||||
## [0.2.3](https://github.com/netease/tango/compare/@music163/tango-setting-form@0.2.2...@music163/tango-setting-form@0.2.3) (2023-09-22)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-setting-form
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-setting-form",
|
||||
"version": "0.2.3",
|
||||
"version": "0.2.5",
|
||||
"description": "setting form of tango-apps",
|
||||
"author": "wwsun <ww.sun@outlook.com>",
|
||||
"homepage": "",
|
||||
@@ -30,7 +30,7 @@
|
||||
"@ant-design/icons": "^4.8.0",
|
||||
"@music163/tango-core": "^0.2.1",
|
||||
"@music163/tango-helpers": "^0.1.3",
|
||||
"@music163/tango-ui": "^0.1.4",
|
||||
"@music163/tango-ui": "^0.1.6",
|
||||
"antd": "^4.24.2",
|
||||
"coral-system": "^1.0.5",
|
||||
"mobx": "6.9.0",
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.1.6](https://github.com/netease/tango/compare/@music163/tango-ui@0.1.5...@music163/tango-ui@0.1.6) (2023-10-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- refactor expSetter ([#41](https://github.com/netease/tango/issues/41)) ([d63517e](https://github.com/netease/tango/commit/d63517ecb936e4227e70c33e610664316625f4f4))
|
||||
|
||||
## [0.1.5](https://github.com/netease/tango/compare/@music163/tango-ui@0.1.4...@music163/tango-ui@0.1.5) (2023-10-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- update tabs style ([46b5445](https://github.com/netease/tango/commit/46b54459ceb23f9df52b9eb40dc935bc872ba1b4))
|
||||
|
||||
## [0.1.4](https://github.com/netease/tango/compare/@music163/tango-ui@0.1.3...@music163/tango-ui@0.1.4) (2023-09-18)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-ui
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-ui",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.6",
|
||||
"description": "ui widgets of tango",
|
||||
"keywords": [
|
||||
"react",
|
||||
|
||||
@@ -9,7 +9,9 @@ export interface CopyClipboardProps {
|
||||
* 拷贝成功后到回调
|
||||
*/
|
||||
onCopy?: () => void;
|
||||
children?: React.ReactNode | ((copied: boolean) => React.ReactElement);
|
||||
children?:
|
||||
| React.ReactNode
|
||||
| ((data: { copied: boolean; onClick: Function }) => React.ReactElement);
|
||||
}
|
||||
|
||||
export function CopyClipboard({ onCopy, text, children }: CopyClipboardProps) {
|
||||
@@ -37,7 +39,7 @@ export function CopyClipboard({ onCopy, text, children }: CopyClipboardProps) {
|
||||
};
|
||||
|
||||
if (typeof children === 'function') {
|
||||
return React.cloneElement(children(copied), {
|
||||
return React.cloneElement(children({ copied, onClick }), {
|
||||
onClick,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export function Tabs({
|
||||
const tabBarStyle = useMemo(() => {
|
||||
let ret = centered ? centerTabBarStyle : defaultTabBarStyle;
|
||||
if (isTabBarSticky) {
|
||||
ret = { ...ret, position: 'sticky', top: tabBarStickyOffset, zIndex: 1, background: '#fff' };
|
||||
ret = { ...ret, position: 'sticky', top: tabBarStickyOffset, zIndex: 2, background: '#fff' };
|
||||
}
|
||||
return ret;
|
||||
}, [centered, isTabBarSticky, tabBarStickyOffset]);
|
||||
|
||||
Reference in New Issue
Block a user