Преглед на файлове

feat(web-admin): mobile-friendly OA money field for phone preview

Add decimal inputmode, taller touch targets under 768px, and wrapping uppercase; document phase-1 vs phase-2 mobile scope.

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu преди 3 месеца
родител
ревизия
be41e8f202

+ 21 - 1
docs/superpowers/specs/2026-07-30-oa-money-field-design.md

@@ -27,8 +27,15 @@
 - 多币种、外币大写
 - 多币种、外币大写
 - 大写字符串单独字段落库 / 导出专用列
 - 大写字符串单独字段落库 / 导出专用列
 - 负数金额(本期禁止输入;清洗后不允许为负)
 - 负数金额(本期禁止输入;清洗后不允许为负)
-- 小程序端本期实现(契约须可映射;PC 先落地)
 - 用金额控件改写审批人解析策略
 - 用金额控件改写审批人解析策略
+- **阶段 2**:小程序/H5 审批运行时渲染器(另立规格;本仓库暂无 `miniprogram/` 工程时先保证 Schema 可映射)
+
+### 1.3 移动端分阶段
+
+| 阶段 | 范围 | 状态 |
+|------|------|------|
+| **1** | 管理端设计器手机预览 + Web 窄屏(`inputmode=decimal`、触控高度、大写换行) | 本期交付 |
+| **2** | 小程序/H5 发起/待办/详情识别 `type: money` | 后续立项 |
 
 
 ---
 ---
 
 
@@ -139,6 +146,17 @@ type MoneyFormValue = number | null
 - `0` → `零元整`。
 - `0` → `零元整`。
 - 与钉钉/银行常见惯例对齐即可,不要求与某一银行票据逐字公证;需单测覆盖样例集。
 - 与钉钉/银行常见惯例对齐即可,不要求与某一银行票据逐字公证;需单测覆盖样例集。
 
 
+### 5.5 移动端阶段 1(管理端窄屏 / 手机预览)
+
+对齐 `docs/superpowers/specs/2026-07-14-oa-approval-dingtalk-design.md` §4.6(同一 Schema、可降级布局、手机预览):
+
+- 填单输入使用 `inputmode="decimal"`,便于移动端弹出数字键盘(经 `AmountInput` 可选 prop 传入,不改变其它业务页默认行为)。
+- 输入框触控高度:默认 `min-height: 40px`;视口宽度 &lt; 768px 时 `44px`。
+- 大写行 `word-break` / `overflow-wrap`,窄屏可折行,避免撑破设计器手机画布。
+- 设计器「手机」预览与 PC 同结构(输入 + 可选大写);存值仍为 `number | null`。
+
+阶段 2(小程序/H5 运行时)不在本节实现范围内。
+
 ---
 ---
 
 
 ## 6. 流程条件
 ## 6. 流程条件
@@ -180,6 +198,8 @@ type MoneyFormValue = number | null
 5. 关闭大写后填单/详情/打印均无大写行。  
 5. 关闭大写后填单/详情/打印均无大写行。  
 6. 详情与打印展示与 Schema 配置一致。  
 6. 详情与打印展示与 Schema 配置一致。  
 7. 大写工具函数有单元测试(零、整数、角分、较大金额等)。  
 7. 大写工具函数有单元测试(零、整数、角分、较大金额等)。  
+8. 设计器切换手机预览时,金额控件可见输入区与大写预览,无横向撑破。  
+9. 窄屏/手机浏览器填单时输入框带 `inputmode=decimal`(或等效数字键盘提示)。  
 
 
 ---
 ---
 
 

+ 5 - 1
web-admin/src/components/common/amount-input/index.vue

@@ -4,6 +4,7 @@
     :placeholder="placeholder"
     :placeholder="placeholder"
     :disabled="disabled"
     :disabled="disabled"
     :clearable="clearable"
     :clearable="clearable"
+    :inputmode="inputmode"
     style="width: 100%"
     style="width: 100%"
     @focus="handleFocus"
     @focus="handleFocus"
     @blur="handleBlur"
     @blur="handleBlur"
