|
|
@@ -0,0 +1,295 @@
|
|
|
+/**
|
|
|
+ * Designer 实例管理 + Sheet 操作 + 数据源读写
|
|
|
+ * 管理 designer ref,暴露所有公共 sheet/designer 操作函数
|
|
|
+ */
|
|
|
+import { ref } from 'vue'
|
|
|
+import GC from '../tools/gc'
|
|
|
+import { is, changeStringToObject } from '../tools/spreadUtils'
|
|
|
+import {
|
|
|
+ getSheetBindingPathData,
|
|
|
+ generateDefaultData,
|
|
|
+ deepMergeSchemaValue,
|
|
|
+ generateAndReturnDataSourceOADate,
|
|
|
+} from '../tools/dataSourceProcessor'
|
|
|
+
|
|
|
+export function useSpreadDesigner() {
|
|
|
+ const designer = ref(null)
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 监听单元格值变化,同步其他 sheet 的 dataSource
|
|
|
+ */
|
|
|
+ function registerCellValuesChangeEventHandlerForEverySheet(sheets) {
|
|
|
+ sheets.forEach((activedSheet) => {
|
|
|
+ activedSheet.bind(GC.Spread.Sheets.Events.ValueChanged, function (sender, args) {
|
|
|
+ const bindingPathName = activedSheet.getBindingPath(args.row, args.col)
|
|
|
+ if (bindingPathName) {
|
|
|
+ try {
|
|
|
+ for (const sheet of sheets) {
|
|
|
+ const dataSource = sheet.getDataSource()?.getSource() || {}
|
|
|
+ if (
|
|
|
+ !dataSource.hasOwnProperty(bindingPathName) ||
|
|
|
+ (activedSheet.name() === sheet.name() && activedSheet.__ID__ === sheet.__ID__)
|
|
|
+ )
|
|
|
+ continue
|
|
|
+
|
|
|
+ const tables = sheet.tables?.all() || []
|
|
|
+ for (let i = 0; i < tables.length; i++) {
|
|
|
+ const table = sheet.tables.all()[i]
|
|
|
+ table.expandBoundRows(true)
|
|
|
+ }
|
|
|
+ const newDataSource = new GC.Spread.Sheets.Bindings.CellBindingSource({
|
|
|
+ ...dataSource,
|
|
|
+ [bindingPathName]: args.newValue,
|
|
|
+ })
|
|
|
+ sheet.setDataSource(newDataSource)
|
|
|
+ }
|
|
|
+ } catch (err) {
|
|
|
+ console.error('监听单元格出错啦', err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ function registerZoomEventHandler(designerInstance) {
|
|
|
+ if (!designerInstance) return
|
|
|
+ const spreadInstance = designerInstance?.getWorkbook()
|
|
|
+ if (Object.prototype.toString.call(spreadInstance) !== '[object Object]') return
|
|
|
+ spreadInstance.bind(GC.Spread.Sheets.Events.ViewZooming, function (sender, args) {
|
|
|
+ const activedSheet = spreadInstance.getActiveSheet()
|
|
|
+ const minZoom = calcSheetZoom(activedSheet)
|
|
|
+ if (args.newZoomFactor < minZoom) args.cancel = true
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ function calcSheetZoom(sheet) {
|
|
|
+ const screenWidth = window.screen.width
|
|
|
+ const usedRange = sheet.getUsedRange(GC.Spread.Sheets.UsedRangeType.style)
|
|
|
+ sheet.showCell(usedRange.row, usedRange.col)
|
|
|
+
|
|
|
+ const scrollbarWidth = 40
|
|
|
+ let totalWidth = 0
|
|
|
+ for (let col = usedRange.col; col <= usedRange.colCount - 1; col++) {
|
|
|
+ totalWidth += sheet.getColumnWidth(col)
|
|
|
+ }
|
|
|
+ return 1 + (screenWidth - scrollbarWidth - totalWidth) / totalWidth
|
|
|
+ }
|
|
|
+
|
|
|
+ function initDesignerSheetConfig(sheet) {
|
|
|
+ sheet?.zoom(1)
|
|
|
+ sheet?.zoom(calcSheetZoom(sheet))
|
|
|
+ sheet.options.rowHeaderVisible = false
|
|
|
+ sheet.options.colHeaderVisible = false
|
|
|
+ sheet.setActiveCell(null)
|
|
|
+ }
|
|
|
+
|
|
|
+ async function setDefaultSchema(designerInstance, bindingPathSchema) {
|
|
|
+ const initBindingPathSchema = !bindingPathSchema ? {} : changeStringToObject(bindingPathSchema)
|
|
|
+ await designerInstance.setData('treeNodeFromJson', JSON.stringify(initBindingPathSchema))
|
|
|
+ }
|
|
|
+
|
|
|
+ function getDefaultSchema(designerInstance) {
|
|
|
+ return (
|
|
|
+ designerInstance.getData('updatedTreeNode') ||
|
|
|
+ designerInstance.getData('treeNodeFromJson') ||
|
|
|
+ designerInstance.getData('oldTreeNodeFromJson')
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置数据到模板文件,渲染对应数据
|
|
|
+ * @param beforeSetDataSource 可选钩子,在数据合并完成后、写入 sheet 前调用。
|
|
|
+ * 接收 (spreadInstance, resultDataSource),返回处理后的 dataSource。
|
|
|
+ * 用于续页等特殊处理(spreadDesigner 传入,generic 不传)。
|
|
|
+ */
|
|
|
+ function setDataSource(designerInstance, schemaData, dataSourceValues, beforeSetDataSource) {
|
|
|
+ dataSourceValues =
|
|
|
+ !is(dataSourceValues, 'Object') && is(dataSourceValues, 'String')
|
|
|
+ ? JSON.parse(dataSourceValues)
|
|
|
+ : dataSourceValues || {}
|
|
|
+ schemaData =
|
|
|
+ !is(schemaData, 'Object') && is(schemaData, 'String')
|
|
|
+ ? JSON.parse(schemaData)
|
|
|
+ : schemaData || {}
|
|
|
+
|
|
|
+ const formatterSource = generateDefaultData(schemaData)
|
|
|
+ const resultDataSource = deepMergeSchemaValue(formatterSource, dataSourceValues)
|
|
|
+
|
|
|
+ const spreadInstance = designerInstance.getWorkbook()
|
|
|
+
|
|
|
+ // 续页等特殊处理钩子
|
|
|
+ let finalDataSource = resultDataSource
|
|
|
+ if (beforeSetDataSource) {
|
|
|
+ finalDataSource = beforeSetDataSource(spreadInstance, resultDataSource) || resultDataSource
|
|
|
+ }
|
|
|
+
|
|
|
+ registerCellValuesChangeEventHandlerForEverySheet(spreadInstance.sheets)
|
|
|
+ for (const sheet of spreadInstance.sheets) {
|
|
|
+ const tables = sheet.tables?.all() || []
|
|
|
+ for (let i = 0; i < tables.length; i++) {
|
|
|
+ const table = tables[i]
|
|
|
+ table.expandBoundRows(true)
|
|
|
+ }
|
|
|
+ const filterResultDataSource = getSheetBindingPathData(sheet, finalDataSource)
|
|
|
+ const dataSource = new GC.Spread.Sheets.Bindings.CellBindingSource(filterResultDataSource)
|
|
|
+ sheet.setDataSource(dataSource)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function getDataSource(designerInstance) {
|
|
|
+ const spreadInstance = designerInstance.getWorkbook()
|
|
|
+
|
|
|
+ let dataSource = {}
|
|
|
+ for (const sheet of spreadInstance.sheets) {
|
|
|
+ const source = sheet.getDataSource()?.getSource()
|
|
|
+ if (source) {
|
|
|
+ for (const key in source) {
|
|
|
+ if (is(source[key], 'Object') && is(dataSource[key], 'Object')) {
|
|
|
+ source[key] = Object.assign(dataSource[key], source[key])
|
|
|
+ }
|
|
|
+ }
|
|
|
+ dataSource = {
|
|
|
+ ...dataSource,
|
|
|
+ ...source,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ dataSource = generateAndReturnDataSourceOADate(dataSource)
|
|
|
+ return dataSource
|
|
|
+ }
|
|
|
+
|
|
|
+ function handleSheetTableCopyTo(designerInstance, isRowMerage) {
|
|
|
+ if (!designerInstance) return
|
|
|
+ const spreadInstance = designerInstance.getWorkbook()
|
|
|
+ for (const sheet of spreadInstance.sheets) {
|
|
|
+ const tables = sheet.tables?.all() || []
|
|
|
+
|
|
|
+ for (let tableIndex = 0; tableIndex < tables.length; tableIndex++) {
|
|
|
+ const range = tables[tableIndex].range()
|
|
|
+ const { row, rowCount, col, colCount } = range
|
|
|
+
|
|
|
+ sheet.getRange(row, col, rowCount, colCount).wordWrap(true)
|
|
|
+
|
|
|
+ if (isRowMerage) {
|
|
|
+ const firstRowSpans = []
|
|
|
+ for (let c = col; c < col + colCount; c++) {
|
|
|
+ const span = sheet.getSpan(row, c)
|
|
|
+ if (span && span.row === row) {
|
|
|
+ firstRowSpans.push({
|
|
|
+ startCol: span.col,
|
|
|
+ colCount: span.colCount,
|
|
|
+ })
|
|
|
+ c += span.colCount - 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (firstRowSpans.length === 0) {
|
|
|
+ const firstRowValues = []
|
|
|
+ for (let c = col; c < col + colCount; c++) {
|
|
|
+ firstRowValues.push(sheet.getValue(row, c))
|
|
|
+ }
|
|
|
+
|
|
|
+ let startMergeCol = 0
|
|
|
+ for (let c = 1; c <= colCount; c++) {
|
|
|
+ if (c === colCount || firstRowValues[c] !== firstRowValues[c - 1]) {
|
|
|
+ if (c - startMergeCol > 1) {
|
|
|
+ firstRowSpans.push({
|
|
|
+ startCol: col + startMergeCol,
|
|
|
+ colCount: c - startMergeCol,
|
|
|
+ })
|
|
|
+ sheet.addSpan(row, col + startMergeCol, 1, c - startMergeCol)
|
|
|
+ }
|
|
|
+ startMergeCol = c
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let r = 1; r < rowCount; r++) {
|
|
|
+ const currentRow = row + r
|
|
|
+ firstRowSpans.forEach((mergeInfo) => {
|
|
|
+ try {
|
|
|
+ const firstCellValue = sheet.getValue(currentRow, mergeInfo.startCol)
|
|
|
+ let shouldMerge = true
|
|
|
+
|
|
|
+ for (let c = 1; c < mergeInfo.colCount; c++) {
|
|
|
+ const currentCellValue = sheet.getValue(currentRow, mergeInfo.startCol + c)
|
|
|
+ if (currentCellValue !== firstCellValue) {
|
|
|
+ shouldMerge = false
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (shouldMerge) {
|
|
|
+ sheet.addSpan(currentRow, mergeInfo.startCol, 1, mergeInfo.colCount)
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.warn(
|
|
|
+ `合并单元格失败 at (${currentRow}, ${mergeInfo.startCol}):`,
|
|
|
+ error.message,
|
|
|
+ )
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let rowIndex = 1; rowIndex < rowCount; rowIndex++) {
|
|
|
+ const currentRow = row + rowIndex
|
|
|
+ sheet?.copyTo(
|
|
|
+ row,
|
|
|
+ col,
|
|
|
+ currentRow,
|
|
|
+ col,
|
|
|
+ 1,
|
|
|
+ colCount,
|
|
|
+ GC.Spread.Sheets.CopyToOptions.style | GC.Spread.Sheets.CopyToOptions.formula,
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
|
|
|
+ const currentRow = row + rowIndex
|
|
|
+ sheet?.autoFitRow(currentRow)
|
|
|
+ const currentHeight = sheet.getRowHeight(currentRow)
|
|
|
+ const newHeight = Math.max(currentHeight * 1.2, 25)
|
|
|
+ sheet.setRowHeight(currentRow, newHeight)
|
|
|
+ }
|
|
|
+ }, 100)
|
|
|
+ }
|
|
|
+
|
|
|
+ const currentTheme = sheet.currentTheme()
|
|
|
+ currentTheme.bodyFont('仿宋_GB2312')
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function removeSheetCellFocus(designerInstance) {
|
|
|
+ const spreadInstance = designerInstance.getWorkbook()
|
|
|
+ if (spreadInstance) {
|
|
|
+ spreadInstance.focus(false)
|
|
|
+ for (const sheet of spreadInstance?.sheets) {
|
|
|
+ sheet.clearSelection()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function listenActiveSheetChange(spreadInstance) {
|
|
|
+ spreadInstance.bind(GC.Spread.Sheets.Events.ActiveSheetChanged, function (sender, args) {
|
|
|
+ const newSheet = args.newSheet
|
|
|
+ initDesignerSheetConfig(newSheet)
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ designer,
|
|
|
+ registerZoomEventHandler,
|
|
|
+ calcSheetZoom,
|
|
|
+ initDesignerSheetConfig,
|
|
|
+ setDefaultSchema,
|
|
|
+ getDefaultSchema,
|
|
|
+ setDataSource,
|
|
|
+ getDataSource,
|
|
|
+ handleSheetTableCopyTo,
|
|
|
+ removeSheetCellFocus,
|
|
|
+ registerCellValuesChangeEventHandlerForEverySheet,
|
|
|
+ listenActiveSheetChange,
|
|
|
+ }
|
|
|
+}
|