Merge branch 'dev' of https://gitee.com/yudaocode/yudao-ui-admin-vben into dev
This commit is contained in:
@@ -38,9 +38,9 @@ export namespace MallDeliveryExpressTemplateApi {
|
|||||||
/** 排序 */
|
/** 排序 */
|
||||||
sort: number;
|
sort: number;
|
||||||
/** 计费区域列表 */
|
/** 计费区域列表 */
|
||||||
templateCharge: TemplateCharge[];
|
charges: TemplateCharge[];
|
||||||
/** 包邮区域列表 */
|
/** 包邮区域列表 */
|
||||||
templateFree: TemplateFree[];
|
frees: TemplateFree[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 运费模板精简信息 */
|
/** 运费模板精简信息 */
|
||||||
|
|||||||
@@ -29,21 +29,18 @@ export namespace MallDeliveryPickUpStoreApi {
|
|||||||
longitude: number;
|
longitude: number;
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status: number;
|
status: number;
|
||||||
/** 绑定用户编号组数 */
|
|
||||||
verifyUserIds: number[];
|
|
||||||
/** 营业时间 用于fieldMappingTime */
|
/** 营业时间 用于fieldMappingTime */
|
||||||
rangeTime: any[];
|
rangeTime: any[];
|
||||||
|
/** 绑定用户编号组数 */
|
||||||
|
verifyUserIds?: number[];
|
||||||
|
verifyUsers?: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 绑定自提店员请求 */
|
/** 绑定自提店员请求 */
|
||||||
export interface BindStaffRequest {
|
export interface DeliveryPickUpBindReqVO {
|
||||||
id?: number;
|
id?: number;
|
||||||
/** 门店名称 */
|
|
||||||
name: string;
|
|
||||||
/** 门店编号 */
|
|
||||||
storeId: number;
|
|
||||||
/** 用户编号列表 */
|
/** 用户编号列表 */
|
||||||
userIds: number[];
|
verifyUserIds: number[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,8 +86,8 @@ export function deleteDeliveryPickUpStore(id: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 绑定自提店员 */
|
/** 绑定自提店员 */
|
||||||
export function bindStoreStaffId(
|
export function bindDeliveryPickUpStore(
|
||||||
data: MallDeliveryPickUpStoreApi.BindStaffRequest,
|
data: MallDeliveryPickUpStoreApi.DeliveryPickUpBindReqVO,
|
||||||
) {
|
) {
|
||||||
return requestClient.post('/trade/delivery/pick-up-store/bind', data);
|
return requestClient.post('/trade/delivery/pick-up-store/bind', data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,18 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'code',
|
fieldName: 'code',
|
||||||
label: '公司编码',
|
label: '公司编码',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入快递编码',
|
||||||
|
},
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'name',
|
fieldName: 'name',
|
||||||
label: '公司名称',
|
label: '公司名称',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入快递名称',
|
||||||
|
},
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -34,6 +40,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
fieldName: 'logo',
|
fieldName: 'logo',
|
||||||
label: '公司 logo',
|
label: '公司 logo',
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
|
help: '推荐 180x180 图片分辨率',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldName: 'sort',
|
fieldName: 'sort',
|
||||||
@@ -41,6 +48,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
component: 'InputNumber',
|
component: 'InputNumber',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
min: 0,
|
min: 0,
|
||||||
|
placeholder: '请输入显示顺序',
|
||||||
},
|
},
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
},
|
},
|
||||||
@@ -65,17 +73,26 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
fieldName: 'name',
|
fieldName: 'name',
|
||||||
label: '快递公司名称',
|
label: '快递公司名称',
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入快递公司名称',
|
||||||
|
allowClear: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldName: 'code',
|
fieldName: 'code',
|
||||||
label: '快递公司编号',
|
label: '快递公司编号',
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入快递公司编号',
|
||||||
|
allowClear: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldName: 'status',
|
fieldName: 'status',
|
||||||
label: '状态',
|
label: '状态',
|
||||||
component: 'Select',
|
component: 'Select',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
|
placeholder: '请选择状态',
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||||
},
|
},
|
||||||
@@ -89,18 +106,22 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||||||
{
|
{
|
||||||
field: 'id',
|
field: 'id',
|
||||||
title: '编号',
|
title: '编号',
|
||||||
|
minWidth: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'code',
|
field: 'code',
|
||||||
title: '公司编码',
|
title: '公司编码',
|
||||||
|
minWidth: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'name',
|
field: 'name',
|
||||||
title: '公司名称',
|
title: '公司名称',
|
||||||
|
minWidth: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'logo',
|
field: 'logo',
|
||||||
title: '公司 logo',
|
title: '公司 logo',
|
||||||
|
minWidth: 120,
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellImage',
|
name: 'CellImage',
|
||||||
},
|
},
|
||||||
@@ -108,10 +129,12 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||||||
{
|
{
|
||||||
field: 'sort',
|
field: 'sort',
|
||||||
title: '显示顺序',
|
title: '显示顺序',
|
||||||
|
minWidth: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'status',
|
field: 'status',
|
||||||
title: '状态',
|
title: '状态',
|
||||||
|
minWidth: 100,
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellDict',
|
name: 'CellDict',
|
||||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||||
@@ -120,6 +143,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||||||
{
|
{
|
||||||
field: 'createTime',
|
field: 'createTime',
|
||||||
title: '创建时间',
|
title: '创建时间',
|
||||||
|
minWidth: 180,
|
||||||
formatter: 'formatDateTime',
|
formatter: 'formatDateTime',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const [FormModal, formModalApi] = useVbenModal({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/** 刷新表格 */
|
/** 刷新表格 */
|
||||||
function onRefresh() {
|
function handleRefresh() {
|
||||||
gridApi.query();
|
gridApi.query();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,11 +51,9 @@ async function handleDelete(row: MallDeliveryExpressApi.DeliveryExpress) {
|
|||||||
duration: 0,
|
duration: 0,
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await deleteDeliveryExpress(row.id as number);
|
await deleteDeliveryExpress(row.id!);
|
||||||
message.success({
|
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
handleRefresh();
|
||||||
});
|
|
||||||
onRefresh();
|
|
||||||
} finally {
|
} finally {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
}
|
}
|
||||||
@@ -82,6 +80,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
},
|
},
|
||||||
rowConfig: {
|
rowConfig: {
|
||||||
keyField: 'id',
|
keyField: 'id',
|
||||||
|
isHover: true,
|
||||||
},
|
},
|
||||||
toolbarConfig: {
|
toolbarConfig: {
|
||||||
refresh: true,
|
refresh: true,
|
||||||
@@ -93,7 +92,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height>
|
<Page auto-content-height>
|
||||||
<FormModal @success="onRefresh" />
|
<FormModal @success="handleRefresh" />
|
||||||
<Grid table-title="快递公司列表">
|
<Grid table-title="快递公司列表">
|
||||||
<template #toolbar-tools>
|
<template #toolbar-tools>
|
||||||
<TableAction
|
<TableAction
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal class="w-2/5" :title="getTitle">
|
<Modal :title="getTitle" class="w-2/5">
|
||||||
<Form class="mx-4" />
|
<Form class="mx-4" />
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,11 +1,109 @@
|
|||||||
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';
|
||||||
|
|
||||||
|
/** 计费方式列标题映射 */
|
||||||
|
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(
|
||||||
|
chargeMode = 1,
|
||||||
|
): VxeTableGridOptions['columns'] {
|
||||||
|
const chargeTitleMap = CHARGE_MODE_TITLE_MAP[chargeMode];
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field: 'areaIds',
|
||||||
|
title: '区域',
|
||||||
|
minWidth: 300,
|
||||||
|
slots: { default: 'areaIds' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'startCount',
|
||||||
|
title: chargeTitleMap?.startCountTitle,
|
||||||
|
width: 120,
|
||||||
|
slots: { default: 'startCount' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'startPrice',
|
||||||
|
title: '运费(元)',
|
||||||
|
width: 120,
|
||||||
|
slots: { default: 'startPrice' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'extraCount',
|
||||||
|
title: chargeTitleMap?.extraCountTitle,
|
||||||
|
width: 120,
|
||||||
|
slots: { default: 'extraCount' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'extraPrice',
|
||||||
|
title: '续费(元)',
|
||||||
|
width: 120,
|
||||||
|
slots: { default: 'extraPrice' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
width: 80,
|
||||||
|
fixed: 'right',
|
||||||
|
slots: { default: 'actions' },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 包邮设置表格列 */
|
||||||
|
export function useFreesColumns(
|
||||||
|
chargeMode = 1,
|
||||||
|
): VxeTableGridOptions['columns'] {
|
||||||
|
const freeTitleMap = FREE_MODE_TITLE_MAP[chargeMode];
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field: 'areaIds',
|
||||||
|
title: '区域',
|
||||||
|
minWidth: 300,
|
||||||
|
slots: { default: 'areaIds' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'freeCount',
|
||||||
|
title: freeTitleMap?.freeCountTitle,
|
||||||
|
width: 120,
|
||||||
|
slots: { default: 'freeCount' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'freePrice',
|
||||||
|
title: '包邮金额(元)',
|
||||||
|
width: 120,
|
||||||
|
slots: { default: 'freePrice' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
width: 80,
|
||||||
|
fixed: 'right',
|
||||||
|
slots: { default: 'actions' },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/** 新增/修改的表单 */
|
/** 新增/修改的表单 */
|
||||||
export function useFormSchema(): VbenFormSchema[] {
|
export function useFormSchema(): VbenFormSchema[] {
|
||||||
return [
|
return [
|
||||||
@@ -21,6 +119,9 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'name',
|
fieldName: 'name',
|
||||||
label: '模板名称',
|
label: '模板名称',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入模板名称',
|
||||||
|
},
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -32,17 +133,30 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
buttonStyle: 'solid',
|
buttonStyle: 'solid',
|
||||||
optionType: 'button',
|
optionType: 'button',
|
||||||
},
|
},
|
||||||
rules: z.number().default(CommonStatusEnum.ENABLE),
|
rules: z.number().default(1),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldName: 'sort',
|
fieldName: 'sort',
|
||||||
label: '显示顺序',
|
label: '显示顺序',
|
||||||
component: 'InputNumber',
|
component: 'InputNumber',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
|
placeholder: '请输入显示顺序',
|
||||||
min: 0,
|
min: 0,
|
||||||
},
|
},
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'charges',
|
||||||
|
label: '运费设置',
|
||||||
|
component: 'Input',
|
||||||
|
formItemClass: 'col-span-3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'frees',
|
||||||
|
label: '包邮设置',
|
||||||
|
component: 'Input',
|
||||||
|
formItemClass: 'col-span-3',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,12 +167,17 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
fieldName: 'name',
|
fieldName: 'name',
|
||||||
label: '模板名称',
|
label: '模板名称',
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入模板名称',
|
||||||
|
allowClear: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldName: 'chargeMode',
|
fieldName: 'chargeMode',
|
||||||
label: '计费方式',
|
label: '计费方式',
|
||||||
component: 'Select',
|
component: 'Select',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
|
placeholder: '请选择计费方式',
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
options: getDictOptions(DICT_TYPE.EXPRESS_CHARGE_MODE, 'number'),
|
options: getDictOptions(DICT_TYPE.EXPRESS_CHARGE_MODE, 'number'),
|
||||||
},
|
},
|
||||||
@@ -72,14 +191,17 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||||||
{
|
{
|
||||||
field: 'id',
|
field: 'id',
|
||||||
title: '编号',
|
title: '编号',
|
||||||
|
minWidth: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'name',
|
field: 'name',
|
||||||
title: '模板名称',
|
title: '模板名称',
|
||||||
|
minWidth: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'chargeMode',
|
field: 'chargeMode',
|
||||||
title: '计费方式',
|
title: '计费方式',
|
||||||
|
minWidth: 100,
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellDict',
|
name: 'CellDict',
|
||||||
props: { type: DICT_TYPE.EXPRESS_CHARGE_MODE },
|
props: { type: DICT_TYPE.EXPRESS_CHARGE_MODE },
|
||||||
@@ -88,10 +210,12 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||||||
{
|
{
|
||||||
field: 'sort',
|
field: 'sort',
|
||||||
title: '显示顺序',
|
title: '显示顺序',
|
||||||
|
minWidth: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'createTime',
|
field: 'createTime',
|
||||||
title: '创建时间',
|
title: '创建时间',
|
||||||
|
minWidth: 180,
|
||||||
formatter: 'formatDateTime',
|
formatter: 'formatDateTime',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const [FormModal, formModalApi] = useVbenModal({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/** 刷新表格 */
|
/** 刷新表格 */
|
||||||
function onRefresh() {
|
function handleRefresh() {
|
||||||
gridApi.query();
|
gridApi.query();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,10 +46,8 @@ async function handleDelete(
|
|||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await deleteDeliveryExpressTemplate(row.id as number);
|
await deleteDeliveryExpressTemplate(row.id as number);
|
||||||
message.success({
|
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
handleRefresh();
|
||||||
});
|
|
||||||
onRefresh();
|
|
||||||
} finally {
|
} finally {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
}
|
}
|
||||||
@@ -76,6 +74,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
},
|
},
|
||||||
rowConfig: {
|
rowConfig: {
|
||||||
keyField: 'id',
|
keyField: 'id',
|
||||||
|
isHover: true,
|
||||||
},
|
},
|
||||||
toolbarConfig: {
|
toolbarConfig: {
|
||||||
refresh: true,
|
refresh: true,
|
||||||
@@ -87,7 +86,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height>
|
<Page auto-content-height>
|
||||||
<FormModal @success="onRefresh" />
|
<FormModal @success="handleRefresh" />
|
||||||
<Grid table-title="快递模板列表">
|
<Grid table-title="快递模板列表">
|
||||||
<template #toolbar-tools>
|
<template #toolbar-tools>
|
||||||
<TableAction
|
<TableAction
|
||||||
|
|||||||
@@ -0,0 +1,220 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import type { MallDeliveryExpressTemplateApi } from '#/api/mall/trade/delivery/expressTemplate';
|
||||||
|
import type { SystemAreaApi } from '#/api/system/area';
|
||||||
|
|
||||||
|
import { computed, nextTick, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import { InputNumber, TreeSelect } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
|
||||||
|
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 columnTitle = computed(() => CHARGE_MODE_TITLE_MAP[props.chargeMode]);
|
||||||
|
|
||||||
|
/** 表格配置 */
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
gridOptions: {
|
||||||
|
columns: useChargesColumns(props.chargeMode),
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 监听计费方式变化 */
|
||||||
|
watch(
|
||||||
|
() => props.chargeMode,
|
||||||
|
() => {
|
||||||
|
const columns = useChargesColumns(props.chargeMode);
|
||||||
|
if (gridApi.grid && columns) {
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Grid class="w-full">
|
||||||
|
<template #areaIds="{ row }">
|
||||||
|
<!-- TODO 芋艿:可优化,使用 Cascade。不过貌似 antd 在 multiple 貌似有 bug! -->
|
||||||
|
<TreeSelect
|
||||||
|
v-model:value="row.areaIds"
|
||||||
|
:tree-data="areaTree"
|
||||||
|
:field-names="{
|
||||||
|
label: 'name',
|
||||||
|
value: 'id',
|
||||||
|
children: 'children',
|
||||||
|
}"
|
||||||
|
placeholder="请选择地区"
|
||||||
|
class="w-full"
|
||||||
|
multiple
|
||||||
|
tree-checkable
|
||||||
|
:max-tag-count="1"
|
||||||
|
@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,9 +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, fenToYuan, yuanToFen } from '@vben/utils';
|
||||||
|
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
@@ -13,12 +15,19 @@ 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';
|
||||||
|
import ChargeItemForm from './charge-item-form.vue';
|
||||||
|
import FreeItemForm from './free-item-form.vue';
|
||||||
|
|
||||||
const emit = defineEmits(['success']);
|
const emit = defineEmits(['success']);
|
||||||
const formData = ref<MallDeliveryExpressTemplateApi.ExpressTemplate>();
|
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(() => {
|
const getTitle = computed(() => {
|
||||||
return formData.value?.id
|
return formData.value?.id
|
||||||
? $t('ui.actionTitle.edit', ['快递模板'])
|
? $t('ui.actionTitle.edit', ['快递模板'])
|
||||||
@@ -30,26 +39,83 @@ const [Form, formApi] = useVbenForm({
|
|||||||
componentProps: {
|
componentProps: {
|
||||||
class: 'w-full',
|
class: 'w-full',
|
||||||
},
|
},
|
||||||
formItemClass: 'col-span-2',
|
|
||||||
labelWidth: 120,
|
labelWidth: 120,
|
||||||
},
|
},
|
||||||
layout: 'horizontal',
|
wrapperClass: 'grid-cols-3',
|
||||||
|
layout: 'vertical',
|
||||||
schema: useFormSchema(),
|
schema: useFormSchema(),
|
||||||
showDefaultActions: false,
|
showDefaultActions: false,
|
||||||
|
handleValuesChange: (values, changedFields) => {
|
||||||
|
// 目的:触发子表单的 columns 变化
|
||||||
|
if (changedFields.includes('chargeMode')) {
|
||||||
|
formData.value!.chargeMode = values.chargeMode;
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO @xingyu:城市处理;
|
/** 更新运费设置 */
|
||||||
|
const handleUpdateCharges = async (
|
||||||
|
charges: MallDeliveryExpressTemplateApi.TemplateCharge[],
|
||||||
|
) => {
|
||||||
|
formData.value =
|
||||||
|
await formApi.getValues<MallDeliveryExpressTemplateApi.ExpressTemplate>();
|
||||||
|
formData.value.charges = charges;
|
||||||
|
await formApi.setValues({
|
||||||
|
charges,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 更新包邮设置 */
|
||||||
|
const handleUpdateFrees = async (
|
||||||
|
frees: MallDeliveryExpressTemplateApi.TemplateFree[],
|
||||||
|
) => {
|
||||||
|
formData.value =
|
||||||
|
await formApi.getValues<MallDeliveryExpressTemplateApi.ExpressTemplate>();
|
||||||
|
formData.value.frees = frees;
|
||||||
|
await formApi.setValues({
|
||||||
|
frees,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 创建或更新快递模板 */
|
||||||
const [Modal, modalApi] = useVbenModal({
|
const [Modal, modalApi] = useVbenModal({
|
||||||
async onConfirm() {
|
async onConfirm() {
|
||||||
const { valid } = await formApi.validate();
|
const { valid } = await formApi.validate();
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const chargeFormInstance = Array.isArray(chargeItemFormRef.value)
|
||||||
|
? chargeItemFormRef.value[0]
|
||||||
|
: chargeItemFormRef.value;
|
||||||
|
const freeFormInstance = Array.isArray(freeItemFormRef.value)
|
||||||
|
? freeItemFormRef.value[0]
|
||||||
|
: freeItemFormRef.value;
|
||||||
|
try {
|
||||||
|
chargeFormInstance.validate();
|
||||||
|
freeFormInstance.validate();
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error.message || '子表单验证失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
// 提交表单
|
// 提交表单
|
||||||
const data =
|
const data = cloneDeep(
|
||||||
(await formApi.getValues()) as MallDeliveryExpressTemplateApi.ExpressTemplate;
|
await formApi.getValues(),
|
||||||
|
) as MallDeliveryExpressTemplateApi.ExpressTemplate;
|
||||||
try {
|
try {
|
||||||
|
// 转换金额单位
|
||||||
|
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
|
await (formData.value?.id
|
||||||
? updateDeliveryExpressTemplate(data)
|
? updateDeliveryExpressTemplate(data)
|
||||||
: createDeliveryExpressTemplate(data));
|
: createDeliveryExpressTemplate(data));
|
||||||
@@ -75,6 +141,18 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
try {
|
try {
|
||||||
formData.value = await getDeliveryExpressTemplate(data.id);
|
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
|
// 设置到 values
|
||||||
await formApi.setValues(formData.value);
|
await formApi.setValues(formData.value);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -82,10 +160,34 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** 初始化 */
|
||||||
|
onMounted(async () => {
|
||||||
|
areaTree.value = await getAreaTree();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal class="w-2/5" :title="getTitle">
|
<Modal class="w-1/2" :title="getTitle">
|
||||||
<Form class="mx-4" />
|
<Form class="mx-3">
|
||||||
|
<template #charges>
|
||||||
|
<ChargeItemForm
|
||||||
|
ref="chargeItemFormRef"
|
||||||
|
: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"
|
||||||
|
:area-tree="areaTree"
|
||||||
|
@update:items="handleUpdateFrees"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,195 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import type { MallDeliveryExpressTemplateApi } from '#/api/mall/trade/delivery/expressTemplate';
|
||||||
|
import type { SystemAreaApi } from '#/api/system/area';
|
||||||
|
|
||||||
|
import { computed, nextTick, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import { InputNumber, TreeSelect } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
|
||||||
|
import { FREE_MODE_TITLE_MAP, useFreesColumns } from '../data';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
items?: MallDeliveryExpressTemplateApi.TemplateFree[];
|
||||||
|
chargeMode?: number;
|
||||||
|
areaTree?: SystemAreaApi.Area[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
items: () => [],
|
||||||
|
chargeMode: 1,
|
||||||
|
areaTree: () => [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:items']);
|
||||||
|
|
||||||
|
const tableData = ref<any[]>([]);
|
||||||
|
const columnTitle = computed(() => FREE_MODE_TITLE_MAP[props.chargeMode]);
|
||||||
|
|
||||||
|
/** 表格配置 */
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
gridOptions: {
|
||||||
|
columns: useFreesColumns(props.chargeMode),
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 监听计费方式变化 */
|
||||||
|
watch(
|
||||||
|
() => props.chargeMode,
|
||||||
|
() => {
|
||||||
|
const columns = useFreesColumns(props.chargeMode);
|
||||||
|
if (gridApi.grid && columns) {
|
||||||
|
gridApi.grid.reloadColumn(columns);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 处理新增 */
|
||||||
|
function handleAdd() {
|
||||||
|
const newRow = {
|
||||||
|
areaIds: [],
|
||||||
|
freeCount: undefined,
|
||||||
|
freePrice: 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.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,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Grid class="w-full">
|
||||||
|
<template #areaIds="{ row }">
|
||||||
|
<!-- TODO 芋艿:可优化,使用 Cascade。不过貌似 antd 在 multiple 貌似有 bug! -->
|
||||||
|
<TreeSelect
|
||||||
|
v-model:value="row.areaIds"
|
||||||
|
:tree-data="areaTree"
|
||||||
|
:field-names="{
|
||||||
|
label: 'name',
|
||||||
|
value: 'id',
|
||||||
|
children: 'children',
|
||||||
|
}"
|
||||||
|
placeholder="请选择地区"
|
||||||
|
class="w-full"
|
||||||
|
multiple
|
||||||
|
tree-checkable
|
||||||
|
:max-tag-count="1"
|
||||||
|
@change="handleRowChange(row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #freeCount="{ row }">
|
||||||
|
<InputNumber
|
||||||
|
v-model:value="row.freeCount"
|
||||||
|
:min="1"
|
||||||
|
@change="handleRowChange(row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #freePrice="{ row }">
|
||||||
|
<InputNumber
|
||||||
|
v-model:value="row.freePrice"
|
||||||
|
: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>
|
||||||
@@ -20,43 +20,64 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
show: () => false,
|
show: () => false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
component: 'ImageUpload',
|
|
||||||
fieldName: 'logo',
|
|
||||||
label: '门店logo',
|
|
||||||
rules: 'required',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'name',
|
fieldName: 'name',
|
||||||
label: '门店名称',
|
label: '门店名称',
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入门店名称',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'phone',
|
fieldName: 'phone',
|
||||||
label: '门店手机',
|
label: '门店手机',
|
||||||
rules: 'mobileRequired',
|
rules: 'mobileRequired',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入门店手机',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'ImageUpload',
|
||||||
|
fieldName: 'logo',
|
||||||
|
label: '门店 logo',
|
||||||
|
rules: 'required',
|
||||||
|
formItemClass: 'col-span-2',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请上传门店 logo',
|
||||||
|
},
|
||||||
|
help: '推荐 180x180 图片分辨率',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Textarea',
|
component: 'Textarea',
|
||||||
fieldName: 'introduction',
|
fieldName: 'introduction',
|
||||||
label: '门店简介',
|
label: '门店简介',
|
||||||
|
formItemClass: 'col-span-2',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入门店简介',
|
||||||
|
rows: 4,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldName: 'areaId',
|
fieldName: 'areaId',
|
||||||
label: '地址',
|
label: '门店所在地区',
|
||||||
component: 'ApiTreeSelect',
|
component: 'ApiTreeSelect',
|
||||||
|
rules: 'required',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: () => getAreaTree(),
|
api: () => getAreaTree(),
|
||||||
fieldNames: { label: 'name', value: 'id', children: 'children' },
|
fieldNames: { label: 'name', value: 'id', children: 'children' },
|
||||||
|
placeholder: '请选择省市区',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'detailAddress',
|
fieldName: 'detailAddress',
|
||||||
label: '详细地址',
|
label: '门店详细地址',
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入门店详细地址',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'TimeRangePicker',
|
component: 'TimeRangePicker',
|
||||||
@@ -66,33 +87,9 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
componentProps: {
|
componentProps: {
|
||||||
format: 'HH:mm',
|
format: 'HH:mm',
|
||||||
minuteStep: 15,
|
minuteStep: 15,
|
||||||
disabledTime: () => {
|
placeholder: ['开始时间', '结束时间'],
|
||||||
return {
|
|
||||||
disabledHours: () => {
|
|
||||||
return [0, 1, 2, 3, 4, 5, 6, 7];
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
component: 'Input',
|
|
||||||
fieldName: 'longitude',
|
|
||||||
label: '经度',
|
|
||||||
rules: 'required',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
component: 'Input',
|
|
||||||
fieldName: 'latitude',
|
|
||||||
label: '纬度',
|
|
||||||
rules: 'required',
|
|
||||||
},
|
|
||||||
// TODO @xingyu:缺少地图
|
|
||||||
{
|
|
||||||
component: 'Input',
|
|
||||||
fieldName: 'getGeo',
|
|
||||||
label: '获取经纬度',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
fieldName: 'status',
|
fieldName: 'status',
|
||||||
label: '门店状态',
|
label: '门店状态',
|
||||||
@@ -104,6 +101,24 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
},
|
},
|
||||||
rules: z.number().default(CommonStatusEnum.ENABLE),
|
rules: z.number().default(CommonStatusEnum.ENABLE),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
fieldName: 'longitude',
|
||||||
|
label: '经度',
|
||||||
|
rules: 'required',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入门店经度',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
fieldName: 'latitude',
|
||||||
|
label: '纬度',
|
||||||
|
rules: 'required',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入门店纬度',
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,23 +152,7 @@ export function useBindFormSchema(): VbenFormSchema[] {
|
|||||||
fieldNames: { label: 'nickname', value: 'id' },
|
fieldNames: { label: 'nickname', value: 'id' },
|
||||||
mode: 'tags',
|
mode: 'tags',
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
},
|
placeholder: '请选择门店店员',
|
||||||
},
|
|
||||||
{
|
|
||||||
component: 'Select',
|
|
||||||
fieldName: 'verifyUsers',
|
|
||||||
label: '店员列表',
|
|
||||||
rules: 'required',
|
|
||||||
componentProps: {
|
|
||||||
options: [],
|
|
||||||
mode: 'tags',
|
|
||||||
},
|
|
||||||
dependencies: {
|
|
||||||
triggerFields: ['verifyUserIds'],
|
|
||||||
trigger(values, form) {
|
|
||||||
form.setFieldValue('verifyUsers', values.verifyUserIds);
|
|
||||||
},
|
|
||||||
disabled: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -166,11 +165,19 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
fieldName: 'phone',
|
fieldName: 'phone',
|
||||||
label: '门店手机',
|
label: '门店手机',
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入门店手机',
|
||||||
|
allowClear: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldName: 'name',
|
fieldName: 'name',
|
||||||
label: '门店名称',
|
label: '门店名称',
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入门店名称',
|
||||||
|
allowClear: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldName: 'status',
|
fieldName: 'status',
|
||||||
@@ -179,6 +186,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
componentProps: {
|
componentProps: {
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||||
|
placeholder: '请选择门店状态',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -188,6 +196,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
componentProps: {
|
componentProps: {
|
||||||
...getRangePickerDefaultProps(),
|
...getRangePickerDefaultProps(),
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
|
placeholder: ['开始时间', '结束时间'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -199,10 +208,12 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||||||
{
|
{
|
||||||
field: 'id',
|
field: 'id',
|
||||||
title: '编号',
|
title: '编号',
|
||||||
|
minWidth: 80,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'logo',
|
field: 'logo',
|
||||||
title: '门店logo',
|
title: '门店 logo',
|
||||||
|
minWidth: 100,
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellImage',
|
name: 'CellImage',
|
||||||
},
|
},
|
||||||
@@ -210,18 +221,22 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||||||
{
|
{
|
||||||
field: 'name',
|
field: 'name',
|
||||||
title: '门店名称',
|
title: '门店名称',
|
||||||
|
minWidth: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'phone',
|
field: 'phone',
|
||||||
title: '门店手机',
|
title: '门店手机',
|
||||||
|
minWidth: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'detailAddress',
|
field: 'detailAddress',
|
||||||
title: '地址',
|
title: '地址',
|
||||||
|
minWidth: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'openingTime',
|
field: 'openingTime',
|
||||||
title: '营业时间',
|
title: '营业时间',
|
||||||
|
minWidth: 160,
|
||||||
formatter: ({ row }) => {
|
formatter: ({ row }) => {
|
||||||
return `${row.openingTime} ~ ${row.closingTime}`;
|
return `${row.openingTime} ~ ${row.closingTime}`;
|
||||||
},
|
},
|
||||||
@@ -229,6 +244,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||||||
{
|
{
|
||||||
field: 'status',
|
field: 'status',
|
||||||
title: '开启状态',
|
title: '开启状态',
|
||||||
|
minWidth: 100,
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellDict',
|
name: 'CellDict',
|
||||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||||
@@ -237,11 +253,12 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||||||
{
|
{
|
||||||
field: 'createTime',
|
field: 'createTime',
|
||||||
title: '创建时间',
|
title: '创建时间',
|
||||||
|
minWidth: 160,
|
||||||
formatter: 'formatDateTime',
|
formatter: 'formatDateTime',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 200,
|
width: 220,
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'actions' },
|
slots: { default: 'actions' },
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
|||||||
import type { MallDeliveryPickUpStoreApi } from '#/api/mall/trade/delivery/pickUpStore';
|
import type { MallDeliveryPickUpStoreApi } from '#/api/mall/trade/delivery/pickUpStore';
|
||||||
|
|
||||||
import { Page, useVbenModal } from '@vben/common-ui';
|
import { Page, useVbenModal } from '@vben/common-ui';
|
||||||
|
import { isEmpty } from '@vben/utils';
|
||||||
|
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
@@ -28,7 +29,7 @@ const [BindFormModal, bindFormModalApi] = useVbenModal({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/** 刷新表格 */
|
/** 刷新表格 */
|
||||||
function onRefresh() {
|
function handleRefresh() {
|
||||||
gridApi.query();
|
gridApi.query();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,10 +56,8 @@ async function handleDelete(row: MallDeliveryPickUpStoreApi.PickUpStore) {
|
|||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await deleteDeliveryPickUpStore(row.id as number);
|
await deleteDeliveryPickUpStore(row.id as number);
|
||||||
message.success({
|
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
handleRefresh();
|
||||||
});
|
|
||||||
onRefresh();
|
|
||||||
} finally {
|
} finally {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
}
|
}
|
||||||
@@ -85,6 +84,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
},
|
},
|
||||||
rowConfig: {
|
rowConfig: {
|
||||||
keyField: 'id',
|
keyField: 'id',
|
||||||
|
isHover: true,
|
||||||
},
|
},
|
||||||
toolbarConfig: {
|
toolbarConfig: {
|
||||||
refresh: true,
|
refresh: true,
|
||||||
@@ -96,7 +96,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height>
|
<Page auto-content-height>
|
||||||
<FormModal @success="onRefresh" />
|
<FormModal @success="handleRefresh" />
|
||||||
<BindFormModal />
|
<BindFormModal />
|
||||||
<Grid table-title="门店列表">
|
<Grid table-title="门店列表">
|
||||||
<template #toolbar-tools>
|
<template #toolbar-tools>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { MallDeliveryPickUpStoreApi } from '#/api/mall/trade/delivery/pickUpStore';
|
import type { MallDeliveryPickUpStoreApi } from '#/api/mall/trade/delivery/pickUpStore';
|
||||||
|
|
||||||
import { computed, ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
import { useVbenModal } from '@vben/common-ui';
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ import { message } from 'ant-design-vue';
|
|||||||
|
|
||||||
import { useVbenForm } from '#/adapter/form';
|
import { useVbenForm } from '#/adapter/form';
|
||||||
import {
|
import {
|
||||||
bindStoreStaffId,
|
bindDeliveryPickUpStore,
|
||||||
getDeliveryPickUpStore,
|
getDeliveryPickUpStore,
|
||||||
} from '#/api/mall/trade/delivery/pickUpStore';
|
} from '#/api/mall/trade/delivery/pickUpStore';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
@@ -18,11 +18,6 @@ import { useBindFormSchema } from '../data';
|
|||||||
|
|
||||||
const emit = defineEmits(['success']);
|
const emit = defineEmits(['success']);
|
||||||
const formData = ref<MallDeliveryPickUpStoreApi.PickUpStore>();
|
const formData = ref<MallDeliveryPickUpStoreApi.PickUpStore>();
|
||||||
const getTitle = computed(() => {
|
|
||||||
return formData.value?.id
|
|
||||||
? $t('ui.actionTitle.edit', ['绑定店员'])
|
|
||||||
: $t('ui.actionTitle.create', ['绑定店员']);
|
|
||||||
});
|
|
||||||
|
|
||||||
const [Form, formApi] = useVbenForm({
|
const [Form, formApi] = useVbenForm({
|
||||||
commonConfig: {
|
commonConfig: {
|
||||||
@@ -46,9 +41,9 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
// 提交表单
|
// 提交表单
|
||||||
const data =
|
const data =
|
||||||
(await formApi.getValues()) as MallDeliveryPickUpStoreApi.BindStaffRequest;
|
(await formApi.getValues()) as MallDeliveryPickUpStoreApi.DeliveryPickUpBindReqVO;
|
||||||
try {
|
try {
|
||||||
await bindStoreStaffId(data);
|
await bindDeliveryPickUpStore(data);
|
||||||
// 关闭并提示
|
// 关闭并提示
|
||||||
await modalApi.close();
|
await modalApi.close();
|
||||||
emit('success');
|
emit('success');
|
||||||
@@ -64,7 +59,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
}
|
}
|
||||||
// 加载数据
|
// 加载数据
|
||||||
const data =
|
const data =
|
||||||
modalApi.getData<MallDeliveryPickUpStoreApi.BindStaffRequest>();
|
modalApi.getData<MallDeliveryPickUpStoreApi.DeliveryPickUpBindReqVO>();
|
||||||
if (!data || !data.id) {
|
if (!data || !data.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -72,6 +67,9 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
try {
|
try {
|
||||||
formData.value = await getDeliveryPickUpStore(data.id);
|
formData.value = await getDeliveryPickUpStore(data.id);
|
||||||
// 设置到 values
|
// 设置到 values
|
||||||
|
formData.value.verifyUserIds = formData.value.verifyUsers?.map(
|
||||||
|
(item: any) => item.id,
|
||||||
|
);
|
||||||
await formApi.setValues(formData.value);
|
await formApi.setValues(formData.value);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.unlock();
|
modalApi.unlock();
|
||||||
@@ -81,7 +79,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal class="w-2/5" :title="getTitle">
|
<Modal title="绑定店员" class="w-2/5">
|
||||||
<Form class="mx-4" />
|
<Form class="mx-4" />
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ import { computed, ref } from 'vue';
|
|||||||
|
|
||||||
import { useVbenModal } from '@vben/common-ui';
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
|
||||||
import { message } from 'ant-design-vue';
|
import { Button, message } from 'ant-design-vue';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
import { useVbenForm } from '#/adapter/form';
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
import { getTradeConfig } from '#/api/mall/trade/config';
|
||||||
import {
|
import {
|
||||||
createDeliveryPickUpStore,
|
createDeliveryPickUpStore,
|
||||||
getDeliveryPickUpStore,
|
getDeliveryPickUpStore,
|
||||||
@@ -22,19 +23,63 @@ const emit = defineEmits(['success']);
|
|||||||
const formData = ref<MallDeliveryPickUpStoreApi.PickUpStore>();
|
const formData = ref<MallDeliveryPickUpStoreApi.PickUpStore>();
|
||||||
const getTitle = computed(() => {
|
const getTitle = computed(() => {
|
||||||
return formData.value?.id
|
return formData.value?.id
|
||||||
? $t('ui.actionTitle.edit', ['自提订单'])
|
? $t('ui.actionTitle.edit', ['门店'])
|
||||||
: $t('ui.actionTitle.create', ['自提订单']);
|
: $t('ui.actionTitle.create', ['门店']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mapVisible = ref(false); // 是否显示地图弹窗
|
||||||
|
const tencentLbsUrl = ref(''); // 腾讯位置服务 url
|
||||||
|
|
||||||
|
/** 获取经纬度相关方法 */
|
||||||
|
function selectAddress(loc: any) {
|
||||||
|
if (loc.latlng?.lat) {
|
||||||
|
formApi.setFieldValue('latitude', loc.latlng.lat);
|
||||||
|
}
|
||||||
|
if (loc.latlng?.lng) {
|
||||||
|
formApi.setFieldValue('longitude', loc.latlng.lng);
|
||||||
|
}
|
||||||
|
mapVisible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 初始化腾讯地图 */
|
||||||
|
async function initTencentLbsMap() {
|
||||||
|
// 从配置中获取腾讯地图的 key
|
||||||
|
const data = await getTradeConfig();
|
||||||
|
const key = data.tencentLbsKey;
|
||||||
|
if (!key) {
|
||||||
|
message.warning('请先配置腾讯位置服务的 key');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置 url
|
||||||
|
if (!window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(window as any).selectAddress = selectAddress;
|
||||||
|
window.addEventListener(
|
||||||
|
'message',
|
||||||
|
(event) => {
|
||||||
|
// 接收位置信息,用户选择确认位置点后选点组件会触发该事件,回传用户的位置信息
|
||||||
|
const loc = event.data;
|
||||||
|
if (loc && loc.module === 'locationPicker') {
|
||||||
|
// 防止其他应用也会向该页面 post 信息,需判断 module 是否为 'locationPicker'
|
||||||
|
(window.parent as any).selectAddress(loc);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
tencentLbsUrl.value = `https://apis.map.qq.com/tools/locpicker?type=1&key=${key}&referer=myapp`;
|
||||||
|
}
|
||||||
|
|
||||||
const [Form, formApi] = useVbenForm({
|
const [Form, formApi] = useVbenForm({
|
||||||
commonConfig: {
|
commonConfig: {
|
||||||
componentProps: {
|
componentProps: {
|
||||||
class: 'w-full',
|
class: 'w-full',
|
||||||
},
|
},
|
||||||
formItemClass: 'col-span-2',
|
|
||||||
labelWidth: 120,
|
labelWidth: 120,
|
||||||
},
|
},
|
||||||
fieldMappingTime: [['rangeTime', ['openingTime', 'closingTime'], 'HH:mm']],
|
fieldMappingTime: [['rangeTime', ['openingTime', 'closingTime'], 'HH:mm']],
|
||||||
|
wrapperClass: 'grid-cols-2',
|
||||||
layout: 'horizontal',
|
layout: 'horizontal',
|
||||||
schema: useFormSchema(),
|
schema: useFormSchema(),
|
||||||
showDefaultActions: false,
|
showDefaultActions: false,
|
||||||
@@ -70,6 +115,8 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
// 加载数据
|
// 加载数据
|
||||||
const data = modalApi.getData<MallDeliveryPickUpStoreApi.PickUpStore>();
|
const data = modalApi.getData<MallDeliveryPickUpStoreApi.PickUpStore>();
|
||||||
if (!data || !data.id) {
|
if (!data || !data.id) {
|
||||||
|
// 初始化地图
|
||||||
|
await initTencentLbsMap();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
@@ -83,13 +130,32 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
await formApi.setValues(formData.value);
|
await formApi.setValues(formData.value);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.unlock();
|
modalApi.unlock();
|
||||||
|
// 初始化地图
|
||||||
|
await initTencentLbsMap();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal class="w-2/5" :title="getTitle">
|
<Modal :title="getTitle" class="w-2/5">
|
||||||
<Form class="mx-4" />
|
<Form class="mx-4" />
|
||||||
|
<template #prepend-footer>
|
||||||
|
<Button @click="mapVisible = true"> 获取经纬度 </Button>
|
||||||
|
</template>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<!-- 地图弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:open="mapVisible"
|
||||||
|
title="获取经纬度"
|
||||||
|
:width="800"
|
||||||
|
:footer="null"
|
||||||
|
>
|
||||||
|
<iframe
|
||||||
|
v-if="mapVisible && tencentLbsUrl"
|
||||||
|
:src="tencentLbsUrl"
|
||||||
|
class="h-[600px] w-full border-0"
|
||||||
|
></iframe>
|
||||||
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -38,9 +38,9 @@ export namespace MallDeliveryExpressTemplateApi {
|
|||||||
/** 排序 */
|
/** 排序 */
|
||||||
sort: number;
|
sort: number;
|
||||||
/** 计费区域列表 */
|
/** 计费区域列表 */
|
||||||
templateCharge: TemplateCharge[];
|
charges: TemplateCharge[];
|
||||||
/** 包邮区域列表 */
|
/** 包邮区域列表 */
|
||||||
templateFree: TemplateFree[];
|
frees: TemplateFree[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 运费模板精简信息 */
|
/** 运费模板精简信息 */
|
||||||
|
|||||||
@@ -21,12 +21,18 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'code',
|
fieldName: 'code',
|
||||||
label: '公司编码',
|
label: '公司编码',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入快递编码',
|
||||||
|
},
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'name',
|
fieldName: 'name',
|
||||||
label: '公司名称',
|
label: '公司名称',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入快递名称',
|
||||||
|
},
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -34,6 +40,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
fieldName: 'logo',
|
fieldName: 'logo',
|
||||||
label: '公司 logo',
|
label: '公司 logo',
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
|
help: '推荐 180x180 图片分辨率',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldName: 'sort',
|
fieldName: 'sort',
|
||||||
@@ -41,6 +48,9 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
component: 'InputNumber',
|
component: 'InputNumber',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
min: 0,
|
min: 0,
|
||||||
|
placeholder: '请输入显示顺序',
|
||||||
|
controlsPosition: 'right',
|
||||||
|
class: '!w-full',
|
||||||
},
|
},
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
},
|
},
|
||||||
@@ -65,18 +75,27 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
fieldName: 'name',
|
fieldName: 'name',
|
||||||
label: '快递公司名称',
|
label: '快递公司名称',
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入快递公司名称',
|
||||||
|
allowClear: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldName: 'code',
|
fieldName: 'code',
|
||||||
label: '快递公司编号',
|
label: '快递公司编号',
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入快递公司编号',
|
||||||
|
allowClear: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldName: 'status',
|
fieldName: 'status',
|
||||||
label: '状态',
|
label: '状态',
|
||||||
component: 'Select',
|
component: 'Select',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
clearable: true,
|
placeholder: '请选择状态',
|
||||||
|
allowClear: true,
|
||||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -89,18 +108,22 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||||||
{
|
{
|
||||||
field: 'id',
|
field: 'id',
|
||||||
title: '编号',
|
title: '编号',
|
||||||
|
minWidth: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'code',
|
field: 'code',
|
||||||
title: '公司编码',
|
title: '公司编码',
|
||||||
|
minWidth: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'name',
|
field: 'name',
|
||||||
title: '公司名称',
|
title: '公司名称',
|
||||||
|
minWidth: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'logo',
|
field: 'logo',
|
||||||
title: '公司 logo',
|
title: '公司 logo',
|
||||||
|
minWidth: 120,
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellImage',
|
name: 'CellImage',
|
||||||
},
|
},
|
||||||
@@ -108,10 +131,12 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||||||
{
|
{
|
||||||
field: 'sort',
|
field: 'sort',
|
||||||
title: '显示顺序',
|
title: '显示顺序',
|
||||||
|
minWidth: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'status',
|
field: 'status',
|
||||||
title: '状态',
|
title: '状态',
|
||||||
|
minWidth: 100,
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellDict',
|
name: 'CellDict',
|
||||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||||
@@ -120,6 +145,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||||||
{
|
{
|
||||||
field: 'createTime',
|
field: 'createTime',
|
||||||
title: '创建时间',
|
title: '创建时间',
|
||||||
|
minWidth: 180,
|
||||||
formatter: 'formatDateTime',
|
formatter: 'formatDateTime',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const [FormModal, formModalApi] = useVbenModal({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/** 刷新表格 */
|
/** 刷新表格 */
|
||||||
function onRefresh() {
|
function handleRefresh() {
|
||||||
gridApi.query();
|
gridApi.query();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,9 +50,9 @@ async function handleDelete(row: MallDeliveryExpressApi.DeliveryExpress) {
|
|||||||
text: $t('ui.actionMessage.deleting', [row.name]),
|
text: $t('ui.actionMessage.deleting', [row.name]),
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await deleteDeliveryExpress(row.id as number);
|
await deleteDeliveryExpress(row.id!);
|
||||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||||
onRefresh();
|
handleRefresh();
|
||||||
} finally {
|
} finally {
|
||||||
loadingInstance.close();
|
loadingInstance.close();
|
||||||
}
|
}
|
||||||
@@ -79,6 +79,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
},
|
},
|
||||||
rowConfig: {
|
rowConfig: {
|
||||||
keyField: 'id',
|
keyField: 'id',
|
||||||
|
isHover: true,
|
||||||
},
|
},
|
||||||
toolbarConfig: {
|
toolbarConfig: {
|
||||||
refresh: true,
|
refresh: true,
|
||||||
@@ -90,7 +91,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height>
|
<Page auto-content-height>
|
||||||
<FormModal @success="onRefresh" />
|
<FormModal @success="handleRefresh" />
|
||||||
<Grid table-title="快递公司列表">
|
<Grid table-title="快递公司列表">
|
||||||
<template #toolbar-tools>
|
<template #toolbar-tools>
|
||||||
<TableAction
|
<TableAction
|
||||||
@@ -117,6 +118,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
:actions="[
|
:actions="[
|
||||||
{
|
{
|
||||||
label: $t('common.edit'),
|
label: $t('common.edit'),
|
||||||
|
type: 'primary',
|
||||||
link: true,
|
link: true,
|
||||||
icon: ACTION_ICON.EDIT,
|
icon: ACTION_ICON.EDIT,
|
||||||
auth: ['trade:delivery:express:update'],
|
auth: ['trade:delivery:express:update'],
|
||||||
@@ -124,8 +126,8 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: $t('common.delete'),
|
label: $t('common.delete'),
|
||||||
link: true,
|
|
||||||
type: 'danger',
|
type: 'danger',
|
||||||
|
link: true,
|
||||||
icon: ACTION_ICON.DELETE,
|
icon: ACTION_ICON.DELETE,
|
||||||
auth: ['trade:delivery:express:delete'],
|
auth: ['trade:delivery:express:delete'],
|
||||||
popConfirm: {
|
popConfirm: {
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal class="w-2/5" :title="getTitle">
|
<Modal :title="getTitle" class="w-2/5">
|
||||||
<Form class="mx-4" />
|
<Form class="mx-4" />
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,11 +1,109 @@
|
|||||||
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';
|
||||||
|
|
||||||
|
/** 计费方式列标题映射 */
|
||||||
|
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(
|
||||||
|
chargeMode = 1,
|
||||||
|
): VxeTableGridOptions['columns'] {
|
||||||
|
const chargeTitleMap = CHARGE_MODE_TITLE_MAP[chargeMode];
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field: 'areaIds',
|
||||||
|
title: '区域',
|
||||||
|
minWidth: 300,
|
||||||
|
slots: { default: 'areaIds' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'startCount',
|
||||||
|
title: chargeTitleMap?.startCountTitle,
|
||||||
|
width: 120,
|
||||||
|
slots: { default: 'startCount' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'startPrice',
|
||||||
|
title: '运费(元)',
|
||||||
|
width: 120,
|
||||||
|
slots: { default: 'startPrice' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'extraCount',
|
||||||
|
title: chargeTitleMap?.extraCountTitle,
|
||||||
|
width: 120,
|
||||||
|
slots: { default: 'extraCount' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'extraPrice',
|
||||||
|
title: '续费(元)',
|
||||||
|
width: 120,
|
||||||
|
slots: { default: 'extraPrice' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
width: 80,
|
||||||
|
fixed: 'right',
|
||||||
|
slots: { default: 'actions' },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 包邮设置表格列 */
|
||||||
|
export function useFreesColumns(
|
||||||
|
chargeMode = 1,
|
||||||
|
): VxeTableGridOptions['columns'] {
|
||||||
|
const freeTitleMap = FREE_MODE_TITLE_MAP[chargeMode];
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field: 'areaIds',
|
||||||
|
title: '区域',
|
||||||
|
minWidth: 300,
|
||||||
|
slots: { default: 'areaIds' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'freeCount',
|
||||||
|
title: freeTitleMap?.freeCountTitle,
|
||||||
|
width: 120,
|
||||||
|
slots: { default: 'freeCount' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'freePrice',
|
||||||
|
title: '包邮金额(元)',
|
||||||
|
width: 120,
|
||||||
|
slots: { default: 'freePrice' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
width: 80,
|
||||||
|
fixed: 'right',
|
||||||
|
slots: { default: 'actions' },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/** 新增/修改的表单 */
|
/** 新增/修改的表单 */
|
||||||
export function useFormSchema(): VbenFormSchema[] {
|
export function useFormSchema(): VbenFormSchema[] {
|
||||||
return [
|
return [
|
||||||
@@ -21,6 +119,9 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'name',
|
fieldName: 'name',
|
||||||
label: '模板名称',
|
label: '模板名称',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入模板名称',
|
||||||
|
},
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -32,17 +133,32 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
buttonStyle: 'solid',
|
buttonStyle: 'solid',
|
||||||
optionType: 'button',
|
optionType: 'button',
|
||||||
},
|
},
|
||||||
rules: z.number().default(CommonStatusEnum.ENABLE),
|
rules: z.number().default(1),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldName: 'sort',
|
fieldName: 'sort',
|
||||||
label: '显示顺序',
|
label: '显示顺序',
|
||||||
component: 'InputNumber',
|
component: 'InputNumber',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
|
placeholder: '请输入显示顺序',
|
||||||
min: 0,
|
min: 0,
|
||||||
|
controlsPosition: 'right',
|
||||||
|
class: '!w-full',
|
||||||
},
|
},
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'charges',
|
||||||
|
label: '运费设置',
|
||||||
|
component: 'Input',
|
||||||
|
formItemClass: 'col-span-3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'frees',
|
||||||
|
label: '包邮设置',
|
||||||
|
component: 'Input',
|
||||||
|
formItemClass: 'col-span-3',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,12 +169,17 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
fieldName: 'name',
|
fieldName: 'name',
|
||||||
label: '模板名称',
|
label: '模板名称',
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入模板名称',
|
||||||
|
clearable: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldName: 'chargeMode',
|
fieldName: 'chargeMode',
|
||||||
label: '计费方式',
|
label: '计费方式',
|
||||||
component: 'Select',
|
component: 'Select',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
|
placeholder: '请选择计费方式',
|
||||||
clearable: true,
|
clearable: true,
|
||||||
options: getDictOptions(DICT_TYPE.EXPRESS_CHARGE_MODE, 'number'),
|
options: getDictOptions(DICT_TYPE.EXPRESS_CHARGE_MODE, 'number'),
|
||||||
},
|
},
|
||||||
@@ -72,14 +193,17 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||||||
{
|
{
|
||||||
field: 'id',
|
field: 'id',
|
||||||
title: '编号',
|
title: '编号',
|
||||||
|
minWidth: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'name',
|
field: 'name',
|
||||||
title: '模板名称',
|
title: '模板名称',
|
||||||
|
minWidth: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'chargeMode',
|
field: 'chargeMode',
|
||||||
title: '计费方式',
|
title: '计费方式',
|
||||||
|
minWidth: 100,
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellDict',
|
name: 'CellDict',
|
||||||
props: { type: DICT_TYPE.EXPRESS_CHARGE_MODE },
|
props: { type: DICT_TYPE.EXPRESS_CHARGE_MODE },
|
||||||
@@ -88,10 +212,12 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||||||
{
|
{
|
||||||
field: 'sort',
|
field: 'sort',
|
||||||
title: '显示顺序',
|
title: '显示顺序',
|
||||||
|
minWidth: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'createTime',
|
field: 'createTime',
|
||||||
title: '创建时间',
|
title: '创建时间',
|
||||||
|
minWidth: 180,
|
||||||
formatter: 'formatDateTime',
|
formatter: 'formatDateTime',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const [FormModal, formModalApi] = useVbenModal({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/** 刷新表格 */
|
/** 刷新表格 */
|
||||||
function onRefresh() {
|
function handleRefresh() {
|
||||||
gridApi.query();
|
gridApi.query();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ async function handleDelete(
|
|||||||
try {
|
try {
|
||||||
await deleteDeliveryExpressTemplate(row.id as number);
|
await deleteDeliveryExpressTemplate(row.id as number);
|
||||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||||
onRefresh();
|
handleRefresh();
|
||||||
} finally {
|
} finally {
|
||||||
loadingInstance.close();
|
loadingInstance.close();
|
||||||
}
|
}
|
||||||
@@ -73,6 +73,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
},
|
},
|
||||||
rowConfig: {
|
rowConfig: {
|
||||||
keyField: 'id',
|
keyField: 'id',
|
||||||
|
isHover: true,
|
||||||
},
|
},
|
||||||
toolbarConfig: {
|
toolbarConfig: {
|
||||||
refresh: true,
|
refresh: true,
|
||||||
@@ -84,7 +85,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height>
|
<Page auto-content-height>
|
||||||
<FormModal @success="onRefresh" />
|
<FormModal @success="handleRefresh" />
|
||||||
<Grid table-title="快递模板列表">
|
<Grid table-title="快递模板列表">
|
||||||
<template #toolbar-tools>
|
<template #toolbar-tools>
|
||||||
<TableAction
|
<TableAction
|
||||||
@@ -104,6 +105,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
:actions="[
|
:actions="[
|
||||||
{
|
{
|
||||||
label: $t('common.edit'),
|
label: $t('common.edit'),
|
||||||
|
type: 'primary',
|
||||||
link: true,
|
link: true,
|
||||||
icon: ACTION_ICON.EDIT,
|
icon: ACTION_ICON.EDIT,
|
||||||
auth: ['trade:delivery:express-template:update'],
|
auth: ['trade:delivery:express-template:update'],
|
||||||
@@ -111,8 +113,8 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: $t('common.delete'),
|
label: $t('common.delete'),
|
||||||
link: true,
|
|
||||||
type: 'danger',
|
type: 'danger',
|
||||||
|
link: true,
|
||||||
icon: ACTION_ICON.DELETE,
|
icon: ACTION_ICON.DELETE,
|
||||||
auth: ['trade:delivery:express-template:delete'],
|
auth: ['trade:delivery:express-template:delete'],
|
||||||
popConfirm: {
|
popConfirm: {
|
||||||
|
|||||||
@@ -0,0 +1,231 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import type { MallDeliveryExpressTemplateApi } from '#/api/mall/trade/delivery/expressTemplate';
|
||||||
|
import type { SystemAreaApi } from '#/api/system/area';
|
||||||
|
|
||||||
|
import { computed, nextTick, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import { ElInputNumber, ElTreeSelect } from 'element-plus';
|
||||||
|
|
||||||
|
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
|
||||||
|
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 columnTitle = computed(() => CHARGE_MODE_TITLE_MAP[props.chargeMode]);
|
||||||
|
|
||||||
|
/** 表格配置 */
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
gridOptions: {
|
||||||
|
columns: useChargesColumns(props.chargeMode),
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 监听计费方式变化 */
|
||||||
|
watch(
|
||||||
|
() => props.chargeMode,
|
||||||
|
() => {
|
||||||
|
const columns = useChargesColumns(props.chargeMode);
|
||||||
|
if (gridApi.grid && columns) {
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Grid class="w-full">
|
||||||
|
<template #areaIds="{ row }">
|
||||||
|
<!-- TODO 芋艿:可优化,使用 Cascade。不过貌似 ele 在 multiple 貌似有 bug! -->
|
||||||
|
<ElTreeSelect
|
||||||
|
v-model="row.areaIds"
|
||||||
|
:data="areaTree"
|
||||||
|
node-key="id"
|
||||||
|
:props="{
|
||||||
|
label: 'name',
|
||||||
|
value: 'id',
|
||||||
|
children: 'children',
|
||||||
|
}"
|
||||||
|
placeholder="请选择地区"
|
||||||
|
class="w-full"
|
||||||
|
multiple
|
||||||
|
show-checkbox
|
||||||
|
collapse-tags
|
||||||
|
collapse-tags-tooltip
|
||||||
|
:max-collapse-tags="1"
|
||||||
|
@change="handleRowChange(row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #startCount="{ row }">
|
||||||
|
<ElInputNumber
|
||||||
|
v-model="row.startCount"
|
||||||
|
:min="1"
|
||||||
|
@change="handleRowChange(row)"
|
||||||
|
controls-position="right"
|
||||||
|
class="!w-full"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #startPrice="{ row }">
|
||||||
|
<ElInputNumber
|
||||||
|
v-model="row.startPrice"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
@change="handleRowChange(row)"
|
||||||
|
controls-position="right"
|
||||||
|
class="!w-full"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #extraCount="{ row }">
|
||||||
|
<ElInputNumber
|
||||||
|
v-model="row.extraCount"
|
||||||
|
:min="1"
|
||||||
|
@change="handleRowChange(row)"
|
||||||
|
controls-position="right"
|
||||||
|
class="!w-full"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #extraPrice="{ row }">
|
||||||
|
<ElInputNumber
|
||||||
|
v-model="row.extraPrice"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
@change="handleRowChange(row)"
|
||||||
|
controls-position="right"
|
||||||
|
class="!w-full"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ row }">
|
||||||
|
<TableAction
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
type: 'danger',
|
||||||
|
link: 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,9 +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, fenToYuan, yuanToFen } from '@vben/utils';
|
||||||
|
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
@@ -13,12 +15,19 @@ 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';
|
||||||
|
import ChargeItemForm from './charge-item-form.vue';
|
||||||
|
import FreeItemForm from './free-item-form.vue';
|
||||||
|
|
||||||
const emit = defineEmits(['success']);
|
const emit = defineEmits(['success']);
|
||||||
const formData = ref<MallDeliveryExpressTemplateApi.ExpressTemplate>();
|
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(() => {
|
const getTitle = computed(() => {
|
||||||
return formData.value?.id
|
return formData.value?.id
|
||||||
? $t('ui.actionTitle.edit', ['快递模板'])
|
? $t('ui.actionTitle.edit', ['快递模板'])
|
||||||
@@ -30,26 +39,83 @@ const [Form, formApi] = useVbenForm({
|
|||||||
componentProps: {
|
componentProps: {
|
||||||
class: 'w-full',
|
class: 'w-full',
|
||||||
},
|
},
|
||||||
formItemClass: 'col-span-2',
|
|
||||||
labelWidth: 120,
|
labelWidth: 120,
|
||||||
},
|
},
|
||||||
layout: 'horizontal',
|
wrapperClass: 'grid-cols-3',
|
||||||
|
layout: 'vertical',
|
||||||
schema: useFormSchema(),
|
schema: useFormSchema(),
|
||||||
showDefaultActions: false,
|
showDefaultActions: false,
|
||||||
|
handleValuesChange: (values, changedFields) => {
|
||||||
|
// 目的:触发子表单的 columns 变化
|
||||||
|
if (changedFields.includes('chargeMode')) {
|
||||||
|
formData.value!.chargeMode = values.chargeMode;
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO @xingyu:城市处理;
|
/** 更新运费设置 */
|
||||||
|
const handleUpdateCharges = async (
|
||||||
|
charges: MallDeliveryExpressTemplateApi.TemplateCharge[],
|
||||||
|
) => {
|
||||||
|
formData.value =
|
||||||
|
await formApi.getValues<MallDeliveryExpressTemplateApi.ExpressTemplate>();
|
||||||
|
formData.value.charges = charges;
|
||||||
|
await formApi.setValues({
|
||||||
|
charges,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 更新包邮设置 */
|
||||||
|
const handleUpdateFrees = async (
|
||||||
|
frees: MallDeliveryExpressTemplateApi.TemplateFree[],
|
||||||
|
) => {
|
||||||
|
formData.value =
|
||||||
|
await formApi.getValues<MallDeliveryExpressTemplateApi.ExpressTemplate>();
|
||||||
|
formData.value.frees = frees;
|
||||||
|
await formApi.setValues({
|
||||||
|
frees,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 创建或更新快递模板 */
|
||||||
const [Modal, modalApi] = useVbenModal({
|
const [Modal, modalApi] = useVbenModal({
|
||||||
async onConfirm() {
|
async onConfirm() {
|
||||||
const { valid } = await formApi.validate();
|
const { valid } = await formApi.validate();
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const chargeFormInstance = Array.isArray(chargeItemFormRef.value)
|
||||||
|
? chargeItemFormRef.value[0]
|
||||||
|
: chargeItemFormRef.value;
|
||||||
|
const freeFormInstance = Array.isArray(freeItemFormRef.value)
|
||||||
|
? freeItemFormRef.value[0]
|
||||||
|
: freeItemFormRef.value;
|
||||||
|
try {
|
||||||
|
chargeFormInstance.validate();
|
||||||
|
freeFormInstance.validate();
|
||||||
|
} catch (error: any) {
|
||||||
|
ElMessage.error(error.message || '子表单验证失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
// 提交表单
|
// 提交表单
|
||||||
const data =
|
const data = cloneDeep(
|
||||||
(await formApi.getValues()) as MallDeliveryExpressTemplateApi.ExpressTemplate;
|
await formApi.getValues(),
|
||||||
|
) as MallDeliveryExpressTemplateApi.ExpressTemplate;
|
||||||
try {
|
try {
|
||||||
|
// 转换金额单位
|
||||||
|
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
|
await (formData.value?.id
|
||||||
? updateDeliveryExpressTemplate(data)
|
? updateDeliveryExpressTemplate(data)
|
||||||
: createDeliveryExpressTemplate(data));
|
: createDeliveryExpressTemplate(data));
|
||||||
@@ -75,6 +141,18 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
try {
|
try {
|
||||||
formData.value = await getDeliveryExpressTemplate(data.id);
|
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
|
// 设置到 values
|
||||||
await formApi.setValues(formData.value);
|
await formApi.setValues(formData.value);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -82,10 +160,34 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** 初始化 */
|
||||||
|
onMounted(async () => {
|
||||||
|
areaTree.value = await getAreaTree();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal class="w-2/5" :title="getTitle">
|
<Modal class="w-1/2" :title="getTitle">
|
||||||
<Form class="mx-4" />
|
<Form class="mx-3">
|
||||||
|
<template #charges>
|
||||||
|
<ChargeItemForm
|
||||||
|
ref="chargeItemFormRef"
|
||||||
|
: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"
|
||||||
|
:area-tree="areaTree"
|
||||||
|
@update:items="handleUpdateFrees"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,202 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import type { MallDeliveryExpressTemplateApi } from '#/api/mall/trade/delivery/expressTemplate';
|
||||||
|
import type { SystemAreaApi } from '#/api/system/area';
|
||||||
|
|
||||||
|
import { computed, nextTick, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import { ElInputNumber, ElTreeSelect } from 'element-plus';
|
||||||
|
|
||||||
|
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
|
||||||
|
import { FREE_MODE_TITLE_MAP, useFreesColumns } from '../data';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
items?: MallDeliveryExpressTemplateApi.TemplateFree[];
|
||||||
|
chargeMode?: number;
|
||||||
|
areaTree?: SystemAreaApi.Area[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
items: () => [],
|
||||||
|
chargeMode: 1,
|
||||||
|
areaTree: () => [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:items']);
|
||||||
|
|
||||||
|
const tableData = ref<any[]>([]);
|
||||||
|
const columnTitle = computed(() => FREE_MODE_TITLE_MAP[props.chargeMode]);
|
||||||
|
|
||||||
|
/** 表格配置 */
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
gridOptions: {
|
||||||
|
columns: useFreesColumns(props.chargeMode),
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 监听计费方式变化 */
|
||||||
|
watch(
|
||||||
|
() => props.chargeMode,
|
||||||
|
() => {
|
||||||
|
const columns = useFreesColumns(props.chargeMode);
|
||||||
|
if (gridApi.grid && columns) {
|
||||||
|
gridApi.grid.reloadColumn(columns);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 处理新增 */
|
||||||
|
function handleAdd() {
|
||||||
|
const newRow = {
|
||||||
|
areaIds: [],
|
||||||
|
freeCount: undefined,
|
||||||
|
freePrice: 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.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,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Grid class="w-full">
|
||||||
|
<template #areaIds="{ row }">
|
||||||
|
<!-- TODO 芋艿:可优化,使用 Cascade。不过貌似 ele 在 multiple 貌似有 bug! -->
|
||||||
|
<ElTreeSelect
|
||||||
|
v-model="row.areaIds"
|
||||||
|
:data="areaTree"
|
||||||
|
node-key="id"
|
||||||
|
:props="{
|
||||||
|
label: 'name',
|
||||||
|
value: 'id',
|
||||||
|
children: 'children',
|
||||||
|
}"
|
||||||
|
placeholder="请选择地区"
|
||||||
|
class="w-full"
|
||||||
|
multiple
|
||||||
|
show-checkbox
|
||||||
|
collapse-tags
|
||||||
|
collapse-tags-tooltip
|
||||||
|
:max-collapse-tags="1"
|
||||||
|
@change="handleRowChange(row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #freeCount="{ row }">
|
||||||
|
<ElInputNumber
|
||||||
|
v-model="row.freeCount"
|
||||||
|
:min="1"
|
||||||
|
@change="handleRowChange(row)"
|
||||||
|
controls-position="right"
|
||||||
|
class="!w-full"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #freePrice="{ row }">
|
||||||
|
<ElInputNumber
|
||||||
|
v-model="row.freePrice"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
@change="handleRowChange(row)"
|
||||||
|
controls-position="right"
|
||||||
|
class="!w-full"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ row }">
|
||||||
|
<TableAction
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
type: 'danger',
|
||||||
|
link: 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>
|
||||||
@@ -23,7 +23,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
{
|
{
|
||||||
component: 'ImageUpload',
|
component: 'ImageUpload',
|
||||||
fieldName: 'logo',
|
fieldName: 'logo',
|
||||||
label: '门店logo',
|
label: '门店 logo',
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -201,7 +201,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'logo',
|
field: 'logo',
|
||||||
title: '门店logo',
|
title: '门店 logo',
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellImage',
|
name: 'CellImage',
|
||||||
},
|
},
|
||||||
|
|||||||
3810
pnpm-lock.yaml
generated
3810
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -197,7 +197,7 @@ catalog:
|
|||||||
unbuild: ^3.6.1
|
unbuild: ^3.6.1
|
||||||
unplugin-element-plus: ^0.10.0
|
unplugin-element-plus: ^0.10.0
|
||||||
vee-validate: ^4.15.1
|
vee-validate: ^4.15.1
|
||||||
vite: ^5.4.10
|
vite: ^7.1.2
|
||||||
vite-plugin-compression: ^0.5.1
|
vite-plugin-compression: ^0.5.1
|
||||||
vite-plugin-dts: ^4.5.4
|
vite-plugin-dts: ^4.5.4
|
||||||
vite-plugin-html: ^3.2.2
|
vite-plugin-html: ^3.2.2
|
||||||
|
|||||||
Reference in New Issue
Block a user