version: v4.7.1

This commit is contained in:
XiaoDaiGua-Ray 2024-03-17 15:25:49 +08:00
parent 70eee28aa8
commit 3bf297d81c
15 changed files with 596 additions and 319 deletions

View File

@ -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
做了一些核心依赖的升级操作。

View File

@ -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",

92
pnpm-lock.yaml generated
View File

@ -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:

View File

@ -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} />

View File

@ -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;
}

View File

@ -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(() => {

View File

@ -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, svgpng, 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: [],
}),
},
}

View File

@ -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
*/

View File

@ -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(

View File

@ -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,
},

2
src/types/app.d.ts vendored
View File

@ -1,5 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
export {}
import 'vue-router'
import type { AppRouteMeta } from '@/router/types'

View File

@ -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>

View File

@ -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>

View File

@ -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: {},
}
},
})

View File

@ -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',
},
],
}),