| | |
| | | const bytes = Number(size) |
| | | if (!Number.isFinite(bytes) || bytes <= 0) return '0 B' |
| | | if (bytes < 1024) return `${bytes} B` |
| | | if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1).replace(/\.0$/, '')} KB` |
| | | return `${(bytes / (1024 * 1024)).toFixed(1).replace(/\.0$/, '')} MB` |
| | | if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(4).replace(/\.0$/, '')} KB` |
| | | return `${(bytes / (1024 * 1024)).toFixed(4).replace(/\.0$/, '')} MB` |
| | | } |
| | | |
| | | const createLocalFileSnapshot = (file, index = 0) => { |
| | |
| | | const formatCompactNumber = (value) => { |
| | | const amount = Number(value) || 0 |
| | | if (Math.abs(amount) >= 10000) { |
| | | return `${(amount / 10000).toFixed(2).replace(/\.?0+$/, '')}\u4e07` |
| | | return `${(amount / 10000).toFixed(4).replace(/\.?0+$/, '')}\u4e07` |
| | | } |
| | | return amount.toLocaleString('zh-CN', { maximumFractionDigits: 2 }) |
| | | } |
| | |
| | | } else { |
| | | // 默认保留两位小数 |
| | | sums[index] = parseFloat(sum).toFixed( |
| | | specialFormat[prop]?.decimalPlaces ?? 2 |
| | | specialFormat[prop]?.decimalPlaces ?? 4 |
| | | ); |
| | | } |
| | | } else { |
| | |
| | | // 不含税总价计算 |
| | | const calculateTaxExclusiveTotalPrice = (taxInclusiveTotalPrice, taxRate) => { |
| | | const taxRateDecimal = taxRate / 100; |
| | | return (taxInclusiveTotalPrice / (1 + taxRateDecimal)).toFixed(2); |
| | | return (taxInclusiveTotalPrice / (1 + taxRateDecimal)).toFixed(4); |
| | | }; |
| | | // 含税总价计算 |
| | | const calculateTaxIncludeTotalPrice = (taxInclusiveUnitPrice, quantity) => { |
| | | return (taxInclusiveUnitPrice * quantity).toFixed(2); |
| | | return (taxInclusiveUnitPrice * quantity).toFixed(4); |
| | | }; |
| | | // 导出函数供其他文件使用 |
| | | export { |
| | |
| | | // 格式化货币 |
| | | const formatCurrency = (value) => { |
| | | if (!value) return '0.00'; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ','); |
| | | return Number(value).toFixed(4).replace(/\B(?=(\d{3})+(?!\d))/g, ','); |
| | | }; |
| | | |
| | | // 获取状态标签类型 |
| | |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="资产原值" prop="originalValue"> |
| | | <el-input-number v-model="form.originalValue" :min="0" :precision="2" style="width: 100%;" @change="calculateNetValue" /> |
| | | <el-input-number v-model="form.originalValue" :min="0" :precision="4" style="width: 100%;" @change="calculateNetValue" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="残值率" prop="residualRate"> |
| | | <el-input-number v-model="form.residualRate" :min="0" :max="10" :precision="2" style="width: 100%;" /> |
| | | <el-input-number v-model="form.residualRate" :min="0" :max="10" :precision="4" style="width: 100%;" /> |
| | | <span style="margin-left: 10px;">%</span> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | return Number(value).toFixed(4).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const getCategoryLabel = (category) => { |
| | |
| | | const calculateNetValue = () => { |
| | | const originalValue = Number(form.originalValue || 0); |
| | | const accumulatedDepreciation = Number(form.accumulatedDepreciation || 0); |
| | | form.netValue = Number((originalValue - accumulatedDepreciation).toFixed(2)); |
| | | form.netValue = Number((originalValue - accumulatedDepreciation).toFixed(4)); |
| | | }; |
| | | |
| | | // 联调约定:分页参数固定为 current/size,返回 data.records/data.total |
| | |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="资产原值" prop="originalValue"> |
| | | <el-input-number v-model="form.originalValue" :min="0" :precision="2" style="width: 100%;" @change="calculateNetValue" /> |
| | | <el-input-number v-model="form.originalValue" :min="0" :precision="4" style="width: 100%;" @change="calculateNetValue" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="残值率" prop="residualRate"> |
| | | <el-input-number v-model="form.residualRate" :min="0" :max="10" :precision="2" style="width: 100%;" /> |
| | | <el-input-number v-model="form.residualRate" :min="0" :max="10" :precision="4" style="width: 100%;" /> |
| | | <span style="margin-left: 10px;">%</span> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | return Number(value).toFixed(4).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const getCategoryLabel = (category) => { |
| | |
| | | const calculateNetValue = () => { |
| | | const originalValue = Number(form.originalValue || 0); |
| | | const accumulatedAmortization = Number(form.accumulatedAmortization || 0); |
| | | form.netValue = Number((originalValue - accumulatedAmortization).toFixed(2)); |
| | | form.netValue = Number((originalValue - accumulatedAmortization).toFixed(4)); |
| | | }; |
| | | |
| | | // 联调约定:分页参数固定为 current/size,返回 data.records/data.total |
| | |
| | | :class="pageInfo.netRevenue >= 0 ? 'plus' : 'minus'"> |
| | | {{ pageInfo.netRevenue >= 0 ? '+' : '' }}{{ formatMoney(pageInfo.netRevenue) }} |
| | | </div> |
| | | <div class="rate">利润率: {{ pageInfo.totalIncome > 0 ? ((pageInfo.netRevenue / pageInfo.totalIncome) * 100).toFixed(1) : 0 }}%</div> |
| | | <div class="rate">利润率: {{ pageInfo.totalIncome > 0 ? ((pageInfo.netRevenue / pageInfo.totalIncome) * 100).toFixed(4) : 0 }}%</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | <el-input-number |
| | | v-model="form.amount" |
| | | :min="0" |
| | | :precision="2" |
| | | :precision="4" |
| | | style="width: 100%;" |
| | | :disabled="isView" |
| | | placeholder="根据入库单含税金额自动换算,可修改" |
| | |
| | | <el-input-number |
| | | v-model="form.taxAmount" |
| | | :min="0" |
| | | :precision="2" |
| | | :precision="4" |
| | | :controls="false" |
| | | style="width: 100%;" |
| | | disabled |
| | |
| | | <el-input-number |
| | | v-model="form.totalAmount" |
| | | :min="0" |
| | | :precision="2" |
| | | :precision="4" |
| | | :controls="false" |
| | | style="width: 100%;" |
| | | disabled |
| | |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | return Number(value).toFixed(4).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const normalizeStatus = (status) => { |
| | |
| | | |
| | | /** 不含税金额变更:税额、价税合计正向计算 */ |
| | | const calculateTaxFromExclusive = () => { |
| | | form.taxAmount = Number((form.amount * form.taxRate / 100).toFixed(2)); |
| | | form.totalAmount = Number((form.amount + form.taxAmount).toFixed(2)); |
| | | form.taxAmount = Number((form.amount * form.taxRate / 100).toFixed(4)); |
| | | form.totalAmount = Number((form.amount + form.taxAmount).toFixed(4)); |
| | | }; |
| | | |
| | | /** 价税合计变更:按税率反算不含税金额、税额 */ |
| | |
| | | return; |
| | | } |
| | | const rate = Number(form.taxRate) / 100; |
| | | form.totalAmount = Number(total.toFixed(2)); |
| | | form.amount = Number((form.totalAmount / (1 + rate)).toFixed(2)); |
| | | form.taxAmount = Number((form.totalAmount - form.amount).toFixed(2)); |
| | | form.totalAmount = Number(total.toFixed(4)); |
| | | form.amount = Number((form.totalAmount / (1 + rate)).toFixed(4)); |
| | | form.taxAmount = Number((form.totalAmount - form.amount).toFixed(4)); |
| | | }; |
| | | |
| | | const handleTaxRateChange = () => { |
| | |
| | | .reduce((acc, row) => acc + getInboundRowTaxInclusiveAmount(row), 0); |
| | | } |
| | | |
| | | calculateTaxFromInclusive(taxInclusiveSum > 0 ? Number(taxInclusiveSum.toFixed(2)) : 0); |
| | | calculateTaxFromInclusive(taxInclusiveSum > 0 ? Number(taxInclusiveSum.toFixed(4)) : 0); |
| | | }; |
| | | |
| | | const inboundBatchDisplayText = computed(() => { |
| | |
| | | const taxRate = toFormNumber(row.taxRate) || 13; |
| | | |
| | | if (totalAmount > 0 && amount === 0 && taxAmount === 0) { |
| | | amount = Number((totalAmount / (1 + taxRate / 100)).toFixed(2)); |
| | | taxAmount = Number((totalAmount - amount).toFixed(2)); |
| | | amount = Number((totalAmount / (1 + taxRate / 100)).toFixed(4)); |
| | | taxAmount = Number((totalAmount - amount).toFixed(4)); |
| | | } else if (totalAmount > 0 && amount > 0 && taxAmount === 0) { |
| | | taxAmount = Number((totalAmount - amount).toFixed(2)); |
| | | taxAmount = Number((totalAmount - amount).toFixed(4)); |
| | | } else if (amount > 0 && taxAmount === 0 && totalAmount === 0) { |
| | | taxAmount = Number((amount * taxRate / 100).toFixed(2)); |
| | | totalAmount = Number((amount + taxAmount).toFixed(2)); |
| | | taxAmount = Number((amount * taxRate / 100).toFixed(4)); |
| | | totalAmount = Number((amount + taxAmount).toFixed(4)); |
| | | } else if (amount > 0 && taxAmount > 0 && totalAmount === 0) { |
| | | totalAmount = Number((amount + taxAmount).toFixed(2)); |
| | | totalAmount = Number((amount + taxAmount).toFixed(4)); |
| | | } |
| | | |
| | | return { amount, taxAmount, totalAmount }; |
| | |
| | | <div> |
| | | <el-statistic title="本页付款合计" |
| | | :value="totalPaymentAmount" |
| | | :precision="2" |
| | | :precision="4" |
| | | prefix="¥" /> |
| | | </div> |
| | | <div> |
| | |
| | | const formatMoney = value => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value) |
| | | .toFixed(2) |
| | | .toFixed(4) |
| | | .replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | |
| | | <el-input-number |
| | | v-model="form.paymentAmount" |
| | | :min="0" |
| | | :precision="2" |
| | | :precision="4" |
| | | style="width: 100%;" |
| | | :disabled="isView" |
| | | placeholder="根据入库单自动汇总,可修改" |
| | |
| | | <el-input-number |
| | | v-model="paymentForm.paymentAmount" |
| | | :min="0" |
| | | :precision="2" |
| | | :precision="4" |
| | | style="width: 100%;" |
| | | /> |
| | | </el-form-item> |
| | |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | return Number(value).toFixed(4).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const normalizeStatus = (status) => { |
| | |
| | | .reduce((acc, row) => acc + getInboundRowTaxInclusiveAmount(row), 0); |
| | | } |
| | | |
| | | form.paymentAmount = sum > 0 ? Number(sum.toFixed(2)) : 0; |
| | | form.paymentAmount = sum > 0 ? Number(sum.toFixed(4)) : 0; |
| | | }; |
| | | |
| | | const inboundBatchDisplayText = computed(() => { |
| | |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | return Number(value).toFixed(4).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | |
| | | <el-input-number |
| | | v-model="form.amount" |
| | | :min="0" |
| | | :precision="2" |
| | | :precision="4" |
| | | :disabled="isView" |
| | | style="width: 100%;" |
| | | placeholder="根据所选出库单自动汇总,可修改" |
| | |
| | | const sum = outboundBatchOptions.value |
| | | .filter((opt) => selected.some((id) => isSameOutboundId(id, opt.value))) |
| | | .reduce((acc, opt) => acc + (Number(opt.outboundAmount) || 0), 0); |
| | | form.amount = sum > 0 ? Number(sum.toFixed(2)) : 0; |
| | | form.amount = sum > 0 ? Number(sum.toFixed(4)) : 0; |
| | | }; |
| | | |
| | | const getOutboundRowId = (row) => row?.id ?? row?.stockOutRecordId; |
| | |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | return Number(value).toFixed(4).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const getStatusLabel = (status) => { |
| | |
| | | <el-input-number |
| | | v-model="form.amount" |
| | | :min="0" |
| | | :precision="2" |
| | | :precision="4" |
| | | style="width: 100%;" |
| | | :disabled="isView" |
| | | @change="calculateTax" |
| | |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | return Number(value).toFixed(4).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const calculateTax = () => { |
| | | form.taxAmount = Number((form.amount * form.taxRate / 100).toFixed(2)); |
| | | form.totalAmount = Number((form.amount + form.taxAmount).toFixed(2)); |
| | | form.taxAmount = Number((form.amount * form.taxRate / 100).toFixed(4)); |
| | | form.totalAmount = Number((form.amount + form.taxAmount).toFixed(4)); |
| | | }; |
| | | |
| | | const handleInvoiceTypeChange = () => { |
| | |
| | | <div> |
| | | <el-statistic title="本页收款合计" |
| | | :value="totalReceiptAmount" |
| | | :precision="2" |
| | | :precision="4" |
| | | prefix="¥" /> |
| | | </div> |
| | | <div> |
| | |
| | | prop="amount"> |
| | | <el-input-number v-model="form.amount" |
| | | :min="0" |
| | | :precision="2" |
| | | :precision="4" |
| | | style="width: 100%;" |
| | | :disabled="isView" |
| | | placeholder="根据关联单据自动汇总,可修改" /> |
| | |
| | | const formatMoney = value => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value) |
| | | .toFixed(2) |
| | | .toFixed(4) |
| | | .replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | |
| | | const sum = outboundBatchOptions.value |
| | | .filter(opt => selected.some(id => isSameOutboundId(id, opt.value))) |
| | | .reduce((acc, opt) => acc + (Number(opt.outboundAmount) || 0), 0); |
| | | form.amount = sum > 0 ? Number(sum.toFixed(2)) : 0; |
| | | form.amount = sum > 0 ? Number(sum.toFixed(4)) : 0; |
| | | }; |
| | | |
| | | const restoreOutboundTableSelection = () => { |
| | |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | return Number(value).toFixed(4).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | return Number(value).toFixed(4).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | // 联调约定:明细账按科目与期间过滤 |
| | |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | return Number(value).toFixed(4).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | // 联调约定:总账接口返回行数组(rowType/date/voucherNo/summary/debit/credit/direction/balance) |
| | |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <div> |
| | | <el-statistic title="借方合计" :value="totalDebit" :precision="2" prefix="¥" /> |
| | | <el-statistic title="贷方合计" :value="totalCredit" :precision="2" prefix="¥" style="margin-left: 30px;" /> |
| | | <el-statistic title="借方合计" :value="totalDebit" :precision="4" prefix="¥" /> |
| | | <el-statistic title="贷方合计" :value="totalCredit" :precision="4" prefix="¥" style="margin-left: 30px;" /> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="add" icon="Plus">新增凭证</el-button> |
| | |
| | | <!-- 借方11列 --> |
| | | <template v-if="editingCell.row === rowIndex && editingCell.type === 'debit'"> |
| | | <td colspan="11" class="debit-input-cell"> |
| | | <el-input-number ref="amountInputRef" v-model="entry.debit" :disabled="isViewMode" :min="0" :precision="2" :controls="false" :value-on-clear="undefined" size="small" @blur="finishEdit" class="full-width-input" /> |
| | | <el-input-number ref="amountInputRef" v-model="entry.debit" :disabled="isViewMode" :min="0" :precision="4" :controls="false" :value-on-clear="undefined" size="small" @blur="finishEdit" class="full-width-input" /> |
| | | </td> |
| | | </template> |
| | | <template v-else> |
| | |
| | | <!-- 贷方11列 --> |
| | | <template v-if="editingCell.row === rowIndex && editingCell.type === 'credit'"> |
| | | <td colspan="11" class="credit-input-cell"> |
| | | <el-input-number ref="amountInputRef" v-model="entry.credit" :disabled="isViewMode" :min="0" :precision="2" :controls="false" :value-on-clear="undefined" size="small" @blur="finishEdit" class="full-width-input" /> |
| | | <el-input-number ref="amountInputRef" v-model="entry.credit" :disabled="isViewMode" :min="0" :precision="4" :controls="false" :value-on-clear="undefined" size="small" @blur="finishEdit" class="full-width-input" /> |
| | | </td> |
| | | </template> |
| | | <template v-else> |
| | |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | return Number(value).toFixed(4).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const normalizeVoucherStatus = status => String(status || "").toLowerCase(); |
| | |
| | | return new Array(length).fill(''); |
| | | } |
| | | |
| | | const amountStr = Number(amount).toFixed(2); |
| | | const amountStr = Number(amount).toFixed(4); |
| | | const [intPart, decPart] = amountStr.split('.'); |
| | | const fullAmount = intPart + decPart; |
| | | |
| | |
| | | const lines = list |
| | | .map((p) => { |
| | | const colorBox = `<span style="display:inline-block;margin-right:6px;border-radius:2px;width:10px;height:10px;background:${p.color}"></span>`; |
| | | return `${colorBox}${p.seriesName}<b style="float:right;">${Number(p.value || 0).toFixed(2)}</b>`; |
| | | return `${colorBox}${p.seriesName}<b style="float:right;">${Number(p.value || 0).toFixed(4)}</b>`; |
| | | }) |
| | | .join("<br/>"); |
| | | return `<div style="min-width:140px;"><div style="font-weight:700;margin-bottom:6px;">${name}</div>${lines}</div>`; |
| | |
| | | value: formatNumber(productionOverviewData.value.totalOutput), |
| | | subLabel: "累计报废", |
| | | subValue: formatNumber(productionOverviewData.value.totalScrap), |
| | | trend: `良率 ${Number(productionOverviewData.value.yieldRate || 0).toFixed(2)}%`, |
| | | trend: `良率 ${Number(productionOverviewData.value.yieldRate || 0).toFixed(4)}%`, |
| | | icon: Operation, |
| | | visible: visibleModules.value.production, |
| | | }, |
| | |
| | | |
| | | const getCompareText = (value) => { |
| | | const num = Number(value || 0); |
| | | const abs = Math.abs(num).toFixed(2); |
| | | const abs = Math.abs(num).toFixed(4); |
| | | if (num > 0) return `较昨日 ↑ ${abs}%`; |
| | | if (num < 0) return `较昨日 ↓ ${abs}%`; |
| | | return "较昨日 持平"; |
| | |
| | | key: "oee", |
| | | label: "设备 OEE", |
| | | percent: clampPercent(oee), |
| | | display: `${oee.toFixed(2)}%`, |
| | | display: `${oee.toFixed(4)}%`, |
| | | delta: getCompareText(oeeCompare), |
| | | trend: getCompareTrend(oeeCompare), |
| | | color: "#2d8cff", |
| | |
| | | key: "order", |
| | | label: "订单达成率", |
| | | percent: clampPercent(orderAchievement), |
| | | display: `${orderAchievement.toFixed(2)}%`, |
| | | display: `${orderAchievement.toFixed(4)}%`, |
| | | delta: getCompareText(orderCompare), |
| | | trend: getCompareTrend(orderCompare), |
| | | color: "#31d2ff", |
| | |
| | | key: "defect", |
| | | label: "不良率", |
| | | percent: clampPercent(defectRate), |
| | | display: `${defectRate.toFixed(2)}%`, |
| | | display: `${defectRate.toFixed(4)}%`, |
| | | delta: getCompareText(defectCompare), |
| | | trend: getCompareTrend(defectCompare), |
| | | color: "#f6a23f", |
| | |
| | | const trendText = (value) => { |
| | | const num = Number(value || 0); |
| | | const flag = num >= 0 ? "↑" : "↓"; |
| | | return `${flag} ${Math.abs(num).toFixed(1)}%`; |
| | | return `${flag} ${Math.abs(num).toFixed(4)}%`; |
| | | }; |
| | | |
| | | const ratioNumber = (numerator, denominator) => { |
| | |
| | | return (n / d) * 100; |
| | | }; |
| | | |
| | | const ratioText = (numerator, denominator) => `${ratioNumber(numerator, denominator).toFixed(1)}%`; |
| | | const ratioText = (numerator, denominator) => `${ratioNumber(numerator, denominator).toFixed(4)}%`; |
| | | |
| | | const clampPercent = (val) => Math.max(0, Math.min(100, Number(val || 0))); |
| | | |
| | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="容积(m³):" prop="volume"> |
| | | <el-input-number v-model="form.volume" :min="0" :precision="2" style="width: 100%" /> |
| | | <el-input-number v-model="form.volume" :min="0" :precision="4" style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="当前压力(MPa):" prop="currentPressure"> |
| | | <el-input-number v-model="form.currentPressure" :min="0" :precision="2" style="width: 100%" /> |
| | | <el-input-number v-model="form.currentPressure" :min="0" :precision="4" style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="预警阈值:" prop="warningThreshold"> |
| | | <el-input-number v-model="form.warningThreshold" :min="0" :precision="2" style="width: 100%" /> |
| | | <el-input-number v-model="form.warningThreshold" :min="0" :precision="4" style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | |
| | | align="right" |
| | | > |
| | | <template #default="scope"> |
| | | <span>{{ scope.row.amount?.toFixed(2) }}</span> |
| | | <span>{{ scope.row.amount?.toFixed(4) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | |
| | | align="right" |
| | | > |
| | | <template #default="scope"> |
| | | <span>{{ scope.row.liters?.toFixed(2) }}</span> |
| | | <span>{{ scope.row.liters?.toFixed(4) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | |
| | | <span |
| | | :style="scope.row.isAbnormal ? 'color:#F56C6C;font-weight:600;' : ''" |
| | | > |
| | | {{ scope.row.fuelConsumption != null ? scope.row.fuelConsumption.toFixed(2) : '-' }} |
| | | {{ scope.row.fuelConsumption != null ? scope.row.fuelConsumption.toFixed(4) : '-' }} |
| | | </span> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | > |
| | | <template #default="scope"> |
| | | <span> |
| | | {{ scope.row.avgConsumption != null ? scope.row.avgConsumption.toFixed(2) : '-' }} |
| | | {{ scope.row.avgConsumption != null ? scope.row.avgConsumption.toFixed(4) : '-' }} |
| | | </span> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | v-model="form.amount" |
| | | :min="0" |
| | | :step="0.01" |
| | | :precision="2" |
| | | :precision="4" |
| | | placeholder="请输入金额" |
| | | style="width: 100%" |
| | | /> |
| | |
| | | v-model="form.liters" |
| | | :min="0" |
| | | :step="0.01" |
| | | :precision="2" |
| | | :precision="4" |
| | | placeholder="请输入升数" |
| | | style="width: 100%" |
| | | /> |
| | |
| | | placeholder="请输入" |
| | | :min="0" |
| | | :step="0.1" |
| | | :precision="2" |
| | | :precision="4" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | |
| | | placeholder="请输入" |
| | | :min="0" |
| | | :step="0.1" |
| | | :precision="2" |
| | | :precision="4" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | |
| | | style="margin: 0"> |
| | | <el-input-number v-model="row.unitQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | :precision="4" |
| | | :step="1" |
| | | controls-position="right" |
| | | style="width: 100%" |
| | |
| | | style="margin: 0"> |
| | | <el-input-number v-model="row.demandedQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | :precision="4" |
| | | :step="1" |
| | | controls-position="right" |
| | | style="width: 100%" |
| | |
| | | if (!Number.isFinite(numberValue)) { |
| | | return 0; |
| | | } |
| | | return Number(numberValue.toFixed(2)); |
| | | return Number(numberValue.toFixed(4)); |
| | | }; |
| | | |
| | | const syncDemandedQuantityTree = (items, parentDemandedQuantity = null) => { |
| | |
| | | style="margin: 0"> |
| | | <el-input-number v-model="row.unitQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | :precision="4" |
| | | :step="1" |
| | | controls-position="right" |
| | | style="width: 100%" |
| | |
| | | style="margin: 0"> |
| | | <el-input-number v-model="row.demandedQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | :precision="4" |
| | | :step="1" |
| | | controls-position="right" |
| | | style="width: 100%" |
| | |
| | | if (!Number.isFinite(numberValue)) { |
| | | return 0; |
| | | } |
| | | return Number(numberValue.toFixed(2)); |
| | | return Number(numberValue.toFixed(4)); |
| | | }; |
| | | |
| | | const syncDemandedQuantityTree = ( |
| | |
| | | style="margin: 0"> |
| | | <el-input-number v-model="row.unitQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | :precision="4" |
| | | :step="1" |
| | | controls-position="right" |
| | | style="width: 100%" |
| | |
| | | style="margin: 0"> |
| | | <el-input-number v-model="row.demandedQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | :precision="4" |
| | | :step="1" |
| | | controls-position="right" |
| | | style="width: 100%" |
| | |
| | | minWidth: 100, |
| | | formatData: val => { |
| | | if (val == null || val === "") return "-"; |
| | | return parseFloat(val).toFixed(2) + "%"; |
| | | return parseFloat(val).toFixed(4) + "%"; |
| | | }, |
| | | }, |
| | | ]); |
| | |
| | | placeholder="请输入" |
| | | :min="0" |
| | | :step="0.1" |
| | | :precision="2" |
| | | :precision="4" |
| | | clearable |
| | | @change="changeNum" |
| | | style="width: 100%" |
| | |
| | | placeholder="请输入" |
| | | :min="0" |
| | | :step="0.1" |
| | | :precision="2" |
| | | :precision="4" |
| | | clearable |
| | | style="width: 100%" |
| | | @change="changeNum" |
| | |
| | | const unitPrice = Number(form.value.unitPrice ?? 0); |
| | | |
| | | if (quantity > 0 && unitPrice > 0) { |
| | | form.value.totalPrice = (quantity * unitPrice).toFixed(2); |
| | | form.value.totalPrice = (quantity * unitPrice).toFixed(4); |
| | | } else { |
| | | form.value.totalPrice = '0.00'; |
| | | } |
| | |
| | | style="width: 100%" |
| | | v-model="scope.row.finishedNum" |
| | | :disabled="!scope.row.editType" |
| | | :precision="2" |
| | | :precision="4" |
| | | placeholder="请输入" |
| | | clearable |
| | | @change="changeNum(scope.row)" /> |
| | |
| | | </div> |
| | | <div class="sensor-data"> |
| | | <div class="data-item"> |
| | | <span>甲烷: {{ sensor.methane.toFixed(2) }}%</span> |
| | | <span>甲烷: {{ sensor.methane.toFixed(4) }}%</span> |
| | | <el-progress |
| | | :percentage="Math.min(Math.round(sensor.methane * 40 * 100) / 100, 100)" |
| | | :color="getProgressColor(Math.min(Math.round(sensor.methane * 40 * 100) / 100, 100), 80)" |
| | |
| | | /> |
| | | </div> |
| | | <div class="data-item"> |
| | | <span>硫化氢: {{ sensor.h2s.toFixed(2) }}ppm</span> |
| | | <span>硫化氢: {{ sensor.h2s.toFixed(4) }}ppm</span> |
| | | <el-progress |
| | | :percentage="Math.min(Math.round((sensor.h2s / 20) * 100 * 100) / 100, 100)" |
| | | :color="getProgressColor(Math.min(Math.round((sensor.h2s / 20) * 100 * 100) / 100, 100), 80)" |
| | |
| | | </div> |
| | | <div class="sensor-data"> |
| | | <div class="data-item"> |
| | | <span>甲烷: {{ sensor.methane.toFixed(2) }}%</span> |
| | | <span>甲烷: {{ sensor.methane.toFixed(4) }}%</span> |
| | | <el-progress |
| | | :percentage="Math.min(Math.round(sensor.methane * 40 * 100) / 100, 100)" |
| | | :color="getProgressColor(sensor.methane, 2.5)" |
| | |
| | | /> |
| | | </div> |
| | | <div class="data-item"> |
| | | <span>硫化氢: {{ sensor.h2s.toFixed(2) }}ppm</span> |
| | | <span>硫化氢: {{ sensor.h2s.toFixed(4) }}ppm</span> |
| | | <el-progress |
| | | :percentage="Math.min(Math.round((sensor.h2s / 20) * 100 * 100) / 100, 100)" |
| | | :color="getProgressColor(sensor.h2s, 10)" |
| | |
| | | formatProgress(percentage) { |
| | | if (percentage == null || isNaN(percentage)) return '0.00%' |
| | | const val = Math.round(Number(percentage) * 100) / 100 |
| | | return `${val.toFixed(2)}%` |
| | | return `${val.toFixed(4)}%` |
| | | }, |
| | | // 初始化图表 |
| | | initChart() { |
| | |
| | | generateRandomData(count, min, max) { |
| | | const data = [] |
| | | for (let i = 0; i < count; i++) { |
| | | data.push(+(Math.random() * (max - min) + min).toFixed(2)) |
| | | data.push(+(Math.random() * (max - min) + min).toFixed(4)) |
| | | } |
| | | return data |
| | | }, |
| | |
| | | refreshSensorData() { |
| | | // 更新储罐区传感器数据 |
| | | this.tankSensors.forEach(sensor => { |
| | | sensor.methane = +(Math.random() * 4).toFixed(2) |
| | | sensor.h2s = +(Math.random() * 15).toFixed(2) |
| | | sensor.methane = +(Math.random() * 4).toFixed(4) |
| | | sensor.h2s = +(Math.random() * 15).toFixed(4) |
| | | sensor.status = this.getSensorStatus(sensor.methane, sensor.h2s) |
| | | }) |
| | | |
| | | // 更新压缩机传感器数据 |
| | | this.compressorSensors.forEach(sensor => { |
| | | sensor.methane = +(Math.random() * 6).toFixed(2) |
| | | sensor.h2s = +(Math.random() * 20).toFixed(2) |
| | | sensor.methane = +(Math.random() * 6).toFixed(4) |
| | | sensor.h2s = +(Math.random() * 20).toFixed(4) |
| | | sensor.status = this.getSensorStatus(sensor.methane, sensor.h2s) |
| | | }) |
| | | |
| | |
| | | const h2sPct = Math.min(Math.round((sensor.h2s / 20) * 100 * 100) / 100, 100) |
| | | const isMethaneMajor = methanePct >= h2sPct |
| | | const overGas = isMethaneMajor ? '甲烷' : '硫化氢' |
| | | const percent = (isMethaneMajor ? methanePct : h2sPct).toFixed(2) |
| | | const percent = (isMethaneMajor ? methanePct : h2sPct).toFixed(4) |
| | | this.currentWarning = { |
| | | location: sensor.name, |
| | | gas: overGas, |
| | |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="数量:" prop="quantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请输入" clearable :precision="2" :disabled="isViewMode || processQuantityDisabled"/> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请输入" clearable :precision="4" :disabled="isViewMode || processQuantityDisabled"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | v-model="form.qualifiedQuantity" |
| | | placeholder="请输入" |
| | | clearable |
| | | :precision="2" |
| | | :precision="4" |
| | | @change="handleQualifiedQuantityChange" |
| | | :disabled="isViewMode" /> |
| | | </el-form-item> |
| | |
| | | v-model="form.unqualifiedQuantity" |
| | | placeholder="请输入" |
| | | clearable |
| | | :precision="2" |
| | | :precision="4" |
| | | @change="handleUnqualifiedQuantityChange" |
| | | :disabled="isViewMode" /> |
| | | </el-form-item> |
| | |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="数量:" prop="quantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请输入" clearable :precision="2"/> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请输入" clearable :precision="4"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | v-model="form.quantity" |
| | | placeholder="请输入" |
| | | clearable |
| | | :precision="2" |
| | | :precision="4" |
| | | :disabled="isViewMode || processQuantityDisabled" /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | v-model="form.qualifiedQuantity" |
| | | placeholder="请输入" |
| | | clearable |
| | | :precision="2" |
| | | :precision="4" |
| | | @change="handleQualifiedQuantityChange" |
| | | :disabled="isViewMode" /> |
| | | </el-form-item> |
| | |
| | | v-model="form.unqualifiedQuantity" |
| | | placeholder="请输入" |
| | | clearable |
| | | :precision="2" |
| | | :precision="4" |
| | | @change="handleUnqualifiedQuantityChange" |
| | | :disabled="isViewMode" /> |
| | | </el-form-item> |
| | |
| | | <el-col :span="12"> |
| | | <el-form-item label="数量:" prop="quantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请输入" |
| | | clearable :precision="2" :disabled="isViewMode || supplierQuantityDisabled"/> |
| | | clearable :precision="4" :disabled="isViewMode || supplierQuantityDisabled"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | <el-col :span="12"> |
| | | <el-form-item label="合格数量:" prop="qualifiedQuantity"> |
| | | <el-input-number :step="0.01" :min="0" :max="form.quantity || 0" style="width: 100%" |
| | | v-model="form.qualifiedQuantity" placeholder="请输入" :precision="2" |
| | | v-model="form.qualifiedQuantity" placeholder="请输入" :precision="4" |
| | | @change="onQualifiedChange" :disabled="isViewMode"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="不合格数量:" prop="unqualifiedQuantity"> |
| | | <el-input-number :step="0.01" :min="0" :max="form.quantity || 0" style="width: 100%" |
| | | v-model="form.unqualifiedQuantity" placeholder="请输入" :precision="2" |
| | | v-model="form.unqualifiedQuantity" placeholder="请输入" :precision="4" |
| | | @change="onUnqualifiedChange" :disabled="isViewMode"/> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | </template> |
| | | <EChart :series="passRateSeries" :legend="{ show: false }" :chartStyle="{ height: '340px', width: '100%' }" /> |
| | | <div class="passrate-text"> |
| | | 当前合格率:<b>{{ (passRate * 100).toFixed(1) }}%</b> |
| | | 当前合格率:<b>{{ (passRate * 100).toFixed(4) }}%</b> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | |
| | | progress: { show: true, width: 12 }, |
| | | axisLine: { lineStyle: { width: 12 } }, |
| | | pointer: { show: true }, |
| | | detail: { valueAnimation: true, formatter: (v) => `${(v * 100).toFixed(1)}%` }, |
| | | detail: { valueAnimation: true, formatter: (v) => `${(v * 100).toFixed(4)}%` }, |
| | | data: [{ value: passRate.value }], |
| | | }, |
| | | ]) |
| | |
| | | spcData.value.shift() |
| | | } |
| | | spcXAxis.value.push(`${spcXAxis.value.length + 1}`) |
| | | spcData.value.push(parseFloat(nextVal.toFixed(2))) |
| | | spcData.value.push(parseFloat(nextVal.toFixed(4))) |
| | | spcSeries.value[0].data = [...spcData.value] |
| | | spcSeries.value[1].data = new Array(spcData.value.length).fill(UCL.value) |
| | | spcSeries.value[2].data = new Array(spcData.value.length).fill(CL.value) |
| | |
| | | } |
| | | const last = spcData.value[spcData.value.length - 1] |
| | | if (last > UCL.value) { |
| | | speak(`预警,最新测量值 ${last.toFixed(2)} 超过上限`) |
| | | speak(`预警,最新测量值 ${last.toFixed(4)} 超过上限`) |
| | | } |
| | | if (last < LCL.value) { |
| | | speak(`预警,最新测量值 ${last.toFixed(2)} 低于下限`) |
| | | speak(`预警,最新测量值 ${last.toFixed(4)} 低于下限`) |
| | | } |
| | | } |
| | | |
| | |
| | | for (let i = 0; i < 20; i++) { |
| | | spcXAxis.value.push(`${i + 1}`) |
| | | const v = CL.value + (Math.random() - 0.5) * 6 |
| | | spcData.value.push(parseFloat(v.toFixed(2))) |
| | | spcData.value.push(parseFloat(v.toFixed(4))) |
| | | } |
| | | spcSeries.value[0].data = [...spcData.value] |
| | | spcSeries.value[1].data = new Array(spcData.value.length).fill(UCL.value) |