From bac2e99f0d190ccf0c3d9837d7dee80bafb88f19 Mon Sep 17 00:00:00 2001 From: "Mr.j" <2221464500@qq.com> Date: Wed, 13 Aug 2025 10:17:19 +0800 Subject: [PATCH 01/19] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E4=B8=AD=E5=BF=83=E6=B7=BB=E5=8A=A0=E8=AE=BE=E5=A4=87=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E5=BC=B9=E7=AA=97=E9=A1=B5=E9=9D=A2=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E5=8F=A6=E5=A4=96=E6=90=AD=E5=BB=BA=E5=AE=8C=E6=88=90=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E7=9B=98=E5=BA=93=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/equipment/index.ts | 22 +- src/router/route.ts | 10 + src/stores/modules/route.ts | 36 +- src/types/components.d.ts | 1 + .../components/EquipmentTypeModal.vue | 201 +++++++++++ .../device-management/index.vue | 34 ++ .../device-management/inventory.vue | 330 ++++++++++++++++++ 7 files changed, 622 insertions(+), 12 deletions(-) create mode 100644 src/views/system-resource/device-management/components/EquipmentTypeModal.vue create mode 100644 src/views/system-resource/device-management/inventory.vue diff --git a/src/apis/equipment/index.ts b/src/apis/equipment/index.ts index 79de61c..35e2089 100644 --- a/src/apis/equipment/index.ts +++ b/src/apis/equipment/index.ts @@ -49,4 +49,24 @@ export function returnEquipment(equipmentId: string) { } // 导出设备采购 API -export * from './procurement' \ No newline at end of file +export * from './procurement' + +// 设备盘库相关 API +/** @desc 分页查询设备盘库记录 */ +export function pageEquipmentInventory(query: T.EquipmentPageQuery) { + return http.get(`${BASE_URL}/inventory/page`, query) +} + +/** @desc 执行设备盘库 */ +export function executeEquipmentInventory(equipmentId: string, inventoryResult: string, remark?: string) { + return http.post(`${BASE_URL}/inventory/${equipmentId}`, null, { + params: { inventoryResult, remark } + }) +} + +/** @desc 批量执行设备盘库 */ +export function batchExecuteEquipmentInventory(equipmentIds: string[], inventoryResult: string, remark?: string) { + return http.post(`${BASE_URL}/inventory/batch`, null, { + params: { equipmentIds, inventoryResult, remark } + }) +} \ No newline at end of file diff --git a/src/router/route.ts b/src/router/route.ts index 2f749c1..5a06e43 100644 --- a/src/router/route.ts +++ b/src/router/route.ts @@ -1282,6 +1282,16 @@ export const systemRoutes: RouteRecordRaw[] = [ hidden: false, }, }, + { + path: '/system-resource/device-management/inventory', + name: 'DeviceInventory', + component: () => import('@/views/system-resource/device-management/inventory.vue'), + meta: { + title: '设备盘库', + icon: 'inbox', + hidden: false, + }, + }, { path: '/system-resource/device-management/online', name: 'SystemResourceDeviceOnline', diff --git a/src/stores/modules/route.ts b/src/stores/modules/route.ts index 909490b..fb90fbd 100644 --- a/src/stores/modules/route.ts +++ b/src/stores/modules/route.ts @@ -226,6 +226,20 @@ const storeSetup = () => { { id: 2013, parentId: 2010, + title: '设备盘库', + type: 2, + path: '/asset-management/device-management/inventory', + name: 'DeviceInventory', + component: 'system-resource/device-management/inventory', + icon: 'inbox', + isExternal: false, + isCache: false, + isHidden: false, + sort: 3, + }, + { + id: 2014, + parentId: 2010, title: '审批台', type: 2, path: '/asset-management/device-management/approval', @@ -235,10 +249,10 @@ const storeSetup = () => { isExternal: false, isCache: false, isHidden: false, - sort: 3, + sort: 4, }, { - id: 2014, + id: 2015, parentId: 2010, title: '在线管理', type: 1, @@ -250,11 +264,11 @@ const storeSetup = () => { isExternal: false, isCache: false, isHidden: false, - sort: 4, + sort: 5, children: [ { - id: 20141, - parentId: 2014, + id: 20151, + parentId: 2015, title: '无人机', type: 2, path: '/asset-management/device-management/online/drone', @@ -267,8 +281,8 @@ const storeSetup = () => { sort: 1, }, { - id: 20142, - parentId: 2014, + id: 20152, + parentId: 2015, title: '机巢', type: 2, path: '/asset-management/device-management/online/nest', @@ -281,8 +295,8 @@ const storeSetup = () => { sort: 2, }, { - id: 20143, - parentId: 2014, + id: 20153, + parentId: 2015, title: '其他智能终端', type: 2, path: '/asset-management/device-management/online/smart-terminal', @@ -297,7 +311,7 @@ const storeSetup = () => { ], }, { - id: 2015, + id: 2016, parentId: 2010, title: '设备详情', type: 2, @@ -308,7 +322,7 @@ const storeSetup = () => { isExternal: false, isCache: false, isHidden: true, - sort: 5, + sort: 6, }, ], }, diff --git a/src/types/components.d.ts b/src/types/components.d.ts index 7fa6b1b..81c6b77 100644 --- a/src/types/components.d.ts +++ b/src/types/components.d.ts @@ -7,6 +7,7 @@ export {} declare module 'vue' { export interface GlobalComponents { + CircularProgress: typeof import('./../components/CircularProgress/index.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] } diff --git a/src/views/system-resource/device-management/components/EquipmentTypeModal.vue b/src/views/system-resource/device-management/components/EquipmentTypeModal.vue new file mode 100644 index 0000000..a28ee9e --- /dev/null +++ b/src/views/system-resource/device-management/components/EquipmentTypeModal.vue @@ -0,0 +1,201 @@ + + + + + diff --git a/src/views/system-resource/device-management/index.vue b/src/views/system-resource/device-management/index.vue index 7e731a2..908d55a 100644 --- a/src/views/system-resource/device-management/index.vue +++ b/src/views/system-resource/device-management/index.vue @@ -19,6 +19,12 @@ @search="handleSearch" @reset="handleReset" /> + + + 添加设备类型 + @@ -250,9 +264,11 @@ import { IconPlus, IconRefresh, IconSearch, + IconTag, } from '@arco-design/web-vue/es/icon' import message from '@arco-design/web-vue/es/message' import DeviceModal from './components/DeviceModal.vue' +import EquipmentTypeModal from './components/EquipmentTypeModal.vue' import EquipmentSearch from './components/EquipmentSearch.vue' import router from '@/router' import { EquipmentAPI } from '@/apis' @@ -287,6 +303,11 @@ const modalVisible = ref(false) const currentEquipment = ref(null) const modalMode = ref<'add' | 'edit' | 'view'>('add') +// 设备类型弹窗控制 +const equipmentTypeModalVisible = ref(false) +const currentEquipmentType = ref(null) +const equipmentTypeModalMode = ref<'add' | 'edit'>('add') + // 表格列配置 const columns = [ { @@ -913,6 +934,19 @@ const handleModalSuccess = () => { loadData() } +// 新增设备类型 +const handleAddEquipmentType = () => { + equipmentTypeModalMode.value = 'add' + currentEquipmentType.value = null + equipmentTypeModalVisible.value = true +} + +// 设备类型弹窗成功回调 +const handleEquipmentTypeModalSuccess = () => { + equipmentTypeModalVisible.value = false + loadData() +} + // 监听表格数据变化 watch(tableData, (_newData) => { // 数据变化监听 diff --git a/src/views/system-resource/device-management/inventory.vue b/src/views/system-resource/device-management/inventory.vue new file mode 100644 index 0000000..bb08cfe --- /dev/null +++ b/src/views/system-resource/device-management/inventory.vue @@ -0,0 +1,330 @@ + + + + + From f438b4d2537ae75a749c6379a0dea4e2fc686f54 Mon Sep 17 00:00:00 2001 From: crushing1111 <909171938@qq.com> Date: Wed, 13 Aug 2025 10:24:24 +0800 Subject: [PATCH 02/19] =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/task/task-approval/TaskApproval.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/task/task-approval/TaskApproval.vue b/src/views/task/task-approval/TaskApproval.vue index 67b89a4..c41d446 100644 --- a/src/views/task/task-approval/TaskApproval.vue +++ b/src/views/task/task-approval/TaskApproval.vue @@ -6,7 +6,7 @@

审批转交的任务

