Browse Source

refactor: use setup

main
xingyu 2 years ago
parent
commit
b808fceb5f
  1. 287
      src/components/FormDesign/src/components/VFormDesign/components/ComponentProps.vue
  2. 48
      src/components/FormDesign/src/components/VFormDesign/components/FormItemColumnProps.vue
  3. 76
      src/components/FormDesign/src/components/VFormDesign/components/FormItemProps.vue
  4. 39
      src/components/FormDesign/src/components/VFormDesign/components/FormNode.vue
  5. 62
      src/components/FormDesign/src/components/VFormDesign/components/LayoutItem.vue
  6. 80
      src/components/FormDesign/src/components/VFormDesign/components/PreviewCode.vue
  7. 48
      src/components/FormDesign/src/components/VFormDesign/modules/CollapseItem.vue
  8. 71
      src/components/FormDesign/src/components/VFormDesign/modules/FormComponentPanel.vue
  9. 28
      src/components/FormDesign/src/components/VFormDesign/modules/PropsPanel.vue
  10. 105
      src/components/FormDesign/src/components/VFormDesign/modules/Toolbar.vue
  11. 49
      src/layouts/default/header/components/lock/LockModal.vue
  12. 410
      src/layouts/default/sider/MixSider.vue

287
src/components/FormDesign/src/components/VFormDesign/components/ComponentProps.vue

