Merge branch 'dev_pro2.0' of http://114.132.189.42:9002/r/mom-pro2-before into dev_pro2.0
| | |
| | | statusTypeName?: string; |
| | | statusId: number; |
| | | statusName?: string; |
| | | endStatus: number; |
| | | endRemark: string; |
| | | dealTime: Date; |
| | | totalProductPrice: number; |
| | |
| | | /** 商机更新状态请求 */ |
| | | export interface BusinessUpdateStatusReqVO { |
| | | id: number; |
| | | statusId: number | undefined; |
| | | endStatus: number | undefined; |
| | | statusId: number; |
| | | } |
| | | } |
| | | |
| | |
| | | id?: number; |
| | | name: string; |
| | | percent?: number; |
| | | endStatus?: number; |
| | | key?: string; |
| | | sort?: number; |
| | | } |
| | | } |
| | | |
| | | /** 默认商机状态 */ |
| | | export const DEFAULT_STATUSES = [ |
| | | { |
| | | endStatus: 1, |
| | | key: '结束', |
| | | name: '赢单', |
| | | percent: 100, |
| | | }, |
| | | { |
| | | endStatus: 2, |
| | | key: '结束', |
| | | name: '输单', |
| | | percent: 0, |
| | | }, |
| | | { |
| | | endStatus: 3, |
| | | key: '结束', |
| | | name: '无效', |
| | | percent: 0, |
| | | }, |
| | | ] satisfies CrmBusinessStatusApi.BusinessStatusType[]; |
| | | |
| | | /** 查询商机状态组列表 */ |
| | | export function getBusinessStatusPage(params: PageParam) { |
| | |
| | | contractId?: number; // 关联 CRM 合同编号 |
| | | contractNo?: string; // CRM 合同单号 |
| | | needProduction?: number; // 是否需要生产:0-不需要,1-需要 |
| | | productionStatus?: number; // 生产状态:0-未完成,1-已完成 |
| | | productionFinishTime?: Date; // 生产完成时间 |
| | | hasSalesNotice?: boolean; // 是否已生成发货通知单 |
| | | canCreateSalesNotice?: boolean; // 是否可以生成发货通知单 |
| | | items?: SaleOrderItem[]; // 销售订单产品明细列表 |
| | |
| | | title: '商机阶段', |
| | | fixed: 'right', |
| | | width: 120, |
| | | slots: { default: 'statusName' }, |
| | | }, |
| | | { |
| | | title: '操作', |
| | |
| | | import { erpPriceInputFormatter, formatDateTime } from '@vben/utils'; |
| | | |
| | | import { |
| | | DEFAULT_STATUSES, |
| | | getBusinessStatusSimpleList, |
| | | } from '#/api/crm/business/status'; |
| | | |
| | |
| | | }, |
| | | { |
| | | fieldName: 'statusId', |
| | | label: '商机状态', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'endStatus', |
| | | label: '商机状态', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: '商机阶段', |
| | | component: 'Select', |
| | | dependencies: { |
| | |
| | | const statusList = await getBusinessStatusSimpleList( |
| | | formData.value?.statusTypeId ?? 0, |
| | | ); |
| | | const statusOptions = statusList.map((item) => ({ |
| | | const options = statusList.map((item) => ({ |
| | | label: `${item.name}(赢单率:${item.percent}%)`, |
| | | value: item.id, |
| | | })); |
| | | const options = DEFAULT_STATUSES.map((item) => ({ |
| | | label: `${item.name}(赢单率:${item.percent}%)`, |
| | | value: item.endStatus, |
| | | })); |
| | | statusOptions.push(...options); |
| | | return { |
| | | options: statusOptions, |
| | | options, |
| | | }; |
| | | }, |
| | | }, |
| | |
| | | <ContractDetailsList |
| | | :biz-id="businessId" |
| | | :biz-type="BizTypeEnum.CRM_BUSINESS" |
| | | :customer-id="business.customerId" |
| | | :customer-name="business.customerName" |
| | | :business-name="business.name" |
| | | /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane tab="操作日志" key="6" :force-render="true"> |
| | |
| | | // 提交表单 |
| | | const data = (await formApi.getValues()) as CrmBusinessApi.Business; |
| | | try { |
| | | if (!data.status) { |
| | | if (!data.statusId) { |
| | | return; |
| | | } |
| | | await updateBusinessStatus({ |
| | | id: data.id, |
| | | statusId: data.status > 0 ? data.status : undefined, |
| | | endStatus: data.status < 0 ? -data.status : undefined, |
| | | statusId: data.statusId, |
| | | }); |
| | | // 关闭并提示 |
| | | await modalApi.close(); |
| | |
| | | if (!data || !data.id) { |
| | | return; |
| | | } |
| | | data.status = data.endStatus === null ? data.statusId : -data.endStatus; |
| | | formData.value = data; |
| | | modalApi.lock(); |
| | | try { |
| | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { Button, message, Tabs } from 'ant-design-vue'; |
| | | import { Button, message, Tabs, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | |
| | | |
| | | const { push } = useRouter(); |
| | | const sceneType = ref('1'); |
| | | |
| | | const STATUS_COLORS: Record<string, string> = { |
| | | 拜访: 'default', |
| | | 初步接触: 'processing', |
| | | 需求确认: 'cyan', |
| | | 方案报价: 'orange', |
| | | 商务谈判: 'volcano', |
| | | 合同签订: 'success', |
| | | 赢单: 'success', |
| | | 输单: 'error', |
| | | 无效: 'default', |
| | | }; |
| | | |
| | | function getStatusColor(name: string): string { |
| | | return STATUS_COLORS[name] || 'blue'; |
| | | } |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | |
| | | {{ row.customerName }} |
| | | </Button> |
| | | </template> |
| | | <template #statusName="{ row }"> |
| | | <Tag v-if="row.statusName" :color="getStatusColor(row.statusName)">{{ row.statusName }}</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-1/2"> |
| | | <Modal :title="getTitle" class="w-3/4"> |
| | | <Form class="mx-4"> |
| | | <template #product="slotProps"> |
| | | <ProductEditTable |
| | |
| | | export function useFormColumns(): VxeTableGridOptions['columns'] { |
| | | return [ |
| | | { |
| | | field: 'endStatus', |
| | | title: '阶段', |
| | | minWidth: 100, |
| | | slots: { default: 'endStatus' }, |
| | | type: 'seq', |
| | | title: '序号', |
| | | width: 60, |
| | | }, |
| | | { |
| | | field: 'name', |
| | |
| | | import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | createBusinessStatus, |
| | | DEFAULT_STATUSES, |
| | | getBusinessStatus, |
| | | updateBusinessStatus, |
| | | } from '#/api/crm/business/status'; |
| | |
| | | try { |
| | | if (formData.value?.statuses && formData.value.statuses.length > 0) { |
| | | data.statuses = formData.value.statuses; |
| | | data.statuses.splice(-3, 3); |
| | | } |
| | | await (formData.value?.id |
| | | ? updateBusinessStatus(data) |
| | |
| | | // 设置到 values |
| | | await formApi.setValues(formData.value as any); |
| | | await gridApi.grid.reloadData( |
| | | (formData.value!.statuses = |
| | | formData.value?.statuses?.concat(DEFAULT_STATUSES)) as any, |
| | | (formData.value!.statuses) as any, |
| | | ); |
| | | } finally { |
| | | modalApi.unlock(); |
| | |
| | | |
| | | /** 添加状态 */ |
| | | async function handleAddStatus() { |
| | | formData.value!.statuses!.splice(-3, 0, { |
| | | formData.value!.statuses!.push({ |
| | | name: '', |
| | | percent: undefined, |
| | | }); |
| | |
| | | mode: 'cell', |
| | | }, |
| | | columns: useFormColumns(), |
| | | data: formData.value?.statuses?.concat(DEFAULT_STATUSES), |
| | | data: formData.value?.statuses, |
| | | border: true, |
| | | showOverflow: true, |
| | | autoResize: true, |
| | |
| | | <Form class="mx-4"> |
| | | <template #statuses> |
| | | <Grid class="w-full"> |
| | | <template #endStatus="{ row, rowIndex }"> |
| | | <span> |
| | | {{ row.endStatus ? '结束' : `阶段${rowIndex + 1}` }} |
| | | </span> |
| | | </template> |
| | | <template #name="{ row }"> |
| | | <Input |
| | | v-if="!row.endStatus" |
| | | v-model:value="row.name" |
| | | placeholder="请输入状态名" |
| | | /> |
| | | <span v-else>{{ row.name }}</span> |
| | | </template> |
| | | <template #percent="{ row }"> |
| | | <InputNumber |
| | | v-if="!row.endStatus" |
| | | v-model:value="row.percent" |
| | | :min="0" |
| | | :max="100" |
| | | :precision="2" |
| | | placeholder="请输入赢单率" |
| | | /> |
| | | <span v-else>{{ row.percent }}</span> |
| | | </template> |
| | | <template #actions="{ row, rowIndex }"> |
| | | <TableAction |
| | |
| | | { |
| | | label: $t('ui.actionTitle.create'), |
| | | type: 'link', |
| | | ifShow: () => !row.endStatus, |
| | | onClick: handleAddStatus, |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | ifShow: () => !row.endStatus, |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.name]), |
| | | confirm: deleteStatusArea.bind(null, row, rowIndex), |
| | |
| | | const props = defineProps<{ |
| | | bizId: number; // 业务编号 |
| | | bizType: number; // 业务类型 |
| | | customerId?: number; // 客户编号(商机/联系人详情传入) |
| | | customerName?: string; // 客户名称(商机/联系人详情传入) |
| | | businessName?: string; // 商机名称(商机详情传入) |
| | | }>(); |
| | | |
| | | const { push } = useRouter(); |
| | |
| | | ? { |
| | | customerId: props.bizId, |
| | | } |
| | | : { businessId: props.bizId }, |
| | | : { |
| | | businessId: props.bizId, |
| | | customerId: props.customerId, |
| | | customerName: props.customerName, |
| | | businessName: props.businessName, |
| | | }, |
| | | ) |
| | | .open(); |
| | | } |
| | |
| | | customerId: props.bizId, |
| | | ...formValues, |
| | | }); |
| | | } else if (props.bizType === BizTypeEnum.CRM_CONTACT) { |
| | | } else if (props.bizType === BizTypeEnum.CRM_BUSINESS) { |
| | | return await getContractPageByBusiness({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | |
| | | componentProps: { |
| | | placeholder: '请选择客户', |
| | | }, |
| | | dependencies: { |
| | | triggerFields: ['customerId'], |
| | | disabled: (values) => !!values.customerId, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'orderDate', |
| | |
| | | placeholder: '请选择商机', |
| | | }, |
| | | dependencies: { |
| | | triggerFields: ['customerId'], |
| | | disabled: (values) => !values.customerId, |
| | | triggerFields: ['customerId', 'businessId'], |
| | | disabled: (values) => !!values.businessId || !values.customerId, |
| | | async componentProps(values) { |
| | | if (values.businessId && values.businessName) { |
| | | return { |
| | | options: [{ label: values.businessName, value: values.businessId }], |
| | | placeholder: '请选择商机', |
| | | }; |
| | | } |
| | | if (!values.customerId) { |
| | | return { |
| | | options: [], |
| | |
| | | // 加载数据 |
| | | const data = modalApi.getData<CrmContractApi.Contract>(); |
| | | if (!data || !data.id) { |
| | | // 新增时,回显从商机/客户详情传入的数据 |
| | | if (data) { |
| | | await formApi.setValues(data); |
| | | } |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'itemBarCode', |
| | | title: '条码', |
| | | minWidth: 150, |
| | | }, |
| | | { |
| | | field: 'itemUnitName', |
| | | title: '单位', |
| | | minWidth: 80, |
| | |
| | | import type { CrmContractApi } from '#/api/crm/contract'; |
| | | import type { MdmItemApi } from '#/api/mdm/item'; |
| | | |
| | | import { nextTick, onMounted, ref, watch } from 'vue'; |
| | | import { nextTick, ref, watch } from 'vue'; |
| | | |
| | | import { erpPriceMultiply } from '@vben/utils'; |
| | | |
| | | import { InputNumber, Select } from 'ant-design-vue'; |
| | | import { InputNumber } from 'ant-design-vue'; |
| | | |
| | | import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { BizTypeEnum } from '#/api/crm/permission'; |
| | | import { getItemPage } from '#/api/mdm/item'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useProductEditTableColumns } from './data'; |
| | |
| | | } |
| | | |
| | | /** 切换产品时同步基础信息 */ |
| | | function handleProductChange(itemId: any, row: any) { |
| | | const product = productOptions.value.find((p) => p.id === itemId); |
| | | function handleProductChange(product: MdmItemApi.Item | undefined, row: any) { |
| | | if (!product) { |
| | | return; |
| | | } |
| | | row.itemId = itemId; |
| | | row.itemId = product.id; |
| | | row.itemName = product.name; |
| | | row.itemCode = product.code; |
| | | row.itemBarCode = product.barCode; |
| | |
| | | immediate: true, |
| | | }, |
| | | ); |
| | | |
| | | /** 初始化 */ |
| | | const productOptions = ref<MdmItemApi.Item[]>([]); // 物料下拉选项 |
| | | onMounted(async () => { |
| | | const res = await getItemPage({ pageNo: 1, pageSize: 100, status: 0 }); |
| | | productOptions.value = res.list || []; |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Grid class="w-full"> |
| | | <template #itemId="{ row }"> |
| | | <Select |
| | | v-model:value="row.itemId" |
| | | :options="productOptions" |
| | | :field-names="{ label: 'name', value: 'id' }" |
| | | class="w-full" |
| | | <MdmItemSelect |
| | | :model-value="row.itemId" |
| | | placeholder="请选择产品" |
| | | @update:model-value="row.itemId = $event" |
| | | @change="handleProductChange($event, row)" |
| | | /> |
| | | </template> |
| | |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'productionStatus', |
| | | label: '生产状态', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: ['needProduction', 'productionStatus'], |
| | | show: (values) => { |
| | | return formType === 'detail' && values.needProduction === 1; |
| | | }, |
| | | }, |
| | | componentProps: { |
| | | disabled: true, |
| | | }, |
| | | slots: { default: 'productionStatus' }, |
| | | }, |
| | | { |
| | | fieldName: 'productionFinishTime', |
| | | label: '生产完成时间', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: ['productionStatus'], |
| | | show: (values) => { |
| | | return formType === 'detail' && values.productionStatus === 1; |
| | | }, |
| | | }, |
| | | componentProps: { |
| | | disabled: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'orderTime', |
| | | label: '订单时间', |
| | | component: 'DatePicker', |
| | |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'productionStatus', |
| | | label: '生产状态', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: [ |
| | | { label: '未完成', value: 0 }, |
| | | { label: '已完成', value: 1 }, |
| | | ], |
| | | placeholder: '请选择生产状态', |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'returnStatus', |
| | | label: '退货状态', |
| | | component: 'Select', |
| | |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'outStatus', |
| | | title: '出库状态', |
| | | minWidth: 100, |
| | | slots: { default: 'outStatus' }, |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | | title: '金额合计', |
| | | formatter: 'formatAmount2', |
| | |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'outStatus', |
| | | title: '出库状态', |
| | | minWidth: 100, |
| | | fixed: 'right', |
| | | slots: { default: 'outStatus' }, |
| | | }, |
| | | { |
| | | field: 'productionStatus', |
| | | title: '生产状态', |
| | | minWidth: 100, |
| | | fixed: 'right', |
| | | slots: { default: 'productionStatus' }, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: '状态', |
| | | minWidth: 100, |
| | |
| | | <Tag v-else-if="row.outStatus === 2" color="warning">部分出库</Tag> |
| | | <Tag v-else-if="row.outStatus === 3" color="success">全部出库</Tag> |
| | | </template> |
| | | <template #productionStatus="{ row }"> |
| | | <template v-if="row.needProduction !== 1"> |
| | | <Tag color="default">无需生产</Tag> |
| | | </template> |
| | | <template v-else> |
| | | <Tag v-if="row.productionStatus === 0" color="warning">未完成</Tag> |
| | | <Tag v-else-if="row.productionStatus === 1" color="success">已完成</Tag> |
| | | </template> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { getAccountSimpleList } from '#/api/erp/finance/account'; |
| | |
| | | :show-confirm-button="formType !== 'detail'" |
| | | > |
| | | <Form class="mx-3"> |
| | | <template #productionStatus="{ modelValue }"> |
| | | <Tag v-if="modelValue === 0" color="warning">未完成</Tag> |
| | | <Tag v-else-if="modelValue === 1" color="success">已完成</Tag> |
| | | </template> |
| | | <template #items> |
| | | <ItemForm |
| | | ref="itemFormRef" |