|
|
@@ -40,102 +40,127 @@
|
|
|
<!-- 表头操作图标(已注释) -->
|
|
|
<!-- <i ... ></i> -->
|
|
|
|
|
|
- <!-- ========== 复选框模式 ========== -->
|
|
|
- <template v-if="cell.mode === 'checkbox'">
|
|
|
- <div class="checkbox-group">
|
|
|
+ <div style="display: flex; align-items: center">
|
|
|
+ <span>{{ cell.label }}</span>
|
|
|
+ <template v-if="cell.mode === 'checkbox'">
|
|
|
+ <div class="checkbox-group" style="flex: 1">
|
|
|
+ <div
|
|
|
+ v-for="cb in cell.checkboxes"
|
|
|
+ :key="cb.id"
|
|
|
+ class="checkbox-item"
|
|
|
+ >
|
|
|
+ <input type="checkbox" v-model="cb.checked" />
|
|
|
+ <input
|
|
|
+ v-if="edit"
|
|
|
+ type="text"
|
|
|
+ v-model="cb.label"
|
|
|
+ class="checkbox-label-input"
|
|
|
+ @input="
|
|
|
+ onCheckboxLabelInput(rowIndex, colIndex, cb.id)
|
|
|
+ "
|
|
|
+ />
|
|
|
+ <span v-else>{{ cb.label }}</span>
|
|
|
+ <i
|
|
|
+ v-if="edit"
|
|
|
+ class="el-icon-delete"
|
|
|
+ @click.stop="
|
|
|
+ removeCheckbox(rowIndex, colIndex, cb.id)
|
|
|
+ "
|
|
|
+ ></i>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ v-if="edit"
|
|
|
+ class="add-checkbox"
|
|
|
+ @click="addCheckbox(rowIndex, colIndex)"
|
|
|
+ >
|
|
|
+ <i class="el-icon-circle-plus-outline"></i> 新增选项
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- ========== 🆕 人员选择模式 ========== -->
|
|
|
+ <template v-else-if="cell.mode === 'person'">
|
|
|
<div
|
|
|
- v-for="cb in cell.checkboxes"
|
|
|
- :key="cb.id"
|
|
|
- class="checkbox-item"
|
|
|
+ style="
|
|
|
+ width: 100%;
|
|
|
+ position: relative;
|
|
|
+ min-height: 30px;
|
|
|
+ flex: 1;
|
|
|
+ "
|
|
|
>
|
|
|
- <input type="checkbox" v-model="cb.checked" />
|
|
|
- <input
|
|
|
- v-if="edit"
|
|
|
- type="text"
|
|
|
- v-model="cb.label"
|
|
|
- class="checkbox-label-input"
|
|
|
- @input="onCheckboxLabelInput(rowIndex, colIndex, cb.id)"
|
|
|
+ <!-- 人员选择组件 -->
|
|
|
+ <personSelect
|
|
|
+ v-model="cell.value"
|
|
|
+ :disabled="readonly || cell.readonly === 2"
|
|
|
+ :readonly="readonly || cell.readonly === 2"
|
|
|
+ @change="onPersonChange(rowIndex, colIndex)"
|
|
|
+ style="width: 95%"
|
|
|
+ :init="false"
|
|
|
+ :selectList="userPage"
|
|
|
/>
|
|
|
- <span v-else>{{ cb.label }}</span>
|
|
|
+ <!-- 编辑图标(仅在编辑模式下显示) -->
|
|
|
<i
|
|
|
v-if="edit"
|
|
|
- class="el-icon-delete"
|
|
|
- @click.stop="removeCheckbox(rowIndex, colIndex, cb.id)"
|
|
|
+ class="el-icon-edit person-edit-icon"
|
|
|
+ @click.stop="openPersonConfig(cell, rowIndex, colIndex)"
|
|
|
></i>
|
|
|
</div>
|
|
|
+ </template>
|
|
|
+ <!-- ========== 🆕 部门选择模式 ========== -->
|
|
|
+ <template v-else-if="cell.mode === 'dept'">
|
|
|
<div
|
|
|
- v-if="edit"
|
|
|
- class="add-checkbox"
|
|
|
- @click="addCheckbox(rowIndex, colIndex)"
|
|
|
+ style="
|
|
|
+ width: 100%;
|
|
|
+ position: relative;
|
|
|
+ min-height: 30px;
|
|
|
+ flex: 1;
|
|
|
+ "
|
|
|
>
|
|
|
- <i class="el-icon-circle-plus-outline"></i> 新增选项
|
|
|
+ <deptSelect
|
|
|
+ v-model="cell.value"
|
|
|
+ :disabled="readonly || cell.readonly === 2"
|
|
|
+ :isBindPlan="readonly || cell.readonly === 2"
|
|
|
+ @change="onDeptChange(rowIndex, colIndex)"
|
|
|
+ style="width: 95%"
|
|
|
+ placeholder="请选择部门"
|
|
|
+ :init="false"
|
|
|
+ :selectList="deptList"
|
|
|
+ />
|
|
|
+ <i
|
|
|
+ v-if="edit"
|
|
|
+ class="el-icon-edit person-edit-icon"
|
|
|
+ @click.stop="openPersonConfig(cell, rowIndex, colIndex)"
|
|
|
+ ></i>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
-
|
|
|
- <!-- ========== 🆕 人员选择模式 ========== -->
|
|
|
- <template v-else-if="cell.mode === 'person'">
|
|
|
- <div
|
|
|
- style="width: 100%; position: relative; min-height: 30px"
|
|
|
- >
|
|
|
- <!-- 人员选择组件 -->
|
|
|
- <personSelect
|
|
|
- v-model="cell.value"
|
|
|
- :disabled="!edit"
|
|
|
- :readonly="readonly || cell.readonly === 2"
|
|
|
- @change="onPersonChange(rowIndex, colIndex)"
|
|
|
- style="width: 95%"
|
|
|
- />
|
|
|
- <!-- 编辑图标(仅在编辑模式下显示) -->
|
|
|
- <i
|
|
|
- v-if="edit"
|
|
|
- class="el-icon-edit person-edit-icon"
|
|
|
- @click.stop="openPersonConfig(cell, rowIndex, colIndex)"
|
|
|
- ></i>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <!-- ========== 🆕 部门选择模式 ========== -->
|
|
|
- <template v-else-if="cell.mode === 'dept'">
|
|
|
- <div
|
|
|
- style="width: 100%; position: relative; min-height: 30px"
|
|
|
- >
|
|
|
- <deptSelect
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- ========== 文本模式 ========== -->
|
|
|
+ <template v-else>
|
|
|
+ <textarea
|
|
|
v-model="cell.value"
|
|
|
- :disabled="!edit"
|
|
|
- :isBindPlan="readonly || cell.readonly === 2"
|
|
|
- @change="onDeptChange(rowIndex, colIndex)"
|
|
|
- style="width: 95%"
|
|
|
- placeholder="请选择部门"
|
|
|
- />
|
|
|
- <i
|
|
|
- v-if="edit"
|
|
|
- class="el-icon-edit person-edit-icon"
|
|
|
- @click.stop="openPersonConfig(cell, rowIndex, colIndex)"
|
|
|
- ></i>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
-
|
|
|
- <!-- ========== 文本模式 ========== -->
|
|
|
- <template v-else>
|
|
|
- <textarea
|
|
|
- v-model="cell.value"
|
|
|
- class="templateInput"
|
|
|
- :style="{ textAlign: cell.textAlign || 'center' }"
|
|
|
- :id="cell.id"
|
|
|
- :ref="cell.id + 'ref'"
|
|
|
- :readonly="cell.readonly === 2 || readonly"
|
|
|
- @mousedown="onCellMouseDown"
|
|
|
- @click="
|
|
|
- inputClick(
|
|
|
- cell,
|
|
|
- rowIndex === 0 ? 'columns' : null,
|
|
|
- $event
|
|
|
- )
|
|
|
- "
|
|
|
- @input="onInput($event)"
|
|
|
- autocomplete="off"
|
|
|
- ></textarea>
|
|
|
- </template>
|
|
|
+ class="templateInput"
|
|
|
+ :style="{
|
|
|
+ textAlign: cell.textAlign || 'left',
|
|
|
+ flex: 1
|
|
|
+ }"
|
|
|
+ :id="cell.id"
|
|
|
+ :ref="cell.id + 'ref'"
|
|
|
+ :readonly="cell.readonly === 2 || readonly"
|
|
|
+ @mousedown="onCellMouseDown"
|
|
|
+ @click="
|
|
|
+ inputClick(
|
|
|
+ cell,
|
|
|
+ rowIndex === 0 ? 'columns' : null,
|
|
|
+ $event
|
|
|
+ )
|
|
|
+ "
|
|
|
+ @input="onInput($event)"
|
|
|
+ autocomplete="off"
|
|
|
+ ></textarea>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- ========== 复选框模式 ========== -->
|
|
|
</td>
|
|
|
</template>
|
|
|
</tr>
|
|
|
@@ -161,6 +186,8 @@
|
|
|
},
|
|
|
data() {
|
|
|
return {
|
|
|
+ userPage: [],
|
|
|
+ deptList: [],
|
|
|
form: null,
|
|
|
valueObj: {},
|
|
|
tableData: [],
|
|
|
@@ -258,6 +285,14 @@
|
|
|
this.menuCloseHandler = null;
|
|
|
}
|
|
|
},
|
|
|
+ created() {
|
|
|
+ getUserPage({ pageNum: 1, size: -1 }).then((res) => {
|
|
|
+ this.userPage = res.list;
|
|
|
+ });
|
|
|
+ listOrganizations({ pageNum: 1, size: -1 }).then((res) => {
|
|
|
+ this.deptList = res
|
|
|
+ });
|
|
|
+ },
|
|
|
methods: {
|
|
|
getTransitions(key) {
|
|
|
return [
|
|
|
@@ -287,7 +322,7 @@
|
|
|
return {
|
|
|
...cell.style,
|
|
|
width: width + 'px',
|
|
|
- 'text-align': cell.textAlign || 'center' // 默认居中
|
|
|
+ 'text-align': cell.textAlign || 'left' // 默认居中
|
|
|
};
|
|
|
},
|
|
|
|
|
|
@@ -299,11 +334,12 @@
|
|
|
value: '',
|
|
|
rowspan: 1,
|
|
|
colspan: 1,
|
|
|
+ label: '',
|
|
|
width: safeWidth,
|
|
|
style: { width: safeWidth },
|
|
|
readonly: 1,
|
|
|
mode: 'text', // 'text' | 'checkbox' | 'person'
|
|
|
- textAlign: 'center',
|
|
|
+ textAlign: 'left',
|
|
|
checkboxes: []
|
|
|
};
|
|
|
},
|
|
|
@@ -940,9 +976,8 @@
|
|
|
},
|
|
|
// ========== 🆕 打印(异步 + 人员名称映射) ==========
|
|
|
async printTable() {
|
|
|
- // 1. 先获取用户映射(如果未加载)
|
|
|
await this.fetchUserMap();
|
|
|
- await this.fetchDeptMap(); // 🆕 加载部门映射
|
|
|
+ await this.fetchDeptMap();
|
|
|
const tableEl = this.$el.querySelector('.custom-table');
|
|
|
if (!tableEl) return;
|
|
|
|
|
|
@@ -953,16 +988,7 @@
|
|
|
)
|
|
|
.forEach((el) => el.remove());
|
|
|
|
|
|
- // 处理 textarea
|
|
|
- clone.querySelectorAll('textarea').forEach((ta) => {
|
|
|
- const div = document.createElement('div');
|
|
|
- div.textContent = ta.value;
|
|
|
- div.style.cssText =
|
|
|
- 'text-align:center;word-break:break-all;padding:4px;white-space:pre-wrap;';
|
|
|
- ta.parentNode.replaceChild(div, ta);
|
|
|
- });
|
|
|
-
|
|
|
- // 处理复选框和人员选择
|
|
|
+ // 遍历所有单元格,根据模式重新构建显示内容(保留 label)
|
|
|
for (let r = 0; r < this.tableData.length; r++) {
|
|
|
for (let c = 0; c < this.tableData[r].length; c++) {
|
|
|
const cell = this.tableData[r][c];
|
|
|
@@ -971,65 +997,68 @@
|
|
|
);
|
|
|
if (!td) continue;
|
|
|
|
|
|
+ const label = cell.label || '';
|
|
|
+ let displayHtml = '';
|
|
|
+ td.style.padding = '3px';
|
|
|
if (cell.mode === 'checkbox' && Array.isArray(cell.checkboxes)) {
|
|
|
- const container = document.createElement('div');
|
|
|
- container.style.cssText =
|
|
|
- 'display:flex; flex-wrap:wrap; gap:5px; justify-content:center;';
|
|
|
- cell.checkboxes.forEach((cb) => {
|
|
|
- const item = document.createElement('div');
|
|
|
- item.style.cssText =
|
|
|
- 'display:inline-flex; align-items:center; gap:5px; white-space:nowrap;';
|
|
|
- const symbolSpan = document.createElement('span');
|
|
|
- symbolSpan.textContent = cb.checked ? '☑' : '☐';
|
|
|
- symbolSpan.style.fontSize = '14px';
|
|
|
- const labelSpan = document.createElement('span');
|
|
|
- labelSpan.textContent = cb.label || '';
|
|
|
- item.appendChild(symbolSpan);
|
|
|
- item.appendChild(labelSpan);
|
|
|
- container.appendChild(item);
|
|
|
- });
|
|
|
- td.innerHTML = '';
|
|
|
- td.appendChild(container);
|
|
|
+ const items = cell.checkboxes
|
|
|
+ .map((cb) => {
|
|
|
+ const symbol = cb.checked ? '☑' : '☐';
|
|
|
+ return `<span style="margin:0 4px;">${symbol} ${
|
|
|
+ cb.label || ''
|
|
|
+ }</span>`;
|
|
|
+ })
|
|
|
+ .join('');
|
|
|
+ displayHtml = `<div style="display:flex; align-items:center; justify-content:center; gap:5px; flex-wrap:wrap;">${
|
|
|
+ label ? `<span >${label}</span>` : ''
|
|
|
+ }${items}</div>`;
|
|
|
} else if (cell.mode === 'person') {
|
|
|
- // 🆕 使用映射显示人员名称
|
|
|
- const userId = cell.value;
|
|
|
- const userName = this.userMap[userId] || userId || '';
|
|
|
- const div = document.createElement('div');
|
|
|
- div.textContent = userName;
|
|
|
- div.style.cssText = 'text-align:center;padding:4px;';
|
|
|
- td.innerHTML = '';
|
|
|
- td.appendChild(div);
|
|
|
+ const userName = this.userMap[cell.value] || cell.value || '';
|
|
|
+ displayHtml = `<div style="display:flex; align-items:center; justify-content:center; gap:5px;">${
|
|
|
+ label ? `<span >${label}</span>` : ''
|
|
|
+ }<span style="flex:1">${userName}</span></div>`;
|
|
|
} else if (cell.mode === 'dept') {
|
|
|
- // 🆕
|
|
|
- const deptId = cell.value;
|
|
|
- const deptName = this.deptMap[deptId] || deptId || '';
|
|
|
- const div = document.createElement('div');
|
|
|
- div.textContent = deptName;
|
|
|
- div.style.cssText = 'text-align:center;padding:4px;';
|
|
|
- td.innerHTML = '';
|
|
|
- td.appendChild(div);
|
|
|
+ const deptName = this.deptMap[cell.value] || cell.value || '';
|
|
|
+ displayHtml = `<div style="display:flex; align-items:center; justify-content:center; gap:5px;">${
|
|
|
+ label ? `<span >${label}</span>` : ''
|
|
|
+ }<span style="flex:1">${deptName}</span></div>`;
|
|
|
+ } else {
|
|
|
+ console.log(cell.textAlign);
|
|
|
+ // 文本模式
|
|
|
+ displayHtml = `<div style="display:flex; align-items:center; justify-content:${
|
|
|
+ cell.textAlign == 'left'
|
|
|
+ ? 'flex-start'
|
|
|
+ : cell.textAlign == 'right'
|
|
|
+ ? 'flex-end'
|
|
|
+ : 'center'
|
|
|
+ };">${label ? `<span >${label}</span>` : ''}<span>${
|
|
|
+ cell.value || ''
|
|
|
+ }</span></div>`;
|
|
|
}
|
|
|
+
|
|
|
+ td.innerHTML = displayHtml;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 打印 iframe(保持不变)
|
|
|
const iframe = document.createElement('iframe');
|
|
|
iframe.style.cssText = 'position:absolute;width:0;height:0;border:0;';
|
|
|
document.body.appendChild(iframe);
|
|
|
const doc = iframe.contentWindow.document;
|
|
|
doc.write(`
|
|
|
- <!DOCTYPE html>
|
|
|
- <html>
|
|
|
- <head>
|
|
|
- <meta charset="utf-8">
|
|
|
- <style>
|
|
|
- body { margin: 0; padding: 0; font-family: Arial, sans-serif; }
|
|
|
- table { width: 100%; border-collapse: collapse; table-layout: auto; }
|
|
|
- td { border: 1px solid #000; vertical-align: middle; text-align: center; padding: 1px; font-size: 12px; padding:0px; }
|
|
|
- </style>
|
|
|
- </head>
|
|
|
- <body>${clone.outerHTML}</body>
|
|
|
- </html>
|
|
|
- `);
|
|
|
+ <!DOCTYPE html>
|
|
|
+ <html>
|
|
|
+ <head>
|
|
|
+ <meta charset="utf-8">
|
|
|
+ <style>
|
|
|
+ body { margin: 0; padding: 0; font-family: Arial, sans-serif; }
|
|
|
+ table { width: 100%; border-collapse: collapse; table-layout: auto; }
|
|
|
+ td { border: 1px solid #000; vertical-align: middle; padding: 1px; font-size: 12px; padding:0px; }
|
|
|
+ </style>
|
|
|
+ </head>
|
|
|
+ <body>${clone.outerHTML}</body>
|
|
|
+ </html>
|
|
|
+ `);
|
|
|
doc.close();
|
|
|
iframe.contentWindow.focus();
|
|
|
iframe.contentWindow.print();
|