feat:【mall 商城】快递模版的迁移(antd 100%)
This commit is contained in:
@@ -6,10 +6,32 @@ import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
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 [
|
||||
{ type: 'seq', title: '序号', width: 50 },
|
||||
{
|
||||
field: 'areaIds',
|
||||
title: '区域',
|
||||
@@ -18,7 +40,7 @@ export function useChargesColumns(): VxeTableGridOptions['columns'] {
|
||||
},
|
||||
{
|
||||
field: 'startCount',
|
||||
title: '首件数',
|
||||
title: chargeTitleMap?.startCountTitle,
|
||||
width: 120,
|
||||
slots: { default: 'startCount' },
|
||||
},
|
||||
@@ -30,7 +52,7 @@ export function useChargesColumns(): VxeTableGridOptions['columns'] {
|
||||
},
|
||||
{
|
||||
field: 'extraCount',
|
||||
title: '续件数',
|
||||
title: chargeTitleMap?.extraCountTitle,
|
||||
width: 120,
|
||||
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 [
|
||||
{ type: 'seq', title: '序号', width: 50 },
|
||||
{
|
||||
field: 'areaIds',
|
||||
title: '区域',
|
||||
@@ -61,7 +85,7 @@ export function useFreesColumns(): VxeTableGridOptions['columns'] {
|
||||
},
|
||||
{
|
||||
field: 'freeCount',
|
||||
title: '包邮件数',
|
||||
title: freeTitleMap?.freeCountTitle,
|
||||
width: 120,
|
||||
slots: { default: 'freeCount' },
|
||||
},
|
||||
@@ -99,7 +123,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
placeholder: '请输入模板名称',
|
||||
},
|
||||
rules: 'required',
|
||||
formItemClass: 'col-span-1',
|
||||
},
|
||||
{
|
||||
fieldName: 'chargeMode',
|
||||
@@ -111,7 +134,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
optionType: 'button',
|
||||
},
|
||||
rules: z.number().default(1),
|
||||
formItemClass: 'col-span-1',
|
||||
},
|
||||
{
|
||||
fieldName: 'sort',
|
||||
@@ -122,25 +144,18 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
min: 0,
|
||||
},
|
||||
rules: 'required',
|
||||
formItemClass: 'col-span-1',
|
||||
},
|
||||
{
|
||||
fieldName: 'charges',
|
||||
label: '运费设置',
|
||||
component: 'Input',
|
||||
formItemClass: 'col-span-3',
|
||||
dependencies: {
|
||||
triggerFields: ['chargeMode'],
|
||||
show: () => true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'frees',
|
||||
label: '包邮设置',
|
||||
component: 'Input',
|
||||
formItemClass: 'col-span-3',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -2,45 +2,35 @@
|
||||
import type { MallDeliveryExpressTemplateApi } from '#/api/mall/trade/delivery/expressTemplate';
|
||||
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 { 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 {
|
||||
items?: MallDeliveryExpressTemplateApi.TemplateCharge[];
|
||||
chargeMode?: number;
|
||||
areaTree?: SystemAreaApi.Area[];
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
items: () => [],
|
||||
chargeMode: 1,
|
||||
areaTree: () => [],
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:items']);
|
||||
|
||||
const tableData = ref<any[]>([]);
|
||||
const areaTree = ref<SystemAreaApi.Area[]>([]);
|
||||
|
||||
// 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 columnTitle = computed(() => CHARGE_MODE_TITLE_MAP[props.chargeMode]);
|
||||
|
||||
/** 表格配置 */
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: useChargesColumns(),
|
||||
columns: useChargesColumns(props.chargeMode),
|
||||
data: tableData.value,
|
||||
minHeight: 200,
|
||||
autoResize: true,
|
||||
@@ -68,7 +58,6 @@ watch(
|
||||
tableData.value = [...items];
|
||||
await nextTick();
|
||||
await gridApi.grid.reloadData(tableData.value);
|
||||
updateColumnsTitle();
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
@@ -79,24 +68,13 @@ watch(
|
||||
watch(
|
||||
() => 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() {
|
||||
const newRow = {
|
||||
@@ -139,7 +117,7 @@ function validate() {
|
||||
}
|
||||
if (!item.startCount || item.startCount <= 0) {
|
||||
throw new Error(
|
||||
`运费设置第 ${i + 1} 行:${columnTitle.value.startCountTitle}必须大于 0`,
|
||||
`运费设置第 ${i + 1} 行:${columnTitle.value?.startCountTitle}必须大于 0`,
|
||||
);
|
||||
}
|
||||
if (!item.startPrice || item.startPrice <= 0) {
|
||||
@@ -147,7 +125,7 @@ function validate() {
|
||||
}
|
||||
if (!item.extraCount || item.extraCount <= 0) {
|
||||
throw new Error(
|
||||
`运费设置第 ${i + 1} 行:${columnTitle.value.extraCountTitle}必须大于 0`,
|
||||
`运费设置第 ${i + 1} 行:${columnTitle.value?.extraCountTitle}必须大于 0`,
|
||||
);
|
||||
}
|
||||
if (!item.extraPrice || item.extraPrice <= 0) {
|
||||
@@ -159,16 +137,12 @@ function validate() {
|
||||
defineExpose({
|
||||
validate,
|
||||
});
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
areaTree.value = await getAreaTree();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Grid class="w-full">
|
||||
<template #areaIds="{ row }">
|
||||
<!-- TODO 芋艿:可优化,使用 Cascade。不过貌似 antd 在 multiple 貌似有 bug! -->
|
||||
<TreeSelect
|
||||
v-model:value="row.areaIds"
|
||||
:tree-data="areaTree"
|
||||
@@ -181,8 +155,7 @@ onMounted(async () => {
|
||||
class="w-full"
|
||||
multiple
|
||||
tree-checkable
|
||||
:show-checked-strategy="TreeSelect.SHOW_CHILD"
|
||||
:max-tag-count="5"
|
||||
:max-tag-count="1"
|
||||
@change="handleRowChange(row)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<script lang="ts" setup>
|
||||
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 { cloneDeep } from '@vben/utils';
|
||||
import { cloneDeep, fenToYuan, yuanToFen } from '@vben/utils';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
@@ -14,6 +15,7 @@ import {
|
||||
getDeliveryExpressTemplate,
|
||||
updateDeliveryExpressTemplate,
|
||||
} from '#/api/mall/trade/delivery/expressTemplate';
|
||||
import { getAreaTree } from '#/api/system/area';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
@@ -24,6 +26,7 @@ const emit = defineEmits(['success']);
|
||||
const formData = ref<MallDeliveryExpressTemplateApi.ExpressTemplate>();
|
||||
const chargeItemFormRef = ref<InstanceType<typeof ChargeItemForm>>();
|
||||
const freeItemFormRef = ref<InstanceType<typeof FreeItemForm>>();
|
||||
const areaTree = ref<SystemAreaApi.Area[]>([]);
|
||||
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
@@ -38,28 +41,38 @@ const [Form, formApi] = useVbenForm({
|
||||
},
|
||||
labelWidth: 120,
|
||||
},
|
||||
wrapperClass: 'grid-cols-1',
|
||||
wrapperClass: 'grid-cols-3',
|
||||
layout: 'vertical',
|
||||
schema: useFormSchema(),
|
||||
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 =
|
||||
formApi.getValues() as MallDeliveryExpressTemplateApi.ExpressTemplate;
|
||||
await formApi.getValues<MallDeliveryExpressTemplateApi.ExpressTemplate>();
|
||||
formData.value.charges = charges;
|
||||
formApi.setValues({
|
||||
await formApi.setValues({
|
||||
charges,
|
||||
});
|
||||
};
|
||||
|
||||
/** 更新包邮设置 */
|
||||
const handleUpdateFrees = (frees: any[]) => {
|
||||
const handleUpdateFrees = async (
|
||||
frees: MallDeliveryExpressTemplateApi.TemplateFree[],
|
||||
) => {
|
||||
formData.value =
|
||||
formApi.getValues() as MallDeliveryExpressTemplateApi.ExpressTemplate;
|
||||
await formApi.getValues<MallDeliveryExpressTemplateApi.ExpressTemplate>();
|
||||
formData.value.frees = frees;
|
||||
formApi.setValues({
|
||||
await formApi.setValues({
|
||||
frees,
|
||||
});
|
||||
};
|
||||
@@ -71,22 +84,15 @@ const [Modal, modalApi] = useVbenModal({
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证子表单
|
||||
const chargeFormInstance = Array.isArray(chargeItemFormRef.value)
|
||||
? chargeItemFormRef.value[0]
|
||||
: chargeItemFormRef.value;
|
||||
const freeFormInstance = Array.isArray(freeItemFormRef.value)
|
||||
? freeItemFormRef.value[0]
|
||||
: freeItemFormRef.value;
|
||||
|
||||
try {
|
||||
if (chargeFormInstance) {
|
||||
chargeFormInstance.validate();
|
||||
}
|
||||
if (freeFormInstance) {
|
||||
freeFormInstance.validate();
|
||||
}
|
||||
chargeFormInstance.validate();
|
||||
freeFormInstance.validate();
|
||||
} catch (error: any) {
|
||||
message.error(error.message || '子表单验证失败');
|
||||
return;
|
||||
@@ -98,19 +104,21 @@ const [Modal, modalApi] = useVbenModal({
|
||||
await formApi.getValues(),
|
||||
) as MallDeliveryExpressTemplateApi.ExpressTemplate;
|
||||
try {
|
||||
// 前端价格以元展示,提交到后端用分计算
|
||||
data.charges?.forEach((item: any) => {
|
||||
item.startPrice = Math.round(item.startPrice * 100);
|
||||
item.extraPrice = Math.round(item.extraPrice * 100);
|
||||
});
|
||||
data.frees?.forEach((item: any) => {
|
||||
item.freePrice = Math.round(item.freePrice * 100);
|
||||
});
|
||||
|
||||
// 转换金额单位
|
||||
data.charges?.forEach(
|
||||
(item: MallDeliveryExpressTemplateApi.TemplateCharge) => {
|
||||
item.startPrice = yuanToFen(item.startPrice);
|
||||
item.extraPrice = yuanToFen(item.extraPrice);
|
||||
},
|
||||
);
|
||||
data.frees?.forEach(
|
||||
(item: MallDeliveryExpressTemplateApi.TemplateFree) => {
|
||||
item.freePrice = yuanToFen(item.freePrice);
|
||||
},
|
||||
);
|
||||
await (formData.value?.id
|
||||
? updateDeliveryExpressTemplate(data)
|
||||
: createDeliveryExpressTemplate(data));
|
||||
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
@@ -128,68 +136,55 @@ const [Modal, modalApi] = useVbenModal({
|
||||
const data =
|
||||
modalApi.getData<MallDeliveryExpressTemplateApi.ExpressTemplate>();
|
||||
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);
|
||||
} else {
|
||||
await loadFormData(data.id);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// 重置表单数据
|
||||
function resetFormData() {
|
||||
formData.value = {
|
||||
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();
|
||||
}
|
||||
}
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
areaTree.value = await getAreaTree();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-[80%]" :title="getTitle">
|
||||
<Modal class="w-1/2" :title="getTitle">
|
||||
<Form class="mx-3">
|
||||
<template #charges>
|
||||
<ChargeItemForm
|
||||
ref="chargeItemFormRef"
|
||||
:items="formData?.charges ?? []"
|
||||
:charge-mode="formData?.chargeMode ?? 1"
|
||||
:items="formData?.charges"
|
||||
:charge-mode="formData?.chargeMode"
|
||||
:area-tree="areaTree"
|
||||
@update:items="handleUpdateCharges"
|
||||
/>
|
||||
</template>
|
||||
<template #frees>
|
||||
<FreeItemForm
|
||||
ref="freeItemFormRef"
|
||||
:items="formData?.frees ?? []"
|
||||
:charge-mode="formData?.chargeMode ?? 1"
|
||||
:items="formData?.frees"
|
||||
:charge-mode="formData?.chargeMode"
|
||||
:area-tree="areaTree"
|
||||
@update:items="handleUpdateFrees"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -1,44 +1,36 @@
|
||||
<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 { getAreaTree } from '#/api/system/area';
|
||||
|
||||
import { useFreesColumns } from '../data';
|
||||
import { FREE_MODE_TITLE_MAP, useFreesColumns } from '../data';
|
||||
|
||||
interface Props {
|
||||
items?: any[];
|
||||
disabled?: boolean;
|
||||
items?: MallDeliveryExpressTemplateApi.TemplateFree[];
|
||||
chargeMode?: number;
|
||||
areaTree?: SystemAreaApi.Area[];
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
items: () => [],
|
||||
disabled: false,
|
||||
chargeMode: 1,
|
||||
areaTree: () => [],
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:items']);
|
||||
|
||||
const tableData = ref<any[]>([]);
|
||||
const areaTree = ref([]);
|
||||
|
||||
// 根据计费方式设置列标题
|
||||
const columnTitle = computed(() => {
|
||||
const titleMap = {
|
||||
1: { freeCountTitle: '包邮件数' },
|
||||
2: { freeCountTitle: '包邮重量(kg)' },
|
||||
3: { freeCountTitle: '包邮体积(m³)' },
|
||||
};
|
||||
return titleMap[props.chargeMode] || titleMap[1];
|
||||
});
|
||||
const columnTitle = computed(() => FREE_MODE_TITLE_MAP[props.chargeMode]);
|
||||
|
||||
/** 表格配置 */
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: useFreesColumns(),
|
||||
columns: useFreesColumns(props.chargeMode),
|
||||
data: tableData.value,
|
||||
minHeight: 200,
|
||||
autoResize: true,
|
||||
@@ -66,7 +58,6 @@ watch(
|
||||
tableData.value = [...items];
|
||||
await nextTick();
|
||||
await gridApi.grid.reloadData(tableData.value);
|
||||
updateColumnsTitle();
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
@@ -77,30 +68,19 @@ watch(
|
||||
watch(
|
||||
() => 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() {
|
||||
const newRow = {
|
||||
seq: Date.now(),
|
||||
areaIds: [],
|
||||
freeCount: 1,
|
||||
freePrice: 1,
|
||||
freeCount: undefined,
|
||||
freePrice: undefined,
|
||||
};
|
||||
tableData.value.push(newRow);
|
||||
emit('update:items', [...tableData.value]);
|
||||
@@ -115,18 +95,6 @@ function handleDelete(row: any) {
|
||||
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) {
|
||||
const index = tableData.value.findIndex((item) => item.seq === row.seq);
|
||||
@@ -147,11 +115,11 @@ function validate() {
|
||||
}
|
||||
if (!item.freeCount || item.freeCount <= 0) {
|
||||
throw new Error(
|
||||
`包邮设置第 ${i + 1} 行:${columnTitle.value.freeCountTitle}必须大于0`,
|
||||
`包邮设置第 ${i + 1} 行:${columnTitle.value?.freeCountTitle}必须大于 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({
|
||||
validate,
|
||||
});
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
try {
|
||||
areaTree.value = await getAreaTree();
|
||||
} catch (error) {
|
||||
console.error('加载区域数据失败:', error);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Grid class="w-full">
|
||||
<template #areaIds="{ row }">
|
||||
<Cascader
|
||||
<!-- TODO 芋艿:可优化,使用 Cascade。不过貌似 antd 在 multiple 貌似有 bug! -->
|
||||
<TreeSelect
|
||||
v-model:value="row.areaIds"
|
||||
:options="areaTree"
|
||||
:tree-data="areaTree"
|
||||
:field-names="{
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
@@ -184,18 +144,16 @@ onMounted(async () => {
|
||||
placeholder="请选择地区"
|
||||
class="w-full"
|
||||
multiple
|
||||
show-checked-strategy="SHOW_CHILD"
|
||||
:disabled="disabled"
|
||||
@change="handleAreaChange($event, row)"
|
||||
tree-checkable
|
||||
:max-tag-count="1"
|
||||
@change="handleRowChange(row)"
|
||||
/>
|
||||
</template>
|
||||
<template #freeCount="{ row }">
|
||||
<InputNumber
|
||||
v-model:value="row.freeCount"
|
||||
:min="1"
|
||||
class="w-full"
|
||||
:disabled="disabled"
|
||||
@change="handleValueChange('freeCount', $event, row)"
|
||||
@change="handleRowChange(row)"
|
||||
/>
|
||||
</template>
|
||||
<template #freePrice="{ row }">
|
||||
@@ -203,14 +161,11 @@ onMounted(async () => {
|
||||
v-model:value="row.freePrice"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
class="w-full"
|
||||
:disabled="disabled"
|
||||
@change="handleValueChange('freePrice', $event, row)"
|
||||
@change="handleRowChange(row)"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
v-if="!disabled"
|
||||
:actions="[
|
||||
{
|
||||
label: '删除',
|
||||
@@ -224,10 +179,8 @@ onMounted(async () => {
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #bottom>
|
||||
<TableAction
|
||||
v-if="!disabled"
|
||||
class="mt-2 flex justify-center"
|
||||
:actions="[
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user