diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8fb39b90..97f5d1d6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,32 @@
 # CHANGE LOG
 
+## 4.7.1
+
+## Feats
+
+- 更新 `vite` 版本至 `5.1.6`
+- `vite-plugin-cdn2` 相关
+  - 更新 `vite-plugin-cdn2` 版本至 `1.1.0`
+  - 更新 `vite.plugin.config.ts` 关于 `cdn2` 配置
+  - 新增 `cdnResolve` 方法,自定义 `resolve`
+- 更新主流浏览器版本号升级
+- `RChart` 组件相关
+  - 新增 `intersectionObserver` 配置项,用于配置是否使用 `IntersectionObserver` 监听图表渲染
+    > 但是该配置项不支持动态修改,只能在初始化时配置
+  - 优化组件的注释,并且补充了一些注释
+  - 新增 `intersectionObserver` 配置项,手动指定 `IntersectionObserver` 需要监听的元素
+  - `observer` 更名为 `autoResizeObserverTarget`
+  - 补充 `chart` 示例页面
+- 优化亮色主题下锁屏样式
+
+## Fixes
+
+- 修复 `vite-plugin-cdn2` 插件构建 `echarts` 失败问题,具体查看该 [issue](https://github.com/nonzzz/vite-plugin-cdn/issues/42)
+- `RChart` 组件相关
+  - 修复卸载组件不能完全清理 `inst` 问题
+  - 修复组件不能正常触发初始化动画问题
+  - 修正 `isDispose` 方法返回值含义,现在返回 `true` 代表已经卸载,`false` 代表未卸载
+
 ## 4.7.0
 
 做了一些核心依赖的升级操作。
diff --git a/package.json b/package.json
index 9786c04a..a55dfdf8 100755
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "ray-template",
   "private": false,
-  "version": "4.7.0",
+  "version": "4.7.1",
   "type": "module",
   "engines": {
     "node": "^18.0.0 || >=20.0.0",
@@ -92,9 +92,9 @@
     "typescript": "^5.2.2",
     "unplugin-auto-import": "^0.17.5",
     "unplugin-vue-components": "^0.26.0",
-    "vite": "^5.1.5",
+    "vite": "^5.1.6",
     "vite-bundle-analyzer": "0.8.1",
-    "vite-plugin-cdn2": "0.15.4",
+    "vite-plugin-cdn2": "1.1.0",
     "vite-plugin-compression": "^0.5.1",
     "vite-plugin-ejs": "^1.7.0",
     "vite-plugin-eslint": "1.8.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f8178276..64b25c62 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -108,10 +108,10 @@ devDependencies:
     version: 6.5.0(eslint@8.56.0)(typescript@5.2.2)
   '@vitejs/plugin-vue':
     specifier: ^5.0.4
-    version: 5.0.4(vite@5.1.5)(vue@3.4.21)
+    version: 5.0.4(vite@5.1.6)(vue@3.4.21)
   '@vitejs/plugin-vue-jsx':
     specifier: ^3.1.0
-    version: 3.1.0(vite@5.1.5)(vue@3.4.21)
+    version: 3.1.0(vite@5.1.6)(vue@3.4.21)
   '@vue-hooks-plus/resolvers':
     specifier: 1.2.4
     version: 1.2.4(vue-hooks-plus@1.8.8)
@@ -182,35 +182,35 @@ devDependencies:
     specifier: ^0.26.0
     version: 0.26.0(vue@3.4.21)
   vite:
-    specifier: ^5.1.5
-    version: 5.1.5(@types/node@20.4.7)(sass@1.71.1)
+    specifier: ^5.1.6
+    version: 5.1.6(@types/node@20.4.7)(sass@1.71.1)
   vite-bundle-analyzer:
     specifier: 0.8.1
     version: 0.8.1
   vite-plugin-cdn2:
-    specifier: 0.15.4
-    version: 0.15.4
+    specifier: 1.1.0
+    version: 1.1.0
   vite-plugin-compression:
     specifier: ^0.5.1
-    version: 0.5.1(vite@5.1.5)
+    version: 0.5.1(vite@5.1.6)
   vite-plugin-ejs:
     specifier: ^1.7.0
-    version: 1.7.0(vite@5.1.5)
+    version: 1.7.0(vite@5.1.6)
   vite-plugin-eslint:
     specifier: 1.8.1
-    version: 1.8.1(eslint@8.56.0)(vite@5.1.5)
+    version: 1.8.1(eslint@8.56.0)(vite@5.1.6)
   vite-plugin-imp:
     specifier: ^2.4.0
-    version: 2.4.0(vite@5.1.5)
+    version: 2.4.0(vite@5.1.6)
   vite-plugin-inspect:
     specifier: ^0.8.3
-    version: 0.8.3(vite@5.1.5)
+    version: 0.8.3(vite@5.1.6)
   vite-plugin-mock-dev-server:
     specifier: 1.4.7
-    version: 1.4.7(vite@5.1.5)
+    version: 1.4.7(vite@5.1.6)
   vite-plugin-svg-icons:
     specifier: ^2.0.1
-    version: 2.0.1(vite@5.1.5)
+    version: 2.0.1(vite@5.1.6)
   vite-svg-loader:
     specifier: ^4.0.0
     version: 4.0.0
@@ -1494,14 +1494,10 @@ packages:
   /@types/eslint@8.44.2:
     resolution: {integrity: sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg==}
     dependencies:
-      '@types/estree': 1.0.1
+      '@types/estree': 1.0.5
       '@types/json-schema': 7.0.14
     dev: true
 
-  /@types/estree@1.0.1:
-    resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==}
-    dev: true
-
   /@types/estree@1.0.5:
     resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
     dev: true
@@ -1848,7 +1844,7 @@ packages:
     resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
     dev: true
 
-  /@vitejs/plugin-vue-jsx@3.1.0(vite@5.1.5)(vue@3.4.21):
+  /@vitejs/plugin-vue-jsx@3.1.0(vite@5.1.6)(vue@3.4.21):
     resolution: {integrity: sha512-w9M6F3LSEU5kszVb9An2/MmXNxocAnUb3WhRr8bHlimhDrXNt6n6D2nJQR3UXpGlZHh/EsgouOHCsM8V3Ln+WA==}
     engines: {node: ^14.18.0 || >=16.0.0}
     peerDependencies:
@@ -1858,20 +1854,20 @@ packages:
       '@babel/core': 7.23.9
       '@babel/plugin-transform-typescript': 7.23.6(@babel/core@7.23.9)
       '@vue/babel-plugin-jsx': 1.1.5(@babel/core@7.23.9)
-      vite: 5.1.5(@types/node@20.4.7)(sass@1.71.1)
+      vite: 5.1.6(@types/node@20.4.7)(sass@1.71.1)
       vue: 3.4.21(typescript@5.2.2)
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@vitejs/plugin-vue@5.0.4(vite@5.1.5)(vue@3.4.21):
+  /@vitejs/plugin-vue@5.0.4(vite@5.1.6)(vue@3.4.21):
     resolution: {integrity: sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==}
     engines: {node: ^18.0.0 || >=20.0.0}
     peerDependencies:
       vite: ^5.0.0
       vue: ^3.2.25
     dependencies:
-      vite: 5.1.5(@types/node@20.4.7)(sass@1.71.1)
+      vite: 5.1.6(@types/node@20.4.7)(sass@1.71.1)
       vue: 3.4.21(typescript@5.2.2)
     dev: true
 
@@ -2533,7 +2529,7 @@ packages:
       postcss: ^8.1.0
     dependencies:
       browserslist: 4.21.10
-      caniuse-lite: 1.0.30001534
+      caniuse-lite: 1.0.30001593
       fraction.js: 4.3.4
       normalize-range: 0.1.2
       picocolors: 1.0.0
@@ -2658,7 +2654,7 @@ packages:
     engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
     hasBin: true
     dependencies:
-      caniuse-lite: 1.0.30001534
+      caniuse-lite: 1.0.30001593
       electron-to-chromium: 1.4.522
       node-releases: 2.0.13
       update-browserslist-db: 1.0.11(browserslist@4.21.10)
@@ -2669,7 +2665,7 @@ packages:
     engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
     hasBin: true
     dependencies:
-      caniuse-lite: 1.0.30001571
+      caniuse-lite: 1.0.30001593
       electron-to-chromium: 1.4.616
       node-releases: 2.0.14
       update-browserslist-db: 1.0.13(browserslist@4.22.2)
@@ -2761,12 +2757,8 @@ packages:
     engines: {node: '>=10'}
     dev: true
 
-  /caniuse-lite@1.0.30001534:
-    resolution: {integrity: sha512-vlPVrhsCS7XaSh2VvWluIQEzVhefrUQcEsQWSS5A5V+dM07uv1qHeQzAOTGIMy9i3e9bH15+muvI/UHojVgS/Q==}
-    dev: true
-
-  /caniuse-lite@1.0.30001571:
-    resolution: {integrity: sha512-tYq/6MoXhdezDLFZuCO/TKboTzuQ/xR5cFdgXPfDtM7/kchBO3b4VWghE/OAi/DV7tTdhmLjZiZBZi1fA/GheQ==}
+  /caniuse-lite@1.0.30001593:
+    resolution: {integrity: sha512-UWM1zlo3cZfkpBysd7AS+z+v007q9G1+fLTUU42rQnY6t2axoogPW/xol6T7juU5EUoOhML4WgBIdG+9yYqAjQ==}
     dev: true
 
   /canvas@2.11.2:
@@ -7576,21 +7568,21 @@ packages:
       source-map: 0.7.4
     dev: true
 
-  /vite-plugin-cdn2@0.15.4:
-    resolution: {integrity: sha512-7eTUerun6Nyvx60dzPsEXWOwzAdDs7s81p/7YHBqYNL1wfG5r2KH0qqiw9/gaOJjnI34LvnaFncULgqpcV9thw==}
+  /vite-plugin-cdn2@1.1.0:
+    resolution: {integrity: sha512-mqz9frTLpwT7XVuJppb/OmsEaJOxcY5Xt/+VOeewQAJTsl91TZe1ySv+YyoQbVn+BQbamNHZbUteIuoQzRFdpQ==}
     dependencies:
       '@babel/core': 7.23.9
       '@rollup/pluginutils': 5.1.0
       '@xn-sakina/rml-wasm': 2.3.0
       debug: 4.3.4
-      magic-string: 0.30.7
+      magic-string: 0.30.8
       rs-module-lexer: 2.3.0
     transitivePeerDependencies:
       - rollup
       - supports-color
     dev: true
 
-  /vite-plugin-compression@0.5.1(vite@5.1.5):
+  /vite-plugin-compression@0.5.1(vite@5.1.6):
     resolution: {integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==}
     peerDependencies:
       vite: '>=2.0.0'
@@ -7598,21 +7590,21 @@ packages:
       chalk: 4.1.2
       debug: 4.3.4
       fs-extra: 10.1.0
-      vite: 5.1.5(@types/node@20.4.7)(sass@1.71.1)
+      vite: 5.1.6(@types/node@20.4.7)(sass@1.71.1)
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /vite-plugin-ejs@1.7.0(vite@5.1.5):
+  /vite-plugin-ejs@1.7.0(vite@5.1.6):
     resolution: {integrity: sha512-JNP3zQDC4mSbfoJ3G73s5mmZITD8NGjUmLkq4swxyahy/W0xuokK9U9IJGXw7KCggq6UucT6hJ0p+tQrNtqTZw==}
     peerDependencies:
       vite: '>=5.0.0'
     dependencies:
       ejs: 3.1.9
-      vite: 5.1.5(@types/node@20.4.7)(sass@1.71.1)
+      vite: 5.1.6(@types/node@20.4.7)(sass@1.71.1)
     dev: true
 
-  /vite-plugin-eslint@1.8.1(eslint@8.56.0)(vite@5.1.5):
+  /vite-plugin-eslint@1.8.1(eslint@8.56.0)(vite@5.1.6):
     resolution: {integrity: sha512-PqdMf3Y2fLO9FsNPmMX+//2BF5SF8nEWspZdgl4kSt7UvHDRHVVfHvxsD7ULYzZrJDGRxR81Nq7TOFgwMnUang==}
     peerDependencies:
       eslint: '>=7'
@@ -7622,10 +7614,10 @@ packages:
       '@types/eslint': 8.44.2
       eslint: 8.56.0
       rollup: 2.79.1
-      vite: 5.1.5(@types/node@20.4.7)(sass@1.71.1)
+      vite: 5.1.6(@types/node@20.4.7)(sass@1.71.1)
     dev: true
 
-  /vite-plugin-imp@2.4.0(vite@5.1.5):
+  /vite-plugin-imp@2.4.0(vite@5.1.6):
     resolution: {integrity: sha512-L/6/nvOw+MyNh4UxAlCZHsmKd5MitmHamqqAWB15sbUgVIEz/OQ8jpKr6kkQU0eA/AIe8fkCVbQBlP81ajrqWg==}
     peerDependencies:
       vite: '>= 2.0.0-beta.5'
@@ -7637,12 +7629,12 @@ packages:
       chalk: 4.1.2
       param-case: 3.0.4
       pascal-case: 3.1.2
-      vite: 5.1.5(@types/node@20.4.7)(sass@1.71.1)
+      vite: 5.1.6(@types/node@20.4.7)(sass@1.71.1)
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /vite-plugin-inspect@0.8.3(vite@5.1.5):
+  /vite-plugin-inspect@0.8.3(vite@5.1.6):
     resolution: {integrity: sha512-SBVzOIdP/kwe6hjkt7LSW4D0+REqqe58AumcnCfRNw4Kt3mbS9pEBkch+nupu2PBxv2tQi69EQHQ1ZA1vgB/Og==}
     engines: {node: '>=14'}
     peerDependencies:
@@ -7661,13 +7653,13 @@ packages:
       perfect-debounce: 1.0.0
       picocolors: 1.0.0
       sirv: 2.0.4
-      vite: 5.1.5(@types/node@20.4.7)(sass@1.71.1)
+      vite: 5.1.6(@types/node@20.4.7)(sass@1.71.1)
     transitivePeerDependencies:
       - rollup
       - supports-color
     dev: true
 
-  /vite-plugin-mock-dev-server@1.4.7(vite@5.1.5):
+  /vite-plugin-mock-dev-server@1.4.7(vite@5.1.6):
     resolution: {integrity: sha512-vGNW423fkmMibf0BfYL89n2n4tNKDt51d6Ee14gC1LlLiJAp6jabJBPsjWgU+uMgtp68+1uBb5F1qTlqdAhnoQ==}
     engines: {node: ^16 || ^18 || >= 20}
     peerDependencies:
@@ -7689,7 +7681,7 @@ packages:
       mime-types: 2.1.35
       path-to-regexp: 6.2.1
       picocolors: 1.0.0
-      vite: 5.1.5(@types/node@20.4.7)(sass@1.71.1)
+      vite: 5.1.6(@types/node@20.4.7)(sass@1.71.1)
       ws: 8.16.0
     transitivePeerDependencies:
       - bufferutil
@@ -7698,7 +7690,7 @@ packages:
       - utf-8-validate
     dev: true
 
-  /vite-plugin-svg-icons@2.0.1(vite@5.1.5):
+  /vite-plugin-svg-icons@2.0.1(vite@5.1.6):
     resolution: {integrity: sha512-6ktD+DhV6Rz3VtedYvBKKVA2eXF+sAQVaKkKLDSqGUfnhqXl3bj5PPkVTl3VexfTuZy66PmINi8Q6eFnVfRUmA==}
     peerDependencies:
       vite: '>=2.0.0'
@@ -7711,7 +7703,7 @@ packages:
       pathe: 0.2.0
       svg-baker: 1.7.0
       svgo: 2.8.0
-      vite: 5.1.5(@types/node@20.4.7)(sass@1.71.1)
+      vite: 5.1.6(@types/node@20.4.7)(sass@1.71.1)
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -7723,8 +7715,8 @@ packages:
       svgo: 3.0.2
     dev: true
 
-  /vite@5.1.5(@types/node@20.4.7)(sass@1.71.1):
-    resolution: {integrity: sha512-BdN1xh0Of/oQafhU+FvopafUp6WaYenLU/NFoL5WyJL++GxkNfieKzBhM24H3HVsPQrlAqB7iJYTHabzaRed5Q==}
+  /vite@5.1.6(@types/node@20.4.7)(sass@1.71.1):
+    resolution: {integrity: sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==}
     engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
     peerDependencies:
diff --git a/src/app-components/app/AppLockScreen/components/UnlockScreen/index.tsx b/src/app-components/app/AppLockScreen/components/UnlockScreen/index.tsx
index 67a739ab..3df58253 100644
--- a/src/app-components/app/AppLockScreen/components/UnlockScreen/index.tsx
+++ b/src/app-components/app/AppLockScreen/components/UnlockScreen/index.tsx
@@ -105,16 +105,19 @@ export default defineComponent({
     return (
       <div class="app-lock-screen__unlock">
         <div class="app-lock-screen__unlock__content">
-          <div
-            class={[
-              'app-lock-screen__unlock__content-bg',
-              isTabletOrSmaller
-                ? 'app-lock-screen__unlock__content-bg--smaller'
-                : '',
-            ]}
-          >
-            <div class="left">{hmSplit[0]}</div>
-            <div class="right">{hmSplit[1]}</div>
+          <div class="app-lock-screen__unlock__content-wrapper">
+            <div
+              class={[
+                'app-lock-screen__unlock__content-bg__wrapper',
+                'app-lock-screen__unlock__content-bg',
+                isTabletOrSmaller
+                  ? 'app-lock-screen__unlock__content-bg--smaller'
+                  : '',
+              ]}
+            >
+              <div class="left">{hmSplit[0]}</div>
+              <div class="right">{hmSplit[1]}</div>
+            </div>
           </div>
           <div class="app-lock-screen__unlock__content-avatar">
             <AppAvatar vertical align="center" avatarSize={52} />
diff --git a/src/app-components/app/AppLockScreen/index.scss b/src/app-components/app/AppLockScreen/index.scss
index e85663bf..1a11e99a 100644
--- a/src/app-components/app/AppLockScreen/index.scss
+++ b/src/app-components/app/AppLockScreen/index.scss
@@ -1,10 +1,10 @@
 .app-lock-screen__content {
   & .app-lock-screen__input {
-    & button[class*="n-button"] {
+    & button[class*='n-button'] {
       width: 100%;
     }
 
-    & form[class*="n-form"] {
+    & form[class*='n-form'] {
       margin: 24px 0px;
     }
   }
@@ -12,45 +12,54 @@
   & .app-lock-screen__unlock {
     .app-lock-screen__unlock__content {
       position: relative;
-      width: 100%;
-      height: 100%;
       @include flexCenter;
       flex-direction: column;
 
-      & .app-lock-screen__unlock__content-bg {
-        position: absolute;
-        width: 100%;
-        height: 100%;
-        @include flexCenter;
-        font-size: 220px;
-        gap: 80px;
-        z-index: 0;
+      & .app-lock-screen__unlock__content-wrapper {
+        position: fixed;
+        inset: 0px;
 
-        &.app-lock-screen__unlock__content-bg--smaller {
-          & .left,
-          & .right {
-            padding: 0px;
-            font-size: 90px;
-            padding: 24px;
-            border-radius: 4px;
-          }
+        & .app-lock-screen__unlock__content-bg__wrapper {
+          width: 100%;
+          height: 100%;
+          background-color: rgb(16, 16, 20);
         }
 
-        & .left,
-        & .right {
+        & .app-lock-screen__unlock__content-bg {
+          position: absolute;
+          width: 100%;
+          height: 100%;
           @include flexCenter;
-          border-radius: 30px;
-          background-color: #141313;
-          font-weight: 700;
-          padding: 80px;
-          filter: blur(4px);
+          font-size: 320px;
+          gap: 80px;
+          z-index: 0;
+
+          &.app-lock-screen__unlock__content-bg--smaller {
+            & .left,
+            & .right {
+              padding: 0px;
+              font-size: 90px;
+              padding: 24px;
+              border-radius: 4px;
+            }
+          }
+
+          & .left,
+          & .right {
+            @include flexCenter;
+            border-radius: 30px;
+            background-color: #141313;
+            font-weight: 700;
+            padding: 80px;
+            filter: blur(4px);
+          }
         }
       }
 
       & .app-lock-screen__unlock__content-avatar {
         margin-top: 5px;
         color: #bababa;
-        font-weight: 500;
+        font-weight: bolder;
         z-index: 1;
       }
 
diff --git a/src/components/RChart/src/index.tsx b/src/components/RChart/src/index.tsx
index 94f7a1f4..9ca46fb2 100644
--- a/src/components/RChart/src/index.tsx
+++ b/src/components/RChart/src/index.tsx
@@ -1,6 +1,6 @@
 import './index.scss'
 
-import * as echarts from 'echarts/core' // `echarts` 核心模块
+import { use, registerTheme, init } from 'echarts/core' // echarts 核心模块
 import {
   TitleComponent,
   TooltipComponent,
@@ -10,7 +10,7 @@ import {
   LegendComponent,
   ToolboxComponent,
   AriaComponent,
-} from 'echarts/components' // 提示框, 标题, 直角坐标系, 数据集, 内置数据转换器等组件(组件后缀都为 `Component`)
+} from 'echarts/components' // 提示框, 标题, 直角坐标系, 数据集, 内置数据转换器等组件(组件后缀都为 Component)
 import {
   BarChart,
   LineChart,
@@ -18,9 +18,9 @@ import {
   CandlestickChart,
   ScatterChart,
   PictorialBarChart,
-} from 'echarts/charts' // 系列类型(后缀都为 `SeriesOption`)
+} from 'echarts/charts' // 系列类型(后缀都为 SeriesOption)
 import { LabelLayout, UniversalTransition } from 'echarts/features' // 标签自动布局, 全局过渡动画等特性
-import { CanvasRenderer } from 'echarts/renderers' // `echarts` 渲染器
+import { CanvasRenderer } from 'echarts/renderers' // echarts 渲染器
 import { NCard } from 'naive-ui'
 
 import props from './props'
@@ -28,28 +28,35 @@ import { throttle } from 'lodash-es'
 import { completeSize, downloadBase64File, call, renderNode } from '@/utils'
 import { setupChartTheme } from './utils'
 import { APP_THEME } from '@/app-config'
-import { useResizeObserver } from '@vueuse/core'
+import { useResizeObserver, useIntersectionObserver } from '@vueuse/core'
 import { RMoreDropdown } from '@/components'
 import { useSettingGetters } from '@/store'
 
 import type { WatchStopHandle } from 'vue'
 import type { AnyFC } from '@/types'
 import type { DebouncedFunc } from 'lodash-es'
-import type { UseResizeObserverReturn } from '@vueuse/core'
+import type {
+  UseResizeObserverReturn,
+  UseIntersectionObserverReturn,
+} from '@vueuse/core'
 import type { ECharts, EChartsCoreOption } from 'echarts/core'
 import type { DropdownProps, DropdownOption } from 'naive-ui'
 
+// setOption 默认配置项
 const defaultChartOptions = {
   notMerge: false,
   lazyUpdate: true,
   silent: false,
   replaceMerge: [],
 }
+// 获取 chart 主题
 const echartThemes = setupChartTheme()
+// download 下载功能 key
+const __CHART_DOWN_LOAD_CHART__ = '__R_CHART_DOWN_LOAD_CHART__'
 
-/** 注册主题 */
+// 注册主题
 echartThemes.forEach((curr) => {
-  echarts.registerTheme(curr.name, curr.theme)
+  registerTheme(curr.name, curr.theme)
 })
 
 /**
@@ -78,28 +85,30 @@ export default defineComponent({
   setup(props, { expose }) {
     const { getAppTheme } = useSettingGetters()
     const rayChartRef = ref<HTMLElement>() // echart 容器实例
-    const rayChartWrapperRef = ref<HTMLElement>()
+    const rayChartWrapperRef = ref<HTMLElement>() // echart 父容器实例
     const echartInstanceRef = ref<ECharts>() // echart 实例
     let resizeThrottleReturn: DebouncedFunc<AnyFC> | null // resize 防抖方法实例
-    let resizeObserverReturn: UseResizeObserverReturn | null
-    const { echartTheme } = APP_THEME
-    let watchCallback: WatchStopHandle | null
+    let resizeObserverReturn: UseResizeObserverReturn | null // resize observer 实例
+    const { echartTheme } = APP_THEME // 当前配置主题
+    let watchCallback: WatchStopHandle | null // watch props 回调
     let echartInst: ECharts | null // 无代理响应式代理缓存 echart inst
     const moreDropDownOptions = computed<DropdownProps['options']>(() => [
       {
         label: '下载图片',
-        key: '__DOWN_LOAD_CHART__',
+        key: __CHART_DOWN_LOAD_CHART__,
         disabled: !(
           echartInstanceRef.value && echartInstanceRef.value.getDom()
         ),
       },
-    ])
+    ]) // 下拉框配置项
     const cssVarsRef = computed(() => {
       return {
         '--ray-chart-width': completeSize(props.width),
         '--ray-chart-height': completeSize(props.height),
       }
     })
+    const targetIsVisible = ref(false) // 目标是否可见
+    let intersectionObserverReturn: UseIntersectionObserverReturn | null // intersectionObserver 实例
 
     /**
      *
@@ -109,7 +118,7 @@ export default defineComponent({
      * 该方法必须在注册图表之前调用
      */
     const registerChartCore = async () => {
-      echarts.use([
+      use([
         TitleComponent,
         TooltipComponent,
         GridComponent,
@@ -119,7 +128,7 @@ export default defineComponent({
         ToolboxComponent,
         AriaComponent,
       ]) // 注册组件
-      echarts.use([
+      use([
         BarChart,
         LineChart,
         PieChart,
@@ -127,11 +136,11 @@ export default defineComponent({
         ScatterChart,
         PictorialBarChart,
       ]) // 注册 chart series type
-      echarts.use([LabelLayout, UniversalTransition]) // 注册布局, 过度效果
-      echarts.use([CanvasRenderer]) // 注册渲染器
+      use([LabelLayout, UniversalTransition]) // 注册布局, 过度效果
+      use([CanvasRenderer]) // 注册渲染器
 
       try {
-        echarts.use(props.use?.filter(Boolean))
+        use(props.use?.filter(Boolean))
       } catch (e) {
         console.error('[RChart register error]: ', e)
       }
@@ -148,6 +157,10 @@ export default defineComponent({
      * 但是,如果未获取到 echartTheme 属性,则会使用默认样式
      */
     const updateChartTheme = () => {
+      if (echartInst?.getDom()) {
+        destroyChart()
+      }
+
       if (props.theme === 'default') {
         props.autoChangeTheme ? renderChart('dark') : renderChart('')
 
@@ -169,10 +182,12 @@ export default defineComponent({
 
     /**
      *
-     * @returns `chart options`
+     * @param ops 待合并 chart options
      *
-     * 合并配置项
-     * 如果有需要特殊全局配置的可以在此继续写...
+     * @description
+     * 合并 chart options。
+     *
+     * 如果启用了 showAria 则会自动合并 aria 配置项。
      */
     const combineChartOptions = (ops: EChartsCoreOption) => {
       let options = unref(ops)
@@ -211,7 +226,7 @@ export default defineComponent({
 
       try {
         /** 注册 chart */
-        echartInst = echarts.init(element, theme, {
+        echartInst = init(element, theme, {
           /** 如果款度为 0, 则以 200px 填充 */
           width: width === 0 ? 200 : void 0,
           /** 如果高度为 0, 则以 200px 填充 */
@@ -219,7 +234,12 @@ export default defineComponent({
         })
         echartInstanceRef.value = echartInst
 
-        /** 设置 options 配置项 */
+        // 渲染成功回调
+        if (onSuccess) {
+          call(onSuccess, echartInst)
+        }
+
+        // 是否强制下一队列渲染图表
         if (props.nextTick) {
           echartInst.setOption({})
 
@@ -229,34 +249,35 @@ export default defineComponent({
         } else {
           options && echartInst?.setOption(options)
         }
-
-        /** 渲染成功回调 */
-        if (onSuccess) {
-          call(onSuccess, echartInst)
-        }
       } catch (e) {
         /** 渲染失败回调 */
         if (onError) {
           call(onError)
         }
 
-        console.error('RChart render error: ', e)
+        console.error('[RChart]: render error: ', e)
       }
     }
 
-    // chart 是否已经销毁
-    const isDispose = () => !!(echartInst && echartInst.getDom())
+    /**
+     *
+     * @description
+     * chart 是否已经销毁。
+     * 如果销毁则返回 true, 否则返回 false。
+     */
+    const isDispose = () => !(echartInst && echartInst.getDom())
 
     /**
      *
-     * 销毁 `chart` 实例, 释放资源
+     * 销毁 chart 实例, 释放资源
      */
     const destroyChart = () => {
-      if (isDispose()) {
+      if (!isDispose()) {
         echartInst!.clear()
         echartInst!.dispose()
 
         echartInstanceRef.value = void 0
+        echartInst = null
       }
     }
 
@@ -276,7 +297,7 @@ export default defineComponent({
      * 当前仅实现下载图片功能
      */
     const dropdownSelect = (key: string | number, option: DropdownOption) => {
-      if (key === '__DOWN_LOAD_CHART__' && isDispose()) {
+      if (key === __CHART_DOWN_LOAD_CHART__ && !isDispose()) {
         const { filename, ...args } = props.downloadOptions
 
         downloadBase64File(
@@ -293,35 +314,64 @@ export default defineComponent({
     }
 
     const mount = () => {
+      // 注册事件
+      if (props.autoResize) {
+        if (!resizeThrottleReturn) {
+          resizeThrottleReturn = throttle(resizeChart, props.throttleWait)
+        }
+
+        /**
+         *
+         * 监听内容区域尺寸变化更新 chart。
+         * 如果没有传入 autoResizeObserverTarget 属性,则默认监听容器尺寸变化。
+         */
+        if (!resizeObserverReturn) {
+          resizeObserverReturn = useResizeObserver(
+            props.autoResizeObserverTarget || rayChartWrapperRef,
+            resizeThrottleReturn as AnyFC,
+          )
+        }
+      }
+
       // 避免重复渲染
       if (echartInst?.getDom()) {
-        console.warn(
-          '[RChart mount]: There is a chart instance already initialized on the dom. Execution was interrupted.',
-        )
-
         return
       }
 
+      // 如果目标不可见并且启用了 intersectionObserver 则不渲染
+      if (!targetIsVisible.value && props.intersectionObserver) {
+        return
+      }
+
+      // 渲染 chart
       updateChartTheme()
 
-      /** 注册事件 */
-      if (props.autoResize) {
-        resizeThrottleReturn = throttle(resizeChart, props.throttleWait)
-        /** 监听内容区域尺寸变化更新 chart */
-        resizeObserverReturn = useResizeObserver(
-          props.observer || rayChartWrapperRef,
-          resizeThrottleReturn,
-        )
-      }
+      // 初始化完成后移除 intersectionObserver 监听
+      intersectionObserverReturn?.stop()
+    }
+
+    if (props.intersectionObserver) {
+      intersectionObserverReturn = useIntersectionObserver(
+        props.intersectionObserverTarget || rayChartWrapperRef,
+        ([entry]) => {
+          targetIsVisible.value = entry.isIntersecting
+        },
+        props.intersectionOptions,
+      )
     }
 
     const unmount = () => {
-      /** 卸载 echarts */
+      // 卸载 echarts
       destroyChart()
-      /** 注销防抖 */
+      // 注销防抖
       resizeThrottleReturn?.cancel()
-      /** 注销 observer 监听 */
-      resizeObserverReturn?.stop?.()
+      // 注销 observer 监听
+      resizeObserverReturn?.stop()
+      intersectionObserverReturn?.stop()
+
+      intersectionObserverReturn = null
+      resizeThrottleReturn = null
+      resizeObserverReturn = null
     }
 
     /** 监听全局主题变化, 然后重新渲染对应主题 echarts */
@@ -359,13 +409,14 @@ export default defineComponent({
         watchCallback = watch(
           () => props.options,
           (ndata) => {
-            /** 重新组合 options */
+            // 重新组合 options
             const options = combineChartOptions(ndata)
             const setOpt = Object.assign(
+              {},
               props.setChartOptions,
               defaultChartOptions,
             )
-            /** 如果 options 发生变动更新 echarts */
+            // 如果 options 发生变动更新 echarts
             echartInst?.setOption(options, setOpt)
           },
           {
@@ -377,9 +428,15 @@ export default defineComponent({
         watchCallback?.()
       }
 
+      // 监听 loading 变化
       props.loading
         ? echartInst?.showLoading(props.loadingOptions)
         : echartInst?.hideLoading()
+
+      // 当前图表容器是否处于可见状态,如果可见则渲染图表
+      if (targetIsVisible.value) {
+        mount()
+      }
     })
 
     expose({
@@ -390,10 +447,11 @@ export default defineComponent({
     })
 
     onBeforeMount(async () => {
-      /** 注册 echarts 组件与渲染器 */
+      // 注册 echarts 组件与渲染器
       await registerChartCore()
     })
     onMounted(() => {
+      // 初始化渲染
       mount()
     })
     onBeforeUnmount(() => {
diff --git a/src/components/RChart/src/props.ts b/src/components/RChart/src/props.ts
index 2a8a95dd..55faa362 100644
--- a/src/components/RChart/src/props.ts
+++ b/src/components/RChart/src/props.ts
@@ -1,11 +1,14 @@
-import type * as echarts from 'echarts/core' // `echarts` 核心模块
+import type * as echarts from 'echarts/core' // echarts 核心模块
 import type { PropType, VNode } from 'vue'
 import type { MaybeArray } from '@/types'
 import type { ECharts, SetOptionOpts } from 'echarts/core'
-import type { MaybeComputedElementRef, MaybeElement } from '@vueuse/core'
+import type {
+  MaybeComputedElementRef,
+  MaybeElement,
+  UseIntersectionObserverOptions,
+} from '@vueuse/core'
 import type {
   LoadingOptions,
-  AutoResize,
   ChartTheme,
   EChartsExtensionInstallRegisters,
   RChartPresetType,
@@ -16,195 +19,340 @@ import type { CardProps, DropdownProps, DropdownOption } from 'naive-ui'
 import { loadingOptions } from './utils'
 
 const props = {
-  bordered: {
-    /**
-     *
-     * 仅在 preset 为 card 时生效
-     *
-     * 设置边框
-     */
+  /**
+   *
+   * @description
+   * 是否开启 IntersectionObserver 监听,用于监听图表是否在可视区域内再进行渲染。
+   * 默认监听图表容器是否在可视区域内,也可以配置 intersectionObserverTarget 属性监听指定元素。
+   *
+   * 该方法需要浏览器支持 IntersectionObserver API。
+   *
+   * @default true
+   */
+  intersectionObserver: {
     type: Boolean,
     default: true,
   },
+  /**
+   *
+   * @description
+   * 指定 IntersectionObserver 监听的目标元素。
+   *
+   * 该属性需要开启 intersectionObserver 才能生效。
+   *
+   * @default null
+   */
+  intersectionObserverTarget: {
+    type: Object as PropType<MaybeComputedElementRef<MaybeElement>>,
+    default: null,
+  },
+  /**
+   *
+   * @description
+   * IntersectionObserver 配置项。
+   *
+   * 该属性需要开启 intersectionObserver 才能生效。
+   *
+   * @see https://www.vueusejs.com/core/useIntersectionObserver/
+   *
+   * @default {threshold:0.1}
+   */
+  intersectionOptions: {
+    type: Object as PropType<UseIntersectionObserverOptions>,
+    default: {
+      threshold: 0.1,
+    },
+  },
+  /**
+   *
+   * @description
+   * 仅在 preset 为 card 时生效。
+   *
+   * @default true
+   */
+  bordered: {
+    type: Boolean,
+    default: true,
+  },
+  /**
+   *
+   * @description
+   * 仅在 preset 为 card 时生效。
+   *
+   * type: 导出的格式,可选 png, jpg, svg。注意: png, jpg 只有在 canvas 渲染器的时候可使用,svg 只有在使用 svg 渲染器的时候可用。
+   * pixelRatio: 导出的图片分辨率比例,默认为 1。
+   * backgroundColor: 导出的图片背景色,默认使用 option 里的 backgroundColor。
+   * excludeComponents: 忽略组件的列表,例如要忽略 toolbox 就是 ['toolbox']。
+   *
+   * @default {}
+   */
   downloadOptions: {
-    /**
-     *
-     * 仅在 preset 为 card 时生效
-     *
-     * type: 导出的格式,可选 png, jpg, svg。注意:png, jpg 只有在 canvas 渲染器的时候可使用,svg 只有在使用 svg 渲染器的时候可用
-     * pixelRatio: 导出的图片分辨率比例,默认为 1
-     * backgroundColor: 导出的图片背景色,默认使用 option 里的 backgroundColor
-     * excludeComponents: 忽略组件的列表,例如要忽略 toolbox 就是 ['toolbox']
-     */
     type: Object as PropType<RChartDownloadOptions>,
     default: () => ({}),
   },
+  /**
+   *
+   * @description
+   * dropdown 选中回调。
+   *
+   * 仅在 preset 为 card 时生效。
+   *
+   * @default undefined
+   */
   onDropdownSelect: {
-    // 仅在 preset 为 card 时生效
     type: [Function, Array] as PropType<
       MaybeArray<(key: string | number, option: DropdownOption) => void>
     >,
   },
+  /**
+   *
+   * @description
+   * dropdown 列表。
+   *
+   * 仅在 preset 为 card 时生效。
+   *
+   * @default []
+   */
   dropdownOptions: {
-    // 仅在 preset 为 card 时生效
     type: Array as PropType<DropdownProps['options']>,
   },
+  /**
+   *
+   * @description
+   * 是否启用预设样式。
+   *
+   * @default undefined
+   */
   preset: {
-    // 是否启用预设样式
     type: String as PropType<RChartPresetType>,
   },
+  /**
+   *
+   * @description
+   * 设置 content 区域的样式。
+   *
+   * 仅在 preset 为 card 时生效。
+   *
+   * @default undefined
+   */
   contentStyle: {
-    // 仅在 preset 为 card 时生效
     type: [String, Object] as PropType<CardProps['contentStyle']>,
   },
+  /**
+   *
+   * @description
+   * 设置预设样式的标题。
+   *
+   * 仅在 preset 为 card 时生效。
+   *
+   * @default undefined
+   */
   title: {
-    // 仅在 preset 为 card 时生效
     type: [String, Function] as PropType<string | (() => VNode)>,
   },
+  /**
+   *
+   * @description
+   * chart 默认宽度,默认为 100%。
+   *
+   * 但是,如果未获取到实际宽度,那么会以 200px 宽度填充。
+   *
+   * @default 100%
+   */
   width: {
-    /**
-     *
-     * chart 容器初始化宽度
-     *
-     * 如果未能继承宽度, 则会以 200px 宽度填充
-     */
     type: String,
     default: '100%',
   },
+  /**
+   *
+   * @description
+   * chart 默认高度,默认为 100%。
+   *
+   * 但是,如果未获取到实际高度,那么会以 200px 高度填充。
+   *
+   * @default 100%
+   */
   height: {
-    /**
-     *
-     * chart 容器初始化高度
-     *
-     * 如果未能继承高度, 则会以 200px 宽度填充
-     */
     type: String,
     default: '100%',
   },
+  /**
+   *
+   * @description
+   * 是否启用自动调整大小,默认跟随图表容器尺寸变化。
+   *
+   * @default true
+   */
   autoResize: {
-    /**
-     *
-     * `chart` 是否跟随窗口尺寸变化自动变化
-     *
-     * 如果为对象, 则可以指定其变化尺寸, 实现图表大小不等于容器大小的效果
-     * 默认每秒触发一次的频率
-     */
-    type: [Boolean, Object] as PropType<AutoResize>,
+    type: Boolean,
     default: true,
   },
+  /**
+   *
+   * @description
+   * 是否启用 chart 无障碍模式。
+   * 启用该配置项后会覆盖 options 中的 aria。
+   *
+   * @default false
+   */
   showAria: {
-    /**
-     *
-     * 是否开启 `chart` 无障碍访问
-     *
-     * 此选项会覆盖 `options` 中的 `aria` 配置
-     */
     type: Boolean,
     default: false,
   },
+  /**
+   *
+   * @description
+   * chart 图表配置项。
+   *
+   * @default {}
+   */
   options: {
     type: Object as PropType<echarts.EChartsCoreOption>,
     default: () => ({}),
   },
+  /**
+   *
+   * @description
+   * chart 渲染成功回调函数。
+   *
+   * @default null
+   */
   onSuccess: {
-    /**
-     *
-     * 渲染成功回调函数
-     */
     type: [Function, Array] as PropType<MaybeArray<(e: ECharts) => void>>,
     default: null,
   },
+  /**
+   *
+   * @description
+   * chart 渲染失败回调函数。
+   *
+   * @default null
+   */
   onError: {
-    /**
-     *
-     * 渲染失败回调函数
-     */
     type: [Function, Array] as PropType<MaybeArray<() => void>>,
     default: null,
   },
+  /**
+   *
+   * @description
+   * 手动指定 chart 主题配置项。
+   *
+   * @default null
+   */
   theme: {
-    /**
-     *
-     * 手动指定 chart theme
-     */
     type: String as PropType<ChartTheme>,
     default: null,
   },
+  /**
+   *
+   * @description
+   * 是否自动跟随模板主题切换。
+   * 该配置项会覆盖 theme 配置项。
+   *
+   * @default true
+   */
   autoChangeTheme: {
-    /**
-     *
-     * 是否自动跟随模板主题切换
-     * 如果开启此属性, 则会覆盖 `theme` 属性
-     *
-     * 注意: 这个属性重度依赖此模板
-     */
     type: Boolean,
     default: true,
   },
+  /**
+   *
+   * @description
+   * 手动拓展 chart 图的相关组件。
+   *
+   * 该配置项不支持动态调用,及时动态更新了该属性,也不会生效。
+   * 并且,该配置项必须在 RChart 组件初始化时候配置。
+   *
+   * @default []
+   */
   use: {
-    /**
-     *
-     * 拓展 `echarts` 图表
-     * 用于自己手动拓展相关的包
-     *
-     * 注意,该方法不支持动态调用,及时动态更新了该属性,也不会生效
-     */
     type: Array as PropType<EChartsExtensionInstallRegisters[]>,
     default: () => [],
   },
+  /**
+   *
+   * @description
+   * 是否开启 watch 监听 options 配置项。
+   *
+   * @default true
+   */
   watchOptions: {
-    /** 主动监听 options 变化 */
     type: Boolean,
     default: true,
   },
+  /**
+   *
+   * @description
+   * 是否启用 chart 加载动画。
+   *
+   * @default false
+   */
   loading: {
-    /** 加载动画 */
     type: Boolean,
     default: false,
   },
+  /**
+   *
+   * @description
+   * chart 加载动画配置项。
+   *
+   * @default {}
+   */
   loadingOptions: {
-    /** 配置加载动画样式 */
     type: Object as PropType<LoadingOptions>,
     default: () => loadingOptions(),
   },
-  observer: {
-    /**
-     *
-     * 需要被监听尺寸的元素
-     * 需要开启 autoResize 才能生效
-     * 默认以父元素作为监听对象
-     */
+  /**
+   *
+   * @description
+   * 手动设置 autoResize 监听的元素。
+   * 该元素必须是一个有效的 DOM 元素,并且需要开启 autoResize 才能生效。
+   *
+   * 默认以图表容器元素作为监听对象。
+   *
+   * @default null
+   */
+  autoResizeObserverTarget: {
     type: Object as PropType<MaybeComputedElementRef<MaybeElement>>,
     default: null,
   },
+  /**
+   *
+   * @description
+   * 节流等待时间。
+   *
+   * @default 500
+   */
   throttleWait: {
-    /** 节流等待时间 */
     type: Number,
     default: 500,
   },
+  /**
+   *
+   * @description
+   * 是否将渲染放置下一个队列。
+   *
+   * @default true
+   */
   nextTick: {
-    /**
-     *
-     * 是否将渲染放置下一个队列
-     */
     type: Boolean,
     default: true,
   },
+  /**
+   *
+   * @description
+   * 设置 setOptions 方法配置项。
+   *
+   * @default {notMerge:false,lazyUpdate:true,silent:false,replaceMerge:[]}
+   */
   setChartOptions: {
-    /**
-     *
-     * 当 options 配置项更改时候,setOptions 方法配置项
-     *
-     * 默认值
-     * notMerge: false,
-     * lazyUpdate: true,
-     * silent: false,
-     * replaceMerge: [],
-     *
-     * 会自动进行合并配置项
-     */
     type: Object as PropType<SetOptionOpts>,
-    default: () => ({}),
+    default: () => ({
+      notMerge: false,
+      lazyUpdate: true,
+      silent: false,
+      replaceMerge: [],
+    }),
   },
 }
 
diff --git a/src/components/RChart/src/types.ts b/src/components/RChart/src/types.ts
index ad3117c1..ecde5e4e 100644
--- a/src/components/RChart/src/types.ts
+++ b/src/components/RChart/src/types.ts
@@ -36,13 +36,6 @@ export interface LoadingOptions {
   fontFamily: string // 字体系列
 }
 
-export type AutoResize =
-  | boolean
-  | {
-      width: number
-      height: number
-    }
-
 export type ChartTheme =
   | 'macarons-dark'
   | 'macarons'
@@ -53,31 +46,34 @@ export type ChartTheme =
 export interface RChartInst {
   /**
    *
-   * echart 实例
-   * 访问当前 chart 图所有方法与属性
+   * echart 实例。
+   * 访问当前 chart 图所有方法与属性。
    *
    * @default undefined
    */
   echart: Ref<ECharts | undefined>
   /**
    *
-   * 手动卸载当前 chart 图
-   * 注意:不会卸载当前组件,仅仅是卸载 chart
+   * @description
+   * 手动卸载当前 chart 图。
+   * 注意:不会卸载当前组件,仅仅是卸载 chart。
    *
    * @default () => void
    */
   dispose: () => void
   /**
    *
-   * 手动渲染 chart 图
-   * 注意:会根据当前的 options 配置项与 props 配置项重新渲染 chart
+   * @description
+   * 手动渲染 chart 图。
+   * 注意:会根据当前的 options 配置项与 props 配置项重新渲染 chart。
    *
    * @default () => void
    */
   render: () => void
   /**
    *
-   * 判断图表是否已经卸载
+   * @description
+   * 判断图表是否已经卸载。
    *
    * @returns 图表是否已经卸载
    */
diff --git a/src/components/RChart/src/utils.ts b/src/components/RChart/src/utils.ts
index d95ac4ea..a84c852d 100644
--- a/src/components/RChart/src/utils.ts
+++ b/src/components/RChart/src/utils.ts
@@ -16,14 +16,15 @@ import type {
 } from '@/components/RChart/src/types'
 
 /**
+ *
+ * @see https://echarts.apache.org/zh/theme-builder.html
  *
  * @description
- * 自动注册所有主题
+ * 自动注册所有主题。
  *
- * 默认以文件名当作主题名称
+ * 默认以文件名当作主题名称。
  *
- * 主题配置器:https://echarts.apache.org/zh/theme-builder.html
- * 流程:
+ * 主题编辑器使用方法:
  * 1. 配置、选择主题
  * 2. 点击下载主题
  * 3. 选择 json 类型,然后复制
@@ -56,9 +57,16 @@ export const setupChartTheme = () => {
 }
 
 /**
+ *
+ * @param options 加载自定义配置项
  *
  * @description
- * 为了方便使用加载动画, 写了此方法, 虽然没啥用
+ * chart 加载配置项。
+ *
+ * @see https://echarts.apache.org/zh/api.html#echartsInstance.showLoading
+ *
+ * @example
+ * const options = loadingOptions({ ...LoadingOptions })
  */
 export const loadingOptions = (options?: LoadingOptions) =>
   Object.assign(
diff --git a/src/components/RModal/src/props.ts b/src/components/RModal/src/props.ts
index 338e2c3f..a1f48ade 100644
--- a/src/components/RModal/src/props.ts
+++ b/src/components/RModal/src/props.ts
@@ -13,19 +13,21 @@ import { modalProps } from 'naive-ui'
 
 const props = {
   ...modalProps,
+  /**
+   *
+   * @description
+   * 是否记住上一次的位置。
+   *
+   * @default true
+   */
   memo: {
-    /**
-     *
-     * 是否记住上一次的位置
-     *
-     * @default true
-     */
     type: Boolean,
     default: true,
   },
   /**
    *
-   * 是否全屏
+   * @description
+   * 是否全屏。
    *
    * @default false
    */
@@ -33,44 +35,48 @@ const props = {
     type: Boolean,
     default: false,
   },
+  /**
+   *
+   * @description
+   * preset 空时宽度设置。
+   *
+   * @default 600
+   */
   width: {
-    /**
-     *
-     * preset 空时宽度设置
-     *
-     * @default 600
-     */
     type: [String, Number],
     default: 600,
   },
+  /**
+   *
+   * @description
+   * preset 为 card 时宽度设置。
+   *
+   * @default 600
+   */
   cardWidth: {
-    /**
-     *
-     * preset 为 card 时宽度设置
-     *
-     * @default 600
-     */
     type: [String, Number],
     default: 600,
   },
+  /**
+   *
+   * @description
+   * preset 为 dialog 时宽度设置。
+   *
+   * @default 446
+   */
   dialogWidth: {
-    /**
-     *
-     * preset 为 dialog 时宽度设置
-     *
-     * @default 446
-     */
     type: [String, Number],
     default: 446,
   },
+  /**
+   *
+   * @description
+   * 是否启用拖拽。
+   * 当启用拖拽时,可以通过拖拽 header 部分控制模态框。
+   *
+   * @default false
+   */
   dad: {
-    /**
-     *
-     * 是否启用拖拽
-     * 当启用拖拽时,可以通过拖拽 header 部分控制模态框
-     *
-     * @default false
-     */
     type: Boolean,
     default: false,
   },
diff --git a/src/types/app.d.ts b/src/types/app.d.ts
index f680bda4..1cc53bb4 100644
--- a/src/types/app.d.ts
+++ b/src/types/app.d.ts
@@ -1,5 +1,7 @@
 /* eslint-disable @typescript-eslint/no-explicit-any */
 
+export {}
+
 import 'vue-router'
 
 import type { AppRouteMeta } from '@/router/types'
diff --git a/src/utils/vue/renderNode.ts b/src/utils/vue/renderNode.ts
index 0241b925..579456c5 100644
--- a/src/utils/vue/renderNode.ts
+++ b/src/utils/vue/renderNode.ts
@@ -31,8 +31,6 @@ export interface RenderNodeOptions<T extends DefaultElement> {
   defaultElement?: T
 }
 
-export type RenderNodeReturn = ReturnType<typeof renderNode>
-
 /**
  *
  * @param vnode 将 jsx element, slot, h, string 等渲染为 vnode
@@ -41,9 +39,10 @@ export type RenderNodeReturn = ReturnType<typeof renderNode>
  * 可以将常见的类型转换为 vnode
  *
  * @example
- * renderNode('hello world') => () => 'hello world'
- * renderNode(<div>hello world</div>) => () => <div>hello world</div>
- * renderNode(() => <div>hello world</div>) => () => <div>hello world</div>
+ * renderNode('hello world') // () => 'hello world'
+ * renderNode(<div>hello world</div>) // () => <div>hello world</div>
+ * renderNode(() => <div>hello world</div>) // () => <div>hello world</div>
+ * renderNode(null, { defaultElement: () => <span>hello world</span> }) // () => 'hello world'
  */
 export function renderNode<T extends DefaultElement>(
   vnode: RenderVNodeType,
@@ -65,3 +64,5 @@ export function renderNode<T extends DefaultElement>(
     return vnode
   }
 }
+
+export type RenderNodeReturn = ReturnType<typeof renderNode>
diff --git a/src/views/demo/echart/index.tsx b/src/views/demo/echart/index.tsx
index 390cd954..29a49b50 100644
--- a/src/views/demo/echart/index.tsx
+++ b/src/views/demo/echart/index.tsx
@@ -179,14 +179,18 @@ const Echart = defineComponent({
     }
 
     const mountChart = () => {
-      baseChartRef.value?.render()
+      if (!baseChartRef.value?.isDispose()) {
+        baseChartRef.value?.render()
+      } else {
+        window.$message.warning('图表已经渲染')
+      }
     }
 
     const unmountChart = () => {
       baseChartRef.value?.dispose()
     }
 
-    const handleUpdateTitle = () => {
+    const updateChartOptions = () => {
       const createData = () => Math.floor((Math.random() + 1) * 100)
 
       baseLineOptions.value.series[0].data = new Array(7)
@@ -209,7 +213,7 @@ const Echart = defineComponent({
       ...toRefs(state),
       mountChart,
       unmountChart,
-      handleUpdateTitle,
+      updateChartOptions,
     }
   },
   render() {
@@ -236,13 +240,19 @@ const Echart = defineComponent({
             <li>
               <h3>5. 配置 setChartOptions 属性,可以定制化合并模式</h3>
             </li>
+            <li>
+              <h3>
+                6. 默认启用 intersectionObserver
+                属性,只有元素在可见范围才会渲染图表,可以滚动查看效果
+              </h3>
+            </li>
           </ul>
         </NCard>
         <NCard title="预设 card 风格图表">
           <NFlex style={['padding: 18px 0']}>
             <NButton onClick={this.mountChart.bind(this)}>渲染</NButton>
             <NButton onClick={this.unmountChart.bind(this)}>卸载</NButton>
-            <NButton onClick={this.handleUpdateTitle.bind(this)}>
+            <NButton onClick={this.updateChartOptions.bind(this)}>
               更新配置项
             </NButton>
           </NFlex>
diff --git a/vite-helper/index.ts b/vite-helper/index.ts
index c9b608a2..36220f2b 100644
--- a/vite-helper/index.ts
+++ b/vite-helper/index.ts
@@ -1,4 +1,5 @@
 import pkg from '../package.json'
+import { defineResolve } from 'vite-plugin-cdn2/resolve'
 
 import type { DependenciesKey } from './type'
 
@@ -64,3 +65,18 @@ export const getDependenciesVersion = (dependenciesKey: DependenciesKey) => {
 
   return result.replace(/^[^\w\s]+/, '')
 }
+
+export const cdnResolve = defineResolve({
+  name: 'RayTemplateCdnResolve',
+  setup({ extra }) {
+    const baseURL = 'https://cdnjs.cloudflare.com/ajax/libs/'
+    const { version, name, relativeModule } = extra
+    const url = new URL(`${name}/${version}/${relativeModule}`, baseURL)
+
+    return {
+      url: url.href,
+      injectTo: 'head-prepend',
+      attrs: {},
+    }
+  },
+})
diff --git a/vite.plugin.config.ts b/vite.plugin.config.ts
index 2b62ab6f..226d93f5 100644
--- a/vite.plugin.config.ts
+++ b/vite.plugin.config.ts
@@ -26,7 +26,7 @@ import mockDevServerPlugin from 'vite-plugin-mock-dev-server'
 import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
 import unpluginViteComponents from 'unplugin-vue-components/vite'
 import { cdn as viteCDNPlugin } from 'vite-plugin-cdn2'
-import { getDependenciesVersion } from './vite-helper'
+import { getDependenciesVersion, cdnResolve } from './vite-helper'
 
 import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
 
@@ -60,46 +60,47 @@ function onlyBuildOptions(mode: string): PluginOption[] {
   return [
     viteCDNPlugin({
       // modules 顺序 vue, vue-demi 必须保持当前顺序加载,否则会出现加载错误问题
+      resolve: cdnResolve,
       modules: [
         {
           name: 'vue',
           global: 'Vue',
-          resolve: `${resolve('vue')}/vue.global.min.js`,
+          relativeModule: 'vue.global.min.js',
         },
         {
           name: 'vue-demi',
           global: 'VueDemi',
-          resolve: `${resolve('vue-demi')}/index.iife.min.js`,
+          relativeModule: 'index.iife.min.js',
         },
         {
           name: 'naive-ui',
           global: 'naive',
-          resolve: `${resolve('naive-ui')}/index.prod.js`,
+          relativeModule: 'index.prod.js',
         },
         {
           name: 'pinia',
           global: 'Pinia',
-          resolve: `${resolve('pinia')}/pinia.iife.min.js`,
+          relativeModule: 'pinia.iife.min.js',
         },
         {
           name: 'vue-router',
           global: 'VueRouter',
-          resolve: `${resolve('vue-router')}/vue-router.global.min.js`,
+          relativeModule: 'vue-router.global.min.js',
         },
         {
           name: 'vue-i18n',
           global: 'VueI18n',
-          resolve: `${resolve('vue-i18n')}/vue-i18n.global.min.js`,
+          relativeModule: 'vue-i18n.global.min.js',
         },
         {
           name: 'echarts',
           global: 'echarts',
-          resolve: `${resolve('echarts')}/echarts.min.js`,
+          relativeModule: 'echarts.min.js',
         },
         {
           name: 'axios',
           global: 'axios',
-          resolve: `${resolve('axios')}/axios.min.js`,
+          relativeModule: 'axios.min.js',
         },
       ],
     }),