Appearance
实现 尺码与规格对照表 工具
分类:developer | 标签:尺码、对照、规格 服装鞋码戒指纸张尺码互查
2.1 功能说明
服装鞋码戒指纸张尺码互查
2.1.1 使用指南
功能说明
把日常最容易搞混的六类尺码规格整理成对照表:服装、鞋码、戒指、内衣、纸张、纸箱。 每个页签都支持输入自己的实测数据反查尺码,也可以直接翻表。全部数据内置,不联网。
各页签怎么用
服装尺码:先切换男装 / 女装,输入胸围(厘米)即可定位到对应的国标号型、S/M/L、美码、欧码、英码。 国标号型如「165/88A」表示适合身高 165 cm、胸围 88 cm、体型偏标准(A 型)的人。
鞋码:最可靠的方式是量脚长。把脚后跟贴墙站立,量最长脚趾到墙的距离(毫米),输入后查中国码、欧码、美码、英码。
戒指尺寸:用软尺或纸条绕手指一圈量出内周长(毫米),输入后自动匹配最接近的港码、国内码、美码、欧码。
内衣尺码:分别输入下胸围和上胸围(厘米),工具按「下胸围定号、上下胸围差定罩杯」计算,例如 75B。
纸张规格:ISO 216 标准 A / B 系列全尺寸,输入「A4」或输入毫米数都能查。
纸箱与快递:常见电商快递纸箱编号与尺寸、容积,以及文件封和快递袋规格。
关于纸张标准
ISO 216 规定 A0 面积为 1 平方米,长宽比为根号 2 比 1,沿长边对折即得下一号。 A4(210 × 297 mm)是最常用的办公纸张。B 系列以 B0 为 1000 × 1414 mm,尺寸介于相邻两个 A 号之间,常用于海报和书籍。 表中数值按标准取整,与实际裁切可能有 1 mm 以内的差异。
测量小技巧
1. 量胸围时软尺水平绕过胸部最丰满处,呼吸自然,不要勒紧;
2. 量脚长建议在傍晚进行,此时脚部略有肿胀,更接近日常穿着状态;
3. 量手指周长同样建议在下午,且避开手指发凉发胀的时候,冬夏可差半个尺码;
4. 数值正好卡在两档中间时,弹性面料的衣物选小一码,硬挺面料和鞋子选大一码。
数据说明与免责
服装数据参考国家标准 GB/T 1335 号型系列与常见国际尺码换算惯例,鞋码参考 GB/T 3293 及主流品牌换算表。 不同品牌、不同款式的版型差异非常大,尤其是欧美品牌与国内快时尚品牌之间, 同样标注 M 码实际尺寸可能相差一个码。运动鞋各品牌(如同一脚长在不同品牌间可差半码到一码)差异更明显。
本表仅供快速参考,正式下单前请以商家提供的具体尺码表和实测数据为准,重要物品建议先咨询客服。
2.2 代码实现
2.2.1 组件结构
本工具是一个基于 Vue 3 <script setup> 语法的单文件组件(SFC),统一包裹在 ToolPageShell 组件内,由它提供页面标题、工具 ID、分类与「使用指南」插槽等通用外壳;核心业务逻辑(响应式状态、计算属性、事件处理函数)全部写在 <script setup> 中,输入/输出通过 el-input、el-button 等 Element Plus 组件与用户交互,所有数据均在浏览器本地处理,不会上传服务器。
本工具目录 SizeChart/ 下除 index.vue 外,还包含以下源文件:
data.js(143 行)
2.2.2 核心逻辑概览
<script setup> 中定义的主要函数/方法:
num()inRange()clothesRowClass()shoeRowClass()ringRowClass()paperRowClass()downloadCsv()
2.2.3 关键实现代码
以下是该工具的完整 <script setup> 实现(核心代码)。模板(<template>)与样式(<style>)已省略。
vue
import { computed, ref } from 'vue'
import ToolPageShell from '@/components/ToolPageShell.vue'
import {
BRA_BANDS, BRA_CUPS, CARTONS, ENVELOPES, MEN_CLOTHES,
PAPER_A, PAPER_B, RING_SIZES, SHOE_MEN, SHOE_WOMEN, WOMEN_CLOTHES
} from './data.js'
const activeTab = ref('clothes')
const clothesGender = ref('women')
const bustInput = ref('')
const shoeGender = ref('women')
const footInput = ref('')
const ringInput = ref('')
const underBust = ref('')
const overBust = ref('')
const paperInput = ref('')
const cartonQuery = ref('')
function num (v) {
const n = parseFloat(String(v).trim())
return Number.isFinite(n) ? n : null
}
// 解析 "88-92" 这类区间
function inRange (rangeStr, value) {
const parts = rangeStr.split('-').map(Number)
if (parts.length === 2) return value >= parts[0] && value <= parts[1]
return value === parts[0]
}
// ---------- 服装 ----------
const clothesRows = computed(() => clothesGender.value === 'women' ? WOMEN_CLOTHES : MEN_CLOTHES)
const clothesHit = computed(() => {
const v = num(bustInput.value)
if (v === null) return null
return clothesRows.value.find(r => inRange(r.bust, v)) || null
})
const clothesMatch = computed(() => {
const r = clothesHit.value
if (!r) return ''
return '胸围 ' + num(bustInput.value) + ' cm 对应:国标 ' + r.cn + ' · 国际 ' + r.intl +
' · 美码 ' + r.us + ' · 欧码 ' + r.eu + ' · 英码 ' + r.uk
})
function clothesRowClass ({ row }) {
return clothesHit.value && clothesHit.value.cn === row.cn ? 'hit-row' : ''
}
// ---------- 鞋码 ----------
const shoeRows = computed(() => shoeGender.value === 'women' ? SHOE_WOMEN : SHOE_MEN)
const shoeHit = computed(() => {
const v = num(footInput.value)
if (v === null) return null
// 取脚长最接近且不小于实测值的一档,超出范围则取最接近的一档
const list = shoeRows.value
const fit = list.find(r => r.foot >= v)
if (fit) return fit
return list.reduce((best, r) => Math.abs(r.foot - v) < Math.abs(best.foot - v) ? r : best, list[0])
})
const shoeMatch = computed(() => {
const v = num(footInput.value)
const r = shoeHit.value
if (!r || v === null) return ''
const warn = (v < shoeRows.value[0].foot || v > shoeRows.value[shoeRows.value.length - 1].foot)
? '(超出本表范围,已取最接近的一档)' : ''
return '脚长 ' + v + ' mm 建议:中国码 ' + r.cn + ' · 欧码 ' + r.eu +
' · 美码 ' + r.us + ' · 英码 ' + r.uk + warn
})
function shoeRowClass ({ row }) {
return shoeHit.value && shoeHit.value.foot === row.foot ? 'hit-row' : ''
}
// ---------- 戒指 ----------
const ringHit = computed(() => {
const v = num(ringInput.value)
if (v === null) return null
return RING_SIZES.reduce((best, r) =>
Math.abs(r.circumference - v) < Math.abs(best.circumference - v) ? r : best, RING_SIZES[0])
})
const ringMatch = computed(() => {
const v = num(ringInput.value)
const r = ringHit.value
if (!r || v === null) return ''
const diff = Math.abs(r.circumference - v).toFixed(1)
return '内周长 ' + v + ' mm 最接近:港码 ' + r.hk + ' · 国内码 ' + r.cn + ' · 美码 ' + r.us +
' · 欧码 ' + r.eu + '(内径 ' + r.diameter + ' mm,相差 ' + diff + ' mm)'
})
function ringRowClass ({ row }) {
return ringHit.value && ringHit.value.hk === row.hk ? 'hit-row' : ''
}
// ---------- 内衣 ----------
const braMatch = computed(() => {
const u = num(underBust.value)
const o = num(overBust.value)
if (u === null || o === null) return { text: '', type: 'info' }
if (o <= u) return { text: '上胸围应大于下胸围,请检查输入', type: 'warning' }
const band = Math.round(u / 5) * 5
const diff = o - u
let cup = BRA_CUPS[0]
for (const c of BRA_CUPS) {
if (Math.abs(c.diff - diff) < Math.abs(cup.diff - diff)) cup = c
}
const inBand = BRA_BANDS.some(b => String(b.band) === String(band))
const tail = inBand ? '' : '(号型超出常见范围,建议到实体店试穿)'
return {
text: '下胸围 ' + u + ' cm、上胸围 ' + o + ' cm,差值 ' + diff.toFixed(1) +
' cm,推荐尺码:' + band + cup.cup + tail,
type: inBand ? 'success' : 'warning'
}
})
// ---------- 纸张 ----------
const ALL_PAPER = [...PAPER_A, ...PAPER_B]
const paperHit = computed(() => {
const q = paperInput.value.trim().toUpperCase()
if (!q) return null
const byName = ALL_PAPER.find(p => p.name === q)
if (byName) return byName
const v = num(q)
if (v === null) return null
return ALL_PAPER.reduce((best, p) => {
const d = Math.min(Math.abs(p.width - v), Math.abs(p.height - v))
const bd = Math.min(Math.abs(best.width - v), Math.abs(best.height - v))
return d < bd ? p : best
}, ALL_PAPER[0])
})
const paperMatch = computed(() => {
const p = paperHit.value
if (!p) return ''
return p.name + ':' + p.size + '(' + p.inch + ')'
})
function paperRowClass ({ row }) {
return paperHit.value && paperHit.value.name === row.name ? 'hit-row' : ''
}
// ---------- 纸箱 ----------
const cartonRows = computed(() => {
const q = cartonQuery.value.trim()
if (!q) return CARTONS
return CARTONS.filter(c => c.name.includes(q) || c.usage.includes(q) || c.size.includes(q))
})
// ---------- 导出 ----------
const clothesCols = computed(() => clothesGender.value === 'women'
? [['cn', '国标号型'], ['intl', '国际'], ['us', '美码'], ['eu', '欧码'], ['uk', '英码'], ['bust', '胸围cm'], ['waist', '腰围cm'], ['hip', '臀围cm']]
: [['cn', '国标号型'], ['intl', '国际'], ['us', '美码'], ['eu', '欧码'], ['uk', '英码'], ['bust', '胸围cm'], ['waist', '腰围cm'], ['neck', '领围cm']])
const shoeCols = [['foot', '脚长mm'], ['cn', '中国码'], ['eu', '欧码'], ['us', '美码'], ['uk', '英码']]
const ringCols = [['hk', '港码'], ['cn', '国内码'], ['us', '美码'], ['eu', '欧码'], ['circumference', '内周长mm'], ['diameter', '内径mm']]
const paperCols = [['name', '规格'], ['size', '毫米'], ['inch', '英寸']]
const cartonCols = [['name', '箱号'], ['size', '尺寸mm'], ['volume', '容积L'], ['usage', '用途']]
function downloadCsv (rows, cols, filename) {
const columns = Array.isArray(cols) ? cols : cols.value
const head = columns.map(c => c[1])
const body = rows.map(r => columns.map(c => r[c[0]]))
const csv = [head, ...body]
.map(r => r.map(v => '"' + String(v === undefined ? '' : 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 = filename + '.csv'
a.click()
URL.revokeObjectURL(a.href)
}2.3 效果截图
