Commit 0bc993b675b6d4d06175863ac391e5fd0b3b8c5a

Authored by gesilong
2 parents 86d0a87b e15b5ae9

Merge branch 'cjerp-2.0-plan' into test_cjerp

Too many changes to show.

To preserve performance only 11 of 24 files are displayed.

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