|
@@ -75,6 +75,10 @@
|
|
|
<van-field v-model="tacObjPblmstb.score" readonly label="扣分值" />
|
|
<van-field v-model="tacObjPblmstb.score" readonly label="扣分值" />
|
|
|
<van-field v-model="tacObjPblmstb.persName" readonly label="上报人" />
|
|
<van-field v-model="tacObjPblmstb.persName" readonly label="上报人" />
|
|
|
<van-field v-model="tacObjPblmstb.collTime" readonly label="上报时间" />
|
|
<van-field v-model="tacObjPblmstb.collTime" readonly label="上报时间" />
|
|
|
|
|
+ <div style="display: flex;align-items: center;margin-top: -1%;">
|
|
|
|
|
+ <div style="width: 20%;text-align: center;font-weight: 700;font-size: 14px;">上传照片</div>
|
|
|
|
|
+ <van-uploader v-model="fileList" :after-read="afterRead" style=""/>
|
|
|
|
|
+ </div>
|
|
|
</van-cell-group>
|
|
</van-cell-group>
|
|
|
<div style="margin: 16px;">
|
|
<div style="margin: 16px;">
|
|
|
<van-button @click="save" block native-type="submit" round type="primary">
|
|
<van-button @click="save" block native-type="submit" round type="primary">
|
|
@@ -95,15 +99,18 @@ import {computed, onMounted, ref, watch} from "vue";
|
|
|
import {useRoute} from "vue-router";
|
|
import {useRoute} from "vue-router";
|
|
|
import tacObjPblmstbList from './TacObjPblmstbList.vue';
|
|
import tacObjPblmstbList from './TacObjPblmstbList.vue';
|
|
|
import {getTacQuestionById} from "@/api/inspect";
|
|
import {getTacQuestionById} from "@/api/inspect";
|
|
|
|
|
+import { showConfirmDialog } from 'vant';
|
|
|
import {addTacQuestion, getIllegalActById, getTacUnitList} from "@/api/questions";
|
|
import {addTacQuestion, getIllegalActById, getTacUnitList} from "@/api/questions";
|
|
|
import {useUserStore} from "@/stores/user";
|
|
import {useUserStore} from "@/stores/user";
|
|
|
import request from "@/utils/request";
|
|
import request from "@/utils/request";
|
|
|
const route = useRoute();
|
|
const route = useRoute();
|
|
|
const userStore = useUserStore();
|
|
const userStore = useUserStore();
|
|
|
|
|
+const fileList = ref([]);
|
|
|
const listType = ref(route.query.inspectType || '1');
|
|
const listType = ref(route.query.inspectType || '1');
|
|
|
const pblm = ref({});
|
|
const pblm = ref({});
|
|
|
const tacObjPblmstb = ref({
|
|
const tacObjPblmstb = ref({
|
|
|
});
|
|
});
|
|
|
|
|
+const url = process.env.VUE_APP_BASE_HOST + process.env.VUE_APP_BASE_API
|
|
|
const columns = ref([]);
|
|
const columns = ref([]);
|
|
|
const columns1 = ref([]);
|
|
const columns1 = ref([]);
|
|
|
const columns2 = ref([]);
|
|
const columns2 = ref([]);
|
|
@@ -123,6 +130,7 @@ const router = useRoute();
|
|
|
const parSectino = ref([]);
|
|
const parSectino = ref([]);
|
|
|
const subjectList = ref([]);
|
|
const subjectList = ref([]);
|
|
|
const tacObjPblmstbShow = ref(false);
|
|
const tacObjPblmstbShow = ref(false);
|
|
|
|
|
+const accessToken = localStorage.getItem('accessToken') || '';
|
|
|
function onConfirm3(selectedValues, selectedOptions) {
|
|
function onConfirm3(selectedValues, selectedOptions) {
|
|
|
tacObjPblmstb.value.ifCasePblm = selectedValues.selectedOptions[0].text
|
|
tacObjPblmstb.value.ifCasePblm = selectedValues.selectedOptions[0].text
|
|
|
showPicker3.value = false;
|
|
showPicker3.value = false;
|
|
@@ -221,9 +229,54 @@ function getDe(){
|
|
|
}
|
|
}
|
|
|
columns2.value = [...new Set(columns2.value.map(item => JSON.stringify(item)))].map(str => JSON.parse(str))
|
|
columns2.value = [...new Set(columns2.value.map(item => JSON.stringify(item)))].map(str => JSON.parse(str))
|
|
|
})
|
|
})
|
|
|
|
|
+ res.data.gwComFiles.forEach(item => {
|
|
|
|
|
+ item.filePath = item.filePath.replace(/\\/g, '/');
|
|
|
|
|
+ fileList.value.push({
|
|
|
|
|
+ url: url + item.filePath,
|
|
|
|
|
+ name: item.fileName,
|
|
|
|
|
+ isImage: true,
|
|
|
|
|
+ id: item.id
|
|
|
|
|
+ })
|
|
|
|
|
+ console.log(fileList.value);
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
+// 获取需要认证的图片并转换为 base64
|
|
|
|
|
+async function getAuthImageAsBase64(imageUrl) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 1. 使用 fetch 请求图片,携带 token
|
|
|
|
|
+ const response = await fetch(imageUrl, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${localStorage.getItem('token')}`,
|
|
|
|
|
+ 'X-Token': localStorage.getItem('token') // 根据实际情况调整
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (!response.ok) {
|
|
|
|
|
+ throw new Error(`HTTP错误: ${response.status}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 获取图片的 ArrayBuffer
|
|
|
|
|
+ const arrayBuffer = await response.arrayBuffer();
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 转换为 base64
|
|
|
|
|
+ const base64 = btoa(
|
|
|
|
|
+ new Uint8Array(arrayBuffer).reduce(
|
|
|
|
|
+ (data, byte) => data + String.fromCharCode(byte),
|
|
|
|
|
+ ''
|
|
|
|
|
+ )
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // 4. 根据图片类型生成 data URL
|
|
|
|
|
+ const contentType = response.headers.get('content-type') || 'image/png';
|
|
|
|
|
+ return `data:${contentType};base64,${base64}`;
|
|
|
|
|
+
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('获取认证图片失败:', error);
|
|
|
|
|
+ throw error;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
async function save(){
|
|
async function save(){
|
|
|
console.log(tacObjPblmstb.value);
|
|
console.log(tacObjPblmstb.value);
|
|
|
tacObjPblmstb.value.ifCasePblm = tacObjPblmstb.value.ifCasePblm === '是' ? '1' : '0';
|
|
tacObjPblmstb.value.ifCasePblm = tacObjPblmstb.value.ifCasePblm === '是' ? '1' : '0';
|
|
@@ -233,6 +286,10 @@ async function save(){
|
|
|
delete tacObjPblmstb.value.collTime;
|
|
delete tacObjPblmstb.value.collTime;
|
|
|
await request.post('/dc/insp/pblm/update', tacObjPblmstb.value).then((res) => {
|
|
await request.post('/dc/insp/pblm/update', tacObjPblmstb.value).then((res) => {
|
|
|
if (res.success) {
|
|
if (res.success) {
|
|
|
|
|
+
|
|
|
|
|
+ if(newFile.value && newFile.value.id){
|
|
|
|
|
+ uploadFileToServer(newFile.value, parData.value.pblmId)
|
|
|
|
|
+ }
|
|
|
showSuccessToast('保存成功!');
|
|
showSuccessToast('保存成功!');
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
@@ -269,6 +326,183 @@ watch(() => tacObjPblmstb.value.checkPoint, (newVal, oldVal) => {
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
+function getObjectArrayDiff(arr1, arr2, key) {
|
|
|
|
|
+ if (!key) throw new Error('必须指定对比的唯一字段(如id)');
|
|
|
|
|
+
|
|
|
|
|
+ // 提取两个数组的key值集合
|
|
|
|
|
+ const keySet1 = new Set(arr1.map(item => item[key]));
|
|
|
|
|
+ const keySet2 = new Set(arr2.map(item => item[key]));
|
|
|
|
|
+
|
|
|
|
|
+ // 仅在arr1中存在的对象
|
|
|
|
|
+ const onlyInArr1 = arr1.filter(item => !keySet2.has(item[key]));
|
|
|
|
|
+ // 仅在arr2中存在的对象
|
|
|
|
|
+ const onlyInArr2 = arr2.filter(item => !keySet1.has(item[key]));
|
|
|
|
|
+ // 所有差异对象(合并)
|
|
|
|
|
+ const allDiff = [...onlyInArr1, ...onlyInArr2];
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ onlyInArr1,
|
|
|
|
|
+ onlyInArr2,
|
|
|
|
|
+ allDiff
|
|
|
|
|
+ };
|
|
|
|
|
+}
|
|
|
|
|
+const dataURLtoBlob = (dataURL) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const arr = dataURL.split(',');
|
|
|
|
|
+ const mime = arr[0].match(/:(.*?);/)[1];
|
|
|
|
|
+ const bstr = atob(arr[1]);
|
|
|
|
|
+ let n = bstr.length;
|
|
|
|
|
+ const u8arr = new Uint8Array(n);
|
|
|
|
|
+ while (n--) {
|
|
|
|
|
+ u8arr[n] = bstr.charCodeAt(n);
|
|
|
|
|
+ }
|
|
|
|
|
+ return new Blob([u8arr], { type: mime });
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('base64转Blob失败:', e);
|
|
|
|
|
+ return new Blob([], { type: 'application/octet-stream' });
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 2. 手动构建multipart/form-data(保留之前验证正确的逻辑)
|
|
|
|
|
+const buildMultipartFormData = async (file) => {
|
|
|
|
|
+ if (!file) throw new Error('文件为空');
|
|
|
|
|
+
|
|
|
|
|
+ // 生成标准boundary
|
|
|
|
|
+ const boundary = `----WebKitFormBoundary${Math.random().toString(36).substr(2, 16)}`;
|
|
|
|
|
+
|
|
|
|
|
+ // 处理文件为Blob
|
|
|
|
|
+ let fileBlob;
|
|
|
|
|
+ if (file.content instanceof Blob || file.content instanceof File) {
|
|
|
|
|
+ fileBlob = file.content;
|
|
|
|
|
+ } else if (typeof file.content === 'string' && file.content.startsWith('data:')) {
|
|
|
|
|
+ fileBlob = dataURLtoBlob(file.content);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ throw new Error(`不支持的文件格式: ${typeof file.content}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 读取文件为ArrayBuffer
|
|
|
|
|
+ const fileBuffer = await new Promise((resolve, reject) => {
|
|
|
|
|
+ const reader = new FileReader();
|
|
|
|
|
+ reader.onload = () => resolve(reader.result);
|
|
|
|
|
+ reader.onerror = reject;
|
|
|
|
|
+ reader.readAsArrayBuffer(fileBlob);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 手动拼接multipart格式(字符串转Uint8Array,兼容App端)
|
|
|
|
|
+ const stringToUint8Array = (str) => {
|
|
|
|
|
+ const arr = [];
|
|
|
|
|
+ for (let i = 0; i < str.length; i++) {
|
|
|
|
|
+ arr.push(str.charCodeAt(i));
|
|
|
|
|
+ }
|
|
|
|
|
+ return new Uint8Array(arr);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const parts = [];
|
|
|
|
|
+ // 拼接分隔符+元信息
|
|
|
|
|
+ parts.push(stringToUint8Array(`--${boundary}\r\n`));
|
|
|
|
|
+ parts.push(stringToUint8Array(`Content-Disposition: form-data; name="file"; filename="${encodeURIComponent(file.name || 'upload.png')}"\r\n`));
|
|
|
|
|
+ parts.push(stringToUint8Array(`Content-Type: ${fileBlob.type || 'application/octet-stream'}\r\n\r\n`));
|
|
|
|
|
+ // 拼接文件二进制数据
|
|
|
|
|
+ parts.push(new Uint8Array(fileBuffer));
|
|
|
|
|
+ // 拼接结束分隔符
|
|
|
|
|
+ parts.push(stringToUint8Array(`\r\n--${boundary}--\r\n`));
|
|
|
|
|
+
|
|
|
|
|
+ // 合并所有片段
|
|
|
|
|
+ const totalLength = parts.reduce((sum, part) => sum + part.length, 0);
|
|
|
|
|
+ const resultBuffer = new Uint8Array(totalLength);
|
|
|
|
|
+ let offset = 0;
|
|
|
|
|
+ parts.forEach(part => {
|
|
|
|
|
+ resultBuffer.set(part, offset);
|
|
|
|
|
+ offset += part.length;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ body: resultBuffer,
|
|
|
|
|
+ contentType: `multipart/form-data; boundary=${boundary}`
|
|
|
|
|
+ };
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 3. 核心:改用Fetch API上传文件(替换Axios)
|
|
|
|
|
+const uploadFileToServer = async (file, bizId) => {
|
|
|
|
|
+ if (!file || !bizId) {
|
|
|
|
|
+ showFailToast('文件或业务ID为空');
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 构建标准multipart请求体
|
|
|
|
|
+ const { body, contentType } = await buildMultipartFormData(file);
|
|
|
|
|
+
|
|
|
|
|
+ // 获取token和自定义头(和Axios拦截器一致)
|
|
|
|
|
+ const appStore = useAppStore();
|
|
|
|
|
+ const userStore = useUserStore();
|
|
|
|
|
+ const token = getToken();
|
|
|
|
|
+
|
|
|
|
|
+ // 拼接完整接口地址(补充baseURL,和你的request封装一致)
|
|
|
|
|
+ const baseURL = process.env.VUE_APP_BASE_API;
|
|
|
|
|
+ const fullUrl = `${baseURL}/file/insert?bizId=${bizId}`;
|
|
|
|
|
+
|
|
|
|
|
+ // 使用Fetch API发送请求(原生支持二进制,无兼容性问题)
|
|
|
|
|
+ const formData = new FormData();
|
|
|
|
|
+ formData.append('file', fileList.value[0].content); // 文件参数
|
|
|
|
|
+ const response = await fetch(fullUrl, {
|
|
|
|
|
+ method: 'POST',
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Content-Type': contentType, // 带boundary的标准格式
|
|
|
|
|
+ 'Accept': 'application/json',
|
|
|
|
|
+ 'Persid': userStore.userId,
|
|
|
|
|
+ 'Accesstoken': token,
|
|
|
|
|
+ 'Cache-Control': 'no-cache'
|
|
|
|
|
+ },
|
|
|
|
|
+ body: new Blob([body], { type: contentType }), // 二进制请求体
|
|
|
|
|
+ timeout: 30000,
|
|
|
|
|
+ credentials: 'include' // 保持cookie/token会话(和Axios一致)
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 解析响应
|
|
|
|
|
+ if (!response.ok) {
|
|
|
|
|
+ throw new Error(`请求失败: ${response.status}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ const resData = await response.json();
|
|
|
|
|
+ console.log('文件上传成功:', resData);
|
|
|
|
|
+ return resData;
|
|
|
|
|
+
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('文件上传失败:', error);
|
|
|
|
|
+ showFailToast('文件上传失败');
|
|
|
|
|
+ throw error;
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+// /pdcApi/file
|
|
|
|
|
+const newFile = ref({})
|
|
|
|
|
+const isAdding = ref(false);
|
|
|
|
|
+watch(() => fileList.value, (newVal, oldVal) => {
|
|
|
|
|
+ if (newVal.length>oldVal.length) {
|
|
|
|
|
+ isAdding.value = true;
|
|
|
|
|
+ newFile.value = getObjectArrayDiff(newVal, oldVal, 'id').onlyInArr1[0]
|
|
|
|
|
+ console.log(newFile.value.onlyInArr1);
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ else{
|
|
|
|
|
+ isAdding.value = false;
|
|
|
|
|
+ newFile.value = getObjectArrayDiff(newVal, oldVal, 'id').onlyInArr2[0];
|
|
|
|
|
+ showConfirmDialog({
|
|
|
|
|
+ title: '删除',
|
|
|
|
|
+ message:
|
|
|
|
|
+ '是否确认删除',
|
|
|
|
|
+ })
|
|
|
|
|
+ .then(() => {
|
|
|
|
|
+ request.post(`/file/${newFile.value.id}`).then(res=>{
|
|
|
|
|
+ if(res.success){
|
|
|
|
|
+ showSuccessToast('删除成功!');
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch(() => {
|
|
|
|
|
+ fileList.value = oldVal;
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+});
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
|
var par = JSON.parse(router.query.object);
|
|
var par = JSON.parse(router.query.object);
|
|
|
pblm.value.objId = par.objId
|
|
pblm.value.objId = par.objId
|