diff --git a/App.vue b/App.vue index 620f282..49182bf 100644 --- a/App.vue +++ b/App.vue @@ -24,6 +24,7 @@ onLaunch((options) => { getUserInfo(); useUserStore().changMiniProgramAppStatus(false); useUserStore().changMachineEnv(false); + useLocationStore().getLocationLoop()//循环获取定位 return; } if (isY9MachineType()) { @@ -32,7 +33,18 @@ onLaunch((options) => { useUserStore().logOutApp(); useUserStore().changMiniProgramAppStatus(true); useUserStore().changMachineEnv(true); - useLocationStore().getLocation(); + (function loop() { + console.log('📍一体机尝试获取定位') + useLocationStore().getLocation().then(({longitude,latitude})=>{ + console.log(`✅一体机获取定位成功:lng:${longitude},lat${latitude}`) + }) + .catch(err=>{ + console.log('❌一体机获取定位失败,30s后尝试重新获取') + setTimeout(() => { + loop() + }, 3000); + }) + })() uQRListen = new IncreaseRevie(); inactivityManager = new GlobalInactivityManager(handleInactivity, 60 * 1000); inactivityManager.start(); @@ -40,6 +52,7 @@ onLaunch((options) => { } // 正式上线去除此方法 console.warn('浏览器环境'); + useLocationStore().getLocationLoop()//循环获取定位 useUserStore().changMiniProgramAppStatus(true); useUserStore().changMachineEnv(false); useUserStore().initSeesionId(); //更新 @@ -57,6 +70,7 @@ onLaunch((options) => { onMounted(() => {}); + onShow(() => { console.log('App Show'); }); diff --git a/packageA/pages/personalInfo/personalInfo.vue b/packageA/pages/personalInfo/personalInfo.vue index 952b2d6..9eb1737 100644 --- a/packageA/pages/personalInfo/personalInfo.vue +++ b/packageA/pages/personalInfo/personalInfo.vue @@ -82,6 +82,7 @@ const { userInfo } = storeToRefs(useUserStore()); const { getUserResume } = useUserStore(); const { dictLabel, oneDictData } = useDictStore(); const openSelectPopup = inject('openSelectPopup'); +import { FileValidator } from '@/static/js/fileValidator.js'; //文件校验 const percent = ref('0%'); const state = reactive({ @@ -278,15 +279,26 @@ function selectAvatar() { sizeType: ['original', 'compressed'], sourceType: ['album', 'camera'], count: 1, - success: ({ tempFilePaths, tempFiles }) => { - $api.uploadFile(tempFilePaths[0], true) - .then((res) => { - res = JSON.parse(res); - if (res.msg) fromValue.avatar = res.msg; - }) - .catch((err) => { - $api.msg('上传失败'); - }); + success: async (res) => { + const tempFilePaths = res.tempFilePaths; + const file = res.tempFiles[0]; + + const imageValidator = new FileValidator(); + + try { + await imageValidator.validate(file); + + $api.uploadFile(tempFilePaths[0], true) + .then((res) => { + res = JSON.parse(res); + if (res.msg) fromValue.avatar = res.msg; + }) + .catch((err) => { + $api.msg('上传失败'); + }); + } catch (error) { + $api.msg(error); + } }, fail: (error) => {}, }); diff --git a/pages/chat/components/ai-paging.vue b/pages/chat/components/ai-paging.vue index e4868e2..a72a13f 100644 --- a/pages/chat/components/ai-paging.vue +++ b/pages/chat/components/ai-paging.vue @@ -281,6 +281,8 @@ const { messages, isTyping, textInput, chatSessionID } = storeToRefs(useChatGrou import successIcon from '@/static/icon/success.png'; import useUserStore from '@/stores/useUserStore'; const { isMachineEnv } = storeToRefs(useUserStore()); + +import {FileValidator} from "@/static/js/fileValidator.js" //文件校验 // hook // 语音识别 const { @@ -536,22 +538,29 @@ function uploadCamera(type = 'camera') { count: 1, //默认9 sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有 sourceType: [type], //从相册选择 - success: function (res) { + success: async (res)=> { const tempFilePaths = res.tempFilePaths; const file = res.tempFiles[0]; - // 继续上传 - $api.uploadFile(tempFilePaths[0], true).then((resData) => { - resData = JSON.parse(resData); - console.log(file.type,'++') - if (isImage(file.type)) { - filesList.value.push({ - url: resData.msg, - type: file.type, - name: file.name, - }); - textInput.value = state.uploadFileTips; - } - }); + + const imageValidator = new FileValidator() + try { + await imageValidator.validate(file) + + $api.uploadFile(tempFilePaths[0], true).then((resData) => { + resData = JSON.parse(resData); + console.log(file.type,'++') + if (isImage(file.type)) { + filesList.value.push({ + url: resData.msg, + type: file.type, + name: file.name, + }); + textInput.value = state.uploadFileTips; + } + }); + } catch (error) { + $api.msg(error) + } }, }); } @@ -560,25 +569,30 @@ function getUploadFile(type = 'camera') { if (VerifyNumberFiles()) return; uni.chooseFile({ count: 1, - success: (res) => { + success: async(res) => { const tempFilePaths = res.tempFilePaths; const file = res.tempFiles[0]; const allowedTypes = config.allowedFileTypes || []; const size = $api.formatFileSize(file.size); - if (!allowedTypes.includes(file.type)) { - return $api.msg('仅支持 txt md word pdf ppt csv excel 格式类型'); - } - // 继续上传 - $api.uploadFile(tempFilePaths[0], true).then((resData) => { - resData = JSON.parse(resData); - filesList.value.push({ - url: resData.msg, - type: file.type, - name: file.name, - size: size, + + const imageValidator = new FileValidator({allowedExtensions:config.allowedFileTypes}) + + try{ + await imageValidator.validate(file) + + $api.uploadFile(tempFilePaths[0], true).then((resData) => { + resData = JSON.parse(resData); + filesList.value.push({ + url: resData.msg, + type: file.type, + name: file.name, + size: size, + }); + textInput.value = state.uploadFileTips; }); - textInput.value = state.uploadFileTips; - }); + }catch(error){ + $api.msg(error) + } }, }); } diff --git a/pages/index/components/index-refactor.vue b/pages/index/components/index-refactor.vue index 185c693..9bd0d5e 100644 --- a/pages/index/components/index-refactor.vue +++ b/pages/index/components/index-refactor.vue @@ -470,7 +470,6 @@ const { columnCount, columnSpace } = useColumnCount(() => { getJobRecommend('refresh'); nextTick(() => { waterfallsFlowRef.value?.refresh?.(); - useLocationStore().getLocation(); }); }); diff --git a/pages/index/components/index-two.vue b/pages/index/components/index-two.vue index 4ea08ea..14c80c0 100644 --- a/pages/index/components/index-two.vue +++ b/pages/index/components/index-two.vue @@ -129,7 +129,6 @@ const { columnCount, columnSpace } = useColumnCount(() => { pageSize.value = 10 * (columnCount.value - 1); nextTick(() => { waterfallsFlowRef.value?.refresh?.(); - useLocationStore().getLocation(); }); }); diff --git a/pages/search/search.vue b/pages/search/search.vue index 0b27ae2..75fdce9 100644 --- a/pages/search/search.vue +++ b/pages/search/search.vue @@ -151,7 +151,6 @@ const { columnCount, columnSpace } = useColumnCount(() => { pageSize.value = 10 * (columnCount.value - 1); nextTick(() => { waterfallsFlowRef.value?.refresh?.(); - useLocationStore().getLocation(); }); }); diff --git a/static/js/fileValidator.js b/static/js/fileValidator.js new file mode 100644 index 0000000..bace6b7 --- /dev/null +++ b/static/js/fileValidator.js @@ -0,0 +1,170 @@ +const KNOWN_SIGNATURES = { + png: '89504E470D0A1A0A', + jpg: 'FFD8FF', + jpeg: 'FFD8FF', + gif: '47494638', + webp: '52494646', + docx: '504B0304', + xlsx: '504B0304', + pptx: '504B0304', + doc: 'D0CF11E0', + xls: 'D0CF11E0', + ppt: 'D0CF11E0', + pdf: '25504446', + txt: 'TYPE_TEXT', + csv: 'TYPE_TEXT', + md: 'TYPE_TEXT', + json: 'TYPE_TEXT', +}; +export class FileValidator { + version = '1.0.0'; + signs = Object.keys(KNOWN_SIGNATURES); + constructor(options = {}) { + this.maxSizeMB = options.maxSizeMB || 10; + if (options.allowedExtensions && Array.isArray(options.allowedExtensions)) { + this.allowedConfig = {}; + options.allowedExtensions.forEach((ext) => { + const key = ext.toLowerCase(); + if (KNOWN_SIGNATURES[key]) { + this.allowedConfig[key] = KNOWN_SIGNATURES[key]; + } else { + console.warn(`[FileValidator] 未知的文件类型: .${key},已忽略`); + } + }); + } else { + this.allowedConfig = { + ...KNOWN_SIGNATURES, + }; + } + } + _isValidUTF8(buffer) { + try { + const decoder = new TextDecoder('utf-8', { + fatal: true, + }); + decoder.decode(buffer); + return true; + } catch (e) { + return false; + } + } + _bufferToHex(buffer) { + return Array.prototype.map + .call(new Uint8Array(buffer), (x) => ('00' + x.toString(16)).slice(-2)) + .join('') + .toUpperCase(); + } + _countCSVRows(buffer) { + const decoder = new TextDecoder('utf-8'); + const text = decoder.decode(buffer); + let rowCount = 0; + let inQuote = false; + let len = text.length; + for (let i = 0; i < len; i++) { + const char = text[i]; + if (char === '"') { + inQuote = !inQuote; + } else if (char === '\n' && !inQuote) { + rowCount++; + } + } + if (len > 0 && text[len - 1] !== '\n') { + rowCount++; + } + return rowCount; + } + _validateTextContent(buffer, extension) { + let contentStr = ''; + try { + const decoder = new TextDecoder('utf-8', { + fatal: true, + }); + contentStr = decoder.decode(buffer); + } catch (e) { + console.warn('UTF-8 解码失败', e); + return false; + } + if (contentStr.includes('\0')) { + return false; + } + if (extension === 'json') { + try { + JSON.parse(contentStr); + } catch (e) { + console.warn('无效的 JSON 格式'); + return false; + } + } + return true; + } + validate(file) { + return new Promise((resolve, reject) => { + if (!file || !file.name) return reject('无效的文件对象'); + if (file.size > this.maxSizeMB * 1024 * 1024) { + return reject(`文件大小超出限制 (最大 ${this.maxSizeMB}MB)`); + } + const fileName = file.name.toLowerCase(); + const extension = fileName.substring(fileName.lastIndexOf('.') + 1); + const expectedMagic = this.allowedConfig[extension]; + if (!expectedMagic) { + return reject(`不支持的文件格式: .${extension}`); + } + const reader = new FileReader(); + reader.onload = (e) => { + const buffer = e.target.result; + let isSafe = false; + if (expectedMagic === 'TYPE_TEXT') { + if (this._validateTextContent(buffer, extension)) { + isSafe = true; + } else { + if (extension === 'json') { + return reject(`文件异常:不是有效的 JSON 文件`); + } + return reject(`文件异常:.${extension} 包含非法二进制内容或编码错误`); + } + if (extension === 'csv' && this.csvMaxRows > 0) { + const rows = this._countCSVRows(buffer); + if (rows > this.csvMaxRows) { + return reject(`CSV 行数超出限制 (当前 ${rows} 行,最大允许 ${this.csvMaxRows} 行)`); + } + } + } else { + const fileHeader = this._bufferToHex(buffer.slice(0, 8)); + if (fileHeader.startsWith(expectedMagic)) { + isSafe = true; + } else { + return reject(`文件可能已被篡改 (真实类型与 .${extension} 不符)`); + } + } + if (isSafe) resolve(true); + }; + reader.onerror = () => reject('文件读取失败,无法校验'); + if (expectedMagic === 'TYPE_TEXT' && extension === 'json') { + reader.readAsArrayBuffer(file); + } else { + reader.readAsArrayBuffer(file.slice(0, 2048)); + } + }); + } +} + + +// 【demo】 +// 如果传入了 allowedExtensions,则只使用传入的;否则使用全部 KNOWN_SIGNATURES +// const imageValidator = new FileValidator({ +// maxSizeMB: 5, +// allowedExtensions: ['png', 'jpg', 'jpeg'], +// }); + +// imageValidator +// .validate(file) +// .then(() => { +// statusDiv.textContent = `检测通过: ${file.name}`; +// statusDiv.style.color = 'green'; +// console.log('图片校验通过,开始上传...'); +// // upload(file)... +// }) +// .catch((err) => { +// statusDiv.textContent = `检测失败: ${err}`; +// statusDiv.style.color = 'red'; +// }); \ No newline at end of file diff --git a/static/upload.html b/static/upload.html index 3c4e6da..97826c0 100644 --- a/static/upload.html +++ b/static/upload.html @@ -1,1252 +1,1389 @@ - - -
- - -选择文件上传到终端
-选择文件上传到终端
+