Showing
11 changed files
with
3809 additions
and
25 deletions
| ... | ... | @@ -8,3 +8,67 @@ export function salesmanQuotationQueryApi(params) { |
| 8 | 8 | params |
| 9 | 9 | }) |
| 10 | 10 | } |
| 11 | + | |
| 12 | +// 生成编号 | |
| 13 | +export function salesmanQuotationGenerateCodeApi() { | |
| 14 | + return request({ | |
| 15 | + url: '/procurement/salesmanQuotation/generateCode', | |
| 16 | + method: 'get' | |
| 17 | + }) | |
| 18 | +} | |
| 19 | + | |
| 20 | +// 新增保存 | |
| 21 | +export function salesmanQuotationSaveApi(data) { | |
| 22 | + return request({ | |
| 23 | + url: '/procurement/salesmanQuotation', | |
| 24 | + method: 'post', | |
| 25 | + data, | |
| 26 | + contentType: ContentTypeEnum.JSON | |
| 27 | + }) | |
| 28 | +} | |
| 29 | + | |
| 30 | +// 详情 | |
| 31 | +export function salesmanQuotationDetailApi(params) { | |
| 32 | + return request({ | |
| 33 | + url: '/procurement/salesmanQuotation', | |
| 34 | + method: 'get', | |
| 35 | + params | |
| 36 | + }) | |
| 37 | +} | |
| 38 | + | |
| 39 | +// 审核详情 | |
| 40 | +export function salesmanQuotationGetExamineByIdApi(id) { | |
| 41 | + return request({ | |
| 42 | + url: '/procurement/salesmanQuotation/getExamineById', | |
| 43 | + method: 'get', | |
| 44 | + params: { id } | |
| 45 | + }) | |
| 46 | +} | |
| 47 | + | |
| 48 | +// 编辑 | |
| 49 | +export function salesmanQuotationUpdateApi(data) { | |
| 50 | + return request({ | |
| 51 | + url: '/procurement/salesmanQuotation', | |
| 52 | + method: 'put', | |
| 53 | + data, | |
| 54 | + contentType: ContentTypeEnum.JSON | |
| 55 | + }) | |
| 56 | +} | |
| 57 | + | |
| 58 | +// 品种关系查询 | |
| 59 | +export function breedRelationshipQueryApi(params) { | |
| 60 | + return request({ | |
| 61 | + url: '/baseData/breedRelationship/query', | |
| 62 | + method: 'get', | |
| 63 | + params | |
| 64 | + }) | |
| 65 | +} | |
| 66 | + | |
| 67 | +// 取消 | |
| 68 | +export function salesmanQuotationCancelApi(params) { | |
| 69 | + return request({ | |
| 70 | + url: '/procurement/salesmanQuotation/cancel', | |
| 71 | + method: 'post', | |
| 72 | + params | |
| 73 | + }) | |
| 74 | +} | ... | ... |
| ... | ... | @@ -283,30 +283,38 @@ |
| 283 | 283 | "navigationBarTextStyle": "black" |
| 284 | 284 | } |
| 285 | 285 | }, |
| 286 | - // { | |
| 287 | - // "path": "pages/sales-price/add", | |
| 288 | - // "style": { | |
| 289 | - // "navigationBarTitleText": "新增业务员报价单", | |
| 290 | - // "navigationBarBackgroundColor": "#ffffff", | |
| 291 | - // "navigationBarTextStyle": "black" | |
| 292 | - // } | |
| 293 | - // }, | |
| 294 | - // { | |
| 295 | - // "path": "pages/sales-price/modify", | |
| 296 | - // "style": { | |
| 297 | - // "navigationBarTitleText": "编辑业务员报价单", | |
| 298 | - // "navigationBarBackgroundColor": "#ffffff", | |
| 299 | - // "navigationBarTextStyle": "black" | |
| 300 | - // } | |
| 301 | - // }, | |
| 302 | - // { | |
| 303 | - // "path": "pages/sales-price/detail", | |
| 304 | - // "style": { | |
| 305 | - // "navigationBarTitleText": "查看业务员报价单", | |
| 306 | - // "navigationBarBackgroundColor": "#ffffff", | |
| 307 | - // "navigationBarTextStyle": "black" | |
| 308 | - // } | |
| 309 | - // }, | |
| 286 | + { | |
| 287 | + "path": "pages/sales-price/add", | |
| 288 | + "style": { | |
| 289 | + "navigationBarTitleText": "新增业务员报价单", | |
| 290 | + "navigationBarBackgroundColor": "#ffffff", | |
| 291 | + "navigationBarTextStyle": "black" | |
| 292 | + } | |
| 293 | + }, | |
| 294 | + { | |
| 295 | + "path": "pages/sales-price/modify", | |
| 296 | + "style": { | |
| 297 | + "navigationBarTitleText": "编辑业务员报价单", | |
| 298 | + "navigationBarBackgroundColor": "#ffffff", | |
| 299 | + "navigationBarTextStyle": "black" | |
| 300 | + } | |
| 301 | + }, | |
| 302 | + { | |
| 303 | + "path": "pages/sales-price/detail", | |
| 304 | + "style": { | |
| 305 | + "navigationBarTitleText": "查看业务员报价单", | |
| 306 | + "navigationBarBackgroundColor": "#ffffff", | |
| 307 | + "navigationBarTextStyle": "black" | |
| 308 | + } | |
| 309 | + }, | |
| 310 | + { | |
| 311 | + "path": "pages/sales-price/copy", | |
| 312 | + "style": { | |
| 313 | + "navigationBarTitleText": "复制业务员报价单", | |
| 314 | + "navigationBarBackgroundColor": "#ffffff", | |
| 315 | + "navigationBarTextStyle": "black" | |
| 316 | + } | |
| 317 | + }, | |
| 310 | 318 | |
| 311 | 319 | { |
| 312 | 320 | "path": "pages/supplier-price/index", | ... | ... |
pages/sales-price/add.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="page"> | |
| 3 | + <scroll-view class="scroll" scroll-y> | |
| 4 | + <uni-list> | |
| 5 | + <!-- 编号 --> | |
| 6 | + <uni-list-item class="select-item" :class="form.code ? 'is-filled' : 'is-empty'"> | |
| 7 | + <template v-slot:body> | |
| 8 | + <view class="item-title"><text class="required">*</text><text>编号</text></view> | |
| 9 | + </template> | |
| 10 | + <template v-slot:footer> | |
| 11 | + <view class="serial-number-row"> | |
| 12 | + <text class="readonly-text value-code">{{ form.code }}</text> | |
| 13 | + <button class="generate-btn" @click="initCode">点此生成</button> | |
| 14 | + </view> | |
| 15 | + </template> | |
| 16 | + </uni-list-item> | |
| 17 | + | |
| 18 | + <!-- 供应商名称 --> | |
| 19 | + <uni-list-item class="select-item" :class="form.supplierId ? 'is-filled' : 'is-empty'" clickable | |
| 20 | + @click="openSupplierSheet" :rightText="form.supplierName || '请选择供应商名称'" showArrow> | |
| 21 | + <template v-slot:body> | |
| 22 | + <view class="item-title"><text class="required">*</text><text>供应商名称</text></view> | |
| 23 | + </template> | |
| 24 | + </uni-list-item> | |
| 25 | + | |
| 26 | + <!-- 采购处(带出,禁用) --> | |
| 27 | + <uni-list-item title="采购处"> | |
| 28 | + <template v-slot:footer> | |
| 29 | + <uni-easyinput v-model="form.purchaseDepartmentName" :inputBorder="false" disabled placeholder="选择供应商后自动带出" /> | |
| 30 | + </template> | |
| 31 | + </uni-list-item> | |
| 32 | + | |
| 33 | + <!-- 区域(带出,禁用) --> | |
| 34 | + <uni-list-item title="区域"> | |
| 35 | + <template v-slot:footer> | |
| 36 | + <uni-easyinput v-model="form.regionName" :inputBorder="false" disabled placeholder="选择供应商后自动带出" /> | |
| 37 | + </template> | |
| 38 | + </uni-list-item> | |
| 39 | + | |
| 40 | + <!-- 报价时效 --> | |
| 41 | + <uni-list-item> | |
| 42 | + <template v-slot:body> | |
| 43 | + <view class="item-title"><text class="required">*</text><text>报价时效</text></view> | |
| 44 | + </template> | |
| 45 | + <template v-slot:footer> | |
| 46 | + <uni-easyinput v-model="form.validityPeriod" :inputBorder="false" placeholder="请输入报价时效" /> | |
| 47 | + </template> | |
| 48 | + </uni-list-item> | |
| 49 | + | |
| 50 | + <!-- 报价人(当前登录用户,禁用) --> | |
| 51 | + <uni-list-item title="报价人"> | |
| 52 | + <template v-slot:footer> | |
| 53 | + <uni-easyinput v-model="form.quoterName" :inputBorder="false" disabled placeholder="自动获取" /> | |
| 54 | + </template> | |
| 55 | + </uni-list-item> | |
| 56 | + | |
| 57 | + <!-- 是否长单操作 --> | |
| 58 | + <uni-list-item class="select-item" :class="typeof form.longTermOperation === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 59 | + @click="openSheet('longTermOperation')" :rightText="boolText(form.longTermOperation)" showArrow> | |
| 60 | + <template v-slot:body> | |
| 61 | + <view class="item-title"><text class="required">*</text><text>是否长单操作</text></view> | |
| 62 | + </template> | |
| 63 | + </uni-list-item> | |
| 64 | + | |
| 65 | + <!-- 是否终端客户 --> | |
| 66 | + <uni-list-item class="select-item" :class="typeof form.terminalCustomer === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 67 | + @click="openSheet('terminalCustomer')" :rightText="boolText(form.terminalCustomer)" showArrow> | |
| 68 | + <template v-slot:body> | |
| 69 | + <view class="item-title"><text class="required">*</text><text>是否终端客户</text></view> | |
| 70 | + </template> | |
| 71 | + </uni-list-item> | |
| 72 | + | |
| 73 | + <!-- 是否未点价 --> | |
| 74 | + <uni-list-item class="select-item" :class="typeof form.priced === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 75 | + @click="openSheet('priced')" :rightText="boolText(form.priced)" showArrow> | |
| 76 | + <template v-slot:body> | |
| 77 | + <view class="item-title"><text class="required">*</text><text>是否未点价</text></view> | |
| 78 | + </template> | |
| 79 | + </uni-list-item> | |
| 80 | + | |
| 81 | + <!-- 点价截止日期 - 仅在 是否未点价 = 否 时显示且必填 --> | |
| 82 | + <uni-list-item v-if="form.priced === false" class="select-item" :class="form.pricingDeadline ? 'is-filled' : 'is-empty'"> | |
| 83 | + <template v-slot:body> | |
| 84 | + <view class="item-title"><text class="required">*</text><text>点价截止日期</text></view> | |
| 85 | + </template> | |
| 86 | + <template v-slot:footer> | |
| 87 | + <uni-datetime-picker type="date" v-model="form.pricingDeadline" /> | |
| 88 | + </template> | |
| 89 | + </uni-list-item> | |
| 90 | + | |
| 91 | + <!-- 其他费用凭票到厂另行结算 --> | |
| 92 | + <uni-list-item class="select-item" :class="typeof form.otherExpenseSettlement === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 93 | + @click="openSheet('otherExpenseSettlement')" :rightText="boolText(form.otherExpenseSettlement)" showArrow> | |
| 94 | + <template v-slot:body> | |
| 95 | + <view class="item-title"><text class="required">*</text><text>其他费用凭票到厂另行结算</text></view> | |
| 96 | + </template> | |
| 97 | + </uni-list-item> | |
| 98 | + | |
| 99 | + <!-- 交提货方式 --> | |
| 100 | + <uni-list-item class="select-item" :class="form.deliveryMode ? 'is-filled' : 'is-empty'" clickable | |
| 101 | + @click="openDictSheet('deliveryMode', 'DELIVERY_METHOD', '交提货方式')" :rightText="getDeliveryMethodLabel() || '请选择交提货方式'" showArrow> | |
| 102 | + <template v-slot:body> | |
| 103 | + <view class="item-title"><text class="required">*</text><text>交提货方式</text></view> | |
| 104 | + </template> | |
| 105 | + </uni-list-item> | |
| 106 | + | |
| 107 | + <!-- 是否通货买断 --> | |
| 108 | + <uni-list-item class="select-item" :class="typeof form.commodityBuyout === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 109 | + @click="openSheet('commodityBuyout')" :rightText="boolText(form.commodityBuyout)" showArrow> | |
| 110 | + <template v-slot:body> | |
| 111 | + <view class="item-title"><text class="required">*</text><text>是否通货买断</text></view> | |
| 112 | + </template> | |
| 113 | + </uni-list-item> | |
| 114 | + | |
| 115 | + <!-- 是否质量买断 --> | |
| 116 | + <uni-list-item class="select-item" :class="typeof form.qualityBuyout === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 117 | + @click="openSheet('qualityBuyout')" :rightText="boolText(form.qualityBuyout)" showArrow> | |
| 118 | + <template v-slot:body> | |
| 119 | + <view class="item-title"><text class="required">*</text><text>是否质量买断</text></view> | |
| 120 | + </template> | |
| 121 | + </uni-list-item> | |
| 122 | + | |
| 123 | + <!-- 是否数量买断 --> | |
| 124 | + <uni-list-item class="select-item" :class="typeof form.quantityBuyout === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 125 | + @click="openSheet('quantityBuyout')" :rightText="boolText(form.quantityBuyout)" showArrow> | |
| 126 | + <template v-slot:body> | |
| 127 | + <view class="item-title"><text class="required">*</text><text>是否数量买断</text></view> | |
| 128 | + </template> | |
| 129 | + </uni-list-item> | |
| 130 | + | |
| 131 | + <!-- 是否送货上门 --> | |
| 132 | + <uni-list-item class="select-item" :class="typeof form.doorDelivery === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 133 | + @click="openSheet('doorDelivery')" :rightText="boolText(form.doorDelivery)" showArrow> | |
| 134 | + <template v-slot:body> | |
| 135 | + <view class="item-title"><text class="required">*</text><text>是否送货上门</text></view> | |
| 136 | + </template> | |
| 137 | + </uni-list-item> | |
| 138 | + | |
| 139 | + <!-- 装车费 --> | |
| 140 | + <uni-list-item title="装车费"> | |
| 141 | + <template v-slot:footer> | |
| 142 | + <uni-easyinput v-model="form.loadingFee" type="digit" :inputBorder="false" placeholder="请输入装车费" /> | |
| 143 | + </template> | |
| 144 | + </uni-list-item> | |
| 145 | + | |
| 146 | + <!-- 开票品种 --> | |
| 147 | + <uni-list-item> | |
| 148 | + <template v-slot:body> | |
| 149 | + <view class="item-title"><text class="required">*</text><text>开票品种</text></view> | |
| 150 | + </template> | |
| 151 | + <template v-slot:footer> | |
| 152 | + <uni-easyinput v-model="form.invoiceProductName" :inputBorder="false" placeholder="请输入开票品种" /> | |
| 153 | + </template> | |
| 154 | + </uni-list-item> | |
| 155 | + | |
| 156 | + <!-- 交货日期 --> | |
| 157 | + <uni-list-item> | |
| 158 | + <template v-slot:body> | |
| 159 | + <view class="item-title"><text class="required">*</text><text>交货日期</text></view> | |
| 160 | + </template> | |
| 161 | + <template v-slot:footer> | |
| 162 | + <uni-datetime-picker type="date" v-model="form.deliveryDate" /> | |
| 163 | + </template> | |
| 164 | + </uni-list-item> | |
| 165 | + | |
| 166 | + <!-- 结算方式 --> | |
| 167 | + <uni-list-item> | |
| 168 | + <template v-slot:body> | |
| 169 | + <view class="item-title"><text class="required">*</text><text>结算方式</text></view> | |
| 170 | + </template> | |
| 171 | + <template v-slot:footer> | |
| 172 | + <uni-easyinput v-model="form.settlementMethod" :inputBorder="false" placeholder="请输入结算方式" /> | |
| 173 | + </template> | |
| 174 | + </uni-list-item> | |
| 175 | + | |
| 176 | + <!-- 结算比例(%) --> | |
| 177 | + <uni-list-item> | |
| 178 | + <template v-slot:body> | |
| 179 | + <view class="item-title"><text class="required">*</text><text>结算比例(%)</text></view> | |
| 180 | + </template> | |
| 181 | + <template v-slot:footer> | |
| 182 | + <uni-easyinput v-model="form.settlementRatio" type="digit" :inputBorder="false" placeholder="请输入结算比例" @blur="onRatioBlur" /> | |
| 183 | + </template> | |
| 184 | + </uni-list-item> | |
| 185 | + | |
| 186 | + <!-- 协调事由 --> | |
| 187 | + <uni-list-item title="协调事由"> | |
| 188 | + <template v-slot:footer> | |
| 189 | + <uni-easyinput v-model="form.coordinationReason" :inputBorder="false" placeholder="请输入协调事由" disabled /> | |
| 190 | + </template> | |
| 191 | + </uni-list-item> | |
| 192 | + | |
| 193 | + <!-- 票类状况 --> | |
| 194 | + <uni-list-item title="票类状况"> | |
| 195 | + <template v-slot:footer> | |
| 196 | + <uni-easyinput v-model="form.invoiceTypeStatus" :inputBorder="false" placeholder="请输入票类状况" disabled /> | |
| 197 | + </template> | |
| 198 | + </uni-list-item> | |
| 199 | + | |
| 200 | + <!-- 明细信息 --> | |
| 201 | + <ProductRel ref="productRel" mode="add" :options="productList" :priced="form.priced" @change="onProductsChange" /> | |
| 202 | + </uni-list> | |
| 203 | + | |
| 204 | + <!-- 料质状况模块(明细下方) --> | |
| 205 | + <view class="material-condition-section"> | |
| 206 | + <view class="section-header"> | |
| 207 | + <image class="opCollapse" src="/static/images/title.png" /> | |
| 208 | + <text class="section-title">料质状况</text> | |
| 209 | + </view> | |
| 210 | + <view class="section-content"> | |
| 211 | + <view class="form-row"> | |
| 212 | + <text class="form-label">是否启用料质状况</text> | |
| 213 | + <view class="bool-btns"> | |
| 214 | + <view class="bool-btn" :class="{ active: form.materialConditionEnabled === true }" @click="form.materialConditionEnabled = true">是</view> | |
| 215 | + <view class="bool-btn" :class="{ active: form.materialConditionEnabled === false }" @click="form.materialConditionEnabled = false">否</view> | |
| 216 | + </view> | |
| 217 | + </view> | |
| 218 | + | |
| 219 | + <template v-if="form.materialConditionEnabled"> | |
| 220 | + <view v-for="mc in materialConditionConfigs" :key="mc.field" class="material-group"> | |
| 221 | + <text class="material-label">{{ mc.label }}</text> | |
| 222 | + <view class="tag-list"> | |
| 223 | + <view | |
| 224 | + v-for="opt in getDictOptions(mc.dictCode)" | |
| 225 | + :key="opt.code" | |
| 226 | + class="tag-btn" | |
| 227 | + :class="{ active: isTagSelected(mc.field, opt.code) }" | |
| 228 | + @click="toggleTag(mc.field, opt.code)" | |
| 229 | + > | |
| 230 | + {{ opt.name }} | |
| 231 | + </view> | |
| 232 | + </view> | |
| 233 | + </view> | |
| 234 | + </template> | |
| 235 | + </view> | |
| 236 | + </view> | |
| 237 | + | |
| 238 | + <!-- 备注模块 --> | |
| 239 | + <view class="remark-section"> | |
| 240 | + <view class="section-header"> | |
| 241 | + <image class="opCollapse" src="/static/images/title.png" /> | |
| 242 | + <text class="section-title">备注</text> | |
| 243 | + </view> | |
| 244 | + <view class="section-content"> | |
| 245 | + <uni-easyinput v-model="form.remark" type="textarea" :inputBorder="false" placeholder="请输入备注" maxlength="2000" /> | |
| 246 | + </view> | |
| 247 | + </view> | |
| 248 | + | |
| 249 | + <view class="footer"> | |
| 250 | + <button class="btn submit" type="primary" @click="onSubmit">提交</button> | |
| 251 | + </view> | |
| 252 | + </scroll-view> | |
| 253 | + | |
| 254 | + <SingleSelectSheet :visible.sync="sheet.visible" :title="sheet.title" :options="sheet.options" | |
| 255 | + v-model="sheet.value" @confirm="onSheetConfirm" /> | |
| 256 | + | |
| 257 | + <RelateSelectSheet :visible.sync="supplierSheet.visible" :title="supplierSheet.title" | |
| 258 | + :fetchFn="supplierSheet.fetchFn" :display-fields="supplierSheet.displayFields" | |
| 259 | + :multiple="false" row-key="id" :selectedKeys.sync="supplierSheet.selectedKeys" | |
| 260 | + @confirm="onSupplierConfirm" /> | |
| 261 | + </view> | |
| 262 | +</template> | |
| 263 | +<script> | |
| 264 | +import SingleSelectSheet from '@/components/single-select/index.vue' | |
| 265 | +import RelateSelectSheet from '@/components/relate-select/index.vue' | |
| 266 | +import ProductRel from './productRel.vue' | |
| 267 | +import { salesmanQuotationGenerateCodeApi, salesmanQuotationSaveApi, breedRelationshipQueryApi } from '@/api/procure-manage/salesmanQuotation.js' | |
| 268 | +import { domesticCustomerCreditQueryApi } from '@/api/procure-manage/domesticCustomerCredit.js' | |
| 269 | +import { getInfo } from '@/api/login.js' | |
| 270 | +import { getDicByCodes } from '@/utils/dic.js' | |
| 271 | + | |
| 272 | +export default { | |
| 273 | + name: 'AddSalesPrice', | |
| 274 | + components: { SingleSelectSheet, RelateSelectSheet, ProductRel }, | |
| 275 | + data() { | |
| 276 | + return { | |
| 277 | + form: { | |
| 278 | + code: '', | |
| 279 | + supplierId: '', | |
| 280 | + supplierName: '', | |
| 281 | + purchaseDepartmentName: '', | |
| 282 | + regionName: '', | |
| 283 | + validityPeriod: '', | |
| 284 | + quoterName: '', | |
| 285 | + longTermOperation: false, | |
| 286 | + terminalCustomer: false, | |
| 287 | + priced: true, | |
| 288 | + pricingDeadline: '', | |
| 289 | + otherExpenseSettlement: false, | |
| 290 | + deliveryMode: '', | |
| 291 | + commodityBuyout: false, | |
| 292 | + qualityBuyout: false, | |
| 293 | + quantityBuyout: false, | |
| 294 | + doorDelivery: false, | |
| 295 | + loadingFee: '', | |
| 296 | + invoiceProductName: '', | |
| 297 | + deliveryDate: '', | |
| 298 | + settlementMethod: '', | |
| 299 | + settlementRatio: '', | |
| 300 | + coordinationReason: '受市场影响,申请协调协调事由', | |
| 301 | + invoiceTypeStatus: '增票', | |
| 302 | + materialConditionEnabled: false, | |
| 303 | + materialConditionCopperRice: [], | |
| 304 | + materialConditionBrightCopper: [], | |
| 305 | + materialConditionFirstClass: [], | |
| 306 | + materialConditionSecondClass: [], | |
| 307 | + materialConditionTinCoated: [], | |
| 308 | + materialConditionScrap: [], | |
| 309 | + materialConditionWhiteYellow: [], | |
| 310 | + materialConditionPhosphorCopper: [], | |
| 311 | + remark: '' | |
| 312 | + }, | |
| 313 | + productList: [], | |
| 314 | + productLineList: [], | |
| 315 | + sheet: { visible: false, title: '请选择', options: [], value: '', field: '' }, | |
| 316 | + supplierSheet: { | |
| 317 | + visible: false, | |
| 318 | + title: '选择供应商', | |
| 319 | + fetchFn: null, | |
| 320 | + displayFields: [ | |
| 321 | + { label: '单位名称', field: 'unitName' }, | |
| 322 | + { label: '采购处', field: 'purchaseDepartmentName' }, | |
| 323 | + { label: '区域', field: 'regionName' } | |
| 324 | + ], | |
| 325 | + selectedKeys: [] | |
| 326 | + }, | |
| 327 | + materialConditionConfigs: [ | |
| 328 | + { field: 'materialConditionCopperRice', label: '铜米', dictCode: 'COPPER_RICE' }, | |
| 329 | + { field: 'materialConditionBrightCopper', label: '光亮铜', dictCode: 'BRIGHT_COPPER' }, | |
| 330 | + { field: 'materialConditionFirstClass', label: '一特', dictCode: 'FIRST_CLASS' }, | |
| 331 | + { field: 'materialConditionSecondClass', label: '二特', dictCode: 'SECOND_CLASS' }, | |
| 332 | + { field: 'materialConditionTinCoated', label: '镀锡紫', dictCode: 'TIN_COATED' }, | |
| 333 | + { field: 'materialConditionScrap', label: '角料', dictCode: 'SCRAP' }, | |
| 334 | + { field: 'materialConditionWhiteYellow', label: '镀白黄', dictCode: 'WHITE_YELLOW' }, | |
| 335 | + { field: 'materialConditionPhosphorCopper', label: '磷铜', dictCode: 'PHOSPHOR_COPPER' } | |
| 336 | + ], | |
| 337 | + dictData: {} | |
| 338 | + } | |
| 339 | + }, | |
| 340 | + created() { | |
| 341 | + this.supplierSheet.fetchFn = this.fetchSuppliers.bind(this) | |
| 342 | + this.initCode() | |
| 343 | + this.loadQuoter() | |
| 344 | + this.loadProducts() | |
| 345 | + this.loadDictData() | |
| 346 | + }, | |
| 347 | + methods: { | |
| 348 | + async loadDictData() { | |
| 349 | + try { | |
| 350 | + const codes = [...this.materialConditionConfigs.map(mc => mc.dictCode), 'DELIVERY_METHOD'] | |
| 351 | + const res = await getDicByCodes(codes) | |
| 352 | + this.dictData = res || {} | |
| 353 | + } catch (e) { | |
| 354 | + this.dictData = {} | |
| 355 | + } | |
| 356 | + }, | |
| 357 | + getDictOptions(dictCode) { | |
| 358 | + const data = this.dictData[dictCode] | |
| 359 | + return (data && data.data) ? data.data : [] | |
| 360 | + }, | |
| 361 | + isTagSelected(field, code) { | |
| 362 | + const selected = this.form[field] | |
| 363 | + return Array.isArray(selected) && selected.includes(code) | |
| 364 | + }, | |
| 365 | + toggleTag(field, code) { | |
| 366 | + let selected = this.form[field] | |
| 367 | + if (!Array.isArray(selected)) { | |
| 368 | + selected = [] | |
| 369 | + } | |
| 370 | + const index = selected.indexOf(code) | |
| 371 | + if (index > -1) { | |
| 372 | + selected.splice(index, 1) | |
| 373 | + } else { | |
| 374 | + selected.push(code) | |
| 375 | + } | |
| 376 | + this.$set(this.form, field, [...selected]) | |
| 377 | + }, | |
| 378 | + async initCode() { | |
| 379 | + try { | |
| 380 | + const res = await salesmanQuotationGenerateCodeApi() | |
| 381 | + this.form.code = (res && res.data) ? res.data : '' | |
| 382 | + } catch (e) { | |
| 383 | + this.form.code = '' | |
| 384 | + } | |
| 385 | + }, | |
| 386 | + async loadQuoter() { | |
| 387 | + try { | |
| 388 | + // 优先使用 store 中已缓存的用户信息 | |
| 389 | + const storeUser = this.$store && this.$store.state && this.$store.state.user ? this.$store.state.user : {} | |
| 390 | + if (storeUser.name) { | |
| 391 | + this.form.quoterName = storeUser.name | |
| 392 | + return | |
| 393 | + } | |
| 394 | + const res = await getInfo() | |
| 395 | + const _data = res && res.data ? res.data : {} | |
| 396 | + this.form.quoterName = _data.name || '' | |
| 397 | + } catch (e) { | |
| 398 | + this.form.quoterName = '' | |
| 399 | + } | |
| 400 | + }, | |
| 401 | + async loadProducts() { | |
| 402 | + try { | |
| 403 | + const res = await breedRelationshipQueryApi({ pageIndex: 1, pageSize: 9999 }) | |
| 404 | + const _data = res && res.data ? res.data : {} | |
| 405 | + const list = _data.datas || _data.list || _data.records || [] | |
| 406 | + this.productList = list.map(it => ({ | |
| 407 | + label: it.productName || it.name || '', | |
| 408 | + value: it.productId || it.id || '', | |
| 409 | + lossRate: it.fixedLossRate != null ? it.fixedLossRate : (it.lossRate != null ? it.lossRate : (it.attritionRate != null ? it.attritionRate : '')) | |
| 410 | + })) | |
| 411 | + } catch (e) { | |
| 412 | + this.productList = [] | |
| 413 | + } | |
| 414 | + }, | |
| 415 | + // 供应商列表数据源(RelateSelectSheet fetchFn) | |
| 416 | + fetchSuppliers({ pageIndex, pageSize, extra }) { | |
| 417 | + const name = (extra && (extra.name || extra.keyword || extra.key)) || '' | |
| 418 | + const params = { | |
| 419 | + pageIndex, | |
| 420 | + pageSize, | |
| 421 | + available: true, | |
| 422 | + unitName: name, | |
| 423 | + status: 'PASS', | |
| 424 | + freeze: false | |
| 425 | + } | |
| 426 | + return domesticCustomerCreditQueryApi(params).then(res => { | |
| 427 | + const _data = res && res.data ? res.data : {} | |
| 428 | + const records = _data.datas || _data.list || _data.records || [] | |
| 429 | + const totalCount = _data.totalCount || _data.count || 0 | |
| 430 | + const hasNext = _data.hasNext || false | |
| 431 | + return { records, totalCount, hasNext } | |
| 432 | + }).catch(() => ({ records: [], totalCount: 0, hasNext: false })) | |
| 433 | + }, | |
| 434 | + openSupplierSheet() { | |
| 435 | + this.supplierSheet.selectedKeys = this.form.supplierId ? [this.form.supplierId] : [] | |
| 436 | + this.supplierSheet.visible = true | |
| 437 | + }, | |
| 438 | + onSupplierConfirm({ items }) { | |
| 439 | + const first = (items && items.length > 0) ? items[0] : null | |
| 440 | + if (!first) return | |
| 441 | + this.form.supplierId = first.id || '' | |
| 442 | + this.form.supplierName = first.unitName || '' | |
| 443 | + this.form.purchaseDepartmentName = first.purchaseDepartmentName || '' | |
| 444 | + this.form.regionName = first.regionName || '' | |
| 445 | + }, | |
| 446 | + boolText(v) { | |
| 447 | + if (typeof v !== 'boolean') return '请选择' | |
| 448 | + return v ? '是' : '否' | |
| 449 | + }, | |
| 450 | + openSheet(field) { | |
| 451 | + const opts = [{ label: '是', value: true }, { label: '否', value: false }] | |
| 452 | + const cur = this.form[field] | |
| 453 | + const match = opts.find(o => o.value === cur) | |
| 454 | + const titleMap = { | |
| 455 | + longTermOperation: '是否长单操作', | |
| 456 | + terminalCustomer: '是否终端客户', | |
| 457 | + priced: '是否未点价', | |
| 458 | + otherExpenseSettlement: '其他费用凭票到厂另行结算', | |
| 459 | + commodityBuyout: '是否通货买断', | |
| 460 | + qualityBuyout: '是否质量买断', | |
| 461 | + quantityBuyout: '是否数量买断', | |
| 462 | + doorDelivery: '是否送货上门' | |
| 463 | + } | |
| 464 | + this.sheet = { visible: true, title: titleMap[field] || '请选择', options: opts, value: match ? match.value : '', field, isDict: false } | |
| 465 | + }, | |
| 466 | + openDictSheet(field, dictCode, title) { | |
| 467 | + const raw = this.getDictOptions(dictCode) || [] | |
| 468 | + const opts = raw.map(o => ({ label: o.name || o.label || '', value: o.code != null ? o.code : (o.value != null ? o.value : '') })) | |
| 469 | + const cur = this.form[field] | |
| 470 | + const match = opts.find(o => String(o.value) === String(cur)) | |
| 471 | + this.sheet = { visible: true, title: title || '请选择', options: opts, value: match ? match.value : '', field, isDict: true } | |
| 472 | + }, | |
| 473 | + getDeliveryMethodLabel() { | |
| 474 | + const opts = this.getDictOptions('DELIVERY_METHOD') || [] | |
| 475 | + const m = opts.find(o => String(o.code != null ? o.code : o.value) === String(this.form.deliveryMode)) | |
| 476 | + return m ? (m.name || m.label || '') : '' | |
| 477 | + }, | |
| 478 | + onSheetConfirm({ value }) { | |
| 479 | + const field = this.sheet.field | |
| 480 | + if (!field) return | |
| 481 | + if (this.sheet.isDict) { | |
| 482 | + this.form[field] = value | |
| 483 | + } else { | |
| 484 | + this.form[field] = !!value | |
| 485 | + // 切换 是否未点价 时,若改为 是 则清空点价截止日期 | |
| 486 | + if (field === 'priced' && value === true) { | |
| 487 | + this.form.pricingDeadline = '' | |
| 488 | + } | |
| 489 | + } | |
| 490 | + }, | |
| 491 | + onRatioBlur() { | |
| 492 | + let v = parseFloat(this.form.settlementRatio) | |
| 493 | + if (isNaN(v)) v = '' | |
| 494 | + else v = Math.round(v * 100) / 100 | |
| 495 | + this.form.settlementRatio = v | |
| 496 | + }, | |
| 497 | + onProductsChange(products) { | |
| 498 | + this.productLineList = Array.isArray(products) ? products : [] | |
| 499 | + }, | |
| 500 | + validateRequired() { | |
| 501 | + const stringChecks = [ | |
| 502 | + { key: 'code', label: '编号', msg: '请输入编号' }, | |
| 503 | + { key: 'supplierId', label: '供应商名称', msg: '请选择供应商' }, | |
| 504 | + { key: 'validityPeriod', label: '报价时效', msg: '请输入报价时效' }, | |
| 505 | + { key: 'deliveryMode', label: '交提货方式', msg: '请选择交提货方式' }, | |
| 506 | + { key: 'invoiceProductName', label: '开票品种', msg: '请输入开票品种' }, | |
| 507 | + { key: 'deliveryDate', label: '交货日期', msg: '请选择交货日期' }, | |
| 508 | + { key: 'settlementMethod', label: '结算方式', msg: '请输入结算方式' }, | |
| 509 | + { key: 'settlementRatio', label: '结算比例', msg: '请输入结算比例' } | |
| 510 | + ] | |
| 511 | + for (const it of stringChecks) { | |
| 512 | + const val = this.form[it.key] | |
| 513 | + const empty = (val === undefined || val === null || (typeof val === 'string' && val.trim() === '')) | |
| 514 | + if (empty) { uni.showToast({ title: it.msg, icon: 'none' }); return false } | |
| 515 | + } | |
| 516 | + const boolChecks = [ | |
| 517 | + { key: 'longTermOperation', msg: '请选择是否长单操作' }, | |
| 518 | + { key: 'terminalCustomer', msg: '请选择是否终端客户' }, | |
| 519 | + { key: 'priced', msg: '请选择是否未点价' }, | |
| 520 | + { key: 'otherExpenseSettlement', msg: '请选择其他费用凭票到厂另行结算' }, | |
| 521 | + { key: 'commodityBuyout', msg: '请选择是否通货买断' }, | |
| 522 | + { key: 'qualityBuyout', msg: '请选择是否质量买断' }, | |
| 523 | + { key: 'quantityBuyout', msg: '请选择是否数量买断' }, | |
| 524 | + { key: 'doorDelivery', msg: '请选择是否送货上门' } | |
| 525 | + ] | |
| 526 | + for (const it of boolChecks) { | |
| 527 | + if (typeof this.form[it.key] !== 'boolean') { | |
| 528 | + uni.showToast({ title: it.msg, icon: 'none' }) | |
| 529 | + return false | |
| 530 | + } | |
| 531 | + } | |
| 532 | + // 点价截止日期:是否未点价=否 时必填 | |
| 533 | + if (this.form.priced === false && !this.form.pricingDeadline) { | |
| 534 | + uni.showToast({ title: '请选择点价截止日期', icon: 'none' }) | |
| 535 | + return false | |
| 536 | + } | |
| 537 | + // 明细校验 | |
| 538 | + const list = Array.isArray(this.productLineList) ? this.productLineList : [] | |
| 539 | + if (list.length === 0) { | |
| 540 | + uni.showToast({ title: '请添加产品明细', icon: 'none' }) | |
| 541 | + return false | |
| 542 | + } | |
| 543 | + if (this.$refs.productRel && !this.$refs.productRel.validate(this.form.priced)) return false | |
| 544 | + return true | |
| 545 | + }, | |
| 546 | + async onSubmit() { | |
| 547 | + if (!this.validateRequired()) return | |
| 548 | + const confirmRes = await new Promise(resolve => { | |
| 549 | + uni.showModal({ title: '提示', content: '确定新增业务员报价单吗?', confirmText: '确定', cancelText: '取消', success: resolve }) | |
| 550 | + }) | |
| 551 | + if (!(confirmRes && confirmRes.confirm)) return | |
| 552 | + const clean = (obj) => { | |
| 553 | + const out = {} | |
| 554 | + Object.keys(obj || {}).forEach(k => { | |
| 555 | + const v = obj[k] | |
| 556 | + const isEmptyString = typeof v === 'string' && v.trim() === '' | |
| 557 | + const isUndef = v === undefined || v === null | |
| 558 | + const isNaNNumber = typeof v === 'number' && isNaN(v) | |
| 559 | + if (!(isEmptyString || isUndef || isNaNNumber)) out[k] = v | |
| 560 | + }) | |
| 561 | + return out | |
| 562 | + } | |
| 563 | + const lines = (this.productLineList || []).map(it => clean(it)) | |
| 564 | + const materialFields = this.materialConditionConfigs.map(mc => mc.field) | |
| 565 | + const formCopy = { ...this.form } | |
| 566 | + materialFields.forEach(f => { | |
| 567 | + const arr = formCopy[f] | |
| 568 | + formCopy[f] = Array.isArray(arr) ? arr.join(',') : (arr || '') | |
| 569 | + }) | |
| 570 | + const payload = clean({ | |
| 571 | + ...formCopy, | |
| 572 | + quotationLineList: lines | |
| 573 | + }) | |
| 574 | + try { | |
| 575 | + await salesmanQuotationSaveApi(payload) | |
| 576 | + uni.showToast({ title: '新增成功', icon: 'none' }) | |
| 577 | + try { uni.setStorageSync('SALES_PRICE_LIST_NEED_REFRESH', '1') } catch (e) {} | |
| 578 | + setTimeout(() => { uni.redirectTo({ url: '/pages/sales-price/index' }) }, 400) | |
| 579 | + } catch (e) { | |
| 580 | + uni.showToast({ title: e.msg || '新增失败', icon: 'none' }) | |
| 581 | + } | |
| 582 | + } | |
| 583 | + } | |
| 584 | +} | |
| 585 | +</script> | |
| 586 | +<style lang="scss" scoped> | |
| 587 | +.page { | |
| 588 | + display: flex; | |
| 589 | + flex-direction: column; | |
| 590 | + height: 100%; | |
| 591 | +} | |
| 592 | + | |
| 593 | +.scroll { | |
| 594 | + flex: 1; | |
| 595 | + padding: 12rpx 0 392rpx !important; | |
| 596 | +} | |
| 597 | + | |
| 598 | +.footer { | |
| 599 | + z-index: 2; | |
| 600 | + position: fixed; | |
| 601 | + left: 0; | |
| 602 | + right: 0; | |
| 603 | + bottom: 0; | |
| 604 | + padding: 32rpx; | |
| 605 | + padding-bottom: calc(32rpx + env(safe-area-inset-bottom)); | |
| 606 | + background: #fff; | |
| 607 | + box-shadow: 0 -8rpx 24rpx rgba(0, 0, 0, 0.06); | |
| 608 | + | |
| 609 | + .btn { | |
| 610 | + height: 80rpx; | |
| 611 | + line-height: 80rpx; | |
| 612 | + border-radius: 12rpx; | |
| 613 | + font-size: 32rpx; | |
| 614 | + } | |
| 615 | + | |
| 616 | + .submit { | |
| 617 | + background: $theme-primary; | |
| 618 | + color: #fff; | |
| 619 | + } | |
| 620 | +} | |
| 621 | + | |
| 622 | +::v-deep .uni-list { | |
| 623 | + .uni-easyinput { | |
| 624 | + display: flex; | |
| 625 | + | |
| 626 | + .uni-input-input { | |
| 627 | + color: rgba(0, 0, 0, 0.9); | |
| 628 | + } | |
| 629 | + } | |
| 630 | + | |
| 631 | + .uni-input-placeholder { | |
| 632 | + z-index: 1; | |
| 633 | + } | |
| 634 | + | |
| 635 | + .uni-input-input { | |
| 636 | + background-color: #ffffff; | |
| 637 | + } | |
| 638 | + | |
| 639 | + background: transparent; | |
| 640 | + | |
| 641 | + &-item { | |
| 642 | + &__extra-text { | |
| 643 | + font-size: 32rpx; | |
| 644 | + } | |
| 645 | + | |
| 646 | + &__content-title { | |
| 647 | + font-size: 32rpx; | |
| 648 | + color: rgba(0, 0, 0, 0.9); | |
| 649 | + } | |
| 650 | + | |
| 651 | + &__container { | |
| 652 | + padding: 32rpx; | |
| 653 | + | |
| 654 | + .uni-easyinput { | |
| 655 | + &__placeholder-class { | |
| 656 | + font-size: 32rpx; | |
| 657 | + color: rgba(0, 0, 0, 0.4); | |
| 658 | + } | |
| 659 | + | |
| 660 | + &__content { | |
| 661 | + border: none; | |
| 662 | + background-color: #ffffff !important; | |
| 663 | + | |
| 664 | + &-input { | |
| 665 | + padding-left: 0 !important; | |
| 666 | + height: 48rpx; | |
| 667 | + line-height: 48rpx; | |
| 668 | + font-size: 32rpx; | |
| 669 | + } | |
| 670 | + | |
| 671 | + .content-clear-icon { | |
| 672 | + font-size: 44rpx !important; | |
| 673 | + } | |
| 674 | + } | |
| 675 | + } | |
| 676 | + | |
| 677 | + .item-title, | |
| 678 | + .uni-list-item__content { | |
| 679 | + flex: none; | |
| 680 | + min-height: 48rpx; | |
| 681 | + line-height: 48rpx; | |
| 682 | + font-size: 32rpx; | |
| 683 | + position: relative; | |
| 684 | + width: 240rpx; | |
| 685 | + margin-right: 32rpx; | |
| 686 | + color: rgba(0, 0, 0, 0.9); | |
| 687 | + | |
| 688 | + .required { | |
| 689 | + color: red; | |
| 690 | + position: absolute; | |
| 691 | + top: 50%; | |
| 692 | + transform: translateY(-50%); | |
| 693 | + left: -16rpx; | |
| 694 | + } | |
| 695 | + } | |
| 696 | + } | |
| 697 | + | |
| 698 | + &.select-item { | |
| 699 | + &.is-empty { | |
| 700 | + .uni-list-item__extra-text { | |
| 701 | + color: rgba(0, 0, 0, 0.4) !important; | |
| 702 | + } | |
| 703 | + } | |
| 704 | + | |
| 705 | + &.is-filled { | |
| 706 | + .uni-list-item__extra-text { | |
| 707 | + color: rgba(0, 0, 0, 0.9) !important; | |
| 708 | + } | |
| 709 | + } | |
| 710 | + | |
| 711 | + .serial-number-row { | |
| 712 | + display: flex; | |
| 713 | + align-items: center; | |
| 714 | + | |
| 715 | + .generate-btn { | |
| 716 | + margin-left: 24rpx; | |
| 717 | + height: 48rpx; | |
| 718 | + line-height: 48rpx; | |
| 719 | + padding: 0 24rpx; | |
| 720 | + font-size: 24rpx; | |
| 721 | + border-radius: 8rpx; | |
| 722 | + background: $theme-primary; | |
| 723 | + color: #fff; | |
| 724 | + } | |
| 725 | + } | |
| 726 | + } | |
| 727 | + } | |
| 728 | +} | |
| 729 | + | |
| 730 | +.readonly-text { | |
| 731 | + color: rgba(0, 0, 0, 0.9); | |
| 732 | + font-size: 32rpx; | |
| 733 | + line-height: 48rpx; | |
| 734 | + text-align: right; | |
| 735 | + white-space: pre-wrap; | |
| 736 | + word-break: break-all; | |
| 737 | +} | |
| 738 | + | |
| 739 | +.value-code { | |
| 740 | + width: 260rpx; | |
| 741 | + text-align: left; | |
| 742 | +} | |
| 743 | + | |
| 744 | +.material-condition-section, | |
| 745 | +.remark-section { | |
| 746 | + margin-top: 20rpx; | |
| 747 | + background: #fff; | |
| 748 | +} | |
| 749 | + | |
| 750 | +.section-header { | |
| 751 | + display: flex; | |
| 752 | + align-items: center; | |
| 753 | + padding: 24rpx 32rpx; | |
| 754 | + border-bottom: 1rpx solid #f0f0f0; | |
| 755 | +} | |
| 756 | + | |
| 757 | +.section-title { | |
| 758 | + font-size: 32rpx; | |
| 759 | + color: rgba(0, 0, 0, 0.9); | |
| 760 | + font-weight: 600; | |
| 761 | +} | |
| 762 | + | |
| 763 | +.opCollapse { | |
| 764 | + width: 32rpx; | |
| 765 | + height: 28rpx; | |
| 766 | + margin-right: 16rpx; | |
| 767 | + margin-top: 8rpx; | |
| 768 | +} | |
| 769 | + | |
| 770 | +.section-content { | |
| 771 | + padding: 32rpx; | |
| 772 | +} | |
| 773 | + | |
| 774 | +.form-row { | |
| 775 | + display: flex; | |
| 776 | + align-items: center; | |
| 777 | + justify-content: space-between; | |
| 778 | + margin-bottom: 24rpx; | |
| 779 | +} | |
| 780 | + | |
| 781 | +.form-row:last-child { | |
| 782 | + margin-bottom: 0; | |
| 783 | +} | |
| 784 | + | |
| 785 | +.form-label { | |
| 786 | + font-size: 32rpx; | |
| 787 | + color: rgba(0, 0, 0, 0.9); | |
| 788 | +} | |
| 789 | + | |
| 790 | +.bool-btns { | |
| 791 | + display: flex; | |
| 792 | + gap: 20rpx; | |
| 793 | +} | |
| 794 | + | |
| 795 | +.bool-btn { | |
| 796 | + padding: 12rpx 40rpx; | |
| 797 | + border: 1rpx solid #ddd; | |
| 798 | + border-radius: 8rpx; | |
| 799 | + font-size: 28rpx; | |
| 800 | + color: #666; | |
| 801 | + background: #fff; | |
| 802 | +} | |
| 803 | + | |
| 804 | +.bool-btn.active { | |
| 805 | + background: $theme-primary; | |
| 806 | + border-color: $theme-primary; | |
| 807 | + color: #fff; | |
| 808 | +} | |
| 809 | + | |
| 810 | +.material-group { | |
| 811 | + margin-top: 32rpx; | |
| 812 | +} | |
| 813 | + | |
| 814 | +.material-label { | |
| 815 | + display: block; | |
| 816 | + font-size: 30rpx; | |
| 817 | + color: rgba(0, 0, 0, 0.9); | |
| 818 | + margin-bottom: 20rpx; | |
| 819 | +} | |
| 820 | + | |
| 821 | +.tag-list { | |
| 822 | + display: flex; | |
| 823 | + flex-wrap: wrap; | |
| 824 | + gap: 16rpx; | |
| 825 | +} | |
| 826 | + | |
| 827 | +.tag-btn { | |
| 828 | + padding: 12rpx 24rpx; | |
| 829 | + border: 1rpx solid #ddd; | |
| 830 | + border-radius: 8rpx; | |
| 831 | + font-size: 28rpx; | |
| 832 | + color: #666; | |
| 833 | + background: #fff; | |
| 834 | +} | |
| 835 | + | |
| 836 | +.tag-btn.active { | |
| 837 | + background: $theme-primary; | |
| 838 | + border-color: $theme-primary; | |
| 839 | + color: #fff; | |
| 840 | +} | |
| 841 | + | |
| 842 | +::v-deep .uni-easyinput { | |
| 843 | + width: 100%; | |
| 844 | + | |
| 845 | + &__content { | |
| 846 | + background: #f8f8f8 !important; | |
| 847 | + border-radius: 8rpx; | |
| 848 | + | |
| 849 | + &-input { | |
| 850 | + font-size: 30rpx; | |
| 851 | + } | |
| 852 | + } | |
| 853 | +} | |
| 854 | +</style> | ... | ... |
pages/sales-price/copy.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="page"> | |
| 3 | + <scroll-view class="scroll" scroll-y> | |
| 4 | + <uni-list> | |
| 5 | + <!-- 编号 --> | |
| 6 | + <uni-list-item class="select-item" :class="form.code ? 'is-filled' : 'is-empty'"> | |
| 7 | + <template v-slot:body> | |
| 8 | + <view class="item-title"><text class="required">*</text><text>编号</text></view> | |
| 9 | + </template> | |
| 10 | + <template v-slot:footer> | |
| 11 | + <view class="serial-number-row"> | |
| 12 | + <text class="readonly-text value-code">{{ form.code }}</text> | |
| 13 | + <button class="generate-btn" @click="initCode">点此生成</button> | |
| 14 | + </view> | |
| 15 | + </template> | |
| 16 | + </uni-list-item> | |
| 17 | + | |
| 18 | + <!-- 供应商名称 --> | |
| 19 | + <uni-list-item class="select-item" :class="form.supplierId ? 'is-filled' : 'is-empty'" clickable | |
| 20 | + @click="openSupplierSheet" :rightText="form.supplierName || '请选择供应商名称'" showArrow> | |
| 21 | + <template v-slot:body> | |
| 22 | + <view class="item-title"><text class="required">*</text><text>供应商名称</text></view> | |
| 23 | + </template> | |
| 24 | + </uni-list-item> | |
| 25 | + | |
| 26 | + <!-- 采购处(带出,禁用) --> | |
| 27 | + <uni-list-item title="采购处"> | |
| 28 | + <template v-slot:footer> | |
| 29 | + <uni-easyinput v-model="form.purchaseDepartmentName" :inputBorder="false" disabled placeholder="选择供应商后自动带出" /> | |
| 30 | + </template> | |
| 31 | + </uni-list-item> | |
| 32 | + | |
| 33 | + <!-- 区域(带出,禁用) --> | |
| 34 | + <uni-list-item title="区域"> | |
| 35 | + <template v-slot:footer> | |
| 36 | + <uni-easyinput v-model="form.regionName" :inputBorder="false" disabled placeholder="选择供应商后自动带出" /> | |
| 37 | + </template> | |
| 38 | + </uni-list-item> | |
| 39 | + | |
| 40 | + <!-- 报价时效 --> | |
| 41 | + <uni-list-item> | |
| 42 | + <template v-slot:body> | |
| 43 | + <view class="item-title"><text class="required">*</text><text>报价时效</text></view> | |
| 44 | + </template> | |
| 45 | + <template v-slot:footer> | |
| 46 | + <uni-easyinput v-model="form.validityPeriod" :inputBorder="false" placeholder="请输入报价时效" /> | |
| 47 | + </template> | |
| 48 | + </uni-list-item> | |
| 49 | + | |
| 50 | + <!-- 报价人(禁用) --> | |
| 51 | + <uni-list-item title="报价人"> | |
| 52 | + <template v-slot:footer> | |
| 53 | + <uni-easyinput v-model="form.quoterName" :inputBorder="false" disabled placeholder="自动获取" /> | |
| 54 | + </template> | |
| 55 | + </uni-list-item> | |
| 56 | + | |
| 57 | + <!-- 是否长单操作 --> | |
| 58 | + <uni-list-item class="select-item" :class="typeof form.longTermOperation === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 59 | + @click="openSheet('longTermOperation')" :rightText="boolText(form.longTermOperation)" showArrow> | |
| 60 | + <template v-slot:body> | |
| 61 | + <view class="item-title"><text class="required">*</text><text>是否长单操作</text></view> | |
| 62 | + </template> | |
| 63 | + </uni-list-item> | |
| 64 | + | |
| 65 | + <!-- 是否终端客户 --> | |
| 66 | + <uni-list-item class="select-item" :class="typeof form.terminalCustomer === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 67 | + @click="openSheet('terminalCustomer')" :rightText="boolText(form.terminalCustomer)" showArrow> | |
| 68 | + <template v-slot:body> | |
| 69 | + <view class="item-title"><text class="required">*</text><text>是否终端客户</text></view> | |
| 70 | + </template> | |
| 71 | + </uni-list-item> | |
| 72 | + | |
| 73 | + <!-- 是否未点价 --> | |
| 74 | + <uni-list-item class="select-item" :class="typeof form.priced === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 75 | + @click="openSheet('priced')" :rightText="boolText(form.priced)" showArrow> | |
| 76 | + <template v-slot:body> | |
| 77 | + <view class="item-title"><text class="required">*</text><text>是否未点价</text></view> | |
| 78 | + </template> | |
| 79 | + </uni-list-item> | |
| 80 | + | |
| 81 | + <!-- 点价截止日期 - 仅在 是否未点价 = 否 时显示且必填 --> | |
| 82 | + <uni-list-item v-if="form.priced === false" class="select-item" :class="form.pricingDeadline ? 'is-filled' : 'is-empty'"> | |
| 83 | + <template v-slot:body> | |
| 84 | + <view class="item-title"><text class="required">*</text><text>点价截止日期</text></view> | |
| 85 | + </template> | |
| 86 | + <template v-slot:footer> | |
| 87 | + <uni-datetime-picker type="date" v-model="form.pricingDeadline" /> | |
| 88 | + </template> | |
| 89 | + </uni-list-item> | |
| 90 | + | |
| 91 | + <!-- 其他费用凭票到厂另行结算 --> | |
| 92 | + <uni-list-item class="select-item" :class="typeof form.otherExpenseSettlement === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 93 | + @click="openSheet('otherExpenseSettlement')" :rightText="boolText(form.otherExpenseSettlement)" showArrow> | |
| 94 | + <template v-slot:body> | |
| 95 | + <view class="item-title"><text class="required">*</text><text>其他费用凭票到厂另行结算</text></view> | |
| 96 | + </template> | |
| 97 | + </uni-list-item> | |
| 98 | + | |
| 99 | + <!-- 交提货方式 --> | |
| 100 | + <uni-list-item class="select-item" :class="form.deliveryMode ? 'is-filled' : 'is-empty'" clickable | |
| 101 | + @click="openDictSheet('deliveryMode', 'DELIVERY_METHOD', '交提货方式')" :rightText="getDeliveryMethodLabel() || '请选择交提货方式'" showArrow> | |
| 102 | + <template v-slot:body> | |
| 103 | + <view class="item-title"><text class="required">*</text><text>交提货方式</text></view> | |
| 104 | + </template> | |
| 105 | + </uni-list-item> | |
| 106 | + | |
| 107 | + <!-- 是否通货买断 --> | |
| 108 | + <uni-list-item class="select-item" :class="typeof form.commodityBuyout === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 109 | + @click="openSheet('commodityBuyout')" :rightText="boolText(form.commodityBuyout)" showArrow> | |
| 110 | + <template v-slot:body> | |
| 111 | + <view class="item-title"><text class="required">*</text><text>是否通货买断</text></view> | |
| 112 | + </template> | |
| 113 | + </uni-list-item> | |
| 114 | + | |
| 115 | + <!-- 是否质量买断 --> | |
| 116 | + <uni-list-item class="select-item" :class="typeof form.qualityBuyout === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 117 | + @click="openSheet('qualityBuyout')" :rightText="boolText(form.qualityBuyout)" showArrow> | |
| 118 | + <template v-slot:body> | |
| 119 | + <view class="item-title"><text class="required">*</text><text>是否质量买断</text></view> | |
| 120 | + </template> | |
| 121 | + </uni-list-item> | |
| 122 | + | |
| 123 | + <!-- 是否数量买断 --> | |
| 124 | + <uni-list-item class="select-item" :class="typeof form.quantityBuyout === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 125 | + @click="openSheet('quantityBuyout')" :rightText="boolText(form.quantityBuyout)" showArrow> | |
| 126 | + <template v-slot:body> | |
| 127 | + <view class="item-title"><text class="required">*</text><text>是否数量买断</text></view> | |
| 128 | + </template> | |
| 129 | + </uni-list-item> | |
| 130 | + | |
| 131 | + <!-- 是否送货上门 --> | |
| 132 | + <uni-list-item class="select-item" :class="typeof form.doorDelivery === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 133 | + @click="openSheet('doorDelivery')" :rightText="boolText(form.doorDelivery)" showArrow> | |
| 134 | + <template v-slot:body> | |
| 135 | + <view class="item-title"><text class="required">*</text><text>是否送货上门</text></view> | |
| 136 | + </template> | |
| 137 | + </uni-list-item> | |
| 138 | + | |
| 139 | + <!-- 装车费 --> | |
| 140 | + <uni-list-item title="装车费"> | |
| 141 | + <template v-slot:footer> | |
| 142 | + <uni-easyinput v-model="form.loadingFee" type="digit" :inputBorder="false" placeholder="请输入装车费" /> | |
| 143 | + </template> | |
| 144 | + </uni-list-item> | |
| 145 | + | |
| 146 | + <!-- 开票品种 --> | |
| 147 | + <uni-list-item> | |
| 148 | + <template v-slot:body> | |
| 149 | + <view class="item-title"><text class="required">*</text><text>开票品种</text></view> | |
| 150 | + </template> | |
| 151 | + <template v-slot:footer> | |
| 152 | + <uni-easyinput v-model="form.invoiceProductName" :inputBorder="false" placeholder="请输入开票品种" /> | |
| 153 | + </template> | |
| 154 | + </uni-list-item> | |
| 155 | + | |
| 156 | + <!-- 交货日期 --> | |
| 157 | + <uni-list-item> | |
| 158 | + <template v-slot:body> | |
| 159 | + <view class="item-title"><text class="required">*</text><text>交货日期</text></view> | |
| 160 | + </template> | |
| 161 | + <template v-slot:footer> | |
| 162 | + <uni-datetime-picker type="date" v-model="form.deliveryDate" /> | |
| 163 | + </template> | |
| 164 | + </uni-list-item> | |
| 165 | + | |
| 166 | + <!-- 结算方式 --> | |
| 167 | + <uni-list-item> | |
| 168 | + <template v-slot:body> | |
| 169 | + <view class="item-title"><text class="required">*</text><text>结算方式</text></view> | |
| 170 | + </template> | |
| 171 | + <template v-slot:footer> | |
| 172 | + <uni-easyinput v-model="form.settlementMethod" :inputBorder="false" placeholder="请输入结算方式" /> | |
| 173 | + </template> | |
| 174 | + </uni-list-item> | |
| 175 | + | |
| 176 | + <!-- 结算比例(%) --> | |
| 177 | + <uni-list-item> | |
| 178 | + <template v-slot:body> | |
| 179 | + <view class="item-title"><text class="required">*</text><text>结算比例(%)</text></view> | |
| 180 | + </template> | |
| 181 | + <template v-slot:footer> | |
| 182 | + <uni-easyinput v-model="form.settlementRatio" type="digit" :inputBorder="false" placeholder="请输入结算比例" @blur="onRatioBlur" /> | |
| 183 | + </template> | |
| 184 | + </uni-list-item> | |
| 185 | + | |
| 186 | + <!-- 协调事由 --> | |
| 187 | + <uni-list-item title="协调事由"> | |
| 188 | + <template v-slot:footer> | |
| 189 | + <uni-easyinput v-model="form.coordinationReason" :inputBorder="false" placeholder="请输入协调事由" disabled /> | |
| 190 | + </template> | |
| 191 | + </uni-list-item> | |
| 192 | + | |
| 193 | + <!-- 票类状况 --> | |
| 194 | + <uni-list-item title="票类状况"> | |
| 195 | + <template v-slot:footer> | |
| 196 | + <uni-easyinput v-model="form.invoiceTypeStatus" :inputBorder="false" placeholder="请输入票类状况" disabled /> | |
| 197 | + </template> | |
| 198 | + </uni-list-item> | |
| 199 | + | |
| 200 | + <!-- 明细信息 --> | |
| 201 | + <ProductRel ref="productRel" mode="add" :options="productList" :priced="form.priced" :list="productLineList" @change="onProductsChange" /> | |
| 202 | + </uni-list> | |
| 203 | + | |
| 204 | + <!-- 料质状况模块(明细下方) --> | |
| 205 | + <view class="material-condition-section"> | |
| 206 | + <view class="section-header"> | |
| 207 | + <image class="opCollapse" src="/static/images/title.png" /> | |
| 208 | + <text class="section-title">料质状况</text> | |
| 209 | + </view> | |
| 210 | + <view class="section-content"> | |
| 211 | + <view class="form-row"> | |
| 212 | + <text class="form-label">是否启用料质状况</text> | |
| 213 | + <view class="bool-btns"> | |
| 214 | + <view class="bool-btn" :class="{ active: form.materialConditionEnabled === true }" @click="form.materialConditionEnabled = true">是</view> | |
| 215 | + <view class="bool-btn" :class="{ active: form.materialConditionEnabled === false }" @click="form.materialConditionEnabled = false">否</view> | |
| 216 | + </view> | |
| 217 | + </view> | |
| 218 | + | |
| 219 | + <template v-if="form.materialConditionEnabled"> | |
| 220 | + <view v-for="mc in materialConditionConfigs" :key="mc.field" class="material-group"> | |
| 221 | + <text class="material-label">{{ mc.label }}</text> | |
| 222 | + <view class="tag-list"> | |
| 223 | + <view | |
| 224 | + v-for="opt in getDictOptions(mc.dictCode)" | |
| 225 | + :key="opt.code" | |
| 226 | + class="tag-btn" | |
| 227 | + :class="{ active: isTagSelected(mc.field, opt.code) }" | |
| 228 | + @click="toggleTag(mc.field, opt.code)" | |
| 229 | + > | |
| 230 | + {{ opt.name }} | |
| 231 | + </view> | |
| 232 | + </view> | |
| 233 | + </view> | |
| 234 | + </template> | |
| 235 | + </view> | |
| 236 | + </view> | |
| 237 | + | |
| 238 | + <!-- 备注模块 --> | |
| 239 | + <view class="remark-section"> | |
| 240 | + <view class="section-header"> | |
| 241 | + <image class="opCollapse" src="/static/images/title.png" /> | |
| 242 | + <text class="section-title">备注</text> | |
| 243 | + </view> | |
| 244 | + <view class="section-content"> | |
| 245 | + <uni-easyinput v-model="form.remark" type="textarea" :inputBorder="false" placeholder="请输入备注" maxlength="2000" /> | |
| 246 | + </view> | |
| 247 | + </view> | |
| 248 | + | |
| 249 | + <view class="footer"> | |
| 250 | + <button class="btn submit" type="primary" @click="onSubmit">提交</button> | |
| 251 | + </view> | |
| 252 | + </scroll-view> | |
| 253 | + | |
| 254 | + <SingleSelectSheet :visible.sync="sheet.visible" :title="sheet.title" :options="sheet.options" | |
| 255 | + v-model="sheet.value" @confirm="onSheetConfirm" /> | |
| 256 | + | |
| 257 | + <RelateSelectSheet :visible.sync="supplierSheet.visible" :title="supplierSheet.title" | |
| 258 | + :fetchFn="supplierSheet.fetchFn" :display-fields="supplierSheet.displayFields" | |
| 259 | + :multiple="false" row-key="id" :selectedKeys.sync="supplierSheet.selectedKeys" | |
| 260 | + @confirm="onSupplierConfirm" /> | |
| 261 | + </view> | |
| 262 | +</template> | |
| 263 | +<script> | |
| 264 | +import SingleSelectSheet from '@/components/single-select/index.vue' | |
| 265 | +import RelateSelectSheet from '@/components/relate-select/index.vue' | |
| 266 | +import ProductRel from './productRel.vue' | |
| 267 | +import { salesmanQuotationDetailApi, salesmanQuotationGenerateCodeApi, salesmanQuotationSaveApi, breedRelationshipQueryApi } from '@/api/procure-manage/salesmanQuotation.js' | |
| 268 | +import { domesticCustomerCreditQueryApi } from '@/api/procure-manage/domesticCustomerCredit.js' | |
| 269 | +import { getDicByCodes } from '@/utils/dic.js' | |
| 270 | + | |
| 271 | +export default { | |
| 272 | + name: 'CopySalesPrice', | |
| 273 | + components: { SingleSelectSheet, RelateSelectSheet, ProductRel }, | |
| 274 | + data() { | |
| 275 | + return { | |
| 276 | + sourceId: '', | |
| 277 | + form: { | |
| 278 | + code: '', | |
| 279 | + supplierId: '', | |
| 280 | + supplierName: '', | |
| 281 | + purchaseDepartmentName: '', | |
| 282 | + regionName: '', | |
| 283 | + validityPeriod: '', | |
| 284 | + quoterName: '', | |
| 285 | + longTermOperation: false, | |
| 286 | + terminalCustomer: false, | |
| 287 | + priced: true, | |
| 288 | + pricingDeadline: '', | |
| 289 | + otherExpenseSettlement: false, | |
| 290 | + deliveryMode: '', | |
| 291 | + commodityBuyout: false, | |
| 292 | + qualityBuyout: false, | |
| 293 | + quantityBuyout: false, | |
| 294 | + doorDelivery: false, | |
| 295 | + loadingFee: '', | |
| 296 | + invoiceProductName: '', | |
| 297 | + deliveryDate: '', | |
| 298 | + settlementMethod: '', | |
| 299 | + settlementRatio: '', | |
| 300 | + coordinationReason: '', | |
| 301 | + invoiceTypeStatus: '', | |
| 302 | + materialConditionEnabled: false, | |
| 303 | + materialConditionCopperRice: [], | |
| 304 | + materialConditionBrightCopper: [], | |
| 305 | + materialConditionFirstClass: [], | |
| 306 | + materialConditionSecondClass: [], | |
| 307 | + materialConditionTinCoated: [], | |
| 308 | + materialConditionScrap: [], | |
| 309 | + materialConditionWhiteYellow: [], | |
| 310 | + materialConditionPhosphorCopper: [], | |
| 311 | + remark: '' | |
| 312 | + }, | |
| 313 | + productList: [], | |
| 314 | + productLineList: [], | |
| 315 | + sheet: { visible: false, title: '请选择', options: [], value: '', field: '' }, | |
| 316 | + supplierSheet: { | |
| 317 | + visible: false, | |
| 318 | + title: '选择供应商', | |
| 319 | + fetchFn: null, | |
| 320 | + displayFields: [ | |
| 321 | + { label: '单位名称', field: 'unitName' }, | |
| 322 | + { label: '采购处', field: 'purchaseDepartmentName' }, | |
| 323 | + { label: '区域', field: 'regionName' } | |
| 324 | + ], | |
| 325 | + selectedKeys: [] | |
| 326 | + }, | |
| 327 | + materialConditionConfigs: [ | |
| 328 | + { field: 'materialConditionCopperRice', label: '铜米', dictCode: 'COPPER_RICE' }, | |
| 329 | + { field: 'materialConditionBrightCopper', label: '光亮铜', dictCode: 'BRIGHT_COPPER' }, | |
| 330 | + { field: 'materialConditionFirstClass', label: '一特', dictCode: 'FIRST_CLASS' }, | |
| 331 | + { field: 'materialConditionSecondClass', label: '二特', dictCode: 'SECOND_CLASS' }, | |
| 332 | + { field: 'materialConditionTinCoated', label: '镀锡紫', dictCode: 'TIN_COATED' }, | |
| 333 | + { field: 'materialConditionScrap', label: '角料', dictCode: 'SCRAP' }, | |
| 334 | + { field: 'materialConditionWhiteYellow', label: '镀白黄', dictCode: 'WHITE_YELLOW' }, | |
| 335 | + { field: 'materialConditionPhosphorCopper', label: '磷铜', dictCode: 'PHOSPHOR_COPPER' } | |
| 336 | + ], | |
| 337 | + dictData: {} | |
| 338 | + } | |
| 339 | + }, | |
| 340 | + onLoad(query) { | |
| 341 | + this.sourceId = (query && query.id) || '' | |
| 342 | + this.supplierSheet.fetchFn = this.fetchSuppliers.bind(this) | |
| 343 | + this.loadProducts() | |
| 344 | + this.loadDictData() | |
| 345 | + this.loadSourceDetail() | |
| 346 | + this.initCode() | |
| 347 | + }, | |
| 348 | + methods: { | |
| 349 | + async loadDictData() { | |
| 350 | + try { | |
| 351 | + const codes = [...this.materialConditionConfigs.map(mc => mc.dictCode), 'DELIVERY_METHOD'] | |
| 352 | + const res = await getDicByCodes(codes) | |
| 353 | + this.dictData = res || {} | |
| 354 | + } catch (e) { | |
| 355 | + this.dictData = {} | |
| 356 | + } | |
| 357 | + }, | |
| 358 | + getDictOptions(dictCode) { | |
| 359 | + const data = this.dictData[dictCode] | |
| 360 | + return (data && data.data) ? data.data : [] | |
| 361 | + }, | |
| 362 | + isTagSelected(field, code) { | |
| 363 | + const selected = this.form[field] | |
| 364 | + return Array.isArray(selected) && selected.includes(code) | |
| 365 | + }, | |
| 366 | + toggleTag(field, code) { | |
| 367 | + let selected = this.form[field] | |
| 368 | + if (!Array.isArray(selected)) { | |
| 369 | + selected = [] | |
| 370 | + } | |
| 371 | + const index = selected.indexOf(code) | |
| 372 | + if (index > -1) { | |
| 373 | + selected.splice(index, 1) | |
| 374 | + } else { | |
| 375 | + selected.push(code) | |
| 376 | + } | |
| 377 | + this.$set(this.form, field, [...selected]) | |
| 378 | + }, | |
| 379 | + async initCode() { | |
| 380 | + try { | |
| 381 | + const res = await salesmanQuotationGenerateCodeApi() | |
| 382 | + this.form.code = (res && res.data) ? res.data : '' | |
| 383 | + } catch (e) { | |
| 384 | + this.form.code = '' | |
| 385 | + } | |
| 386 | + }, | |
| 387 | + async loadProducts() { | |
| 388 | + try { | |
| 389 | + const res = await breedRelationshipQueryApi({ pageIndex: 1, pageSize: 9999 }) | |
| 390 | + const _data = res && res.data ? res.data : {} | |
| 391 | + const list = _data.datas || _data.list || _data.records || [] | |
| 392 | + this.productList = list.map(it => ({ | |
| 393 | + label: it.productName || it.name || '', | |
| 394 | + value: it.productId || it.id || '', | |
| 395 | + lossRate: it.fixedLossRate != null ? it.fixedLossRate : (it.lossRate != null ? it.lossRate : (it.attritionRate != null ? it.attritionRate : '')) | |
| 396 | + })) | |
| 397 | + } catch (e) { | |
| 398 | + this.productList = [] | |
| 399 | + } | |
| 400 | + }, | |
| 401 | + async loadSourceDetail() { | |
| 402 | + if (!this.sourceId) return | |
| 403 | + try { | |
| 404 | + const res = await salesmanQuotationDetailApi({ id: this.sourceId }) | |
| 405 | + const d = (res && res.data) ? res.data : {} | |
| 406 | + // 清空 id 和 code,复制为新增 | |
| 407 | + delete d.id | |
| 408 | + delete d.code | |
| 409 | + // 清空明细行的 id 和 itemId | |
| 410 | + const lineList = Array.isArray(d.quotationLineList) ? d.quotationLineList : (Array.isArray(d.salesmanQuotationLineList) ? d.salesmanQuotationLineList : []) | |
| 411 | + lineList.forEach(it => { | |
| 412 | + delete it.id | |
| 413 | + delete it.itemId | |
| 414 | + }) | |
| 415 | + const materialFields = this.materialConditionConfigs.map(mc => mc.field) | |
| 416 | + materialFields.forEach(f => { | |
| 417 | + const v = d[f] | |
| 418 | + d[f] = (typeof v === 'string' && v) ? v.split(',').filter(Boolean) : (Array.isArray(v) ? v : []) | |
| 419 | + }) | |
| 420 | + this.form = { ...this.form, ...d } | |
| 421 | + this.productLineList = lineList | |
| 422 | + } catch (e) { | |
| 423 | + uni.showToast({ title: '加载源数据失败', icon: 'none' }) | |
| 424 | + } | |
| 425 | + }, | |
| 426 | + fetchSuppliers({ pageIndex, pageSize, extra }) { | |
| 427 | + const name = (extra && (extra.name || extra.keyword || extra.key)) || '' | |
| 428 | + const params = { | |
| 429 | + pageIndex, | |
| 430 | + pageSize, | |
| 431 | + available: true, | |
| 432 | + unitName: name, | |
| 433 | + status: 'PASS', | |
| 434 | + freeze: false | |
| 435 | + } | |
| 436 | + return domesticCustomerCreditQueryApi(params).then(res => { | |
| 437 | + const _data = res && res.data ? res.data : {} | |
| 438 | + const records = _data.datas || _data.list || _data.records || [] | |
| 439 | + const totalCount = _data.totalCount || _data.count || 0 | |
| 440 | + const hasNext = _data.hasNext || false | |
| 441 | + return { records, totalCount, hasNext } | |
| 442 | + }).catch(() => ({ records: [], totalCount: 0, hasNext: false })) | |
| 443 | + }, | |
| 444 | + openSupplierSheet() { | |
| 445 | + this.supplierSheet.selectedKeys = this.form.supplierId ? [this.form.supplierId] : [] | |
| 446 | + this.supplierSheet.visible = true | |
| 447 | + }, | |
| 448 | + onSupplierConfirm({ items }) { | |
| 449 | + const first = (items && items.length > 0) ? items[0] : null | |
| 450 | + if (!first) return | |
| 451 | + this.form.supplierId = first.id || '' | |
| 452 | + this.form.supplierName = first.unitName || '' | |
| 453 | + this.form.purchaseDepartmentName = first.purchaseDepartmentName || '' | |
| 454 | + this.form.regionName = first.regionName || '' | |
| 455 | + }, | |
| 456 | + boolText(v) { | |
| 457 | + if (typeof v !== 'boolean') return '请选择' | |
| 458 | + return v ? '是' : '否' | |
| 459 | + }, | |
| 460 | + openSheet(field) { | |
| 461 | + const opts = [{ label: '是', value: true }, { label: '否', value: false }] | |
| 462 | + const cur = this.form[field] | |
| 463 | + const match = opts.find(o => o.value === cur) | |
| 464 | + const titleMap = { | |
| 465 | + longTermOperation: '是否长单操作', | |
| 466 | + terminalCustomer: '是否终端客户', | |
| 467 | + priced: '是否未点价', | |
| 468 | + otherExpenseSettlement: '其他费用凭票到厂另行结算', | |
| 469 | + commodityBuyout: '是否通货买断', | |
| 470 | + qualityBuyout: '是否质量买断', | |
| 471 | + quantityBuyout: '是否数量买断', | |
| 472 | + doorDelivery: '是否送货上门' | |
| 473 | + } | |
| 474 | + this.sheet = { visible: true, title: titleMap[field] || '请选择', options: opts, value: match ? match.value : '', field, isDict: false } | |
| 475 | + }, | |
| 476 | + openDictSheet(field, dictCode, title) { | |
| 477 | + const raw = this.getDictOptions(dictCode) || [] | |
| 478 | + const opts = raw.map(o => ({ label: o.name || o.label || '', value: o.code != null ? o.code : (o.value != null ? o.value : '') })) | |
| 479 | + const cur = this.form[field] | |
| 480 | + const match = opts.find(o => String(o.value) === String(cur)) | |
| 481 | + this.sheet = { visible: true, title: title || '请选择', options: opts, value: match ? match.value : '', field, isDict: true } | |
| 482 | + }, | |
| 483 | + getDeliveryMethodLabel() { | |
| 484 | + const opts = this.getDictOptions('DELIVERY_METHOD') || [] | |
| 485 | + const m = opts.find(o => String(o.code != null ? o.code : o.value) === String(this.form.deliveryMode)) | |
| 486 | + return m ? (m.name || m.label || '') : '' | |
| 487 | + }, | |
| 488 | + onSheetConfirm({ value }) { | |
| 489 | + const field = this.sheet.field | |
| 490 | + if (!field) return | |
| 491 | + if (this.sheet.isDict) { | |
| 492 | + this.form[field] = value | |
| 493 | + } else { | |
| 494 | + this.form[field] = !!value | |
| 495 | + if (field === 'priced' && value === true) { | |
| 496 | + this.form.pricingDeadline = '' | |
| 497 | + } | |
| 498 | + } | |
| 499 | + }, | |
| 500 | + onRatioBlur() { | |
| 501 | + let v = parseFloat(this.form.settlementRatio) | |
| 502 | + if (isNaN(v)) v = '' | |
| 503 | + else v = Math.round(v * 100) / 100 | |
| 504 | + this.form.settlementRatio = v | |
| 505 | + }, | |
| 506 | + onProductsChange(products) { | |
| 507 | + this.productLineList = Array.isArray(products) ? products : [] | |
| 508 | + }, | |
| 509 | + validateRequired() { | |
| 510 | + const stringChecks = [ | |
| 511 | + { key: 'code', msg: '请输入编号' }, | |
| 512 | + { key: 'supplierId', msg: '请选择供应商' }, | |
| 513 | + { key: 'validityPeriod', msg: '请输入报价时效' }, | |
| 514 | + { key: 'deliveryMode', msg: '请选择交提货方式' }, | |
| 515 | + { key: 'invoiceProductName', msg: '请输入开票品种' }, | |
| 516 | + { key: 'deliveryDate', msg: '请选择交货日期' }, | |
| 517 | + { key: 'settlementMethod', msg: '请输入结算方式' }, | |
| 518 | + { key: 'settlementRatio', msg: '请输入结算比例' } | |
| 519 | + ] | |
| 520 | + for (const it of stringChecks) { | |
| 521 | + const val = this.form[it.key] | |
| 522 | + const empty = (val === undefined || val === null || (typeof val === 'string' && val.trim() === '')) | |
| 523 | + if (empty) { uni.showToast({ title: it.msg, icon: 'none' }); return false } | |
| 524 | + } | |
| 525 | + const boolChecks = [ | |
| 526 | + { key: 'longTermOperation', msg: '请选择是否长单操作' }, | |
| 527 | + { key: 'terminalCustomer', msg: '请选择是否终端客户' }, | |
| 528 | + { key: 'priced', msg: '请选择是否未点价' }, | |
| 529 | + { key: 'otherExpenseSettlement', msg: '请选择其他费用凭票到厂另行结算' }, | |
| 530 | + { key: 'commodityBuyout', msg: '请选择是否通货买断' }, | |
| 531 | + { key: 'qualityBuyout', msg: '请选择是否质量买断' }, | |
| 532 | + { key: 'quantityBuyout', msg: '请选择是否数量买断' }, | |
| 533 | + { key: 'doorDelivery', msg: '请选择是否送货上门' } | |
| 534 | + ] | |
| 535 | + for (const it of boolChecks) { | |
| 536 | + if (typeof this.form[it.key] !== 'boolean') { | |
| 537 | + uni.showToast({ title: it.msg, icon: 'none' }) | |
| 538 | + return false | |
| 539 | + } | |
| 540 | + } | |
| 541 | + if (this.form.priced === false && !this.form.pricingDeadline) { | |
| 542 | + uni.showToast({ title: '请选择点价截止日期', icon: 'none' }) | |
| 543 | + return false | |
| 544 | + } | |
| 545 | + const list = Array.isArray(this.productLineList) ? this.productLineList : [] | |
| 546 | + if (list.length === 0) { | |
| 547 | + uni.showToast({ title: '请添加产品明细', icon: 'none' }) | |
| 548 | + return false | |
| 549 | + } | |
| 550 | + if (this.$refs.productRel && !this.$refs.productRel.validate(this.form.priced)) return false | |
| 551 | + return true | |
| 552 | + }, | |
| 553 | + async onSubmit() { | |
| 554 | + if (!this.validateRequired()) return | |
| 555 | + const confirmRes = await new Promise(resolve => { | |
| 556 | + uni.showModal({ title: '提示', content: '确定复制提交吗?', confirmText: '确定', cancelText: '取消', success: resolve }) | |
| 557 | + }) | |
| 558 | + if (!(confirmRes && confirmRes.confirm)) return | |
| 559 | + const clean = (obj) => { | |
| 560 | + const out = {} | |
| 561 | + Object.keys(obj || {}).forEach(k => { | |
| 562 | + const v = obj[k] | |
| 563 | + const isEmptyString = typeof v === 'string' && v.trim() === '' | |
| 564 | + const isUndef = v === undefined || v === null | |
| 565 | + const isNaNNumber = typeof v === 'number' && isNaN(v) | |
| 566 | + if (!(isEmptyString || isUndef || isNaNNumber)) out[k] = v | |
| 567 | + }) | |
| 568 | + return out | |
| 569 | + } | |
| 570 | + const lines = (this.productLineList || []).map(it => { | |
| 571 | + const c = clean(it) | |
| 572 | + delete c.id | |
| 573 | + delete c.itemId | |
| 574 | + return c | |
| 575 | + }) | |
| 576 | + const materialFields = this.materialConditionConfigs.map(mc => mc.field) | |
| 577 | + const formCopy = { ...this.form } | |
| 578 | + delete formCopy.id | |
| 579 | + materialFields.forEach(f => { | |
| 580 | + const arr = formCopy[f] | |
| 581 | + formCopy[f] = Array.isArray(arr) ? arr.join(',') : (arr || '') | |
| 582 | + }) | |
| 583 | + const payload = clean({ | |
| 584 | + ...formCopy, | |
| 585 | + quotationLineList: lines | |
| 586 | + }) | |
| 587 | + try { | |
| 588 | + await salesmanQuotationSaveApi(payload) | |
| 589 | + uni.showToast({ title: '复制成功', icon: 'none' }) | |
| 590 | + try { uni.setStorageSync('SALES_PRICE_LIST_NEED_REFRESH', '1') } catch (e) {} | |
| 591 | + setTimeout(() => { uni.redirectTo({ url: '/pages/sales-price/index' }) }, 400) | |
| 592 | + } catch (e) { | |
| 593 | + uni.showToast({ title: e.msg || '复制失败', icon: 'none' }) | |
| 594 | + } | |
| 595 | + } | |
| 596 | + } | |
| 597 | +} | |
| 598 | +</script> | |
| 599 | +<style lang="scss" scoped> | |
| 600 | +.page { | |
| 601 | + display: flex; | |
| 602 | + flex-direction: column; | |
| 603 | + height: 100%; | |
| 604 | +} | |
| 605 | + | |
| 606 | +.scroll { | |
| 607 | + flex: 1; | |
| 608 | + padding: 12rpx 0 392rpx !important; | |
| 609 | +} | |
| 610 | + | |
| 611 | +.footer { | |
| 612 | + z-index: 2; | |
| 613 | + position: fixed; | |
| 614 | + left: 0; | |
| 615 | + right: 0; | |
| 616 | + bottom: 0; | |
| 617 | + padding: 32rpx; | |
| 618 | + padding-bottom: calc(32rpx + env(safe-area-inset-bottom)); | |
| 619 | + background: #fff; | |
| 620 | + box-shadow: 0 -8rpx 24rpx rgba(0, 0, 0, 0.06); | |
| 621 | + | |
| 622 | + .btn { | |
| 623 | + height: 80rpx; | |
| 624 | + line-height: 80rpx; | |
| 625 | + border-radius: 12rpx; | |
| 626 | + font-size: 32rpx; | |
| 627 | + } | |
| 628 | + | |
| 629 | + .submit { | |
| 630 | + background: $theme-primary; | |
| 631 | + color: #fff; | |
| 632 | + } | |
| 633 | +} | |
| 634 | + | |
| 635 | +::v-deep .uni-list { | |
| 636 | + .uni-easyinput { | |
| 637 | + display: flex; | |
| 638 | + | |
| 639 | + .uni-input-input { | |
| 640 | + color: rgba(0, 0, 0, 0.9); | |
| 641 | + } | |
| 642 | + } | |
| 643 | + | |
| 644 | + .uni-input-placeholder { | |
| 645 | + z-index: 1; | |
| 646 | + } | |
| 647 | + | |
| 648 | + .uni-input-input { | |
| 649 | + background-color: #ffffff; | |
| 650 | + } | |
| 651 | + | |
| 652 | + background: transparent; | |
| 653 | + | |
| 654 | + &-item { | |
| 655 | + &__extra-text { | |
| 656 | + font-size: 32rpx; | |
| 657 | + } | |
| 658 | + | |
| 659 | + &__content-title { | |
| 660 | + font-size: 32rpx; | |
| 661 | + color: rgba(0, 0, 0, 0.9); | |
| 662 | + } | |
| 663 | + | |
| 664 | + &__container { | |
| 665 | + padding: 32rpx; | |
| 666 | + | |
| 667 | + .uni-easyinput { | |
| 668 | + &__placeholder-class { | |
| 669 | + font-size: 32rpx; | |
| 670 | + color: rgba(0, 0, 0, 0.4); | |
| 671 | + } | |
| 672 | + | |
| 673 | + &__content { | |
| 674 | + border: none; | |
| 675 | + background-color: #ffffff !important; | |
| 676 | + | |
| 677 | + &-input { | |
| 678 | + padding-left: 0 !important; | |
| 679 | + height: 48rpx; | |
| 680 | + line-height: 48rpx; | |
| 681 | + font-size: 32rpx; | |
| 682 | + } | |
| 683 | + | |
| 684 | + .content-clear-icon { | |
| 685 | + font-size: 44rpx !important; | |
| 686 | + } | |
| 687 | + } | |
| 688 | + } | |
| 689 | + | |
| 690 | + .item-title, | |
| 691 | + .uni-list-item__content { | |
| 692 | + flex: none; | |
| 693 | + min-height: 48rpx; | |
| 694 | + line-height: 48rpx; | |
| 695 | + font-size: 32rpx; | |
| 696 | + position: relative; | |
| 697 | + width: 240rpx; | |
| 698 | + margin-right: 32rpx; | |
| 699 | + color: rgba(0, 0, 0, 0.9); | |
| 700 | + | |
| 701 | + .required { | |
| 702 | + color: red; | |
| 703 | + position: absolute; | |
| 704 | + top: 50%; | |
| 705 | + transform: translateY(-50%); | |
| 706 | + left: -16rpx; | |
| 707 | + } | |
| 708 | + } | |
| 709 | + } | |
| 710 | + | |
| 711 | + &.select-item { | |
| 712 | + &.is-empty { | |
| 713 | + .uni-list-item__extra-text { | |
| 714 | + color: rgba(0, 0, 0, 0.4) !important; | |
| 715 | + } | |
| 716 | + } | |
| 717 | + | |
| 718 | + &.is-filled { | |
| 719 | + .uni-list-item__extra-text { | |
| 720 | + color: rgba(0, 0, 0, 0.9) !important; | |
| 721 | + } | |
| 722 | + } | |
| 723 | + | |
| 724 | + .serial-number-row { | |
| 725 | + display: flex; | |
| 726 | + align-items: center; | |
| 727 | + | |
| 728 | + .generate-btn { | |
| 729 | + margin-left: 24rpx; | |
| 730 | + height: 48rpx; | |
| 731 | + line-height: 48rpx; | |
| 732 | + padding: 0 24rpx; | |
| 733 | + font-size: 24rpx; | |
| 734 | + border-radius: 8rpx; | |
| 735 | + background: $theme-primary; | |
| 736 | + color: #fff; | |
| 737 | + } | |
| 738 | + } | |
| 739 | + } | |
| 740 | + } | |
| 741 | +} | |
| 742 | + | |
| 743 | +.readonly-text { | |
| 744 | + color: rgba(0, 0, 0, 0.9); | |
| 745 | + font-size: 32rpx; | |
| 746 | + line-height: 48rpx; | |
| 747 | + text-align: right; | |
| 748 | + white-space: pre-wrap; | |
| 749 | + word-break: break-all; | |
| 750 | +} | |
| 751 | + | |
| 752 | +.value-code { | |
| 753 | + width: 260rpx; | |
| 754 | + text-align: left; | |
| 755 | +} | |
| 756 | + | |
| 757 | +.material-condition-section, | |
| 758 | +.remark-section { | |
| 759 | + margin-top: 20rpx; | |
| 760 | + background: #fff; | |
| 761 | +} | |
| 762 | + | |
| 763 | +.section-header { | |
| 764 | + display: flex; | |
| 765 | + align-items: center; | |
| 766 | + padding: 24rpx 32rpx; | |
| 767 | + border-bottom: 1rpx solid #f0f0f0; | |
| 768 | +} | |
| 769 | + | |
| 770 | +.section-title { | |
| 771 | + font-size: 32rpx; | |
| 772 | + color: rgba(0, 0, 0, 0.9); | |
| 773 | + font-weight: 600; | |
| 774 | +} | |
| 775 | + | |
| 776 | +.opCollapse { | |
| 777 | + width: 32rpx; | |
| 778 | + height: 28rpx; | |
| 779 | + margin-right: 16rpx; | |
| 780 | + margin-top: 8rpx; | |
| 781 | +} | |
| 782 | + | |
| 783 | +.section-content { | |
| 784 | + padding: 32rpx; | |
| 785 | +} | |
| 786 | + | |
| 787 | +.form-row { | |
| 788 | + display: flex; | |
| 789 | + align-items: center; | |
| 790 | + justify-content: space-between; | |
| 791 | + margin-bottom: 24rpx; | |
| 792 | +} | |
| 793 | + | |
| 794 | +.form-row:last-child { | |
| 795 | + margin-bottom: 0; | |
| 796 | +} | |
| 797 | + | |
| 798 | +.form-label { | |
| 799 | + font-size: 32rpx; | |
| 800 | + color: rgba(0, 0, 0, 0.9); | |
| 801 | +} | |
| 802 | + | |
| 803 | +.bool-btns { | |
| 804 | + display: flex; | |
| 805 | + gap: 20rpx; | |
| 806 | +} | |
| 807 | + | |
| 808 | +.bool-btn { | |
| 809 | + padding: 12rpx 40rpx; | |
| 810 | + border: 1rpx solid #ddd; | |
| 811 | + border-radius: 8rpx; | |
| 812 | + font-size: 28rpx; | |
| 813 | + color: #666; | |
| 814 | + background: #fff; | |
| 815 | +} | |
| 816 | + | |
| 817 | +.bool-btn.active { | |
| 818 | + background: $theme-primary; | |
| 819 | + border-color: $theme-primary; | |
| 820 | + color: #fff; | |
| 821 | +} | |
| 822 | + | |
| 823 | +.material-group { | |
| 824 | + margin-top: 32rpx; | |
| 825 | +} | |
| 826 | + | |
| 827 | +.material-label { | |
| 828 | + display: block; | |
| 829 | + font-size: 30rpx; | |
| 830 | + color: rgba(0, 0, 0, 0.9); | |
| 831 | + margin-bottom: 20rpx; | |
| 832 | +} | |
| 833 | + | |
| 834 | +.tag-list { | |
| 835 | + display: flex; | |
| 836 | + flex-wrap: wrap; | |
| 837 | + gap: 16rpx; | |
| 838 | +} | |
| 839 | + | |
| 840 | +.tag-btn { | |
| 841 | + padding: 12rpx 24rpx; | |
| 842 | + border: 1rpx solid #ddd; | |
| 843 | + border-radius: 8rpx; | |
| 844 | + font-size: 28rpx; | |
| 845 | + color: #666; | |
| 846 | + background: #fff; | |
| 847 | +} | |
| 848 | + | |
| 849 | +.tag-btn.active { | |
| 850 | + background: $theme-primary; | |
| 851 | + border-color: $theme-primary; | |
| 852 | + color: #fff; | |
| 853 | +} | |
| 854 | + | |
| 855 | +::v-deep .uni-easyinput { | |
| 856 | + width: 100%; | |
| 857 | + | |
| 858 | + &__content { | |
| 859 | + background: #f8f8f8 !important; | |
| 860 | + border-radius: 8rpx; | |
| 861 | + | |
| 862 | + &-input { | |
| 863 | + font-size: 30rpx; | |
| 864 | + } | |
| 865 | + } | |
| 866 | +} | |
| 867 | +</style> | ... | ... |
pages/sales-price/detail.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="page"> | |
| 3 | + <scroll-view class="scroll" scroll-y> | |
| 4 | + <view class="detail-page"> | |
| 5 | + <!-- 基础信息 --> | |
| 6 | + <view class="section"> | |
| 7 | + <text class="row company">{{ safeText(form.code) }}</text> | |
| 8 | + <view class="row"><text class="label">供应商名称</text><text class="value">{{ safeText(form.supplierName) }}</text></view> | |
| 9 | + <view class="row"><text class="label">采购处</text><text class="value">{{ safeText(form.purchaseDepartmentName) }}</text></view> | |
| 10 | + <view class="row"><text class="label">区域</text><text class="value">{{ safeText(form.regionName) }}</text></view> | |
| 11 | + <view class="row"><text class="label">报价时效</text><text class="value">{{ safeText(form.validityPeriod) }}</text></view> | |
| 12 | + <view class="row"><text class="label">报价人</text><text class="value">{{ safeText(form.quoterName) }}</text></view> | |
| 13 | + <view class="row"><text class="label">是否长单操作</text><text class="value">{{ boolText(form.longTermOperation) }}</text></view> | |
| 14 | + <view class="row"><text class="label">是否终端客户</text><text class="value">{{ boolText(form.terminalCustomer) }}</text></view> | |
| 15 | + <view class="row"><text class="label">是否未点价</text><text class="value">{{ boolText(form.priced) }}</text></view> | |
| 16 | + <view v-if="form.priced === false" class="row"><text class="label">点价截止日期</text><text class="value">{{ formatDateOnly(form.pricingDeadline) }}</text></view> | |
| 17 | + <view class="row"><text class="label">其他费用凭票到厂另行结算</text><text class="value">{{ boolText(form.otherExpenseSettlement) }}</text></view> | |
| 18 | + <view class="row"><text class="label">交提货方式</text><text class="value">{{ getDeliveryMethodLabel() }}</text></view> | |
| 19 | + <view class="row"><text class="label">是否通货买断</text><text class="value">{{ boolText(form.commodityBuyout) }}</text></view> | |
| 20 | + <view class="row"><text class="label">是否质量买断</text><text class="value">{{ boolText(form.qualityBuyout) }}</text></view> | |
| 21 | + <view class="row"><text class="label">是否数量买断</text><text class="value">{{ boolText(form.quantityBuyout) }}</text></view> | |
| 22 | + <view class="row"><text class="label">是否送货上门</text><text class="value">{{ boolText(form.doorDelivery) }}</text></view> | |
| 23 | + <view class="row"><text class="label">装车费</text><text class="value">{{ safeText(form.loadingFee) }}</text></view> | |
| 24 | + <view class="row"><text class="label">开票品种</text><text class="value">{{ safeText(form.invoiceProductName) }}</text></view> | |
| 25 | + <view class="row"><text class="label">交货日期</text><text class="value">{{ formatDateOnly(form.deliveryDate) }}</text></view> | |
| 26 | + <view class="row"><text class="label">结算方式</text><text class="value">{{ safeText(form.settlementMethod) }}</text></view> | |
| 27 | + <view class="row"><text class="label">结算比例(%)</text><text class="value">{{ safeText(form.settlementRatio) }}</text></view> | |
| 28 | + <view class="row"><text class="label">协调事由</text><text class="value">{{ safeText(form.coordinationReason) }}</text></view> | |
| 29 | + <view class="row"><text class="label">票类状况</text><text class="value">{{ safeText(form.invoiceTypeStatus) }}</text></view> | |
| 30 | + </view> | |
| 31 | + | |
| 32 | + <!-- 明细信息 --> | |
| 33 | + <ProductRel mode="view" :list="productLineList" /> | |
| 34 | + | |
| 35 | + <!-- 料质状况 --> | |
| 36 | + <view class="title-header"> | |
| 37 | + <image class="title-header_icon" src="/static/images/title.png" /> | |
| 38 | + <span>料质状况</span> | |
| 39 | + </view> | |
| 40 | + <view class="section"> | |
| 41 | + <view class="row"><text class="label">是否启用料质状况</text><text class="value">{{ boolText(form.materialConditionEnabled) }}</text></view> | |
| 42 | + <template v-if="form.materialConditionEnabled"> | |
| 43 | + <view v-for="mc in materialConditionConfigs" :key="mc.field" class="material-group"> | |
| 44 | + <text class="material-label">{{ mc.label }}</text> | |
| 45 | + <view class="tag-list"> | |
| 46 | + <view | |
| 47 | + v-for="opt in getDictOptions(mc.dictCode)" | |
| 48 | + :key="opt.code" | |
| 49 | + class="tag-btn" | |
| 50 | + :class="{ active: isTagSelected(mc.field, opt.code) }" | |
| 51 | + >{{ opt.name }}</view> | |
| 52 | + </view> | |
| 53 | + </view> | |
| 54 | + </template> | |
| 55 | + </view> | |
| 56 | + | |
| 57 | + <!-- 备注 --> | |
| 58 | + <view class="title-header"> | |
| 59 | + <image class="title-header_icon" src="/static/images/title.png" /> | |
| 60 | + <span>备注</span> | |
| 61 | + </view> | |
| 62 | + <view class="section"> | |
| 63 | + <text class="remark-text">{{ safeText(form.remark) }}</text> | |
| 64 | + </view> | |
| 65 | + </view> | |
| 66 | + </scroll-view> | |
| 67 | + <detail-buttons :buttons="displayButtons" @click="handleButtonClick" /> | |
| 68 | + </view> | |
| 69 | +</template> | |
| 70 | +<script> | |
| 71 | +import DetailButtons from '@/components/detail-buttons/index.vue' | |
| 72 | +import ProductRel from './productRel.vue' | |
| 73 | +import { salesmanQuotationDetailApi, salesmanQuotationCancelApi } from '@/api/procure-manage/salesmanQuotation.js' | |
| 74 | +import { getDicByCodes } from '@/utils/dic.js' | |
| 75 | +import { getInfo } from '@/api/login.js' | |
| 76 | +export default { | |
| 77 | + name: 'DetailSalesPrice', | |
| 78 | + components: { DetailButtons, ProductRel }, | |
| 79 | + data() { | |
| 80 | + return { | |
| 81 | + id: '', | |
| 82 | + form: {}, | |
| 83 | + productLineList: [], | |
| 84 | + materialConditionConfigs: [ | |
| 85 | + { field: 'materialConditionCopperRice', label: '铜米', dictCode: 'COPPER_RICE' }, | |
| 86 | + { field: 'materialConditionBrightCopper', label: '光亮铜', dictCode: 'BRIGHT_COPPER' }, | |
| 87 | + { field: 'materialConditionFirstClass', label: '一特', dictCode: 'FIRST_CLASS' }, | |
| 88 | + { field: 'materialConditionSecondClass', label: '二特', dictCode: 'SECOND_CLASS' }, | |
| 89 | + { field: 'materialConditionTinCoated', label: '镀锡紫', dictCode: 'TIN_COATED' }, | |
| 90 | + { field: 'materialConditionScrap', label: '角料', dictCode: 'SCRAP' }, | |
| 91 | + { field: 'materialConditionWhiteYellow', label: '镀白黄', dictCode: 'WHITE_YELLOW' }, | |
| 92 | + { field: 'materialConditionPhosphorCopper', label: '磷铜', dictCode: 'PHOSPHOR_COPPER' } | |
| 93 | + ], | |
| 94 | + dictData: {}, | |
| 95 | + buttons: [ | |
| 96 | + { text: '编辑', visible: true, variant: 'outline', event: 'edit' }, | |
| 97 | + { text: '复制', visible: true, variant: 'primary', event: 'copy' }, | |
| 98 | + { text: '取消', visible: true, variant: 'outline', event: 'cancel' }, | |
| 99 | + { text: '生成计划', visible: true, variant: 'outline', event: 'plan' }, | |
| 100 | + { text: '审核', visible: true, variant: 'primary', event: 'approve' }, | |
| 101 | + { text: '审核详情', visible: true, event: 'approveDetail' }, | |
| 102 | + ], | |
| 103 | + investigatorId: '', | |
| 104 | + } | |
| 105 | + }, | |
| 106 | + computed: { | |
| 107 | + displayButtons() { | |
| 108 | + const s = this.form.approvalStatus || '' | |
| 109 | + const c = this.form.createById || '' | |
| 110 | + const p = this.form.status || '' | |
| 111 | + return [ | |
| 112 | + { ...this.buttons[0], visible: (s === 'REFUSE' && c === this.investigatorId && this.$auth.hasPermi('procure-manage:sales-price:modify')) }, | |
| 113 | + { ...this.buttons[1], visible: this.$auth.hasPermi('procure-manage:sales-price:copy') }, | |
| 114 | + { ...this.buttons[2], visible: (s === 'REFUSE' && c === this.investigatorId && this.$auth.hasPermi('procure-manage:sales-price:cancel')) }, | |
| 115 | + { ...this.buttons[3], visible: (p === 'DEAL_CLOSED' && this.$auth.hasPermi('procure-manage:sales-price:create')) }, | |
| 116 | + { ...this.buttons[4], visible: (s === 'AUDIT' && this.form.showExamine && this.$auth.hasPermi('procure-manage:sales-price:approves')) }, | |
| 117 | + { ...this.buttons[5], visible: (this.form.showExamineDetail && this.$auth.hasPermi('procure-manage:sales-price:view')) }, | |
| 118 | + ] | |
| 119 | + } | |
| 120 | + }, | |
| 121 | + onLoad(query) { | |
| 122 | + this.id = (query && query.id) || '' | |
| 123 | + this.loadDictData() | |
| 124 | + this.loadDetail() | |
| 125 | + // 获取当前用户信息 | |
| 126 | + this.getUserInfo(); | |
| 127 | + }, | |
| 128 | + methods: { | |
| 129 | + // 获取当前用户信息 | |
| 130 | + async getUserInfo() { | |
| 131 | + getInfo() | |
| 132 | + .then((res) => { | |
| 133 | + this.investigatorId = res?.id || ''; | |
| 134 | + }) | |
| 135 | + .catch((err) => { | |
| 136 | + console.error('获取用户信息失败:', err); | |
| 137 | + }); | |
| 138 | + }, | |
| 139 | + async loadDictData() { | |
| 140 | + try { | |
| 141 | + const codes = [...this.materialConditionConfigs.map(mc => mc.dictCode), 'DELIVERY_METHOD'] | |
| 142 | + const res = await getDicByCodes(codes) | |
| 143 | + this.dictData = res || {} | |
| 144 | + } catch (e) { | |
| 145 | + this.dictData = {} | |
| 146 | + } | |
| 147 | + }, | |
| 148 | + getDictOptions(dictCode) { | |
| 149 | + const data = this.dictData[dictCode] | |
| 150 | + return (data && data.data) ? data.data : [] | |
| 151 | + }, | |
| 152 | + isTagSelected(field, code) { | |
| 153 | + const selected = this.form[field] | |
| 154 | + return Array.isArray(selected) && selected.includes(code) | |
| 155 | + }, | |
| 156 | + boolText(v) { | |
| 157 | + if (typeof v !== 'boolean') return '-' | |
| 158 | + return v ? '是' : '否' | |
| 159 | + }, | |
| 160 | + getDeliveryMethodLabel() { | |
| 161 | + const opts = this.getDictOptions('DELIVERY_METHOD') || [] | |
| 162 | + const m = opts.find(o => String(o.code != null ? o.code : o.value) === String(this.form.deliveryMode)) | |
| 163 | + return m ? (m.name || m.label || '-') : '-' | |
| 164 | + }, | |
| 165 | + formatDateOnly(v) { | |
| 166 | + const s = v == null ? '' : String(v).trim() | |
| 167 | + if (!s) return '-' | |
| 168 | + if (s.length >= 10) return s.slice(0, 10) | |
| 169 | + return s | |
| 170 | + }, | |
| 171 | + safeText(v) { | |
| 172 | + const s = v == null ? '' : String(v) | |
| 173 | + return s.trim() ? s : '-' | |
| 174 | + }, | |
| 175 | + async loadDetail() { | |
| 176 | + if (!this.id) return | |
| 177 | + try { | |
| 178 | + const res = await salesmanQuotationDetailApi({ id: this.id }) | |
| 179 | + const d = (res && res.data) ? res.data : {} | |
| 180 | + const materialFields = this.materialConditionConfigs.map(mc => mc.field) | |
| 181 | + materialFields.forEach(f => { | |
| 182 | + const v = d[f] | |
| 183 | + d[f] = (typeof v === 'string' && v) ? v.split(',').filter(Boolean) : (Array.isArray(v) ? v : []) | |
| 184 | + }) | |
| 185 | + this.form = d | |
| 186 | + this.productLineList = Array.isArray(d.quotationLineList) ? d.quotationLineList : (Array.isArray(d.salesmanQuotationLineList) ? d.salesmanQuotationLineList : []) | |
| 187 | + } catch (e) { | |
| 188 | + uni.showToast({ title: '加载详情失败', icon: 'none' }) | |
| 189 | + } | |
| 190 | + }, | |
| 191 | + handleButtonClick(btn) { | |
| 192 | + if (!btn || btn.disabled) return | |
| 193 | + const e = btn.event || '' | |
| 194 | + if (e === 'edit') return this.onEdit() | |
| 195 | + if (e === 'copy') return this.onCopy() | |
| 196 | + if (e === 'approve') return this.onApprove() | |
| 197 | + if (e === 'approveDetail') return this.onApproveDetail() | |
| 198 | + if (e === 'cancel') return this.onCancel() | |
| 199 | + }, | |
| 200 | + onEdit() { | |
| 201 | + uni.navigateTo({ url: '/pages/sales-price/modify?id=' + this.id }) | |
| 202 | + }, | |
| 203 | + onCopy() { | |
| 204 | + uni.navigateTo({ url: '/pages/sales-price/copy?id=' + this.id }) | |
| 205 | + }, | |
| 206 | + onCancel() { | |
| 207 | + const id = this.id || (this.form && this.form.id) || '' | |
| 208 | + if (!id) return | |
| 209 | + uni.showModal({ | |
| 210 | + title: '系统提示', | |
| 211 | + content: '是否确定取消该流程?', | |
| 212 | + confirmText: '确定', | |
| 213 | + cancelText: '取消', | |
| 214 | + success: (res) => { | |
| 215 | + if (res && res.confirm) { | |
| 216 | + salesmanQuotationCancelApi({ id }).then(() => { | |
| 217 | + uni.showToast({ title: '已取消', icon: 'none' }) | |
| 218 | + try { uni.setStorageSync('SALES_PRICE_LIST_NEED_REFRESH', '1') } catch (e) {} | |
| 219 | + setTimeout(() => { uni.redirectTo({ url: '/pages/supplier-price/index' }) }, 300) | |
| 220 | + }).catch((e) => { | |
| 221 | + uni.showToast({ title: (e && e.msg) || '取消失败', icon: 'none' }) | |
| 222 | + }) | |
| 223 | + } | |
| 224 | + } | |
| 225 | + }) | |
| 226 | + }, | |
| 227 | + onApprove() { | |
| 228 | + const id = this.id || (this.form && this.form.id) || '' | |
| 229 | + if (!id) return | |
| 230 | + const CACHE_KEY = 'sourceBusinessId' | |
| 231 | + const TYPE = 'contractType' | |
| 232 | + uni.setStorageSync(TYPE, 'SALESMAN_QUOTATION') | |
| 233 | + uni.setStorageSync(CACHE_KEY, id) | |
| 234 | + uni.navigateTo({ url: '/pages/flow/audit' }) | |
| 235 | + }, | |
| 236 | + onApproveDetail() { | |
| 237 | + const id = this.id || (this.form && this.form.id) || '' | |
| 238 | + if (!id) return | |
| 239 | + const CACHE_KEY = 'sourceBusinessId' | |
| 240 | + const TYPE = 'contractType' | |
| 241 | + uni.setStorageSync(TYPE, 'SALESMAN_QUOTATION') | |
| 242 | + uni.setStorageSync(CACHE_KEY, id) | |
| 243 | + uni.navigateTo({ url: '/pages/flow/audit_detail' }) | |
| 244 | + } | |
| 245 | + } | |
| 246 | +} | |
| 247 | +</script> | |
| 248 | +<style lang="scss" scoped> | |
| 249 | +.page { | |
| 250 | + display: flex; | |
| 251 | + flex-direction: column; | |
| 252 | + height: 100%; | |
| 253 | +} | |
| 254 | + | |
| 255 | +.scroll { | |
| 256 | + flex: 1; | |
| 257 | + padding: 8rpx 0 144rpx; | |
| 258 | +} | |
| 259 | + | |
| 260 | +.detail-page { | |
| 261 | + background: #f3f3f3; | |
| 262 | +} | |
| 263 | + | |
| 264 | +.title-header { | |
| 265 | + display: flex; | |
| 266 | + align-items: center; | |
| 267 | + padding: 24rpx 32rpx 12rpx; | |
| 268 | + background-color: #ffffff; | |
| 269 | + | |
| 270 | + .title-header_icon { | |
| 271 | + width: 36rpx; | |
| 272 | + height: 36rpx; | |
| 273 | + margin-right: 12rpx; | |
| 274 | + } | |
| 275 | + | |
| 276 | + span { | |
| 277 | + font-size: 34rpx; | |
| 278 | + font-weight: 600; | |
| 279 | + color: rgba(0, 0, 0, 0.9); | |
| 280 | + } | |
| 281 | +} | |
| 282 | + | |
| 283 | +.section { | |
| 284 | + padding: 32rpx; | |
| 285 | + background: #fff; | |
| 286 | + margin-bottom: 20rpx; | |
| 287 | +} | |
| 288 | + | |
| 289 | +.row { | |
| 290 | + display: flex; | |
| 291 | + margin-bottom: 28rpx; | |
| 292 | + | |
| 293 | + &:last-child { | |
| 294 | + margin-bottom: 0; | |
| 295 | + } | |
| 296 | + | |
| 297 | + &.company { | |
| 298 | + font-size: 36rpx; | |
| 299 | + font-weight: 600; | |
| 300 | + color: rgba(0, 0, 0, 0.9); | |
| 301 | + padding-top: 8rpx; | |
| 302 | + line-height: 50rpx; | |
| 303 | + } | |
| 304 | + | |
| 305 | + .label { | |
| 306 | + max-width: 420rpx; | |
| 307 | + margin-right: 20rpx; | |
| 308 | + line-height: 32rpx; | |
| 309 | + font-size: 28rpx; | |
| 310 | + color: rgba(0, 0, 0, 0.6); | |
| 311 | + } | |
| 312 | + | |
| 313 | + .value { | |
| 314 | + flex: 1; | |
| 315 | + line-height: 32rpx; | |
| 316 | + font-size: 28rpx; | |
| 317 | + color: rgba(0, 0, 0, 0.9); | |
| 318 | + text-align: right; | |
| 319 | + word-break: break-all; | |
| 320 | + } | |
| 321 | +} | |
| 322 | + | |
| 323 | +.line-block { | |
| 324 | + padding-bottom: 24rpx; | |
| 325 | + margin-bottom: 24rpx; | |
| 326 | + border-bottom: 1rpx solid #f0f0f0; | |
| 327 | + | |
| 328 | + &:last-child { | |
| 329 | + border-bottom: none; | |
| 330 | + margin-bottom: 0; | |
| 331 | + padding-bottom: 0; | |
| 332 | + } | |
| 333 | +} | |
| 334 | + | |
| 335 | +.material-group { | |
| 336 | + margin-top: 32rpx; | |
| 337 | + | |
| 338 | + &:first-child { | |
| 339 | + margin-top: 0; | |
| 340 | + } | |
| 341 | +} | |
| 342 | + | |
| 343 | +.material-label { | |
| 344 | + display: block; | |
| 345 | + font-size: 28rpx; | |
| 346 | + color: rgba(0, 0, 0, 0.6); | |
| 347 | + margin-bottom: 20rpx; | |
| 348 | +} | |
| 349 | + | |
| 350 | +.tag-list { | |
| 351 | + display: flex; | |
| 352 | + flex-wrap: wrap; | |
| 353 | + gap: 16rpx; | |
| 354 | +} | |
| 355 | + | |
| 356 | +.tag-btn { | |
| 357 | + padding: 12rpx 24rpx; | |
| 358 | + border: 1rpx solid #ddd; | |
| 359 | + border-radius: 8rpx; | |
| 360 | + font-size: 28rpx; | |
| 361 | + color: #999; | |
| 362 | + background: #f5f5f5; | |
| 363 | +} | |
| 364 | + | |
| 365 | +.tag-btn.active { | |
| 366 | + background: $theme-primary; | |
| 367 | + border-color: $theme-primary; | |
| 368 | + color: #fff; | |
| 369 | +} | |
| 370 | + | |
| 371 | +.remark-text { | |
| 372 | + font-size: 28rpx; | |
| 373 | + color: rgba(0, 0, 0, 0.9); | |
| 374 | + line-height: 1.6; | |
| 375 | + white-space: pre-wrap; | |
| 376 | +} | |
| 377 | + | |
| 378 | +.empty-tip { | |
| 379 | + text-align: center; | |
| 380 | + font-size: 28rpx; | |
| 381 | + color: rgba(0, 0, 0, 0.4); | |
| 382 | + padding: 24rpx 0; | |
| 383 | +} | |
| 384 | +</style> | ... | ... |
| ... | ... | @@ -6,6 +6,7 @@ |
| 6 | 6 | cancelButton="none" bgColor="#F3F3F3" textColor="rgba(0,0,0,0.4)" @confirm="search" |
| 7 | 7 | @input="onSearchInput" /> |
| 8 | 8 | <view class="tool-icons"> |
| 9 | + <image v-if="$auth.hasPermi('procure-manage:sales-price:add')" class="tool-icon" src="/static/images/dev_manage/add_icon.png" @click="onAdd" /> | |
| 9 | 10 | <image class="tool-icon" src="/static/images/dev_manage/filter_icon.png" @click="openFilter" /> |
| 10 | 11 | </view> |
| 11 | 12 | </view> |
| ... | ... | @@ -115,7 +116,10 @@ export default { |
| 115 | 116 | this.searchDebounceTimer = null |
| 116 | 117 | } |
| 117 | 118 | }, |
| 118 | - methods: { | |
| 119 | + methods: { | |
| 120 | + onAdd() { | |
| 121 | + uni.navigateTo({ url: '/pages/sales-price/add' }) | |
| 122 | + }, | |
| 119 | 123 | async loadDicData() { |
| 120 | 124 | try { |
| 121 | 125 | const results = await getDicByCodes(['AUDIT_STATUS']) | ... | ... |
pages/sales-price/modify.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="page"> | |
| 3 | + <scroll-view class="scroll" scroll-y> | |
| 4 | + <uni-list> | |
| 5 | + <!-- 编号 --> | |
| 6 | + <uni-list-item class="select-item" :class="form.code ? 'is-filled' : 'is-empty'"> | |
| 7 | + <template v-slot:body> | |
| 8 | + <view class="item-title"><text class="required">*</text><text>编号</text></view> | |
| 9 | + </template> | |
| 10 | + <template v-slot:footer> | |
| 11 | + <view class="serial-number-row"> | |
| 12 | + <text class="readonly-text value-code">{{ form.code }}</text> | |
| 13 | + </view> | |
| 14 | + </template> | |
| 15 | + </uni-list-item> | |
| 16 | + | |
| 17 | + <!-- 供应商名称 --> | |
| 18 | + <uni-list-item class="select-item" :class="form.supplierId ? 'is-filled' : 'is-empty'" clickable | |
| 19 | + @click="openSupplierSheet" :rightText="form.supplierName || '请选择供应商名称'" 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 | + <!-- 采购处(带出,禁用) --> | |
| 26 | + <uni-list-item title="采购处"> | |
| 27 | + <template v-slot:footer> | |
| 28 | + <uni-easyinput v-model="form.purchaseDepartmentName" :inputBorder="false" disabled placeholder="选择供应商后自动带出" /> | |
| 29 | + </template> | |
| 30 | + </uni-list-item> | |
| 31 | + | |
| 32 | + <!-- 区域(带出,禁用) --> | |
| 33 | + <uni-list-item title="区域"> | |
| 34 | + <template v-slot:footer> | |
| 35 | + <uni-easyinput v-model="form.regionName" :inputBorder="false" disabled placeholder="选择供应商后自动带出" /> | |
| 36 | + </template> | |
| 37 | + </uni-list-item> | |
| 38 | + | |
| 39 | + <!-- 报价时效 --> | |
| 40 | + <uni-list-item> | |
| 41 | + <template v-slot:body> | |
| 42 | + <view class="item-title"><text class="required">*</text><text>报价时效</text></view> | |
| 43 | + </template> | |
| 44 | + <template v-slot:footer> | |
| 45 | + <uni-easyinput v-model="form.validityPeriod" :inputBorder="false" placeholder="请输入报价时效" /> | |
| 46 | + </template> | |
| 47 | + </uni-list-item> | |
| 48 | + | |
| 49 | + <!-- 报价人(禁用) --> | |
| 50 | + <uni-list-item title="报价人"> | |
| 51 | + <template v-slot:footer> | |
| 52 | + <uni-easyinput v-model="form.quoterName" :inputBorder="false" disabled placeholder="自动获取" /> | |
| 53 | + </template> | |
| 54 | + </uni-list-item> | |
| 55 | + | |
| 56 | + <!-- 是否长单操作 --> | |
| 57 | + <uni-list-item class="select-item" :class="typeof form.longTermOperation === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 58 | + @click="openSheet('longTermOperation')" :rightText="boolText(form.longTermOperation)" showArrow> | |
| 59 | + <template v-slot:body> | |
| 60 | + <view class="item-title"><text class="required">*</text><text>是否长单操作</text></view> | |
| 61 | + </template> | |
| 62 | + </uni-list-item> | |
| 63 | + | |
| 64 | + <!-- 是否终端客户 --> | |
| 65 | + <uni-list-item class="select-item" :class="typeof form.terminalCustomer === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 66 | + @click="openSheet('terminalCustomer')" :rightText="boolText(form.terminalCustomer)" showArrow> | |
| 67 | + <template v-slot:body> | |
| 68 | + <view class="item-title"><text class="required">*</text><text>是否终端客户</text></view> | |
| 69 | + </template> | |
| 70 | + </uni-list-item> | |
| 71 | + | |
| 72 | + <!-- 是否未点价 --> | |
| 73 | + <uni-list-item class="select-item" :class="typeof form.priced === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 74 | + @click="openSheet('priced')" :rightText="boolText(form.priced)" showArrow> | |
| 75 | + <template v-slot:body> | |
| 76 | + <view class="item-title"><text class="required">*</text><text>是否未点价</text></view> | |
| 77 | + </template> | |
| 78 | + </uni-list-item> | |
| 79 | + | |
| 80 | + <!-- 点价截止日期 - 仅在 是否未点价 = 否 时显示且必填 --> | |
| 81 | + <uni-list-item v-if="form.priced === false" class="select-item" :class="form.pricingDeadline ? 'is-filled' : 'is-empty'"> | |
| 82 | + <template v-slot:body> | |
| 83 | + <view class="item-title"><text class="required">*</text><text>点价截止日期</text></view> | |
| 84 | + </template> | |
| 85 | + <template v-slot:footer> | |
| 86 | + <uni-datetime-picker type="date" v-model="form.pricingDeadline" /> | |
| 87 | + </template> | |
| 88 | + </uni-list-item> | |
| 89 | + | |
| 90 | + <!-- 其他费用凭票到厂另行结算 --> | |
| 91 | + <uni-list-item class="select-item" :class="typeof form.otherExpenseSettlement === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 92 | + @click="openSheet('otherExpenseSettlement')" :rightText="boolText(form.otherExpenseSettlement)" showArrow> | |
| 93 | + <template v-slot:body> | |
| 94 | + <view class="item-title"><text class="required">*</text><text>其他费用凭票到厂另行结算</text></view> | |
| 95 | + </template> | |
| 96 | + </uni-list-item> | |
| 97 | + | |
| 98 | + <!-- 交提货方式 --> | |
| 99 | + <uni-list-item class="select-item" :class="form.deliveryMode ? 'is-filled' : 'is-empty'" clickable | |
| 100 | + @click="openDictSheet('deliveryMode', 'DELIVERY_METHOD', '交提货方式')" :rightText="getDeliveryMethodLabel() || '请选择交提货方式'" showArrow> | |
| 101 | + <template v-slot:body> | |
| 102 | + <view class="item-title"><text class="required">*</text><text>交提货方式</text></view> | |
| 103 | + </template> | |
| 104 | + </uni-list-item> | |
| 105 | + | |
| 106 | + <!-- 是否通货买断 --> | |
| 107 | + <uni-list-item class="select-item" :class="typeof form.commodityBuyout === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 108 | + @click="openSheet('commodityBuyout')" :rightText="boolText(form.commodityBuyout)" showArrow> | |
| 109 | + <template v-slot:body> | |
| 110 | + <view class="item-title"><text class="required">*</text><text>是否通货买断</text></view> | |
| 111 | + </template> | |
| 112 | + </uni-list-item> | |
| 113 | + | |
| 114 | + <!-- 是否质量买断 --> | |
| 115 | + <uni-list-item class="select-item" :class="typeof form.qualityBuyout === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 116 | + @click="openSheet('qualityBuyout')" :rightText="boolText(form.qualityBuyout)" showArrow> | |
| 117 | + <template v-slot:body> | |
| 118 | + <view class="item-title"><text class="required">*</text><text>是否质量买断</text></view> | |
| 119 | + </template> | |
| 120 | + </uni-list-item> | |
| 121 | + | |
| 122 | + <!-- 是否数量买断 --> | |
| 123 | + <uni-list-item class="select-item" :class="typeof form.quantityBuyout === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 124 | + @click="openSheet('quantityBuyout')" :rightText="boolText(form.quantityBuyout)" showArrow> | |
| 125 | + <template v-slot:body> | |
| 126 | + <view class="item-title"><text class="required">*</text><text>是否数量买断</text></view> | |
| 127 | + </template> | |
| 128 | + </uni-list-item> | |
| 129 | + | |
| 130 | + <!-- 是否送货上门 --> | |
| 131 | + <uni-list-item class="select-item" :class="typeof form.doorDelivery === 'boolean' ? 'is-filled' : 'is-empty'" clickable | |
| 132 | + @click="openSheet('doorDelivery')" :rightText="boolText(form.doorDelivery)" showArrow> | |
| 133 | + <template v-slot:body> | |
| 134 | + <view class="item-title"><text class="required">*</text><text>是否送货上门</text></view> | |
| 135 | + </template> | |
| 136 | + </uni-list-item> | |
| 137 | + | |
| 138 | + <!-- 装车费 --> | |
| 139 | + <uni-list-item title="装车费"> | |
| 140 | + <template v-slot:footer> | |
| 141 | + <uni-easyinput v-model="form.loadingFee" type="digit" :inputBorder="false" placeholder="请输入装车费" /> | |
| 142 | + </template> | |
| 143 | + </uni-list-item> | |
| 144 | + | |
| 145 | + <!-- 开票品种 --> | |
| 146 | + <uni-list-item> | |
| 147 | + <template v-slot:body> | |
| 148 | + <view class="item-title"><text class="required">*</text><text>开票品种</text></view> | |
| 149 | + </template> | |
| 150 | + <template v-slot:footer> | |
| 151 | + <uni-easyinput v-model="form.invoiceProductName" :inputBorder="false" placeholder="请输入开票品种" /> | |
| 152 | + </template> | |
| 153 | + </uni-list-item> | |
| 154 | + | |
| 155 | + <!-- 交货日期 --> | |
| 156 | + <uni-list-item> | |
| 157 | + <template v-slot:body> | |
| 158 | + <view class="item-title"><text class="required">*</text><text>交货日期</text></view> | |
| 159 | + </template> | |
| 160 | + <template v-slot:footer> | |
| 161 | + <uni-datetime-picker type="date" v-model="form.deliveryDate" /> | |
| 162 | + </template> | |
| 163 | + </uni-list-item> | |
| 164 | + | |
| 165 | + <!-- 结算方式 --> | |
| 166 | + <uni-list-item> | |
| 167 | + <template v-slot:body> | |
| 168 | + <view class="item-title"><text class="required">*</text><text>结算方式</text></view> | |
| 169 | + </template> | |
| 170 | + <template v-slot:footer> | |
| 171 | + <uni-easyinput v-model="form.settlementMethod" :inputBorder="false" placeholder="请输入结算方式" /> | |
| 172 | + </template> | |
| 173 | + </uni-list-item> | |
| 174 | + | |
| 175 | + <!-- 结算比例(%) --> | |
| 176 | + <uni-list-item> | |
| 177 | + <template v-slot:body> | |
| 178 | + <view class="item-title"><text class="required">*</text><text>结算比例(%)</text></view> | |
| 179 | + </template> | |
| 180 | + <template v-slot:footer> | |
| 181 | + <uni-easyinput v-model="form.settlementRatio" type="digit" :inputBorder="false" placeholder="请输入结算比例" @blur="onRatioBlur" /> | |
| 182 | + </template> | |
| 183 | + </uni-list-item> | |
| 184 | + | |
| 185 | + <!-- 协调事由 --> | |
| 186 | + <uni-list-item title="协调事由"> | |
| 187 | + <template v-slot:footer> | |
| 188 | + <uni-easyinput v-model="form.coordinationReason" :inputBorder="false" placeholder="请输入协调事由" disabled /> | |
| 189 | + </template> | |
| 190 | + </uni-list-item> | |
| 191 | + | |
| 192 | + <!-- 票类状况 --> | |
| 193 | + <uni-list-item title="票类状况"> | |
| 194 | + <template v-slot:footer> | |
| 195 | + <uni-easyinput v-model="form.invoiceTypeStatus" :inputBorder="false" placeholder="请输入票类状况" disabled /> | |
| 196 | + </template> | |
| 197 | + </uni-list-item> | |
| 198 | + | |
| 199 | + <!-- 明细信息 --> | |
| 200 | + <ProductRel ref="productRel" mode="edit" :options="productList" :priced="form.priced" :list="productLineList" @change="onProductsChange" /> | |
| 201 | + </uni-list> | |
| 202 | + | |
| 203 | + <!-- 料质状况模块(明细下方) --> | |
| 204 | + <view class="material-condition-section"> | |
| 205 | + <view class="section-header"> | |
| 206 | + <image class="opCollapse" src="/static/images/title.png" /> | |
| 207 | + <text class="section-title">料质状况</text> | |
| 208 | + </view> | |
| 209 | + <view class="section-content"> | |
| 210 | + <view class="form-row"> | |
| 211 | + <text class="form-label">是否启用料质状况</text> | |
| 212 | + <view class="bool-btns"> | |
| 213 | + <view class="bool-btn" :class="{ active: form.materialConditionEnabled === true }" @click="form.materialConditionEnabled = true">是</view> | |
| 214 | + <view class="bool-btn" :class="{ active: form.materialConditionEnabled === false }" @click="form.materialConditionEnabled = false">否</view> | |
| 215 | + </view> | |
| 216 | + </view> | |
| 217 | + | |
| 218 | + <template v-if="form.materialConditionEnabled"> | |
| 219 | + <view v-for="mc in materialConditionConfigs" :key="mc.field" class="material-group"> | |
| 220 | + <text class="material-label">{{ mc.label }}</text> | |
| 221 | + <view class="tag-list"> | |
| 222 | + <view | |
| 223 | + v-for="opt in getDictOptions(mc.dictCode)" | |
| 224 | + :key="opt.code" | |
| 225 | + class="tag-btn" | |
| 226 | + :class="{ active: isTagSelected(mc.field, opt.code) }" | |
| 227 | + @click="toggleTag(mc.field, opt.code)" | |
| 228 | + > | |
| 229 | + {{ opt.name }} | |
| 230 | + </view> | |
| 231 | + </view> | |
| 232 | + </view> | |
| 233 | + </template> | |
| 234 | + </view> | |
| 235 | + </view> | |
| 236 | + | |
| 237 | + <!-- 备注模块 --> | |
| 238 | + <view class="remark-section"> | |
| 239 | + <view class="section-header"> | |
| 240 | + <image class="opCollapse" src="/static/images/title.png" /> | |
| 241 | + <text class="section-title">备注</text> | |
| 242 | + </view> | |
| 243 | + <view class="section-content"> | |
| 244 | + <uni-easyinput v-model="form.remark" type="textarea" :inputBorder="false" placeholder="请输入备注" maxlength="2000" /> | |
| 245 | + </view> | |
| 246 | + </view> | |
| 247 | + | |
| 248 | + <view class="footer"> | |
| 249 | + <button class="btn submit" type="primary" @click="onSubmit">保存</button> | |
| 250 | + </view> | |
| 251 | + </scroll-view> | |
| 252 | + | |
| 253 | + <SingleSelectSheet :visible.sync="sheet.visible" :title="sheet.title" :options="sheet.options" | |
| 254 | + v-model="sheet.value" @confirm="onSheetConfirm" /> | |
| 255 | + | |
| 256 | + <RelateSelectSheet :visible.sync="supplierSheet.visible" :title="supplierSheet.title" | |
| 257 | + :fetchFn="supplierSheet.fetchFn" :display-fields="supplierSheet.displayFields" | |
| 258 | + :multiple="false" row-key="id" :selectedKeys.sync="supplierSheet.selectedKeys" | |
| 259 | + @confirm="onSupplierConfirm" /> | |
| 260 | + </view> | |
| 261 | +</template> | |
| 262 | +<script> | |
| 263 | +import SingleSelectSheet from '@/components/single-select/index.vue' | |
| 264 | +import RelateSelectSheet from '@/components/relate-select/index.vue' | |
| 265 | +import ProductRel from './productRel.vue' | |
| 266 | +import { salesmanQuotationDetailApi, salesmanQuotationUpdateApi, breedRelationshipQueryApi } from '@/api/procure-manage/salesmanQuotation.js' | |
| 267 | +import { domesticCustomerCreditQueryApi } from '@/api/procure-manage/domesticCustomerCredit.js' | |
| 268 | +import { getDicByCodes } from '@/utils/dic.js' | |
| 269 | + | |
| 270 | +export default { | |
| 271 | + name: 'ModifySalesPrice', | |
| 272 | + components: { SingleSelectSheet, RelateSelectSheet, ProductRel }, | |
| 273 | + data() { | |
| 274 | + return { | |
| 275 | + id: '', | |
| 276 | + form: { | |
| 277 | + code: '', | |
| 278 | + supplierId: '', | |
| 279 | + supplierName: '', | |
| 280 | + purchaseDepartmentName: '', | |
| 281 | + regionName: '', | |
| 282 | + validityPeriod: '', | |
| 283 | + quoterName: '', | |
| 284 | + longTermOperation: false, | |
| 285 | + terminalCustomer: false, | |
| 286 | + priced: true, | |
| 287 | + pricingDeadline: '', | |
| 288 | + otherExpenseSettlement: false, | |
| 289 | + deliveryMode: '', | |
| 290 | + commodityBuyout: false, | |
| 291 | + qualityBuyout: false, | |
| 292 | + quantityBuyout: false, | |
| 293 | + doorDelivery: false, | |
| 294 | + loadingFee: '', | |
| 295 | + invoiceProductName: '', | |
| 296 | + deliveryDate: '', | |
| 297 | + settlementMethod: '', | |
| 298 | + settlementRatio: '', | |
| 299 | + coordinationReason: '', | |
| 300 | + invoiceTypeStatus: '', | |
| 301 | + materialConditionEnabled: false, | |
| 302 | + materialConditionCopperRice: [], | |
| 303 | + materialConditionBrightCopper: [], | |
| 304 | + materialConditionFirstClass: [], | |
| 305 | + materialConditionSecondClass: [], | |
| 306 | + materialConditionTinCoated: [], | |
| 307 | + materialConditionScrap: [], | |
| 308 | + materialConditionWhiteYellow: [], | |
| 309 | + materialConditionPhosphorCopper: [], | |
| 310 | + remark: '' | |
| 311 | + }, | |
| 312 | + productList: [], | |
| 313 | + productLineList: [], | |
| 314 | + sheet: { visible: false, title: '请选择', options: [], value: '', field: '' }, | |
| 315 | + supplierSheet: { | |
| 316 | + visible: false, | |
| 317 | + title: '选择供应商', | |
| 318 | + fetchFn: null, | |
| 319 | + displayFields: [ | |
| 320 | + { label: '单位名称', field: 'unitName' }, | |
| 321 | + { label: '采购处', field: 'purchaseDepartmentName' }, | |
| 322 | + { label: '区域', field: 'regionName' } | |
| 323 | + ], | |
| 324 | + selectedKeys: [] | |
| 325 | + }, | |
| 326 | + materialConditionConfigs: [ | |
| 327 | + { field: 'materialConditionCopperRice', label: '铜米', dictCode: 'COPPER_RICE' }, | |
| 328 | + { field: 'materialConditionBrightCopper', label: '光亮铜', dictCode: 'BRIGHT_COPPER' }, | |
| 329 | + { field: 'materialConditionFirstClass', label: '一特', dictCode: 'FIRST_CLASS' }, | |
| 330 | + { field: 'materialConditionSecondClass', label: '二特', dictCode: 'SECOND_CLASS' }, | |
| 331 | + { field: 'materialConditionTinCoated', label: '镀锡紫', dictCode: 'TIN_COATED' }, | |
| 332 | + { field: 'materialConditionScrap', label: '角料', dictCode: 'SCRAP' }, | |
| 333 | + { field: 'materialConditionWhiteYellow', label: '镀白黄', dictCode: 'WHITE_YELLOW' }, | |
| 334 | + { field: 'materialConditionPhosphorCopper', label: '磷铜', dictCode: 'PHOSPHOR_COPPER' } | |
| 335 | + ], | |
| 336 | + dictData: {} | |
| 337 | + } | |
| 338 | + }, | |
| 339 | + onLoad(query) { | |
| 340 | + this.id = (query && query.id) || '' | |
| 341 | + this.supplierSheet.fetchFn = this.fetchSuppliers.bind(this) | |
| 342 | + this.loadProducts() | |
| 343 | + this.loadDictData() | |
| 344 | + this.loadDetail() | |
| 345 | + }, | |
| 346 | + methods: { | |
| 347 | + async loadDictData() { | |
| 348 | + try { | |
| 349 | + const codes = [...this.materialConditionConfigs.map(mc => mc.dictCode), 'DELIVERY_METHOD'] | |
| 350 | + const res = await getDicByCodes(codes) | |
| 351 | + this.dictData = res || {} | |
| 352 | + } catch (e) { | |
| 353 | + this.dictData = {} | |
| 354 | + } | |
| 355 | + }, | |
| 356 | + getDictOptions(dictCode) { | |
| 357 | + const data = this.dictData[dictCode] | |
| 358 | + return (data && data.data) ? data.data : [] | |
| 359 | + }, | |
| 360 | + isTagSelected(field, code) { | |
| 361 | + const selected = this.form[field] | |
| 362 | + return Array.isArray(selected) && selected.includes(code) | |
| 363 | + }, | |
| 364 | + toggleTag(field, code) { | |
| 365 | + let selected = this.form[field] | |
| 366 | + if (!Array.isArray(selected)) { | |
| 367 | + selected = [] | |
| 368 | + } | |
| 369 | + const index = selected.indexOf(code) | |
| 370 | + if (index > -1) { | |
| 371 | + selected.splice(index, 1) | |
| 372 | + } else { | |
| 373 | + selected.push(code) | |
| 374 | + } | |
| 375 | + this.$set(this.form, field, [...selected]) | |
| 376 | + }, | |
| 377 | + async loadProducts() { | |
| 378 | + try { | |
| 379 | + const res = await breedRelationshipQueryApi({ pageIndex: 1, pageSize: 9999 }) | |
| 380 | + const _data = res && res.data ? res.data : {} | |
| 381 | + const list = _data.datas || _data.list || _data.records || [] | |
| 382 | + this.productList = list.map(it => ({ | |
| 383 | + label: it.productName || it.name || '', | |
| 384 | + value: it.productId || it.id || '', | |
| 385 | + lossRate: it.fixedLossRate != null ? it.fixedLossRate : (it.lossRate != null ? it.lossRate : (it.attritionRate != null ? it.attritionRate : '')) | |
| 386 | + })) | |
| 387 | + } catch (e) { | |
| 388 | + this.productList = [] | |
| 389 | + } | |
| 390 | + }, | |
| 391 | + async loadDetail() { | |
| 392 | + if (!this.id) return | |
| 393 | + try { | |
| 394 | + const res = await salesmanQuotationDetailApi({ id: this.id }) | |
| 395 | + const d = (res && res.data) ? res.data : {} | |
| 396 | + const materialFields = this.materialConditionConfigs.map(mc => mc.field) | |
| 397 | + materialFields.forEach(f => { | |
| 398 | + const v = d[f] | |
| 399 | + d[f] = (typeof v === 'string' && v) ? v.split(',').filter(Boolean) : (Array.isArray(v) ? v : []) | |
| 400 | + }) | |
| 401 | + this.form = { ...this.form, ...d } | |
| 402 | + this.productLineList = Array.isArray(d.quotationLineList) ? d.quotationLineList : (Array.isArray(d.salesmanQuotationLineList) ? d.salesmanQuotationLineList : []) | |
| 403 | + } catch (e) { | |
| 404 | + uni.showToast({ title: '加载详情失败', icon: 'none' }) | |
| 405 | + } | |
| 406 | + }, | |
| 407 | + fetchSuppliers({ pageIndex, pageSize, extra }) { | |
| 408 | + const name = (extra && (extra.name || extra.keyword || extra.key)) || '' | |
| 409 | + const params = { | |
| 410 | + pageIndex, | |
| 411 | + pageSize, | |
| 412 | + available: true, | |
| 413 | + unitName: name, | |
| 414 | + status: 'PASS', | |
| 415 | + freeze: false | |
| 416 | + } | |
| 417 | + return domesticCustomerCreditQueryApi(params).then(res => { | |
| 418 | + const _data = res && res.data ? res.data : {} | |
| 419 | + const records = _data.datas || _data.list || _data.records || [] | |
| 420 | + const totalCount = _data.totalCount || _data.count || 0 | |
| 421 | + const hasNext = _data.hasNext || false | |
| 422 | + return { records, totalCount, hasNext } | |
| 423 | + }).catch(() => ({ records: [], totalCount: 0, hasNext: false })) | |
| 424 | + }, | |
| 425 | + openSupplierSheet() { | |
| 426 | + this.supplierSheet.selectedKeys = this.form.supplierId ? [this.form.supplierId] : [] | |
| 427 | + this.supplierSheet.visible = true | |
| 428 | + }, | |
| 429 | + onSupplierConfirm({ items }) { | |
| 430 | + const first = (items && items.length > 0) ? items[0] : null | |
| 431 | + if (!first) return | |
| 432 | + this.form.supplierId = first.id || '' | |
| 433 | + this.form.supplierName = first.unitName || '' | |
| 434 | + this.form.purchaseDepartmentName = first.purchaseDepartmentName || '' | |
| 435 | + this.form.regionName = first.regionName || '' | |
| 436 | + }, | |
| 437 | + boolText(v) { | |
| 438 | + if (typeof v !== 'boolean') return '请选择' | |
| 439 | + return v ? '是' : '否' | |
| 440 | + }, | |
| 441 | + openSheet(field) { | |
| 442 | + const opts = [{ label: '是', value: true }, { label: '否', value: false }] | |
| 443 | + const cur = this.form[field] | |
| 444 | + const match = opts.find(o => o.value === cur) | |
| 445 | + const titleMap = { | |
| 446 | + longTermOperation: '是否长单操作', | |
| 447 | + terminalCustomer: '是否终端客户', | |
| 448 | + priced: '是否未点价', | |
| 449 | + otherExpenseSettlement: '其他费用凭票到厂另行结算', | |
| 450 | + commodityBuyout: '是否通货买断', | |
| 451 | + qualityBuyout: '是否质量买断', | |
| 452 | + quantityBuyout: '是否数量买断', | |
| 453 | + doorDelivery: '是否送货上门' | |
| 454 | + } | |
| 455 | + this.sheet = { visible: true, title: titleMap[field] || '请选择', options: opts, value: match ? match.value : '', field, isDict: false } | |
| 456 | + }, | |
| 457 | + openDictSheet(field, dictCode, title) { | |
| 458 | + const raw = this.getDictOptions(dictCode) || [] | |
| 459 | + const opts = raw.map(o => ({ label: o.name || o.label || '', value: o.code != null ? o.code : (o.value != null ? o.value : '') })) | |
| 460 | + const cur = this.form[field] | |
| 461 | + const match = opts.find(o => String(o.value) === String(cur)) | |
| 462 | + this.sheet = { visible: true, title: title || '请选择', options: opts, value: match ? match.value : '', field, isDict: true } | |
| 463 | + }, | |
| 464 | + getDeliveryMethodLabel() { | |
| 465 | + const opts = this.getDictOptions('DELIVERY_METHOD') || [] | |
| 466 | + const m = opts.find(o => String(o.code != null ? o.code : o.value) === String(this.form.deliveryMode)) | |
| 467 | + return m ? (m.name || m.label || '') : '' | |
| 468 | + }, | |
| 469 | + onSheetConfirm({ value }) { | |
| 470 | + const field = this.sheet.field | |
| 471 | + if (!field) return | |
| 472 | + if (this.sheet.isDict) { | |
| 473 | + this.form[field] = value | |
| 474 | + } else { | |
| 475 | + this.form[field] = !!value | |
| 476 | + if (field === 'priced' && value === true) { | |
| 477 | + this.form.pricingDeadline = '' | |
| 478 | + } | |
| 479 | + } | |
| 480 | + }, | |
| 481 | + onRatioBlur() { | |
| 482 | + let v = parseFloat(this.form.settlementRatio) | |
| 483 | + if (isNaN(v)) v = '' | |
| 484 | + else v = Math.round(v * 100) / 100 | |
| 485 | + this.form.settlementRatio = v | |
| 486 | + }, | |
| 487 | + onProductsChange(products) { | |
| 488 | + this.productLineList = Array.isArray(products) ? products : [] | |
| 489 | + }, | |
| 490 | + validateRequired() { | |
| 491 | + const stringChecks = [ | |
| 492 | + { key: 'code', msg: '请输入编号' }, | |
| 493 | + { key: 'supplierId', msg: '请选择供应商' }, | |
| 494 | + { key: 'validityPeriod', msg: '请输入报价时效' }, | |
| 495 | + { key: 'deliveryMode', msg: '请选择交提货方式' }, | |
| 496 | + { key: 'invoiceProductName', msg: '请输入开票品种' }, | |
| 497 | + { key: 'deliveryDate', msg: '请选择交货日期' }, | |
| 498 | + { key: 'settlementMethod', msg: '请输入结算方式' }, | |
| 499 | + { key: 'settlementRatio', msg: '请输入结算比例' } | |
| 500 | + ] | |
| 501 | + for (const it of stringChecks) { | |
| 502 | + const val = this.form[it.key] | |
| 503 | + const empty = (val === undefined || val === null || (typeof val === 'string' && val.trim() === '')) | |
| 504 | + if (empty) { uni.showToast({ title: it.msg, icon: 'none' }); return false } | |
| 505 | + } | |
| 506 | + const boolChecks = [ | |
| 507 | + { key: 'longTermOperation', msg: '请选择是否长单操作' }, | |
| 508 | + { key: 'terminalCustomer', msg: '请选择是否终端客户' }, | |
| 509 | + { key: 'priced', msg: '请选择是否未点价' }, | |
| 510 | + { key: 'otherExpenseSettlement', msg: '请选择其他费用凭票到厂另行结算' }, | |
| 511 | + { key: 'commodityBuyout', msg: '请选择是否通货买断' }, | |
| 512 | + { key: 'qualityBuyout', msg: '请选择是否质量买断' }, | |
| 513 | + { key: 'quantityBuyout', msg: '请选择是否数量买断' }, | |
| 514 | + { key: 'doorDelivery', msg: '请选择是否送货上门' } | |
| 515 | + ] | |
| 516 | + for (const it of boolChecks) { | |
| 517 | + if (typeof this.form[it.key] !== 'boolean') { | |
| 518 | + uni.showToast({ title: it.msg, icon: 'none' }) | |
| 519 | + return false | |
| 520 | + } | |
| 521 | + } | |
| 522 | + if (this.form.priced === false && !this.form.pricingDeadline) { | |
| 523 | + uni.showToast({ title: '请选择点价截止日期', icon: 'none' }) | |
| 524 | + return false | |
| 525 | + } | |
| 526 | + const list = Array.isArray(this.productLineList) ? this.productLineList : [] | |
| 527 | + if (list.length === 0) { | |
| 528 | + uni.showToast({ title: '请添加产品明细', icon: 'none' }) | |
| 529 | + return false | |
| 530 | + } | |
| 531 | + if (this.$refs.productRel && !this.$refs.productRel.validate(this.form.priced)) return false | |
| 532 | + return true | |
| 533 | + }, | |
| 534 | + async onSubmit() { | |
| 535 | + if (!this.validateRequired()) return | |
| 536 | + const confirmRes = await new Promise(resolve => { | |
| 537 | + uni.showModal({ title: '提示', content: '确定保存修改吗?', confirmText: '确定', cancelText: '取消', success: resolve }) | |
| 538 | + }) | |
| 539 | + if (!(confirmRes && confirmRes.confirm)) return | |
| 540 | + const clean = (obj) => { | |
| 541 | + const out = {} | |
| 542 | + Object.keys(obj || {}).forEach(k => { | |
| 543 | + const v = obj[k] | |
| 544 | + const isEmptyString = typeof v === 'string' && v.trim() === '' | |
| 545 | + const isUndef = v === undefined || v === null | |
| 546 | + const isNaNNumber = typeof v === 'number' && isNaN(v) | |
| 547 | + if (!(isEmptyString || isUndef || isNaNNumber)) out[k] = v | |
| 548 | + }) | |
| 549 | + return out | |
| 550 | + } | |
| 551 | + const lines = (this.productLineList || []).map(it => clean(it)) | |
| 552 | + const materialFields = this.materialConditionConfigs.map(mc => mc.field) | |
| 553 | + const formCopy = { ...this.form } | |
| 554 | + materialFields.forEach(f => { | |
| 555 | + const arr = formCopy[f] | |
| 556 | + formCopy[f] = Array.isArray(arr) ? arr.join(',') : (arr || '') | |
| 557 | + }) | |
| 558 | + const payload = clean({ | |
| 559 | + ...formCopy, | |
| 560 | + id: this.id, | |
| 561 | + quotationLineList: lines | |
| 562 | + }) | |
| 563 | + try { | |
| 564 | + await salesmanQuotationUpdateApi(payload) | |
| 565 | + uni.showToast({ title: '保存成功', icon: 'none' }) | |
| 566 | + try { uni.setStorageSync('SALES_PRICE_LIST_NEED_REFRESH', '1') } catch (e) {} | |
| 567 | + setTimeout(() => { uni.redirectTo({ url: '/pages/sales-price/index' }) }, 400) | |
| 568 | + } catch (e) { | |
| 569 | + uni.showToast({ title: e.msg || '保存失败', icon: 'none' }) | |
| 570 | + } | |
| 571 | + } | |
| 572 | + } | |
| 573 | +} | |
| 574 | +</script> | |
| 575 | +<style lang="scss" scoped> | |
| 576 | +.page { | |
| 577 | + display: flex; | |
| 578 | + flex-direction: column; | |
| 579 | + height: 100%; | |
| 580 | +} | |
| 581 | + | |
| 582 | +.scroll { | |
| 583 | + flex: 1; | |
| 584 | + padding: 12rpx 0 392rpx !important; | |
| 585 | +} | |
| 586 | + | |
| 587 | +.footer { | |
| 588 | + z-index: 2; | |
| 589 | + position: fixed; | |
| 590 | + left: 0; | |
| 591 | + right: 0; | |
| 592 | + bottom: 0; | |
| 593 | + padding: 32rpx; | |
| 594 | + padding-bottom: calc(32rpx + env(safe-area-inset-bottom)); | |
| 595 | + background: #fff; | |
| 596 | + box-shadow: 0 -8rpx 24rpx rgba(0, 0, 0, 0.06); | |
| 597 | + | |
| 598 | + .btn { | |
| 599 | + height: 80rpx; | |
| 600 | + line-height: 80rpx; | |
| 601 | + border-radius: 12rpx; | |
| 602 | + font-size: 32rpx; | |
| 603 | + } | |
| 604 | + | |
| 605 | + .submit { | |
| 606 | + background: $theme-primary; | |
| 607 | + color: #fff; | |
| 608 | + } | |
| 609 | +} | |
| 610 | + | |
| 611 | +::v-deep .uni-list { | |
| 612 | + .uni-easyinput { | |
| 613 | + display: flex; | |
| 614 | + | |
| 615 | + .uni-input-input { | |
| 616 | + color: rgba(0, 0, 0, 0.9); | |
| 617 | + } | |
| 618 | + } | |
| 619 | + | |
| 620 | + .uni-input-placeholder { | |
| 621 | + z-index: 1; | |
| 622 | + } | |
| 623 | + | |
| 624 | + .uni-input-input { | |
| 625 | + background-color: #ffffff; | |
| 626 | + } | |
| 627 | + | |
| 628 | + background: transparent; | |
| 629 | + | |
| 630 | + &-item { | |
| 631 | + &__extra-text { | |
| 632 | + font-size: 32rpx; | |
| 633 | + } | |
| 634 | + | |
| 635 | + &__content-title { | |
| 636 | + font-size: 32rpx; | |
| 637 | + color: rgba(0, 0, 0, 0.9); | |
| 638 | + } | |
| 639 | + | |
| 640 | + &__container { | |
| 641 | + padding: 32rpx; | |
| 642 | + | |
| 643 | + .uni-easyinput { | |
| 644 | + &__placeholder-class { | |
| 645 | + font-size: 32rpx; | |
| 646 | + color: rgba(0, 0, 0, 0.4); | |
| 647 | + } | |
| 648 | + | |
| 649 | + &__content { | |
| 650 | + border: none; | |
| 651 | + background-color: #ffffff !important; | |
| 652 | + | |
| 653 | + &-input { | |
| 654 | + padding-left: 0 !important; | |
| 655 | + height: 48rpx; | |
| 656 | + line-height: 48rpx; | |
| 657 | + font-size: 32rpx; | |
| 658 | + } | |
| 659 | + | |
| 660 | + .content-clear-icon { | |
| 661 | + font-size: 44rpx !important; | |
| 662 | + } | |
| 663 | + } | |
| 664 | + } | |
| 665 | + | |
| 666 | + .item-title, | |
| 667 | + .uni-list-item__content { | |
| 668 | + flex: none; | |
| 669 | + min-height: 48rpx; | |
| 670 | + line-height: 48rpx; | |
| 671 | + font-size: 32rpx; | |
| 672 | + position: relative; | |
| 673 | + width: 240rpx; | |
| 674 | + margin-right: 32rpx; | |
| 675 | + color: rgba(0, 0, 0, 0.9); | |
| 676 | + | |
| 677 | + .required { | |
| 678 | + color: red; | |
| 679 | + position: absolute; | |
| 680 | + top: 50%; | |
| 681 | + transform: translateY(-50%); | |
| 682 | + left: -16rpx; | |
| 683 | + } | |
| 684 | + } | |
| 685 | + } | |
| 686 | + | |
| 687 | + &.select-item { | |
| 688 | + &.is-empty { | |
| 689 | + .uni-list-item__extra-text { | |
| 690 | + color: rgba(0, 0, 0, 0.4) !important; | |
| 691 | + } | |
| 692 | + } | |
| 693 | + | |
| 694 | + &.is-filled { | |
| 695 | + .uni-list-item__extra-text { | |
| 696 | + color: rgba(0, 0, 0, 0.9) !important; | |
| 697 | + } | |
| 698 | + } | |
| 699 | + | |
| 700 | + .serial-number-row { | |
| 701 | + display: flex; | |
| 702 | + align-items: center; | |
| 703 | + | |
| 704 | + .generate-btn { | |
| 705 | + margin-left: 24rpx; | |
| 706 | + height: 48rpx; | |
| 707 | + line-height: 48rpx; | |
| 708 | + padding: 0 24rpx; | |
| 709 | + font-size: 24rpx; | |
| 710 | + border-radius: 8rpx; | |
| 711 | + background: $theme-primary; | |
| 712 | + color: #fff; | |
| 713 | + } | |
| 714 | + } | |
| 715 | + } | |
| 716 | + } | |
| 717 | +} | |
| 718 | + | |
| 719 | +.readonly-text { | |
| 720 | + color: rgba(0, 0, 0, 0.9); | |
| 721 | + font-size: 32rpx; | |
| 722 | + line-height: 48rpx; | |
| 723 | + text-align: right; | |
| 724 | + white-space: pre-wrap; | |
| 725 | + word-break: break-all; | |
| 726 | +} | |
| 727 | + | |
| 728 | +.value-code { | |
| 729 | + width: 260rpx; | |
| 730 | + text-align: left; | |
| 731 | +} | |
| 732 | + | |
| 733 | +.material-condition-section, | |
| 734 | +.remark-section { | |
| 735 | + margin-top: 20rpx; | |
| 736 | + background: #fff; | |
| 737 | +} | |
| 738 | + | |
| 739 | +.section-header { | |
| 740 | + display: flex; | |
| 741 | + align-items: center; | |
| 742 | + padding: 24rpx 32rpx; | |
| 743 | + border-bottom: 1rpx solid #f0f0f0; | |
| 744 | +} | |
| 745 | + | |
| 746 | +.section-title { | |
| 747 | + font-size: 32rpx; | |
| 748 | + color: rgba(0, 0, 0, 0.9); | |
| 749 | + font-weight: 600; | |
| 750 | +} | |
| 751 | + | |
| 752 | +.opCollapse { | |
| 753 | + width: 32rpx; | |
| 754 | + height: 28rpx; | |
| 755 | + margin-right: 16rpx; | |
| 756 | + margin-top: 8rpx; | |
| 757 | +} | |
| 758 | + | |
| 759 | +.section-content { | |
| 760 | + padding: 32rpx; | |
| 761 | +} | |
| 762 | + | |
| 763 | +.form-row { | |
| 764 | + display: flex; | |
| 765 | + align-items: center; | |
| 766 | + justify-content: space-between; | |
| 767 | + margin-bottom: 24rpx; | |
| 768 | +} | |
| 769 | + | |
| 770 | +.form-row:last-child { | |
| 771 | + margin-bottom: 0; | |
| 772 | +} | |
| 773 | + | |
| 774 | +.form-label { | |
| 775 | + font-size: 32rpx; | |
| 776 | + color: rgba(0, 0, 0, 0.9); | |
| 777 | +} | |
| 778 | + | |
| 779 | +.bool-btns { | |
| 780 | + display: flex; | |
| 781 | + gap: 20rpx; | |
| 782 | +} | |
| 783 | + | |
| 784 | +.bool-btn { | |
| 785 | + padding: 12rpx 40rpx; | |
| 786 | + border: 1rpx solid #ddd; | |
| 787 | + border-radius: 8rpx; | |
| 788 | + font-size: 28rpx; | |
| 789 | + color: #666; | |
| 790 | + background: #fff; | |
| 791 | +} | |
| 792 | + | |
| 793 | +.bool-btn.active { | |
| 794 | + background: $theme-primary; | |
| 795 | + border-color: $theme-primary; | |
| 796 | + color: #fff; | |
| 797 | +} | |
| 798 | + | |
| 799 | +.material-group { | |
| 800 | + margin-top: 32rpx; | |
| 801 | +} | |
| 802 | + | |
| 803 | +.material-label { | |
| 804 | + display: block; | |
| 805 | + font-size: 30rpx; | |
| 806 | + color: rgba(0, 0, 0, 0.9); | |
| 807 | + margin-bottom: 20rpx; | |
| 808 | +} | |
| 809 | + | |
| 810 | +.tag-list { | |
| 811 | + display: flex; | |
| 812 | + flex-wrap: wrap; | |
| 813 | + gap: 16rpx; | |
| 814 | +} | |
| 815 | + | |
| 816 | +.tag-btn { | |
| 817 | + padding: 12rpx 24rpx; | |
| 818 | + border: 1rpx solid #ddd; | |
| 819 | + border-radius: 8rpx; | |
| 820 | + font-size: 28rpx; | |
| 821 | + color: #666; | |
| 822 | + background: #fff; | |
| 823 | +} | |
| 824 | + | |
| 825 | +.tag-btn.active { | |
| 826 | + background: $theme-primary; | |
| 827 | + border-color: $theme-primary; | |
| 828 | + color: #fff; | |
| 829 | +} | |
| 830 | + | |
| 831 | +::v-deep .uni-easyinput { | |
| 832 | + width: 100%; | |
| 833 | + | |
| 834 | + &__content { | |
| 835 | + background: #f8f8f8 !important; | |
| 836 | + border-radius: 8rpx; | |
| 837 | + | |
| 838 | + &-input { | |
| 839 | + font-size: 30rpx; | |
| 840 | + } | |
| 841 | + } | |
| 842 | +} | |
| 843 | +</style> | ... | ... |
pages/sales-price/productRel.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="product-rel"> | |
| 3 | + <view class="header"> | |
| 4 | + <image class="opCollapse" src="/static/images/title.png" /> | |
| 5 | + <text class="title">明细信息</text> | |
| 6 | + <view class="ops"> | |
| 7 | + <image v-if="mode === 'add' || mode === 'edit'" class="opAdd" @click="onAdd" src="/static/images/plus.png" /> | |
| 8 | + <view v-if="mode === 'view'" class="op1" @click="toggleViewCollapse"> | |
| 9 | + <image class="opAdd" :src="collapsedView ? '/static/images/down.png' : '/static/images/up.png'" /> | |
| 10 | + <text class="op">{{ collapsedView ? '展开' : '收起' }}</text> | |
| 11 | + </view> | |
| 12 | + </view> | |
| 13 | + </view> | |
| 14 | + | |
| 15 | + <view v-if="mode === 'add' || mode === 'edit'" class="add-list"> | |
| 16 | + <view v-for="(item, idx) in items" :key="item._key || idx" class="block"> | |
| 17 | + <uni-list v-show="!item.collapsed"> | |
| 18 | + <uni-list-item class="select-item" :class="item.productName ? 'is-filled' : 'is-empty'" clickable @click="openProductSheet(idx)" :rightText="item.productName || '请选择品种'" showArrow> | |
| 19 | + <template v-slot:body> | |
| 20 | + <view class="item-title"><text class="required">*</text><text>品种</text></view> | |
| 21 | + </template> | |
| 22 | + </uni-list-item> | |
| 23 | + <uni-list-item title="料型"> | |
| 24 | + <template v-slot:footer> | |
| 25 | + <uni-easyinput v-model="item.materialType" :inputBorder="false" placeholder="请输入料型" /> | |
| 26 | + </template> | |
| 27 | + </uni-list-item> | |
| 28 | + <uni-list-item title="数量(吨)"> | |
| 29 | + <template v-slot:footer> | |
| 30 | + <uni-easyinput v-model="item.quantity" type="digit" :inputBorder="false" placeholder="请输入数量" @input="onNonNegativeInput(idx, 'quantity')" @blur="onNonNegativeBlur(idx, 'quantity', 1)" /> | |
| 31 | + </template> | |
| 32 | + </uni-list-item> | |
| 33 | + <uni-list-item title="采购价"> | |
| 34 | + <template v-slot:footer> | |
| 35 | + <uni-easyinput v-model="item.purchasePrice" type="digit" :inputBorder="false" placeholder="请输入采购价" @input="onNonNegativeInput(idx, 'purchasePrice')" @blur="onNonNegativeBlur(idx, 'purchasePrice', 2)" @change="recalcEstimatedLoss(idx)" /> | |
| 36 | + </template> | |
| 37 | + </uni-list-item> | |
| 38 | + <uni-list-item title="买断料预计损耗"> | |
| 39 | + <template v-slot:footer> | |
| 40 | + <uni-easyinput v-model="item.estimatedLoss" type="digit" :inputBorder="false" disabled placeholder="" /> | |
| 41 | + </template> | |
| 42 | + </uni-list-item> | |
| 43 | + <uni-list-item title="申请损耗(%)"> | |
| 44 | + <template v-slot:footer> | |
| 45 | + <uni-easyinput v-model="item.applicationAttrition" type="digit" :inputBorder="false" placeholder="请输入申请损耗" @input="onNonNegativeInput(idx, 'applicationAttrition')" @blur="onNonNegativeBlur(idx, 'applicationAttrition', 2)" @change="recalcEstimatedLoss(idx)" /> | |
| 46 | + </template> | |
| 47 | + </uni-list-item> | |
| 48 | + </uni-list> | |
| 49 | + <uni-list v-show="item.collapsed"> | |
| 50 | + <uni-list-item class="select-item" :class="item.productName ? 'is-filled' : 'is-empty'" clickable @click="openProductSheet(idx)" :rightText="item.productName || '请选择品种'" showArrow> | |
| 51 | + <template v-slot:body> | |
| 52 | + <view class="item-title"><text class="required">*</text><text>品种</text></view> | |
| 53 | + </template> | |
| 54 | + </uni-list-item> | |
| 55 | + </uni-list> | |
| 56 | + <view class="block-ops"> | |
| 57 | + <div class="del" @click="onRemove(idx)"> | |
| 58 | + <image src="/static/images/delete.png" class="icon" /> | |
| 59 | + 删除 | |
| 60 | + </div> | |
| 61 | + <div class="toggle" @click="toggleItem(idx)"> | |
| 62 | + <image :src="item.collapsed ? '/static/images/up.png' : '/static/images/down.png'" class="icon" /> | |
| 63 | + {{ item.collapsed ? '展开' : '收起' }}</div> | |
| 64 | + </view> | |
| 65 | + </view> | |
| 66 | + </view> | |
| 67 | + | |
| 68 | + <view v-else class="view-list" v-show="!collapsedView"> | |
| 69 | + <view v-for="(item, idx) in items" :key="'v-' + (item._key || idx)" class="card"> | |
| 70 | + <view class="row"><text class="label">品种</text><text class="value">{{ item.productName }}</text></view> | |
| 71 | + <view class="row"><text class="label">料型</text><text class="value">{{ item.materialType }}</text></view> | |
| 72 | + <view class="row"><text class="label">数量(吨)</text><text class="value">{{ item.quantity }}</text></view> | |
| 73 | + <view class="row"><text class="label">采购价</text><text class="value">{{ item.purchasePrice }}</text></view> | |
| 74 | + <view class="row"><text class="label">买断料预计损耗</text><text class="value">{{ item.estimatedLoss }}</text></view> | |
| 75 | + <view class="row"><text class="label">申请损耗(%)</text><text class="value">{{ item.applicationAttrition }}</text></view> | |
| 76 | + </view> | |
| 77 | + </view> | |
| 78 | + | |
| 79 | + <SingleSelectSheet :visible.sync="sheet.visible" :title="sheet.title" :options="sheet.options" v-model="sheet.value" @confirm="onProductConfirm" /> | |
| 80 | + </view> | |
| 81 | +</template> | |
| 82 | +<script> | |
| 83 | +import SingleSelectSheet from '@/components/single-select/index.vue' | |
| 84 | + | |
| 85 | +export default { | |
| 86 | + name: 'SalesPriceProductRel', | |
| 87 | + props: { | |
| 88 | + mode: { type: String, default: 'add' }, | |
| 89 | + list: { type: Array, default: () => [] }, | |
| 90 | + max: { type: Number, default: 50 }, | |
| 91 | + options: { type: Array, default: () => [] }, | |
| 92 | + priced: { type: Boolean, default: true } | |
| 93 | + }, | |
| 94 | + components: { SingleSelectSheet }, | |
| 95 | + data() { | |
| 96 | + return { | |
| 97 | + items: [], | |
| 98 | + collapsedView: false, | |
| 99 | + sheet: { visible: false, title: '请选择', options: [], value: '', idx: -1 }, | |
| 100 | + _initialized: false | |
| 101 | + } | |
| 102 | + }, | |
| 103 | + computed: { | |
| 104 | + selectOptions() { | |
| 105 | + const list = Array.isArray(this.options) ? this.options : [] | |
| 106 | + return list.map(o => ({ | |
| 107 | + label: o.label != null ? o.label : (o.name != null ? o.name : ''), | |
| 108 | + value: o.value != null ? o.value : (o.id != null ? o.id : o.productId) | |
| 109 | + })) | |
| 110 | + } | |
| 111 | + }, | |
| 112 | + watch: { | |
| 113 | + items: { | |
| 114 | + handler() { this.emitChange() }, | |
| 115 | + deep: true | |
| 116 | + }, | |
| 117 | + list: { | |
| 118 | + handler(v) { | |
| 119 | + if (!v || !v.length) return | |
| 120 | + this._initialized = false | |
| 121 | + this.initItems() | |
| 122 | + this.$nextTick(() => { this._initialized = true }) | |
| 123 | + } | |
| 124 | + } | |
| 125 | + }, | |
| 126 | + created() { | |
| 127 | + this.initItems() | |
| 128 | + this.$nextTick(() => { this._initialized = true }) | |
| 129 | + }, | |
| 130 | + methods: { | |
| 131 | + initItems() { | |
| 132 | + const src = Array.isArray(this.list) && this.list.length > 0 ? this.list : [] | |
| 133 | + if (!src.length) { | |
| 134 | + this.items = [{ ...this.defaultItem(), _key: this.newKey(), collapsed: false }] | |
| 135 | + return | |
| 136 | + } | |
| 137 | + const existing = this.items || [] | |
| 138 | + const existingMap = {} | |
| 139 | + existing.forEach(it => { | |
| 140 | + if (it.itemId) existingMap[it.itemId] = it | |
| 141 | + }) | |
| 142 | + this.items = src.map((x, i) => { | |
| 143 | + const key = x && x.itemId ? x.itemId : (x && x._key ? x._key : this.newKey()) | |
| 144 | + const old = existingMap[key] | |
| 145 | + return { | |
| 146 | + ...this.defaultItem(), | |
| 147 | + ...x, | |
| 148 | + _key: key, | |
| 149 | + itemId: x && x.itemId ? x.itemId : key, | |
| 150 | + collapsed: old ? old.collapsed : true | |
| 151 | + } | |
| 152 | + }) | |
| 153 | + }, | |
| 154 | + newKey() { | |
| 155 | + return `${Date.now()}-${Math.random().toString(16).slice(2)}` | |
| 156 | + }, | |
| 157 | + defaultItem() { | |
| 158 | + return { | |
| 159 | + productId: '', | |
| 160 | + productName: '', | |
| 161 | + materialType: '', | |
| 162 | + quantity: '', | |
| 163 | + purchasePrice: '', | |
| 164 | + estimatedLoss: '', | |
| 165 | + applicationAttrition: '', | |
| 166 | + attritionRate: '' | |
| 167 | + } | |
| 168 | + }, | |
| 169 | + onNonNegativeInput(idx, field) { | |
| 170 | + const it = this.items[idx] | |
| 171 | + if (!it) return | |
| 172 | + let v = String(it[field] != null ? it[field] : '') | |
| 173 | + v = v.replace(/[^0-9.]/g, '') | |
| 174 | + v = v.replace(/(\..*)\./g, '$1') | |
| 175 | + it[field] = v | |
| 176 | + this.$set(this.items, idx, it) | |
| 177 | + if (field === 'applicationAttrition' || field === 'purchasePrice') { | |
| 178 | + this.recalcEstimatedLoss(idx) | |
| 179 | + } | |
| 180 | + }, | |
| 181 | + onNonNegativeBlur(idx, field, digits) { | |
| 182 | + const it = this.items[idx] | |
| 183 | + if (!it) return | |
| 184 | + let raw = it[field] | |
| 185 | + if (raw === '' || raw === null || raw === undefined) { | |
| 186 | + this.$set(this.items, idx, it) | |
| 187 | + return | |
| 188 | + } | |
| 189 | + let val = parseFloat(raw) | |
| 190 | + if (isNaN(val)) val = 0 | |
| 191 | + const m = Math.pow(10, digits) | |
| 192 | + const rounded = Math.round(val * m) / m | |
| 193 | + it[field] = rounded | |
| 194 | + this.$set(this.items, idx, it) | |
| 195 | + if (field === 'applicationAttrition' || field === 'purchasePrice') { | |
| 196 | + this.recalcEstimatedLoss(idx) | |
| 197 | + } | |
| 198 | + }, | |
| 199 | + recalcEstimatedLoss(idx) { | |
| 200 | + const it = this.items[idx] | |
| 201 | + if (!it) return | |
| 202 | + const price = parseFloat(it.purchasePrice) | |
| 203 | + if (isNaN(price) || price === 0) { | |
| 204 | + it.estimatedLoss = '' | |
| 205 | + this.$set(this.items, idx, it) | |
| 206 | + return | |
| 207 | + } | |
| 208 | + const app = it.applicationAttrition | |
| 209 | + const appNum = parseFloat(app) | |
| 210 | + if (app !== '' && app != null && !isNaN(appNum)) { | |
| 211 | + it.estimatedLoss = Math.round(price * appNum / 100 * 100) / 100 | |
| 212 | + } else { | |
| 213 | + const opt = (this.options || []).find(o => String(o.value) === String(it.productId)) | |
| 214 | + const rate = opt ? (opt.lossRate != null ? opt.lossRate : opt.fixedLossRate) : null | |
| 215 | + const rateNum = parseFloat(rate) | |
| 216 | + if (!isNaN(rateNum)) { | |
| 217 | + it.estimatedLoss = Math.round(price * rateNum / 100 * 100) / 100 | |
| 218 | + } else { | |
| 219 | + it.estimatedLoss = '' | |
| 220 | + } | |
| 221 | + } | |
| 222 | + this.$set(this.items, idx, it) | |
| 223 | + }, | |
| 224 | + onAdd() { | |
| 225 | + if (this.items.length >= this.max) { | |
| 226 | + uni.showToast({ title: `最多${this.max}条明细`, icon: 'none' }) | |
| 227 | + return | |
| 228 | + } | |
| 229 | + this.items.push({ ...this.defaultItem(), _key: this.newKey(), collapsed: false }) | |
| 230 | + }, | |
| 231 | + onRemove(idx) { | |
| 232 | + this.items.splice(idx, 1) | |
| 233 | + if (this.items.length === 0) { | |
| 234 | + this.items.push({ ...this.defaultItem(), _key: this.newKey(), collapsed: false }) | |
| 235 | + } | |
| 236 | + }, | |
| 237 | + toggleItem(idx) { | |
| 238 | + const it = this.items[idx] | |
| 239 | + if (!it) return | |
| 240 | + this.$set(it, 'collapsed', !it.collapsed) | |
| 241 | + }, | |
| 242 | + toggleViewCollapse() { | |
| 243 | + this.collapsedView = !this.collapsedView | |
| 244 | + }, | |
| 245 | + openProductSheet(idx) { | |
| 246 | + const opts = this.selectOptions | |
| 247 | + const cur = this.items[idx] && this.items[idx].productId | |
| 248 | + const match = opts.find(o => String(o.value) === String(cur)) | |
| 249 | + this.sheet = { visible: true, title: '请选择品种', options: opts, value: match ? match.value : '', idx } | |
| 250 | + }, | |
| 251 | + onProductConfirm({ value, label }) { | |
| 252 | + const idx = this.sheet.idx | |
| 253 | + if (idx < 0 || !this.items[idx]) return | |
| 254 | + const it = this.items[idx] | |
| 255 | + this.$set(it, 'productId', value) | |
| 256 | + this.$set(it, 'productName', label) | |
| 257 | + const opt = (this.options || []).find(o => String(o.value != null ? o.value : (o.id != null ? o.id : o.productId)) === String(value)) | |
| 258 | + const rate = opt ? (opt.lossRate != null ? opt.lossRate : opt.fixedLossRate) : '' | |
| 259 | + this.$set(it, 'attritionRate', rate != null ? rate : '') | |
| 260 | + this.recalcEstimatedLoss(idx) | |
| 261 | + }, | |
| 262 | + emitChange() { | |
| 263 | + if (!this._initialized) return | |
| 264 | + const list = (this.items || []).map(it => { | |
| 265 | + const c = { ...it } | |
| 266 | + const key = c._key | |
| 267 | + delete c._key | |
| 268 | + delete c.collapsed | |
| 269 | + delete c._selected | |
| 270 | + if (!c.itemId) c.itemId = key | |
| 271 | + return c | |
| 272 | + }) | |
| 273 | + this.$emit('change', list) | |
| 274 | + }, | |
| 275 | + validate(priced) { | |
| 276 | + const list = this.items || [] | |
| 277 | + for (let i = 0; i < list.length; i++) { | |
| 278 | + const it = list[i] | |
| 279 | + if (!it.productId) { | |
| 280 | + uni.showToast({ title: `第${i + 1}条明细:请选择品种`, icon: 'none' }) | |
| 281 | + return false | |
| 282 | + } | |
| 283 | + if (priced === false && (it.purchasePrice === '' || it.purchasePrice == null)) { | |
| 284 | + uni.showToast({ title: `第${i + 1}条明细:未点价为否时采购价必填`, icon: 'none' }) | |
| 285 | + return false | |
| 286 | + } | |
| 287 | + } | |
| 288 | + return true | |
| 289 | + } | |
| 290 | + } | |
| 291 | +} | |
| 292 | +</script> | |
| 293 | +<style lang="scss" scoped> | |
| 294 | +.product-rel { | |
| 295 | + margin-top: 20rpx; | |
| 296 | + background: #fff; | |
| 297 | +} | |
| 298 | + | |
| 299 | +.header { | |
| 300 | + display: flex; | |
| 301 | + align-items: center; | |
| 302 | + padding: 24rpx 32rpx; | |
| 303 | + border-bottom: 1rpx solid #f0f0f0; | |
| 304 | +} | |
| 305 | + | |
| 306 | +.dot { | |
| 307 | + width: 16rpx; | |
| 308 | + height: 16rpx; | |
| 309 | + background: #3D48A3; | |
| 310 | + border-radius: 50%; | |
| 311 | + margin-right: 12rpx; | |
| 312 | +} | |
| 313 | + | |
| 314 | +.title { | |
| 315 | + font-size: 32rpx; | |
| 316 | + color: rgba(0, 0, 0, 0.9); | |
| 317 | + font-weight: 600; | |
| 318 | +} | |
| 319 | + | |
| 320 | +.ops { | |
| 321 | + margin-left: auto; | |
| 322 | +} | |
| 323 | + | |
| 324 | +.op { | |
| 325 | + color: $theme-primary; | |
| 326 | + font-size: 28rpx; | |
| 327 | + margin-left: 8rpx; | |
| 328 | +} | |
| 329 | + | |
| 330 | +.op1 { | |
| 331 | + display: flex; | |
| 332 | + align-items: center; | |
| 333 | +} | |
| 334 | + | |
| 335 | +.opAdd { | |
| 336 | + color: rgba(0, 0, 0, 0.6); | |
| 337 | + width: 40rpx; | |
| 338 | + height: 40rpx; | |
| 339 | +} | |
| 340 | + | |
| 341 | +.opCollapse { | |
| 342 | + color: rgba(0, 0, 0, 0.6); | |
| 343 | + width: 32rpx; | |
| 344 | + height: 28rpx; | |
| 345 | + margin-right: 16rpx; | |
| 346 | + margin-top: 8rpx; | |
| 347 | +} | |
| 348 | + | |
| 349 | +.block { | |
| 350 | + background: #fff; | |
| 351 | + margin-bottom: 20rpx; | |
| 352 | +} | |
| 353 | + | |
| 354 | +::v-deep .uni-list-item__content { | |
| 355 | + display: flex; | |
| 356 | + justify-content: center; | |
| 357 | +} | |
| 358 | + | |
| 359 | +::v-deep .uni-list { | |
| 360 | + background: transparent; | |
| 361 | + | |
| 362 | + &-item { | |
| 363 | + &__container { | |
| 364 | + padding: 32rpx; | |
| 365 | + } | |
| 366 | + | |
| 367 | + &__content-title { | |
| 368 | + font-size: 28rpx; | |
| 369 | + color: rgba(0, 0, 0, 0.9); | |
| 370 | + } | |
| 371 | + | |
| 372 | + &__extra-text { | |
| 373 | + font-size: 32rpx; | |
| 374 | + } | |
| 375 | + | |
| 376 | + .uni-easyinput { | |
| 377 | + width: 100%; | |
| 378 | + | |
| 379 | + &__placeholder-class { | |
| 380 | + font-size: 32rpx; | |
| 381 | + color: rgba(0, 0, 0, 0.4); | |
| 382 | + } | |
| 383 | + | |
| 384 | + &__content { | |
| 385 | + border: none; | |
| 386 | + display: flex; | |
| 387 | + | |
| 388 | + &-input { | |
| 389 | + padding-left: 0 !important; | |
| 390 | + height: 48rpx; | |
| 391 | + line-height: 48rpx; | |
| 392 | + font-size: 32rpx; | |
| 393 | + } | |
| 394 | + } | |
| 395 | + } | |
| 396 | + } | |
| 397 | +} | |
| 398 | + | |
| 399 | +.block-ops { | |
| 400 | + display: flex; | |
| 401 | + padding: 20rpx 32rpx 20rpx; | |
| 402 | + justify-content: space-around; | |
| 403 | +} | |
| 404 | + | |
| 405 | +.del { | |
| 406 | + color: #D54941; | |
| 407 | + font-size: 28rpx; | |
| 408 | + display: flex; | |
| 409 | + align-items: center; | |
| 410 | + | |
| 411 | + image { | |
| 412 | + width: 40rpx; | |
| 413 | + height: 40rpx; | |
| 414 | + } | |
| 415 | +} | |
| 416 | + | |
| 417 | +.toggle { | |
| 418 | + color: $theme-primary; | |
| 419 | + font-size: 28rpx; | |
| 420 | + display: flex; | |
| 421 | + align-items: center; | |
| 422 | + | |
| 423 | + image { | |
| 424 | + width: 40rpx; | |
| 425 | + height: 40rpx; | |
| 426 | + } | |
| 427 | +} | |
| 428 | + | |
| 429 | +.view-list { | |
| 430 | + padding: 24rpx 32rpx; | |
| 431 | + background: #ffffff; | |
| 432 | +} | |
| 433 | + | |
| 434 | +.card { | |
| 435 | + background: #f3f3f3; | |
| 436 | + border-radius: 16rpx; | |
| 437 | + padding: 24rpx; | |
| 438 | + margin-bottom: 20rpx; | |
| 439 | +} | |
| 440 | + | |
| 441 | +.row { | |
| 442 | + display: flex; | |
| 443 | + margin-bottom: 20rpx; | |
| 444 | +} | |
| 445 | + | |
| 446 | +.row:last-child { | |
| 447 | + margin-bottom: 0; | |
| 448 | +} | |
| 449 | + | |
| 450 | +.label { | |
| 451 | + width: 240rpx; | |
| 452 | + color: rgba(0, 0, 0, 0.6); | |
| 453 | + font-size: 28rpx; | |
| 454 | +} | |
| 455 | + | |
| 456 | +.value { | |
| 457 | + flex: 1; | |
| 458 | + color: rgba(0, 0, 0, 0.9); | |
| 459 | + font-size: 28rpx; | |
| 460 | +} | |
| 461 | + | |
| 462 | +.item-title { | |
| 463 | + position: relative; | |
| 464 | + width: 210rpx; | |
| 465 | + margin-right: 32rpx; | |
| 466 | + color: rgba(0, 0, 0, 0.9); | |
| 467 | + font-size: 32rpx; | |
| 468 | + min-height: 48rpx; | |
| 469 | + line-height: 48rpx; | |
| 470 | + | |
| 471 | + .required { | |
| 472 | + color: red; | |
| 473 | + position: absolute; | |
| 474 | + top: 50%; | |
| 475 | + transform: translateY(-50%); | |
| 476 | + left: -16rpx; | |
| 477 | + } | |
| 478 | +} | |
| 479 | +</style> | ... | ... |
| 1 | +<template> | |
| 2 | + <view class="detail-page"> | |
| 3 | + <scroll-view class="scroll" scroll-y> | |
| 4 | + <!-- 基础信息 --> | |
| 5 | + <view class="section"> | |
| 6 | + <text class="row company">{{ safeText(form.code) }}</text> | |
| 7 | + <view class="row"><text class="label">供应商名称</text><text class="value">{{ safeText(form.supplierName) }}</text></view> | |
| 8 | + <view class="row"><text class="label">采购处</text><text class="value">{{ safeText(form.purchaseDepartmentName) }}</text></view> | |
| 9 | + <view class="row"><text class="label">区域</text><text class="value">{{ safeText(form.regionName) }}</text></view> | |
| 10 | + <view class="row"><text class="label">报价时效</text><text class="value">{{ safeText(form.validityPeriod) }}</text></view> | |
| 11 | + <view class="row"><text class="label">报价人</text><text class="value">{{ safeText(form.quoterName) }}</text></view> | |
| 12 | + <view class="row"><text class="label">是否长单操作</text><text class="value">{{ boolText(form.longTermOperation) }}</text></view> | |
| 13 | + <view class="row"><text class="label">是否终端客户</text><text class="value">{{ boolText(form.terminalCustomer) }}</text></view> | |
| 14 | + <view class="row"><text class="label">是否未点价</text><text class="value">{{ boolText(form.priced) }}</text></view> | |
| 15 | + <view v-if="form.priced === false" class="row"><text class="label">点价截止日期</text><text class="value">{{ formatDateOnly(form.pricingDeadline) }}</text></view> | |
| 16 | + <view class="row"><text class="label">其他费用凭票到厂另行结算</text><text class="value">{{ boolText(form.otherExpenseSettlement) }}</text></view> | |
| 17 | + <view class="row"><text class="label">交提货方式</text><text class="value">{{ getDeliveryMethodLabel() }}</text></view> | |
| 18 | + <view class="row"><text class="label">是否通货买断</text><text class="value">{{ boolText(form.commodityBuyout) }}</text></view> | |
| 19 | + <view class="row"><text class="label">是否质量买断</text><text class="value">{{ boolText(form.qualityBuyout) }}</text></view> | |
| 20 | + <view class="row"><text class="label">是否数量买断</text><text class="value">{{ boolText(form.quantityBuyout) }}</text></view> | |
| 21 | + <view class="row"><text class="label">是否送货上门</text><text class="value">{{ boolText(form.doorDelivery) }}</text></view> | |
| 22 | + <view class="row"><text class="label">装车费</text><text class="value">{{ safeText(form.loadingFee) }}</text></view> | |
| 23 | + <view class="row"><text class="label">开票品种</text><text class="value">{{ safeText(form.invoiceProductName) }}</text></view> | |
| 24 | + <view class="row"><text class="label">交货日期</text><text class="value">{{ formatDateOnly(form.deliveryDate) }}</text></view> | |
| 25 | + <view class="row"><text class="label">结算方式</text><text class="value">{{ safeText(form.settlementMethod) }}</text></view> | |
| 26 | + <view class="row"><text class="label">结算比例(%)</text><text class="value">{{ safeText(form.settlementRatio) }}</text></view> | |
| 27 | + <view class="row"><text class="label">协调事由</text><text class="value">{{ safeText(form.coordinationReason) }}</text></view> | |
| 28 | + <view class="row"><text class="label">票类状况</text><text class="value">{{ safeText(form.invoiceTypeStatus) }}</text></view> | |
| 29 | + </view> | |
| 30 | + | |
| 31 | + <!-- 明细信息 --> | |
| 32 | + <ProductRel mode="view" :list="productLineList" /> | |
| 33 | + | |
| 34 | + <!-- 料质状况 --> | |
| 35 | + <view class="title-header"> | |
| 36 | + <image class="title-header_icon" src="/static/images/title.png" /> | |
| 37 | + <span>料质状况</span> | |
| 38 | + </view> | |
| 39 | + <view class="section"> | |
| 40 | + <view class="row"><text class="label">是否启用料质状况</text><text class="value">{{ boolText(form.materialConditionEnabled) }}</text></view> | |
| 41 | + <template v-if="form.materialConditionEnabled"> | |
| 42 | + <view v-for="mc in materialConditionConfigs" :key="mc.field" class="material-group"> | |
| 43 | + <text class="material-label">{{ mc.label }}</text> | |
| 44 | + <view class="tag-list"> | |
| 45 | + <view | |
| 46 | + v-for="opt in getDictOptions(mc.dictCode)" | |
| 47 | + :key="opt.code" | |
| 48 | + class="tag-btn" | |
| 49 | + :class="{ active: isTagSelected(mc.field, opt.code) }" | |
| 50 | + >{{ opt.name }}</view> | |
| 51 | + </view> | |
| 52 | + </view> | |
| 53 | + </template> | |
| 54 | + </view> | |
| 55 | + | |
| 56 | + <!-- 备注 --> | |
| 57 | + <view class="title-header"> | |
| 58 | + <image class="title-header_icon" src="/static/images/title.png" /> | |
| 59 | + <span>备注</span> | |
| 60 | + </view> | |
| 61 | + <view class="section"> | |
| 62 | + <text class="remark-text">{{ safeText(form.remark) }}</text> | |
| 63 | + </view> | |
| 64 | + </scroll-view> | |
| 65 | + </view> | |
| 66 | +</template> | |
| 67 | +<script> | |
| 68 | +import ProductRel from './productRel.vue' | |
| 69 | +import { salesmanQuotationDetailApi } from '@/api/procure-manage/salesmanQuotation.js' | |
| 70 | +import { getDicByCodes } from '@/utils/dic.js' | |
| 71 | + | |
| 72 | +export default { | |
| 73 | + name: 'SalesmanQuotationViewer', | |
| 74 | + components: { ProductRel }, | |
| 75 | + props: { id: { type: [String, Number], default: '' } }, | |
| 76 | + data() { | |
| 77 | + return { | |
| 78 | + form: {}, | |
| 79 | + productLineList: [], | |
| 80 | + materialConditionConfigs: [ | |
| 81 | + { field: 'materialConditionCopperRice', label: '铜米', dictCode: 'COPPER_RICE' }, | |
| 82 | + { field: 'materialConditionBrightCopper', label: '光亮铜', dictCode: 'BRIGHT_COPPER' }, | |
| 83 | + { field: 'materialConditionFirstClass', label: '一特', dictCode: 'FIRST_CLASS' }, | |
| 84 | + { field: 'materialConditionSecondClass', label: '二特', dictCode: 'SECOND_CLASS' }, | |
| 85 | + { field: 'materialConditionTinCoated', label: '镀锡紫', dictCode: 'TIN_COATED' }, | |
| 86 | + { field: 'materialConditionScrap', label: '角料', dictCode: 'SCRAP' }, | |
| 87 | + { field: 'materialConditionWhiteYellow', label: '镀白黄', dictCode: 'WHITE_YELLOW' }, | |
| 88 | + { field: 'materialConditionPhosphorCopper', label: '磷铜', dictCode: 'PHOSPHOR_COPPER' } | |
| 89 | + ], | |
| 90 | + dictData: {} | |
| 91 | + } | |
| 92 | + }, | |
| 93 | + created() { | |
| 94 | + this.loadDictData() | |
| 95 | + }, | |
| 96 | + watch: { | |
| 97 | + id: { | |
| 98 | + immediate: true, | |
| 99 | + handler(val) { | |
| 100 | + const v = (val !== undefined && val !== null) ? String(val) : '' | |
| 101 | + if (v) this.loadDetail(v) | |
| 102 | + } | |
| 103 | + } | |
| 104 | + }, | |
| 105 | + methods: { | |
| 106 | + async loadDictData() { | |
| 107 | + try { | |
| 108 | + const codes = [...this.materialConditionConfigs.map(mc => mc.dictCode), 'DELIVERY_METHOD'] | |
| 109 | + const res = await getDicByCodes(codes) | |
| 110 | + this.dictData = res || {} | |
| 111 | + } catch (e) { | |
| 112 | + this.dictData = {} | |
| 113 | + } | |
| 114 | + }, | |
| 115 | + getDictOptions(dictCode) { | |
| 116 | + const data = this.dictData[dictCode] | |
| 117 | + return (data && data.data) ? data.data : [] | |
| 118 | + }, | |
| 119 | + isTagSelected(field, code) { | |
| 120 | + const selected = this.form[field] | |
| 121 | + return Array.isArray(selected) && selected.includes(code) | |
| 122 | + }, | |
| 123 | + boolText(v) { | |
| 124 | + if (typeof v !== 'boolean') return '-' | |
| 125 | + return v ? '是' : '否' | |
| 126 | + }, | |
| 127 | + getDeliveryMethodLabel() { | |
| 128 | + const opts = this.getDictOptions('DELIVERY_METHOD') || [] | |
| 129 | + const m = opts.find(o => String(o.code != null ? o.code : o.value) === String(this.form.deliveryMode)) | |
| 130 | + return m ? (m.name || m.label || '-') : '-' | |
| 131 | + }, | |
| 132 | + formatDateOnly(v) { | |
| 133 | + const s = v == null ? '' : String(v).trim() | |
| 134 | + if (!s) return '-' | |
| 135 | + if (s.length >= 10) return s.slice(0, 10) | |
| 136 | + return s | |
| 137 | + }, | |
| 138 | + safeText(v) { | |
| 139 | + const s = v == null ? '' : String(v) | |
| 140 | + return s.trim() ? s : '-' | |
| 141 | + }, | |
| 142 | + async loadDetail(id) { | |
| 143 | + // 审核详情接口不可用时回退到普通详情接口 | |
| 144 | + try { | |
| 145 | + const res = await salesmanQuotationDetailApi({ id }) | |
| 146 | + const d = (res && res.data) ? res.data : {} | |
| 147 | + const materialFields = this.materialConditionConfigs.map(mc => mc.field) | |
| 148 | + materialFields.forEach(f => { | |
| 149 | + const v = d[f] | |
| 150 | + d[f] = (typeof v === 'string' && v) ? v.split(',').filter(Boolean) : (Array.isArray(v) ? v : []) | |
| 151 | + }) | |
| 152 | + this.form = d | |
| 153 | + this.productLineList = Array.isArray(d.quotationLineList) ? d.quotationLineList : (Array.isArray(d.salesmanQuotationLineList) ? d.salesmanQuotationLineList : []) | |
| 154 | + } catch (err) { | |
| 155 | + this.form = {} | |
| 156 | + this.productLineList = [] | |
| 157 | + } | |
| 158 | + } | |
| 159 | + } | |
| 160 | +} | |
| 161 | +</script> | |
| 162 | +<style lang="scss" scoped> | |
| 163 | +.detail-page { | |
| 164 | + background: #f3f3f3; | |
| 165 | +} | |
| 166 | + | |
| 167 | +.scroll { | |
| 168 | + flex: 1; | |
| 169 | + padding: 8rpx 0 144rpx; | |
| 170 | +} | |
| 171 | + | |
| 172 | +.title-header { | |
| 173 | + display: flex; | |
| 174 | + align-items: center; | |
| 175 | + padding: 24rpx 32rpx 12rpx; | |
| 176 | + background-color: #ffffff; | |
| 177 | + | |
| 178 | + .title-header_icon { | |
| 179 | + width: 36rpx; | |
| 180 | + height: 36rpx; | |
| 181 | + margin-right: 12rpx; | |
| 182 | + } | |
| 183 | + | |
| 184 | + span { | |
| 185 | + font-size: 34rpx; | |
| 186 | + font-weight: 600; | |
| 187 | + color: rgba(0, 0, 0, 0.9); | |
| 188 | + } | |
| 189 | +} | |
| 190 | + | |
| 191 | +.section { | |
| 192 | + padding: 32rpx; | |
| 193 | + background: #fff; | |
| 194 | + margin-bottom: 20rpx; | |
| 195 | +} | |
| 196 | + | |
| 197 | +.row { | |
| 198 | + display: flex; | |
| 199 | + margin-bottom: 28rpx; | |
| 200 | + | |
| 201 | + &:last-child { | |
| 202 | + margin-bottom: 0; | |
| 203 | + } | |
| 204 | + | |
| 205 | + &.company { | |
| 206 | + font-size: 36rpx; | |
| 207 | + font-weight: 600; | |
| 208 | + color: rgba(0, 0, 0, 0.9); | |
| 209 | + padding-top: 8rpx; | |
| 210 | + line-height: 50rpx; | |
| 211 | + } | |
| 212 | + | |
| 213 | + .label { | |
| 214 | + max-width: 420rpx; | |
| 215 | + margin-right: 20rpx; | |
| 216 | + line-height: 32rpx; | |
| 217 | + font-size: 28rpx; | |
| 218 | + color: rgba(0, 0, 0, 0.6); | |
| 219 | + } | |
| 220 | + | |
| 221 | + .value { | |
| 222 | + flex: 1; | |
| 223 | + line-height: 32rpx; | |
| 224 | + font-size: 28rpx; | |
| 225 | + color: rgba(0, 0, 0, 0.9); | |
| 226 | + text-align: right; | |
| 227 | + word-break: break-all; | |
| 228 | + } | |
| 229 | +} | |
| 230 | + | |
| 231 | +.material-group { | |
| 232 | + margin-top: 32rpx; | |
| 233 | + | |
| 234 | + &:first-child { | |
| 235 | + margin-top: 0; | |
| 236 | + } | |
| 237 | +} | |
| 238 | + | |
| 239 | +.material-label { | |
| 240 | + display: block; | |
| 241 | + font-size: 28rpx; | |
| 242 | + color: rgba(0, 0, 0, 0.6); | |
| 243 | + margin-bottom: 20rpx; | |
| 244 | +} | |
| 245 | + | |
| 246 | +.tag-list { | |
| 247 | + display: flex; | |
| 248 | + flex-wrap: wrap; | |
| 249 | + gap: 16rpx; | |
| 250 | +} | |
| 251 | + | |
| 252 | +.tag-btn { | |
| 253 | + padding: 12rpx 24rpx; | |
| 254 | + border: 1rpx solid #ddd; | |
| 255 | + border-radius: 8rpx; | |
| 256 | + font-size: 28rpx; | |
| 257 | + color: #999; | |
| 258 | + background: #f5f5f5; | |
| 259 | +} | |
| 260 | + | |
| 261 | +.tag-btn.active { | |
| 262 | + background: $theme-primary; | |
| 263 | + border-color: $theme-primary; | |
| 264 | + color: #fff; | |
| 265 | +} | |
| 266 | + | |
| 267 | +.remark-text { | |
| 268 | + font-size: 28rpx; | |
| 269 | + color: rgba(0, 0, 0, 0.9); | |
| 270 | + line-height: 1.6; | |
| 271 | + white-space: pre-wrap; | |
| 272 | +} | |
| 273 | +</style> | ... | ... |
| 1 | 1 | import CustomerDevelopViewer from '@/pages/dev_manage/viewer.vue' |
| 2 | 2 | import CustomerCreditViewer from '@/pages/credit_manage/viewer.vue' |
| 3 | 3 | import DomesticCustomerCreditViewer from '@/pages/domestic_trade/domesticCreditViewer.vue' |
| 4 | +import SalesmanQuotationViewer from '@/pages/sales-price/salesmanQuotationViewer.vue' | |
| 4 | 5 | import ForeignTradeCreditViewer from '@/pages/foreign_trade/foreignTradeCreditViewer.vue' |
| 5 | 6 | import RetailViewer from '@/pages/contract_retail/retailViewer.vue' |
| 6 | 7 | import ForeignStdViewer from '@/pages/contract_foreign_std/foreignStdViewer.vue' |
| ... | ... | @@ -29,6 +30,7 @@ export default function registerComponents(Vue) { |
| 29 | 30 | Vue.component('CustomerDevelopViewer', CustomerDevelopViewer) |
| 30 | 31 | Vue.component('CustomerCreditViewer', CustomerCreditViewer) |
| 31 | 32 | Vue.component('DomesticCustomerCreditViewer', DomesticCustomerCreditViewer) |
| 33 | + Vue.component('SalesmanQuotationViewer', SalesmanQuotationViewer) | |
| 32 | 34 | Vue.component('ForeignTradeCreditViewer', ForeignTradeCreditViewer) |
| 33 | 35 | Vue.component('RetailViewer', RetailViewer) |
| 34 | 36 | Vue.component('StandardContractViewer', StandardContractViewer) | ... | ... |
| ... | ... | @@ -20,6 +20,12 @@ export const getSysFlowComponentPath = (bizFlag) => { |
| 20 | 20 | case 'CUSTOMER_CREDIT_IN_EDIT': // 客户资信(内贸) |
| 21 | 21 | componentPath = 'DomesticCustomerCreditViewer'; // 客户资信(内贸)-审批 |
| 22 | 22 | break; |
| 23 | + case 'SALESMAN_QUOTATION': // 业务员报价单 | |
| 24 | + componentPath = 'SalesmanQuotationViewer'; // 业务员报价单-审批详情 | |
| 25 | + break; | |
| 26 | + case 'SALESMAN_QUOTATION_EDIT': // 业务员报价单 | |
| 27 | + componentPath = 'SalesmanQuotationViewer'; // 业务员报价单-审批 | |
| 28 | + break; | |
| 23 | 29 | case 'CUSTOMER_CREDIT_OUT': // 客户资信(外贸) |
| 24 | 30 | componentPath = 'ForeignTradeCreditViewer'; // 客户资信(外贸)-审批详情 |
| 25 | 31 | break; | ... | ... |