mirror of
https://gitee.com/yudaocode/yudao-ui-admin-vben.git
synced 2025-12-30 02:22:25 +00:00
feat:【ele】【mall】spu 代码对齐 antd
This commit is contained in:
@@ -22,16 +22,6 @@ const emit = defineEmits<{
|
||||
const visible = ref(false);
|
||||
const spuId = ref<number>();
|
||||
|
||||
/** 处理选中 */
|
||||
function handleRadioChange() {
|
||||
const selectedRow = gridApi.grid.getRadioRecord() as MallSpuApi.Sku;
|
||||
if (selectedRow) {
|
||||
emit('change', selectedRow);
|
||||
closeModal();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO @puhui999:这里的代码风格,对齐 antd 的;可以使用 idea 对比两个文件哈;
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: useSkuGridColumns(),
|
||||
@@ -39,20 +29,33 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
border: true,
|
||||
radioConfig: {
|
||||
reserve: true,
|
||||
highlight: true,
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
gridEvents: {
|
||||
radioChange: handleRadioChange,
|
||||
radioChange: () => {
|
||||
const selectedRow = gridApi.grid.getRadioRecord() as MallSpuApi.Sku;
|
||||
if (selectedRow) {
|
||||
emit('change', selectedRow);
|
||||
// 关闭弹窗
|
||||
visible.value = false;
|
||||
gridApi.grid.clearRadioRow();
|
||||
spuId.value = undefined;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/** 关闭弹窗 */
|
||||
function closeModal() {
|
||||
visible.value = false;
|
||||
gridApi.grid.clearRadioRow();
|
||||
spuId.value = undefined;
|
||||
}
|
||||
|
||||
@@ -63,6 +66,8 @@ async function openModal(data?: SpuData) {
|
||||
}
|
||||
spuId.value = data.spuId;
|
||||
visible.value = true;
|
||||
// 注意:useVbenVxeGrid 关闭分页(pagerConfig.enabled=false)后,proxyConfig.ajax.query 的结果不会传递到 vxe-table
|
||||
// 需要手动调用 reloadData 设置表格数据
|
||||
if (!spuId.value) {
|
||||
gridApi.grid?.reloadData([]);
|
||||
return;
|
||||
|
||||
@@ -312,8 +312,10 @@ onMounted(async () => {
|
||||
</template>
|
||||
</Grid>
|
||||
<template #footer>
|
||||
<el-button @click="closeModal">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确定</el-button>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="closeModal">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</template>
|
||||
|
||||
@@ -108,14 +108,12 @@ function emitSpuChange() {
|
||||
<ElImage
|
||||
:src="spu.picUrl"
|
||||
class="h-full w-full rounded-lg object-cover"
|
||||
:preview-src-list="[spu.picUrl!]"
|
||||
fit="cover"
|
||||
/>
|
||||
<!-- 删除按钮 -->
|
||||
<!-- TODO @puhui999:还是使用 IconifyIcon:使用自己的中立的图标,方便 antd 和 ele 共享 -->
|
||||
<IconifyIcon
|
||||
v-if="!disabled"
|
||||
icon="ep:circle-close-filled"
|
||||
icon="lucide:circle-x"
|
||||
class="absolute -right-2 -top-2 cursor-pointer text-xl text-red-500 opacity-0 transition-opacity hover:text-red-600 group-hover:opacity-100"
|
||||
@click="handleRemoveSpu(index)"
|
||||
/>
|
||||
@@ -129,8 +127,7 @@ function emitSpuChange() {
|
||||
class="flex h-[60px] w-[60px] cursor-pointer items-center justify-center rounded-lg border-2 border-dashed transition-colors hover:border-primary hover:bg-primary/5"
|
||||
@click="handleOpenSpuSelect"
|
||||
>
|
||||
<!-- TODO @puhui999:还是使用 IconifyIcon:使用自己的中立的图标,方便 antd 和 ele 共享 -->
|
||||
<IconifyIcon icon="ep:plus" class="text-xl text-gray-400" />
|
||||
<IconifyIcon icon="lucide:plus" class="text-xl text-gray-400" />
|
||||
</div>
|
||||
</ElTooltip>
|
||||
</div>
|
||||
|
||||
@@ -52,19 +52,23 @@ const formSchema = computed<VbenFormSchema[]>(() => [
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入商品名称',
|
||||
clearable: true,
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'categoryId',
|
||||
label: '商品分类',
|
||||
component: 'ApiTreeSelect',
|
||||
component: 'TreeSelect',
|
||||
componentProps: {
|
||||
options: categoryTreeList,
|
||||
props: { label: 'name', children: 'children' },
|
||||
nodeKey: 'id',
|
||||
data: categoryTreeList,
|
||||
props: {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
},
|
||||
checkStrictly: true,
|
||||
placeholder: '请选择商品分类',
|
||||
clearable: true,
|
||||
filterable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -73,7 +77,7 @@ const formSchema = computed<VbenFormSchema[]>(() => [
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
clearable: true,
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
]);
|
||||
@@ -162,40 +166,35 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
async function openModal(data?: MallSpuApi.Spu | MallSpuApi.Spu[]) {
|
||||
initData.value = data;
|
||||
visible.value = true;
|
||||
// 等待 Grid 组件完全初始化后再查询数据
|
||||
await nextTick();
|
||||
// 1. 查询数据
|
||||
await gridApi.query();
|
||||
// 2. 设置已选中行
|
||||
const tableData = gridApi.grid.getTableData().fullData;
|
||||
if (
|
||||
props.multiple &&
|
||||
Array.isArray(initData.value) &&
|
||||
initData.value.length > 0
|
||||
) {
|
||||
setTimeout(() => {
|
||||
(initData.value as unknown as MallSpuApi.Spu[])!.forEach((spu) => {
|
||||
if (gridApi.grid) {
|
||||
// 1. 先查询数据
|
||||
await gridApi.query();
|
||||
// 2. 设置已选中行
|
||||
if (props.multiple && Array.isArray(data) && data.length > 0) {
|
||||
setTimeout(() => {
|
||||
const tableData = gridApi.grid.getTableData().fullData;
|
||||
data.forEach((spu) => {
|
||||
const row = tableData.find(
|
||||
(item: MallSpuApi.Spu) => item.id === spu.id,
|
||||
);
|
||||
if (row) {
|
||||
gridApi.grid.setCheckboxRow(row, true);
|
||||
}
|
||||
});
|
||||
}, 300);
|
||||
} else if (!props.multiple && data && !Array.isArray(data)) {
|
||||
setTimeout(() => {
|
||||
const tableData = gridApi.grid.getTableData().fullData;
|
||||
const row = tableData.find(
|
||||
(item: MallSpuApi.Spu) => item.id === spu.id,
|
||||
(item: MallSpuApi.Spu) => item.id === data.id,
|
||||
);
|
||||
if (row) {
|
||||
gridApi.grid.setCheckboxRow(row, true);
|
||||
gridApi.grid.setRadioRow(row);
|
||||
}
|
||||
});
|
||||
}, 300);
|
||||
} else if (
|
||||
!props.multiple &&
|
||||
initData.value &&
|
||||
!Array.isArray(initData.value)
|
||||
) {
|
||||
setTimeout(() => {
|
||||
const row = tableData.find(
|
||||
(item: MallSpuApi.Spu) =>
|
||||
item.id === (initData.value as MallSpuApi.Spu).id,
|
||||
);
|
||||
if (row) {
|
||||
gridApi.grid.setRadioRow(row);
|
||||
}
|
||||
}, 300);
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,10 +213,9 @@ function handleConfirm() {
|
||||
closeModal();
|
||||
}
|
||||
|
||||
/** 对外暴露的方法 */
|
||||
defineExpose({
|
||||
open: openModal,
|
||||
});
|
||||
}); // 对外暴露的方法
|
||||
|
||||
/** 初始化分类数据 */
|
||||
onMounted(async () => {
|
||||
@@ -232,13 +230,14 @@ onMounted(async () => {
|
||||
title="选择商品"
|
||||
width="950px"
|
||||
:destroy-on-close="true"
|
||||
:append-to-body="true"
|
||||
@close="closeModal"
|
||||
>
|
||||
<Grid />
|
||||
<template v-if="props.multiple" #footer>
|
||||
<el-button @click="closeModal">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确定</el-button>
|
||||
<template #footer>
|
||||
<span v-if="props.multiple" class="dialog-footer">
|
||||
<el-button @click="closeModal">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</template>
|
||||
|
||||
@@ -32,7 +32,6 @@ const spuId = ref<number>();
|
||||
const { params, name } = useRoute();
|
||||
const { closeCurrentTab } = useTabs();
|
||||
const activeTabName = ref('info');
|
||||
|
||||
const formLoading = ref(false); // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const isDetail = ref(name === 'ProductSpuDetail'); // 是否查看详情
|
||||
const skuListRef = ref(); // 商品属性列表 Ref
|
||||
@@ -70,7 +69,6 @@ const formData = ref<MallSpuApi.Spu>({
|
||||
}); // spu 表单数据
|
||||
const propertyList = ref<PropertyAndValues[]>([]); // 商品属性列表
|
||||
const ruleConfig: RuleConfig[] = [
|
||||
// TODO @puhui999:ele 这里都有 :number,antd 要不要加?
|
||||
{
|
||||
name: 'stock',
|
||||
rule: (arg: number) => arg >= 0,
|
||||
@@ -200,7 +198,7 @@ async function handleSubmit() {
|
||||
item.secondBrokeragePrice = convertToInteger(item.secondBrokeragePrice);
|
||||
});
|
||||
}
|
||||
// 处理轮播图列表 TODO @puhui999:这个是必须的哇?
|
||||
// 处理轮播图列表:上传组件可能返回对象或字符串,统一处理成字符串数组
|
||||
const newSliderPicUrls: any[] = [];
|
||||
values.sliderPicUrls!.forEach((item: any) => {
|
||||
// 如果是前端选的图
|
||||
@@ -319,13 +317,10 @@ onMounted(async () => {
|
||||
<ProductPropertyAddFormModal :property-list="propertyList" />
|
||||
|
||||
<Page auto-content-height>
|
||||
<ElCard class="spu-form-card h-full w-full" v-loading="formLoading">
|
||||
<ElCard class="h-full w-full" v-loading="formLoading">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<ElTabs
|
||||
v-model="activeTabName"
|
||||
@tab-click="(tab: any) => handleTabChange(tab.paneName)"
|
||||
>
|
||||
<ElTabs v-model="activeTabName" @tab-change="handleTabChange">
|
||||
<ElTabPane label="基础设置" name="info" />
|
||||
<ElTabPane label="价格库存" name="sku" />
|
||||
<ElTabPane label="物流设置" name="delivery" />
|
||||
@@ -333,7 +328,7 @@ onMounted(async () => {
|
||||
<ElTabPane label="其它设置" name="other" />
|
||||
</ElTabs>
|
||||
<div>
|
||||
<ElButton v-if="!isDetail" type="primary" @click="handleSubmit">
|
||||
<ElButton type="primary" v-if="!isDetail" @click="handleSubmit">
|
||||
保存
|
||||
</ElButton>
|
||||
<ElButton v-else @click="() => closeCurrentTab()">
|
||||
@@ -343,71 +338,61 @@ onMounted(async () => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="flex-1 overflow-auto">
|
||||
<InfoForm class="w-3/5" v-show="activeTabName === 'info'" />
|
||||
<SkuForm class="w-full" v-show="activeTabName === 'sku'">
|
||||
<template #singleSkuList>
|
||||
<SkuList
|
||||
ref="skuListRef"
|
||||
class="w-full"
|
||||
<InfoForm class="w-3/5" v-show="activeTabName === 'info'" />
|
||||
<SkuForm class="w-full" v-show="activeTabName === 'sku'">
|
||||
<template #singleSkuList>
|
||||
<SkuList
|
||||
ref="skuListRef"
|
||||
class="w-full"
|
||||
:is-detail="isDetail"
|
||||
:prop-form-data="formData"
|
||||
:property-list="propertyList"
|
||||
:rule-config="ruleConfig"
|
||||
/>
|
||||
</template>
|
||||
<template #productAttributes>
|
||||
<div>
|
||||
<ElButton class="mb-10px mr-15px" @click="openPropertyAddForm">
|
||||
添加属性
|
||||
</ElButton>
|
||||
<ProductAttributes
|
||||
:is-detail="isDetail"
|
||||
:prop-form-data="formData"
|
||||
:property-list="propertyList"
|
||||
:rule-config="ruleConfig"
|
||||
@success="generateSkus"
|
||||
/>
|
||||
</template>
|
||||
<template #productAttributes>
|
||||
<div>
|
||||
<ElButton class="mb-10px mr-15px" @click="openPropertyAddForm">
|
||||
添加属性
|
||||
</ElButton>
|
||||
<ProductAttributes
|
||||
:is-detail="isDetail"
|
||||
:property-list="propertyList"
|
||||
@success="generateSkus"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #batchSkuList>
|
||||
<SkuList
|
||||
:is-batch="true"
|
||||
:is-detail="isDetail"
|
||||
:prop-form-data="formData"
|
||||
:property-list="propertyList"
|
||||
/>
|
||||
</template>
|
||||
<template #multiSkuList>
|
||||
<SkuList
|
||||
ref="skuListRef"
|
||||
:is-detail="isDetail"
|
||||
:prop-form-data="formData"
|
||||
:property-list="propertyList"
|
||||
:rule-config="ruleConfig"
|
||||
/>
|
||||
</template>
|
||||
</SkuForm>
|
||||
<DeliveryForm class="w-3/5" v-show="activeTabName === 'delivery'" />
|
||||
<DescriptionForm
|
||||
class="w-3/5"
|
||||
v-show="activeTabName === 'description'"
|
||||
/>
|
||||
<OtherForm class="w-3/5" v-show="activeTabName === 'other'" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #batchSkuList>
|
||||
<SkuList
|
||||
:is-batch="true"
|
||||
:is-detail="isDetail"
|
||||
:prop-form-data="formData"
|
||||
:property-list="propertyList"
|
||||
/>
|
||||
</template>
|
||||
<template #multiSkuList>
|
||||
<SkuList
|
||||
ref="skuListRef"
|
||||
:is-detail="isDetail"
|
||||
:prop-form-data="formData"
|
||||
:property-list="propertyList"
|
||||
:rule-config="ruleConfig"
|
||||
/>
|
||||
</template>
|
||||
</SkuForm>
|
||||
<DeliveryForm class="w-3/5" v-show="activeTabName === 'delivery'" />
|
||||
<DescriptionForm
|
||||
class="w-3/5"
|
||||
v-show="activeTabName === 'description'"
|
||||
/>
|
||||
<OtherForm class="w-3/5" v-show="activeTabName === 'other'" />
|
||||
</ElCard>
|
||||
</Page>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.spu-form-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.spu-form-card :deep(.el-card__body) {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-tabs__nav-wrap::after) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -215,18 +215,17 @@ async function getAttributeOptions(propertyId: number) {
|
||||
<ElOption
|
||||
v-for="item2 in attributeOptions"
|
||||
:key="item2.id"
|
||||
:value="item2.name"
|
||||
:label="item2.name"
|
||||
:value="item2.name"
|
||||
/>
|
||||
</ElSelect>
|
||||
<ElTag
|
||||
v-show="!inputVisible(index)"
|
||||
@click="showInput(index)"
|
||||
class="mx-1 cursor-pointer"
|
||||
effect="plain"
|
||||
class="mx-1 cursor-pointer border-dashed bg-gray-100"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon class="mr-2" icon="ep:plus" />
|
||||
<IconifyIcon class="mr-2" icon="lucide:plus" />
|
||||
添加
|
||||
</div>
|
||||
</ElTag>
|
||||
|
||||
@@ -61,9 +61,9 @@ const formSchema: VbenFormSchema[] = [
|
||||
}));
|
||||
},
|
||||
filterable: true,
|
||||
allowCreate: true,
|
||||
placeholder: '请选择属性名称。如果不存在,可手动输入选择',
|
||||
multiple: true,
|
||||
allowCreate: true,
|
||||
clearable: true,
|
||||
},
|
||||
rules: 'required',
|
||||
|
||||
Reference in New Issue
Block a user