init
This commit is contained in:
348
apps/web-ele/src/components/db-hst/index.vue
Normal file
348
apps/web-ele/src/components/db-hst/index.vue
Normal file
@@ -0,0 +1,348 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, reactive, ref, nextTick, createVNode, render, watch, computed } from 'vue'
|
||||
import { ElDropdown, ElDropdownMenu, ElDropdownItem } from 'element-plus'
|
||||
import Handsontable from "handsontable";
|
||||
import { HotTable } from '@handsontable/vue3'
|
||||
import { registerLanguageDictionary, zhCN } from 'handsontable/i18n'
|
||||
import { registerAllModules } from 'handsontable/registry'
|
||||
import 'handsontable/styles/handsontable.css'
|
||||
import 'handsontable/styles/ht-theme-main.css'
|
||||
registerAllModules()
|
||||
registerLanguageDictionary(zhCN)
|
||||
import { handlerDropdownRenderer } from './dropdown'
|
||||
import { handlerTableRenderer } from './table'
|
||||
import { computeCodeColWidth,codeRenderer } from './tree'
|
||||
// import { sourceDataObject } from './mockData'
|
||||
// const language = ref('zh-CN')
|
||||
defineOptions({ name: 'DbHst' });
|
||||
const componentProps = defineProps<{ settings?: any }>()
|
||||
// 导入和注册插件和单元格类型
|
||||
// import { registerCellType, NumericCellType } from 'handsontable/cellTypes';
|
||||
// import { registerPlugin, UndoRedo } from 'handsontable/plugins';
|
||||
// registerCellType(NumericCellType);
|
||||
// registerPlugin(UndoRedo);
|
||||
// const tableHeight = computed(() => componentProps.height ?? 0)
|
||||
// const defaultData = ref<any[][]>(Array.from({ length: 30 }, () => Array(componentProps.columns?.length ?? 0).fill('')))
|
||||
const hotTableComponent = ref<any>(null)
|
||||
|
||||
const codeColWidth = ref<number>(120)
|
||||
|
||||
|
||||
// const colHeaders = ref<string[]>([])
|
||||
let defaultSettings = {
|
||||
themeName: 'ht-theme-main',
|
||||
language: 'zh-CN',
|
||||
// data: sourceDataObject,
|
||||
// colWidths: [100, 120, 100, 100, 100, 100],
|
||||
// rowHeights: [30, 30, 30, 30, 30, 30],
|
||||
colWidths: 120, // 固定列宽
|
||||
// colWidths(index) {
|
||||
// return (index + 1) * 40;
|
||||
// },
|
||||
// colWidths: undefined,
|
||||
rowHeights: '23px', // 固定行高
|
||||
wordWrap: false,// 禁止单元格内容自动换行
|
||||
|
||||
//manualColumnMove: true,
|
||||
manualColumnResize: true,
|
||||
autoRowSize: false,
|
||||
autoColumnSize: false,
|
||||
|
||||
renderAllRows: false,
|
||||
viewportColumnRenderingOffset: 12,//渲染列数
|
||||
viewportRowRenderingOffset: 12,//渲染行数
|
||||
// colHeaders: componentProps.colHeaders ?? [],
|
||||
rowHeaders: false,
|
||||
// columns: componentProps.columns ?? [],
|
||||
autoWrapRow: true,
|
||||
autoWrapCol: true,
|
||||
width: '100%',
|
||||
// height: 'auto',
|
||||
// height: tableHeight.value,
|
||||
// height: 200,
|
||||
// stretchH: 'all',
|
||||
// loading: true,
|
||||
//contextMenu: true,
|
||||
// dialog: true,
|
||||
// dialog: {
|
||||
// content: 'This dialog can be controlled programmatically.',
|
||||
// closable: true,
|
||||
// contentBackground: true,
|
||||
// background: 'semi-transparent',
|
||||
// },
|
||||
licenseKey: '424fc-f3b67-5905b-a191b-9b809',
|
||||
// 如果使用第一行作为列头(colHeaders: false),添加以下配置
|
||||
// cells: function(row: number, col: number) {
|
||||
// const cellProperties: any = {};
|
||||
|
||||
// // 如果 colHeaders 为 false,将第一行设置为列头样式
|
||||
// if (row === 0) {
|
||||
// cellProperties.readOnly = true; // 不可编辑
|
||||
// cellProperties.className = 'custom-header-row'; // 自定义样式类
|
||||
// cellProperties.renderer = function(instance: any, td: HTMLTableCellElement, row: number, col: number, prop: any, value: any, cellProperties: any) {
|
||||
// Handsontable.renderers.TextRenderer.apply(this, arguments as any);
|
||||
// td.style.fontWeight = 'bold';
|
||||
// td.style.backgroundColor = '#f5f5f5';
|
||||
// td.style.textAlign = 'center';
|
||||
// td.style.borderBottom = '2px solid #ddd';
|
||||
// };
|
||||
// }
|
||||
|
||||
// return cellProperties;
|
||||
// },
|
||||
modifyColWidth: (width: number, col: number) => {
|
||||
const hot = hotInstance.value
|
||||
if (!hot) return width
|
||||
const codeCol = hot.propToCol('code')
|
||||
// console.log('modifyColWidth',codeCol,width)
|
||||
return col === codeCol ? (codeColWidth.value ?? width) : width
|
||||
},
|
||||
afterChange: (changes: any, source: string) => {
|
||||
if (!changes || !hotInstance.value) return
|
||||
if (source !== 'edit' && source !== 'Autofill' && source !== 'UndoRedo') return
|
||||
const hot = hotInstance.value
|
||||
const codeCol = hot.propToCol('code')
|
||||
const hasCodeEdit = changes.some((c: any) => c && (c[1] === 'code' || c[1] === codeCol))
|
||||
// console.log('afterChange',changes,hasCodeEdit, codeCol)
|
||||
if (!hasCodeEdit) return
|
||||
codeColWidth.value = computeCodeColWidth(hot)
|
||||
// console.log('afterChange',codeColWidth.value)
|
||||
hot.render()
|
||||
// console.log('afterChange',codeColWidth.value)
|
||||
},
|
||||
}
|
||||
// 合并外部 settings 和默认配置
|
||||
let hotSettings = {}
|
||||
// 保留必要的回调函数
|
||||
|
||||
const hotInstance = ref<any>(null)
|
||||
|
||||
onMounted(() => {
|
||||
hotInstance.value = hotTableComponent.value?.hotInstance
|
||||
})
|
||||
onUnmounted(() => {
|
||||
})
|
||||
|
||||
watch(
|
||||
() => componentProps.settings,
|
||||
(newSettings) => {
|
||||
if (!newSettings) return
|
||||
const merged = {
|
||||
...defaultSettings,
|
||||
...newSettings,
|
||||
}
|
||||
Object.assign(hotSettings, merged)
|
||||
hotSettings = merged
|
||||
// console.log(merged)
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const loadData = (rows: any[][]) => {
|
||||
|
||||
if (!hotInstance.value) return
|
||||
// hotInstance.value.loadData(rows.length === 0?defaultData.value:rows)
|
||||
hotInstance.value.loadData(rows)
|
||||
console.log('Source Data:', hotInstance.value.getSourceData());
|
||||
}
|
||||
|
||||
const updateCodeColWidth = () => {
|
||||
if (!hotInstance.value) return
|
||||
codeColWidth.value = computeCodeColWidth(hotInstance.value)
|
||||
hotInstance.value.render()
|
||||
}
|
||||
defineExpose({ loadData, hotTableComponent, hotInstance, updateCodeColWidth, codeColWidth })
|
||||
|
||||
|
||||
Handsontable.renderers.registerRenderer("db-table", handlerTableRenderer);
|
||||
Handsontable.renderers.registerRenderer("db-dropdown", handlerDropdownRenderer);
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<hot-table ref="hotTableComponent" :settings="hotSettings"></hot-table>
|
||||
<!-- <div id="hot-dialog-container" style="display:none">
|
||||
<div class="ht-dialog-content">
|
||||
<h3>执行操作</h3>
|
||||
<el-table :data="gridData">
|
||||
<el-table-column property="date" label="Date" width="150" />
|
||||
<el-table-column property="name" label="Name" width="200" />
|
||||
<el-table-column property="address" label="Address" />
|
||||
</el-table>
|
||||
<div style="margin-top:12px;display:flex;gap:8px;justify-content:flex-end">
|
||||
<button class="el-button el-button--default el-button--small btn-cancel">取消</button>
|
||||
<button class="el-button el-button--primary el-button--small btn-ok">确定</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<!-- <el-popover
|
||||
ref="popoverRef"
|
||||
:virtual-ref="popoverButtonRef"
|
||||
v-model:visible="isPopoverOpen"
|
||||
trigger="manual"
|
||||
virtual-triggering
|
||||
width="auto"
|
||||
>
|
||||
<el-table :data="gridData" size="small" @row-click="onClickOutside" border>
|
||||
<el-table-column width="150" property="date" label="date" />
|
||||
<el-table-column width="100" property="name" label="name" />
|
||||
<el-table-column width="300" property="address" label="address" />
|
||||
</el-table>
|
||||
</el-popover> -->
|
||||
</template>
|
||||
|
||||
<style lang="css">
|
||||
/* 禁止单元格内容换行 */
|
||||
.handsontable td {
|
||||
white-space: nowrap !important;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
/* 自定义滚动条样式 */
|
||||
/* .ht_master .wtHolder::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
.ht_master .wtHolder::-webkit-scrollbar-thumb {
|
||||
background-color: #888;
|
||||
border-radius: 6px;
|
||||
border: 3px solid #ffffff;
|
||||
}
|
||||
|
||||
.ht_master .wtHolder::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #555;
|
||||
} */
|
||||
|
||||
.ht_master .wtHolder{
|
||||
/* overflow: hidden !important; */
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #a6a8ac #ecf0f1;
|
||||
}
|
||||
/* dropdown */
|
||||
.ht-cell-dropdown { display: flex; align-items: center; justify-content: space-between; width: 100%; position: relative; box-sizing: border-box; height: 100%; }
|
||||
.ht-cell-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.ht-cell-value:empty::after { content: "\200b"; }
|
||||
.ht-cell-caret { position: absolute; right: 0; top: 50%; transform: translateY(-50%); width: 0; height: 0; border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 5px solid #979797; }
|
||||
.ht-dropdown-menu { position: absolute; background: #fff; border: 1px solid var(--el-border-color-light); border-radius: 8px; box-shadow: 0 10px 20px rgba(0,0,0,0.1); min-width: 160px; max-height:300px; overflow: auto; z-index: 10000; }
|
||||
.ht-dropdown-menu.is-bottom { margin-top: 4px; }
|
||||
.ht-dropdown-menu.is-top { margin-bottom: 4px; }
|
||||
.ht-dropdown-item { padding: 8px 12px; cursor: pointer; font-size: 13px; }
|
||||
.ht-dropdown-item:hover { background-color: #f5f7fa; }
|
||||
.ht-dropdown-item.is-selected { background-color: #eef3ff; }
|
||||
.ht-dropdown-item.is-disabled { color: #c0c4cc; cursor: not-allowed; }
|
||||
/* tree */
|
||||
.handsontable .text-relative span.ht_nestingLevel_empty{
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 5px;
|
||||
height: 1px;
|
||||
order: -2;
|
||||
}
|
||||
.handsontable .text-relative span:last-child {
|
||||
padding-left: calc(var(--ht-icon-size) + 5px);
|
||||
}
|
||||
/* table */
|
||||
|
||||
/* 自定义下拉渲染样式 */
|
||||
.hot-cell-dropdown { display: flex; align-items: center; gap: 6px; padding: 0 6px; }
|
||||
.hot-dropdown-display { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.hot-dropdown-text { font-size: 12px; color: #1f2328; }
|
||||
.hot-dropdown-placeholder { font-size: 12px; color: #a0a0a0; }
|
||||
|
||||
.hot-dropdown-trigger {
|
||||
margin-left: auto;
|
||||
border: none;
|
||||
background: transparent;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
font-size: 0;
|
||||
}
|
||||
.hot-dropdown-trigger::after {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
-webkit-mask-size: contain;
|
||||
-webkit-mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cpath d='m21 21-4.35-4.35'%3E%3C/path%3E%3C/svg%3E");
|
||||
background-color: currentColor;
|
||||
}
|
||||
.hot-dropdown-trigger::after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.hot-dropdown { position: fixed; z-index: 10000; background: #fff; border: 1px solid #e5e7eb; box-shadow: 0 8px 24px rgba(0,0,0,0.12); border-radius: 6px; max-height: 260px; overflow: hidden; will-change: top, left; display: flex; flex-direction: column; }
|
||||
.hot-dropdown--up { border-top-left-radius: 6px; border-top-right-radius: 6px; }
|
||||
.hot-dropdown--down { border-bottom-left-radius: 6px; border-bottom-right-radius: 6px; }
|
||||
.hot-dropdown-search { padding: 0px; border-bottom: 1px solid #e5e7eb; background: #fff; position: sticky; top: 0; z-index: 1; }
|
||||
.hot-dropdown-search-input { width: 100%; padding: 6px 10px; border: 1px solid #d1d5db; border-radius: 4px; font-size: 12px; outline: none; transition: border-color 0.2s; }
|
||||
.hot-dropdown-search-input:focus { border-color: #3b82f6; }
|
||||
.hot-dropdown-table-wrapper { overflow: auto; flex: 1; }
|
||||
.hot-dropdown-table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
||||
.hot-dropdown-table thead th { position: sticky; top: 0; background: #f9fafb; font-weight: 600; color: #374151; padding: 8px; border-bottom: 1px solid #e5e7eb; text-align: left; }
|
||||
.hot-dropdown-table tbody td { padding: 8px; border-bottom: 1px solid #f3f4f6; color: #374151; }
|
||||
.hot-dropdown-row { cursor: pointer; }
|
||||
.hot-dropdown-row:hover { background: #f3f4f6; }
|
||||
|
||||
/** 指引线line */
|
||||
tbody[role="rowgroup"] tr[role="row"] td .ht_nestingLevel_empty{
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 5px;
|
||||
height: 1px;
|
||||
order: -2;
|
||||
}
|
||||
tbody[role="rowgroup"] tr[role="row"] td .ht_nestingLevel_empty:last-child {
|
||||
padding-left: calc(var(--ht-icon-size) + 5px);
|
||||
}
|
||||
tbody[role="rowgroup"] tr[role="row"] td .ht_nestingLevel_empty::before{
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -13px;
|
||||
height: 26px;
|
||||
width: 1px;
|
||||
border-left: 1px solid #ababab;
|
||||
}
|
||||
tbody[role="rowgroup"] tr[role="row"] td .ht_nestingLevel_empty::after{
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
width: 16px;
|
||||
height: 1px;
|
||||
border-top: 1px solid #ababab;
|
||||
}
|
||||
tbody[role="rowgroup"] tr[role="row"] td .ht_nestingLevel_empty{
|
||||
padding-left: 7px;
|
||||
}
|
||||
|
||||
/* 最后一个 ht_nestingLevel_empty(rowHeader 前面的那个) */
|
||||
tbody[role="rowgroup"] tr[role="row"] td .ht_nestingLevel_empty + .rowHeader {
|
||||
/* 通过相邻选择器反向选择 */
|
||||
padding-left: 10px !important
|
||||
}
|
||||
/* 选择后面还有 ht_nestingLevel_empty 的元素(不是最后一个) */
|
||||
tbody[role="rowgroup"] tr[role="row"] td .ht_nestingLevel_empty:has(+ .ht_nestingLevel_empty)::before {
|
||||
/* 你的样式 */
|
||||
/* height: 0px; */
|
||||
}
|
||||
/* 选择后面还有 ht_nestingLevel_empty 的元素(不是最后一个) */
|
||||
tbody[role="rowgroup"] tr[role="row"] td .ht_nestingLevel_empty:has(+ .ht_nestingLevel_empty)::after {
|
||||
/* 你的样式 */
|
||||
width: 0px !important;
|
||||
}
|
||||
|
||||
/* 或者用这个:选择后面不是 ht_nestingLevel_empty 的那个 */
|
||||
tbody[role="rowgroup"] tr[role="row"] td .ht_nestingLevel_empty:not(:has(+ .ht_nestingLevel_empty))::before {
|
||||
/* height: 13px; */
|
||||
}
|
||||
|
||||
tbody[role="rowgroup"] tr[role="row"] td .ht_nestingLevel_empty:not(:has(+ .ht_nestingLevel_empty))::after {
|
||||
/* 你的特殊样式 */
|
||||
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user