Merge branch 'dev_pro2.0' of http://114.132.189.42:9002/r/mom-pro2-before into dev_pro2.0
| | |
| | | |
| | | /** é¢åå
¬æµ·å®¢æ· */ |
| | | export function receiveCustomer(ids: number[]) { |
| | | return requestClient.put('/crm/customer/receive', { ids: ids.join(',') }); |
| | | return requestClient.put('/crm/customer/receive', null, { |
| | | params: { ids: ids.join(',') }, |
| | | }); |
| | | } |
| | | |
| | | /** åé
å
¬æµ·ç»å¯¹åºè´è´£äºº */ |
| | |
| | | depositPrice?: number; // å®ééé¢ï¼åä½ï¼å
|
| | | contractId?: number; // å
³è CRM ååç¼å· |
| | | contractNo?: string; // CRM åååå· |
| | | needProduction?: number; // æ¯å¦éè¦ç产ï¼0-ä¸éè¦ï¼1-éè¦ |
| | | items?: SaleOrderItem[]; // éå®è®¢å产åæç»å表 |
| | | } |
| | | |
| | |
| | | totalTaxPrice?: number; // å«ç¨æ»ä»·ï¼åä½ï¼å
|
| | | remark?: string; // 夿³¨ |
| | | stockCount?: number; // åºåæ°éï¼æ¾ç¤ºåæ®µï¼ |
| | | needProduction?: number; // æ¯å¦éè¦ç产ï¼0-ä¸éè¦ï¼1-éè¦ |
| | | mpsId?: number; // MPSç¼å· |
| | | } |
| | | } |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace MesProMpsApi { |
| | | /** MPS 主ç产计å */ |
| | | export interface Mps { |
| | | id?: number; // MPS ç¼å· |
| | | code?: string; // MPS ç¼ç |
| | | status?: number; // ç¶æï¼0=è稿 1=å·²ä¸å 2=已宿 3=å·²åæ¶ï¼ |
| | | saleOrderId?: number; // éå®è®¢åID |
| | | saleOrderNo?: string; // éå®è®¢åå· |
| | | saleOrderItemId?: number; // éå®è®¢åæç»ID |
| | | productId?: number; // 产åID |
| | | productCode?: string; // 产åç¼ç |
| | | productName?: string; // 产ååç§° |
| | | productSpecification?: string; // è§æ ¼åå· |
| | | unitMeasureName?: string; // åä½ |
| | | quantity?: number; // 计åç产æ°é |
| | | quantityProduced?: number; // å·²ç产æ°é |
| | | clientId?: number; // 客æ·ID |
| | | clientCode?: string; // 客æ·ç¼ç |
| | | clientName?: string; // 客æ·åç§° |
| | | requestDate?: number; // éæ±æ¥æ |
| | | issueDate?: number; // ä¸åæ¥æ |
| | | finishDate?: number; // å®ææ¥æ |
| | | workOrderId?: number; // ç产工åID |
| | | workOrderCode?: string; // ç产工åç¼ç |
| | | mergeFlag?: number; // æ¯å¦åå¹¶ä¸åï¼0-å¦ï¼1-æ¯ï¼ |
| | | mergeBatchNo?: string; // åå¹¶æ¹æ¬¡å· |
| | | remark?: string; // 夿³¨ |
| | | createTime?: number; // å建æ¶é´ |
| | | } |
| | | |
| | | /** MPS å页æ¥è¯¢åæ° */ |
| | | export interface PageParams extends PageParam { |
| | | code?: string; |
| | | saleOrderNo?: string; |
| | | productId?: number; |
| | | clientId?: number; |
| | | status?: number; |
| | | requestDate?: number[]; |
| | | } |
| | | |
| | | /** MPS åå»ºè¯·æ± */ |
| | | export interface CreateMpsReq { |
| | | code?: string; |
| | | saleOrderId?: number; |
| | | saleOrderNo?: string; |
| | | saleOrderItemId?: number; |
| | | productId?: number; |
| | | quantity?: number; |
| | | unitMeasureId?: number; |
| | | clientId?: number; |
| | | requestDate?: string; |
| | | remark?: string; |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢ MPS å页 */ |
| | | export function getMpsPage(params: MesProMpsApi.PageParams) { |
| | | return requestClient.get<PageResult<MesProMpsApi.Mps>>('/mes/pro-mps/page', { |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | /** æ¥è¯¢ MPS 详æ
*/ |
| | | export function getMps(id: number) { |
| | | return requestClient.get<MesProMpsApi.Mps>(`/mes/pro-mps/get?id=${id}`); |
| | | } |
| | | |
| | | /** å建 MPS */ |
| | | export function createMps(data: MesProMpsApi.CreateMpsReq) { |
| | | return requestClient.post('/mes/pro-mps/create', data); |
| | | } |
| | | |
| | | /** æ´æ° MPS */ |
| | | export function updateMps(data: MesProMpsApi.CreateMpsReq & { id: number }) { |
| | | return requestClient.put('/mes/pro-mps/update', data); |
| | | } |
| | | |
| | | /** å é¤ MPS */ |
| | | export function deleteMps(ids: number[]) { |
| | | return requestClient.delete('/mes/pro-mps/delete', { |
| | | params: { ids: ids.join(',') }, |
| | | }); |
| | | } |
| | | |
| | | /** å¯¼åº MPS Excel */ |
| | | export function exportMps(params: any) { |
| | | return requestClient.download('/mes/pro-mps/export-excel', { params }); |
| | | } |
| | | |
| | | /** ä¸å MPS */ |
| | | export function issueMps(id: number) { |
| | | return requestClient.post(`/mes/pro-mps/issue?id=${id}`); |
| | | } |
| | | |
| | | /** åå¹¶ä¸å MPS */ |
| | | export function mergeIssueMps(ids: number[]) { |
| | | return requestClient.post('/mes/pro-mps/merge-issue', ids); |
| | | } |
| | | |
| | | /** åæ¶ MPS */ |
| | | export function cancelMps(id: number) { |
| | | return requestClient.post(`/mes/pro-mps/cancel?id=${id}`); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { CrmCustomerApi } from '#/api/crm/customer'; |
| | | |
| | | import { nextTick, ref } from 'vue'; |
| | | |
| | | import { message, Modal, Tabs } from 'ant-design-vue'; |
| | | |
| | | import { useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { getCustomerPage } from '#/api/crm/customer'; |
| | | |
| | | defineOptions({ name: 'CrmCustomerSelectDialog' }); |
| | | |
| | | const emit = defineEmits<{ |
| | | selected: [rows: CrmCustomerApi.Customer[]]; |
| | | }>(); |
| | | |
| | | const open = ref(false); |
| | | const multiple = ref(true); |
| | | const selectedRows = ref<CrmCustomerApi.Customer[]>([]); |
| | | const preSelectedIds = ref<number[]>([]); |
| | | const sceneType = ref('1'); |
| | | |
| | | function getTableRows() { |
| | | return gridApi.grid.getTableData().fullData as CrmCustomerApi.Customer[]; |
| | | } |
| | | |
| | | function getMultipleSelectedRows() { |
| | | const selectedMap = new Map<number, CrmCustomerApi.Customer>(); |
| | | const records = [ |
| | | ...(gridApi.grid.getCheckboxReserveRecords?.() ?? []), |
| | | ...(gridApi.grid.getCheckboxRecords?.() ?? []), |
| | | ] as CrmCustomerApi.Customer[]; |
| | | records.forEach((row) => { |
| | | const rowId = row.id; |
| | | if (rowId) { |
| | | selectedMap.set(rowId, row); |
| | | } |
| | | }); |
| | | return [...selectedMap.values()]; |
| | | } |
| | | |
| | | function handleCheckboxSelectChange() { |
| | | selectedRows.value = getMultipleSelectedRows(); |
| | | } |
| | | |
| | | function handleRadioChange(row: CrmCustomerApi.Customer) { |
| | | selectedRows.value = [row]; |
| | | } |
| | | |
| | | async function toggleMultipleRow(row: CrmCustomerApi.Customer) { |
| | | const selected = gridApi.grid.isCheckedByCheckboxRow(row); |
| | | await gridApi.grid.setCheckboxRow(row, !selected); |
| | | selectedRows.value = getMultipleSelectedRows(); |
| | | } |
| | | |
| | | async function handleCellDblclick({ |
| | | row, |
| | | }: { |
| | | row: CrmCustomerApi.Customer; |
| | | }) { |
| | | if (multiple.value) { |
| | | await toggleMultipleRow(row); |
| | | return; |
| | | } |
| | | selectedRows.value = [row]; |
| | | await gridApi.grid.setRadioRow(row); |
| | | handleConfirm(); |
| | | } |
| | | |
| | | async function applyPreSelection() { |
| | | if (preSelectedIds.value.length === 0) { |
| | | return; |
| | | } |
| | | const rows = getTableRows(); |
| | | for (const row of rows) { |
| | | if ( |
| | | row.id === undefined || |
| | | row.id === null || |
| | | !preSelectedIds.value.includes(row.id) |
| | | ) { |
| | | continue; |
| | | } |
| | | if (multiple.value) { |
| | | await gridApi.grid.setCheckboxRow(row, true); |
| | | } else { |
| | | await gridApi.grid.setRadioRow(row); |
| | | selectedRows.value = [row]; |
| | | return; |
| | | } |
| | | } |
| | | if (multiple.value) { |
| | | selectedRows.value = getMultipleSelectedRows(); |
| | | } |
| | | } |
| | | |
| | | function handleSceneTypeChange(key: number | string) { |
| | | sceneType.value = key.toString(); |
| | | gridApi.query(); |
| | | } |
| | | |
| | | const gridColumns = [ |
| | | { field: 'name', title: '客æ·åç§°', minWidth: 180 }, |
| | | { field: 'mobile', title: 'ææºå·', width: 140 }, |
| | | { field: 'telephone', title: 'çµè¯', width: 140 }, |
| | | { field: 'areaName', title: 'æå¨å°', width: 160 }, |
| | | { field: 'detailAddress', title: '详ç»å°å', minWidth: 200 }, |
| | | ]; |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: [ |
| | | { |
| | | fieldName: 'name', |
| | | label: '客æ·åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥å®¢æ·åç§°', |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | gridOptions: { |
| | | columns: [{ type: 'radio', width: 50 }, ...gridColumns], |
| | | height: 460, |
| | | keepSource: true, |
| | | checkboxConfig: { |
| | | highlight: true, |
| | | range: true, |
| | | reserve: true, |
| | | }, |
| | | radioConfig: { |
| | | highlight: true, |
| | | trigger: 'row', |
| | | }, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getCustomerPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | sceneType: sceneType.value, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<CrmCustomerApi.Customer>, |
| | | gridEvents: { |
| | | cellDblclick: handleCellDblclick, |
| | | checkboxAll: handleCheckboxSelectChange, |
| | | checkboxChange: handleCheckboxSelectChange, |
| | | radioChange: ({ |
| | | row, |
| | | }: { |
| | | row: CrmCustomerApi.Customer; |
| | | }) => { |
| | | handleRadioChange(row); |
| | | }, |
| | | }, |
| | | }); |
| | | |
| | | async function resetQueryState() { |
| | | sceneType.value = '1'; |
| | | selectedRows.value = []; |
| | | await gridApi.grid.clearCheckboxRow(); |
| | | await gridApi.grid.clearCheckboxReserve(); |
| | | await gridApi.grid.clearRadioRow(); |
| | | await gridApi.formApi.resetForm(); |
| | | } |
| | | |
| | | async function openModal( |
| | | selectedIds?: number[], |
| | | options?: { multiple?: boolean }, |
| | | ) { |
| | | open.value = true; |
| | | multiple.value = options?.multiple ?? true; |
| | | preSelectedIds.value = selectedIds || []; |
| | | await nextTick(); |
| | | gridApi.setGridOptions({ |
| | | columns: multiple.value |
| | | ? [{ type: 'checkbox', width: 50 }, ...gridColumns] |
| | | : [{ type: 'radio', width: 50 }, ...gridColumns], |
| | | }); |
| | | await resetQueryState(); |
| | | await gridApi.query(); |
| | | await nextTick(); |
| | | await applyPreSelection(); |
| | | } |
| | | |
| | | async function closeModal() { |
| | | open.value = false; |
| | | await resetQueryState(); |
| | | } |
| | | |
| | | function handleConfirm() { |
| | | const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value; |
| | | if (rows.length === 0) { |
| | | message.warning( |
| | | multiple.value ? '请è³å°éæ©ä¸æ¡æ°æ®' : 'è¯·éæ©ä¸æ¡æ°æ®', |
| | | ); |
| | | return; |
| | | } |
| | | emit('selected', multiple.value ? rows : [rows[0]!]); |
| | | open.value = false; |
| | | } |
| | | |
| | | defineExpose({ open: openModal }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal |
| | | v-model:open="open" |
| | | title="客æ·éæ©" |
| | | width="70%" |
| | | :destroy-on-close="true" |
| | | @ok="handleConfirm" |
| | | @cancel="closeModal" |
| | | > |
| | | <Tabs class="mb-2" @change="handleSceneTypeChange"> |
| | | <Tabs.TabPane key="1" tab="æè´è´£ç" /> |
| | | <Tabs.TabPane key="2" tab="æåä¸ç" /> |
| | | <Tabs.TabPane key="3" tab="ä¸å±è´è´£ç" /> |
| | | </Tabs> |
| | | <Grid table-title="客æ·å表" /> |
| | | </Modal> |
| | | </template> |
| | |
| | | <script lang="ts" setup> |
| | | import type { CrmCustomerApi } from '#/api/crm/customer'; |
| | | |
| | | import { computed, ref, watch } from 'vue'; |
| | | import { computed, ref, useAttrs, watch } from 'vue'; |
| | | |
| | | import { Select } from 'ant-design-vue'; |
| | | import { IconifyIcon } from '@vben/icons'; |
| | | |
| | | import { getCustomerSimpleList } from '#/api/crm/customer'; |
| | | import { Input, Tooltip } from 'ant-design-vue'; |
| | | |
| | | interface Props { |
| | | value?: number; |
| | | disabled?: boolean; |
| | | } |
| | | import { getCustomer } from '#/api/crm/customer'; |
| | | |
| | | const props = withDefaults(defineProps<Props>(), { |
| | | value: undefined, |
| | | disabled: false, |
| | | }); |
| | | import CrmCustomerSelectDialog from './crm-customer-select-dialog.vue'; |
| | | |
| | | const emit = defineEmits(['update:value', 'change']); |
| | | defineOptions({ name: 'CrmCustomerSelect', inheritAttrs: false }); |
| | | |
| | | const customerList = ref<CrmCustomerApi.Customer[]>([]); |
| | | const loading = ref(false); |
| | | |
| | | const options = computed(() => { |
| | | return customerList.value.map((item) => ({ |
| | | label: item.name, |
| | | value: item.id, |
| | | })); |
| | | }); |
| | | |
| | | const selectedValue = ref(props.value); |
| | | |
| | | watch( |
| | | () => props.value, |
| | | (val) => { |
| | | selectedValue.value = val; |
| | | const props = withDefaults( |
| | | defineProps<{ |
| | | allowClear?: boolean; |
| | | disabled?: boolean; |
| | | modelValue?: number; |
| | | placeholder?: string; |
| | | }>(), |
| | | { |
| | | allowClear: true, |
| | | disabled: false, |
| | | modelValue: undefined, |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | }, |
| | | ); |
| | | const emit = defineEmits<{ |
| | | change: [item: CrmCustomerApi.Customer | undefined]; |
| | | 'update:modelValue': [value: number | undefined]; |
| | | }>(); |
| | | const attrs = useAttrs(); |
| | | const dialogRef = ref<InstanceType<typeof CrmCustomerSelectDialog>>(); |
| | | const hovering = ref(false); |
| | | const selectedItem = ref<CrmCustomerApi.Customer>(); |
| | | |
| | | watch(selectedValue, (val) => { |
| | | emit('update:value', val); |
| | | const customer = customerList.value.find((item) => item.id === val); |
| | | emit('change', customer); |
| | | }); |
| | | const displayLabel = computed(() => selectedItem.value?.name ?? ''); |
| | | const showClear = computed( |
| | | () => |
| | | props.allowClear && |
| | | !props.disabled && |
| | | hovering.value && |
| | | props.modelValue !== null, |
| | | ); |
| | | |
| | | async function loadCustomers() { |
| | | if (customerList.value.length > 0) return; |
| | | loading.value = true; |
| | | try { |
| | | customerList.value = await getCustomerSimpleList(); |
| | | } finally { |
| | | loading.value = false; |
| | | async function resolveCustomerById(id: number | undefined) { |
| | | if (!id) { |
| | | selectedItem.value = undefined; |
| | | return; |
| | | } |
| | | if (selectedItem.value?.id === id) { |
| | | return; |
| | | } |
| | | selectedItem.value = await getCustomer(id); |
| | | } |
| | | |
| | | function filterOption(input: string, option: any) { |
| | | return option.label.toLowerCase().includes(input.toLowerCase()); |
| | | watch( |
| | | () => props.modelValue, |
| | | (value) => { |
| | | resolveCustomerById(value); |
| | | }, |
| | | { immediate: true }, |
| | | ); |
| | | |
| | | function clearSelected() { |
| | | selectedItem.value = undefined; |
| | | emit('update:modelValue', undefined); |
| | | emit('change', undefined); |
| | | } |
| | | |
| | | loadCustomers(); |
| | | function handleClick(event: MouseEvent) { |
| | | if (props.disabled) { |
| | | return; |
| | | } |
| | | const target = event.target as HTMLElement; |
| | | if (showClear.value && target.closest('.ant-input-suffix')) { |
| | | event.stopPropagation(); |
| | | clearSelected(); |
| | | return; |
| | | } |
| | | const selectedIds = ( |
| | | props.modelValue === null ? [] : [props.modelValue] |
| | | ) as number[]; |
| | | dialogRef.value?.open(selectedIds, { multiple: false }); |
| | | } |
| | | |
| | | function handleSelected(rows: CrmCustomerApi.Customer[]) { |
| | | const item = rows[0]; |
| | | if (!item) { |
| | | return; |
| | | } |
| | | selectedItem.value = item; |
| | | emit('update:modelValue', item.id); |
| | | emit('change', item); |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <Select |
| | | v-model:value="selectedValue" |
| | | :options="options" |
| | | :loading="loading" |
| | | :disabled="disabled" |
| | | placeholder="è¯·éæ©å®¢æ·" |
| | | show-search |
| | | :filter-option="filterOption" |
| | | allow-clear |
| | | /> |
| | | </template> |
| | | <div |
| | | v-bind="attrs" |
| | | class="w-full" |
| | | :class="disabled ? 'cursor-not-allowed' : 'cursor-pointer'" |
| | | @click="handleClick" |
| | | @mouseenter="hovering = true" |
| | | @mouseleave="hovering = false" |
| | | > |
| | | <Tooltip :mouse-enter-delay="0.5" :open="selectedItem ? undefined : false"> |
| | | <template #title> |
| | | <div v-if="selectedItem" class="leading-6"> |
| | | <div>åç§°ï¼{{ selectedItem.name || '-' }}</div> |
| | | <div>ææºï¼{{ selectedItem.mobile || '-' }}</div> |
| | | <div>å°åºï¼{{ selectedItem.areaName || '-' }}</div> |
| | | </div> |
| | | </template> |
| | | <Input |
| | | :disabled="disabled" |
| | | :placeholder="placeholder" |
| | | :value="displayLabel" |
| | | readonly |
| | | > |
| | | <template #suffix> |
| | | <IconifyIcon |
| | | class="size-4" |
| | | :icon="showClear ? 'lucide:circle-x' : 'lucide:search'" |
| | | /> |
| | | </template> |
| | | </Input> |
| | | </Tooltip> |
| | | </div> |
| | | <CrmCustomerSelectDialog ref="dialogRef" @selected="handleSelected" /> |
| | | </template> |
| | |
| | | |
| | | import { z } from '#/adapter/form'; |
| | | import { getBusinessStatusTypeSimpleList } from '#/api/crm/business/status'; |
| | | import { getCustomerSimpleList } from '#/api/crm/customer'; |
| | | import { getSimpleUserList } from '#/api/system/user'; |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹ç表å */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | |
| | | { |
| | | fieldName: 'customerId', |
| | | label: '客æ·åç§°', |
| | | component: 'ApiSelect', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | api: getCustomerSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | allowClear: true, |
| | | }, |
| | |
| | | import { useUserStore } from '@vben/stores'; |
| | | |
| | | import { getSimpleContactList } from '#/api/crm/contact'; |
| | | import { getCustomerSimpleList } from '#/api/crm/customer'; |
| | | import { getSimpleUserList } from '#/api/system/user'; |
| | | import { AreaCascader } from '#/components/area'; |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹ç表å */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | |
| | | { |
| | | fieldName: 'customerId', |
| | | label: '客æ·åç§°', |
| | | component: 'ApiSelect', |
| | | component: markRaw(CrmCustomerSelect), |
| | | rules: 'required', |
| | | componentProps: { |
| | | api: getCustomerSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | }, |
| | | }, |
| | |
| | | { |
| | | fieldName: 'customerId', |
| | | label: '客æ·', |
| | | component: 'ApiSelect', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | api: getCustomerSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | }, |
| | | }, |
| | |
| | | import { z } from '#/adapter/form'; |
| | | import { getSimpleBusinessList } from '#/api/crm/business'; |
| | | import { getSimpleContactList } from '#/api/crm/contact'; |
| | | import { getCustomerSimpleList } from '#/api/crm/customer'; |
| | | import { getSimpleUserList } from '#/api/system/user'; |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹ç表å */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | |
| | | { |
| | | fieldName: 'customerId', |
| | | label: '客æ·åç§°', |
| | | component: 'ApiSelect', |
| | | component: markRaw(CrmCustomerSelect), |
| | | rules: 'required', |
| | | componentProps: { |
| | | api: getCustomerSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | }, |
| | | }, |
| | |
| | | { |
| | | fieldName: 'customerId', |
| | | label: '客æ·', |
| | | component: 'ApiSelect', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | api: getCustomerSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | allowClear: true, |
| | | }, |
| | |
| | | import { useUserStore } from '@vben/stores'; |
| | | |
| | | import { getContractSimpleList } from '#/api/crm/contract'; |
| | | import { getCustomerSimpleList } from '#/api/crm/customer'; |
| | | import { |
| | | getReceivablePlan, |
| | | getReceivablePlanSimpleList, |
| | | } from '#/api/crm/receivable/plan'; |
| | | import { getSimpleUserList } from '#/api/system/user'; |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹ç表å */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | |
| | | { |
| | | fieldName: 'customerId', |
| | | label: '客æ·åç§°', |
| | | component: 'ApiSelect', |
| | | component: markRaw(CrmCustomerSelect), |
| | | rules: 'required', |
| | | componentProps: (_values, form) => ({ |
| | | api: getCustomerSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | onChange: () => { |
| | | form.setFieldValue('contractId', undefined); |
| | |
| | | { |
| | | fieldName: 'customerId', |
| | | label: '客æ·', |
| | | component: 'ApiSelect', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | api: getCustomerSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | allowClear: true, |
| | | }, |
| | |
| | | import { erpPriceInputFormatter } from '@vben/utils'; |
| | | |
| | | import { getContractSimpleList } from '#/api/crm/contract'; |
| | | import { getCustomerSimpleList } from '#/api/crm/customer'; |
| | | import { getSimpleUserList } from '#/api/system/user'; |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹ç表å */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | |
| | | { |
| | | fieldName: 'customerId', |
| | | label: '客æ·', |
| | | component: 'ApiSelect', |
| | | component: markRaw(CrmCustomerSelect), |
| | | rules: 'required', |
| | | componentProps: { |
| | | api: getCustomerSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | allowClear: true, |
| | | }, |
| | |
| | | { |
| | | fieldName: 'customerId', |
| | | label: '客æ·', |
| | | component: 'ApiSelect', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | api: getCustomerSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | allowClear: true, |
| | | }, |
| | |
| | | import { z } from '#/adapter/form'; |
| | | import { getSimpleBusinessList } from '#/api/crm/business'; |
| | | import { getSimpleContactList } from '#/api/crm/contact'; |
| | | import { getCustomerSimpleList } from '#/api/crm/customer'; |
| | | import { getItemPage } from '#/api/mdm/item'; |
| | | import { getSimpleUserList } from '#/api/system/user'; |
| | | import { useUserStore } from '#/packages/stores/src'; |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | /** æ¥ä»·åç¶æ */ |
| | | export const QUOTATION_STATUS = { |
| | |
| | | { |
| | | fieldName: 'customerId', |
| | | label: '客æ·åç§°', |
| | | component: 'ApiSelect', |
| | | component: markRaw(CrmCustomerSelect), |
| | | rules: 'required', |
| | | componentProps: { |
| | | api: getCustomerSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | }, |
| | | }, |
| | |
| | | { |
| | | fieldName: 'customerId', |
| | | label: '客æ·', |
| | | component: 'ApiSelect', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | api: getCustomerSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | allowClear: true, |
| | | }, |
| | |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.ERP_AUDIT_STATUS }, |
| | |
| | | field: 'inStatus', |
| | | title: 'å
¥åºç¶æ', |
| | | minWidth: 100, |
| | | fixed: 'right', |
| | | slots: { default: 'inStatus' }, |
| | | }, |
| | | { |
| | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { downloadFileFromBlobPart, isEmpty } from '@vben/utils'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #inStatus="{ row }"> |
| | | <Tag v-if="row.inStatus === 0" color="default">æªå
¥åº</Tag> |
| | | <Tag v-else-if="row.inStatus === 1" color="warning">é¨åå
¥åº</Tag> |
| | | <Tag v-else-if="row.inStatus === 2" color="success">å
¨é¨å
¥åº</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | |
| | | import { z } from '#/adapter/form'; |
| | | import { getAccountSimpleList } from '#/api/erp/finance/account'; |
| | | import { getItemPage } from '#/api/mdm/item'; |
| | | import { getCustomerSimpleList as getCrmCustomerSimpleList } from '#/api/crm/customer'; |
| | | import { getSimpleUserList } from '#/api/system/user'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'edit'; |
| | |
| | | { |
| | | label: '客æ·', |
| | | fieldName: 'customerId', |
| | | component: 'ApiSelect', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | allowClear: true, |
| | | showSearch: true, |
| | | api: getCrmCustomerSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | |
| | | minWidth: 80, |
| | | }, |
| | | { |
| | | field: 'needProduction', |
| | | title: 'éè¦ç产', |
| | | minWidth: 100, |
| | | slots: { default: 'needProduction' }, |
| | | }, |
| | | { |
| | | field: 'remark', |
| | | title: '夿³¨', |
| | | minWidth: 150, |
| | |
| | | { |
| | | fieldName: 'supplierId', |
| | | label: '客æ·', |
| | | component: 'ApiSelect', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | allowClear: true, |
| | | showSearch: true, |
| | | api: getCrmCustomerSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | }, |
| | | }, |
| | | { |
| | |
| | | slots: { default: 'status' }, |
| | | }, |
| | | { |
| | | field: 'needProduction', |
| | | title: 'éè¦ç产', |
| | | minWidth: 100, |
| | | fixed: 'right', |
| | | slots: { default: 'needProduction' }, |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 260, |
| | | fixed: 'right', |
| | |
| | | <Tag v-if="row.status === 20" color="success">å·²å®¡æ ¸</Tag> |
| | | <Tag v-if="row.status === 30" color="error">å·²ä½åº</Tag> |
| | | </template> |
| | | <template #needProduction="{ row }"> |
| | | <Tag v-if="row.needProduction === 1" color="success">æ¯</Tag> |
| | | <Tag v-else color="default">å¦</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | |
| | | erpPriceMultiply, |
| | | } from '@vben/utils'; |
| | | |
| | | import { Input, InputNumber, Select } from 'ant-design-vue'; |
| | | import { Input, InputNumber, Select, Tag } from 'ant-design-vue'; |
| | | |
| | | import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { getItemPage } from '#/api/mdm/item'; |
| | |
| | | taxPrice: undefined, |
| | | totalPrice: undefined, |
| | | remark: undefined, |
| | | needProduction: 1, // é»è®¤éè¦ç产 |
| | | }; |
| | | tableData.value.push(newRow); |
| | | // éç¥ç¶ç»ä»¶æ´æ° |
| | |
| | | erpPriceMultiply(row.totalProductPrice, (row.taxPercent || 0) / 100) ?? 0; |
| | | row.totalPrice = row.totalProductPrice + row.taxPrice; |
| | | } |
| | | // é»è®¤éè¦ç产 |
| | | if (row.needProduction === undefined) { |
| | | row.needProduction = 1; |
| | | } |
| | | } |
| | | |
| | | /** è¡¨åæ ¡éª */ |
| | |
| | | /> |
| | | <span v-else>{{ row.productName || '-' }}</span> |
| | | </template> |
| | | <template #needProduction="{ row }"> |
| | | <Select |
| | | v-if="!disabled" |
| | | v-model:value="row.needProduction" |
| | | :options="[ |
| | | { label: 'æ¯', value: 1 }, |
| | | { label: 'å¦', value: 0 }, |
| | | ]" |
| | | class="w-full" |
| | | placeholder="è¯·éæ©" |
| | | @change="handleRowChange(row)" |
| | | /> |
| | | <Tag v-else-if="row.needProduction === 1" color="success">æ¯</Tag> |
| | | <Tag v-else color="default">å¦</Tag> |
| | | </template> |
| | | <template #count="{ row }"> |
| | | <InputNumber |
| | | v-if="!disabled" |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesProMpsApi } from '#/api/mes/pro/mps'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'edit'; |
| | | |
| | | /** MPS ç¶ææä¸¾ */ |
| | | export const MpsStatusEnum = { |
| | | DRAFT: 0, // è稿 |
| | | ISSUED: 1, // å·²ä¸å |
| | | FINISHED: 2, // 已宿 |
| | | CANCELLED: 3, // 已忶 |
| | | }; |
| | | |
| | | /** æ°å¢/ç¼è¾è¡¨å */ |
| | | export function useFormSchema(formType: FormType): VbenFormSchema[] { |
| | | const isCreate = formType === 'create'; |
| | | const isEdit = formType === 'edit'; |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'code', |
| | | label: 'MPSç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'ç³»ç»èªå¨çæ', |
| | | disabled: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'saleOrderId', |
| | | label: 'éå®è®¢åID', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥éå®è®¢åIDï¼å¯éï¼', |
| | | disabled: !isCreate && !isEdit, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'saleOrderNo', |
| | | label: 'éå®è®¢åå·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥éå®è®¢åå·ï¼å¯éï¼', |
| | | disabled: !isCreate && !isEdit, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'saleOrderItemId', |
| | | label: 'éå®è®¢åæç»ID', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥éå®è®¢åæç»IDï¼å¯éï¼', |
| | | disabled: !isCreate && !isEdit, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'productId', |
| | | label: '产å', |
| | | component: markRaw(MdItemSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©äº§å', |
| | | disabled: !isCreate && !isEdit, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'quantity', |
| | | label: '计åç产æ°é', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | placeholder: '请è¾å
¥è®¡åç产æ°é', |
| | | min: 1, |
| | | precision: 2, |
| | | disabled: !isCreate && !isEdit, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'clientId', |
| | | label: '客æ·', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | disabled: !isCreate && !isEdit, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'requestDate', |
| | | label: 'éæ±æ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | placeholder: 'è¯·éæ©éæ±æ¥æ', |
| | | format: 'YYYY-MM-DD', |
| | | valueFormat: 'x', |
| | | disabled: !isCreate && !isEdit, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 3, |
| | | disabled: !isCreate && !isEdit, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'code', |
| | | label: 'MPSç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥MPSç¼ç ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'saleOrderNo', |
| | | label: 'éå®è®¢åå·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥éå®è®¢åå·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'productId', |
| | | label: '产å', |
| | | component: markRaw(MdItemSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©äº§å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'clientId', |
| | | label: '客æ·', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: [ |
| | | { label: 'è稿', value: 0 }, |
| | | { label: 'å·²ä¸å', value: 1 }, |
| | | { label: '已宿', value: 2 }, |
| | | { label: '已忶', value: 3 }, |
| | | ], |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'requestDate', |
| | | label: 'éæ±æ¥æ', |
| | | component: 'RangePicker', |
| | | componentProps: { |
| | | ...getRangePickerDefaultProps(), |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<MesProMpsApi.Mps>['columns'] { |
| | | return [ |
| | | { |
| | | type: 'checkbox', |
| | | width: 50, |
| | | fixed: 'left', |
| | | }, |
| | | { |
| | | field: 'code', |
| | | title: 'MPSç¼ç ', |
| | | fixed: 'left', |
| | | width: 180, |
| | | slots: { default: 'code' }, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | width: 100, |
| | | slots: { default: 'status' }, |
| | | }, |
| | | { |
| | | field: 'saleOrderNo', |
| | | title: 'éå®è®¢åå·', |
| | | width: 180, |
| | | }, |
| | | { |
| | | field: 'productCode', |
| | | title: '产åç¼ç ', |
| | | width: 120, |
| | | }, |
| | | { |
| | | field: 'productName', |
| | | title: '产ååç§°', |
| | | minWidth: 150, |
| | | }, |
| | | { |
| | | field: 'productSpecification', |
| | | title: 'è§æ ¼åå·', |
| | | width: 120, |
| | | }, |
| | | { |
| | | field: 'unitMeasureName', |
| | | title: 'åä½', |
| | | width: 80, |
| | | }, |
| | | { |
| | | field: 'quantity', |
| | | title: 'è®¡åæ°é', |
| | | width: 100, |
| | | formatter: 'formatAmount3', |
| | | }, |
| | | { |
| | | field: 'quantityProduced', |
| | | title: 'å·²ç产æ°é', |
| | | width: 100, |
| | | formatter: 'formatAmount3', |
| | | }, |
| | | { |
| | | field: 'clientCode', |
| | | title: '客æ·ç¼ç ', |
| | | width: 120, |
| | | }, |
| | | { |
| | | field: 'clientName', |
| | | title: '客æ·åç§°', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'requestDate', |
| | | title: 'éæ±æ¥æ', |
| | | width: 120, |
| | | formatter: 'formatDate', |
| | | }, |
| | | { |
| | | field: 'issueDate', |
| | | title: 'ä¸åæ¥æ', |
| | | width: 120, |
| | | formatter: 'formatDate', |
| | | }, |
| | | { |
| | | field: 'workOrderCode', |
| | | title: 'ç产工åç¼ç ', |
| | | width: 180, |
| | | slots: { default: 'workOrderCode' }, |
| | | }, |
| | | { |
| | | field: 'mergeFlag', |
| | | title: 'åå¹¶ä¸å', |
| | | width: 100, |
| | | slots: { default: 'mergeFlag' }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 180, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 280, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesProMpsApi } from '#/api/mes/pro/mps'; |
| | | |
| | | import { ref } from 'vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { downloadFileFromBlobPart, isEmpty } from '@vben/utils'; |
| | | |
| | | import { Button, message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | cancelMps, |
| | | deleteMps, |
| | | exportMps, |
| | | getMpsPage, |
| | | issueMps, |
| | | mergeIssueMps, |
| | | } from '#/api/mes/pro/mps'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { MpsStatusEnum, useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | /** MPS ç产主计åå表 */ |
| | | defineOptions({ name: 'MesProMps' }); |
| | | |
| | | const router = useRouter(); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const checkedIds = ref<number[]>([]); |
| | | const checkedRows = ref<MesProMpsApi.Mps[]>([]); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | checkedIds.value = []; |
| | | checkedRows.value = []; |
| | | } |
| | | |
| | | /** æ°å¢ MPS */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** 导åºè¡¨æ ¼ */ |
| | | async function handleExport() { |
| | | const data = await exportMps(await gridApi.formApi.getValues()); |
| | | downloadFileFromBlobPart({ fileName: 'ç产主计å.xls', source: data }); |
| | | } |
| | | |
| | | /** æ¥ç详æ
*/ |
| | | function handleDetail(row: MesProMpsApi.Mps) { |
| | | formModalApi.setData({ formType: 'detail', id: row.id }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾ */ |
| | | function handleEdit(row: MesProMpsApi.Mps) { |
| | | formModalApi.setData({ formType: 'edit', id: row.id }).open(); |
| | | } |
| | | |
| | | /** å é¤ */ |
| | | async function handleDelete(ids: number[]) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting'), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteMps(ids); |
| | | message.success($t('ui.actionMessage.deleteSuccess')); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** ä¸å MPS */ |
| | | async function handleIssue(row: MesProMpsApi.Mps) { |
| | | const hideLoading = message.loading({ |
| | | content: 'ä¸åä¸...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await issueMps(row.id!); |
| | | message.success('ä¸åæå'); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** åå¹¶ä¸å */ |
| | | async function handleMergeIssue() { |
| | | if (checkedIds.value.length < 2) { |
| | | message.warning('请è³å°éæ©2æ¡è®°å½'); |
| | | return; |
| | | } |
| | | // æ ¡éªï¼ç¸å产åãèç¨¿ç¶æ |
| | | const firstRow = checkedRows.value[0]; |
| | | const isSameProduct = checkedRows.value.every( |
| | | (row) => row.productId === firstRow.productId, |
| | | ); |
| | | if (!isSameProduct) { |
| | | message.error('åªè½éæ©ç¸å产åçMPSè¿è¡åå¹¶ä¸å'); |
| | | return; |
| | | } |
| | | const isAllDraft = checkedRows.value.every( |
| | | (row) => row.status === MpsStatusEnum.DRAFT, |
| | | ); |
| | | if (!isAllDraft) { |
| | | message.error('åªè½éæ©èç¨¿ç¶æçMPSè¿è¡åå¹¶ä¸å'); |
| | | return; |
| | | } |
| | | const hideLoading = message.loading({ |
| | | content: 'åå¹¶ä¸åä¸...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await mergeIssueMps(checkedIds.value); |
| | | message.success('åå¹¶ä¸åæå'); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** åæ¶ MPS */ |
| | | async function handleCancel(row: MesProMpsApi.Mps) { |
| | | const hideLoading = message.loading({ |
| | | content: 'åæ¶ä¸...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await cancelMps(row.id!); |
| | | message.success('åæ¶æå'); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** 跳转å°ç产工å */ |
| | | function handleGoToWorkOrder(row: MesProMpsApi.Mps) { |
| | | if (row.workOrderId) { |
| | | router.push({ |
| | | path: '/mes/pro/workorder', |
| | | query: { id: row.workOrderId }, |
| | | }); |
| | | } |
| | | } |
| | | |
| | | /** è¡éæ©åå */ |
| | | function handleRowCheckboxChange({ |
| | | records, |
| | | }: { |
| | | records: MesProMpsApi.Mps[]; |
| | | }) { |
| | | checkedIds.value = records.map((item) => item.id!); |
| | | checkedRows.value = records; |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getMpsPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<MesProMpsApi.Mps>, |
| | | gridEvents: { |
| | | checkboxAll: handleRowCheckboxChange, |
| | | checkboxChange: handleRowCheckboxChange, |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <Grid table-title="ç产主计å(MPS)å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['MPS']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['mes:pro-mps:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['mes:pro-mps:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | { |
| | | label: 'åå¹¶ä¸å', |
| | | type: 'primary', |
| | | disabled: isEmpty(checkedIds) || checkedIds.length < 2, |
| | | icon: ACTION_ICON.AUDIT, |
| | | auth: ['mes:pro-mps:merge-issue'], |
| | | popConfirm: { |
| | | title: '确认åå¹¶ä¸åéä¸çMPSåï¼', |
| | | confirm: handleMergeIssue, |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #code="{ row }"> |
| | | <Button type="link" @click="handleDetail(row)"> |
| | | {{ row.code }} |
| | | </Button> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <Tag v-if="row.status === 0" color="blue">è稿</Tag> |
| | | <Tag v-else-if="row.status === 1" color="success">å·²ä¸å</Tag> |
| | | <Tag v-else-if="row.status === 2" color="purple">已宿</Tag> |
| | | <Tag v-else-if="row.status === 3" color="red">已忶</Tag> |
| | | </template> |
| | | <template #workOrderCode="{ row }"> |
| | | <Button |
| | | v-if="row.workOrderCode" |
| | | type="link" |
| | | @click="handleGoToWorkOrder(row)" |
| | | > |
| | | {{ row.workOrderCode }} |
| | | </Button> |
| | | <span v-else>-</span> |
| | | </template> |
| | | <template #mergeFlag="{ row }"> |
| | | <Tag v-if="row.mergeFlag === 1" color="success">æ¯</Tag> |
| | | <Tag v-else color="default">å¦</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.detail'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | | auth: ['mes:pro-mps:query'], |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['mes:pro-mps:update'], |
| | | ifShow: () => row.status === MpsStatusEnum.DRAFT, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'ä¸å', |
| | | type: 'link', |
| | | icon: ACTION_ICON.AUDIT, |
| | | auth: ['mes:pro-mps:issue'], |
| | | ifShow: () => row.status === MpsStatusEnum.DRAFT, |
| | | popConfirm: { |
| | | title: `确认ä¸å${row.code}åï¼`, |
| | | confirm: handleIssue.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: 'åæ¶', |
| | | type: 'link', |
| | | danger: true, |
| | | icon: 'ant-design:stop-outlined', |
| | | auth: ['mes:pro-mps:cancel'], |
| | | ifShow: () => row.status === MpsStatusEnum.ISSUED, |
| | | popConfirm: { |
| | | title: `ç¡®è®¤åæ¶${row.code}åï¼`, |
| | | confirm: handleCancel.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mes:pro-mps:delete'], |
| | | ifShow: () => row.status === MpsStatusEnum.DRAFT, |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.code]), |
| | | confirm: handleDelete.bind(null, [row.id!]), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | |
| | | import type { MesProMpsApi } from '#/api/mes/pro/mps'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { Descriptions, message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createMps, getMps, updateMps } from '#/api/mes/pro/mps'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { MpsStatusEnum, useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<MesProMpsApi.Mps>(); |
| | | const formType = ref<FormType>('detail'); |
| | | |
| | | const isEditable = computed(() => ['create', 'edit'].includes(formType.value)); |
| | | |
| | | const getTitle = computed(() => { |
| | | switch (formType.value) { |
| | | case 'create': |
| | | return 'æ°å¢MPS'; |
| | | case 'edit': |
| | | return 'ç¼è¾MPS'; |
| | | default: |
| | | return 'MPS详æ
'; |
| | | } |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 110, |
| | | }, |
| | | layout: 'horizontal', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | wrapperClass: 'grid-cols-3', |
| | | }); |
| | | |
| | | /** éæ°æè½½ schema å¹¶æè¡¨åææ§å¶åºé¨ç¡®è®¤æé® */ |
| | | function applySchema() { |
| | | formApi.setState({ schema: useFormSchema(formType.value) }); |
| | | modalApi.setState({ showConfirmButton: isEditable.value }); |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (!isEditable.value) { |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | const data = (await formApi.getValues()) as MesProMpsApi.CreateMpsReq; |
| | | if (formData.value?.id) { |
| | | await updateMps({ ...data, id: formData.value.id }); |
| | | } else { |
| | | await createMps(data); |
| | | } |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | formData.value = undefined; |
| | | return; |
| | | } |
| | | // å è½½æ°æ® |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | applySchema(); |
| | | if (data?.id) { |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getMps(data.id); |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-3/5"> |
| | | <Form class="mx-4" /> |
| | | <!-- 详æ
模å¼é¢å¤å±ç¤ºåªè¯»ä¿¡æ¯ --> |
| | | <div v-if="formType === 'detail' && formData" class="mx-4"> |
| | | <Descriptions bordered :column="2"> |
| | | <Descriptions.Item label="MPSç¼ç "> |
| | | {{ formData.code }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="ç¶æ"> |
| | | <Tag v-if="formData.status === 0" color="blue">è稿</Tag> |
| | | <Tag v-else-if="formData.status === 1" color="success">å·²ä¸å</Tag> |
| | | <Tag v-else-if="formData.status === 2" color="purple">已宿</Tag> |
| | | <Tag v-else-if="formData.status === 3" color="red">已忶</Tag> |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="éå®è®¢åå·"> |
| | | {{ formData.saleOrderNo }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="éå®è®¢åæç»ID"> |
| | | {{ formData.saleOrderItemId }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="产åç¼ç "> |
| | | {{ formData.productCode }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="产ååç§°"> |
| | | {{ formData.productName }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="è§æ ¼åå·"> |
| | | {{ formData.productSpecification }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="åä½"> |
| | | {{ formData.unitMeasureName }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="计åç产æ°é"> |
| | | {{ formData.quantity }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="å·²ç产æ°é"> |
| | | {{ formData.quantityProduced }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="客æ·ç¼ç "> |
| | | {{ formData.clientCode }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="客æ·åç§°"> |
| | | {{ formData.clientName }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="éæ±æ¥æ"> |
| | | {{ formData.requestDate ? new Date(formData.requestDate).toLocaleDateString() : '-' }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="ä¸åæ¥æ"> |
| | | {{ formData.issueDate ? new Date(formData.issueDate).toLocaleDateString() : '-' }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="å®ææ¥æ"> |
| | | {{ formData.finishDate ? new Date(formData.finishDate).toLocaleDateString() : '-' }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="ç产工åç¼ç "> |
| | | {{ formData.workOrderCode || '-' }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="æ¯å¦åå¹¶ä¸å"> |
| | | <Tag v-if="formData.mergeFlag === 1" color="success">æ¯</Tag> |
| | | <Tag v-else color="default">å¦</Tag> |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="åå¹¶æ¹æ¬¡å·"> |
| | | {{ formData.mergeBatchNo || '-' }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="夿³¨" :span="2"> |
| | | {{ formData.remark || '-' }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="å建æ¶é´" :span="2"> |
| | | {{ formData.createTime ? new Date(formData.createTime).toLocaleString() : '-' }} |
| | | </Descriptions.Item> |
| | | </Descriptions> |
| | | </div> |
| | | </Modal> |
| | | </template> |