feat:【mall 商城】快递模版的迁移(antd 100%)

This commit is contained in:
YunaiV
2025-10-12 19:29:55 +08:00
parent 4270916ce4
commit 31a583419a
4 changed files with 141 additions and 205 deletions

View File

@@ -6,10 +6,32 @@ import { getDictOptions } from '@vben/hooks';
import { z } from '#/adapter/form'; import { z } from '#/adapter/form';
/** 计费方式列标题映射 */
export const CHARGE_MODE_TITLE_MAP: Record<
number,
{
extraCountTitle: string;
startCountTitle: string;
}
> = {
1: { startCountTitle: '首件', extraCountTitle: '续件' },
2: { startCountTitle: '首件重量(kg)', extraCountTitle: '续件重量(kg)' },
3: { startCountTitle: '首件体积(m³)', extraCountTitle: '续件体积(m³)' },
};
/** 包邮方式列标题映射 */
export const FREE_MODE_TITLE_MAP: Record<number, { freeCountTitle: string }> = {
1: { freeCountTitle: '包邮件数' },
2: { freeCountTitle: '包邮重量(kg)' },
3: { freeCountTitle: '包邮体积(m³)' },
};
/** 运费设置表格列 */ /** 运费设置表格列 */
export function useChargesColumns(): VxeTableGridOptions['columns'] { export function useChargesColumns(
chargeMode = 1,
): VxeTableGridOptions['columns'] {
const chargeTitleMap = CHARGE_MODE_TITLE_MAP[chargeMode];
return [ return [
{ type: 'seq', title: '序号', width: 50 },
{ {
field: 'areaIds', field: 'areaIds',
title: '区域', title: '区域',
@@ -18,7 +40,7 @@ export function useChargesColumns(): VxeTableGridOptions['columns'] {
}, },
{ {
field: 'startCount', field: 'startCount',
title: '首件数', title: chargeTitleMap?.startCountTitle,
width: 120, width: 120,
slots: { default: 'startCount' }, slots: { default: 'startCount' },
}, },
@@ -30,7 +52,7 @@ export function useChargesColumns(): VxeTableGridOptions['columns'] {
}, },
{ {
field: 'extraCount', field: 'extraCount',
title: '续件数', title: chargeTitleMap?.extraCountTitle,
width: 120, width: 120,
slots: { default: 'extraCount' }, slots: { default: 'extraCount' },
}, },
@@ -50,9 +72,11 @@ export function useChargesColumns(): VxeTableGridOptions['columns'] {
} }
/** 包邮设置表格列 */ /** 包邮设置表格列 */
export function useFreesColumns(): VxeTableGridOptions['columns'] { export function useFreesColumns(
chargeMode = 1,
): VxeTableGridOptions['columns'] {
const freeTitleMap = FREE_MODE_TITLE_MAP[chargeMode];
return [ return [
{ type: 'seq', title: '序号', width: 50 },
{ {
field: 'areaIds', field: 'areaIds',
title: '区域', title: '区域',
@@ -61,7 +85,7 @@ export function useFreesColumns(): VxeTableGridOptions['columns'] {
}, },
{ {
field: 'freeCount', field: 'freeCount',
title: '包邮件数', title: freeTitleMap?.freeCountTitle,
width: 120, width: 120,
slots: { default: 'freeCount' }, slots: { default: 'freeCount' },
}, },
@@ -99,7 +123,6 @@ export function useFormSchema(): VbenFormSchema[] {
placeholder: '请输入模板名称', placeholder: '请输入模板名称',
}, },
rules: 'required', rules: 'required',
formItemClass: 'col-span-1',
}, },
{ {
fieldName: 'chargeMode', fieldName: 'chargeMode',
@@ -111,7 +134,6 @@ export function useFormSchema(): VbenFormSchema[] {
optionType: 'button', optionType: 'button',
}, },
rules: z.number().default(1), rules: z.number().default(1),
formItemClass: 'col-span-1',
}, },
{ {
fieldName: 'sort', fieldName: 'sort',
@@ -122,25 +144,18 @@ export function useFormSchema(): VbenFormSchema[] {
min: 0, min: 0,
}, },
rules: 'required', rules: 'required',
formItemClass: 'col-span-1',
}, },
{ {
fieldName: 'charges', fieldName: 'charges',
label: '运费设置', label: '运费设置',
component: 'Input',
formItemClass: 'col-span-3', formItemClass: 'col-span-3',
dependencies: {
triggerFields: ['chargeMode'],
show: () => true,
},
}, },
{ {
fieldName: 'frees', fieldName: 'frees',
label: '包邮设置', label: '包邮设置',
component: 'Input',
formItemClass: 'col-span-3', formItemClass: 'col-span-3',
dependencies: {
triggerFields: [''],
show: () => true,
},
}, },
]; ];
} }

