Commit 81aab8ac9056a097b3050fbb600706d59d1b55dd

Authored by “wangming”
1 parent 2ef28d30

123

Showing 30 changed files with 5149 additions and 1466 deletions
Antis.Erp.Plat/antis-ncc-admin/src/utils/wtRejectApproval.js
@@ -67,3 +67,39 @@ export function postApprovePurchaseInbound(id, remark) { @@ -67,3 +67,39 @@ export function postApprovePurchaseInbound(id, remark) {
67 data: { remark: remark || '' } 67 data: { remark: remark || '' }
68 }) 68 })
69 } 69 }
  70 +
  71 +/** 应收款增加减少(wt_yskzjjs):提交审核 */
  72 +export function postYskzjjsSubmitForAudit(id) {
  73 + return request({
  74 + url: `/api/Extend/WtYskzjjs/Actions/SubmitForAudit/${id}`,
  75 + method: 'POST',
  76 + data: {}
  77 + })
  78 +}
  79 +
  80 +/** 应收款增加减少:撤回审核(回草稿) */
  81 +export function postYskzjjsWithdrawAudit(id) {
  82 + return request({
  83 + url: `/api/Extend/WtYskzjjs/Actions/WithdrawAudit/${id}`,
  84 + method: 'POST',
  85 + data: {}
  86 + })
  87 +}
  88 +
  89 +/** 应收款增加减少:审核通过 */
  90 +export function postYskzjjsApprove(id, remark) {
  91 + return request({
  92 + url: `/api/Extend/WtYskzjjs/Actions/Approve/${id}`,
  93 + method: 'POST',
  94 + data: { remark: remark || '' }
  95 + })
  96 +}
  97 +
  98 +/** 应收款增加减少:审核不通过 */
  99 +export function postYskzjjsReject(id, remark) {
  100 + return request({
  101 + url: `/api/Extend/WtYskzjjs/Actions/Reject/${id}`,
  102 + method: 'POST',
  103 + data: { remark: remark || '' }
  104 + })
  105 +}
Antis.Erp.Plat/antis-ncc-admin/src/views/basic/todoCenter/index.vue
@@ -4,34 +4,72 @@ @@ -4,34 +4,72 @@
4 <div class="NCC-common-head"> 4 <div class="NCC-common-head">
5 <div> 5 <div>
6 <span class="page-title"> 6 <span class="page-title">
7 - <i class="icon-ym icon-ym-generator-todo" style="color:#409EFF;margin-right:8px" /> 7 + <i
  8 + class="icon-ym icon-ym-generator-todo"
  9 + style="color:#409EFF;margin-right:8px"
  10 + />
8 待办中心 11 待办中心
9 </span> 12 </span>
10 - <span class="page-desc">同价调拨单、销售/预售出库、销售/预售退货、采购入库单、委托代销发/退/结算、商品调价单等待办(与首页待办数据源一致)</span> 13 + <span class="page-desc"
  14 + >同价调拨单、销售/预售出库、销售/预售退货、采购入库单、委托代销发/退/结算、商品调价单等待办(与首页待办数据源一致)</span
  15 + >
11 </div> 16 </div>
12 <div class="NCC-common-head-right"> 17 <div class="NCC-common-head-right">
13 <el-tooltip effect="dark" content="刷新" placement="top"> 18 <el-tooltip effect="dark" content="刷新" placement="top">
14 - <el-link icon="icon-ym icon-ym-Refresh NCC-common-head-icon" :underline="false" @click="loadData" /> 19 + <el-link
  20 + icon="icon-ym icon-ym-Refresh NCC-common-head-icon"
  21 + :underline="false"
  22 + @click="loadData"
  23 + />
15 </el-tooltip> 24 </el-tooltip>
16 </div> 25 </div>
17 </div> 26 </div>
18 <div class="NCC-common-layout-main NCC-flex-main"> 27 <div class="NCC-common-layout-main NCC-flex-main">
19 - <el-table v-loading="loading" :data="list" border stripe empty-text="暂无待办">  
20 - <el-table-column type="index" width="50" label="#" align="center" :index="tableIndexMethod" />  
21 - <el-table-column prop="fullName" label="待办事项" min-width="280" show-overflow-tooltip> 28 + <el-table
  29 + v-loading="loading"
  30 + :data="list"
  31 + border
  32 + stripe
  33 + empty-text="暂无待办"
  34 + >
  35 + <el-table-column
  36 + type="index"
  37 + width="50"
  38 + label="#"
  39 + align="center"
  40 + :index="tableIndexMethod"
  41 + />
  42 + <el-table-column
  43 + prop="fullName"
  44 + label="待办事项"
  45 + min-width="280"
  46 + show-overflow-tooltip
  47 + >
22 <template slot-scope="scope"> 48 <template slot-scope="scope">
23 <span class="cell-nowrap"> 49 <span class="cell-nowrap">
24 - <i class="el-icon-document" style="color:#409EFF;margin-right:6px" /> 50 + <i
  51 + class="el-icon-document"
  52 + style="color:#409EFF;margin-right:6px"
  53 + />
25 {{ cellText(scope.row.fullName) }} 54 {{ cellText(scope.row.fullName) }}
26 </span> 55 </span>
27 </template> 56 </template>
28 </el-table-column> 57 </el-table-column>
29 - <el-table-column prop="creatorTime" label="日期" width="130" align="center">  
30 - <template slot-scope="scope">{{ formatTime(scope.row.creatorTime) }}</template> 58 + <el-table-column
  59 + prop="creatorTime"
  60 + label="日期"
  61 + width="130"
  62 + align="center"
  63 + >
  64 + <template slot-scope="scope">{{
  65 + formatTime(scope.row.creatorTime)
  66 + }}</template>
31 </el-table-column> 67 </el-table-column>
32 <el-table-column label="操作" width="200" align="left" fixed="right"> 68 <el-table-column label="操作" width="200" align="left" fixed="right">
33 <template slot-scope="scope"> 69 <template slot-scope="scope">
34 - <el-button type="text" @click="openProcess(scope.row)">处理</el-button> 70 + <el-button type="text" @click="openProcess(scope.row)"
  71 + >处理</el-button
  72 + >
35 </template> 73 </template>
36 </el-table-column> 74 </el-table-column>
37 </el-table> 75 </el-table>
@@ -53,9 +91,25 @@ @@ -53,9 +91,25 @@
53 > 91 >
54 <template slot="footer"> 92 <template slot="footer">
55 <el-button @click="closeProcessDialog">关闭</el-button> 93 <el-button @click="closeProcessDialog">关闭</el-button>
56 - <el-button v-if="canLevel1Approve" type="warning" @click="handleLevel1Approve">一级审核</el-button>  
57 - <el-button v-if="canLevel2Approve" type="success" @click="handleLevel2Approve">二级审核</el-button>  
58 - <el-button v-if="canAuditReject" type="danger" plain @click="handleReject">审核不通过</el-button> 94 + <el-button
  95 + v-if="canLevel1Approve"
  96 + type="warning"
  97 + @click="handleLevel1Approve"
  98 + >一级审核</el-button
  99 + >
  100 + <el-button
  101 + v-if="canLevel2Approve"
  102 + type="success"
  103 + @click="handleLevel2Approve"
  104 + >二级审核</el-button
  105 + >
  106 + <el-button
  107 + v-if="canAuditReject"
  108 + type="danger"
  109 + plain
  110 + @click="handleReject"
  111 + >审核不通过</el-button
  112 + >
59 </template> 113 </template>
60 </WtTjdbdDetailView> 114 </WtTjdbdDetailView>
61 115
@@ -67,9 +121,25 @@ @@ -67,9 +121,25 @@
67 > 121 >
68 <template slot="footer"> 122 <template slot="footer">
69 <el-button @click="closeProcessDialog">关闭</el-button> 123 <el-button @click="closeProcessDialog">关闭</el-button>
70 - <el-button v-if="canLevel1Approve" type="warning" @click="handleLevel1Approve">一级审核</el-button>  
71 - <el-button v-if="canLevel2Approve" type="success" @click="handleLevel2Approve">二级审核</el-button>  
72 - <el-button v-if="canAuditReject" type="danger" plain @click="handleReject">审核不通过</el-button> 124 + <el-button
  125 + v-if="canLevel1Approve"
  126 + type="warning"
  127 + @click="handleLevel1Approve"
  128 + >一级审核</el-button
  129 + >
  130 + <el-button
  131 + v-if="canLevel2Approve"
  132 + type="success"
  133 + @click="handleLevel2Approve"
  134 + >二级审核</el-button
  135 + >
  136 + <el-button
  137 + v-if="canAuditReject"
  138 + type="danger"
  139 + plain
  140 + @click="handleReject"
  141 + >审核不通过</el-button
  142 + >
73 </template> 143 </template>
74 </WtXsckdDetailView> 144 </WtXsckdDetailView>
75 145
@@ -81,9 +151,25 @@ @@ -81,9 +151,25 @@
81 > 151 >
82 <template slot="footer"> 152 <template slot="footer">
83 <el-button @click="closeProcessDialog">关闭</el-button> 153 <el-button @click="closeProcessDialog">关闭</el-button>
84 - <el-button v-if="canLevel1Approve" type="warning" @click="handleLevel1Approve">一级审核</el-button>  
85 - <el-button v-if="canLevel2Approve" type="success" @click="handleLevel2Approve">二级审核</el-button>  
86 - <el-button v-if="canAuditReject" type="danger" plain @click="handleReject">审核不通过</el-button> 154 + <el-button
  155 + v-if="canLevel1Approve"
  156 + type="warning"
  157 + @click="handleLevel1Approve"
  158 + >一级审核</el-button
  159 + >
  160 + <el-button
  161 + v-if="canLevel2Approve"
  162 + type="success"
  163 + @click="handleLevel2Approve"
  164 + >二级审核</el-button
  165 + >
  166 + <el-button
  167 + v-if="canAuditReject"
  168 + type="danger"
  169 + plain
  170 + @click="handleReject"
  171 + >审核不通过</el-button
  172 + >
87 </template> 173 </template>
88 </WtXsthdDetailView> 174 </WtXsthdDetailView>
89 175
@@ -95,9 +181,25 @@ @@ -95,9 +181,25 @@
95 > 181 >
96 <template slot="footer"> 182 <template slot="footer">
97 <el-button @click="closeProcessDialog">关闭</el-button> 183 <el-button @click="closeProcessDialog">关闭</el-button>
98 - <el-button v-if="canLevel1Approve" type="warning" @click="handleLevel1Approve">一级审核</el-button>  
99 - <el-button v-if="canLevel2Approve" type="success" @click="handleLevel2Approve">二级审核</el-button>  
100 - <el-button v-if="canAuditReject" type="danger" plain @click="handleReject">审核不通过</el-button> 184 + <el-button
  185 + v-if="canLevel1Approve"
  186 + type="warning"
  187 + @click="handleLevel1Approve"
  188 + >一级审核</el-button
  189 + >
  190 + <el-button
  191 + v-if="canLevel2Approve"
  192 + type="success"
  193 + @click="handleLevel2Approve"
  194 + >二级审核</el-button
  195 + >
  196 + <el-button
  197 + v-if="canAuditReject"
  198 + type="danger"
  199 + plain
  200 + @click="handleReject"
  201 + >审核不通过</el-button
  202 + >
101 </template> 203 </template>
102 </WtYsckdDetailView> 204 </WtYsckdDetailView>
103 205
@@ -109,9 +211,25 @@ @@ -109,9 +211,25 @@
109 > 211 >
110 <template slot="footer"> 212 <template slot="footer">
111 <el-button @click="closeProcessDialog">关闭</el-button> 213 <el-button @click="closeProcessDialog">关闭</el-button>
112 - <el-button v-if="canLevel1Approve" type="warning" @click="handleLevel1Approve">一级审核</el-button>  
113 - <el-button v-if="canLevel2Approve" type="success" @click="handleLevel2Approve">二级审核</el-button>  
114 - <el-button v-if="canAuditReject" type="danger" plain @click="handleReject">审核不通过</el-button> 214 + <el-button
  215 + v-if="canLevel1Approve"
  216 + type="warning"
  217 + @click="handleLevel1Approve"
  218 + >一级审核</el-button
  219 + >
  220 + <el-button
  221 + v-if="canLevel2Approve"
  222 + type="success"
  223 + @click="handleLevel2Approve"
  224 + >二级审核</el-button
  225 + >
  226 + <el-button
  227 + v-if="canAuditReject"
  228 + type="danger"
  229 + plain
  230 + @click="handleReject"
  231 + >审核不通过</el-button
  232 + >
115 </template> 233 </template>
116 </WtYsthdDetailView> 234 </WtYsthdDetailView>
117 235
@@ -123,9 +241,25 @@ @@ -123,9 +241,25 @@
123 > 241 >
124 <template slot="footer"> 242 <template slot="footer">
125 <el-button @click="closeProcessDialog">关闭</el-button> 243 <el-button @click="closeProcessDialog">关闭</el-button>
126 - <el-button v-if="canLevel1Approve" type="warning" @click="handleLevel1Approve">一级审核</el-button>  
127 - <el-button v-if="canLevel2Approve" type="success" @click="handleLevel2Approve">二级审核</el-button>  
128 - <el-button v-if="canAuditReject" type="danger" plain @click="handleReject">审核不通过</el-button> 244 + <el-button
  245 + v-if="canLevel1Approve"
  246 + type="warning"
  247 + @click="handleLevel1Approve"
  248 + >一级审核</el-button
  249 + >
  250 + <el-button
  251 + v-if="canLevel2Approve"
  252 + type="success"
  253 + @click="handleLevel2Approve"
  254 + >二级审核</el-button
  255 + >
  256 + <el-button
  257 + v-if="canAuditReject"
  258 + type="danger"
  259 + plain
  260 + @click="handleReject"
  261 + >审核不通过</el-button
  262 + >
129 </template> 263 </template>
130 </WtCgrkdDetailView> 264 </WtCgrkdDetailView>
131 265
@@ -137,9 +271,25 @@ @@ -137,9 +271,25 @@
137 > 271 >
138 <template slot="footer"> 272 <template slot="footer">
139 <el-button @click="closeProcessDialog">关闭</el-button> 273 <el-button @click="closeProcessDialog">关闭</el-button>
140 - <el-button v-if="canLevel1Approve" type="warning" @click="handleLevel1Approve">一级审核</el-button>  
141 - <el-button v-if="canLevel2Approve" type="success" @click="handleLevel2Approve">二级审核</el-button>  
142 - <el-button v-if="canAuditReject" type="danger" plain @click="handleReject">审核不通过</el-button> 274 + <el-button
  275 + v-if="canLevel1Approve"
  276 + type="warning"
  277 + @click="handleLevel1Approve"
  278 + >一级审核</el-button
  279 + >
  280 + <el-button
  281 + v-if="canLevel2Approve"
  282 + type="success"
  283 + @click="handleLevel2Approve"
  284 + >二级审核</el-button
  285 + >
  286 + <el-button
  287 + v-if="canAuditReject"
  288 + type="danger"
  289 + plain
  290 + @click="handleReject"
  291 + >审核不通过</el-button
  292 + >
143 </template> 293 </template>
144 </WtPriceAdjustDetailView> 294 </WtPriceAdjustDetailView>
145 295
@@ -151,9 +301,25 @@ @@ -151,9 +301,25 @@
151 > 301 >
152 <template slot="footer"> 302 <template slot="footer">
153 <el-button @click="closeProcessDialog">关闭</el-button> 303 <el-button @click="closeProcessDialog">关闭</el-button>
154 - <el-button v-if="canLevel1Approve" type="warning" @click="handleLevel1Approve">一级审核</el-button>  
155 - <el-button v-if="canLevel2Approve" type="success" @click="handleLevel2Approve">二级审核</el-button>  
156 - <el-button v-if="canAuditReject" type="danger" plain @click="handleReject">审核不通过</el-button> 304 + <el-button
  305 + v-if="canLevel1Approve"
  306 + type="warning"
  307 + @click="handleLevel1Approve"
  308 + >一级审核</el-button
  309 + >
  310 + <el-button
  311 + v-if="canLevel2Approve"
  312 + type="success"
  313 + @click="handleLevel2Approve"
  314 + >二级审核</el-button
  315 + >
  316 + <el-button
  317 + v-if="canAuditReject"
  318 + type="danger"
  319 + plain
  320 + @click="handleReject"
  321 + >审核不通过</el-button
  322 + >
157 </template> 323 </template>
158 </WtXswtdxfhdDetailView> 324 </WtXswtdxfhdDetailView>
159 325
@@ -165,9 +331,25 @@ @@ -165,9 +331,25 @@
165 > 331 >
166 <template slot="footer"> 332 <template slot="footer">
167 <el-button @click="closeProcessDialog">关闭</el-button> 333 <el-button @click="closeProcessDialog">关闭</el-button>
168 - <el-button v-if="canLevel1Approve" type="warning" @click="handleLevel1Approve">一级审核</el-button>  
169 - <el-button v-if="canLevel2Approve" type="success" @click="handleLevel2Approve">二级审核</el-button>  
170 - <el-button v-if="canAuditReject" type="danger" plain @click="handleReject">审核不通过</el-button> 334 + <el-button
  335 + v-if="canLevel1Approve"
  336 + type="warning"
  337 + @click="handleLevel1Approve"
  338 + >一级审核</el-button
  339 + >
  340 + <el-button
  341 + v-if="canLevel2Approve"
  342 + type="success"
  343 + @click="handleLevel2Approve"
  344 + >二级审核</el-button
  345 + >
  346 + <el-button
  347 + v-if="canAuditReject"
  348 + type="danger"
  349 + plain
  350 + @click="handleReject"
  351 + >审核不通过</el-button
  352 + >
171 </template> 353 </template>
172 </WtXswtdxthdDetailView> 354 </WtXswtdxthdDetailView>
173 355
@@ -179,44 +361,98 @@ @@ -179,44 +361,98 @@
179 > 361 >
180 <template slot="footer"> 362 <template slot="footer">
181 <el-button @click="closeProcessDialog">关闭</el-button> 363 <el-button @click="closeProcessDialog">关闭</el-button>
182 - <el-button v-if="canLevel1Approve" type="warning" @click="handleLevel1Approve">一级审核</el-button>  
183 - <el-button v-if="canLevel2Approve" type="success" @click="handleLevel2Approve">二级审核</el-button>  
184 - <el-button v-if="canAuditReject" type="danger" plain @click="handleReject">审核不通过</el-button> 364 + <el-button
  365 + v-if="canLevel1Approve"
  366 + type="warning"
  367 + @click="handleLevel1Approve"
  368 + >一级审核</el-button
  369 + >
  370 + <el-button
  371 + v-if="canLevel2Approve"
  372 + type="success"
  373 + @click="handleLevel2Approve"
  374 + >二级审核</el-button
  375 + >
  376 + <el-button
  377 + v-if="canAuditReject"
  378 + type="danger"
  379 + plain
  380 + @click="handleReject"
  381 + >审核不通过</el-button
  382 + >
185 </template> 383 </template>
186 </WtXswtdxjsdDetailView> 384 </WtXswtdxjsdDetailView>
  385 +
  386 + <WtYskzjjsQtDetailView
  387 + v-if="processVisible && processBillType === '其他应收单'"
  388 + ref="processDlg"
  389 + @close="onProcessDialogClose"
  390 + @loaded="onProcessLoaded"
  391 + >
  392 + <template slot="footer">
  393 + <el-button @click="closeProcessDialog">关闭</el-button>
  394 + <el-button
  395 + v-if="canLevel1Approve"
  396 + type="warning"
  397 + @click="handleLevel1Approve"
  398 + >一级审核</el-button
  399 + >
  400 + <el-button
  401 + v-if="canLevel2Approve"
  402 + type="success"
  403 + @click="handleLevel2Approve"
  404 + >二级审核</el-button
  405 + >
  406 + <el-button
  407 + v-if="canAuditReject"
  408 + type="danger"
  409 + plain
  410 + @click="handleReject"
  411 + >审核不通过</el-button
  412 + >
  413 + </template>
  414 + </WtYskzjjsQtDetailView>
187 </div> 415 </div>
188 </template> 416 </template>
189 417
190 <script> 418 <script>
191 -import { getMyFlowTodo } from '@/api/home'  
192 -import WtTjdbdDetailView from '@/views/wtTjdbd/detail-view.vue'  
193 -import WtXsckdDetailView from '@/views/wtXsckd/detail-view.vue'  
194 -import WtXsthdDetailView from '@/views/wtXsthd/detail-view.vue'  
195 -import WtYsckdDetailView from '@/views/wtYsckd/detail-view.vue'  
196 -import WtYsthdDetailView from '@/views/wtYsthd/detail-view.vue'  
197 -import WtCgrkdDetailView from '@/views/wtCgrkd/detail-view.vue'  
198 -import WtPriceAdjustDetailView from '@/views/wtPriceAdjust/detail-view.vue'  
199 -import WtXswtdxfhdDetailView from '@/views/wtXswtdxfhd/detail-view.vue'  
200 -import WtXswtdxthdDetailView from '@/views/wtXswtdxthd/detail-view.vue'  
201 -import WtXswtdxjsdDetailView from '@/views/wtXswtdxjsd/detail-view.vue'  
202 -import { promptApprovalRemark, postApproveGeneric, postRejectGeneric } from '@/utils/wtRejectApproval' 419 +import { getMyFlowTodo } from "@/api/home";
  420 +import WtTjdbdDetailView from "@/views/wtTjdbd/detail-view.vue";
  421 +import WtXsckdDetailView from "@/views/wtXsckd/detail-view.vue";
  422 +import WtXsthdDetailView from "@/views/wtXsthd/detail-view.vue";
  423 +import WtYsckdDetailView from "@/views/wtYsckd/detail-view.vue";
  424 +import WtYsthdDetailView from "@/views/wtYsthd/detail-view.vue";
  425 +import WtCgrkdDetailView from "@/views/wtCgrkd/detail-view.vue";
  426 +import WtPriceAdjustDetailView from "@/views/wtPriceAdjust/detail-view.vue";
  427 +import WtXswtdxfhdDetailView from "@/views/wtXswtdxfhd/detail-view.vue";
  428 +import WtXswtdxthdDetailView from "@/views/wtXswtdxthd/detail-view.vue";
  429 +import WtXswtdxjsdDetailView from "@/views/wtXswtdxjsd/detail-view.vue";
  430 +import WtYskzjjsQtDetailView from "@/views/wtYskzjjs_qt/detail-view.vue";
  431 +import {
  432 + promptApprovalRemark,
  433 + postApproveGeneric,
  434 + postRejectGeneric,
  435 + postYskzjjsApprove,
  436 + postYskzjjsReject
  437 +} from "@/utils/wtRejectApproval";
203 438
204 // 较长/易混淆的单据名称靠前,便于 fullName 解析 439 // 较长/易混淆的单据名称靠前,便于 fullName 解析
205 const BILL_TYPES = [ 440 const BILL_TYPES = [
206 - '委托代销退货单',  
207 - '委托代销发货单',  
208 - '委托代销结算单',  
209 - '同价调拨单',  
210 - '预售出库单',  
211 - '预售退货单',  
212 - '销售出库单',  
213 - '销售退货单',  
214 - '采购入库单',  
215 - '商品调价单'  
216 -] 441 + "委托代销退货单",
  442 + "委托代销发货单",
  443 + "委托代销结算单",
  444 + "同价调拨单",
  445 + "预售出库单",
  446 + "预售退货单",
  447 + "销售出库单",
  448 + "销售退货单",
  449 + "采购入库单",
  450 + "商品调价单",
  451 + "其他应收单"
  452 +];
217 453
218 export default { 454 export default {
219 - name: 'todoCenter', 455 + name: "todoCenter",
220 components: { 456 components: {
221 WtTjdbdDetailView, 457 WtTjdbdDetailView,
222 WtXsckdDetailView, 458 WtXsckdDetailView,
@@ -227,7 +463,8 @@ export default { @@ -227,7 +463,8 @@ export default {
227 WtPriceAdjustDetailView, 463 WtPriceAdjustDetailView,
228 WtXswtdxfhdDetailView, 464 WtXswtdxfhdDetailView,
229 WtXswtdxthdDetailView, 465 WtXswtdxthdDetailView,
230 - WtXswtdxjsdDetailView 466 + WtXswtdxjsdDetailView,
  467 + WtYskzjjsQtDetailView
231 }, 468 },
232 data() { 469 data() {
233 return { 470 return {
@@ -239,291 +476,335 @@ export default { @@ -239,291 +476,335 @@ export default {
239 pageSize: 20 476 pageSize: 20
240 }, 477 },
241 processVisible: false, 478 processVisible: false,
242 - processBillId: '',  
243 - processBillType: '', 479 + processBillId: "",
  480 + processBillType: "",
244 processDetail: null, 481 processDetail: null,
245 /** 审核成功后刷新详情,若已为「已审核」则关闭弹窗(含单级审核一次通过) */ 482 /** 审核成功后刷新详情,若已为「已审核」则关闭弹窗(含单级审核一次通过) */
246 pendingCloseWhenApproved: false, 483 pendingCloseWhenApproved: false,
247 /** 审核不通过后刷新详情,若为「审核不通过」则关闭弹窗 */ 484 /** 审核不通过后刷新详情,若为「审核不通过」则关闭弹窗 */
248 pendingCloseAfterReject: false 485 pendingCloseAfterReject: false
249 - } 486 + };
250 }, 487 },
251 computed: { 488 computed: {
252 canLevel1Approve() { 489 canLevel1Approve() {
253 - const s = this.getAuditStatus(this.processDetail)  
254 - return !s || s === '待审核' 490 + const s = this.getAuditStatus(this.processDetail);
  491 + return !s || s === "待审核";
255 }, 492 },
256 canLevel2Approve() { 493 canLevel2Approve() {
257 - const s = this.getAuditStatus(this.processDetail)  
258 - return s === '一级已审' || s === '待二级' || s === '一级已审/待二级' 494 + const s = this.getAuditStatus(this.processDetail);
  495 + return s === "一级已审" || s === "待二级" || s === "一级已审/待二级";
259 }, 496 },
260 canAuditReject() { 497 canAuditReject() {
261 - return this.canLevel1Approve || this.canLevel2Approve 498 + return this.canLevel1Approve || this.canLevel2Approve;
262 } 499 }
263 }, 500 },
264 created() { 501 created() {
265 - this.loadData() 502 + this.loadData();
266 }, 503 },
267 methods: { 504 methods: {
268 cellText(v) { 505 cellText(v) {
269 - if (v === null || v === undefined || v === '') return '无'  
270 - return v 506 + if (v === null || v === undefined || v === "") return "无";
  507 + return v;
271 }, 508 },
272 getAuditStatus(row) { 509 getAuditStatus(row) {
273 - if (!row) return ''  
274 - const z = row.djzt != null && row.djzt !== '' ? row.djzt : row.Djzt  
275 - const sh = row.shzt != null && row.shzt !== '' ? row.shzt : row.Shzt  
276 - return String(z || sh || '').trim() 510 + if (!row) return "";
  511 + const z = row.djzt != null && row.djzt !== "" ? row.djzt : row.Djzt;
  512 + const sh = row.shzt != null && row.shzt !== "" ? row.shzt : row.Shzt;
  513 + return String(z || sh || "").trim();
277 }, 514 },
278 resolveBillType(row) { 515 resolveBillType(row) {
279 - const raw = row && row.billType != null ? String(row.billType).trim() : ''  
280 - if (raw && BILL_TYPES.includes(raw)) return raw  
281 - const name = row && row.fullName ? String(row.fullName) : '' 516 + const raw =
  517 + row && row.billType != null ? String(row.billType).trim() : "";
  518 + if (raw && BILL_TYPES.includes(raw)) return raw;
  519 + const name = row && row.fullName ? String(row.fullName) : "";
282 for (let i = 0; i < BILL_TYPES.length; i++) { 520 for (let i = 0; i < BILL_TYPES.length; i++) {
283 - if (name.includes(BILL_TYPES[i])) return BILL_TYPES[i] 521 + if (name.includes(BILL_TYPES[i])) return BILL_TYPES[i];
284 } 522 }
285 - if (name.includes('TJD')) return '商品调价单'  
286 - return '同价调拨单' 523 + if (name.includes("TJD")) return "商品调价单";
  524 + return "同价调拨单";
287 }, 525 },
288 formatTime(v) { 526 formatTime(v) {
289 - if (!v) return '无'  
290 - const d = new Date(v)  
291 - if (isNaN(d.getTime())) return '无'  
292 - const y = d.getFullYear()  
293 - const m = String(d.getMonth() + 1).padStart(2, '0')  
294 - const day = String(d.getDate()).padStart(2, '0')  
295 - return `${y}-${m}-${day}` 527 + if (!v) return "无";
  528 + const d = new Date(v);
  529 + if (isNaN(d.getTime())) return "无";
  530 + const y = d.getFullYear();
  531 + const m = String(d.getMonth() + 1).padStart(2, "0");
  532 + const day = String(d.getDate()).padStart(2, "0");
  533 + return `${y}-${m}-${day}`;
296 }, 534 },
297 tableIndexMethod(index) { 535 tableIndexMethod(index) {
298 - return (this.listQuery.currentPage - 1) * this.listQuery.pageSize + index + 1 536 + return (
  537 + (this.listQuery.currentPage - 1) * this.listQuery.pageSize + index + 1
  538 + );
299 }, 539 },
300 loadData() { 540 loadData() {
301 - this.loading = true 541 + this.loading = true;
302 return getMyFlowTodo({ 542 return getMyFlowTodo({
303 currentPage: this.listQuery.currentPage, 543 currentPage: this.listQuery.currentPage,
304 pageSize: this.listQuery.pageSize 544 pageSize: this.listQuery.pageSize
305 }) 545 })
306 .then(res => { 546 .then(res => {
307 - const raw = (res.data && res.data.list) || []  
308 - this.list = Array.isArray(raw) ? raw : []  
309 - const pg = res.data && res.data.pagination  
310 - this.total = (pg && typeof pg.total === 'number' ? pg.total : this.list.length) || 0  
311 - if (this.list.length === 0 && this.total > 0 && this.listQuery.currentPage > 1) {  
312 - const lastPage = Math.max(1, Math.ceil(this.total / this.listQuery.pageSize)) 547 + const raw = (res.data && res.data.list) || [];
  548 + this.list = Array.isArray(raw) ? raw : [];
  549 + const pg = res.data && res.data.pagination;
  550 + this.total =
  551 + (pg && typeof pg.total === "number"
  552 + ? pg.total
  553 + : this.list.length) || 0;
  554 + if (
  555 + this.list.length === 0 &&
  556 + this.total > 0 &&
  557 + this.listQuery.currentPage > 1
  558 + ) {
  559 + const lastPage = Math.max(
  560 + 1,
  561 + Math.ceil(this.total / this.listQuery.pageSize)
  562 + );
313 if (this.listQuery.currentPage > lastPage) { 563 if (this.listQuery.currentPage > lastPage) {
314 - this.listQuery.currentPage = lastPage  
315 - return this.loadData() 564 + this.listQuery.currentPage = lastPage;
  565 + return this.loadData();
316 } 566 }
317 } 567 }
318 }) 568 })
319 .catch(() => { 569 .catch(() => {
320 - this.$message.error('加载待办失败')  
321 - this.list = []  
322 - this.total = 0 570 + this.$message.error("加载待办失败");
  571 + this.list = [];
  572 + this.total = 0;
323 }) 573 })
324 .finally(() => { 574 .finally(() => {
325 - this.loading = false  
326 - }) 575 + this.loading = false;
  576 + });
327 }, 577 },
328 openProcess(row) { 578 openProcess(row) {
329 - const id = row && row.id 579 + const id = row && row.id;
330 if (!id) { 580 if (!id) {
331 - this.$message.warning('无单据编号')  
332 - return 581 + this.$message.warning("无单据编号");
  582 + return;
333 } 583 }
334 - const billType = this.resolveBillType(row)  
335 - this.processBillId = String(id)  
336 - this.processBillType = billType  
337 - this.processDetail = null  
338 - this.pendingCloseWhenApproved = false  
339 - this.pendingCloseAfterReject = false  
340 - this.processVisible = true 584 + const billType = this.resolveBillType(row);
  585 + this.processBillId = String(id);
  586 + this.processBillType = billType;
  587 + this.processDetail = null;
  588 + this.pendingCloseWhenApproved = false;
  589 + this.pendingCloseAfterReject = false;
  590 + this.processVisible = true;
341 this.$nextTick(() => { 591 this.$nextTick(() => {
342 - if (this.$refs.processDlg) this.$refs.processDlg.init(this.processBillId)  
343 - }) 592 + if (this.$refs.processDlg)
  593 + this.$refs.processDlg.init(this.processBillId);
  594 + });
344 }, 595 },
345 onProcessDialogClose() { 596 onProcessDialogClose() {
346 - this.processVisible = false  
347 - this.processBillId = ''  
348 - this.processBillType = ''  
349 - this.processDetail = null  
350 - this.pendingCloseWhenApproved = false  
351 - this.pendingCloseAfterReject = false 597 + this.processVisible = false;
  598 + this.processBillId = "";
  599 + this.processBillType = "";
  600 + this.processDetail = null;
  601 + this.pendingCloseWhenApproved = false;
  602 + this.pendingCloseAfterReject = false;
352 }, 603 },
353 closeProcessDialog() { 604 closeProcessDialog() {
354 - if (this.$refs.processDlg) this.$refs.processDlg.close() 605 + if (this.$refs.processDlg) this.$refs.processDlg.close();
355 }, 606 },
356 onProcessLoaded(detail) { 607 onProcessLoaded(detail) {
357 - this.processDetail = detail || null 608 + this.processDetail = detail || null;
358 if (this.pendingCloseWhenApproved) { 609 if (this.pendingCloseWhenApproved) {
359 - this.pendingCloseWhenApproved = false  
360 - if (this.getAuditStatus(detail) === '已审核') { 610 + this.pendingCloseWhenApproved = false;
  611 + if (this.getAuditStatus(detail) === "已审核") {
361 this.$nextTick(() => { 612 this.$nextTick(() => {
362 - if (this.$refs.processDlg) this.$refs.processDlg.close()  
363 - }) 613 + if (this.$refs.processDlg) this.$refs.processDlg.close();
  614 + });
364 } 615 }
365 } 616 }
366 if (this.pendingCloseAfterReject) { 617 if (this.pendingCloseAfterReject) {
367 - this.pendingCloseAfterReject = false  
368 - if (this.getAuditStatus(detail) === '审核不通过') { 618 + this.pendingCloseAfterReject = false;
  619 + if (this.getAuditStatus(detail) === "审核不通过") {
369 this.$nextTick(() => { 620 this.$nextTick(() => {
370 - if (this.$refs.processDlg) this.$refs.processDlg.close()  
371 - }) 621 + if (this.$refs.processDlg) this.$refs.processDlg.close();
  622 + });
372 } 623 }
373 } 624 }
374 }, 625 },
375 level1Confirm() { 626 level1Confirm() {
376 - const t = this.processBillType  
377 - if (t === '同价调拨单') {  
378 - return { msg: '确认执行一级审核(调出仓确认发货)?', title: '一级审核确认' } 627 + const t = this.processBillType;
  628 + if (t === "其他应收单") {
  629 + return { msg: "确认执行一级审核该其他应收单?", title: "审核确认" };
379 } 630 }
380 - if (t === '销售出库单') {  
381 - return { msg: '确认审核该销售出库单?', title: '审核确认' } 631 + if (t === "同价调拨单") {
  632 + return {
  633 + msg: "确认执行一级审核(调出仓确认发货)?",
  634 + title: "一级审核确认"
  635 + };
  636 + }
  637 + if (t === "销售出库单") {
  638 + return { msg: "确认审核该销售出库单?", title: "审核确认" };
382 } 639 }
383 - if (t === '销售退货单') {  
384 - return { msg: '确认审核该销售退货单?', title: '审核确认' } 640 + if (t === "销售退货单") {
  641 + return { msg: "确认审核该销售退货单?", title: "审核确认" };
385 } 642 }
386 - if (t === '预售出库单') {  
387 - return { msg: '确认审核该预售出库单?', title: '审核确认' } 643 + if (t === "预售出库单") {
  644 + return { msg: "确认审核该预售出库单?", title: "审核确认" };
388 } 645 }
389 - if (t === '预售退货单') {  
390 - return { msg: '确认审核该预售退货单?', title: '审核确认' } 646 + if (t === "预售退货单") {
  647 + return { msg: "确认审核该预售退货单?", title: "审核确认" };
391 } 648 }
392 - if (t === '采购入库单') { 649 + if (t === "采购入库单") {
393 return { 650 return {
394 - msg: '确认审核该采购入库单?最终审核通过后将生成序列号并更新库存。',  
395 - title: '审核确认'  
396 - } 651 + msg: "确认审核该采购入库单?最终审核通过后将生成序列号并更新库存。",
  652 + title: "审核确认"
  653 + };
397 } 654 }
398 - if (t === '商品调价单') { 655 + if (t === "商品调价单") {
399 return { 656 return {
400 - msg: '确认审核该商品调价单?通过后调后售价将写入商品档案零售价。',  
401 - title: '审核确认'  
402 - } 657 + msg: "确认审核该商品调价单?通过后调后售价将写入商品档案零售价。",
  658 + title: "审核确认"
  659 + };
403 } 660 }
404 - if (t === '委托代销发货单') {  
405 - return { msg: '确认审核该委托代销发货单?', title: '审核确认' } 661 + if (t === "委托代销发货单") {
  662 + return { msg: "确认审核该委托代销发货单?", title: "审核确认" };
406 } 663 }
407 - if (t === '委托代销退货单') {  
408 - return { msg: '确认审核该委托代销退货单?', title: '审核确认' } 664 + if (t === "委托代销退货单") {
  665 + return { msg: "确认审核该委托代销退货单?", title: "审核确认" };
409 } 666 }
410 - if (t === '委托代销结算单') {  
411 - return { msg: '确认审核该委托代销结算单?', title: '审核确认' } 667 + if (t === "委托代销结算单") {
  668 + return { msg: "确认审核该委托代销结算单?", title: "审核确认" };
412 } 669 }
413 - return { msg: '确认执行一级审核?', title: '审核确认' } 670 + return { msg: "确认执行一级审核?", title: "审核确认" };
414 }, 671 },
415 level2Confirm() { 672 level2Confirm() {
416 - const t = this.processBillType  
417 - if (t === '同价调拨单') {  
418 - return { msg: '确认执行二级审核(确认收货并入库出库)?', title: '二级审核确认' } 673 + const t = this.processBillType;
  674 + if (t === "同价调拨单") {
  675 + return {
  676 + msg: "确认执行二级审核(确认收货并入库出库)?",
  677 + title: "二级审核确认"
  678 + };
419 } 679 }
420 - if (t === '销售出库单') {  
421 - return { msg: '确认审核该销售出库单?', title: '审核确认' } 680 + if (t === "销售出库单") {
  681 + return { msg: "确认审核该销售出库单?", title: "审核确认" };
422 } 682 }
423 - if (t === '销售退货单') {  
424 - return { msg: '确认审核该销售退货单?', title: '审核确认' } 683 + if (t === "销售退货单") {
  684 + return { msg: "确认审核该销售退货单?", title: "审核确认" };
425 } 685 }
426 - if (t === '预售出库单') {  
427 - return { msg: '确认审核该预售出库单?', title: '审核确认' } 686 + if (t === "预售出库单") {
  687 + return { msg: "确认审核该预售出库单?", title: "审核确认" };
428 } 688 }
429 - if (t === '预售退货单') {  
430 - return { msg: '确认审核该预售退货单?', title: '审核确认' } 689 + if (t === "预售退货单") {
  690 + return { msg: "确认审核该预售退货单?", title: "审核确认" };
431 } 691 }
432 - if (t === '采购入库单') { 692 + if (t === "采购入库单") {
433 return { 693 return {
434 - msg: '确认审核该采购入库单?最终审核通过后将生成序列号并更新库存。',  
435 - title: '审核确认'  
436 - } 694 + msg: "确认审核该采购入库单?最终审核通过后将生成序列号并更新库存。",
  695 + title: "审核确认"
  696 + };
437 } 697 }
438 - if (t === '商品调价单') { 698 + if (t === "商品调价单") {
439 return { 699 return {
440 - msg: '确认二级审核该商品调价单?通过后调后售价将写入商品档案零售价。',  
441 - title: '二级审核确认'  
442 - } 700 + msg: "确认二级审核该商品调价单?通过后调后售价将写入商品档案零售价。",
  701 + title: "二级审核确认"
  702 + };
443 } 703 }
444 - if (t === '委托代销发货单') {  
445 - return { msg: '确认审核该委托代销发货单?', title: '审核确认' } 704 + if (t === "委托代销发货单") {
  705 + return { msg: "确认审核该委托代销发货单?", title: "审核确认" };
446 } 706 }
447 - if (t === '委托代销退货单') {  
448 - return { msg: '确认审核该委托代销退货单?', title: '审核确认' } 707 + if (t === "委托代销退货单") {
  708 + return { msg: "确认审核该委托代销退货单?", title: "审核确认" };
449 } 709 }
450 - if (t === '委托代销结算单') {  
451 - return { msg: '确认审核该委托代销结算单?', title: '审核确认' } 710 + if (t === "委托代销结算单") {
  711 + return { msg: "确认审核该委托代销结算单?", title: "审核确认" };
452 } 712 }
453 - return { msg: '确认执行二级审核?', title: '审核确认' } 713 + return { msg: "确认执行二级审核?", title: "审核确认" };
454 }, 714 },
455 handleLevel1Approve() { 715 handleLevel1Approve() {
456 - const id = this.processBillId  
457 - if (!id) return  
458 - const { msg, title } = this.level1Confirm()  
459 - this.$confirm(msg, title, { type: 'warning' })  
460 - .then(() => promptApprovalRemark(this, '审批备注'))  
461 - .then(remark => postApproveGeneric(id, remark)) 716 + const id = this.processBillId;
  717 + if (!id) return;
  718 + const { msg, title } = this.level1Confirm();
  719 + this.$confirm(msg, title, { type: "warning" })
  720 + .then(() => promptApprovalRemark(this, "审批备注"))
  721 + .then(remark =>
  722 + this.processBillType === "其他应收单"
  723 + ? postYskzjjsApprove(id, remark)
  724 + : postApproveGeneric(id, remark)
  725 + )
462 .then(res => { 726 .then(res => {
463 if (res.data && res.data.success) { 727 if (res.data && res.data.success) {
464 - this.$message({ type: 'success', message: res.data.message || '操作成功' })  
465 - this.pendingCloseWhenApproved = true  
466 - this.loadData() 728 + this.$message({
  729 + type: "success",
  730 + message: res.data.message || "操作成功"
  731 + });
  732 + this.pendingCloseWhenApproved = true;
  733 + this.loadData();
467 this.$nextTick(() => { 734 this.$nextTick(() => {
468 - if (this.$refs.processDlg) this.$refs.processDlg.init(id)  
469 - }) 735 + if (this.$refs.processDlg) this.$refs.processDlg.init(id);
  736 + });
470 } else { 737 } else {
471 this.$message({ 738 this.$message({
472 - type: 'error',  
473 - message: (res.data && res.data.message) || '审核失败'  
474 - }) 739 + type: "error",
  740 + message: (res.data && res.data.message) || "审核失败"
  741 + });
475 } 742 }
476 }) 743 })
477 - .catch(() => {}) 744 + .catch(() => {});
478 }, 745 },
479 handleLevel2Approve() { 746 handleLevel2Approve() {
480 - const id = this.processBillId  
481 - if (!id) return  
482 - const { msg, title } = this.level2Confirm()  
483 - this.$confirm(msg, title, { type: 'warning' })  
484 - .then(() => promptApprovalRemark(this, '审批备注'))  
485 - .then(remark => postApproveGeneric(id, remark)) 747 + const id = this.processBillId;
  748 + if (!id) return;
  749 + const { msg, title } = this.level2Confirm();
  750 + this.$confirm(msg, title, { type: "warning" })
  751 + .then(() => promptApprovalRemark(this, "审批备注"))
  752 + .then(remark =>
  753 + this.processBillType === "其他应收单"
  754 + ? postYskzjjsApprove(id, remark)
  755 + : postApproveGeneric(id, remark)
  756 + )
486 .then(res => { 757 .then(res => {
487 if (res.data && res.data.success) { 758 if (res.data && res.data.success) {
488 - this.$message({ type: 'success', message: res.data.message || '操作成功' })  
489 - this.pendingCloseWhenApproved = true  
490 - this.loadData() 759 + this.$message({
  760 + type: "success",
  761 + message: res.data.message || "操作成功"
  762 + });
  763 + this.pendingCloseWhenApproved = true;
  764 + this.loadData();
491 this.$nextTick(() => { 765 this.$nextTick(() => {
492 - if (this.$refs.processDlg) this.$refs.processDlg.init(id)  
493 - }) 766 + if (this.$refs.processDlg) this.$refs.processDlg.init(id);
  767 + });
494 } else { 768 } else {
495 this.$message({ 769 this.$message({
496 - type: 'error',  
497 - message: (res.data && res.data.message) || '审核失败'  
498 - }) 770 + type: "error",
  771 + message: (res.data && res.data.message) || "审核失败"
  772 + });
499 } 773 }
500 }) 774 })
501 - .catch(() => {}) 775 + .catch(() => {});
502 }, 776 },
503 handleReject() { 777 handleReject() {
504 - const id = this.processBillId  
505 - if (!id) return  
506 - promptApprovalRemark(this, '审核不通过')  
507 - .then(reason => postRejectGeneric(id, reason)) 778 + const id = this.processBillId;
  779 + if (!id) return;
  780 + promptApprovalRemark(this, "审核不通过")
  781 + .then(reason =>
  782 + this.processBillType === "其他应收单"
  783 + ? postYskzjjsReject(id, reason)
  784 + : postRejectGeneric(id, reason)
  785 + )
508 .then(res => { 786 .then(res => {
509 if (res.data && res.data.success) { 787 if (res.data && res.data.success) {
510 - this.$message({ type: 'success', message: res.data.message || '已标记审核不通过' })  
511 - this.pendingCloseAfterReject = true  
512 - this.loadData() 788 + this.$message({
  789 + type: "success",
  790 + message: res.data.message || "已标记审核不通过"
  791 + });
  792 + this.pendingCloseAfterReject = true;
  793 + this.loadData();
513 this.$nextTick(() => { 794 this.$nextTick(() => {
514 - if (this.$refs.processDlg) this.$refs.processDlg.init(id)  
515 - }) 795 + if (this.$refs.processDlg) this.$refs.processDlg.init(id);
  796 + });
516 } else { 797 } else {
517 this.$message({ 798 this.$message({
518 - type: 'error',  
519 - message: (res.data && res.data.message) || '操作失败'  
520 - }) 799 + type: "error",
  800 + message: (res.data && res.data.message) || "操作失败"
  801 + });
521 } 802 }
522 }) 803 })
523 - .catch(() => {}) 804 + .catch(() => {});
524 } 805 }
525 } 806 }
526 -} 807 +};
527 </script> 808 </script>
528 809
529 <style scoped lang="scss"> 810 <style scoped lang="scss">
Antis.Erp.Plat/antis-ncc-admin/src/views/permission/user/Form.vue
@@ -21,6 +21,18 @@ @@ -21,6 +21,18 @@
21 <el-input v-model="dataForm.account" placeholder="账户名称" /> 21 <el-input v-model="dataForm.account" placeholder="账户名称" />
22 </el-form-item> 22 </el-form-item>
23 </el-col> 23 </el-col>
  24 + <el-col v-if="!dataForm.id" :sm="12" :xs="24">
  25 + <el-form-item label="密码" prop="password">
  26 + <el-input v-model="dataForm.password" type="password" autocomplete="new-password"
  27 + placeholder="6-18 位字符" show-password />
  28 + </el-form-item>
  29 + </el-col>
  30 + <el-col v-if="!dataForm.id" :sm="12" :xs="24">
  31 + <el-form-item label="确认密码" prop="confirmPassword">
  32 + <el-input v-model="dataForm.confirmPassword" type="password" autocomplete="new-password"
  33 + placeholder="再次输入密码" show-password />
  34 + </el-form-item>
  35 + </el-col>
24 <el-col :sm="12" :xs="24"> 36 <el-col :sm="12" :xs="24">
25 <el-form-item label="姓名" prop="realName"> 37 <el-form-item label="姓名" prop="realName">
26 <el-input v-model="dataForm.realName" placeholder="真实姓名" /> 38 <el-input v-model="dataForm.realName" placeholder="真实姓名" />
@@ -215,6 +227,8 @@ export default { @@ -215,6 +227,8 @@ export default {
215 sortCode: 0, 227 sortCode: 0,
216 enabledMark: 1, 228 enabledMark: 1,
217 account: '', 229 account: '',
  230 + password: '',
  231 + confirmPassword: '',
218 realName: '', 232 realName: '',
219 organizeId: '', 233 organizeId: '',
220 managerId: '', 234 managerId: '',
@@ -269,6 +283,50 @@ export default { @@ -269,6 +283,50 @@ export default {
269 ], 283 ],
270 organizeId: [ 284 organizeId: [
271 { required: true, message: '请选择组织', trigger: 'change' } 285 { required: true, message: '请选择组织', trigger: 'change' }
  286 + ],
  287 + password: [
  288 + {
  289 + validator: (rule, value, callback) => {
  290 + if (this.dataForm.id) {
  291 + callback()
  292 + return
  293 + }
  294 + if (!value) {
  295 + callback(new Error('请输入密码'))
  296 + return
  297 + }
  298 + const s = value.toString()
  299 + if (s.length < 6 || s.length > 18) {
  300 + callback(new Error('密码长度为6 - 18个字符'))
  301 + return
  302 + }
  303 + if (this.dataForm.confirmPassword !== '') {
  304 + this.$refs.dataForm.validateField('confirmPassword')
  305 + }
  306 + callback()
  307 + },
  308 + trigger: 'blur'
  309 + }
  310 + ],
  311 + confirmPassword: [
  312 + {
  313 + validator: (rule, value, callback) => {
  314 + if (this.dataForm.id) {
  315 + callback()
  316 + return
  317 + }
  318 + if (!value) {
  319 + callback(new Error('请再次输入密码'))
  320 + return
  321 + }
  322 + if (value !== this.dataForm.password) {
  323 + callback(new Error('两次输入密码不一致'))
  324 + return
  325 + }
  326 + callback()
  327 + },
  328 + trigger: 'blur'
  329 + }
272 ] 330 ]
273 } 331 }
274 } 332 }
@@ -328,6 +386,8 @@ export default { @@ -328,6 +386,8 @@ export default {
328 this.formLoading = true 386 this.formLoading = true
329 getUserInfo(this.dataForm.id).then(res => { 387 getUserInfo(this.dataForm.id).then(res => {
330 this.dataForm = res.data 388 this.dataForm = res.data
  389 + this.$set(this.dataForm, 'password', '')
  390 + this.$set(this.dataForm, 'confirmPassword', '')
331 if (this.dataForm.roleId) this.roleId = this.dataForm.roleId.split(',') 391 if (this.dataForm.roleId) this.roleId = this.dataForm.roleId.split(',')
332 this.formLoading = false 392 this.formLoading = false
333 }).catch(() => this.formLoading = false) 393 }).catch(() => this.formLoading = false)
@@ -354,7 +414,12 @@ export default { @@ -354,7 +414,12 @@ export default {
354 if (valid) { 414 if (valid) {
355 this.btnLoading = true 415 this.btnLoading = true
356 const formMethod = this.dataForm.id ? updateUser : createUser 416 const formMethod = this.dataForm.id ? updateUser : createUser
357 - formMethod(this.dataForm).then(res => { 417 + const payload = { ...this.dataForm }
  418 + delete payload.confirmPassword
  419 + if (payload.id) {
  420 + delete payload.password
  421 + }
  422 + formMethod(payload).then(res => {
358 this.$message({ 423 this.$message({
359 message: res.msg, 424 message: res.msg,
360 type: 'success', 425 type: 'success',
Antis.Erp.Plat/antis-ncc-admin/src/views/wtPurchaseSummary/index.vue
@@ -67,20 +67,33 @@ @@ -67,20 +67,33 @@
67 reserve-keyword 67 reserve-keyword
68 popper-class="wt-purchase-sum-product-dropdown" 68 popper-class="wt-purchase-sum-product-dropdown"
69 :remote-method="handleProductSearch" 69 :remote-method="handleProductSearch"
70 - :loading="productLoading" 70 + :loading="productRemoteLoading"
71 placeholder="输入商品编码或名称搜索" 71 placeholder="输入商品编码或名称搜索"
72 style="width: 100%" 72 style="width: 100%"
73 > 73 >
74 <el-option 74 <el-option
75 v-for="item in productOptions" 75 v-for="item in productOptions"
76 - :key="item.F_Id || item.id" 76 + :key="productOptionValue(item) + '-' + (item.spbm || '')"
77 :label="formatProductLabel(item)" 77 :label="formatProductLabel(item)"
78 - :value="item.F_Id || item.id" 78 + :value="productOptionValue(item)"
79 > 79 >
80 - <div class="product-opt-row cell-nowrap" :title="formatProductLabel(item)">  
81 - <span v-if="productCodeRaw(item)" class="product-opt-code">{{ productCodeRaw(item) }}</span>  
82 - <span v-if="productCodeRaw(item) && productNameRaw(item)" class="product-opt-sep">|</span>  
83 - <span class="product-opt-name">{{ productNameRaw(item) || '无' }}</span> 80 + <div
  81 + class="product-opt-row cell-nowrap"
  82 + :title="formatProductLabel(item)"
  83 + >
  84 + <span
  85 + v-if="productCodeRaw(item)"
  86 + class="product-opt-code"
  87 + >{{ productCodeRaw(item) }}</span
  88 + >
  89 + <span
  90 + v-if="productCodeRaw(item) && productNameRaw(item)"
  91 + class="product-opt-sep"
  92 + >|</span
  93 + >
  94 + <span class="product-opt-name">{{
  95 + productNameRaw(item) || "无"
  96 + }}</span>
84 </div> 97 </div>
85 </el-option> 98 </el-option>
86 </el-select> 99 </el-select>
@@ -127,8 +140,15 @@ @@ -127,8 +140,15 @@
127 </el-col> 140 </el-col>
128 <el-col :span="6"> 141 <el-col :span="6">
129 <el-form-item> 142 <el-form-item>
130 - <el-button type="primary" icon="el-icon-search" @click="handleSearch">查询</el-button>  
131 - <el-button icon="el-icon-refresh-right" @click="handleReset">重置</el-button> 143 + <el-button
  144 + type="primary"
  145 + icon="el-icon-search"
  146 + @click="handleSearch"
  147 + >查询</el-button
  148 + >
  149 + <el-button icon="el-icon-refresh-right" @click="handleReset"
  150 + >重置</el-button
  151 + >
132 </el-form-item> 152 </el-form-item>
133 </el-col> 153 </el-col>
134 </el-form> 154 </el-form>
@@ -138,11 +158,27 @@ @@ -138,11 +158,27 @@
138 <div class="NCC-common-head purchase-sum-head"> 158 <div class="NCC-common-head purchase-sum-head">
139 <div> 159 <div>
140 <span class="purchase-sum-head__title"> 160 <span class="purchase-sum-head__title">
141 - <i class="el-icon-s-data purchase-sum-head__icon" aria-hidden="true" />  
142 - 商品采购汇总(分类 → 品牌 → 商品明细分类 → 采购明细) 161 + <i
  162 + class="el-icon-s-data purchase-sum-head__icon"
  163 + aria-hidden="true"
  164 + />
  165 + 商品采购汇总(分类 → 品牌 → 商品 → 采购明细)
143 </span> 166 </span>
144 </div> 167 </div>
145 - <div class="NCC-common-head-right"> 168 + <div class="NCC-common-head-right purchase-sum-head__tools">
  169 + <span class="linear-mode-label">
  170 + <i
  171 + class="el-icon-s-operation row-ico--primary"
  172 + aria-hidden="true"
  173 + />
  174 + 线性列表
  175 + </span>
  176 + <el-switch
  177 + v-model="linearInlineMode"
  178 + active-color="#409EFF"
  179 + inactive-color="#dcdfe6"
  180 + @change="onLinearInlineModeChange"
  181 + />
146 <el-tooltip effect="dark" content="刷新" placement="top"> 182 <el-tooltip effect="dark" content="刷新" placement="top">
147 <el-link 183 <el-link
148 icon="icon-ym icon-ym-Refresh NCC-common-head-icon" 184 icon="icon-ym icon-ym-Refresh NCC-common-head-icon"
@@ -155,145 +191,581 @@ @@ -155,145 +191,581 @@
155 </div> 191 </div>
156 192
157 <el-table 193 <el-table
  194 + ref="categoryTable"
158 v-loading="categoryLoading" 195 v-loading="categoryLoading"
159 :data="categoryList" 196 :data="categoryList"
160 border 197 border
161 class="purchase-tree-table" 198 class="purchase-tree-table"
162 - :row-key="r => String(r['分类Id'])" 199 + :row-key="categoryRowKey"
163 @expand-change="onCategoryExpand" 200 @expand-change="onCategoryExpand"
  201 + @sort-change="onCategorySort"
164 > 202 >
165 <el-table-column type="expand" width="48"> 203 <el-table-column type="expand" width="48">
166 <template slot-scope="catScope"> 204 <template slot-scope="catScope">
167 - <div v-loading="brandLoading[categoryRowKey(catScope.row)]" class="nested-wrap">  
168 - <el-table  
169 - :data="brandMap[categoryRowKey(catScope.row)] || []"  
170 - border  
171 - size="small"  
172 - class="nested-table"  
173 - :row-key="r => brandRowKey(catScope.row, r)"  
174 - @expand-change="(r, er) => onBrandExpand(catScope.row, r, er)"  
175 - >  
176 - <el-table-column type="expand" width="44">  
177 - <template slot-scope="brandScope">  
178 - <div v-loading="productLoading[brandRowKey(catScope.row, brandScope.row)]" class="nested-wrap">  
179 - <el-table  
180 - :data="productMap[brandRowKey(catScope.row, brandScope.row)] || []"  
181 - border  
182 - size="small"  
183 - class="nested-table nested-table--deep"  
184 - :row-key="r => productRowKey(catScope.row, brandScope.row, r)"  
185 - @expand-change="(r, er) => onProductExpand(catScope.row, brandScope.row, r, er)" 205 + <div
  206 + v-if="linearInlineMode"
  207 + v-loading="linearInlineLoading[categoryRowKey(catScope.row)]"
  208 + class="nested-wrap nested-wrap--linear"
  209 + >
  210 + <div class="nested-table-host">
  211 + <el-table
  212 + :data="
  213 + (linearInlineMap[categoryRowKey(catScope.row)] || {})
  214 + .list || []
  215 + "
  216 + border
  217 + size="small"
  218 + class="nested-table nested-table--scroll"
  219 + :row-key="
  220 + (r, idx) =>
  221 + lineDetailRowKey(
  222 + 'lin',
  223 + catScope.row,
  224 + null,
  225 + null,
  226 + r,
  227 + idx
  228 + )
  229 + "
  230 + >
  231 + <el-table-column
  232 + type="index"
  233 + label="行号"
  234 + width="52"
  235 + align="center"
  236 + />
  237 + <el-table-column
  238 + prop="单据日期"
  239 + label="单据日期"
  240 + width="108"
  241 + show-overflow-tooltip
  242 + />
  243 + <el-table-column
  244 + prop="单据编号"
  245 + label="单据编号"
  246 + min-width="140"
  247 + show-overflow-tooltip
  248 + />
  249 + <el-table-column
  250 + prop="单据类型"
  251 + label="单据类型"
  252 + width="108"
  253 + show-overflow-tooltip
  254 + />
  255 + <el-table-column
  256 + prop="往来单位"
  257 + label="往来单位"
  258 + min-width="120"
  259 + show-overflow-tooltip
  260 + />
  261 + <el-table-column
  262 + prop="经手人"
  263 + label="经手人"
  264 + width="88"
  265 + show-overflow-tooltip
  266 + />
  267 + <el-table-column
  268 + prop="仓库名称"
  269 + label="仓库名称"
  270 + min-width="100"
  271 + show-overflow-tooltip
  272 + />
  273 + <el-table-column
  274 + prop="商品名称"
  275 + label="商品名称"
  276 + min-width="140"
  277 + show-overflow-tooltip
  278 + />
  279 + <el-table-column
  280 + prop="数量"
  281 + label="数量"
  282 + width="88"
  283 + align="right"
  284 + >
  285 + <template slot-scope="s">{{
  286 + formatQty(s.row["数量"])
  287 + }}</template>
  288 + </el-table-column>
  289 + <el-table-column
  290 + prop="入库单价"
  291 + label="入库单价"
  292 + width="100"
  293 + align="right"
  294 + >
  295 + <template slot-scope="s">{{
  296 + formatMoney(s.row["入库单价"])
  297 + }}</template>
  298 + </el-table-column>
  299 + <el-table-column
  300 + prop="采购金额"
  301 + label="采购金额"
  302 + width="108"
  303 + align="right"
  304 + >
  305 + <template slot-scope="s">{{
  306 + formatMoney(s.row["采购金额"])
  307 + }}</template>
  308 + </el-table-column>
  309 + </el-table>
  310 + </div>
  311 + </div>
  312 + <div
  313 + v-else
  314 + v-loading="brandLoading[categoryRowKey(catScope.row)]"
  315 + class="nested-wrap"
  316 + >
  317 + <div class="nested-table-host">
  318 + <el-table
  319 + :data="brandMap[categoryRowKey(catScope.row)] || []"
  320 + border
  321 + size="small"
  322 + class="nested-table nested-table--scroll"
  323 + :row-key="r => brandRowKey(catScope.row, r)"
  324 + @expand-change="
  325 + (r, er) => onBrandExpand(catScope.row, r, er)
  326 + "
  327 + @sort-change="ev => onBrandSort(catScope.row, ev)"
  328 + >
  329 + <el-table-column type="expand" width="44">
  330 + <template slot-scope="brandScope">
  331 + <div
  332 + v-loading="
  333 + aggProductLoading[
  334 + brandRowKey(catScope.row, brandScope.row)
  335 + ]
  336 + "
  337 + class="nested-wrap"
186 > 338 >
187 - <el-table-column type="expand" width="42">  
188 - <template slot-scope="prodScope">  
189 - <div v-loading="lineLoading[lineKey(catScope.row, brandScope.row, prodScope.row)]" class="nested-wrap detail-wrap">  
190 - <el-table  
191 - :data="(lineMap[lineKey(catScope.row, brandScope.row, prodScope.row)] || {}).list || []"  
192 - border  
193 - size="mini"  
194 - class="nested-table"  
195 - >  
196 - <el-table-column type="index" label="行号" width="52" align="center" />  
197 - <el-table-column prop="单据日期" label="单据日期" width="108" show-overflow-tooltip sortable :sort-method="sortTextCol('单据日期')" />  
198 - <el-table-column prop="单据编号" label="单据编号" min-width="140" show-overflow-tooltip sortable :sort-method="sortTextCol('单据编号')" />  
199 - <el-table-column prop="单据类型" label="单据类型" width="108" show-overflow-tooltip sortable :sort-method="sortTextCol('单据类型')" />  
200 - <el-table-column prop="往来单位" label="往来单位" min-width="120" show-overflow-tooltip sortable :sort-method="sortTextCol('往来单位')" />  
201 - <el-table-column prop="经手人" label="经手人" width="88" show-overflow-tooltip sortable :sort-method="sortTextCol('经手人')" />  
202 - <el-table-column prop="仓库名称" label="仓库名称" min-width="100" show-overflow-tooltip sortable :sort-method="sortTextCol('仓库名称')" />  
203 - <el-table-column prop="商品名称" label="商品名称" min-width="140" show-overflow-tooltip sortable :sort-method="sortTextCol('商品名称')" />  
204 - <el-table-column prop="数量" label="数量" width="88" align="right" sortable :sort-method="sortNumCol('数量')">  
205 - <template slot-scope="s">{{ formatQty(s.row['数量']) }}</template>  
206 - </el-table-column>  
207 - <el-table-column prop="入库单价" label="入库单价" width="100" align="right" sortable :sort-method="sortNumCol('入库单价')">  
208 - <template slot-scope="s">{{ formatMoney(s.row['入库单价']) }}</template>  
209 - </el-table-column>  
210 - <el-table-column prop="采购金额" label="采购金额" width="108" align="right" sortable :sort-method="sortNumCol('采购金额')">  
211 - <template slot-scope="s">{{ formatMoney(s.row['采购金额']) }}</template>  
212 - </el-table-column>  
213 - </el-table>  
214 - <div v-if="linePager[lineKey(catScope.row, brandScope.row, prodScope.row)]" class="mini-pager">  
215 - <el-pagination  
216 - small  
217 - layout="prev, pager, next, total"  
218 - :total="linePager[lineKey(catScope.row, brandScope.row, prodScope.row)].total"  
219 - :page-size="linePager[lineKey(catScope.row, brandScope.row, prodScope.row)].pageSize"  
220 - :current-page="linePager[lineKey(catScope.row, brandScope.row, prodScope.row)].currentPage"  
221 - @current-change="p => fetchLines(catScope.row, brandScope.row, prodScope.row, p)" 339 + <div class="nested-table-host">
  340 + <el-table
  341 + :data="
  342 + productMap[
  343 + brandRowKey(catScope.row, brandScope.row)
  344 + ] || []
  345 + "
  346 + border
  347 + size="small"
  348 + class="nested-table nested-table--deep nested-table--scroll"
  349 + :row-key="
  350 + r =>
  351 + productRowKey(catScope.row, brandScope.row, r)
  352 + "
  353 + @expand-change="
  354 + (r, er) =>
  355 + onProductExpand(
  356 + catScope.row,
  357 + brandScope.row,
  358 + r,
  359 + er
  360 + )
  361 + "
  362 + @sort-change="
  363 + ev =>
  364 + onProductSort(
  365 + catScope.row,
  366 + brandScope.row,
  367 + ev
  368 + )
  369 + "
  370 + >
  371 + <el-table-column type="expand" width="42">
  372 + <template slot-scope="prodScope">
  373 + <div
  374 + v-loading="
  375 + lineLoading[
  376 + lineKey(
  377 + catScope.row,
  378 + brandScope.row,
  379 + prodScope.row
  380 + )
  381 + ]
  382 + "
  383 + class="nested-wrap detail-wrap"
  384 + >
  385 + <div
  386 + class="nested-table-host nested-table-host--detail"
  387 + >
  388 + <el-table
  389 + :data="
  390 + (
  391 + lineMap[
  392 + lineKey(
  393 + catScope.row,
  394 + brandScope.row,
  395 + prodScope.row
  396 + )
  397 + ] || {}
  398 + ).list || []
  399 + "
  400 + border
  401 + size="mini"
  402 + class="nested-table nested-table--scroll"
  403 + :row-key="
  404 + (r, idx) =>
  405 + lineDetailRowKey(
  406 + 'mx',
  407 + catScope.row,
  408 + brandScope.row,
  409 + prodScope.row,
  410 + r,
  411 + idx
  412 + )
  413 + "
  414 + @sort-change="
  415 + ev =>
  416 + onLineSort(
  417 + catScope.row,
  418 + brandScope.row,
  419 + prodScope.row,
  420 + ev
  421 + )
  422 + "
  423 + >
  424 + <el-table-column
  425 + type="index"
  426 + label="行号"
  427 + width="52"
  428 + align="center"
  429 + />
  430 + <el-table-column
  431 + prop="单据日期"
  432 + label="单据日期"
  433 + width="108"
  434 + show-overflow-tooltip
  435 + sortable="custom"
  436 + />
  437 + <el-table-column
  438 + prop="单据编号"
  439 + label="单据编号"
  440 + min-width="140"
  441 + show-overflow-tooltip
  442 + sortable="custom"
  443 + />
  444 + <el-table-column
  445 + prop="单据类型"
  446 + label="单据类型"
  447 + width="108"
  448 + show-overflow-tooltip
  449 + sortable="custom"
  450 + />
  451 + <el-table-column
  452 + prop="往来单位"
  453 + label="往来单位"
  454 + min-width="120"
  455 + show-overflow-tooltip
  456 + sortable="custom"
  457 + />
  458 + <el-table-column
  459 + prop="经手人"
  460 + label="经手人"
  461 + width="88"
  462 + show-overflow-tooltip
  463 + sortable="custom"
  464 + />
  465 + <el-table-column
  466 + prop="仓库名称"
  467 + label="仓库名称"
  468 + min-width="100"
  469 + show-overflow-tooltip
  470 + sortable="custom"
  471 + />
  472 + <el-table-column
  473 + prop="商品名称"
  474 + label="商品名称"
  475 + min-width="140"
  476 + show-overflow-tooltip
  477 + sortable="custom"
  478 + />
  479 + <el-table-column
  480 + prop="数量"
  481 + label="数量"
  482 + width="88"
  483 + align="right"
  484 + sortable="custom"
  485 + >
  486 + <template slot-scope="s">{{
  487 + formatQty(s.row["数量"])
  488 + }}</template>
  489 + </el-table-column>
  490 + <el-table-column
  491 + prop="入库单价"
  492 + label="入库单价"
  493 + width="100"
  494 + align="right"
  495 + sortable="custom"
  496 + >
  497 + <template slot-scope="s">{{
  498 + formatMoney(s.row["入库单价"])
  499 + }}</template>
  500 + </el-table-column>
  501 + <el-table-column
  502 + prop="采购金额"
  503 + label="采购金额"
  504 + width="108"
  505 + align="right"
  506 + sortable="custom"
  507 + >
  508 + <template slot-scope="s">{{
  509 + formatMoney(s.row["采购金额"])
  510 + }}</template>
  511 + </el-table-column>
  512 + </el-table>
  513 + </div>
  514 + <div
  515 + v-if="
  516 + linePager[
  517 + lineKey(
  518 + catScope.row,
  519 + brandScope.row,
  520 + prodScope.row
  521 + )
  522 + ]
  523 + "
  524 + class="mini-pager"
  525 + >
  526 + <el-pagination
  527 + small
  528 + layout="prev, pager, next, total"
  529 + :total="
  530 + linePager[
  531 + lineKey(
  532 + catScope.row,
  533 + brandScope.row,
  534 + prodScope.row
  535 + )
  536 + ].total
  537 + "
  538 + :page-size="
  539 + linePager[
  540 + lineKey(
  541 + catScope.row,
  542 + brandScope.row,
  543 + prodScope.row
  544 + )
  545 + ].pageSize
  546 + "
  547 + :current-page="
  548 + linePager[
  549 + lineKey(
  550 + catScope.row,
  551 + brandScope.row,
  552 + prodScope.row
  553 + )
  554 + ].currentPage
  555 + "
  556 + @current-change="
  557 + p =>
  558 + fetchLines(
  559 + catScope.row,
  560 + brandScope.row,
  561 + prodScope.row,
  562 + p
  563 + )
  564 + "
  565 + />
  566 + </div>
  567 + </div>
  568 + </template>
  569 + </el-table-column>
  570 + <el-table-column
  571 + prop="商品编号"
  572 + label="商品编号"
  573 + min-width="100"
  574 + show-overflow-tooltip
  575 + sortable="custom"
  576 + >
  577 + <template slot-scope="s">
  578 + <i
  579 + class="el-icon-postcard row-ico row-ico--primary"
222 /> 580 />
223 - </div>  
224 - </div>  
225 - </template>  
226 - </el-table-column>  
227 - <el-table-column label="商品编号" min-width="100" show-overflow-tooltip sortable :sort-method="sortTextCol('商品编号')">  
228 - <template slot-scope="s">  
229 - <i class="el-icon-postcard row-ico row-ico--primary" />  
230 - {{ cellText(s.row['商品编号']) }}  
231 - </template>  
232 - </el-table-column>  
233 - <el-table-column label="明细分类" min-width="120" show-overflow-tooltip sortable :sort-method="sortTextCol('明细分类')">  
234 - <template slot-scope="s">  
235 - <i class="el-icon-collection-tag row-ico row-ico--info" />  
236 - {{ cellText(s.row['明细分类']) }}  
237 - </template>  
238 - </el-table-column>  
239 - <el-table-column label="商品名称" min-width="140" show-overflow-tooltip sortable :sort-method="sortTextCol('商品名称')">  
240 - <template slot-scope="s">  
241 - <i class="el-icon-goods row-ico row-ico--muted" />  
242 - {{ cellText(s.row['商品名称']) }}  
243 - </template>  
244 - </el-table-column>  
245 - <el-table-column label="数量" width="88" align="right" sortable :sort-method="sortNumCol('数量')">  
246 - <template slot-scope="s">{{ formatQty(s.row['数量']) }}</template>  
247 - </el-table-column>  
248 - <el-table-column label="入库单价" width="100" align="right" sortable :sort-method="sortNumCol('入库单价')">  
249 - <template slot-scope="s">{{ formatMoney(s.row['入库单价']) }}</template>  
250 - </el-table-column>  
251 - <el-table-column label="采购金额" width="108" align="right" sortable :sort-method="sortNumCol('采购金额')">  
252 - <template slot-scope="s">{{ formatMoney(s.row['采购金额']) }}</template>  
253 - </el-table-column>  
254 - </el-table>  
255 - </div>  
256 - </template>  
257 - </el-table-column>  
258 - <el-table-column label="品牌" min-width="140" show-overflow-tooltip sortable :sort-method="sortTextCol('品牌名称')">  
259 - <template slot-scope="s">  
260 - <i class="el-icon-medal row-ico row-ico--primary" />  
261 - {{ cellText(s.row['品牌名称'] || s.row['商品名称']) }}  
262 - </template>  
263 - </el-table-column>  
264 - <el-table-column label="数量" width="88" align="right" sortable :sort-method="sortNumCol('数量')">  
265 - <template slot-scope="s">{{ formatQty(s.row['数量']) }}</template>  
266 - </el-table-column>  
267 - <el-table-column label="入库单价" width="100" align="right" sortable :sort-method="sortNumCol('入库单价')">  
268 - <template slot-scope="s">{{ formatMoney(s.row['入库单价']) }}</template>  
269 - </el-table-column>  
270 - <el-table-column label="采购金额" width="108" align="right" sortable :sort-method="sortNumCol('采购金额')">  
271 - <template slot-scope="s">{{ formatMoney(s.row['采购金额']) }}</template>  
272 - </el-table-column>  
273 - </el-table>  
274 - <div v-if="!(brandMap[categoryRowKey(catScope.row)] || []).length && !brandLoading[categoryRowKey(catScope.row)]" class="nested-empty">暂无品牌数据</div> 581 + {{ cellText(s.row["商品编号"]) }}
  582 + </template>
  583 + </el-table-column>
  584 + <el-table-column
  585 + prop="明细分类"
  586 + label="明细分类"
  587 + min-width="120"
  588 + show-overflow-tooltip
  589 + sortable="custom"
  590 + >
  591 + <template slot-scope="s">
  592 + <i
  593 + class="el-icon-collection-tag row-ico row-ico--info"
  594 + />
  595 + {{ cellText(s.row["明细分类"]) }}
  596 + </template>
  597 + </el-table-column>
  598 + <el-table-column
  599 + prop="商品名称"
  600 + label="商品名称"
  601 + min-width="140"
  602 + show-overflow-tooltip
  603 + sortable="custom"
  604 + >
  605 + <template slot-scope="s">
  606 + <i
  607 + class="el-icon-goods row-ico row-ico--muted"
  608 + />
  609 + {{ cellText(s.row["商品名称"]) }}
  610 + </template>
  611 + </el-table-column>
  612 + <el-table-column
  613 + prop="数量"
  614 + label="数量"
  615 + width="88"
  616 + align="right"
  617 + sortable="custom"
  618 + >
  619 + <template slot-scope="s">{{
  620 + formatQty(s.row["数量"])
  621 + }}</template>
  622 + </el-table-column>
  623 + <el-table-column
  624 + prop="入库单价"
  625 + label="入库单价"
  626 + width="100"
  627 + align="right"
  628 + sortable="custom"
  629 + >
  630 + <template slot-scope="s">{{
  631 + formatMoney(s.row["入库单价"])
  632 + }}</template>
  633 + </el-table-column>
  634 + <el-table-column
  635 + prop="采购金额"
  636 + label="采购金额"
  637 + width="108"
  638 + align="right"
  639 + sortable="custom"
  640 + >
  641 + <template slot-scope="s">{{
  642 + formatMoney(s.row["采购金额"])
  643 + }}</template>
  644 + </el-table-column>
  645 + </el-table>
  646 + </div>
  647 + </div>
  648 + </template>
  649 + </el-table-column>
  650 + <el-table-column
  651 + prop="品牌名称"
  652 + label="品牌"
  653 + min-width="140"
  654 + show-overflow-tooltip
  655 + sortable="custom"
  656 + >
  657 + <template slot-scope="s">
  658 + <i class="el-icon-medal row-ico row-ico--primary" />
  659 + {{ cellText(s.row["品牌名称"] || s.row["商品名称"]) }}
  660 + </template>
  661 + </el-table-column>
  662 + <el-table-column
  663 + prop="数量"
  664 + label="数量"
  665 + width="88"
  666 + align="right"
  667 + sortable="custom"
  668 + >
  669 + <template slot-scope="s">{{
  670 + formatQty(s.row["数量"])
  671 + }}</template>
  672 + </el-table-column>
  673 + <el-table-column
  674 + prop="入库单价"
  675 + label="入库单价"
  676 + width="100"
  677 + align="right"
  678 + sortable="custom"
  679 + >
  680 + <template slot-scope="s">{{
  681 + formatMoney(s.row["入库单价"])
  682 + }}</template>
  683 + </el-table-column>
  684 + <el-table-column
  685 + prop="采购金额"
  686 + label="采购金额"
  687 + width="108"
  688 + align="right"
  689 + sortable="custom"
  690 + >
  691 + <template slot-scope="s">{{
  692 + formatMoney(s.row["采购金额"])
  693 + }}</template>
  694 + </el-table-column>
  695 + </el-table>
  696 + </div>
  697 + <div
  698 + v-if="
  699 + !(brandMap[categoryRowKey(catScope.row)] || []).length &&
  700 + !brandLoading[categoryRowKey(catScope.row)]
  701 + "
  702 + class="nested-empty"
  703 + >
  704 + 暂无品牌数据
  705 + </div>
275 </div> 706 </div>
276 </template> 707 </template>
277 </el-table-column> 708 </el-table-column>
278 - <el-table-column type="index" label="行号" width="56" align="center" />  
279 - <el-table-column label="分类名称" min-width="160" show-overflow-tooltip sortable :sort-method="sortTextCol('分类名称')"> 709 + <el-table-column
  710 + type="index"
  711 + label="行号"
  712 + width="56"
  713 + align="center"
  714 + />
  715 + <el-table-column
  716 + prop="分类名称"
  717 + label="分类名称"
  718 + min-width="160"
  719 + show-overflow-tooltip
  720 + sortable="custom"
  721 + >
280 <template slot-scope="scope"> 722 <template slot-scope="scope">
281 <i class="el-icon-folder-opened row-ico row-ico--primary" /> 723 <i class="el-icon-folder-opened row-ico row-ico--primary" />
282 - {{ cellText(scope.row['分类名称']) }} 724 + {{ cellText(scope.row["分类名称"]) }}
283 </template> 725 </template>
284 </el-table-column> 726 </el-table-column>
285 - <el-table-column label="数量" width="96" align="right" sortable :sort-method="sortNumCol('数量')">  
286 - <template slot-scope="scope">{{ formatQty(scope.row['数量']) }}</template> 727 + <el-table-column
  728 + prop="数量"
  729 + label="数量"
  730 + width="96"
  731 + align="right"
  732 + sortable="custom"
  733 + >
  734 + <template slot-scope="scope">{{
  735 + formatQty(scope.row["数量"])
  736 + }}</template>
287 </el-table-column> 737 </el-table-column>
288 - <el-table-column label="入库单价" width="104" align="right" sortable :sort-method="sortNumCol('入库单价')">  
289 - <template slot-scope="scope">{{ formatMoney(scope.row['入库单价']) }}</template> 738 + <el-table-column
  739 + prop="入库单价"
  740 + label="入库单价"
  741 + width="104"
  742 + align="right"
  743 + sortable="custom"
  744 + >
  745 + <template slot-scope="scope">{{
  746 + formatMoney(scope.row["入库单价"])
  747 + }}</template>
290 </el-table-column> 748 </el-table-column>
291 - <el-table-column label="采购金额" width="112" align="right" sortable :sort-method="sortNumCol('采购金额')">  
292 - <template slot-scope="scope">{{ formatMoney(scope.row['采购金额']) }}</template> 749 + <el-table-column
  750 + prop="采购金额"
  751 + label="采购金额"
  752 + width="112"
  753 + align="right"
  754 + sortable="custom"
  755 + >
  756 + <template slot-scope="scope">{{
  757 + formatMoney(scope.row["采购金额"])
  758 + }}</template>
293 </el-table-column> 759 </el-table-column>
294 <el-table-column label="操作" width="120" align="left" fixed="right"> 760 <el-table-column label="操作" width="120" align="left" fixed="right">
295 <template slot-scope="scope"> 761 <template slot-scope="scope">
296 - <el-button type="text" size="mini" icon="el-icon-tickets" @click.stop="openLinearDialog(scope.row)">线性列表</el-button> 762 + <el-button
  763 + type="text"
  764 + size="mini"
  765 + icon="el-icon-tickets"
  766 + @click.stop="openLinearDialog(scope.row)"
  767 + >线性列表</el-button
  768 + >
297 </template> 769 </template>
298 </el-table-column> 770 </el-table-column>
299 </el-table> 771 </el-table>
@@ -302,7 +774,9 @@ @@ -302,7 +774,9 @@
302 <i class="el-icon-s-data row-ico--primary" /> 774 <i class="el-icon-s-data row-ico--primary" />
303 <span>合计(当前分类列表)</span> 775 <span>合计(当前分类列表)</span>
304 <span class="sum-item">数量:{{ formatQty(sumCategoryQty) }}</span> 776 <span class="sum-item">数量:{{ formatQty(sumCategoryQty) }}</span>
305 - <span class="sum-item">采购金额:{{ formatMoney(sumCategoryAmt) }}</span> 777 + <span class="sum-item"
  778 + >采购金额:{{ formatMoney(sumCategoryAmt) }}</span
  779 + >
306 </div> 780 </div>
307 </div> 781 </div>
308 </div> 782 </div>
@@ -318,23 +792,91 @@ @@ -318,23 +792,91 @@
318 > 792 >
319 <div v-loading="linearLoading" class="linear-dialog-body"> 793 <div v-loading="linearLoading" class="linear-dialog-body">
320 <p class="linear-hint">{{ linearHint }}</p> 794 <p class="linear-hint">{{ linearHint }}</p>
321 - <el-table :data="linearRows" border size="small" max-height="520" class="linear-table"> 795 + <el-table
  796 + :data="linearRows"
  797 + border
  798 + size="small"
  799 + max-height="520"
  800 + class="linear-table"
  801 + >
322 <el-table-column type="index" label="行号" width="52" /> 802 <el-table-column type="index" label="行号" width="52" />
323 - <el-table-column prop="单据日期" label="单据日期" width="100" show-overflow-tooltip sortable :sort-method="sortTextCol('单据日期')" />  
324 - <el-table-column prop="单据编号" label="单据编号" min-width="130" show-overflow-tooltip sortable :sort-method="sortTextCol('单据编号')" />  
325 - <el-table-column prop="单据类型" label="单据类型" width="100" show-overflow-tooltip />  
326 - <el-table-column prop="往来单位" label="往来单位" min-width="110" show-overflow-tooltip />  
327 - <el-table-column prop="经手人" label="经手人" width="80" show-overflow-tooltip />  
328 - <el-table-column prop="仓库名称" label="仓库名称" min-width="96" show-overflow-tooltip />  
329 - <el-table-column prop="商品名称" label="商品名称" min-width="120" show-overflow-tooltip />  
330 - <el-table-column prop="数量" label="数量" width="80" align="right" sortable :sort-method="sortNumCol('数量')">  
331 - <template slot-scope="s">{{ formatQty(s.row['数量']) }}</template> 803 + <el-table-column
  804 + prop="单据日期"
  805 + label="单据日期"
  806 + width="100"
  807 + show-overflow-tooltip
  808 + sortable
  809 + :sort-method="sortTextCol('单据日期')"
  810 + />
  811 + <el-table-column
  812 + prop="单据编号"
  813 + label="单据编号"
  814 + min-width="130"
  815 + show-overflow-tooltip
  816 + sortable
  817 + :sort-method="sortTextCol('单据编号')"
  818 + />
  819 + <el-table-column
  820 + prop="单据类型"
  821 + label="单据类型"
  822 + width="100"
  823 + show-overflow-tooltip
  824 + />
  825 + <el-table-column
  826 + prop="往来单位"
  827 + label="往来单位"
  828 + min-width="110"
  829 + show-overflow-tooltip
  830 + />
  831 + <el-table-column
  832 + prop="经手人"
  833 + label="经手人"
  834 + width="80"
  835 + show-overflow-tooltip
  836 + />
  837 + <el-table-column
  838 + prop="仓库名称"
  839 + label="仓库名称"
  840 + min-width="96"
  841 + show-overflow-tooltip
  842 + />
  843 + <el-table-column
  844 + prop="商品名称"
  845 + label="商品名称"
  846 + min-width="120"
  847 + show-overflow-tooltip
  848 + />
  849 + <el-table-column
  850 + prop="数量"
  851 + label="数量"
  852 + width="80"
  853 + align="right"
  854 + sortable
  855 + :sort-method="sortNumCol('数量')"
  856 + >
  857 + <template slot-scope="s">{{ formatQty(s.row["数量"]) }}</template>
332 </el-table-column> 858 </el-table-column>
333 - <el-table-column prop="入库单价" label="入库单价" width="92" align="right">  
334 - <template slot-scope="s">{{ formatMoney(s.row['入库单价']) }}</template> 859 + <el-table-column
  860 + prop="入库单价"
  861 + label="入库单价"
  862 + width="92"
  863 + align="right"
  864 + >
  865 + <template slot-scope="s">{{
  866 + formatMoney(s.row["入库单价"])
  867 + }}</template>
335 </el-table-column> 868 </el-table-column>
336 - <el-table-column prop="采购金额" label="采购金额" width="100" align="right" sortable :sort-method="sortNumCol('采购金额')">  
337 - <template slot-scope="s">{{ formatMoney(s.row['采购金额']) }}</template> 869 + <el-table-column
  870 + prop="采购金额"
  871 + label="采购金额"
  872 + width="100"
  873 + align="right"
  874 + sortable
  875 + :sort-method="sortNumCol('采购金额')"
  876 + >
  877 + <template slot-scope="s">{{
  878 + formatMoney(s.row["采购金额"])
  879 + }}</template>
338 </el-table-column> 880 </el-table-column>
339 </el-table> 881 </el-table>
340 </div> 882 </div>
@@ -343,13 +885,23 @@ @@ -343,13 +885,23 @@
343 </template> 885 </template>
344 886
345 <script> 887 <script>
346 -import request from '@/utils/request'  
347 -import { previewDataInterface } from '@/api/systemData/dataInterface' 888 +import request from "@/utils/request";
  889 +import { previewDataInterface } from "@/api/systemData/dataInterface";
348 890
349 -const DEFAULT_BILL_TYPES = ['采购入库单', '采购退货单'] 891 +const BILL_TYPE_SUGGEST = [
  892 + "采购入库单",
  893 + "采购退货单",
  894 + "销售出库单",
  895 + "销售退货单",
  896 + "预售出库单",
  897 + "预售退货单",
  898 + "委托代销发货单",
  899 + "委托代销退货单",
  900 + "委托代销结算单"
  901 +];
350 902
351 export default { 903 export default {
352 - name: 'PurchaseSummary', 904 + name: "PurchaseSummary",
353 data() { 905 data() {
354 return { 906 return {
355 categoryList: [], 907 categoryList: [],
@@ -357,102 +909,127 @@ export default { @@ -357,102 +909,127 @@ export default {
357 brandMap: {}, 909 brandMap: {},
358 brandLoading: {}, 910 brandLoading: {},
359 productMap: {}, 911 productMap: {},
360 - productLoading: {}, 912 + aggProductLoading: {},
361 lineMap: {}, 913 lineMap: {},
362 linePager: {}, 914 linePager: {},
363 lineLoading: {}, 915 lineLoading: {},
364 - billTypeOptions: [...DEFAULT_BILL_TYPES], 916 + lineSort: {},
  917 + billTypeOptions: [...BILL_TYPE_SUGGEST],
  918 + linearInlineMode: false,
  919 + linearInlineMap: {},
  920 + linearInlineLoading: {},
365 filters: { 921 filters: {
366 dateRange: [], 922 dateRange: [],
367 contactUnit: [], 923 contactUnit: [],
368 agent: [], 924 agent: [],
369 - productSpId: '', 925 + productSpId: "",
370 warehouse: [], 926 warehouse: [],
371 - billType: [...DEFAULT_BILL_TYPES] 927 + billType: []
372 }, 928 },
373 contactUnitOptions: [], 929 contactUnitOptions: [],
374 agentOptions: [], 930 agentOptions: [],
375 warehouseOptions: [], 931 warehouseOptions: [],
376 productOptions: [], 932 productOptions: [],
377 - productLoading: false, 933 + productRemoteLoading: false,
378 linearVisible: false, 934 linearVisible: false,
379 linearLoading: false, 935 linearLoading: false,
380 linearRows: [], 936 linearRows: [],
381 - linearHint: ''  
382 - } 937 + linearHint: ""
  938 + };
383 }, 939 },
384 computed: { 940 computed: {
385 sumCategoryQty() { 941 sumCategoryQty() {
386 - return this.categoryList.reduce((s, r) => s + this.parseNum(r['数量']), 0) 942 + return this.categoryList.reduce(
  943 + (s, r) => s + this.parseNum(r["数量"]),
  944 + 0
  945 + );
387 }, 946 },
388 sumCategoryAmt() { 947 sumCategoryAmt() {
389 - return this.categoryList.reduce((s, r) => s + this.parseNum(r['采购金额']), 0) 948 + return this.categoryList.reduce(
  949 + (s, r) => s + this.parseNum(r["采购金额"]),
  950 + 0
  951 + );
390 } 952 }
391 }, 953 },
392 created() { 954 created() {
393 - this.loadFilterOptions()  
394 - this.fetchCategories() 955 + this.loadFilterOptions();
  956 + this.fetchCategories();
395 }, 957 },
396 methods: { 958 methods: {
  959 + /** 表格 row-key(带前缀,避免嵌套表展开后 key 冲突) */
397 categoryRowKey(row) { 960 categoryRowKey(row) {
398 - return String(row['分类Id'] != null ? row['分类Id'] : '') 961 + return `ps-cat:${String(row["分类Id"] != null ? row["分类Id"] : "")}`;
399 }, 962 },
400 brandRowKey(catRow, brandRow) { 963 brandRowKey(catRow, brandRow) {
401 - return `${this.categoryRowKey(catRow)}|${String(brandRow['品牌Id'] != null ? brandRow['品牌Id'] : '')}` 964 + return `${this.categoryRowKey(catRow)}|br:${String(
  965 + brandRow["品牌Id"] != null ? brandRow["品牌Id"] : "_"
  966 + )}`;
402 }, 967 },
403 productRowKey(catRow, brandRow, prodRow) { 968 productRowKey(catRow, brandRow, prodRow) {
404 - return `${this.brandRowKey(catRow, brandRow)}|${String(prodRow['商品Id'] != null ? prodRow['商品Id'] : '')}` 969 + return `${this.brandRowKey(catRow, brandRow)}|sp:${String(
  970 + prodRow["商品Id"] != null ? prodRow["商品Id"] : ""
  971 + )}`;
405 }, 972 },
406 lineKey(catRow, brandRow, prodRow) { 973 lineKey(catRow, brandRow, prodRow) {
407 - return this.productRowKey(catRow, brandRow, prodRow) 974 + return this.productRowKey(catRow, brandRow, prodRow);
408 }, 975 },
409 parseNum(val) { 976 parseNum(val) {
410 - if (val == null || val === '') return 0  
411 - const n = Number(String(val).replace(/,/g, '').trim())  
412 - return Number.isFinite(n) ? n : 0 977 + if (val == null || val === "") return 0;
  978 + const n = Number(
  979 + String(val)
  980 + .replace(/,/g, "")
  981 + .trim()
  982 + );
  983 + return Number.isFinite(n) ? n : 0;
413 }, 984 },
414 sortNumCol(prop) { 985 sortNumCol(prop) {
415 - return (a, b) => this.parseNum(a[prop]) - this.parseNum(b[prop]) 986 + return (a, b) => this.parseNum(a[prop]) - this.parseNum(b[prop]);
416 }, 987 },
417 sortTextCol(prop) { 988 sortTextCol(prop) {
418 return (a, b) => { 989 return (a, b) => {
419 - const sa = String(a[prop] != null ? a[prop] : '').trim()  
420 - const sb = String(b[prop] != null ? b[prop] : '').trim()  
421 - return sa.localeCompare(sb, 'zh-CN')  
422 - } 990 + const sa = String(a[prop] != null ? a[prop] : "").trim();
  991 + const sb = String(b[prop] != null ? b[prop] : "").trim();
  992 + return sa.localeCompare(sb, "zh-CN");
  993 + };
423 }, 994 },
424 cellText(v) { 995 cellText(v) {
425 - if (v == null || String(v).trim() === '') return '无'  
426 - return String(v) 996 + if (v == null || String(v).trim() === "") return "无";
  997 + return String(v);
427 }, 998 },
428 formatQty(val) { 999 formatQty(val) {
429 - if (val === null || val === undefined || val === '') return '无'  
430 - const n = Number(val)  
431 - if (Number.isNaN(n)) return String(val)  
432 - return n.toLocaleString('zh-CN', { minimumFractionDigits: 0, maximumFractionDigits: 4 }) 1000 + if (val === null || val === undefined || val === "") return "无";
  1001 + const n = Number(val);
  1002 + if (Number.isNaN(n)) return String(val);
  1003 + return n.toLocaleString("zh-CN", {
  1004 + minimumFractionDigits: 0,
  1005 + maximumFractionDigits: 4
  1006 + });
433 }, 1007 },
434 formatMoney(val) { 1008 formatMoney(val) {
435 - if (val === null || val === undefined || val === '') return '无'  
436 - const n = Number(val)  
437 - if (Number.isNaN(n)) return String(val)  
438 - return n.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 4 }) 1009 + if (val === null || val === undefined || val === "") return "无";
  1010 + const n = Number(val);
  1011 + if (Number.isNaN(n)) return String(val);
  1012 + return n.toLocaleString("zh-CN", {
  1013 + minimumFractionDigits: 2,
  1014 + maximumFractionDigits: 4
  1015 + });
439 }, 1016 },
440 /** 商品编码(空则返回空串,便于单行展示) */ 1017 /** 商品编码(空则返回空串,便于单行展示) */
441 productCodeRaw(item) { 1018 productCodeRaw(item) {
442 - if (!item) return '' 1019 + if (!item) return "";
443 const c = 1020 const c =
444 item.F_Spbm || 1021 item.F_Spbm ||
445 item.spbm || 1022 item.spbm ||
446 item.Spbm || 1023 item.Spbm ||
447 item.商品编号 || 1024 item.商品编号 ||
448 item.code || 1025 item.code ||
449 - ''  
450 - const t = c != null && String(c).trim() !== '' ? String(c).trim() : ''  
451 - return t 1026 + "";
  1027 + const t = c != null && String(c).trim() !== "" ? String(c).trim() : "";
  1028 + return t;
452 }, 1029 },
453 /** 商品名称 */ 1030 /** 商品名称 */
454 productNameRaw(item) { 1031 productNameRaw(item) {
455 - if (!item) return '' 1032 + if (!item) return "";
456 const n = 1033 const n =
457 item.F_Spmc || 1034 item.F_Spmc ||
458 item.spmc || 1035 item.spmc ||
@@ -460,271 +1037,454 @@ export default { @@ -460,271 +1037,454 @@ export default {
460 item.productName || 1037 item.productName ||
461 item.商品名称 || 1038 item.商品名称 ||
462 item.name || 1039 item.name ||
463 - ''  
464 - const t = n != null && String(n).trim() !== '' ? String(n).trim() : ''  
465 - return t 1040 + "";
  1041 + const t = n != null && String(n).trim() !== "" ? String(n).trim() : "";
  1042 + return t;
466 }, 1043 },
467 /** 选中后在输入框内显示的完整文案(编码 + 名称) */ 1044 /** 选中后在输入框内显示的完整文案(编码 + 名称) */
468 formatProductLabel(item) { 1045 formatProductLabel(item) {
469 - if (!item) return ''  
470 - const code = this.productCodeRaw(item)  
471 - const name = this.productNameRaw(item)  
472 - if (code && name) return `${code} | ${name}`  
473 - if (name) return name  
474 - if (code) return code  
475 - const id = item.F_Id || item.id  
476 - return id ? String(id) : '无' 1046 + if (!item) return "";
  1047 + const code = this.productCodeRaw(item);
  1048 + const name = this.productNameRaw(item);
  1049 + if (code && name) return `${code} | ${name}`;
  1050 + if (name) return name;
  1051 + if (code) return code;
  1052 + const id = this.productOptionValue(item);
  1053 + return id ? String(id) : "无";
  1054 + },
  1055 + productOptionValue(item) {
  1056 + if (!item) return "";
  1057 + const v = item.id || item.F_Id || item.f_Id;
  1058 + return v != null && String(v).trim() !== "" ? String(v).trim() : "";
  1059 + },
  1060 + normalizeArrayPayload(data) {
  1061 + if (Array.isArray(data)) return data;
  1062 + if (data && Array.isArray(data.list)) return data.list;
  1063 + // 兼容:{ code, msg, data: { list: [...] } }
  1064 + if (data && data.data && Array.isArray(data.data.list))
  1065 + return data.data.list;
  1066 + if (data && Array.isArray(data.data)) return data.data;
  1067 + if (data && Array.isArray(data.records)) return data.records;
  1068 + return [];
  1069 + },
  1070 + lineDetailRowKey(prefix, catRow, brandRow, prodRow, lineRow, idx) {
  1071 + const pk = prodRow
  1072 + ? this.lineKey(catRow, brandRow, prodRow)
  1073 + : this.categoryRowKey(catRow);
  1074 + const doc =
  1075 + lineRow && lineRow["单据编号"] != null
  1076 + ? String(lineRow["单据编号"])
  1077 + : "";
  1078 + const nm =
  1079 + lineRow && lineRow["商品名称"] != null
  1080 + ? String(lineRow["商品名称"]).slice(0, 48)
  1081 + : "";
  1082 + return `${prefix}|${pk}|i${idx}|${doc}|${nm}`;
477 }, 1083 },
478 buildBasePayload() { 1084 buildBasePayload() {
479 - const payload = {} 1085 + const payload = {};
480 if (this.filters.dateRange && this.filters.dateRange.length === 2) { 1086 if (this.filters.dateRange && this.filters.dateRange.length === 2) {
481 - payload.startDate = this.filters.dateRange[0]  
482 - payload.endDate = this.filters.dateRange[1] 1087 + payload.startDate = this.filters.dateRange[0];
  1088 + payload.endDate = this.filters.dateRange[1];
483 } 1089 }
484 - if (this.filters.contactUnit.length) payload.contactUnit = this.filters.contactUnit.join(',')  
485 - if (this.filters.agent.length) payload.agent = this.filters.agent.join(',')  
486 - if (this.filters.warehouse.length) payload.warehouse = this.filters.warehouse.join(',')  
487 - if (this.filters.productSpId) payload.productSpId = this.filters.productSpId  
488 - const bt = this.filters.billType || []  
489 - if (bt.length > 0 && bt.length < this.billTypeOptions.length) {  
490 - payload.billType = bt.join(',')  
491 - } else {  
492 - payload.billType = '' 1090 + if (this.filters.contactUnit.length)
  1091 + payload.contactUnit = this.filters.contactUnit.join(",");
  1092 + if (this.filters.agent.length) {
  1093 + const a = this.filters.agent
  1094 + .map(x => String(x).trim())
  1095 + .filter(Boolean)
  1096 + .join(",");
  1097 + if (a) payload.agent = a;
493 } 1098 }
494 - return payload 1099 + if (this.filters.warehouse.length)
  1100 + payload.warehouse = this.filters.warehouse.join(",");
  1101 + if (this.filters.productSpId)
  1102 + payload.productSpId = this.filters.productSpId;
  1103 + const bt = this.filters.billType || [];
  1104 + if (bt.length)
  1105 + payload.billType = bt
  1106 + .map(x => String(x).trim())
  1107 + .filter(Boolean)
  1108 + .join(",");
  1109 + return payload;
495 }, 1110 },
496 async loadFilterOptions() { 1111 async loadFilterOptions() {
497 try { 1112 try {
498 const companyRes = await request({ 1113 const companyRes = await request({
499 - url: '/api/Extend/WtWldw',  
500 - method: 'GET', 1114 + url: "/api/Extend/WtWldw",
  1115 + method: "GET",
501 data: { pageSize: 1000, currentPage: 1 } 1116 data: { pageSize: 1000, currentPage: 1 }
502 - })  
503 - const companyList = companyRes.data.list || companyRes.data || [] 1117 + });
  1118 + const companyList = companyRes.data.list || companyRes.data || [];
504 this.contactUnitOptions = companyList 1119 this.contactUnitOptions = companyList
505 .map(x => ({ 1120 .map(x => ({
506 label: x.dwmc || x.F_dwmc || x.id, 1121 label: x.dwmc || x.F_dwmc || x.id,
507 value: x.id || x.F_Id 1122 value: x.id || x.F_Id
508 })) 1123 }))
509 - .filter(x => x.value) 1124 + .filter(x => x.value);
510 1125
511 const warehouseRes = await request({ 1126 const warehouseRes = await request({
512 - url: '/api/Extend/WtCk',  
513 - method: 'GET', 1127 + url: "/api/Extend/WtCk",
  1128 + method: "GET",
514 data: { pageSize: 1000, currentPage: 1 } 1129 data: { pageSize: 1000, currentPage: 1 }
515 - })  
516 - const warehouseList = warehouseRes.data.list || warehouseRes.data || [] 1130 + });
  1131 + const warehouseList = warehouseRes.data.list || warehouseRes.data || [];
517 this.warehouseOptions = warehouseList.map(x => ({ 1132 this.warehouseOptions = warehouseList.map(x => ({
518 label: x.mdmc || x.F_mdmc || x.id, 1133 label: x.mdmc || x.F_mdmc || x.id,
519 value: x.id || x.F_Id 1134 value: x.id || x.F_Id
520 - })) 1135 + }));
521 1136
522 - const userRes = await previewDataInterface('675937572047815941')  
523 - const userList = userRes.data || []  
524 - this.agentOptions = (userList || []) 1137 + const userRes = await previewDataInterface("675937572047815941");
  1138 + const userList = this.normalizeArrayPayload(userRes.data);
  1139 + this.agentOptions = userList
525 .map(x => ({ 1140 .map(x => ({
526 - label: x.realName || x.F_RealName || x.fullName || x.name || x.F_Account || x.account || '',  
527 - value: x.id || x.F_Id || x.userId 1141 + label: String(
  1142 + x.realName ||
  1143 + x.F_RealName ||
  1144 + x.fullName ||
  1145 + x.name ||
  1146 + x.F_Account ||
  1147 + x.account ||
  1148 + x.f_RealName ||
  1149 + ""
  1150 + ).trim(),
  1151 + value: String(x.id || x.F_Id || x.userId || x.f_Id || "").trim()
528 })) 1152 }))
529 - .filter(x => x.value && x.label) 1153 + .filter(x => x.value && x.label);
530 } catch (e) { 1154 } catch (e) {
531 - console.error('采购汇总筛选下拉加载失败', e) 1155 + console.error("采购汇总筛选下拉加载失败", e);
532 } 1156 }
533 }, 1157 },
534 async handleProductSearch(query) { 1158 async handleProductSearch(query) {
535 if (!query) { 1159 if (!query) {
536 - this.productOptions = []  
537 - return 1160 + this.productOptions = [];
  1161 + return;
538 } 1162 }
539 - this.productLoading = true 1163 + this.productRemoteLoading = true;
540 try { 1164 try {
541 const res = await request({ 1165 const res = await request({
542 - url: '/api/Extend/WtSp/GetListByKeyword',  
543 - method: 'GET',  
544 - data: { pageSize: 200, currentPage: 1, keyword: query }  
545 - })  
546 - this.productOptions = res.data.list || res.data || [] 1166 + url: "/api/Extend/WtSp/GetListByKeyword",
  1167 + method: "GET",
  1168 + data: { pageSize: 200, currentPage: 1, keyword: String(query).trim() }
  1169 + });
  1170 + this.productOptions = this.normalizeArrayPayload(res.data);
547 } catch (e) { 1171 } catch (e) {
548 - console.error('采购汇总商品搜索失败', e) 1172 + console.error("采购汇总商品搜索失败", e);
549 } finally { 1173 } finally {
550 - this.productLoading = false 1174 + this.productRemoteLoading = false;
551 } 1175 }
552 }, 1176 },
553 clearTreeCaches() { 1177 clearTreeCaches() {
554 - this.brandMap = {}  
555 - this.brandLoading = {}  
556 - this.productMap = {}  
557 - this.productLoading = {}  
558 - this.lineMap = {}  
559 - this.linePager = {}  
560 - this.lineLoading = {} 1178 + this.brandMap = {};
  1179 + this.brandLoading = {};
  1180 + this.productMap = {};
  1181 + this.aggProductLoading = {};
  1182 + this.lineMap = {};
  1183 + this.linePager = {};
  1184 + this.lineLoading = {};
  1185 + this.lineSort = {};
  1186 + this.linearInlineMap = {};
  1187 + this.linearInlineLoading = {};
561 }, 1188 },
562 fetchCategories() { 1189 fetchCategories() {
563 - this.categoryLoading = true  
564 - this.clearTreeCaches() 1190 + this.categoryLoading = true;
  1191 + this.clearTreeCaches();
565 request({ 1192 request({
566 - url: '/api/Extend/WtXsckd/Actions/GetPurchaseSummaryByCategory',  
567 - method: 'GET', 1193 + url: "/api/Extend/WtXsckd/Actions/GetPurchaseSummaryByCategory",
  1194 + method: "GET",
568 data: this.buildBasePayload() 1195 data: this.buildBasePayload()
569 }) 1196 })
570 .then(res => { 1197 .then(res => {
571 - const rows = Array.isArray(res.data) ? res.data : (res.data && res.data.list) || []  
572 - this.categoryList = rows 1198 + // 兼容后端返回:{ code, msg, data: [...] } / { data: { list: [...] } } / [...]
  1199 + this.categoryList = this.normalizeArrayPayload(res.data);
573 }) 1200 })
574 .catch(() => { 1201 .catch(() => {
575 - this.categoryList = [] 1202 + this.categoryList = [];
576 }) 1203 })
577 .finally(() => { 1204 .finally(() => {
578 - this.categoryLoading = false  
579 - }) 1205 + this.categoryLoading = false;
  1206 + });
  1207 + },
  1208 + onLinearInlineModeChange() {
  1209 + this.clearTreeCaches();
  1210 + this.fetchCategories();
  1211 + this.$nextTick(() => {
  1212 + const t = this.$refs.categoryTable;
  1213 + if (t && this.categoryList && this.categoryList.length) {
  1214 + this.categoryList.forEach(r => {
  1215 + try {
  1216 + t.toggleRowExpansion(r, false);
  1217 + } catch (e) {
  1218 + /* ignore */
  1219 + }
  1220 + });
  1221 + }
  1222 + });
580 }, 1223 },
581 onCategoryExpand(row, expandedRows) { 1224 onCategoryExpand(row, expandedRows) {
582 - const id = String(row['分类Id'])  
583 - if (expandedRows.some(r => String(r['分类Id']) === id)) {  
584 - this.loadBrands(row) 1225 + const id = String(row["分类Id"]);
  1226 + const isOpen = expandedRows.some(r => String(r["分类Id"]) === id);
  1227 + const ck = this.categoryRowKey(row);
  1228 + if (!isOpen) {
  1229 + this.$delete(this.linearInlineMap, ck);
  1230 + this.$delete(this.brandMap, ck);
  1231 + this.$delete(this.brandLoading, ck);
  1232 + return;
  1233 + }
  1234 + if (this.linearInlineMode) {
  1235 + this.loadLinearInline(row);
  1236 + } else {
  1237 + this.loadBrands(row);
585 } 1238 }
586 }, 1239 },
587 - loadBrands(catRow) {  
588 - const k = this.categoryRowKey(catRow)  
589 - if (this.brandMap[k] !== undefined || this.brandLoading[k]) return  
590 - this.$set(this.brandLoading, k, true)  
591 - const cid = String(catRow['分类Id'] != null ? catRow['分类Id'] : '') 1240 + loadLinearInline(catRow) {
  1241 + const k = this.categoryRowKey(catRow);
  1242 + this.$set(this.linearInlineLoading, k, true);
  1243 + const cid = String(catRow["分类Id"] != null ? catRow["分类Id"] : "");
592 request({ 1244 request({
593 - url: '/api/Extend/WtXsckd/Actions/GetPurchaseSummaryByBrand',  
594 - method: 'GET', 1245 + url: "/api/Extend/WtXsckd/Actions/GetPurchaseSummaryLinear",
  1246 + method: "GET",
595 data: { ...this.buildBasePayload(), categoryId: cid } 1247 data: { ...this.buildBasePayload(), categoryId: cid }
596 }) 1248 })
597 .then(res => { 1249 .then(res => {
598 - const rows = Array.isArray(res.data) ? res.data : (res.data && res.data.list) || []  
599 - this.$set(this.brandMap, k, rows) 1250 + const rows = this.normalizeArrayPayload(res.data);
  1251 + this.$set(this.linearInlineMap, k, { list: rows, loaded: true });
600 }) 1252 })
601 .catch(() => { 1253 .catch(() => {
602 - this.$set(this.brandMap, k, []) 1254 + this.$set(this.linearInlineMap, k, { list: [], loaded: true });
603 }) 1255 })
604 .finally(() => { 1256 .finally(() => {
605 - this.$set(this.brandLoading, k, false) 1257 + this.$set(this.linearInlineLoading, k, false);
  1258 + });
  1259 + },
  1260 + loadBrands(catRow) {
  1261 + const k = this.categoryRowKey(catRow);
  1262 + if (this.brandMap[k] !== undefined || this.brandLoading[k]) return;
  1263 + this.$set(this.brandLoading, k, true);
  1264 + const cid = String(catRow["分类Id"] != null ? catRow["分类Id"] : "");
  1265 + request({
  1266 + url: "/api/Extend/WtXsckd/Actions/GetPurchaseSummaryByBrand",
  1267 + method: "GET",
  1268 + data: { ...this.buildBasePayload(), categoryId: cid }
  1269 + })
  1270 + .then(res => {
  1271 + this.$set(this.brandMap, k, this.normalizeArrayPayload(res.data));
  1272 + })
  1273 + .catch(() => {
  1274 + this.$set(this.brandMap, k, []);
606 }) 1275 })
  1276 + .finally(() => {
  1277 + this.$set(this.brandLoading, k, false);
  1278 + });
607 }, 1279 },
608 onBrandExpand(catRow, brandRow, expandedRows) { 1280 onBrandExpand(catRow, brandRow, expandedRows) {
609 - if (expandedRows.some(r => this.brandRowKey(catRow, r) === this.brandRowKey(catRow, brandRow))) {  
610 - this.loadProducts(catRow, brandRow) 1281 + if (
  1282 + expandedRows.some(
  1283 + r =>
  1284 + this.brandRowKey(catRow, r) === this.brandRowKey(catRow, brandRow)
  1285 + )
  1286 + ) {
  1287 + this.loadProducts(catRow, brandRow);
611 } 1288 }
612 }, 1289 },
613 loadProducts(catRow, brandRow) { 1290 loadProducts(catRow, brandRow) {
614 - const k = this.brandRowKey(catRow, brandRow)  
615 - if (this.productMap[k] !== undefined || this.productLoading[k]) return  
616 - this.$set(this.productLoading, k, true)  
617 - const cid = String(catRow['分类Id'] != null ? catRow['分类Id'] : '')  
618 - const bid = String(brandRow['品牌Id'] != null ? brandRow['品牌Id'] : '') 1291 + const k = this.brandRowKey(catRow, brandRow);
  1292 + if (this.productMap[k] !== undefined || this.aggProductLoading[k]) return;
  1293 + this.$set(this.aggProductLoading, k, true);
  1294 + const cid = String(catRow["分类Id"] != null ? catRow["分类Id"] : "");
  1295 + const bid = String(brandRow["品牌Id"] != null ? brandRow["品牌Id"] : "");
619 request({ 1296 request({
620 - url: '/api/Extend/WtXsckd/Actions/GetPurchaseSummaryByProductAgg',  
621 - method: 'GET', 1297 + url: "/api/Extend/WtXsckd/Actions/GetPurchaseSummaryByProductAgg",
  1298 + method: "GET",
622 data: { ...this.buildBasePayload(), categoryId: cid, brandId: bid } 1299 data: { ...this.buildBasePayload(), categoryId: cid, brandId: bid }
623 }) 1300 })
624 .then(res => { 1301 .then(res => {
625 - const rows = Array.isArray(res.data) ? res.data : (res.data && res.data.list) || []  
626 - this.$set(this.productMap, k, rows) 1302 + this.$set(this.productMap, k, this.normalizeArrayPayload(res.data));
627 }) 1303 })
628 .catch(() => { 1304 .catch(() => {
629 - this.$set(this.productMap, k, []) 1305 + this.$set(this.productMap, k, []);
630 }) 1306 })
631 .finally(() => { 1307 .finally(() => {
632 - this.$set(this.productLoading, k, false)  
633 - }) 1308 + this.$set(this.aggProductLoading, k, false);
  1309 + });
634 }, 1310 },
635 onProductExpand(catRow, brandRow, prodRow, expandedRows) { 1311 onProductExpand(catRow, brandRow, prodRow, expandedRows) {
636 - if (expandedRows.some(r => this.productRowKey(catRow, brandRow, r) === this.productRowKey(catRow, brandRow, prodRow))) {  
637 - this.initLinePager(catRow, brandRow, prodRow)  
638 - this.fetchLines(catRow, brandRow, prodRow, 1) 1312 + if (
  1313 + expandedRows.some(
  1314 + r =>
  1315 + this.productRowKey(catRow, brandRow, r) ===
  1316 + this.productRowKey(catRow, brandRow, prodRow)
  1317 + )
  1318 + ) {
  1319 + this.initLinePager(catRow, brandRow, prodRow);
  1320 + this.fetchLines(catRow, brandRow, prodRow, 1);
639 } 1321 }
640 }, 1322 },
641 initLinePager(catRow, brandRow, prodRow) { 1323 initLinePager(catRow, brandRow, prodRow) {
642 - const lk = this.lineKey(catRow, brandRow, prodRow) 1324 + const lk = this.lineKey(catRow, brandRow, prodRow);
643 if (!this.linePager[lk]) { 1325 if (!this.linePager[lk]) {
644 - this.$set(this.linePager, lk, { currentPage: 1, pageSize: 50, total: 0 }) 1326 + this.$set(this.linePager, lk, {
  1327 + currentPage: 1,
  1328 + pageSize: 50,
  1329 + total: 0
  1330 + });
645 } 1331 }
646 }, 1332 },
647 fetchLines(catRow, brandRow, prodRow, page) { 1333 fetchLines(catRow, brandRow, prodRow, page) {
648 - const lk = this.lineKey(catRow, brandRow, prodRow)  
649 - this.initLinePager(catRow, brandRow, prodRow)  
650 - const pager = this.linePager[lk]  
651 - pager.currentPage = page || 1  
652 - this.$set(this.lineLoading, lk, true)  
653 - const cid = String(catRow['分类Id'] != null ? catRow['分类Id'] : '')  
654 - const bid = String(brandRow['品牌Id'] != null ? brandRow['品牌Id'] : '')  
655 - const pid = String(prodRow['商品Id'] != null ? prodRow['商品Id'] : '') 1334 + const lk = this.lineKey(catRow, brandRow, prodRow);
  1335 + this.initLinePager(catRow, brandRow, prodRow);
  1336 + const pager = this.linePager[lk];
  1337 + pager.currentPage = page || 1;
  1338 + this.$set(this.lineLoading, lk, true);
  1339 + const cid = String(catRow["分类Id"] != null ? catRow["分类Id"] : "");
  1340 + const bid = String(brandRow["品牌Id"] != null ? brandRow["品牌Id"] : "");
  1341 + const pid = String(prodRow["商品Id"] != null ? prodRow["商品Id"] : "");
  1342 + const sortCfg = this.lineSort[lk] || {};
656 request({ 1343 request({
657 - url: '/api/Extend/WtXsckd/Actions/GetPurchaseSummary',  
658 - method: 'GET', 1344 + url: "/api/Extend/WtXsckd/Actions/GetPurchaseSummary",
  1345 + method: "GET",
659 data: { 1346 data: {
660 ...this.buildBasePayload(), 1347 ...this.buildBasePayload(),
661 categoryId: cid, 1348 categoryId: cid,
662 brandId: bid, 1349 brandId: bid,
663 productSpId: pid, 1350 productSpId: pid,
664 currentPage: pager.currentPage, 1351 currentPage: pager.currentPage,
665 - pageSize: pager.pageSize 1352 + pageSize: pager.pageSize,
  1353 + ...(sortCfg.sortField
  1354 + ? {
  1355 + sortField: sortCfg.sortField,
  1356 + sortOrder: sortCfg.sortOrder || "desc"
  1357 + }
  1358 + : {})
666 } 1359 }
667 }) 1360 })
668 .then(res => { 1361 .then(res => {
669 - const body = res.data  
670 - let list = []  
671 - let total = 0  
672 - if (body && typeof body === 'object' && !Array.isArray(body) && body.list !== undefined) {  
673 - list = body.list || []  
674 - total = body.total != null ? Number(body.total) : 0 1362 + // 明细接口返回:{ code, msg, data: { list: [], total } }
  1363 + // 兼容历史:{ list, total } 或直接数组
  1364 + const body =
  1365 + res.data && res.data.data != null ? res.data.data : res.data;
  1366 + let list = [];
  1367 + let total = 0;
  1368 + if (
  1369 + body &&
  1370 + typeof body === "object" &&
  1371 + !Array.isArray(body) &&
  1372 + body.list !== undefined
  1373 + ) {
  1374 + list = body.list || [];
  1375 + total = body.total != null ? Number(body.total) : 0;
675 } else if (Array.isArray(body)) { 1376 } else if (Array.isArray(body)) {
676 - list = body  
677 - total = body.length 1377 + list = body;
  1378 + total = body.length;
678 } 1379 }
679 - this.$set(this.lineMap, lk, { list })  
680 - this.$set(this.linePager[lk], 'total', total) 1380 + this.$set(this.lineMap, lk, { list });
  1381 + this.$set(this.linePager[lk], "total", total);
681 }) 1382 })
682 .catch(() => { 1383 .catch(() => {
683 - this.$set(this.lineMap, lk, { list: [] }) 1384 + this.$set(this.lineMap, lk, { list: [] });
684 }) 1385 })
685 .finally(() => { 1386 .finally(() => {
686 - this.$set(this.lineLoading, lk, false)  
687 - }) 1387 + this.$set(this.lineLoading, lk, false);
  1388 + });
688 }, 1389 },
689 openLinearDialog(catRow) { 1390 openLinearDialog(catRow) {
690 - const cid = String(catRow['分类Id'] != null ? catRow['分类Id'] : '')  
691 - const cname = this.cellText(catRow['分类名称'])  
692 - this.linearHint = `分类:${cname}(最多展示 2000 条,与当前筛选条件一致)`  
693 - this.linearVisible = true  
694 - this.linearLoading = true  
695 - this.linearRows = [] 1391 + const cid = String(catRow["分类Id"] != null ? catRow["分类Id"] : "");
  1392 + const cname = this.cellText(catRow["分类名称"]);
  1393 + this.linearHint = `分类:${cname}(默认每页最多 2000 条,与当前筛选条件一致;可传 currentPage、pageSize 分页)`;
  1394 + this.linearVisible = true;
  1395 + this.linearLoading = true;
  1396 + this.linearRows = [];
696 request({ 1397 request({
697 - url: '/api/Extend/WtXsckd/Actions/GetPurchaseSummaryLinear',  
698 - method: 'GET', 1398 + url: "/api/Extend/WtXsckd/Actions/GetPurchaseSummaryLinear",
  1399 + method: "GET",
699 data: { ...this.buildBasePayload(), categoryId: cid } 1400 data: { ...this.buildBasePayload(), categoryId: cid }
700 }) 1401 })
701 .then(res => { 1402 .then(res => {
702 - this.linearRows = Array.isArray(res.data) ? res.data : (res.data && res.data.list) || [] 1403 + this.linearRows = this.normalizeArrayPayload(res.data);
703 }) 1404 })
704 .catch(() => { 1405 .catch(() => {
705 - this.linearRows = [] 1406 + this.linearRows = [];
706 }) 1407 })
707 .finally(() => { 1408 .finally(() => {
708 - this.linearLoading = false  
709 - }) 1409 + this.linearLoading = false;
  1410 + });
710 }, 1411 },
711 handleSearch() { 1412 handleSearch() {
712 - this.fetchCategories() 1413 + this.fetchCategories();
713 }, 1414 },
714 handleReset() { 1415 handleReset() {
715 this.filters = { 1416 this.filters = {
716 dateRange: [], 1417 dateRange: [],
717 contactUnit: [], 1418 contactUnit: [],
718 agent: [], 1419 agent: [],
719 - productSpId: '', 1420 + productSpId: "",
720 warehouse: [], 1421 warehouse: [],
721 - billType: [...DEFAULT_BILL_TYPES] 1422 + billType: []
  1423 + };
  1424 + this.productOptions = [];
  1425 + this.fetchCategories();
  1426 + },
  1427 + sortAggRows(rows, prop, order) {
  1428 + if (!rows || !rows.length || !prop || !order) return;
  1429 + const dir = order === "ascending" ? 1 : -1;
  1430 + const numCols = ["数量", "入库单价", "采购金额"];
  1431 + const isNum = numCols.indexOf(prop) >= 0;
  1432 + rows.sort((a, b) => {
  1433 + if (isNum)
  1434 + return (this.parseNum(a[prop]) - this.parseNum(b[prop])) * dir;
  1435 + const sa = String(a[prop] != null ? a[prop] : "").trim();
  1436 + const sb = String(b[prop] != null ? b[prop] : "").trim();
  1437 + return sa.localeCompare(sb, "zh-CN") * dir;
  1438 + });
  1439 + },
  1440 + onCategorySort({ prop, order }) {
  1441 + if (!order) {
  1442 + this.fetchCategories();
  1443 + return;
  1444 + }
  1445 + this.sortAggRows(this.categoryList, prop, order);
  1446 + },
  1447 + onBrandSort(catRow, { prop, order }) {
  1448 + const k = this.categoryRowKey(catRow);
  1449 + const rows = this.brandMap[k];
  1450 + if (!order) {
  1451 + this.loadBrandsForce(catRow);
  1452 + return;
  1453 + }
  1454 + if (rows && rows.length) this.sortAggRows(rows, prop, order);
  1455 + },
  1456 + onProductSort(catRow, brandRow, { prop, order }) {
  1457 + const k = this.brandRowKey(catRow, brandRow);
  1458 + const rows = this.productMap[k];
  1459 + if (!order) {
  1460 + this.loadProductsForce(catRow, brandRow);
  1461 + return;
722 } 1462 }
723 - this.productOptions = []  
724 - this.fetchCategories() 1463 + if (rows && rows.length) this.sortAggRows(rows, prop, order);
  1464 + },
  1465 + loadBrandsForce(catRow) {
  1466 + const k = this.categoryRowKey(catRow);
  1467 + this.$delete(this.brandMap, k);
  1468 + this.loadBrands(catRow);
  1469 + },
  1470 + loadProductsForce(catRow, brandRow) {
  1471 + const k = this.brandRowKey(catRow, brandRow);
  1472 + this.$delete(this.productMap, k);
  1473 + this.loadProducts(catRow, brandRow);
  1474 + },
  1475 + onLineSort(catRow, brandRow, prodRow, { prop, order }) {
  1476 + const lk = this.lineKey(catRow, brandRow, prodRow);
  1477 + if (!prop || !order) {
  1478 + this.$delete(this.lineSort, lk);
  1479 + this.fetchLines(catRow, brandRow, prodRow, 1);
  1480 + return;
  1481 + }
  1482 + const sortOrder = order === "ascending" ? "asc" : "desc";
  1483 + this.$set(this.lineSort, lk, { sortField: prop, sortOrder });
  1484 + this.fetchLines(catRow, brandRow, prodRow, 1);
725 } 1485 }
726 } 1486 }
727 -} 1487 +};
728 </script> 1488 </script>
729 1489
730 <style lang="scss" scoped> 1490 <style lang="scss" scoped>
@@ -747,6 +1507,21 @@ export default { @@ -747,6 +1507,21 @@ export default {
747 font-size: 18px; 1507 font-size: 18px;
748 } 1508 }
749 1509
  1510 +.purchase-sum-head__tools {
  1511 + display: inline-flex;
  1512 + align-items: center;
  1513 + flex-wrap: wrap;
  1514 + gap: 10px;
  1515 +}
  1516 +
  1517 +.linear-mode-label {
  1518 + font-size: 13px;
  1519 + color: #606266;
  1520 + display: inline-flex;
  1521 + align-items: center;
  1522 + gap: 4px;
  1523 +}
  1524 +
750 .purchase-tree-table { 1525 .purchase-tree-table {
751 ::v-deep .el-table .cell { 1526 ::v-deep .el-table .cell {
752 white-space: nowrap; 1527 white-space: nowrap;
@@ -758,8 +1533,28 @@ export default { @@ -758,8 +1533,28 @@ export default {
758 background: #fafbfc; 1533 background: #fafbfc;
759 } 1534 }
760 1535
  1536 +.nested-wrap--linear {
  1537 + padding-left: 12px;
  1538 +}
  1539 +
  1540 +.nested-table-host {
  1541 + width: 100%;
  1542 + max-width: 100%;
  1543 + overflow-x: auto;
  1544 + -webkit-overflow-scrolling: touch;
  1545 +}
  1546 +
  1547 +.nested-table-host--detail {
  1548 + min-width: 720px;
  1549 +}
  1550 +
761 .nested-table { 1551 .nested-table {
762 width: 100%; 1552 width: 100%;
  1553 + min-width: 100%;
  1554 +}
  1555 +
  1556 +.nested-table--scroll {
  1557 + min-width: 960px;
763 } 1558 }
764 1559
765 .nested-table--deep ::v-deep .el-table__body-wrapper { 1560 .nested-table--deep ::v-deep .el-table__body-wrapper {
@@ -768,7 +1563,8 @@ export default { @@ -768,7 +1563,8 @@ export default {
768 } 1563 }
769 1564
770 .detail-wrap { 1565 .detail-wrap {
771 - min-width: 720px; 1566 + min-width: 0;
  1567 + max-width: 100%;
772 } 1568 }
773 1569
774 .mini-pager { 1570 .mini-pager {
Antis.Erp.Plat/antis-ncc-admin/src/views/wtShrysz/djmcOptions.js
@@ -3,14 +3,16 @@ @@ -3,14 +3,16 @@
3 * 须与 wt_xsckd.djlx、wt_shrysz.djmc、Dashboard 待办 QueryWtXsckdAuditTodosAsync 的 billDjlx、商品调价单等业务名完全一致。 3 * 须与 wt_xsckd.djlx、wt_shrysz.djmc、Dashboard 待办 QueryWtXsckdAuditTodosAsync 的 billDjlx、商品调价单等业务名完全一致。
4 */ 4 */
5 export const djmcOptions = [ 5 export const djmcOptions = [
6 - { fullName: '同价调拨单', id: '同价调拨单' },  
7 - { fullName: '销售出库单', id: '销售出库单' },  
8 - { fullName: '预售出库单', id: '预售出库单' },  
9 - { fullName: '销售退货单', id: '销售退货单' },  
10 - { fullName: '预售退货单', id: '预售退货单' },  
11 - { fullName: '采购入库单', id: '采购入库单' },  
12 - { fullName: '委托代销发货单', id: '委托代销发货单' },  
13 - { fullName: '委托代销退货单', id: '委托代销退货单' },  
14 - { fullName: '委托代销结算单', id: '委托代销结算单' },  
15 - { fullName: '商品调价单', id: '商品调价单' }  
16 -] 6 + { fullName: "同价调拨单", id: "同价调拨单" },
  7 + { fullName: "销售出库单", id: "销售出库单" },
  8 + { fullName: "预售出库单", id: "预售出库单" },
  9 + { fullName: "销售退货单", id: "销售退货单" },
  10 + { fullName: "预售退货单", id: "预售退货单" },
  11 + { fullName: "采购入库单", id: "采购入库单" },
  12 + { fullName: "委托代销发货单", id: "委托代销发货单" },
  13 + { fullName: "委托代销退货单", id: "委托代销退货单" },
  14 + { fullName: "委托代销结算单", id: "委托代销结算单" },
  15 + { fullName: "商品调价单", id: "商品调价单" },
  16 + // 其他收入单(独立主表 wt_yskzjjs,审核配置仍走 wt_shrysz.djmc 匹配 djlx)
  17 + { fullName: "其他应收单", id: "其他应收单" }
  18 +];
Antis.Erp.Plat/antis-ncc-admin/src/views/wtStockSummary/index.vue
@@ -66,7 +66,7 @@ @@ -66,7 +66,7 @@
66 <el-col :span="12"> 66 <el-col :span="12">
67 <el-form-item label="商品"> 67 <el-form-item label="商品">
68 <el-select 68 <el-select
69 - v-model="filters.productKeyword" 69 + v-model="filters.productSpId"
70 filterable 70 filterable
71 remote 71 remote
72 clearable 72 clearable
@@ -80,14 +80,20 @@ @@ -80,14 +80,20 @@
80 v-for="item in productOptions" 80 v-for="item in productOptions"
81 :key="item.F_Id || item.id" 81 :key="item.F_Id || item.id"
82 :label="formatProductLabel(item)" 82 :label="formatProductLabel(item)"
83 - :value="formatProductLabel(item)" 83 + :value="productOptionValue(item)"
84 /> 84 />
85 </el-select> 85 </el-select>
86 </el-form-item> 86 </el-form-item>
87 </el-col> 87 </el-col>
88 <el-col :span="24"> 88 <el-col :span="24">
89 <div class="action-row"> 89 <div class="action-row">
90 - <el-button type="primary" size="mini" icon="el-icon-search" @click="handleSearch">搜索</el-button> 90 + <el-button
  91 + type="primary"
  92 + size="mini"
  93 + icon="el-icon-search"
  94 + @click="handleSearch"
  95 + >搜索</el-button
  96 + >
91 <el-button size="mini" @click="handleReset">重置</el-button> 97 <el-button size="mini" @click="handleReset">重置</el-button>
92 </div> 98 </div>
93 </el-col> 99 </el-col>
@@ -97,12 +103,68 @@ @@ -97,12 +103,68 @@
97 103
98 <div class="table-section"> 104 <div class="table-section">
99 <div class="table-title-bar"> 105 <div class="table-title-bar">
100 - <i class="el-icon-data-analysis" style="font-size: 20px; margin-right: 8px; color: #409eff" /> 106 + <i
  107 + class="el-icon-data-analysis"
  108 + style="font-size: 20px; margin-right: 8px; color: #409eff"
  109 + />
101 <span>商品库存汇总</span> 110 <span>商品库存汇总</span>
102 - <span class="hint-text">展开分类行可查看该分类下商品明细(品牌、编码、名称及数量成本)</span> 111 + <span v-if="!isProductMode" class="hint-text"
  112 + >展开分类行可查看该分类下商品明细(品牌、编码、名称及数量成本)</span
  113 + >
  114 + <span v-else class="hint-text"
  115 + >已选择商品:按仓库展示数量、成本均价、总成本</span
  116 + >
103 </div> 117 </div>
104 <div class="table-scroll"> 118 <div class="table-scroll">
  119 + <el-table v-if="isProductMode" :data="list" border class="stock-table">
  120 + <el-table-column
  121 + label="仓库名称"
  122 + min-width="180"
  123 + show-overflow-tooltip
  124 + >
  125 + <template slot-scope="scope">
  126 + <i class="el-icon-office-building row-icon cat" />
  127 + <span>{{ cellText(scope.row["仓库名称"]) }}</span>
  128 + </template>
  129 + </el-table-column>
  130 + <el-table-column label="商品编码" width="140" show-overflow-tooltip>
  131 + <template slot-scope="scope">
  132 + <i class="el-icon-postcard row-icon code" />
  133 + {{ cellText(scope.row["商品编码"]) }}
  134 + </template>
  135 + </el-table-column>
  136 + <el-table-column
  137 + label="商品名称"
  138 + min-width="220"
  139 + show-overflow-tooltip
  140 + >
  141 + <template slot-scope="scope">
  142 + <i class="el-icon-tickets row-icon pname" />
  143 + {{ cellText(scope.row["商品名称"]) }}
  144 + </template>
  145 + </el-table-column>
  146 + <el-table-column label="数量" width="120" align="right">
  147 + <template slot-scope="scope">
  148 + <i class="el-icon-goods row-icon qty" />
  149 + {{ formatQty(scope.row["数量"]) }}
  150 + </template>
  151 + </el-table-column>
  152 + <el-table-column label="成本均价" width="130" align="right">
  153 + <template slot-scope="scope">
  154 + <i class="el-icon-coin row-icon money" />
  155 + {{ formatMoney(scope.row["成本均价"]) }}
  156 + </template>
  157 + </el-table-column>
  158 + <el-table-column label="总成本" width="140" align="right">
  159 + <template slot-scope="scope">
  160 + <i class="el-icon-wallet row-icon total" />
  161 + {{ formatMoney(scope.row["总成本"]) }}
  162 + </template>
  163 + </el-table-column>
  164 + </el-table>
  165 +
105 <el-table 166 <el-table
  167 + v-else
106 ref="categoryTable" 168 ref="categoryTable"
107 :data="list" 169 :data="list"
108 :row-key="rowKeyResolver" 170 :row-key="rowKeyResolver"
@@ -126,45 +188,60 @@ @@ -126,45 +188,60 @@
126 size="small" 188 size="small"
127 class="nested-product-table" 189 class="nested-product-table"
128 > 190 >
129 - <el-table-column label="品牌名称" width="120" show-overflow-tooltip> 191 + <el-table-column
  192 + label="品牌名称"
  193 + width="120"
  194 + show-overflow-tooltip
  195 + >
130 <template slot-scope="s"> 196 <template slot-scope="s">
131 <i class="el-icon-medal row-icon brand" /> 197 <i class="el-icon-medal row-icon brand" />
132 - {{ cellText(s.row['品牌名称']) }} 198 + {{ cellText(s.row["品牌名称"]) }}
133 </template> 199 </template>
134 </el-table-column> 200 </el-table-column>
135 - <el-table-column label="商品编码" width="120" show-overflow-tooltip> 201 + <el-table-column
  202 + label="商品编码"
  203 + width="120"
  204 + show-overflow-tooltip
  205 + >
136 <template slot-scope="s"> 206 <template slot-scope="s">
137 <i class="el-icon-postcard row-icon code" /> 207 <i class="el-icon-postcard row-icon code" />
138 - {{ cellText(s.row['商品编码']) }} 208 + {{ cellText(s.row["商品编码"]) }}
139 </template> 209 </template>
140 </el-table-column> 210 </el-table-column>
141 - <el-table-column label="商品名称" min-width="200" show-overflow-tooltip> 211 + <el-table-column
  212 + label="商品名称"
  213 + min-width="200"
  214 + show-overflow-tooltip
  215 + >
142 <template slot-scope="s"> 216 <template slot-scope="s">
143 <i class="el-icon-tickets row-icon pname" /> 217 <i class="el-icon-tickets row-icon pname" />
144 - {{ cellText(s.row['商品名称']) }} 218 + {{ cellText(s.row["商品名称"]) }}
145 </template> 219 </template>
146 </el-table-column> 220 </el-table-column>
147 <el-table-column label="数量" width="100" align="right"> 221 <el-table-column label="数量" width="100" align="right">
148 <template slot-scope="s"> 222 <template slot-scope="s">
149 <i class="el-icon-goods row-icon qty" /> 223 <i class="el-icon-goods row-icon qty" />
150 - {{ formatQty(s.row['数量']) }} 224 + {{ formatQty(s.row["数量"]) }}
151 </template> 225 </template>
152 </el-table-column> 226 </el-table-column>
153 <el-table-column label="成本均价" width="110" align="right"> 227 <el-table-column label="成本均价" width="110" align="right">
154 <template slot-scope="s"> 228 <template slot-scope="s">
155 <i class="el-icon-coin row-icon money" /> 229 <i class="el-icon-coin row-icon money" />
156 - {{ formatMoney(s.row['成本均价']) }} 230 + {{ formatMoney(s.row["成本均价"]) }}
157 </template> 231 </template>
158 </el-table-column> 232 </el-table-column>
159 <el-table-column label="总成本" width="120" align="right"> 233 <el-table-column label="总成本" width="120" align="right">
160 <template slot-scope="s"> 234 <template slot-scope="s">
161 <i class="el-icon-wallet row-icon total" /> 235 <i class="el-icon-wallet row-icon total" />
162 - {{ formatMoney(s.row['总成本']) }} 236 + {{ formatMoney(s.row["总成本"]) }}
163 </template> 237 </template>
164 </el-table-column> 238 </el-table-column>
165 </el-table> 239 </el-table>
166 <div 240 <div
167 - v-if="(productDetailMap[expandKey(props.row)] || []).length === 0 && !expandLoading[expandKey(props.row)]" 241 + v-if="
  242 + (productDetailMap[expandKey(props.row)] || []).length ===
  243 + 0 && !expandLoading[expandKey(props.row)]
  244 + "
168 class="expand-empty" 245 class="expand-empty"
169 > 246 >
170 暂无商品明细 247 暂无商品明细
@@ -173,34 +250,44 @@ @@ -173,34 +250,44 @@
173 </div> 250 </div>
174 </template> 251 </template>
175 </el-table-column> 252 </el-table-column>
176 - <el-table-column label="分类名称" min-width="200" show-overflow-tooltip> 253 + <el-table-column
  254 + label="分类名称"
  255 + min-width="200"
  256 + show-overflow-tooltip
  257 + >
177 <template slot-scope="scope"> 258 <template slot-scope="scope">
178 <i class="el-icon-folder-opened row-icon cat" /> 259 <i class="el-icon-folder-opened row-icon cat" />
179 - <span>{{ cellText(scope.row['分类名称']) }}</span> 260 + <span>{{ cellText(scope.row["分类名称"]) }}</span>
180 </template> 261 </template>
181 </el-table-column> 262 </el-table-column>
182 <el-table-column label="数量" width="120" align="right"> 263 <el-table-column label="数量" width="120" align="right">
183 <template slot-scope="scope"> 264 <template slot-scope="scope">
184 <i class="el-icon-goods row-icon qty" /> 265 <i class="el-icon-goods row-icon qty" />
185 - {{ formatQty(scope.row['数量']) }} 266 + {{ formatQty(scope.row["数量"]) }}
186 </template> 267 </template>
187 </el-table-column> 268 </el-table-column>
188 <el-table-column label="成本均价" width="130" align="right"> 269 <el-table-column label="成本均价" width="130" align="right">
189 <template slot-scope="scope"> 270 <template slot-scope="scope">
190 <i class="el-icon-coin row-icon money" /> 271 <i class="el-icon-coin row-icon money" />
191 - {{ formatMoney(scope.row['成本均价']) }} 272 + {{ formatMoney(scope.row["成本均价"]) }}
192 </template> 273 </template>
193 </el-table-column> 274 </el-table-column>
194 <el-table-column label="总成本" width="140" align="right"> 275 <el-table-column label="总成本" width="140" align="right">
195 <template slot-scope="scope"> 276 <template slot-scope="scope">
196 <i class="el-icon-wallet row-icon total" /> 277 <i class="el-icon-wallet row-icon total" />
197 - {{ formatMoney(scope.row['总成本']) }} 278 + {{ formatMoney(scope.row["总成本"]) }}
198 </template> 279 </template>
199 </el-table-column> 280 </el-table-column>
200 </el-table> 281 </el-table>
201 282
202 <div class="summary-footer"> 283 <div class="summary-footer">
203 - <span><i class="el-icon-s-data" style="color: #409eff; margin-right: 6px" />合计(分类汇总)</span> 284 + <span>
  285 + <i
  286 + class="el-icon-s-data"
  287 + style="color: #409eff; margin-right: 6px"
  288 + />
  289 + {{ isProductMode ? "合计(按仓库)" : "合计(分类汇总)" }}
  290 + </span>
204 <span class="sum-item">数量:{{ totalQty }}</span> 291 <span class="sum-item">数量:{{ totalQty }}</span>
205 <span class="sum-item">总成本:{{ totalCost }}</span> 292 <span class="sum-item">总成本:{{ totalCost }}</span>
206 </div> 293 </div>
@@ -210,10 +297,10 @@ @@ -210,10 +297,10 @@
210 </template> 297 </template>
211 298
212 <script> 299 <script>
213 -import request from '@/utils/request' 300 +import request from "@/utils/request";
214 301
215 export default { 302 export default {
216 - name: 'StockSummary', 303 + name: "StockSummary",
217 data() { 304 data() {
218 return { 305 return {
219 list: [], 306 list: [],
@@ -224,183 +311,225 @@ export default { @@ -224,183 +311,225 @@ export default {
224 warehouse: [], 311 warehouse: [],
225 brand: [], 312 brand: [],
226 category: [], 313 category: [],
227 - productKeyword: '' 314 + productSpId: ""
228 }, 315 },
229 warehouseOptions: [], 316 warehouseOptions: [],
230 brandOptions: [], 317 brandOptions: [],
231 categoryOptions: [], 318 categoryOptions: [],
232 productOptions: [], 319 productOptions: [],
233 productLoading: false 320 productLoading: false
234 - } 321 + };
235 }, 322 },
236 computed: { 323 computed: {
  324 + isProductMode() {
  325 + return !!this.filters.productSpId;
  326 + },
237 totalQty() { 327 totalQty() {
238 - return this.list.reduce((s, r) => s + Number(r['数量'] || 0), 0) 328 + return this.list.reduce((s, r) => s + Number(r["数量"] || 0), 0);
239 }, 329 },
240 totalCost() { 330 totalCost() {
241 - const n = this.list.reduce((s, r) => s + Number(r['总成本'] || 0), 0)  
242 - return n.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) 331 + const n = this.list.reduce((s, r) => s + Number(r["总成本"] || 0), 0);
  332 + return n.toLocaleString("zh-CN", {
  333 + minimumFractionDigits: 2,
  334 + maximumFractionDigits: 2
  335 + });
243 } 336 }
244 }, 337 },
245 methods: { 338 methods: {
  339 + normalizeArrayPayload(data) {
  340 + if (Array.isArray(data)) return data;
  341 + if (data && Array.isArray(data.list)) return data.list;
  342 + if (data && data.data && Array.isArray(data.data.list))
  343 + return data.data.list;
  344 + if (data && Array.isArray(data.data)) return data.data;
  345 + return [];
  346 + },
246 rowKeyResolver(row) { 347 rowKeyResolver(row) {
247 - const id = row['分类Id']  
248 - if (id !== null && id !== undefined && id !== '') {  
249 - return `cat-${id}` 348 + const id = row["分类Id"];
  349 + if (id !== null && id !== undefined && id !== "") {
  350 + return `cat-${id}`;
250 } 351 }
251 - return row._rowUid || 'cat-unknown' 352 + return row._rowUid || "cat-unknown";
252 }, 353 },
253 expandKey(row) { 354 expandKey(row) {
254 - return this.rowKeyResolver(row) 355 + return this.rowKeyResolver(row);
255 }, 356 },
256 categoryIdOf(row) { 357 categoryIdOf(row) {
257 - const id = row['分类Id']  
258 - if (id === null || id === undefined || id === '') return null  
259 - return String(id) 358 + const id = row["分类Id"];
  359 + if (id === null || id === undefined || id === "") return null;
  360 + return String(id);
260 }, 361 },
261 cellText(v) { 362 cellText(v) {
262 - if (v === null || v === undefined || v === '') return '无'  
263 - return String(v) 363 + if (v === null || v === undefined || v === "") return "无";
  364 + return String(v);
264 }, 365 },
265 formatQty(v) { 366 formatQty(v) {
266 - if (v === null || v === undefined || v === '') return '无'  
267 - const n = Number(v)  
268 - if (Number.isNaN(n)) return String(v)  
269 - return n.toLocaleString('zh-CN', { maximumFractionDigits: 4 }) 367 + if (v === null || v === undefined || v === "") return "无";
  368 + const n = Number(v);
  369 + if (Number.isNaN(n)) return String(v);
  370 + return n.toLocaleString("zh-CN", { maximumFractionDigits: 4 });
270 }, 371 },
271 formatMoney(v) { 372 formatMoney(v) {
272 - if (v === null || v === undefined || v === '') return '无'  
273 - const n = Number(v)  
274 - if (Number.isNaN(n)) return String(v)  
275 - return n.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 4 }) 373 + if (v === null || v === undefined || v === "") return "无";
  374 + const n = Number(v);
  375 + if (Number.isNaN(n)) return String(v);
  376 + return n.toLocaleString("zh-CN", {
  377 + minimumFractionDigits: 2,
  378 + maximumFractionDigits: 4
  379 + });
276 }, 380 },
277 formatProductLabel(item) { 381 formatProductLabel(item) {
278 - if (!item) return ''  
279 - const code = item.F_Spbm || item.spbm || item.商品编号 || ''  
280 - const name = item.F_Spmc || item.spmc || item.商品名称 || ''  
281 - return [code, name].filter(Boolean).join(' ') 382 + if (!item) return "";
  383 + const code = item.F_Spbm || item.spbm || item.商品编号 || "";
  384 + const name = item.F_Spmc || item.spmc || item.商品名称 || "";
  385 + return [code, name].filter(Boolean).join(" ");
  386 + },
  387 + productOptionValue(item) {
  388 + if (!item) return "";
  389 + const v = item.id || item.F_Id || item.f_Id;
  390 + return v != null && String(v).trim() !== "" ? String(v).trim() : "";
282 }, 391 },
283 async loadOptions() { 392 async loadOptions() {
284 try { 393 try {
285 const [ckRes, plRes, ppRes] = await Promise.all([ 394 const [ckRes, plRes, ppRes] = await Promise.all([
286 - request({ url: '/api/Extend/WtCk', method: 'GET', data: { pageSize: 1000, currentPage: 1 } }),  
287 - request({ url: '/api/Extend/WtPl', method: 'GET', data: { pageSize: 1000, currentPage: 1 } }),  
288 - request({ url: '/api/Extend/WtPp', method: 'GET', data: { pageSize: 1000, currentPage: 1 } })  
289 - ])  
290 - const cks = ckRes.data.list || ckRes.data || [] 395 + request({
  396 + url: "/api/Extend/WtCk",
  397 + method: "GET",
  398 + data: { pageSize: 1000, currentPage: 1 }
  399 + }),
  400 + request({
  401 + url: "/api/Extend/WtPl",
  402 + method: "GET",
  403 + data: { pageSize: 1000, currentPage: 1 }
  404 + }),
  405 + request({
  406 + url: "/api/Extend/WtPp",
  407 + method: "GET",
  408 + data: { pageSize: 1000, currentPage: 1 }
  409 + })
  410 + ]);
  411 + const cks = ckRes.data.list || ckRes.data || [];
291 this.warehouseOptions = cks.map(x => ({ 412 this.warehouseOptions = cks.map(x => ({
292 label: x.mdmc || x.F_mdmc || x.id, 413 label: x.mdmc || x.F_mdmc || x.id,
293 value: x.id || x.F_Id 414 value: x.id || x.F_Id
294 - }))  
295 - const pls = plRes.data.list || plRes.data || [] 415 + }));
  416 + const pls = plRes.data.list || plRes.data || [];
296 this.categoryOptions = pls.map(x => ({ 417 this.categoryOptions = pls.map(x => ({
297 label: x.plmc || x.F_Plmc || x.id, 418 label: x.plmc || x.F_Plmc || x.id,
298 value: x.id || x.F_Id 419 value: x.id || x.F_Id
299 - }))  
300 - const pps = ppRes.data.list || ppRes.data || [] 420 + }));
  421 + const pps = ppRes.data.list || ppRes.data || [];
301 this.brandOptions = pps.map(x => ({ 422 this.brandOptions = pps.map(x => ({
302 label: x.ppmc || x.F_Ppmc || x.id, 423 label: x.ppmc || x.F_Ppmc || x.id,
303 value: x.id || x.F_Id 424 value: x.id || x.F_Id
304 - })) 425 + }));
305 } catch (e) { 426 } catch (e) {
306 - console.error('库存汇总筛选项加载失败', e) 427 + console.error("库存汇总筛选项加载失败", e);
307 } 428 }
308 }, 429 },
309 async handleProductSearch(query) { 430 async handleProductSearch(query) {
310 if (!query) { 431 if (!query) {
311 - this.productOptions = []  
312 - return 432 + this.productOptions = [];
  433 + return;
313 } 434 }
314 - this.productLoading = true 435 + this.productLoading = true;
315 try { 436 try {
316 const res = await request({ 437 const res = await request({
317 - url: '/api/Extend/WtSp',  
318 - method: 'GET',  
319 - data: { pageSize: 200, currentPage: 1, keyword: query }  
320 - })  
321 - this.productOptions = res.data.list || res.data || [] 438 + url: "/api/Extend/WtSp/GetListByKeyword",
  439 + method: "GET",
  440 + data: { pageSize: 200, currentPage: 1, keyword: String(query).trim() }
  441 + });
  442 + this.productOptions = this.normalizeArrayPayload(res.data);
322 } catch (e) { 443 } catch (e) {
323 - console.error(e) 444 + console.error(e);
324 } finally { 445 } finally {
325 - this.productLoading = false 446 + this.productLoading = false;
326 } 447 }
327 }, 448 },
328 buildPayload() { 449 buildPayload() {
329 - const p = {}  
330 - if (this.filters.warehouse.length) p.warehouse = this.filters.warehouse.join(',')  
331 - if (this.filters.brand.length) p.brand = this.filters.brand.join(',')  
332 - if (this.filters.category.length) p.category = this.filters.category.join(',')  
333 - if (this.filters.productKeyword) p.product = this.filters.productKeyword  
334 - return p 450 + const p = {};
  451 + if (this.filters.warehouse.length)
  452 + p.warehouse = this.filters.warehouse.join(",");
  453 + if (this.filters.brand.length) p.brand = this.filters.brand.join(",");
  454 + if (this.filters.category.length)
  455 + p.category = this.filters.category.join(",");
  456 + if (this.filters.productSpId) p.productSpId = this.filters.productSpId;
  457 + return p;
335 }, 458 },
336 normalizeCategoryRows(rows) { 459 normalizeCategoryRows(rows) {
337 - const arr = rows || [] 460 + const arr = rows || [];
338 return arr.map((r, i) => { 461 return arr.map((r, i) => {
339 - const hasId = r['分类Id'] !== null && r['分类Id'] !== undefined && r['分类Id'] !== ''  
340 - return hasId ? { ...r } : { ...r, _rowUid: `no-pl-${i}` }  
341 - }) 462 + const hasId =
  463 + r["分类Id"] !== null &&
  464 + r["分类Id"] !== undefined &&
  465 + r["分类Id"] !== "";
  466 + return hasId ? { ...r } : { ...r, _rowUid: `no-pl-${i}` };
  467 + });
342 }, 468 },
343 fetchData() { 469 fetchData() {
344 - this.productDetailMap = {}  
345 - this.productDetailLoaded = {}  
346 - this.expandLoading = {}  
347 - request({  
348 - url: '/api/Extend/WtXsckd/Actions/GetStockSummaryByCategory',  
349 - method: 'GET',  
350 - data: this.buildPayload()  
351 - }).then(res => {  
352 - this.list = this.normalizeCategoryRows(res.data || [])  
353 - }) 470 + this.productDetailMap = {};
  471 + this.productDetailLoaded = {};
  472 + this.expandLoading = {};
  473 + const payload = this.buildPayload();
  474 + const url = this.isProductMode
  475 + ? "/api/Extend/WtXsckd/Actions/GetStockSummaryByWarehouse"
  476 + : "/api/Extend/WtXsckd/Actions/GetStockSummaryByCategory";
  477 + request({ url, method: "GET", data: payload }).then(res => {
  478 + const rows = this.normalizeArrayPayload(res.data);
  479 + this.list = this.isProductMode
  480 + ? rows
  481 + : this.normalizeCategoryRows(rows);
  482 + });
354 }, 483 },
355 async loadProductDetailsForRow(row) { 484 async loadProductDetailsForRow(row) {
356 - const cid = this.categoryIdOf(row)  
357 - const key = this.expandKey(row)  
358 - if (!cid) return  
359 - if (this.productDetailLoaded[key]) return  
360 - this.$set(this.expandLoading, key, true) 485 + const cid = this.categoryIdOf(row);
  486 + const key = this.expandKey(row);
  487 + if (!cid) return;
  488 + if (this.productDetailLoaded[key]) return;
  489 + this.$set(this.expandLoading, key, true);
361 try { 490 try {
362 - const payload = { ...this.buildPayload(), categoryId: cid } 491 + const payload = { ...this.buildPayload(), categoryId: cid };
363 const res = await request({ 492 const res = await request({
364 - url: '/api/Extend/WtXsckd/Actions/GetStockSummaryByProduct',  
365 - method: 'GET', 493 + url: "/api/Extend/WtXsckd/Actions/GetStockSummaryByProduct",
  494 + method: "GET",
366 data: payload 495 data: payload
367 - })  
368 - const rows = res.data || []  
369 - this.$set(this.productDetailMap, key, rows)  
370 - this.$set(this.productDetailLoaded, key, true) 496 + });
  497 + const rows = res.data || [];
  498 + this.$set(this.productDetailMap, key, rows);
  499 + this.$set(this.productDetailLoaded, key, true);
371 } catch (e) { 500 } catch (e) {
372 - console.error(e)  
373 - this.$set(this.productDetailMap, key, [])  
374 - this.$set(this.productDetailLoaded, key, true) 501 + console.error(e);
  502 + this.$set(this.productDetailMap, key, []);
  503 + this.$set(this.productDetailLoaded, key, true);
375 } finally { 504 } finally {
376 - this.$set(this.expandLoading, key, false) 505 + this.$set(this.expandLoading, key, false);
377 } 506 }
378 }, 507 },
379 onExpandChange(row, expandedRows) { 508 onExpandChange(row, expandedRows) {
380 - const key = this.expandKey(row)  
381 - const opened = expandedRows.some(r => this.expandKey(r) === key)  
382 - if (!opened) return  
383 - if (!this.categoryIdOf(row)) return  
384 - this.loadProductDetailsForRow(row) 509 + const key = this.expandKey(row);
  510 + const opened = expandedRows.some(r => this.expandKey(r) === key);
  511 + if (!opened) return;
  512 + if (!this.categoryIdOf(row)) return;
  513 + this.loadProductDetailsForRow(row);
385 }, 514 },
386 handleSearch() { 515 handleSearch() {
387 - this.fetchData() 516 + this.fetchData();
388 }, 517 },
389 handleReset() { 518 handleReset() {
390 this.filters = { 519 this.filters = {
391 warehouse: [], 520 warehouse: [],
392 brand: [], 521 brand: [],
393 category: [], 522 category: [],
394 - productKeyword: ''  
395 - }  
396 - this.fetchData() 523 + productSpId: ""
  524 + };
  525 + this.fetchData();
397 } 526 }
398 }, 527 },
399 created() { 528 created() {
400 - this.loadOptions()  
401 - this.fetchData() 529 + this.loadOptions();
  530 + this.fetchData();
402 } 531 }
403 -} 532 +};
404 </script> 533 </script>
405 534
406 <style scoped> 535 <style scoped>
Antis.Erp.Plat/antis-ncc-admin/src/views/wtYskzjjs_qt/Form.vue
1 <template> 1 <template>
2 - <el-dialog :title="!dataForm.id ? '新建' : isDetail ? '详情':'编辑'" :close-on-click-modal="false" :visible.sync="visible" class="NCC-dialog NCC-dialog_center" lock-scroll width="90%">  
3 - <el-row :gutter="15" class="" >  
4 - <el-form ref="elForm" :model="dataForm" size="small" label-width="100px" label-position="right" :disabled="!!isDetail" :rules="rules">  
5 - <el-col :span="8">  
6 - <el-form-item label="单据编号" prop="id">  
7 - <el-input v-model="dataForm.id" placeholder="请输入" clearable readonly :style='{"width":"100%"}' >  
8 - </el-input>  
9 - </el-form-item>  
10 - </el-col>  
11 - <el-col :span="8">  
12 - <el-form-item label="单据日期" prop="djrq">  
13 - <el-date-picker v-model="dataForm.djrq" placeholder="请选择" clearable :style='{"width":"100%"}' type='date' format="yyyy-MM-dd" value-format="timestamp" >  
14 - </el-date-picker>  
15 - </el-form-item>  
16 - </el-col>  
17 - <el-col :span="8">  
18 - <el-form-item label="经手人" prop="jsr">  
19 - <user-select v-model="dataForm.jsr" placeholder="请选择" clearable >  
20 - </user-select>  
21 - </el-form-item>  
22 - </el-col>  
23 - <el-col :span="8">  
24 - <el-form-item label="往来单位" prop="wldw">  
25 - <el-select v-model="dataForm.wldw" placeholder="请选择" clearable filterable :style='{"width":"100%"}'>  
26 - <el-option v-for="(item, index) in wldwOptions" :key="index" :label="item.dwmc" :value="item.id" :disabled="item.disabled"></el-option>  
27 - </el-select>  
28 - </el-form-item>  
29 - </el-col>  
30 - <el-col :span="24">  
31 - <el-form-item label-width="0">  
32 - <el-table :data="dataForm.wtYskzjjsMxList" size='mini'>  
33 - <el-table-column type="index" width="50" label="序号" align="center" />  
34 - <el-table-column prop="srxmmc" label="收入项目">  
35 - <template slot-scope="scope">  
36 - <el-select  
37 - v-model="scope.row.srxmmc"  
38 - placeholder="请选择或输入"  
39 - clearable  
40 - filterable  
41 - allow-create  
42 - default-first-option  
43 - >  
44 - <el-option v-for="(item, index) in srxmmcOptions" :key="index" :label="item.fullName" :value="item.id" :disabled="item.disabled"></el-option>  
45 - </el-select>  
46 - </template>  
47 - </el-table-column>  
48 - <el-table-column prop="je" label="原币金额">  
49 - <template slot-scope="scope">  
50 - <el-input-number  
51 - v-model.number="scope.row.je"  
52 - placeholder="请输入"  
53 - :min="0"  
54 - :precision="2"  
55 - controls-position="right"  
56 - style="width: 100%"  
57 - @change="calculateTotalAmount"  
58 - />  
59 - </template>  
60 - </el-table-column>  
61 - <el-table-column prop="bz" label="备注">  
62 - <template slot-scope="scope">  
63 - <el-input v-model="scope.row.bz" placeholder="请输入" clearable ></el-input>  
64 - </template>  
65 - </el-table-column>  
66 - <el-table-column label="操作" width="50">  
67 - <template slot-scope="scope">  
68 - <el-button size="mini" type="text" class="NCC-table-delBtn" @click="handleDelWtYskzjjsMxEntityList(scope.$index)">删除</el-button>  
69 - </template>  
70 - </el-table-column>  
71 - </el-table>  
72 - <div class="table-actions" @click="addHandleWtYskzjjsMxEntityList()">  
73 - <el-button type="text" icon="el-icon-plus">新增</el-button>  
74 - </div>  
75 - </el-form-item>  
76 - </el-col>  
77 - <!-- 保留收款账户和收款金额,隐藏付款账户和付款金额 -->  
78 - <!--  
79 - <el-col :span="6">  
80 - <el-form-item label="付款账户" prop="fkzh">  
81 - <el-select v-model="dataForm.fkzh" placeholder="请选择" clearable :style='{"width":"100%"}' >  
82 - <el-option v-for="(item, index) in fkzhOptions" :key="index" :label="item.fullName" :value="item.id" ></el-option>  
83 - </el-select>  
84 - </el-form-item>  
85 - </el-col>  
86 - <el-col :span="6">  
87 - <el-form-item label="付款金额" prop="fkje">  
88 - <el-input v-model="dataForm.fkje" placeholder="请输入" clearable :style='{"width":"100%"}' >  
89 - </el-input>  
90 - </el-form-item>  
91 - </el-col>  
92 - -->  
93 - <el-col :span="6">  
94 - <el-form-item label="收款账户" prop="skzh">  
95 - <el-select v-model="dataForm.skzh" placeholder="请选择" clearable :style='{"width":"100%"}' >  
96 - <el-option v-for="(item, index) in skzhOptions" :key="index" :label="item.fullName" :value="item.id" ></el-option>  
97 - </el-select>  
98 - </el-form-item>  
99 - </el-col>  
100 - <el-col :span="6">  
101 - <el-form-item label="收款金额" prop="skje">  
102 - <el-input v-model="dataForm.skje" placeholder="请输入" clearable :style='{"width":"100%"}' >  
103 - </el-input>  
104 - </el-form-item>  
105 - </el-col>  
106 - <el-col :span="24" v-if="false" >  
107 - <el-form-item label="单据类型" prop="djlx">  
108 - <el-input v-model="dataForm.djlx" placeholder="请输入" clearable :style='{"width":"100%"}' >  
109 - </el-input>  
110 - </el-form-item>  
111 - </el-col>  
112 - </el-form>  
113 - </el-row>  
114 - <span slot="footer" class="dialog-footer">  
115 - <el-button @click="visible = false">取 消</el-button>  
116 - <el-button type="primary" @click="dataFormSubmit()" v-if="!isDetail">确 定</el-button>  
117 - </span>  
118 - </el-dialog> 2 + <el-dialog
  3 + :title="!dataForm.id ? '新建' : isDetail ? '详情' : '编辑'"
  4 + :close-on-click-modal="false"
  5 + :visible.sync="visible"
  6 + class="NCC-dialog NCC-dialog_center"
  7 + lock-scroll
  8 + width="90%"
  9 + >
  10 + <el-row :gutter="15" class="">
  11 + <el-form
  12 + ref="elForm"
  13 + :model="dataForm"
  14 + size="small"
  15 + label-width="100px"
  16 + label-position="right"
  17 + :disabled="!!isDetail"
  18 + :rules="rules"
  19 + >
  20 + <el-col :span="8">
  21 + <el-form-item label="单据编号" prop="id">
  22 + <el-input
  23 + v-model="dataForm.id"
  24 + placeholder="请输入"
  25 + clearable
  26 + readonly
  27 + :style="{ width: '100%' }"
  28 + >
  29 + </el-input>
  30 + </el-form-item>
  31 + </el-col>
  32 + <el-col :span="8">
  33 + <el-form-item label="单据日期" prop="djrq">
  34 + <el-date-picker
  35 + v-model="dataForm.djrq"
  36 + placeholder="请选择"
  37 + clearable
  38 + :style="{ width: '100%' }"
  39 + type="date"
  40 + format="yyyy-MM-dd"
  41 + value-format="timestamp"
  42 + >
  43 + </el-date-picker>
  44 + </el-form-item>
  45 + </el-col>
  46 + <el-col :span="8">
  47 + <el-form-item label="经手人" prop="jsr">
  48 + <user-select v-model="dataForm.jsr" placeholder="请选择" clearable>
  49 + </user-select>
  50 + </el-form-item>
  51 + </el-col>
  52 + <el-col :span="8">
  53 + <el-form-item label="往来单位" prop="wldw">
  54 + <el-select
  55 + v-model="dataForm.wldw"
  56 + placeholder="输入关键字搜索往来单位"
  57 + clearable
  58 + filterable
  59 + remote
  60 + reserve-keyword
  61 + :remote-method="remoteWldw"
  62 + :loading="wldwLoading"
  63 + :style="{ width: '100%' }"
  64 + @change="onWldwChange"
  65 + >
  66 + <el-option
  67 + v-for="item in wldwOptions"
  68 + :key="'w' + item.id"
  69 + :label="item.dwmc"
  70 + :value="String(item.id)"
  71 + />
  72 + </el-select>
  73 + </el-form-item>
  74 + </el-col>
  75 + <el-col :span="8" v-if="dataForm.id">
  76 + <el-form-item label="审核状态">
  77 + <el-input
  78 + :value="dataForm.djzt || '无'"
  79 + readonly
  80 + :style="{ width: '100%' }"
  81 + />
  82 + </el-form-item>
  83 + </el-col>
  84 + <el-col :span="24">
  85 + <el-form-item label-width="0" prop="wtYskzjjsMxList">
  86 + <el-table :data="dataForm.wtYskzjjsMxList" size="mini">
  87 + <el-table-column
  88 + type="index"
  89 + width="50"
  90 + label="序号"
  91 + align="center"
  92 + />
  93 + <el-table-column prop="srxmmc" label="收入项目">
  94 + <template slot-scope="scope">
  95 + <el-select
  96 + v-model="scope.row.srxmmc"
  97 + placeholder="请选择或输入"
  98 + clearable
  99 + filterable
  100 + allow-create
  101 + default-first-option
  102 + >
  103 + <el-option
  104 + v-for="(item, index) in srxmmcOptions"
  105 + :key="index"
  106 + :label="item.fullName"
  107 + :value="item.id"
  108 + :disabled="item.disabled"
  109 + ></el-option>
  110 + </el-select>
  111 + </template>
  112 + </el-table-column>
  113 + <el-table-column prop="je" label="原币金额">
  114 + <template slot-scope="scope">
  115 + <el-input-number
  116 + v-model.number="scope.row.je"
  117 + placeholder="请输入"
  118 + :min="0"
  119 + :precision="2"
  120 + controls-position="right"
  121 + style="width: 100%"
  122 + @change="calculateTotalAmount"
  123 + />
  124 + </template>
  125 + </el-table-column>
  126 + <el-table-column prop="bz" label="备注">
  127 + <template slot-scope="scope">
  128 + <el-input
  129 + v-model="scope.row.bz"
  130 + placeholder="请输入"
  131 + clearable
  132 + ></el-input>
  133 + </template>
  134 + </el-table-column>
  135 + <el-table-column label="操作" width="50">
  136 + <template slot-scope="scope">
  137 + <el-button
  138 + size="mini"
  139 + type="text"
  140 + class="NCC-table-delBtn"
  141 + @click="handleDelWtYskzjjsMxEntityList(scope.$index)"
  142 + >删除</el-button
  143 + >
  144 + </template>
  145 + </el-table-column>
  146 + </el-table>
  147 + <div
  148 + class="table-actions"
  149 + @click="addHandleWtYskzjjsMxEntityList()"
  150 + >
  151 + <el-button type="text" icon="el-icon-plus">新增</el-button>
  152 + </div>
  153 + </el-form-item>
  154 + </el-col>
  155 + <el-col :span="6">
  156 + <el-form-item label="收款账户" prop="skzh">
  157 + <el-select
  158 + v-model="dataForm.skzh"
  159 + placeholder="请选择"
  160 + clearable
  161 + :style="{ width: '100%' }"
  162 + >
  163 + <el-option
  164 + v-for="(item, index) in skzhOptions"
  165 + :key="index"
  166 + :label="item.fullName"
  167 + :value="item.id"
  168 + ></el-option>
  169 + </el-select>
  170 + </el-form-item>
  171 + </el-col>
  172 + <el-col :span="6">
  173 + <el-form-item label="收款金额" prop="skje">
  174 + <el-input
  175 + v-model="dataForm.skje"
  176 + placeholder="根据明细合计"
  177 + clearable
  178 + readonly
  179 + :style="{ width: '100%' }"
  180 + >
  181 + </el-input>
  182 + </el-form-item>
  183 + </el-col>
  184 + <el-col :span="24" v-if="false">
  185 + <el-form-item label="单据类型" prop="djlx">
  186 + <el-input
  187 + v-model="dataForm.djlx"
  188 + placeholder="请输入"
  189 + clearable
  190 + :style="{ width: '100%' }"
  191 + >
  192 + </el-input>
  193 + </el-form-item>
  194 + </el-col>
  195 + </el-form>
  196 + </el-row>
  197 + <span slot="footer" class="dialog-footer">
  198 + <el-button @click="visible = false">取 消</el-button>
  199 + <el-button type="default" @click="saveDraft" v-if="!isDetail"
  200 + >保存草稿</el-button
  201 + >
  202 + <el-button type="primary" @click="dataFormSubmit()" v-if="!isDetail"
  203 + >提交审核</el-button
  204 + >
  205 + </span>
  206 + </el-dialog>
119 </template> 207 </template>
120 <script> 208 <script>
121 - import request from '@/utils/request'  
122 - import { getDictionaryDataSelector } from '@/api/systemData/dictionary'  
123 - import { getAccountSelector } from '@/api/extend/wtAccount'  
124 - export default {  
125 - components: {},  
126 - props: [],  
127 - data() {  
128 - return {  
129 - loading: false,  
130 - visible: false,  
131 - isDetail: false,  
132 - dataForm: {  
133 - id:'',  
134 - id:undefined,  
135 - djrq:undefined,  
136 - jsr:undefined,  
137 - wldw: undefined, // 新增  
138 - wtYskzjjsMxList:[],  
139 - fkzh:undefined,  
140 - fkje:undefined,  
141 - skzh:undefined,  
142 - skje:undefined,  
143 - djlx:undefined,  
144 - },  
145 - rules: {  
146 - },  
147 - srxmmcOptions : [],  
148 - fkzhOptions : [],  
149 - skzhOptions : [],  
150 - wldwOptions : [],  
151 - }  
152 - },  
153 - computed: {},  
154 - watch: {},  
155 - created() {  
156 - this.getsrxmmcOptions();  
157 - this.getfkzhOptions();  
158 - this.getskzhOptions();  
159 - this.getWldwOptions();  
160 - // 新建时设置默认值  
161 - if (!this.dataForm.id) {  
162 - // 单据日期  
163 - this.dataForm.djrq = Date.now();  
164 - // 经手人  
165 - if (this.$store && this.$store.getters && this.$store.getters.userInfo) {  
166 - this.dataForm.jsr = this.$store.getters.userInfo.userId || this.$store.getters.userInfo.id;  
167 - }  
168 - // 单据类型  
169 - this.dataForm.djlx = '其他应收款';  
170 - }  
171 - },  
172 - mounted() {  
173 - },  
174 - methods: {  
175 - getsrxmmcOptions(){  
176 - getDictionaryDataSelector('715562947862070533').then(res => {  
177 - this.srxmmcOptions = res.data.list  
178 - });  
179 - },  
180 - getfkzhOptions(){  
181 - getAccountSelector().then(res => {  
182 - this.fkzhOptions = res.data.list  
183 - });  
184 - },  
185 - getskzhOptions(){  
186 - getAccountSelector().then(res => {  
187 - this.skzhOptions = res.data.list  
188 - });  
189 - },  
190 - getWldwOptions(){  
191 - request({  
192 - url: '/api/Extend/WtWldw',  
193 - method: 'GET',  
194 - data: { currentPage: 1, pageSize: 5000 }  
195 - }).then(res => {  
196 - this.wldwOptions = res.data.list || res.data || []  
197 - })  
198 - },  
199 - // 计算明细表原币金额合计  
200 - calculateTotalAmount() {  
201 - let totalAmount = 0;  
202 - if (this.dataForm.wtYskzjjsMxList && this.dataForm.wtYskzjjsMxList.length > 0) {  
203 - this.dataForm.wtYskzjjsMxList.forEach(row => {  
204 - const je = parseFloat(row.je) || 0;  
205 - totalAmount += je;  
206 - });  
207 - }  
208 - // 更新收款金额  
209 - this.dataForm.skje = totalAmount.toFixed(2);  
210 - },  
211 - goBack() {  
212 - this.$emit('refresh')  
213 - },  
214 - init(id, isDetail) {  
215 - this.dataForm.id = id || 0;  
216 - this.visible = true;  
217 - this.isDetail = isDetail || false;  
218 - this.$nextTick(() => {  
219 - this.$refs['elForm'].resetFields();  
220 - if (this.dataForm.id) {  
221 - request({  
222 - url: '/api/Extend/WtYskzjjs/' + this.dataForm.id,  
223 - method: 'get'  
224 - }).then(res =>{  
225 - this.dataForm = res.data;  
226 - // 编辑时计算合计  
227 - this.$nextTick(() => {  
228 - this.calculateTotalAmount();  
229 - });  
230 - })  
231 - } else {  
232 - // resetFields后再赋值,确保djlx不会被清空  
233 - this.dataForm.djlx = '其他应收款';  
234 - }  
235 - })  
236 - },  
237 - dataFormSubmit() {  
238 - this.$refs['elForm'].validate((valid) => {  
239 - if (valid) {  
240 - if (!this.dataForm.id) {  
241 - request({  
242 - url: `/api/Extend/WtYskzjjs`,  
243 - method: 'post',  
244 - data: this.dataForm,  
245 - }).then((res) => {  
246 - this.$message({  
247 - message: res.msg,  
248 - type: 'success',  
249 - duration: 1000,  
250 - onClose: () => {  
251 - this.visible = false,  
252 - this.$emit('refresh', true)  
253 - }  
254 - })  
255 - })  
256 - } else {  
257 - request({  
258 - url: '/api/Extend/WtYskzjjs/' + this.dataForm.id,  
259 - method: 'PUT',  
260 - data: this.dataForm  
261 - }).then((res) => {  
262 - this.$message({  
263 - message: res.msg,  
264 - type: 'success',  
265 - duration: 1000,  
266 - onClose: () => {  
267 - this.visible = false  
268 - this.$emit('refresh', true)  
269 - }  
270 - })  
271 - })  
272 - }  
273 - }  
274 - })  
275 - },  
276 - addHandleWtYskzjjsMxEntityList() {  
277 - let item = {  
278 - srxmmc: undefined,  
279 - je: undefined,  
280 - bz: undefined,  
281 - mxlx: '其他收入单',  
282 - }  
283 - this.dataForm.wtYskzjjsMxList.push(item)  
284 - // 新增明细行后重新计算合计  
285 - this.$nextTick(() => {  
286 - this.calculateTotalAmount();  
287 - });  
288 - },  
289 - handleDelWtYskzjjsMxEntityList(index) {  
290 - this.dataForm.wtYskzjjsMxList.splice(index, 1);  
291 - // 删除明细行后重新计算合计  
292 - this.$nextTick(() => {  
293 - this.calculateTotalAmount();  
294 - });  
295 - },  
296 - }  
297 - } 209 +import request from "@/utils/request";
  210 +import { getDictionaryDataSelector } from "@/api/systemData/dictionary";
  211 +import { getAccountSelector } from "@/api/extend/wtAccount";
  212 +export default {
  213 + components: {},
  214 + props: [],
  215 + data() {
  216 + const validateSkje = (rule, value, callback) => {
  217 + const n = parseFloat(value);
  218 + if (
  219 + value === "" ||
  220 + value === undefined ||
  221 + value === null ||
  222 + isNaN(n) ||
  223 + n <= 0
  224 + ) {
  225 + callback(new Error("请填写明细金额,收款金额须大于 0"));
  226 + } else {
  227 + callback();
  228 + }
  229 + };
  230 + const validateMxList = (rule, value, callback) => {
  231 + const list = value || [];
  232 + if (!list.length) {
  233 + callback(new Error("请至少添加一条明细"));
  234 + return;
  235 + }
  236 + for (let i = 0; i < list.length; i++) {
  237 + const row = list[i];
  238 + if (
  239 + row.srxmmc === undefined ||
  240 + row.srxmmc === null ||
  241 + row.srxmmc === ""
  242 + ) {
  243 + callback(new Error(`第 ${i + 1} 行收入项目为必填`));
  244 + return;
  245 + }
  246 + const je = parseFloat(row.je);
  247 + if (
  248 + row.je === undefined ||
  249 + row.je === null ||
  250 + row.je === "" ||
  251 + isNaN(je) ||
  252 + je <= 0
  253 + ) {
  254 + callback(new Error(`第 ${i + 1} 行原币金额为必填且须大于 0`));
  255 + return;
  256 + }
  257 + }
  258 + callback();
  259 + };
  260 + return {
  261 + loading: false,
  262 + visible: false,
  263 + isDetail: false,
  264 + wldwLoading: false,
  265 + dataForm: {
  266 + id: undefined,
  267 + djrq: undefined,
  268 + jsr: undefined,
  269 + wldw: undefined,
  270 + wtYskzjjsMxList: [],
  271 + fkzh: undefined,
  272 + fkje: undefined,
  273 + skzh: undefined,
  274 + skje: undefined,
  275 + djlx: undefined,
  276 + djzt: "草稿"
  277 + },
  278 + rules: {
  279 + djrq: [
  280 + { required: true, message: "请选择单据日期", trigger: "change" }
  281 + ],
  282 + jsr: [{ required: true, message: "请选择经手人", trigger: "change" }],
  283 + wldw: [
  284 + { required: true, message: "请选择往来单位", trigger: "change" }
  285 + ],
  286 + skzh: [
  287 + { required: true, message: "请选择收款账户", trigger: "change" }
  288 + ],
  289 + skje: [{ validator: validateSkje, trigger: "blur" }],
  290 + wtYskzjjsMxList: [{ validator: validateMxList, trigger: "change" }]
  291 + },
  292 + srxmmcOptions: [],
  293 + fkzhOptions: [],
  294 + skzhOptions: [],
  295 + wldwOptions: []
  296 + };
  297 + },
  298 + computed: {},
  299 + watch: {},
  300 + created() {
  301 + this.getsrxmmcOptions();
  302 + this.getfkzhOptions();
  303 + this.getskzhOptions();
  304 + },
  305 + mounted() {},
  306 + methods: {
  307 + getsrxmmcOptions() {
  308 + getDictionaryDataSelector("715562947862070533").then(res => {
  309 + this.srxmmcOptions = res.data.list;
  310 + });
  311 + },
  312 + getfkzhOptions() {
  313 + getAccountSelector().then(res => {
  314 + this.fkzhOptions = res.data.list;
  315 + });
  316 + },
  317 + getskzhOptions() {
  318 + getAccountSelector().then(res => {
  319 + this.skzhOptions = res.data.list;
  320 + });
  321 + },
  322 + remoteWldw(query) {
  323 + this.wldwLoading = true;
  324 + request({
  325 + url: "/api/Extend/WtWldw",
  326 + method: "GET",
  327 + data: {
  328 + currentPage: 1,
  329 + pageSize: 50,
  330 + dwmc: query && String(query).trim() ? String(query).trim() : undefined
  331 + }
  332 + })
  333 + .then(res => {
  334 + const raw = res.data.list || res.data || [];
  335 + this.wldwOptions = raw
  336 + .map(x => {
  337 + const id = x && (x.id || x.F_Id || x.f_Id);
  338 + return { ...x, id: id != null ? String(id) : "" };
  339 + })
  340 + .filter(x => x.id);
  341 + })
  342 + .finally(() => {
  343 + this.wldwLoading = false;
  344 + });
  345 + },
  346 + ensureWldwOption(id) {
  347 + if (!id) return Promise.resolve();
  348 + const sid = String(id);
  349 + if (this.wldwOptions.some(x => String(x.id) === sid))
  350 + return Promise.resolve();
  351 + return request({
  352 + url: "/api/Extend/WtWldw",
  353 + method: "GET",
  354 + data: { currentPage: 1, pageSize: 20, id: sid }
  355 + }).then(res => {
  356 + const raw = res.data.list || res.data || [];
  357 + const hit = raw.find(x => String(x.id || x.F_Id || x.f_Id) === sid);
  358 + if (hit) {
  359 + const row = { ...hit, id: String(hit.id || hit.F_Id || hit.f_Id) };
  360 + this.wldwOptions = [
  361 + row,
  362 + ...this.wldwOptions.filter(x => String(x.id) !== sid)
  363 + ];
  364 + }
  365 + });
  366 + },
  367 + onWldwChange() {
  368 + this.syncKhFromWldw();
  369 + },
  370 + syncKhFromWldw() {
  371 + const w = this.dataForm.wldw;
  372 + if (!w || !this.dataForm.wtYskzjjsMxList) return;
  373 + this.dataForm.wtYskzjjsMxList.forEach(row => {
  374 + if (!row) return;
  375 + if (row.kh === undefined || row.kh === null || row.kh === "") {
  376 + this.$set(row, "kh", String(w));
  377 + }
  378 + });
  379 + },
  380 + // 计算明细表原币金额合计
  381 + calculateTotalAmount() {
  382 + let totalAmount = 0;
  383 + if (
  384 + this.dataForm.wtYskzjjsMxList &&
  385 + this.dataForm.wtYskzjjsMxList.length > 0
  386 + ) {
  387 + this.dataForm.wtYskzjjsMxList.forEach(row => {
  388 + const je = parseFloat(row.je) || 0;
  389 + totalAmount += je;
  390 + });
  391 + }
  392 + this.dataForm.skje = totalAmount > 0 ? totalAmount.toFixed(2) : "";
  393 + this.$nextTick(() => {
  394 + this.$refs.elForm && this.$refs.elForm.validateField("skje");
  395 + this.$refs.elForm && this.$refs.elForm.validateField("wtYskzjjsMxList");
  396 + });
  397 + },
  398 + goBack() {
  399 + this.$emit("refresh");
  400 + },
  401 + init(id, isDetail) {
  402 + this.dataForm.id = id || 0;
  403 + this.visible = true;
  404 + this.isDetail = isDetail || false;
  405 + this.$nextTick(() => {
  406 + if (this.$refs["elForm"]) this.$refs["elForm"].resetFields();
  407 + this.wldwOptions = [];
  408 + if (this.dataForm.id) {
  409 + request({
  410 + url: "/api/Extend/WtYskzjjs/" + this.dataForm.id,
  411 + method: "get"
  412 + }).then(res => {
  413 + this.dataForm = Object.assign({}, this.dataForm, res.data);
  414 + if (!this.dataForm.wtYskzjjsMxList)
  415 + this.dataForm.wtYskzjjsMxList = [];
  416 + if (this.dataForm.wldw != null && this.dataForm.wldw !== "") {
  417 + this.dataForm.wldw = String(this.dataForm.wldw);
  418 + }
  419 + this.$nextTick(() => {
  420 + this.ensureWldwOption(this.dataForm.wldw).then(() => {
  421 + this.remoteWldw("");
  422 + });
  423 + this.calculateTotalAmount();
  424 + });
  425 + });
  426 + } else {
  427 + this.dataForm.djlx = "其他应收单";
  428 + this.dataForm.djzt = "草稿";
  429 + this.dataForm.djrq = Date.now();
  430 + if (
  431 + this.$store &&
  432 + this.$store.getters &&
  433 + this.$store.getters.userInfo
  434 + ) {
  435 + this.dataForm.jsr =
  436 + this.$store.getters.userInfo.userId ||
  437 + this.$store.getters.userInfo.id;
  438 + }
  439 + this.dataForm.wtYskzjjsMxList = [];
  440 + this.$nextTick(() => {
  441 + this.remoteWldw("");
  442 + });
  443 + }
  444 + });
  445 + },
  446 + validateSubmitMessage() {
  447 + return new Promise(resolve => {
  448 + this.$refs["elForm"].validate(valid => {
  449 + if (!valid) {
  450 + this.$message.warning("请完善必填项后再提交(备注除外)");
  451 + resolve(false);
  452 + return;
  453 + }
  454 + const list = this.dataForm.wtYskzjjsMxList || [];
  455 + if (!list.length) {
  456 + this.$message.warning("请至少添加一条明细后再提交");
  457 + resolve(false);
  458 + return;
  459 + }
  460 + for (let i = 0; i < list.length; i++) {
  461 + const row = list[i];
  462 + if (
  463 + row.srxmmc === undefined ||
  464 + row.srxmmc === null ||
  465 + row.srxmmc === ""
  466 + ) {
  467 + this.$message.warning(`第 ${i + 1} 行收入项目为必填`);
  468 + resolve(false);
  469 + return;
  470 + }
  471 + const je = parseFloat(row.je);
  472 + if (
  473 + row.je === undefined ||
  474 + row.je === null ||
  475 + row.je === "" ||
  476 + isNaN(je) ||
  477 + je <= 0
  478 + ) {
  479 + this.$message.warning(`第 ${i + 1} 行原币金额须大于 0`);
  480 + resolve(false);
  481 + return;
  482 + }
  483 + }
  484 + resolve(true);
  485 + });
  486 + });
  487 + },
  488 + saveDraft() {
  489 + this.dataForm.djlx = "其他应收单";
  490 + this.dataForm.djzt = "草稿";
  491 + this.syncKhFromWldw();
  492 + if (!this.dataForm.djrq) {
  493 + this.$message.warning("请选择单据日期后再保存草稿");
  494 + return;
  495 + }
  496 + if (!this.dataForm.jsr) {
  497 + this.$message.warning("请选择经手人后再保存草稿");
  498 + return;
  499 + }
  500 + if (!this.dataForm.wldw) {
  501 + this.$message.warning("请选择往来单位后再保存草稿");
  502 + return;
  503 + }
  504 + this.persistBill();
  505 + },
  506 + async dataFormSubmit() {
  507 + this.dataForm.djlx = "其他应收单";
  508 + this.dataForm.djzt = "待审核";
  509 + this.syncKhFromWldw();
  510 + const ok = await this.validateSubmitMessage();
  511 + if (!ok) return;
  512 + this.persistBill();
  513 + },
  514 + persistBill() {
  515 + const payload = { ...this.dataForm };
  516 + this.syncKhFromWldw();
  517 + if (!payload.id) {
  518 + request({
  519 + url: `/api/Extend/WtYskzjjs`,
  520 + method: "post",
  521 + data: payload
  522 + }).then(res => {
  523 + this.$message({
  524 + message: res.msg,
  525 + type: "success",
  526 + duration: 1000,
  527 + onClose: () => {
  528 + this.visible = false;
  529 + this.$emit("refresh", true);
  530 + }
  531 + });
  532 + });
  533 + } else {
  534 + request({
  535 + url: "/api/Extend/WtYskzjjs/" + this.dataForm.id,
  536 + method: "PUT",
  537 + data: payload
  538 + }).then(res => {
  539 + this.$message({
  540 + message: res.msg,
  541 + type: "success",
  542 + duration: 1000,
  543 + onClose: () => {
  544 + this.visible = false;
  545 + this.$emit("refresh", true);
  546 + }
  547 + });
  548 + });
  549 + }
  550 + },
  551 + addHandleWtYskzjjsMxEntityList() {
  552 + let item = {
  553 + srxmmc: undefined,
  554 + je: undefined,
  555 + bz: undefined,
  556 + mxlx: "其他收入单",
  557 + kh: undefined
  558 + };
  559 + this.dataForm.wtYskzjjsMxList.push(item);
  560 + this.syncKhFromWldw();
  561 + this.$nextTick(() => {
  562 + this.calculateTotalAmount();
  563 + this.$refs.elForm && this.$refs.elForm.validateField("wtYskzjjsMxList");
  564 + });
  565 + },
  566 + handleDelWtYskzjjsMxEntityList(index) {
  567 + this.dataForm.wtYskzjjsMxList.splice(index, 1);
  568 + this.$nextTick(() => {
  569 + this.calculateTotalAmount();
  570 + this.$refs.elForm && this.$refs.elForm.validateField("wtYskzjjsMxList");
  571 + });
  572 + }
  573 + }
  574 +};
298 </script> 575 </script>
Antis.Erp.Plat/antis-ncc-admin/src/views/wtYskzjjs_qt/detail-view.vue
1 <template> 1 <template>
2 - <el-dialog  
3 - title="其他应收款详情"  
4 - :close-on-click-modal="false"  
5 - :visible.sync="visible"  
6 - class="NCC-dialog NCC-dialog_center qt-detail-dialog"  
7 - lock-scroll  
8 - width="960px"  
9 - top="6vh"  
10 - @close="handleClose"  
11 - >  
12 - <div v-loading="loading" class="qt-detail-body">  
13 - <template v-if="detail">  
14 - <el-descriptions :column="2" border size="small">  
15 - <el-descriptions-item label="单据编号">{{ cellText(detail.id) }}</el-descriptions-item>  
16 - <el-descriptions-item label="审核状态">{{ detail.djzt || detail.shzt || '未审核' }}</el-descriptions-item>  
17 - <el-descriptions-item label="日期">{{ formatDate(detail.djrq) }}</el-descriptions-item>  
18 - <el-descriptions-item label="往来单位">{{ wldwText(detail.wldw) }}</el-descriptions-item>  
19 - <el-descriptions-item label="金额">{{ money(detail.skje) }}</el-descriptions-item>  
20 - <el-descriptions-item label="经手人">{{ cellText(detail.jsr) }}</el-descriptions-item>  
21 - <el-descriptions-item label="摘要" :span="2">  
22 - <ncc-bill-summary :value="detail.billZy || detail.zy || detail.bz" mode="block" :show-icon="false" />  
23 - </el-descriptions-item>  
24 - </el-descriptions> 2 + <el-dialog
  3 + title="其他应收单详情"
  4 + :close-on-click-modal="false"
  5 + :visible.sync="visible"
  6 + class="NCC-dialog NCC-dialog_center qt-detail-dialog"
  7 + lock-scroll
  8 + width="960px"
  9 + top="6vh"
  10 + @close="handleClose"
  11 + >
  12 + <div v-loading="loading" class="qt-detail-body">
  13 + <template v-if="detail">
  14 + <el-descriptions :column="2" border size="small">
  15 + <el-descriptions-item label="单据编号">{{
  16 + cellText(detail.id)
  17 + }}</el-descriptions-item>
  18 + <el-descriptions-item label="审核状态">{{
  19 + cellText(detail.djzt || detail.shzt || "未审核")
  20 + }}</el-descriptions-item>
  21 + <el-descriptions-item label="日期">{{
  22 + formatDate(detail.djrq)
  23 + }}</el-descriptions-item>
  24 + <el-descriptions-item label="往来单位">{{
  25 + cellText(detail.wldwmc || wldwText(detail.wldw))
  26 + }}</el-descriptions-item>
  27 + <el-descriptions-item label="金额">{{
  28 + money(detail.skje)
  29 + }}</el-descriptions-item>
  30 + <el-descriptions-item label="经手人">{{
  31 + cellText(detail.jsr)
  32 + }}</el-descriptions-item>
  33 + <el-descriptions-item label="审批备注" :span="2">{{
  34 + cellText(detail.spbz)
  35 + }}</el-descriptions-item>
  36 + <el-descriptions-item label="摘要" :span="2">
  37 + <ncc-bill-summary
  38 + :value="detail.billZy || detail.zy || detail.bz"
  39 + mode="block"
  40 + :show-icon="false"
  41 + />
  42 + </el-descriptions-item>
  43 + </el-descriptions>
25 44
26 - <div class="mx-title">明细</div>  
27 - <el-table :data="detail.wtYskzjjsMxList || []" border size="small" class="mx-table" empty-text="无">  
28 - <el-table-column type="index" width="52" label="#" align="center" />  
29 - <el-table-column label="往来单位" min-width="150" show-overflow-tooltip>  
30 - <template slot-scope="scope">{{ wldwText(scope.row.kh) }}</template>  
31 - </el-table-column>  
32 - <el-table-column label="收入项目" min-width="180" show-overflow-tooltip>  
33 - <template slot-scope="scope">{{ srxmText(scope.row.srxmmc) }}</template>  
34 - </el-table-column>  
35 - <el-table-column prop="je" label="金额" width="120" align="right">  
36 - <template slot-scope="scope">{{ money(scope.row.je) }}</template>  
37 - </el-table-column>  
38 - <el-table-column label="备注" min-width="180" show-overflow-tooltip>  
39 - <template slot-scope="scope">{{ cellText(scope.row.bz) }}</template>  
40 - </el-table-column>  
41 - </el-table>  
42 - </template>  
43 - </div>  
44 - </el-dialog> 45 + <div class="mx-title">明细</div>
  46 + <el-table
  47 + :data="detail.wtYskzjjsMxList || []"
  48 + border
  49 + size="small"
  50 + class="mx-table"
  51 + empty-text="无"
  52 + >
  53 + <el-table-column type="index" width="52" label="#" align="center" />
  54 + <el-table-column
  55 + label="往来单位"
  56 + min-width="150"
  57 + show-overflow-tooltip
  58 + >
  59 + <template slot-scope="scope">{{
  60 + wldwText(scope.row.kh || detail.wldw)
  61 + }}</template>
  62 + </el-table-column>
  63 + <el-table-column
  64 + label="收入项目"
  65 + min-width="180"
  66 + show-overflow-tooltip
  67 + >
  68 + <template slot-scope="scope">{{
  69 + srxmText(scope.row.srxmmc)
  70 + }}</template>
  71 + </el-table-column>
  72 + <el-table-column prop="je" label="金额" width="120" align="right">
  73 + <template slot-scope="scope">{{ money(scope.row.je) }}</template>
  74 + </el-table-column>
  75 + <el-table-column label="备注" min-width="180" show-overflow-tooltip>
  76 + <template slot-scope="scope">{{ cellText(scope.row.bz) }}</template>
  77 + </el-table-column>
  78 + </el-table>
  79 + </template>
  80 + </div>
  81 +
  82 + <template slot="footer">
  83 + <slot name="footer">
  84 + <el-button @click="close">关闭</el-button>
  85 + </slot>
  86 + </template>
  87 + </el-dialog>
45 </template> 88 </template>
46 89
47 <script> 90 <script>
48 -import request from '@/utils/request'  
49 -import { getDictionaryDataSelector } from '@/api/systemData/dictionary' 91 +import request from "@/utils/request";
  92 +import { getDictionaryDataSelector } from "@/api/systemData/dictionary";
50 93
51 export default { 94 export default {
52 - name: 'WtYskzjjsQtDetailView',  
53 - data() {  
54 - return {  
55 - visible: false,  
56 - loading: false,  
57 - detail: null,  
58 - wldwOptions: [],  
59 - srxmmcOptions: []  
60 - }  
61 - },  
62 - created() {  
63 - this.loadWldw()  
64 - this.loadSrxmmc()  
65 - },  
66 - methods: {  
67 - loadWldw() {  
68 - request({  
69 - url: '/api/Extend/WtWldw',  
70 - method: 'GET',  
71 - data: { currentPage: 1, pageSize: 5000 }  
72 - }).then(res => {  
73 - this.wldwOptions = res.data.list || res.data || []  
74 - })  
75 - },  
76 - loadSrxmmc() {  
77 - getDictionaryDataSelector('715562947862070533').then(res => {  
78 - this.srxmmcOptions = res.data.list || []  
79 - })  
80 - },  
81 - cellText(v) {  
82 - return v === undefined || v === null || v === '' ? '无' : v  
83 - },  
84 - money(v) {  
85 - const n = Number(v)  
86 - return isNaN(n) ? '0.00' : n.toFixed(2)  
87 - },  
88 - formatDate(v) {  
89 - if (!v) return '无'  
90 - const d = new Date(v)  
91 - if (isNaN(d.getTime())) return '无'  
92 - return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`  
93 - },  
94 - wldwText(id) {  
95 - if (!id) return '无'  
96 - const hit = this.wldwOptions.find(item => String(item.id) === String(id))  
97 - return hit && hit.dwmc ? hit.dwmc : id  
98 - },  
99 - srxmText(v) {  
100 - if (!v) return '无'  
101 - const hit = this.srxmmcOptions.find(item => String(item.id) === String(v))  
102 - return hit && hit.fullName ? hit.fullName : v  
103 - },  
104 - handleClose() {  
105 - this.detail = null  
106 - this.$emit('close')  
107 - },  
108 - init(id) {  
109 - if (!id) return  
110 - this.visible = true  
111 - this.loading = true  
112 - request({  
113 - url: `/api/Extend/WtYskzjjs/${id}`,  
114 - method: 'GET'  
115 - }).then(res => {  
116 - this.detail = res.data || null  
117 - if (this.detail && !this.detail.wtYskzjjsMxList) {  
118 - this.$set(this.detail, 'wtYskzjjsMxList', [])  
119 - }  
120 - }).finally(() => {  
121 - this.loading = false  
122 - })  
123 - }  
124 - }  
125 -} 95 + name: "WtYskzjjsQtDetailView",
  96 + data() {
  97 + return {
  98 + visible: false,
  99 + loading: false,
  100 + detail: null,
  101 + wldwOptions: [],
  102 + srxmmcOptions: []
  103 + };
  104 + },
  105 + created() {
  106 + this.loadWldw();
  107 + this.loadSrxmmc();
  108 + },
  109 + methods: {
  110 + loadWldw() {
  111 + request({
  112 + url: "/api/Extend/WtWldw",
  113 + method: "GET",
  114 + data: { currentPage: 1, pageSize: 5000 }
  115 + }).then(res => {
  116 + this.wldwOptions = res.data.list || res.data || [];
  117 + });
  118 + },
  119 + loadSrxmmc() {
  120 + getDictionaryDataSelector("715562947862070533").then(res => {
  121 + this.srxmmcOptions = res.data.list || [];
  122 + });
  123 + },
  124 + cellText(v) {
  125 + return v === undefined || v === null || v === "" ? "无" : v;
  126 + },
  127 + money(v) {
  128 + const n = Number(v);
  129 + return isNaN(n) ? "0.00" : n.toFixed(2);
  130 + },
  131 + formatDate(v) {
  132 + if (!v) return "无";
  133 + const d = new Date(v);
  134 + if (isNaN(d.getTime())) return "无";
  135 + return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(
  136 + 2,
  137 + "0"
  138 + )}-${String(d.getDate()).padStart(2, "0")}`;
  139 + },
  140 + wldwText(id) {
  141 + if (!id) return "无";
  142 + const hit = this.wldwOptions.find(item => String(item.id) === String(id));
  143 + return hit && hit.dwmc ? hit.dwmc : id;
  144 + },
  145 + srxmText(v) {
  146 + if (!v) return "无";
  147 + const hit = this.srxmmcOptions.find(
  148 + item => String(item.id) === String(v)
  149 + );
  150 + return hit && hit.fullName ? hit.fullName : v;
  151 + },
  152 + handleClose() {
  153 + this.detail = null;
  154 + this.$emit("close");
  155 + },
  156 + close() {
  157 + this.visible = false;
  158 + },
  159 + init(id) {
  160 + if (!id) return;
  161 + this.visible = true;
  162 + this.loading = true;
  163 + request({
  164 + url: `/api/Extend/WtYskzjjs/${id}`,
  165 + method: "GET"
  166 + })
  167 + .then(res => {
  168 + this.detail = res.data || null;
  169 + if (this.detail && !this.detail.wtYskzjjsMxList) {
  170 + this.$set(this.detail, "wtYskzjjsMxList", []);
  171 + }
  172 + this.$emit("loaded", this.detail);
  173 + })
  174 + .finally(() => {
  175 + this.loading = false;
  176 + });
  177 + }
  178 + }
  179 +};
126 </script> 180 </script>
127 181
128 <style scoped lang="scss"> 182 <style scoped lang="scss">
129 .mx-title { 183 .mx-title {
130 - margin: 14px 0 10px;  
131 - font-weight: 600; 184 + margin: 14px 0 10px;
  185 + font-weight: 600;
132 } 186 }
133 .mx-table ::v-deep .cell { 187 .mx-table ::v-deep .cell {
134 - white-space: nowrap; 188 + white-space: nowrap;
135 } 189 }
136 .mx-table { 190 .mx-table {
137 - margin-bottom: 12px; 191 + margin-bottom: 12px;
138 } 192 }
139 .qt-detail-body { 193 .qt-detail-body {
140 - padding-bottom: 8px; 194 + padding-bottom: 8px;
141 } 195 }
142 .qt-detail-dialog ::v-deep .el-dialog__body { 196 .qt-detail-dialog ::v-deep .el-dialog__body {
143 - padding-bottom: 20px; 197 + padding-bottom: 20px;
144 } 198 }
145 </style> 199 </style>
Antis.Erp.Plat/antis-ncc-admin/src/views/wtYskzjjs_qt/index.vue
1 <template> 1 <template>
2 - <div class="NCC-common-layout">  
3 - <div class="NCC-common-layout-center">  
4 - <el-row class="NCC-common-search-box" :gutter="16">  
5 - <el-form @submit.native.prevent>  
6 - <el-col :span="6">  
7 - <el-form-item label="单据编号">  
8 - <el-input v-model="query.id" placeholder="单据编号" clearable />  
9 - </el-form-item>  
10 - </el-col>  
11 - <el-col :span="6">  
12 - <el-form-item label="单据日期">  
13 - <el-date-picker v-model="query.djrq" type="daterange" value-format="timestamp" format="yyyy-MM-dd" start-placeholder="开始日期" end-placeholder="结束日期">  
14 - </el-date-picker>  
15 - </el-form-item>  
16 - </el-col>  
17 - <template v-if="showAll">  
18 - <el-col :span="6">  
19 - <el-form-item label="往来单位">  
20 - <el-select v-model="query.kh" placeholder="请选择往来单位" clearable filterable >  
21 - <el-option v-for="(item, index) in wldwOptions" :key="index" :label="item.dwmc" :value="item.id" />  
22 - </el-select>  
23 - </el-form-item>  
24 - </el-col>  
25 - <el-col :span="6">  
26 - <el-form-item label="经手人">  
27 - <userSelect v-model="query.jsr" placeholder="请选择经手人" />  
28 - </el-form-item>  
29 - </el-col>  
30 - </template>  
31 - <el-col :span="6">  
32 - <el-form-item>  
33 - <el-button type="primary" icon="el-icon-search" @click="search()">查询</el-button>  
34 - <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button>  
35 - <el-button type="text" icon="el-icon-arrow-down" @click="showAll=true" v-if="!showAll">展开</el-button>  
36 - <el-button type="text" icon="el-icon-arrow-up" @click="showAll=false" v-else>收起</el-button>  
37 - </el-form-item>  
38 - </el-col>  
39 - </el-form>  
40 - </el-row>  
41 - <div class="NCC-common-layout-main NCC-flex-main">  
42 - <div class="NCC-common-head">  
43 - <div>  
44 - <el-button type="primary" icon="el-icon-plus" @click="addOrUpdateHandle()">新增</el-button>  
45 - <el-button type="text" icon="el-icon-download" @click="exportData()">导出</el-button>  
46 - <el-button type="text" icon="el-icon-delete" @click="handleBatchRemoveDel()">批量删除</el-button>  
47 - </div>  
48 - <div class="NCC-common-head-right">  
49 - <el-tooltip effect="dark" content="刷新" placement="top">  
50 - <el-link icon="icon-ym icon-ym-Refresh NCC-common-head-icon" :underline="false" @click="reset()" />  
51 - </el-tooltip>  
52 - <screenfull isContainer />  
53 - </div>  
54 - </div>  
55 - <NCC-table v-loading="listLoading" :data="list" has-c @selection-change="handleSelectionChange">  
56 - <el-table-column prop="djrq" label="单据日期" align="left">  
57 - <template slot-scope="scope">  
58 - {{ scope.row.djrq ? (new Date(scope.row.djrq).toLocaleDateString('zh-CN', {year:'numeric',month:'2-digit',day:'2-digit'}).replace(/\//g,'-')) : '' }}  
59 - </template>  
60 - </el-table-column>  
61 - <el-table-column prop="id" label="单据编号" align="left" />  
62 - <el-table-column label="审核状态" align="left" width="100">  
63 - <template slot-scope="scope">  
64 - {{ scope.row.djzt || scope.row.shzt || '未审核' }}  
65 - </template>  
66 - </el-table-column>  
67 - <el-table-column label="往来单位" align="left" min-width="160" show-overflow-tooltip>  
68 - <template slot-scope="scope">  
69 - {{ wldwText(scope.row.wldw || scope.row.kh) }}  
70 - </template>  
71 - </el-table-column>  
72 - <el-table-column prop="skje" label="金额" align="left" />  
73 - <el-table-column prop="jsr" label="经手人" align="left" />  
74 - <el-table-column label="摘要" align="left" min-width="200">  
75 - <template slot-scope="scope">  
76 - <ncc-table-summary-cell :row="scope.row" fields="billZy,BillZy,zy,Zy,bz,Bz" />  
77 - </template>  
78 - </el-table-column>  
79 - <el-table-column label="操作" fixed="right" width="140">  
80 - <template slot-scope="scope">  
81 - <el-button type="text" @click="openDetail(scope.row.id)" >查看</el-button>  
82 - <el-button type="text" @click="addOrUpdateHandle(scope.row.id)" >编辑</el-button>  
83 - <el-button type="text" @click="handleDel(scope.row.id)" class="NCC-table-delBtn" >删除</el-button>  
84 - </template>  
85 - </el-table-column>  
86 - </NCC-table>  
87 - <pagination :total="total" :page.sync="listQuery.currentPage" :limit.sync="listQuery.pageSize" @pagination="initData" />  
88 - </div> 2 + <div class="NCC-common-layout">
  3 + <div class="NCC-common-layout-center">
  4 + <el-row class="NCC-common-search-box" :gutter="16">
  5 + <el-form @submit.native.prevent>
  6 + <el-col :span="6">
  7 + <el-form-item label="单据编号">
  8 + <el-input v-model="query.id" placeholder="单据编号" clearable />
  9 + </el-form-item>
  10 + </el-col>
  11 + <el-col :span="6">
  12 + <el-form-item label="单据日期">
  13 + <el-date-picker
  14 + v-model="query.djrq"
  15 + type="daterange"
  16 + value-format="timestamp"
  17 + format="yyyy-MM-dd"
  18 + start-placeholder="开始日期"
  19 + end-placeholder="结束日期"
  20 + >
  21 + </el-date-picker>
  22 + </el-form-item>
  23 + </el-col>
  24 + <template v-if="showAll">
  25 + <el-col :span="6">
  26 + <el-form-item label="往来单位">
  27 + <el-select
  28 + v-model="query.kh"
  29 + placeholder="请选择往来单位"
  30 + clearable
  31 + filterable
  32 + >
  33 + <el-option
  34 + v-for="(item, index) in wldwOptions"
  35 + :key="index"
  36 + :label="item.dwmc"
  37 + :value="item.id"
  38 + />
  39 + </el-select>
  40 + </el-form-item>
  41 + </el-col>
  42 + <el-col :span="6">
  43 + <el-form-item label="经手人">
  44 + <userSelect v-model="query.jsr" placeholder="请选择经手人" />
  45 + </el-form-item>
  46 + </el-col>
  47 + </template>
  48 + <el-col :span="6">
  49 + <el-form-item>
  50 + <el-button type="primary" icon="el-icon-search" @click="search()"
  51 + >查询</el-button
  52 + >
  53 + <el-button icon="el-icon-refresh-right" @click="reset()"
  54 + >重置</el-button
  55 + >
  56 + <el-button
  57 + type="text"
  58 + icon="el-icon-arrow-down"
  59 + @click="showAll = true"
  60 + v-if="!showAll"
  61 + >展开</el-button
  62 + >
  63 + <el-button
  64 + type="text"
  65 + icon="el-icon-arrow-up"
  66 + @click="showAll = false"
  67 + v-else
  68 + >收起</el-button
  69 + >
  70 + </el-form-item>
  71 + </el-col>
  72 + </el-form>
  73 + </el-row>
  74 + <div class="NCC-common-layout-main NCC-flex-main">
  75 + <div class="NCC-common-head">
  76 + <div>
  77 + <el-button
  78 + type="primary"
  79 + icon="el-icon-plus"
  80 + @click="addOrUpdateHandle()"
  81 + >新增</el-button
  82 + >
  83 + <el-button type="text" icon="el-icon-download" @click="exportData()"
  84 + >导出</el-button
  85 + >
  86 + <el-button
  87 + type="text"
  88 + icon="el-icon-delete"
  89 + @click="handleBatchRemoveDel()"
  90 + >批量删除</el-button
  91 + >
  92 + </div>
  93 + <div class="NCC-common-head-right">
  94 + <el-tooltip effect="dark" content="刷新" placement="top">
  95 + <el-link
  96 + icon="icon-ym icon-ym-Refresh NCC-common-head-icon"
  97 + :underline="false"
  98 + @click="reset()"
  99 + />
  100 + </el-tooltip>
  101 + <screenfull isContainer />
  102 + </div>
89 </div> 103 </div>
90 - <NCC-Form v-if="formVisible" ref="NCCForm" @refresh="refresh" />  
91 - <DetailView v-if="detailVisible" ref="NCCDetailView" @close="detailVisible = false" />  
92 - <ExportBox v-if="exportBoxVisible" ref="ExportBox" @download="download" />  
93 - </div> 104 + <NCC-table
  105 + v-loading="listLoading"
  106 + :data="list"
  107 + has-c
  108 + @selection-change="handleSelectionChange"
  109 + >
  110 + <el-table-column prop="djrq" label="单据日期" align="left">
  111 + <template slot-scope="scope">
  112 + {{
  113 + scope.row.djrq
  114 + ? new Date(scope.row.djrq)
  115 + .toLocaleDateString("zh-CN", {
  116 + year: "numeric",
  117 + month: "2-digit",
  118 + day: "2-digit"
  119 + })
  120 + .replace(/\//g, "-")
  121 + : ""
  122 + }}
  123 + </template>
  124 + </el-table-column>
  125 + <el-table-column prop="id" label="单据编号" align="left" />
  126 + <el-table-column label="审核状态" align="left" width="130">
  127 + <template slot-scope="scope">
  128 + <el-tag
  129 + v-if="auditStatus(scope.row) === '草稿'"
  130 + type="info"
  131 + size="mini"
  132 + >草稿</el-tag
  133 + >
  134 + <el-tag
  135 + v-else-if="auditStatus(scope.row) === '待审核'"
  136 + type="warning"
  137 + size="mini"
  138 + >待审核</el-tag
  139 + >
  140 + <el-tag
  141 + v-else-if="
  142 + auditStatus(scope.row) === '一级已审' ||
  143 + auditStatus(scope.row) === '待二级' ||
  144 + auditStatus(scope.row) === '一级已审/待二级'
  145 + "
  146 + type="warning"
  147 + size="mini"
  148 + >一级已审</el-tag
  149 + >
  150 + <el-tag
  151 + v-else-if="auditStatus(scope.row) === '已审核'"
  152 + type="success"
  153 + size="mini"
  154 + >已审核</el-tag
  155 + >
  156 + <el-tag
  157 + v-else-if="auditStatus(scope.row) === '审核不通过'"
  158 + type="danger"
  159 + size="mini"
  160 + >审核不通过</el-tag
  161 + >
  162 + <el-tag v-else type="info" size="mini">{{
  163 + cellText(auditStatus(scope.row) || "未审核")
  164 + }}</el-tag>
  165 + </template>
  166 + </el-table-column>
  167 + <el-table-column
  168 + label="往来单位"
  169 + align="left"
  170 + min-width="160"
  171 + show-overflow-tooltip
  172 + >
  173 + <template slot-scope="scope">
  174 + {{ showWldw(scope.row) }}
  175 + </template>
  176 + </el-table-column>
  177 + <el-table-column prop="skje" label="金额" align="left" />
  178 + <el-table-column prop="jsr" label="经手人" align="left" />
  179 + <el-table-column label="摘要" align="left" min-width="200">
  180 + <template slot-scope="scope">
  181 + <ncc-table-summary-cell
  182 + :row="scope.row"
  183 + fields="billZy,BillZy,zy,Zy,bz,Bz"
  184 + />
  185 + </template>
  186 + </el-table-column>
  187 + <el-table-column label="操作" fixed="right" width="300">
  188 + <template slot-scope="scope">
  189 + <el-button type="text" @click="openDetail(scope.row.id)"
  190 + >查看</el-button
  191 + >
  192 + <el-button type="text" @click="openAuditInfo(scope.row)"
  193 + >审核信息</el-button
  194 + >
  195 + <el-button
  196 + v-if="canEditRow(scope.row)"
  197 + type="text"
  198 + @click="addOrUpdateHandle(scope.row.id)"
  199 + >编辑</el-button
  200 + >
  201 + <el-button
  202 + type="text"
  203 + style="color:#E6A23C"
  204 + v-if="canSubmitAudit(scope.row)"
  205 + @click="handleSubmitAudit(scope.row.id)"
  206 + >提交审核</el-button
  207 + >
  208 + <el-button
  209 + type="text"
  210 + v-if="canWithdrawAudit(scope.row)"
  211 + @click="handleWithdrawAudit(scope.row.id)"
  212 + >撤回</el-button
  213 + >
  214 + <el-button
  215 + type="text"
  216 + style="color:#E6A23C"
  217 + v-if="canLevel1Approve(scope.row)"
  218 + @click="handleLevel1Approve(scope.row.id)"
  219 + >一级审核</el-button
  220 + >
  221 + <el-button
  222 + type="text"
  223 + style="color:#67C23A"
  224 + v-if="canLevel2Approve(scope.row)"
  225 + @click="handleLevel2Approve(scope.row.id)"
  226 + >二级审核</el-button
  227 + >
  228 + <el-button
  229 + type="text"
  230 + style="color:#F56C6C"
  231 + v-if="canReject(scope.row)"
  232 + @click="handleReject(scope.row.id)"
  233 + >审核不通过</el-button
  234 + >
  235 + <el-button
  236 + type="text"
  237 + @click="handleDel(scope.row.id)"
  238 + class="NCC-table-delBtn"
  239 + v-if="canEditRow(scope.row)"
  240 + >删除</el-button
  241 + >
  242 + </template>
  243 + </el-table-column>
  244 + </NCC-table>
  245 + <pagination
  246 + :total="total"
  247 + :page.sync="listQuery.currentPage"
  248 + :limit.sync="listQuery.pageSize"
  249 + @pagination="initData"
  250 + />
  251 + </div>
  252 + </div>
  253 + <NCC-Form v-if="formVisible" ref="NCCForm" @refresh="refresh" />
  254 + <DetailView
  255 + v-if="detailVisible"
  256 + ref="NCCDetailView"
  257 + @close="detailVisible = false"
  258 + />
  259 + <ExportBox v-if="exportBoxVisible" ref="ExportBox" @download="download" />
  260 + </div>
94 </template> 261 </template>
95 <script> 262 <script>
96 - import request from '@/utils/request'  
97 - import NCCForm from './Form'  
98 - import DetailView from './detail-view'  
99 - import ExportBox from './ExportBox'  
100 - export default {  
101 - components: { NCCForm, DetailView, ExportBox },  
102 - data() {  
103 - const billType = '其他应收款'  
104 - return {  
105 - billType,  
106 - showAll: false,  
107 - query: {  
108 - id:undefined,  
109 - djrq:undefined,  
110 - kh:undefined,  
111 - jsr:undefined,  
112 - djlx: billType,  
113 - },  
114 - list: [],  
115 - listLoading: true,  
116 - multipleSelection: [], total: 0,  
117 - listQuery: {  
118 - currentPage: 1,  
119 - pageSize: 20,  
120 - sort: "desc",  
121 - sidx: "",  
122 - },  
123 - formVisible: false,  
124 - detailVisible: false,  
125 - exportBoxVisible: false,  
126 - columnList: [  
127 - { prop: 'djrq', label: '单据日期' },  
128 - { prop: 'id', label: '单据编号' },  
129 - { prop: 'djzt', label: '审核状态' },  
130 - { prop: 'wldw', label: '往来单位' },  
131 - { prop: 'skje', label: '金额' },  
132 - { prop: 'jsr', label: '经手人' },  
133 - { prop: 'zy', label: '摘要' },  
134 - ],  
135 - wldwOptions : [],  
136 - }  
137 - },  
138 - computed: {},  
139 - created() {  
140 - this.initData()  
141 - this.getWldwOptions();  
142 - },  
143 - methods: {  
144 - getWldwOptions() {  
145 - request({  
146 - url: '/api/Extend/WtWldw',  
147 - method: 'GET',  
148 - data: { currentPage: 1, pageSize: 5000 }  
149 - }).then(res => {  
150 - this.wldwOptions = res.data.list || res.data || []  
151 - })  
152 - },  
153 - wldwText(val) {  
154 - if (!val) return '无'  
155 - const hit = this.wldwOptions.find(item => String(item.id) === String(val))  
156 - return hit && hit.dwmc ? hit.dwmc : val  
157 - },  
158 - openDetail(id) {  
159 - if (!id) return  
160 - this.detailVisible = true  
161 - this.$nextTick(() => {  
162 - this.$refs.NCCDetailView && this.$refs.NCCDetailView.init(id)  
163 - })  
164 - },  
165 - initData() {  
166 - this.listLoading = true;  
167 - let _query = {  
168 - ...this.listQuery,  
169 - ...this.query  
170 - };  
171 - let query = {}  
172 - for (let key in _query) {  
173 - if (Array.isArray(_query[key])) {  
174 - query[key] = _query[key].join()  
175 - } else {  
176 - query[key] = _query[key]  
177 - }  
178 - }  
179 - request({  
180 - url: `/api/Extend/WtYskzjjs`,  
181 - method: 'GET',  
182 - data: query  
183 - }).then(res => {  
184 - this.list = res.data.list  
185 - this.total = res.data.pagination.total  
186 - this.listLoading = false  
187 - })  
188 - },  
189 - handleDel(id) {  
190 - this.$confirm('此操作将永久删除该数据, 是否继续?', '提示', {  
191 - type: 'warning'  
192 - }).then(() => {  
193 - request({  
194 - url: `/api/Extend/WtYskzjjs/${id}`,  
195 - method: 'DELETE'  
196 - }).then(res => {  
197 - this.$message({  
198 - type: 'success',  
199 - message: res.msg,  
200 - onClose: () => {  
201 - this.initData()  
202 - }  
203 - });  
204 - })  
205 - }).catch(() => {  
206 - });  
207 - },  
208 - handleSelectionChange(val) {  
209 - const res = val.map(item => item.id)  
210 - this.multipleSelection = res  
211 - },  
212 - handleBatchRemoveDel() {  
213 - if (!this.multipleSelection.length) {  
214 - this.$message({  
215 - type: 'error',  
216 - message: '请选择一条数据',  
217 - duration: 1500,  
218 - })  
219 - return  
220 - }  
221 - const ids = this.multipleSelection  
222 - this.$confirm('您确定要删除这些数据吗, 是否继续?', '提示', {  
223 - type: 'warning'  
224 - }).then(() => {  
225 - request({  
226 - url: `/api/Extend/WtYskzjjs/batchRemove`,  
227 - method: 'POST',  
228 - data: ids ,  
229 - }).then(res => {  
230 - this.$message({  
231 - type: 'success',  
232 - message: res.msg,  
233 - onClose: () => {  
234 - this.initData()  
235 - }  
236 - });  
237 - })  
238 - }).catch(() => { })  
239 - },  
240 - addOrUpdateHandle(id, isDetail) {  
241 - this.formVisible = true  
242 - this.$nextTick(() => {  
243 - this.$refs.NCCForm.init(id, isDetail)  
244 - })  
245 - },  
246 - exportData() {  
247 - this.exportBoxVisible = true  
248 - this.$nextTick(() => {  
249 - this.$refs.ExportBox.init(this.columnList)  
250 - })  
251 - },  
252 - download(data) {  
253 - let query = { ...data, ...this.listQuery, ...this.query }  
254 - request({  
255 - url: `/api/Extend/WtYskzjjs/Actions/Export`,  
256 - method: 'GET',  
257 - data: query  
258 - }).then(res => {  
259 - if (!res.data.url) return  
260 - window.location.href = this.define.comUrl + res.data.url  
261 - this.$refs.ExportBox.visible = false  
262 - this.exportBoxVisible = false  
263 - })  
264 - },  
265 - search() {  
266 - this.listQuery = {  
267 - currentPage: 1,  
268 - pageSize: 20,  
269 - sort: "desc",  
270 - sidx: "",  
271 - }  
272 - this.query.djlx = this.billType  
273 - this.initData()  
274 - },  
275 - refresh(isrRefresh) {  
276 - this.formVisible = false  
277 - if (isrRefresh) this.reset()  
278 - },  
279 - reset() {  
280 - for (let key in this.query) {  
281 - this.query[key] = undefined  
282 - }  
283 - this.query.djlx = this.billType  
284 - this.listQuery = {  
285 - currentPage: 1,  
286 - pageSize: 20,  
287 - sort: "desc",  
288 - sidx: "",  
289 - }  
290 - this.initData()  
291 - }  
292 - } 263 +import request from "@/utils/request";
  264 +import NCCForm from "./Form";
  265 +import DetailView from "./detail-view";
  266 +import ExportBox from "./ExportBox";
  267 +import {
  268 + promptApprovalRemark,
  269 + postYskzjjsSubmitForAudit,
  270 + postYskzjjsWithdrawAudit,
  271 + postYskzjjsApprove,
  272 + postYskzjjsReject
  273 +} from "@/utils/wtRejectApproval";
  274 +export default {
  275 + components: { NCCForm, DetailView, ExportBox },
  276 + data() {
  277 + const billType = "其他应收单";
  278 + return {
  279 + billType,
  280 + showAll: false,
  281 + query: {
  282 + id: undefined,
  283 + djrq: undefined,
  284 + kh: undefined,
  285 + jsr: undefined,
  286 + djlx: billType
  287 + },
  288 + list: [],
  289 + listLoading: true,
  290 + multipleSelection: [],
  291 + total: 0,
  292 + listQuery: {
  293 + currentPage: 1,
  294 + pageSize: 20,
  295 + sort: "desc",
  296 + sidx: ""
  297 + },
  298 + formVisible: false,
  299 + detailVisible: false,
  300 + exportBoxVisible: false,
  301 + columnList: [
  302 + { prop: "djrq", label: "单据日期" },
  303 + { prop: "id", label: "单据编号" },
  304 + { prop: "djzt", label: "审核状态" },
  305 + { prop: "wldw", label: "往来单位" },
  306 + { prop: "skje", label: "金额" },
  307 + { prop: "jsr", label: "经手人" },
  308 + { prop: "zy", label: "摘要" }
  309 + ],
  310 + wldwOptions: []
  311 + };
  312 + },
  313 + computed: {},
  314 + created() {
  315 + this.initData();
  316 + this.getWldwOptions();
  317 + },
  318 + methods: {
  319 + cellText(v) {
  320 + if (v === null || v === undefined || v === "") return "无";
  321 + return v;
  322 + },
  323 + auditStatus(row) {
  324 + if (!row) return "";
  325 + return (row.djzt || row.shzt || "").trim();
  326 + },
  327 + showWldw(row) {
  328 + if (!row) return "无";
  329 + if (row.wldwmc && row.wldwmc !== "无") return row.wldwmc;
  330 + return this.wldwText(row.wldw || row.kh);
  331 + },
  332 + canEditRow(row) {
  333 + const z = this.auditStatus(row);
  334 + return !z || z === "草稿" || z === "审核不通过";
  335 + },
  336 + canSubmitAudit(row) {
  337 + const z = this.auditStatus(row);
  338 + return z === "草稿" || z === "审核不通过";
  339 + },
  340 + canWithdrawAudit(row) {
  341 + const z = this.auditStatus(row);
  342 + return (
  343 + z === "待审核" ||
  344 + z === "一级已审" ||
  345 + z === "待二级" ||
  346 + z === "一级已审/待二级"
  347 + );
  348 + },
  349 + canLevel1Approve(row) {
  350 + const z = this.auditStatus(row);
  351 + return z === "待审核" || !z;
  352 + },
  353 + canLevel2Approve(row) {
  354 + const z = this.auditStatus(row);
  355 + return z === "一级已审" || z === "待二级" || z === "一级已审/待二级";
  356 + },
  357 + canReject(row) {
  358 + return this.canLevel1Approve(row) || this.canLevel2Approve(row);
  359 + },
  360 + openAuditInfo(row) {
  361 + const z = this.auditStatus(row) || "无";
  362 + const sp = row && row.spbz ? String(row.spbz) : "无";
  363 + this.$alert(
  364 + `审核状态:${this.cellText(z)}\n\n审批备注:\n${this.cellText(sp)}`,
  365 + "审核信息"
  366 + );
  367 + },
  368 + handleSubmitAudit(id) {
  369 + if (!id) return;
  370 + this.$confirm("确认提交审核?", "提示", { type: "warning" })
  371 + .then(() => postYskzjjsSubmitForAudit(id))
  372 + .then(res => {
  373 + const d = res.data || {};
  374 + if (d.success) {
  375 + this.$message.success(d.message || "已提交审核");
  376 + this.initData();
  377 + } else {
  378 + this.$message.error(d.message || "提交失败");
  379 + }
  380 + })
  381 + .catch(() => {});
  382 + },
  383 + handleWithdrawAudit(id) {
  384 + if (!id) return;
  385 + this.$confirm("确认撤回审核(单据将变为草稿)?", "提示", {
  386 + type: "warning"
  387 + })
  388 + .then(() => postYskzjjsWithdrawAudit(id))
  389 + .then(res => {
  390 + const d = res.data || {};
  391 + if (d.success) {
  392 + this.$message.success(d.message || "已撤回");
  393 + this.initData();
  394 + } else {
  395 + this.$message.error(d.message || "撤回失败");
  396 + }
  397 + })
  398 + .catch(() => {});
  399 + },
  400 + handleLevel1Approve(id) {
  401 + this.$confirm("确认执行一级审核?", "一级审核", { type: "warning" })
  402 + .then(() => promptApprovalRemark(this, "审批备注"))
  403 + .then(remark => postYskzjjsApprove(id, remark))
  404 + .then(res => {
  405 + const d = res.data || {};
  406 + if (d.success) {
  407 + this.$message.success(d.message || "审核成功");
  408 + this.initData();
  409 + } else {
  410 + this.$message.error(d.message || "审核失败");
  411 + }
  412 + })
  413 + .catch(() => {});
  414 + },
  415 + handleLevel2Approve(id) {
  416 + this.$confirm("确认执行二级审核?", "二级审核", { type: "warning" })
  417 + .then(() => promptApprovalRemark(this, "审批备注"))
  418 + .then(remark => postYskzjjsApprove(id, remark))
  419 + .then(res => {
  420 + const d = res.data || {};
  421 + if (d.success) {
  422 + this.$message.success(d.message || "审核成功");
  423 + this.initData();
  424 + } else {
  425 + this.$message.error(d.message || "审核失败");
  426 + }
  427 + })
  428 + .catch(() => {});
  429 + },
  430 + handleReject(id) {
  431 + promptApprovalRemark(this, "审核不通过")
  432 + .then(remark => postYskzjjsReject(id, remark))
  433 + .then(res => {
  434 + const d = res.data || {};
  435 + if (d.success) {
  436 + this.$message.success(d.message || "已标记审核不通过");
  437 + this.initData();
  438 + } else {
  439 + this.$message.error(d.message || "操作失败");
  440 + }
  441 + })
  442 + .catch(() => {});
  443 + },
  444 + getWldwOptions() {
  445 + request({
  446 + url: "/api/Extend/WtWldw",
  447 + method: "GET",
  448 + data: { currentPage: 1, pageSize: 5000 }
  449 + }).then(res => {
  450 + const raw = res.data.list || res.data || [];
  451 + this.wldwOptions = raw
  452 + .map(x => ({
  453 + ...x,
  454 + id: String(x && (x.id || x.F_Id || x.f_Id || ""))
  455 + }))
  456 + .filter(x => x.id);
  457 + });
  458 + },
  459 + wldwText(val) {
  460 + if (!val) return "无";
  461 + const hit = this.wldwOptions.find(
  462 + item => String(item.id) === String(val)
  463 + );
  464 + return hit && hit.dwmc ? hit.dwmc : val;
  465 + },
  466 + openDetail(id) {
  467 + if (!id) return;
  468 + this.detailVisible = true;
  469 + this.$nextTick(() => {
  470 + this.$refs.NCCDetailView && this.$refs.NCCDetailView.init(id);
  471 + });
  472 + },
  473 + initData() {
  474 + this.listLoading = true;
  475 + let _query = {
  476 + ...this.listQuery,
  477 + ...this.query
  478 + };
  479 + let query = {};
  480 + for (let key in _query) {
  481 + if (Array.isArray(_query[key])) {
  482 + query[key] = _query[key].join();
  483 + } else {
  484 + query[key] = _query[key];
  485 + }
  486 + }
  487 + request({
  488 + url: `/api/Extend/WtYskzjjs`,
  489 + method: "GET",
  490 + data: query
  491 + }).then(res => {
  492 + this.list = res.data.list;
  493 + this.total = res.data.pagination.total;
  494 + this.listLoading = false;
  495 + });
  496 + },
  497 + handleDel(id) {
  498 + this.$confirm("此操作将永久删除该数据, 是否继续?", "提示", {
  499 + type: "warning"
  500 + })
  501 + .then(() => {
  502 + request({
  503 + url: `/api/Extend/WtYskzjjs/${id}`,
  504 + method: "DELETE"
  505 + }).then(res => {
  506 + this.$message({
  507 + type: "success",
  508 + message: res.msg,
  509 + onClose: () => {
  510 + this.initData();
  511 + }
  512 + });
  513 + });
  514 + })
  515 + .catch(() => {});
  516 + },
  517 + handleSelectionChange(val) {
  518 + const res = val.map(item => item.id);
  519 + this.multipleSelection = res;
  520 + },
  521 + handleBatchRemoveDel() {
  522 + if (!this.multipleSelection.length) {
  523 + this.$message({
  524 + type: "error",
  525 + message: "请选择一条数据",
  526 + duration: 1500
  527 + });
  528 + return;
  529 + }
  530 + const ids = this.multipleSelection;
  531 + this.$confirm("您确定要删除这些数据吗, 是否继续?", "提示", {
  532 + type: "warning"
  533 + })
  534 + .then(() => {
  535 + request({
  536 + url: `/api/Extend/WtYskzjjs/batchRemove`,
  537 + method: "POST",
  538 + data: ids
  539 + }).then(res => {
  540 + this.$message({
  541 + type: "success",
  542 + message: res.msg,
  543 + onClose: () => {
  544 + this.initData();
  545 + }
  546 + });
  547 + });
  548 + })
  549 + .catch(() => {});
  550 + },
  551 + addOrUpdateHandle(id, isDetail) {
  552 + this.formVisible = true;
  553 + this.$nextTick(() => {
  554 + this.$refs.NCCForm.init(id, isDetail);
  555 + });
  556 + },
  557 + exportData() {
  558 + this.exportBoxVisible = true;
  559 + this.$nextTick(() => {
  560 + this.$refs.ExportBox.init(this.columnList);
  561 + });
  562 + },
  563 + download(data) {
  564 + let query = { ...data, ...this.listQuery, ...this.query };
  565 + request({
  566 + url: `/api/Extend/WtYskzjjs/Actions/Export`,
  567 + method: "GET",
  568 + data: query
  569 + }).then(res => {
  570 + if (!res.data.url) return;
  571 + window.location.href = this.define.comUrl + res.data.url;
  572 + this.$refs.ExportBox.visible = false;
  573 + this.exportBoxVisible = false;
  574 + });
  575 + },
  576 + search() {
  577 + this.listQuery = {
  578 + currentPage: 1,
  579 + pageSize: 20,
  580 + sort: "desc",
  581 + sidx: ""
  582 + };
  583 + this.query.djlx = this.billType;
  584 + this.initData();
  585 + },
  586 + refresh(isrRefresh) {
  587 + this.formVisible = false;
  588 + if (isrRefresh) this.reset();
  589 + },
  590 + reset() {
  591 + for (let key in this.query) {
  592 + this.query[key] = undefined;
  593 + }
  594 + this.query.djlx = this.billType;
  595 + this.listQuery = {
  596 + currentPage: 1,
  597 + pageSize: 20,
  598 + sort: "desc",
  599 + sidx: ""
  600 + };
  601 + this.initData();
293 } 602 }
294 -</script>  
295 \ No newline at end of file 603 \ No newline at end of file
  604 + }
  605 +};
  606 +</script>
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdCrInput.cs
@@ -77,6 +77,26 @@ namespace NCC.Extend.Entitys.Dto.WtFysrd @@ -77,6 +77,26 @@ namespace NCC.Extend.Entitys.Dto.WtFysrd
77 /// 付款单位 77 /// 付款单位
78 /// </summary> 78 /// </summary>
79 public string fkdw { get; set; } 79 public string fkdw { get; set; }
  80 +
  81 + /// <summary>
  82 + /// 往来单位(wt_wldw.F_Id);与 fkdw 同步保存
  83 + /// </summary>
  84 + public string wldw { get; set; }
  85 +
  86 + /// <summary>
  87 + /// 兼容请求:客户主键,落库时与 wldw/fkdw 对齐
  88 + /// </summary>
  89 + public string kh { get; set; }
  90 +
  91 + /// <summary>
  92 + /// 兼容请求:供应商主键,落库时与 wldw/fkdw 对齐
  93 + /// </summary>
  94 + public string gys { get; set; }
  95 +
  96 + /// <summary>
  97 + /// 单据状态(一般仅查询回显;写入以服务端为准)
  98 + /// </summary>
  99 + public string djzt { get; set; }
80 100
81 /// <summary> 101 /// <summary>
82 /// 费用收入单明细 102 /// 费用收入单明细
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdInfoOutput.cs
@@ -77,6 +77,36 @@ namespace NCC.Extend.Entitys.Dto.WtFysrd @@ -77,6 +77,36 @@ namespace NCC.Extend.Entitys.Dto.WtFysrd
77 /// 付款单位 77 /// 付款单位
78 /// </summary> 78 /// </summary>
79 public string fkdw { get; set; } 79 public string fkdw { get; set; }
  80 +
  81 + /// <summary>
  82 + /// 往来单位主键(与 fkdw 一致)
  83 + /// </summary>
  84 + public string wldw { get; set; }
  85 +
  86 + /// <summary>
  87 + /// 往来单位名称
  88 + /// </summary>
  89 + public string wldwmc { get; set; }
  90 +
  91 + /// <summary>
  92 + /// 单据状态
  93 + /// </summary>
  94 + public string djzt { get; set; }
  95 +
  96 + /// <summary>
  97 + /// 审批备注
  98 + /// </summary>
  99 + public string spbz { get; set; }
  100 +
  101 + /// <summary>
  102 + /// 一级审核人
  103 + /// </summary>
  104 + public string shr1 { get; set; }
  105 +
  106 + /// <summary>
  107 + /// 二级审核人
  108 + /// </summary>
  109 + public string shr2 { get; set; }
80 110
81 /// <summary> 111 /// <summary>
82 /// 费用收入单明细 112 /// 费用收入单明细
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdListOutput.cs
@@ -71,6 +71,26 @@ namespace NCC.Extend.Entitys.Dto.WtFysrd @@ -71,6 +71,26 @@ namespace NCC.Extend.Entitys.Dto.WtFysrd
71 /// 单据类型 71 /// 单据类型
72 /// </summary> 72 /// </summary>
73 public string djlx { get; set; } 73 public string djlx { get; set; }
  74 +
  75 + /// <summary>
  76 + /// 付款单位
  77 + /// </summary>
  78 + public string fkdw { get; set; }
  79 +
  80 + /// <summary>
  81 + /// 往来单位主键
  82 + /// </summary>
  83 + public string wldw { get; set; }
  84 +
  85 + /// <summary>
  86 + /// 往来单位名称
  87 + /// </summary>
  88 + public string wldwmc { get; set; }
  89 +
  90 + /// <summary>
  91 + /// 单据状态
  92 + /// </summary>
  93 + public string djzt { get; set; }
74 94
75 } 95 }
76 } 96 }
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdListQueryInput.cs
@@ -83,6 +83,16 @@ namespace NCC.Extend.Entitys.Dto.WtFysrd @@ -83,6 +83,16 @@ namespace NCC.Extend.Entitys.Dto.WtFysrd
83 /// 单据类型 83 /// 单据类型
84 /// </summary> 84 /// </summary>
85 public string djlx { get; set; } 85 public string djlx { get; set; }
  86 +
  87 + /// <summary>
  88 + /// 付款单位
  89 + /// </summary>
  90 + public string fkdw { get; set; }
  91 +
  92 + /// <summary>
  93 + /// 往来单位(与 fkdw 任一匹配)
  94 + /// </summary>
  95 + public string wldw { get; set; }
86 96
87 } 97 }
88 } 98 }
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtPurchaseSummaryQueryInput.cs
@@ -36,11 +36,21 @@ namespace NCC.Extend.Entitys.Dto.WtXsckd @@ -36,11 +36,21 @@ namespace NCC.Extend.Entitys.Dto.WtXsckd
36 public string Warehouse { get; set; } 36 public string Warehouse { get; set; }
37 37
38 /// <summary> 38 /// <summary>
39 - /// 单据类型(支持逗号分隔多值 39 + /// 单据类型(逗号分隔,仅允许采购/销售/预售/委托代销共 9 类;空或 all 表示默认包含全部 9 类
40 /// </summary> 40 /// </summary>
41 public string BillType { get; set; } 41 public string BillType { get; set; }
42 42
43 /// <summary> 43 /// <summary>
  44 + /// 排序列键(白名单):分类/品牌/商品聚合支持「分类名称」「采购金额」等;明细/线性列表支持「单据日期」「数量」等;非法则走各接口默认排序。
  45 + /// </summary>
  46 + public string SortField { get; set; }
  47 +
  48 + /// <summary>
  49 + /// 排序方向:asc / desc(默认 desc)
  50 + /// </summary>
  51 + public string SortOrder { get; set; }
  52 +
  53 + /// <summary>
44 /// 下钻:商品分类主键(wt_pl.F_Id),与明细接口联用 54 /// 下钻:商品分类主键(wt_pl.F_Id),与明细接口联用
45 /// </summary> 55 /// </summary>
46 public string CategoryId { get; set; } 56 public string CategoryId { get; set; }
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtStockSummaryQueryInput.cs
@@ -24,5 +24,10 @@ namespace NCC.Extend.Entitys.Dto.WtXsckd @@ -24,5 +24,10 @@ namespace NCC.Extend.Entitys.Dto.WtXsckd
24 /// 商品关键字(匹配 wt_sp 名称或编码) 24 /// 商品关键字(匹配 wt_sp 名称或编码)
25 /// </summary> 25 /// </summary>
26 public string Product { get; set; } 26 public string Product { get; set; }
  27 +
  28 + /// <summary>
  29 + /// 商品主键(wt_sp.F_Id);非空时按精确商品筛选(优先级高于 <see cref="Product"/>)
  30 + /// </summary>
  31 + public string ProductSpId { get; set; }
27 } 32 }
28 } 33 }
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtYskzjjsCrInput.cs
@@ -29,6 +29,11 @@ namespace NCC.Extend.Entitys.Dto.WtYskzjjs @@ -29,6 +29,11 @@ namespace NCC.Extend.Entitys.Dto.WtYskzjjs
29 public string jsr { get; set; } 29 public string jsr { get; set; }
30 30
31 /// <summary> 31 /// <summary>
  32 + /// 往来单位(主表 <c>wldw</c>,与明细 <c>kh</c> 对应)
  33 + /// </summary>
  34 + public string wldw { get; set; }
  35 +
  36 + /// <summary>
32 /// 审核状态 37 /// 审核状态
33 /// </summary> 38 /// </summary>
34 public string djzt { get; set; } 39 public string djzt { get; set; }
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtYskzjjsInfoOutput.cs
@@ -27,6 +27,11 @@ namespace NCC.Extend.Entitys.Dto.WtYskzjjs @@ -27,6 +27,11 @@ namespace NCC.Extend.Entitys.Dto.WtYskzjjs
27 /// 往来单位 27 /// 往来单位
28 /// </summary> 28 /// </summary>
29 public string wldw { get; set; } 29 public string wldw { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 往来单位名称(详情展示,服务端解析)
  33 + /// </summary>
  34 + public string wldwmc { get; set; }
30 35
31 /// <summary> 36 /// <summary>
32 /// 经手人 37 /// 经手人
@@ -34,6 +39,26 @@ namespace NCC.Extend.Entitys.Dto.WtYskzjjs @@ -34,6 +39,26 @@ namespace NCC.Extend.Entitys.Dto.WtYskzjjs
34 public string jsr { get; set; } 39 public string jsr { get; set; }
35 40
36 /// <summary> 41 /// <summary>
  42 + /// 审批备注
  43 + /// </summary>
  44 + public string spbz { get; set; }
  45 +
  46 + /// <summary>
  47 + /// 审核人(终审)
  48 + /// </summary>
  49 + public string shr { get; set; }
  50 +
  51 + /// <summary>
  52 + /// 一级审核人
  53 + /// </summary>
  54 + public string shr1 { get; set; }
  55 +
  56 + /// <summary>
  57 + /// 二级审核人
  58 + /// </summary>
  59 + public string shr2 { get; set; }
  60 +
  61 + /// <summary>
37 /// 审核状态 62 /// 审核状态
38 /// </summary> 63 /// </summary>
39 public string djzt { get; set; } 64 public string djzt { get; set; }
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtYskzjjsListOutput.cs
@@ -86,6 +86,26 @@ namespace NCC.Extend.Entitys.Dto.WtYskzjjs @@ -86,6 +86,26 @@ namespace NCC.Extend.Entitys.Dto.WtYskzjjs
86 /// 往来单位名称(按往来单位/会员/供应商自动解析) 86 /// 往来单位名称(按往来单位/会员/供应商自动解析)
87 /// </summary> 87 /// </summary>
88 public string wldwmc { get; set; } 88 public string wldwmc { get; set; }
  89 +
  90 + /// <summary>
  91 + /// 审批备注
  92 + /// </summary>
  93 + public string spbz { get; set; }
  94 +
  95 + /// <summary>
  96 + /// 审核人(终审)
  97 + /// </summary>
  98 + public string shr { get; set; }
  99 +
  100 + /// <summary>
  101 + /// 一级审核人
  102 + /// </summary>
  103 + public string shr1 { get; set; }
  104 +
  105 + /// <summary>
  106 + /// 二级审核人
  107 + /// </summary>
  108 + public string shr2 { get; set; }
89 109
90 } 110 }
91 } 111 }
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/WtFysrdEntity.cs
@@ -94,6 +94,36 @@ namespace NCC.Extend.Entitys @@ -94,6 +94,36 @@ namespace NCC.Extend.Entitys
94 /// </summary> 94 /// </summary>
95 [SugarColumn(ColumnName = "fkdw")] 95 [SugarColumn(ColumnName = "fkdw")]
96 public string Fkdw { get; set; } 96 public string Fkdw { get; set; }
  97 +
  98 + /// <summary>
  99 + /// 往来单位(与 fkdw 存同一 wt_wldw.F_Id,便于与 kh/gys/wldw 统一口径)
  100 + /// </summary>
  101 + [SugarColumn(ColumnName = "wldw", IsNullable = true)]
  102 + public string Wldw { get; set; }
  103 +
  104 + /// <summary>
  105 + /// 单据状态(草稿/待审核/一级已审/已审核/审核不通过)
  106 + /// </summary>
  107 + [SugarColumn(ColumnName = "djzt", IsNullable = true)]
  108 + public string Djzt { get; set; }
  109 +
  110 + /// <summary>
  111 + /// 审批备注(与业务备注 bz 分离)
  112 + /// </summary>
  113 + [SugarColumn(ColumnName = "spbz", IsNullable = true)]
  114 + public string Spbz { get; set; }
  115 +
  116 + /// <summary>
  117 + /// 一级审核人
  118 + /// </summary>
  119 + [SugarColumn(ColumnName = "shr1", IsNullable = true)]
  120 + public string Shr1 { get; set; }
  121 +
  122 + /// <summary>
  123 + /// 二级审核人
  124 + /// </summary>
  125 + [SugarColumn(ColumnName = "shr2", IsNullable = true)]
  126 + public string Shr2 { get; set; }
97 127
98 } 128 }
99 } 129 }
100 \ No newline at end of file 130 \ No newline at end of file
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/WtYskzjjsEntity.cs
@@ -42,6 +42,30 @@ namespace NCC.Extend.Entitys @@ -42,6 +42,30 @@ namespace NCC.Extend.Entitys
42 public string Jsr { get; set; } 42 public string Jsr { get; set; }
43 43
44 /// <summary> 44 /// <summary>
  45 + /// 审批备注(与业务备注分离;库表须含列 <c>spbz</c>)
  46 + /// </summary>
  47 + [SugarColumn(ColumnName = "spbz", IsNullable = true, ColumnDataType = "text")]
  48 + public string Spbz { get; set; }
  49 +
  50 + /// <summary>
  51 + /// 审核人(终审)
  52 + /// </summary>
  53 + [SugarColumn(ColumnName = "shr", IsNullable = true)]
  54 + public string Shr { get; set; }
  55 +
  56 + /// <summary>
  57 + /// 一级审核人
  58 + /// </summary>
  59 + [SugarColumn(ColumnName = "shr1", IsNullable = true)]
  60 + public string Shr1 { get; set; }
  61 +
  62 + /// <summary>
  63 + /// 二级审核人
  64 + /// </summary>
  65 + [SugarColumn(ColumnName = "shr2", IsNullable = true)]
  66 + public string Shr2 { get; set; }
  67 +
  68 + /// <summary>
45 /// 审核状态 69 /// 审核状态
46 /// </summary> 70 /// </summary>
47 [SugarColumn(ColumnName = "djzt")] 71 [SugarColumn(ColumnName = "djzt")]
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtFysrdService.cs
@@ -36,6 +36,7 @@ namespace NCC.Extend.WtFysrd @@ -36,6 +36,7 @@ namespace NCC.Extend.WtFysrd
36 private readonly ISqlSugarRepository<WtFysrdMxEntity> _wtFysrdMxRepository; 36 private readonly ISqlSugarRepository<WtFysrdMxEntity> _wtFysrdMxRepository;
37 private readonly SqlSugarScope _db; 37 private readonly SqlSugarScope _db;
38 private readonly IUserManager _userManager; 38 private readonly IUserManager _userManager;
  39 + private readonly WtFysrdWorkflowHelper _fysrdWorkflow;
39 40
40 /// <summary> 41 /// <summary>
41 /// 初始化一个<see cref="WtFysrdService"/>类型的新实例 42 /// 初始化一个<see cref="WtFysrdService"/>类型的新实例
@@ -43,12 +44,71 @@ namespace NCC.Extend.WtFysrd @@ -43,12 +44,71 @@ namespace NCC.Extend.WtFysrd
43 public WtFysrdService( 44 public WtFysrdService(
44 ISqlSugarRepository<WtFysrdEntity> wtFysrdRepository, 45 ISqlSugarRepository<WtFysrdEntity> wtFysrdRepository,
45 ISqlSugarRepository<WtFysrdMxEntity> wtFysrdMxRepository, 46 ISqlSugarRepository<WtFysrdMxEntity> wtFysrdMxRepository,
46 - IUserManager userManager) 47 + IUserManager userManager,
  48 + WtFysrdWorkflowHelper fysrdWorkflow)
47 { 49 {
48 _wtFysrdRepository = wtFysrdRepository; 50 _wtFysrdRepository = wtFysrdRepository;
49 _db = _wtFysrdRepository.Context; 51 _db = _wtFysrdRepository.Context;
50 _wtFysrdMxRepository = wtFysrdMxRepository; 52 _wtFysrdMxRepository = wtFysrdMxRepository;
51 _userManager = userManager; 53 _userManager = userManager;
  54 + _fysrdWorkflow = fysrdWorkflow;
  55 + }
  56 +
  57 + private static bool IsLockedForEdit(string djzt)
  58 + {
  59 + var s = djzt?.Trim() ?? "";
  60 + return s == "待审核" || s == "已审核" || s == "一级已审" || s == "待二级" || s == "一级已审/待二级";
  61 + }
  62 +
  63 + private static bool IsDeletableState(string djzt)
  64 + {
  65 + var s = djzt?.Trim() ?? "";
  66 + return string.IsNullOrEmpty(s) || s == "草稿" || s == "审核不通过";
  67 + }
  68 +
  69 + private void NormalizeHeaderFromInput(WtFysrdCrInput input, WtFysrdEntity entity)
  70 + {
  71 + if (input == null || entity == null) return;
  72 + if (!string.IsNullOrWhiteSpace(input.djlx))
  73 + entity.Djlx = WtFysrdWorkflowHelper.NormalizeOtherIncomeDjlx(input.djlx) ?? input.djlx;
  74 + if (!string.IsNullOrWhiteSpace(WtFysrdWorkflowHelper.ResolveCounterpartyId(input.wldw, input.fkdw, input.kh, input.gys)))
  75 + WtFysrdWorkflowHelper.ApplyCounterpartyToEntity(entity, input.wldw, input.fkdw, input.kh, input.gys);
  76 + }
  77 +
  78 + private async Task EnrichWldwForListAsync(IEnumerable<WtFysrdListOutput> rows)
  79 + {
  80 + if (rows == null) return;
  81 + var rowList = rows as IList<WtFysrdListOutput> ?? rows.ToList();
  82 + if (rowList.Count == 0) return;
  83 + foreach (var r in rowList)
  84 + {
  85 + if (string.IsNullOrWhiteSpace(r.wldw))
  86 + r.wldw = r.fkdw;
  87 + if (string.IsNullOrWhiteSpace(r.fkdw))
  88 + r.fkdw = r.wldw;
  89 + }
  90 + var ids = rowList.Select(x => x.wldw).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList();
  91 + if (ids.Count == 0) return;
  92 + var wldwRows = await _db.Queryable<WtWldwEntity>().In(w => w.Id, ids).ToListAsync();
  93 + var map = wldwRows.ToDictionary(x => x.Id, x => x.Dwmc, StringComparer.OrdinalIgnoreCase);
  94 + foreach (var r in rowList)
  95 + {
  96 + if (!string.IsNullOrWhiteSpace(r.wldw) && map.TryGetValue(r.wldw, out var mc))
  97 + r.wldwmc = mc;
  98 + }
  99 + }
  100 +
  101 + /// <summary>
  102 + /// 提交审核:草稿/审核不通过 → 待审核(其他收入单;除备注外必填校验)
  103 + /// </summary>
  104 + /// <param name="id">单据编号</param>
  105 + [HttpPost("Actions/SubmitForAudit/{id}")]
  106 + public async Task<dynamic> SubmitForAudit(string id)
  107 + {
  108 + dynamic r = await _fysrdWorkflow.SubmitForAuditAsync(id);
  109 + if (r.success == true)
  110 + return new { msg = (string)(r.message ?? "已提交") };
  111 + throw NCCException.Bah((string)(r.message ?? "提交失败"));
52 } 112 }
53 113
54 /// <summary> 114 /// <summary>
@@ -59,9 +119,22 @@ namespace NCC.Extend.WtFysrd @@ -59,9 +119,22 @@ namespace NCC.Extend.WtFysrd
59 [HttpGet("{id}")] 119 [HttpGet("{id}")]
60 public async Task<dynamic> GetInfo(string id) 120 public async Task<dynamic> GetInfo(string id)
61 { 121 {
  122 + _fysrdWorkflow.EnsureFysrdAuditColumns();
62 var entity = await _db.Queryable<WtFysrdEntity>().FirstAsync(p => p.Id == id); 123 var entity = await _db.Queryable<WtFysrdEntity>().FirstAsync(p => p.Id == id);
63 var output = entity.Adapt<WtFysrdInfoOutput>(); 124 var output = entity.Adapt<WtFysrdInfoOutput>();
64 - 125 + output.djlx = WtFysrdWorkflowHelper.NormalizeOtherIncomeDjlx(entity.Djlx) ?? entity.Djlx;
  126 + output.wldw = string.IsNullOrWhiteSpace(entity.Wldw) ? entity.Fkdw : entity.Wldw;
  127 + output.fkdw = string.IsNullOrWhiteSpace(entity.Fkdw) ? entity.Wldw : entity.Fkdw;
  128 + output.djzt = entity.Djzt;
  129 + output.spbz = entity.Spbz;
  130 + output.shr1 = entity.Shr1;
  131 + output.shr2 = entity.Shr2;
  132 + var wldwId = output.wldw ?? output.fkdw;
  133 + if (!string.IsNullOrWhiteSpace(wldwId))
  134 + {
  135 + var w = await _db.Queryable<WtWldwEntity>().FirstAsync(x => x.Id == wldwId);
  136 + output.wldwmc = w?.Dwmc;
  137 + }
65 var wtFysrdMxList = await _db.Queryable<WtFysrdMxEntity>().Where(w => w.Djbh == entity.Id).ToListAsync(); 138 var wtFysrdMxList = await _db.Queryable<WtFysrdMxEntity>().Where(w => w.Djbh == entity.Id).ToListAsync();
66 output.wtFysrdMxList = wtFysrdMxList.Adapt<List<WtFysrdMxInfoOutput>>(); 139 output.wtFysrdMxList = wtFysrdMxList.Adapt<List<WtFysrdMxInfoOutput>>();
67 return output; 140 return output;
@@ -75,6 +148,7 @@ namespace NCC.Extend.WtFysrd @@ -75,6 +148,7 @@ namespace NCC.Extend.WtFysrd
75 [HttpGet("")] 148 [HttpGet("")]
76 public async Task<dynamic> GetList([FromQuery] WtFysrdListQueryInput input) 149 public async Task<dynamic> GetList([FromQuery] WtFysrdListQueryInput input)
77 { 150 {
  151 + _fysrdWorkflow.EnsureFysrdAuditColumns();
78 var sidx = input.sidx == null ? "id" : input.sidx; 152 var sidx = input.sidx == null ? "id" : input.sidx;
79 List<string> queryDjrq = input.djrq != null ? input.djrq.Split(',').ToObeject<List<string>>() : null; 153 List<string> queryDjrq = input.djrq != null ? input.djrq.Split(',').ToObeject<List<string>>() : null;
80 DateTime? startDjrq = queryDjrq != null ? Ext.GetDateTime(queryDjrq.First()) : null; 154 DateTime? startDjrq = queryDjrq != null ? Ext.GetDateTime(queryDjrq.First()) : null;
@@ -94,6 +168,8 @@ namespace NCC.Extend.WtFysrd @@ -94,6 +168,8 @@ namespace NCC.Extend.WtFysrd
94 .WhereIF(!string.IsNullOrEmpty(input.bz), p => p.Bz.Contains(input.bz)) 168 .WhereIF(!string.IsNullOrEmpty(input.bz), p => p.Bz.Contains(input.bz))
95 .WhereIF(!string.IsNullOrEmpty(input.zy), p => p.Zy.Contains(input.zy)) 169 .WhereIF(!string.IsNullOrEmpty(input.zy), p => p.Zy.Contains(input.zy))
96 .WhereIF(!string.IsNullOrEmpty(input.djlx), p => p.Djlx.Contains(input.djlx)) 170 .WhereIF(!string.IsNullOrEmpty(input.djlx), p => p.Djlx.Contains(input.djlx))
  171 + .WhereIF(!string.IsNullOrEmpty(input.fkdw), p => p.Fkdw == input.fkdw || p.Wldw == input.fkdw)
  172 + .WhereIF(!string.IsNullOrEmpty(input.wldw), p => p.Wldw == input.wldw || p.Fkdw == input.wldw)
97 .Select(it=> new WtFysrdListOutput 173 .Select(it=> new WtFysrdListOutput
98 { 174 {
99 id = it.Id, 175 id = it.Id,
@@ -109,7 +185,11 @@ namespace NCC.Extend.WtFysrd @@ -109,7 +185,11 @@ namespace NCC.Extend.WtFysrd
109 bz=it.Bz, 185 bz=it.Bz,
110 zy=it.Zy, 186 zy=it.Zy,
111 djlx=it.Djlx, 187 djlx=it.Djlx,
  188 + fkdw = it.Fkdw,
  189 + wldw = it.Wldw,
  190 + djzt = it.Djzt,
112 }).MergeTable().OrderBy(sidx+" "+input.sort).ToPagedListAsync(input.currentPage, input.pageSize); 191 }).MergeTable().OrderBy(sidx+" "+input.sort).ToPagedListAsync(input.currentPage, input.pageSize);
  192 + await EnrichWldwForListAsync(data.list);
113 return PageResult<WtFysrdListOutput>.SqlSugarPageResult(data); 193 return PageResult<WtFysrdListOutput>.SqlSugarPageResult(data);
114 } 194 }
115 195
@@ -121,15 +201,18 @@ namespace NCC.Extend.WtFysrd @@ -121,15 +201,18 @@ namespace NCC.Extend.WtFysrd
121 [HttpPost("")] 201 [HttpPost("")]
122 public async Task Create([FromBody] WtFysrdCrInput input) 202 public async Task Create([FromBody] WtFysrdCrInput input)
123 { 203 {
  204 + _fysrdWorkflow.EnsureFysrdAuditColumns();
124 var userInfo = await _userManager.GetUserInfo(); 205 var userInfo = await _userManager.GetUserInfo();
125 var entity = input.Adapt<WtFysrdEntity>(); 206 var entity = input.Adapt<WtFysrdEntity>();
  207 + NormalizeHeaderFromInput(input, entity);
  208 + entity.Djzt = "草稿";
126 // 生成每日递增单号 209 // 生成每日递增单号
127 var today = DateTime.Now.ToString("yyyyMMdd"); 210 var today = DateTime.Now.ToString("yyyyMMdd");
128 var prefix = "FY"; 211 var prefix = "FY";
129 // 根据单据类型设置前缀 212 // 根据单据类型设置前缀
130 - if (!string.IsNullOrEmpty(input.djlx)) {  
131 - if (input.djlx.Contains("现金费用单")) prefix = "XF";  
132 - else if (input.djlx.Contains("其他收入单")) prefix = "QT"; 213 + if (!string.IsNullOrEmpty(entity.Djlx)) {
  214 + if (entity.Djlx.Contains("现金费用单")) prefix = "XF";
  215 + else if (WtFysrdWorkflowHelper.IsOtherIncomeDjlx(entity.Djlx)) prefix = "QT";
133 else prefix = "FY"; // 默认费用单前缀 216 else prefix = "FY"; // 默认费用单前缀
134 } 217 }
135 var maxId = await _db.Queryable<WtFysrdEntity>() 218 var maxId = await _db.Queryable<WtFysrdEntity>()
@@ -199,6 +282,7 @@ namespace NCC.Extend.WtFysrd @@ -199,6 +282,7 @@ namespace NCC.Extend.WtFysrd
199 [NonAction] 282 [NonAction]
200 public async Task<dynamic> GetNoPagingList([FromQuery] WtFysrdListQueryInput input) 283 public async Task<dynamic> GetNoPagingList([FromQuery] WtFysrdListQueryInput input)
201 { 284 {
  285 + _fysrdWorkflow.EnsureFysrdAuditColumns();
202 var sidx = input.sidx == null ? "id" : input.sidx; 286 var sidx = input.sidx == null ? "id" : input.sidx;
203 List<string> queryDjrq = input.djrq != null ? input.djrq.Split(',').ToObeject<List<string>>() : null; 287 List<string> queryDjrq = input.djrq != null ? input.djrq.Split(',').ToObeject<List<string>>() : null;
204 DateTime? startDjrq = queryDjrq != null ? Ext.GetDateTime(queryDjrq.First()) : null; 288 DateTime? startDjrq = queryDjrq != null ? Ext.GetDateTime(queryDjrq.First()) : null;
@@ -218,6 +302,8 @@ namespace NCC.Extend.WtFysrd @@ -218,6 +302,8 @@ namespace NCC.Extend.WtFysrd
218 .WhereIF(!string.IsNullOrEmpty(input.bz), p => p.Bz.Contains(input.bz)) 302 .WhereIF(!string.IsNullOrEmpty(input.bz), p => p.Bz.Contains(input.bz))
219 .WhereIF(!string.IsNullOrEmpty(input.zy), p => p.Zy.Contains(input.zy)) 303 .WhereIF(!string.IsNullOrEmpty(input.zy), p => p.Zy.Contains(input.zy))
220 .WhereIF(!string.IsNullOrEmpty(input.djlx), p => p.Djlx.Contains(input.djlx)) 304 .WhereIF(!string.IsNullOrEmpty(input.djlx), p => p.Djlx.Contains(input.djlx))
  305 + .WhereIF(!string.IsNullOrEmpty(input.fkdw), p => p.Fkdw == input.fkdw || p.Wldw == input.fkdw)
  306 + .WhereIF(!string.IsNullOrEmpty(input.wldw), p => p.Wldw == input.wldw || p.Fkdw == input.wldw)
221 .Select(it=> new WtFysrdListOutput 307 .Select(it=> new WtFysrdListOutput
222 { 308 {
223 id = it.Id, 309 id = it.Id,
@@ -233,7 +319,11 @@ namespace NCC.Extend.WtFysrd @@ -233,7 +319,11 @@ namespace NCC.Extend.WtFysrd
233 bz=it.Bz, 319 bz=it.Bz,
234 zy=it.Zy, 320 zy=it.Zy,
235 djlx=it.Djlx, 321 djlx=it.Djlx,
  322 + fkdw = it.Fkdw,
  323 + wldw = it.Wldw,
  324 + djzt = it.Djzt,
236 }).MergeTable().OrderBy(sidx+" "+input.sort).ToListAsync(); 325 }).MergeTable().OrderBy(sidx+" "+input.sort).ToListAsync();
  326 + await EnrichWldwForListAsync(data);
237 return data; 327 return data;
238 } 328 }
239 329
@@ -256,7 +346,7 @@ namespace NCC.Extend.WtFysrd @@ -256,7 +346,7 @@ namespace NCC.Extend.WtFysrd
256 { 346 {
257 exportData = await this.GetNoPagingList(input); 347 exportData = await this.GetNoPagingList(input);
258 } 348 }
259 - List<ParamsModel> paramList = "[{\"value\":\"单据编号\",\"field\":\"id\"},{\"value\":\"单据日期\",\"field\":\"djrq\"},{\"value\":\"分支机构\",\"field\":\"fzjg\"},{\"value\":\"部门\",\"field\":\"bm\"},{\"value\":\"经手人\",\"field\":\"jsr\"},{\"value\":\"结算账户\",\"field\":\"jszh\"},{\"value\":\"金额\",\"field\":\"je\"},{\"value\":\"制单人\",\"field\":\"zdr\"},{\"value\":\"审核人\",\"field\":\"shr\"},{\"value\":\"过账人\",\"field\":\"gzr\"},{\"value\":\"备注\",\"field\":\"bz\"},{\"value\":\"摘要\",\"field\":\"zy\"},{\"value\":\"单据类型\",\"field\":\"djlx\"},]".ToList<ParamsModel>(); 349 + List<ParamsModel> paramList = "[{\"value\":\"单据编号\",\"field\":\"id\"},{\"value\":\"单据日期\",\"field\":\"djrq\"},{\"value\":\"分支机构\",\"field\":\"fzjg\"},{\"value\":\"部门\",\"field\":\"bm\"},{\"value\":\"经手人\",\"field\":\"jsr\"},{\"value\":\"结算账户\",\"field\":\"jszh\"},{\"value\":\"金额\",\"field\":\"je\"},{\"value\":\"制单人\",\"field\":\"zdr\"},{\"value\":\"审核人\",\"field\":\"shr\"},{\"value\":\"过账人\",\"field\":\"gzr\"},{\"value\":\"备注\",\"field\":\"bz\"},{\"value\":\"摘要\",\"field\":\"zy\"},{\"value\":\"单据类型\",\"field\":\"djlx\"},{\"value\":\"往来单位\",\"field\":\"wldw\"},{\"value\":\"往来单位名称\",\"field\":\"wldwmc\"},{\"value\":\"付款单位\",\"field\":\"fkdw\"},{\"value\":\"单据状态\",\"field\":\"djzt\"},]".ToList<ParamsModel>();
260 ExcelConfig excelconfig = new ExcelConfig(); 350 ExcelConfig excelconfig = new ExcelConfig();
261 excelconfig.FileName = "费用单.xls"; 351 excelconfig.FileName = "费用单.xls";
262 excelconfig.HeadFont = "微软雅黑"; 352 excelconfig.HeadFont = "微软雅黑";
@@ -292,6 +382,11 @@ namespace NCC.Extend.WtFysrd @@ -292,6 +382,11 @@ namespace NCC.Extend.WtFysrd
292 public async Task BatchRemove([FromBody] List<string> ids) 382 public async Task BatchRemove([FromBody] List<string> ids)
293 { 383 {
294 var entitys = await _db.Queryable<WtFysrdEntity>().In(it => it.Id, ids).ToListAsync(); 384 var entitys = await _db.Queryable<WtFysrdEntity>().In(it => it.Id, ids).ToListAsync();
  385 + foreach (var e in entitys)
  386 + {
  387 + if (!IsDeletableState(e.Djzt))
  388 + throw NCCException.Bah($"单据 {e.Id} 当前状态不允许删除");
  389 + }
295 if (entitys.Count > 0) 390 if (entitys.Count > 0)
296 { 391 {
297 try 392 try
@@ -324,7 +419,37 @@ namespace NCC.Extend.WtFysrd @@ -324,7 +419,37 @@ namespace NCC.Extend.WtFysrd
324 [HttpPut("{id}")] 419 [HttpPut("{id}")]
325 public async Task Update(string id, [FromBody] WtFysrdUpInput input) 420 public async Task Update(string id, [FromBody] WtFysrdUpInput input)
326 { 421 {
  422 + _fysrdWorkflow.EnsureFysrdAuditColumns();
  423 + var existing = await _db.Queryable<WtFysrdEntity>().FirstAsync(p => p.Id == id);
  424 + if (IsLockedForEdit(existing.Djzt))
  425 + throw NCCException.Bah("当前单据状态不允许修改");
  426 +
327 var entity = input.Adapt<WtFysrdEntity>(); 427 var entity = input.Adapt<WtFysrdEntity>();
  428 + entity.Id = id;
  429 + entity.Djzt = existing.Djzt;
  430 + if (string.IsNullOrWhiteSpace(entity.Djlx))
  431 + entity.Djlx = existing.Djlx;
  432 + NormalizeHeaderFromInput(input, entity);
  433 + if (string.IsNullOrWhiteSpace(WtFysrdWorkflowHelper.ResolveCounterpartyId(entity.Wldw, entity.Fkdw, null, null)))
  434 + {
  435 + entity.Wldw = existing.Wldw;
  436 + entity.Fkdw = existing.Fkdw;
  437 + }
  438 +
  439 + if (WtFysrdWorkflowHelper.IsOtherIncomeDjlx(entity.Djlx)
  440 + && !string.Equals(existing.Djzt?.Trim(), "草稿", StringComparison.Ordinal))
  441 + {
  442 + var mxForVal = input.wtFysrdMxList.Adapt<List<WtFysrdMxEntity>>();
  443 + try
  444 + {
  445 + WtFysrdWorkflowHelper.ValidateOtherIncomeForSubmit(entity, mxForVal);
  446 + }
  447 + catch (InvalidOperationException ex)
  448 + {
  449 + throw NCCException.Bah(ex.Message);
  450 + }
  451 + }
  452 +
328 try 453 try
329 { 454 {
330 //开启事务 455 //开启事务
@@ -367,6 +492,8 @@ namespace NCC.Extend.WtFysrd @@ -367,6 +492,8 @@ namespace NCC.Extend.WtFysrd
367 { 492 {
368 var entity = await _db.Queryable<WtFysrdEntity>().FirstAsync(p => p.Id == id); 493 var entity = await _db.Queryable<WtFysrdEntity>().FirstAsync(p => p.Id == id);
369 _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005); 494 _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005);
  495 + if (!IsDeletableState(entity.Djzt))
  496 + throw NCCException.Bah("当前单据状态不允许删除");
370 try 497 try
371 { 498 {
372 //开启事务 499 //开启事务
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtFysrdWorkflowHelper.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Threading.Tasks;
  5 +using NCC.Common.Core.Manager;
  6 +using NCC.Dependency;
  7 +using NCC.Extend.Entitys;
  8 +using SqlSugar;
  9 +
  10 +namespace NCC.Extend.WtFysrd
  11 +{
  12 + /// <summary>
  13 + /// 其他收入单(wt_fysrd)审批流:与待办中心、<c>ApproveGeneric/RejectGeneric/ReverseApproval</c> 对接。
  14 + /// </summary>
  15 + public class WtFysrdWorkflowHelper : ITransient
  16 + {
  17 + /// <summary>与 <c>wt_shrysz.djmc</c>、待办 <c>billType</c> 一致</summary>
  18 + public const string BillName = "其他收入单";
  19 +
  20 + private readonly SqlSugarScope _db;
  21 + private readonly IUserManager _userManager;
  22 + private static bool _auditColumnsChecked;
  23 +
  24 + public WtFysrdWorkflowHelper(ISqlSugarRepository<WtFysrdEntity> fysrdRepository, IUserManager userManager)
  25 + {
  26 + _db = fysrdRepository.Context;
  27 + _userManager = userManager;
  28 + }
  29 +
  30 + /// <summary>
  31 + /// 确保 wt_fysrd 具备审批与往来单位统一字段(djzt、spbz、shr、shr1、shr2、wldw)
  32 + /// </summary>
  33 + public void EnsureFysrdAuditColumns()
  34 + {
  35 + if (_auditColumnsChecked) return;
  36 + lock (typeof(WtFysrdWorkflowHelper))
  37 + {
  38 + if (_auditColumnsChecked) return;
  39 + try
  40 + {
  41 + if (!_db.DbMaintenance.IsAnyTable("wt_fysrd")) { _auditColumnsChecked = true; return; }
  42 + var cols = _db.DbMaintenance.GetColumnInfosByTableName("wt_fysrd");
  43 + var names = cols.Select(c => c.DbColumnName.ToLowerInvariant()).ToHashSet();
  44 + if (!names.Contains("djzt"))
  45 + _db.Ado.ExecuteCommand(
  46 + "ALTER TABLE `wt_fysrd` ADD COLUMN `djzt` varchar(32) NULL COMMENT '单据状态'");
  47 + if (!names.Contains("spbz"))
  48 + _db.Ado.ExecuteCommand(
  49 + "ALTER TABLE `wt_fysrd` ADD COLUMN `spbz` text NULL COMMENT '审批备注'");
  50 + if (!names.Contains("shr"))
  51 + _db.Ado.ExecuteCommand(
  52 + "ALTER TABLE `wt_fysrd` ADD COLUMN `shr` varchar(64) NULL COMMENT '审核人'");
  53 + if (!names.Contains("shr1"))
  54 + _db.Ado.ExecuteCommand(
  55 + "ALTER TABLE `wt_fysrd` ADD COLUMN `shr1` varchar(500) NULL COMMENT '一级审核人'");
  56 + if (!names.Contains("shr2"))
  57 + _db.Ado.ExecuteCommand(
  58 + "ALTER TABLE `wt_fysrd` ADD COLUMN `shr2` varchar(500) NULL COMMENT '二级审核人'");
  59 + if (!names.Contains("wldw"))
  60 + _db.Ado.ExecuteCommand(
  61 + "ALTER TABLE `wt_fysrd` ADD COLUMN `wldw` varchar(64) NULL COMMENT '往来单位(wt_wldw.F_Id),与 fkdw 同步'");
  62 +
  63 + _db.Ado.ExecuteCommand(
  64 + "UPDATE `wt_fysrd` SET `djzt`='草稿' WHERE `djzt` IS NULL OR TRIM(IFNULL(`djzt`,''))=''");
  65 + _db.Ado.ExecuteCommand(
  66 + "UPDATE `wt_fysrd` SET `wldw`=`fkdw` WHERE (`wldw` IS NULL OR TRIM(IFNULL(`wldw`,''))='') AND `fkdw` IS NOT NULL AND TRIM(`fkdw`)<>''");
  67 + }
  68 + catch (Exception ex) { Console.WriteLine($"EnsureFysrdAuditColumns: {ex.Message}"); }
  69 + _auditColumnsChecked = true;
  70 + }
  71 + }
  72 +
  73 + /// <summary>是否按「其他收入单」口径处理的单据类型(含历史前端传值)</summary>
  74 + public static bool IsOtherIncomeDjlx(string djlx)
  75 + {
  76 + var n = NormalizeOtherIncomeDjlx(djlx);
  77 + return string.Equals(n, BillName, StringComparison.Ordinal);
  78 + }
  79 +
  80 + /// <summary>统一单据类型展示与审核配置匹配用字符串</summary>
  81 + public static string NormalizeOtherIncomeDjlx(string djlx)
  82 + {
  83 + if (string.IsNullOrWhiteSpace(djlx)) return djlx;
  84 + var t = djlx.Trim().Replace("其它", "其他");
  85 + if (t == "其他收入" || t == "其他收入单" || t.Contains("其他收入单"))
  86 + return BillName;
  87 + if (t.Contains("其他收入"))
  88 + return BillName;
  89 + return djlx.Trim();
  90 + }
  91 +
  92 + /// <summary>往来单位主键:优先 wldw,其次 fkdw、kh、gys(请求兼容)</summary>
  93 + public static string ResolveCounterpartyId(string wldw, string fkdw, string kh, string gys)
  94 + {
  95 + var v = !string.IsNullOrWhiteSpace(wldw) ? wldw
  96 + : !string.IsNullOrWhiteSpace(fkdw) ? fkdw
  97 + : !string.IsNullOrWhiteSpace(kh) ? kh
  98 + : gys;
  99 + return string.IsNullOrWhiteSpace(v) ? null : v.Trim();
  100 + }
  101 +
  102 + public static void ApplyCounterpartyToEntity(WtFysrdEntity entity, string wldw, string fkdw, string kh, string gys)
  103 + {
  104 + var id = ResolveCounterpartyId(wldw, fkdw, kh, gys);
  105 + if (string.IsNullOrEmpty(id)) return;
  106 + entity.Wldw = id;
  107 + entity.Fkdw = id;
  108 + }
  109 +
  110 + private static void AppendSpbz(WtFysrdEntity entity, string actionTag, string remark)
  111 + {
  112 + var r = (remark ?? "").Trim();
  113 + if (string.IsNullOrEmpty(r)) return;
  114 + var line = string.IsNullOrEmpty(actionTag) ? r : $"[{actionTag}] {r}";
  115 + var existing = entity.Spbz?.TrimEnd() ?? "";
  116 + entity.Spbz = string.IsNullOrWhiteSpace(existing) ? line : existing + "\n" + line;
  117 + }
  118 +
  119 + private static bool IsUserInAuditConfig(string configValue, string userId, string userAccount)
  120 + {
  121 + if (string.IsNullOrWhiteSpace(configValue)) return false;
  122 + if (string.IsNullOrWhiteSpace(userId) && string.IsNullOrWhiteSpace(userAccount)) return false;
  123 + var users = configValue
  124 + .Split(new[] { ',', ',', ';', ';', '|', ' ' }, StringSplitOptions.RemoveEmptyEntries)
  125 + .Select(x => x.Trim())
  126 + .Where(x => !string.IsNullOrEmpty(x))
  127 + .ToList();
  128 + return users.Any(x =>
  129 + (!string.IsNullOrWhiteSpace(userId) && string.Equals(x, userId.Trim(), StringComparison.OrdinalIgnoreCase))
  130 + || (!string.IsNullOrWhiteSpace(userAccount) && string.Equals(x, userAccount.Trim(), StringComparison.OrdinalIgnoreCase)));
  131 + }
  132 +
  133 + /// <summary>
  134 + /// 提交审核前校验:除备注外主表及明细必填(仅其他收入单)
  135 + /// </summary>
  136 + public static void ValidateOtherIncomeForSubmit(WtFysrdEntity h, List<WtFysrdMxEntity> mxList)
  137 + {
  138 + if (h == null) throw new ArgumentNullException(nameof(h));
  139 + if (!IsOtherIncomeDjlx(h.Djlx))
  140 + return;
  141 +
  142 + if (h.Djrq == null)
  143 + throw new InvalidOperationException("单据日期不能为空");
  144 + if (string.IsNullOrWhiteSpace(h.Fzjg))
  145 + throw new InvalidOperationException("分支机构不能为空");
  146 + if (string.IsNullOrWhiteSpace(h.Bm))
  147 + throw new InvalidOperationException("部门不能为空");
  148 + if (string.IsNullOrWhiteSpace(h.Jsr))
  149 + throw new InvalidOperationException("经手人不能为空");
  150 + if (string.IsNullOrWhiteSpace(h.Jszh))
  151 + throw new InvalidOperationException("结算账户不能为空");
  152 + if (h.Je <= 0)
  153 + throw new InvalidOperationException("金额必须大于 0");
  154 + if (string.IsNullOrWhiteSpace(h.Zdr))
  155 + throw new InvalidOperationException("制单人不能为空");
  156 + if (string.IsNullOrWhiteSpace(h.Zy))
  157 + throw new InvalidOperationException("摘要不能为空");
  158 + var cp = ResolveCounterpartyId(h.Wldw, h.Fkdw, null, null);
  159 + if (string.IsNullOrWhiteSpace(cp))
  160 + throw new InvalidOperationException("往来单位不能为空");
  161 +
  162 + if (mxList == null || mxList.Count == 0)
  163 + throw new InvalidOperationException("请先维护收入明细");
  164 +
  165 + foreach (var mx in mxList)
  166 + {
  167 + if (string.IsNullOrWhiteSpace(mx.Km))
  168 + throw new InvalidOperationException("明细科目不能为空");
  169 + if (mx.Je <= 0)
  170 + throw new InvalidOperationException("明细金额必须大于 0");
  171 + if (string.IsNullOrWhiteSpace(mx.Mxlx))
  172 + throw new InvalidOperationException("明细类型不能为空");
  173 + }
  174 + }
  175 +
  176 + /// <summary>
  177 + /// 草稿 / 审核不通过 → 待审核
  178 + /// </summary>
  179 + public async Task<dynamic> SubmitForAuditAsync(string id)
  180 + {
  181 + EnsureFysrdAuditColumns();
  182 + var entity = await _db.Queryable<WtFysrdEntity>().FirstAsync(p => p.Id == id);
  183 + if (entity == null)
  184 + return new { success = false, message = "单据不存在" };
  185 + if (!IsOtherIncomeDjlx(entity.Djlx))
  186 + return new { success = false, message = "该接口仅支持其他收入单" };
  187 +
  188 + var st = entity.Djzt?.Trim() ?? "";
  189 + if (st != "草稿" && st != "审核不通过")
  190 + return new { success = false, message = "仅草稿或审核不通过状态可提交审核" };
  191 +
  192 + var mxList = await _db.Queryable<WtFysrdMxEntity>().Where(w => w.Djbh == id).ToListAsync();
  193 + try
  194 + {
  195 + ValidateOtherIncomeForSubmit(entity, mxList);
  196 + }
  197 + catch (InvalidOperationException ex)
  198 + {
  199 + return new { success = false, message = ex.Message };
  200 + }
  201 +
  202 + entity.Djzt = "待审核";
  203 + await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt }).ExecuteCommandAsync();
  204 + return new { success = true, message = "已提交审核,请在待办中心处理" };
  205 + }
  206 +
  207 + /// <summary>
  208 + /// 与 <see cref="NCC.Extend.WtXsckd.WtXsckdService.ApproveGeneric"/> 对接
  209 + /// </summary>
  210 + public async Task<dynamic> ApproveAsync(string id, string remark)
  211 + {
  212 + EnsureFysrdAuditColumns();
  213 + try
  214 + {
  215 + _db.BeginTran();
  216 + var entity = await _db.Queryable<WtFysrdEntity>().FirstAsync(p => p.Id == id);
  217 + if (entity == null)
  218 + {
  219 + _db.RollbackTran();
  220 + return new { success = false, message = "单据不存在" };
  221 + }
  222 + if (!IsOtherIncomeDjlx(entity.Djlx))
  223 + {
  224 + _db.RollbackTran();
  225 + return new { success = false, message = "该单据不是其他收入单" };
  226 + }
  227 +
  228 + if (entity.Djzt == "已审核")
  229 + {
  230 + _db.RollbackTran();
  231 + return new { success = false, message = "该单据已经审核,无需重复审核" };
  232 + }
  233 +
  234 + var userInfo = await _userManager.GetUserInfo();
  235 + var userId = userInfo?.userId ?? "";
  236 + var userAccount = userInfo?.userAccount?.Trim();
  237 + if (string.IsNullOrEmpty(userAccount))
  238 + userAccount = _userManager.Account?.Trim();
  239 +
  240 + var approvalConfig = await _db.Queryable<WtShryszEntity>()
  241 + .Where(c => c.Djmc == BillName)
  242 + .FirstAsync();
  243 +
  244 + var configShr1 = approvalConfig?.Shr1;
  245 + var configShr2 = approvalConfig?.Shr2;
  246 + var hasTwoLevel = !string.IsNullOrWhiteSpace(configShr1) && !string.IsNullOrWhiteSpace(configShr2);
  247 +
  248 + if (string.IsNullOrWhiteSpace(configShr1))
  249 + {
  250 + _db.RollbackTran();
  251 + return new { success = false, message = "未配置其他收入单一级审核人员,请先在审核人员设置中维护" };
  252 + }
  253 +
  254 + var djztTrim = entity.Djzt?.Trim() ?? "";
  255 + if (entity.Djzt == "待审核" || string.IsNullOrEmpty(djztTrim))
  256 + {
  257 + if (!IsUserInAuditConfig(configShr1, userId, userAccount))
  258 + {
  259 + _db.RollbackTran();
  260 + return new { success = false, message = "当前用户不在其他收入单一级审核人员范围内" };
  261 + }
  262 +
  263 + if (hasTwoLevel)
  264 + {
  265 + entity.Djzt = "一级已审";
  266 + entity.Shr1 = userId;
  267 + AppendSpbz(entity, "一级通过", remark);
  268 + await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Shr1, x.Spbz }).ExecuteCommandAsync();
  269 + _db.CommitTran();
  270 + return new { success = true, message = "一级审核通过,等待二级审核" };
  271 + }
  272 +
  273 + entity.Djzt = "已审核";
  274 + entity.Shr = userId;
  275 + entity.Shr1 = userId;
  276 + AppendSpbz(entity, "审核通过", remark);
  277 + await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Shr, x.Shr1, x.Spbz }).ExecuteCommandAsync();
  278 + _db.CommitTran();
  279 + return new { success = true, message = "审核通过" };
  280 + }
  281 +
  282 + if (entity.Djzt == "一级已审" || entity.Djzt == "待二级" || entity.Djzt == "一级已审/待二级")
  283 + {
  284 + if (!hasTwoLevel)
  285 + {
  286 + _db.RollbackTran();
  287 + return new { success = false, message = "当前为单级审核配置,单据状态异常" };
  288 + }
  289 + if (string.IsNullOrWhiteSpace(configShr2))
  290 + {
  291 + _db.RollbackTran();
  292 + return new { success = false, message = "未配置其他收入单二级审核人员,请先在审核人员设置中维护" };
  293 + }
  294 + if (!IsUserInAuditConfig(configShr2, userId, userAccount))
  295 + {
  296 + _db.RollbackTran();
  297 + return new { success = false, message = "当前用户不在其他收入单二级审核人员范围内" };
  298 + }
  299 +
  300 + entity.Djzt = "已审核";
  301 + entity.Shr2 = userId;
  302 + entity.Shr = userId;
  303 + AppendSpbz(entity, "二级通过", remark);
  304 + await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Shr, x.Shr2, x.Spbz }).ExecuteCommandAsync();
  305 + _db.CommitTran();
  306 + return new { success = true, message = "二级审核通过,审核完成" };
  307 + }
  308 +
  309 + _db.RollbackTran();
  310 + return new { success = false, message = $"当前单据状态「{entity.Djzt}」不允许审核" };
  311 + }
  312 + catch (Exception ex)
  313 + {
  314 + _db.RollbackTran();
  315 + return new { success = false, message = $"审核失败: {ex.Message}" };
  316 + }
  317 + }
  318 +
  319 + /// <summary>
  320 + /// 与 RejectGeneric 对接
  321 + /// </summary>
  322 + public async Task<dynamic> RejectAsync(string id, string remark)
  323 + {
  324 + EnsureFysrdAuditColumns();
  325 + var entity = await _db.Queryable<WtFysrdEntity>().FirstAsync(p => p.Id == id);
  326 + if (entity == null)
  327 + return new { success = false, message = "单据不存在" };
  328 + if (!IsOtherIncomeDjlx(entity.Djlx))
  329 + return new { success = false, message = "该单据不是其他收入单" };
  330 + if (entity.Djzt == "已审核")
  331 + return new { success = false, message = "该单据已审核通过" };
  332 + if (entity.Djzt == "审核不通过")
  333 + return new { success = false, message = "该单据已是审核不通过状态" };
  334 + if (entity.Djzt == "草稿")
  335 + return new { success = false, message = "草稿状态请直接修改或删除单据" };
  336 +
  337 + var userInfo = await _userManager.GetUserInfo();
  338 + var userId = userInfo?.userId ?? "";
  339 + var userAccount = userInfo?.userAccount?.Trim();
  340 + if (string.IsNullOrEmpty(userAccount))
  341 + userAccount = _userManager.Account?.Trim();
  342 +
  343 + var approvalConfig = await _db.Queryable<WtShryszEntity>()
  344 + .Where(c => c.Djmc == BillName)
  345 + .FirstAsync();
  346 + var configShr1 = approvalConfig?.Shr1;
  347 + var configShr2 = approvalConfig?.Shr2;
  348 +
  349 + var djztTrim = entity.Djzt?.Trim() ?? "";
  350 + var level1 = djztTrim == "待审核" || string.IsNullOrEmpty(djztTrim);
  351 + var level2 = entity.Djzt == "一级已审" || entity.Djzt == "待二级" || entity.Djzt == "一级已审/待二级";
  352 +
  353 + if (!level1 && !level2)
  354 + return new { success = false, message = $"当前单据状态「{entity.Djzt}」不允许审核不通过" };
  355 +
  356 + if (level1)
  357 + {
  358 + if (string.IsNullOrWhiteSpace(configShr1))
  359 + return new { success = false, message = "未配置其他收入单一级审核人员,请先在审核人员设置中维护" };
  360 + if (!IsUserInAuditConfig(configShr1, userId, userAccount))
  361 + return new { success = false, message = "当前用户不在其他收入单一级审核人员范围内" };
  362 + }
  363 + else
  364 + {
  365 + if (string.IsNullOrWhiteSpace(configShr2))
  366 + return new { success = false, message = "未配置其他收入单二级审核人员,请先在审核人员设置中维护" };
  367 + if (!IsUserInAuditConfig(configShr2, userId, userAccount))
  368 + return new { success = false, message = "当前用户不在其他收入单二级审核人员范围内" };
  369 + }
  370 +
  371 + _db.BeginTran();
  372 + try
  373 + {
  374 + var row = await _db.Queryable<WtFysrdEntity>().FirstAsync(p => p.Id == id);
  375 + if (row == null)
  376 + {
  377 + _db.RollbackTran();
  378 + return new { success = false, message = "单据不存在" };
  379 + }
  380 + AppendSpbz(row, "审核不通过", remark);
  381 + row.Djzt = "审核不通过";
  382 + row.Shr = userId;
  383 + row.Shr1 = null;
  384 + row.Shr2 = null;
  385 + await _db.Updateable(row).UpdateColumns(x => new { x.Djzt, x.Spbz, x.Shr, x.Shr1, x.Shr2 }).ExecuteCommandAsync();
  386 + _db.CommitTran();
  387 + return new { success = true, message = "已标记为审核不通过" };
  388 + }
  389 + catch (Exception ex)
  390 + {
  391 + _db.RollbackTran();
  392 + return new { success = false, message = $"操作失败: {ex.Message}" };
  393 + }
  394 + }
  395 +
  396 + /// <summary>
  397 + /// 与 ReverseApproval 对接:已审核或一级已审 → 待审核
  398 + /// </summary>
  399 + public async Task<dynamic> ReverseAsync(string id)
  400 + {
  401 + EnsureFysrdAuditColumns();
  402 + try
  403 + {
  404 + var entity = await _db.Queryable<WtFysrdEntity>().FirstAsync(p => p.Id == id);
  405 + if (entity == null)
  406 + return new { success = false, message = "单据不存在" };
  407 + if (!IsOtherIncomeDjlx(entity.Djlx))
  408 + return new { success = false, message = "该单据不是其他收入单" };
  409 +
  410 + if (entity.Djzt != "已审核" && entity.Djzt != "一级已审" && entity.Djzt != "一级已审/待二级" && entity.Djzt != "待二级")
  411 + return new { success = false, message = "当前单据状态不允许反审" };
  412 +
  413 + var userInfo = await _userManager.GetUserInfo();
  414 + var userId = userInfo?.userId ?? "";
  415 + var userAccount = userInfo?.userAccount?.Trim();
  416 + if (string.IsNullOrEmpty(userAccount))
  417 + userAccount = _userManager.Account?.Trim();
  418 +
  419 + var approvalConfig = await _db.Queryable<WtShryszEntity>()
  420 + .Where(c => c.Djmc == BillName)
  421 + .FirstAsync();
  422 +
  423 + var configShr1 = approvalConfig?.Shr1;
  424 + var configShr2 = approvalConfig?.Shr2;
  425 + var bothUnset = string.IsNullOrWhiteSpace(configShr1) && string.IsNullOrWhiteSpace(configShr2);
  426 + var isAuthorized = bothUnset
  427 + || IsUserInAuditConfig(configShr1 ?? "", userId, userAccount)
  428 + || IsUserInAuditConfig(configShr2 ?? "", userId, userAccount);
  429 +
  430 + if (!isAuthorized)
  431 + return new { success = false, message = "您没有反审权限,只有一级或二级审核人可以反审" };
  432 +
  433 + entity.Djzt = "待审核";
  434 + entity.Shr = null;
  435 + entity.Shr1 = null;
  436 + entity.Shr2 = null;
  437 + await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Shr, x.Shr1, x.Shr2 }).ExecuteCommandAsync();
  438 + return new { success = true, message = "反审成功,单据已恢复为待审核状态" };
  439 + }
  440 + catch (Exception ex)
  441 + {
  442 + return new { success = false, message = $"反审失败: {ex.Message}" };
  443 + }
  444 + }
  445 + }
  446 +}
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtXsckdService.cs
@@ -50,6 +50,7 @@ namespace NCC.Extend.WtXsckd @@ -50,6 +50,7 @@ namespace NCC.Extend.WtXsckd
50 private readonly WtBillSummaryService _billSummaryService; 50 private readonly WtBillSummaryService _billSummaryService;
51 private readonly WtDjzyService _wtDjzyService; 51 private readonly WtDjzyService _wtDjzyService;
52 private readonly WtTjd.WtTjdWorkflowHelper _tjdWorkflowHelper; 52 private readonly WtTjd.WtTjdWorkflowHelper _tjdWorkflowHelper;
  53 + private readonly WtFysrd.WtFysrdWorkflowHelper _fysrdWorkflowHelper;
53 54
54 // 序列号生成信号量,确保线程安全 55 // 序列号生成信号量,确保线程安全
55 private static readonly SemaphoreSlim _serialNumberSemaphore = new SemaphoreSlim(1, 1); 56 private static readonly SemaphoreSlim _serialNumberSemaphore = new SemaphoreSlim(1, 1);
@@ -1691,7 +1692,8 @@ WHERE d.djlx IN (&#39;销售退货单&#39;,&#39;预售退货单&#39;,&#39;委托代销退货单&#39;) @@ -1691,7 +1692,8 @@ WHERE d.djlx IN (&#39;销售退货单&#39;,&#39;预售退货单&#39;,&#39;委托代销退货单&#39;)
1691 IUserManager userManager, 1692 IUserManager userManager,
1692 WtBillSummaryService billSummaryService, 1693 WtBillSummaryService billSummaryService,
1693 WtDjzyService wtDjzyService, 1694 WtDjzyService wtDjzyService,
1694 - WtTjd.WtTjdWorkflowHelper tjdWorkflowHelper) 1695 + WtTjd.WtTjdWorkflowHelper tjdWorkflowHelper,
  1696 + WtFysrd.WtFysrdWorkflowHelper fysrdWorkflowHelper)
1695 { 1697 {
1696 _wtXsckdRepository = wtXsckdRepository; 1698 _wtXsckdRepository = wtXsckdRepository;
1697 _db = _wtXsckdRepository.Context; 1699 _db = _wtXsckdRepository.Context;
@@ -1700,6 +1702,7 @@ WHERE d.djlx IN (&#39;销售退货单&#39;,&#39;预售退货单&#39;,&#39;委托代销退货单&#39;) @@ -1700,6 +1702,7 @@ WHERE d.djlx IN (&#39;销售退货单&#39;,&#39;预售退货单&#39;,&#39;委托代销退货单&#39;)
1700 _billSummaryService = billSummaryService; 1702 _billSummaryService = billSummaryService;
1701 _wtDjzyService = wtDjzyService; 1703 _wtDjzyService = wtDjzyService;
1702 _tjdWorkflowHelper = tjdWorkflowHelper; 1704 _tjdWorkflowHelper = tjdWorkflowHelper;
  1705 + _fysrdWorkflowHelper = fysrdWorkflowHelper;
1703 } 1706 }
1704 1707
1705 /// <summary> 1708 /// <summary>
@@ -4453,7 +4456,12 @@ ORDER BY IFNULL(MAX(pp.F_Ppmc), &#39;&#39;), IFNULL(MAX(s.F_Spbm), &#39;&#39;)&quot;; @@ -4453,7 +4456,12 @@ ORDER BY IFNULL(MAX(pp.F_Ppmc), &#39;&#39;), IFNULL(MAX(s.F_Spbm), &#39;&#39;)&quot;;
4453 AddInCondition("c.ck", input.Warehouse, "stockWh"); 4456 AddInCondition("c.ck", input.Warehouse, "stockWh");
4454 AddInCondition("s.F_Pp", input.Brand, "stockPp"); 4457 AddInCondition("s.F_Pp", input.Brand, "stockPp");
4455 4458
4456 - if (!string.IsNullOrWhiteSpace(input.Product)) 4459 + if (!string.IsNullOrWhiteSpace(input.ProductSpId))
  4460 + {
  4461 + whereList.Add("s.F_Id = @stockProdId");
  4462 + paramList.Add(new SugarParameter("@stockProdId", input.ProductSpId.Trim()));
  4463 + }
  4464 + else if (!string.IsNullOrWhiteSpace(input.Product))
4457 { 4465 {
4458 whereList.Add("(s.F_Spmc LIKE @stockProdKw OR s.F_Spbm LIKE @stockProdKw)"); 4466 whereList.Add("(s.F_Spmc LIKE @stockProdKw OR s.F_Spbm LIKE @stockProdKw)");
4459 paramList.Add(new SugarParameter("@stockProdKw", $"%{input.Product.Trim()}%")); 4467 paramList.Add(new SugarParameter("@stockProdKw", $"%{input.Product.Trim()}%"));
@@ -4464,6 +4472,51 @@ ORDER BY IFNULL(MAX(pp.F_Ppmc), &#39;&#39;), IFNULL(MAX(s.F_Spbm), &#39;&#39;)&quot;; @@ -4464,6 +4472,51 @@ ORDER BY IFNULL(MAX(pp.F_Ppmc), &#39;&#39;), IFNULL(MAX(s.F_Spbm), &#39;&#39;)&quot;;
4464 } 4472 }
4465 4473
4466 /// <summary> 4474 /// <summary>
  4475 + /// 商品库存汇总(按仓库聚合):用于“选择某个商品后,展示该商品在各仓数量/成本”。
  4476 + /// </summary>
  4477 + /// <param name="input">必须提供 ProductSpId;可选 Warehouse(限定仓库范围)</param>
  4478 + [HttpGet("Actions/GetStockSummaryByWarehouse")]
  4479 + public async Task<dynamic> GetStockSummaryByWarehouse([FromQuery] WtStockSummaryQueryInput input)
  4480 + {
  4481 + input ??= new WtStockSummaryQueryInput();
  4482 + if (string.IsNullOrWhiteSpace(input.ProductSpId))
  4483 + {
  4484 + throw NCCException.Oh("商品 productSpId 不能为空");
  4485 + }
  4486 +
  4487 + // 仓库维度不需要分类/品牌筛选,但允许传 warehouse 限定仓库范围
  4488 + var (whereSql, paramList) = BuildWtStockCostSummaryWhere(
  4489 + new WtStockSummaryQueryInput
  4490 + {
  4491 + Warehouse = input.Warehouse,
  4492 + ProductSpId = input.ProductSpId
  4493 + },
  4494 + null);
  4495 +
  4496 + var sql = $@"
  4497 +SELECT
  4498 + c.ck AS `仓库Id`,
  4499 + IFNULL(NULLIF(TRIM(ck.F_mdmc), ''), CONCAT('未知仓库(', c.ck, ')')) AS `仓库名称`,
  4500 + IFNULL(NULLIF(TRIM(MAX(s.F_Spbm)), ''), '无') AS `商品编码`,
  4501 + IFNULL(NULLIF(TRIM(MAX(s.F_Spmc)), ''), '无') AS `商品名称`,
  4502 + SUM(CAST(c.sl AS DECIMAL(18,4))) AS `数量`,
  4503 + CASE WHEN SUM(CAST(c.sl AS DECIMAL(18,4))) = 0 THEN 0
  4504 + ELSE SUM(CAST(c.sl AS DECIMAL(18,4)) * c.cbj) / SUM(CAST(c.sl AS DECIMAL(18,4))) END AS `成本均价`,
  4505 + SUM(CAST(c.sl AS DECIMAL(18,4)) * c.cbj) AS `总成本`
  4506 +FROM wt_sp_cost c
  4507 +INNER JOIN wt_sp s ON s.F_Id = c.spbh
  4508 +LEFT JOIN wt_ck ck ON ck.F_Id = c.ck
  4509 +WHERE c.sl <> 0
  4510 + AND c.ck IS NOT NULL AND TRIM(c.ck) <> ''
  4511 + {whereSql}
  4512 +GROUP BY c.ck, ck.F_mdmc
  4513 +ORDER BY SUM(CAST(c.sl AS DECIMAL(18,4)) * c.cbj) DESC";
  4514 +
  4515 + var list = await _db.Ado.GetDataTableAsync(sql, paramList);
  4516 + return list;
  4517 + }
  4518 +
  4519 + /// <summary>
4467 /// 获取指定商品在指定门店/仓库范围内的库存数量(与商品库存汇总口径一致:仅汇总 <c>wt_sp_cost.sl</c>,不按序列号条数统计;大量无序列号商品亦适用)。 4520 /// 获取指定商品在指定门店/仓库范围内的库存数量(与商品库存汇总口径一致:仅汇总 <c>wt_sp_cost.sl</c>,不按序列号条数统计;大量无序列号商品亦适用)。
4468 /// </summary> 4521 /// </summary>
4469 /// <param name="productId">商品ID</param> 4522 /// <param name="productId">商品ID</param>
@@ -4580,11 +4633,135 @@ INNER JOIN wt_xsckd d ON d.F_Id = mx.djbh @@ -4580,11 +4633,135 @@ INNER JOIN wt_xsckd d ON d.F_Id = mx.djbh
4580 LEFT JOIN wt_sp sp ON sp.F_Id = mx.spbh 4633 LEFT JOIN wt_sp sp ON sp.F_Id = mx.spbh
4581 LEFT JOIN wt_pl pl ON pl.F_Id = sp.F_Pl 4634 LEFT JOIN wt_pl pl ON pl.F_Id = sp.F_Pl
4582 LEFT JOIN wt_pp pp ON pp.F_Id = sp.F_Pp 4635 LEFT JOIN wt_pp pp ON pp.F_Id = sp.F_Pp
4583 -LEFT JOIN wt_wldw w ON w.F_Id = d.gys  
4584 -LEFT JOIN BASE_USER u ON u.F_Id = d.jsr 4636 +LEFT JOIN wt_wldw w ON w.F_Id = IFNULL(NULLIF(TRIM(d.gys), ''), d.kh)
  4637 +LEFT JOIN BASE_USER u ON u.F_Id = IFNULL(NULLIF(TRIM(d.jsr), ''), d.zdr)
4585 LEFT JOIN wt_ck ck ON ck.F_Id = IFNULL(NULLIF(TRIM(mx.rkck), ''), d.rkck) 4638 LEFT JOIN wt_ck ck ON ck.F_Id = IFNULL(NULLIF(TRIM(mx.rkck), ''), d.rkck)
4586 WHERE 1 = 1"; 4639 WHERE 1 = 1";
4587 4640
  4641 + /// <summary>采购汇总限定单据类型(默认全集)。</summary>
  4642 + private static readonly string[] PurSumAllowedDjlx =
  4643 + {
  4644 + "采购入库单", "采购退货单", "销售出库单", "销售退货单", "预售出库单", "预售退货单",
  4645 + "委托代销发货单", "委托代销退货单", "委托代销结算单"
  4646 + };
  4647 +
  4648 + /// <summary>主表 d.djlx 加减符号(与明细 mx 解耦)。</summary>
  4649 + private const string PurSumBizSignCase =
  4650 + "CASE WHEN d.djlx IN ('采购入库单','销售退货单','预售退货单','委托代销退货单') THEN 1 "
  4651 + + "WHEN d.djlx IN ('采购退货单','销售出库单','预售出库单','委托代销发货单','委托代销结算单') THEN -1 ELSE 0 END";
  4652 +
  4653 + // 注意:部分退货/冲销类单据明细 mx.sl/mx.je 可能已为负数;若再按单据类型乘 -1 会出现“双重取反”导致统计方向错误。
  4654 + // 统一口径:先取 ABS,再按单据类型决定正负。
  4655 + private static string PurSumSignedSlExpr => $"(ABS(CAST(mx.sl AS DECIMAL(18,4))) * ({PurSumBizSignCase}))";
  4656 +
  4657 + private static string PurSumSignedJeExpr => $"(ABS(CAST(mx.je AS DECIMAL(18,4))) * ({PurSumBizSignCase}))";
  4658 +
  4659 + // 单价保持正数展示(方向由数量/金额体现),避免出现负单价
  4660 + private static string PurSumSignedDjExpr => "(ABS(CAST(mx.dj AS DECIMAL(18,6))))";
  4661 +
  4662 + private static string NormalizePurSumDjlx(string raw)
  4663 + {
  4664 + if (string.IsNullOrWhiteSpace(raw))
  4665 + {
  4666 + return raw;
  4667 + }
  4668 +
  4669 + var t = raw.Trim();
  4670 + return t == "委托代销结算" ? "委托代销结算单" : t;
  4671 + }
  4672 +
  4673 + /// <summary>
  4674 + /// 采购汇总聚合 ORDER BY(白名单)。
  4675 + /// </summary>
  4676 + private static string BuildPurchaseSummaryAggOrderBy(string kind, string sortField, string sortOrder)
  4677 + {
  4678 + var dir = string.Equals(sortOrder?.Trim(), "asc", StringComparison.OrdinalIgnoreCase)
  4679 + ? "ASC"
  4680 + : "DESC";
  4681 +
  4682 + var sumJe = $"SUM({PurSumSignedJeExpr})";
  4683 + var sumSl = $"SUM({PurSumSignedSlExpr})";
  4684 + var avgDj = $"CASE WHEN ABS({sumSl}) < 0.00000001 THEN 0 ELSE {sumJe} / {sumSl} END";
  4685 +
  4686 + if (string.Equals(kind, "category", StringComparison.OrdinalIgnoreCase))
  4687 + {
  4688 + var col = (sortField?.Trim()) switch
  4689 + {
  4690 + "分类Id" or "categoryId" => "IFNULL(NULLIF(TRIM(sp.F_Pl), ''), '__NONE__')",
  4691 + "分类名称" or "categoryName" => "IFNULL(NULLIF(TRIM(MAX(pl.F_Plmc)), ''), '无')",
  4692 + "数量" or "qty" => sumSl,
  4693 + "入库单价" or "inboundPrice" or "unitPrice" => avgDj,
  4694 + "采购金额" or "purchaseAmount" => sumJe,
  4695 + _ => sumJe
  4696 + };
  4697 + return $"{col} {dir}";
  4698 + }
  4699 +
  4700 + if (string.Equals(kind, "brand", StringComparison.OrdinalIgnoreCase))
  4701 + {
  4702 + var col = (sortField?.Trim()) switch
  4703 + {
  4704 + "品牌Id" or "brandId" => "IFNULL(NULLIF(TRIM(sp.F_Pp), ''), '')",
  4705 + "品牌名称" or "brandName" => "IFNULL(NULLIF(TRIM(MAX(pp.F_Ppmc)), ''), '无')",
  4706 + "数量" or "qty" => sumSl,
  4707 + "入库单价" or "inboundPrice" or "unitPrice" => avgDj,
  4708 + "采购金额" or "purchaseAmount" => sumJe,
  4709 + _ => sumJe
  4710 + };
  4711 + return $"{col} {dir}";
  4712 + }
  4713 +
  4714 + if (string.Equals(kind, "productAgg", StringComparison.OrdinalIgnoreCase))
  4715 + {
  4716 + var col = (sortField?.Trim()) switch
  4717 + {
  4718 + "商品Id" or "productId" => "sp.F_Id",
  4719 + "商品编号" or "productCode" => "IFNULL(NULLIF(TRIM(MAX(sp.F_Spbm)), ''), '无')",
  4720 + "商品名称" or "productName" => "IFNULL(NULLIF(TRIM(MAX(sp.F_Spmc)), ''), '无')",
  4721 + "明细分类" or "detailCategory" =>
  4722 + "IFNULL(NULLIF(TRIM(CONCAT_WS(' / ', NULLIF(TRIM(sp.F_Splx1), ''), NULLIF(TRIM(sp.F_Splx2), ''))), ''), '无')",
  4723 + "数量" or "qty" => sumSl,
  4724 + "入库单价" or "inboundPrice" or "unitPrice" => avgDj,
  4725 + "采购金额" or "purchaseAmount" => sumJe,
  4726 + _ => sumJe
  4727 + };
  4728 + return $"{col} {dir}";
  4729 + }
  4730 +
  4731 + return $"{sumJe} DESC";
  4732 + }
  4733 +
  4734 + /// <summary>
  4735 + /// 采购明细 / 线性列表 ORDER BY(仅允许白名单列,防注入);数量金额单价按采购汇总符号口径排序。
  4736 + /// </summary>
  4737 + private static string BuildPurchaseSummaryDetailOrderBy(string sortField, string sortOrder)
  4738 + {
  4739 + var dir = string.Equals(sortOrder?.Trim(), "asc", StringComparison.OrdinalIgnoreCase)
  4740 + ? "ASC"
  4741 + : "DESC";
  4742 +
  4743 + var agentExpr = "IFNULL(NULLIF(TRIM(u.F_RealName), ''), '无')";
  4744 +
  4745 + var col = sortField?.Trim() switch
  4746 + {
  4747 + "单据日期" or "billDate" => "d.djrq",
  4748 + "单据编号" or "billId" => "d.F_Id",
  4749 + "单据类型" or "billType" => "d.djlx",
  4750 + "往来单位" or "contactName" => "IFNULL(NULLIF(TRIM(w.dwmc), ''), '无')",
  4751 + "经手人" or "agentName" => agentExpr,
  4752 + "仓库名称" or "warehouseName" => "IFNULL(NULLIF(TRIM(ck.F_mdmc), ''), '无')",
  4753 + "商品名称" or "productName" => "IFNULL(NULLIF(TRIM(mx.spmc), ''), '无')",
  4754 + "数量" or "qty" => PurSumSignedSlExpr,
  4755 + "入库单价" or "inboundPrice" or "unitPrice" => PurSumSignedDjExpr,
  4756 + "采购金额" or "purchaseAmount" => PurSumSignedJeExpr,
  4757 + _ => null
  4758 + };
  4759 +
  4760 + return string.IsNullOrEmpty(col)
  4761 + ? "d.djrq DESC, d.F_Id DESC, mx.F_Id"
  4762 + : $"{col} {dir}, d.F_Id DESC, mx.F_Id";
  4763 + }
  4764 +
4588 /// <summary> 4765 /// <summary>
4589 /// 采购汇总公共 WHERE(需配合含 sp/pl/pp 的 PurchaseSummaryJoinFromSql 使用)。 4766 /// 采购汇总公共 WHERE(需配合含 sp/pl/pp 的 PurchaseSummaryJoinFromSql 使用)。
4590 /// </summary> 4767 /// </summary>
@@ -4644,22 +4821,56 @@ WHERE 1 = 1&quot;; @@ -4644,22 +4821,56 @@ WHERE 1 = 1&quot;;
4644 paramList.Add(new SugarParameter("@purSumEndDate", endDate.Date.AddDays(1))); 4821 paramList.Add(new SugarParameter("@purSumEndDate", endDate.Date.AddDays(1)));
4645 } 4822 }
4646 4823
4647 - AddInCondition("d.gys", input.ContactUnit, "purSumContact");  
4648 -  
4649 - // 经手人:主表 wt_xsckd.jsr 存 BASE_USER.F_Id,按用户主键 IN 筛选(与前端下拉 value 一致)  
4650 - AddInCondition("d.jsr", input.Agent, "purSumJsr"); 4824 + whereList.Add("d.djzt = @purSumDjzt");
  4825 + paramList.Add(new SugarParameter("@purSumDjzt", "已审核"));
4651 4826
4652 - AddInCondition("IFNULL(NULLIF(TRIM(mx.rkck), ''), d.rkck)", input.Warehouse, "purSumWh");  
4653 -  
4654 - var billTypeValues = SplitCsv(input.BillType); 4827 + var allowedSet = new HashSet<string>(PurSumAllowedDjlx, StringComparer.Ordinal);
  4828 + var billRaw = input.BillType?.Trim();
  4829 + var billAll = string.IsNullOrEmpty(billRaw) || billRaw.Equals("all", StringComparison.OrdinalIgnoreCase);
  4830 + var billRequested = SplitCsv(input.BillType).Select(NormalizePurSumDjlx).Where(s => !string.IsNullOrWhiteSpace(s)).ToList();
  4831 + var billTypeValues = billAll || billRequested.Count == 0
  4832 + ? PurSumAllowedDjlx.ToList()
  4833 + : billRequested.Where(allowedSet.Contains).Distinct().ToList();
4655 if (billTypeValues.Count == 0) 4834 if (billTypeValues.Count == 0)
4656 { 4835 {
4657 - billTypeValues.Add("采购入库单");  
4658 - billTypeValues.Add("采购退货单"); 4836 + billTypeValues = PurSumAllowedDjlx.ToList();
4659 } 4837 }
4660 4838
4661 AddInCondition("d.djlx", string.Join(",", billTypeValues), "purSumDjlx"); 4839 AddInCondition("d.djlx", string.Join(",", billTypeValues), "purSumDjlx");
4662 4840
  4841 + var contactVals = SplitCsv(input.ContactUnit);
  4842 + if (contactVals.Count > 0)
  4843 + {
  4844 + var gysParts = new List<string>();
  4845 + var khParts = new List<string>();
  4846 + for (var i = 0; i < contactVals.Count; i++)
  4847 + {
  4848 + var pn = $"@purSumContact{i}";
  4849 + gysParts.Add($"d.gys = {pn}");
  4850 + khParts.Add($"d.kh = {pn}");
  4851 + paramList.Add(new SugarParameter(pn, contactVals[i]));
  4852 + }
  4853 +
  4854 + whereList.Add($"(({string.Join(" OR ", gysParts)}) OR ({string.Join(" OR ", khParts)}))");
  4855 + }
  4856 +
  4857 + var agentVals = SplitCsv(input.Agent);
  4858 + if (agentVals.Count > 0)
  4859 + {
  4860 + var agentParts = new List<string>();
  4861 + for (var i = 0; i < agentVals.Count; i++)
  4862 + {
  4863 + var pn = $"@purSumJsr{i}";
  4864 + agentParts.Add(
  4865 + $"IFNULL(NULLIF(TRIM(d.jsr), ''), IFNULL(NULLIF(TRIM(d.zdr), ''), '')) = {pn}");
  4866 + paramList.Add(new SugarParameter(pn, agentVals[i]));
  4867 + }
  4868 +
  4869 + whereList.Add($"({string.Join(" OR ", agentParts)})");
  4870 + }
  4871 +
  4872 + AddInCondition("IFNULL(NULLIF(TRIM(mx.rkck), ''), d.rkck)", input.Warehouse, "purSumWh");
  4873 +
4663 var productValues = SplitCsv(input.Product); 4874 var productValues = SplitCsv(input.Product);
4664 if (productValues.Count > 0) 4875 if (productValues.Count > 0)
4665 { 4876 {
@@ -4668,7 +4879,8 @@ WHERE 1 = 1&quot;; @@ -4668,7 +4879,8 @@ WHERE 1 = 1&quot;;
4668 { 4879 {
4669 var pEq = $"@purProdEq{i}"; 4880 var pEq = $"@purProdEq{i}";
4670 var pLike = $"@purProdLike{i}"; 4881 var pLike = $"@purProdLike{i}";
4671 - productConditions.Add($"(mx.spbh = {pEq} OR sp.F_Spbm = {pEq} OR mx.spmc LIKE {pLike} OR IFNULL(sp.F_Spmc, '') LIKE {pLike})"); 4882 + productConditions.Add(
  4883 + $"(mx.spbh = {pEq} OR sp.F_Id = {pEq} OR sp.F_Spbm = {pEq} OR mx.spmc LIKE {pLike} OR IFNULL(sp.F_Spmc, '') LIKE {pLike})");
4672 paramList.Add(new SugarParameter(pEq, productValues[i])); 4884 paramList.Add(new SugarParameter(pEq, productValues[i]));
4673 paramList.Add(new SugarParameter(pLike, $"%{productValues[i]}%")); 4885 paramList.Add(new SugarParameter(pLike, $"%{productValues[i]}%"));
4674 } 4886 }
@@ -4697,7 +4909,7 @@ WHERE 1 = 1&quot;; @@ -4697,7 +4909,7 @@ WHERE 1 = 1&quot;;
4697 var spId = !string.IsNullOrWhiteSpace(scopeProductSpId) ? scopeProductSpId.Trim() : (input.ProductSpId ?? string.Empty).Trim(); 4909 var spId = !string.IsNullOrWhiteSpace(scopeProductSpId) ? scopeProductSpId.Trim() : (input.ProductSpId ?? string.Empty).Trim();
4698 if (!string.IsNullOrEmpty(spId)) 4910 if (!string.IsNullOrEmpty(spId))
4699 { 4911 {
4700 - whereList.Add("mx.spbh = @purSumScopeSp"); 4912 + whereList.Add("(mx.spbh = @purSumScopeSp OR sp.F_Id = @purSumScopeSp)");
4701 paramList.Add(new SugarParameter("@purSumScopeSp", spId)); 4913 paramList.Add(new SugarParameter("@purSumScopeSp", spId));
4702 } 4914 }
4703 4915
@@ -4713,20 +4925,21 @@ WHERE 1 = 1&quot;; @@ -4713,20 +4925,21 @@ WHERE 1 = 1&quot;;
4713 { 4925 {
4714 input ??= new WtPurchaseSummaryQueryInput(); 4926 input ??= new WtPurchaseSummaryQueryInput();
4715 var (whereSql, paramList) = BuildPurchaseSummaryWhere(input, null, null, null); 4927 var (whereSql, paramList) = BuildPurchaseSummaryWhere(input, null, null, null);
  4928 + var orderBy = BuildPurchaseSummaryAggOrderBy("category", input.SortField, input.SortOrder);
4716 var sql = $@" 4929 var sql = $@"
4717 SELECT 4930 SELECT
4718 IFNULL(NULLIF(TRIM(sp.F_Pl), ''), '__NONE__') AS `分类Id`, 4931 IFNULL(NULLIF(TRIM(sp.F_Pl), ''), '__NONE__') AS `分类Id`,
4719 IFNULL(NULLIF(TRIM(MAX(pl.F_Plmc)), ''), '无') AS `分类名称`, 4932 IFNULL(NULLIF(TRIM(MAX(pl.F_Plmc)), ''), '无') AS `分类名称`,
4720 '无' AS `商品编号`, 4933 '无' AS `商品编号`,
4721 IFNULL(NULLIF(TRIM(MAX(pl.F_Plmc)), ''), '无') AS `商品名称`, 4934 IFNULL(NULLIF(TRIM(MAX(pl.F_Plmc)), ''), '无') AS `商品名称`,
4722 - SUM(CAST(mx.sl AS DECIMAL(18,4))) AS `数量`,  
4723 - CASE WHEN ABS(SUM(CAST(mx.sl AS DECIMAL(18,4)))) < 0.00000001 THEN 0  
4724 - ELSE SUM(CAST(mx.je AS DECIMAL(18,4))) / SUM(CAST(mx.sl AS DECIMAL(18,4))) END AS `入库单价`,  
4725 - SUM(CAST(mx.je AS DECIMAL(18,4))) AS `采购金额` 4935 + SUM({PurSumSignedSlExpr}) AS `数量`,
  4936 + CASE WHEN ABS(SUM({PurSumSignedSlExpr})) < 0.00000001 THEN 0
  4937 + ELSE SUM({PurSumSignedJeExpr}) / SUM({PurSumSignedSlExpr}) END AS `入库单价`,
  4938 + SUM({PurSumSignedJeExpr}) AS `采购金额`
4726 {PurchaseSummaryJoinFromSql} 4939 {PurchaseSummaryJoinFromSql}
4727 {whereSql} 4940 {whereSql}
4728 GROUP BY sp.F_Pl, pl.F_Plmc 4941 GROUP BY sp.F_Pl, pl.F_Plmc
4729 -ORDER BY SUM(CAST(mx.je AS DECIMAL(18,4))) DESC"; 4942 +ORDER BY {orderBy}";
4730 var list = await _db.Ado.GetDataTableAsync(sql, paramList); 4943 var list = await _db.Ado.GetDataTableAsync(sql, paramList);
4731 return list; 4944 return list;
4732 } 4945 }
@@ -4744,20 +4957,21 @@ ORDER BY SUM(CAST(mx.je AS DECIMAL(18,4))) DESC&quot;; @@ -4744,20 +4957,21 @@ ORDER BY SUM(CAST(mx.je AS DECIMAL(18,4))) DESC&quot;;
4744 4957
4745 input ??= new WtPurchaseSummaryQueryInput(); 4958 input ??= new WtPurchaseSummaryQueryInput();
4746 var (whereSql, paramList) = BuildPurchaseSummaryWhere(input, categoryId, null, null); 4959 var (whereSql, paramList) = BuildPurchaseSummaryWhere(input, categoryId, null, null);
  4960 + var orderBy = BuildPurchaseSummaryAggOrderBy("brand", input.SortField, input.SortOrder);
4747 var sql = $@" 4961 var sql = $@"
4748 SELECT 4962 SELECT
4749 IFNULL(NULLIF(TRIM(sp.F_Pp), ''), '') AS `品牌Id`, 4963 IFNULL(NULLIF(TRIM(sp.F_Pp), ''), '') AS `品牌Id`,
4750 IFNULL(NULLIF(TRIM(MAX(pp.F_Ppmc)), ''), '无') AS `品牌名称`, 4964 IFNULL(NULLIF(TRIM(MAX(pp.F_Ppmc)), ''), '无') AS `品牌名称`,
4751 '无' AS `商品编号`, 4965 '无' AS `商品编号`,
4752 IFNULL(NULLIF(TRIM(MAX(pp.F_Ppmc)), ''), '无') AS `商品名称`, 4966 IFNULL(NULLIF(TRIM(MAX(pp.F_Ppmc)), ''), '无') AS `商品名称`,
4753 - SUM(CAST(mx.sl AS DECIMAL(18,4))) AS `数量`,  
4754 - CASE WHEN ABS(SUM(CAST(mx.sl AS DECIMAL(18,4)))) < 0.00000001 THEN 0  
4755 - ELSE SUM(CAST(mx.je AS DECIMAL(18,4))) / SUM(CAST(mx.sl AS DECIMAL(18,4))) END AS `入库单价`,  
4756 - SUM(CAST(mx.je AS DECIMAL(18,4))) AS `采购金额` 4967 + SUM({PurSumSignedSlExpr}) AS `数量`,
  4968 + CASE WHEN ABS(SUM({PurSumSignedSlExpr})) < 0.00000001 THEN 0
  4969 + ELSE SUM({PurSumSignedJeExpr}) / SUM({PurSumSignedSlExpr}) END AS `入库单价`,
  4970 + SUM({PurSumSignedJeExpr}) AS `采购金额`
4757 {PurchaseSummaryJoinFromSql} 4971 {PurchaseSummaryJoinFromSql}
4758 {whereSql} 4972 {whereSql}
4759 GROUP BY sp.F_Pp, pp.F_Ppmc 4973 GROUP BY sp.F_Pp, pp.F_Ppmc
4760 -ORDER BY SUM(CAST(mx.je AS DECIMAL(18,4))) DESC"; 4974 +ORDER BY {orderBy}";
4761 var list = await _db.Ado.GetDataTableAsync(sql, paramList); 4975 var list = await _db.Ado.GetDataTableAsync(sql, paramList);
4762 return list; 4976 return list;
4763 } 4977 }
@@ -4780,26 +4994,27 @@ ORDER BY SUM(CAST(mx.je AS DECIMAL(18,4))) DESC&quot;; @@ -4780,26 +4994,27 @@ ORDER BY SUM(CAST(mx.je AS DECIMAL(18,4))) DESC&quot;;
4780 4994
4781 input ??= new WtPurchaseSummaryQueryInput(); 4995 input ??= new WtPurchaseSummaryQueryInput();
4782 var (whereSql, paramList) = BuildPurchaseSummaryWhere(input, categoryId, brandId, null); 4996 var (whereSql, paramList) = BuildPurchaseSummaryWhere(input, categoryId, brandId, null);
  4997 + var orderBy = BuildPurchaseSummaryAggOrderBy("productAgg", input.SortField, input.SortOrder);
4783 var sql = $@" 4998 var sql = $@"
4784 SELECT 4999 SELECT
4785 sp.F_Id AS `商品Id`, 5000 sp.F_Id AS `商品Id`,
4786 IFNULL(NULLIF(TRIM(MAX(sp.F_Spbm)), ''), '无') AS `商品编号`, 5001 IFNULL(NULLIF(TRIM(MAX(sp.F_Spbm)), ''), '无') AS `商品编号`,
4787 IFNULL(NULLIF(TRIM(MAX(sp.F_Spmc)), ''), '无') AS `商品名称`, 5002 IFNULL(NULLIF(TRIM(MAX(sp.F_Spmc)), ''), '无') AS `商品名称`,
4788 IFNULL(NULLIF(TRIM(CONCAT_WS(' / ', NULLIF(TRIM(sp.F_Splx1), ''), NULLIF(TRIM(sp.F_Splx2), ''))), ''), '无') AS `明细分类`, 5003 IFNULL(NULLIF(TRIM(CONCAT_WS(' / ', NULLIF(TRIM(sp.F_Splx1), ''), NULLIF(TRIM(sp.F_Splx2), ''))), ''), '无') AS `明细分类`,
4789 - SUM(CAST(mx.sl AS DECIMAL(18,4))) AS `数量`,  
4790 - CASE WHEN ABS(SUM(CAST(mx.sl AS DECIMAL(18,4)))) < 0.00000001 THEN 0  
4791 - ELSE SUM(CAST(mx.je AS DECIMAL(18,4))) / SUM(CAST(mx.sl AS DECIMAL(18,4))) END AS `入库单价`,  
4792 - SUM(CAST(mx.je AS DECIMAL(18,4))) AS `采购金额` 5004 + SUM({PurSumSignedSlExpr}) AS `数量`,
  5005 + CASE WHEN ABS(SUM({PurSumSignedSlExpr})) < 0.00000001 THEN 0
  5006 + ELSE SUM({PurSumSignedJeExpr}) / SUM({PurSumSignedSlExpr}) END AS `入库单价`,
  5007 + SUM({PurSumSignedJeExpr}) AS `采购金额`
4793 {PurchaseSummaryJoinFromSql} 5008 {PurchaseSummaryJoinFromSql}
4794 {whereSql} 5009 {whereSql}
4795 GROUP BY sp.F_Id, sp.F_Splx1, sp.F_Splx2 5010 GROUP BY sp.F_Id, sp.F_Splx1, sp.F_Splx2
4796 -ORDER BY IFNULL(MAX(sp.F_Spbm), ''), IFNULL(MAX(sp.F_Spmc), '')"; 5011 +ORDER BY {orderBy}";
4797 var list = await _db.Ado.GetDataTableAsync(sql, paramList); 5012 var list = await _db.Ado.GetDataTableAsync(sql, paramList);
4798 return list; 5013 return list;
4799 } 5014 }
4800 5015
4801 /// <summary> 5016 /// <summary>
4802 - /// 商品采购「线性列表」:指定分类下全部采购明细行(最多 2000 条),与明细列表列一致 5017 + /// 商品采购「线性列表」:指定分类下全部相关单据明细(分页,与 GetPurchaseSummary 筛选及加减口径一致)
4803 /// </summary> 5018 /// </summary>
4804 [HttpGet("Actions/GetPurchaseSummaryLinear")] 5019 [HttpGet("Actions/GetPurchaseSummaryLinear")]
4805 public async Task<dynamic> GetPurchaseSummaryLinear([FromQuery] string categoryId, [FromQuery] WtPurchaseSummaryQueryInput input = null) 5020 public async Task<dynamic> GetPurchaseSummaryLinear([FromQuery] string categoryId, [FromQuery] WtPurchaseSummaryQueryInput input = null)
@@ -4811,25 +5026,50 @@ ORDER BY IFNULL(MAX(sp.F_Spbm), &#39;&#39;), IFNULL(MAX(sp.F_Spmc), &#39;&#39;)&quot;; @@ -4811,25 +5026,50 @@ ORDER BY IFNULL(MAX(sp.F_Spbm), &#39;&#39;), IFNULL(MAX(sp.F_Spmc), &#39;&#39;)&quot;;
4811 5026
4812 input ??= new WtPurchaseSummaryQueryInput(); 5027 input ??= new WtPurchaseSummaryQueryInput();
4813 var (whereSql, paramList) = BuildPurchaseSummaryWhere(input, categoryId, null, null); 5028 var (whereSql, paramList) = BuildPurchaseSummaryWhere(input, categoryId, null, null);
4814 - const int maxRows = 2000; 5029 +
  5030 + var page = input.CurrentPage.GetValueOrDefault(1);
  5031 + if (page < 1)
  5032 + {
  5033 + page = 1;
  5034 + }
  5035 +
  5036 + // 线性列表单次可拉较大窗口(默认 2000),与历史「最多 2000 条」一致;仍支持分页防一次过大。
  5037 + var pageSize = input.PageSize.GetValueOrDefault(2000);
  5038 + if (pageSize < 1)
  5039 + {
  5040 + pageSize = 2000;
  5041 + }
  5042 +
  5043 + if (pageSize > 2000)
  5044 + {
  5045 + pageSize = 2000;
  5046 + }
  5047 +
  5048 + var offset = (page - 1) * pageSize;
  5049 +
  5050 + var countSql = "SELECT COUNT(*) " + PurchaseSummaryJoinFromSql + whereSql;
  5051 + var totalObj = await _db.Ado.GetScalarAsync(countSql, paramList);
  5052 + var total = Convert.ToInt32(totalObj ?? 0);
  5053 +
  5054 + var orderByLin = BuildPurchaseSummaryDetailOrderBy(input.SortField, input.SortOrder);
4815 var sql = $@" 5055 var sql = $@"
4816 SELECT 5056 SELECT
4817 DATE_FORMAT(d.djrq, '%Y-%m-%d') AS `单据日期`, 5057 DATE_FORMAT(d.djrq, '%Y-%m-%d') AS `单据日期`,
4818 d.F_Id AS `单据编号`, 5058 d.F_Id AS `单据编号`,
4819 d.djlx AS `单据类型`, 5059 d.djlx AS `单据类型`,
4820 IFNULL(NULLIF(TRIM(w.dwmc), ''), '无') AS `往来单位`, 5060 IFNULL(NULLIF(TRIM(w.dwmc), ''), '无') AS `往来单位`,
4821 - IFNULL(NULLIF(TRIM(u.F_RealName), ''), IFNULL(NULLIF(TRIM(d.jsr), ''), '无')) AS `经手人`, 5061 + IFNULL(NULLIF(TRIM(u.F_RealName), ''), '无') AS `经手人`,
4822 IFNULL(NULLIF(TRIM(ck.F_mdmc), ''), '无') AS `仓库名称`, 5062 IFNULL(NULLIF(TRIM(ck.F_mdmc), ''), '无') AS `仓库名称`,
4823 IFNULL(NULLIF(TRIM(mx.spmc), ''), '无') AS `商品名称`, 5063 IFNULL(NULLIF(TRIM(mx.spmc), ''), '无') AS `商品名称`,
4824 - CAST(mx.sl AS DECIMAL(18,4)) AS `数量`,  
4825 - mx.dj AS `入库单价`,  
4826 - mx.je AS `采购金额` 5064 + {PurSumSignedSlExpr} AS `数量`,
  5065 + {PurSumSignedDjExpr} AS `入库单价`,
  5066 + {PurSumSignedJeExpr} AS `采购金额`
4827 {PurchaseSummaryJoinFromSql} 5067 {PurchaseSummaryJoinFromSql}
4828 {whereSql} 5068 {whereSql}
4829 -ORDER BY d.djrq DESC, d.F_Id, mx.F_Id  
4830 -LIMIT {maxRows}"; 5069 +ORDER BY {orderByLin}
  5070 +LIMIT {offset}, {pageSize}";
4831 var list = await _db.Ado.GetDataTableAsync(sql, paramList); 5071 var list = await _db.Ado.GetDataTableAsync(sql, paramList);
4832 - return list; 5072 + return new { list, total };
4833 } 5073 }
4834 5074
4835 /// <summary> 5075 /// <summary>
@@ -4866,21 +5106,22 @@ LIMIT {maxRows}&quot;; @@ -4866,21 +5106,22 @@ LIMIT {maxRows}&quot;;
4866 var totalObj = await _db.Ado.GetScalarAsync(countSql, paramList); 5106 var totalObj = await _db.Ado.GetScalarAsync(countSql, paramList);
4867 var total = Convert.ToInt32(totalObj ?? 0); 5107 var total = Convert.ToInt32(totalObj ?? 0);
4868 5108
  5109 + var orderByDetail = BuildPurchaseSummaryDetailOrderBy(input.SortField, input.SortOrder);
4869 var listSql = $@" 5110 var listSql = $@"
4870 SELECT 5111 SELECT
4871 DATE_FORMAT(d.djrq, '%Y-%m-%d') AS `单据日期`, 5112 DATE_FORMAT(d.djrq, '%Y-%m-%d') AS `单据日期`,
4872 d.F_Id AS `单据编号`, 5113 d.F_Id AS `单据编号`,
4873 d.djlx AS `单据类型`, 5114 d.djlx AS `单据类型`,
4874 IFNULL(NULLIF(TRIM(w.dwmc), ''), '无') AS `往来单位`, 5115 IFNULL(NULLIF(TRIM(w.dwmc), ''), '无') AS `往来单位`,
4875 - IFNULL(NULLIF(TRIM(u.F_RealName), ''), IFNULL(NULLIF(TRIM(d.jsr), ''), '无')) AS `经手人`, 5116 + IFNULL(NULLIF(TRIM(u.F_RealName), ''), '无') AS `经手人`,
4876 IFNULL(NULLIF(TRIM(ck.F_mdmc), ''), '无') AS `仓库名称`, 5117 IFNULL(NULLIF(TRIM(ck.F_mdmc), ''), '无') AS `仓库名称`,
4877 IFNULL(NULLIF(TRIM(mx.spmc), ''), '无') AS `商品名称`, 5118 IFNULL(NULLIF(TRIM(mx.spmc), ''), '无') AS `商品名称`,
4878 - CAST(mx.sl AS DECIMAL(18,4)) AS `数量`,  
4879 - mx.dj AS `入库单价`,  
4880 - mx.je AS `采购金额` 5119 + {PurSumSignedSlExpr} AS `数量`,
  5120 + {PurSumSignedDjExpr} AS `入库单价`,
  5121 + {PurSumSignedJeExpr} AS `采购金额`
4881 {PurchaseSummaryJoinFromSql} 5122 {PurchaseSummaryJoinFromSql}
4882 {whereSql} 5123 {whereSql}
4883 -ORDER BY d.djrq DESC, d.F_Id, mx.F_Id 5124 +ORDER BY {orderByDetail}
4884 LIMIT {offset}, {pageSize}"; 5125 LIMIT {offset}, {pageSize}";
4885 var list = await _db.Ado.GetDataTableAsync(listSql, paramList); 5126 var list = await _db.Ado.GetDataTableAsync(listSql, paramList);
4886 return new { list, total }; 5127 return new { list, total };
@@ -5744,11 +5985,13 @@ LIMIT {offset}, {pageSize}&quot;; @@ -5744,11 +5985,13 @@ LIMIT {offset}, {pageSize}&quot;;
5744 } 5985 }
5745 5986
5746 /// <summary> 5987 /// <summary>
5747 - /// 通用审核接口:自动根据单据的 djlx 匹配审核配置;编号以 TJD 开头时优先判断是否为 wt_xsckd 同价调拨单,否则走商品调价单审批流 5988 + /// 通用审核接口:自动根据单据的 djlx 匹配审核配置;编号以 TJD 开头时优先判断是否为 wt_xsckd 同价调拨单,否则走商品调价单审批流;wt_fysrd 其他收入单走费用收入单审批流
5748 /// </summary> 5989 /// </summary>
5749 [HttpPost("ApproveGeneric/{id}")] 5990 [HttpPost("ApproveGeneric/{id}")]
5750 public async Task<dynamic> ApproveGeneric(string id, [FromBody] WtApprovalRemarkInput input) 5991 public async Task<dynamic> ApproveGeneric(string id, [FromBody] WtApprovalRemarkInput input)
5751 { 5992 {
  5993 + if (!string.IsNullOrEmpty(id) && await _db.Queryable<WtFysrdEntity>().AnyAsync(x => x.Id == id))
  5994 + return await _fysrdWorkflowHelper.ApproveAsync(id, input?.remark);
5752 if (!string.IsNullOrEmpty(id) && id.StartsWith("TJD", StringComparison.Ordinal)) 5995 if (!string.IsNullOrEmpty(id) && id.StartsWith("TJD", StringComparison.Ordinal))
5753 { 5996 {
5754 var isSamePriceTransfer = await _db.Queryable<WtXsckdEntity>() 5997 var isSamePriceTransfer = await _db.Queryable<WtXsckdEntity>()
@@ -5768,6 +6011,8 @@ LIMIT {offset}, {pageSize}&quot;; @@ -5768,6 +6011,8 @@ LIMIT {offset}, {pageSize}&quot;;
5768 [HttpPost("RejectGeneric/{id}")] 6011 [HttpPost("RejectGeneric/{id}")]
5769 public async Task<dynamic> RejectGeneric(string id, [FromBody] WtApprovalRemarkInput input) 6012 public async Task<dynamic> RejectGeneric(string id, [FromBody] WtApprovalRemarkInput input)
5770 { 6013 {
  6014 + if (!string.IsNullOrEmpty(id) && await _db.Queryable<WtFysrdEntity>().AnyAsync(x => x.Id == id))
  6015 + return await _fysrdWorkflowHelper.RejectAsync(id, input?.remark);
5771 if (!string.IsNullOrEmpty(id) && id.StartsWith("TJD", StringComparison.Ordinal)) 6016 if (!string.IsNullOrEmpty(id) && id.StartsWith("TJD", StringComparison.Ordinal))
5772 { 6017 {
5773 var isSamePriceTransfer = await _db.Queryable<WtXsckdEntity>() 6018 var isSamePriceTransfer = await _db.Queryable<WtXsckdEntity>()
@@ -5942,6 +6187,9 @@ LIMIT {offset}, {pageSize}&quot;; @@ -5942,6 +6187,9 @@ LIMIT {offset}, {pageSize}&quot;;
5942 { 6187 {
5943 try 6188 try
5944 { 6189 {
  6190 + if (!string.IsNullOrEmpty(id) && await _db.Queryable<WtFysrdEntity>().AnyAsync(x => x.Id == id))
  6191 + return await _fysrdWorkflowHelper.ReverseAsync(id);
  6192 +
5945 var entity = await _db.Queryable<WtXsckdEntity>().Where(x => x.Id == id).FirstAsync(); 6193 var entity = await _db.Queryable<WtXsckdEntity>().Where(x => x.Id == id).FirstAsync();
5946 if (entity == null) 6194 if (entity == null)
5947 return new { success = false, message = "单据不存在" }; 6195 return new { success = false, message = "单据不存在" };
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtYskzjjsService.cs
@@ -14,6 +14,7 @@ using System.Collections.Generic; @@ -14,6 +14,7 @@ using System.Collections.Generic;
14 using System.Linq; 14 using System.Linq;
15 using System.Threading.Tasks; 15 using System.Threading.Tasks;
16 using NCC.Extend.Entitys; 16 using NCC.Extend.Entitys;
  17 +using NCC.Extend.Entitys.Dto;
17 using NCC.Extend.Entitys.Dto.WtYskzjjs; 18 using NCC.Extend.Entitys.Dto.WtYskzjjs;
18 using Yitter.IdGenerator; 19 using Yitter.IdGenerator;
19 using NCC.Common.Helper; 20 using NCC.Common.Helper;
@@ -40,6 +41,7 @@ namespace NCC.Extend.WtYskzjjs @@ -40,6 +41,7 @@ namespace NCC.Extend.WtYskzjjs
40 private readonly IUserManager _userManager; 41 private readonly IUserManager _userManager;
41 private readonly WtDjzyService _wtDjzyService; 42 private readonly WtDjzyService _wtDjzyService;
42 private readonly WtBillSummaryService _billSummaryService; 43 private readonly WtBillSummaryService _billSummaryService;
  44 + private static bool _yskAuditColsEnsured;
43 45
44 /// <summary> 46 /// <summary>
45 /// 初始化一个<see cref="WtYskzjjsService"/>类型的新实例 47 /// 初始化一个<see cref="WtYskzjjsService"/>类型的新实例
@@ -60,6 +62,39 @@ namespace NCC.Extend.WtYskzjjs @@ -60,6 +62,39 @@ namespace NCC.Extend.WtYskzjjs
60 } 62 }
61 63
62 /// <summary> 64 /// <summary>
  65 + /// 确保 wt_yskzjjs 具备审批相关列(避免线上旧库缺列导致 Unknown column)。
  66 + /// </summary>
  67 + private void EnsureYskzjjsAuditColumns()
  68 + {
  69 + if (_yskAuditColsEnsured) return;
  70 + lock (typeof(WtYskzjjsService))
  71 + {
  72 + if (_yskAuditColsEnsured) return;
  73 + try
  74 + {
  75 + if (!_db.DbMaintenance.IsAnyTable("wt_yskzjjs")) { _yskAuditColsEnsured = true; return; }
  76 + var cols = _db.DbMaintenance.GetColumnInfosByTableName("wt_yskzjjs");
  77 + var names = cols.Select(c => c.DbColumnName.ToLowerInvariant()).ToHashSet();
  78 + if (!names.Contains("djzt"))
  79 + _db.Ado.ExecuteCommand("ALTER TABLE `wt_yskzjjs` ADD COLUMN `djzt` varchar(32) NULL COMMENT '单据状态'");
  80 + if (!names.Contains("spbz"))
  81 + _db.Ado.ExecuteCommand("ALTER TABLE `wt_yskzjjs` ADD COLUMN `spbz` text NULL COMMENT '审批备注'");
  82 + if (!names.Contains("shr"))
  83 + _db.Ado.ExecuteCommand("ALTER TABLE `wt_yskzjjs` ADD COLUMN `shr` varchar(64) NULL COMMENT '审核人'");
  84 + if (!names.Contains("shr1"))
  85 + _db.Ado.ExecuteCommand("ALTER TABLE `wt_yskzjjs` ADD COLUMN `shr1` varchar(500) NULL COMMENT '一级审核人'");
  86 + if (!names.Contains("shr2"))
  87 + _db.Ado.ExecuteCommand("ALTER TABLE `wt_yskzjjs` ADD COLUMN `shr2` varchar(500) NULL COMMENT '二级审核人'");
  88 + }
  89 + catch (Exception ex)
  90 + {
  91 + Console.WriteLine($"EnsureYskzjjsAuditColumns: {ex.Message}");
  92 + }
  93 + _yskAuditColsEnsured = true;
  94 + }
  95 + }
  96 +
  97 + /// <summary>
63 /// 获取应收款增加减少 98 /// 获取应收款增加减少
64 /// </summary> 99 /// </summary>
65 /// <param name="id">参数</param> 100 /// <param name="id">参数</param>
@@ -67,12 +102,14 @@ namespace NCC.Extend.WtYskzjjs @@ -67,12 +102,14 @@ namespace NCC.Extend.WtYskzjjs
67 [HttpGet("{id}")] 102 [HttpGet("{id}")]
68 public async Task<dynamic> GetInfo(string id) 103 public async Task<dynamic> GetInfo(string id)
69 { 104 {
  105 + EnsureYskzjjsAuditColumns();
70 var entity = await _db.Queryable<WtYskzjjsEntity>().FirstAsync(p => p.Id == id); 106 var entity = await _db.Queryable<WtYskzjjsEntity>().FirstAsync(p => p.Id == id);
71 var output = entity.Adapt<WtYskzjjsInfoOutput>(); 107 var output = entity.Adapt<WtYskzjjsInfoOutput>();
72 108
73 var wtYskzjjsMxList = await _db.Queryable<WtYskzjjsMxEntity>().Where(w => w.Djbh == entity.Id).ToListAsync(); 109 var wtYskzjjsMxList = await _db.Queryable<WtYskzjjsMxEntity>().Where(w => w.Djbh == entity.Id).ToListAsync();
74 output.wtYskzjjsMxList = wtYskzjjsMxList.Adapt<List<WtYskzjjsMxInfoOutput>>(); 110 output.wtYskzjjsMxList = wtYskzjjsMxList.Adapt<List<WtYskzjjsMxInfoOutput>>();
75 output.billZy = await _billSummaryService.ComputeWtYskzjjsZyByBillIdAsync(entity.Id); 111 output.billZy = await _billSummaryService.ComputeWtYskzjjsZyByBillIdAsync(entity.Id);
  112 + output.wldwmc = await ResolveWldwMcAsync(output.wldw);
76 return output; 113 return output;
77 } 114 }
78 115
@@ -84,6 +121,7 @@ namespace NCC.Extend.WtYskzjjs @@ -84,6 +121,7 @@ namespace NCC.Extend.WtYskzjjs
84 [HttpGet("")] 121 [HttpGet("")]
85 public async Task<dynamic> GetList([FromQuery] WtYskzjjsListQueryInput input) 122 public async Task<dynamic> GetList([FromQuery] WtYskzjjsListQueryInput input)
86 { 123 {
  124 + EnsureYskzjjsAuditColumns();
87 var sidx = input.sidx == null ? "id" : input.sidx; 125 var sidx = input.sidx == null ? "id" : input.sidx;
88 List<string> queryDjrq = input.djrq != null ? input.djrq.Split(',').ToObeject<List<string>>() : null; 126 List<string> queryDjrq = input.djrq != null ? input.djrq.Split(',').ToObeject<List<string>>() : null;
89 DateTime? startDjrq = queryDjrq != null ? Ext.GetDateTime(queryDjrq.First()) : null; 127 DateTime? startDjrq = queryDjrq != null ? Ext.GetDateTime(queryDjrq.First()) : null;
@@ -113,6 +151,10 @@ namespace NCC.Extend.WtYskzjjs @@ -113,6 +151,10 @@ namespace NCC.Extend.WtYskzjjs
113 djrq = d.Djrq, 151 djrq = d.Djrq,
114 jsr = d.Jsr, 152 jsr = d.Jsr,
115 djzt = d.Djzt, 153 djzt = d.Djzt,
  154 + spbz = d.Spbz,
  155 + shr = d.Shr,
  156 + shr1 = d.Shr1,
  157 + shr2 = d.Shr2,
116 dyddh = d.Dyddh, 158 dyddh = d.Dyddh,
117 hysjh = d.Hysjh, 159 hysjh = d.Hysjh,
118 fkzh = d.Fkzh, 160 fkzh = d.Fkzh,
@@ -132,6 +174,10 @@ namespace NCC.Extend.WtYskzjjs @@ -132,6 +174,10 @@ namespace NCC.Extend.WtYskzjjs
132 djrq = it.djrq, 174 djrq = it.djrq,
133 jsr = it.jsr, 175 jsr = it.jsr,
134 djzt = it.djzt, 176 djzt = it.djzt,
  177 + spbz = it.spbz,
  178 + shr = it.shr,
  179 + shr1 = it.shr1,
  180 + shr2 = it.shr2,
135 dyddh = it.dyddh, 181 dyddh = it.dyddh,
136 hysjh = it.hysjh, 182 hysjh = it.hysjh,
137 fkzh = it.fkzh, 183 fkzh = it.fkzh,
@@ -158,9 +204,12 @@ namespace NCC.Extend.WtYskzjjs @@ -158,9 +204,12 @@ namespace NCC.Extend.WtYskzjjs
158 [HttpPost("")] 204 [HttpPost("")]
159 public async Task Create([FromBody] WtYskzjjsCrInput input) 205 public async Task Create([FromBody] WtYskzjjsCrInput input)
160 { 206 {
  207 + EnsureYskzjjsAuditColumns();
161 var userInfo = await _userManager.GetUserInfo(); 208 var userInfo = await _userManager.GetUserInfo();
162 NormalizeDjlxForQt(input); 209 NormalizeDjlxForQt(input);
163 NormalizeDjlxForTransfer(input); 210 NormalizeDjlxForTransfer(input);
  211 + SyncMxKhFromWldw(input);
  212 + ThrowIfInvalidForSubmit(input, input?.djzt);
164 var entity = input.Adapt<WtYskzjjsEntity>(); 213 var entity = input.Adapt<WtYskzjjsEntity>();
165 entity.Id = await BuildCreateBillId(input); 214 entity.Id = await BuildCreateBillId(input);
166 try 215 try
@@ -264,6 +313,10 @@ namespace NCC.Extend.WtYskzjjs @@ -264,6 +313,10 @@ namespace NCC.Extend.WtYskzjjs
264 djrq = d.Djrq, 313 djrq = d.Djrq,
265 jsr = d.Jsr, 314 jsr = d.Jsr,
266 djzt = d.Djzt, 315 djzt = d.Djzt,
  316 + spbz = d.Spbz,
  317 + shr = d.Shr,
  318 + shr1 = d.Shr1,
  319 + shr2 = d.Shr2,
267 dyddh = d.Dyddh, 320 dyddh = d.Dyddh,
268 hysjh = d.Hysjh, 321 hysjh = d.Hysjh,
269 fkzh = d.Fkzh, 322 fkzh = d.Fkzh,
@@ -283,6 +336,10 @@ namespace NCC.Extend.WtYskzjjs @@ -283,6 +336,10 @@ namespace NCC.Extend.WtYskzjjs
283 djrq = it.djrq, 336 djrq = it.djrq,
284 jsr = it.jsr, 337 jsr = it.jsr,
285 djzt = it.djzt, 338 djzt = it.djzt,
  339 + spbz = it.spbz,
  340 + shr = it.shr,
  341 + shr1 = it.shr1,
  342 + shr2 = it.shr2,
286 dyddh = it.dyddh, 343 dyddh = it.dyddh,
287 hysjh = it.hysjh, 344 hysjh = it.hysjh,
288 fkzh = it.fkzh, 345 fkzh = it.fkzh,
@@ -389,8 +446,11 @@ namespace NCC.Extend.WtYskzjjs @@ -389,8 +446,11 @@ namespace NCC.Extend.WtYskzjjs
389 [HttpPut("{id}")] 446 [HttpPut("{id}")]
390 public async Task Update(string id, [FromBody] WtYskzjjsUpInput input) 447 public async Task Update(string id, [FromBody] WtYskzjjsUpInput input)
391 { 448 {
  449 + EnsureYskzjjsAuditColumns();
392 NormalizeDjlxForQt(input); 450 NormalizeDjlxForQt(input);
393 NormalizeDjlxForTransfer(input); 451 NormalizeDjlxForTransfer(input);
  452 + SyncMxKhFromWldw(input);
  453 + ThrowIfInvalidForSubmit(input, input?.djzt);
394 var entity = input.Adapt<WtYskzjjsEntity>(); 454 var entity = input.Adapt<WtYskzjjsEntity>();
395 try 455 try
396 { 456 {
@@ -461,7 +521,7 @@ namespace NCC.Extend.WtYskzjjs @@ -461,7 +521,7 @@ namespace NCC.Extend.WtYskzjjs
461 } 521 }
462 522
463 /// <summary> 523 /// <summary>
464 - /// 兼容 qt 页面对 djlx 的历史传值:当明细为“其他收入单”时统一落库为“其他应收”。 524 + /// 兼容 qt 页面对 djlx 的历史传值:当明细为“其他收入单”时统一落库为“其他应收”。
465 /// 仅修正 qt 场景,不影响其它单据类型。 525 /// 仅修正 qt 场景,不影响其它单据类型。
466 /// </summary> 526 /// </summary>
467 /// <param name="input">新增/更新入参</param> 527 /// <param name="input">新增/更新入参</param>
@@ -478,9 +538,10 @@ namespace NCC.Extend.WtYskzjjs @@ -478,9 +538,10 @@ namespace NCC.Extend.WtYskzjjs
478 538
479 if (string.IsNullOrWhiteSpace(input.djlx) || 539 if (string.IsNullOrWhiteSpace(input.djlx) ||
480 input.djlx.Trim().Equals("应收款减少", StringComparison.Ordinal) || 540 input.djlx.Trim().Equals("应收款减少", StringComparison.Ordinal) ||
481 - input.djlx.Trim().Equals("其他收入单", StringComparison.Ordinal)) 541 + input.djlx.Trim().Equals("其他收入单", StringComparison.Ordinal) ||
  542 + input.djlx.Trim().Equals("其他应收款", StringComparison.Ordinal))
482 { 543 {
483 - input.djlx = "其他应收"; 544 + input.djlx = "其他应收";
484 } 545 }
485 } 546 }
486 547
@@ -598,5 +659,326 @@ namespace NCC.Extend.WtYskzjjs @@ -598,5 +659,326 @@ namespace NCC.Extend.WtYskzjjs
598 659
599 await _billSummaryService.EnrichWtYskzjjsListBillZyAsync(rowList); 660 await _billSummaryService.EnrichWtYskzjjsListBillZyAsync(rowList);
600 } 661 }
  662 +
  663 + /// <summary>
  664 + /// 主表往来单位写入明细客户字段,便于列表按 kh 筛选与详情列展示。
  665 + /// </summary>
  666 + private static void SyncMxKhFromWldw(WtYskzjjsCrInput input)
  667 + {
  668 + if (input == null || string.IsNullOrWhiteSpace(input.wldw) || input.wtYskzjjsMxList == null)
  669 + return;
  670 + var w = input.wldw.Trim();
  671 + foreach (var mx in input.wtYskzjjsMxList.Where(m => m != null))
  672 + {
  673 + if (string.IsNullOrWhiteSpace(mx.kh))
  674 + mx.kh = w;
  675 + }
  676 + }
  677 +
  678 + private async Task<string> ResolveWldwMcAsync(string wldwId)
  679 + {
  680 + if (string.IsNullOrWhiteSpace(wldwId))
  681 + return "无";
  682 + var dwmc = (await _db.Queryable<WtWldwEntity>().Where(x => x.Id == wldwId).Select(x => x.Dwmc).ToListAsync()).FirstOrDefault();
  683 + if (!string.IsNullOrWhiteSpace(dwmc))
  684 + return dwmc;
  685 + var xm = (await _db.Queryable<WtHyEntity>().Where(x => x.Id == wldwId).Select(x => x.Xm).ToListAsync()).FirstOrDefault();
  686 + if (!string.IsNullOrWhiteSpace(xm))
  687 + return xm;
  688 + var gysmc = (await _db.Queryable<WtGysEntity>().Where(x => x.Id == wldwId).Select(x => x.Gysmc).ToListAsync()).FirstOrDefault();
  689 + if (!string.IsNullOrWhiteSpace(gysmc))
  690 + return gysmc;
  691 + return "无";
  692 + }
  693 +
  694 + private static void AppendApprovalSpbzLineYsk(WtYskzjjsEntity entity, string actionTag, string remark)
  695 + {
  696 + var r = (remark ?? "").Trim();
  697 + if (string.IsNullOrEmpty(r)) return;
  698 + var line = string.IsNullOrEmpty(actionTag) ? r : $"[{actionTag}] {r}";
  699 + var existing = entity.Spbz?.TrimEnd() ?? "";
  700 + entity.Spbz = string.IsNullOrWhiteSpace(existing) ? line : existing + "\n" + line;
  701 + }
  702 +
  703 + private static bool IsUserInAuditConfigYsk(string configValue, string userId, string userAccount = null)
  704 + {
  705 + if (string.IsNullOrWhiteSpace(configValue))
  706 + return false;
  707 + if (string.IsNullOrWhiteSpace(userId) && string.IsNullOrWhiteSpace(userAccount))
  708 + return false;
  709 +
  710 + var users = configValue
  711 + .Split(new[] { ',', ',', ';', ';', '|', ' ' }, StringSplitOptions.RemoveEmptyEntries)
  712 + .Select(x => x.Trim())
  713 + .Where(x => !string.IsNullOrEmpty(x))
  714 + .ToList();
  715 +
  716 + return users.Any(x =>
  717 + (!string.IsNullOrWhiteSpace(userId) && string.Equals(x, userId.Trim(), StringComparison.OrdinalIgnoreCase))
  718 + || (!string.IsNullOrWhiteSpace(userAccount) && string.Equals(x, userAccount.Trim(), StringComparison.OrdinalIgnoreCase)));
  719 + }
  720 +
  721 + /// <summary>
  722 + /// 提交审核:草稿 / 空 / 审核不通过 → 待审核
  723 + /// </summary>
  724 + [HttpPost("Actions/SubmitForAudit/{id}")]
  725 + public async Task<dynamic> SubmitForAudit(string id)
  726 + {
  727 + EnsureYskzjjsAuditColumns();
  728 + var entity = await _db.Queryable<WtYskzjjsEntity>().FirstAsync(p => p.Id == id);
  729 + if (entity == null)
  730 + return new { success = false, message = "单据不存在" };
  731 + var z = entity.Djzt?.Trim() ?? "";
  732 + if (z != "草稿" && z != "审核不通过" && !string.IsNullOrEmpty(z))
  733 + return new { success = false, message = $"当前状态「{entity.Djzt}」不可提交审核" };
  734 +
  735 + // 提交审核前做必填校验(备注除外)
  736 + var mxList = await _db.Queryable<WtYskzjjsMxEntity>().Where(x => x.Djbh == entity.Id).ToListAsync();
  737 + var inputLike = new WtYskzjjsCrInput
  738 + {
  739 + id = entity.Id,
  740 + djlx = entity.Djlx,
  741 + djrq = entity.Djrq,
  742 + jsr = entity.Jsr,
  743 + wldw = entity.Wldw,
  744 + djzt = "待审核",
  745 + fkzh = entity.Fkzh,
  746 + fkje = entity.Fkje,
  747 + skzh = entity.Skzh,
  748 + skje = entity.Skje,
  749 + zy = entity.Zy,
  750 + wtYskzjjsMxList = mxList?.Select(m => new WtYskzjjsMxCrInput
  751 + {
  752 + srxmmc = m.Srxmmc,
  753 + je = m.Je,
  754 + bz = m.Bz,
  755 + mxlx = m.Mxlx,
  756 + kh = m.Kh,
  757 + zhbh = m.Zhbh
  758 + }).ToList()
  759 + };
  760 + try
  761 + {
  762 + ThrowIfInvalidForSubmit(inputLike, "待审核");
  763 + }
  764 + catch (Exception ex)
  765 + {
  766 + return new { success = false, message = ex.Message };
  767 + }
  768 +
  769 + entity.Djzt = "待审核";
  770 + await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt }).ExecuteCommandAsync();
  771 + return new { success = true, message = "已提交审核" };
  772 + }
  773 +
  774 + /// <summary>
  775 + /// 除备注外必填:仅在提交审核/非草稿状态强制校验;草稿可放宽。
  776 + /// </summary>
  777 + private static void ThrowIfInvalidForSubmit(WtYskzjjsCrInput input, string djzt)
  778 + {
  779 + if (input == null) throw NCCException.Oh("参数不能为空");
  780 + var z = (djzt ?? input.djzt ?? "").Trim();
  781 + if (string.IsNullOrEmpty(z) || z == "草稿") return;
  782 +
  783 + if (input.djrq == null) throw NCCException.Oh("请选择单据日期");
  784 + if (string.IsNullOrWhiteSpace(input.jsr)) throw NCCException.Oh("请选择经手人");
  785 + if (string.IsNullOrWhiteSpace(input.wldw)) throw NCCException.Oh("请选择往来单位");
  786 + if (string.IsNullOrWhiteSpace(input.skzh)) throw NCCException.Oh("请选择收款账户");
  787 + if (input.skje <= 0) throw NCCException.Oh("请填写明细金额,收款金额须大于 0");
  788 + if (input.wtYskzjjsMxList == null || input.wtYskzjjsMxList.Count == 0) throw NCCException.Oh("请至少添加一条明细");
  789 +
  790 + for (var i = 0; i < input.wtYskzjjsMxList.Count; i++)
  791 + {
  792 + var row = input.wtYskzjjsMxList[i];
  793 + if (row == null) throw NCCException.Oh($"第 {i + 1} 行明细不能为空");
  794 + if (string.IsNullOrWhiteSpace(row.srxmmc)) throw NCCException.Oh($"第 {i + 1} 行收入项目为必填");
  795 + if (row.je <= 0) throw NCCException.Oh($"第 {i + 1} 行原币金额为必填且须大于 0");
  796 + }
  797 + }
  798 +
  799 + /// <summary>
  800 + /// 撤回:待审核或一级已审 → 草稿
  801 + /// </summary>
  802 + [HttpPost("Actions/WithdrawAudit/{id}")]
  803 + public async Task<dynamic> WithdrawAudit(string id)
  804 + {
  805 + EnsureYskzjjsAuditColumns();
  806 + var entity = await _db.Queryable<WtYskzjjsEntity>().FirstAsync(p => p.Id == id);
  807 + if (entity == null)
  808 + return new { success = false, message = "单据不存在" };
  809 + var z = entity.Djzt?.Trim() ?? "";
  810 + if (z != "待审核" && z != "一级已审" && z != "待二级" && z != "一级已审/待二级")
  811 + return new { success = false, message = $"当前状态「{entity.Djzt}」不可撤回" };
  812 +
  813 + entity.Djzt = "草稿";
  814 + entity.Shr1 = null;
  815 + entity.Shr2 = null;
  816 + await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Shr1, x.Shr2 }).ExecuteCommandAsync();
  817 + return new { success = true, message = "已撤回为草稿" };
  818 + }
  819 +
  820 + /// <summary>
  821 + /// 审核通过(按 <c>wt_shrysz.djmc</c> 与主表 <c>djlx</c> 匹配,支持一级/两级)
  822 + /// </summary>
  823 + [HttpPost("Actions/Approve/{id}")]
  824 + public async Task<dynamic> Approve(string id, [FromBody] WtApprovalRemarkInput input = null)
  825 + {
  826 + EnsureYskzjjsAuditColumns();
  827 + return await ApproveYskzjjsDocumentAsync(id, input?.remark);
  828 + }
  829 +
  830 + /// <summary>
  831 + /// 审核不通过(审批备注写入 <c>spbz</c>)
  832 + /// </summary>
  833 + [HttpPost("Actions/Reject/{id}")]
  834 + public async Task<dynamic> Reject(string id, [FromBody] WtApprovalRemarkInput input = null)
  835 + {
  836 + EnsureYskzjjsAuditColumns();
  837 + return await RejectYskzjjsDocumentAsync(id, input?.remark);
  838 + }
  839 +
  840 + private async Task<dynamic> ApproveYskzjjsDocumentAsync(string id, string approvalRemark)
  841 + {
  842 + try
  843 + {
  844 + _db.BeginTran();
  845 + var entity = await _db.Queryable<WtYskzjjsEntity>().Where(x => x.Id == id).FirstAsync();
  846 + if (entity == null)
  847 + {
  848 + _db.RollbackTran();
  849 + return new { success = false, message = "单据不存在" };
  850 + }
  851 +
  852 + if (entity.Djzt == "已审核")
  853 + {
  854 + _db.RollbackTran();
  855 + return new { success = false, message = "该单据已经审核,无需重复审核" };
  856 + }
  857 +
  858 + var userInfo = await _userManager.GetUserInfo();
  859 + var userId = userInfo?.userId ?? "";
  860 + var userAccount = userInfo?.userAccount?.Trim();
  861 + if (string.IsNullOrEmpty(userAccount))
  862 + userAccount = _userManager.Account?.Trim();
  863 +
  864 + var djmc = entity.Djlx?.Trim() ?? "";
  865 + var approvalConfig = await _db.Queryable<WtShryszEntity>()
  866 + .Where(c => c.Djmc == djmc)
  867 + .FirstAsync();
  868 +
  869 + var configShr1 = approvalConfig?.Shr1;
  870 + var configShr2 = approvalConfig?.Shr2;
  871 + var hasTwoLevel = !string.IsNullOrEmpty(configShr1) && !string.IsNullOrEmpty(configShr2);
  872 +
  873 + if (entity.Djzt == "待审核" || string.IsNullOrEmpty(entity.Djzt))
  874 + {
  875 + if (hasTwoLevel)
  876 + {
  877 + entity.Djzt = "一级已审";
  878 + entity.Shr1 = userId;
  879 + AppendApprovalSpbzLineYsk(entity, "一级通过", approvalRemark);
  880 + await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Shr1, x.Spbz }).ExecuteCommandAsync();
  881 + _db.CommitTran();
  882 + return new { success = true, message = "一级审核通过,等待二级审核" };
  883 + }
  884 +
  885 + entity.Djzt = "已审核";
  886 + entity.Shr = userId;
  887 + entity.Shr1 = userId;
  888 + AppendApprovalSpbzLineYsk(entity, "审核通过", approvalRemark);
  889 + await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Shr, x.Shr1, x.Spbz }).ExecuteCommandAsync();
  890 + _db.CommitTran();
  891 + return new { success = true, message = "审核通过" };
  892 + }
  893 +
  894 + if (entity.Djzt == "一级已审" || entity.Djzt == "一级已审/待二级" || entity.Djzt == "待二级")
  895 + {
  896 + if (!hasTwoLevel)
  897 + {
  898 + _db.RollbackTran();
  899 + return new { success = false, message = "未配置两级审核,当前状态不允许二级操作" };
  900 + }
  901 + if (string.IsNullOrWhiteSpace(configShr2))
  902 + {
  903 + _db.RollbackTran();
  904 + return new { success = false, message = "未配置二级审核人员" };
  905 + }
  906 + if (!IsUserInAuditConfigYsk(configShr2, userId, userAccount))
  907 + {
  908 + _db.RollbackTran();
  909 + return new { success = false, message = "当前用户不在二级审核人员范围内" };
  910 + }
  911 +
  912 + entity.Djzt = "已审核";
  913 + entity.Shr2 = userId;
  914 + entity.Shr = userId;
  915 + AppendApprovalSpbzLineYsk(entity, "二级通过", approvalRemark);
  916 + await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Shr, x.Shr2, x.Spbz }).ExecuteCommandAsync();
  917 + _db.CommitTran();
  918 + return new { success = true, message = "二级审核通过,审核完成" };
  919 + }
  920 +
  921 + _db.RollbackTran();
  922 + return new { success = false, message = $"当前单据状态「{entity.Djzt}」不允许审核" };
  923 + }
  924 + catch (Exception ex)
  925 + {
  926 + _db.RollbackTran();
  927 + return new { success = false, message = $"审核失败: {ex.Message}" };
  928 + }
  929 + }
  930 +
  931 + private async Task<dynamic> RejectYskzjjsDocumentAsync(string id, string approvalRemark)
  932 + {
  933 + try
  934 + {
  935 + _db.BeginTran();
  936 + var entity = await _db.Queryable<WtYskzjjsEntity>().Where(x => x.Id == id).FirstAsync();
  937 + if (entity == null)
  938 + {
  939 + _db.RollbackTran();
  940 + return new { success = false, message = "单据不存在" };
  941 + }
  942 +
  943 + if (entity.Djzt == "已审核")
  944 + {
  945 + _db.RollbackTran();
  946 + return new { success = false, message = "该单据已审核通过" };
  947 + }
  948 +
  949 + if (entity.Djzt == "审核不通过")
  950 + {
  951 + _db.RollbackTran();
  952 + return new { success = false, message = "该单据已是审核不通过状态" };
  953 + }
  954 +
  955 + if (entity.Djzt == "草稿")
  956 + {
  957 + _db.RollbackTran();
  958 + return new { success = false, message = "草稿状态请直接修改或删除单据" };
  959 + }
  960 +
  961 + var djztTrim = entity.Djzt?.Trim() ?? "";
  962 + var level1 = djztTrim == "待审核" || string.IsNullOrEmpty(djztTrim);
  963 + var level2 = entity.Djzt == "一级已审" || entity.Djzt == "待二级" || entity.Djzt == "一级已审/待二级";
  964 +
  965 + if (!level1 && !level2)
  966 + {
  967 + _db.RollbackTran();
  968 + return new { success = false, message = $"当前单据状态「{entity.Djzt}」不允许审核不通过" };
  969 + }
  970 +
  971 + entity.Djzt = "审核不通过";
  972 + AppendApprovalSpbzLineYsk(entity, "审核不通过", approvalRemark);
  973 + await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Spbz }).ExecuteCommandAsync();
  974 + _db.CommitTran();
  975 + return new { success = true, message = "已标记审核不通过" };
  976 + }
  977 + catch (Exception ex)
  978 + {
  979 + _db.RollbackTran();
  980 + return new { success = false, message = $"操作失败: {ex.Message}" };
  981 + }
  982 + }
601 } 983 }
602 } 984 }
Antis.Erp.Plat/netcore/src/Modularity/OAuth/NCC.OAuth/Service/OAuthService.cs
@@ -148,7 +148,11 @@ namespace NCC.OAuth.Service @@ -148,7 +148,11 @@ namespace NCC.OAuth.Service
148 bool isMoble = false; 148 bool isMoble = false;
149 if (input.account == user.MobilePhone) isMoble = true; 149 if (input.account == user.MobilePhone) isMoble = true;
150 //获取加密后的密码 150 //获取加密后的密码
151 - var encryptPasswod = MD5Encryption.Encrypt(input.password + user.Secretkey); 151 + var pwd = (input.password ?? "").Trim();
  152 + // 兼容:前端既可能传 MD5(明文),也可能直接传明文(如收银台)。
  153 + // 系统入库口径为:MD5(MD5(明文) + Secretkey)
  154 + var md5Once = NormalizePasswordToMd5Once(pwd);
  155 + var encryptPasswod = MD5Encryption.Encrypt(md5Once + user.Secretkey);
152 156
153 var userAnyPwd = await _userService.GetInfoByLogin(input.account, encryptPasswod, isMoble); 157 var userAnyPwd = await _userService.GetInfoByLogin(input.account, encryptPasswod, isMoble);
154 _ = userAnyPwd ?? throw NCCException.Oh(ErrorCode.D1000); 158 _ = userAnyPwd ?? throw NCCException.Oh(ErrorCode.D1000);
@@ -597,7 +601,9 @@ namespace NCC.OAuth.Service @@ -597,7 +601,9 @@ namespace NCC.OAuth.Service
597 var secretkey = (await _userService.GetInfoByAccount(input.account)).Secretkey; 601 var secretkey = (await _userService.GetInfoByAccount(input.account)).Secretkey;
598 602
599 //获取加密后的密码 603 //获取加密后的密码
600 - var encryptPasswod = MD5Encryption.Encrypt(input.password + secretkey); 604 + var pwd = (input.password ?? "").Trim();
  605 + var md5Once = NormalizePasswordToMd5Once(pwd);
  606 + var encryptPasswod = MD5Encryption.Encrypt(md5Once + secretkey);
601 607
602 bool isMoble = false; 608 bool isMoble = false;
603 if (input.account == users.MobilePhone) isMoble = true; 609 if (input.account == users.MobilePhone) isMoble = true;
@@ -606,6 +612,25 @@ namespace NCC.OAuth.Service @@ -606,6 +612,25 @@ namespace NCC.OAuth.Service
606 _ = user ?? throw NCCException.Oh(ErrorCode.D1000); 612 _ = user ?? throw NCCException.Oh(ErrorCode.D1000);
607 } 613 }
608 614
  615 + private static bool LooksLikeMd5(string s)
  616 + {
  617 + if (string.IsNullOrEmpty(s) || s.Length != 32) return false;
  618 + foreach (var ch in s)
  619 + {
  620 + var isHex = (ch >= '0' && ch <= '9')
  621 + || (ch >= 'a' && ch <= 'f')
  622 + || (ch >= 'A' && ch <= 'F');
  623 + if (!isHex) return false;
  624 + }
  625 + return true;
  626 + }
  627 +
  628 + private static string NormalizePasswordToMd5Once(string passwordOrMd5)
  629 + {
  630 + var p = (passwordOrMd5 ?? "").Trim();
  631 + return LooksLikeMd5(p) ? p : MD5Encryption.Encrypt(p);
  632 + }
  633 +
609 /// <summary> 634 /// <summary>
610 /// 获取当前登录用户信息 635 /// 获取当前登录用户信息
611 /// </summary> 636 /// </summary>
Antis.Erp.Plat/netcore/src/Modularity/System/NCC.System.Entitys/Dto/Permission/User/UserCrInput.cs
@@ -133,8 +133,16 @@ namespace NCC.System.Entitys.Dto.Permission.User @@ -133,8 +133,16 @@ namespace NCC.System.Entitys.Dto.Permission.User
133 /// 排序 133 /// 排序
134 /// </summary> 134 /// </summary>
135 public long? sortCode { get; set; } 135 public long? sortCode { get; set; }
136 -  
137 - 136 +
  137 + /// <summary>
  138 + /// 初始密码(明文,可选)。为空或未传时仍按系统默认密码规则生成(与历史行为一致)。
  139 + /// </summary>
  140 + /// <remarks>
  141 + /// 非空时按与登录校验一致的口径入库:MD5(MD5(明文) + 用户 Secretkey)。
  142 + /// (登录请求中的 password 通常为 MD5(明文),服务端再拼接 Secretkey 后做最终 MD5)
  143 + /// </remarks>
  144 + public string password { get; set; }
  145 +
138 /// <summary> 146 /// <summary>
139 /// 门店信息 147 /// 门店信息
140 /// </summary> 148 /// </summary>
Antis.Erp.Plat/netcore/src/Modularity/System/NCC.System/Service/Permission/UsersService.cs
@@ -262,10 +262,31 @@ namespace NCC.System.Service.Permission @@ -262,10 +262,31 @@ namespace NCC.System.Service.Permission
262 } 262 }
263 263
264 /// <summary> 264 /// <summary>
265 - /// 新建 265 + /// 新建用户
266 /// </summary> 266 /// </summary>
267 - /// <param name="input">参数</param>  
268 - /// <returns></returns> 267 + /// <remarks>
  268 + /// 可选传入 <c>password</c>(明文)。非空时按与「重置密码」相同的规则写入库,便于新建用户后直接以明文登录;
  269 + /// 未传或为空时仍使用系统默认密码哈希(兼容旧前端)。
  270 + ///
  271 + /// 示例请求:
  272 + /// ```json
  273 + /// {
  274 + /// "account": "zhangsan",
  275 + /// "password": "PlainTextPwd",
  276 + /// "realName": "张三",
  277 + /// "organizeId": "orgId",
  278 + /// "headIcon": "/api/file/Image/userAvatar/001.png"
  279 + /// }
  280 + /// ```
  281 + ///
  282 + /// 参数说明:
  283 + /// - password: 可选,非空时为初始明文密码
  284 + /// </remarks>
  285 + /// <param name="input">创建用户参数</param>
  286 + /// <returns>无返回体,成功即完成创建</returns>
  287 + /// <response code="200">创建成功</response>
  288 + /// <response code="400">参数或业务校验失败</response>
  289 + /// <response code="500">服务器内部错误</response>
269 [HttpPost("")] 290 [HttpPost("")]
270 public async Task Create([FromBody] UserCrInput input) 291 public async Task Create([FromBody] UserCrInput input)
271 { 292 {
@@ -294,7 +315,12 @@ namespace NCC.System.Service.Permission @@ -294,7 +315,12 @@ namespace NCC.System.Service.Permission
294 entity.Birthday = input.birthday.IsNullOrEmpty() ? DateTime.Now : Ext.GetDateTime(input.birthday.ToString()); 315 entity.Birthday = input.birthday.IsNullOrEmpty() ? DateTime.Now : Ext.GetDateTime(input.birthday.ToString());
295 entity.QuickQuery = PinyinUtil.PinyinString(input.realName); 316 entity.QuickQuery = PinyinUtil.PinyinString(input.realName);
296 entity.Secretkey = Guid.NewGuid().ToString(); 317 entity.Secretkey = Guid.NewGuid().ToString();
297 - entity.Password = MD5Encryption.Encrypt(MD5Encryption.Encrypt(CommonConst.DEFAULT_PASSWORD) + entity.Secretkey); 318 + if (string.IsNullOrWhiteSpace(input.password))
  319 + {
  320 + throw NCCException.Oh("请输入密码");
  321 + }
  322 + entity.Password = HashPasswordForStorage(input.password.Trim(), entity.Secretkey);
  323 +
298 entity.Mdxx = input.mdxx; 324 entity.Mdxx = input.mdxx;
299 var headIcon = input.headIcon.Split('/').ToList().Last(); 325 var headIcon = input.headIcon.Split('/').ToList().Last();
300 if (string.IsNullOrEmpty(headIcon)) 326 if (string.IsNullOrEmpty(headIcon))
@@ -680,7 +706,7 @@ namespace NCC.System.Service.Permission @@ -680,7 +706,7 @@ namespace NCC.System.Service.Permission
680 706
681 _ = entity ?? throw NCCException.Oh(ErrorCode.D1002); 707 _ = entity ?? throw NCCException.Oh(ErrorCode.D1002);
682 708
683 - var password = MD5Encryption.Encrypt(input.userPassword + entity.Secretkey); 709 + var password = HashPasswordForStorage(input.userPassword, entity.Secretkey);
684 710
685 var isOk = await _userRepository.Context.Updateable<UserEntity>().SetColumns(it => new UserEntity() 711 var isOk = await _userRepository.Context.Updateable<UserEntity>().SetColumns(it => new UserEntity()
686 { 712 {
@@ -798,7 +824,9 @@ namespace NCC.System.Service.Permission @@ -798,7 +824,9 @@ namespace NCC.System.Service.Permission
798 [NonAction] 824 [NonAction]
799 public async Task<UserEntity> GetInfoByAccount(string account) 825 public async Task<UserEntity> GetInfoByAccount(string account)
800 { 826 {
801 - return await _userRepository.FirstOrDefaultAsync(u => u.Account == account || u.MobilePhone == account && u.DeleteMark == null); 827 + // 注意:必须同时过滤 DeleteMark,否则同账号存在“已删除历史记录”时会取错 Secretkey,导致登录永远校验失败
  828 + return await _userRepository.FirstOrDefaultAsync(u =>
  829 + (u.Account == account || u.MobilePhone == account) && u.DeleteMark == null);
802 } 830 }
803 831
804 /// <summary> 832 /// <summary>
@@ -930,6 +958,38 @@ namespace NCC.System.Service.Permission @@ -930,6 +958,38 @@ namespace NCC.System.Service.Permission
930 var ids = PositionIds.Split(","); 958 var ids = PositionIds.Split(",");
931 return await _positionRepository.Entities.In(it => it.Id, ids).Select(it => new { id = it.Id, name = it.FullName }).MergeTable().Select<PositionInfo>().ToListAsync(); 959 return await _positionRepository.Entities.In(it => it.Id, ids).Select(it => new { id = it.Id, name = it.FullName }).MergeTable().Select<PositionInfo>().ToListAsync();
932 } 960 }
  961 +
  962 + /// <summary>
  963 + /// 用户密码入库哈希(与重置密码、个人修改密码、OAuth 登录一致)。
  964 + /// </summary>
  965 + /// <param name="plainPassword">明文密码</param>
  966 + /// <param name="secretkey">用户秘钥</param>
  967 + /// <returns>可写入 <see cref="UserEntity.Password"/> 的哈希值</returns>
  968 + private static string HashPasswordForStorage(string passwordOrMd5, string secretkey)
  969 + {
  970 + // 系统密码入库口径(与 OAuth 登录一致):
  971 + // - 登录/改密/重置密码:前端通常传 MD5(明文),服务端再做 MD5(MD5值 + Secretkey)
  972 + // - 新建用户:本次新增支持传“明文 password”,这里自动先做一层 MD5,再按同口径入库
  973 + var p = (passwordOrMd5 ?? "").Trim();
  974 + if (string.IsNullOrEmpty(p)) return MD5Encryption.Encrypt(MD5Encryption.Encrypt(CommonConst.DEFAULT_PASSWORD) + secretkey);
  975 +
  976 + static bool LooksLikeMd5(string s)
  977 + {
  978 + if (s.Length != 32) return false;
  979 + foreach (var ch in s)
  980 + {
  981 + var isHex = (ch >= '0' && ch <= '9')
  982 + || (ch >= 'a' && ch <= 'f')
  983 + || (ch >= 'A' && ch <= 'F');
  984 + if (!isHex) return false;
  985 + }
  986 + return true;
  987 + }
  988 +
  989 + var md5Once = LooksLikeMd5(p) ? p : MD5Encryption.Encrypt(p);
  990 + return MD5Encryption.Encrypt(md5Once + secretkey);
  991 + }
  992 +
933 #endregion 993 #endregion
934 } 994 }
935 } 995 }
936 \ No newline at end of file 996 \ No newline at end of file
Antis.Erp.Plat/netcore/src/Modularity/VisualDev/NCC.VisualDev/DashboardService.cs
@@ -26,6 +26,7 @@ namespace NCC.VisualDev @@ -26,6 +26,7 @@ namespace NCC.VisualDev
26 private readonly IUserManager _userManager; 26 private readonly IUserManager _userManager;
27 private readonly IDictionaryDataService _dictionaryDataService; 27 private readonly IDictionaryDataService _dictionaryDataService;
28 private readonly SqlSugarScope _db; 28 private readonly SqlSugarScope _db;
  29 + private static bool _wtFysrdAuditColsEnsured;
29 30
30 /// <summary> 31 /// <summary>
31 /// 初始化一个<see cref="DashboardService"/>类型的新实例 32 /// 初始化一个<see cref="DashboardService"/>类型的新实例
@@ -344,6 +345,8 @@ namespace NCC.VisualDev @@ -344,6 +345,8 @@ namespace NCC.VisualDev
344 /// </summary> 345 /// </summary>
345 private async Task<List<FlowTodoOutput>> GetExtendBillTodoListAsync(string userId, string userAccount) 346 private async Task<List<FlowTodoOutput>> GetExtendBillTodoListAsync(string userId, string userAccount)
346 { 347 {
  348 + EnsureWtFysrdAuditColumnsForTodo();
  349 + EnsureWtYskzjjsAuditColumnsForTodo();
347 var list = new List<FlowTodoOutput>(); 350 var list = new List<FlowTodoOutput>();
348 list.AddRange(await QueryWtXsckdAuditTodosAsync("同价调拨单", userId, userAccount, null)); 351 list.AddRange(await QueryWtXsckdAuditTodosAsync("同价调拨单", userId, userAccount, null));
349 // 仅单据来源为「后台」或未标记(ly 空)的销售出库单进待办;备注「抖音订单:」同后端免审规则 352 // 仅单据来源为「后台」或未标记(ly 空)的销售出库单进待办;备注「抖音订单:」同后端免审规则
@@ -361,12 +364,160 @@ namespace NCC.VisualDev @@ -361,12 +364,160 @@ namespace NCC.VisualDev
361 list.AddRange(await QueryWtXsckdAuditTodosAsync("委托代销退货单", userId, userAccount, null)); 364 list.AddRange(await QueryWtXsckdAuditTodosAsync("委托代销退货单", userId, userAccount, null));
362 list.AddRange(await QueryWtXsckdAuditTodosAsync("委托代销结算单", userId, userAccount, null)); 365 list.AddRange(await QueryWtXsckdAuditTodosAsync("委托代销结算单", userId, userAccount, null));
363 list.AddRange(await QueryWtTjdAuditTodosAsync(userId, userAccount)); 366 list.AddRange(await QueryWtTjdAuditTodosAsync(userId, userAccount));
  367 + list.AddRange(await QueryWtFysrdQtsrAuditTodosAsync(userId, userAccount));
  368 + list.AddRange(await QueryWtYskzjjsQtAuditTodosAsync(userId, userAccount));
364 return list.OrderByDescending(x => x.creatorTime).ToList(); 369 return list.OrderByDescending(x => x.creatorTime).ToList();
365 } 370 }
366 371
  372 + /// <summary>与 <c>wt_shrysz.djmc</c>、<c>WtFysrdWorkflowHelper.BillName</c> 一致</summary>
  373 + private const string FysrdQtsrTodoBillName = "其他收入单";
  374 +
  375 + /// <summary>
  376 + /// 待办 SQL 依赖列存在;与 Extend 内 <c>WtFysrdWorkflowHelper.EnsureFysrdAuditColumns</c> 保持一致
  377 + /// </summary>
  378 + private void EnsureWtFysrdAuditColumnsForTodo()
  379 + {
  380 + if (_wtFysrdAuditColsEnsured) return;
  381 + lock (typeof(DashboardService))
  382 + {
  383 + if (_wtFysrdAuditColsEnsured) return;
  384 + try
  385 + {
  386 + if (!_db.DbMaintenance.IsAnyTable("wt_fysrd")) { _wtFysrdAuditColsEnsured = true; return; }
  387 + var cols = _db.DbMaintenance.GetColumnInfosByTableName("wt_fysrd");
  388 + var names = cols.Select(c => c.DbColumnName.ToLowerInvariant()).ToHashSet();
  389 + if (!names.Contains("djzt"))
  390 + _db.Ado.ExecuteCommand(
  391 + "ALTER TABLE `wt_fysrd` ADD COLUMN `djzt` varchar(32) NULL COMMENT '单据状态'");
  392 + if (!names.Contains("spbz"))
  393 + _db.Ado.ExecuteCommand(
  394 + "ALTER TABLE `wt_fysrd` ADD COLUMN `spbz` text NULL COMMENT '审批备注'");
  395 + if (!names.Contains("shr"))
  396 + _db.Ado.ExecuteCommand(
  397 + "ALTER TABLE `wt_fysrd` ADD COLUMN `shr` varchar(64) NULL COMMENT '审核人'");
  398 + if (!names.Contains("shr1"))
  399 + _db.Ado.ExecuteCommand(
  400 + "ALTER TABLE `wt_fysrd` ADD COLUMN `shr1` varchar(500) NULL COMMENT '一级审核人'");
  401 + if (!names.Contains("shr2"))
  402 + _db.Ado.ExecuteCommand(
  403 + "ALTER TABLE `wt_fysrd` ADD COLUMN `shr2` varchar(500) NULL COMMENT '二级审核人'");
  404 + if (!names.Contains("wldw"))
  405 + _db.Ado.ExecuteCommand(
  406 + "ALTER TABLE `wt_fysrd` ADD COLUMN `wldw` varchar(64) NULL COMMENT '往来单位'");
  407 + }
  408 + catch (Exception ex) { Console.WriteLine($"EnsureWtFysrdAuditColumnsForTodo: {ex.Message}"); }
  409 + _wtFysrdAuditColsEnsured = true;
  410 + }
  411 + }
  412 +
367 /// <summary>与 <c>wt_shrysz.djmc</c>、<c>WtTjdWorkflowHelper.BillName</c> 一致</summary> 413 /// <summary>与 <c>wt_shrysz.djmc</c>、<c>WtTjdWorkflowHelper.BillName</c> 一致</summary>
368 private const string TjdTodoBillName = "商品调价单"; 414 private const string TjdTodoBillName = "商品调价单";
369 415
  416 + /// <summary>与 <c>wt_shrysz.djmc</c>、其他应收单 djlx 一致</summary>
  417 + private const string YskzjjsQtTodoBillName = "其他应收单";
  418 +
  419 + private static bool _wtYskzjjsAuditColsEnsured;
  420 +
  421 + /// <summary>
  422 + /// 待办 SQL 依赖列存在;与 Extend 内 WtYskzjjsService 的列映射保持一致
  423 + /// </summary>
  424 + private void EnsureWtYskzjjsAuditColumnsForTodo()
  425 + {
  426 + if (_wtYskzjjsAuditColsEnsured) return;
  427 + lock (typeof(DashboardService))
  428 + {
  429 + if (_wtYskzjjsAuditColsEnsured) return;
  430 + try
  431 + {
  432 + if (!_db.DbMaintenance.IsAnyTable("wt_yskzjjs")) { _wtYskzjjsAuditColsEnsured = true; return; }
  433 + var cols = _db.DbMaintenance.GetColumnInfosByTableName("wt_yskzjjs");
  434 + var names = cols.Select(c => c.DbColumnName.ToLowerInvariant()).ToHashSet();
  435 + if (!names.Contains("djzt"))
  436 + _db.Ado.ExecuteCommand(
  437 + "ALTER TABLE `wt_yskzjjs` ADD COLUMN `djzt` varchar(32) NULL COMMENT '单据状态'");
  438 + if (!names.Contains("spbz"))
  439 + _db.Ado.ExecuteCommand(
  440 + "ALTER TABLE `wt_yskzjjs` ADD COLUMN `spbz` text NULL COMMENT '审批备注'");
  441 + if (!names.Contains("shr"))
  442 + _db.Ado.ExecuteCommand(
  443 + "ALTER TABLE `wt_yskzjjs` ADD COLUMN `shr` varchar(64) NULL COMMENT '审核人'");
  444 + if (!names.Contains("shr1"))
  445 + _db.Ado.ExecuteCommand(
  446 + "ALTER TABLE `wt_yskzjjs` ADD COLUMN `shr1` varchar(500) NULL COMMENT '一级审核人'");
  447 + if (!names.Contains("shr2"))
  448 + _db.Ado.ExecuteCommand(
  449 + "ALTER TABLE `wt_yskzjjs` ADD COLUMN `shr2` varchar(500) NULL COMMENT '二级审核人'");
  450 + if (!names.Contains("wldw"))
  451 + _db.Ado.ExecuteCommand(
  452 + "ALTER TABLE `wt_yskzjjs` ADD COLUMN `wldw` varchar(64) NULL COMMENT '往来单位'");
  453 + if (!names.Contains("djlx"))
  454 + _db.Ado.ExecuteCommand(
  455 + "ALTER TABLE `wt_yskzjjs` ADD COLUMN `djlx` varchar(64) NULL COMMENT '单据类型'");
  456 + if (!names.Contains("djrq"))
  457 + _db.Ado.ExecuteCommand(
  458 + "ALTER TABLE `wt_yskzjjs` ADD COLUMN `djrq` datetime NULL COMMENT '单据日期'");
  459 + }
  460 + catch (Exception ex) { Console.WriteLine($"EnsureWtYskzjjsAuditColumnsForTodo: {ex.Message}"); }
  461 + _wtYskzjjsAuditColsEnsured = true;
  462 + }
  463 + }
  464 +
  465 + /// <summary>
  466 + /// 其他收入单待办(wt_fysrd + wt_shrysz)
  467 + /// </summary>
  468 + private async Task<List<FlowTodoOutput>> QueryWtFysrdQtsrAuditTodosAsync(string userId, string userAccount)
  469 + {
  470 + var result = new List<FlowTodoOutput>();
  471 + const string sql = @"
  472 +SELECT d.F_Id AS id, d.djrq, d.djzt,
  473 + IFNULL(NULLIF(TRIM(s.shr1), ''), s.shr) AS shr1_eff,
  474 + IFNULL(NULLIF(TRIM(s.shr2), ''), s.shr) AS shr2_eff
  475 +FROM wt_fysrd d
  476 +LEFT JOIN wt_shrysz s ON TRIM(s.djmc) = @billName
  477 +WHERE (d.F_Id LIKE 'QT%' OR TRIM(IFNULL(d.djlx,'')) IN ('其他收入单','其它收入单','其他收入','其它收入'))
  478 + AND IFNULL(d.djzt, '') <> '草稿'
  479 + AND IFNULL(d.djzt, '') <> '已审核'
  480 + AND IFNULL(d.djzt, '') <> '审核不通过'
  481 + AND (
  482 + d.djzt IN ('待审核', '一级已审', '待二级', '一级已审/待二级')
  483 + OR d.djzt IS NULL
  484 + OR TRIM(IFNULL(d.djzt, '')) = ''
  485 + )
  486 +ORDER BY d.djrq DESC";
  487 +
  488 + var rows = await _db.Ado.SqlQueryAsync<dynamic>(sql, new { billName = FysrdQtsrTodoBillName });
  489 + if (rows == null || rows.Count == 0) return result;
  490 +
  491 + foreach (var row in rows)
  492 + {
  493 + var status = Convert.ToString(row.djzt)?.Trim() ?? string.Empty;
  494 + var level1 = status == "待审核" || string.IsNullOrEmpty(status);
  495 + var level2 = status == "一级已审" || status == "待二级" || status == "一级已审/待二级";
  496 + if (!level1 && !level2) continue;
  497 +
  498 + var configUsers = level1 ? Convert.ToString(row.shr1_eff) : Convert.ToString(row.shr2_eff);
  499 + if (!string.IsNullOrWhiteSpace(configUsers) && !IsInAuditUsers(configUsers, userId, userAccount))
  500 + continue;
  501 +
  502 + DateTime? createTime = null;
  503 + var rawTime = Convert.ToString(row.djrq);
  504 + if (DateTime.TryParse(rawTime, out DateTime dt))
  505 + createTime = dt;
  506 +
  507 + var id = Convert.ToString(row.id);
  508 + var levelText = level1 ? "一级审核待办" : "二级审核待办";
  509 + result.Add(new FlowTodoOutput
  510 + {
  511 + id = id,
  512 + fullName = $"{FysrdQtsrTodoBillName} {id} - {levelText}",
  513 + creatorTime = createTime,
  514 + billType = FysrdQtsrTodoBillName
  515 + });
  516 + }
  517 +
  518 + return result;
  519 + }
  520 +
370 /// <summary> 521 /// <summary>
371 /// 商品调价单待办(wt_tjd + wt_shrysz) 522 /// 商品调价单待办(wt_tjd + wt_shrysz)
372 /// </summary> 523 /// </summary>
@@ -424,6 +575,62 @@ ORDER BY d.djrq DESC&quot;; @@ -424,6 +575,62 @@ ORDER BY d.djrq DESC&quot;;
424 } 575 }
425 576
426 /// <summary> 577 /// <summary>
  578 + /// 其他应收单待办(wt_yskzjjs + wt_shrysz)
  579 + /// </summary>
  580 + private async Task<List<FlowTodoOutput>> QueryWtYskzjjsQtAuditTodosAsync(string userId, string userAccount)
  581 + {
  582 + var result = new List<FlowTodoOutput>();
  583 + const string sql = @"
  584 +SELECT d.F_Id AS id, d.djrq, d.djzt,
  585 + IFNULL(NULLIF(TRIM(s.shr1), ''), s.shr) AS shr1_eff,
  586 + IFNULL(NULLIF(TRIM(s.shr2), ''), s.shr) AS shr2_eff
  587 +FROM wt_yskzjjs d
  588 +LEFT JOIN wt_shrysz s ON TRIM(s.djmc) = @billName
  589 +WHERE TRIM(IFNULL(d.djlx,'')) IN ('其他应收单','其他应收款')
  590 + AND IFNULL(d.djzt, '') <> '草稿'
  591 + AND IFNULL(d.djzt, '') <> '已审核'
  592 + AND IFNULL(d.djzt, '') <> '审核不通过'
  593 + AND (
  594 + d.djzt IN ('待审核', '一级已审', '待二级', '一级已审/待二级')
  595 + OR d.djzt IS NULL
  596 + OR TRIM(IFNULL(d.djzt, '')) = ''
  597 + )
  598 +ORDER BY d.djrq DESC";
  599 +
  600 + var rows = await _db.Ado.SqlQueryAsync<dynamic>(sql, new { billName = YskzjjsQtTodoBillName });
  601 + if (rows == null || rows.Count == 0) return result;
  602 +
  603 + foreach (var row in rows)
  604 + {
  605 + var status = Convert.ToString(row.djzt)?.Trim() ?? string.Empty;
  606 + var level1 = status == "待审核" || string.IsNullOrEmpty(status);
  607 + var level2 = status == "一级已审" || status == "待二级" || status == "一级已审/待二级";
  608 + if (!level1 && !level2) continue;
  609 +
  610 + var configUsers = level1 ? Convert.ToString(row.shr1_eff) : Convert.ToString(row.shr2_eff);
  611 + if (!string.IsNullOrWhiteSpace(configUsers) && !IsInAuditUsers(configUsers, userId, userAccount))
  612 + continue;
  613 +
  614 + DateTime? createTime = null;
  615 + var rawTime = Convert.ToString(row.djrq);
  616 + if (DateTime.TryParse(rawTime, out DateTime dt))
  617 + createTime = dt;
  618 +
  619 + var id = Convert.ToString(row.id);
  620 + var levelText = level1 ? "一级审核待办" : "二级审核待办";
  621 + result.Add(new FlowTodoOutput
  622 + {
  623 + id = id,
  624 + fullName = $"{YskzjjsQtTodoBillName} {id} - {levelText}",
  625 + creatorTime = createTime,
  626 + billType = YskzjjsQtTodoBillName
  627 + });
  628 + }
  629 +
  630 + return result;
  631 + }
  632 +
  633 + /// <summary>
427 /// 按单据类型查询待审核/待二级单据,并按 shr1/shr2(或 shr)过滤当前用户 634 /// 按单据类型查询待审核/待二级单据,并按 shr1/shr2(或 shr)过滤当前用户
428 /// </summary> 635 /// </summary>
429 /// <param name="billDjlx">与 wt_xsckd.djlx、wt_shrysz.djmc 一致</param> 636 /// <param name="billDjlx">与 wt_xsckd.djlx、wt_shrysz.djmc 一致</param>
Antis.Erp.Plat/sy/home.html
@@ -475,7 +475,7 @@ @@ -475,7 +475,7 @@
475 <svg class="pos-inline-svg pos-header-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"> 475 <svg class="pos-inline-svg pos-header-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
476 <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" /> 476 <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" />
477 </svg> 477 </svg>
478 - <span>收银台</span> 478 + <span>{{ posCashierHeaderTitle }}</span>
479 </div> 479 </div>
480 <div class="home-right pos-header-tool-row"> 480 <div class="home-right pos-header-tool-row">
481 <button type="button" class="pos-header-tool-btn settlement-touch" aria-label="刷新页面" onclick="location.reload()"> 481 <button type="button" class="pos-header-tool-btn settlement-touch" aria-label="刷新页面" onclick="location.reload()">
@@ -800,6 +800,21 @@ @@ -800,6 +800,21 @@
800 }, 800 },
801 // 添加计算属性 801 // 添加计算属性
802 computed: { 802 computed: {
  803 + /** 顶栏标题:登录所选门店名称 + 收银台 */
  804 + posCashierHeaderTitle() {
  805 + try {
  806 + var raw = localStorage.getItem('selectedStore');
  807 + if (raw) {
  808 + var s = JSON.parse(raw);
  809 + var name = String(s.mdmc || s.Mdmc || s.name || '').trim();
  810 + if (name) {
  811 + var withDian = name.lastIndexOf('店') === name.length - 1 ? name : (name + '店');
  812 + return withDian + '收银台';
  813 + }
  814 + }
  815 + } catch (e) {}
  816 + return '收银台';
  817 + },
803 // 入0出1:仅手动输入序列号 818 // 入0出1:仅手动输入序列号
804 isSerialNumberModalR0C1() { 819 isSerialNumberModalR0C1() {
805 return this.currentSerialNumberItem && this.currentSerialNumberItem.spxlhType === '2'; 820 return this.currentSerialNumberItem && this.currentSerialNumberItem.spxlhType === '2';
Antis.Erp.Plat/sy/settlement.html
@@ -3749,7 +3749,7 @@ @@ -3749,7 +3749,7 @@
3749 <svg class="pos-inline-svg pos-header-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"> 3749 <svg class="pos-inline-svg pos-header-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
3750 <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" /> 3750 <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" />
3751 </svg> 3751 </svg>
3752 - <span>收银台</span> 3752 + <span>{{ posCashierHeaderTitle }}</span>
3753 </div> 3753 </div>
3754 <div class="home-right pos-header-tool-row"> 3754 <div class="home-right pos-header-tool-row">
3755 <button type="button" class="pos-header-tool-btn settlement-touch" aria-label="刷新页面" onclick="location.reload()"> 3755 <button type="button" class="pos-header-tool-btn settlement-touch" aria-label="刷新页面" onclick="location.reload()">
@@ -4410,6 +4410,21 @@ @@ -4410,6 +4410,21 @@
4410 // } 4410 // }
4411 }, 4411 },
4412 computed: { 4412 computed: {
  4413 + /** 顶栏标题:登录所选门店名称 + 收银台 */
  4414 + posCashierHeaderTitle() {
  4415 + try {
  4416 + var raw = localStorage.getItem('selectedStore');
  4417 + if (raw) {
  4418 + var s = JSON.parse(raw);
  4419 + var name = String(s.mdmc || s.Mdmc || s.name || '').trim();
  4420 + if (name) {
  4421 + var withDian = name.lastIndexOf('店') === name.length - 1 ? name : (name + '店');
  4422 + return withDian + '收银台';
  4423 + }
  4424 + }
  4425 + } catch (e) {}
  4426 + return '收银台';
  4427 + },
4413 settlementMemberBenefitLine() { 4428 settlementMemberBenefitLine() {
4414 if (!this.hyinfo || this.hyinfo === 'null') return ''; 4429 if (!this.hyinfo || this.hyinfo === 'null') return '';
4415 var h = this.hyinfo; 4430 var h = this.hyinfo;