103 lines
3.1 KiB
QML
103 lines
3.1 KiB
QML
|
|
// ExcelTableView.qml
|
|||
|
|
import QtQuick 2.15
|
|||
|
|
import QtQuick.Controls 2.15
|
|||
|
|
import QtQuick.Layouts 1.15
|
|||
|
|
import Excel 1.0
|
|||
|
|
|
|||
|
|
Item {
|
|||
|
|
id: root
|
|||
|
|
|
|||
|
|
// 公共属性
|
|||
|
|
property var dataModel: [] // 表格数据
|
|||
|
|
property string filePath: "" // Excel文件路径
|
|||
|
|
property var sheet: null // 工作表(索引或名称)
|
|||
|
|
property bool autoLoad: true // 文件路径变化时自动加载
|
|||
|
|
property color headerColor: "#f0f0f0" // 表头背景色
|
|||
|
|
property color cellColor: "white" // 单元格背景色
|
|||
|
|
property int cellPadding: 8 // 单元格内边距
|
|||
|
|
property int headerFontSize: 12 // 表头字体大小
|
|||
|
|
property int cellFontSize: 11 // 单元格字体大小
|
|||
|
|
|
|||
|
|
// 信号
|
|||
|
|
signal dataChanged(var newData) // 数据修改时触发
|
|||
|
|
signal loadCompleted() // 数据加载完成时触发
|
|||
|
|
|
|||
|
|
// 表格尺寸
|
|||
|
|
property real rowHeight: 40
|
|||
|
|
property real colWidth: 120
|
|||
|
|
|
|||
|
|
// 私有属性
|
|||
|
|
QtObject {
|
|||
|
|
id: internal
|
|||
|
|
property int columns: 0
|
|||
|
|
property int rows: 0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 自动加载机制
|
|||
|
|
onFilePathChanged: if(autoLoad) loadData()
|
|||
|
|
|
|||
|
|
// 加载Excel数据
|
|||
|
|
function loadData() {
|
|||
|
|
var result = Excel.read(filePath, sheet)
|
|||
|
|
if(result.length > 0) {
|
|||
|
|
dataModel = result
|
|||
|
|
processDataModel()
|
|||
|
|
loadCompleted()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理数据模型
|
|||
|
|
function processDataModel() {
|
|||
|
|
internal.rows = dataModel.length
|
|||
|
|
internal.columns = dataModel[0] ? dataModel[0].length : 0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 保存数据到Excel
|
|||
|
|
function saveToExcel(path) {
|
|||
|
|
return Excel.write(path || filePath, dataModel)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 表格布局
|
|||
|
|
Flickable {
|
|||
|
|
anchors.fill: parent
|
|||
|
|
contentWidth: colWidth * internal.columns
|
|||
|
|
contentHeight: rowHeight * internal.rows
|
|||
|
|
clip: true
|
|||
|
|
|
|||
|
|
Grid {
|
|||
|
|
columns: internal.columns
|
|||
|
|
rows: internal.rows
|
|||
|
|
spacing: 1
|
|||
|
|
|
|||
|
|
Repeater {
|
|||
|
|
model: dataModel
|
|||
|
|
|
|||
|
|
// 单元格模板
|
|||
|
|
Rectangle {
|
|||
|
|
width: colWidth
|
|||
|
|
height: rowHeight
|
|||
|
|
color: (index < internal.columns) ? headerColor : cellColor
|
|||
|
|
|
|||
|
|
TextInput {
|
|||
|
|
anchors.fill: parent
|
|||
|
|
anchors.margins: cellPadding
|
|||
|
|
text: modelData ? modelData : ""
|
|||
|
|
font.pixelSize: (index < internal.columns) ? headerFontSize : cellFontSize
|
|||
|
|
verticalAlignment: Text.AlignVCenter
|
|||
|
|
|
|||
|
|
onTextChanged: {
|
|||
|
|
if(index >= internal.columns) {
|
|||
|
|
// 更新数据模型
|
|||
|
|
var row = Math.floor(index/internal.columns)
|
|||
|
|
var col = index % internal.columns
|
|||
|
|
dataModel[row][col] = text
|
|||
|
|
dataChanged(dataModel)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|