View File

@@ -2,45 +2,35 @@
import type { MallDeliveryExpressTemplateApi } from '#/api/mall/trade/delivery/expressTemplate'; import type { MallDeliveryExpressTemplateApi } from '#/api/mall/trade/delivery/expressTemplate';
import type { SystemAreaApi } from '#/api/system/area'; import type { SystemAreaApi } from '#/api/system/area';
import { computed, nextTick, onMounted, ref, watch } from 'vue'; import { computed, nextTick, ref, watch } from 'vue';
import { InputNumber, TreeSelect } from 'ant-design-vue'; import { InputNumber, TreeSelect } from 'ant-design-vue';
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getAreaTree } from '#/api/system/area';
import { useChargesColumns } from '../data'; import { CHARGE_MODE_TITLE_MAP, useChargesColumns } from '../data';
interface Props { interface Props {
items?: MallDeliveryExpressTemplateApi.TemplateCharge[]; items?: MallDeliveryExpressTemplateApi.TemplateCharge[];
chargeMode?: number; chargeMode?: number;
areaTree?: SystemAreaApi.Area[];
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
items: () => [], items: () => [],
chargeMode: 1, chargeMode: 1,
areaTree: () => [],
}); });
const emit = defineEmits(['update:items']); const emit = defineEmits(['update:items']);
const tableData = ref<any[]>([]); const tableData = ref<any[]>([]);
const areaTree = ref<SystemAreaApi.Area[]>([]); const columnTitle = computed(() => CHARGE_MODE_TITLE_MAP[props.chargeMode]);
// TODO @AI待优化
// 根据计费方式设置列标题
const columnTitle = computed(() => {
const titleMap = {
1: { startCountTitle: '首件', extraCountTitle: '续件' },
2: { startCountTitle: '首件重量(kg)', extraCountTitle: '续件重量(kg)' },
3: { startCountTitle: '首件体积(m³)', extraCountTitle: '续件体积(m³)' },
};
return titleMap[props.chargeMode] || titleMap[1];
});
/** 表格配置 */ /** 表格配置 */
const [Grid, gridApi] = useVbenVxeGrid({ const [Grid, gridApi] = useVbenVxeGrid({
gridOptions: { gridOptions: {
columns: useChargesColumns(), columns: useChargesColumns(props.chargeMode),
data: tableData.value, data: tableData.value,
minHeight: 200, minHeight: 200,
autoResize: true, autoResize: true,
@@ -68,7 +58,6 @@ watch(
tableData.value = [...items]; tableData.value = [...items];
await nextTick(); await nextTick();
await gridApi.grid.reloadData(tableData.value); await gridApi.grid.reloadData(tableData.value);
updateColumnsTitle();
}, },
{ {
immediate: true, immediate: true,
@@ -79,24 +68,13 @@ watch(
watch( watch(
() => props.chargeMode, () => props.chargeMode,
() => { () => {
updateColumnsTitle(); const columns = useChargesColumns(props.chargeMode);
if (gridApi.grid && columns) {
gridApi.grid.reloadColumn(columns);
}
}, },
); );
/** 更新列标题 */
function updateColumnsTitle() {
const columns = useChargesColumns();
const startCountCol = columns.find((col) => col.field === 'startCount');
const extraCountCol = columns.find((col) => col.field === 'extraCount');
if (startCountCol) startCountCol.title = columnTitle.value.startCountTitle;
if (extraCountCol) extraCountCol.title = columnTitle.value.extraCountTitle;
if (gridApi.grid) {
gridApi.grid.reloadColumn(columns);
}
}
/** 处理新增 */ /** 处理新增 */
function handleAdd() { function handleAdd() {
const newRow = { const newRow = {
@@ -139,7 +117,7 @@ function validate() {
} }
if (!item.startCount || item.startCount <= 0) { if (!item.startCount || item.startCount <= 0) {
throw new Error( throw new Error(
`运费设置第 ${i + 1} 行:${columnTitle.value.startCountTitle}必须大于 0`, `运费设置第 ${i + 1} 行:${columnTitle.value?.startCountTitle}必须大于 0`,
); );
} }
if (!item.startPrice || item.startPrice <= 0) { if (!item.startPrice || item.startPrice <= 0) {
@@ -147,7 +125,7 @@ function validate() {
} }
if (!item.extraCount || item.extraCount <= 0) { if (!item.extraCount || item.extraCount <= 0) {
throw new Error( throw new Error(
`运费设置第 ${i + 1} 行:${columnTitle.value.extraCountTitle}必须大于 0`, `运费设置第 ${i + 1} 行:${columnTitle.value?.extraCountTitle}必须大于 0`,
); );
} }
if (!item.extraPrice || item.extraPrice <= 0) { if (!item.extraPrice || item.extraPrice <= 0) {
@@ -159,16 +137,12 @@ function validate() {
defineExpose({ defineExpose({
validate, validate,
}); });
/** 初始化 */
onMounted(async () => {
areaTree.value = await getAreaTree();
});
</script> </script>
<template> <template>
<Grid class="w-full"> <Grid class="w-full">
<template #areaIds="{ row }"> <template #areaIds="{ row }">
<!-- TODO 芋艿可优化使用 Cascade不过貌似 antd multiple 貌似有 bug -->
<TreeSelect <TreeSelect
v-model:value="row.areaIds" v-model:value="row.areaIds"
:tree-data="areaTree" :tree-data="areaTree"
@@ -181,8 +155,7 @@ onMounted(async () => {
class="w-full" class="w-full"
multiple multiple
tree-checkable tree-checkable
:show-checked-strategy="TreeSelect.SHOW_CHILD" :max-tag-count="1"
:max-tag-count="5"
@change="handleRowChange(row)" @change="handleRowChange(row)"
/> />
</template> </template>

View File

@@ -1,10 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { MallDeliveryExpressTemplateApi } from '#/api/mall/trade/delivery/expressTemplate'; import type { MallDeliveryExpressTemplateApi } from '#/api/mall/trade/delivery/expressTemplate';
import type { SystemAreaApi } from '#/api/system/area';
import { computed, ref } from 'vue'; import { computed, onMounted, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui'; import { useVbenModal } from '@vben/common-ui';
import { cloneDeep } from '@vben/utils'; import { cloneDeep, fenToYuan, yuanToFen } from '@vben/utils';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
@@ -14,6 +15,7 @@ import {
getDeliveryExpressTemplate, getDeliveryExpressTemplate,
updateDeliveryExpressTemplate, updateDeliveryExpressTemplate,
} from '#/api/mall/trade/delivery/expressTemplate'; } from '#/api/mall/trade/delivery/expressTemplate';
import { getAreaTree } from '#/api/system/area';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { useFormSchema } from '../data'; import { useFormSchema } from '../data';
@@ -24,6 +26,7 @@ const emit = defineEmits(['success']);
const formData = ref<MallDeliveryExpressTemplateApi.ExpressTemplate>(); const formData = ref<MallDeliveryExpressTemplateApi.ExpressTemplate>();
const chargeItemFormRef = ref<InstanceType<typeof ChargeItemForm>>(); const chargeItemFormRef = ref<InstanceType<typeof ChargeItemForm>>();
const freeItemFormRef = ref<InstanceType<typeof FreeItemForm>>(); const freeItemFormRef = ref<InstanceType<typeof FreeItemForm>>();
const areaTree = ref<SystemAreaApi.Area[]>([]);
const getTitle = computed(() => { const getTitle = computed(() => {
return formData.value?.id return formData.value?.id
@@ -38,28 +41,38 @@ const [Form, formApi] = useVbenForm({
}, },
labelWidth: 120, labelWidth: 120,
}, },
wrapperClass: 'grid-cols-1', wrapperClass: 'grid-cols-3',
layout: 'vertical', layout: 'vertical',
schema: useFormSchema(), schema: useFormSchema(),
showDefaultActions: false, showDefaultActions: false,
handleValuesChange: (values, changedFields) => {
// 目的:触发子表单的 columns 变化
if (changedFields.includes('chargeMode')) {
formData.value!.chargeMode = values.chargeMode;
}
},
}); });
/** 更新运费设置 */ /** 更新运费设置 */
const handleUpdateCharges = (charges: any[]) => { const handleUpdateCharges = async (
charges: MallDeliveryExpressTemplateApi.TemplateCharge[],
) => {
formData.value = formData.value =
formApi.getValues() as MallDeliveryExpressTemplateApi.ExpressTemplate; await formApi.getValues<MallDeliveryExpressTemplateApi.ExpressTemplate>();
formData.value.charges = charges; formData.value.charges = charges;
formApi.setValues({ await formApi.setValues({
charges, charges,
}); });
}; };
/** 更新包邮设置 */ /** 更新包邮设置 */
const handleUpdateFrees = (frees: any[]) => { const handleUpdateFrees = async (
frees: MallDeliveryExpressTemplateApi.TemplateFree[],
) => {
formData.value = formData.value =
formApi.getValues() as MallDeliveryExpressTemplateApi.ExpressTemplate; await formApi.getValues<MallDeliveryExpressTemplateApi.ExpressTemplate>();
formData.value.frees = frees; formData.value.frees = frees;
formApi.setValues({ await formApi.setValues({
frees, frees,
}); });
}; };
@@ -71,22 +84,15 @@ const [Modal, modalApi] = useVbenModal({
if (!valid) { if (!valid) {
return; return;
} }
// 验证子表单
const chargeFormInstance = Array.isArray(chargeItemFormRef.value) const chargeFormInstance = Array.isArray(chargeItemFormRef.value)
? chargeItemFormRef.value[0] ? chargeItemFormRef.value[0]
: chargeItemFormRef.value; : chargeItemFormRef.value;
const freeFormInstance = Array.isArray(freeItemFormRef.value) const freeFormInstance = Array.isArray(freeItemFormRef.value)
? freeItemFormRef.value[0] ? freeItemFormRef.value[0]
: freeItemFormRef.value; : freeItemFormRef.value;
try { try {
if (chargeFormInstance) { chargeFormInstance.validate();
chargeFormInstance.validate(); freeFormInstance.validate();
}
if (freeFormInstance) {
freeFormInstance.validate();
}
} catch (error: any) { } catch (error: any) {
message.error(error.message || '子表单验证失败'); message.error(error.message || '子表单验证失败');
return; return;
@@ -98,19 +104,21 @@ const [Modal, modalApi] = useVbenModal({
await formApi.getValues(), await formApi.getValues(),
) as MallDeliveryExpressTemplateApi.ExpressTemplate; ) as MallDeliveryExpressTemplateApi.ExpressTemplate;
try { try {
// 前端价格以元展示,提交到后端用分计算 // 转换金额单位
data.charges?.forEach((item: any) => { data.charges?.forEach(
item.startPrice = Math.round(item.startPrice * 100); (item: MallDeliveryExpressTemplateApi.TemplateCharge) => {
item.extraPrice = Math.round(item.extraPrice * 100); item.startPrice = yuanToFen(item.startPrice);
}); item.extraPrice = yuanToFen(item.extraPrice);
data.frees?.forEach((item: any) => { },
item.freePrice = Math.round(item.freePrice * 100); );
}); data.frees?.forEach(
(item: MallDeliveryExpressTemplateApi.TemplateFree) => {
item.freePrice = yuanToFen(item.freePrice);
},
);
await (formData.value?.id await (formData.value?.id
? updateDeliveryExpressTemplate(data) ? updateDeliveryExpressTemplate(data)
: createDeliveryExpressTemplate(data)); : createDeliveryExpressTemplate(data));
// 关闭并提示 // 关闭并提示
await modalApi.close(); await modalApi.close();
emit('success'); emit('success');
@@ -128,68 +136,55 @@ const [Modal, modalApi] = useVbenModal({
const data = const data =
modalApi.getData<MallDeliveryExpressTemplateApi.ExpressTemplate>(); modalApi.getData<MallDeliveryExpressTemplateApi.ExpressTemplate>();
if (!data || !data.id) { if (!data || !data.id) {
resetFormData(); return;
}
modalApi.lock();
try {
formData.value = await getDeliveryExpressTemplate(data.id);
// 转换金额单位
formData.value.charges?.forEach(
(item: MallDeliveryExpressTemplateApi.TemplateCharge) => {
item.startPrice = Number.parseFloat(fenToYuan(item.startPrice));
item.extraPrice = Number.parseFloat(fenToYuan(item.extraPrice));
},
);
formData.value.frees?.forEach(
(item: MallDeliveryExpressTemplateApi.TemplateFree) => {
item.freePrice = Number.parseFloat(fenToYuan(item.freePrice));
},
);
// 设置到 values
await formApi.setValues(formData.value); await formApi.setValues(formData.value);
} else { } finally {
await loadFormData(data.id); modalApi.unlock();
} }
}, },
}); });
// 重置表单数据 /** 初始化 */
function resetFormData() { onMounted(async () => {
formData.value = { areaTree.value = await getAreaTree();
id: undefined, });
name: '',
chargeMode: 1,
sort: 0,
charges: [],
frees: [],
};
}
// 加载表单数据
async function loadFormData(id: number) {
modalApi.lock();
try {
const data = await getDeliveryExpressTemplate(id);
formData.value = data;
// 前端价格以元展示
formData.value.charges?.forEach((item: any, index: number) => {
item.seq = item.seq || Date.now() + index;
item.startPrice = item.startPrice / 100;
item.extraPrice = item.extraPrice / 100;
});
formData.value.frees?.forEach((item: any, index: number) => {
item.seq = item.seq || Date.now() + index;
item.freePrice = item.freePrice / 100;
});
// 设置到 values
await formApi.setValues(formData.value);
} finally {
modalApi.unlock();
}
}
</script> </script>
<template> <template>
<Modal class="w-[80%]" :title="getTitle"> <Modal class="w-1/2" :title="getTitle">
<Form class="mx-3"> <Form class="mx-3">
<template #charges> <template #charges>
<ChargeItemForm <ChargeItemForm
ref="chargeItemFormRef" ref="chargeItemFormRef"
:items="formData?.charges ?? []" :items="formData?.charges"
:charge-mode="formData?.chargeMode ?? 1" :charge-mode="formData?.chargeMode"
:area-tree="areaTree"
@update:items="handleUpdateCharges" @update:items="handleUpdateCharges"
/> />
</template> </template>
<template #frees> <template #frees>
<FreeItemForm <FreeItemForm
ref="freeItemFormRef" ref="freeItemFormRef"
:items="formData?.frees ?? []" :items="formData?.frees"
:charge-mode="formData?.chargeMode ?? 1" :charge-mode="formData?.chargeMode"
:area-tree="areaTree"
@update:items="handleUpdateFrees" @update:items="handleUpdateFrees"
/> />
</template> </template>

View File

@@ -1,44 +1,36 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, nextTick, onMounted, ref, watch } from 'vue'; import type { MallDeliveryExpressTemplateApi } from '#/api/mall/trade/delivery/expressTemplate';
import type { SystemAreaApi } from '#/api/system/area';
import { Cascader, InputNumber } from 'ant-design-vue'; import { computed, nextTick, ref, watch } from 'vue';
import { InputNumber, TreeSelect } from 'ant-design-vue';
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getAreaTree } from '#/api/system/area';
import { useFreesColumns } from '../data'; import { FREE_MODE_TITLE_MAP, useFreesColumns } from '../data';
interface Props { interface Props {
items?: any[]; items?: MallDeliveryExpressTemplateApi.TemplateFree[];
disabled?: boolean;
chargeMode?: number; chargeMode?: number;
areaTree?: SystemAreaApi.Area[];
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
items: () => [], items: () => [],
disabled: false,
chargeMode: 1, chargeMode: 1,
areaTree: () => [],
}); });
const emit = defineEmits(['update:items']); const emit = defineEmits(['update:items']);
const tableData = ref<any[]>([]); const tableData = ref<any[]>([]);
const areaTree = ref([]); const columnTitle = computed(() => FREE_MODE_TITLE_MAP[props.chargeMode]);
// 根据计费方式设置列标题
const columnTitle = computed(() => {
const titleMap = {
1: { freeCountTitle: '包邮件数' },
2: { freeCountTitle: '包邮重量(kg)' },
3: { freeCountTitle: '包邮体积(m³)' },
};
return titleMap[props.chargeMode] || titleMap[1];
});
/** 表格配置 */ /** 表格配置 */
const [Grid, gridApi] = useVbenVxeGrid({ const [Grid, gridApi] = useVbenVxeGrid({
gridOptions: { gridOptions: {
columns: useFreesColumns(), columns: useFreesColumns(props.chargeMode),
data: tableData.value, data: tableData.value,
minHeight: 200, minHeight: 200,
autoResize: true, autoResize: true,
@@ -66,7 +58,6 @@ watch(
tableData.value = [...items]; tableData.value = [...items];
await nextTick(); await nextTick();
await gridApi.grid.reloadData(tableData.value); await gridApi.grid.reloadData(tableData.value);
updateColumnsTitle();
}, },
{ {
immediate: true, immediate: true,
@@ -77,30 +68,19 @@ watch(
watch( watch(
() => props.chargeMode, () => props.chargeMode,
() => { () => {
updateColumnsTitle(); const columns = useFreesColumns(props.chargeMode);
if (gridApi.grid && columns) {
gridApi.grid.reloadColumn(columns);
}
}, },
); );
// TODO @AI待优化。
/** 更新列标题 */
function updateColumnsTitle() {
const columns = useFreesColumns();
const freeCountCol = columns.find((col) => col.field === 'freeCount');
if (freeCountCol) freeCountCol.title = columnTitle.value.freeCountTitle;
if (gridApi.grid) {
gridApi.grid.reloadColumn(columns);
}
}
/** 处理新增 */ /** 处理新增 */
function handleAdd() { function handleAdd() {
const newRow = { const newRow = {
seq: Date.now(),
areaIds: [], areaIds: [],
freeCount: 1, freeCount: undefined,
freePrice: 1, freePrice: undefined,
}; };
tableData.value.push(newRow); tableData.value.push(newRow);
emit('update:items', [...tableData.value]); emit('update:items', [...tableData.value]);
@@ -115,18 +95,6 @@ function handleDelete(row: any) {
emit('update:items', [...tableData.value]); emit('update:items', [...tableData.value]);
} }
/** 处理区域变更 */
function handleAreaChange(areaIds: any[], row: any) {
row.areaIds = areaIds;
handleRowChange(row);
}
/** 处理数值变更 */
function handleValueChange(field: string, value: any, row: any) {
row[field] = value;
handleRowChange(row);
}
/** 处理行数据变更 */ /** 处理行数据变更 */
function handleRowChange(row: any) { function handleRowChange(row: any) {
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
@@ -147,11 +115,11 @@ function validate() {
} }
if (!item.freeCount || item.freeCount <= 0) { if (!item.freeCount || item.freeCount <= 0) {
throw new Error( throw new Error(
`包邮设置第 ${i + 1} 行:${columnTitle.value.freeCountTitle}必须大于0`, `包邮设置第 ${i + 1} 行:${columnTitle.value?.freeCountTitle}必须大于 0`,
); );
} }
if (!item.freePrice || item.freePrice <= 0) { if (!item.freePrice || item.freePrice <= 0) {
throw new Error(`包邮设置第 ${i + 1}包邮金额必须大于0`); throw new Error(`包邮设置第 ${i + 1} 行:包邮金额必须大于 0`);
} }
} }
} }
@@ -159,23 +127,15 @@ function validate() {
defineExpose({ defineExpose({
validate, validate,
}); });
/** 初始化 */
onMounted(async () => {
try {
areaTree.value = await getAreaTree();
} catch (error) {
console.error('加载区域数据失败:', error);
}
});
</script> </script>
<template> <template>
<Grid class="w-full"> <Grid class="w-full">
<template #areaIds="{ row }"> <template #areaIds="{ row }">
<Cascader <!-- TODO 芋艿可优化使用 Cascade不过貌似 antd multiple 貌似有 bug -->
<TreeSelect
v-model:value="row.areaIds" v-model:value="row.areaIds"
:options="areaTree" :tree-data="areaTree"
:field-names="{ :field-names="{
label: 'name', label: 'name',
value: 'id', value: 'id',
@@ -184,18 +144,16 @@ onMounted(async () => {
placeholder="请选择地区" placeholder="请选择地区"
class="w-full" class="w-full"
multiple multiple
show-checked-strategy="SHOW_CHILD" tree-checkable
:disabled="disabled" :max-tag-count="1"
@change="handleAreaChange($event, row)" @change="handleRowChange(row)"
/> />
</template> </template>
<template #freeCount="{ row }"> <template #freeCount="{ row }">
<InputNumber <InputNumber
v-model:value="row.freeCount" v-model:value="row.freeCount"
:min="1" :min="1"
class="w-full" @change="handleRowChange(row)"
:disabled="disabled"
@change="handleValueChange('freeCount', $event, row)"
/> />
</template> </template>
<template #freePrice="{ row }"> <template #freePrice="{ row }">
@@ -203,14 +161,11 @@ onMounted(async () => {
v-model:value="row.freePrice" v-model:value="row.freePrice"
:min="0" :min="0"
:precision="2" :precision="2"
class="w-full" @change="handleRowChange(row)"
:disabled="disabled"
@change="handleValueChange('freePrice', $event, row)"
/> />
</template> </template>
<template #actions="{ row }"> <template #actions="{ row }">
<TableAction <TableAction
v-if="!disabled"
:actions="[ :actions="[
{ {
label: '删除', label: '删除',
@@ -224,10 +179,8 @@ onMounted(async () => {
]" ]"
/> />
</template> </template>
<template #bottom> <template #bottom>
<TableAction <TableAction
v-if="!disabled"
class="mt-2 flex justify-center" class="mt-2 flex justify-center"
:actions="[ :actions="[
{ {