feat:【antd】mall 发布界面的评审

pull/238/MERGE
YunaiV 2025-10-22 23:53:48 +08:00
parent 495a924d56
commit ebc7aba637
10 changed files with 176 additions and 191 deletions

View File

@ -4,7 +4,6 @@ import { DeliveryTypeEnum, DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { handleTree } from '@vben/utils';
import { z } from '#/adapter/form';
import { getSimpleBrandList } from '#/api/mall/product/brand';
import { getCategoryList } from '#/api/mall/product/category';
import { getSimpleTemplateList } from '#/api/mall/trade/delivery/expressTemplate';
@ -33,7 +32,6 @@ export function useInfoFormSchema(): VbenFormSchema[] {
{
fieldName: 'categoryId',
label: '分类名称',
// component: 'ApiCascader',
component: 'ApiTreeSelect',
componentProps: {
api: async () => {
@ -285,7 +283,7 @@ export function useOtherFormSchema(): VbenFormSchema[] {
componentProps: {
min: 0,
},
rules: z.number().min(0).optional().default(0),
rules: 'required',
},
{
fieldName: 'giveIntegral',
@ -294,7 +292,7 @@ export function useOtherFormSchema(): VbenFormSchema[] {
componentProps: {
min: 0,
},
rules: z.number().min(0).optional().default(0),
rules: 'required',
},
{
fieldName: 'virtualSalesCount',
@ -303,7 +301,7 @@ export function useOtherFormSchema(): VbenFormSchema[] {
componentProps: {
min: 0,
},
rules: z.number().min(0).optional().default(0),
rules: 'required',
},
];
}

View File

@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import type { MallSpuApi } from '#/api/mall/product/spu';
// TODO @puhui999这个是不是 api 后端有定义类似的?如果是,是不是放到 api 哈?
export interface PropertyAndValues {
id: number;
name: string;
@ -23,12 +24,8 @@ export interface RuleConfig {
message: string;
}
/**
* -
*
* @param spu
* @return PropertyAndValues
*/
// TODO @puhui999这个是只有 index.ts 在用么?还是别的模块也会用
/** 获得商品的规格列表 - 商品相关的公共函数 */
const getPropertyList = (spu: MallSpuApi.Spu): PropertyAndValues[] => {
// 直接拿返回的 skus 属性逆向生成出 propertyList
const properties: PropertyAndValues[] = [];
@ -62,4 +59,5 @@ const getPropertyList = (spu: MallSpuApi.Spu): PropertyAndValues[] => {
export { getPropertyList };
// 导出组件
// TODO @puhui999如果 sku-list.vue 要对外,可以考虑在 spu 下面,搞个 components 模块目前看别的模块应该会用到哈。modules 是当前模块用到的components 是跨模块要用到的。
export { default as SkuList } from './modules/sku-list.vue';

View File

@ -8,7 +8,7 @@ import { useRoute } from 'vue-router';
import { Page, useVbenModal } from '@vben/common-ui';
import { useTabs } from '@vben/hooks';
import { convertToInteger, floatToFixed2, formatToFraction } from '@vben/utils';
import { convertToInteger, formatToFraction } from '@vben/utils';
import { Button, Card, message } from 'ant-design-vue';
@ -31,11 +31,6 @@ const spuId = ref<number>();
const { params, name } = useRoute();
const { closeCurrentTab } = useTabs();
const activeTabName = ref('info');
function onTabChange(key: string) {
activeTabName.value = key;
}
const tabList = ref([
{
key: 'info',
@ -58,44 +53,43 @@ const tabList = ref([
tab: '其它设置',
},
]);
// spu
const formData = ref<MallSpuApi.Spu>({
name: '', //
categoryId: undefined, //
keyword: '', //
picUrl: '', //
sliderPicUrls: [], //
introduction: '', //
deliveryTypes: [], //
deliveryTemplateId: undefined, //
brandId: undefined, //
specType: false, //
subCommissionType: false, //
skus: [
{
price: 0, //
marketPrice: 0, //
costPrice: 0, //
barCode: '', //
picUrl: '', //
stock: 0, //
weight: 0, //
volume: 0, //
firstBrokeragePrice: 0, //
secondBrokeragePrice: 0, //
},
],
description: '', //
sort: 0, //
giveIntegral: 0, //
virtualSalesCount: 0, //
});
const propertyList = ref<PropertyAndValues[]>([]); //
const formLoading = ref(true); // 12
const isDetail = ref(false); //
const formLoading = ref(false); // 12
const isDetail = ref(name === 'ProductSpuDetail'); //
const skuListRef = ref(); // Ref
// sku
const formData = ref<MallSpuApi.Spu>({
name: '',
categoryId: undefined,
keyword: '',
picUrl: '',
sliderPicUrls: [],
introduction: '',
deliveryTypes: [],
deliveryTemplateId: undefined,
brandId: undefined,
specType: false,
subCommissionType: false,
skus: [
{
price: 0,
marketPrice: 0,
costPrice: 0,
barCode: '',
picUrl: '',
stock: 0,
weight: 0,
volume: 0,
firstBrokeragePrice: 0,
secondBrokeragePrice: 0,
},
],
description: '',
sort: 0,
giveIntegral: 0,
virtualSalesCount: 0,
}); // spu
const propertyList = ref<PropertyAndValues[]>([]); //
const ruleConfig: RuleConfig[] = [
{
name: 'stock',
@ -117,7 +111,7 @@ const ruleConfig: RuleConfig[] = [
rule: (arg) => arg >= 0.01,
message: '商品成本价格必须大于等于 0.00 元!!!',
},
];
]; // sku
const [InfoForm, infoFormApi] = useVbenForm({
commonConfig: {
@ -146,11 +140,11 @@ const [SkuForm, skuFormApi] = useVbenForm({
handleValuesChange: (values, fieldsChanged) => {
if (fieldsChanged.includes('subCommissionType')) {
formData.value.subCommissionType = values.subCommissionType;
changeSubCommissionType();
handleChangeSubCommissionType();
}
if (fieldsChanged.includes('specType')) {
formData.value.specType = values.specType;
onChangeSpec();
handleChangeSpec();
}
},
});
@ -199,7 +193,13 @@ const [OtherForm, otherFormApi] = useVbenForm({
showDefaultActions: false,
});
async function onSubmit() {
/** tab 切换 */
function handleTabChange(key: string) {
activeTabName.value = key;
}
/** 提交表单 */
async function handleSubmit() {
const values: MallSpuApi.Spu = await infoFormApi
.merge(skuFormApi)
.merge(deliveryFormApi)
@ -216,7 +216,7 @@ async function onSubmit() {
return;
}
values.skus.forEach((item) => {
// sku
//
item.price = convertToInteger(item.price);
item.marketPrice = convertToInteger(item.marketPrice);
item.costPrice = convertToInteger(item.costPrice);
@ -224,7 +224,7 @@ async function onSubmit() {
item.secondBrokeragePrice = convertToInteger(item.secondBrokeragePrice);
});
}
//
// TODO @puhui999
const newSliderPicUrls: any[] = [];
values.sliderPicUrls!.forEach((item: any) => {
//
@ -234,12 +234,13 @@ async function onSubmit() {
});
values.sliderPicUrls = newSliderPicUrls;
//
await (spuId.value ? updateSpu(values) : createSpu(values));
}
/** 获得详情 */
async function getDetail() {
if (name === 'ProductSpuDetail') {
if (isDetail.value) {
isDetail.value = true;
infoFormApi.setDisabled(true);
skuFormApi.setDisabled(true);
@ -247,45 +248,36 @@ async function getDetail() {
descriptionFormApi.setDisabled(true);
otherFormApi.setDisabled(true);
}
const id = params.id as unknown as number;
if (id) {
try {
const res = await getSpu(spuId.value!);
res.skus?.forEach((item) => {
if (isDetail.value) {
item.price = floatToFixed2(item.price);
item.marketPrice = floatToFixed2(item.marketPrice);
item.costPrice = floatToFixed2(item.costPrice);
item.firstBrokeragePrice = floatToFixed2(item.firstBrokeragePrice);
item.secondBrokeragePrice = floatToFixed2(item.secondBrokeragePrice);
} else {
//
item.price = formatToFraction(item.price);
item.marketPrice = formatToFraction(item.marketPrice);
item.costPrice = formatToFraction(item.costPrice);
item.firstBrokeragePrice = formatToFraction(item.firstBrokeragePrice);
item.secondBrokeragePrice = formatToFraction(
item.secondBrokeragePrice,
);
}
});
formData.value = res;
//
infoFormApi.setValues(res);
skuFormApi.setValues(res);
deliveryFormApi.setValues(res);
descriptionFormApi.setValues(res);
otherFormApi.setValues(res);
} finally {
formLoading.value = false;
}
}
// SKU PropertyAndValues
propertyList.value = getPropertyList(formData.value);
formLoading.value = true;
try {
const res = await getSpu(spuId.value!);
//
res.skus?.forEach((item) => {
item.price = formatToFraction(item.price);
item.marketPrice = formatToFraction(item.marketPrice);
item.costPrice = formatToFraction(item.costPrice);
item.firstBrokeragePrice = formatToFraction(item.firstBrokeragePrice);
item.secondBrokeragePrice = formatToFraction(item.secondBrokeragePrice);
});
formData.value = res;
//
infoFormApi.setValues(res).then();
skuFormApi.setValues(res).then();
deliveryFormApi.setValues(res).then();
descriptionFormApi.setValues(res).then();
otherFormApi.setValues(res).then();
// SKU PropertyAndValues
propertyList.value = getPropertyList(formData.value);
} finally {
formLoading.value = false;
}
}
// =========== sku form ===========
/** 打开属性添加表单 */
function openPropertyAddForm() {
productPropertyAddFormApi.open();
}
@ -296,7 +288,7 @@ function generateSkus(propertyList: any[]) {
}
/** 分销类型 */
function changeSubCommissionType() {
function handleChangeSubCommissionType() {
//
for (const item of formData.value.skus!) {
item.firstBrokeragePrice = 0;
@ -305,10 +297,10 @@ function changeSubCommissionType() {
}
/** 选择规格 */
function onChangeSpec() {
function handleChangeSpec() {
//
propertyList.value = [];
// sku
// sku
formData.value.skus = [
{
price: 0,
@ -325,7 +317,7 @@ function onChangeSpec() {
];
}
// sku form schema
/** 监听 sku form schema 变化,更新表单 */
watch(
propertyList,
() => {
@ -336,6 +328,7 @@ watch(
{ deep: true },
);
/** 初始化 */
onMounted(async () => {
spuId.value = params.id as unknown as number;
if (!spuId.value) {
@ -355,18 +348,18 @@ onMounted(async () => {
:loading="formLoading"
:tab-list="tabList"
:active-key="activeTabName"
@tab-change="onTabChange"
@tab-change="handleTabChange"
>
<template #tabBarExtraContent>
<Button type="primary" v-if="!isDetail" @click="onSubmit">
<Button type="primary" v-if="!isDetail" @click="handleSubmit">
保存
</Button>
<Button type="default" v-else @click="() => closeCurrentTab()">
返回列表
</Button>
</template>
<InfoForm class="w-3/5" v-show="activeTabName === 'info'" />
<InfoForm class="w-3/5" v-show="activeTabName === 'info'" />
<SkuForm class="w-full" v-show="activeTabName === 'sku'">
<template #singleSkuList>
<SkuList
@ -418,6 +411,7 @@ onMounted(async () => {
</div>
</template>
<style lang="scss" scoped>
// TODO @puhui999
:deep(.ant-tabs-tab-btn) {
font-size: 14px !important;
}

View File

@ -21,7 +21,6 @@ const props = withDefaults(defineProps<Props>(), {
isDetail: false,
});
/** 输入框失去焦点或点击回车时触发 */
const emit = defineEmits(['success']);
interface Props {
@ -30,12 +29,15 @@ interface Props {
}
const inputValue = ref<string[]>([]); // tags 使
const attributeIndex = ref<null | number>(null); // index
//
const attributeIndex = ref<null | number>(null); // index
const inputVisible = computed(() => (index: number) => {
if (attributeIndex.value === null) return false;
if (attributeIndex.value === index) return true;
});
if (attributeIndex.value === null) {
return false;
}
if (attributeIndex.value === index) {
return true;
}
}); //
interface InputRefItem {
inputRef?: {
@ -46,7 +48,10 @@ interface InputRefItem {
focus: () => void;
}
const inputRef = ref<InputRefItem[]>([]); // Ref
const inputRef = ref<InputRefItem[]>([]); // Ref
const attributeList = ref<PropertyAndValues[]>([]); //
const attributeOptions = ref<MallPropertyApi.PropertyValue[]>([]); //
/** 解决 ref 在 v-for 中的获取问题*/
function setInputRef(el: any) {
if (el === null || el === undefined) return;
@ -59,13 +64,13 @@ function setInputRef(el: any) {
inputRef.value.push(el);
}
}
const attributeList = ref<PropertyAndValues[]>([]); //
const attributeOptions = ref<MallPropertyApi.PropertyValue[]>([]); //
watch(
() => props.propertyList,
(data) => {
if (!data) return;
if (!data) {
return;
}
attributeList.value = data;
},
{
@ -74,12 +79,12 @@ watch(
},
);
/** 删除属性值*/
/** 删除属性值 */
function handleCloseValue(index: number, valueIndex: number) {
attributeList.value?.[index]?.values?.splice(valueIndex, 1);
}
/** 删除属性*/
/** 删除属性 */
function handleCloseProperty(index: number) {
attributeList.value?.splice(index, 1);
emit('success', attributeList.value);
@ -93,7 +98,7 @@ async function showInput(index: number) {
await getAttributeOptions(attributeList.value?.[index]?.id!);
}
// success
/** 定义 success 事件,用于操作成功后的回调 */
async function handleInputConfirm(index: number, propertyId: number) {
// tags inputValue
const currentValue = inputValue.value?.[inputValue.value.length - 1]?.trim();
@ -154,6 +159,7 @@ async function getAttributeOptions(propertyId: number) {
<template>
<Col v-for="(item, index) in attributeList" :key="index">
<!-- TODO @puhui9991间隙可以看看2)vue3 + element-plus 添加属性这个按钮是和属性名在一排感觉更好看点 -->
<div>
<span class="mx-1">属性名</span>
<Tag
@ -174,6 +180,7 @@ async function getAttributeOptions(propertyId: number) {
class="mx-1"
@close="handleCloseValue(index, valueIndex)"
>
<!-- TODO @puhui999这里貌似爆红idea -->
{{ value.name }}
</Tag>
<Select

View File

@ -35,7 +35,9 @@ const attributeOptions = ref<MallPropertyApi.Property[]>([]); // 商品属性名
watch(
() => props.propertyList,
(data) => {
if (!data) return;
if (!data) {
return;
}
attributeList.value = data as any[];
},
{
@ -44,7 +46,6 @@ watch(
},
);
//
const formSchema: VbenFormSchema[] = [
{
fieldName: 'name',
@ -62,7 +63,6 @@ const formSchema: VbenFormSchema[] = [
showSearch: true,
filterOption: true,
placeholder: '请选择属性名称。如果不存在,可手动输入选择',
//
mode: 'tags',
maxTagCount: 1,
allowClear: true,
@ -71,7 +71,6 @@ const formSchema: VbenFormSchema[] = [
},
];
//
const [Form, formApi] = useVbenForm({
commonConfig: {
componentProps: {
@ -85,16 +84,15 @@ const [Form, formApi] = useVbenForm({
showDefaultActions: false,
});
//
const [Modal, modalApi] = useVbenModal({
destroyOnClose: true,
async onConfirm() {
const { valid } = await formApi.validate();
if (!valid) return;
if (!valid) {
return;
}
const values = await formApi.getValues();
const name = Array.isArray(values.name) ? values.name[0] : values.name;
//
for (const attrItem of attributeList.value) {
if (attrItem.name === name) {
@ -103,6 +101,8 @@ const [Modal, modalApi] = useVbenModal({
}
}
// TODO @puhui999modalApi.lock();
// 使
const existProperty = attributeOptions.value.find(
(item: MallPropertyApi.Property) => item.name === name,
@ -113,6 +113,7 @@ const [Modal, modalApi] = useVbenModal({
name,
values: [],
});
// TODO @puhui999 if else await modalApi.close(); emit('success'); add existProperty
await modalApi.close();
emit('success');
return;
@ -132,7 +133,6 @@ const [Modal, modalApi] = useVbenModal({
await modalApi.close();
emit('success');
} catch (error) {
//
console.error('添加属性失败:', error);
}
},
@ -140,7 +140,6 @@ const [Modal, modalApi] = useVbenModal({
if (!isOpen) {
return;
}
//
await formApi.resetForm();
},
});

View File

@ -46,16 +46,16 @@ const { isBatch, isDetail, isComponent, isActivityComponent } = props;
const formData: Ref<MallSpuApi.Spu | undefined> = ref<MallSpuApi.Spu>(); //
const skuList = ref<MallSpuApi.Sku[]>([
{
price: 0, //
marketPrice: 0, //
costPrice: 0, //
barCode: '', //
picUrl: '', //
stock: 0, //
weight: 0, //
volume: 0, //
firstBrokeragePrice: 0, //
secondBrokeragePrice: 0, //
price: 0,
marketPrice: 0,
costPrice: 0,
barCode: '',
picUrl: '',
stock: 0,
weight: 0,
volume: 0,
firstBrokeragePrice: 0,
secondBrokeragePrice: 0,
},
]); //
@ -91,9 +91,7 @@ function deleteSku(row: MallSpuApi.Sku) {
const tableHeaders = ref<{ label: string; prop: string }[]>([]); //
/**
* 保存时每个商品规格的表单要校验下例如说,销售金额最低是 0.01 这种
*/
/** 保存时,每个商品规格的表单要校验下。例如说,销售金额最低是 0.01 这种 */
function validateSku() {
validateProperty();
let warningInfo = '请检查商品各行相关属性配置,';
@ -116,6 +114,7 @@ function validateSku() {
}
}
// TODO @puhui999 getNestedValue
function getValue(obj: any, arg: string): unknown {
const keys = arg.split('.');
let value: any = obj;
@ -132,19 +131,20 @@ function getValue(obj: any, arg: string): unknown {
/**
* 选择时触发
*
* @param records 传递过来的选中的 sku 是一个数组
*/
function handleSelectionChange({ records }: { records: MallSpuApi.Sku[] }) {
emit('selectionChange', records);
}
/**
* 将传进来的值赋值给 skuList
*/
/** 将传进来的值赋值给 skuList */
watch(
() => props.propFormData,
(data) => {
if (!data) return;
if (!data) {
return;
}
formData.value = data;
},
{
@ -196,9 +196,7 @@ function generateTableData(propertyList: PropertyAndValues[]) {
}
}
/**
* 生成 skus 前置校验
*/
/** 生成 skus 前置校验 */
function validateData(propertyList: PropertyAndValues[]): boolean {
const skuPropertyIds: number[] = [];
formData.value!.skus!.forEach((sku: MallSpuApi.Sku) =>
@ -302,13 +300,13 @@ function getSkuTableRef() {
return activitySkuListRef.value;
}
// sku
defineExpose({ generateTableData, validateSku, getSkuTableRef });
</script>
<template>
<div>
<!-- 情况一添加/修改 -->
<!-- TODO @puhui999有可以通过 grid 来做么主要考虑这样不直接使用 vxe 标签抽象程度更高 -->
<VxeTable
v-if="!isDetail && !isActivityComponent"
:data="isBatch ? skuList : formData?.skus || []"
@ -328,7 +326,7 @@ defineExpose({ generateTableData, validateSku, getSkuTableRef });
</template>
</VxeColumn>
<template v-if="formData?.specType && !isBatch">
<!-- 根据商品属性动态添加 -->
<!-- 根据商品属性动态添加 -->
<VxeColumn
v-for="(item, index) in tableHeaders"
:key="index"
@ -481,7 +479,7 @@ defineExpose({ generateTableData, validateSku, getSkuTableRef });
</template>
</VxeColumn>
<template v-if="formData?.specType && !isBatch">
<!-- 根据商品属性动态添加 -->
<!-- 根据商品属性动态添加 -->
<VxeColumn
v-for="(item, index) in tableHeaders"
:key="index"
@ -565,7 +563,7 @@ defineExpose({ generateTableData, validateSku, getSkuTableRef });
</template>
</VxeColumn>
<template v-if="formData?.specType">
<!-- 根据商品属性动态添加 -->
<!-- 根据商品属性动态添加 -->
<VxeColumn
v-for="(item, index) in tableHeaders"
:key="index"
@ -605,7 +603,7 @@ defineExpose({ generateTableData, validateSku, getSkuTableRef });
{{ row.stock }}
</template>
</VxeColumn>
<!-- 方便扩展每个活动配置的属性不一样 -->
<!-- 方便扩展每个活动配置的属性不一样 -->
<slot name="extension"></slot>
</VxeTable>
</div>

View File

@ -24,7 +24,8 @@ const emit = defineEmits<{
const selectedSkuId = ref<number>();
const spuId = ref<number>();
//
/** 配置列 */
// TODO @puhui999
const gridColumns = computed<VxeGridProps['columns']>(() => [
{
field: 'id',
@ -65,7 +66,6 @@ const gridColumns = computed<VxeGridProps['columns']>(() => [
},
]);
//
const [Grid, gridApi] = useVbenVxeGrid({
gridOptions: {
columns: gridColumns.value,
@ -95,14 +95,13 @@ const [Grid, gridApi] = useVbenVxeGrid({
},
});
//
/** 处理选中 */
function handleSelected(row: MallSpuApi.Sku) {
emit('change', row);
modalApi.close();
selectedSkuId.value = undefined;
}
//
const [Modal, modalApi] = useVbenModal({
destroyOnClose: true,
onOpenChange: async (isOpen: boolean) => {
@ -111,8 +110,8 @@ const [Modal, modalApi] = useVbenModal({
spuId.value = undefined;
return;
}
const data = modalApi.getData<SpuData>();
// TODO @puhui999 if return
if (data?.spuId) {
spuId.value = data.spuId;
//
@ -125,7 +124,6 @@ const [Modal, modalApi] = useVbenModal({
<template>
<Modal class="w-[700px]" title="选择规格">
<Grid>
<!-- 单选列 -->
<template #radio-column="{ row }">
<Input
v-model="selectedSkuId"

View File

@ -1,5 +1,6 @@
<!-- SPU 商品选择弹窗组件 -->
<script lang="ts" setup>
// TODO @puhui999 components
import type { CheckboxChangeEvent } from 'ant-design-vue/es/checkbox/interface';
import type { VbenFormSchema } from '#/adapter/form';
@ -30,33 +31,15 @@ const emit = defineEmits<{
change: [spu: MallSpuApi.Spu | MallSpuApi.Spu[]];
}>();
// SPU ID
const selectedSpuId = ref<number>();
// map
const checkedStatus = ref<Record<number, boolean>>({});
// SPU
const checkedSpus = ref<MallSpuApi.Spu[]>([]);
//
const isCheckAll = ref(false);
//
const isIndeterminate = ref(false);
const selectedSpuId = ref<number>(); // SPU ID
const checkedStatus = ref<Record<number, boolean>>({}); // map
const checkedSpus = ref<MallSpuApi.Spu[]>([]); // SPU
const isCheckAll = ref(false); //
const isIndeterminate = ref(false); //
//
const categoryList = ref<any[]>([]);
//
const categoryTreeList = ref<any[]>([]);
const categoryList = ref<any[]>([]); //
const categoryTreeList = ref<any[]>([]); //
//
onMounted(async () => {
try {
categoryList.value = await getCategoryList({});
categoryTreeList.value = handleTree(categoryList.value, 'id', 'parentId');
} catch (error) {
console.error('加载分类数据失败:', error);
}
});
//
const formSchema = computed<VbenFormSchema[]>(() => {
return [
{
@ -97,7 +80,6 @@ const formSchema = computed<VbenFormSchema[]>(() => {
];
});
//
const gridColumns = computed<VxeGridProps['columns']>(() => {
const columns: VxeGridProps['columns'] = [];
@ -121,7 +103,7 @@ const gridColumns = computed<VxeGridProps['columns']>(() => {
});
}
//
//
columns.push(
{
field: 'id',
@ -157,7 +139,6 @@ const gridColumns = computed<VxeGridProps['columns']>(() => {
return columns;
});
//
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: formSchema.value,
@ -172,6 +153,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
proxyConfig: {
ajax: {
async query({ page }: any, formValues: any) {
// TODO @puhui999 try catch
try {
const params = {
pageNo: page.currentPage,
@ -182,6 +164,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
createTime: formValues.createTime || undefined,
};
// TODO @puhui999 params getSpuPage
const data = await getSpuPage(params);
//
@ -208,14 +191,15 @@ const [Grid, gridApi] = useVbenVxeGrid({
},
});
//
// TODO @puhui999 Grid
/** 单选:处理选中 */
function handleSingleSelected(row: MallSpuApi.Spu) {
selectedSpuId.value = row.id;
emit('change', row);
modalApi.close();
}
// /
/** 多选:全选/全不选 */
function handleCheckAll(e: CheckboxChangeEvent) {
const checked = e.target.checked;
isCheckAll.value = checked;
@ -228,7 +212,7 @@ function handleCheckAll(e: CheckboxChangeEvent) {
calculateIsCheckAll();
}
//
/** 多选:选中单个 */
function handleCheckOne(
checked: boolean,
spu: MallSpuApi.Spu,
@ -255,7 +239,7 @@ function handleCheckOne(
}
}
//
/** 多选:计算全选状态 */
function calculateIsCheckAll() {
const currentList = gridApi.grid.getData();
if (currentList.length === 0) {
@ -272,7 +256,6 @@ function calculateIsCheckAll() {
isIndeterminate.value = checkedCount > 0 && checkedCount < currentList.length;
}
//
const [Modal, modalApi] = useVbenModal({
destroyOnClose: true,
//
@ -284,7 +267,7 @@ const [Modal, modalApi] = useVbenModal({
: undefined,
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
//
// TODO @puhui999 selectedSpuId.value
if (!props.multiple) {
selectedSpuId.value = undefined;
}
@ -300,7 +283,6 @@ const [Modal, modalApi] = useVbenModal({
checkedStatus.value = {};
isCheckAll.value = false;
isIndeterminate.value = false;
//
if (Array.isArray(data) && data.length > 0) {
checkedSpus.value = [...data];
@ -316,9 +298,16 @@ const [Modal, modalApi] = useVbenModal({
}
//
// TODO @puhui999100%
await gridApi.query();
},
});
/** 初始化分类数据 */
onMounted(async () => {
categoryList.value = await getCategoryList({});
categoryTreeList.value = handleTree(categoryList.value, 'id', 'parentId');
});
</script>
<template>

View File

@ -145,7 +145,9 @@ const [Modal, modalApi] = useVbenModal({
// ""
async onConfirm() {
const { valid } = await formApi.validate();
if (!valid) return;
if (!valid) {
return;
}
modalApi.lock();
try {

View File

@ -53,7 +53,9 @@ const rewardRuleRef = ref<InstanceType<typeof RewardRule>>();
const [Modal, modalApi] = useVbenModal({
async onConfirm() {
const { valid } = await formApi.validate();
if (!valid) return;
if (!valid) {
return;
}
modalApi.lock();
try {