颜色配置表单

main
一杯沧海 1 year ago
parent b7d451b5fb
commit 32a7696b66
  1. 16
      src/App.vue
  2. 3
      src/engineer/components/Canvas.vue
  3. 124
      src/engineer/components/switch.vue
  4. 62
      src/engineer/configs/BackgroundConfig.vue
  5. 19
      src/engineer/configs/BaseConfig.vue
  6. 18
      src/engineer/configs/BooleanConfig.vue
  7. 21
      src/engineer/configs/ColorConfig.vue
  8. 69
      src/engineer/configs/ImageConfig.vue
  9. 13
      src/engineer/configs/ListConfig.vue
  10. 47
      src/engineer/configs/MarkConfig.vue
  11. 9
      src/engineer/configs/ObjectConfig.vue
  12. 6
      src/engineer/configs/render.ts
  13. 27
      src/engineer/utils/clone.ts
  14. 1
      src/engineer/utils/index.ts

@ -143,12 +143,14 @@ categories.value.unshift({
{ {
type: 'list', type: 'list',
field: 'items', field: 'items',
label: '', label: '轮播图设置',
help: '最多可添加10张图片,建议宽度750px;鼠标拖拽左侧圆点可调整图片顺序', help: '最多可添加10张图片,建议宽度750px;鼠标拖拽左侧圆点可调整图片顺序',
addable: true, addable: true,
configs: [{ configs: [{
type: 'image', type: 'image',
field: 'image', field: 'image',
label: '图片',
required: true,
// //
inlines: [{ inlines: [{
type: 'text', type: 'text',
@ -159,10 +161,8 @@ categories.value.unshift({
type: 'text', type: 'text',
field: 'link', field: 'link',
label: '链接', label: '链接',
// help: '', // "${label}" help: '请输入链接', // "${label}"
}], }],
label: '图片',
required: true,
}], }],
}, },
{ {
@ -207,14 +207,18 @@ categories.value.unshift({
items: [ items: [
{ {
image: '', image: '',
// title: '', title: '123',
// link: '', link: '123',
} }
], ],
indicator: { indicator: {
style: 'circle', style: 'circle',
color: '#000000', color: '#000000',
position: 'left', position: 'left',
},
background: {
enabled: true,
value: "#ffffff",
} }
}, },
theme: { theme: {

@ -2,6 +2,7 @@
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import Draggable from 'vuedraggable' import Draggable from 'vuedraggable'
import { useContext, getModule } from '../context' import { useContext, getModule } from '../context'
import { clone } from '../utils'
import { RenderBlock } from '../render' import { RenderBlock } from '../render'
import { DddView } from '../views' import { DddView } from '../views'
@ -73,7 +74,7 @@ function handleDragAdd(e: DraggableEvent): void {
ctx.activeBlockId = block.vid ctx.activeBlockId = block.vid
ctx.hoverViewId = '#canvas' ctx.hoverViewId = '#canvas'
ctx.configurator = 'block' ctx.configurator = 'block'
ctx.sources[block.vid] = module.init ?? {} ctx.sources[block.vid] = module?.init ? clone(module.init) : {}
key.value++ key.value++
} }

@ -0,0 +1,124 @@
<script lang="ts" setup>
import { computed, ref } from 'vue'
defineOptions({
name: 'Switch'
})
const props = withDefaults(
defineProps<{
size?: 'small' | 'medium' | 'large'
value?: boolean
loading?: boolean
defaultValue?: boolean
disabled?: boolean
bgActiveColor?: string
}>(),
{
size: 'medium',
defaultValue: false,
disabled: false,
bgActiveColor: '#0256FF'
}
)
const { _width } = formartSize(props.size)
const _bgActiveColor = computed(() => props.bgActiveColor)
const status = ref<boolean>()
status.value = props.value || props.defaultValue
const emits = defineEmits<{
(event: 'change', status: boolean): void
}>()
type tempObj = {
_width: string
_height: string
}
function formartSize(str: string): tempObj {
let _width = "", _height = ""
if (str === 'small') {
_width = "40px"
_height = "20px"
}
if (str === 'medium') {
_width = "40px"
_height = "20px"
}
if (str === 'large') {
_width = "40px"
_height = "20px"
}
return { _width, _height }
}
const handleClick = () => {
if (!props.disabled) {
status.value = !status.value
emits('change', status.value)
}
}
</script>
<template>
<div class="d-switch" :class="{ 'is-checked': status }">
<input
class="d-switch__input"
ref="input"
type="checkbox"
:checked="status"
@change="handleClick"
/>
<span class="d-switch_action"></span>
</div>
</template>
<style lang="less" scoped>
.d-switch {
position: relative;
height: 18px;
transition: #0256FF 0.2s;
width: v-bind(_width);
background: rgb(117, 117, 117);
border-radius: 10px;
display: inline-flex;
align-items: center;
vertical-align: middle;
.d-switch__input {
position: relative;
z-index: 1;
margin: 0;
width: 100%;
height: 100%;
opacity: 0;
}
.d-switch_action {
position: absolute;
transition: 0.2s;
left: 2px;
top: 2px;
z-index: 0;
height: 14px;
width: 14px;
background: #fff;
border-radius: 50%;
}
&.is-checked {
background: v-bind(_bgActiveColor);
.d-switch_action {
left: 100%;
background: #fff;
margin-left: -18px;
}
}
}
</style>

@ -1,62 +1,86 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from "vue"; import { computed, ref } from "vue";
import { useSource } from "../context" import { useSource } from "../context"
defineOptions({ defineOptions({
name: "DddBackgroundConfig" name: "DddBackgroundConfig"
}) })
const props = defineProps<{ const props = defineProps<{
field: string field: string
help?: string help?: string
features: string[] features: string[]
label?: string label?: string
}>() }>()
const tempString = useSource<string>(props.field)
const chooseType = ref<string>()
if(typeof tempString.value === 'string'){
chooseType.value = 'color'
}
const tempString = useSource(props.field)
const typeList = computed(() => { const typeList = computed(() => {
return props.features.map((item: any)=> { return props.features.map((item: any) => {
return { return {
value: item, value: item,
label: formart(item) label: formart(item)
} }
}) })
}) })
function formart(str: string) { function formart(str: string) {
const temp: Record<string, any> = { const temp: Record<string, any> = {
color: '纯色', color: '纯色',
gradient: '渐变', gradient: '渐变',
} }
return temp[str] return temp[str]
} }
</script> </script>
<template> <template>
<div>
<div class="box"> <div class="box">
<label>{{ label }}</label> <label>{{ label }}</label>
<div class="box-typeList"> <div class="box-typeList">
<div v-for="(item,index) in typeList" :key="index" class="box-typeList-box"> <div v-for="(item, index) in typeList" :key="index" :class="{ 'is-active': chooseType === item.value }"
class="box-typeList-box" @click="chooseType = item.value">
<span>{{ item.label }}</span> <span>{{ item.label }}</span>
</div> </div>
</div> </div>
</div>
<template v-if="chooseType === 'color'">
<label style="display: flex;justify-content: space-between;">
<span>颜色</span>
<input v-model="tempString" type="color">
</label>
</template>
</div> </div>
</template> </template>
<style lang="less" scoped> <style lang="less" scoped>
.box{ .box {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
&-typeList{
&-typeList {
display: flex; display: flex;
&-box{
&-box {
display: flex; display: flex;
color:#fff; color: #0256ff;
background-color: aqua; border: 1px solid #0256ff;
width:50px; width: 50px;
height:20px; height: 20px;
font-size: 12px; font-size: 12px;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
&.is-active {
background-color: #0256ff;
color: #fff;
} }
} }
} }
}
</style> </style>

@ -0,0 +1,19 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { useSource } from "../context"
defineOptions({
name: 'DddBaseConfig'
})
const props = defineProps<{
field: string
label?: string
help?:string
}>()
const tempStr = useSource<string>(props.field)
</script>
<template>
<div>
</div>
</template>

@ -1,5 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue'
import { useSource } from '../context' import { useSource } from '../context'
import Switch from '../components/switch.vue'
defineOptions({ defineOptions({
name: "DddBooleanConfig" name: "DddBooleanConfig"
@ -13,20 +15,18 @@ const props = defineProps<{
const tempString = useSource<boolean>(props.field) const tempString = useSource<boolean>(props.field)
const defaultValue = computed(() => {
return tempString.value ? true: false
})
</script> </script>
<template> <template>
<div> <div>
<label> <label style="display: flex;justify-content: space-between;align-items: center;">
{{ label }} <span>{{ label }}</span>
<Switch :default-value="defaultValue" @change="(e) => tempString=e"></Switch>
</label> </label>
<div>
<label></label>
<input type="radio" :value="true" v-model="tempString"><br>
<label></label>
<input type="radio" :value="false" v-model="tempString"><br>
</div> </div>
</div>
</template> </template>

@ -0,0 +1,21 @@
<script lang="ts" setup>
import { useSource} from "../context"
defineOptions({
name: "DddColorConfig"
})
const props = defineProps<{
field: string
label?: string
help?: string
}>()
const tempStr = useSource<string>(props.field)
</script>
<template>
<label style="display: flex;justify-content: space-between;align-items: center;">
<span>{{ label }}</span>
<input v-model="tempStr" type="color">
</label>
</template>

@ -0,0 +1,69 @@
<script lang="ts" setup>
import { computed } from 'vue';
import { RenderConfig } from './render';
import { useSource } from '../context'
defineOptions({
name: "DddImageConfig"
})
const props = defineProps<{
field: string
label?: string
help?: string
required?: boolean
inlines?: any
}>()
const inlineFieldPrefix = computed(() => {
const i = props.field.lastIndexOf('.')
return i > -1 ? `${props.field.slice(0, i)}.` : ''
})
const img = useSource<string>(props.field)
</script>
<template>
<div class="container">
<div class="container-imgbox">
<span>{{ label }}<span v-if="required" style="color: brown;">*</span></span>
<!-- <input type="text" v-model="img"> -->
</div>
<div class="container-rightbox">
<div v-for="(line, index) in props.inlines" :key="index">
<!-- {{inlineFieldPrefix}}{{ line.field }} -->
<RenderConfig :type="line.type" :props="{
...line,
field: `${inlineFieldPrefix}${line.field}`,
}" />
</div>
</div>
</div>
</template>
<style lang="less" scoped>
.container {
box-sizing: border-box;
border-radius: 2px;
padding: 2px;
display:flex;
gap:10px;
align-items:center;
background-color: #f5f5f5;
margin-bottom: 5px;
&-imgbox {
width: 55px;
height: 55px;
border-radius:5px;
border : 1px solid #ccc;
display:flex;
justify-content: center;
align-items: center;
}
&-rightbox{
display: flex;
flex:1;
overflow: hidden;
flex-direction: column;
}
}
</style>

@ -35,18 +35,19 @@ const add = () => {
</script> </script>
<template> <template>
<fieldset>
<legend>{{ label }}设置</legend>
<div v-for="(_, line) in list" :key="line"> <div v-for="(_, line) in list" :key="line">
<div v-for="(cfg, idx) in configs" :key="idx"> <div v-for="(cfg, idx) in configs" :key="idx">
<RenderConfig <RenderConfig :type="cfg.type" :props="{
:type="cfg.type"
:props="{
...cfg, ...cfg,
field: `${field}.${line}.${cfg.field}`, field: `${field}.${line}.${cfg.field}`,
}" }" />
/>
</div> </div>
</div> </div>
<div> <div>
<button @click="add">+</button> <button @click="add">+增加</button>
</div> </div>
</fieldset>
</template> </template>

@ -14,21 +14,44 @@ const props = defineProps<{
const tempString = useSource<string>(props.field) const tempString = useSource<string>(props.field)
function selectChange(e: any){
console.log(e.target.value)
}
</script> </script>
<template> <template>
<div> <div class="box">
<label> <label>{{ label }}</label>
{{ label }} <div class="box-list">
</label> <div v-for="(opt,idx) in values" :key="idx" class="box-list-item" :class="[tempString === opt.value && 'is-active']" @click="tempString = opt.value">
<div> <span>{{ opt.label }}</span>
<select v-model="tempString" @change="selectChange"> </div>
<option value="none">不设置</option>
<option v-for="(opt,idx) in values" :key="idx" :value="opt.value">{{ opt.label }}</option>
</select>
</div> </div>
</div> </div>
</template> </template>
<style lang="less" scoped>
.box {
display: flex;
justify-content: space-between;
align-items: center;
&-list {
display: flex;
&-item {
display: flex;
color: #0256ff;
border: 1px solid #0256ff;
width: 50px;
height: 20px;
font-size: 12px;
justify-content: center;
align-items: center;
&.is-active {
background-color: #0256ff;
color: #fff;
}
}
}
}
</style>

@ -1,6 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ModuleConfig } from ".."; import { ModuleConfig } from "..";
import { useSource } from "../context"
import { RenderConfig } from './render'; import { RenderConfig } from './render';
defineOptions({ defineOptions({
@ -18,10 +17,8 @@ defineProps<{
</script> </script>
<template> <template>
<div> <fieldset>
<label> <legend>{{ label }}设置</legend>
<span>{{ label }}设置</span>
</label>
<div v-for="(cfg, idx) in configs" :key="idx"> <div v-for="(cfg, idx) in configs" :key="idx">
<RenderConfig <RenderConfig
:type="cfg.type" :type="cfg.type"
@ -31,5 +28,5 @@ defineProps<{
}" }"
/> />
</div> </div>
</div> </fieldset>
</template> </template>

@ -6,6 +6,8 @@ import ObjectConfig from './ObjectConfig.vue'
import MarkConfig from "./MarkConfig.vue"; import MarkConfig from "./MarkConfig.vue";
import BooleanConfig from "./BooleanConfig.vue" import BooleanConfig from "./BooleanConfig.vue"
import BackgroundConfig from "./BackgroundConfig.vue"; import BackgroundConfig from "./BackgroundConfig.vue";
import ImageConfig from "./ImageConfig.vue";
import ColorConfig from './ColorConfig.vue'
const configs: Record<string, Component> = { const configs: Record<string, Component> = {
text: TextConfig, text: TextConfig,
@ -13,7 +15,9 @@ const configs: Record<string, Component> = {
object: ObjectConfig, object: ObjectConfig,
mark: MarkConfig, mark: MarkConfig,
boolean: BooleanConfig, boolean: BooleanConfig,
background:BackgroundConfig, background: BackgroundConfig,
image: ImageConfig,
color: ColorConfig,
} }
function render(type: string, props: Record<string, any>) { function render(type: string, props: Record<string, any>) {

@ -0,0 +1,27 @@
import { error } from "console"
export function clone<T>(v: T): T {
switch (typeof v) {
case "string":
case "boolean":
case "number":
case "undefined":
return v
case "symbol":
case "function":
case "bigint":
throw new Error("invalid type")
case "object":
if (v == null) {
return v
}
if (Array.isArray(v)) {
return v.map(clone) as T
}
const obj = Object.create(null)
for (const [key, value] of Object.entries(v)) {
obj[key] = clone(value)
}
return obj
}
}

@ -1,6 +1,7 @@
export * from './align' export * from './align'
export * from './background' export * from './background'
export * from './border' export * from './border'
export * from './clone'
export * from './clip' export * from './clip'
export * from './gap' export * from './gap'
export * from './hash' export * from './hash'

Loading…
Cancel
Save