AppML 案例 Employees
员工管理案例的完整实现:模型、数据、页面与日期处理。
数据
[
{ "id": 101, "name": "张伟", "title": "后端工程师", "dept": "技术部", "hiredate": "2021-03-15", "email": "zhangwei@example.com" },
{ "id": 102, "name": "李娜", "title": "产品经理", "dept": "产品部", "hiredate": "2019-08-01", "email": "lina@example.com" },
{ "id": 103, "name": "王强", "title": "测试工程师", "dept": "技术部", "hiredate": "2023-01-09", "email": "wangqiang@example.com" },
{ "id": 104, "name": "赵敏", "title": "设计师", "dept": "设计部", "hiredate": "2022-11-21", "email": "zhaomin@example.com" }
]Store dates as ISO YYYY-MM-DD so string order matches chronological order and timezones cannot shift them. With 2021/3/15 style, sorting compares strings and 2021/11/1 lands before 2021/3/15.
模型
<appml>
<datasource>
<query>data/employees.json</query>
</datasource>
<html>
<title>员工管理</title>
</html>
<items>
<item name="id" label="工号" type="number" primarykey="true" readonly="true"/>
<item name="name" label="姓名" required="true" maxlength="20"/>
<item name="title" label="职位" required="true" maxlength="30"/>
<item name="dept" label="部门" default="技术部"/>
<item name="hiredate" label="入职日期" type="date" required="true"/>
<item name="email" label="邮箱" type="email"/>
</items>
</appml>页面
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>员工管理</title>
<script src="js/appml.js"></script>
<style>
body { font-family: system-ui, sans-serif; margin: 2rem; color: #1f2937; }
table { border-collapse: collapse; min-width: 640px; }
th, td { border: 1px solid #d1d5db; padding: .45rem .8rem; }
th { background: #f3f4f6; text-align: left; }
td:nth-child(1), td:nth-child(5) { text-align: right; }
</style>
</head>
<body>
<h1>员工管理</h1>
<appml-app src="models/employees.xml">
<appml-view view="list"></appml-view>
<appml-view view="form"></appml-view>
</appml-app>
</body>
</html>工号 姓名 职位 部门 入职日期 邮箱
101 张伟 后端工程师 技术部 2021-03-15 zhangwei@example.com
102 李娜 产品经理 产品部 2019-08-01 lina@example.com
103 王强 测试工程师 技术部 2023-01-09 wangqiang@example.com
104 赵敏 设计师 设计部 2022-11-21 zhaomin@example.com按部门分组
function employeesController(appml) {
appml.onload = function () {
const groups = {};
appml.data.forEach((e) => {
(groups[e.dept] = groups[e.dept] || []).push(e);
});
console.log(groups);
// { 技术部: [...], 产品部: [...], 设计部: [...] }
};
}AppML has no grouped view; aggregation from appml.data is up to you. Rendering a grouped UI means abandoning templates and building DOM yourself — a typical limit of the framework.
工龄计算
function yearsOfService(hiredate) {
const start = new Date(hiredate);
const now = new Date();
let years = now.getFullYear() - start.getFullYear();
const beforeAnniversary =
now.getMonth() < start.getMonth() ||
(now.getMonth() === start.getMonth() && now.getDate() < start.getDate());
if (beforeAnniversary) years -= 1;
return years;
}Dividing a millisecond difference by 365 days is wrong across leap years and year boundaries. Compute the year difference and subtract one if this year's anniversary has not passed.
new Date('2021-03-15') parses as UTC midnight — 08:00 in UTC+8, and in some older browsers as local UTC-8 time, off by a day. Parse with split('-') when the exact date matters.完整目录
appml-employees/
├── index.html
├── js/appml.js
├── models/employees.xml
└── data/employees.jsonFour files and it runs. In a real project the data comes from an API: point <query> at a URL returning JSON and nothing else changes.
数据
[
{ "id": 101, "name": "张伟", "title": "后端工程师", "dept": "技术部", "hiredate": "2021-03-15", "email": "zhangwei@example.com" },
{ "id": 102, "name": "李娜", "title": "产品经理", "dept": "产品部", "hiredate": "2019-08-01", "email": "lina@example.com" },
{ "id": 103, "name": "王强", "title": "测试工程师", "dept": "技术部", "hiredate": "2023-01-09", "email": "wangqiang@example.com" },
{ "id": 104, "name": "赵敏", "title": "设计师", "dept": "设计部", "hiredate": "2022-11-21", "email": "zhaomin@example.com" }
]日期用 YYYY-MM-DD 的 ISO 格式存。这样字符串排序和日期排序结果一致,跨时区也不会错——用 2021/3/15 这种写法,排序会变成按字符串比,2021/11/1 会排在 2021/3/15 前面。
模型
<appml>
<datasource>
<query>data/employees.json</query>
</datasource>
<html>
<title>员工管理</title>
</html>
<items>
<item name="id" label="工号" type="number" primarykey="true" readonly="true"/>
<item name="name" label="姓名" required="true" maxlength="20"/>
<item name="title" label="职位" required="true" maxlength="30"/>
<item name="dept" label="部门" default="技术部"/>
<item name="hiredate" label="入职日期" type="date" required="true"/>
<item name="email" label="邮箱" type="email"/>
</items>
</appml>页面
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>员工管理</title>
<script src="js/appml.js"></script>
<style>
body { font-family: system-ui, sans-serif; margin: 2rem; color: #1f2937; }
table { border-collapse: collapse; min-width: 640px; }
th, td { border: 1px solid #d1d5db; padding: .45rem .8rem; }
th { background: #f3f4f6; text-align: left; }
td:nth-child(1), td:nth-child(5) { text-align: right; }
</style>
</head>
<body>
<h1>员工管理</h1>
<appml-app src="models/employees.xml">
<appml-view view="list"></appml-view>
<appml-view view="form"></appml-view>
</appml-app>
</body>
</html>工号 姓名 职位 部门 入职日期 邮箱
101 张伟 后端工程师 技术部 2021-03-15 zhangwei@example.com
102 李娜 产品经理 产品部 2019-08-01 lina@example.com
103 王强 测试工程师 技术部 2023-01-09 wangqiang@example.com
104 赵敏 设计师 设计部 2022-11-21 zhaomin@example.com按部门分组
function employeesController(appml) {
appml.onload = function () {
const groups = {};
appml.data.forEach((e) => {
(groups[e.dept] = groups[e.dept] || []).push(e);
});
console.log(groups);
// { 技术部: [...], 产品部: [...], 设计部: [...] }
};
}AppML 没有分组视图,只能自己从 appml.data 聚合。想渲染分组界面就得放弃模板,自己生成 DOM——这是框架能力边界的典型例子。
工龄计算
function yearsOfService(hiredate) {
const start = new Date(hiredate);
const now = new Date();
let years = now.getFullYear() - start.getFullYear();
const beforeAnniversary =
now.getMonth() < start.getMonth() ||
(now.getMonth() === start.getMonth() && now.getDate() < start.getDate());
if (beforeAnniversary) years -= 1;
return years;
}直接相减两个时间戳再除以 365 天会错:闰年、跨年日期都会偏。按「年份差,再看今年是否已过入职月日」计算才准确。
new Date('2021-03-15') 会被当成 UTC 零点解析,在 UTC+8 下是当天 08:00;某些旧浏览器会当成 UTC-8 的本地时间,导致差一天。需要精确日期时自己 split('-') 解析。完整目录
appml-employees/
├── index.html
├── js/appml.js
├── models/employees.xml
└── data/employees.json四个文件就能跑起来。真实项目里数据来自接口,把 <query> 换成一个返回 JSON 的 URL 即可,其余不动。