diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9bd61c7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,49 @@ +name: CI +# Event设置为main分支的pull request事件, +# 这里的main分支相当于master分支,github项目新建是把main设置为默认分支,我懒得改了所以就保持这样吧 +on: + pull_request: + branches: main +jobs: + # 只需要定义一个job并命名为CI + CI: + runs-on: ubuntu-latest + steps: + # 拉取项目代码 + - name: Checkout repository + uses: actions/checkout@v2 + # 给当前环境下载node + - name: Use Node.js + uses: actions/setup-node@v3 + with: + node-version: '18.x' + # 检查缓存 + # 如果key命中缓存则直接将缓存的文件还原到 path 目录,从而减少流水线运行时间 + # 若 key 没命中缓存时,在当前Job成功完成时将自动创建一个新缓存 + - name: Cache + # 缓存命中结果会存储在steps.[id].outputs.cache-hit里,该变量在继后的step中可读 + id: cache-dependencies + uses: actions/cache@v3 + with: + # 缓存文件目录的路径 + path: | + **/node_modules + # key中定义缓存标志位的生成方式。runner.OS指当前环境的系统。外加对yarn.lock内容生成哈希码作为key值,如果yarn.lock改变则代表依赖有变化。 + # 这里用yarn.lock而不是package.json是因为package.json中还有version和description之类的描述项目但和依赖无关的属性 + key: ${{runner.OS}}-${{hashFiles('**/yarn.lock')}} + # 安装依赖 + - name: Installing Dependencies + # 如果缓存标志位没命中,则执行该step。否则就跳过该step + if: steps.cache-dependencies.outputs.cache-hit != 'true' + run: yarn install + # 预构建 + - name: Prebuild + run: yarn build + # 运行代码扫描 + - name: Running Lint + # 通过前面章节定义的命令行执行代码扫描 + run: yarn eslint + # 运行自动化测试 + - name: Running Test + # 通过前面章节定义的命令行执行自动化测试 + run: yarn test diff --git a/README.md b/README.md index c428612..bd373f1 100644 --- a/README.md +++ b/README.md @@ -31,26 +31,28 @@ You can view the detailed usage guide through the following links: - Out-of-the-box front-end low-code designer, providing flexible and easy-to-use designer React components - Developed using TypeScript, providing complete type definition files -## 🌐 Compatibility - -- Modern browsers(Chrome >= 80, Edge >= 80, last 2 safari versions, last 2 firefox versions) - ## 🎯 Milestone ```mermaid gantt dateFormat YYYY-MM-DD - title Tango Release Milestone + title Tango 1.0 Milestones excludes weekends %% (`excludes` accepts specific dates in YYYY-MM-DD format, days of the week ("sunday") or "weekends", but not the word "weekdays".) section Builder Release - Alpha version :active, m1, 2023-08-10, 2023-08-30 - Beta version : m2, 2023-09-01, 2023-09-30 - 1.0 RC : m3, 2023-10-01, 2023-12-15 - 1.0 version : m4, after m3, 10d + 1.0 Alpha : m1, 2023-10-01, 2024-01-31 + 1.0 RC : m2, 2024-02-01, 2024-04-30 + 1.0 Stable : m3, 2024-04-30, 2024-12-31 ``` +> [!NOTE] +> Alpha versions will continue to optimize some implementation protocols and continuously integrate new features from the internal version of NetEase; RC versions will provide a stable parsing protocol and implementation; The 1.0 official version is planned to be released in Q2 2024. After the release of the 1.0 official version, we will continue to iterate on versions and gradually incorporate core capabilities such as process design, component library, and AIGC from the internal version. + +## 🌐 Compatibility + +- Modern browsers(Chrome >= 80, Edge >= 80, last 2 safari versions, last 2 firefox versions) + ## 💻 Development ### Environment diff --git a/README.zh-CN.md b/README.zh-CN.md index d50a1a8..6aa2f71 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -31,26 +31,28 @@ - 开箱即用的前端低代码设计器,提供灵活易用的设计器 React 组件 - 使用 TypeScript 开发,提供完整的类型定义文件 -## 🌐 兼容环境 - -- 现代浏览器(Chrome >= 80, Edge >= 80, last 2 safari versions, last 2 firefox versions) - ## 🎯 里程碑 ```mermaid gantt dateFormat YYYY-MM-DD - title Tango Release Milestone + title Tango 1.0 Milestones excludes weekends %% (`excludes` accepts specific dates in YYYY-MM-DD format, days of the week ("sunday") or "weekends", but not the word "weekdays".) section Builder Release - Alpha version :active, m1, 2023-08-10, 2023-08-30 - Beta version : m2, 2023-09-01, 2023-09-30 - 1.0 RC : m3, 2023-10-01, 2023-12-15 - 1.0 version : m4, after m3, 10d + 1.0 Alpha : m1, 2023-10-01, 2024-01-31 + 1.0 RC : m2, 2024-02-01, 2024-04-30 + 1.0 Stable : m3, 2024-04-30, 2024-12-31 ``` +> [!NOTE] +> Alpha 版本会持续对一些实现协议进行优化,并持续融合网易内网版本中的新增特性;RC 版本将会提供基本稳定的解析协议和实现;1.0 正式版本计划在 2024 年Q2发布。1.0 正式版发布后我们将会持续进行版本迭代,并逐步融入内网版本中的流程设计、组件库、AIGC 等核心基础能力。 + +## 🌐 兼容环境 + +- 现代浏览器(Chrome >= 80, Edge >= 80, last 2 safari versions, last 2 firefox versions) + ## 💻 开发 ### 推荐开发环境 diff --git a/apps/website/docusaurus.config.js b/apps/website/docusaurus.config.js index 39cdc0b..d443c85 100644 --- a/apps/website/docusaurus.config.js +++ b/apps/website/docusaurus.config.js @@ -62,13 +62,13 @@ const config = { ({ // Replace with your project's social card image: 'img/social-card.png', - announcementBar: { - id: 'notion_alert', - content: '🏗 当前版本为测试版,请暂时不要用于生产环境,正式版将于2023年Q4发布!', - backgroundColor: 'var(--ifm-color-primary-contrast-background)', - textColor: 'var(--ifm-color-primary-contrast-foreground)', - isCloseable: false, - }, + // announcementBar: { + // id: 'notion_alert', + // content: '🏗 当前版本为测试版,请暂时不要用于生产环境,正式版将于2023年Q4发布!', + // backgroundColor: 'var(--ifm-color-primary-contrast-background)', + // textColor: 'var(--ifm-color-primary-contrast-foreground)', + // isCloseable: false, + // }, navbar: { title: '', logo: { diff --git a/packages/core/src/helpers/ast/traverse.ts b/packages/core/src/helpers/ast/traverse.ts index 10d29b0..303fe05 100644 --- a/packages/core/src/helpers/ast/traverse.ts +++ b/packages/core/src/helpers/ast/traverse.ts @@ -1006,7 +1006,7 @@ export function serviceConfig2Node(payload: object) { }); } -export function updateServiceConfigToServiceFile(ast: t.File, config: object) { +export function updateServiceConfigToServiceFile(ast: t.File, config: Dict) { traverse(ast, { CallExpression(path) { const calleeName = keyNode2value(path.node.callee) as string; diff --git a/packages/core/src/models/interfaces.ts b/packages/core/src/models/interfaces.ts index 932be88..ad051fd 100644 --- a/packages/core/src/models/interfaces.ts +++ b/packages/core/src/models/interfaces.ts @@ -206,14 +206,14 @@ export interface IWorkspace { getServiceFunction?: (serviceKey: string) => { name: string; moduleName: string; - config: object; + config: Dict; }; - listServiceFunctions?: () => Record; + listServiceFunctions?: () => Dict; removeServiceFunction?: (serviceKey: string) => void; - addServiceFunction?: (serviceName: string, config: object, modName?: string) => void; - addServiceFunctions?: (configs: object, modName?: string) => void; - updateServiceFunction?: (serviceName: string, payload: object, modName?: string) => void; - updateServiceBaseConfig?: (config: object, modName?: string) => void; + addServiceFunction?: (serviceName: string, config: Dict, modName?: string) => void; + addServiceFunctions?: (configs: Dict, modName?: string) => void; + updateServiceFunction?: (serviceName: string, payload: Dict, modName?: string) => void; + updateServiceBaseConfig?: (config: Dict, modName?: string) => void; // ----------------- 状态管理文件操作 ----------------- addStoreState?: (storeName: string, stateName: string, initValue: string) => void; diff --git a/packages/core/src/models/service-module.ts b/packages/core/src/models/service-module.ts index f653464..5940462 100644 --- a/packages/core/src/models/service-module.ts +++ b/packages/core/src/models/service-module.ts @@ -77,7 +77,7 @@ export class TangoServiceModule extends TangoModule { * @param configs { [name: string]: object } * @returns */ - addServiceFunctions(configs: object) { + addServiceFunctions(configs: Dict) { this.ast = updateServiceConfigToServiceFile(this.ast, configs); return this; } diff --git a/packages/core/src/models/workspace.ts b/packages/core/src/models/workspace.ts index 707ce91..dbed7bd 100644 --- a/packages/core/src/models/workspace.ts +++ b/packages/core/src/models/workspace.ts @@ -2,6 +2,7 @@ import { action, computed, makeObservable, observable } from 'mobx'; import { JSXElement } from '@babel/types'; import { ComponentPrototypeType, + Dict, hasFileExtension, isStoreVariablePath, isString, @@ -637,9 +638,10 @@ export class Workspace extends EventTarget implements IWorkspace { /** * 获取服务函数的列表 + * @returns 返回服务函数的列表 { [serviceKey: string]: Dict } */ listServiceFunctions() { - const ret: Record = {}; + const ret: Record = {}; Object.keys(this.serviceModules).forEach((moduleName) => { const module = this.serviceModules[moduleName]; Object.keys(module.serviceFunctions).forEach((name) => { @@ -653,18 +655,18 @@ export class Workspace extends EventTarget implements IWorkspace { /** * 更新服务函数 */ - updateServiceFunction(serviceName: string, payload: object, moduleName = 'index') { + updateServiceFunction(serviceName: string, payload: Dict, moduleName = 'index') { this.serviceModules[moduleName].updateServiceFunction(serviceName, payload).update(); } /** * 新增服务函数,支持批量添加 */ - addServiceFunction(name: string, config: object, moduleName = 'index') { + addServiceFunction(name: string, config: Dict, moduleName = 'index') { this.serviceModules[moduleName]?.addServiceFunction(name, config).update(); } - addServiceFunctions(configs: object, modName = 'index') { + addServiceFunctions(configs: Dict, modName = 'index') { this.serviceModules[modName]?.addServiceFunctions(configs).update(); } @@ -680,7 +682,7 @@ export class Workspace extends EventTarget implements IWorkspace { /** * 更新服务的基础配置 */ - updateServiceBaseConfig(config: object, moduleName = 'index') { + updateServiceBaseConfig(config: Dict, moduleName = 'index') { this.serviceModules[moduleName]?.updateBaseConfig(config).update(); } diff --git a/packages/designer/src/components/variable-tree/index.tsx b/packages/designer/src/components/variable-tree/index.tsx index fc9caf0..641ddab 100644 --- a/packages/designer/src/components/variable-tree/index.tsx +++ b/packages/designer/src/components/variable-tree/index.tsx @@ -55,6 +55,21 @@ const varTreeStyle = css` type SelectNodeCallback = (data: IVariableTreeNode) => void; +type DetailModeType = + | 'detail' + | 'storeVariableDetail' + | 'serviceDetail' + | 'addVariable' + | 'addStore' + | 'addService'; + +interface IVariableTreeRenderDetailState { + mode: DetailModeType; + setMode: (mode: DetailModeType) => void; + activeNode: IVariableTreeNode; + clear: () => void; +} + export interface VariableTreeProps { defaultValueDetailMode?: ValueDetailProps['defaultMode']; dataSource: IVariableTreeNode[]; @@ -66,43 +81,63 @@ export interface VariableTreeProps { getStoreNames?: () => string[]; onSelect?: SelectNodeCallback; onAddStoreVariable?: (storeName: string, data: any) => void; + onUpdateStoreVariable?: ValueDefineProps['onSave']; + onRemoveStoreVariable?: (variableKey: string) => void; onAddStore?: (newStoreName: string) => void; onAddService?: (data: object) => void; - onRemoveVariable?: (variableKey: string) => void; - onUpdateVariable?: ValueDefineProps['onSave']; onUpdateService?: (data: object) => void; + onRemoveService?: (variableKey: string) => void; onCopy?: (data: IVariableTreeNode) => void; onView?: SelectNodeCallback; height?: number | string; showViewButton?: boolean; + /** + * 自定义头部 + */ + renderHeaderExtra?: ( + props: VariableTreeProps, + state: IVariableTreeRenderDetailState, + ) => React.ReactNode; + /** + * 自定义变量详情渲染 + * @param props + * @param state + * @returns + */ + renderDetail?: ( + props: VariableTreeProps, + state: IVariableTreeRenderDetailState, + ) => React.ReactNode; } -export function VariableTree({ - dataSource = [], - serviceModules = [], - appContext = {}, - defaultValueDetailMode, - onSelect = noop, - onAddStoreVariable = noop, - onAddStore = noop, - onAddService = noop, - onRemoveVariable = noop, - onUpdateVariable = noop, - onUpdateService = noop, - onCopy = noop, - onView = noop, - getServiceData, - getServiceNames, - getStoreNames, - getPreviewValue = noop, - showViewButton, - ...rest -}: VariableTreeProps) { +export function VariableTree(props: VariableTreeProps) { + const { + dataSource = [], + serviceModules = [], + appContext = {}, + defaultValueDetailMode, + onSelect = noop, + onAddStoreVariable = noop, + onAddStore = noop, + onAddService = noop, + onUpdateStoreVariable = noop, + onUpdateService = noop, + onRemoveStoreVariable, + onRemoveService, + onCopy = noop, + onView = noop, + getServiceData, + getServiceNames, + getStoreNames, + getPreviewValue = noop, + showViewButton, + renderHeaderExtra, + renderDetail: renderDetailProp, + ...rest + } = props; const [keyword, setKeyword] = useState(''); const [activeNode, setActiveNode] = useState(); - const [mode, setMode] = useState< - 'detail' | 'storeVariableDetail' | 'serviceDetail' | 'addVariable' | 'addStore' | 'addService' - >(); + const [mode, setMode] = useState(); const clear = useCallback(() => { setActiveNode(null); setMode(null); @@ -127,9 +162,12 @@ export function VariableTree({ : dataSource; }, [keyword, dataSource]); + const state = { activeNode, mode, setMode, clear }; + return ( + {renderHeaderExtra?.(props, state)} setKeyword(val?.trim())} /> @@ -161,7 +199,11 @@ export function VariableTree({ { - onRemoveVariable(node.key); + if (isStoreVariablePath(node.key)) { + onRemoveStoreVariable(node.key); + } else { + onRemoveService(node.key); + } }} >