From dcb008d381a3d1ab3bd32c9aaa4e08473acd5ab2 Mon Sep 17 00:00:00 2001 From: justin Date: Tue, 13 Oct 2026 14:47:33 +0800 Subject: [PATCH] Refactor recurring investment form and API payloads: - Simplify the creation form by removing price type and CT offset fields, defaulting to market orders. - Update `buildScheduleInvestmentCreateBody` and related functions to handle new payload structure with `ctOffsetMinutes` set to null. - Consolidate end type options into a dropdown, integrating 'GTC' as a selection. - Adjust internationalization files to reflect new labels and hints for the updated form. - Document changes in `IMPACT_ANALYSIS.md` and `TASK_SUMMARY.md` for clarity on the updates. --- IMPACT_ANALYSIS.md | 70 ++++- TASK_SUMMARY.md | 11 +- src/i18n/locales/en-US.ts | 8 +- src/i18n/locales/zh-CN.ts | 7 +- src/i18n/locales/zh-TW.ts | 7 +- src/types/components.d.ts | 1 - src/types/recurringInvestment.ts | 2 +- src/types/scheduleInvestment.ts | 9 +- src/utils/scheduleInvestmentPayload.ts | 13 +- src/utils/scheduleInvestmentPlanMapper.ts | 20 +- src/views/RecurringInvestmentView.vue | 315 +++++----------------- vite.config.ts | 2 +- 12 files changed, 186 insertions(+), 279 deletions(-) diff --git a/IMPACT_ANALYSIS.md b/IMPACT_ANALYSIS.md index 0bbcc1b..779f306 100644 --- a/IMPACT_ANALYSIS.md +++ b/IMPACT_ANALYSIS.md @@ -239,7 +239,7 @@ | 位置 | 差异 | |------|------| | 表单 | 新增 `investmentType`、`orderPriceType`、`frequencyType`、`frequencyDay`、`ctOffsetMinutes`;按金额/数量切换输入;周期仅 DAILY/WEEKLY/MONTHLY。 | -| `buildScheduleInvestmentCreateBody` | 将表单映射为 `ScheduleInvestmentCreateBody`(含 `endType` DATE/COUNT/NONE、`totalExecutionCount`、`productCode`)。 | +| `buildScheduleInvestmentCreateBody` | 将表单映射为 `ScheduleInvestmentCreateBody`(含 `endType` NO_END/END_DATE/COUNT、`totalExecutionCount`、`productCode`)。 | | `createScheduleInvestmentPlan` | `POST /trade/schedule-investment/plan/create`;确认建立时调用,成功后仍写入本地示例列表。 | ### 3. 调用方与影响范围 @@ -1017,3 +1017,71 @@ - 移动宽:执行记录为卡片,无横向挤压。 - 平板 / 窄桌面:表格可横滑,见提示文案;首列固定。 - 宽桌面:弹窗加宽后六列表格可读。 + +--- + +## 补充:创建表单隐藏价格类型与 CT 偏移(2026-10-13) + +### 1. 改动概览 + +- **背景与目标**:创建计划固定市价单,不再展示「订单价格类型」「交易时间偏移」;创建与「预览下次执行」请求显式传 `ctOffsetMinutes: null`(待后端支持缺省/0 后可再调整)。 +- **涉及模块**:`RecurringInvestmentView.vue`、`scheduleInvestmentPayload.ts`、`scheduleInvestment.ts` 类型、三语 `recurring.*`。 +- **改动类型**:功能调整(表单简化)。 + +### 2. 方法级改动分析 + +| 位置 | 差异 | +|------|------| +| 创建表单 UI | 移除价格类型、限价、CT 偏移控件;投资金额/数量下增加 `orderPriceTypeMarketHint`。 | +| `buildScheduleInvestmentCreateBody` | 固定 `ctOffsetMinutes: null`;`orderPriceType` 固定 `MARKET`。 | +| `buildNextExecutionPreviewBody` | 固定 `ctOffsetMinutes: null`。 | +| `ScheduleInvestmentCreateBody` | `ctOffsetMinutes: number \| null`,创建时传 `null`。 | +| 确认弹窗 | 移除 CT 偏移预览行;仍展示订单价格类型(市价)。 | +| `getScheduleInvestmentCtTimeOptions` | 创建表单不再调用(详情仍可读接口返回的偏移)。 | + +### 3. 调用方与影响范围 + +- 仅创建/预览路径;计划详情、列表仍展示后端返回的 `ctOffsetMinutes`。 +- **破坏性变更**:否(对外接口字段减少可选传参,行为与产品约定一致)。 + +### 4. 风险与回滚 + +- **风险级别**:低;依赖后端对缺省 `ctOffsetMinutes` 的处理与文档一致。 +- **回滚方式是否简单**:是(恢复表单项与 payload 字段即可)。 + +### 5. 验证与测试 + +- `npm run build` 通过。 +- 创建表单:无价格类型/偏移字段,金额下有两行 hint;预览下次执行仅需交易所与周期。 +- 网络:create / next-execution 请求体 `ctOffsetMinutes` 为 `null`。 + +--- + +## 补充:结束类型 GTC 下拉项(2026-10-13) + +- **背景**:将「无结束日期」勾选并入结束类型 `el-select`,文案为「GTC」。 +- **改动类型**:功能优化(UI/表单)。 + +### 2. 方法级改动分析 + +| 位置 | 差异 | +|------|------| +| `EndMode` | 新增 `'none'`。 | +| `RecurringInvestmentView` | `endModeOptions` 含 date / none / count;移除 `noEnd` 表单项与勾选框;`date` 模式必填结束日期。 | +| `resolveEndType` | `none`→`NO_END`、`date`→`END_DATE`、`count`→`COUNT`。 | +| `scheduleInvestmentPlanMapper` | API `NO_END`(兼容 `NONE`)映射 `endMode: 'none'`。 | +| i18n | `recurring.endByGtc`、`recurring.endGtcHint`。 | + +### 3. 调用方与影响范围 + +- 创建计划、确认预览、列表/详情结束条件展示文案统一为 GTC 选项。 +- **破坏性变更**:是(创建请求 `endType` 由 `NONE`/`DATE` 改为 `NO_END`/`END_DATE`;列表仍兼容旧枚举读取)。 + +### 4. 风险与回滚 + +- **风险级别**:低。 +- **回滚方式是否简单**:是。 + +### 5. 验证与测试 + +- `npm run build`;表单三种结束类型切换与提交 payload `endType` 符合预期。 diff --git a/TASK_SUMMARY.md b/TASK_SUMMARY.md index f93bc66..b37e421 100644 --- a/TASK_SUMMARY.md +++ b/TASK_SUMMARY.md @@ -151,7 +151,7 @@ ## 补充:结束类型合一控件 -- 「特定日期 / 期數」与结束日期或期数输入合并为 `field-combo`;日期模式下保留「无结束日期」勾选。 +- 「特定日期 / GTC / 期数」与结束日期或期数输入合并为 `field-combo`;无结束日期改为下拉项 `endByGtc`(`endMode: none`),不再使用独立勾选框;表单标签为「结束类型」;接口 `endType` 为 `NO_END` / `END_DATE` / `COUNT`。 --- @@ -298,3 +298,12 @@ - 详情弹窗按断点加宽;移动端执行记录卡片列表,平板/桌面表格横滑 + 首列 sticky + 滑动提示。 - 参见 `IMPACT_ANALYSIS.md` 对应补充。 + +--- + +## 补充:创建表单隐藏价格类型与 CT 偏移(2026-10-13) + +- 创建表单不展示「订单价格类型」「交易时间偏移」,固定市价;在投资金额/数量 hint 下增加市价说明 `orderPriceTypeMarketHint`。 +- 创建计划与 `plan/next-execution` 请求体 `ctOffsetMinutes` 传 `null`;预览校验文案改为「请先选择交易所与定投周期」。 +- 确认弹窗去掉 CT 偏移行;计划详情仍展示后端返回的偏移。 +- 详见 `IMPACT_ANALYSIS.md` 本节补充。 diff --git a/src/i18n/locales/en-US.ts b/src/i18n/locales/en-US.ts index 14851a1..9a7bbc0 100644 --- a/src/i18n/locales/en-US.ts +++ b/src/i18n/locales/en-US.ts @@ -85,6 +85,8 @@ export default { MARKET: 'Market', LIMIT: 'Limit', }, + orderPriceTypeMarketHint: + 'Recurring orders are placed as market orders after the open on each execution date; fills follow actual trades.', limitPrice: 'Limit price', limitPricePlaceholder: 'e.g. 350.5', limitPriceHint: 'Required for limit orders; must be greater than 0', @@ -117,7 +119,9 @@ export default { pleaseSelect: 'Please select', endModeLabel: 'End type', endByDate: 'Specific date', + endByGtc: 'GTC', endByCount: 'Periods', + endGtcHint: 'The plan keeps running until you terminate it', endDate: 'End date', noEndDate: 'No end date', endCount: 'Periods', @@ -126,7 +130,7 @@ export default { endCountValue: '{n} periods', notesTitle: 'Notes', notes: { - n1: 'Recurring orders are placed as market orders at the open on each execution date; fills follow actual trades.', + n1: 'Recurring orders are placed as market orders after the open on each execution date; fills follow actual trades.', n2: 'Fractional shares are not supported. Quantity is rounded down to whole shares, which may leave residual cash.', n3: 'If the execution date is a non-trading day, the order is deferred to the next trading day.', n4: 'If available funds are insufficient, that period’s order will not be executed; the plan continues.', @@ -135,7 +139,7 @@ export default { reset: 'Reset', previewNextExecution: 'Preview next run', previewNextExecutionTitle: 'Next execution preview', - previewNextExecutionFill: 'Select exchange, frequency, and CT offset first', + previewNextExecutionFill: 'Select exchange and frequency first', previewScheduledDate: 'Scheduled date', previewExecutionDate: 'Execution date', previewExecutionDateTime: 'Execution time', diff --git a/src/i18n/locales/zh-CN.ts b/src/i18n/locales/zh-CN.ts index bcda2de..d5b6207 100644 --- a/src/i18n/locales/zh-CN.ts +++ b/src/i18n/locales/zh-CN.ts @@ -85,6 +85,7 @@ export default { MARKET: '市价', LIMIT: '限价', }, + orderPriceTypeMarketHint: '定期投资指示于执行日开市后以市价单执行,成交价以实际成交为准。', limitPrice: '限价', limitPricePlaceholder: '例如 350.5', limitPriceHint: '限价单须填写大于 0 的价格', @@ -117,7 +118,9 @@ export default { pleaseSelect: '请选择', endModeLabel: '结束类型', endByDate: '特定日期', + endByGtc: 'GTC', endByCount: '期数', + endGtcHint: '计划将持续执行,直至您手动终止', endDate: '结束日期', noEndDate: '无结束日期', endCount: '期数', @@ -126,7 +129,7 @@ export default { endCountValue: '期数 {n} 期', notesTitle: '注意事项', notes: { - n1: '定期投资指示于执行日开市时以市价单执行,成交价以实际成交为准。', + n1: '定期投资指示于执行日开市后以市价单执行,成交价以实际成交为准。', n2: '本功能不支持不足一股的股份。定期投资的买入股数将向下取整至整股,因此可能产生碎股(零股)。', n3: '如执行日为非交易日,将顺延至下一个交易日执行。', n4: '账户可用资金不足时,该期指示将不获执行,计划仍会继续。', @@ -135,7 +138,7 @@ export default { reset: '重设', previewNextExecution: '预览下次执行', previewNextExecutionTitle: '下次执行预览', - previewNextExecutionFill: '请先选择交易所、定投周期与时间偏移', + previewNextExecutionFill: '请先选择交易所与定投周期', previewScheduledDate: '周期日期', previewExecutionDate: '执行日期', previewExecutionDateTime: '执行时间', diff --git a/src/i18n/locales/zh-TW.ts b/src/i18n/locales/zh-TW.ts index 401e2b6..95a4fe6 100644 --- a/src/i18n/locales/zh-TW.ts +++ b/src/i18n/locales/zh-TW.ts @@ -85,6 +85,7 @@ export default { MARKET: '市價', LIMIT: '限價', }, + orderPriceTypeMarketHint: '定期投資指示於執行日開市後以市價單執行,成交價以實際成交為準。', limitPrice: '限價', limitPricePlaceholder: '例如 350.5', limitPriceHint: '限價單須填寫大於 0 的價格', @@ -117,7 +118,9 @@ export default { pleaseSelect: '請選擇', endModeLabel: '結束類型', endByDate: '特定日期', + endByGtc: 'GTC', endByCount: '期數', + endGtcHint: '計劃將持續執行,直至您手動終止', endDate: '結束日期', noEndDate: '無結束日期', endCount: '期數', @@ -126,7 +129,7 @@ export default { endCountValue: '期數 {n} 期', notesTitle: '注意事項', notes: { - n1: '定期投資指示於執行日開市時以市價單執行,成交價以實際成交為準。', + n1: '定期投資指示於執行日開市後以市價單執行,成交價以實際成交為準。', n2: '本功能不支援不足一股的股份。定期投資的買入股數將向下取整至整股,因此可能產生碎股(零股)。', n3: '如執行日為非交易日,將順延至下一個交易日執行。', n4: '帳戶可用資金不足時,該期指示將不獲執行,計劃仍會繼續。', @@ -135,7 +138,7 @@ export default { reset: '重設', previewNextExecution: '預覽下次執行', previewNextExecutionTitle: '下次執行預覽', - previewNextExecutionFill: '請先選擇交易所、定投週期與時間偏移', + previewNextExecutionFill: '請先選擇交易所與定投週期', previewScheduledDate: '週期日期', previewExecutionDate: '執行日期', previewExecutionDateTime: '執行時間', diff --git a/src/types/components.d.ts b/src/types/components.d.ts index ca4834e..bc29b93 100644 --- a/src/types/components.d.ts +++ b/src/types/components.d.ts @@ -14,7 +14,6 @@ declare module 'vue' { AppPage: typeof import('./../components/layout/AppPage.vue')['default'] AssetChipPicker: typeof import('./../components/recurring/AssetChipPicker.vue')['default'] ElButton: typeof import('element-plus/es')['ElButton'] - ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] ElCol: typeof import('element-plus/es')['ElCol'] ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] diff --git a/src/types/recurringInvestment.ts b/src/types/recurringInvestment.ts index d67bafd..03c3654 100644 --- a/src/types/recurringInvestment.ts +++ b/src/types/recurringInvestment.ts @@ -9,7 +9,7 @@ export type IntervalKey = | 'semiannual' | 'annual' -export type EndMode = 'date' | 'count' +export type EndMode = 'date' | 'count' | 'none' export type PlanStatus = 'active' | 'created' | 'cancelled' diff --git a/src/types/scheduleInvestment.ts b/src/types/scheduleInvestment.ts index 97436df..c186994 100644 --- a/src/types/scheduleInvestment.ts +++ b/src/types/scheduleInvestment.ts @@ -6,7 +6,7 @@ export type OrderPriceType = 'MARKET' | 'LIMIT' export type FrequencyType = 'DAILY' | 'WEEKLY' | 'MONTHLY' -export type ScheduleEndType = 'DATE' | 'COUNT' | 'NONE' +export type ScheduleEndType = 'NO_END' | 'END_DATE' | 'COUNT' export type WeekdayCode = 'MON' | 'TUE' | 'WED' | 'THU' | 'FRI' | 'SAT' | 'SUN' @@ -117,8 +117,8 @@ export interface ScheduleInvestmentNextExecutionRequest { frequencyType: FrequencyType /** WEEKLY / MONTHLY 时必填 */ frequencyDay?: string - /** 空或 0 表示 CT 开市时刻;最大 240 */ - ctOffsetMinutes?: number + /** null / 0 表示 CT 开市时刻;最大 240 */ + ctOffsetMinutes?: number | null /** 空表示交易所交易日 */ startDate?: string } @@ -223,5 +223,6 @@ export interface ScheduleInvestmentCreateBody { endType: ScheduleEndType endDate?: string totalExecutionCount?: number - ctOffsetMinutes: number + /** null 表示 CT 开市默认(后端未支持省略时显式传 null) */ + ctOffsetMinutes: number | null } diff --git a/src/utils/scheduleInvestmentPayload.ts b/src/utils/scheduleInvestmentPayload.ts index 6d02eef..0d6bb79 100644 --- a/src/utils/scheduleInvestmentPayload.ts +++ b/src/utils/scheduleInvestmentPayload.ts @@ -20,11 +20,10 @@ export interface ScheduleFormPayloadInput { frequencyType: FrequencyType frequencyDay: string startDate: string - endMode: 'date' | 'count' + endMode: 'date' | 'count' | 'none' endDate: string noEnd: boolean endCount: number | null - ctOffsetMinutes: number clientId: string | null accountId: string | null } @@ -33,10 +32,10 @@ export function resolveEndType(input: Pick { - const endType = record.endType as ScheduleEndType + const endType = normalizeScheduleEndType(record.endType) if (endType === 'COUNT' && record.totalExecutionCount != null) { return { endMode: 'count', @@ -49,9 +63,9 @@ function resolveEndFields(record: ScheduleInvestmentPlanRecord): Pick< endDisplay: '', } } - if (endType === 'NONE' || !record.endDate) { + if (endType === 'NO_END' || endType === null || !record.endDate) { return { - endMode: 'date', + endMode: 'none', endDate: '', endCount: null, noEnd: true, diff --git a/src/views/RecurringInvestmentView.vue b/src/views/RecurringInvestmentView.vue index 80eebcd..a4bb325 100644 --- a/src/views/RecurringInvestmentView.vue +++ b/src/views/RecurringInvestmentView.vue @@ -6,7 +6,6 @@ import AppPage from '@/components/layout/AppPage.vue' import AssetChipPicker from '@/components/recurring/AssetChipPicker.vue' import { createScheduleInvestmentPlan, - getScheduleInvestmentCtTimeOptions, getScheduleInvestmentExchanges, getScheduleInvestmentPlanDetail, getScheduleInvestmentPlanPage, @@ -32,11 +31,9 @@ import type { import { FREQUENCY_TYPE_KEYS, INVESTMENT_TYPE_KEYS, - ORDER_PRICE_TYPE_KEYS, type FrequencyType, type InvestmentType, type OrderPriceType, - type ScheduleInvestmentCtTimeOption, type ScheduleInvestmentExchangeItem, type ScheduleInvestmentNextExecutionData, type ScheduleInvestmentPlanDetailPlan, @@ -52,6 +49,7 @@ import { import { mapApiPlanStatus, mapSchedulePlanRecordToRecurringPlan, + normalizeScheduleEndType, } from '@/utils/scheduleInvestmentPlanMapper' import { formatFrequencyDayLabel, @@ -74,10 +72,8 @@ interface FormState { limitPrice: string frequencyType: FrequencyType | '' frequencyDay: string - ctOffsetMinutes: number endMode: EndMode endDate: string - noEnd: boolean endCount: string } @@ -122,8 +118,6 @@ const terminateSubmitting = ref(false) const nextExecutionPreviewVisible = ref(false) const nextExecutionPreviewLoading = ref(false) const nextExecutionPreview = ref(null) -const ctOffsetOptions = ref([]) -const ctOffsetLoading = ref(false) const PLAN_PAGE_SIZE = 50 @@ -137,10 +131,8 @@ const form = reactive({ limitPrice: '', frequencyType: '', frequencyDay: '', - ctOffsetMinutes: 30, - endMode: 'date', + endMode: 'none', endDate: '', - noEnd: false, endCount: '', }) @@ -149,12 +141,10 @@ const fieldError = reactive({ startDate: false, amount: false, quantity: false, - orderPriceType: false, - limitPrice: false, frequencyType: false, frequencyDay: false, - ctOffsetMinutes: false, endCount: false, + endDate: false, }) const exchangeSelectOptions = computed(() => @@ -173,13 +163,6 @@ const frequencyOptions = computed(() => })), ) -const orderPriceOptions = computed(() => - ORDER_PRICE_TYPE_KEYS.map((key) => ({ - value: key, - label: t(`recurring.orderPriceType.${key}`), - })), -) - const investmentTypeOptions = computed(() => INVESTMENT_TYPE_KEYS.map((key) => ({ value: key, @@ -197,16 +180,29 @@ const investmentValueHint = computed(() => const endModeOptions = computed(() => [ { value: 'date' as EndMode, label: t('recurring.endByDate') }, + { value: 'none' as EndMode, label: t('recurring.endByGtc') }, { value: 'count' as EndMode, label: t('recurring.endByCount') }, ]) -const endValueFieldError = computed(() => - form.endMode === 'count' ? fieldError.endCount : false, -) +const endValueFieldError = computed(() => { + if (form.endMode === 'count') { + return fieldError.endCount + } + if (form.endMode === 'date') { + return fieldError.endDate + } + return false +}) -const endValueHint = computed(() => - form.endMode === 'count' ? t('recurring.endCountHint') : t('recurring.noEndDate'), -) +const endValueHint = computed(() => { + if (form.endMode === 'count') { + return t('recurring.endCountHint') + } + if (form.endMode === 'none') { + return t('recurring.endGtcHint') + } + return '' +}) const weekdayOptions = computed(() => WEEKLY_ISO_DAY_VALUES.map((iso) => { @@ -229,21 +225,6 @@ const showFrequencyDay = computed( () => form.frequencyType === 'WEEKLY' || form.frequencyType === 'MONTHLY', ) -const ctOffsetSelectEnabled = computed(() => ctOffsetOptions.value.length > 0) - -const ctOffsetHintText = computed(() => - ctOffsetSelectEnabled.value ? t('recurring.ctOffsetHint') : t('recurring.ctOffsetPickAssetHint'), -) - -watch( - () => form.noEnd, - (checked) => { - if (checked) { - form.endDate = '' - } - }, -) - watch( () => form.frequencyType, (type) => { @@ -266,20 +247,20 @@ watch( }, ) -watch( - () => form.orderPriceType, - (type) => { - fieldError.limitPrice = false - if (type !== 'LIMIT') { - form.limitPrice = '' - } - }, -) - watch( () => form.endMode, - () => { + (mode) => { fieldError.endCount = false + fieldError.endDate = false + if (mode === 'none') { + form.endDate = '' + } + if (mode === 'count') { + form.endDate = '' + } + if (mode === 'date') { + form.endCount = '' + } }, ) @@ -289,65 +270,6 @@ watch(picked, () => { } }) -function clearCtOffsetOptions() { - ctOffsetOptions.value = [] -} - -function syncCtOffsetAfterOptionsLoad() { - if (!ctOffsetOptions.value.length) { - return - } - const allowed = new Set(ctOffsetOptions.value.map((item) => item.offsetMinutes)) - if (!allowed.has(form.ctOffsetMinutes)) { - form.ctOffsetMinutes = ctOffsetOptions.value[0].offsetMinutes - } -} - -async function loadCtOffsetOptions(exchangeCode: string, productCode: string) { - ctOffsetLoading.value = true - try { - const res = await getScheduleInvestmentCtTimeOptions({ exchangeCode, productCode }) - ctOffsetOptions.value = Array.isArray(res.data?.options) ? res.data.options : [] - syncCtOffsetAfterOptionsLoad() - } catch { - ctOffsetOptions.value = [] - } finally { - ctOffsetLoading.value = false - } -} - -function resolvePickedProductCode(): string { - const asset = picked.value[0] - if (!asset) { - return '' - } - return asset.productCode?.trim() ?? '' -} - -watch( - () => [form.exchangeCode, resolvePickedProductCode()] as const, - ([exchangeCode, productCode]) => { - if (!exchangeCode || !productCode) { - clearCtOffsetOptions() - return - } - loadCtOffsetOptions(exchangeCode, productCode) - }, -) - -function isCtOffsetValid(offset: number): boolean { - if (ctOffsetOptions.value.length) { - return ctOffsetOptions.value.some((item) => item.offsetMinutes === offset) - } - return ( - offset !== null && - offset !== undefined && - !Number.isNaN(offset) && - offset >= 0 && - offset <= 240 - ) -} - function mapSimpleStockToAsset(item: SimpleStockItem): InvestAsset { const type: AssetType = item.productType === '1' ? 'stock' : 'etf' const engName = item.engName?.trim() ?? '' @@ -537,8 +459,8 @@ function endTextForPlan(plan: RecurringPlan) { if (plan.endMode === 'count' && plan.endCount) { return t('recurring.endCountValue', { n: plan.endCount }) } - if (plan.noEnd || !plan.endDate) { - return t('recurring.noEndDate') + if (plan.endMode === 'none' || plan.noEnd || !plan.endDate) { + return t('recurring.endByGtc') } return plan.endDate } @@ -547,8 +469,8 @@ function buildEndDisplay() { if (form.endMode === 'count') { return t('recurring.endCountValue', { n: Number.parseInt(form.endCount, 10) }) } - if (form.noEnd || !form.endDate) { - return t('recurring.noEndDate') + if (form.endMode === 'none') { + return t('recurring.endByGtc') } return form.endDate } @@ -559,12 +481,10 @@ function clearErrors() { fieldError.startDate = false fieldError.amount = false fieldError.quantity = false - fieldError.orderPriceType = false - fieldError.limitPrice = false fieldError.frequencyType = false fieldError.frequencyDay = false - fieldError.ctOffsetMinutes = false fieldError.endCount = false + fieldError.endDate = false } function validate(): boolean { @@ -596,17 +516,6 @@ function validate(): boolean { ok = false } } - if (!form.orderPriceType) { - fieldError.orderPriceType = true - ok = false - } - if (form.orderPriceType === 'LIMIT') { - const limit = parseAmountInput(form.limitPrice) - if (!form.limitPrice.trim() || Number.isNaN(limit) || limit <= 0) { - fieldError.limitPrice = true - ok = false - } - } if (!form.frequencyType) { fieldError.frequencyType = true ok = false @@ -615,10 +524,6 @@ function validate(): boolean { fieldError.frequencyDay = true ok = false } - if (!isCtOffsetValid(form.ctOffsetMinutes)) { - fieldError.ctOffsetMinutes = true - ok = false - } if (form.endMode === 'count') { const count = Number.parseInt(form.endCount, 10) if (!form.endCount.trim() || Number.isNaN(count) || count < 1) { @@ -626,6 +531,10 @@ function validate(): boolean { ok = false } } + if (form.endMode === 'date' && !form.endDate) { + fieldError.endDate = true + ok = false + } return ok } @@ -634,7 +543,6 @@ function validateNextExecutionPreview(): boolean { fieldError.exchangeCode = false fieldError.frequencyType = false fieldError.frequencyDay = false - fieldError.ctOffsetMinutes = false let ok = true if (!form.exchangeCode) { @@ -649,11 +557,6 @@ function validateNextExecutionPreview(): boolean { fieldError.frequencyDay = true ok = false } - if (!isCtOffsetValid(form.ctOffsetMinutes)) { - fieldError.ctOffsetMinutes = true - ok = false - } - return ok } @@ -663,12 +566,12 @@ function buildNextExecutionPreviewBody() { exchangeCode: string frequencyType: FrequencyType frequencyDay?: string - ctOffsetMinutes: number + ctOffsetMinutes: null startDate?: string } = { exchangeCode: form.exchangeCode, frequencyType, - ctOffsetMinutes: form.ctOffsetMinutes, + ctOffsetMinutes: null, } const apiDay = frequencyDayForApi(frequencyType, form.frequencyDay) if (apiDay) { @@ -722,11 +625,8 @@ function resetForm() { form.limitPrice = '' form.frequencyType = '' form.frequencyDay = '' - form.ctOffsetMinutes = 0 - clearCtOffsetOptions() - form.endMode = 'date' + form.endMode = 'none' form.endDate = '' - form.noEnd = false form.endCount = '' clearErrors() } @@ -750,14 +650,11 @@ function onSubmit() { endMode: form.endMode, endDate: form.endDate, endCount: form.endMode === 'count' ? Number.parseInt(form.endCount, 10) : null, - noEnd: form.endMode === 'date' && (form.noEnd || !form.endDate), + noEnd: form.endMode === 'none', investmentType: form.investmentType, quantity, - orderPriceType: form.orderPriceType, - limitPrice: - form.orderPriceType === 'LIMIT' ? parseAmountInput(form.limitPrice) : undefined, + orderPriceType: 'MARKET', frequencyDay: showFrequencyDay.value ? form.frequencyDay : undefined, - ctOffsetMinutes: form.ctOffsetMinutes, } confirmVisible.value = true } @@ -770,24 +667,21 @@ function buildCreateRequestBody(): ReturnType { } return typeLabel }) -const previewOffset = computed(() => - draft.value?.ctOffsetMinutes != null - ? t('recurring.ctOffsetPreview', { n: draft.value.ctOffsetMinutes }) - : '', -)