@brms/ai-skills 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +256 -0
  2. package/bin/brms-skills.mjs +411 -0
  3. package/package.json +30 -0
  4. package/skills/brms-prototype-generator/SKILL.md +129 -0
  5. package/skills/brms-prototype-generator/agents/openai.yaml +7 -0
  6. package/skills/brms-prototype-generator/examples/few-shot-examples.md +577 -0
  7. package/skills/brms-prototype-generator/references/01-list-query.md +444 -0
  8. package/skills/brms-prototype-generator/references/02-form-entry.md +129 -0
  9. package/skills/brms-prototype-generator/references/03-detail-display.md +125 -0
  10. package/skills/brms-prototype-generator/references/04-composite-page-package.md +339 -0
  11. package/skills/brms-prototype-generator/references/05-dialog-patterns.md +113 -0
  12. package/skills/brms-prototype-generator/references/06-backend-request-patterns.md +118 -0
  13. package/skills/brms-prototype-generator/references/resource-index.md +46 -0
  14. package/skills/brms-prototype-generator/references/system-prompt.md +242 -0
  15. package/skills/brms-prototype-generator/scripts/analyze-doc.mjs +554 -0
  16. package/skills/brms-prototype-generator/scripts/check-project.mjs +228 -0
  17. package/skills/brms-prototype-generator/scripts/discover-targets.mjs +158 -0
  18. package/skills/brms-prototype-generator/scripts/install-codex.mjs +74 -0
  19. package/skills/brms-prototype-generator/scripts/plan-pages.mjs +390 -0
  20. package/skills/brms-prototype-generator/scripts/validate-generated.mjs +838 -0
  21. package/skills/brms-prototype-generator/templates/user-input-template.md +182 -0
  22. package/skills/brms-vxe-plus-developer/SKILL.md +105 -0
  23. package/skills/brms-vxe-plus-developer/agents/openai.yaml +7 -0
  24. package/skills/brms-vxe-plus-developer/references/prototype-to-real.md +54 -0
  25. package/skills/brms-vxe-plus-developer/references/real-base-development.md +110 -0
  26. package/skills/brms-vxe-plus-developer/references/resource-index.md +43 -0
  27. package/skills/brms-vxe-plus-developer/references/review-checklist.md +49 -0
  28. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/01-mental-model.md +150 -0
  29. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/02-vxe-plus-form.md +302 -0
  30. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/03-vxe-plus-table.md +253 -0
  31. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/04-example-map.md +488 -0
  32. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/05-request-and-eiinfo.md +170 -0
  33. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/90-anti-patterns.md +137 -0
  34. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/README.md +43 -0
  35. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/README.md +21 -0
  36. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/A1/P0/A1P01601.vue +483 -0
  37. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/A1/P1/A1P11011.vue +444 -0
  38. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/AB/BP/ABBP0201.vue +1648 -0
  39. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/AM/AF/component/AMAF0601/Bidding/formConfig.ts +228 -0
  40. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/AM/AF/component/AMAF0601/Record/columns.ts +110 -0
  41. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/BM/BR/BMBR01.vue +130 -0
  42. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/BM/BR/component/BMBR01/columns.ts +94 -0
  43. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/BM/BR/component/BMBR01/formConfig.ts +108 -0
  44. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Change/formConfig.ts +123 -0
  45. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Change/index.vue +103 -0
  46. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Clause/columns.ts +48 -0
  47. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Clause/index.vue +202 -0
  48. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Correcte/formConfig.ts +117 -0
  49. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Correcte/index.vue +103 -0
  50. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Pay/Payment/formConfig.ts +90 -0
  51. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Pay/Payment/index.vue +42 -0
  52. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Pay/columns.ts +376 -0
  53. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Pay/index.vue +619 -0
  54. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Settle/Domestic/formConfig.ts +73 -0
  55. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Settle/Domestic/index.vue +47 -0
  56. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Settle/Foreign/formConfig.ts +141 -0
  57. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Settle/Foreign/index.vue +42 -0
  58. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Settle/columns.ts +123 -0
  59. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Settle/index.vue +593 -0
  60. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/index.vue +68 -0
  61. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Fee/columns.ts +150 -0
  62. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Fee/index.vue +235 -0
  63. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Files/columns.ts +63 -0
  64. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Files/index.vue +117 -0
  65. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Goods/columns.ts +327 -0
  66. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Goods/index.vue +790 -0
  67. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Base/Approve/formConfig.ts +341 -0
  68. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Base/Approve/index.vue +63 -0
  69. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Base/Approve2/formConfig.ts +232 -0
  70. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Base/Approve2/index.vue +27 -0
  71. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Base/Diff/columns.ts +46 -0
  72. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Base/Diff/index.vue +92 -0
  73. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Base/formConfig.ts +979 -0
  74. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Base/index.vue +62 -0
  75. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Other/formConfig.ts +179 -0
  76. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Other/index.vue +140 -0
  77. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Sign/formConfig.ts +118 -0
  78. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Sign/index.vue +44 -0
  79. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/index.vue +168 -0
  80. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Party/Major/formConfig.ts +257 -0
  81. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Party/Major/index.vue +47 -0
  82. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Party/columns.ts +256 -0
  83. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Party/index.vue +738 -0
  84. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Price/formConfig.ts +174 -0
  85. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Price/index.vue +51 -0
  86. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Top/index.vue +924 -0
  87. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/SM/SW/SMSW0101.vue +567 -0
  88. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/demo/index.vue +448 -0
  89. package/skills/brms-vxe-plus-developer/scripts/check-project.mjs +259 -0
  90. package/skills/brms-vxe-plus-developer/scripts/check-vxe-plus-page.mjs +137 -0
