feat(web-admin): 列表 Excel 查看改为 Univer 只读在线表格
点击工具栏打开可筛选、复制的只读弹层,替换原行列点选复制模式,且不写回业务数据。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
43e240ad6a
commit
aad74505c0
10 files changed
+1901
-374
No files matched your search
@@ -1,11 +1,13 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { describe, it } from 'node:test'
|
||||
import {
|
||||
buildExcelSnapshot,
|
||||
buildExcelTsv,
|
||||
excelCell,
|
||||
isCopyableColumn,
|
||||
isRowSelectorColumn,
|
||||
resolveCopyAxes,
|
||||
snapshotToUniverCellData,
|
||||
toggleAxis
|
||||
} from './list-excel-copy'
|
||||
|
||||
@@ -135,3 +137,45 @@ describe('buildExcelTsv', () => {
|
||||
assert.equal(tsv, '联系人\t状态\r\n张三\t合作中\r\n李四\t跟进中')
|
||||
})
|
||||
})
|
||||
|
||||
describe('buildExcelSnapshot', () => {
|
||||
it('keeps copyable columns, cleans cells, and drops 操作', () => {
|
||||
const snapshot = buildExcelSnapshot({
|
||||
columns: [
|
||||
{ id: 'name', label: ' 联系人\n' },
|
||||
{ id: 'act', label: '操作' },
|
||||
{ id: 'status', label: '状态' }
|
||||
],
|
||||
rowValues: [
|
||||
['张三\t甲', '编辑', '合作中'],
|
||||
['李四', '删除', '跟进中']
|
||||
]
|
||||
})
|
||||
assert.deepEqual(snapshot.headers, ['联系人', '状态'])
|
||||
assert.deepEqual(snapshot.rows, [
|
||||
['张三 甲', '合作中'],
|
||||
['李四', '跟进中']
|
||||
])
|
||||
})
|
||||
|
||||
it('returns empty snapshot when no copyable columns', () => {
|
||||
const snapshot = buildExcelSnapshot({
|
||||
columns: [{ id: 'a', label: '操作' }],
|
||||
rowValues: [['x']]
|
||||
})
|
||||
assert.deepEqual(snapshot, { headers: [], rows: [] })
|
||||
})
|
||||
})
|
||||
|
||||
describe('snapshotToUniverCellData', () => {
|
||||
it('puts headers on row 0 and data from row 1', () => {
|
||||
const cellData = snapshotToUniverCellData({
|
||||
headers: ['A', 'B'],
|
||||
rows: [['1', '2']]
|
||||
})
|
||||
assert.deepEqual(cellData[0][0], { v: 'A', t: 1 })
|
||||
assert.deepEqual(cellData[0][1], { v: 'B', t: 1 })
|
||||
assert.deepEqual(cellData[1][0], { v: '1', t: 1 })
|
||||
assert.deepEqual(cellData[1][1], { v: '2', t: 1 })
|
||||
})
|
||||
})
|
||||
@@ -68,6 +68,54 @@ export function buildExcelTsv(headers: string[], rows: string[][]): string {
|
||||
return lines.join('\r\n')
|
||||
}
|
||||
|
||||
/** 灌入只读在线表格的快照:表头 + 当前页文本矩阵 */
|
||||
export type ListExcelSnapshot = {
|
||||
headers: string[]
|
||||
rows: string[][]
|
||||
}
|
||||
|
||||
/**
|
||||
* 从可复制列与行文本构建快照。headers/rows 均经 excelCell 清洗。
|
||||
* rowValues 与 columns 按下标对齐;操作列等会被跳过。
|
||||
* 无可复制列时返回 { headers: [], rows: [] }。
|
||||
*/
|
||||
export function buildExcelSnapshot(input: {
|
||||
columns: ListExcelColumn[]
|
||||
/** 与 columns 按下标对齐的行文本;通常来自表格 DOM textContent */
|
||||
rowValues: string[][]
|
||||
}): ListExcelSnapshot {
|
||||
const indices: number[] = []
|
||||
const headers: string[] = []
|
||||
input.columns.forEach((col, index) => {
|
||||
if (!col.id || !isCopyableColumn(col)) return
|
||||
indices.push(index)
|
||||
headers.push(excelCell(col.label))
|
||||
})
|
||||
if (!indices.length) return { headers: [], rows: [] }
|
||||
const rows = input.rowValues.map((row) =>
|
||||
indices.map((colIndex) => excelCell(row[colIndex] ?? ''))
|
||||
)
|
||||
return { headers, rows }
|
||||
}
|
||||
|
||||
/** 将快照转为 Univer cellData(第 0 行为表头)。 */
|
||||
export function snapshotToUniverCellData(snapshot: ListExcelSnapshot): Record<
|
||||
number,
|
||||
Record<number, { v: string; t: number }>
|
||||
> {
|
||||
const cellData: Record<number, Record<number, { v: string; t: number }>> = {}
|
||||
const writeRow = (r: number, values: string[]) => {
|
||||
const row: Record<number, { v: string; t: number }> = {}
|
||||
values.forEach((value, c) => {
|
||||
row[c] = { v: value, t: 1 }
|
||||
})
|
||||
cellData[r] = row
|
||||
}
|
||||
writeRow(0, snapshot.headers)
|
||||
snapshot.rows.forEach((row, index) => writeRow(index + 1, row))
|
||||
return cellData
|
||||
}
|
||||
|
||||
function range(count: number): number[] {
|
||||
return Array.from({ length: count }, (_, index) => index)
|
||||
}
|
||||
Reference in new issue
Block a user