專案架構概覽
- 前端:ExtJS 3.x(GridPanel、FormPanel、Window、PagingToolbar)
- 後端:PHP + MySQL(
index.php 列表、add.php 新增、edit.php 修改、del.php 刪除、search.php 搜尋)
- 資料格式:JSON(
{totalProperty: N, root: [...]})
前端核心代碼結構
1. 資料模型與 Store
var record = [
{name: 'id'}, {name: 'name'}, {name: 'pass'},
{name: 'sex'}, {name: 'email'}
];
var reader = new Ext.data.JsonReader({
totalProperty: 'totalProperty', root: 'root'
}, record);
var store = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({url: 'index.php'}),
reader: reader
});
2. GridPanel 欄位定義
var cm = new Ext.grid.ColumnModel([
new Ext.grid.RowNumberer(),
new Ext.grid.CheckboxSelectionModel(),
{header: 'ID', dataIndex: 'id', width: 50, sortable: true},
{header: '用戶名', dataIndex: 'name', width: 80, sortable: true},
{header: '密碼', dataIndex: 'pass', width: 175, sortable: true},
{header: '性別', dataIndex: 'sex', width: 145, sortable: true, renderer: renderSex},
{header: '電子郵箱', dataIndex: 'email', width: 150, sortable: true}
]);
function renderSex(v) {
return v === 'male'
? "<span style='color:red;font-weight:bold;'>男</span>"
: "<span style='color:green;font-weight:bold;'>女</span>";
}
3. 分頁工具列
var pageSize = 16;
var pagingBar = new Ext.PagingToolbar({
store: store, pageSize: pageSize,
displayInfo: true,
displayMsg: '顯示第 {0} 條到第 {1} 條,共 {2} 條',
emptyMsg: '無資料'
});
4. 新增/修改視窗與表單驗證
var formPanel = new Ext.form.FormPanel({
frame: true, labelAlign: 'right', labelWidth: 55,
items: [
{xtype: 'hidden', name: 'id'},
{xtype: 'textfield', fieldLabel: '用戶名', name: 'name', allowBlank: false},
{xtype: 'radiogroup', fieldLabel: '性別', columns: 2, allowBlank: false,
items: [{boxLabel: '男', name: 'sex', inputValue: 'male'},
{boxLabel: '女', name: 'sex', inputValue: 'female'}]},
{xtype: 'textfield', fieldLabel: '密碼', name: 'pass', allowBlank: false},
{xtype: 'textarea', fieldLabel: '電子郵箱', name: 'email', allowBlank: false}
]
});
function addUser() {
var win = new Ext.Window({
title: '新增用戶', width: 350, height: 250, modal: true,
items: [formPanel],
buttons: [{
text: '保存', handler: function() {
formPanel.getForm().submit({
url: 'add.php', method: 'POST',
waitMsg: '保存中...',
success: function() {
Ext.Msg.alert('提示', '添加成功!');
win.close();
store.reload({params: {start: 0, limit: pageSize}});
},
failure: function(form, action) {
Ext.Msg.alert('提示', action.result.errors.reason);
}
});
}
}, {text: '重置', handler: function() { formPanel.getForm().reset(); }}]
});
formPanel.getForm().reset();
win.show();
}
5. 刪除確認與 AJAX 請求
function removeUser() {
var record = grid.getSelectionModel().getSelected();
if (!record) { Ext.Msg.alert('提示', '請先選擇記錄!'); return; }
Ext.Msg.confirm('確認', '確定刪除?', function(btn) {
if (btn !== 'yes') return;
Ext.Ajax.request({
url: 'del.php', method: 'POST',
params: {id: record.data.id},
success: function(resp) {
var r = Ext.decode(resp.responseText);
if (r) { Ext.Msg.alert('提示', '刪除成功'); store.reload(); }
else { Ext.Msg.alert('提示', '刪除失敗'); }
}
});
});
}
後端 PHP 範例 (index.php)
<?php
header('Content-Type: application/json; charset=utf-8');
$link = mysqli_connect('localhost', 'root', '123456', 'stu');
mysqli_set_charset($link, 'utf8');
$start = (int)$_POST['start'];
$limit = (int)$_POST['limit'];
$total = mysqli_fetch_row(mysqli_query($link, 'SELECT COUNT(*) FROM men'))[0];
$res = mysqli_query($link, "SELECT * FROM men LIMIT $start, $limit");
$data = [];
while ($row = mysqli_fetch_assoc($res)) $data[] = $row;
echo json_encode(['totalProperty' => $total, 'root' => $data]);
?>
資料庫結構 (men.sql)
CREATE TABLE `men` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL,
`pass` VARCHAR(32) NOT NULL,
`sex` VARCHAR(10) NOT NULL,
`email` VARCHAR(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
關鍵優化提醒
- 後端務必使用 Prepared Statements 防 SQL 注入。
- 密碼存儲請用
password_hash() / password_verify(),不可明文。
- ExtJS 3.x 已停止維護,新專案建議使用 ExtJS 7+ 或 Vue/React + Element Plus/Ant Design。
- JSON 輸出建議設定
JSON_UNESCAPED_UNICODE 避免中文被轉義。