Commit 45e3bea45b148a9aeea36370850c364ba2b85f47

Authored by gesilong
1 parent e8715703

commit: 合同管理模块的联调

Too many changes to show.

To preserve performance only 12 of 34 files are displayed.

@@ -94,6 +94,17 @@ export function createContractApi(data) { @@ -94,6 +94,17 @@ export function createContractApi(data) {
94 }) 94 })
95 } 95 }
96 96
  97 +// 删除合同
  98 +export function deleteContractApi(id) {
  99 + return request({
  100 + url: `${baseUrl}/contractDistributorStandard`,
  101 + method: 'delete',
  102 + data: { id:id },
  103 + contentType: ContentTypeEnum.FORM_URLENCODED,
  104 + region
  105 + })
  106 +}
  107 +
97 // 更新合同 108 // 更新合同
98 export function updateContractApi(data) { 109 export function updateContractApi(data) {
99 return request({ 110 return request({
@@ -186,12 +186,54 @@ @@ -186,12 +186,54 @@
186 "navigationBarTextStyle": "black" 186 "navigationBarTextStyle": "black"
187 } 187 }
188 }, { 188 }, {
  189 + "path": "pages/contract_stock/add",
  190 + "style": {
  191 + "navigationBarTitleText": "新增经销库存合同",
  192 + "navigationBarBackgroundColor": "#ffffff",
  193 + "navigationBarTextStyle": "black"
  194 + }
  195 + },{
  196 + "path": "pages/contract_stock/detail",
  197 + "style": {
  198 + "navigationBarTitleText": "经销库存合同详情",
  199 + "navigationBarBackgroundColor": "#ffffff",
  200 + "navigationBarTextStyle": "black"
  201 + }
  202 + },{
  203 + "path": "pages/contract_stock/modify",
  204 + "style": {
  205 + "navigationBarTitleText": "编辑经销库存合同",
  206 + "navigationBarBackgroundColor": "#ffffff",
  207 + "navigationBarTextStyle": "black"
  208 + }
  209 + },{
189 "path": "pages/contract_unplan/index", 210 "path": "pages/contract_unplan/index",
190 "style": { 211 "style": {
191 "navigationBarTitleText": "经销未锁规合同", 212 "navigationBarTitleText": "经销未锁规合同",
192 "navigationBarBackgroundColor": "#ffffff", 213 "navigationBarBackgroundColor": "#ffffff",
193 "navigationBarTextStyle": "black" 214 "navigationBarTextStyle": "black"
194 } 215 }
  216 + },{
  217 + "path": "pages/contract_unplan/add",
  218 + "style": {
  219 + "navigationBarTitleText": "新增经销未锁规合同",
  220 + "navigationBarBackgroundColor": "#ffffff",
  221 + "navigationBarTextStyle": "black"
  222 + }
  223 + },{
  224 + "path": "pages/contract_unplan/detail",
  225 + "style": {
  226 + "navigationBarTitleText": "经销未锁规合同详情",
  227 + "navigationBarBackgroundColor": "#ffffff",
  228 + "navigationBarTextStyle": "black"
  229 + }
  230 + },{
  231 + "path": "pages/contract_unplan/modify",
  232 + "style": {
  233 + "navigationBarTitleText": "编辑经销未锁规合同",
  234 + "navigationBarBackgroundColor": "#ffffff",
  235 + "navigationBarTextStyle": "black"
  236 + }
195 }, { 237 }, {
196 "path": "pages/contract_process/index", 238 "path": "pages/contract_process/index",
197 "style": { 239 "style": {
@@ -199,6 +241,27 @@ @@ -199,6 +241,27 @@
199 "navigationBarBackgroundColor": "#ffffff", 241 "navigationBarBackgroundColor": "#ffffff",
200 "navigationBarTextStyle": "black" 242 "navigationBarTextStyle": "black"
201 } 243 }
  244 + },{
  245 + "path": "pages/contract_process/add",
  246 + "style": {
  247 + "navigationBarTitleText": "新增加工标准合同",
  248 + "navigationBarBackgroundColor": "#ffffff",
  249 + "navigationBarTextStyle": "black"
  250 + }
  251 + },{
  252 + "path": "pages/contract_process/detail",
  253 + "style": {
  254 + "navigationBarTitleText": "加工标准合同详情",
  255 + "navigationBarBackgroundColor": "#ffffff",
  256 + "navigationBarTextStyle": "black"
  257 + }
  258 + },{
  259 + "path": "pages/contract_process/modify",
  260 + "style": {
  261 + "navigationBarTitleText": "编辑加工标准合同",
  262 + "navigationBarBackgroundColor": "#ffffff",
  263 + "navigationBarTextStyle": "black"
  264 + }
202 }, { 265 }, {
203 "path": "pages/contract_foreign_std/index", 266 "path": "pages/contract_foreign_std/index",
204 "style": { 267 "style": {
@@ -206,6 +269,27 @@ @@ -206,6 +269,27 @@
206 "navigationBarBackgroundColor": "#ffffff", 269 "navigationBarBackgroundColor": "#ffffff",
207 "navigationBarTextStyle": "black" 270 "navigationBarTextStyle": "black"
208 } 271 }
  272 + },{
  273 + "path": "pages/contract_foreign_std/add",
  274 + "style": {
  275 + "navigationBarTitleText": "新增外贸标准合同",
  276 + "navigationBarBackgroundColor": "#ffffff",
  277 + "navigationBarTextStyle": "black"
  278 + }
  279 + },{
  280 + "path": "pages/contract_foreign_std/detail",
  281 + "style": {
  282 + "navigationBarTitleText": "外贸标准合同详情",
  283 + "navigationBarBackgroundColor": "#ffffff",
  284 + "navigationBarTextStyle": "black"
  285 + }
  286 + },{
  287 + "path": "pages/contract_foreign_std/modify",
  288 + "style": {
  289 + "navigationBarTitleText": "编辑外贸标准合同",
  290 + "navigationBarBackgroundColor": "#ffffff",
  291 + "navigationBarTextStyle": "black"
  292 + }
209 }, { 293 }, {
210 "path": "pages/contract_foreign_stock/index", 294 "path": "pages/contract_foreign_stock/index",
211 "style": { 295 "style": {
@@ -213,6 +297,27 @@ @@ -213,6 +297,27 @@
213 "navigationBarBackgroundColor": "#ffffff", 297 "navigationBarBackgroundColor": "#ffffff",
214 "navigationBarTextStyle": "black" 298 "navigationBarTextStyle": "black"
215 } 299 }
  300 + },{
  301 + "path": "pages/contract_foreign_stock/add",
  302 + "style": {
  303 + "navigationBarTitleText": "新增外贸库存合同",
  304 + "navigationBarBackgroundColor": "#ffffff",
  305 + "navigationBarTextStyle": "black"
  306 + }
  307 + },{
  308 + "path": "pages/contract_foreign_stock/detail",
  309 + "style": {
  310 + "navigationBarTitleText": "外贸库存合同详情",
  311 + "navigationBarBackgroundColor": "#ffffff",
  312 + "navigationBarTextStyle": "black"
  313 + }
  314 + },{
  315 + "path": "pages/contract_foreign_stock/modify",
  316 + "style": {
  317 + "navigationBarTitleText": "编辑外贸库存合同",
  318 + "navigationBarBackgroundColor": "#ffffff",
  319 + "navigationBarTextStyle": "black"
  320 + }
216 }, { 321 }, {
217 "path": "pages/contract_foreign_unplan/index", 322 "path": "pages/contract_foreign_unplan/index",
218 "style": { 323 "style": {
@@ -220,6 +325,27 @@ @@ -220,6 +325,27 @@
220 "navigationBarBackgroundColor": "#ffffff", 325 "navigationBarBackgroundColor": "#ffffff",
221 "navigationBarTextStyle": "black" 326 "navigationBarTextStyle": "black"
222 } 327 }
  328 + },{
  329 + "path": "pages/contract_foreign_unplan/add",
  330 + "style": {
  331 + "navigationBarTitleText": "新增外贸未锁规合同",
  332 + "navigationBarBackgroundColor": "#ffffff",
  333 + "navigationBarTextStyle": "black"
  334 + }
  335 + },{
  336 + "path": "pages/contract_foreign_unplan/detail",
  337 + "style": {
  338 + "navigationBarTitleText": "外贸未锁规合同详情",
  339 + "navigationBarBackgroundColor": "#ffffff",
  340 + "navigationBarTextStyle": "black"
  341 + }
  342 + },{
  343 + "path": "pages/contract_foreign_unplan/modify",
  344 + "style": {
  345 + "navigationBarTitleText": "编辑外贸未锁规合同",
  346 + "navigationBarBackgroundColor": "#ffffff",
  347 + "navigationBarTextStyle": "black"
  348 + }
223 } 349 }
224 ], 350 ],
225 "subPackages": [{ 351 "subPackages": [{
  1 +<template>
  2 + <view class="page">
  3 + <scroll-view class="scroll" scroll-y>
  4 + <uni-list>
  5 + <uni-list-item title="编号">
  6 + <template v-slot:footer>
  7 + <uni-easyinput v-model="form.code" :inputBorder="false" disabled />
  8 + </template>
  9 + </uni-list-item>
  10 +
  11 + <uni-list-item class="select-item" :class="form.supplier ? 'is-filled' : 'is-empty'" clickable
  12 + @click="openSheet('supplier')" :rightText="displayLabel('supplierName')" showArrow>
  13 + <template v-slot:body>
  14 + <view class="item-title"><text class="required">*</text><text>供方</text></view>
  15 + </template>
  16 + </uni-list-item>
  17 +
  18 + <uni-list-item class="select-item" :class="form.buyer ? 'is-filled' : 'is-empty'" clickable
  19 + @click="openRelate('buyer')" :rightText="form.buyerName || '请选择需方'" showArrow>
  20 + <template v-slot:body>
  21 + <view class="item-title"><text class="required">*</text><text>需方</text></view>
  22 + </template>
  23 + </uni-list-item>
  24 +
  25 + <uni-list-item title="订货日期">
  26 + <template v-slot:footer>
  27 + <uni-datetime-picker type="date" v-model="form.orderDate" />
  28 + </template>
  29 + </uni-list-item>
  30 +
  31 + <uni-list-item title="单位">
  32 + <template v-slot:footer>
  33 + <uni-easyinput v-model="form.unit" :inputBorder="false" disabled />
  34 + </template>
  35 + </uni-list-item>
  36 + <uni-list-item class="select-item" :class="form.workshopId ? 'is-filled' : 'is-empty'" clickable
  37 + @click="openSheet('workshopId')" :rightText="displayLabel('workshopIdName')" showArrow>
  38 + <template v-slot:body>
  39 + <view class="item-title"><text class="required">*</text><text>生产厂</text></view>
  40 + </template>
  41 + </uni-list-item>
  42 + <ProductRel mode="add" :orderDateBase="form.orderDate" @change="onProductsChange" :options="productList" />
  43 + <uni-list-item title="合计人民币金额(大写)">
  44 + <template v-slot:footer>
  45 + <uni-easyinput v-model="form.totalAmountCapital" placeholder="自动计算" :inputBorder="false" disabled />
  46 + </template>
  47 + </uni-list-item>
  48 + <uni-list-item title="交付定金、数额、时间">
  49 + <template v-slot:footer>
  50 + <uni-easyinput v-model="form.depositInfo" placeholder="请输入交付定金、数额、时间" :inputBorder="false" />
  51 + </template>
  52 + </uni-list-item>
  53 + <uni-list-item title="包装要求">
  54 + <template v-slot:footer>
  55 + <uni-easyinput v-model="form.packagingRequirements" placeholder="请输入包装要求"
  56 + :inputBorder="false" />
  57 + </template>
  58 + </uni-list-item>
  59 + <uni-list-item title="付款方式、付款期限">
  60 + <template v-slot:footer>
  61 + <uni-easyinput v-model="form.paymentTerms" placeholder="请输入付款方式、付款期限" :inputBorder="false" />
  62 + </template>
  63 + </uni-list-item>
  64 + <uni-list-item title="运输方式">
  65 + <template v-slot:footer>
  66 + <uni-easyinput v-model="form.transportMode" placeholder="请输入运输方式" :inputBorder="false" />
  67 + </template>
  68 + </uni-list-item>
  69 +
  70 + <uni-list-item class="select-item" :class="(Array.isArray(form.destinationId) && form.destinationId.length) ? 'is-filled' : 'is-empty'" clickable
  71 + @click="openCitySelector" :rightText="form.destinationLabel || '请选择'" showArrow>
  72 + <template v-slot:body>
  73 + <view class="item-title"><text>目的地</text></view>
  74 + <CitySelector ref="citySelectorRef" v-model="form.destinationId" @change="onCityChange" />
  75 + </template>
  76 + </uni-list-item>
  77 + <uni-list-item class="select-item" :class="form.includesPackagingFeeName ? 'is-filled' : 'is-empty'" clickable
  78 + @click="openSheet('includesPackagingFee')" :rightText="form.includesPackagingFeeName || '请选择'" showArrow>
  79 + <template v-slot:body>
  80 + <view class="item-title"><text>单价中是否已包含包装费</text></view>
  81 + </template>
  82 + </uni-list-item>
  83 + <uni-list-item class="select-item" :class="form.includesTransportFeeName ? 'is-filled' : 'is-empty'" clickable
  84 + @click="openSheet('includesTransportFee')" :rightText="form.includesTransportFeeName || '请选择'" showArrow>
  85 + <template v-slot:body>
  86 + <view class="item-title"><text>单价中是否已包含运费</text></view>
  87 + </template>
  88 + </uni-list-item>
  89 + <uni-list-item title="需方指定收货人">
  90 + <template v-slot:footer>
  91 + <uni-easyinput v-model="form.designatedConsignee" placeholder="请输入需方指定收货人"
  92 + :inputBorder="false" />
  93 + </template>
  94 + </uni-list-item>
  95 + <view class="group">
  96 + <view class="group-title">特别条款要求</view>
  97 + <view class="radio-list">
  98 + <view v-for="(opt, i) in specialTermsList" :key="'cr-' + i" class="radio-item"
  99 + @click="onRadioSelect('specialTerms', 'specialTermsName', opt)">
  100 + <view :class="['radio', { checked: form.specialTerms === opt.value }]" />
  101 + <text class="label">{{ opt.label }}</text>
  102 + </view>
  103 + </view>
  104 + </view>
  105 + <view class="group">
  106 + <view class="group-title">执行标准</view>
  107 + <view class="radio-list">
  108 + <view v-for="(opt, i) in executionStandardList" :key="'es-' + i" class="radio-item"
  109 + @click="onRadioSelect('executionStandard', 'executionStandardName', opt)">
  110 + <view :class="['radio', { checked: form.executionStandard === opt.value }]" />
  111 + <text class="label">{{ opt.label }}</text>
  112 + </view>
  113 + </view>
  114 + </view>
  115 + <uni-list-item v-if="form.executionStandard === 'OTHER'" title="其他">
  116 + <template v-slot:footer>
  117 + <uni-easyinput v-model="form.executionStandardRemarks" placeholder="请输入其他标准备注"
  118 + :inputBorder="false" />
  119 + </template>
  120 + </uni-list-item>
  121 + <uni-list-item title="特别说明">
  122 + <template v-slot:footer>
  123 + <uni-easyinput v-model="form.specialInstructions" placeholder="请输入特别说明" :inputBorder="false" />
  124 + </template>
  125 + </uni-list-item>
  126 + <uni-list-item title="备注">
  127 + <template v-slot:footer>
  128 + <uni-easyinput v-model="form.remarks" placeholder="请输入备注" :inputBorder="false" />
  129 + </template>
  130 + </uni-list-item>
  131 + <view class="quality">
  132 + <image class="opCollapse" src="/static/images/title.png" />
  133 + <text class="title">具体质量要求</text>
  134 + </view>
  135 + <uni-list-item title="件重条头">
  136 + <template v-slot:footer>
  137 + <uni-easyinput v-model="form.pieceWeightHead" placeholder="请输入" :inputBorder="false" />
  138 + </template>
  139 + </uni-list-item>
  140 + <uni-list-item title="表面">
  141 + <template v-slot:footer>
  142 + <uni-easyinput v-model="form.surface" placeholder="请输入" :inputBorder="false" />
  143 + </template>
  144 + </uni-list-item>
  145 + <uni-list-item title="公差">
  146 + <template v-slot:footer>
  147 + <uni-easyinput v-model="form.tolerance" placeholder="请输入" :inputBorder="false" />
  148 + </template>
  149 + </uni-list-item>
  150 + <uni-list-item title="性能">
  151 + <template v-slot:footer>
  152 + <uni-easyinput v-model="form.performance" placeholder="请输入" :inputBorder="false" />
  153 + </template>
  154 + </uni-list-item>
  155 + <uni-list-item title="成分">
  156 + <template v-slot:footer>
  157 + <uni-easyinput v-model="form.component" placeholder="请输入" :inputBorder="false" />
  158 + </template>
  159 + </uni-list-item>
  160 + <uni-list-item title="包装">
  161 + <template v-slot:footer>
  162 + <uni-easyinput v-model="form.packaging" placeholder="请输入" :inputBorder="false" />
  163 + </template>
  164 + </uni-list-item>
  165 + </uni-list>
  166 +
  167 + </scroll-view>
  168 + <view class="footer">
  169 + <div class="total">
  170 + <div class="total-text">
  171 + 合计
  172 + </div>
  173 + <div class="total-item">
  174 + <div class="total-item-text">
  175 + 数量
  176 + </div>
  177 + <div class="total-item-price">
  178 + {{ (sumQuantity || 0).toFixed(2) }}t
  179 + </div>
  180 + </div>
  181 + <div class="total-item">
  182 + <div class="total-item-text">
  183 + 不含税金额
  184 + </div>
  185 + <div class="total-item-price text-red">
  186 + ¥{{ (sumAmountExcl || 0).toFixed(2) }}
  187 + </div>
  188 + </div>
  189 + <div class="total-item">
  190 + <div class="total-item-text">
  191 + 总金额
  192 + </div>
  193 + <div class="total-item-price text-red">
  194 + ¥{{ (sumTotal || 0).toFixed(2) }}
  195 + </div>
  196 + </div>
  197 + </div>
  198 + <button class="btn submit" type="primary" @click="onSubmit">提交</button>
  199 + </view>
  200 + <SingleSelectSheet :visible.sync="sheet.visible" :title="sheet.title" :options="sheet.options"
  201 + v-model="sheet.value" @confirm="onSheetConfirm" />
  202 + <RelateSelectSheet :visible.sync="relate.visible" :title="relate.title" :source="relate.source"
  203 + :display-fields="relate.display" :multiple="relate.multiple" :row-key="relate.rowKey"
  204 + :selectedKeys.sync="relate.selectedKeys" @confirm="onRelateConfirm" />
  205 + </view>
  206 +
  207 +</template>
  208 +<script>
  209 +import SingleSelectSheet from '@/components/single-select/index.vue'
  210 +import RelateSelectSheet from '@/components/relate-select/index.vue'
  211 +import ProductRel from './productRel.vue'
  212 +import CitySelector from '@/components/city-selector/index.vue'
  213 +import { getRetailCodeApi, createContractApi } from '@/api/contract'
  214 +import { getDicByCodes } from '@/utils/dic'
  215 +import { formatCurrencyToChinese } from '@/utils/common'
  216 +import { workshopQueryApi } from '@/api/devManage'
  217 +
  218 +export default {
  219 + name: 'AddContractForeignStd',
  220 + components: { SingleSelectSheet, RelateSelectSheet, ProductRel, CitySelector },
  221 + data() {
  222 + return {
  223 + form: {
  224 + code: '',
  225 + supplier: '',
  226 + supplierName: '',
  227 + buyer: '',
  228 + buyerName: '',
  229 + orderDate: '',
  230 + designatedConsignee: '',
  231 + specialTerms: '',
  232 + specialTermsName: '',
  233 + executionStandard: '',
  234 + executionStandardName: '',
  235 + executionStandardRemarks: '',
  236 + includesPackagingFee: false,
  237 + includesPackagingFeeName: '',
  238 + includesTransportFee: false,
  239 + includesTransportFeeName: '',
  240 + unit: '元、公斤、元/公斤',
  241 + totalAmountCapital: '',
  242 + destinationId: [],
  243 + destinationLabel: '',
  244 + workshopIdName: '',
  245 + workshopId: '',
  246 + },
  247 + supplierList: [],
  248 + specialTermsList: [],
  249 + executionStandardList: [],
  250 + yesNoList: [{ label: '是', value: true }, { label: '否', value: false }],
  251 + sheet: { visible: false, title: '请选择', field: '', options: [], value: '' },
  252 + relate: { visible: false, title: '选择', source: '', display: [], multiple: false, rowKey: 'id', selectedKeys: [], fieldKey: '' },
  253 + sumQuantity: 0,
  254 + sumAmountExcl: 0,
  255 + sumTotal: 0,
  256 + productLineList: [],
  257 + productList: [],
  258 + }
  259 + },
  260 + created() {
  261 + this.loadSuppliers()
  262 + this.loadExtraOptions()
  263 + this.initCode()
  264 + this.form.orderDate = this.formatDate(new Date())
  265 + this.$nextTick(() => {
  266 + if (Array.isArray(this.form.destinationId) && this.form.destinationId.length) {
  267 + this.initDestinationLabel()
  268 + }
  269 + })
  270 + },
  271 + methods: {
  272 + onProductsChange(products) {
  273 + const list = Array.isArray(products) ? products : []
  274 + const sumQ = list.reduce((acc, it) => acc + (parseFloat(it.quantity) || 0), 0)
  275 + const sumE = list.reduce((acc, it) => acc + (parseFloat(it.amountExcludingTax) || 0), 0)
  276 + const sumT = list.reduce((acc, it) => acc + (parseFloat(it.totalAmount) || 0), 0)
  277 + this.sumQuantity = sumQ
  278 + this.sumAmountExcl = sumE
  279 + this.sumTotal = sumT
  280 + this.form.totalAmountCapital = formatCurrencyToChinese(sumT)
  281 + this.productLineList = list
  282 + },
  283 + formatDate(d) {
  284 + const y = d.getFullYear()
  285 + const m = String(d.getMonth() + 1).padStart(2, '0')
  286 + const day = String(d.getDate()).padStart(2, '0')
  287 + return `${y}-${m}-${day}`
  288 + },
  289 + async initCode() {
  290 + try {
  291 + const res = await getRetailCodeApi()
  292 + const code = (res && res.data) ? res.data : ''
  293 + this.form.code = code
  294 + } catch (e) { this.form.code = '' }
  295 + },
  296 + async loadSuppliers() {
  297 + try {
  298 + const results = await getDicByCodes(['SUPPLIER'])
  299 + const items = results && results.SUPPLIER && results.SUPPLIER.data ? results.SUPPLIER.data : []
  300 + this.supplierList = items.map(it => ({ label: it.name, value: it.code }))
  301 + } catch (e) { this.supplierList = [] }
  302 + },
  303 + async loadExtraOptions() {
  304 + try {
  305 + const results = await getDicByCodes(['CONDITIONS_REQUIRED', 'APPLICABLE_STANDARD', 'CONTRACT_PRODUCT'])
  306 + const c1 = results && results.CONDITIONS_REQUIRED && results.CONDITIONS_REQUIRED.data ? results.CONDITIONS_REQUIRED.data : []
  307 + const c2 = results && results.APPLICABLE_STANDARD && results.APPLICABLE_STANDARD.data ? results.APPLICABLE_STANDARD.data : []
  308 + const c3 = results && results.CONTRACT_PRODUCT && results.CONTRACT_PRODUCT.data ? results.CONTRACT_PRODUCT.data : []
  309 + this.specialTermsList = c1.map(it => ({ label: it.name, value: it.code }))
  310 + this.executionStandardList = c2.map(it => ({ label: it.name, value: it.code }))
  311 + this.productList = c3.map(it => ({ label: it.name, value: it.code }))
  312 + } catch (e) {
  313 + this.specialTermsList = []
  314 + this.executionStandardList = []
  315 + this.productList = []
  316 + }
  317 + },
  318 + displayLabel(field) {
  319 + const m = this.form
  320 + const map = { supplierName: '请选择供方', buyerName: '请选择需方', workshopIdName: '请选择生产厂' }
  321 + const val = m[field]
  322 + return val ? String(val) : map[field]
  323 + },
  324 + openCitySelector() {
  325 + this.$refs.citySelectorRef && this.$refs.citySelectorRef.open()
  326 + },
  327 + async initDestinationLabel() {
  328 + const comp = this.$refs.citySelectorRef
  329 + if (comp && typeof comp.getLabel === 'function') {
  330 + const label = await comp.getLabel()
  331 + this.form.destinationLabel = label || ''
  332 + }
  333 + },
  334 + onCityChange(payload) {
  335 + const label = payload && payload.label != null ? payload.label : ''
  336 + this.form.destinationLabel = label
  337 + },
  338 + async openSheet(field) {
  339 + const setSheet = (title, options) => {
  340 + const current = this.form[field]
  341 + const match = (options || []).find(o => String(o.label) === String(current) || String(o.value) === String(current))
  342 + this.sheet = { ...this.sheet, visible: true, title, options, field, value: match ? match.value : '' }
  343 + }
  344 + if (field === 'workshopId') {
  345 + const res = await workshopQueryApi({ pageIndex: 1, pageSize: 9999 })
  346 + const _data = res.data || {}
  347 + const list = _data.datas || (Array.isArray(_data) ? _data : [])
  348 + const opts = (list || []).map(it => ({
  349 + label: it.name ,
  350 + value: it.id
  351 + }))
  352 + setSheet('生产厂', opts)
  353 + } else if (field === 'supplier') {
  354 + setSheet('供方', this.supplierList)
  355 + } else if (field === 'includesPackagingFee') {
  356 + setSheet('单价中是否已包含包装费', this.yesNoList)
  357 + } else if (field === 'includesTransportFee') {
  358 + setSheet('单价中是否已包含运费', this.yesNoList)
  359 + }
  360 + },
  361 + onSheetConfirm({ value, label }) {
  362 + const field = this.sheet.field
  363 + if (!field) return
  364 + const v = (value === undefined || value === null) ? '' : value
  365 + this.form[field] = v
  366 + this.form[field + 'Name'] = label || ''
  367 + },
  368 + openRelate(fieldKey) {
  369 + let config = {}
  370 + if (fieldKey === 'buyer') {
  371 + config = { title: '需方', source: 'customer', rowKey: 'id', multiple: false, display: [{ label: '姓名', field: 'name' }, { label: '编号', field: 'code' }, { label: '状态', field: 'available', format: v => (v ? '启用' : '停用') }] }
  372 + }
  373 + const selectedKeys = this.form[fieldKey] ? [this.form[fieldKey]] : []
  374 + this.sheet.visible = false
  375 + this.relate = { ...this.relate, title: config.title, source: config.source, display: config.display, multiple: config.multiple, rowKey: config.rowKey, selectedKeys, fieldKey }
  376 + this.$nextTick(() => { this.relate.visible = true })
  377 + },
  378 + onRelateConfirm({ items }) {
  379 + const _fieldKey = this.relate.fieldKey
  380 + const first = (items && items.length > 0) ? items[0] : null
  381 + this.form[_fieldKey] = (first && first.id) ? first.id : ''
  382 + this.form[_fieldKey + 'Name'] = (first && first.name) ? first.name : ''
  383 + },
  384 + onRadioSelect(field, nameField, opt) {
  385 + const val = opt && opt.value != null ? opt.value : ''
  386 + const label = opt && opt.label != null ? opt.label : ''
  387 + this.form[field] = val
  388 + this.form[nameField] = label
  389 + if (field === 'executionStandard' && val !== 'OTHER') {
  390 + this.form.executionStandardRemarks = ''
  391 + }
  392 + },
  393 + async onSubmit() {
  394 + if (!this.validateRequired()) return
  395 + const confirmRes = await new Promise(resolve => {
  396 + uni.showModal({ title: '提示', content: '确定新增经销未锁规合同吗?', confirmText: '确定', cancelText: '取消', success: resolve })
  397 + })
  398 + if (!(confirmRes && confirmRes.confirm)) return
  399 + const clean = (obj) => {
  400 + const out = {}
  401 + Object.keys(obj || {}).forEach(k => {
  402 + const v = obj[k]
  403 + const isEmptyString = typeof v === 'string' && v.trim() === ''
  404 + const isUndef = v === undefined || v === null
  405 + const isNaNNumber = typeof v === 'number' && isNaN(v)
  406 + if (!(isEmptyString || isUndef || isNaNNumber)) out[k] = v
  407 + })
  408 + return out
  409 + }
  410 + const lines = (this.productLineList || []).map(it => clean(it))
  411 + const { destinationLabel, destinationId, ...formForSubmit } = this.form;
  412 + // 区id
  413 + const destination = destinationId && destinationId.length > 0 ? destinationId[destinationId.length - 1] : '';
  414 + const payload = clean({
  415 + ...formForSubmit,
  416 + destination,
  417 + type: 'INTL_STD_CONTRACT',
  418 + sumQuantity: this.sumQuantity,
  419 + sumAmountExcl: this.sumAmountExcl,
  420 + sumTotal: this.sumTotal,
  421 + contractDistributorLineList: lines
  422 + })
  423 + console.log('onSubmit__payload', payload)
  424 +
  425 + try {
  426 + await createContractApi(payload)
  427 + uni.showToast({ title: '新增成功', icon: 'none' })
  428 + setTimeout(() => { uni.redirectTo({ url: '/pages/contract_foreign_std/index' }) }, 400)
  429 + } catch (e) {
  430 + uni.showToast({ title: '提交失败', icon: 'none' })
  431 + }
  432 + },
  433 + validateRequired() {
  434 + const checks = [
  435 + { key: 'code', label: '编号' },
  436 + { key: 'supplier', label: '供方' },
  437 + { key: 'buyer', label: '需方' },
  438 + { key: 'orderDate', label: '订货日期' },
  439 + { key: 'workshopId', label: '生产厂' },
  440 + ]
  441 + for (const it of checks) {
  442 + const val = this.form[it.key]
  443 + const empty = (val === undefined || val === null || (typeof val === 'string' && val.trim() === '') || (typeof val === 'number' && isNaN(val)))
  444 + if (empty) { uni.showToast({ title: `请先选择${it.label}`, icon: 'none' }); return false }
  445 + }
  446 + if (!Array.isArray(this.productLineList) || this.productLineList.length === 0) {
  447 + uni.showToast({ title: '请至少添加一条产品明细', icon: 'none' }); return false
  448 + }
  449 + for (const [idx, it] of this.productLineList.entries()) {
  450 + if (!it.productName || !it.quantity || !it.unitPrice) {
  451 + uni.showToast({ title: `第${idx + 1}条明细未完整填写`, icon: 'none' }); return false
  452 + }
  453 + }
  454 + return true
  455 + }
  456 + }
  457 +}
  458 +
  459 +</script>
  460 +<style lang="scss" scoped>
  461 +.total {
  462 + .total-text {
  463 + font-weight: 600;
  464 + font-size: 32rpx;
  465 + color: rgba(0, 0, 0, 0.9);
  466 + padding-bottom: 28rpx;
  467 + border-bottom: 2rpx solid #E7E7E7;
  468 + }
  469 + .total-item {
  470 + display: flex;
  471 + align-items: center;
  472 + .total-item-text {
  473 + font-weight: 400;
  474 + font-size: 28rpx;
  475 + color: rgba(0, 0, 0, 0.6);
  476 + line-height: 32rpx;
  477 + width: 240rpx;
  478 + padding: 24rpx 0;
  479 + }
  480 + .total-item-price {
  481 + font-weight: 600;
  482 + font-size: 32rpx;
  483 + color: rgba(0, 0, 0, 0.9);
  484 + line-height: 32rpx;
  485 + }
  486 + .text-red {
  487 + color: #D54941;
  488 + }
  489 + }
  490 +
  491 +}
  492 +.page {
  493 + display: flex;
  494 + flex-direction: column;
  495 + height: 100%;
  496 +}
  497 +
  498 +.scroll {
  499 + flex: 1;
  500 + padding: 12rpx 0 480rpx !important;
  501 +}
  502 +
  503 +.footer {
  504 + z-index: 2;
  505 + position: fixed;
  506 + left: 0;
  507 + right: 0;
  508 + bottom: 0;
  509 + padding: 32rpx;
  510 + padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
  511 + background: #fff;
  512 + box-shadow: 0 -8rpx 24rpx rgba(0, 0, 0, 0.06);
  513 +
  514 + .btn {
  515 + height: 80rpx;
  516 + line-height: 80rpx;
  517 + border-radius: 12rpx;
  518 + font-size: 32rpx;
  519 + }
  520 +
  521 + .submit {
  522 + background: $theme-primary;
  523 + color: #fff;
  524 + }
  525 +}
  526 +
  527 +.group {
  528 + background: #fff;
  529 + padding: 24rpx 32rpx;
  530 + margin-bottom: 20rpx;
  531 +}
  532 +
  533 +.group-title {
  534 + color: rgba(0, 0, 0, 0.9);
  535 + font-size: 32rpx;
  536 + padding-bottom: 16rpx;
  537 +}
  538 +
  539 +.radio-list {
  540 + display: flex;
  541 + flex-direction: column;
  542 +}
  543 +
  544 +.radio-item {
  545 + display: flex;
  546 + align-items: center;
  547 + padding: 24rpx 0;
  548 + border-bottom: 1rpx solid #f0f0f0;
  549 +}
  550 +
  551 +.radio-item:last-child {
  552 + border-bottom: none;
  553 +}
  554 +
  555 +.radio {
  556 + width: 32rpx;
  557 + height: 32rpx;
  558 + border-radius: 50%;
  559 + border: 2rpx solid #DCDCDC;
  560 + margin-right: 20rpx;
  561 + box-sizing: border-box;
  562 +}
  563 +
  564 +.radio.checked {
  565 + background: $theme-primary;
  566 + border-color: $theme-primary;
  567 +}
  568 +
  569 +.radio-item .label {
  570 + font-size: 32rpx;
  571 + color: rgba(0, 0, 0, 0.9);
  572 +}
  573 +
  574 +.scroll {
  575 + flex: 1;
  576 + padding: 12rpx 0 160rpx;
  577 +}
  578 +
  579 +.quality {
  580 + background-color: #fff;
  581 + display: flex;
  582 + align-items: center;
  583 + padding: 24rpx 32rpx;
  584 + border-bottom: 1rpx solid #f0f0f0;
  585 + margin-top: 20rpx;
  586 +
  587 + .title {
  588 + font-size: 32rpx;
  589 + color: rgba(0, 0, 0, 0.9);
  590 + font-weight: 600;
  591 + }
  592 +
  593 + .opCollapse {
  594 + color: rgba(0, 0, 0, 0.6);
  595 + width: 24rpx;
  596 + height: 24rpx;
  597 + margin-right: 16rpx;
  598 + margin-top: 8rpx;
  599 + }
  600 +}
  601 +
  602 +::v-deep .uni-list {
  603 + .uni-easyinput {
  604 + display: flex;
  605 +
  606 + .uni-input-input {
  607 + color: rgba(0, 0, 0, 0.9);
  608 + }
  609 + }
  610 +
  611 + .uni-input-placeholder {
  612 + z-index: 1;
  613 + }
  614 +
  615 + .uni-input-input {
  616 + background-color: #ffffff;
  617 + }
  618 +
  619 + background: transparent;
  620 +
  621 + &-item {
  622 + &__extra-text {
  623 + font-size: 32rpx;
  624 + }
  625 +
  626 + &__content-title {
  627 + font-size: 32rpx;
  628 + color: rgba(0, 0, 0, 0.9);
  629 + }
  630 +
  631 + &__container {
  632 + padding: 32rpx;
  633 +
  634 + .uni-easyinput {
  635 + &__placeholder-class {
  636 + font-size: 32rpx;
  637 + color: rgba(0, 0, 0, 0.4);
  638 + }
  639 +
  640 + &__content {
  641 + border: none;
  642 + background-color: #ffffff !important;
  643 +
  644 + &-input {
  645 + padding-left: 0 !important;
  646 + height: 48rpx;
  647 + line-height: 48rpx;
  648 + font-size: 32rpx;
  649 + }
  650 +
  651 + .content-clear-icon {
  652 + font-size: 44rpx !important;
  653 + }
  654 + }
  655 + }
  656 +
  657 + .item-title,
  658 + .uni-list-item__content {
  659 + flex: none;
  660 + min-height: 48rpx;
  661 + line-height: 48rpx;
  662 + font-size: 32rpx;
  663 + position: relative;
  664 + width: 162rpx;
  665 + margin-right: 32rpx;
  666 + color: rgba(0, 0, 0, 0.9);
  667 +
  668 + .required {
  669 + color: red;
  670 + position: absolute;
  671 + top: 50%;
  672 + transform: translateY(-50%);
  673 + left: -16rpx;
  674 + }
  675 + }
  676 + }
  677 +
  678 + &.select-item {
  679 + &.is-empty {
  680 + .uni-list-item__extra-text {
  681 + color: rgba(0, 0, 0, 0.4) !important;
  682 + }
  683 + }
  684 +
  685 + &.is-filled {
  686 + .uni-list-item__extra-text {
  687 + color: rgba(0, 0, 0, 0.9) !important;
  688 + }
  689 + }
  690 + }
  691 +
  692 + &.mgb10 {
  693 + margin-bottom: 20rpx;
  694 + }
  695 + }
  696 +}
  697 +</style>
  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 customer">{{ detail.code }}</text>
  7 + <view class="row"><text class="label">供方</text><text class="value">{{ detail.supplierName || '-'
  8 + }}</text></view>
  9 + <view class="row"><text class="label">需方</text><text class="value">{{ detail.buyerName || '-'
  10 + }}</text></view>
  11 + <view class="row"><text class="label">订货日期</text><text class="value">{{ detail.orderDate }}</text>
  12 + </view>
  13 + <view class="row"><text class="label">单位</text><text class="value">{{ detail.unit }}</text></view>
  14 + <view class="row"><text class="label">生产厂</text><text class="value">{{ detail.workshopName || '-'
  15 + }}</text></view>
  16 + </view>
  17 +
  18 + <view class="section1">
  19 + <ProductRel mode="view" :list="productList" />
  20 + </view>
  21 +
  22 + <view class="section">
  23 + <view class="row"><text class="label">合计人民币金额(大写)</text><text class="value">{{
  24 + detail.totalAmountCapital || '-' }}</text></view>
  25 + <view class="row"><text class="label">交付定金、数额、时间</text><text class="value">{{ detail.depositInfo ||
  26 + '-' }}</text></view>
  27 + <view class="row"><text class="label">包装要求</text><text class="value">{{ detail.packagingRequirements
  28 + || '-' }}</text></view>
  29 + <view class="row"><text class="label">付款方式、付款期限</text><text class="value">{{ detail.paymentTerms ||
  30 + '-' }}</text></view>
  31 + <view class="row"><text class="label">运输方式</text><text class="value">{{ detail.transportMode || '-'
  32 + }}</text></view>
  33 + <view class="row"><text class="label">目的地</text><text class="value">{{ detail.destinationLabel || '-'
  34 + }}</text></view>
  35 + <view class="row"><text class="label">单价中是否已包含包装费</text><text class="value">{{
  36 + detail.includesPackagingFeeName || '-' }}</text></view>
  37 + <view class="row"><text class="label">单价中是否已包含运费</text><text class="value">{{
  38 + detail.includesTransportFeeName || '-' }}</text></view>
  39 + <view class="row"><text class="label">需方指定收货人</text><text class="value">{{
  40 + detail.designatedConsignee || '-' }}</text></view>
  41 +
  42 + <view class="row"><text class="label">特别条款要求</text><text class="value">{{ detail.specialTermsName ||
  43 + '-' }}</text></view>
  44 + <view class="row"><text class="label">执行标准</text><text class="value">{{ detail.executionStandardName
  45 + || '-' }}</text></view>
  46 + <view class="row" v-if="detail.executionStandard === 'OTHER'"><text class="label">其他</text><text
  47 + class="value">{{ detail.executionStandardRemarks || '-' }}</text></view>
  48 + <view class="row"><text class="label">特别说明</text><text class="value">{{ detail.specialInstructions
  49 + || '-' }}</text></view>
  50 + <view class="row"><text class="label">备注</text><text class="value">{{ detail.remarks || '-'
  51 + }}</text></view>
  52 + </view>
  53 +
  54 + <view class="section">
  55 + <view class="row"><text class="label">规范性合同</text><text class="value">{{ detail.standardFileName || '-'
  56 + }}</text></view>
  57 + <view class="row"><text class="label">合同是否规范</text><text class="value">{{ detail.standardStandardized ? '是' : '否'
  58 + }}</text></view>
  59 + </view>
  60 +
  61 + <view class="section">
  62 + <text class="row customer">具体质量要求</text>
  63 + <view class="row"><text class="label">件重条头</text><text class="value">{{ detail.pieceWeightHead ||
  64 + '-' }}</text></view>
  65 + <view class="row"><text class="label">表面</text><text class="value">{{ detail.surface || '-'
  66 + }}</text></view>
  67 + <view class="row"><text class="label">公差</text><text class="value">{{ detail.tolerance || '-'
  68 + }}</text></view>
  69 + <view class="row"><text class="label">性能</text><text class="value">{{ detail.performance || '-'
  70 + }}</text></view>
  71 + <view class="row"><text class="label">成分</text><text class="value">{{ detail.component || '-'
  72 + }}</text></view>
  73 + <view class="row"><text class="label">包装</text><text class="value">{{ detail.packaging || '-'
  74 + }}</text></view>
  75 + </view>
  76 + </view>
  77 + </scroll-view>
  78 + <detail-buttons :buttons="displayButtons" @click="handleButtonClick" />
  79 + </view>
  80 +</template>
  81 +
  82 +<script>
  83 +import { getContractApi, deleteContractApi } from '@/api/contract'
  84 +import ProductRel from './productRel.vue'
  85 +import DetailButtons from '@/components/detail-buttons/index.vue'
  86 +
  87 +export default {
  88 + name: 'ContractForeignStdDetail',
  89 + components: { ProductRel, DetailButtons },
  90 + data() {
  91 + return {
  92 + id: '',
  93 + detail: {
  94 + code: '',
  95 + supplier: '',
  96 + supplierName: '',
  97 + buyer: '',
  98 + buyerName: '',
  99 + orderDate: '',
  100 + unit: '',
  101 + workshopId: '',
  102 + workshopName: '',
  103 + designatedConsignee: '',
  104 + specialTerms: '',
  105 + specialTermsName: '',
  106 + executionStandard: '',
  107 + executionStandardName: '',
  108 + executionStandardRemarks: '',
  109 + includesPackagingFee: false,
  110 + includesPackagingFeeName: '',
  111 + includesTransportFee: false,
  112 + includesTransportFeeName: '',
  113 + totalAmountCapital: '',
  114 + depositInfo: '',
  115 + packagingRequirements: '',
  116 + paymentTerms: '',
  117 + transportMode: '',
  118 + destinationId: '',
  119 + destinationLabel: '',
  120 + specialInstructions: '',
  121 + remarks: '',
  122 + pieceWeightHead: '',
  123 + surface: '',
  124 + tolerance: '',
  125 + performance: '',
  126 + component: '',
  127 + packaging: ''
  128 + },
  129 + productList: [],
  130 + buttons: [{
  131 + text: '编辑',
  132 + visible: true,
  133 + variant: 'outline',
  134 + event: 'edit',
  135 + },
  136 + {
  137 + text: '删除',
  138 + visible: true,
  139 + variant: 'outline',
  140 + event: 'delete',
  141 + style: {
  142 + color: '#D54941',
  143 + border: '1px solid #D54941'
  144 + }
  145 + },
  146 + {
  147 + text: '上传合同附件',
  148 + visible: true,
  149 + variant: 'outline',
  150 + event: 'upload'
  151 + },
  152 + {
  153 + text: '审核',
  154 + visible: true,
  155 + variant: 'primary',
  156 + event: 'audit'
  157 + },
  158 + {
  159 + text: '审核详情',
  160 + visible: true,
  161 + variant: 'primary',
  162 + event: 'auditDetail'
  163 + },
  164 + ],
  165 + }
  166 + },
  167 + computed: {
  168 + displayButtons() {
  169 + const s = this.detail && this.detail.status || ''
  170 + const t = this.detail.standardApproved || ''
  171 + return [
  172 + { ...this.buttons[0]},
  173 + // { ...this.buttons[0], visible: (s === 'DRAFT') },
  174 + { ...this.buttons[1], visible: (s === 'DRAFT') },
  175 + { ...this.buttons[2], visible: (s !== 'DELETED' && t !== 'AUDIT' && t !== 'PASS') },
  176 + { ...this.buttons[3], visible: (s === 'STANDARD' && t === 'AUDIT') },
  177 + { ...this.buttons[4], visible: (s === 'STANDARD') }
  178 + ]
  179 + }
  180 + },
  181 + onLoad(options) {
  182 + const id = options && options.id ? options.id : ''
  183 + this.id = id
  184 + this.loadDetail()
  185 + },
  186 + methods: {
  187 + onDelete() {
  188 + uni.showModal({
  189 + title: '确认删除',
  190 + content: '确认删除该合同吗?',
  191 + success: (res) => {
  192 + if (res.confirm) {
  193 + deleteContractApi(this.id).then(() => {
  194 + uni.showToast({
  195 + title: '删除成功',
  196 + icon: 'success'
  197 + })
  198 + setTimeout(() => {
  199 + uni.navigateTo({
  200 + url: '/pages/contract_foreign_std/index'
  201 + })
  202 + }, 500)
  203 + }).catch(() => {
  204 + uni.showToast({
  205 + title: '删除失败',
  206 + icon: 'error'
  207 + })
  208 + })
  209 + }
  210 + }
  211 + })
  212 + },
  213 + onEdit() {
  214 + const id = this.detail.id || this.detail.code || ''
  215 + const query = id ? ('?id=' + encodeURIComponent(id)) : ''
  216 + uni.navigateTo({
  217 + url: '/pages/contract_foreign_std/modify' + query
  218 + })
  219 + },
  220 + handleButtonClick(btn) {
  221 + if (!btn || btn.disabled) return
  222 + if (typeof btn.onClick === 'function') return btn.onClick(this.detail, btn.params)
  223 + const e = btn.event || ''
  224 + if (e === 'edit') return this.onEdit(btn && btn.params)
  225 + if (e === 'delete') return this.onDelete(btn && btn.params)
  226 + // if (e === 'upload') return this.onUpload(btn && btn.params)
  227 + // if (e === 'audit') return this.onAudit(btn && btn.params)
  228 + // if (e === 'auditDetail') return this.onAuditDetail(btn && btn.params)
  229 + },
  230 + async loadDetail() {
  231 + if (!this.id) return
  232 + try {
  233 + const res = await getContractApi(this.id)
  234 + const data = res && res.data ? res.data : {}
  235 + const includesPackagingFeeName = data.includesPackagingFeeName || (data.includesPackagingFee ? '是' : '否')
  236 + const includesTransportFeeName = data.includesTransportFeeName || (data.includesTransportFee ? '是' : '否')
  237 + this.detail = {
  238 + ...this.detail,
  239 + ...data,
  240 + includesPackagingFeeName, includesTransportFeeName,
  241 + destinationId: data.provinceId && data.cityId && data.districtId ? [data.provinceId, data.cityId, data.districtId] : '',
  242 + destinationLabel: data.provinceName && data.cityName && data.districtName ? `${data.provinceName} / ${data.cityName} / ${data.districtName}` : '',
  243 + }
  244 + const lines = Array.isArray(data.contractDistributorLineList) ? data.contractDistributorLineList : []
  245 + this.productList = lines
  246 + } catch (e) {
  247 + this.detail = { ...this.detail }
  248 + this.productList = []
  249 + }
  250 + }
  251 + }
  252 +}
  253 +</script>
  254 +
  255 +<style lang="scss" scoped>
  256 +.page {
  257 + display: flex;
  258 + flex-direction: column;
  259 + height: 100%;
  260 +}
  261 +
  262 +.scroll {
  263 + flex: 1;
  264 + padding: 8rpx 0 144rpx;
  265 +}
  266 +
  267 +.detail-page {
  268 + background: #f3f3f3;
  269 +}
  270 +
  271 +.section {
  272 + padding: 32rpx;
  273 + background: #fff;
  274 + margin-bottom: 20rpx;
  275 +}
  276 +.section1 {
  277 + background: #fff;
  278 + margin-bottom: 20rpx;
  279 +}
  280 +
  281 +.row {
  282 + display: flex;
  283 + margin-bottom: 20rpx;
  284 + align-items: center;
  285 +}
  286 +
  287 +.row:last-child {
  288 + margin-bottom: 0;
  289 +}
  290 +
  291 +.label {
  292 + width: 280rpx;
  293 + color: rgba(0, 0, 0, 0.6);
  294 + font-size: 28rpx;
  295 +}
  296 +
  297 +.value {
  298 + flex: 1;
  299 + text-align: right;
  300 + color: rgba(0, 0, 0, 0.9);
  301 + font-size: 28rpx;
  302 +}
  303 +
  304 +.customer {
  305 + font-weight: 600;
  306 + font-size: 36rpx;
  307 + color: rgba(0, 0, 0, 0.9);
  308 + padding-bottom: 12rpx;
  309 +}
  310 +</style>
@@ -46,7 +46,7 @@ @@ -46,7 +46,7 @@
46 @error="onCardError" 46 @error="onCardError"
47 > 47 >
48 <template v-slot="{ item }"> 48 <template v-slot="{ item }">
49 - <view class="card"> 49 + <view class="card" @click="goDetail(item)">
50 <view class="card-header"> 50 <view class="card-header">
51 <text class="title omit2">{{ item.buyerName }}</text> 51 <text class="title omit2">{{ item.buyerName }}</text>
52 <text v-if="item.status === 'STANDARD'" :class="['status']" :style="{ background: statusMap[item.shippingStatusName] }">{{ item.shippingStatusName }}</text> 52 <text v-if="item.status === 'STANDARD'" :class="['status']" :style="{ background: statusMap[item.shippingStatusName] }">{{ item.shippingStatusName }}</text>
@@ -157,6 +157,9 @@ export default { @@ -157,6 +157,9 @@ export default {
157 } 157 }
158 }, 158 },
159 methods: { 159 methods: {
  160 + goDetail(item) {
  161 + uni.navigateTo({ url: '/pages/contract_foreign_std/detail?id=' + item.id })
  162 + },
160 onCardLoaded({ items }) { 163 onCardLoaded({ items }) {
161 this.currentItems = items 164 this.currentItems = items
162 }, 165 },
@@ -201,7 +204,7 @@ export default { @@ -201,7 +204,7 @@ export default {
201 if (!this.batchMode) this.selectedKeys = [] 204 if (!this.batchMode) this.selectedKeys = []
202 }, 205 },
203 onAdd() { 206 onAdd() {
204 - uni.showToast({ title: '点击新增', icon: 'none' }) 207 + uni.navigateTo({ url: '/pages/contract_foreign_std/add' })
205 }, 208 },
206 fetchList({ pageIndex, pageSize, query, extra }) { 209 fetchList({ pageIndex, pageSize, query, extra }) {
207 console.log('fetchList', pageIndex, pageSize, query, extra) 210 console.log('fetchList', pageIndex, pageSize, query, extra)
  1 +<template>
  2 + <view class="page">
  3 + <scroll-view class="scroll" scroll-y>
  4 + <uni-list>
  5 + <uni-list-item title="编号">
  6 + <template v-slot:footer>
  7 + <uni-easyinput v-model="form.code" :inputBorder="false" disabled />
  8 + </template>
  9 + </uni-list-item>
  10 +
  11 + <uni-list-item class="select-item" :class="form.supplier ? 'is-filled' : 'is-empty'" clickable
  12 + @click="openSheet('supplier')" :rightText="displayLabel('supplierName')" showArrow>
  13 + <template v-slot:body>
  14 + <view class="item-title"><text class="required">*</text><text>供方</text></view>
  15 + </template>
  16 + </uni-list-item>
  17 +
  18 + <uni-list-item class="select-item" :class="form.buyer ? 'is-filled' : 'is-empty'" clickable
  19 + @click="openRelate('buyer')" :rightText="form.buyerName || '请选择需方'" showArrow>
  20 + <template v-slot:body>
  21 + <view class="item-title"><text class="required">*</text><text>需方</text></view>
  22 + </template>
  23 + </uni-list-item>
  24 +
  25 + <uni-list-item class="select-item" :class="form.workshopId ? 'is-filled' : 'is-empty'" clickable
  26 + @click="openSheet('workshopId')" :rightText="form.workshopName || '请选择生产厂'" showArrow>
  27 + <template v-slot:body>
  28 + <view class="item-title"><text class="required">*</text><text>生产厂</text></view>
  29 + </template>
  30 + </uni-list-item>
  31 +
  32 + <uni-list-item title="订货日期">
  33 + <template v-slot:footer>
  34 + <uni-datetime-picker type="date" v-model="form.orderDate" />
  35 + </template>
  36 + </uni-list-item>
  37 +
  38 + <uni-list-item title="单位">
  39 + <template v-slot:footer>
  40 + <uni-easyinput v-model="form.unit" :inputBorder="false" disabled />
  41 + </template>
  42 + </uni-list-item>
  43 +
  44 + <ProductRel mode="add" :orderDateBase="form.orderDate" :list="productLineList" @change="onProductsChange" :options="productList" />
  45 +
  46 + <uni-list-item title="合计人民币金额(大写)">
  47 + <template v-slot:footer>
  48 + <uni-easyinput v-model="form.totalAmountCapital" placeholder="自动计算" :inputBorder="false"
  49 + disabled />
  50 + </template>
  51 + </uni-list-item>
  52 + <uni-list-item title="交付定金、数额、时间">
  53 + <template v-slot:footer>
  54 + <uni-easyinput v-model="form.depositInfo" placeholder="请输入交付定金、数额、时间" :inputBorder="false" />
  55 + </template>
  56 + </uni-list-item>
  57 + <uni-list-item title="包装要求">
  58 + <template v-slot:footer>
  59 + <uni-easyinput v-model="form.packagingRequirements" placeholder="请输入包装要求"
  60 + :inputBorder="false" />
  61 + </template>
  62 + </uni-list-item>
  63 + <uni-list-item title="付款方式、付款期限">
  64 + <template v-slot:footer>
  65 + <uni-easyinput v-model="form.paymentTerms" placeholder="请输入付款方式、付款期限" :inputBorder="false" />
  66 + </template>
  67 + </uni-list-item>
  68 + <uni-list-item title="运输方式">
  69 + <template v-slot:footer>
  70 + <uni-easyinput v-model="form.transportMode" placeholder="请输入运输方式" :inputBorder="false" />
  71 + </template>
  72 + </uni-list-item>
  73 + <uni-list-item class="select-item" :class="(Array.isArray(form.destinationId) && form.destinationId.length) ? 'is-filled' : 'is-empty'" clickable
  74 + @click="openCitySelector" :rightText="form.destinationLabel || '请选择'" showArrow>
  75 + <template v-slot:body>
  76 + <view class="item-title"><text>目的地</text></view>
  77 + <CitySelector ref="citySelectorRef" v-model="form.destinationId" @change="onCityChange" />
  78 + </template>
  79 + </uni-list-item>
  80 +
  81 + <uni-list-item class="select-item" :class="form.includesPackagingFeeName ? 'is-filled' : 'is-empty'"
  82 + clickable @click="openSheet('includesPackagingFee')"
  83 + :rightText="form.includesPackagingFeeName || '请选择'" showArrow>
  84 + <template v-slot:body>
  85 + <view class="item-title"><text>单价中是否已包含包装费</text></view>
  86 + </template>
  87 + </uni-list-item>
  88 + <uni-list-item class="select-item" :class="form.includesTransportFeeName ? 'is-filled' : 'is-empty'"
  89 + clickable @click="openSheet('includesTransportFee')"
  90 + :rightText="form.includesTransportFeeName || '请选择'" showArrow>
  91 + <template v-slot:body>
  92 + <view class="item-title"><text>单价中是否已包含运费</text></view>
  93 + </template>
  94 + </uni-list-item>
  95 + <uni-list-item title="需方指定收货人">
  96 + <template v-slot:footer>
  97 + <uni-easyinput v-model="form.designatedConsignee" placeholder="请输入需方指定收货人"
  98 + :inputBorder="false" />
  99 + </template>
  100 + </uni-list-item>
  101 +
  102 + <view class="group">
  103 + <view class="group-title">特别条款要求</view>
  104 + <view class="radio-list">
  105 + <view v-for="(opt, i) in specialTermsList" :key="'cr-' + i" class="radio-item"
  106 + @click="onRadioSelect('specialTerms', 'specialTermsName', opt)">
  107 + <view :class="['radio', { checked: form.specialTerms === opt.value }]" />
  108 + <text class="label">{{ opt.label }}</text>
  109 + </view>
  110 + </view>
  111 + </view>
  112 + <view class="group">
  113 + <view class="group-title">执行标准</view>
  114 + <view class="radio-list">
  115 + <view v-for="(opt, i) in executionStandardList" :key="'es-' + i" class="radio-item"
  116 + @click="onRadioSelect('executionStandard', 'executionStandardName', opt)">
  117 + <view :class="['radio', { checked: form.executionStandard === opt.value }]" />
  118 + <text class="label">{{ opt.label }}</text>
  119 + </view>
  120 + </view>
  121 + </view>
  122 + <uni-list-item v-if="form.executionStandard === 'OTHER'" title="其他">
  123 + <template v-slot:footer>
  124 + <uni-easyinput v-model="form.executionStandardRemarks" placeholder="请输入其他标准备注"
  125 + :inputBorder="false" />
  126 + </template>
  127 + </uni-list-item>
  128 + <uni-list-item title="特别说明">
  129 + <template v-slot:footer>
  130 + <uni-easyinput v-model="form.specialInstructions" placeholder="请输入特别说明" :inputBorder="false" />
  131 + </template>
  132 + </uni-list-item>
  133 + <uni-list-item title="备注">
  134 + <template v-slot:footer>
  135 + <uni-easyinput v-model="form.remarks" placeholder="请输入备注" :inputBorder="false" />
  136 + </template>
  137 + </uni-list-item>
  138 +
  139 + <view class="quality">
  140 + <image class="opCollapse" src="/static/images/title.png" />
  141 + <text class="title">具体质量要求</text>
  142 + </view>
  143 + <uni-list-item title="件重条头">
  144 + <template v-slot:footer>
  145 + <uni-easyinput v-model="form.pieceWeightHead" placeholder="请输入" :inputBorder="false" />
  146 + </template>
  147 + </uni-list-item>
  148 + <uni-list-item title="表面">
  149 + <template v-slot:footer>
  150 + <uni-easyinput v-model="form.surface" placeholder="请输入" :inputBorder="false" />
  151 + </template>
  152 + </uni-list-item>
  153 + <uni-list-item title="公差">
  154 + <template v-slot:footer>
  155 + <uni-easyinput v-model="form.tolerance" placeholder="请输入" :inputBorder="false" />
  156 + </template>
  157 + </uni-list-item>
  158 + <uni-list-item title="性能">
  159 + <template v-slot:footer>
  160 + <uni-easyinput v-model="form.performance" placeholder="请输入" :inputBorder="false" />
  161 + </template>
  162 + </uni-list-item>
  163 + <uni-list-item title="成分">
  164 + <template v-slot:footer>
  165 + <uni-easyinput v-model="form.component" placeholder="请输入" :inputBorder="false" />
  166 + </template>
  167 + </uni-list-item>
  168 + <uni-list-item title="包装">
  169 + <template v-slot:footer>
  170 + <uni-easyinput v-model="form.packaging" placeholder="请输入" :inputBorder="false" />
  171 + </template>
  172 + </uni-list-item>
  173 + </uni-list>
  174 + </scroll-view>
  175 + <view class="footer">
  176 + <div class="total">
  177 + <div class="total-text">合计</div>
  178 + <div class="total-item">
  179 + <div class="total-item-text">数量</div>
  180 + <div class="total-item-price">{{ (sumQuantity || 0).toFixed(2) }}t</div>
  181 + </div>
  182 + <div class="total-item">
  183 + <div class="total-item-text">不含税金额</div>
  184 + <div class="total-item-price text-red">¥{{ (sumAmountExcl || 0).toFixed(2) }}</div>
  185 + </div>
  186 + <div class="total-item">
  187 + <div class="total-item-text">总金额</div>
  188 + <div class="total-item-price text-red">¥{{ (sumTotal || 0).toFixed(2) }}</div>
  189 + </div>
  190 + </div>
  191 + <button class="btn submit" type="primary" @click="onSubmit">保存</button>
  192 + </view>
  193 + <SingleSelectSheet :visible.sync="sheet.visible" :title="sheet.title" :options="sheet.options"
  194 + v-model="sheet.value" @confirm="onSheetConfirm" />
  195 + <RelateSelectSheet :visible.sync="relate.visible" :title="relate.title" :source="relate.source"
  196 + :display-fields="relate.display" :multiple="relate.multiple" :row-key="relate.rowKey"
  197 + :selectedKeys.sync="relate.selectedKeys" @confirm="onRelateConfirm" />
  198 + </view>
  199 +</template>
  200 +
  201 +<script>
  202 +import SingleSelectSheet from '@/components/single-select/index.vue'
  203 +import RelateSelectSheet from '@/components/relate-select/index.vue'
  204 +import ProductRel from './productRel.vue'
  205 +import CitySelector from '@/components/city-selector/index.vue'
  206 +import { getContractApi, updateContractApi } from '@/api/contract'
  207 +import { getDicByCodes } from '@/utils/dic'
  208 +import { formatCurrencyToChinese } from '@/utils/common'
  209 +import { workshopQueryApi } from '@/api/devManage'
  210 +export default {
  211 + name: 'ModifyContractForeignStd',
  212 + components: { SingleSelectSheet, RelateSelectSheet, ProductRel, CitySelector },
  213 + data() {
  214 + return {
  215 + id: '',
  216 + form: {
  217 + id: '',
  218 + code: '',
  219 + supplier: '',
  220 + supplierName: '',
  221 + buyer: '',
  222 + buyerName: '',
  223 + workshopId: '',
  224 + workshopName: '',
  225 + orderDate: '',
  226 + designatedConsignee: '',
  227 + specialTerms: '',
  228 + specialTermsName: '',
  229 + executionStandard: '',
  230 + executionStandardName: '',
  231 + executionStandardRemarks: '',
  232 + includesPackagingFee: false,
  233 + includesPackagingFeeName: '',
  234 + includesTransportFee: false,
  235 + includesTransportFeeName: '',
  236 + unit: '元、公斤、元/公斤',
  237 + totalAmountCapital: '',
  238 + depositInfo: '',
  239 + packagingRequirements: '',
  240 + paymentTerms: '',
  241 + transportMode: '',
  242 + destinationId: [],
  243 + destinationLabel: '',
  244 + specialInstructions: '',
  245 + remarks: '',
  246 + pieceWeightHead: '',
  247 + surface: '',
  248 + tolerance: '',
  249 + performance: '',
  250 + component: '',
  251 + packaging: ''
  252 + },
  253 + supplierList: [],
  254 + specialTermsList: [],
  255 + executionStandardList: [],
  256 + yesNoList: [{ label: '是', value: true }, { label: '否', value: false }],
  257 + sheet: { visible: false, title: '请选择', field: '', options: [], value: '' },
  258 + relate: { visible: false, title: '选择', source: '', display: [], multiple: false, rowKey: 'id', selectedKeys: [], fieldKey: '' },
  259 + sumQuantity: 0,
  260 + sumAmountExcl: 0,
  261 + sumTotal: 0,
  262 + productLineList: [],
  263 + newProductLineList: [],
  264 + productList: []
  265 + }
  266 + },
  267 + onLoad(query) {
  268 + this.id = (query && query.id) ? query.id : ''
  269 + },
  270 + created() {
  271 + this.loadSuppliers()
  272 + this.loadExtraOptions()
  273 + this.loadDetail()
  274 + this.$nextTick(() => {
  275 + if (Array.isArray(this.form.destinationId) && this.form.destinationId.length) {
  276 + this.initDestinationLabel()
  277 + }
  278 + })
  279 + },
  280 + methods: {
  281 + async loadDetail() {
  282 + if (!this.id) return
  283 + try {
  284 + const res = await getContractApi(this.id)
  285 + const data = res && res.data ? res.data : {}
  286 + const includesPackagingFeeName = data.includesPackagingFeeName || (data.includesPackagingFee ? '是' : '否')
  287 + const includesTransportFeeName = data.includesTransportFeeName || (data.includesTransportFee ? '是' : '否')
  288 + const m = { ...data, includesPackagingFeeName, includesTransportFeeName }
  289 + this.form = {
  290 + ...this.form,
  291 + id: m.id || '',
  292 + code: m.code || '',
  293 + supplier: m.supplier || '',
  294 + supplierName: m.supplierName || '',
  295 + buyer: m.buyer || (m.customer && m.customer.id) || '',
  296 + buyerName: m.buyerName || (m.customer && m.customer.name) || '',
  297 + orderDate: m.orderDate || '',
  298 + designatedConsignee: m.designatedConsignee || '',
  299 + specialTerms: m.specialTerms || '',
  300 + specialTermsName: m.specialTermsName || '',
  301 + executionStandard: m.executionStandard || '',
  302 + executionStandardName: m.executionStandardName || '',
  303 + executionStandardRemarks: m.executionStandardRemarks || '',
  304 + includesPackagingFee: !!m.includesPackagingFee,
  305 + includesPackagingFeeName,
  306 + includesTransportFee: !!m.includesTransportFee,
  307 + includesTransportFeeName,
  308 + unit: m.unit || this.form.unit,
  309 + totalAmountCapital: m.totalAmountCapital || '',
  310 + depositInfo: m.depositInfo || '',
  311 + packagingRequirements: m.packagingRequirements || '',
  312 + paymentTerms: m.paymentTerms || '',
  313 + transportMode: m.transportMode || '',
  314 + destinationId: (m.provinceId && m.cityId && m.districtId) ? [m.provinceId, m.cityId, m.districtId] : (Array.isArray(m.destinationId) ? m.destinationId : []),
  315 + destinationLabel: (m.provinceName && m.cityName && m.districtName) ? `${m.provinceName} / ${m.cityName} / ${m.districtName}` : (m.destinationLabel || ''),
  316 + specialInstructions: m.specialInstructions || '',
  317 + remarks: m.remarks || '',
  318 + pieceWeightHead: m.pieceWeightHead || '',
  319 + surface: m.surface || '',
  320 + tolerance: m.tolerance || '',
  321 + performance: m.performance || '',
  322 + component: m.component || '',
  323 + packaging: m.packaging || '',
  324 + workshopId: m.workshopId || '',
  325 + workshopName: m.workshopName || '',
  326 + }
  327 + const lines = Array.isArray(m.contractDistributorLineList) ? m.contractDistributorLineList : []
  328 + this.productLineList = lines
  329 + this.onProductsChange(lines)
  330 + } catch (e) { }
  331 + },
  332 + async initDestinationLabel() {
  333 + const comp = this.$refs.citySelectorRef
  334 + if (comp && typeof comp.getLabel === 'function') {
  335 + const label = await comp.getLabel()
  336 + this.form.destinationLabel = label || ''
  337 + }
  338 + },
  339 + openCitySelector() {
  340 + this.$refs.citySelectorRef && this.$refs.citySelectorRef.open()
  341 + },
  342 + onCityChange(payload) {
  343 + const label = payload && payload.label != null ? payload.label : ''
  344 + this.form.destinationLabel = label
  345 + },
  346 + onProductsChange(products) {
  347 + const list = Array.isArray(products) ? products : []
  348 + this.newProductLineList = list
  349 + const sumQ = list.reduce((acc, it) => acc + (parseFloat(it.quantity) || 0), 0)
  350 + const sumE = list.reduce((acc, it) => acc + (parseFloat(it.amountExcludingTax) || 0), 0)
  351 + const sumT = list.reduce((acc, it) => acc + (parseFloat(it.totalAmount) || 0), 0)
  352 + this.sumQuantity = sumQ
  353 + this.sumAmountExcl = sumE
  354 + this.sumTotal = sumT
  355 + this.form.totalAmountCapital = formatCurrencyToChinese(sumT)
  356 + },
  357 + async loadSuppliers() {
  358 + try {
  359 + const results = await getDicByCodes(['SUPPLIER'])
  360 + const items = results && results.SUPPLIER && results.SUPPLIER.data ? results.SUPPLIER.data : []
  361 + this.supplierList = items.map(it => ({ label: it.name, value: it.code }))
  362 + } catch (e) { this.supplierList = [] }
  363 + },
  364 + async loadExtraOptions() {
  365 + try {
  366 + const results = await getDicByCodes(['CONDITIONS_REQUIRED', 'APPLICABLE_STANDARD', 'CONTRACT_PRODUCT'])
  367 + const c1 = results && results.CONDITIONS_REQUIRED && results.CONDITIONS_REQUIRED.data ? results.CONDITIONS_REQUIRED.data : []
  368 + const c2 = results && results.APPLICABLE_STANDARD && results.APPLICABLE_STANDARD.data ? results.APPLICABLE_STANDARD.data : []
  369 + const c3 = results && results.CONTRACT_PRODUCT && results.CONTRACT_PRODUCT.data ? results.CONTRACT_PRODUCT.data : []
  370 + this.specialTermsList = c1.map(it => ({ label: it.name, value: it.code }))
  371 + this.executionStandardList = c2.map(it => ({ label: it.name, value: it.code }))
  372 + this.productList = c3.map(it => ({ label: it.name, value: it.code }))
  373 + } catch (e) {
  374 + this.specialTermsList = []
  375 + this.executionStandardList = []
  376 + this.productList = []
  377 + }
  378 + },
  379 + displayLabel(field) {
  380 + const m = this.form
  381 + const map = { supplierName: '请选择供方', buyerName: '请选择需方', workshopName: '请选择生产厂' }
  382 + const val = m[field]
  383 + return val ? String(val) : map[field]
  384 + },
  385 + async openSheet(field) {
  386 + const setSheet = (title, options) => {
  387 + const current = this.form[field]
  388 + const match = (options || []).find(o => String(o.label) === String(current) || String(o.value) === String(current))
  389 + this.sheet = { ...this.sheet, visible: true, title, options, field, value: match ? match.value : '' }
  390 + }
  391 + if (field === 'workshopId') {
  392 + const res = await workshopQueryApi({ pageIndex: 1, pageSize: 9999 })
  393 + const _data = res.data || {}
  394 + const list = _data.datas || (Array.isArray(_data) ? _data : [])
  395 + const opts = (list || []).map(it => ({
  396 + label: it.name ,
  397 + value: it.id
  398 + }))
  399 + setSheet('生产厂', opts)
  400 + } else if (field === 'supplier') {
  401 + setSheet('供方', this.supplierList)
  402 + } else if (field === 'includesPackagingFee') {
  403 + setSheet('单价中是否已包含包装费', this.yesNoList)
  404 + } else if (field === 'includesTransportFee') {
  405 + setSheet('单价中是否已包含运费', this.yesNoList)
  406 + }
  407 + },
  408 + onSheetConfirm({ value, label }) {
  409 + const field = this.sheet.field
  410 + if (!field) return
  411 + const v = (value === undefined || value === null) ? '' : value
  412 + this.form[field] = v
  413 + this.form[field + 'Name'] = label || ''
  414 + },
  415 + openRelate(fieldKey) {
  416 + let config = {}
  417 + if (fieldKey === 'buyer') {
  418 + config = { title: '需方', source: 'customer', rowKey: 'id', multiple: false, display: [{ label: '姓名', field: 'name' }, { label: '编号', field: 'code' }, { label: '状态', field: 'available', format: v => (v ? '启用' : '停用') }] }
  419 + }
  420 + const selectedKeys = this.form[fieldKey] ? [this.form[fieldKey]] : []
  421 + this.sheet.visible = false
  422 + this.relate = { ...this.relate, title: config.title, source: config.source, display: config.display, multiple: config.multiple, rowKey: config.rowKey, selectedKeys, fieldKey }
  423 + this.$nextTick(() => { this.relate.visible = true })
  424 + },
  425 + onRelateConfirm({ items }) {
  426 + const _fieldKey = this.relate.fieldKey
  427 + const first = (items && items.length > 0) ? items[0] : null
  428 + this.form[_fieldKey] = (first && first.id) ? first.id : ''
  429 + this.form[_fieldKey + 'Name'] = (first && first.name) ? first.name : ''
  430 + },
  431 + onRadioSelect(field, nameField, opt) {
  432 + const val = opt && opt.value != null ? opt.value : ''
  433 + const label = opt && opt.label != null ? opt.label : ''
  434 + this.form[field] = val
  435 + this.form[nameField] = label
  436 + if (field === 'executionStandard' && val !== 'OTHER') {
  437 + this.form.executionStandardRemarks = ''
  438 + }
  439 + },
  440 + validateRequired() {
  441 + const checks = [
  442 + { key: 'code', label: '编号' },
  443 + { key: 'supplier', label: '供方' },
  444 + { key: 'buyer', label: '需方' },
  445 + { key: 'orderDate', label: '订货日期' },
  446 + { key: 'workshopId', label: '生产厂' },
  447 + ]
  448 + for (const it of checks) {
  449 + const val = this.form[it.key]
  450 + const empty = (val === undefined || val === null || (typeof val === 'string' && val.trim() === '') || (typeof val === 'number' && isNaN(val)))
  451 + if (empty) { uni.showToast({ title: `请先选择${it.label}`, icon: 'none' }); return false }
  452 + }
  453 + if (!Array.isArray(this.productLineList) || this.productLineList.length === 0) {
  454 + uni.showToast({ title: '请至少添加一条产品明细', icon: 'none' }); return false
  455 + }
  456 + for (const [idx, it] of this.productLineList.entries()) {
  457 + if (!it.productName || !it.quantity || !it.unitPrice) {
  458 + uni.showToast({ title: `第${idx + 1}条明细未完整填写`, icon: 'none' }); return false
  459 + }
  460 + }
  461 + return true
  462 + },
  463 + async onSubmit() {
  464 + console.log('onSubmit__payload', payload)
  465 + if (!this.validateRequired()) return
  466 + const confirmRes = await new Promise(resolve => {
  467 + uni.showModal({ title: '提示', content: '确定保存当前经销未锁规合同吗?', confirmText: '确定', cancelText: '取消', success: resolve })
  468 + })
  469 + if (!(confirmRes && confirmRes.confirm)) return
  470 + const clean = (obj) => {
  471 + const out = {}
  472 + Object.keys(obj || {}).forEach(k => {
  473 + const v = obj[k]
  474 + const isEmptyString = typeof v === 'string' && v.trim() === ''
  475 + const isUndef = v === undefined || v === null
  476 + const isNaNNumber = typeof v === 'number' && isNaN(v)
  477 + if (!(isEmptyString || isUndef || isNaNNumber)) out[k] = v
  478 + })
  479 + return out
  480 + }
  481 + const lines = (this.newProductLineList || []).map(it => clean(it))
  482 + const { destinationLabel, destinationId, ...formForSubmit } = this.form;
  483 + const destination = destinationId && destinationId.length > 0 ? destinationId[destinationId.length - 1] : '';
  484 + const payload = clean({
  485 + ...formForSubmit,
  486 + id: this.form.id,
  487 + destination,
  488 + type: 'INTL_STD_CONTRACT',
  489 + sumQuantity: this.sumQuantity,
  490 + sumAmountExcl: this.sumAmountExcl,
  491 + sumTotal: this.sumTotal,
  492 + contractDistributorLineList: lines
  493 + })
  494 + try {
  495 + await updateContractApi(payload)
  496 + uni.showToast({ title: '保存成功', icon: 'none' })
  497 + setTimeout(() => { uni.redirectTo({ url: '/pages/contract_foreign_std/index' }) }, 400)
  498 + } catch (e) {
  499 + uni.showToast({ title: '提交失败', icon: 'none' })
  500 + }
  501 + }
  502 + }
  503 +}
  504 +</script>
  505 +
  506 +<style lang="scss" scoped>
  507 +.total {
  508 + .total-text {
  509 + font-weight: 600;
  510 + font-size: 32rpx;
  511 + color: rgba(0, 0, 0, 0.9);
  512 + padding-bottom: 28rpx;
  513 + border-bottom: 2rpx solid #E7E7E7;
  514 + }
  515 +
  516 + .total-item {
  517 + display: flex;
  518 + align-items: center;
  519 + }
  520 +
  521 + .total-item-text {
  522 + font-weight: 400;
  523 + font-size: 28rpx;
  524 + color: rgba(0, 0, 0, 0.6);
  525 + line-height: 32rpx;
  526 + width: 240rpx;
  527 + padding: 24rpx 0;
  528 + }
  529 +
  530 + .total-item-price {
  531 + font-weight: 600;
  532 + font-size: 32rpx;
  533 + color: rgba(0, 0, 0, 0.9);
  534 + line-height: 32rpx;
  535 + }
  536 +
  537 + .text-red {
  538 + color: #D54941;
  539 + }
  540 +}
  541 +
  542 +.page {
  543 + display: flex;
  544 + flex-direction: column;
  545 + height: 100%;
  546 +}
  547 +
  548 +.scroll {
  549 + flex: 1;
  550 + padding: 12rpx 0 480rpx !important;
  551 +}
  552 +
  553 +.footer {
  554 + z-index: 2;
  555 + position: fixed;
  556 + left: 0;
  557 + right: 0;
  558 + bottom: 0;
  559 + padding: 32rpx;
  560 + padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
  561 + background: #fff;
  562 + box-shadow: 0 -8rpx 24rpx rgba(0, 0, 0, 0.06);
  563 +}
  564 +
  565 +.footer .btn {
  566 + height: 80rpx;
  567 + line-height: 80rpx;
  568 + border-radius: 12rpx;
  569 + font-size: 32rpx;
  570 +}
  571 +
  572 +.footer .submit {
  573 + background: $theme-primary;
  574 + color: #fff;
  575 +}
  576 +
  577 +.group {
  578 + background: #fff;
  579 + padding: 24rpx 32rpx;
  580 + margin-bottom: 20rpx;
  581 +}
  582 +
  583 +.group-title {
  584 + color: rgba(0, 0, 0, 0.9);
  585 + font-size: 32rpx;
  586 + padding-bottom: 16rpx;
  587 +}
  588 +
  589 +.radio-list {
  590 + display: flex;
  591 + flex-direction: column;
  592 +}
  593 +.quality {
  594 + background-color: #fff;
  595 + display: flex;
  596 + align-items: center;
  597 + padding: 24rpx 32rpx;
  598 + border-bottom: 1rpx solid #f0f0f0;
  599 + margin-top: 20rpx;
  600 +
  601 + .title {
  602 + font-size: 32rpx;
  603 + color: rgba(0, 0, 0, 0.9);
  604 + font-weight: 600;
  605 + }
  606 +
  607 + .opCollapse {
  608 + color: rgba(0, 0, 0, 0.6);
  609 + width: 24rpx;
  610 + height: 24rpx;
  611 + margin-right: 16rpx;
  612 + margin-top: 8rpx;
  613 + }
  614 +}
  615 +
  616 +.radio-item {
  617 + display: flex;
  618 + align-items: center;
  619 + padding: 24rpx 0;
  620 + border-bottom: 1rpx solid #f0f0f0;
  621 +}
  622 +
  623 +.radio-item:last-child {
  624 + border-bottom: none;
  625 +}
  626 +
  627 +.radio {
  628 + width: 32rpx;
  629 + height: 32rpx;
  630 + border-radius: 50%;
  631 + border: 2rpx solid #DCDCDC;
  632 + margin-right: 20rpx;
  633 + box-sizing: border-box;
  634 +}
  635 +
  636 +.radio.checked {
  637 + background: $theme-primary;
  638 + border-color: $theme-primary;
  639 +}
  640 +
  641 +.radio-item .label {
  642 + font-size: 32rpx;
  643 + color: rgba(0, 0, 0, 0.9);
  644 +}
  645 +
  646 +::v-deep .uni-list {
  647 + background: transparent;
  648 +}
  649 +
  650 +::v-deep .uni-list .uni-easyinput .uni-input-input {
  651 + color: rgba(0, 0, 0, 0.9);
  652 +}
  653 +
  654 +::v-deep .uni-list .uni-input-placeholder {
  655 + z-index: 1;
  656 +}
  657 +
  658 +::v-deep .uni-list .uni-input-input {
  659 + background-color: #ffffff;
  660 +}
  661 +
  662 +::v-deep .uni-list-item__extra-text {
  663 + font-size: 32rpx;
  664 +}
  665 +
  666 +::v-deep .uni-list-item__content-title {
  667 + font-size: 32rpx;
  668 + color: rgba(0, 0, 0, 0.9);
  669 +}
  670 +
  671 +::v-deep .uni-list-item__container {
  672 + padding: 32rpx;
  673 +}
  674 +
  675 +::v-deep .uni-list-item__container .uni-easyinput__placeholder-class {
  676 + font-size: 32rpx;
  677 + color: rgba(0, 0, 0, 0.4);
  678 +}
  679 +
  680 +::v-deep .uni-list-item__container .uni-easyinput__content {
  681 + border: none;
  682 + background-color: #ffffff !important;
  683 +}
  684 +
  685 +::v-deep .uni-list-item__container .uni-easyinput__content-input {
  686 + padding-left: 0 !important;
  687 + height: 48rpx;
  688 + line-height: 48rpx;
  689 + font-size: 32rpx;
  690 +}
  691 +
  692 +::v-deep .uni-list-item__container .uni-easyinput__content .content-clear-icon {
  693 + font-size: 44rpx !important;
  694 +}
  695 +
  696 +::v-deep .uni-list-item__container .item-title,
  697 +::v-deep .uni-list-item__container .uni-list-item__content {
  698 + flex: none;
  699 + min-height: 48rpx;
  700 + line-height: 48rpx;
  701 + font-size: 32rpx;
  702 + position: relative;
  703 + width: 162rpx;
  704 + margin-right: 32rpx;
  705 + color: rgba(0, 0, 0, 0.9);
  706 +}
  707 +
  708 +::v-deep .uni-list-item__container .item-title .required {
  709 + color: red;
  710 + position: absolute;
  711 + top: 50%;
  712 + transform: translateY(-50%);
  713 + left: -16rpx;
  714 +}
  715 +
  716 +::v-deep .uni-list-item.select-item.is-empty .uni-list-item__extra-text {
  717 + color: rgba(0, 0, 0, 0.4) !important;
  718 +}
  719 +
  720 +::v-deep .uni-list-item.select-item.is-filled .uni-list-item__extra-text {
  721 + color: rgba(0, 0, 0, 0.9) !important;
  722 +}
  723 +::v-deep .uni-easyinput {
  724 + &__placeholder-class {
  725 + font-size: 32rpx;
  726 + color: rgba(0, 0, 0, 0.4);
  727 + }
  728 +
  729 + &__content {
  730 + border: none;
  731 + background-color: #ffffff !important;
  732 + height: 100%;
  733 + &-input {
  734 + padding-left: 0 !important;
  735 + height: 48rpx;
  736 + line-height: 48rpx;
  737 + font-size: 32rpx;
  738 + }
  739 +
  740 + .content-clear-icon {
  741 + font-size: 44rpx !important;
  742 + }
  743 + }
  744 + }
  745 +
  746 +</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'" 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 +
  13 + </view>
  14 + </view>
  15 +
  16 + <view v-if="mode === 'add'" class="add-list">
  17 + <view v-for="(item, idx) in items" :key="idx" class="block">
  18 + <uni-list v-show="item.collapsed">
  19 + <uni-list-item class="select-item" :class="item.productName ? 'is-filled' : 'is-empty'" clickable @click="openProductSheet(idx)" :rightText="item.productName || '请选择产品名称'" showArrow>
  20 + <template v-slot:body>
  21 + <view class="item-title"><text>产品名称</text></view>
  22 + </template>
  23 + </uni-list-item>
  24 + <uni-list-item title="行业">
  25 + <template v-slot:footer>
  26 + <uni-easyinput v-model="item.industry" :inputBorder="false" placeholder="请输入行业名称" />
  27 + </template>
  28 + </uni-list-item>
  29 + <uni-list-item title="牌号">
  30 + <template v-slot:footer>
  31 + <uni-easyinput v-model="item.brand" :inputBorder="false" placeholder="请输入牌号" />
  32 + </template>
  33 + </uni-list-item>
  34 + </uni-list>
  35 + <uni-list v-show="!item.collapsed">
  36 + <uni-list-item class="select-item" :class="item.productName ? 'is-filled' : 'is-empty'" clickable @click="openProductSheet(idx)" :rightText="item.productName || '请选择产品名称'" showArrow>
  37 + <template v-slot:body>
  38 + <view class="item-title"><text>产品名称</text></view>
  39 + </template>
  40 + </uni-list-item>
  41 + <uni-list-item title="行业">
  42 + <template v-slot:footer>
  43 + <uni-easyinput v-model="item.industry" :inputBorder="false" placeholder="请输入行业名称" />
  44 + </template>
  45 + </uni-list-item>
  46 + <uni-list-item title="牌号">
  47 + <template v-slot:footer>
  48 + <uni-easyinput v-model="item.brand" :inputBorder="false" placeholder="请输入牌号" />
  49 + </template>
  50 + </uni-list-item>
  51 + <uni-list-item title="品质">
  52 + <template v-slot:footer>
  53 + <uni-easyinput v-model="item.quality" :inputBorder="false" placeholder="请输入品质" />
  54 + </template>
  55 + </uni-list-item>
  56 + <uni-list-item title="厚度">
  57 + <template v-slot:footer>
  58 + <uni-easyinput v-model="item.thickness" :inputBorder="false" placeholder="请输入厚度" />
  59 + </template>
  60 + </uni-list-item>
  61 + <uni-list-item title="厚度公差(单项+)">
  62 + <template v-slot:footer>
  63 + <uni-easyinput v-model="item.thicknessTolPos" :inputBorder="false"
  64 + placeholder="请输入厚度公差(单项+)" />
  65 + </template>
  66 + </uni-list-item>
  67 + <uni-list-item title="厚度公差(单项-)">
  68 + <template v-slot:footer>
  69 + <uni-easyinput v-model="item.thicknessTolNeg" :inputBorder="false"
  70 + placeholder="请输入厚度公差(单项-)" />
  71 + </template>
  72 + </uni-list-item>
  73 + <uni-list-item title="宽度">
  74 + <template v-slot:footer>
  75 + <uni-easyinput v-model="item.width" :inputBorder="false" placeholder="请输入宽度" />
  76 + </template>
  77 + </uni-list-item>
  78 + <uni-list-item title="宽度公差(单项+)">
  79 + <template v-slot:footer>
  80 + <uni-easyinput v-model="item.widthTolPos" :inputBorder="false" placeholder="请输入宽度公差(单项+)" />
  81 + </template>
  82 + </uni-list-item>
  83 + <uni-list-item title="宽度公差(单项-)">
  84 + <template v-slot:footer>
  85 + <uni-easyinput v-model="item.widthTolNeg" :inputBorder="false" placeholder="请输入宽度公差(单项-)" />
  86 + </template>
  87 + </uni-list-item>
  88 + <uni-list-item title="长度">
  89 + <template v-slot:footer>
  90 + <uni-easyinput v-model="item.length" :inputBorder="false" placeholder="请输入长度" />
  91 + </template>
  92 + </uni-list-item>
  93 + <uni-list-item title="长度公差(单项+)">
  94 + <template v-slot:footer>
  95 + <uni-easyinput v-model="item.lengthTolPos" :inputBorder="false"
  96 + placeholder="请输入长度公差(单项+)" />
  97 + </template>
  98 + </uni-list-item>
  99 + <uni-list-item title="长度公差(单项-)">
  100 + <template v-slot:footer>
  101 + <uni-easyinput v-model="item.lengthTolNeg" :inputBorder="false"
  102 + placeholder="请输入长度公差(单项-)" />
  103 + </template>
  104 + </uni-list-item>
  105 + <uni-list-item title="状态">
  106 + <template v-slot:footer>
  107 + <uni-easyinput v-model="item.status" :inputBorder="false" placeholder="请输入状态" />
  108 + </template>
  109 + </uni-list-item>
  110 + <uni-list-item title="数量">
  111 + <template v-slot:footer>
  112 + <uni-easyinput v-model="item.quantity" type="number" :inputBorder="false" placeholder="请输入数量" @input="onImmediateChange(idx)" @blur="onNumberBlur(idx, 'quantity', 0)" />
  113 + </template>
  114 + </uni-list-item>
  115 + <uni-list-item title="单价">
  116 + <template v-slot:footer>
  117 + <uni-easyinput v-model="item.unitPrice" disabled type="number" :inputBorder="false" placeholder="请输入单价" @input="onImmediateChange(idx)" @blur="onNumberBlur(idx, 'unitPrice', 0)" />
  118 + </template>
  119 + </uni-list-item>
  120 + <uni-list-item title="外贸加工费">
  121 + <template v-slot:footer>
  122 + <uni-easyinput v-model="item.processingFee" type="number" :inputBorder="false" placeholder="请输入外贸加工费" />
  123 + </template>
  124 + </uni-list-item>
  125 + <uni-list-item title="不含税金额">
  126 + <template v-slot:footer>
  127 + <uni-easyinput v-model="item.amountExcludingTax" type="number" :inputBorder="false" disabled placeholder="自动计算" />
  128 + </template>
  129 + </uni-list-item>
  130 + <uni-list-item title="总金额">
  131 + <template v-slot:footer>
  132 + <uni-easyinput v-model="item.totalAmount" type="number" :inputBorder="false" disabled placeholder="自动计算" />
  133 + </template>
  134 + </uni-list-item>
  135 + <uni-list-item title="发货日期">
  136 + <template v-slot:footer>
  137 + <uni-datetime-picker type="date" v-model="item.orderDate" @change="onDateChange(idx, $event)" />
  138 + </template>
  139 + </uni-list-item>
  140 + </uni-list>
  141 + <view class="block-ops">
  142 + <div class="del" @click="onRemove(idx)">
  143 + <image src="/static/images/delete.png" class="icon" />
  144 + 删除
  145 + </div>
  146 + <div class="toggle" @click="toggleItem(idx)">
  147 + <image :src="item.collapsed ? '/static/images/up.png' : '/static/images/down.png'" class="icon" />
  148 + {{ item.collapsed ? '展开' : '收起' }}</div>
  149 + </view>
  150 + </view>
  151 + </view>
  152 +
  153 + <view v-else class="view-list" v-show="!collapsedView">
  154 + <view v-for="(item, idx) in items" :key="'v-' + idx" class="card">
  155 + <view class="row"><text class="label">产品名称</text><text class="value">{{ item.productName }}</text></view>
  156 + <view class="row"><text class="label">行业</text><text class="value">{{ item.industry }}</text></view>
  157 + <view class="row"><text class="label">牌号</text><text class="value">{{ item.brand }}</text></view>
  158 + <view class="row"><text class="label">品质</text><text class="value">{{ item.quality }}</text></view>
  159 + <view class="row"><text class="label">规格</text><text class="value">{{ item.specDisplay }}</text></view>
  160 + <view class="row"><text class="label">状态</text><text class="value">{{ item.status }}</text></view>
  161 + <view class="row"><text class="label">数量</text><text class="value">{{ item.quantity }}</text></view>
  162 + <view class="row"><text class="label">单价</text><text class="value">{{ formatCurrency(item.unitPrice)
  163 + }}</text>
  164 + </view>
  165 + <view class="row"><text class="label">不含税金额</text><text class="value">{{
  166 + formatCurrency(item.amountExcludingTax)
  167 + }}</text></view>
  168 + <view class="row"><text class="label">总金额</text><text class="value">{{ formatCurrency(item.totalAmount)
  169 + }}</text></view>
  170 + <view class="row"><text class="label">发货日期</text><text class="value">{{ item.orderDate }}</text></view>
  171 + </view>
  172 + </view>
  173 + <SingleSelectSheet :visible.sync="sheet.visible" :title="sheet.title" :options="sheet.options" v-model="sheet.value" @confirm="onProductConfirm" />
  174 + </view>
  175 +</template>
  176 +<script>
  177 +import SingleSelectSheet from '@/components/single-select/index.vue'
  178 +export default {
  179 + name: 'ProductRel',
  180 + props: {
  181 + mode: { type: String, default: 'add' },
  182 + list: { type: Array, default: () => [] },
  183 + max: { type: Number, default: 8 },
  184 + orderDateBase: { type: String, default: '' },
  185 + options: { type: Array, default: () => [] }
  186 + },
  187 + components: { SingleSelectSheet },
  188 + data() {
  189 + return {
  190 + items: [],
  191 + collapsedView: false,
  192 + sheet: { visible: false, title: '请选择产品', options: [], value: '', idx: -1 }
  193 + }
  194 + },
  195 + computed: {
  196 + selectOptions() {
  197 + const list = Array.isArray(this.options) ? this.options : []
  198 + return list.map(o => ({
  199 + label: o.label != null ? o.label : (o.text != null ? o.text : (o.name != null ? o.name : '')),
  200 + value: o.value != null ? o.value : (o.id != null ? o.id : o.productId)
  201 + }))
  202 + }
  203 + },
  204 + watch: {
  205 + items: {
  206 + handler() { this.emitChange() },
  207 + deep: true
  208 + },
  209 + list: {
  210 + handler(v) {
  211 + // const arr = Array.isArray(v) ? v : []
  212 + // this.items = arr.map(x => ({ ...this.defaultItem(), ...x, collapsed: true }))
  213 + this.items = v.map(x => ({ ...this.defaultItem(), ...x, collapsed: true }))
  214 + console.log('v', v)
  215 + },
  216 + deep: true
  217 + }
  218 + },
  219 + created() {
  220 + const init = Array.isArray(this.list) && this.list.length > 0 ? this.list.map(v => ({ ...this.defaultItem(), ...v, collapsed: true })) : [{ ...this.defaultItem(), collapsed: false }]
  221 + this.items = init
  222 + this.recalculateAll()
  223 + },
  224 + methods: {
  225 + defaultItem() {
  226 + return { productId: '', productName: '', industry: '', brand: '', quality: '', thickness: '', thicknessTolPos: '', thicknessTolNeg: '', width: '', widthTolPos: '', widthTolNeg: '', length: '', lengthTolPos: '', lengthTolNeg: '', status: '', quantity: '', unitPrice: '', processingFee: undefined, amountExcludingTax: 0, totalAmount: 0, orderDate: '' }
  227 + },
  228 + onImmediateChange(idx) {
  229 + this.$nextTick(() => this.recalculate(idx))
  230 + },
  231 + toNumber(val) {
  232 + if (typeof val === 'number') return isNaN(val) ? 0 : val
  233 + const n = parseFloat(String(val).replace(/[^0-9.\-]/g, ''))
  234 + return isNaN(n) ? 0 : n
  235 + },
  236 + round(val, digits = 2) {
  237 + const n = Number(val)
  238 + if (isNaN(n)) return 0
  239 + const m = Math.pow(10, digits)
  240 + return Math.round(n * m) / m
  241 + },
  242 + onNumberBlur(idx, field, digits) {
  243 + const it = this.items[idx]
  244 + if (!it) return
  245 + const raw = it[field]
  246 + // 如果为空则保持为空,不自动置为0,仅重新计算依赖字段
  247 + if (raw === '' || raw === null || raw === undefined) {
  248 + this.$set(this.items, idx, it)
  249 + this.recalculate(idx)
  250 + return
  251 + }
  252 + const num = this.toNumber(raw)
  253 + const rounded = this.round(num, digits)
  254 + it[field] = rounded
  255 + this.$set(this.items, idx, it)
  256 + this.recalculate(idx)
  257 + },
  258 + formatCurrency(val) {
  259 + if (val == null || val === '') return ''
  260 + const num = Number(val)
  261 + const pre = isNaN(num) ? '' : '¥'
  262 + const fixed = isNaN(num) ? String(val) : num.toFixed(2)
  263 + return `${pre}${fixed}`
  264 + },
  265 + specOf(item) {
  266 + const t = [item.thickness, item.thicknessTolPos, item.thicknessTolNeg].filter(Boolean).join('/')
  267 + const w = [item.width, item.widthTolPos, item.widthTolNeg].filter(Boolean).join('/')
  268 + const l = [item.length, item.lengthTolPos, item.lengthTolNeg].filter(Boolean).join('/')
  269 + return [t, w, l].filter(Boolean).join(' × ')
  270 + },
  271 + openProductSheet(idx) {
  272 + const opts = this.selectOptions
  273 + const current = this.items[idx] && this.items[idx].productId
  274 + const match = opts.find(o => o.value === current)
  275 + this.sheet = { ...this.sheet, visible: true, title: '请选择产品', options: opts, idx, value: match ? match.value : '' }
  276 + },
  277 + onProductConfirm({ value, label }) {
  278 + const idx = this.sheet.idx
  279 + const it = this.items[idx]
  280 + if (!it) { this.sheet.visible = false; return }
  281 + it.productId = value
  282 + it.productName = label || ''
  283 + this.$set(this.items, idx, it)
  284 + this.sheet.visible = false
  285 + this.emitChange()
  286 + },
  287 + recalculate(idx) {
  288 + const TAX_RATE = 0.13
  289 + const it = this.items[idx]
  290 + if (!it) return
  291 + const qty = this.toNumber(it.quantity)
  292 + const price = this.toNumber(it.unitPrice)
  293 + const total = this.round(qty * price, 2)
  294 + const excl = this.round(total / (1 + TAX_RATE), 2)
  295 + const next = { ...it, totalAmount: total, amountExcludingTax: excl }
  296 + this.$set(this.items, idx, next)
  297 + },
  298 + recalculateAll() {
  299 + for (let i = 0; i < this.items.length; i++) this.recalculate(i)
  300 + },
  301 + onAdd() {
  302 + if (this.items.length >= this.max) return uni.showToast({ title: `最多添加${this.max}个`, icon: 'none' })
  303 + const obj = this.defaultItem()
  304 + obj.collapsed = true
  305 + this.items.push(obj)
  306 + this.emitChange()
  307 + },
  308 + onRemove(idx) {
  309 + this.items.splice(idx, 1)
  310 + this.emitChange()
  311 + },
  312 + toggleItem(idx) {
  313 + const it = this.items[idx]
  314 + if (!it) return
  315 + it.collapsed = !it.collapsed
  316 + this.$set(this.items, idx, it)
  317 + },
  318 + emitChange() {
  319 + const out = this.items.map(it => ({ ...it, specDisplay: this.specOf(it) }))
  320 + this.$emit('input', out)
  321 + this.$emit('update:value', out)
  322 + this.$emit('change', out)
  323 + },
  324 + onDateChange(idx, e) {
  325 + const it = this.items[idx]
  326 + if (!it) return
  327 + const val = typeof e === 'string' ? e : (e && e.detail && e.detail.value) ? e.detail.value : it.orderDate
  328 + const dateStr = String(val).slice(0, 10)
  329 + const base = this.orderDateBase ? new Date(this.orderDateBase) : null
  330 + const d = new Date(dateStr)
  331 + if (base && !isNaN(d.getTime()) && d.getTime() < base.getTime()) {
  332 + uni.showToast({ title: '发货日期不得早于订货日期', icon: 'none' })
  333 + it.orderDate = this.orderDateBase
  334 + } else {
  335 + it.orderDate = dateStr
  336 + }
  337 + this.$set(this.items, idx, it)
  338 + },
  339 + toggleViewCollapse() {
  340 + this.collapsedView = !this.collapsedView
  341 + }
  342 + }
  343 +}
  344 +</script>
  345 +<style lang="scss" scoped>
  346 +.product-rel {
  347 + margin-top: 10px;
  348 +}
  349 +
  350 +.header {
  351 + background-color: #fff;
  352 + display: flex;
  353 + align-items: center;
  354 + padding: 24rpx 32rpx;
  355 + border-bottom: 1rpx solid #f0f0f0;
  356 +}
  357 +
  358 +.dot {
  359 + width: 16rpx;
  360 + height: 16rpx;
  361 + background: #3D48A3;
  362 + border-radius: 50%;
  363 + margin-right: 12rpx;
  364 +}
  365 +
  366 +.title {
  367 + font-size: 32rpx;
  368 + color: rgba(0, 0, 0, 0.9);
  369 + font-weight: 600;
  370 +}
  371 +
  372 +.ops {
  373 + margin-left: auto;
  374 +}
  375 +.op {
  376 + color: $theme-primary;
  377 + font-size: 28rpx;
  378 + margin-left: 8rpx;
  379 +}
  380 +.op1 {
  381 + display: flex;
  382 + align-items: center;
  383 +}
  384 +
  385 +.opAdd {
  386 + color: rgba(0, 0, 0, 0.6);
  387 + width: 40rpx;
  388 + height: 40rpx;
  389 +}
  390 +
  391 +.opCollapse {
  392 + color: rgba(0, 0, 0, 0.6);
  393 + width: 24rpx;
  394 + height: 24rpx;
  395 + margin-right: 16rpx;
  396 + margin-top: 8rpx;
  397 +}
  398 +
  399 +
  400 +.block {
  401 + background: #fff;
  402 + margin-bottom: 20rpx;
  403 +}
  404 +::v-deep .uni-list-item__content {
  405 + display: flex;
  406 + justify-content: center;
  407 +}
  408 +::v-deep .uni-list {
  409 + background: transparent;
  410 +
  411 + &-item {
  412 + &__container {
  413 + padding: 32rpx;
  414 + }
  415 +
  416 + &__content-title {
  417 + font-size: 28rpx;
  418 + color: rgba(0, 0, 0, 0.9);
  419 + }
  420 +
  421 + &__extra-text {
  422 + font-size: 32rpx;
  423 + }
  424 +
  425 + .uni-easyinput {
  426 + width: 100%;
  427 +
  428 + &__placeholder-class {
  429 + font-size: 32rpx;
  430 + color: rgba(0, 0, 0, 0.4);
  431 + }
  432 +
  433 + &__content {
  434 + border: none;
  435 + display: flex;
  436 + &-input {
  437 + padding-left: 0 !important;
  438 + height: 48rpx;
  439 + line-height: 48rpx;
  440 + font-size: 32rpx;
  441 + }
  442 + }
  443 +
  444 + .uni-input-placeholder {
  445 + // z-index: 2;
  446 + }
  447 + }
  448 + }
  449 +}
  450 +
  451 +.block-ops {
  452 + display: flex;
  453 + padding: 20rpx 32rpx 20rpx;
  454 + justify-content: space-around;
  455 +}
  456 +
  457 +.del {
  458 + color: #D54941;
  459 + font-size: 28rpx;
  460 + display: flex;
  461 + align-items: center;
  462 + image {
  463 + width: 40rpx;
  464 + height: 40rpx;
  465 + }
  466 +}
  467 +
  468 +.toggle {
  469 + color: $theme-primary;
  470 + font-size: 28rpx;
  471 + display: flex;
  472 + align-items: center;
  473 + image {
  474 + width: 40rpx;
  475 + height: 40rpx;
  476 + }
  477 +}
  478 +
  479 +.view-list {
  480 + padding: 24rpx 32rpx;
  481 + background: #ffffff;
  482 +}
  483 +
  484 +.card {
  485 + background: #f3f3f3;
  486 + border-radius: 16rpx;
  487 + padding: 24rpx;
  488 + margin-bottom: 20rpx;
  489 +}
  490 +
  491 +.row {
  492 + display: flex;
  493 + margin-bottom: 20rpx;
  494 +}
  495 +
  496 +.row:last-child {
  497 + margin-bottom: 0;
  498 +}
  499 +
  500 +.label {
  501 + width: 120rpx;
  502 + color: rgba(0, 0, 0, 0.6);
  503 + font-size: 28rpx;
  504 +}
  505 +
  506 +.value {
  507 + flex: 1;
  508 + text-align: right;
  509 + color: rgba(0, 0, 0, 0.9);
  510 + font-size: 28rpx;
  511 +}
  512 +</style>
  1 +<template>
  2 + <view class="page">
  3 + <scroll-view class="scroll" scroll-y>
  4 + <uni-list>
  5 + <uni-list-item title="编号">
  6 + <template v-slot:footer>
  7 + <uni-easyinput v-model="form.code" :inputBorder="false" disabled />
  8 + </template>
  9 + </uni-list-item>
  10 +
  11 + <uni-list-item class="select-item" :class="form.supplier ? 'is-filled' : 'is-empty'" clickable
  12 + @click="openSheet('supplier')" :rightText="displayLabel('supplierName')" showArrow>
  13 + <template v-slot:body>
  14 + <view class="item-title"><text class="required">*</text><text>供方</text></view>
  15 + </template>
  16 + </uni-list-item>
  17 +
  18 + <uni-list-item class="select-item" :class="form.buyer ? 'is-filled' : 'is-empty'" clickable
  19 + @click="openRelate('buyer')" :rightText="form.buyerName || '请选择需方'" showArrow>
  20 + <template v-slot:body>
  21 + <view class="item-title"><text class="required">*</text><text>需方</text></view>
  22 + </template>
  23 + </uni-list-item>
  24 +
  25 + <uni-list-item title="订货日期">
  26 + <template v-slot:footer>
  27 + <uni-datetime-picker type="date" v-model="form.orderDate" />
  28 + </template>
  29 + </uni-list-item>
  30 +
  31 + <uni-list-item title="单位">
  32 + <template v-slot:footer>
  33 + <uni-easyinput v-model="form.unit" :inputBorder="false" disabled />
  34 + </template>
  35 + </uni-list-item>
  36 + <uni-list-item class="select-item" :class="form.workshopId ? 'is-filled' : 'is-empty'" clickable
  37 + @click="openSheet('workshopId')" :rightText="displayLabel('workshopIdName')" showArrow>
  38 + <template v-slot:body>
  39 + <view class="item-title"><text class="required">*</text><text>生产厂</text></view>
  40 + </template>
  41 + </uni-list-item>
  42 + <ProductRel mode="add" :orderDateBase="form.orderDate" @change="onProductsChange" :options="productList" />
  43 + <uni-list-item title="合计人民币金额(大写)">
  44 + <template v-slot:footer>
  45 + <uni-easyinput v-model="form.totalAmountCapital" placeholder="自动计算" :inputBorder="false" disabled />
  46 + </template>
  47 + </uni-list-item>
  48 + <uni-list-item title="交付定金、数额、时间">
  49 + <template v-slot:footer>
  50 + <uni-easyinput v-model="form.depositInfo" placeholder="请输入交付定金、数额、时间" :inputBorder="false" />
  51 + </template>
  52 + </uni-list-item>
  53 + <uni-list-item title="包装要求">
  54 + <template v-slot:footer>
  55 + <uni-easyinput v-model="form.packagingRequirements" placeholder="请输入包装要求"
  56 + :inputBorder="false" />
  57 + </template>
  58 + </uni-list-item>
  59 + <uni-list-item title="付款方式、付款期限">
  60 + <template v-slot:footer>
  61 + <uni-easyinput v-model="form.paymentTerms" placeholder="请输入付款方式、付款期限" :inputBorder="false" />
  62 + </template>
  63 + </uni-list-item>
  64 + <uni-list-item title="运输方式">
  65 + <template v-slot:footer>
  66 + <uni-easyinput v-model="form.transportMode" placeholder="请输入运输方式" :inputBorder="false" />
  67 + </template>
  68 + </uni-list-item>
  69 +
  70 + <uni-list-item class="select-item" :class="(Array.isArray(form.destinationId) && form.destinationId.length) ? 'is-filled' : 'is-empty'" clickable
  71 + @click="openCitySelector" :rightText="form.destinationLabel || '请选择'" showArrow>
  72 + <template v-slot:body>
  73 + <view class="item-title"><text>目的地</text></view>
  74 + <CitySelector ref="citySelectorRef" v-model="form.destinationId" @change="onCityChange" />
  75 + </template>
  76 + </uni-list-item>
  77 + <uni-list-item class="select-item" :class="form.includesPackagingFeeName ? 'is-filled' : 'is-empty'" clickable
  78 + @click="openSheet('includesPackagingFee')" :rightText="form.includesPackagingFeeName || '请选择'" showArrow>
  79 + <template v-slot:body>
  80 + <view class="item-title"><text>单价中是否已包含包装费</text></view>
  81 + </template>
  82 + </uni-list-item>
  83 + <uni-list-item class="select-item" :class="form.includesTransportFeeName ? 'is-filled' : 'is-empty'" clickable
  84 + @click="openSheet('includesTransportFee')" :rightText="form.includesTransportFeeName || '请选择'" showArrow>
  85 + <template v-slot:body>
  86 + <view class="item-title"><text>单价中是否已包含运费</text></view>
  87 + </template>
  88 + </uni-list-item>
  89 + <uni-list-item title="需方指定收货人">
  90 + <template v-slot:footer>
  91 + <uni-easyinput v-model="form.designatedConsignee" placeholder="请输入需方指定收货人"
  92 + :inputBorder="false" />
  93 + </template>
  94 + </uni-list-item>
  95 + <view class="group">
  96 + <view class="group-title">特别条款要求</view>
  97 + <view class="radio-list">
  98 + <view v-for="(opt, i) in specialTermsList" :key="'cr-' + i" class="radio-item"
  99 + @click="onRadioSelect('specialTerms', 'specialTermsName', opt)">
  100 + <view :class="['radio', { checked: form.specialTerms === opt.value }]" />
  101 + <text class="label">{{ opt.label }}</text>
  102 + </view>
  103 + </view>
  104 + </view>
  105 + <view class="group">
  106 + <view class="group-title">执行标准</view>
  107 + <view class="radio-list">
  108 + <view v-for="(opt, i) in executionStandardList" :key="'es-' + i" class="radio-item"
  109 + @click="onRadioSelect('executionStandard', 'executionStandardName', opt)">
  110 + <view :class="['radio', { checked: form.executionStandard === opt.value }]" />
  111 + <text class="label">{{ opt.label }}</text>
  112 + </view>
  113 + </view>
  114 + </view>
  115 + <uni-list-item v-if="form.executionStandard === 'OTHER'" title="其他">
  116 + <template v-slot:footer>
  117 + <uni-easyinput v-model="form.executionStandardRemarks" placeholder="请输入其他标准备注"
  118 + :inputBorder="false" />
  119 + </template>
  120 + </uni-list-item>
  121 + <uni-list-item title="特别说明">
  122 + <template v-slot:footer>
  123 + <uni-easyinput v-model="form.specialInstructions" placeholder="请输入特别说明" :inputBorder="false" />
  124 + </template>
  125 + </uni-list-item>
  126 + <uni-list-item title="备注">
  127 + <template v-slot:footer>
  128 + <uni-easyinput v-model="form.remarks" placeholder="请输入备注" :inputBorder="false" />
  129 + </template>
  130 + </uni-list-item>
  131 + <view class="quality">
  132 + <image class="opCollapse" src="/static/images/title.png" />
  133 + <text class="title">具体质量要求</text>
  134 + </view>
  135 + <uni-list-item title="件重条头">
  136 + <template v-slot:footer>
  137 + <uni-easyinput v-model="form.pieceWeightHead" placeholder="请输入" :inputBorder="false" />
  138 + </template>
  139 + </uni-list-item>
  140 + <uni-list-item title="表面">
  141 + <template v-slot:footer>
  142 + <uni-easyinput v-model="form.surface" placeholder="请输入" :inputBorder="false" />
  143 + </template>
  144 + </uni-list-item>
  145 + <uni-list-item title="公差">
  146 + <template v-slot:footer>
  147 + <uni-easyinput v-model="form.tolerance" placeholder="请输入" :inputBorder="false" />
  148 + </template>
  149 + </uni-list-item>
  150 + <uni-list-item title="性能">
  151 + <template v-slot:footer>
  152 + <uni-easyinput v-model="form.performance" placeholder="请输入" :inputBorder="false" />
  153 + </template>
  154 + </uni-list-item>
  155 + <uni-list-item title="成分">
  156 + <template v-slot:footer>
  157 + <uni-easyinput v-model="form.component" placeholder="请输入" :inputBorder="false" />
  158 + </template>
  159 + </uni-list-item>
  160 + <uni-list-item title="包装">
  161 + <template v-slot:footer>
  162 + <uni-easyinput v-model="form.packaging" placeholder="请输入" :inputBorder="false" />
  163 + </template>
  164 + </uni-list-item>
  165 + </uni-list>
  166 +
  167 + </scroll-view>
  168 + <view class="footer">
  169 + <div class="total">
  170 + <div class="total-text">
  171 + 合计
  172 + </div>
  173 + <div class="total-item">
  174 + <div class="total-item-text">
  175 + 数量
  176 + </div>
  177 + <div class="total-item-price">
  178 + {{ (sumQuantity || 0).toFixed(2) }}t
  179 + </div>
  180 + </div>
  181 + <div class="total-item">
  182 + <div class="total-item-text">
  183 + 不含税金额
  184 + </div>
  185 + <div class="total-item-price text-red">
  186 + ¥{{ (sumAmountExcl || 0).toFixed(2) }}
  187 + </div>
  188 + </div>
  189 + <div class="total-item">
  190 + <div class="total-item-text">
  191 + 总金额
  192 + </div>
  193 + <div class="total-item-price text-red">
  194 + ¥{{ (sumTotal || 0).toFixed(2) }}
  195 + </div>
  196 + </div>
  197 + </div>
  198 + <button class="btn submit" type="primary" @click="onSubmit">提交</button>
  199 + </view>
  200 + <SingleSelectSheet :visible.sync="sheet.visible" :title="sheet.title" :options="sheet.options"
  201 + v-model="sheet.value" @confirm="onSheetConfirm" />
  202 + <RelateSelectSheet :visible.sync="relate.visible" :title="relate.title" :source="relate.source"
  203 + :display-fields="relate.display" :multiple="relate.multiple" :row-key="relate.rowKey"
  204 + :selectedKeys.sync="relate.selectedKeys" @confirm="onRelateConfirm" />
  205 + </view>
  206 +
  207 +</template>
  208 +<script>
  209 +import SingleSelectSheet from '@/components/single-select/index.vue'
  210 +import RelateSelectSheet from '@/components/relate-select/index.vue'
  211 +import ProductRel from './productRel.vue'
  212 +import CitySelector from '@/components/city-selector/index.vue'
  213 +import { getRetailCodeApi, createContractApi } from '@/api/contract'
  214 +import { getDicByCodes } from '@/utils/dic'
  215 +import { formatCurrencyToChinese } from '@/utils/common'
  216 +import { workshopQueryApi } from '@/api/devManage'
  217 +
  218 +export default {
  219 + name: 'AddContractForeignStock',
  220 + components: { SingleSelectSheet, RelateSelectSheet, ProductRel, CitySelector },
  221 + data() {
  222 + return {
  223 + form: {
  224 + code: '',
  225 + supplier: '',
  226 + supplierName: '',
  227 + buyer: '',
  228 + buyerName: '',
  229 + orderDate: '',
  230 + designatedConsignee: '',
  231 + specialTerms: '',
  232 + specialTermsName: '',
  233 + executionStandard: '',
  234 + executionStandardName: '',
  235 + executionStandardRemarks: '',
  236 + includesPackagingFee: false,
  237 + includesPackagingFeeName: '',
  238 + includesTransportFee: false,
  239 + includesTransportFeeName: '',
  240 + unit: '元、公斤、元/公斤',
  241 + totalAmountCapital: '',
  242 + destinationId: [],
  243 + destinationLabel: '',
  244 + workshopIdName: '',
  245 + workshopId: '',
  246 + },
  247 + supplierList: [],
  248 + specialTermsList: [],
  249 + executionStandardList: [],
  250 + yesNoList: [{ label: '是', value: true }, { label: '否', value: false }],
  251 + sheet: { visible: false, title: '请选择', field: '', options: [], value: '' },
  252 + relate: { visible: false, title: '选择', source: '', display: [], multiple: false, rowKey: 'id', selectedKeys: [], fieldKey: '' },
  253 + sumQuantity: 0,
  254 + sumAmountExcl: 0,
  255 + sumTotal: 0,
  256 + productLineList: [],
  257 + productList: [],
  258 + }
  259 + },
  260 + created() {
  261 + this.loadSuppliers()
  262 + this.loadExtraOptions()
  263 + this.initCode()
  264 + this.form.orderDate = this.formatDate(new Date())
  265 + this.$nextTick(() => {
  266 + if (Array.isArray(this.form.destinationId) && this.form.destinationId.length) {
  267 + this.initDestinationLabel()
  268 + }
  269 + })
  270 + },
  271 + methods: {
  272 + onProductsChange(products) {
  273 + const list = Array.isArray(products) ? products : []
  274 + const sumQ = list.reduce((acc, it) => acc + (parseFloat(it.quantity) || 0), 0)
  275 + const sumE = list.reduce((acc, it) => acc + (parseFloat(it.amountExcludingTax) || 0), 0)
  276 + const sumT = list.reduce((acc, it) => acc + (parseFloat(it.totalAmount) || 0), 0)
  277 + this.sumQuantity = sumQ
  278 + this.sumAmountExcl = sumE
  279 + this.sumTotal = sumT
  280 + this.form.totalAmountCapital = formatCurrencyToChinese(sumT)
  281 + this.productLineList = list
  282 + },
  283 + formatDate(d) {
  284 + const y = d.getFullYear()
  285 + const m = String(d.getMonth() + 1).padStart(2, '0')
  286 + const day = String(d.getDate()).padStart(2, '0')
  287 + return `${y}-${m}-${day}`
  288 + },
  289 + async initCode() {
  290 + try {
  291 + const res = await getRetailCodeApi()
  292 + const code = (res && res.data) ? res.data : ''
  293 + this.form.code = code
  294 + } catch (e) { this.form.code = '' }
  295 + },
  296 + async loadSuppliers() {
  297 + try {
  298 + const results = await getDicByCodes(['SUPPLIER'])
  299 + const items = results && results.SUPPLIER && results.SUPPLIER.data ? results.SUPPLIER.data : []
  300 + this.supplierList = items.map(it => ({ label: it.name, value: it.code }))
  301 + } catch (e) { this.supplierList = [] }
  302 + },
  303 + async loadExtraOptions() {
  304 + try {
  305 + const results = await getDicByCodes(['CONDITIONS_REQUIRED', 'APPLICABLE_STANDARD', 'CONTRACT_PRODUCT'])
  306 + const c1 = results && results.CONDITIONS_REQUIRED && results.CONDITIONS_REQUIRED.data ? results.CONDITIONS_REQUIRED.data : []
  307 + const c2 = results && results.APPLICABLE_STANDARD && results.APPLICABLE_STANDARD.data ? results.APPLICABLE_STANDARD.data : []
  308 + const c3 = results && results.CONTRACT_PRODUCT && results.CONTRACT_PRODUCT.data ? results.CONTRACT_PRODUCT.data : []
  309 + this.specialTermsList = c1.map(it => ({ label: it.name, value: it.code }))
  310 + this.executionStandardList = c2.map(it => ({ label: it.name, value: it.code }))
  311 + this.productList = c3.map(it => ({ label: it.name, value: it.code }))
  312 + } catch (e) {
  313 + this.specialTermsList = []
  314 + this.executionStandardList = []
  315 + this.productList = []
  316 + }
  317 + },
  318 + displayLabel(field) {
  319 + const m = this.form
  320 + const map = { supplierName: '请选择供方', buyerName: '请选择需方', workshopIdName: '请选择生产厂' }
  321 + const val = m[field]
  322 + return val ? String(val) : map[field]
  323 + },
  324 + openCitySelector() {
  325 + this.$refs.citySelectorRef && this.$refs.citySelectorRef.open()
  326 + },
  327 + async initDestinationLabel() {
  328 + const comp = this.$refs.citySelectorRef
  329 + if (comp && typeof comp.getLabel === 'function') {
  330 + const label = await comp.getLabel()
  331 + this.form.destinationLabel = label || ''
  332 + }
  333 + },
  334 + onCityChange(payload) {
  335 + const label = payload && payload.label != null ? payload.label : ''
  336 + this.form.destinationLabel = label
  337 + },
  338 + async openSheet(field) {
  339 + const setSheet = (title, options) => {
  340 + const current = this.form[field]
  341 + const match = (options || []).find(o => String(o.label) === String(current) || String(o.value) === String(current))
  342 + this.sheet = { ...this.sheet, visible: true, title, options, field, value: match ? match.value : '' }
  343 + }
  344 + if (field === 'workshopId') {
  345 + const res = await workshopQueryApi({ pageIndex: 1, pageSize: 9999 })
  346 + const _data = res.data || {}
  347 + const list = _data.datas || (Array.isArray(_data) ? _data : [])
  348 + const opts = (list || []).map(it => ({
  349 + label: it.name ,
  350 + value: it.id
  351 + }))
  352 + setSheet('生产厂', opts)
  353 + } else if (field === 'supplier') {
  354 + setSheet('供方', this.supplierList)
  355 + } else if (field === 'includesPackagingFee') {
  356 + setSheet('单价中是否已包含包装费', this.yesNoList)
  357 + } else if (field === 'includesTransportFee') {
  358 + setSheet('单价中是否已包含运费', this.yesNoList)
  359 + }
  360 + },
  361 + onSheetConfirm({ value, label }) {
  362 + const field = this.sheet.field
  363 + if (!field) return
  364 + const v = (value === undefined || value === null) ? '' : value
  365 + this.form[field] = v
  366 + this.form[field + 'Name'] = label || ''
  367 + },
  368 + openRelate(fieldKey) {
  369 + let config = {}
  370 + if (fieldKey === 'buyer') {
  371 + config = { title: '需方', source: 'customer', rowKey: 'id', multiple: false, display: [{ label: '姓名', field: 'name' }, { label: '编号', field: 'code' }, { label: '状态', field: 'available', format: v => (v ? '启用' : '停用') }] }
  372 + }
  373 + const selectedKeys = this.form[fieldKey] ? [this.form[fieldKey]] : []
  374 + this.sheet.visible = false
  375 + this.relate = { ...this.relate, title: config.title, source: config.source, display: config.display, multiple: config.multiple, rowKey: config.rowKey, selectedKeys, fieldKey }
  376 + this.$nextTick(() => { this.relate.visible = true })
  377 + },
  378 + onRelateConfirm({ items }) {
  379 + const _fieldKey = this.relate.fieldKey
  380 + const first = (items && items.length > 0) ? items[0] : null
  381 + this.form[_fieldKey] = (first && first.id) ? first.id : ''
  382 + this.form[_fieldKey + 'Name'] = (first && first.name) ? first.name : ''
  383 + },
  384 + onRadioSelect(field, nameField, opt) {
  385 + const val = opt && opt.value != null ? opt.value : ''
  386 + const label = opt && opt.label != null ? opt.label : ''
  387 + this.form[field] = val
  388 + this.form[nameField] = label
  389 + if (field === 'executionStandard' && val !== 'OTHER') {
  390 + this.form.executionStandardRemarks = ''
  391 + }
  392 + },
  393 + async onSubmit() {
  394 + if (!this.validateRequired()) return
  395 + const confirmRes = await new Promise(resolve => {
  396 + uni.showModal({ title: '提示', content: '确定新增经销未锁规合同吗?', confirmText: '确定', cancelText: '取消', success: resolve })
  397 + })
  398 + if (!(confirmRes && confirmRes.confirm)) return
  399 + const clean = (obj) => {
  400 + const out = {}
  401 + Object.keys(obj || {}).forEach(k => {
  402 + const v = obj[k]
  403 + const isEmptyString = typeof v === 'string' && v.trim() === ''
  404 + const isUndef = v === undefined || v === null
  405 + const isNaNNumber = typeof v === 'number' && isNaN(v)
  406 + if (!(isEmptyString || isUndef || isNaNNumber)) out[k] = v
  407 + })
  408 + return out
  409 + }
  410 + const lines = (this.productLineList || []).map(it => clean(it))
  411 + const { destinationLabel, destinationId, ...formForSubmit } = this.form;
  412 + // 区id
  413 + const destination = destinationId && destinationId.length > 0 ? destinationId[destinationId.length - 1] : '';
  414 + const payload = clean({
  415 + ...formForSubmit,
  416 + destination,
  417 + type: 'INTL_INVENTORY_AGMT',
  418 + sumQuantity: this.sumQuantity,
  419 + sumAmountExcl: this.sumAmountExcl,
  420 + sumTotal: this.sumTotal,
  421 + contractDistributorLineList: lines
  422 + })
  423 + console.log('onSubmit__payload', payload)
  424 +
  425 + try {
  426 + await createContractApi(payload)
  427 + uni.showToast({ title: '新增成功', icon: 'none' })
  428 + setTimeout(() => { uni.redirectTo({ url: '/pages/contract_foreign_stock/index' }) }, 400)
  429 + } catch (e) {
  430 + uni.showToast({ title: '提交失败', icon: 'none' })
  431 + }
  432 + },
  433 + validateRequired() {
  434 + const checks = [
  435 + { key: 'code', label: '编号' },
  436 + { key: 'supplier', label: '供方' },
  437 + { key: 'buyer', label: '需方' },
  438 + { key: 'orderDate', label: '订货日期' },
  439 + { key: 'workshopId', label: '生产厂' },
  440 + ]
  441 + for (const it of checks) {
  442 + const val = this.form[it.key]
  443 + const empty = (val === undefined || val === null || (typeof val === 'string' && val.trim() === '') || (typeof val === 'number' && isNaN(val)))
  444 + if (empty) { uni.showToast({ title: `请先选择${it.label}`, icon: 'none' }); return false }
  445 + }
  446 + if (!Array.isArray(this.productLineList) || this.productLineList.length === 0) {
  447 + uni.showToast({ title: '请至少添加一条产品明细', icon: 'none' }); return false
  448 + }
  449 + for (const [idx, it] of this.productLineList.entries()) {
  450 + if (!it.productName || !it.quantity || !it.unitPrice) {
  451 + uni.showToast({ title: `第${idx + 1}条明细未完整填写`, icon: 'none' }); return false
  452 + }
  453 + }
  454 + return true
  455 + }
  456 + }
  457 +}
  458 +
  459 +</script>
  460 +<style lang="scss" scoped>
  461 +.total {
  462 + .total-text {
  463 + font-weight: 600;
  464 + font-size: 32rpx;
  465 + color: rgba(0, 0, 0, 0.9);
  466 + padding-bottom: 28rpx;
  467 + border-bottom: 2rpx solid #E7E7E7;
  468 + }
  469 + .total-item {
  470 + display: flex;
  471 + align-items: center;
  472 + .total-item-text {
  473 + font-weight: 400;
  474 + font-size: 28rpx;
  475 + color: rgba(0, 0, 0, 0.6);
  476 + line-height: 32rpx;
  477 + width: 240rpx;
  478 + padding: 24rpx 0;
  479 + }
  480 + .total-item-price {
  481 + font-weight: 600;
  482 + font-size: 32rpx;
  483 + color: rgba(0, 0, 0, 0.9);
  484 + line-height: 32rpx;
  485 + }
  486 + .text-red {
  487 + color: #D54941;
  488 + }
  489 + }
  490 +
  491 +}
  492 +.page {
  493 + display: flex;
  494 + flex-direction: column;
  495 + height: 100%;
  496 +}
  497 +
  498 +.scroll {
  499 + flex: 1;
  500 + padding: 12rpx 0 480rpx !important;
  501 +}
  502 +
  503 +.footer {
  504 + z-index: 2;
  505 + position: fixed;
  506 + left: 0;
  507 + right: 0;
  508 + bottom: 0;
  509 + padding: 32rpx;
  510 + padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
  511 + background: #fff;
  512 + box-shadow: 0 -8rpx 24rpx rgba(0, 0, 0, 0.06);
  513 +
  514 + .btn {
  515 + height: 80rpx;
  516 + line-height: 80rpx;
  517 + border-radius: 12rpx;
  518 + font-size: 32rpx;
  519 + }
  520 +
  521 + .submit {
  522 + background: $theme-primary;
  523 + color: #fff;
  524 + }
  525 +}
  526 +
  527 +.group {
  528 + background: #fff;
  529 + padding: 24rpx 32rpx;
  530 + margin-bottom: 20rpx;
  531 +}
  532 +
  533 +.group-title {
  534 + color: rgba(0, 0, 0, 0.9);
  535 + font-size: 32rpx;
  536 + padding-bottom: 16rpx;
  537 +}
  538 +
  539 +.radio-list {
  540 + display: flex;
  541 + flex-direction: column;
  542 +}
  543 +
  544 +.radio-item {
  545 + display: flex;
  546 + align-items: center;
  547 + padding: 24rpx 0;
  548 + border-bottom: 1rpx solid #f0f0f0;
  549 +}
  550 +
  551 +.radio-item:last-child {
  552 + border-bottom: none;
  553 +}
  554 +
  555 +.radio {
  556 + width: 32rpx;
  557 + height: 32rpx;
  558 + border-radius: 50%;
  559 + border: 2rpx solid #DCDCDC;
  560 + margin-right: 20rpx;
  561 + box-sizing: border-box;
  562 +}
  563 +
  564 +.radio.checked {
  565 + background: $theme-primary;
  566 + border-color: $theme-primary;
  567 +}
  568 +
  569 +.radio-item .label {
  570 + font-size: 32rpx;
  571 + color: rgba(0, 0, 0, 0.9);
  572 +}
  573 +
  574 +.scroll {
  575 + flex: 1;
  576 + padding: 12rpx 0 160rpx;
  577 +}
  578 +
  579 +.quality {
  580 + background-color: #fff;
  581 + display: flex;
  582 + align-items: center;
  583 + padding: 24rpx 32rpx;
  584 + border-bottom: 1rpx solid #f0f0f0;
  585 + margin-top: 20rpx;
  586 +
  587 + .title {
  588 + font-size: 32rpx;
  589 + color: rgba(0, 0, 0, 0.9);
  590 + font-weight: 600;
  591 + }
  592 +
  593 + .opCollapse {
  594 + color: rgba(0, 0, 0, 0.6);
  595 + width: 24rpx;
  596 + height: 24rpx;
  597 + margin-right: 16rpx;
  598 + margin-top: 8rpx;
  599 + }
  600 +}
  601 +
  602 +::v-deep .uni-list {
  603 + .uni-easyinput {
  604 + display: flex;
  605 +
  606 + .uni-input-input {
  607 + color: rgba(0, 0, 0, 0.9);
  608 + }
  609 + }
  610 +
  611 + .uni-input-placeholder {
  612 + z-index: 1;
  613 + }
  614 +
  615 + .uni-input-input {
  616 + background-color: #ffffff;
  617 + }
  618 +
  619 + background: transparent;
  620 +
  621 + &-item {
  622 + &__extra-text {
  623 + font-size: 32rpx;
  624 + }
  625 +
  626 + &__content-title {
  627 + font-size: 32rpx;
  628 + color: rgba(0, 0, 0, 0.9);
  629 + }
  630 +
  631 + &__container {
  632 + padding: 32rpx;
  633 +
  634 + .uni-easyinput {
  635 + &__placeholder-class {
  636 + font-size: 32rpx;
  637 + color: rgba(0, 0, 0, 0.4);
  638 + }
  639 +
  640 + &__content {
  641 + border: none;
  642 + background-color: #ffffff !important;
  643 +
  644 + &-input {
  645 + padding-left: 0 !important;
  646 + height: 48rpx;
  647 + line-height: 48rpx;
  648 + font-size: 32rpx;
  649 + }
  650 +
  651 + .content-clear-icon {
  652 + font-size: 44rpx !important;
  653 + }
  654 + }
  655 + }
  656 +
  657 + .item-title,
  658 + .uni-list-item__content {
  659 + flex: none;
  660 + min-height: 48rpx;
  661 + line-height: 48rpx;
  662 + font-size: 32rpx;
  663 + position: relative;
  664 + width: 162rpx;
  665 + margin-right: 32rpx;
  666 + color: rgba(0, 0, 0, 0.9);
  667 +
  668 + .required {
  669 + color: red;
  670 + position: absolute;
  671 + top: 50%;
  672 + transform: translateY(-50%);
  673 + left: -16rpx;
  674 + }
  675 + }
  676 + }
  677 +
  678 + &.select-item {
  679 + &.is-empty {
  680 + .uni-list-item__extra-text {
  681 + color: rgba(0, 0, 0, 0.4) !important;
  682 + }
  683 + }
  684 +
  685 + &.is-filled {
  686 + .uni-list-item__extra-text {
  687 + color: rgba(0, 0, 0, 0.9) !important;
  688 + }
  689 + }
  690 + }
  691 +
  692 + &.mgb10 {
  693 + margin-bottom: 20rpx;
  694 + }
  695 + }
  696 +}
  697 +</style>
  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 customer">{{ detail.code }}</text>
  7 + <view class="row"><text class="label">供方</text><text class="value">{{ detail.supplierName || '-'
  8 + }}</text></view>
  9 + <view class="row"><text class="label">需方</text><text class="value">{{ detail.buyerName || '-'
  10 + }}</text></view>
  11 + <view class="row"><text class="label">订货日期</text><text class="value">{{ detail.orderDate }}</text>
  12 + </view>
  13 + <view class="row"><text class="label">单位</text><text class="value">{{ detail.unit }}</text></view>
  14 + <view class="row"><text class="label">生产厂</text><text class="value">{{ detail.workshopName || '-'
  15 + }}</text></view>
  16 + </view>
  17 +
  18 + <view class="section1">
  19 + <ProductRel mode="view" :list="productList" />
  20 + </view>
  21 +
  22 + <view class="section">
  23 + <view class="row"><text class="label">合计人民币金额(大写)</text><text class="value">{{
  24 + detail.totalAmountCapital || '-' }}</text></view>
  25 + <view class="row"><text class="label">交付定金、数额、时间</text><text class="value">{{ detail.depositInfo ||
  26 + '-' }}</text></view>
  27 + <view class="row"><text class="label">包装要求</text><text class="value">{{ detail.packagingRequirements
  28 + || '-' }}</text></view>
  29 + <view class="row"><text class="label">付款方式、付款期限</text><text class="value">{{ detail.paymentTerms ||
  30 + '-' }}</text></view>
  31 + <view class="row"><text class="label">运输方式</text><text class="value">{{ detail.transportMode || '-'
  32 + }}</text></view>
  33 + <view class="row"><text class="label">目的地</text><text class="value">{{ detail.destinationLabel || '-'
  34 + }}</text></view>
  35 + <view class="row"><text class="label">单价中是否已包含包装费</text><text class="value">{{
  36 + detail.includesPackagingFeeName || '-' }}</text></view>
  37 + <view class="row"><text class="label">单价中是否已包含运费</text><text class="value">{{
  38 + detail.includesTransportFeeName || '-' }}</text></view>
  39 + <view class="row"><text class="label">需方指定收货人</text><text class="value">{{
  40 + detail.designatedConsignee || '-' }}</text></view>
  41 +
  42 + <view class="row"><text class="label">特别条款要求</text><text class="value">{{ detail.specialTermsName ||
  43 + '-' }}</text></view>
  44 + <view class="row"><text class="label">执行标准</text><text class="value">{{ detail.executionStandardName
  45 + || '-' }}</text></view>
  46 + <view class="row" v-if="detail.executionStandard === 'OTHER'"><text class="label">其他</text><text
  47 + class="value">{{ detail.executionStandardRemarks || '-' }}</text></view>
  48 + <view class="row"><text class="label">特别说明</text><text class="value">{{ detail.specialInstructions
  49 + || '-' }}</text></view>
  50 + <view class="row"><text class="label">备注</text><text class="value">{{ detail.remarks || '-'
  51 + }}</text></view>
  52 + </view>
  53 +
  54 + <view class="section">
  55 + <view class="row"><text class="label">规范性合同</text><text class="value">{{ detail.standardFileName || '-'
  56 + }}</text></view>
  57 + <view class="row"><text class="label">合同是否规范</text><text class="value">{{ detail.standardStandardized ? '是' : '否'
  58 + }}</text></view>
  59 + </view>
  60 +
  61 + <view class="section">
  62 + <text class="row customer">具体质量要求</text>
  63 + <view class="row"><text class="label">件重条头</text><text class="value">{{ detail.pieceWeightHead ||
  64 + '-' }}</text></view>
  65 + <view class="row"><text class="label">表面</text><text class="value">{{ detail.surface || '-'
  66 + }}</text></view>
  67 + <view class="row"><text class="label">公差</text><text class="value">{{ detail.tolerance || '-'
  68 + }}</text></view>
  69 + <view class="row"><text class="label">性能</text><text class="value">{{ detail.performance || '-'
  70 + }}</text></view>
  71 + <view class="row"><text class="label">成分</text><text class="value">{{ detail.component || '-'
  72 + }}</text></view>
  73 + <view class="row"><text class="label">包装</text><text class="value">{{ detail.packaging || '-'
  74 + }}</text></view>
  75 + </view>
  76 + </view>
  77 + </scroll-view>
  78 + <detail-buttons :buttons="displayButtons" @click="handleButtonClick" />
  79 + </view>
  80 +</template>
  81 +
  82 +<script>
  83 +import { getContractApi, deleteContractApi } from '@/api/contract'
  84 +import ProductRel from './productRel.vue'
  85 +import DetailButtons from '@/components/detail-buttons/index.vue'
  86 +
  87 +export default {
  88 + name: 'ContractForeignStockDetail',
  89 + components: { ProductRel, DetailButtons },
  90 + data() {
  91 + return {
  92 + id: '',
  93 + detail: {
  94 + code: '',
  95 + supplier: '',
  96 + supplierName: '',
  97 + buyer: '',
  98 + buyerName: '',
  99 + orderDate: '',
  100 + unit: '',
  101 + workshopId: '',
  102 + workshopName: '',
  103 + designatedConsignee: '',
  104 + specialTerms: '',
  105 + specialTermsName: '',
  106 + executionStandard: '',
  107 + executionStandardName: '',
  108 + executionStandardRemarks: '',
  109 + includesPackagingFee: false,
  110 + includesPackagingFeeName: '',
  111 + includesTransportFee: false,
  112 + includesTransportFeeName: '',
  113 + totalAmountCapital: '',
  114 + depositInfo: '',
  115 + packagingRequirements: '',
  116 + paymentTerms: '',
  117 + transportMode: '',
  118 + destinationId: '',
  119 + destinationLabel: '',
  120 + specialInstructions: '',
  121 + remarks: '',
  122 + pieceWeightHead: '',
  123 + surface: '',
  124 + tolerance: '',
  125 + performance: '',
  126 + component: '',
  127 + packaging: ''
  128 + },
  129 + productList: [],
  130 + buttons: [{
  131 + text: '编辑',
  132 + visible: true,
  133 + variant: 'outline',
  134 + event: 'edit',
  135 + },
  136 + {
  137 + text: '删除',
  138 + visible: true,
  139 + variant: 'outline',
  140 + event: 'delete',
  141 + style: {
  142 + color: '#D54941',
  143 + border: '1px solid #D54941'
  144 + }
  145 + },
  146 + {
  147 + text: '上传合同附件',
  148 + visible: true,
  149 + variant: 'outline',
  150 + event: 'upload'
  151 + },
  152 + {
  153 + text: '审核',
  154 + visible: true,
  155 + variant: 'primary',
  156 + event: 'audit'
  157 + },
  158 + {
  159 + text: '审核详情',
  160 + visible: true,
  161 + variant: 'primary',
  162 + event: 'auditDetail'
  163 + },
  164 + ],
  165 + }
  166 + },
  167 + computed: {
  168 + displayButtons() {
  169 + const s = this.detail && this.detail.status || ''
  170 + const t = this.detail.standardApproved || ''
  171 + return [
  172 + { ...this.buttons[0]},
  173 + // { ...this.buttons[0], visible: (s === 'DRAFT') },
  174 + { ...this.buttons[1], visible: (s === 'DRAFT') },
  175 + { ...this.buttons[2], visible: (s !== 'DELETED' && t !== 'AUDIT' && t !== 'PASS') },
  176 + { ...this.buttons[3], visible: (s === 'STANDARD' && t === 'AUDIT') },
  177 + { ...this.buttons[4], visible: (s === 'STANDARD') }
  178 + ]
  179 + }
  180 + },
  181 + onLoad(options) {
  182 + const id = options && options.id ? options.id : ''
  183 + this.id = id
  184 + this.loadDetail()
  185 + },
  186 + methods: {
  187 + onDelete() {
  188 + uni.showModal({
  189 + title: '确认删除',
  190 + content: '确认删除该合同吗?',
  191 + success: (res) => {
  192 + if (res.confirm) {
  193 + deleteContractApi(this.id).then(() => {
  194 + uni.showToast({
  195 + title: '删除成功',
  196 + icon: 'success'
  197 + })
  198 + setTimeout(() => {
  199 + uni.navigateTo({
  200 + url: '/pages/contract_foreign_stock/index'
  201 + })
  202 + }, 500)
  203 + }).catch(() => {
  204 + uni.showToast({
  205 + title: '删除失败',
  206 + icon: 'error'
  207 + })
  208 + })
  209 + }
  210 + }
  211 + })
  212 + },
  213 + onEdit() {
  214 + const id = this.detail.id || this.detail.code || ''
  215 + const query = id ? ('?id=' + encodeURIComponent(id)) : ''
  216 + uni.navigateTo({
  217 + url: '/pages/contract_foreign_stock/modify' + query
  218 + })
  219 + },
  220 + handleButtonClick(btn) {
  221 + if (!btn || btn.disabled) return
  222 + if (typeof btn.onClick === 'function') return btn.onClick(this.detail, btn.params)
  223 + const e = btn.event || ''
  224 + if (e === 'edit') return this.onEdit(btn && btn.params)
  225 + if (e === 'delete') return this.onDelete(btn && btn.params)
  226 + // if (e === 'upload') return this.onUpload(btn && btn.params)
  227 + // if (e === 'audit') return this.onAudit(btn && btn.params)
  228 + // if (e === 'auditDetail') return this.onAuditDetail(btn && btn.params)
  229 + },
  230 + async loadDetail() {
  231 + if (!this.id) return
  232 + try {
  233 + const res = await getContractApi(this.id)
  234 + const data = res && res.data ? res.data : {}
  235 + const includesPackagingFeeName = data.includesPackagingFeeName || (data.includesPackagingFee ? '是' : '否')
  236 + const includesTransportFeeName = data.includesTransportFeeName || (data.includesTransportFee ? '是' : '否')
  237 + this.detail = {
  238 + ...this.detail,
  239 + ...data,
  240 + includesPackagingFeeName, includesTransportFeeName,
  241 + destinationId: data.provinceId && data.cityId && data.districtId ? [data.provinceId, data.cityId, data.districtId] : '',
  242 + destinationLabel: data.provinceName && data.cityName && data.districtName ? `${data.provinceName} / ${data.cityName} / ${data.districtName}` : '',
  243 + }
  244 + const lines = Array.isArray(data.contractDistributorLineList) ? data.contractDistributorLineList : []
  245 + this.productList = lines
  246 + } catch (e) {
  247 + this.detail = { ...this.detail }
  248 + this.productList = []
  249 + }
  250 + }
  251 + }
  252 +}
  253 +</script>
  254 +
  255 +<style lang="scss" scoped>
  256 +.page {
  257 + display: flex;
  258 + flex-direction: column;
  259 + height: 100%;
  260 +}
  261 +
  262 +.scroll {
  263 + flex: 1;
  264 + padding: 8rpx 0 144rpx;
  265 +}
  266 +
  267 +.detail-page {
  268 + background: #f3f3f3;
  269 +}
  270 +
  271 +.section {
  272 + padding: 32rpx;
  273 + background: #fff;
  274 + margin-bottom: 20rpx;
  275 +}
  276 +.section1 {
  277 + background: #fff;
  278 + margin-bottom: 20rpx;
  279 +}
  280 +
  281 +.row {
  282 + display: flex;
  283 + margin-bottom: 20rpx;
  284 + align-items: center;
  285 +}
  286 +
  287 +.row:last-child {
  288 + margin-bottom: 0;
  289 +}
  290 +
  291 +.label {
  292 + width: 280rpx;
  293 + color: rgba(0, 0, 0, 0.6);
  294 + font-size: 28rpx;
  295 +}
  296 +
  297 +.value {
  298 + flex: 1;
  299 + text-align: right;
  300 + color: rgba(0, 0, 0, 0.9);
  301 + font-size: 28rpx;
  302 +}
  303 +
  304 +.customer {
  305 + font-weight: 600;
  306 + font-size: 36rpx;
  307 + color: rgba(0, 0, 0, 0.9);
  308 + padding-bottom: 12rpx;
  309 +}
  310 +</style>
@@ -46,7 +46,7 @@ @@ -46,7 +46,7 @@
46 @error="onCardError" 46 @error="onCardError"
47 > 47 >
48 <template v-slot="{ item }"> 48 <template v-slot="{ item }">
49 - <view class="card"> 49 + <view class="card" @click="goDetail(item)">
50 <view class="card-header"> 50 <view class="card-header">
51 <text class="title omit2">{{ item.buyerName }}</text> 51 <text class="title omit2">{{ item.buyerName }}</text>
52 <text v-if="item.status === 'STANDARD'" :class="['status']" :style="{ background: statusMap[item.shippingStatusName] }">{{ item.shippingStatusName }}</text> 52 <text v-if="item.status === 'STANDARD'" :class="['status']" :style="{ background: statusMap[item.shippingStatusName] }">{{ item.shippingStatusName }}</text>
@@ -158,6 +158,9 @@ export default { @@ -158,6 +158,9 @@ export default {
158 } 158 }
159 }, 159 },
160 methods: { 160 methods: {
  161 + goDetail(item) {
  162 + uni.navigateTo({ url: '/pages/contract_foreign_stock/detail?id=' + item.id })
  163 + },
161 onCardLoaded({ items }) { 164 onCardLoaded({ items }) {
162 this.currentItems = items 165 this.currentItems = items
163 }, 166 },
@@ -202,7 +205,7 @@ export default { @@ -202,7 +205,7 @@ export default {
202 if (!this.batchMode) this.selectedKeys = [] 205 if (!this.batchMode) this.selectedKeys = []
203 }, 206 },
204 onAdd() { 207 onAdd() {
205 - uni.showToast({ title: '点击新增', icon: 'none' }) 208 + uni.navigateTo({ url: '/pages/contract_foreign_stock/add' })
206 }, 209 },
207 fetchList({ pageIndex, pageSize, query, extra }) { 210 fetchList({ pageIndex, pageSize, query, extra }) {
208 const params = { pageIndex, pageSize, ...extra, ...query } 211 const params = { pageIndex, pageSize, ...extra, ...query }
  1 +<template>
  2 + <view class="page">
  3 + <scroll-view class="scroll" scroll-y>
  4 + <uni-list>
  5 + <uni-list-item title="编号">
  6 + <template v-slot:footer>
  7 + <uni-easyinput v-model="form.code" :inputBorder="false" disabled />
  8 + </template>
  9 + </uni-list-item>
  10 +
  11 + <uni-list-item class="select-item" :class="form.supplier ? 'is-filled' : 'is-empty'" clickable
  12 + @click="openSheet('supplier')" :rightText="displayLabel('supplierName')" showArrow>
  13 + <template v-slot:body>
  14 + <view class="item-title"><text class="required">*</text><text>供方</text></view>
  15 + </template>
  16 + </uni-list-item>
  17 +
  18 + <uni-list-item class="select-item" :class="form.buyer ? 'is-filled' : 'is-empty'" clickable
  19 + @click="openRelate('buyer')" :rightText="form.buyerName || '请选择需方'" showArrow>
  20 + <template v-slot:body>
  21 + <view class="item-title"><text class="required">*</text><text>需方</text></view>
  22 + </template>
  23 + </uni-list-item>
  24 +
  25 + <uni-list-item class="select-item" :class="form.workshopId ? 'is-filled' : 'is-empty'" clickable
  26 + @click="openSheet('workshopId')" :rightText="form.workshopName || '请选择生产厂'" showArrow>
  27 + <template v-slot:body>
  28 + <view class="item-title"><text class="required">*</text><text>生产厂</text></view>
  29 + </template>
  30 + </uni-list-item>
  31 +
  32 + <uni-list-item title="订货日期">
  33 + <template v-slot:footer>
  34 + <uni-datetime-picker type="date" v-model="form.orderDate" />
  35 + </template>
  36 + </uni-list-item>
  37 +
  38 + <uni-list-item title="单位">
  39 + <template v-slot:footer>
  40 + <uni-easyinput v-model="form.unit" :inputBorder="false" disabled />
  41 + </template>
  42 + </uni-list-item>
  43 +
  44 + <ProductRel mode="add" :orderDateBase="form.orderDate" :list="productLineList" @change="onProductsChange" :options="productList" />
  45 +
  46 + <uni-list-item title="合计人民币金额(大写)">
  47 + <template v-slot:footer>
  48 + <uni-easyinput v-model="form.totalAmountCapital" placeholder="自动计算" :inputBorder="false"
  49 + disabled />
  50 + </template>
  51 + </uni-list-item>
  52 + <uni-list-item title="交付定金、数额、时间">
  53 + <template v-slot:footer>
  54 + <uni-easyinput v-model="form.depositInfo" placeholder="请输入交付定金、数额、时间" :inputBorder="false" />
  55 + </template>
  56 + </uni-list-item>
  57 + <uni-list-item title="包装要求">
  58 + <template v-slot:footer>
  59 + <uni-easyinput v-model="form.packagingRequirements" placeholder="请输入包装要求"
  60 + :inputBorder="false" />
  61 + </template>
  62 + </uni-list-item>
  63 + <uni-list-item title="付款方式、付款期限">
  64 + <template v-slot:footer>
  65 + <uni-easyinput v-model="form.paymentTerms" placeholder="请输入付款方式、付款期限" :inputBorder="false" />
  66 + </template>
  67 + </uni-list-item>
  68 + <uni-list-item title="运输方式">
  69 + <template v-slot:footer>
  70 + <uni-easyinput v-model="form.transportMode" placeholder="请输入运输方式" :inputBorder="false" />
  71 + </template>
  72 + </uni-list-item>
  73 + <uni-list-item class="select-item" :class="(Array.isArray(form.destinationId) && form.destinationId.length) ? 'is-filled' : 'is-empty'" clickable
  74 + @click="openCitySelector" :rightText="form.destinationLabel || '请选择'" showArrow>
  75 + <template v-slot:body>
  76 + <view class="item-title"><text>目的地</text></view>
  77 + <CitySelector ref="citySelectorRef" v-model="form.destinationId" @change="onCityChange" />
  78 + </template>
  79 + </uni-list-item>
  80 +
  81 + <uni-list-item class="select-item" :class="form.includesPackagingFeeName ? 'is-filled' : 'is-empty'"
  82 + clickable @click="openSheet('includesPackagingFee')"
  83 + :rightText="form.includesPackagingFeeName || '请选择'" showArrow>
  84 + <template v-slot:body>
  85 + <view class="item-title"><text>单价中是否已包含包装费</text></view>
  86 + </template>
  87 + </uni-list-item>
  88 + <uni-list-item class="select-item" :class="form.includesTransportFeeName ? 'is-filled' : 'is-empty'"
  89 + clickable @click="openSheet('includesTransportFee')"
  90 + :rightText="form.includesTransportFeeName || '请选择'" showArrow>
  91 + <template v-slot:body>
  92 + <view class="item-title"><text>单价中是否已包含运费</text></view>
  93 + </template>
  94 + </uni-list-item>
  95 + <uni-list-item title="需方指定收货人">
  96 + <template v-slot:footer>
  97 + <uni-easyinput v-model="form.designatedConsignee" placeholder="请输入需方指定收货人"
  98 + :inputBorder="false" />
  99 + </template>
  100 + </uni-list-item>
  101 +
  102 + <view class="group">
  103 + <view class="group-title">特别条款要求</view>
  104 + <view class="radio-list">
  105 + <view v-for="(opt, i) in specialTermsList" :key="'cr-' + i" class="radio-item"
  106 + @click="onRadioSelect('specialTerms', 'specialTermsName', opt)">
  107 + <view :class="['radio', { checked: form.specialTerms === opt.value }]" />
  108 + <text class="label">{{ opt.label }}</text>
  109 + </view>
  110 + </view>
  111 + </view>
  112 + <view class="group">
  113 + <view class="group-title">执行标准</view>
  114 + <view class="radio-list">
  115 + <view v-for="(opt, i) in executionStandardList" :key="'es-' + i" class="radio-item"
  116 + @click="onRadioSelect('executionStandard', 'executionStandardName', opt)">
  117 + <view :class="['radio', { checked: form.executionStandard === opt.value }]" />
  118 + <text class="label">{{ opt.label }}</text>
  119 + </view>
  120 + </view>
  121 + </view>
  122 + <uni-list-item v-if="form.executionStandard === 'OTHER'" title="其他">
  123 + <template v-slot:footer>
  124 + <uni-easyinput v-model="form.executionStandardRemarks" placeholder="请输入其他标准备注"
  125 + :inputBorder="false" />
  126 + </template>
  127 + </uni-list-item>
  128 + <uni-list-item title="特别说明">
  129 + <template v-slot:footer>
  130 + <uni-easyinput v-model="form.specialInstructions" placeholder="请输入特别说明" :inputBorder="false" />
  131 + </template>
  132 + </uni-list-item>
  133 + <uni-list-item title="备注">
  134 + <template v-slot:footer>
  135 + <uni-easyinput v-model="form.remarks" placeholder="请输入备注" :inputBorder="false" />
  136 + </template>
  137 + </uni-list-item>
  138 +
  139 + <view class="quality">
  140 + <image class="opCollapse" src="/static/images/title.png" />
  141 + <text class="title">具体质量要求</text>
  142 + </view>
  143 + <uni-list-item title="件重条头">
  144 + <template v-slot:footer>
  145 + <uni-easyinput v-model="form.pieceWeightHead" placeholder="请输入" :inputBorder="false" />
  146 + </template>
  147 + </uni-list-item>
  148 + <uni-list-item title="表面">
  149 + <template v-slot:footer>
  150 + <uni-easyinput v-model="form.surface" placeholder="请输入" :inputBorder="false" />
  151 + </template>
  152 + </uni-list-item>
  153 + <uni-list-item title="公差">
  154 + <template v-slot:footer>
  155 + <uni-easyinput v-model="form.tolerance" placeholder="请输入" :inputBorder="false" />
  156 + </template>
  157 + </uni-list-item>
  158 + <uni-list-item title="性能">
  159 + <template v-slot:footer>
  160 + <uni-easyinput v-model="form.performance" placeholder="请输入" :inputBorder="false" />
  161 + </template>
  162 + </uni-list-item>
  163 + <uni-list-item title="成分">
  164 + <template v-slot:footer>
  165 + <uni-easyinput v-model="form.component" placeholder="请输入" :inputBorder="false" />
  166 + </template>
  167 + </uni-list-item>
  168 + <uni-list-item title="包装">
  169 + <template v-slot:footer>
  170 + <uni-easyinput v-model="form.packaging" placeholder="请输入" :inputBorder="false" />
  171 + </template>
  172 + </uni-list-item>
  173 + </uni-list>
  174 + </scroll-view>
  175 + <view class="footer">
  176 + <div class="total">
  177 + <div class="total-text">合计</div>
  178 + <div class="total-item">
  179 + <div class="total-item-text">数量</div>
  180 + <div class="total-item-price">{{ (sumQuantity || 0).toFixed(2) }}t</div>
  181 + </div>
  182 + <div class="total-item">
  183 + <div class="total-item-text">不含税金额</div>
  184 + <div class="total-item-price text-red">¥{{ (sumAmountExcl || 0).toFixed(2) }}</div>
  185 + </div>
  186 + <div class="total-item">
  187 + <div class="total-item-text">总金额</div>
  188 + <div class="total-item-price text-red">¥{{ (sumTotal || 0).toFixed(2) }}</div>
  189 + </div>
  190 + </div>
  191 + <button class="btn submit" type="primary" @click="onSubmit">保存</button>
  192 + </view>
  193 + <SingleSelectSheet :visible.sync="sheet.visible" :title="sheet.title" :options="sheet.options"
  194 + v-model="sheet.value" @confirm="onSheetConfirm" />
  195 + <RelateSelectSheet :visible.sync="relate.visible" :title="relate.title" :source="relate.source"
  196 + :display-fields="relate.display" :multiple="relate.multiple" :row-key="relate.rowKey"
  197 + :selectedKeys.sync="relate.selectedKeys" @confirm="onRelateConfirm" />
  198 + </view>
  199 +</template>
  200 +
  201 +<script>
  202 +import SingleSelectSheet from '@/components/single-select/index.vue'
  203 +import RelateSelectSheet from '@/components/relate-select/index.vue'
  204 +import ProductRel from './productRel.vue'
  205 +import CitySelector from '@/components/city-selector/index.vue'
  206 +import { getContractApi, updateContractApi } from '@/api/contract'
  207 +import { getDicByCodes } from '@/utils/dic'
  208 +import { formatCurrencyToChinese } from '@/utils/common'
  209 +import { workshopQueryApi } from '@/api/devManage'
  210 +export default {
  211 + name: 'ModifyContractForeignStock',
  212 + components: { SingleSelectSheet, RelateSelectSheet, ProductRel, CitySelector },
  213 + data() {
  214 + return {
  215 + id: '',
  216 + form: {
  217 + id: '',
  218 + code: '',
  219 + supplier: '',
  220 + supplierName: '',
  221 + buyer: '',
  222 + buyerName: '',
  223 + workshopId: '',
  224 + workshopName: '',
  225 + orderDate: '',
  226 + designatedConsignee: '',
  227 + specialTerms: '',
  228 + specialTermsName: '',
  229 + executionStandard: '',
  230 + executionStandardName: '',
  231 + executionStandardRemarks: '',
  232 + includesPackagingFee: false,
  233 + includesPackagingFeeName: '',
  234 + includesTransportFee: false,
  235 + includesTransportFeeName: '',
  236 + unit: '元、公斤、元/公斤',
  237 + totalAmountCapital: '',
  238 + depositInfo: '',
  239 + packagingRequirements: '',
  240 + paymentTerms: '',
  241 + transportMode: '',
  242 + destinationId: [],
  243 + destinationLabel: '',
  244 + specialInstructions: '',
  245 + remarks: '',
  246 + pieceWeightHead: '',
  247 + surface: '',
  248 + tolerance: '',
  249 + performance: '',
  250 + component: '',
  251 + packaging: ''
  252 + },
  253 + supplierList: [],
  254 + specialTermsList: [],
  255 + executionStandardList: [],
  256 + yesNoList: [{ label: '是', value: true }, { label: '否', value: false }],
  257 + sheet: { visible: false, title: '请选择', field: '', options: [], value: '' },
  258 + relate: { visible: false, title: '选择', source: '', display: [], multiple: false, rowKey: 'id', selectedKeys: [], fieldKey: '' },
  259 + sumQuantity: 0,
  260 + sumAmountExcl: 0,
  261 + sumTotal: 0,
  262 + productLineList: [],
  263 + newProductLineList: [],
  264 + productList: []
  265 + }
  266 + },
  267 + onLoad(query) {
  268 + this.id = (query && query.id) ? query.id : ''
  269 + },
  270 + created() {
  271 + this.loadSuppliers()
  272 + this.loadExtraOptions()
  273 + this.loadDetail()
  274 + this.$nextTick(() => {
  275 + if (Array.isArray(this.form.destinationId) && this.form.destinationId.length) {
  276 + this.initDestinationLabel()
  277 + }
  278 + })
  279 + },
  280 + methods: {
  281 + async loadDetail() {
  282 + if (!this.id) return
  283 + try {
  284 + const res = await getContractApi(this.id)
  285 + const data = res && res.data ? res.data : {}
  286 + const includesPackagingFeeName = data.includesPackagingFeeName || (data.includesPackagingFee ? '是' : '否')
  287 + const includesTransportFeeName = data.includesTransportFeeName || (data.includesTransportFee ? '是' : '否')
  288 + const m = { ...data, includesPackagingFeeName, includesTransportFeeName }
  289 + this.form = {
  290 + ...this.form,
  291 + id: m.id || '',
  292 + code: m.code || '',
  293 + supplier: m.supplier || '',
  294 + supplierName: m.supplierName || '',
  295 + buyer: m.buyer || (m.customer && m.customer.id) || '',
  296 + buyerName: m.buyerName || (m.customer && m.customer.name) || '',
  297 + orderDate: m.orderDate || '',
  298 + designatedConsignee: m.designatedConsignee || '',
  299 + specialTerms: m.specialTerms || '',
  300 + specialTermsName: m.specialTermsName || '',
  301 + executionStandard: m.executionStandard || '',
  302 + executionStandardName: m.executionStandardName || '',
  303 + executionStandardRemarks: m.executionStandardRemarks || '',
  304 + includesPackagingFee: !!m.includesPackagingFee,
  305 + includesPackagingFeeName,
  306 + includesTransportFee: !!m.includesTransportFee,
  307 + includesTransportFeeName,
  308 + unit: m.unit || this.form.unit,
  309 + totalAmountCapital: m.totalAmountCapital || '',
  310 + depositInfo: m.depositInfo || '',
  311 + packagingRequirements: m.packagingRequirements || '',
  312 + paymentTerms: m.paymentTerms || '',
  313 + transportMode: m.transportMode || '',
  314 + destinationId: (m.provinceId && m.cityId && m.districtId) ? [m.provinceId, m.cityId, m.districtId] : (Array.isArray(m.destinationId) ? m.destinationId : []),
  315 + destinationLabel: (m.provinceName && m.cityName && m.districtName) ? `${m.provinceName} / ${m.cityName} / ${m.districtName}` : (m.destinationLabel || ''),
  316 + specialInstructions: m.specialInstructions || '',
  317 + remarks: m.remarks || '',
  318 + pieceWeightHead: m.pieceWeightHead || '',
  319 + surface: m.surface || '',
  320 + tolerance: m.tolerance || '',
  321 + performance: m.performance || '',
  322 + component: m.component || '',
  323 + packaging: m.packaging || '',
  324 + workshopId: m.workshopId || '',
  325 + workshopName: m.workshopName || '',
  326 + }
  327 + const lines = Array.isArray(m.contractDistributorLineList) ? m.contractDistributorLineList : []
  328 + this.productLineList = lines
  329 + this.onProductsChange(lines)
  330 + } catch (e) { }
  331 + },
  332 + async initDestinationLabel() {
  333 + const comp = this.$refs.citySelectorRef
  334 + if (comp && typeof comp.getLabel === 'function') {
  335 + const label = await comp.getLabel()
  336 + this.form.destinationLabel = label || ''
  337 + }
  338 + },
  339 + openCitySelector() {
  340 + this.$refs.citySelectorRef && this.$refs.citySelectorRef.open()
  341 + },
  342 + onCityChange(payload) {
  343 + const label = payload && payload.label != null ? payload.label : ''
  344 + this.form.destinationLabel = label
  345 + },
  346 + onProductsChange(products) {
  347 + const list = Array.isArray(products) ? products : []
  348 + this.newProductLineList = list
  349 + const sumQ = list.reduce((acc, it) => acc + (parseFloat(it.quantity) || 0), 0)
  350 + const sumE = list.reduce((acc, it) => acc + (parseFloat(it.amountExcludingTax) || 0), 0)
  351 + const sumT = list.reduce((acc, it) => acc + (parseFloat(it.totalAmount) || 0), 0)
  352 + this.sumQuantity = sumQ
  353 + this.sumAmountExcl = sumE
  354 + this.sumTotal = sumT
  355 + this.form.totalAmountCapital = formatCurrencyToChinese(sumT)
  356 + },
  357 + async loadSuppliers() {
  358 + try {
  359 + const results = await getDicByCodes(['SUPPLIER'])
  360 + const items = results && results.SUPPLIER && results.SUPPLIER.data ? results.SUPPLIER.data : []
  361 + this.supplierList = items.map(it => ({ label: it.name, value: it.code }))
  362 + } catch (e) { this.supplierList = [] }
  363 + },
  364 + async loadExtraOptions() {
  365 + try {
  366 + const results = await getDicByCodes(['CONDITIONS_REQUIRED', 'APPLICABLE_STANDARD', 'CONTRACT_PRODUCT'])
  367 + const c1 = results && results.CONDITIONS_REQUIRED && results.CONDITIONS_REQUIRED.data ? results.CONDITIONS_REQUIRED.data : []
  368 + const c2 = results && results.APPLICABLE_STANDARD && results.APPLICABLE_STANDARD.data ? results.APPLICABLE_STANDARD.data : []
  369 + const c3 = results && results.CONTRACT_PRODUCT && results.CONTRACT_PRODUCT.data ? results.CONTRACT_PRODUCT.data : []
  370 + this.specialTermsList = c1.map(it => ({ label: it.name, value: it.code }))
  371 + this.executionStandardList = c2.map(it => ({ label: it.name, value: it.code }))
  372 + this.productList = c3.map(it => ({ label: it.name, value: it.code }))
  373 + } catch (e) {
  374 + this.specialTermsList = []
  375 + this.executionStandardList = []
  376 + this.productList = []
  377 + }
  378 + },
  379 + displayLabel(field) {
  380 + const m = this.form
  381 + const map = { supplierName: '请选择供方', buyerName: '请选择需方', workshopName: '请选择生产厂' }
  382 + const val = m[field]
  383 + return val ? String(val) : map[field]
  384 + },
  385 + async openSheet(field) {
  386 + const setSheet = (title, options) => {
  387 + const current = this.form[field]
  388 + const match = (options || []).find(o => String(o.label) === String(current) || String(o.value) === String(current))
  389 + this.sheet = { ...this.sheet, visible: true, title, options, field, value: match ? match.value : '' }
  390 + }
  391 + if (field === 'workshopId') {
  392 + const res = await workshopQueryApi({ pageIndex: 1, pageSize: 9999 })
  393 + const _data = res.data || {}
  394 + const list = _data.datas || (Array.isArray(_data) ? _data : [])
  395 + const opts = (list || []).map(it => ({
  396 + label: it.name ,
  397 + value: it.id
  398 + }))
  399 + setSheet('生产厂', opts)
  400 + } else if (field === 'supplier') {
  401 + setSheet('供方', this.supplierList)
  402 + } else if (field === 'includesPackagingFee') {
  403 + setSheet('单价中是否已包含包装费', this.yesNoList)
  404 + } else if (field === 'includesTransportFee') {
  405 + setSheet('单价中是否已包含运费', this.yesNoList)
  406 + }
  407 + },
  408 + onSheetConfirm({ value, label }) {
  409 + const field = this.sheet.field
  410 + if (!field) return
  411 + const v = (value === undefined || value === null) ? '' : value
  412 + this.form[field] = v
  413 + this.form[field + 'Name'] = label || ''
  414 + },
  415 + openRelate(fieldKey) {
  416 + let config = {}
  417 + if (fieldKey === 'buyer') {
  418 + config = { title: '需方', source: 'customer', rowKey: 'id', multiple: false, display: [{ label: '姓名', field: 'name' }, { label: '编号', field: 'code' }, { label: '状态', field: 'available', format: v => (v ? '启用' : '停用') }] }
  419 + }
  420 + const selectedKeys = this.form[fieldKey] ? [this.form[fieldKey]] : []
  421 + this.sheet.visible = false
  422 + this.relate = { ...this.relate, title: config.title, source: config.source, display: config.display, multiple: config.multiple, rowKey: config.rowKey, selectedKeys, fieldKey }
  423 + this.$nextTick(() => { this.relate.visible = true })
  424 + },
  425 + onRelateConfirm({ items }) {
  426 + const _fieldKey = this.relate.fieldKey
  427 + const first = (items && items.length > 0) ? items[0] : null
  428 + this.form[_fieldKey] = (first && first.id) ? first.id : ''
  429 + this.form[_fieldKey + 'Name'] = (first && first.name) ? first.name : ''
  430 + },
  431 + onRadioSelect(field, nameField, opt) {
  432 + const val = opt && opt.value != null ? opt.value : ''
  433 + const label = opt && opt.label != null ? opt.label : ''
  434 + this.form[field] = val
  435 + this.form[nameField] = label
  436 + if (field === 'executionStandard' && val !== 'OTHER') {
  437 + this.form.executionStandardRemarks = ''
  438 + }
  439 + },
  440 + validateRequired() {
  441 + const checks = [
  442 + { key: 'code', label: '编号' },
  443 + { key: 'supplier', label: '供方' },
  444 + { key: 'buyer', label: '需方' },
  445 + { key: 'orderDate', label: '订货日期' },
  446 + { key: 'workshopId', label: '生产厂' },
  447 + ]
  448 + for (const it of checks) {
  449 + const val = this.form[it.key]
  450 + const empty = (val === undefined || val === null || (typeof val === 'string' && val.trim() === '') || (typeof val === 'number' && isNaN(val)))
  451 + if (empty) { uni.showToast({ title: `请先选择${it.label}`, icon: 'none' }); return false }
  452 + }
  453 + if (!Array.isArray(this.productLineList) || this.productLineList.length === 0) {
  454 + uni.showToast({ title: '请至少添加一条产品明细', icon: 'none' }); return false
  455 + }
  456 + for (const [idx, it] of this.productLineList.entries()) {
  457 + if (!it.productName || !it.quantity || !it.unitPrice) {
  458 + uni.showToast({ title: `第${idx + 1}条明细未完整填写`, icon: 'none' }); return false
  459 + }
  460 + }
  461 + return true
  462 + },
  463 + async onSubmit() {
  464 + console.log('onSubmit__payload', payload)
  465 + if (!this.validateRequired()) return
  466 + const confirmRes = await new Promise(resolve => {
  467 + uni.showModal({ title: '提示', content: '确定保存当前经销未锁规合同吗?', confirmText: '确定', cancelText: '取消', success: resolve })
  468 + })
  469 + if (!(confirmRes && confirmRes.confirm)) return
  470 + const clean = (obj) => {
  471 + const out = {}
  472 + Object.keys(obj || {}).forEach(k => {
  473 + const v = obj[k]
  474 + const isEmptyString = typeof v === 'string' && v.trim() === ''
  475 + const isUndef = v === undefined || v === null
  476 + const isNaNNumber = typeof v === 'number' && isNaN(v)
  477 + if (!(isEmptyString || isUndef || isNaNNumber)) out[k] = v
  478 + })
  479 + return out
  480 + }
  481 + const lines = (this.newProductLineList || []).map(it => clean(it))
  482 + const { destinationLabel, destinationId, ...formForSubmit } = this.form;
  483 + const destination = destinationId && destinationId.length > 0 ? destinationId[destinationId.length - 1] : '';
  484 + const payload = clean({
  485 + ...formForSubmit,
  486 + id: this.form.id,
  487 + destination,
  488 + type: 'INTL_INVENTORY_AGMT',
  489 + sumQuantity: this.sumQuantity,
  490 + sumAmountExcl: this.sumAmountExcl,
  491 + sumTotal: this.sumTotal,
  492 + contractDistributorLineList: lines
  493 + })
  494 + try {
  495 + await updateContractApi(payload)
  496 + uni.showToast({ title: '保存成功', icon: 'none' })
  497 + setTimeout(() => { uni.redirectTo({ url: '/pages/contract_foreign_stock/index' }) }, 400)
  498 + } catch (e) {
  499 + uni.showToast({ title: '提交失败', icon: 'none' })
  500 + }
  501 + }
  502 + }
  503 +}
  504 +</script>
  505 +
  506 +<style lang="scss" scoped>
  507 +.total {
  508 + .total-text {
  509 + font-weight: 600;
  510 + font-size: 32rpx;
  511 + color: rgba(0, 0, 0, 0.9);
  512 + padding-bottom: 28rpx;
  513 + border-bottom: 2rpx solid #E7E7E7;
  514 + }
  515 +
  516 + .total-item {
  517 + display: flex;
  518 + align-items: center;
  519 + }
  520 +
  521 + .total-item-text {
  522 + font-weight: 400;
  523 + font-size: 28rpx;
  524 + color: rgba(0, 0, 0, 0.6);
  525 + line-height: 32rpx;
  526 + width: 240rpx;
  527 + padding: 24rpx 0;
  528 + }
  529 +
  530 + .total-item-price {
  531 + font-weight: 600;
  532 + font-size: 32rpx;
  533 + color: rgba(0, 0, 0, 0.9);
  534 + line-height: 32rpx;
  535 + }
  536 +
  537 + .text-red {
  538 + color: #D54941;
  539 + }
  540 +}
  541 +
  542 +.page {
  543 + display: flex;
  544 + flex-direction: column;
  545 + height: 100%;
  546 +}
  547 +
  548 +.scroll {
  549 + flex: 1;
  550 + padding: 12rpx 0 480rpx !important;
  551 +}
  552 +
  553 +.footer {
  554 + z-index: 2;
  555 + position: fixed;
  556 + left: 0;
  557 + right: 0;
  558 + bottom: 0;
  559 + padding: 32rpx;
  560 + padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
  561 + background: #fff;
  562 + box-shadow: 0 -8rpx 24rpx rgba(0, 0, 0, 0.06);
  563 +}
  564 +
  565 +.footer .btn {
  566 + height: 80rpx;
  567 + line-height: 80rpx;
  568 + border-radius: 12rpx;
  569 + font-size: 32rpx;
  570 +}
  571 +
  572 +.footer .submit {
  573 + background: $theme-primary;
  574 + color: #fff;
  575 +}
  576 +
  577 +.group {
  578 + background: #fff;
  579 + padding: 24rpx 32rpx;
  580 + margin-bottom: 20rpx;
  581 +}
  582 +
  583 +.group-title {
  584 + color: rgba(0, 0, 0, 0.9);
  585 + font-size: 32rpx;
  586 + padding-bottom: 16rpx;
  587 +}
  588 +
  589 +.radio-list {
  590 + display: flex;
  591 + flex-direction: column;
  592 +}
  593 +.quality {
  594 + background-color: #fff;
  595 + display: flex;
  596 + align-items: center;
  597 + padding: 24rpx 32rpx;
  598 + border-bottom: 1rpx solid #f0f0f0;
  599 + margin-top: 20rpx;
  600 +
  601 + .title {
  602 + font-size: 32rpx;
  603 + color: rgba(0, 0, 0, 0.9);
  604 + font-weight: 600;
  605 + }
  606 +
  607 + .opCollapse {
  608 + color: rgba(0, 0, 0, 0.6);
  609 + width: 24rpx;
  610 + height: 24rpx;
  611 + margin-right: 16rpx;
  612 + margin-top: 8rpx;
  613 + }
  614 +}
  615 +
  616 +.radio-item {
  617 + display: flex;
  618 + align-items: center;
  619 + padding: 24rpx 0;
  620 + border-bottom: 1rpx solid #f0f0f0;
  621 +}
  622 +
  623 +.radio-item:last-child {
  624 + border-bottom: none;
  625 +}
  626 +
  627 +.radio {
  628 + width: 32rpx;
  629 + height: 32rpx;
  630 + border-radius: 50%;
  631 + border: 2rpx solid #DCDCDC;
  632 + margin-right: 20rpx;
  633 + box-sizing: border-box;
  634 +}
  635 +
  636 +.radio.checked {
  637 + background: $theme-primary;
  638 + border-color: $theme-primary;
  639 +}
  640 +
  641 +.radio-item .label {
  642 + font-size: 32rpx;
  643 + color: rgba(0, 0, 0, 0.9);
  644 +}
  645 +
  646 +::v-deep .uni-list {
  647 + background: transparent;
  648 +}
  649 +
  650 +::v-deep .uni-list .uni-easyinput .uni-input-input {
  651 + color: rgba(0, 0, 0, 0.9);
  652 +}
  653 +
  654 +::v-deep .uni-list .uni-input-placeholder {
  655 + z-index: 1;
  656 +}
  657 +
  658 +::v-deep .uni-list .uni-input-input {
  659 + background-color: #ffffff;
  660 +}
  661 +
  662 +::v-deep .uni-list-item__extra-text {
  663 + font-size: 32rpx;
  664 +}
  665 +
  666 +::v-deep .uni-list-item__content-title {
  667 + font-size: 32rpx;
  668 + color: rgba(0, 0, 0, 0.9);
  669 +}
  670 +
  671 +::v-deep .uni-list-item__container {
  672 + padding: 32rpx;
  673 +}
  674 +
  675 +::v-deep .uni-list-item__container .uni-easyinput__placeholder-class {
  676 + font-size: 32rpx;
  677 + color: rgba(0, 0, 0, 0.4);
  678 +}
  679 +
  680 +::v-deep .uni-list-item__container .uni-easyinput__content {
  681 + border: none;
  682 + background-color: #ffffff !important;
  683 +}
  684 +
  685 +::v-deep .uni-list-item__container .uni-easyinput__content-input {
  686 + padding-left: 0 !important;
  687 + height: 48rpx;
  688 + line-height: 48rpx;
  689 + font-size: 32rpx;
  690 +}
  691 +
  692 +::v-deep .uni-list-item__container .uni-easyinput__content .content-clear-icon {
  693 + font-size: 44rpx !important;
  694 +}
  695 +
  696 +::v-deep .uni-list-item__container .item-title,
  697 +::v-deep .uni-list-item__container .uni-list-item__content {
  698 + flex: none;
  699 + min-height: 48rpx;
  700 + line-height: 48rpx;
  701 + font-size: 32rpx;
  702 + position: relative;
  703 + width: 162rpx;
  704 + margin-right: 32rpx;
  705 + color: rgba(0, 0, 0, 0.9);
  706 +}
  707 +
  708 +::v-deep .uni-list-item__container .item-title .required {
  709 + color: red;
  710 + position: absolute;
  711 + top: 50%;
  712 + transform: translateY(-50%);
  713 + left: -16rpx;
  714 +}
  715 +
  716 +::v-deep .uni-list-item.select-item.is-empty .uni-list-item__extra-text {
  717 + color: rgba(0, 0, 0, 0.4) !important;
  718 +}
  719 +
  720 +::v-deep .uni-list-item.select-item.is-filled .uni-list-item__extra-text {
  721 + color: rgba(0, 0, 0, 0.9) !important;
  722 +}
  723 +::v-deep .uni-easyinput {
  724 + &__placeholder-class {
  725 + font-size: 32rpx;
  726 + color: rgba(0, 0, 0, 0.4);
  727 + }
  728 +
  729 + &__content {
  730 + border: none;
  731 + background-color: #ffffff !important;
  732 + height: 100%;
  733 + &-input {
  734 + padding-left: 0 !important;
  735 + height: 48rpx;
  736 + line-height: 48rpx;
  737 + font-size: 32rpx;
  738 + }
  739 +
  740 + .content-clear-icon {
  741 + font-size: 44rpx !important;
  742 + }
  743 + }
  744 + }
  745 +
  746 +</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'" 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 +
  13 + </view>
  14 + </view>
  15 +
  16 + <view v-if="mode === 'add'" class="add-list">
  17 + <view v-for="(item, idx) in items" :key="idx" class="block">
  18 + <uni-list v-show="item.collapsed">
  19 + <uni-list-item class="select-item" :class="item.productName ? 'is-filled' : 'is-empty'" clickable @click="openProductSheet(idx)" :rightText="item.productName || '请选择产品名称'" showArrow>
  20 + <template v-slot:body>
  21 + <view class="item-title"><text>产品名称</text></view>
  22 + </template>
  23 + </uni-list-item>
  24 + <uni-list-item title="行业">
  25 + <template v-slot:footer>
  26 + <uni-easyinput v-model="item.industry" :inputBorder="false" placeholder="请输入行业名称" />
  27 + </template>
  28 + </uni-list-item>
  29 + <uni-list-item title="牌号">
  30 + <template v-slot:footer>
  31 + <uni-easyinput v-model="item.brand" :inputBorder="false" placeholder="请输入牌号" />
  32 + </template>
  33 + </uni-list-item>
  34 + </uni-list>
  35 + <uni-list v-show="!item.collapsed">
  36 + <uni-list-item class="select-item" :class="item.productName ? 'is-filled' : 'is-empty'" clickable @click="openProductSheet(idx)" :rightText="item.productName || '请选择产品名称'" showArrow>
  37 + <template v-slot:body>
  38 + <view class="item-title"><text>产品名称</text></view>
  39 + </template>
  40 + </uni-list-item>
  41 + <uni-list-item title="行业">
  42 + <template v-slot:footer>
  43 + <uni-easyinput v-model="item.industry" :inputBorder="false" placeholder="请输入行业名称" />
  44 + </template>
  45 + </uni-list-item>
  46 + <uni-list-item title="牌号">
  47 + <template v-slot:footer>
  48 + <uni-easyinput v-model="item.brand" :inputBorder="false" placeholder="请输入牌号" />
  49 + </template>
  50 + </uni-list-item>
  51 + <uni-list-item title="品质">
  52 + <template v-slot:footer>
  53 + <uni-easyinput v-model="item.quality" :inputBorder="false" placeholder="请输入品质" />
  54 + </template>
  55 + </uni-list-item>
  56 + <uni-list-item title="厚度">
  57 + <template v-slot:footer>
  58 + <uni-easyinput v-model="item.thickness" :inputBorder="false" placeholder="请输入厚度" />
  59 + </template>
  60 + </uni-list-item>
  61 + <uni-list-item title="厚度公差(单项+)">
  62 + <template v-slot:footer>
  63 + <uni-easyinput v-model="item.thicknessTolPos" :inputBorder="false"
  64 + placeholder="请输入厚度公差(单项+)" />
  65 + </template>
  66 + </uni-list-item>
  67 + <uni-list-item title="厚度公差(单项-)">
  68 + <template v-slot:footer>
  69 + <uni-easyinput v-model="item.thicknessTolNeg" :inputBorder="false"
  70 + placeholder="请输入厚度公差(单项-)" />
  71 + </template>
  72 + </uni-list-item>
  73 + <uni-list-item title="宽度">
  74 + <template v-slot:footer>
  75 + <uni-easyinput v-model="item.width" :inputBorder="false" placeholder="请输入宽度" />
  76 + </template>
  77 + </uni-list-item>
  78 + <uni-list-item title="宽度公差(单项+)">
  79 + <template v-slot:footer>
  80 + <uni-easyinput v-model="item.widthTolPos" :inputBorder="false" placeholder="请输入宽度公差(单项+)" />
  81 + </template>
  82 + </uni-list-item>
  83 + <uni-list-item title="宽度公差(单项-)">
  84 + <template v-slot:footer>
  85 + <uni-easyinput v-model="item.widthTolNeg" :inputBorder="false" placeholder="请输入宽度公差(单项-)" />
  86 + </template>
  87 + </uni-list-item>
  88 + <uni-list-item title="长度">
  89 + <template v-slot:footer>
  90 + <uni-easyinput v-model="item.length" :inputBorder="false" placeholder="请输入长度" />
  91 + </template>
  92 + </uni-list-item>
  93 + <uni-list-item title="长度公差(单项+)">
  94 + <template v-slot:footer>
  95 + <uni-easyinput v-model="item.lengthTolPos" :inputBorder="false"
  96 + placeholder="请输入长度公差(单项+)" />
  97 + </template>
  98 + </uni-list-item>
  99 + <uni-list-item title="长度公差(单项-)">
  100 + <template v-slot:footer>
  101 + <uni-easyinput v-model="item.lengthTolNeg" :inputBorder="false"
  102 + placeholder="请输入长度公差(单项-)" />
  103 + </template>
  104 + </uni-list-item>
  105 + <uni-list-item title="状态">
  106 + <template v-slot:footer>
  107 + <uni-easyinput v-model="item.status" :inputBorder="false" placeholder="请输入状态" />
  108 + </template>
  109 + </uni-list-item>
  110 + <uni-list-item title="数量">
  111 + <template v-slot:footer>
  112 + <uni-easyinput v-model="item.quantity" type="number" :inputBorder="false" placeholder="请输入数量" @input="onImmediateChange(idx)" @blur="onNumberBlur(idx, 'quantity', 0)" />
  113 + </template>
  114 + </uni-list-item>
  115 + <uni-list-item title="单价">
  116 + <template v-slot:footer>
  117 + <uni-easyinput v-model="item.unitPrice" disabled type="number" :inputBorder="false" placeholder="请输入单价" @input="onImmediateChange(idx)" @blur="onNumberBlur(idx, 'unitPrice', 0)" />
  118 + </template>
  119 + </uni-list-item>
  120 + <uni-list-item title="外贸加工费">
  121 + <template v-slot:footer>
  122 + <uni-easyinput v-model="item.processingFee" type="number" :inputBorder="false" placeholder="请输入外贸加工费" />
  123 + </template>
  124 + </uni-list-item>
  125 + <uni-list-item title="不含税金额">
  126 + <template v-slot:footer>
  127 + <uni-easyinput v-model="item.amountExcludingTax" type="number" :inputBorder="false" disabled placeholder="自动计算" />
  128 + </template>
  129 + </uni-list-item>
  130 + <uni-list-item title="总金额">
  131 + <template v-slot:footer>
  132 + <uni-easyinput v-model="item.totalAmount" type="number" :inputBorder="false" disabled placeholder="自动计算" />
  133 + </template>
  134 + </uni-list-item>
  135 + <uni-list-item title="发货日期">
  136 + <template v-slot:footer>
  137 + <uni-datetime-picker type="date" v-model="item.orderDate" @change="onDateChange(idx, $event)" />
  138 + </template>
  139 + </uni-list-item>
  140 + </uni-list>
  141 + <view class="block-ops">
  142 + <div class="del" @click="onRemove(idx)">
  143 + <image src="/static/images/delete.png" class="icon" />
  144 + 删除
  145 + </div>
  146 + <div class="toggle" @click="toggleItem(idx)">
  147 + <image :src="item.collapsed ? '/static/images/up.png' : '/static/images/down.png'" class="icon" />
  148 + {{ item.collapsed ? '展开' : '收起' }}</div>
  149 + </view>
  150 + </view>
  151 + </view>
  152 +
  153 + <view v-else class="view-list" v-show="!collapsedView">
  154 + <view v-for="(item, idx) in items" :key="'v-' + idx" class="card">
  155 + <view class="row"><text class="label">产品名称</text><text class="value">{{ item.productName }}</text></view>
  156 + <view class="row"><text class="label">行业</text><text class="value">{{ item.industry }}</text></view>
  157 + <view class="row"><text class="label">牌号</text><text class="value">{{ item.brand }}</text></view>
  158 + <view class="row"><text class="label">品质</text><text class="value">{{ item.quality }}</text></view>
  159 + <view class="row"><text class="label">规格</text><text class="value">{{ item.specDisplay }}</text></view>
  160 + <view class="row"><text class="label">状态</text><text class="value">{{ item.status }}</text></view>
  161 + <view class="row"><text class="label">数量</text><text class="value">{{ item.quantity }}</text></view>
  162 + <view class="row"><text class="label">单价</text><text class="value">{{ formatCurrency(item.unitPrice)
  163 + }}</text>
  164 + </view>
  165 + <view class="row"><text class="label">不含税金额</text><text class="value">{{
  166 + formatCurrency(item.amountExcludingTax)
  167 + }}</text></view>
  168 + <view class="row"><text class="label">总金额</text><text class="value">{{ formatCurrency(item.totalAmount)
  169 + }}</text></view>
  170 + <view class="row"><text class="label">发货日期</text><text class="value">{{ item.orderDate }}</text></view>
  171 + </view>
  172 + </view>
  173 + <SingleSelectSheet :visible.sync="sheet.visible" :title="sheet.title" :options="sheet.options" v-model="sheet.value" @confirm="onProductConfirm" />
  174 + </view>
  175 +</template>
  176 +<script>
  177 +import SingleSelectSheet from '@/components/single-select/index.vue'
  178 +export default {
  179 + name: 'ProductRel',
  180 + props: {
  181 + mode: { type: String, default: 'add' },
  182 + list: { type: Array, default: () => [] },
  183 + max: { type: Number, default: 8 },
  184 + orderDateBase: { type: String, default: '' },
  185 + options: { type: Array, default: () => [] }
  186 + },
  187 + components: { SingleSelectSheet },
  188 + data() {
  189 + return {
  190 + items: [],
  191 + collapsedView: false,
  192 + sheet: { visible: false, title: '请选择产品', options: [], value: '', idx: -1 }
  193 + }
  194 + },
  195 + computed: {
  196 + selectOptions() {
  197 + const list = Array.isArray(this.options) ? this.options : []
  198 + return list.map(o => ({
  199 + label: o.label != null ? o.label : (o.text != null ? o.text : (o.name != null ? o.name : '')),
  200 + value: o.value != null ? o.value : (o.id != null ? o.id : o.productId)
  201 + }))
  202 + }
  203 + },
  204 + watch: {
  205 + items: {
  206 + handler() { this.emitChange() },
  207 + deep: true
  208 + },
  209 + list: {
  210 + handler(v) {
  211 + // const arr = Array.isArray(v) ? v : []
  212 + // this.items = arr.map(x => ({ ...this.defaultItem(), ...x, collapsed: true }))
  213 + this.items = v.map(x => ({ ...this.defaultItem(), ...x, collapsed: true }))
  214 + console.log('v', v)
  215 + },
  216 + deep: true
  217 + }
  218 + },
  219 + created() {
  220 + const init = Array.isArray(this.list) && this.list.length > 0 ? this.list.map(v => ({ ...this.defaultItem(), ...v, collapsed: true })) : [{ ...this.defaultItem(), collapsed: false }]
  221 + this.items = init
  222 + this.recalculateAll()
  223 + },
  224 + methods: {
  225 + defaultItem() {
  226 + return { productId: '', productName: '', industry: '', brand: '', quality: '', thickness: '', thicknessTolPos: '', thicknessTolNeg: '', width: '', widthTolPos: '', widthTolNeg: '', length: '', lengthTolPos: '', lengthTolNeg: '', status: '', quantity: '', unitPrice: '', processingFee: undefined, amountExcludingTax: 0, totalAmount: 0, orderDate: '' }
  227 + },
  228 + onImmediateChange(idx) {
  229 + this.$nextTick(() => this.recalculate(idx))
  230 + },
  231 + toNumber(val) {
  232 + if (typeof val === 'number') return isNaN(val) ? 0 : val
  233 + const n = parseFloat(String(val).replace(/[^0-9.\-]/g, ''))
  234 + return isNaN(n) ? 0 : n
  235 + },
  236 + round(val, digits = 2) {
  237 + const n = Number(val)
  238 + if (isNaN(n)) return 0
  239 + const m = Math.pow(10, digits)
  240 + return Math.round(n * m) / m
  241 + },
  242 + onNumberBlur(idx, field, digits) {
  243 + const it = this.items[idx]
  244 + if (!it) return
  245 + const raw = it[field]
  246 + // 如果为空则保持为空,不自动置为0,仅重新计算依赖字段
  247 + if (raw === '' || raw === null || raw === undefined) {
  248 + this.$set(this.items, idx, it)
  249 + this.recalculate(idx)
  250 + return
  251 + }
  252 + const num = this.toNumber(raw)
  253 + const rounded = this.round(num, digits)
  254 + it[field] = rounded
  255 + this.$set(this.items, idx, it)
  256 + this.recalculate(idx)
  257 + },
  258 + formatCurrency(val) {
  259 + if (val == null || val === '') return ''
  260 + const num = Number(val)
  261 + const pre = isNaN(num) ? '' : '¥'
  262 + const fixed = isNaN(num) ? String(val) : num.toFixed(2)
  263 + return `${pre}${fixed}`
  264 + },
  265 + specOf(item) {
  266 + const t = [item.thickness, item.thicknessTolPos, item.thicknessTolNeg].filter(Boolean).join('/')
  267 + const w = [item.width, item.widthTolPos, item.widthTolNeg].filter(Boolean).join('/')
  268 + const l = [item.length, item.lengthTolPos, item.lengthTolNeg].filter(Boolean).join('/')
  269 + return [t, w, l].filter(Boolean).join(' × ')
  270 + },
  271 + openProductSheet(idx) {
  272 + const opts = this.selectOptions
  273 + const current = this.items[idx] && this.items[idx].productId
  274 + const match = opts.find(o => o.value === current)
  275 + this.sheet = { ...this.sheet, visible: true, title: '请选择产品', options: opts, idx, value: match ? match.value : '' }
  276 + },
  277 + onProductConfirm({ value, label }) {
  278 + const idx = this.sheet.idx
  279 + const it = this.items[idx]
  280 + if (!it) { this.sheet.visible = false; return }
  281 + it.productId = value
  282 + it.productName = label || ''
  283 + this.$set(this.items, idx, it)
  284 + this.sheet.visible = false
  285 + this.emitChange()
  286 + },
  287 + recalculate(idx) {
  288 + const TAX_RATE = 0.13
  289 + const it = this.items[idx]
  290 + if (!it) return
  291 + const qty = this.toNumber(it.quantity)
  292 + const price = this.toNumber(it.unitPrice)
  293 + const total = this.round(qty * price, 2)
  294 + const excl = this.round(total / (1 + TAX_RATE), 2)
  295 + const next = { ...it, totalAmount: total, amountExcludingTax: excl }
  296 + this.$set(this.items, idx, next)
  297 + },
  298 + recalculateAll() {
  299 + for (let i = 0; i < this.items.length; i++) this.recalculate(i)
  300 + },
  301 + onAdd() {
  302 + if (this.items.length >= this.max) return uni.showToast({ title: `最多添加${this.max}个`, icon: 'none' })
  303 + const obj = this.defaultItem()
  304 + obj.collapsed = true
  305 + this.items.push(obj)
  306 + this.emitChange()
  307 + },
  308 + onRemove(idx) {
  309 + this.items.splice(idx, 1)
  310 + this.emitChange()
  311 + },
  312 + toggleItem(idx) {
  313 + const it = this.items[idx]
  314 + if (!it) return
  315 + it.collapsed = !it.collapsed
  316 + this.$set(this.items, idx, it)
  317 + },
  318 + emitChange() {
  319 + const out = this.items.map(it => ({ ...it, specDisplay: this.specOf(it) }))
  320 + this.$emit('input', out)
  321 + this.$emit('update:value', out)
  322 + this.$emit('change', out)
  323 + },
  324 + onDateChange(idx, e) {
  325 + const it = this.items[idx]
  326 + if (!it) return
  327 + const val = typeof e === 'string' ? e : (e && e.detail && e.detail.value) ? e.detail.value : it.orderDate
  328 + const dateStr = String(val).slice(0, 10)
  329 + const base = this.orderDateBase ? new Date(this.orderDateBase) : null
  330 + const d = new Date(dateStr)
  331 + if (base && !isNaN(d.getTime()) && d.getTime() < base.getTime()) {
  332 + uni.showToast({ title: '发货日期不得早于订货日期', icon: 'none' })
  333 + it.orderDate = this.orderDateBase
  334 + } else {
  335 + it.orderDate = dateStr
  336 + }
  337 + this.$set(this.items, idx, it)
  338 + },
  339 + toggleViewCollapse() {
  340 + this.collapsedView = !this.collapsedView
  341 + }
  342 + }
  343 +}
  344 +</script>
  345 +<style lang="scss" scoped>
  346 +.product-rel {
  347 + margin-top: 10px;
  348 +}
  349 +
  350 +.header {
  351 + background-color: #fff;
  352 + display: flex;
  353 + align-items: center;
  354 + padding: 24rpx 32rpx;
  355 + border-bottom: 1rpx solid #f0f0f0;
  356 +}
  357 +
  358 +.dot {
  359 + width: 16rpx;
  360 + height: 16rpx;
  361 + background: #3D48A3;
  362 + border-radius: 50%;
  363 + margin-right: 12rpx;
  364 +}
  365 +
  366 +.title {
  367 + font-size: 32rpx;
  368 + color: rgba(0, 0, 0, 0.9);
  369 + font-weight: 600;
  370 +}
  371 +
  372 +.ops {
  373 + margin-left: auto;
  374 +}
  375 +.op {
  376 + color: $theme-primary;
  377 + font-size: 28rpx;
  378 + margin-left: 8rpx;
  379 +}
  380 +.op1 {
  381 + display: flex;
  382 + align-items: center;
  383 +}
  384 +
  385 +.opAdd {
  386 + color: rgba(0, 0, 0, 0.6);
  387 + width: 40rpx;
  388 + height: 40rpx;
  389 +}
  390 +
  391 +.opCollapse {
  392 + color: rgba(0, 0, 0, 0.6);
  393 + width: 24rpx;
  394 + height: 24rpx;
  395 + margin-right: 16rpx;
  396 + margin-top: 8rpx;
  397 +}
  398 +
  399 +
  400 +.block {
  401 + background: #fff;
  402 + margin-bottom: 20rpx;
  403 +}
  404 +::v-deep .uni-list-item__content {
  405 + display: flex;
  406 + justify-content: center;
  407 +}
  408 +::v-deep .uni-list {
  409 + background: transparent;
  410 +
  411 + &-item {
  412 + &__container {
  413 + padding: 32rpx;
  414 + }
  415 +
  416 + &__content-title {
  417 + font-size: 28rpx;
  418 + color: rgba(0, 0, 0, 0.9);
  419 + }
  420 +
  421 + &__extra-text {
  422 + font-size: 32rpx;
  423 + }
  424 +
  425 + .uni-easyinput {
  426 + width: 100%;
  427 +
  428 + &__placeholder-class {
  429 + font-size: 32rpx;
  430 + color: rgba(0, 0, 0, 0.4);
  431 + }
  432 +
  433 + &__content {
  434 + border: none;
  435 + display: flex;
  436 + &-input {
  437 + padding-left: 0 !important;
  438 + height: 48rpx;
  439 + line-height: 48rpx;
  440 + font-size: 32rpx;
  441 + }
  442 + }
  443 +
  444 + .uni-input-placeholder {
  445 + // z-index: 2;
  446 + }
  447 + }
  448 + }
  449 +}
  450 +
  451 +.block-ops {
  452 + display: flex;
  453 + padding: 20rpx 32rpx 20rpx;
  454 + justify-content: space-around;
  455 +}
  456 +
  457 +.del {
  458 + color: #D54941;
  459 + font-size: 28rpx;
  460 + display: flex;
  461 + align-items: center;
  462 + image {
  463 + width: 40rpx;
  464 + height: 40rpx;
  465 + }
  466 +}
  467 +
  468 +.toggle {
  469 + color: $theme-primary;
  470 + font-size: 28rpx;
  471 + display: flex;
  472 + align-items: center;
  473 + image {
  474 + width: 40rpx;
  475 + height: 40rpx;
  476 + }
  477 +}
  478 +
  479 +.view-list {
  480 + padding: 24rpx 32rpx;
  481 + background: #ffffff;
  482 +}
  483 +
  484 +.card {
  485 + background: #f3f3f3;
  486 + border-radius: 16rpx;
  487 + padding: 24rpx;
  488 + margin-bottom: 20rpx;
  489 +}
  490 +
  491 +.row {
  492 + display: flex;
  493 + margin-bottom: 20rpx;
  494 +}
  495 +
  496 +.row:last-child {
  497 + margin-bottom: 0;
  498 +}
  499 +
  500 +.label {
  501 + width: 120rpx;
  502 + color: rgba(0, 0, 0, 0.6);
  503 + font-size: 28rpx;
  504 +}
  505 +
  506 +.value {
  507 + flex: 1;
  508 + text-align: right;
  509 + color: rgba(0, 0, 0, 0.9);
  510 + font-size: 28rpx;
  511 +}
  512 +</style>