Skip to content

Commit

Permalink
feat: add captcha internationalization
Browse files Browse the repository at this point in the history
  • Loading branch information
Squall2017 committed Sep 13, 2024
1 parent d95633a commit c55375d
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { CaptchaCardProps } from './types';
import { computed } from 'vue';
import { $t } from '@vben/locales';
import {
Card,
CardContent,
Expand Down Expand Up @@ -45,7 +46,12 @@ function handleClick(e: MouseEvent) {
<Card :style="rootStyles" aria-labelledby="captcha-title" role="region">
<CardHeader class="p-0">
<CardTitle id="captcha-title" class="flex items-center justify-between">
<span>{{ title }}</span>
<template v-if="$slots.title">
<slot name="title">{{ $t('captcha.title') }}</slot>
</template>
<template v-else>
<span>{{ title }}</span>
</template>
<div class="flex items-center justify-end">
<slot name="extra"></slot>
</div>
Expand All @@ -54,9 +60,9 @@ function handleClick(e: MouseEvent) {
<CardContent class="relative mt-2 flex w-full overflow-hidden rounded p-0">
<img
v-show="captchaImage"
:alt="$t('captcha.alt')"
:src="captchaImage"
:style="captchaStyles"
alt="验证码图片(支持img标签src属性值)"
class="relative z-10"
@click="handleClick"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { CaptchaPoint, PointSelectionCaptchaProps } from './types';
import { ref } from 'vue';
import { RotateCw } from '@vben/icons';
import { $t } from '@vben/locales';
import { VbenButton, VbenIconButton } from '@vben-core/shadcn-ui';
import { CaptchaCard } from '.';
Expand All @@ -15,7 +16,7 @@ const props = withDefaults(defineProps<PointSelectionCaptchaProps>(), {
paddingX: '12px',
paddingY: '16px',
showConfirm: false,
title: '请完成安全验证',
title: '',
width: '300px',
});
Expand All @@ -26,7 +27,7 @@ const emit = defineEmits<{
}>();
if (!props.hintImage && !props.hintText) {
throw new Error('必须提供提示图片或提示文本中的至少一个');
throw new Error('At least one of hint image or hint text must be provided');
}
const points = ref<CaptchaPoint[]>([]);
Expand Down Expand Up @@ -130,33 +131,37 @@ function handleConfirm() {
:width="width"
@click="handleClick"
>
<template #title>
<slot name="title">{{ $t('captcha.title') }}</slot>
</template>

<template #extra>
<VbenIconButton
aria-label="刷新验证码"
:aria-label="$t('captcha.refreshAriaLabel')"
class="ml-1"
@click="handleRefresh"
>
<RotateCw class="size-5" />
</VbenIconButton>
<VbenButton
v-if="showConfirm"
aria-label="确认选择"
:aria-label="$t('captcha.confirmAriaLabel')"
class="ml-2"
size="sm"
@click="handleConfirm"
>
确认
{{ $t('captcha.confirm') }}
</VbenButton>
</template>

<div
v-for="(point, index) in points"
:key="index"
:aria-label="$t('captcha.pointAriaLabel') + (index + 1)"
:style="{
top: `${point.y - POINT_OFFSET}px`,
left: `${point.x - POINT_OFFSET}px`,
}"
aria-label="点击点 {{ index + 1 }}"
class="bg-primary text-primary-50 border-primary-50 absolute z-20 flex h-5 w-5 cursor-default items-center justify-center rounded-full border-2"
role="button"
>
Expand All @@ -165,15 +170,15 @@ function handleConfirm() {
<template #footer>
<img
v-if="hintImage"
:alt="$t('captcha.alt')"
:src="hintImage"
alt="提示图片(支持img标签src属性值)"
class="h-10 w-full rounded border border-solid border-slate-200"
/>
<div
v-else-if="hintText"
class="flex h-10 w-full items-center justify-center rounded border border-solid border-slate-200"
>
{{ `请依次点击` + `${hintText}` }}
{{ `${$t('captcha.clickInOrder')}` + `${hintText}` }}
</div>
</template>
</CaptchaCard>
Expand Down
9 changes: 9 additions & 0 deletions packages/locales/src/langs/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -311,5 +311,14 @@
"sidebarToggle": "Enable Sidebar Toggle",
"lockScreen": "Enable Lock Screen"
}
},
"captcha": {
"alt": "Supports img tag src attribute value",
"title": "Please complete the security verification",
"refreshAriaLabel": "Refresh captcha",
"confirmAriaLabel": "Confirm selection",
"confirm": "Confirm",
"pointAriaLabel": "Click point",
"clickInOrder": "Please click in order"
}
}
9 changes: 9 additions & 0 deletions packages/locales/src/langs/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -311,5 +311,14 @@
"sidebarToggle": "启用侧边栏切换",
"lockScreen": "启用锁屏"
}
},
"captcha": {
"alt": "支持img标签src属性值",
"title": "请完成安全验证",
"refreshAriaLabel": "刷新验证码",
"confirmAriaLabel": "确认选择",
"confirm": "确认",
"pointAriaLabel": "点击点",
"clickInOrder": "请依次点击"
}
}
22 changes: 21 additions & 1 deletion playground/src/locales/langs/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,27 @@
"custom": "Custom Component"
},
"captcha": {
"title": "Captcha"
"title": "Captcha",
"captchaCardTitle": "Please complete the security verification",
"pageDescription": "Verify user identity by clicking on specific locations in the image.",
"pageTitle": "Captcha Component Example",
"basic": "Basic Usage",
"titlePlaceholder": "Captcha Title Text",
"captchaImageUrlPlaceholder": "Captcha Image (supports img tag src attribute value)",
"hintImage": "Hint Image",
"hintText": "Hint Text",
"hintImagePlaceholder": "Hint Image (supports img tag src attribute value)",
"hintTextPlaceholder": "Hint Text",
"showConfirm": "Show Confirm",
"hideConfirm": "Hide Confirm",
"widthPlaceholder": "Captcha Image Width Default 300px",
"heightPlaceholder": "Captcha Image Height Default 220px",
"paddingXPlaceholder": "Horizontal Padding Default 12px",
"paddingYPlaceholder": "Vertical Padding Default 16px",
"index": "Index:",
"timestamp": "Timestamp:",
"x": "x:",
"y": "y:"
}
}
}
Expand Down
22 changes: 21 additions & 1 deletion playground/src/locales/langs/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,27 @@
"custom": "自定义组件"
},
"captcha": {
"title": "验证码"
"title": "验证码",
"captchaCardTitle": "请完成安全验证",
"pageDescription": "通过点击图片中的特定位置来验证用户身份。",
"pageTitle": "验证码组件示例",
"basic": "基本使用",
"titlePlaceholder": "验证码标题文案",
"captchaImageUrlPlaceholder": "验证码图片(支持img标签src属性值)",
"hintImage": "提示图片",
"hintText": "提示文本",
"hintImagePlaceholder": "提示图片(支持img标签src属性值)",
"hintTextPlaceholder": "提示文本",
"showConfirm": "展示确认",
"hideConfirm": "隐藏确认",
"widthPlaceholder": "验证码图片宽度 默认300px",
"heightPlaceholder": "验证码图片高度 默认220px",
"paddingXPlaceholder": "水平内边距 默认12px",
"paddingYPlaceholder": "垂直内边距 默认16px",
"index": "索引:",
"timestamp": "时间戳:",
"x": "x:",
"y": "y:"
}
}
}
Expand Down
59 changes: 37 additions & 22 deletions playground/src/views/examples/captcha/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { Page, PointSelectionCaptcha } from '@vben/common-ui';
import { Card, Input, InputNumber, message, Switch } from 'ant-design-vue';
import { $t } from '#/locales';
import { captchaImage, hintImage } from './base64';
const selectedPoints = ref<CaptchaPoint[]>([]);
Expand All @@ -21,14 +23,15 @@ const params = reactive({
paddingY: undefined,
showConfirm: true,
showHintImage: true,
title: '请完成安全验证',
title: '',
width: undefined,
});
const handleConfirm = (points: CaptchaPoint[], clear: () => void) => {
message.success({
content: `captcha points: ${JSON.stringify(points)}`,
});
clear();
selectedPoints.value = [];
};
const handleRefresh = () => {
selectedPoints.value = [];
Expand All @@ -40,56 +43,56 @@ const handleClick = (point: CaptchaPoint) => {

<template>
<Page
description="通过点击图片中的特定位置来验证用户身份。"
title="验证码组件示例"
:description="$t('page.examples.captcha.pageDescription')"
:title="$t('page.examples.captcha.pageTitle')"
>
<Card class="mb-4" title="基本使用">
<Card :title="$t('page.examples.captcha.basic')" class="mb-4">
<div class="mb-3 flex items-center justify-start">
<Input
v-model:value="params.title"
:placeholder="$t('page.examples.captcha.titlePlaceholder')"
class="w-64"
placeholder="验证码标题文案"
/>
<Input
v-model:value="params.captchaImageUrl"
:placeholder="$t('page.examples.captcha.captchaImageUrlPlaceholder')"
class="ml-8 w-64"
placeholder="验证码图片(支持img标签src属性值)"
/>
<div class="ml-8 flex w-96 items-center">
<Switch
v-model:checked="params.showHintImage"
checked-children="提示图片"
:checked-children="$t('page.examples.captcha.hintImage')"
:un-checked-children="$t('page.examples.captcha.hintText')"
class="mr-4 w-40"
un-checked-children="提示文本"
/>
<Input
v-show="params.showHintImage"
v-model:value="params.hintImageUrl"
placeholder="提示图片(支持img标签src属性值)"
:placeholder="$t('page.examples.captcha.hintImagePlaceholder')"
/>
<Input
v-show="!params.showHintImage"
v-model:value="params.hintText"
placeholder="提示文本"
:placeholder="$t('page.examples.captcha.hintTextPlaceholder')"
/>
</div>

<Switch
v-model:checked="params.showConfirm"
checked-children="展示确认"
:checked-children="$t('page.examples.captcha.showConfirm')"
:un-checked-children="$t('page.examples.captcha.hideConfirm')"
class="ml-8 w-28"
un-checked-children="隐藏确认"
/>
</div>
<div class="mb-3 flex items-center justify-start">
<div>
<InputNumber
v-model:value="params.width"
:min="1"
:placeholder="$t('page.examples.captcha.widthPlaceholder')"
:precision="0"
:step="1"
class="w-64"
placeholder="验证码图片宽度 默认300px"
>
<template #addonAfter>px</template>
</InputNumber>
Expand All @@ -98,10 +101,10 @@ const handleClick = (point: CaptchaPoint) => {
<InputNumber
v-model:value="params.height"
:min="1"
:placeholder="$t('page.examples.captcha.heightPlaceholder')"
:precision="0"
:step="1"
class="w-64"
placeholder="验证码图片高度 默认220px"
>
<template #addonAfter>px</template>
</InputNumber>
Expand All @@ -110,10 +113,10 @@ const handleClick = (point: CaptchaPoint) => {
<InputNumber
v-model:value="params.paddingX"
:min="1"
:placeholder="$t('page.examples.captcha.paddingXPlaceholder')"
:precision="0"
:step="1"
class="w-64"
placeholder="水平内边距 默认12px"
>
<template #addonAfter>px</template>
</InputNumber>
Expand All @@ -122,10 +125,10 @@ const handleClick = (point: CaptchaPoint) => {
<InputNumber
v-model:value="params.paddingY"
:min="1"
:placeholder="$t('page.examples.captcha.paddingYPlaceholder')"
:precision="0"
:step="1"
class="w-64"
placeholder="垂直内边距 默认16px"
>
<template #addonAfter>px</template>
</InputNumber>
Expand All @@ -142,19 +145,31 @@ const handleClick = (point: CaptchaPoint) => {
:padding-x="params.paddingX"
:padding-y="params.paddingY"
:show-confirm="params.showConfirm"
:title="params.title"
:width="params.width || 300"
class="float-left"
@click="handleClick"
@confirm="handleConfirm"
@refresh="handleRefresh"
/>
>
<template #title>
{{ params.title || $t('page.examples.captcha.captchaCardTitle') }}
</template>
</PointSelectionCaptcha>

<ol class="float-left p-5">
<li v-for="point in selectedPoints" :key="point.i" class="flex">
<span class="mr-3 w-16">索引:{{ point.i }}</span>
<span class="mr-3 w-44">时间戳:{{ point.t }}</span>
<span class="mr-3 w-16">x:{{ point.x }}</span>
<span class="mr-3 w-16">y:{{ point.y }}</span>
<span class="mr-3 w-16">{{
$t('page.examples.captcha.index') + point.i
}}</span>
<span class="mr-3 w-44">{{
$t('page.examples.captcha.timestamp') + point.t
}}</span>
<span class="mr-3 w-16">{{
$t('page.examples.captcha.x') + point.x
}}</span>
<span class="mr-3 w-16">{{
$t('page.examples.captcha.y') + point.y
}}</span>
</li>
</ol>
</Card>
Expand Down

0 comments on commit c55375d

Please sign in to comment.