| | |
| | | <text class="title">å°è´¦è¯¦æ
</text> |
| | | </view> |
| | | |
| | | <!-- 表ååºå --> |
| | | <view class="form-section"> |
| | | <van-form ref="formRef" :modelValue="form" :rules="rules" label-width="100px" input-align="right"> |
| | | <van-field label="éå®ååå·" name="salesContractNo" borderBottom="true" v-model="form.salesContractNo" placeholder="请è¾å
¥éå®ååå·"> |
| | | </van-field> |
| | | <van-field label="éå®ååå·" name="salesContractNo" borderBottom="true" v-model="form.salesContractNo" placeholder="请è¾å
¥éå®ååå·"> |
| | | </van-field> |
| | | <van-field label="éå®ååå·" name="salesContractNo" borderBottom="true" v-model="form.salesContractNo" placeholder="请è¾å
¥éå®ååå·"> |
| | | </van-field> |
| | | <van-field label="éå®ååå·" name="salesContractNo" borderBottom="true" v-model="form.salesContractNo" placeholder="请è¾å
¥éå®ååå·"> |
| | | </van-field> |
| | | <van-field label="éå®ååå·" name="salesContractNo" borderBottom="true" v-model="form.salesContractNo" placeholder="请è¾å
¥éå®ååå·"> |
| | | </van-field> |
| | | <van-field label="éå®ååå·" name="salesContractNo" borderBottom="true" v-model="form.salesContractNo" placeholder="请è¾å
¥éå®ååå·"> |
| | | </van-field> |
| | | <van-field label="å½å
¥äºº" name="salesContractNo" borderBottom="true" v-model="form.salesContractNo" placeholder="请è¾å
¥" readonly> |
| | | </van-field> |
| | | <van-field label="å½å
¥æ¥æ" name="salesContractNo" borderBottom="true" v-model="form.salesContractNo" placeholder="请è¾å
¥" readonly> |
| | | </van-field> |
| | | </van-form> |
| | | </view> |
| | | |
| | | <!-- 产åä¿¡æ¯ --> |
| | | <view class="product-section"> |
| | | <view class="section-header"> |
| | | <text class="section-title">产åä¿¡æ¯</text> |
| | | <button class="add-btn" @click="addProduct">æ°å¢</button> |
| | | </view> |
| | | <view class="product-card" v-for="(product, idx) in products" :key="idx"> |
| | | <view class="product-row"> |
| | | <text class="product-label">产åç±»</text> |
| | | <uni-easyinput v-model="product.type" placeholder="请è¾å
¥äº§åç±»" /> |
| | | </view> |
| | | <view class="product-row"> |
| | | <text class="product-label">åä½</text> |
| | | <uni-easyinput v-model="product.unit" placeholder="请è¾å
¥åä½" /> |
| | | <text class="product-label">æ°é</text> |
| | | <uni-easyinput v-model="product.amount" placeholder="请è¾å
¥æ°é" type="number" /> |
| | | </view> |
| | | <view class="product-row"> |
| | | <text class="product-label">ç¨ç</text> |
| | | <uni-easyinput v-model="product.taxRate" placeholder="请è¾å
¥ç¨ç" /> |
| | | <text class="product-label">å«ç¨åä»·</text> |
| | | <uni-easyinput v-model="product.taxPrice" placeholder="请è¾å
¥å«ç¨åä»·" /> |
| | | </view> |
| | | <view class="product-row"> |
| | | <text class="product-label">å«ç¨æ»ä»·</text> |
| | | <uni-easyinput v-model="product.taxTotal" placeholder="请è¾å
¥å«ç¨æ»ä»·" /> |
| | | <text class="product-label">ååéé¢</text> |
| | | <uni-easyinput v-model="product.contractAmount" placeholder="请è¾å
¥ååéé¢" /> |
| | | </view> |
| | | <view class="product-row"> |
| | | <text class="product-label">æä½</text> |
| | | <uni-easyinput v-model="product.operateDate" placeholder="请è¾å
¥æä½æ¶é´" /> |
| | | </view> |
| | | <view class="product-row"> |
| | | <text class="product-label">夿³¨</text> |
| | | <uni-easyinput v-model="product.remark" placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </view> |
| | | <view class="product-row del-row"> |
| | | <button class="del-btn" @click="removeProduct(idx)">å é¤</button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- åºé¨æé® --> |
| | | <view class="footer-btns"> |
| | | <van-button class="cancel-btn" @click="goBack">åæ¶</van-button> |
| | | <van-button class="save-btn" @click="submitForm">ä¿å</van-button> |
| | | </view> |
| | | <!-- 表ååºå --> |
| | | <van-form @submit="onSubmit" label-width="110px" input-align="right" style="margin-top: 10px" error-message-align="right" scroll-to-error scroll-to-error-position="center"> |
| | | <van-field label="éå®ååå·" name="salesContractNo" borderBottom="true" v-model="form.salesContractNo" placeholder="èªå¨çæ" disabled> |
| | | </van-field> |
| | | <van-field |
| | | v-model="form.salesman" |
| | | is-link |
| | | readonly |
| | | name="salesman" |
| | | label="ä¸å¡å" |
| | | required |
| | | placeholder="ç¹å»éæ©ä¸å¡å" |
| | | :rules="[{ required: true, message: 'è¯·éæ©ä¸å¡å' }]" |
| | | @click="showPicker = true" |
| | | /> |
| | | <van-field label="客æ·ååå·" name="customerContractNo" borderBottom="true" |
| | | v-model="form.customerContractNo" required |
| | | placeholder="请è¾å
¥å®¢æ·ååå·" :rules="[{ required: true, message: '客æ·ååå·ä¸è½ä¸ºç©º' }]"> |
| | | </van-field> |
| | | <van-field |
| | | v-model="form.customerName" |
| | | is-link |
| | | readonly |
| | | required |
| | | name="customerName" |
| | | label="客æ·åç§°" |
| | | placeholder="ç¹å»éæ©å®¢æ·" |
| | | :rules="[{ required: true, message: 'è¯·éæ©å®¢æ·' }]" |
| | | @click="showCustomerPicker = true" |
| | | /> |
| | | <van-field label="项ç®åç§°" name="projectName" borderBottom="true" v-model="form.projectName" placeholder="请è¾å
¥é¡¹ç®åç§°" :rules="[{ required: true, message: '项ç®åç§°ä¸è½ä¸ºç©º' }]" required> |
| | | </van-field> |
| | | <van-field |
| | | v-model="form.executionDate" |
| | | is-link |
| | | readonly |
| | | required |
| | | name="executionDate" |
| | | label="ç¾è®¢æ¥æ" |
| | | placeholder="ç¹å»éæ©æ¶é´" |
| | | :rules="[{ required: true, message: 'ç¾è®¢æ¥æä¸è½ä¸ºç©º' }]" |
| | | @click="showDatePicker = true" |
| | | /> |
| | | <van-popup v-model:show="showDatePicker" destroy-on-close position="bottom"> |
| | | <van-date-picker |
| | | v-model="pickerDateValue" |
| | | @confirm="onDateConfirm" |
| | | @cancel="showDatePicker = false" |
| | | /> |
| | | </van-popup> |
| | | <van-field label="仿¬¾æ¹å¼" name="paymentMethod" borderBottom="true" v-model="form.paymentMethod" placeholder="请è¾å
¥ä»æ¬¾æ¹å¼"> |
| | | </van-field> |
| | | <van-field label="å½å
¥äºº" name="entryPersonName" borderBottom="true" v-model="form.entryPersonName" placeholder="请è¾å
¥" disabled> |
| | | </van-field> |
| | | <van-field label="å½å
¥æ¥æ" name="entryDate" borderBottom="true" v-model="form.entryDate" placeholder="请è¾å
¥" disabled> |
| | | </van-field> |
| | | <van-popup v-model:show="showPicker" destroy-on-close position="bottom"> |
| | | <van-picker |
| | | :columns="userList" |
| | | v-model="pickerValue" |
| | | @confirm="onConfirm" |
| | | @cancel="showPicker = false" |
| | | /> |
| | | </van-popup> |
| | | <van-popup v-model:show="showCustomerPicker" destroy-on-close position="bottom"> |
| | | <van-picker |
| | | :columns="customerOption" |
| | | v-model="pickerCustomerValue" |
| | | @confirm="onCustomerConfirm" |
| | | @cancel="showCustomerPicker = false" |
| | | /> |
| | | </van-popup> |
| | | |
| | | <!-- 产åå¤§ç±»éæ©å¨ --> |
| | | <van-popup v-model:show="showCategoryPicker" destroy-on-close position="bottom"> |
| | | <!-- 头鍿é®åºå --> |
| | | <view class="popup-header"> |
| | | <view @click="showCategoryPicker = false" class="cancelButton">åæ¶</view> |
| | | <view @click="confirmCategorySelection" class="confirmButton">ç¡®å®</view> |
| | | </view> |
| | | <up-tree |
| | | :data="productOptions" |
| | | :props="defaultProps" |
| | | show-checkbox |
| | | default-expand-all |
| | | check-strictly |
| | | @check-change="onCategoryConfirm" |
| | | /> |
| | | </van-popup> |
| | | |
| | | <!-- è§æ ¼åå·éæ©å¨ --> |
| | | <van-popup v-model:show="showSpecificationPicker" destroy-on-close position="bottom"> |
| | | <van-picker |
| | | :columns="modelOptions" |
| | | v-model="pickerSpecificationValue" |
| | | @confirm="onSpecificationConfirm" |
| | | @cancel="showSpecificationPicker = false" |
| | | /> |
| | | </van-popup> |
| | | |
| | | <!-- ç¨çéæ©å¨ --> |
| | | <van-popup v-model:show="showTaxRatePicker" destroy-on-close position="bottom"> |
| | | <van-picker |
| | | :columns="taxRateOptions" |
| | | v-model="pickerTaxRateValue" |
| | | @confirm="onTaxRateConfirm" |
| | | @cancel="showTaxRatePicker = false" |
| | | /> |
| | | </van-popup> |
| | | |
| | | <!-- å票类åéæ©å¨ --> |
| | | <van-popup v-model:show="showInvoiceTypePicker" destroy-on-close position="bottom"> |
| | | <van-picker |
| | | :columns="invoiceTypeOptions" |
| | | v-model="pickerInvoiceTypeValue" |
| | | @confirm="onInvoiceTypeConfirm" |
| | | @cancel="showInvoiceTypePicker = false" |
| | | /> |
| | | </van-popup> |
| | | <!-- 产åä¿¡æ¯ --> |
| | | <view class="product-section"> |
| | | <view class="section-header"> |
| | | <text class="section-title">产åä¿¡æ¯</text> |
| | | <van-button type="primary" size="small" @click="addProduct" class="add-btn" icon="plus" v-if="operationType !== 'view'">æ°å¢</van-button> |
| | | </view> |
| | | <view class="product-card" v-for="(product, idx) in productData" :key="idx"> |
| | | <!-- 产åç±» --> |
| | | <view class="product-header"> |
| | | <view class="product-title"> |
| | | <van-icon name="description" color="#2979ff" size="15" /> |
| | | <text class="product-productCategory">产å {{ idx + 1 }}</text> |
| | | </view> |
| | | <!-- æä½æé® --> |
| | | <view class="product-actions" v-if="operationType !== 'view'"> |
| | | <van-button type="danger" size="mini" @click="removeProduct(idx)" class="del-btn" icon="delete">å é¤</van-button> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 产åä¿¡æ¯è¡¨å --> |
| | | <view class="product-form"> |
| | | <!-- 产å大类 --> |
| | | <van-field |
| | | v-model="product.productCategory" |
| | | is-link |
| | | readonly |
| | | name="productCategory" |
| | | label="产å大类" |
| | | required |
| | | placeholder="è¯·éæ©" |
| | | :rules="[{ required: true, message: 'è¯·éæ©' }]" |
| | | @click="openCategoryPicker(idx)" |
| | | /> |
| | | |
| | | <!-- è§æ ¼åå· --> |
| | | <van-field |
| | | v-model="product.specificationModel" |
| | | is-link |
| | | readonly |
| | | name="specificationModel" |
| | | label="è§æ ¼åå·" |
| | | required |
| | | :rules="[{ required: true, message: 'è¯·éæ©' }]" |
| | | placeholder="è¯·éæ©" |
| | | @click="openSpecificationPicker(idx)" |
| | | /> |
| | | |
| | | <!-- åä½ --> |
| | | <van-field |
| | | v-model="product.unit" |
| | | name="unit" |
| | | label="åä½" |
| | | required |
| | | :rules="[{ required: true, message: '请è¾å
¥' }]" |
| | | placeholder="请è¾å
¥" |
| | | /> |
| | | |
| | | <!-- ç¨ç --> |
| | | <van-field |
| | | v-model="product.taxRate" |
| | | is-link |
| | | readonly |
| | | name="taxRate" |
| | | label="ç¨ç(%)" |
| | | required |
| | | :rules="[{ required: true, message: 'è¯·éæ©' }]" |
| | | placeholder="è¯·éæ©" |
| | | @click="openTaxRatePicker(idx)" |
| | | /> |
| | | |
| | | <!-- å«ç¨åä»· --> |
| | | <van-field |
| | | v-model="product.taxInclusiveUnitPrice" |
| | | name="taxInclusiveUnitPrice" |
| | | label="å«ç¨åä»·(å
)" |
| | | type="number" |
| | | required |
| | | :rules="[{ required: true, message: '请è¾å
¥' }]" |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatTaxPrice(idx)" |
| | | /> |
| | | |
| | | <!-- æ°é --> |
| | | <van-field |
| | | v-model="product.quantity" |
| | | name="quantity" |
| | | label="æ°é" |
| | | type="number" |
| | | :rules="[{ required: true, message: '请è¾å
¥' }]" |
| | | required |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatAmount(idx)" |
| | | /> |
| | | |
| | | <!-- å«ç¨æ»ä»· --> |
| | | <van-field |
| | | v-model="product.taxInclusiveTotalPrice" |
| | | name="taxInclusiveTotalPrice" |
| | | label="å«ç¨æ»ä»·(å
)" |
| | | type="number" |
| | | :rules="[{ required: true, message: '请è¾å
¥' }]" |
| | | required |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatTaxTotal(idx)" |
| | | /> |
| | | |
| | | <!-- ä¸å«ç¨æ»ä»· --> |
| | | <van-field |
| | | v-model="product.taxExclusiveTotalPrice" |
| | | name="taxExclusiveTotalPrice" |
| | | label="ä¸å«ç¨æ»ä»·(å
)" |
| | | type="number" |
| | | required |
| | | :rules="[{ required: true, message: '请è¾å
¥' }]" |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatNoTaxTotal(idx)" |
| | | /> |
| | | |
| | | <!-- å票类å --> |
| | | <van-field |
| | | v-model="product.invoiceType" |
| | | is-link |
| | | readonly |
| | | name="invoiceType" |
| | | label="å票类å" |
| | | :rules="[{ required: true, message: 'è¯·éæ©' }]" |
| | | required |
| | | placeholder="è¯·éæ©" |
| | | @click="openInvoiceTypePicker(idx)" |
| | | /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view class="footer-btns" v-if="operationType !== 'view'"> |
| | | <van-button class="cancel-btn" @click="goBack">åæ¶</van-button> |
| | | <van-button class="save-btn" native-type="submit" form-type="submit">ä¿å</van-button> |
| | | </view> |
| | | </van-form> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from 'vue'; |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | const formRef = ref(); |
| | | const paymentMethods = ['对å
¬è½¬è´¦', 'ç°é', 'å
¶ä»']; |
| | | import {onMounted, ref} from 'vue'; |
| | | import {userListNoPage} from "@/api/system/user"; |
| | | import { |
| | | addOrUpdateSalesLedger, |
| | | addOrUpdateSalesLedgerProduct, |
| | | customerList, |
| | | getSalesLedgerWithProducts, |
| | | modelList, |
| | | productTreeList |
| | | } from "@/api/salesManagement/salesLedger"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import {calculateTaxExclusiveTotalPrice} from "@/utils/summarizeTable"; |
| | | |
| | | // è·å页é¢åæ° |
| | | const operationType = ref(''); |
| | | const editData = ref(null); |
| | | |
| | | const userStore = useUserStore() |
| | | const form = ref({ |
| | | id: '', |
| | | salesContractNo: '', |
| | | customerContract: '', |
| | | projectName: '', |
| | | contractAmount: '', |
| | | executionDate: '', |
| | | customerContractNo: '', |
| | | customerId: '', |
| | | customerName: '', |
| | | projectName: '', |
| | | executionDate: '', |
| | | paymentMethod: '', |
| | | entryPerson: '', |
| | | entryPersonName: '', |
| | | entryDate: '', |
| | | }); |
| | | const rules = { |
| | | salesContractNo: { |
| | | rules: [{ required: true, errorMessage: 'éå®ååå·ä¸è½ä¸ºç©º' }] |
| | | }, |
| | | customerContract: { |
| | | rules: [{ required: true, errorMessage: '客æ·ååä¸è½ä¸ºç©º' }] |
| | | }, |
| | | projectName: { |
| | | rules: [{ required: true, errorMessage: '项ç®åç§°ä¸è½ä¸ºç©º' }] |
| | | }, |
| | | contractAmount: { |
| | | rules: [{ required: true, errorMessage: 'ååéé¢ä¸è½ä¸ºç©º' }] |
| | | }, |
| | | executionDate: { |
| | | rules: [{ required: true, errorMessage: 'ç¾è®¢æ¥æä¸è½ä¸ºç©º' }] |
| | | }, |
| | | paymentMethod: { |
| | | rules: [{ required: true, errorMessage: 'è¯·éæ©ä»æ¬¾æ¹å¼' }] |
| | | } |
| | | }; |
| | | const products = ref([ |
| | | { |
| | | type: 'LS-29911', |
| | | unit: 'å¨åºé', |
| | | amount: '86590905972612', |
| | | taxRate: 'è¿éæ¯é¡¹ç®åç§°', |
| | | taxPrice: 'è¿éæ¯é¡¹ç®åç§°', |
| | | taxTotal: 'è¿éæ¯é¡¹ç®åç§°', |
| | | contractAmount: '32011å
', |
| | | operateDate: '2022-02-22 11:30:50', |
| | | remark: '', |
| | | }, |
| | | { |
| | | type: 'LS-29911', |
| | | unit: 'å¨åºé', |
| | | amount: '86590905972612', |
| | | taxRate: 'è¿éæ¯é¡¹ç®åç§°', |
| | | taxPrice: 'è¿éæ¯é¡¹ç®åç§°', |
| | | taxTotal: 'è¿éæ¯é¡¹ç®åç§°', |
| | | contractAmount: '32011å
', |
| | | operateDate: '2022-02-22 11:30:50', |
| | | remark: '', |
| | | }, |
| | | const pickerValue = ref(['']); |
| | | const pickerDateValue = ref([]); |
| | | const showPicker = ref(false); |
| | | const showDatePicker = ref(false); |
| | | const pickerCustomerValue = ref(['']); |
| | | const showCustomerPicker = ref(false); |
| | | const userList = ref([]); |
| | | const customerOption = ref([]); |
| | | const productData = ref([]); |
| | | |
| | | // éæ©å¨ç¸å
³åé |
| | | const showCategoryPicker = ref(false); |
| | | const showSpecificationPicker = ref(false); |
| | | const showTaxRatePicker = ref(false); |
| | | const showInvoiceTypePicker = ref(false); |
| | | const pickerCategoryValue = ref(['']); |
| | | const pickerSpecificationValue = ref(['']); |
| | | const pickerTaxRateValue = ref(['']); |
| | | const pickerInvoiceTypeValue = ref(['']); |
| | | const currentProductIndex = ref(0); |
| | | |
| | | // éé¡¹æ°æ® |
| | | const productOptions = ref([]); |
| | | const selectedCategoryNode = ref(null); |
| | | const defaultProps = ref({ |
| | | children: 'children', |
| | | label: 'label', |
| | | nodeKey: 'id' |
| | | }); |
| | | |
| | | const modelOptions = ref([]); |
| | | // 鲿¢å¾ªç¯è®¡ç®çæ å¿ |
| | | // const isCalculating = ref(false); |
| | | const taxRateOptions = ref([ |
| | | { text: '1', value: '1' }, |
| | | { text: '6', value: '6' }, |
| | | { text: '13', value: '13' }, |
| | | ]); |
| | | |
| | | const invoiceTypeOptions = ref([ |
| | | { text: '墿®ç¥¨', value: '墿®ç¥¨' }, |
| | | { text: 'å¢ä¸ç¥¨', value: 'å¢ä¸ç¥¨' }, |
| | | ]); |
| | | |
| | | const addProduct = () => { |
| | | products.value.push({ |
| | | type: '', |
| | | if (productData.value === null) { |
| | | productData.value = [] |
| | | } |
| | | productData.value.push({ |
| | | productCategory: '', |
| | | specificationModel: '', |
| | | productModelId: '', |
| | | unit: '', |
| | | amount: '', |
| | | taxRate: '', |
| | | taxPrice: '', |
| | | taxTotal: '', |
| | | contractAmount: '', |
| | | operateDate: '', |
| | | remark: '', |
| | | taxInclusiveUnitPrice: '', |
| | | quantity: '', |
| | | taxInclusiveTotalPrice: '', |
| | | taxExclusiveTotalPrice: '', |
| | | invoiceType: '' |
| | | }); |
| | | }; |
| | | const onConfirm = ({ selectedValues, selectedOptions }) => { |
| | | form.value.salesman = selectedOptions[0]?.text; |
| | | pickerValue.value = [selectedValues[0]]; |
| | | showPicker.value = false; |
| | | }; |
| | | const onCustomerConfirm = ({ selectedValues, selectedOptions }) => { |
| | | form.value.customerName = selectedOptions[0]?.text; |
| | | form.value.customerId = selectedOptions[0]?.value; |
| | | pickerCustomerValue.value = [selectedValues[0]]; |
| | | showCustomerPicker.value = false; |
| | | }; |
| | | const onDateConfirm = ({ selectedValues }) => { |
| | | form.value.executionDate = selectedValues.join('-'); |
| | | pickerDateValue.value = selectedValues; |
| | | showDatePicker.value = false; |
| | | }; |
| | | const removeProduct = (idx) => { |
| | | products.value.splice(idx, 1); |
| | | productData.value.splice(idx, 1); |
| | | }; |
| | | const submitForm = () => { |
| | | formRef.value.validate().then(() => { |
| | | uni.showToast({ title: 'ä¿åæå', icon: 'success' }); |
| | | }); |
| | | |
| | | // æ¾ç¤ºéæ©å¨ |
| | | const openCategoryPicker = (idx) => { |
| | | currentProductIndex.value = idx; |
| | | showCategoryPicker.value = true; |
| | | }; |
| | | |
| | | const openSpecificationPicker = (idx) => { |
| | | currentProductIndex.value = idx; |
| | | showSpecificationPicker.value = true; |
| | | }; |
| | | |
| | | const openTaxRatePicker = (idx) => { |
| | | currentProductIndex.value = idx; |
| | | showTaxRatePicker.value = true; |
| | | }; |
| | | |
| | | const openInvoiceTypePicker = (idx) => { |
| | | currentProductIndex.value = idx; |
| | | showInvoiceTypePicker.value = true; |
| | | }; |
| | | |
| | | // éæ©å¨ç¡®è®¤äºä»¶ |
| | | const onCategoryConfirm = (node) => { |
| | | // è·åéä¸çèç¹ä¿¡æ¯ |
| | | console.log('selected node---', node); |
| | | // åå¨éä¸çèç¹ï¼ç¨äºç¡®è®¤æ¶è·åæ°æ® |
| | | selectedCategoryNode.value = node; |
| | | }; |
| | | |
| | | // 确认产åå¤§ç±»éæ© |
| | | const confirmCategorySelection = () => { |
| | | if (selectedCategoryNode.value) { |
| | | // 设置éä¸ç产å大类 |
| | | productData.value[currentProductIndex.value].productCategory = selectedCategoryNode.value.label; |
| | | const id = selectedCategoryNode.value.id |
| | | // éç½®éä¸çèç¹ |
| | | selectedCategoryNode.value = null; |
| | | productData.value[currentProductIndex.value].specificationModel = '' |
| | | productData.value[currentProductIndex.value].productModelId = '' |
| | | productData.value[currentProductIndex.value].pickerSpecificationValue = [''] |
| | | getModels(id) |
| | | } |
| | | showCategoryPicker.value = false; |
| | | }; |
| | | // è·åè§æ ¼åå· |
| | | const getModels = (value) => { |
| | | modelList({ id: value }).then((res) => { |
| | | modelOptions.value = res.map(user => ({ |
| | | text: user.model, |
| | | value: user.id |
| | | })); |
| | | }); |
| | | }; |
| | | // éæ©è§æ ¼åå· |
| | | const onSpecificationConfirm = ({ selectedValues, selectedOptions }) => { |
| | | productData.value[currentProductIndex.value].specificationModel = selectedOptions[0]?.text; |
| | | productData.value[currentProductIndex.value].productModelId = selectedOptions[0]?.value; |
| | | productData.value[currentProductIndex.value].unit = selectedOptions[0]?.unit; |
| | | pickerSpecificationValue.value = [selectedValues[0]]; |
| | | showSpecificationPicker.value = false; |
| | | }; |
| | | // éæ©ç¨ç |
| | | const onTaxRateConfirm = ({ selectedValues, selectedOptions }) => { |
| | | productData.value[currentProductIndex.value].taxRate = selectedOptions[0]?.value; |
| | | pickerTaxRateValue.value = [selectedValues[0]]; |
| | | showTaxRatePicker.value = false; |
| | | // if (isCalculating.value) return; |
| | | const inclusiveTotalPrice = parseFloat(productData.value[currentProductIndex.value].taxInclusiveTotalPrice); |
| | | const taxRate = parseFloat(productData.value[currentProductIndex.value].taxRate); |
| | | if (!inclusiveTotalPrice || !taxRate) { |
| | | return; |
| | | } |
| | | // isCalculating.value = true; |
| | | // 计ç®ä¸å«ç¨æ»ä»· |
| | | productData.value[currentProductIndex.value].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice( |
| | | inclusiveTotalPrice, |
| | | taxRate |
| | | ); |
| | | // isCalculating.value = false; |
| | | }; |
| | | |
| | | const onInvoiceTypeConfirm = ({ selectedValues, selectedOptions }) => { |
| | | productData.value[currentProductIndex.value].invoiceType = selectedOptions[0]?.text; |
| | | pickerInvoiceTypeValue.value = [selectedValues[0]]; |
| | | showInvoiceTypePicker.value = false; |
| | | }; |
| | | |
| | | // æ ¼å¼å彿° - åºå®ä¸¤ä½å°æ° |
| | | const formatTaxPrice = (idx) => { |
| | | if (productData.value[idx].taxInclusiveUnitPrice) { |
| | | const value = parseFloat(productData.value[idx].taxInclusiveUnitPrice); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].taxInclusiveUnitPrice = value.toFixed(2); |
| | | } |
| | | } |
| | | if (!productData.value[currentProductIndex.value].taxRate) { |
| | | uni.showToast({ |
| | | title: '请å
éæ©ç¨ç', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | const quantity = parseFloat(productData.value[currentProductIndex.value].quantity); |
| | | const unitPrice = parseFloat(productData.value[currentProductIndex.value].taxInclusiveUnitPrice); |
| | | |
| | | if (!quantity || quantity <= 0 || !unitPrice) { |
| | | return; |
| | | } |
| | | // 计ç®å«ç¨æ»ä»· |
| | | productData.value[currentProductIndex.value].taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2); |
| | | |
| | | // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· |
| | | if (productData.value[currentProductIndex.value].taxRate) { |
| | | productData.value[currentProductIndex.value].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice( |
| | | productData.value[currentProductIndex.value].taxInclusiveTotalPrice, |
| | | productData.value[currentProductIndex.value].taxRate |
| | | ); |
| | | } |
| | | }; |
| | | // æ°éè¾å
¥æ¡å¤±ç¦ |
| | | const formatAmount = (idx) => { |
| | | if (productData.value[idx].quantity) { |
| | | const value = parseFloat(productData.value[idx].quantity); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].quantity = value.toFixed(2); |
| | | } |
| | | } |
| | | if (!productData.value[currentProductIndex.value].taxRate) { |
| | | uni.showToast({ |
| | | title: '请å
éæ©ç¨ç', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | const quantity = parseFloat(productData.value[currentProductIndex.value].quantity); |
| | | const unitPrice = parseFloat(productData.value[currentProductIndex.value].taxInclusiveUnitPrice); |
| | | |
| | | if (!quantity || quantity <= 0 || !unitPrice) { |
| | | return; |
| | | } |
| | | // 计ç®å«ç¨æ»ä»· |
| | | productData.value[currentProductIndex.value].taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2); |
| | | // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· |
| | | if (productData.value[currentProductIndex.value].taxRate) { |
| | | productData.value[currentProductIndex.value].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice( |
| | | productData.value[currentProductIndex.value].taxInclusiveTotalPrice, |
| | | productData.value[currentProductIndex.value].taxRate |
| | | ); |
| | | } |
| | | }; |
| | | // å«ç¨æ»ä»·å¤±ç¦ï¼æ ¹æ®å«ç¨æ»ä»·è®¡ç®å«ç¨åä»·åæ°é |
| | | const formatTaxTotal = (idx) => { |
| | | if (productData.value[idx].taxInclusiveTotalPrice) { |
| | | const value = parseFloat(productData.value[idx].taxInclusiveTotalPrice); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].taxInclusiveTotalPrice = value.toFixed(2); |
| | | } |
| | | } |
| | | const totalPrice = parseFloat(productData.value[currentProductIndex.value].taxInclusiveTotalPrice); |
| | | const quantity = parseFloat(productData.value[currentProductIndex.value].quantity); |
| | | |
| | | if (!totalPrice || !quantity || quantity <= 0) { |
| | | return; |
| | | } |
| | | // 计ç®å«ç¨åä»· = å«ç¨æ»ä»· / æ°é |
| | | productData.value[currentProductIndex.value].taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2); |
| | | // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· |
| | | if (productData.value[currentProductIndex.value].taxRate) { |
| | | productData.value[currentProductIndex.value].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice( |
| | | totalPrice, |
| | | productData.value[currentProductIndex.value].taxRate |
| | | ); |
| | | } |
| | | }; |
| | | // ä¸å«ç¨æ»ä»·å¤±ç¦, æ ¹æ®ä¸å«ç¨æ»ä»·è®¡ç®å«ç¨åä»·åæ°é |
| | | const formatNoTaxTotal = (idx) => { |
| | | if (productData.value[idx].taxExclusiveTotalPrice) { |
| | | const value = parseFloat(productData.value[idx].taxExclusiveTotalPrice); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].taxExclusiveTotalPrice = value.toFixed(2); |
| | | } |
| | | } |
| | | if (!productData.value[currentProductIndex.value].taxRate) { |
| | | uni.showToast({ |
| | | title: '请å
éæ©ç¨ç', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | const exclusiveTotalPrice = parseFloat(productData.value[currentProductIndex.value].taxExclusiveTotalPrice); |
| | | const quantity = parseFloat(productData.value[currentProductIndex.value].quantity); |
| | | const taxRate = parseFloat(productData.value[currentProductIndex.value].taxRate); |
| | | if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) { |
| | | return; |
| | | } |
| | | // å
计ç®å«ç¨æ»ä»· = ä¸å«ç¨æ»ä»· / (1 - ç¨ç/100) |
| | | const taxRateDecimal = taxRate / 100; |
| | | const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal); |
| | | productData.value[currentProductIndex.value].taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2); |
| | | // 计ç®å«ç¨åä»· = å«ç¨æ»ä»· / æ°é |
| | | productData.value[currentProductIndex.value].taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(2); |
| | | }; |
| | | const goBack = () => { |
| | | // æ¸
çæ¬å°åå¨çæ°æ® |
| | | uni.removeStorageSync('operationType'); |
| | | uni.removeStorageSync('editData'); |
| | | uni.navigateBack(); |
| | | }; |
| | | const onSubmit = () => { |
| | | if (productData.value !== null && productData.value.length > 0) { |
| | | form.value.productData = JSON.parse(JSON.stringify(productData.value)); |
| | | } else { |
| | | uni.showToast({ |
| | | title: '请添å 产åä¿¡æ¯', |
| | | icon: 'none' |
| | | }); |
| | | return |
| | | } |
| | | form.value.type = 1; |
| | | addOrUpdateSalesLedger(form.value).then((res) => { |
| | | uni.showToast({ |
| | | title: 'æäº¤æå', |
| | | icon: 'success', |
| | | }); |
| | | goBack(); |
| | | }); |
| | | }; |
| | | const setUserInfo = () => { |
| | | form.value.entryPerson = userStore.id; |
| | | form.value.entryPersonName = userStore.name; |
| | | // 设置å½å¤©æ¥æ |
| | | const today = new Date() |
| | | const year = today.getFullYear() |
| | | const month = String(today.getMonth() + 1).padStart(2, '0') |
| | | const day = String(today.getDate()).padStart(2, '0') |
| | | form.value.entryDate = `${year}-${month}-${day}` |
| | | pickerDateValue.value = [year.toString(), month.toString(), day.toString()] |
| | | } |
| | | // å¡«å
è¡¨åæ°æ®ï¼ç¼è¾æ¨¡å¼ï¼ |
| | | const fillFormData = () => { |
| | | if (!editData.value) return; |
| | | getSalesLedgerWithProducts({ id: editData.value.id, type: 1 }).then((res) => { |
| | | productData.value = res.productData; |
| | | }); |
| | | console.log(editData.value) |
| | | // å¡«å
åºæ¬ä¿¡æ¯ |
| | | form.value.salesContractNo = editData.value.salesContractNo || ''; |
| | | form.value.customerContractNo = editData.value.customerContractNo || ''; |
| | | form.value.customerName = editData.value.customerName || ''; |
| | | form.value.projectName = editData.value.projectName || ''; |
| | | form.value.executionDate = editData.value.executionDate || ''; |
| | | form.value.paymentMethod = editData.value.paymentMethod || ''; |
| | | form.value.salesman = editData.value.salesman || ''; |
| | | form.value.entryPerson = editData.value.entryPerson || ''; |
| | | form.value.entryPersonName = editData.value.entryPersonName || ''; |
| | | form.value.entryDate = editData.value.entryDate || ''; |
| | | form.value.id = editData.value.id || ''; |
| | | form.value.customerId = editData.value.customerId || ''; |
| | | |
| | | // 设置ä¸å¡åéæ©å¨çå¼ |
| | | if (editData.value.salesman) { |
| | | const salesmanIndex = userList.value.findIndex(user => user.text === editData.value.salesman); |
| | | if (salesmanIndex !== -1) { |
| | | pickerValue.value = [userList.value[salesmanIndex].value]; |
| | | } |
| | | } |
| | | |
| | | // 设置客æ·éæ©å¨çå¼ |
| | | if (editData.value.customerName) { |
| | | const customerIndex = customerOption.value.findIndex(customer => customer.text === editData.value.customerName); |
| | | if (customerIndex !== -1) { |
| | | pickerCustomerValue.value = [customerOption.value[customerIndex].value] |
| | | } |
| | | } |
| | | |
| | | // è®¾ç½®æ¥æéæ©å¨çå¼ |
| | | if (editData.value.executionDate) { |
| | | pickerDateValue.value = editData.value.executionDate.split('-').map(num => parseInt(num, 10)) |
| | | console.log(pickerDateValue.value) |
| | | } |
| | | }; |
| | | const getUserList = () => { |
| | | userListNoPage().then((res) => { |
| | | // å°ç¨æ·æ°æ®ç»è£
æ picker éè¦çæ ¼å¼ |
| | | userList.value = res.data.map(user => ({ |
| | | text: user.nickName, |
| | | value: user.nickName |
| | | })); |
| | | }) |
| | | } |
| | | const getCustomerList = () => { |
| | | customerList().then((res) => { |
| | | // å°ç¨æ·æ°æ®ç»è£
æ picker éè¦çæ ¼å¼ |
| | | customerOption.value = res.map(item => ({ |
| | | text: item.customerName, |
| | | value: item.id |
| | | })); |
| | | }) |
| | | } |
| | | const convertIdToValue = (data) => { |
| | | // å¦æä¼ å
¥ç䏿¯æ°ç»ï¼åè¿å空æ°ç» |
| | | if (!Array.isArray(data)) { |
| | | return []; |
| | | } |
| | | // é彿 å°å½æ° |
| | | return data.map(item => { |
| | | // å建æ°å¯¹è±¡ï¼æ å°å段 |
| | | const mappedItem = { |
| | | label: item.label, // å
³é®ï¼å° label æ å°ä¸º text |
| | | id: item.id, // ä¿ç id |
| | | }; |
| | | // 妿åå¨ children æ°ç»ï¼åéå½å¤ç |
| | | if (item.children && Array.isArray(item.children) && item.children.length > 0) { |
| | | mappedItem.children = convertIdToValue(item.children); |
| | | } |
| | | return mappedItem; |
| | | }); |
| | | }; |
| | | // è·å产å大类treeæ°æ® |
| | | const getProductOptions = () => { |
| | | productTreeList().then((res) => { |
| | | productOptions.value = convertIdToValue(res); |
| | | }); |
| | | }; |
| | | onMounted(() => { |
| | | // è·å页é¢åæ° |
| | | operationType.value = uni.getStorageSync('operationType') || ''; |
| | | |
| | | // è·å人åå表 |
| | | getUserList() |
| | | // è·å客æ·å表 |
| | | getCustomerList() |
| | | // è·å产å大类treeæ°æ® |
| | | getProductOptions() |
| | | // èµå¼é»è®¤ä¿¡æ¯ |
| | | if (operationType.value === 'add') { |
| | | setUserInfo() |
| | | } |
| | | |
| | | // è·åç¼è¾æ°æ®å¹¶å¡«å
表å |
| | | const editDataStr = uni.getStorageSync('editData'); |
| | | if (editDataStr) { |
| | | try { |
| | | editData.value = JSON.parse(editDataStr); |
| | | // 妿æ¯ç¼è¾æ¨¡å¼ï¼çå¾
æ°æ®å è½½å®æåå¡«å
è¡¨åæ°æ® |
| | | if (operationType.value !== 'add' && editData.value) { |
| | | // ä½¿ç¨ nextTick ç¡®ä¿æ°æ®å è½½å®æååå¡«å
|
| | | setTimeout(() => { |
| | | fillFormData(); |
| | | }, 100); |
| | | } |
| | | } catch (error) { |
| | | console.error('è§£æç¼è¾æ°æ®å¤±è´¥:', error); |
| | | } |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .account-detail { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 80px; |
| | | padding-bottom: 5rem; |
| | | } |
| | | .header { |
| | | display: flex; |
| | | align-items: center; |
| | | background: #fff; |
| | | padding: 16px 20px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | padding: 1rem 1.25rem; |
| | | border-bottom: 0.0625rem solid #f0f0f0; |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 100; |
| | | /* å
¼å®¹ iOS åæµ·/çµå¨å²å®å
¨åº */ |
| | | padding-top: env(safe-area-inset-top); |
| | | } |
| | | .title { |
| | | flex: 1; |
| | | text-align: center; |
| | | font-size: 18px; |
| | | font-size: 1.125rem; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | .form-section { |
| | | margin-top: 16px; |
| | | margin-top: 1rem; |
| | | } |
| | | .van-field { |
| | | height: 56px; |
| | | line-height: 36px; |
| | | height: 3.4rem; |
| | | } |
| | | .van-cell { |
| | | align-items: center; |
| | | } |
| | | .product-section { |
| | | background: #fff; |
| | | margin: 16px; |
| | | border-radius: 16px; |
| | | padding: 20px 16px 8px 16px; |
| | | box-shadow: 0 2px 8px rgba(0,0,0,0.04); |
| | | margin-top: 1rem; |
| | | padding: 1rem; |
| | | box-shadow: 0 0.125rem 0.5rem rgba(0,0,0,0.04); |
| | | } |
| | | .section-header { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-bottom: 12px; |
| | | margin-bottom: 1rem; |
| | | } |
| | | .section-title { |
| | | font-size: 16px; |
| | | font-size: 1rem; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | .add-btn { |
| | | background: #2979ff; |
| | | color: #fff; |
| | | border-radius: 8px; |
| | | padding: 4px 16px; |
| | | font-size: 14px; |
| | | border-radius: 0.25rem; |
| | | } |
| | | .product-card { |
| | | background: #f8f9fa; |
| | | border-radius: 12px; |
| | | padding: 12px; |
| | | margin-bottom: 16px; |
| | | box-shadow: 0 1px 4px rgba(41,121,255,0.06); |
| | | background: #FFFFFF; |
| | | box-shadow: 0 0 1.25rem 0 rgba(0,57,117,0.08); |
| | | border-radius: 0.5rem 0.5rem 0.5rem 0.5rem; |
| | | padding: 1rem 0.5rem 0 0.5rem; |
| | | position: relative; |
| | | } |
| | | .product-row { |
| | | .product-header { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 8px; |
| | | justify-content: space-between; |
| | | padding: 0 0.5rem 0.75rem 0.5rem; |
| | | border-bottom: 0.0625rem solid #e8e8e8; |
| | | } |
| | | .product-label { |
| | | min-width: 60px; |
| | | color: #888; |
| | | font-size: 13px; |
| | | .product-productCategory { |
| | | margin-left: 0.5rem; |
| | | font-size: 0.875rem; |
| | | font-weight: 500; |
| | | color: #333; |
| | | } |
| | | .del-row { |
| | | justify-content: flex-end; |
| | | .info-grid { |
| | | display: grid; |
| | | grid-template-columns: 1fr 1fr; |
| | | gap: 0.75rem; |
| | | margin-bottom: 1rem; |
| | | } |
| | | .info-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 0.25rem; |
| | | } |
| | | .info-label { |
| | | font-size: 0.75rem; |
| | | color: #666; |
| | | font-weight: 400; |
| | | } |
| | | .info-value { |
| | | font-size: 0.875rem; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | .info-value.highlight { |
| | | color: #2979ff; |
| | | font-weight: 600; |
| | | } |
| | | .product-form { |
| | | margin-bottom: 1rem; |
| | | } |
| | | .del-btn { |
| | | background: #ff4d4f; |
| | | color: #fff; |
| | | border-radius: 8px; |
| | | padding: 4px 16px; |
| | | font-size: 13px; |
| | | margin-top: 4px; |
| | | border-radius: 0.25rem; |
| | | } |
| | | .footer-btns { |
| | | position: fixed; |
| | |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | padding: 12px 0; |
| | | box-shadow: 0 -2px 8px rgba(0,0,0,0.05); |
| | | padding: 0.75rem 0; |
| | | box-shadow: 0 -0.125rem 0.5rem rgba(0,0,0,0.05); |
| | | z-index: 1000; |
| | | } |
| | | .cancel-btn { |
| | | font-weight: 400; |
| | | font-size: 16px; |
| | | font-size: 1rem; |
| | | color: #FFFFFF; |
| | | width: 102px; |
| | | width: 6.375rem; |
| | | background: #C7C9CC; |
| | | box-shadow: 0px 4px 10px 0px rgba(3,88,185,0.2); |
| | | border-radius: 40px 40px 40px 40px; |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | .save-btn { |
| | | font-weight: 400; |
| | | font-size: 16px; |
| | | font-size: 1rem; |
| | | color: #FFFFFF; |
| | | width: 224px; |
| | | width: 14rem; |
| | | background: linear-gradient( 140deg, #00BAFF 0%, #006CFB 100%); |
| | | box-shadow: 0px 4px 10px 0px rgba(3,88,185,0.2); |
| | | border-radius: 40px 40px 40px 40px; |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | .popup-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 1rem; |
| | | background: #fff; |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 10; |
| | | } |
| | | .cancelButton { |
| | | color: #969799 |
| | | } |
| | | .confirmButton { |
| | | color: #1989FA |
| | | } |
| | | .u-tree { |
| | | height: 13rem; |
| | | } |
| | | </style> |