Commit 1d7e84255c069cbd62bffdad0bb9f770ecb6ac8a

Authored by gesilong
2 parents 246be218 314f4d0f

Merge branch 'cjerp-2.0-plan' into publish_cjerp

Too many changes to show.

To preserve performance only 13 of 30 files are displayed.

... ... @@ -120,4 +120,40 @@ export function workshopQueryApi(params) {
120 120 method: 'get',
121 121 params
122 122 })
  123 +}
  124 +
  125 +// 价格表 查询
  126 +export function priceTableQueryApi(params) {
  127 + return request({
  128 + url: `/baseData/price/query`,
  129 + method: 'get',
  130 + params
  131 + })
  132 +}
  133 +
  134 +// 品种关系 查询
  135 +export function breedRelationshipQueryApi(params) {
  136 + return request({
  137 + url: `/baseData/breedRelationship/query`,
  138 + method: 'get',
  139 + params
  140 + })
  141 +}
  142 +
  143 +// 公司简称 查询
  144 +export function companyShortQueryApi(params) {
  145 + return request({
  146 + url: `/basedata/company/short/query`,
  147 + method: 'get',
  148 + params
  149 + })
  150 +}
  151 +
  152 +// 价格表 详情
  153 +export function priceTableDetailApi(params) {
  154 + return request({
  155 + url: `/baseData/price`,
  156 + method: 'get',
  157 + params
  158 + })
123 159 }
\ No newline at end of file
... ...
  1 +import request from '@/utils/request'
  2 +
  3 +export function buyoutApplyDetailByPlanIdApi(planId) {
  4 + return request({
  5 + url: '/procurement/buyout/apply/detailByPlanId',
  6 + method: 'get',
  7 + params: { planId: planId }
  8 + })
  9 +}
  10 +
  11 +export function buyoutApplyCreateApi(data) {
  12 + return request({
  13 + url: '/procurement/buyout/apply/create',
  14 + method: 'post',
  15 + data: data,
  16 + contentType: 'application/json'
  17 + })
  18 +}
  19 +
  20 +export function buyoutApplyAbolishApi(planId) {
  21 + return request({
  22 + url: '/procurement/buyout/apply/abolish',
  23 + method: 'post',
  24 + params: { planId: planId }
  25 + })
  26 +}
  27 +
  28 +export function buyoutApplyDeleteByPlanIdApi(planId) {
  29 + return request({
  30 + url: '/procurement/buyout/apply/deleteByPlanId?planId=' + planId,
  31 + method: 'post',
  32 + })
  33 +}
... ...
  1 +import request from '@/utils/request'
  2 +import { ContentTypeEnum } from '@/utils/httpEnum'
  3 +
  4 +export function procurementPlanQueryApi(params) {
  5 + return request({
  6 + url: '/procurement/plan/query',
  7 + method: 'get',
  8 + params
  9 + })
  10 +}
  11 +
  12 +export function procurementPlanChangeQueryApi(params) {
  13 + return request({
  14 + url: '/procurement/plan/change/query',
  15 + method: 'get',
  16 + params
  17 + })
  18 +}
  19 +
  20 +export function procurementReplenishQueryApi(params) {
  21 + return request({
  22 + url: '/procurement/plan/replenish/query',
  23 + method: 'get',
  24 + params
  25 + })
  26 +}
  27 +
  28 +export function procurementPlanCanChangeOrReplenishmentApi(params) {
  29 + return request({
  30 + url: '/procurement/plan/queryCanChangeOrReplenishment',
  31 + method: 'get',
  32 + params
  33 + })
  34 +}
  35 +
  36 +
  37 +export function procurementPlanChangeCreateApi(data) {
  38 + return request({
  39 + url: '/procurement/plan/change/create',
  40 + method: 'post',
  41 + data,
  42 + contentType: ContentTypeEnum.JSON
  43 + })
  44 +}
  45 +
  46 +export function procurementPlanReplenplenishApi(data) {
  47 + return request({
  48 + url: '/procurement/plan/replenish',
  49 + method: 'post',
  50 + data,
  51 + contentType: ContentTypeEnum.JSON
  52 + })
  53 +}
  54 +
  55 +export function procurementPlanChangeFindByIdApi(params) {
  56 + return request({
  57 + url: '/procurement/plan/change/findById',
  58 + method: 'get',
  59 + params
  60 + })
  61 +}
  62 +
  63 +// export function procurementPlanReplenishFindByIdApi(params) {
  64 +// return request({
  65 +// url: '/procurement/plan/replenish',
  66 +// method: 'get',
  67 +// params
  68 +// })
  69 +// }
  70 +
  71 +export function procurementPlanReplenishFindByIdApi(params) {
  72 + return request({
  73 + url: '/procurement/plan/replenish',
  74 + method: 'get',
  75 + params
  76 + })
  77 +}
  78 +
  79 +export function procurementPlanChangeUpdateApi(data) {
  80 + return request({
  81 + url: '/procurement/plan/change/update',
  82 + method: 'post',
  83 + data,
  84 + contentType: ContentTypeEnum.JSON
  85 + })
  86 +}
  87 +
  88 +export function procurementPlanReplenishUpdateApi(data) {
  89 + return request({
  90 + url: '/procurement/plan/replenish',
  91 + method: 'put',
  92 + data,
  93 + contentType: ContentTypeEnum.JSON
  94 + })
  95 +}
  96 +
  97 +export function procurementPlanSaveApi(data) {
  98 + return request({
  99 + url: '/procurement/plan',
  100 + method: 'post',
  101 + data,
  102 + contentType: ContentTypeEnum.JSON
  103 + })
  104 +}
  105 +
  106 +export function procurementPlanDetailApi(params) {
  107 + return request({
  108 + url: '/procurement/plan',
  109 + method: 'get',
  110 + params
  111 + })
  112 +}
  113 +
  114 +export function procurementPlanUpdateApi(data) {
  115 + return request({
  116 + url: '/procurement/plan',
  117 + method: 'put',
  118 + data,
  119 + contentType: ContentTypeEnum.JSON
  120 + })
  121 +}
  122 +
  123 +export function procurementPlanGetApi(id) {
  124 + return request({
  125 + url: '/procurement/plan/get',
  126 + method: 'get',
  127 + params: { id }
  128 + })
  129 +}
  130 +
  131 +export function procurementPlanGenerateContractApi(id) {
  132 + return request({
  133 + url: '/procurement/plan/generateContract?id=' + id,
  134 + method: 'post',
  135 + })
  136 +}
  137 +
  138 +export function procurementPlanSubmitAuditApi(id) {
  139 + return request({
  140 + url: '/procurement/plan/submitAudit?id=' + id,
  141 + method: 'post',
  142 + })
  143 +}
  144 +
  145 +export function procurementPlanSubmitApi(id) {
  146 + return request({
  147 + url: '/procurement/plan/submit?id=' + id,
  148 + method: 'post',
  149 + })
  150 +}
  151 +
  152 +export function procurementPlanStopApi(id) {
  153 + return request({
  154 + url: '/procurement/plan/stop?id=' + id,
  155 + method: 'post',
  156 + })
  157 +}
  158 +
  159 +export function procurementPlanChangeSubmitApi(id) {
  160 + return request({
  161 + url: '/procurement/plan/change/submit?id=' + id,
  162 + method: 'post',
  163 + })
  164 +}
  165 +
  166 +export function procurementPlanReplenishSubmitApi(id) {
  167 + return request({
  168 + url: '/procurement/plan/replenish/submit?id=' + id,
  169 + method: 'post',
  170 + })
  171 +}
  172 +
  173 +export function procurementPlanChangeCancelApi(id) {
  174 + return request({
  175 + url: '/procurement/plan/change/cancel?id=' + id,
  176 + method: 'post',
  177 + })
  178 +}
  179 +
  180 +
  181 +export function procurementPlanReplenishCancelApi(id) {
  182 + return request({
  183 + url: '/procurement/plan/replenish/cancel?id=' + id,
  184 + method: 'post',
  185 + })
  186 +}
\ No newline at end of file
... ...
... ... @@ -72,3 +72,12 @@ export function salesmanQuotationCancelApi(params) {
72 72 params
73 73 })
74 74 }
  75 +
  76 +// 生成计划
  77 +export function salesmanQuotationCreatePlanApi(params) {
  78 + return request({
  79 + url: '/procurement/salesmanQuotation/createPlan?id=' + params.id,
  80 + method: 'post',
  81 + params
  82 + })
  83 +}
