123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- <template>
- <div class="app-container p-2">
- <el-row :gutter="20">
- <el-col :lg="30" :xs="24">
- <el-row :span="24" :gutter="10">
- <el-col :span="18">
- <h2 v-if="detailData.title" style="font-weight: bolder">{{ detailData.title }}</h2>
- <p class="report-period">【填报周期】:{{ detailData.start }} 至 {{ detailData.end }}</p>
- </el-col>
- <el-col :span="1.5">
- <el-button type="primary" @click="handleReport">上报</el-button>
- </el-col>
- <el-col :span="1.5">
- <el-button type="primary" @click="exportToExcel">导出表格</el-button>
- </el-col>
- <el-col :span="1.5">
- <el-button type="primary" @click="handleSave">保存</el-button>
- </el-col>
- <el-col :span="1.5">
- <el-button type="danger" @click="handleReturn">返回</el-button>
- </el-col>
- </el-row>
- </el-col>
- <el-row :gutter="10" class="mb8">
- <el-col :span="1.5">
- <!-- <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>
- </el-col>
- <el-col :span="1.5">
- <el-button type="primary" @click="handleAddRow">新增一行</el-button>
- </el-col>
- </el-row>
- <el-col :lg="30" :xs="24">
- <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>
- </template>
- <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 () => {
- 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 = [];
- // 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 = response.tableData
- wrapper.value.hotInstance.loadData(response.tableData);
- }
- // 填充映射
- // response.fields.forEach(field => {
- // headerMapping.value[field.field_comment] = field.field_name;
- // });
- };
- const addDefaultRow = () => {
- const newRow = {};
- editableHeaders.value.forEach((header) => {
- newRow[header] = '';
- });
- tableData.value.push(newRow);
- };
- const saveEdit = (rowIndex, header, value) => {
- tableData.value[rowIndex][header] = value;
- };
- const saveHeader = (header, newValue) => {
- const index = editableHeaders.value.indexOf(header);
- if (index !== -1) {
- editableHeaders.value.splice(index, 1, newValue);
- }
- };
- const exportToExcel = () => {
- 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' });
- const blob = new Blob([excelBuffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
- const link = document.createElement('a');
- const url = window.URL.createObjectURL(blob);
- link.href = url;
- link.download = 'SheetJS.xlsx';
- link.click();
- window.URL.revokeObjectURL(url); // 清理
- };
- 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 = () => {
- wrapper.value.hotInstance.alter('insert_row_below', wrapper.value.hotInstance.countRows()); // 在末尾插入空行
- }
- const handleSave = () => {
- // 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 () => {
- formattedData();
- // fillingReport(props.eventId)
- // 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(() => {
- 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>
- .app-container {
- font-family: Avenir, Helvetica, Arial, sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- color: #2c3e50;
- }
- .report-period {
- margin-top: 10px;
- font-size: 14px;
- color: #606266;
- }
- .editable-span {
- cursor: pointer;
- display: inline-block;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .editable-span[contenteditable='true'] {
- white-space: normal;
- outline: none; /* 移除编辑时的焦点边框 */
- }
- .editable-span[contenteditable='true']:empty::before {
- content: attr(data-placeholder); /* 可选:为空时显示占位符 */
- color: #999;
- }
- .editable-header {
- cursor: pointer;
- display: inline-block;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .editable-header[contenteditable='true'] {
- white-space: normal;
- outline: none; /* 移除编辑时的焦点边框 */
- }
- .editable-header[contenteditable='true']:empty::before {
- content: attr(data-placeholder); /* 可选:为空时显示占位符 */
- color: #999;
- }
- </style>
|