feat:【antd】【crm】产品的 components 优化
This commit is contained in:
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!-- 联系人列表:用于【联系人】【商机】详情种,展示它们关联的联系人列表 -->
|
||||
<!-- 联系人列表:用于【联系人】【商机】详情中,展示它们关联的联系人列表 -->
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { CrmContactApi } from '#/api/crm/contact';
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
111
apps/web-antd/src/views/crm/product/components/data.ts
Normal file
111
apps/web-antd/src/views/crm/product/components/data.ts
Normal 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' },
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -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>,
|
||||
});
|
||||
@@ -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>
|
||||
9
apps/web-antd/src/views/crm/product/components/index.ts
Normal file
9
apps/web-antd/src/views/crm/product/components/index.ts
Normal 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'),
|
||||
);
|
||||
@@ -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' },
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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'),
|
||||
);
|
||||
Reference in New Issue
Block a user