| src/components/Editor/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/components/imageUpload/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/config.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/manifest.json | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/inspectionUpload/components/formDia.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/inspectionUpload/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/login.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/utils/request.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/components/Editor/index.vue
@@ -1,18 +1,18 @@ <template> <view class="editor-container"> <div class="editor"> <QuillEditor v-model:content="content" contentType="html" @textChange="(e) => emit('update:modelValue', content)" :options="options" :style="styles" /> <!-- <QuillEditor v-model:content="content"--> <!-- contentType="html"--> <!-- @textChange="(e) => emit('update:modelValue', content)"--> <!-- :options="options"--> <!-- :style="styles" />--> </div> </view> </template> <script setup> import { ref, computed, watch } from "vue"; import { QuillEditor } from "@vueup/vue-quill"; // import { QuillEditor } from "@vueup/vue-quill"; import "@vueup/vue-quill/dist/vue-quill.snow.css"; import { getToken } from "@/utils/auth"; src/components/imageUpload/index.vue
@@ -103,7 +103,7 @@ // Props 定义 const props = defineProps({ modelValue: [String, Object, Array], action: { type: String, default: "/common/minioUploads" }, action: { type: String, default: "/file/upload" }, data: { type: Object }, limit: { type: Number, default: 5 }, fileSize: { type: Number, default: 10 }, // 默认10MB,适合视频 @@ -182,7 +182,7 @@ const testServerConnection = () => { return new Promise((resolve) => { uni.request({ url: uploadFileUrl.value.replace('/common/minioUploads', '/common/test'), url: uploadFileUrl.value.replace('/common/minioUploads', '/file/upload'), method: 'GET', timeout: 5000, success: (res) => { @@ -383,6 +383,8 @@ const uploadTask = uni.uploadFile({ ...uploadParams, success: (res) => { console.log('res---', res) try { if (res.statusCode === 200) { const response = JSON.parse(res.data); src/config.js
@@ -1,11 +1,6 @@ // 应用全局配置 const config = { // baseUrl: 'https://vue.ruoyi.vip/prod-api', // baseUrl: 'http://localhost/prod-api', baseUrl: 'http://114.132.189.42:9068', // 新疆海川开心 // baseUrl: 'http://192.168.1.185:9988', // 本地测试 //cloud后台网关地址 // baseUrl: 'http://192.168.10.3:8080', baseUrl: 'http://114.132.189.42:9030', // 应用信息 appInfo: { // 应用名称 src/manifest.json
@@ -1,6 +1,6 @@ { "name" : "信息管理", "appid" : "__UNI__E1C100D", "name" : "湟水峡信息管理", "appid" : "__UNI__6C30F75", "description" : "", "versionName" : "1.0.0", "versionCode" : "100", @@ -23,8 +23,7 @@ /* 模块配置 */ "modules" : { "Camera" : {}, "Barcode" : {}, "Push" : {} "Barcode" : {} }, /* 应用发布信息 */ "distribute" : { @@ -53,17 +52,7 @@ "dSYMs" : false }, /* SDK配置 */ "sdkConfigs" : { "push" : { "unipush" : { "icons" : { "small" : { "ldpi" : "D:/xindao/wenjian/img/logo/app.png" } } } } }, "sdkConfigs" : {}, "icons" : { "android" : { "hdpi" : "unpackage/res/icons/72x72.png", src/pages/index.vue
@@ -18,74 +18,11 @@ <view class="hero-section"> <view class="bg-img"> <view class="hero-content"> <text class="hero-title">军泰伟业</text> <text class="hero-title">湟水峡</text> </view> <view class="hero-wave"></view> </view> </view> <!-- <view class="notice-section">--> <!-- <view class="notice">--> <!-- <view class="notice-content">--> <!-- <view class="notice-left">--> <!-- <text class="notice-status">通知</text>--> <!-- </view>--> <!-- <view class="notice-separator"></view>--> <!-- <view class="notice-right">--> <!-- <text class="notice-label">{{currentStatus}}</text>--> <!-- <text class="notice-text">当日销售设备数:<text class="notice-number">{{number}}<text class="notice-unit">个</text></text></text>--> <!-- </view>--> <!-- </view>--> <!-- </view>--> <!-- </view>--> <!-- 营销管理模块 --> <!-- <view class="common-module marketing-module">--> <!-- <view class="module-header">--> <!-- <view class="module-title-container">--> <!-- <text class="module-title">营销管理</text>--> <!-- </view>--> <!-- </view>--> <!-- <view class="module-content">--> <!-- <up-grid :border="false"--> <!-- col="4">--> <!-- <up-grid-item v-for="(item, index) in marketingItems"--> <!-- :key="index"--> <!-- @click="handleCommonItemClick(item)">--> <!-- <view class="icon-container"--> <!-- :style="{ background: item.bgColor }">--> <!-- <up-icon :name="item.icon"--> <!-- :size="58"--> <!-- color="#ffffff"></up-icon>--> <!-- </view>--> <!-- <text class="item-label">{{item.label}}</text>--> <!-- </up-grid-item>--> <!-- </up-grid>--> <!-- </view>--> <!-- </view>--> <!-- <!– 采购管理模块 –>--> <!-- <view class="common-module purchase-module">--> <!-- <view class="module-header">--> <!-- <view class="module-title-container">--> <!-- <text class="module-title">采购管理</text>--> <!-- </view>--> <!-- </view>--> <!-- <view class="module-content">--> <!-- <up-grid :border="false"--> <!-- col="4">--> <!-- <up-grid-item v-for="(item, index) in purchaseItems"--> <!-- :key="index"--> <!-- @click="handleCommonItemClick(item)">--> <!-- <view class="icon-container"--> <!-- :style="{ background: item.bgColor }">--> <!-- <up-icon :name="item.icon"--> <!-- :size="58"--> <!-- color="#ffffff"></up-icon>--> <!-- </view>--> <!-- <text class="item-label">{{item.label}}</text>--> <!-- </up-grid-item>--> <!-- </up-grid>--> <!-- </view>--> <!-- </view>--> <!-- <!– 协同办公模块 –>--> <view class="common-module collaboration-module"> <view class="module-header"> <view class="module-title-container"> @@ -109,83 +46,30 @@ </up-grid> </view> </view> <!-- 生产管控模块 --> <!-- <view class="common-module production-module">--> <!-- <view class="module-header">--> <!-- <view class="module-title-container">--> <!-- <text class="module-title">生产管控</text>--> <!-- </view>--> <!-- </view>--> <!-- <view class="module-content">--> <!-- <up-grid--> <!-- :border="false"--> <!-- col="4"--> <!-- >--> <!-- <up-grid-item--> <!-- v-for="(item, index) in productionItems"--> <!-- :key="index"--> <!-- @click="handleCommonItemClick(item)"--> <!-- >--> <!-- <view class="icon-container" :style="{ background: item.bgColor }">--> <!-- <up-icon--> <!-- :name="item.icon"--> <!-- :size="58"--> <!-- color="#ffffff"--> <!-- ></up-icon>--> <!-- </view>--> <!-- <text class="item-label">{{item.label}}</text>--> <!-- </up-grid-item>--> <!-- </up-grid>--> <!-- </view>--> <!-- </view>--> <!-- 生产管控模块 --> <view class="common-module equipment-module"> <view class="module-header"> <view class="module-title-container"> <text class="module-title">生产管控</text> </view> </view> <view class="module-content"> <up-grid :border="false" col="4"> <up-grid-item v-for="(item, index) in productionItems" :key="index" @click="handleCommonItemClick(item)"> <view class="icon-container" :style="{ background: item.bgColor }"> <up-icon :name="item.icon" :size="58" color="#ffffff"></up-icon> </view> <text class="item-label">{{item.label}}</text> </up-grid-item> </up-grid> </view> </view> <!-- 设备管理模块 --> <!-- <view class="common-module equipment-module">--> <!-- <view class="module-header">--> <!-- <view class="module-title-container">--> <!-- <text class="module-title">设备管理</text>--> <!-- </view>--> <!-- </view>--> <!-- <view class="module-content">--> <!-- <up-grid :border="false"--> <!-- col="4">--> <!-- <up-grid-item v-for="(item, index) in equipmentItems"--> <!-- :key="index"--> <!-- @click="handleCommonItemClick(item)">--> <!-- <view class="icon-container"--> <!-- :style="{ background: item.bgColor }">--> <!-- <up-icon :name="item.icon"--> <!-- :size="58"--> <!-- color="#ffffff"></up-icon>--> <!-- </view>--> <!-- <text class="item-label">{{item.label}}</text>--> <!-- </up-grid-item>--> <!-- </up-grid>--> <!-- </view>--> <!-- </view>--> <view class="common-module equipment-module"> <view class="module-header"> <view class="module-title-container"> <text class="module-title">设备管理</text> </view> </view> <view class="module-content"> <up-grid :border="false" col="4"> <up-grid-item v-for="(item, index) in equipmentItems" :key="index" @click="handleCommonItemClick(item)"> <view class="icon-container" :style="{ background: item.bgColor }"> <up-icon :name="item.icon" :size="58" color="#ffffff"></up-icon> </view> <text class="item-label">{{item.label}}</text> </up-grid-item> </up-grid> </view> </view> </view> </template> @@ -279,62 +163,6 @@ // 协同办公功能数据 const collaborationItems = reactive([ { icon: "/static/images/icon/gongchuguanli@2x.png", label: "公出管理", }, { icon: "/static/images/icon/qingjiaguanli@2x.png", label: "请假管理", }, { icon: "/static/images/icon/chuchaiguanli@2x.png", label: "出差管理", }, { icon: "/static/images/icon/chuchaiguanli@2x.png", label: "报销管理", }, { icon: "/static/images/icon/chuchaiguanli@2x.png", label: "采购管理", }, { icon: "/static/images/icon/chuchaiguanli@2x.png", label: "报价管理", }, { icon: "/static/images/icon/chuchaiguanli@2x.png", label: "出库管理", }, { icon: "/static/images/icon/qingjiaguanli@2x.png", label: "会议设置", }, { icon: "/static/images/icon/qingjiaguanli@2x.png", label: "会议列表", }, { icon: "/static/images/icon/qingjiaguanli@2x.png", label: "会议申请", }, { icon: "/static/images/icon/qingjiaguanli@2x.png", label: "会议审批", }, { icon: "/static/images/icon/qingjiaguanli@2x.png", label: "会议发布", }, { icon: "/static/images/icon/qingjiaguanli@2x.png", label: "会议总结", }, { icon: "/static/images/icon/xietongshenpi@2x.png", label: "协同审批", }, { icon: "/static/images/icon/kehubaifang@2x.png", label: "客户拜访", }, @@ -371,41 +199,9 @@ // 设备管理功能数据 const equipmentItems = reactive([ // { // icon: '/static/images/icon/shebeitaizhang@2x.png', // label: '设备台账', // }, { icon: "/static/images/icon/shbeibaoxiu@2x.png", label: "设备报修", }, { icon: "/static/images/icon/shbeibaoyang@2x.png", label: "设备保养", }, { icon: "/static/images/icon/xunjianshangchuan@2x.png", label: "巡检上传", }, { icon: "/static/images/icon/guzhangfenxi@2x.png", label: "分析追溯", bgColor: "#ff9800", }, { icon: "/static/images/icon/zhinengpaidan@2x.png", label: "智能派单", bgColor: "#ff6b35", }, { icon: "/static/images/icon/zuoyezhidao@2x.png", label: "作业指导", bgColor: "#4caf50", }, { icon: "/static/images/icon/jieguoyanzheng@2x.png", label: "结果验证", bgColor: "#9c27b0", }, ]); src/pages/inspectionUpload/components/formDia.vue
@@ -21,8 +21,8 @@ name="before" multiple :maxCount="10" :maxSize="1024 * 1024" accept="video/*" :maxSize="5 * 1024 * 1024" accept="image/*" :previewFullImage="true" ></u-upload> </view> @@ -36,8 +36,8 @@ name="after" multiple :maxCount="10" :maxSize="1024 * 1024" accept="video/*" :maxSize="5 * 1024 * 1024" accept="image/*" :previewFullImage="true" ></u-upload> </view> @@ -51,8 +51,8 @@ name="issue" multiple :maxCount="10" :maxSize="1024 * 1024" accept="video/*" :maxSize="5 * 1024 * 1024" accept="image/*" :previewFullImage="true" ></u-upload> </view> @@ -67,8 +67,10 @@ </template> <script setup> import { ref } from 'vue' import { ref, computed } from 'vue' import { submitInspectionRecord } from '@/api/equipmentManagement/inspection.js' import { getToken } from '@/utils/auth' import config from '@/config' const emit = defineEmits(['closeDia']) @@ -78,50 +80,106 @@ const issueModelValue = ref([]) const infoData = ref(null) // 计算上传URL const uploadFileUrl = computed(() => { let baseUrl = ''; if (process.env.VUE_APP_BASE_API) { baseUrl = process.env.VUE_APP_BASE_API; } else { baseUrl = config.baseUrl; } return baseUrl + '/file/upload'; }) const uploadSingleFile = async (fileItem, typeValue) => { const token = getToken() if (!token) throw new Error('用户未登录') // H5: u-upload 可能给原生 File(fileItem.file) const rawFile = fileItem?.file if (rawFile) { const formData = new FormData() formData.append('file', rawFile, rawFile.name || 'image.jpg') formData.append('type', String(typeValue)) const res = await fetch(uploadFileUrl.value, { method: 'POST', headers: { Authorization: 'Bearer ' + token }, body: formData }) const data = await res.json() if (data?.code !== 200) throw new Error(data?.msg || '上传失败') return { url: data?.data?.url, name: rawFile.name || 'image.jpg', status: 'success' } } // 非 H5 / 兼容:走 uni.uploadFile return await new Promise((resolve, reject) => { uni.uploadFile({ url: uploadFileUrl.value, filePath: fileItem.url, name: 'file', header: { 'Authorization': `Bearer ${token}` }, formData: { type: typeValue }, success: (res) => { try { const data = JSON.parse(res.data) if (data.code === 200) { resolve({ url: data.data.url, name: fileItem.name, status: 'success' }) } else { reject(new Error(data.msg || '上传失败')) } } catch (e) { reject(e) } }, fail: (err) => reject(err) }) }) } // 文件上传处理 const afterRead = (event) => { const { name, file } = event // 上传文件到服务器 uni.uploadFile({ url: '/api/upload', // 替换为实际的上传接口 filePath: file.url, name: 'file', success: (res) => { const data = JSON.parse(res.data) if (data.code === 200) { const fileItem = { url: data.data.url, name: file.name, status: 'success' } // 根据name添加到对应的数组 if (name === 'before') { beforeModelValue.value.push(fileItem) } else if (name === 'after') { afterModelValue.value.push(fileItem) } else if (name === 'issue') { issueModelValue.value.push(fileItem) } uni.showToast({ title: '上传成功', icon: 'success' }) } else { uni.showToast({ title: '上传失败', icon: 'error' }) // 根据上传类型设置不同的type值 let typeValue = 10 // 默认值 if (name === 'before') { typeValue = 10 // 生产前 } else if (name === 'after') { typeValue = 11 // 生产中 } else if (name === 'issue') { typeValue = 12 // 生产后 } const files = Array.isArray(file) ? file : [file] Promise.resolve().then(async () => { for (const f of files) { const uploaded = await uploadSingleFile(f, typeValue) if (name === 'before') { beforeModelValue.value.push(uploaded) } else if (name === 'after') { afterModelValue.value.push(uploaded) } else if (name === 'issue') { issueModelValue.value.push(uploaded) } }, fail: () => { uni.showToast({ title: '上传失败', icon: 'error' }) } uni.showToast({ title: '上传成功', icon: 'success' }) }).catch((err) => { console.error('上传失败:', err) uni.showToast({ title: '上传失败', icon: 'error' }) }) } src/pages/inspectionUpload/index.vue
@@ -68,32 +68,6 @@ </view> <!-- 扫码区域 - 全局弹窗 --> <view v-if="isScanning" class="qr-scan-overlay"> <view class="qr-scan-container"> <view class="scan-header"> <text class="scan-title">扫描二维码</text> <u-button type="error" size="small" @click.stop="stopScan" :customStyle="{ borderRadius: '15px', height: '30px', fontSize: '12px' }"> 关闭 </u-button> </view> <camera class="qr-camera" device-position="back" flash="off" @scancode="handleScanCode" @error="handleCameraError"></camera> <view class="scan-frame-wrapper"> <view class="scan-frame"></view> <view class="scan-tip">请将二维码放入框内</view> </view> <u-alert v-if="cameraError" :title="cameraError" type="error" :showIcon="true" :closable="true" @close="cameraError = ''" :customStyle="{ margin: '10px 0' }"></u-alert> </view> </view> <!-- 图片上传弹窗 - 原生实现 --> <view v-if="showUploadDialog" class="custom-modal-overlay" @click="closeUploadDialog"> <view class="custom-modal-container" @click.stop> @@ -162,10 +136,10 @@ <view v-if="getCurrentFiles().length > 0" class="file-list"> <view v-for="(file, index) in getCurrentFiles()" :key="index" class="file-item"> <view class="file-preview-container"> <image v-if="file?.path?.fileType === 'image'" :src="file?.url || file?.tempFilePath?.tempFilePath || file?.path?.tempFilePath" <image v-if="file.type === 'image' || (file.type !== 'video' && !file.type)" :src="file.url || file.tempFilePath || file.path || file.downloadUrl" class="file-preview" mode="aspectFill" /> <view v-else class="video-preview"> <view v-else-if="file.type === 'video'" class="video-preview"> <uni-icons type="videocam" name="videocam" size="18" color="#fff" style="margin-right: 5px;"></uni-icons> <text class="video-text">视频</text> @@ -294,6 +268,7 @@ import { getLedgerById } from '@/api/equipmentManagement/ledger.js' import { inspectionTaskList, uploadInspectionTask } from "@/api/inspectionManagement"; import { getToken } from "@/utils/auth"; import config from '@/config' // 组件引用已移除 @@ -347,7 +322,7 @@ // 上传配置 const uploadConfig = { action: "/common/minioUploads", action: "/file/upload", limit: 10, fileSize: 50, // MB fileType: ['jpg', 'jpeg', 'png', 'mp4', 'mov'], @@ -356,15 +331,7 @@ // 计算上传URL const uploadFileUrl = computed(() => { let baseUrl = ''; if (process.env.VUE_APP_BASE_API) { baseUrl = process.env.VUE_APP_BASE_API; } else if (process.env.NODE_ENV === 'development') { baseUrl = 'http://114.132.189.42:9068'; } else { baseUrl = 'http://114.132.189.42:9068'; } const baseUrl = 'http://114.132.189.42:9030'; return baseUrl + uploadConfig.action; }) @@ -377,10 +344,6 @@ // 请求取消标志,用于取消正在进行的请求 let isRequestCancelled = false // 扫码相关状态 const isScanning = ref(false) const cameraError = ref('') const pagesPames = reactive({ size: 10, @@ -438,11 +401,6 @@ onUnmounted(() => { // 设置取消标志,阻止后续的异步操作 isRequestCancelled = true // 停止扫码 if (isScanning.value) { isScanning.value = false } // 关闭上传弹窗 if (showUploadDialog.value) { @@ -528,114 +486,77 @@ } } // 为指定任务开始扫码 // 为指定任务开始扫码(真机) const startScanForTask = async (task) => { try { // 记录当前扫描的任务 currentScanningTask.value = task // 显示扫描界面 isScanning.value = true // 使用uniapp的扫码API uni.scanCode({ success: (res) => { handleScanSuccess(res) }, fail: (err) => { console.error('扫码失败:', err) uni.showToast({ title: '扫码失败', icon: 'error' }) // 关闭扫描界面 isScanning.value = false } }) } catch (e) { console.error('启动扫码失败:', e) uni.showToast({ title: '启动扫码失败', icon: 'error' }) isScanning.value = false } } // 停止扫码 const stopScan = () => { isScanning.value = false currentScanningTask.value = null } // 扫码成功处理 const handleScanSuccess = async (result) => { // 扫码成功处理:校验后打开上传弹窗 const handleScanSuccess = (result) => { try { // 解析二维码数据,提取deviceId let deviceId = '' // 检查是否是URL格式 if (result.result.includes('deviceId=')) { // 从URL中提取deviceId const url = result.result const match = url.match(/deviceId=(\d+)/) if (match && match[1]) { deviceId = match[1] } } else { // 尝试解析JSON格式 try { const qrData = JSON.parse(result.result) deviceId = qrData.deviceId || qrData.qrCodeId || '' } catch (e) { // 如果不是JSON格式,直接使用结果 deviceId = result.result if (result?.result && typeof result.result === 'string') { if (result.result.includes('deviceId=')) { const match = result.result.match(/deviceId=(\d+)/) if (match && match[1]) deviceId = match[1] } else { try { const qrData = JSON.parse(result.result) deviceId = qrData.deviceId || qrData.qrCodeId || '' } catch (e) { deviceId = result.result } } } if (!deviceId) { uni.showToast({ title: '未识别到设备ID', icon: 'error' }) isScanning.value = false uni.showToast({ title: '未识别到设备ID', icon: 'error' }) return } // 获取当前任务的taskId const currentTaskId = currentScanningTask.value?.taskId || currentScanningTask.value?.id // 对比deviceId和taskId if (deviceId === currentTaskId.toString()) { uni.showToast({ title: '识别成功', icon: 'success' }) // 先关闭扫描界面 isScanning.value = false // 延迟打开上传弹窗,确保扫描界面完全关闭 setTimeout(() => { openUploadDialog(currentScanningTask.value) }, 300) } else { uni.showToast({ title: '请扫描正确的设备', icon: 'error' }) // 关闭扫描界面 isScanning.value = false if (!currentTaskId) { uni.showToast({ title: '任务信息缺失', icon: 'error' }) return } if (deviceId === currentTaskId.toString()) { uni.showToast({ title: '识别成功', icon: 'success' }) openUploadDialog(currentScanningTask.value) } else { uni.showToast({ title: '请扫描正确的设备', icon: 'error' }) } } catch (error) { console.error('扫码结果处理失败:', error) uni.showToast({ title: error.message || '数据解析失败', title: error?.message || '数据解析失败', icon: 'error' }) // 关闭扫描界面 isScanning.value = false } } // 打开上传弹窗 const openUploadDialog = (task) => { @@ -787,10 +708,20 @@ arr.push(...issueModelValue.value); } // 传给后端的临时文件ID列表(tempFileIds) // 兼容:有些接口可能返回 tempId / tempFileId / id let tempFileIds = [] if (arr !== null && arr.length > 0) { tempFileIds = arr .map((item) => item?.tempId ?? item?.tempFileId ?? item?.id) .filter((v) => v !== undefined && v !== null && v !== '') } // 提交数据 infoData.value.storageBlobDTO = arr; // 添加异常状态信息 infoData.value.hasException = hasException.value; infoData.value.tempFileIds = tempFileIds; const result = await uploadInspectionTask({ ...infoData.value }); // 检查提交结果 @@ -840,16 +771,6 @@ } } // 摄像头错误处理 const handleCameraError = (error) => { cameraError.value = '摄像头访问失败,请检查权限设置' } // 扫码事件处理 const handleScanCode = (result) => { handleScanSuccess(result) } // 查看附件 const viewAttachments = async (task) => { try { @@ -859,32 +780,40 @@ // 解析新的数据结构 attachmentList.value = [] // 生产前附件 (type=0) if (task.beforeProduction && Array.isArray(task.beforeProduction)) { const beforeFiles = task.beforeProduction.map(file => ({ // 后端反显字段(你提供的数据结构): // - commonFileListBefore:生产前(通常 type=10) // - commonFileListAfter:生产中(通常 type=11) // - commonFileList:可能是全部/兜底(若包含生产后,一般 type=12) const allList = Array.isArray(task?.commonFileList) ? task.commonFileList : [] const beforeList = Array.isArray(task?.commonFileListBefore) ? task.commonFileListBefore : allList.filter(f => f?.type === 10) const afterList = Array.isArray(task?.commonFileListAfter) ? task.commonFileListAfter : allList.filter(f => f?.type === 11) // 如果后端后续补了 commonFileListIssue,则优先用;否则从 commonFileList 里按 type=12 兜底 const issueList = Array.isArray(task?.commonFileListIssue) ? task.commonFileListIssue : allList.filter(f => f?.type === 12) const mapToViewFile = (file, viewType) => { const u = normalizeFileUrl(file?.url || file?.downloadUrl || '') return { ...file, type: 0 // 确保type为0 })) attachmentList.value.push(...beforeFiles) // 用于三标签页分组:0=生产前 1=生产中 2=生产后 type: viewType, name: file?.name || file?.originalFilename || file?.bucketFilename, bucketFilename: file?.bucketFilename || file?.name, originalFilename: file?.originalFilename || file?.name, url: u, downloadUrl: u, size: file?.size || file?.byteSize, } } // 生产中附件 (type=1) if (task.afterProduction && Array.isArray(task.afterProduction)) { const afterFiles = task.afterProduction.map(file => ({ ...file, type: 1 // 确保type为1 })) attachmentList.value.push(...afterFiles) } // 生产后附件 (type=2) if (task.productionIssues && Array.isArray(task.productionIssues)) { const issueFiles = task.productionIssues.map(file => ({ ...file, type: 2 // 确保type为2 })) attachmentList.value.push(...issueFiles) } attachmentList.value.push(...beforeList.map(f => mapToViewFile(f, 0))) attachmentList.value.push(...afterList.map(f => mapToViewFile(f, 1))) attachmentList.value.push(...issueList.map(f => mapToViewFile(f, 2))) showAttachmentDialog.value = true @@ -917,13 +846,13 @@ const getTabType = () => { switch (currentUploadType.value) { case 'before': return 0 return 10 case 'after': return 1 return 11 case 'issue': return 2 return 12 default: return 0 return 10 } } // 获取当前查看类型的附件 @@ -954,6 +883,41 @@ const name = file.bucketFilename || file.originalFilename || file.name || '' const ext = name.split('.').pop()?.toLowerCase() return ['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(ext) } // 文件访问基础域(后端要求前缀) const filePreviewBase = 'http://114.132.189.42:9098' // 将后端返回的文件地址规范成可访问URL // 兼容场景: // - 已经是 http/https:直接返回 // - 以 / 开头:拼接 filePreviewBase // - Windows 本地路径(如 D:\ruoyi\prod\uploads...\xx.jpg):尝试截取 prod 之后的相对路径并拼接 filePreviewBase const normalizeFileUrl = (rawUrl) => { try { if (!rawUrl || typeof rawUrl !== 'string') return '' const url = rawUrl.trim() if (!url) return '' if (/^https?:\/\//i.test(url)) return url if (url.startsWith('/')) return `${filePreviewBase}${url}` // Windows path -> web path if (/^[a-zA-Z]:\\/.test(url)) { const normalized = url.replace(/\\/g, '/') const idx = normalized.indexOf('/prod/') if (idx >= 0) { const relative = normalized.slice(idx + '/prod/'.length) return `${filePreviewBase}/${relative}` } // 兜底:无法推断映射规则时,至少把反斜杠变成正斜杠 return normalized } // 其他相对路径:直接用 baseUrl 拼一下 return `${filePreviewBase}/${url.replace(/^\//, '')}` } catch (e) { return rawUrl || '' } } // 预览附件 @@ -994,52 +958,80 @@ }) } // 使用相机 // 拍照/拍视频(真机优先用 chooseMedia;不支持则降级) const chooseMedia = (type) => { let mediaPamaes = { count: 1, mediaType: [type || 'image'], sizeType: ['compressed', 'original'], sourceType: ['camera'], if (getCurrentFiles().length >= uploadConfig.limit) { uni.showToast({ title: `最多只能选择${uploadConfig.limit}个文件`, icon: 'none' }) return } uni.chooseMedia({ ...mediaPamaes, success: (res) => { try { if (!res.tempFiles || res.tempFiles.length === 0) { throw new Error('未获取到图片文件'); const remaining = uploadConfig.limit - getCurrentFiles().length // 优先:chooseMedia(支持 image/video) if (typeof uni.chooseMedia === 'function') { uni.chooseMedia({ count: Math.min(remaining, 1), mediaType: [type || 'image'], sizeType: ['compressed', 'original'], sourceType: ['camera'], success: (res) => { try { const files = res?.tempFiles || [] if (!files.length) throw new Error('未获取到文件') files.forEach((tf, idx) => { const filePath = tf.tempFilePath || tf.path || '' const fileType = tf.fileType || type || 'image' const ext = fileType === 'video' ? 'mp4' : 'jpg' const file = { tempFilePath: filePath, path: filePath, type: fileType, name: `${fileType}_${Date.now()}_${idx}.${ext}`, size: tf.size || 0, duration: tf.duration || 0, createTime: Date.now(), uid: Date.now() + Math.random() + idx } handleBeforeUpload(file) }) } catch (e) { console.error('处理拍摄结果失败:', e) uni.showToast({ title: '处理文件失败', icon: 'error' }) } }, fail: (err) => { console.error('拍摄失败:', err) uni.showToast({ title: '拍摄失败', icon: 'error' }) } }) return } const tempFilePath = res.tempFiles[0]; const tempFile = res.tempFiles && res.tempFiles[0] ? res.tempFiles[0] : {}; const file = { tempFilePath: tempFilePath, path: tempFilePath, // 保持兼容性 // 降级:chooseImage / chooseVideo if (type === 'video') { chooseVideo() } else { uni.chooseImage({ count: 1, sizeType: ['compressed', 'original'], sourceType: ['camera'], success: (res) => { const tempFilePath = res?.tempFilePaths?.[0] const tempFile = res?.tempFiles?.[0] || {} if (!tempFilePath) return handleBeforeUpload({ tempFilePath, path: tempFilePath, type: 'image', name: `photo_${Date.now()}.jpg`, size: tempFile.size || 0, createTime: new Date().getTime(), createTime: Date.now(), uid: Date.now() + Math.random() }; handleBeforeUpload(file); } catch (error) { console.error('处理拍照结果失败:', error); uni.showToast({ title: '处理图片失败', icon: 'error' }); }) } }, fail: (err) => { console.error('拍照失败:', err); uni.showToast({ title: '拍照失败: ' + (err.errMsg || '未知错误'), icon: 'error' }); } }) }) } } // 拍照 @@ -1193,36 +1185,6 @@ // 上传前校验 const handleBeforeUpload = async (file) => { // 检查网络连接 const hasNetwork = await checkNetworkConnection(); if (!hasNetwork) { uni.showToast({ title: '网络连接不可用,请检查网络设置', icon: 'none' }); return false; } // 校验文件大小 if (uploadConfig.fileSize && file.size) { const isLt = file.size / 1024 / 1024 < uploadConfig.fileSize; if (!isLt) { uni.showToast({ title: `文件大小不能超过 ${uploadConfig.fileSize} MB!`, icon: 'none' }); return false; } } // 校验视频时长 if (file.type === 'video' && file.duration && file.duration > uploadConfig.maxVideoDuration) { uni.showToast({ title: `视频时长不能超过 ${uploadConfig.maxVideoDuration} 秒!`, icon: 'none' }); return false; } // 校验文件类型 if (uploadConfig.fileType && Array.isArray(uploadConfig.fileType) && uploadConfig.fileType.length > 0) { @@ -1258,18 +1220,11 @@ return true; } // 文件上传处理 const uploadFile = (file) => { // 文件上传处理(真机走 uni.uploadFile) const uploadFile = async (file) => { uploading.value = true; uploadProgress.value = 0; number.value++; // 增加上传计数 // 确保文件路径正确 const filePath = file.tempFilePath?.tempFilePath || file.path?.tempFilePath || ''; if (!filePath) { handleUploadError('文件路径不存在'); return; } // 确保token存在 const token = getToken(); @@ -1278,20 +1233,28 @@ return; } // 准备上传参数 const uploadParams = { const typeValue = getTabType(); // 生产前:10, 生产中:11, 生产后:12 uploadWithUniUploadFile(file, file.tempFilePath || file.path || '', typeValue, token); } // 使用uni.uploadFile上传(非H5环境或H5回退方案) const uploadWithUniUploadFile = (file, filePath, typeValue, token) => { if (!filePath) { handleUploadError('文件路径不存在'); return; } const uploadTask = uni.uploadFile({ url: uploadFileUrl.value, filePath: filePath, name: 'files', name: 'file', formData: { type: getTabType() || 0 type: typeValue }, header: { 'Authorization': `Bearer ${token}` } }; const uploadTask = uni.uploadFile({ ...uploadParams, }, success: (res) => { try { if (res.statusCode === 200) { @@ -1348,7 +1311,10 @@ } // 上传失败处理 const handleUploadError = (message = '上传文件失败', showRetry = true) => { const handleUploadError = (message = '上传文件失败', showRetry = false) => { uploading.value = false; uploadProgress.value = 0; if (showRetry) { uni.showModal({ title: '上传失败', @@ -1369,63 +1335,69 @@ // 上传成功回调 const handleUploadSuccess = (res, file) => { if (res.code === 200 && res.data && Array.isArray(res.data) && res.data.length > 0) { const uploadedFile = res.data[0]; console.log('上传成功响应:', res); // 处理不同的数据结构:可能是数组,也可能是单个对象 let uploadedFile = null; uploadedFile = res.data; // 根据当前上传类型设置type字段 let typeValue = 0; // 默认为生产前 switch (currentUploadType.value) { case 'before': typeValue = 0; break; case 'after': typeValue = 1; break; case 'issue': typeValue = 2; break; } // 确保上传的文件数据完整,包含id和type const fileData = { ...file, id: uploadedFile.id, // 添加服务器返回的id url: uploadedFile.url || uploadedFile.downloadUrl, bucketFilename: uploadedFile.bucketFilename || file.name, downloadUrl: uploadedFile.downloadUrl || uploadedFile.url, size: uploadedFile.size || file.size, createTime: uploadedFile.createTime || new Date().getTime(), type: typeValue // 添加类型字段:0=生产前, 1=生产中, 2=生产后 }; uploadList.value.push(fileData); uploadedSuccessfully(); } else { if (!uploadedFile) { console.error('无法解析上传响应数据:', res); number.value--; // 上传失败时减少计数 handleUploadError(res.msg || '上传失败'); handleUploadError('上传响应数据格式错误', false); return; } // 根据当前上传类型设置type字段 let typeValue = 0; // 默认为生产前 switch (currentUploadType.value) { case 'before': typeValue = 0; break; case 'after': typeValue = 1; break; case 'issue': typeValue = 2; break; } // 确保上传的文件数据完整,包含id和type const fileData = { ...file, id: uploadedFile.id, // 添加服务器返回的id tempId: uploadedFile.tempId ?? uploadedFile.tempFileId ?? uploadedFile.id, url: uploadedFile.url || uploadedFile.downloadUrl || file.tempFilePath || file.path, bucketFilename: uploadedFile.bucketFilename || uploadedFile.originalFilename || file.name, downloadUrl: uploadedFile.downloadUrl || uploadedFile.url, size: uploadedFile.size || uploadedFile.byteSize || file.size, createTime: uploadedFile.createTime || new Date().getTime(), type: typeValue // 添加类型字段:0=生产前, 1=生产中, 2=生产后 }; uploadList.value.push(fileData); // 立即添加到对应的分类,不等待所有文件上传完成 switch (currentUploadType.value) { case 'before': beforeModelValue.value.push(fileData); break; case 'after': afterModelValue.value.push(fileData); break; case 'issue': issueModelValue.value.push(fileData); break; } // 重置上传列表(因为已经添加到对应分类了) uploadList.value = []; number.value = 0; } // 上传结束处理 // 上传结束处理(已废弃,现在在handleUploadSuccess中直接处理) const uploadedSuccessfully = () => { if (number.value > 0 && uploadList.value.length === number.value) { // 根据当前上传类型,将文件添加到对应的分类 switch (currentUploadType.value) { case 'before': beforeModelValue.value = [...beforeModelValue.value, ...uploadList.value]; break; case 'after': afterModelValue.value = [...afterModelValue.value, ...uploadList.value]; break; case 'issue': issueModelValue.value = [...issueModelValue.value, ...uploadList.value]; break; } // 重置状态 uploadList.value = []; number.value = 0; } // 此函数已不再使用,文件上传成功后立即添加到对应分类 } // 格式化文件大小 src/pages/login.vue
@@ -6,22 +6,10 @@ <view class="login-form-content"> <view class="input-item flex align-center"> <up-input prefixIcon="account" placeholder="请输入账号" border="bottom" @blur="getUserLoginFacotryList" maxlength="30" v-model="loginForm.userName" clearable></up-input> </view> <view class="input-item flex align-center"> <up-input prefixIcon="lock" placeholder="请输入密码" border="bottom" maxlength="20" v-model="loginForm.password" clearable type="password"></up-input> </view> <view class="input-item flex align-center select-container"> <up-icon name="tags" size="18"></up-icon> <up-picker-data v-model="loginForm.factoryId" title="请选择公司" :options="factoryList" valueKey="id" style="width: 100%;" labelKey="name"> </up-picker-data> </view> <view> <button @click="handleLogin" class="login-btn cu-btn block bg-blue lg round">登录</button> @@ -50,23 +38,18 @@ icon: 'none' }) } import { userLoginFacotryList} from '@/api/login' import { ref, onMounted } from "vue"; import useUserStore from '@/store/modules/user' import { getWxCode } from '@/utils/geek'; import { wxLogin } from '@/api/oauth'; import { setToken } from '@/utils/auth'; import View from "@/pages/procurementManagement/procurementLedger/view.vue"; const userStore = useUserStore() const useWxLogin = ref(false); // 是否使用微信登录 const rememberPassword = ref(false); // 记住密码 const loginForm = ref({ userName: "", password: "", factoryId: "", currentFatoryName: "", }); const factoryList = ref([]) // 公司列表 // 保存密码到本地存储 function savePassword() { @@ -109,30 +92,6 @@ }) } function getUserLoginFacotryList() { if(loginForm.value.userName){ userLoginFacotryList({userName:loginForm.value.userName}).then(res => { console.log('res',res) // 检查res.data是否为数组 if (res.data && Array.isArray(res.data)) { // 重新组装数据格式:deptId变成id,deptName变成name factoryList.value = res.data.map(item => ({ id: item.deptId, name: item.deptName })) } else { // 如果res.data不是数组,设置为空数组 factoryList.value = [] } }).catch(error => { showToast('获取公司列表失败:', error) factoryList.value = [] }) }else { factoryList.value = [] } } async function handleLogin() { if (loginForm.value.userName === "") { showToast("请输入您的账号") @@ -166,7 +125,6 @@ // 页面加载时检查是否有保存的密码 onMounted(() => { loadPassword(); getUserLoginFacotryList() }); </script> @@ -219,26 +177,6 @@ line-height: 20px; text-align: left; padding-left: 15px; } } .select-container { flex: 1; border-bottom: 1px solid #e5e5e5; padding: 6px 9px; :deep(.up-select) { border: none; background: transparent; .up-select__label { font-size: 14px; color: #333; } .up-select__value { font-size: 14px; color: #333; } } } src/utils/request.ts
@@ -22,12 +22,12 @@ config.url = url } // 记录请求参数 // console.log('请求发送参数:', { // url: (config.baseUrl || baseUrl) + config.url, // method: config.method || 'GET', // headers: config.header, // data: config.data // }) console.log('请求发送参数:', { url: (config.baseUrl || baseUrl) + config.url, method: config.method || 'GET', headers: config.header, data: config.data }) return new Promise((resolve, reject) => { uni.request({ method: config.method || 'GET',