diff --git a/.github/workflows/node-ci.yml b/.github/workflows/node-ci.yml index 793009536..e8330d68a 100644 --- a/.github/workflows/node-ci.yml +++ b/.github/workflows/node-ci.yml @@ -1,6 +1,12 @@ name: Nocobase test on: + push: + branches: + - main + - develop + paths-ignore: + - 'docs/**' pull_request: paths-ignore: - 'docs/**' diff --git a/docs/zh-CN/development/guide/ui-router.md b/docs/zh-CN/development/guide/ui-router.md index a587e8b4f..5894440f7 100644 --- a/docs/zh-CN/development/guide/ui-router.md +++ b/docs/zh-CN/development/guide/ui-router.md @@ -1 +1,63 @@ # UI Router + +NocoBase Client 的 Router 基于 [React Router](https://v5.reactrouter.com/web/guides/quick-start),可以通过 `` 来配置 ui routes,例子如下: + +```tsx +/** + * defaultShowCode: true + */ +import React from 'react'; +import { Link, MemoryRouter as Router } from 'react-router-dom'; +import { RouteRedirectProps, RouteSwitchProvider, RouteSwitch } from '@nocobase/client'; + +const Home = () =>

Home

; +const About = () =>

About

; + +const routes: RouteRedirectProps[] = [ + { + type: 'route', + path: '/', + exact: true, + component: 'Home', + }, + { + type: 'route', + path: '/about', + component: 'About', + }, +]; + +export default () => { + return ( + + + Home, About + + + + ); +}; +``` + +在完整的 NocoBase 应用里,可以类似以下的的方式扩展 Route: + +```tsx | pure +import { RouteSwitchContext } from '@nocobase/client'; +import React, { useContext } from 'react'; + +const HelloWorld = () => { + return
Hello ui router
; +}; + +export default React.memo((props) => { + const ctx = useContext(RouteSwitchContext); + ctx.routes.push({ + type: 'route', + path: '/hello-world', + component: HelloWorld, + }); + return {props.children}; +}); +``` + +完整示例查看 [packages/samples/custom-page](https://github.com/nocobase/nocobase/tree/develop/packages/samples/custom-page) \ No newline at end of file diff --git a/packages/core/server/src/plugin-manager.ts b/packages/core/server/src/plugin-manager.ts index ac8405119..4169ed6d0 100644 --- a/packages/core/server/src/plugin-manager.ts +++ b/packages/core/server/src/plugin-manager.ts @@ -111,11 +111,18 @@ export class PluginManager { ctx.throw(400, 'filterByTk invalid'); } const name = pm.getPackageName(filterByTk); + console.log(name, pm.plugins.keys()); const plugin = pm.get(name); - if (plugin.model) { + if (plugin?.model) { await plugin.model.destroy(); + pm.remove(name); + } else { + await pm.repository.destroy({ + filter: { + name: filterByTk, + }, + }); } - pm.remove(name); await app.reload(); await app.start(); ctx.body = 'ok'; diff --git a/packages/samples/custom-page/README.md b/packages/samples/custom-page/README.md new file mode 100644 index 000000000..d83f8849d --- /dev/null +++ b/packages/samples/custom-page/README.md @@ -0,0 +1,28 @@ +# Custom page + +## Register + +```ts +yarn pm add sample-custom-page +``` + +## Activate + +```bash +yarn pm enable sample-custom-page +``` + +## Launch the app + +```bash +# for development +yarn dev + +# for production +yarn build +yarn start +``` + +## Visit the custom page + +Open http://localhost:13000/hello-world in a web browser. diff --git a/packages/samples/custom-page/client.d.ts b/packages/samples/custom-page/client.d.ts new file mode 100755 index 000000000..765db9222 --- /dev/null +++ b/packages/samples/custom-page/client.d.ts @@ -0,0 +1,4 @@ +// @ts-nocheck +export * from './lib/client'; +export { default } from './lib/client'; + diff --git a/packages/samples/custom-page/client.js b/packages/samples/custom-page/client.js new file mode 100755 index 000000000..238820257 --- /dev/null +++ b/packages/samples/custom-page/client.js @@ -0,0 +1,30 @@ +"use strict"; + +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } + +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + +var _index = _interopRequireWildcard(require("./lib/client")); + +Object.defineProperty(exports, "__esModule", { + value: true +}); +var _exportNames = {}; +Object.defineProperty(exports, "default", { + enumerable: true, + get: function get() { + return _index.default; + } +}); + +Object.keys(_index).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; + if (key in exports && exports[key] === _index[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function get() { + return _index[key]; + } + }); +}); diff --git a/packages/samples/custom-page/package.json b/packages/samples/custom-page/package.json new file mode 100644 index 000000000..61b5f8266 --- /dev/null +++ b/packages/samples/custom-page/package.json @@ -0,0 +1,11 @@ +{ + "name": "@nocobase/plugin-sample-custom-page", + "version": "0.7.4-alpha.7", + "main": "lib/server/index.js", + "dependencies": {}, + "devDependencies": { + "@nocobase/client": "0.7.4-alpha.7", + "@nocobase/server": "0.7.4-alpha.7", + "@nocobase/test": "0.7.4-alpha.7" + } +} diff --git a/packages/samples/custom-page/server.d.ts b/packages/samples/custom-page/server.d.ts new file mode 100755 index 000000000..e70edb928 --- /dev/null +++ b/packages/samples/custom-page/server.d.ts @@ -0,0 +1,4 @@ +// @ts-nocheck +export * from './lib/server'; +export { default } from './lib/server'; + diff --git a/packages/samples/custom-page/server.js b/packages/samples/custom-page/server.js new file mode 100755 index 000000000..d06a7eb92 --- /dev/null +++ b/packages/samples/custom-page/server.js @@ -0,0 +1,30 @@ +"use strict"; + +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } + +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + +var _index = _interopRequireWildcard(require("./lib/server")); + +Object.defineProperty(exports, "__esModule", { + value: true +}); +var _exportNames = {}; +Object.defineProperty(exports, "default", { + enumerable: true, + get: function get() { + return _index.default; + } +}); + +Object.keys(_index).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; + if (key in exports && exports[key] === _index[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function get() { + return _index[key]; + } + }); +}); diff --git a/packages/samples/custom-page/src/client/index.tsx b/packages/samples/custom-page/src/client/index.tsx new file mode 100644 index 000000000..289a2fcbd --- /dev/null +++ b/packages/samples/custom-page/src/client/index.tsx @@ -0,0 +1,16 @@ +import { RouteSwitchContext } from '@nocobase/client'; +import React, { useContext } from 'react'; + +const HelloWorld = () => { + return
Hello ui router
; +}; + +export default React.memo((props) => { + const ctx = useContext(RouteSwitchContext); + ctx.routes.push({ + type: 'route', + path: '/hello-world', + component: HelloWorld, + }); + return {props.children}; +}); diff --git a/packages/samples/custom-page/src/index.ts b/packages/samples/custom-page/src/index.ts new file mode 100644 index 000000000..7ddad5814 --- /dev/null +++ b/packages/samples/custom-page/src/index.ts @@ -0,0 +1 @@ +export { default } from './server'; diff --git a/packages/samples/custom-page/src/server/index.ts b/packages/samples/custom-page/src/server/index.ts new file mode 100644 index 000000000..66d77e12b --- /dev/null +++ b/packages/samples/custom-page/src/server/index.ts @@ -0,0 +1,21 @@ +import { InstallOptions, Plugin } from '@nocobase/server'; + +export class CustomPagePlugin extends Plugin { + getName(): string { + return this.getPackageName(__dirname); + } + + initialize() {} + + beforeLoad() { + // TODO + } + + async load() {} + + async install(options: InstallOptions) { + // TODO + } +} + +export default CustomPagePlugin;