From bfbb1ea3c6bb2dd7db064fb189290a1bfcf6c9cd Mon Sep 17 00:00:00 2001 From: seatonwan9 Date: 星期四, 28 八月 2025 02:14:48 +0800 Subject: [PATCH] 提交源码 --- src/api/userInfo.ts | 3 src/views/productManage/product/index.vue | 82 -- src/views/tradeManage/track/index.vue | 72 ++ src/views/tradeManage/seller/index.vue | 195 +++++-- src/views/pointsManage/personal/index.vue | 43 + src/views/tradeManage/detail/index.vue | 52 + src/views/productManage/price/index.vue | 132 ++-- src/utils/orderWorkflow.ts | 258 +++++++++ .env.development | 2 vite.config.ts | 5 src/views/tradeManage/buyer/index.vue | 219 ++++++-- src/api/productApi.ts | 32 + src/views/productManage/productPriceViewer/index.vue | 308 +++++++---- src/api/orderApi.ts | 32 + src/api/cartApi.ts | 12 src/router/index.ts | 62 ++ src/App.vue | 8 src/stores/modules/userInfo.ts | 1 src/router/modules/transactionManage.ts | 9 src/views/loginT/index.vue | 4 20 files changed, 1,137 insertions(+), 394 deletions(-) diff --git a/.env.development b/.env.development index 18cb983..6982930 100644 --- a/.env.development +++ b/.env.development @@ -15,4 +15,4 @@ #璺宠浆iframe鍦板潃 VITE_IFREAM_URL='http://36.133.126.111:7099' # 鎵爜鐧诲綍椤靛湴鍧� 鐧诲綍瀹屼細鑷姩璺宠浆鍒板ぇ灞� -VITE_BASE_LOGIN_URL='https://portal.ccccltd.cn/sso_sys?cb=http://zynlpt.ccccltd.cn' \ No newline at end of file +VITE_BASE_LOGIN_URL='https://portal.ccccltd.cn/sso_sys?cb=http://zynlpt.ccccltd.cn' diff --git a/src/App.vue b/src/App.vue index d96e197..3eb6df3 100644 --- a/src/App.vue +++ b/src/App.vue @@ -29,13 +29,7 @@ }) } provide('reload', reload) -// 涓存椂鍒濆鍖栵細鍐欏叆 userId 涓� unitId锛屼究浜庢湰鍦拌仈璋冨悗绔笅鍗曟帴鍙� -if (!userStore.getUserId) { - userStore.userId = '1' -} -if (!userStore.getUnitId) { - userStore.unitId = '1' -} + setInterval(() => { // 杩欎釜鍒ゆ柇浠h〃鐧诲綍鎴愬姛鍚庢墠寮�濮嬪啓鍏ユ椂闂� if(userStore.getAdminToken) { diff --git a/src/api/cartApi.ts b/src/api/cartApi.ts index 2d46ee8..20cf968 100644 --- a/src/api/cartApi.ts +++ b/src/api/cartApi.ts @@ -5,7 +5,7 @@ const cartApi = { // 娣诲姞鍟嗗搧鍒拌喘鐗╄溅 - addToCart(data: any, userId: number, unitId: number): ApiPromise { + addToCart(data: any, userId: string, unitId: string): ApiPromise { return createAxios({ url: `${baseUrl}/add`, method: 'post', @@ -15,7 +15,7 @@ }, // 浠庤喘鐗╄溅绉婚櫎鍟嗗搧 - removeFromCart(userId: number, unitId: number, pricingId: number): ApiPromise { + removeFromCart(userId: string, unitId: string, pricingId: string): ApiPromise { return createAxios({ url: `${baseUrl}/remove`, method: 'delete', @@ -24,7 +24,7 @@ }, // 鏇存柊璐墿杞﹀晢鍝佹暟閲� - updateCartItem(userId: number, unitId: number, pricingId: number, quantity: number): ApiPromise { + updateCartItem(userId: string, unitId: string, pricingId: number, quantity: number): ApiPromise { return createAxios({ url: `${baseUrl}/update`, method: 'put', @@ -33,7 +33,7 @@ }, // 鑾峰彇璐墿杞︿俊鎭� - getCartInfo(userId: number, unitId: number): ApiPromise { + getCartInfo(userId: string, unitId: string): ApiPromise { return createAxios({ url: `${baseUrl}/info`, method: 'get', @@ -42,7 +42,7 @@ }, // 鑾峰彇璐墿杞﹀晢鍝佸垪琛� - getCartItems(userId: number, unitId: number): ApiPromise { + getCartItems(userId: string, unitId: string): ApiPromise { return createAxios({ url: `${baseUrl}/items`, method: 'get', @@ -51,7 +51,7 @@ }, // 娓呯┖璐墿杞� - clearCart(userId: number, unitId: number): ApiPromise { + clearCart(userId: string, unitId: string): ApiPromise { return createAxios({ url: `${baseUrl}/clear`, method: 'delete', diff --git a/src/api/orderApi.ts b/src/api/orderApi.ts index aad0872..208163b 100644 --- a/src/api/orderApi.ts +++ b/src/api/orderApi.ts @@ -29,9 +29,23 @@ data }) as ApiPromise }, + getBuyerOrderPageWithProductConditions(data: any): ApiPromise { + return createAxios({ + url: `${url}/buyer/page/with-product-conditions`, + method: 'post', + data + }) as ApiPromise + }, getSellerOrderPage(data: any): ApiPromise { return createAxios({ url: `${url}/seller/page`, + method: 'post', + data + }) as ApiPromise + }, + getSellerOrderPageWithProductConditions(data: any): ApiPromise { + return createAxios({ + url: `${url}/seller/page/with-product-conditions`, method: 'post', data }) as ApiPromise @@ -128,6 +142,24 @@ orderId } }) as ApiPromise + }, + + // 鍚姩骞跺畬鎴愬鎵规祦绋嬶紙澶栭儴宸ヤ綔娴佹湇鍔★級 + startWorkflowAndComplete(data: { processdefId: string; userid: string; businessKey: string }): ApiPromise { + return createAxios({ + url: `/test/app/workflow/startProcessAndComplete`, + method: 'post', + data + }) as ApiPromise + }, + + // 鏇存柊璁㈠崟鐨勫伐浣滄祦ID + updateWorkflowId(orderId: string, workflowId: string): ApiPromise { + return createAxios({ + url: `${url}/workflow/update`, + method: 'post', + params: { orderId, workflowId } + }) as ApiPromise } } diff --git a/src/api/productApi.ts b/src/api/productApi.ts new file mode 100644 index 0000000..ec73b81 --- /dev/null +++ b/src/api/productApi.ts @@ -0,0 +1,32 @@ +import createAxios from '@/utils/axios' + +let url = '/test/report/product/' + +const productApi = { + // 鑾峰彇浜у搧鍒楄〃 + getProductList(data: any): ApiPromise { + return createAxios({ + url: `${url}accessApplyList`, + method: 'POST', + data: data, + }) as ApiPromise + }, + + // 鏍规嵁ID鑾峰彇浜у搧璇︽儏 + getProductById(data: any): ApiPromise { + return createAxios({ + url: `${url}get`, + method: 'post', + data: data, + }) as ApiPromise + }, + getCategoryByParent(data: any):ApiPromise{ + return createAxios({ + url: `${url}categoryByParent`, + method: 'post', + data: data, + }) as ApiPromise + } +} + +export default productApi diff --git a/src/api/userInfo.ts b/src/api/userInfo.ts index 16345b7..6e85b61 100644 --- a/src/api/userInfo.ts +++ b/src/api/userInfo.ts @@ -1,4 +1,5 @@ import createAxios from '@/utils/axios' +let url = '/test' // 淇敼瀵嗙爜 export function updatePassw(data: object = {}): ApiPromise { @@ -14,7 +15,7 @@ // 鏌ョ湅涓汉淇℃伅 export function queryUserDetail(data: object = {}): ApiPromise { return createAxios({ - url: `/admin/common/userDetail`, + url: `${url}/admin/common/userDetail`, headers: { 'Content-Type': 'application/json;charset=UTF-8' }, diff --git a/src/router/index.ts b/src/router/index.ts index 6f447b2..cd9b7b9 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -12,6 +12,7 @@ import { useUserInfo } from '@/stores/modules/userInfo' import { useCommonInfo } from '@/stores/modules/common' // 鐘舵�佺鐞嗗櫒 import { useNavTabs } from '@/stores/modules/navTabs' // 鏍囩瀵艰埅 +import { queryUserDetail } from '@/api/userInfo' const router = createRouter({ history: createWebHistory('/manage/'), @@ -20,7 +21,7 @@ return { top: 0 } }, }) -router.beforeEach((to, from, next) => { +router.beforeEach(async (to, from, next) => { console.log("to",to) const userStore = useUserInfo() // * 鍦ㄨ烦杞矾鐢变箣鍓嶏紝娓呴櫎鎵�鏈夌殑璇锋眰 @@ -41,11 +42,17 @@ } // 濡傛灉娌℃湁token锛岃缃竴涓粯璁oken骞剁户缁� if (!userStore.getAdminToken) { - const obj: any = { - adminToken: 'dev-token', - screenToken: '', + const localUserInfo = localStorage.getItem('userInfo') + if (localUserInfo) { + const userInfo = JSON.parse(localUserInfo) + if (userInfo.adminToken) { + const obj: any = { + adminToken: userInfo.adminToken, + screenToken: '', + } + userStore.updateUserInfo(obj) + } } - userStore.updateUserInfo(obj) } next() return @@ -58,12 +65,17 @@ } else { if (to.query.token) { const token = to.query.token as string - localStorage.setItem('lastRecordTime', new Date().getTime()) + localStorage.setItem('lastRecordTime', new Date().getTime().toString()) const obj: any = { adminToken: token, screenToken: '', } userStore.updateUserInfo(obj) + const localUserInfo = localStorage.getItem('userInfo') + if (localUserInfo) { + const userInfo = JSON.parse(localUserInfo) + userStore.updateUserDetail(userInfo) + } // chrome document.body.scrollTop = 0 // firefox @@ -72,9 +84,43 @@ window.pageYOffset = 0 next() } else if (!userStore.getAdminToken) { - // 濡傛灉娌℃湁锛屽垯璺宠嚦鐧诲綍椤甸潰 - next({ path: '/loginT' }) + // 浠� localStorage 涓幏鍙� userInfo 鐨� adminToken + const localUserInfo = localStorage.getItem('userInfo') + if (localUserInfo) { + try { + const userInfo = JSON.parse(localUserInfo) + if (userInfo.adminToken) { + // 濡傛灉鑾峰彇鍒� adminToken锛屽垯鏇存柊 userStore + userStore.updateUserInfo({ + adminToken: userInfo.adminToken, + screenToken: userInfo.screenToken || '', + fullUnitName: userInfo.fullUnitName || '', + empCode: userInfo.empCode || '', + menuList: userInfo.menuList || [] + }) + // 鍚屾椂鏇存柊鐢ㄦ埛璇︾粏淇℃伅 + userStore.updateUserDetail(userInfo) + next() + } else { + // 濡傛灉娌℃湁 adminToken锛屽垯鐢ㄦ埛鏈櫥褰� + next({ path: '/loginT' }) + } + } catch (error) { + console.error('瑙f瀽 localStorage 涓殑 userInfo 澶辫触:', error) + // 瑙f瀽澶辫触锛岃涓虹敤鎴锋湭鐧诲綍 + next({ path: '/loginT' }) + } + } else { + // 濡傛灉娌℃湁 userInfo锛屽垯鐢ㄦ埛鏈櫥褰� + next({ path: '/loginT' }) + } } else { + // 鑾峰彇鐢ㄦ埛淇℃伅 + const localUserInfo = localStorage.getItem('userInfo') + if (localUserInfo) { + const userInfo = JSON.parse(localUserInfo) + userStore.updateUserDetail(userInfo) + } next() } } diff --git a/src/router/modules/transactionManage.ts b/src/router/modules/transactionManage.ts index 0485a5b..a3e0b1a 100644 --- a/src/router/modules/transactionManage.ts +++ b/src/router/modules/transactionManage.ts @@ -12,6 +12,15 @@ }, }, { + path: 'track/:id', + name: 'tradeTrack', + component: () => import('@/views/tradeManage/track/index.vue'), + meta: { + title: '璁㈠崟瀹℃壒杞ㄨ抗', + keepAlive: false, + }, + }, + { path: 'seller', name: 'tradeSellerCenter', component: () => import('@/views/tradeManage/seller/index.vue'), diff --git a/src/stores/modules/userInfo.ts b/src/stores/modules/userInfo.ts index 22e00c0..4d62116 100644 --- a/src/stores/modules/userInfo.ts +++ b/src/stores/modules/userInfo.ts @@ -3,7 +3,6 @@ import { defineStore } from 'pinia' import { UserInfo } from '@/stores/interface' import router from '@/router/index' -import { updateUserDetail } from '@/api/userInfo' export const useUserInfo = defineStore('userInfo', { state: (): UserInfo => { diff --git a/src/utils/orderWorkflow.ts b/src/utils/orderWorkflow.ts new file mode 100644 index 0000000..965a67e --- /dev/null +++ b/src/utils/orderWorkflow.ts @@ -0,0 +1,258 @@ +/** + * 璁㈠崟宸ヤ綔娴佺▼鎺у埗鍣� + * 绠$悊璁㈠崟鐘舵�佹祦杞拰鎿嶄綔鏉冮檺 + */ + +// 璁㈠崟鐘舵�佹灇涓撅紙鎸夌収娴佺▼椤哄簭锛� +export enum OrderStatus { + WAIT_UPLOAD = '寰呬笂浼犳枃浠�', // 1 + WAIT_AUTHORIZE = '寰呮巿鏉�', // 2 + WAIT_CONFIRM = '寰呬氦鏄撶‘璁�', // 3 + COMPLETED = '宸插畬鎴�', // 4 + EVALUATED = '宸茶瘎浠�' // 5 (鏈�缁堢姸鎬�) +} + +// 鎿嶄綔绫诲瀷鏋氫妇 +export enum ActionType { + VIEW = '鏌ョ湅', + TRACK = '杩借釜', + UPLOAD_FILE = '鎻愪氦鏂囦欢', + AUTHORIZE = '鎺堟潈', + CONFIRM_TRADE = '浜ゆ槗纭', + EVALUATE = '璇勪环', + CANCEL_ORDER = '鍙栨秷璁㈠崟' +} + +// 椤甸潰绫诲瀷鏋氫妇 +export enum PageType { + TRADE_APPROVAL = 'tradeApproval', // 浜ゆ槗瀹℃牳 + BUYER_CENTER = 'buyerCenter', // 涔板涓績 + SELLER_CENTER = 'sellerCenter' // 鍗栧涓績 +} + +// 鎿嶄綔閰嶇疆鎺ュ彛 +interface ActionConfig { + type: ActionType + routeName?: string + params?: Record<string, any> +} + +// 鍚勯〉闈㈠湪涓嶅悓鐘舵�佷笅鐨勬搷浣滈厤缃� +const PAGE_ACTION_CONFIG: Record<PageType, Record<OrderStatus, ActionConfig[]>> = { + [PageType.TRADE_APPROVAL]: { + [OrderStatus.WAIT_UPLOAD]: [ + { type: ActionType.VIEW, routeName: 'tradeOrderDetail' }, + { type: ActionType.TRACK } + ], + [OrderStatus.WAIT_AUTHORIZE]: [ + { type: ActionType.VIEW, routeName: 'tradeOrderDetail' }, + { type: ActionType.TRACK }, + { type: ActionType.AUTHORIZE, routeName: 'tradeApproval' } + ], + [OrderStatus.WAIT_CONFIRM]: [ + { type: ActionType.VIEW, routeName: 'tradeOrderDetail' }, + { type: ActionType.TRACK } + ], + [OrderStatus.COMPLETED]: [ + { type: ActionType.VIEW, routeName: 'tradeOrderDetail' }, + { type: ActionType.TRACK } + ], + [OrderStatus.EVALUATED]: [ + { type: ActionType.VIEW, routeName: 'tradeOrderDetail' }, + { type: ActionType.TRACK } + ] + }, + [PageType.BUYER_CENTER]: { + [OrderStatus.WAIT_UPLOAD]: [ + { type: ActionType.VIEW, routeName: 'tradeOrderDetail' }, + { type: ActionType.TRACK }, + { type: ActionType.UPLOAD_FILE, routeName: 'tradeOrderUpload' }, + { type: ActionType.CANCEL_ORDER } + ], + [OrderStatus.WAIT_AUTHORIZE]: [ + { type: ActionType.VIEW, routeName: 'tradeOrderDetail' }, + { type: ActionType.TRACK }, + { type: ActionType.CANCEL_ORDER } + ], + [OrderStatus.WAIT_CONFIRM]: [ + { type: ActionType.VIEW, routeName: 'tradeOrderDetail' }, + { type: ActionType.TRACK }, + { type: ActionType.CONFIRM_TRADE, routeName: 'tradeOrderConfirm' }, + { type: ActionType.CANCEL_ORDER } + ], + [OrderStatus.COMPLETED]: [ + { type: ActionType.VIEW, routeName: 'tradeOrderDetail' }, + { type: ActionType.TRACK }, + { type: ActionType.EVALUATE, routeName: 'tradeOrderEvaluate' } + ], + [OrderStatus.EVALUATED]: [ + { type: ActionType.VIEW, routeName: 'tradeOrderDetail' }, + { type: ActionType.TRACK } + ] + }, + [PageType.SELLER_CENTER]: { + [OrderStatus.WAIT_UPLOAD]: [ + { type: ActionType.VIEW, routeName: 'tradeOrderDetail' }, + { type: ActionType.TRACK } + ], + [OrderStatus.WAIT_AUTHORIZE]: [ + { type: ActionType.VIEW, routeName: 'tradeOrderDetail' }, + { type: ActionType.TRACK } + ], + [OrderStatus.WAIT_CONFIRM]: [ + { type: ActionType.VIEW, routeName: 'tradeOrderDetail' }, + { type: ActionType.TRACK } + ], + [OrderStatus.COMPLETED]: [ + { type: ActionType.VIEW, routeName: 'tradeOrderDetail' }, + { type: ActionType.TRACK } + ], + [OrderStatus.EVALUATED]: [ + { type: ActionType.VIEW, routeName: 'tradeOrderDetail' }, + { type: ActionType.TRACK } + ] + } +} + +// 鐘舵�佹祦杞槧灏勶紙褰撳墠鐘舵�� -> 涓嬩竴涓姸鎬侊級 +const STATUS_FLOW_MAP: Record<OrderStatus, OrderStatus | null> = { + [OrderStatus.WAIT_UPLOAD]: OrderStatus.WAIT_AUTHORIZE, + [OrderStatus.WAIT_AUTHORIZE]: OrderStatus.WAIT_CONFIRM, + [OrderStatus.WAIT_CONFIRM]: OrderStatus.COMPLETED, + [OrderStatus.COMPLETED]: OrderStatus.EVALUATED, + [OrderStatus.EVALUATED]: null // 鏈�缁堢姸鎬侊紝鏃犱笅涓�鐘舵�� +} + +/** + * 璁㈠崟宸ヤ綔娴佺▼鎺у埗鍣ㄧ被 + */ +export class OrderWorkflowController { + + /** + * 鑾峰彇鎸囧畾椤甸潰鍜岀姸鎬佷笅鐨勫彲鐢ㄦ搷浣滃垪琛� + * @param pageType 椤甸潰绫诲瀷 + * @param currentStatus 褰撳墠璁㈠崟鐘舵�� + * @returns 鎿嶄綔閰嶇疆鍒楄〃 + */ + static getAvailableActions(pageType: PageType, currentStatus: OrderStatus): ActionConfig[] { + const pageConfig = PAGE_ACTION_CONFIG[pageType] + if (!pageConfig) { + console.warn(`鏈壘鍒伴〉闈㈢被鍨� ${pageType} 鐨勯厤缃甡) + return [] + } + + const statusConfig = pageConfig[currentStatus] + if (!statusConfig) { + console.warn(`鏈壘鍒扮姸鎬� ${currentStatus} 鍦ㄩ〉闈� ${pageType} 涓殑閰嶇疆`) + return [] + } + + return statusConfig + } + + /** + * 鍒ゆ柇鏄惁鍙互鎵ц鎸囧畾鎿嶄綔 + * @param pageType 椤甸潰绫诲瀷 + * @param currentStatus 褰撳墠璁㈠崟鐘舵�� + * @param actionType 鎿嶄綔绫诲瀷 + * @returns 鏄惁鍙互鎵ц + */ + static canExecuteAction(pageType: PageType, currentStatus: OrderStatus, actionType: ActionType): boolean { + const actions = this.getAvailableActions(pageType, currentStatus) + return actions.some(action => action.type === actionType) + } + + /** + * 鑾峰彇涓嬩竴涓姸鎬� + * @param currentStatus 褰撳墠鐘舵�� + * @returns 涓嬩竴涓姸鎬侊紝濡傛灉宸叉槸鏈�缁堢姸鎬佸垯杩斿洖null + */ + static getNextStatus(currentStatus: OrderStatus): OrderStatus | null { + return STATUS_FLOW_MAP[currentStatus] || null + } + + /** + * 鍒ゆ柇褰撳墠鐘舵�佹槸鍚︿负鏈�缁堢姸鎬� + * @param currentStatus 褰撳墠鐘舵�� + * @returns 鏄惁涓烘渶缁堢姸鎬� + */ + static isFinalStatus(currentStatus: OrderStatus): boolean { + return currentStatus === OrderStatus.EVALUATED + } + + /** + * 楠岃瘉鐘舵�佹祦杞槸鍚﹀悎娉� + * @param fromStatus 璧峰鐘舵�� + * @param toStatus 鐩爣鐘舵�� + * @returns 鏄惁涓哄悎娉曠殑鐘舵�佹祦杞� + */ + static validateStatusTransition(fromStatus: OrderStatus, toStatus: OrderStatus): boolean { + const expectedNextStatus = this.getNextStatus(fromStatus) + return expectedNextStatus === toStatus + } + + /** + * 鑾峰彇鎵�鏈夌姸鎬佺殑椤哄簭鍒楄〃 + * @returns 鐘舵�佸垪琛� + */ + static getAllStatusInOrder(): OrderStatus[] { + return [ + OrderStatus.WAIT_UPLOAD, + OrderStatus.WAIT_AUTHORIZE, + OrderStatus.WAIT_CONFIRM, + OrderStatus.COMPLETED, + OrderStatus.EVALUATED + ] + } + + /** + * 鏍规嵁浜у搧濂椾欢鍒ゆ柇鍒濆鐘舵�� + * @param orderDetails 璁㈠崟璇︽儏鍒楄〃 + * @returns 鍒濆鐘舵�� + */ + static determineInitialStatus(orderDetails: any[]): OrderStatus { + // 妫�鏌ユ槸鍚︽湁浠锋牸绫诲瀷涓�"鍗忚"鐨勫浠� + const hasAgreementPrice = orderDetails.some(detail => { + const priceType = String(detail.priceType || '').toLowerCase() + return priceType.includes('鍗忚') || priceType.includes('agreement') + }) + + // 濡傛灉鏈夊崗璁环鏍硷紝浠�"寰呬笂浼犳枃浠�"寮�濮嬶紝鍚﹀垯浠�"寰呮巿鏉�"寮�濮� + return hasAgreementPrice ? OrderStatus.WAIT_UPLOAD : OrderStatus.WAIT_AUTHORIZE + } +} + +// 鐘舵�佹槧灏勫伐鍏峰嚱鏁帮紙鐢ㄤ簬鍓嶅悗绔姸鎬佽浆鎹級 +export const StatusMapper = { + /** + * 鍓嶇鏋氫妇鐘舵�佽浆鍚庣涓枃鐘舵�� + */ + toServerStatus: (uiStatus: string): string => { + // 鐩存帴杩斿洖涓枃鐘舵�侊紝鍥犱负鏋氫妇鍊兼湰韬氨鏄腑鏂� + return uiStatus + }, + + /** + * 鍚庣涓枃鐘舵�佽浆鍓嶇鏋氫妇鐘舵�� + */ + toUIStatus: (serverStatus: string): OrderStatus => { + // 鏍囧噯鍖栫姸鎬佸悕绉� + const normalizedStatus = serverStatus?.trim() + + switch (normalizedStatus) { + case '寰呬笂浼犳枃浠�': + return OrderStatus.WAIT_UPLOAD + case '寰呮巿鏉�': + return OrderStatus.WAIT_AUTHORIZE + case '寰呬氦鏄撶‘璁�': + return OrderStatus.WAIT_CONFIRM + case '宸插畬鎴�': + return OrderStatus.COMPLETED + case '宸茶瘎浠�': + return OrderStatus.EVALUATED + default: + console.warn(`鏈煡鐨勮鍗曠姸鎬�: ${serverStatus}锛岄粯璁よ繑鍥炲緟鎺堟潈鐘舵�乣) + return OrderStatus.WAIT_AUTHORIZE + } + } +} diff --git a/src/views/loginT/index.vue b/src/views/loginT/index.vue index 4b7b3f7..2eaf23e 100644 --- a/src/views/loginT/index.vue +++ b/src/views/loginT/index.vue @@ -111,7 +111,7 @@ <script lang="ts" setup> import { reactive, ref, toRefs, onMounted } from 'vue' import { login } from '@/api/login' -import { FormInstance, FormRules } from 'element-plus' +import { ElMessage, FormInstance, FormRules } from 'element-plus' import router from '@/router' import { useUserInfo } from '@/stores/modules/userInfo' import { getAssetsImages } from '@/utils/common' @@ -165,7 +165,7 @@ // 姝e父鐧诲綍鎺ュ彛璋冪敤 login(state.userInfo).then((res: any) => { if (res.code == 200) { - localStorage.setItem('lastRecordTime', new Date().getTime()) + localStorage.setItem('lastRecordTime', new Date().getTime().toString()) // 灏嗙紦瀛樿繘琛岀紦瀛樻垨鑰呯姸鎬佺鐞嗗櫒涓� userStore.removeMenuList() userStore.updateUserInfo(res.data) diff --git a/src/views/pointsManage/personal/index.vue b/src/views/pointsManage/personal/index.vue index 4ee14ac..1449cf4 100644 --- a/src/views/pointsManage/personal/index.vue +++ b/src/views/pointsManage/personal/index.vue @@ -157,6 +157,11 @@ } from '@element-plus/icons-vue' import pointsApi from '@/api/pointsApi' import type { PointsStats, PointsFlow, PointsQueryParams } from '@/types/points' +import { useUserInfo } from '@/stores/modules/userInfo' +import { queryUserDetail } from '@/api/userInfo' + +// 鑾峰彇鐢ㄦ埛淇℃伅 store +const userStore = useUserInfo() // 绉垎缁熻 const stats = ref<PointsStats>({ @@ -168,7 +173,7 @@ // 鏌ヨ鍙傛暟 const queryParams = reactive<PointsQueryParams>({ - userId: '1', + userId: '', dataCategory: '', dataType: '', pageNum: 1, @@ -208,7 +213,13 @@ // 鑾峰彇绉垎缁熻 const getPointsStats = async () => { try { - const userId = 1; + const userId = userStore.getUserId || userStore.getUserInfo?.userId + if (!userId) { + console.error('鏃犳硶鑾峰彇鐢ㄦ埛ID') + return + } + // 鏇存柊鏌ヨ鍙傛暟涓殑 userId + queryParams.userId = userId.toString() const res = await pointsApi.getPersonalPointsStats(userId) if (res.code === 200 && res.data) { stats.value = res.data @@ -233,6 +244,13 @@ // 鑾峰彇绉垎娴佹按 const getPointsFlow = async () => { try { + const userId = userStore.getUserId || userStore.getUserInfo?.userId + if (!userId) { + console.error('鏃犳硶鑾峰彇鐢ㄦ埛ID') + return + } + // 鏇存柊鏌ヨ鍙傛暟涓殑 userId + queryParams.userId = userId.toString() const res = await pointsApi.getPersonalPointsFlow(queryParams) if (res.code === 200) { flowList.value = res.data.list || [] @@ -300,7 +318,26 @@ ElMessage.info('鍏抽棴鍏ㄩ儴鍔熻兘') } -onMounted(() => { +onMounted(async () => { + // 椤甸潰鎸傝浇鏃讹紝妫�鏌� userStore 鏄惁宸叉湁鐢ㄦ埛淇℃伅 + let userId = userStore.getUserId || userStore.getUserInfo?.userId + if (!userId) { + try { + const res: any = await queryUserDetail() + if (res?.code === 200 && res.data) { + userStore.updateUserDetail(res.data) + userId = res.data.userId || res.data.id + } else { + ElMessage.error(res?.msg || '鏃犳硶鑾峰彇鐢ㄦ埛淇℃伅锛岃鍏堢櫥褰�') + return + } + } catch (e) { + console.error('鑾峰彇鐢ㄦ埛璇︽儏澶辫触:', e) + ElMessage.error('鑾峰彇鐢ㄦ埛淇℃伅澶辫触锛岃绋嶅悗閲嶈瘯') + return + } + } + getPointsStats() getPointsFlow() getCategoryList() diff --git a/src/views/productManage/price/index.vue b/src/views/productManage/price/index.vue index 55821cb..5752e6e 100644 --- a/src/views/productManage/price/index.vue +++ b/src/views/productManage/price/index.vue @@ -19,14 +19,14 @@ <el-descriptions v-if="productDetail" :column="2" border> <el-descriptions-item label="浜у搧鍚嶇О" label-width="10%">{{ productDetail.name }}</el-descriptions-item> - <el-descriptions-item label="鎻愭姤鍗曚綅" label-width="10%">{{ productDetail.submitUnit }}</el-descriptions-item> - <el-descriptions-item label="鎻愭姤浜�" label-width="10%">{{ productDetail.submitter }}</el-descriptions-item> - <el-descriptions-item label="琛屼笟棰嗗煙" label-width="10%">{{ productDetail.industry }}</el-descriptions-item> - <el-descriptions-item label="鍗曚綅宸ョ▼" label-width="10%">{{ productDetail.projectUnit }}</el-descriptions-item> - <el-descriptions-item label="浜т笟闃舵" label-width="10%">{{ productDetail.industryStage }}</el-descriptions-item> - <el-descriptions-item label="浜у搧绫诲瀷"label-width="10%">{{ productDetail.productType }}</el-descriptions-item> + <el-descriptions-item label="鎻愭姤鍗曚綅" label-width="10%">{{ productDetail.submissionUnit }}</el-descriptions-item> + <el-descriptions-item label="鎻愭姤浜�" label-width="10%">{{ productDetail.createBy }}</el-descriptions-item> + <el-descriptions-item label="琛屼笟棰嗗煙" label-width="10%">{{ productDetail.industrialChainName }}</el-descriptions-item> + <el-descriptions-item label="鍗曚綅宸ョ▼" label-width="10%">{{ productDetail.importantAreaName }}</el-descriptions-item> + <el-descriptions-item label="浜т笟闃舵" label-width="10%">{{ productDetail.businessProcessName }}</el-descriptions-item> + <el-descriptions-item label="浜у搧绫诲瀷"label-width="10%">{{ productDetail.typeName }}</el-descriptions-item> <el-descriptions-item label="浜у搧绠�浠�" :span="3"> - <div class="intro">{{ productDetail.description }}</div> + <div class="intro">{{ productDetail.describe }}</div> </el-descriptions-item> </el-descriptions> <el-empty v-else description="鏈壘鍒拌浜у搧鐨勮鎯�" /> @@ -49,16 +49,16 @@ class="pricing-group" > <div class="group-header">{{ group.suiteName }}</div> - <div class="pricing-cards-wrapper"> - <div + <div class="pricing-cards-wrapper"> + <div v-for="pricing in group.items" - :key="pricing.id" - class="pricing-card" - :class="{ - 'pricing-card-enabled': pricing.enableStatus === 'ENABLED', - 'pricing-card-disabled': pricing.enableStatus === 'DISABLED' - }" - > + :key="pricing.id" + class="pricing-card" + :class="{ + 'pricing-card-enabled': pricing.enableStatus === 'ENABLED', + 'pricing-card-disabled': pricing.enableStatus === 'DISABLED' + }" + > <div class="pricing-card-table"> <div class="pricing-row"> <div class="pricing-cell label-cell">閿�鍞舰寮�</div> @@ -144,16 +144,16 @@ class="price-form" > - <el-form-item label="浜у搧濂椾欢" prop="productSuite"> + <el-form-item label="浜у搧濂椾欢" prop="productSuite"> <el-select v-model="formData.productSuite" placeholder="璇烽�夋嫨" style="width: 60%"> <el-option v-for="opt in productSuiteOptions" :key="opt.value" :label="opt.label" :value="opt.value" /> </el-select> </el-form-item> - <el-form-item label="瀹㈡埛瀵硅薄" prop="customerObject"> + <el-form-item label="瀹㈡埛瀵硅薄" prop="customerObject"> <el-radio-group v-model="formData.customerObject"> <el-radio v-for="opt in customerObjectOptions" :key="opt.value" :label="opt.value">{{ opt.label }}</el-radio> </el-radio-group> - </el-form-item> + </el-form-item> <el-form-item label="閿�鍞舰寮�" prop="salesForm" class="sales-form-item"> <el-radio-group v-model="formData.salesForm" class="sales-form-group"> <el-radio v-for="opt in salesFormOptions" :key="opt.value" :label="opt.value">{{ opt.label }}</el-radio> @@ -208,7 +208,7 @@ <el-radio v-for="opt in enableStatusOptions" :key="opt.value" :label="opt.value">{{ opt.label }}</el-radio> </el-radio-group> </el-form-item> - + <el-form-item label="澶囨敞"> <el-input type="textarea" v-model="formData.remark" :rows="3" placeholder="璇疯緭鍏ュ娉�" /> @@ -239,18 +239,19 @@ import { Goods, Lock, Unlock } from '@element-plus/icons-vue' import ProductPriceViewer from '@/views/productManage/productPriceViewer/index.vue' import productPricingApi from '@/api/productPricingApi' +import productApi from '@/api/productApi' interface ProductDetail { id: string name: string - submitUnit: string - submitter: string - industry: string - projectUnit: string - industryStage: string - productType: string - description: string + submissionUnit: string + createBy: string + industrialChainName: string + importantAreaName: string + businessProcessName: string + typeName: string + describe: string shelfStatus?: '寰呬笂鏋�' | '宸蹭笂鏋�' | '宸蹭笅鏋�' } @@ -297,32 +298,32 @@ }) // 妯℃嫙浜у搧璇︽儏鏁版嵁婧� -const mockProductMap: Record<string, ProductDetail> = { - '1': { - id: '1', - name: '鏁板瓧鍖栦骇鍝丄', - submitUnit: '涓氦涓�鍏眬', - submitter: '寮犱笁', - industry: '浜ら�氬熀纭�璁炬柦', - projectUnit: '鏌愰珮閫熷叕璺伐绋�', - industryStage: '搴旂敤闃舵', - productType: '杞欢/骞冲彴', - description: '鏈骇鍝佸畾浣嶄负浠ュ缓璁炬湡BIM鏁板瓧璧勪骇浣滀负鏁板瓧搴曠洏锛岀粨鍚堥」鐩繍钀ョ淮淇濋渶姹傜殑瀹炴椂鎬с�佷氦浜掓�с�佷究鎹锋�х殑涓夌淮鍙鍖栬繍缁寸鐞嗙郴缁熴�傜郴缁熸彁渚涢」鐩暟瀛楀寲銆佹櫤鑳藉寲杩愮淮绠$悊鍔熻兘锛岃兘澶熻В鍐冲缓绛戣繍琛岀淮鎶ょ鐞嗕腑鐨勫疄闄呴棶棰橈紝瀹炵幇淇℃伅蹇�熸暣鍚堜笌鏌ヨ銆佷俊鎭湁鏁堝叡浜笌浼犻�掞紝鎻愬崌椤圭洰缁煎悎绠$悊涓庣淮鎶ゆ按骞炽��', - shelfStatus: '寰呬笂鏋�' - }, - '2': { - id: '2', - name: '鏁板瓧鍖栦骇鍝丅', - submitUnit: '涓氦浜岃埅灞�', - submitter: '鏉庡洓', - industry: '甯傛斂宸ョ▼', - projectUnit: '鏅烘収绠″粖椤圭洰', - industryStage: '鐮斿彂闃舵', - productType: '纭欢/浼犳劅', - description: '闈㈠悜鍩庡競绠″粖鐩戞祴鐨勪紶鎰熻澶囦笌閲囬泦缃戝叧锛屾敮鎸佽竟缂樿绠椾笌杩滅▼杩愮淮銆�', - shelfStatus: '宸蹭笂鏋�' - } -} +// const mockProductMap: Record<string, ProductDetail> = { +// '1': { +// id: '1', +// name: '鏁板瓧鍖栦骇鍝丄', +// submitUnit: '涓氦涓�鍏眬', +// submitter: '寮犱笁', +// industry: '浜ら�氬熀纭�璁炬柦', +// projectUnit: '鏌愰珮閫熷叕璺伐绋�', +// industryStage: '搴旂敤闃舵', +// productType: '杞欢/骞冲彴', +// description: '鏈骇鍝佸畾浣嶄负浠ュ缓璁炬湡BIM鏁板瓧璧勪骇浣滀负鏁板瓧搴曠洏锛岀粨鍚堥」鐩繍钀ョ淮淇濋渶姹傜殑瀹炴椂鎬с�佷氦浜掓�с�佷究鎹锋�х殑涓夌淮鍙鍖栬繍缁寸鐞嗙郴缁熴�傜郴缁熸彁渚涢」鐩暟瀛楀寲銆佹櫤鑳藉寲杩愮淮绠$悊鍔熻兘锛岃兘澶熻В鍐冲缓绛戣繍琛岀淮鎶ょ鐞嗕腑鐨勫疄闄呴棶棰橈紝瀹炵幇淇℃伅蹇�熸暣鍚堜笌鏌ヨ銆佷俊鎭湁鏁堝叡浜笌浼犻�掞紝鎻愬崌椤圭洰缁煎悎绠$悊涓庣淮鎶ゆ按骞炽��', +// shelfStatus: '寰呬笂鏋�' +// }, +// '2': { +// id: '2', +// name: '鏁板瓧鍖栦骇鍝丅', +// submitUnit: '涓氦浜岃埅灞�', +// submitter: '鏉庡洓', +// industry: '甯傛斂宸ョ▼', +// projectUnit: '鏅烘収绠″粖椤圭洰', +// industryStage: '鐮斿彂闃舵', +// productType: '纭欢/浼犳劅', +// description: '闈㈠悜鍩庡競绠″粖鐩戞祴鐨勪紶鎰熻澶囦笌閲囬泦缃戝叧锛屾敮鎸佽竟缂樿绠椾笌杩滅▼杩愮淮銆�', +// shelfStatus: '宸蹭笂鏋�' +// } +// } const loading = ref(false) const priceList = ref<any[]>([]) @@ -503,14 +504,17 @@ } loading.value = true try { - productDetail.value = mockProductMap[productId] || null - // const detailRes: any = await productApi.getProductDetail({ id: productId }) - // if (detailRes?.code === 200) { - // productDetail.value = detailRes.data || null - // } else { - // productDetail.value = null - // ElMessage.error(detailRes?.msg || '鑾峰彇浜у搧璇︽儏澶辫触') - // } + // productDetail.value = mockProductMap[productId] || null + const data = { + id: productId + } + const detailRes: any = await productApi.getProductById(data) + if (detailRes?.code === 200) { + productDetail.value = detailRes.data || null + } else { + productDetail.value = null + ElMessage.error(detailRes?.msg || '鑾峰彇浜у搧璇︽儏澶辫触') + } await loadPricingList(productId) } catch (e) { productDetail.value = null @@ -638,7 +642,7 @@ return { id: row.id, - productId: Number(row.productId), + productId: row.productId, productName: productDetail.value?.name, suiteName: row.productSuite, salesForm: mapSalesFormToCN(row.salesForm), @@ -789,7 +793,7 @@ return { id: isEditMode.value && formData.id ? formData.id : undefined, - productId: Number(currentProductId.value), + productId: currentProductId.value, productName: productDetail.value?.name, suiteName: formData.productSuite, salesForm: mapSalesFormToCN(formData.salesForm), @@ -812,7 +816,7 @@ const res: any = await productPricingApi.update(payload) if (res?.code === 200) { ElMessage.success('淇敼鎴愬姛') - } else { + } else { ElMessage.error(res?.msg || '淇敼澶辫触') return } diff --git a/src/views/productManage/product/index.vue b/src/views/productManage/product/index.vue index 1bd5240..0ed5d6c 100644 --- a/src/views/productManage/product/index.vue +++ b/src/views/productManage/product/index.vue @@ -24,9 +24,9 @@ > <el-table-column prop="id" label="浜у搧ID" width="120" /> <el-table-column prop="name" label="浜у搧鍚嶇О" min-width="200" /> - <el-table-column prop="productType" label="浜у搧绫诲瀷" width="120" /> - <el-table-column prop="industry" label="琛屼笟棰嗗煙" width="150" /> - <el-table-column prop="submitUnit" label="鎻愭姤鍗曚綅" width="150" /> + <el-table-column prop="typeName" label="浜у搧绫诲瀷" width="120" /> + <el-table-column prop="importantAreaName" label="琛屼笟棰嗗煙" width="150" /> + <el-table-column prop="submissionUnit" label="鎻愭姤鍗曚綅" width="150" /> <el-table-column prop="shelfStatus" label="涓婃灦鐘舵��" width="100"> <template #default="{ row }"> <el-tag :type="getStatusType(row.shelfStatus)" size="small"> @@ -56,11 +56,11 @@ </el-card> <!-- 浜у搧浠锋牸鏌ョ湅寮圭獥 --> - <ProductPriceViewer + <!-- <ProductPriceViewer v-model="priceViewerVisible" :product-id="currentProductId" @order="handleOrder" - /> + /> --> </div> </template> @@ -69,6 +69,7 @@ import { useRouter } from 'vue-router' import { ElMessage } from 'element-plus' import ProductPriceViewer from '@/views/productManage/productPriceViewer/index.vue' +import productApi from '@/api/productApi' interface ProductItem { id: string @@ -91,57 +92,6 @@ const priceViewerVisible = ref(false) const currentProductId = ref('') -// 妯℃嫙浜у搧鏁版嵁 -const mockProductList: ProductItem[] = [ - { - id: '1', - name: '鏁板瓧鍖栦骇鍝丄', - productType: '杞欢/骞冲彴', - industry: '浜ら�氬熀纭�璁炬柦', - submitUnit: '涓氦涓�鍏眬', - submitter: '寮犱笁', - projectUnit: '鏌愰珮閫熷叕璺伐绋�', - industryStage: '搴旂敤闃舵', - description: '鏈骇鍝佸畾浣嶄负浠ュ缓璁炬湡BIM鏁板瓧璧勪骇浣滀负鏁板瓧搴曠洏锛岀粨鍚堥」鐩繍钀ョ淮淇濋渶姹傜殑瀹炴椂鎬с�佷氦浜掓�с�佷究鎹锋�х殑涓夌淮鍙鍖栬繍缁寸鐞嗙郴缁熴�傜郴缁熸彁渚涢」鐩暟瀛楀寲銆佹櫤鑳藉寲杩愮淮绠$悊鍔熻兘锛岃兘澶熻В鍐冲缓绛戣繍琛岀淮鎶ょ鐞嗕腑鐨勫疄闄呴棶棰橈紝瀹炵幇淇℃伅蹇�熸暣鍚堜笌鏌ヨ銆佷俊鎭湁鏁堝叡浜笌浼犻�掞紝鎻愬崌椤圭洰缁煎悎绠$悊涓庣淮鎶ゆ按骞炽��', - shelfStatus: '寰呬笂鏋�' - }, - { - id: '2', - name: '鏁板瓧鍖栦骇鍝丅', - productType: '纭欢/浼犳劅', - industry: '甯傛斂宸ョ▼', - submitUnit: '涓氦浜岃埅灞�', - submitter: '鏉庡洓', - projectUnit: '鏅烘収绠″粖椤圭洰', - industryStage: '鐮斿彂闃舵', - description: '闈㈠悜鍩庡競绠″粖鐩戞祴鐨勪紶鎰熻澶囦笌閲囬泦缃戝叧锛屾敮鎸佽竟缂樿绠椾笌杩滅▼杩愮淮銆�', - shelfStatus: '宸蹭笂鏋�' - }, - { - id: '3', - name: '鏁板瓧鍖栦骇鍝丆', - productType: '杞欢/骞冲彴', - industry: '寤虹瓚宸ョ▼', - submitUnit: '涓氦涓夎埅灞�', - submitter: '鐜嬩簲', - projectUnit: '鏅烘収寤虹瓚椤圭洰', - industryStage: '搴旂敤闃舵', - description: '鍩轰簬BIM鎶�鏈殑寤虹瓚宸ョ▼绠$悊骞冲彴锛屾彁渚涜璁°�佹柦宸ャ�佽繍缁村叏鐢熷懡鍛ㄦ湡绠$悊銆�', - shelfStatus: '宸蹭笅鏋�' - }, - { - id: '10004', - name: '鏁板瓧鍖栦骇鍝丏', - productType: '纭欢/浼犳劅', - industry: '姘村埄宸ョ▼', - submitUnit: '涓氦鍥涜埅灞�', - submitter: '璧靛叚', - projectUnit: '鏅烘収姘村埄椤圭洰', - industryStage: '鐮斿彂闃舵', - description: '姘村埄宸ョ▼鐩戞祴璁惧涓庢暟鎹噰闆嗙郴缁燂紝鏀寔瀹炴椂鐩戞祴鍜岄璀︺��', - shelfStatus: '宸蹭笂鏋�' - } -] // 鑾峰彇鐘舵�佺被鍨� const getStatusType = (status: string) => { @@ -162,8 +112,16 @@ loading.value = true try { // 妯℃嫙API璋冪敤 - await new Promise(resolve => setTimeout(resolve, 500)) - productList.value = mockProductList + // await new Promise(resolve => setTimeout(resolve, 500)) + let data = {"name":"","industrialChainId":"","importantAreaId":"","importantAreaIdList":[],"typeId":"","typeChildId":[],"startDate":"","endDate":"","page":{"current":1,"size":10,"total":3}} + const res: any = await productApi.getProductList(data) + if (res?.code === 200) { + productList.value = res.data.records || [] + } else { + productList.value = [] + ElMessage.error(res?.msg || '鑾峰彇浜у搧鍒楄〃澶辫触') + } + // productList.value = mockProductList } catch (error) { console.error('鍔犺浇浜у搧鍒楄〃澶辫触:', error) ElMessage.error('鍔犺浇浜у搧鍒楄〃澶辫触') @@ -187,8 +145,12 @@ // 鏌ョ湅瀹氫环 const handleViewPricing = (row: ProductItem) => { - currentProductId.value = row.id - priceViewerVisible.value = true + // currentProductId.value = row.id + // priceViewerVisible.value = true + router.push({ + path: '/product/priceViewer', + query: { productId: row.id} + }) } // 澶勭悊璁㈣喘 diff --git a/src/views/productManage/productPriceViewer/index.vue b/src/views/productManage/productPriceViewer/index.vue index a559117..71022ee 100644 --- a/src/views/productManage/productPriceViewer/index.vue +++ b/src/views/productManage/productPriceViewer/index.vue @@ -1,12 +1,5 @@ <template> - <el-dialog - v-model="visible" - :title="dialogTitle" - :before-close="handleClose" - destroy-on-close - class="product-price-dialog" - :width="dialogWidth" - > + <div> <div class="price-viewer-container" v-loading="loading"> <!-- 浠锋牸瀵规瘮琛ㄦ牸 --> <div class="pricing-table-container" v-if="showPricePanel && priceList.length > 0"> @@ -19,7 +12,7 @@ > <div class="pricing-table-wrapper"> <table class="pricing-table"> - <thead> + <thead> <tr> <th class="feature-column">鍔熻兘瀵规瘮</th> <th @@ -174,19 +167,19 @@ <span class="label">浜у搧鍚嶇О锛�</span><span class="value strong">{{ productHeader.name }}</span> </div> <div class="grid-item"> - <span class="label">琛屼笟鏉垮潡锛�</span><span class="value">{{ productHeader.industry }}</span> + <span class="label">琛屼笟鏉垮潡锛�</span><span class="value">{{ productHeader.industrialChainName }}</span> </div> <div class="grid-item"> - <span class="label">鎻愪緵鑰咃細</span><span class="value">{{ productHeader.provider }}</span> + <span class="label">鎻愪緵鑰咃細</span><span class="value">{{ productHeader.submissionUnit }}</span> </div> <div class="grid-item"> <span class="label">鐢ㄦ埛濮撳悕锛�</span><span class="value">{{ userInfo.name }}</span> </div> <div class="grid-item"> - <span class="label">鍗曚綅锛�</span><span class="value">{{ userInfo.unit }}</span> + <span class="label">鍗曚綅锛�</span><span class="value">{{ userInfo.unitName }}</span> </div> <div class="grid-item"> - <span class="label">閮ㄩ棬锛�</span><span class="value">{{ userInfo.department }}</span> + <span class="label">閮ㄩ棬锛�</span><span class="value">{{ userInfo.departmentName }}</span> </div> </div> </el-card> @@ -338,32 +331,29 @@ <el-empty description="鏆傛棤浠锋牸淇℃伅" /> </div> </div> - - <template #footer v-if="!showOrderStatus"> - <span class="dialog-footer"> - <template v-if="showPricePanel"> - <el-button @click="handleClose">鍏抽棴</el-button> - <el-button type="primary" @click="handleOrder">绔嬪嵆璁㈣喘</el-button> - </template> - <template v-else> - <el-button @click="returnPricePanel">杩斿洖浠锋牸瀵规瘮</el-button> - <el-button type="primary" @click="submitOrder">鎻愪氦鐢宠</el-button> - </template> - </span> - </template> - </el-dialog> + <div class="footer" v-if="showPricePanel"> + <el-button type="primary" @click="handleOrder">绔嬪嵆璁㈣喘</el-button> + </div> + <div class="footer" v-else> + <el-button @click="returnPricePanel">杩斿洖浠锋牸瀵规瘮</el-button> + <el-button type="primary" @click="submitOrder">鎻愪氦鐢宠</el-button> + </div> + </div> <!-- 浜у搧璁㈣喘瀵硅瘽妗嗙Щ闄わ紝鏀逛负鍐呭祵灞曠ず --> </template> <script setup lang="ts"> -import { ref, watch, computed } from 'vue' +import { ref, watch, computed, onMounted } from 'vue' import { ElMessage } from 'element-plus' import { useRoute } from 'vue-router' import productPricingApi from '@/api/productPricingApi' import cartApi from '@/api/cartApi' import orderApi from '@/api/orderApi' import { ShoppingCart, Coin, Money } from '@element-plus/icons-vue' +import { useUserInfo } from '@/stores/modules/userInfo' +import { queryUserDetail } from '@/api/userInfo' +import productApi from '@/api/productApi' const route = useRoute() @@ -384,36 +374,11 @@ enableStatus: 'ENABLED' | 'DISABLED' } -interface Props { - modelValue: boolean - productId?: string - width?: string -} - -interface Emits { - (e: 'update:modelValue', value: boolean): void - (e: 'order', selectedItems: PriceItem[]): void -} - // 鍏煎 path 鍙傛暟 productId 涓� query 鍙傛暟 id/productId const currentProductId = computed<string | undefined>(() => { return (route.params.productId as string) || (route.query.productId as string) || (route.query.id as string) }) - -const props = withDefaults(defineProps<Props>(), { - modelValue: false, - productId: '', - width: '90%' -}) - - -const emit = defineEmits<Emits>() - -const visible = computed({ - get: () => props.modelValue, - set: (value) => emit('update:modelValue', value) -}) const loading = ref(false) const activeTab = ref('enterprise') @@ -424,8 +389,32 @@ const showPricePanel = ref(true) const showOrderStatus = ref(false) // 妯℃嫙鐢ㄦ埛淇℃伅锛堝疄闄呭簲浠庣敤鎴风姸鎬佽幏鍙栵級 -const currentUserId = ref(1) -const currentUnitId = ref(1) +const userStore = useUserInfo() +let currentUserId = computed(() => userStore.getUserId || userStore.getUserInfo?.userId) +const currentUnitId = computed(() => userStore.getUnitId || userStore.getUserInfo?.unitId || '') + +onMounted(async () => { + if (!currentUserId.value) { + try { + const res: any = await queryUserDetail() + if (res?.code === 200 && res.data) { + userStore.updateUserDetail(res.data) + currentUserId = res.data.userId || res.data.id + userInfo.value = res.data + } else { + ElMessage.error(res?.msg || '鏃犳硶鑾峰彇鐢ㄦ埛淇℃伅锛岃鍏堢櫥褰�') + return + } + } catch (e) { + console.error('鑾峰彇鐢ㄦ埛璇︽儏澶辫触:', e) + ElMessage.error('鑾峰彇鐢ㄦ埛淇℃伅澶辫触锛岃绋嶅悗閲嶈瘯') + return + } + }else{ + userInfo.value = userStore.getUserInfos + } + +}) type PriceTypeKey = 'POINTS' | 'CURRENCY' | 'AGREEMENT' | 'FREE' interface OrderSuite extends PriceItem { selected: boolean @@ -513,14 +502,15 @@ // 浜у搧淇℃伅/鐢ㄦ埛淇℃伅锛堥潤鎬侊級 const productHeader = ref({ - name: '涓氦鏂硅繙鏅鸿兘瀹炴祴瀹為噺绠$悊绯荤粺', - industry: '鍏矾,甯傛斂,寤虹瓚', - provider: '涓氦寤虹瓚闆嗗洟绗竴宸ョ▼鏈夐檺鍏徃' + name: '', + industrialChainName: '', + submissionUnit: '', + createUserId: '' }) const userInfo = ref({ - name: '寮犻潤', - unit: '淇$闆嗗洟', - department: '闂ㄦ埛绯荤粺涓存椂缁�' + name: '', + departmentName: '', + unitName: '' }) // 鍏ㄩ�� @@ -551,7 +541,7 @@ const suite = orderSuites.value[index] if (!suite) return try { - await removeFromCart(suite.id) + await removeFromCart(String(suite.id)) orderSuites.value.splice(index, 1) } catch (e) { } @@ -563,8 +553,6 @@ ElMessage.success('璁㈣喘鐢宠鎻愪氦鎴愬姛锛�') // 娓呯┖閫夋嫨鐘舵�� selectedSuiteIds.value.clear() - // 鍏抽棴浠锋牸鏌ョ湅鍣� - visible.value = false } // 寮圭獥鏍囬 @@ -572,10 +560,6 @@ return `銆�${productName.value}銆戜骇鍝佸畾浠穈 }) -// 寮圭獥瀹藉害 -const dialogWidth = computed(() => { - return props.width -}) // 鎸夊鎴峰璞″垎缁勭殑浠锋牸鏁版嵁 const groupedPriceData = computed(() => { @@ -701,31 +685,33 @@ } finally { loading.value = false } + //鑾峰彇浜у搧璇︽儏淇℃伅 + try { + // productDetail.value = mockProductMap[productId] || null + const data = { + id: productId + } + const detailRes: any = await productApi.getProductById(data) + if (detailRes?.code === 200) { + productHeader.value = detailRes.data || { name: '', industrialChainName: '', submissionUnit: '', createUserId: '' } + } else { + productHeader.value = { name: '', industrialChainName: '', submissionUnit: '', createUserId: '' } + ElMessage.error(detailRes?.msg || '鑾峰彇浜у搧璇︽儏澶辫触') + } + } catch (e) { + productHeader.value = { name: '', industrialChainName: '', submissionUnit: '', createUserId: '' } + ElMessage.error('鑾峰彇浜у搧璇︽儏澶辫触') + } finally { + loading.value = false + } } // 鐩戝惉浜у搧ID鍙樺寲 -watch(() => props.productId, (newProductId) => { - if (newProductId && visible.value) { +watch(currentProductId, (newProductId) => { + if (newProductId) { fetchProductData(newProductId) } -}) - -// 鐩戝惉寮圭獥鏄剧ず鐘舵�� -watch(() => visible.value, (newVisible) => { - if (newVisible && props.productId) { - fetchProductData(props.productId) - } -}) - -// 鍏抽棴寮圭獥 -const handleClose = () => { - visible.value = false - showOrderPanel.value = false - showOrderStatus.value = false - showPricePanel.value = true - orderSuites.value = [] - selectedSuiteIds.value.clear() -} +},{ immediate: true }) // 濂椾欢閫夋嫨澶勭悊 const handleSuiteSelect = (suiteId: number, checked: any) => { @@ -740,7 +726,7 @@ } else { selectedSuiteIds.value.delete(suiteId) // 浠庤喘鐗╄溅绉婚櫎 - removeFromCart(suiteId) + removeFromCart(String(suiteId)) } } @@ -784,24 +770,31 @@ ElMessage.error('鏆備笉鏀寔璐у竵浜ゆ槗锛岃閫夋嫨绉垎鎴栧崗璁敮浠樻柟寮�') return } - + if (!idempotencyToken.value) { ElMessage.error('涓嬪崟Token鑾峰彇澶辫触锛岃杩斿洖閲嶈瘯') return } - + const hasPoints = items.some(item => item.priceType === 'POINTS') + const hasAGREEMENT = items.some(item => item.priceType === 'AGREEMENT') + let paymentType = '' + if(hasPoints){ + paymentType = '绉垎' + }else{ + paymentType = '鍗忚' + } // 缁勮鍒涘缓璁㈠崟鍙傛暟锛圕reateOrderDTO锛� const payload = { userId: currentUserId.value, unitId: currentUnitId.value, productName: productHeader.value.name, - providerName: productHeader.value.provider, - providerId: 3, - paymentType: '绉垎', + providerName: productHeader.value.submissionUnit, + providerId: productHeader.value.createUserId, + paymentType: paymentType, buyerRemarks: '', items: items.map(it => ({ pricingId: it.id, - productId: Number(it.productId), + productId: it.productId, suiteName: getProductSuiteText(it.productSuite), salesForm: getSalesFormText(it.salesForm), customerType: getCustomerObjectText(it.customerObject), @@ -813,8 +806,8 @@ quantity: it.quantity, duration: it.duration, totalPrice: undefined, - providerId: 3, - providerName: productHeader.value.provider, + providerId: productHeader.value.createUserId, + providerName: productHeader.value.submissionUnit, remarks: '' })) } @@ -827,11 +820,28 @@ orderStatus.value = { id: data.orderId || '鈥�', productName: data.productName || productHeader.value.name, - provider: data.providerName || productHeader.value.provider, + provider: data.providerName || productHeader.value.submissionUnit, status: 'SUBMITTED', submitTime: data.applyTime ? String(data.applyTime) : new Date().toLocaleString(), applyTime: data.applyTime ? String(data.applyTime) : new Date().toLocaleString() } + // 璋冪敤宸ヤ綔娴佹帴鍙e彂璧峰鎵规祦绋嬶紝鎷垮埌娴佺▼瀹炰緥ID鍚庡洖鍐欒鍗晈orkflow_id + try { + // 鏍规嵁鏄惁鍖呭惈鍗忚鏄庣粏锛岄厤缃笉鍚屾祦绋嬪畾涔変笌涓氬姟Key锛堝厛鐢ㄩ潤鎬佸�煎崰浣嶏級 + const processdefId = hasAGREEMENT ? 'Process_Agreement_Static' : 'Process_Points_Static' + const businessKey = hasAGREEMENT ? 'agreement_biz_key' : 'points_biz_key' + const wfRes: any = await orderApi.startWorkflowAndComplete({ + processdefId, + userid: String(currentUserId.value || ''), + businessKey + }) + if (wfRes?.code === 200 && wfRes.data?.processinstId) { + await orderApi.updateWorkflowId(data.orderId, wfRes.data.processinstId) + } + } catch (e) { + console.warn('鍚姩宸ヤ綔娴佸け璐ユ垨鏇存柊workflow_id澶辫触', e) + } + // 娓呯┖璐墿杞︼紙鍚庣 + 鏈湴鐘舵�侊級 try { const clearRes: any = await cartApi.clearCart(currentUserId.value, currentUnitId.value) @@ -859,7 +869,7 @@ try { const cartData = { pricingId: priceItem.id, - productId: Number(priceItem.productId), + productId: priceItem.productId, productName: productName.value, suiteName: getProductSuiteText(priceItem.productSuite), salesForm: getSalesFormText(priceItem.salesForm), @@ -885,7 +895,7 @@ } } -const removeFromCart = async (pricingId: number) => { +const removeFromCart = async (pricingId: string) => { try { const res: any = await cartApi.removeFromCart(currentUserId.value, currentUnitId.value, pricingId) if (res?.code === 200) { @@ -1084,29 +1094,65 @@ .pricing-table-container { .pricing-tabs { :deep(.el-tabs__header) { - margin-bottom: 5px; + margin-bottom: 12px; } - + /* 灞呬腑 tabs 瀵艰埅 */ + :deep(.el-tabs__nav-wrap) { + display: flex; + justify-content: center; + } + :deep(.el-tabs__nav) { + margin: 0 auto; + border: none !important; + } + /* 鍗$墖鍨� tabs 缇庡寲涓哄渾瑙掕兌鍥婃晥鏋� */ + :deep(.el-tabs--card > .el-tabs__header .el-tabs__nav) { + border: none; + } + :deep(.el-tabs--card > .el-tabs__header .el-tabs__item) { + border: none; + } + :deep(.el-tabs__item) { + border-radius: 999px; + padding: 8px 18px; + margin: 0 6px; + color: #606266; + background: #f6f8fb; + transition: all .2s ease; + } + :deep(.el-tabs__item:hover) { + color: #3a7afe; + background: #eff4ff; + } :deep(.el-tabs__item.is-active) { - font-weight: bold; + font-weight: 600; + color: #fff; + background: #3a7afe; + box-shadow: 0 6px 16px rgba(58, 122, 254, 0.25); } } .pricing-table-wrapper { overflow-x: auto; + background: #fff; + border-radius: 12px; + box-shadow: 0 6px 24px rgba(0, 0, 0, 0.06); + padding: 8px; .pricing-table { width: 100%; - border-collapse: collapse; - border: 1px solid #e4e7ed; + border-collapse: separate; + border-spacing: 0; background: #fff; + border-radius: 10px; th, td { - border: 1px solid #e4e7ed; - padding: 6px 8px; + border-bottom: 1px solid #eef2f7; + border-right: 1px solid #f3f5f7; + padding: 12px 14px; text-align: center; vertical-align: middle; - font-size: 16px; + font-size: 14px; } tr th:first-child, td:first-child{ font-weight: 700; @@ -1114,7 +1160,7 @@ } th { - background-color: #f5f7fa; + background: linear-gradient(180deg, #f7f9fc 0%, #eef2f7 100%); font-weight: 600; color: #303133; } @@ -1126,9 +1172,9 @@ .sub-header { th { - background-color: #fafafa; + background-color: #f8fafc; font-weight: 500; - font-size: 16px; + font-size: 13px; color: #606266; } } @@ -1141,26 +1187,34 @@ } .feature-value { - color: #606266; - background: #fafafa; /* 琛屼负鐏拌壊 */ + color: #303133; + background: #fff; + } + + tbody tr:hover td { + background: #fafcff; } .order-methods { display: flex; flex-direction: column; - gap: 4px; + gap: 6px; align-items: center; .method-item { - font-size: 16px; - color: #409eff; + font-size: 12px; + color: #3a7afe; + background: #f4f7ff; + border: 1px solid #e3ebff; + border-radius: 999px; + padding: 2px 10px; } } .price-info { display: flex; flex-direction: column; /* 绔栨帓 */ - gap: 8px; + gap: 10px; align-items: center; .price-item { @@ -1168,24 +1222,29 @@ align-items: flex-start; flex-direction: column; gap: 6px; + padding: 10px 12px; + border: 1px solid #f0f2f5; + border-radius: 10px; + background: #fff; + box-shadow: inset 0 -1px 0 rgba(0,0,0,0.02); .price-lable-icon{ display: flex; gap: 6px; } .price-label { - font-size: 16px; - color: #606266; + font-size: 12px; + color: #909399; } .price-value { - font-weight: 500; - font-size: 20px; + font-weight: 600; + font-size: 18px; &.points { color: #e7900d; } &.currency { - color: #e7900d; + color: #10b981; } &.free { @@ -1193,7 +1252,7 @@ } } .price-icon.points { color: #e6a23c; } - .price-icon.currency { color: #e7900d; } + .price-icon.currency { color: #16a34a; } } .add-checkbox { @@ -1206,7 +1265,7 @@ justify-content: center; align-items: center; padding: 8px 0; - .add-cart-icon { color: #409eff; font-size:22px} + .add-cart-icon { color: #3a7afe; font-size:22px} } } } @@ -1348,4 +1407,5 @@ .order-summary .summary-value.highlight{ color:#409eff; } .order-summary .summary-value.total{ color:#e6a23c; } .order-summary .summary-value.currency{ color:#f56c6c; } +.footer { display: flex; flex-direction: row-reverse; margin-right: 50px; gap: 50px; margin-top: 20px;} </style> diff --git a/src/views/tradeManage/buyer/index.vue b/src/views/tradeManage/buyer/index.vue index dc072b9..4c67314 100644 --- a/src/views/tradeManage/buyer/index.vue +++ b/src/views/tradeManage/buyer/index.vue @@ -9,7 +9,7 @@ <el-input v-model="query.productName" placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�" clearable style="width: 100%" /> </el-form-item> <el-form-item label="琛屼笟棰嗗煙" class="col-25"> - <el-select v-model="query.industry" placeholder="璇烽�夋嫨琛屼笟棰嗗煙" clearable style="width: 100%"> + <el-select v-model="query.industry" placeholder="璇烽�夋嫨琛屼笟棰嗗煙" clearable style="width: 100%" @change="handleIndustryChange"> <el-option v-for="item in industryOptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> @@ -31,7 +31,7 @@ <el-input v-model="query.orderNo" placeholder="璇疯緭鍏ヨ鍗曠紪鍙�" clearable style="width: 100%" /> </el-form-item> <el-form-item label="浜у搧绫诲瀷" class="col-25"> - <el-select v-model="query.productType" placeholder="璇烽�夋嫨浜у搧绫诲瀷" clearable style="width: 100%"> + <el-select v-model="query.productType" placeholder="璇烽�夋嫨浜у搧绫诲瀷" clearable style="width: 100%" @change="handleProductTypeChange"> <el-option v-for="item in productTypeOptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> @@ -74,9 +74,7 @@ <template #default="{ row }"> <div v-if="row.isMainOrder" class="main-order-info"> <div class="order-header"> - <div class="order-item status-item"> - <el-tag :type="getStatusType(row.status)" size="small">{{ row.statusName }}</el-tag> - </div> + <div class="order-item"> <span class="label">鐢宠鏃堕棿:</span> <span class="value">{{ row.applyTime }}</span> @@ -88,6 +86,9 @@ <div class="order-item"> <span class="label">渚涘簲鏂�:</span> <span class="value">{{ row.supplySide }}</span> + </div> + <div class="order-item status-item"> + <el-tag :type="getStatusType(row.status)" size="small">{{ row.statusName }}</el-tag> </div> </div> </div> @@ -269,10 +270,10 @@ </el-card> <!-- 璁㈠崟鐘舵�佸璇濇 --> - <ProductOrderStatusDialog + <!-- <ProductOrderStatusDialog v-model="orderStatusDialogVisible" :order-id="currentOrderId" - /> + /> --> </div> </template> @@ -282,9 +283,11 @@ import { Search, Refresh } from '@element-plus/icons-vue' import { ElMessage, ElMessageBox } from 'element-plus' import orderApi from '@/api/orderApi' +import productApi from '@/api/productApi' import { useUserInfo } from '@/stores/modules/userInfo' import ProductOrderStatusDialog from '@/views/productManage/productOrderStatusDialog/index.vue' import { OrderWorkflowController, OrderStatus, ActionType, PageType, StatusMapper } from '@/utils/orderWorkflow' +import { queryUserDetail } from '@/api/userInfo' const router = useRouter() const userStore = useUserInfo() @@ -299,35 +302,11 @@ { label: '宸茶瘎浠�', value: 'EVALUATED' }, ] -// 琛屼笟棰嗗煙閫夐」 -const industryOptions = [ - { label: '寤虹瓚宸ョ▼', value: 'construction' }, - { label: '浜ら�氬伐绋�', value: 'transportation' }, - { label: '姘村埄宸ョ▼', value: 'water' }, - { label: '鐢靛姏宸ョ▼', value: 'power' }, -] - -// 鍗曚綅宸ョ▼閫夐」 -const unitProjectOptions = [ - { label: '鍦熷缓宸ョ▼', value: 'civil' }, - { label: '瀹夎宸ョ▼', value: 'installation' }, - { label: '瑁呴グ宸ョ▼', value: 'decoration' }, -] - -// 浜у搧绫诲瀷閫夐」 -const productTypeOptions = [ - { label: '浼佷笟绉佹湁SaaS鐗堣鍙�', value: 'enterprise_private' }, - { label: '浼佷笟鍏湁SaaS鐗堣鍙�', value: 'enterprise_public' }, - { label: '椤圭洰鍏湁SaaS鐗堣鍙�', value: 'project_public' }, - { label: '涓汉鍏湁SaaS鐗堣鍙�', value: 'personal_public' }, -] - -// 浜у搧绫诲瀷瀛愮骇閫夐」 -const productSubTypeOptions = [ - { label: '鍩虹鐗�', value: 'basic' }, - { label: '鏍囧噯鐗�', value: 'standard' }, - { label: '楂樼骇鐗�', value: 'premium' }, -] +// 鍔ㄦ�侀�夐」鏁版嵁 +const industryOptions = ref<any[]>([]) +const unitProjectOptions = ref<any[]>([]) +const productTypeOptions = ref<any[]>([]) +const productSubTypeOptions = ref<any[]>([]) // 鏌ヨ鏉′欢 const query = reactive({ @@ -380,6 +359,92 @@ if (/(鍗忚|agreement)/i.test(s)) return 'agreement' if (/(鍏嶈垂|free)/i.test(s)) return 'free' return 'currency' +} + +// 鑾峰彇琛屼笟棰嗗煙閫夐」 +const getIndustryOptions = async () => { + try { + const res = await productApi.getCategoryByParent({ parentCode: 'business_direction' }) + if (res?.code === 200 && res.data) { + industryOptions.value = res.data.map((item: any) => ({ + label: item.name, + value: item.id + })) + } + } catch (error) { + console.error('鑾峰彇琛屼笟棰嗗煙閫夐」澶辫触:', error) + } +} + +// 鑾峰彇浜у搧绫诲瀷閫夐」 +const getProductTypeOptions = async () => { + try { + const res = await productApi.getCategoryByParent({ parentCode: 'product_type' }) + if (res?.code === 200 && res.data) { + productTypeOptions.value = res.data.map((item: any) => ({ + label: item.name, + value: item.id + })) + } + } catch (error) { + console.error('鑾峰彇浜у搧绫诲瀷閫夐」澶辫触:', error) + } +} + +// 鏍规嵁琛屼笟棰嗗煙鑾峰彇鍗曚綅宸ョ▼閫夐」 +const getUnitProjectOptions = async (industryCode: string) => { + if (!industryCode) { + unitProjectOptions.value = [] + return + } + try { + const res = await productApi.getCategoryByParent({ parentId: industryCode }) + if (res?.code === 200 && res.data) { + unitProjectOptions.value = res.data.map((item: any) => ({ + label: item.name, + value: item.id + })) + } + } catch (error) { + console.error('鑾峰彇鍗曚綅宸ョ▼閫夐」澶辫触:', error) + unitProjectOptions.value = [] + } +} + +// 鏍规嵁浜у搧绫诲瀷鑾峰彇浜у搧瀛愮骇閫夐」 +const getProductSubTypeOptions = async (productTypeCode: string) => { + if (!productTypeCode) { + productSubTypeOptions.value = [] + return + } + try { + const res = await productApi.getCategoryByParent({ parentId: productTypeCode }) + if (res?.code === 200 && res.data) { + productSubTypeOptions.value = res.data.map((item: any) => ({ + label: item.name, + value: item.id + })) + } + } catch (error) { + console.error('鑾峰彇浜у搧瀛愮骇閫夐」澶辫触:', error) + productSubTypeOptions.value = [] + } +} + +// 澶勭悊琛屼笟棰嗗煙鍙樺寲 +const handleIndustryChange = async (value: string) => { + // 娓呯┖鍗曚綅宸ョ▼閫夋嫨 + query.unitProject = '' + // 鑾峰彇瀵瑰簲鐨勫崟浣嶅伐绋嬮�夐」 + await getUnitProjectOptions(value) +} + +// 澶勭悊浜у搧绫诲瀷鍙樺寲 +const handleProductTypeChange = async (value: string) => { + // 娓呯┖浜у搧瀛愮骇閫夋嫨 + query.productSubType = '' + // 鑾峰彇瀵瑰簲鐨勪骇鍝佸瓙绾ч�夐」 + await getProductSubTypeOptions(value) } // 鑾峰彇鐘舵�佺被鍨� @@ -480,33 +545,31 @@ pageSize: page.size, productName: query.productName || undefined, orderId: query.orderNo || undefined, - userId: userStore.getUserId ? Number(userStore.getUserId) : undefined, + userId: userStore.getUserId ? userStore.getUserId : undefined, } if (query.status) payload.orderStatus = statusUiToServer[query.status] if (Array.isArray(query.dateRange) && query.dateRange.length === 2) { payload.applyTimeStart = query.dateRange[0] payload.applyTimeEnd = query.dateRange[1] } + + // 娣诲姞浜у搧鏉′欢鏌ヨ + if (query.industry) payload.industryId = query.industry + if (query.unitProject) payload.unitProjectId = query.unitProject + if (query.productType) payload.productTypeId = query.productType + if (query.productSubType) payload.productSubTypeId = query.productSubType - const res = (await orderApi.getBuyerOrderPage(payload)) as any + // 鏍规嵁鏄惁鏈変骇鍝佹潯浠堕�夋嫨涓嶅悓鐨凙PI + const hasProductConditions = query.industry || query.unitProject || query.productType || query.productSubType + const apiMethod = hasProductConditions ? orderApi.getBuyerOrderPageWithProductConditions : orderApi.getBuyerOrderPage + + const res = (await apiMethod(payload)) as any const pageData = res?.data const list: any[] = Array.isArray(pageData?.list) ? pageData.list : [] page.total = Number(pageData?.total || 0) - // 骞跺彂鑾峰彇姣忎釜璁㈠崟鐨勮鎯咃紙鐢ㄤ簬鏋勯�犲瓙璁㈠崟琛岋級 - const detailsArr = await Promise.all( - list.map(async (order: any) => { - try { - const detailRes = (await orderApi.getOrderDetail(order.orderId)) as any - return detailRes?.data - } catch (e) { - return null - } - }) - ) - const flatData: any[] = [] - list.forEach((order: any, idx: number) => { + list.forEach((order: any) => { const uiStatus = statusServerToUi[order.orderStatus] || 'WAIT_UPLOAD' const mainRow: any = { id: order.orderId, @@ -518,11 +581,10 @@ status: uiStatus, statusName: order.orderStatus || '', orderStatus: StatusMapper.toUIStatus(order.orderStatus), // 杞崲涓烘爣鍑嗙姸鎬佹灇涓� + workFlowId: order.workflowId || '' } - - const detail = detailsArr[idx] - const subOrders: any[] = Array.isArray(detail?.orderDetails) - ? detail.orderDetails.map((d: any, i: number) => ({ + const subOrders: any[] = Array.isArray(order?.orderDetails) + ? order.orderDetails.map((d: any, i: number) => ({ id: `${order.orderId}-${i + 1}`, isMainOrder: false, productName: order.productName || '', @@ -560,6 +622,9 @@ status: '', dateRange: [], }) + // 娓呯┖鍔ㄦ�侀�夐」 + unitProjectOptions.value = [] + productSubTypeOptions.value = [] page.current = 1 handleSearch() } @@ -569,7 +634,13 @@ const toUpload = (row: any) => router.push({ name: 'tradeOrderUpload', params: { id: row.id } }) const toConfirm = (row: any) => router.push({ name: 'tradeOrderConfirm', params: { id: row.id } }) const toEvaluate = (row: any) => router.push({ name: 'tradeOrderEvaluate', params: { id: row.id } }) -const toTrack = (row: any) => router.push({ name: 'tradeTrack', params: { id: row.id } }) +const toTrack = (row: any) => { + if (!row.workFlowId) { + ElMessage.warning('璇ヨ鍗曟殏鏃犲伐浣滄祦淇℃伅') + return + } + router.push({ name: 'tradeTrack', params: { id: row.workFlowId } }) +} // 杩借釜璁㈠崟 - 鏄剧ず璁㈠崟鐘舵�佸璇濇 const showOrderTrack = (row: any) => { @@ -590,7 +661,8 @@ toDetail(order) break case ActionType.TRACK: - showOrderTrack(order) + // showOrderTrack(order) + toTrack(order) break case ActionType.UPLOAD_FILE: toUpload(order) @@ -635,7 +707,33 @@ } } -onMounted(handleSearch) +onMounted(async ()=>{ + // 鑾峰彇鐢ㄦ埛淇℃伅 + if (!userStore.getUserId) { + try { + const res: any = await queryUserDetail() + if (res?.code === 200 && res.data) { + userStore.updateUserDetail(res.data) + } else { + ElMessage.error(res?.msg || '鏃犳硶鑾峰彇鐢ㄦ埛淇℃伅锛岃鍏堢櫥褰�') + return + } + } catch (e) { + console.error('鑾峰彇鐢ㄦ埛璇︽儏澶辫触:', e) + ElMessage.error('鑾峰彇鐢ㄦ埛淇℃伅澶辫触锛岃绋嶅悗閲嶈瘯') + return + } + } + + // 鑾峰彇鍒濆閫夐」鏁版嵁 + await Promise.all([ + getIndustryOptions(), + getProductTypeOptions() + ]) + + // 鎵ц鎼滅储 + handleSearch() +}) </script> <style scoped lang="scss"> @@ -779,12 +877,15 @@ gap: 20px; align-items: center; overflow: hidden; - + .status-item{ + flex-direction: row-reverse; + } .order-item { display: flex; align-items: center; gap: 8px; flex-shrink: 0; + flex: 1; .label { color: #909399; diff --git a/src/views/tradeManage/detail/index.vue b/src/views/tradeManage/detail/index.vue index 098fffc..7882083 100644 --- a/src/views/tradeManage/detail/index.vue +++ b/src/views/tradeManage/detail/index.vue @@ -226,11 +226,11 @@ </el-card> <!-- 瀹℃壒杩借釜 --> - <el-card class="mt15" shadow="never" v-if="detail.records?.length"> + <el-card class="mt15" shadow="never"> <div class="title">瀹℃壒杩借釜</div> <!-- 鏍囩椤� --> - <el-tabs v-model="activeTab" class="approval-tabs"> + <!-- <el-tabs v-model="activeTab" class="approval-tabs"> <el-tab-pane label="瀹℃壒璁板綍" name="records"> <el-table :data="detail.records" @@ -279,13 +279,18 @@ </el-table-column> </el-table> </el-tab-pane> - </el-tabs> + </el-tabs> --> - <!-- 杩斿洖鎸夐挳 --> - <div class="action-buttons"> + <!-- 澶栭儴绯荤粺瀹℃壒杞ㄨ抗 iframe --> + <div class="iframe-wrap" v-if="workflowIframeUrl"> + <iframe :src="workflowIframeUrl" class="workflow-iframe" referrerpolicy="no-referrer"></iframe> + </div> + + </el-card> + <!-- 杩斿洖鎸夐挳 --> + <div class="action-buttons"> <el-button @click="goBack">杩斿洖</el-button> </div> - </el-card> </div> </template> @@ -296,12 +301,22 @@ import { ElMessage } from 'element-plus' import orderApi from '@/api/orderApi' import createAxios from '@/utils/axios' +import productApi from '@/api/productApi' +import { useUserInfo } from '@/stores/modules/userInfo' +const hostUrl = import.meta.env.VITE_AXIOS_BASE_URL const route = useRoute() const router = useRouter() const detail = reactive<any>({ items: [], records: [], nodes: [] }) const orderTableWrapRef = ref<HTMLElement | null>(null) const activeTab = ref('records') +const userStore = useUserInfo() +// 澶栭儴瀹℃壒杞ㄨ抗iframe鍦板潃 +const workflowIframeUrl = computed(() => { + const pid = (detail.workflowId || route.query.processinstId || '').toString().trim() + if (!pid) return '' + return `${hostUrl}/activity/history?processinstId=${encodeURIComponent(pid)}&token=${userStore.getAdminToken}` +}) const labelStyle = { width: '180px', maxWidth: '180px' } const contentStyle = { width: 'calc(50% - 180px)' } @@ -379,6 +394,26 @@ const statusName: string = data.orderStatus || '' const uiStatus = statusServerToUi[statusName] || 'INFO' + // 鏍规嵁浜у搧id鑾峰彇浜у搧淇℃伅锛屾洿鏂板ご閮ㄥ睍绀� + try { + if (data.productId) { + const detailRes: any = await productApi.getProductById({ id: data.productId }) + if (detailRes?.code === 200 && detailRes.data) { + // 鐢ㄤ骇鍝佽鎯呰ˉ鍏ㄥご淇℃伅 + data.productName = detailRes.data.name || data.productName + data.providerName = detailRes.data.submissionUnit || data.providerName + data.industry = detailRes.data.industrialChainName || data.industry + data.productDesc = detailRes.data.describe || data.productDesc + data.projectUnit = detailRes.data.importantAreaName || data.productDesc + data.productType = detailRes.data.typeName || data.productDesc + } + } + } catch (e) { + // 蹇界暐浜у搧璇︽儏澶辫触锛屼笉闃诲璁㈠崟璇︽儏 + } + + // 鑾峰彇鐢ㄦ埛淇℃伅 + // 鏄犲皠璁㈠崟璇︽儏澶撮儴淇℃伅 const head = { orderNo: data.orderId, @@ -430,6 +465,7 @@ cashTotal: cashTotalNum.toLocaleString(), records: [], nodes: [], + workflowId: data.workflowId || data.processinstId || '' }) // 鍒濆鍖栦氦鏄撲俊鎭娉ㄦ暟鎹� @@ -1006,6 +1042,10 @@ text-align: left !important; } +/* 澶栭儴瀹℃壒杞ㄨ抗 iframe 鏍峰紡 */ +.iframe-wrap { margin-top: 10px; } +.workflow-iframe { width: 100%; height: 500px; border: none; border-radius: 6px; } + /* 鏂囦欢鎿嶄綔鎸夐挳鏍峰紡 */ .file-actions { display: flex; diff --git a/src/views/tradeManage/seller/index.vue b/src/views/tradeManage/seller/index.vue index 75068d4..3735a6f 100644 --- a/src/views/tradeManage/seller/index.vue +++ b/src/views/tradeManage/seller/index.vue @@ -9,7 +9,7 @@ <el-input v-model="query.productName" placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�" clearable style="width: 100%" /> </el-form-item> <el-form-item label="琛屼笟棰嗗煙" class="col-25"> - <el-select v-model="query.industry" placeholder="璇烽�夋嫨琛屼笟棰嗗煙" clearable style="width: 100%"> + <el-select v-model="query.industry" placeholder="璇烽�夋嫨琛屼笟棰嗗煙" clearable style="width: 100%" @change="handleIndustryChange"> <el-option v-for="item in industryOptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> @@ -19,7 +19,7 @@ </el-select> </el-form-item> <el-form-item label="浜у搧绫诲瀷" class="col-30"> - <el-select v-model="query.productType" placeholder="璇烽�夋嫨浜у搧绫诲瀷" clearable style="width: 100%"> + <el-select v-model="query.productType" placeholder="璇烽�夋嫨浜у搧绫诲瀷" clearable style="width: 100%" @change="handleProductTypeChange"> <el-option v-for="item in productTypeOptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> @@ -198,7 +198,7 @@ > 鏌ョ湅 </el-button> - <el-button + <!-- <el-button v-else-if="action.type === ActionType.TRACK" type="primary" link @@ -206,7 +206,7 @@ @click="handleAction(action, row.parentOrder)" > 杩借釜 - </el-button> + </el-button> --> </template> </div> </div> @@ -246,9 +246,11 @@ import { Search, Refresh } from '@element-plus/icons-vue' import { ElMessage, ElMessageBox } from 'element-plus' import orderApi from '@/api/orderApi' +import productApi from '@/api/productApi' import { useUserInfo } from '@/stores/modules/userInfo' import ProductOrderStatusDialog from '@/views/productManage/productOrderStatusDialog/index.vue' import { OrderWorkflowController, OrderStatus, ActionType, PageType, StatusMapper } from '@/utils/orderWorkflow' +import { queryUserDetail } from '@/api/userInfo' const router = useRouter() const userStore = useUserInfo() @@ -263,35 +265,11 @@ { label: '宸茶瘎浠�', value: 'EVALUATED' }, ] -// 琛屼笟棰嗗煙閫夐」 -const industryOptions = [ - { label: '寤虹瓚宸ョ▼', value: 'construction' }, - { label: '浜ら�氬伐绋�', value: 'transportation' }, - { label: '姘村埄宸ョ▼', value: 'water' }, - { label: '鐢靛姏宸ョ▼', value: 'power' }, -] - -// 鍗曚綅宸ョ▼閫夐」 -const unitProjectOptions = [ - { label: '鍦熷缓宸ョ▼', value: 'civil' }, - { label: '瀹夎宸ョ▼', value: 'installation' }, - { label: '瑁呴グ宸ョ▼', value: 'decoration' }, -] - -// 浜у搧绫诲瀷閫夐」 -const productTypeOptions = [ - { label: '浼佷笟绉佹湁SaaS鐗堣鍙�', value: 'enterprise_private' }, - { label: '浼佷笟鍏湁SaaS鐗堣鍙�', value: 'enterprise_public' }, - { label: '椤圭洰鍏湁SaaS鐗堣鍙�', value: 'project_public' }, - { label: '涓汉鍏湁SaaS鐗堣鍙�', value: 'personal_public' }, -] - -// 浜у搧绫诲瀷瀛愮骇閫夐」 -const productSubTypeOptions = [ - { label: '鍩虹鐗�', value: 'basic' }, - { label: '鏍囧噯鐗�', value: 'standard' }, - { label: '楂樼骇鐗�', value: 'premium' }, -] +// 鍔ㄦ�侀�夐」鏁版嵁 +const industryOptions = ref<any[]>([]) +const unitProjectOptions = ref<any[]>([]) +const productTypeOptions = ref<any[]>([]) +const productSubTypeOptions = ref<any[]>([]) // 鏌ヨ鏉′欢 const query = reactive({ @@ -344,6 +322,92 @@ if (/(鍗忚|agreement)/i.test(s)) return 'agreement' if (/(鍏嶈垂|free)/i.test(s)) return 'free' return 'currency' +} + +// 鑾峰彇琛屼笟棰嗗煙閫夐」 +const getIndustryOptions = async () => { + try { + const res = await productApi.getCategoryByParent({ parentCode: 'business_direction' }) + if (res?.code === 200 && res.data) { + industryOptions.value = res.data.map((item: any) => ({ + label: item.name, + value: item.id + })) + } + } catch (error) { + console.error('鑾峰彇琛屼笟棰嗗煙閫夐」澶辫触:', error) + } +} + +// 鑾峰彇浜у搧绫诲瀷閫夐」 +const getProductTypeOptions = async () => { + try { + const res = await productApi.getCategoryByParent({ parentCode: 'product_type' }) + if (res?.code === 200 && res.data) { + productTypeOptions.value = res.data.map((item: any) => ({ + label: item.name, + value: item.id + })) + } + } catch (error) { + console.error('鑾峰彇浜у搧绫诲瀷閫夐」澶辫触:', error) + } +} + +// 鏍规嵁琛屼笟棰嗗煙鑾峰彇鍗曚綅宸ョ▼閫夐」 +const getUnitProjectOptions = async (industryCode: string) => { + if (!industryCode) { + unitProjectOptions.value = [] + return + } + try { + const res = await productApi.getCategoryByParent({ parentId: industryCode }) + if (res?.code === 200 && res.data) { + unitProjectOptions.value = res.data.map((item: any) => ({ + label: item.name, + value: item.id + })) + } + } catch (error) { + console.error('鑾峰彇鍗曚綅宸ョ▼閫夐」澶辫触:', error) + unitProjectOptions.value = [] + } +} + +// 鏍规嵁浜у搧绫诲瀷鑾峰彇浜у搧瀛愮骇閫夐」 +const getProductSubTypeOptions = async (productTypeCode: string) => { + if (!productTypeCode) { + productSubTypeOptions.value = [] + return + } + try { + const res = await productApi.getCategoryByParent({ parentId: productTypeCode }) + if (res?.code === 200 && res.data) { + productSubTypeOptions.value = res.data.map((item: any) => ({ + label: item.name, + value: item.id + })) + } + } catch (error) { + console.error('鑾峰彇浜у搧瀛愮骇閫夐」澶辫触:', error) + productSubTypeOptions.value = [] + } +} + +// 澶勭悊琛屼笟棰嗗煙鍙樺寲 +const handleIndustryChange = async (value: string) => { + // 娓呯┖鍗曚綅宸ョ▼閫夋嫨 + query.unitProject = '' + // 鑾峰彇瀵瑰簲鐨勫崟浣嶅伐绋嬮�夐」 + await getUnitProjectOptions(value) +} + +// 澶勭悊浜у搧绫诲瀷鍙樺寲 +const handleProductTypeChange = async (value: string) => { + // 娓呯┖浜у搧瀛愮骇閫夋嫨 + query.productSubType = '' + // 鑾峰彇瀵瑰簲鐨勪骇鍝佸瓙绾ч�夐」 + await getProductSubTypeOptions(value) } @@ -444,7 +508,7 @@ try { // 鑾峰彇鐢ㄦ埛ID浣滀负providerId锛屽鏋滄病鏈夊垯浣跨敤榛樿鍊� const userId = userStore.getUserId - const providerId = userId ? Number(userId) : 1 // 浣跨敤榛樿鍊�1浣滀负涓存椂瑙e喅鏂规 + const providerId = userId ? userId : '' // 浣跨敤榛樿鍊�1浣滀负涓存椂瑙e喅鏂规 const payload: any = { pageNum: page.current, @@ -459,25 +523,23 @@ payload.applyTimeEnd = query.dateRange[1] } - const res = (await orderApi.getSellerOrderPage(payload)) as any + // 娣诲姞浜у搧鏉′欢鏌ヨ + if (query.industry) payload.industryId = query.industry + if (query.unitProject) payload.unitProjectId = query.unitProject + if (query.productType) payload.productTypeId = query.productType + if (query.productSubType) payload.productSubTypeId = query.productSubType + + // 鏍规嵁鏄惁鏈変骇鍝佹潯浠堕�夋嫨涓嶅悓鐨凙PI + const hasProductConditions = query.industry || query.unitProject || query.productType || query.productSubType + const apiMethod = hasProductConditions ? orderApi.getSellerOrderPageWithProductConditions : orderApi.getSellerOrderPage + + const res = (await apiMethod(payload)) as any const pageData = res?.data const list: any[] = Array.isArray(pageData?.list) ? pageData.list : [] page.total = Number(pageData?.total || 0) - // 骞跺彂鑾峰彇姣忎釜璁㈠崟鐨勮鎯咃紙鐢ㄤ簬鏋勯�犲瓙璁㈠崟琛岋級 - const detailsArr = await Promise.all( - list.map(async (order: any) => { - try { - const detailRes = (await orderApi.getOrderDetail(order.orderId)) as any - return detailRes?.data - } catch (e) { - return null - } - }) - ) - const flatData: any[] = [] - list.forEach((order: any, idx: number) => { + list.forEach((order: any) => { const uiStatus = statusServerToUi[order.orderStatus] || 'WAIT_UPLOAD' const mainRow: any = { id: order.orderId, @@ -489,11 +551,11 @@ status: uiStatus, statusName: order.orderStatus || '', orderStatus: StatusMapper.toUIStatus(order.orderStatus), // 杞崲涓烘爣鍑嗙姸鎬佹灇涓� + workFlowId: order.workflowId || '' } - const detail = detailsArr[idx] - const subOrders: any[] = Array.isArray(detail?.orderDetails) - ? detail.orderDetails.map((d: any, i: number) => ({ + const subOrders: any[] = Array.isArray(order?.orderDetails) + ? order.orderDetails.map((d: any, i: number) => ({ id: `${order.orderId}-${i + 1}`, isMainOrder: false, productName: order.productName || '', @@ -537,6 +599,9 @@ status: '', dateRange: [], }) + // 娓呯┖鍔ㄦ�侀�夐」 + unitProjectOptions.value = [] + productSubTypeOptions.value = [] page.current = 1 handleSearch() } @@ -570,7 +635,33 @@ // 璺敱璺宠浆 const toDetail = (row: any) => router.push({ name: 'tradeOrderDetail', params: { id: row.id } }) -onMounted(handleSearch) +onMounted(async ()=>{ + // 鑾峰彇鐢ㄦ埛淇℃伅 + if (!userStore.getUserId) { + try { + const res: any = await queryUserDetail() + if (res?.code === 200 && res.data) { + userStore.updateUserDetail(res.data) + } else { + ElMessage.error(res?.msg || '鏃犳硶鑾峰彇鐢ㄦ埛淇℃伅锛岃鍏堢櫥褰�') + return + } + } catch (e) { + console.error('鑾峰彇鐢ㄦ埛璇︽儏澶辫触:', e) + ElMessage.error('鑾峰彇鐢ㄦ埛淇℃伅澶辫触锛岃绋嶅悗閲嶈瘯') + return + } + } + + // 鑾峰彇鍒濆閫夐」鏁版嵁 + await Promise.all([ + getIndustryOptions(), + getProductTypeOptions() + ]) + + // 鎵ц鎼滅储 + handleSearch() +}) </script> <style scoped lang="scss"> diff --git a/src/views/tradeManage/track/index.vue b/src/views/tradeManage/track/index.vue new file mode 100644 index 0000000..5f4fff6 --- /dev/null +++ b/src/views/tradeManage/track/index.vue @@ -0,0 +1,72 @@ +<template> + <div class="track-page"> + <el-card shadow="never"> + + <div class="content"> + <div class="iframe-wrap" v-if="iframeUrl"> + <iframe :src="iframeUrl" class="workflow-iframe" referrerpolicy="no-referrer"></iframe> + </div> + <div v-else class="empty">鏆傛棤娴佺▼瀹炰緥ID</div> + </div> + </el-card> + <div class="action-buttons"> + <el-button @click="goBack">杩斿洖</el-button> + </div> + </div> +</template> + +<script setup lang="ts"> +import { ref, computed } from 'vue' +import { useRoute, useRouter } from 'vue-router' + +const route = useRoute() +const router = useRouter() + +const processId = computed(() => { + return route.query.processinstId || route.params.id || '' +}) + +const hostUrl = import.meta.env.VITE_AXIOS_BASE_URL + +const iframeUrl = computed(() => { + const pid = processId.value + if (pid && pid.toString().trim()) { + return `${hostUrl}/activity/history?processinstId=${encodeURIComponent(pid.toString().trim())}` + } + return '' +}) + +const goBack = () => router.back() +</script> + +<style scoped> +.track-page { + padding: 20px; +} +.title { + font-weight: 600; + margin-bottom: 10px; +} +.content { + margin-top: 10px; +} +.iframe-wrap { + margin-top: 10px; +} +.workflow-iframe { + width: 100%; + height: 600px; + border: none; + border-radius: 6px; +} +.action-buttons { + display: flex; + justify-content: center; + margin-top: 15px; +} +.empty { + text-align: center; + color: #909399; + padding: 40px 0; +} +</style> diff --git a/vite.config.ts b/vite.config.ts index e83364b..a303c4e 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -17,6 +17,11 @@ const viteConfig = ({ mode }: ConfigEnv): UserConfig => { let proxy: Record<string, string | ProxyOptions> = {} proxy = { + '/api/test': { + target: 'http://36.133.126.111:7099', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api\/test/, '/api'), + }, '/api': { // target: 'http://192.168.0.38:8088', // 鏉� // target: 'http://10.88.211.191:8088', // 鏉� -- Gitblit v1.8.0