From f7ac35468cd9048a93b89a7c39dacac4f5d1748c Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期一, 19 一月 2026 17:11:35 +0800
Subject: [PATCH] 湟水峡 1.下拉框加筛选 2.销售模块不要客户合同号、项目名称 3.客户档案、供应商档案不要和银行有关的字段 4.只需要填客户类型、不需要选择具体的客户针对客户类型产生三种不同的报价、需要绑定产品(比如零售商30、经销商25) 5.客户档案添加客户类型字段
---
src/views/procurementManagement/procurementLedger/index.vue | 259 -----------
src/api/salesManagement/salesLedger.js | 8
src/assets/styles/element-ui.scss | 2
src/views/salesManagement/salesLedger/index.vue | 31 +
src/views/salesManagement/salesQuotation/index.vue | 501 +++++++++++----------
src/components/Dialog/FileListDialog.vue | 309 +++++++++++++
src/components/Dialog/FormDialog.vue | 73 +++
src/views/inventoryManagement/dispatchLog/index.vue | 4
src/components/Dialog/ImportDialog.vue | 172 +++++++
9 files changed, 855 insertions(+), 504 deletions(-)
diff --git a/src/api/salesManagement/salesLedger.js b/src/api/salesManagement/salesLedger.js
index 3a2fa22..6548927 100644
--- a/src/api/salesManagement/salesLedger.js
+++ b/src/api/salesManagement/salesLedger.js
@@ -109,3 +109,11 @@
params: query,
});
}
+// 閿�鍞彴璐﹂〉闈㈠彂璐э紝鏌ヨ搴撳瓨鏄惁鍏呰冻
+export function getProductInventory(query) {
+ return request({
+ url: "/sales/ledger/getProductInventory",
+ method: "get",
+ params: query,
+ });
+}
\ No newline at end of file
diff --git a/src/assets/styles/element-ui.scss b/src/assets/styles/element-ui.scss
index 8c741af..1af1eb5 100644
--- a/src/assets/styles/element-ui.scss
+++ b/src/assets/styles/element-ui.scss
@@ -66,7 +66,7 @@
color: #2e3033;
}
.el-dialog__body {
- padding: 16px 40px 0 40px;
+ padding: 16px 40px 20px 40px;
max-height: 74vh;
overflow-y: auto;
}
diff --git a/src/components/Dialog/FileListDialog.vue b/src/components/Dialog/FileListDialog.vue
new file mode 100644
index 0000000..0721a55
--- /dev/null
+++ b/src/components/Dialog/FileListDialog.vue
@@ -0,0 +1,309 @@
+<template>
+ <el-dialog
+ v-model="dialogVisible"
+ :title="title"
+ :width="width"
+ :before-close="handleClose"
+ >
+ <div class="file-list-toolbar" v-if="showToolbar">
+ <template v-if="useBuiltInUpload">
+ <el-upload
+ v-model:file-list="uploadFileList"
+ class="upload-demo"
+ :action="uploadAction"
+ :headers="uploadHeaders"
+ :show-file-list="false"
+ :on-success="handleDefaultUploadSuccess"
+ :on-error="handleDefaultUploadError"
+ >
+ <el-button
+ v-if="showUploadButton"
+ type="primary"
+ size="small"
+ >
+ 涓婁紶闄勪欢
+ </el-button>
+ </el-upload>
+ </template>
+ <template v-else>
+ <el-button
+ v-if="showUploadButton"
+ type="primary"
+ size="small"
+ @click="handleUpload"
+ >
+ 鏂板闄勪欢
+ </el-button>
+ </template>
+ </div>
+ <el-table :data="tableData" border :height="tableHeight">
+ <el-table-column
+ :label="nameColumnLabel"
+ :prop="nameColumnProp"
+ :min-width="nameColumnMinWidth"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ v-if="showActions"
+ fixed="right"
+ label="鎿嶄綔"
+ :width="actionColumnWidth"
+ align="center"
+ >
+ <template #default="scope">
+ <el-button
+ v-if="showDownload"
+ link
+ type="primary"
+ size="small"
+ @click="handleDownload(scope.row)"
+ >
+ 涓嬭浇
+ </el-button>
+ <el-button
+ v-if="showPreview"
+ link
+ type="primary"
+ size="small"
+ @click="handlePreview(scope.row)"
+ >
+ 棰勮
+ </el-button>
+ <el-button
+ v-if="showDeleteButton"
+ link
+ type="danger"
+ size="small"
+ @click="handleDelete(scope.row, scope.$index)"
+ >
+ 鍒犻櫎
+ </el-button>
+ <slot name="actions" :row="scope.row"></slot>
+ </template>
+ </el-table-column>
+ <slot name="columns"></slot>
+ </el-table>
+ </el-dialog>
+ <filePreview v-if="showPreview" ref="filePreviewRef" />
+</template>
+
+<script setup>
+import { ref, computed, getCurrentInstance } from 'vue'
+import { ElMessage } from 'element-plus'
+import filePreview from '@/components/filePreview/index.vue'
+import { getToken } from '@/utils/auth'
+
+const props = defineProps({
+ modelValue: {
+ type: Boolean,
+ default: false
+ },
+ title: {
+ type: String,
+ default: '闄勪欢'
+ },
+ width: {
+ type: String,
+ default: '40%'
+ },
+ tableHeight: {
+ type: String,
+ default: '40vh'
+ },
+ nameColumnLabel: {
+ type: String,
+ default: '闄勪欢鍚嶇О'
+ },
+ nameColumnProp: {
+ type: String,
+ default: 'name'
+ },
+ nameColumnMinWidth: {
+ type: [String, Number],
+ default: 400
+ },
+ actionColumnWidth: {
+ type: [String, Number],
+ default: 160
+ },
+ showActions: {
+ type: Boolean,
+ default: true
+ },
+ showDownload: {
+ type: Boolean,
+ default: true
+ },
+ showPreview: {
+ type: Boolean,
+ default: true
+ },
+ showUploadButton: {
+ type: Boolean,
+ default: false
+ },
+ showDeleteButton: {
+ type: Boolean,
+ default: false
+ },
+ urlField: {
+ type: String,
+ default: 'url'
+ },
+ downloadMethod: {
+ type: Function,
+ default: null
+ },
+ previewMethod: {
+ type: Function,
+ default: null
+ },
+ uploadMethod: {
+ type: Function,
+ default: null
+ },
+ deleteMethod: {
+ type: Function,
+ default: null
+ },
+ rulesRegulationsManagementId: {
+ type: [String, Number],
+ default: ''
+ },
+ uploadUrl: {
+ type: String,
+ default: `${import.meta.env.VITE_APP_BASE_API}/file/upload`
+ }
+})
+
+const emit = defineEmits(['update:modelValue', 'close', 'download', 'preview', 'upload', 'delete'])
+
+const { proxy } = getCurrentInstance()
+const filePreviewRef = ref(null)
+const uploadFileList = ref([])
+
+const dialogVisible = computed({
+ get: () => props.modelValue,
+ set: (val) => emit('update:modelValue', val)
+})
+
+const tableData = ref([])
+const showToolbar = computed(() => props.showUploadButton)
+const useBuiltInUpload = computed(() => !props.uploadMethod)
+const uploadAction = computed(() => props.uploadUrl)
+const uploadHeaders = computed(() => ({
+ Authorization: `Bearer ${getToken()}`
+}))
+
+const handleClose = () => {
+ emit('close')
+ dialogVisible.value = false
+}
+
+const handleDownload = (row) => {
+ if (props.downloadMethod) {
+ props.downloadMethod(row)
+ } else {
+ // 榛樿涓嬭浇鏂规硶
+ proxy.$download.name(row[props.urlField])
+ }
+ emit('download', row)
+}
+
+const handlePreview = (row) => {
+ if (props.previewMethod) {
+ props.previewMethod(row)
+ } else {
+ // 榛樿棰勮鏂规硶
+ if (filePreviewRef.value) {
+ filePreviewRef.value.open(row[props.urlField])
+ }
+ }
+ emit('preview', row)
+}
+
+const open = (list) => {
+ dialogVisible.value = true
+ tableData.value = list || []
+}
+
+const handleUpload = async () => {
+ if (props.uploadMethod) {
+ // 濡傛灉鎻愪緵浜嗚嚜瀹氫箟涓婁紶鏂规硶锛岀敱鐖剁粍浠惰礋璐f洿鏂板垪琛紙閫氳繃 setList锛�
+ // 杩欓噷涓嶅啀鑷姩娣诲姞锛岄伩鍏嶄笌鐖剁粍浠剁殑 setList 閲嶅
+ await props.uploadMethod()
+ }
+ emit('upload')
+}
+
+const handleDelete = async (row, index) => {
+ if (props.deleteMethod) {
+ const result = await props.deleteMethod(row, index)
+ if (result === false) {
+ return
+ }
+ // 濡傛灉鎻愪緵浜� deleteMethod锛岀敱鐖剁粍浠惰礋璐e埛鏂板垪琛紝涓嶅湪杩欓噷鍒犻櫎
+ } else {
+ // 濡傛灉娌℃湁鎻愪緵 deleteMethod锛屾墠鍦ㄧ粍浠跺唴閮ㄥ垹闄�
+ removeAttachment(index)
+ }
+ emit('delete', row)
+}
+
+const addAttachment = (item) => {
+ tableData.value = [...tableData.value, item]
+}
+
+const handleDefaultUploadSuccess = async (res, file) => {
+ if (res?.code !== 200) {
+ ElMessage.error(res?.msg || '鏂囦欢涓婁紶澶辫触')
+ return
+ }
+ if (!props.rulesRegulationsManagementId) {
+ ElMessage.error('缂哄皯瑙勭珷鍒跺害ID锛屾棤娉曚繚瀛橀檮浠�')
+ return
+ }
+ const fileName = res?.data?.originalName || file?.name
+ const fileUrl = res?.data?.tempPath || res?.data?.url
+ const payload = {
+ fileName,
+ fileUrl,
+ rulesRegulationsManagementId: props.rulesRegulationsManagementId,
+ raw: res?.data || {}
+ }
+ emit('upload', payload)
+}
+
+const handleDefaultUploadError = () => {
+ ElMessage.error('鏂囦欢涓婁紶澶辫触')
+}
+
+const removeAttachment = (index) => {
+ if (index > -1 && index < tableData.value.length) {
+ const newList = [...tableData.value]
+ newList.splice(index, 1)
+ tableData.value = newList
+ }
+}
+
+const setList = (list) => {
+ tableData.value = list || []
+}
+
+defineExpose({
+ open,
+ addAttachment,
+ removeAttachment,
+ setList,
+ handleUpload,
+ handleDelete
+})
+</script>
+
+<style scoped>
+.file-list-toolbar {
+ margin-bottom: 8px;
+ text-align: right;
+}
+</style>
+
diff --git a/src/components/Dialog/FormDialog.vue b/src/components/Dialog/FormDialog.vue
new file mode 100644
index 0000000..5e21b1d
--- /dev/null
+++ b/src/components/Dialog/FormDialog.vue
@@ -0,0 +1,73 @@
+<template>
+ <el-dialog
+ v-model="dialogVisible"
+ :title="computedTitle"
+ :width="width"
+ @close="handleClose"
+ >
+ <slot></slot>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="handleConfirm">纭</el-button>
+ <el-button @click="handleCancel">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
+</template>
+
+<script setup>
+import { computed } from 'vue'
+
+const props = defineProps({
+ modelValue: {
+ type: Boolean,
+ default: false
+ },
+ title: {
+ type: [String, Function],
+ default: ''
+ },
+ operationType: {
+ type: String,
+ default: ''
+ },
+ width: {
+ type: String,
+ default: '70%'
+ }
+})
+
+const emit = defineEmits(['update:modelValue', 'close', 'confirm', 'cancel'])
+
+const dialogVisible = computed({
+ get: () => props.modelValue,
+ set: (val) => emit('update:modelValue', val)
+})
+
+const computedTitle = computed(() => {
+ if (typeof props.title === 'function') {
+ return props.title(props.operationType)
+ }
+ return props.title
+})
+
+const handleClose = () => {
+ emit('close')
+}
+
+const handleConfirm = () => {
+ emit('confirm')
+}
+
+const handleCancel = () => {
+ emit('cancel')
+ dialogVisible.value = false
+}
+</script>
+
+<style scoped>
+.dialog-footer {
+ text-align: center;
+}
+</style>
+
diff --git a/src/components/Dialog/ImportDialog.vue b/src/components/Dialog/ImportDialog.vue
new file mode 100644
index 0000000..5b126dc
--- /dev/null
+++ b/src/components/Dialog/ImportDialog.vue
@@ -0,0 +1,172 @@
+<template>
+ <el-dialog
+ :title="title"
+ v-model="dialogVisible"
+ :width="width"
+ :append-to-body="appendToBody"
+ @close="handleClose"
+ >
+ <el-upload
+ ref="uploadRef"
+ :limit="limit"
+ :accept="accept"
+ :headers="headers"
+ :action="action"
+ :disabled="disabled"
+ :before-upload="beforeUpload"
+ :on-progress="onProgress"
+ :on-success="onSuccess"
+ :on-error="onError"
+ :on-change="onChange"
+ :auto-upload="autoUpload"
+ drag
+ >
+ <el-icon class="el-icon--upload"><UploadFilled /></el-icon>
+ <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
+ <template #tip>
+ <div class="el-upload__tip text-center">
+ <span>{{ tipText }}</span>
+ <el-link
+ v-if="showDownloadTemplate"
+ type="primary"
+ :underline="false"
+ style="font-size: 12px; vertical-align: baseline; margin-left: 5px;"
+ @click="handleDownloadTemplate"
+ >涓嬭浇妯℃澘</el-link
+ >
+ </div>
+ </template>
+ </el-upload>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="handleConfirm">纭� 瀹�</el-button>
+ <el-button @click="handleCancel">鍙� 娑�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+</template>
+
+<script setup>
+import { computed, ref } from 'vue'
+import { UploadFilled } from '@element-plus/icons-vue'
+
+const props = defineProps({
+ modelValue: {
+ type: Boolean,
+ default: false
+ },
+ title: {
+ type: String,
+ default: '瀵煎叆'
+ },
+ width: {
+ type: String,
+ default: '400px'
+ },
+ appendToBody: {
+ type: Boolean,
+ default: true
+ },
+ limit: {
+ type: Number,
+ default: 1
+ },
+ accept: {
+ type: String,
+ default: '.xlsx, .xls'
+ },
+ headers: {
+ type: Object,
+ default: () => ({})
+ },
+ action: {
+ type: String,
+ required: true
+ },
+ disabled: {
+ type: Boolean,
+ default: false
+ },
+ autoUpload: {
+ type: Boolean,
+ default: false
+ },
+ tipText: {
+ type: String,
+ default: '浠呭厑璁稿鍏ls銆亁lsx鏍煎紡鏂囦欢銆�'
+ },
+ showDownloadTemplate: {
+ type: Boolean,
+ default: true
+ },
+ beforeUpload: {
+ type: Function,
+ default: null
+ },
+ onProgress: {
+ type: Function,
+ default: null
+ },
+ onSuccess: {
+ type: Function,
+ default: null
+ },
+ onError: {
+ type: Function,
+ default: null
+ },
+ onChange: {
+ type: Function,
+ default: null
+ }
+})
+
+const emit = defineEmits(['update:modelValue', 'close', 'confirm', 'cancel', 'download-template'])
+
+const dialogVisible = computed({
+ get: () => props.modelValue,
+ set: (val) => emit('update:modelValue', val)
+})
+
+const uploadRef = ref(null)
+
+const handleClose = () => {
+ emit('close')
+}
+
+const handleConfirm = () => {
+ emit('confirm')
+}
+
+const submit = () => {
+ if (uploadRef.value) {
+ uploadRef.value.submit()
+ }
+}
+
+const handleCancel = () => {
+ emit('cancel')
+ dialogVisible.value = false
+}
+
+const handleDownloadTemplate = () => {
+ emit('download-template')
+}
+
+defineExpose({
+ uploadRef,
+ submit,
+ clearFiles: () => {
+ if (uploadRef.value) {
+ uploadRef.value.clearFiles()
+ }
+ }
+})
+</script>
+
+<style scoped>
+.dialog-footer {
+ text-align: center;
+}
+</style>
+
diff --git a/src/views/inventoryManagement/dispatchLog/index.vue b/src/views/inventoryManagement/dispatchLog/index.vue
index 751a92d..1922665 100644
--- a/src/views/inventoryManagement/dispatchLog/index.vue
+++ b/src/views/inventoryManagement/dispatchLog/index.vue
@@ -160,7 +160,7 @@
<div v-for="(item, index) in printData" :key="index" class="print-page">
<div class="delivery-note">
<div class="header">
- <div class="company-name">榧庤瘹鐟炲疄涓氭湁闄愯矗浠诲叕鍙�</div>
+ <div class="company-name">闈掓捣婀熸按宄″啘涓氬彂灞曟湁闄愬叕鍙�</div>
<div class="document-title">闆跺敭鍙戣揣鍗�</div>
</div>
@@ -558,7 +558,7 @@
<div class="print-page">
<div class="delivery-note">
<div class="header">
- <div class="company-name">榧庤瘹鐟炲疄涓氭湁闄愯矗浠诲叕鍙�</div>
+ <div class="company-name">闈掓捣婀熸按宄″啘涓氬彂灞曟湁闄愬叕鍙�</div>
<div class="document-title">闆跺敭鍙戣揣鍗�</div>
</div>
diff --git a/src/views/procurementManagement/procurementLedger/index.vue b/src/views/procurementManagement/procurementLedger/index.vue
index 86864d5..aaab9df 100644
--- a/src/views/procurementManagement/procurementLedger/index.vue
+++ b/src/views/procurementManagement/procurementLedger/index.vue
@@ -39,7 +39,6 @@
<div class="table_list">
<div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
<el-button type="primary" @click="openForm('add')">鏂板鍙拌处</el-button>
- <el-button type="success" @click="openScanAddDialog">鎵爜鏂板</el-button>
<el-button @click="handleOut">瀵煎嚭</el-button>
<el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
</div>
@@ -146,7 +145,7 @@
<el-table-column
fixed="right"
label="鎿嶄綔"
- min-width="150"
+ width="100"
align="center"
>
<template #default="scope">
@@ -157,13 +156,6 @@
@click="openForm('edit', scope.row)"
:disabled="scope.row.receiptPaymentAmount>0 || scope.row.recorderName !== userStore.nickName"
>缂栬緫</el-button
- >
- <el-button
- link
- type="success"
- size="small"
- @click="showQRCode(scope.row)"
- >鐢熸垚浜岀淮鐮�</el-button
>
</template>
@@ -225,6 +217,7 @@
<el-select
v-model="form.supplierId"
placeholder="璇烽�夋嫨"
+ filterable
clearable
>
<el-option
@@ -583,129 +576,6 @@
</template>
</el-dialog>
- <!-- 浜岀淮鐮佹樉绀哄璇濇 -->
- <el-dialog
- v-model="qrCodeDialogVisible"
- title="閲囪喘鍚堝悓鍙蜂簩缁寸爜"
- width="400px"
- center
- >
- <div style="text-align: center;">
- <img :src="qrCodeUrl" alt="浜岀淮鐮�" style="width:200px;height:200px;" />
- <div style="margin: 20px;">
- <el-button type="primary" @click="downloadQRCode">涓嬭浇浜岀淮鐮佸浘鐗�</el-button>
- </div>
- </div>
- </el-dialog>
-
- <!-- 鎵爜鏂板瀵硅瘽妗� -->
- <el-dialog
- v-model="scanAddDialogVisible"
- title="鎵爜鏂板閲囪喘鍙拌处"
- width="70%"
- @close="closeScanAddDialog"
- >
- <el-form
- :model="scanAddForm"
- label-width="140px"
- label-position="top"
- :rules="scanAddRules"
- ref="scanAddFormRef"
- >
- <el-row :gutter="20">
- <el-col :span="24">
- <el-form-item label="鎵爜鍐呭锛�">
- <el-input
- v-model="scanAddForm.scanContent"
- type="textarea"
- :rows="3"
- placeholder="璇锋壂鎻忎簩缁寸爜鎴栨墜鍔ㄨ緭鍏ラ噰璐悎鍚屼俊鎭�"
- @input="parseScanContent"
- />
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="20">
- <el-col :span="12">
- <el-form-item label="閲囪喘鍚堝悓鍙凤細" prop="purchaseContractNumber">
- <el-input
- v-model="scanAddForm.purchaseContractNumber"
- placeholder="璇疯緭鍏�"
- clearable
- />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="渚涘簲鍟嗗悕绉帮細" prop="supplierName">
- <el-input
- v-model="scanAddForm.supplierName"
- placeholder="璇疯緭鍏�"
- clearable
- />
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="20">
- <el-col :span="12">
- <el-form-item label="椤圭洰鍚嶇О锛�" prop="projectName">
- <el-input
- v-model="scanAddForm.projectName"
- placeholder="璇疯緭鍏�"
- clearable
- />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="鍚堝悓閲戦(鍏�)锛�" prop="contractAmount">
- <el-input-number
- v-model="scanAddForm.contractAmount"
- :precision="2"
- :step="0.1"
- clearable
- style="width: 100%"
- placeholder="璇疯緭鍏�"
- />
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="20">
- <el-col :span="12">
- <el-form-item label="浠樻鏂瑰紡锛�">
- <el-input
- v-model="scanAddForm.paymentMethod"
- placeholder="璇疯緭鍏�"
- clearable
- />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="褰曞叆浜猴細">
- <el-input v-model="scanAddForm.recorderName" disabled />
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="20">
- <el-col :span="24">
- <el-form-item label="澶囨敞锛�">
- <el-input
- v-model="scanAddForm.remark"
- type="textarea"
- :rows="2"
- placeholder="璇疯緭鍏ュ娉ㄤ俊鎭�"
- clearable
- />
- </el-form-item>
- </el-col>
- </el-row>
- </el-form>
- <template #footer>
- <div class="dialog-footer">
- <el-button type="primary" @click="submitScanAdd">纭鏂板</el-button>
- <el-button @click="closeScanAddDialog">鍙栨秷</el-button>
- </div>
- </template>
- </el-dialog>
-
<!-- 鎵爜鐧昏瀵硅瘽妗� -->
<el-dialog
v-model="scanDialogVisible"
@@ -848,10 +718,6 @@
import dayjs from "dayjs";
const userStore = useUserStore();
-
-// 浜岀淮鐮佺浉鍏冲彉閲�
-const qrCodeDialogVisible = ref(false);
-const qrCodeUrl = ref("");
// 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
const operationType = ref("");
@@ -1430,65 +1296,6 @@
}
};
-// 鏄剧ず浜岀淮鐮�
-const showQRCode = async (row) => {
- try {
- // 鏋勫缓浜岀淮鐮佸唴瀹癸紝鍙寘鍚噰璐悎鍚屽彿锛堢函鏂囨湰锛�
- const qrContent = row.purchaseContractNumber || '';
- // 妫�鏌ュ唴瀹规槸鍚︿负绌�
- if (!qrContent || qrContent.trim() === '') {
- proxy.$modal.msgWarning("璇ヨ娌℃湁閲囪喘鍚堝悓鍙凤紝鏃犳硶鐢熸垚浜岀淮鐮�");
- return;
- }
- qrCodeUrl.value = await QRCode.toDataURL(qrContent, {
- width: 200,
- margin: 2,
- color: {
- dark: '#000000',
- light: '#FFFFFF'
- }
- });
- qrCodeDialogVisible.value = true;
- } catch (error) {
- console.error('鐢熸垚浜岀淮鐮佸け璐�:', error);
- proxy.$modal.msgError("鐢熸垚浜岀淮鐮佸け璐ワ細" + error.message);
- }
-};
-
-// 涓嬭浇浜岀淮鐮�
-const downloadQRCode = () => {
- if (!qrCodeUrl.value) {
- proxy.$modal.msgWarning("浜岀淮鐮佹湭鐢熸垚");
- return;
- }
-
- const a = document.createElement('a');
- a.href = qrCodeUrl.value;
- a.download = `閲囪喘鍚堝悓鍙蜂簩缁寸爜_${new Date().getTime()}.png`;
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- proxy.$modal.msgSuccess("涓嬭浇鎴愬姛");
-};
-
-// 鎵爜鏂板瀵硅瘽妗嗙浉鍏冲彉閲�
-const scanAddDialogVisible = ref(false);
-const scanAddForm = reactive({
- scanContent: "",
- purchaseContractNumber: "",
- supplierName: "",
- projectName: "",
- contractAmount: "",
- paymentMethod: "",
- recorderName: "",
- scanRemark: "",
-});
-const scanAddRules = {
- purchaseContractNumber: [{ required: true, message: "璇疯緭鍏ラ噰璐悎鍚屽彿", trigger: "blur" }],
- supplierName: [{ required: true, message: "璇疯緭鍏ヤ緵搴斿晢鍚嶇О", trigger: "blur" }],
- projectName: [{ required: true, message: "璇疯緭鍏ラ」鐩悕绉�", trigger: "blur" }],
-};
-
// 鎵爜鐧昏瀵硅瘽妗嗙浉鍏冲彉閲�
const scanDialogVisible = ref(false);
const scanForm = reactive({
@@ -1504,68 +1311,6 @@
scanRemark: [{ required: true, message: "璇疯緭鍏ユ壂鐮佸娉�", trigger: "blur" }],
};
const scanRecords = ref([]);
-
-// 鎵撳紑鎵爜鏂板瀵硅瘽妗�
-const openScanAddDialog = () => {
- scanAddForm.scanContent = "";
- scanAddForm.purchaseContractNumber = "";
- scanAddForm.supplierName = "";
- scanAddForm.projectName = "";
- scanAddForm.contractAmount = "";
- scanAddForm.paymentMethod = "";
- scanAddForm.recorderName = userStore.nickName;
- scanAddForm.scanRemark = "";
- scanAddDialogVisible.value = true;
-};
-
-// 瑙f瀽鎵爜鍐呭锛堟ā鎷熻В鏋愪簩缁寸爜鏁版嵁锛�
-const parseScanContent = (content) => {
- if (!content) return;
-
- // 妯℃嫙瑙f瀽浜岀淮鐮佸唴瀹癸紝杩欓噷鍙互鏍规嵁瀹為檯闇�姹傝皟鏁磋В鏋愰�昏緫
- // 鍋囪鎵爜鍐呭鏍煎紡涓猴細鍚堝悓鍙穦渚涘簲鍟唡椤圭洰|閲戦|浠樻鏂瑰紡
- const parts = content.split('|');
- if (parts.length >= 3) {
- scanAddForm.purchaseContractNumber = parts[0] || "";
- scanAddForm.supplierName = parts[1] || "";
- scanAddForm.projectName = parts[2] || "";
- scanAddForm.contractAmount = parts[3] || "";
- scanAddForm.paymentMethod = parts[4] || "";
- }
-};
-
-// 鍏抽棴鎵爜鏂板瀵硅瘽妗�
-const closeScanAddDialog = () => {
- scanAddDialogVisible.value = false;
- proxy.resetForm("scanAddFormRef");
-};
-
-// 鎻愪氦鎵爜鏂板
-const submitScanAdd = () => {
- proxy.$refs["scanAddFormRef"].validate((valid) => {
- if (valid) {
- // 鏋勫缓鏂板鏁版嵁
- const newData = {
- purchaseContractNumber: scanAddForm.purchaseContractNumber,
- supplierName: scanAddForm.supplierName,
- projectName: scanAddForm.projectName,
- contractAmount: scanAddForm.contractAmount,
- paymentMethod: scanAddForm.paymentMethod,
- recorderName: scanAddForm.recorderName,
- entryDate: getCurrentDate(),
- remark: scanAddForm.scanRemark,
- type: 2
- };
-
- // 妯℃嫙鏂板鎴愬姛
- proxy.$modal.msgSuccess("鎵爜鏂板鎴愬姛锛�");
- closeScanAddDialog();
-
- // 鍙互閫夋嫨鏄惁鍒锋柊鍒楄〃
- // getList();
- }
- });
-};
// 鎵撳紑鎵爜鐧昏瀵硅瘽妗�
const openScanDialog = (row) => {
diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index 242aae8..6b5a5bc 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -100,7 +100,7 @@
<el-select v-model="form.customerId" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'view'" filterable>
<el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id">
{{
- item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber
+ item.customerName+'-'+item.type
}}
</el-option>
</el-select>
@@ -309,7 +309,7 @@
<div v-for="(item, index) in printData" :key="index" class="print-page">
<div class="delivery-note">
<div class="header">
- <div class="company-name">榧庤瘹鐟炲疄涓氭湁闄愯矗浠诲叕鍙�</div>
+ <div class="company-name">闈掓捣婀熸按宄″啘涓氬彂灞曟湁闄愬叕鍙�</div>
<div class="document-title">闆跺敭鍙戣揣鍗�</div>
</div>
@@ -466,6 +466,7 @@
delProduct,
delLedgerFile,
} from "@/api/salesManagement/salesLedger.js";
+import { getQuotationDetail } from "@/api/salesManagement/salesQuotation.js";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import useFormData from "@/hooks/useFormData.js";
import dayjs from "dayjs";
@@ -653,6 +654,7 @@
if (index !== -1) {
productForm.value.specificationModel = modelOptions.value[index].model;
productForm.value.unit = modelOptions.value[index].unit;
+ fetchQuotationPrice();
} else {
productForm.value.specificationModel = null;
productForm.value.unit = null;
@@ -671,6 +673,29 @@
}
}
return null; // 娌℃湁鎵惧埌鑺傜偣锛岃繑鍥瀗ull
+};
+// 鏍规嵁鎶ヤ环鎺ュ彛鍥炲~鍗曚环
+const fetchQuotationPrice = async () => {
+ // 闇�瑕佸鎴风被鍨嬨�佷骇鍝佸悕绉般�佽鏍�
+ const customer = customerOption.value.find((c) => c.id === form.value.customerId);
+ const customerType = customer?.customerType || customer?.type;
+ const productName = productForm.value.productCategory;
+ const specification = productForm.value.specificationModel;
+
+ try {
+ const { data } = await getQuotationDetail({
+ type: customerType,
+ productName,
+ specification,
+ });
+ const price = data;
+ if (price !== null && price !== undefined) {
+ productForm.value.taxInclusiveUnitPrice = Number(price);
+ mathNum(); // 閲嶆柊璁$畻鎬讳环
+ }
+ } catch (error) {
+ console.error("鑾峰彇鎶ヤ环鍗曚环澶辫触", error);
+ }
};
function convertIdToValue(data) {
return data.map((item) => {
@@ -1153,7 +1178,7 @@
<div class="print-page">
<div class="delivery-note">
<div class="header">
- <div class="company-name">榧庤瘹鐟炲疄涓氭湁闄愯矗浠诲叕鍙�</div>
+ <div class="company-name">闈掓捣婀熸按宄″啘涓氬彂灞曟湁闄愬叕鍙�</div>
<div class="document-title">闆跺敭鍙戣揣鍗�</div>
</div>
diff --git a/src/views/salesManagement/salesQuotation/index.vue b/src/views/salesManagement/salesQuotation/index.vue
index cefc769..7da8b7e 100644
--- a/src/views/salesManagement/salesQuotation/index.vue
+++ b/src/views/salesManagement/salesQuotation/index.vue
@@ -15,15 +15,6 @@
</template>
</el-input>
</el-col>
- <el-col :span="8">
- <el-select v-model="searchForm.customer" placeholder="璇烽�夋嫨瀹㈡埛" clearable>
- <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.customerName">
- {{
- item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber
- }}
- </el-option>
- </el-select>
- </el-col>
<!-- <el-col :span="6">-->
<!-- <el-select v-model="searchForm.status" placeholder="璇烽�夋嫨鎶ヤ环鐘舵��" clearable>-->
<!-- <el-option label="鑽夌" value="鑽夌"></el-option>-->
@@ -51,28 +42,14 @@
height="calc(100vh - 22em)"
>
<el-table-column align="center" label="搴忓彿" type="index" width="60" />
- <el-table-column prop="quotationNo" label="鎶ヤ环鍗曞彿" width="150" />
- <el-table-column prop="customer" label="瀹㈡埛鍚嶇О" />
- <el-table-column prop="salesperson" label="涓氬姟鍛�" width="100" />
- <el-table-column prop="quotationDate" label="鎶ヤ环鏃ユ湡" width="120" />
- <el-table-column prop="validDate" label="鏈夋晥鏈熻嚦" width="120" />
- <el-table-column prop="totalAmount" label="鎶ヤ环閲戦" width="120">
- <template #default="scope">
- 楼{{ scope.row.totalAmount.toFixed(2) }}
- </template>
- </el-table-column>
-<!-- <el-table-column prop="status" label="鎶ヤ环鐘舵��" width="100">-->
-<!-- <template #default="scope">-->
-<!-- <el-tag :type="getStatusType(scope.row.status)">-->
-<!-- {{ scope.row.status }}-->
-<!-- </el-tag>-->
-<!-- </template>-->
-<!-- </el-table-column>-->
- <el-table-column label="鎿嶄綔" width="250" fixed="right" align="center">
+ <el-table-column prop="quotationNo" label="鎶ヤ环鍗曞彿" />
+ <el-table-column prop="salesperson" label="涓氬姟鍛�" />
+ <el-table-column prop="quotationDate" label="鎶ヤ环鏃ユ湡" />
+ <el-table-column label="鎿嶄綔" width="200" fixed="right" align="center">
<template #default="scope">
<el-button link type="primary" @click="handleView(scope.row)">鏌ョ湅</el-button>
- <el-button link type="primary" @click="handleEdit(scope.row)" v-if="scope.row.status === '鑽夌'">缂栬緫</el-button>
- <el-button link type="danger" @click="handleDelete(scope.row)" v-if="scope.row.status === '鑽夌'">鍒犻櫎</el-button>
+ <el-button link type="primary" @click="handleEdit(scope.row)" :disabled="!['寰呭鎵�','鎷掔粷'].includes(scope.row.status)">缂栬緫</el-button>
+ <el-button link type="danger" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
</template>
</el-table-column>
</el-table>
@@ -88,94 +65,62 @@
</el-card>
<!-- 鏂板/缂栬緫瀵硅瘽妗� -->
- <el-dialog v-model="dialogVisible" :title="dialogTitle" width="1300px" :close-on-click-modal="false">
- <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
- <!-- 鍩烘湰淇℃伅 -->
- <el-card class="form-card" shadow="never">
+ <FormDialog v-model="dialogVisible" :title="dialogTitle" width="85%" :close-on-click-modal="false" @close="dialogVisible = false" @confirm="handleSubmit" @cancel="dialogVisible = false">
+ <div class="quotation-form-container">
+ <el-form :model="form" :rules="rules" ref="formRef" label-width="120px" class="quotation-form">
+ <!-- 鍩烘湰淇℃伅锛堜粎淇濈暀涓氬姟鍛樺拰鎶ヤ环鏃ユ湡锛� -->
+ <el-card class="form-card" shadow="hover">
<template #header>
- <span class="card-title">鍩烘湰淇℃伅</span>
+ <div class="card-header-wrapper">
+ <el-icon class="card-icon"><Document /></el-icon>
+ <span class="card-title">鍩烘湰淇℃伅</span>
+ </div>
</template>
- <el-row :gutter="20">
- <el-col :span="12">
- <el-form-item label="瀹㈡埛鍚嶇О" prop="customer">
- <el-select v-model="form.customer" placeholder="璇烽�夋嫨瀹㈡埛" style="width: 100%" @change="handleCustomerChange">
- <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.customerName">
- {{
- item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber
- }}
- </el-option>
- </el-select>
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="涓氬姟鍛�" prop="salesperson">
- <el-select v-model="form.salesperson" placeholder="璇烽�夋嫨涓氬姟鍛�" style="width: 100%">
- <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
- :value="item.nickName" />
- </el-select>
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="20">
- <el-col :span="12">
- <el-form-item label="鎶ヤ环鏃ユ湡" prop="quotationDate">
- <el-date-picker
- v-model="form.quotationDate"
- type="date"
- placeholder="閫夋嫨鎶ヤ环鏃ユ湡"
- style="width: 100%"
- format="YYYY-MM-DD"
- value-format="YYYY-MM-DD"
- />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="鏈夋晥鏈熻嚦" prop="validDate">
- <el-date-picker
- v-model="form.validDate"
- type="date"
- placeholder="閫夋嫨鏈夋晥鏈�"
- style="width: 100%"
- format="YYYY-MM-DD"
- value-format="YYYY-MM-DD"
- />
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="20">
- <el-col :span="12">
- <el-form-item label="浠樻鏂瑰紡" prop="paymentMethod">
- <el-select v-model="form.paymentMethod" placeholder="璇烽�夋嫨浠樻鏂瑰紡" style="width: 100%">
- <el-option label="鍏ㄦ鍒颁粯" value="鍏ㄦ鍒颁粯"></el-option>
- <el-option label="鍒嗘湡浠樻" value="鍒嗘湡浠樻"></el-option>
- <el-option label="鏈堢粨" value="鏈堢粨"></el-option>
- </el-select>
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="浜よ揣鏈�" prop="deliveryPeriod">
- <el-date-picker
- v-model="form.deliveryPeriod"
- type="date"
- placeholder="閫夋嫨浜よ揣鏈�"
- style="width: 100%"
- format="YYYY-MM-DD"
- value-format="YYYY-MM-DD"
- />
- </el-form-item>
- </el-col>
- </el-row>
+ <div class="form-content">
+ <el-row :gutter="24">
+ <el-col :span="12">
+ <el-form-item label="涓氬姟鍛�" prop="salesperson">
+ <el-select v-model="form.salesperson" placeholder="璇烽�夋嫨涓氬姟鍛�" style="width: 100%" clearable>
+ <el-option
+ v-for="item in userList"
+ :key="item.nickName"
+ :label="item.nickName"
+ :value="item.nickName"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鎶ヤ环鏃ユ湡" prop="quotationDate">
+ <el-date-picker
+ v-model="form.quotationDate"
+ type="date"
+ placeholder="閫夋嫨鎶ヤ环鏃ユ湡"
+ style="width: 100%"
+ format="YYYY-MM-DD"
+ value-format="YYYY-MM-DD"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </div>
</el-card>
<!-- 浜у搧淇℃伅 -->
- <el-card class="form-card" shadow="never">
+ <el-card class="form-card" shadow="hover">
<template #header>
- <div class="card-header">
+ <div class="card-header-wrapper">
+ <el-icon class="card-icon"><Box /></el-icon>
<span class="card-title">浜у搧淇℃伅</span>
- <el-button type="primary" size="small" @click="addProduct">娣诲姞浜у搧</el-button>
+ <el-button type="primary" size="small" @click="addProduct" class="header-btn">
+ <el-icon><Plus /></el-icon>
+ 娣诲姞浜у搧
+ </el-button>
</div>
</template>
- <el-table :data="form.products" border style="width: 100%">
+ <div class="form-content">
+ <el-table :data="form.products" border style="width: 100%" class="product-table" v-if="form.products.length > 0">
<el-table-column prop="product" label="浜у搧鍚嶇О" width="200">
<template #default="scope">
<el-tree-select
@@ -207,24 +152,24 @@
</el-select>
</template>
</el-table-column>
- <el-table-column prop="quantity" label="鏁伴噺">
- <template #default="scope">
- <el-input-number v-model="scope.row.quantity" :min="1" :precision="0" style="width: 100%" />
- </template>
- </el-table-column>
<el-table-column prop="unit" label="鍗曚綅">
<template #default="scope">
<el-input v-model="scope.row.unit" placeholder="鍗曚綅" />
</template>
</el-table-column>
- <el-table-column prop="unitPrice" label="鍗曚环">
+ <el-table-column prop="unitPrice" label="涓�鎵瑰晢鍗曚环">
<template #default="scope">
- <el-input-number v-model="scope.row.unitPrice" :min="0" :precision="2" style="width: 100%" @change="calculateAmount(scope.row)" />
+ <el-input-number v-model="scope.row.unitPrice" :min="0" :precision="2" style="width: 100%" />
</template>
</el-table-column>
- <el-table-column prop="amount" label="閲戦" width="120">
+ <el-table-column prop="unitPriceTwo" label="缁堢鍟嗗崟浠�">
<template #default="scope">
- <span>楼{{ scope.row.amount.toFixed(2) }}</span>
+ <el-input-number v-model="scope.row.unitPriceTwo" :min="0" :precision="2" style="width: 100%" />
+ </template>
+ </el-table-column>
+ <el-table-column prop="unitPriceThree" label="鍗曚环">
+ <template #default="scope">
+ <el-input-number v-model="scope.row.unitPriceThree" :min="0" :precision="2" style="width: 100%" />
</template>
</el-table-column>
<el-table-column label="鎿嶄綔" width="80" align="center">
@@ -233,83 +178,41 @@
</template>
</el-table-column>
</el-table>
- </el-card>
-
- <!-- 璐圭敤淇℃伅 -->
- <el-card class="form-card" shadow="never">
- <template #header>
- <span class="card-title">璐圭敤淇℃伅</span>
- </template>
- <el-row :gutter="20">
- <el-col :span="8">
- <el-form-item label="浜у搧灏忚">
- <el-input-number v-model="form.subtotal" :precision="2" :min="0" style="width: 100%" readonly />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="杩愯垂">
- <el-input-number v-model="form.freight" :precision="2" :min="0" style="width: 100%" @change="calculateTotal" />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="鍏朵粬璐圭敤">
- <el-input-number v-model="form.otherFee" :precision="2" :min="0" style="width: 100%" @change="calculateTotal" />
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="20">
- <el-col :span="8">
- <el-form-item label="鎶樻墸鐜�(%)">
- <el-input-number v-model="form.discountRate" :precision="2" :min="0" :max="100" style="width: 100%" @change="calculateTotal" />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="鎶樻墸閲戦">
- <el-input-number v-model="form.discountAmount" :precision="2" :min="0" style="width: 100%" readonly />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="鎶ヤ环鎬婚">
- <el-input-number v-model="form.totalAmount" :precision="2" :min="0" style="width: 100%" readonly />
- </el-form-item>
- </el-col>
- </el-row>
+ <el-empty v-else description="鏆傛棤浜у搧锛岃鐐瑰嚮娣诲姞浜у搧" :image-size="80" />
+ </div>
</el-card>
<!-- 澶囨敞淇℃伅 -->
- <el-card class="form-card" shadow="never">
+ <el-card class="form-card" shadow="hover">
<template #header>
- <span class="card-title">澶囨敞淇℃伅</span>
+ <div class="card-header-wrapper">
+ <el-icon class="card-icon"><EditPen /></el-icon>
+ <span class="card-title">澶囨敞淇℃伅</span>
+ </div>
</template>
- <el-form-item label="澶囨敞" prop="remark">
- <el-input type="textarea" v-model="form.remark" placeholder="璇疯緭鍏ュ娉ㄤ俊鎭�" rows="3"></el-input>
- </el-form-item>
+ <div class="form-content">
+ <el-form-item label="澶囨敞" prop="remark">
+ <el-input
+ type="textarea"
+ v-model="form.remark"
+ placeholder="璇疯緭鍏ュ娉ㄤ俊鎭紙閫夊~锛�"
+ :rows="4"
+ maxlength="500"
+ show-word-limit
+ ></el-input>
+ </el-form-item>
+ </div>
</el-card>
</el-form>
- <template #footer>
- <div class="dialog-footer">
- <el-button @click="dialogVisible = false">鍙� 娑�</el-button>
- <el-button type="primary" @click="handleSubmit">纭� 瀹�</el-button>
- </div>
- </template>
- </el-dialog>
+ </div>
+ </FormDialog>
<!-- 鏌ョ湅璇︽儏瀵硅瘽妗� -->
<el-dialog v-model="viewDialogVisible" title="鎶ヤ环璇︽儏" width="800px">
<el-descriptions :column="2" border>
<el-descriptions-item label="鎶ヤ环鍗曞彿">{{ currentQuotation.quotationNo }}</el-descriptions-item>
- <el-descriptions-item label="瀹㈡埛鍚嶇О">{{ currentQuotation.customer }}</el-descriptions-item>
<el-descriptions-item label="涓氬姟鍛�">{{ currentQuotation.salesperson }}</el-descriptions-item>
<el-descriptions-item label="鎶ヤ环鏃ユ湡">{{ currentQuotation.quotationDate }}</el-descriptions-item>
- <el-descriptions-item label="鏈夋晥鏈熻嚦">{{ currentQuotation.validDate }}</el-descriptions-item>
- <el-descriptions-item label="浠樻鏂瑰紡">{{ currentQuotation.paymentMethod }}</el-descriptions-item>
- <el-descriptions-item label="浜よ揣鏈�">{{ currentQuotation.deliveryPeriod }}</el-descriptions-item>
-<!-- <el-descriptions-item label="鎶ヤ环鐘舵��">-->
-<!-- <el-tag :type="getStatusType(currentQuotation.status)">{{ currentQuotation.status }}</el-tag>-->
-<!-- </el-descriptions-item>-->
- <el-descriptions-item label="鎶ヤ环鎬婚" :span="2">
- <span style="font-size: 18px; color: #e6a23c; font-weight: bold;">楼{{ currentQuotation.totalAmount?.toFixed(2) }}</span>
- </el-descriptions-item>
</el-descriptions>
<div style="margin-top: 20px;">
@@ -317,16 +220,20 @@
<el-table :data="currentQuotation.products" border style="width: 100%">
<el-table-column prop="product" label="浜у搧鍚嶇О" />
<el-table-column prop="specification" label="瑙勬牸鍨嬪彿" />
- <el-table-column prop="quantity" label="鏁伴噺" />
<el-table-column prop="unit" label="鍗曚綅" />
- <el-table-column prop="unitPrice" label="鍗曚环">
+ <el-table-column prop="unitPrice" label="涓�鎵瑰晢鍗曚环">
<template #default="scope">
楼{{ scope.row.unitPrice.toFixed(2) }}
</template>
</el-table-column>
- <el-table-column prop="amount" label="閲戦">
+ <el-table-column prop="unitPriceTwo" label="缁堢鍟嗗崟浠�">
<template #default="scope">
- 楼{{ scope.row.amount.toFixed(2) }}
+ 楼{{ scope.row.unitPriceTwo?.toFixed(2) }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="unitPriceThree" label="鍗曚环">
+ <template #default="scope">
+ 楼{{ scope.row.unitPriceThree?.toFixed(2) }}
</template>
</el-table-column>
</el-table>
@@ -343,8 +250,9 @@
<script setup>
import { ref, reactive, computed, onMounted, markRaw, shallowRef } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
-import { Search } from '@element-plus/icons-vue'
+import { Search, Document, Box, EditPen, Plus } from '@element-plus/icons-vue'
import Pagination from '@/components/PIMTable/Pagination.vue'
+import FormDialog from '@/components/Dialog/FormDialog.vue'
import {getQuotationList,addQuotation,updateQuotation,deleteQuotation} from '@/api/salesManagement/salesQuotation.js'
import {userListNoPage} from "@/api/system/user.js";
import {customerList} from "@/api/salesManagement/salesLedger.js";
@@ -354,7 +262,6 @@
const loading = ref(false)
const searchForm = reactive({
quotationNo: '',
- customer: '',
status: ''
})
@@ -377,7 +284,6 @@
quotationDate: '',
validDate: '',
paymentMethod: '',
- deliveryPeriod: '',
status: '鑽夌',
remark: '',
products: [],
@@ -390,12 +296,8 @@
})
const rules = {
- customer: [{ required: true, message: '璇烽�夋嫨瀹㈡埛', trigger: 'change' }],
salesperson: [{ required: true, message: '璇烽�夋嫨涓氬姟鍛�', trigger: 'change' }],
- quotationDate: [{ required: true, message: '璇烽�夋嫨鎶ヤ环鏃ユ湡', trigger: 'change' }],
- validDate: [{ required: true, message: '璇烽�夋嫨鏈夋晥鏈�', trigger: 'change' }],
- paymentMethod: [{ required: true, message: '璇烽�夋嫨浠樻鏂瑰紡', trigger: 'change' }],
- deliveryPeriod: [{ required: true, message: '璇烽�夋嫨浜よ揣鏈�', trigger: 'change' }]
+ quotationDate: [{ required: true, message: '璇烽�夋嫨鎶ヤ环鏃ユ湡', trigger: 'change' }]
}
const userList = ref([]);
const customerOption = ref([]);
@@ -411,9 +313,6 @@
if (searchForm.quotationNo) {
list = list.filter(item => item.quotationNo.includes(searchForm.quotationNo))
}
- if (searchForm.customer) {
- list = list.filter(item => item.customer === searchForm.customer)
- }
if (searchForm.status) {
list = list.filter(item => item.status === searchForm.status)
}
@@ -423,17 +322,16 @@
// 鏂规硶
const getStatusType = (status) => {
const statusMap = {
- '鑽夌': 'info',
- '宸插彂閫�': 'primary',
- '瀹㈡埛纭': 'success',
- '宸茶繃鏈�': 'danger'
+ '寰呭鎵�': 'info',
+ '瀹℃牳涓�': 'primary',
+ '閫氳繃': 'success',
+ '鎷掔粷': 'danger'
}
return statusMap[status] || 'info'
}
const resetSearch = () => {
searchForm.quotationNo = ''
- searchForm.customer = ''
searchForm.status = ''
}
@@ -460,8 +358,10 @@
});
}
const getProductOptions = () => {
- productTreeList().then((res) => {
+ // 杩斿洖 Promise锛屼究浜庣紪杈戞椂 await 纭繚鑳藉弽鏄�
+ return productTreeList().then((res) => {
productOptions.value = convertIdToValue(res);
+ return productOptions.value
});
};
function convertIdToValue(data) {
@@ -477,6 +377,19 @@
return newItem;
});
+}
+// 鏍规嵁鍚嶇О鍙嶆煡鑺傜偣 id锛屼究浜庝粎瀛樺悕绉版椂鐨勫弽鏄�
+function findNodeIdByLabel(nodes, label) {
+ if (!label) return null;
+ for (let i = 0; i < nodes.length; i++) {
+ const node = nodes[i];
+ if (node.label === label) return node.value;
+ if (node.children && node.children.length > 0) {
+ const found = findNodeIdByLabel(node.children, label);
+ if (found !== null && found !== undefined) return found;
+ }
+ }
+ return null;
}
const getModels = (value, row) => {
if (!row) return;
@@ -545,7 +458,6 @@
quotationDate: row.quotationDate || '',
validDate: row.validDate || '',
paymentMethod: row.paymentMethod || '',
- deliveryPeriod: row.deliveryPeriod || '',
status: row.status || '',
remark: row.remark || '',
products: row.products ? row.products.map(product => ({
@@ -555,7 +467,9 @@
specification: product.specification || '',
quantity: product.quantity || 0,
unit: product.unit || '',
- unitPrice: product.unitPrice || 0,
+ unitPrice: product.unitPrice || 0, // 涓�鎵瑰晢鍗曚环
+ unitPriceTwo: product.unitPriceTwo || product.dealerUnitPrice || 0, // 缁堢鍟嗗崟浠�
+ unitPriceThree: product.unitPriceThree || 0, // 鍗曚环
amount: product.amount || 0
})) : [],
totalAmount: row.totalAmount || 0
@@ -563,10 +477,14 @@
viewDialogVisible.value = true
}
-const handleEdit = (row) => {
+const handleEdit = async (row) => {
dialogTitle.value = '缂栬緫鎶ヤ环'
isEdit.value = true
editId.value = row.id
+ form.id = row.id || form.id || null
+ // 鍏堝姞杞戒骇鍝佹爲鏁版嵁锛屽惁鍒� el-tree-select 鏃犳硶鍙嶆樉浜у搧鍚嶇О
+ await getProductOptions()
+
// 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
form.quotationNo = row.quotationNo || ''
form.customer = row.customer || ''
@@ -574,25 +492,42 @@
form.quotationDate = row.quotationDate || ''
form.validDate = row.validDate || ''
form.paymentMethod = row.paymentMethod || ''
- form.deliveryPeriod = row.deliveryPeriod || ''
form.status = row.status || '鑽夌'
form.remark = row.remark || ''
- form.products = row.products ? row.products.map(product => ({
- productId: product.productId || '',
- product: product.product || product.productName || '',
- specificationId: product.specificationId || '',
- specification: product.specification || '',
- quantity: product.quantity || 0,
- unit: product.unit || '',
- unitPrice: product.unitPrice || 0,
- amount: product.amount || 0
- })) : []
+ form.products = row.products ? row.products.map(product => {
+ const productName = product.product || product.productName || ''
+ // 浼樺厛鐢� productId锛涘鏋滃彧鏈夊悕绉帮紝灏濊瘯鍙嶆煡 id 浠ヤ究鏍戦�夋嫨鍣ㄥ弽鏄�
+ const resolvedId = product.productId
+ ? Number(product.productId)
+ : findNodeIdByLabel(productOptions.value, productName) || ''
+ return {
+ productId: resolvedId,
+ product: productName,
+ specificationId: product.specificationId || '',
+ specification: product.specification || '',
+ quantity: product.quantity || 0,
+ unit: product.unit || '',
+ unitPrice: product.unitPrice || 0,
+ unitPriceTwo: product.unitPriceTwo || product.dealerUnitPrice || 0,
+ unitPriceThree: product.unitPriceThree || 0,
+ amount: product.amount || 0
+ }
+ }) : []
form.subtotal = row.subtotal || 0
form.freight = row.freight || 0
form.otherFee = row.otherFee || 0
form.discountRate = row.discountRate || 0
form.discountAmount = row.discountAmount || 0
form.totalAmount = row.totalAmount || 0
+
+ // 鍔犺浇鐢ㄦ埛鍒楄〃
+ let userLists = await userListNoPage();
+ userList.value = (userLists.data || []).map(item => ({
+ userId: item.userId,
+ nickName: item.nickName || '',
+ userName: item.userName || ''
+ }));
+
dialogVisible.value = true
}
@@ -625,7 +560,6 @@
form.quotationDate = ''
form.validDate = ''
form.paymentMethod = ''
- form.deliveryPeriod = ''
form.status = '鑽夌'
form.remark = ''
form.products = []
@@ -646,6 +580,8 @@
specification: '',
quantity: 1,
unit: '',
+ unitPriceTwo: 0,
+ unitPriceThree: 0,
unitPrice: 0,
amount: 0
})
@@ -683,6 +619,12 @@
return
}
+ // 璁$畻鎵�鏈変骇鍝佺殑鍗曚环鎬诲拰
+ form.totalAmount = form.products.reduce((sum, product) => {
+ const price = Number(product.unitPrice) || 0
+ return sum + price
+ }, 0)
+
if (isEdit.value) {
// 缂栬緫
const index = quotationList.value.findIndex(item => item.id === editId.value)
@@ -695,30 +637,16 @@
handleSearch()
}
})
- // quotationList.value[index] = { ...form, id: editId.value }
- // ElMessage.success('缂栬緫鎴愬姛')
}
} else {
// 鏂板
- // const newId = Math.max(...quotationList.value.map(item => item.id)) + 1
- form.quotationNo = `QT${new Date().getFullYear()}${String(new Date().getMonth() + 1).padStart(2, '0')}${String(new Date().getDate()).padStart(2, '0')}`
-
addQuotation(form).then(res=>{
- // console.log(res)
if(res.code===200){
ElMessage.success('鏂板鎴愬姛')
dialogVisible.value = false
handleSearch()
}
})
-
- // quotationList.value.push({
- // ...form,
- // // id: newId,
- // quotationNo: quotationNo
- // })
- // pagination.total++
- // ElMessage.success('鏂板鎴愬姛')
}
}
@@ -731,7 +659,7 @@
}
const handleSearch = ()=>{
const params = {
- page:pagination,
+ ...pagination,
...searchForm
}
getQuotationList(params).then(res=>{
@@ -746,7 +674,6 @@
quotationDate: item.quotationDate || '',
validDate: item.validDate || '',
paymentMethod: item.paymentMethod || '',
- deliveryPeriod: item.deliveryPeriod || '',
status: item.status || '鑽夌',
remark: item.remark || '',
products: item.products ? item.products.map(product => ({
@@ -757,6 +684,8 @@
quantity: product.quantity || 0,
unit: product.unit || '',
unitPrice: product.unitPrice || 0,
+ unitPriceTwo: product.unitPriceTwo || product.dealerUnitPrice || 0,
+ unitPriceThree: product.unitPriceThree || 0,
amount: product.amount || 0
})) : [],
subtotal: item.subtotal || 0,
@@ -784,27 +713,117 @@
})
</script>
-<style scoped>
+<style scoped lang="scss">
.search-row {
margin-bottom: 20px;
}
+.quotation-form-container {
+ padding: 10px 0;
+ max-height: calc(100vh - 200px);
+ overflow-y: auto;
+
+ &::-webkit-scrollbar {
+ width: 6px;
+ height: 6px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: #c1c1c1;
+ border-radius: 3px;
+
+ &:hover {
+ background: #a8a8a8;
+ }
+ }
+}
+
+.quotation-form {
+ .el-form-item {
+ margin-bottom: 22px;
+ }
+}
+
.form-card {
- margin-bottom: 20px;
+ margin-bottom: 24px;
+ border-radius: 8px;
+ transition: all 0.3s ease;
+
+ &:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important;
+ }
+
+ :deep(.el-card__header) {
+ padding: 16px 20px;
+ background: linear-gradient(135deg, #f5f7fa 0%, #ffffff 100%);
+ border-bottom: 1px solid #ebeef5;
+ }
+
+ :deep(.el-card__body) {
+ padding: 20px;
+ }
}
-.card-title {
- font-weight: bold;
- color: #303133;
-}
-
-.card-header {
+.card-header-wrapper {
display: flex;
- justify-content: space-between;
align-items: center;
+ gap: 8px;
+
+ .card-icon {
+ font-size: 18px;
+ color: #409eff;
+ }
+
+ .card-title {
+ font-weight: 600;
+ font-size: 16px;
+ color: #303133;
+ flex: 1;
+ }
+
+ .header-btn {
+ margin-left: auto;
+ }
+}
+
+.form-content {
+ padding: 8px 0;
+}
+
+.product-table {
+ :deep(.el-table__header) {
+ background-color: #f5f7fa;
+
+ th {
+ background-color: #f5f7fa !important;
+ color: #606266;
+ font-weight: 600;
+ }
+ }
+
+ :deep(.el-table__row) {
+ &:hover {
+ background-color: #f5f7fa;
+ }
+ }
+
+ :deep(.el-table__cell) {
+ padding: 12px 0;
+ }
}
.dialog-footer {
text-align: right;
}
+
+// 鍝嶅簲寮忎紭鍖�
+@media (max-width: 1200px) {
+ .approver-nodes-container {
+ gap: 16px;
+ }
+
+ .approver-node-item {
+ min-width: 160px;
+ }
+}
</style>
--
Gitblit v1.9.3