Commit 4b9f3775 authored by 郭铭瑶's avatar 郭铭瑶 🤘

同步翻新组件库

parent 762c3106
---
title: MyAnimate
wrapperClass: my-animate
---
# MyAnimate
基于[animate.css](https://animate.style/)封装的动画组件,对包裹其中的元素实现进入、离开时的动画。
Tag: `m-animate`
```vue demo
<template>
<m-animate enter="fadeInLeft" leave="fadeOutLeft">
<h1 v-show="show">Animate 1</h1>
</m-animate>
<m-animate enter="zoomInDown" leave="zoomOutUp">
<h1 v-show="show">Animate 2</h1>
</m-animate>
<m-animate enter="slideInRight" leave="slideOutRight">
<h1 v-show="show">Animate 3</h1>
</m-animate>
<button @click="show = !show">Click Me!</button>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const show = ref(true)
return {
show,
}
},
}
</script>
```
## Props
| props | description | type | default |
| -------- | ------------------ | ------ | ------- |
| enter | 进入动画名 | string | - |
| leave | 离开动画名 | string | - |
| duration | 动画执行时间(毫秒) | number | 500 |
...@@ -17,10 +17,12 @@ export default defineComponent({ ...@@ -17,10 +17,12 @@ export default defineComponent({
name: 'MyAnimate', name: 'MyAnimate',
displayName: 'm-animate', displayName: 'm-animate',
props: { props: {
/** 进入动画 */
enter: { enter: {
type: String as PropType<string>, type: String as PropType<string>,
required: true, required: true,
}, },
/** 离开动画 */
leave: { leave: {
type: String as PropType<string>, type: String as PropType<string>,
required: true, required: true,
......
---
title: MyCard
wrapperClass: my-card
---
# MyCard
模块包裹组件
Tag: `m-card`
```vue demo
<template>
<m-card title="这是标题"><h1>内容或组件</h1></m-card>
<m-card title="这是标题" mode="2"><h1>内容或组件</h1></m-card>
</template>
```
## Props
| props | description | type | default |
| ----- | ----------- | --------------- | ----------- |
| title | 标题名 | string | - |
| mode | 模式选择 | string / number | 1 |
| enter | 进入动画名 | string | fadeInLeft |
| leave | 离开动画名 | string | fadeOutLeft |
...@@ -4,48 +4,11 @@ ...@@ -4,48 +4,11 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted, PropType, watchEffect } from 'vue' import { defineComponent, onMounted, PropType, watchEffect } from 'vue'
import * as echarts from 'echarts/core' import { use } from 'echarts/core'
import { import { LineChart, BarChart } from 'echarts/charts'
DatasetComponent, use([LineChart, BarChart])
DatasetComponentOption, import useChartGenerate from './useChartGenerate'
TitleComponent, import { BarOption, DatasetComponentOption } from './types'
TitleComponentOption,
TooltipComponent,
TooltipComponentOption,
GridComponent,
GridComponentOption,
LegendComponent,
LegendComponentOption,
} from 'echarts/components'
import {
LineChart,
LineSeriesOption,
BarChart,
BarSeriesOption,
} from 'echarts/charts'
import { SVGRenderer } from 'echarts/renderers'
echarts.use([
DatasetComponent,
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
LineChart,
BarChart,
SVGRenderer,
])
import useChartGenerate from '@/hooks/useChartGenerate.ts'
type ECOption = echarts.ComposeOption<
| DatasetComponentOption
| TitleComponentOption
| TooltipComponentOption
| GridComponentOption
| LegendComponentOption
| LineSeriesOption
| BarSeriesOption
>
export default defineComponent({ export default defineComponent({
name: 'MyBar', name: 'MyBar',
...@@ -56,18 +19,12 @@ export default defineComponent({ ...@@ -56,18 +19,12 @@ export default defineComponent({
default: null, default: null,
}, },
option: { option: {
type: Object as PropType<ECOption>, type: Object as PropType<BarOption>,
default: null,
},
format: {
type: Function as PropType<
(dataset: DatasetComponentOption, option: ECOption) => ECOption
>,
default: null, default: null,
}, },
}, },
setup(props) { setup(props) {
const defaultOption: ECOption = { const defaultOption: BarOption = {
backgroundColor: 'transparent', backgroundColor: 'transparent',
tooltip: { tooltip: {
confine: true, confine: true,
...@@ -108,7 +65,7 @@ export default defineComponent({ ...@@ -108,7 +65,7 @@ export default defineComponent({
}, },
], ],
} }
const defaultSeriesItem: BarSeriesOption = { const defaultSeriesItem = {
type: 'bar', type: 'bar',
barGap: 0, barGap: 0,
emphasis: { emphasis: {
...@@ -117,15 +74,10 @@ export default defineComponent({ ...@@ -117,15 +74,10 @@ export default defineComponent({
} }
const { chartRef, initChart } = useChartGenerate( const { chartRef, initChart } = useChartGenerate(
defaultOption, defaultOption,
defaultSeriesItem, defaultSeriesItem
props.format
) )
onMounted(() => { onMounted(() => initChart(props.dataset, props.option))
initChart(props.dataset, props.option) watchEffect(() => initChart(props.dataset, props.option))
})
watchEffect(() => {
initChart(props.dataset, props.option)
})
return { return {
chartRef, chartRef,
} }
......
This diff is collapsed.
...@@ -3,50 +3,12 @@ ...@@ -3,50 +3,12 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted, PropType, watchEffect } from 'vue' import { defineComponent, PropType, onMounted, watchEffect } from 'vue'
import * as echarts from 'echarts/core' import { use } from 'echarts/core'
import { import { LineChart, BarChart } from 'echarts/charts'
DatasetComponent, use([LineChart, BarChart])
DatasetComponentOption, import useChartGenerate from './useChartGenerate'
TitleComponent, import { LineOption, DatasetComponentOption } from './types'
TitleComponentOption,
TooltipComponent,
TooltipComponentOption,
GridComponent,
GridComponentOption,
LegendComponent,
LegendComponentOption,
DataZoomComponent,
} from 'echarts/components'
import {
LineChart,
LineSeriesOption,
BarChart,
BarSeriesOption,
} from 'echarts/charts'
import { SVGRenderer } from 'echarts/renderers'
echarts.use([
DatasetComponent,
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
DataZoomComponent,
LineChart,
BarChart,
SVGRenderer,
])
import useChartGenerate from '@/hooks/useChartGenerate'
export type ECOption = echarts.ComposeOption<
| DatasetComponentOption
| TitleComponentOption
| TooltipComponentOption
| GridComponentOption
| LegendComponentOption
| LineSeriesOption
| BarSeriesOption
>
export default defineComponent({ export default defineComponent({
name: 'MyLine', name: 'MyLine',
...@@ -57,18 +19,12 @@ export default defineComponent({ ...@@ -57,18 +19,12 @@ export default defineComponent({
default: null, default: null,
}, },
option: { option: {
type: Object as PropType<ECOption>, type: Object as PropType<LineOption>,
default: null,
},
format: {
type: Function as PropType<
(dataset: DatasetComponentOption, option: ECOption) => ECOption
>,
default: null, default: null,
}, },
}, },
setup(props) { setup(props) {
const defaultOption: ECOption = { const defaultOption: LineOption = {
backgroundColor: 'transparent', backgroundColor: 'transparent',
tooltip: { tooltip: {
confine: true, confine: true,
...@@ -104,7 +60,7 @@ export default defineComponent({ ...@@ -104,7 +60,7 @@ export default defineComponent({
}, },
], ],
} }
const defaultSeriesItem: LineSeriesOption = { const defaultSeriesItem = {
type: 'line', type: 'line',
smooth: true, smooth: true,
lineStyle: { lineStyle: {
...@@ -116,15 +72,10 @@ export default defineComponent({ ...@@ -116,15 +72,10 @@ export default defineComponent({
} }
const { chartRef, initChart } = useChartGenerate( const { chartRef, initChart } = useChartGenerate(
defaultOption, defaultOption,
defaultSeriesItem, defaultSeriesItem
props.format
) )
onMounted(async () => { onMounted(() => initChart(props.dataset, props.option))
initChart(props.dataset, props.option) watchEffect(() => initChart(props.dataset, props.option))
})
watchEffect(() => {
initChart(props.dataset, props.option)
})
return { return {
chartRef, chartRef,
} }
......
...@@ -3,41 +3,12 @@ ...@@ -3,41 +3,12 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted, PropType, watchEffect } from 'vue' import { defineComponent, PropType, onMounted, watchEffect } from 'vue'
import * as echarts from 'echarts/core' import { use } from 'echarts/core'
import { import { PieChart } from 'echarts/charts'
DatasetComponent, use([PieChart])
DatasetComponentOption, import useChartGenerate from './useChartGenerate'
TitleComponent, import { PieOption, DatasetComponentOption } from './types'
TitleComponentOption,
TooltipComponent,
TooltipComponentOption,
GridComponent,
GridComponentOption,
LegendComponent,
LegendComponentOption,
} from 'echarts/components'
import { PieChart, PieSeriesOption } from 'echarts/charts'
import { SVGRenderer } from 'echarts/renderers'
echarts.use([
DatasetComponent,
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
PieChart,
SVGRenderer,
])
import useChartGenerate from '@/hooks/useChartGenerate'
type ECOption = echarts.ComposeOption<
| DatasetComponentOption
| TitleComponentOption
| TooltipComponentOption
| GridComponentOption
| LegendComponentOption
| PieSeriesOption
>
export default defineComponent({ export default defineComponent({
name: 'MyPie', name: 'MyPie',
...@@ -48,18 +19,12 @@ export default defineComponent({ ...@@ -48,18 +19,12 @@ export default defineComponent({
default: null, default: null,
}, },
option: { option: {
type: Object as PropType<ECOption>, type: Object as PropType<PieOption>,
default: null,
},
format: {
type: Function as PropType<
(dataset: DatasetComponentOption, option: ECOption) => ECOption
>,
default: null, default: null,
}, },
}, },
setup(props) { setup(props) {
const defaultOption: ECOption = { const defaultOption: PieOption = {
backgroundColor: 'transparent', backgroundColor: 'transparent',
tooltip: { tooltip: {
confine: true, confine: true,
...@@ -73,7 +38,7 @@ export default defineComponent({ ...@@ -73,7 +38,7 @@ export default defineComponent({
containLabel: true, containLabel: true,
}, },
} }
const defaultSeriesItem: PieSeriesOption = { const defaultSeriesItem = {
type: 'pie', type: 'pie',
radius: ['30%', '50%'], radius: ['30%', '50%'],
center: ['50%', '55%'], center: ['50%', '55%'],
...@@ -87,15 +52,10 @@ export default defineComponent({ ...@@ -87,15 +52,10 @@ export default defineComponent({
} }
const { chartRef, initChart } = useChartGenerate( const { chartRef, initChart } = useChartGenerate(
defaultOption, defaultOption,
defaultSeriesItem, defaultSeriesItem
props.format
) )
onMounted(async () => { onMounted(() => initChart(props.dataset, props.option))
initChart(props.dataset, props.option) watchEffect(() => initChart(props.dataset, props.option))
})
watchEffect(() => {
initChart(props.dataset, props.option)
})
return { return {
chartRef, chartRef,
} }
......
...@@ -3,41 +3,12 @@ ...@@ -3,41 +3,12 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted, PropType, watchEffect } from 'vue' import { defineComponent, PropType, onMounted, watchEffect } from 'vue'
import * as echarts from 'echarts/core' import { use } from 'echarts/core'
import { import { RadarChart } from 'echarts/charts'
DatasetComponent, use([RadarChart])
DatasetComponentOption, import useChartGenerate from './useChartGenerate'
TitleComponent, import { RadarOption, DatasetComponentOption } from './types'
TitleComponentOption,
TooltipComponent,
TooltipComponentOption,
GridComponent,
GridComponentOption,
LegendComponent,
LegendComponentOption,
} from 'echarts/components'
import { RadarChart, RadarSeriesOption } from 'echarts/charts'
import { SVGRenderer } from 'echarts/renderers'
echarts.use([
DatasetComponent,
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
RadarChart,
SVGRenderer,
])
import useChartGenerate from '@/hooks/useChartGenerate'
type ECOption = echarts.ComposeOption<
| DatasetComponentOption
| TitleComponentOption
| TooltipComponentOption
| GridComponentOption
| LegendComponentOption
| RadarSeriesOption
>
export default defineComponent({ export default defineComponent({
name: 'MyRadar', name: 'MyRadar',
...@@ -48,18 +19,12 @@ export default defineComponent({ ...@@ -48,18 +19,12 @@ export default defineComponent({
default: null, default: null,
}, },
option: { option: {
type: Object as PropType<ECOption>, type: Object as PropType<RadarOption>,
default: null,
},
format: {
type: Function as PropType<
(dataset: DatasetComponentOption, option: ECOption) => ECOption
>,
default: null, default: null,
}, },
}, },
setup(props) { setup(props) {
const defaultOption: ECOption = { const defaultOption: RadarOption = {
backgroundColor: 'transparent', backgroundColor: 'transparent',
tooltip: { tooltip: {
confine: true, confine: true,
...@@ -74,7 +39,8 @@ export default defineComponent({ ...@@ -74,7 +39,8 @@ export default defineComponent({
}, },
radar: { radar: {
axisName: { axisName: {
show: false, // show: false,
color: 'transparent',
textStyle: { textStyle: {
color: '#fff', color: '#fff',
backgroundColor: 'rgba(255,255,255,.3)', backgroundColor: 'rgba(255,255,255,.3)',
...@@ -108,7 +74,7 @@ export default defineComponent({ ...@@ -108,7 +74,7 @@ export default defineComponent({
radius: '55%', radius: '55%',
}, },
} }
const defaultSeriesItem: RadarSeriesOption = { const defaultSeriesItem = {
type: 'radar', type: 'radar',
symbol: 'none', symbol: 'none',
areaStyle: { areaStyle: {
...@@ -119,11 +85,18 @@ export default defineComponent({ ...@@ -119,11 +85,18 @@ export default defineComponent({
}, },
} }
const { chartRef, initChart } = useChartGenerate( const { chartRef, initChart } = useChartGenerate(
props.option ? Object.assign(defaultOption, props.option) : defaultOption, defaultOption,
defaultSeriesItem, defaultSeriesItem
props.format
) )
onMounted(async () => { onMounted(() => {
// eslint-disable-next-line
;(defaultOption as any).radar.indicator =
props.dataset &&
props.dataset.dimensions &&
props.dataset.dimensions.map((d) => ({
name: (d as any).displayName,
max: (d as any).max,
}))
initChart(props.dataset, props.option) initChart(props.dataset, props.option)
}) })
watchEffect(() => { watchEffect(() => {
......
...@@ -3,41 +3,12 @@ ...@@ -3,41 +3,12 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted, PropType, watchEffect } from 'vue' import { defineComponent, PropType, onMounted, watchEffect } from 'vue'
import * as echarts from 'echarts/core' import { use } from 'echarts/core'
import { import { ScatterChart, EffectScatterChart } from 'echarts/charts'
DatasetComponent, use([ScatterChart, EffectScatterChart])
DatasetComponentOption, import useChartGenerate from './useChartGenerate'
TitleComponent, import { ScatterOption, DatasetComponentOption } from './types'
TitleComponentOption,
TooltipComponent,
TooltipComponentOption,
GridComponent,
GridComponentOption,
LegendComponent,
LegendComponentOption,
} from 'echarts/components'
import { ScatterChart, ScatterSeriesOption } from 'echarts/charts'
import { SVGRenderer } from 'echarts/renderers'
echarts.use([
DatasetComponent,
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
ScatterChart,
SVGRenderer,
])
import useChartGenerate from '@/hooks/useChartGenerate'
type ECOption = echarts.ComposeOption<
| DatasetComponentOption
| TitleComponentOption
| TooltipComponentOption
| GridComponentOption
| LegendComponentOption
| ScatterSeriesOption
>
export default defineComponent({ export default defineComponent({
name: 'MyScatter', name: 'MyScatter',
...@@ -48,18 +19,12 @@ export default defineComponent({ ...@@ -48,18 +19,12 @@ export default defineComponent({
default: null, default: null,
}, },
option: { option: {
type: Object as PropType<ECOption>, type: Object as PropType<ScatterOption>,
default: null,
},
format: {
type: Function as PropType<
(dataset: DatasetComponentOption, option: ECOption) => ECOption
>,
default: null, default: null,
}, },
}, },
setup(props) { setup(props) {
const defaultOption: ECOption = { const defaultOption: ScatterOption = {
backgroundColor: 'transparent', backgroundColor: 'transparent',
tooltip: { tooltip: {
confine: true, confine: true,
...@@ -75,20 +40,15 @@ export default defineComponent({ ...@@ -75,20 +40,15 @@ export default defineComponent({
xAxis: {}, xAxis: {},
yAxis: {}, yAxis: {},
} }
const defaultSeriesItem: ScatterSeriesOption = { const defaultSeriesItem = {
type: 'scatter', type: 'scatter',
} }
const { chartRef, initChart } = useChartGenerate( const { chartRef, initChart } = useChartGenerate(
defaultOption, defaultOption,
defaultSeriesItem, defaultSeriesItem
props.format
) )
onMounted(async () => { onMounted(() => initChart(props.dataset, props.option))
initChart(props.dataset, props.option) watchEffect(() => initChart(props.dataset, props.option))
})
watchEffect(() => {
initChart(props.dataset, props.option)
})
return { return {
chartRef, chartRef,
} }
......
import { ComposeOption } from 'echarts/core'
import {
DatasetComponentOption as DatasetOption,
TitleComponentOption,
TooltipComponentOption,
GridComponentOption,
LegendComponentOption,
DataZoomComponentOption,
} from 'echarts/components'
import {
LineSeriesOption,
BarSeriesOption,
PieSeriesOption,
RadarSeriesOption,
ScatterSeriesOption,
} from 'echarts/charts'
/** Dataset选项参数 */
export type DatasetComponentOption = DatasetOption
/** 附加组件的选项参数(如title、tooltip、legend等) */
export type ComponentsOption = ComposeOption<
| DatasetComponentOption
| TitleComponentOption
| TooltipComponentOption
| GridComponentOption
| LegendComponentOption
| DataZoomComponentOption
>
/** 所有选项参数 */
export type ECOption = ComposeOption<
| LineSeriesOption
| BarSeriesOption
| PieSeriesOption
| RadarSeriesOption
| ScatterSeriesOption
> &
ComponentsOption
/** 柱状图选项参数 */
export type BarOption = ComponentsOption &
ComposeOption<LineSeriesOption | BarSeriesOption>
/** 折线图选项参数 */
export type LineOption = ComponentsOption &
ComposeOption<LineSeriesOption | BarSeriesOption>
/** 饼图选项参数 */
export type PieOption = ComponentsOption & ComposeOption<PieSeriesOption>
/** 雷达图选项参数 */
export type RadarOption = ComponentsOption & ComposeOption<RadarSeriesOption>
/** 散点图选项参数 */
export type ScatterOption = ComponentsOption &
ComposeOption<ScatterSeriesOption>
import { onMounted, onBeforeUnmount, nextTick, shallowRef, Ref } from 'vue'
import { use, init, graphic, ECharts } from 'echarts/core'
import { SVGRenderer } from 'echarts/renderers'
import {
DatasetComponent,
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
DataZoomComponent,
} from 'echarts/components'
use([
SVGRenderer,
DatasetComponent,
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
DataZoomComponent,
])
import { RadarSeriesOption } from 'echarts/charts'
import { ECOption, RadarOption, DatasetComponentOption } from './types'
interface ReturnProp {
chartRef: Ref<null | HTMLElement>
initChart: (dataset: DatasetComponentOption, option?: ECOption) => void
}
/**
* 根据屏幕调整尺寸
* @param myChart Echarts实例引用
* @param chartRef Echarts容器引用
*/
const setResizeAble = (myChart: Ref, chartRef: Ref) => {
const handleResize = () => {
if (!myChart.value) return
myChart.value.resize()
}
onMounted(async () => {
window.addEventListener('resize', handleResize)
await nextTick()
myChart.value = init(chartRef.value as HTMLElement, 'dark')
})
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize)
if (!myChart.value) return
myChart.value.dispose()
myChart.value = null
})
}
/**
* 由于radar图的dataset支持不好
* 故此根据数据集里source数组里的seriesName来重置tooltip显示的名字
* @param defaultOption 图表默认配置
* @param defaultSeriesItem 默认series项
* @param dataset 数据集
* @returns 转换后的图表配置
*/
const transRadarOption = (
defaultOption: RadarOption,
defaultSeriesItem: RadarSeriesOption,
dataset: DatasetComponentOption
): RadarOption => {
const source = dataset && (dataset.source as any[])
const length = (source && source.length) || 0
const result = Object.assign({}, defaultOption)
const seriesData = []
for (let index = 0; index < length; index++) {
const data: number[] = []
Object.keys(source[index]).forEach((key) => {
if (key !== 'seriesName') {
data.push(source[index][key])
}
})
seriesData.push(
Object.assign({}, defaultSeriesItem, {
value: data,
name: source[index].seriesName,
})
)
}
result.series = [
{
type: 'radar',
data: seriesData as any[],
},
]
if (dataset) result.dataset = dataset
return result
}
/**
* 设置color选项中
* 如果有数组项则封装为渐变
* @param options 图表配置
*/
const transLinearColorOption = (options: ECOption): ECOption => {
if (!options.color || (options.color as string[]).length === 0) {
return options
}
const result = Object.assign({}, options)
result.color = (result.color as string[]).map((color) => {
if (Array.isArray(color)) {
if (color.length < 2) {
return color[0]
}
return new graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: color[0],
},
{
offset: 1,
color: color[1],
},
])
}
return color
})
return result
}
/**
* 将defaultSeriesItem和dataset填充进defaultOption形成初始配置项
* @param defaultOption 默认图表配置
* @param defaultSeriesItem 默认series项
* @param dataset 数据集
* @returns 初始配置项
*/
const defaultConfig = <T>(
defaultOption: ECOption,
defaultSeriesItem: T,
dataset: DatasetComponentOption
): ECOption => {
const result = Object.assign({}, defaultOption)
let length = 0
result.series = []
if (dataset) {
result.dataset = dataset
if (dataset.dimensions) {
length = dataset.dimensions.length - 1
} else if (dataset.source && Array.isArray(dataset.source)) {
length = Object.keys(dataset.source[0] || []).length - 1
}
}
for (let index = 0; index < length; index++) {
result.series.push(defaultSeriesItem)
}
return result
}
/**
* 合并默认配置项和自定义配置项
* @param defaultOption 默认图表配置
* @param customOption 自定义图表配置
*/
const mergeOptions = <T>(defaultOption: T, customOption: T) => {
if (!customOption) return
for (const key in customOption) {
if (Array.isArray(customOption[key])) {
defaultOption[key] = customOption[key]
continue
}
if (customOption[key] !== null && typeof customOption[key] === 'object') {
if (!defaultOption[key]) {
defaultOption[key] = customOption[key]
} else {
mergeOptions(defaultOption[key], customOption[key])
}
} else {
defaultOption[key] = customOption[key]
}
}
}
/**
* 构建图表组件公共方法
* @param defaultOption 默认配置
* @param defaultSeriesItem 默认的series项
* @returns chartRef:Echarts容器引用, initChart:初始化方法
*/
const useChartGenerate = <T>(
defaultOption: ECOption,
defaultSeriesItem: T
): ReturnProp => {
const chartRef = shallowRef<null | HTMLElement>(null)
const myChart = shallowRef<null | ECharts>(null)
setResizeAble(myChart, chartRef)
/**
* 根据数据集和配置初始化图表
* @param dataset 数据集
* @param option 自定义图表配置
*/
const initChart = (dataset: DatasetComponentOption, option?: ECOption) => {
if (!myChart.value) return
const config =
(defaultSeriesItem as any).type === 'radar'
? transRadarOption(
defaultOption as RadarOption,
defaultSeriesItem as RadarSeriesOption,
dataset
)
: defaultConfig(defaultOption, defaultSeriesItem, dataset)
mergeOptions(config, option)
myChart.value.setOption(transLinearColorOption(config), true)
}
return { chartRef, initChart }
}
export default useChartGenerate
---
title: MyCount
wrapperClass: my-count
---
# MyCount
数字跳动展示组件
Tag: `m-count`
```vue demo
<template>
<div><m-count :value="99999" /></div>
<div><m-count :value="99999" :speed="1" /></div>
<div><m-count :value="99999" :decimal="2" /></div>
<div><m-count :value="99999" auto-play :duration="5" /></div>
</template>
```
## Props
| props | description | type | default |
| --------- | ---------------------------------------- | --------------- | ------- |
| value | 展示数字 | number / string | 0 |
| decimal | 保留几位小数 | number / string | 0 |
| speed | 数字跳动速度(秒) | number / string | 2 |
| auto-play | 是否定时自动跳动 | boolean | false |
| duration | auto-play 开启后,每次跳动间隔时间(秒) | number / string | 10 |
...@@ -17,6 +17,7 @@ export default defineComponent({ ...@@ -17,6 +17,7 @@ export default defineComponent({
name: 'MyCount', name: 'MyCount',
displayName: 'm-count', displayName: 'm-count',
props: { props: {
/** 展示值 */
value: { value: {
type: [Number, String] as PropType<number | string>, type: [Number, String] as PropType<number | string>,
default: 0, default: 0,
...@@ -61,6 +62,7 @@ export default defineComponent({ ...@@ -61,6 +62,7 @@ export default defineComponent({
countUp.start() countUp.start()
} else { } else {
console.error(`m-count error: ${countUp.error}`) console.error(`m-count error: ${countUp.error}`)
return
} }
countUpInstance.value = countUp countUpInstance.value = countUp
if (props.autoPlay) { if (props.autoPlay) {
......
---
title: MyDrawer
wrapperClass: my-drawer
---
# MyDrawer
侧边抽屉组件
Tag: `m-drawer`
```vue demo
<template>
<m-drawer v-model="show" @close="handleClose">
<h1>内容……</h1>
</m-drawer>
<button @click="show = true">Click Me!</button>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const show = ref(false)
const handleClose = () => {
console.log(
'可以使用close回调,或者直接watch v-model的值进行关闭后的操作'
)
}
return {
show,
handleClose,
}
},
}
</script>
```
## Props
| props | description | type | default |
| -------------------- | ----------- | ------- | ------- |
| v-model / modelValue | 值 | boolean | false |
| width | 抽屉宽度 | string | 49vw |
## Events
| events | description | arguments |
| ------ | ---------------- | --------- |
| close | 关闭抽屉触发事件 | - |
...@@ -23,18 +23,21 @@ export default defineComponent({ ...@@ -23,18 +23,21 @@ export default defineComponent({
displayName: 'm-drawer', displayName: 'm-drawer',
components: { MyAnimate }, components: { MyAnimate },
props: { props: {
/** 值 */
modelValue: { modelValue: {
type: Boolean as PropType<boolean>, type: Boolean as PropType<boolean>,
default: false, default: false,
}, },
/** 宽度 */
width: { width: {
type: String as PropType<string>, type: String as PropType<string>,
default: '49vw', default: '49vw',
}, },
maskClosable: { // /** 点击蒙层是否允许关闭 */
type: Boolean as PropType<boolean>, // maskClosable: {
default: true, // type: Boolean as PropType<boolean>,
}, // default: true,
// },
}, },
emits: ['update:modelValue', 'close'], emits: ['update:modelValue', 'close'],
setup(_, ctx) { setup(_, ctx) {
......
---
title: MyEmpty
wrapperClass: my-empty
---
# MyEmpty
无数据时展示
Tag: `m-empty`
```vue demo
<template>
<m-empty text="建设中" />
</template>
```
## Props
| props | description | type | default |
| ----- | ----------- | ------ | -------- |
| text | 展示信息 | string | 暂无数据 |
...@@ -12,6 +12,7 @@ export default defineComponent({ ...@@ -12,6 +12,7 @@ export default defineComponent({
name: 'MyEmpty', name: 'MyEmpty',
displayName: 'm-empty', displayName: 'm-empty',
props: { props: {
/** 展示信息 */
text: { text: {
type: String as PropType<string>, type: String as PropType<string>,
default: '暂无数据', default: '暂无数据',
......
---
title: MyForm
wrapperClass: my-form
---
# MyForm
Form 表单组件
Tag: `m-form`
### Basic Use
```vue demo
<template>
<m-form
:template="[
'key1:标题1|key2:标题2|key3:标题3',
'key4:标题4|key5:标题5|key6:标题6',
]"
:data="formData"
/>
</template>
<script>
export default {
setup() {
return {
formData: {
key1: '测试1',
key2: '测试2',
key3: '测试3',
key4: '测试4',
key5: '测试5',
key6: '测试6',
},
}
},
}
</script>
```
### Advanced Use
```vue demo
<template>
<m-form
:template="[
'name:小区名称|address:小区地址*3',
'belong:所属居委会|buildingNum:总门牌幢数|roomNum:总户数>formatRoomNum|',
'||buildingArea:总建筑面积>formatArea|area:占地面积>formatArea',
'rang:小区四至范围|excludeRang:四至范围不包括',
'photo:照片#image',
]"
:data="formData"
:formatter="formatter"
/>
</template>
<script>
export default {
setup() {
return {
formData: {
name: '测试文字',
photo: 'https://avatars2.githubusercontent.com/u/43328103?v=4',
address: '测试文字',
belong: '测试文字',
buildingNum: '测试文字',
roomNum: '测试文字',
buildingArea: '测试文字',
area: '测试文字',
rang: '测试文字',
excludeRang: '测试文字',
},
formatter: {
formatArea: (area) => area + '㎡',
formatRoomNum: (val) => val + '万户',
},
}
},
}
</script>
```
## Props
| props | description | type | default |
| --------------------- | ---------------------- | -------- | ------- |
| [template](#template) | 布局模板 | string[] | - |
| data | 表单数据 | object | - |
| formatter | 数据格式化方法对象集合 | object | - |
| label-width | label 宽度 | string | 1rem |
### <i id="template">template introduction</i>
| tag | description |
| --- | ------------------------------------------------------------------------ |
| \| | 将每行划分为 col 块 |
| : | 冒号前为数据 key,冒号后为 label 名称 |
| \* | 后跟该 col 块所占行的比例 |
| > | 后跟该 col 块所需使用的 format 数据方法(方法需在 formatter 参数中定义) |
| # | 后跟该 col 块所属的特殊类型(目前仅实现 image) |
| = | 放在 col 块头则 label 左对齐,尾则右对齐(默认为右对齐) |
<template> <template>
<div class="my-form" :style="{ fontSize: `${size}rem` }"> <div class="my-form">
<a-row v-for="(row, rowIndex) in layout" :key="rowIndex" class="row"> <div v-for="(row, rowIndex) in template" :key="rowIndex" class="row">
<a-col
v-for="(col, key) in row"
:key="key"
class="col"
:span="col.width"
:offset="col.offset || 0"
>
<div <div
:style="`text-align: ${ v-for="(col, colIndex) in row.split('|')"
col.align || 'right' :key="colIndex"
}; width:${labelWidth}rem;`" class="col"
:style="{ flex: calcWidth(col) }"
> >
{{ col.label }} <p :style="{ width: labelWidth, textAlign: calcAlign(col) }">
</div> {{ getLabel(col) }}
<template v-if="col.type == 'img'"> </p>
<img <img
v-for="img in model[key] && model[key].split(',')" v-if="isImage(col)"
:key="img" :src="data[isImage(col)]"
:src="img" :draggable="false"
@click="handleView(img)" @click.stop="handleViewImage(data[isImage(col)])"
/> />
</template> <p v-else class="content">
<div v-else class="content"> {{ formatData(col) }}
{{ col.format ? col.format(model[key]) : model[key] || ' ' }} </p>
</div> </div>
</a-col>
</a-row>
</div> </div>
<MyModal v-model="showImg" title="图片"> <MyModal v-model="showImgModal" title="照片预览">
<img v-if="showImg" style="max-height: 45vh; width: 100%" :src="curImg" /> <img
v-if="showImgModal && imgSrc"
style="max-height: 45vh; width: 100%"
:src="imgSrc"
/>
</MyModal> </MyModal>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, PropType, ref } from 'vue' import { defineComponent, PropType, ref } from 'vue'
import MyModal from '../MyModal/my-modal.vue' import MyModal from '../MyModal/my-modal.vue'
interface ModelProp { interface FormatterType {
[key: string]: any [propName: string]: <T>(val: T) => T
} }
interface DataType {
interface LayoutItemProp { [propName: string]: string | number | string[] | number[]
label: string | number
width: number
offset?: number
align?: 'left' | 'center' | 'right'
type?: 'img'
format?: (arg: any) => any
}
export interface LayoutProp {
[key: string]: LayoutItemProp
} }
export default defineComponent({ export default defineComponent({
name: 'MyForm', name: 'MyForm',
displayName: 'm-form', displayName: 'm-form',
components: { components: { MyModal },
MyModal,
},
props: { props: {
size: { /** 布局模板 */
type: Number as PropType<number>, template: {
default: 0.1, type: Array as PropType<string[]>,
},
labelWidth: {
type: Number as PropType<number>,
default: 1,
},
layout: {
type: Array as PropType<LayoutProp[]>,
required: true, required: true,
}, },
model: { /** 数据 */
type: Object as PropType<ModelProp>, data: {
default() { type: Object as PropType<DataType>,
return {} default: null,
},
/** 数据的格式化方法对象集合 */
formatter: {
type: Object as PropType<FormatterType>,
default: null,
}, },
/** label宽度 */
labelWidth: {
type: String as PropType<string>,
default: '1rem',
}, },
}, },
setup() { setup(props) {
const showImg = ref(false) const calcWidth = (key: string): number => {
const curImg = ref(null) if (!key) return 1
const handleView = (img: any) => { if (key.match(/\*(\d*)[#>:]?/)) {
curImg.value = img return +RegExp.$1
showImg.value = true }
return 1
}
const getLabel = (key: string): string => {
if (!key) return ''
if (key.match(/:([\u4e00-\u9fa5]*[^#=>\*]*)[#>\*]?/)) {
return RegExp.$1 + ':'
}
return ''
}
const imgSrc = ref<string | null>(null)
const showImgModal = ref(false)
const handleViewImage = (src: string): void => {
imgSrc.value = src
showImgModal.value = true
}
/** 是图片则返回图片的key,否则返回false */
const isImage = (key: string): string | false => {
if (!key) return false
if (key.match(/#(\w*)[>:=]?/)) {
if (RegExp.$1 === 'image') {
key.match(/=?(\w*)[#>:]?/)
return RegExp.$1
}
return false
} }
return false
}
const formatData = (key: string): unknown => {
if (!key) return ''
const { formatter, data } = props
key.match(/=?(\w*)[#>\*:]?/)
const dataKey = RegExp.$1
if (formatter && key.match(/>(\w*)[#\*:]?/)) {
return formatter[RegExp.$1](data[dataKey])
}
return data[dataKey] || ''
}
const calcAlign = (key: string): string => {
if (!key) return 'right'
const index = key.indexOf('=')
if (index === 0) return 'left'
if (index === key.length - 1) return 'right'
return 'right'
}
return { return {
showImg, calcWidth,
curImg, getLabel,
handleView, handleViewImage,
isImage,
imgSrc,
showImgModal,
formatData,
calcAlign,
} }
}, },
}) })
</script> </script>
<style lang="stylus" scoped> <style lang="stylus" scoped>
@import '../main.styl'
.my-form .my-form
$full()
.row .row
line-height 2 display flex
background $table-content-bg
padding .04rem 0
&:nth-child(odd) &:nth-child(odd)
background rgba(51,145,255,.2) background transparent
.col .col
display flex display flex
align-items center align-items center
p
padding 0 .05rem padding 0 .05rem
.content box-sizing border-box
>.content
flex 1 flex 1
img padding-left 0
width 19% >img
height .5rem max-height .6rem
margin .05rem .03rem .05rem 0
cursor pointer cursor pointer
</style> </style>
---
title: MyGrid
wrapperClass: my-grid
---
# MyGrid
大屏布局组件
Tag: `m-grid`
```vue demo
<template>
<!-- 真实使用中不需要这个min-height, m-grid默认高度为父容器的100% -->
<m-grid
style="min-height: 50vh;"
:template="[
'title title title',
'box1 . box3',
'box2 . box3',
'box2 box4 box4',
]"
columns="1fr 1.5fr 1fr"
rows="0.4rem 1fr 1fr 1fr"
>
<m-title area="title">大标题</m-title>
<div area="box1" style="background:skyblue;padding:.1rem">
<h3>有area名的元素会被摆放至template中的同名位置</h3>
</div>
<m-card title="box2" area="box2">
<m-grid
:template="['inner1 inner2 inner3', 'inner4 inner4 inner3']"
columns="1rem 20% auto"
rows="30% 1fr"
>
<div area="inner1" style="background:gold"></div>
<div area="inner2" style="background:gold"></div>
<div area="inner3" style="background:gold"></div>
<div area="inner4" style="background:gold"></div>
</m-grid>
</m-card>
<m-card title="box3" area="box3"></m-card>
<div area="box4" style="background:brown;padding:.1rem"></div>
</m-grid>
</template>
```
## Props
| props | description | type | default |
| -------- | ------------ | -------- | ------- |
| template | 区块摆放模板 | string[] | - |
| columns | 列宽比例 | string | - |
| rows | 行高比例 | string | - |
| gap | 区块间隔 | string | 0.05rem |
...@@ -11,18 +11,22 @@ export default defineComponent({ ...@@ -11,18 +11,22 @@ export default defineComponent({
name: 'MyGrid', name: 'MyGrid',
displayName: 'm-grid', displayName: 'm-grid',
props: { props: {
/** 区块摆放模板 */
template: { template: {
type: Array as PropType<string[]>, type: Array as PropType<string[]>,
required: true, required: true,
}, },
/** 列宽比例 */
columns: { columns: {
type: String as PropType<string>, type: String as PropType<string>,
required: true, required: true,
}, },
/** 行高比例 */
rows: { rows: {
type: String as PropType<string>, type: String as PropType<string>,
required: true, required: true,
}, },
/** 区块间隔 */
gap: { gap: {
type: String as PropType<string>, type: String as PropType<string>,
default: '.05rem', default: '.05rem',
...@@ -31,6 +35,7 @@ export default defineComponent({ ...@@ -31,6 +35,7 @@ export default defineComponent({
setup(props) { setup(props) {
const gridRef = ref<HTMLElement | null>(null) const gridRef = ref<HTMLElement | null>(null)
onMounted(() => { onMounted(() => {
if (!gridRef.value) return
const { children } = gridRef.value as HTMLElement const { children } = gridRef.value as HTMLElement
for (let i = 0; i < children.length; i++) { for (let i = 0; i < children.length; i++) {
const child = children[i] as HTMLElement const child = children[i] as HTMLElement
...@@ -68,8 +73,6 @@ export default defineComponent({ ...@@ -68,8 +73,6 @@ export default defineComponent({
@import '../main.styl' @import '../main.styl'
.my-grid .my-grid
$full() $full()
background-size cover
background-position center
position relative position relative
display grid display grid
overflow hidden overflow hidden
......
---
title: MyLoader
wrapperClass: my-loader
---
# MyLoader
Loading 组件
Tag: `m-loader`
```vue demo
<template>
<m-loader v-if="show" />
<button @click="handleClick">Click Me !</button>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const show = ref(false)
const handleClick = () => {
show.value = true
setTimeout(() => {
show.value = false
}, 3000)
}
return {
show,
handleClick,
}
},
}
</script>
```
## Props
| props | description | type | default |
| ----- | ----------- | ---- | ------- |
| - | - | - | - |
---
title: MyModal
wrapperClass: my-modal
---
# MyModal
弹窗组件
Tag: `m-modal`
```vue demo
<template>
<m-modal v-model="show" title="这是个弹窗" @close="handleClose">
<h1>内容……</h1>
</m-modal>
<button @click="show = true">Click Me!</button>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const show = ref(false)
const handleClose = () => {
console.log('可以使用close回调,或者直接watch v-model的值进行操作')
}
return {
show,
handleClose,
}
},
}
</script>
```
## Props
| props | description | type | default |
| -------------------- | -------------------- | ------- | ---------- |
| v-model / modelValue | 值 | boolean | false |
| enter | 进入动画 | string | fadeInDown |
| leave | 离开动画 | string | fadeOutUp |
| title | 标题 | string | - |
| width | 弹窗宽度 | string | 32% |
| offset | 弹窗水平偏移 | string | 0 |
| mask-closable | 点击蒙层是否允许关闭 | boolean | true |
## Events
| events | description | arguments |
| ------ | ---------------- | --------- |
| close | 关闭弹窗触发事件 | - |
...@@ -78,7 +78,7 @@ export default defineComponent({ ...@@ -78,7 +78,7 @@ export default defineComponent({
}, },
}, },
emits: ['update:modelValue', 'close'], emits: ['update:modelValue', 'close'],
setup(props, context) { setup(_, context) {
const closeModal = () => { const closeModal = () => {
context.emit('update:modelValue', false) context.emit('update:modelValue', false)
context.emit('close') context.emit('close')
......
---
title: MyProgress
wrapperClass: my-progress
---
# MyProgress
进度条组件
Tag: `m-progress`
```vue demo
<template>
<m-progress :value="80" />
<m-progress
:value="67"
:msg="{ name: '测试', value: 67, unit: '%' }"
:color="['red', 'gold']"
/>
</template>
```
## Props
| props | description | type | default |
| ----------- | ------------------------- | ----------------- | ---------------------- |
| color | 进度条颜色 如为数组则渐变 | string / string[] | ['#0094FF', '#1EFBFF'] |
| value | 进度百分值 | number | 0 |
| [msg](#msg) | 附加信息 | object | - |
| height | 进度条高度(rem) | number | 0.07 |
### <i id="msg">msg props</i>
| props | description | type | default |
| ----- | ----------- | ------ | ------- |
| name | 名字 | string | - |
| value | 展示值 | number | - |
| unit | 值后边单位 | string | - |
...@@ -28,18 +28,22 @@ export default defineComponent({ ...@@ -28,18 +28,22 @@ export default defineComponent({
displayName: 'm-progress', displayName: 'm-progress',
components: { MonitorCount }, components: { MonitorCount },
props: { props: {
/** 进度条颜色 如为数组则渐变 */
color: { color: {
type: [String, Array] as PropType<string | string[]>, type: [String, Array] as PropType<string | string[]>,
default: () => ['#0094FF', '#1EFBFF'], default: () => ['#0094FF', '#1EFBFF'],
}, },
/** 进度百分值 */
value: { value: {
type: Number as PropType<number>, type: Number as PropType<number>,
default: 0, default: 0,
}, },
/** 附加信息 */
msg: { msg: {
type: Object as PropType<ProgressProps>, type: Object as PropType<ProgressProps>,
default: null, default: null,
}, },
/** 进度条高度 */
height: { height: {
type: Number as PropType<number>, type: Number as PropType<number>,
default: 0.07, default: 0.07,
......
---
title: MyScroll
wrapperClass: my-scroll
---
# MyScroll
轮播组件
Tag: `m-scroll`
```vue demo
<template>
<div style="display:inline-block;width:50%;height:2rem;overflow:hidden;">
<m-scroll :length="10" :limit="3">
<div
v-for="i in 10"
:key="i"
style="height: 0.8rem;text-align:center;background:skyblue;margin-bottom:.05rem;line-height:0.8rem;"
>
{{ i }}
</div>
</m-scroll>
</div>
<div style="display:inline-block;width:50%;height:2rem;overflow:hidden;">
<m-scroll :length="colors.length" :limit="2" mode="2" :step="2">
<div
v-for="color in colors"
:key="color"
:style="`height: 2rem;text-align:center;background:${color};line-height:2rem;`"
>
{{ color }}
</div>
</m-scroll>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const colors = ref(['red', 'blue', 'green', 'brown', 'pink'])
return {
colors,
}
},
}
</script>
```
## Props
| props | description | type | default |
| -------- | --------------------------------------------------------------- | --------------- | ------- |
| mode | 模式 | number / string | 1 |
| length | 数据长度 | number | - |
| limit | 数据长度小于次值则不轮播 | number | - |
| speed | mode1 的轮播速度(毫秒) | number | 50 |
| step | mode2 的每个元素高度, 有 margin 记得也要把 margin 算进去(rem) | number | 0 |
| duration | mode2 的轮播间隔(毫秒) | number | 4000 |
...@@ -64,6 +64,14 @@ export default defineComponent({ ...@@ -64,6 +64,14 @@ export default defineComponent({
() => props.length <= 100 && props.length >= props.limit () => props.length <= 100 && props.length >= props.limit
) )
const start = () => { const start = () => {
if (!props.length) {
console.error('MyScroll 需要length参数!')
return
}
if (!props.limit) {
console.error('MyScroll 需要limit参数!')
return
}
if (props.length < props.limit) return if (props.length < props.limit) return
if (+props.mode === 2) { if (+props.mode === 2) {
startMode2() startMode2()
......
---
title: MyStep
wrapperClass: my-step
---
# MyStep
步骤条组件
Tag: `m-step`
```vue demo
<template>
<m-step :steps="['第一步', '第二步', '第三步', '第四步']" current="3" />
<m-step
:msg="['哈哈哈', `<b style='color:red;'>哈哈哈哈哈</b>`]"
:steps="['第一步', '第二步', '第三步', '第四步']"
current="2"
/>
<m-step
:msg="['msg1', 'msg2', 'msg3', 'msg4']"
:steps="['第一步', '第二步', '第三步', '第四步']"
current="1"
/>
</template>
```
## Props
| props | description | type | default |
| ------- | ------------------ | --------------- | ------- |
| steps | 节点名称 | string[] | - |
| current | 当前节点索引 | number / string | 0 |
| msg | 每个节点的附加信息 | string[] | - |
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<span /> <span />
<p><slot /></p> <p><slot /></p>
<div v-if="addition" class="addition"> <div v-if="addition" class="addition">
<m-count v-if="addition.value" class="count" :value="addition.value" /> <MyCount v-if="addition.value" class="count" :value="addition.value" />
<span v-if="addition.unit">{{ addition.unit }}</span> <span v-if="addition.unit">{{ addition.unit }}</span>
</div> </div>
<input <input
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, PropType, ref } from 'vue' import { defineComponent, PropType, ref } from 'vue'
import MyCount from '../MyCount/my-count.vue'
export interface AdditionProp { export interface AdditionProp {
name?: string name?: string
...@@ -38,6 +39,7 @@ export interface AdditionProp { ...@@ -38,6 +39,7 @@ export interface AdditionProp {
export default defineComponent({ export default defineComponent({
name: 'MySub', name: 'MySub',
displayName: 'm-sub', displayName: 'm-sub',
components: { MyCount },
props: { props: {
addition: { addition: {
type: Object as PropType<AdditionProp>, type: Object as PropType<AdditionProp>,
......
---
title: MyTable
wrapperClass: my-table
---
# MyTable
Table 组件
Tag: `m-table`
```vue demo
<template>
<m-table
:template="[
'标题1|标题2*2|标题3*2',
'key1|key2>customFormatter|key3#image',
]"
:data="tableData"
:formatter="{ customFormatter }"
/>
</template>
<script>
export default {
setup() {
return {
tableData: [
{
key1: '文字1',
key2: '文字2',
key3: 'https://avatars2.githubusercontent.com/u/43328103?v=4',
},
{
key1: '文字1',
key2: '文字2',
key3: 'https://avatars2.githubusercontent.com/u/43328103?v=4',
},
{
key1: '文字1',
key2: '文字2',
key3: 'https://avatars2.githubusercontent.com/u/43328103?v=4',
},
{
key1: '文字1',
key2: '文字2',
key3: 'https://avatars2.githubusercontent.com/u/43328103?v=4',
},
],
customFormatter: (val) => {
return val + '哈哈哈'
},
}
},
}
</script>
```
## Props
| props | description | type | default |
| --------------------- | ---------------------- | -------- | ------- |
| [template](#template) | 布局模板 | string[] | - |
| data | 表单数据 | [] | - |
| formatter | 数据格式化方法对象集合 | object | - |
| selectable | 是否可触发点击事件 | boolean | false |
### <i id="template">template introduction</i>
| tag | description |
| --- | ----------------------------------------------------------------------- |
| \| | 将每行划分为 col 块 |
| \* | 后跟该列所占行的比例 |
| > | 后跟该项所需使用的 format 数据方法(方法需在 formatter 参数中定义) |
| # | 后跟该项所属的特殊类型(目前仅实现 image) |
| = | 放在 template 第一项 col 块头则标题左对齐,尾则右对齐(默认为居中对齐) |
## Events
| events | description | arguments |
| ------ | -------------------------------- | --------- |
| select | 开启 selectable 后点击行触发事件 | rowData |
...@@ -7,13 +7,13 @@ ...@@ -7,13 +7,13 @@
:style="`flex:${calcWidth[index]}`" :style="`flex:${calcWidth[index]}`"
> >
<p :style="`text-align:${calcAlign[index]}`"> <p :style="`text-align:${calcAlign[index]}`">
{{ title.split('*')[0].replace('::', '') }} {{ title.split('*')[0].replace('=', '') }}
</p> </p>
</div> </div>
</div> </div>
<div class="table-content"> <div class="table-content">
<div <div
v-for="(item, index) in dataSource" v-for="(item, index) in data"
:key="index" :key="index"
:class="{ selectable: selectable }" :class="{ selectable: selectable }"
@click.prevent="handleClick(item)" @click.prevent="handleClick(item)"
...@@ -21,19 +21,16 @@ ...@@ -21,19 +21,16 @@
<div <div
v-for="(key, i) in layout.keys" v-for="(key, i) in layout.keys"
:key="key" :key="key"
:style="`flex:${calcWidth[i]}`" :style="`flex:${calcWidth[i]};text-align:${calcAlign[i]}`"
> >
<p v-if="key.indexOf('>') >= 0" :style="`text-align:${calcAlign[i]}`">
{{ transValue(item, key) }}
</p>
<img <img
v-else-if="key.indexOf('#') >= 0 && key.split('#')[1] === 'image'" v-if="isImage(key)"
:src="item[key.split('#')[0]]" :src="item[isImage(key)]"
:draggable="false" :draggable="false"
@click.stop="handleViewImage(item[key.split('#')[0]])" @click.stop="handleViewImage(item[isImage(key)])"
/> />
<p v-else :style="`text-align:${calcAlign[i]}`"> <p v-else :style="`text-align:${calcAlign[i]}`">
{{ item[key] }} {{ formatData(key, item) }}
</p> </p>
</div> </div>
</div> </div>
...@@ -51,8 +48,8 @@ import MyModal from '../MyModal/my-modal.vue' ...@@ -51,8 +48,8 @@ import MyModal from '../MyModal/my-modal.vue'
interface FormatterType { interface FormatterType {
[propName: string]: <T>(val: T) => T [propName: string]: <T>(val: T) => T
} }
interface dataType { interface DataType {
[propName: string]: string | number | string[] [propName: string]: string | number | string[] | number[]
} }
export default defineComponent({ export default defineComponent({
...@@ -60,18 +57,22 @@ export default defineComponent({ ...@@ -60,18 +57,22 @@ export default defineComponent({
displayName: 'm-table', displayName: 'm-table',
components: { MyModal }, components: { MyModal },
props: { props: {
/** 布局模板 */
template: { template: {
type: Array as PropType<string[]>, type: Array as PropType<string[]>,
required: true, required: true,
}, },
/** 数据 */
data: { data: {
type: Array as PropType<dataType[]>, type: Array as PropType<DataType[]>,
default: () => [], default: () => [],
}, },
/** 数据的格式化方法对象集合 */
formatter: { formatter: {
type: Object as PropType<FormatterType>, type: Object as PropType<FormatterType>,
default: null, default: null,
}, },
/** 是否可选择 */
selectable: { selectable: {
type: Boolean as PropType<boolean>, type: Boolean as PropType<boolean>,
default: false, default: false,
...@@ -86,14 +87,7 @@ export default defineComponent({ ...@@ -86,14 +87,7 @@ export default defineComponent({
const keys = template[1].split('|') const keys = template[1].split('|')
return { header, keys } return { header, keys }
}) })
const dataSource = computed(() => props.data) const handleClick = (data: DataType): void => {
const transValue = (item: dataType, key: string) => {
return (
props.formatter &&
props.formatter[key.split('>')[1]](item[key.split('>')[0]])
)
}
const handleClick = (data: dataType) => {
if (props.selectable) ctx.emit('select', data) if (props.selectable) ctx.emit('select', data)
} }
const calcWidth = computed(() => { const calcWidth = computed(() => {
...@@ -106,10 +100,10 @@ export default defineComponent({ ...@@ -106,10 +100,10 @@ export default defineComponent({
}) })
}) })
const calcAlign = computed(() => { const calcAlign = computed(() => {
if (!layout.value) return 'center' if (!layout.value) return ['center', 'center', 'center']
const { length } = layout.value.header const { length } = layout.value.header
return layout.value.header.map((item) => { return layout.value.header.map((item) => {
const index = item.indexOf('::') const index = item.indexOf('=')
if (index === 0) return 'left' if (index === 0) return 'left'
if (index === length) return 'right' if (index === length) return 'right'
return 'center' return 'center'
...@@ -118,20 +112,40 @@ export default defineComponent({ ...@@ -118,20 +112,40 @@ export default defineComponent({
const imgSrc = ref<string | null>(null) const imgSrc = ref<string | null>(null)
const showImgModal = ref(false) const showImgModal = ref(false)
const handleViewImage = (src: string) => { const handleViewImage = (src: string): void => {
imgSrc.value = src imgSrc.value = src
showImgModal.value = true showImgModal.value = true
} }
/** 是图片则返回图片的key,否则返回false */
const isImage = (key: string): string | false => {
if (key.match(/#(\w*)[>:]?/)) {
if (RegExp.$1 === 'image') {
key.match(/(\w*)[#>:]?/)
return RegExp.$1
}
return false
}
return false
}
const formatData = (key: string, data: DataType): unknown => {
const { formatter } = props
key.match(/(\w*)[#>:]?/)
const dataKey = RegExp.$1
if (formatter && key.match(/>(\w*)[#:]?/)) {
return formatter[RegExp.$1](data[dataKey])
}
return data[dataKey] || ''
}
return { return {
layout, layout,
dataSource,
transValue,
handleClick, handleClick,
calcWidth, calcWidth,
calcAlign, calcAlign,
imgSrc, imgSrc,
handleViewImage, handleViewImage,
showImgModal, showImgModal,
isImage,
formatData,
} }
}, },
}) })
...@@ -151,15 +165,15 @@ export default defineComponent({ ...@@ -151,15 +165,15 @@ export default defineComponent({
background $table-title-bg background $table-title-bg
color $blue color $blue
font-weight bold font-weight bold
box-sizing border-box box-sizing inherit
>div >div
box-sizing border-box box-sizing inherit
.table-content .table-content
box-sizing border-box
>div >div
box-sizing border-box
display flex display flex
align-items center
background $table-content-bg background $table-content-bg
box-sizing inherit
&:nth-child(odd) &:nth-child(odd)
background transparent background transparent
&.selectable &.selectable
...@@ -168,6 +182,8 @@ export default defineComponent({ ...@@ -168,6 +182,8 @@ export default defineComponent({
color $table-content-hover-color color $table-content-hover-color
background $table-content-hover-bg background $table-content-hover-bg
img img
width 100% max-height 1rem
max-width 100%
cursor pointer cursor pointer
margin-top .05rem
</style> </style>
---
title: MyTitle
wrapperClass: my-title
---
# MyTitle
大屏标题组件
Tag: `m-title`
```vue demo
<template>
<m-title>XXXXXXX数据平台1</m-title>
<m-title :bg-img="bgImg">XXXXXXX数据平台2</m-title>
</template>
<script>
import bgImg from '@/assets/images/title-bg2.png'
export default {
setup() {
return {
bgImg,
}
},
}
</script>
```
## Props
| props | description | type | default |
| ------ | ----------- | ----- | ------------ |
| bg-img | 背景图片 | Image | title-bg.png |
---
title: MyWave
wrapperClass: my-wave
---
# MyWave
百分比波浪球组件
Tag: `m-wave`
```vue demo
<template>
<m-wave :value="67" size=".5rem">67</m-wave>
<m-wave :value="80" size=".5rem" color="gold">
<m-count :value="80" />
</m-wave>
</template>
```
## Props
| props | description | type | default |
| ----- | ----------- | --------------- | ------- |
| value | 值 | number / string | 0 |
| size | 球体宽高 | string | 0.4rem |
| color | 球体颜色 | string | #4F953B |
<template> <template>
<div class="my-wave-ball" :style="{ width: size, height: size }"> <div
<div class="before" :style="{ top: `${percent}%` }" /> class="my-wave-ball"
<div class="after" :style="{ top: `${percent}%` }" /> :style="{
width: size,
height: size,
borderColor: color,
boxShadow: `0 0 .08rem 0 ${color} inset`,
}"
>
<div class="before" :style="{ top: `${percent}%`, background: color }" />
<div class="after" :style="{ top: `${percent}%`, background: color }" />
<p><m-count :value="value" /> %</p> <p><m-count :value="value" /> %</p>
</div> </div>
</template> </template>
...@@ -10,20 +18,27 @@ ...@@ -10,20 +18,27 @@
import { computed, defineComponent, PropType } from 'vue' import { computed, defineComponent, PropType } from 'vue'
export default defineComponent({ export default defineComponent({
name: 'MyWaveBall', name: 'MyWave',
displayName: 'm-wave-ball', displayName: 'm-wave',
props: { props: {
/** 值 */
value: { value: {
type: Number as PropType<number>, type: [Number, String] as PropType<number | string>,
default: 0, default: 0,
}, },
/** 球体宽高 默认0.4rem */
size: { size: {
type: String as PropType<string>, type: String as PropType<string>,
default: '.4rem', default: '.4rem',
}, },
/** 球体颜色 默认#4F953B */
color: {
type: String as PropType<string>,
default: '#4F953B',
},
}, },
setup(props) { setup(props) {
const percent = computed(() => 250 - props.value) const percent = computed(() => 250 - +props.value)
return { return {
percent, percent,
} }
...@@ -38,15 +53,12 @@ export default defineComponent({ ...@@ -38,15 +53,12 @@ export default defineComponent({
background transparent background transparent
border-radius 50% border-radius 50%
overflow hidden overflow hidden
border .02rem solid $green border .02rem solid
box-sizing content-box box-sizing content-box
box-shadow 0 0 .08rem 0 #6EB629 inset
transform translateZ(0) transform translateZ(0)
$center() $center()
p p
font-size .12rem z-index 30
font-weight bold
z-index: 30;
.before .before
.after .after
content '' content ''
...@@ -54,7 +66,6 @@ export default defineComponent({ ...@@ -54,7 +66,6 @@ export default defineComponent({
width 200% width 200%
height @width height @width
left 50% left 50%
background $green
opacity .5 opacity .5
border-radius 40% border-radius 40%
animation rotate 10s ease infinite alternate animation rotate 10s ease infinite alternate
......
...@@ -19,9 +19,10 @@ import MyPie from './MyChart/my-pie.vue' ...@@ -19,9 +19,10 @@ import MyPie from './MyChart/my-pie.vue'
import MyRadar from './MyChart/my-radar.vue' import MyRadar from './MyChart/my-radar.vue'
import MyScatter from './MyChart/my-scatter.vue' import MyScatter from './MyChart/my-scatter.vue'
import MySub from './MySub/my-sub.vue' import MySub from './MySub/my-sub.vue'
import MyWaveBall from './MyWaveBall/my-wave-ball.vue' import MyWave from './MyWave/my-wave.vue'
import MyProgress from './MyProgress/my-progress.vue' import MyProgress from './MyProgress/my-progress.vue'
import MyDrawer from './MyDrawer/my-drawer.vue' import MyDrawer from './MyDrawer/my-drawer.vue'
import * as ChartTypes from './MyChart/types'
// import { withInstall } from './util' // import { withInstall } from './util'
import 'normalize.css' import 'normalize.css'
...@@ -45,7 +46,7 @@ const components = [ ...@@ -45,7 +46,7 @@ const components = [
MyRadar, MyRadar,
MyScatter, MyScatter,
MySub, MySub,
MyWaveBall, MyWave,
MyProgress, MyProgress,
MyDrawer, MyDrawer,
] ]
...@@ -80,10 +81,13 @@ export { ...@@ -80,10 +81,13 @@ export {
MyRadar, MyRadar,
MyScatter, MyScatter,
MySub, MySub,
MyWaveBall, MyWave,
MyProgress, MyProgress,
MyDrawer, MyDrawer,
} }
// 图表组件的类型参数
export { ChartTypes }
// 默认导出 —— 使用import MyComponent from './components/MyComponent'来引入所有组件 // 默认导出 —— 使用import MyComponent from './components/MyComponent'来引入所有组件
export default { install } export default { install }
...@@ -22,11 +22,35 @@ ...@@ -22,11 +22,35 @@
<m-card title="人口详情"> <m-card title="人口详情">
<Tabs class="tabs" :list="tabs" horizontal /> <Tabs class="tabs" :list="tabs" horizontal />
<SubTitle>基本信息</SubTitle> <SubTitle>基本信息</SubTitle>
<m-form :layout="layout1" :model="data1" :label-width="1.1" /> <m-form
:template="[
'community:所属小区|building:门牌幢',
'communityType:小区类型|roomNo:室号',
'company:物业公司|manager:物业经理',
'manageOffice:管理处|hasElev:有无电梯',
'yewei:业委会|juwei:居委会',
]"
:data="data1"
label-width="1.2rem"
/>
<SubTitle>房主信息</SubTitle> <SubTitle>房主信息</SubTitle>
<m-form :layout="layout2" :model="data2" :label-width="1.1" /> <m-form
:template="['name:姓名|phone:手机号码', 'household:户籍|']"
:data="data2"
label-width="1.2rem"
/>
<Tabs class="tabs" :list="tabs2" horizontal /> <Tabs class="tabs" :list="tabs2" horizontal />
<m-form :layout="layout3" :model="data3" :label-width="1.1" /> <m-form
:template="[
'name:小区名称',
'address:小区地址|belong:所属居委会',
'buildingNum:总门牌幢数|roomNum:总户数',
'buildingArea:总建筑面积|aera:占地面积',
'range:小区四至范围|excludeRange:四至范围不包括',
]"
:data="data3"
label-width="1.2rem"
/>
</m-card> </m-card>
</div> </div>
</template> </template>
...@@ -102,58 +126,6 @@ export default defineComponent({ ...@@ -102,58 +126,6 @@ export default defineComponent({
name: '公共设施', name: '公共设施',
}, },
]) ])
const layout1 = ref([
{
community: {
label: '所属小区',
width: 12,
},
building: {
label: '门牌幢',
width: 12,
},
},
{
communityType: {
label: '小区类型',
width: 12,
},
roomNo: {
label: '室号',
width: 12,
},
},
{
company: {
label: '物业公司',
width: 12,
},
manager: {
label: '物业经理',
width: 12,
},
},
{
manageOffice: {
label: '管理处',
width: 12,
},
hasElev: {
label: '有无电梯',
width: 12,
},
},
{
yewei: {
label: '业委会',
width: 12,
},
juwei: {
label: '居委会',
width: 12,
},
},
])
const data1 = ref({ const data1 = ref({
community: '测试文字', community: '测试文字',
building: '测试文字', building: '测试文字',
...@@ -166,97 +138,28 @@ export default defineComponent({ ...@@ -166,97 +138,28 @@ export default defineComponent({
yewei: '测试文字', yewei: '测试文字',
juwei: '测试文字', juwei: '测试文字',
}) })
const layout2 = ref([
{
name: {
label: '姓名',
width: 12,
},
phone: {
label: '手机号码',
width: 12,
},
},
{
household: {
label: '户籍',
width: 12,
},
},
])
const data2 = ref({ const data2 = ref({
name: '暂无', name: '暂无',
phone: '暂无', phone: '暂无',
household: '暂无', household: '暂无',
}) })
const layout3 = ref([
{
name: {
label: '小区名称',
width: 24,
},
},
{
address: {
label: '小区地址',
width: 12,
},
belong: {
label: '所属居委会',
width: 12,
},
},
{
buildingNum: {
label: '总门牌幢数',
width: 12,
},
roomNum: {
label: '总户数',
width: 12,
},
},
{
buidlingArea: {
label: '总建筑面积',
width: 12,
},
area: {
label: '占地面积',
width: 12,
},
},
{
rang: {
label: '小区四至范围',
width: 12,
},
excludeRang: {
label: '小区四至范围不包括',
width: 12,
},
},
])
const data3 = ref({ const data3 = ref({
name: '测试文字', name: '测试文字',
address: '测试文字', address: '测试文字',
belong: '测试文字', belong: '测试文字',
buildingNum: '测试文字', buildingNum: '测试文字',
roomNum: '测试文字', roomNum: '测试文字',
buidlingArea: '测试文字', buildingArea: '测试文字',
area: '测试文字', area: '测试文字',
rang: '测试文字', range: '测试文字',
excludeRang: '测试文字', excludeRange: '测试文字',
}) })
return { return {
caseList, caseList,
tabs, tabs,
tabs2, tabs2,
layout1,
data1, data1,
layout2,
data2, data2,
layout3,
data3, data3,
} }
}, },
......
...@@ -57,10 +57,6 @@ export default defineComponent({ ...@@ -57,10 +57,6 @@ export default defineComponent({
grid: { grid: {
width: '105%', width: '105%',
left: '-5%', left: '-5%',
right: '2%',
bottom: '1%',
top: '20%',
containLabel: true,
}, },
xAxis: [ xAxis: [
{ {
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<m-card title="有诉必答"> <m-card title="有诉必答">
<div class="summary"> <div class="summary">
<div v-for="item in summary" :key="item.name"> <div v-for="item in summary" :key="item.name">
<m-wave-ball :value="item.percent" size=".34rem" /> <m-wave :value="item.percent" size=".34rem" />
<div> <div>
<m-count class="count" :value="item.value" /> <m-count class="count" :value="item.value" />
<p>{{ item.name }}</p> <p>{{ item.name }}</p>
...@@ -124,7 +124,7 @@ export default defineComponent({ ...@@ -124,7 +124,7 @@ export default defineComponent({
source: [ source: [
{ {
seriesName: '分类排行', seriesName: '分类排行',
class1: 150, class1: 250,
class2: 200, class2: 200,
class3: 200, class3: 200,
class4: 300, class4: 300,
...@@ -136,31 +136,6 @@ export default defineComponent({ ...@@ -136,31 +136,6 @@ export default defineComponent({
color: ['#FF9D27'], color: ['#FF9D27'],
legend: { show: false }, legend: { show: false },
radar: { radar: {
axisName: {
// show: false,
color: 'transparent',
},
indicator: {},
splitArea: {
areaStyle: {
color: [
'rgba(1,124,143,.9)',
'rgba(1,124,143,.7)',
'rgba(1,124,143,.5)',
'rgba(1,124,143,.3)',
'rgba(1,124,143,.1)',
],
},
},
splitLine: {
show: true,
lineStyle: {
color: 'rgba(255,255,255,.3)',
},
},
axisLine: {
show: false,
},
radius: '80%', radius: '80%',
}, },
}) })
......
...@@ -226,7 +226,8 @@ export default defineComponent({ ...@@ -226,7 +226,8 @@ export default defineComponent({
const pieOption = ref({ const pieOption = ref({
color: ['#00BFFF', '#FFCE34', '#826AFA', '#589C20', '#F47C1F'], color: ['#00BFFF', '#FFCE34', '#826AFA', '#589C20', '#F47C1F'],
legend: { show: false }, legend: { show: false },
series: { series: [
{
type: 'pie', type: 'pie',
radius: [0, '90%'], radius: [0, '90%'],
center: ['40%', '50%'], center: ['40%', '50%'],
...@@ -234,6 +235,7 @@ export default defineComponent({ ...@@ -234,6 +235,7 @@ export default defineComponent({
show: false, show: false,
}, },
}, },
],
}) })
return { return {
showChart, showChart,
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<m-progress :value="80" :height="0.1" /> <m-progress :value="80" :height="0.1" />
<div class="house-types"> <div class="house-types">
<div v-for="item in houseTypes" :key="item.name"> <div v-for="item in houseTypes" :key="item.name">
<m-wave-ball :value="item.percent" size=".34rem" /> <m-wave :value="item.percent" size=".34rem" />
<div> <div>
<m-count class="count" :value="item.value" /> <m-count class="count" :value="item.value" />
<p>{{ item.name }}</p> <p>{{ item.name }}</p>
......
...@@ -107,7 +107,8 @@ export default defineComponent({ ...@@ -107,7 +107,8 @@ export default defineComponent({
const pieOption = ref({ const pieOption = ref({
color: ['#8D51C7', '#BEBEBE', '#CA3A40', '#F9B84C', '#34ACFF', '#429321'], color: ['#8D51C7', '#BEBEBE', '#CA3A40', '#F9B84C', '#34ACFF', '#429321'],
legend: { show: false }, legend: { show: false },
series: { series: [
{
type: 'pie', type: 'pie',
roseType: 'radius', roseType: 'radius',
radius: ['10%', '90%'], radius: ['10%', '90%'],
...@@ -116,6 +117,7 @@ export default defineComponent({ ...@@ -116,6 +117,7 @@ export default defineComponent({
show: false, show: false,
}, },
}, },
],
}) })
const oldType = ref([ const oldType = ref([
{ name: '孤老', value: 103, percent: 0.9 }, { name: '孤老', value: 103, percent: 0.9 },
......
...@@ -87,11 +87,7 @@ export default defineComponent({ ...@@ -87,11 +87,7 @@ export default defineComponent({
itemHeight: fontSize.value * 0.8, itemHeight: fontSize.value * 0.8,
}, },
grid: { grid: {
left: '2%',
right: '5%',
bottom: '15%', bottom: '15%',
top: '20%',
containLabel: true,
}, },
dataZoom: [ dataZoom: [
{ {
......
...@@ -89,8 +89,8 @@ module.exports = { ...@@ -89,8 +89,8 @@ module.exports = {
path: './src/components/MyComponent/MyProgress/my-progress.vue', path: './src/components/MyComponent/MyProgress/my-progress.vue',
}, },
{ {
name: 'm-wave-ball', name: 'm-wave',
path: './src/components/MyComponent/MyWaveBall/my-wave-ball.vue', path: './src/components/MyComponent/MyWave/my-wave.vue',
}, },
{ {
name: 'm-sub', name: 'm-sub',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment