This commit is contained in:
zlwu committed 2026-07-06 19:41:24 +08:00
1 parent cdaa0ac273
commit b7cd657f32
32 files changed
+2885 -1936

No files matched your search

+1 -1
View File
@@ -1 +1 @@
56839
35093
+1 -1
View File
@@ -2811,7 +2811,7 @@ def align_fq_payment_details_payload(
pay: BizProtocolPayment,
details: Optional[list],
) -> list:
if pay.pay_mode != "fq" or not pay.installment_count or int(pay.installment_count) < 1:
if pay.pay_mode not in ("fq", "djfq") or not pay.installment_count or int(pay.installment_count) < 1:
return list(details or [])
count = int(pay.installment_count)
rows = [_normalize_fields(dict(d)) for d in (details or [])]
@@ -48,22 +48,16 @@ def validate_payment_details_for_save(
yjf_rows = [r for r in rows if (r.get("pay_status") or r.get("payStatus")) == "yjf"]
if pay_mode == "dj":
if len(dfj_rows) > 1:
raise ValueError("趸交模式仅允许一条待缴费明细")
remaining = round(contract - paid_sum, 2)
if dfj_rows:
due = _dec(
dfj_rows[0].get("due_amount")
if dfj_rows[0].get("due_amount") is not None
else dfj_rows[0].get("dueAmount")
total_due = sum(
_dec(r.get("due_amount") if r.get("due_amount") is not None else r.get("dueAmount"))
for r in rows
)
if contract and abs(total_due - contract) > 0.015:
raise ValueError(
f"趸交各明细应缴金额之和应为 {contract:.2f},当前为 {total_due:.2f}"
)
expected = remaining if yjf_rows else contract
if abs(due - expected) > 0.015:
raise ValueError(
f"趸交待缴金额应为 {expected:.2f},当前为 {due:.2f}"
)
elif pay_mode == "fq":
elif pay_mode in ("fq", "djfq"):
count = int(installment_count or 0)
if count < 1:
raise ValueError("分期模式须填写分期期数")
@@ -76,8 +70,6 @@ def validate_payment_details_for_save(
if is_legacy_installment_ratios(values, Decimal(str(contract)) if contract else None):
if abs(sum(values) - 100.0) > 0.01:
raise ValueError("缴费比例之和须等于 100%")
elif contract and abs(sum(values) - contract) > 0.015:
raise ValueError("分期金额合计须等于合同金额")
if len(rows) != count:
raise ValueError(f"分期明细条数应等于分期期数({count})")
fq_forbidden = frozenset({"jfqrz", "tfqrz"})
@@ -85,12 +77,6 @@ def validate_payment_details_for_save(
status = (r.get("pay_status") or r.get("payStatus") or "dfj").strip()
if status in fq_forbidden:
raise ValueError("分期模式下存在不可保存的缴费状态")
total_due = sum(
_dec(r.get("due_amount") if r.get("due_amount") is not None else r.get("dueAmount"))
for r in rows
)
if contract and abs(total_due - contract) > 0.015:
raise ValueError("分期各期应缴金额之和应等于合同金额")
if yjf_rows:
max_paid_no = max(
int(r.get("installment_no") or r.get("installmentNo") or 0)
@@ -14,7 +14,7 @@ from app.models.models import (
BizProtocolPaymentDetail,
)
_PAY_MODE_LABELS = {"dj": "趸交", "fq": "分期"}
_PAY_MODE_LABELS = {"dj": "趸交", "fq": "分期", "djfq": "趸交+分期"}
_PAY_STATUS_LABELS = {
"dfj": "待缴费",
"jfqrz": "缴费确认中",
@@ -57,7 +57,7 @@ def _protocol_status_hint(pay_mode: str, statuses: List[str]) -> str:
paid = counts.get("yjf", 0)
confirm = counts.get("jfqrz", 0)
pending = counts.get("dfj", 0)
if pay_mode == "fq":
if pay_mode in ("fq", "djfq"):
parts: List[str] = ["分期"]
if paid:
parts.append(f"{paid} 期已缴")
@@ -105,7 +105,7 @@ def _fmt_ymd(val: Any) -> str:
def _payment_detail_row(d: BizProtocolPaymentDetail, pay_mode: str, idx: int) -> Dict[str, Any]:
st = (d.pay_status or "").strip()
inst_no = d.installment_no
if pay_mode == "fq" and inst_no is not None:
if pay_mode in ("fq", "djfq") and inst_no is not None:
label = f"第{inst_no}期"
else:
label = f"缴费{idx + 1}"
@@ -153,7 +153,7 @@ def build_protocol_payment_summary(
"receivedAmount": received,
"pendingAmount": pending,
"statusHint": hint,
"installmentCount": len(detail_rows) if pay_mode == "fq" else None,
"installmentCount": len(detail_rows) if pay_mode in ("fq", "djfq") else None,
"paymentDetails": detail_rows,
**overall,
}
+29 -4
View File
@@ -14,18 +14,29 @@ class TestValidatePaymentDetailsForSave(unittest.TestCase):
[{"payStatus": "dfj", "dueAmount": 10000}],
)
def test_dj_rejects_multiple_dfj(self):
def test_dj_allows_multiple_dfj_when_sum_matches(self):
validate_payment_details_for_save(
"dj",
10000,
None,
[
{"payStatus": "dfj", "dueAmount": 2000},
{"payStatus": "dfj", "dueAmount": 8000},
],
)
def test_dj_rejects_multiple_dfj_wrong_sum(self):
with self.assertRaises(ValueError) as ctx:
validate_payment_details_for_save(
"dj",
10000,
None,
[
{"payStatus": "dfj", "dueAmount": 5000},
{"payStatus": "dfj", "dueAmount": 5000},
{"payStatus": "dfj", "dueAmount": 2000},
{"payStatus": "dfj", "dueAmount": 7000},
],
)
self.assertIn("仅允许一条待缴费", str(ctx.exception))
self.assertIn("之和应为", str(ctx.exception))
def test_dj_partial_paid_remaining(self):
validate_payment_details_for_save(
@@ -50,6 +61,20 @@ class TestValidatePaymentDetailsForSave(unittest.TestCase):
],
)
def test_djfq_same_as_fq(self):
validate_payment_details_for_save(
"djfq",
41000,
4,
[
{"payStatus": "dfj", "installmentNo": 1, "dueAmount": 29360},
{"payStatus": "dfj", "installmentNo": 2, "dueAmount": 3880},
{"payStatus": "dfj", "installmentNo": 3, "dueAmount": 3880},
{"payStatus": "dfj", "installmentNo": 4, "dueAmount": 3880},
],
installment_ratios=[29360, 3880, 3880, 3880],
)
def test_fq_count_mismatch(self):
with self.assertRaises(ValueError):
validate_payment_details_for_save(
@@ -0,0 +1,596 @@
# 采集协议单 · 缴费默认定价 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 按「样本数 × 缴费模式」在新建/首次选模式时填充默认合同金额与分期模板;支持双样本 `djfq`(趸交+分期);趸交允许多条缴费明细;样本数/样本价变化不再自动改合同金额。
**Architecture:** 前端新增 `protocol-payment-presets.ts` 集中定价表与 `applyPaymentPreset()`;`PayMode` 扩展为 `dj | fq | djfq`,分期逻辑通过 `isInstallmentPayMode()` 统一处理 `fq` 与 `djfq`;后端校验放宽趸交多 `dfj`、合计校验,`djfq` 走分期分支;方案 B 用 `paymentDefaultsApplied` 标记避免重复填默认。
**Tech Stack:** Vue 3 · TypeScript · Element Plus · FastAPI · Python unittest · `tsx`(前端纯函数冒烟)
**Design spec:** `docs/superpowers/specs/2026-07-06-protocol-payment-defaults-design.md`
---
## File Map
| 文件 | 职责 |
|------|------|
| `web-admin/src/utils/protocol-payment-presets.ts` | **新建** 定价表、`getActiveSampleCount`、`applyPaymentPreset`、`isInstallmentPayMode` |
| `web-admin/scripts/test-protocol-payment-presets.ts` | **新建** 定价表冒烟脚本(`tsx` 运行) |
| `web-admin/src/utils/protocol-pay-mode-switch.ts` | `PayMode` 含 `djfq`;趸交多明细;切换时用 preset 重建 |
| `web-admin/src/utils/protocol-payment-installment.ts` | `alignFq*` / `syncFq*` 识别 `djfq` |
| `web-admin/src/utils/status-tag-registry.ts` | `djfq` 标签 |
| `web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue` | 模式 UI、首次默认、去掉样本价联动、保存校验 |
| `web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue` | `djfq` 只读展示 |
| `web-admin/src/components/business/protocol-payment-detail-readonly/index.vue` | 分期字段展示含 `djfq` |
| `web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderProtocolPaymentPanel.vue` | 摘要 `djfq` 按分期展示 |
| `web-admin/src/utils/protocol-standard-import-parse.ts` | (P1)识别「趸交+分期」 |
| `backend/app/services/protocol_pay_mode_switch.py` | 趸交多明细合计校验;`djfq`→分期分支 |
| `backend/app/crud/collection_protocol.py` | `align_fq_*` 等 `djfq` 与 `fq` 同等 |
| `backend/app/services/qc_work_order_fee_summary.py` | `djfq` 标签与分期摘要 |
| `backend/tests/test_protocol_pay_mode_switch.py` | 更新/新增校验单测 |
| `backend/tests/test_protocol_payment_presets.py` | **新建**(可选)后端不依赖 preset,本任务以前端脚本为主 |
---
## 依赖顺序
```text
Task 1 (presets 纯函数)
→ Task 2 (后端校验)
→ Task 3 (pay-mode-switch + installment 工具)
→ Task 4 (标签/只读组件)
→ Task 5 (ProtocolFormDialog 主改动)
→ Task 6 (导入 P1 + 收尾 QA)
```
---
### Task 1: 定价表纯函数与冒烟测试
**Files:**
- Create: `web-admin/src/utils/protocol-payment-presets.ts`
- Create: `web-admin/scripts/test-protocol-payment-presets.ts`
- [ ] **Step 1: 创建 `protocol-payment-presets.ts`**
```typescript
// web-admin/src/utils/protocol-payment-presets.ts
export type PayMode = 'dj' | 'fq' | 'djfq'
export type PaymentPreset = {
contractAmount: number
installmentCount?: number
installmentAmounts?: number[]
}
const PRESETS: Record<1 | 2, Partial<Record<PayMode, PaymentPreset>>> = {
1: {
dj: { contractAmount: 22880 },
fq: {
contractAmount: 25880,
installmentCount: 6,
installmentAmounts: [6480, 3880, 3880, 3880, 3880, 3880]
}
},
2: {
dj: { contractAmount: 37880 },
djfq: {
contractAmount: 41000,
installmentCount: 5,
installmentAmounts: [22880, 6480, 3880, 3880, 3880]
},
fq: {
contractAmount: 44000,
installmentCount: 5,
installmentAmounts: [12960, 7760, 7760, 7760, 7760]
}
}
}
export function getActiveSampleCount(
samples: Array<{ sampleStatus?: string }>
): 1 | 2 {
const n = samples.filter((s) => String(s.sampleStatus || '') !== 'yzf').length
return (n >= 2 ? 2 : 1) as 1 | 2
}
export function resolvePaymentPreset(
sampleCount: 1 | 2,
payMode: PayMode
): PaymentPreset | null {
return PRESETS[sampleCount]?.[payMode] ?? null
}
export function isInstallmentPayMode(payMode: string): boolean {
return payMode === 'fq' || payMode === 'djfq'
}
export function availablePayModes(sampleCount: 1 | 2): PayMode[] {
return sampleCount === 2 ? ['dj', 'djfq', 'fq'] : ['dj', 'fq']
}
/** 校验双样本才能用 djfq */
export function isPayModeValidForSampleCount(
payMode: PayMode,
sampleCount: 1 | 2
): boolean {
return availablePayModes(sampleCount).includes(payMode)
}
```
- [ ] **Step 2: 创建冒烟脚本**
```typescript
// web-admin/scripts/test-protocol-payment-presets.ts
import assert from 'node:assert/strict'
import {
resolvePaymentPreset,
getActiveSampleCount,
isInstallmentPayMode,
isPayModeValidForSampleCount
} from '../src/utils/protocol-payment-presets'
const fq1 = resolvePaymentPreset(1, 'fq')!
assert.equal(fq1.contractAmount, 25880)
assert.deepEqual(fq1.installmentAmounts, [6480, 3880, 3880, 3880, 3880, 3880])
assert.equal(
fq1.installmentAmounts!.reduce((s, v) => s + v, 0),
fq1.contractAmount
)
const djfq2 = resolvePaymentPreset(2, 'djfq')!
assert.equal(djfq2.contractAmount, 41000)
assert.equal(djfq2.installmentCount, 5)
assert.equal(getActiveSampleCount([{ sampleStatus: 'ycj' }]), 1)
assert.equal(getActiveSampleCount([{ sampleStatus: 'ycj' }, { sampleStatus: 'yzf' }]), 1)
assert.equal(
getActiveSampleCount([{ sampleStatus: 'ycj' }, { sampleStatus: 'ycj' }]),
2
)
assert.equal(isInstallmentPayMode('djfq'), true)
assert.equal(isInstallmentPayMode('dj'), false)
assert.equal(isPayModeValidForSampleCount('djfq', 1), false)
assert.equal(isPayModeValidForSampleCount('djfq', 2), true)
console.log('protocol-payment-presets: OK')
```
- [ ] **Step 3: 运行冒烟**
```bash
cd web-admin && pnpm exec tsx scripts/test-protocol-payment-presets.ts
```
Expected: 输出 `protocol-payment-presets: OK`,exit 0。
---
### Task 2: 后端保存校验(趸交多明细 + djfq)
**Files:**
- Modify: `backend/app/services/protocol_pay_mode_switch.py`
- Modify: `backend/tests/test_protocol_pay_mode_switch.py`
- [ ] **Step 1: 修改 `validate_payment_details_for_save` 趸交分支**
将 `dj` 分支中「仅 1 条 dfj」改为「全部明细应缴合计 = 合同金额」:
```python
# backend/app/services/protocol_pay_mode_switch.py — pay_mode == "dj" 分支替换为:
if pay_mode == "dj":
total_due = sum(
_dec(r.get("due_amount") if r.get("due_amount") is not None else r.get("dueAmount"))
for r in rows
)
if contract and abs(total_due - contract) > 0.015:
raise ValueError(
f"趸交各明细应缴金额之和应为 {contract:.2f},当前为 {total_due:.2f}"
)
# 保留:存在 yjf 时,dfj 行 due 之和应等于 remaining(可选细化为与 total 一致即可)
```
删除:
```python
if len(dfj_rows) > 1:
raise ValueError("趸交模式仅允许一条待缴费明细")
```
以及单条 `dfj` 的 `expected` 校验(已由 `total_due` 覆盖)。
- [ ] **Step 2: `djfq` 走分期分支**
```python
installment_modes = ("fq", "djfq")
# ...
elif pay_mode in installment_modes:
# 原 fq 逻辑不变
```
- [ ] **Step 3: 更新单测**
`test_dj_rejects_multiple_dfj` 改名为 `test_dj_allows_multiple_dfj_when_sum_matches`:
```python
def test_dj_allows_multiple_dfj_when_sum_matches(self):
validate_payment_details_for_save(
"dj",
10000,
None,
[
{"payStatus": "dfj", "dueAmount": 2000},
{"payStatus": "dfj", "dueAmount": 8000},
],
)
def test_dj_rejects_multiple_dfj_wrong_sum(self):
with self.assertRaises(ValueError) as ctx:
validate_payment_details_for_save(
"dj",
10000,
None,
[
{"payStatus": "dfj", "dueAmount": 2000},
{"payStatus": "dfj", "dueAmount": 7000},
],
)
self.assertIn("之和应为", str(ctx.exception))
```
新增:
```python
def test_djfq_same_as_fq(self):
validate_payment_details_for_save(
"djfq",
41000,
5,
[
{"payStatus": "dfj", "installmentNo": 1, "dueAmount": 22880},
{"payStatus": "dfj", "installmentNo": 2, "dueAmount": 6480},
{"payStatus": "dfj", "installmentNo": 3, "dueAmount": 3880},
{"payStatus": "dfj", "installmentNo": 4, "dueAmount": 3880},
{"payStatus": "dfj", "installmentNo": 5, "dueAmount": 3880},
],
installment_ratios=[22880, 6480, 3880, 3880, 3880],
)
```
- [ ] **Step 4: 运行后端单测**
```bash
cd backend && source venv/bin/activate && python -m pytest tests/test_protocol_pay_mode_switch.py -v
```
Expected: 全部 PASS。
- [ ] **Step 5: 修改 `collection_protocol.py` 分期对齐**
约 L2814,将:
```python
if pay.pay_mode != "fq" or not pay.installment_count:
```
改为:
```python
if pay.pay_mode not in ("fq", "djfq") or not pay.installment_count:
```
全文检索 `pay_mode != "fq"` / `pay_mode == "fq"` 在同文件内一并调整。
---
### Task 3: 前端工具层(pay-mode-switch + installment)
**Files:**
- Modify: `web-admin/src/utils/protocol-pay-mode-switch.ts`
- Modify: `web-admin/src/utils/protocol-payment-installment.ts`
- [ ] **Step 1: 扩展 `PayMode` 并导入 preset**
```typescript
// protocol-pay-mode-switch.ts 顶部
import {
type PayMode,
isInstallmentPayMode,
resolvePaymentPreset,
getActiveSampleCount
} from '@/utils/protocol-payment-presets'
export type { PayMode }
```
- [ ] **Step 2: `canAddPaymentDetail` 趸交始终可添加**
```typescript
export function canAddPaymentDetail(payMode: string, _details: DetailLike[]): boolean {
if (isInstallmentPayMode(payMode)) return true
return true // dj:允许多条明细
}
```
- [ ] **Step 3: `evaluatePayModeSwitch` / `applyPayModeSwitch` 支持 `djfq`**
- `dj` → `djfq`:与 `dj` → `fq` 相同(无已缴才允许)
- `djfq` → `dj`:与 `fq` → `dj`
- `djfq` ↔ `fq`:按分期规则处理
- `applyPayModeSwitch` 重建分期明细时,从 `resolvePaymentPreset(sampleCount, toMode)` 取 `installmentAmounts`,无 preset 时回退 `defaultInstallmentAmounts`
- [ ] **Step 4: `protocol-payment-installment.ts`**
将所有 `payment.payMode !== 'fq'` / `=== 'fq'` 改为使用 `isInstallmentPayMode(payment.payMode as string)`(从 presets 导入)。
- [ ] **Step 5: 类型检查**
```bash
cd web-admin && pnpm exec vue-tsc --noEmit 2>&1 | head -40
```
Expected: 无新增 error(允许既有 warning)。
---
### Task 4: 标签与只读展示
**Files:**
- Modify: `web-admin/src/utils/status-tag-registry.ts`
- Modify: `web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue`
- Modify: `web-admin/src/components/business/protocol-payment-detail-readonly/index.vue`
- Modify: `web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderProtocolPaymentPanel.vue`
- Modify: `backend/app/services/qc_work_order_fee_summary.py`
- [ ] **Step 1: `status-tag-registry.ts` 增加**
```typescript
djfq: { label: '趸交+分期', tagType: 'warning' } // 或 primary,与 fq 区分即可
```
- [ ] **Step 2: 只读组件分期条件**
`payment?.payMode === 'fq'` → `isInstallmentPayMode(payment?.payMode || '')`(ViewDialog、readonly、QcWorkOrderProtocolPaymentPanel)。
- [ ] **Step 3: 后端 `_PAY_MODE_LABELS`**
```python
_PAY_MODE_LABELS = {"dj": "趸交", "fq": "分期", "djfq": "趸交+分期"}
```
`build_protocol_payment_summary` 内 `pay_mode == "fq"` 改为 `pay_mode in ("fq", "djfq")`。
---
### Task 5: ProtocolFormDialog 主改动
**Files:**
- Modify: `web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue`
- [ ] **Step 1: 缴费模式单选 UI**
```vue
<el-radio-group v-model="formData.payMode" @change="onPayModeChange">
<el-radio label="dj">趸交</el-radio>
<el-radio v-if="activeSampleCount === 2" label="djfq">趸交+分期</el-radio>
<el-radio label="fq">分期</el-radio>
</el-radio-group>
```
`activeSampleCount` = `computed(() => getActiveSampleCount(formData.samples))`。
- [ ] **Step 2: 分期区块条件**
`formData.payMode === 'fq'` → `isInstallmentPayMode(formData.payMode)`(模板内所有分期表单项、分期金额行)。
- [ ] **Step 3: 新增状态 `paymentDefaultsApplied`**
```typescript
const paymentDefaultsApplied = ref<Record<PayMode, boolean>>({
dj: false,
fq: false,
djfq: false
})
function applyPaymentPresetForMode(payMode: PayMode) {
const preset = resolvePaymentPreset(getActiveSampleCount(formData.samples), payMode)
if (!preset) return
formData.contractAmount = preset.contractAmount
if (isInstallmentPayMode(payMode) && preset.installmentCount) {
formData.installmentCount = preset.installmentCount
formData.installmentIntervalMonths = formData.installmentIntervalMonths ?? 6
formData.installmentStartDate =
formData.installmentStartDate || defaultInstallmentStartDateYmd()
formData.installmentAdvanceDays =
formData.installmentAdvanceDays ?? DEFAULT_INSTALLMENT_ADVANCE_DAYS
formData.installmentRatios = [...(preset.installmentAmounts || [])]
formData.paymentDetails = generateInitialFqDetails(
getFqParamsFromForm(),
preset.contractAmount
)
initPaymentDetailExpandFlags(formData.paymentDetails)
markPayDetailLocks(formData.paymentDetails)
syncLastConfirmedFqParams()
} else {
formData.paymentDetails = [
createPaymentDetailRow({
dueAmount: preset.contractAmount,
installmentDueDate: formData.lumpSumDeadline || '',
expanded: true
})
]
}
paymentDefaultsApplied.value[payMode] = true
}
```
- [ ] **Step 4: 改造 `onPayModeChange`**
在既有 `evaluatePayModeSwitch` 之前/之后:
```typescript
async function onPayModeChange(newMode: PayMode) {
const oldMode = lastConfirmedPayMode.value
// ... 既有 evaluate + apply 逻辑 ...
if (!paymentDefaultsApplied.value[newMode]) {
applyPaymentPresetForMode(newMode)
}
lastConfirmedPayMode.value = newMode
}
```
新建弹窗 `resetForm` / `openCreate` 时重置 `paymentDefaultsApplied` 全 `false`;编辑加载详情后全部标 `true`(不覆盖库内数据)。
- [ ] **Step 5: 去掉样本价 → 合同额自动联动**
`onSamplePriceChange` 改为空实现或仅刷新弱提示:
```typescript
function onSamplePriceChange() {
// 方案 B:不调用 syncContractAmount()
}
```
删除或停用 `syncContractAmount()` 内对 `formData.contractAmount` 的写入;保留函数供「恢复默认」P1 使用时可再启用。
可选弱提示(缴费 Tab):
```typescript
const samplePriceContractHint = computed(() => {
const sampleTotal = formData.samples
.filter((s) => s.sampleStatus !== 'yzf')
.reduce((sum, s) => sum + (Number(s.price) || 0), 0)
if (formData.contractAmount == null) return ''
if (Math.abs(sampleTotal - Number(formData.contractAmount)) > 0.01) {
return `样本价格合计 ${fmtMoney(sampleTotal)} 与合同金额不一致,请核对`
}
return ''
})
```
- [ ] **Step 6: 保存前校验 `djfq` 样本数**
在 `handleSubmit` 缴费校验段:
```typescript
if (
formData.payMode === 'djfq' &&
getActiveSampleCount(formData.samples) !== 2
) {
await ElMessageBox.alert('「趸交+分期」仅适用于 2 个样本,请切换缴费模式', '无法保存', {
type: 'warning'
})
return
}
if (!isPayModeValidForSampleCount(formData.payMode as PayMode, getActiveSampleCount(formData.samples))) {
await ElMessageBox.alert('当前缴费模式与样本数量不匹配', '无法保存', { type: 'warning' })
return
}
```
- [ ] **Step 7: 标准导入后标记默认已应用**
`applyPaymentFromImport` 末尾:
```typescript
paymentDefaultsApplied.value = { dj: true, fq: true, djfq: true }
```
- [ ] **Step 8: 编辑加载 `lastConfirmedPayMode`**
```typescript
lastConfirmedPayMode.value = (['fq', 'djfq'].includes(formData.payMode)
? formData.payMode
: 'dj') as PayMode
```
- [ ] **Step 9: 手动验证(开发环境)**
1. 新建 → 添加 1 样本 → 首次选「分期」→ 合同 25880、6 期金额正确
2. 再添加第 2 样本 → 合同/明细**不变**
3. 新建 2 样本 → 选「趸交+分期」→ 41000、5 期
4. 趸交 → 添加 2 条明细 2000+20880 → 保存成功
5. 2 样本 `djfq` 删 1 样本 → 保存拦截
---
### Task 6: 标准导入(P1)与收尾
**Files:**
- Modify: `web-admin/src/utils/protocol-standard-import-parse.ts`
- Run: `./scripts/update-graph.sh frontend`
- [ ] **Step 1: 导入识别 `djfq`**
`parsePayMode` 中:
```typescript
if (/趸交\s*[++]\s*分期|趸交.*分期/.test(v)) return { payMode: 'djfq' }
```
`ProtocolStandardImportParsed.payMode` 类型改为 `'dj' | 'fq' | 'djfq'`。
- [ ] **Step 2: 更新知识图谱**
```bash
./scripts/update-graph.sh frontend
```
- [ ] **Step 3: Lint**
```bash
cd web-admin && pnpm lint
cd backend && python -m pytest tests/test_protocol_pay_mode_switch.py -v
```
- [ ] **Step 4: 更新 design spec 状态**
`docs/superpowers/specs/2026-07-06-protocol-payment-defaults-design.md` 表头 `状态` → `已实现`。
---
## Spec 覆盖自检
| Spec § | Task |
|--------|------|
| §3 默认定价表 | Task 1 |
| §4 方案 B 填充时机 | Task 5 Step 3–4、7 |
| §5 趸交多明细 | Task 2、3、5 |
| §5.2 djfq 分期 | Task 2–5 |
| §6 模式切换修订 | Task 3、5 |
| §7 pay_mode=djfq | Task 2、4 |
| §8 UI | Task 4、5 |
| §8.3 样本价解耦 | Task 5 Step 5 |
| §9–10 前后端摘要 | Task 2–5 |
| §11 测试要点 | 各 Task Step 验证 |
| §4.3 恢复默认 P1 | **范围外**(未列入 Task) |
---
## 人工回归清单(提测)
- [ ] 新建 1 样本首次 `fq`:25880 / 6 期模板
- [ ] 新建 2 样本首次 `djfq`:41000 / 5 期
- [ ] 样本数 1→2 后金额不变
- [ ] 趸交 2+ 条 `dfj` 保存成功
- [ ] 分期合计 ≠ 合同额:前后端拦截
- [ ] 已缴锁定 + 模式切换:仍符合 2026-06-04
- [ ] 查看弹窗 / 质检费用摘要:`djfq` 标签与期次表
- [ ] 标准导入多笔趸交转账保存成功
---
## 执行方式
**Plan 已保存至** `docs/superpowers/plans/2026-07-06-protocol-payment-defaults.md`。
**两种执行方式:**
1. **Subagent-Driven(推荐)** — 每 Task 派发独立子代理,Task 间人工/代理复核
2. **Inline Execution** — 本会话按 Task 1→6 连续实现,每 Task 末 checkpoint
告知选用哪种方式即可开始实现。
@@ -0,0 +1,214 @@
# 采集协议单 · 缴费默认定价与明细规则设计
**日期**:2026-07-06
**范围**:`web-admin` 采集协议单新增/编辑(`ProtocolFormDialog.vue`)、标准信息导入、后端缴费保存校验
**前置 spec**:[2026-06-04-protocol-pay-mode-switch-design.md](./2026-06-04-protocol-pay-mode-switch-design.md)、[2026-06-10-protocol-installment-config-design.md](./2026-06-10-protocol-installment-config-design.md)
**状态**:已实现
---
## 1. 背景与目标
业务方给出「样本数 × 缴费模式」下的**默认合同金额、默认期数、默认各期金额**。这些数值仅作录入辅助;用户可自由修改合同金额、分期期数、各期金额;趸交也可拆成多条缴费明细(如预付款 + 尾款)。
当前实现与目标的主要差距:
| 维度 | 现状 | 目标 |
|------|------|------|
| 合同金额来源 | 非作废样本 `price` 之和 | 按定价表填**默认**,用户可改;样本数变化**不**自动改金额 |
| 分期默认金额 | 均分或历史 50/10/10… | 固定模板(6,480 / 3,880…) |
| 分期默认期数 | 一律 6 期 | 1 样本 6 期;2 样本分期/混合 5 期 |
| 缴费模式 | `dj` / `fq` | 增加双样本 **趸交+分期** `djfq` |
| 趸交明细条数 | 后端禁止多条 `dfj` | 允许多条,合计 = 合同金额 |
| 默认填充时机 | 切分期、改样本价等会联动 | **方案 B**:仅新建或**首次**选缴费模式时填默认 |
---
## 2. 决策记录
| 议题 | 决定 |
|------|------|
| 默认值性质 | 仅初始填充,**不锁定**;用户可改合同金额、期数、各期金额 |
| 样本数变化 | **不**自动改合同金额、期数、明细(方案 B) |
| 缴费模式变化 | **首次**从空/未选切到某模式时填该模式默认;已选过且用户已编辑后,再次切换仍走既有「模式切换」确认流(见 §6) |
| 单样本分期默认合同额 | **25,880**(6,480 + 3,880×5) |
| 趸交默认合同额 | 优惠价:1 样本 **22,880**;2 样本 **37,880** |
| 双样本「趸交+分期」 | 新模式 `djfq`,默认 **41,000**,5 期:22,880、6,480、3,880、3,880、3,880 |
| 双样本纯分期 | 默认 **44,000**,5 期:12,960、7,760、7,760、7,760、7,760 |
| 趸交多条明细 | 允许;`Σ dueAmount(全部明细)= contractAmount`;保存时不再限制「仅 1 条 dfj」 |
| 样本 `price` 字段 | 仍维护样本单价;与合同默认**解耦**;导入/保存时可提示样本价合计与合同额不一致,**不阻断** |
| 定价表存放 | 前端 `protocol-payment-presets.ts`(常量);后端镜像校验逻辑不依赖默认表,只校验合计与条数 |
---
## 3. 默认定价表
有效样本数 = 当前协议单内 `sampleStatus !== 'yzf'` 的样本条数,取值 **1** 或 **2**(与现网一致,不支持 0 样本保存缴费)。
| 样本数 | `pay_mode` | 默认合同金额 | 默认期数 | 默认各期金额(元) |
|--------|------------|-------------|----------|-------------------|
| 1 | `dj` | 22,880 | — | 单条明细 `dueAmount = 22,880`(用户可拆多条) |
| 1 | `fq` | 25,880 | 6 | 6,480、3,880、3,880、3,880、3,880、3,880 |
| 2 | `dj` | 37,880 | — | 单条明细 `dueAmount = 37,880`(用户可拆多条) |
| 2 | `djfq` | 41,000 | 5 | 22,880、6,480、3,880、3,880、3,880 |
| 2 | `fq` | 44,000 | 5 | 12,960、7,760、7,760、7,760、7,760 |
**标签**:`djfq` 展示为「趸交+分期」;字典 `status-tag-registry` / 后端摘要增加 `djfq` → `趸交+分期`。
**双样本时缴费模式选项**:`dj`、`djfq`、`fq`(隐藏或禁用 `djfq` 当样本数 ≠ 2)。
---
## 4. 默认填充时机(方案 B)
### 4.1 会填充默认的场景
1. **新建协议单**,用户**第一次**选择缴费模式(`pay_mode` 从初始 `dj` 切到 `fq` / `djfq`,或新建后首次进入缴费 Tab 且明细为空时的初始化,与产品确认以「首次 `onPayModeChange` 且该模式从未应用过」为准)。
2. **新建协议单**,打开时若默认 `pay_mode=dj` 且 `paymentDetails` 为空:按 **当前样本数** 填趸交默认(合同额 + 1 条明细)。若此时样本数仍为 0,待用户添加第 1 个样本后**不**自动重填(方案 B);用户选模式或手动点「套用默认」时再填。
> **实现建议**:维护 `paymentDefaultsApplied: Record<PayMode, boolean>`(或 `lastAppliedPresetKey: string | null`)。某 `pay_mode` 第一次生效时调用 `applyPaymentPreset(sampleCount, payMode)`,并标记已应用。
### 4.2 不会自动改金额的场景
- 增删样本、样本作废/恢复
- 修改样本 `price`
- 修改合同金额后回头改样本
用户自行改合同额、期数、明细;仅触发**合计校验**与既有「分期参数变更 / 模式切换」确认流。
### 4.3 可选辅助(P1,非本期必须)
缴费主表旁「恢复默认」按钮:按当前样本数 + 当前 `pay_mode` 重新套用 §3 定价表,**需二次确认**。
---
## 5. 缴费明细与校验
### 5.1 趸交 `dj`
- 明细 **≥ 1** 条;用户可通过「添加明细」增加(含预付款场景)。
- `canAddPaymentDetail`:改为在 `dj` 下**始终允许**添加(或:待缴条数无硬上限,仅校验合计)。
- 保存校验:
- `Σ dueAmount(全部明细)` 与 `contractAmount` 差额 ≤ 0.01;
- 允许多条 `dfj`;
- 若存在 `yjf`:`Σ yjf.dueAmount + Σ dfj.dueAmount` 仍须等于 `contractAmount`(与现 `remaining` 逻辑一致,但允许多条 `dfj` 分摊剩余)。
### 5.2 分期 `fq` 与混合 `djfq`
- 明细条数 **=** `installment_count`(与现 `fq` 一致)。
- `installment_ratios` 存**各期金额(元)**,非百分比(与现网一致)。
- `Σ 各期 dueAmount = contractAmount`。
- `djfq` 在数据层按分期处理:每期有 `installmentNo`、应缴日;第 1 期金额语义为「趸交部分」,不做单独子表。
### 5.3 与标准信息导入
- 导入解析到的 `contractAmount`、多笔 `transfer` **覆盖**默认填充(与 [2026-07-02-protocol-standard-import-design.md](./2026-07-02-protocol-standard-import-design.md) 一致)。
- 导入后标记 `paymentDefaultsApplied` 全部为已应用,避免再自动覆盖。
---
## 6. 缴费模式切换(在 2026-06-04 上的修订)
既有切换规则(已缴锁定、确认流)**保留**。下列条目**修订**原 spec:
| 原规则 | 新规则 |
|--------|--------|
| 趸交仅 1 条有效明细 | 趸交可多条;切换生成时仍可合并为 1 条**默认**,用户随后可拆 |
| `pay_mode` 仅 `dj`/`fq` | 增加 `djfq`;`djfq` ↔ `fq` 按分期规则;`djfq` → `dj` 同 `fq` → `dj` |
| 切换后分期金额均分 | 切换后若走「重建明细」,使用 §3 对应默认各期金额(非均分) |
**双样本 → 单样本**(或反向):不自动改合同/明细(方案 B);若当前 `pay_mode=djfq` 且样本数变为 1,**阻断保存**并提示「请切换缴费模式」,或自动要求用户改模式(推荐:**保存前校验**样本数与 `djfq` 一致)。
---
## 7. 数据模型
### 7.1 `biz_protocol_payment.pay_mode`
| 值 | 含义 |
|----|------|
| `dj` | 趸交 |
| `fq` | 分期 |
| `djfq` | 趸交+分期(仅双样本) |
字段长度 `varchar(8)` 足够。无需新列;`installment_count` / `installment_ratios` 在 `djfq` 与 `fq` 用法相同。
### 7.2 迁移
- 字典项(若缴费模式走 dict):增加 `djfq`。
- 历史数据无 `djfq`,只读展示兼容即可。
---
## 8. UI
### 8.1 缴费模式
```text
[ 趸交 ] [ 分期 ] // 1 样本
[ 趸交 ] [ 趸交+分期 ] [ 分期 ] // 2 样本
```
### 8.2 分期金额区
- `fq` / `djfq`:展示期数、间隔、开始时间、各期金额输入格;默认期数/金额来自 §3。
- 合计行:`合计 xxx / 合同 xxx`(已有)。
### 8.3 样本价格
- 样本 Tab 的「价格」保留;**移除** `syncContractAmount()` 在样本价变更时对 `contractAmount` 的自动写入(方案 B)。
- 可选弱提示:样本价合计 ≠ 合同金额时展示 `form-tip`(不阻断保存)。
---
## 9. 后端改动摘要
| 文件 | 改动 |
|------|------|
| `protocol_pay_mode_switch.py` | `dj`:删除「仅 1 条 dfj」;改为明细总额校验;支持 `djfq` 走 `fq` 分支 |
| `collection_protocol.py` | 读写 `pay_mode=djfq`;列表/详情标签 |
| `qc_work_order_fee_summary.py` 等 | `djfq` 标签与分期展示 |
---
## 10. 前端改动摘要
| 文件 | 改动 |
|------|------|
| `protocol-payment-presets.ts` | **新建**,§3 定价表 + `applyPaymentPreset()` |
| `protocol-payment-installment.ts` | 默认金额生成改为调用 preset(均分仅作无 preset 时的兜底) |
| `protocol-pay-mode-switch.ts` | `dj` 多明细;`PayMode` 含 `djfq` |
| `ProtocolFormDialog.vue` | 模式 UI、首次填充、去掉样本价→合同额联动 |
| `ProtocolViewDialog.vue` | `djfq` 展示 |
| `status-tag-registry.ts` | `djfq` |
| `protocol-standard-import-parse.ts` | 识别「趸交+分期」文案 → `djfq`(P1) |
---
## 11. 测试要点
- [ ] 新建 1 样本,首次选 `fq` → 合同 25,880,6 期金额正确
- [ ] 新建 2 样本,首次选 `djfq` → 41,000,5 期正确
- [ ] 改样本数 1→2,合同/明细**不变**
- [ ] 趸交添加 2 条明细,合计 = 合同额,保存成功
- [ ] 趸交 2 条 `dfj` 预付款场景,保存成功
- [ ] 修改各期金额后合计 ≠ 合同额,前后端均拦截
- [ ] 模式切换 + 已缴锁定,仍符合 2026-06-04
- [ ] 2 样本 `djfq` 删至 1 样本,保存提示改模式
- [ ] 标准导入多笔转账 + 趸交,保存成功
---
## 12. 范围外
- 定价表后台可配置化(本期写死常量)
- 3 个及以上样本
- 按样本绑定不同缴费模式(每样本独立主表)
---
## 13. 修订记录
| 日期 | 说明 |
|------|------|
| 2026-07-06 | 初稿:默认定价表、方案 B、djfq、趸交多明细 |
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+104 -100
View File
@@ -1,10 +1,10 @@
# Graph Report - src (2026-07-06)
## Corpus Check
- Large corpus: 850 files · ~561,909 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
- Large corpus: 851 files · ~562,525 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
## Summary
- 2845 nodes · 3968 edges · 58 communities detected
- 2839 nodes · 3960 edges · 58 communities detected
- Extraction: 78% EXTRACTED · 22% INFERRED · 0% AMBIGUOUS · INFERRED: 881 edges (avg confidence: 0.8)
- Token cost: 0 input · 0 output
@@ -46,22 +46,22 @@
- [[_COMMUNITY_Community 34|Community 34]]
- [[_COMMUNITY_Community 35|Community 35]]
- [[_COMMUNITY_Community 36|Community 36]]
- [[_COMMUNITY_Community 37|Community 37]]
- [[_COMMUNITY_Community 38|Community 38]]
- [[_COMMUNITY_Community 39|Community 39]]
- [[_COMMUNITY_Community 40|Community 40]]
- [[_COMMUNITY_Community 41|Community 41]]
- [[_COMMUNITY_Community 44|Community 44]]
- [[_COMMUNITY_Community 45|Community 45]]
- [[_COMMUNITY_Community 46|Community 46]]
- [[_COMMUNITY_Community 47|Community 47]]
- [[_COMMUNITY_Community 48|Community 48]]
- [[_COMMUNITY_Community 49|Community 49]]
- [[_COMMUNITY_Community 50|Community 50]]
- [[_COMMUNITY_Community 51|Community 51]]
- [[_COMMUNITY_Community 52|Community 52]]
- [[_COMMUNITY_Community 53|Community 53]]
- [[_COMMUNITY_Community 54|Community 54]]
- [[_COMMUNITY_Community 55|Community 55]]
- [[_COMMUNITY_Community 57|Community 57]]
- [[_COMMUNITY_Community 59|Community 59]]
- [[_COMMUNITY_Community 56|Community 56]]
- [[_COMMUNITY_Community 58|Community 58]]
- [[_COMMUNITY_Community 60|Community 60]]
- [[_COMMUNITY_Community 63|Community 63]]
- [[_COMMUNITY_Community 65|Community 65]]
- [[_COMMUNITY_Community 73|Community 73]]
@@ -70,7 +70,7 @@
## God Nodes (most connected - your core abstractions)
1. `get()` - 279 edges
2. `push()` - 77 edges
2. `push()` - 75 edges
3. `WebSocketClient` - 26 edges
4. `emit()` - 24 edges
5. `set()` - 20 edges
@@ -81,14 +81,14 @@
10. `TableCache` - 14 edges
## Surprising Connections (you probably didn't know these)
- `handleBatchDelete()` --calls--> `withFullscreenLoading()` [INFERRED]
views/basic-info/customer/index.vue → utils/fullscreen-loading.ts
- `buildInboundMetaFields()` --calls--> `push()` [INFERRED]
utils/production-work-order-card-meta.ts → views/operations/storage-facility/index.vue
- `dash()` --calls--> `formatDisplayOrDash()` [INFERRED]
components/business/infusion-schedule-outbound-chain-view/index.vue → utils/datetime-display.ts
components/business/clinical-schedule-payment-detail/index.vue → utils/datetime-display.ts
- `fmtMoney()` --calls--> `formatMoneyDisplay()` [INFERRED]
components/business/clinical-schedule-payment-detail/index.vue → utils/money-format.ts
- `handleAttachmentDownload()` --calls--> `downloadAttachmentFile()` [INFERRED]
components/business/infusion-schedule-outbound-chain-view/index.vue → utils/download-attachment.ts
- `subscribeTokenRefresh()` --calls--> `push()` [INFERRED]
utils/http/index.ts → views/operations/storage-facility/index.vue
@@ -96,91 +96,91 @@
### Community 0 - "Community 0"
Cohesion: 0.01
Nodes (156): fetchAccessLogList(), fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchCellProductSpecList(), fetchCellProductTypeList(), fetchChannelAccount() (+148 more)
Nodes (167): fetchAccessLogList(), fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchCellProductSpecList(), fetchCellProductTypeList(), fetchChannelAccount() (+159 more)
### Community 1 - "Community 1"
Cohesion: 0.03
Nodes (83): batchSavePilotCheckup(), deletePilotCheckupActualItem(), fetchCheckupInstitutionList(), fetchCheckupInstitutionPackageDetail(), fetchCheckupInstitutionPackages(), fetchPilotCheckupDetail(), fetchPilotCheckupList(), markPilotCheckupChecked() (+75 more)
Cohesion: 0.02
Nodes (95): batchSavePilotCheckup(), deletePilotCheckupActualItem(), fetchCheckupInstitutionList(), fetchCheckupInstitutionPackageDetail(), fetchCheckupInstitutionPackages(), fetchPilotCheckupDetail(), fetchPilotCheckupList(), markPilotCheckupChecked() (+87 more)
### Community 2 - "Community 2"
Cohesion: 0.03
Nodes (78): fetchUploadAttachment(), FireworkSystem, handleReportUpload(), handleReportUpload(), walk(), buildBaseFields(), buildFeeSection(), buildInboundBasicSection() (+70 more)
Nodes (54): buildInboundBasicSection(), buildInboundCellStorageViewSections(), buildInboundResearchDonationViewSections(), buildReceptionSection(), buildTemporaryViewSections(), buildTransportSection(), resolveInboundProtocolStatus(), appendTaskResultSections() (+46 more)
### Community 3 - "Community 3"
Cohesion: 0.03
Nodes (62): fetchFullmoonTodoList(), fetchUpdateFullmoonTodoInsurance(), fetchCollectionProtocolAuditList(), fetchCollectionProtocolDetail(), fetchCollectionProtocolList(), fetchCreateProtocolInvoiceRequest(), fetchInvoiceEligiblePayments(), fetchPaymentDetailList() (+54 more)
### Community 4 - "Community 4"
Cohesion: 0.03
Nodes (33): fetchGetMenuList(), ComponentLoader, IframeRouteManager, MenuProcessor, RouteRegistry, RouteTransformer, useAppMode(), useAuth() (+25 more)
### Community 4 - "Community 4"
Cohesion: 0.04
Nodes (65): fetchUploadAttachment(), handleReportUpload(), handleReportUpload(), walk(), async(), for(), handleAttachmentChange(), for() (+57 more)
### Community 5 - "Community 5"
Cohesion: 0.03
Nodes (58): fetchFullmoonTodoList(), fetchUpdateFullmoonTodoInsurance(), fetchCollectionProtocolAuditList(), fetchCollectionProtocolDetail(), fetchCollectionProtocolList(), fetchCreateProtocolInvoiceRequest(), fetchInvoiceEligiblePayments(), fetchPaymentDetailList() (+50 more)
### Community 6 - "Community 6"
Cohesion: 0.02
Nodes (28): fetchAllConsumablePacks(), fetchAllConsumables(), fetchAllConsumableStockLogs(), fetchAllEquipments(), fetchAllOutputTypes(), fetchAllProcessCategories(), fetchAllProcesses(), fetchAllReagents() (+20 more)
### Community 6 - "Community 6"
Cohesion: 0.03
Nodes (42): handleBlur(), handleFocus(), handleInput(), fetchEthnicList(), formatNowDatetime(), formatAmount(), formatAmount(), formatProtocolAmount() (+34 more)
### Community 7 - "Community 7"
Cohesion: 0.03
Nodes (48): buildTemporaryViewSections(), appendTaskResultSections(), batchTasksForPanelSlot(), buildConfiguredOnlySection(), for(), for(), set(), allTabInboundAnchorId() (+40 more)
Nodes (54): fetchMyProductionTasks(), fetchMyQcTaskList(), fetchQcTaskDetail(), fetchQcTaskList(), shouldApplyTriggerSourceFilter(), buildMineTableRows(), buildPresetTasksMap(), buildWorkOrderFromTasks() (+46 more)
### Community 8 - "Community 8"
Cohesion: 0.04
Nodes (49): fetchCheckupInstitutionInvoices(), fetchCheckupInstitutionProjects(), fetchCreateCheckupInstitution(), fetchCreateCheckupInstitutionInvoice(), fetchCreateCheckupInstitutionProject(), fetchUpdateCheckupInstitution(), fetchUpdateCheckupInstitutionInvoice(), fetchUpdateCheckupInstitutionProject() (+41 more)
Nodes (50): fetchCheckupInstitutionInvoices(), fetchCheckupInstitutionProjects(), fetchCreateCheckupInstitution(), fetchCreateCheckupInstitutionInvoice(), fetchCreateCheckupInstitutionProject(), fetchUpdateCheckupInstitution(), fetchUpdateCheckupInstitutionInvoice(), fetchUpdateCheckupInstitutionProject() (+42 more)
### Community 9 - "Community 9"
Cohesion: 0.03
Nodes (41): fetchCreateMenu(), fetchDeleteMenu(), fetchMenuTree(), fetchRoleOptions(), fetchUpdateMenu(), fetchDeleteMaterialBatch(), fetchMaterialBatchList(), fetchMaterialBatchOptions() (+33 more)
Cohesion: 0.04
Nodes (42): buildBaseFields(), buildFeeSection(), buildInboundBasicSection(), buildInboundCellStorageProductionViewSections(), buildInboundResearchDonationProductionViewSections(), buildOutboundBasicExperimentProductionViewSections(), buildOutboundClinicalProductionViewSections(), buildOutboundDetail() (+34 more)
### Community 10 - "Community 10"
Cohesion: 0.04
Nodes (50): fetchMyProductionTasks(), fetchMyQcTaskList(), fetchQcTaskDetail(), fetchQcTaskList(), shouldApplyTriggerSourceFilter(), buildMineTableRows(), buildPresetTasksMap(), buildWorkOrderFromTasks() (+42 more)
Nodes (40): useSettingsHandlers(), useSettingsPanel(), useSettingsState(), RoutePermissionValidator, useCeremony(), useTheme(), if(), walk() (+32 more)
### Community 11 - "Community 11"
Cohesion: 0.04
Nodes (43): useSettingsHandlers(), useSettingsPanel(), useSettingsState(), RoutePermissionValidator, useCeremony(), useTheme(), if(), collectAllPostBindDeptIds() (+35 more)
Cohesion: 0.05
Nodes (53): fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchRefreshToken(), fetchUpdateProfile(), fetchUploadProfileAvatar(), toLogin(), fetchUserInfo() (+45 more)
### Community 12 - "Community 12"
Cohesion: 0.04
Nodes (45): fetchCreateCustomer(), fetchCustomerList(), fetchDeleteCustomer(), fetchUpdateCustomer(), updateInfusionScheduleStatus(), handleDialogClose(), handleAdd(), handleBatchDelete() (+37 more)
Nodes (44): fetchCreateCustomer(), fetchCustomerList(), fetchDeleteCustomer(), fetchUpdateCustomer(), updateInfusionScheduleStatus(), handleDialogClose(), handleAdd(), handleDelete() (+36 more)
### Community 13 - "Community 13"
Cohesion: 0.04
Nodes (36): buildInboundBasicSection(), buildInboundCellStorageViewSections(), buildInboundResearchDonationViewSections(), buildReceptionSection(), buildTransportSection(), resolveInboundProtocolStatus(), protocolStatusDisplay(), maskIdCard() (+28 more)
Nodes (26): handleBlur(), handleFocus(), handleInput(), fetchEthnicList(), formatAmount(), formatAmount(), formatProtocolAmount(), applyDonationBundleToForm() (+18 more)
### Community 14 - "Community 14"
Cohesion: 0.04
Nodes (31): dash(), fmtMoney(), auditorDisplay(), dash(), ensureUnitsInOptions(), expressName(), formatYm(), formatYmd() (+23 more)
### Community 15 - "Community 15"
Cohesion: 0.05
Nodes (34): fetchClinicalPaymentDetail(), fetchClinicalPayments(), fetchClinicalReceiptConfirmed(), fetchClinicalReceiptPending(), fetchClinicalRefundConfirmed(), fetchClinicalRefundPending(), fetchOrderAdjusts(), fetchOrderDetail() (+26 more)
### Community 16 - "Community 16"
### Community 15 - "Community 15"
Cohesion: 0.06
Nodes (37): fetchUserList(), fetchAddTaskStep(), fetchCopyTaskStep(), fetchDeleteTaskStep(), fetchDispatchProductionTaskSteps(), fetchForwardProductionTask(), fetchProductionTaskDetail(), fetchProductionTaskEditorInit() (+29 more)
### Community 16 - "Community 16"
Cohesion: 0.05
Nodes (25): fetchCreateMenu(), fetchDeleteMenu(), fetchMenuTree(), fetchRoleOptions(), fetchUpdateMenu(), collectIds(), postorder(), preorder() (+17 more)
### Community 17 - "Community 17"
Cohesion: 0.08
Nodes (37): formatDatetimeToMinute(), handleChineseDatetimePaste(), parseChineseDatetimeText(), parseBirthOrder(), parseChineseNumeral(), addSampleHint(), earliestCollectTime(), isMaternalBloodEmpty() (+29 more)
### Community 18 - "Community 18"
Cohesion: 0.1
Nodes (2): WebSocketClient, TableCache
Cohesion: 0.06
Nodes (21): fetchQcMaterialBatchList(), fetchQcMaterialBatchOptions(), fetchQcMaterialList(), fetchQcMaterialOptions(), fetchUpdateQcMaterialBatch(), fetchMarkQcMaterialUsageFullyUsed(), financeAttachmentsToUrlString(), for() (+13 more)
### Community 19 - "Community 19"
Cohesion: 0.06
Nodes (20): fetchQcMaterialBatchList(), fetchQcMaterialBatchOptions(), fetchQcMaterialList(), fetchQcMaterialOptions(), fetchUpdateQcMaterialBatch(), fetchMarkQcMaterialUsageFullyUsed(), financeAttachmentsToUrlString(), ensureTabMounted() (+12 more)
Cohesion: 0.09
Nodes (36): attachmentFullUrl(), dash(), handleAttachmentDownload(), handleAttachmentPreview(), buildPaymentFlowSteps(), dash(), formatAmount(), hasFinanceReceipt() (+28 more)
### Community 20 - "Community 20"
Cohesion: 0.08
Nodes (32): toLogin(), base64ToBytes(), bytesToBase64(), decryptJson(), deriveKey(), encryptJson(), fromBase64Url(), hexToBytes() (+24 more)
Cohesion: 0.06
Nodes (13): fetchProductionWorkOrderQcInfo(), dash(), fmtMoney(), auditorDisplay(), dash(), ensureUnitsInOptions(), expressName(), formatYm() (+5 more)
### Community 21 - "Community 21"
Cohesion: 0.07
Nodes (24): fetchMineProductionWorkOrderTabStepCounts(), fetchMineProductionWorkOrderTabStepCountsDeduped(), fetchProductionWorkOrderCellProductOptions(), fetchProductionWorkOrderDetail(), fetchProductionWorkOrderList(), fetchProductionWorkOrderQcDispatchOptions(), fetchProductionWorkOrderQcInfo(), fetchProductionWorkOrderTabStepCounts() (+16 more)
Cohesion: 0.1
Nodes (2): WebSocketClient, TableCache
### Community 22 - "Community 22"
Cohesion: 0.09
@@ -188,44 +188,44 @@ Nodes (8): getDefaultLanguage(), checkStorageCompatibility(), StorageConfig, get
### Community 23 - "Community 23"
Cohesion: 0.08
Nodes (18): fetchProductionReceptionBySampleCode(), fetchProductionReceptionSubmit(), fetchProductionReceptionUpdate(), onUserSelectVisible(), remoteSearchUsers(), buildPayload(), ensureDefaultHandler(), initDialog() (+10 more)
Nodes (20): createEmptySnapshots(), normalizeConfiguredReagents(), normalizeConsumables(), normalizeDataCollections(), normalizeReagents(), normalizeStepOutputs(), serializeAttachments(), serializeEquipmentIds() (+12 more)
### Community 24 - "Community 24"
Cohesion: 0.08
Nodes (20): createEmptySnapshots(), normalizeConfiguredReagents(), normalizeConsumables(), normalizeDataCollections(), normalizeReagents(), normalizeStepOutputs(), serializeAttachments(), serializeEquipmentIds() (+12 more)
Nodes (18): fetchProductionReceptionBySampleCode(), fetchProductionReceptionSubmit(), fetchProductionReceptionUpdate(), onUserSelectVisible(), remoteSearchUsers(), buildPayload(), ensureDefaultHandler(), initDialog() (+10 more)
### Community 25 - "Community 25"
Cohesion: 0.07
Nodes (19): fetchDeleteMaterialBatch(), fetchMaterialBatchList(), fetchMaterialBatchOptions(), fetchMaterialList(), fetchMaterialOptions(), fetchUpdateMaterialBatch(), fetchMaterialUsageList(), fetchMyMaterialUsageList() (+11 more)
### Community 26 - "Community 26"
Cohesion: 0.09
Nodes (28): completePilotApplication(), deletePilotApplication(), fetchPilotApplicationList(), startPilotApplication(), voidPilotApplication(), clearTableSelection(), handleBatchComplete(), handleBatchStart() (+20 more)
### Community 26 - "Community 26"
Cohesion: 0.11
Nodes (21): fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchRefreshToken(), fetchUpdateProfile(), fetchUploadProfileAvatar(), fetchUserInfo(), buildWebEncryptedLoginBody() (+13 more)
### Community 27 - "Community 27"
Cohesion: 0.09
Nodes (22): fetchMineProductionWorkOrderTabStepCounts(), fetchMineProductionWorkOrderTabStepCountsDeduped(), fetchProductionWorkOrderCellProductOptions(), fetchProductionWorkOrderDetail(), fetchProductionWorkOrderList(), fetchProductionWorkOrderQcDispatchOptions(), fetchProductionWorkOrderTabStepCounts(), fetchProductionWorkOrderTabStepCountsDeduped() (+14 more)
### Community 28 - "Community 28"
Cohesion: 0.09
Nodes (11): fetchMineQcWorkOrderTabUnfinishedCounts(), fetchOutboundQcDetail(), fetchQcWorkOrderDetail(), fetchQcWorkOrderList(), fetchQcWorkOrderTabUnfinishedCounts(), reload(), reloadTabCounts(), handleMenuJump() (+3 more)
### Community 28 - "Community 28"
### Community 29 - "Community 29"
Cohesion: 0.12
Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
### Community 29 - "Community 29"
### Community 30 - "Community 30"
Cohesion: 0.11
Nodes (11): fetchOutboundReleaseMaterial(), fetchOutboundReleaseReviewDetail(), fetchOutboundReleaseReviewList(), fetchReleaseReviewList(), handleReset(), handleSearch(), handleToggleSelect(), loadData() (+3 more)
### Community 30 - "Community 30"
### Community 31 - "Community 31"
Cohesion: 0.13
Nodes (18): fetchCellSampleTypeList(), fetchCreateCellSampleType(), fetchDeleteCellSampleType(), fetchUpdateCellSampleType(), handleAdd(), handleDelete(), handleDropdownCommand(), handleEdit() (+10 more)
### Community 31 - "Community 31"
### Community 32 - "Community 32"
Cohesion: 0.19
Nodes (15): fetchAttachmentDownloadUrl(), attachmentFullUrl(), ensureUniqueZipEntryName(), fetchAttachmentBlob(), getAttachmentExtension(), resolveAttachmentFileName(), sanitizeZipPathSegment(), applyInboundLabStorageCodePrefix() (+7 more)
### Community 32 - "Community 32"
Cohesion: 0.12
Nodes (12): fetchApproveTask(), fetchBusinessAllTasks(), fetchCompletedTasks(), fetchDesignerData(), fetchInstanceDetail(), fetchMyInstances(), fetchPendingTasks(), fetchProcessDefList() (+4 more)
### Community 33 - "Community 33"
Cohesion: 0.16
Nodes (14): fetchCreateEnvDetectItem(), fetchDeleteEnvDetectItem(), fetchEnvDetectItemDetail(), fetchEnvDetectItemList(), fetchEnvDetectItemsActive(), fetchUpdateEnvDetectItem(), handleAdd(), handleBatchDelete() (+6 more)
@@ -239,73 +239,73 @@ Cohesion: 0.27
Nodes (14): useChart(), useChartComponent(), useChartOps(), colourBlend(), getCssVar(), getDarkColor(), getLightColor(), handleElementThemeColor() (+6 more)
### Community 36 - "Community 36"
Cohesion: 0.33
Nodes (13): buildProtocolPaymentFlowSteps(), dash(), financeReceiptTime(), formatAmount(), hasFinanceReceipt(), hasFinanceRefund(), hasReceiptSubmit(), hasRefundSubmit() (+5 more)
### Community 37 - "Community 37"
Cohesion: 0.19
Nodes (6): fetchServiceOrderTxnList(), handleReset(), handleSearch(), handleTabChange(), loadData(), switchTab()
### Community 39 - "Community 39"
### Community 38 - "Community 38"
Cohesion: 0.24
Nodes (8): fullFileUrl(), handlePreviewAttachment(), isVideoFile(), normalizeDisplayValue(), resolveReviewCellType(), resolveReviewQuantity(), resolveReviewSpecText(), uniqueJoined()
### Community 40 - "Community 40"
### Community 39 - "Community 39"
Cohesion: 0.24
Nodes (4): addMaterialRow(), nextMaterialRowKey(), onRequisitionSelect(), removeMaterialRow()
### Community 41 - "Community 41"
### Community 40 - "Community 40"
Cohesion: 0.25
Nodes (2): handleTouchEnd(), setTransition()
### Community 44 - "Community 44"
### Community 41 - "Community 41"
Cohesion: 0.28
Nodes (4): fetchMyProductionMaterialConsumptions(), handleReset(), handleSearch(), loadData()
Nodes (1): FireworkSystem
### Community 46 - "Community 46"
### Community 45 - "Community 45"
Cohesion: 0.39
Nodes (6): isIgnorableScriptError(), normalizeErrorMessage(), registerPromiseErrorHandler(), registerResourceErrorHandler(), scriptErrorHandler(), setupErrorHandle()
### Community 47 - "Community 47"
### Community 46 - "Community 46"
Cohesion: 0.36
Nodes (1): TableHeightCalculator
### Community 49 - "Community 49"
### Community 48 - "Community 48"
Cohesion: 0.33
Nodes (2): logApiCryptoBootInfo(), maskSecret()
### Community 50 - "Community 50"
### Community 49 - "Community 49"
Cohesion: 0.38
Nodes (4): handleFocus(), handleInput(), parseDecimalToNumber(), sanitizeDecimalInput()
### Community 51 - "Community 51"
Cohesion: 0.29
Nodes (2): fetchLeaveDetail(), fetchLeaveList()
### Community 52 - "Community 52"
### Community 50 - "Community 50"
Cohesion: 0.4
Nodes (2): isUnfinishedQcWorkOrder(), resolveQcWorkOrderUnfinishedBadgeCount()
### Community 53 - "Community 53"
### Community 51 - "Community 51"
Cohesion: 0.47
Nodes (3): isPaymentDueWithinWarnWindow(), parsePaymentDueYmd(), startOfLocalDay()
### Community 54 - "Community 54"
### Community 52 - "Community 52"
Cohesion: 0.33
Nodes (2): fetchEnvDetectRecordDetail(), fetchEnvDetectRecordList()
### Community 53 - "Community 53"
Cohesion: 0.5
Nodes (2): formatProtocolDocDeleteConfirm(), formatProtocolDocSubject()
### Community 55 - "Community 55"
### Community 54 - "Community 54"
Cohesion: 0.5
Nodes (2): parseEquipmentIds(), syncEquipmentNamesFromOptions()
### Community 57 - "Community 57"
### Community 56 - "Community 56"
Cohesion: 0.5
Nodes (2): focusInput(), handleKeydown()
### Community 59 - "Community 59"
### Community 58 - "Community 58"
Cohesion: 0.67
Nodes (2): if(), logSidebarMenu()
### Community 60 - "Community 60"
Cohesion: 0.5
Nodes (1): fetchQcSmsList()
### Community 63 - "Community 63"
Cohesion: 1.0
Nodes (2): getSettingDefaults(), resetToDefaults()
@@ -327,25 +327,29 @@ Cohesion: 1.0
Nodes (2): emptyTabCounts(), useQcWorkOrderTabUnfinishedCounts()
## Knowledge Gaps
- **Thin community `Community 18`** (42 nodes): `WebSocketClient`, `.calculateReconnectDelay()`, `.clearAllTimers()`, `.clearConnectionTimers()`, `.clearTimer()`, `.close()`, `.closeCurrentSocketForReconnect()`, `.connect()`, `.connectionStatusText()`, `.constructor()`, `.destroyInstance()`, `.flushMessageQueue()`, `.getInstance()`, `.getReadyStateText()`, `.handleClose()`, `.handleConnectionTimeout()`, `.handleError()`, `.handleMessage()`, `.handleOpen()`, `.init()`, `.isWebSocketConnected()`, `.reconnect()`, `.resetHeartbeat()`, `.send()`, `.startHeartbeat()`, `.startPing()`, `TableCache`, `.cleanupExpired()`, `.clear()`, `.clearByTags()`, `.clearCurrentSearch()`, `.clearPagination()`, `.constructor()`, `.evictLRU()`, `.generateKey()`, `.generateTags()`, `.get()`, `.getStats()`, `.log()`, `.set()`, `index.ts`, `tableCache.ts`
- **Thin community `Community 21`** (42 nodes): `WebSocketClient`, `.calculateReconnectDelay()`, `.clearAllTimers()`, `.clearConnectionTimers()`, `.clearTimer()`, `.close()`, `.closeCurrentSocketForReconnect()`, `.connect()`, `.connectionStatusText()`, `.constructor()`, `.destroyInstance()`, `.flushMessageQueue()`, `.getInstance()`, `.getReadyStateText()`, `.handleClose()`, `.handleConnectionTimeout()`, `.handleError()`, `.handleMessage()`, `.handleOpen()`, `.init()`, `.isWebSocketConnected()`, `.reconnect()`, `.resetHeartbeat()`, `.send()`, `.startHeartbeat()`, `.startPing()`, `TableCache`, `.cleanupExpired()`, `.clear()`, `.clearByTags()`, `.clearCurrentSearch()`, `.clearPagination()`, `.constructor()`, `.evictLRU()`, `.generateKey()`, `.generateTags()`, `.get()`, `.getStats()`, `.log()`, `.set()`, `index.ts`, `tableCache.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 41`** (9 nodes): `cleanupEventListeners()`, `handleTouchEnd()`, `handleTouchMove()`, `handleTouchStart()`, `handleWheelScroll()`, `if()`, `setTransition()`, `setupEventListeners()`, `index.vue`
- **Thin community `Community 40`** (9 nodes): `cleanupEventListeners()`, `handleTouchEnd()`, `handleTouchMove()`, `handleTouchStart()`, `handleWheelScroll()`, `if()`, `setTransition()`, `setupEventListeners()`, `index.vue`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 47`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
- **Thin community `Community 41`** (9 nodes): `FireworkSystem`, `.constructor()`, `.createFirework()`, `.createParticle()`, `.getAvailableParticle()`, `.initializePool()`, `.preloadAllImages()`, `.preloadImage()`, `index.vue`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 49`** (7 nodes): `App.vue`, `main.ts`, `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `console.ts`
- **Thin community `Community 46`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 51`** (7 nodes): `fetchCreateLeave()`, `fetchDeleteLeave()`, `fetchLeaveDetail()`, `fetchLeaveList()`, `fetchSubmitLeave()`, `fetchUpdateLeave()`, `leave.ts`
- **Thin community `Community 48`** (7 nodes): `App.vue`, `main.ts`, `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `console.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 52`** (6 nodes): `formatQcWorkOrderUnfinishedBadgeCount()`, `isUnfinishedQcWorkOrder()`, `qcWorkOrderUnfinishedBadgeTitle()`, `resolveQcWorkOrderUnfinishedBadgeCount()`, `sumQcWorkOrderGroupUnfinishedBadgeCount()`, `qc-work-order-unfinished-badge.ts`
- **Thin community `Community 50`** (6 nodes): `formatQcWorkOrderUnfinishedBadgeCount()`, `isUnfinishedQcWorkOrder()`, `qcWorkOrderUnfinishedBadgeTitle()`, `resolveQcWorkOrderUnfinishedBadgeCount()`, `sumQcWorkOrderGroupUnfinishedBadgeCount()`, `qc-work-order-unfinished-badge.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 54`** (5 nodes): `formatProtocolDocBatchDeleteConfirm()`, `formatProtocolDocDeleteConfirm()`, `formatProtocolDocSubject()`, `getProtocolDocTypeLabel()`, `protocol-doc-label.ts`
- **Thin community `Community 52`** (6 nodes): `fetchCreateEnvDetectRecord()`, `fetchDeleteEnvDetectRecord()`, `fetchEnvDetectRecordDetail()`, `fetchEnvDetectRecordList()`, `fetchUpdateEnvDetectRecord()`, `env-detect-record.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 55`** (5 nodes): `equipmentOptionLabel()`, `joinEquipmentIds()`, `parseEquipmentIds()`, `syncEquipmentNamesFromOptions()`, `equipment-select.ts`
- **Thin community `Community 53`** (5 nodes): `formatProtocolDocBatchDeleteConfirm()`, `formatProtocolDocDeleteConfirm()`, `formatProtocolDocSubject()`, `getProtocolDocTypeLabel()`, `protocol-doc-label.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 57`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
- **Thin community `Community 54`** (5 nodes): `equipmentOptionLabel()`, `joinEquipmentIds()`, `parseEquipmentIds()`, `syncEquipmentNamesFromOptions()`, `equipment-select.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 59`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
- **Thin community `Community 56`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 58`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 60`** (4 nodes): `fetchGenerateQcSms()`, `fetchQcSmsList()`, `fetchToggleQcSmsNotified()`, `qc-sms-notification.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 63`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
@@ -361,16 +365,16 @@ Nodes (2): emptyTabCounts(), useQcWorkOrderTabUnfinishedCounts()
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `get()` connect `Community 0` to `Community 1`, `Community 2`, `Community 3`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 12`, `Community 15`, `Community 16`, `Community 19`, `Community 21`, `Community 23`, `Community 25`, `Community 26`, `Community 27`, `Community 29`, `Community 30`, `Community 32`, `Community 33`, `Community 37`, `Community 44`, `Community 51`?**
_High betweenness centrality (0.277) - this node is a cross-community bridge._
- **Why does `push()` connect `Community 2` to `Community 0`, `Community 1`, `Community 3`, `Community 6`, `Community 7`, `Community 8`, `Community 10`, `Community 11`, `Community 13`, `Community 14`, `Community 16`, `Community 17`, `Community 18`, `Community 20`, `Community 21`, `Community 24`, `Community 25`, `Community 27`, `Community 31`, `Community 40`?**
_High betweenness centrality (0.256) - this node is a cross-community bridge._
- **Why does `fetchNotDeletedRegions()` connect `Community 0` to `Community 2`?**
- **Why does `get()` connect `Community 0` to `Community 1`, `Community 2`, `Community 4`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 10`, `Community 11`, `Community 12`, `Community 13`, `Community 14`, `Community 15`, `Community 16`, `Community 18`, `Community 20`, `Community 24`, `Community 25`, `Community 26`, `Community 27`, `Community 28`, `Community 30`, `Community 31`, `Community 33`, `Community 36`, `Community 52`, `Community 60`?**
_High betweenness centrality (0.308) - this node is a cross-community bridge._
- **Why does `push()` connect `Community 4` to `Community 0`, `Community 1`, `Community 2`, `Community 5`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 15`, `Community 16`, `Community 17`, `Community 19`, `Community 20`, `Community 21`, `Community 23`, `Community 26`, `Community 27`, `Community 28`, `Community 32`, `Community 39`, `Community 41`?**
_High betweenness centrality (0.269) - this node is a cross-community bridge._
- **Why does `fetchNotDeletedRegions()` connect `Community 0` to `Community 4`?**
_High betweenness centrality (0.035) - this node is a cross-community bridge._
- **Are the 278 inferred relationships involving `get()` (e.g. with `buildNodeCodePathFromFlat()` and `collectLeafFacilityNodesInTreeOrder()`) actually correct?**
_`get()` has 278 INFERRED edges - model-reasoned connections that need verification._
- **Are the 76 inferred relationships involving `push()` (e.g. with `refreshQcMenuBadges()` and `collectLeafFacilityNodesInTreeOrder()`) actually correct?**
_`push()` has 76 INFERRED edges - model-reasoned connections that need verification._
- **Are the 74 inferred relationships involving `push()` (e.g. with `refreshQcMenuBadges()` and `collectLeafFacilityNodesInTreeOrder()`) actually correct?**
_`push()` has 74 INFERRED edges - model-reasoned connections that need verification._
- **Are the 23 inferred relationships involving `emit()` (e.g. with `handleSubmit()` and `handleDialogClose()`) actually correct?**
_`emit()` has 23 INFERRED edges - model-reasoned connections that need verification._
- **Are the 19 inferred relationships involving `set()` (e.g. with `buildNodeCodePathFromFlat()` and `collectLeafFacilityNodesInTreeOrder()`) actually correct?**
+4 -4
View File
@@ -57,12 +57,12 @@
</div>
<div id="legend"></div>
</div>
<div id="stats">2845 nodes &middot; 3968 edges &middot; 336 communities</div>
<div id="stats">2839 nodes &middot; 3960 edges &middot; 336 communities</div>
</div>
<script>
const RAW_NODES = [{"id": "app_vue", "label": "App.vue", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "App.vue", "community": 49, "community_name": "Community 49", "source_file": "App.vue", "file_type": "code", "degree": 3}, {"id": "src_app_refreshbuttonautoicons", "label": "refreshButtonAutoIcons()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshButtonAutoIcons()", "community": 49, "community_name": "Community 49", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "src_app_refreshpaneltitleicons", "label": "refreshPanelTitleIcons()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshPanelTitleIcons()", "community": 49, "community_name": "Community 49", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "main_ts", "label": "main.ts", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "main.ts", "community": 49, "community_name": "Community 49", "source_file": "main.ts", "file_type": "code", "degree": 4}, {"id": "src_main_masksecret", "label": "maskSecret()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "maskSecret()", "community": 49, "community_name": "Community 49", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "src_main_logapicryptobootinfo", "label": "logApiCryptoBootInfo()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "logApiCryptoBootInfo()", "community": 49, "community_name": "Community 49", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "env_d_ts", "label": "env.d.ts", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "env.d.ts", "community": 112, "community_name": "Community 112", "source_file": "env.d.ts", "file_type": "code", "degree": 0}, {"id": "shims_loose_d_ts", "label": "shims-loose.d.ts", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "shims-loose.d.ts", "community": 113, "community_name": "Community 113", "source_file": "shims-loose.d.ts", "file_type": "code", "degree": 0}, {"id": "directives_index_ts", "label": "index.ts", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "index.ts", "community": 28, "community_name": "Community 28", "source_file": "directives/index.ts", "file_type": "code", "degree": 1}, {"id": "directives_index_setupglobdirectives", "label": "setupGlobDirectives()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "setupGlobDirectives()", "community": 28, "community_name": "Community 28", "source_file": "directives/index.ts", "file_type": "code", "degree": 5}, {"id": "directives_core_roles_ts", "label": "roles.ts", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "roles.ts", "community": 28, "community_name": "Community 28", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 3}, {"id": "core_roles_checkrolepermission", "label": "checkRolePermission()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "checkRolePermission()", "community": 28, "community_name": "Community 28", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_removeelement", "label": "removeElement()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "removeElement()", "community": 28, "community_name": "Community 28", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_setuprolesdirective",Line truncated
const RAW_EDGES = [{"from": "app_vue", "to": "src_app_refreshbuttonautoicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "src_app_refreshpaneltitleicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "main_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "utils_sys_console_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_masksecret", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_logapicryptobootinfo", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "src_main_masksecret", "to": "src_main_logapicryptobootinfo", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_ts", "to": "directives_index_setupglobdirectives", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_setupglobdirectives", "to": "core_auth_setupauthdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "core_roles_setuprolesdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_highlight_setuphighlightdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_ripple_setuprippledirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_core_roles_ts", "to": "core_roles_checkrolepermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_setuprolesdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_roles_checkrolepermission", "to": "core_roles_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_checkauthpermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_setupauthdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_checkauthpermission", "to": "core_auth_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_ripple_ts", "to": "business_ripple_setuprippledirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_highlightcode", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_insertlinenumbers", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_addcopybutton", "label": "contains", "title": "contains [EXTRACTED]", "dLine truncated
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 435}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 114}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 110}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 95}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 94}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 88}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 88}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 88}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 81}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 78}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 77}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 76}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 70}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 69}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 65}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 61}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 50}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 47}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 42}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 42}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 40}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 38}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 37}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 36}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 36}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 35}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 29}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 24}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 22}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 22}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 22}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 20}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 19}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 19}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 17}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 16}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 14}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 14}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 13}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 13}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 10}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 9}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 9}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 9}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 9}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 9}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 8}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 8}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 8}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 7}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 7}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 7}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 6}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 6}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 5}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 5}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 5}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 5}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 4}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 4}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 4}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 4}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 4}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 3}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 3}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 3}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 3}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 3}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 3}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 3}, {"cid": 70, "color": "#4E79A7", "label": "Community 70", "cLine truncated
const RAW_NODES = [{"id": "app_vue", "label": "App.vue", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "App.vue", "community": 48, "community_name": "Community 48", "source_file": "App.vue", "file_type": "code", "degree": 3}, {"id": "src_app_refreshbuttonautoicons", "label": "refreshButtonAutoIcons()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshButtonAutoIcons()", "community": 48, "community_name": "Community 48", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "src_app_refreshpaneltitleicons", "label": "refreshPanelTitleIcons()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshPanelTitleIcons()", "community": 48, "community_name": "Community 48", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "main_ts", "label": "main.ts", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "main.ts", "community": 48, "community_name": "Community 48", "source_file": "main.ts", "file_type": "code", "degree": 4}, {"id": "src_main_masksecret", "label": "maskSecret()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "maskSecret()", "community": 48, "community_name": "Community 48", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "src_main_logapicryptobootinfo", "label": "logApiCryptoBootInfo()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "logApiCryptoBootInfo()", "community": 48, "community_name": "Community 48", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "env_d_ts", "label": "env.d.ts", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "env.d.ts", "community": 112, "community_name": "Community 112", "source_file": "env.d.ts", "file_type": "code", "degree": 0}, {"id": "shims_loose_d_ts", "label": "shims-loose.d.ts", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "shims-loose.d.ts", "community": 113, "community_name": "Community 113", "source_file": "shims-loose.d.ts", "file_type": "code", "degree": 0}, {"id": "directives_index_ts", "label": "index.ts", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "index.ts", "community": 29, "community_name": "Community 29", "source_file": "directives/index.ts", "file_type": "code", "degree": 1}, {"id": "directives_index_setupglobdirectives", "label": "setupGlobDirectives()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "setupGlobDirectives()", "community": 29, "community_name": "Community 29", "source_file": "directives/index.ts", "file_type": "code", "degree": 5}, {"id": "directives_core_roles_ts", "label": "roles.ts", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "roles.ts", "community": 29, "community_name": "Community 29", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 3}, {"id": "core_roles_checkrolepermission", "label": "checkRolePermission()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "checkRolePermission()", "community": 29, "community_name": "Community 29", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_removeelement", "label": "removeElement()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "removeElement()", "community": 29, "community_name": "Community 29", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_setuprolesdirective",Line truncated
const RAW_EDGES = [{"from": "app_vue", "to": "src_app_refreshbuttonautoicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "src_app_refreshpaneltitleicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "main_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "utils_sys_console_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_masksecret", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_logapicryptobootinfo", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "src_main_masksecret", "to": "src_main_logapicryptobootinfo", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_ts", "to": "directives_index_setupglobdirectives", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_setupglobdirectives", "to": "core_auth_setupauthdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "core_roles_setuprolesdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_highlight_setuphighlightdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_ripple_setuprippledirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_core_roles_ts", "to": "core_roles_checkrolepermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_setuprolesdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_roles_checkrolepermission", "to": "core_roles_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_checkauthpermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_setupauthdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_checkauthpermission", "to": "core_auth_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_ripple_ts", "to": "business_ripple_setuprippledirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_highlightcode", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_insertlinenumbers", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_addcopybutton", "label": "contains", "title": "contains [EXTRACTED]", "dLine truncated
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 451}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 133}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 96}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 94}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 90}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 89}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 88}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 85}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 82}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 72}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 71}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 69}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 69}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 64}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 61}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 50}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 48}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 47}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 45}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 43}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 43}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 42}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 37}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 36}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 36}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 35}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 35}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 34}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 24}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 22}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 22}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 22}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 20}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 19}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 17}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 16}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 14}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 13}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 13}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 10}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 9}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 9}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 9}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 9}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 9}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 8}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 8}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 8}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 7}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 7}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 6}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 6}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 6}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 5}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 5}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 5}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 5}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 4}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 4}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 4}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 4}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 4}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 4}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 3}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 3}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 3}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 3}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 3}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 3}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 3}, {"cid": 70, "color": "#4E79A7", "label": "Community 70", "couLine truncated
// HTML-escape helper — prevents XSS when injecting graph data into innerHTML
function esc(s) {
+12 -11
View File
@@ -17,8 +17,8 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/common/index.ts": 1781272556.3597305,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/directive/directive.d.ts": 1781272556.3601494,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/api/api.d.ts": 1781272556.3606477,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1783327276.2077293,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1783327119.615712,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1783328469.3642685,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1783327938.8278427,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/store/index.ts": 1781272556.364766,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/router/index.ts": 1782394569.8806834,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/composables/useAttachmentDropZone.ts": 1782873161.9839864,
@@ -59,7 +59,7 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-mine-task-groups.ts": 1783327864.4037368,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/expected-cell-count.ts": 1782705039.2515786,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-labels.ts": 1782893910.9937537,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts": 1783303672.6673675,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts": 1783329871.573473,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts": 1783306636.6246853,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/fetch-paged-list.ts": 1781272556.3903377,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/decimal-input-format.ts": 1782807247.117702,
@@ -92,9 +92,10 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/money-format.ts": 1782807264.1686463,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/category-path-display.ts": 1781272556.411347,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/transfer-type-dict.ts": 1782713881.7742825,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts": 1782961760.3056056,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts": 1783330000.923092,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/cell-product-spec.ts": 1782709946.3419836,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-work-order-selection-expand.ts": 1783322120.2724662,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-presets.ts": 1783329823.1067264,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-labels.ts": 1781272556.4115942,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-step-assignee.ts": 1782472559.244021,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/process-material-ui.ts": 1781528129.339137,
@@ -103,8 +104,8 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/loginRsa.ts": 1781272556.4129546,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/chinese-datetime-parse.ts": 1782958233.483039,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-task-editor-cache.ts": 1782602746.754815,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts": 1781431995.482862,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts": 1781432316.9136086,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts": 1783329869.6204023,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts": 1783329873.2829163,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/download-attachment.ts": 1781272556.4160507,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/ui/colors.ts": 1781272556.3798137,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/ui/emojo.ts": 1781272556.3802173,
@@ -242,7 +243,7 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-qc-batch-readonly/index.vue": 1782893851.9487352,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/outbound-material-form-sections/index.vue": 1782893851.934857,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/attachment-zip-progress-dialog/index.vue": 1782893851.6959429,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/protocol-payment-detail-readonly/index.vue": 1782893922.525934,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/protocol-payment-detail-readonly/index.vue": 1783330021.0864918,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-code-with-special-display/index.vue": 1782893851.6283524,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-item-param-select-editor/index.vue": 1782893851.5957565,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/system-view-dialog/index.vue": 1782893851.578115,
@@ -314,9 +315,9 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/my-material-usage/index.vue": 1782893854.311591,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/index.vue": 1783326448.8937888,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolStatusChangeDialog.vue": 1782894806.0188534,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue": 1782894754.993778,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue": 1783330003.107557,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolAttachmentReadonly.vue": 1782893854.403395,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue": 1782982745.3279839,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue": 1783329986.6830297,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolAuditDialog.vue": 1782893854.3675368,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/sample-info/index.vue": 1782893854.321358,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/sample-reception-info/index.vue": 1782893854.265202,
@@ -409,7 +410,7 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskPaperAttachmentsPanel.vue": 1782893853.2361045,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue": 1782893853.2094715,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue": 1782893853.1137185,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ProductionWorkOrderCardList.vue": 1783306631.8896008,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ProductionWorkOrderCardList.vue": 1783328174.4363172,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ForwardTaskDialog.vue": 1782893852.8302743,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ProductionWorkOrderQcViewDialog.vue": 1782893852.7941833,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskStepSubList.vue": 1782893852.736343,
@@ -469,7 +470,7 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderTabBar.vue": 1782893854.0314786,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderBatchSubList.vue": 1782893854.0134735,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderCardList.vue": 1783327877.2203474,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderProtocolPaymentPanel.vue": 1782893853.9119349,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderProtocolPaymentPanel.vue": 1783330023.9447763,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/tabs/OutboundPilotTab.vue": 1782893854.159883,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/tabs/InboundCellStorageTab.vue": 1782893854.1357596,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/tabs/QcWorkOrderTabPanel.vue": 1783327002.974947,
@@ -0,0 +1,38 @@
import assert from 'node:assert/strict'
import {
resolvePaymentPreset,
getActiveSampleCount,
isInstallmentPayMode,
isPayModeValidForSampleCount
} from '../src/utils/protocol-payment-presets'
const fq1 = resolvePaymentPreset(1, 'fq')!
assert.equal(fq1.contractAmount, 25880)
assert.deepEqual(fq1.installmentAmounts, [6480, 3880, 3880, 3880, 3880, 3880])
assert.equal(
fq1.installmentAmounts!.reduce((s, v) => s + v, 0),
fq1.contractAmount
)
const djfq2 = resolvePaymentPreset(2, 'djfq')!
assert.equal(djfq2.contractAmount, 41000)
assert.equal(djfq2.installmentCount, 4)
assert.deepEqual(djfq2.installmentAmounts, [29360, 3880, 3880, 3880])
assert.equal(
djfq2.installmentAmounts!.reduce((s, v) => s + v, 0),
djfq2.contractAmount
)
assert.equal(getActiveSampleCount([{ sampleStatus: 'ycj' }]), 1)
assert.equal(getActiveSampleCount([{ sampleStatus: 'ycj' }, { sampleStatus: 'yzf' }]), 1)
assert.equal(
getActiveSampleCount([{ sampleStatus: 'ycj' }, { sampleStatus: 'ycj' }]),
2
)
assert.equal(isInstallmentPayMode('djfq'), true)
assert.equal(isInstallmentPayMode('dj'), false)
assert.equal(isPayModeValidForSampleCount('djfq', 1), false)
assert.equal(isPayModeValidForSampleCount('djfq', 2), true)
console.log('protocol-payment-presets: OK')
@@ -37,11 +37,11 @@
<dd>{{ maskIdCard(String(detail?.newbornIdCard || '')) }}</dd>
</div>
</template>
<div v-if="detail?.payMode === 'fq'" class="view-kv">
<div v-if="isInstallmentPayMode(String(detail?.payMode || ''))" class="view-kv">
<dt>分期期序号</dt>
<dd>{{ dash(detail?.installmentNo) }}</dd>
</div>
<div v-if="detail?.payMode === 'fq'" class="view-kv">
<div v-if="isInstallmentPayMode(String(detail?.payMode || ''))" class="view-kv">
<dt>应缴费时间</dt>
<dd>{{ formatPaymentDueDate(detail?.installmentDueDate) }}</dd>
</div>
@@ -116,6 +116,7 @@
loadTransferTypeDict,
transferTypeLabel
} from '@/utils/protocol-payment-labels'
import { isInstallmentPayMode } from '@/utils/protocol-payment-presets'
const props = withDefaults(
defineProps<{
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_status_tag_registry_ts", "label": "status-tag-registry.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L1"}, {"id": "utils_status_tag_registry_createlabelgetter", "label": "createLabelGetter()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L23"}, {"id": "utils_status_tag_registry_createtagtypegetter", "label": "createTagTypeGetter()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L27"}, {"id": "utils_status_tag_registry_isinactiveprotocolstatus", "label": "isInactiveProtocolStatus()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L47"}, {"id": "utils_status_tag_registry_protocolstatusdisplaylabel", "label": "protocolStatusDisplayLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L55"}, {"id": "utils_status_tag_registry_donationstatusdisplaylabel", "label": "donationStatusDisplayLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L74"}, {"id": "utils_status_tag_registry_protocolstatusdisplaylabelbysource", "label": "protocolStatusDisplayLabelBySource()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L82"}, {"id": "utils_status_tag_registry_joinworkorderstatusfilter", "label": "joinWorkOrderStatusFilter()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L203"}, {"id": "utils_status_tag_registry_matchesworkorderstatusfilter", "label": "matchesWorkOrderStatusFilter()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L208"}, {"id": "utils_status_tag_registry_txntypelabel", "label": "txnTypeLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L482"}, {"id": "utils_status_tag_registry_txntypetagtype", "label": "txnTypeTagType()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L493"}, {"id": "utils_status_tag_registry_txnstatuslabel", "label": "txnStatusLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L505"}, {"id": "utils_status_tag_registry_txnstatustagtype", "label": "txnStatusTagType()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L515"}, {"id": "utils_status_tag_registry_applicationstatuslabel", "label": "applicationStatusLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L526"}, {"id": "utils_status_tag_registry_applicationstatustagtype", "label": "applicationStatusTagType()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L530"}, {"id": "utils_status_tag_registry_checkupstatuslabel", "label": "checkupStatusLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L543"}, {"id": "utils_status_tag_registry_checkupstatustagtype", "label": "checkupStatusTagType()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L551"}, {"id": "utils_status_tag_registry_feestatuslabel", "label": "feeStatusLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L559"}, {"id": "utils_status_tag_registry_feestatustagtype", "label": "feeStatusTagType()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L567"}, {"id": "utils_status_tag_registry_schedulestatuslabel", "label": "scheduleStatusLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L576"}, {"id": "utils_status_tag_registry_schedulestatustagtype", "label": "scheduleStatusTagType()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L585"}, {"id": "utils_sLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_installment_ts", "label": "protocol-payment-installment.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L1"}, {"id": "utils_protocol_payment_installment_defaultinstallmentratios", "label": "defaultInstallmentRatios()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L10"}, {"id": "utils_protocol_payment_installment_resizeinstallmentratios", "label": "resizeInstallmentRatios()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L19"}, {"id": "utils_protocol_payment_installment_finalizeinstallmentratios", "label": "finalizeInstallmentRatios()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L31"}, {"id": "utils_protocol_payment_installment_isinstallmentratiosvalid", "label": "isInstallmentRatiosValid()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L39"}, {"id": "utils_protocol_payment_installment_islegacyinstallmentratios", "label": "isLegacyInstallmentRatios()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L49"}, {"id": "utils_protocol_payment_installment_normalizeinstallmentamounts", "label": "normalizeInstallmentAmounts()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L64"}, {"id": "utils_protocol_payment_installment_defaultinstallmentamounts", "label": "defaultInstallmentAmounts()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L82"}, {"id": "utils_protocol_payment_installment_resizeinstallmentamounts", "label": "resizeInstallmentAmounts()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L89"}, {"id": "utils_protocol_payment_installment_isinstallmentamountsvalid", "label": "isInstallmentAmountsValid()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L115"}, {"id": "utils_protocol_payment_installment_suminstallmentamounts", "label": "sumInstallmentAmounts()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L125"}, {"id": "utils_protocol_payment_installment_scaleinstallmentamountstocontract", "label": "scaleInstallmentAmountsToContract()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L131"}, {"id": "utils_protocol_payment_installment_addmonthsymd", "label": "addMonthsYmd()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L156"}, {"id": "utils_protocol_payment_installment_adddaysymd", "label": "addDaysYmd()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L170"}, {"id": "utils_protocol_payment_installment_addintervalmonthsymd", "label": "addIntervalMonthsYmd()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L185"}, {"id": "utils_protocol_payment_installment_calcfqinstallmentduedate", "label": "calcFqInstallmentDueDate()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L206"}, {"id": "utils_protocol_payment_installment_calcinstallmentdueamounts", "label": "calcInstallmentDueAmounts()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L219"}, {"id": "utils_protocol_payment_installment_calcinstallmentdueamountsbyratios", "label": "calcInstallmentDueAmountsByRatios()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L239"}, {"id": "utils_protocol_payment_installment_calcunpaidamountsbyratios", "label": "calcUnpaidAmountsByRatios()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-installment.ts", "source_location": "L259"}, {"id": "utils_protocol_pLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolviewdialog_vue", "label": "ProtocolViewDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1"}, {"id": "protocol_protocolviewdialog_financeconfirmattachmentfiles", "label": "financeConfirmAttachmentFiles()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1659"}, {"id": "protocol_protocolviewdialog_refundattachmentfiles", "label": "refundAttachmentFiles()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1666"}, {"id": "protocol_protocolviewdialog_refundfinanceconfirmattachmentfiles", "label": "refundFinanceConfirmAttachmentFiles()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1673"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolviewdialog_vue", "target": "protocol_protocolviewdialog_financeconfirmattachmentfiles", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1659", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolviewdialog_vue", "target": "protocol_protocolviewdialog_refundattachmentfiles", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1666", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolviewdialog_vue", "target": "protocol_protocolviewdialog_refundfinanceconfirmattachmentfiles", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1673", "weight": 1.0}], "raw_calls": [{"caller_nid": "protocol_protocolviewdialog_financeconfirmattachmentfiles", "callee": "isArray", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1660"}, {"caller_nid": "protocol_protocolviewdialog_financeconfirmattachmentfiles", "callee": "urlsToAttachmentFiles", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1663"}, {"caller_nid": "protocol_protocolviewdialog_financeconfirmattachmentfiles", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1663"}, {"caller_nid": "protocol_protocolviewdialog_refundattachmentfiles", "callee": "isArray", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1667"}, {"caller_nid": "protocol_protocolviewdialog_refundattachmentfiles", "callee": "urlsToAttachmentFiles", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1670"}, {"caller_nid": "protocol_protocolviewdialog_refundattachmentfiles", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1670"}, {"caller_nid": "protocol_protocolviewdialog_refundfinanceconfirmattachmentfiles", "callee": "isArray", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1674"}, {"caller_nid": "protocol_protocolviewdialog_refundfinanceconfirmattachmentfiles", "callee": "urlsToAttachmentFiles", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1677"}, {"caller_nid": "protocol_protocolviewdialog_refundfinanceconfirmattachmentfiles", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1677"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_protocol_payment_detail_readonly_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/protocol-payment-detail-readonly/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_productionworkordercardlist_vue", "label": "ProductionWorkOrderCardList.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ProductionWorkOrderCardList.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_presets_ts", "label": "protocol-payment-presets.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-presets.ts", "source_location": "L1"}, {"id": "utils_protocol_payment_presets_getactivesamplecount", "label": "getActiveSampleCount()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-presets.ts", "source_location": "L35"}, {"id": "utils_protocol_payment_presets_resolvepaymentpreset", "label": "resolvePaymentPreset()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-presets.ts", "source_location": "L40"}, {"id": "utils_protocol_payment_presets_isinstallmentpaymode", "label": "isInstallmentPayMode()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-presets.ts", "source_location": "L47"}, {"id": "utils_protocol_payment_presets_availablepaymodes", "label": "availablePayModes()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-presets.ts", "source_location": "L51"}, {"id": "utils_protocol_payment_presets_ispaymodevalidforsamplecount", "label": "isPayModeValidForSampleCount()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-presets.ts", "source_location": "L56"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_presets_ts", "target": "utils_protocol_payment_presets_getactivesamplecount", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-presets.ts", "source_location": "L35", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_presets_ts", "target": "utils_protocol_payment_presets_resolvepaymentpreset", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-presets.ts", "source_location": "L40", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_presets_ts", "target": "utils_protocol_payment_presets_isinstallmentpaymode", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-presets.ts", "source_location": "L47", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_presets_ts", "target": "utils_protocol_payment_presets_availablepaymodes", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-presets.ts", "source_location": "L51", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_presets_ts", "target": "utils_protocol_payment_presets_ispaymodevalidforsamplecount", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-presets.ts", "source_location": "L56", "weight": 1.0}, {"source": "utils_protocol_payment_presets_ispaymodevalidforsamplecount", "target": "utils_protocol_payment_presets_availablepaymodes", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-presets.ts", "source_location": "L57", "weight": 1.0}], "raw_calls": [{"caller_nid": "utils_protocol_payment_presets_getactivesamplecount", "callee": "filter", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-presets.ts", "source_location": "L36"}, {"caller_nid": "utils_protocol_payment_presets_ispaymodevalidforsamplecount", "callee": "includes", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-presets.ts", "source_location": "L57"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_qcworkorderprotocolpaymentpanel_vue", "label": "QcWorkOrderProtocolPaymentPanel.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderProtocolPaymentPanel.vue", "source_location": "L1"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_qcworkorderprotocolpaymentpanel_vue", "target": "vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderProtocolPaymentPanel.vue", "source_location": "L177", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_qcworkorderprotocolpaymentpanel_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_work_order", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderProtocolPaymentPanel.vue", "source_location": "L178", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_qcworkorderprotocolpaymentpanel_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_money_format", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderProtocolPaymentPanel.vue", "source_location": "L179", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_qcworkorderprotocolpaymentpanel_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_status_tag_registry", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderProtocolPaymentPanel.vue", "source_location": "L180", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_qcworkorderprotocolpaymentpanel_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_status_tag_registry", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderProtocolPaymentPanel.vue", "source_location": "L185", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_qcworkorderprotocolpaymentpanel_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_presets", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderProtocolPaymentPanel.vue", "source_location": "L186", "weight": 1.0}], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "label": "ProtocolFormDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1"}, {"id": "protocol_protocolformdialog_ensuretransfertypeoptions", "label": "ensureTransferTypeOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1015"}, {"id": "protocol_protocolformdialog_fmtmoney", "label": "fmtMoney()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1021"}, {"id": "protocol_protocolformdialog_pad", "label": "pad()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1414"}, {"id": "protocol_protocolformdialog_applypaymentpresetformode", "label": "applyPaymentPresetForMode()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1715"}, {"id": "protocol_protocolformdialog_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1964"}, {"id": "protocol_protocolformdialog_for", "label": "for()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1986"}, {"id": "protocol_protocolformdialog_applyfqpaymentdefaults", "label": "applyFqPaymentDefaults()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L2690"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_sample_transport_form_section_index_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L911", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L912", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L913", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_collection_protocol", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L926", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_business", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L932", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_download_attachment", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L933", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_composables_useattachmentdropzone", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L934", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_chinesLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_standard_import_parse_ts", "label": "protocol-standard-import-parse.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L1"}, {"id": "utils_protocol_standard_import_parse_normalizeline", "label": "normalizeLine()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L85"}, {"id": "utils_protocol_standard_import_parse_splitkeyvalue", "label": "splitKeyValue()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L91"}, {"id": "utils_protocol_standard_import_parse_normalizechinesedatetimeliteral", "label": "normalizeChineseDatetimeLiteral()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L97"}, {"id": "utils_protocol_standard_import_parse_parsedatetime", "label": "parseDatetime()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L106"}, {"id": "utils_protocol_standard_import_parse_parsesex", "label": "parseSex()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L117"}, {"id": "utils_protocol_standard_import_parse_parsedeliverymode", "label": "parseDeliveryMode()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L125"}, {"id": "utils_protocol_standard_import_parse_parsemoney", "label": "parseMoney()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L133"}, {"id": "utils_protocol_standard_import_parse_parsescore", "label": "parseScore()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L140"}, {"id": "utils_protocol_standard_import_parse_ismaternalbloodempty", "label": "isMaternalBloodEmpty()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L144"}, {"id": "utils_protocol_standard_import_parse_resolvesamplecollecthint", "label": "resolveSampleCollectHint()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L150"}, {"id": "utils_protocol_standard_import_parse_resolvesamplecodehint", "label": "resolveSampleCodeHint()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L161"}, {"id": "utils_protocol_standard_import_parse_upsertsamplehint", "label": "upsertSampleHint()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L177"}, {"id": "utils_protocol_standard_import_parse_earliestcollecttime", "label": "earliestCollectTime()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L190"}, {"id": "utils_protocol_standard_import_parse_parsepaymodefromtext", "label": "parsePayModeFromText()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L197"}, {"id": "utils_protocol_standard_import_parse_parsepaymenttransfersegments", "label": "parsePaymentTransferSegments()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L211"}, {"id": "utils_protocol_standard_import_parse_resolvetransfertypecode", "label": "resolveTransferTypeCode()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L260"}, {"id": "utils_protocol_standard_import_parse_extractcarriernamefromtransport", "label": "extractCarrierNameFromTransport()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L289"}, {"id": "utils_protocol_standard_import_parse_parseprotocolstandardimporttext", "label": "parseProtocolStandardImportText()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-standard-import-parse.ts", "source_location": "L296"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_standard_imLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_pay_mode_switch_ts", "label": "protocol-pay-mode-switch.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L1"}, {"id": "utils_protocol_pay_mode_switch_ispaydetaillocked", "label": "isPayDetailLocked()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L34"}, {"id": "utils_protocol_pay_mode_switch_markpaydetaillocks", "label": "markPayDetailLocks()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L39"}, {"id": "utils_protocol_pay_mode_switch_sumpaidamount", "label": "sumPaidAmount()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L45"}, {"id": "utils_protocol_pay_mode_switch_calcremainingamount", "label": "calcRemainingAmount()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L51"}, {"id": "utils_protocol_pay_mode_switch_formatmoneysummary", "label": "formatMoneySummary()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L57"}, {"id": "utils_protocol_pay_mode_switch_activedetails", "label": "activeDetails()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L61"}, {"id": "utils_protocol_pay_mode_switch_hasblockingstatus", "label": "hasBlockingStatus()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L65"}, {"id": "utils_protocol_pay_mode_switch_countbystatus", "label": "countByStatus()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L69"}, {"id": "utils_protocol_pay_mode_switch_buildinstallmentdetails", "label": "buildInstallmentDetails()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L73"}, {"id": "utils_protocol_pay_mode_switch_createpaymentdetailrow", "label": "createPaymentDetailRow()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L102"}, {"id": "utils_protocol_pay_mode_switch_evaluatepaymodeswitch", "label": "evaluatePayModeSwitch()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L126"}, {"id": "utils_protocol_pay_mode_switch_applypaymodeswitch", "label": "applyPayModeSwitch()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L208"}, {"id": "utils_protocol_pay_mode_switch_canaddpaymentdetail", "label": "canAddPaymentDetail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L244"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_pay_mode_switch_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_installment", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L6", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_pay_mode_switch_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_presets", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L11", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_pay_mode_switch_ts", "target": "utils_protocol_pay_mode_switch_ispaydetaillocked", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L34", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_pay_mode_switch_ts", "target": "utils_protocol_pay_mode_switch_markpaydetaillocks", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-pay-mode-switch.ts", "source_location": "L39", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_pay_mode_switch_ts", "target": "utils_protocol_payLine truncated
+61 -37
View File
@@ -1,5 +1,5 @@
/**
* 采集协议单 · 缴费模式切换(dj ↔ fq)
* 采集协议单 · 缴费模式切换(dj / fq / djfq)
* 规则见 docs/superpowers/specs/2026-06-04-protocol-pay-mode-switch-design.md
*/
@@ -8,8 +8,9 @@ import {
DEFAULT_INSTALLMENT_ADVANCE_DAYS,
defaultInstallmentAmounts
} from '@/utils/protocol-payment-installment'
import { type PayMode, isInstallmentPayMode } from '@/utils/protocol-payment-presets'
export type PayMode = 'dj' | 'fq'
export type { PayMode }
export type DetailLike = Record<string, any>
const LOCKED_STATUSES = new Set(['jfqrz', 'yjf', 'tfqrz', 'ytf'])
@@ -69,6 +70,35 @@ function countByStatus(details: DetailLike[], status: string): number {
return details.filter((d) => String(d.payStatus || 'dfj') === status).length
}
function buildInstallmentDetails(
ctx: PayModeSwitchContext,
rows: DetailLike[]
): DetailLike[] {
const count = Number(ctx.installmentCount) || 6
const interval = Number(ctx.installmentIntervalMonths) || 6
const startDate = ctx.installmentStartDate || ''
const advanceDays = Number(ctx.installmentAdvanceDays ?? DEFAULT_INSTALLMENT_ADVANCE_DAYS)
const amounts =
ctx.installmentRatios?.length && sumPaidAmount(rows) === 0
? ctx.installmentRatios.map((v) => Number(v || 0))
: defaultInstallmentAmounts(ctx.contractAmount, count)
const result: DetailLike[] = []
for (let i = 0; i < count; i++) {
result.push(
createPaymentDetailRow({
dueAmount: amounts[i] ?? null,
installmentNo: i + 1,
installmentDueDate:
startDate && interval
? calcFqInstallmentDueDate(startDate, i, interval, advanceDays)
: '',
expanded: i === 0
})
)
}
return result
}
export function createPaymentDetailRow(options: {
dueAmount: number | null
installmentDueDate?: string
@@ -110,7 +140,7 @@ export function evaluatePayModeSwitch(
}
}
if (fromMode === 'dj' && toMode === 'fq') {
if (fromMode === 'dj' && isInstallmentPayMode(toMode)) {
if (rows.some((d) => isPayDetailLocked(d))) {
return {
action: 'blocked',
@@ -118,13 +148,14 @@ export function evaluatePayModeSwitch(
}
}
const count = ctx.installmentCount || 6
const label = toMode === 'djfq' ? '趸交+分期' : '分期'
return {
action: 'confirm',
summary: `将清除当前趸交计划,按 ${count} 期分期重新生成缴费明细。是否继续?`
summary: `将清除当前趸交计划,按 ${count} 期${label}重新生成缴费明细。是否继续?`
}
}
if (fromMode === 'fq' && toMode === 'dj') {
if (isInstallmentPayMode(fromMode) && toMode === 'dj') {
const dfjCount = countByStatus(rows, 'dfj')
const yjfCount = countByStatus(rows, 'yjf')
@@ -156,6 +187,21 @@ export function evaluatePayModeSwitch(
}
}
if (isInstallmentPayMode(fromMode) && isInstallmentPayMode(toMode) && fromMode !== toMode) {
if (rows.some((d) => isPayDetailLocked(d))) {
return {
action: 'blocked',
reason: '分期已有缴费记录,无法切换分期类型。'
}
}
const count = ctx.installmentCount || 6
const label = toMode === 'djfq' ? '趸交+分期' : '分期'
return {
action: 'confirm',
summary: `将按 ${count} 期${label}重新生成缴费明细。是否继续?`
}
}
return { action: 'noop' }
}
@@ -167,33 +213,11 @@ export function applyPayModeSwitch(
): DetailLike[] {
const rows = activeDetails(details)
if (fromMode === 'dj' && toMode === 'fq') {
const count = Number(ctx.installmentCount) || 6
const interval = Number(ctx.installmentIntervalMonths) || 6
const startDate = ctx.installmentStartDate || ''
const advanceDays = Number(ctx.installmentAdvanceDays ?? DEFAULT_INSTALLMENT_ADVANCE_DAYS)
const amounts =
ctx.installmentRatios?.length && sumPaidAmount(rows) === 0
? ctx.installmentRatios.map((v) => Number(v || 0))
: defaultInstallmentAmounts(ctx.contractAmount, count)
const result: DetailLike[] = []
for (let i = 0; i < count; i++) {
result.push(
createPaymentDetailRow({
dueAmount: amounts[i] ?? null,
installmentNo: i + 1,
installmentDueDate:
startDate && interval
? calcFqInstallmentDueDate(startDate, i, interval, advanceDays)
: '',
expanded: i === 0
})
)
}
return result
if (fromMode === 'dj' && isInstallmentPayMode(toMode)) {
return buildInstallmentDetails(ctx, rows)
}
if (fromMode === 'fq' && toMode === 'dj') {
if (isInstallmentPayMode(fromMode) && toMode === 'dj') {
const yjfRows = rows
.filter((d) => String(d.payStatus) === 'yjf')
.map((d) => ({ ...d, _payModeLocked: true }))
@@ -209,14 +233,14 @@ export function applyPayModeSwitch(
return [...yjfRows, newDjRow]
}
if (isInstallmentPayMode(fromMode) && isInstallmentPayMode(toMode) && fromMode !== toMode) {
return buildInstallmentDetails(ctx, rows)
}
return rows
}
/** 趸交模式下是否还可添加待缴明细 */
export function canAddPaymentDetail(payMode: string, details: DetailLike[]): boolean {
if (payMode === 'fq') return true
const dfjCount = details.filter(
(d) => !isPayDetailLocked(d) && String(d.payStatus || 'dfj') === 'dfj'
).length
return dfjCount < 1
/** 趸交与分期均允许多条缴费明细(合计须等于合同金额) */
export function canAddPaymentDetail(_payMode: string, _details: DetailLike[]): boolean {
return true
}
@@ -1,5 +1,7 @@
/** 分期缴费:期数对齐、应缴日/金额计算(与 ProtocolFormDialog 逻辑一致) */
import { isInstallmentPayMode } from '@/utils/protocol-payment-presets'
const DAYS_PER_MONTH = 30
export const DEFAULT_INSTALLMENT_ADVANCE_DAYS = 30
@@ -311,7 +313,7 @@ export function alignFqPaymentDetailsForDisplay(
payment: PaymentLike,
details: DetailLike[]
): DetailLike[] {
if (!payment || payment.payMode !== 'fq') return details
if (!payment || !isInstallmentPayMode(String(payment.payMode || ''))) return details
const count = Number(payment.installmentCount)
if (!count || count < 1) return details
@@ -396,7 +398,7 @@ export function syncFqPaymentDetailRowCount(
},
paymentDetails: DetailLike[]
): void {
if (payment.payMode !== 'fq') return
if (!isInstallmentPayMode(payment.payMode)) return
const count = Number(payment.installmentCount)
if (!count || count < 1) return
@@ -0,0 +1,58 @@
/** 采集协议单 · 缴费默认定价表(样本数 × 缴费模式) */
export type PayMode = 'dj' | 'fq' | 'djfq'
export type PaymentPreset = {
contractAmount: number
installmentCount?: number
installmentAmounts?: number[]
}
const PRESETS: Record<1 | 2, Partial<Record<PayMode, PaymentPreset>>> = {
1: {
dj: { contractAmount: 22880 },
fq: {
contractAmount: 25880,
installmentCount: 6,
installmentAmounts: [6480, 3880, 3880, 3880, 3880, 3880]
}
},
2: {
dj: { contractAmount: 37880 },
djfq: {
contractAmount: 41000,
installmentCount: 4,
installmentAmounts: [29360, 3880, 3880, 3880]
},
fq: {
contractAmount: 44000,
installmentCount: 5,
installmentAmounts: [12960, 7760, 7760, 7760, 7760]
}
}
}
export function getActiveSampleCount(samples: Array<{ sampleStatus?: string }>): 1 | 2 {
const n = samples.filter((s) => String(s.sampleStatus || '') !== 'yzf').length
return (n >= 2 ? 2 : 1) as 1 | 2
}
export function resolvePaymentPreset(
sampleCount: 1 | 2,
payMode: PayMode
): PaymentPreset | null {
return PRESETS[sampleCount]?.[payMode] ?? null
}
export function isInstallmentPayMode(payMode: string): boolean {
return payMode === 'fq' || payMode === 'djfq'
}
export function availablePayModes(sampleCount: 1 | 2): PayMode[] {
return sampleCount === 2 ? ['dj', 'djfq', 'fq'] : ['dj', 'fq']
}
/** 校验缴费模式与样本数是否匹配(如 djfq 仅双样本) */
export function isPayModeValidForSampleCount(payMode: PayMode, sampleCount: 1 | 2): boolean {
return availablePayModes(sampleCount).includes(payMode)
}
@@ -40,7 +40,7 @@ export type ProtocolStandardImportParsed = {
medicalStaff?: string
hospitalName?: string
salesUserName?: string
payMode: 'dj' | 'fq'
payMode: 'dj' | 'fq' | 'djfq'
contractAmount?: number | null
transferTypeLabel?: string
paymentTransfers: ProtocolPaymentTransferHint[]
@@ -193,12 +193,13 @@ function earliestCollectTime(hints: ProtocolSampleImportHint[]): string | undefi
return times.sort((a, b) => a.localeCompare(b))[0]
}
/** 缴费方式:仅「分期」为 fq,其余(含微信/趸交)均为 dj */
/** 缴费方式:趸交+分期 → djfq;仅「分期」→ fq;其余 → dj */
export function parsePayModeFromText(value: string): {
payMode: 'dj' | 'fq'
payMode: 'dj' | 'fq' | 'djfq' | 'djfq'
transferLabel?: string
} {
const v = (value || '').trim()
if (/趸交\s*[++]\s*分期|趸交.*分期/.test(v)) return { payMode: 'djfq' }
if (/分期/.test(v)) return { payMode: 'fq' }
if (/微信|支付宝|银行卡|银行|现金/.test(v)) {
return { payMode: 'dj', transferLabel: v }
+2 -1
View File
@@ -117,7 +117,8 @@ export const auditStatusTagType = createTagTypeGetter(auditStatus)
export const payMode: StatusRegistry = {
dj: { label: '趸交', tagType: 'primary' },
fq: { label: '分期', tagType: 'warning' }
fq: { label: '分期', tagType: 'warning' },
djfq: { label: '趸交+分期', tagType: 'warning' }
}
export const payModeLabel = createLabelGetter(payMode)
export const payModeTagType = createTagTypeGetter(payMode)
@@ -529,10 +529,19 @@
<div class="section-subtitle">缴费主表</div>
<div class="form-grid form-grid--col6">
<el-form-item label="缴费模式">
<el-radio-group v-model="formData.payMode" @change="onPayModeChange">
<el-radio label="dj">趸交</el-radio>
<el-radio label="fq">分期</el-radio>
</el-radio-group>
<el-select
v-model="formData.payMode"
placeholder="请选择缴费模式"
style="width: 100%"
@change="onPayModeChange"
>
<el-option
v-for="opt in payModeOptions"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
</el-form-item>
<el-form-item label="合同金额(元)">
<AmountInput
@@ -556,7 +565,7 @@
/>
</el-form-item>
</template>
<template v-if="formData.payMode === 'fq'">
<template v-if="isInstallmentPayMode(formData.payMode)">
<el-form-item label="分期期数">
<el-input
v-model="formData.installmentCount"
@@ -602,39 +611,6 @@
</el-form-item>
</template>
</div>
<div v-if="formData.payMode === 'fq'" class="installment-amounts-row">
<el-form-item label="分期金额(元)" class="installment-amounts-field">
<div class="installment-amounts-cells">
<div
v-for="(_amount, rIdx) in formData.installmentRatios"
:key="rIdx"
class="installment-amount-cell"
>
<span class="installment-amount-cell-no">{{ rIdx + 1 }}期</span>
<AmountInput
v-model="formData.installmentRatios[rIdx]"
:min="0"
placeholder="金额"
@change="onInstallmentAmountChange(rIdx)"
/>
</div>
</div>
</el-form-item>
<div class="installment-amounts-summary-row">
<span
class="installment-amounts-sum"
:class="{ 'is-error': !!installmentAmountsError }"
>
合计 {{ fmtMoney(installmentAmountsSum) }}
<template v-if="formData.contractAmount != null">
/ 合同 {{ fmtMoney(formData.contractAmount) }}
</template>
</span>
<div v-if="installmentAmountsError" class="form-tip form-tip--error">
{{ installmentAmountsError }}
</div>
</div>
</div>
</div>
<!-- 缴费明细 -->
@@ -693,7 +669,7 @@
type="danger"
size="small"
class="detail-delete"
@click.stop="formData.paymentDetails.splice(dIdx, 1)"
@click.stop="removePaymentDetail(dIdx)"
>
<el-icon><Delete /></el-icon>
</el-button>
@@ -963,22 +939,28 @@
import { useUserStore } from '@/store/modules/user'
import {
DEFAULT_INSTALLMENT_ADVANCE_DAYS,
isInstallmentAmountsValid,
normalizeInstallmentAmounts,
resizeInstallmentAmounts,
scaleInstallmentAmountsToContract,
sumInstallmentAmounts,
syncFqPaymentDetailRowCount
} from '@/utils/protocol-payment-installment'
import {
applyPayModeSwitch,
canAddPaymentDetail,
createPaymentDetailRow,
evaluatePayModeSwitch,
isPayDetailLocked,
markPayDetailLocks,
sumPaidAmount,
type PayMode
} from '@/utils/protocol-pay-mode-switch'
import {
availablePayModes,
getActiveSampleCount,
isInstallmentPayMode,
isPayModeValidForSampleCount,
resolvePaymentPreset,
type PayMode as PresetPayMode
} from '@/utils/protocol-payment-presets'
import {
applyFqParamsChange,
evaluateFqParamsChange,
@@ -1019,6 +1001,21 @@
/** 上次确认生效的缴费模式(用于切换取消/拦截时回滚) */
const lastConfirmedPayMode = ref<PayMode>('dj')
const activeSampleCount = computed(() => getActiveSampleCount(formData.samples))
const payModeOptions = computed(() => {
const modes = [...availablePayModes(activeSampleCount.value)]
const current = String(formData.payMode || '') as PresetPayMode
if (current && !modes.includes(current)) {
modes.push(current)
}
return modes.map((value) => ({ value, label: payModeLabel(value) }))
})
function hasLockedPaymentDetails() {
return formData.paymentDetails.some((d) => isPayDetailLocked(d))
}
/** 上次确认生效的分期参数(用于编辑时修改期数/间隔/开始时间回滚) */
const lastConfirmedFqParams = ref<FqParams>({
installmentCount: null,
@@ -1357,6 +1354,9 @@
function scrollToSection(key: string) {
activeFormTab.value = key
if (key === 'payment') {
maybeApplyPaymentPresetOnTabEnter()
}
const el = document.getElementById(`form-section-${key}`)
if (el) {
el.scrollIntoView({ behavior: 'smooth', block: 'start' })
@@ -1386,6 +1386,13 @@
}
}
function removePaymentDetail(dIdx: number) {
const detail = formData.paymentDetails[dIdx]
if (detail && isPayDetailLocked(detail)) return
formData.paymentDetails.splice(dIdx, 1)
syncContractAmountFromPaymentDetails()
}
function addPaymentDetail() {
if (!canAddPaymentDetail(formData.payMode, formData.paymentDetails)) return
const now = new Date()
@@ -1446,8 +1453,13 @@
*/
function applyFqPaymentDefaults(options?: { resetCountInterval?: boolean }) {
const reset = options?.resetCountInterval ?? false
const sampleCount = getActiveSampleCount(formData.samples)
const preset = resolvePaymentPreset(sampleCount, formData.payMode as PresetPayMode)
if (preset?.contractAmount != null) {
formData.contractAmount = preset.contractAmount
}
if (reset || isFqNumericFieldEmpty(formData.installmentCount)) {
formData.installmentCount = 6
formData.installmentCount = preset?.installmentCount ?? 6
}
if (reset || isFqNumericFieldEmpty(formData.installmentIntervalMonths)) {
formData.installmentIntervalMonths = 6
@@ -1459,11 +1471,58 @@
formData.installmentAdvanceDays = DEFAULT_INSTALLMENT_ADVANCE_DAYS
}
const count = toIntOrUndefined(formData.installmentCount) || 6
formData.installmentRatios = normalizeInstallmentAmounts(
formData.installmentRatios || [],
formData.contractAmount,
count
)
if (preset?.installmentAmounts?.length) {
formData.installmentRatios = [...preset.installmentAmounts]
} else {
formData.installmentRatios = normalizeInstallmentAmounts(
formData.installmentRatios || [],
formData.contractAmount,
count
)
}
}
function applyPaymentPresetForMode(payMode: PresetPayMode) {
const sampleCount = getActiveSampleCount(formData.samples)
const preset = resolvePaymentPreset(sampleCount, payMode)
if (!preset) return
formData.contractAmount = preset.contractAmount
if (isInstallmentPayMode(payMode) && preset.installmentCount) {
formData.installmentCount = preset.installmentCount
if (isFqNumericFieldEmpty(formData.installmentIntervalMonths)) {
formData.installmentIntervalMonths = 6
}
if (!formData.installmentStartDate) {
formData.installmentStartDate = defaultInstallmentStartDateYmd()
}
if (formData.installmentAdvanceDays == null) {
formData.installmentAdvanceDays = DEFAULT_INSTALLMENT_ADVANCE_DAYS
}
formData.installmentRatios = [...(preset.installmentAmounts || [])]
formData.paymentDetails = generateInitialFqDetails(
getFqParamsFromForm(),
preset.contractAmount
)
initPaymentDetailExpandFlags(formData.paymentDetails)
markPayDetailLocks(formData.paymentDetails)
syncLastConfirmedFqParams()
} else {
formData.paymentDetails = [
createPaymentDetailRow({
dueAmount: preset.contractAmount,
installmentDueDate: formData.lumpSumDeadline || '',
expanded: true
})
]
initPaymentDetailExpandFlags(formData.paymentDetails)
markPayDetailLocks(formData.paymentDetails)
}
}
function maybeApplyPaymentPresetOnTabEnter() {
if (hasLockedPaymentDetails()) return
if (formData.paymentDetails.length > 0) return
applyPaymentPresetForMode(formData.payMode as PresetPayMode)
}
function toIntOrUndefined(val: unknown): number | undefined {
@@ -1529,7 +1588,7 @@
* 与缴费模式切换独立;保留 yjf 已缴行,作废原 dfj 后按剩余金额重建。
*/
async function handleFqParamsChange() {
if (formData.payMode !== 'fq') return
if (formData.payMode !== 'fq' && formData.payMode !== 'djfq') return
const newParams = getFqParamsFromForm()
const oldParams = lastConfirmedFqParams.value
@@ -1625,25 +1684,35 @@
}
}
if (toMode === 'fq') {
applyFqPaymentDefaults({ resetCountInterval: false })
if (!hasLockedPaymentDetails()) {
applyPaymentPresetForMode(toMode as PresetPayMode)
} else {
if (isInstallmentPayMode(toMode)) {
applyFqPaymentDefaults({ resetCountInterval: fromMode === 'dj' })
}
formData.paymentDetails = applyPayModeSwitch(
fromMode,
toMode,
formData.paymentDetails,
payModeSwitchContext()
)
initPaymentDetailExpandFlags(formData.paymentDetails)
markPayDetailLocks(formData.paymentDetails)
const preset = resolvePaymentPreset(
getActiveSampleCount(formData.samples),
toMode as PresetPayMode
)
if (preset?.contractAmount != null) {
formData.contractAmount = preset.contractAmount
}
}
formData.paymentDetails = applyPayModeSwitch(
fromMode,
toMode,
formData.paymentDetails,
payModeSwitchContext()
)
initPaymentDetailExpandFlags(formData.paymentDetails)
markPayDetailLocks(formData.paymentDetails)
if (toMode === 'dj') {
syncLumpSumDueDate()
}
lastConfirmedPayMode.value = toMode
if (toMode === 'fq') {
if (isInstallmentPayMode(toMode)) {
syncLastConfirmedFqParams()
}
}
@@ -1770,7 +1839,7 @@
if (loadedAmounts.length) {
formData.installmentRatios = loadedAmounts.map((v: unknown) => Number(v))
}
if (formData.payMode === 'fq') {
if (isInstallmentPayMode(formData.payMode)) {
applyFqPaymentDefaults()
const count = toIntOrUndefined(formData.installmentCount) || 6
formData.installmentRatios = normalizeInstallmentAmounts(
@@ -1793,7 +1862,9 @@
markPayDetailLocks(formData.paymentDetails)
}
syncFqPaymentDetailRowCount(fqPaymentContext(), formData.paymentDetails)
lastConfirmedPayMode.value = (formData.payMode === 'fq' ? 'fq' : 'dj') as PayMode
lastConfirmedPayMode.value = (
['fq', 'djfq'].includes(formData.payMode) ? formData.payMode : 'dj'
) as PayMode
const pa =
(row as any).protocolAttachments || (bundle.protocol as any)?.protocolAttachments || ''
@@ -1880,16 +1951,16 @@
async function handleSubmit() {
if (!formRef.value) return
await formRef.value.validate()
if (formData.payMode === 'fq') {
if (installmentAmountsError.value) {
await ElMessageBox.alert(installmentAmountsError.value, '分期金额无效', { type: 'warning' })
return
}
const amounts = (formData.installmentRatios || []).map((v) => Number(v || 0))
if (!isInstallmentAmountsValid(amounts, formData.contractAmount)) {
await ElMessageBox.alert('分期金额合计须等于合同金额', '分期金额无效', { type: 'warning' })
return
}
const sampleCount = getActiveSampleCount(formData.samples)
if (!isPayModeValidForSampleCount(formData.payMode as PresetPayMode, sampleCount)) {
await ElMessageBox.alert(
formData.payMode === 'djfq'
? '「趸交+分期」仅适用于 2 个样本,请切换缴费模式'
: '当前缴费模式与样本数量不匹配,请切换缴费模式',
'无法保存',
{ type: 'warning' }
)
return
}
if (!isEdit.value) {
for (const sampleRow of formData.samples) {
@@ -1969,8 +2040,7 @@
installmentIntervalMonths: toFloatOrUndefined(formData.installmentIntervalMonths),
installmentStartDate: formData.installmentStartDate || undefined,
installmentAdvanceDays: toIntOrUndefined(formData.installmentAdvanceDays),
installmentRatios:
formData.payMode === 'fq'
installmentRatios: isInstallmentPayMode(formData.payMode)
? (formData.installmentRatios || []).map((v) => Number(v || 0))
: undefined
},
@@ -2596,7 +2666,7 @@
formData.payMode = parsed.payMode || 'dj'
lastConfirmedPayMode.value = formData.payMode as PayMode
if (parsed.payMode === 'fq') {
if (isInstallmentPayMode(parsed.payMode || 'dj')) {
applyFqPaymentDefaults()
}
@@ -3111,38 +3181,32 @@
handleFqParamsChange()
}
function onInstallmentAmountChange(_changedIdx: number) {
syncPaymentDueAmounts()
handleFqParamsChange()
/** 缴费明细应缴金额之和回写合同金额 */
function syncContractAmountFromPaymentDetails() {
if (!formData.paymentDetails.length) return
const total = formData.paymentDetails.reduce(
(sum, d) => sum + (Number(d.dueAmount) || 0),
0
)
formData.contractAmount = Math.round(total * 100) / 100
}
/** 缴费明细应缴金额变更:分期模式下回写分期金额,保持与主表一致 */
/** 缴费明细应缴金额变更:分期回写 installmentRatios,并同步合同金额 */
function onPaymentDetailDueAmountChange(dIdx: number) {
if (formData.payMode !== 'fq') return
const detail = formData.paymentDetails[dIdx]
if (!detail || isPayDetailLocked(detail)) return
const installmentNo = Number(detail.installmentNo)
const ratioIdx = installmentNo >= 1 ? installmentNo - 1 : dIdx
if (!formData.installmentRatios) formData.installmentRatios = []
while (formData.installmentRatios.length <= ratioIdx) {
formData.installmentRatios.push(0)
if (formData.payMode === 'fq' || formData.payMode === 'djfq') {
const installmentNo = Number(detail.installmentNo)
const ratioIdx = installmentNo >= 1 ? installmentNo - 1 : dIdx
if (!formData.installmentRatios) formData.installmentRatios = []
while (formData.installmentRatios.length <= ratioIdx) {
formData.installmentRatios.push(0)
}
formData.installmentRatios[ratioIdx] = Number(detail.dueAmount) || 0
}
formData.installmentRatios[ratioIdx] = Number(detail.dueAmount) || 0
syncContractAmountFromPaymentDetails()
}
const installmentAmountsError = computed(() => {
const amounts = formData.installmentRatios || []
if (!amounts.length || formData.contractAmount == null) return ''
if (amounts.some((v) => Number(v) < 0)) return '分期金额不能为负数'
const sum = sumInstallmentAmounts(amounts)
if (Math.abs(sum - Number(formData.contractAmount)) > 0.01) {
return `分期金额合计须等于合同金额(${fmtMoney(formData.contractAmount)})`
}
return ''
})
const installmentAmountsSum = computed(() => sumInstallmentAmounts(formData.installmentRatios || []))
/** 按缴费模式同步各明细应缴费金额(跳过已锁定明细) */
function syncPaymentDueAmounts() {
const contract = formData.contractAmount
@@ -3159,14 +3223,14 @@
})
return
}
if (formData.payMode === 'fq') {
const count = formData.installmentCount || formData.paymentDetails.length
if (formData.payMode === 'fq' || formData.payMode === 'djfq') {
const count = Number(formData.installmentCount || formData.paymentDetails.length)
if (!count) return
const amounts = normalizeInstallmentAmounts(
formData.installmentRatios || [],
contract,
Number(count)
)
const ratios = formData.installmentRatios || []
const amounts =
ratios.length === count
? ratios.map((v) => Number(v || 0))
: normalizeInstallmentAmounts(ratios, null, count)
const paidRows = formData.paymentDetails.filter((d) => String(d.payStatus) === 'yjf')
paidRows.forEach((d) => {
const no = Number(d.installmentNo)
@@ -3179,37 +3243,16 @@
}
}
/** 合同金额手动变更:等比缩放分期金额并同步应缴费金额 */
/** 合同金额手动变更:趸交时同步待缴明细应缴金额 */
function onContractAmountChange() {
if (formData.payMode === 'fq' && (formData.installmentRatios || []).length) {
formData.installmentRatios = scaleInstallmentAmountsToContract(
formData.installmentRatios,
formData.contractAmount
)
if (formData.payMode === 'dj') {
syncPaymentDueAmounts()
}
syncPaymentDueAmounts()
}
/** 根据非作废样本价格之和同步合同金额,并联动应缴费金额 */
function syncContractAmount() {
const total = formData.samples.reduce((sum, s) => {
if (s.sampleStatus === 'yzf') return sum
return sum + (Number(s.price) || 0)
}, 0)
const nextContract = Math.round(total * 100) / 100
if (formData.payMode === 'fq' && (formData.installmentRatios || []).length) {
formData.installmentRatios = scaleInstallmentAmountsToContract(
formData.installmentRatios,
nextContract
)
}
formData.contractAmount = nextContract
syncPaymentDueAmounts()
}
/** 样本价格或状态变更 */
/** 样本价格或状态变更(不自动改合同金额) */
function onSamplePriceChange() {
syncContractAmount()
// 保留样本价格字段,不与合同金额联动
}
/* ====== 母亲身份证号 ↔ 出生年月联动 ====== */
@@ -3232,6 +3275,22 @@
() => calcBirthFromIdCard()
)
/** 样本数变化时按当前缴费模式刷新默认定价(新增单;无已缴锁定明细时整单重填) */
watch(activeSampleCount, () => {
if (isEdit.value) return
if (hasLockedPaymentDetails()) {
const preset = resolvePaymentPreset(
getActiveSampleCount(formData.samples),
formData.payMode as PresetPayMode
)
if (preset?.contractAmount != null) {
formData.contractAmount = preset.contractAmount
}
return
}
applyPaymentPresetForMode(formData.payMode as PresetPayMode)
})
/* ====== 趸交截止时间联动 ====== */
watch(
() => formData.lumpSumDeadline,
@@ -642,7 +642,7 @@
><div class="pm-value">{{ formatYmd(payment?.lumpSumDeadline) }}</div></div
>
</template>
<template v-if="payment?.payMode === 'fq'">
<template v-if="isInstallmentPayMode(String(payment?.payMode || ''))">
<div class="payment-metric"
><div class="pm-label">分期期数</div
><div class="pm-value">{{ dash(payment?.installmentCount) }}</div></div
@@ -720,11 +720,11 @@
<span class="info-label">转账时间</span>
<span class="info-value">{{ dash(detail.transferTime) }}</span>
</div>
<div v-if="payment?.payMode === 'fq'" class="pdc-info-item">
<div v-if="isInstallmentPayMode(String(payment?.payMode || ''))" class="pdc-info-item">
<span class="info-label">应缴费时间</span>
<span class="info-value">{{ formatYmd(detail.installmentDueDate) }}</span>
</div>
<div v-if="payment?.payMode === 'fq'" class="pdc-info-item">
<div v-if="isInstallmentPayMode(String(payment?.payMode || ''))" class="pdc-info-item">
<span class="info-label">分期期序号</span>
<span class="info-value">{{ dash(detail.installmentNo) }}</span>
</div>
@@ -1111,6 +1111,7 @@
import { loadTransferTypeDict, transferTypeLabel } from '@/utils/protocol-payment-labels'
import { loadVoidTypeDict, voidTypeLabel } from '@/utils/void-type-dict'
import { formatProtocolDocSubject } from '@/utils/protocol-doc-label'
import { isInstallmentPayMode } from '@/utils/protocol-payment-presets'
const props = withDefaults(
defineProps<{
@@ -1492,7 +1493,9 @@
const steps: OverviewTimelineStep[] = details.map((d, i) => {
const s = String(d.payStatus || '')
const name =
payment.value?.payMode === 'fq' ? `第${d.installmentNo || i + 1}期` : `缴费${i + 1}`
isInstallmentPayMode(String(payment.value?.payMode || ''))
? `第${d.installmentNo || i + 1}期`
: `缴费${i + 1}`
if (s === 'ytf') {
return {
@@ -183,6 +183,7 @@
payStatusTagType
} from '@/utils/status-tag-registry'
import type { TagType } from '@/utils/status-tag-registry'
import { isInstallmentPayMode } from '@/utils/protocol-payment-presets'
defineOptions({ name: 'QcWorkOrderProtocolPaymentPanel' })
@@ -198,7 +199,7 @@
hideInstallmentDetailAmounts?: boolean
}>()
const isInstallment = computed(() => props.summary?.payMode === 'fq')
const isInstallment = computed(() => isInstallmentPayMode(props.summary?.payMode || ''))
const isLumpSum = computed(() => props.summary?.payMode === 'dj')