diff --git a/app/components/register/CaptchaField.vue b/app/components/register/CaptchaField.vue
new file mode 100644
index 0000000..39e6696
--- /dev/null
+++ b/app/components/register/CaptchaField.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+ 验证码加载失败,
+
+
+
+
+
diff --git a/app/components/register/__tests__/CaptchaField.test.ts b/app/components/register/__tests__/CaptchaField.test.ts
new file mode 100644
index 0000000..6f471ae
--- /dev/null
+++ b/app/components/register/__tests__/CaptchaField.test.ts
@@ -0,0 +1,62 @@
+import { describe, it, expect } from 'vitest'
+import { mount } from '@vue/test-utils'
+import CaptchaField from '../CaptchaField.vue'
+
+const stubs = {
+ UInput: {
+ template: '',
+ props: ['type', 'disabled', 'modelValue', 'placeholder'],
+ },
+ UButton: {
+ template: '',
+ props: ['disabled', 'icon', 'variant', 'color', 'square'],
+ emits: ['click'],
+ },
+ UFormField: {
+ template: '
{{ label }}
',
+ props: ['label', 'required'],
+ },
+}
+
+const defaultProps = { svg: '', loading: false, modelValue: '' }
+
+function mountComponent(overrides = {}) {
+ return mount(CaptchaField, {
+ props: { ...defaultProps, ...overrides },
+ global: { stubs },
+ })
+}
+
+describe('CaptchaField', () => {
+ it('renders SVG via v-html', () => {
+ const svg = ''
+ const wrapper = mountComponent({ svg })
+ expect(wrapper.html()).toContain(' {
+ const wrapper = mountComponent()
+ const btn = wrapper.find('button')
+ await btn.trigger('click')
+ expect(wrapper.emitted('refresh')).toBeTruthy()
+ })
+
+ it('disables refresh button when loading', () => {
+ const wrapper = mountComponent({ loading: true })
+ const btn = wrapper.find('button')
+ expect(btn.attributes('disabled')).toBeDefined()
+ })
+
+ it('renders error state when svg is empty', () => {
+ const wrapper = mountComponent({ svg: '' })
+ expect(wrapper.text()).toContain('加载失败')
+ })
+
+ it('emits update:modelValue when input changes', async () => {
+ const wrapper = mountComponent()
+ const input = wrapper.find('input')
+ await input.setValue('abc')
+ expect(wrapper.emitted('update:modelValue')).toBeTruthy()
+ expect(wrapper.emitted('update:modelValue')![0]).toEqual(['abc'])
+ })
+})