baidu / amis

前端低代码框架,通过 JSON 配置就能生成各种页面。
https://baidu.github.io/amis/
Apache License 2.0
17.28k stars 2.51k forks source link

React项目实现APP组件的路由功能 #2558

Open user-huage opened 3 years ago

user-huage commented 3 years ago

实现场景:

请简单描述你想要实现的原始场景,例如:我想要实现React项目在App组件模式下路由跳转页面的功能. 使用的项目是官网文档提供的https://github.com/aisuda/amis-react-starter项目

存在的问题:

通过重写jumpTo方法来实现,但发现会出现文件找不到404的问题 image

当前方案:

app.tsx

import React from 'react';
import 'amis/lib/themes/default.css';
import 'font-awesome/css/font-awesome.css';
import axios from 'axios';
import copy from 'copy-to-clipboard';
import {render as renderAmis, ToastComponent, AlertComponent} from 'amis';
import {alert, confirm} from 'amis/lib/components/Alert';
import {toast} from 'amis/lib/components/Toast';

import {normalizeLink,isCurrentUrl} from './init'
import { createHashHistory } from 'history';
const history = createHashHistory() // hash模式

// amis 环境配置
const env = {
  // 下面三个接口必须实现
  fetcher: ({
    url, // 接口地址
    method, // 请求方法 get、post、put、delete
    data, // 请求数据
    responseType,
    config, // 其他配置
    headers // 请求头
  }: any) => {
    config = config || {};
    config.withCredentials = true;
    responseType && (config.responseType = responseType);

    if (config.cancelExecutor) {
      config.cancelToken = new (axios as any).CancelToken(
        config.cancelExecutor
      );
    }

    config.headers = headers || {};

    if (method !== 'post' && method !== 'put' && method !== 'patch') {
      if (data) {
        config.params = data;
      }
      return (axios as any)[method](url, config);
    } else if (data && data instanceof FormData) {
      config.headers = config.headers || {};
      config.headers['Content-Type'] = 'multipart/form-data';
    } else if (
      data &&
      typeof data !== 'string' &&
      !(data instanceof Blob) &&
      !(data instanceof ArrayBuffer)
    ) {
      data = JSON.stringify(data);
      config.headers = config.headers || {};
      config.headers['Content-Type'] = 'application/json';
    }

    return (axios as any)[method](url, data, config);
  },
  isCancel: (value: any) => (axios as any).isCancel(value),
  copy: (content: string) => {
    copy(content);
    toast.success('内容已复制到粘贴板');
  },

  // 后面这些接口可以不用实现

  // 默认是地址跳转
  jumpTo: (
    to: string /*目标地址*/,
    action: any /* action对象*/
  ) => {
    // 用来实现页面跳转, actionType:link、url 都会进来。
    if (to === 'goBack') {
      return history.back();
    }

    to = normalizeLink(to);

    if (isCurrentUrl(to)) {
      return;
    }

    if (action && action.actionType === 'url') {
      action.blank === false ? (window.location.href = to) : window.open(to, '_blank');
      return;
    } else if (action && action.blank) {
      window.open(to, '_blank');
      return;
    }

    if (/^https?:\/\//.test(to)) {
      window.location.href = to;
    } else {
      history.push(to);
    }
  },

  // updateLocation: (
  //   location: string /*目标地址*/,
  //   replace: boolean /*是replace,还是push?*/
  // ) => {
  //   // 地址替换,跟 jumpTo 类似
  // },

  // isCurrentUrl: (
  //   url: string /*url地址*/,
  // ) => {
  //   // 用来判断是否目标地址当前地址
  // },

  // notify: (
  //   type: 'error' | 'success' /**/,
  //   msg: string /*提示内容*/
  // ) => {
  //   toast[type]
  //     ? toast[type](msg, type === 'error' ? '系统错误' : '系统消息')
  //     : console.warn('[Notify]', type, msg);
  // },
  // alert,
  // confirm,
};

