Merge remote-tracking branch 'origin/dev_pro2.0' into dev_pro2.0
| | |
| | | id?: number; |
| | | code: string; |
| | | name: string; |
| | | type: number; |
| | | type: string; |
| | | status: number; |
| | | contactName?: string; |
| | | contactMobile?: string; |
| | |
| | | labels: { 0: '开启', 1: '关闭' }, |
| | | }; |
| | | |
| | | /** 供应商类型 — 对齐 SupplierTypeEnum (1=生产型 2=贸易型 3=服务型 4=其他) */ |
| | | /** 供应商类型 — 对齐 SupplierTypeEnum (1=生产型 2=贸易型 3=服务型 4=其他) |
| | | * 注意:后端 DO/RespVO 中 type 为 String 类型,此处选项值统一用字符串与后端对齐 */ |
| | | export const SUPPLIER_TYPE = { |
| | | PRODUCTION: 1, |
| | | TRADING: 2, |
| | | SERVICE: 3, |
| | | OTHER: 4, |
| | | PRODUCTION: '1', |
| | | TRADING: '2', |
| | | SERVICE: '3', |
| | | OTHER: '4', |
| | | } as const; |
| | | |
| | | export const SUPPLIER_TYPE_OPTIONS: EnumOption[] = [ |
| | | { label: '生产型', value: 1 }, |
| | | { label: '贸易型', value: 2 }, |
| | | { label: '服务型', value: 3 }, |
| | | { label: '其他', value: 4 }, |
| | | { label: '生产型', value: '1' }, |
| | | { label: '贸易型', value: '2' }, |
| | | { label: '服务型', value: '3' }, |
| | | { label: '其他', value: '4' }, |
| | | ]; |
| | | |
| | | export const SUPPLIER_TYPE_CELLTAG: { data: CellTagItem[]; labels: Record<number, string> } = { |
| | | export const SUPPLIER_TYPE_CELLTAG: { data: CellTagItem[]; labels: Record<string, string> } = { |
| | | data: [ |
| | | { value: 1, color: 'blue' }, |
| | | { value: 2, color: 'green' }, |
| | | { value: 3, color: 'orange' }, |
| | | { value: 4, color: 'default' }, |
| | | { value: '1', color: 'blue' }, |
| | | { value: '2', color: 'green' }, |
| | | { value: '3', color: 'orange' }, |
| | | { value: '4', color: 'default' }, |
| | | ], |
| | | labels: { 1: '生产型', 2: '贸易型', 3: '服务型', 4: '其他' }, |
| | | labels: { '1': '生产型', '2': '贸易型', '3': '服务型', '4': '其他' }, |
| | | }; |
| | | |
| | | // ========== 准入申请 — 对齐 SupplierApplyStatusEnum (0/10/20/30/40/50) ========== |
| | |
| | | const open = ref(false); |
| | | const multiple = ref(true); |
| | | const approvalFilter = ref(true); |
| | | const excludeApproved = ref(false); |
| | | const selectedRows = ref<SrmSupplierApi.SupplierVO[]>([]); |
| | | const preSelectedIds = ref<number[]>([]); |
| | | |
| | |
| | | if (approvalFilter.value) { |
| | | params.approved = true; |
| | | } |
| | | if (excludeApproved.value) { |
| | | params.excludeApproved = true; |
| | | } |
| | | return await getSupplierPage(params); |
| | | }, |
| | | }, |
| | |
| | | |
| | | async function openModal( |
| | | selectedIds?: number[], |
| | | options?: { multiple?: boolean; approvalFilter?: boolean }, |
| | | options?: { multiple?: boolean; approvalFilter?: boolean; excludeApproved?: boolean }, |
| | | ) { |
| | | open.value = true; |
| | | multiple.value = options?.multiple ?? true; |
| | | approvalFilter.value = options?.approvalFilter ?? true; |
| | | excludeApproved.value = options?.excludeApproved ?? false; |
| | | preSelectedIds.value = selectedIds || []; |
| | | await nextTick(); |
| | | gridApi.setGridOptions({ |
| | |
| | | allowClear?: boolean; |
| | | approvalFilter?: boolean; |
| | | disabled?: boolean; |
| | | excludeApproved?: boolean; |
| | | modelValue?: number; |
| | | placeholder?: string; |
| | | supplierName?: string; |
| | |
| | | allowClear: true, |
| | | approvalFilter: true, |
| | | disabled: false, |
| | | excludeApproved: false, |
| | | modelValue: undefined, |
| | | placeholder: '请选择供应商', |
| | | supplierName: undefined, |
| | |
| | | const selectedIds = ( |
| | | props.modelValue === null ? [] : [props.modelValue] |
| | | ) as number[]; |
| | | dialogRef.value?.open(selectedIds, { multiple: false, approvalFilter: props.approvalFilter }); |
| | | dialogRef.value?.open(selectedIds, { multiple: false, approvalFilter: props.approvalFilter, excludeApproved: props.excludeApproved }); |
| | | } |
| | | |
| | | function handleSelected(rows: SrmSupplierApi.SupplierVO[]) { |
| | |
| | | component: markRaw(SrmSupplierSelect), |
| | | componentProps: { |
| | | approvalFilter: false, |
| | | excludeApproved: true, |
| | | placeholder: '请选择SRM供应商(或直接填写供应商名称)', |
| | | onChange: async (supplier?: SrmSupplierApi.SupplierVO) => { |
| | | if (!supplier || !formApi) return; |
| | |
| | | contactEmail: supplier.contactEmail || '', |
| | | taxNo: supplier.taxNo || '', |
| | | address: supplier.address || '', |
| | | bankName: supplier.bankName || '', |
| | | bankAccount: supplier.bankAccount || '', |
| | | remark: supplier.remark || '', |
| | | }); |
| | | }, |
| | | }, |
| | |
| | | triggerFields: ['supplierName'], |
| | | componentProps: (values) => ({ |
| | | approvalFilter: false, |
| | | excludeApproved: true, |
| | | placeholder: '请选择SRM供应商(或直接填写供应商名称)', |
| | | supplierName: values.supplierName as string, |
| | | }), |
| | |
| | | |
| | | import { Page } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | import { message, Modal } from 'ant-design-vue'; |
| | | import { IconifyIcon } from '@vben/icons'; |
| | | |
| | | import { |
| | |
| | | const materialLoading = ref(false); |
| | | const materialFormVisible = ref(false); |
| | | const materialForm = ref<SrmTenderApi.TenderMaterialVO>({ tenderProjectId: projectId }); |
| | | |
| | | // 已添加的物料产品ID集合,避免重复添加 |
| | | const addedProductIds = computed(() => { |
| | | const ids = new Set( |
| | | materialList.value |
| | | .filter((m) => m.productId !== undefined) |
| | | .map((m) => m.productId!), |
| | | ); |
| | | // 编辑时允许保留当前物料的 productId |
| | | if (materialForm.value.id && materialForm.value.productId) { |
| | | ids.delete(materialForm.value.productId); |
| | | } |
| | | return ids; |
| | | }); |
| | | |
| | | // 物料下拉可选列表(排除已添加的) |
| | | const availableMdmItems = computed(() => |
| | | mdmItemList.value.filter((item) => !addedProductIds.value.has(item.id!)), |
| | | ); |
| | | |
| | | async function loadProject() { |
| | | project.value = await getTenderProject(projectId); |
| | |
| | | try { quoteList.value = await getQuoteList(bidId); } finally { quoteLoading.value = false; } |
| | | } |
| | | async function handleSaveQuote() { |
| | | // 校验:报价金额与投标金额一致性 |
| | | const bid = bidList.value.find((b) => b.id === quoteForm.value.bidId); |
| | | if (bid?.bidTotalAmount != null) { |
| | | const otherQuotesTotal = quoteList.value |
| | | .filter((q) => q.id !== quoteForm.value.id) |
| | | .reduce((sum, q) => sum + (q.quotePrice ?? 0), 0); |
| | | const newTotal = otherQuotesTotal + (quoteForm.value.quotePrice ?? 0); |
| | | if (newTotal !== bid.bidTotalAmount) { |
| | | await new Promise<void>((resolve, reject) => { |
| | | Modal.confirm({ |
| | | title: '金额不一致提示', |
| | | content: `报价总金额(${newTotal.toLocaleString()})与投标金额(${bid.bidTotalAmount!.toLocaleString()})不一致,是否继续提交?`, |
| | | okText: '继续提交', |
| | | cancelText: '取消', |
| | | onOk: () => { resolve(); }, |
| | | onCancel: () => { reject(new Error('cancel')); }, |
| | | }); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | if (quoteForm.value.id) { |
| | | await updateQuote(quoteForm.value); |
| | | } else { |
| | |
| | | <a-form layout="vertical" class="mt-4"> |
| | | <a-form-item label="MDM物料" required> |
| | | <a-select v-model:value="materialForm.productId" show-search placeholder="搜索并选择MDM物料" :filter-option="filterOption" option-label-prop="label" @change="onMaterialSelect"> |
| | | <a-select-option v-for="item in mdmItemList" :key="item.id" :value="item.id" :label="`${item.code} ${item.name}`"> |
| | | <a-select-option v-for="item in availableMdmItems" :key="item.id" :value="item.id" :label="`${item.code} ${item.name}`"> |
| | | <div class="flex flex-col"> |
| | | <span class="font-medium">{{ item.code }}</span> |
| | | <span class="text-xs text-gray-400">{{ item.name }}{{ item.specification ? ` · ${item.specification}` : '' }}</span> |