feat:【antd】【crm】产品的 components 优化

This commit is contained in:
YunaiV
2025-09-28 20:00:06 +08:00
parent ee3af0293b
commit 3d2a53a6b2
13 changed files with 171 additions and 132 deletions

View File

@@ -24,7 +24,7 @@ import { ContactDetailsList } from '#/views/crm/contact/components';
import { ContractDetailsList } from '#/views/crm/contract';
import { FollowUp } from '#/views/crm/followup';
import { PermissionList, TransferForm } from '#/views/crm/permission';
import { ProductDetailsList } from '#/views/crm/product';
import { ProductDetailsList } from '#/views/crm/product/components';
import { useDetailSchema } from './detail-data';

View File

@@ -16,7 +16,7 @@ import {
} from '#/api/crm/business';
import { BizTypeEnum } from '#/api/crm/permission';
import { $t } from '#/locales';
import { ProductEditTable } from '#/views/crm/product';
import { ProductEditTable } from '#/views/crm/product/components';
import { useFormSchema } from '../data';

View File

@@ -1,4 +1,4 @@
<!-- 联系人列表用于联系人商机详情展示它们关联的联系人列表 -->
<!-- 联系人列表用于联系人商机详情展示它们关联的联系人列表 -->
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { CrmContactApi } from '#/api/crm/contact';

View File

@@ -19,7 +19,7 @@ import { AsyncOperateLog } from '#/components/operate-log';
import { ContractDetailsInfo, ContractForm } from '#/views/crm/contract';
import { FollowUp } from '#/views/crm/followup';
import { PermissionList, TransferForm } from '#/views/crm/permission';
import { ProductDetailsList } from '#/views/crm/product';
import { ProductDetailsList } from '#/views/crm/product/components';
import {
ReceivableDetailsList,
ReceivablePlanDetailsList,

View File

@@ -15,7 +15,7 @@ import {
} from '#/api/crm/contract';
import { BizTypeEnum } from '#/api/crm/permission';
import { $t } from '#/locales';
import { ProductEditTable } from '#/views/crm/product';
import { ProductEditTable } from '#/views/crm/product/components';
import { useFormSchema } from '../data';

View File

@@ -0,0 +1,111 @@
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { DICT_TYPE } from '@vben/constants';
/** 产品详情列表的列定义 */
export function useDetailListColumns(
showBusinessPrice: boolean,
): VxeTableGridOptions['columns'] {
return [
{
field: 'productName',
title: '产品名称',
},
{
field: 'productNo',
title: '产品条码',
},
{
field: 'productUnit',
title: '产品单位',
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.CRM_PRODUCT_UNIT },
},
},
{
field: 'productPrice',
title: '产品价格(元)',
formatter: 'formatAmount2',
},
{
field: 'businessPrice',
title: '商机价格(元)',
formatter: 'formatAmount2',
visible: showBusinessPrice,
},
{
field: 'contractPrice',
title: '合同价格(元)',
formatter: 'formatAmount2',
visible: !showBusinessPrice,
},
{
field: 'count',
title: '数量',
formatter: 'formatNumber',
},
{
field: 'totalPrice',
title: '合计金额(元)',
formatter: 'formatAmount2',
},
];
}
/** 产品编辑表格的列定义 */
export function useProductEditTableColumns(): VxeTableGridOptions['columns'] {
return [
{ type: 'seq', title: '序号', minWidth: 50 },
{
field: 'productId',
title: '产品名称',
minWidth: 100,
slots: { default: 'productId' },
},
{
field: 'productNo',
title: '条码',
minWidth: 150,
},
{
field: 'productUnit',
title: '单位',
minWidth: 100,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.CRM_PRODUCT_UNIT },
},
},
{
field: 'productPrice',
title: '价格(元)',
minWidth: 100,
formatter: 'formatAmount2',
},
{
field: 'sellingPrice',
title: '售价(元)',
minWidth: 100,
slots: { default: 'sellingPrice' },
},
{
field: 'count',
title: '数量',
minWidth: 100,
slots: { default: 'count' },
},
{
field: 'totalPrice',
title: '合计',
minWidth: 100,
formatter: 'formatAmount2',
},
{
title: '操作',
width: 80,
fixed: 'right',
slots: { default: 'actions' },
},
];
}

View File

