| | |
| | | <PageHeader title="台账详情" @back="goBack" /> |
| | | |
| | | <!-- 表单区域 --> |
| | | <u-form @submit="onSubmit" label-width="110" input-align="right" style="margin-top: 10px" error-message-align="right"> |
| | | <u-form-item label="销售合同号" prop="salesContractNo" border-bottom> |
| | | <u-input v-model="form.salesContractNo" placeholder="自动生成" disabled /> |
| | | </u-form-item> |
| | | <u-form-item |
| | | <up-form @submit="onSubmit" label-width="110" ref="formRef" :rules="rules" :model="form"> |
| | | |
| | | <up-form-item label="销售合同号" prop="salesContractNo" > |
| | | <up-input v-model="form.salesContractNo" placeholder="自动生成" disabled /> |
| | | </up-form-item> |
| | | <up-form-item |
| | | label="业务员" |
| | | prop="salesman" |
| | | required |
| | | border-bottom |
| | | @click="showPicker = true" |
| | | > |
| | | <u-input |
| | | <up-input |
| | | v-model="form.salesman" |
| | | readonly |
| | | placeholder="点击选择业务员" |
| | | readonly="" |
| | | @click="showPicker = true" |
| | | placeholder="点击选择业务员" |
| | | /> |
| | | </u-form-item> |
| | | <u-form-item label="客户合同号" prop="customerContractNo" required border-bottom> |
| | | <u-input |
| | | <template #right> |
| | | <up-icon |
| | | name="arrow-right" |
| | | @click="showPicker = true" |
| | | ></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="客户合同号" prop="customerContractNo" required > |
| | | <up-input |
| | | v-model="form.customerContractNo" |
| | | placeholder="请输入客户合同号" |
| | | /> |
| | | </u-form-item> |
| | | <u-form-item |
| | | </up-form-item> |
| | | <up-form-item |
| | | label="客户名称" |
| | | prop="customerName" |
| | | required |
| | | border-bottom |
| | | > |
| | | <u-input |
| | | <up-input |
| | | v-model="form.customerName" |
| | | readonly |
| | | placeholder="点击选择客户" |
| | | @click="showCustomerPicker = true" |
| | | /> |
| | | </u-form-item> |
| | | <u-form-item label="项目名称" prop="projectName" required border-bottom> |
| | | <u-input v-model="form.projectName" placeholder="请输入项目名称" /> |
| | | </u-form-item> |
| | | <u-form-item |
| | | <template #right> |
| | | <up-icon |
| | | name="arrow-right" |
| | | @click="showCustomerPicker = true" |
| | | ></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="项目名称" prop="projectName" required > |
| | | <up-input v-model="form.projectName" placeholder="请输入项目名称" /> |
| | | </up-form-item> |
| | | <up-form-item |
| | | label="签订日期" |
| | | prop="executionDate" |
| | | required |
| | | border-bottom |
| | | > |
| | | <u-input |
| | | <up-input |
| | | v-model="form.executionDate" |
| | | readonly |
| | | placeholder="点击选择时间" |
| | | @click="showDatePicker = true" |
| | | /> |
| | | </u-form-item> |
| | | <u-popup v-model="showDatePicker" mode="bottom"> |
| | | <u-datetime-picker |
| | | <template #right> |
| | | <up-icon |
| | | name="arrow-right" |
| | | @click="showDatePicker = true" |
| | | ></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="付款方式" prop="paymentMethod" > |
| | | <up-input v-model="form.paymentMethod" placeholder="请输入付款方式" /> |
| | | </up-form-item> |
| | | <up-form-item label="录入人" prop="entryPersonName" > |
| | | <up-input v-model="form.entryPersonName" placeholder="请输入" disabled /> |
| | | </up-form-item> |
| | | <up-form-item label="录入日期" prop="entryDate" > |
| | | <up-input v-model="form.entryDate" placeholder="请输入" disabled /> |
| | | </up-form-item> |
| | | <!-- 业务员选择 --> |
| | | <up-action-sheet |
| | | :show="showPicker" |
| | | :actions="userActionList" |
| | | title="选择业务员" |
| | | @select="onSalesmanSelect" |
| | | @close="showPicker = false" |
| | | /> |
| | | |
| | | <!-- 日期选择 --> |
| | | <up-popup :show="showDatePicker" mode="bottom" @close="showDatePicker = false"> |
| | | <up-datetime-picker |
| | | :show="true" |
| | | v-model="pickerDateValue" |
| | | @confirm="onDateConfirm" |
| | | @cancel="showDatePicker = false" |
| | | mode="date" |
| | | /> |
| | | </u-popup> |
| | | <u-form-item label="付款方式" prop="paymentMethod" border-bottom> |
| | | <u-input v-model="form.paymentMethod" placeholder="请输入付款方式" /> |
| | | </u-form-item> |
| | | <u-form-item label="录入人" prop="entryPersonName" border-bottom> |
| | | <u-input v-model="form.entryPersonName" placeholder="请输入" disabled /> |
| | | </u-form-item> |
| | | <u-form-item label="录入日期" prop="entryDate" border-bottom> |
| | | <u-input v-model="form.entryDate" placeholder="请输入" disabled /> |
| | | </u-form-item> |
| | | <!-- 业务员选择弹窗 --> |
| | | <u-popup v-model="showPicker" mode="bottom"> |
| | | <view class="picker-header"> |
| | | <view class="picker-cancel" @click="showPicker = false">取消</view> |
| | | <view class="picker-title">选择业务员</view> |
| | | <view class="picker-confirm" @click="confirmSalesman">确定</view> |
| | | </view> |
| | | <u-picker |
| | | :columns="userList" |
| | | v-model="pickerValue" |
| | | @change="onPickerChange" |
| | | /> |
| | | </u-popup> |
| | | |
| | | <!-- 客户选择弹窗 --> |
| | | <u-popup v-model="showCustomerPicker" mode="bottom"> |
| | | <view class="picker-header"> |
| | | <view class="picker-cancel" @click="showCustomerPicker = false">取消</view> |
| | | <view class="picker-title">选择客户</view> |
| | | <view class="picker-confirm" @click="confirmCustomer">确定</view> |
| | | </view> |
| | | <u-picker |
| | | :columns="customerOption" |
| | | v-model="pickerCustomerValue" |
| | | @change="onCustomerPickerChange" |
| | | /> |
| | | </u-popup> |
| | | </up-popup> |
| | | <!-- 客户选择 --> |
| | | <up-action-sheet |
| | | :show="showCustomerPicker" |
| | | :actions="customerActionList" |
| | | title="选择客户" |
| | | @select="onCustomerSelect" |
| | | @close="showCustomerPicker = false" |
| | | /> |
| | | |
| | | <!-- 产品大类选择器 --> |
| | | <u-popup v-model="showCategoryPicker" mode="bottom"> |
| | | <up-popup :show="showCategoryPicker" mode="bottom"> |
| | | <!-- 头部按钮区域 --> |
| | | <view class="popup-header"> |
| | | <view @click="showCategoryPicker = false" class="cancelButton">取消</view> |
| | | <view @click="confirmCategorySelection" class="confirmButton">确定</view> |
| | | </view> |
| | | <up-tree |
| | | <u-tree |
| | | :data="productOptions" |
| | | :props="defaultProps" |
| | | show-checkbox |
| | |
| | | check-strictly |
| | | @check-change="onCategoryConfirm" |
| | | /> |
| | | </u-popup> |
| | | </up-popup> |
| | | |
| | | <!-- 规格型号选择器 --> |
| | | <u-popup v-model="showSpecificationPicker" mode="bottom"> |
| | | <u-picker |
| | | :columns="modelOptions" |
| | | v-model="pickerSpecificationValue" |
| | | @confirm="onSpecificationConfirm" |
| | | @cancel="showSpecificationPicker = false" |
| | | /> |
| | | </u-popup> |
| | | <up-action-sheet |
| | | :show="showSpecificationPicker" |
| | | :actions="specificationActionList" |
| | | title="选择规格型号" |
| | | @select="onSpecificationSelect" |
| | | @close="showSpecificationPicker = false" |
| | | /> |
| | | |
| | | <!-- 税率选择器 --> |
| | | <u-popup v-model="showTaxRatePicker" mode="bottom"> |
| | | <u-picker |
| | | :columns="taxRateOptions" |
| | | v-model="pickerTaxRateValue" |
| | | @confirm="onTaxRateConfirm" |
| | | @cancel="showTaxRatePicker = false" |
| | | /> |
| | | </u-popup> |
| | | <up-action-sheet |
| | | :show="showTaxRatePicker" |
| | | :actions="taxRateActionList" |
| | | title="选择税率" |
| | | @select="onTaxRateSelect" |
| | | @close="showTaxRatePicker = false" |
| | | /> |
| | | |
| | | <!-- 发票类型选择器 --> |
| | | <u-popup v-model="showInvoiceTypePicker" mode="bottom"> |
| | | <u-picker |
| | | :columns="invoiceTypeOptions" |
| | | v-model="pickerInvoiceTypeValue" |
| | | @confirm="onInvoiceTypeConfirm" |
| | | @cancel="showInvoiceTypePicker = false" |
| | | /> |
| | | </u-popup> |
| | | <up-action-sheet |
| | | :show="showInvoiceTypePicker" |
| | | :actions="invoiceTypeActionList" |
| | | title="选择发票类型" |
| | | @select="onInvoiceTypeSelect" |
| | | @close="showInvoiceTypePicker = false" |
| | | /> |
| | | <!-- 产品信息 --> |
| | | <view class="product-section"> |
| | | <view class="section-header"> |
| | | <text class="section-title">产品信息</text> |
| | | <u-button type="primary" size="small" @click="addProduct" class="add-btn" v-if="operationType !== 'view'"> |
| | | <u-icon name="plus" size="14" /> |
| | | 新增 |
| | | </u-button> |
| | | <view> |
| | | <text class="section-title">产品信息</text> |
| | | </view> |
| | | <view> |
| | | <up-button type="primary" size="small" @click="addProduct" class="add-btn" v-if="operationType !== 'view'"> |
| | | 新增 |
| | | </up-button> |
| | | </view> |
| | | </view> |
| | | <view class="product-card" v-for="(product, idx) in productData" :key="idx"> |
| | | <!-- 产品类 --> |
| | | <view class="product-header"> |
| | | <view class="product-title"> |
| | | <u-icon name="file-text" color="#2979ff" size="15" /> |
| | | <view class="document-icon"> |
| | | <up-icon name="file-text" size="16" color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="product-productCategory">产品 {{ idx + 1 }}</text> |
| | | </view> |
| | | <!-- 操作按钮 --> |
| | | <view class="product-actions" v-if="operationType !== 'view'"> |
| | | <u-button type="error" size="mini" @click="removeProduct(idx)" class="del-btn"> |
| | | <u-icon name="trash" size="12" /> |
| | | <up-button type="error" size="mini" @click="removeProduct(idx)" class="del-btn"> |
| | | 删除 |
| | | </u-button> |
| | | </up-button> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 产品信息表单 --> |
| | | <view class="product-form"> |
| | | <!-- 产品大类 --> |
| | | <u-form-item |
| | | <up-form-item |
| | | label="产品大类" |
| | | prop="productCategory" |
| | | required |
| | | border-bottom |
| | | :rules="productRules" |
| | | > |
| | | <u-input |
| | | <up-input |
| | | v-model="product.productCategory" |
| | | readonly |
| | | placeholder="请选择" |
| | | @click="openCategoryPicker(idx)" |
| | | /> |
| | | </u-form-item> |
| | | <template #right> |
| | | <up-icon |
| | | name="arrow-right" |
| | | @click="showCategoryPicker = true" |
| | | ></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | |
| | | <!-- 规格型号 --> |
| | | <u-form-item |
| | | <up-form-item |
| | | label="规格型号" |
| | | prop="specificationModel" |
| | | required |
| | | border-bottom |
| | | :rules="productRules" |
| | | > |
| | | <u-input |
| | | <up-input |
| | | v-model="product.specificationModel" |
| | | readonly |
| | | placeholder="请选择" |
| | | @click="openSpecificationPicker(idx)" |
| | | /> |
| | | </u-form-item> |
| | | <template #right> |
| | | <up-icon |
| | | name="arrow-right" |
| | | @click="showSpecificationPicker = true" |
| | | ></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | |
| | | <!-- 单位 --> |
| | | <u-form-item |
| | | <up-form-item |
| | | label="单位" |
| | | prop="unit" |
| | | required |
| | | border-bottom |
| | | :rules="productRules" |
| | | > |
| | | <u-input |
| | | <up-input |
| | | v-model="product.unit" |
| | | placeholder="请输入" |
| | | /> |
| | | </u-form-item> |
| | | </up-form-item> |
| | | |
| | | <!-- 税率 --> |
| | | <u-form-item |
| | | <up-form-item |
| | | label="税率(%)" |
| | | prop="taxRate" |
| | | required |
| | | border-bottom |
| | | :rules="productRules" |
| | | > |
| | | <u-input |
| | | <up-input |
| | | v-model="product.taxRate" |
| | | readonly |
| | | placeholder="请选择" |
| | | @click="openTaxRatePicker(idx)" |
| | | /> |
| | | </u-form-item> |
| | | <template #right> |
| | | <up-icon |
| | | name="arrow-right" |
| | | @click="showTaxRatePicker = true" |
| | | ></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | |
| | | <!-- 含税单价 --> |
| | | <u-form-item |
| | | <up-form-item |
| | | label="含税单价(元)" |
| | | prop="taxInclusiveUnitPrice" |
| | | required |
| | | border-bottom |
| | | :rules="productRules" |
| | | > |
| | | <u-input |
| | | <up-input |
| | | v-model="product.taxInclusiveUnitPrice" |
| | | type="number" |
| | | placeholder="请输入" |
| | | @blur="formatTaxPrice(idx)" |
| | | /> |
| | | </u-form-item> |
| | | </up-form-item> |
| | | |
| | | <!-- 数量 --> |
| | | <u-form-item |
| | | <up-form-item |
| | | label="数量" |
| | | prop="quantity" |
| | | required |
| | | border-bottom |
| | | :rules="productRules" |
| | | > |
| | | <u-input |
| | | <up-input |
| | | v-model="product.quantity" |
| | | type="number" |
| | | placeholder="请输入" |
| | | @blur="formatAmount(idx)" |
| | | /> |
| | | </u-form-item> |
| | | </up-form-item> |
| | | |
| | | <!-- 含税总价 --> |
| | | <u-form-item |
| | | <up-form-item |
| | | label="含税总价(元)" |
| | | prop="taxInclusiveTotalPrice" |
| | | required |
| | | border-bottom |
| | | :rules="productRules" |
| | | > |
| | | <u-input |
| | | <up-input |
| | | v-model="product.taxInclusiveTotalPrice" |
| | | type="number" |
| | | placeholder="请输入" |
| | | @blur="formatTaxTotal(idx)" |
| | | /> |
| | | </u-form-item> |
| | | </up-form-item> |
| | | |
| | | <!-- 不含税总价 --> |
| | | <u-form-item |
| | | <up-form-item |
| | | label="不含税总价(元)" |
| | | prop="taxExclusiveTotalPrice" |
| | | required |
| | | border-bottom |
| | | :rules="productRules" |
| | | > |
| | | <u-input |
| | | <up-input |
| | | v-model="product.taxExclusiveTotalPrice" |
| | | type="number" |
| | | placeholder="请输入" |
| | | @blur="formatNoTaxTotal(idx)" |
| | | /> |
| | | </u-form-item> |
| | | </up-form-item> |
| | | |
| | | <!-- 发票类型 --> |
| | | <u-form-item |
| | | <up-form-item |
| | | label="发票类型" |
| | | prop="invoiceType" |
| | | required |
| | | border-bottom |
| | | :rules="productRules" |
| | | > |
| | | <u-input |
| | | <up-input |
| | | v-model="product.invoiceType" |
| | | readonly |
| | | placeholder="请选择" |
| | | @click="openInvoiceTypePicker(idx)" |
| | | /> |
| | | </u-form-item> |
| | | <template #right> |
| | | <up-icon |
| | | name="arrow-right" |
| | | @click="showInvoiceTypePicker = true" |
| | | ></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </u-form> |
| | | </up-form> |
| | | |
| | | <!-- 使用公共底部按钮组件 --> |
| | | <FooterButtons |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {onMounted, ref} from 'vue'; |
| | | import {onMounted, ref, computed} from 'vue'; |
| | | import {userListNoPage} from "@/api/system/user"; |
| | | import { formatDateToYMD } from '@/utils/ruoyi' |
| | | import { |
| | | addOrUpdateSalesLedger, |
| | | addOrUpdateSalesLedgerProduct, |
| | |
| | | // 获取页面参数 |
| | | const operationType = ref(''); |
| | | const editData = ref(null); |
| | | const formRef = ref(null); |
| | | |
| | | const userStore = useUserStore() |
| | | const form = ref({ |
| | |
| | | entryPersonName: '', |
| | | entryDate: '', |
| | | }); |
| | | const pickerValue = ref(['']); |
| | | const pickerDateValue = ref([]); |
| | | const showPicker = ref(false); |
| | | const showDatePicker = ref(false); |
| | | const pickerCustomerValue = ref(['']); |
| | | const pickerDateValue = ref(Date.now()); |
| | | const showCustomerPicker = ref(false); |
| | | const userList = ref([]); |
| | | const customerOption = ref([]); |
| | | const userActionList = computed(() => { |
| | | return userList.value.map(user => ({ |
| | | name: user.text, |
| | | value: user.value |
| | | })) |
| | | }) |
| | | const formatter = (type, value) => { |
| | | if (type === 'year') { |
| | | return `${value}`; |
| | | } |
| | | if (type === 'month') { |
| | | return `${value}`; |
| | | } |
| | | if (type === 'day') { |
| | | return `${value}`; |
| | | } |
| | | return value; |
| | | }; |
| | | const customerActionList = computed(() => { |
| | | return customerOption.value.map(customer => ({ |
| | | name: customer.text, |
| | | value: customer.value |
| | | })) |
| | | }) |
| | | |
| | | // 日期选择列表已移除,改用 up-datetime-picker |
| | | |
| | | // 产品大类选择列表 |
| | | const categoryActionList = computed(() => { |
| | | const flattenCategories = (categories, result = []) => { |
| | | categories.forEach(category => { |
| | | result.push({ |
| | | name: category.label, |
| | | value: category.id |
| | | }) |
| | | if (category.children && category.children.length > 0) { |
| | | flattenCategories(category.children, result) |
| | | } |
| | | }) |
| | | return result |
| | | } |
| | | return flattenCategories(productOptions.value) |
| | | }) |
| | | |
| | | // 规格型号选择列表 |
| | | const specificationActionList = computed(() => { |
| | | return modelOptions.value.map(model => ({ |
| | | name: model.text, |
| | | value: model.value, |
| | | unit: model.unit |
| | | })) |
| | | }) |
| | | |
| | | // 税率选择列表 |
| | | const taxRateActionList = computed(() => { |
| | | return taxRateOptions.value.map(rate => ({ |
| | | name: rate.text, |
| | | value: rate.value |
| | | })) |
| | | }) |
| | | |
| | | // 发票类型选择列表 |
| | | const invoiceTypeActionList = computed(() => { |
| | | return invoiceTypeOptions.value.map(type => ({ |
| | | name: type.text, |
| | | value: type.value |
| | | })) |
| | | }) |
| | | const productData = ref([]); |
| | | |
| | | // 选择器相关变量 |
| | |
| | | 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 tempSalesmanValue = ref(''); |
| | | const tempCustomerValue = ref(''); |
| | | const selectedSalesman = ref(null); |
| | | const selectedCustomer = ref(null); |
| | | // 临时变量已不再需要 |
| | | const currentProductIndex = ref(0); |
| | | |
| | | // 选项数据 |
| | |
| | | { text: '增专票', value: '增专票' }, |
| | | ]); |
| | | |
| | | // 表单校验规则 |
| | | const rules = { |
| | | salesman: [ |
| | | { required: true, message: '请选择业务员', trigger: 'blur' } |
| | | ], |
| | | customerContractNo: [ |
| | | { required: true, message: '请输入客户合同号', trigger: 'blur' } |
| | | ], |
| | | customerName: [ |
| | | { required: true, message: '请选择客户名称', trigger: 'blur' } |
| | | ], |
| | | projectName: [ |
| | | { required: true, message: '请输入项目名称', trigger: 'blur' } |
| | | ], |
| | | executionDate: [ |
| | | { required: true, message: '请选择签订日期', trigger: 'blur' } |
| | | ] |
| | | }; |
| | | |
| | | // 产品信息校验规则 |
| | | const productRules = { |
| | | productCategory: [ |
| | | { required: true, message: '请选择产品大类', trigger: 'blur' } |
| | | ], |
| | | specificationModel: [ |
| | | { required: true, message: '请选择规格型号', trigger: 'blur' } |
| | | ], |
| | | unit: [ |
| | | { required: true, message: '请输入单位', trigger: 'blur' } |
| | | ], |
| | | taxRate: [ |
| | | { required: true, message: '请选择税率', trigger: 'blur' } |
| | | ], |
| | | taxInclusiveUnitPrice: [ |
| | | { required: true, message: '请输入含税单价', trigger: 'blur' }, |
| | | { type: 'number', min: 0, message: '含税单价必须大于0', trigger: 'blur' } |
| | | ], |
| | | quantity: [ |
| | | { required: true, message: '请输入数量', trigger: 'blur' }, |
| | | { type: 'number', min: 0, message: '数量必须大于0', trigger: 'blur' } |
| | | ], |
| | | taxInclusiveTotalPrice: [ |
| | | { required: true, message: '请输入含税总价', trigger: 'blur' }, |
| | | { type: 'number', min: 0, message: '含税总价必须大于0', trigger: 'blur' } |
| | | ], |
| | | taxExclusiveTotalPrice: [ |
| | | { required: true, message: '请输入不含税总价', trigger: 'blur' }, |
| | | { type: 'number', min: 0, message: '不含税总价必须大于0', trigger: 'blur' } |
| | | ], |
| | | invoiceType: [ |
| | | { required: true, message: '请选择发票类型', trigger: 'blur' } |
| | | ] |
| | | }; |
| | | |
| | | const addProduct = () => { |
| | | if (productData.value === null) { |
| | | productData.value = [] |
| | |
| | | invoiceType: '' |
| | | }); |
| | | }; |
| | | // 业务员选择器变化事件 |
| | | const onPickerChange = ({ selectedValues, selectedOptions }) => { |
| | | selectedSalesman.value = selectedOptions[0]; |
| | | tempSalesmanValue.value = { |
| | | text: selectedOptions[0]?.text, |
| | | value: selectedOptions[0]?.value |
| | | }; |
| | | }; |
| | | // 业务员选择事件 |
| | | const onSalesmanSelect = (item) => { |
| | | form.value.salesman = item.name |
| | | } |
| | | |
| | | // 确认选择业务员 |
| | | const confirmSalesman = () => { |
| | | if (selectedSalesman.value) { |
| | | form.value.salesman = selectedSalesman.value.text; |
| | | pickerValue.value = [selectedSalesman.value.value]; |
| | | } |
| | | showPicker.value = false; |
| | | }; |
| | | |
| | | // 客户选择器变化事件 |
| | | const onCustomerPickerChange = ({ selectedValues, selectedOptions }) => { |
| | | selectedCustomer.value = selectedOptions[0]; |
| | | tempCustomerValue.value = { |
| | | text: selectedOptions[0]?.text, |
| | | value: selectedOptions[0]?.value |
| | | }; |
| | | }; |
| | | |
| | | // 确认选择客户 |
| | | const confirmCustomer = () => { |
| | | if (selectedCustomer.value) { |
| | | form.value.customerName = selectedCustomer.value.text; |
| | | form.value.customerId = selectedCustomer.value.value; |
| | | pickerCustomerValue.value = [selectedCustomer.value.value]; |
| | | } |
| | | showCustomerPicker.value = false; |
| | | }; |
| | | |
| | | // 修改原有的确认方法(保持兼容性) |
| | | const onConfirm = ({ selectedValues, selectedOptions }) => { |
| | | if (selectedOptions && selectedOptions[0]) { |
| | | form.value.salesman = selectedOptions[0].text; |
| | | pickerValue.value = [selectedValues[0]]; |
| | | } |
| | | showPicker.value = false; |
| | | }; |
| | | |
| | | const onCustomerConfirm = ({ selectedValues, selectedOptions }) => { |
| | | if (selectedOptions && selectedOptions[0]) { |
| | | 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; |
| | | // 日期确认事件 |
| | | const onDateConfirm = (e) => { |
| | | form.value.executionDate = formatDateToYMD(e.value) |
| | | pickerDateValue.value = formatDateToYMD(e.value) |
| | | showDatePicker.value = false; |
| | | }; |
| | | } |
| | | |
| | | // 客户选择事件 |
| | | const onCustomerSelect = (item) => { |
| | | form.value.customerName = item.name |
| | | form.value.customerId = item.value |
| | | } |
| | | |
| | | // 原有的确认方法已被新的action-sheet选择方法替代 |
| | | const removeProduct = (idx) => { |
| | | productData.value.splice(idx, 1); |
| | | }; |
| | |
| | | selectedCategoryNode.value = null; |
| | | productData.value[currentProductIndex.value].specificationModel = '' |
| | | productData.value[currentProductIndex.value].productModelId = '' |
| | | productData.value[currentProductIndex.value].pickerSpecificationValue = [''] |
| | | getModels(id) |
| | | } |
| | | showCategoryPicker.value = false; |
| | |
| | | })); |
| | | }); |
| | | }; |
| | | // 选择规格型号 |
| | | 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; |
| | | // 规格型号选择事件 |
| | | const onSpecificationSelect = (item) => { |
| | | productData.value[currentProductIndex.value].specificationModel = item.name |
| | | productData.value[currentProductIndex.value].productModelId = item.value |
| | | productData.value[currentProductIndex.value].unit = item.unit |
| | | } |
| | | // 税率选择事件 |
| | | const onTaxRateSelect = (item) => { |
| | | productData.value[currentProductIndex.value].taxRate = item.value |
| | | // 重新计算不含税总价 |
| | | const inclusiveTotalPrice = parseFloat(productData.value[currentProductIndex.value].taxInclusiveTotalPrice) |
| | | const taxRate = parseFloat(item.value) |
| | | if (inclusiveTotalPrice && taxRate) { |
| | | productData.value[currentProductIndex.value].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice(inclusiveTotalPrice, taxRate) |
| | | } |
| | | // 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 onInvoiceTypeSelect = (item) => { |
| | | productData.value[currentProductIndex.value].invoiceType = item.name |
| | | }; |
| | | |
| | | // 格式化函数 - 固定两位小数 |
| | |
| | | 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 { |
| | | const onSubmit = async () => { |
| | | // 首先校验基本表单 |
| | | const formValid = await formRef.value.validate().catch(() => false); |
| | | if (!formValid) { |
| | | return; |
| | | } |
| | | |
| | | // 校验产品信息 |
| | | if (!productData.value || productData.value.length === 0) { |
| | | uni.showToast({ |
| | | title: '请添加产品信息', |
| | | icon: 'none' |
| | | }); |
| | | return |
| | | return; |
| | | } |
| | | |
| | | // 检查每个产品是否填写完整 |
| | | for (let i = 0; i < productData.value.length; i++) { |
| | | const errors = validateProduct(productData.value[i], i); |
| | | if (errors.length > 0) { |
| | | uni.showToast({ |
| | | title: errors[0], |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // 表单校验通过,提交数据 |
| | | form.value.productData = JSON.parse(JSON.stringify(productData.value)); |
| | | form.value.type = 1; |
| | | addOrUpdateSalesLedger(form.value).then((res) => { |
| | | uni.showToast({ |
| | |
| | | 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()] |
| | | // 设置日期选择器默认值为今天 |
| | | pickerDateValue.value = `${year}-${month}-${day}` |
| | | } |
| | | // 填充表单数据(编辑模式) |
| | | const fillFormData = () => { |
| | |
| | | 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) |
| | | pickerDateValue.value = editData.value.executionDate |
| | | } |
| | | }; |
| | | const getUserList = () => { |
| | | userListNoPage().then((res) => { |
| | | // 确保数据格式正确 |
| | | userList.value = [res.data.map(user => ({ |
| | | // 移除多余的数组包装 |
| | | userList.value = res.data.map(user => ({ |
| | | text: user.nickName, |
| | | value: user.nickName |
| | | }))]; |
| | | })); |
| | | }) |
| | | } |
| | | }; |
| | | const getCustomerList = () => { |
| | | customerList().then((res) => { |
| | | // 确保数据格式正确 |
| | | customerOption.value = [res.map(item => ({ |
| | | // 移除多余的数组包装 |
| | | customerOption.value = res.map(item => ({ |
| | | text: item.customerName, |
| | | value: item.id |
| | | }))]; |
| | | })); |
| | | }) |
| | | } |
| | | }; |
| | | const convertIdToValue = (data) => { |
| | | // 如果传入的不是数组,则返回空数组 |
| | | if (!Array.isArray(data)) { |
| | |
| | | productOptions.value = convertIdToValue(res); |
| | | }); |
| | | }; |
| | | // 单个产品表单验证函数 |
| | | const validateProduct = (product, index) => { |
| | | const errors = []; |
| | | |
| | | if (!product.productCategory) { |
| | | errors.push(`产品${index + 1}:请选择产品大类`); |
| | | } |
| | | if (!product.specificationModel) { |
| | | errors.push(`产品${index + 1}:请选择规格型号`); |
| | | } |
| | | if (!product.unit) { |
| | | errors.push(`产品${index + 1}:请输入单位`); |
| | | } |
| | | if (!product.taxRate) { |
| | | errors.push(`产品${index + 1}:请选择税率`); |
| | | } |
| | | if (!product.taxInclusiveUnitPrice || parseFloat(product.taxInclusiveUnitPrice) <= 0) { |
| | | errors.push(`产品${index + 1}:请输入有效的含税单价`); |
| | | } |
| | | if (!product.quantity || parseFloat(product.quantity) <= 0) { |
| | | errors.push(`产品${index + 1}:请输入有效的数量`); |
| | | } |
| | | if (!product.taxInclusiveTotalPrice || parseFloat(product.taxInclusiveTotalPrice) <= 0) { |
| | | errors.push(`产品${index + 1}:请输入有效的含税总价`); |
| | | } |
| | | if (!product.taxExclusiveTotalPrice || parseFloat(product.taxExclusiveTotalPrice) <= 0) { |
| | | errors.push(`产品${index + 1}:请输入有效的不含税总价`); |
| | | } |
| | | if (!product.invoiceType) { |
| | | errors.push(`产品${index + 1}:请选择发票类型`); |
| | | } |
| | | |
| | | return errors; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | // 获取页面参数 |
| | | operationType.value = uni.getStorageSync('operationType') || ''; |
| | |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .account-detail { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 5rem; |
| | | } |
| | | |
| | | .header { |
| | | display: flex; |
| | | align-items: center; |
| | | background: #fff; |
| | | 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: 1.125rem; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .form-section { |
| | | margin-top: 1rem; |
| | | } |
| | | .van-field { |
| | | height: 3.4rem; |
| | | } |
| | | .van-cell { |
| | | align-items: center; |
| | | } |
| | | .product-section { |
| | | background: #fff; |
| | | 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: 1rem; |
| | | } |
| | | |
| | | .section-title { |
| | | font-size: 1rem; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .product-card { |
| | | 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-header { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | padding: 0 0.5rem 0.75rem 0.5rem; |
| | | border-bottom: 0.0625rem solid #e8e8e8; |
| | | } |
| | | |
| | | .product-productCategory { |
| | | margin-left: 0.5rem; |
| | | font-size: 0.875rem; |
| | | font-weight: 500; |
| | | color: #333; |
| | | } |
| | | |
| | | .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; |
| | | } |
| | | |
| | | .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; |
| | | } |
| | | |
| | | /* 移除 input 边框的样式 */ |
| | | :deep(.u-input) { |
| | | border: none !important; |
| | | box-shadow: none !important; |
| | | background: transparent !important; |
| | | } |
| | | |
| | | :deep(.u-input__content) { |
| | | border: none !important; |
| | | box-shadow: none !important; |
| | | background: transparent !important; |
| | | } |
| | | |
| | | :deep(.u-input__content__field-wrapper) { |
| | | border: none !important; |
| | | box-shadow: none !important; |
| | | background: transparent !important; |
| | | } |
| | | |
| | | :deep(.u-input__content__field-wrapper__field) { |
| | | border: none !important; |
| | | box-shadow: none !important; |
| | | background: transparent !important; |
| | | outline: none !important; |
| | | } |
| | | |
| | | /* 移除 textarea 边框的样式 */ |
| | | :deep(.u-textarea) { |
| | | border: none !important; |
| | | box-shadow: none !important; |
| | | background: transparent !important; |
| | | } |
| | | |
| | | :deep(.u-textarea__content) { |
| | | border: none !important; |
| | | box-shadow: none !important; |
| | | background: transparent !important; |
| | | } |
| | | |
| | | :deep(.u-textarea__content__field) { |
| | | border: none !important; |
| | | box-shadow: none !important; |
| | | background: transparent !important; |
| | | outline: none !important; |
| | | } |
| | | |
| | | /* 移除 form-item 的边框 */ |
| | | :deep(.u-form-item) { |
| | | border: none !important; |
| | | } |
| | | |
| | | :deep(.u-form-item__body) { |
| | | border: none !important; |
| | | } |
| | | |
| | | /* 保持分割线样式 */ |
| | | :deep(.u-form-item--border-bottom) { |
| | | border-bottom: 1px solid #ebeef5 !important; |
| | | } |
| | | |
| | | /* 选择器头部样式 */ |
| | | .picker-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 15px 20px; |
| | | background: #fff; |
| | | border-bottom: 1px solid #ebeef5; |
| | | } |
| | | |
| | | .picker-cancel { |
| | | color: #909399; |
| | | font-size: 16px; |
| | | } |
| | | |
| | | .picker-title { |
| | | color: #303133; |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .picker-confirm { |
| | | color: #2979ff; |
| | | font-size: 16px; |
| | | } |
| | | <style lang="scss"> |
| | | @import '@/static/scss/form-common.scss'; |
| | | </style> |