@@ -29,6 +30,8 @@
     placeholder?: string
     placeholder?: string
     disabled?: boolean
     disabled?: boolean
     clearable?: boolean
     clearable?: boolean
+    /** 移动端数字键盘提示,如 decimal;不传则不设置 */
+    inputmode?: string
   }
   }
 
 
   const props = withDefaults(defineProps<Props>(), {
   const props = withDefaults(defineProps<Props>(), {
@@ -37,7 +40,8 @@
     precision: 2,
     precision: 2,
     placeholder: '',
     placeholder: '',
     disabled: false,
     disabled: false,
-    clearable: true
+    clearable: true,
+    inputmode: undefined
   })
   })
 
 
   const emit = defineEmits<{
   const emit = defineEmits<{

+ 41 - 4
web-admin/src/components/oa-approval/OaMoneyField.vue

@@ -1,13 +1,18 @@
-<!-- FormCreate 自定义控件:金额(数字 + 可选人民币大写) -->
+<!-- FormCreate 自定义控件:金额(数字 + 可选人民币大写;窄屏/手机预览友好) -->
 <template>
 <template>
   <div class="oa-money-field">
   <div class="oa-money-field">
     <template v-if="isDesignerHost">
     <template v-if="isDesignerHost">
-      <el-input model-value="" :placeholder="placeholder || '请输入金额'" disabled />
+      <el-input
+        model-value=""
+        :placeholder="placeholder || '请输入金额'"
+        inputmode="decimal"
+        disabled
+      />
       <div v-if="showUpper" class="oa-money-field__upper">大写金额预览</div>
       <div v-if="showUpper" class="oa-money-field__upper">大写金额预览</div>
     </template>
     </template>
     <template v-else-if="disabled">
     <template v-else-if="disabled">
       <div class="oa-money-field__readonly">
       <div class="oa-money-field__readonly">
-        <span>{{ displayNumber || '—' }}</span>
+        <span class="oa-money-field__num">{{ displayNumber || '—' }}</span>
         <div v-if="showUpper && upperText" class="oa-money-field__upper">{{ upperText }}</div>
         <div v-if="showUpper && upperText" class="oa-money-field__upper">{{ upperText }}</div>
       </div>
       </div>
     </template>
     </template>
@@ -17,6 +22,7 @@
         :placeholder="placeholder || '请输入金额'"
         :placeholder="placeholder || '请输入金额'"
         :min="0"
         :min="0"
         :precision="2"
         :precision="2"
+        inputmode="decimal"
         @update:model-value="onInput"
         @update:model-value="onInput"
       />
       />
       <div v-if="showUpper && upperText" class="oa-money-field__upper">{{ upperText }}</div>
       <div v-if="showUpper && upperText" class="oa-money-field__upper">{{ upperText }}</div>
@@ -81,10 +87,41 @@
 </script>
 </script>
 
 
 <style scoped>
 <style scoped>
+  .oa-money-field {
+    width: 100%;
+  }
+
+  /* 窄屏/手机预览:触控高度与全宽 */
+  .oa-money-field :deep(.el-input__wrapper) {
+    min-height: 40px;
+  }
+
+  .oa-money-field__readonly {
+    width: 100%;
+    word-break: break-word;
+  }
+
+  .oa-money-field__num {
+    display: block;
+    line-height: 1.5;
+  }
+
   .oa-money-field__upper {
   .oa-money-field__upper {
     margin-top: 6px;
     margin-top: 6px;
     font-size: 12px;
     font-size: 12px;
-    line-height: 1.4;
+    line-height: 1.5;
     color: var(--mint-text-muted, #64748b);
     color: var(--mint-text-muted, #64748b);
+    word-break: break-word;
+    overflow-wrap: anywhere;
+  }
+
+  @media (max-width: 767px) {
+    .oa-money-field :deep(.el-input__wrapper) {
+      min-height: 44px;
+    }
+
+    .oa-money-field__upper {
+      font-size: 11px;
+    }
   }
   }
 </style>
 </style>