|
| 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 | +``` |
0 commit comments