feat(lading): custom loading svg (#2456)

* feat(loading): support customize svg

* feat(loading): unit test customize svg

* docs(loading): simple customize svg demo

* docs(loading): supplement composition api documents

* docs(loading): make the load SVG stop rotating

* docs(loading): prompt users to prevent XSS attacks

* docs(loading): i18n

* docs(loading): fix md code wrong

* docs(loading): fix md code wrong
This commit is contained in:
YiJie 2021-07-12 18:13:19 +08:00 committed by GitHub
parent 4676be7c47
commit 1cdf7400db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 283 additions and 11 deletions

View File

@ -177,6 +177,23 @@ describe('Loading', () => {
expect(wrapper.find('.loading-custom-class').exists()).toBeTruthy() expect(wrapper.find('.loading-custom-class').exists()).toBeTruthy()
}) })
test('customSvg directive', async () => {
const wrapper = mount({
directives: {
loading: vLoading,
},
template: `<div v-loading="loading" :element-loading-svg="svg"></div>`,
data() {
return {
loading: true,
svg: `<path class="custom-path" d="M 30 15"/>`,
}
},
})
await nextTick()
expect(wrapper.find('.custom-path').attributes().d).toEqual('M 30 15')
})
test('create service', async () => { test('create service', async () => {
loadingInstance = Loading() loadingInstance = Loading()
expect(document.querySelector('.el-loading-mask')).toBeTruthy() expect(document.querySelector('.el-loading-mask')).toBeTruthy()

View File

@ -78,7 +78,8 @@ export function createLoadingComponent({
render() { render() {
const spinner = h('svg', { const spinner = h('svg', {
class: 'circular', class: 'circular',
viewBox: '25 25 50 50', viewBox: this.svgViewBox ? this.svgViewBox : '25 25 50 50',
...(this.svg ? { innerHTML: this.svg } : {}),
}, [ }, [
h('circle', { class: 'path', cx: '50', cy: '50', r: '20', fill: 'none' }), h('circle', { class: 'path', cx: '50', cy: '50', r: '20', fill: 'none' }),
]) ])

View File

@ -3,11 +3,15 @@ import Loading from './index'
const createInstance = (el, binding) => { const createInstance = (el, binding) => {
const textExr = el.getAttribute('element-loading-text') const textExr = el.getAttribute('element-loading-text')
const spinnerExr = el.getAttribute('element-loading-spinner') const spinnerExr = el.getAttribute('element-loading-spinner')
const svgExr = el.getAttribute('element-loading-svg')
const svgViewBoxExr = el.getAttribute('element-loading-svg-view-box')
const backgroundExr = el.getAttribute('element-loading-background') const backgroundExr = el.getAttribute('element-loading-background')
const customClassExr = el.getAttribute('element-loading-custom-class') const customClassExr = el.getAttribute('element-loading-custom-class')
const vm = binding.instance const vm = binding.instance
el.instance = Loading({ el.instance = Loading({
text: vm && vm[textExr] || textExr, text: vm && vm[textExr] || textExr,
svg: vm && vm[svgExr] || svgExr,
svgViewBox: vm && vm[svgViewBoxExr] || svgViewBoxExr,
spinner: vm && vm[spinnerExr] || spinnerExr, spinner: vm && vm[spinnerExr] || spinnerExr,
background: vm && vm[backgroundExr] || backgroundExr, background: vm && vm[backgroundExr] || backgroundExr,
customClass: vm && vm[customClassExr] || customClassExr, customClass: vm && vm[customClassExr] || customClassExr,

View File

@ -9,6 +9,8 @@ import isServer from '@element-plus/utils/isServer'
const defaults: ILoadingOptions = { const defaults: ILoadingOptions = {
parent: null, parent: null,
background: '', background: '',
svg: null,
svgViewBox: null,
spinner: false, spinner: false,
text: null, text: null,
fullscreen: true, fullscreen: true,

View File

@ -3,6 +3,8 @@ import type { Ref, VNode } from 'vue'
export type ILoadingOptions = { export type ILoadingOptions = {
parent?: ILoadingParentElement parent?: ILoadingParentElement
background?: string background?: string
svg?: string
svgViewBox?: string
spinner?: boolean | string spinner?: boolean | string
text?: string text?: string
fullscreen?: boolean fullscreen?: boolean

View File

@ -1,3 +1,7 @@
.demo-loading .#{$namespace}-table { .demo-loading .#{$namespace}-table {
border: none; border: none;
} }
.demo-loading .custom-loading-svg .el-loading-mask > .el-loading-spinner > .circular {
animation: none;
}

View File

@ -101,7 +101,7 @@ export default defineComponent({
You can customize loading text, loading spinner and background color. You can customize loading text, loading spinner and background color.
:::demo Add attribute `element-loading-text` to the element on which `v-loading` is bound, and its value will be displayed under the spinner. Similarly, `element-loading-spinner` and `element-loading-background` are for customizing loading spinner class name and background color. :::demo Add attribute `element-loading-text` to the element on which `v-loading` is bound, and its value will be displayed under the spinner. Similarly, the `element-loading-spinner`, `element-loading-background`, and `element-loading-svg` attributes are used to set the icon class name, background color value, and loading icon, respectively.
```html ```html
<template> <template>
<el-table <el-table
@ -126,6 +126,28 @@ You can customize loading text, loading spinner and background color.
label="Address"> label="Address">
</el-table-column> </el-table-column>
</el-table> </el-table>
<el-table
v-loading="loading"
:element-loading-svg="svg"
class="custom-loading-svg"
element-loading-svg-view-box="-10, -10, 50, 50"
:data="tableData"
style="width: 100%">
<el-table-column
prop="date"
label="Date"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="Name"
width="180">
</el-table-column>
<el-table-column
prop="address"
label="Address">
</el-table-column>
</el-table>
</template> </template>
<script> <script>
@ -145,7 +167,17 @@ You can customize loading text, loading spinner and background color.
name: 'John Smith', name: 'John Smith',
address: 'No.1518, Jinshajiang Road, Putuo District' address: 'No.1518, Jinshajiang Road, Putuo District'
}], }],
loading: true loading: true,
svg: `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`,
}; };
} }
}; };
@ -176,6 +208,16 @@ export default defineComponent({
}, },
], ],
loading: true, loading: true,
svg: `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`,
}); });
return { return {
...toRefs(state), ...toRefs(state),
@ -188,6 +230,10 @@ export default defineComponent({
``` ```
::: :::
:::warning
Although the `element-loading-svg` attribute supports incoming HTML fragments, it is very dangerous to dynamically render arbitrary HTML on the website, because it is easy to cause [XSS attack](https://en.wikipedia.org/wiki/Cross-site_scripting). Please make sure that the content of `element-loading-svg` is trustworthy. **Never** assign user-submitted content to the `element-loading-svg` attribute.
:::
### Full screen loading ### Full screen loading
Show a full screen animation while loading data. Show a full screen animation while loading data.

View File

@ -101,7 +101,7 @@ export default defineComponent({
Puede personalizar el texto de carga, spinner de carga y color de fondo. Puede personalizar el texto de carga, spinner de carga y color de fondo.
:::demo Agregue el atributo `element-loading-text` al elemento en el que `v-loading` está vinculado, y su valor se mostrará debajo del spinner. Del mismo modo, `element-loading-spinner` y `element-loading-background` son para personalizar el nombre de la clase del spinner y el color de fondo. :::demo Agregue el atributo `element-loading-text` al elemento en el que `v-loading` está vinculado, y su valor se mostrará debajo del spinner. De manera similar, los atributos `element-loading-spinner`,` element-loading-background` y `element-loading-svg` se utilizan para establecer el nombre de la clase del icono, el valor del color de fondo y el icono de carga, respectivamente.
```html ```html
<template> <template>
<el-table <el-table
@ -126,6 +126,28 @@ Puede personalizar el texto de carga, spinner de carga y color de fondo.
label="Dirección"> label="Dirección">
</el-table-column> </el-table-column>
</el-table> </el-table>
<el-table
v-loading="loading"
:element-loading-svg="svg"
class="custom-loading-svg"
element-loading-svg-view-box="-10, -10, 50, 50"
:data="tableData"
style="width: 100%">
<el-table-column
prop="date"
label="Fecha"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="Nombre"
width="180">
</el-table-column>
<el-table-column
prop="address"
label="Dirección">
</el-table-column>
</el-table>
</template> </template>
<script> <script>
@ -145,7 +167,17 @@ Puede personalizar el texto de carga, spinner de carga y color de fondo.
name: 'John Smith', name: 'John Smith',
address: 'No.1518, Jinshajiang Road, Putuo District' address: 'No.1518, Jinshajiang Road, Putuo District'
}], }],
loading: true loading: true,
svg: `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`,
}; };
} }
}; };
@ -176,6 +208,16 @@ export default defineComponent({
}, },
], ],
loading: true, loading: true,
svg: `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`,
}); });
return { return {
...toRefs(state), ...toRefs(state),
@ -188,6 +230,10 @@ export default defineComponent({
``` ```
::: :::
:::warning
Aunque el atributo `element-loading-svg` admite fragmentos HTML entrantes, es muy peligroso representar dinámicamente HTML arbitrario en el sitio web porque es fácil causar [ataque XSS](https://en.wikipedia.org/wiki/Cross-site_scripting). Asegúrese de que el contenido de `element-loading-svg` sea confiable. ** Nunca ** asigne contenido enviado por el usuario al atributo` element-loading-svg`.
:::
### Cargando a pantalla completa ### Cargando a pantalla completa
Muestra una animación de pantalla completa mientras se cargan los datos Muestra una animación de pantalla completa mientras se cargan los datos

View File

@ -101,7 +101,7 @@ export default defineComponent({
Vous pouvez personnaliser le texte, le spinner et la couleur de fond. Vous pouvez personnaliser le texte, le spinner et la couleur de fond.
:::demo Ajoutez l'attribut `element-loading-text` à l'élement sur lequel `v-loading` est attaché et sa valeur sera affichée sous le spinner. De la même façon, `element-loading-spinner` et `element-loading-background` permettent de personnaliser le spinner et la couleur de fond. :::demo Ajoutez l'attribut `element-loading-text` à l'élement sur lequel `v-loading` est attaché et sa valeur sera affichée sous le spinner. De même, les attributs `element-loading-spinner`, `element-loading-background` et `element-loading-svg` sont utilisés pour définir respectivement le nom de la classe d'icône, la valeur de la couleur d'arrière-plan et l'icône de chargement.
```html ```html
<template> <template>
<el-table <el-table
@ -126,6 +126,28 @@ Vous pouvez personnaliser le texte, le spinner et la couleur de fond.
label="Adresse"> label="Adresse">
</el-table-column> </el-table-column>
</el-table> </el-table>
<el-table
v-loading="loading"
:element-loading-svg="svg"
class="custom-loading-svg"
element-loading-svg-view-box="-10, -10, 50, 50"
:data="tableData"
style="width: 100%">
<el-table-column
prop="date"
label="Date"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="Nom"
width="180">
</el-table-column>
<el-table-column
prop="address"
label="Adresse">
</el-table-column>
</el-table>
</template> </template>
<script> <script>
@ -145,7 +167,17 @@ Vous pouvez personnaliser le texte, le spinner et la couleur de fond.
name: 'John Smith', name: 'John Smith',
address: 'No.1518, Jinshajiang Road, Putuo District' address: 'No.1518, Jinshajiang Road, Putuo District'
}], }],
loading: true loading: true,
svg: `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`,
}; };
} }
}; };
@ -176,6 +208,16 @@ export default defineComponent({
}, },
], ],
loading: true, loading: true,
svg: `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`,
}); });
return { return {
...toRefs(state), ...toRefs(state),
@ -188,6 +230,10 @@ export default defineComponent({
``` ```
::: :::
:::warning
Bien que l'attribut `element-loading-svg` prenne en charge les fragments HTML entrants, il est très dangereux de rendre dynamiquement du HTML arbitraire sur le site Web car il est facile de provoquer une [attaque XSS](https://en.wikipedia.org/wiki/Cross-site_scripting). Veuillez vous assurer que le contenu de `element-loading-svg` est digne de confiance. **Ne jamais** affecter le contenu soumis par l'utilisateur à l'attribut `element-loading-svg`.
:::
### Chargement plein écran ### Chargement plein écran
Affichez une animation en plein écran quand vous charger des données. Affichez une animation en plein écran quand vous charger des données.

View File

@ -101,7 +101,7 @@ export default defineComponent({
ローディングテキスト、ローディングスピナー、背景色をカスタマイズすることができます。 ローディングテキスト、ローディングスピナー、背景色をカスタマイズすることができます。
:::demo `v-loading` がバインドされている要素に `element-loading-text` 属性を追加すると、その値がスピナの下に表示されるようになります。同様に、`element-loading-spinner` と `element-loading-background` は、ローディングスピナのクラス名と背景色をカスタマイズするためのものです。 :::demo `v-loading` がバインドされている要素に `element-loading-text` 属性を追加すると、その値がスピナの下に表示されるようになります。同様に、 `element-loading-spinner`、` element-loading-background`、および `element-loading-svg`属性は、それぞれアイコンクラス名、背景色の値、および読み込みアイコンを設定するために使用されます。
```html ```html
<template> <template>
<el-table <el-table
@ -126,8 +126,36 @@ export default defineComponent({
label="Address"> label="Address">
</el-table-column> </el-table-column>
</el-table> </el-table>
<el-table
v-loading="loading"
:element-loading-svg="svg"
class="custom-loading-svg"
element-loading-svg-view-box="-10, -10, 50, 50"
:data="tableData"
style="width: 100%">
<el-table-column
prop="date"
label="Date"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="Name"
width="180">
</el-table-column>
<el-table-column
prop="address"
label="Address">
</el-table-column>
</el-table>
</template> </template>
<style>
.custom-loading-svg .el-loading-mask > .el-loading-spinner > .circular {
animation: none;
}
</style>
<script> <script>
export default { export default {
data() { data() {
@ -145,7 +173,17 @@ export default defineComponent({
name: 'John Smith', name: 'John Smith',
address: 'No.1518, Jinshajiang Road, Putuo District' address: 'No.1518, Jinshajiang Road, Putuo District'
}], }],
loading: true loading: true,
svg: `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`,
}; };
} }
}; };
@ -176,6 +214,16 @@ export default defineComponent({
}, },
], ],
loading: true, loading: true,
svg: `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`,
}); });
return { return {
...toRefs(state), ...toRefs(state),
@ -188,6 +236,10 @@ export default defineComponent({
``` ```
::: :::
:::warning
`element-loading-svg`属性は受信HTMLフラグメントをサポートしますが、[XSS攻撃](https://en.wikipedia.org/wiki/Cross-site_scripting)を引き起こしやすいため、Webサイトで任意のHTMLを動的にレンダリングすることは非常に危険です。`element-loading-svg`のコンテンツが信頼できるものであることを確認してください。**ユーザーが送信したコンテンツを` element-loading-svg`属性に割り当てないでください。
:::
### 全画面読み込み ### 全画面読み込み
データの読み込み中に全画面アニメーションを表示します。 データの読み込み中に全画面アニメーションを表示します。

View File

@ -100,7 +100,7 @@
可自定义加载文案、图标和背景色。 可自定义加载文案、图标和背景色。
:::demo 在绑定了`v-loading`指令的元素上添加`element-loading-text`属性,其值会被渲染为加载文案,并显示在加载图标的下方。类似地,`element-loading-spinner`和`element-loading-background`属性分别用来设定图标类名和背景色值 :::demo 在绑定了`v-loading`指令的元素上添加`element-loading-text`属性,其值会被渲染为加载文案,并显示在加载图标的下方。类似地,`element-loading-spinner`、`element-loading-background`和`element-loading-svg`属性分别用来设定图标类名、背景色值、加载图标
```html ```html
<template> <template>
<el-table <el-table
@ -109,6 +109,28 @@
element-loading-spinner="el-icon-loading" element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.8)" element-loading-background="rgba(0, 0, 0, 0.8)"
:data="tableData" :data="tableData"
style="width: 100%;margin-bottom: 10px">
<el-table-column
prop="date"
label="日期"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="姓名"
width="180">
</el-table-column>
<el-table-column
prop="address"
label="地址">
</el-table-column>
</el-table>
<el-table
v-loading="loading"
:element-loading-svg="svg"
class="custom-loading-svg"
element-loading-svg-view-box="-10, -10, 50, 50"
:data="tableData"
style="width: 100%"> style="width: 100%">
<el-table-column <el-table-column
prop="date" prop="date"
@ -127,6 +149,12 @@
</el-table> </el-table>
</template> </template>
<style>
.custom-loading-svg .el-loading-mask > .el-loading-spinner > .circular {
animation: none;
}
</style>
<script> <script>
export default { export default {
data() { data() {
@ -144,7 +172,17 @@
name: '王小虎', name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄' address: '上海市普陀区金沙江路 1518 弄'
}], }],
loading: true loading: true,
svg: `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`,
}; };
} }
}; };
@ -175,6 +213,16 @@
}, },
], ],
loading: true, loading: true,
svg: `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`,
}); });
return { return {
...toRefs(state), ...toRefs(state),
@ -187,6 +235,10 @@
``` ```
::: :::
:::warning
`element-loading-svg` 属性虽然支持传入 HTML 片段,但是在网站上动态渲染任意 HTML 是非常危险的,因为容易导致 [XSS 攻击](https://en.wikipedia.org/wiki/Cross-site_scripting)。请确保 `element-loading-svg` 的内容是可信的,**永远不要**将用户提交的内容赋值给 `element-loading-svg` 属性。
:::
### 整页加载 ### 整页加载
页面数据加载时显示。 页面数据加载时显示。