perf: dragable components
This commit is contained in:
parent
43b6361515
commit
240a0bd779
|
@ -52,8 +52,13 @@ let propObj = {
|
|||
$$('#props + table tr').reduce((prev, curr) => {
|
||||
const children = curr.children
|
||||
const key = children[0].textContent.replace(/-([a-z])/g, (all, i) => i.toUpperCase())
|
||||
const child3Text = children[3].textContent
|
||||
const defaultValue = ['true', 'false'].includes(child3Text)
|
||||
? child3Text
|
||||
: `'${child3Text == '-' ? '' : child3Text}'`
|
||||
const value = (propObj[children[2].textContent] ?? propObj['string'])({
|
||||
label: `'${children[1].textContent}'`
|
||||
label: `'${children[1].textContent}'`,
|
||||
defaultValue
|
||||
}).replaceAll('"', '')
|
||||
prev[key] = value
|
||||
return prev
|
||||
|
|
|
@ -57,8 +57,13 @@ let propObj = {
|
|||
$$('#props + table tr').reduce((prev, curr) => {
|
||||
const children = curr.children
|
||||
const key = children[0].textContent.replace(/-([a-z])/g, (all, i) => i.toUpperCase())
|
||||
const child3Text = children[3].textContent
|
||||
const defaultValue = ['true', 'false'].includes(child3Text)
|
||||
? child3Text
|
||||
: `'${child3Text == '-' ? '' : child3Text}'`
|
||||
const value = (propObj[children[2].textContent] ?? propObj['string'])({
|
||||
label: `'${children[1].textContent}'`
|
||||
label: `'${children[1].textContent}'`,
|
||||
defaultValue
|
||||
}).replaceAll('"', '')
|
||||
prev[key] = value
|
||||
return prev
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@vant/touch-emulator": "^1.3.0",
|
||||
"@vueuse/core": "^5.0.2",
|
||||
"@vueuse/integrations": "^5.0.2",
|
||||
"@vueuse/core": "^5.0.3",
|
||||
"@vueuse/integrations": "^5.0.3",
|
||||
"animate.css": "^4.1.1",
|
||||
"axios": "^0.21.1",
|
||||
"dayjs": "^1.10.5",
|
||||
|
@ -56,7 +56,7 @@
|
|||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-import": "^2.23.4",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-vue": "^7.11.0",
|
||||
"eslint-plugin-vue": "^7.11.1",
|
||||
"gh-pages": "^3.2.0",
|
||||
"husky": "^6.0.0",
|
||||
"lint-staged": "^11.0.0",
|
||||
|
|
|
@ -1,14 +1,23 @@
|
|||
import { defineComponent } from 'vue'
|
||||
/*
|
||||
* @Author: 卜启缘
|
||||
* @Date: 2021-05-04 05:36:58
|
||||
* @LastEditTime: 2021-06-14 10:03:06
|
||||
* @LastEditors: 卜启缘
|
||||
* @Description:
|
||||
* @FilePath: \vite-vue3-lowcode\preview\views\comp-render.tsx
|
||||
*/
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { VisualEditorBlockData, VisualEditorConfig } from '@/visual-editor/visual-editor.utils'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CompRender',
|
||||
props: {
|
||||
element: {
|
||||
type: Object,
|
||||
type: Object as PropType<VisualEditorBlockData>,
|
||||
default: () => ({})
|
||||
},
|
||||
config: {
|
||||
type: Object,
|
||||
type: Object as PropType<VisualEditorConfig>,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* @Author: 卜启缘
|
||||
* @Date: 2021-06-12 21:29:32
|
||||
* @LastEditTime: 2021-06-12 22:03:43
|
||||
* @LastEditTime: 2021-06-13 19:27:04
|
||||
* @LastEditors: 卜启缘
|
||||
* @Description: 执行组件动画
|
||||
* @FilePath: \vite-vue3-lowcode\src\hooks\useAnimate.ts
|
||||
|
@ -9,30 +9,47 @@
|
|||
|
||||
import { Animation } from '@/visual-editor/visual-editor.utils'
|
||||
|
||||
export const useAnimate = async (animateEl: HTMLElement, animations: Animation | Animation[]) => {
|
||||
export const useAnimate = async (
|
||||
animateEl: HTMLElement,
|
||||
animations: Animation | Animation[],
|
||||
prefixCls = 'animate__'
|
||||
) => {
|
||||
animations = Array.isArray(animations) ? animations : [animations]
|
||||
|
||||
const play = (animate: Animation) =>
|
||||
new Promise((resolve) => {
|
||||
if (animateEl) {
|
||||
const animationName = `animate__${animate.value}`
|
||||
animateEl.style.setProperty('--animate-duration', `${animate.duration}s`)
|
||||
animateEl.style.setProperty('--animate-delay', `${animate.delay}s`)
|
||||
animateEl.style.setProperty(
|
||||
'animation-iteration-count',
|
||||
`${animate.infinite ? 'infinite' : animate.count}`
|
||||
)
|
||||
const animationName = `${prefixCls}${animate.value}`
|
||||
|
||||
// 过滤可能残留的animate.css动画类名
|
||||
animateEl.classList.value = animateEl.classList.value
|
||||
.split(' ')
|
||||
.filter((item) => !item.includes(prefixCls))
|
||||
.join(' ')
|
||||
|
||||
// 设置动画属性
|
||||
const setAnimate = () => {
|
||||
animateEl.style.setProperty('--animate-duration', `${animate.duration}s`)
|
||||
animateEl.style.setProperty('animation-delay', `${animate.delay}s`)
|
||||
animateEl.style.setProperty(
|
||||
'animation-iteration-count',
|
||||
`${animate.infinite ? 'infinite' : animate.count}`
|
||||
)
|
||||
animateEl?.classList.add(`${prefixCls}animated`, animationName)
|
||||
}
|
||||
|
||||
// 动画结束时,删除类名
|
||||
const handleAnimationEnd = (event?: AnimationEvent) => {
|
||||
event?.stopPropagation()
|
||||
animateEl.classList.remove(`animate__animated`, animationName)
|
||||
animateEl.classList.remove(`${prefixCls}animated`, animationName)
|
||||
animateEl.removeEventListener('animationend', handleAnimationEnd)
|
||||
resolve('animation end')
|
||||
}
|
||||
|
||||
animateEl?.classList.add(`animate__animated`, animationName)
|
||||
setAnimate()
|
||||
|
||||
animateEl?.addEventListener('animationend', handleAnimationEnd, { once: true })
|
||||
// animateEl?.addEventListener('animationcancel', handleAnimationEnd, { once: true })
|
||||
} else {
|
||||
resolve('动画执行失败!执行动画元素不存在!')
|
||||
}
|
||||
|
|
|
@ -6,16 +6,23 @@ import {
|
|||
createEditorSelectProp
|
||||
} from '@/visual-editor/visual-editor.props'
|
||||
import { VisualEditorComponent } from '@/visual-editor/visual-editor.utils'
|
||||
import { useGlobalProperties } from '@/hooks/useGlobalProperties'
|
||||
|
||||
export default {
|
||||
key: 'divider',
|
||||
moduleName: 'baseWidgets',
|
||||
label: '分割线',
|
||||
preview: () => <Divider style="width:190px">文本</Divider>,
|
||||
render: ({ props }) => {
|
||||
const style = `color:${props['text-color']};borderColor:${props['divider-color']}`
|
||||
render: ({ props, block }) => {
|
||||
const { registerRef } = useGlobalProperties()
|
||||
|
||||
const style = {
|
||||
color: props['text-color'],
|
||||
borderColor: props['divider-color']
|
||||
}
|
||||
|
||||
return (
|
||||
<Divider {...props} style={style}>
|
||||
<Divider ref={(el) => registerRef(el, block._vid)} {...props} style={style}>
|
||||
{{
|
||||
default: () => props.text
|
||||
}}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* @Author: 卜启缘
|
||||
* @Date: 2021-06-01 09:45:21
|
||||
* @LastEditTime: 2021-06-12 09:55:10
|
||||
* @LastEditTime: 2021-06-14 10:31:27
|
||||
* @LastEditors: 卜启缘
|
||||
* @Description: 图片组件
|
||||
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\image\index.tsx
|
||||
|
@ -32,8 +32,8 @@ export default {
|
|||
),
|
||||
render: ({ props, block }) => {
|
||||
const { registerRef } = useGlobalProperties()
|
||||
const ImageComp = () => <Image ref={(el) => registerRef(el, block._vid)} {...props} />
|
||||
return <ImageComp></ImageComp>
|
||||
|
||||
return <Image ref={(el) => registerRef(el, block._vid)} {...props} />
|
||||
},
|
||||
props: {
|
||||
src: createEditorInputProp({
|
||||
|
|
|
@ -13,6 +13,11 @@ export default {
|
|||
render: ({ model, size, block, props, custom }) => {
|
||||
const { registerRef } = useGlobalProperties()
|
||||
|
||||
let rules = []
|
||||
try {
|
||||
rules = JSON.parse(props.rules)
|
||||
} catch (e) {}
|
||||
|
||||
return (
|
||||
<Field
|
||||
ref={(el) => registerRef(el, block._vid)}
|
||||
|
@ -20,6 +25,7 @@ export default {
|
|||
{...props}
|
||||
{...model.default}
|
||||
v-model={props.modelValue}
|
||||
rules={rules}
|
||||
style={{
|
||||
width: size.width ? `${size.width}px` : null
|
||||
}}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* @Author: 卜启缘
|
||||
* @Date: 2021-06-14 12:24:12
|
||||
* @LastEditTime: 2021-06-14 12:38:02
|
||||
* @LastEditors: 卜启缘
|
||||
* @Description:
|
||||
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\notice-bar\createFieldProps.ts
|
||||
*/
|
||||
import {
|
||||
createEditorInputProp,
|
||||
createEditorSelectProp,
|
||||
createEditorSwitchProp
|
||||
} from '@/visual-editor/visual-editor.props'
|
||||
|
||||
export const createFieldProps = () => ({
|
||||
background: createEditorInputProp({ label: '滚动条背景' }),
|
||||
color: createEditorInputProp({ label: '通知文本颜色' }),
|
||||
delay: createEditorInputProp({ label: '动画延迟时间 (s)' }),
|
||||
leftIcon: createEditorInputProp({ label: '左侧图标名称或图片链接', defaultValue: 'volume-o' }),
|
||||
mode: createEditorSelectProp({
|
||||
label: '通知栏模式',
|
||||
options: [
|
||||
{
|
||||
label: '默认',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
label: '可关闭',
|
||||
value: 'closeable'
|
||||
},
|
||||
{
|
||||
label: '链接',
|
||||
value: 'link'
|
||||
}
|
||||
]
|
||||
}),
|
||||
scrollable: createEditorSwitchProp({ label: '是否开启滚动播放,内容长度溢出时默认开启' }),
|
||||
speed: createEditorInputProp({ label: '滚动速率 (px/s)' }),
|
||||
text: createEditorInputProp({
|
||||
label: '通知文本内容',
|
||||
defaultValue: '在代码阅读过程中人们说脏话的频率是衡量代码质量的唯一标准。'
|
||||
}),
|
||||
wrapable: createEditorSwitchProp({ label: '是否开启文本换行,只在禁用滚动时生效' })
|
||||
})
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* @Author: 卜启缘
|
||||
* @Date: 2021-06-14 12:24:12
|
||||
* @LastEditTime: 2021-06-14 12:56:23
|
||||
* @LastEditors: 卜启缘
|
||||
* @Description:
|
||||
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\notice-bar\index.tsx
|
||||
*/
|
||||
import { NoticeBar } from 'vant'
|
||||
import { VisualEditorComponent } from '@/visual-editor/visual-editor.utils'
|
||||
import { createFieldProps } from './createFieldProps'
|
||||
import { useGlobalProperties } from '@/hooks/useGlobalProperties'
|
||||
|
||||
export default {
|
||||
key: 'NoticeBar',
|
||||
moduleName: 'baseWidgets',
|
||||
label: '通知栏',
|
||||
preview: () => (
|
||||
<NoticeBar
|
||||
style={{ width: '180px' }}
|
||||
leftIcon={'volume-o'}
|
||||
text={'在代码阅读过程中人们说脏话的频率是衡量代码质量的唯一标准。'}
|
||||
/>
|
||||
),
|
||||
render: ({ block, props }) => {
|
||||
const { registerRef } = useGlobalProperties()
|
||||
|
||||
return <NoticeBar ref={(el) => registerRef(el, block._vid)} {...props} />
|
||||
},
|
||||
props: createFieldProps(),
|
||||
resize: {
|
||||
width: true
|
||||
},
|
||||
model: {
|
||||
default: '绑定字段'
|
||||
}
|
||||
} as VisualEditorComponent
|
|
@ -49,7 +49,7 @@ export default {
|
|||
)
|
||||
}}
|
||||
</Field>
|
||||
<Popup v-model={[state.showPicker, 'show', ['modifier']]} position={'bottom'}>
|
||||
<Popup v-model={[state.showPicker, 'show']} position={'bottom'}>
|
||||
<Picker
|
||||
ref={(el) => registerRef(el, block._vid)}
|
||||
{...props}
|
||||
|
|
|
@ -11,9 +11,11 @@ export default {
|
|||
key: 'process',
|
||||
moduleName: 'baseWidgets',
|
||||
label: '进度条',
|
||||
preview: () => <Progress style="width:190px" percentage={0} />,
|
||||
preview: () => <Progress style="width:190px" percentage={50} />,
|
||||
render: ({ props }) => {
|
||||
return <Progress {...props} pivotText={props.pivotText || undefined} />
|
||||
const RenderProgress = () => <Progress {...props} pivotText={props.pivotText || undefined} />
|
||||
|
||||
return <RenderProgress />
|
||||
},
|
||||
props: {
|
||||
percentage: createEditorInputNumberProp({ label: '进度百分比', defaultValue: 50 }),
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
export default {
|
||||
key: 'rate',
|
||||
moduleName: 'baseWidgets',
|
||||
label: '表单项类型 - 单选框',
|
||||
label: '表单项类型 - 评分',
|
||||
preview: () => (
|
||||
<Field
|
||||
name="rate"
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
export default {
|
||||
key: 'stepper',
|
||||
moduleName: 'baseWidgets',
|
||||
label: '表单项类型 - 单选框',
|
||||
label: '表单项类型 - 步进器',
|
||||
preview: () => (
|
||||
<Field
|
||||
name="stepper"
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* @Author: 卜启缘
|
||||
* @Date: 2021-06-14 12:24:12
|
||||
* @LastEditTime: 2021-06-14 18:43:21
|
||||
* @LastEditors: 卜启缘
|
||||
* @Description:
|
||||
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\swipe\createFieldProps.ts
|
||||
*/
|
||||
import {
|
||||
createEditorInputProp,
|
||||
createEditorSwitchProp,
|
||||
createEditorCrossSortableProp
|
||||
} from '@/visual-editor/visual-editor.props'
|
||||
|
||||
export const createFieldProps = () => ({
|
||||
images: createEditorCrossSortableProp({
|
||||
label: '图片列表',
|
||||
labelPosition: 'top',
|
||||
defaultValue: ['https://img.yzcdn.cn/vant/apple-1.jpg', 'https://img.yzcdn.cn/vant/apple-2.jpg']
|
||||
}),
|
||||
width: createEditorInputProp({ label: '滑块宽度,单位为 px', defaultValue: 'auto' }),
|
||||
height: createEditorInputProp({ label: '滑块高度,单位为 px', defaultValue: '200' }),
|
||||
autoplay: createEditorInputProp({ label: '自动轮播间隔,单位为 ms', defaultValue: '' }),
|
||||
duration: createEditorInputProp({ label: '动画时长,单位为 ms', defaultValue: '500' }),
|
||||
indicatorColor: createEditorInputProp({ label: '指示器颜色', defaultValue: '#1989fa' }),
|
||||
initialSwipe: createEditorInputProp({ label: '初始位置索引值', defaultValue: '0' }),
|
||||
lazyRender: createEditorSwitchProp({ label: '是否延迟渲染未展示的轮播', defaultValue: false }),
|
||||
loop: createEditorSwitchProp({ label: '是否开启循环播放', defaultValue: true }),
|
||||
showIndicators: createEditorSwitchProp({ label: '是否显示指示器', defaultValue: true }),
|
||||
stopPropagation: createEditorSwitchProp({ label: '是否阻止滑动事件冒泡', defaultValue: true }),
|
||||
touchable: createEditorSwitchProp({ label: '是否可以通过手势滑动', defaultValue: true }),
|
||||
vertical: createEditorSwitchProp({ label: '是否为纵向滚动', defaultValue: false })
|
||||
})
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* @Author: 卜启缘
|
||||
* @Date: 2021-06-14 12:24:12
|
||||
* @LastEditTime: 2021-06-14 18:48:44
|
||||
* @LastEditors: 卜启缘
|
||||
* @Description:
|
||||
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\swipe\index.tsx
|
||||
*/
|
||||
import { Swipe, SwipeItem } from 'vant'
|
||||
import { VisualEditorComponent } from '@/visual-editor/visual-editor.utils'
|
||||
import { createFieldProps } from './createFieldProps'
|
||||
import { useGlobalProperties } from '@/hooks/useGlobalProperties'
|
||||
|
||||
const swipeItemStyle = `color: #fff;
|
||||
font-size: 20px;
|
||||
line-height: 150px;
|
||||
text-align: center;
|
||||
background-color: #39a9ed;`
|
||||
|
||||
export default {
|
||||
key: 'swipe',
|
||||
moduleName: 'baseWidgets',
|
||||
label: '轮播图',
|
||||
preview: () => (
|
||||
<Swipe style={{ width: '180px', height: '80%' }} indicatorColor={'white'}>
|
||||
<SwipeItem style={swipeItemStyle}>1</SwipeItem>
|
||||
<SwipeItem style={swipeItemStyle}>2</SwipeItem>
|
||||
<SwipeItem style={swipeItemStyle}>3</SwipeItem>
|
||||
<SwipeItem style={swipeItemStyle}>4</SwipeItem>
|
||||
</Swipe>
|
||||
),
|
||||
render: ({ block, props }) => {
|
||||
const { registerRef } = useGlobalProperties()
|
||||
|
||||
return (
|
||||
<Swipe ref={(el) => registerRef(el, block._vid)} {...props}>
|
||||
{props.images?.map((item) => (
|
||||
<>
|
||||
<SwipeItem key={item}>
|
||||
<img src={item} />
|
||||
</SwipeItem>
|
||||
</>
|
||||
))}
|
||||
</Swipe>
|
||||
)
|
||||
},
|
||||
props: createFieldProps(),
|
||||
resize: {
|
||||
width: true
|
||||
},
|
||||
model: {
|
||||
default: '绑定字段'
|
||||
}
|
||||
} as VisualEditorComponent
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* @Author: 卜启缘
|
||||
* @Date: 2021-06-14 00:53:21
|
||||
* @LastEditTime: 2021-06-14 00:55:55
|
||||
* @LastEditors: 卜启缘
|
||||
* @Description: 可用字体集
|
||||
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\text\fontArr.ts
|
||||
*/
|
||||
export const fontArr = [
|
||||
{ label: '宋体', value: 'SimSun' },
|
||||
{ label: '黑体', value: 'SimHei' },
|
||||
{ label: '微软雅黑', value: 'Microsoft Yahei' },
|
||||
{ label: '微软正黑体', value: 'Microsoft JhengHei' },
|
||||
{ label: '楷体', value: 'KaiTi' },
|
||||
{ label: '新宋体', value: 'NSimSun' },
|
||||
{ label: '仿宋', value: 'FangSong' },
|
||||
{ label: '苹方', value: 'PingFang SC' },
|
||||
{ label: '华文黑体', value: 'STHeiti' },
|
||||
{ label: '华文楷体', value: 'STKaiti' },
|
||||
{ label: '华文宋体', value: 'STSong' },
|
||||
{ label: '华文仿宋', value: 'STFangsong' },
|
||||
{ label: '华文中宋', value: 'STZhongsong' },
|
||||
{ label: '华文琥珀', value: 'STHupo' },
|
||||
{ label: '华文新魏', value: 'STXinwei' },
|
||||
{ label: '华文隶书', value: 'STLiti' },
|
||||
{ label: '华文行楷', value: 'STXingkai' },
|
||||
{ label: '冬青黑体简', value: 'Hiragino Sans GB' },
|
||||
{ label: '兰亭黑-简', value: 'Lantinghei SC' },
|
||||
{ label: '翩翩体-简', value: 'Hanzipen SC' },
|
||||
{ label: '手札体-简', value: 'Hannotate SC' },
|
||||
{ label: '宋体-简', value: 'Songti SC' },
|
||||
{ label: '娃娃体-简', value: 'Wawati SC' },
|
||||
{ label: '魏碑-简', value: 'Weibei SC' },
|
||||
{ label: '行楷-简', value: 'Xingkai SC' },
|
||||
{ label: '雅痞-简', value: 'Yapi SC' },
|
||||
{ label: '圆体-简', value: 'Yuanti SC' },
|
||||
{ label: '幼圆', value: 'YouYuan' },
|
||||
{ label: '隶书', value: 'LiSu' },
|
||||
{ label: '华文细黑', value: 'STXihei' },
|
||||
{ label: '华文楷体', value: 'STKaiti' },
|
||||
{ label: '华文宋体', value: 'STSong' },
|
||||
{ label: '华文仿宋', value: 'STFangsong' },
|
||||
{ label: '华文中宋', value: 'STZhongsong' },
|
||||
{ label: '华文彩云', value: 'STCaiyun' },
|
||||
{ label: '华文琥珀', value: 'STHupo' },
|
||||
{ label: '华文新魏', value: 'STXinwei' },
|
||||
{ label: '华文隶书', value: 'STLiti' },
|
||||
{ label: '华文行楷', value: 'STXingkai' },
|
||||
{ label: '方正舒体', value: 'FZShuTi' },
|
||||
{ label: '方正姚体', value: 'FZYaoti' },
|
||||
{ label: '思源黑体', value: 'Source Han Sans CN' },
|
||||
{ label: '思源宋体', value: 'Source Han Serif SC' },
|
||||
{ label: '文泉驿微米黑', value: 'WenQuanYi Micro Hei' }
|
||||
]
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* @Author: 卜启缘
|
||||
* @Date: 2021-06-01 09:45:21
|
||||
* @LastEditTime: 2021-06-12 10:06:33
|
||||
* @LastEditTime: 2021-06-14 10:17:54
|
||||
* @LastEditors: 卜启缘
|
||||
* @Description:
|
||||
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\text\index.tsx
|
||||
|
@ -13,6 +13,7 @@ import {
|
|||
createEditorSelectProp
|
||||
} from '@/visual-editor/visual-editor.props'
|
||||
import { VisualEditorComponent } from '@/visual-editor/visual-editor.utils'
|
||||
import { fontArr } from './fontArr'
|
||||
|
||||
export default {
|
||||
key: 'text',
|
||||
|
@ -21,18 +22,19 @@ export default {
|
|||
preview: () => <span>预览文本</span>,
|
||||
render: ({ props, block }) => {
|
||||
const { registerRef } = useGlobalProperties()
|
||||
const Text = () => (
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={(el) => registerRef(el, block._vid)}
|
||||
style={{ color: props.color, fontSize: props.size }}
|
||||
style={{ color: props.color, fontSize: props.size, fontFamily: props.font }}
|
||||
>
|
||||
{props.text || '默认文本'}
|
||||
</div>
|
||||
)
|
||||
return <Text></Text>
|
||||
},
|
||||
props: {
|
||||
text: createEditorInputProp({ label: '显示文本' }),
|
||||
font: createEditorSelectProp({ label: '字体设置', options: fontArr }),
|
||||
color: createEditorColorProp('字体颜色'),
|
||||
size: createEditorSelectProp({
|
||||
label: '字体大小',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!--
|
||||
* @Author: 卜启缘
|
||||
* @Date: 2021-06-01 13:30:22
|
||||
* @LastEditTime: 2021-06-12 18:29:28
|
||||
* @LastEditTime: 2021-06-14 00:21:31
|
||||
* @LastEditors: 卜启缘
|
||||
* @Description: 手机模拟器
|
||||
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\common\simulator.vue
|
||||
|
|
|
@ -151,7 +151,7 @@ export const useTools = () => {
|
|||
icon: 'el-icon-position',
|
||||
onClick: () => {
|
||||
localStorage.setItem(localKey, JSON.stringify(jsonData))
|
||||
window.open(location.href.replace('/#/', '/preview/'))
|
||||
window.open(location.href.replace('/#/', '/preview/#/'))
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* @Author: 卜启缘
|
||||
* @Date: 2021-06-11 18:08:01
|
||||
* @LastEditTime: 2021-06-12 22:07:05
|
||||
* @LastEditTime: 2021-06-13 18:32:53
|
||||
* @LastEditors: 卜启缘
|
||||
* @Description: 动画组件
|
||||
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\animate\Animate.tsx
|
||||
|
@ -92,10 +92,10 @@ export const Animate = defineComponent({
|
|||
<>
|
||||
{currentBlock.value.animations?.map((item, index) => (
|
||||
<ElAlert
|
||||
onClose={() => delAnimate(index)}
|
||||
key={item.value}
|
||||
type={'info'}
|
||||
style={{ marginTop: '12px' }}
|
||||
key={item.value}
|
||||
onClose={() => delAnimate(index)}
|
||||
>
|
||||
{{
|
||||
title: () => (
|
||||
|
@ -164,9 +164,10 @@ export const Animate = defineComponent({
|
|||
<div v-show={!state.isAddAnimates}>
|
||||
<ElButton
|
||||
type={'primary'}
|
||||
onClick={() => (state.isAddAnimates = true)}
|
||||
disabled={!currentBlock.value.animations}
|
||||
plain
|
||||
icon={'el-icon-plus'}
|
||||
onClick={() => (state.isAddAnimates = true)}
|
||||
>
|
||||
添加动画
|
||||
</ElButton>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
/*
|
||||
* @Author: 卜启缘
|
||||
* @Date: 2021-06-10 16:23:06
|
||||
* @LastEditTime: 2021-06-10 16:46:36
|
||||
* @LastEditTime: 2021-06-14 17:22:11
|
||||
* @LastEditors: 卜启缘
|
||||
* @Description: 组件属性编辑器
|
||||
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\AttrEditor.tsx
|
||||
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\attr-editor\AttrEditor.tsx
|
||||
*/
|
||||
import { defineComponent } from 'vue'
|
||||
import {
|
||||
|
@ -19,7 +19,7 @@ import {
|
|||
ElPopover
|
||||
} from 'element-plus'
|
||||
import { VisualEditorProps, VisualEditorPropsType } from '@/visual-editor/visual-editor.props'
|
||||
import { TablePropEditor } from '../'
|
||||
import { TablePropEditor, CrossSortableOptionsEditor } from '../'
|
||||
import { useDotProp } from '@/visual-editor/hooks/useDotProp'
|
||||
import { useVisualData } from '@/visual-editor/hooks/useVisualData'
|
||||
|
||||
|
@ -37,6 +37,9 @@ export const AttrEditor = defineComponent({
|
|||
[VisualEditorPropsType.inputNumber]: () => <ElInputNumber v-model={propObj[prop]} />,
|
||||
[VisualEditorPropsType.switch]: () => <ElSwitch v-model={propObj[prop]} />,
|
||||
[VisualEditorPropsType.color]: () => <ElColorPicker v-model={propObj[prop]} />,
|
||||
[VisualEditorPropsType.crossSortable]: () => (
|
||||
<CrossSortableOptionsEditor v-model={propObj[prop]} />
|
||||
),
|
||||
[VisualEditorPropsType.select]: () => (
|
||||
<ElSelect v-model={propObj[prop]} valueKey={'value'} multiple={propConfig.multiple}>
|
||||
{propConfig.options?.map((opt) => (
|
||||
|
@ -91,21 +94,31 @@ export const AttrEditor = defineComponent({
|
|||
content.push(
|
||||
...Object.entries(component.props || {}).map(([propName, propConfig]) => (
|
||||
<>
|
||||
<ElFormItem key={currentBlock.value._vid + propName}>
|
||||
<ElFormItem
|
||||
key={currentBlock.value._vid + propName}
|
||||
style={
|
||||
propConfig.labelPosition == 'top'
|
||||
? {
|
||||
display: 'flex',
|
||||
'flex-direction': 'column',
|
||||
'align-items': 'flex-start'
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
{{
|
||||
label: () =>
|
||||
propConfig.tips ? (
|
||||
<>
|
||||
label: () => (
|
||||
<>
|
||||
{propConfig.tips && (
|
||||
<ElPopover width={200} trigger={'hover'} content={propConfig.tips}>
|
||||
{{
|
||||
reference: () => <i class={'el-icon-warning-outline'}></i>
|
||||
}}
|
||||
</ElPopover>
|
||||
{propConfig.label}
|
||||
</>
|
||||
) : (
|
||||
propConfig.label
|
||||
),
|
||||
)}
|
||||
{propConfig.label}
|
||||
</>
|
||||
),
|
||||
default: () => renderEditor(propName, propConfig)
|
||||
}}
|
||||
</ElFormItem>
|
||||
|
@ -117,7 +130,7 @@ export const AttrEditor = defineComponent({
|
|||
}
|
||||
return (
|
||||
<>
|
||||
<ElForm size="mini" label-position="left">
|
||||
<ElForm size="mini" labelPosition={'left'}>
|
||||
{content}
|
||||
</ElForm>
|
||||
</>
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* @Author: 卜启缘
|
||||
* @Date: 2021-06-14 15:00:45
|
||||
* @LastEditTime: 2021-06-14 17:41:14
|
||||
* @LastEditors: 卜启缘
|
||||
* @Description: 可以拖拽排序的选项列表
|
||||
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\cross-sortable-options\cross-sortable-options.tsx
|
||||
*/
|
||||
|
||||
import { defineComponent, reactive, computed, PropType } from 'vue'
|
||||
import Draggable from 'vuedraggable'
|
||||
import { ElInput } from 'element-plus'
|
||||
import { useVModel } from '@vueuse/core'
|
||||
|
||||
export const CrossSortableOptionsEditor = defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const state = reactive({
|
||||
list: useVModel(props, 'modelValue', emit),
|
||||
drag: false
|
||||
})
|
||||
|
||||
const dragOptions = computed(() => {
|
||||
return {
|
||||
animation: 200,
|
||||
group: 'description',
|
||||
disabled: false,
|
||||
ghostClass: 'ghost'
|
||||
}
|
||||
})
|
||||
|
||||
return () => (
|
||||
<Draggable
|
||||
tag="ul"
|
||||
list={state.list}
|
||||
class="list-group"
|
||||
component-data={{
|
||||
tag: 'ul',
|
||||
type: 'transition-group',
|
||||
name: !state.drag ? 'flip-list' : null
|
||||
}}
|
||||
handle=".handle"
|
||||
{...dragOptions.value}
|
||||
itemKey={''}
|
||||
onStart={() => (state.drag = true)}
|
||||
onEnd={() => (state.drag = false)}
|
||||
>
|
||||
{{
|
||||
item: ({ element, index }) => (
|
||||
<div class={'flex items-center justify-between'}>
|
||||
<i class={'el-icon-s-grid handle'}></i>
|
||||
<ElInput
|
||||
v-model={state.list[index]}
|
||||
class={'m-12px'}
|
||||
style={{ width: '270px' }}
|
||||
></ElInput>
|
||||
<div class={'flex flex-col'}>
|
||||
<i class={'el-icon-circle-plus-outline'} onClick={() => state.list.push('')}></i>
|
||||
<i class={'el-icon-remove-outline'} onClick={() => state.list.splice(index, 1)}></i>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
</Draggable>
|
||||
)
|
||||
}
|
||||
})
|
|
@ -1,10 +1,13 @@
|
|||
/**
|
||||
* @name: index
|
||||
* @author: 卜启缘
|
||||
* @date: 2021/5/30 10:57
|
||||
* @description:index
|
||||
* @update: 2021/5/30 10:57
|
||||
/*
|
||||
* @Author: 卜启缘
|
||||
* @Date: 2021-06-12 22:18:48
|
||||
* @LastEditTime: 2021-06-14 16:58:34
|
||||
* @LastEditors: 卜启缘
|
||||
* @Description:
|
||||
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\index.ts
|
||||
*/
|
||||
|
||||
export { TablePropEditor } from './table-prop-editor/table-prop-editor'
|
||||
export { AttrEditor } from './attr-editor/AttrEditor'
|
||||
export { Animate } from './animate/Animate'
|
||||
export { CrossSortableOptionsEditor } from './cross-sortable-options/cross-sortable-options'
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* @Author: 卜启缘
|
||||
* @Date: 2021-06-13 22:07:29
|
||||
* @LastEditTime: 2021-06-14 18:18:51
|
||||
* @LastEditors: 卜启缘
|
||||
* @Description: 当前页面配置
|
||||
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\page-setting\pageSetting.tsx
|
||||
*/
|
|
@ -1,17 +0,0 @@
|
|||
/**
|
||||
* @name: index.common
|
||||
* @author: 卜启缘
|
||||
* @date: 2021/5/3 16:05
|
||||
* @description:index.common
|
||||
* @update: 2021/5/3 16:05
|
||||
*/
|
||||
body {
|
||||
.el-form-item__label {
|
||||
font-size: 12px;
|
||||
}
|
||||
.el-form-item--mini .el-form-item__content {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
|
@ -67,6 +67,16 @@ $boxShadow: -2px 0 4px 0 rgb(0 0 0 / 10%);
|
|||
padding-bottom: 50px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.el-form-item__label {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.el-form-item--mini .el-form-item__content {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* @Author: 卜启缘
|
||||
* @Date: 2021-06-01 13:22:14
|
||||
* @LastEditTime: 2021-06-12 19:25:26
|
||||
* @LastEditTime: 2021-06-13 21:26:49
|
||||
* @LastEditors: 卜启缘
|
||||
* @Description: 属性编辑器
|
||||
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\index.tsx
|
||||
|
@ -10,11 +10,7 @@
|
|||
|
||||
import { defineComponent, reactive } from 'vue'
|
||||
import styles from './index.module.scss'
|
||||
import './index.common.scss'
|
||||
import { ElTabPane, ElTabs } from 'element-plus'
|
||||
|
||||
console.log(styles, 'styles')
|
||||
|
||||
import MonacoEditor from '../common/monaco-editor/MonacoEditor'
|
||||
import { useVisualData } from '@/visual-editor/hooks/useVisualData'
|
||||
import { AttrEditor, Animate } from './components'
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
/*
|
||||
* @Author: 卜启缘
|
||||
* @Date: 2021-05-04 05:36:58
|
||||
* @LastEditTime: 2021-06-14 10:02:47
|
||||
* @LastEditors: 卜启缘
|
||||
* @Description:
|
||||
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\simulator-editor\comp-render.tsx
|
||||
*/
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { VisualEditorBlockData } from '@/visual-editor/visual-editor.utils'
|
||||
import { VisualEditorBlockData, VisualEditorConfig } from '@/visual-editor/visual-editor.utils'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CompRender',
|
||||
|
@ -9,7 +17,7 @@ export default defineComponent({
|
|||
default: () => ({})
|
||||
},
|
||||
config: {
|
||||
type: Object,
|
||||
type: Object as PropType<VisualEditorConfig>,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
|
|
|
@ -94,9 +94,9 @@ export default defineComponent({
|
|||
height: 100%;
|
||||
min-height: 40px;
|
||||
|
||||
&.isDrag div[data-draggable='true'] {
|
||||
padding: 8px 0;
|
||||
}
|
||||
// &.isDrag div[data-draggable='true'] {
|
||||
// padding: 2px 0;
|
||||
// }
|
||||
|
||||
&.isDrag:not(.no-child) :deep(.list-group-item.has-slot) {
|
||||
@include showContainerBorder;
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
<template>
|
||||
<DraggableTransitionGroup v-model:drag="drag" v-model="currentPage.blocks">
|
||||
<DraggableTransitionGroup
|
||||
v-model:drag="drag"
|
||||
v-model="currentPage.blocks"
|
||||
style="min-height: 500px"
|
||||
>
|
||||
<template #item="{ element: outElement }">
|
||||
<div
|
||||
class="list-group-item"
|
||||
|
@ -14,6 +18,7 @@
|
|||
@mousedown="selectComp(outElement)"
|
||||
>
|
||||
<comp-render
|
||||
:key="outElement._vid"
|
||||
:config="visualConfig"
|
||||
:element="outElement"
|
||||
:style="{
|
||||
|
@ -93,6 +98,7 @@ export default defineComponent({
|
|||
}
|
||||
}
|
||||
|
||||
// 给当前点击的组件设置聚焦
|
||||
const handleSlotsFocus = (block, _vid) => {
|
||||
const slots = block.props?.slots || {}
|
||||
if (Object.keys(slots).length > 0) {
|
||||
|
@ -112,6 +118,7 @@ export default defineComponent({
|
|||
}
|
||||
}
|
||||
|
||||
// 选择要操作的组件
|
||||
const selectComp = (element) => {
|
||||
setCurrentBlock(element)
|
||||
currentPage.value.blocks.forEach((block) => {
|
||||
|
@ -165,7 +172,10 @@ export default defineComponent({
|
|||
const index = parentBlocks.findIndex((item) => item._vid == block._vid)
|
||||
if (index != -1) {
|
||||
delete globalProperties.$$refs[parentBlocks[index]._vid]
|
||||
parentBlocks.splice(index, 1)
|
||||
const delTarget = parentBlocks.splice(index, 1)[0]
|
||||
if (delTarget.focus) {
|
||||
setCurrentBlock({} as VisualEditorBlockData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
v-model:drag="isDrag"
|
||||
class="inner-draggable"
|
||||
:class="{ slot: !slotChildren?.length }"
|
||||
:data-slot="`插槽(${slotKey})\n 拖拽组件到此处${drag}`"
|
||||
:data-slot="`插槽(${slotKey})\n 拖拽组件到此处`"
|
||||
>
|
||||
<template #item="{ element: innerElement }">
|
||||
<div
|
||||
|
|
|
@ -122,6 +122,8 @@ export const initVisualData = (): VisualData => {
|
|||
state.currentPage = jsonData.pages['/']
|
||||
router.replace('/')
|
||||
}
|
||||
const currentFocusBlock = state.currentPage.blocks.find((item) => item.focus)
|
||||
setCurrentBlock(currentFocusBlock ?? ({} as VisualEditorBlockData))
|
||||
}
|
||||
|
||||
// 设置当前被操作的组件
|
||||
|
|
|
@ -4,13 +4,15 @@ export enum VisualEditorPropsType {
|
|||
color = 'color',
|
||||
select = 'select',
|
||||
table = 'table',
|
||||
switch = 'switch'
|
||||
switch = 'switch',
|
||||
crossSortable = 'crossSortable'
|
||||
}
|
||||
|
||||
export type VisualEditorProps = {
|
||||
type: VisualEditorPropsType
|
||||
label: string
|
||||
tips?: string
|
||||
tips?: string // 表单项提示
|
||||
labelPosition?: string // 表单域标签的位置
|
||||
multiple?: boolean
|
||||
defaultValue?: any
|
||||
} & {
|
||||
|
@ -150,3 +152,24 @@ export function createEditorTableProp({
|
|||
defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------CrossSortableOptions-------------------------------------------*/
|
||||
|
||||
interface EditorCrossSortableProp {
|
||||
label: string
|
||||
labelPosition: string
|
||||
defaultValue?: string[]
|
||||
}
|
||||
|
||||
export function createEditorCrossSortableProp({
|
||||
label,
|
||||
labelPosition,
|
||||
defaultValue
|
||||
}: EditorCrossSortableProp): VisualEditorProps {
|
||||
return {
|
||||
type: VisualEditorPropsType.crossSortable,
|
||||
label,
|
||||
labelPosition,
|
||||
defaultValue
|
||||
}
|
||||
}
|
||||
|
|
36
yarn.lock
36
yarn.lock
|
@ -964,20 +964,20 @@
|
|||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.1.1.tgz#2287cfc3dc20e5b20aeb65c2c3a56533bdca801c"
|
||||
integrity sha512-g+4pzAw7PYSjARtLBoDq6DmcblX8i9KJHSCnyM5VDDFFifUaUT9iHbFpOF/KOizQ9f7QAqU2JH3Y6aXjzUMhVA==
|
||||
|
||||
"@vueuse/core@^5.0.2":
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-5.0.2.tgz#302389f620c0d4b51fdf157012d9b5b522b605e7"
|
||||
integrity sha512-Sp9+7AL4Cg3Tx6I55WoH7zICGRlp6ZUF9NW3EU8SZTkryHm0afAjFfASMwlfV030JFeh45BdqafDOrenVmM9Cw==
|
||||
"@vueuse/core@^5.0.3":
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-5.0.3.tgz#8f3170e2a51ae62fb1725c84d4cc02a7552aad0b"
|
||||
integrity sha512-TMCL11EVMaj2Y5qdYosvuwA+i1aKrerFXs7fhNZiQiLCWxF8XsrNdxzoiaI2n12UcmSOXvd1xdyWs7Nss+p/Hg==
|
||||
dependencies:
|
||||
"@vueuse/shared" "5.0.2"
|
||||
"@vueuse/shared" "5.0.3"
|
||||
vue-demi "*"
|
||||
|
||||
"@vueuse/integrations@^5.0.2":
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@vueuse/integrations/-/integrations-5.0.2.tgz#95b9f5bea831430f747d7311d9497ec9f52db3bd"
|
||||
integrity sha512-jh9Ywz8zu2YmsSd1xeyXkuoA8fhNRkoUkzX4y/hSm2IQC2ydkD8Rk/aOvPAew5RiwndtWhXQYu5XrSMQRsYDYQ==
|
||||
"@vueuse/integrations@^5.0.3":
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@vueuse/integrations/-/integrations-5.0.3.tgz#64820965d068b356b4df50ed47a87adf0141f68e"
|
||||
integrity sha512-c7dy7u4XTJeSfq/NnFotoUPrbkPJv/DbWwEYVW9YXXMdDvz1IxDdXUFrOu8ElM0qzGn4CuYg6Yr37uYR13MgXg==
|
||||
dependencies:
|
||||
"@vueuse/shared" "5.0.2"
|
||||
"@vueuse/shared" "5.0.3"
|
||||
vue-demi "*"
|
||||
optionalDependencies:
|
||||
axios "^0.21.1"
|
||||
|
@ -987,10 +987,10 @@
|
|||
qrcode "^1.4.4"
|
||||
universal-cookie "^4.0.4"
|
||||
|
||||
"@vueuse/shared@5.0.2":
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-5.0.2.tgz#274c2bf163d25eb7fd2fc51f23088a2b7f060594"
|
||||
integrity sha512-S1hRRmEdipjTD4DbXgPdw4ZZYebU/nDi75vNP3Ibpa1irW3NUNUKOT/TWnwRHLQvXquUtdvalhI8D9Db+czZJg==
|
||||
"@vueuse/shared@5.0.3":
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-5.0.3.tgz#31613951d5036459650ad8d47a9185e8950ea3c9"
|
||||
integrity sha512-aY93WPygr8H/4RB8YuOmAD83Y+faq7zwW10Kd9i0kD9zf5ysVP+32j09rF/mZVtGCa0CSM8ambPZMsEhCkRbwQ==
|
||||
dependencies:
|
||||
vue-demi "*"
|
||||
|
||||
|
@ -2324,10 +2324,10 @@ eslint-plugin-prettier@^3.4.0:
|
|||
dependencies:
|
||||
prettier-linter-helpers "^1.0.0"
|
||||
|
||||
eslint-plugin-vue@^7.11.0:
|
||||
version "7.11.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-7.11.0.tgz#c19b098899b7e3cd692beffbbe73611064ef1ea6"
|
||||
integrity sha512-Qwo8wilqnOXnG9B5auEiTstyaHefyhHd5lEhhxemwXoWsAxIW2yppzuVudowC5n+qn1nMLNV9TANkTthBK7Waw==
|
||||
eslint-plugin-vue@^7.11.1:
|
||||
version "7.11.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-7.11.1.tgz#77eb4b44032d5cca79f9af21d06991d8694a314a"
|
||||
integrity sha512-lbw3vkEAGqYjqd1HpPFWHXtYaS8mILTJ5KOpJfRxO3Fo7o0wCf1zD7vSOasbm6nTA9xIgvZQ4VcyGIzQXxznHw==
|
||||
dependencies:
|
||||
eslint-utils "^2.1.0"
|
||||
natural-compare "^1.4.0"
|
||||
|
|
Loading…
Reference in New Issue