feat:【antd】【mall】diy-editor 代码风格统一 & 逐个测试 30%

This commit is contained in:
YunaiV
2025-11-11 09:58:36 +08:00
parent fadad35b20
commit 6a270e26d8
14 changed files with 56 additions and 66 deletions

View File

@@ -133,6 +133,7 @@ function handleSliderChange(prop: string) {
</TabPane> </TabPane>
<!-- 每个组件的通用内容 --> <!-- 每个组件的通用内容 -->
<!-- TODO @xingyu这里的样式貌似没 ele 版本的好看 -->
<TabPane tab="样式" key="style" force-render> <TabPane tab="样式" key="style" force-render>
<p class="text-lg font-bold">组件样式</p> <p class="text-lg font-bold">组件样式</p>
<div class="flex flex-col gap-2 rounded-md p-4 shadow-lg"> <div class="flex flex-col gap-2 rounded-md p-4 shadow-lg">
@@ -159,6 +160,7 @@ function handleSliderChange(prop: string) {
<template #tip>建议宽度 750px</template> <template #tip>建议宽度 750px</template>
</UploadImg> </UploadImg>
</FormItem> </FormItem>
<!-- TODO @xingyu没完全对齐 -->
<Tree :tree-data="treeData" default-expand-all :block-node="true"> <Tree :tree-data="treeData" default-expand-all :block-node="true">
<template #title="{ dataRef }"> <template #title="{ dataRef }">
<FormItem <FormItem

View File

@@ -49,6 +49,7 @@ const emits = defineEmits<{
type DiyComponentWithStyle = DiyComponent<any> & { type DiyComponentWithStyle = DiyComponent<any> & {
property: { style?: ComponentStyle }; property: { style?: ComponentStyle };
}; };
/** 组件样式 */ /** 组件样式 */
const style = computed(() => { const style = computed(() => {
const componentStyle = props.component.property.style; const componentStyle = props.component.property.style;
@@ -108,6 +109,7 @@ const handleDeleteComponent = () => {
class="component-toolbar" class="component-toolbar"
v-if="showToolbar && component.name && active" v-if="showToolbar && component.name && active"
> >
<!-- TODO @xingyu按钮少的时候会存在遮住的情况 -->
<VerticalButtonGroup size="small"> <VerticalButtonGroup size="small">
<Button <Button
:disabled="!canMoveUp" :disabled="!canMoveUp"

View File

@@ -19,6 +19,7 @@ const props = defineProps<{
list: DiyComponentLibrary[]; list: DiyComponentLibrary[];
}>(); }>();
// TODO @xingyu要不要换成 reactive和 ele 一致
const groups = ref<any[]>([]); // 组件分组 const groups = ref<any[]>([]); // 组件分组
const extendGroups = ref<string[]>([]); // 展开的折叠面板 const extendGroups = ref<string[]>([]); // 展开的折叠面板
@@ -99,4 +100,5 @@ function handleCloneComponent(component: DiyComponent<any>) {
</Collapse.Panel> </Collapse.Panel>
</Collapse> </Collapse>
</div> </div>
<!-- TODO @xingyuele 里面有一些 style看看是不是都迁移完了特别是 drag-area 是全局样式 -->
</template> </template>

View File

@@ -11,7 +11,6 @@ defineOptions({ name: 'ImageBar' });
defineProps<{ property: ImageBarProperty }>(); defineProps<{ property: ImageBarProperty }>();
</script> </script>
<template> <template>
<!-- 无图片 -->
<div <div
class="bg-card flex h-12 items-center justify-center" class="bg-card flex h-12 items-center justify-center"
v-if="!property.imgUrl" v-if="!property.imgUrl"

View File

@@ -9,11 +9,13 @@ import { AppLinkInput } from '#/views/mall/promotion/components';
import ComponentContainerProperty from '../../component-container-property.vue'; import ComponentContainerProperty from '../../component-container-property.vue';
// 图片展示属性面板 /** 图片展示属性面板 */
defineOptions({ name: 'ImageBarProperty' }); defineOptions({ name: 'ImageBarProperty' });
const props = defineProps<{ modelValue: ImageBarProperty }>(); const props = defineProps<{ modelValue: ImageBarProperty }>();
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);
const formData = useVModel(props, 'modelValue', emit); const formData = useVModel(props, 'modelValue', emit);
</script> </script>

View File

@@ -13,10 +13,11 @@ import { getSpuDetailList } from '#/api/mall/product/spu';
/** 商品卡片 */ /** 商品卡片 */
defineOptions({ name: 'ProductCard' }); defineOptions({ name: 'ProductCard' });
// 定义属性
const props = defineProps<{ property: ProductCardProperty }>(); const props = defineProps<{ property: ProductCardProperty }>();
// 商品列表
const spuList = ref<MallSpuApi.Spu[]>([]); const spuList = ref<MallSpuApi.Spu[]>([]); // 商品列表
watch( watch(
() => props.property.spuIds, () => props.property.spuIds,
async () => { async () => {
@@ -28,28 +29,21 @@ watch(
}, },
); );
/** /** 计算商品的间距 */
* 计算商品的间距
* @param index 商品索引
*/
function calculateSpace(index: number) { function calculateSpace(index: number) {
// 商品的列数 const columns = props.property.layoutType === 'twoCol' ? 2 : 1; // 商品的列数
const columns = props.property.layoutType === 'twoCol' ? 2 : 1; const marginLeft = index % columns === 0 ? '0' : `${props.property.space}px`; // 第一列没有左边距
// 第一没有边距 const marginTop = index < columns ? '0' : `${props.property.space}px`; // 第一没有边距
const marginLeft = index % columns === 0 ? '0' : `${props.property.space}px`;
// 第一行没有上边距
const marginTop = index < columns ? '0' : `${props.property.space}px`;
return { marginLeft, marginTop }; return { marginLeft, marginTop };
} }
// 容器 const containerRef = ref(); // 容器
const containerRef = ref();
// 计算商品的宽度 /** 计算商品的宽度 */
function calculateWidth() { function calculateWidth() {
let width = '100%'; let width = '100%';
// 双列时每列的宽度为:(总宽度 - 间距)/ 2
if (props.property.layoutType === 'twoCol') { if (props.property.layoutType === 'twoCol') {
// 双列时每列的宽度为:(总宽度 - 间距)/ 2
width = `${(containerRef.value.offsetWidth - props.property.space) / 2}px`; width = `${(containerRef.value.offsetWidth - props.property.space) / 2}px`;
} }
return { width }; return { width };
@@ -61,7 +55,7 @@ function calculateWidth() {
ref="containerRef" ref="containerRef"
> >
<div <div
class="bg-card relative box-content flex flex-row flex-wrap overflow-hidden" class="relative box-content flex flex-row flex-wrap overflow-hidden"
:style="{ :style="{
...calculateSpace(index), ...calculateSpace(index),
...calculateWidth(), ...calculateWidth(),

View File

@@ -27,14 +27,11 @@ export interface DiyComponentLibrary {
components: string[]; // 组件列表 components: string[]; // 组件列表
} }
// 组件样式 /** 组件样式 */
export interface ComponentStyle { export interface ComponentStyle {
// 背景类型 bgType: 'color' | 'img'; // 背景类型
bgType: 'color' | 'img'; bgColor: string; // 背景颜色
// 背景颜色 bgImg: string; // 背景图片
bgColor: string;
// 背景图片
bgImg: string;
// 外边距 // 外边距
margin: number; margin: number;
marginTop: number; marginTop: number;

View File

@@ -27,7 +27,7 @@ const props = defineProps<{ modelValue: ComponentStyle }>();
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);
const formData = useVModel(props, 'modelValue', emit); const formData = useVModel(props, 'modelValue', emit);
const treeData = [ const treeData: any[] = [
{ {
label: '外部边距', label: '外部边距',
prop: 'margin', prop: 'margin',
@@ -96,7 +96,7 @@ const treeData = [
}, },
]; ];
const handleSliderChange = (prop: string) => { function handleSliderChange(prop: string) {
switch (prop) { switch (prop) {
case 'borderRadius': { case 'borderRadius': {
formData.value.borderTopLeftRadius = formData.value.borderRadius; formData.value.borderTopLeftRadius = formData.value.borderRadius;
@@ -120,7 +120,7 @@ const handleSliderChange = (prop: string) => {
break; break;
} }
} }
}; }
</script> </script>
<template> <template>

View File

@@ -53,11 +53,11 @@ watch(
); );
/** 克隆组件 */ /** 克隆组件 */
const handleCloneComponent = (component: DiyComponent<any>) => { function handleCloneComponent(component: DiyComponent<any>) {
const instance = cloneDeep(component); const instance = cloneDeep(component);
instance.uid = Date.now(); instance.uid = Date.now();
return instance; return instance;
}; }
</script> </script>
<template> <template>

View File

@@ -11,7 +11,7 @@ export interface ImageBarProperty {
export const component = { export const component = {
id: 'ImageBar', id: 'ImageBar',
name: '图片展示', name: '图片展示',
icon: 'ep:picture', icon: 'lucide:image',
property: { property: {
imgUrl: '', imgUrl: '',
url: '', url: '',

View File

@@ -13,7 +13,9 @@ import ComponentContainerProperty from '../../component-container-property.vue';
defineOptions({ name: 'ImageBarProperty' }); defineOptions({ name: 'ImageBarProperty' });
const props = defineProps<{ modelValue: ImageBarProperty }>(); const props = defineProps<{ modelValue: ImageBarProperty }>();
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);
const formData = useVModel(props, 'modelValue', emit); const formData = useVModel(props, 'modelValue', emit);
</script> </script>

View File

@@ -1,32 +1,25 @@
import { defineAsyncComponent } from 'vue'; /**
/*
* 组件注册 * 组件注册
* *
* 组件规范: * 组件规范:每个子目录就是一个独立的组件,每个目录包括以下三个文件:
* 1. 每个子目录就是一个独立的组件,每个目录包括以下三个文件: * 1. config.ts组件配置必选用于定义组件、组件默认的属性、定义属性的类型
* 2. config.ts组件配置必选用于定义组件、组件默认的属性、定义属性的类型 * 2. index.vue组件展示用于展示组件的渲染效果。可以不提供如 Page页面设置只需要属性配置表单即可
* 3. index.vue组件展示用于展示组件的渲染效果。可以不提供如 Page页面设置只需要属性配置表单即可 * 3. property.vue组件属性表单用于配置组件必选
* 4. property.vue组件属性表单用于配置组件必选
* *
* 注: * 注:
* 组件IDconfig.ts中配置的id为准与组件目录的名称无关但还是建议组件目录的名称与组件ID保持一致 * 组件 IDconfig.ts 中配置的 id 为准,与组件目录的名称无关,但还是建议组件目录的名称与组件 ID 保持一致
*/ */
import { defineAsyncComponent } from 'vue';
// 导入组件界面模块 const viewModules: Record<string, any> = import.meta.glob('./*/*.vue'); // 导入组件界面模块
const viewModules: Record<string, any> = import.meta.glob('./*/*.vue');
// 导入配置模块
const configModules: Record<string, any> = import.meta.glob('./*/config.ts', { const configModules: Record<string, any> = import.meta.glob('./*/config.ts', {
eager: true, eager: true,
}); }); // 导入配置模块
// 界面模块 const components: Record<string, any> = {}; // 界面模块
const components: Record<string, any> = {}; const componentConfigs: Record<string, any> = {}; // 组件配置模块
// 组件配置模块
const componentConfigs: Record<string, any> = {};
// 组件界面的类型 type ViewType = 'index' | 'property'; // 组件界面的类型
type ViewType = 'index' | 'property';
/** /**
* 注册组件的界面模块 * 注册组件的界面模块

View File

@@ -9,19 +9,19 @@ import { fenToYuan } from '@vben/utils';
import { ElImage } from 'element-plus'; import { ElImage } from 'element-plus';
import * as ProductSpuApi from '#/api/mall/product/spu'; import { getSpuDetailList } from '#/api/mall/product/spu';
/** 商品卡片 */ /** 商品卡片 */
defineOptions({ name: 'ProductCard' }); defineOptions({ name: 'ProductCard' });
const props = defineProps<{ property: ProductCardProperty }>(); const props = defineProps<{ property: ProductCardProperty }>();
const spuList = ref<MallSpuApi.Spu[]>([]); const spuList = ref<MallSpuApi.Spu[]>([]); // 商品列表
watch( watch(
() => props.property.spuIds, () => props.property.spuIds,
async () => { async () => {
spuList.value = await ProductSpuApi.getSpuDetailList(props.property.spuIds); spuList.value = await getSpuDetailList(props.property.spuIds);
}, },
{ {
immediate: true, immediate: true,
@@ -37,17 +37,17 @@ function calculateSpace(index: number) {
return { marginLeft, marginTop }; return { marginLeft, marginTop };
} }
const containerRef = ref(); const containerRef = ref(); // 容器
/** 计算商品的宽度 */ /** 计算商品的宽度 */
const calculateWidth = () => { function calculateWidth() {
let width = '100%'; let width = '100%';
if (props.property.layoutType === 'twoCol') { if (props.property.layoutType === 'twoCol') {
// 双列时每列的宽度为:(总宽度 - 间距)/ 2 // 双列时每列的宽度为:(总宽度 - 间距)/ 2
width = `${(containerRef.value.offsetWidth - props.property.space) / 2}px`; width = `${(containerRef.value.offsetWidth - props.property.space) / 2}px`;
} }
return { width }; return { width };
}; }
</script> </script>
<template> <template>
<div <div

View File

@@ -27,14 +27,11 @@ export interface DiyComponentLibrary {
components: string[]; // 组件列表 components: string[]; // 组件列表
} }
// 组件样式 /** 组件样式 */
export interface ComponentStyle { export interface ComponentStyle {
// 背景类型 bgType: 'color' | 'img'; // 背景类型
bgType: 'color' | 'img'; bgColor: string; // 背景颜色
// 背景颜色 bgImg: string; // 背景图片
bgColor: string;
// 背景图片
bgImg: string;
// 外边距 // 外边距
margin: number; margin: number;
marginTop: number; marginTop: number;