Compare commits

4 Commits

Author SHA1 Message Date
Apcallover
49af03f4bb flat: 暂存 2025-12-19 17:43:23 +08:00
4ae11e31f4 地图优化 2025-12-19 16:45:21 +08:00
bca0d997c6 style 2025-12-19 16:33:15 +08:00
Apcallover
b43eb98a1c flat: ok 2025-12-19 16:13:09 +08:00
6 changed files with 204 additions and 33 deletions

View File

@@ -1,6 +1,6 @@
export default { export default {
// baseUrl: 'https://fw.rc.qingdao.gov.cn/rgpp-api/api', // 内网 baseUrl: 'http://36.105.163.21:30081/rgpp/api', // 内网
baseUrl: 'https://qd.zhaopinzao8dian.com/api', // 测试 // baseUrl: 'https://qd.zhaopinzao8dian.com/api', // 测试
// baseUrl: 'http://192.168.3.29:8081', // baseUrl: 'http://192.168.3.29:8081',
// baseUrl: 'http://10.213.6.207:19010/api', // baseUrl: 'http://10.213.6.207:19010/api',
// 语音转文字 // 语音转文字

View File

@@ -1,6 +1,6 @@
{ {
"name" : "qingdao-employment-service", "name" : "qingdao-employment-service",
"appid" : "__UNI__2496162", "appid" : "__UNI__C939371",
"description" : "招聘", "description" : "招聘",
"versionName" : "1.0.0", "versionName" : "1.0.0",
"versionCode" : "100", "versionCode" : "100",

View File

@@ -82,7 +82,7 @@ const { userInfo } = storeToRefs(useUserStore());
const { getUserResume } = useUserStore(); const { getUserResume } = useUserStore();
const { dictLabel, oneDictData } = useDictStore(); const { dictLabel, oneDictData } = useDictStore();
const openSelectPopup = inject('openSelectPopup'); const openSelectPopup = inject('openSelectPopup');
import { FileValidator } from '@/static/js/fileValidator.js'; //文件校验 import { FileValidator } from '@/utils/fileValidator.js'; //文件校验
const percent = ref('0%'); const percent = ref('0%');
const state = reactive({ const state = reactive({

View File

@@ -282,7 +282,7 @@ import successIcon from '@/static/icon/success.png';
import useUserStore from '@/stores/useUserStore'; import useUserStore from '@/stores/useUserStore';
const { isMachineEnv } = storeToRefs(useUserStore()); const { isMachineEnv } = storeToRefs(useUserStore());
import {FileValidator} from "@/static/js/fileValidator.js" //文件校验 import { FileValidator } from '@/utils/fileValidator.js'; //文件校验
// hook // hook
// 语音识别 // 语音识别
const { const {

View File

@@ -2,13 +2,14 @@
<scroll-view :scroll-y="true" class="nearby-scroll" @scrolltolower="scrollBottom"> <scroll-view :scroll-y="true" class="nearby-scroll" @scrolltolower="scrollBottom">
<view class="nearby-map" @touchmove.stop.prevent> <view class="nearby-map" @touchmove.stop.prevent>
<map <map
style="width: 100%; height: 410rpx" style="width: 100%; height: 690rpx"
:latitude="latitudeVal" :latitude="latitudeVal"
:longitude="longitudeVal" :longitude="longitudeVal"
:markers="mapCovers" :markers="mapCovers"
:circles="mapCircles" :circles="mapCircles"
:controls="mapControls" :controls="mapControls"
@controltap="handleControl" @controltap="handleControl"
:scale="mapScale"
></map> ></map>
<view class="nearby-select"> <view class="nearby-select">
<view class="select-view" @click="changeRangeShow"> <view class="select-view" @click="changeRangeShow">
@@ -106,16 +107,18 @@ const tMap = ref();
const progress = ref(); const progress = ref();
const mapCovers = ref([]); const mapCovers = ref([]);
const mapCircles = ref([]); const mapCircles = ref([]);
const mapScale = ref(14.5)
const mapControls = ref([ const mapControls = ref([
{ {
id: 1, id: 1,
position: { position: {
// 控件位置 // 控件位置
left: customSystem.systemInfo.screenWidth - 48 - 14, left: customSystem.systemInfo.screenWidth - uni.upx2px(75 + 30),
top: 320, top: uni.upx2px(655 - 75 - 30),
width: 48, width: uni.upx2px(75),
height: 48, height: uni.upx2px(75),
}, },
width:100,
iconPath: LocationPng, // 控件图标 iconPath: LocationPng, // 控件图标
}, },
]); ]);
@@ -148,6 +151,8 @@ function changeRangeShow() {
function changeRadius(item) { function changeRadius(item) {
console.log(item); console.log(item);
if(item > 1) mapScale.value = 14.5 - item * 0.3
else mapScale.value = 14.5
pageState.search.radius = item; pageState.search.radius = item;
rangeShow.value = false; rangeShow.value = false;
progressChange(item); progressChange(item);
@@ -221,27 +226,23 @@ onMounted(() => {
}); });
function getInit() { function getInit() {
useLocationStore()
.getLocation()
.then((res) => {
mapCovers.value = [ mapCovers.value = [
{ {
latitude: res.latitude, latitude: latitudeVal.value,
longitude: res.longitude, longitude: longitudeVal.value,
iconPath: point2, iconPath: point2,
}, },
]; ];
mapCircles.value = [ mapCircles.value = [
{ {
latitude: res.latitude, latitude: latitudeVal.value,
longitude: res.longitude, longitude:longitudeVal.value,
radius: 1000, radius: 1000,
fillColor: '#1c52fa25', fillColor: '#1c52fa25',
color: '#256BFA', color: '#256BFA',
}, },
]; ];
getJobList('refresh'); getJobList('refresh');
});
} }
function progressChange(value) { function progressChange(value) {
@@ -363,7 +364,7 @@ defineExpose({ loadData, handleFilterConfirm });
height: 100%; height: 100%;
background: #f4f4f4; background: #f4f4f4;
.nearby-map .nearby-map
height: 400rpx; height: 655rpx;
background: #e8e8e8; background: #e8e8e8;
overflow: hidden overflow: hidden
.nearby-list .nearby-list

170
utils/fileValidator.js Normal file
View File

@@ -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';
// });