Toggle navigation
Toggle navigation
This project
Loading...
Sign in
Hooke
/
manulife-weapp
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Graphs
Network
Create a new issue
Commits
Issue Boards
Authored by
hookehuyr
2026-04-20 14:03:47 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
b30e65bae6c6675c37aa9f2ebaa042287688ec87
b30e65ba
1 parent
6e432342
feat(plan): 支持MPC孕22周年龄选项
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
209 additions
and
17 deletions
src/api/plan.js
src/components/plan/PlanFields/AgePickerGlobal.vue
src/components/plan/PlanTemplates/CriticalIllnessTemplate.vue
src/config/__tests__/plan-templates.test.js
src/config/plan-templates.js
src/utils/__tests__/agePickerOptions.test.js
src/utils/__tests__/planFieldTransformers.test.js
src/utils/agePickerOptions.js
src/utils/planFieldTransformers.js
src/api/plan.js
View file @
b30e65b
...
...
@@ -13,7 +13,7 @@ const Api = {
* @param {Object} params 请求参数
* @param {string} params.customer_name 申请人
* @param {string} params.customer_gender 性别
* @param {
integer
} params.customer_age 年龄
* @param {
string
} params.customer_age 年龄
* @param {string} params.customer_birthday 出生年月日
* @param {integer} params.annual_premium 年缴保费
* @param {string} params.payment_years 繳費年期
...
...
src/components/plan/PlanFields/AgePickerGlobal.vue
View file @
b30e65b
...
...
@@ -43,6 +43,7 @@
* - 显示格式:3位数字(如 018 表示 18 岁)
* - 提交格式:数字(如 18)
* - 年龄范围:0-120 岁
* - 支持按产品配置注入特殊年龄选项(如“孕22周”)
* - 使用 GlobalPopupManager 管理弹窗层级
* @author Claude Code
* @version 2.0.0 - 支持全局弹窗管理器
...
...
@@ -56,6 +57,13 @@
import { ref, computed, watch, onMounted } from 'vue'
import IconFont from '@/components/icons/IconFont.vue'
import { useGlobalPopup } from './GlobalPopupManager'
import {
buildAgePickerColumn,
DEFAULT_AGE_PICKER_VALUE,
formatAgeDisplayValue,
normalizeAgePickerValue,
parseAgePickerValue
} from '@/utils/agePickerOptions'
/**
* 使用全局弹窗管理器
...
...
@@ -107,12 +115,21 @@ const props = defineProps({
},
/**
* 绑定的值(数字)
* @type {number}
* 绑定的值(数字
年龄或特殊年龄文本
)
* @type {number
|string
}
*/
modelValue: {
type:
Number
,
type:
[Number, String]
,
default: null
},
/**
* 产品级特殊年龄选项
* @type {Array<string>}
*/
specialOptions: {
type: Array,
default: () => []
}
})
...
...
@@ -143,17 +160,14 @@ const showPicker = ref(false)
/**
* Picker 当前值(3位数字格式)
*/
const pickerValue = ref([
'018'
])
const pickerValue = ref([
DEFAULT_AGE_PICKER_VALUE
])
/**
* 年龄选项列(0-120 岁,3位数字格式)
*/
const ageColumns = computed(() => {
return [
Array.from({ length: 121 }, (_, i) => ({
text: `${i} 岁`,
value: String(i).padStart(3, '0')
}))
buildAgePickerColumn({ specialOptions: props.specialOptions })
]
})
...
...
@@ -161,10 +175,7 @@ const ageColumns = computed(() => {
* 显示的值(转换为中文格式)
*/
const displayValue = computed(() => {
if (props.modelValue === null || props.modelValue === undefined) {
return ''
}
return `${props.modelValue} 岁`
return formatAgeDisplayValue(props.modelValue)
})
/**
...
...
@@ -178,7 +189,7 @@ const handleTap = () => {
// 如果有值,转换为3位数字格式
if (props.modelValue !== null && props.modelValue !== undefined) {
pickerValue.value = [
String(props.modelValue).padStart(3, '0'
)]
pickerValue.value = [
normalizeAgePickerValue(props.modelValue, props.specialOptions
)]
}
showPicker.value = true
...
...
@@ -193,8 +204,7 @@ const handleTap = () => {
* onConfirm({ selectedValue: ['018'] })
*/
const onConfirm = ({ selectedValue }) => {
// 将3位数字格式转换为普通数字
const age = parseInt(selectedValue[0], 10)
const age = parseAgePickerValue(selectedValue[0])
emit('update:modelValue', age)
emit('change', age)
...
...
@@ -226,7 +236,7 @@ watch(
() => props.modelValue,
(newVal) => {
if (newVal !== null && newVal !== undefined) {
pickerValue.value = [
String(newVal).padStart(3, '0'
)]
pickerValue.value = [
normalizeAgePickerValue(newVal, props.specialOptions
)]
}
}
)
...
...
src/components/plan/PlanTemplates/CriticalIllnessTemplate.vue
View file @
b30e65b
...
...
@@ -72,6 +72,7 @@ const props = defineProps({
* @type {Object}
* @property {string} currency - 币种代码
* @property {Array<string>} payment_periods - 缴费年期选项
* @property {Array<string>} special_age_options - 产品专属年龄特殊选项
* @property {Object} age_range - 年龄范围 { min, max }
* @property {string} insurance_period - 保险期间
* @property {Object} form_schema - 表单 Schema
...
...
@@ -152,6 +153,10 @@ const getFieldProps = (field) => {
fieldProps
.
options
=
field
.
options
}
if
(
field
.
key
===
'
age
'
&&
Array
.
isArray
(
props
.
config
?.
special_age_options
))
{
fieldProps
.
specialOptions
=
props
.
config
.
special_age_options
}
// 缴费年期选项由模板配置提供
if
(
field
.
options_from
===
'
payment_periods
'
)
{
fieldProps
.
options
=
fieldProps
.
options
||
props
.
config
?.
payment_periods
...
...
src/config/__tests__/plan-templates.test.js
View file @
b30e65b
...
...
@@ -44,3 +44,11 @@ describe('plan field definitions amount semantics', () => {
})
})
})
describe
(
'critical illness mpc special age options'
,
()
=>
{
it
(
'should expose pregnancy week option only for mpc'
,
()
=>
{
expect
(
PLAN_TEMPLATES
[
'critical-illness-mpc'
].
config
.
special_age_options
).
toEqual
([
'孕22周'
])
expect
(
PLAN_TEMPLATES
[
'critical-illness-mbc-pro'
].
config
.
special_age_options
).
toBeUndefined
()
expect
(
PLAN_TEMPLATES
[
'critical-illness-mbc2'
].
config
.
special_age_options
).
toBeUndefined
()
})
})
...
...
src/config/plan-templates.js
View file @
b30e65b
...
...
@@ -197,6 +197,7 @@ export const PLAN_TEMPLATES = {
component
:
'CriticalIllnessTemplate'
,
config
:
{
currency
:
'USD'
,
special_age_options
:
[
'孕22周'
],
// 仅 MPC 使用:年龄字段增加前置特殊选项
payment_periods
:
[
'10 年(15 日 - 65 岁)'
,
'20 年(15 日 - 65 岁)'
,
...
...
src/utils/__tests__/agePickerOptions.test.js
0 → 100644
View file @
b30e65b
import
{
describe
,
expect
,
it
}
from
'vitest'
import
{
buildAgePickerColumn
,
formatAgeDisplayValue
,
normalizeAgePickerValue
,
parseAgePickerValue
,
SPECIAL_AGE_VALUE_PREFIX
}
from
'../agePickerOptions'
describe
(
'agePickerOptions'
,
()
=>
{
it
(
'should prepend special age options before numeric ages'
,
()
=>
{
const
column
=
buildAgePickerColumn
({
specialOptions
:
[
'孕22周'
]
})
expect
(
column
[
0
]).
toEqual
({
text
:
'孕22周'
,
value
:
`
${
SPECIAL_AGE_VALUE_PREFIX
}
孕22周`
})
expect
(
column
[
1
]).
toEqual
({
text
:
'0 岁'
,
value
:
'000'
})
})
it
(
'should format special and numeric age display values'
,
()
=>
{
expect
(
formatAgeDisplayValue
(
'孕22周'
)).
toBe
(
'孕22周'
)
expect
(
formatAgeDisplayValue
(
0
)).
toBe
(
'0 岁'
)
expect
(
formatAgeDisplayValue
(
'12'
)).
toBe
(
'12 岁'
)
})
it
(
'should normalize and parse picker values correctly'
,
()
=>
{
expect
(
normalizeAgePickerValue
(
'孕22周'
,
[
'孕22周'
])).
toBe
(
`
${
SPECIAL_AGE_VALUE_PREFIX
}
孕22周`
)
expect
(
normalizeAgePickerValue
(
3
,
[
'孕22周'
])).
toBe
(
'003'
)
expect
(
parseAgePickerValue
(
`
${
SPECIAL_AGE_VALUE_PREFIX
}
孕22周`
)).
toBe
(
'孕22周'
)
expect
(
parseAgePickerValue
(
'003'
)).
toBe
(
3
)
})
})
src/utils/__tests__/planFieldTransformers.test.js
View file @
b30e65b
...
...
@@ -72,6 +72,10 @@ describe('formatAge', () => {
expect
(
formatAge
(
0
)).
toBe
(
'0岁'
)
})
it
(
'should keep special age labels unchanged'
,
()
=>
{
expect
(
formatAge
(
'孕22周'
)).
toBe
(
'孕22周'
)
})
it
(
'should handle null and undefined'
,
()
=>
{
expect
(
formatAge
(
null
)).
toBe
(
null
)
expect
(
formatAge
(
undefined
)).
toBe
(
null
)
...
...
src/utils/agePickerOptions.js
0 → 100644
View file @
b30e65b
/**
* 特殊年龄选项在 Picker 内部使用字符串前缀编码,
* 用来和普通数字年龄值(如 "003")区分。
*/
export
const
SPECIAL_AGE_VALUE_PREFIX
=
'__special_age__:'
/**
* 未选择年龄时,Picker 默认定位到 18 岁。
*/
export
const
DEFAULT_AGE_PICKER_VALUE
=
'018'
/**
* 清洗配置里的特殊年龄选项,去掉空值和多余空格。
*/
const
normalizeSpecialOptions
=
(
specialOptions
=
[])
=>
{
return
specialOptions
.
map
(
option
=>
String
(
option
??
''
).
trim
())
.
filter
(
Boolean
)
}
/**
* 构建 NutUI Picker 需要的年龄选项列。
* 特殊年龄项会排在最前面,随后才是 0-120 岁的普通年龄。
*/
export
function
buildAgePickerColumn
({
specialOptions
=
[],
minAge
=
0
,
maxAge
=
120
}
=
{})
{
const
normalizedSpecialOptions
=
normalizeSpecialOptions
(
specialOptions
)
const
numericOptions
=
Array
.
from
({
length
:
maxAge
-
minAge
+
1
},
(
_
,
index
)
=>
{
const
age
=
minAge
+
index
return
{
text
:
`
${
age
}
岁`
,
value
:
String
(
age
).
padStart
(
3
,
'0'
)
}
})
const
specialAgeOptions
=
normalizedSpecialOptions
.
map
(
option
=>
({
text
:
option
,
value
:
`
${
SPECIAL_AGE_VALUE_PREFIX
}${
option
}
`
}))
return
[...
specialAgeOptions
,
...
numericOptions
]
}
/**
* 将表单里的年龄值转换成输入框展示文案。
* 数字年龄显示为 "X 岁",特殊年龄文本原样显示。
*/
export
function
formatAgeDisplayValue
(
value
)
{
if
(
value
===
null
||
value
===
undefined
)
{
return
''
}
if
(
typeof
value
===
'number'
&&
!
Number
.
isNaN
(
value
))
{
return
`
${
value
}
岁`
}
const
normalizedValue
=
String
(
value
).
trim
()
if
(
!
normalizedValue
)
{
return
''
}
if
(
/^
\d
+$/
.
test
(
normalizedValue
))
{
return
`
${
parseInt
(
normalizedValue
,
10
)}
岁`
}
return
normalizedValue
}
/**
* 将外部年龄值转换成 Picker 内部 value。
* 普通年龄会变成三位数字字符串,特殊年龄会追加内部前缀。
*/
export
function
normalizeAgePickerValue
(
value
,
specialOptions
=
[],
defaultValue
=
DEFAULT_AGE_PICKER_VALUE
)
{
if
(
value
===
null
||
value
===
undefined
||
value
===
''
)
{
return
defaultValue
}
const
normalizedSpecialOptions
=
normalizeSpecialOptions
(
specialOptions
)
if
(
typeof
value
===
'string'
)
{
const
normalizedValue
=
value
.
trim
()
if
(
normalizedSpecialOptions
.
includes
(
normalizedValue
))
{
return
`
${
SPECIAL_AGE_VALUE_PREFIX
}${
normalizedValue
}
`
}
if
(
/^
\d
+$/
.
test
(
normalizedValue
))
{
return
String
(
parseInt
(
normalizedValue
,
10
)).
padStart
(
3
,
'0'
)
}
return
defaultValue
}
if
(
typeof
value
===
'number'
&&
!
Number
.
isNaN
(
value
))
{
return
String
(
value
).
padStart
(
3
,
'0'
)
}
return
defaultValue
}
/**
* 将 Picker 返回值还原成表单实际存储值。
* 特殊年龄返回原始文本,普通年龄返回数字。
*/
export
function
parseAgePickerValue
(
value
)
{
if
(
typeof
value
!==
'string'
)
{
return
null
}
if
(
value
.
startsWith
(
SPECIAL_AGE_VALUE_PREFIX
))
{
return
value
.
slice
(
SPECIAL_AGE_VALUE_PREFIX
.
length
)
}
const
parsedValue
=
parseInt
(
value
,
10
)
return
Number
.
isNaN
(
parsedValue
)
?
null
:
parsedValue
}
src/utils/planFieldTransformers.js
View file @
b30e65b
...
...
@@ -76,6 +76,20 @@ export function formatAge(value) {
if
(
value
===
null
||
value
===
undefined
)
{
return
null
}
if
(
typeof
value
===
'string'
)
{
const
normalizedValue
=
value
.
trim
()
if
(
!
normalizedValue
)
{
return
null
}
if
(
!
/^
\d
+$/
.
test
(
normalizedValue
))
{
return
normalizedValue
}
return
`
${
parseInt
(
normalizedValue
,
10
)}
岁`
}
return
`
${
value
}
岁`
}
...
...
Please
register
or
login
to post a comment