@ -1,22 +1,9 @@
<!-- <!--
* @Description: 组件属性控件 * @Description: 组件属性控件
--> -->
<script lang="ts"> <script lang="ts" setup>
import { import { Checkbox, Col, Empty, Form, FormItem, Select } from 'ant-design-vue'
Checkbox, import { computed, ref, watch } from 'vue'
Col,
Empty,
Form,
FormItem,
Input,
InputNumber,
RadioGroup,
Row,
Select,
Switch,
} from 'ant-design-vue'
import RadioButtonGroup from '/@/components/Form/src/components/RadioButtonGroup.vue'
import { computed, defineComponent, ref, watch } from 'vue'
import { useFormDesignState } from '../../../hooks/useFormDesignState' import { useFormDesignState } from '../../../hooks/useFormDesignState'
import { import {
baseComponentAttrs, baseComponentAttrs,
@ -28,145 +15,116 @@ import { formItemsForEach, remove } from '../../../utils'
import type { IBaseFormAttrs } from '../config/formItemPropsConfig' import type { IBaseFormAttrs } from '../config/formItemPropsConfig'
import FormOptions from './FormOptions.vue' import FormOptions from './FormOptions.vue'
export default defineComponent({ const { formConfig } = useFormDesignState()
name: 'ComponentProps', // compuated
components: { const allOptions = ref([] as Omit<IBaseFormAttrs, 'tag'>[])
FormOptions, function showControlAttrs(includes: string[] | undefined) {
Empty, if (!includes)
Input, return true
Form, return includes.includes(formConfig.value.currentItem!.component)
FormItem, }
Switch,
Checkbox, if (formConfig.value.currentItem) {
Select, formConfig.value.currentItem.componentProps
InputNumber, = formConfig.value.currentItem.componentProps || {}
RadioGroup, }
RadioButtonGroup,
Col, watch(
Row, () => formConfig.value.currentItem?.field,
(_newValue, oldValue) => {
formConfig.value.schemas
&& formItemsForEach(formConfig.value.schemas, (item) => {
if (item.link) {
const index = item.link.findIndex(linkItem => linkItem === oldValue)
index !== -1 && remove(item.link, index)
}
})
}, },
setup() { )
// compuated
watch(
const allOptions = ref([] as Omit<IBaseFormAttrs, 'tag'>[]) () => formConfig.value.currentItem && formConfig.value.currentItem.component,
const showControlAttrs = (includes: string[] | undefined) => { () => {
if (!includes) allOptions.value = []
return true baseComponentControlAttrs.forEach((item) => {
return includes.includes(formConfig.value.currentItem!.component) item.category = 'control'
} if (!item.includes) {
// include
const { formConfig } = useFormDesignState()
allOptions.value.push(item)
if (formConfig.value.currentItem) { }
formConfig.value.currentItem.componentProps else if (item.includes.includes(formConfig.value.currentItem!.component)) {
= formConfig.value.currentItem.componentProps || {} // include
} allOptions.value.push(item)
}
watch( })
() => formConfig.value.currentItem?.field,
(_newValue, oldValue) => {
formConfig.value.schemas
&& formItemsForEach(formConfig.value.schemas, (item) => {
if (item.link) {
const index = item.link.findIndex(linkItem => linkItem === oldValue)
index !== -1 && remove(item.link, index)
}
})
},
)
watch( baseComponentCommonAttrs.forEach((item) => {
() => formConfig.value.currentItem && formConfig.value.currentItem.component, item.category = 'input'
() => { if (item.includes) {
allOptions.value = [] if (item.includes.includes(formConfig.value.currentItem!.component))
baseComponentControlAttrs.forEach((item) => { allOptions.value.push(item)
item.category = 'control' }
if (!item.includes) { else if (item.exclude) {
// include if (!item.exclude.includes(formConfig.value.currentItem!.component))
allOptions.value.push(item)
}
else {
allOptions.value.push(item)
}
})
baseComponentAttrs[formConfig.value.currentItem!.component]
&& baseComponentAttrs[formConfig.value.currentItem!.component].forEach(async (item) => {
if (item.component) {
if (['Switch', 'Checkbox', 'Radio'].includes(item.component)) {
item.category = 'control'
allOptions.value.push(item) allOptions.value.push(item)
} }
else if (item.includes.includes(formConfig.value.currentItem!.component)) {
// include
allOptions.value.push(item)
}
})
baseComponentCommonAttrs.forEach((item) => {
item.category = 'input'
if (item.includes) {
if (item.includes.includes(formConfig.value.currentItem!.component))
allOptions.value.push(item)
}
else if (item.exclude) {
if (!item.exclude.includes(formConfig.value.currentItem!.component))
allOptions.value.push(item)
}
else { else {
item.category = 'input'
allOptions.value.push(item) allOptions.value.push(item)
} }
}) }
baseComponentAttrs[formConfig.value.currentItem!.component]
&& baseComponentAttrs[formConfig.value.currentItem!.component].forEach(async (item) => {
if (item.component) {
if (['Switch', 'Checkbox', 'Radio'].includes(item.component)) {
item.category = 'control'
allOptions.value.push(item)
}
else {
item.category = 'input'
allOptions.value.push(item)
}
}
})
},
{
immediate: true,
},
)
//
const controlOptions = computed(() => {
return allOptions.value.filter((item) => {
return item.category === 'control'
}) })
}) },
{
immediate: true,
},
)
//
const controlOptions = computed(() => {
return allOptions.value.filter((item) => {
return item.category === 'control'
})
})
// //
const inputOptions = computed(() => { const inputOptions = computed(() => {
return allOptions.value.filter((item) => { return allOptions.value.filter((item) => {
return item.category === 'input' return item.category === 'input'
}) })
}) })
watch( watch(
() => formConfig.value.currentItem!.componentProps, () => formConfig.value.currentItem!.componentProps,
() => { () => {
const func = componentPropsFuncs[formConfig.value.currentItem!.component] const func = componentPropsFuncs[formConfig.value.currentItem!.component]
if (func) if (func)
func(formConfig.value.currentItem!.componentProps, allOptions.value) func(formConfig.value.currentItem!.componentProps, allOptions.value)
}, },
{ {
immediate: true, immediate: true,
deep: true, deep: true,
},
)
const linkOptions = computed(() => {
return (
formConfig.value.schemas
&& formConfig.value.schemas
.filter(item => item.key !== formConfig.value.currentItem!.key)
.map(({ label, field }) => ({ label: `${label}/${field}`, value: field }))
)
})
return {
formConfig,
showControlAttrs,
linkOptions,
controlOptions,
inputOptions,
}
}, },
)
const linkOptions = computed(() => {
return (
formConfig.value.schemas
&& formConfig.value.schemas
.filter(item => item.key !== formConfig.value.currentItem!.key)
.map(({ label, field }) => ({ label: `${label}/${field}`, value: field }))
)
}) })
</script> </script>
@ -184,27 +142,20 @@ export default defineComponent({
<div v-if="item.children"> <div v-if="item.children">
<component <component
v-bind="child.componentProps" v-bind="child.componentProps" :is="child.component" v-for="(child, index) of item.children"
:is="child.component" :key="index" v-model:value="formConfig.currentItem.componentProps[item.name][index]"
v-for="(child, index) of item.children"
:key="index"
v-model:value="formConfig.currentItem.componentProps[item.name][index]"
/> />
</div> </div>
<!-- 如果不是数组则正常处理属性值 --> <!-- 如果不是数组则正常处理属性值 -->
<component <component
v-bind="item.componentProps" v-bind="item.componentProps" :is="item.component" v-else
:is="item.component" v-model:value="formConfig.currentItem.componentProps[item.name]" class="component-prop"
v-else
v-model:value="formConfig.currentItem.componentProps[item.name]"
class="component-prop"
/> />
</FormItem> </FormItem>
<FormItem label="控制属性"> <FormItem label="控制属性">
<Col v-for="item in controlOptions" :key="item.name"> <Col v-for="item in controlOptions" :key="item.name">
<Checkbox <Checkbox
v-if="showControlAttrs(item.includes)" v-if="showControlAttrs(item.includes)" v-bind="item.componentProps"
v-bind="item.componentProps"
v-model:checked="formConfig.currentItem.componentProps[item.name]" v-model:checked="formConfig.currentItem.componentProps[item.name]"
> >
{{ item.label }} {{ item.label }}
@ -213,25 +164,19 @@ export default defineComponent({
</FormItem> </FormItem>
</div> </div>
<FormItem label="关联字段"> <FormItem label="关联字段">
<Select <Select v-model:value="formConfig.currentItem.link" mode="multiple" :options="linkOptions" />
v-model:value="formConfig.currentItem.link"
mode="multiple"
:options="linkOptions"
/>
</FormItem> </FormItem>
<FormItem <FormItem
v-if=" v-if="[
[ 'Select',
'Select', 'CheckboxGroup',
'CheckboxGroup', 'RadioGroup',
'RadioGroup', 'TreeSelect',
'TreeSelect', 'Cascader',
'Cascader', 'AutoComplete',
'AutoComplete', ].includes(formConfig.currentItem.component)
].includes(formConfig.currentItem.component) " label="选项"
"
label="选项"
> >
<FormOptions /> <FormOptions />
</FormItem> </FormItem>

48
src/components/FormDesign/src/components/VFormDesign/components/FormItemColumnProps.vue

@ -1,45 +1,20 @@
<!-- <!--
* @Description: 表单项属性 * @Description: 表单项属性
--> -->
<script lang="ts"> <script lang="ts" setup>
import { defineComponent } from 'vue' import { Empty, Form, FormItem } from 'ant-design-vue'
import { Checkbox, Empty, Form, FormItem, Input, Select, Slider, Switch } from 'ant-design-vue'
import { isArray } from 'lodash-es' import { isArray } from 'lodash-es'
import { baseItemColumnProps } from '../config/formItemPropsConfig' import { baseItemColumnProps } from '../config/formItemPropsConfig'
import { useFormDesignState } from '../../../hooks/useFormDesignState' import { useFormDesignState } from '../../../hooks/useFormDesignState'
import RuleProps from './RuleProps.vue'
export default defineComponent({ const { formConfig } = useFormDesignState()
name: 'FormItemProps', function showProps(exclude: string[] | undefined) {
components: { if (!exclude)
RuleProps, return true
Empty,
Input,
Form,
FormItem,
Switch,
Checkbox,
Select,
Slider,
},
// props: {} as PropsOptions,
setup() { return isArray(exclude) ? !exclude.includes(formConfig.value.currentItem!.component) : true
const { formConfig } = useFormDesignState() }
const showProps = (exclude: string[] | undefined) => {
if (!exclude)
return true
return isArray(exclude) ? !exclude.includes(formConfig.value.currentItem!.component) : true
}
return {
baseItemColumnProps,
formConfig,
showProps,
}
},
})
</script> </script>
<template> <template>
@ -50,11 +25,8 @@ export default defineComponent({
<div v-for="item of baseItemColumnProps" :key="item.name"> <div v-for="item of baseItemColumnProps" :key="item.name">
<FormItem v-if="showProps(item.exclude)" :label="item.label"> <FormItem v-if="showProps(item.exclude)" :label="item.label">
<component <component
v-bind="item.componentProps" v-bind="item.componentProps" :is="item.component" v-if="formConfig.currentItem.colProps"
:is="item.component" v-model:value="formConfig.currentItem.colProps[item.name]" class="component-props"
v-if="formConfig.currentItem.colProps"
v-model:value="formConfig.currentItem.colProps[item.name]"
class="component-props"
/> />
</FormItem> </FormItem>
</div> </div>

76
src/components/FormDesign/src/components/VFormDesign/components/FormItemProps.vue

@ -1,8 +1,8 @@
<!-- <!--
* @Description: 表单项属性控件属性面板 * @Description: 表单项属性控件属性面板
--> -->
<script lang="ts"> <script lang="ts" setup>
import { computed, defineComponent, watch } from 'vue' import { computed, watch } from 'vue'
import { import {
Checkbox, Checkbox,
Col, Col,
@ -10,9 +10,6 @@ import {
Form, Form,
FormItem, FormItem,
Input, Input,
RadioGroup,
Select,
Slider,
Switch, Switch,
} from 'ant-design-vue' } from 'ant-design-vue'
import { isArray } from 'lodash-es' import { isArray } from 'lodash-es'
@ -26,61 +23,32 @@ import {
import { useFormDesignState } from '../../../hooks/useFormDesignState' import { useFormDesignState } from '../../../hooks/useFormDesignState'
import RuleProps from './RuleProps.vue' import RuleProps from './RuleProps.vue'
export default defineComponent({ const { formConfig } = useFormDesignState()
name: 'FormItemProps',
components: {
RuleProps,
Empty,
Input,
Form,
FormItem,
Switch,
Checkbox,
Select,
Slider,
Col,
RadioGroup,
},
// props: {} as PropsOptions,
setup() {
const { formConfig } = useFormDesignState()
watch( watch(
() => formConfig.value, () => formConfig.value,
() => { () => {
if (formConfig.value.currentItem) { if (formConfig.value.currentItem) {
formConfig.value.currentItem.itemProps = formConfig.value.currentItem.itemProps || {} formConfig.value.currentItem.itemProps = formConfig.value.currentItem.itemProps || {}
formConfig.value.currentItem.itemProps.labelCol formConfig.value.currentItem.itemProps.labelCol
= formConfig.value.currentItem.itemProps.labelCol || {} = formConfig.value.currentItem.itemProps.labelCol || {}
formConfig.value.currentItem.itemProps.wrapperCol formConfig.value.currentItem.itemProps.wrapperCol
= formConfig.value.currentItem.itemProps.wrapperCol || {} = formConfig.value.currentItem.itemProps.wrapperCol || {}
}
},
{ deep: true, immediate: true },
)
const showProps = (exclude: string[] | undefined) => {
if (!exclude)
return true
return isArray(exclude) ? !exclude.includes(formConfig.value.currentItem!.component) : true
} }
},
{ deep: true, immediate: true },
)
function showProps(exclude: string[] | undefined) {
if (!exclude)
return true
const controlPropsList = computed(() => { return isArray(exclude) ? !exclude.includes(formConfig.value.currentItem!.component) : true
return baseFormItemControlAttrs.filter((item) => { }
return showProps(item.exclude)
})
})
return { const controlPropsList = computed(() => {
baseFormItemProps, return baseFormItemControlAttrs.filter((item) => {
advanceFormItemProps, return showProps(item.exclude)
advanceFormItemColProps, })
formConfig,
controlPropsList,
showProps,
}
},
}) })
</script> </script>

39
src/components/FormDesign/src/components/VFormDesign/components/FormNode.vue

@ -1,42 +1,29 @@
<!-- <!--
* @Description: 拖拽节点控件 * @Description: 拖拽节点控件
--> -->
<script lang="ts"> <script lang="ts" setup>
import type { PropType } from 'vue' import type { PropType } from 'vue'
import { defineComponent, reactive, toRefs } from 'vue'
import type { IVFormComponent } from '../../../typings/v-form-component' import type { IVFormComponent } from '../../../typings/v-form-component'
import { useFormDesignState } from '../../../hooks/useFormDesignState' import { useFormDesignState } from '../../../hooks/useFormDesignState'
import VFormItem from '../../VFormItem/index.vue' import VFormItem from '../../VFormItem/index.vue'
import FormNodeOperate from './FormNodeOperate.vue' import FormNodeOperate from './FormNodeOperate.vue'
// import VFormItem from '../../VFormItem/vFormItem.vue'; const props = defineProps(
export default defineComponent({ {
name: 'FormNode',
components: {
VFormItem,
FormNodeOperate,
},
props: {
schema: { schema: {
type: Object as PropType<IVFormComponent>, type: Object as PropType<IVFormComponent>,
required: true, required: true,
}, },
},
setup(props) {
const { formConfig, formDesignMethods } = useFormDesignState()
const state = reactive({})
// formDesignMethods
const handleSelectItem = () => {
// formDesignMethods
formDesignMethods.handleSetSelectItem(props.schema)
}
return {
...toRefs(state),
handleSelectItem,
formConfig,
}
}, },
})
)
const { formConfig, formDesignMethods } = useFormDesignState()
// formDesignMethods
function handleSelectItem() {
// formDesignMethods
formDesignMethods.handleSetSelectItem(props.schema)
}
</script> </script>
<template> <template>

62
src/components/FormDesign/src/components/VFormDesign/components/LayoutItem.vue

@ -3,9 +3,9 @@
* 千万不要在template下面的第一行加注释因为这里拖动的第一个元素 * 千万不要在template下面的第一行加注释因为这里拖动的第一个元素
--> -->
<script lang="ts"> <script lang="ts" setup>
import type { PropType } from 'vue' import type { PropType } from 'vue'
import { computed, defineComponent, reactive, toRefs } from 'vue' import { computed } from 'vue'
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
import { Col, Row } from 'ant-design-vue' import { Col, Row } from 'ant-design-vue'
import { useFormDesignState } from '../../../hooks/useFormDesignState' import { useFormDesignState } from '../../../hooks/useFormDesignState'
@ -13,16 +13,8 @@ import type { IVFormComponent } from '../../../typings/v-form-component'
import FormNode from './FormNode.vue' import FormNode from './FormNode.vue'
import FormNodeOperate from './FormNodeOperate.vue' import FormNodeOperate from './FormNodeOperate.vue'
export default defineComponent({ const props = defineProps(
name: 'LayoutItem', {
components: {
FormNode,
FormNodeOperate,
Draggable: draggable,
Row,
Col,
},
props: {
schema: { schema: {
type: Object as PropType<IVFormComponent>, type: Object as PropType<IVFormComponent>,
required: true, required: true,
@ -32,33 +24,23 @@ export default defineComponent({
required: true, required: true,
}, },
}, },
emits: ['dragStart', 'handleColAdd', 'handle-copy', 'handle-delete'], )
setup(props) {
const {
formDesignMethods: { handleSetSelectItem },
formConfig,
} = useFormDesignState()
const state = reactive({})
const colPropsComputed = computed(() => {
const { colProps = {} } = props.schema
return colProps
})
const list1 = computed(() => props.schema.columns) const emit = defineEmits(['dragStart', 'handleColAdd', 'handle-copy', 'handle-delete'])
// AColdiv const Draggable = draggable
const layoutTag = computed(() => {
return formConfig.value.layout === 'horizontal' ? 'Col' : 'div'
})
return { const { formDesignMethods: { handleSetSelectItem }, formConfig } = useFormDesignState()
...toRefs(state), const colPropsComputed = computed(() => {
colPropsComputed, const { colProps = {} } = props.schema
handleSetSelectItem, return colProps
layoutTag, })
list1,
} const list1 = computed(() => props.schema.columns)
},
// AColdiv
const layoutTag = computed(() => {
return formConfig.value.layout === 'horizontal' ? 'Col' : 'div'
}) })
</script> </script>
@ -87,8 +69,8 @@ export default defineComponent({
class="drag-move" class="drag-move"
:schema="element" :schema="element"
:current-item="currentItem" :current-item="currentItem"
@handle-copy="$emit('handle-copy')" @handle-copy="emit('handle-copy')"
@handle-delete="$emit('handle-delete')" @handle-delete="emit('handle-delete')"
/> />
</template> </template>
</Draggable> </Draggable>
@ -102,8 +84,8 @@ export default defineComponent({
:key="schema.key" :key="schema.key"
:schema="schema" :schema="schema"
:current-item="currentItem" :current-item="currentItem"
@handle-copy="$emit('handle-copy')" @handle-copy="emit('handle-copy')"
@handle-delete="$emit('handle-delete')" @handle-delete="emit('handle-delete')"
/> />
</Col> </Col>
</template> </template>

80
src/components/FormDesign/src/components/VFormDesign/components/PreviewCode.vue

@ -1,16 +1,12 @@
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, reactive, ref, toRefs, unref } from 'vue' import { ref, unref } from 'vue'
import { CodeEditor, MODE } from '@/components/CodeEditor' import { CodeEditor, MODE } from '@/components/CodeEditor'
import { useCopyToClipboard } from '@/hooks/web/useCopyToClipboard' import { useCopyToClipboard } from '@/hooks/web/useCopyToClipboard'
import { useMessage } from '@/hooks/web/useMessage' import { useMessage } from '@/hooks/web/useMessage'
export default defineComponent({ const props = defineProps(
name: 'PreviewCode', {
components: {
CodeEditor,
},
props: {
fileFormat: { fileFormat: {
type: String, type: String,
default: 'json', default: 'json',
@ -20,51 +16,37 @@ export default defineComponent({
default: '', default: '',
}, },
}, },
setup(props) { )
const state = reactive({
open: false,
})
const myEditor = ref(null)
const exportData = (data: string, fileName = `file.${props.fileFormat}`) => { const myEditor = ref(null)
let content = 'data:text/csv;charset=utf-8,'
content += data
const encodedUri = encodeURI(content)
const actions = document.createElement('a')
actions.setAttribute('href', encodedUri)
actions.setAttribute('download', fileName)
actions.click()
}
const handleExportJson = () => { function exportData(data: string, fileName = `file.${props.fileFormat}`) {
exportData(props.editorJson) let content = 'data:text/csv;charset=utf-8,'
} content += data
const { clipboardRef, copiedRef } = useCopyToClipboard() const encodedUri = encodeURI(content)
const { createMessage } = useMessage() const actions = document.createElement('a')
actions.setAttribute('href', encodedUri)
actions.setAttribute('download', fileName)
actions.click()
}
const handleCopyJson = () => { function handleExportJson() {
// exportData(props.editorJson)
const value = props.editorJson }
if (!value) { const { clipboardRef, copiedRef } = useCopyToClipboard()
createMessage.warning('代码为空!') const { createMessage } = useMessage()
return
}
clipboardRef.value = value
if (unref(copiedRef))
createMessage.warning('复制成功!')
}
return { function handleCopyJson() {
...toRefs(state), //
myEditor, const value = props.editorJson
exportData, if (!value) {
handleCopyJson, createMessage.warning('代码为空!')
handleExportJson, return
MODE, }
} clipboardRef.value = value
}, if (unref(copiedRef))
}) createMessage.warning('复制成功!')
}
</script> </script>
<template> <template>

48
src/components/FormDesign/src/components/VFormDesign/modules/CollapseItem.vue

@ -1,16 +1,13 @@
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, reactive } from 'vue'
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
import type { IVFormComponent } from '../../../typings/v-form-component' import type { IVFormComponent } from '../../../typings/v-form-component'
import { Icon } from '@/components/Icon' import { Icon } from '@/components/Icon'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
export default defineComponent({ const props = defineProps(
name: 'CollapseItem', {
components: { Draggable: draggable, Icon },
props: {
list: { list: {
type: [Array], type: Array as unknown as any[],
default: () => [], default: () => [],
}, },
handleListPush: { handleListPush: {
@ -18,24 +15,23 @@ export default defineComponent({
default: null, default: null,
}, },
}, },
setup(props, { emit }) { )
const { prefixCls } = useDesign('form-design-collapse-item') const emit = defineEmits(['start', 'add-attrs', 'handle-list-push'])
const Draggable = draggable
const state = reactive({}) const { prefixCls } = useDesign('form-design-collapse-item')
const handleStart = (e: any, list1: IVFormComponent[]) => {
emit('start', list1[e.oldIndex].component) function handleStart(e: any, list1: IVFormComponent[]) {
} emit('start', list1[e.oldIndex].component)
const handleAdd = (e: any) => { }
console.log(e) function handleAdd(e: any) {
} console.log(e)
// https://github.com/SortableJS/vue.draggable.next }
// https://github.com/SortableJS/vue.draggable.next/blob/master/example/components/custom-clone.vue // https://github.com/SortableJS/vue.draggable.next
const cloneItem = (one) => { // https://github.com/SortableJS/vue.draggable.next/blob/master/example/components/custom-clone.vue
return props.handleListPush(one) function cloneItem(one) {
} return props.handleListPush(one)
return { prefixCls, state, handleStart, handleAdd, cloneItem } }
},
})
</script> </script>
<template> <template>
@ -57,8 +53,8 @@ export default defineComponent({
<template #item="{ element, index }"> <template #item="{ element, index }">
<li <li
class="bs-box text-ellipsis" class="bs-box text-ellipsis"
@dragstart="$emit('add-attrs', list, index)" @dragstart="emit('add-attrs', list, index)"
@click="$emit('handle-list-push', element)" @click="emit('handle-list-push', element)"
> >
<!-- <svg v-if="element.icon.indexOf('icon-') > -1" class="icon" aria-hidden="true"> <!-- <svg v-if="element.icon.indexOf('icon-') > -1" class="icon" aria-hidden="true">
<use :xlink:href="`#${element.icon}`" /> <use :xlink:href="`#${element.icon}`" />

71
src/components/FormDesign/src/components/VFormDesign/modules/FormComponentPanel.vue

@ -2,7 +2,7 @@
* @Description: 中间表单布局面板 * @Description: 中间表单布局面板
* https://github.com/SortableJS/vue.draggable.next/issues/138 * https://github.com/SortableJS/vue.draggable.next/issues/138
--> -->
<script lang="ts"> <script lang="ts" setup>
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
import { computed, defineComponent } from 'vue' import { computed, defineComponent } from 'vue'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
@ -10,52 +10,37 @@ import { Empty, Form } from 'ant-design-vue'
import { useFormDesignState } from '../../../hooks/useFormDesignState' import { useFormDesignState } from '../../../hooks/useFormDesignState'
import LayoutItem from '../components/LayoutItem.vue' import LayoutItem from '../components/LayoutItem.vue'
export default defineComponent({ const emit = defineEmits(['handleSetSelectItem'])
name: 'FormComponentPanel',
components: {
LayoutItem,
Draggable: draggable,
Form,
Empty,
},
emits: ['handleSetSelectItem'],
setup(_, { emit }) {
const { formConfig } = useFormDesignState()
/**
* 拖拽完成事件
* @param newIndex
*/
const addItem = ({ newIndex }: any) => {
formConfig.value.schemas = formConfig.value.schemas || []
const schemas = formConfig.value.schemas
schemas[newIndex] = cloneDeep(schemas[newIndex])
emit('handleSetSelectItem', schemas[newIndex])
}
/** const Draggable = draggable
* 拖拽开始事件
* @param e {Object} 事件对象
*/
const handleDragStart = (e: any) => {
emit('handleSetSelectItem', formConfig.value.schemas[e.oldIndex])
}
// currentItem const { formConfig } = useFormDesignState()
// AColdiv /**
const layoutTag = computed(() => { * 拖拽完成事件
return formConfig.value.layout === 'horizontal' ? 'Col' : 'div' * @param newIndex
}) */
function addItem({ newIndex }: any) {
formConfig.value.schemas = formConfig.value.schemas || []
return { const schemas = formConfig.value.schemas
addItem, schemas[newIndex] = cloneDeep(schemas[newIndex])
handleDragStart, emit('handleSetSelectItem', schemas[newIndex])
formConfig, }
layoutTag,
} /**
}, * 拖拽开始事件
* @param e {Object} 事件对象
*/
function handleDragStart(e: any) {
emit('handleSetSelectItem', formConfig.value.schemas[e.oldIndex])
}
// currentItem
// AColdiv
const layoutTag = computed(() => {
return formConfig.value.layout === 'horizontal' ? 'Col' : 'div'
}) })
</script> </script>

28
src/components/FormDesign/src/components/VFormDesign/modules/PropsPanel.vue

@ -1,8 +1,8 @@
<!-- <!--
* @Description: 右侧属性配置面板 * @Description: 右侧属性配置面板
--> -->
<script lang="ts"> <script lang="ts" setup>
import { computed, defineComponent } from 'vue' import { computed } from 'vue'
import { TabPane, Tabs } from 'ant-design-vue' import { TabPane, Tabs } from 'ant-design-vue'
import FormProps from '../components/FormProps.vue' import FormProps from '../components/FormProps.vue'
import FormItemProps from '../components/FormItemProps.vue' import FormItemProps from '../components/FormItemProps.vue'
@ -15,25 +15,11 @@ type ChangeTabKey = 1 | 2
export interface IPropsPanel { export interface IPropsPanel {
changeTab: (key: ChangeTabKey) => void changeTab: (key: ChangeTabKey) => void
} }
export default defineComponent({ const { formConfig } = useFormDesignState()
name: 'PropsPanel', const slotProps = computed(() => {
components: { return customComponents.find(
FormProps, item => item.component === formConfig.value.currentItem?.component,
FormItemProps, )
ComponentProps,
ComponentColumnProps,
Tabs,
TabPane,
},
setup() {
const { formConfig } = useFormDesignState()
const slotProps = computed(() => {
return customComponents.find(
item => item.component === formConfig.value.currentItem?.component,
)
})
return { formConfig, customComponents, slotProps }
},
}) })
</script> </script>

105
src/components/FormDesign/src/components/VFormDesign/modules/Toolbar.vue

@ -1,8 +1,8 @@
<!-- <!--
* @Description: 工具栏 * @Description: 工具栏
--> -->
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, inject, reactive, toRefs } from 'vue' import { inject, reactive } from 'vue'
import type { UseRefHistoryReturn } from '@vueuse/core' import type { UseRefHistoryReturn } from '@vueuse/core'
import { Divider, Tooltip } from 'ant-design-vue' import { Divider, Tooltip } from 'ant-design-vue'
import type { IFormConfig } from '../../../typings/v-form-component' import type { IFormConfig } from '../../../typings/v-form-component'
@ -15,62 +15,51 @@ interface IToolbarsConfig {
event: string event: string
} }
export default defineComponent({ const state = reactive<{
name: 'OperatingArea', toolbarsConfigs: IToolbarsConfig[]
components: { }>({
Tooltip, toolbarsConfigs: [
Icon, {
Divider, title: '预览-支持布局',
}, type: 'preview',
setup() { event: 'handlePreview',
const state = reactive<{ icon: 'ant-design:chrome-filled',
toolbarsConfigs: IToolbarsConfig[] },
}>({ {
toolbarsConfigs: [ title: '预览-不支持布局',
{ type: 'preview',
title: '预览-支持布局', event: 'handlePreview2',
type: 'preview', icon: 'ant-design:chrome-filled',
event: 'handlePreview', },
icon: 'ant-design:chrome-filled', {
}, title: '导入JSON',
{ type: 'importJson',
title: '预览-不支持布局', event: 'handleOpenImportJsonModal',
type: 'preview', icon: 'ant-design:import-outlined',
event: 'handlePreview2', },
icon: 'ant-design:chrome-filled', {
}, title: '生成JSON',
{ type: 'exportJson',
title: '导入JSON', event: 'handleOpenJsonModal',
type: 'importJson', icon: 'ant-design:export-outlined',
event: 'handleOpenImportJsonModal', },
icon: 'ant-design:import-outlined', {
}, title: '生成代码',
{ type: 'exportCode',
title: '生成JSON', event: 'handleOpenCodeModal',
type: 'exportJson', icon: 'ant-design:code-filled',
event: 'handleOpenJsonModal', },
icon: 'ant-design:export-outlined', {
}, title: '清空',
{ type: 'reset',
title: '生成代码', event: 'handleClearFormItems',
type: 'exportCode', icon: 'ant-design:clear-outlined',
event: 'handleOpenCodeModal', },
icon: 'ant-design:code-filled', ],
},
{
title: '清空',
type: 'reset',
event: 'handleClearFormItems',
icon: 'ant-design:clear-outlined',
},
],
})
const historyRef = inject('historyReturn') as UseRefHistoryReturn<IFormConfig, IFormConfig>
const { undo, redo, canUndo, canRedo } = historyRef
return { ...toRefs(state), undo, redo, canUndo, canRedo }
},
}) })
const historyRef = inject('historyReturn') as UseRefHistoryReturn<IFormConfig, IFormConfig>
const { undo, redo, canUndo, canRedo } = historyRef
</script> </script>
<template> <template>
@ -78,7 +67,7 @@ export default defineComponent({
<!-- 头部操作按钮区域 start --> <!-- 头部操作按钮区域 start -->
<!-- 操作左侧区域 start --> <!-- 操作左侧区域 start -->
<div class="left-btn-box"> <div class="left-btn-box">
<Tooltip v-for="item in toolbarsConfigs" :key="item.icon" :title="item.title"> <Tooltip v-for="item in state.toolbarsConfigs" :key="item.icon" :title="item.title">
<a class="toolbar-text" @click="$emit(item.event)"> <a class="toolbar-text" @click="$emit(item.event)">
<Icon :icon="item.icon" /> <Icon :icon="item.icon" />
</a> </a>

49
src/layouts/default/header/components/lock/LockModal.vue

@ -1,7 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue' import { computed } from 'vue'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useDesign } from '@/hooks/web/useDesign'
import { BasicForm, useForm } from '@/components/Form' import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal' import { BasicModal, useModalInner } from '@/components/Modal'
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
@ -11,7 +10,6 @@ import headerImg from '@/assets/images/header.jpg'
defineOptions({ name: 'LockModal' }) defineOptions({ name: 'LockModal' })
const { t } = useI18n() const { t } = useI18n()
const { prefixCls } = useDesign('header-lock-modal')
const userStore = useUserStore() const userStore = useUserStore()
const lockStore = useLockStore() const lockStore = useLockStore()
@ -52,18 +50,18 @@ const avatar = computed(() => {
</script> </script>
<template> <template>
<BasicModal :footer="null" width="25%" :title="t('layout.header.lockScreen')" v-bind="$attrs" :class="prefixCls" @register="register"> <BasicModal :footer="null" width="25%" :title="t('layout.header.lockScreen')" v-bind="$attrs" @register="register">
<div :class="`${prefixCls}__entry`"> <div class="relative rounded-10 px-8 pb-8 pt-30">
<div :class="`${prefixCls}__header`"> <div class="absolute left-[calc(50%-45px)] top-0 w-auto text-center">
<img :src="avatar" :class="`${prefixCls}__header-img`"> <img :src="avatar" class="w-18 rounded-50%">
<p :class="`${prefixCls}__header-name`"> <p class="mt-2">
{{ getRealName }} {{ getRealName }}
</p> </p>
</div> </div>
<BasicForm @register="registerForm" /> <BasicForm @register="registerForm" />
<div :class="`${prefixCls}__footer`"> <div class="mt-4 text-center">
<a-button type="primary" block class="mt-2" @click="handleLock"> <a-button type="primary" block class="mt-2" @click="handleLock">
{{ t('layout.header.lockScreenBtn') }} {{ t('layout.header.lockScreenBtn') }}
</a-button> </a-button>
@ -71,38 +69,3 @@ const avatar = computed(() => {
</div> </div>
</BasicModal> </BasicModal>
</template> </template>
<style lang="less">
@prefix-cls: ~'@{namespace}-header-lock-modal';
.@{prefix-cls} {
&__entry {
position: relative;
//height: 240px;
padding: 130px 30px 30px;
border-radius: 10px;
}
&__header {
position: absolute;
top: 0;
left: calc(50% - 45px);
width: auto;
text-align: center;
&-img {
width: 70px;
border-radius: 50%;
}
&-name {
margin-top: 5px;
}
}
&__footer {
margin-top: 16px;
text-align: center;
}
}
</style>

410
src/layouts/default/sider/MixSider.vue

@ -1,7 +1,8 @@
<script lang="ts"> <script lang="ts" setup>
import type { CSSProperties } from 'vue' import type { CSSProperties } from 'vue'
import { computed, defineComponent, onMounted, ref, unref, watch } from 'vue' import { computed, onMounted, ref, unref, watch } from 'vue'
import type { RouteLocationNormalized } from 'vue-router' import type { RouteLocationNormalized } from 'vue-router'
import { onClickOutside } from '@vueuse/core'
import LayoutTrigger from '../trigger/index.vue' import LayoutTrigger from '../trigger/index.vue'
import { useDragLine } from './useLayoutSider' import { useDragLine } from './useLayoutSider'
import type { Menu } from '@/router/types' import type { Menu } from '@/router/types'
@ -16,257 +17,220 @@ import { useDesign } from '@/hooks/web/useDesign'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useGo } from '@/hooks/web/usePage' import { useGo } from '@/hooks/web/usePage'
import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '@/enums/appEnum' import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '@/enums/appEnum'
import clickOutside from '@/directives/clickOutside'
import { getChildrenMenus, getCurrentParentPath, getShallowMenus } from '@/router/menus' import { getChildrenMenus, getCurrentParentPath, getShallowMenus } from '@/router/menus'
import { listenerRouteChange } from '@/logics/mitt/routeChange' import { listenerRouteChange } from '@/logics/mitt/routeChange'
export default defineComponent({ const wrap = ref(null)
name: 'LayoutMixSider', const menuModules = ref<Menu[]>([])
components: { const activePath = ref('')
ScrollContainer, const childrenMenus = ref<Menu[]>([])
AppLogo, const openMenu = ref(false)
SimpleMenu, const dragBarRef = ref<ElRef>(null)
Icon, const sideRef = ref<ElRef>(null)
LayoutTrigger, const currentRoute = ref<Nullable<RouteLocationNormalized>>(null)
SimpleMenuTag,
}, const { prefixCls } = useDesign('layout-mix-sider')
directives: { const go = useGo()
clickOutside, const { t } = useI18n()
}, const {
setup() { getMenuWidth,
const menuModules = ref<Menu[]>([]) getCanDrag,
const activePath = ref('') getCloseMixSidebarOnChange,
const childrenMenus = ref<Menu[]>([]) getMenuTheme,
const openMenu = ref(false) getMixSideTrigger,
const dragBarRef = ref<ElRef>(null) getRealWidth,
const sideRef = ref<ElRef>(null) getMixSideFixed,
const currentRoute = ref<Nullable<RouteLocationNormalized>>(null) mixSideHasChildren,
setMenuSetting,
const { prefixCls } = useDesign('layout-mix-sider') getIsMixSidebar,
const go = useGo() getCollapsed,
const { t } = useI18n() } = useMenuSetting()
const {
getMenuWidth, const { title } = useGlobSetting()
getCanDrag, const permissionStore = usePermissionStore()
getCloseMixSidebarOnChange,
getMenuTheme, useDragLine(sideRef, dragBarRef, true)
getMixSideTrigger,
getRealWidth, const getMenuStyle = computed((): CSSProperties => {
getMixSideFixed, return {
mixSideHasChildren, width: unref(openMenu) ? `${unref(getMenuWidth)}px` : 0,
setMenuSetting, // eslint-disable-next-line @typescript-eslint/no-use-before-define
getIsMixSidebar, left: `${unref(getMixSideWidth)}px`,
getCollapsed, }
} = useMenuSetting() })
const { title } = useGlobSetting()
const permissionStore = usePermissionStore()
useDragLine(sideRef, dragBarRef, true)
const getMenuStyle = computed((): CSSProperties => {
return {
width: unref(openMenu) ? `${unref(getMenuWidth)}px` : 0,
// eslint-disable-next-line @typescript-eslint/no-use-before-define
left: `${unref(getMixSideWidth)}px`,
}
})
const getIsFixed = computed(() => {
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
mixSideHasChildren.value = unref(childrenMenus).length > 0
const isFixed = unref(getMixSideFixed) && unref(mixSideHasChildren)
if (isFixed)
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
openMenu.value = true
return isFixed const getIsFixed = computed(() => {
}) // eslint-disable-next-line vue/no-side-effects-in-computed-properties
mixSideHasChildren.value = unref(childrenMenus).length > 0
const getMixSideWidth = computed(() => { const isFixed = unref(getMixSideFixed) && unref(mixSideHasChildren)
return unref(getCollapsed) ? SIDE_BAR_MINI_WIDTH : SIDE_BAR_SHOW_TIT_MINI_WIDTH if (isFixed)
}) // eslint-disable-next-line vue/no-side-effects-in-computed-properties
openMenu.value = true
const getDomStyle = computed((): CSSProperties => {
const fixedWidth = unref(getIsFixed) ? unref(getRealWidth) : 0
const width = `${unref(getMixSideWidth) + fixedWidth}px`
return getWrapCommonStyle(width)
})
const getWrapStyle = computed((): CSSProperties => {
const width = `${unref(getMixSideWidth)}px`
return getWrapCommonStyle(width)
})
const getMenuEvents = computed(() => {
return !unref(getMixSideFixed)
? {
onMouseleave: () => {
setActive(true)
closeMenu()
},
}
: {}
})
const getShowDragBar = computed(() => unref(getCanDrag)) return isFixed
})
onMounted(async () => { const getMixSideWidth = computed(() => {
menuModules.value = await getShallowMenus() return unref(getCollapsed) ? SIDE_BAR_MINI_WIDTH : SIDE_BAR_SHOW_TIT_MINI_WIDTH
}) })
// Menu changes const getDomStyle = computed((): CSSProperties => {
watch( const fixedWidth = unref(getIsFixed) ? unref(getRealWidth) : 0
[() => permissionStore.getLastBuildMenuTime, () => permissionStore.getBackMenuList], const width = `${unref(getMixSideWidth) + fixedWidth}px`
async () => { return getWrapCommonStyle(width)
menuModules.value = await getShallowMenus() })
},
{
immediate: true,
},
)
listenerRouteChange((route) => { const getWrapStyle = computed((): CSSProperties => {
currentRoute.value = route const width = `${unref(getMixSideWidth)}px`
setActive(true) return getWrapCommonStyle(width)
if (unref(getCloseMixSidebarOnChange)) })
closeMenu()
})
function getWrapCommonStyle(width: string): CSSProperties {
return {
width,
maxWidth: width,
minWidth: width,
flex: `0 0 ${width}`,
}
}
// Process module menu click const getMenuEvents = computed(() => {
async function handleModuleClick(path: string, hover = false) { return !unref(getMixSideFixed)
const children = await getChildrenMenus(path) ? {
if (unref(activePath) === path) { onMouseleave: () => {
if (!hover) { setActive(true)
if (!unref(openMenu)) closeMenu()
openMenu.value = true },
else
closeMenu()
}
else {
if (!unref(openMenu))
openMenu.value = true
}
if (!unref(openMenu))
setActive()
}
else {
openMenu.value = true
activePath.value = path
} }
: {}
})
if (!children || children.length === 0) { const getShowDragBar = computed(() => unref(getCanDrag))
if (!hover)
go(path) onMounted(async () => {
childrenMenus.value = [] menuModules.value = await getShallowMenus()
})
// Menu changes
watch(
[() => permissionStore.getLastBuildMenuTime, () => permissionStore.getBackMenuList],
async () => {
menuModules.value = await getShallowMenus()
},
{
immediate: true,
},
)
listenerRouteChange((route) => {
currentRoute.value = route
setActive(true)
if (unref(getCloseMixSidebarOnChange))
closeMenu()
})
function getWrapCommonStyle(width: string): CSSProperties {
return {
width,
maxWidth: width,
minWidth: width,
flex: `0 0 ${width}`,
}
}
// Process module menu click
async function handleModuleClick(path: string, hover = false) {
const children = await getChildrenMenus(path)
if (unref(activePath) === path) {
if (!hover) {
if (!unref(openMenu))
openMenu.value = true
else
closeMenu() closeMenu()
return
}
childrenMenus.value = children
} }
else {
// Set the currently active menu and submenu if (!unref(openMenu))
async function setActive(setChildren = false) { openMenu.value = true
const path = currentRoute.value?.path
if (!path)
return
activePath.value = await getCurrentParentPath(path)
// hanldeModuleClick(parentPath);
if (unref(getIsMixSidebar)) {
const activeMenu = unref(menuModules).find(item => item.path === unref(activePath))
const p = activeMenu?.path
if (p) {
const children = await getChildrenMenus(p)
if (setChildren) {
childrenMenus.value = children
if (unref(getMixSideFixed))
openMenu.value = children.length > 0
}
if (children.length === 0)
childrenMenus.value = []
}
}
} }
if (!unref(openMenu))
setActive()
}
else {
openMenu.value = true
activePath.value = path
}
function handleMenuClick(path: string) { if (!children || children.length === 0) {
if (!hover)
go(path) go(path)
} childrenMenus.value = []
closeMenu()
function handleClickOutside() { return
setActive(true) }
closeMenu() childrenMenus.value = children
} }
function getItemEvents(item: Menu) { // Set the currently active menu and submenu
if (unref(getMixSideTrigger) === 'hover') { async function setActive(setChildren = false) {
return { const path = currentRoute.value?.path
onMouseenter: () => handleModuleClick(item.path, true), if (!path)
onClick: async () => { return
const children = await getChildrenMenus(item.path) activePath.value = await getCurrentParentPath(path)
if (item.path && (!children || children.length === 0)) // hanldeModuleClick(parentPath);
go(item.path) if (unref(getIsMixSidebar)) {
}, const activeMenu = unref(menuModules).find(item => item.path === unref(activePath))
} const p = activeMenu?.path
} if (p) {
return { const children = await getChildrenMenus(p)
onClick: () => handleModuleClick(item.path), if (setChildren) {
childrenMenus.value = children
if (unref(getMixSideFixed))
openMenu.value = children.length > 0
} }
if (children.length === 0)
childrenMenus.value = []
} }
}
}
function handleFixedMenu() { function handleMenuClick(path: string) {
setMenuSetting({ go(path)
mixSideFixed: !unref(getIsFixed), }
})
}
// Close menu function handleClickOutside() {
function closeMenu() { setActive(true)
if (!unref(getIsFixed)) closeMenu()
openMenu.value = false }
}
function getItemEvents(item: Menu) {
if (unref(getMixSideTrigger) === 'hover') {
return { return {
t, onMouseenter: () => handleModuleClick(item.path, true),
prefixCls, onClick: async () => {
menuModules, const children = await getChildrenMenus(item.path)
handleModuleClick, if (item.path && (!children || children.length === 0))
activePath, go(item.path)
childrenMenus, },
getShowDragBar,
handleMenuClick,
getMenuStyle,
handleClickOutside,
sideRef,
dragBarRef,
title,
openMenu,
getMenuTheme,
getItemEvents,
getMenuEvents,
getDomStyle,
handleFixedMenu,
getMixSideFixed,
getWrapStyle,
getCollapsed,
} }
}, }
return {
onClick: () => handleModuleClick(item.path),
}
}
function handleFixedMenu() {
setMenuSetting({
mixSideFixed: !unref(getIsFixed),
})
}
// Close menu
function closeMenu() {
if (!unref(getIsFixed))
openMenu.value = false
}
onClickOutside(wrap, () => {
handleClickOutside()
}) })
</script> </script>
<template> <template>
<div :class="`${prefixCls}-dom`" :style="getDomStyle" /> <div :class="`${prefixCls}-dom`" :style="getDomStyle" />
<div <div
v-click-outside="handleClickOutside" ref="wrap"
:style="getWrapStyle" :style="getWrapStyle"
:class="[ :class="[
prefixCls, prefixCls,