Appearance
实现 化学元素周期表 工具
分类:general | 标签:元素、周期表、化学 交互式元素周期表,点击查看详情
2.1 功能说明
交互式元素周期表,点击查看详情
2.1.1 使用指南
功能说明
标准 18 列长式元素周期表,收录全部 {{ ELEMENTS.length }} 种化学元素(原子序数 1–118)。 点击任意元素方块可查看详细属性;顶部搜索框支持按元素符号、中文名、英文名、原子序数检索; 右侧类别图例可按元素类别筛选。全部数据内置于页面,不联网、不上传。
怎么用
1. 直接点方块看详情:包含相对原子质量、周期/族、电子排布式、分层电子数、常见化合价、熔沸点与密度等;
2. 搜索框输入「Fe」「铁」「26」「Iron」都能定位到铁,命中元素高亮、其余淡出;
3. 点击类别图例(如「过渡金属」)只看该类元素,再点一次取消;
4. 「显示电子排布」开关会把方块下方的相对原子质量换成外层电子排布,方便对照记忆;
5. 详情卡片右下角可复制该元素信息,工具栏可把当前筛选结果导出为 CSV。
关于表格布局
采用国际纯粹与应用化学联合会(IUPAC)推荐的 18 列格式,族号 1–18 标注在顶部,周期 1–7 标注在左侧。 镧系(57–71)与锕系(89–103)按惯例抽出,单独排在主表下方两行,主表第 3 族对应位置以占位格标出。 镧系与锕系元素不单独编族号,详情中族号显示为「镧系」「锕系」。
数据来源与精度
元素英文名、相对原子质量、熔点、沸点、密度、发现者、类别与表格坐标来自公开数据集 Bowserinator/Periodic-Table-JSON(CC BY-SA 3.0)。 中文名称、族号、电子排布式、分层电子数、常见化合价由本工具在此基础上补充生成: 电子排布式按构造原理(Madelung 能级顺序)推导,并对铬、铜、铌、钼、钌、铑、钯、银、镧、铈、钆、铂、金、锕、钍、镤、铀、镎、锔、铹等 20 种基态构型反常的元素做了修正;分层电子数由电子排布式反推,已校验各元素电子总数与原子序数一致。
相对原子质量取 IUPAC 常用值,多数保留 3–4 位有效数字;放射性元素(如锝、钷及 84 号以后元素) 通常没有稳定同位素,其数值为最长寿命同位素的质量数,仅供参考。
使用限制
104–118 号超重元素半衰期极短,熔点、沸点、密度多为理论预测或缺测,表中留空; 其化合价标注为「预测」。这些元素的中文名为全国科学技术名词审定委员会公布的用字, 属较生僻汉字,若设备字体缺失可能显示为方框,此时可参考元素符号与英文名。
本工具面向教学与速查场景,涉及科研计算、工业配方或考试作答时,请以教材和权威手册的数值为准。
2.2 代码实现
2.2.1 组件结构
本工具是一个基于 Vue 3 <script setup> 语法的单文件组件(SFC),统一包裹在 ToolPageShell 组件内,由它提供页面标题、工具 ID、分类与「使用指南」插槽等通用外壳;核心业务逻辑(响应式状态、计算属性、事件处理函数)全部写在 <script setup> 中,输入/输出通过 el-input、el-button 等 Element Plus 组件与用户交互,所有数据均在浏览器本地处理,不会上传服务器。
2.2.2 核心逻辑概览
<script setup> 中定义的主要函数/方法:
normalizeCategory()colorOf()select()formatMass()outerConfig()groupText()phaseText()tempText()densityText()elementText()copyCurrent()downloadCsv()
2.2.3 关键实现代码
以下是该工具的完整 <script setup> 实现(核心代码)。模板(<template>)与样式(<style>)已省略。
vue
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import ToolPageShell from '@/components/ToolPageShell.vue'
import DATA from './data.json'
const ELEMENTS = DATA.elements
// 类别配色(浅色主题)
const CATEGORY_LIST = [
{ key: 'alkali metal', label: '碱金属', bg: '#fff1f0', border: '#f5695c' },
{ key: 'alkaline earth metal', label: '碱土金属', bg: '#fff5e6', border: '#f0932b' },
{ key: 'transition metal', label: '过渡金属', bg: '#fff8e1', border: '#e0af2b' },
{ key: 'post-transition metal', label: '主族后金属', bg: '#eef7ee', border: '#5aa469' },
{ key: 'metalloid', label: '准金属', bg: '#e9f7f4', border: '#2fa392' },
{ key: 'diatomic nonmetal', label: '双原子非金属', bg: '#eaf3ff', border: '#3883fa' },
{ key: 'polyatomic nonmetal', label: '多原子非金属', bg: '#eef0ff', border: '#5c6bc0' },
{ key: 'noble gas', label: '稀有气体', bg: '#f5edff', border: '#8e5cd9' },
{ key: 'lanthanide', label: '镧系元素', bg: '#fdeef7', border: '#d4589a' },
{ key: 'actinide', label: '锕系元素', bg: '#fdeef2', border: '#c1436d' },
{ key: 'unknown', label: '性质未知', bg: '#f4f4f5', border: '#909399' }
]
const PLACEHOLDERS = [
{ row: 7, label: '57-71' },
{ row: 8, label: '89-103' }
]
const COLOR_MAP = {}
for (const c of CATEGORY_LIST) COLOR_MAP[c.key] = c
const UNKNOWN_COLOR = COLOR_MAP.unknown
const query = ref('')
const activeCategory = ref('')
const showConfig = ref(false)
const drawerVisible = ref(false)
const current = ref(null)
function normalizeCategory (raw) {
return raw.startsWith('unknown') ? 'unknown' : raw
}
function colorOf (el) {
return COLOR_MAP[normalizeCategory(el.category)] || UNKNOWN_COLOR
}
const categoryCount = computed(() => {
const map = {}
for (const el of ELEMENTS) {
const k = normalizeCategory(el.category)
map[k] = (map[k] || 0) + 1
}
return map
})
const filtered = computed(() => {
const q = query.value.trim().toLowerCase()
return ELEMENTS.filter(el => {
if (activeCategory.value && normalizeCategory(el.category) !== activeCategory.value) return false
if (!q) return true
return el.symbol.toLowerCase().includes(q) ||
el.name.includes(q) ||
el.nameEn.toLowerCase().includes(q) ||
String(el.number) === q ||
el.categoryCn.includes(q)
})
})
const isFiltering = computed(() => !!query.value.trim() || !!activeCategory.value)
const matchedIds = computed(() => new Set(filtered.value.map(el => el.number)))
function select (el) {
current.value = el
drawerVisible.value = true
}
function formatMass (m) {
if (m === null || m === undefined) return '—'
// 放射性元素质量数多为整数,稳定元素保留 3 位小数
return Number.isInteger(m) ? String(m) : m.toFixed(m < 10 ? 4 : 3).replace(/0+$/, '').replace(/\.$/, '')
}
// 方块上展示的外层电子排布(去掉稀有气体芯)
function outerConfig (el) {
return el.electron_configuration.replace(/^\[\w+\]\s*/, '')
}
function groupText (el) {
if (el.group) return '第 ' + el.group + ' 族'
return el.category === 'lanthanide' ? '镧系' : '锕系'
}
function phaseText (p) {
return { Gas: '气态', Solid: '固态', Liquid: '液态' }[p] || '未知'
}
function tempText (k) {
if (k === null || k === undefined) return '暂无数据'
return (k - 273.15).toFixed(1) + ' °C(' + k + ' K)'
}
function densityText (el) {
if (el.density === null || el.density === undefined) return '暂无数据'
return el.density + (el.phase === 'Gas' ? ' g/L' : ' g/cm³')
}
function elementText (el) {
return [
el.number + ' ' + el.symbol + ' ' + el.name + '(' + el.nameEn + ')',
'类别:' + el.categoryCn,
'相对原子质量:' + formatMass(el.atomic_mass),
'周期/族:第 ' + el.period + ' 周期 · ' + groupText(el),
'电子排布式:' + el.electron_configuration,
'完整电子排布:' + el.electron_configuration_full,
'分层电子数:' + el.shells.join(', '),
'常见化合价:' + el.common_oxidation_states,
'常温状态:' + phaseText(el.phase),
'密度:' + densityText(el),
'熔点:' + tempText(el.melt),
'沸点:' + tempText(el.boil)
].join('\n')
}
async function copyCurrent () {
if (!current.value) return
const text = elementText(current.value)
try {
await navigator.clipboard.writeText(text)
ElMessage.success('已复制 ' + current.value.name + ' 的信息')
} catch {
ElMessage.warning('复制失败,请手动选择复制')
}
}
function downloadCsv () {
const head = ['原子序数', '符号', '中文名', '英文名', '相对原子质量', '周期', '族', '类别', '电子排布式', '常见化合价']
const rows = filtered.value.map(el => [
el.number, el.symbol, el.name, el.nameEn, formatMass(el.atomic_mass),
el.period, groupText(el), el.categoryCn, el.electron_configuration, el.common_oxidation_states
])
const csv = [head, ...rows]
.map(r => r.map(v => '"' + String(v).replace(/"/g, '""') + '"').join(','))
.join('\n')
const blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8' })
const a = document.createElement('a')
a.href = URL.createObjectURL(blob)
a.download = '元素周期表.csv'
a.click()
URL.revokeObjectURL(a.href)
}2.3 效果截图