@@ -1,3 +1,4 @@
<!-- 产品列表用于商机合同详情中展示它们关联的产品列表 -->
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { CrmProductApi } from '#/api/crm/product';
@@ -11,16 +12,20 @@ import { getBusiness } from '#/api/crm/business';
import { getContract } from '#/api/crm/contract';
import { BizTypeEnum } from '#/api/crm/permission';
import { useDetailListColumns } from '../detail/data';
import { useDetailListColumns } from './data';
/** 组件入参 */
const props = defineProps<{
bizId: number;
bizType: BizTypeEnum;
}>();
/** 整单折扣 */
const discountPercent = ref(0);
/** 产品总金额 */
const totalProductPrice = ref(0);
/** 构建产品列表表格 */
const [Grid] = useVbenVxeGrid({
gridOptions: {
columns: useDetailListColumns(props.bizType === BizTypeEnum.CRM_BUSINESS),
@@ -48,6 +53,7 @@ const [Grid] = useVbenVxeGrid({
keepSource: true,
rowConfig: {
keyField: 'id',
isHover: true,
},
} as VxeTableGridOptions<CrmProductApi.Product>,
});

View File

@@ -12,8 +12,9 @@ import { InputNumber, Select } from 'ant-design-vue';
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { BizTypeEnum } from '#/api/crm/permission';
import { getProductSimpleList } from '#/api/crm/product';
import { $t } from '#/locales';
import { useProductEditTableColumns } from '../data';
import { useProductEditTableColumns } from './data';
const props = defineProps<{
bizType: BizTypeEnum;
@@ -24,16 +25,20 @@ const props = defineProps<{
const emit = defineEmits(['update:products']);
/** 表格内部数据 */
const tableData = ref<any[]>([]);
/** 添加产品行 */
function handleAdd() {
gridApi.grid.insertAt(null, -1);
}
/** 删除产品行 */
function handleDelete(row: CrmProductApi.Product) {
gridApi.grid.remove(row);
}
/** 切换产品时同步基础信息 */
function handleProductChange(productId: any, row: any) {
const product = productOptions.value.find((p) => p.id === productId);
if (!product) {
@@ -48,11 +53,13 @@ function handleProductChange(productId: any, row: any) {
handleUpdateValue(row);
}
/** 金额变动时重新计算合计 */
function handlePriceChange(row: any) {
row.totalPrice = erpPriceMultiply(row.sellingPrice, row.count) ?? 0;
handleUpdateValue(row);
}
/** 将最新数据写回并通知父组件 */
function handleUpdateValue(row: any) {
const index = tableData.value.findIndex((item) => item.id === row.id);
if (props.bizType === BizTypeEnum.CRM_BUSINESS) {
@@ -69,7 +76,6 @@ function handleUpdateValue(row: any) {
emit('update:products', [...tableData.value]);
}
/** 表格配置 */
const [Grid, gridApi] = useVbenVxeGrid({
gridOptions: {
editConfig: {
@@ -84,6 +90,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
keepSource: true,
rowConfig: {
keyField: 'id',
isHover: true,
},
pagerConfig: {
enabled: false,
@@ -119,8 +126,10 @@ watch(
},
);
/** 初始化 */
/** 产品下拉选项 */
const productOptions = ref<CrmProductApi.Product[]>([]);
/** 初始化 */
onMounted(async () => {
productOptions.value = await getProductSimpleList();
});
@@ -133,7 +142,7 @@ onMounted(async () => {
v-model:value="row.productId"
:options="productOptions"
:field-names="{ label: 'name', value: 'id' }"
style="width: 100%"
class="w-full"
@change="handleProductChange($event, row)"
/>
</template>

View File

@@ -0,0 +1,9 @@
import { defineAsyncComponent } from 'vue';
export const ProductDetailsList = defineAsyncComponent(
() => import('./detail-list.vue'),
);
export const ProductEditTable = defineAsyncComponent(
() => import('./edit-table.vue'),
);

View File

@@ -27,6 +27,10 @@ export function useFormSchema(): VbenFormSchema[] {
fieldName: 'name',
label: '产品名称',
rules: 'required',
componentProps: {
placeholder: '请输入产品名称',
allowClear: true,
},
},
{
component: 'ApiSelect',
@@ -39,6 +43,8 @@ export function useFormSchema(): VbenFormSchema[] {
label: 'nickname',
value: 'id',
},
placeholder: '请选择负责人',
allowClear: true,
},
defaultValue: userStore.userInfo?.id,
},
@@ -47,6 +53,10 @@ export function useFormSchema(): VbenFormSchema[] {
fieldName: 'no',
label: '产品编码',
rules: 'required',
componentProps: {
placeholder: '请输入产品编码',
allowClear: true,
},
},
{
component: 'ApiTreeSelect',
@@ -59,6 +69,8 @@ export function useFormSchema(): VbenFormSchema[] {
return handleTree(data);
},
fieldNames: { label: 'name', value: 'id', children: 'children' },
placeholder: '请选择产品类型',
allowClear: true,
},
},
{
@@ -67,6 +79,8 @@ export function useFormSchema(): VbenFormSchema[] {
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.CRM_PRODUCT_UNIT, 'number'),
placeholder: '请选择产品单位',
allowClear: true,
},
rules: 'required',
},
@@ -79,12 +93,17 @@ export function useFormSchema(): VbenFormSchema[] {
min: 0,
precision: 2,
step: 0.1,
placeholder: '请输入产品价格',
},
},
{
component: 'Textarea',
fieldName: 'description',
label: '产品描述',
componentProps: {
placeholder: '请输入产品描述',
allowClear: true,
},
},
{
fieldName: 'status',
@@ -107,6 +126,10 @@ export function useGridFormSchema(): VbenFormSchema[] {
fieldName: 'name',
label: '产品名称',
component: 'Input',
componentProps: {
placeholder: '请输入产品名称',
allowClear: true,
},
},
{
fieldName: 'status',
@@ -114,6 +137,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
component: 'Select',
componentProps: {
allowClear: true,
placeholder: '请选择上架状态',
options: getDictOptions(DICT_TYPE.CRM_PRODUCT_STATUS, 'number'),
},
},
@@ -204,59 +228,3 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
];
}
/** 代码生成表格列定义 */
export function useProductEditTableColumns(): VxeTableGridOptions['columns'] {
return [
{ type: 'seq', title: '序号', minWidth: 50 },
{
field: 'productId',
title: '产品名称',
minWidth: 100,
slots: { default: 'productId' },
},
{
field: 'productNo',
title: '条码',
minWidth: 150,
},
{
field: 'productUnit',
title: '单位',
minWidth: 100,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.CRM_PRODUCT_UNIT },
},
},
{
field: 'productPrice',
title: '价格(元)',
minWidth: 100,
formatter: 'formatAmount2',
},
{
field: 'sellingPrice',
title: '售价(元)',
minWidth: 100,
slots: { default: 'sellingPrice' },
},
{
field: 'count',
title: '数量',
minWidth: 100,
slots: { default: 'count' },
},
{
field: 'totalPrice',
title: '合计',
minWidth: 100,
formatter: 'formatAmount2',
},
{
title: '操作',
width: 80,
fixed: 'right',
slots: { default: 'actions' },
},
];
}

View File

@@ -1,4 +1,3 @@
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { DescriptionItemSchema } from '#/components/description';
import { h } from 'vue';
@@ -72,53 +71,3 @@ export function useDetailBaseSchema(): DescriptionItemSchema[] {
];
}
/** 详情列表的字段 */
export function useDetailListColumns(
showBussinePrice: boolean,
): VxeTableGridOptions['columns'] {
return [
{
field: 'productName',
title: '产品名称',
},
{
field: 'productNo',
title: '产品条码',
},
{
field: 'productUnit',
title: '产品单位',
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.CRM_PRODUCT_UNIT },
},
},
{
field: 'productPrice',
title: '产品价格(元)',
formatter: 'formatAmount2',
},
{
field: 'businessPrice',
title: '商机价格(元)',
formatter: 'formatAmount2',
visible: showBussinePrice,
},
{
field: 'contractPrice',
title: '合同价格(元)',
formatter: 'formatAmount2',
visible: !showBussinePrice,
},
{
field: 'count',
title: '数量',
formatter: 'formatNumber',
},
{
field: 'totalPrice',
title: '合计金额(元)',
formatter: 'formatAmount2',
},
];
}

View File

@@ -15,9 +15,9 @@ import { BizTypeEnum } from '#/api/crm/permission';
import { getProduct } from '#/api/crm/product';
import { useDescription } from '#/components/description';
import { AsyncOperateLog } from '#/components/operate-log';
import { ProductDetailsInfo } from '#/views/crm/product';
import { useDetailSchema } from './data';
import Info from './modules/info.vue';
const route = useRoute();
const router = useRouter();
@@ -80,7 +80,7 @@ onMounted(() => {
<Card class="mt-4 min-h-[60%]">
<Tabs>
<Tabs.TabPane tab="详细资料" key="1" :force-render="true">
<ProductDetailsInfo :product="product" />
<Info :product="product" />
</Tabs.TabPane>
<Tabs.TabPane tab="操作日志" key="2" :force-render="true">
<AsyncOperateLog :log-list="logList" />

View File

@@ -1,13 +0,0 @@
import { defineAsyncComponent } from 'vue';
export const ProductDetailsInfo = defineAsyncComponent(
() => import('./detail/modules/info.vue'),
);
export const ProductDetailsList = defineAsyncComponent(
() => import('./modules/detail-list.vue'),
);
export const ProductEditTable = defineAsyncComponent(
() => import('./modules/product-table.vue'),
);