Commit b688ba6d5cf884a23b57d4af8d8da1b44766cfc4

Authored by 史婷婷
1 parent efe39249

feat: 补货单-填写补货时间-100%

... ... @@ -720,6 +720,14 @@
720 720 "navigationBarBackgroundColor": "#ffffff",
721 721 "navigationBarTextStyle": "black"
722 722 }
  723 + },
  724 + {
  725 + "path": "pages/replenishment_order/fill",
  726 + "style": {
  727 + "navigationBarTitleText": "填写补货时间",
  728 + "navigationBarBackgroundColor": "#ffffff",
  729 + "navigationBarTextStyle": "black"
  730 + }
723 731 }
724 732 ],
725 733 "subPackages": [
... ...
  1 +<template>
  2 + <view class="page">
  3 + <scroll-view class="scroll" scroll-y>
  4 + <uni-list>
  5 + <view class="section">
  6 + <!-- 编辑模式下,订单编号通常不允许修改,或者作为只读显示,但为了保持与新增页UI一致,这里保留结构但设为不可点击 -->
  7 + <uni-list-item class="select-item is-filled" :rightText="form.purchaseOrderName || ''">
  8 + <template v-slot:body>
  9 + <view class="item-title"><text class="required">*</text><text>订单编号</text></view>
  10 + </template>
  11 + </uni-list-item>
  12 + <uni-list-item title="补货单编号">
  13 + <template v-slot:footer>
  14 + <view class="readonly-text">{{ form.code }}</view>
  15 + </template>
  16 + </uni-list-item>
  17 + <uni-list-item title="分厂">
  18 + <template v-slot:footer>
  19 + <view class="readonly-text">{{ form.workshopName }}</view>
  20 + </template>
  21 + </uni-list-item>
  22 + <uni-list-item title="办事处">
  23 + <template v-slot:footer>
  24 + <view class="readonly-text">{{ form.deptName }}</view>
  25 + </template>
  26 + </uni-list-item>
  27 + <uni-list-item title="区域">
  28 + <template v-slot:footer>
  29 + <view class="readonly-text">{{ form.regionName }}</view>
  30 + </template>
  31 + </uni-list-item>
  32 + <uni-list-item title="购货单位">
  33 + <template v-slot:footer>
  34 + <view class="readonly-text">{{ form.customerName }}</view>
  35 + </template>
  36 + </uni-list-item>
  37 + <uni-list-item title="原计划发货日期">
  38 + <template v-slot:footer>
  39 + <view class="readonly-text">{{ form.originPlanShipDate }}</view>
  40 + </template>
  41 + </uni-list-item>
  42 + </view>
  43 +
  44 + <!-- 产品 -->
  45 + <view class="section2">
  46 + <!-- mode="fill" 允许个别字段编辑 -->
  47 + <Product mode="fill" :list="initPurchaseOrderLineList" @change="purchaseOrderLineListChange"
  48 + :orderDate="form.orderDate" />
  49 + </view>
  50 +
  51 + </uni-list>
  52 + </scroll-view>
  53 +
  54 + <view class="footer">
  55 + <view class="view-total">
  56 + <view class="head">合计</view>
  57 + <view class="row">
  58 + <view class="row2">
  59 + <text class="label">需发</text><text class="value">{{ form.totalQuantity }}</text>
  60 + </view>
  61 + <view class="row2">
  62 + <text class="label">实发</text><text class="value">{{ form.totalShippedQuantity }}</text>
  63 + </view>
  64 + </view>
  65 + <view class="row">
  66 + <view class="row2">
  67 + <text class="label">需求补货</text><text class="value">{{ form.totalSupplementaryQuantity }}</text>
  68 + </view>
  69 + </view>
  70 + </view>
  71 + <button class="btn submit" type="primary" @click="onSubmit">保存</button>
  72 + </view>
  73 + </view>
  74 +</template>
  75 +
  76 +<script>
  77 +import { fillRestockTimeApi, getDetailApi } from '@/api/replenishment_order.js'
  78 +import Product from './product.vue'
  79 +
  80 +export default {
  81 + name: 'ReplenishmentOrderFill',
  82 + components: { Product },
  83 + data() {
  84 + return {
  85 + form: {
  86 + purchaseOrderId: '',
  87 + id: '',
  88 + // 订单基础信息
  89 + purchaseOrderName: '',
  90 + customerName: '',
  91 + customerId: '',
  92 + workshopName: '',
  93 + workshopId: '',
  94 + originPlanShipDate: '',
  95 + deptName: '',
  96 + deptId: '',
  97 + totalQuantity: '',
  98 + totalShippedQuantity: '',
  99 + totalSupplementaryQuantity: '',
  100 + code: '',
  101 + // 默认当前日期 格式为 yyyy-MM-dd
  102 + orderDate: new Date().toISOString().substring(0, 10),
  103 +
  104 + },
  105 + initPurchaseOrderLineList: [],
  106 + maxDeliveryDate: new Date().toISOString().substring(0, 10),
  107 + }
  108 + },
  109 + onLoad(query) {
  110 + const id = (query && (query.id || query.code)) || ''
  111 + if (id) {
  112 + this.loadDetail(id)
  113 + }
  114 + },
  115 + methods: {
  116 + async loadDetail(id) {
  117 + try {
  118 + const res = await getDetailApi(id)
  119 + const m = res.data || {}
  120 + const next = { ...this.form, ...m }
  121 + // 确保ID存在
  122 + next.id = m.id || id
  123 + // 映射列表
  124 + // 注意:详情返回的是 replenishmentOrderLineList,需要赋值给 initPurchaseOrderLineList 以便 Product 组件初始化
  125 + // 且需要处理字段兼容性,确保 Product 组件能正确显示和编辑
  126 + const lines = Array.isArray(m.replenishmentOrderLineList) ? m.replenishmentOrderLineList.map(x => ({
  127 + ...x,
  128 + // 确保 Product 组件需要的字段存在
  129 + // Product组件使用: quantity(需发), shippedQuantity(实发), supplementaryQuantity(需求补货), salesPrice(单价)
  130 + // 详情接口返回的字段应该已经包含了这些,如果有差异需要在此处转换
  131 + // 注意:add.vue中 onRelateConfirm 做了映射,这里是回显,通常直接使用即可
  132 + })) : []
  133 +
  134 + this.form = next;
  135 + this.initPurchaseOrderLineList = lines;
  136 + // 初始计算合计
  137 + this.calculateSummary(lines)
  138 + } catch (e) {
  139 + uni.showToast({ title: '加载失败', icon: 'none' })
  140 + }
  141 + },
  142 + validateRequired() {
  143 + const checks = [
  144 + { key: 'purchaseOrderName', label: '订单编号' }
  145 + ]
  146 + for (const it of checks) {
  147 + const val = this.form[it.key]
  148 + if (val === undefined || val === null || String(val).trim() === '') {
  149 + uni.showToast({ title: `请先选择${it.label}`, icon: 'none' })
  150 + return false
  151 + }
  152 + }
  153 + return true
  154 + },
  155 + validateLineListRequired() {
  156 + const list = Array.isArray(this.form.purchaseOrderLineList) ? this.form.purchaseOrderLineList : []
  157 + if (list.length === 0) {
  158 + uni.showToast({ title: '请先添加产品', icon: 'none' })
  159 + return false
  160 + }
  161 + const fields = [
  162 + { key: 'confirmedDeliveryDate', label: '生产科(车间)确认交付时间' },
  163 + ]
  164 + for (let i = 0; i < list.length; i++) {
  165 + const it = list[i] || {}
  166 + for (const f of fields) {
  167 + const v = it && it[f.key]
  168 + if (v === undefined || v === null || String(v).trim() === '') {
  169 + uni.showToast({ title: `产品第${i + 1}条:${f.label}不能为空!`, icon: 'none' })
  170 + return false
  171 + }
  172 + }
  173 + }
  174 + return true
  175 + },
  176 + async onSubmit() {
  177 + if (!this.validateRequired()) return
  178 + if (!this.validateLineListRequired()) return
  179 + const payload = { ...this.form }
  180 + // 后端接口通常期望 replenishmentOrderLineList
  181 + payload.replenishmentOrderLineList = payload.purchaseOrderLineList || [];
  182 + // 清理可能存在的多余字段
  183 + delete payload.purchaseOrderLineList;
  184 +
  185 + console.log('onSubmit__payload', payload)
  186 + try {
  187 + await fillRestockTimeApi(payload)
  188 + uni.showToast({ title: '保存成功', icon: 'success' })
  189 + setTimeout(() => { uni.redirectTo({ url: '/pages/replenishment_order/index' }) }, 300)
  190 + } catch (e) {
  191 + uni.showToast({ title: (e && e.msg) || '保存失败', icon: 'none' })
  192 + }
  193 + },
  194 + calculateSummary(list) {
  195 + const summary = (list || []).reduce((acc, it) => {
  196 + const qty = Number(it.supplementaryQuantity) || 0
  197 + const shipped = Number(it.shippedQuantity) || 0
  198 + const orderQty = Number(it.quantity) || 0
  199 + acc.totalSupplementaryQuantity += qty
  200 + acc.totalShippedQuantity += shipped
  201 + acc.totalQuantity += orderQty
  202 + return acc
  203 + }, { totalQuantity: 0, totalShippedQuantity: 0, totalSupplementaryQuantity: 0 })
  204 + this.form.totalQuantity = summary.totalQuantity
  205 + this.form.totalShippedQuantity = summary.totalShippedQuantity
  206 + this.form.totalSupplementaryQuantity = summary.totalSupplementaryQuantity
  207 + },
  208 + purchaseOrderLineListChange(data) {
  209 + const list = Array.isArray(data) ? data : []
  210 + this.form.purchaseOrderLineList = list
  211 + this.calculateSummary(list)
  212 + },
  213 + }
  214 +}
  215 +</script>
  216 +
  217 +<style lang="scss" scoped>
  218 +.page {
  219 + display: flex;
  220 + flex-direction: column;
  221 + height: 100%;
  222 +}
  223 +
  224 +.scroll {
  225 + flex: 1;
  226 + padding: 6rpx 0 356rpx;
  227 +}
  228 +
  229 +
  230 +
  231 +.title-header {
  232 + background-color: #fff;
  233 + display: flex;
  234 + align-items: center;
  235 + padding: 32rpx 32rpx 22rpx;
  236 +
  237 + .title-header_icon {
  238 + width: 32rpx;
  239 + height: 28rpx;
  240 + margin-right: 16rpx;
  241 + }
  242 +
  243 + span {
  244 + color: rgba(0, 0, 0, 0.9);
  245 + font-size: 32rpx;
  246 + line-height: 44rpx;
  247 + font-weight: 600;
  248 + }
  249 +}
  250 +
  251 +
  252 +.section {
  253 + background: #fff;
  254 + margin-bottom: 20rpx;
  255 +}
  256 +
  257 +.section2 {
  258 + background: #f1f1f1;
  259 +}
  260 +
  261 +::v-deep .uni-list {
  262 + background: transparent;
  263 +
  264 + &-item {
  265 + &__extra-text {
  266 + font-size: 32rpx;
  267 + }
  268 +
  269 + &__content-title {
  270 + font-size: 32rpx;
  271 + color: rgba(0, 0, 0, 0.9);
  272 + }
  273 +
  274 + &__container {
  275 + padding: 32rpx;
  276 + // align-items: center;
  277 +
  278 + .uni-easyinput {
  279 +
  280 + .is-disabled {
  281 + background-color: transparent !important;
  282 + }
  283 +
  284 + &__placeholder-class {
  285 + font-size: 32rpx;
  286 + color: rgba(0, 0, 0, 0.4);
  287 + }
  288 +
  289 + &__content {
  290 + border: none;
  291 +
  292 + &-input {
  293 + padding-left: 0 !important;
  294 + height: 48rpx;
  295 + line-height: 48rpx;
  296 + font-size: 32rpx;
  297 + }
  298 +
  299 + .content-clear-icon {
  300 + font-size: 44rpx !important;
  301 + }
  302 + }
  303 + }
  304 +
  305 + .amount-row {
  306 + flex: 1;
  307 + display: flex;
  308 + align-items: center;
  309 +
  310 + .uni-easyinput {
  311 + flex: 1;
  312 + }
  313 +
  314 + .unit {
  315 + margin-left: 16rpx;
  316 + color: rgba(0, 0, 0, 0.9);
  317 + }
  318 + }
  319 +
  320 + .item-title,
  321 + .uni-list-item__content {
  322 + flex: none;
  323 + min-height: 48rpx;
  324 + line-height: 48rpx;
  325 + font-size: 32rpx;
  326 + position: relative;
  327 + width: 210rpx;
  328 + margin-right: 32rpx;
  329 + color: rgba(0, 0, 0, 0.9);
  330 + padding-right: 0;
  331 +
  332 +
  333 + .required {
  334 + color: red;
  335 + position: absolute;
  336 + top: 50%;
  337 + transform: translateY(-50%);
  338 + left: -16rpx;
  339 + }
  340 + }
  341 +
  342 + }
  343 +
  344 + &.select-item {
  345 + &.is-empty {
  346 + .uni-list-item__extra-text {
  347 + color: rgba(0, 0, 0, 0.4) !important;
  348 + }
  349 + }
  350 +
  351 + &.is-filled {
  352 + .uni-list-item__extra-text {
  353 + color: rgba(0, 0, 0, 0.9) !important;
  354 + }
  355 + }
  356 +
  357 + .serial-number-row {
  358 + display: flex;
  359 + align-items: center;
  360 + }
  361 +
  362 + }
  363 +
  364 + &.mgb10 {
  365 + margin-bottom: 20rpx;
  366 + }
  367 +
  368 + }
  369 +
  370 + .title-header {
  371 + background-color: #fff;
  372 + display: flex;
  373 + align-items: center;
  374 + padding: 32rpx 32rpx 22rpx;
  375 +
  376 + &_icon {
  377 + width: 32rpx;
  378 + height: 28rpx;
  379 + margin-right: 16rpx;
  380 + }
  381 +
  382 + span {
  383 + color: rgba(0, 0, 0, 0.9);
  384 + font-size: 32rpx;
  385 + line-height: 44rpx;
  386 + font-weight: 600;
  387 + }
  388 + }
  389 +}
  390 +
  391 +/* 只读 easyinput 根据内容自适应高度 */
  392 +::v-deep .uni-list-item__container {
  393 + align-items: flex-start;
  394 +}
  395 +
  396 +/* 只读文本样式 */
  397 +.readonly-text {
  398 + color: rgba(0, 0, 0, 0.9);
  399 + font-size: 32rpx;
  400 + line-height: 48rpx;
  401 + text-align: right;
  402 + white-space: pre-wrap;
  403 + word-break: break-all;
  404 +}
  405 +
  406 +
  407 +.footer {
  408 + position: fixed;
  409 + left: 0;
  410 + right: 0;
  411 + bottom: 0;
  412 + padding: 0 32rpx 32rpx;
  413 + padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
  414 + background: #fff;
  415 + box-shadow: 0 -8rpx 24rpx rgba(0, 0, 0, 0.06);
  416 + z-index: 10;
  417 +
  418 + .btn {
  419 + height: 80rpx;
  420 + line-height: 80rpx;
  421 + border-radius: 12rpx;
  422 + font-size: 32rpx;
  423 + }
  424 +
  425 + .submit {
  426 + background: $theme-primary;
  427 + color: #fff;
  428 + }
  429 +
  430 + .view-total {
  431 + padding: 20rpx 0;
  432 +
  433 + .head {
  434 + font-size: 32rpx;
  435 + font-weight: 600;
  436 + line-height: 50rpx;
  437 + color: rgba(0, 0, 0, 0.9);
  438 + padding-bottom: 16rpx;
  439 + margin-bottom: 24rpx;
  440 + ;
  441 + border-bottom: 1px dashed #E7E7E7;
  442 + }
  443 +
  444 + .row {
  445 + display: flex;
  446 + margin-bottom: 24rpx;
  447 + line-height: 32rpx;
  448 +
  449 + .row2 {
  450 + width: 50%;
  451 + }
  452 +
  453 + .label {
  454 + width: 180rpx;
  455 + margin-right: 14rpx;
  456 + color: rgba(0, 0, 0, 0.6);
  457 + font-size: 28rpx;
  458 + }
  459 +
  460 + .value {
  461 + flex: 1;
  462 + color: rgba(0, 0, 0, 0.9);
  463 + font-size: 28rpx;
  464 + white-space: pre-wrap;
  465 + word-break: break-all;
  466 + }
  467 + }
  468 + }
  469 +}
  470 +</style>
\ No newline at end of file
... ...
... ... @@ -171,6 +171,113 @@
171 171 </view>
172 172 </view>
173 173
  174 + <view v-else-if="mode === 'fill'" class="section">
  175 + <view v-for="(item, idx) in items" :key="'a-' + idx" class="block">
  176 + <uni-list class="edit-list">
  177 + <uni-list-item title="牌号">
  178 + <template v-slot:footer>
  179 + <text class="value">{{ item.brand }}</text>
  180 + </template>
  181 + </uni-list-item>
  182 + <!-- 厚(公差) * 宽(公差) * 长(公差) -->
  183 + <uni-list-item title="规格(mm)">
  184 + <template v-slot:footer>
  185 + <view class="value value-spec">
  186 + <view v-if="item.thickness" class="value-spec_val">{{ item.thickness }}</view>
  187 + <view v-if="item.thickness" class="value-spec_box">
  188 + <view v-if="item.thicknessTolPos" class="value-spec_box_1">{{ item.thicknessTolPos >
  189 + 0 ? '+'
  190 + +
  191 + item.thicknessTolPos : item.thicknessTolPos }}
  192 + </view>
  193 + <view v-if="item.thicknessTolNeg" class="value-spec_box_2">{{ item.thicknessTolNeg >
  194 + 0 ? '+'
  195 + +
  196 + item.thicknessTolNeg : item.thicknessTolNeg }}
  197 + </view>
  198 + </view>
  199 + <view v-if="item.width" class="value-spec_val p12">*</view>
  200 + <view v-if="item.width" class="value-spec_val">{{ item.width }}</view>
  201 + <view v-if="item.width" class="value-spec_box">
  202 + <view v-if="item.widthTolPos" class="value-spec_box_1">{{ item.widthTolPos > 0 ? '+'
  203 + +
  204 + item.widthTolPos : item.widthTolPos }}
  205 + </view>
  206 + <view v-if="item.widthTolNeg" class="value-spec_box_2">{{ item.widthTolNeg > 0 ? '+'
  207 + +
  208 + item.widthTolNeg : item.widthTolNeg }}
  209 + </view>
  210 + </view>
  211 + <view v-if="item.length" class="value-spec_val p12">*</view>
  212 + <view v-if="item.length" class="value-spec_val">{{ item.length }}</view>
  213 + <view v-if="item.length" class="value-spec_box">
  214 + <view v-if="item.lengthTolPos" class="value-spec_box_1">{{ item.lengthTolPos > 0 ?
  215 + '+' +
  216 + item.lengthTolPos : item.lengthTolPos }}
  217 + </view>
  218 + <view v-if="item.lengthTolNeg" class="value-spec_box_2">{{ item.lengthTolNeg > 0 ?
  219 + '+' +
  220 + item.lengthTolNeg : item.lengthTolNeg }}
  221 + </view>
  222 + </view>
  223 + </view>
  224 + </template>
  225 + </uni-list-item>
  226 + <view v-show="!item.collapsed">
  227 + <uni-list-item title="状态">
  228 + <template v-slot:footer>
  229 + <text class="value">{{ item.status }}</text>
  230 + </template>
  231 + </uni-list-item>
  232 + <uni-list-item title="需发数量(kg)">
  233 + <template v-slot:footer>
  234 + <text class="value">{{ item.quantity }}</text>
  235 + </template>
  236 + </uni-list-item>
  237 + <uni-list-item title="实发数量(kg)">
  238 + <template v-slot:footer>
  239 + <text class="value">{{ item.shippedQuantity }}</text>
  240 + </template>
  241 + </uni-list-item>
  242 + <uni-list-item title="需求补货数量(kg)">
  243 + <template v-slot:footer>
  244 + <text class="value">{{ item.supplementaryQuantity }}</text>
  245 + </template>
  246 + </uni-list-item>
  247 + <uni-list-item class="amount-item" title="单价(元/kg)">
  248 + <template v-slot:footer>
  249 + <text class="value">{{ item.salesPrice }}</text>
  250 + </template>
  251 + </uni-list-item>
  252 + <uni-list-item class="amount-item" title="包装费(元/kg)">
  253 + <template v-slot:footer>
  254 + <text class="value">{{ item.packagingFee }}</text>
  255 + </template>
  256 + </uni-list-item>
  257 + <uni-list-item title="生产科(车间)确认交付时间">
  258 + <template v-slot:footer>
  259 + <uni-datetime-picker type="date"
  260 + v-model="item.confirmedDeliveryDate" />
  261 + </template>
  262 + </uni-list-item>
  263 + <uni-list-item title="备注">
  264 + <template v-slot:footer>
  265 + <text class="value">{{ item.remarks }}元</text>
  266 + </template>
  267 + </uni-list-item>
  268 + </view>
  269 + </uni-list>
  270 +
  271 + <view class="block-ops">
  272 + <div class="toggle" @click="toggleItem(idx)">
  273 + <image :src="item.collapsed ? '/static/images/up.png' : '/static/images/down.png'"
  274 + class="icon" />
  275 + {{ item.collapsed ? '展开' : '收起' }}
  276 + </div>
  277 + </view>
  278 + </view>
  279 + </view>
  280 +
174 281 <view v-else-if="mode === 'view'" class="view-list" v-show="!collapsedView">
175 282 <view v-for="(item, idx) in items" :key="'v-' + idx" class="card">
176 283 <view class="row"><text class="label">牌号</text><text class="value">{{ item.brand }}</text></view>
... ... @@ -211,13 +318,20 @@
211 318 </view>
212 319 </view>
213 320 <view class="row"><text class="label">状态</text><text class="value">{{ item.status }}</text></view>
214   - <view class="row"><text class="label">需发数量(kg)</text><text class="value">{{ item.quantity }}</text></view>
215   - <view class="row"><text class="label">实发数量(kg)</text><text class="value">{{ item.shippedQuantity }}</text></view>
216   - <view class="row"><text class="label">需求补货数量(kg)</text><text class="value">{{ item.supplementaryQuantity }}</text></view>
217   - <view class="row"><text class="label">单价(元/kg)</text><text class="value">{{ item.salesPrice }}</text></view>
218   - <view class="row"><text class="label">包装费</text><text class="value">{{
  321 + <view class="row"><text class="label">需发数量(kg)</text><text class="value">{{ item.quantity }}</text>
  322 + </view>
  323 + <view class="row"><text class="label">实发数量(kg)</text><text class="value">{{ item.shippedQuantity
  324 + }}</text>
  325 + </view>
  326 + <view class="row"><text class="label">需求补货数量(kg)</text><text class="value">{{ item.supplementaryQuantity
  327 + }}</text></view>
  328 + <view class="row"><text class="label">单价(元/kg)</text><text class="value">{{ item.salesPrice }}</text>
  329 + </view>
  330 + <view class="row"><text class="label">包装费(元/kg)</text><text class="value">{{
219 331 item.packagingFee }}</text></view>
220   - <view class="row"><text class="label">生产科(车间)确认交付时间</text><text class="value">{{ item.confirmedDeliveryDate }}</text>
  332 + <view class="row"><text class="label">生产科(车间)确认交付时间</text><text class="value">{{
  333 + item.confirmedDeliveryDate
  334 + }}</text>
221 335 </view>
222 336 <view class="row"><text class="label">备注</text><text class="value">{{ item.remarks }}</text></view>
223 337 </view>
... ...