- +
Date: Wed, 13 Aug 2025 10:39:00 +0800 Subject: [PATCH 03/19] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/task/task-progress/TaskProgress.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/task/task-progress/TaskProgress.vue b/src/views/task/task-progress/TaskProgress.vue index 0267e80..3ff1392 100644 --- a/src/views/task/task-progress/TaskProgress.vue +++ b/src/views/task/task-progress/TaskProgress.vue @@ -1,7 +1,7 @@ + + + + + + @@ -271,6 +331,8 @@ import message from '@arco-design/web-vue/es/message' import ProcurementModal from './components/ProcurementModal.vue' import ProcurementSearch from './components/ProcurementSearch.vue' import ProcurementApplicationModal from './components/ProcurementApplicationModal.vue' +import ReceiptDetailModal from './components/ReceiptDetailModal.vue' +import PaymentDetailModal from './components/PaymentDetailModal.vue' import { equipmentProcurementApi } from '@/apis/equipment/procurement' import { equipmentApprovalApi } from '@/apis/equipment/approval' import type { EquipmentListReq, EquipmentResp } from '@/apis/equipment/type' @@ -307,6 +369,14 @@ const modalMode = ref<'add' | 'edit' | 'view'>('add') const applicationModalVisible = ref(false) const currentApplicationData = ref(null) +// 收货详情弹窗控制 +const receiptDetailModalVisible = ref(false) +const currentReceiptData = ref(null) + +// 支付详情弹窗控制 +const paymentDetailModalVisible = ref(false) +const currentPaymentData = ref(null) + // 表格选择 const selectedRowKeys = ref([]) const rowSelection = reactive({ @@ -418,6 +488,20 @@ const columns = [ slotName: 'createTime', width: 160, }, + { + title: '收货状态', + dataIndex: 'receiptStatus', + key: 'receiptStatus', + slotName: 'receiptStatus', + width: 120, + }, + { + title: '支付状态', + dataIndex: 'paymentStatus', + key: 'paymentStatus', + slotName: 'paymentStatus', + width: 120, + }, { title: '操作', key: 'action', @@ -527,6 +611,50 @@ const getHealthStatusText = (status: string) => { return textMap[status] || '未知' } +// 获取收货状态颜色 +const getReceiptStatusColor = (status: string) => { + const colorMap: Record = { + NOT_RECEIVED: 'gray', + RECEIVED: 'green', + PARTIALLY_RECEIVED: 'orange', + REJECTED: 'red', + } + return colorMap[status] || 'blue' +} + +// 获取收货状态文本 +const getReceiptStatusText = (status: string) => { + const textMap: Record = { + NOT_RECEIVED: '未收货', + RECEIVED: '已收货', + PARTIALLY_RECEIVED: '部分收货', + REJECTED: '已拒收', + } + return textMap[status] || '未知' +} + +// 获取支付状态颜色 +const getPaymentStatusColor = (status: string) => { + const colorMap: Record = { + NOT_PAID: 'gray', + PAID: 'green', + PARTIALLY_PAID: 'orange', + REJECTED: 'red', + } + return colorMap[status] || 'blue' +} + +// 获取支付状态文本 +const getPaymentStatusText = (status: string) => { + const textMap: Record = { + NOT_PAID: '未支付', + PAID: '已支付', + PARTIALLY_PAID: '部分支付', + REJECTED: '已拒付', + } + return textMap[status] || '未知' +} + // 格式化价格 const formatPrice = (price: number) => { return price.toLocaleString('zh-CN', { @@ -601,6 +729,8 @@ const transformBackendData = (data: any[]): EquipmentResp[] => { inventoryBasis: item.inventoryBasis, dynamicRecord: item.dynamicRecord, procurementStatus: item.procurementStatus, + receiptStatus: item.receiptStatus || 'NOT_RECEIVED', + paymentStatus: item.paymentStatus || 'NOT_PAID', })) } @@ -850,6 +980,60 @@ const canApplyProcurement = (record: EquipmentResp) => { return canApply } +// 检查是否可以收货 +const canReceiveGoods = (record: EquipmentResp) => { + const receiptStatus = (record as any).receiptStatus + return receiptStatus === 'NOT_RECEIVED' || receiptStatus === 'PARTIALLY_RECEIVED' +} + +// 收货操作 +const handleReceiveGoods = async (record: EquipmentResp) => { + try { + // 暂时模拟API调用 + // await equipmentProcurementApi.receiveGoods(record.equipmentId) + message.success('收货成功') + // 模拟状态更新 + (record as any).receiptStatus = 'RECEIVED' + loadData(currentSearchParams.value) + } catch (error: any) { + console.error('收货失败:', error) + message.error(error?.message || '收货失败') + } +} + +// 查看收货详情 +const handleViewReceipt = (record: EquipmentResp) => { + currentReceiptData.value = { ...record } + receiptDetailModalVisible.value = true +} + +// 检查是否可以付款 +const canMakePayment = (record: EquipmentResp) => { + const paymentStatus = (record as any).paymentStatus + return paymentStatus === 'NOT_PAID' || paymentStatus === 'PARTIALLY_PAID' +} + +// 付款操作 +const handleMakePayment = async (record: EquipmentResp) => { + try { + // 暂时模拟API调用 + // await equipmentProcurementApi.makePayment(record.equipmentId) + message.success('付款成功') + // 模拟状态更新 + (record as any).paymentStatus = 'PAID' + loadData(currentSearchParams.value) + } catch (error: any) { + console.error('付款失败:', error) + message.error(error?.message || '付款失败') + } +} + +// 查看支付详情 +const handleViewPayment = (record: EquipmentResp) => { + currentPaymentData.value = { ...record } + paymentDetailModalVisible.value = true +} + // 获取审批状态颜色 const getApprovalStatusColor = (status: string) => { const colorMap: Record = { From aa40502f824d4f91cf62a4d171da03a206486772 Mon Sep 17 00:00:00 2001 From: "Mr.j" <2221464500@qq.com> Date: Wed, 13 Aug 2025 14:22:11 +0800 Subject: [PATCH 06/19] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E5=92=8C=E5=88=B0=E8=B4=A7=E5=BC=B9=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 4 +- src/apis/equipment/procurement.ts | 33 +- src/apis/equipment/type.ts | 226 +++++++- src/types/components.d.ts | 63 --- .../procurement/components/PaymentModal.vue | 505 ++++++++++++++++++ .../procurement/components/ReceiptModal.vue | 368 +++++++++++++ .../device-management/procurement/index.vue | 58 +- 7 files changed, 1148 insertions(+), 109 deletions(-) create mode 100644 src/views/system-resource/device-management/procurement/components/PaymentModal.vue create mode 100644 src/views/system-resource/device-management/procurement/components/ReceiptModal.vue diff --git a/.env.development b/.env.development index 8b0f18b..b362568 100644 --- a/.env.development +++ b/.env.development @@ -4,8 +4,8 @@ VITE_API_PREFIX = '/dev-api' # 接口地址 # VITE_API_BASE_URL = 'http://pms.dtyx.net:9158/' -# VITE_API_BASE_URL = 'http://localhost:8888/' -VITE_API_BASE_URL = 'http://10.18.34.163:8888/' +VITE_API_BASE_URL = 'http://localhost:8888/' +# VITE_API_BASE_URL = 'http://10.18.34.163:8888/' # VITE_API_BASE_URL = 'http://10.18.34.213:8888/' # 接口地址 (WebSocket) diff --git a/src/apis/equipment/procurement.ts b/src/apis/equipment/procurement.ts index dd8b3f6..2aab76b 100644 --- a/src/apis/equipment/procurement.ts +++ b/src/apis/equipment/procurement.ts @@ -99,6 +99,34 @@ export const equipmentProcurementApi = { return http.get>(`/equipment/procurement/detail/${equipmentId}`) }, + /** + * 确认收货 + */ + receiveGoods: (equipmentId: string, data: ReceiptRequest) => { + return http.post>(`/equipment/procurement/receipt/${equipmentId}`, data) + }, + + /** + * 获取收货详情 + */ + getReceiptDetail: (equipmentId: string) => { + return http.get>(`/equipment/procurement/receipt/${equipmentId}`) + }, + + /** + * 执行付款 + */ + makePayment: (equipmentId: string, data: PaymentRequest) => { + return http.post>(`/equipment/procurement/payment/${equipmentId}`, data) + }, + + /** + * 获取支付详情 + */ + getPaymentDetail: (equipmentId: string) => { + return http.get>(`/equipment/procurement/payment/${equipmentId}`) + }, + /** * 获取采购统计信息 */ @@ -117,9 +145,6 @@ export const equipmentProcurementApi = { * 导出设备采购记录 */ export: (params: EquipmentListReq) => { - return http.get('/equipment/procurement/export', { - params, - responseType: 'blob' - }) + return http.get('/equipment/procurement/export', params, { responseType: 'blob' }) } } \ No newline at end of file diff --git a/src/apis/equipment/type.ts b/src/apis/equipment/type.ts index 2bd5a1e..a7fdf57 100644 --- a/src/apis/equipment/type.ts +++ b/src/apis/equipment/type.ts @@ -101,28 +101,28 @@ export interface EquipmentResp { /** 资产编号 */ assetCode?: string /** 设备名称 */ - equipmentName: string + equipmentName?: string /** 设备类型 */ - equipmentType: string - /** 设备类型描述 */ + equipmentType?: string + /** 设备类型标签 */ equipmentTypeLabel?: string /** 设备型号 */ - equipmentModel: string - /** 设备SN */ - equipmentSn: string + equipmentModel?: string + /** 设备序列号 */ + equipmentSn?: string /** 品牌 */ brand?: string /** 配置规格/参数 */ specification?: string /** 设备状态 */ - equipmentStatus: string - /** 设备状态描述 */ + equipmentStatus?: string + /** 设备状态标签 */ equipmentStatusLabel?: string /** 使用状态 */ - useStatus: string + useStatus?: string /** 位置状态 */ locationStatus?: string - /** 位置状态描述 */ + /** 位置状态标签 */ locationStatusLabel?: string /** 设备当前物理位置 */ physicalLocation?: string @@ -130,7 +130,7 @@ export interface EquipmentResp { responsiblePerson?: string /** 健康状态 */ healthStatus?: string - /** 健康状态描述 */ + /** 健康状态标签 */ healthStatusLabel?: string /** 采购时间 */ purchaseTime?: string @@ -138,13 +138,13 @@ export interface EquipmentResp { inStockTime?: string /** 启用时间 */ activationTime?: string - /** 预计报废时间 */ + /** 预期报废时间 */ expectedScrapTime?: string /** 实际报废时间 */ actualScrapTime?: string /** 状态变更时间 */ statusChangeTime?: string - /** 采购订单 */ + /** 采购订单号 */ purchaseOrder?: string /** 供应商名称 */ supplierName?: string @@ -158,9 +158,9 @@ export interface EquipmentResp { depreciationYears?: number /** 残值 */ salvageValue?: number - /** 保修截止日期 */ + /** 保修到期日期 */ warrantyExpireDate?: string - /** 上次维护日期 */ + /** 最后维护日期 */ lastMaintenanceDate?: string /** 下次维护日期 */ nextMaintenanceDate?: string @@ -176,7 +176,7 @@ export interface EquipmentResp { projectName?: string /** 使用人ID */ userId?: string - /** 使用人 */ + /** 使用人姓名 */ name?: string /** 创建时间 */ createTime?: string @@ -194,12 +194,14 @@ export interface EquipmentResp { inventoryBasis?: string /** 动态记录 */ dynamicRecord?: string - /** 采购状态 */ procurementStatus?: string - /** 审批状态 */ approvalStatus?: string + /** 收货状态 */ + receiptStatus?: string + /** 支付状态 */ + paymentStatus?: string } /** @@ -406,3 +408,191 @@ export interface EquipmentApprovalResp { /** 更新时间 */ updateTime: string } + +/** + * 收货请求参数 + */ +export interface ReceiptRequest { + /** 收货时间 */ + receiptTime: string + /** 收货人 */ + receiptPerson: string + /** 收货数量 */ + receiptQuantity: number + /** 收货备注 */ + receiptRemark?: string + /** 外观检查结果 */ + appearanceCheck: string + /** 功能测试结果 */ + functionTest: string + /** 包装完整性 */ + packageIntegrity: string + /** 配件完整性 */ + accessoryIntegrity: string + /** 检查结果 */ + checkResult: 'PASS' | 'FAIL' | 'CONDITIONAL' + /** 检查备注 */ + checkRemark?: string + /** 入库位置 */ + storageLocation: string + /** 库管员 */ + storageManager: string +} + +/** + * 收货详情响应 + */ +export interface ReceiptDetail { + /** 设备ID */ + equipmentId: string + /** 设备名称 */ + equipmentName: string + /** 设备类型 */ + equipmentType: string + /** 设备型号 */ + equipmentModel: string + /** 品牌 */ + brand: string + /** 供应商 */ + supplierName: string + /** 采购订单 */ + purchaseOrder: string + /** 收货状态 */ + receiptStatus: string + /** 收货时间 */ + receiptTime: string + /** 收货人 */ + receiptPerson: string + /** 收货数量 */ + receiptQuantity: number + /** 收货备注 */ + receiptRemark?: string + /** 外观检查 */ + appearanceCheck: string + /** 功能测试 */ + functionTest: string + /** 包装完整性 */ + packageIntegrity: string + /** 配件完整性 */ + accessoryIntegrity: string + /** 检查结果 */ + checkResult: string + /** 检查备注 */ + checkRemark?: string + /** 入库状态 */ + storageStatus: string + /** 入库时间 */ + storageTime: string + /** 入库位置 */ + storageLocation: string + /** 库管员 */ + storageManager: string +} + +/** + * 支付请求参数 + */ +export interface PaymentRequest { + /** 支付方式 */ + paymentMethod: string + /** 支付金额 */ + paymentAmount: number + /** 支付时间 */ + paymentTime: string + /** 支付人 */ + paymentPerson: string + /** 支付备注 */ + paymentRemark?: string + /** 发票类型 */ + invoiceType: string + /** 发票号码 */ + invoiceNumber: string + /** 开票日期 */ + invoiceDate: string + /** 发票金额 */ + invoiceAmount: number + /** 税率 */ + taxRate: number + /** 税额 */ + taxAmount: number + /** 不含税金额 */ + amountWithoutTax: number + /** 合同编号 */ + contractNumber: string + /** 合同金额 */ + contractAmount: number + /** 签订日期 */ + contractDate: string + /** 付款条件 */ + paymentTerms: string + /** 付款期限 */ + paymentDeadline: string +} + +/** + * 支付详情响应 + */ +export interface PaymentDetail { + /** 设备ID */ + equipmentId: string + /** 设备名称 */ + equipmentName: string + /** 设备类型 */ + equipmentType: string + /** 设备型号 */ + equipmentModel: string + /** 品牌 */ + brand: string + /** 供应商 */ + supplierName: string + /** 采购订单 */ + purchaseOrder: string + /** 采购价格 */ + purchasePrice: number + /** 采购数量 */ + quantity: number + /** 总金额 */ + totalPrice: number + /** 采购日期 */ + purchaseTime: string + /** 支付状态 */ + paymentStatus: string + /** 支付方式 */ + paymentMethod: string + /** 支付金额 */ + paymentAmount: number + /** 支付时间 */ + paymentTime: string + /** 支付人 */ + paymentPerson: string + /** 支付备注 */ + paymentRemark?: string + /** 发票状态 */ + invoiceStatus: string + /** 发票类型 */ + invoiceType: string + /** 发票号码 */ + invoiceNumber: string + /** 开票日期 */ + invoiceDate: string + /** 发票金额 */ + invoiceAmount: number + /** 税率 */ + taxRate: number + /** 税额 */ + taxAmount: number + /** 不含税金额 */ + amountWithoutTax: number + /** 合同编号 */ + contractNumber: string + /** 合同状态 */ + contractStatus: string + /** 合同金额 */ + contractAmount: number + /** 签订日期 */ + contractDate: string + /** 付款条件 */ + paymentTerms: string + /** 付款期限 */ + paymentDeadline: string +} diff --git a/src/types/components.d.ts b/src/types/components.d.ts index a427ece..7fa6b1b 100644 --- a/src/types/components.d.ts +++ b/src/types/components.d.ts @@ -7,70 +7,7 @@ export {} declare module 'vue' { export interface GlobalComponents { - ApprovalAssistant: typeof import('./../components/ApprovalAssistant/index.vue')['default'] - ApprovalMessageItem: typeof import('./../components/NotificationCenter/ApprovalMessageItem.vue')['default'] - Avatar: typeof import('./../components/Avatar/index.vue')['default'] - Breadcrumb: typeof import('./../components/Breadcrumb/index.vue')['default'] - CellCopy: typeof import('./../components/CellCopy/index.vue')['default'] - Chart: typeof import('./../components/Chart/index.vue')['default'] - CircularProgress: typeof import('./../components/CircularProgress/index.vue')['default'] - ColumnSetting: typeof import('./../components/GiTable/src/components/ColumnSetting.vue')['default'] - CronForm: typeof import('./../components/GenCron/CronForm/index.vue')['default'] - CronModal: typeof import('./../components/GenCron/CronModal/index.vue')['default'] - DateRangePicker: typeof import('./../components/DateRangePicker/index.vue')['default'] - DayForm: typeof import('./../components/GenCron/CronForm/component/day-form.vue')['default'] - FilePreview: typeof import('./../components/FilePreview/index.vue')['default'] - GiCellAvatar: typeof import('./../components/GiCell/GiCellAvatar.vue')['default'] - GiCellGender: typeof import('./../components/GiCell/GiCellGender.vue')['default'] - GiCellStatus: typeof import('./../components/GiCell/GiCellStatus.vue')['default'] - GiCellTag: typeof import('./../components/GiCell/GiCellTag.vue')['default'] - GiCellTags: typeof import('./../components/GiCell/GiCellTags.vue')['default'] - GiCodeView: typeof import('./../components/GiCodeView/index.vue')['default'] - GiDot: typeof import('./../components/GiDot/index.tsx')['default'] - GiEditTable: typeof import('./../components/GiEditTable/GiEditTable.vue')['default'] - GiFooter: typeof import('./../components/GiFooter/index.vue')['default'] - GiForm: typeof import('./../components/GiForm/src/GiForm.vue')['default'] - GiIconBox: typeof import('./../components/GiIconBox/index.vue')['default'] - GiIconSelector: typeof import('./../components/GiIconSelector/index.vue')['default'] - GiIframe: typeof import('./../components/GiIframe/index.vue')['default'] - GiOption: typeof import('./../components/GiOption/index.vue')['default'] - GiOptionItem: typeof import('./../components/GiOptionItem/index.vue')['default'] - GiPageLayout: typeof import('./../components/GiPageLayout/index.vue')['default'] - GiSpace: typeof import('./../components/GiSpace/index.vue')['default'] - GiSplitButton: typeof import('./../components/GiSplitButton/index.vue')['default'] - GiSplitPane: typeof import('./../components/GiSplitPane/index.vue')['default'] - GiSplitPaneFlexibleBox: typeof import('./../components/GiSplitPane/components/GiSplitPaneFlexibleBox.vue')['default'] - GiSvgIcon: typeof import('./../components/GiSvgIcon/index.vue')['default'] - GiTable: typeof import('./../components/GiTable/src/GiTable.vue')['default'] - GiTag: typeof import('./../components/GiTag/index.tsx')['default'] - GiThemeBtn: typeof import('./../components/GiThemeBtn/index.vue')['default'] - HourForm: typeof import('./../components/GenCron/CronForm/component/hour-form.vue')['default'] - Icon403: typeof import('./../components/icons/Icon403.vue')['default'] - Icon404: typeof import('./../components/icons/Icon404.vue')['default'] - Icon500: typeof import('./../components/icons/Icon500.vue')['default'] - IconBorders: typeof import('./../components/icons/IconBorders.vue')['default'] - IconTableSize: typeof import('./../components/icons/IconTableSize.vue')['default'] - IconTreeAdd: typeof import('./../components/icons/IconTreeAdd.vue')['default'] - IconTreeReduce: typeof import('./../components/icons/IconTreeReduce.vue')['default'] - ImageImport: typeof import('./../components/ImageImport/index.vue')['default'] - ImageImportWizard: typeof import('./../components/ImageImportWizard/index.vue')['default'] - IndustrialImageList: typeof import('./../components/IndustrialImageList/index.vue')['default'] - JsonPretty: typeof import('./../components/JsonPretty/index.vue')['default'] - MinuteForm: typeof import('./../components/GenCron/CronForm/component/minute-form.vue')['default'] - MonthForm: typeof import('./../components/GenCron/CronForm/component/month-form.vue')['default'] - NotificationCenter: typeof import('./../components/NotificationCenter/index.vue')['default'] - ParentView: typeof import('./../components/ParentView/index.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] - SecondForm: typeof import('./../components/GenCron/CronForm/component/second-form.vue')['default'] - SplitPanel: typeof import('./../components/SplitPanel/index.vue')['default'] - TextCopy: typeof import('./../components/TextCopy/index.vue')['default'] - TurbineGrid: typeof import('./../components/TurbineGrid/index.vue')['default'] - UserSelect: typeof import('./../components/UserSelect/index.vue')['default'] - Verify: typeof import('./../components/Verify/index.vue')['default'] - VerifyPoints: typeof import('./../components/Verify/Verify/VerifyPoints.vue')['default'] - VerifySlide: typeof import('./../components/Verify/Verify/VerifySlide.vue')['default'] - WeekForm: typeof import('./../components/GenCron/CronForm/component/week-form.vue')['default'] - YearForm: typeof import('./../components/GenCron/CronForm/component/year-form.vue')['default'] } } diff --git a/src/views/system-resource/device-management/procurement/components/PaymentModal.vue b/src/views/system-resource/device-management/procurement/components/PaymentModal.vue new file mode 100644 index 0000000..81b7d52 --- /dev/null +++ b/src/views/system-resource/device-management/procurement/components/PaymentModal.vue @@ -0,0 +1,505 @@ + + + + + diff --git a/src/views/system-resource/device-management/procurement/components/ReceiptModal.vue b/src/views/system-resource/device-management/procurement/components/ReceiptModal.vue new file mode 100644 index 0000000..1d8a437 --- /dev/null +++ b/src/views/system-resource/device-management/procurement/components/ReceiptModal.vue @@ -0,0 +1,368 @@ + + + + + diff --git a/src/views/system-resource/device-management/procurement/index.vue b/src/views/system-resource/device-management/procurement/index.vue index 5155506..91f57f1 100644 --- a/src/views/system-resource/device-management/procurement/index.vue +++ b/src/views/system-resource/device-management/procurement/index.vue @@ -312,6 +312,20 @@ v-model:visible="paymentDetailModalVisible" :payment-data="currentPaymentData" /> + + + + + +
@@ -333,6 +347,8 @@ import ProcurementSearch from './components/ProcurementSearch.vue' import ProcurementApplicationModal from './components/ProcurementApplicationModal.vue' import ReceiptDetailModal from './components/ReceiptDetailModal.vue' import PaymentDetailModal from './components/PaymentDetailModal.vue' +import ReceiptModal from './components/ReceiptModal.vue' +import PaymentModal from './components/PaymentModal.vue' import { equipmentProcurementApi } from '@/apis/equipment/procurement' import { equipmentApprovalApi } from '@/apis/equipment/approval' import type { EquipmentListReq, EquipmentResp } from '@/apis/equipment/type' @@ -377,6 +393,10 @@ const currentReceiptData = ref(null) const paymentDetailModalVisible = ref(false) const currentPaymentData = ref(null) +// 收货弹窗控制 +const receiptModalVisible = ref(false) +const paymentModalVisible = ref(false) + // 表格选择 const selectedRowKeys = ref([]) const rowSelection = reactive({ @@ -988,17 +1008,8 @@ const canReceiveGoods = (record: EquipmentResp) => { // 收货操作 const handleReceiveGoods = async (record: EquipmentResp) => { - try { - // 暂时模拟API调用 - // await equipmentProcurementApi.receiveGoods(record.equipmentId) - message.success('收货成功') - // 模拟状态更新 - (record as any).receiptStatus = 'RECEIVED' - loadData(currentSearchParams.value) - } catch (error: any) { - console.error('收货失败:', error) - message.error(error?.message || '收货失败') - } + currentReceiptData.value = { ...record } + receiptModalVisible.value = true } // 查看收货详情 @@ -1007,6 +1018,12 @@ const handleViewReceipt = (record: EquipmentResp) => { receiptDetailModalVisible.value = true } +// 收货成功回调 +const handleReceiptSuccess = () => { + receiptModalVisible.value = false + loadData(currentSearchParams.value) +} + // 检查是否可以付款 const canMakePayment = (record: EquipmentResp) => { const paymentStatus = (record as any).paymentStatus @@ -1015,17 +1032,8 @@ const canMakePayment = (record: EquipmentResp) => { // 付款操作 const handleMakePayment = async (record: EquipmentResp) => { - try { - // 暂时模拟API调用 - // await equipmentProcurementApi.makePayment(record.equipmentId) - message.success('付款成功') - // 模拟状态更新 - (record as any).paymentStatus = 'PAID' - loadData(currentSearchParams.value) - } catch (error: any) { - console.error('付款失败:', error) - message.error(error?.message || '付款失败') - } + currentPaymentData.value = { ...record } + paymentModalVisible.value = true } // 查看支付详情 @@ -1034,6 +1042,12 @@ const handleViewPayment = (record: EquipmentResp) => { paymentDetailModalVisible.value = true } +// 支付成功回调 +const handlePaymentSuccess = () => { + paymentModalVisible.value = false + loadData(currentSearchParams.value) +} + // 获取审批状态颜色 const getApprovalStatusColor = (status: string) => { const colorMap: Record = { From e81a9430b549041e7a0ad3e8b3d9fff2df377d04 Mon Sep 17 00:00:00 2001 From: crushing1111 <909171938@qq.com> Date: Wed, 13 Aug 2025 14:26:19 +0800 Subject: [PATCH 07/19] =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E7=BB=9F=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/task/task-approval/TaskApproval.vue | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/views/task/task-approval/TaskApproval.vue b/src/views/task/task-approval/TaskApproval.vue index c41d446..8defe5c 100644 --- a/src/views/task/task-approval/TaskApproval.vue +++ b/src/views/task/task-approval/TaskApproval.vue @@ -200,47 +200,51 @@ const handleReject = (taskId: string) => { /* 待审批状态样式 */ .status-item.pending { - border-top-color: #f59e0b; - background-color: rgba(245, 158, 11, 0.1); /* 浅橙色背景 */ + background-color: #f59e0b; + background-image: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); } .status-item.pending .count { - color: #f59e0b; + color: #fff; } /* 已通过状态样式 */ .status-item.approved { - border-top-color: #10b981; - background-color: rgba(16, 185, 129, 0.1); /* 浅绿色背景 */ + background-color: #10b981; + background-image: linear-gradient(135deg, #10b981 0%, #059669 100%); } .status-item.approved .count { - color: #10b981; + color: #fff; } /* 已拒绝状态样式 */ .status-item.rejected { - border-top-color: #ef4444; - background-color: rgba(239, 68, 68, 0.1); /* 浅红色背景 */ + background-color: #ef4444; + background-image: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); } .status-item.rejected .count { - color: #ef4444; + color: #fff; } /* 状态文字样式 */ .status-text { - font-size: 14px; - color: #333; + font-size: 16px; + font-weight: 500; + color: #fff; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); } /* 数字样式 */ .count { - font-size: 20px; - font-weight: bold; + font-size: 28px; + font-weight: 700; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.15); + letter-spacing: 0.5px; } /* 激活态样式(可选,点击后高亮) */ .status-item.active { transform: scale(1.02); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); } .content { From 2e5cbe31e5945a5748748c56ca3aab7dcb245e69 Mon Sep 17 00:00:00 2001 From: chabai <14799297+dhasjklhdfjkasfbhfasfj@user.noreply.gitee.com> Date: Wed, 13 Aug 2025 14:27:43 +0800 Subject: [PATCH 08/19] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=99=BA=E8=83=BD?= =?UTF-8?q?=E5=95=86=E5=8A=A1=E6=A8=A1=E5=9D=97=E5=AF=B9=E7=9A=84=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=88=86=E9=A1=B5=E5=92=8C=E9=A1=B5=E9=9D=A2=E5=B8=83?= =?UTF-8?q?=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/bussiness-data/bussiness.vue | 174 +++++++++++++++---------- 1 file changed, 102 insertions(+), 72 deletions(-) diff --git a/src/views/bussiness-data/bussiness.vue b/src/views/bussiness-data/bussiness.vue index dcc7abf..2e6b855 100644 --- a/src/views/bussiness-data/bussiness.vue +++ b/src/views/bussiness-data/bussiness.vue @@ -143,19 +143,22 @@ - - - -
- + +
+
+ 文件列表 ({{ fileList.length }}) +
+
+ +
@@ -336,7 +339,7 @@
-
+
@@ -2629,6 +2634,7 @@ onMounted(() => { background: var(--color-bg-1); min-height: 0; max-height: calc(100vh - 120px); + position: relative; } .file-card { @@ -2640,6 +2646,7 @@ onMounted(() => { position: relative; height: 100%; overflow: hidden; + padding-bottom: 80px; /* 为分页器留出空间 */ } /* 表格容器 */ @@ -2654,7 +2661,7 @@ onMounted(() => { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); margin-bottom: 0; min-height: 300px; - max-height: calc(100vh - 300px); + max-height: calc(100vh - 380px); /* 调整高度为分页器留出空间 */ } /* 表头行样式 */ @@ -3100,14 +3107,65 @@ onMounted(() => { } /* 分页样式 */ -.pagination-container, .file-pagination { - margin-top: 16px; - text-align: right; - padding: 0 16px 16px; -} - -.file-pagination { +.pagination-container { + position: absolute; + bottom: 0; + left: 0; + right: 0; + background: var(--color-bg-1); + padding: 16px 24px; border-top: 1px solid var(--color-border); + display: flex; + justify-content: flex-end; + align-items: center; + z-index: 10; + box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.06); + margin-top: 0; + + .arco-pagination { + margin: 0; + + .arco-pagination-item { + border-radius: 6px; + margin: 0 4px; + transition: all 0.2s ease; + + &:hover { + border-color: var(--color-primary); + color: var(--color-primary); + } + + &.arco-pagination-item-active { + background: var(--color-primary); + border-color: var(--color-primary); + color: white; + } + } + + .arco-pagination-prev, + .arco-pagination-next { + border-radius: 6px; + transition: all 0.2s ease; + + &:hover { + border-color: var(--color-primary); + color: var(--color-primary); + } + } + + .arco-pagination-size-changer { + margin-left: 16px; + } + + .arco-pagination-jumper { + margin-left: 16px; + } + + .arco-pagination-total { + color: var(--color-text-2); + font-size: 14px; + } + } } @@ -3530,9 +3588,29 @@ onMounted(() => { 100% { transform: translateX(100%); } } +/* 文件头部容器样式 */ +.file-header-container { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 16px; + padding: 16px 0; +} + +.file-title { + display: flex; + align-items: center; +} + +.file-list-title { + font-size: 16px; + font-weight: 600; + color: var(--color-text-1); + margin: 0; +} + /* 文件搜索样式 */ .file-search-container { - margin: 16px 0; display: flex; align-items: center; gap: 12px; @@ -3601,55 +3679,7 @@ onMounted(() => { border-top-color: var(--color-primary); } -/* 文件分页样式 */ -.file-pagination { - position: sticky; - bottom: 0; - left: 0; - right: 0; - margin-top: 16px; - padding: 16px 0; - display: flex; - justify-content: center; - border-top: 1px solid var(--color-border); - background: var(--color-bg-1); - flex-shrink: 0; - z-index: 10; - - .arco-pagination { - .arco-pagination-total { - color: var(--color-text-2); - font-size: 14px; - } - - .arco-pagination-item { - border-radius: 6px; - transition: all 0.2s ease; - - &:hover { - border-color: var(--color-primary); - color: var(--color-primary); - } - - &.arco-pagination-item-active { - background: var(--color-primary); - border-color: var(--color-primary); - color: white; - } - } - - .arco-pagination-prev, - .arco-pagination-next { - border-radius: 6px; - transition: all 0.2s ease; - - &:hover { - border-color: var(--color-primary); - color: var(--color-primary); - } - } - } -} + /* 树形文件夹结构 */ .folder-tree-container { From 79b8eb340fc5497c9cb21dcb3ac3beeda5d00a9f Mon Sep 17 00:00:00 2001 From: chabai <14799297+dhasjklhdfjkasfbhfasfj@user.noreply.gitee.com> Date: Wed, 13 Aug 2025 14:44:02 +0800 Subject: [PATCH 09/19] =?UTF-8?q?=E6=99=BA=E8=83=BD=E5=95=86=E5=8A=A1?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E5=8F=AF=E4=BB=A5=E8=B0=83=E6=95=B4=E5=B7=A6?= =?UTF-8?q?=E4=BE=A7=E6=A0=8F=E7=9A=84=E5=AE=BD=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/types/components.d.ts | 63 +++++++++++ src/views/bussiness-data/bussiness.vue | 142 ++++++++++++++++++++++++- 2 files changed, 203 insertions(+), 2 deletions(-) diff --git a/src/types/components.d.ts b/src/types/components.d.ts index 7fa6b1b..a427ece 100644 --- a/src/types/components.d.ts +++ b/src/types/components.d.ts @@ -7,7 +7,70 @@ export {} declare module 'vue' { export interface GlobalComponents { + ApprovalAssistant: typeof import('./../components/ApprovalAssistant/index.vue')['default'] + ApprovalMessageItem: typeof import('./../components/NotificationCenter/ApprovalMessageItem.vue')['default'] + Avatar: typeof import('./../components/Avatar/index.vue')['default'] + Breadcrumb: typeof import('./../components/Breadcrumb/index.vue')['default'] + CellCopy: typeof import('./../components/CellCopy/index.vue')['default'] + Chart: typeof import('./../components/Chart/index.vue')['default'] + CircularProgress: typeof import('./../components/CircularProgress/index.vue')['default'] + ColumnSetting: typeof import('./../components/GiTable/src/components/ColumnSetting.vue')['default'] + CronForm: typeof import('./../components/GenCron/CronForm/index.vue')['default'] + CronModal: typeof import('./../components/GenCron/CronModal/index.vue')['default'] + DateRangePicker: typeof import('./../components/DateRangePicker/index.vue')['default'] + DayForm: typeof import('./../components/GenCron/CronForm/component/day-form.vue')['default'] + FilePreview: typeof import('./../components/FilePreview/index.vue')['default'] + GiCellAvatar: typeof import('./../components/GiCell/GiCellAvatar.vue')['default'] + GiCellGender: typeof import('./../components/GiCell/GiCellGender.vue')['default'] + GiCellStatus: typeof import('./../components/GiCell/GiCellStatus.vue')['default'] + GiCellTag: typeof import('./../components/GiCell/GiCellTag.vue')['default'] + GiCellTags: typeof import('./../components/GiCell/GiCellTags.vue')['default'] + GiCodeView: typeof import('./../components/GiCodeView/index.vue')['default'] + GiDot: typeof import('./../components/GiDot/index.tsx')['default'] + GiEditTable: typeof import('./../components/GiEditTable/GiEditTable.vue')['default'] + GiFooter: typeof import('./../components/GiFooter/index.vue')['default'] + GiForm: typeof import('./../components/GiForm/src/GiForm.vue')['default'] + GiIconBox: typeof import('./../components/GiIconBox/index.vue')['default'] + GiIconSelector: typeof import('./../components/GiIconSelector/index.vue')['default'] + GiIframe: typeof import('./../components/GiIframe/index.vue')['default'] + GiOption: typeof import('./../components/GiOption/index.vue')['default'] + GiOptionItem: typeof import('./../components/GiOptionItem/index.vue')['default'] + GiPageLayout: typeof import('./../components/GiPageLayout/index.vue')['default'] + GiSpace: typeof import('./../components/GiSpace/index.vue')['default'] + GiSplitButton: typeof import('./../components/GiSplitButton/index.vue')['default'] + GiSplitPane: typeof import('./../components/GiSplitPane/index.vue')['default'] + GiSplitPaneFlexibleBox: typeof import('./../components/GiSplitPane/components/GiSplitPaneFlexibleBox.vue')['default'] + GiSvgIcon: typeof import('./../components/GiSvgIcon/index.vue')['default'] + GiTable: typeof import('./../components/GiTable/src/GiTable.vue')['default'] + GiTag: typeof import('./../components/GiTag/index.tsx')['default'] + GiThemeBtn: typeof import('./../components/GiThemeBtn/index.vue')['default'] + HourForm: typeof import('./../components/GenCron/CronForm/component/hour-form.vue')['default'] + Icon403: typeof import('./../components/icons/Icon403.vue')['default'] + Icon404: typeof import('./../components/icons/Icon404.vue')['default'] + Icon500: typeof import('./../components/icons/Icon500.vue')['default'] + IconBorders: typeof import('./../components/icons/IconBorders.vue')['default'] + IconTableSize: typeof import('./../components/icons/IconTableSize.vue')['default'] + IconTreeAdd: typeof import('./../components/icons/IconTreeAdd.vue')['default'] + IconTreeReduce: typeof import('./../components/icons/IconTreeReduce.vue')['default'] + ImageImport: typeof import('./../components/ImageImport/index.vue')['default'] + ImageImportWizard: typeof import('./../components/ImageImportWizard/index.vue')['default'] + IndustrialImageList: typeof import('./../components/IndustrialImageList/index.vue')['default'] + JsonPretty: typeof import('./../components/JsonPretty/index.vue')['default'] + MinuteForm: typeof import('./../components/GenCron/CronForm/component/minute-form.vue')['default'] + MonthForm: typeof import('./../components/GenCron/CronForm/component/month-form.vue')['default'] + NotificationCenter: typeof import('./../components/NotificationCenter/index.vue')['default'] + ParentView: typeof import('./../components/ParentView/index.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] + SecondForm: typeof import('./../components/GenCron/CronForm/component/second-form.vue')['default'] + SplitPanel: typeof import('./../components/SplitPanel/index.vue')['default'] + TextCopy: typeof import('./../components/TextCopy/index.vue')['default'] + TurbineGrid: typeof import('./../components/TurbineGrid/index.vue')['default'] + UserSelect: typeof import('./../components/UserSelect/index.vue')['default'] + Verify: typeof import('./../components/Verify/index.vue')['default'] + VerifyPoints: typeof import('./../components/Verify/Verify/VerifyPoints.vue')['default'] + VerifySlide: typeof import('./../components/Verify/Verify/VerifySlide.vue')['default'] + WeekForm: typeof import('./../components/GenCron/CronForm/component/week-form.vue')['default'] + YearForm: typeof import('./../components/GenCron/CronForm/component/year-form.vue')['default'] } } diff --git a/src/views/bussiness-data/bussiness.vue b/src/views/bussiness-data/bussiness.vue index 2e6b855..4c4cb9a 100644 --- a/src/views/bussiness-data/bussiness.vue +++ b/src/views/bussiness-data/bussiness.vue @@ -2,7 +2,7 @@
- + + @@ -2362,6 +2371,86 @@ const fileTypeText = (type) => { // 侧边栏控制 const sidebarCollapsed = ref(false); +// 侧边栏宽度控制 +const sidebarWidth = ref(260); // 默认宽度 +const isResizing = ref(false); +const startX = ref(0); +const startWidth = ref(0); + +// 从localStorage加载保存的宽度 +const loadSavedWidth = () => { + try { + const savedWidth = localStorage.getItem('bussiness-sidebar-width'); + if (savedWidth) { + const width = parseInt(savedWidth); + if (width >= 200 && width <= 500) { + sidebarWidth.value = width; + } + } + } catch (error) { + console.warn('加载侧边栏宽度失败:', error); + } +}; + +// 保存宽度到localStorage +const saveWidth = (width) => { + try { + localStorage.setItem('bussiness-sidebar-width', width.toString()); + } catch (error) { + console.warn('保存侧边栏宽度失败:', error); + } +}; + + + +// 开始拖拽调整大小 +const startResize = (event) => { + event.preventDefault(); + isResizing.value = true; + startX.value = event.type === 'mousedown' ? event.clientX : event.touches[0].clientX; + startWidth.value = sidebarWidth.value; + + // 添加事件监听器 + if (event.type === 'mousedown') { + document.addEventListener('mousemove', handleResize); + document.addEventListener('mouseup', stopResize); + } else { + document.addEventListener('touchmove', handleResize); + document.addEventListener('touchend', stopResize); + } + + // 添加样式 + document.body.classList.add('resizing'); +}; + +// 处理拖拽调整 +const handleResize = (event) => { + if (!isResizing.value) return; + + const currentX = event.type === 'mousemove' ? event.clientX : event.touches[0].clientX; + const deltaX = currentX - startX.value; + const newWidth = Math.max(200, Math.min(500, startWidth.value + deltaX)); + + sidebarWidth.value = newWidth; +}; + +// 停止拖拽调整 +const stopResize = () => { + isResizing.value = false; + + // 移除事件监听器 + document.removeEventListener('mousemove', handleResize); + document.removeEventListener('mouseup', stopResize); + document.removeEventListener('touchmove', handleResize); + document.removeEventListener('touchend', stopResize); + + // 移除样式 + document.body.classList.remove('resizing'); + + // 保存宽度 + saveWidth(sidebarWidth.value); +}; + // 打开新建文件夹对话框 const handleCreateFolder = () => { folderForm.id = ''; @@ -2448,6 +2537,7 @@ watch(uploadDialogVisible, (visible) => { // 初始化加载 onMounted(() => { + loadSavedWidth(); // 加载保存的侧边栏宽度 initData(); }); @@ -3786,4 +3876,52 @@ onMounted(() => { } } } + +/* 拖拽分隔线样式 */ +.sidebar-resizer { + position: absolute; + top: 0; + right: 0; + width: 6px; + height: 100%; + cursor: col-resize; + background: transparent; + transition: background-color 0.2s ease; + z-index: 10; + + &:hover { + background: rgba(var(--color-primary-6), 0.1); + } + + &:active { + background: rgba(var(--color-primary-6), 0.2); + } +} + +.resizer-handle { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 2px; + height: 40px; + background: var(--color-primary); + border-radius: 1px; + opacity: 0.6; + transition: opacity 0.2s ease; +} + +.sidebar-resizer:hover .resizer-handle { + opacity: 1; +} + +/* 拖拽时的全局样式 */ +body.resizing { + cursor: col-resize !important; + user-select: none !important; +} + +body.resizing * { + cursor: col-resize !important; +} From a3aaf7da071e98a82d50f044295052af9ace97fa Mon Sep 17 00:00:00 2001 From: Maple <869445424@qq.com> Date: Wed, 13 Aug 2025 16:21:33 +0800 Subject: [PATCH 10/19] =?UTF-8?q?fix:=E6=96=BD=E5=B7=A5=E7=AB=8B=E9=A1=B9?= =?UTF-8?q?=E6=96=B0=E5=BB=BA=E9=A1=B9=E7=9B=AE=E7=9A=84=E6=97=B6=E5=80=99?= =?UTF-8?q?=E7=9A=84=E5=AD=97=E6=AE=B5=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/project/type.ts | 10 +- src/types/auto-imports.d.ts | 2 +- src/views/project/index.vue | 392 ++++++++++++++++++++++++++---------- 3 files changed, 295 insertions(+), 109 deletions(-) diff --git a/src/apis/project/type.ts b/src/apis/project/type.ts index 506cb93..5d5f527 100644 --- a/src/apis/project/type.ts +++ b/src/apis/project/type.ts @@ -18,6 +18,8 @@ export interface ProjectResp { projectCategory?: string // 项目类型/服务 projectManagerId?: string // 项目经理ID projectManagerName?: string // 项目经理姓名 + projectOrigin?: string // 项目来源 + projectStaff?: string[] // 施工人员 startDate?: string // 开始日期 endDate?: string // 结束日期 @@ -28,10 +30,10 @@ export interface ProjectResp { coverUrl?: string // 封面URL createDt?: Date updateDt?: Date - + // 为了保持向后兼容,添加一些别名字段 id?: string // projectId的别名 - fieldName?: string // farmName的别名 + fieldName?: string // farmName的别名 fieldLocation?: string // farmAddress的别名 commissionUnit?: string // client的别名 commissionContact?: string // clientContact的别名 @@ -85,7 +87,7 @@ export interface TaskQuery { status?: string } -export interface TaskPageQuery extends TaskQuery, PageQuery {} +export interface TaskPageQuery extends TaskQuery, PageQuery {} // ==================== 人员调度相关类型 ==================== @@ -339,4 +341,4 @@ export interface PageRes { total: number page: number pageSize: number -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/types/auto-imports.d.ts b/src/types/auto-imports.d.ts index 369aad4..eab6be6 100644 --- a/src/types/auto-imports.d.ts +++ b/src/types/auto-imports.d.ts @@ -70,6 +70,6 @@ declare global { // for type re-export declare global { // @ts-ignore - export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' + export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue' import('vue') } diff --git a/src/views/project/index.vue b/src/views/project/index.vue index b523e17..e9a8e4e 100644 --- a/src/views/project/index.vue +++ b/src/views/project/index.vue @@ -2,19 +2,21 @@ 项目管理页面 已完成接口对接: 1. 项目列表查询 (listProject) - 支持分页和条件查询 - 2. 项目新增 (addProject) + 2. 项目新增 (addProject) 3. 项目修改 (updateProject) 4. 项目删除 (deleteProject) 5. 项目导出 (exportProject) 6. 项目导入 (importProject) - + 所有API调用都已添加错误处理和类型安全检查 --> @@ -107,8 +97,6 @@ import { } from '@/apis/industrial-image' import DeformationTap from './components/DeformationTap.vue' -// 预览弹窗引用(待重新设计) -// const previewModal = ref() // 活动选项卡 const activeTab = ref('image') diff --git a/src/views/tower-monitoring/clearance.vue b/src/views/tower-monitoring/clearance.vue new file mode 100644 index 0000000..b3ff709 --- /dev/null +++ b/src/views/tower-monitoring/clearance.vue @@ -0,0 +1,498 @@ + + + + + + diff --git a/src/views/tower-monitoring/deformation.vue b/src/views/tower-monitoring/deformation.vue new file mode 100644 index 0000000..1152c74 --- /dev/null +++ b/src/views/tower-monitoring/deformation.vue @@ -0,0 +1,665 @@ + + + + + + \ No newline at end of file diff --git a/src/views/tower-monitoring/vibration.vue b/src/views/tower-monitoring/vibration.vue new file mode 100644 index 0000000..07d9e49 --- /dev/null +++ b/src/views/tower-monitoring/vibration.vue @@ -0,0 +1,497 @@ + + + + + + diff --git a/src/views/tower-monitoring/whistle.vue b/src/views/tower-monitoring/whistle.vue new file mode 100644 index 0000000..07d9e49 --- /dev/null +++ b/src/views/tower-monitoring/whistle.vue @@ -0,0 +1,497 @@ + + + + + + From f9c5bc22c230dfab93ae294defb24d126ea7b2a2 Mon Sep 17 00:00:00 2001 From: "Mr.j" <2221464500@qq.com> Date: Thu, 14 Aug 2025 10:31:24 +0800 Subject: [PATCH 17/19] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E4=B8=AD=E5=BF=83=E9=81=AE=E7=9B=96=E5=B1=82=E7=BA=A7=E5=A4=AA?= =?UTF-8?q?=E9=AB=98=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/NotificationCenter/index.vue | 30 +++++----- src/types/auto-imports.d.ts | 2 +- src/types/components.d.ts | 63 --------------------- 3 files changed, 16 insertions(+), 79 deletions(-) diff --git a/src/components/NotificationCenter/index.vue b/src/components/NotificationCenter/index.vue index 7fb4ccf..7230f61 100644 --- a/src/components/NotificationCenter/index.vue +++ b/src/components/NotificationCenter/index.vue @@ -19,7 +19,7 @@ :mask-closable="true" :closable="true" :destroy-on-close="false" - :z-index="999999" + :z-index="1000" class="notification-modal" > @@ -673,7 +673,7 @@ defineExpose({ diff --git a/src/types/auto-imports.d.ts b/src/types/auto-imports.d.ts index eab6be6..369aad4 100644 --- a/src/types/auto-imports.d.ts +++ b/src/types/auto-imports.d.ts @@ -70,6 +70,6 @@ declare global { // for type re-export declare global { // @ts-ignore - export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue' + export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' import('vue') } diff --git a/src/types/components.d.ts b/src/types/components.d.ts index a427ece..7fa6b1b 100644 --- a/src/types/components.d.ts +++ b/src/types/components.d.ts @@ -7,70 +7,7 @@ export {} declare module 'vue' { export interface GlobalComponents { - ApprovalAssistant: typeof import('./../components/ApprovalAssistant/index.vue')['default'] - ApprovalMessageItem: typeof import('./../components/NotificationCenter/ApprovalMessageItem.vue')['default'] - Avatar: typeof import('./../components/Avatar/index.vue')['default'] - Breadcrumb: typeof import('./../components/Breadcrumb/index.vue')['default'] - CellCopy: typeof import('./../components/CellCopy/index.vue')['default'] - Chart: typeof import('./../components/Chart/index.vue')['default'] - CircularProgress: typeof import('./../components/CircularProgress/index.vue')['default'] - ColumnSetting: typeof import('./../components/GiTable/src/components/ColumnSetting.vue')['default'] - CronForm: typeof import('./../components/GenCron/CronForm/index.vue')['default'] - CronModal: typeof import('./../components/GenCron/CronModal/index.vue')['default'] - DateRangePicker: typeof import('./../components/DateRangePicker/index.vue')['default'] - DayForm: typeof import('./../components/GenCron/CronForm/component/day-form.vue')['default'] - FilePreview: typeof import('./../components/FilePreview/index.vue')['default'] - GiCellAvatar: typeof import('./../components/GiCell/GiCellAvatar.vue')['default'] - GiCellGender: typeof import('./../components/GiCell/GiCellGender.vue')['default'] - GiCellStatus: typeof import('./../components/GiCell/GiCellStatus.vue')['default'] - GiCellTag: typeof import('./../components/GiCell/GiCellTag.vue')['default'] - GiCellTags: typeof import('./../components/GiCell/GiCellTags.vue')['default'] - GiCodeView: typeof import('./../components/GiCodeView/index.vue')['default'] - GiDot: typeof import('./../components/GiDot/index.tsx')['default'] - GiEditTable: typeof import('./../components/GiEditTable/GiEditTable.vue')['default'] - GiFooter: typeof import('./../components/GiFooter/index.vue')['default'] - GiForm: typeof import('./../components/GiForm/src/GiForm.vue')['default'] - GiIconBox: typeof import('./../components/GiIconBox/index.vue')['default'] - GiIconSelector: typeof import('./../components/GiIconSelector/index.vue')['default'] - GiIframe: typeof import('./../components/GiIframe/index.vue')['default'] - GiOption: typeof import('./../components/GiOption/index.vue')['default'] - GiOptionItem: typeof import('./../components/GiOptionItem/index.vue')['default'] - GiPageLayout: typeof import('./../components/GiPageLayout/index.vue')['default'] - GiSpace: typeof import('./../components/GiSpace/index.vue')['default'] - GiSplitButton: typeof import('./../components/GiSplitButton/index.vue')['default'] - GiSplitPane: typeof import('./../components/GiSplitPane/index.vue')['default'] - GiSplitPaneFlexibleBox: typeof import('./../components/GiSplitPane/components/GiSplitPaneFlexibleBox.vue')['default'] - GiSvgIcon: typeof import('./../components/GiSvgIcon/index.vue')['default'] - GiTable: typeof import('./../components/GiTable/src/GiTable.vue')['default'] - GiTag: typeof import('./../components/GiTag/index.tsx')['default'] - GiThemeBtn: typeof import('./../components/GiThemeBtn/index.vue')['default'] - HourForm: typeof import('./../components/GenCron/CronForm/component/hour-form.vue')['default'] - Icon403: typeof import('./../components/icons/Icon403.vue')['default'] - Icon404: typeof import('./../components/icons/Icon404.vue')['default'] - Icon500: typeof import('./../components/icons/Icon500.vue')['default'] - IconBorders: typeof import('./../components/icons/IconBorders.vue')['default'] - IconTableSize: typeof import('./../components/icons/IconTableSize.vue')['default'] - IconTreeAdd: typeof import('./../components/icons/IconTreeAdd.vue')['default'] - IconTreeReduce: typeof import('./../components/icons/IconTreeReduce.vue')['default'] - ImageImport: typeof import('./../components/ImageImport/index.vue')['default'] - ImageImportWizard: typeof import('./../components/ImageImportWizard/index.vue')['default'] - IndustrialImageList: typeof import('./../components/IndustrialImageList/index.vue')['default'] - JsonPretty: typeof import('./../components/JsonPretty/index.vue')['default'] - MinuteForm: typeof import('./../components/GenCron/CronForm/component/minute-form.vue')['default'] - MonthForm: typeof import('./../components/GenCron/CronForm/component/month-form.vue')['default'] - NotificationCenter: typeof import('./../components/NotificationCenter/index.vue')['default'] - ParentView: typeof import('./../components/ParentView/index.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] - SecondForm: typeof import('./../components/GenCron/CronForm/component/second-form.vue')['default'] - SplitPanel: typeof import('./../components/SplitPanel/index.vue')['default'] - TextCopy: typeof import('./../components/TextCopy/index.vue')['default'] - TurbineGrid: typeof import('./../components/TurbineGrid/index.vue')['default'] - UserSelect: typeof import('./../components/UserSelect/index.vue')['default'] - Verify: typeof import('./../components/Verify/index.vue')['default'] - VerifyPoints: typeof import('./../components/Verify/Verify/VerifyPoints.vue')['default'] - VerifySlide: typeof import('./../components/Verify/Verify/VerifySlide.vue')['default'] - WeekForm: typeof import('./../components/GenCron/CronForm/component/week-form.vue')['default'] - YearForm: typeof import('./../components/GenCron/CronForm/component/year-form.vue')['default'] } } From b147dbd313d4f71e9820c9bd925bc2702fada1fd Mon Sep 17 00:00:00 2001 From: chabai <14799297+dhasjklhdfjkasfbhfasfj@user.noreply.gitee.com> Date: Thu, 14 Aug 2025 11:39:05 +0800 Subject: [PATCH 18/19] bussiness --- src/views/bussiness-data/bussiness.vue | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/views/bussiness-data/bussiness.vue b/src/views/bussiness-data/bussiness.vue index 2e6b855..9cebdba 100644 --- a/src/views/bussiness-data/bussiness.vue +++ b/src/views/bussiness-data/bussiness.vue @@ -2458,8 +2458,6 @@ onMounted(() => { background-color: var(--color-bg-1); } - - /* 侧边栏样式 */ .folder-sidebar { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); From 7087e1e504c92b1cd79b8a362f659f830a18b62b Mon Sep 17 00:00:00 2001 From: chabai <14799297+dhasjklhdfjkasfbhfasfj@user.noreply.gitee.com> Date: Thu, 14 Aug 2025 11:41:21 +0800 Subject: [PATCH 19/19] =?UTF-8?q?=E8=BF=94=E5=9B=9E=E5=8E=9F=E6=9D=A5?= =?UTF-8?q?=E5=95=86=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/bussiness-data/bussiness.vue | 1755 ++++++++++++++--- .../bussiness-data/components/FileHeader.vue | 105 - .../bussiness-data/components/FileList.vue | 669 ------- .../components/FilePagination.vue | 116 -- .../bussiness-data/components/FileUpload.vue | 557 ------ 5 files changed, 1534 insertions(+), 1668 deletions(-) delete mode 100644 src/views/bussiness-data/components/FileHeader.vue delete mode 100644 src/views/bussiness-data/components/FileList.vue delete mode 100644 src/views/bussiness-data/components/FilePagination.vue delete mode 100644 src/views/bussiness-data/components/FileUpload.vue diff --git a/src/views/bussiness-data/bussiness.vue b/src/views/bussiness-data/bussiness.vue index 67adb78..9cebdba 100644 --- a/src/views/bussiness-data/bussiness.vue +++ b/src/views/bussiness-data/bussiness.vue @@ -2,7 +2,7 @@
- - + - - + + + + + + 上传文件 + + + + 新建文件夹 + + - + +
+
+ 文件列表 ({{ fileList.length }}) +
+
+ +
+
+ + + + + + + + + + + +
+ + + +
+ 文件名 +
+
+
+
+
+
+ +
+ 类型 +
+
+
+
+
+
+ +
+ 大小 +
+
+
+
+
+
+ +
+ 修改时间 +
+
+
+
+
+
+ 操作 +
+ + + + + +
+ +
+ {{ file.fileName || file.name }} +
{{ file.fileName || file.name }}
+
+
+
+ + + +
{{ fileTypeText(getFileExtension(file.fileName || file.name)) }}
+
+ + + +
{{ formatFileListSize(file.fileSize || file.size) }}
+
+ + + +
{{ formatUploadTime(file.uploadTime || file.uploadTime) }}
+
+ + + +
+ + + + + + + + + + + + +
+
+
+
- +
+ +
+ + + + + +
@@ -192,13 +408,128 @@ - - + + title="上传文件" + width="620px" + :mask-closable="false" + @ok="handleUploadSubmit" + @cancel="resetUpload" + :confirm-loading="uploading" + :ok-disabled="!canUpload" + > + + + +
+ + + + + 点击选择文件 + + + + +
+ 支持 {{ allowedFileTypesText }} 等格式,单个文件不超过 {{ maxFileSizeText }} +
+
+ + +
+
+
+ +
+
{{ file.name }}
+
+ {{ formatFileSize(file.size) }} + {{ file.error }} +
+
+
+ + +
+ +
+ + +
+ + + + + + +
+
+
+
+ + + + + 根目录 + + {{ folder.name }} + + + +
+
// 导入核心依赖 import { ref, reactive, onMounted, computed, watch, nextTick, h } from 'vue'; -// 导入子组件 -import FilePagination from './components/FilePagination.vue'; -import FileHeader from './components/FileHeader.vue'; -import FileList from './components/FileList.vue'; -import FileUpload from './components/FileUpload.vue'; import { IconFolder, IconFile, IconPlus, - + IconUpload, IconMenuFold, IconMenuUnfold, IconEye, @@ -263,10 +589,10 @@ import { IconRefresh, IconEdit, IconFolderAdd, - + IconStop } from '@arco-design/web-vue/es/icon'; import { Message, Modal } from '@arco-design/web-vue'; - +import axios from 'axios'; // 导入API import { @@ -277,7 +603,7 @@ import { deleteFolderApi, deleteFileApi, downloadFileApi, - + uploadFileApi, updateFileNameApi, renameFileApi, previewFileApi @@ -330,10 +656,47 @@ const folderRules = { ] }; +// 上传相关状态 +const uploadForm = reactive({ + folderId: '' +}); +const fileListTemp = ref([]); const folderFormRef = ref(null); +const uploadFormRef = ref(null); +const uploadRef = ref(null); const folderColor = 'var(--color-primary)'; const refreshing = ref(false); const folderSubmitting = ref(false); +const uploading = ref(false); +const allowedFileTypes = '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.zip,.txt,.jpg,.jpeg,.png,.gif,.bmp,.webp'; +const allowedFileTypesText = 'PDF, Word, Excel, PPT, 压缩文件, 文本文件, 图片文件'; +const maxFileSize = 1000 * 1024 * 1024; // 1000MB +const maxFileSizeText = '1000MB'; +const cancelTokens = ref({}); + +// 计算属性:是否有文件可上传 +const hasFiles = computed(() => { + console.log('=== hasFiles计算属性执行 ==='); + console.log('原始fileListTemp:', fileListTemp.value); + console.log('fileListTemp长度:', fileListTemp.value.length); + + const validFiles = fileListTemp.value.filter(file => { + const isValid = !file.error && file.status !== 'removed' && file.status !== 'canceled'; + console.log(`文件 ${file.name}: error=${file.error}, status=${file.status}, isValid=${isValid}`); + return isValid; + }); + + console.log('过滤后的有效文件:', validFiles); + console.log('有效文件数量:', validFiles.length); + console.log('hasFiles结果:', validFiles.length > 0); + + return validFiles.length > 0; +}); + +// 计算属性:是否可以上传 +const canUpload = computed(() => { + return hasFiles.value && !uploading.value && uploadForm.folderId; +}); // 计算属性:将平铺的文件夹数据转换为树形结构 const folderTreeData = computed(() => { @@ -668,12 +1031,8 @@ const loadFiles = async (folderId) => { if (sortField.value && sortOrder.value) { apiParams.sortField = sortField.value; apiParams.sortOrder = sortOrder.value; - console.log('📤 发送排序参数:', apiParams.sortField, apiParams.sortOrder); - } else { - console.log('📤 未发送排序参数'); } - console.log('📤 发送API参数:', apiParams); const res = await getFilesApi(apiParams); // 根据后端返回的数据结构处理 @@ -698,34 +1057,21 @@ const loadFiles = async (folderId) => { // 排序处理函数 const handleSortChange = (field) => { - console.log('=== 排序处理函数被调用 ==='); - console.log('传入的字段:', field); - const backendField = sortFieldMap[field]; - console.log('映射后的后端字段:', backendField); - if (!backendField) { - console.log('❌ 找不到对应的后端字段,退出'); - return; - } - - console.log('当前排序字段:', sortField.value); - console.log('当前排序方向:', sortOrder.value); + if (!backendField) return; // 切换排序方向 if (sortField.value === backendField) { sortOrder.value = sortOrder.value === 'asc' ? 'desc' : 'asc'; - console.log('✅ 切换排序方向为:', sortOrder.value); } else { // 新字段,默认降序 sortField.value = backendField; sortOrder.value = 'desc'; - console.log('✅ 设置新排序字段:', sortField.value, '排序方向:', sortOrder.value); } // 重新加载文件列表 if (currentFolderId.value) { - console.log('🔄 重新加载文件列表,文件夹ID:', currentFolderId.value); loadFiles(currentFolderId.value); } }; @@ -903,7 +1249,7 @@ const handleRenameFolder = (folder) => { } const folderId = folder.key || folder.id; - let currentName = folder.title || folder.name; + const currentName = folder.title || folder.name; if (!folderId) { Message.error('文件夹ID不能为空'); @@ -1080,9 +1426,159 @@ const submitFolderForm = async () => { } }; +// 格式化上传时间 +const formatUploadTime = (timeStr) => { + if (!timeStr) return '未知时间'; + const date = new Date(timeStr); + return date.toLocaleString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false + }); +}; +// 文件变化处理 +const handleFileChange = (info) => { + console.log('=== 文件变化事件 ==='); + console.log('完整info对象:', info); + + // 安全检查:确保 info 存在且是数组 + if (!info || !Array.isArray(info)) { + console.log('❌ info 不存在或不是数组,跳过处理'); + return; + } + + const fileList = info; + console.log('文件列表:', fileList); + console.log('文件列表长度:', fileList.length); + + // 检查是否是组件内部状态触发的(可能是之前的状态) + if (fileList.length === 0) { + console.log('⚠️ 文件列表为空,可能是组件内部状态,跳过处理'); + return; + } + + // 检查是否是对话框刚打开时的触发(可能是之前的状态) + if (!uploadDialogVisible.value) { + console.log('⚠️ 对话框未显示,可能是之前的状态触发,跳过处理'); + return; + } + + // 获取当前已存在的文件UID列表,用于去重 + const existingUids = fileListTemp.value.map(f => f.uid); + console.log('已存在的文件UID:', existingUids); + + // 获取当前文件夹中已存在的文件名列表,用于检查重复 + const currentFolderFiles = fileList.value || []; + const existingFileNames = currentFolderFiles.map(f => f.fileName || f.name); + console.log('当前文件夹中的文件:', existingFileNames); + + // 强制重置上传组件状态 + if (uploadRef.value) { + try { + uploadRef.value.reset(); + console.log('已强制重置上传组件'); + } catch (error) { + console.log('重置上传组件时出错:', error); + } + } + + // 处理新选择的文件 - 支持多文件选择,同时避免重复 + fileList.forEach((file, index) => { + console.log(`处理第${index + 1}个文件:`, file); + console.log('文件名称:', file.name); + console.log('文件大小:', file.size); + console.log('文件UID:', file.uid); + console.log('文件对象结构:', Object.keys(file)); + + // 检查文件是否已存在(去重) + if (existingUids.includes(file.uid)) { + console.log('⚠️ 文件已存在,跳过:', file.name); + return; + } + + // 确保文件对象有正确的属性 + const fileObj = { + uid: file.uid, + name: file.name, + size: file.size || file.file?.size || 0, + type: file.type || file.file?.type || '', + status: 'ready', + error: '', + originFileObj: file.file || file // 保存原始File对象 + }; + + console.log('开始验证新文件...'); + + // 检查文件是否已存在于当前文件夹中 + if (existingFileNames.includes(fileObj.name)) { + fileObj.error = '文件已存在于当前文件夹中'; + console.log('⚠️ 文件已存在于文件夹中:', fileObj.name); + // 显示友好的提示信息 + Message.warning(`文件 "${fileObj.name}" 已存在于当前文件夹中,已跳过`); + return; + } + + // 验证文件 + const isValid = validateFile(fileObj); + console.log('文件验证结果:', isValid); + + if (isValid) { + // 支持多文件:添加到列表 + fileListTemp.value.push(fileObj); + console.log('✅ 成功添加文件到列表:', fileObj.name); + } else { + console.log('❌ 文件验证失败:', fileObj.name, '错误:', fileObj.error); + } + }); + + console.log('=== 当前文件列表状态 ==='); + console.log('fileListTemp长度:', fileListTemp.value.length); + console.log('fileListTemp内容:', fileListTemp.value); + console.log('hasFiles计算结果:', hasFiles.value); +}; - +// 文件验证 +const validateFile = (file) => { + console.log('=== 开始验证文件 ==='); + console.log('验证文件:', file.name); + console.log('文件大小:', file.size); + + // 清除之前的错误 + file.error = ''; + + // 验证文件类型 + const ext = getFileExtension(file.name).toLowerCase(); + console.log('文件扩展名:', ext); + + const allowedExts = allowedFileTypes + .split(',') + .map(type => type.toLowerCase().replace(/^\./, '')); + + console.log('允许的扩展名:', allowedExts); + console.log('扩展名是否匹配:', allowedExts.includes(ext)); + + if (!allowedExts.includes(ext)) { + file.error = `不支持的文件类型,支持: ${allowedFileTypesText}`; + console.log('❌ 文件类型验证失败:', file.error); + return false; + } + + // 验证文件大小 + console.log('文件大小验证:', file.size, '<=', maxFileSize); + if (file.size > maxFileSize) { + file.error = `文件过大,最大支持 ${maxFileSizeText}`; + console.log('❌ 文件大小验证失败:', file.error); + return false; + } + + console.log('✅ 文件验证通过'); + return true; +}; // 获取文件扩展名 const getFileExtension = (fileName) => { @@ -1090,6 +1586,157 @@ const getFileExtension = (fileName) => { return lastDotIndex > 0 ? fileName.slice(lastDotIndex + 1) : ''; }; +// 获取文件图标颜色 +const fileColor = (extension) => { + const colorMap = { + pdf: '#ff4d4f', + doc: '#1890ff', + docx: '#1890ff', + xls: '#52c41a', + xlsx: '#52c41a', + ppt: '#faad14', + pptx: '#faad14', + zip: '#722ed1', + txt: '#8c8c8c', + // 图片格式颜色 + jpg: '#52c41a', + jpeg: '#52c41a', + png: '#1890ff', + gif: '#faad14', + bmp: '#722ed1', + webp: '#13c2c2' + }; + return colorMap[extension.toLowerCase()] || 'var(--color-text-3)'; +}; + + + +// 移除文件 +const removeFile = (file) => { + fileListTemp.value = fileListTemp.value.filter(f => f.uid !== file.uid); + + // 如果是正在上传的文件,取消请求 + if (file.status === 'uploading' && cancelTokens.value[file.uid]) { + cancelTokens.value[file.uid].cancel('上传已取消'); + delete cancelTokens.value[file.uid]; + } +}; + +// 取消上传 +const cancelUpload = (file) => { + if (cancelTokens.value[file.uid]) { + cancelTokens.value[file.uid].cancel('上传已取消'); + file.status = 'canceled'; + } +}; + +// 提交上传 +const handleUploadSubmit = async () => { + // 过滤有效文件 + const validFiles = fileListTemp.value.filter(file => + !file.error && file.status !== 'removed' && file.status !== 'canceled' + ); + + if (validFiles.length === 0) { + Message.warning('请选择有效的文件'); + return; + } + + // 验证文件夹ID + if (!uploadForm.folderId) { + Message.warning('请选择目标文件夹'); + return; + } + + uploading.value = true; + let hasError = false; + let hasFileExists = false; + + for (const fileItem of validFiles) { + // 获取原始File对象 + const realFile = fileItem.originFileObj || fileItem; + + if (!realFile) { + hasError = true; + continue; + } + + fileItem.status = 'uploading'; + fileItem.percent = 0; + + // 创建取消令牌 + const source = axios.CancelToken.source(); + cancelTokens.value[fileItem.uid] = source; + + // 调用API + const result = await uploadFileApi( + realFile, + Number(uploadForm.folderId), + (progressEvent) => { + if (progressEvent.lengthComputable) { + fileItem.percent = Math.round((progressEvent.loaded / progressEvent.total) * 100); + } + }, + source.token + ); + + // 检查上传结果 + if (result.code === 200) { + fileItem.status = 'success'; + fileItem.percent = 100; + } else if (result.code === 400 && result.msg && result.msg.includes('已存在')) { + // 文件已存在的情况 + fileItem.status = 'error'; + fileItem.error = '文件已存在'; + hasFileExists = true; + } else { + fileItem.status = 'error'; + fileItem.error = result.msg || '上传失败'; + hasError = true; + } + } + + // 根据结果显示相应的消息 + if (hasFileExists && !hasError) { + Message.warning('文件已存在'); + } else if (hasError) { + Message.error('上传失败'); + } else { + Message.success('上传成功'); + // 刷新当前文件夹文件列表 + if (currentFolderId.value === uploadForm.folderId) { + loadFiles(currentFolderId.value); + } + } + + resetUpload(); +}; + +// 重置上传表单 +const resetUpload = () => { + console.log('=== 重置上传表单 ==='); + + // 取消所有正在进行的上传 + Object.values(cancelTokens.value).forEach(source => { + source.cancel('上传已取消'); + }); + + // 重置所有状态 + uploadDialogVisible.value = false; + uploadForm.folderId = currentFolderId.value || ''; + fileListTemp.value = []; + cancelTokens.value = {}; + uploading.value = false; + + // 清空上传组件 + if (uploadRef.value) { + uploadRef.value.reset(); + console.log('已重置上传组件'); + } + + console.log('上传表单重置完成'); +}; + // 预览文件 const handlePreview = async (file) => { try { @@ -1660,97 +2307,61 @@ const handleDelete = (file) => { }); }; +// 格式化文件大小 +const formatFileSize = (fileSize) => { + const size = Number(fileSize); + if (isNaN(size) || size < 0) return '未知'; + + if (size < 1024) return `${size} B`; + if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`; + if (size < 1024 * 1024 * 1024) return `${(size / (1024 * 1024)).toFixed(1)} MB`; + return `${(size / (1024 * 1024 * 1024)).toFixed(1)} GB`; +}; + +// 专门用于文件列表的格式化函数(假设后端返回的是KB单位) +const formatFileListSize = (fileSize) => { + const size = Number(fileSize); + if (isNaN(size) || size < 0) return '未知'; + + // 假设后端返回的是KB单位 + if (size < 1024) { + return `${size} KB`; + } else if (size < 1024 * 1024) { + return `${(size / 1024).toFixed(1)} MB`; + } else { + return `${(size / (1024 * 1024)).toFixed(1)} GB`; + } +}; - - - - +const fileTypeText = (type) => { + const types = { + pdf: 'PDF文档', + doc: 'Word文档', + docx: 'Word文档', + xls: 'Excel表格', + xlsx: 'Excel表格', + ppt: 'PPT演示', + pptx: 'PPT演示', + zip: '压缩文件', + txt: '文本文件', + // 图片格式 + jpg: 'JPG图片', + jpeg: 'JPEG图片', + png: 'PNG图片', + gif: 'GIF图片', + bmp: 'BMP图片', + webp: 'WebP图片', + unknown: '未知类型' + }; + + return types[type] || type; +}; // 侧边栏控制 const sidebarCollapsed = ref(false); -// 侧边栏宽度控制 -const sidebarWidth = ref(260); // 默认宽度 -const isResizing = ref(false); -const startX = ref(0); -const startWidth = ref(0); - -// 从localStorage加载保存的宽度 -const loadSavedWidth = () => { - try { - const savedWidth = localStorage.getItem('bussiness-sidebar-width'); - if (savedWidth) { - const width = parseInt(savedWidth); - if (width >= 200 && width <= 500) { - sidebarWidth.value = width; - } - } - } catch (error) { - console.warn('加载侧边栏宽度失败:', error); - } -}; - -// 保存宽度到localStorage -const saveWidth = (width) => { - try { - localStorage.setItem('bussiness-sidebar-width', width.toString()); - } catch (error) { - console.warn('保存侧边栏宽度失败:', error); - } -}; - - - -// 开始拖拽调整大小 -const startResize = (event) => { - event.preventDefault(); - isResizing.value = true; - startX.value = event.type === 'mousedown' ? event.clientX : event.touches[0].clientX; - startWidth.value = sidebarWidth.value; - - // 添加事件监听器 - if (event.type === 'mousedown') { - document.addEventListener('mousemove', handleResize); - document.addEventListener('mouseup', stopResize); - } else { - document.addEventListener('touchmove', handleResize); - document.addEventListener('touchend', stopResize); - } - - // 添加样式 - document.body.classList.add('resizing'); -}; - -// 处理拖拽调整 -const handleResize = (event) => { - if (!isResizing.value) return; - - const currentX = event.type === 'mousemove' ? event.clientX : event.touches[0].clientX; - const deltaX = currentX - startX.value; - const newWidth = Math.max(200, Math.min(500, startWidth.value + deltaX)); - - sidebarWidth.value = newWidth; -}; - -// 停止拖拽调整 -const stopResize = () => { - isResizing.value = false; - - // 移除事件监听器 - document.removeEventListener('mousemove', handleResize); - document.removeEventListener('mouseup', stopResize); - document.removeEventListener('touchmove', handleResize); - document.removeEventListener('touchend', stopResize); - - // 移除样式 - document.body.classList.remove('resizing'); - - // 保存宽度 - saveWidth(sidebarWidth.value); -}; - // 打开新建文件夹对话框 const handleCreateFolder = () => { folderForm.id = ''; @@ -1761,15 +2372,27 @@ const handleCreateFolder = () => { // 打开上传文件对话框 const handleUploadFile = () => { - uploadDialogVisible.value = true; -}; - -// 上传成功回调 -const handleUploadSuccess = () => { - // 刷新当前文件夹文件列表 - if (currentFolderId.value) { - loadFiles(currentFolderId.value); + // 清空文件列表,避免显示之前上传的文件 + fileListTemp.value = []; + + // 重置上传组件状态 + if (uploadRef.value) { + try { + uploadRef.value.reset(); + // 强制清空组件的内部文件列表 + if (uploadRef.value.fileList) { + uploadRef.value.fileList = []; + } + if (uploadRef.value.fileListTemp) { + uploadRef.value.fileListTemp = []; + } + } catch (error) { + console.log('重置上传组件时出错:', error); + } } + + uploadForm.folderId = currentFolderId.value || ''; + uploadDialogVisible.value = true; }; // 侧边栏控制函数 @@ -1788,13 +2411,43 @@ watch(currentFolderId, (newId) => { } }); - +// 监听上传对话框显示状态,确保文件列表清空 +watch(uploadDialogVisible, (visible) => { + if (visible) { + console.log('=== 上传对话框已显示,确保文件列表清空 ==='); + // 立即清空文件列表 + fileListTemp.value = []; + console.log('✅ 已清空文件列表'); + + // 强制重置上传组件 + if (uploadRef.value) { + try { + uploadRef.value.reset(); + // 强制清空组件的内部文件列表 + if (uploadRef.value.fileList) { + uploadRef.value.fileList = []; + } + if (uploadRef.value.fileListTemp) { + uploadRef.value.fileListTemp = []; + } + console.log('✅ 已重置上传组件'); + } catch (error) { + console.log('❌ 重置上传组件时出错:', error); + } + } + + // 延迟再次清空,确保处理完所有可能的触发 + setTimeout(() => { + fileListTemp.value = []; + console.log('✅ 延迟清空文件列表'); + }, 100); + } +}); // 初始化加载 onMounted(() => { - loadSavedWidth(); // 加载保存的侧边栏宽度 initData(); }); @@ -1950,7 +2603,22 @@ onMounted(() => { /* 删除旧的span样式,因为现在使用.folder-name */ +/* 顶部导航样式 */ +.file-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 24px; + background: var(--color-bg-1); + border-bottom: 1px solid var(--color-border); + height: 64px; +} +.breadcrumbs { + display: flex; + align-items: center; + gap: 8px; +} /* 文件内容区域样式 */ .file-content { @@ -1979,13 +2647,360 @@ onMounted(() => { padding-bottom: 80px; /* 为分页器留出空间 */ } +/* 表格容器 */ +.file-grid-container { + flex: 1; + width: 100%; + margin-top: 16px; + border-radius: 8px; + border: 1px solid var(--color-border); + overflow-y: auto; + background-color: var(--color-bg-1); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); + margin-bottom: 0; + min-height: 300px; + max-height: calc(100vh - 380px); /* 调整高度为分页器留出空间 */ +} +/* 表头行样式 */ +.table-header-row { + padding: 0 16px; + height: 48px; + line-height: 48px; + background-color: var(--color-fill-1); + border-bottom: 1px solid var(--color-border); + font-size: 13px; + color: var(--color-text-3); + font-weight: 500; +} +/* 数据行样式 */ +.table-data-row { + display: flex; + padding: 0 16px; + height: 64px; + align-items: center; + border-bottom: 1px solid var(--color-border); + transition: all 0.25s ease; + cursor: pointer; + background-color: var(--color-bg-1); + + &:last-child { + border-bottom: none; + } + + &:hover { + background-color: rgba(22, 93, 255, 0.1); + transform: translateY(-1px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05); + } +} +/* 通用列样式 */ +.table-column { + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + white-space: nowrap; + padding: 0 8px; +} +.cell-content { + display: inline-block; + width: 100%; + text-align: center; + justify-content: center; + align-items: center; +} +.name-column { + padding: 0 14px; + justify-content: flex-start !important; +} +.file-info { + display: flex; + align-items: center; + width: 100%; +} +.file-icon { + font-size: 20px; + margin-right: 12px; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; +} + +.folder-icon { + color: var(--color-primary); + background-color: var(--color-primary-light-1); +} + +.file-name { + font-size: 14px; + font-weight: 500; + max-width: 220px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + transition: color 0.2s ease; +} + +.table-data-row:hover .file-name { + color: var(--color-primary); +} + +.type-column, .size-column, .time-column { + color: var(--color-text-3); + font-size: 14px; + justify-content: center; + align-items: center; + padding: 4px; +} + +.action-column { + justify-content: center; +} + +.file-main { + display: flex; + align-items: center; + width: 100%; +} + +.file-icon-large { + font-size: 24px; + margin-right: 12px; + flex-shrink: 0; +} + +.file-name-wrap { + flex: 1; + overflow: hidden; + min-width: 0; + display: flex; + justify-content: center; +} + +.file-name { + margin: 0; + font-size: 14px; + color: var(--color-text-1); + transition: color 0.2s; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + .table-data-row:hover & { + color: var(--color-primary); + } +} + +.file-name-small { + font-size: 14px; + color: var(--color-text-4); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-align: center; + display: flex; + justify-content: center; + align-items: center; + width: 100%; +} + +/* 操作按钮区域 */ +.file-actions { + display: flex; + gap: 4px; + justify-content: center; +} + +.action-btn { + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + background: transparent; + border: none; + cursor: pointer; + transition: all 0.2s ease; + color: var(--color-text-3); + + &:hover { + background: var(--color-fill-3); + color: var(--color-primary); + } +} + +.delete-btn { + &:hover { + color: var(--color-danger); + background-color: rgba(255, 77, 77, 0.05); + } +} + +/* 响应式调整 */ +@media (max-width: 1200px) { + .name-column { + flex: 0 0 35% !important; + max-width: 35% !important; + } + .time-column { + flex: 0 0 20% !important; + max-width: 20% !important; + } +} + +@media (max-width: 992px) { + .name-column { + flex: 0 0 45% !important; + max-width: 45% !important; + } + .type-column { + flex: 0 0 20% !important; + max-width: 20% !important; + } + .time-column { + display: none; + } + .action-column { + flex: 0 0 35% !important; + max-width: 35% !important; + } + .file-actions { + justify-content: flex-end; + } +} + +@media (max-width: 768px) { + .size-column, .time-column { + display: none; + } + .type-column { + flex: 0 0 25% !important; + max-width: 25% !important; + } + .name-column { + flex: 0 0 45% !important; + max-width: 45% !important; + } + .action-column { + flex: 0 0 30% !important; + max-width: 30% !important; + } + .file-content { + padding: 12px; + } + .file-grid-container { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } +} + +@media (max-width: 576px) { + .type-column { + display: none; + } + .name-column { + flex: 0 0 60% !important; + max-width: 60% !important; + } + .action-column { + flex: 0 0 40% !important; + max-width: 40% !important; + } + .file-header { + padding: 0 12px; + flex-wrap: wrap; + } + .breadcrumbs { + margin-bottom: 8px; + width: 100%; + } + .file-card { + min-height: auto; + } +} + +/* 浏览器缩放调整 */ +@media (max-height: 800px) { + .folder-content { + max-height: calc(100vh - 180px); + } + + .file-content { + max-height: calc(100vh - 100px); + } + + .file-grid-container { + max-height: calc(100vh - 280px); + } +} + +@media (max-height: 600px) { + .folder-content { + max-height: calc(100vh - 160px); + } + + .file-content { + max-height: calc(100vh - 80px); + } + + .file-grid-container { + max-height: calc(100vh - 260px); + } +} + +/* 空状态样式 */ +.initial-state, .empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 64px 0; + color: var(--color-text-3); + background-color: var(--color-fill-1); + border-radius: 8px; + text-align: center; +} + +.initial-icon { + font-size: 48px; + margin-bottom: 16px; + color: var(--color-text-4); +} + +:deep(.empty-state .arco-btn) { + margin-top: 16px; + padding: 8px 16px; + background-color: var(--color-primary); + color: white; + border-radius: 4px; + border: none; + cursor: pointer; + transition: all 0.2s ease; + font-weight: 500; +} + +:deep(.empty-state .arco-btn:hover) { + background-color: var(--color-primary-dark-1); + transform: translateY(-1px); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +:deep(.empty-state .arco-btn:active) { + transform: translateY(0); +} /* 上传区域样式 */ .upload-area { @@ -2015,9 +3030,141 @@ onMounted(() => { color: var(--color-text-3); } +.upload-hint { + margin-top: 8px; + font-size: 12px; + color: var(--color-text-4); +} +/* 上传相关样式 */ +.upload-file-list { + margin-top: 16px; + border-radius: 4px; + border: 1px solid var(--color-border); + overflow: hidden; +} +.upload-file-item { + display: flex; + align-items: center; + padding: 12px; + border-bottom: 1px solid var(--color-border); + + &:last-child { + border-bottom: none; + } + + &:hover { + background-color: var(--color-fill-1); + } +} +.file-info { + display: flex; + align-items: center; + flex: 1; + min-width: 0; +} + +.file-icon { + font-size: 20px; + margin-right: 12px; + flex-shrink: 0; +} + +.file-name { + flex: 1; + min-width: 0; +} + +.name-text { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 14px; +} + +.file-meta { + font-size: 12px; + color: var(--color-text-4); + margin-top: 4px; +} + +.file-error { + color: var(--color-danger); + margin-left: 8px; +} + +.file-progress { + flex: 1; + margin: 0 16px; +} + +.file-actions { + flex-shrink: 0; +} + +/* 分页样式 */ +.pagination-container { + position: absolute; + bottom: 0; + left: 0; + right: 0; + background: var(--color-bg-1); + padding: 16px 24px; + border-top: 1px solid var(--color-border); + display: flex; + justify-content: flex-end; + align-items: center; + z-index: 10; + box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.06); + margin-top: 0; + + .arco-pagination { + margin: 0; + + .arco-pagination-item { + border-radius: 6px; + margin: 0 4px; + transition: all 0.2s ease; + + &:hover { + border-color: var(--color-primary); + color: var(--color-primary); + } + + &.arco-pagination-item-active { + background: var(--color-primary); + border-color: var(--color-primary); + color: white; + } + } + + .arco-pagination-prev, + .arco-pagination-next { + border-radius: 6px; + transition: all 0.2s ease; + + &:hover { + border-color: var(--color-primary); + color: var(--color-primary); + } + } + + .arco-pagination-size-changer { + margin-left: 16px; + } + + .arco-pagination-jumper { + margin-left: 16px; + } + + .arco-pagination-total { + color: var(--color-text-2); + font-size: 14px; + } + } +} @@ -2307,14 +3454,228 @@ onMounted(() => { } } +/* 上传文件相关样式 */ +.upload-container { + display: flex; + flex-direction: column; + gap: 12px; +} +.upload-btn { + align-self: flex-start; + border-radius: 8px; + font-weight: 500; + transition: all 0.3s ease; + + &:hover { + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3); + } +} + +.upload-hint { + color: var(--color-text-3); + font-size: 12px; + line-height: 1.4; + padding: 8px 12px; + background: var(--color-fill-2); + border-radius: 6px; + border-left: 3px solid var(--color-primary-light-3); +} + +.upload-file-list { + margin-top: 16px; + border: 1px solid var(--color-border); + border-radius: 8px; + background: var(--color-bg-1); + max-height: 300px; + overflow-y: auto; +} + +.upload-file-item { + display: flex; + align-items: center; + padding: 12px 16px; + border-bottom: 1px solid var(--color-border); + transition: all 0.2s ease; + + &:last-child { + border-bottom: none; + } + + &:hover { + background: var(--color-fill-1); + } + + &.file-error { + background: rgba(255, 77, 79, 0.05); + border-left: 3px solid #ff4d4f; + } +} + +.file-info { + display: flex; + align-items: center; + flex: 1; + gap: 12px; + min-width: 0; +} + +.file-icon { + font-size: 20px; + flex-shrink: 0; +} + +.file-details { + flex: 1; + min-width: 0; +} + +.file-name { + font-weight: 500; + color: var(--color-text-1); + margin-bottom: 4px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.file-meta { + font-size: 12px; + color: var(--color-text-3); + display: flex; + align-items: center; + gap: 8px; +} + +.error-text { + color: #ff4d4f; + font-weight: 500; +} + +.file-progress { + margin: 0 16px; + min-width: 120px; +} + +.file-actions { + display: flex; + gap: 4px; +} + +.remove-btn { + color: var(--color-text-3); + + &:hover { + color: #ff4d4f; + background: rgba(255, 77, 79, 0.1); + } +} + +.cancel-btn { + color: var(--color-text-3); + + &:hover { + color: #faad14; + background: rgba(250, 173, 20, 0.1); + } +} @keyframes shimmer { 0% { transform: translateX(-100%); } 100% { transform: translateX(100%); } } +/* 文件头部容器样式 */ +.file-header-container { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 16px; + padding: 16px 0; +} +.file-title { + display: flex; + align-items: center; +} + +.file-list-title { + font-size: 16px; + font-weight: 600; + color: var(--color-text-1); + margin: 0; +} + +/* 文件搜索样式 */ +.file-search-container { + display: flex; + align-items: center; + gap: 12px; +} + +.file-search-input { + max-width: 300px; + transition: all 0.3s ease; + + &:focus-within { + box-shadow: 0 0 0 2px rgba(22, 93, 255, 0.1); + } +} + +/* 可排序表头样式 */ +.sortable-header { + display: flex; + align-items: center; + justify-content: center; + gap: 6px; + cursor: pointer; + padding: 4px 8px; + border-radius: 4px; + transition: all 0.2s ease; + user-select: none; +} + +.sortable-header:hover { + background: var(--color-fill-2); + color: var(--color-primary); +} + +.sort-indicator { + display: flex; + flex-direction: column; + gap: 1px; + margin-left: 4px; +} + +.sort-arrow { + width: 0; + height: 0; + border-left: 3px solid transparent; + border-right: 3px solid transparent; + transition: all 0.2s ease; +} + +.sort-arrow.up { + border-bottom: 3px solid var(--color-text-4); +} + +.sort-arrow.down { + border-top: 3px solid var(--color-text-4); +} + +.sort-arrow.active { + border-bottom-color: var(--color-primary); + border-top-color: var(--color-primary); +} + +.sortable-header:hover .sort-arrow.up { + border-bottom-color: var(--color-primary); +} + +.sortable-header:hover .sort-arrow.down { + border-top-color: var(--color-primary); +} @@ -2423,52 +3784,4 @@ onMounted(() => { } } } - -/* 拖拽分隔线样式 */ -.sidebar-resizer { - position: absolute; - top: 0; - right: 0; - width: 6px; - height: 100%; - cursor: col-resize; - background: transparent; - transition: background-color 0.2s ease; - z-index: 10; - - &:hover { - background: rgba(var(--color-primary-6), 0.1); - } - - &:active { - background: rgba(var(--color-primary-6), 0.2); - } -} - -.resizer-handle { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 2px; - height: 40px; - background: var(--color-primary); - border-radius: 1px; - opacity: 0.6; - transition: opacity 0.2s ease; -} - -.sidebar-resizer:hover .resizer-handle { - opacity: 1; -} - -/* 拖拽时的全局样式 */ -body.resizing { - cursor: col-resize !important; - user-select: none !important; -} - -body.resizing * { - cursor: col-resize !important; -} diff --git a/src/views/bussiness-data/components/FileHeader.vue b/src/views/bussiness-data/components/FileHeader.vue deleted file mode 100644 index 6d56a3f..0000000 --- a/src/views/bussiness-data/components/FileHeader.vue +++ /dev/null @@ -1,105 +0,0 @@ - - - - - diff --git a/src/views/bussiness-data/components/FileList.vue b/src/views/bussiness-data/components/FileList.vue deleted file mode 100644 index 4901a88..0000000 --- a/src/views/bussiness-data/components/FileList.vue +++ /dev/null @@ -1,669 +0,0 @@ - - - - - diff --git a/src/views/bussiness-data/components/FilePagination.vue b/src/views/bussiness-data/components/FilePagination.vue deleted file mode 100644 index 915a632..0000000 --- a/src/views/bussiness-data/components/FilePagination.vue +++ /dev/null @@ -1,116 +0,0 @@ - - - - - diff --git a/src/views/bussiness-data/components/FileUpload.vue b/src/views/bussiness-data/components/FileUpload.vue deleted file mode 100644 index 4802274..0000000 --- a/src/views/bussiness-data/components/FileUpload.vue +++ /dev/null @@ -1,557 +0,0 @@ - - - - -