@@ -0,0 +1,1648 @@
1
+ <template>
2
+ <bx-container id="INQU" ref="detailContainer" title="询报价详情" style="background: #F2F3F5;" auto show-collapse @btn-click="btnClick">
3
+ <div class="bx-container-ABBP">
4
+ <div class="formBox">
5
+ <bx-container title="基础信息">
6
+ <div class="bx-container-ABBP">
7
+ <VxePlusForm id="inqu_status" ref="base" :form-options="formConfigSet">
8
+ <template #fileCount_default="{ data }">
9
+ <el-button type="primary" @click="handleUpload(data)">
10
+ 附件管理
11
+ </el-button>
12
+ <!-- <span class="ml10 red font12 bold">附件个数:{{ data.fileCount }}</span> -->
13
+ </template>
14
+ <template #proposalInfo_default>
15
+ <vxe-textarea v-if="proposalInfoFlag" v-model="formDataSet.proposalInfo" :disabled="!editable" placeholder="请输入主要内容" :rows="3" :autosize="{ minRows: 3, maxRows: 10 }" />
16
+ </template>
17
+ </VxePlusForm>
18
+ <el-divider v-if="buyFlag" />
19
+ <div v-if="buyFlag" class="dashed-box">
20
+ <p>采购</p>
21
+ <div class="form-container">
22
+ <VxePlusForm ref="buyParty" :key="`buyParty${key}`" :form-options="formPartyBuy" />
23
+ </div>
24
+ </div>
25
+ <el-divider />
26
+ <div v-if="saleFlag" class="dashed-box">
27
+ <p>销售</p>
28
+ <div class="form-container">
29
+ <VxePlusForm ref="saleParty" :key="`saleParty${key}`" :form-options="formPartySale" />
30
+ </div>
31
+ </div>
32
+ <el-divider v-if="saleFlag" />
33
+ </div>
34
+ </bx-container>
35
+ </div>
36
+ <div v-if="buyFlag || saleFlag" class="formBox flex-row">
37
+ <bx-container v-if="buyFlag" class="flex-1" title="采购信息">
38
+ <VxePlusForm id="inqu3_status" ref="buy" :form-options="formConfigBuy">
39
+ <template #buyer_settleInfo_default>
40
+ <vxe-textarea v-if="buySettleInfoFlag" v-model="formDataBuy.settleInfo" :disabled="!editable" placeholder="请输入结算付款说明" :rows="3" :autosize="{ minRows: 3, maxRows: 10 }" />
41
+ </template>
42
+ </VxePlusForm>
43
+ </bx-container>
44
+ <bx-container v-if="saleFlag" class="flex-1" title="销售信息">
45
+ <VxePlusForm id="inqu4_status" ref="sale" :form-options="formConfigSale">
46
+ <template #seller_settleInfo_default>
47
+ <vxe-textarea v-if="saleSettleInfoFlag" v-model="formDataSale.settleInfo" :disabled="!editable" placeholder="请输入结算收款说明" :rows="3" :autosize="{ minRows: 3, maxRows: 10 }" />
48
+ </template>
49
+ </VxePlusForm>
50
+ </bx-container>
51
+ </div>
52
+ <div v-if="buyFlag || saleFlag" class="formBox">
53
+ <bx-container v-if="buyFlag || saleFlag" id="INQU_GOODS" ref="goodsContainer" auto title="商品信息" @btn-click="goodsClick">
54
+ <div class="bx-container-ABBP">
55
+ <div v-for="(item, idx) in goodsList" :key="idx" class="goods-box">
56
+ <div class="goods-index-wrapper">
57
+ <span class="goods-box-index" :data-index="idx + 1">&nbsp;</span>
58
+ </div>
59
+ <VxePlusForm :ref="(el) => setGoodsFormRef(el, idx)" :key="idx" :form-options="item">
60
+ <template #priceIndexId_title="{ data }">
61
+ <span
62
+ v-if="data.categorySegment1 === 'A01' && (formDataBuy.hedgingFlag === 'Y' || formDataSale.hedgingFlag === 'Y')"
63
+ class="x-is-required"
64
+ >*</span>
65
+ 计价指数
66
+ </template>
67
+ <template #discountRatioPurchase_title="{ data }">
68
+ <span
69
+ v-if="data.categorySegment1 === 'A01' && formDataBuy.hedgingFlag === 'Y'"
70
+ class="x-is-required"
71
+ >*</span>
72
+ 采购套保折扣比例(%)
73
+ </template>
74
+ <template #discountRatioSale_title="{ data }">
75
+ <span
76
+ v-if="data.categorySegment1 === 'A01' && formDataSale.hedgingFlag === 'Y'"
77
+ class="x-is-required"
78
+ >*</span>
79
+ 销售套保折扣比例(%)
80
+ </template>
81
+ <template #goodsContent_title="{ data }">
82
+ <span
83
+ v-if="data.categorySegment1 === 'A01' && (formDataBuy.hedgingFlag === 'Y' || formDataSale.hedgingFlag === 'Y')"
84
+ class="x-is-required"
85
+ >*</span>
86
+ 含量(%)
87
+ </template>
88
+ <template #h2oValueBase_title="{ data }">
89
+ <span
90
+ v-if="data.categorySegment1 === 'A01' && (formDataBuy.hedgingFlag === 'Y' || formDataSale.hedgingFlag === 'Y')"
91
+ class="x-is-required"
92
+ >*</span>
93
+ 水分值(%)
94
+ </template>
95
+ <template #titleSlot>
96
+ <el-divider content-position="left">
97
+ <span style="font-weight: 600;">库存价值</span>
98
+ </el-divider>
99
+ </template>
100
+ <template #pricePurchaseList="{ data }">
101
+ <div class="flex-1 flex items-center whitespace-nowrap">
102
+ <vxe-number-input v-model="data.priceSale" :controls="false" digits="6" type="float" class="flex mx-1" disabled />
103
+ <span>= (货款单价</span>
104
+ <vxe-number-input v-model="data.goodsPrice" :controls="false" digits="6" type="float" class="flex mx-1" :disabled="approveFlag" @input="calculatePriceSale(data)" />
105
+ <span>+ 运费单价</span>
106
+ <vxe-number-input v-model="data.freightPrice" :controls="false" digits="6" type="float" class="flex mx-1" :disabled="approveFlag" @input="calculatePriceSale(data)" />
107
+ <span>)</span>
108
+ <vxe-select v-model="data.priceTaxRateSale" class="flex mx-1" :options="taxRateList" :disabled="approveFlag" />
109
+ <vxe-select v-model="data.priceStyleSale" class="flex mx-1" :options="priceStyleList" :disabled="approveFlag" />
110
+ <vxe-select v-model="data.priceTaxFlagSale" class="flex mx-1" :options="priceTaxFlagList" :disabled="approveFlag" />
111
+ </div>
112
+ </template>
113
+ <template #lessMoreList="{ data }">
114
+ <div class="flex flex-1 items-center whitespace-nowrap">
115
+ <span>+</span>
116
+ <BxInputPercentage v-model="data.moreRatio" class="flex mx-1" :disabled="approveFlag" />
117
+ <span>-</span>
118
+ <BxInputPercentage v-model="data.lessRatio" class="flex mx-1" :disabled="approveFlag" />
119
+ </div>
120
+ </template>
121
+ <template #saleLessMoreList="{ data }">
122
+ <div class="flex flex-1 items-center whitespace-nowrap">
123
+ <span>+</span>
124
+ <BxInputPercentage v-model="data.saleMoreRatio" class="flex mx-1" :disabled="approveFlag" />
125
+ <span>-</span>
126
+ <BxInputPercentage v-model="data.saleLessRatio" class="flex mx-1" :disabled="approveFlag" />
127
+ </div>
128
+ </template>
129
+ </VxePlusForm>
130
+
131
+ <!-- 销售模式下的表格 -->
132
+ <VxePlusTable
133
+ v-if="onlySale"
134
+ id="ABBP0201_result"
135
+ :ref="(el) => setSaleGridRef(el, idx)"
136
+ :columns="columnsBuy"
137
+ :form-config="goodsBuy(item)"
138
+ :service-config="serviceGoodsBuy"
139
+ :show-checkbox="false"
140
+ :is-edit="true"
141
+ :toolbar-buttons="!isDetail ? toolbarButtons : []"
142
+ :edit-rules="editRules"
143
+ :transform-response="transformResponse"
144
+ v-on="gridEvents(item, idx)"
145
+ >
146
+ <template #operation="{ row }">
147
+ <el-link v-if="!isDetail && formConfigSet.data.businessType !== '30' && !approveFlag" type="danger" :underline="false" @click="deleteSaleRow(row, idx)">
148
+ 删除
149
+ </el-link>
150
+ </template>
151
+ </VxePlusTable>
152
+
153
+ <el-button v-if="!isDetail && !approveFlag" class="delBtn" type="danger" @click="delGoods(idx)">
154
+ 删除
155
+ </el-button>
156
+ </div>
157
+ </div>
158
+ </bx-container>
159
+ </div>
160
+ <div v-if="saleFlag" class="formBox">
161
+ <bx-container v-if="saleFlag" auto title="利润预估">
162
+ <div class="bx-container-ABBP">
163
+ <div>
164
+ <VxePlusForm ref="estimate" :form-options="formConfigEstimate">
165
+ <template #titleSuffix="{ data }">
166
+ 预估毛利
167
+ <span title="更新预估毛利">
168
+ <vxe-icon name="refresh" status="primary" @click="updateProposalProfit(data)" />
169
+ </span>
170
+ </template>
171
+ <template #proposalProfitDesc_default>
172
+ <vxe-textarea v-if="proposalProfitDescFlag" v-model="formDataEstimate.proposalProfitDesc" :disabled="!editable" placeholder="请输入说明" :rows="3" :autosize="{ minRows: 3, maxRows: 10 }" />
173
+ </template>
174
+ </VxePlusForm>
175
+ </div>
176
+ <div>
177
+ <vxe-checkbox-group v-model="valGroup1" :disabled="approveFlag" @change="groupChange">
178
+ <vxe-checkbox
179
+ v-for="option in checkboxOptions" :key="option.value" :label="option.value"
180
+ :content="option.label"
181
+ />
182
+ </vxe-checkbox-group>
183
+ </div>
184
+ <div class="flex flex-1 flex-col">
185
+ <VxePlusTable
186
+ id="ABBP0201_result" ref="xGrid" :columns="columns" :form-config="formConfig"
187
+ :service-config="serviceConfig" :show-checkbox="false" :is-edit="true"
188
+ />
189
+ </div>
190
+ </div>
191
+ </bx-container>
192
+ </div>
193
+ </div>
194
+ </bx-container>
195
+ </template>
196
+
197
+ <script setup lang="ts">
198
+ import { EiBlock, EiCommunicator, EiInfo } from '@eplat/ei'
199
+ import type { Ref } from 'vue'
200
+ import { computed, inject, nextTick, onMounted, reactive, ref, toValue, watch } from 'vue'
201
+ import { ElMessage, ElMessageBox } from 'element-plus'
202
+ import { useRoute, useRouter } from 'vue-router'
203
+ import type { VxeGridProps } from 'vxe-table'
204
+ import { getCodeSetByService, useDialog } from '@vxe-plus/components'
205
+ import { to } from 'await-to-js'
206
+ import { clone } from 'xe-utils'
207
+ import dayjs from 'dayjs'
208
+ import { useTable } from '@/hooks/useTable'
209
+ import { useFormConfigByABBP0201 } from '@/views/AB/BP/component/ABBP02/formConfig'
210
+ import { useRefreshPage } from '@/hooks/useRefreshPage'
211
+ import { useLoading } from '@/hooks/useLoading'
212
+ import { judgeIsDirectPass, useApprove } from '@/hooks/useApprove'
213
+ import { useFileUpload } from '@/hooks/useFileUpload'
214
+ import ABBP02_dialog from '@/views/AB/BP/component/ABBP02/ABBP02_dialog.vue'
215
+ import { useCodeSetStore } from '@/stores/modules/codeSet'
216
+ import order_dialog_new from '@/views/AB/BP/component/dialog/order_dialog_new/index.vue'
217
+ import purchase_resources from '@/views/AB/BP/component/dialog/purchase_resources.vue'
218
+ import sourceSupplier from '@/views/AB/BP/component/dialog/sourceSupplier.vue'
219
+ // 刷新当前页面
220
+ const { refreshPage } = useRefreshPage()
221
+ const { openDialog } = useDialog()
222
+ const { openFileABBP } = useFileUpload()
223
+ // const loading = ref(false)
224
+ const router = useRouter()
225
+ const route = useRoute()
226
+ const xGrid = ref<any>(null)
227
+ const goodsList: Ref<any[]> = ref([])
228
+ const buyFlag = ref(false)
229
+ const saleFlag = ref(false)
230
+ const onlySale = ref(false)
231
+ const initLoadEiInfo = toValue(inject<Ref<EiInfo> | undefined>('__eiInfo__', undefined))
232
+ const base = ref<any>(null)
233
+ const buyParty = ref<any>(null)
234
+ const saleParty = ref<any>(null)
235
+ const buy = ref<any>(null)
236
+ const sale = ref<any>(null)
237
+ const estimate = ref<any>(null)
238
+ const detailContainer = ref<any>(null)
239
+ const goodsContainer = ref<any>(null)
240
+ const isDetail = ref()
241
+ const { startApproveProcess } = useApprove()
242
+ const approveFlag = computed(() => route.query.status === 'true')
243
+
244
+ const proposalInfoFlag = ref(true)
245
+ const buySettleInfoFlag = ref(true)
246
+ const saleSettleInfoFlag = ref(true)
247
+ const proposalProfitDescFlag = ref(true)
248
+
249
+ const isProposalInfoInit = ref(false)
250
+ const isBuyerInit = ref(false)
251
+ const isSaleInit = ref(false)
252
+ const isProposalProfitDescInit = ref(false)
253
+
254
+ // 销售表格引用数组
255
+ const saleGrids = ref<any[]>([])
256
+ // 获取详情跳转过来的主键ID及缓存参数
257
+ const { bpProposalMainId, cacheKey: queryCacheKey } = route.query
258
+ provide('__approveFlag__', route.query.status === 'true' ? true : undefined)
259
+ const { formDataEstimate, formDataSet, formConfigSet, formConfigBuy, formDataBuy, formConfigSale, formDataSale, formConfigGoods, formPartyBuy, formPartySale, formConfigEstimate, key, fieldVisibility } = useFormConfigByABBP0201(bpProposalMainId)
260
+ const serviceConfig = reactive({
261
+ serviceName: 'ABBP02',
262
+ queryMethod: 'queryFee',
263
+ insertMethod: 'addUnit',
264
+ updateMethod: 'save',
265
+ deleteMethod: 'deleteUnit',
266
+ })
267
+ const serviceGoodsBuy = reactive({
268
+ serviceName: 'ABBP02',
269
+ queryMethod: 'queryGoodsRel',
270
+ })
271
+ // 若 URL 带有 cacheKey,则采购资源表格改用 ABBP05.listPagePcGoods
272
+ if (queryCacheKey) {
273
+ serviceGoodsBuy.serviceName = 'ABBP05'
274
+ serviceGoodsBuy.queryMethod = 'listPagePcGoods'
275
+ }
276
+ const toolbarButtons = ref<NonNullable<VxeGridProps['toolbarConfig']>['buttons']>([
277
+ { name: '新增采购资源', code: 'addSaleGoods', status: 'primary' },
278
+ ])
279
+ const { getCodeSetList } = useCodeSetStore()
280
+
281
+ const priceStyleList = ref<any[]>([])
282
+ getCodeSetList('brmerp.abbp.priceStyle').then((res) => {
283
+ priceStyleList.value = res
284
+ })
285
+ const taxRateList = ref<any[]>([])
286
+ getCodeSetList('brmerp.bm.taxRate').then((res) => {
287
+ taxRateList.value = res
288
+ })
289
+ const priceTaxFlagList = ref<any[]>([])
290
+ getCodeSetList('brmerp.pm.priceTaxFlag').then((res) => {
291
+ priceTaxFlagList.value = res
292
+ })
293
+ // 设置销售表格引用(按商品索引)
294
+ function setSaleGridRef(el: any, idx: number) {
295
+ if (el) {
296
+ saleGrids.value[idx] = el
297
+ }
298
+ }
299
+ const formData = reactive({
300
+ bpProposalMainId,
301
+ })
302
+ const formConfig = reactive({
303
+ data: formData,
304
+ })
305
+ const columns = useTable().createColumns([
306
+ {
307
+ type: 'seq',
308
+ title: '序号',
309
+ width: 60,
310
+ fixed: 'left',
311
+ },
312
+ {
313
+ field: 'feeName',
314
+ title: '名称',
315
+ },
316
+ {
317
+ field: 'priceFee',
318
+ title: '不含税单价',
319
+ editRender: {
320
+ name: 'VxeInput',
321
+ immediate: true,
322
+ },
323
+ },
324
+ {
325
+ field: 'measureUnitName',
326
+ title: '计价单位',
327
+ },
328
+ {
329
+ field: 'currencyCode',
330
+ title: '测算币种',
331
+ cellRender: {
332
+ name: 'tableSelect',
333
+ props: {
334
+ codeSet: 'DMDC01.queryByCode',
335
+ promiseFn: params => getCodeSetByService({
336
+ serviceName: 'DMDC01',
337
+ methodName: 'queryByCode',
338
+ blockName: 'currencyBlock',
339
+ params,
340
+ value: 'valueField',
341
+ label: 'textField',
342
+ }),
343
+ backfillFields: 'currencyName',
344
+ },
345
+ },
346
+ },
347
+ {
348
+ field: 'exchangeRate',
349
+ title: '测算汇率',
350
+ },
351
+ ], {
352
+ width: undefined,
353
+ })
354
+ const columnsBuy = useTable().createColumns([
355
+ {
356
+ field: 'quantity',
357
+ title: '采购数量',
358
+ editRender: {
359
+ name: 'VxeInput',
360
+ immediate: true,
361
+
362
+ },
363
+ },
364
+ {
365
+ field: 'relBillType',
366
+ title: '类型',
367
+ cellRender: {
368
+ name: 'tableSelect',
369
+ props: {
370
+ codeSet: 'BRMERP.acbp.relBillType',
371
+ },
372
+ },
373
+ },
374
+ {
375
+ field: 'relBillCode',
376
+ title: '单号',
377
+ },
378
+ {
379
+ field: 'partyName',
380
+ title: '供应商',
381
+ },
382
+ {
383
+ field: 'sourceSupplierName',
384
+ title: '源供应商',
385
+ editRender: {
386
+ name: 'tableSelectDialog',
387
+ props: {
388
+ component: sourceSupplier,
389
+ title: `源供应商查询`,
390
+ height: '600',
391
+ fields: 'sourceSupplierGroupCode,sourceSupplierName',
392
+ dialogFields: 'corpGroupCode,corpName',
393
+ },
394
+ },
395
+ },
396
+ {
397
+ field: 'pricePurchase',
398
+ title: '采购单价',
399
+ },
400
+ {
401
+ field: 'goodsLeftQty',
402
+ title: '未匹配数量',
403
+ },
404
+ {
405
+ field: 'proposalTitle',
406
+ title: '主题',
407
+ },
408
+ {
409
+ field: 'caozuo',
410
+ title: '操作',
411
+ slots: {
412
+ default: 'operation',
413
+ },
414
+ width: 100,
415
+ },
416
+ ], {
417
+ width: undefined,
418
+ })
419
+ const editRules = reactive({
420
+ quantity: [
421
+ {
422
+ validator(data) {
423
+ return new Promise((resolve, reject) => {
424
+ const quantity = Number(data.cellValue)
425
+ const goodsLeftQty = Number(data.row?.goodsLeftQty ?? 0)
426
+ if (!Number.isFinite(quantity) || quantity <= 0) {
427
+ reject(new Error('请输入大于0的采购数量'))
428
+ return
429
+ }
430
+ if (quantity > goodsLeftQty) {
431
+ reject(new Error('采购数量不能大于未匹配数量'))
432
+ return
433
+ }
434
+ resolve('')
435
+ })
436
+ },
437
+ },
438
+ ],
439
+ })
440
+ function goodsBuy(item) {
441
+ return {
442
+ data: {
443
+ bpProposalMainId: item.data.bpProposalGoodsId ? bpProposalMainId : '',
444
+ bpProposalGoodsId: item.data.bpProposalGoodsId,
445
+ },
446
+ }
447
+ }
448
+ // 添加商品表单的ref数组
449
+ const goodsForms = ref<any[]>([])
450
+ /**
451
+ * 参考 queryGoodsRel 的入参方式:按「每个商品」用该商品的 item.data 拼入参,请求 JMJP06/queryPriceGoodsHisMaxMin,回填最低/最高指导价。
452
+ * 每次只查一个商品,入参来自该商品的 data(如 cgGoodsId),与 goodsBuy(item).data 用法一致。
453
+ */
454
+ async function fetchPriceGoodsHisMaxMinForGoods(goodsItems: { data: any }[]) {
455
+ const base = formConfigSet.data
456
+ const sale = formDataSale
457
+ const isStatus40 = base.proposalStatus === '40'
458
+ const isFutureSp = base.saleType === 'FUTURE_SP'
459
+ for (let i = 0; i < goodsItems.length; i++) {
460
+ const item = goodsItems[i]
461
+ const data = item.data
462
+ const cgGoodsId = data?.cgGoodsId
463
+ if (!cgGoodsId)
464
+ continue
465
+ const ei = new EiInfo()
466
+ if (isStatus40)
467
+ ei.set('inqu_status-0-queryTimestampStr', dayjs().format('YYYY-MM-DD HH:mm:ss'))
468
+ ei.set('inqu_status-0-cgGoodsId', cgGoodsId)
469
+ ei.set('inqu_status-0-priceType', base.saleType ?? '')
470
+ ei.set('inqu_status-0-paymentType', sale.paymentType ?? '')
471
+ ei.set('inqu_status-0-currencyCode', sale.currencyCode ?? '')
472
+ if (!isFutureSp) {
473
+ ei.set('inqu_status-0-portCode', sale.portCode ?? '')
474
+ if (data.deliveryType === '10')
475
+ ei.set('inqu_status-0-deliveryType', 'FOB')
476
+ else if (data.deliveryType === '20')
477
+ ei.set('inqu_status-0-deliveryType', 'FOT')
478
+ else
479
+ ei.set('inqu_status-0-deliveryType', data.deliveryType ?? '')
480
+ ei.set('inqu_status-0-priceAdjustType', 'FIXED')
481
+ }
482
+ else {
483
+ ei.set('inqu_status-0-priceTerms', sale.tradeTermsCode ?? '')
484
+ ei.set('inqu_status-0-priceAdjustType', data.priceAdjustType ?? '')
485
+ }
486
+ ei.set('inqu_status-0-pricingUnit', data.priceStyleSale === '20' ? 'WET' : 'DRY')
487
+ try {
488
+ const res = await EiCommunicator.send('JMJP06', 'queryPriceGoodsHisMaxMin', ei)
489
+ const rows = res.getBlock('result')?.getMappedRows()
490
+ const row = rows?.[0]
491
+ if (row) {
492
+ data.minGuidePrice = row.minPrice
493
+ data.maxGuidePrice = row.maxPrice
494
+ }
495
+ }
496
+ catch (_e) {
497
+ console.error('请求 JMJP06/queryPriceGoodsHisMaxMin 失败:', _e)
498
+ }
499
+ }
500
+ }
501
+
502
+ // 更新工具栏按钮
503
+ function updateToolbarButtons() {
504
+ const { proposalType, businessType } = formConfigSet.data
505
+ // 当businessType为'30'时,隐藏所有工具栏按钮
506
+ if (businessType === '30') {
507
+ toolbarButtons.value = []
508
+ return
509
+ }
510
+
511
+ const internalSale = proposalType === 'SC' && businessType === '20'
512
+ const processSale = proposalType === 'SC' && businessType === '40'
513
+ if (internalSale) {
514
+ toolbarButtons.value = [
515
+ { name: '新增采购资源', code: 'addSaleGoods', status: 'primary' },
516
+ { name: '新增采购合同', code: 'addConcat', status: 'primary' },
517
+ ]
518
+ }
519
+ else if (processSale) {
520
+ toolbarButtons.value = [
521
+ { name: '新增加工订单', code: 'addSaleGoods', status: 'primary' },
522
+ { name: '新增采购资源', code: 'addBuyGoods', status: 'primary' },
523
+ ]
524
+ }
525
+ else {
526
+ toolbarButtons.value = [
527
+ { name: '新增采购资源', code: 'addSaleGoods', status: 'primary' },
528
+ ]
529
+ }
530
+ // 刷新表格 重新渲染工具栏按钮
531
+ if (proposalType === 'SC') {
532
+ onlySale.value = false
533
+ nextTick(() => {
534
+ onlySale.value = true
535
+ })
536
+ }
537
+ }
538
+
539
+ const editable = computed(() => {
540
+ if (bpProposalMainId) {
541
+ return ['10', '15'].includes(formConfigSet.data.proposalStatus)
542
+ }
543
+ return true
544
+ })
545
+
546
+ // 采购资源表格加载后的数据加工
547
+ function transformResponse(data: any[]) {
548
+ // 仅当 URL 带有 cacheKey,且当前使用 ABBP05.listPagePcGoods 时才处理
549
+ if (!queryCacheKey || serviceGoodsBuy.serviceName !== 'ABBP05') {
550
+ return data
551
+ }
552
+
553
+ // 从 initLoad 返回的 result 块中,根据 bpProposalGoodsId 匹配数量
554
+ const cacheRows = initLoadEiInfo?.getBlock('result')?.getMappedRows?.() || []
555
+ const quantityMap = new Map<string, any>()
556
+ cacheRows.forEach((r: any) => {
557
+ if (r.bpProposalGoodsId) {
558
+ quantityMap.set(r.bpProposalGoodsId, r)
559
+ }
560
+ })
561
+
562
+ return data.map((row: any) => {
563
+ // 参照 toolbar 中 from ABBP02_dialog 回填的处理逻辑
564
+ const cacheRow = quantityMap.get(row.bpProposalGoodsId)
565
+ row.quantity = cacheRow && cacheRow.quantity != null ? cacheRow.quantity : ''
566
+ row.relProposalMainId = row.bpProposalMainId
567
+ row.relProposalGoodsId = row.bpProposalGoodsId
568
+ row.bpProposalMainId = bpProposalMainId || ''
569
+ // bpProposalGoodsId 在列表里通常就是当前商品关联的 goodsId,这里保留原值
570
+ row.relBillCode = row.bpProposalMainCode
571
+ row.relBillType = 'proposal'
572
+ row.sourceSupplierGroupCode = row.partyGroupCode
573
+ row.sourceSupplierName = row.partyName
574
+ return row
575
+ })
576
+ }
577
+
578
+ watch(() => formDataSet.proposalInfo, () => {
579
+ if (bpProposalMainId && !isProposalInfoInit.value) {
580
+ isProposalInfoInit.value = true
581
+ if (formDataSet.proposalInfo) {
582
+ proposalInfoFlag.value = false
583
+ setTimeout(() => {
584
+ proposalInfoFlag.value = true
585
+ }, 100)
586
+ }
587
+ }
588
+ })
589
+
590
+ watch(() => formDataBuy.settleInfo, () => {
591
+ if (bpProposalMainId && !isBuyerInit.value) {
592
+ isBuyerInit.value = true
593
+ if (formDataBuy.settleInfo) {
594
+ buySettleInfoFlag.value = false
595
+ setTimeout(() => {
596
+ buySettleInfoFlag.value = true
597
+ }, 100)
598
+ }
599
+ }
600
+ })
601
+
602
+ watch(() => formDataSale.settleInfo, () => {
603
+ if (bpProposalMainId && !isSaleInit.value) {
604
+ isSaleInit.value = true
605
+ if (formDataSale.settleInfo) {
606
+ saleSettleInfoFlag.value = false
607
+ setTimeout(() => {
608
+ saleSettleInfoFlag.value = true
609
+ }, 100)
610
+ }
611
+ }
612
+ })
613
+
614
+ watch(() => formDataEstimate.proposalProfitDesc, () => {
615
+ if (bpProposalMainId && !isProposalProfitDescInit.value) {
616
+ isProposalProfitDescInit.value = true
617
+ if (formDataEstimate.proposalProfitDesc) {
618
+ proposalProfitDescFlag.value = false
619
+ setTimeout(() => {
620
+ proposalProfitDescFlag.value = true
621
+ }, 100)
622
+ }
623
+ }
624
+ })
625
+
626
+ watch([
627
+ () => formConfigSet.data.proposalType,
628
+ () => goodsList.value.length,
629
+ ], ([newVal]) => {
630
+ buyFlag.value = newVal === 'PC' || newVal === 'PS'
631
+ saleFlag.value = newVal === 'SC' || newVal === 'PS'
632
+ onlySale.value = newVal === 'SC'
633
+ updateToolbarButtons()
634
+ if (newVal) {
635
+ setTimeout(() => {
636
+ const businessType = formConfigSet.data.businessType
637
+
638
+ // 定义列配置
639
+ const columnsToToggle = {
640
+ processingMine: ['partyName', 'pricePurchase', 'proposalTitle'],
641
+ sourceSupplier: ['sourceSupplierName'],
642
+ }
643
+
644
+ // 批量处理网格列显示/隐藏
645
+ saleGrids.value.forEach((item) => {
646
+ const gridInstance = item.getGridInstance()
647
+
648
+ // 加工矿业务类型处理
649
+ if (businessType === '40') {
650
+ gridInstance?.hideColumn(columnsToToggle.processingMine)
651
+ }
652
+ else {
653
+ gridInstance?.showColumn(columnsToToggle.processingMine)
654
+ }
655
+
656
+ // 供应商业务类型处理
657
+ if (businessType === '10') {
658
+ gridInstance?.showColumn(columnsToToggle.sourceSupplier)
659
+ }
660
+ else {
661
+ gridInstance?.hideColumn(columnsToToggle.sourceSupplier)
662
+ }
663
+ })
664
+ }, 1800)
665
+ }
666
+ })
667
+
668
+ watch(() => formConfigSet.data.businessType, () => {
669
+ updateToolbarButtons()
670
+ nextTick(() => {
671
+ if (formConfigSet.data.businessType === '30') {
672
+ goodsContainer.value.btnSetVisible(['ADD'], false)
673
+ }
674
+ })
675
+ })
676
+
677
+ /** 条件1:与 formConfig 最低/最高指导价展示条件一致(仅销售 + 期现/港口) */
678
+ function isGuidePriceCondition1() {
679
+ const { proposalType, businessType, saleType } = formConfigSet.data
680
+ return proposalType === 'SC' && businessType === '30' && (saleType === 'FUTURE_SP' || saleType === 'PORT_SP')
681
+ }
682
+
683
+ /** 条件2-销售维度:收款方式、币种、贸易术语(FUTURE_SP)、港口代码(PORT_SP) 均有值 */
684
+ function isGuidePriceCondition2Sale() {
685
+ const { saleType } = formConfigSet.data
686
+ const sale = formDataSale
687
+ if (!sale?.paymentType || !sale?.currencyCode)
688
+ return false
689
+ if (saleType === 'FUTURE_SP' && !sale?.tradeTermsCode)
690
+ return false
691
+ if (saleType === 'PORT_SP' && !sale?.portCode)
692
+ return false
693
+ return true
694
+ }
695
+
696
+ /** 条件2-单商品维度:商品ID(cgGoodsId)、priceStyleSale、交货方式、调价方式(FUTURE_SP) 均有值 */
697
+ function isGuidePriceCondition2Good(data: any) {
698
+ const { saleType } = formConfigSet.data
699
+ if (!data?.cgGoodsId || !data?.priceStyleSale)
700
+ return false
701
+ if (saleType === 'FUTURE_SP')
702
+ return !!data?.priceAdjustType
703
+ // PORT_SP 等需交货方式
704
+ return !!data?.deliveryType
705
+ }
706
+
707
+ /** 商品外部条件变化:对所有满足条件2的商品各请求一次 */
708
+ async function tryFetchGuidePriceAll() {
709
+ if (!isGuidePriceCondition1() || !isGuidePriceCondition2Sale())
710
+ return
711
+ const filtered = goodsList.value.filter(item => isGuidePriceCondition2Good(item.data))
712
+ if (filtered.length === 0)
713
+ return
714
+ await fetchPriceGoodsHisMaxMinForGoods(filtered)
715
+ }
716
+
717
+ /** 记录每个商品内部条件的签名,用于判断「哪个商品」发生变化 */
718
+ const prevGoodsSignatures = ref<string[]>([])
719
+
720
+ /** 商品内部条件变化:只对发生变化的商品请求一次(仅当前商品) */
721
+ async function tryFetchGuidePriceChangedGoodsOnly() {
722
+ if (!isGuidePriceCondition1() || !isGuidePriceCondition2Sale())
723
+ return
724
+ const list = goodsList.value
725
+ const currentSigs = list.map(g =>
726
+ [g.data?.cgGoodsId, g.data?.priceStyleSale, g.data?.deliveryType, g.data?.priceAdjustType].join(':'),
727
+ )
728
+ const prev = prevGoodsSignatures.value
729
+ const toFetch: { data: any }[] = []
730
+ if (prev.length !== currentSigs.length) {
731
+ toFetch.push(...list.filter(item => isGuidePriceCondition2Good(item.data)))
732
+ }
733
+ else {
734
+ for (let i = 0; i < list.length; i++) {
735
+ if (currentSigs[i] !== prev[i] && isGuidePriceCondition2Good(list[i].data))
736
+ toFetch.push(list[i])
737
+ }
738
+ }
739
+ prevGoodsSignatures.value = currentSigs
740
+ if (toFetch.length > 0)
741
+ await fetchPriceGoodsHisMaxMinForGoods(toFetch)
742
+ }
743
+
744
+ /** 监听商品外部条件:销售方式、收款方式、币种、贸易术语、港口代码 → 对所有商品请求 */
745
+ watch(
746
+ [
747
+ () => formConfigSet.data.saleType,
748
+ () => formDataSale.paymentType,
749
+ () => formDataSale.currencyCode,
750
+ () => formDataSale.tradeTermsCode,
751
+ () => formDataSale.portCode,
752
+ ],
753
+ async () => { await tryFetchGuidePriceAll() },
754
+ )
755
+
756
+ /** 监听商品内部条件:某商品的 cgGoodsId / priceStyleSale / deliveryType / priceAdjustType → 仅对该商品请求 */
757
+ watch(
758
+ () => goodsList.value.map(g =>
759
+ [g.data?.cgGoodsId, g.data?.priceStyleSale, g.data?.deliveryType, g.data?.priceAdjustType].join(':'),
760
+ ),
761
+ async () => { await tryFetchGuidePriceChangedGoodsOnly() },
762
+ { deep: true },
763
+ )
764
+
765
+ function gridEvents(item, idx) {
766
+ return {
767
+ toolbarButtonClick: async (obj) => {
768
+ const { code } = obj
769
+ // 加工矿并且销售
770
+ if (code === 'addSaleGoods' && formConfigSet.data.proposalType === 'SC' && formConfigSet.data.businessType === '40') {
771
+ openDialog(order_dialog_new, {
772
+ title: '加工订单',
773
+ componentParam: {
774
+ disableIds: ['produceEntrustStatus', 'orgId'],
775
+ param: {
776
+ produceEntrustStatus: '40',
777
+ orgId: formConfigSet.data.orgId,
778
+ instockGoodsName: item.data.goodsName,
779
+ instockGoodsCode: item.data.goodsCode,
780
+ },
781
+ },
782
+ }, (obj: any, exposed: any) => {
783
+ if (obj && Array.isArray(obj)) {
784
+ const $grid = saleGrids.value[idx]?.getGridInstance?.()
785
+ if ($grid) {
786
+ obj.forEach((row) => {
787
+ row.quantity = ''
788
+ row.relProposalMainId = row.meProduceEntrustMainId
789
+ row.relProposalGoodsId = row.instockCgGoodsId
790
+ row.bpProposalMainId = bpProposalMainId || ''
791
+ row.bpProposalGoodsId = item.data?.bpProposalGoodsId || ''
792
+ row.relBillCode = row.meProduceEntrustMainCode
793
+ row.relBillType = 'prorder'
794
+ $grid.insert(row).then(({ row: newRow }) => {
795
+ $grid.setEditRow(newRow)
796
+ })
797
+ })
798
+ }
799
+ }
800
+ exposed.close()
801
+ })
802
+ }
803
+ // 国内矿并且销售
804
+ else if (code === 'addConcat' && formConfigSet.data.proposalType === 'SC' && formConfigSet.data.businessType === '20') {
805
+ openDialog(purchase_resources, {
806
+ title: '采购合同',
807
+ height: 600,
808
+ componentParam: {
809
+ param: {
810
+ orgId: formConfigSale.data.signPartyCode,
811
+ goodsCode: item.data.goodsCode,
812
+ goodsName: item.data.goodsName,
813
+ },
814
+ },
815
+ }, (obj: any, exposed: any) => {
816
+ if (obj && Array.isArray(obj)) {
817
+ const $grid = saleGrids.value[idx]?.getGridInstance?.()
818
+ if ($grid) {
819
+ obj.forEach((row) => {
820
+ row.quantity = ''
821
+ row.partyName = row.supplierName
822
+ row.pricePurchase = row.priceTaxFlag === 'Y' ? row.priceWithtax : row.priceNotax
823
+ row.proposalTitle = row.contractName
824
+ row.relProposalMainId = row.pcContractMainId
825
+ row.relProposalGoodsId = row.pcContractGoodsId
826
+ row.bpProposalMainId = bpProposalMainId || ''
827
+ row.bpProposalGoodsId = item.data?.bpProposalGoodsId || ''
828
+ row.relBillCode = row.pcContractCode
829
+ row.relBillType = 'contract'
830
+ $grid.insert(row).then(({ row: newRow }) => {
831
+ $grid.setEditRow(newRow)
832
+ })
833
+ })
834
+ }
835
+ }
836
+ exposed.close()
837
+ })
838
+ }
839
+ else {
840
+ openDialog(ABBP02_dialog, {
841
+ title: '采购资源',
842
+ height: 600,
843
+ componentParam: {
844
+ param: {
845
+ orgId: formPartyBuy.data.orgId || formPartySale.data.orgId || '',
846
+ deptId: formPartyBuy.data.deptId || formPartySale.data.deptId || '',
847
+ goodsCode: item.data.goodsCode,
848
+ bookId: formConfigSet.data.bookId,
849
+ businessTypeMain: formConfigSet.data.businessType,
850
+ },
851
+ },
852
+ }, (obj: any, exposed: any) => {
853
+ if (obj && Array.isArray(obj)) {
854
+ const $grid = saleGrids.value[idx]?.getGridInstance?.()
855
+ if ($grid) {
856
+ obj.forEach((row) => {
857
+ row.quantity = ''
858
+ row.relProposalMainId = row.bpProposalMainId
859
+ row.relProposalGoodsId = row.bpProposalGoodsId
860
+ row.bpProposalMainId = bpProposalMainId || ''
861
+ row.bpProposalGoodsId = item.data?.bpProposalGoodsId || ''
862
+ row.relBillCode = row.bpProposalMainCode
863
+ row.relBillType = 'proposal'
864
+ row.sourceSupplierGroupCode = row.partyGroupCode
865
+ row.sourceSupplierName = row.partyName
866
+ row.goodsLeftQty = row.goodsLeftQty || 0
867
+ $grid.insert(row).then(({ row: newRow }) => {
868
+ console.log('newRow: ', newRow)
869
+ $grid.setEditRow(newRow)
870
+ })
871
+ })
872
+ }
873
+ }
874
+ exposed.close()
875
+ })
876
+ }
877
+ },
878
+ }
879
+ }
880
+
881
+ function handleUpload(item) {
882
+ const flag = ['10', '15'].includes(formConfigSet.data.proposalStatus) ? 'operate' : 'show'
883
+
884
+ openFileABBP('BP_PROPOSAL,TRADE_CHAIN', bpProposalMainId, item.bpProposalMainCode, '1.00', flag, (obj: any, exposed: any) => {
885
+ exposed.close()
886
+ }, formConfigSet.data.otTradeChainMainId || '')
887
+ }
888
+
889
+ function deleteSaleRow(row: any, idx: number) {
890
+ const $grid = saleGrids.value[idx]?.getGridInstance?.()
891
+ if (!$grid) {
892
+ console.error(`销售表格实例(索引${idx})不可用`)
893
+ return
894
+ }
895
+ $grid.remove(row)
896
+ }
897
+ function addGoods() {
898
+ const objData = {
899
+ data: {
900
+ priceStylePurchase: '20',
901
+ priceTaxFlagPurchase: 'Y',
902
+ priceStyleSale: '20',
903
+ priceTaxFlagSale: 'Y',
904
+ priceTaxRatePurchase: '0.130000',
905
+ priceTaxRateSale: '0.130000',
906
+ goodsContent: null,
907
+ tonPricePurchase: null,
908
+ tonPriceSale: null,
909
+ },
910
+ items: clone(formConfigGoods.items, true),
911
+ rules: clone(formConfigGoods.rules, true),
912
+ formRef: null,
913
+ params: { fieldVisibility: clone(fieldVisibility, true) },
914
+ }
915
+ goodsList.value.push(objData)
916
+ // 添加表单时留出引用位置
917
+ goodsForms.value.push(null)
918
+
919
+ // 滚动到新增的商品位置
920
+ nextTick(() => {
921
+ // 获取新增商品的索引(最后一个商品)
922
+ const newGoodsIndex = goodsList.value.length - 1
923
+ // 查找对应的商品容器
924
+ const goodsBoxes = document.querySelectorAll('.goods-box')
925
+ if (goodsBoxes[newGoodsIndex]) {
926
+ goodsBoxes[newGoodsIndex].scrollIntoView({
927
+ behavior: 'smooth',
928
+ block: 'start',
929
+ })
930
+ }
931
+ })
932
+ }
933
+ function delGoods(idx: number) {
934
+ if (goodsList.value.length <= 1) {
935
+ ElMessage.error('至少保留一个商品信息')
936
+ }
937
+ else {
938
+ goodsList.value.splice(idx, 1)
939
+ // 同时删除对应的表单引用
940
+ goodsForms.value.splice(idx, 1)
941
+ }
942
+ }
943
+
944
+ // 定义接口
945
+ interface GroupOption {
946
+ label: string
947
+ value: string
948
+ }
949
+
950
+ // 定义表格行数据结构
951
+ interface TableRowData {
952
+ value: string
953
+ feeName: string
954
+ feeCode: string
955
+ priceFee: string
956
+ priceTaxFlag: string
957
+ measureUnitName: string
958
+ measureUnitCode: string
959
+ currencyCode: string
960
+ exchangeRate: string
961
+ remark: string
962
+ [key: string]: any // 允许其他字段
963
+ }
964
+
965
+ // 定义ref变量
966
+ const valGroup1 = ref<string[]>([])
967
+ const dataMap = ref<Record<string, TableRowData>>({})
968
+ const prevSelected = ref<string[]>([])
969
+ const checkboxOptions = ref<GroupOption[]>([]) // 存储所有选项
970
+
971
+ // 修改后的 groupChange 方法,使用 feeCode 作为标识
972
+ function groupChange({ checklist }: { checklist: string[] }) {
973
+ const $grid = xGrid.value?.getGridInstance()
974
+ if (!$grid) {
975
+ console.error('表格实例不可用')
976
+ return
977
+ }
978
+
979
+ // 获取当前表格所有数据(实时)
980
+ const currentTableData = [...$grid.getTableData().tableData] as TableRowData[]
981
+
982
+ // 创建 feeCode 到行的映射(用于快速查找)
983
+ const feeCodeToRows = new Map<string, TableRowData[]>()
984
+
985
+ currentTableData.forEach((row) => {
986
+ const feeCode = row.feeCode
987
+ if (!feeCode)
988
+ return
989
+
990
+ if (!feeCodeToRows.has(feeCode)) {
991
+ feeCodeToRows.set(feeCode, [])
992
+ }
993
+ feeCodeToRows.get(feeCode)!.push(row)
994
+ })
995
+ // 找出需要删除的 feeCode(在之前选中但不在当前选中的值)
996
+ const codesToRemove = prevSelected.value.filter(v => !checklist.includes(v))
997
+ // 先处理删除项:找出需要删除的 feeCode 对应的行
998
+ const rowsToDelete: TableRowData[] = []
999
+ codesToRemove.forEach((feeCode) => {
1000
+ const rows = feeCodeToRows.get(feeCode)
1001
+ if (rows && rows.length > 0) {
1002
+ rows.forEach((row) => {
1003
+ rowsToDelete.push(row)
1004
+ })
1005
+ }
1006
+ })
1007
+
1008
+ // 删除这些行
1009
+ if (rowsToDelete.length > 0) {
1010
+ $grid.remove(rowsToDelete)
1011
+
1012
+ // 从 dataMap 中删除对应项
1013
+ rowsToDelete.forEach((row) => {
1014
+ if (row.feeCode && dataMap.value[row.feeCode]) {
1015
+ delete dataMap.value[row.feeCode]
1016
+ }
1017
+ })
1018
+ }
1019
+
1020
+ // 再处理新增项
1021
+ const addedCodes: string[] = []
1022
+ checklist.forEach((feeCode) => {
1023
+ if (!prevSelected.value.includes(feeCode)) {
1024
+ addedCodes.push(feeCode)
1025
+
1026
+ // 检查该 feeCode 是否已存在于表格中
1027
+ const existingRows = feeCodeToRows.get(feeCode) || []
1028
+
1029
+ if (existingRows.length === 0) {
1030
+ // 如果表格中不存在该 feeCode,则添加新行
1031
+ const rowData = dataMap.value[feeCode] || {
1032
+ feeCode,
1033
+ feeName: checkboxOptions.value.find(opt => opt.value === feeCode)?.label || '',
1034
+ priceFee: '0.00',
1035
+ measureUnitName: '元/每吨',
1036
+ measureUnitCode: '070',
1037
+ priceTaxFlag: 'N',
1038
+ currencyCode: formConfigSale.data.currencyCode || 'CNY',
1039
+ currencyName: formConfigSale.data.currencyName || '人民币',
1040
+ exchangeRate: '1.0',
1041
+ remark: '0.00',
1042
+ }
1043
+
1044
+ // 确保使用当前表单中的动态值
1045
+ rowData.currencyCode = formConfigSale.data.currencyCode || rowData.currencyCode
1046
+
1047
+ $grid.insert(rowData).then(({ row }: any) => {
1048
+ $grid.setEditRow(row)
1049
+ // 更新 dataMap
1050
+ dataMap.value[feeCode] = { ...row }
1051
+ console.log(`已添加新行: ${feeCode}`)
1052
+ })
1053
+ }
1054
+ else {
1055
+ console.log(`feeCode ${feeCode} 已存在于表格中,无需添加`)
1056
+ }
1057
+ }
1058
+ })
1059
+
1060
+ // 更新历史记录
1061
+ prevSelected.value = [...checklist]
1062
+ // 强制刷新表格(确保视图更新)
1063
+ $grid.refreshColumn()
1064
+ }
1065
+ function updateProposalProfit(data) {
1066
+ formConfigEstimate.data.proposalProfitManual = data.proposalProfit
1067
+ formConfigEstimate.data.profitManualCurrencyCode = data.profitCurrencyCode
1068
+ }
1069
+ // 修改 fetchGroupData 方法
1070
+ async function fetchGroupData() {
1071
+ try {
1072
+ const ei = new EiInfo()
1073
+ ei.set('codeset', 'brmerp.abbp.feeCode')
1074
+ const res = await EiCommunicator.callService('S_ED_02', ei)
1075
+ const response = res.getAttr()
1076
+ checkboxOptions.value = response?.list || []
1077
+ await nextTick()
1078
+ const $grid = xGrid.value?.getGridInstance()
1079
+
1080
+ // 初始化 dataMap
1081
+ dataMap.value = {}
1082
+
1083
+ if ($grid) {
1084
+ // 获取当前表格所有数据
1085
+ const tableData = [...$grid.getTableData().tableData] as TableRowData[]
1086
+
1087
+ // 1. 初始化 dataMap:将当前表格数据存入 dataMap
1088
+ tableData.forEach((row) => {
1089
+ if (row.feeCode) {
1090
+ dataMap.value[row.feeCode] = { ...row }
1091
+ }
1092
+ })
1093
+
1094
+ // 已选中的数据
1095
+ valGroup1.value = (initLoadEiInfo?.getAttr() as any).selectedFeeCodeList || []
1096
+ prevSelected.value = [...valGroup1.value]
1097
+ }
1098
+ else {
1099
+ // 没有表格实例时使用默认值
1100
+ valGroup1.value = [] // 默认值
1101
+ prevSelected.value = [...valGroup1.value]
1102
+ }
1103
+ }
1104
+ catch (error) {
1105
+ console.error('获取分组数据出错:', error)
1106
+ ElMessage.error('获取分组数据失败,请稍后重试')
1107
+ }
1108
+ }
1109
+ // 监听表单中单位、币种、汇率的变化,更新表格
1110
+ watch(() => [
1111
+ formConfigSale.data.currencyCode,
1112
+ ], () => {
1113
+ const $grid = xGrid.value?.getGridInstance()
1114
+ if (!$grid)
1115
+ return
1116
+
1117
+ // 更新 dataMap 中的单位、币种和汇率(为后续新增行使用)
1118
+ Object.keys(dataMap.value).forEach((key) => {
1119
+ const row = dataMap.value[key]
1120
+ row.currencyCode = formConfigSale.data.currencyCode || row.currencyCode
1121
+ })
1122
+
1123
+ // 更新当前表格中的单位、币种和汇率
1124
+ const updatedData = $grid.getTableData().tableData.map((row: TableRowData) => ({
1125
+ ...row,
1126
+ currencyCode: formConfigSale.data.currencyCode || row.currencyCode,
1127
+ }))
1128
+
1129
+ $grid.loadData(updatedData)
1130
+ }, { deep: true })
1131
+
1132
+ /**
1133
+ * 动态给表单赋值
1134
+ */
1135
+ onMounted(() => {
1136
+ nextTick(() => {
1137
+ fetchGroupData()
1138
+ // 有 bpProposalMainId(正常详情)或有 cacheKey(缓存场景)时,都需要执行赋值逻辑
1139
+ if (initLoadEiInfo && (bpProposalMainId || queryCacheKey)) {
1140
+ const inqu_statusData = initLoadEiInfo.getBlock('inqu_status')?.getMappedRows()
1141
+ const rowsInqu1 = initLoadEiInfo.getBlock('inqu1_status')?.getMappedRows() || []
1142
+ const goodsData = initLoadEiInfo.getBlock('inqu2_status')?.getMappedRows()
1143
+ const proposalType = formConfigSet.data.proposalType
1144
+ if (proposalType === 'SC') {
1145
+ Object.assign(formPartySale.data, rowsInqu1[0])
1146
+ }
1147
+ else if (proposalType === 'PC') {
1148
+ Object.assign(formPartyBuy.data, rowsInqu1[0])
1149
+ }
1150
+ else if (rowsInqu1.length > 0) {
1151
+ const isCustomerFirst = rowsInqu1[0].partyType === 'CUSTOMER'
1152
+ Object.assign(isCustomerFirst ? formPartySale.data : formPartyBuy.data, rowsInqu1[0])
1153
+ Object.assign(isCustomerFirst ? formPartyBuy.data : formPartySale.data, rowsInqu1[1])
1154
+ }
1155
+
1156
+ setTimeout(() => {
1157
+ if (goodsData && goodsData.length > 0) {
1158
+ Promise.all(goodsData.map((good) => {
1159
+ return new Promise((resolve) => {
1160
+ const ei = new EiInfo()
1161
+ ei.set('inqu_status-0-bpProposalMainId', bpProposalMainId || '')
1162
+ ei.set('inqu_status-0-bpProposalGoodsId', good.bpProposalGoodsId)
1163
+ EiCommunicator.send('ABBP02', 'queryStockValue', ei).then((res) => {
1164
+ resolve(res.getBlock('result')?.getMappedRows()[0])
1165
+ })
1166
+ })
1167
+ })).then(async (stockValueDatas: any[]) => {
1168
+ goodsList.value = goodsData.map((el, idx) => ({
1169
+ inventoryValue: undefined,
1170
+ ...stockValueDatas[idx],
1171
+ ...el,
1172
+ })).map(item => ({
1173
+ data: item,
1174
+ items: clone(formConfigGoods.items, true),
1175
+ rules: clone(formConfigGoods.rules, true),
1176
+ formRef: null,
1177
+ params: { fieldVisibility: clone(fieldVisibility, true) },
1178
+ }))
1179
+
1180
+ // 初始化表单引用数组
1181
+ goodsForms.value = Array(goodsList.value.length).fill(null)
1182
+ // 参考 queryGoodsRel:初始化时按每个商品用该商品的 cgGoodsId 请求一次 queryPriceGoodsHisMaxMin
1183
+ if (isGuidePriceCondition1()) {
1184
+ await fetchPriceGoodsHisMaxMinForGoods(goodsList.value)
1185
+ }
1186
+ nextTick(() => {
1187
+ updateToolbarButtons()
1188
+ })
1189
+ })
1190
+ }
1191
+ }, 500)
1192
+
1193
+ // 给预估信息赋值
1194
+ const { proposalProfit, proposalProfitManual, proposalProfitDesc, profitManualCurrencyCode, profitCurrencyCode } = inqu_statusData[0]
1195
+ Object.assign(formConfigEstimate.data, {
1196
+ proposalProfit,
1197
+ proposalProfitManual,
1198
+ proposalProfitDesc,
1199
+ profitManualCurrencyCode,
1200
+ profitCurrencyCode,
1201
+ })
1202
+ }
1203
+ else {
1204
+ goodsList.value = [formConfigGoods]
1205
+ }
1206
+ // 若 URL 带有 cacheKey,则无论接口返回什么状态,页面内状态统一视为 '10'
1207
+ if (queryCacheKey) {
1208
+ formConfigSet.data.proposalStatus = '10'
1209
+ formConfigSet.data.businessType = '10'
1210
+ formConfigSet.data.proposalType = 'SC'
1211
+ }
1212
+ isDetail.value = ['40', '00'].includes(formConfigSet.data.proposalStatus)
1213
+ if (isDetail.value) {
1214
+ detailContainer?.value?.btnSetVisible(['SAVE', 'SUBMIT', 'TEMP'], false)
1215
+ goodsContainer?.value?.btnSetVisible(['ADD'], false)
1216
+ }
1217
+ })
1218
+ })
1219
+
1220
+ async function save(flag) {
1221
+ // 定义需要校验的表单列表
1222
+ // flag == true 表示保存 需要校验
1223
+ if (flag) {
1224
+ const formTasks = [
1225
+ { form: base, label: '基础信息', required: true },
1226
+ { form: buyParty, label: '采购签约主体', required: buyFlag.value },
1227
+ { form: saleParty, label: '销售签约主体', required: saleFlag.value },
1228
+ { form: buy, label: '采购信息', required: buyFlag.value },
1229
+ { form: sale, label: '销售信息', required: saleFlag.value },
1230
+ { form: estimate, label: '利润预估', required: saleFlag.value },
1231
+ ]
1232
+
1233
+ // 校验普通表单
1234
+ for (const task of formTasks) {
1235
+ if (task.required) {
1236
+ const valid = await task.form.value?.getFormInstance()?.validate()
1237
+ if (valid) {
1238
+ ElMessage.error(`${task.label}表单校验失败,请检查必填项`)
1239
+ return
1240
+ }
1241
+ }
1242
+ }
1243
+
1244
+ // 校验商品表单
1245
+ if (goodsForms.value.length > 0) {
1246
+ const validResults = await Promise.all(
1247
+ goodsForms.value.map(form =>
1248
+ form?.getFormInstance()?.validate().catch((error) => {
1249
+ console.error('商品表单校验失败:', error)
1250
+ return false
1251
+ }),
1252
+ ),
1253
+ )
1254
+
1255
+ const invalidIndex = validResults.findIndex(valid => valid)
1256
+ if (invalidIndex >= 0) {
1257
+ ElMessage.error(`商品信息第${invalidIndex + 1}行填写有误,请检查`)
1258
+ return
1259
+ }
1260
+ }
1261
+ }
1262
+ // 定义需要提取的商品字段
1263
+ const GOODS_FIELDS = [
1264
+ 'costFormulaName',
1265
+ 'costFormulaId',
1266
+ 'costOverflowSymbol',
1267
+ 'costOverflowPrice',
1268
+ 'costFormulaUnit',
1269
+ 'saleOverflowSymbol',
1270
+ 'saleOverflowPrice',
1271
+ 'marketCategoryName',
1272
+ 'marketOverflowSymbol',
1273
+ 'marketOverflowPrice',
1274
+ 'marketPriceUnit',
1275
+ 'goodsContent',
1276
+ 'categoryCode',
1277
+ 'categoryName',
1278
+ 'cgGoodsCode',
1279
+ 'bpProposalStockValueId',
1280
+ 'bpProposalGoodsId',
1281
+ 'bpProposalMainId',
1282
+ 'zpMarketCategoryId',
1283
+ 'inventoryValue',
1284
+ ] as const
1285
+
1286
+ // 提取商品字段的工具函数
1287
+ const extractGoodsFields = (formData: any) => {
1288
+ return GOODS_FIELDS.reduce((acc, field) => {
1289
+ acc[field] = formData[field]
1290
+ return acc
1291
+ }, {} as Record<string, any>)
1292
+ }
1293
+
1294
+ // 获取商品信息数据(包含每个商品对应的销售表格数据)
1295
+ const goodsData = [] as any
1296
+ for (let i = 0; i < goodsForms.value.length; i++) {
1297
+ const form = goodsForms.value[i]
1298
+ // 获取商品表单数据
1299
+ const goodFormData = await form.getFormInstance().data
1300
+ // 若 URL 带有 cacheKey,则暂存/保存时将商品本身的 bpProposalGoodsId 置空,避免沿用缓存中的商品主键
1301
+ if (queryCacheKey) {
1302
+ goodFormData.bpProposalGoodsId = ''
1303
+ }
1304
+
1305
+ const extractedFields = extractGoodsFields(goodFormData)
1306
+ if (formConfigSet.data.proposalType === 'SC') {
1307
+ // 获取销售表格数据
1308
+ let saleTableData = []
1309
+
1310
+ // 每个商品索引对应一个销售表格索引
1311
+ const saleGrid = saleGrids.value[i]
1312
+ if (saleGrid) {
1313
+ const gridInstance = saleGrid.getGridInstance?.()
1314
+ if (gridInstance) {
1315
+ const tableData = gridInstance.getTableData()
1316
+ saleTableData = tableData?.tableData || []
1317
+ }
1318
+ }
1319
+ // 将销售表格数据放入商品的relList字段
1320
+ goodsData.push({
1321
+ ...goodFormData,
1322
+ relList: saleTableData, // 添加销售表格数据到relList字段
1323
+ })
1324
+ }
1325
+ else {
1326
+ goodsData.push({
1327
+ ...goodFormData,
1328
+ stockValueSaveReqs: [extractedFields],
1329
+ })
1330
+ }
1331
+ }
1332
+
1333
+ // SC+40:每个商品的 relList 中至少需有一条加工订单(relBillType=prorder)
1334
+ if (flag && formConfigSet.data.proposalType === 'SC' && formConfigSet.data.businessType === '40') {
1335
+ const invalidGoodIndex = goodsData.findIndex((good: any) => {
1336
+ const relList = good.relList || []
1337
+ return !relList.some((row: any) => row.relBillType === 'prorder')
1338
+ })
1339
+ if (invalidGoodIndex >= 0) {
1340
+ ElMessage.error(`商品信息第${invalidGoodIndex + 1}行:加工矿销售场景下,每个商品至少需添加一条加工订单关联数据`)
1341
+ return
1342
+ }
1343
+ }
1344
+
1345
+ // 条件判断获取数据
1346
+ const inqu1Map = {
1347
+ PS: [formPartyBuy.data, formPartySale.data],
1348
+ PC: [formPartyBuy.data],
1349
+ SC: [formPartySale.data],
1350
+ }
1351
+ const inqu1 = inqu1Map[formConfigSet.data.proposalType] || []
1352
+
1353
+ // 获取利润预估表格数据
1354
+ const $grid = xGrid.value?.getGridInstance()
1355
+ const allTableData = $grid ? $grid.getTableData().tableData : []
1356
+
1357
+ const eiInfo = new EiInfo()
1358
+ const proposalObj = {
1359
+ proposal: {
1360
+ ...formConfigSet.data,
1361
+ ...formConfigEstimate.data,
1362
+ bpProposalMainId: bpProposalMainId || '',
1363
+ },
1364
+ partyList: inqu1,
1365
+ goodsList: goodsData, // 使用修改后的goodsData(包含relList)
1366
+ feeList: allTableData,
1367
+ } as any
1368
+
1369
+ if (formConfigSet.data.proposalType === 'PC') {
1370
+ proposalObj.purchase = {
1371
+ ...formConfigBuy.data,
1372
+ mineShipNum: formConfigSet.data.mineShipNum,
1373
+ }
1374
+ }
1375
+ else if (formConfigSet.data.proposalType === 'SC') {
1376
+ proposalObj.sale = formConfigSale.data
1377
+ proposalObj.sale.quotationPeriodDate = formConfigSale.data.quotationPeriodDateSale || ''
1378
+ }
1379
+ else {
1380
+ proposalObj.purchase = formConfigBuy.data
1381
+ proposalObj.sale = formConfigSale.data
1382
+ proposalObj.sale.quotationPeriodDate = formConfigSale.data.quotationPeriodDateSale || ''
1383
+ }
1384
+ // 把proposalObj里为空的值('')替换成null
1385
+ const replaceEmptyStringsWithNull = (obj: any): any => {
1386
+ if (obj === null || obj === undefined) {
1387
+ return obj
1388
+ }
1389
+
1390
+ if (typeof obj === 'string') {
1391
+ return obj === '' ? null : obj
1392
+ }
1393
+
1394
+ if (Array.isArray(obj)) {
1395
+ return obj.map(item => replaceEmptyStringsWithNull(item))
1396
+ }
1397
+
1398
+ if (typeof obj === 'object') {
1399
+ const result: any = {}
1400
+ for (const key in obj) {
1401
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
1402
+ result[key] = replaceEmptyStringsWithNull(obj[key])
1403
+ }
1404
+ }
1405
+ return result
1406
+ }
1407
+
1408
+ return obj
1409
+ }
1410
+
1411
+ // 应用转换
1412
+ const processedProposalObj = replaceEmptyStringsWithNull(proposalObj)
1413
+
1414
+ eiInfo.set('proposal', JSON.stringify(processedProposalObj))
1415
+ eiInfo.set('useServiceId', 'S_AB_BP_04_01')
1416
+
1417
+ useLoading()
1418
+
1419
+ try {
1420
+ const serveName = flag ? 'saveOrUpdate' : 'tempSaveInfo'
1421
+ const res: EiInfo = await EiCommunicator.send('ABBP04', serveName, eiInfo)
1422
+
1423
+ ElMessage.success('保存成功')
1424
+ const proposalStatus = JSON.parse((res.getAttr() as any).proposal).proposalStatus
1425
+ await router.replace({
1426
+ path: '/web/ABBP0201',
1427
+ query: {
1428
+ serviceName: 'ABBP02',
1429
+ methodName: 'detail',
1430
+ bpProposalMainId: (res.getAttr() as any).primaryId,
1431
+ status: ['12', '20', '40', '60', '00'].includes(proposalStatus) ? 'true' : 'false',
1432
+ },
1433
+ })
1434
+
1435
+ // 刷新页面
1436
+ refreshPage()
1437
+ }
1438
+ catch (error: any) {
1439
+ console.error('保存失败:', error)
1440
+ ElMessage.error(`保存失败,原因:${error.message || error}`)
1441
+ }
1442
+ finally {
1443
+ useLoading().close()
1444
+ }
1445
+ }
1446
+
1447
+ // 在模板中收集商品表单引用
1448
+ function setGoodsFormRef(el: any, idx: number) {
1449
+ if (el) {
1450
+ goodsForms.value[idx] = el
1451
+ }
1452
+ }
1453
+
1454
+ // 递交
1455
+ function handleSubmit() {
1456
+ ElMessageBox.confirm(`确定要递交吗?`).then(async () => {
1457
+ const ei = new EiInfo()
1458
+ ei.addBlock(EiBlock.build('result', [{
1459
+ bpProposalMainId,
1460
+ }]))
1461
+
1462
+ const [err, res] = await to(EiCommunicator.send('ABBP02', 'checkProposalSubmitApproved', ei))
1463
+ if (err)
1464
+ return false
1465
+
1466
+ const attr = res.getAttr() as any
1467
+ useLoading()
1468
+ const [err1] = await to(startApproveProcess({
1469
+ originId: attr.wfBizId,
1470
+ originCode: attr.wfBizCode,
1471
+ approveTypeCode: attr.approveTypeCode,
1472
+ organizationId: attr.organizationId,
1473
+ docType: attr.docType,
1474
+ variables: attr.variables,
1475
+ callback: () => {},
1476
+ callbackServiceName: attr.callbackServiceName,
1477
+ callbackMethodName: attr.callbackMethodName,
1478
+ callbackParam: attr.callbackParam,
1479
+ taskExtraDisplayContent: attr.taskExtraDisplayContent,
1480
+ billDisplayData: attr.billDisplayData,
1481
+ nextActivityUserList: [],
1482
+ runtimeCheckFlag: undefined,
1483
+ }))
1484
+ if (err1) {
1485
+ return false
1486
+ }
1487
+ const [err2, isDirectPass] = await to(judgeIsDirectPass(attr.approveTypeCode, attr.organizationId, attr.docType, attr.runtimeCheckFlag))
1488
+ if (err2) {
1489
+ return false
1490
+ }
1491
+ if (!isDirectPass) {
1492
+ ElMessage.success(`操作成功`)
1493
+ // 刷新页面
1494
+ router.replace({
1495
+ path: '/web/ABBP0201',
1496
+ query: {
1497
+ serviceName: 'ABBP02',
1498
+ methodName: 'detail',
1499
+ bpProposalMainId,
1500
+ status: 'true', // 递交后是待审批12状态 不可编辑
1501
+ },
1502
+ })
1503
+ }
1504
+ })
1505
+ }
1506
+
1507
+ /**
1508
+ * 表单按钮
1509
+ * @param btn
1510
+ */
1511
+ function btnClick(btn: any) {
1512
+ switch (btn.code) {
1513
+ case 'TEMP':
1514
+ save(false)
1515
+ break
1516
+ case 'SAVE':
1517
+ save(true)
1518
+ break
1519
+ case 'SUBMIT':
1520
+ if (bpProposalMainId) {
1521
+ if (formConfigSet.data.proposalStatus !== '10' && formConfigSet.data.proposalStatus !== '15') {
1522
+ ElMessage.error(`只有【初始】【审批退回】状态下的合同才可以递交`)
1523
+ }
1524
+ else {
1525
+ handleSubmit()
1526
+ }
1527
+ }
1528
+ else {
1529
+ ElMessage.error('请先保存再递交!')
1530
+ }
1531
+ break
1532
+ default:
1533
+ break
1534
+ }
1535
+ }
1536
+ /**
1537
+ * 商品按钮
1538
+ * @param btn
1539
+ */
1540
+ function goodsClick(btn: any) {
1541
+ switch (btn.code) {
1542
+ case 'ADD':
1543
+ addGoods()
1544
+ break
1545
+ default:
1546
+ break
1547
+ }
1548
+ }
1549
+
1550
+ // 计算销售单价
1551
+ function calculatePriceSale(data: any) {
1552
+ if (['SC'].includes(formConfigSet.data.proposalType) && ['30', '40'].includes(formConfigSet.data.businessType)) {
1553
+ const goodsPrice = Number.parseFloat(data.goodsPrice) || 0
1554
+ const freightPrice = Number.parseFloat(data.freightPrice) || 0
1555
+ data.priceSale = (goodsPrice + freightPrice).toFixed(6)
1556
+ }
1557
+ }
1558
+ </script>
1559
+
1560
+ <style scoped>
1561
+ .goods-index-wrapper {
1562
+ position: absolute;
1563
+ top: 2px;
1564
+ left: 2px;
1565
+ z-index: 10;
1566
+ }
1567
+ .goods-box-index {
1568
+ position: relative;
1569
+ display: flex;
1570
+ align-items: center;
1571
+ justify-content: center;
1572
+ width: 0;
1573
+ height: 0;
1574
+ font-size: 14px;
1575
+ font-weight: 500;
1576
+ color: #606266;
1577
+ border-top: 20px solid #f3f3f3;
1578
+ border-right: 20px solid transparent;
1579
+ border-bottom: 20px solid transparent;
1580
+ border-left: 20px solid #f3f3f3;
1581
+ }
1582
+ .goods-box-index::after {
1583
+ position: absolute;
1584
+ top: -18px;
1585
+ left: -12px;
1586
+ font-size: 14px;
1587
+ font-weight: 500;
1588
+ color: #606266;
1589
+ content: attr(data-index);
1590
+ }
1591
+ .formBox {
1592
+ padding: 10px 20px;
1593
+ margin-bottom: 20px;
1594
+ background-color: #ffffff;
1595
+ border: 1px solid #cccccc;
1596
+ border-radius: 10px;
1597
+ }
1598
+ .dashed-box {
1599
+ display: table; /* 表格布局 */
1600
+ width: 100%;
1601
+ border-collapse: collapse; /* 合并边框 */
1602
+ }
1603
+ .dashed-box p {
1604
+ display: table-cell;
1605
+ width: 30px;
1606
+ text-align: center;
1607
+ letter-spacing: 5px; /* 增加字间距提升可读性 */
1608
+ vertical-align: middle; /* 垂直居中 */
1609
+ background: #dbefff;
1610
+ writing-mode: vertical-lr; /* 垂直排列文字 */
1611
+ }
1612
+ .form-container {
1613
+ display: table-cell;
1614
+ vertical-align: top; /* 顶部对齐 */
1615
+ }
1616
+ :deep(.el-divider--horizontal) {
1617
+ margin: 5px 0;
1618
+ border-top: 2px dashed #dbefff;
1619
+ }
1620
+ .bx-container-ABBP {
1621
+ display: unset;
1622
+ flex: 1;
1623
+ flex-direction: column;
1624
+ padding: 10px;
1625
+ overflow: auto;
1626
+ }
1627
+ .goods-box {
1628
+ position: relative;
1629
+ padding-bottom: 40px;
1630
+ margin-bottom: 10px;
1631
+ border: 4px dashed #dbefff;
1632
+ border-radius: 10px;
1633
+ }
1634
+ .delBtn {
1635
+ position: absolute;
1636
+ right: 10px;
1637
+ bottom: 5px;
1638
+ }
1639
+ .border-rd-lg {
1640
+ border-radius: 0 !important;
1641
+ }
1642
+ .x-is-required {
1643
+ font-family: Verdana, Arial, Tahoma;
1644
+ font-weight: 400;
1645
+ color: var(--vxe-ui-form-validate-error-color);
1646
+ vertical-align: middle;
1647
+ }
1648
+ </style>