|
|
@@ -94,6 +94,26 @@
|
|
|
></i>
|
|
|
</div>
|
|
|
</template>
|
|
|
+ <!-- ========== 🆕 部门选择模式 ========== -->
|
|
|
+ <template v-else-if="cell.mode === 'dept'">
|
|
|
+ <div
|
|
|
+ style="width: 100%; position: relative; min-height: 30px"
|
|
|
+ >
|
|
|
+ <deptSelect
|
|
|
+ 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>
|
|
|
@@ -127,13 +147,13 @@
|
|
|
|
|
|
<script>
|
|
|
import { generateRandomString } from '@/utils/util';
|
|
|
- // 🆕 导入人员选择组件
|
|
|
import personSelect from '@/components/CommomSelect/person-select.vue';
|
|
|
- // 🆕 导入人员列表接口
|
|
|
+ import deptSelect from '@/components/CommomSelect/dept-select.vue'; // 🆕 导入部门选择组件
|
|
|
import { getUserPage } from '@/api/system/organization';
|
|
|
+ import { listOrganizations } from '@/api/system/organization'; // 🆕 导入部门列表接口
|
|
|
export default {
|
|
|
// 🆕 注册人员选择组件
|
|
|
- components: { personSelect },
|
|
|
+ components: { personSelect, deptSelect },
|
|
|
props: {
|
|
|
id: { type: String, default: '' },
|
|
|
edit: { type: Boolean, default: true },
|
|
|
@@ -239,6 +259,27 @@
|
|
|
}
|
|
|
},
|
|
|
methods: {
|
|
|
+ getTransitions(key) {
|
|
|
+ return [
|
|
|
+ { target: 'text', label: '转为文本模式', method: 'disableDeptMode' },
|
|
|
+ {
|
|
|
+ target: 'checkbox',
|
|
|
+ label: '转为复选框模式',
|
|
|
+ method: 'enableCheckboxMode'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ target: 'person',
|
|
|
+ label: '转为人员选择模式',
|
|
|
+ method: 'enablePersonMode'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ target: 'dept',
|
|
|
+ label: '转为部门选择模式',
|
|
|
+ method: 'enableDeptMode'
|
|
|
+ }
|
|
|
+ ].filter((item) => item.target !== key);
|
|
|
+ },
|
|
|
+
|
|
|
// ========== 工具 ==========
|
|
|
getCellStyle(cell) {
|
|
|
let width = parseFloat(cell.style?.width || cell.width || 100);
|
|
|
@@ -549,28 +590,30 @@
|
|
|
this.currentRowIndex = rowIndex;
|
|
|
this.currentColumnIndex = colIndex;
|
|
|
|
|
|
+ // 移除旧菜单和监听
|
|
|
const existingMenu = document.querySelector('.custom-context-menu');
|
|
|
if (existingMenu) existingMenu.remove();
|
|
|
-
|
|
|
if (this.menuCloseHandler) {
|
|
|
document.removeEventListener('click', this.menuCloseHandler);
|
|
|
this.menuCloseHandler = null;
|
|
|
}
|
|
|
|
|
|
+ // 创建菜单容器
|
|
|
const menu = document.createElement('div');
|
|
|
menu.className = 'custom-context-menu';
|
|
|
menu.style.cssText = `
|
|
|
- position: fixed;
|
|
|
- left: ${event.clientX}px;
|
|
|
- top: ${event.clientY}px;
|
|
|
- background: white;
|
|
|
- border: 1px solid #ccc;
|
|
|
- box-shadow: 2px 2px 8px rgba(0,0,0,0.2);
|
|
|
- z-index: 10000;
|
|
|
- padding: 5px 0;
|
|
|
- min-width: 120px;
|
|
|
- `;
|
|
|
-
|
|
|
+ position: fixed;
|
|
|
+ left: ${event.clientX}px;
|
|
|
+ top: ${event.clientY}px;
|
|
|
+ background: white;
|
|
|
+ border: 1px solid #ccc;
|
|
|
+ box-shadow: 2px 2px 8px rgba(0,0,0,0.2);
|
|
|
+ z-index: 10000;
|
|
|
+ padding: 5px 0;
|
|
|
+ min-width: 120px;
|
|
|
+ `;
|
|
|
+
|
|
|
+ // 菜单项添加函数
|
|
|
const addItem = (text, onClick, isSeparator = false) => {
|
|
|
if (isSeparator) {
|
|
|
const hr = document.createElement('hr');
|
|
|
@@ -595,6 +638,7 @@
|
|
|
menu.appendChild(item);
|
|
|
};
|
|
|
|
|
|
+ // 关闭菜单的全局监听
|
|
|
this.menuCloseHandler = (e) => {
|
|
|
if (!menu.contains(e.target)) {
|
|
|
menu.remove();
|
|
|
@@ -603,59 +647,31 @@
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- // ===== 根据行号区分菜单项 =====
|
|
|
+ // ---- 行操作菜单 ----
|
|
|
if (rowIndex === 0) {
|
|
|
- // 表头行:插入/删除列
|
|
|
addItem('插入列', () => this.addColumn(colIndex));
|
|
|
addItem('删除列', () => this.removeColumn(colIndex));
|
|
|
- addItem('', null, true); // 分隔线
|
|
|
+ addItem('', null, true);
|
|
|
} else {
|
|
|
- // 普通行:插入/删除行
|
|
|
addItem('插入行', () => this.addRow(rowIndex));
|
|
|
addItem('删除行', () => this.removeRow(rowIndex));
|
|
|
addItem('', null, true);
|
|
|
}
|
|
|
|
|
|
- // ===== 模式切换(所有行通用) =====
|
|
|
+ // ---- 模式切换
|
|
|
const cell = this.tableData[rowIndex][colIndex];
|
|
|
- if (!cell) {
|
|
|
- document.body.appendChild(menu);
|
|
|
- setTimeout(
|
|
|
- () => document.addEventListener('click', this.menuCloseHandler),
|
|
|
- 0
|
|
|
- );
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 根据当前模式显示可用切换项
|
|
|
- if (cell.mode === 'text' || !cell.mode) {
|
|
|
- addItem('转为复选框模式', () =>
|
|
|
- this.enableCheckboxMode(rowIndex, colIndex)
|
|
|
- );
|
|
|
- addItem('转为人员选择模式', () =>
|
|
|
- this.enablePersonMode(rowIndex, colIndex)
|
|
|
- );
|
|
|
- } else if (cell.mode === 'checkbox') {
|
|
|
- addItem('转为文本模式', () =>
|
|
|
- this.disableCheckboxMode(rowIndex, colIndex)
|
|
|
- );
|
|
|
- addItem('转为人员选择模式', () =>
|
|
|
- this.enablePersonMode(rowIndex, colIndex)
|
|
|
- );
|
|
|
- } else if (cell.mode === 'person') {
|
|
|
- addItem('转为文本模式', () =>
|
|
|
- this.disablePersonMode(rowIndex, colIndex)
|
|
|
- );
|
|
|
- addItem('转为复选框模式', () =>
|
|
|
- this.enableCheckboxMode(rowIndex, colIndex)
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- // 当为复选框模式时,额外显示“添加复选框”
|
|
|
- if (cell.mode === 'checkbox') {
|
|
|
- addItem('添加复选框', () => this.addCheckbox(rowIndex, colIndex));
|
|
|
+ if (cell) {
|
|
|
+ const currentMode = cell.mode || 'text';
|
|
|
+ this.getTransitions(currentMode).forEach(({ label, method }) => {
|
|
|
+ addItem(label, () => this[method](rowIndex, colIndex));
|
|
|
+ });
|
|
|
+ // 复选框模式额外选项
|
|
|
+ if (currentMode === 'checkbox') {
|
|
|
+ addItem('添加复选框', () => this.addCheckbox(rowIndex, colIndex));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+ // 渲染菜单
|
|
|
document.body.appendChild(menu);
|
|
|
setTimeout(() => {
|
|
|
document.addEventListener('click', this.menuCloseHandler);
|
|
|
@@ -713,6 +729,20 @@
|
|
|
|
|
|
onCheckboxLabelInput() {},
|
|
|
|
|
|
+ enableDeptMode(rowIndex, colIndex) {
|
|
|
+ const cell = this.tableData[rowIndex][colIndex];
|
|
|
+ if (!cell) return;
|
|
|
+ this.$set(cell, 'mode', 'dept');
|
|
|
+ this.$forceUpdate();
|
|
|
+ },
|
|
|
+
|
|
|
+ disableDeptMode(rowIndex, colIndex) {
|
|
|
+ const cell = this.tableData[rowIndex][colIndex];
|
|
|
+ if (!cell) return;
|
|
|
+ this.$set(cell, 'mode', 'text');
|
|
|
+ this.$forceUpdate();
|
|
|
+ },
|
|
|
+ onDeptChange() {},
|
|
|
// ========== 🆕 人员选择 ==========
|
|
|
enablePersonMode(rowIndex, colIndex) {
|
|
|
const cell = this.tableData[rowIndex][colIndex];
|
|
|
@@ -862,6 +892,34 @@
|
|
|
valueObj: { tableData }
|
|
|
});
|
|
|
},
|
|
|
+
|
|
|
+ async fetchDeptMap() {
|
|
|
+ if (this.deptMapLoaded) return;
|
|
|
+ try {
|
|
|
+ const res = await listOrganizations({ pageNum: 1, size: -1 });
|
|
|
+ // 递归展平树形数据
|
|
|
+ const flatten = (nodes) => {
|
|
|
+ let result = [];
|
|
|
+ nodes.forEach((node) => {
|
|
|
+ result.push({ id: node.id, name: node.name });
|
|
|
+ if (node.children && node.children.length) {
|
|
|
+ result = result.concat(flatten(node.children));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return result;
|
|
|
+ };
|
|
|
+ const flatList = flatten(res || []);
|
|
|
+ const map = {};
|
|
|
+ flatList.forEach((dept) => {
|
|
|
+ map[dept.id] = dept.name || '未知部门';
|
|
|
+ });
|
|
|
+ this.deptMap = map;
|
|
|
+ this.deptMapLoaded = true;
|
|
|
+ } catch (error) {
|
|
|
+ console.warn('获取部门列表失败,打印时将显示ID', error);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
// ========== 🆕 获取用户映射(供打印使用) ==========
|
|
|
async fetchUserMap() {
|
|
|
if (this.userMapLoaded) return;
|
|
|
@@ -884,7 +942,7 @@
|
|
|
async printTable() {
|
|
|
// 1. 先获取用户映射(如果未加载)
|
|
|
await this.fetchUserMap();
|
|
|
-
|
|
|
+ await this.fetchDeptMap(); // 🆕 加载部门映射
|
|
|
const tableEl = this.$el.querySelector('.custom-table');
|
|
|
if (!tableEl) return;
|
|
|
|
|
|
@@ -941,6 +999,15 @@
|
|
|
div.style.cssText = 'text-align:center;padding:4px;';
|
|
|
td.innerHTML = '';
|
|
|
td.appendChild(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);
|
|
|
}
|
|
|
}
|
|
|
}
|