class AMISComponent extends React.Component<any, any> {
  render() {
    return renderAmis(
      // 这里是 amis 的 Json 配置。
      {
        "type": "app",
        "brandName": "应用名称",
        "pages": [
          {
            "label": "Home",
            "url": "/",
            "redirect": "/index/1"
          },
          {
            "label": "示例",
            "children": [
              {
                "label": "页面A",
                "url": "index",
                "schema": {
                  "type": "page",
                  "title": "页面A",
                  "body": "页面A"
                },
                "children": [
                  {
                    "label": "页面A-1",
                    "url": "1",
                    "schema": {
                      "type": "page",
                      "title": "页面A-1",
                      "body": "页面A-1"
                    }
                  },
                  {
                    "label": "页面A-2",
                    "url": "2",
                    "schema": {
                      "type": "page",
                      "title": "页面A-2",
                      "body": "页面A-2"
                    }
                  },
                  {
                    "label": "页面A-3",
                    "url": "3",
                    "schema": {
                      "type": "page",
                      "title": "页面A-3",
                      "body": "页面A-3"
                    }
                  }
                ]
              },
              {
                "label": "页面B",
                "schema": {
                  "type": "page",
                  "title": "页面B",
                  "body": "页面B"
                }
              },
              {
                "label": "页面C",
                "schema": {
                  "type": "page",
                  "title": "页面C",
                  "body": "页面C"
                }
              },
              {
                "label": "列表示例",
                "url": "/crud",
                "rewrite": "/crud/list",
                "icon": "fa fa-cube",
                "children": [
                  {
                    "label": "列表",
                    "url": "/crud/list",
                    "icon": "fa fa-list",
                    "schemaApi": "get:/page/crud-list.json"
                  },
                  {
                    "label": "新增",
                    "url": "/crud/new",
                    "icon": "fa fa-plus",
                    "schemaApi": "get:/page/crud-new.json"
                  },
                  {
                    "label": "查看",
                    "url": "/crud/:id",
                    "schemaApi": "get:/page/crud-view.json"
                  },
                  {
                    "label": "修改",
                    "url": "/crud/:id/edit",
                    "schemaApi": "get:/page/crud-edit.json"
                  }
                ]
              }
            ]
          },
          {
            "label": "分组2",
            "children": [
              {
                "label": "用户管理",
                "schema": {
                  "type": "page",
                  "title": "用户管理",
                  "body": "页面C"
                }
              },
              {
                "label": "外部链接",
                "link": "http://baidu.gitee.io/amis"
              },
              {
                "label": "部门管理",
                "schemaApi": "https://3xsw4ap8wah59.cfc-execute.bj.baidubce.com/api/amis-mock/mock2/service/form?tpl=tpl3"
              }
            ]
          }
        ],
      },
      {
        // props...
      },
      env
    );
  }
}

class APP extends React.Component<any, any> {
  render() {
    return (
      <>
        <ToastComponent key="toast" position={'top-right'} />
        <AlertComponent key="alert" />
        <AMISComponent />
      </>
    );
  }
}

export default APP;

init.js

import { createHashHistory } from 'history';
const history = createHashHistory() // hash模式

function normalizeLink(to, location = history.location) {
  to = to || '';
  if (to && to[0] === '#') {
    to = location.pathname + location.search + to;
  } else if (to && to[0] === '?') {
    to = location.pathname + to;
  }

  const idx = to.indexOf('?');
  const idx2 = to.indexOf('#');
  let pathname = ~idx
    ? to.substring(0, idx)
    : ~idx2
      ? to.substring(0, idx2)
      : to;
  let search = ~idx ? to.substring(idx, ~idx2 ? idx2 : undefined) : '';
  let hash = ~idx2 ? to.substring(idx2) : location.hash;

  if (!pathname) {
    pathname = location.pathname;
  } else if (pathname[0] != '/' && !/^https?\:\/\//.test(pathname)) {
    let relativeBase = location.pathname;
    const paths = relativeBase.split('/');
    paths.pop();
    let m;
    while ((m = /^\.\.?\//.exec(pathname))) {
      if (m[0] === '../') {
        paths.pop();
      }
      pathname = pathname.substring(m[0].length);
    }
    pathname = paths.concat(pathname).join('/');
  }

  return pathname + search + hash;
}

function isCurrentUrl(to, ctx) {
  if (!to) {
    return false;
  }
  const pathname = history.location.pathname;
  const link = normalizeLink(to, {
    ...location,
    pathname,
    hash: ''
  });

  if (!~link.indexOf('http') && ~link.indexOf(':')) {
    let strict = ctx && ctx.strict;
    return match(link, {
      decode: decodeURIComponent,
      strict: typeof strict !== 'undefined' ? strict : true
    })(pathname);
  }

  return decodeURI(pathname) === link;
}

export {normalizeLink,isCurrentUrl}
yugonglian commented 3 years ago

同样的问题,amis 为1.2.3 ,怎么改路径都是404

user-huage commented 3 years ago

@图灵

nwind commented 3 years ago

react 项目直接用 react-router,参考旧版的实现 https://github.com/aisuda/amis-admin/tree/webpack