... ...
... ... @@ -413,6 +413,110 @@
413 413 }
414 414 },
415 415 {
  416 + "path": "pages/domestic-plan/index",
  417 + "style": {
  418 + "navigationBarTitleText": "内贸计划单",
  419 + "navigationBarBackgroundColor": "#ffffff",
  420 + "navigationBarTextStyle": "black"
  421 + }
  422 + },
  423 + {
  424 + "path": "pages/domestic-plan/detail",
  425 + "style": {
  426 + "navigationBarTitleText": "内贸计划单详情",
  427 + "navigationBarBackgroundColor": "#ffffff",
  428 + "navigationBarTextStyle": "black"
  429 + }
  430 + },
  431 + {
  432 + "path": "pages/domestic-plan/supplement",
  433 + "style": {
  434 + "navigationBarTitleText": "内贸计划单补充",
  435 + "navigationBarBackgroundColor": "#ffffff",
  436 + "navigationBarTextStyle": "black"
  437 + }
  438 + },
  439 + {
  440 + "path": "pages/domestic-plan/selectWorkshop",
  441 + "style": {
  442 + "navigationBarTitleText": "选择分厂",
  443 + "navigationBarBackgroundColor": "#ffffff",
  444 + "navigationBarTextStyle": "black"
  445 + }
  446 + },
  447 + {
  448 + "path": "pages/domestic-plan/viewBuyoutApply",
  449 + "style": {
  450 + "navigationBarTitleText": "查看买断操作申请单",
  451 + "navigationBarBackgroundColor": "#ffffff",
  452 + "navigationBarTextStyle": "black"
  453 + }
  454 + },
  455 + {
  456 + "path": "pages/schedule-plan/index",
  457 + "style": {
  458 + "navigationBarTitleText": "计划变更单",
  459 + "navigationBarBackgroundColor": "#ffffff",
  460 + "navigationBarTextStyle": "black"
  461 + }
  462 + },
  463 + {
  464 + "path": "pages/schedule-plan/add",
  465 + "style": {
  466 + "navigationBarTitleText": "新增计划变更单",
  467 + "navigationBarBackgroundColor": "#ffffff",
  468 + "navigationBarTextStyle": "black"
  469 + }
  470 + },
  471 + {
  472 + "path": "pages/schedule-plan/detail",
  473 + "style": {
  474 + "navigationBarTitleText": "计划变更单详情",
  475 + "navigationBarBackgroundColor": "#ffffff",
  476 + "navigationBarTextStyle": "black"
  477 + }
  478 + },
  479 + {
  480 + "path": "pages/schedule-plan/modify",
  481 + "style": {
  482 + "navigationBarTitleText": "编辑计划变更单",
  483 + "navigationBarBackgroundColor": "#ffffff",
  484 + "navigationBarTextStyle": "black"
  485 + }
  486 + },
  487 + {
  488 + "path": "pages/supple-plan/index",
  489 + "style": {
  490 + "navigationBarTitleText": "计划补录单",
  491 + "navigationBarBackgroundColor": "#ffffff",
  492 + "navigationBarTextStyle": "black"
  493 + }
  494 + },
  495 + {
  496 + "path": "pages/supple-plan/add",
  497 + "style": {
  498 + "navigationBarTitleText": "新增计划补录单",
  499 + "navigationBarBackgroundColor": "#ffffff",
  500 + "navigationBarTextStyle": "black"
  501 + }
  502 + },
  503 + {
  504 + "path": "pages/supple-plan/modify",
  505 + "style": {
  506 + "navigationBarTitleText": "修改计划补录单",
  507 + "navigationBarBackgroundColor": "#ffffff",
  508 + "navigationBarTextStyle": "black"
  509 + }
  510 + },
  511 + {
  512 + "path": "pages/supple-plan/detail",
  513 + "style": {
  514 + "navigationBarTitleText": "计划补录单详情",
  515 + "navigationBarBackgroundColor": "#ffffff",
  516 + "navigationBarTextStyle": "black"
  517 + }
  518 + },
  519 + {
416 520 "path": "pages/order_list/index",
417 521 "style": {
418 522 "navigationBarTitleText": "订货单列表",
... ...
... ... @@ -893,4 +893,4 @@ export default {
893 893 height: 60rpx;
894 894 align-items: center;
895 895 }
896   -</style>
  896 +</style>
\ No newline at end of file
... ...
... ... @@ -875,4 +875,4 @@ export default {
875 875 height: 60rpx;
876 876 align-items: center;
877 877 }
878   -</style>
  878 +</style>
\ No newline at end of file
... ...
  1 +<template>
  2 + <view class="page">
  3 + <scroll-view class="scroll" scroll-y>
  4 + <view class="detail-page">
  5 + <view class="section">
  6 + <text class="row company">{{ safeText(form.code) }}</text>
  7 + <view class="row"><text class="label">公司简称</text><text class="value">{{ safeText(form.companyShortName || form.companyName) }}</text></view>
  8 + <view class="row"><text class="label">供应商名称</text><text class="value">{{ safeText(form.supplierName) }}</text></view>
  9 + <view class="row"><text class="label">采购处</text><text class="value">{{ safeText(form.purchaseDepartmentName || form.purchaseDepartment) }}</text></view>
  10 + <view class="row"><text class="label">区域</text><text class="value">{{ safeText(form.regionName || form.region) }}</text></view>
  11 + <view class="row"><text class="label">日期</text><text class="value">{{ formatDateOnly(form.date) }}</text></view>
  12 + <view class="row"><text class="label">报价时效</text><text class="value">{{ safeText(form.validityPeriod) }}</text></view>
  13 + <view class="row"><text class="label">报价人</text><text class="value">{{ safeText(form.quoterName) }}</text></view>
  14 + <view class="row"><text class="label">是否长单操作</text><text class="value">{{ boolText(form.longTermOperation) }}</text></view>
  15 + <view class="row"><text class="label">是否终端客户</text><text class="value">{{ boolText(form.terminalCustomer) }}</text></view>
  16 + <view class="row"><text class="label">是否未点价</text><text class="value">{{ boolText(form.priced) }}</text></view>
  17 + <view v-if="form.priced === true" class="row"><text class="label">点价截止日期</text><text class="value">{{ formatDateOnly(form.pricingDeadline) }}</text></view>
  18 + <view v-if="form.priced === true" class="row"><text class="label">是否暂定价</text><text class="value">{{ boolText(form.temporaryPrice) }}</text></view>
  19 + <view class="row"><text class="label">其他费用凭票到厂另行结算</text><text class="value">{{ boolText(form.otherExpenseSettlement) }}</text></view>
  20 + <view class="row"><text class="label">交提货方式</text><text class="value">{{ getDeliveryMethodLabel() }}</text></view>
  21 + <view class="row"><text class="label">是否通货买断</text><text class="value">{{ boolText(form.commodityBuyout) }}</text></view>
  22 + <view class="row"><text class="label">是否质量买断</text><text class="value">{{ boolText(form.qualityBuyout) }}</text></view>
  23 + <view class="row"><text class="label">是否数量买断</text><text class="value">{{ boolText(form.quantityBuyout) }}</text></view>
  24 + <view class="row"><text class="label">是否送货上门</text><text class="value">{{ boolText(form.doorDelivery) }}</text></view>
  25 + <view class="row"><text class="label">另付运费</text><text class="value">{{ formatNumber(form.shippingCost) }}</text></view>
  26 + <view class="row"><text class="label">装车费</text><text class="value">{{ formatNumber(form.loadingFee) }}</text></view>
  27 + <view class="row"><text class="label">开票品名</text><text class="value">{{ safeText(form.invoiceProductName) }}</text></view>
  28 + <view class="row"><text class="label">交货日期</text><text class="value">{{ formatDateOnly(form.deliveryDate) }}</text></view>
  29 + <view class="row"><text class="label">结算方式</text><text class="value">{{ safeText(form.settlementMethod) }}</text></view>
  30 + <view class="row"><text class="label">结算比例(%)</text><text class="value">{{ formatNumber(form.settlementRatio) }}</text></view>
  31 + <view class="row"><text class="label">价格表编号</text><text class="value">{{ getPriceTableLabel() }}</text></view>
  32 + <view class="row"><text class="label">协调事由</text><text class="value">{{ safeText(form.coordinationReason) }}</text></view>
  33 + <view class="row"><text class="label">票类状况</text><text class="value">{{ safeText(form.invoiceTypeStatus) }}</text></view>
  34 + <view class="row" v-if="attachmentList.length">
  35 + <text class="label">附件</text>
  36 + <view class="attach-list">
  37 + <view v-for="(at, idx) in attachmentList" :key="idx" class="attach-item">
  38 + <text class="attach-name">{{ at.name }}</text>
  39 + <text class="attach-view" @click.stop="onPreviewAttachment(at)">查看</text>
  40 + </view>
  41 + </view>
  42 + </view>
  43 + </view>
  44 +
  45 + <ProductRel
  46 + mode="view"
  47 + :list="lineListWithName"
  48 + :product-options="productOptions"
  49 + :workshop-options="workshopOptions"
  50 + />
  51 +
  52 + <view class="title-header">
  53 + <image class="title-header_icon" src="/static/images/title.png" />
  54 + <span>料质状况</span>
  55 + </view>
  56 + <view class="section">
  57 + <view class="row"><text class="label">是否启用料质状况</text><text class="value">{{ boolText(form.materialConditionEnabled) }}</text></view>
  58 + <template v-if="form.materialConditionEnabled">
  59 + <view v-for="mc in materialConditionConfigs" :key="mc.field" class="material-group">
  60 + <text class="material-label">{{ mc.label }}</text>
  61 + <view class="tag-list">
  62 + <view v-for="opt in getDictOptions(mc.dictCode)" :key="opt.code" class="tag-btn" :class="{ active: isTagSelected(mc.field, opt.code) }">{{ opt.name }}</view>
  63 + </view>
  64 + </view>
  65 + </template>
  66 + </view>
  67 +
  68 + <view class="title-header">
  69 + <image class="title-header_icon" src="/static/images/title.png" />
  70 + <span>备注</span>
  71 + </view>
  72 + <view class="section">
  73 + <text class="remark-text">{{ safeText(form.remark) }}</text>
  74 + </view>
  75 + </view>
  76 + </scroll-view>
  77 + <detail-buttons :buttons="displayButtons" @click="handleButtonClick" />
  78 + </view>
  79 +</template>
  80 +
  81 +<script>
  82 +import DetailButtons from '@/components/detail-buttons/index.vue'
  83 +import ProductRel from './productRel.vue'
  84 +import { procurementPlanDetailApi, procurementPlanGenerateContractApi, procurementPlanSubmitAuditApi, procurementPlanSubmitApi, procurementPlanStopApi } from '@/api/procure-manage/procurementPlan.js'
  85 +import { buyoutApplyCreateApi, buyoutApplyAbolishApi, buyoutApplyDeleteByPlanIdApi } from '@/api/procure-manage/buyout.js'
  86 +import { getDicByCodes, getDicName } from '@/utils/dic.js'
  87 +import { getInfo } from '@/api/login.js'
  88 +import { priceTableQueryApi, breedRelationshipQueryApi, workshopQueryApi } from '@/api/devManage.js'
  89 +
  90 +export default {
  91 + name: 'DomesticPlanDetail',
  92 + components: { DetailButtons, ProductRel },
  93 + data() {
  94 + return {
  95 + id: '',
  96 + form: {},
  97 + lineList: [],
  98 + attachmentList: [],
  99 + materialConditionConfigs: [
  100 + { field: 'materialConditionCopperRice', label: '铜米', dictCode: 'COPPER_RICE' },
  101 + { field: 'materialConditionBrightCopper', label: '光亮铜', dictCode: 'BRIGHT_COPPER' },
  102 + { field: 'materialConditionFirstClass', label: '一特', dictCode: 'FIRST_CLASS' },
  103 + { field: 'materialConditionSecondClass', label: '二特', dictCode: 'SECOND_CLASS' },
  104 + { field: 'materialConditionTinCoated', label: '镀锡紫', dictCode: 'TIN_COATED' },
  105 + { field: 'materialConditionScrap', label: '角料', dictCode: 'SCRAP' },
  106 + { field: 'materialConditionWhiteYellow', label: '镀白黄', dictCode: 'WHITE_YELLOW' },
  107 + { field: 'materialConditionPhosphorCopper', label: '磷铜', dictCode: 'PHOSPHOR_COPPER' }
  108 + ],
  109 + dicOptions: {
  110 + AUDIT_STATUS: [],
  111 + PROCESS_STATUS: [],
  112 + DELIVERY_METHOD: []
  113 + },
  114 + dictData: {},
  115 + currentUserId: '',
  116 + priceTableList: [],
  117 + productOptions: [],
  118 + workshopOptions: [],
  119 + planRoleFlags: {
  120 + isPlanner: false
  121 + },
  122 + PROCESS_STATUS_CODE: {
  123 + TO_SUPPLEMENT: 'TO_SUPPLEMENT',
  124 + TO_SPLIT: 'TO_SPLIT',
  125 + TO_SUBMIT_REVIEW: 'TO_SUBMIT_REVIEW',
  126 + AUDIT: 'AUDIT',
  127 + REFUSE: 'REFUSE',
  128 + COMPLETED: 'COMPLETED',
  129 + ABORTED: 'ABORTED'
  130 + },
  131 + buttonTemplates: [
  132 + { text: '补充', variant: 'primary', event: 'supplement' },
  133 + { text: '生成合同', variant: 'primary', event: 'generateContract' },
  134 + { text: '选择分厂', variant: 'primary', event: 'selectWorkshop' },
  135 + { text: '提交', variant: 'primary', event: 'submitSplit' },
  136 + { text: '提交审核', variant: 'primary', event: 'submitReview' },
  137 + { text: '终止', variant: 'danger', event: 'abort' },
  138 + { text: '审核', variant: 'primary', event: 'approve' },
  139 + { text: '审核详情', variant: 'outline', event: 'approveDetail' },
  140 + { text: '生成买断操作申请单', variant: 'outline', event: 'generate' },
  141 + { text: '废除买断操作申请单', variant: 'danger', event: 'cancel' },
  142 + { text: '查看买断操作申请单', variant: 'outline', event: 'look' },
  143 + // { text: '上传附件', variant: 'primary', event: 'upload' }
  144 + ]
  145 + }
  146 + },
  147 + computed: {
  148 + productMap() {
  149 + var list = Array.isArray(this.productOptions) ? this.productOptions : []
  150 + var m = {}
  151 + list.forEach(function (o) {
  152 + var key = String(o.productId || o.id || o.value || '')
  153 + if (key) m[key] = o
  154 + })
  155 + return m
  156 + },
  157 + workshopMap() {
  158 + var list = Array.isArray(this.workshopOptions) ? this.workshopOptions : []
  159 + var m = {}
  160 + list.forEach(function (o) {
  161 + var key = String(o.workshopId || o.id || o.value || '')
  162 + if (key) m[key] = o
  163 + })
  164 + return m
  165 + },
  166 + lineListWithName() {
  167 + var self = this
  168 + var list = Array.isArray(this.lineList) ? this.lineList : []
  169 + return list.map(function (row) {
  170 + var pid = String(row.productId || '')
  171 + var wid = String(row.workshopId || '')
  172 + var p = pid ? self.productMap[pid] : null
  173 + var w = wid ? self.workshopMap[wid] : null
  174 + return Object.assign({}, row, {
  175 + productName: row.productName || (p ? (p.productName || p.name || p.label || '') : ''),
  176 + workshopName: row.workshopName || (w ? (w.workshopName || w.name || w.label || '') : '')
  177 + })
  178 + })
  179 + },
  180 + displayButtons() {
  181 + var self = this
  182 + var f = this.form || {}
  183 + var ps = String(f.processStatus != null ? f.processStatus : '')
  184 + var aStatus = String(f.approvalStatus != null ? f.approvalStatus : '')
  185 + var SC = this.PROCESS_STATUS_CODE
  186 + var inStatus = function (codes) { return codes.indexOf(ps) >= 0 }
  187 + console.log(SC,inStatus([SC.TO_SUBMIT_REVIEW, SC.AUDIT, SC.COMPLETED, SC.REFUSE]),'SC')
  188 + var perms = {
  189 + supplement: this.hasPerm('plan-manage:domestic-plan:supple'),
  190 + generateContract: this.hasPerm('plan-manage:domestic-plan:do'),
  191 + selectWorkshop: this.hasPerm('plan-manage:domestic-plan:split'),
  192 + submitSplit: this.hasPerm('plan-manage:domestic-plan:submit'),
  193 + submitReview: this.hasPerm('plan-manage:domestic-plan:goapprove'),
  194 + abort: this.hasPerm('plan-manage:domestic-plan:stop'),
  195 + approve: this.hasPerm('plan-manage:domestic-plan:approve'),
  196 + approveDetail: this.hasPerm('plan-manage:domestic-plan:view'),
  197 + print: this.hasPerm('plan-manage:domestic-plan:print'),
  198 + generate: this.hasPerm('plan-manage:domestic-plan:generate'),
  199 + cancel: this.hasPerm('plan-manage:domestic-plan:cancel'),
  200 + look: this.hasPerm('plan-manage:domestic-plan:look'),
  201 + // upload: this.hasPerm('plan-manage:domestic-plan:upload')
  202 + }
  203 + var visibleMap = {
  204 + supplement: inStatus([SC.TO_SUPPLEMENT]) && perms.supplement,
  205 + generateContract: inStatus([SC.TO_SUPPLEMENT]) && perms.generateContract,
  206 + selectWorkshop: inStatus([SC.TO_SPLIT]) && perms.selectWorkshop,
  207 + submitSplit: inStatus([SC.TO_SPLIT]) && perms.submitSplit,
  208 + submitReview: inStatus([SC.TO_SUBMIT_REVIEW]) && perms.submitReview,
  209 + abort: inStatus([SC.TO_SUBMIT_REVIEW, SC.AUDIT, SC.REFUSE]) && perms.abort,
  210 + approve: (inStatus([SC.AUDIT, SC.REFUSE]) && aStatus === 'AUDIT' && !!f.showExamine) && perms.approve,
  211 + approveDetail: !!f.showExamineDetail && (inStatus([SC.AUDIT, SC.COMPLETED, SC.REFUSE])) && perms.approveDetail,
  212 + print: inStatus([SC.TO_SUBMIT_REVIEW, SC.AUDIT, SC.COMPLETED, SC.ABORTED, SC.REFUSE]) && perms.print,
  213 + generate: !!f.showGenerateBuyoutApply && inStatus([SC.TO_SUBMIT_REVIEW, SC.AUDIT, SC.COMPLETED]) && perms.generate,
  214 + cancel: !!f.showAbolishBuyoutApply && inStatus([SC.TO_SUBMIT_REVIEW, SC.AUDIT, SC.COMPLETED]) && perms.cancel,
  215 + look: inStatus([SC.TO_SUBMIT_REVIEW, SC.AUDIT, SC.COMPLETED, SC.REFUSE, SC.ABORTED]) && perms.look,
  216 + // uploadAttachment: inStatus([SC.COMPLETED]) && !!this.planRoleFlags.isPlanner && perms.uploadAttachment
  217 + }
  218 + var out = []
  219 + for (var i = 0; i < this.buttonTemplates.length; i++) {
  220 + var tpl = this.buttonTemplates[i]
  221 + var visible = !!visibleMap[tpl.event]
  222 + if (visible) {
  223 + out.push(Object.assign({}, tpl))
  224 + }
  225 + }
  226 + return out
  227 + }
  228 + },
  229 + onLoad(query) {
  230 + this.id = (query && query.id) || ''
  231 + this.loadDictData()
  232 + this.loadRefData()
  233 + this.getUserInfo()
  234 + this.loadDetail()
  235 + },
  236 + onShow() {
  237 + var needRefresh = ''
  238 + try { needRefresh = uni.getStorageSync('DOMESTIC_PLAN_DETAIL_NEED_REFRESH') } catch (e) {}
  239 + if (!needRefresh) return
  240 + try { uni.removeStorageSync('DOMESTIC_PLAN_DETAIL_NEED_REFRESH') } catch (e) {}
  241 + this.loadDetail()
  242 + },
  243 + methods: {
  244 + hasPerm(code) {
  245 + try {
  246 + return !!(this.$auth && this.$auth.hasPermi && this.$auth.hasPermi(code))
  247 + } catch (e) {
  248 + return true
  249 + }
  250 + },
  251 + async getUserInfo() {
  252 + try {
  253 + var res = await getInfo()
  254 + this.currentUserId = (res && res.id) ? res.id : ''
  255 + } catch (e) {
  256 + this.currentUserId = ''
  257 + }
  258 + },
  259 + async loadDictData() {
  260 + var codes = [].concat(
  261 + this.materialConditionConfigs.map(function (mc) { return mc.dictCode }),
  262 + ['DELIVERY_METHOD', 'AUDIT_STATUS', 'PROCESS_STATUS']
  263 + )
  264 + try {
  265 + var results = await getDicByCodes(codes)
  266 + var d = results || {}
  267 + this.dictData = d
  268 + this.dicOptions.AUDIT_STATUS = (d.AUDIT_STATUS && d.AUDIT_STATUS.data) || []
  269 + this.dicOptions.PROCESS_STATUS = (d.PROCESS_STATUS && d.PROCESS_STATUS.data) || []
  270 + this.dicOptions.DELIVERY_METHOD = (d.DELIVERY_METHOD && d.DELIVERY_METHOD.data) || []
  271 + } catch (e) {
  272 + this.dictData = {}
  273 + }
  274 + },
  275 + getDictOptions(dictCode) {
  276 + var data = this.dictData[dictCode]
  277 + return (data && data.data) ? data.data : []
  278 + },
  279 + isTagSelected(field, code) {
  280 + var selected = this.form ? this.form[field] : undefined
  281 + if (Array.isArray(selected)) return selected.indexOf(code) >= 0
  282 + if (typeof selected === 'string' && selected) {
  283 + return selected.split(',').indexOf(code) >= 0
  284 + }
  285 + return false
  286 + },
  287 + getAuditStatusText(status) {
  288 + var v = status == null ? '' : String(status)
  289 + if (!v) return '-'
  290 + return getDicName('AUDIT_STATUS', v, this.dicOptions.AUDIT_STATUS) || v
  291 + },
  292 + getProcessStatusText(status) {
  293 + var v = status == null ? '' : String(status)
  294 + if (!v) return '-'
  295 + return getDicName('PROCESS_STATUS', v, this.dicOptions.PROCESS_STATUS) || v
  296 + },
  297 + getDeliveryMethodLabel() {
  298 + var opts = this.getDictOptions('DELIVERY_METHOD') || []
  299 + var v = String(this.form.deliveryMode != null ? this.form.deliveryMode : '')
  300 + if (!v) return '-'
  301 + for (var i = 0; i < opts.length; i++) {
  302 + var o = opts[i]
  303 + var code = o.code != null ? String(o.code) : (o.value != null ? String(o.value) : '')
  304 + if (code === v) return o.name || o.label || '-'
  305 + }
  306 + return v
  307 + },
  308 + getPriceTableLabel() {
  309 + var code = this.form.priceTableCode != null ? this.form.priceTableCode : (this.form.priceId != null ? this.form.priceId : '')
  310 + if (code == null || String(code).trim() === '') return '-'
  311 + var sc = String(code)
  312 + var list = Array.isArray(this.priceTableList) ? this.priceTableList : []
  313 + for (var i = 0; i < list.length; i++) {
  314 + var o = list[i]
  315 + var id = String(o.id || o.priceId || o.value || '')
  316 + var c = String(o.priceTableCode || o.code || o.priceCode || '')
  317 + if (id === sc || c === sc) {
  318 + var name = o.priceTableName || o.tableName || o.name || o.label || ''
  319 + var cd = o.priceTableCode || o.code || c
  320 + return (name ? name + ' ' : '') + (cd ? cd : '') || '-'
  321 + }
  322 + }
  323 + return sc
  324 + },
  325 + async loadRefData() {
  326 + var self = this
  327 + try {
  328 + var results = await Promise.all([
  329 + this.pCall(priceTableQueryApi, { pageIndex: 1, pageSize: 1000 }),
  330 + this.pCall(breedRelationshipQueryApi, { pageIndex: 1, pageSize: 1000 }),
  331 + this.pCall(workshopQueryApi, { pageIndex: 1, pageSize: 1000 })
  332 + ])
  333 + var r1 = (results[0] && results[0].data) ? results[0].data : {}
  334 + var r2 = (results[1] && results[1].data) ? results[1].data : {}
  335 + var r3 = (results[2] && results[2].data) ? results[2].data : {}
  336 + self.priceTableList = (r1.datas && Array.isArray(r1.datas)) ? r1.datas : ((r1.list && Array.isArray(r1.list)) ? r1.list : ((r1.records && Array.isArray(r1.records)) ? r1.records : []))
  337 + self.productOptions = (r2.datas && Array.isArray(r2.datas)) ? r2.datas : ((r2.list && Array.isArray(r2.list)) ? r2.list : ((r2.records && Array.isArray(r2.records)) ? r2.records : []))
  338 + self.workshopOptions = (r3.datas && Array.isArray(r3.datas)) ? r3.datas : ((r3.list && Array.isArray(r3.list)) ? r3.list : ((r3.records && Array.isArray(r3.records)) ? r3.records : []))
  339 + } catch (e) {
  340 + self.priceTableList = []
  341 + self.productOptions = []
  342 + self.workshopOptions = []
  343 + }
  344 + },
  345 + pCall(fn, params) {
  346 + try {
  347 + return Promise.resolve(fn(params))
  348 + } catch (e) {
  349 + return Promise.reject(e)
  350 + }
  351 + },
  352 + boolText(v) {
  353 + if (typeof v !== 'boolean') return '-'
  354 + return v ? '是' : '否'
  355 + },
  356 + formatDateOnly(v) {
  357 + var s = v == null ? '' : String(v).trim()
  358 + if (!s) return '-'
  359 + if (s.length >= 10) return s.slice(0, 10)
  360 + return s
  361 + },
  362 + safeText(v) {
  363 + var s = v == null ? '' : String(v)
  364 + return s.trim() ? s : '-'
  365 + },
  366 + formatNumber(v) {
  367 + if (v == null || v === '') return '-'
  368 + var n = Number(v)
  369 + if (isNaN(n)) return String(v)
  370 + return n.toLocaleString('zh-CN', { maximumFractionDigits: 4 })
  371 + },
  372 + buildAttachmentList() {
  373 + var ids = this.form.attachmentFileIds
  374 + var names = this.form.attachmentFileNames
  375 + var idArr = (typeof ids === 'string' && ids) ? ids.split(',').filter(Boolean) : (Array.isArray(ids) ? ids : [])
  376 + var nameArr = (typeof names === 'string' && names) ? names.split(',').filter(Boolean) : (Array.isArray(names) ? names : [])
  377 + var len = Math.max(idArr.length, nameArr.length)
  378 + var list = []
  379 + for (var i = 0; i < len; i++) {
  380 + list.push({ id: idArr[i] || '', name: nameArr[i] || ('附件' + (i + 1)) })
  381 + }
  382 + this.attachmentList = list
  383 + },
  384 + async loadDetail() {
  385 + if (!this.id) return
  386 + try {
  387 + var res = await procurementPlanDetailApi({ id: this.id })
  388 + var d = (res && res.data) ? res.data : {}
  389 + var self = this
  390 + this.materialConditionConfigs.forEach(function (mc) {
  391 + var v = d[mc.field]
  392 + if (typeof v === 'string' && v) {
  393 + d[mc.field] = v.split(',').filter(Boolean)
  394 + } else if (!Array.isArray(v)) {
  395 + d[mc.field] = []
  396 + }
  397 + })
  398 + this.form = d
  399 + this.lineList = Array.isArray(d.procurementPlanLineList) ? d.procurementPlanLineList : []
  400 + this.buildAttachmentList()
  401 + } catch (e) {
  402 + uni.showToast({ title: '加载详情失败', icon: 'none' })
  403 + }
  404 + },
  405 + onPreviewAttachment(at) {
  406 + if (!at || !at.id) {
  407 + uni.showToast({ title: '无法查看', icon: 'none' })
  408 + return
  409 + }
  410 + uni.showToast({ title: '查看附件 ' + at.name, icon: 'none' })
  411 + },
  412 + handleButtonClick(btn) {
  413 + if (!btn || btn.disabled) return
  414 + var e = btn.event || ''
  415 + if (e === 'view') return this.onView()
  416 + if (e === 'supplement') return this.onSupplement()
  417 + if (e === 'generateContract') return this.onGenerateContract()
  418 + if (e === 'selectWorkshop') return this.onSelectWorkshop()
  419 + if (e === 'submitSplit') return this.onSubmitSplit()
  420 + if (e === 'submitReview') return this.onSubmitReview()
  421 + if (e === 'abort') return this.onAbort()
  422 + if (e === 'approve') return this.onApprove()
  423 + if (e === 'approveDetail') return this.onApproveDetail()
  424 + if (e === 'print') return this.onPrint()
  425 + if (e === 'generateBuyoutApply') return this.onGenerateBuyoutApply()
  426 + if (e === 'cancel') return this.onAbolishBuyoutApply()
  427 + if (e === 'look') return this.onViewBuyoutApply()
  428 + if (e === 'uploadAttachment') return this.onUploadAttachment()
  429 + },
  430 +
  431 + onView() {
  432 + uni.showToast({ title: '查看', icon: 'none' })
  433 + },
  434 + onSupplement() {
  435 + uni.navigateTo({ url: '/pages/domestic-plan/supplement?id=' + this.id })
  436 + },
  437 + onGenerateContract() {
  438 + var self = this
  439 + var id = this.id || (this.form && this.form.id) || ''
  440 + if (!id) {
  441 + uni.showToast({ title: '缺少单据id', icon: 'none' })
  442 + return
  443 + }
  444 + uni.showModal({
  445 + title: '生成合同',
  446 + content: '合同生成之后无法再编辑,确定生成吗?',
  447 + confirmText: '确定',
  448 + cancelText: '取消',
  449 + success: function (res) {
  450 + if (!res || !res.confirm) return
  451 + uni.showLoading({ title: '生成中', mask: true })
  452 + procurementPlanGenerateContractApi(id).then(function () {
  453 + uni.hideLoading()
  454 + uni.showToast({ title: '生成成功', icon: 'none' })
  455 + try {
  456 + uni.setStorageSync('DOMESTIC_PLAN_LIST_NEED_REFRESH', '1')
  457 + uni.setStorageSync('SUPPLE_PLAN_LIST_NEED_REFRESH', '1')
  458 + } catch (e) {}
  459 + self.loadDetail()
  460 + }).catch(function (e) {
  461 + uni.hideLoading()
  462 + uni.showToast({ title: (e && (e.msg || e.message)) ? (e.msg || e.message) : '生成合同失败', icon: 'none' })
  463 + })
  464 + }
  465 + })
  466 + },
  467 + refreshListFlags() {
  468 + try {
  469 + uni.setStorageSync('DOMESTIC_PLAN_LIST_NEED_REFRESH', '1')
  470 + uni.setStorageSync('SUPPLE_PLAN_LIST_NEED_REFRESH', '1')
  471 + } catch (e) {}
  472 + },
  473 + runWithConfirm(opts) {
  474 + var self = this
  475 + var title = opts.title || '提示'
  476 + var content = opts.content || ''
  477 + var run = opts.run
  478 + var loading = opts.loading || '处理中'
  479 + var successText = opts.successText || '操作成功'
  480 + var failText = opts.failText || '操作失败'
  481 + if (!this.id) {
  482 + uni.showToast({ title: '缺少单据id', icon: 'none' })
  483 + return
  484 + }
  485 + var apiFn = function () {
  486 + uni.showLoading({ title: loading, mask: true })
  487 + Promise.resolve(run(self.id)).then(function () {
  488 + uni.hideLoading()
  489 + uni.showToast({ title: successText, icon: 'none' })
  490 + self.refreshListFlags()
  491 + self.loadDetail()
  492 + }).catch(function (e) {
  493 + uni.hideLoading()
  494 + uni.showToast({ title: (e && (e.msg || e.message)) ? (e.msg || e.message) : failText, icon: 'none' })
  495 + })
  496 + }
  497 + if (opts.skipConfirm) {
  498 + apiFn()
  499 + return
  500 + }
  501 + uni.showModal({
  502 + title: title,
  503 + content: content,
  504 + confirmText: '确定',
  505 + cancelText: '取消',
  506 + success: function (res) {
  507 + if (!res || !res.confirm) return
  508 + apiFn()
  509 + }
  510 + })
  511 + },
  512 + onSelectWorkshop() {
  513 + var id = this.id || (this.form && this.form.id) || ''
  514 + if (!id) {
  515 + uni.showToast({ title: '缺少单据id', icon: 'none' })
  516 + return
  517 + }
  518 + uni.navigateTo({ url: '/pages/domestic-plan/selectWorkshop?id=' + id })
  519 + },
  520 + onSubmitSplit() {
  521 + this.runWithConfirm({
  522 + title: '提交',
  523 + content: '提交之后无法再选择分厂,确定提交吗?',
  524 + loading: '提交中',
  525 + successText: '提交成功',
  526 + failText: '提交失败',
  527 + run: function (id) { return procurementPlanSubmitApi(id) }
  528 + })
  529 + },
  530 + onSubmitReview() {
  531 + this.runWithConfirm({
  532 + title: '提交审核',
  533 + content: '确认提交审核吗?',
  534 + loading: '提交中',
  535 + successText: '提交成功',
  536 + failText: '提交审核失败',
  537 + run: function (id) { return procurementPlanSubmitAuditApi(id) }
  538 + })
  539 + },
  540 + onAbort() {
  541 + this.runWithConfirm({
  542 + title: '中止',
  543 + content: '若有关联计划,则关联计划会受到影响,是否确认中止?',
  544 + loading: '中止中',
  545 + successText: '中止成功',
  546 + failText: '中止失败',
  547 + run: function (id) { return procurementPlanStopApi(id) }
  548 + })
  549 + },
  550 + onPrint() {
  551 + uni.showToast({ title: '打印(待对接)', icon: 'none' })
  552 + },
  553 + onGenerateBuyoutApply() {
  554 + var self = this
  555 + var id = this.id || (this.form && this.form.id) || ''
  556 + this.runWithConfirm({
  557 + title: '生成买断操作申请单',
  558 + content: '确定生成买断操作申请单吗?',
  559 + loading: '生成中',
  560 + successText: '生成成功',
  561 + failText: '生成买断操作申请单失败',
  562 + run: function () { return buyoutApplyCreateApi({ procurementPlanId: id }) }
  563 + })
  564 + },
  565 + onAbolishBuyoutApply() {
  566 + var self = this
  567 + var id = this.id || (this.form && this.form.id) || ''
  568 + this.runWithConfirm({
  569 + title: '废除买断操作申请单',
  570 + content: '确定废除买断操作申请单吗?',
  571 + loading: '废除中',
  572 + successText: '废除成功',
  573 + failText: '废除买断操作申请单失败',
  574 + run: function () { return buyoutApplyDeleteByPlanIdApi(id) }
  575 + })
  576 + },
  577 + onViewBuyoutApply() {
  578 + var id = this.id || (this.form && this.form.id) || ''
  579 + if (!id) {
  580 + uni.showToast({ title: '缺少单据id', icon: 'none' })
  581 + return
  582 + }
  583 + uni.navigateTo({ url: '/pages/domestic-plan/viewBuyoutApply?planId=' + id })
  584 + },
  585 + onUploadAttachment() {
  586 + uni.showToast({ title: '上传附件(待对接)', icon: 'none' })
  587 + },
  588 + onApprove() {
  589 + var id = this.id || (this.form && this.form.id) || ''
  590 + if (!id) return
  591 + try {
  592 + uni.setStorageSync('contractType', 'PROCUREMENT_PLAN')
  593 + uni.setStorageSync('sourceBusinessId', id)
  594 + } catch (e) {}
  595 + uni.navigateTo({ url: '/pages/flow/audit' })
  596 + },
  597 + onApproveDetail() {
  598 + var id = this.id || (this.form && this.form.id) || ''
  599 + if (!id) return
  600 + try {
  601 + uni.setStorageSync('contractType', 'PROCUREMENT_PLAN')
  602 + uni.setStorageSync('sourceBusinessId', id)
  603 + } catch (e) {}
  604 + uni.navigateTo({ url: '/pages/flow/audit_detail' })
  605 + }
  606 + }
  607 +}
  608 +</script>
  609 +
  610 +<style lang="scss" scoped>
  611 +.page {
  612 + display: flex;
  613 + flex-direction: column;
  614 + height: 100%;
  615 +}
  616 +.scroll {
  617 + flex: 1;
  618 + padding: 8rpx 0 144rpx;
  619 +}
  620 +.detail-page {
  621 + background: #f3f3f3;
  622 +}
  623 +.title-header {
  624 + display: flex;
  625 + align-items: center;
  626 + padding: 24rpx 32rpx 12rpx;
  627 + background-color: #ffffff;
  628 + .title-header_icon {
  629 + width: 36rpx;
  630 + height: 36rpx;
  631 + margin-right: 12rpx;
  632 + }
  633 + span {
  634 + font-size: 34rpx;
  635 + font-weight: 600;
  636 + color: rgba(0, 0, 0, 0.9);
  637 + }
  638 +}
  639 +.section {
  640 + padding: 32rpx;
  641 + background: #fff;
  642 + margin-bottom: 20rpx;
  643 +}
  644 +.row {
  645 + display: flex;
  646 + margin-bottom: 28rpx;
  647 + &:last-child { margin-bottom: 0; }
  648 + &.company {
  649 + font-size: 36rpx;
  650 + font-weight: 600;
  651 + color: rgba(0, 0, 0, 0.9);
  652 + padding-top: 8rpx;
  653 + line-height: 50rpx;
  654 + }
  655 + .label {
  656 + max-width: 420rpx;
  657 + margin-right: 20rpx;
  658 + line-height: 32rpx;
  659 + font-size: 28rpx;
  660 + color: rgba(0, 0, 0, 0.6);
  661 + }
  662 + .value {
  663 + flex: 1;
  664 + line-height: 32rpx;
  665 + font-size: 28rpx;
  666 + color: rgba(0, 0, 0, 0.9);
  667 + text-align: right;
  668 + word-break: break-all;
  669 + }
  670 +}
  671 +.attach-list {
  672 + flex: 1;
  673 + display: flex;
  674 + flex-direction: column;
  675 + align-items: flex-end;
  676 + gap: 8rpx;
  677 +}
  678 +.attach-item {
  679 + display: flex;
  680 + align-items: center;
  681 + gap: 16rpx;
  682 + .attach-name {
  683 + font-size: 26rpx;
  684 + color: rgba(0, 0, 0, 0.9);
  685 + }
  686 + .attach-view {
  687 + font-size: 26rpx;
  688 + color: $theme-primary;
  689 + }
  690 +}
  691 +.material-group {
  692 + margin-top: 32rpx;
  693 + &:first-child { margin-top: 0; }
  694 +}
  695 +.material-label {
  696 + display: block;
  697 + font-size: 28rpx;
  698 + color: rgba(0, 0, 0, 0.6);
  699 + margin-bottom: 20rpx;
  700 +}
  701 +.tag-list {
  702 + display: flex;
  703 + flex-wrap: wrap;
  704 + gap: 16rpx;
  705 +}
  706 +.tag-btn {
  707 + padding: 12rpx 24rpx;
  708 + border: 1rpx solid #ddd;
  709 + border-radius: 8rpx;
  710 + font-size: 28rpx;
  711 + color: #999;
  712 + background: #f5f5f5;
  713 +}
  714 +.tag-btn.active {
  715 + background: $theme-primary;
  716 + border-color: $theme-primary;
  717 + color: #fff;
  718 +}
  719 +.remark-text {
  720 + font-size: 28rpx;
  721 + color: rgba(0, 0, 0, 0.9);
  722 + line-height: 1.6;
  723 + white-space: pre-wrap;
  724 +}
  725 +.empty-tip {
  726 + text-align: center;
  727 + font-size: 28rpx;
  728 + color: rgba(0, 0, 0, 0.4);
  729 + padding: 24rpx 0;
  730 +}
  731 +</style>
... ...
  1 +<template>
  2 + <view class="detail-page">
  3 + <scroll-view class="scroll" scroll-y>
  4 + <view class="section">
  5 + <text class="row company">{{ safeText(form.code) }}</text>
  6 + <view class="row"><text class="label">公司简称</text><text class="value">{{ safeText(form.companyShortName || form.companyName) }}</text></view>
  7 + <view class="row"><text class="label">供应商名称</text><text class="value">{{ safeText(form.supplierName) }}</text></view>
  8 + <view class="row"><text class="label">采购处</text><text class="value">{{ safeText(form.purchaseDepartmentName || form.purchaseDepartment) }}</text></view>
  9 + <view class="row"><text class="label">区域</text><text class="value">{{ safeText(form.regionName || form.region) }}</text></view>
  10 + <view class="row"><text class="label">日期</text><text class="value">{{ formatDateOnly(form.date) }}</text></view>
  11 + <view class="row"><text class="label">报价时效</text><text class="value">{{ safeText(form.validityPeriod) }}</text></view>
  12 + <view class="row"><text class="label">报价人</text><text class="value">{{ safeText(form.quoterName) }}</text></view>
  13 + <view class="row"><text class="label">是否长单操作</text><text class="value">{{ boolText(form.longTermOperation) }}</text></view>
  14 + <view class="row"><text class="label">是否终端客户</text><text class="value">{{ boolText(form.terminalCustomer) }}</text></view>
  15 + <view class="row"><text class="label">是否未点价</text><text class="value">{{ boolText(form.priced) }}</text></view>
  16 + <view v-if="form.priced === true" class="row"><text class="label">点价截止日期</text><text class="value">{{ formatDateOnly(form.pricingDeadline) }}</text></view>
  17 + <view v-if="form.priced === true" class="row"><text class="label">是否暂定价</text><text class="value">{{ boolText(form.temporaryPrice) }}</text></view>
  18 + <view class="row"><text class="label">其他费用凭票到厂另行结算</text><text class="value">{{ boolText(form.otherExpenseSettlement) }}</text></view>
  19 + <view class="row"><text class="label">交提货方式</text><text class="value">{{ getDeliveryMethodLabel() }}</text></view>
  20 + <view class="row"><text class="label">是否通货买断</text><text class="value">{{ boolText(form.commodityBuyout) }}</text></view>
  21 + <view class="row"><text class="label">是否质量买断</text><text class="value">{{ boolText(form.qualityBuyout) }}</text></view>
  22 + <view class="row"><text class="label">是否数量买断</text><text class="value">{{ boolText(form.quantityBuyout) }}</text></view>
  23 + <view class="row"><text class="label">是否送货上门</text><text class="value">{{ boolText(form.doorDelivery) }}</text></view>
  24 + <view class="row"><text class="label">另付运费</text><text class="value">{{ formatNumber(form.shippingCost) }}</text></view>
  25 + <view class="row"><text class="label">装车费</text><text class="value">{{ formatNumber(form.loadingFee) }}</text></view>
  26 + <view class="row"><text class="label">开票品名</text><text class="value">{{ safeText(form.invoiceProductName) }}</text></view>
  27 + <view class="row"><text class="label">交货日期</text><text class="value">{{ formatDateOnly(form.deliveryDate) }}</text></view>
  28 + <view class="row"><text class="label">结算方式</text><text class="value">{{ safeText(form.settlementMethod) }}</text></view>
  29 + <view class="row"><text class="label">结算比例(%)</text><text class="value">{{ formatNumber(form.settlementRatio) }}</text></view>
  30 + <view class="row"><text class="label">价格表编号</text><text class="value">{{ getPriceTableLabel() }}</text></view>
  31 + <view class="row"><text class="label">协调事由</text><text class="value">{{ safeText(form.coordinationReason) }}</text></view>
  32 + <view class="row"><text class="label">票类状况</text><text class="value">{{ safeText(form.invoiceTypeStatus) }}</text></view>
  33 + <view class="row" v-if="attachmentList.length">
  34 + <text class="label">附件</text>
  35 + <view class="attach-list">
  36 + <view v-for="(at, idx) in attachmentList" :key="idx" class="attach-item">
  37 + <text class="attach-name">{{ at.name }}</text>
  38 + <text class="attach-view" @click.stop="onPreviewAttachment(at)">查看</text>
  39 + </view>
  40 + </view>
  41 + </view>
  42 + </view>
  43 +
  44 + <ProductRel
  45 + mode="view"
  46 + :list="lineListWithName"
  47 + :product-options="productOptions"
  48 + :workshop-options="workshopOptions"
  49 + />
  50 +
  51 + <view class="title-header">
  52 + <image class="title-header_icon" src="/static/images/title.png" />
  53 + <span>料质状况</span>
  54 + </view>
  55 + <view class="section">
  56 + <view class="row"><text class="label">是否启用料质状况</text><text class="value">{{ boolText(form.materialConditionEnabled) }}</text></view>
  57 + <template v-if="form.materialConditionEnabled">
  58 + <view v-for="mc in materialConditionConfigs" :key="mc.field" class="material-group">
  59 + <text class="material-label">{{ mc.label }}</text>
  60 + <view class="tag-list">
  61 + <view v-for="opt in getDictOptions(mc.dictCode)" :key="opt.code" class="tag-btn" :class="{ active: isTagSelected(mc.field, opt.code) }">{{ opt.name }}</view>
  62 + </view>
  63 + </view>
  64 + </template>
  65 + </view>
  66 +
  67 + <view class="title-header">
  68 + <image class="title-header_icon" src="/static/images/title.png" />
  69 + <span>备注</span>
  70 + </view>
  71 + <view class="section">
  72 + <text class="remark-text">{{ safeText(form.remark) }}</text>
  73 + </view>
  74 + </scroll-view>
  75 + </view>
  76 +</template>
  77 +
  78 +<script>
  79 +import ProductRel from './productRel.vue'
  80 +import { procurementPlanDetailApi } from '@/api/procure-manage/procurementPlan.js'
  81 +import { getDicByCodes, getDicName } from '@/utils/dic.js'
  82 +import { priceTableQueryApi, breedRelationshipQueryApi, workshopQueryApi } from '@/api/devManage.js'
  83 +
  84 +export default {
  85 + name: 'DomesticPlanViewer',
  86 + components: { ProductRel },
  87 + props: {
  88 + id: { type: [String, Number], default: '' }
  89 + },
  90 + data() {
  91 + return {
  92 + form: {},
  93 + lineList: [],
  94 + attachmentList: [],
  95 + materialConditionConfigs: [
  96 + { field: 'materialConditionCopperRice', label: '铜米', dictCode: 'COPPER_RICE' },
  97 + { field: 'materialConditionBrightCopper', label: '光亮铜', dictCode: 'BRIGHT_COPPER' },
  98 + { field: 'materialConditionFirstClass', label: '一特', dictCode: 'FIRST_CLASS' },
  99 + { field: 'materialConditionSecondClass', label: '二特', dictCode: 'SECOND_CLASS' },
  100 + { field: 'materialConditionTinCoated', label: '镀锡紫', dictCode: 'TIN_COATED' },
  101 + { field: 'materialConditionScrap', label: '角料', dictCode: 'SCRAP' },
  102 + { field: 'materialConditionWhiteYellow', label: '镀白黄', dictCode: 'WHITE_YELLOW' },
  103 + { field: 'materialConditionPhosphorCopper', label: '磷铜', dictCode: 'PHOSPHOR_COPPER' }
  104 + ],
  105 + dicOptions: {
  106 + AUDIT_STATUS: [],
  107 + PROCESS_STATUS: [],
  108 + DELIVERY_METHOD: []
  109 + },
  110 + dictData: {},
  111 + priceTableList: [],
  112 + productOptions: [],
  113 + workshopOptions: []
  114 + }
  115 + },
  116 + computed: {
  117 + productMap() {
  118 + var list = Array.isArray(this.productOptions) ? this.productOptions : []
  119 + var m = {}
  120 + list.forEach(function (o) {
  121 + var key = String(o.productId || o.id || o.value || '')
  122 + if (key) m[key] = o
  123 + })
  124 + return m
  125 + },
  126 + workshopMap() {
  127 + var list = Array.isArray(this.workshopOptions) ? this.workshopOptions : []
  128 + var m = {}
  129 + list.forEach(function (o) {
  130 + var key = String(o.workshopId || o.id || o.value || '')
  131 + if (key) m[key] = o
  132 + })
  133 + return m
  134 + },
  135 + lineListWithName() {
  136 + var self = this
  137 + var list = Array.isArray(this.lineList) ? this.lineList : []
  138 + return list.map(function (row) {
  139 + var pid = String(row.productId || '')
  140 + var wid = String(row.workshopId || '')
  141 + var p = pid ? self.productMap[pid] : null
  142 + var w = wid ? self.workshopMap[wid] : null
  143 + return Object.assign({}, row, {
  144 + productName: row.productName || (p ? (p.productName || p.name || p.label || '') : ''),
  145 + workshopName: row.workshopName || (w ? (w.workshopName || w.name || w.label || '') : '')
  146 + })
  147 + })
  148 + }
  149 + },
  150 + created() {
  151 + this.loadDictData()
  152 + this.loadRefData()
  153 + },
  154 + watch: {
  155 + id: {
  156 + immediate: true,
  157 + handler(val) {
  158 + var v = (val !== undefined && val !== null) ? String(val) : ''
  159 + if (v) this.loadDetail(v)
  160 + }
  161 + }
  162 + },
  163 + methods: {
  164 + async loadDictData() {
  165 + var self = this
  166 + var codes = [].concat(
  167 + this.materialConditionConfigs.map(function (mc) { return mc.dictCode }),
  168 + ['DELIVERY_METHOD', 'AUDIT_STATUS', 'PROCESS_STATUS']
  169 + )
  170 + try {
  171 + var results = await getDicByCodes(codes)
  172 + var d = results || {}
  173 + this.dictData = d
  174 + this.dicOptions.AUDIT_STATUS = (d.AUDIT_STATUS && d.AUDIT_STATUS.data) || []
  175 + this.dicOptions.PROCESS_STATUS = (d.PROCESS_STATUS && d.PROCESS_STATUS.data) || []
  176 + this.dicOptions.DELIVERY_METHOD = (d.DELIVERY_METHOD && d.DELIVERY_METHOD.data) || []
  177 + } catch (e) {
  178 + this.dictData = {}
  179 + }
  180 + },
  181 + getDictOptions(dictCode) {
  182 + var data = this.dictData[dictCode]
  183 + return (data && data.data) ? data.data : []
  184 + },
  185 + isTagSelected(field, code) {
  186 + var selected = this.form ? this.form[field] : undefined
  187 + if (Array.isArray(selected)) return selected.indexOf(code) >= 0
  188 + if (typeof selected === 'string' && selected) {
  189 + return selected.split(',').indexOf(code) >= 0
  190 + }
  191 + return false
  192 + },
  193 + getAuditStatusText(status) {
  194 + var v = status == null ? '' : String(status)
  195 + if (!v) return '-'
  196 + return getDicName('AUDIT_STATUS', v, this.dicOptions.AUDIT_STATUS) || v
  197 + },
  198 + getProcessStatusText(status) {
  199 + var v = status == null ? '' : String(status)
  200 + if (!v) return '-'
  201 + return getDicName('PROCESS_STATUS', v, this.dicOptions.PROCESS_STATUS) || v
  202 + },
  203 + getDeliveryMethodLabel() {
  204 + var opts = this.getDictOptions('DELIVERY_METHOD') || []
  205 + var v = String(this.form.deliveryMode != null ? this.form.deliveryMode : '')
  206 + if (!v) return '-'
  207 + for (var i = 0; i < opts.length; i++) {
  208 + var o = opts[i]
  209 + var code = o.code != null ? String(o.code) : (o.value != null ? String(o.value) : '')
  210 + if (code === v) return o.name || o.label || '-'
  211 + }
  212 + return v
  213 + },
  214 + getPriceTableLabel() {
  215 + var code = this.form.priceTableCode != null ? this.form.priceTableCode : (this.form.priceId != null ? this.form.priceId : '')
  216 + if (code == null || String(code).trim() === '') return '-'
  217 + var sc = String(code)
  218 + var list = Array.isArray(this.priceTableList) ? this.priceTableList : []
  219 + for (var i = 0; i < list.length; i++) {
  220 + var o = list[i]
  221 + var id = String(o.id || o.priceId || o.value || '')
  222 + var c = String(o.priceTableCode || o.code || o.priceCode || '')
  223 + if (id === sc || c === sc) {
  224 + var name = o.priceTableName || o.tableName || o.name || o.label || ''
  225 + var cd = o.priceTableCode || o.code || c
  226 + return (name ? name + ' ' : '') + (cd ? cd : '') || '-'
  227 + }
  228 + }
  229 + return sc
  230 + },
  231 + async loadRefData() {
  232 + var self = this
  233 + try {
  234 + var results = await Promise.all([
  235 + this.pCall(priceTableQueryApi, { pageIndex: 1, pageSize: 1000 }),
  236 + this.pCall(breedRelationshipQueryApi, { pageIndex: 1, pageSize: 1000 }),
  237 + this.pCall(workshopQueryApi, { pageIndex: 1, pageSize: 1000 })
  238 + ])
  239 + var r1 = (results[0] && results[0].data) ? results[0].data : {}
  240 + var r2 = (results[1] && results[1].data) ? results[1].data : {}
  241 + var r3 = (results[2] && results[2].data) ? results[2].data : {}
  242 + self.priceTableList = (r1.datas && Array.isArray(r1.datas)) ? r1.datas : ((r1.list && Array.isArray(r1.list)) ? r1.list : ((r1.records && Array.isArray(r1.records)) ? r1.records : []))
  243 + self.productOptions = (r2.datas && Array.isArray(r2.datas)) ? r2.datas : ((r2.list && Array.isArray(r2.list)) ? r2.list : ((r2.records && Array.isArray(r2.records)) ? r2.records : []))
  244 + self.workshopOptions = (r3.datas && Array.isArray(r3.datas)) ? r3.datas : ((r3.list && Array.isArray(r3.list)) ? r3.list : ((r3.records && Array.isArray(r3.records)) ? r3.records : []))
  245 + } catch (e) {
  246 + self.priceTableList = []
  247 + self.productOptions = []
  248 + self.workshopOptions = []
  249 + }
  250 + },
  251 + pCall(fn, params) {
  252 + try {
  253 + return Promise.resolve(fn(params))
  254 + } catch (e) {
  255 + return Promise.reject(e)
  256 + }
  257 + },
  258 + boolText(v) {
  259 + if (typeof v !== 'boolean') return '-'
  260 + return v ? '是' : '否'
  261 + },
  262 + formatDateOnly(v) {
  263 + var s = v == null ? '' : String(v).trim()
  264 + if (!s) return '-'
  265 + if (s.length >= 10) return s.slice(0, 10)
  266 + return s
  267 + },
  268 + safeText(v) {
  269 + var s = v == null ? '' : String(v)
  270 + return s.trim() ? s : '-'
  271 + },
  272 + formatNumber(v) {
  273 + if (v == null || v === '') return '-'
  274 + var n = Number(v)
  275 + if (isNaN(n)) return String(v)
  276 + return n.toLocaleString('zh-CN', { maximumFractionDigits: 4 })
  277 + },
  278 + buildAttachmentList() {
  279 + var ids = this.form.attachmentFileIds
  280 + var names = this.form.attachmentFileNames
  281 + var idArr = (typeof ids === 'string' && ids) ? ids.split(',').filter(Boolean) : (Array.isArray(ids) ? ids : [])
  282 + var nameArr = (typeof names === 'string' && names) ? names.split(',').filter(Boolean) : (Array.isArray(names) ? names : [])
  283 + var len = Math.max(idArr.length, nameArr.length)
  284 + var list = []
  285 + for (var i = 0; i < len; i++) {
  286 + list.push({ id: idArr[i] || '', name: nameArr[i] || ('附件' + (i + 1)) })
  287 + }
  288 + this.attachmentList = list
  289 + },
  290 + getFormValues() {
  291 + var self = this
  292 + var form = this.form || {}
  293 + var result = JSON.parse(JSON.stringify(form))
  294 + this.materialConditionConfigs.forEach(function (mc) {
  295 + var arr = form[mc.field]
  296 + var listField = mc.field + 'List'
  297 + if (Array.isArray(arr)) {
  298 + result[mc.field] = arr.join(',')
  299 + result[listField] = arr.slice()
  300 + } else if (typeof arr === 'string' && arr) {
  301 + result[mc.field] = arr
  302 + result[listField] = arr.split(',').filter(Boolean)
  303 + } else {
  304 + result[mc.field] = ''
  305 + result[listField] = []
  306 + }
  307 + })
  308 + var lines = Array.isArray(this.lineList) ? this.lineList : []
  309 + result.procurementPlanLineList = lines.map(function (r) {
  310 + var line = JSON.parse(JSON.stringify(r))
  311 + if (r.shippingCost != null && r.shippingCost !== '') {
  312 + line.shippingCost = self.toNumber ? self.toNumber(r.shippingCost) : Number(r.shippingCost)
  313 + }
  314 + if (r.loadingFee != null && r.loadingFee !== '') {
  315 + line.loadingFee = self.toNumber ? self.toNumber(r.loadingFee) : Number(r.loadingFee)
  316 + }
  317 + if (r.actualPurchasePrice != null && r.actualPurchasePrice !== '') {
  318 + line.actualPurchasePrice = self.toNumber ? self.toNumber(r.actualPurchasePrice) : Number(r.actualPurchasePrice)
  319 + }
  320 + if (r.includingShippingPrice != null && r.includingShippingPrice !== '') {
  321 + line.includingShippingPrice = self.toNumber ? self.toNumber(r.includingShippingPrice) : Number(r.includingShippingPrice)
  322 + }
  323 + if (r.coordinationRange != null && r.coordinationRange !== '') {
  324 + line.coordinationRange = self.toNumber ? self.toNumber(r.coordinationRange) : Number(r.coordinationRange)
  325 + }
  326 + if (r.priceListPrices != null && r.priceListPrices !== '') {
  327 + line.priceListPrices = self.toNumber ? self.toNumber(r.priceListPrices) : Number(r.priceListPrices)
  328 + }
  329 + if (r.quantity != null && r.quantity !== '') {
  330 + line.quantity = self.toNumber ? self.toNumber(r.quantity) : Number(r.quantity)
  331 + }
  332 + if (r.estimatedLoss != null && r.estimatedLoss !== '') {
  333 + line.estimatedLoss = self.toNumber ? self.toNumber(r.estimatedLoss) : Number(r.estimatedLoss)
  334 + }
  335 + if (r.applicationAttrition != null && r.applicationAttrition !== '') {
  336 + line.applicationAttrition = self.toNumber ? self.toNumber(r.applicationAttrition) : Number(r.applicationAttrition)
  337 + }
  338 + return line
  339 + })
  340 + return result
  341 + },
  342 + checkForm() {
  343 + return true
  344 + },
  345 + async loadDetail(id) {
  346 + try {
  347 + var res = await procurementPlanDetailApi({ id: id })
  348 + var d = (res && res.data) ? res.data : {}
  349 + var self = this
  350 + this.materialConditionConfigs.forEach(function (mc) {
  351 + var v = d[mc.field]
  352 + if (typeof v === 'string' && v) {
  353 + d[mc.field] = v.split(',').filter(Boolean)
  354 + } else if (!Array.isArray(v)) {
  355 + d[mc.field] = []
  356 + }
  357 + })
  358 + this.form = d
  359 + this.lineList = Array.isArray(d.procurementPlanLineList) ? d.procurementPlanLineList : []
  360 + this.buildAttachmentList()
  361 + } catch (e) {
  362 + this.form = {}
  363 + this.lineList = []
  364 + this.attachmentList = []
  365 + }
  366 + },
  367 + onPreviewAttachment(at) {
  368 + if (!at || !at.id) {
  369 + uni.showToast({ title: '无法查看', icon: 'none' })
  370 + return
  371 + }
  372 + uni.showToast({ title: '查看附件 ' + at.name, icon: 'none' })
  373 + }
  374 + }
  375 +}
  376 +</script>
  377 +
  378 +<style lang="scss" scoped>
  379 +.detail-page {
  380 + background: #f3f3f3;
  381 +}
  382 +.scroll {
  383 + flex: 1;
  384 + padding: 8rpx 0 144rpx;
  385 +}
  386 +.title-header {
  387 + display: flex;
  388 + align-items: center;
  389 + padding: 24rpx 32rpx 12rpx;
  390 + background-color: #ffffff;
  391 + .title-header_icon {
  392 + width: 36rpx;
  393 + height: 36rpx;
  394 + margin-right: 12rpx;
  395 + }
  396 + span {
  397 + font-size: 34rpx;
  398 + font-weight: 600;
  399 + color: rgba(0, 0, 0, 0.9);
  400 + }
  401 +}
  402 +.section {
  403 + padding: 32rpx;
  404 + background: #fff;
  405 + margin-bottom: 20rpx;
  406 +}
  407 +.row {
  408 + display: flex;
  409 + margin-bottom: 28rpx;
  410 + &:last-child { margin-bottom: 0; }
  411 + &.company {
  412 + font-size: 36rpx;
  413 + font-weight: 600;
  414 + color: rgba(0, 0, 0, 0.9);
  415 + padding-top: 8rpx;
  416 + line-height: 50rpx;
  417 + }
  418 + .label {
  419 + max-width: 420rpx;
  420 + margin-right: 20rpx;
  421 + line-height: 32rpx;
  422 + font-size: 28rpx;
  423 + color: rgba(0, 0, 0, 0.6);
  424 + }
  425 + .value {
  426 + flex: 1;
  427 + line-height: 32rpx;
  428 + font-size: 28rpx;
  429 + color: rgba(0, 0, 0, 0.9);
  430 + text-align: right;
  431 + word-break: break-all;
  432 + }
  433 +}
  434 +.attach-list {
  435 + flex: 1;
  436 + display: flex;
  437 + flex-direction: column;
  438 + align-items: flex-end;
  439 + gap: 8rpx;
  440 +}
  441 +.attach-item {
  442 + display: flex;
  443 + align-items: center;
  444 + gap: 16rpx;
  445 + .attach-name {
  446 + font-size: 26rpx;
  447 + color: rgba(0, 0, 0, 0.9);
  448 + }
  449 + .attach-view {
  450 + font-size: 26rpx;
  451 + color: $theme-primary;
  452 + }
  453 +}
  454 +.material-group {
  455 + margin-top: 32rpx;
  456 + &:first-child { margin-top: 0; }
  457 +}
  458 +.material-label {
  459 + display: block;
  460 + font-size: 28rpx;
  461 + color: rgba(0, 0, 0, 0.6);
  462 + margin-bottom: 20rpx;
  463 +}
  464 +.tag-list {
  465 + display: flex;
  466 + flex-wrap: wrap;
  467 + gap: 16rpx;
  468 +}
  469 +.tag-btn {
  470 + padding: 12rpx 24rpx;
  471 + border: 1rpx solid #ddd;
  472 + border-radius: 8rpx;
  473 + font-size: 28rpx;
  474 + color: #999;
  475 + background: #f5f5f5;
  476 +}
  477 +.tag-btn.active {
  478 + background: $theme-primary;
  479 + border-color: $theme-primary;
  480 + color: #fff;
  481 +}
  482 +.remark-text {
  483 + font-size: 28rpx;
  484 + color: rgba(0, 0, 0, 0.9);
  485 + line-height: 1.6;
  486 + white-space: pre-wrap;
  487 +}
  488 +</style>
... ...
  1 +<template>
  2 + <view class="page">
  3 + <view class="plan-list-fixed">
  4 + <view class="search-row">
  5 + <uni-search-bar
  6 + v-model="searchKeyword"
  7 + radius="6"
  8 + placeholder="请输入编号或供应商名称"
  9 + clearButton="auto"
  10 + cancelButton="none"
  11 + bgColor="#F3F3F3"
  12 + textColor="rgba(0,0,0,0.4)"
  13 + @confirm="search"
  14 + @input="onSearchInput"
  15 + />
  16 + <view class="tool-icons">
  17 + <image class="tool-icon" src="/static/images/dev_manage/filter_icon.png" @click="openFilter" />
  18 + </view>
  19 + </view>
  20 +
  21 + <view class="tabs">
  22 + <view
  23 + v-for="(t, i) in tabs"
  24 + :key="i"
  25 + :class="['tab', { active: frozen === t.value }]"
  26 + @click="switchTab(t)"
  27 + >
  28 + {{ t.label }}
  29 + </view>
  30 + </view>
  31 + </view>
  32 +
  33 + <view class="list-box">
  34 + <card-list
  35 + ref="cardRef"
  36 + :fetch-fn="fetchList"
  37 + :query="query"
  38 + :extra="extraParams"
  39 + row-key="id"
  40 + :enable-refresh="true"
  41 + :enable-load-more="true"
  42 + @loaded="onCardLoaded"
  43 + @error="onCardError"
  44 + >
  45 + <template v-slot="{ item }">
  46 + <view class="card" @click="onCardClick(item)">
  47 + <view class="card-header">
  48 + <text class="title omit2">{{ item.code || '-' }}</text>
  49 + <text :class="['status', `status_${item.approvalStatus}`]">{{ getAuditStatusText(item.approvalStatus) }}</text>
  50 + </view>
  51 + <view class="info-row">
  52 + <text>公司名称</text>
  53 + <text>{{item.companyShortName || '-' }}</text>
  54 + </view>
  55 + <view class="info-row">
  56 + <text>供应商名称</text>
  57 + <text>{{ item.supplierName || '-' }}</text>
  58 + </view>
  59 + <view class="info-row">
  60 + <text>数量</text>
  61 + <text>{{ formatQuantity(item.totalQuantity || 0) }}</text>
  62 + </view>
  63 + <view class="info-row">
  64 + <text>采购处</text>
  65 + <text>{{ item.purchaseDepartmentName || '-' }}</text>
  66 + </view>
  67 + </view>
  68 + </template>
  69 + </card-list>
  70 + </view>
  71 +
  72 + <filter-modal
  73 + :visible.sync="filterVisible"
  74 + :value.sync="filterForm"
  75 + title="筛选"
  76 + @reset="onFilterReset"
  77 + @confirm="onFilterConfirm"
  78 + >
  79 + <template v-slot="{ model }">
  80 + <view class="filter-form">
  81 + <view class="form-item">
  82 + <view class="label">编号</view>
  83 + <input class="input" v-model="model.code" placeholder="请输入编号" />
  84 + </view>
  85 + <view class="form-item">
  86 + <view class="label">供应商名称</view>
  87 + <input class="input" v-model="model.supplierName" placeholder="请输入供应商名称" />
  88 + </view>
  89 + <view class="form-item">
  90 + <view class="label">日期</view>
  91 + <uni-datetime-picker type="daterange" v-model="model.dateRange" start="2023-01-01" />
  92 + </view>
  93 + </view>
  94 + </template>
  95 + </filter-modal>
  96 + </view>
  97 +</template>
  98 +
  99 +<script>
  100 +import CardList from '@/components/card/index.vue'
  101 +import FilterModal from '@/components/filter/index.vue'
  102 +import { getDicByCodes, getDicName } from '@/utils/dic.js'
  103 +import { procurementPlanQueryApi } from '@/api/procure-manage/procurementPlan.js'
  104 +
  105 +export default {
  106 + name: 'DomesticPlan',
  107 + components: { CardList, FilterModal },
  108 + data() {
  109 + return {
  110 + searchKeyword: '',
  111 + searchKeywordDebounced: '',
  112 + searchDebounceTimer: null,
  113 + frozen: false,
  114 + tabs: [
  115 + { label: '未冻结', value: false },
  116 + { label: '已冻结', value: true }
  117 + ],
  118 + query: {
  119 + code: '',
  120 + supplierName: '',
  121 + dateRange: []
  122 + },
  123 + extraParams: {},
  124 + currentItems: [],
  125 + filterVisible: false,
  126 + filterForm: {
  127 + code: '',
  128 + supplierName: '',
  129 + dateRange: []
  130 + },
  131 + dicOptions: {
  132 + AUDIT_STATUS: []
  133 + }
  134 + }
  135 + },
  136 + computed: {
  137 + extraCombined() {
  138 + const keyword = this.searchKeywordDebounced || ''
  139 + return {
  140 + keyword: keyword || undefined,
  141 + frozen: this.frozen
  142 + }
  143 + }
  144 + },
  145 + watch: {
  146 + extraCombined: {
  147 + deep: true,
  148 + handler(v) {
  149 + this.extraParams = v
  150 + },
  151 + immediate: true
  152 + }
  153 + },
  154 + created() {
  155 + this.loadDicData()
  156 + },
  157 + onShow() {
  158 + let needRefresh = ''
  159 + try { needRefresh = uni.getStorageSync('DOMESTIC_PLAN_LIST_NEED_REFRESH') } catch (e) {}
  160 + if (!needRefresh) return
  161 + try { uni.removeStorageSync('DOMESTIC_PLAN_LIST_NEED_REFRESH') } catch (e) {}
  162 + if (this.$refs && this.$refs.cardRef && this.$refs.cardRef.reload) {
  163 + this.$refs.cardRef.reload()
  164 + }
  165 + },
  166 + onReachBottom() {
  167 + if (this.$refs && this.$refs.cardRef && this.$refs.cardRef.onLoadMore) {
  168 + this.$refs.cardRef.onLoadMore()
  169 + }
  170 + },
  171 + beforeDestroy() {
  172 + if (this.searchDebounceTimer) {
  173 + clearTimeout(this.searchDebounceTimer)
  174 + this.searchDebounceTimer = null
  175 + }
  176 + },
  177 + methods: {
  178 + async loadDicData() {
  179 + try {
  180 + const results = await getDicByCodes(['AUDIT_STATUS'])
  181 + this.dicOptions.AUDIT_STATUS = (results.AUDIT_STATUS && results.AUDIT_STATUS.data) || []
  182 + } catch (e) {
  183 + this.dicOptions.AUDIT_STATUS = []
  184 + }
  185 + },
  186 + getAuditStatusText(status) {
  187 + const v = status == null ? '' : String(status)
  188 + if (!v) return '-'
  189 + return getDicName('AUDIT_STATUS', v, this.dicOptions.AUDIT_STATUS) || v
  190 + },
  191 + getTotalQuantity(item) {
  192 + const lines = item && item.procurementPlanLineList && Array.isArray(item.procurementPlanLineList)
  193 + ? item.procurementPlanLineList
  194 + : []
  195 + let total = 0
  196 + lines.forEach(line => {
  197 + const q = line && line.quantity != null ? Number(line.quantity) : 0
  198 + if (!isNaN(q)) total += q
  199 + })
  200 + return total
  201 + },
  202 + formatQuantity(val) {
  203 + if (val == null) return '-'
  204 + try {
  205 + const num = Number(val)
  206 + if (isNaN(num)) return '-'
  207 + return num.toLocaleString('zh-CN', { maximumFractionDigits: 4 })
  208 + } catch (e) {
  209 + return String(val)
  210 + }
  211 + },
  212 + onCardLoaded(_ref) {
  213 + var items = _ref.items
  214 + this.currentItems = items
  215 + },
  216 + onCardError() {
  217 + uni.showToast({ title: '列表加载失败', icon: 'none' })
  218 + },
  219 + onSearchInput() {
  220 + var _this = this
  221 + if (this.searchDebounceTimer) clearTimeout(this.searchDebounceTimer)
  222 + this.searchDebounceTimer = setTimeout(function () {
  223 + _this.searchKeywordDebounced = _this.searchKeyword
  224 + _this.searchDebounceTimer = null
  225 + }, 1200)
  226 + },
  227 + search(e) {
  228 + const val = e && e.value != null ? e.value : this.searchKeyword
  229 + this.searchKeyword = val
  230 + this.searchKeywordDebounced = val
  231 + },
  232 + switchTab(t) {
  233 + this.frozen = t.value
  234 + },
  235 + openFilter() {
  236 + this.filterForm = {
  237 + code: this.query.code || '',
  238 + supplierName: this.query.supplierName || '',
  239 + dateRange: Array.isArray(this.query.dateRange) ? this.query.dateRange.slice(0) : []
  240 + }
  241 + this.filterVisible = true
  242 + },
  243 + onFilterReset(payload) {
  244 + this.filterForm = payload
  245 + },
  246 + onFilterConfirm(payload) {
  247 + this.query = {
  248 + code: payload.code || '',
  249 + supplierName: payload.supplierName || '',
  250 + dateRange: Array.isArray(payload.dateRange) ? payload.dateRange : []
  251 + }
  252 + },
  253 + fetchList(_ref2) {
  254 + var _this2 = this
  255 + var pageIndex = _ref2.pageIndex
  256 + var pageSize = _ref2.pageSize
  257 + var query = _ref2.query
  258 + var extra = _ref2.extra
  259 + const q = query || {}
  260 + const e = extra || {}
  261 + const range = Array.isArray(q.dateRange) ? q.dateRange : []
  262 + const keyword = e.keyword || ''
  263 + const params = {
  264 + pageIndex: pageIndex,
  265 + pageSize: pageSize,
  266 + frozen: e.frozen != null ? e.frozen : false,
  267 + code: q.code || '',
  268 + supplierName: q.supplierName || '',
  269 + startDate: range && range.length === 2 ? range[0] : '',
  270 + endDate: range && range.length === 2 ? range[1] : ''
  271 + }
  272 + if (keyword) {
  273 + if (!params.code) params.code = keyword
  274 + if (!params.supplierName) params.supplierName = keyword
  275 + }
  276 + return procurementPlanQueryApi(params).then(function (res) {
  277 + const _data = res && res.data ? res.data : {}
  278 + let records = _data.datas || _data.list || _data.records || []
  279 + const totalCount = _data.totalCount || _data.count || 0
  280 + const hasNext = _data.hasNext || false
  281 + records = records.map(function (it) { return Object.assign({}, it) })
  282 + return { records: records, totalCount: totalCount, hasNext: hasNext }
  283 + }).catch(function () {
  284 + _this2.onCardError()
  285 + return { records: [], totalCount: 0, hasNext: false }
  286 + })
  287 + },
  288 + onCardClick(item) {
  289 + uni.navigateTo({ url: '/pages/domestic-plan/detail?id=' + item.id })
  290 + }
  291 + }
  292 +}
  293 +</script>
  294 +
  295 +<style lang="scss" scoped>
  296 +.page {
  297 + display: flex;
  298 + flex-direction: column;
  299 + height: 100vh;
  300 +}
  301 +.plan-list-fixed {
  302 + position: fixed;
  303 + top: 96rpx;
  304 + left: 0;
  305 + right: 0;
  306 + z-index: 2;
  307 + background: #fff;
  308 + .search-row {
  309 + display: flex;
  310 + align-items: center;
  311 + padding: 16rpx 32rpx;
  312 + .uni-searchbar { padding: 0; flex: 1; }
  313 + .tool-icons {
  314 + display: flex;
  315 + .tool-icon { width: 48rpx; height: 48rpx; display: block; margin-left: 32rpx; }
  316 + }
  317 + }
  318 + .tabs {
  319 + display: flex;
  320 + align-items: center;
  321 + justify-content: space-between;
  322 + padding: 26rpx 34rpx;
  323 + border-bottom: solid 1px #E7E7E7;
  324 + .tab { width: 50%; text-align: center; color: rgba(0,0,0,0.9); line-height: 44rpx; position: relative; font-weight: 600; }
  325 + .tab.active { color: $theme-primary; }
  326 + .tab.active::after {
  327 + content: '';
  328 + position: absolute;
  329 + left: 50%;
  330 + transform: translateX(-50%);
  331 + bottom: -13px;
  332 + width: 32rpx;
  333 + height: 6rpx;
  334 + border-radius: 4rpx;
  335 + background: $theme-primary;
  336 + }
  337 + }
  338 +}
  339 +::v-deep .uni-searchbar__box { height: 80rpx !important; justify-content: start; .uni-searchbar__box-search-input { font-size: 32rpx !important; } }
  340 +.list-box {
  341 + flex: 1;
  342 + padding-top: 236rpx;
  343 + .card { position: relative; }
  344 + .card-header {
  345 + margin-bottom: 28rpx;
  346 + position: relative;
  347 + .title { font-size: 36rpx; font-weight: 600; line-height: 50rpx; color: rgba(0,0,0,0.9); width: 578rpx; }
  348 + .status {
  349 + position: absolute;
  350 + top: -32rpx;
  351 + right: -12rpx;
  352 + height: 48rpx;
  353 + line-height: 48rpx;
  354 + font-weight: 600;
  355 + color: #fff;
  356 + font-size: 24rpx;
  357 + padding: 0 14rpx;
  358 + border-radius: 6rpx;
  359 + box-sizing: content-box;
  360 +
  361 + &.status_AUDIT {
  362 + background: $theme-primary;
  363 + }
  364 +
  365 + &.status_PASS {
  366 + background: #2BA471;
  367 + }
  368 +
  369 + &.status_REFUSE {
  370 + background: #D54941;
  371 + }
  372 +
  373 + &.status_CANCEL {
  374 + background: #E7E7E7;
  375 + color: rgba(0, 0, 0, 0.9);
  376 + }
  377 + }
  378 + }
  379 + .info-row {
  380 + display: flex;
  381 + align-items: center;
  382 + color: rgba(0,0,0,0.6);
  383 + font-size: 28rpx;
  384 + margin-bottom: 24rpx;
  385 + height: 32rpx;
  386 + &:last-child { margin-bottom: 0; }
  387 + text {
  388 + width: 50%;
  389 + &:last-child { color: rgba(0,0,0,0.9); width: 50%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  390 + }
  391 + }
  392 +}
  393 +.filter-form {
  394 + .form-item { margin-bottom: 24rpx; }
  395 + .label { margin-bottom: 20rpx; color: rgba(0,0,0,0.9); height: 44rpx; line-height: 44rpx; font-size: 30rpx; }
  396 + .input {
  397 + width: 100%;
  398 + height: 72rpx;
  399 + line-height: 72rpx;
  400 + padding: 0 24rpx;
  401 + border: 1rpx solid #E7E7E7;
  402 + border-radius: 8rpx;
  403 + font-size: 28rpx;
  404 + color: rgba(0,0,0,0.9);
  405 + background: #fff;
  406 + box-sizing: border-box;
  407 + }
  408 +}
  409 +</style>
... ...
  1 +<template>
  2 + <view class="product-rel">
  3 + <view class="header">
  4 + <image class="opCollapse" src="/static/images/title.png" />
  5 + <text class="title">明细信息</text>
  6 + <view class="ops">
  7 + <image v-if="(mode === 'add' || mode === 'edit') && !isSupplement && !isSelectWorkshop" class="opAdd" @click="onAdd" src="/static/images/plus.png" />
  8 + <view v-if="mode === 'view'" class="op1" @click="toggleViewCollapse">
  9 + <image class="opAdd" :src="collapsedView ? '/static/images/down.png' : '/static/images/up.png'" />
  10 + <text class="op">{{ collapsedView ? '展开' : '收起' }}</text>
  11 + </view>
  12 + </view>
  13 + </view>
  14 +
  15 + <view v-if="mode === 'add' || mode === 'edit'" class="add-list">
  16 + <view v-for="(item, idx) in items" :key="item._key || idx" class="block">
  17 + <uni-list v-show="!item.collapsed">
  18 + <uni-list-item
  19 + class="select-item"
  20 + :class="item.productName ? 'is-filled' : 'is-empty'"
  21 + :clickable="!isSelectWorkshop && !isSupplement"
  22 + @click="!isSelectWorkshop && !isSupplement ? openSelectSheet('product', idx) : undefined"
  23 + :rightText="item.productName || (isSelectWorkshop || isSupplement ? '-' : '请选择品种')"
  24 + :showArrow="!isSelectWorkshop && !isSupplement"
  25 + >
  26 + <template v-slot:body>
  27 + <view class="item-title" :class="{ 'no-required': isSelectWorkshop || isSupplement }">
  28 + <text v-if="!isSelectWorkshop && !isSupplement" class="required">*</text><text>品种</text>
  29 + </view>
  30 + </template>
  31 + </uni-list-item>
  32 + <uni-list-item title="料型">
  33 + <template v-slot:footer>
  34 + <uni-easyinput v-if="!isSelectWorkshop && !isSupplement" v-model="item.materialType" :inputBorder="false" placeholder="请输入料型" />
  35 + <text v-else class="lock-value">{{ safeText(item.materialType) }}</text>
  36 + </template>
  37 + </uni-list-item>
  38 + <uni-list-item title="数量(吨)">
  39 + <template v-slot:footer>
  40 + <template v-if="!isSelectWorkshop && !isSupplement">
  41 + <uni-easyinput
  42 + v-model="item.quantity"
  43 + type="digit"
  44 + :inputBorder="false"
  45 + placeholder="请输入数量"
  46 + @input="onNonNegativeInput(idx, 'quantity')"
  47 + @blur="onNonNegativeBlur(idx, 'quantity', 4)"
  48 + />
  49 + </template>
  50 + <text v-else class="lock-value">{{ formatTextNumber(item.quantity) }}</text>
  51 + </template>
  52 + </uni-list-item>
  53 + <uni-list-item title="实际采购价">
  54 + <template v-slot:footer>
  55 + <template v-if="!isSelectWorkshop && !isSupplement">
  56 + <uni-easyinput
  57 + v-model="item.actualPurchasePrice"
  58 + type="digit"
  59 + :inputBorder="false"
  60 + placeholder="请输入实际采购价"
  61 + @input="onNonNegativeInput(idx, 'actualPurchasePrice')"
  62 + @blur="onNonNegativeBlur(idx, 'actualPurchasePrice', 2)"
  63 + @change="recalcAll(idx)"
  64 + />
  65 + </template>
  66 + <text v-else class="lock-value">{{ formatTextNumber(item.actualPurchasePrice) }}</text>
  67 + </template>
  68 + </uni-list-item>
  69 + <uni-list-item title="含运到厂价">
  70 + <template v-slot:footer>
  71 + <text class="lock-value">{{ formatTextNumber(item.includingShippingPrice) }}</text>
  72 + </template>
  73 + </uni-list-item>
  74 + <uni-list-item title="价格表价格">
  75 + <template v-slot:footer>
  76 + <uni-easyinput
  77 + v-if="!isSelectWorkshop"
  78 + v-model="item.priceListPrices"
  79 + type="digit"
  80 + :inputBorder="false"
  81 + placeholder="请输入价格表价格"
  82 + @input="onNonNegativeInput(idx, 'priceListPrices')"
  83 + @blur="onNonNegativeBlur(idx, 'priceListPrices', 2)"
  84 + />
  85 + <text v-else class="lock-value">{{ formatTextNumber(item.priceListPrices) }}</text>
  86 + </template>
  87 + </uni-list-item>
  88 + <uni-list-item title="协调幅度">
  89 + <template v-slot:footer>
  90 + <text class="lock-value">{{ formatTextNumber(item.coordinationRange) }}</text>
  91 + </template>
  92 + </uni-list-item>
  93 + <uni-list-item title="买断料预计损耗">
  94 + <template v-slot:footer>
  95 + <text class="lock-value">{{ formatTextNumber(item.estimatedLoss) }}</text>
  96 + </template>
  97 + </uni-list-item>
  98 + <uni-list-item title="申请损耗(%)">
  99 + <template v-slot:footer>
  100 + <template v-if="!isSelectWorkshop && !isSupplement">
  101 + <uni-easyinput
  102 + v-model="item.applicationAttrition"
  103 + type="digit"
  104 + :inputBorder="false"
  105 + placeholder="请输入申请损耗"
  106 + @input="onNonNegativeInput(idx, 'applicationAttrition')"
  107 + @blur="onNonNegativeBlur(idx, 'applicationAttrition', 2)"
  108 + @change="recalcAll(idx)"
  109 + />
  110 + </template>
  111 + <text v-else class="lock-value">{{ formatTextNumber(item.applicationAttrition) }}</text>
  112 + </template>
  113 + </uni-list-item>
  114 + <uni-list-item
  115 + class="select-item"
  116 + :class="item.workshopName ? 'is-filled' : 'is-empty'"
  117 + :clickable="!isSupplement"
  118 + @click="!isSupplement ? openSelectSheet('workshop', idx) : undefined"
  119 + :rightText="item.workshopName || (isSupplement ? '-' : '请选择分厂')"
  120 + :showArrow="!isSupplement"
  121 + >
  122 + <template v-slot:body>
  123 + <view class="item-title" :class="{ 'no-required': isSupplement }">
  124 + <text v-if="!isSupplement" class="required">*</text><text>选择分厂</text>
  125 + </view>
  126 + </template>
  127 + </uni-list-item>
  128 + </uni-list>
  129 + <uni-list v-show="item.collapsed">
  130 + <uni-list-item
  131 + class="select-item"
  132 + :class="item.productName ? 'is-filled' : 'is-empty'"
  133 + :clickable="!isSelectWorkshop && !isSupplement"
  134 + @click="!isSelectWorkshop && !isSupplement ? openSelectSheet('product', idx) : undefined"
  135 + :rightText="item.productName || (isSelectWorkshop || isSupplement ? '-' : '请选择品种')"
  136 + :showArrow="!isSelectWorkshop && !isSupplement"
  137 + >
  138 + <template v-slot:body>
  139 + <view class="item-title" :class="{ 'no-required': isSelectWorkshop || isSupplement }">
  140 + <text v-if="!isSelectWorkshop && !isSupplement" class="required">*</text><text>品种</text>
  141 + </view>
  142 + </template>
  143 + </uni-list-item>
  144 + </uni-list>
  145 + <view class="block-ops" :class="{ 'readonly-ops': isSelectWorkshop || isSupplement }">
  146 + <div v-if="!isSelectWorkshop && !isSupplement" class="del" @click="onRemove(idx)">
  147 + <image src="/static/images/delete.png" class="icon" />
  148 + 删除
  149 + </div>
  150 + <div class="toggle" @click="toggleItem(idx)">
  151 + <image :src="item.collapsed ? '/static/images/up.png' : '/static/images/down.png'" class="icon" />
  152 + {{ item.collapsed ? '展开' : '收起' }}
  153 + </div>
  154 + </view>
  155 + </view>
  156 + </view>
  157 +
  158 + <view v-else class="view-list" v-show="!collapsedView">
  159 + <view v-if="!items.length" class="empty-tip">暂无明细</view>
  160 + <view v-for="(item, idx) in items" :key="'v-' + (item._key || item.itemId || idx)" class="card">
  161 + <view class="row"><text class="label">品种</text><text class="value">{{ safeText(item.productName || item.productId) }}</text></view>
  162 + <view class="row"><text class="label">料型</text><text class="value">{{ safeText(item.materialType) }}</text></view>
  163 + <view class="row"><text class="label">数量(吨)</text><text class="value">{{ formatNumber(item.quantity) }}</text></view>
  164 + <view class="row"><text class="label">实际采购价</text><text class="value">{{ formatNumber(item.actualPurchasePrice) }}</text></view>
  165 + <view class="row"><text class="label">含运到厂价</text><text class="value">{{ formatNumber(item.includingShippingPrice) }}</text></view>
  166 + <view class="row"><text class="label">价格表价格</text><text class="value">{{ formatNumber(item.priceListPrices) }}</text></view>
  167 + <view class="row"><text class="label">协调幅度</text><text class="value">{{ formatNumber(item.coordinationRange) }}</text></view>
  168 + <view class="row"><text class="label">买断料预计损耗</text><text class="value">{{ formatNumber(item.estimatedLoss) }}</text></view>
  169 + <view class="row"><text class="label">申请损耗(%)</text><text class="value">{{ formatNumber(item.applicationAttrition) }}</text></view>
  170 + <view class="row"><text class="label">分厂</text><text class="value">{{ safeText(item.workshopName || item.workshopId) }}</text></view>
  171 + </view>
  172 + </view>
  173 +
  174 + <SingleSelectSheet
  175 + :visible.sync="sheet.visible"
  176 + :title="sheet.title"
  177 + :options="sheet.options"
  178 + v-model="sheet.value"
  179 + @confirm="onSheetConfirm"
  180 + />
  181 + </view>
  182 +</template>
  183 +
  184 +<script>
  185 +import SingleSelectSheet from '@/components/single-select/index.vue'
  186 +
  187 +export default {
  188 + name: 'DomesticPlanProductRel',
  189 + props: {
  190 + mode: { type: String, default: 'view' },
  191 + scene: { type: String, default: '' },
  192 + list: { type: Array, default: function () { return [] } },
  193 + max: { type: Number, default: 50 },
  194 + productOptions: { type: Array, default: function () { return [] } },
  195 + workshopOptions: { type: Array, default: function () { return [] } }
  196 + },
  197 + components: { SingleSelectSheet },
  198 + data() {
  199 + return {
  200 + items: [],
  201 + collapsedView: false,
  202 + sheet: { visible: false, title: '请选择', options: [], value: '', idx: -1, type: '' },
  203 + _initialized: false,
  204 + _emitSuppressed: false
  205 + }
  206 + },
  207 + computed: {
  208 + isSupplement() { return this.scene === 'supplement' },
  209 + isSelectWorkshop() { return this.scene === 'selectWorkshop' },
  210 + productOptsComputed() {
  211 + var list = Array.isArray(this.productOptions) ? this.productOptions : []
  212 + return list.map(function (o) {
  213 + return {
  214 + label: o.label != null ? o.label : (o.name != null ? o.name : (o.productName || '')),
  215 + value: o.value != null ? o.value : (o.id != null ? o.id : o.productId)
  216 + }
  217 + })
  218 + },
  219 + workshopOptsComputed() {
  220 + var list = Array.isArray(this.workshopOptions) ? this.workshopOptions : []
  221 + return list.map(function (o) {
  222 + return {
  223 + label: o.label != null ? o.label : (o.name != null ? o.name : (o.workshopName || '')),
  224 + value: o.value != null ? o.value : (o.id != null ? o.id : o.workshopId)
  225 + }
  226 + })
  227 + }
  228 + },
  229 + watch: {
  230 + items: {
  231 + handler() { this.emitChange() },
  232 + deep: true
  233 + },
  234 + list: {
  235 + handler(v) {
  236 + if (this._emitSuppressed) return
  237 + this._emitSuppressed = true
  238 + this._initialized = false
  239 + this.initItems()
  240 + var self = this
  241 + this.$nextTick(function () {
  242 + self._initialized = true
  243 + self.recalcAllItems()
  244 + self.$nextTick(function () { self._emitSuppressed = false })
  245 + })
  246 + },
  247 + immediate: false
  248 + }
  249 + },
  250 + created() {
  251 + this._emitSuppressed = true
  252 + this.initItems()
  253 + var self = this
  254 + this.$nextTick(function () {
  255 + self._initialized = true
  256 + self.recalcAllItems()
  257 + self.$nextTick(function () { self._emitSuppressed = false })
  258 + })
  259 + },
  260 + methods: {
  261 + initItems() {
  262 + var src = Array.isArray(this.list) && this.list.length > 0 ? this.list : []
  263 + if (!src.length) {
  264 + if (this.mode === 'add' || this.mode === 'edit') {
  265 + this.items = [Object.assign({}, this.defaultItem(), { _key: this.newKey(), collapsed: false })]
  266 + } else {
  267 + this.items = []
  268 + }
  269 + return
  270 + }
  271 + var existing = this.items || []
  272 + var existingMap = {}
  273 + existing.forEach(function (it) {
  274 + if (it.itemId) existingMap[it.itemId] = it
  275 + })
  276 + var self = this
  277 + this.items = src.map(function (x, i) {
  278 + var key = x && x.itemId ? x.itemId : (x && x._key ? x._key : self.newKey())
  279 + var old = existingMap[key]
  280 + return Object.assign({}, self.defaultItem(), x, {
  281 + _key: key,
  282 + itemId: x && x.itemId ? x.itemId : key,
  283 + collapsed: old ? old.collapsed : true
  284 + })
  285 + })
  286 + },
  287 + newKey() {
  288 + return Date.now() + '-' + Math.random().toString(16).slice(2)
  289 + },
  290 + defaultItem() {
  291 + return {
  292 + productId: '',
  293 + productName: '',
  294 + materialType: '',
  295 + quantity: '',
  296 + actualPurchasePrice: '',
  297 + includingShippingPrice: '',
  298 + priceListPrices: '',
  299 + coordinationRange: '',
  300 + estimatedLoss: '',
  301 + applicationAttrition: '',
  302 + workshopId: '',
  303 + workshopName: '',
  304 + fixedLossRate: ''
  305 + }
  306 + },
  307 + onNonNegativeInput(idx, field) {
  308 + var it = this.items[idx]
  309 + if (!it) return
  310 + var v = String(it[field] != null ? it[field] : '')
  311 + v = v.replace(/[^0-9.]/g, '')
  312 + v = v.replace(/(\..*)\./g, '$1')
  313 + it[field] = v
  314 + this.$set(this.items, idx, it)
  315 + },
  316 + onSignedInput(idx, field) {
  317 + var it = this.items[idx]
  318 + if (!it) return
  319 + var v = String(it[field] != null ? it[field] : '')
  320 + var sign = ''
  321 + if (v.charAt(0) === '-') { sign = '-'; v = v.slice(1) }
  322 + v = v.replace(/[^0-9.]/g, '')
  323 + v = v.replace(/(\..*)\./g, '$1')
  324 + it[field] = sign + v
  325 + this.$set(this.items, idx, it)
  326 + },
  327 + onNonNegativeBlur(idx, field, digits) {
  328 + var it = this.items[idx]
  329 + if (!it) return
  330 + var raw = it[field]
  331 + if (raw === '' || raw === null || raw === undefined) {
  332 + this.$set(this.items, idx, it)
  333 + return
  334 + }
  335 + var val = parseFloat(raw)
  336 + if (isNaN(val)) {
  337 + it[field] = ''
  338 + this.$set(this.items, idx, it)
  339 + return
  340 + }
  341 + var m = Math.pow(10, digits)
  342 + var rounded = Math.round(val * m) / m
  343 + it[field] = rounded
  344 + this.$set(this.items, idx, it)
  345 + },
  346 + getProductOption(productId) {
  347 + var pid = productId != null ? String(productId) : ''
  348 + if (!pid) return null
  349 + var pOpts = Array.isArray(this.productOptions) ? this.productOptions : []
  350 + for (var i = 0; i < pOpts.length; i++) {
  351 + var o = pOpts[i]
  352 + var id = o.id != null ? String(o.id) : (o.productId != null ? String(o.productId) : (o.value != null ? String(o.value) : ''))
  353 + if (id === pid) return o
  354 + }
  355 + return null
  356 + },
  357 + round2(v) {
  358 + var n = Number(v)
  359 + if (isNaN(n)) return ''
  360 + return Math.round(n * 100) / 100
  361 + },
  362 + recalcAll(idx) {
  363 + var it = this.items[idx]
  364 + if (!it) return
  365 + var priceRaw = it.actualPurchasePrice
  366 + var price = parseFloat(priceRaw)
  367 + var priceIsNumber = priceRaw !== '' && priceRaw != null && !isNaN(price)
  368 + var estimatedLoss = ''
  369 + if (priceIsNumber) {
  370 + var app = it.applicationAttrition
  371 + var appNum = parseFloat(app)
  372 + var appHasVal = app !== '' && app != null && !isNaN(appNum)
  373 + if (appHasVal) {
  374 + estimatedLoss = this.round2(price * appNum / 100)
  375 + } else {
  376 + var pOpt = this.getProductOption(it.productId)
  377 + var fixedRate = null
  378 + if (pOpt) {
  379 + fixedRate = pOpt.fixedLossRate != null ? pOpt.fixedLossRate : (pOpt.lossRate != null ? pOpt.lossRate : (pOpt.attritionRate != null ? pOpt.attritionRate : null))
  380 + }
  381 + var fixedNum = parseFloat(fixedRate)
  382 + if (!isNaN(fixedNum)) {
  383 + estimatedLoss = this.round2(price * fixedNum / 100)
  384 + } else if (it.fixedLossRate != null && it.fixedLossRate !== '') {
  385 + var fallbackNum = parseFloat(it.fixedLossRate)
  386 + if (!isNaN(fallbackNum)) {
  387 + estimatedLoss = this.round2(price * fallbackNum / 100)
  388 + }
  389 + }
  390 + }
  391 + }
  392 + it.estimatedLoss = estimatedLoss
  393 + this.$set(this.items, idx, it)
  394 + },
  395 + recalcAllItems() {
  396 + var len = (this.items || []).length
  397 + for (var i = 0; i < len; i++) {
  398 + this.recalcAll(i)
  399 + }
  400 + },
  401 + onAdd() {
  402 + if (this.items.length >= this.max) {
  403 + uni.showToast({ title: '最多' + this.max + '条明细', icon: 'none' })
  404 + return
  405 + }
  406 + this.items.push(Object.assign({}, this.defaultItem(), { _key: this.newKey(), collapsed: false }))
  407 + },
  408 + onRemove(idx) {
  409 + this.items.splice(idx, 1)
  410 + if ((this.mode === 'add' || this.mode === 'edit') && this.items.length === 0) {
  411 + this.items.push(Object.assign({}, this.defaultItem(), { _key: this.newKey(), collapsed: false }))
  412 + }
  413 + },
  414 + toggleItem(idx) {
  415 + var it = this.items[idx]
  416 + if (!it) return
  417 + this.$set(it, 'collapsed', !it.collapsed)
  418 + },
  419 + toggleViewCollapse() {
  420 + this.collapsedView = !this.collapsedView
  421 + },
  422 + openSelectSheet(type, idx) {
  423 + if (type === 'product') {
  424 + var pOpts = this.productOptsComputed
  425 + var curP = this.items[idx] && this.items[idx].productId
  426 + var mP = pOpts.find(function (o) { return String(o.value) === String(curP) })
  427 + this.sheet = { visible: true, title: '请选择品种', options: pOpts, value: mP ? mP.value : '', idx: idx, type: type }
  428 + } else if (type === 'workshop') {
  429 + var wOpts = this.workshopOptsComputed
  430 + var curW = this.items[idx] && this.items[idx].workshopId
  431 + var mW = wOpts.find(function (o) { return String(o.value) === String(curW) })
  432 + this.sheet = { visible: true, title: '请选择分厂', options: wOpts, value: mW ? mW.value : '', idx: idx, type: type }
  433 + }
  434 + },
  435 + onSheetConfirm(_ref) {
  436 + var value = _ref.value
  437 + var label = _ref.label
  438 + var idx = this.sheet.idx
  439 + var type = this.sheet.type
  440 + if (idx < 0 || !this.items[idx]) return
  441 + var it = this.items[idx]
  442 + if (type === 'product') {
  443 + this.$set(it, 'productId', value)
  444 + this.$set(it, 'productName', label)
  445 + var pOpt = this.getProductOption(value)
  446 + if (pOpt) {
  447 + var rate = pOpt.fixedLossRate != null ? pOpt.fixedLossRate : (pOpt.lossRate != null ? pOpt.lossRate : (pOpt.attritionRate != null ? pOpt.attritionRate : ''))
  448 + this.$set(it, 'fixedLossRate', rate != null ? rate : '')
  449 + } else {
  450 + this.$set(it, 'fixedLossRate', '')
  451 + }
  452 + this.recalcAll(idx)
  453 + } else if (type === 'workshop') {
  454 + this.$set(it, 'workshopId', value)
  455 + this.$set(it, 'workshopName', label)
  456 + }
  457 + },
  458 + emitChange() {
  459 + if (!this._initialized) return
  460 + if (this._emitSuppressed) return
  461 + var list = (this.items || []).map(function (it) {
  462 + var c = Object.assign({}, it)
  463 + var key = c._key
  464 + delete c._key
  465 + delete c.collapsed
  466 + delete c._selected
  467 + delete c.fixedLossRate
  468 + if (!c.itemId) c.itemId = key
  469 + return c
  470 + })
  471 + this.$emit('change', list)
  472 + },
  473 + safeText(v) {
  474 + var s = v == null ? '' : String(v)
  475 + return s.trim() ? s : '-'
  476 + },
  477 + formatNumber(v) {
  478 + if (v == null || v === '') return '-'
  479 + var n = Number(v)
  480 + if (isNaN(n)) return String(v)
  481 + return n.toLocaleString('zh-CN', { maximumFractionDigits: 4 })
  482 + },
  483 + formatTextNumber(v) {
  484 + if (v == null || v === '') return '-'
  485 + var n = Number(v)
  486 + if (isNaN(n)) return String(v)
  487 + return n.toLocaleString('zh-CN', { maximumFractionDigits: 4 })
  488 + },
  489 + validate() {
  490 + var list = this.items || []
  491 + for (var i = 0; i < list.length; i++) {
  492 + var it = list[i]
  493 + if (!this.isSupplement && !this.isSelectWorkshop && !it.productId) {
  494 + uni.showToast({ title: '第' + (i + 1) + '条明细:请选择品种', icon: 'none' })
  495 + return false
  496 + }
  497 + if (!this.isSupplement && !it.workshopId) {
  498 + uni.showToast({ title: '第' + (i + 1) + '条明细:请选择分厂', icon: 'none' })
  499 + return false
  500 + }
  501 + }
  502 + return true
  503 + }
  504 + }
  505 +}
  506 +</script>
  507 +
  508 +<style lang="scss" scoped>
  509 +.product-rel {
  510 + margin-top: 20rpx;
  511 + background: #fff;
  512 +}
  513 +.header {
  514 + display: flex;
  515 + align-items: center;
  516 + padding: 24rpx 32rpx;
  517 + border-bottom: 1rpx solid #f0f0f0;
  518 +}
  519 +.title {
  520 + font-size: 32rpx;
  521 + color: rgba(0, 0, 0, 0.9);
  522 + font-weight: 600;
  523 +}
  524 +.ops {
  525 + margin-left: auto;
  526 +}
  527 +.op {
  528 + color: $theme-primary;
  529 + font-size: 28rpx;
  530 + margin-left: 8rpx;
  531 +}
  532 +.op1 {
  533 + display: flex;
  534 + align-items: center;
  535 +}
  536 +.opAdd {
  537 + color: rgba(0, 0, 0, 0.6);
  538 + width: 40rpx;
  539 + height: 40rpx;
  540 +}
  541 +.opCollapse {
  542 + color: rgba(0, 0, 0, 0.6);
  543 + width: 32rpx;
  544 + height: 28rpx;
  545 + margin-right: 16rpx;
  546 + margin-top: 8rpx;
  547 +}
  548 +.block {
  549 + background: #fff;
  550 + margin-bottom: 20rpx;
  551 +}
  552 +::v-deep .uni-list-item__content {
  553 + display: flex;
  554 + justify-content: center;
  555 +}
  556 +::v-deep .uni-list {
  557 + .uni-easyinput {
  558 + display: flex;
  559 + .uni-input-input {
  560 + color: rgba(0, 0, 0, 0.9);
  561 + }
  562 + }
  563 + .uni-input-placeholder { z-index: 1; }
  564 + .uni-input-input { background-color: #ffffff; }
  565 + background: transparent;
  566 + &-item {
  567 + &__container { padding: 32rpx; }
  568 + &__content-title {
  569 + font-size: 28rpx;
  570 + color: rgba(0, 0, 0, 0.9);
  571 + }
  572 + &__extra-text { font-size: 32rpx; }
  573 + .uni-easyinput {
  574 + width: 100%;
  575 + &__placeholder-class {
  576 + font-size: 32rpx;
  577 + color: rgba(0, 0, 0, 0.4);
  578 + }
  579 + &__content {
  580 + border: none;
  581 + background-color: #ffffff !important;
  582 + display: flex;
  583 + &-input {
  584 + padding-left: 0 !important;
  585 + height: 48rpx;
  586 + line-height: 48rpx;
  587 + font-size: 32rpx;
  588 + }
  589 + }
  590 + }
  591 + &.select-item {
  592 + &.is-empty {
  593 + .uni-list-item__extra-text { color: rgba(0, 0, 0, 0.4) !important; }
  594 + }
  595 + &.is-filled {
  596 + .uni-list-item__extra-text { color: rgba(0, 0, 0, 0.9) !important; }
  597 + }
  598 + }
  599 + }
  600 +}
  601 +
  602 +::v-deep .uni-easyinput {
  603 + width: 100%;
  604 + &__content {
  605 + background: #f8f8f8 !important;
  606 + border-radius: 8rpx;
  607 + &-input { font-size: 30rpx; }
  608 + }
  609 +}
  610 +.block-ops {
  611 + display: flex;
  612 + padding: 20rpx 32rpx 20rpx;
  613 + justify-content: space-around;
  614 + &.readonly-ops {
  615 + justify-content: flex-end;
  616 + }
  617 +}
  618 +.del {
  619 + color: #D54941;
  620 + font-size: 28rpx;
  621 + display: flex;
  622 + align-items: center;
  623 + image { width: 40rpx; height: 40rpx; }
  624 +}
  625 +.toggle {
  626 + color: $theme-primary;
  627 + font-size: 28rpx;
  628 + display: flex;
  629 + align-items: center;
  630 + image { width: 40rpx; height: 40rpx; }
  631 +}
  632 +.view-list {
  633 + padding: 24rpx 32rpx;
  634 + background: #ffffff;
  635 +}
  636 +.empty-tip {
  637 + text-align: center;
  638 + font-size: 28rpx;
  639 + color: rgba(0, 0, 0, 0.4);
  640 + padding: 24rpx 0;
  641 +}
  642 +.card {
  643 + background: #f3f3f3;
  644 + border-radius: 16rpx;
  645 + padding: 24rpx;
  646 + margin-bottom: 20rpx;
  647 +}
  648 +.row {
  649 + display: flex;
  650 + margin-bottom: 20rpx;
  651 +}
  652 +.row:last-child { margin-bottom: 0; }
  653 +.label {
  654 + width: 260rpx;
  655 + color: rgba(0, 0, 0, 0.6);
  656 + font-size: 28rpx;
  657 +}
  658 +.value {
  659 + flex: 1;
  660 + color: rgba(0, 0, 0, 0.9);
  661 + font-size: 28rpx;
  662 + word-break: break-all;
  663 +}
  664 +.item-title {
  665 + position: relative;
  666 + width: 210rpx;
  667 + margin-right: 32rpx;
  668 + color: rgba(0, 0, 0, 0.9);
  669 + font-size: 32rpx;
  670 + min-height: 48rpx;
  671 + line-height: 48rpx;
  672 + .required {
  673 + color: red;
  674 + position: absolute;
  675 + top: 50%;
  676 + transform: translateY(-50%);
  677 + left: -16rpx;
  678 + }
  679 +}
  680 +.lock-value {
  681 + flex: 1;
  682 + text-align: right;
  683 + font-size: 32rpx;
  684 + color: rgba(0, 0, 0, 0.85);
  685 + padding: 8rpx 0;
  686 +}
  687 +</style>
... ...
  1 +<template>
  2 + <view class="page">
  3 + <scroll-view scroll-y class="scroll">
  4 + <uni-list>
  5 + <uni-list-item class="select-item" :class="detail.code ? 'is-filled' : 'is-empty'">
  6 + <template v-slot:body>
  7 + <view class="item-title"><text>编号</text></view>
  8 + </template>
  9 + <template v-slot:footer>
  10 + <view class="serial-number-row">
  11 + <text class="readonly-text value-code">{{ safeText(detail.code) }}</text>
  12 + </view>
  13 + </template>
  14 + </uni-list-item>
  15 +
  16 + <uni-list-item title="公司名称">
  17 + <template v-slot:footer>
  18 + <uni-easyinput v-model="companyNameDisplay" :inputBorder="false" disabled placeholder="-" />
  19 + </template>
  20 + </uni-list-item>
  21 +
  22 + <uni-list-item title="供应商名称">
  23 + <template v-slot:footer>
  24 + <uni-easyinput v-model="detail.supplierName" :inputBorder="false" disabled placeholder="-" />
  25 + </template>
  26 + </uni-list-item>
  27 +
  28 + <uni-list-item title="采购处">
  29 + <template v-slot:footer>
  30 + <uni-easyinput v-model="purchaseDepartmentDisplay" :inputBorder="false" disabled placeholder="-" />
  31 + </template>
  32 + </uni-list-item>
  33 +
  34 + <uni-list-item title="区域">
  35 + <template v-slot:footer>
  36 + <uni-easyinput v-model="regionDisplay" :inputBorder="false" disabled placeholder="-" />
  37 + </template>
  38 + </uni-list-item>
  39 +
  40 + <uni-list-item title="日期">
  41 + <template v-slot:footer>
  42 + <uni-easyinput v-model="dateDisplay" :inputBorder="false" disabled placeholder="-" />
  43 + </template>
  44 + </uni-list-item>
  45 +
  46 + <uni-list-item title="报价时效">
  47 + <template v-slot:footer>
  48 + <uni-easyinput v-model="detail.validityPeriod" :inputBorder="false" disabled placeholder="-" />
  49 + </template>
  50 + </uni-list-item>
  51 +
  52 + <uni-list-item title="报价人">
  53 + <template v-slot:footer>
  54 + <uni-easyinput v-model="detail.quoterName" :inputBorder="false" disabled placeholder="-" />
  55 + </template>
  56 + </uni-list-item>
  57 +
  58 + <uni-list-item class="select-item" :class="typeof detail.longTermOperation === 'boolean' ? 'is-filled' : 'is-empty'">
  59 + <template v-slot:body>
  60 + <view class="item-title"><text>是否长单操作</text></view>
  61 + </template>
  62 + <template v-slot:footer>
  63 + <text class="readonly-text">{{ boolText(detail.longTermOperation) }}</text>
  64 + </template>
  65 + </uni-list-item>
  66 +
  67 + <uni-list-item class="select-item" :class="typeof detail.terminalCustomer === 'boolean' ? 'is-filled' : 'is-empty'">
  68 + <template v-slot:body>
  69 + <view class="item-title"><text>是否终端客户</text></view>
  70 + </template>
  71 + <template v-slot:footer>
  72 + <text class="readonly-text">{{ boolText(detail.terminalCustomer) }}</text>
  73 + </template>
  74 + </uni-list-item>
  75 +
  76 + <uni-list-item class="select-item" :class="typeof detail.priced === 'boolean' ? 'is-filled' : 'is-empty'">
  77 + <template v-slot:body>
  78 + <view class="item-title"><text>是否未点价</text></view>
  79 + </template>
  80 + <template v-slot:footer>
  81 + <text class="readonly-text">{{ boolText(detail.priced) }}</text>
  82 + </template>
  83 + </uni-list-item>
  84 +
  85 + <uni-list-item v-if="detail.priced === true" title="点价截止日期">
  86 + <template v-slot:footer>
  87 + <uni-easyinput v-model="pricingDeadlineDisplay" :inputBorder="false" disabled placeholder="-" />
  88 + </template>
  89 + </uni-list-item>
  90 +
  91 + <uni-list-item v-if="detail.priced === true" class="select-item" :class="typeof detail.temporaryPrice === 'boolean' ? 'is-filled' : 'is-empty'">
  92 + <template v-slot:body>
  93 + <view class="item-title"><text>是否暂定价</text></view>
  94 + </template>
  95 + <template v-slot:footer>
  96 + <text class="readonly-text">{{ boolText(detail.temporaryPrice) }}</text>
  97 + </template>
  98 + </uni-list-item>
  99 +
  100 + <uni-list-item class="select-item" :class="typeof detail.otherExpenseSettlement === 'boolean' ? 'is-filled' : 'is-empty'">
  101 + <template v-slot:body>
  102 + <view class="item-title"><text>其他费用凭票到厂另行结算</text></view>
  103 + </template>
  104 + <template v-slot:footer>
  105 + <text class="readonly-text">{{ boolText(detail.otherExpenseSettlement) }}</text>
  106 + </template>
  107 + </uni-list-item>
  108 +
  109 + <uni-list-item class="select-item" :class="detail.deliveryMode ? 'is-filled' : 'is-empty'">
  110 + <template v-slot:body>
  111 + <view class="item-title"><text>交提货方式</text></view>
  112 + </template>
  113 + <template v-slot:footer>
  114 + <text class="readonly-text">{{ getDeliveryMethodLabel() }}</text>
  115 + </template>
  116 + </uni-list-item>
  117 +
  118 + <uni-list-item class="select-item" :class="typeof detail.commodityBuyout === 'boolean' ? 'is-filled' : 'is-empty'">
  119 + <template v-slot:body>
  120 + <view class="item-title"><text>是否通货买断</text></view>
  121 + </template>
  122 + <template v-slot:footer>
  123 + <text class="readonly-text">{{ boolText(detail.commodityBuyout) }}</text>
  124 + </template>
  125 + </uni-list-item>
  126 +
  127 + <uni-list-item class="select-item" :class="typeof detail.qualityBuyout === 'boolean' ? 'is-filled' : 'is-empty'">
  128 + <template v-slot:body>
  129 + <view class="item-title"><text>是否质量买断</text></view>
  130 + </template>
  131 + <template v-slot:footer>
  132 + <text class="readonly-text">{{ boolText(detail.qualityBuyout) }}</text>
  133 + </template>
  134 + </uni-list-item>
  135 +
  136 + <uni-list-item class="select-item" :class="typeof detail.quantityBuyout === 'boolean' ? 'is-filled' : 'is-empty'">
  137 + <template v-slot:body>
  138 + <view class="item-title"><text>是否数量买断</text></view>
  139 + </template>
  140 + <template v-slot:footer>
  141 + <text class="readonly-text">{{ boolText(detail.quantityBuyout) }}</text>
  142 + </template>
  143 + </uni-list-item>
  144 +
  145 + <uni-list-item class="select-item" :class="typeof detail.doorDelivery === 'boolean' ? 'is-filled' : 'is-empty'">
  146 + <template v-slot:body>
  147 + <view class="item-title"><text>是否送货上门</text></view>
  148 + </template>
  149 + <template v-slot:footer>
  150 + <text class="readonly-text">{{ boolText(detail.doorDelivery) }}</text>
  151 + </template>
  152 + </uni-list-item>
  153 +
  154 + <uni-list-item title="另付运费">
  155 + <template v-slot:footer>
  156 + <uni-easyinput v-model="shippingCostDisplay" :inputBorder="false" disabled placeholder="-" />
  157 + </template>
  158 + </uni-list-item>
  159 +
  160 + <uni-list-item title="装车费">
  161 + <template v-slot:footer>
  162 + <uni-easyinput v-model="loadingFeeDisplay" :inputBorder="false" disabled placeholder="-" />
  163 + </template>
  164 + </uni-list-item>
  165 +
  166 + <uni-list-item title="开票品名">
  167 + <template v-slot:footer>
  168 + <uni-easyinput v-model="detail.invoiceProductName" :inputBorder="false" disabled placeholder="-" />
  169 + </template>
  170 + </uni-list-item>
  171 +
  172 + <uni-list-item title="交货日期">
  173 + <template v-slot:footer>
  174 + <uni-easyinput v-model="deliveryDateDisplay" :inputBorder="false" disabled placeholder="-" />
  175 + </template>
  176 + </uni-list-item>
  177 +
  178 + <uni-list-item title="结算方式">
  179 + <template v-slot:footer>
  180 + <uni-easyinput v-model="detail.settlementMethod" :inputBorder="false" disabled placeholder="-" />
  181 + </template>
  182 + </uni-list-item>
  183 +
  184 + <uni-list-item title="结算比例(%)">
  185 + <template v-slot:footer>
  186 + <uni-easyinput v-model="settlementRatioDisplay" :inputBorder="false" disabled placeholder="-" />
  187 + </template>
  188 + </uni-list-item>
  189 +
  190 + <uni-list-item title="价格表编号">
  191 + <template v-slot:footer>
  192 + <uni-easyinput v-model="priceTableDisplay" :inputBorder="false" disabled placeholder="-" />
  193 + </template>
  194 + </uni-list-item>
  195 +
  196 + <uni-list-item title="协调事由">
  197 + <template v-slot:footer>
  198 + <uni-easyinput v-model="detail.coordinationReason" :inputBorder="false" disabled placeholder="-" />
  199 + </template>
  200 + </uni-list-item>
  201 +
  202 + <uni-list-item title="票类状况">
  203 + <template v-slot:footer>
  204 + <uni-easyinput v-model="detail.invoiceTypeStatus" :inputBorder="false" disabled placeholder="-" />
  205 + </template>
  206 + </uni-list-item>
  207 +
  208 + <uni-list-item v-if="attachmentList.length" title="附件">
  209 + <template v-slot:footer>
  210 + <view class="attach-list">
  211 + <view v-for="(at, idx) in attachmentList" :key="idx" class="attach-item">
  212 + <text class="attach-name">{{ at.name }}</text>
  213 + <text class="attach-view" @click.stop="onPreviewAttachment(at)">查看</text>
  214 + </view>
  215 + </view>
  216 + </template>
  217 + </uni-list-item>
  218 +
  219 + <ProductRel
  220 + ref="productRel"
  221 + mode="edit"
  222 + :scene="workshopScene"
  223 + :list="lineList"
  224 + :product-options="productOptions"
  225 + :workshop-options="workshopOptions"
  226 + @change="onLineChange"
  227 + />
  228 + </uni-list>
  229 +
  230 + <view class="material-condition-section">
  231 + <view class="section-header">
  232 + <image class="opCollapse" src="/static/images/title.png" />
  233 + <text class="section-title">料质状况</text>
  234 + </view>
  235 + <view class="section-content">
  236 + <view class="form-row">
  237 + <text class="form-label">是否启用料质状况</text>
  238 + <text class="form-value">{{ boolText(detail.materialConditionEnabled) }}</text>
  239 + </view>
  240 + <template v-if="detail.materialConditionEnabled">
  241 + <view v-for="mc in materialConditionConfigs" :key="mc.field" class="material-group">
  242 + <text class="material-label">{{ mc.label }}</text>
  243 + <view class="tag-list">
  244 + <view
  245 + v-for="opt in getDictOptions(mc.dictCode)"
  246 + :key="opt.code"
  247 + class="tag-btn"
  248 + :class="{ active: isTagSelected(mc.field, opt.code) }"
  249 + >
  250 + {{ opt.name }}
  251 + </view>
  252 + </view>
  253 + </view>
  254 + </template>
  255 + </view>
  256 + </view>
  257 +
  258 + <view class="remark-section">
  259 + <view class="section-header">
  260 + <image class="opCollapse" src="/static/images/title.png" />
  261 + <text class="section-title">备注</text>
  262 + </view>
  263 + <view class="section-content">
  264 + <text class="remark-text">{{ safeText(detail.remark) }}</text>
  265 + </view>
  266 + </view>
  267 +
  268 + <view class="footer-tip">提示:仅「选择分厂」字段可编辑,其余字段自动带出且不可编辑</view>
  269 + </scroll-view>
  270 +
  271 + <view class="bottom-bar">
  272 + <button class="btn btn-outline" @click="onBack">取消</button>
  273 + <button class="btn btn-primary" @click="onSubmit">保存</button>
  274 + </view>
  275 + </view>
  276 +</template>
  277 +
  278 +<script>
  279 +import ProductRel from './productRel.vue'
  280 +import { procurementPlanDetailApi, procurementPlanUpdateApi } from '@/api/procure-manage/procurementPlan.js'
  281 +import { breedRelationshipQueryApi, workshopQueryApi, priceTableQueryApi, priceTableDetailApi } from '@/api/devManage.js'
  282 +import { getDicByCodes } from '@/utils/dic.js'
  283 +
  284 +export default {
  285 + name: 'DomesticPlanSelectWorkshop',
  286 + components: { ProductRel },
  287 + data() {
  288 + return {
  289 + workshopScene: 'selectWorkshop',
  290 + id: '',
  291 + detail: {},
  292 + attachmentList: [],
  293 + materialConditionConfigs: [
  294 + { field: 'materialConditionCopperRice', label: '铜米', dictCode: 'COPPER_RICE' },
  295 + { field: 'materialConditionBrightCopper', label: '光亮铜', dictCode: 'BRIGHT_COPPER' },
  296 + { field: 'materialConditionFirstClass', label: '一特', dictCode: 'FIRST_CLASS' },
  297 + { field: 'materialConditionSecondClass', label: '二特', dictCode: 'SECOND_CLASS' },
  298 + { field: 'materialConditionTinCoated', label: '镀锡紫', dictCode: 'TIN_COATED' },
  299 + { field: 'materialConditionScrap', label: '角料', dictCode: 'SCRAP' },
  300 + { field: 'materialConditionWhiteYellow', label: '镀白黄', dictCode: 'WHITE_YELLOW' },
  301 + { field: 'materialConditionPhosphorCopper', label: '磷铜', dictCode: 'PHOSPHOR_COPPER' }
  302 + ],
  303 + dictData: {},
  304 + lineList: [],
  305 + productOptions: [],
  306 + workshopOptions: [],
  307 + priceTableList: []
  308 + }
  309 + },
  310 + computed: {
  311 + companyNameDisplay() {
  312 + var d = this.detail || {}
  313 + return d.companyShortName != null ? d.companyShortName : (d.companyName != null ? d.companyName : '')
  314 + },
  315 + purchaseDepartmentDisplay() {
  316 + var d = this.detail || {}
  317 + return d.purchaseDepartmentName != null ? d.purchaseDepartmentName : (d.purchaseDepartment != null ? d.purchaseDepartment : '')
  318 + },
  319 + regionDisplay() {
  320 + var d = this.detail || {}
  321 + return d.regionName != null ? d.regionName : (d.region != null ? d.region : '')
  322 + },
  323 + dateDisplay() {
  324 + var d = this.detail || {}
  325 + var s = d.date == null ? '' : String(d.date).trim()
  326 + if (!s) return ''
  327 + return s.length >= 10 ? s.slice(0, 10) : s
  328 + },
  329 + pricingDeadlineDisplay() {
  330 + var d = this.detail || {}
  331 + var s = d.pricingDeadline == null ? '' : String(d.pricingDeadline).trim()
  332 + if (!s) return ''
  333 + return s.length >= 10 ? s.slice(0, 10) : s
  334 + },
  335 + deliveryDateDisplay() {
  336 + var d = this.detail || {}
  337 + var s = d.deliveryDate == null ? '' : String(d.deliveryDate).trim()
  338 + if (!s) return ''
  339 + return s.length >= 10 ? s.slice(0, 10) : s
  340 + },
  341 + loadingFeeDisplay() {
  342 + var v = (this.detail || {}).loadingFee
  343 + if (v == null || v === '') return ''
  344 + var n = Number(v)
  345 + if (isNaN(n)) return String(v)
  346 + return n.toLocaleString('zh-CN', { maximumFractionDigits: 4 })
  347 + },
  348 + shippingCostDisplay() {
  349 + var v = (this.detail || {}).shippingCost
  350 + if (v == null || v === '') return ''
  351 + var n = Number(v)
  352 + if (isNaN(n)) return String(v)
  353 + return n.toLocaleString('zh-CN', { maximumFractionDigits: 2 })
  354 + },
  355 + settlementRatioDisplay() {
  356 + var v = (this.detail || {}).settlementRatio
  357 + if (v == null || v === '') return ''
  358 + var n = Number(v)
  359 + if (isNaN(n)) return String(v)
  360 + return n.toLocaleString('zh-CN', { maximumFractionDigits: 4 })
  361 + },
  362 + priceTableDisplay() {
  363 + var d = this.detail || {}
  364 + var code = d.priceTableCode != null ? d.priceTableCode : (d.priceId != null ? d.priceId : (d.priceTableId != null ? d.priceTableId : ''))
  365 + if (code == null || String(code).trim() === '') return ''
  366 + var sc = String(code)
  367 + var list = Array.isArray(this.priceTableList) ? this.priceTableList : []
  368 + for (var i = 0; i < list.length; i++) {
  369 + var o = list[i]
  370 + var id = String(o.id || o.priceId || o.value || '')
  371 + var c = String(o.priceTableCode || o.code || o.priceCode || '')
  372 + if (id === sc || c === sc) {
  373 + var name = o.priceTableName || o.tableName || o.name || o.label || ''
  374 + var cd = o.priceTableCode || o.code || c
  375 + return (name ? name + ' ' : '') + (cd ? cd : '') || ''
  376 + }
  377 + }
  378 + var fallbackName = d.priceTableName != null ? d.priceTableName : ''
  379 + var fallbackCode = d.priceTableCode != null ? d.priceTableCode : sc
  380 + return (fallbackName ? fallbackName + ' ' : '') + (fallbackCode ? fallbackCode : '') || ''
  381 + }
  382 + },
  383 + onLoad(query) {
  384 + this.id = (query && query.id) ? query.id : ''
  385 + this.loadDictData()
  386 + this.loadRefData()
  387 + if (this.id) {
  388 + this.loadSourceDetail()
  389 + }
  390 + },
  391 + methods: {
  392 + toNumber(v) {
  393 + if (v == null || v === '') return 0
  394 + var n = Number(v)
  395 + return isNaN(n) ? 0 : n
  396 + },
  397 + toFixed(v, n) {
  398 + var num = Number(v)
  399 + if (isNaN(num)) return ''
  400 + return num.toFixed(n)
  401 + },
  402 + safeText(v) {
  403 + var s = v == null ? '' : String(v)
  404 + return s.trim() ? s : '-'
  405 + },
  406 + boolText(v) {
  407 + if (typeof v !== 'boolean') return '-'
  408 + return v ? '是' : '否'
  409 + },
  410 + async loadDictData() {
  411 + var codes = this.materialConditionConfigs.map(function (mc) { return mc.dictCode }).concat(['DELIVERY_METHOD'])
  412 + try {
  413 + var results = await getDicByCodes(codes)
  414 + this.dictData = results || {}
  415 + } catch (e) {
  416 + this.dictData = {}
  417 + }
  418 + },
  419 + getDictOptions(dictCode) {
  420 + var data = this.dictData[dictCode]
  421 + return (data && data.data) ? data.data : []
  422 + },
  423 + isTagSelected(field, code) {
  424 + var selected = this.detail ? this.detail[field] : undefined
  425 + if (Array.isArray(selected)) return selected.indexOf(code) >= 0
  426 + if (typeof selected === 'string' && selected) {
  427 + return selected.split(',').indexOf(code) >= 0
  428 + }
  429 + return false
  430 + },
  431 + getDeliveryMethodLabel() {
  432 + var opts = this.getDictOptions('DELIVERY_METHOD') || []
  433 + var v = String(this.detail.deliveryMode != null ? this.detail.deliveryMode : '')
  434 + if (!v) return '-'
  435 + for (var i = 0; i < opts.length; i++) {
  436 + var o = opts[i]
  437 + var code = o.code != null ? String(o.code) : (o.value != null ? String(o.value) : '')
  438 + if (code === v) return o.name || o.label || '-'
  439 + }
  440 + return v
  441 + },
  442 + buildAttachmentList() {
  443 + var ids = this.detail.attachmentFileIds
  444 + var names = this.detail.attachmentFileNames
  445 + var idArr = (typeof ids === 'string' && ids) ? ids.split(',').filter(Boolean) : (Array.isArray(ids) ? ids : [])
  446 + var nameArr = (typeof names === 'string' && names) ? names.split(',').filter(Boolean) : (Array.isArray(names) ? names : [])
  447 + var len = Math.max(idArr.length, nameArr.length)
  448 + var list = []
  449 + for (var i = 0; i < len; i++) {
  450 + list.push({ id: idArr[i] || '', name: nameArr[i] || ('附件' + (i + 1)) })
  451 + }
  452 + this.attachmentList = list
  453 + },
  454 + onPreviewAttachment(at) {
  455 + if (!at || !at.id) {
  456 + uni.showToast({ title: '无法查看', icon: 'none' })
  457 + return
  458 + }
  459 + uni.showToast({ title: '查看附件 ' + at.name, icon: 'none' })
  460 + },
  461 + async loadSourceDetail() {
  462 + if (!this.id) return
  463 + try {
  464 + var res = await procurementPlanDetailApi({ id: this.id })
  465 + var d = (res && res.data) ? res.data : {}
  466 + var self = this
  467 + this.materialConditionConfigs.forEach(function (mc) {
  468 + var v = d[mc.field]
  469 + if (typeof v === 'string' && v) {
  470 + d[mc.field] = v.split(',').filter(Boolean)
  471 + } else if (!Array.isArray(v)) {
  472 + d[mc.field] = []
  473 + }
  474 + })
  475 + this.detail = d
  476 + this.lineList = Array.isArray(d.procurementPlanLineList) ? d.procurementPlanLineList.slice() : []
  477 + this.buildAttachmentList()
  478 + this.fillLineNames()
  479 + await this.fillPriceByDetailPriceId()
  480 + } catch (e) {
  481 + uni.showToast({ title: '加载详情失败', icon: 'none' })
  482 + }
  483 + },
  484 + async loadRefData() {
  485 + try {
  486 + var results = await Promise.all([
  487 + this.$pCall(priceTableQueryApi, { pageIndex: 1, pageSize: 1000 }),
  488 + this.$pCall(breedRelationshipQueryApi, { pageIndex: 1, pageSize: 1000 }),
  489 + this.$pCall(workshopQueryApi, { pageIndex: 1, pageSize: 1000 })
  490 + ])
  491 + var r0 = (results[0] && results[0].data) ? results[0].data : {}
  492 + var r1 = (results[1] && results[1].data) ? results[1].data : {}
  493 + var r2 = (results[2] && results[2].data) ? results[2].data : {}
  494 + this.priceTableList = (r0.datas && Array.isArray(r0.datas)) ? r0.datas : ((r0.list && Array.isArray(r0.list)) ? r0.list : ((r0.records && Array.isArray(r0.records)) ? r0.records : []))
  495 + this.productOptions = (r1.datas && Array.isArray(r1.datas)) ? r1.datas : ((r1.list && Array.isArray(r1.list)) ? r1.list : ((r1.records && Array.isArray(r1.records)) ? r1.records : []))
  496 + this.workshopOptions = (r2.datas && Array.isArray(r2.datas)) ? r2.datas : ((r2.list && Array.isArray(r2.list)) ? r2.list : ((r2.records && Array.isArray(r2.records)) ? r2.records : []))
  497 + this.fillLineNames()
  498 + } catch (e) {
  499 + this.priceTableList = []
  500 + this.productOptions = []
  501 + this.workshopOptions = []
  502 + }
  503 + },
  504 + $pCall(fn, params) {
  505 + try { return Promise.resolve(fn(params)) } catch (e) { return Promise.reject(e) }
  506 + },
  507 + onLineChange(list) {
  508 + this.lineList = Array.isArray(list) ? list.slice() : []
  509 + this.fillLineNames()
  510 + },
  511 + fillLineNames() {
  512 + if (!Array.isArray(this.lineList) || !this.lineList.length) return
  513 + var pOpts = Array.isArray(this.productOptions) ? this.productOptions : []
  514 + var wOpts = Array.isArray(this.workshopOptions) ? this.workshopOptions : []
  515 + var self = this
  516 + this.lineList = this.lineList.map(function (row) {
  517 + var r = Object.assign({}, row)
  518 + var pid = r.productId != null ? String(r.productId) : ''
  519 + var wid = r.workshopId != null ? String(r.workshopId) : ''
  520 + if ((r.productName == null || String(r.productName).trim() === '') && pid) {
  521 + var p = pOpts.find(function (o) {
  522 + var id = o.id != null ? String(o.id) : (o.productId != null ? String(o.productId) : (o.value != null ? String(o.value) : ''))
  523 + return id === pid
  524 + })
  525 + if (p) {
  526 + r.productName = p.name != null ? p.name : (p.productName != null ? p.productName : (p.label != null ? p.label : ''))
  527 + }
  528 + }
  529 + if ((r.workshopName == null || String(r.workshopName).trim() === '') && wid) {
  530 + var w = wOpts.find(function (o) {
  531 + var id = o.id != null ? String(o.id) : (o.workshopId != null ? String(o.workshopId) : (o.value != null ? String(o.value) : ''))
  532 + return id === wid
  533 + })
  534 + if (w) {
  535 + r.workshopName = w.name != null ? w.name : (w.workshopName != null ? w.workshopName : (w.label != null ? w.label : ''))
  536 + }
  537 + }
  538 + return r
  539 + })
  540 + this.notifyLineChange()
  541 + },
  542 + notifyLineChange() {
  543 + try {
  544 + var pr = this.$refs.productRel
  545 + if (pr) {
  546 + if (typeof pr.initItems === 'function') {
  547 + pr._initialized = false
  548 + pr.initItems()
  549 + var self = this
  550 + this.$nextTick(function () {
  551 + pr._initialized = true
  552 + })
  553 + }
  554 + }
  555 + } catch (e) {
  556 + console.warn('notifyLineChange error:', e)
  557 + }
  558 + },
  559 + async fillPriceByDetailPriceId() {
  560 + var d = this.detail || {}
  561 + var priceId = d.priceId != null ? d.priceId : (d.priceTableId != null ? d.priceTableId : '')
  562 + if (!priceId) return
  563 + await this.fetchPriceDetailAndApply(priceId)
  564 + },
  565 + async fetchPriceDetailAndApply(priceId) {
  566 + if (!priceId) return
  567 + var self = this
  568 + try {
  569 + var res = await priceTableDetailApi({ id: priceId })
  570 + var d = (res && res.data) ? res.data : {}
  571 + var priceLines = self.extractPriceLines(d)
  572 + self.applyPriceLinesToLines(priceLines)
  573 + } catch (e) {
  574 + return
  575 + }
  576 + },
  577 + extractPriceLines(priceDetail) {
  578 + if (!priceDetail || typeof priceDetail !== 'object') return []
  579 + var candidates = [
  580 + priceDetail.procurementPriceLineList,
  581 + priceDetail.priceLineList,
  582 + priceDetail.priceList,
  583 + priceDetail.lineList,
  584 + priceDetail.datas,
  585 + priceDetail.records,
  586 + priceDetail.list
  587 + ]
  588 + for (var i = 0; i < candidates.length; i++) {
  589 + if (Array.isArray(candidates[i]) && candidates[i].length) return candidates[i]
  590 + }
  591 + for (var k in priceDetail) {
  592 + if (!Object.prototype.hasOwnProperty.call(priceDetail, k)) continue
  593 + if (Array.isArray(priceDetail[k])) {
  594 + var arr = priceDetail[k]
  595 + if (arr.length && arr[0] && typeof arr[0] === 'object' && (arr[0].price != null || arr[0].productId != null || arr[0].breedId != null || arr[0].varietyId != null)) {
  596 + return arr
  597 + }
  598 + }
  599 + }
  600 + return []
  601 + },
  602 + buildPriceMap(priceLines) {
  603 + var m = {}
  604 + if (!Array.isArray(priceLines)) return m
  605 + for (var i = 0; i < priceLines.length; i++) {
  606 + var line = priceLines[i] || {}
  607 + if (line.price == null) continue
  608 + var key = String(line.productId || line.breedId || line.varietyId || line.id || ('__idx_' + i))
  609 + m[key] = { price: Number(line.price), meta: line }
  610 + }
  611 + return m
  612 + },
  613 + applyPriceLinesToLines(priceLines) {
  614 + if (!Array.isArray(this.lineList)) return
  615 + var priceMap = this.buildPriceMap(priceLines)
  616 + var self = this
  617 + this.lineList = this.lineList.map(function (row) {
  618 + var pid = String(row.productId || row.breedId || row.varietyId || '')
  619 + var price = null
  620 + if (pid && priceMap[pid] != null) {
  621 + price = priceMap[pid].price
  622 + } else {
  623 + var pname = (row.productName || '').trim()
  624 + if (pname) {
  625 + for (var k in priceMap) {
  626 + if (!Object.prototype.hasOwnProperty.call(priceMap, k)) continue
  627 + var meta = priceMap[k] && priceMap[k].meta ? priceMap[k].meta : null
  628 + if (meta && ((meta.productName && meta.productName === pname) || (meta.breedName && meta.breedName === pname) || (meta.varietyName && meta.varietyName === pname))) {
  629 + price = priceMap[k].price
  630 + break
  631 + }
  632 + }
  633 + }
  634 + }
  635 + if (price != null) {
  636 + row.priceListPrices = self.toFixed(price, 2)
  637 + } else {
  638 + row.priceListPrices = ''
  639 + }
  640 + return row
  641 + })
  642 + this.fillLineNames()
  643 + },
  644 + joinStr(arr) {
  645 + if (Array.isArray(arr)) return arr.filter(Boolean).join(',')
  646 + if (typeof arr === 'string') return arr
  647 + return ''
  648 + },
  649 + toBool(v, fallback) {
  650 + if (typeof v === 'boolean') return v
  651 + if (v === 'true' || v === '1' || v === 1) return true
  652 + if (v === 'false' || v === '0' || v === 0) return false
  653 + return typeof fallback === 'boolean' ? fallback : false
  654 + },
  655 + pickId(v) {
  656 + if (v == null || v === '') return ''
  657 + if (typeof v === 'string' || typeof v === 'number') return String(v)
  658 + if (typeof v === 'object') {
  659 + return String(v.id != null ? v.id : (v.value != null ? v.value : (v.code != null ? v.code : '')))
  660 + }
  661 + return String(v)
  662 + },
  663 + buildPayload() {
  664 + var self = this
  665 + var d = this.detail || {}
  666 + var lines = Array.isArray(this.lineList) ? this.lineList.slice() : []
  667 + var lineList = lines.map(function (row, i) {
  668 + var r = row || {}
  669 + var itemId = r.itemId || (r._key != null ? r._key : (r.id ? String(r.id) : ('line-' + Date.now() + '-' + i)))
  670 + return {
  671 + id: r.id != null ? r.id : '',
  672 + itemId: itemId,
  673 + procurementPlanId: (r.procurementPlanId != null ? r.procurementPlanId : (self.id || d.id || '')),
  674 + productId: self.pickId(r.productId),
  675 + materialType: r.materialType != null ? String(r.materialType) : '',
  676 + quantity: self.toNumber(r.quantity),
  677 + actualPurchasePrice: self.toNumber(r.actualPurchasePrice),
  678 + includingShippingPrice: self.toNumber(r.includingShippingPrice),
  679 + priceListPrices: self.toNumber(r.priceListPrices),
  680 + coordinationRange: self.toNumber(r.coordinationRange),
  681 + estimatedLoss: self.toNumber(r.estimatedLoss),
  682 + applicationAttrition: self.toNumber(r.applicationAttrition),
  683 + workshopId: self.pickId(r.workshopId)
  684 + }
  685 + })
  686 + var mcFields = this.materialConditionConfigs.map(function (mc) { return mc.field })
  687 + var materialPayload = {}
  688 + mcFields.forEach(function (f) {
  689 + materialPayload[f] = self.joinStr(d[f])
  690 + })
  691 + var body = {
  692 + id: this.id || d.id || '',
  693 + code: d.code != null ? String(d.code) : '',
  694 + companyShortId: this.pickId(d.companyShortId || d.companyId),
  695 + supplierId: this.pickId(d.supplierId),
  696 + purchaseDepartment: this.pickId(d.purchaseDepartmentId || d.purchaseDepartment),
  697 + region: this.pickId(d.regionId || d.region),
  698 + date: d.date != null ? String(d.date) : '',
  699 + validityPeriod: d.validityPeriod != null ? String(d.validityPeriod) : '',
  700 + quoterId: this.pickId(d.quoterId),
  701 + quoterName: d.quoterName != null ? String(d.quoterName) : '',
  702 + longTermOperation: this.toBool(d.longTermOperation, false),
  703 + terminalCustomer: this.toBool(d.terminalCustomer, false),
  704 + priced: this.toBool(d.priced, false),
  705 + pricingDeadline: d.pricingDeadline != null ? String(d.pricingDeadline) : '',
  706 + temporaryPrice: this.toBool(d.temporaryPrice, false),
  707 + otherExpenseSettlement: this.toBool(d.otherExpenseSettlement, false),
  708 + deliveryMode: d.deliveryMode != null ? String(d.deliveryMode) : '',
  709 + commodityBuyout: this.toBool(d.commodityBuyout, false),
  710 + qualityBuyout: this.toBool(d.qualityBuyout, false),
  711 + quantityBuyout: this.toBool(d.quantityBuyout, false),
  712 + doorDelivery: this.toBool(d.doorDelivery, false),
  713 + shippingCost: this.toNumber(d.shippingCost),
  714 + loadingFee: this.toNumber(d.loadingFee),
  715 + invoiceProductName: d.invoiceProductName != null ? String(d.invoiceProductName) : '',
  716 + deliveryDate: d.deliveryDate != null ? String(d.deliveryDate) : '',
  717 + settlementMethod: d.settlementMethod != null ? String(d.settlementMethod) : '',
  718 + settlementRatio: this.toNumber(d.settlementRatio),
  719 + priceId: this.pickId(d.priceId || d.priceTableId),
  720 + coordinationReason: d.coordinationReason != null ? String(d.coordinationReason) : '',
  721 + invoiceTypeStatus: d.invoiceTypeStatus != null ? String(d.invoiceTypeStatus) : '',
  722 + remark: d.remark != null ? String(d.remark) : '',
  723 + materialConditionEnabled: this.toBool(d.materialConditionEnabled, false),
  724 + approvalStatus: d.approvalStatus != null ? String(d.approvalStatus) : '',
  725 + processStatus: d.processStatus != null ? String(d.processStatus) : '',
  726 + processNode: d.processNode != null ? String(d.processNode) : '',
  727 + procurementPlanLineList: lineList
  728 + }
  729 + Object.keys(materialPayload).forEach(function (k) {
  730 + body[k] = materialPayload[k]
  731 + })
  732 + return body
  733 + },
  734 + onBack() {
  735 + uni.navigateBack({ delta: 1 })
  736 + },
  737 + validateForm() {
  738 + var pr = this.$refs.productRel
  739 + if (pr && typeof pr.validate === 'function') {
  740 + var ok = pr.validate()
  741 + if (!ok) return false
  742 + }
  743 + return true
  744 + },
  745 + async onSubmit() {
  746 + if (!this.validateForm()) return
  747 + var payload = this.buildPayload()
  748 + try {
  749 + await procurementPlanUpdateApi(payload)
  750 + uni.showToast({ title: '保存成功', icon: 'none' })
  751 + try {
  752 + uni.setStorageSync('DOMESTIC_PLAN_LIST_NEED_REFRESH', '1')
  753 + uni.setStorageSync('SUPPLE_PLAN_LIST_NEED_REFRESH', '1')
  754 + uni.setStorageSync('DOMESTIC_PLAN_DETAIL_NEED_REFRESH', '1')
  755 + } catch (e) {}
  756 + var self = this
  757 + setTimeout(function () {
  758 + uni.navigateBack({ delta: 1 })
  759 + }, 400)
  760 + } catch (e) {
  761 + uni.showToast({ title: (e && (e.msg || e.message)) ? (e.msg || e.message) : '保存失败', icon: 'none' })
  762 + }
  763 + }
  764 + }
  765 +}
  766 +</script>
  767 +
  768 +<style lang="scss" scoped>
  769 +.page {
  770 + display: flex;
  771 + flex-direction: column;
  772 + height: 100%;
  773 +}
  774 +.scroll {
  775 + flex: 1;
  776 + padding: 12rpx 0 392rpx !important;
  777 +}
  778 +.bottom-bar {
  779 + z-index: 2;
  780 + position: fixed;
  781 + left: 0;
  782 + right: 0;
  783 + bottom: 0;
  784 + padding: 32rpx;
  785 + padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
  786 + background: #fff;
  787 + box-shadow: 0 -8rpx 24rpx rgba(0, 0, 0, 0.06);
  788 + display: flex;
  789 + gap: 24rpx;
  790 + .btn {
  791 + flex: 1;
  792 + height: 80rpx;
  793 + line-height: 80rpx;
  794 + border-radius: 12rpx;
  795 + font-size: 32rpx;
  796 + }
  797 + .btn-outline {
  798 + background: #fff;
  799 + color: $theme-primary;
  800 + border: 2rpx solid $theme-primary;
  801 + }
  802 + .btn-primary {
  803 + background: $theme-primary;
  804 + color: #fff;
  805 + border: 2rpx solid $theme-primary;
  806 + }
  807 +}
  808 +
  809 +::v-deep .uni-list {
  810 + .uni-easyinput {
  811 + display: flex;
  812 + .uni-input-input {
  813 + color: rgba(0, 0, 0, 0.9);
  814 + }
  815 + }
  816 + .uni-input-placeholder { z-index: 1; }
  817 + .uni-input-input { background-color: #ffffff; }
  818 + background: transparent;
  819 + &-item {
  820 + &__extra-text { font-size: 32rpx; }
  821 + &__content-title {
  822 + font-size: 32rpx;
  823 + color: rgba(0, 0, 0, 0.9);
  824 + }
  825 + &__container {
  826 + padding: 32rpx;
  827 + .uni-easyinput {
  828 + &__placeholder-class {
  829 + font-size: 32rpx;
  830 + color: rgba(0, 0, 0, 0.4);
  831 + }
  832 + &__content {
  833 + border: none;
  834 + background-color: #ffffff !important;
  835 + &-input {
  836 + padding-left: 0 !important;
  837 + height: 48rpx;
  838 + line-height: 48rpx;
  839 + font-size: 32rpx;
  840 + }
  841 + .content-clear-icon {
  842 + font-size: 44rpx !important;
  843 + }
  844 + }
  845 + }
  846 + .item-title,
  847 + .uni-list-item__content {
  848 + flex: none;
  849 + min-height: 48rpx;
  850 + line-height: 48rpx;
  851 + font-size: 32rpx;
  852 + position: relative;
  853 + width: 240rpx;
  854 + margin-right: 32rpx;
  855 + color: rgba(0, 0, 0, 0.9);
  856 + .required {
  857 + color: red;
  858 + position: absolute;
  859 + top: 50%;
  860 + transform: translateY(-50%);
  861 + left: -16rpx;
  862 + }
  863 + }
  864 + }
  865 + &.select-item {
  866 + &.is-empty {
  867 + .uni-list-item__extra-text {
  868 + color: rgba(0, 0, 0, 0.4) !important;
  869 + }
  870 + }
  871 + &.is-filled {
  872 + .uni-list-item__extra-text {
  873 + color: rgba(0, 0, 0, 0.9) !important;
  874 + }
  875 + }
  876 + .serial-number-row {
  877 + display: flex;
  878 + align-items: center;
  879 + }
  880 + }
  881 + }
  882 +}
  883 +
  884 +.readonly-text {
  885 + color: rgba(0, 0, 0, 0.9);
  886 + font-size: 32rpx;
  887 + line-height: 48rpx;
  888 + text-align: right;
  889 + white-space: pre-wrap;
  890 + word-break: break-all;
  891 +}
  892 +.value-code {
  893 + width: 260rpx;
  894 + text-align: left;
  895 +}
  896 +
  897 +.attach-list {
  898 + flex: 1;
  899 + display: flex;
  900 + flex-direction: column;
  901 + align-items: flex-end;
  902 + gap: 8rpx;
  903 +}
  904 +.attach-item {
  905 + display: flex;
  906 + align-items: center;
  907 + gap: 16rpx;
  908 + .attach-name { font-size: 26rpx; color: rgba(0,0,0,0.9); }
  909 + .attach-view { font-size: 26rpx; color: $theme-primary; }
  910 +}
  911 +
  912 +.material-condition-section,
  913 +.remark-section {
  914 + margin-top: 20rpx;
  915 + background: #fff;
  916 +}
  917 +.section-header {
  918 + display: flex;
  919 + align-items: center;
  920 + padding: 24rpx 32rpx;
  921 + border-bottom: 1rpx solid #f0f0f0;
  922 +}
  923 +.section-title {
  924 + font-size: 32rpx;
  925 + color: rgba(0, 0, 0, 0.9);
  926 + font-weight: 600;
  927 +}
  928 +.opCollapse {
  929 + width: 32rpx;
  930 + height: 28rpx;
  931 + margin-right: 16rpx;
  932 + margin-top: 8rpx;
  933 +}
  934 +.section-content {
  935 + padding: 32rpx;
  936 +}
  937 +.form-row {
  938 + display: flex;
  939 + align-items: center;
  940 + justify-content: space-between;
  941 + margin-bottom: 24rpx;
  942 +}
  943 +.form-row:last-child { margin-bottom: 0; }
  944 +.form-label {
  945 + font-size: 32rpx;
  946 + color: rgba(0, 0, 0, 0.9);
  947 +}
  948 +.form-value {
  949 + font-size: 32rpx;
  950 + color: rgba(0, 0, 0, 0.9);
  951 +}
  952 +.material-group {
  953 + margin-top: 32rpx;
  954 +}
  955 +.material-label {
  956 + display: block;
  957 + font-size: 30rpx;
  958 + color: rgba(0, 0, 0, 0.9);
  959 + margin-bottom: 20rpx;
  960 +}
  961 +.tag-list {
  962 + display: flex;
  963 + flex-wrap: wrap;
  964 + gap: 16rpx;
  965 +}
  966 +.tag-btn {
  967 + padding: 12rpx 24rpx;
  968 + border: 1rpx solid #ddd;
  969 + border-radius: 8rpx;
  970 + font-size: 28rpx;
  971 + color: #666;
  972 + background: #fff;
  973 +}
  974 +.tag-btn.active {
  975 + background: $theme-primary;
  976 + border-color: $theme-primary;
  977 + color: #fff;
  978 +}
  979 +.remark-text {
  980 + font-size: 30rpx;
  981 + color: rgba(0,0,0,0.9);
  982 + line-height: 1.6;
  983 + white-space: pre-wrap;
  984 +}
  985 +.footer-tip {
  986 + padding: 24rpx 32rpx;
  987 + color: rgba(0,0,0,0.45);
  988 + font-size: 24rpx;
  989 +}
  990 +
  991 +::v-deep .uni-easyinput {
  992 + width: 100%;
  993 + &__content {
  994 + background: #f8f8f8 !important;
  995 + border-radius: 8rpx;
  996 + &-input { font-size: 30rpx; }
  997 + }
  998 +}
  999 +</style>
... ...
  1 +<template>
  2 + <view class="page">
  3 + <scroll-view scroll-y class="scroll">
  4 + <uni-list>
  5 + <uni-list-item class="select-item" :class="detail.code ? 'is-filled' : 'is-empty'">
  6 + <template v-slot:body>
  7 + <view class="item-title"><text>编号</text></view>
  8 + </template>
  9 + <template v-slot:footer>
  10 + <view class="serial-number-row">
  11 + <text class="readonly-text value-code">{{ safeText(detail.code) }}</text>
  12 + </view>
  13 + </template>
  14 + </uni-list-item>
  15 +
  16 + <uni-list-item class="select-item" :class="form.companyId ? 'is-filled' : 'is-empty'" clickable
  17 + @click="openCompanySheet" :rightText="form.companyName || '请选择公司名称'" showArrow>
  18 + <template v-slot:body>
  19 + <view class="item-title"><text class="required">*</text><text>公司名称</text></view>
  20 + </template>
  21 + </uni-list-item>
  22 +
  23 + <uni-list-item title="供应商名称">
  24 + <template v-slot:footer>
  25 + <uni-easyinput v-model="detail.supplierName" :inputBorder="false" disabled placeholder="-" />
  26 + </template>
  27 + </uni-list-item>
  28 +
  29 + <uni-list-item title="采购处">
  30 + <template v-slot:footer>
  31 + <uni-easyinput v-model="purchaseDepartmentDisplay" :inputBorder="false" disabled placeholder="-" />
  32 + </template>
  33 + </uni-list-item>
  34 +
  35 + <uni-list-item title="区域">
  36 + <template v-slot:footer>
  37 + <uni-easyinput v-model="regionDisplay" :inputBorder="false" disabled placeholder="-" />
  38 + </template>
  39 + </uni-list-item>
  40 +
  41 + <uni-list-item>
  42 + <template v-slot:body>
  43 + <view class="item-title"><text class="required">*</text><text>日期</text></view>
  44 + </template>
  45 + <template v-slot:footer>
  46 + <uni-datetime-picker type="date" v-model="form.date" return-type="string" />
  47 + </template>
  48 + </uni-list-item>
  49 +
  50 + <uni-list-item title="报价时效">
  51 + <template v-slot:footer>
  52 + <uni-easyinput v-model="detail.validityPeriod" :inputBorder="false" disabled placeholder="-" />
  53 + </template>
  54 + </uni-list-item>
  55 +
  56 + <uni-list-item title="报价人">
  57 + <template v-slot:footer>
  58 + <uni-easyinput v-model="detail.quoterName" :inputBorder="false" disabled placeholder="-" />
  59 + </template>
  60 + </uni-list-item>
  61 +
  62 + <uni-list-item class="select-item" :class="typeof detail.longTermOperation === 'boolean' ? 'is-filled' : 'is-empty'">
  63 + <template v-slot:body>
  64 + <view class="item-title"><text>是否长单操作</text></view>
  65 + </template>
  66 + <template v-slot:footer>
  67 + <text class="readonly-text">{{ boolText(detail.longTermOperation) }}</text>
  68 + </template>
  69 + </uni-list-item>
  70 +
  71 + <uni-list-item class="select-item" :class="typeof detail.terminalCustomer === 'boolean' ? 'is-filled' : 'is-empty'">
  72 + <template v-slot:body>
  73 + <view class="item-title"><text>是否终端客户</text></view>
  74 + </template>
  75 + <template v-slot:footer>
  76 + <text class="readonly-text">{{ boolText(detail.terminalCustomer) }}</text>
  77 + </template>
  78 + </uni-list-item>
  79 +
  80 + <uni-list-item class="select-item" :class="typeof detail.priced === 'boolean' ? 'is-filled' : 'is-empty'">
  81 + <template v-slot:body>
  82 + <view class="item-title"><text>是否未点价</text></view>
  83 + </template>
  84 + <template v-slot:footer>
  85 + <text class="readonly-text">{{ boolText(detail.priced) }}</text>
  86 + </template>
  87 + </uni-list-item>
  88 +
  89 + <uni-list-item v-if="detail.priced === true" title="点价截止日期">
  90 + <template v-slot:footer>
  91 + <uni-easyinput v-model="pricingDeadlineDisplay" :inputBorder="false" disabled placeholder="-" />
  92 + </template>
  93 + </uni-list-item>
  94 +
  95 + <uni-list-item v-if="detail.priced === true" class="select-item" :class="typeof detail.temporaryPrice === 'boolean' ? 'is-filled' : 'is-empty'">
  96 + <template v-slot:body>
  97 + <view class="item-title"><text>是否暂定价</text></view>
  98 + </template>
  99 + <template v-slot:footer>
  100 + <text class="readonly-text">{{ boolText(detail.temporaryPrice) }}</text>
  101 + </template>
  102 + </uni-list-item>
  103 +
  104 + <uni-list-item class="select-item" :class="typeof detail.otherExpenseSettlement === 'boolean' ? 'is-filled' : 'is-empty'">
  105 + <template v-slot:body>
  106 + <view class="item-title"><text>其他费用凭票到厂另行结算</text></view>
  107 + </template>
  108 + <template v-slot:footer>
  109 + <text class="readonly-text">{{ boolText(detail.otherExpenseSettlement) }}</text>
  110 + </template>
  111 + </uni-list-item>
  112 +
  113 + <uni-list-item class="select-item" :class="detail.deliveryMode ? 'is-filled' : 'is-empty'">
  114 + <template v-slot:body>
  115 + <view class="item-title"><text>交提货方式</text></view>
  116 + </template>
  117 + <template v-slot:footer>
  118 + <text class="readonly-text">{{ getDeliveryMethodLabel() }}</text>
  119 + </template>
  120 + </uni-list-item>
  121 +
  122 + <uni-list-item class="select-item" :class="typeof detail.commodityBuyout === 'boolean' ? 'is-filled' : 'is-empty'">
  123 + <template v-slot:body>
  124 + <view class="item-title"><text>是否通货买断</text></view>
  125 + </template>
  126 + <template v-slot:footer>
  127 + <text class="readonly-text">{{ boolText(detail.commodityBuyout) }}</text>
  128 + </template>
  129 + </uni-list-item>
  130 +
  131 + <uni-list-item class="select-item" :class="typeof detail.qualityBuyout === 'boolean' ? 'is-filled' : 'is-empty'">
  132 + <template v-slot:body>
  133 + <view class="item-title"><text>是否质量买断</text></view>
  134 + </template>
  135 + <template v-slot:footer>
  136 + <text class="readonly-text">{{ boolText(detail.qualityBuyout) }}</text>
  137 + </template>
  138 + </uni-list-item>
  139 +
  140 + <uni-list-item class="select-item" :class="typeof detail.quantityBuyout === 'boolean' ? 'is-filled' : 'is-empty'">
  141 + <template v-slot:body>
  142 + <view class="item-title"><text>是否数量买断</text></view>
  143 + </template>
  144 + <template v-slot:footer>
  145 + <text class="readonly-text">{{ boolText(detail.quantityBuyout) }}</text>
  146 + </template>
  147 + </uni-list-item>
  148 +
  149 + <uni-list-item class="select-item" :class="typeof detail.doorDelivery === 'boolean' ? 'is-filled' : 'is-empty'">
  150 + <template v-slot:body>
  151 + <view class="item-title"><text>是否送货上门</text></view>
  152 + </template>
  153 + <template v-slot:footer>
  154 + <text class="readonly-text">{{ boolText(detail.doorDelivery) }}</text>
  155 + </template>
  156 + </uni-list-item>
  157 +
  158 + <uni-list-item title="另付运费">
  159 + <template v-slot:footer>
  160 + <uni-easyinput
  161 + v-model="form.shippingCost"
  162 + type="digit"
  163 + :inputBorder="false"
  164 + placeholder="请输入另付运费"
  165 + @input="onShippingCostInput"
  166 + @blur="onShippingCostBlur"
  167 + />
  168 + </template>
  169 + </uni-list-item>
  170 +
  171 + <uni-list-item title="装车费">
  172 + <template v-slot:footer>
  173 + <uni-easyinput v-model="loadingFeeDisplay" :inputBorder="false" disabled placeholder="-" />
  174 + </template>
  175 + </uni-list-item>
  176 +
  177 + <uni-list-item title="开票品名">
  178 + <template v-slot:footer>
  179 + <uni-easyinput v-model="detail.invoiceProductName" :inputBorder="false" disabled placeholder="-" />
  180 + </template>
  181 + </uni-list-item>
  182 +
  183 + <uni-list-item title="交货日期">
  184 + <template v-slot:footer>
  185 + <uni-easyinput v-model="deliveryDateDisplay" :inputBorder="false" disabled placeholder="-" />
  186 + </template>
  187 + </uni-list-item>
  188 +
  189 + <uni-list-item title="结算方式">
  190 + <template v-slot:footer>
  191 + <uni-easyinput v-model="detail.settlementMethod" :inputBorder="false" disabled placeholder="-" />
  192 + </template>
  193 + </uni-list-item>
  194 +
  195 + <uni-list-item title="结算比例(%)">
  196 + <template v-slot:footer>
  197 + <uni-easyinput v-model="settlementRatioDisplay" :inputBorder="false" disabled placeholder="-" />
  198 + </template>
  199 + </uni-list-item>
  200 +
  201 + <uni-list-item class="select-item" :class="form.priceTableId || form.priceTableCode ? 'is-filled' : 'is-empty'" clickable
  202 + @click="openPriceTableSheet" :rightText="form.priceTableCodeLabel || '请选择价格表编号'" showArrow>
  203 + <template v-slot:body>
  204 + <view class="item-title"><text class="required">*</text><text>价格表编号</text></view>
  205 + </template>
  206 + </uni-list-item>
  207 +
  208 + <uni-list-item title="协调事由">
  209 + <template v-slot:footer>
  210 + <uni-easyinput v-model="detail.coordinationReason" :inputBorder="false" disabled placeholder="-" />
  211 + </template>
  212 + </uni-list-item>
  213 +
  214 + <uni-list-item title="票类状况">
  215 + <template v-slot:footer>
  216 + <uni-easyinput v-model="detail.invoiceTypeStatus" :inputBorder="false" disabled placeholder="-" />
  217 + </template>
  218 + </uni-list-item>
  219 +
  220 + <uni-list-item v-if="attachmentList.length" title="附件">
  221 + <template v-slot:footer>
  222 + <view class="attach-list">
  223 + <view v-for="(at, idx) in attachmentList" :key="idx" class="attach-item">
  224 + <text class="attach-name">{{ at.name }}</text>
  225 + <text class="attach-view" @click.stop="onPreviewAttachment(at)">查看</text>
  226 + </view>
  227 + </view>
  228 + </template>
  229 + </uni-list-item>
  230 +
  231 + <ProductRel
  232 + ref="productRel"
  233 + mode="edit"
  234 + :scene="supplementScene"
  235 + :list="lineList"
  236 + :product-options="productOptions"
  237 + :workshop-options="workshopOptions"
  238 + @change="onLineChange"
  239 + />
  240 + </uni-list>
  241 +
  242 + <view class="material-condition-section">
  243 + <view class="section-header">
  244 + <image class="opCollapse" src="/static/images/title.png" />
  245 + <text class="section-title">料质状况</text>
  246 + </view>
  247 + <view class="section-content">
  248 + <view class="form-row">
  249 + <text class="form-label">是否启用料质状况</text>
  250 + <text class="form-value">{{ boolText(detail.materialConditionEnabled) }}</text>
  251 + </view>
  252 + <template v-if="detail.materialConditionEnabled">
  253 + <view v-for="mc in materialConditionConfigs" :key="mc.field" class="material-group">
  254 + <text class="material-label">{{ mc.label }}</text>
  255 + <view class="tag-list">
  256 + <view
  257 + v-for="opt in getDictOptions(mc.dictCode)"
  258 + :key="opt.code"
  259 + class="tag-btn"
  260 + :class="{ active: isTagSelected(mc.field, opt.code) }"
  261 + >
  262 + {{ opt.name }}
  263 + </view>
  264 + </view>
  265 + </view>
  266 + </template>
  267 + </view>
  268 + </view>
  269 +
  270 + <view class="remark-section">
  271 + <view class="section-header">
  272 + <image class="opCollapse" src="/static/images/title.png" />
  273 + <text class="section-title">备注</text>
  274 + </view>
  275 + <view class="section-content">
  276 + <text class="remark-text">{{ safeText(detail.remark) }}</text>
  277 + </view>
  278 + </view>
  279 +
  280 + <view class="footer-tip">提示:含运到厂价 = 实际采购价 + 另付运费;协调幅度 = 含运到厂价 + 买断料预计损耗 - 价格表价格</view>
  281 + </scroll-view>
  282 +
  283 + <view class="bottom-bar">
  284 + <button class="btn btn-outline" @click="onBack">取消</button>
  285 + <button class="btn btn-primary" @click="onSubmit">保存</button>
  286 + </view>
  287 +
  288 + <RelateSelectSheet
  289 + v-if="companySheetVisible"
  290 + :visible.sync="companySheetVisible"
  291 + title="选择公司"
  292 + :fetch-fn="fetchCompanyList"
  293 + :display-fields="companyDisplayFields"
  294 + :page-size="20"
  295 + :multiple="false"
  296 + row-key="id"
  297 + @confirm="onCompanyConfirm"
  298 + />
  299 + <RelateSelectSheet
  300 + v-if="priceSheetVisible"
  301 + :visible.sync="priceSheetVisible"
  302 + title="选择价格表"
  303 + :fetch-fn="fetchPriceTableList"
  304 + :display-fields="priceTableDisplayFields"
  305 + :page-size="20"
  306 + :multiple="false"
  307 + row-key="id"
  308 + @confirm="onPriceTableConfirm"
  309 + />
  310 + </view>
  311 +</template>
  312 +
  313 +<script>
  314 +import ProductRel from './productRel.vue'
  315 +import RelateSelectSheet from '@/components/relate-select/index.vue'
  316 +import { procurementPlanDetailApi, procurementPlanUpdateApi } from '@/api/procure-manage/procurementPlan.js'
  317 +import {
  318 + companyShortQueryApi,
  319 + priceTableQueryApi,
  320 + priceTableDetailApi,
  321 + breedRelationshipQueryApi,
  322 + workshopQueryApi
  323 +} from '@/api/devManage.js'
  324 +import { getDicByCodes, getDicName } from '@/utils/dic.js'
  325 +
  326 +export default {
  327 + name: 'DomesticPlanSupplement',
  328 + components: { ProductRel, RelateSelectSheet },
  329 + data() {
  330 + return {
  331 + supplementScene: 'supplement',
  332 + id: '',
  333 + detail: {},
  334 + attachmentList: [],
  335 + materialConditionConfigs: [
  336 + { field: 'materialConditionCopperRice', label: '铜米', dictCode: 'COPPER_RICE' },
  337 + { field: 'materialConditionBrightCopper', label: '光亮铜', dictCode: 'BRIGHT_COPPER' },
  338 + { field: 'materialConditionFirstClass', label: '一特', dictCode: 'FIRST_CLASS' },
  339 + { field: 'materialConditionSecondClass', label: '二特', dictCode: 'SECOND_CLASS' },
  340 + { field: 'materialConditionTinCoated', label: '镀锡紫', dictCode: 'TIN_COATED' },
  341 + { field: 'materialConditionScrap', label: '角料', dictCode: 'SCRAP' },
  342 + { field: 'materialConditionWhiteYellow', label: '镀白黄', dictCode: 'WHITE_YELLOW' },
  343 + { field: 'materialConditionPhosphorCopper', label: '磷铜', dictCode: 'PHOSPHOR_COPPER' }
  344 + ],
  345 + dicOptions: {
  346 + AUDIT_STATUS: [],
  347 + PROCESS_STATUS: [],
  348 + DELIVERY_METHOD: []
  349 + },
  350 + dictData: {},
  351 + form: {
  352 + id: '',
  353 + companyId: '',
  354 + companyName: '',
  355 + date: '',
  356 + shippingCost: '',
  357 + priceTableId: '',
  358 + priceTableCode: '',
  359 + priceTableCodeLabel: ''
  360 + },
  361 + lineList: [],
  362 + productOptions: [],
  363 + workshopOptions: [],
  364 + priceTableList: [],
  365 + companySheetVisible: false,
  366 + priceSheetVisible: false,
  367 + companyDisplayFields: [
  368 + { label: '公司简称', field: 'shortName' },
  369 + { label: '公司名称', field: 'companyName' },
  370 + { label: '办事处', field: 'deptName' }
  371 + ],
  372 + priceTableDisplayFields: [
  373 + { label: '价格表编号', field: 'code' },
  374 + { label: '创建人', field: 'createBy' },
  375 + ]
  376 + }
  377 + },
  378 + computed: {
  379 + shippingCostNumber() {
  380 + var v = this.form.shippingCost
  381 + if (v == null || v === '') return 0
  382 + var n = Number(v)
  383 + return isNaN(n) ? 0 : n
  384 + },
  385 + purchaseDepartmentDisplay() {
  386 + var d = this.detail || {}
  387 + return d.purchaseDepartmentName != null ? d.purchaseDepartmentName : (d.purchaseDepartment != null ? d.purchaseDepartment : '')
  388 + },
  389 + regionDisplay() {
  390 + var d = this.detail || {}
  391 + return d.regionName != null ? d.regionName : (d.region != null ? d.region : '')
  392 + },
  393 + pricingDeadlineDisplay() {
  394 + var d = this.detail || {}
  395 + var s = d.pricingDeadline == null ? '' : String(d.pricingDeadline).trim()
  396 + if (!s) return ''
  397 + if (s.length >= 10) return s.slice(0, 10)
  398 + return s
  399 + },
  400 + deliveryDateDisplay() {
  401 + var d = this.detail || {}
  402 + var s = d.deliveryDate == null ? '' : String(d.deliveryDate).trim()
  403 + if (!s) return ''
  404 + if (s.length >= 10) return s.slice(0, 10)
  405 + return s
  406 + },
  407 + loadingFeeDisplay() {
  408 + var v = (this.detail || {}).loadingFee
  409 + if (v == null || v === '') return ''
  410 + var n = Number(v)
  411 + if (isNaN(n)) return String(v)
  412 + return n.toLocaleString('zh-CN', { maximumFractionDigits: 4 })
  413 + },
  414 + settlementRatioDisplay() {
  415 + var v = (this.detail || {}).settlementRatio
  416 + if (v == null || v === '') return ''
  417 + var n = Number(v)
  418 + if (isNaN(n)) return String(v)
  419 + return n.toLocaleString('zh-CN', { maximumFractionDigits: 4 })
  420 + }
  421 + },
  422 + onLoad(query) {
  423 + this.id = (query && query.id) ? query.id : ''
  424 + this.loadDictData()
  425 + this.loadRefData()
  426 + if (this.id) {
  427 + this.loadSourceDetail()
  428 + }
  429 + },
  430 + methods: {
  431 + toNumber(v) {
  432 + if (v == null || v === '') return 0
  433 + var n = Number(v)
  434 + return isNaN(n) ? 0 : n
  435 + },
  436 + toFixed(v, n) {
  437 + var num = Number(v)
  438 + if (isNaN(num)) return ''
  439 + return num.toFixed(n)
  440 + },
  441 + safeText(v) {
  442 + var s = v == null ? '' : String(v)
  443 + return s.trim() ? s : '-'
  444 + },
  445 + boolText(v) {
  446 + if (typeof v !== 'boolean') return '-'
  447 + return v ? '是' : '否'
  448 + },
  449 + async loadDictData() {
  450 + var codes = [].concat(
  451 + this.materialConditionConfigs.map(function (mc) { return mc.dictCode }),
  452 + ['DELIVERY_METHOD', 'AUDIT_STATUS', 'PROCESS_STATUS']
  453 + )
  454 + try {
  455 + var results = await getDicByCodes(codes)
  456 + var d = results || {}
  457 + this.dictData = d
  458 + this.dicOptions.AUDIT_STATUS = (d.AUDIT_STATUS && d.AUDIT_STATUS.data) || []
  459 + this.dicOptions.PROCESS_STATUS = (d.PROCESS_STATUS && d.PROCESS_STATUS.data) || []
  460 + this.dicOptions.DELIVERY_METHOD = (d.DELIVERY_METHOD && d.DELIVERY_METHOD.data) || []
  461 + } catch (e) {
  462 + this.dictData = {}
  463 + }
  464 + },
  465 + getDictOptions(dictCode) {
  466 + var data = this.dictData[dictCode]
  467 + return (data && data.data) ? data.data : []
  468 + },
  469 + isTagSelected(field, code) {
  470 + var selected = this.detail ? this.detail[field] : undefined
  471 + if (Array.isArray(selected)) return selected.indexOf(code) >= 0
  472 + if (typeof selected === 'string' && selected) {
  473 + return selected.split(',').indexOf(code) >= 0
  474 + }
  475 + return false
  476 + },
  477 + getDeliveryMethodLabel() {
  478 + var opts = this.getDictOptions('DELIVERY_METHOD') || []
  479 + var v = String(this.detail.deliveryMode != null ? this.detail.deliveryMode : '')
  480 + if (!v) return '-'
  481 + for (var i = 0; i < opts.length; i++) {
  482 + var o = opts[i]
  483 + var code = o.code != null ? String(o.code) : (o.value != null ? String(o.value) : '')
  484 + if (code === v) return o.name || o.label || '-'
  485 + }
  486 + return v
  487 + },
  488 + buildAttachmentList() {
  489 + var ids = this.detail.attachmentFileIds
  490 + var names = this.detail.attachmentFileNames
  491 + var idArr = (typeof ids === 'string' && ids) ? ids.split(',').filter(Boolean) : (Array.isArray(ids) ? ids : [])
  492 + var nameArr = (typeof names === 'string' && names) ? names.split(',').filter(Boolean) : (Array.isArray(names) ? names : [])
  493 + var len = Math.max(idArr.length, nameArr.length)
  494 + var list = []
  495 + for (var i = 0; i < len; i++) {
  496 + list.push({ id: idArr[i] || '', name: nameArr[i] || ('附件' + (i + 1)) })
  497 + }
  498 + this.attachmentList = list
  499 + },
  500 + onPreviewAttachment(at) {
  501 + if (!at || !at.id) {
  502 + uni.showToast({ title: '无法查看', icon: 'none' })
  503 + return
  504 + }
  505 + uni.showToast({ title: '查看附件 ' + at.name, icon: 'none' })
  506 + },
  507 + async loadSourceDetail() {
  508 + if (!this.id) return
  509 + try {
  510 + var res = await procurementPlanDetailApi({ id: this.id })
  511 + var d = (res && res.data) ? res.data : {}
  512 + var self = this
  513 + this.materialConditionConfigs.forEach(function (mc) {
  514 + var v = d[mc.field]
  515 + if (typeof v === 'string' && v) {
  516 + d[mc.field] = v.split(',').filter(Boolean)
  517 + } else if (!Array.isArray(v)) {
  518 + d[mc.field] = []
  519 + }
  520 + })
  521 + this.detail = d
  522 + var date = ''
  523 + if (d.date) {
  524 + var s = String(d.date)
  525 + date = s.length >= 10 ? s.slice(0, 10) : s
  526 + }
  527 + this.form.id = d.id || ''
  528 + this.form.companyId = d.companyId || ''
  529 + this.form.companyName = d.companyShortName || d.companyName || ''
  530 + this.form.date = date
  531 + var sc = d.shippingCost
  532 + if (sc != null && sc !== '') {
  533 + this.form.shippingCost = this.toFixed(sc, 2)
  534 + }
  535 + this.lineList = Array.isArray(d.procurementPlanLineList) ? d.procurementPlanLineList.slice() : []
  536 + this.buildAttachmentList()
  537 + this.fillLineNames()
  538 + this.refreshLinesFromShipping()
  539 + this.fillPriceTableFromDetail(d)
  540 + } catch (e) {
  541 + uni.showToast({ title: '加载报价单失败', icon: 'none' })
  542 + }
  543 + },
  544 + async loadRefData() {
  545 + try {
  546 + var results = await Promise.all([
  547 + this.$pCall(priceTableQueryApi, { pageIndex: 1, pageSize: 1000 }),
  548 + this.$pCall(breedRelationshipQueryApi, { pageIndex: 1, pageSize: 1000 }),
  549 + this.$pCall(workshopQueryApi, { pageIndex: 1, pageSize: 1000 })
  550 + ])
  551 + var r0 = (results[0] && results[0].data) ? results[0].data : {}
  552 + var r1 = (results[1] && results[1].data) ? results[1].data : {}
  553 + var r2 = (results[2] && results[2].data) ? results[2].data : {}
  554 + this.priceTableList = (r0.datas && Array.isArray(r0.datas)) ? r0.datas : ((r0.list && Array.isArray(r0.list)) ? r0.list : ((r0.records && Array.isArray(r0.records)) ? r0.records : []))
  555 + this.productOptions = (r1.datas && Array.isArray(r1.datas)) ? r1.datas : ((r1.list && Array.isArray(r1.list)) ? r1.list : ((r1.records && Array.isArray(r1.records)) ? r1.records : []))
  556 + this.workshopOptions = (r2.datas && Array.isArray(r2.datas)) ? r2.datas : ((r2.list && Array.isArray(r2.list)) ? r2.list : ((r2.records && Array.isArray(r2.records)) ? r2.records : []))
  557 + this.fillLineNames()
  558 + } catch (e) {
  559 + this.priceTableList = []
  560 + this.productOptions = []
  561 + this.workshopOptions = []
  562 + }
  563 + },
  564 + $pCall(fn, params) {
  565 + try { return Promise.resolve(fn(params)) } catch (e) { return Promise.reject(e) }
  566 + },
  567 + fetchCompanyList(args) {
  568 + var extra = (args && args.extra) ? args.extra : {}
  569 + var keyword = extra.keyword || extra.name || ''
  570 + var params = { pageIndex: args.pageIndex || 1, pageSize: args.pageSize || 20 }
  571 + if (keyword) params.name = keyword
  572 + var self = this
  573 + return companyShortQueryApi(params).then(function (res) {
  574 + var d = (res && res.data) ? res.data : {}
  575 + var records = (d.datas && Array.isArray(d.datas)) ? d.datas : ((d.records && Array.isArray(d.records)) ? d.records : ((d.list && Array.isArray(d.list)) ? d.list : []))
  576 + var total = d.totalCount || d.count || (records ? records.length : 0)
  577 + var hasNext = !!d.hasNext
  578 + return { records: records, totalCount: total, hasNext: hasNext }
  579 + }).catch(function () {
  580 + return { records: [], totalCount: 0, hasNext: false }
  581 + })
  582 + },
  583 + fetchPriceTableList(args) {
  584 + var extra = (args && args.extra) ? args.extra : {}
  585 + var keyword = extra.keyword || extra.name || ''
  586 + var params = { pageIndex: args.pageIndex || 1, pageSize: args.pageSize || 20 }
  587 + if (keyword) params.name = keyword
  588 + var self = this
  589 + return priceTableQueryApi(params).then(function (res) {
  590 + var d = (res && res.data) ? res.data : {}
  591 + var records = (d.datas && Array.isArray(d.datas)) ? d.datas : ((d.records && Array.isArray(d.records)) ? d.records : ((d.list && Array.isArray(d.list)) ? d.list : []))
  592 + var total = d.totalCount || d.count || (records ? records.length : 0)
  593 + var hasNext = !!d.hasNext
  594 + return { records: records, totalCount: total, hasNext: hasNext }
  595 + }).catch(function () {
  596 + return { records: [], totalCount: 0, hasNext: false }
  597 + })
  598 + },
  599 + openCompanySheet() {
  600 + this.companySheetVisible = true
  601 + },
  602 + openPriceTableSheet() {
  603 + this.priceSheetVisible = true
  604 + },
  605 + onCompanyConfirm(e) {
  606 + var items = (e && e.items) ? e.items : []
  607 + if (!items.length) return
  608 + var row = items[0] || {}
  609 + this.form.companyId = row.id || ''
  610 + this.form.companyName = row.companyShortName || row.companyName || row.name || ''
  611 + },
  612 + onPriceTableConfirm(e) {
  613 + var items = (e && e.items) ? e.items : []
  614 + if (!items.length) return
  615 + var row = items[0] || {}
  616 + this.selectPriceTableRow(row)
  617 + },
  618 + selectPriceTableRow(row) {
  619 + this.form.priceTableId = row.id || ''
  620 + this.form.priceTableCode = row.priceTableCode || row.code || ''
  621 + var code = row.priceTableCode || row.code || ''
  622 + var name = row.priceTableName || row.name || ''
  623 + this.form.priceTableCodeLabel = name ? (name + (code ? ' (' + code + ')' : '')) : code
  624 + if (this.form.priceTableId) {
  625 + this.fetchPriceDetailAndApply(this.form.priceTableId)
  626 + }
  627 + },
  628 + fillPriceTableFromDetail(d) {
  629 + var detail = d || this.detail || {}
  630 + var code = detail.priceTableCode != null ? detail.priceTableCode : (detail.priceId != null ? detail.priceId : (detail.priceTableId != null ? detail.priceTableId : ''))
  631 + if (!code) return
  632 + var sc = String(code)
  633 + var list = Array.isArray(this.priceTableList) ? this.priceTableList : []
  634 + var matched = null
  635 + for (var i = 0; i < list.length; i++) {
  636 + var o = list[i]
  637 + var id = String(o.id || o.priceId || o.value || '')
  638 + var c = String(o.priceTableCode || o.code || o.priceCode || '')
  639 + if (id === sc || c === sc) {
  640 + matched = o
  641 + break
  642 + }
  643 + }
  644 + if (matched) {
  645 + this.selectPriceTableRow(matched)
  646 + return
  647 + }
  648 + this.form.priceTableId = detail.priceId != null ? detail.priceId : (detail.priceTableId != null ? detail.priceTableId : sc)
  649 + this.form.priceTableCode = detail.priceTableCode != null ? detail.priceTableCode : sc
  650 + var name = detail.priceTableName != null ? detail.priceTableName : ''
  651 + var cd = detail.priceTableCode != null ? detail.priceTableCode : sc
  652 + this.form.priceTableCodeLabel = name ? (name + (cd ? ' (' + cd + ')' : '')) : cd
  653 + if (this.form.priceTableId) {
  654 + this.fetchPriceDetailAndApply(this.form.priceTableId)
  655 + }
  656 + },
  657 + async loadLatestPriceTableAndApply() {
  658 + var self = this
  659 + try {
  660 + var res = await priceTableQueryApi({ pageIndex: 1, pageSize: 1, orderByColumn: 'createTime desc,id desc' })
  661 + var d = (res && res.data) ? res.data : {}
  662 + var list = (d.datas && Array.isArray(d.datas)) ? d.datas : ((d.records && Array.isArray(d.records)) ? d.records : ((d.list && Array.isArray(d.list)) ? d.list : []))
  663 + if (!list || !list.length) return
  664 + var row = list[0]
  665 + self.selectPriceTableRow(row)
  666 + } catch (e) {
  667 + return
  668 + }
  669 + },
  670 + async fetchPriceDetailAndApply(priceId) {
  671 + if (!priceId) return
  672 + var self = this
  673 + try {
  674 + var res = await priceTableDetailApi({ id: priceId })
  675 + var d = (res && res.data) ? res.data : {}
  676 + var priceLines = self.extractPriceLines(d)
  677 + console.log(priceLines,d,'6666')
  678 + self.applyPriceLinesToLines(priceLines)
  679 + } catch (e) {
  680 + return
  681 + }
  682 + },
  683 + extractPriceLines(priceDetail) {
  684 + if (!priceDetail || typeof priceDetail !== 'object') return []
  685 + var candidates = [
  686 + priceDetail.procurementPriceLineList,
  687 + priceDetail.priceLineList,
  688 + priceDetail.priceList,
  689 + priceDetail.lineList,
  690 + priceDetail.datas,
  691 + priceDetail.records,
  692 + priceDetail.list
  693 + ]
  694 + for (var i = 0; i < candidates.length; i++) {
  695 + if (Array.isArray(candidates[i]) && candidates[i].length) return candidates[i]
  696 + }
  697 + for (var k in priceDetail) {
  698 + if (!Object.prototype.hasOwnProperty.call(priceDetail, k)) continue
  699 + if (Array.isArray(priceDetail[k])) {
  700 + var arr = priceDetail[k]
  701 + if (arr.length && arr[0] && typeof arr[0] === 'object' && (arr[0].price != null || arr[0].productId != null || arr[0].breedId != null || arr[0].varietyId != null)) {
  702 + return arr
  703 + }
  704 + }
  705 + }
  706 + return []
  707 + },
  708 + applyPriceLinesToLines(priceLines) {
  709 + if (!Array.isArray(this.lineList)) return
  710 + var priceMap = this.buildPriceMap(priceLines)
  711 + var self = this
  712 + this.lineList = this.lineList.map(function (row) {
  713 + var pid = String(row.productId || row.breedId || row.varietyId || '')
  714 + var price = null
  715 + if (pid && priceMap[pid] != null) {
  716 + price = priceMap[pid].price
  717 + } else {
  718 + var pname = (row.productName || '').trim()
  719 + if (pname) {
  720 + for (var k in priceMap) {
  721 + if (!Object.prototype.hasOwnProperty.call(priceMap, k)) continue
  722 + var meta = priceMap[k] && priceMap[k].meta ? priceMap[k].meta : null
  723 + if (meta && ((meta.productName && meta.productName === pname) || (meta.breedName && meta.breedName === pname) || (meta.varietyName && meta.varietyName === pname))) {
  724 + price = priceMap[k].price
  725 + break
  726 + }
  727 + }
  728 + }
  729 + }
  730 + if (price != null) {
  731 + row.priceListPrices = self.toFixed(price, 2)
  732 + } else {
  733 + row.priceListPrices = ''
  734 + }
  735 + return row
  736 + })
  737 + this.refreshLinesFromShipping()
  738 + this.notifyLineChange()
  739 + },
  740 + buildPriceMap(priceLines) {
  741 + var m = {}
  742 + if (!Array.isArray(priceLines)) return m
  743 + for (var i = 0; i < priceLines.length; i++) {
  744 + var line = priceLines[i] || {}
  745 + if (line.price == null) continue
  746 + var key = String(line.productId || line.breedId || line.varietyId || line.id || ('__idx_' + i))
  747 + m[key] = { price: Number(line.price), meta: line }
  748 + }
  749 + return m
  750 + },
  751 + onShippingCostInput(val) {
  752 + var s = val == null ? '' : String(val)
  753 + s = s.replace(/[^\d.]/g, '')
  754 + var parts = s.split('.')
  755 + if (parts.length > 2) s = parts[0] + '.' + parts.slice(1).join('')
  756 + this.form.shippingCost = s
  757 + this.refreshLinesFromShipping()
  758 + },
  759 + onShippingCostBlur() {
  760 + if (this.form.shippingCost == null || this.form.shippingCost === '') {
  761 + this.refreshLinesFromShipping()
  762 + return
  763 + }
  764 + var n = Number(this.form.shippingCost)
  765 + if (isNaN(n)) {
  766 + this.form.shippingCost = ''
  767 + } else {
  768 + if (n < 0) n = 0
  769 + this.form.shippingCost = this.toFixed(n, 2)
  770 + }
  771 + this.refreshLinesFromShipping()
  772 + },
  773 + refreshLinesFromShipping() {
  774 + var self = this
  775 + var sc = self.shippingCostNumber
  776 + if (!Array.isArray(self.lineList)) return
  777 + self.lineList = self.lineList.map(function (row) {
  778 + var actual = self.toNumber(row.actualPurchasePrice)
  779 + var including = actual + sc
  780 + row.includingShippingPrice = self.toFixed(including, 2)
  781 + var priceList = self.toNumber(row.priceListPrices)
  782 + var loss = self.toNumber(row.estimatedLoss)
  783 + var coord = including + loss - priceList
  784 + row.coordinationRange = self.toFixed(coord, 2)
  785 + return row
  786 + })
  787 + self.notifyLineChange()
  788 + },
  789 + onLineChange(list) {
  790 + this.lineList = Array.isArray(list) ? list.slice() : []
  791 + this.fillLineNames()
  792 + this.refreshLinesFromShipping()
  793 + },
  794 + fillLineNames() {
  795 + if (!Array.isArray(this.lineList) || !this.lineList.length) return
  796 + var pOpts = Array.isArray(this.productOptions) ? this.productOptions : []
  797 + var wOpts = Array.isArray(this.workshopOptions) ? this.workshopOptions : []
  798 + var self = this
  799 + this.lineList = this.lineList.map(function (row) {
  800 + var r = Object.assign({}, row)
  801 + var pid = r.productId != null ? String(r.productId) : ''
  802 + var wid = r.workshopId != null ? String(r.workshopId) : ''
  803 + if ((r.productName == null || String(r.productName).trim() === '') && pid) {
  804 + var p = pOpts.find(function (o) {
  805 + var id = o.id != null ? String(o.id) : (o.productId != null ? String(o.productId) : (o.value != null ? String(o.value) : ''))
  806 + return id === pid
  807 + })
  808 + if (p) {
  809 + r.productName = p.name != null ? p.name : (p.productName != null ? p.productName : (p.label != null ? p.label : ''))
  810 + }
  811 + }
  812 + if ((r.workshopName == null || String(r.workshopName).trim() === '') && wid) {
  813 + var w = wOpts.find(function (o) {
  814 + var id = o.id != null ? String(o.id) : (o.workshopId != null ? String(o.workshopId) : (o.value != null ? String(o.value) : ''))
  815 + return id === wid
  816 + })
  817 + if (w) {
  818 + r.workshopName = w.name != null ? w.name : (w.workshopName != null ? w.workshopName : (w.label != null ? w.label : ''))
  819 + }
  820 + }
  821 + return r
  822 + })
  823 + },
  824 + notifyLineChange() {
  825 + try {
  826 + var pr = this.$refs.productRel
  827 + if (pr) {
  828 + if (typeof pr.initItems === 'function') {
  829 + pr._initialized = false
  830 + pr.initItems()
  831 + var self = this
  832 + this.$nextTick(function () {
  833 + pr._initialized = true
  834 + })
  835 + }
  836 + }
  837 + } catch (e) {
  838 + console.warn('notifyLineChange error:', e)
  839 + }
  840 + },
  841 + validateForm() {
  842 + if (!this.form.companyName || !this.form.companyId) {
  843 + uni.showToast({ title: '请选择公司名称', icon: 'none' })
  844 + return false
  845 + }
  846 + if (!this.form.date) {
  847 + uni.showToast({ title: '请选择日期', icon: 'none' })
  848 + return false
  849 + }
  850 + if (!this.form.priceTableId && !this.form.priceTableCode) {
  851 + uni.showToast({ title: '请选择价格表', icon: 'none' })
  852 + return false
  853 + }
  854 + var pr = this.$refs.productRel
  855 + if (pr && typeof pr.validate === 'function') {
  856 + var ok = pr.validate()
  857 + if (!ok) return false
  858 + }
  859 + return true
  860 + },
  861 + joinStr(arr) {
  862 + if (Array.isArray(arr)) return arr.filter(Boolean).join(',')
  863 + if (typeof arr === 'string') return arr
  864 + return ''
  865 + },
  866 + toBool(v, fallback) {
  867 + if (typeof v === 'boolean') return v
  868 + if (v === 'true' || v === '1' || v === 1) return true
  869 + if (v === 'false' || v === '0' || v === 0) return false
  870 + return typeof fallback === 'boolean' ? fallback : false
  871 + },
  872 + pickId(v) {
  873 + if (v == null || v === '') return ''
  874 + if (typeof v === 'string' || typeof v === 'number') return String(v)
  875 + if (typeof v === 'object') {
  876 + return String(v.id != null ? v.id : (v.value != null ? v.value : (v.code != null ? v.code : '')))
  877 + }
  878 + return String(v)
  879 + },
  880 + buildPayload() {
  881 + var self = this
  882 + var d = this.detail || {}
  883 + var lines = Array.isArray(this.lineList) ? this.lineList.slice() : []
  884 + var lineList = lines.map(function (row, i) {
  885 + var r = row || {}
  886 + var itemId = r.itemId || (r._key != null ? r._key : (r.id ? String(r.id) : ('line-' + Date.now() + '-' + i)))
  887 + return {
  888 + id: r.id != null ? r.id : '',
  889 + itemId: itemId,
  890 + procurementPlanId: (r.procurementPlanId != null ? r.procurementPlanId : (self.form.id || d.id || '')),
  891 + productId: self.pickId(r.productId),
  892 + materialType: r.materialType != null ? String(r.materialType) : '',
  893 + quantity: self.toNumber(r.quantity),
  894 + actualPurchasePrice: self.toNumber(r.actualPurchasePrice),
  895 + includingShippingPrice: self.toNumber(r.includingShippingPrice),
  896 + priceListPrices: self.toNumber(r.priceListPrices),
  897 + coordinationRange: self.toNumber(r.coordinationRange),
  898 + estimatedLoss: self.toNumber(r.estimatedLoss),
  899 + applicationAttrition: self.toNumber(r.applicationAttrition),
  900 + workshopId: self.pickId(r.workshopId)
  901 + }
  902 + })
  903 + var mcFields = this.materialConditionConfigs.map(function (mc) { return mc.field })
  904 + var materialPayload = {}
  905 + mcFields.forEach(function (f) {
  906 + materialPayload[f] = self.joinStr(d[f])
  907 + })
  908 + var body = {
  909 + id: this.form.id || d.id || '',
  910 + code: d.code != null ? String(d.code) : '',
  911 + companyShortId: this.pickId(this.form.companyId || d.companyShortId || d.companyId),
  912 + supplierId: this.pickId(d.supplierId),
  913 + purchaseDepartment: this.pickId(d.purchaseDepartmentId || d.purchaseDepartment),
  914 + region: this.pickId(d.regionId || d.region),
  915 + date: this.form.date || '',
  916 + validityPeriod: d.validityPeriod != null ? String(d.validityPeriod) : '',
  917 + quoterId: this.pickId(d.quoterId),
  918 + quoterName: d.quoterName != null ? String(d.quoterName) : '',
  919 + longTermOperation: this.toBool(d.longTermOperation, false),
  920 + terminalCustomer: this.toBool(d.terminalCustomer, false),
  921 + priced: this.toBool(d.priced, false),
  922 + pricingDeadline: d.pricingDeadline != null ? String(d.pricingDeadline) : '',
  923 + temporaryPrice: this.toBool(d.temporaryPrice, false),
  924 + otherExpenseSettlement: this.toBool(d.otherExpenseSettlement, false),
  925 + deliveryMode: d.deliveryMode != null ? String(d.deliveryMode) : '',
  926 + commodityBuyout: this.toBool(d.commodityBuyout, false),
  927 + qualityBuyout: this.toBool(d.qualityBuyout, false),
  928 + quantityBuyout: this.toBool(d.quantityBuyout, false),
  929 + doorDelivery: this.toBool(d.doorDelivery, false),
  930 + shippingCost: this.toNumber(this.form.shippingCost),
  931 + loadingFee: this.toNumber(d.loadingFee),
  932 + invoiceProductName: d.invoiceProductName != null ? String(d.invoiceProductName) : '',
  933 + deliveryDate: d.deliveryDate != null ? String(d.deliveryDate) : '',
  934 + settlementMethod: d.settlementMethod != null ? String(d.settlementMethod) : '',
  935 + settlementRatio: this.toNumber(d.settlementRatio),
  936 + priceId: this.pickId(this.form.priceTableId || d.priceId || d.priceTableId),
  937 + coordinationReason: d.coordinationReason != null ? String(d.coordinationReason) : '',
  938 + invoiceTypeStatus: d.invoiceTypeStatus != null ? String(d.invoiceTypeStatus) : '',
  939 + remark: d.remark != null ? String(d.remark) : '',
  940 + materialConditionEnabled: this.toBool(d.materialConditionEnabled, false),
  941 + approvalStatus: d.approvalStatus != null ? String(d.approvalStatus) : '',
  942 + processStatus: d.processStatus != null ? String(d.processStatus) : '',
  943 + processNode: d.processNode != null ? String(d.processNode) : '',
  944 + procurementPlanLineList: lineList
  945 + }
  946 + Object.keys(materialPayload).forEach(function (k) {
  947 + body[k] = materialPayload[k]
  948 + })
  949 + return body
  950 + },
  951 + onBack() {
  952 + uni.navigateBack({ delta: 1 })
  953 + },
  954 + async onSubmit() {
  955 + if (!this.validateForm()) return
  956 + var payload = this.buildPayload()
  957 + try {
  958 + await procurementPlanUpdateApi(payload)
  959 + uni.showToast({ title: '保存成功', icon: 'none' })
  960 + try {
  961 + uni.setStorageSync('DOMESTIC_PLAN_LIST_NEED_REFRESH', '1')
  962 + uni.setStorageSync('SUPPLE_PLAN_LIST_NEED_REFRESH', '1')
  963 + uni.setStorageSync('DOMESTIC_PLAN_DETAIL_NEED_REFRESH', '1')
  964 + } catch (e) {}
  965 + var self = this
  966 + setTimeout(function () {
  967 + uni.navigateBack({ delta: 1 })
  968 + }, 400)
  969 + } catch (e) {
  970 + uni.showToast({ title: (e && (e.msg || e.message)) ? (e.msg || e.message) : '保存失败', icon: 'none' })
  971 + }
  972 + }
  973 + }
  974 +}
  975 +</script>
  976 +
  977 +<style lang="scss" scoped>
  978 +.page {
  979 + display: flex;
  980 + flex-direction: column;
  981 + height: 100%;
  982 +}
  983 +.scroll {
  984 + flex: 1;
  985 + padding: 12rpx 0 392rpx !important;
  986 +}
  987 +.bottom-bar {
  988 + z-index: 2;
  989 + position: fixed;
  990 + left: 0;
  991 + right: 0;
  992 + bottom: 0;
  993 + padding: 32rpx;
  994 + padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
  995 + background: #fff;
  996 + box-shadow: 0 -8rpx 24rpx rgba(0, 0, 0, 0.06);
  997 + display: flex;
  998 + gap: 24rpx;
  999 + .btn {
  1000 + flex: 1;
  1001 + height: 80rpx;
  1002 + line-height: 80rpx;
  1003 + border-radius: 12rpx;
  1004 + font-size: 32rpx;
  1005 + }
  1006 + .btn-outline {
  1007 + background: #fff;
  1008 + color: $theme-primary;
  1009 + border: 2rpx solid $theme-primary;
  1010 + }
  1011 + .btn-primary {
  1012 + background: $theme-primary;
  1013 + color: #fff;
  1014 + border: 2rpx solid $theme-primary;
  1015 + }
  1016 +}
  1017 +
  1018 +::v-deep .uni-list {
  1019 + .uni-easyinput {
  1020 + display: flex;
  1021 + .uni-input-input {
  1022 + color: rgba(0, 0, 0, 0.9);
  1023 + }
  1024 + }
  1025 + .uni-input-placeholder { z-index: 1; }
  1026 + .uni-input-input { background-color: #ffffff; }
  1027 + background: transparent;
  1028 + &-item {
  1029 + &__extra-text { font-size: 32rpx; }
  1030 + &__content-title {
  1031 + font-size: 32rpx;
  1032 + color: rgba(0, 0, 0, 0.9);
  1033 + }
  1034 + &__container {
  1035 + padding: 32rpx;
  1036 + .uni-easyinput {
  1037 + &__placeholder-class {
  1038 + font-size: 32rpx;
  1039 + color: rgba(0, 0, 0, 0.4);
  1040 + }
  1041 + &__content {
  1042 + border: none;
  1043 + background-color: #ffffff !important;
  1044 + &-input {
  1045 + padding-left: 0 !important;
  1046 + height: 48rpx;
  1047 + line-height: 48rpx;
  1048 + font-size: 32rpx;
  1049 + }
  1050 + .content-clear-icon {
  1051 + font-size: 44rpx !important;
  1052 + }
  1053 + }
  1054 + }
  1055 + .item-title,
  1056 + .uni-list-item__content {
  1057 + flex: none;
  1058 + min-height: 48rpx;
  1059 + line-height: 48rpx;
  1060 + font-size: 32rpx;
  1061 + position: relative;
  1062 + width: 240rpx;
  1063 + margin-right: 32rpx;
  1064 + color: rgba(0, 0, 0, 0.9);
  1065 + .required {
  1066 + color: red;
  1067 + position: absolute;
  1068 + top: 50%;
  1069 + transform: translateY(-50%);
  1070 + left: -16rpx;
  1071 + }
  1072 + }
  1073 + }
  1074 + &.select-item {
  1075 + &.is-empty {
  1076 + .uni-list-item__extra-text {
  1077 + color: rgba(0, 0, 0, 0.4) !important;
  1078 + }
  1079 + }
  1080 + &.is-filled {
  1081 + .uni-list-item__extra-text {
  1082 + color: rgba(0, 0, 0, 0.9) !important;
  1083 + }
  1084 + }
  1085 + .serial-number-row {
  1086 + display: flex;
  1087 + align-items: center;
  1088 + }
  1089 + }
  1090 + }
  1091 +}
  1092 +
  1093 +.readonly-text {
  1094 + color: rgba(0, 0, 0, 0.9);
  1095 + font-size: 32rpx;
  1096 + line-height: 48rpx;
  1097 + text-align: right;
  1098 + white-space: pre-wrap;
  1099 + word-break: break-all;
  1100 +}
  1101 +.value-code {
  1102 + width: 260rpx;
  1103 + text-align: left;
  1104 +}
  1105 +
  1106 +.attach-list {
  1107 + flex: 1;
  1108 + display: flex;
  1109 + flex-direction: column;
  1110 + align-items: flex-end;
  1111 + gap: 8rpx;
  1112 +}
  1113 +.attach-item {
  1114 + display: flex;
  1115 + align-items: center;
  1116 + gap: 16rpx;
  1117 + .attach-name { font-size: 26rpx; color: rgba(0,0,0,0.9); }
  1118 + .attach-view { font-size: 26rpx; color: $theme-primary; }
  1119 +}
  1120 +
  1121 +.material-condition-section,
  1122 +.remark-section {
  1123 + margin-top: 20rpx;
  1124 + background: #fff;
  1125 +}
  1126 +.section-header {
  1127 + display: flex;
  1128 + align-items: center;
  1129 + padding: 24rpx 32rpx;
  1130 + border-bottom: 1rpx solid #f0f0f0;
  1131 +}
  1132 +.section-title {
  1133 + font-size: 32rpx;
  1134 + color: rgba(0, 0, 0, 0.9);
  1135 + font-weight: 600;
  1136 +}
  1137 +.opCollapse {
  1138 + width: 32rpx;
  1139 + height: 28rpx;
  1140 + margin-right: 16rpx;
  1141 + margin-top: 8rpx;
  1142 +}
  1143 +.section-content {
  1144 + padding: 32rpx;
  1145 +}
  1146 +.form-row {
  1147 + display: flex;
  1148 + align-items: center;
  1149 + justify-content: space-between;
  1150 + margin-bottom: 24rpx;
  1151 +}
  1152 +.form-row:last-child { margin-bottom: 0; }
  1153 +.form-label {
  1154 + font-size: 32rpx;
  1155 + color: rgba(0, 0, 0, 0.9);
  1156 +}
  1157 +.form-value {
  1158 + font-size: 32rpx;
  1159 + color: rgba(0, 0, 0, 0.9);
  1160 +}
  1161 +.material-group {
  1162 + margin-top: 32rpx;
  1163 +}
  1164 +.material-label {
  1165 + display: block;
  1166 + font-size: 30rpx;
  1167 + color: rgba(0, 0, 0, 0.9);
  1168 + margin-bottom: 20rpx;
  1169 +}
  1170 +.tag-list {
  1171 + display: flex;
  1172 + flex-wrap: wrap;
  1173 + gap: 16rpx;
  1174 +}
  1175 +.tag-btn {
  1176 + padding: 12rpx 24rpx;
  1177 + border: 1rpx solid #ddd;
  1178 + border-radius: 8rpx;
  1179 + font-size: 28rpx;
  1180 + color: #666;
  1181 + background: #fff;
  1182 +}
  1183 +.tag-btn.active {
  1184 + background: $theme-primary;
  1185 + border-color: $theme-primary;
  1186 + color: #fff;
  1187 +}
  1188 +.remark-text {
  1189 + font-size: 30rpx;
  1190 + color: rgba(0,0,0,0.9);
  1191 + line-height: 1.6;
  1192 + white-space: pre-wrap;
  1193 +}
  1194 +.footer-tip {
  1195 + padding: 24rpx 32rpx;
  1196 + color: rgba(0,0,0,0.45);
  1197 + font-size: 24rpx;
  1198 +}
  1199 +
  1200 +::v-deep .uni-easyinput {
  1201 + width: 100%;
  1202 + &__content {
  1203 + background: #f8f8f8 !important;
  1204 + border-radius: 8rpx;
  1205 + &-input { font-size: 30rpx; }
  1206 + }
  1207 +}
  1208 +</style>
... ...