Skip to content

Commit 1c31a9b

Browse files
authored
Merge pull request #5108 from william-xue/feat/2093-personnel-info-demo
feat(demo): add personnel information sheet with phone masking #2093
2 parents 6a69ff1 + 6bd6d23 commit 1c31a9b

3 files changed

Lines changed: 343 additions & 0 deletions

File tree

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
---
2+
category: examples
3+
group: Business
4+
title: Personnel Information Sheet
5+
cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/list-table.png
6+
link: custom_define/custom_icon
7+
---
8+
9+
# Personnel Information Sheet
10+
11+
Display personnel information with phone numbers masked by default (showing only first 3 and last 4 digits). Click the eye icon to toggle displaying the full phone number.
12+
13+
## Key Configuration
14+
15+
- `fieldFormat` formats phone numbers: shows `138****5678` by default
16+
- `VTable.register.icon` registers eye-open / eye-close icons with `visibleTime: 'always'` to always show the toggle button
17+
- `click_cell` event listens for icon clicks, toggles per-row visibility state, and calls `updateRecords` to refresh the cell
18+
19+
## Code Demo
20+
21+
```javascript livedemo template=vtable
22+
// Register "show full number" icon (eye open)
23+
VTable.register.icon('eye-open', {
24+
type: 'svg',
25+
name: 'eye-open',
26+
width: 16,
27+
height: 16,
28+
positionType: VTable.TYPES.IconPosition.right,
29+
marginLeft: 4,
30+
cursor: 'pointer',
31+
visibleTime: 'always',
32+
tooltip: {
33+
title: 'Click to hide number',
34+
placement: VTable.TYPES.Placement.top
35+
},
36+
svg: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#666" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
37+
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
38+
<circle cx="12" cy="12" r="3"/>
39+
</svg>`
40+
});
41+
42+
// Register "hide number" icon (eye closed)
43+
VTable.register.icon('eye-close', {
44+
type: 'svg',
45+
name: 'eye-close',
46+
width: 16,
47+
height: 16,
48+
positionType: VTable.TYPES.IconPosition.right,
49+
marginLeft: 4,
50+
cursor: 'pointer',
51+
visibleTime: 'always',
52+
tooltip: {
53+
title: 'Click to show number',
54+
placement: VTable.TYPES.Placement.top
55+
},
56+
svg: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
57+
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"/>
58+
<path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/>
59+
<line x1="1" y1="1" x2="23" y2="23"/>
60+
</svg>`
61+
});
62+
63+
// Mask phone number: keep first 3 and last 4 digits, replace the rest with ****
64+
function maskPhone(phone) {
65+
if (!phone || phone.length < 7) return phone;
66+
return phone.slice(0, 3) + '****' + phone.slice(-4);
67+
}
68+
69+
// Sample personnel data
70+
const records = [
71+
{ id: '001', name: 'Zhang Wei', department: 'R&D', position: 'Senior Engineer', phone: '13812345678', email: 'zhangwei@company.com', location: 'Beijing' },
72+
{ id: '002', name: 'Li Na', department: 'Product', position: 'Product Manager', phone: '13987654321', email: 'lina@company.com', location: 'Shanghai' },
73+
{ id: '003', name: 'Wang Fang', department: 'Design', position: 'UI Designer', phone: '15012349876', email: 'wangfang@company.com', location: 'Shenzhen' },
74+
{ id: '004', name: 'Zhao Lei', department: 'Operations', position: 'Operations Specialist', phone: '18611112222', email: 'zhaolei@company.com', location: 'Guangzhou' },
75+
{ id: '005', name: 'Chen Jing', department: 'HR', position: 'HR Manager', phone: '13711113333', email: 'chenjing@company.com', location: 'Hangzhou' },
76+
{ id: '006', name: 'Liu Yang', department: 'R&D', position: 'Frontend Engineer', phone: '15888889999', email: 'liuyang@company.com', location: 'Beijing' },
77+
{ id: '007', name: 'Zhou Chao', department: 'Sales', position: 'Sales Director', phone: '13666667777', email: 'zhouchao@company.com', location: 'Chengdu' },
78+
{ id: '008', name: 'Wu Min', department: 'Finance', position: 'Finance Supervisor', phone: '18900001111', email: 'wumin@company.com', location: 'Wuhan' },
79+
{ id: '009', name: 'Zheng Hao', department: 'R&D', position: 'Backend Engineer', phone: '13544445555', email: 'zhenghao@company.com', location: "Xi'an" },
80+
{ id: '010', name: 'Sun Li', department: 'Marketing', position: 'Marketing Specialist', phone: '17722223333', email: 'sunli@company.com', location: 'Nanjing' }
81+
];
82+
83+
// Track per-row phone number visibility
84+
const phoneVisible = {};
85+
86+
let tableInstance;
87+
88+
const columns = [
89+
{
90+
field: 'id',
91+
title: 'ID',
92+
width: 70,
93+
style: { textAlign: 'center', color: '#666' }
94+
},
95+
{
96+
field: 'name',
97+
title: 'Name',
98+
width: 120
99+
},
100+
{
101+
field: 'department',
102+
title: 'Department',
103+
width: 120
104+
},
105+
{
106+
field: 'position',
107+
title: 'Position',
108+
width: 160
109+
},
110+
{
111+
field: 'phone',
112+
title: 'Phone Number',
113+
width: 190,
114+
// Show masked or full number based on per-row visibility state
115+
fieldFormat(record) {
116+
const rowId = record.id;
117+
return phoneVisible[rowId] ? record.phone : maskPhone(record.phone);
118+
},
119+
// Show eye-open when full number is visible, eye-close when masked
120+
icon(args) {
121+
const rowId = args.record?.id;
122+
return phoneVisible[rowId] ? 'eye-open' : 'eye-close';
123+
}
124+
},
125+
{
126+
field: 'email',
127+
title: 'Email',
128+
width: 210
129+
},
130+
{
131+
field: 'location',
132+
title: 'City',
133+
width: 100,
134+
style: { textAlign: 'center' }
135+
}
136+
];
137+
138+
const option = {
139+
records,
140+
columns,
141+
widthMode: 'standard',
142+
frozenColCount: 1,
143+
theme: VTable.themes.ARCO
144+
};
145+
146+
tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option);
147+
window['tableInstance'] = tableInstance;
148+
149+
// Listen for icon clicks to toggle phone number visibility per row
150+
tableInstance.on('click_cell', args => {
151+
const { col, row, targetIcon } = args;
152+
if (!targetIcon) return;
153+
if (targetIcon.name === 'eye-open' || targetIcon.name === 'eye-close') {
154+
const record = tableInstance.getCellOriginRecord(col, row);
155+
if (!record) return;
156+
const rowId = record.id;
157+
// Toggle visibility for this row
158+
phoneVisible[rowId] = !phoneVisible[rowId];
159+
// Refresh the row to reflect the new state
160+
tableInstance.updateRecords([record], [row - tableInstance.columnHeaderLevelCount]);
161+
}
162+
});
163+
```

docs/assets/demo/menu.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1854,7 +1854,24 @@
18541854
"zh": "产品对比表",
18551855
"en": "Product Compare Table"
18561856
}
1857+
},
1858+
{
1859+
"path": "personnel-info",
1860+
"title": {
1861+
"zh": "人员信息表",
1862+
"en": "Personnel Information Sheet"
1863+
},
1864+
"meta": {
1865+
"title": "Personnel Information Sheet",
1866+
"keywords": "personnel, phone mask, privacy, icon, click",
1867+
"category": "demo",
1868+
"group": "Business",
1869+
"cover": "https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/list-table.png",
1870+
"link": "'../../guide/custom_define/custom_icon'",
1871+
"option": ""
1872+
}
18571873
}
1874+
18581875
]
18591876
}
18601877
]
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
---
2+
category: examples
3+
group: Business
4+
title: 人员信息表
5+
cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/list-table.png
6+
link: custom_define/custom_icon
7+
---
8+
9+
# 人员信息表
10+
11+
展示人员信息,电话号码默认脱敏显示(部分隐藏),点击眼睛图标可切换显示完整号码。
12+
13+
## 关键配置
14+
15+
- `fieldFormat` 格式化电话号码,默认只显示前三位和后四位,中间用 `****` 替换
16+
- `VTable.register.icon` 注册显示/隐藏图标,通过 `visibleTime: 'always'` 常驻显示
17+
- `click_cell` 事件监听图标点击,切换该行的电话号码显示状态并调用 `updateRecords` 刷新
18+
19+
## 代码演示
20+
21+
```javascript livedemo template=vtable
22+
// 注册"显示完整号码"图标(眼睛张开)
23+
VTable.register.icon('eye-open', {
24+
type: 'svg',
25+
name: 'eye-open',
26+
width: 16,
27+
height: 16,
28+
positionType: VTable.TYPES.IconPosition.right,
29+
marginLeft: 4,
30+
cursor: 'pointer',
31+
visibleTime: 'always',
32+
tooltip: {
33+
title: '点击隐藏号码',
34+
placement: VTable.TYPES.Placement.top
35+
},
36+
svg: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#666" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
37+
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
38+
<circle cx="12" cy="12" r="3"/>
39+
</svg>`
40+
});
41+
42+
// 注册"隐藏号码"图标(眼睛闭合)
43+
VTable.register.icon('eye-close', {
44+
type: 'svg',
45+
name: 'eye-close',
46+
width: 16,
47+
height: 16,
48+
positionType: VTable.TYPES.IconPosition.right,
49+
marginLeft: 4,
50+
cursor: 'pointer',
51+
visibleTime: 'always',
52+
tooltip: {
53+
title: '点击显示号码',
54+
placement: VTable.TYPES.Placement.top
55+
},
56+
svg: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
57+
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"/>
58+
<path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/>
59+
<line x1="1" y1="1" x2="23" y2="23"/>
60+
</svg>`
61+
});
62+
63+
// 脱敏电话号码:保留前3位和后4位,中间替换为 ****
64+
function maskPhone(phone) {
65+
if (!phone || phone.length < 7) return phone;
66+
return phone.slice(0, 3) + '****' + phone.slice(-4);
67+
}
68+
69+
// 模拟人员数据
70+
const records = [
71+
{ id: '001', name: '张伟', department: '研发部', position: '高级工程师', phone: '13812345678', email: 'zhangwei@company.com', location: '北京' },
72+
{ id: '002', name: '李娜', department: '产品部', position: '产品经理', phone: '13987654321', email: 'lina@company.com', location: '上海' },
73+
{ id: '003', name: '王芳', department: '设计部', position: 'UI设计师', phone: '15012349876', email: 'wangfang@company.com', location: '深圳' },
74+
{ id: '004', name: '赵磊', department: '运营部', position: '运营专员', phone: '18611112222', email: 'zhaolei@company.com', location: '广州' },
75+
{ id: '005', name: '陈静', department: '人力资源部', position: 'HR经理', phone: '13711113333', email: 'chenjing@company.com', location: '杭州' },
76+
{ id: '006', name: '刘洋', department: '研发部', position: '前端工程师', phone: '15888889999', email: 'liuyang@company.com', location: '北京' },
77+
{ id: '007', name: '周超', department: '销售部', position: '销售总监', phone: '13666667777', email: 'zhouchao@company.com', location: '成都' },
78+
{ id: '008', name: '吴敏', department: '财务部', position: '财务主管', phone: '18900001111', email: 'wumin@company.com', location: '武汉' },
79+
{ id: '009', name: '郑浩', department: '研发部', position: '后端工程师', phone: '13544445555', email: 'zhenghao@company.com', location: '西安' },
80+
{ id: '010', name: '孙丽', department: '市场部', position: '市场专员', phone: '17722223333', email: 'sunli@company.com', location: '南京' }
81+
];
82+
83+
// 记录每行的电话号码展开状态
84+
const phoneVisible = {};
85+
86+
let tableInstance;
87+
88+
const columns = [
89+
{
90+
field: 'id',
91+
title: '工号',
92+
width: 70,
93+
style: { textAlign: 'center', color: '#666' }
94+
},
95+
{
96+
field: 'name',
97+
title: '姓名',
98+
width: 90
99+
},
100+
{
101+
field: 'department',
102+
title: '部门',
103+
width: 120
104+
},
105+
{
106+
field: 'position',
107+
title: '职位',
108+
width: 130
109+
},
110+
{
111+
field: 'phone',
112+
title: '电话号码',
113+
width: 180,
114+
// 根据展开状态决定显示脱敏号码还是完整号码
115+
fieldFormat(record) {
116+
const rowId = record.id;
117+
return phoneVisible[rowId] ? record.phone : maskPhone(record.phone);
118+
},
119+
// 根据展开状态动态切换图标
120+
icon(args) {
121+
const rowId = args.record?.id;
122+
return phoneVisible[rowId] ? 'eye-open' : 'eye-close';
123+
}
124+
},
125+
{
126+
field: 'email',
127+
title: '邮箱',
128+
width: 200
129+
},
130+
{
131+
field: 'location',
132+
title: '所在城市',
133+
width: 100,
134+
style: { textAlign: 'center' }
135+
}
136+
];
137+
138+
const option = {
139+
records,
140+
columns,
141+
widthMode: 'standard',
142+
frozenColCount: 1,
143+
theme: VTable.themes.ARCO
144+
};
145+
146+
tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option);
147+
window['tableInstance'] = tableInstance;
148+
149+
// 监听图标点击事件,切换电话号码的显示状态
150+
tableInstance.on('click_cell', args => {
151+
const { col, row, targetIcon } = args;
152+
if (!targetIcon) return;
153+
if (targetIcon.name === 'eye-open' || targetIcon.name === 'eye-close') {
154+
const record = tableInstance.getCellOriginRecord(col, row);
155+
if (!record) return;
156+
const rowId = record.id;
157+
// 切换该行的显示状态
158+
phoneVisible[rowId] = !phoneVisible[rowId];
159+
// 刷新该行数据
160+
tableInstance.updateRecords([record], [row - tableInstance.columnHeaderLevelCount]);
161+
}
162+
});
163+
```

0 commit comments

Comments
 (0)