yangyin
2024-11-08 f41e52e3debf30558d556dc0451776f5422fb9b8
src/components/amis/AMISRenderer.vue
@@ -1,21 +1,48 @@
<template><div class="my-amis-scope">加载中...</div></template>
<template><div ref="amisRootRef" class="my-amis-scope">加载中...</div></template>
<script lang="ts">
// import '/static/sdk/sdk.js';
// import '/static/sdk/sdk.css';
// import '/static/sdk/iconfont.css';
import { accessSessionKey, handleNoAuth } from '/@/utils/request';
import { NO_AUTH_API_LIST } from '/@/api/ai/chat';
import { LOGIN_URL, TEL_LOGIN_URL } from '/@/api/ai/user';
<script setup lang="ts">
import { MAIN_URL } from '/@/constants';
import router from '/@/router/index';
import { Local } from '/@/utils/storage';
import { accessSessionKey, handleNoAuth } from '/@/utils/request';
// 可以不引用, 如果你不想要任何辅助类样式的话 (比如 `m-t-xs` 这种)
// https://aisuda.bce.baidu.com/amis/zh-CN/style/index
// import 'amis/sdk/helper.css';
import qs from 'qs';
import { onMounted, onUnmounted, reactive, ref, shallowRef, watch } from 'vue';
import { NO_AUTH_API_LIST } from '/@/api/ai/chat';
import { LOGIN_URL, TEL_LOGIN_URL } from '/@/api/ai/user';
import { Local } from '/@/utils/storage';
import { isSharePage } from '/@/stores/chatRoom';
const amisRootRef = ref<HTMLDivElement>(null);
const props = defineProps({
   schema: {
      type: Object,
      default: () => ({
         type: 'page',
         body: 'Hello World!',
      }),
   },
   locals: {
      type: Object,
      default: () => ({}),
   },
   context:{
      type: Object,
      default: () => ({}),
   },
   props: {
      type: Object,
      default: () => ({}),
   },
   env: {
      type: Object,
      default: () => ({}),
   },
});
const emit = defineEmits(['ready']);
function loadScript(callback) {
   window.eventList.amisSdkJsPromise
@@ -27,18 +54,10 @@
      });
}
function loadStyles(styles) {
   for (const path of styles) {
      const style = document.createElement('link');
      style.setAttribute('rel', 'stylesheet');
      style.setAttribute('type', 'text/css');
      style.setAttribute('href', path);
      document.head.appendChild(style);
   }
}
function loadSDK() {
   return new Promise((resolve, reject) => {
      // loadStyles(['/static/amis/sdk/sdk.css', '/static/amis/sdk/helper.css', '/static/amis/sdk/iconfont.css']);
      if (window.amisRequire) {
         resolve();
         return;
@@ -54,183 +73,182 @@
   });
}
export default {
   name: 'AMISRenderer',
   components: {},
   props: {
      schema: {
         type: Object,
         default: () => ({
            type: 'page',
            body: 'Hello World!',
         }),
      },
      locals: {
         type: Object,
         default: () => ({}),
      },
      props: {
         type: Object,
         default: () => ({}),
      },
      env: {
         type: Object,
         default: () => ({}),
      },
   },
   data() {
      return {
         // 这里面的数据所有 amis 页面都可以获取到
         // 可以用来放一下公共数据,比如用户信息等
         // 不要放受控数据,受控数据应该通过 data 下发
         context: {
            siteName: 'AMIS DEMO',
         },
         get location() {
            const current = router.currentRoute.value;
            return {
               pathname: current?.path,
               hash: current?.hash,
               query: current?.query,
               search: `?${qs.stringify(current.query)}`,
            };
            // return 'localtion'
         },
         loading: false,
         amisInstance: null,
         unmounted: false,
      };
   },
   watch: {
      locals: function () {
         this.updateProps();
      },
      props: function () {
         this.updateProps();
      },
      $route: function () {
         this.updateProps();
      },
   },
   async mounted() {
      try {
         this.loading = true;
         await loadSDK();
      } finally {
         this.loading = false;
      }
      if (this.unmounted) {
         return;
      }
const current = router.currentRoute.value;
      const scoped = amisRequire('amis/embed');
      const { normalizeLink } = amisRequire('amis');
      const instance = scoped.embed(
         this.$el,
         this.schema,
         {
            data: {
               ...this.locals,
            },
            context: this.context,
            location: this.location,
const location = {
   pathname: current?.path,
   hash: current?.hash,
   query: current?.query,
   search: `?${qs.stringify(current.query)}`,
};
            // todo 下发 location 对象
            ...this.props,
const loading = ref(false);
const amisInstance = shallowRef(null);
const unmounted = ref(false);
const updateProps = () => {
   amisInstance.value?.updateProps({
      data: {
         // ...props.locals,
      },
      context: props.context,
      ...props.props,
   });
};
watch(
   () => props.locals,
   (val) => {
      updateProps();
   }
);
watch(
   () => props.props,
   (val) => {
      updateProps();
   }
);
// watch(() => router.value, (val) => {
// })
onMounted(async () => {
   try {
      loading.value = true;
      await loadSDK();
   } finally {
      loading.value = false;
   }
   if (unmounted.value) {
      return;
   }
   const scoped = amisRequire('amis/embed');
   const { normalizeLink } = amisRequire('amis');
   const instance = scoped.embed(
      amisRootRef.value,
      props.schema,
      {
         data: {
            // ...props.locals,
         },
         {
            requestAdaptor(api) {
               // 支持异步,可以通过 api.mockResponse 来设置返回结果,跳过真正的请求发送
               // 此功能自定义 fetcher 的话会失效
               // api.context 中包含发送请求前的上下文信息
               // 获取本地的 token
               const accessSession = Local.get(accessSessionKey);
               if (!NO_AUTH_API_LIST.includes(api.url)) {
                  if (accessSession) {
                     // 将 token 添加到请求报文头中
                     api.headers['hswatersession'] = accessSession;
                  } else {
                     if (api.url !== LOGIN_URL && api.url !== TEL_LOGIN_URL) {
                        handleNoAuth(api.url);
                        throw '权限验证失败';
                     }
         context: props.context,
         location: location,
         // todo 下发 location 对象
         ...props.props,
      },
      {
         requestAdaptor(api) {
            // 支持异步,可以通过 api.mockResponse 来设置返回结果,跳过真正的请求发送
            // 此功能自定义 fetcher 的话会失效
            // api.context 中包含发送请求前的上下文信息
            // 获取本地的 token
            const accessSession = Local.get(accessSessionKey);
            if (!NO_AUTH_API_LIST.includes(api.url) && !isSharePage.value) {
               if (accessSession) {
                  // 将 token 添加到请求报文头中
                  api.headers['hswatersession'] = accessSession;
               } else {
                  if (api.url !== LOGIN_URL && api.url !== TEL_LOGIN_URL) {
                     handleNoAuth(api.url);
                     throw '权限验证失败';
                  }
               }
            }
               api.url = `${MAIN_URL}${api.url}`;
               return api;
            },
            // 覆盖 amis env
            // 参考 https://aisuda.bce.baidu.com/amis/zh-CN/docs/start/getting-started#sdk
            jumpTo: (to, action) => {
               return;
               console.log('🚀 ~ to:', to);
               console.log('🚀 ~ action:', action);
               if (to === 'goBack') {
                  return router.go(-1);
               }
               to = normalizeLink(to, this.location);
               if (action?.actionType === 'url') {
                  action.blank === false ? router.push(to) : window.open(to);
                  return;
               }
               // 主要是支持 nav 中的跳转
               if (action && to && action.target) {
                  window.open(to, action.target);
                  return;
               }
               if (/^https?:\/\//.test(to)) {
                  window.location.replace(to);
               } else {
                  router.push(to);
               }
            },
            updateLocation: (location, replace) => {
               // 禁止跳转
               return;
               console.log('🚀 ~ location:', location);
               console.log('🚀 ~ replace:', replace);
               if (location === 'goBack') {
                  return router.go(-1);
               }
               location = normalizeLink(location, this.location);
               replace ? router.replace(location) : router.replace(location);
            },
            ...this.env,
            api.url = `${MAIN_URL}${api.url}`;
            return api;
         },
         () => {
            this.$emit('ready', {
               instance,
            });
         }
      );
         // 全局 api 适配器。
         // 另外在 amis 配置项中的 api 也可以配置适配器,针对某个特定接口单独处理。
         responseAdaptor(api, payload, query, request, response) {
            //                {
            //   "json_ok": true,
            //   "sections": [
            //     {
            //       "section_id": "knowledge_base",
            //       "section_name": "水务知识库",
            //       "section_title": "拥有水务行业相关的通用知识,包括:法律法规、设计规范、给排水相关知识等等"
            //     },
            //     {
            //       "section_id": "office_assistant",
            //       "section_name": "办公助手",
            //       "section_title": "办公助手能够辅助写会议通知、请假条、工作总结、PPT等工作。"
            //     },
            //     {
            //       "section_id": "customer_service",
            //       "section_name": "客户服务",
            //       "section_title": "针对水务企业对外客户服务相关的内容,包括:抄表、开账、收费,以及热线相关"
            //     },
            //     {
            //       "section_id": "network_operation",
            //       "section_name": "管网运行",
            //       "section_title": "针对水务企业管网运行与维护管理、水质管理等相关"
            //     },
            //     {
            //       "section_id": "waterworks_operation",
            //       "section_name": "水厂运行",
            //       "section_title": "针对水务企业水厂运日常运营管理服务,包括:水厂工艺流程、水泵运行调度"
            //     }
            //   ]
            // }
            return payload;
         },
         // 覆盖 amis env
         // 参考 https://aisuda.bce.baidu.com/amis/zh-CN/docs/start/getting-started#sdk
         jumpTo: (to, action) => {
            return;
            if (to === 'goBack') {
               return router.go(-1);
            }
      this.amisInstance = instance;
   },
            to = normalizeLink(to, this.location);
   methods: {
      updateProps() {
         this.amisInstance?.updateProps({
            data: {
               ...this.locals,
            },
            context: this.context,
            ...this.props,
         });
            if (action?.actionType === 'url') {
               action.blank === false ? router.push(to) : window.open(to);
               return;
            }
            // 主要是支持 nav 中的跳转
            if (action && to && action.target) {
               window.open(to, action.target);
               return;
            }
            if (/^https?:\/\//.test(to)) {
               window.location.replace(to);
            } else {
               router.push(to);
            }
         },
         updateLocation: (location, replace) => {
            // 禁止跳转
            return;
            if (location === 'goBack') {
               return router.go(-1);
            }
            location = normalizeLink(location, this.location);
            replace ? router.replace(location) : router.replace(location);
         },
         ...props.env,
      },
   },
      () => {
         emit('ready', {
            instance,
         });
      }
   );
   amisInstance.value = instance;
});
   unmounted() {
      this.unmounted = true;
      this.amisInstance?.unmount();
   },
};
onUnmounted(() => {
   unmounted.value = true;
   amisInstance.value?.unmount();
});
</script>