feat:【mall 商城】快递模版的迁移(antd 40% 表单部分)
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import type { VbenFormSchema } from '#/adapter/form';
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
|
||||||
import { CommonStatusEnum, DICT_TYPE } from '@vben/constants';
|
import { DICT_TYPE } from '@vben/constants';
|
||||||
import { getDictOptions } from '@vben/hooks';
|
import { getDictOptions } from '@vben/hooks';
|
||||||
|
|
||||||
import { z } from '#/adapter/form';
|
import { z } from '#/adapter/form';
|
||||||
@@ -99,6 +99,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
placeholder: '请输入模板名称',
|
placeholder: '请输入模板名称',
|
||||||
},
|
},
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
|
formItemClass: 'col-span-1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldName: 'chargeMode',
|
fieldName: 'chargeMode',
|
||||||
@@ -109,7 +110,8 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
buttonStyle: 'solid',
|
buttonStyle: 'solid',
|
||||||
optionType: 'button',
|
optionType: 'button',
|
||||||
},
|
},
|
||||||
rules: z.number().default(CommonStatusEnum.ENABLE),
|
rules: z.number().default(1),
|
||||||
|
formItemClass: 'col-span-1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldName: 'sort',
|
fieldName: 'sort',
|
||||||
@@ -120,6 +122,25 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
min: 0,
|
min: 0,
|
||||||
},
|
},
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
|
formItemClass: 'col-span-1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'charges',
|
||||||
|
label: '运费设置',
|
||||||
|
formItemClass: 'col-span-3',
|
||||||
|
dependencies: {
|
||||||
|
triggerFields: ['chargeMode'],
|
||||||
|
show: () => true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'frees',
|
||||||
|
label: '包邮设置',
|
||||||
|
formItemClass: 'col-span-3',
|
||||||
|
dependencies: {
|
||||||
|
triggerFields: [''],
|
||||||
|
show: () => true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,247 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
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 { InputNumber, TreeSelect } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { getAreaTree } from '#/api/system/area';
|
||||||
|
|
||||||
|
import { useChargesColumns } from '../data';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
items?: MallDeliveryExpressTemplateApi.TemplateCharge[];
|
||||||
|
chargeMode?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
items: () => [],
|
||||||
|
chargeMode: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
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 [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
gridOptions: {
|
||||||
|
columns: useChargesColumns(),
|
||||||
|
data: tableData.value,
|
||||||
|
minHeight: 200,
|
||||||
|
autoResize: true,
|
||||||
|
border: true,
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'seq',
|
||||||
|
isHover: true,
|
||||||
|
},
|
||||||
|
pagerConfig: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 监听外部传入的数据 */
|
||||||
|
watch(
|
||||||
|
() => props.items,
|
||||||
|
async (items) => {
|
||||||
|
if (!items) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tableData.value = [...items];
|
||||||
|
await nextTick();
|
||||||
|
await gridApi.grid.reloadData(tableData.value);
|
||||||
|
updateColumnsTitle();
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 监听计费方式变化 */
|
||||||
|
watch(
|
||||||
|
() => props.chargeMode,
|
||||||
|
() => {
|
||||||
|
updateColumnsTitle();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 更新列标题 */
|
||||||
|
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 = {
|
||||||
|
areaIds: [],
|
||||||
|
startCount: undefined,
|
||||||
|
startPrice: undefined,
|
||||||
|
extraCount: undefined,
|
||||||
|
extraPrice: undefined,
|
||||||
|
};
|
||||||
|
tableData.value.push(newRow);
|
||||||
|
emit('update:items', [...tableData.value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 处理删除 */
|
||||||
|
function handleDelete(row: any) {
|
||||||
|
const index = tableData.value.findIndex((item) => item.seq === row.seq);
|
||||||
|
if (index !== -1) {
|
||||||
|
tableData.value.splice(index, 1);
|
||||||
|
}
|
||||||
|
emit('update:items', [...tableData.value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 处理行数据变更 */
|
||||||
|
function handleRowChange(row: any) {
|
||||||
|
const index = tableData.value.findIndex((item) => item.seq === row.seq);
|
||||||
|
if (index === -1) {
|
||||||
|
tableData.value.push(row);
|
||||||
|
} else {
|
||||||
|
tableData.value[index] = row;
|
||||||
|
}
|
||||||
|
emit('update:items', [...tableData.value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 表单校验 */
|
||||||
|
function validate() {
|
||||||
|
for (let i = 0; i < tableData.value.length; i++) {
|
||||||
|
const item = tableData.value[i];
|
||||||
|
if (!item.areaIds || item.areaIds.length === 0) {
|
||||||
|
throw new Error(`运费设置第 ${i + 1} 行:区域不能为空`);
|
||||||
|
}
|
||||||
|
if (!item.startCount || item.startCount <= 0) {
|
||||||
|
throw new Error(
|
||||||
|
`运费设置第 ${i + 1} 行:${columnTitle.value.startCountTitle}必须大于 0`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!item.startPrice || item.startPrice <= 0) {
|
||||||
|
throw new Error(`运费设置第 ${i + 1} 行:运费必须大于0`);
|
||||||
|
}
|
||||||
|
if (!item.extraCount || item.extraCount <= 0) {
|
||||||
|
throw new Error(
|
||||||
|
`运费设置第 ${i + 1} 行:${columnTitle.value.extraCountTitle}必须大于 0`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!item.extraPrice || item.extraPrice <= 0) {
|
||||||
|
throw new Error(`运费设置第 ${i + 1} 行:续费必须大于 0`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
validate,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 初始化 */
|
||||||
|
onMounted(async () => {
|
||||||
|
areaTree.value = await getAreaTree();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Grid class="w-full">
|
||||||
|
<template #areaIds="{ row }">
|
||||||
|
<TreeSelect
|
||||||
|
v-model:value="row.areaIds"
|
||||||
|
:tree-data="areaTree"
|
||||||
|
:field-names="{
|
||||||
|
label: 'name',
|
||||||
|
value: 'id',
|
||||||
|
children: 'children',
|
||||||
|
}"
|
||||||
|
placeholder="请选择地区"
|
||||||
|
class="w-full"
|
||||||
|
multiple
|
||||||
|
tree-checkable
|
||||||
|
:show-checked-strategy="TreeSelect.SHOW_CHILD"
|
||||||
|
:max-tag-count="5"
|
||||||
|
@change="handleRowChange(row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #startCount="{ row }">
|
||||||
|
<InputNumber
|
||||||
|
v-model:value="row.startCount"
|
||||||
|
:min="1"
|
||||||
|
@change="handleRowChange(row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #startPrice="{ row }">
|
||||||
|
<InputNumber
|
||||||
|
v-model:value="row.startPrice"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
@change="handleRowChange(row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #extraCount="{ row }">
|
||||||
|
<InputNumber
|
||||||
|
v-model:value="row.extraCount"
|
||||||
|
:min="1"
|
||||||
|
@change="handleRowChange(row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #extraPrice="{ row }">
|
||||||
|
<InputNumber
|
||||||
|
v-model:value="row.extraPrice"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
@change="handleRowChange(row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ row }">
|
||||||
|
<TableAction
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
type: 'link',
|
||||||
|
danger: true,
|
||||||
|
popConfirm: {
|
||||||
|
title: '确认删除该区域吗?',
|
||||||
|
confirm: handleDelete.bind(null, row),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #bottom>
|
||||||
|
<TableAction
|
||||||
|
class="mt-2 flex justify-center"
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: '添加计费区域',
|
||||||
|
type: 'default',
|
||||||
|
onClick: handleAdd,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Grid>
|
||||||
|
</template>
|
||||||
@@ -1,67 +1,29 @@
|
|||||||
<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 { computed, nextTick, onMounted, ref, watch } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
import { useVbenModal } from '@vben/common-ui';
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
import { DICT_TYPE } from '@vben/constants';
|
|
||||||
import { getDictOptions } from '@vben/hooks';
|
|
||||||
import { cloneDeep } from '@vben/utils';
|
import { cloneDeep } from '@vben/utils';
|
||||||
|
|
||||||
import {
|
import { message } from 'ant-design-vue';
|
||||||
Cascader,
|
|
||||||
Form,
|
|
||||||
FormItem,
|
|
||||||
Input,
|
|
||||||
InputNumber,
|
|
||||||
message,
|
|
||||||
RadioGroup,
|
|
||||||
} from 'ant-design-vue';
|
|
||||||
|
|
||||||
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { useVbenForm } from '#/adapter/form';
|
||||||
import {
|
import {
|
||||||
createDeliveryExpressTemplate,
|
createDeliveryExpressTemplate,
|
||||||
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 { useChargesColumns, useFreesColumns } from '../data';
|
import { useFormSchema } from '../data';
|
||||||
|
import ChargeItemForm from './charge-item-form.vue';
|
||||||
|
import FreeItemForm from './free-item-form.vue';
|
||||||
|
|
||||||
const emit = defineEmits(['success']);
|
const emit = defineEmits(['success']);
|
||||||
const formRef = ref();
|
const formData = ref<MallDeliveryExpressTemplateApi.ExpressTemplate>();
|
||||||
const chargesGridRef = ref();
|
const chargeItemFormRef = ref<InstanceType<typeof ChargeItemForm>>();
|
||||||
const freesGridRef = ref();
|
const freeItemFormRef = ref<InstanceType<typeof FreeItemForm>>();
|
||||||
|
|
||||||
const formData = ref<any>({
|
|
||||||
id: undefined,
|
|
||||||
name: '',
|
|
||||||
chargeMode: 1,
|
|
||||||
sort: 0,
|
|
||||||
charges: [],
|
|
||||||
frees: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
const areaTree = ref([]);
|
|
||||||
const columnTitleMap = new Map();
|
|
||||||
const columnTitle = ref({
|
|
||||||
startCountTitle: '首件',
|
|
||||||
extraCountTitle: '续件',
|
|
||||||
freeCountTitle: '包邮件数',
|
|
||||||
});
|
|
||||||
|
|
||||||
const chargeModeOptions = getDictOptions(
|
|
||||||
DICT_TYPE.EXPRESS_CHARGE_MODE,
|
|
||||||
'number',
|
|
||||||
);
|
|
||||||
const cascaderProps = {
|
|
||||||
multiple: true,
|
|
||||||
checkStrictly: false,
|
|
||||||
label: 'name',
|
|
||||||
value: 'id',
|
|
||||||
children: 'children',
|
|
||||||
};
|
|
||||||
|
|
||||||
const getTitle = computed(() => {
|
const getTitle = computed(() => {
|
||||||
return formData.value?.id
|
return formData.value?.id
|
||||||
@@ -69,68 +31,73 @@ const getTitle = computed(() => {
|
|||||||
: $t('ui.actionTitle.create', ['快递模板']);
|
: $t('ui.actionTitle.create', ['快递模板']);
|
||||||
});
|
});
|
||||||
|
|
||||||
const formRules = {
|
const [Form, formApi] = useVbenForm({
|
||||||
name: [{ required: true, message: '模板名称不能为空', trigger: 'blur' }],
|
commonConfig: {
|
||||||
chargeMode: [
|
componentProps: {
|
||||||
{ required: true, message: '配送计费方式不能为空', trigger: 'change' },
|
class: 'w-full',
|
||||||
],
|
},
|
||||||
sort: [{ required: true, message: '分类排序不能为空', trigger: 'blur' }],
|
labelWidth: 120,
|
||||||
|
},
|
||||||
|
wrapperClass: 'grid-cols-1',
|
||||||
|
layout: 'vertical',
|
||||||
|
schema: useFormSchema(),
|
||||||
|
showDefaultActions: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 更新运费设置 */
|
||||||
|
const handleUpdateCharges = (charges: any[]) => {
|
||||||
|
formData.value =
|
||||||
|
formApi.getValues() as MallDeliveryExpressTemplateApi.ExpressTemplate;
|
||||||
|
formData.value.charges = charges;
|
||||||
|
formApi.setValues({
|
||||||
|
charges,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 运费设置表格
|
/** 更新包邮设置 */
|
||||||
const [ChargesGrid, chargesGridApi] = useVbenVxeGrid({
|
const handleUpdateFrees = (frees: any[]) => {
|
||||||
gridOptions: {
|
formData.value =
|
||||||
columns: useChargesColumns(),
|
formApi.getValues() as MallDeliveryExpressTemplateApi.ExpressTemplate;
|
||||||
data: formData.value.charges,
|
formData.value.frees = frees;
|
||||||
minHeight: 200,
|
formApi.setValues({
|
||||||
autoResize: true,
|
frees,
|
||||||
border: true,
|
});
|
||||||
rowConfig: {
|
};
|
||||||
keyField: 'seq',
|
|
||||||
isHover: true,
|
|
||||||
},
|
|
||||||
pagerConfig: {
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
toolbarConfig: {
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 包邮设置表格
|
|
||||||
const [FreesGrid, freesGridApi] = useVbenVxeGrid({
|
|
||||||
gridOptions: {
|
|
||||||
columns: useFreesColumns(),
|
|
||||||
data: formData.value.frees,
|
|
||||||
minHeight: 200,
|
|
||||||
autoResize: true,
|
|
||||||
border: true,
|
|
||||||
rowConfig: {
|
|
||||||
keyField: 'seq',
|
|
||||||
isHover: true,
|
|
||||||
},
|
|
||||||
pagerConfig: {
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
toolbarConfig: {
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
|
/** 创建或更新快递模板 */
|
||||||
const [Modal, modalApi] = useVbenModal({
|
const [Modal, modalApi] = useVbenModal({
|
||||||
async onConfirm() {
|
async onConfirm() {
|
||||||
|
const { valid } = await formApi.validate();
|
||||||
|
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 {
|
try {
|
||||||
await formRef.value?.validate();
|
if (chargeFormInstance) {
|
||||||
validateTables();
|
chargeFormInstance.validate();
|
||||||
} catch {
|
}
|
||||||
|
if (freeFormInstance) {
|
||||||
|
freeFormInstance.validate();
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error.message || '子表单验证失败');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
|
// 提交表单
|
||||||
|
const data = cloneDeep(
|
||||||
|
await formApi.getValues(),
|
||||||
|
) as MallDeliveryExpressTemplateApi.ExpressTemplate;
|
||||||
try {
|
try {
|
||||||
const data = cloneDeep(formData.value);
|
|
||||||
// 前端价格以元展示,提交到后端用分计算
|
// 前端价格以元展示,提交到后端用分计算
|
||||||
data.charges?.forEach((item: any) => {
|
data.charges?.forEach((item: any) => {
|
||||||
item.startPrice = Math.round(item.startPrice * 100);
|
item.startPrice = Math.round(item.startPrice * 100);
|
||||||
@@ -144,6 +111,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
? updateDeliveryExpressTemplate(data)
|
? updateDeliveryExpressTemplate(data)
|
||||||
: createDeliveryExpressTemplate(data));
|
: createDeliveryExpressTemplate(data));
|
||||||
|
|
||||||
|
// 关闭并提示
|
||||||
await modalApi.close();
|
await modalApi.close();
|
||||||
emit('success');
|
emit('success');
|
||||||
message.success($t('ui.actionMessage.operationSuccess'));
|
message.success($t('ui.actionMessage.operationSuccess'));
|
||||||
@@ -153,21 +121,18 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
resetFormData();
|
formData.value = undefined;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 加载数据
|
||||||
const data =
|
const data =
|
||||||
modalApi.getData<MallDeliveryExpressTemplateApi.ExpressTemplate>();
|
modalApi.getData<MallDeliveryExpressTemplateApi.ExpressTemplate>();
|
||||||
if (!data || !data.id) {
|
if (!data || !data.id) {
|
||||||
resetFormData();
|
resetFormData();
|
||||||
initDefaultData();
|
await formApi.setValues(formData.value);
|
||||||
} else {
|
} else {
|
||||||
await loadFormData(data.id);
|
await loadFormData(data.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
await nextTick();
|
|
||||||
updateTables();
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -183,27 +148,12 @@ function resetFormData() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化默认数据
|
|
||||||
function initDefaultData() {
|
|
||||||
formData.value.charges = [
|
|
||||||
{
|
|
||||||
seq: Date.now(),
|
|
||||||
areaIds: [1],
|
|
||||||
startCount: 2,
|
|
||||||
startPrice: 5,
|
|
||||||
extraCount: 5,
|
|
||||||
extraPrice: 10,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加载表单数据
|
// 加载表单数据
|
||||||
async function loadFormData(id: number) {
|
async function loadFormData(id: number) {
|
||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
try {
|
try {
|
||||||
const data = await getDeliveryExpressTemplate(id);
|
const data = await getDeliveryExpressTemplate(id);
|
||||||
formData.value = data;
|
formData.value = data;
|
||||||
columnTitle.value = columnTitleMap.get(formData.value.chargeMode);
|
|
||||||
|
|
||||||
// 前端价格以元展示
|
// 前端价格以元展示
|
||||||
formData.value.charges?.forEach((item: any, index: number) => {
|
formData.value.charges?.forEach((item: any, index: number) => {
|
||||||
@@ -215,358 +165,34 @@ async function loadFormData(id: number) {
|
|||||||
item.seq = item.seq || Date.now() + index;
|
item.seq = item.seq || Date.now() + index;
|
||||||
item.freePrice = item.freePrice / 100;
|
item.freePrice = item.freePrice / 100;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 设置到 values
|
||||||
|
await formApi.setValues(formData.value);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.unlock();
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新表格数据
|
|
||||||
async function updateTables() {
|
|
||||||
await nextTick();
|
|
||||||
if (chargesGridApi.grid) {
|
|
||||||
await chargesGridApi.grid.reloadData(formData.value.charges || []);
|
|
||||||
}
|
|
||||||
if (freesGridApi.grid) {
|
|
||||||
await freesGridApi.grid.reloadData(formData.value.frees || []);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计费方式改变
|
|
||||||
function handleChargeModeChange(value: number) {
|
|
||||||
columnTitle.value = columnTitleMap.get(value);
|
|
||||||
updateChargesColumns();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新运费表格列标题
|
|
||||||
function updateChargesColumns() {
|
|
||||||
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;
|
|
||||||
|
|
||||||
chargesGridApi.grid.reloadColumn(columns);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新包邮表格列标题
|
|
||||||
function updateFreesColumns() {
|
|
||||||
const columns = useFreesColumns();
|
|
||||||
const freeCountCol = columns.find((col) => col.field === 'freeCount');
|
|
||||||
|
|
||||||
if (freeCountCol) freeCountCol.title = columnTitle.value.freeCountTitle;
|
|
||||||
|
|
||||||
freesGridApi.grid.reloadColumn(columns);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加计费区域
|
|
||||||
function addChargeArea() {
|
|
||||||
const newRow = {
|
|
||||||
seq: Date.now(),
|
|
||||||
areaIds: [],
|
|
||||||
startCount: 1,
|
|
||||||
startPrice: 1,
|
|
||||||
extraCount: 1,
|
|
||||||
extraPrice: 1,
|
|
||||||
};
|
|
||||||
formData.value.charges.push(newRow);
|
|
||||||
chargesGridApi.grid.reloadData(formData.value.charges);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除计费区域
|
|
||||||
function deleteChargeArea(row: any) {
|
|
||||||
const index = formData.value.charges.findIndex(
|
|
||||||
(item: any) => item.seq === row.seq,
|
|
||||||
);
|
|
||||||
if (index !== -1) {
|
|
||||||
formData.value.charges.splice(index, 1);
|
|
||||||
chargesGridApi.grid.reloadData(formData.value.charges);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加包邮区域
|
|
||||||
function addFreeArea() {
|
|
||||||
const newRow = {
|
|
||||||
seq: Date.now(),
|
|
||||||
areaIds: [],
|
|
||||||
freeCount: 1,
|
|
||||||
freePrice: 1,
|
|
||||||
};
|
|
||||||
formData.value.frees.push(newRow);
|
|
||||||
freesGridApi.grid.reloadData(formData.value.frees);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除包邮区域
|
|
||||||
function deleteFreeArea(row: any) {
|
|
||||||
const index = formData.value.frees.findIndex(
|
|
||||||
(item: any) => item.seq === row.seq,
|
|
||||||
);
|
|
||||||
if (index !== -1) {
|
|
||||||
formData.value.frees.splice(index, 1);
|
|
||||||
freesGridApi.grid.reloadData(formData.value.frees);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理运费区域变更
|
|
||||||
function handleChargeAreaChange(areaIds: any[], row: any) {
|
|
||||||
row.areaIds = areaIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理包邮区域变更
|
|
||||||
function handleFreeAreaChange(areaIds: any[], row: any) {
|
|
||||||
row.areaIds = areaIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理数值变更
|
|
||||||
function handleChargeValueChange(field: string, value: any, row: any) {
|
|
||||||
row[field] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleFreeValueChange(field: string, value: any, row: any) {
|
|
||||||
row[field] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 表格验证
|
|
||||||
function validateTables() {
|
|
||||||
for (let i = 0; i < formData.value.charges.length; i++) {
|
|
||||||
const item = formData.value.charges[i];
|
|
||||||
if (!item.areaIds || item.areaIds.length === 0) {
|
|
||||||
throw new Error(`运费设置第 ${i + 1} 行:区域不能为空`);
|
|
||||||
}
|
|
||||||
if (!item.startCount || item.startCount <= 0) {
|
|
||||||
throw new Error(`运费设置第 ${i + 1} 行:首件数必须大于0`);
|
|
||||||
}
|
|
||||||
if (!item.startPrice || item.startPrice <= 0) {
|
|
||||||
throw new Error(`运费设置第 ${i + 1} 行:运费必须大于0`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < formData.value.frees.length; i++) {
|
|
||||||
const item = formData.value.frees[i];
|
|
||||||
if (!item.areaIds || item.areaIds.length === 0) {
|
|
||||||
throw new Error(`包邮设置第 ${i + 1} 行:区域不能为空`);
|
|
||||||
}
|
|
||||||
if (!item.freeCount || item.freeCount <= 0) {
|
|
||||||
throw new Error(`包邮设置第 ${i + 1} 行:包邮件数必须大于0`);
|
|
||||||
}
|
|
||||||
if (!item.freePrice || item.freePrice <= 0) {
|
|
||||||
throw new Error(`包邮设置第 ${i + 1} 行:包邮金额必须大于0`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化数据
|
|
||||||
async function initData() {
|
|
||||||
// 表头标题和计费方式的映射
|
|
||||||
columnTitleMap.set(1, {
|
|
||||||
startCountTitle: '首件',
|
|
||||||
extraCountTitle: '续件',
|
|
||||||
freeCountTitle: '包邮件数',
|
|
||||||
});
|
|
||||||
columnTitleMap.set(2, {
|
|
||||||
startCountTitle: '首件重量(kg)',
|
|
||||||
extraCountTitle: '续件重量(kg)',
|
|
||||||
freeCountTitle: '包邮重量(kg)',
|
|
||||||
});
|
|
||||||
columnTitleMap.set(3, {
|
|
||||||
startCountTitle: '首件体积(m³)',
|
|
||||||
extraCountTitle: '续件体积(m³)',
|
|
||||||
freeCountTitle: '包邮体积(m³)',
|
|
||||||
});
|
|
||||||
|
|
||||||
// 加载区域数据
|
|
||||||
try {
|
|
||||||
areaTree.value = await getAreaTree();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('加载区域数据失败:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
initData();
|
|
||||||
});
|
|
||||||
|
|
||||||
// 监听计费方式变化
|
|
||||||
watch(
|
|
||||||
() => formData.value.chargeMode,
|
|
||||||
() => {
|
|
||||||
updateChargesColumns();
|
|
||||||
updateFreesColumns();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal class="w-[80%]" :title="getTitle">
|
<Modal class="w-[80%]" :title="getTitle">
|
||||||
<Form
|
<Form class="mx-3">
|
||||||
ref="formRef"
|
<template #charges>
|
||||||
:model="formData"
|
<ChargeItemForm
|
||||||
:rules="formRules"
|
ref="chargeItemFormRef"
|
||||||
:label-col="{ span: 4 }"
|
:items="formData?.charges ?? []"
|
||||||
:wrapper-col="{ span: 20 }"
|
:charge-mode="formData?.chargeMode ?? 1"
|
||||||
class="mx-4"
|
@update:items="handleUpdateCharges"
|
||||||
>
|
|
||||||
<FormItem label="模板名称" name="name">
|
|
||||||
<Input v-model:value="formData.name" placeholder="请输入模板名称" />
|
|
||||||
</FormItem>
|
|
||||||
|
|
||||||
<FormItem label="计费方式" name="chargeMode">
|
|
||||||
<RadioGroup
|
|
||||||
v-model:value="formData.chargeMode"
|
|
||||||
:options="chargeModeOptions"
|
|
||||||
@change="handleChargeModeChange"
|
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</template>
|
||||||
|
<template #frees>
|
||||||
<FormItem label="运费设置">
|
<FreeItemForm
|
||||||
<ChargesGrid ref="chargesGridRef">
|
ref="freeItemFormRef"
|
||||||
<template #areaIds="{ row }">
|
:items="formData?.frees ?? []"
|
||||||
<Cascader
|
:charge-mode="formData?.chargeMode ?? 1"
|
||||||
v-model:value="row.areaIds"
|
@update:items="handleUpdateFrees"
|
||||||
:options="areaTree"
|
|
||||||
:field-names="cascaderProps"
|
|
||||||
placeholder="请选择地区"
|
|
||||||
class="w-full"
|
|
||||||
multiple
|
|
||||||
show-checked-strategy="SHOW_CHILD"
|
|
||||||
@change="handleChargeAreaChange($event, row)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #startCount="{ row }">
|
|
||||||
<InputNumber
|
|
||||||
v-model:value="row.startCount"
|
|
||||||
:min="1"
|
|
||||||
class="w-full"
|
|
||||||
@change="handleChargeValueChange('startCount', $event, row)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #startPrice="{ row }">
|
|
||||||
<InputNumber
|
|
||||||
v-model:value="row.startPrice"
|
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
class="w-full"
|
|
||||||
@change="handleChargeValueChange('startPrice', $event, row)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #extraCount="{ row }">
|
|
||||||
<InputNumber
|
|
||||||
v-model:value="row.extraCount"
|
|
||||||
:min="1"
|
|
||||||
class="w-full"
|
|
||||||
@change="handleChargeValueChange('extraCount', $event, row)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #extraPrice="{ row }">
|
|
||||||
<InputNumber
|
|
||||||
v-model:value="row.extraPrice"
|
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
class="w-full"
|
|
||||||
@change="handleChargeValueChange('extraPrice', $event, row)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #actions="{ row }">
|
|
||||||
<TableAction
|
|
||||||
:actions="[
|
|
||||||
{
|
|
||||||
label: '删除',
|
|
||||||
type: 'link',
|
|
||||||
danger: true,
|
|
||||||
popConfirm: {
|
|
||||||
title: '确认删除该区域吗?',
|
|
||||||
confirm: deleteChargeArea.bind(null, row),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #bottom>
|
|
||||||
<TableAction
|
|
||||||
class="mt-2 flex justify-center"
|
|
||||||
:actions="[
|
|
||||||
{
|
|
||||||
label: '添加计费区域',
|
|
||||||
type: 'default',
|
|
||||||
onClick: addChargeArea,
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</ChargesGrid>
|
|
||||||
</FormItem>
|
|
||||||
|
|
||||||
<FormItem label="包邮设置">
|
|
||||||
<FreesGrid ref="freesGridRef">
|
|
||||||
<template #areaIds="{ row }">
|
|
||||||
<Cascader
|
|
||||||
v-model:value="row.areaIds"
|
|
||||||
:options="areaTree"
|
|
||||||
:field-names="cascaderProps"
|
|
||||||
placeholder="请选择地区"
|
|
||||||
class="w-full"
|
|
||||||
multiple
|
|
||||||
show-checked-strategy="SHOW_CHILD"
|
|
||||||
@change="handleFreeAreaChange($event, row)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #freeCount="{ row }">
|
|
||||||
<InputNumber
|
|
||||||
v-model:value="row.freeCount"
|
|
||||||
:min="1"
|
|
||||||
class="w-full"
|
|
||||||
@change="handleFreeValueChange('freeCount', $event, row)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #freePrice="{ row }">
|
|
||||||
<InputNumber
|
|
||||||
v-model:value="row.freePrice"
|
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
class="w-full"
|
|
||||||
@change="handleFreeValueChange('freePrice', $event, row)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #actions="{ row }">
|
|
||||||
<TableAction
|
|
||||||
:actions="[
|
|
||||||
{
|
|
||||||
label: '删除',
|
|
||||||
type: 'link',
|
|
||||||
danger: true,
|
|
||||||
popConfirm: {
|
|
||||||
title: '确认删除该区域吗?',
|
|
||||||
confirm: deleteFreeArea.bind(null, row),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #bottom>
|
|
||||||
<TableAction
|
|
||||||
class="mt-2 flex justify-center"
|
|
||||||
:actions="[
|
|
||||||
{
|
|
||||||
label: '添加包邮区域',
|
|
||||||
type: 'default',
|
|
||||||
onClick: addFreeArea,
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</FreesGrid>
|
|
||||||
</FormItem>
|
|
||||||
|
|
||||||
<FormItem label="排序" name="sort">
|
|
||||||
<InputNumber
|
|
||||||
v-model:value="formData.sort"
|
|
||||||
:min="0"
|
|
||||||
placeholder="请输入显示顺序"
|
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</template>
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,242 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, nextTick, onMounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import { Cascader, InputNumber } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { getAreaTree } from '#/api/system/area';
|
||||||
|
|
||||||
|
import { useFreesColumns } from '../data';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
items?: any[];
|
||||||
|
disabled?: boolean;
|
||||||
|
chargeMode?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
items: () => [],
|
||||||
|
disabled: false,
|
||||||
|
chargeMode: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
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 [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
gridOptions: {
|
||||||
|
columns: useFreesColumns(),
|
||||||
|
data: tableData.value,
|
||||||
|
minHeight: 200,
|
||||||
|
autoResize: true,
|
||||||
|
border: true,
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'seq',
|
||||||
|
isHover: true,
|
||||||
|
},
|
||||||
|
pagerConfig: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 监听外部传入的数据 */
|
||||||
|
watch(
|
||||||
|
() => props.items,
|
||||||
|
async (items) => {
|
||||||
|
if (!items) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tableData.value = [...items];
|
||||||
|
await nextTick();
|
||||||
|
await gridApi.grid.reloadData(tableData.value);
|
||||||
|
updateColumnsTitle();
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 监听计费方式变化 */
|
||||||
|
watch(
|
||||||
|
() => props.chargeMode,
|
||||||
|
() => {
|
||||||
|
updateColumnsTitle();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
};
|
||||||
|
tableData.value.push(newRow);
|
||||||
|
emit('update:items', [...tableData.value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 处理删除 */
|
||||||
|
function handleDelete(row: any) {
|
||||||
|
const index = tableData.value.findIndex((item) => item.seq === row.seq);
|
||||||
|
if (index !== -1) {
|
||||||
|
tableData.value.splice(index, 1);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
if (index === -1) {
|
||||||
|
tableData.value.push(row);
|
||||||
|
} else {
|
||||||
|
tableData.value[index] = row;
|
||||||
|
}
|
||||||
|
emit('update:items', [...tableData.value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 表单校验 */
|
||||||
|
function validate() {
|
||||||
|
for (let i = 0; i < tableData.value.length; i++) {
|
||||||
|
const item = tableData.value[i];
|
||||||
|
if (!item.areaIds || item.areaIds.length === 0) {
|
||||||
|
throw new Error(`包邮设置第 ${i + 1} 行:区域不能为空`);
|
||||||
|
}
|
||||||
|
if (!item.freeCount || item.freeCount <= 0) {
|
||||||
|
throw new Error(
|
||||||
|
`包邮设置第 ${i + 1} 行:${columnTitle.value.freeCountTitle}必须大于0`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!item.freePrice || item.freePrice <= 0) {
|
||||||
|
throw new Error(`包邮设置第 ${i + 1} 行:包邮金额必须大于0`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
validate,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 初始化 */
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
areaTree.value = await getAreaTree();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载区域数据失败:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Grid class="w-full">
|
||||||
|
<template #areaIds="{ row }">
|
||||||
|
<Cascader
|
||||||
|
v-model:value="row.areaIds"
|
||||||
|
:options="areaTree"
|
||||||
|
:field-names="{
|
||||||
|
label: 'name',
|
||||||
|
value: 'id',
|
||||||
|
children: 'children',
|
||||||
|
}"
|
||||||
|
placeholder="请选择地区"
|
||||||
|
class="w-full"
|
||||||
|
multiple
|
||||||
|
show-checked-strategy="SHOW_CHILD"
|
||||||
|
:disabled="disabled"
|
||||||
|
@change="handleAreaChange($event, row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #freeCount="{ row }">
|
||||||
|
<InputNumber
|
||||||
|
v-model:value="row.freeCount"
|
||||||
|
:min="1"
|
||||||
|
class="w-full"
|
||||||
|
:disabled="disabled"
|
||||||
|
@change="handleValueChange('freeCount', $event, row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #freePrice="{ row }">
|
||||||
|
<InputNumber
|
||||||
|
v-model:value="row.freePrice"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
class="w-full"
|
||||||
|
:disabled="disabled"
|
||||||
|
@change="handleValueChange('freePrice', $event, row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ row }">
|
||||||
|
<TableAction
|
||||||
|
v-if="!disabled"
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
type: 'link',
|
||||||
|
danger: true,
|
||||||
|
popConfirm: {
|
||||||
|
title: '确认删除该区域吗?',
|
||||||
|
confirm: handleDelete.bind(null, row),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #bottom>
|
||||||
|
<TableAction
|
||||||
|
v-if="!disabled"
|
||||||
|
class="mt-2 flex justify-center"
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: '添加包邮区域',
|
||||||
|
type: 'default',
|
||||||
|
onClick: handleAdd,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Grid>
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user