|
@@ -23,7 +23,12 @@
|
|
|
</el-col>
|
|
|
<el-row :gutter="10" class="mb8">
|
|
|
<el-col :span="1.5">
|
|
|
- <el-button type="primary" @click="handleAdd">批量导入</el-button>
|
|
|
+<!-- <el-button type="primary" @click="handleAdd">导入</el-button>-->
|
|
|
+ <el-upload class="upload-demo" :http-request="handleAdd" :before-upload="beforeUpload" accept=".xlsx, .xls">
|
|
|
+ <template #trigger>
|
|
|
+ <el-button size="big" type="primary">导入</el-button>
|
|
|
+ </template>
|
|
|
+ </el-upload>
|
|
|
</el-col>
|
|
|
<el-col :span="1.5">
|
|
|
<el-button type="primary" @click="handleDownloadEmptyTable">下载空表格</el-button>
|
|
@@ -33,26 +38,15 @@
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
<el-col :lg="30" :xs="24">
|
|
|
- <el-table :data="tableData" border>
|
|
|
- <el-table-column v-for="header in editableHeaders" :key="header" :label="header" :prop="header">
|
|
|
- <template #header="{ column }">
|
|
|
- <span
|
|
|
- class="editable-header"
|
|
|
- contenteditable="true"
|
|
|
- @blur="saveHeader(column.label, $event.target.innerText)"
|
|
|
- v-text="column.label"
|
|
|
- ></span>
|
|
|
- </template>
|
|
|
- <template #default="{ row, $index }">
|
|
|
- <span
|
|
|
- class="editable-span"
|
|
|
- contenteditable="true"
|
|
|
- @blur="saveEdit($index, header, $event.target.innerText)"
|
|
|
- v-text="row[header]"
|
|
|
- ></span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
+ <div :style="{ height: tableHeight + 'px' }">
|
|
|
+ <hot-table ref="wrapper" :data="tableData" :settings="hotSettings" />
|
|
|
+ </div>
|
|
|
+<!-- <div :style="{ height: tableHeight + 'px' }" style="height: 400px">-->
|
|
|
+<!-- <hot-table :settings="hotSettings">-->
|
|
|
+<!-- <hot-column v-for="item in editableHeaders" :title="item.field_comment">-->
|
|
|
+<!-- </hot-column>-->
|
|
|
+<!-- </hot-table>-->
|
|
|
+<!-- </div>-->
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
</div>
|
|
@@ -61,50 +55,64 @@
|
|
|
<script setup lang="ts">
|
|
|
import * as XLSX from 'xlsx';
|
|
|
import { writeView, submitFill } from '@/api/dataFilling/datafilling';
|
|
|
+import { HotTable, HotColumn } from '@handsontable/vue3';
|
|
|
+import 'handsontable/languages';
|
|
|
+import 'handsontable/dist/handsontable.full.css';
|
|
|
+import { registerAllModules } from 'handsontable/registry';
|
|
|
+import { ElButton } from 'element-plus';
|
|
|
+import { ref } from 'vue';
|
|
|
+import { fillingReport } from '@/api/dataFilling/fillingManage';
|
|
|
+registerAllModules();
|
|
|
|
|
|
const props = defineProps({
|
|
|
eventId: String
|
|
|
});
|
|
|
|
|
|
const emits = defineEmits(['close']);
|
|
|
-
|
|
|
+let wrapper = ref();
|
|
|
+let tableHeight = window.innerHeight - 230
|
|
|
const detailData = ref({
|
|
|
title: '表单数据',
|
|
|
start: '2024-10-15 17:02:22',
|
|
|
end: '2024-10-15 18:00:00'
|
|
|
});
|
|
|
|
|
|
+const fileList = ref([]);
|
|
|
+const { proxy } = getCurrentInstance();
|
|
|
const editableHeaders = ref<string[]>([]);
|
|
|
const tableData = ref<any[]>([]);
|
|
|
const headerMapping = ref<{ [key: string]: string }>({});
|
|
|
+const fields = ref();
|
|
|
// 获取表头数据
|
|
|
const fetchHeaders = async () => {
|
|
|
- try {
|
|
|
- const response = await writeView({ report_id: props.eventId });
|
|
|
- if (response.code === 200) {
|
|
|
- const headers = response.fields.map((field) => field.field_comment);
|
|
|
- editableHeaders.value = headers;
|
|
|
-
|
|
|
- // 填充映射
|
|
|
- response.fields.forEach(field => {
|
|
|
- headerMapping.value[field.field_comment] = field.field_name;
|
|
|
- });
|
|
|
- } else {
|
|
|
- console.error('获取表头失败:', response.msg);
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('请求错误:', error);
|
|
|
- }
|
|
|
-};
|
|
|
+ const response = await writeView({ report_id: props.eventId });
|
|
|
+ fields.value = response.fields;
|
|
|
+ const headers = response.fields.map((field) => field.field_comment);
|
|
|
+ editableHeaders.value = headers;
|
|
|
+ let arr1 = [];
|
|
|
+ let arr2 = [];
|
|
|
+ let arr3 = [];
|
|
|
|
|
|
-// 初始化表格数据
|
|
|
-const loadFromLocalStorage = () => {
|
|
|
- const storedData = localStorage.getItem('tableData');
|
|
|
- if (storedData) {
|
|
|
- tableData.value = JSON.parse(storedData);
|
|
|
+ // tableData.value = response.tableData;
|
|
|
+ //如果tableData是空就用下满这段,tableData有数据就用tableData的内容
|
|
|
+ if (!response.tableData || response.tableData.length === 0) {
|
|
|
+ headers.forEach((item) => {
|
|
|
+ arr1.push('');
|
|
|
+ arr2.push('');
|
|
|
+ arr3.push('');
|
|
|
+ })
|
|
|
+ tableData.value = [arr1, arr2 ,arr3];
|
|
|
+ wrapper.value.hotInstance.loadData([arr1, arr2 ,arr3]);
|
|
|
} else {
|
|
|
- tableData.value = [];
|
|
|
+ tableData.value = response.tableData
|
|
|
+ wrapper.value.hotInstance.loadData(response.tableData);
|
|
|
}
|
|
|
+
|
|
|
+ // 填充映射
|
|
|
+ // response.fields.forEach(field => {
|
|
|
+ // headerMapping.value[field.field_comment] = field.field_name;
|
|
|
+ // });
|
|
|
+
|
|
|
};
|
|
|
|
|
|
const addDefaultRow = () => {
|
|
@@ -127,7 +135,16 @@ const saveHeader = (header, newValue) => {
|
|
|
};
|
|
|
|
|
|
const exportToExcel = () => {
|
|
|
- const worksheet = XLSX.utils.json_to_sheet(tableData.value);
|
|
|
+ let resultList = [];
|
|
|
+ resultList.push(editableHeaders.value);
|
|
|
+ // editableHeaders.value.forEach((header) => {
|
|
|
+ // resultList.push(header);
|
|
|
+ // })
|
|
|
+ tableData.value.forEach((item) => {
|
|
|
+ resultList.push(item);
|
|
|
+ })
|
|
|
+
|
|
|
+ const worksheet = XLSX.utils.aoa_to_sheet(resultList);
|
|
|
const workbook = { Sheets: { data: worksheet }, SheetNames: ['data'] };
|
|
|
const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
|
|
|
|
|
@@ -140,51 +157,164 @@ const exportToExcel = () => {
|
|
|
window.URL.revokeObjectURL(url); // 清理
|
|
|
};
|
|
|
|
|
|
-const handleAdd = () => console.log('批量导入');
|
|
|
+const handleAdd = ({file}) => {
|
|
|
+
|
|
|
+ const reader = new FileReader();
|
|
|
+ reader.onload = (event) => {
|
|
|
+ nextTick(() => {
|
|
|
+ const data = new Uint8Array(event.target.result);
|
|
|
+ const workbook = XLSX.read(data, { type: 'array' });
|
|
|
+ const firstSheetName = workbook.SheetNames[0];
|
|
|
+ const worksheet = workbook.Sheets[firstSheetName];
|
|
|
+ const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
|
|
|
+ tableData.value = jsonData.slice(1); // 假设第一行是标题行,我们跳过它
|
|
|
+ wrapper.value.hotInstance.loadData(tableData.value);
|
|
|
+ });
|
|
|
+ };
|
|
|
+ reader.readAsArrayBuffer(file);
|
|
|
+ fileList.value = [];
|
|
|
+ return { success: true, file };
|
|
|
+};
|
|
|
+const beforeUpload = (file) => {
|
|
|
+ const isExcel = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || file.type === 'application/vnd.ms-excel';
|
|
|
+ if (!isExcel) {
|
|
|
+ proxy.$modal.msgError('只能上传xlsx/xls文件!');
|
|
|
+ }
|
|
|
+ return isExcel;
|
|
|
+};
|
|
|
+
|
|
|
const handleDownloadEmptyTable = () => console.log('下载空表格');
|
|
|
-const handleAddRow = () => addDefaultRow();
|
|
|
+const handleAddRow = () => {
|
|
|
+ wrapper.value.hotInstance.alter('insert_row_below', wrapper.value.hotInstance.countRows()); // 在末尾插入空行
|
|
|
+}
|
|
|
const handleSave = () => {
|
|
|
- localStorage.setItem('tableData', JSON.stringify(tableData.value));
|
|
|
- alert('数据已保存');
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ // localStorage.setItem('tableData', JSON.stringify(tableData.value));
|
|
|
+ // alert('数据已保存');
|
|
|
};
|
|
|
const handleReturn = () => {
|
|
|
emits('close');
|
|
|
};
|
|
|
+const dataTemp = ref()
|
|
|
+const formattedData = () => {
|
|
|
+ dataTemp.value = tableData.value.map(row =>
|
|
|
+ fields.value.reduce((obj, field, index) => {
|
|
|
+ const key = field.file_name === 'xmbz' ? 'bz' : field.file_name;
|
|
|
+ obj[key] = row[index];
|
|
|
+ return obj;
|
|
|
+ }, {})
|
|
|
+ )
|
|
|
+};
|
|
|
const handleReport = async () => {
|
|
|
- try {
|
|
|
- const mappedData = tableData.value.map((row) => {
|
|
|
- const mappedRow: any = {};
|
|
|
- Object.keys(row).forEach((header) => {
|
|
|
- const fieldName = headerMapping.value[header];
|
|
|
- if (fieldName) {
|
|
|
- mappedRow[fieldName] = row[header];
|
|
|
- }
|
|
|
- });
|
|
|
- return mappedRow;
|
|
|
- });
|
|
|
-
|
|
|
- const data = {
|
|
|
- report_id: props.eventId,
|
|
|
- data: mappedData
|
|
|
- };
|
|
|
+ formattedData();
|
|
|
+ // fillingReport(props.eventId)
|
|
|
|
|
|
- const response = await submitFill(data);
|
|
|
- if (response.code === 200) {
|
|
|
- alert('数据上报成功');
|
|
|
- } else {
|
|
|
- alert('数据上报失败: ' + response.msg);
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('上报数据时发生错误:', error);
|
|
|
- alert('数据上报失败,请稍后再试');
|
|
|
- }
|
|
|
+ // try {
|
|
|
+ // const mappedData = tableData.value.map((row) => {
|
|
|
+ // const mappedRow: any = {};
|
|
|
+ // Object.keys(row).forEach((header) => {
|
|
|
+ // const fieldName = headerMapping.value[header];
|
|
|
+ // if (fieldName) {
|
|
|
+ // mappedRow[fieldName] = row[header];
|
|
|
+ // }
|
|
|
+ // });
|
|
|
+ // return mappedRow;
|
|
|
+ // });
|
|
|
+ //
|
|
|
+ // const data = {
|
|
|
+ // report_id: props.eventId,
|
|
|
+ // data: mappedData
|
|
|
+ // };
|
|
|
+ //
|
|
|
+ // const response = await submitFill(data);
|
|
|
+ // if (response.code === 200) {
|
|
|
+ // alert('数据上报成功');
|
|
|
+ // } else {
|
|
|
+ // alert('数据上报失败: ' + response.msg);
|
|
|
+ // }
|
|
|
+ // } catch (error) {
|
|
|
+ // console.error('上报数据时发生错误:', error);
|
|
|
+ // alert('数据上报失败,请稍后再试');
|
|
|
+ // }
|
|
|
};
|
|
|
// 组件挂载时调用
|
|
|
onMounted(() => {
|
|
|
- loadFromLocalStorage();
|
|
|
fetchHeaders();
|
|
|
addDefaultRow();
|
|
|
});
|
|
|
+
|
|
|
+const hotSettings = reactive({
|
|
|
+ data: [['1','2']],
|
|
|
+ language: 'zh-CN',
|
|
|
+ colHeaders: editableHeaders,
|
|
|
+ rowHeaders: true,
|
|
|
+ autoColumnSize: true,
|
|
|
+ width: '100%', // auto or 100%
|
|
|
+ height: '100%', // auto or 100%
|
|
|
+ licenseKey: 'non-commercial-and-evaluation', // 隐藏版权文字
|
|
|
+ colWidths: 129, // 默认单元格宽度
|
|
|
+ rowHeights: 28, // 默认单元格高度
|
|
|
+ wordWrap: true, // 单元格文字是否换行展示
|
|
|
+ contextMenu: {
|
|
|
+ // 自定义右键菜单
|
|
|
+ items: {
|
|
|
+ 'row_above': {
|
|
|
+ name: '向上插一行'
|
|
|
+ },
|
|
|
+ 'row_below': {
|
|
|
+ name: '向下插一行'
|
|
|
+ },
|
|
|
+ 'col_left': {
|
|
|
+ name: '向左插一列'
|
|
|
+ },
|
|
|
+ 'col_right': {
|
|
|
+ name: '向右插一列'
|
|
|
+ },
|
|
|
+ 'hsep1': '---------', // 分隔线
|
|
|
+ 'remove_row': {
|
|
|
+ name: '删除当前行'
|
|
|
+ },
|
|
|
+ 'remove_col': {
|
|
|
+ name: '删除当前列'
|
|
|
+ },
|
|
|
+ 'clear_column': {
|
|
|
+ name: '清空当前列'
|
|
|
+ },
|
|
|
+ 'hsep2': '---------', // 必须和上次的变量名不一样
|
|
|
+ 'undo': {
|
|
|
+ name: '撤销'
|
|
|
+ },
|
|
|
+ 'cut': {
|
|
|
+ name: '剪切'
|
|
|
+ },
|
|
|
+ 'copy': {
|
|
|
+ name: '复制'
|
|
|
+ },
|
|
|
+ 'alignment': {
|
|
|
+ name: '对齐'
|
|
|
+ },
|
|
|
+ 'hsep3': '---------',
|
|
|
+ 'commentsAddEdit': {
|
|
|
+ // 必须开启 comments: true
|
|
|
+ name: '添加备注'
|
|
|
+ },
|
|
|
+ 'commentsRemove': {
|
|
|
+ // 必须开启 comments: true
|
|
|
+ name: '删除备注'
|
|
|
+ },
|
|
|
+ 'freeze_column': {
|
|
|
+ // 必须开启 manualColumnFreeze: true
|
|
|
+ name: '固定列'
|
|
|
+ },
|
|
|
+ 'unfreeze_column': {
|
|
|
+ // 必须开启 manualColumnFreeze: true
|
|
|
+ name: '取消固定列'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+});
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
@@ -192,7 +322,6 @@ onMounted(() => {
|
|
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
|
|
-webkit-font-smoothing: antialiased;
|
|
|
-moz-osx-font-smoothing: grayscale;
|
|
|
- text-align: center;
|
|
|
color: #2c3e50;
|
|
|
}
|
|
|
.report-period {
|