未验证 提交 502d3c78 编写于 作者: N Nicky Chan 提交者: GitHub

Redesign chart toolbar icons and improve UX for download chart JSON data (#309)

-Add new toolbar icons and reimplement functions instead of using Echart built in icons
-Remove show data link option, instead add a collapse menu to select data download from each chart
-Only show toolbar icons when hover over card
上级 8f9339e0
...@@ -39,7 +39,7 @@ export default { ...@@ -39,7 +39,7 @@ export default {
#app { #app {
font-family: 'Merriweather Sans', Helvetica, Arial, sans-serif; font-family: 'Merriweather Sans', Helvetica, Arial, sans-serif;
font-weight: bold; font-weight: regular;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
color: $-content-text-color; color: $-content-text-color;
......
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="76" height="76" viewBox="0 0 76.00 76.00" enable-background="new 0 0 76.00 76.00" xml:space="preserve">
<path fill="#999999" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 25,52L 51,52L 51,57L 25,57L 25,52 Z M 35,16L 41,16L 41,36.5L 49,27L 49,36.5L 38,49L 27,36.5L 27,27L 35,36.5L 35,16 Z "/>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="76" height="76" viewBox="0 0 76.00 76.00" enable-background="new 0 0 76.00 76.00" xml:space="preserve">
<path fill="#999999" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 44.5,19L 58,19L 58,32.75L 53,37.5L 53,28L 37.75,43.25L 34,39.5L 49.5,24L 39.5,24L 44.5,19 Z M 20,27L 42.5,27L 37.5,32L 25,32L 25,52L 45,52L 45,40.5L 50,35.5L 50,57L 20,57L 20,27 Z "/>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="76" height="76" viewBox="0 0 76.00 76.00" enable-background="new 0 0 76.00 76.00" xml:space="preserve">
<path fill="#008c99" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 44.5,19L 58,19L 58,32.75L 53,37.5L 53,28L 37.75,43.25L 34,39.5L 49.5,24L 39.5,24L 44.5,19 Z M 20,27L 42.5,27L 37.5,32L 25,32L 25,52L 45,52L 45,40.5L 50,35.5L 50,57L 20,57L 20,27 Z "/>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="76.0106" height="76.0106" viewBox="0 0 76.01 76.01" enable-background="new 0 0 76.01 76.01" xml:space="preserve">
<path fill="#999999" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 25.3362,20.5864L 25.3348,29.2137C 28.5107,25.8499 33.0116,23.7507 38.0029,23.7507C 47.6232,23.7507 55.422,31.5494 55.422,41.1698C 55.422,45.9799 53.4723,50.3347 50.32,53.4869L 46.401,49.5679C 48.5503,47.4187 49.8796,44.4495 49.8796,41.1699C 49.8796,34.6106 44.5623,29.2932 38.003,29.2932C 34.4855,29.2932 31.3251,30.8224 29.1504,33.2522L 38.0029,33.2531L 33.2529,38.0031L 20.5862,38.0031L 20.5862,25.3364L 25.3362,20.5864 Z "/>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="76" height="76" viewBox="0 0 76.00 76.00" enable-background="new 0 0 76.00 76.00" xml:space="preserve">
<path fill="#999999" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 17.4167,53.8333L 17.4167,49.0833L 22.1667,49.0833L 22.1667,28.5L 17.4167,28.5L 17.4167,23.75L 22.1667,23.75L 22.1667,19L 26.9167,19L 26.9167,23.75L 48.6875,23.75L 53.4375,19L 57,22.5625L 52.25,27.3125L 52.25,49.0833L 57,49.0833L 57,53.8333L 52.25,53.8333L 52.25,58.5833L 47.5,58.5833L 47.5,53.8333L 26.9167,53.8333L 26.9167,58.5833L 22.1667,58.5833L 22.1667,53.8333L 17.4167,53.8333 Z M 30.4792,49.0833L 47.5,49.0833L 47.5,32.0625L 30.4792,49.0833 Z M 26.9167,45.5208L 43.9375,28.5L 26.9167,28.5L 26.9167,45.5208 Z "/>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="76" height="76" viewBox="0 0 76.00 76.00" enable-background="new 0 0 76.00 76.00" xml:space="preserve">
<path fill="#008c99" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 17.4167,53.8333L 17.4167,49.0833L 22.1667,49.0833L 22.1667,28.5L 17.4167,28.5L 17.4167,23.75L 22.1667,23.75L 22.1667,19L 26.9167,19L 26.9167,23.75L 48.6875,23.75L 53.4375,19L 57,22.5625L 52.25,27.3125L 52.25,49.0833L 57,49.0833L 57,53.8333L 52.25,53.8333L 52.25,58.5833L 47.5,58.5833L 47.5,53.8333L 26.9167,53.8333L 26.9167,58.5833L 22.1667,58.5833L 22.1667,53.8333L 17.4167,53.8333 Z M 30.4792,49.0833L 47.5,49.0833L 47.5,32.0625L 30.4792,49.0833 Z M 26.9167,45.5208L 43.9375,28.5L 26.9167,28.5L 26.9167,45.5208 Z "/>
</svg>
<template> <template>
<div class="visual-dl-histogram-charts"> <v-card hover class="visual-dl-page-charts">
<div class="visual-dl-chart-box" ref="visual_dl_chart_box"> <div class="visual-dl-chart-box" ref="visual_dl_chart_box">
</div> </div>
<div> <div class="visual-dl-chart-actions">
<v-btn flat @click="expandArea"> <v-btn color="toolbox_icon" flat icon @click="isExpand = !isExpand" class="chart-toolbox-icons" >
<v-icon size="20">settings_overscan</v-icon> <img v-if="!isExpand" src="../../assets/ic_fullscreen_off.svg"/>
<img v-if="isExpand" src="../../assets/ic_fullscreen_on.svg"/>
</v-btn> </v-btn>
</div> </div>
</div> </v-card>
</template> </template>
<script> <script>
// libs // libs
...@@ -43,6 +44,9 @@ export default { ...@@ -43,6 +44,9 @@ export default {
}, },
running: function(val) { running: function(val) {
val ? this.startInterval() : this.stopInterval(); val ? this.startInterval() : this.stopInterval();
},
isExpand: function(val) {
this.expandArea(val);
} }
}, },
mounted() { mounted() {
...@@ -478,16 +482,16 @@ export default { ...@@ -478,16 +482,16 @@ export default {
}); });
}, },
expandArea() { expandArea(expand) {
let isExpand = this.isExpand;
let pageBoxWidth = document.getElementsByClassName('visual-dl-chart-page')[0].offsetWidth; let pageBoxWidth = document.getElementsByClassName('visual-dl-chart-page')[0].offsetWidth;
if (!isExpand) { let width = pageBoxWidth * 0.96; //4% margin
if (expand) {
let el = this.$refs.visual_dl_chart_box; let el = this.$refs.visual_dl_chart_box;
el.style.width = pageBoxWidth + 'px'; el.style.width = width + 'px';
el.style.height = '600px'; el.style.height = '600px';
this.isExpand = true; this.isExpand = true;
this.myChart.resize({ this.myChart.resize({
width: pageBoxWidth, width: width,
height: 600 height: 600
}); });
} }
...@@ -506,14 +510,36 @@ export default { ...@@ -506,14 +510,36 @@ export default {
}; };
</script> </script>
<style lang="stylus"> <style lang="stylus">
.visual-dl-histogram-charts .visual-dl-page-charts
float left float left
margin 2% 2% 0 0 margin 2% 2% 0 0
background #fff background #fff
padding 10px padding 10px
position relative
.visual-dl-chart-box .visual-dl-chart-box
float left
width 400px width 400px
height 300px height 300px
.visual-dl-chart-actions
opacity 0
transition: opacity .3s ease-out;
position absolute
top 4px
right 10px
img
width 30px
height 30px
position absolute
top 0
bottom 0
margin auto
.chart-toolbox-icons
width 25px
height 25px
margin-left -4px
margin-right -4px
.visual-dl-page-charts:hover
.visual-dl-chart-actions
opacity 1
</style> </style>
<template> <template>
<div class="visual-dl-image"> <v-card hover class="visual-dl-image">
<h3 class="visual-dl-image-title">{{tagInfo.tag.displayName}} <h3 class="visual-dl-image-title">{{tagInfo.tag.displayName}}
<span class="visual-dl-image-run-icon">{{tagInfo.run}}</span> <span class="visual-dl-image-run-icon">{{tagInfo.run}}</span>
</h3> </h3>
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
dark></v-slider> dark></v-slider>
<img :width="imageWidth" :height="imageHeight" :src="imgData.imgSrc" /> <img :width="imageWidth" :height="imageHeight" :src="imgData.imgSrc" />
</div> </v-card>
</template> </template>
<script> <script>
import {getPluginImagesImages} from '../../service'; import {getPluginImagesImages} from '../../service';
......
...@@ -9,7 +9,8 @@ Vue.config.productionTip = false ...@@ -9,7 +9,8 @@ Vue.config.productionTip = false
Vue.use(Vuetify, { Vue.use(Vuetify, {
theme: { theme: {
primary: '#008c99', primary: '#008c99',
accent: '#008c99' accent: '#008c99',
toolbox_icon: '#999999'
} }
}) })
......
...@@ -50,7 +50,6 @@ export default { ...@@ -50,7 +50,6 @@ export default {
smoothing: 0.6, smoothing: 0.6,
horizontal: 'step', horizontal: 'step',
sortingMethod: 'default', sortingMethod: 'default',
downloadLink: false,
outlier: false, outlier: false,
runs: [], runs: [],
running: true running: true
......
<template> <template>
<div class="visual-dl-page-charts"> <v-card hover class="visual-dl-page-charts">
<div ref="chartBox" class="visual-dl-chart-box" :style="computedStyle"> <div ref="chartBox" class="visual-dl-chart-box" :style="computedStyle">
</div> </div>
<div class="visual-dl-chart-actions"> <div class="visual-dl-chart-actions">
<v-btn flat @click="expandArea" class="chart-expand"> <v-btn color="toolbox_icon" flat icon @click="isSelectZoomEnable = !isSelectZoomEnable" class="chart-toolbox-icons">
<v-icon size="20">settings_overscan</v-icon> <img v-if="!isSelectZoomEnable" src="../../assets/ic_zoom_select_off.svg"/>
<img v-if="isSelectZoomEnable" src="../../assets/ic_zoom_select_on.svg"/>
</v-btn> </v-btn>
<v-select <v-btn color="toolbox_icon" flat icon @click="restoreChart" class="chart-toolbox-icons">
v-if="downloadLink && tagInfo.tagList.length > 0" <img src="../../assets/ic_undo.svg"/>
class="download-selector"
:items="tagInfo.tagList"
v-model="runItemForDownload"
item-text="run"
item-value="run"
/>
<v-btn flat
v-if="downloadLink && tagInfo.tagList.length > 0"
class="download-button"
@click="handleDownLoad">
<v-icon size="20">file_download</v-icon>
</v-btn> </v-btn>
<v-btn color="toolbox_icon" flat icon @click="isExpand = !isExpand" class="chart-toolbox-icons" >
<img v-if="!isExpand" src="../../assets/ic_fullscreen_off.svg"/>
<img v-if="isExpand" src="../../assets/ic_fullscreen_on.svg"/>
</v-btn>
<v-btn color="toolbox_icon" flat icon @click="saveChartAsImage" class="chart-toolbox-icons" >
<img src="../../assets/ic_download.svg"/>
</v-btn>
<v-menu v-if="tagInfo.tagList.length > 0">
<v-btn color="toolbox_icon" slot="activator" flat icon class="chart-toolbox-icons">
<v-icon >more_vert</v-icon>
</v-btn>
<v-list dense>
<v-list-tile>
<v-list-tile-content>
<v-list-tile-title>Download data in JSON</v-list-tile-title>
</v-list-tile-content>
<v-list-tile-action>
<v-icon>expand_more</v-icon>
</v-list-tile-action>
</v-list-tile>
<v-list-tile v-for="subItem in tagInfo.tagList" :key="subItem.run" @click="handleDownLoad(subItem.run)">
<v-list-tile-content>
<v-list-tile-title>&nbsp;&nbsp;&nbsp;{{ subItem.run }}</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-list>
</v-menu>
</div> </div>
</div> </v-card>
</template> </template>
<script> <script>
...@@ -45,7 +63,7 @@ const maxQuantile = 0.95; ...@@ -45,7 +63,7 @@ const maxQuantile = 0.95;
const intervalTime = 15; const intervalTime = 15;
export default { export default {
props: ['tagInfo', 'groupNameReg', 'smoothing', 'horizontal', 'sortingMethod', 'downloadLink', 'outlier', 'runs', props: ['tagInfo', 'groupNameReg', 'smoothing', 'horizontal', 'sortingMethod', 'outlier', 'runs',
'running', 'runsItems'], 'running', 'runsItems'],
computed: { computed: {
computedStyle() { computedStyle() {
...@@ -57,16 +75,12 @@ export default { ...@@ -57,16 +75,12 @@ export default {
return { return {
width: 400, width: 400,
height: 300, height: 300,
// choose run type for download file
runItemForDownload: {},
isExpand: false, isExpand: false,
isSelectZoomEnable: true,
originData: [] originData: []
}; };
}, },
watch: { watch: {
runsItems: function(val) {
this.initRunItemForDownload();
},
originData: function(val) { originData: function(val) {
this.setChartData(); this.setChartData();
this.setChartsOutlier(); this.setChartsOutlier();
...@@ -86,13 +100,17 @@ export default { ...@@ -86,13 +100,17 @@ export default {
this.myChart.clear(); this.myChart.clear();
this.setChartsOptions(val); this.setChartsOptions(val);
this.getOriginChartData(val); this.getOriginChartData(val);
},
isExpand: function(val) {
this.expandArea(val);
},
isSelectZoomEnable: function(val) {
this.toggleSelectZoom(val);
} }
// runs: function(val) {
// this.setChartsRuns();
// }
}, },
mounted() { mounted() {
this.initChart(this.tagInfo); this.initChart(this.tagInfo);
this.toggleSelectZoom(true);
if (this.running) { if (this.running) {
this.startInterval(); this.startInterval();
...@@ -107,13 +125,6 @@ export default { ...@@ -107,13 +125,6 @@ export default {
this.stopInterval(); this.stopInterval();
}, },
methods: { methods: {
initRunItemForDownload() {
if (this.tagInfo.tagList.length === 0) {
return;
}
this.runItemForDownload = this.tagInfo.tagList[0].run;
},
// Create a Scalar Chart, initialize it with default settings, then load datas // Create a Scalar Chart, initialize it with default settings, then load datas
initChart(tagInfo) { initChart(tagInfo) {
this.createChart(); this.createChart();
...@@ -186,9 +197,6 @@ export default { ...@@ -186,9 +197,6 @@ export default {
fontWeight: 'normal' fontWeight: 'normal'
} }
}, },
dataZoom: [{
type: 'inside'
}],
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: { axisPointer: {
...@@ -205,12 +213,11 @@ export default { ...@@ -205,12 +213,11 @@ export default {
}, },
toolbox: { toolbox: {
show: true, show: true,
showTitle: true, showTitle: false,
itemSize: 0,
feature: { feature: {
dataZoom: {}, dataZoom: {},
restore: {}, },
saveAsImage: {}
}
}, },
legend: { legend: {
data: legendOptions, data: legendOptions,
...@@ -220,7 +227,7 @@ export default { ...@@ -220,7 +227,7 @@ export default {
left: '12%', left: '12%',
top: '25%', top: '25%',
right: '10%', right: '10%',
bottom: '8%' bottom: '12%'
}, },
xAxis: { xAxis: {
type: 'value', type: 'value',
...@@ -303,10 +310,10 @@ export default { ...@@ -303,10 +310,10 @@ export default {
getChartOptions() { getChartOptions() {
return this.myChart.getOption() || {}; return this.myChart.getOption() || {};
}, },
handleDownLoad() { handleDownLoad(runItemForDownload) {
let options = this.getChartOptions(); let options = this.getChartOptions();
let series = options.series || []; let series = options.series || [];
let seriesItem = series.find(item => item.name === this.runItemForDownload) || {}; let seriesItem = series.find(item => item.name === runItemForDownload) || {};
let fileName = this.tagInfo.tag.replace(/\//g, '-'); let fileName = this.tagInfo.tag.replace(/\//g, '-');
generateJsonAndDownload(seriesItem.data, fileName); generateJsonAndDownload(seriesItem.data, fileName);
}, },
...@@ -441,15 +448,15 @@ export default { ...@@ -441,15 +448,15 @@ export default {
this.myChart.setOption(horizontalToxAxisOptions[this.horizontal]); this.myChart.setOption(horizontalToxAxisOptions[this.horizontal]);
}, },
expandArea() { expandArea(expand) {
let pageBoxWidth = document.getElementsByClassName('visual-dl-chart-page-box')[0].offsetWidth; let pageBoxWidth = document.getElementsByClassName('visual-dl-chart-page-box')[0].offsetWidth;
if (!this.isExpand) { let width = pageBoxWidth * 0.96; //4% margin
if (expand) {
let el = this.$refs.chartBox; let el = this.$refs.chartBox;
el.style.width = pageBoxWidth + 'px'; el.style.width = width + 'px';
el.style.height = '600px'; el.style.height = '600px';
this.isExpand = true;
this.myChart.resize({ this.myChart.resize({
width: pageBoxWidth, width: width,
height: 600 height: 600
}); });
} }
...@@ -457,7 +464,6 @@ export default { ...@@ -457,7 +464,6 @@ export default {
let el = this.$refs.chartBox; let el = this.$refs.chartBox;
el.style.width = '400px'; el.style.width = '400px';
el.style.height = '300px'; el.style.height = '300px';
this.isExpand = false;
this.myChart.resize({ this.myChart.resize({
width: 400, width: 400,
height: 300 height: 300
...@@ -465,6 +471,37 @@ export default { ...@@ -465,6 +471,37 @@ export default {
} }
}, },
toggleSelectZoom(enable) {
let instance = this;
setTimeout(function() {
instance.myChart.dispatchAction({
type: 'takeGlobalCursor',
key: 'dataZoomSelect',
dataZoomSelectActive: enable
});
}, 0)
},
restoreChart() {
this.myChart.dispatchAction({
type: 'restore'
})
},
saveChartAsImage() {
let dataUrl = this.myChart.getDataURL({
pixelRatio: 1,
backgroundColor: '#fff'
});
let fileName = this.tagInfo.tag.replace(/\//g, '-');
let link = document.createElement("a");
link.download = fileName;
link.href = dataUrl;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
},
getFormatterPoints(data) { getFormatterPoints(data) {
let originData = this.originData; let originData = this.originData;
let tagList = this.tagInfo.tagList; let tagList = this.tagInfo.tagList;
...@@ -579,17 +616,30 @@ export default { ...@@ -579,17 +616,30 @@ export default {
.visual-dl-page-charts .visual-dl-page-charts
float left float left
margin 2% 2% 0 0 margin 2% 2% 0 0
background: #fff; padding 10px
padding: 10px; position relative
.visual-dl-chart-actions .visual-dl-chart-actions
vertical-align: middle opacity 0
.chart-expand transition: opacity .3s ease-out;
float left position absolute
.download-selector top 4px
float left right 10px
width 100px img
margin-top -10px width 30px
.download-button height 30px
float left position absolute
top 0
bottom 0
margin auto
.chart-toolbox-icons
width 25px
height 25px
margin-left -4px
margin-right -4px
.visual-dl-page-charts:hover
.visual-dl-chart-actions
opacity 1
</style> </style>
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
:smoothing="config.smoothing" :smoothing="config.smoothing"
:horizontal="config.horizontal" :horizontal="config.horizontal"
:sortingMethod="config.sortingMethod" :sortingMethod="config.sortingMethod"
:downloadLink="config.downloadLink"
:outlier="config.outlier" :outlier="config.outlier"
:runs="config.runs" :runs="config.runs"
:running="config.running" :running="config.running"
......
...@@ -29,10 +29,9 @@ ...@@ -29,10 +29,9 @@
v-model="config.sortingMethod" v-model="config.sortingMethod"
label="Tooltip sorting method" label="Tooltip sorting method"
class="visual-dl-page-config-selector" class="visual-dl-page-config-selector"
dark dark dense
></v-select> ></v-select>
<v-checkbox class="visual-dl-page-config-checkbox" label="Show data download links" v-model="config.downloadLink" dark></v-checkbox>
<v-checkbox class="visual-dl-page-config-checkbox" label="Ignore outliers in chart scaling" v-model="config.outlier" dark></v-checkbox> <v-checkbox class="visual-dl-page-config-checkbox" label="Ignore outliers in chart scaling" v-model="config.outlier" dark></v-checkbox>
<label class="visual-dl-page-checkbox-group-label">Runs</label> <label class="visual-dl-page-checkbox-group-label">Runs</label>
......
...@@ -92,10 +92,10 @@ const config = { ...@@ -92,10 +92,10 @@ const config = {
use: getLoaders(isDev, 'stylus') use: getLoaders(isDev, 'stylus')
}, },
{ {
test: /\.(gif|png|jpe?g)$/i, test: /\.(gif|png|jpe?g|svg)$/i,
loader: 'file-loader', loader: 'file-loader',
options: { options: {
name: 'images/[name].[hash].[ext]' name: 'assets/[name].[hash].[ext]'
} }
}, },
{ {
...@@ -108,7 +108,7 @@ const config = { ...@@ -108,7 +108,7 @@ const config = {
} }
}, },
{ {
test: /\.(ttf|eot|svg)$/, test: /\.(ttf|eot)$/,
loader: 'file-loader', loader: 'file-loader',
options: { options: {
name: 'fonts/[name].[hash].[ext]' name: 'fonts/[name].[hash].[ext]'
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册