| | |
| | | otherPrice?: number; // å
¶ä»è´¹ç¨ |
| | | totalProductPrice?: number; // å计ååéé¢ |
| | | taxPrice?: number; // å计ç¨é¢ |
| | | inspectionStatus?: number; // éªæ¶ç¶æï¼0:å¾
éªæ¶ 1:å·²éªæ¶ 2:éªæ¶ä¸åæ ¼ï¼ |
| | | inspectionTime?: string; // éªæ¶æ¶é´ |
| | | inspectorId?: number; // éªæ¶äººåç¼å· |
| | | inspectionResult?: string; // éªæ¶ç»æ |
| | | items?: PurchaseInItem[]; // éè´å
¥åºæç» |
| | | } |
| | | |
| | |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | /** éªæ¶éè´å
¥åº */ |
| | | export function inspectPurchaseIn(data: { |
| | | id: number; |
| | | inspectionStatus: number; |
| | | inspectionResult?: string; |
| | | }) { |
| | | return requestClient.put('/erp/purchase-in/inspect', data); |
| | | } |
| | |
| | | export function exportPurchaseOrder(params: any) { |
| | | return requestClient.download('/erp/purchase-order/export-excel', { params }); |
| | | } |
| | | |
| | | /** è·åéè´å®¡æ ¸æµç¨å表 */ |
| | | export function getPurchaseApproveProcessList() { |
| | | return requestClient.get<any[]>('/erp/purchase-order/approve-process-list'); |
| | | } |
| | | |
| | | /** è·åéè´å®¡æ ¸æµç¨å表ååº */ |
| | | export interface ProcessDefinition { |
| | | id: string; // æµç¨å®ä¹ID |
| | | key: string; // æµç¨Key |
| | | name: string; // æµç¨åç§° |
| | | version: number; // çæ¬å· |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '../../../../packages/effects/request/src'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace ErpPurchaseRequestApi { |
| | | /** éè´ç³è¯·ä¿¡æ¯ */ |
| | | export interface PurchaseRequest { |
| | | id?: number; // ç¼å· |
| | | no?: string; // ç³è¯·ç¼å· |
| | | supplierId?: number; // ä¾åºåç¼å· |
| | | supplierName?: string; // ä¾åºååç§° |
| | | requestTime: string; // ç³è¯·æ¶é´ |
| | | requesterId?: number; // ç³è¯·äººç¼å· |
| | | requesterName?: string; // ç³è¯·äººåç§° |
| | | reason?: string; // ç³è¯·çç± |
| | | estimatedAmount?: number; // é¢ä¼°éé¢ |
| | | status?: number; // ç¶æ |
| | | remark?: string; // 夿³¨ |
| | | items?: PurchaseRequestItem[]; // ç³è¯·æç» |
| | | createTime?: string; // å建æ¶é´ |
| | | } |
| | | |
| | | /** éè´ç³è¯·æç» */ |
| | | export interface PurchaseRequestItem { |
| | | id?: number; // ç¼å· |
| | | productId: number; // 产åç¼å· |
| | | productName?: string; // 产ååç§° |
| | | productUnitName?: string; // 产ååä½ |
| | | productBarCode?: string; // äº§åæ¡ç |
| | | requestCount: number; // ç³è¯·æ°é |
| | | estimatedPrice?: number; // é¢ä¼°åä»· |
| | | requiredDate?: string; // éæ±æ¥æ |
| | | remark?: string; // 夿³¨ |
| | | } |
| | | } |
| | | |
| | | /** å建éè´ç³è¯· */ |
| | | export function createPurchaseRequest(data: ErpPurchaseRequestApi.PurchaseRequest) { |
| | | return requestClient.post('/erp/purchase-request/create', data); |
| | | } |
| | | |
| | | /** æ´æ°éè´ç³è¯· */ |
| | | export function updatePurchaseRequest(data: ErpPurchaseRequestApi.PurchaseRequest) { |
| | | return requestClient.put('/erp/purchase-request/update', data); |
| | | } |
| | | |
| | | /** 审æ¹éè´ç³è¯· */ |
| | | export function approvePurchaseRequest(params: { |
| | | id: number; |
| | | status: number; |
| | | remark?: string; |
| | | }) { |
| | | return requestClient.put('/erp/purchase-request/approve', null, { params }); |
| | | } |
| | | |
| | | /** 转为éè´è®¢å */ |
| | | export function convertToPurchaseOrder(id: number) { |
| | | return requestClient.put('/erp/purchase-request/convert', null, { params: { id } }); |
| | | } |
| | | |
| | | /** å é¤éè´ç³è¯· */ |
| | | export function deletePurchaseRequest(ids: number[]) { |
| | | return requestClient.delete('/erp/purchase-request/delete', { params: { ids: ids.join(',') } }); |
| | | } |
| | | |
| | | /** è·åéè´ç³è¯·è¯¦æ
*/ |
| | | export function getPurchaseRequest(id: number) { |
| | | return requestClient.get<ErpPurchaseRequestApi.PurchaseRequest>( |
| | | `/erp/purchase-request/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** è·åéè´ç³è¯·å页 */ |
| | | export function getPurchaseRequestPage(params: PageParam) { |
| | | return requestClient.get<PageResult<ErpPurchaseRequestApi.PurchaseRequest>>( |
| | | '/erp/purchase-request/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** 导åºéè´ç³è¯· Excel */ |
| | | export function exportPurchaseRequest(params: any) { |
| | | return requestClient.download('/erp/purchase-request/export-excel', { params }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '../../../../packages/effects/request/src'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace ErpPurchaseStatisticsApi { |
| | | /** éè´æ±æ»ç»è®¡ */ |
| | | export interface Summary { |
| | | totalOrderCount?: number; // æ»è®¢åæ° |
| | | totalOrderAmount?: number; // æ»è®¢åéé¢ |
| | | totalInCount?: number; // æ»å
¥åºæ° |
| | | totalInAmount?: number; // æ»å
¥åºéé¢ |
| | | totalReturnCount?: number; // æ»éè´§æ° |
| | | totalReturnAmount?: number; // æ»éè´§éé¢ |
| | | } |
| | | |
| | | /** ä¾åºåä¾è´§ç»è®¡ */ |
| | | export interface SupplierStatistics { |
| | | supplierId?: number; // ä¾åºåç¼å· |
| | | supplierName?: string; // ä¾åºååç§° |
| | | orderCount?: number; // è®¢åæ° |
| | | orderAmount?: number; // 订åéé¢ |
| | | inCount?: number; // å
¥åºæ° |
| | | inAmount?: number; // å
¥åºéé¢ |
| | | returnCount?: number; // éè´§æ° |
| | | returnAmount?: number; // éè´§éé¢ |
| | | } |
| | | |
| | | /** éè´ä»·æ ¼åæ */ |
| | | export interface PriceAnalysis { |
| | | productId?: number; // 产åç¼å· |
| | | productName?: string; // 产ååç§° |
| | | productBarCode?: string; // äº§åæ¡ç |
| | | avgPrice?: number; // å¹³åä»·æ ¼ |
| | | minPrice?: number; // æä½ä»·æ ¼ |
| | | maxPrice?: number; // æé«ä»·æ ¼ |
| | | latestPrice?: number; // æè¿ä»·æ ¼ |
| | | priceChangeRate?: number; // ä»·æ ¼ååç |
| | | } |
| | | |
| | | /** æªå®æè®¢åè·è¸ª */ |
| | | export interface UncompletedOrder { |
| | | orderId?: number; // 订åç¼å· |
| | | orderNo?: string; // 订åå· |
| | | supplierName?: string; // ä¾åºååç§° |
| | | orderTime?: string; // è®¢åæ¶é´ |
| | | productNames?: string; // 产ååç§° |
| | | orderAmount?: number; // 订åéé¢ |
| | | inAmount?: number; // å
¥åºéé¢ |
| | | uncompletedAmount?: number; // æªå®æéé¢ |
| | | daysOverdue?: number; // è¶
æå¤©æ° |
| | | } |
| | | } |
| | | |
| | | /** éè´æ±æ»ç»è®¡ */ |
| | | export function getPurchaseSummary(params?: any) { |
| | | return requestClient.get<ErpPurchaseStatisticsApi.Summary>( |
| | | '/erp/purchase-statistics/summary', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** ä¾åºåä¾è´§ç»è®¡ */ |
| | | export function getSupplierStatistics(params: PageParam) { |
| | | return requestClient.get<PageResult<ErpPurchaseStatisticsApi.SupplierStatistics>>( |
| | | '/erp/purchase-statistics/supplier', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** éè´ä»·æ ¼åæ */ |
| | | export function getPriceAnalysis(params: PageParam) { |
| | | return requestClient.get<PageResult<ErpPurchaseStatisticsApi.PriceAnalysis>>( |
| | | '/erp/purchase-statistics/price', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æªå®æè®¢åè·è¸ª */ |
| | | export function getUncompletedOrders(params: PageParam) { |
| | | return requestClient.get<PageResult<ErpPurchaseStatisticsApi.UncompletedOrder>>( |
| | | '/erp/purchase-statistics/uncompleted', |
| | | { params }, |
| | | ); |
| | | } |
| | |
| | | export interface Supplier { |
| | | id?: number; // ä¾åºåç¼å· |
| | | name: string; // ä¾åºååç§° |
| | | creditCode?: string; // ç»ä¸ç¤¾ä¼ä¿¡ç¨ä»£ç |
| | | contact: string; // è系人 |
| | | mobile: string; // ææºå·ç |
| | | telephone: string; // èç³»çµè¯ |
| | | phone?: string; // èç³»çµè¯ï¼å¤ç¨ï¼ |
| | | email: string; // çµåé®ç®± |
| | | fax: string; // ä¼ ç |
| | | address?: string; // å°å |
| | | remark: string; // 夿³¨ |
| | | status: number; // å¼å¯ç¶æ |
| | | sort: number; // æåº |
| | | taxNo: string; // 纳ç¨äººè¯å«å· |
| | | taxPercent: number; // ç¨ç |
| | | bankName: string; // 弿·è¡ |
| | | bankAccount: string; // 弿·è´¦å· |
| | | bankName: string; // 弿·é¶è¡ |
| | | bankAccount: string; // é¶è¡è´¦å· |
| | | bankAddress: string; // 弿·å°å |
| | | qualification?: string; // èµè´¨ä¿¡æ¯ |
| | | cooperationTime?: string; // åä½å¼å§æ¶é´ |
| | | } |
| | | } |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '#/packages/effects/request/src'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace MdmUnitApi { |
| | | /** 计éåä½ä¿¡æ¯ */ |
| | | export interface Unit { |
| | | id?: number; // åä½ç¼å· |
| | | code: string; // åä½ç¼ç |
| | | name: string; // åä½åç§° |
| | | primaryFlag?: boolean; // æ¯å¦ä¸»åä½ |
| | | primaryId?: number; // 主åä½ç¼å· |
| | | changeRate?: number; // ä¸ä¸»å使¢ç®æ¯ä¾ |
| | | status: number; // ç¶æï¼0:å¯ç¨, 1:ç¦ç¨ï¼ |
| | | remark?: string; // 夿³¨ |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢è®¡éåä½å页 */ |
| | | export function getUnitPage(params: PageParam) { |
| | | return requestClient.get<PageResult<MdmUnitApi.Unit>>('/mdm/unit/page', { |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | /** æ¥è¯¢è®¡éåä½åè¡¨ï¼æ ¹æ®IDï¼ */ |
| | | export function getUnitListByIds(ids: string) { |
| | | return requestClient.get<MdmUnitApi.Unit[]>(`/mdm/unit/list-by-ids?ids=${ids}`); |
| | | } |
| | | |
| | | /** æ¥è¯¢è®¡éåä½è¯¦æ
*/ |
| | | export function getUnit(id: number) { |
| | | return requestClient.get<MdmUnitApi.Unit>(`/mdm/unit/get?id=${id}`); |
| | | } |
| | | |
| | | /** æ°å¢è®¡éåä½ */ |
| | | export function createUnit(data: MdmUnitApi.Unit) { |
| | | return requestClient.post('/mdm/unit/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹è®¡éåä½ */ |
| | | export function updateUnit(data: MdmUnitApi.Unit) { |
| | | return requestClient.put('/mdm/unit/update', data); |
| | | } |
| | | |
| | | /** å é¤è®¡éåä½ */ |
| | | export function deleteUnit(id: number) { |
| | | return requestClient.delete(`/mdm/unit/delete?id=${id}`); |
| | | } |
| | |
| | | import { getDictOptions } from '#/packages/effects/hooks/src'; |
| | | |
| | | import { z } from '#/adapter/form'; |
| | | import { getUnitPage } from '#/api/mdm/unit'; |
| | | |
| | | /** ç©æç±»åé项 */ |
| | | export const ITEM_TYPE_OPTIONS = [ |
| | |
| | | { label: 'æå', value: 3 }, |
| | | { label: 'è¾
æ', value: 4 }, |
| | | ]; |
| | | |
| | | /** ç©æç±»åæ å° */ |
| | | export const ITEM_TYPE_MAP: Record<number, string> = { |
| | | 1: 'åæ', |
| | | 2: 'åæå', |
| | | 3: 'æå', |
| | | 4: 'è¾
æ', |
| | | }; |
| | | |
| | | /** ç©æç±»åé¢è²æ å° */ |
| | | export const ITEM_TYPE_COLOR_MAP: Record<number, string> = { |
| | | 1: 'green', |
| | | 2: 'orange', |
| | | 3: 'blue', |
| | | 4: 'purple', |
| | | }; |
| | | |
| | | /** æ°å¢/ä¿®æ¹ç表å */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | component: 'RadioGroup', |
| | | fieldName: 'itemType', |
| | | label: 'ç©æç±»å', |
| | | componentProps: { |
| | | options: ITEM_TYPE_OPTIONS, |
| | | buttonStyle: 'solid', |
| | | optionType: 'button', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | component: 'ApiSelect', |
| | | fieldName: 'unitMeasureId', |
| | | label: '计éåä½', |
| | | rules: 'required', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©è®¡éåä½', |
| | | allowClear: true, |
| | | api: async () => { |
| | | const res = await getUnitPage({ pageNo: 1, pageSize: 100, status: 0 }); |
| | | return res.list || []; |
| | | }, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | }, |
| | | }, |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'barCode', |
| | | label: 'æ¡ç ', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ¡ç ', |
| | | }, |
| | | }, |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'specification', |
| | | label: 'è§æ ¼åå·', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è§æ ¼åå·', |
| | | }, |
| | | }, |
| | | { |
| | | component: 'TreeSelect', |
| | | fieldName: 'categoryId', |
| | | label: 'ç©æåç±»', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ç©æåç±»', |
| | | allowClear: true, |
| | | treeData: [], |
| | | fieldNames: { label: 'name', value: 'id', children: 'children' }, |
| | | }, |
| | | }, |
| | | { |
| | | component: 'Select', |
| | | fieldName: 'unitMeasureId', |
| | | label: '计éåä½', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©è®¡éåä½', |
| | | allowClear: true, |
| | | options: [], |
| | | fieldNames: { label: 'name', value: 'id' }, |
| | | }, |
| | | }, |
| | | { |
| | | component: 'Select', |
| | | fieldName: 'brandId', |
| | | label: 'åç', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©åç', |
| | | allowClear: true, |
| | | options: [], |
| | | fieldNames: { label: 'name', value: 'id' }, |
| | | }, |
| | | }, |
| | | { |
| | | component: 'Select', |
| | | fieldName: 'itemType', |
| | | label: 'ç©æç±»å', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ç©æç±»å', |
| | | options: ITEM_TYPE_OPTIONS, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | component: 'InputNumber', |
| | |
| | | checkedChildren: 'æ¯', |
| | | unCheckedChildren: 'å¦', |
| | | }, |
| | | rules: z.boolean().default(false), |
| | | rules: z.boolean().default(true), |
| | | }, |
| | | { |
| | | component: 'InputNumber', |
| | |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'categoryId', |
| | | label: 'ç©æåç±»', |
| | | component: 'TreeSelect', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ç©æåç±»', |
| | | allowClear: true, |
| | | treeData: [], |
| | | fieldNames: { label: 'name', value: 'id', children: 'children' }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'itemType', |
| | | label: 'ç©æç±»å', |
| | | component: 'Select', |
| | |
| | | minWidth: 150, |
| | | }, |
| | | { |
| | | field: 'categoryName', |
| | | title: 'åç±»', |
| | | minWidth: 100, |
| | | }, |
| | | { |
| | | field: 'unitMeasureName', |
| | | title: 'åä½', |
| | | minWidth: 80, |
| | |
| | | field: 'itemType', |
| | | title: 'ç©æç±»å', |
| | | minWidth: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MDM_ITEM_TYPE }, |
| | | }, |
| | | slots: { default: 'itemType' }, |
| | | }, |
| | | { |
| | | field: 'purchasePrice', |
| | |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| | | } |
| | |
| | | } from '#/api/mdm/item'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import { useGridColumns, useGridFormSchema, ITEM_TYPE_MAP, ITEM_TYPE_COLOR_MAP } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | defineOptions({ name: 'MdmItem' }); |
| | |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #itemType="{ row }"> |
| | | <Tag v-if="row.itemType === 1" color="green">åæ</Tag> |
| | | <Tag v-else-if="row.itemType === 2" color="orange">åæå</Tag> |
| | | <Tag v-else-if="row.itemType === 3" color="blue">æå</Tag> |
| | | <Tag v-else-if="row.itemType === 4" color="purple">è¾
æ</Tag> |
| | | </template> |
| | | <template #isBatchManaged="{ row }"> |
| | | <Tag v-if="row.isBatchManaged" color="success">æ¯</Tag> |
| | | <Tag v-else>å¦</Tag> |
| | |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['mdm:item:update'], |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: row.status === 0 ? 'åç¨' : 'å¯ç¨', |
| | | type: 'link', |
| | | auth: ['mdm:item:update'], |
| | | onClick: handleStatusChange.bind(null, row, row.status === 0), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | |
| | | import { CommonStatusEnum, DICT_TYPE } from '#/packages/constants/src'; |
| | | import { getDictOptions } from '#/packages/effects/hooks/src'; |
| | | |
| | | import { z } from '#/adapter/form'; |
| | | import { getUnitPage } from '#/api/mdm/unit'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹ç表å */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'id', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'code', |
| | | label: 'åä½ç¼ç ', |
| | | rules: 'required', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥åä½ç¼ç ', |
| | | }, |
| | | }, |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'name', |
| | | label: 'åä½åç§°', |
| | | rules: 'required', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥åä½åç§°', |
| | | }, |
| | | }, |
| | | { |
| | | component: 'Switch', |
| | | fieldName: 'primaryFlag', |
| | | label: 'æ¯å¦ä¸»åä½', |
| | | componentProps: { |
| | | class: 'w-auto', |
| | | checkedChildren: 'æ¯', |
| | | unCheckedChildren: 'å¦', |
| | | }, |
| | | rules: z.boolean().default(false), |
| | | }, |
| | | { |
| | | component: 'ApiSelect', |
| | | fieldName: 'primaryId', |
| | | label: '主åä½', |
| | | dependencies: { |
| | | triggerFields: ['primaryFlag'], |
| | | show: (values) => { |
| | | return values.primaryFlag === false; |
| | | }, |
| | | required: (values) => { |
| | | return values.primaryFlag === false; |
| | | }, |
| | | }, |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ä¸»åä½', |
| | | allowClear: true, |
| | | api: async () => { |
| | | const res = await getUnitPage({ pageNo: 1, pageSize: 100, primaryFlag: true, status: 0 }); |
| | | return res.list || []; |
| | | }, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | }, |
| | | }, |
| | | { |
| | | component: 'InputNumber', |
| | | fieldName: 'changeRate', |
| | | label: 'æ¢ç®æ¯ä¾', |
| | | dependencies: { |
| | | triggerFields: ['primaryFlag'], |
| | | show: (values) => { |
| | | return values.primaryFlag === false; |
| | | }, |
| | | required: (values) => { |
| | | return values.primaryFlag === false; |
| | | }, |
| | | }, |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 4, |
| | | placeholder: '请è¾å
¥æ¢ç®æ¯ä¾', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'RadioGroup', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), |
| | | buttonStyle: 'solid', |
| | | optionType: 'button', |
| | | }, |
| | | rules: z.number().default(CommonStatusEnum.ENABLE), |
| | | }, |
| | | { |
| | | component: 'Textarea', |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 3, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'code', |
| | | label: 'åä½ç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥åä½ç¼ç ', |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'åä½åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥åä½åç§°', |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions['columns'] { |
| | | return [ |
| | | { type: 'checkbox', width: 40 }, |
| | | { |
| | | field: 'code', |
| | | title: 'åä½ç¼ç ', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'name', |
| | | title: 'åä½åç§°', |
| | | minWidth: 150, |
| | | }, |
| | | { |
| | | field: 'primaryFlag', |
| | | title: '主åä½', |
| | | minWidth: 80, |
| | | slots: { default: 'primaryFlag' }, |
| | | }, |
| | | { |
| | | field: 'changeRate', |
| | | title: 'æ¢ç®æ¯ä¾', |
| | | minWidth: 100, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | minWidth: 80, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.COMMON_STATUS }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | minWidth: 180, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 130, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MdmUnitApi } from '#/api/mdm/unit'; |
| | | |
| | | import { Page, useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deleteUnit, |
| | | getUnitPage, |
| | | } from '#/api/mdm/unit'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | defineOptions({ name: 'MdmUnit' }); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** å建计éåä½ */ |
| | | function handleCreate() { |
| | | formModalApi.setData(null).open(); |
| | | } |
| | | |
| | | /** ç¼è¾è®¡éåä½ */ |
| | | function handleEdit(row: MdmUnitApi.Unit) { |
| | | formModalApi.setData(row).open(); |
| | | } |
| | | |
| | | /** å é¤è®¡éåä½ */ |
| | | async function handleDelete(row: MdmUnitApi.Unit) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', [row.name]), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteUnit(row.id as number); |
| | | message.success($t('ui.actionMessage.deleteSuccess', [row.name])); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getUnitPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<MdmUnitApi.Unit>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <Grid table-title="计éåä½å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['计éåä½']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['mdm:unit:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #primaryFlag="{ row }"> |
| | | <Tag v-if="row.primaryFlag" color="success">æ¯</Tag> |
| | | <Tag v-else>å¦</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['mdm:unit:update'], |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mdm:unit:delete'], |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.name]), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { MdmUnitApi } from '#/api/mdm/unit'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createUnit, |
| | | getUnit, |
| | | updateUnit, |
| | | } from '#/api/mdm/unit'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<MdmUnitApi.Unit>(); |
| | | const getTitle = computed(() => { |
| | | return formData.value?.id |
| | | ? $t('ui.actionTitle.edit', ['计éåä½']) |
| | | : $t('ui.actionTitle.create', ['计éåä½']); |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 100, |
| | | }, |
| | | layout: 'horizontal', |
| | | schema: useFormSchema(), |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = (await formApi.getValues()) as MdmUnitApi.Unit; |
| | | try { |
| | | await (formData.value?.id |
| | | ? updateUnit(data) |
| | | : createUnit(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<MdmUnitApi.Unit>(); |
| | | if (!data || !data.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getUnit(data.id); |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal class="w-[500px]" :title="getTitle"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| | |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'inspectionStatus', |
| | | title: 'éªæ¶ç¶æ', |
| | | minWidth: 120, |
| | | slots: { default: 'inspectionStatus' }, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: '审æ¹ç¶æ', |
| | | minWidth: 120, |
| | |
| | | import { Page, useVbenModal } from '../../../../packages/effects/common-ui/src'; |
| | | import { downloadFileFromBlobPart, isEmpty } from '../../../../packages/utils/src'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deletePurchaseIn, |
| | | exportPurchaseIn, |
| | | getPurchaseInPage, |
| | | inspectPurchaseIn, |
| | | updatePurchaseInStatus, |
| | | } from '#/api/erp/purchase/in'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | import InspectionForm from './modules/inspection-form.vue'; |
| | | |
| | | /** ERP éè´å
¥åºå表 */ |
| | | defineOptions({ name: 'ErpPurchaseIn' }); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [InspectionModal, inspectionModalApi] = useVbenModal({ |
| | | connectedComponent: InspectionForm, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | /** éªæ¶æä½ */ |
| | | function handleInspection(row: ErpPurchaseInApi.PurchaseIn) { |
| | | inspectionModalApi.setData({ id: row.id }).open(); |
| | | } |
| | | |
| | | const checkedIds = ref<number[]>([]); |
| | | function handleRowCheckboxChange({ |
| | | records, |
| | |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height><FormModal @success="handleRefresh" /> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <InspectionModal @success="handleRefresh" /> |
| | | <Grid table-title="éè´å
¥åºå表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #inspectionStatus="{ row }"> |
| | | <Tag v-if="row.inspectionStatus === 0" color="warning">å¾
éªæ¶</Tag> |
| | | <Tag v-else-if="row.inspectionStatus === 1" color="success">å·²éªæ¶</Tag> |
| | | <Tag v-else-if="row.inspectionStatus === 2" color="error">éªæ¶ä¸åæ ¼</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'éªæ¶', |
| | | type: 'link', |
| | | auth: ['erp:purchase-in:inspect'], |
| | | ifShow: () => row.inspectionStatus === 0 && row.status === 20, |
| | | onClick: handleInspection.bind(null, row), |
| | | }, |
| | | { |
| | | label: row.status === 10 ? '审æ¹' : 'å审æ¹', |
| | | type: 'link', |
| | | icon: ACTION_ICON.AUDIT, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import { ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '../../../../../packages/effects/common-ui/src'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { inspectPurchaseIn } from '#/api/erp/purchase/in'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const purchaseInId = ref<number>(); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 100, |
| | | }, |
| | | wrapperClass: 'grid-cols-1', |
| | | layout: 'horizontal', |
| | | schema: [ |
| | | { |
| | | fieldName: 'inspectionStatus', |
| | | label: 'éªæ¶ç¶æ', |
| | | component: 'RadioGroup', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'éªæ¶åæ ¼', value: 1 }, |
| | | { label: 'éªæ¶ä¸åæ ¼', value: 2 }, |
| | | ], |
| | | buttonStyle: 'solid', |
| | | optionType: 'button', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'inspectionResult', |
| | | label: 'éªæ¶ç»æ', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥éªæ¶ç»æ', |
| | | rows: 3, |
| | | }, |
| | | }, |
| | | ], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | if (!purchaseInId.value) { |
| | | message.error('å
¥åºåIDä¸åå¨'); |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = await formApi.getValues(); |
| | | try { |
| | | await inspectPurchaseIn({ |
| | | id: purchaseInId.value, |
| | | inspectionStatus: data.inspectionStatus, |
| | | inspectionResult: data.inspectionResult, |
| | | }); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('éªæ¶æå'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | purchaseInId.value = undefined; |
| | | await formApi.resetForm(); |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ id: number }>(); |
| | | if (!data || !data.id) { |
| | | return; |
| | | } |
| | | purchaseInId.value = data.id; |
| | | // é»è®¤éæ©éªæ¶åæ ¼ |
| | | await formApi.setValues({ inspectionStatus: 1 }); |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="éè´å
¥åºéªæ¶" class="w-1/3"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| | |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | placeholder: '请è¾å
¥æ¯ä»è®¢é', |
| | | precision: 2, |
| | | min: 0, |
| | | }, |
| | | fieldName: 'depositPrice', |
| | | label: 'æ¯ä»è®¢é', |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | }, |
| | | }, |
| | | { |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | placeholder: '请è¾å
¥æ¯ä»è®¢é', |
| | | precision: 2, |
| | | min: 0, |
| | | }, |
| | | fieldName: 'depositPrice', |
| | | label: 'æ¯ä»è®¢é', |
| | | rules: z.number().min(0).optional(), |
| | | }, |
| | | ]; |
| | | } |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | |
| | | import type { ErpPurchaseOrderApi } from '#/api/erp/purchase/order'; |
| | | import type { ErpPurchaseOrderApi, ProcessDefinition } from '#/api/erp/purchase/order'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | |
| | | import { getAccountSimpleList } from '#/api/erp/finance/account'; |
| | | import { |
| | | createPurchaseOrder, |
| | | getPurchaseApproveProcessList, |
| | | getPurchaseOrder, |
| | | updatePurchaseOrder, |
| | | } from '#/api/erp/purchase/order'; |
| | |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | import PurchaseOrderItemForm from './item-form.vue'; |
| | | import ProcessSelectModal from './process-select-modal.vue'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<ErpPurchaseOrderApi.PurchaseOrder>(); |
| | | const formType = ref<FormType>('create'); // 表åç±»åï¼'create' | 'edit' | 'detail' |
| | | const formType = ref<FormType>('create'); |
| | | const itemFormRef = ref<InstanceType<typeof PurchaseOrderItemForm>>(); |
| | | const selectedProcessId = ref<string>(''); |
| | | |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'create') { |
| | |
| | | schema: useFormSchema(formType.value), |
| | | showDefaultActions: false, |
| | | handleValuesChange: (values, changedFields) => { |
| | | // ç®çï¼åæ¥å° item-form ç»ä»¶ï¼è§¦åæ´ä½çä»·æ ¼è®¡ç® |
| | | if (formData.value && changedFields.includes('discountPercent')) { |
| | | formData.value.discountPercent = values.discountPercent; |
| | | } |
| | | }, |
| | | }); |
| | | |
| | | const [ProcessModal, processModalApi] = useVbenModal({ |
| | | connectedComponent: ProcessSelectModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** æ´æ°éè´è®¢å项 */ |
| | |
| | | }); |
| | | } |
| | | |
| | | /** æµç¨éæ©æå */ |
| | | function handleProcessSelectSuccess(processId: string) { |
| | | selectedProcessId.value = processId; |
| | | submitOrder(); |
| | | } |
| | | |
| | | /** æäº¤è®¢å */ |
| | | async function submitOrder() { |
| | | const data = (await formApi.getValues()) as ErpPurchaseOrderApi.PurchaseOrder; |
| | | data.items = formData.value?.items?.map((item) => ({ |
| | | ...item, |
| | | id: undefined, |
| | | })); |
| | | if (data.fileUrl && Array.isArray(data.fileUrl)) { |
| | | data.fileUrl = data.fileUrl.length > 0 ? data.fileUrl[0] : ''; |
| | | } |
| | | if (selectedProcessId.value) { |
| | | data.processDefinitionId = selectedProcessId.value; |
| | | } |
| | | await (formType.value === 'create' |
| | | ? createPurchaseOrder(data) |
| | | : updatePurchaseOrder(data)); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } |
| | | |
| | | /** åå»ºææ´æ°éè´è®¢å */ |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | |
| | | } |
| | | |
| | | modalApi.lock(); |
| | | // æäº¤è¡¨å |
| | | const data = |
| | | (await formApi.getValues()) as ErpPurchaseOrderApi.PurchaseOrder; |
| | | data.items = formData.value?.items?.map((item) => ({ |
| | | ...item, |
| | | // è§£å³æ°å¢éè´è®¢åæ¥é |
| | | id: undefined, |
| | | })); |
| | | // å°æä»¶æ°ç»è½¬æ¢ä¸ºå符串 |
| | | if (data.fileUrl && Array.isArray(data.fileUrl)) { |
| | | data.fileUrl = data.fileUrl.length > 0 ? data.fileUrl[0] : ''; |
| | | } |
| | | try { |
| | | await (formType.value === 'create' |
| | | ? createPurchaseOrder(data) |
| | | : updatePurchaseOrder(data)); |
| | | // å
³éå¹¶æç¤º |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | |
| | | if (formType.value === 'create') { |
| | | try { |
| | | const processes = await getPurchaseApproveProcessList(); |
| | | if (processes.length === 1) { |
| | | selectedProcessId.value = processes[0].id; |
| | | await submitOrder(); |
| | | } else if (processes.length > 1) { |
| | | processModalApi.setData(processes).open(); |
| | | } else { |
| | | message.error('éè´å®¡æ ¸åç±»ä¸æ²¡æå¯ç¨çæµç¨æ¨¡åï¼è¯·å
å建并å叿µç¨'); |
| | | } |
| | | } catch (error: any) { |
| | | message.error(error.message || 'è·åæµç¨å表失败'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } else { |
| | | try { |
| | | await submitOrder(); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | formData.value = undefined; |
| | | selectedProcessId.value = ''; |
| | | return; |
| | | } |
| | | // å è½½æ°æ® |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | formApi.setDisabled(formType.value === 'detail'); |
| | | formApi.updateSchema(useFormSchema(formType.value)); |
| | | if (!data || !data.id) { |
| | | // æ°å¢æ¶ï¼é»è®¤éä¸è´¦æ· |
| | | const accountList = await getAccountSimpleList(); |
| | | const defaultAccount = accountList.find((item) => item.defaultStatus); |
| | | if (defaultAccount) { |
| | |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getPurchaseOrder(data.id); |
| | | // è®¾ç½®å° values |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | |
| | | class="w-3/4" |
| | | :show-confirm-button="formType !== 'detail'" |
| | | > |
| | | <ProcessModal @success="handleProcessSelectSuccess" /> |
| | | <Form class="mx-3"> |
| | | <template #items> |
| | | <PurchaseOrderItemForm |
| | |
| | | <script lang="ts" setup> |
| | | import type { ErpProductApi } from '#/api/erp/product/product'; |
| | | import type { MdmItemApi } from '#/api/mdm/item'; |
| | | import type { ErpPurchaseOrderApi } from '#/api/erp/purchase/order'; |
| | | |
| | | import { computed, nextTick, onMounted, ref, watch } from 'vue'; |
| | |
| | | import { Input, InputNumber, Select } from 'ant-design-vue'; |
| | | |
| | | import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { getProductSimpleList } from '#/api/erp/product/product'; |
| | | import { getItemPage } from '#/api/mdm/item'; |
| | | import { getStockCount } from '#/api/erp/stock/stock'; |
| | | |
| | | import { useFormItemColumns } from '../data'; |
| | |
| | | ]); |
| | | |
| | | const tableData = ref<ErpPurchaseOrderApi.PurchaseOrderItem[]>([]); // è¡¨æ ¼æ°æ® |
| | | const productOptions = ref<ErpProductApi.Product[]>([]); // 产å䏿é项 |
| | | const productOptions = ref<MdmItemApi.Item[]>([]); // 产å䏿é项 |
| | | |
| | | /** è·åè¡¨æ ¼åè®¡æ°æ® */ |
| | | const summaries = computed(() => { |
| | |
| | | return; |
| | | } |
| | | row.productId = productId; |
| | | row.productUnitId = product.unitId; |
| | | row.productUnitId = product.unitMeasureId; |
| | | row.productBarCode = product.barCode; |
| | | row.productUnitName = product.unitName; |
| | | row.productUnitName = product.unitMeasureName; |
| | | row.productName = product.name; |
| | | row.stockCount = (await getStockCount(productId)) || 0; |
| | | row.productPrice = product.purchasePrice || 0; |
| | |
| | | |
| | | /** åå§å */ |
| | | onMounted(async () => { |
| | | productOptions.value = await getProductSimpleList(); |
| | | const res = await getItemPage({ pageNo: 1, pageSize: 100, status: 0 }); |
| | | productOptions.value = res.list || []; |
| | | // ç®çï¼æ°å¢æ¶ï¼é»è®¤æ·»å ä¸è¡ |
| | | if (tableData.value.length === 0) { |
| | | handleAdd(); |
| | |
| | | <Grid class="w-full"> |
| | | <template #productId="{ row }"> |
| | | <Select |
| | | v-if="!disabled" |
| | | v-model:value="row.productId" |
| | | :options="productOptions" |
| | | :field-names="{ label: 'name', value: 'id' }" |
| | |
| | | show-search |
| | | @change="handleProductChange($event, row)" |
| | | /> |
| | | <span v-else>{{ row.productName || '-' }}</span> |
| | | </template> |
| | | <template #count="{ row }"> |
| | | <InputNumber |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { ProcessDefinition } from '#/api/erp/purchase/order'; |
| | | |
| | | import { ref, computed } from 'vue'; |
| | | |
| | | import { useVbenModal } from '../../../../../packages/effects/common-ui/src'; |
| | | |
| | | import { Select } from 'ant-design-vue'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const processList = ref<ProcessDefinition[]>([]); |
| | | const selectedProcessId = ref<string>(''); |
| | | |
| | | const processOptions = computed(() => { |
| | | return processList.value.map((p) => ({ |
| | | label: `${p.name} (V${p.version})`, |
| | | value: p.id, |
| | | })); |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (!selectedProcessId.value) { |
| | | return; |
| | | } |
| | | await modalApi.close(); |
| | | emit('success', selectedProcessId.value); |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | selectedProcessId.value = ''; |
| | | processList.value = []; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<ProcessDefinition[]>(); |
| | | if (data && data.length > 0) { |
| | | processList.value = data; |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="éæ©å®¡æ¹æµç¨" class="w-1/3"> |
| | | <Select |
| | | v-model:value="selectedProcessId" |
| | | :options="processOptions" |
| | | style="width: 100%" |
| | | placeholder="è¯·éæ©å®¡æ¹æµç¨" |
| | | /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | |
| | | import { DICT_TYPE } from '../../../../packages/constants/src'; |
| | | import { getDictOptions } from '../../../../packages/effects/hooks/src'; |
| | | import { erpPriceInputFormatter } from '../../../../packages/utils/src'; |
| | | |
| | | import { getSupplierSimpleList } from '#/api/erp/purchase/supplier'; |
| | | import { getSimpleUserList } from '#/api/system/user'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'edit'; |
| | | |
| | | /** ç¶æå¸¸é */ |
| | | export const PURCHASE_REQUEST_STATUS = { |
| | | PENDING: 0, // å¾
å®¡æ¹ |
| | | APPROVED: 1, // å·²å®¡æ¹ |
| | | REJECTED: 2, // å·²æç» |
| | | CONVERTED: 3, // 已转订å |
| | | }; |
| | | |
| | | /** 表åçé
置项 */ |
| | | export function useFormSchema(formType: FormType): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'no', |
| | | label: 'ç³è¯·ç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'ç³»ç»èªå¨çæ', |
| | | disabled: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'requestTime', |
| | | label: 'ç³è¯·æ¶é´', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'éæ©ç³è¯·æ¶é´', |
| | | showTime: true, |
| | | format: 'YYYY-MM-DD HH:mm:ss', |
| | | valueFormat: 'x', |
| | | disabled: formType === 'detail', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | label: 'ä¾åºå', |
| | | fieldName: 'supplierId', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ä¾åºå', |
| | | allowClear: true, |
| | | showSearch: true, |
| | | api: getSupplierSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | disabled: formType === 'detail', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'reason', |
| | | label: 'ç³è¯·çç±', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ç³è¯·çç±', |
| | | autoSize: { minRows: 1, maxRows: 1 }, |
| | | disabled: formType === 'detail', |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | autoSize: { minRows: 1, maxRows: 1 }, |
| | | disabled: formType === 'detail', |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'items', |
| | | label: 'ç³è¯·æç»', |
| | | component: 'Input', |
| | | formItemClass: 'col-span-3', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** 表åçæç»è¡¨æ ¼å */ |
| | | export function useFormItemColumns( |
| | | disabled: boolean, |
| | | ): VxeTableGridOptions['columns'] { |
| | | return [ |
| | | { type: 'seq', title: 'åºå·', minWidth: 50, fixed: 'left' }, |
| | | { |
| | | field: 'productId', |
| | | title: '产ååç§°', |
| | | minWidth: 200, |
| | | slots: { default: 'productId' }, |
| | | }, |
| | | { |
| | | field: 'productBarCode', |
| | | title: 'æ¡ç ', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'productUnitName', |
| | | title: 'åä½', |
| | | minWidth: 80, |
| | | }, |
| | | { |
| | | field: 'requestCount', |
| | | title: 'ç³è¯·æ°é', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | slots: { default: 'requestCount' }, |
| | | }, |
| | | { |
| | | field: 'estimatedPrice', |
| | | title: 'é¢ä¼°åä»·', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | slots: { default: 'estimatedPrice' }, |
| | | }, |
| | | { |
| | | field: 'totalPrice', |
| | | title: 'é¢ä¼°éé¢', |
| | | minWidth: 120, |
| | | fixed: 'right', |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'requiredDate', |
| | | title: 'éæ±æ¥æ', |
| | | minWidth: 140, |
| | | fixed: 'right', |
| | | slots: { default: 'requiredDate' }, |
| | | }, |
| | | { |
| | | field: 'remark', |
| | | title: '夿³¨', |
| | | minWidth: 150, |
| | | slots: { default: 'remark' }, |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 50, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | visible: !disabled, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'no', |
| | | label: 'ç³è¯·ç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ç³è¯·ç¼å·', |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'supplierId', |
| | | label: 'ä¾åºå', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ä¾åºå', |
| | | allowClear: true, |
| | | showSearch: true, |
| | | api: getSupplierSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'requestTime', |
| | | label: 'ç³è¯·æ¶é´', |
| | | component: 'RangePicker', |
| | | componentProps: { |
| | | ...getRangePickerDefaultProps(), |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'requesterId', |
| | | label: 'ç³è¯·äºº', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ç³è¯·äºº', |
| | | allowClear: true, |
| | | showSearch: true, |
| | | api: getSimpleUserList, |
| | | labelField: 'nickname', |
| | | valueField: 'id', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'å¾
审æ¹', value: 0 }, |
| | | { label: '已审æ¹', value: 1 }, |
| | | { label: 'å·²æç»', value: 2 }, |
| | | { label: '已转订å', value: 3 }, |
| | | ], |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions['columns'] { |
| | | return [ |
| | | { |
| | | type: 'checkbox', |
| | | width: 50, |
| | | fixed: 'left', |
| | | }, |
| | | { |
| | | field: 'no', |
| | | title: 'ç³è¯·ç¼å·', |
| | | width: 200, |
| | | fixed: 'left', |
| | | }, |
| | | { |
| | | field: 'supplierName', |
| | | title: 'ä¾åºå', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'requestTime', |
| | | title: 'ç³è¯·æ¶é´', |
| | | width: 160, |
| | | formatter: 'formatDate', |
| | | }, |
| | | { |
| | | field: 'requesterName', |
| | | title: 'ç³è¯·äºº', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'estimatedAmount', |
| | | title: 'é¢ä¼°éé¢', |
| | | formatter: 'formatAmount2', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | minWidth: 120, |
| | | slots: { default: 'status' }, |
| | | }, |
| | | { |
| | | field: 'reason', |
| | | title: 'ç³è¯·çç±', |
| | | minWidth: 150, |
| | | showOverflow: 'tooltip', |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 160, |
| | | formatter: 'formatDate', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 200, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { ErpPurchaseRequestApi } from '#/api/erp/purchase/request'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { Page, useVbenModal } from '../../../../packages/effects/common-ui/src'; |
| | | import { downloadFileFromBlobPart, isEmpty } from '../../../../packages/utils/src'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | approvePurchaseRequest, |
| | | convertToPurchaseOrder, |
| | | deletePurchaseRequest, |
| | | exportPurchaseRequest, |
| | | getPurchaseRequestPage, |
| | | } from '#/api/erp/purchase/request'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema, PURCHASE_REQUEST_STATUS } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | /** ERP éè´ç³è¯·å表 */ |
| | | defineOptions({ name: 'ErpPurchaseRequest' }); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** 导åºè¡¨æ ¼ */ |
| | | async function handleExport() { |
| | | const data = await exportPurchaseRequest(await gridApi.formApi.getValues()); |
| | | downloadFileFromBlobPart({ fileName: 'éè´ç³è¯·.xls', source: data }); |
| | | } |
| | | |
| | | /** æ°å¢éè´ç³è¯· */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾éè´ç³è¯· */ |
| | | function handleEdit(row: ErpPurchaseRequestApi.PurchaseRequest) { |
| | | 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 deletePurchaseRequest(ids); |
| | | message.success($t('ui.actionMessage.deleteSuccess')); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** 审æ¹éè´ç³è¯· */ |
| | | async function handleApprove(row: ErpPurchaseRequestApi.PurchaseRequest, status: number) { |
| | | const hideLoading = message.loading({ |
| | | content: `ç¡®å®${status === 1 ? '审æ¹éè¿' : 'å®¡æ¹æç»'}该ç³è¯·åï¼`, |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await approvePurchaseRequest({ id: row.id!, status, remark: '' }); |
| | | message.success(`${status === 1 ? '审æ¹éè¿' : 'å®¡æ¹æç»'}æå`); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** 转为éè´è®¢å */ |
| | | async function handleConvert(row: ErpPurchaseRequestApi.PurchaseRequest) { |
| | | const hideLoading = message.loading({ |
| | | content: 'æ£å¨è½¬ä¸ºéè´è®¢å...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await convertToPurchaseOrder(row.id!); |
| | | message.success('å·²æå转为éè´è®¢å'); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | const checkedIds = ref<number[]>([]); |
| | | function handleRowCheckboxChange({ |
| | | records, |
| | | }: { |
| | | records: ErpPurchaseRequestApi.PurchaseRequest[]; |
| | | }) { |
| | | checkedIds.value = records.map((item) => item.id!); |
| | | } |
| | | |
| | | /** æ¥ç详æ
*/ |
| | | function handleDetail(row: ErpPurchaseRequestApi.PurchaseRequest) { |
| | | formModalApi.setData({ formType: 'detail', id: row.id }).open(); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getPurchaseRequestPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<ErpPurchaseRequestApi.PurchaseRequest>, |
| | | gridEvents: { |
| | | checkboxAll: handleRowCheckboxChange, |
| | | checkboxChange: handleRowCheckboxChange, |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <Grid table-title="éè´ç³è¯·å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['éè´ç³è¯·']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['erp:purchase-request:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['erp:purchase-request:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | { |
| | | label: 'æ¹éå é¤', |
| | | type: 'primary', |
| | | danger: true, |
| | | disabled: isEmpty(checkedIds), |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['erp:purchase-request:delete'], |
| | | popConfirm: { |
| | | title: `æ¯å¦å 餿é䏿°æ®ï¼`, |
| | | confirm: handleDelete.bind(null, checkedIds), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <Tag v-if="row.status === 0" color="warning">å¾
审æ¹</Tag> |
| | | <Tag v-else-if="row.status === 1" color="success">已审æ¹</Tag> |
| | | <Tag v-else-if="row.status === 2" color="error">å·²æç»</Tag> |
| | | <Tag v-else-if="row.status === 3" color="default">已转订å</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.detail'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | | auth: ['erp:purchase-request:query'], |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['erp:purchase-request:update'], |
| | | ifShow: () => row.status === 0, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: '审æ¹éè¿', |
| | | type: 'link', |
| | | auth: ['erp:purchase-request:approve'], |
| | | ifShow: () => row.status === 0, |
| | | popConfirm: { |
| | | title: '确认审æ¹éè¿è¯¥ç³è¯·åï¼', |
| | | confirm: handleApprove.bind(null, row, 1), |
| | | }, |
| | | }, |
| | | { |
| | | label: 'å®¡æ¹æç»', |
| | | type: 'link', |
| | | danger: true, |
| | | auth: ['erp:purchase-request:approve'], |
| | | ifShow: () => row.status === 0, |
| | | popConfirm: { |
| | | title: 'ç¡®è®¤å®¡æ¹æç»è¯¥ç³è¯·åï¼', |
| | | confirm: handleApprove.bind(null, row, 2), |
| | | }, |
| | | }, |
| | | { |
| | | label: '转订å', |
| | | type: 'link', |
| | | auth: ['erp:purchase-request:convert'], |
| | | ifShow: () => row.status === 1, |
| | | popConfirm: { |
| | | title: '确认转为éè´è®¢ååï¼', |
| | | confirm: handleConvert.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['erp:purchase-request:delete'], |
| | | ifShow: () => row.status === 0, |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.no]), |
| | | confirm: handleDelete.bind(null, [row.id!]), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | |
| | | import type { ErpPurchaseRequestApi } from '#/api/erp/purchase/request'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '../../../../../packages/effects/common-ui/src'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createPurchaseRequest, |
| | | getPurchaseRequest, |
| | | updatePurchaseRequest, |
| | | } from '#/api/erp/purchase/request'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema, PURCHASE_REQUEST_STATUS } from '../data'; |
| | | import PurchaseRequestItemForm from './item-form.vue'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<ErpPurchaseRequestApi.PurchaseRequest>(); |
| | | const formType = ref<FormType>('create'); |
| | | const itemFormRef = ref<InstanceType<typeof PurchaseRequestItemForm>>(); |
| | | |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'create') { |
| | | return $t('ui.actionTitle.create', ['éè´ç³è¯·']); |
| | | } else if (formType.value === 'edit') { |
| | | return $t('ui.actionTitle.edit', ['éè´ç³è¯·']); |
| | | } else { |
| | | return 'éè´ç³è¯·è¯¦æ
'; |
| | | } |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 120, |
| | | }, |
| | | wrapperClass: 'grid-cols-3', |
| | | layout: 'vertical', |
| | | schema: useFormSchema(formType.value), |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | /** æ´æ°éè´ç³è¯·é¡¹ */ |
| | | function handleUpdateItems(items: ErpPurchaseRequestApi.PurchaseRequestItem[]) { |
| | | formData.value = modalApi.getData<ErpPurchaseRequestApi.PurchaseRequest>(); |
| | | formData.value.items = items; |
| | | formApi.setValues({ |
| | | items, |
| | | }); |
| | | } |
| | | |
| | | /** åå»ºææ´æ°éè´ç³è¯· */ |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | const itemFormInstance = Array.isArray(itemFormRef.value) |
| | | ? itemFormRef.value[0] |
| | | : itemFormRef.value; |
| | | try { |
| | | itemFormInstance.validate(); |
| | | } catch (error: any) { |
| | | message.error(error.message || 'å表åéªè¯å¤±è´¥'); |
| | | return; |
| | | } |
| | | |
| | | modalApi.lock(); |
| | | const data = |
| | | (await formApi.getValues()) as ErpPurchaseRequestApi.PurchaseRequest; |
| | | data.items = formData.value?.items?.map((item) => ({ |
| | | ...item, |
| | | id: undefined, |
| | | })); |
| | | try { |
| | | await (formType.value === 'create' |
| | | ? createPurchaseRequest(data) |
| | | : updatePurchaseRequest(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; |
| | | formApi.setDisabled(formType.value === 'detail'); |
| | | formApi.updateSchema(useFormSchema(formType.value)); |
| | | if (!data || !data.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getPurchaseRequest(data.id); |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal |
| | | :title="getTitle" |
| | | class="w-3/4" |
| | | :show-confirm-button="formType !== 'detail'" |
| | | > |
| | | <Form class="mx-3"> |
| | | <template #items> |
| | | <PurchaseRequestItemForm |
| | | ref="itemFormRef" |
| | | :items="formData?.items ?? []" |
| | | :disabled="formType === 'detail'" |
| | | @update:items="handleUpdateItems" |
| | | /> |
| | | </template> |
| | | </Form> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { MdmItemApi } from '#/api/mdm/item'; |
| | | import type { ErpPurchaseRequestApi } from '#/api/erp/purchase/request'; |
| | | |
| | | import { computed, nextTick, onMounted, ref, watch } from 'vue'; |
| | | |
| | | import { |
| | | erpCountInputFormatter, |
| | | erpPriceInputFormatter, |
| | | erpPriceMultiply, |
| | | } from '../../../../../packages/utils/src'; |
| | | |
| | | import { Input, InputNumber, Select, DatePicker } from 'ant-design-vue'; |
| | | |
| | | import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { getItemPage } from '#/api/mdm/item'; |
| | | |
| | | import { useFormItemColumns } from '../data'; |
| | | |
| | | interface Props { |
| | | items?: ErpPurchaseRequestApi.PurchaseRequestItem[]; |
| | | disabled?: boolean; |
| | | } |
| | | |
| | | const props = withDefaults(defineProps<Props>(), { |
| | | items: () => [], |
| | | disabled: false, |
| | | }); |
| | | |
| | | const emit = defineEmits(['update:items']); |
| | | |
| | | const tableData = ref<ErpPurchaseRequestApi.PurchaseRequestItem[]>([]); |
| | | const productOptions = ref<MdmItemApi.Item[]>([]); |
| | | |
| | | /** è·åè¡¨æ ¼åè®¡æ°æ® */ |
| | | const summaries = computed(() => { |
| | | return { |
| | | count: tableData.value.reduce((sum, item) => sum + (item.requestCount || 0), 0), |
| | | totalPrice: tableData.value.reduce( |
| | | (sum, item) => { |
| | | const price = erpPriceMultiply(item.estimatedPrice || 0, item.requestCount || 0) ?? 0; |
| | | return sum + price; |
| | | }, |
| | | 0, |
| | | ), |
| | | }; |
| | | }); |
| | | |
| | | /** è¡¨æ ¼é
ç½® */ |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | gridOptions: { |
| | | columns: useFormItemColumns(props.disabled), |
| | | data: tableData.value, |
| | | minHeight: 250, |
| | | autoResize: true, |
| | | border: true, |
| | | rowConfig: { |
| | | keyField: 'seq', |
| | | isHover: true, |
| | | }, |
| | | pagerConfig: { |
| | | enabled: false, |
| | | }, |
| | | toolbarConfig: { |
| | | enabled: false, |
| | | }, |
| | | }, |
| | | }); |
| | | |
| | | /** çå¬å¤é¨ä¼ å
¥çåæ°æ® */ |
| | | watch( |
| | | () => props.items, |
| | | async (items) => { |
| | | if (!items) { |
| | | return; |
| | | } |
| | | items.forEach((item) => initRow(item)); |
| | | tableData.value = [...items]; |
| | | await nextTick(); |
| | | await gridApi.grid.reloadData(tableData.value); |
| | | }, |
| | | { |
| | | immediate: true, |
| | | }, |
| | | ); |
| | | |
| | | /** å¤çæ°å¢ */ |
| | | function handleAdd() { |
| | | const newRow = { |
| | | id: undefined, |
| | | productId: undefined, |
| | | productUnitName: undefined, |
| | | productBarCode: undefined, |
| | | requestCount: 1, |
| | | estimatedPrice: undefined, |
| | | requiredDate: undefined, |
| | | remark: undefined, |
| | | totalPrice: 0, |
| | | }; |
| | | tableData.value.push(newRow); |
| | | emit('update:items', [...tableData.value]); |
| | | } |
| | | |
| | | /** å¤çå é¤ */ |
| | | function handleDelete(row: ErpPurchaseRequestApi.PurchaseRequestItem) { |
| | | const index = tableData.value.findIndex((item) => item.seq === row.seq); |
| | | if (index !== -1) { |
| | | tableData.value.splice(index, 1); |
| | | } |
| | | emit('update:items', [...tableData.value]); |
| | | } |
| | | |
| | | /** å¤ç产ååæ´ */ |
| | | async function handleProductChange(productId: any, row: any) { |
| | | const product = productOptions.value.find((p) => p.id === productId); |
| | | if (!product) { |
| | | return; |
| | | } |
| | | row.productId = productId; |
| | | row.productBarCode = product.barCode; |
| | | row.productUnitName = product.unitMeasureName; |
| | | row.productName = product.name; |
| | | row.estimatedPrice = product.purchasePrice || 0; |
| | | row.requestCount = row.requestCount || 1; |
| | | handleRowChange(row); |
| | | } |
| | | |
| | | /** å¤çè¡æ°æ®åæ´ */ |
| | | function handleRowChange(row: any) { |
| | | const index = tableData.value.findIndex((item) => item.seq === row.seq); |
| | | if (index === -1) { |
| | | tableData.value.push(row); |
| | | } else { |
| | | tableData.value[index] = row; |
| | | } |
| | | // 计ç®é¢ä¼°éé¢ |
| | | if (row.estimatedPrice && row.requestCount) { |
| | | row.totalPrice = erpPriceMultiply(row.estimatedPrice, row.requestCount) ?? 0; |
| | | } |
| | | emit('update:items', [...tableData.value]); |
| | | } |
| | | |
| | | /** åå§åè¡æ°æ® */ |
| | | function initRow(row: ErpPurchaseRequestApi.PurchaseRequestItem) { |
| | | if (row.estimatedPrice && row.requestCount) { |
| | | row.totalPrice = erpPriceMultiply(row.estimatedPrice, row.requestCount) ?? 0; |
| | | } |
| | | } |
| | | |
| | | /** è¡¨åæ ¡éª */ |
| | | function validate() { |
| | | for (let i = 0; i < tableData.value.length; i++) { |
| | | const item = tableData.value[i]; |
| | | if (item) { |
| | | if (!item.productId) { |
| | | throw new Error(`第 ${i + 1} è¡ï¼äº§åä¸è½ä¸ºç©º`); |
| | | } |
| | | if (!item.requestCount || item.requestCount <= 0) { |
| | | throw new Error(`第 ${i + 1} è¡ï¼ç³è¯·æ°éä¸è½ä¸ºç©º`); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | defineExpose({ |
| | | validate, |
| | | }); |
| | | |
| | | /** åå§å */ |
| | | onMounted(async () => { |
| | | const res = await getItemPage({ pageNo: 1, pageSize: 100, status: 0 }); |
| | | productOptions.value = res.list || []; |
| | | if (tableData.value.length === 0) { |
| | | handleAdd(); |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Grid class="w-full"> |
| | | <template #productId="{ row }"> |
| | | <Select |
| | | v-if="!disabled" |
| | | v-model:value="row.productId" |
| | | :options="productOptions" |
| | | :field-names="{ label: 'name', value: 'id' }" |
| | | class="w-full" |
| | | placeholder="è¯·éæ©äº§å" |
| | | show-search |
| | | @change="handleProductChange($event, row)" |
| | | /> |
| | | <span v-else>{{ row.productName || '-' }}</span> |
| | | </template> |
| | | <template #requestCount="{ row }"> |
| | | <InputNumber |
| | | v-if="!disabled" |
| | | v-model:value="row.requestCount" |
| | | :min="0" |
| | | :precision="3" |
| | | @change="handleRowChange(row)" |
| | | /> |
| | | <span v-else>{{ erpCountInputFormatter(row.requestCount) || '-' }}</span> |
| | | </template> |
| | | <template #estimatedPrice="{ row }"> |
| | | <InputNumber |
| | | v-if="!disabled" |
| | | v-model:value="row.estimatedPrice" |
| | | :min="0" |
| | | :precision="2" |
| | | @change="handleRowChange(row)" |
| | | /> |
| | | <span v-else>{{ erpPriceInputFormatter(row.estimatedPrice) || '-' }}</span> |
| | | </template> |
| | | <template #requiredDate="{ row }"> |
| | | <DatePicker |
| | | v-if="!disabled" |
| | | v-model:value="row.requiredDate" |
| | | valueFormat="YYYY-MM-DD" |
| | | class="w-full" |
| | | @change="handleRowChange(row)" |
| | | /> |
| | | <span v-else>{{ row.requiredDate || '-' }}</span> |
| | | </template> |
| | | <template #remark="{ row }"> |
| | | <Input v-if="!disabled" v-model:value="row.remark" class="w-full" @change="handleRowChange(row)" /> |
| | | <span v-else>{{ row.remark || '-' }}</span> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'å é¤', |
| | | type: 'link', |
| | | danger: true, |
| | | popConfirm: { |
| | | title: '确认å é¤è¯¥äº§ååï¼', |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | |
| | | <template #bottom> |
| | | <div class="mt-2 rounded border border-border bg-muted p-2"> |
| | | <div class="flex justify-between text-sm text-muted-foreground"> |
| | | <span class="font-medium text-foreground">å计ï¼</span> |
| | | <div class="flex space-x-4"> |
| | | <span>ç³è¯·æ°éï¼{{ erpCountInputFormatter(summaries.count) }}</span> |
| | | <span>é¢ä¼°éé¢ï¼{{ erpPriceInputFormatter(summaries.totalPrice) }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <TableAction |
| | | v-if="!disabled" |
| | | class="mt-2 flex justify-center" |
| | | :actions="[ |
| | | { |
| | | label: 'æ·»å ç³è¯·äº§å', |
| | | type: 'default', |
| | | onClick: handleAdd, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import { onMounted, ref } from 'vue'; |
| | | |
| | | import { Page } from '../../../../packages/effects/common-ui/src'; |
| | | import { erpPriceInputFormatter } from '../../../../packages/utils/src'; |
| | | |
| | | import { Card, Col, Row, Statistic } from 'ant-design-vue'; |
| | | |
| | | import { getPurchaseSummary } from '#/api/erp/purchase/statistics'; |
| | | import type { ErpPurchaseStatisticsApi } from '#/api/erp/purchase/statistics'; |
| | | |
| | | defineOptions({ name: 'ErpPurchaseStatistics' }); |
| | | |
| | | const loading = ref(false); |
| | | const summary = ref<ErpPurchaseStatisticsApi.Summary>({}); |
| | | |
| | | /** è·åç»è®¡æ°æ® */ |
| | | async function fetchSummary() { |
| | | loading.value = true; |
| | | try { |
| | | summary.value = await getPurchaseSummary(); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | } |
| | | |
| | | onMounted(() => { |
| | | fetchSummary(); |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <div class="p-4"> |
| | | <Card title="éè´æ±æ»ç»è®¡" :loading="loading"> |
| | | <Row :gutter="16"> |
| | | <Col :span="6"> |
| | | <Statistic |
| | | title="æ»è®¢åæ°" |
| | | :value="summary.totalOrderCount || 0" |
| | | suffix="å" |
| | | /> |
| | | </Col> |
| | | <Col :span="6"> |
| | | <Statistic |
| | | title="æ»è®¢åéé¢" |
| | | :value="erpPriceInputFormatter(summary.totalOrderAmount) || '0.00'" |
| | | suffix="å
" |
| | | /> |
| | | </Col> |
| | | <Col :span="6"> |
| | | <Statistic |
| | | title="æ»å
¥åºæ°" |
| | | :value="summary.totalInCount || 0" |
| | | suffix="å" |
| | | /> |
| | | </Col> |
| | | <Col :span="6"> |
| | | <Statistic |
| | | title="æ»å
¥åºéé¢" |
| | | :value="erpPriceInputFormatter(summary.totalInAmount) || '0.00'" |
| | | suffix="å
" |
| | | /> |
| | | </Col> |
| | | </Row> |
| | | <Row :gutter="16" class="mt-4"> |
| | | <Col :span="6"> |
| | | <Statistic |
| | | title="æ»éè´§æ°" |
| | | :value="summary.totalReturnCount || 0" |
| | | suffix="å" |
| | | /> |
| | | </Col> |
| | | <Col :span="6"> |
| | | <Statistic |
| | | title="æ»éè´§éé¢" |
| | | :value="erpPriceInputFormatter(summary.totalReturnAmount) || '0.00'" |
| | | suffix="å
" |
| | | /> |
| | | </Col> |
| | | </Row> |
| | | </Card> |
| | | </div> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { ErpPurchaseStatisticsApi } from '#/api/erp/purchase/statistics'; |
| | | |
| | | import { Page } from '../../../../packages/effects/common-ui/src'; |
| | | |
| | | import { Tag } from 'ant-design-vue'; |
| | | |
| | | import { useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { getPriceAnalysis } from '#/api/erp/purchase/statistics'; |
| | | import { getProductSimpleList } from '#/api/erp/product/product'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | |
| | | defineOptions({ name: 'ErpPurchaseStatisticsPrice' }); |
| | | |
| | | /** æç´¢è¡¨å */ |
| | | const formSchema: VbenFormSchema[] = [ |
| | | { |
| | | fieldName: 'productId', |
| | | label: '产å', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©äº§å', |
| | | allowClear: true, |
| | | showSearch: true, |
| | | api: getProductSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'orderTime', |
| | | label: 'éè´æ¶é´', |
| | | component: 'RangePicker', |
| | | componentProps: { |
| | | ...getRangePickerDefaultProps(), |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | ]; |
| | | |
| | | /** åè¡¨åæ®µ */ |
| | | const columns: VxeTableGridOptions['columns'] = [ |
| | | { |
| | | field: 'productName', |
| | | title: '产ååç§°', |
| | | minWidth: 150, |
| | | }, |
| | | { |
| | | field: 'productBarCode', |
| | | title: 'äº§åæ¡ç ', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'avgPrice', |
| | | title: 'å¹³åä»·æ ¼', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'minPrice', |
| | | title: 'æä½ä»·æ ¼', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'maxPrice', |
| | | title: 'æé«ä»·æ ¼', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'latestPrice', |
| | | title: 'æè¿ä»·æ ¼', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'priceChangeRate', |
| | | title: 'ä»·æ ¼ååç', |
| | | minWidth: 120, |
| | | slots: { default: 'priceChangeRate' }, |
| | | }, |
| | | ]; |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: formSchema, |
| | | }, |
| | | gridOptions: { |
| | | columns, |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getPriceAnalysis({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'productId', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<ErpPurchaseStatisticsApi.PriceAnalysis>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <Grid table-title="éè´ä»·æ ¼åæ"> |
| | | <template #priceChangeRate="{ row }"> |
| | | <Tag v-if="row.priceChangeRate > 0" color="red"> |
| | | +{{ row.priceChangeRate }}% |
| | | </Tag> |
| | | <Tag v-else-if="row.priceChangeRate < 0" color="green"> |
| | | {{ row.priceChangeRate }}% |
| | | </Tag> |
| | | <Tag v-else color="default">{{ row.priceChangeRate }}%</Tag> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { ErpPurchaseStatisticsApi } from '#/api/erp/purchase/statistics'; |
| | | |
| | | import { Page } from '../../../../packages/effects/common-ui/src'; |
| | | |
| | | import { useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { getSupplierStatistics } from '#/api/erp/purchase/statistics'; |
| | | import { getSupplierSimpleList } from '#/api/erp/purchase/supplier'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | |
| | | defineOptions({ name: 'ErpPurchaseStatisticsSupplier' }); |
| | | |
| | | /** æç´¢è¡¨å */ |
| | | const formSchema: VbenFormSchema[] = [ |
| | | { |
| | | fieldName: 'supplierId', |
| | | label: 'ä¾åºå', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ä¾åºå', |
| | | allowClear: true, |
| | | showSearch: true, |
| | | api: getSupplierSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'orderTime', |
| | | label: 'è®¢åæ¶é´', |
| | | component: 'RangePicker', |
| | | componentProps: { |
| | | ...getRangePickerDefaultProps(), |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | ]; |
| | | |
| | | /** åè¡¨åæ®µ */ |
| | | const columns: VxeTableGridOptions['columns'] = [ |
| | | { |
| | | field: 'supplierName', |
| | | title: 'ä¾åºå', |
| | | minWidth: 150, |
| | | }, |
| | | { |
| | | field: 'orderCount', |
| | | title: 'è®¢åæ°', |
| | | minWidth: 100, |
| | | formatter: 'formatAmount3', |
| | | }, |
| | | { |
| | | field: 'orderAmount', |
| | | title: '订åéé¢', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'inCount', |
| | | title: 'å
¥åºæ°', |
| | | minWidth: 100, |
| | | formatter: 'formatAmount3', |
| | | }, |
| | | { |
| | | field: 'inAmount', |
| | | title: 'å
¥åºéé¢', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'returnCount', |
| | | title: 'éè´§æ°', |
| | | minWidth: 100, |
| | | formatter: 'formatAmount3', |
| | | }, |
| | | { |
| | | field: 'returnAmount', |
| | | title: 'éè´§éé¢', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | ]; |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: formSchema, |
| | | }, |
| | | gridOptions: { |
| | | columns, |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getSupplierStatistics({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'supplierId', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<ErpPurchaseStatisticsApi.SupplierStatistics>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <Grid table-title="ä¾åºåä¾è´§ç»è®¡" /> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { ErpPurchaseStatisticsApi } from '#/api/erp/purchase/statistics'; |
| | | |
| | | import { Page } from '../../../../packages/effects/common-ui/src'; |
| | | |
| | | import { Tag } from 'ant-design-vue'; |
| | | |
| | | import { useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { getUncompletedOrders } from '#/api/erp/purchase/statistics'; |
| | | import { getSupplierSimpleList } from '#/api/erp/purchase/supplier'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | |
| | | defineOptions({ name: 'ErpPurchaseStatisticsUncompleted' }); |
| | | |
| | | /** æç´¢è¡¨å */ |
| | | const formSchema: VbenFormSchema[] = [ |
| | | { |
| | | fieldName: 'orderNo', |
| | | label: '订åå·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è®¢åå·', |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'supplierId', |
| | | label: 'ä¾åºå', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ä¾åºå', |
| | | allowClear: true, |
| | | showSearch: true, |
| | | api: getSupplierSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'orderTime', |
| | | label: 'è®¢åæ¶é´', |
| | | component: 'RangePicker', |
| | | componentProps: { |
| | | ...getRangePickerDefaultProps(), |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | ]; |
| | | |
| | | /** åè¡¨åæ®µ */ |
| | | const columns: VxeTableGridOptions['columns'] = [ |
| | | { |
| | | field: 'orderNo', |
| | | title: '订åå·', |
| | | minWidth: 150, |
| | | }, |
| | | { |
| | | field: 'supplierName', |
| | | title: 'ä¾åºå', |
| | | minWidth: 150, |
| | | }, |
| | | { |
| | | field: 'orderTime', |
| | | title: 'è®¢åæ¶é´', |
| | | minWidth: 160, |
| | | formatter: 'formatDate', |
| | | }, |
| | | { |
| | | field: 'productNames', |
| | | title: '产åä¿¡æ¯', |
| | | minWidth: 150, |
| | | showOverflow: 'tooltip', |
| | | }, |
| | | { |
| | | field: 'orderAmount', |
| | | title: '订åéé¢', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'inAmount', |
| | | title: 'å
¥åºéé¢', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'uncompletedAmount', |
| | | title: 'æªå®æéé¢', |
| | | minWidth: 120, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'daysOverdue', |
| | | title: 'è¶
æå¤©æ°', |
| | | minWidth: 100, |
| | | slots: { default: 'daysOverdue' }, |
| | | }, |
| | | ]; |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: formSchema, |
| | | }, |
| | | gridOptions: { |
| | | columns, |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getUncompletedOrders({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'orderId', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<ErpPurchaseStatisticsApi.UncompletedOrder>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <Grid table-title="æªå®æè®¢åè·è¸ª"> |
| | | <template #daysOverdue="{ row }"> |
| | | <Tag v-if="row.daysOverdue > 0" color="error"> |
| | | å·²è¶
æ {{ row.daysOverdue }} 天 |
| | | </Tag> |
| | | <Tag v-else color="success">æªè¶
æ</Tag> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| | |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'creditCode', |
| | | label: 'ç»ä¸ç¤¾ä¼ä¿¡ç¨ä»£ç ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ç»ä¸ç¤¾ä¼ä¿¡ç¨ä»£ç ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'taxNo', |
| | | label: 'ç¨å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ç¨å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'contact', |
| | | label: 'è系人', |
| | | component: 'Input', |
| | |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'mobile', |
| | | label: 'ææºå·ç ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ææºå·ç ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'telephone', |
| | | fieldName: 'phone', |
| | | label: 'èç³»çµè¯', |
| | | component: 'Input', |
| | | componentProps: { |
| | |
| | | }, |
| | | { |
| | | fieldName: 'email', |
| | | label: 'çµåé®ç®±', |
| | | label: 'é®ç®±', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥çµåé®ç®±', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'fax', |
| | | label: 'ä¼ ç', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ä¼ ç', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'å¼å¯ç¶æ', |
| | | component: 'RadioGroup', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), |
| | | buttonStyle: 'solid', |
| | | optionType: 'button', |
| | | }, |
| | | rules: z.number().default(CommonStatusEnum.ENABLE), |
| | | }, |
| | | { |
| | | fieldName: 'sort', |
| | | label: 'æåº', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | placeholder: '请è¾å
¥æåº', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'taxNo', |
| | | label: '纳ç¨äººè¯å«å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥çº³ç¨äººè¯å«å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'taxPercent', |
| | | label: 'ç¨ç(%)', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | placeholder: '请è¾å
¥ç¨ç', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥é®ç®±', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'bankName', |
| | | label: '弿·è¡', |
| | | label: '弿·é¶è¡', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¼æ·è¡', |
| | | placeholder: '请è¾å
¥å¼æ·é¶è¡', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'bankAccount', |
| | | label: '弿·è´¦å·', |
| | | label: 'é¶è¡è´¦å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¼æ·è´¦å·', |
| | | placeholder: '请è¾å
¥é¶è¡è´¦å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'bankAddress', |
| | | label: '弿·å°å', |
| | | fieldName: 'cooperationTime', |
| | | label: 'åä½å¼å§æ¶é´', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©åä½å¼å§æ¶é´', |
| | | class: '!w-full', |
| | | valueFormat: 'x', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'address', |
| | | label: 'å°å', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¼æ·å°å', |
| | | placeholder: '请è¾å
¥å°å', |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'RadioGroup', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'å¯ç¨', value: 0 }, |
| | | { label: 'ç¦ç¨', value: 1 }, |
| | | ], |
| | | buttonStyle: 'solid', |
| | | optionType: 'button', |
| | | }, |
| | | rules: z.number().default(0), |
| | | }, |
| | | { |
| | | fieldName: 'qualification', |
| | | label: 'èµè´¨ä¿¡æ¯', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥èµè´¨ä¿¡æ¯', |
| | | rows: 3, |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'remark', |