feat(archive): 添加档案管理模块及相关功能
- 新增档案管理字典常量定义(分类状态、文档状态、密级等)
- 为报销单、课题申报、会员增加归档字段和归档接口
- 实现报销单、课题申报、会员归档功能及界面按钮
- 添加档案借阅管理的数据模型和表单配置
- 添加档案分类管理的数据模型和表单配置
- 更新归档相关API返回生成的档案编号信息
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace ArchiveBorrowApi { |
| | | /** åé
å */ |
| | | export interface Borrow { |
| | | id?: number; // åé
åç¼å· |
| | | no?: string; // åé
åå·ï¼å端çæï¼JY+yyyyMMdd+6使µæ°´ï¼åªè¯»ï¼ |
| | | documentId: number; // æ¡£æ¡ ID |
| | | documentNo?: string; // æ¡£æ¡ç¼å·ï¼å¿«ç
§ï¼ |
| | | documentTitle?: string; // æ¡£æ¡æ é¢ï¼å¿«ç
§ï¼ |
| | | borrowerUserId?: number; // åé
äººç¨æ· ID |
| | | borrowerName: string; // åé
人å§å |
| | | borrowerDept?: string; // åé
人é¨é¨ |
| | | borrowerPhone?: string; // åé
人èç³»çµè¯ |
| | | borrowDate: string; // åé
æ¥æ |
| | | borrowPurpose?: string; // åé
ç®ç |
| | | returnDeadline: string; // å½è¿æé |
| | | status?: number; // ç¶æï¼åå
¸ archive_borrow_status |
| | | processInstanceId?: string; // BPM æµç¨å®ä¾ ID |
| | | approveTime?: string; // 审æ¹éè¿ï¼å®é
ååºï¼æ¶é´ |
| | | returnTime?: string; // å®é
å½è¿æ¶é´ |
| | | returnConfirmUserId?: number; // å½è¿ç¡®è®¤äººç¨æ· ID |
| | | returnConfirmUserName?: string; // å½è¿ç¡®è®¤äººå§å |
| | | returnRemark?: string; // å½è¿å¤æ³¨ |
| | | rejectReason?: string; // 驳ååå |
| | | rejectTime?: string; // é©³åæ¶é´ |
| | | remark?: string; // 夿³¨ |
| | | overdue?: boolean; // æ¯å¦é¾æï¼åç«¯å®æ¶è®¡ç®ï¼ |
| | | overdueDays?: number; // 龿天æ°ï¼åç«¯å®æ¶è®¡ç®ï¼ |
| | | createTime?: string; // å建æ¶é´ |
| | | } |
| | | |
| | | /** å®¡æ¹æµç¨å®ä¹ */ |
| | | export interface ProcessDefinition { |
| | | id: string; |
| | | key: string; |
| | | name: string; |
| | | } |
| | | |
| | | /** å½è¿ç»è®°è¯·æ± */ |
| | | export interface ReturnReq { |
| | | id: number; // åé
åç¼å· |
| | | returnRemark?: string; // å½è¿å¤æ³¨ |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢åé
åå页 */ |
| | | export function getBorrowPage(params: PageParam) { |
| | | return requestClient.get<PageResult<ArchiveBorrowApi.Borrow>>( |
| | | '/archive/borrow/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢é¾ææªè¿åé
åå页 */ |
| | | export function getOverdueBorrowPage(params: PageParam) { |
| | | return requestClient.get<PageResult<ArchiveBorrowApi.Borrow>>( |
| | | '/archive/borrow/overdue-page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢åé
å详æ
*/ |
| | | export function getBorrow(id: number) { |
| | | return requestClient.get<ArchiveBorrowApi.Borrow>( |
| | | `/archive/borrow/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢åé
å */ |
| | | export function createBorrow(data: ArchiveBorrowApi.Borrow) { |
| | | return requestClient.post('/archive/borrow/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹åé
å */ |
| | | export function updateBorrow(data: ArchiveBorrowApi.Borrow) { |
| | | return requestClient.put('/archive/borrow/update', data); |
| | | } |
| | | |
| | | /** å é¤åé
å */ |
| | | export function deleteBorrow(id: number) { |
| | | return requestClient.delete('/archive/borrow/delete', { |
| | | params: { id }, |
| | | }); |
| | | } |
| | | |
| | | /** æäº¤åé
åå®¡æ¹ */ |
| | | export function submitBorrow(id: number, processDefinitionKey?: string) { |
| | | return requestClient.post('/archive/borrow/submit', null, { |
| | | params: { id, processDefinitionKey }, |
| | | }); |
| | | } |
| | | |
| | | /** è·åæ¡£æ¡åé
å®¡æ¹æµç¨å表 */ |
| | | export function getBorrowApproveProcessList() { |
| | | return requestClient.get<ArchiveBorrowApi.ProcessDefinition[]>( |
| | | '/archive/borrow/approve-process-list', |
| | | ); |
| | | } |
| | | |
| | | /** å½è¿ç»è®° */ |
| | | export function returnBorrow(data: ArchiveBorrowApi.ReturnReq) { |
| | | return requestClient.post('/archive/borrow/return', data); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace ArchiveCategoryApi { |
| | | /** æ¡£æ¡åç±»ï¼ä¹¦æ¶/åç±»æ ï¼ */ |
| | | export interface Category { |
| | | id?: number; // åç±»ç¼å· |
| | | parentId?: number; // ç¶åç±» IDï¼0=顶级 |
| | | name?: string; // åç±»åç§° |
| | | code?: string; // åç±»ç¼ç ï¼å¯ä¸ï¼member/research/meeting/finance/document/partyï¼ |
| | | sort?: number; // æ¾ç¤ºæåº |
| | | description?: string; // å类说æ |
| | | defaultSecretLevel?: number; // 该åç±»ä¸æ¡£æ¡é»è®¤å¯çº§ï¼åå
¸ archive_secret_level |
| | | status?: number; // ç¶æï¼åå
¸ archive_category_status |
| | | remark?: string; // 夿³¨ |
| | | createTime?: string; // å建æ¶é´ |
| | | children?: Category[]; // ååç±»ï¼æ å½¢è¿åï¼ |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢æ¡£æ¡åç±»å表ï¼å¹³éºï¼ */ |
| | | export function getCategoryList(params?: ArchiveCategoryApi.Category) { |
| | | return requestClient.get<ArchiveCategoryApi.Category[]>( |
| | | '/archive/category/list', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢æ¡£æ¡åç±»æ */ |
| | | export function getCategoryTree() { |
| | | return requestClient.get<ArchiveCategoryApi.Category[]>( |
| | | '/archive/category/tree', |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢æ¡£æ¡å类详æ
*/ |
| | | export function getCategory(id: number) { |
| | | return requestClient.get<ArchiveCategoryApi.Category>( |
| | | `/archive/category/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢æ¡£æ¡åç±» */ |
| | | export function createCategory(data: ArchiveCategoryApi.Category) { |
| | | return requestClient.post('/archive/category/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹æ¡£æ¡åç±» */ |
| | | export function updateCategory(data: ArchiveCategoryApi.Category) { |
| | | return requestClient.put('/archive/category/update', data); |
| | | } |
| | | |
| | | /** å 餿¡£æ¡åç±» */ |
| | | export function deleteCategory(id: number) { |
| | | return requestClient.delete('/archive/category/delete', { |
| | | params: { id }, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import type { SystemStorageApi } from '#/api/system/storage'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace ArchiveDocumentApi { |
| | | /** æ¡£æ¡ï¼çµåéä»¶ + å®ä½åæ¾ä½ç½®ï¼ */ |
| | | export interface Document { |
| | | id?: number; // æ¡£æ¡ç¼å· |
| | | no?: string; // æ¡£æ¡ç¼å·ï¼å端çæï¼DA+yyyyMMdd+6使µæ°´ï¼åªè¯»ï¼ |
| | | title: string; // æ¡£æ¡æ é¢ |
| | | categoryId: number; // æ¡£æ¡åç±» ID |
| | | categoryName?: string; // åç±»åç§°ï¼å端åä½ï¼ |
| | | secretLevel?: number; // å¯çº§ï¼åå
¸ archive_secret_level |
| | | carrierType?: number; // è½½ä½ï¼åå
¸ archive_carrier_type |
| | | shelfNo?: string; // å®ä½-书æ¶å· |
| | | layerNo?: string; // å®ä½-å±å· |
| | | columnNo?: string; // å®ä½-åå· |
| | | boxNo?: string; // å®ä½-åå·/æ¡£æ¡çå· |
| | | storePlace?: string; // å®ä½åæ¾ä½ç½®æè¿° |
| | | sourceType?: number; // æ¥æºï¼åå
¸ archive_source_typeï¼å端åï¼å端åªè¯»ï¼ |
| | | sourceNo?: string; // æ¥æºåå·ï¼ç«é¡¹ç¼å·/ä¼åç¼å·/æ¥éåå·ï¼ |
| | | sourceId?: number; // æ¥æºåæ® ID |
| | | sourceTitle?: string; // æ¥æºåæ®æ é¢ |
| | | archiveDate?: string; // 彿¡£æ¥æ |
| | | archivistUserId?: number; // 彿¡£äººç¨æ· ID |
| | | archivistName?: string; // 彿¡£äººå§å |
| | | status?: number; // æ¡£æ¡ç¶æï¼åå
¸ archive_document_status |
| | | summary?: string; // æè¦ |
| | | tags?: string; // å
³é®è¯ï¼è±æéå·åé |
| | | remark?: string; // 夿³¨ |
| | | createTime?: string; // å建æ¶é´ |
| | | attachmentList?: SystemStorageApi.StorageBlob[]; // éä»¶å表 |
| | | hasAttachment?: boolean; // æ¯å¦æéä»¶ |
| | | blobIds?: number[]; // æäº¤æ¶çéä»¶ ID å表ï¼å
¨éï¼ |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢æ¡£æ¡å页 */ |
| | | export function getDocumentPage(params: PageParam) { |
| | | return requestClient.get<PageResult<ArchiveDocumentApi.Document>>( |
| | | '/archive/document/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢æ¡£æ¡è¯¦æ
*/ |
| | | export function getDocument(id: number) { |
| | | return requestClient.get<ArchiveDocumentApi.Document>( |
| | | `/archive/document/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢æ¡£æ¡ */ |
| | | export function createDocument(data: ArchiveDocumentApi.Document) { |
| | | return requestClient.post('/archive/document/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹æ¡£æ¡ */ |
| | | export function updateDocument(data: ArchiveDocumentApi.Document) { |
| | | return requestClient.put('/archive/document/update', data); |
| | | } |
| | | |
| | | /** å 餿¡£æ¡ */ |
| | | export function deleteDocument(id: number) { |
| | | return requestClient.delete('/archive/document/delete', { |
| | | params: { id }, |
| | | }); |
| | | } |
| | | |
| | | /** å¯¼åºæ¡£æ¡ Excel */ |
| | | export function exportDocument(params: ArchiveDocumentApi.Document) { |
| | | return requestClient.download('/archive/document/export-excel', { params }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace ArchiveStatApi { |
| | | /** å类维度ç»è®¡é¡¹ */ |
| | | export interface StatCategory { |
| | | categoryId?: number; // åç±» ID |
| | | name?: string; // åç±»åç§° |
| | | total?: number; // æ¡£æ¡æ°é |
| | | } |
| | | |
| | | /** åå
¸å¼ç»´åº¦ç»è®¡é¡¹ */ |
| | | export interface StatValue { |
| | | value?: number; // åå
¸å¼ |
| | | name?: string; // åå
¸æ ç¾ï¼å端转ä¹ï¼ |
| | | total?: number; // æ¡£æ¡æ°é |
| | | } |
| | | |
| | | /** æ¡£æ¡ç»è®¡æ¦è§ */ |
| | | export interface StatOverview { |
| | | totalCount?: number; // æ¡£æ¡æ»æ° |
| | | inStockCount?: number; // å¨åºæ¡£æ¡æ° |
| | | borrowedCount?: number; // ååºä¸æ¡£æ¡æ° |
| | | borrowingCount?: number; // ååºä¸åé
åæ° |
| | | overdueCount?: number; // 龿æªè¿åé
åæ° |
| | | byCategory?: StatCategory[]; // æåç±»åå¸ |
| | | bySecretLevel?: StatValue[]; // æå¯çº§åå¸ |
| | | byCarrierType?: StatValue[]; // æè½½ä½åå¸ |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢æ¡£æ¡ç»è®¡æ¦è§ */ |
| | | export function getArchiveStatOverview() { |
| | | return requestClient.get<ArchiveStatApi.StatOverview>( |
| | | '/archive/stat/overview', |
| | | ); |
| | | } |
| | |
| | | applyDate: string; // ç³è¯·æ¥æ yyyy-MM-dd |
| | | processInstanceId?: string; // BPM æµç¨å®ä¾ ID |
| | | archiveTime?: string; // 彿¡£æ¶é´ |
| | | archiveDocumentId?: number; // 彿¡£çæçæ¡£æ¡ IDï¼é空å³å·²çææ¡£æ¡ï¼ |
| | | archived?: boolean; // æ¯å¦å·²çææ¡£æ¡ |
| | | remark?: string; // 夿³¨ |
| | | creator?: string; // å建人 ID |
| | | creatorName?: string; // å建人å§å |
| | |
| | | }); |
| | | } |
| | | |
| | | /** 彿¡£æ¥éåï¼ä»
å·²éè¿å¯å½æ¡£ï¼ */ |
| | | /** 彿¡£æ¥éåï¼ä»
å·²éè¿å¯å½æ¡£ï¼ï¼è¿åçæçæ¡£æ¡ç¼å· */ |
| | | export function archiveReimbursement(id: number) { |
| | | return requestClient.post('/finance/reimbursement/archive', null, { |
| | | return requestClient.post<string>('/finance/reimbursement/archive', null, { |
| | | params: { id }, |
| | | }); |
| | | } |
| | |
| | | researchNo?: string; // ç«é¡¹ç¼å·ï¼å®¡æ¹éè¿åçæ |
| | | approveTime?: string; // ç«é¡¹æ¶é´ |
| | | noticeId?: number; // å·²åå¸çç«é¡¹éç¥å
Œ ID |
| | | archiveDocumentId?: number; // 彿¡£çæçæ¡£æ¡ IDï¼é空å³å·²å½æ¡£ï¼ |
| | | archived?: boolean; // æ¯å¦å·²å½æ¡£ |
| | | remark?: string; // 夿³¨ |
| | | creator?: string; // å建人 ID |
| | | creatorName?: string; // å建人å§å |
| | |
| | | }); |
| | | } |
| | | |
| | | /** 彿¡£å°æ¡£æ¡ç®¡çï¼ä»
å·²ç«é¡¹å¯å½æ¡£ï¼ï¼è¿åçæçæ¡£æ¡ç¼å· */ |
| | | export function archiveApplication(id: number) { |
| | | return requestClient.post<string>('/research/application/archive', null, { |
| | | params: { id }, |
| | | }); |
| | | } |
| | | |
| | | /** æ¥è¯¢æç³æ¥ä¹¦çä¸å®¶è¯å®¡æç»å表 */ |
| | | export function getTopicReviewList(applicationId: number) { |
| | | return requestClient.get<ResearchApplicationApi.TopicReview[]>( |
| | |
| | | joinDate?: string; // å
¥ä¼æ¥æ |
| | | expireDate?: string; // æææè³ |
| | | status?: number; // ç¶æï¼åå
¸ ums_member_status |
| | | archiveDocumentId?: number; // 彿¡£çæçæ¡£æ¡ IDï¼é空å³å·²å½æ¡£ï¼ |
| | | archived?: boolean; // æ¯å¦å·²å½æ¡£ |
| | | remark?: string; // 夿³¨ |
| | | createTime?: string; // å建æ¶é´ |
| | | attachmentList?: SystemStorageApi.StorageBlob[]; // éä»¶å表 |
| | |
| | | return requestClient.delete('/ums/member/delete', { |
| | | params: { id }, |
| | | }); |
| | | } |
| | | |
| | | /** 彿¡£å°æ¡£æ¡ç®¡çï¼ä»
æ£å¸¸ç¶æä¼åå¯å½æ¡£ï¼ï¼è¿åçæçæ¡£æ¡ç¼å· */ |
| | | export function archiveMember(id: number) { |
| | | return requestClient.post<string>('/ums/member/archive', null, { |
| | | params: { id }, |
| | | }); |
| | | } |
| | |
| | | RESEARCH_FUND_STATUS: 'research_fund_status', // ç»è´¹æ¨ä»ç¶æ |
| | | } as const; |
| | | |
| | | /** ========== ARCHIVE - æ¡£æ¡ç®¡ç模å ========== */ |
| | | const ARCHIVE_DICT = { |
| | | ARCHIVE_CATEGORY_STATUS: 'archive_category_status', // æ¡£æ¡åç±»å¯ç¨/åç¨ |
| | | ARCHIVE_DOCUMENT_STATUS: 'archive_document_status', // æ¡£æ¡ç¶æï¼å¨åº/ååºä¸ï¼ |
| | | ARCHIVE_SECRET_LEVEL: 'archive_secret_level', // æ¡£æ¡å¯çº§ï¼å
¬å¼/å
é¨/ä¿å¯ï¼ |
| | | ARCHIVE_CARRIER_TYPE: 'archive_carrier_type', // æ¡£æ¡è½½ä½ï¼çµå/å®ä½/çµå+å®ä½ï¼ |
| | | ARCHIVE_SOURCE_TYPE: 'archive_source_type', // æ¡£æ¡æ¥æºï¼æå·¥/课é¢/ä¼å/è´¢å¡ï¼ |
| | | ARCHIVE_BORROW_STATUS: 'archive_borrow_status', // åé
åç¶æ |
| | | } as const; |
| | | |
| | | /** ========== AI - 人工æºè½æ¨¡å ========== */ |
| | | const AI_DICT = { |
| | | AI_GENERATE_MODE: 'ai_generate_mode', // AI çææ¨¡å¼ |
| | |
| | | const DICT_TYPE = { |
| | | ...AFTERSALES_DICT, |
| | | ...AI_DICT, |
| | | ...ARCHIVE_DICT, |
| | | ...BPM_DICT, |
| | | ...CRM_DICT, |
| | | ...ERP_DICT, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { ArchiveBorrowApi } from '#/api/archive/borrow'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import { getDocumentPage } from '#/api/archive/document'; |
| | | import { getSimpleUserList } from '#/api/system/user'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'edit'; |
| | | |
| | | /** åé
åç¶æï¼å¯¹åºåå
¸ archive_borrow_statusï¼ */ |
| | | export const BorrowStatus = { |
| | | DRAFT: 0, // è稿 |
| | | APPROVING: 15, // 审æ¹ä¸ |
| | | BORROWED: 20, // ååºä¸ |
| | | RETURNED: 30, // å·²å½è¿ |
| | | REJECTED: 35, // 已驳å |
| | | } as const; |
| | | |
| | | /** åé
ç»è®°è¡¨åé
ç½® */ |
| | | export function useFormSchema( |
| | | formType: FormType, |
| | | documentId?: number, |
| | | ): VbenFormSchema[] { |
| | | const readonly = formType === 'detail'; |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'documentId', |
| | | label: 'åé
æ¡£æ¡', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | allowClear: true, |
| | | showSearch: true, |
| | | placeholder: 'è¯·éæ©å¨åºæ¡£æ¡', |
| | | api: async () => { |
| | | const { list } = await getDocumentPage({ |
| | | pageNo: 1, |
| | | pageSize: 100, |
| | | status: 10, |
| | | }); |
| | | return list.map((item) => ({ |
| | | id: item.id, |
| | | label: item.no ? `${item.no} ${item.title}` : item.title, |
| | | })); |
| | | }, |
| | | labelField: 'label', |
| | | valueField: 'id', |
| | | disabled: readonly || !!documentId, |
| | | }, |
| | | rules: 'selectRequired', |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'borrowerUserId', |
| | | label: 'åé
人', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | allowClear: true, |
| | | showSearch: true, |
| | | placeholder: 'è¯·éæ©åé
人', |
| | | api: getSimpleUserList, |
| | | labelField: 'nickname', |
| | | valueField: 'id', |
| | | disabled: readonly, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'borrowerName', |
| | | label: 'åé
人å§å', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥åé
人å§å', |
| | | disabled: readonly, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'borrowerDept', |
| | | label: 'åé
人é¨é¨', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥åé
人é¨é¨', |
| | | disabled: readonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'borrowerPhone', |
| | | label: 'èç³»çµè¯', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥èç³»çµè¯', |
| | | disabled: readonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'borrowDate', |
| | | label: 'åé
æ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | allowClear: true, |
| | | format: 'YYYY-MM-DD', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | placeholder: 'è¯·éæ©åé
æ¥æ', |
| | | disabled: readonly, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'returnDeadline', |
| | | label: 'å½è¿æé', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | allowClear: true, |
| | | format: 'YYYY-MM-DD', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | placeholder: 'è¯·éæ©å½è¿æé', |
| | | disabled: readonly, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'borrowPurpose', |
| | | label: 'åé
ç®ç', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | rows: 2, |
| | | placeholder: '请è¾å
¥åé
ç®ç', |
| | | disabled: readonly, |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | rows: 2, |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | disabled: readonly, |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'no', |
| | | label: 'åé
åå·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥åé
åå·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'documentNo', |
| | | label: 'æ¡£æ¡ç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥æ¡£æ¡ç¼å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'documentTitle', |
| | | label: 'æ¡£æ¡æ é¢', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥æ¡£æ¡æ é¢', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'borrowerName', |
| | | label: 'åé
人', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥åé
人å§å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.ARCHIVE_BORROW_STATUS, 'number'), |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'borrowDate', |
| | | label: 'åé
æ¥æ', |
| | | component: 'RangePicker', |
| | | componentProps: { |
| | | allowClear: true, |
| | | format: 'YYYY-MM-DD', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | placeholder: ['å¼å§æ¥æ', 'ç»ææ¥æ'], |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'returnDeadline', |
| | | label: 'å½è¿æé', |
| | | component: 'RangePicker', |
| | | componentProps: { |
| | | allowClear: true, |
| | | format: 'YYYY-MM-DD', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | placeholder: ['å¼å§æ¥æ', 'ç»ææ¥æ'], |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** è¡¨æ ¼åé
ç½® */ |
| | | export function useGridColumns(): VxeTableGridOptions<ArchiveBorrowApi.Borrow>['columns'] { |
| | | return [ |
| | | { |
| | | field: 'no', |
| | | title: 'åé
åå·', |
| | | minWidth: 170, |
| | | }, |
| | | { |
| | | field: 'documentNo', |
| | | title: 'æ¡£æ¡ç¼å·', |
| | | minWidth: 170, |
| | | }, |
| | | { |
| | | field: 'documentTitle', |
| | | title: 'æ¡£æ¡æ é¢', |
| | | minWidth: 200, |
| | | }, |
| | | { |
| | | field: 'borrowerName', |
| | | title: 'åé
人', |
| | | minWidth: 100, |
| | | }, |
| | | { |
| | | field: 'borrowerDept', |
| | | title: 'é¨é¨', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'borrowDate', |
| | | title: 'åé
æ¥æ', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'returnDeadline', |
| | | title: 'å½è¿æé', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'overdue', |
| | | title: '龿', |
| | | minWidth: 110, |
| | | slots: { default: 'overdue' }, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | minWidth: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.ARCHIVE_BORROW_STATUS }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'approveTime', |
| | | title: 'ååºæ¶é´', |
| | | minWidth: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'returnTime', |
| | | title: 'å½è¿æ¶é´', |
| | | minWidth: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 230, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { ArchiveBorrowApi } from '#/api/archive/borrow'; |
| | | import type { DescriptionItemSchema } from '#/components/description'; |
| | | |
| | | import { h, onMounted, ref } from 'vue'; |
| | | |
| | | import { ContentWrap } from '@vben/common-ui'; |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { formatDateTime } from '@vben/utils'; |
| | | |
| | | import { Spin, Tag } from 'ant-design-vue'; |
| | | |
| | | import { getBorrow } from '#/api/archive/borrow'; |
| | | import { DictTag } from '#/components/dict-tag'; |
| | | import { useDescription } from '#/components/description'; |
| | | |
| | | defineOptions({ name: 'ArchiveBorrowApproveDetail' }); |
| | | |
| | | const props = defineProps<{ id: string }>(); |
| | | |
| | | const loading = ref(false); |
| | | const formData = ref<ArchiveBorrowApi.Borrow>(); |
| | | |
| | | /** ææ¬åæ®µç©ºå¼å ä½ */ |
| | | function textRender(val: unknown) { |
| | | return val === 0 || val ? String(val) : '-'; |
| | | } |
| | | |
| | | /** æ¥ææ¶é´å段空å¼å ä½ */ |
| | | function timeRender(val: unknown) { |
| | | return val ? (formatDateTime(val as string) as string) : '-'; |
| | | } |
| | | |
| | | /** 龿å±ç¤º */ |
| | | function overdueRender(_: unknown, record: ArchiveBorrowApi.Borrow) { |
| | | return record?.overdue |
| | | ? h(Tag, { color: 'error' }, () => `龿 ${record.overdueDays} 天`) |
| | | : h(Tag, { color: 'success' }, () => 'æªé¾æ'); |
| | | } |
| | | |
| | | const [Descriptions] = useDescription({ |
| | | bordered: true, |
| | | column: 2, |
| | | schema: [ |
| | | { label: 'åé
åå·', field: 'no', render: textRender }, |
| | | { |
| | | label: 'ç¶æ', |
| | | field: 'status', |
| | | render: (val) => |
| | | h(DictTag, { type: DICT_TYPE.ARCHIVE_BORROW_STATUS, value: val }), |
| | | }, |
| | | { label: 'æ¡£æ¡ç¼å·', field: 'documentNo', render: textRender }, |
| | | { label: 'æ¡£æ¡æ é¢', field: 'documentTitle', render: textRender }, |
| | | { label: 'åé
人', field: 'borrowerName', render: textRender }, |
| | | { label: 'åé
人é¨é¨', field: 'borrowerDept', render: textRender }, |
| | | { label: 'èç³»çµè¯', field: 'borrowerPhone', render: textRender }, |
| | | { label: 'åé
æ¥æ', field: 'borrowDate', render: textRender }, |
| | | { label: 'å½è¿æé', field: 'returnDeadline', render: textRender }, |
| | | { |
| | | label: '龿æ
åµ', |
| | | field: 'overdue', |
| | | render: (val, record) => overdueRender(val, record as ArchiveBorrowApi.Borrow), |
| | | }, |
| | | { |
| | | label: 'ååºæ¶é´', |
| | | field: 'approveTime', |
| | | render: timeRender, |
| | | }, |
| | | { |
| | | label: 'å½è¿æ¶é´', |
| | | field: 'returnTime', |
| | | render: timeRender, |
| | | }, |
| | | { label: 'å½è¿ç¡®è®¤äºº', field: 'returnConfirmUserName', render: textRender }, |
| | | { |
| | | label: 'åé
ç®ç', |
| | | field: 'borrowPurpose', |
| | | span: 2, |
| | | render: textRender, |
| | | }, |
| | | { |
| | | label: 'å½è¿å¤æ³¨', |
| | | field: 'returnRemark', |
| | | span: 2, |
| | | render: textRender, |
| | | }, |
| | | { |
| | | label: '驳ååå ', |
| | | field: 'rejectReason', |
| | | span: 2, |
| | | render: textRender, |
| | | }, |
| | | { |
| | | label: '驳忶é´', |
| | | field: 'rejectTime', |
| | | render: timeRender, |
| | | }, |
| | | { label: 'å建æ¶é´', field: 'createTime', render: timeRender }, |
| | | { label: '夿³¨', field: 'remark', span: 2, render: textRender }, |
| | | ] as DescriptionItemSchema[], |
| | | }); |
| | | |
| | | onMounted(async () => { |
| | | try { |
| | | loading.value = true; |
| | | formData.value = await getBorrow(Number(props.id)); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <ContentWrap class="m-2"> |
| | | <Spin :spinning="loading" tip="å è½½ä¸..."> |
| | | <div class="rounded-lg border border-border bg-background p-4"> |
| | | <div class="mb-4 flex items-center gap-2"> |
| | | <span class="inline-block h-4 w-1 rounded bg-primary" /> |
| | | <span class="text-base font-semibold">æ¡£æ¡åé
åä¿¡æ¯</span> |
| | | </div> |
| | | <Descriptions :data="formData" /> |
| | | </div> |
| | | </Spin> |
| | | </ContentWrap> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { ArchiveBorrowApi } from '#/api/archive/borrow'; |
| | | |
| | | import { ref } from 'vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deleteBorrow, |
| | | getBorrowApproveProcessList, |
| | | getBorrowPage, |
| | | getOverdueBorrowPage, |
| | | } from '#/api/archive/borrow'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { BorrowStatus, useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | import ProcessSelectModal from './modules/process-select-modal.vue'; |
| | | import ReturnForm from './modules/return-form.vue'; |
| | | |
| | | /** åé
ç»è®° */ |
| | | defineOptions({ name: 'ArchiveBorrow' }); |
| | | |
| | | const router = useRouter(); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ProcessModal, processModalApi] = useVbenModal({ |
| | | connectedComponent: ProcessSelectModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ReturnModal, returnModalApi] = useVbenModal({ |
| | | connectedComponent: ReturnForm, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** æ¯å¦åªç龿æªè¿ */ |
| | | const overdueOnly = ref(false); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** 忢ãåªç龿ã */ |
| | | function handleToggleOverdue() { |
| | | overdueOnly.value = !overdueOnly.value; |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** ç»è®°åé
*/ |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾åé
å */ |
| | | function handleEdit(row: ArchiveBorrowApi.Borrow) { |
| | | formModalApi.setData({ formType: 'edit', id: row.id }).open(); |
| | | } |
| | | |
| | | /** æ¥çåé
å详æ
*/ |
| | | function handleDetail(row: ArchiveBorrowApi.Borrow) { |
| | | formModalApi.setData({ formType: 'detail', id: row.id }).open(); |
| | | } |
| | | |
| | | /** å é¤åé
å */ |
| | | async function handleDelete(row: ArchiveBorrowApi.Borrow) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', [row.no]), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteBorrow(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', [row.no])); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** æäº¤å®¡æ¹ - å
è·åæµç¨åè¡¨ï¼æµç¨ key ä¸åæ» */ |
| | | async function handleSubmit(row: ArchiveBorrowApi.Borrow) { |
| | | const hideLoading = message.loading({ |
| | | content: 'è·åå®¡æ¹æµç¨...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | const processList = await getBorrowApproveProcessList(); |
| | | hideLoading(); |
| | | if (!processList || processList.length === 0) { |
| | | message.warning( |
| | | 'ææ å¯ç¨å®¡æ¹æµç¨ï¼è¯·å
å° BPM æµç¨è®¾è®¡å¨å叿¡£æ¡åé
å®¡æ¹æµç¨', |
| | | ); |
| | | return; |
| | | } |
| | | processModalApi.setData({ id: row.id, processList }).open(); |
| | | } catch { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** å½è¿ç»è®° */ |
| | | function handleReturn(row: ArchiveBorrowApi.Borrow) { |
| | | returnModalApi.setData(row).open(); |
| | | } |
| | | |
| | | /** æ¥ç审æ¹è¿åº¦ */ |
| | | function handleProcessDetail(row: ArchiveBorrowApi.Borrow) { |
| | | if (!row.processInstanceId) { |
| | | message.warning('该åé
åå°æªæäº¤å®¡æ¹'); |
| | | return; |
| | | } |
| | | router.push({ |
| | | name: 'BpmProcessInstanceDetail', |
| | | query: { id: row.processInstanceId }, |
| | | }); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | const params = { |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }; |
| | | return overdueOnly.value |
| | | ? await getOverdueBorrowPage(params) |
| | | : await getBorrowPage(params); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<ArchiveBorrowApi.Borrow>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <ProcessModal @success="handleRefresh" /> |
| | | <ReturnModal @success="handleRefresh" /> |
| | | <Grid table-title="åé
ç»è®°"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'ç»è®°åé
', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['archive:borrow:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: overdueOnly ? 'æ¥çå
¨é¨' : 'åªç龿', |
| | | type: overdueOnly ? 'primary' : 'default', |
| | | danger: overdueOnly, |
| | | icon: ACTION_ICON.SEARCH, |
| | | auth: ['archive:borrow:query'], |
| | | onClick: handleToggleOverdue, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #overdue="{ row }"> |
| | | <Tag v-if="row.overdue" color="error">龿 {{ row.overdueDays }} 天</Tag> |
| | | <span v-else>-</span> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.detail'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | | auth: ['archive:borrow:query'], |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['archive:borrow:update'], |
| | | ifShow: () => |
| | | row.status === BorrowStatus.DRAFT || |
| | | row.status === BorrowStatus.REJECTED, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æäº¤å®¡æ¹', |
| | | type: 'link', |
| | | icon: ACTION_ICON.AUDIT, |
| | | auth: ['archive:borrow:submit'], |
| | | ifShow: () => |
| | | row.status === BorrowStatus.DRAFT || |
| | | row.status === BorrowStatus.REJECTED, |
| | | onClick: handleSubmit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'å½è¿ç»è®°', |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['archive:borrow:return'], |
| | | ifShow: () => row.status === BorrowStatus.BORROWED, |
| | | onClick: handleReturn.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æ¥ç审æ¹', |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | | auth: ['archive:borrow:query'], |
| | | ifShow: () => !!row.processInstanceId, |
| | | onClick: handleProcessDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['archive:borrow:delete'], |
| | | ifShow: () => row.status === BorrowStatus.DRAFT, |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.no]), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { ArchiveBorrowApi } from '#/api/archive/borrow'; |
| | | |
| | | import type { FormType } from '../data'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | import dayjs from 'dayjs'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createBorrow, |
| | | getBorrow, |
| | | updateBorrow, |
| | | } from '#/api/archive/borrow'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | defineOptions({ name: 'ArchiveBorrowForm' }); |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const formType = ref<FormType>('create'); |
| | | /** 仿¡£æ¡å表ãç»è®°åé
ã带å
¥çæ¡£æ¡ ID */ |
| | | const fixedDocumentId = ref<number>(); |
| | | |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'create') { |
| | | return 'ç»è®°åé
'; |
| | | } |
| | | if (formType.value === 'edit') { |
| | | return $t('ui.actionTitle.edit', ['åé
å']); |
| | | } |
| | | return 'åé
å详æ
'; |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 110, |
| | | }, |
| | | wrapperClass: 'grid-cols-2', |
| | | layout: 'vertical', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | /** åªæäº¤å端 SaveReqVO æ¥åçåæ®µ */ |
| | | async function buildSubmitData() { |
| | | const values = (await formApi.getValues()) as ArchiveBorrowApi.Borrow; |
| | | return { |
| | | id: values.id, |
| | | documentId: values.documentId, |
| | | borrowerUserId: values.borrowerUserId, |
| | | borrowerName: values.borrowerName, |
| | | borrowerDept: values.borrowerDept, |
| | | borrowerPhone: values.borrowerPhone, |
| | | borrowDate: values.borrowDate, |
| | | borrowPurpose: values.borrowPurpose, |
| | | returnDeadline: values.returnDeadline, |
| | | remark: values.remark, |
| | | } as ArchiveBorrowApi.Borrow; |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = await buildSubmitData(); |
| | | try { |
| | | await (formType.value === 'create' |
| | | ? createBorrow(data) |
| | | : updateBorrow(data)); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | fixedDocumentId.value = undefined; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ |
| | | formType: FormType; |
| | | id?: number; |
| | | documentId?: number; |
| | | }>(); |
| | | formType.value = data.formType; |
| | | fixedDocumentId.value = data.documentId; |
| | | formApi.setState({ schema: useFormSchema(formType.value, data.documentId) }); |
| | | formApi.setDisabled(formType.value === 'detail'); |
| | | if (data?.id) { |
| | | modalApi.lock(); |
| | | try { |
| | | const detail = await getBorrow(data.id); |
| | | await formApi.setValues(detail, false); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } else { |
| | | await formApi.resetForm(); |
| | | // æ°å»ºæ¶å¸¦åºæ¡£æ¡ä¸é»è®¤åé
æ¥æï¼ä»å¤©ï¼ |
| | | await formApi.setValues({ |
| | | documentId: data.documentId, |
| | | borrowDate: dayjs().format('YYYY-MM-DD'), |
| | | }); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal |
| | | :title="getTitle" |
| | | class="w-2/3" |
| | | :close-on-click-modal="false" |
| | | :show-confirm-button="formType !== 'detail'" |
| | | > |
| | | <Form class="mx-3" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { ArchiveBorrowApi } from '#/api/archive/borrow'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message, Select } from 'ant-design-vue'; |
| | | |
| | | import { submitBorrow } from '#/api/archive/borrow'; |
| | | |
| | | defineOptions({ name: 'ArchiveBorrowProcessSelect' }); |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const borrowId = ref<number>(); |
| | | const processList = ref<ArchiveBorrowApi.ProcessDefinition[]>([]); |
| | | const selectedProcessKey = ref<string>(''); |
| | | |
| | | /** æµç¨ key åªè½æ¥èªåç«¯å®æ¶å表ï¼ç¦æ¢åç«¯åæ» */ |
| | | const processOptions = computed(() => |
| | | processList.value.map((item) => ({ label: item.name, value: item.key })), |
| | | ); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (!selectedProcessKey.value) { |
| | | message.warning('è¯·éæ©å®¡æ¹æµç¨'); |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await submitBorrow(borrowId.value!, selectedProcessKey.value); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('æäº¤æå'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | borrowId.value = undefined; |
| | | selectedProcessKey.value = ''; |
| | | processList.value = []; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ |
| | | id: number; |
| | | processList: ArchiveBorrowApi.ProcessDefinition[]; |
| | | }>(); |
| | | borrowId.value = data.id; |
| | | processList.value = data.processList || []; |
| | | // é»è®¤éæ©ç¬¬ä¸ä¸ªæµç¨ |
| | | if (processList.value.length > 0) { |
| | | selectedProcessKey.value = processList.value[0]!.key; |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="éæ©å®¡æ¹æµç¨" class="w-1/3"> |
| | | <div class="mb-4"> |
| | | <span class="mr-2">å®¡æ¹æµç¨ï¼</span> |
| | | <Select |
| | | v-model:value="selectedProcessKey" |
| | | :options="processOptions" |
| | | style="width: 240px" |
| | | placeholder="è¯·éæ©å®¡æ¹æµç¨" |
| | | /> |
| | | </div> |
| | | <p class="text-sm text-gray-500"> |
| | | æäº¤å便¬¡æµè½¬ï¼æ¡£æ¡ç®¡çåå®¡æ ¸ â å管é¢å¯¼å®¡æ¹ |
| | | </p> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { ArchiveBorrowApi } from '#/api/archive/borrow'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { returnBorrow } from '#/api/archive/borrow'; |
| | | |
| | | defineOptions({ name: 'ArchiveBorrowReturnForm' }); |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | /** å½åå½è¿çåé
å */ |
| | | const current = ref<ArchiveBorrowApi.Borrow>(); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | }, |
| | | wrapperClass: 'grid-cols-1', |
| | | layout: 'vertical', |
| | | schema: [ |
| | | { |
| | | fieldName: 'returnRemark', |
| | | label: 'å½è¿å¤æ³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | rows: 3, |
| | | placeholder: 'å¦å½è¿æ¶æ¡£æ¡å®å¥½ãæ¯å¦æç¼ºé¡µç', |
| | | }, |
| | | }, |
| | | ], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const values = (await formApi.getValues()) as { returnRemark?: string }; |
| | | modalApi.lock(); |
| | | try { |
| | | await returnBorrow({ |
| | | id: current.value!.id!, |
| | | returnRemark: values.returnRemark, |
| | | }); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('å½è¿ç»è®°æå'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | current.value = undefined; |
| | | return; |
| | | } |
| | | current.value = modalApi.getData<ArchiveBorrowApi.Borrow>(); |
| | | await formApi.resetForm(); |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="å½è¿ç»è®°" class="w-1/2"> |
| | | <div class="mx-3 mb-3 text-sm text-gray-600"> |
| | | <p> |
| | | åé
åå·ï¼{{ current?.no }}ãæ¡£æ¡ï¼{{ current?.documentNo }} |
| | | {{ current?.documentTitle }} |
| | | </p> |
| | | <p> |
| | | åé
人ï¼{{ current?.borrowerName }}ãå½è¿æéï¼{{ current?.returnDeadline }} |
| | | </p> |
| | | </div> |
| | | <Form class="mx-3" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { ArchiveCategoryApi } from '#/api/archive/category'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | import { handleTree } from '@vben/utils'; |
| | | |
| | | import { getCategoryList } from '#/api/archive/category'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹ç表å */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'id', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'parentId', |
| | | label: 'ä¸çº§åç±»', |
| | | component: 'ApiTreeSelect', |
| | | componentProps: { |
| | | allowClear: true, |
| | | api: async () => { |
| | | const data = await getCategoryList(); |
| | | data.unshift({ |
| | | id: 0, |
| | | name: '顶级åç±»', |
| | | } as ArchiveCategoryApi.Category); |
| | | return handleTree(data); |
| | | }, |
| | | fieldNames: { label: 'name', value: 'id', children: 'children' }, |
| | | placeholder: 'è¯·éæ©ä¸çº§åç±»', |
| | | showSearch: true, |
| | | treeDefaultExpandAll: true, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'åç±»åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥åç±»åç§°', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'code', |
| | | label: 'åç±»ç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'è±æç¼ç ï¼å¦ member/research/finance', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'defaultSecretLevel', |
| | | label: 'é»è®¤å¯çº§', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.ARCHIVE_SECRET_LEVEL, 'number'), |
| | | placeholder: '该åç±»ä¸æ¡£æ¡çé»è®¤å¯çº§', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'sort', |
| | | label: 'æ¾ç¤ºæåº', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | min: 0, |
| | | placeholder: 'æ°åè¶å°è¶é å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.ARCHIVE_CATEGORY_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'description', |
| | | label: 'å类说æ', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | rows: 2, |
| | | placeholder: '请è¾å
¥å类说æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | rows: 2, |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'name', |
| | | label: 'åç±»åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥åç±»åç§°', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.ARCHIVE_CATEGORY_STATUS, 'number'), |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** è¡¨æ ¼åé
ç½® */ |
| | | export function useGridColumns(): VxeTableGridOptions<ArchiveCategoryApi.Category>['columns'] { |
| | | return [ |
| | | { |
| | | field: 'name', |
| | | title: 'åç±»åç§°', |
| | | treeNode: true, |
| | | }, |
| | | { |
| | | field: 'code', |
| | | title: 'åç±»ç¼ç ', |
| | | minWidth: 130, |
| | | }, |
| | | { |
| | | field: 'defaultSecretLevel', |
| | | title: 'é»è®¤å¯çº§', |
| | | minWidth: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.ARCHIVE_SECRET_LEVEL }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'sort', |
| | | title: 'æåº', |
| | | minWidth: 80, |
| | | }, |
| | | { |
| | | field: 'description', |
| | | title: 'å类说æ', |
| | | minWidth: 180, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | minWidth: 90, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.ARCHIVE_CATEGORY_STATUS }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | minWidth: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'actions', |
| | | title: 'æä½', |
| | | width: 250, |
| | | fixed: 'right', |
| | | slots: { |
| | | default: 'actions', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { ArchiveCategoryApi } from '#/api/archive/category'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deleteCategory, |
| | | getCategoryList, |
| | | } from '#/api/archive/category'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | /** æ¡£æ¡åç±»ï¼ä¹¦æ¶ï¼ */ |
| | | defineOptions({ name: 'ArchiveCategory' }); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** 忢æ å½¢å±å¼/æ¶ç¼©ç¶æ */ |
| | | const isExpanded = ref(false); |
| | | function handleExpand() { |
| | | isExpanded.value = !isExpanded.value; |
| | | gridApi.grid.setAllTreeExpand(isExpanded.value); |
| | | } |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** å建åç±» */ |
| | | function handleCreate() { |
| | | formModalApi.setData(null).open(); |
| | | } |
| | | |
| | | /** æ·»å ä¸çº§åç±» */ |
| | | function handleAppend(row: ArchiveCategoryApi.Category) { |
| | | formModalApi.setData({ parentId: row.id }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾åç±» */ |
| | | function handleEdit(row: ArchiveCategoryApi.Category) { |
| | | formModalApi.setData(row).open(); |
| | | } |
| | | |
| | | /** å é¤åç±» */ |
| | | async function handleDelete(row: ArchiveCategoryApi.Category) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', [row.name]), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteCategory(row.id!); |
| | | 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, |
| | | pagerConfig: { |
| | | enabled: false, |
| | | }, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async (_, formValues) => { |
| | | return await getCategoryList(formValues); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | treeConfig: { |
| | | parentField: 'parentId', |
| | | rowField: 'id', |
| | | transform: true, |
| | | expandAll: true, |
| | | reserve: true, |
| | | }, |
| | | } as VxeTableGridOptions<ArchiveCategoryApi.Category>, |
| | | }); |
| | | </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: ['archive:category:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: isExpanded ? 'æ¶ç¼©' : 'å±å¼', |
| | | type: 'primary', |
| | | onClick: handleExpand, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'æ°å¢ä¸çº§', |
| | | type: 'link', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['archive:category:create'], |
| | | onClick: handleAppend.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['archive:category:update'], |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['archive:category:delete'], |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.name]), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { ArchiveCategoryApi } from '#/api/archive/category'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createCategory, |
| | | getCategory, |
| | | updateCategory, |
| | | } from '#/api/archive/category'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | defineOptions({ name: 'ArchiveCategoryForm' }); |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const formData = ref<ArchiveCategoryApi.Category>(); |
| | | |
| | | const getTitle = computed(() => { |
| | | if (formData.value?.id) { |
| | | return $t('ui.actionTitle.edit', ['æ¡£æ¡åç±»']); |
| | | } |
| | | return formData.value?.parentId |
| | | ? 'æ°å¢ä¸çº§åç±»' |
| | | : $t('ui.actionTitle.create', ['æ¡£æ¡åç±»']); |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | labelWidth: 90, |
| | | }, |
| | | 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 ArchiveCategoryApi.Category; |
| | | try { |
| | | await (formData.value?.id |
| | | ? updateCategory({ ...data, id: formData.value.id }) |
| | | : createCategory(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; |
| | | } |
| | | let data = modalApi.getData<ArchiveCategoryApi.Category>(); |
| | | // æ°å¢ï¼é¡¶çº§ / ä¸çº§ï¼æ¶åªå¸¦ parentId ä½ä¸ºé»è®¤å¼ |
| | | if (!data || !data.id) { |
| | | formData.value = data ?? undefined; |
| | | await formApi.resetForm(); |
| | | if (data?.parentId !== undefined) { |
| | | await formApi.setValues({ parentId: data.parentId }); |
| | | } |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | data = await getCategory(data.id); |
| | | formData.value = data; |
| | | await formApi.setValues(data); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal class="w-2/5" :title="getTitle"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { ArchiveDocumentApi } from '#/api/archive/document'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | import { handleTree } from '@vben/utils'; |
| | | |
| | | import { getCategoryList } from '#/api/archive/category'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'edit'; |
| | | |
| | | /** æ¡£æ¡è¡¨åé
ç½® */ |
| | | export function useFormSchema(formType: FormType): VbenFormSchema[] { |
| | | const readonly = formType === 'detail'; |
| | | // æ¡£æ¡ç¼å·ãæ¥æºãç¶æã彿¡£äººç±å端维æ¤ï¼ä»
ç¼è¾/详æ
å¯è§ |
| | | const showBackendFields = formType !== 'create'; |
| | | |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'no', |
| | | label: 'æ¡£æ¡ç¼å·', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => showBackendFields, |
| | | }, |
| | | componentProps: { |
| | | disabled: true, |
| | | placeholder: 'ç³»ç»èªå¨çæ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'æ¡£æ¡ç¶æ', |
| | | component: 'Select', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => showBackendFields, |
| | | }, |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.ARCHIVE_DOCUMENT_STATUS, 'number'), |
| | | disabled: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'title', |
| | | label: 'æ¡£æ¡æ é¢', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | disabled: readonly, |
| | | placeholder: '请è¾å
¥æ¡£æ¡æ é¢', |
| | | }, |
| | | rules: 'required', |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'categoryId', |
| | | label: 'æ¡£æ¡åç±»', |
| | | component: 'ApiTreeSelect', |
| | | componentProps: { |
| | | allowClear: true, |
| | | api: async () => handleTree(await getCategoryList({ status: 0 })), |
| | | fieldNames: { label: 'name', value: 'id', children: 'children' }, |
| | | placeholder: 'è¯·éæ©æ¡£æ¡åç±»', |
| | | showSearch: true, |
| | | treeDefaultExpandAll: true, |
| | | disabled: readonly, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'secretLevel', |
| | | label: 'å¯çº§', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.ARCHIVE_SECRET_LEVEL, 'number'), |
| | | placeholder: 'è¯·éæ©å¯çº§', |
| | | disabled: readonly, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'carrierType', |
| | | label: 'è½½ä½', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.ARCHIVE_CARRIER_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©è½½ä½', |
| | | disabled: readonly, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'archiveDate', |
| | | label: '彿¡£æ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | allowClear: true, |
| | | format: 'YYYY-MM-DD', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | placeholder: 'è¯·éæ©å½æ¡£æ¥æ', |
| | | disabled: readonly, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'shelfNo', |
| | | label: '书æ¶å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: 'å¦ Aæ¶', |
| | | disabled: readonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'layerNo', |
| | | label: 'å±å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: 'å¦ 2å±', |
| | | disabled: readonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'columnNo', |
| | | label: 'åå·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: 'å¦ 3å', |
| | | disabled: readonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'boxNo', |
| | | label: 'åå·/æ¡£æ¡çå·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: 'å¦ æ¡£æ¡ç-001', |
| | | disabled: readonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'storePlace', |
| | | label: 'åæ¾ä½ç½®', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: 'å¦ 3å·åºæ¿Aæ¶2å±', |
| | | disabled: readonly, |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'tags', |
| | | label: 'å
³é®è¯', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: 'å¤ä¸ªå
³é®è¯ç¨è±æéå·åéï¼ä¾¿äºæ£ç´¢', |
| | | disabled: readonly, |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'summary', |
| | | label: 'æè¦', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | rows: 2, |
| | | placeholder: '请è¾å
¥æè¦', |
| | | disabled: readonly, |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'sourceType', |
| | | label: 'æ¥æºç±»å', |
| | | component: 'Select', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => showBackendFields, |
| | | }, |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.ARCHIVE_SOURCE_TYPE, 'number'), |
| | | disabled: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'sourceNo', |
| | | label: 'æ¥æºåå·', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => showBackendFields, |
| | | }, |
| | | componentProps: { |
| | | disabled: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'archivistName', |
| | | label: '彿¡£äºº', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => showBackendFields, |
| | | }, |
| | | componentProps: { |
| | | disabled: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | rows: 2, |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | disabled: readonly, |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'blobIds', |
| | | label: 'çµåæä»¶', |
| | | component: 'FileUpload', |
| | | componentProps: { |
| | | valueKey: 'id', |
| | | maxNumber: 10, |
| | | multiple: true, |
| | | showDescription: !readonly, |
| | | disabled: readonly, |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'no', |
| | | label: 'æ¡£æ¡ç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥æ¡£æ¡ç¼å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'title', |
| | | label: 'æ¡£æ¡æ é¢', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥æ¡£æ¡æ é¢', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'categoryId', |
| | | label: 'æ¡£æ¡åç±»', |
| | | component: 'ApiTreeSelect', |
| | | componentProps: { |
| | | allowClear: true, |
| | | api: async () => handleTree(await getCategoryList({ status: 0 })), |
| | | fieldNames: { label: 'name', value: 'id', children: 'children' }, |
| | | placeholder: 'è¯·éæ©æ¡£æ¡åç±»', |
| | | showSearch: true, |
| | | treeDefaultExpandAll: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'secretLevel', |
| | | label: 'å¯çº§', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.ARCHIVE_SECRET_LEVEL, 'number'), |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©å¯çº§', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'carrierType', |
| | | label: 'è½½ä½', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.ARCHIVE_CARRIER_TYPE, 'number'), |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©è½½ä½', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'sourceType', |
| | | label: 'æ¥æºç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.ARCHIVE_SOURCE_TYPE, 'number'), |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©æ¥æºç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'sourceNo', |
| | | label: 'æ¥æºåå·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥æ¥æºåå·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'tags', |
| | | label: 'å
³é®è¯', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥å
³é®è¯', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'æ¡£æ¡ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.ARCHIVE_DOCUMENT_STATUS, 'number'), |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©æ¡£æ¡ç¶æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'archiveDate', |
| | | label: '彿¡£æ¥æ', |
| | | component: 'RangePicker', |
| | | componentProps: { |
| | | allowClear: true, |
| | | format: 'YYYY-MM-DD', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | placeholder: ['å¼å§æ¥æ', 'ç»ææ¥æ'], |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** è¡¨æ ¼åé
ç½® */ |
| | | export function useGridColumns(): VxeTableGridOptions<ArchiveDocumentApi.Document>['columns'] { |
| | | return [ |
| | | { |
| | | field: 'no', |
| | | title: 'æ¡£æ¡ç¼å·', |
| | | minWidth: 170, |
| | | }, |
| | | { |
| | | field: 'title', |
| | | title: 'æ¡£æ¡æ é¢', |
| | | minWidth: 220, |
| | | }, |
| | | { |
| | | field: 'categoryName', |
| | | title: 'æ¡£æ¡åç±»', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'secretLevel', |
| | | title: 'å¯çº§', |
| | | minWidth: 90, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.ARCHIVE_SECRET_LEVEL }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'carrierType', |
| | | title: 'è½½ä½', |
| | | minWidth: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.ARCHIVE_CARRIER_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'sourceType', |
| | | title: 'æ¥æº', |
| | | minWidth: 90, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.ARCHIVE_SOURCE_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'sourceNo', |
| | | title: 'æ¥æºåå·', |
| | | minWidth: 160, |
| | | }, |
| | | { |
| | | field: 'storePlace', |
| | | title: 'åæ¾ä½ç½®', |
| | | minWidth: 170, |
| | | }, |
| | | { |
| | | field: 'archiveDate', |
| | | title: '彿¡£æ¥æ', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'æ¡£æ¡ç¶æ', |
| | | minWidth: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.ARCHIVE_DOCUMENT_STATUS }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'hasAttachment', |
| | | title: 'çµåæä»¶', |
| | | minWidth: 100, |
| | | slots: { default: 'hasAttachment' }, |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 200, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { ArchiveDocumentApi } from '#/api/archive/document'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deleteDocument, |
| | | exportDocument, |
| | | getDocumentPage, |
| | | } from '#/api/archive/document'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | /** æ¡£æ¡ç®¡ç */ |
| | | defineOptions({ name: 'ArchiveDocument' }); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æ°å¢æ¡£æ¡ */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾æ¡£æ¡ */ |
| | | function handleEdit(row: ArchiveDocumentApi.Document) { |
| | | formModalApi.setData({ formType: 'edit', id: row.id }).open(); |
| | | } |
| | | |
| | | /** æ¥çæ¡£æ¡è¯¦æ
*/ |
| | | function handleDetail(row: ArchiveDocumentApi.Document) { |
| | | formModalApi.setData({ formType: 'detail', id: row.id }).open(); |
| | | } |
| | | |
| | | /** å 餿¡£æ¡ */ |
| | | async function handleDelete(row: ArchiveDocumentApi.Document) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', [row.title]), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteDocument(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', [row.title])); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** å¯¼åº Excel */ |
| | | async function handleExport() { |
| | | const data = await exportDocument(await gridApi.formApi.getValues()); |
| | | downloadFileFromBlobPart({ fileName: 'æ¡£æ¡.xls', source: data }); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getDocumentPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<ArchiveDocumentApi.Document>, |
| | | }); |
| | | </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: ['archive:document:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: '导åº', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['archive:document:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #hasAttachment="{ row }"> |
| | | <Tag :color="row.hasAttachment ? 'success' : 'default'"> |
| | | {{ row.hasAttachment ? 'æ' : 'æ ' }} |
| | | </Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.detail'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | | auth: ['archive:document:query'], |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['archive:document:update'], |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['archive:document:delete'], |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.no]), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { ArchiveDocumentApi } from '#/api/archive/document'; |
| | | |
| | | import type { FormType } from '../data'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createDocument, |
| | | getDocument, |
| | | updateDocument, |
| | | } from '#/api/archive/document'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | defineOptions({ name: 'ArchiveDocumentForm' }); |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const formType = ref<FormType>('create'); |
| | | |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'create') { |
| | | return $t('ui.actionTitle.create', ['æ¡£æ¡']); |
| | | } |
| | | if (formType.value === 'edit') { |
| | | return $t('ui.actionTitle.edit', ['æ¡£æ¡']); |
| | | } |
| | | return 'æ¡£æ¡è¯¦æ
'; |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 120, |
| | | }, |
| | | wrapperClass: 'grid-cols-2', |
| | | layout: 'vertical', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | /** åªæäº¤å端 SaveReqVO æ¥åçåæ®µï¼é¿å
åä¼ no/status/source* çåªè¯»å段 */ |
| | | async function buildSubmitData() { |
| | | const values = (await formApi.getValues()) as ArchiveDocumentApi.Document; |
| | | return { |
| | | id: values.id, |
| | | title: values.title, |
| | | categoryId: values.categoryId, |
| | | secretLevel: values.secretLevel, |
| | | carrierType: values.carrierType, |
| | | shelfNo: values.shelfNo, |
| | | layerNo: values.layerNo, |
| | | columnNo: values.columnNo, |
| | | boxNo: values.boxNo, |
| | | storePlace: values.storePlace, |
| | | archiveDate: values.archiveDate, |
| | | summary: values.summary, |
| | | tags: values.tags, |
| | | remark: values.remark, |
| | | // é件为å
¨éæ¿æ¢è¯ä¹ï¼æ¸
空å
¨é¨éä»¶æ¶å¿
é¡»æ¾å¼ä¼ 空æ°ç» |
| | | blobIds: values.blobIds ?? [], |
| | | } as ArchiveDocumentApi.Document; |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = await buildSubmitData(); |
| | | try { |
| | | await (formType.value === 'create' |
| | | ? createDocument(data) |
| | | : updateDocument(data)); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | formApi.setState({ schema: useFormSchema(formType.value) }); |
| | | formApi.setDisabled(formType.value === 'detail'); |
| | | if (data?.id) { |
| | | modalApi.lock(); |
| | | try { |
| | | const detail = await getDocument(data.id); |
| | | // å·²æé件转æä¸ä¼ ç»ä»¶å¯è¯å«çæä»¶å¯¹è±¡ï¼æäº¤æ¶æ valueKey=id æå为 blobId |
| | | const blobIds = (detail.attachmentList ?? []).map((item) => ({ |
| | | uid: String(item.id), |
| | | name: item.name || '', |
| | | url: item.url || '', |
| | | status: 'done', |
| | | id: item.id, |
| | | })); |
| | | await formApi.setValues({ ...detail, blobIds }, false); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } else { |
| | | await formApi.resetForm(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal |
| | | :title="getTitle" |
| | | class="w-3/4" |
| | | :close-on-click-modal="false" |
| | | :show-confirm-button="formType !== 'detail'" |
| | | > |
| | | <Form class="mx-3" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { EChartsOption } from '@vben/plugins/echarts'; |
| | | |
| | | import type { ArchiveStatApi } from '#/api/archive/stat'; |
| | | |
| | | /** é¡¶é¨ç»è®¡å¡ç */ |
| | | export interface StatCard { |
| | | key: keyof ArchiveStatApi.StatOverview; |
| | | label: string; |
| | | color: string; |
| | | } |
| | | |
| | | export const STAT_CARDS: StatCard[] = [ |
| | | { key: 'totalCount', label: 'æ¡£æ¡æ»æ°', color: '#1677ff' }, |
| | | { key: 'inStockCount', label: 'å¨åºæ¡£æ¡', color: '#52c41a' }, |
| | | { key: 'borrowedCount', label: 'ååºä¸æ¡£æ¡', color: '#faad14' }, |
| | | { key: 'overdueCount', label: '龿æªè¿', color: '#ff4d4f' }, |
| | | ]; |
| | | |
| | | /** éç¨é¥¼å¾é
ç½® */ |
| | | export function buildPieOption( |
| | | title: string, |
| | | rows: { name: string; value: number }[], |
| | | ): EChartsOption { |
| | | return { |
| | | title: { text: title, left: 'center', textStyle: { fontSize: 14 } }, |
| | | tooltip: { trigger: 'item' }, |
| | | legend: { orient: 'vertical', left: 'left', top: 'middle' }, |
| | | series: [ |
| | | { |
| | | name: title, |
| | | type: 'pie', |
| | | radius: ['40%', '65%'], |
| | | center: ['60%', '55%'], |
| | | data: rows, |
| | | emphasis: { |
| | | itemStyle: { shadowBlur: 10, shadowColor: 'rgba(0, 0, 0, 0.5)' }, |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | } |
| | | |
| | | /** éç¨æ±ç¶å¾é
ç½® */ |
| | | export function buildBarOption( |
| | | title: string, |
| | | rows: { name: string; value: number }[], |
| | | ): EChartsOption { |
| | | return { |
| | | title: { text: title, left: 'center', textStyle: { fontSize: 14 } }, |
| | | tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, |
| | | grid: { left: 20, right: 20, bottom: 20, containLabel: true }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: rows.map((item) => item.name), |
| | | axisTick: { alignWithLabel: true }, |
| | | }, |
| | | yAxis: { type: 'value', minInterval: 1 }, |
| | | series: [ |
| | | { |
| | | name: title, |
| | | type: 'bar', |
| | | barMaxWidth: 48, |
| | | data: rows.map((item) => item.value), |
| | | label: { show: true, position: 'top' }, |
| | | }, |
| | | ], |
| | | }; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { EchartsUIType } from '@vben/plugins/echarts'; |
| | | |
| | | import type { ArchiveStatApi } from '#/api/archive/stat'; |
| | | |
| | | import { computed, onMounted, ref } from 'vue'; |
| | | |
| | | import { ContentWrap, Page } from '@vben/common-ui'; |
| | | import { EchartsUI, useEcharts } from '@vben/plugins/echarts'; |
| | | |
| | | import { Spin } from 'ant-design-vue'; |
| | | |
| | | import { getArchiveStatOverview } from '#/api/archive/stat'; |
| | | |
| | | import { buildBarOption, buildPieOption, STAT_CARDS } from './data'; |
| | | |
| | | /** æ¡£æ¡ç»è®¡ */ |
| | | defineOptions({ name: 'ArchiveStat' }); |
| | | |
| | | const loading = ref(false); |
| | | const overview = ref<ArchiveStatApi.StatOverview>({}); |
| | | |
| | | const categoryChartRef = ref<EchartsUIType>(); |
| | | const secretChartRef = ref<EchartsUIType>(); |
| | | const carrierChartRef = ref<EchartsUIType>(); |
| | | |
| | | const { renderEcharts: renderCategory } = useEcharts(categoryChartRef); |
| | | const { renderEcharts: renderSecret } = useEcharts(secretChartRef); |
| | | const { renderEcharts: renderCarrier } = useEcharts(carrierChartRef); |
| | | |
| | | /** å¡çåå¼ */ |
| | | const cards = computed(() => |
| | | STAT_CARDS.map((card) => ({ |
| | | ...card, |
| | | value: (overview.value[card.key] as number | undefined) ?? 0, |
| | | })), |
| | | ); |
| | | |
| | | async function loadData() { |
| | | loading.value = true; |
| | | try { |
| | | const data = await getArchiveStatOverview(); |
| | | overview.value = data; |
| | | await Promise.all([ |
| | | renderCategory( |
| | | buildPieOption( |
| | | 'æåç±»åå¸', |
| | | (data.byCategory ?? []).map((item) => ({ |
| | | name: item.name ?? 'æªå½å', |
| | | value: item.total ?? 0, |
| | | })), |
| | | ), |
| | | ), |
| | | renderSecret( |
| | | buildBarOption( |
| | | 'æå¯çº§åå¸', |
| | | (data.bySecretLevel ?? []).map((item) => ({ |
| | | name: item.name ?? 'æªè®¾ç½®', |
| | | value: item.total ?? 0, |
| | | })), |
| | | ), |
| | | ), |
| | | renderCarrier( |
| | | buildPieOption( |
| | | 'æè½½ä½åå¸', |
| | | (data.byCarrierType ?? []).map((item) => ({ |
| | | name: item.name ?? 'æªè®¾ç½®', |
| | | value: item.total ?? 0, |
| | | })), |
| | | ), |
| | | ), |
| | | ]); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | } |
| | | |
| | | onMounted(loadData); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <Spin :spinning="loading" tip="å è½½ä¸..."> |
| | | <div class="space-y-4"> |
| | | <!-- ç»è®¡å¡ç --> |
| | | <div class="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4"> |
| | | <ContentWrap |
| | | v-for="card in cards" |
| | | :key="card.key" |
| | | class="flex items-center justify-between" |
| | | > |
| | | <div> |
| | | <p class="text-sm text-gray-500">{{ card.label }}</p> |
| | | <p |
| | | class="mt-1 text-2xl font-semibold" |
| | | :style="{ color: card.color }" |
| | | > |
| | | {{ card.value }} |
| | | </p> |
| | | </div> |
| | | <span |
| | | class="inline-block h-10 w-1 rounded" |
| | | :style="{ backgroundColor: card.color }" |
| | | /> |
| | | </ContentWrap> |
| | | </div> |
| | | |
| | | <!-- åå¸å¾è¡¨ --> |
| | | <div class="grid grid-cols-1 gap-4 xl:grid-cols-2"> |
| | | <ContentWrap> |
| | | <EchartsUI class="h-72 w-full" ref="categoryChartRef" /> |
| | | </ContentWrap> |
| | | <ContentWrap> |
| | | <EchartsUI class="h-72 w-full" ref="secretChartRef" /> |
| | | </ContentWrap> |
| | | </div> |
| | | <ContentWrap> |
| | | <EchartsUI class="h-72 w-full" ref="carrierChartRef" /> |
| | | </ContentWrap> |
| | | </div> |
| | | </Spin> |
| | | </Page> |
| | | </template> |
| | |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await archiveReimbursement(row.id!); |
| | | message.success('彿¡£æå'); |
| | | const archiveNo = await archiveReimbursement(row.id!); |
| | | message.success(`已彿¡£å¹¶çææ¡£æ¡ ${archiveNo}`); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | archiveApplication, |
| | | deleteApplication, |
| | | getApplicationApproveProcessList, |
| | | getApplicationPage, |
| | |
| | | name: 'BpmProcessInstanceDetail', |
| | | query: { id: row.processInstanceId }, |
| | | }); |
| | | } |
| | | |
| | | /** 彿¡£å°æ¡£æ¡ç®¡çï¼ä»
å·²ç«é¡¹å¯å½æ¡£ï¼ */ |
| | | async function handleArchive(row: ResearchApplicationApi.Application) { |
| | | const hideLoading = message.loading({ content: '彿¡£ä¸...', duration: 0 }); |
| | | try { |
| | | const archiveNo = await archiveApplication(row.id!); |
| | | message.success(`彿¡£æåï¼å·²çææ¡£æ¡ ${archiveNo}`); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | const checkedIds = ref<number[]>([]); |
| | |
| | | onClick: handlePublishNotice.bind(null, row), |
| | | }, |
| | | { |
| | | label: '彿¡£', |
| | | type: 'link', |
| | | icon: ACTION_ICON.UPLOAD, |
| | | auth: ['research:application:archive'], |
| | | ifShow: () => |
| | | row.status === ApplicationStatus.APPROVED && |
| | | !row.archiveDocumentId, |
| | | popConfirm: { |
| | | title: `ç¡®è®¤å½æ¡£è¯¾é¢ ${row.researchNo || row.no} åï¼å½æ¡£åå°å¨æ¡£æ¡ç®¡ççæå¯¹åºæ¡£æ¡`, |
| | | confirm: handleArchive.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: 'æ¥ç审æ¹', |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { deleteMember, getMemberPage } from '#/api/ums/member'; |
| | | import { |
| | | archiveMember, |
| | | deleteMember, |
| | | getMemberPage, |
| | | } from '#/api/ums/member'; |
| | | |
| | | import { MemberType, useGridColumns, useGridFormSchema } from './data'; |
| | | import { |
| | | MemberStatus, |
| | | MemberType, |
| | | useGridColumns, |
| | | useGridFormSchema, |
| | | } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | /** ä¼åå表 */ |
| | |
| | | const checkedIds = ref<number[]>([]); |
| | | function handleRowCheckboxChange({ records }: { records: UmsMemberApi.Member[] }) { |
| | | checkedIds.value = records.map((item) => item.id!); |
| | | } |
| | | |
| | | /** 彿¡£å°æ¡£æ¡ç®¡çï¼ä»
æ£å¸¸ç¶æä¼åå¯å½æ¡£ï¼ */ |
| | | async function handleArchive(row: UmsMemberApi.Member) { |
| | | const hideLoading = message.loading({ content: '彿¡£ä¸...', duration: 0 }); |
| | | try { |
| | | const archiveNo = await archiveMember(row.id!); |
| | | message.success(`彿¡£æåï¼å·²çææ¡£æ¡ ${archiveNo}`); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** ç级åå
¸æ å°ï¼æ ¹æ®ä¼åç±»åæ¾ç¤ºå¯¹åºçç级åç§° */ |
| | |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: '彿¡£', |
| | | type: 'link', |
| | | icon: ACTION_ICON.UPLOAD, |
| | | auth: ['ums:member:archive'], |
| | | ifShow: () => |
| | | row.status === MemberStatus.NORMAL && !row.archiveDocumentId, |
| | | popConfirm: { |
| | | title: `ç¡®è®¤å½æ¡£ä¼å ${row.name} åï¼å½æ¡£åå°å¨æ¡£æ¡ç®¡ççæå¯¹åºæ¡£æ¡`, |
| | | confirm: handleArchive.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: 'å é¤', |
| | | type: 'link', |
| | | danger: true, |