| | |
| | | method: "delete", |
| | | }); |
| | | }; |
| | | |
| | | // æ¥è¯¢ä¿å
»ä»»å¡éä»¶å表 |
| | | export function listMaintenanceTaskFiles(query) { |
| | | return request({ |
| | | url: "/maintenanceTaskFile/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | // æ°å¢ä¿å
»ä»»å¡éä»¶ |
| | | export function addMaintenanceTaskFile(data) { |
| | | return request({ |
| | | url: "/maintenanceTaskFile/add", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // å é¤ä¿å
»ä»»å¡éä»¶ |
| | | export function delMaintenanceTaskFile(id) { |
| | | return request({ |
| | | url: "/maintenanceTaskFile/del", |
| | | method: "delete", |
| | | data: Array.isArray(id) ? id : [id], |
| | | }); |
| | | } |
| | | |
| | |
| | | method: "get", |
| | | }); |
| | | } |
| | | export function approveProcessGetInfo(query) { |
| | | return request({ |
| | | url: '/approveProcess/get', |
| | | method: 'get', |
| | | params: query, |
| | | }) |
| | | } |
| | |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | |
| | | // æ°å¢åè´§ä¿¡æ¯ |
| | | export function addShippingInfo(data) { |
| | | return request({ |
| | | url: "/shippingInfo/add", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/sales/salesAccount/out", |
| | | "style": { |
| | | "navigationBarTitleText": "åè´§ç¶æ", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/sales/salesAccount/goOut", |
| | | "style": { |
| | | "navigationBarTitleText": "åè´§", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/sales/salesAccount/detail", |
| | | "style": { |
| | | "navigationBarTitleText": "ä¿®æ¹å°è´¦", |
| | |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/equipmentManagement/upkeep/fileList", |
| | | "style": { |
| | | "navigationBarTitleText": "ä¿å
»æä»¶ç®¡ç", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/equipmentManagement/inspection/index", |
| | | "style": { |
| | | "navigationBarTitleText": "设å¤å·¡æ£", |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="file-list-page"> |
| | | <!-- 页é¢å¤´é¨ --> |
| | | <PageHeader title="é件管ç" |
| | | @back="goBack" /> |
| | | <!-- éä»¶å表 --> |
| | | <view class="file-list-container"> |
| | | <view v-if="fileList.length > 0" |
| | | class="file-list"> |
| | | <view v-for="(file, index) in fileList" |
| | | :key="file.id || index" |
| | | class="file-item"> |
| | | <!-- æä»¶å¾æ --> |
| | | <!-- <view class="file-icon" |
| | | :class="getFileIconClass(file.fileType)"> |
| | | <up-icon :name="getFileIcon(file.fileType)" |
| | | size="24" |
| | | color="#ffffff" /> |
| | | </view> --> |
| | | <!-- æä»¶ä¿¡æ¯ --> |
| | | <view class="file-info"> |
| | | <text class="file-name">{{ file.name }}</text> |
| | | <!-- <text class="file-meta">{{ formatFileSize(file.fileSize) }} · {{ file.uploadTime || file.createTime }}</text> --> |
| | | </view> |
| | | <!-- æä½æé® --> |
| | | <view class="file-actions"> |
| | | <!-- <u-button size="small" |
| | | type="primary" |
| | | plain |
| | | @click="previewFile(file)">é¢è§</u-button> --> |
| | | <u-button size="small" |
| | | type="info" |
| | | plain |
| | | @click="downloadFile(file)">ä¸è½½å¹¶é¢è§</u-button> |
| | | <u-button size="small" |
| | | type="error" |
| | | plain |
| | | @click="confirmDelete(file, index)">å é¤</u-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- ç©ºç¶æ --> |
| | | <view v-else |
| | | class="empty-state"> |
| | | <up-icon name="document" |
| | | size="64" |
| | | color="#c0c4cc" /> |
| | | <text class="empty-text">ææ éä»¶</text> |
| | | </view> |
| | | </view> |
| | | <!-- <a rel="nofollow" |
| | | id="downloadLink" |
| | | href="#" |
| | | style="display:none;">ä¸è½½ææ¬æä»¶</a> --> |
| | | <!-- ä¸ä¼ æé® --> |
| | | <view class="upload-button" |
| | | @click="chooseFile"> |
| | | <up-icon name="plus" |
| | | size="24" |
| | | color="#ffffff" /> |
| | | <text class="upload-text">ä¸ä¼ éä»¶</text> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from "vue"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import config from "@/config"; |
| | | import { getToken } from "@/utils/auth"; |
| | | // import { saveAs } from "file-saver"; |
| | | import { |
| | | listMaintenanceTaskFiles, |
| | | addMaintenanceTaskFile, |
| | | delMaintenanceTaskFile, |
| | | } from "@/api/equipmentManagement/upkeep"; |
| | | import { blobValidate } from "@/utils/ruoyi"; |
| | | |
| | | // éä»¶å表 |
| | | const fileList = ref([]); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | // const request = axios.create({ |
| | | // baseURL: "URL.com", |
| | | // adapter: axiosAdapterUniapp, |
| | | // }); |
| | | // è·åæä»¶å¾æ |
| | | const getFileIcon = fileType => { |
| | | const iconMap = { |
| | | doc: "document", |
| | | docx: "document", |
| | | xls: "grid", |
| | | xlsx: "grid", |
| | | pdf: "document", |
| | | ppt: "copy", |
| | | pptx: "copy", |
| | | txt: "document", |
| | | jpg: "image", |
| | | jpeg: "image", |
| | | png: "image", |
| | | gif: "image", |
| | | zip: "folder", |
| | | rar: "folder", |
| | | }; |
| | | return iconMap[fileType.toLowerCase()] || "document"; |
| | | }; |
| | | |
| | | // è·åæä»¶å¾æ æ ·å¼ç±» |
| | | const getFileIconClass = fileType => { |
| | | const colorMap = { |
| | | doc: "blue", |
| | | docx: "blue", |
| | | xls: "green", |
| | | xlsx: "green", |
| | | pdf: "red", |
| | | ppt: "orange", |
| | | pptx: "orange", |
| | | txt: "gray", |
| | | jpg: "purple", |
| | | jpeg: "purple", |
| | | png: "purple", |
| | | gif: "purple", |
| | | zip: "yellow", |
| | | rar: "yellow", |
| | | }; |
| | | return colorMap[fileType.toLowerCase()] || "gray"; |
| | | }; |
| | | |
| | | // æ ¼å¼åæä»¶å¤§å° |
| | | const formatFileSize = bytes => { |
| | | if (bytes === 0) return "0 B"; |
| | | const k = 1024; |
| | | const sizes = ["B", "KB", "MB", "GB"]; |
| | | const i = Math.floor(Math.log(bytes) / Math.log(k)); |
| | | return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; |
| | | }; |
| | | |
| | | // éæ©æä»¶ |
| | | const chooseFile = () => { |
| | | uni.chooseImage({ |
| | | count: 9, |
| | | sizeType: ["original", "compressed"], |
| | | sourceType: ["album", "camera"], |
| | | success: res => { |
| | | console.log(res, "éæ©å¾çæå"); |
| | | uploadFiles(res.tempFiles); |
| | | }, |
| | | fail: err => { |
| | | console.error("éæ©å¾ç失败:", err); |
| | | showToast("éæ©æä»¶å¤±è´¥"); |
| | | }, |
| | | }); |
| | | // uni.chooseFile({ |
| | | // count: 9, |
| | | // extension: [ |
| | | // ".doc", |
| | | // ".docx", |
| | | // ".xls", |
| | | // ".xlsx", |
| | | // ".pdf", |
| | | // ".ppt", |
| | | // ".pptx", |
| | | // ".txt", |
| | | // ".jpg", |
| | | // ".jpeg", |
| | | // ".png", |
| | | // ".gif", |
| | | // ".zip", |
| | | // ".rar", |
| | | // ], |
| | | // success: res => { |
| | | // console.log(res, "éæ©æä»¶æå"); |
| | | // uploadFiles(res.tempFiles); |
| | | // }, |
| | | // fail: err => { |
| | | // showToast("éæ©æä»¶å¤±è´¥"); |
| | | // }, |
| | | // }); |
| | | }; |
| | | |
| | | // ä¸ä¼ æä»¶ |
| | | const uploadFiles = tempFiles => { |
| | | console.log(tempFiles, "ä¸ä¼ æä»¶1"); |
| | | tempFiles.forEach((tempFile, index) => { |
| | | // æ¾ç¤ºä¸ä¼ ä¸æç¤º |
| | | uni.showLoading({ |
| | | title: "ä¸ä¼ ä¸...", |
| | | mask: true, |
| | | }); |
| | | console.log(tempFile, "ä¸ä¼ æä»¶2"); |
| | | // 1. ç´æ¥ä½¿ç¨ uni.uploadFile ä¸ä¼ æä»¶ |
| | | uni.uploadFile({ |
| | | url: config.baseUrl + "/file/upload", |
| | | filePath: tempFile.path, |
| | | name: "file", |
| | | header: { |
| | | Authorization: "Bearer " + getToken(), |
| | | }, |
| | | success: uploadRes => { |
| | | uni.hideLoading(); |
| | | console.log(uploadRes, "ä¸ä¼ æä»¶3"); |
| | | |
| | | try { |
| | | const res = JSON.parse(uploadRes.data); |
| | | console.log(res, "ä¸ä¼ æä»¶4"); |
| | | if (res.code === 200) { |
| | | // 2. æåæä»¶ä¿¡æ¯ |
| | | const fileName = tempFile.name |
| | | ? tempFile.name |
| | | : tempFile.path.split("/").pop(); |
| | | // const fileType = fileName.split(".").pop(); |
| | | // 3. æé ä¿åæä»¶ä¿¡æ¯çåæ° |
| | | const saveData = { |
| | | name: fileName, |
| | | deviceMaintenanceId: upkeepId.value, |
| | | url: res.data.tempPath || "", |
| | | }; |
| | | console.log(saveData, "ä¿åæä»¶ä¿¡æ¯åæ°"); |
| | | // 4. è°ç¨ addRuleFile æ¥å£ä¿åæä»¶ä¿¡æ¯ |
| | | addMaintenanceTaskFile(saveData) |
| | | .then(addRes => { |
| | | if (addRes.code === 200) { |
| | | // 5. æ·»å å°æä»¶å表 |
| | | const newFile = { |
| | | ...addRes.data, |
| | | uploadTime: new Date().toLocaleString(), |
| | | }; |
| | | // fileList.value.push(newFile); |
| | | getFileList(); |
| | | showToast("ä¸ä¼ æå"); |
| | | } else { |
| | | showToast("ä¿åæä»¶ä¿¡æ¯å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | console.error("ä¿åæä»¶ä¿¡æ¯å¤±è´¥:", err); |
| | | showToast("ä¿åæä»¶ä¿¡æ¯å¤±è´¥"); |
| | | }); |
| | | } else { |
| | | showToast("æä»¶ä¸ä¼ 失败"); |
| | | } |
| | | } catch (e) { |
| | | console.error("è§£æä¸ä¼ ç»æå¤±è´¥:", e); |
| | | showToast("ä¸ä¼ 失败"); |
| | | } |
| | | }, |
| | | fail: err => { |
| | | uni.hideLoading(); |
| | | console.error("ä¸ä¼ 失败:", err); |
| | | showToast("ä¸ä¼ 失败"); |
| | | }, |
| | | }); |
| | | }); |
| | | }; |
| | | // ä¸è½½æä»¶ |
| | | const downloadFile = file => { |
| | | var url = |
| | | config.baseUrl + |
| | | "/common/download?fileName=" + |
| | | encodeURIComponent(file.url) + |
| | | "&delete=true"; |
| | | console.log(url, "url"); |
| | | |
| | | uni |
| | | .downloadFile({ |
| | | url: url, |
| | | responseType: "blob", |
| | | header: { Authorization: "Bearer " + getToken() }, |
| | | }) |
| | | .then(res => { |
| | | let osType = uni.getStorageSync("deviceInfo").osName; |
| | | let filePath = res.tempFilePath; |
| | | if (osType === "ios") { |
| | | uni.openDocument({ |
| | | filePath: filePath, |
| | | showMenu: true, |
| | | success: res => {}, |
| | | fail: err => { |
| | | console.log("uni.openDocument--fail"); |
| | | reject(err); |
| | | }, |
| | | }); |
| | | } else { |
| | | uni.saveFile({ |
| | | tempFilePath: filePath, |
| | | success: fileRes => { |
| | | uni.showToast({ |
| | | icon: "none", |
| | | mask: true, |
| | | title: |
| | | "æä»¶å·²ä¿åï¼Android/data/uni.UNI720216F/apps/__UNI__720216F/" + |
| | | fileRes.savedFilePath, //ä¿åè·¯å¾ |
| | | duration: 3000, |
| | | }); |
| | | setTimeout(() => { |
| | | //æå¼ææ¡£æ¥ç |
| | | uni.openDocument({ |
| | | filePath: fileRes.savedFilePath, |
| | | success: function (res) {}, |
| | | }); |
| | | }, 1000); |
| | | }, |
| | | fail: err => { |
| | | console.log("uni.save--fail"); |
| | | reject(err); |
| | | }, |
| | | }); |
| | | } |
| | | // const isBlob = blobValidate(res.data); |
| | | // if (isBlob) { |
| | | // const blob = new Blob([res.data], { type: "text/plain" }); |
| | | // const url = URL.createObjectURL(blob); |
| | | // const downloadLink = document.getElementById("downloadLink"); |
| | | // downloadLink.href = url; |
| | | // downloadLink.download = file.name; |
| | | // downloadLink.click(); |
| | | // showToast("ä¸è½½æå"); |
| | | // } else { |
| | | // showToast("ä¸è½½å¤±è´¥"); |
| | | // } |
| | | }) |
| | | .catch(err => { |
| | | console.error("ä¸è½½å¤±è´¥:", err); |
| | | showToast("ä¸è½½å¤±è´¥"); |
| | | }); |
| | | }; |
| | | |
| | | // 确认å é¤ |
| | | const confirmDelete = (file, index) => { |
| | | uni.showModal({ |
| | | title: "å é¤ç¡®è®¤", |
| | | content: `ç¡®å®è¦å é¤éä»¶ "${file.name}" åï¼`, |
| | | success: res => { |
| | | if (res.confirm) { |
| | | deleteFile(file.id, index); |
| | | } |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // å 餿件 |
| | | const deleteFile = (fileId, index) => { |
| | | uni.showLoading({ |
| | | title: "å é¤ä¸...", |
| | | mask: true, |
| | | }); |
| | | |
| | | delMaintenanceTaskFile([fileId]) |
| | | .then(res => { |
| | | uni.hideLoading(); |
| | | if (res.code === 200) { |
| | | // fileList.value.splice(index, 1); |
| | | getFileList(); |
| | | showToast("å 餿å"); |
| | | } else { |
| | | showToast("å é¤å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | uni.hideLoading(); |
| | | showToast("å é¤å¤±è´¥"); |
| | | }); |
| | | }; |
| | | |
| | | // æ¾ç¤ºæç¤º |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | const rulesRegulationsManagementId = ref(""); |
| | | const upkeepId = ref(""); |
| | | // 页é¢å è½½æ¶ |
| | | onMounted(() => { |
| | | // ä» API è·åéä»¶å表 |
| | | |
| | | // 仿¬å°åå¨è·å rulesRegulationsManagementId |
| | | rulesRegulationsManagementId.value = uni.getStorageSync( |
| | | "rulesRegulationsManagement" |
| | | ); |
| | | upkeepId.value = uni.getStorageSync("upkeepId"); |
| | | getFileList(); |
| | | }); |
| | | |
| | | // è·åéä»¶å表 |
| | | const getFileList = () => { |
| | | uni.showLoading({ |
| | | title: "å è½½ä¸...", |
| | | mask: true, |
| | | }); |
| | | |
| | | listMaintenanceTaskFiles({ |
| | | current: 1, |
| | | size: 100, |
| | | deviceMaintenanceId: upkeepId.value, |
| | | rulesRegulationsManagementId: upkeepId.value, |
| | | }) |
| | | .then(res => { |
| | | uni.hideLoading(); |
| | | if (res.code === 200) { |
| | | fileList.value = res.data.records || []; |
| | | } else { |
| | | showToast("è·åéä»¶å表失败"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | uni.hideLoading(); |
| | | showToast("è·åéä»¶å表失败"); |
| | | }); |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "../../../styles/sales-common.scss"; |
| | | |
| | | .file-list-page { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 100rpx; |
| | | } |
| | | |
| | | .file-list-container { |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .file-list { |
| | | background: #ffffff; |
| | | border-radius: 8rpx; |
| | | overflow: hidden; |
| | | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .file-item { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 20rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | |
| | | &:last-child { |
| | | border-bottom: none; |
| | | } |
| | | } |
| | | |
| | | .file-icon { |
| | | width: 56rpx; |
| | | height: 56rpx; |
| | | border-radius: 8rpx; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | margin-right: 20rpx; |
| | | |
| | | &.blue { |
| | | background: #409eff; |
| | | } |
| | | |
| | | &.green { |
| | | background: #67c23a; |
| | | } |
| | | |
| | | &.red { |
| | | background: #f56c6c; |
| | | } |
| | | |
| | | &.orange { |
| | | background: #e6a23c; |
| | | } |
| | | |
| | | &.gray { |
| | | background: #909399; |
| | | } |
| | | |
| | | &.purple { |
| | | background: #909399; |
| | | } |
| | | |
| | | &.yellow { |
| | | background: #e6a23c; |
| | | } |
| | | } |
| | | |
| | | .file-info { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .file-name { |
| | | display: block; |
| | | font-size: 16px; |
| | | color: #303133; |
| | | margin-bottom: 8rpx; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .file-meta { |
| | | display: block; |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .file-actions { |
| | | display: flex; |
| | | gap: 12rpx; |
| | | } |
| | | |
| | | .empty-state { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 100rpx 0; |
| | | background: #ffffff; |
| | | border-radius: 8rpx; |
| | | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .empty-text { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | margin-top: 20rpx; |
| | | } |
| | | |
| | | .upload-button { |
| | | position: fixed; |
| | | bottom: 40rpx; |
| | | right: 40rpx; |
| | | width: 130rpx; |
| | | height: 130rpx; |
| | | border-radius: 50%; |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | box-shadow: 0 4rpx 16rpx rgba(102, 126, 234, 0.4); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | .upload-text { |
| | | font-size: 10px; |
| | | color: #ffffff; |
| | | margin-top: 4rpx; |
| | | } |
| | | |
| | | .upload-progress { |
| | | padding: 40rpx 0; |
| | | } |
| | | |
| | | .upload-progress-text { |
| | | display: block; |
| | | text-align: center; |
| | | margin-top: 20rpx; |
| | | font-size: 14px; |
| | | color: #606266; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <view class="sales-account"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="设å¤ä¿å
»" @back="goBack" /> |
| | | |
| | | <PageHeader title="设å¤ä¿å
»" |
| | | @back="goBack" /> |
| | | <!-- æç´¢åºå --> |
| | | <view class="search-section"> |
| | | <view class="search-bar"> |
| | | <view class="search-input"> |
| | | <up-input |
| | | class="search-text" |
| | | placeholder="请è¾å
¥è®¾å¤åç§°æç´¢" |
| | | v-model="searchKeyword" |
| | | @change="getList" |
| | | clearable |
| | | /> |
| | | <up-input class="search-text" |
| | | placeholder="请è¾å
¥è®¾å¤åç§°æç´¢" |
| | | v-model="searchKeyword" |
| | | @change="getList" |
| | | clearable /> |
| | | </view> |
| | | <view class="filter-button" @click="getList"> |
| | | <up-icon name="search" size="24" color="#999"></up-icon> |
| | | <view class="filter-button" |
| | | @click="getList"> |
| | | <up-icon name="search" |
| | | size="24" |
| | | color="#999"></up-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 设å¤ä¿å
»å表 --> |
| | | <view class="ledger-list" v-if="upkeepList.length > 0"> |
| | | <view v-for="(item, index) in upkeepList" :key="index"> |
| | | <view class="ledger-item" @click="toggleSelection(item)"> |
| | | <view class="ledger-list" |
| | | v-if="upkeepList.length > 0"> |
| | | <view v-for="(item, index) in upkeepList" |
| | | :key="index"> |
| | | <view class="ledger-item" |
| | | @click="toggleSelection(item)"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <view class="document-icon"> |
| | | <up-icon name="file-text" size="16" color="#ffffff"></up-icon> |
| | | <up-icon name="file-text" |
| | | size="16" |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="item-id">设å¤åç§°ï¼{{ item.deviceName }}</text> |
| | | </view> |
| | | <view class="status-tag"> |
| | | <u-tag v-if="item.status === 1" type="success">å®ç»</u-tag> |
| | | <u-tag v-if="item.status === 0" type="error">å¾
ä¿å
»</u-tag> |
| | | <u-tag v-if="item.status === 1" |
| | | type="success">å®ç»</u-tag> |
| | | <u-tag v-if="item.status === 0" |
| | | type="error">å¾
ä¿å
»</u-tag> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">è§æ ¼åå·</text> |
| | |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ä¿å
ȍȾ</text> |
| | | <view class="detail-value"> |
| | | <u-tag v-if="item.maintenanceResult === 1" type="success" size="mini"> |
| | | å®å¥½ |
| | | </u-tag> |
| | | <u-tag v-if="item.maintenanceResult === 0" type="error" size="mini"> |
| | | ç»´ä¿® |
| | | </u-tag> |
| | | <text v-if="item.maintenanceResult === undefined || item.maintenanceResult === null">-</text> |
| | | </view> |
| | | <u-tag v-if="item.maintenanceResult === 1" |
| | | type="success" |
| | | size="mini"> |
| | | å®å¥½ |
| | | </u-tag> |
| | | <u-tag v-if="item.maintenanceResult === 0" |
| | | type="error" |
| | | size="mini"> |
| | | ç»´ä¿® |
| | | </u-tag> |
| | | <text v-if="item.maintenanceResult === undefined || item.maintenanceResult === null">-</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- æé®åºå --> |
| | | <view class="action-buttons"> |
| | | <u-button |
| | | type="primary" |
| | | size="small" |
| | | class="action-btn" |
| | | :disabled="item.status === 1" |
| | | @click.stop="edit(item.id)" |
| | | > |
| | | <u-button type="primary" |
| | | size="small" |
| | | class="action-btn" |
| | | :disabled="item.status === 1" |
| | | @click.stop="edit(item.id)"> |
| | | ç¼è¾ |
| | | </u-button> |
| | | <u-button |
| | | type="warning" |
| | | size="small" |
| | | class="action-btn" |
| | | :disabled="item.status === 1" |
| | | @click.stop="addMaintain(item.id)" |
| | | > |
| | | <u-button type="warning" |
| | | size="small" |
| | | class="action-btn" |
| | | :disabled="item.status === 1" |
| | | @click.stop="addMaintain(item.id)"> |
| | | ä¿å
» |
| | | </u-button> |
| | | <u-button |
| | | type="error" |
| | | size="small" |
| | | plain |
| | | class="action-btn" |
| | | @click.stop="delUpkeepByIds(item.id)" |
| | | > |
| | | <u-button type="error" |
| | | size="small" |
| | | plain |
| | | class="action-btn" |
| | | @click.stop="delUpkeepByIds(item.id)"> |
| | | å é¤ |
| | | </u-button> |
| | | <u-button type="warning" |
| | | size="small" |
| | | class="action-btn" |
| | | @click.stop="addFile(item.id)"> |
| | | éä»¶ |
| | | </u-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-else class="no-data"> |
| | | <view v-else |
| | | class="no-data"> |
| | | <text>ææ è®¾å¤ä¿å
»æ°æ®</text> |
| | | </view> |
| | | |
| | | <!-- æµ®å¨æ°å¢æé® --> |
| | | <view class="fab-button" @click="addPlan"> |
| | | <up-icon name="plus" size="24" color="#ffffff"></up-icon> |
| | | <view class="fab-button" |
| | | @click="addPlan"> |
| | | <up-icon name="plus" |
| | | size="24" |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from 'vue' |
| | | import { onShow } from '@dcloudio/uni-app' |
| | | import PageHeader from '@/components/PageHeader.vue' |
| | | import { getUpkeepPage, delUpkeep } from '@/api/equipmentManagement/upkeep' |
| | | import useUserStore from "@/store/modules/user" |
| | | // æ¾ç¤ºæç¤ºä¿¡æ¯ |
| | | const showToast = (message) => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: 'none' |
| | | }) |
| | | }; |
| | | import dayjs from "dayjs" |
| | | import { ref, onMounted } from "vue"; |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import { getUpkeepPage, delUpkeep } from "@/api/equipmentManagement/upkeep"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | // æ¾ç¤ºæç¤ºä¿¡æ¯ |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | import dayjs from "dayjs"; |
| | | |
| | | const userStore = useUserStore() |
| | | const userStore = useUserStore(); |
| | | |
| | | // æç´¢å
³é®è¯ |
| | | const searchKeyword = ref('') |
| | | // æç´¢å
³é®è¯ |
| | | const searchKeyword = ref(""); |
| | | |
| | | // 设å¤ä¿å
»æ°æ® |
| | | const upkeepList = ref([]) |
| | | // 设å¤ä¿å
»æ°æ® |
| | | const upkeepList = ref([]); |
| | | |
| | | // å¤éå表 |
| | | const multipleList = ref([]) |
| | | // å¤éå表 |
| | | const multipleList = ref([]); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack() |
| | | } |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // æ ¼å¼åæ¥æ |
| | | const formatDate = (dateStr) => { |
| | | if (!dateStr) return '' |
| | | return dayjs(dateStr).format("YYYY-MM-DD") |
| | | } |
| | | // æ ¼å¼åæ¥æ |
| | | const formatDate = dateStr => { |
| | | if (!dateStr) return ""; |
| | | return dayjs(dateStr).format("YYYY-MM-DD"); |
| | | }; |
| | | |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | const formatDateTime = (dateStr) => { |
| | | if (!dateStr) return '' |
| | | return dayjs(dateStr).format("YYYY-MM-DD HH:mm:ss") |
| | | } |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | const formatDateTime = dateStr => { |
| | | if (!dateStr) return ""; |
| | | return dayjs(dateStr).format("YYYY-MM-DD HH:mm:ss"); |
| | | }; |
| | | |
| | | // æ¥è¯¢å表 |
| | | const getList = () => { |
| | | showLoadingToast('å è½½ä¸...') |
| | | const params = { |
| | | current: -1, |
| | | size: -1, |
| | | deviceName: searchKeyword.value || undefined |
| | | } |
| | | getUpkeepPage(params) |
| | | .then((res) => { |
| | | // 妿res.data䏿¯æ°ç»ï¼è®¾ç½®ä¸ºç©ºæ°ç» |
| | | upkeepList.value = res.records || res.data?.records || [] |
| | | closeToast() |
| | | }) |
| | | .catch(() => { |
| | | closeToast() |
| | | showToast('è·åæ°æ®å¤±è´¥') |
| | | }) |
| | | } |
| | | // æ¥è¯¢å表 |
| | | const getList = () => { |
| | | showLoadingToast("å è½½ä¸..."); |
| | | const params = { |
| | | current: -1, |
| | | size: -1, |
| | | deviceName: searchKeyword.value || undefined, |
| | | }; |
| | | getUpkeepPage(params) |
| | | .then(res => { |
| | | // 妿res.data䏿¯æ°ç»ï¼è®¾ç½®ä¸ºç©ºæ°ç» |
| | | upkeepList.value = res.records || res.data?.records || []; |
| | | closeToast(); |
| | | }) |
| | | .catch(() => { |
| | | closeToast(); |
| | | showToast("è·åæ°æ®å¤±è´¥"); |
| | | }); |
| | | }; |
| | | // æ°å¢éä»¶ - 跳转å°éä»¶é¡µé¢ |
| | | const addFile = id => { |
| | | // ä½¿ç¨æ¬å°åå¨ä¼ éid |
| | | uni.setStorageSync("upkeepId", id); |
| | | uni.navigateTo({ |
| | | url: "/pages/equipmentManagement/upkeep/fileList", |
| | | }); |
| | | }; |
| | | |
| | | // æ¾ç¤ºå è½½æç¤º |
| | | const showLoadingToast = (message) => { |
| | | uni.showLoading({ |
| | | title: message, |
| | | mask: true |
| | | }); |
| | | }; |
| | | // æ¾ç¤ºå è½½æç¤º |
| | | const showLoadingToast = message => { |
| | | uni.showLoading({ |
| | | title: message, |
| | | mask: true, |
| | | }); |
| | | }; |
| | | |
| | | // å
³éæç¤º |
| | | const closeToast = () => { |
| | | uni.hideLoading(); |
| | | }; |
| | | // å
³éæç¤º |
| | | const closeToast = () => { |
| | | uni.hideLoading(); |
| | | }; |
| | | |
| | | // åæ¢éæ©ç¶æ |
| | | const toggleSelection = (item) => { |
| | | const index = multipleList.value.findIndex(selected => selected.id === item.id) |
| | | if (index > -1) { |
| | | multipleList.value.splice(index, 1) |
| | | } else { |
| | | multipleList.value.push(item) |
| | | } |
| | | } |
| | | |
| | | // æ£æ¥æ¯å¦å·²éæ© |
| | | const isSelected = (item) => { |
| | | return multipleList.value.some(selected => selected.id === item.id) |
| | | } |
| | | |
| | | // æ°å¢ä¿å
» - 跳转å°ä¿å
»é¡µé¢ |
| | | const addMaintain = (id) => { |
| | | if (!id && multipleList.value.length !== 1) { |
| | | showToast('è¯·éæ©ä¸æ¡è®°å½') |
| | | return |
| | | } |
| | | const targetId = id || multipleList.value[0].id |
| | | // ä½¿ç¨æ¬å°åå¨ä¼ éid |
| | | uni.setStorageSync('repairId', targetId) |
| | | uni.navigateTo({ |
| | | url: '/pages/equipmentManagement/upkeep/maintain' |
| | | }) |
| | | } |
| | | |
| | | // æ°å¢è®¡å - è·³è½¬å°æ°å¢é¡µé¢ |
| | | const addPlan = () => { |
| | | uni.navigateTo({ |
| | | url: '/pages/equipmentManagement/upkeep/add' |
| | | }) |
| | | } |
| | | |
| | | // ç¼è¾ - 跳转å°add页é¢ï¼éè¿idåºåæ°å¢è¿æ¯ç¼è¾ |
| | | const edit = (id) => { |
| | | if (!id) return |
| | | // ä½¿ç¨æ¬å°åå¨ä¼ éid |
| | | uni.setStorageSync('repairId', id) |
| | | uni.navigateTo({ |
| | | url: '/pages/equipmentManagement/upkeep/add' |
| | | }) |
| | | } |
| | | |
| | | // å é¤ä¿å
»æ°æ® |
| | | const delUpkeepByIds = async (ids) => { |
| | | const deleteIds = Array.isArray(ids) ? ids : [ids] |
| | | if (deleteIds.length === 0) { |
| | | showToast('è¯·éæ©è¦å é¤çè®°å½') |
| | | return |
| | | } |
| | | |
| | | uni.showModal({ |
| | | title: 'è¦å', |
| | | content: '确认å é¤ä¿å
»æ°æ®, æ¤æä½ä¸å¯é?', |
| | | confirmText: 'ç¡®å®', |
| | | cancelText: 'åæ¶', |
| | | success: async (res) => { |
| | | if (!res.confirm) return |
| | | try { |
| | | // é个å é¤ |
| | | for (const id of deleteIds) { |
| | | const response = await delUpkeep(id) |
| | | if (response.code !== 200) { |
| | | showToast('å é¤å¤±è´¥') |
| | | return |
| | | } |
| | | } |
| | | showToast('å 餿å') |
| | | multipleList.value = [] |
| | | getList() |
| | | } catch (e) { |
| | | showToast('å é¤å¤±è´¥') |
| | | } |
| | | // åæ¢éæ©ç¶æ |
| | | const toggleSelection = item => { |
| | | const index = multipleList.value.findIndex( |
| | | selected => selected.id === item.id |
| | | ); |
| | | if (index > -1) { |
| | | multipleList.value.splice(index, 1); |
| | | } else { |
| | | multipleList.value.push(item); |
| | | } |
| | | }) |
| | | } |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList() |
| | | }) |
| | | // æ£æ¥æ¯å¦å·²éæ© |
| | | const isSelected = item => { |
| | | return multipleList.value.some(selected => selected.id === item.id); |
| | | }; |
| | | |
| | | onShow(() => { |
| | | getList() |
| | | }) |
| | | // æ°å¢ä¿å
» - 跳转å°ä¿å
»é¡µé¢ |
| | | const addMaintain = id => { |
| | | if (!id && multipleList.value.length !== 1) { |
| | | showToast("è¯·éæ©ä¸æ¡è®°å½"); |
| | | return; |
| | | } |
| | | const targetId = id || multipleList.value[0].id; |
| | | // ä½¿ç¨æ¬å°åå¨ä¼ éid |
| | | uni.setStorageSync("repairId", targetId); |
| | | uni.navigateTo({ |
| | | url: "/pages/equipmentManagement/upkeep/maintain", |
| | | }); |
| | | }; |
| | | |
| | | // æ°å¢è®¡å - è·³è½¬å°æ°å¢é¡µé¢ |
| | | const addPlan = () => { |
| | | uni.navigateTo({ |
| | | url: "/pages/equipmentManagement/upkeep/add", |
| | | }); |
| | | }; |
| | | |
| | | // ç¼è¾ - 跳转å°add页é¢ï¼éè¿idåºåæ°å¢è¿æ¯ç¼è¾ |
| | | const edit = id => { |
| | | if (!id) return; |
| | | // ä½¿ç¨æ¬å°åå¨ä¼ éid |
| | | uni.setStorageSync("repairId", id); |
| | | uni.navigateTo({ |
| | | url: "/pages/equipmentManagement/upkeep/add", |
| | | }); |
| | | }; |
| | | |
| | | // å é¤ä¿å
»æ°æ® |
| | | const delUpkeepByIds = async ids => { |
| | | const deleteIds = Array.isArray(ids) ? ids : [ids]; |
| | | if (deleteIds.length === 0) { |
| | | showToast("è¯·éæ©è¦å é¤çè®°å½"); |
| | | return; |
| | | } |
| | | |
| | | uni.showModal({ |
| | | title: "è¦å", |
| | | content: "确认å é¤ä¿å
»æ°æ®, æ¤æä½ä¸å¯é?", |
| | | confirmText: "ç¡®å®", |
| | | cancelText: "åæ¶", |
| | | success: async res => { |
| | | if (!res.confirm) return; |
| | | try { |
| | | // é个å é¤ |
| | | for (const id of deleteIds) { |
| | | const response = await delUpkeep(id); |
| | | if (response.code !== 200) { |
| | | showToast("å é¤å¤±è´¥"); |
| | | return; |
| | | } |
| | | } |
| | | showToast("å 餿å"); |
| | | multipleList.value = []; |
| | | getList(); |
| | | } catch (e) { |
| | | showToast("å é¤å¤±è´¥"); |
| | | } |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | |
| | | onShow(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import '@/styles/sales-common.scss'; |
| | | @import "@/styles/sales-common.scss"; |
| | | |
| | | // 设å¤ä¿å
»ç¹ææ ·å¼ |
| | | .sales-account { |
| | | padding-bottom: 80px; // ä¸ºæµ®å¨æé®çåºç©ºé´ |
| | | } |
| | | // 设å¤ä¿å
»ç¹ææ ·å¼ |
| | | .sales-account { |
| | | padding-bottom: 80px; // ä¸ºæµ®å¨æé®çåºç©ºé´ |
| | | } |
| | | |
| | | .action-section { |
| | | padding: 10px 20px; |
| | | background: #ffffff; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | .action-section { |
| | | padding: 10px 20px; |
| | | background: #ffffff; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .action-section .action-buttons { |
| | | gap: 8px; // ä¸å
Œ
±æ ·å¼ä¸ç 12px ä¸å |
| | | justify-content: flex-start; |
| | | } |
| | | .action-section .action-buttons { |
| | | gap: 8px; // ä¸å
Œ
±æ ·å¼ä¸ç 12px ä¸å |
| | | justify-content: flex-start; |
| | | } |
| | | |
| | | .checkbox-wrapper { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | .checkbox-wrapper { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .status-tag { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | .status-tag { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .detail-label { |
| | | min-width: 80px; // ä¸å
Œ
±æ ·å¼ä¸ç 60px ä¸å |
| | | } |
| | | .detail-label { |
| | | min-width: 80px; // ä¸å
Œ
±æ ·å¼ä¸ç 60px ä¸å |
| | | } |
| | | |
| | | .detail-value { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | align-items: center; |
| | | } |
| | | .detail-value { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | align-items: center; |
| | | } |
| | | |
| | | .ledger-item .action-buttons { |
| | | gap: 8px; // ä¸å
Œ
±æ ·å¼ä¸ç 12px ä¸å |
| | | } |
| | | .ledger-item .action-buttons { |
| | | gap: 8px; // ä¸å
Œ
±æ ·å¼ä¸ç 12px ä¸å |
| | | } |
| | | </style> |
| | |
| | | name="before" |
| | | multiple |
| | | :maxCount="10" |
| | | :maxSize="1024 * 1024" |
| | | accept="video/*" |
| | | :maxSize="5 * 1024 * 1024" |
| | | accept="image/*" |
| | | :previewFullImage="true" |
| | | ></u-upload> |
| | | </view> |
| | |
| | | name="after" |
| | | multiple |
| | | :maxCount="10" |
| | | :maxSize="1024 * 1024" |
| | | accept="video/*" |
| | | :maxSize="5 * 1024 * 1024" |
| | | accept="image/*" |
| | | :previewFullImage="true" |
| | | ></u-upload> |
| | | </view> |
| | |
| | | name="issue" |
| | | multiple |
| | | :maxCount="10" |
| | | :maxSize="1024 * 1024" |
| | | accept="video/*" |
| | | :maxSize="5 * 1024 * 1024" |
| | | accept="image/*" |
| | | :previewFullImage="true" |
| | | ></u-upload> |
| | | </view> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from 'vue' |
| | | import { ref, computed } from 'vue' |
| | | import { submitInspectionRecord } from '@/api/equipmentManagement/inspection.js' |
| | | import { getToken } from '@/utils/auth' |
| | | import config from '@/config' |
| | | |
| | | const emit = defineEmits(['closeDia']) |
| | | |
| | |
| | | const issueModelValue = ref([]) |
| | | const infoData = ref(null) |
| | | |
| | | // 计ç®ä¸ä¼ URL |
| | | const uploadFileUrl = computed(() => { |
| | | let baseUrl = ''; |
| | | |
| | | if (process.env.VUE_APP_BASE_API) { |
| | | baseUrl = process.env.VUE_APP_BASE_API; |
| | | } else { |
| | | baseUrl = config.baseUrl; |
| | | } |
| | | |
| | | return baseUrl + '/file/upload'; |
| | | }) |
| | | |
| | | const uploadSingleFile = async (fileItem, typeValue) => { |
| | | const token = getToken() |
| | | if (!token) throw new Error('ç¨æ·æªç»å½') |
| | | |
| | | // H5: u-upload å¯è½ç»åç Fileï¼fileItem.fileï¼ |
| | | const rawFile = fileItem?.file |
| | | if (rawFile) { |
| | | const formData = new FormData() |
| | | formData.append('file', rawFile, rawFile.name || 'image.jpg') |
| | | formData.append('type', String(typeValue)) |
| | | const res = await fetch(uploadFileUrl.value, { |
| | | method: 'POST', |
| | | headers: { Authorization: 'Bearer ' + token }, |
| | | body: formData |
| | | }) |
| | | const data = await res.json() |
| | | if (data?.code !== 200) throw new Error(data?.msg || 'ä¸ä¼ 失败') |
| | | return { |
| | | url: data?.data?.url, |
| | | name: rawFile.name || 'image.jpg', |
| | | status: 'success' |
| | | } |
| | | } |
| | | |
| | | // é H5 / å
¼å®¹ï¼èµ° uni.uploadFile |
| | | return await new Promise((resolve, reject) => { |
| | | uni.uploadFile({ |
| | | url: uploadFileUrl.value, |
| | | filePath: fileItem.url, |
| | | name: 'file', |
| | | header: { |
| | | 'Authorization': `Bearer ${token}` |
| | | }, |
| | | formData: { |
| | | type: typeValue |
| | | }, |
| | | success: (res) => { |
| | | try { |
| | | const data = JSON.parse(res.data) |
| | | if (data.code === 200) { |
| | | resolve({ |
| | | url: data.data.url, |
| | | name: fileItem.name, |
| | | status: 'success' |
| | | }) |
| | | } else { |
| | | reject(new Error(data.msg || 'ä¸ä¼ 失败')) |
| | | } |
| | | } catch (e) { |
| | | reject(e) |
| | | } |
| | | }, |
| | | fail: (err) => reject(err) |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | // æä»¶ä¸ä¼ å¤ç |
| | | const afterRead = (event) => { |
| | | const { name, file } = event |
| | | |
| | | // ä¸ä¼ æä»¶å°æå¡å¨ |
| | | uni.uploadFile({ |
| | | url: '/api/upload', // æ¿æ¢ä¸ºå®é
çä¸ä¼ æ¥å£ |
| | | filePath: file.url, |
| | | name: 'file', |
| | | success: (res) => { |
| | | const data = JSON.parse(res.data) |
| | | if (data.code === 200) { |
| | | const fileItem = { |
| | | url: data.data.url, |
| | | name: file.name, |
| | | status: 'success' |
| | | } |
| | | |
| | | // æ ¹æ®nameæ·»å å°å¯¹åºçæ°ç» |
| | | if (name === 'before') { |
| | | beforeModelValue.value.push(fileItem) |
| | | } else if (name === 'after') { |
| | | afterModelValue.value.push(fileItem) |
| | | } else if (name === 'issue') { |
| | | issueModelValue.value.push(fileItem) |
| | | } |
| | | |
| | | uni.showToast({ |
| | | title: 'ä¸ä¼ æå', |
| | | icon: 'success' |
| | | }) |
| | | } else { |
| | | uni.showToast({ |
| | | title: 'ä¸ä¼ 失败', |
| | | icon: 'error' |
| | | }) |
| | | // æ ¹æ®ä¸ä¼ ç±»å设置ä¸åçtypeå¼ |
| | | let typeValue = 10 // é»è®¤å¼ |
| | | if (name === 'before') { |
| | | typeValue = 10 // ç产å |
| | | } else if (name === 'after') { |
| | | typeValue = 11 // çäº§ä¸ |
| | | } else if (name === 'issue') { |
| | | typeValue = 12 // ç产å |
| | | } |
| | | |
| | | const files = Array.isArray(file) ? file : [file] |
| | | Promise.resolve().then(async () => { |
| | | for (const f of files) { |
| | | const uploaded = await uploadSingleFile(f, typeValue) |
| | | if (name === 'before') { |
| | | beforeModelValue.value.push(uploaded) |
| | | } else if (name === 'after') { |
| | | afterModelValue.value.push(uploaded) |
| | | } else if (name === 'issue') { |
| | | issueModelValue.value.push(uploaded) |
| | | } |
| | | }, |
| | | fail: () => { |
| | | uni.showToast({ |
| | | title: 'ä¸ä¼ 失败', |
| | | icon: 'error' |
| | | }) |
| | | } |
| | | uni.showToast({ title: 'ä¸ä¼ æå', icon: 'success' }) |
| | | }).catch((err) => { |
| | | console.error('ä¸ä¼ 失败:', err) |
| | | uni.showToast({ title: 'ä¸ä¼ 失败', icon: 'error' }) |
| | | }) |
| | | } |
| | | |
| | |
| | | |
| | | </view> |
| | | |
| | | <!-- æ«ç åºå - å
¨å±å¼¹çª --> |
| | | <view v-if="isScanning" class="qr-scan-overlay"> |
| | | <view class="qr-scan-container"> |
| | | <view class="scan-header"> |
| | | <text class="scan-title">æ«æäºç»´ç </text> |
| | | <u-button type="error" size="small" @click.stop="stopScan" :customStyle="{ |
| | | borderRadius: '15px', |
| | | height: '30px', |
| | | fontSize: '12px' |
| | | }"> |
| | | å
³é |
| | | </u-button> |
| | | </view> |
| | | <camera class="qr-camera" device-position="back" flash="off" @scancode="handleScanCode" |
| | | @error="handleCameraError"></camera> |
| | | <view class="scan-frame-wrapper"> |
| | | <view class="scan-frame"></view> |
| | | <view class="scan-tip">请å°äºç»´ç æ¾å
¥æ¡å
</view> |
| | | </view> |
| | | <u-alert v-if="cameraError" :title="cameraError" type="error" :showIcon="true" :closable="true" |
| | | @close="cameraError = ''" :customStyle="{ |
| | | margin: '10px 0' |
| | | }"></u-alert> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- å¾çä¸ä¼ å¼¹çª - åçå®ç° --> |
| | | <view v-if="showUploadDialog" class="custom-modal-overlay" @click="closeUploadDialog"> |
| | | <view class="custom-modal-container" @click.stop> |
| | |
| | | <view v-if="getCurrentFiles().length > 0" class="file-list"> |
| | | <view v-for="(file, index) in getCurrentFiles()" :key="index" class="file-item"> |
| | | <view class="file-preview-container"> |
| | | <image v-if="file?.path?.fileType === 'image'" |
| | | :src="file?.url || file?.tempFilePath?.tempFilePath || file?.path?.tempFilePath" |
| | | <image v-if="file.type === 'image' || (file.type !== 'video' && !file.type)" |
| | | :src="file.url || file.tempFilePath || file.path || file.downloadUrl" |
| | | class="file-preview" mode="aspectFill" /> |
| | | <view v-else class="video-preview"> |
| | | <view v-else-if="file.type === 'video'" class="video-preview"> |
| | | <uni-icons type="videocam" name="videocam" size="18" color="#fff" |
| | | style="margin-right: 5px;"></uni-icons> |
| | | <text class="video-text">è§é¢</text> |
| | |
| | | import { getLedgerById } from '@/api/equipmentManagement/ledger.js' |
| | | import { inspectionTaskList, uploadInspectionTask } from "@/api/inspectionManagement"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import config from '@/config' |
| | | |
| | | // ç»ä»¶å¼ç¨å·²ç§»é¤ |
| | | |
| | |
| | | |
| | | // ä¸ä¼ é
ç½® |
| | | const uploadConfig = { |
| | | action: "/common/minioUploads", |
| | | action: "/file/upload", |
| | | limit: 10, |
| | | fileSize: 50, // MB |
| | | fileType: ['jpg', 'jpeg', 'png', 'mp4', 'mov'], |
| | |
| | | |
| | | // 计ç®ä¸ä¼ URL |
| | | const uploadFileUrl = computed(() => { |
| | | let baseUrl = ''; |
| | | |
| | | if (process.env.VUE_APP_BASE_API) { |
| | | baseUrl = process.env.VUE_APP_BASE_API; |
| | | } else if (process.env.NODE_ENV === 'development') { |
| | | baseUrl = 'http://114.132.189.42:9068'; |
| | | } else { |
| | | baseUrl = 'http://114.132.189.42:9068'; |
| | | } |
| | | const baseUrl = 'http://114.132.189.42:9030'; |
| | | |
| | | return baseUrl + uploadConfig.action; |
| | | }) |
| | |
| | | |
| | | // 请æ±åæ¶æ å¿ï¼ç¨äºåæ¶æ£å¨è¿è¡çè¯·æ± |
| | | let isRequestCancelled = false |
| | | |
| | | // æ«ç ç¸å
³ç¶æ |
| | | const isScanning = ref(false) |
| | | const cameraError = ref('') |
| | | |
| | | const pagesPames = reactive({ |
| | | size: 10, |
| | |
| | | onUnmounted(() => { |
| | | // è®¾ç½®åæ¶æ å¿ï¼é»æ¢åç»ç弿¥æä½ |
| | | isRequestCancelled = true |
| | | |
| | | // 忢æ«ç |
| | | if (isScanning.value) { |
| | | isScanning.value = false |
| | | } |
| | | |
| | | // å
³éä¸ä¼ å¼¹çª |
| | | if (showUploadDialog.value) { |
| | |
| | | } |
| | | } |
| | | |
| | | // 为æå®ä»»å¡å¼å§æ«ç |
| | | // 为æå®ä»»å¡å¼å§æ«ç ï¼çæºï¼ |
| | | const startScanForTask = async (task) => { |
| | | try { |
| | | // è®°å½å½åæ«æçä»»å¡ |
| | | currentScanningTask.value = task |
| | | |
| | | // æ¾ç¤ºæ«æçé¢ |
| | | isScanning.value = true |
| | | |
| | | // 使ç¨uniappçæ«ç API |
| | | uni.scanCode({ |
| | | success: (res) => { |
| | | handleScanSuccess(res) |
| | | }, |
| | | fail: (err) => { |
| | | console.error('æ«ç 失败:', err) |
| | | uni.showToast({ |
| | | title: 'æ«ç 失败', |
| | | icon: 'error' |
| | | }) |
| | | // å
³éæ«æçé¢ |
| | | isScanning.value = false |
| | | } |
| | | }) |
| | | } catch (e) { |
| | | console.error('å¯å¨æ«ç 失败:', e) |
| | | uni.showToast({ |
| | | title: 'å¯å¨æ«ç 失败', |
| | | icon: 'error' |
| | | }) |
| | | isScanning.value = false |
| | | } |
| | | } |
| | | |
| | | // 忢æ«ç |
| | | const stopScan = () => { |
| | | isScanning.value = false |
| | | currentScanningTask.value = null |
| | | } |
| | | |
| | | // æ«ç æåå¤ç |
| | | const handleScanSuccess = async (result) => { |
| | | // æ«ç æåå¤çï¼æ ¡éªåæå¼ä¸ä¼ å¼¹çª |
| | | const handleScanSuccess = (result) => { |
| | | try { |
| | | // è§£æäºç»´ç æ°æ®ï¼æådeviceId |
| | | let deviceId = '' |
| | | |
| | | // æ£æ¥æ¯å¦æ¯URLæ ¼å¼ |
| | | if (result.result.includes('deviceId=')) { |
| | | // ä»URL䏿ådeviceId |
| | | const url = result.result |
| | | const match = url.match(/deviceId=(\d+)/) |
| | | if (match && match[1]) { |
| | | deviceId = match[1] |
| | | } |
| | | } else { |
| | | // å°è¯è§£æJSONæ ¼å¼ |
| | | try { |
| | | const qrData = JSON.parse(result.result) |
| | | deviceId = qrData.deviceId || qrData.qrCodeId || '' |
| | | } catch (e) { |
| | | // 妿䏿¯JSONæ ¼å¼ï¼ç´æ¥ä½¿ç¨ç»æ |
| | | deviceId = result.result |
| | | if (result?.result && typeof result.result === 'string') { |
| | | if (result.result.includes('deviceId=')) { |
| | | const match = result.result.match(/deviceId=(\d+)/) |
| | | if (match && match[1]) deviceId = match[1] |
| | | } else { |
| | | try { |
| | | const qrData = JSON.parse(result.result) |
| | | deviceId = qrData.deviceId || qrData.qrCodeId || '' |
| | | } catch (e) { |
| | | deviceId = result.result |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (!deviceId) { |
| | | uni.showToast({ |
| | | title: 'æªè¯å«å°è®¾å¤ID', |
| | | icon: 'error' |
| | | }) |
| | | isScanning.value = false |
| | | uni.showToast({ title: 'æªè¯å«å°è®¾å¤ID', icon: 'error' }) |
| | | return |
| | | } |
| | | |
| | | // è·åå½åä»»å¡çtaskId |
| | | const currentTaskId = currentScanningTask.value?.taskId || currentScanningTask.value?.id |
| | | |
| | | // 对æ¯deviceIdåtaskId |
| | | if (deviceId === currentTaskId.toString()) { |
| | | uni.showToast({ |
| | | title: 'è¯å«æå', |
| | | icon: 'success' |
| | | }) |
| | | |
| | | // å
å
³éæ«æçé¢ |
| | | isScanning.value = false |
| | | |
| | | // å»¶è¿æå¼ä¸ä¼ å¼¹çªï¼ç¡®ä¿æ«æçé¢å®å
¨å
³é |
| | | setTimeout(() => { |
| | | openUploadDialog(currentScanningTask.value) |
| | | }, 300) |
| | | } else { |
| | | uni.showToast({ |
| | | title: 'è¯·æ«ææ£ç¡®ç设å¤', |
| | | icon: 'error' |
| | | }) |
| | | |
| | | // å
³éæ«æçé¢ |
| | | isScanning.value = false |
| | | if (!currentTaskId) { |
| | | uni.showToast({ title: 'ä»»å¡ä¿¡æ¯ç¼ºå¤±', icon: 'error' }) |
| | | return |
| | | } |
| | | |
| | | if (deviceId === currentTaskId.toString()) { |
| | | uni.showToast({ title: 'è¯å«æå', icon: 'success' }) |
| | | openUploadDialog(currentScanningTask.value) |
| | | } else { |
| | | uni.showToast({ title: 'è¯·æ«ææ£ç¡®ç设å¤', icon: 'error' }) |
| | | } |
| | | } catch (error) { |
| | | console.error('æ«ç ç»æå¤ç失败:', error) |
| | | uni.showToast({ |
| | | title: error.message || 'æ°æ®è§£æå¤±è´¥', |
| | | title: error?.message || 'æ°æ®è§£æå¤±è´¥', |
| | | icon: 'error' |
| | | }) |
| | | // å
³éæ«æçé¢ |
| | | isScanning.value = false |
| | | } |
| | | } |
| | | |
| | | |
| | | // æå¼ä¸ä¼ å¼¹çª |
| | | const openUploadDialog = (task) => { |
| | |
| | | arr.push(...issueModelValue.value); |
| | | } |
| | | |
| | | // ä¼ ç»å端çä¸´æ¶æä»¶IDå表ï¼tempFileIdsï¼ |
| | | // å
¼å®¹ï¼æäºæ¥å£å¯è½è¿å tempId / tempFileId / id |
| | | let tempFileIds = [] |
| | | if (arr !== null && arr.length > 0) { |
| | | tempFileIds = arr |
| | | .map((item) => item?.tempId ?? item?.tempFileId ?? item?.id) |
| | | .filter((v) => v !== undefined && v !== null && v !== '') |
| | | } |
| | | |
| | | // æäº¤æ°æ® |
| | | infoData.value.storageBlobDTO = arr; |
| | | // æ·»å å¼å¸¸ç¶æä¿¡æ¯ |
| | | infoData.value.hasException = hasException.value; |
| | | infoData.value.tempFileIds = tempFileIds; |
| | | const result = await uploadInspectionTask({ ...infoData.value }); |
| | | |
| | | // æ£æ¥æäº¤ç»æ |
| | |
| | | } |
| | | } |
| | | |
| | | // æå头é误å¤ç |
| | | const handleCameraError = (error) => { |
| | | cameraError.value = 'æå头访é®å¤±è´¥ï¼è¯·æ£æ¥æé设置' |
| | | } |
| | | |
| | | // æ«ç äºä»¶å¤ç |
| | | const handleScanCode = (result) => { |
| | | handleScanSuccess(result) |
| | | } |
| | | |
| | | // æ¥çéä»¶ |
| | | const viewAttachments = async (task) => { |
| | | try { |
| | |
| | | // è§£ææ°çæ°æ®ç»æ |
| | | attachmentList.value = [] |
| | | |
| | | // ç产åéä»¶ (type=0) |
| | | if (task.beforeProduction && Array.isArray(task.beforeProduction)) { |
| | | const beforeFiles = task.beforeProduction.map(file => ({ |
| | | // åç«¯åæ¾åæ®µï¼ä½ æä¾çæ°æ®ç»æï¼ï¼ |
| | | // - commonFileListBeforeï¼ç产åï¼é常 type=10ï¼ |
| | | // - commonFileListAfterï¼ç产ä¸ï¼é常 type=11ï¼ |
| | | // - commonFileListï¼å¯è½æ¯å
¨é¨/å
åºï¼è¥å
å«ç产åï¼ä¸è¬ type=12ï¼ |
| | | const allList = Array.isArray(task?.commonFileList) ? task.commonFileList : [] |
| | | const beforeList = Array.isArray(task?.commonFileListBefore) |
| | | ? task.commonFileListBefore |
| | | : allList.filter(f => f?.type === 10) |
| | | const afterList = Array.isArray(task?.commonFileListAfter) |
| | | ? task.commonFileListAfter |
| | | : allList.filter(f => f?.type === 11) |
| | | // 妿å端åç»è¡¥äº commonFileListIssueï¼åä¼å
ç¨ï¼å¦åä» commonFileList éæ type=12 å
åº |
| | | const issueList = Array.isArray(task?.commonFileListIssue) |
| | | ? task.commonFileListIssue |
| | | : allList.filter(f => f?.type === 12) |
| | | |
| | | const mapToViewFile = (file, viewType) => { |
| | | const u = normalizeFileUrl(file?.url || file?.downloadUrl || '') |
| | | return { |
| | | ...file, |
| | | type: 0 // ç¡®ä¿type为0 |
| | | })) |
| | | attachmentList.value.push(...beforeFiles) |
| | | // ç¨äºä¸æ ç¾é¡µåç»ï¼0=ç产å 1=çäº§ä¸ 2=ç产å |
| | | type: viewType, |
| | | name: file?.name || file?.originalFilename || file?.bucketFilename, |
| | | bucketFilename: file?.bucketFilename || file?.name, |
| | | originalFilename: file?.originalFilename || file?.name, |
| | | url: u, |
| | | downloadUrl: u, |
| | | size: file?.size || file?.byteSize, |
| | | } |
| | | } |
| | | |
| | | // ç产ä¸éä»¶ (type=1) |
| | | if (task.afterProduction && Array.isArray(task.afterProduction)) { |
| | | const afterFiles = task.afterProduction.map(file => ({ |
| | | ...file, |
| | | type: 1 // ç¡®ä¿type为1 |
| | | })) |
| | | attachmentList.value.push(...afterFiles) |
| | | } |
| | | |
| | | // ç产åéä»¶ (type=2) |
| | | if (task.productionIssues && Array.isArray(task.productionIssues)) { |
| | | const issueFiles = task.productionIssues.map(file => ({ |
| | | ...file, |
| | | type: 2 // ç¡®ä¿type为2 |
| | | })) |
| | | attachmentList.value.push(...issueFiles) |
| | | } |
| | | attachmentList.value.push(...beforeList.map(f => mapToViewFile(f, 0))) |
| | | attachmentList.value.push(...afterList.map(f => mapToViewFile(f, 1))) |
| | | attachmentList.value.push(...issueList.map(f => mapToViewFile(f, 2))) |
| | | |
| | | showAttachmentDialog.value = true |
| | | |
| | |
| | | const getTabType = () => { |
| | | switch (currentUploadType.value) { |
| | | case 'before': |
| | | return 0 |
| | | return 10 |
| | | case 'after': |
| | | return 1 |
| | | return 11 |
| | | case 'issue': |
| | | return 2 |
| | | return 12 |
| | | default: |
| | | return 0 |
| | | return 10 |
| | | } |
| | | } |
| | | // è·åå½åæ¥çç±»åçéä»¶ |
| | |
| | | const name = file.bucketFilename || file.originalFilename || file.name || '' |
| | | const ext = name.split('.').pop()?.toLowerCase() |
| | | return ['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(ext) |
| | | } |
| | | |
| | | // æä»¶è®¿é®åºç¡åï¼åç«¯è¦æ±åç¼ï¼ |
| | | const filePreviewBase = 'http://114.132.189.42:9098' |
| | | |
| | | // å°å端è¿åçæä»¶å°åè§èæå¯è®¿é®URL |
| | | // å
¼å®¹åºæ¯ï¼ |
| | | // - å·²ç»æ¯ http/httpsï¼ç´æ¥è¿å |
| | | // - 以 / å¼å¤´ï¼æ¼æ¥ filePreviewBase |
| | | // - Windows æ¬å°è·¯å¾ï¼å¦ D:\ruoyi\prod\uploads...\xx.jpgï¼ï¼å°è¯æªå prod ä¹åçç¸å¯¹è·¯å¾å¹¶æ¼æ¥ filePreviewBase |
| | | const normalizeFileUrl = (rawUrl) => { |
| | | try { |
| | | if (!rawUrl || typeof rawUrl !== 'string') return '' |
| | | const url = rawUrl.trim() |
| | | if (!url) return '' |
| | | if (/^https?:\/\//i.test(url)) return url |
| | | if (url.startsWith('/')) return `${filePreviewBase}${url}` |
| | | |
| | | // Windows path -> web path |
| | | if (/^[a-zA-Z]:\\/.test(url)) { |
| | | const normalized = url.replace(/\\/g, '/') |
| | | const idx = normalized.indexOf('/prod/') |
| | | if (idx >= 0) { |
| | | const relative = normalized.slice(idx + '/prod/'.length) |
| | | return `${filePreviewBase}/${relative}` |
| | | } |
| | | // å
åºï¼æ æ³æ¨ææ å°è§åæ¶ï¼è³å°æåææ åææ£ææ |
| | | return normalized |
| | | } |
| | | |
| | | // å
¶ä»ç¸å¯¹è·¯å¾ï¼ç´æ¥ç¨ baseUrl æ¼ä¸ä¸ |
| | | return `${filePreviewBase}/${url.replace(/^\//, '')}` |
| | | } catch (e) { |
| | | return rawUrl || '' |
| | | } |
| | | } |
| | | |
| | | // é¢è§éä»¶ |
| | |
| | | }) |
| | | } |
| | | |
| | | // 使ç¨ç¸æº |
| | | // æç
§/æè§é¢ï¼çæºä¼å
ç¨ chooseMediaï¼ä¸æ¯æåéçº§ï¼ |
| | | const chooseMedia = (type) => { |
| | | let mediaPamaes = { |
| | | count: 1, |
| | | mediaType: [type || 'image'], |
| | | sizeType: ['compressed', 'original'], |
| | | sourceType: ['camera'], |
| | | if (getCurrentFiles().length >= uploadConfig.limit) { |
| | | uni.showToast({ title: `æå¤åªè½éæ©${uploadConfig.limit}个æä»¶`, icon: 'none' }) |
| | | return |
| | | } |
| | | uni.chooseMedia({ |
| | | ...mediaPamaes, |
| | | success: (res) => { |
| | | try { |
| | | if (!res.tempFiles || res.tempFiles.length === 0) { |
| | | throw new Error('æªè·åå°å¾çæä»¶'); |
| | | |
| | | const remaining = uploadConfig.limit - getCurrentFiles().length |
| | | |
| | | // ä¼å
ï¼chooseMediaï¼æ¯æ image/videoï¼ |
| | | if (typeof uni.chooseMedia === 'function') { |
| | | uni.chooseMedia({ |
| | | count: Math.min(remaining, 1), |
| | | mediaType: [type || 'image'], |
| | | sizeType: ['compressed', 'original'], |
| | | sourceType: ['camera'], |
| | | success: (res) => { |
| | | try { |
| | | const files = res?.tempFiles || [] |
| | | if (!files.length) throw new Error('æªè·åå°æä»¶') |
| | | |
| | | files.forEach((tf, idx) => { |
| | | const filePath = tf.tempFilePath || tf.path || '' |
| | | const fileType = tf.fileType || type || 'image' |
| | | const ext = fileType === 'video' ? 'mp4' : 'jpg' |
| | | const file = { |
| | | tempFilePath: filePath, |
| | | path: filePath, |
| | | type: fileType, |
| | | name: `${fileType}_${Date.now()}_${idx}.${ext}`, |
| | | size: tf.size || 0, |
| | | duration: tf.duration || 0, |
| | | createTime: Date.now(), |
| | | uid: Date.now() + Math.random() + idx |
| | | } |
| | | handleBeforeUpload(file) |
| | | }) |
| | | } catch (e) { |
| | | console.error('å¤çææç»æå¤±è´¥:', e) |
| | | uni.showToast({ title: 'å¤çæä»¶å¤±è´¥', icon: 'error' }) |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | console.error('ææå¤±è´¥:', err) |
| | | uni.showToast({ title: 'ææå¤±è´¥', icon: 'error' }) |
| | | } |
| | | }) |
| | | return |
| | | } |
| | | |
| | | const tempFilePath = res.tempFiles[0]; |
| | | const tempFile = res.tempFiles && res.tempFiles[0] ? res.tempFiles[0] : {}; |
| | | |
| | | const file = { |
| | | tempFilePath: tempFilePath, |
| | | path: tempFilePath, // ä¿æå
¼å®¹æ§ |
| | | // é级ï¼chooseImage / chooseVideo |
| | | if (type === 'video') { |
| | | chooseVideo() |
| | | } else { |
| | | uni.chooseImage({ |
| | | count: 1, |
| | | sizeType: ['compressed', 'original'], |
| | | sourceType: ['camera'], |
| | | success: (res) => { |
| | | const tempFilePath = res?.tempFilePaths?.[0] |
| | | const tempFile = res?.tempFiles?.[0] || {} |
| | | if (!tempFilePath) return |
| | | handleBeforeUpload({ |
| | | tempFilePath, |
| | | path: tempFilePath, |
| | | type: 'image', |
| | | name: `photo_${Date.now()}.jpg`, |
| | | size: tempFile.size || 0, |
| | | createTime: new Date().getTime(), |
| | | createTime: Date.now(), |
| | | uid: Date.now() + Math.random() |
| | | }; |
| | | |
| | | handleBeforeUpload(file); |
| | | } catch (error) { |
| | | console.error('å¤çæç
§ç»æå¤±è´¥:', error); |
| | | uni.showToast({ |
| | | title: 'å¤çå¾ç失败', |
| | | icon: 'error' |
| | | }); |
| | | }) |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | console.error('æç
§å¤±è´¥:', err); |
| | | uni.showToast({ |
| | | title: 'æç
§å¤±è´¥: ' + (err.errMsg || 'æªç¥é误'), |
| | | icon: 'error' |
| | | }); |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | } |
| | | |
| | | // æç
§ |
| | |
| | | |
| | | // ä¸ä¼ åæ ¡éª |
| | | const handleBeforeUpload = async (file) => { |
| | | // æ£æ¥ç½ç»è¿æ¥ |
| | | const hasNetwork = await checkNetworkConnection(); |
| | | if (!hasNetwork) { |
| | | uni.showToast({ |
| | | title: 'ç½ç»è¿æ¥ä¸å¯ç¨ï¼è¯·æ£æ¥ç½ç»è®¾ç½®', |
| | | icon: 'none' |
| | | }); |
| | | return false; |
| | | } |
| | | |
| | | // æ ¡éªæä»¶å¤§å° |
| | | if (uploadConfig.fileSize && file.size) { |
| | | const isLt = file.size / 1024 / 1024 < uploadConfig.fileSize; |
| | | if (!isLt) { |
| | | uni.showToast({ |
| | | title: `æä»¶å¤§å°ä¸è½è¶
è¿ ${uploadConfig.fileSize} MB!`, |
| | | icon: 'none' |
| | | }); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | // æ ¡éªè§é¢æ¶é¿ |
| | | if (file.type === 'video' && file.duration && file.duration > uploadConfig.maxVideoDuration) { |
| | | uni.showToast({ |
| | | title: `è§é¢æ¶é¿ä¸è½è¶
è¿ ${uploadConfig.maxVideoDuration} ç§!`, |
| | | icon: 'none' |
| | | }); |
| | | return false; |
| | | } |
| | | |
| | | // æ ¡éªæä»¶ç±»å |
| | | if (uploadConfig.fileType && Array.isArray(uploadConfig.fileType) && uploadConfig.fileType.length > 0) { |
| | |
| | | return true; |
| | | } |
| | | |
| | | // æä»¶ä¸ä¼ å¤ç |
| | | const uploadFile = (file) => { |
| | | // æä»¶ä¸ä¼ å¤çï¼çæºèµ° uni.uploadFileï¼ |
| | | const uploadFile = async (file) => { |
| | | uploading.value = true; |
| | | uploadProgress.value = 0; |
| | | number.value++; // å¢å ä¸ä¼ è®¡æ° |
| | | |
| | | // ç¡®ä¿æä»¶è·¯å¾æ£ç¡® |
| | | const filePath = file.tempFilePath?.tempFilePath || file.path?.tempFilePath || ''; |
| | | if (!filePath) { |
| | | handleUploadError('æä»¶è·¯å¾ä¸åå¨'); |
| | | return; |
| | | } |
| | | |
| | | // ç¡®ä¿tokenåå¨ |
| | | const token = getToken(); |
| | |
| | | return; |
| | | } |
| | | |
| | | // åå¤ä¸ä¼ åæ° |
| | | const uploadParams = { |
| | | const typeValue = getTabType(); // ç产å:10, ç产ä¸:11, ç产å:12 |
| | | |
| | | uploadWithUniUploadFile(file, file.tempFilePath || file.path || '', typeValue, token); |
| | | } |
| | | |
| | | // 使ç¨uni.uploadFileä¸ä¼ ï¼éH5ç¯å¢æH5åéæ¹æ¡ï¼ |
| | | const uploadWithUniUploadFile = (file, filePath, typeValue, token) => { |
| | | if (!filePath) { |
| | | handleUploadError('æä»¶è·¯å¾ä¸åå¨'); |
| | | return; |
| | | } |
| | | |
| | | const uploadTask = uni.uploadFile({ |
| | | url: uploadFileUrl.value, |
| | | filePath: filePath, |
| | | name: 'files', |
| | | name: 'file', |
| | | formData: { |
| | | type: getTabType() || 0 |
| | | type: typeValue |
| | | }, |
| | | header: { |
| | | 'Authorization': `Bearer ${token}` |
| | | } |
| | | }; |
| | | const uploadTask = uni.uploadFile({ |
| | | ...uploadParams, |
| | | }, |
| | | success: (res) => { |
| | | try { |
| | | if (res.statusCode === 200) { |
| | |
| | | } |
| | | |
| | | // ä¸ä¼ 失败å¤ç |
| | | const handleUploadError = (message = 'ä¸ä¼ æä»¶å¤±è´¥', showRetry = true) => { |
| | | const handleUploadError = (message = 'ä¸ä¼ æä»¶å¤±è´¥', showRetry = false) => { |
| | | uploading.value = false; |
| | | uploadProgress.value = 0; |
| | | |
| | | if (showRetry) { |
| | | uni.showModal({ |
| | | title: 'ä¸ä¼ 失败', |
| | |
| | | |
| | | // ä¸ä¼ æååè° |
| | | const handleUploadSuccess = (res, file) => { |
| | | if (res.code === 200 && res.data && Array.isArray(res.data) && res.data.length > 0) { |
| | | const uploadedFile = res.data[0]; |
| | | console.log('ä¸ä¼ æåååº:', res); |
| | | |
| | | // å¤çä¸åçæ°æ®ç»æï¼å¯è½æ¯æ°ç»ï¼ä¹å¯è½æ¯å个对象 |
| | | let uploadedFile = null; |
| | | uploadedFile = res.data; |
| | | |
| | | // æ ¹æ®å½åä¸ä¼ ç±»å设置typeåæ®µ |
| | | let typeValue = 0; // é»è®¤ä¸ºç产å |
| | | switch (currentUploadType.value) { |
| | | case 'before': |
| | | typeValue = 0; |
| | | break; |
| | | case 'after': |
| | | typeValue = 1; |
| | | break; |
| | | case 'issue': |
| | | typeValue = 2; |
| | | break; |
| | | } |
| | | |
| | | // ç¡®ä¿ä¸ä¼ çæä»¶æ°æ®å®æ´ï¼å
å«idåtype |
| | | const fileData = { |
| | | ...file, |
| | | id: uploadedFile.id, // æ·»å æå¡å¨è¿åçid |
| | | url: uploadedFile.url || uploadedFile.downloadUrl, |
| | | bucketFilename: uploadedFile.bucketFilename || file.name, |
| | | downloadUrl: uploadedFile.downloadUrl || uploadedFile.url, |
| | | size: uploadedFile.size || file.size, |
| | | createTime: uploadedFile.createTime || new Date().getTime(), |
| | | type: typeValue // æ·»å ç±»ååæ®µï¼0=ç产å, 1=ç产ä¸, 2=ç产å |
| | | }; |
| | | |
| | | uploadList.value.push(fileData); |
| | | uploadedSuccessfully(); |
| | | } else { |
| | | if (!uploadedFile) { |
| | | console.error('æ æ³è§£æä¸ä¼ ååºæ°æ®:', res); |
| | | number.value--; // ä¸ä¼ 失败æ¶åå°è®¡æ° |
| | | handleUploadError(res.msg || 'ä¸ä¼ 失败'); |
| | | handleUploadError('ä¸ä¼ ååºæ°æ®æ ¼å¼é误', false); |
| | | return; |
| | | } |
| | | |
| | | // æ ¹æ®å½åä¸ä¼ ç±»å设置typeåæ®µ |
| | | let typeValue = 0; // é»è®¤ä¸ºç产å |
| | | switch (currentUploadType.value) { |
| | | case 'before': |
| | | typeValue = 0; |
| | | break; |
| | | case 'after': |
| | | typeValue = 1; |
| | | break; |
| | | case 'issue': |
| | | typeValue = 2; |
| | | break; |
| | | } |
| | | |
| | | // ç¡®ä¿ä¸ä¼ çæä»¶æ°æ®å®æ´ï¼å
å«idåtype |
| | | const fileData = { |
| | | ...file, |
| | | id: uploadedFile.id, // æ·»å æå¡å¨è¿åçid |
| | | tempId: uploadedFile.tempId ?? uploadedFile.tempFileId ?? uploadedFile.id, |
| | | url: uploadedFile.url || uploadedFile.downloadUrl || file.tempFilePath || file.path, |
| | | bucketFilename: uploadedFile.bucketFilename || uploadedFile.originalFilename || file.name, |
| | | downloadUrl: uploadedFile.downloadUrl || uploadedFile.url, |
| | | size: uploadedFile.size || uploadedFile.byteSize || file.size, |
| | | createTime: uploadedFile.createTime || new Date().getTime(), |
| | | type: typeValue // æ·»å ç±»ååæ®µï¼0=ç产å, 1=ç产ä¸, 2=ç产å |
| | | }; |
| | | |
| | | uploadList.value.push(fileData); |
| | | |
| | | // ç«å³æ·»å å°å¯¹åºçåç±»ï¼ä¸çå¾
æææä»¶ä¸ä¼ 宿 |
| | | switch (currentUploadType.value) { |
| | | case 'before': |
| | | beforeModelValue.value.push(fileData); |
| | | break; |
| | | case 'after': |
| | | afterModelValue.value.push(fileData); |
| | | break; |
| | | case 'issue': |
| | | issueModelValue.value.push(fileData); |
| | | break; |
| | | } |
| | | |
| | | // éç½®ä¸ä¼ å表ï¼å ä¸ºå·²ç»æ·»å å°å¯¹åºåç±»äºï¼ |
| | | uploadList.value = []; |
| | | number.value = 0; |
| | | } |
| | | |
| | | // ä¸ä¼ ç»æå¤ç |
| | | // ä¸ä¼ ç»æå¤çï¼å·²åºå¼ï¼ç°å¨å¨handleUploadSuccessä¸ç´æ¥å¤çï¼ |
| | | const uploadedSuccessfully = () => { |
| | | if (number.value > 0 && uploadList.value.length === number.value) { |
| | | // æ ¹æ®å½åä¸ä¼ ç±»åï¼å°æä»¶æ·»å å°å¯¹åºçåç±» |
| | | switch (currentUploadType.value) { |
| | | case 'before': |
| | | beforeModelValue.value = [...beforeModelValue.value, ...uploadList.value]; |
| | | break; |
| | | case 'after': |
| | | afterModelValue.value = [...afterModelValue.value, ...uploadList.value]; |
| | | break; |
| | | case 'issue': |
| | | issueModelValue.value = [...issueModelValue.value, ...uploadList.value]; |
| | | break; |
| | | } |
| | | |
| | | // éç½®ç¶æ |
| | | uploadList.value = []; |
| | | number.value = 0; |
| | | } |
| | | // æ¤å½æ°å·²ä¸å使ç¨ï¼æä»¶ä¸ä¼ æååç«å³æ·»å å°å¯¹åºåç±» |
| | | } |
| | | |
| | | // æ ¼å¼åæä»¶å¤§å° |
| | |
| | | <template> |
| | | <view class="receipt-payment-detail"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="ä¾åºå徿¥è¯¦æ
" @back="goBack" /> |
| | | |
| | | <!-- ç»è®¡ä¿¡æ¯ --> |
| | | <view class="summary-info" v-if="tableData.length > 0"> |
| | | <view class="summary-item"> |
| | | <text class="summary-label">æ»è®°å½æ°</text> |
| | | <text class="summary-value">{{ tableData.length }}</text> |
| | | </view> |
| | | <view class="summary-item"> |
| | | <text class="summary-label">å¼ç¥¨æ»éé¢</text> |
| | | <text class="summary-value">{{ formatAmount(invoiceTotal) }}</text> |
| | | </view> |
| | | <view class="summary-item"> |
| | | <text class="summary-label">忬¾æ»éé¢</text> |
| | | <text class="summary-value highlight">{{ formatAmount(receiptTotal) }}</text> |
| | | </view> |
| | | <view class="summary-item"> |
| | | <text class="summary-label">åºæ¶æ»éé¢</text> |
| | | <text class="summary-value danger">{{ formatAmount(unReceiptTotal) }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 忬¾è®°å½æç»å表 --> |
| | | <view class="detail-list" v-if="tableData.length > 0"> |
| | | <view v-for="(item, index) in tableData" :key="index" class="detail-item"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <view class="record-icon"> |
| | | <up-icon name="file-text" size="16" color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="item-index">{{ index + 1 }}</text> |
| | | </view> |
| | | <view class="item-date">{{ item.happenTime }}</view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å票éé¢(å
)</text> |
| | | <text class="detail-value">{{ formatAmount(item.invoiceAmount) }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">仿¬¾éé¢(å
)</text> |
| | | <text class="detail-value highlight">{{ formatAmount(item.currentPaymentAmount) }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åºä»éé¢(å
)</text> |
| | | <text class="detail-value danger">{{ formatAmount(item.payableAmount) }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else class="no-data"> |
| | | <text>ææ åæ¬¾è®°å½</text> |
| | | </view> |
| | | </view> |
| | | <view class="receipt-payment-detail"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="ä¾åºå徿¥è¯¦æ
" |
| | | @back="goBack" /> |
| | | <!-- ç»è®¡ä¿¡æ¯ --> |
| | | <view class="summary-info" |
| | | v-if="tableData.length > 0"> |
| | | <view class="summary-item"> |
| | | <text class="summary-label">æ»è®°å½æ°</text> |
| | | <text class="summary-value">{{ tableData.length }}</text> |
| | | </view> |
| | | <view class="summary-item"> |
| | | <text class="summary-label">å¼ç¥¨æ»éé¢</text> |
| | | <text class="summary-value">{{ formatAmount(invoiceTotal) }}</text> |
| | | </view> |
| | | <view class="summary-item"> |
| | | <text class="summary-label">忬¾æ»éé¢</text> |
| | | <text class="summary-value highlight">{{ formatAmount(receiptTotal) }}</text> |
| | | </view> |
| | | <view class="summary-item"> |
| | | <text class="summary-label">åºæ¶æ»éé¢</text> |
| | | <text class="summary-value danger">{{ formatAmount(unReceiptTotal) }}</text> |
| | | </view> |
| | | </view> |
| | | <!-- 忬¾è®°å½æç»å表 --> |
| | | <view class="detail-list" |
| | | v-if="tableData.length > 0"> |
| | | <view v-for="(item, index) in tableData" |
| | | :key="index" |
| | | class="detail-item"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <view class="record-icon"> |
| | | <up-icon name="file-text" |
| | | size="16" |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="item-index">{{ index + 1 }}</text> |
| | | </view> |
| | | <view class="item-date">{{ item.happenTime }}</view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å票éé¢(å
)</text> |
| | | <text class="detail-value">{{ formatAmount(item.invoiceAmount) }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">仿¬¾éé¢(å
)</text> |
| | | <text class="detail-value highlight">{{ formatAmount(item.currentPaymentAmount) }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åºä»éé¢(å
)</text> |
| | | <text class="detail-value danger">{{ formatAmount(item.payableAmount) }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else |
| | | class="no-data"> |
| | | <text>ææ åæ¬¾è®°å½</text> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted } from 'vue'; |
| | | import { onShow } from '@dcloudio/uni-app'; |
| | | import {paymentLedgerList, paymentRecordList} from "@/api/procurementManagement/paymentLedger"; |
| | | import { ref, computed, onMounted } from "vue"; |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import { |
| | | paymentLedgerList, |
| | | paymentRecordList, |
| | | } from "@/api/procurementManagement/paymentLedger"; |
| | | |
| | | // 客æ·ä¿¡æ¯ |
| | | const supplierId = ref(''); |
| | | // 客æ·ä¿¡æ¯ |
| | | const supplierId = ref(""); |
| | | |
| | | // è¡¨æ ¼æ°æ® |
| | | const tableData = ref([]); |
| | | // è¡¨æ ¼æ°æ® |
| | | const tableData = ref([]); |
| | | |
| | | const invoiceTotal = computed(() => { |
| | | return tableData.value.reduce((sum, item) => { |
| | | return sum + (parseFloat(item.invoiceAmount) || 0); |
| | | }, 0); |
| | | }); |
| | | const invoiceTotal = computed(() => { |
| | | return tableData.value.reduce((sum, item) => { |
| | | return sum + (parseFloat(item.invoiceAmount) || 0); |
| | | }, 0); |
| | | }); |
| | | |
| | | const receiptTotal = computed(() => { |
| | | return tableData.value.reduce((sum, item) => { |
| | | return sum + (parseFloat(item.receiptAmount) || 0); |
| | | }, 0); |
| | | }); |
| | | const receiptTotal = computed(() => { |
| | | return tableData.value.reduce((sum, item) => { |
| | | return sum + (parseFloat(item.receiptAmount) || 0); |
| | | }, 0); |
| | | }); |
| | | |
| | | const unReceiptTotal = computed(() => { |
| | | return tableData.value.reduce((sum, item) => { |
| | | return sum + (parseFloat(item.unReceiptAmount) || 0); |
| | | }, 0); |
| | | }); |
| | | const unReceiptTotal = computed(() => { |
| | | return tableData.value.reduce((sum, item) => { |
| | | return sum + (parseFloat(item.unReceiptAmount) || 0); |
| | | }, 0); |
| | | }); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.removeStorageSync('supplierId') |
| | | uni.navigateBack(); |
| | | }; |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.removeStorageSync("supplierId"); |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // è·å页é¢åæ° |
| | | const getPageParams = () => { |
| | | // 仿¬å°åå¨è·åä¾åºåID |
| | | const storedSupplierId = uni.getStorageSync('supplierId'); |
| | | if (storedSupplierId) { |
| | | supplierId.value = storedSupplierId; |
| | | } |
| | | }; |
| | | // è·å页é¢åæ° |
| | | const getPageParams = () => { |
| | | // 仿¬å°åå¨è·åä¾åºåID |
| | | const storedSupplierId = uni.getStorageSync("supplierId"); |
| | | if (storedSupplierId) { |
| | | supplierId.value = storedSupplierId; |
| | | } |
| | | }; |
| | | |
| | | // æ¥è¯¢å表 |
| | | const getList = () => { |
| | | if (!supplierId.value) { |
| | | uni.showToast({ |
| | | title: '客æ·ä¿¡æ¯ç¼ºå¤±', |
| | | icon: 'error' |
| | | }); |
| | | return; |
| | | } |
| | | showLoadingToast('å è½½ä¸...') |
| | | paymentRecordList(supplierId.value).then((res) => { |
| | | tableData.value = res.data; |
| | | closeToast() |
| | | }).catch(() => { |
| | | closeToast() |
| | | uni.showToast({ |
| | | title: 'æ¥è¯¢å¤±è´¥', |
| | | icon: 'error' |
| | | }); |
| | | }); |
| | | }; |
| | | // æ¥è¯¢å表 |
| | | const getList = () => { |
| | | if (!supplierId.value) { |
| | | uni.showToast({ |
| | | title: "客æ·ä¿¡æ¯ç¼ºå¤±", |
| | | icon: "error", |
| | | }); |
| | | return; |
| | | } |
| | | showLoadingToast("å è½½ä¸..."); |
| | | paymentRecordList(supplierId.value) |
| | | .then(res => { |
| | | tableData.value = res.data; |
| | | closeToast(); |
| | | }) |
| | | .catch(() => { |
| | | closeToast(); |
| | | uni.showToast({ |
| | | title: "æ¥è¯¢å¤±è´¥", |
| | | icon: "error", |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | // æ ¼å¼åéé¢ |
| | | const formatAmount = (amount) => { |
| | | return amount ? parseFloat(amount).toFixed(2) : '0.00'; |
| | | }; |
| | | // æ ¼å¼åéé¢ |
| | | const formatAmount = amount => { |
| | | return amount ? parseFloat(amount).toFixed(2) : "0.00"; |
| | | }; |
| | | |
| | | // æ¾ç¤ºå è½½æç¤º |
| | | const showLoadingToast = (message) => { |
| | | uni.showLoading({ |
| | | title: message, |
| | | mask: true |
| | | }); |
| | | }; |
| | | // æ¾ç¤ºå è½½æç¤º |
| | | const showLoadingToast = message => { |
| | | uni.showLoading({ |
| | | title: message, |
| | | mask: true, |
| | | }); |
| | | }; |
| | | |
| | | // å
³éæç¤º |
| | | const closeToast = () => { |
| | | uni.hideLoading(); |
| | | }; |
| | | // å
³éæç¤º |
| | | const closeToast = () => { |
| | | uni.hideLoading(); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | // 页é¢å è½½æ¶è·ååæ°å¹¶å·æ°å表 |
| | | getPageParams(); |
| | | getList(); |
| | | }); |
| | | onMounted(() => { |
| | | // 页é¢å è½½æ¶è·ååæ°å¹¶å·æ°å表 |
| | | getPageParams(); |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .receipt-payment-detail { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | position: relative; |
| | | } |
| | | .receipt-payment-detail { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | position: relative; |
| | | } |
| | | |
| | | .u-divider { |
| | | margin: 0 !important; |
| | | } |
| | | .u-divider { |
| | | margin: 0 !important; |
| | | } |
| | | |
| | | .summary-info { |
| | | background: #ffffff; |
| | | margin: 20px 20px 0 20px; |
| | | border-radius: 12px; |
| | | padding: 16px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | } |
| | | .summary-info { |
| | | background: #ffffff; |
| | | margin: 20px 20px 0 20px; |
| | | border-radius: 12px; |
| | | padding: 16px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .summary-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 8px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | .summary-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 8px; |
| | | |
| | | .summary-label { |
| | | font-size: 14px; |
| | | color: #666; |
| | | } |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | .summary-value { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | .summary-label { |
| | | font-size: 14px; |
| | | color: #666; |
| | | } |
| | | |
| | | .summary-value.highlight { |
| | | color: #2979ff; |
| | | font-weight: 600; |
| | | } |
| | | .summary-value { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .summary-value.danger { |
| | | color: #ff4757; |
| | | font-weight: 600; |
| | | } |
| | | .summary-value.highlight { |
| | | color: #2979ff; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .detail-list { |
| | | padding: 20px; |
| | | } |
| | | .summary-value.danger { |
| | | color: #ff4757; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .detail-item { |
| | | background: #ffffff; |
| | | border-radius: 12px; |
| | | margin-bottom: 16px; |
| | | overflow: hidden; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | padding: 0 16px; |
| | | } |
| | | .detail-list { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .item-header { |
| | | padding: 10px 0; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | .detail-item { |
| | | background: #ffffff; |
| | | border-radius: 12px; |
| | | margin-bottom: 16px; |
| | | overflow: hidden; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | padding: 0 16px; |
| | | } |
| | | |
| | | .item-left { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | .item-header { |
| | | padding: 10px 0; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .record-icon { |
| | | width: 24px; |
| | | height: 24px; |
| | | background: #2979ff; |
| | | border-radius: 4px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | .item-left { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .item-index { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | .record-icon { |
| | | width: 24px; |
| | | height: 24px; |
| | | background: #2979ff; |
| | | border-radius: 4px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .item-date { |
| | | font-size: 12px; |
| | | color: #666; |
| | | } |
| | | .item-index { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .item-details { |
| | | padding: 16px 0; |
| | | } |
| | | .item-date { |
| | | font-size: 12px; |
| | | color: #666; |
| | | } |
| | | |
| | | .detail-row { |
| | | display: flex; |
| | | align-items: flex-end; |
| | | justify-content: space-between; |
| | | margin-bottom: 8px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | .item-details { |
| | | padding: 16px 0; |
| | | } |
| | | |
| | | .detail-label { |
| | | font-size: 12px; |
| | | color: #777777; |
| | | min-width: 60px; |
| | | } |
| | | .detail-row { |
| | | display: flex; |
| | | align-items: flex-end; |
| | | justify-content: space-between; |
| | | margin-bottom: 8px; |
| | | |
| | | .detail-value { |
| | | font-size: 12px; |
| | | color: #000000; |
| | | text-align: right; |
| | | flex: 1; |
| | | margin-left: 16px; |
| | | } |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | .detail-value.highlight { |
| | | color: #2979ff; |
| | | font-weight: 500; |
| | | } |
| | | .detail-label { |
| | | font-size: 12px; |
| | | color: #777777; |
| | | min-width: 60px; |
| | | } |
| | | |
| | | .detail-value.danger { |
| | | color: #ff4757; |
| | | font-weight: 500; |
| | | } |
| | | .detail-value { |
| | | font-size: 12px; |
| | | color: #000000; |
| | | text-align: right; |
| | | flex: 1; |
| | | margin-left: 16px; |
| | | } |
| | | |
| | | .no-data { |
| | | padding: 40px 0; |
| | | text-align: center; |
| | | color: #999; |
| | | } |
| | | .detail-value.highlight { |
| | | color: #2979ff; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .detail-value.danger { |
| | | color: #ff4757; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .no-data { |
| | | padding: 40px 0; |
| | | text-align: center; |
| | | color: #999; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <view class="account-detail"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="å°è´¦è¯¦æ
" @back="goBack" /> |
| | | |
| | | <!-- 表ååºå --> |
| | | <up-form @submit="onSubmit" label-width="110" ref="formRef" :rules="rules" :model="form"> |
| | | <up-form-item label="éè´ååå·" prop="purchaseContractNumber"> |
| | | <up-input v-model="form.purchaseContractNumber" placeholder="èªå¨çæ" disabled /> |
| | | </up-form-item> |
| | | <up-form-item |
| | | label="éå®ååå·" |
| | | prop="salesContractNo" |
| | | required |
| | | @click="showPicker = true" |
| | | > |
| | | <up-input |
| | | v-model="form.salesContractNo" |
| | | readonly="" |
| | | @click="showPicker = true" |
| | | placeholder="ç¹å»éæ©éå®ååå·" |
| | | /> |
| | | <template #right> |
| | | <up-icon |
| | | name="arrow-right" |
| | | @click="showPicker = true" |
| | | ></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item |
| | | label="ä¾åºååç§°" |
| | | prop="supplierName" |
| | | required |
| | | @click="showCustomerPicker = true" |
| | | > |
| | | <up-input |
| | | v-model="form.supplierName" |
| | | readonly="" |
| | | @click="showCustomerPicker = true" |
| | | placeholder="ç¹å»éæ©ä¾åºå" |
| | | /> |
| | | <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="paymentMethod" > |
| | | <up-input v-model="form.paymentMethod" placeholder="请è¾å
¥ä»æ¬¾æ¹å¼" /> |
| | | </up-form-item> |
| | | <up-form-item label="å½å
¥äºº" prop="recorderName" > |
| | | <up-input v-model="form.recorderName" 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="salesContractActionList" |
| | | title="éæ©éå®ååå·" |
| | | @select="onSalesmanSelect" |
| | | @close="showPicker = false" |
| | | /> |
| | | |
| | | <!-- ä¾åºåéæ© --> |
| | | <up-action-sheet |
| | | :show="showCustomerPicker" |
| | | :actions="supplierActionList" |
| | | title="éæ©ä¾åºå" |
| | | @select="onCustomerSelect" |
| | | @close="showCustomerPicker = false" |
| | | /> |
| | | |
| | | <!-- 产åå¤§ç±»éæ©å¨ --> |
| | | <up-popup :show="showCategoryPicker" mode="bottom"> |
| | | <!-- 头鍿é®åºå --> |
| | | <view class="popup-header"> |
| | | <view @click="showCategoryPicker = false" class="cancelButton">åæ¶</view> |
| | | <view @click="confirmCategorySelection" class="confirmButton">ç¡®å®</view> |
| | | </view> |
| | | <u-tree |
| | | :data="productOptions" |
| | | :props="defaultProps" |
| | | show-checkbox |
| | | default-expand-all |
| | | check-strictly |
| | | @check-change="onCategoryConfirm" |
| | | /> |
| | | </up-popup> |
| | | |
| | | <!-- è§æ ¼åå·éæ©å¨ --> |
| | | <up-action-sheet |
| | | :show="showSpecificationPicker" |
| | | :actions="specificationActionList" |
| | | title="éæ©è§æ ¼åå·" |
| | | @select="onSpecificationSelect" |
| | | @close="showSpecificationPicker = false" |
| | | /> |
| | | |
| | | <!-- ç¨çéæ©å¨ --> |
| | | <up-action-sheet |
| | | :show="showTaxRatePicker" |
| | | :actions="taxRateActionList" |
| | | title="éæ©ç¨ç" |
| | | @select="onTaxRateSelect" |
| | | @close="showTaxRatePicker = false" |
| | | /> |
| | | |
| | | <!-- å票类åéæ©å¨ --> |
| | | <up-action-sheet |
| | | :show="showInvoiceTypePicker" |
| | | :actions="invoiceTypeActionList" |
| | | title="éæ©å票类å" |
| | | @select="onInvoiceTypeSelect" |
| | | @close="showInvoiceTypePicker = false" |
| | | /> |
| | | <!-- 产åä¿¡æ¯ --> |
| | | <view class="product-section"> |
| | | <view class="section-header"> |
| | | <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"> |
| | | <up-icon name="file-text" size="16" color="#2979ff"></up-icon> |
| | | <text class="product-productCategory">产å {{ idx + 1 }}</text> |
| | | </view> |
| | | <!-- æä½æé® --> |
| | | <view class="product-actions" v-if="operationType !== 'view'"> |
| | | <up-button type="error" size="mini" @click="removeProduct(idx)" class="del-btn"> |
| | | å é¤ |
| | | </up-button> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 产åä¿¡æ¯è¡¨å --> |
| | | <view class="product-form"> |
| | | <!-- 产å大类 --> |
| | | <up-form-item |
| | | label="产å大类" |
| | | prop="productCategory" |
| | | required |
| | | :rules="productRules" |
| | | > |
| | | <up-input |
| | | v-model="product.productCategory" |
| | | readonly |
| | | placeholder="è¯·éæ©" |
| | | @click="openCategoryPicker(idx)" |
| | | /> |
| | | <template #right> |
| | | <up-icon |
| | | name="arrow-right" |
| | | @click="showCategoryPicker = true" |
| | | ></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | |
| | | <!-- è§æ ¼åå· --> |
| | | <up-form-item |
| | | label="è§æ ¼åå·" |
| | | prop="specificationModel" |
| | | required |
| | | :rules="productRules" |
| | | > |
| | | <up-input |
| | | v-model="product.specificationModel" |
| | | readonly |
| | | placeholder="è¯·éæ©" |
| | | @click="openSpecificationPicker(idx)" |
| | | /> |
| | | <template #right> |
| | | <up-icon |
| | | name="arrow-right" |
| | | @click="showSpecificationPicker = true" |
| | | ></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | |
| | | <!-- åä½ --> |
| | | <up-form-item |
| | | label="åä½" |
| | | prop="unit" |
| | | required |
| | | :rules="productRules" |
| | | > |
| | | <up-input |
| | | v-model="product.unit" |
| | | placeholder="请è¾å
¥" |
| | | /> |
| | | </up-form-item> |
| | | |
| | | <!-- ç¨ç --> |
| | | <up-form-item |
| | | label="ç¨ç(%)" |
| | | prop="taxRate" |
| | | required |
| | | :rules="productRules" |
| | | > |
| | | <up-input |
| | | v-model="product.taxRate" |
| | | readonly |
| | | placeholder="è¯·éæ©" |
| | | @click="openTaxRatePicker(idx)" |
| | | /> |
| | | <template #right> |
| | | <up-icon |
| | | name="arrow-right" |
| | | @click="showTaxRatePicker = true" |
| | | ></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | |
| | | <!-- å«ç¨åä»· --> |
| | | <up-form-item |
| | | label="å«ç¨åä»·(å
)" |
| | | prop="taxInclusiveUnitPrice" |
| | | required |
| | | :rules="productRules" |
| | | > |
| | | <up-input |
| | | v-model="product.taxInclusiveUnitPrice" |
| | | type="number" |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatTaxPrice(idx)" |
| | | /> |
| | | </up-form-item> |
| | | |
| | | <!-- æ°é --> |
| | | <up-form-item |
| | | label="æ°é" |
| | | prop="quantity" |
| | | required |
| | | :rules="productRules" |
| | | > |
| | | <up-input |
| | | v-model="product.quantity" |
| | | type="number" |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatAmount(idx)" |
| | | /> |
| | | </up-form-item> |
| | | |
| | | <!-- å«ç¨æ»ä»· --> |
| | | <up-form-item |
| | | label="å«ç¨æ»ä»·(å
)" |
| | | prop="taxInclusiveTotalPrice" |
| | | required |
| | | :rules="productRules" |
| | | > |
| | | <up-input |
| | | v-model="product.taxInclusiveTotalPrice" |
| | | type="number" |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatTaxTotal(idx)" |
| | | /> |
| | | </up-form-item> |
| | | |
| | | <!-- ä¸å«ç¨æ»ä»· --> |
| | | <up-form-item |
| | | label="ä¸å«ç¨æ»ä»·(å
)" |
| | | prop="taxExclusiveTotalPrice" |
| | | required |
| | | :rules="productRules" |
| | | > |
| | | <up-input |
| | | v-model="product.taxExclusiveTotalPrice" |
| | | type="number" |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatNoTaxTotal(idx)" |
| | | /> |
| | | </up-form-item> |
| | | |
| | | <!-- å票类å --> |
| | | <up-form-item |
| | | label="å票类å" |
| | | prop="invoiceType" |
| | | required |
| | | :rules="productRules" |
| | | > |
| | | <up-input |
| | | v-model="product.invoiceType" |
| | | readonly |
| | | placeholder="è¯·éæ©" |
| | | @click="openInvoiceTypePicker(idx)" |
| | | /> |
| | | <template #right> |
| | | <up-icon |
| | | name="arrow-right" |
| | | @click="showInvoiceTypePicker = true" |
| | | ></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 使ç¨å
Œ
±åºé¨æé®ç»ä»¶ --> |
| | | <FooterButtons |
| | | :show="operationType !== 'view'" |
| | | cancelText="åæ¶" |
| | | confirmText="ä¿å" |
| | | @cancel="goBack" |
| | | @confirm="onSubmit" |
| | | /> |
| | | </up-form> |
| | | <PageHeader title="å°è´¦è¯¦æ
" |
| | | @back="goBack" /> |
| | | <!-- 表ååºå --> |
| | | <up-form @submit="onSubmit" |
| | | label-width="110" |
| | | ref="formRef" |
| | | :rules="rules" |
| | | :model="form"> |
| | | <up-form-item label="éè´ååå·" |
| | | prop="purchaseContractNumber"> |
| | | <up-input v-model="form.purchaseContractNumber" |
| | | placeholder="èªå¨çæ" |
| | | disabled /> |
| | | </up-form-item> |
| | | <up-form-item label="éå®ååå·" |
| | | prop="salesContractNo" |
| | | required |
| | | @click="showPicker = true"> |
| | | <up-input v-model="form.salesContractNo" |
| | | readonly="" |
| | | @click="showPicker = true" |
| | | placeholder="ç¹å»éæ©éå®ååå·" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showPicker = true"></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="ä¾åºååç§°" |
| | | prop="supplierName" |
| | | required |
| | | @click="showCustomerPicker = true"> |
| | | <up-input v-model="form.supplierName" |
| | | readonly="" |
| | | @click="showCustomerPicker = true" |
| | | placeholder="ç¹å»éæ©ä¾åºå" /> |
| | | <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="paymentMethod"> |
| | | <up-input v-model="form.paymentMethod" |
| | | placeholder="请è¾å
¥ä»æ¬¾æ¹å¼" /> |
| | | </up-form-item> |
| | | <up-form-item label="ç¾è®¢æ¥æ" |
| | | required |
| | | prop="executionDate"> |
| | | <up-input v-model="form.executionDate" |
| | | placeholder="è¯·éæ©" |
| | | readonly="" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showTimePicker = true"></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="å½å
¥äºº" |
| | | prop="recorderName"> |
| | | <up-input v-model="form.recorderName" |
| | | placeholder="请è¾å
¥" |
| | | disabled /> |
| | | </up-form-item> |
| | | <up-form-item label="å½å
¥æ¥æ" |
| | | prop="entryDate"> |
| | | <up-input v-model="form.entryDate" |
| | | placeholder="请è¾å
¥" |
| | | disabled /> |
| | | </up-form-item> |
| | | <view class="approval-process"> |
| | | <view class="approval-header"> |
| | | <text class="approval-title">å®¡æ ¸æµç¨</text> |
| | | <text class="approval-desc">æ¯ä¸ªæ¥éª¤åªè½éæ©ä¸ä¸ªå®¡æ¹äºº</text> |
| | | </view> |
| | | <view class="approval-steps"> |
| | | <view v-for="(step, stepIndex) in approverNodes" |
| | | :key="stepIndex" |
| | | class="approval-step"> |
| | | <view class="step-dot"></view> |
| | | <view class="step-title"> |
| | | <text>审æ¹äºº</text> |
| | | </view> |
| | | <view class="approver-container"> |
| | | <view v-if="step.nickName" |
| | | class="approver-item"> |
| | | <view class="approver-avatar"> |
| | | <text class="avatar-text">{{ step.nickName.charAt(0) }}</text> |
| | | <view class="status-dot"></view> |
| | | </view> |
| | | <view class="approver-info"> |
| | | <text class="approver-name">{{ step.nickName }}</text> |
| | | </view> |
| | | <view class="delete-approver-btn" |
| | | @click="removeApprover(stepIndex)">Ã</view> |
| | | </view> |
| | | <view v-else |
| | | class="add-approver-btn" |
| | | @click="addApprover(stepIndex)"> |
| | | <view class="add-circle">+</view> |
| | | <text class="add-label">鿩审æ¹äºº</text> |
| | | </view> |
| | | </view> |
| | | <view class="step-line" |
| | | v-if="stepIndex < approverNodes.length - 1"></view> |
| | | <view class="delete-step-btn" |
| | | v-if="approverNodes.length > 1" |
| | | @click="removeApprovalStep(stepIndex)">å é¤èç¹</view> |
| | | </view> |
| | | </view> |
| | | <view class="add-step-btn"> |
| | | <u-button icon="plus" |
| | | plain |
| | | type="primary" |
| | | style="width: 100%" |
| | | @click="addApprovalStep">æ°å¢èç¹</u-button> |
| | | </view> |
| | | </view> |
| | | <up-popup :show="showTimePicker" |
| | | mode="bottom" |
| | | @close="showTimePicker = false"> |
| | | <up-datetime-picker :show="true" |
| | | v-model="currentDate" |
| | | @confirm="onDateConfirm" |
| | | @cancel="showTimePicker = false" |
| | | mode="date" /> |
| | | </up-popup> |
| | | <!-- éå®ååå·éæ© --> |
| | | <up-action-sheet :show="showPicker" |
| | | :actions="salesContractActionList" |
| | | title="éæ©éå®ååå·" |
| | | @select="onSalesmanSelect" |
| | | @close="showPicker = false" /> |
| | | <!-- ä¾åºåéæ© --> |
| | | <up-action-sheet :show="showCustomerPicker" |
| | | :actions="supplierActionList" |
| | | title="éæ©ä¾åºå" |
| | | @select="onCustomerSelect" |
| | | @close="showCustomerPicker = false" /> |
| | | <!-- 产åå¤§ç±»éæ©å¨ --> |
| | | <up-popup :show="showCategoryPicker" |
| | | mode="bottom"> |
| | | <!-- 头鍿é®åºå --> |
| | | <view class="popup-header"> |
| | | <view @click="showCategoryPicker = false" |
| | | class="cancelButton">åæ¶</view> |
| | | <view @click="confirmCategorySelection" |
| | | class="confirmButton">ç¡®å®</view> |
| | | </view> |
| | | <u-tree :data="productOptions" |
| | | :props="defaultProps" |
| | | show-checkbox |
| | | default-expand-all |
| | | check-strictly |
| | | @check-change="onCategoryConfirm" /> |
| | | </up-popup> |
| | | <!-- è§æ ¼åå·éæ©å¨ --> |
| | | <up-action-sheet :show="showSpecificationPicker" |
| | | :actions="specificationActionList" |
| | | title="éæ©è§æ ¼åå·" |
| | | @select="onSpecificationSelect" |
| | | @close="showSpecificationPicker = false" /> |
| | | <!-- ç¨çéæ©å¨ --> |
| | | <up-action-sheet :show="showTaxRatePicker" |
| | | :actions="taxRateActionList" |
| | | title="éæ©ç¨ç" |
| | | @select="onTaxRateSelect" |
| | | @close="showTaxRatePicker = false" /> |
| | | <!-- å票类åéæ©å¨ --> |
| | | <up-action-sheet :show="showInvoiceTypePicker" |
| | | :actions="invoiceTypeActionList" |
| | | title="éæ©å票类å" |
| | | @select="onInvoiceTypeSelect" |
| | | @close="showInvoiceTypePicker = false" /> |
| | | <!-- 产åä¿¡æ¯ --> |
| | | <view class="product-section"> |
| | | <view class="section-header"> |
| | | <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"> |
| | | <up-icon name="file-text" |
| | | size="16" |
| | | color="#2979ff"></up-icon> |
| | | <text class="product-productCategory">产å {{ idx + 1 }}</text> |
| | | </view> |
| | | <!-- æä½æé® --> |
| | | <view class="product-actions" |
| | | v-if="operationType !== 'view'"> |
| | | <up-button type="error" |
| | | size="mini" |
| | | @click="removeProduct(idx)" |
| | | class="del-btn"> |
| | | å é¤ |
| | | </up-button> |
| | | </view> |
| | | </view> |
| | | <!-- 产åä¿¡æ¯è¡¨å --> |
| | | <view class="product-form"> |
| | | <!-- 产å大类 --> |
| | | <up-form-item label="产å大类" |
| | | prop="productCategory" |
| | | required |
| | | :rules="productRules"> |
| | | <up-input v-model="product.productCategory" |
| | | readonly |
| | | placeholder="è¯·éæ©" |
| | | @click="openCategoryPicker(idx)" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showCategoryPicker = true"></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | <!-- è§æ ¼åå· --> |
| | | <up-form-item label="è§æ ¼åå·" |
| | | prop="specificationModel" |
| | | required |
| | | :rules="productRules"> |
| | | <up-input v-model="product.specificationModel" |
| | | readonly |
| | | placeholder="è¯·éæ©" |
| | | @click="openSpecificationPicker(idx)" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showSpecificationPicker = true"></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | <!-- åä½ --> |
| | | <up-form-item label="åä½" |
| | | prop="unit" |
| | | required |
| | | :rules="productRules"> |
| | | <up-input v-model="product.unit" |
| | | placeholder="请è¾å
¥" /> |
| | | </up-form-item> |
| | | <!-- ç¨ç --> |
| | | <up-form-item label="ç¨ç(%)" |
| | | prop="taxRate" |
| | | required |
| | | :rules="productRules"> |
| | | <up-input v-model="product.taxRate" |
| | | readonly |
| | | placeholder="è¯·éæ©" |
| | | @click="openTaxRatePicker(idx)" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showTaxRatePicker = true"></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | <!-- å«ç¨åä»· --> |
| | | <up-form-item label="å«ç¨åä»·(å
)" |
| | | prop="taxInclusiveUnitPrice" |
| | | required |
| | | :rules="productRules"> |
| | | <up-input v-model="product.taxInclusiveUnitPrice" |
| | | type="number" |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatTaxPrice(idx)" /> |
| | | </up-form-item> |
| | | <!-- æ°é --> |
| | | <up-form-item label="æ°é" |
| | | prop="quantity" |
| | | required |
| | | :rules="productRules"> |
| | | <up-input v-model="product.quantity" |
| | | type="number" |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatAmount(idx)" /> |
| | | </up-form-item> |
| | | <!-- å«ç¨æ»ä»· --> |
| | | <up-form-item label="å«ç¨æ»ä»·(å
)" |
| | | prop="taxInclusiveTotalPrice" |
| | | required |
| | | :rules="productRules"> |
| | | <up-input v-model="product.taxInclusiveTotalPrice" |
| | | type="number" |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatTaxTotal(idx)" /> |
| | | </up-form-item> |
| | | <!-- ä¸å«ç¨æ»ä»· --> |
| | | <up-form-item label="ä¸å«ç¨æ»ä»·(å
)" |
| | | prop="taxExclusiveTotalPrice" |
| | | required |
| | | :rules="productRules"> |
| | | <up-input v-model="product.taxExclusiveTotalPrice" |
| | | type="number" |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatNoTaxTotal(idx)" /> |
| | | </up-form-item> |
| | | <!-- å票类å --> |
| | | <up-form-item label="å票类å" |
| | | prop="invoiceType" |
| | | required |
| | | :rules="productRules"> |
| | | <up-input v-model="product.invoiceType" |
| | | readonly |
| | | placeholder="è¯·éæ©" |
| | | @click="openInvoiceTypePicker(idx)" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showInvoiceTypePicker = true"></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | <!-- åºåé¢è¦æ°é --> |
| | | <up-form-item label="åºåé¢è¦æ°é" |
| | | prop="warnNum" |
| | | required |
| | | :rules="productRules"> |
| | | <up-input v-model="product.warnNum" |
| | | type="number" |
| | | placeholder="请è¾å
¥" /> |
| | | </up-form-item> |
| | | <up-form-item label="æ¯å¦è´¨æ£" |
| | | prop="invoiceType" |
| | | required |
| | | :rules="productRules"> |
| | | <u-radio-group v-model="product.isChecked" |
| | | placement="row" |
| | | @change="groupChange"> |
| | | <u-radio :customStyle="{marginRight: '40rpx'}" |
| | | label="æ¯" |
| | | :name="true"> |
| | | </u-radio> |
| | | <u-radio label="å¦" |
| | | :name="false"> |
| | | </u-radio> |
| | | </u-radio-group> |
| | | </up-form-item> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- 使ç¨å
Œ
±åºé¨æé®ç»ä»¶ --> |
| | | <FooterButtons :show="operationType !== 'view'" |
| | | cancelText="åæ¶" |
| | | confirmText="ä¿å" |
| | | @cancel="goBack" |
| | | @confirm="onSubmit" /> |
| | | </up-form> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {onMounted, ref, computed} from 'vue'; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import {calculateTaxExclusiveTotalPrice} from "@/utils/summarizeTable"; |
| | | import {formatDateToYMD} from '@/utils/ruoyi' |
| | | import { |
| | | addOrEditPurchase, createPurchaseNo, |
| | | getOptions, |
| | | getPurchaseById, |
| | | getSalesNo |
| | | } from "@/api/procurementManagement/procurementLedger"; |
| | | import PageHeader from '@/components/PageHeader.vue'; |
| | | import FooterButtons from '@/components/FooterButtons.vue'; |
| | | import { onMounted, ref, computed } from "vue"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { calculateTaxExclusiveTotalPrice } from "@/utils/summarizeTable"; |
| | | import { formatDateToYMD } from "@/utils/ruoyi"; |
| | | import { |
| | | addOrEditPurchase, |
| | | createPurchaseNo, |
| | | getOptions, |
| | | getPurchaseById, |
| | | getSalesNo, |
| | | approveProcessGetInfo, |
| | | } from "@/api/procurementManagement/procurementLedger"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import FooterButtons from "@/components/FooterButtons.vue"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user"; |
| | | // è·å页é¢åæ° |
| | | const operationType = ref(""); |
| | | const editData = ref(null); |
| | | const formRef = ref(null); |
| | | |
| | | // è·å页é¢åæ° |
| | | const operationType = ref(''); |
| | | const editData = ref(null); |
| | | const formRef = ref(null); |
| | | |
| | | const userStore = useUserStore() |
| | | const form = ref({ |
| | | id: '', |
| | | salesContractNo: '', |
| | | purchaseContractNumber: '', |
| | | supplierId: '', |
| | | supplierName: '', |
| | | projectName: '', |
| | | paymentMethod: '', |
| | | recorderId: '', |
| | | recorderName: '', |
| | | entryDate: '', |
| | | }); |
| | | const showPicker = ref(false); |
| | | const showCustomerPicker = ref(false); |
| | | const salesContractList = ref([]); |
| | | const supplierList = ref([]); |
| | | const productData = ref([]); |
| | | |
| | | // 计ç®éå®ååå·éæ©å表 |
| | | const salesContractActionList = computed(() => { |
| | | return salesContractList.value.map(item => ({ |
| | | name: item.text, |
| | | value: item.value |
| | | })) |
| | | }) |
| | | |
| | | // 计ç®ä¾åºåéæ©å表 |
| | | const supplierActionList = computed(() => { |
| | | return supplierList.value.map(item => ({ |
| | | name: item.text, |
| | | value: item.value |
| | | })) |
| | | }) |
| | | |
| | | // éæ©å¨ç¸å
³åé |
| | | const showCategoryPicker = ref(false); |
| | | const showSpecificationPicker = ref(false); |
| | | const showTaxRatePicker = ref(false); |
| | | const showInvoiceTypePicker = ref(false); |
| | | const currentProductIndex = ref(0); |
| | | |
| | | // éé¡¹æ°æ® |
| | | const productOptions = ref([]); |
| | | const selectedCategoryNode = ref(null); |
| | | const defaultProps = ref({ |
| | | children: 'children', |
| | | label: 'label', |
| | | nodeKey: 'id' |
| | | }); |
| | | |
| | | const modelOptions = ref([]); |
| | | const taxRateOptions = ref([ |
| | | { text: '1', value: '1' }, |
| | | { text: '6', value: '6' }, |
| | | { text: '13', value: '13' }, |
| | | ]); |
| | | |
| | | const invoiceTypeOptions = ref([ |
| | | { text: '墿®ç¥¨', value: '墿®ç¥¨' }, |
| | | { text: 'å¢ä¸ç¥¨', 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 rules = { |
| | | salesContractNo: [ |
| | | { required: true, message: 'è¯·éæ©éå®ååå·', trigger: 'blur' } |
| | | ], |
| | | supplierName: [ |
| | | { required: true, message: 'è¯·éæ©ä¾åºååç§°', trigger: 'blur' } |
| | | ], |
| | | projectName: [ |
| | | { 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 = [] |
| | | } |
| | | productData.value.push({ |
| | | productCategory: '', |
| | | specificationModel: '', |
| | | productModelId: '', |
| | | unit: '', |
| | | taxRate: '', |
| | | taxInclusiveUnitPrice: '', |
| | | quantity: '', |
| | | taxInclusiveTotalPrice: '', |
| | | taxExclusiveTotalPrice: '', |
| | | invoiceType: '' |
| | | const userStore = useUserStore(); |
| | | const form = ref({ |
| | | id: "", |
| | | salesContractNo: "", |
| | | purchaseContractNumber: "", |
| | | supplierId: "", |
| | | supplierName: "", |
| | | projectName: "", |
| | | paymentMethod: "", |
| | | recorderId: "", |
| | | recorderName: "", |
| | | entryDate: "", |
| | | approveUserIds: "", |
| | | executionDate: "", |
| | | }); |
| | | }; |
| | | const showTimePicker = ref(false); |
| | | const showPicker = ref(false); |
| | | const showCustomerPicker = ref(false); |
| | | const salesContractList = ref([]); |
| | | const supplierList = ref([]); |
| | | const productData = ref([]); |
| | | const currentDate = ref(Date.now()); |
| | | // 计ç®éå®ååå·éæ©å表 |
| | | const salesContractActionList = computed(() => { |
| | | return salesContractList.value.map(item => ({ |
| | | name: item.text, |
| | | value: item.value, |
| | | })); |
| | | }); |
| | | |
| | | // éå®ååå·éæ©äºä»¶ |
| | | const onSalesmanSelect = (item) => { |
| | | form.value.salesContractNo = item.name |
| | | // æ¥æ¾å¯¹åºçid |
| | | const selectedItem = salesContractList.value.find(contract => contract.text === item.name); |
| | | if (selectedItem) { |
| | | form.value.salesLedgerId = selectedItem.value; |
| | | } |
| | | showPicker.value = false; |
| | | } |
| | | // 计ç®ä¾åºåéæ©å表 |
| | | const supplierActionList = computed(() => { |
| | | return supplierList.value.map(item => ({ |
| | | name: item.text, |
| | | value: item.value, |
| | | })); |
| | | }); |
| | | |
| | | // ä¾åºåéæ©äºä»¶ |
| | | const onCustomerSelect = (item) => { |
| | | form.value.supplierName = item.name |
| | | // æ¥æ¾å¯¹åºçid |
| | | const selectedItem = supplierList.value.find(supplier => supplier.text === item.name); |
| | | if (selectedItem) { |
| | | form.value.supplierId = selectedItem.value; |
| | | } |
| | | showCustomerPicker.value = false; |
| | | } |
| | | // éæ©å¨ç¸å
³åé |
| | | const showCategoryPicker = ref(false); |
| | | const showSpecificationPicker = ref(false); |
| | | const showTaxRatePicker = ref(false); |
| | | const showInvoiceTypePicker = ref(false); |
| | | const currentProductIndex = ref(0); |
| | | |
| | | const removeProduct = (idx) => { |
| | | productData.value.splice(idx, 1); |
| | | }; |
| | | // éé¡¹æ°æ® |
| | | const productOptions = ref([]); |
| | | const selectedCategoryNode = ref(null); |
| | | const defaultProps = ref({ |
| | | children: "children", |
| | | label: "label", |
| | | nodeKey: "id", |
| | | }); |
| | | |
| | | // æ¾ç¤ºéæ©å¨ |
| | | const openCategoryPicker = (idx) => { |
| | | currentProductIndex.value = idx; |
| | | showCategoryPicker.value = true; |
| | | }; |
| | | const modelOptions = ref([]); |
| | | const taxRateOptions = ref([ |
| | | { text: "1", value: "1" }, |
| | | { text: "6", value: "6" }, |
| | | { text: "13", value: "13" }, |
| | | ]); |
| | | |
| | | const openSpecificationPicker = (idx) => { |
| | | currentProductIndex.value = idx; |
| | | showSpecificationPicker.value = true; |
| | | }; |
| | | const invoiceTypeOptions = ref([ |
| | | { text: "墿®ç¥¨", value: "墿®ç¥¨" }, |
| | | { text: "å¢ä¸ç¥¨", value: "å¢ä¸ç¥¨" }, |
| | | ]); |
| | | |
| | | const openTaxRatePicker = (idx) => { |
| | | currentProductIndex.value = idx; |
| | | showTaxRatePicker.value = true; |
| | | }; |
| | | // 计ç®è§æ ¼åå·éæ©å表 |
| | | const specificationActionList = computed(() => { |
| | | return modelOptions.value.map(model => ({ |
| | | name: model.text, |
| | | value: model.value, |
| | | unit: model.unit, |
| | | })); |
| | | }); |
| | | |
| | | const openInvoiceTypePicker = (idx) => { |
| | | currentProductIndex.value = idx; |
| | | showInvoiceTypePicker.value = true; |
| | | }; |
| | | // 计ç®ç¨çéæ©å表 |
| | | const taxRateActionList = computed(() => { |
| | | return taxRateOptions.value.map(rate => ({ |
| | | name: rate.text, |
| | | value: rate.value, |
| | | })); |
| | | }); |
| | | |
| | | // éæ©å¨ç¡®è®¤äºä»¶ |
| | | const onCategoryConfirm = (node) => { |
| | | // è·åéä¸çèç¹ä¿¡æ¯ |
| | | console.log('selected node---', node); |
| | | // åå¨éä¸çèç¹ï¼ç¨äºç¡®è®¤æ¶è·åæ°æ® |
| | | selectedCategoryNode.value = node; |
| | | }; |
| | | // 计ç®å票类åéæ©å表 |
| | | const invoiceTypeActionList = computed(() => { |
| | | return invoiceTypeOptions.value.map(type => ({ |
| | | name: type.text, |
| | | value: type.value, |
| | | })); |
| | | }); |
| | | |
| | | // 确认产åå¤§ç±»éæ© |
| | | const confirmCategorySelection = () => { |
| | | if (selectedCategoryNode.value) { |
| | | // 设置éä¸ç产å大类 |
| | | productData.value[currentProductIndex.value].productCategory = selectedCategoryNode.value.label; |
| | | const id = selectedCategoryNode.value.id |
| | | // éç½®éä¸çèç¹ |
| | | selectedCategoryNode.value = null; |
| | | productData.value[currentProductIndex.value].specificationModel = '' |
| | | productData.value[currentProductIndex.value].productModelId = '' |
| | | getModels(id) |
| | | } |
| | | showCategoryPicker.value = false; |
| | | }; |
| | | // è¡¨åæ ¡éªè§å |
| | | const rules = { |
| | | salesContractNo: [ |
| | | { required: true, message: "è¯·éæ©éå®ååå·", trigger: "blur" }, |
| | | ], |
| | | supplierName: [ |
| | | { required: true, message: "è¯·éæ©ä¾åºååç§°", trigger: "blur" }, |
| | | ], |
| | | projectName: [{ required: true, message: "请è¾å
¥é¡¹ç®åç§°", trigger: "blur" }], |
| | | executionDate: [ |
| | | { required: true, message: "è¯·éæ©ç¾è®¢æ¥æ", trigger: "blur" }, |
| | | ], |
| | | }; |
| | | |
| | | // è·åè§æ ¼åå· |
| | | const getModels = (value) => { |
| | | modelList({ id: value }).then((res) => { |
| | | modelOptions.value = res.map(user => ({ |
| | | text: user.model, |
| | | value: user.id, |
| | | unit: user.unit, |
| | | })); |
| | | }); |
| | | }; |
| | | // 产åä¿¡æ¯æ ¡éªè§å |
| | | 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 onSpecificationSelect = (item) => { |
| | | productData.value[currentProductIndex.value].specificationModel = item.name |
| | | productData.value[currentProductIndex.value].productModelId = item.value |
| | | productData.value[currentProductIndex.value].unit = item.unit |
| | | showSpecificationPicker.value = false; |
| | | }; |
| | | warnNum: [ |
| | | { required: true, message: "请è¾å
¥åºåé¢è¦æ°é", trigger: "blur" }, |
| | | { |
| | | type: "number", |
| | | min: 0, |
| | | message: "åºåé¢è¦æ°éå¿
须大äº0", |
| | | trigger: "blur", |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | // ç¨çéæ©äºä»¶ |
| | | const onTaxRateSelect = (item) => { |
| | | productData.value[currentProductIndex.value].taxRate = item.value |
| | | showTaxRatePicker.value = false; |
| | | // éæ°è®¡ç®ä¸å«ç¨æ»ä»· |
| | | const inclusiveTotalPrice = parseFloat(productData.value[currentProductIndex.value].taxInclusiveTotalPrice) |
| | | const taxRate = parseFloat(item.value) |
| | | if (inclusiveTotalPrice && taxRate) { |
| | | productData.value[currentProductIndex.value].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice(inclusiveTotalPrice, taxRate) |
| | | } |
| | | }; |
| | | |
| | | // å票类åéæ©äºä»¶ |
| | | const onInvoiceTypeSelect = (item) => { |
| | | productData.value[currentProductIndex.value].invoiceType = item.name |
| | | showInvoiceTypePicker.value = false; |
| | | }; |
| | | |
| | | // æ ¼å¼å彿° - åºå®ä¸¤ä½å°æ° |
| | | const formatTaxPrice = (idx) => { |
| | | if (productData.value[idx].taxInclusiveUnitPrice) { |
| | | const value = parseFloat(productData.value[idx].taxInclusiveUnitPrice); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].taxInclusiveUnitPrice = value.toFixed(2); |
| | | const addProduct = () => { |
| | | if (productData.value === null) { |
| | | productData.value = []; |
| | | } |
| | | } |
| | | if (!productData.value[currentProductIndex.value].taxRate) { |
| | | uni.showToast({ |
| | | title: '请å
éæ©ç¨ç', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | const quantity = parseFloat(productData.value[currentProductIndex.value].quantity); |
| | | const unitPrice = parseFloat(productData.value[currentProductIndex.value].taxInclusiveUnitPrice); |
| | | |
| | | if (!quantity || quantity <= 0 || !unitPrice) { |
| | | return; |
| | | } |
| | | // 计ç®å«ç¨æ»ä»· |
| | | productData.value[currentProductIndex.value].taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2); |
| | | |
| | | // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· |
| | | if (productData.value[currentProductIndex.value].taxRate) { |
| | | productData.value[currentProductIndex.value].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice( |
| | | productData.value[currentProductIndex.value].taxInclusiveTotalPrice, |
| | | productData.value[currentProductIndex.value].taxRate |
| | | ); |
| | | } |
| | | }; |
| | | productData.value.push({ |
| | | productCategory: "", |
| | | specificationModel: "", |
| | | productModelId: "", |
| | | unit: "", |
| | | taxRate: "", |
| | | taxInclusiveUnitPrice: "", |
| | | quantity: "", |
| | | taxInclusiveTotalPrice: "", |
| | | taxExclusiveTotalPrice: "", |
| | | invoiceType: "", |
| | | isChecked: false, |
| | | warnNum: "", |
| | | }); |
| | | }; |
| | | |
| | | // æ°éè¾å
¥æ¡å¤±ç¦ |
| | | const formatAmount = (idx) => { |
| | | if (productData.value[idx].quantity) { |
| | | const value = parseFloat(productData.value[idx].quantity); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].quantity = value.toFixed(2); |
| | | // éå®ååå·éæ©äºä»¶ |
| | | const onSalesmanSelect = item => { |
| | | form.value.salesContractNo = item.name; |
| | | // æ¥æ¾å¯¹åºçid |
| | | const selectedItem = salesContractList.value.find( |
| | | contract => contract.text === item.name |
| | | ); |
| | | if (selectedItem) { |
| | | form.value.salesLedgerId = selectedItem.value; |
| | | } |
| | | } |
| | | if (!productData.value[currentProductIndex.value].taxRate) { |
| | | uni.showToast({ |
| | | title: '请å
éæ©ç¨ç', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | const quantity = parseFloat(productData.value[currentProductIndex.value].quantity); |
| | | const unitPrice = parseFloat(productData.value[currentProductIndex.value].taxInclusiveUnitPrice); |
| | | |
| | | if (!quantity || quantity <= 0 || !unitPrice) { |
| | | return; |
| | | } |
| | | // 计ç®å«ç¨æ»ä»· |
| | | productData.value[currentProductIndex.value].taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2); |
| | | // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· |
| | | if (productData.value[currentProductIndex.value].taxRate) { |
| | | productData.value[currentProductIndex.value].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice( |
| | | productData.value[currentProductIndex.value].taxInclusiveTotalPrice, |
| | | productData.value[currentProductIndex.value].taxRate |
| | | ); |
| | | } |
| | | }; |
| | | showPicker.value = false; |
| | | }; |
| | | |
| | | // å«ç¨æ»ä»·å¤±ç¦ï¼æ ¹æ®å«ç¨æ»ä»·è®¡ç®å«ç¨åä»·åæ°é |
| | | const formatTaxTotal = (idx) => { |
| | | if (productData.value[idx].taxInclusiveTotalPrice) { |
| | | const value = parseFloat(productData.value[idx].taxInclusiveTotalPrice); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].taxInclusiveTotalPrice = value.toFixed(2); |
| | | // ä¾åºåéæ©äºä»¶ |
| | | const onCustomerSelect = item => { |
| | | form.value.supplierName = item.name; |
| | | // æ¥æ¾å¯¹åºçid |
| | | const selectedItem = supplierList.value.find( |
| | | supplier => supplier.text === item.name |
| | | ); |
| | | if (selectedItem) { |
| | | form.value.supplierId = selectedItem.value; |
| | | } |
| | | } |
| | | const totalPrice = parseFloat(productData.value[currentProductIndex.value].taxInclusiveTotalPrice); |
| | | const quantity = parseFloat(productData.value[currentProductIndex.value].quantity); |
| | | |
| | | if (!totalPrice || !quantity || quantity <= 0) { |
| | | return; |
| | | } |
| | | // 计ç®å«ç¨åä»· = å«ç¨æ»ä»· / æ°é |
| | | productData.value[currentProductIndex.value].taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2); |
| | | // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· |
| | | if (productData.value[currentProductIndex.value].taxRate) { |
| | | productData.value[currentProductIndex.value].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice( |
| | | totalPrice, |
| | | productData.value[currentProductIndex.value].taxRate |
| | | ); |
| | | } |
| | | }; |
| | | showCustomerPicker.value = false; |
| | | }; |
| | | |
| | | // ä¸å«ç¨æ»ä»·å¤±ç¦, æ ¹æ®ä¸å«ç¨æ»ä»·è®¡ç®å«ç¨åä»·åæ°é |
| | | const formatNoTaxTotal = (idx) => { |
| | | if (productData.value[idx].taxExclusiveTotalPrice) { |
| | | const value = parseFloat(productData.value[idx].taxExclusiveTotalPrice); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].taxExclusiveTotalPrice = value.toFixed(2); |
| | | const removeProduct = idx => { |
| | | productData.value.splice(idx, 1); |
| | | }; |
| | | |
| | | // æ¾ç¤ºéæ©å¨ |
| | | const openCategoryPicker = idx => { |
| | | currentProductIndex.value = idx; |
| | | showCategoryPicker.value = true; |
| | | }; |
| | | |
| | | const openSpecificationPicker = idx => { |
| | | currentProductIndex.value = idx; |
| | | showSpecificationPicker.value = true; |
| | | }; |
| | | |
| | | const openTaxRatePicker = idx => { |
| | | currentProductIndex.value = idx; |
| | | showTaxRatePicker.value = true; |
| | | }; |
| | | |
| | | const openInvoiceTypePicker = idx => { |
| | | currentProductIndex.value = idx; |
| | | showInvoiceTypePicker.value = true; |
| | | }; |
| | | |
| | | // éæ©å¨ç¡®è®¤äºä»¶ |
| | | const onCategoryConfirm = node => { |
| | | // è·åéä¸çèç¹ä¿¡æ¯ |
| | | console.log("selected node---", node); |
| | | // åå¨éä¸çèç¹ï¼ç¨äºç¡®è®¤æ¶è·åæ°æ® |
| | | selectedCategoryNode.value = node; |
| | | }; |
| | | |
| | | // 确认产åå¤§ç±»éæ© |
| | | const confirmCategorySelection = () => { |
| | | if (selectedCategoryNode.value) { |
| | | // 设置éä¸ç产å大类 |
| | | productData.value[currentProductIndex.value].productCategory = |
| | | selectedCategoryNode.value.label; |
| | | const id = selectedCategoryNode.value.id; |
| | | // éç½®éä¸çèç¹ |
| | | selectedCategoryNode.value = null; |
| | | productData.value[currentProductIndex.value].specificationModel = ""; |
| | | productData.value[currentProductIndex.value].productModelId = ""; |
| | | getModels(id); |
| | | } |
| | | } |
| | | if (!productData.value[currentProductIndex.value].taxRate) { |
| | | uni.showToast({ |
| | | title: '请å
éæ©ç¨ç', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | const exclusiveTotalPrice = parseFloat(productData.value[currentProductIndex.value].taxExclusiveTotalPrice); |
| | | const quantity = parseFloat(productData.value[currentProductIndex.value].quantity); |
| | | const taxRate = parseFloat(productData.value[currentProductIndex.value].taxRate); |
| | | if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) { |
| | | return; |
| | | } |
| | | // å
计ç®å«ç¨æ»ä»· = ä¸å«ç¨æ»ä»· / (1 - ç¨ç/100) |
| | | const taxRateDecimal = taxRate / 100; |
| | | const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal); |
| | | productData.value[currentProductIndex.value].taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2); |
| | | // 计ç®å«ç¨åä»· = å«ç¨æ»ä»· / æ°é |
| | | productData.value[currentProductIndex.value].taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(2); |
| | | }; |
| | | showCategoryPicker.value = false; |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | // æ¸
çæ¬å°åå¨çæ°æ® |
| | | uni.removeStorageSync('operationType'); |
| | | uni.removeStorageSync('editData'); |
| | | uni.navigateBack(); |
| | | }; |
| | | // è·åè§æ ¼åå· |
| | | const getModels = value => { |
| | | modelList({ id: value }).then(res => { |
| | | modelOptions.value = res.map(user => ({ |
| | | text: user.model, |
| | | value: user.id, |
| | | unit: user.unit, |
| | | })); |
| | | }); |
| | | }; |
| | | |
| | | const onSubmit = () => { |
| | | if (productData.value !== null && productData.value.length > 0) { |
| | | form.value.productData = JSON.parse(JSON.stringify(productData.value)); |
| | | } else { |
| | | uni.showToast({ |
| | | title: '请添å 产åä¿¡æ¯', |
| | | icon: 'none' |
| | | }); |
| | | return |
| | | } |
| | | form.value.type = 2; |
| | | addOrEditPurchase(form.value).then((res) => { |
| | | uni.showToast({ |
| | | title: 'æäº¤æå', |
| | | icon: 'success', |
| | | }); |
| | | goBack(); |
| | | }); |
| | | }; |
| | | // è§æ ¼åå·éæ©äºä»¶ |
| | | const onSpecificationSelect = item => { |
| | | productData.value[currentProductIndex.value].specificationModel = item.name; |
| | | productData.value[currentProductIndex.value].productModelId = item.value; |
| | | productData.value[currentProductIndex.value].unit = item.unit; |
| | | showSpecificationPicker.value = false; |
| | | }; |
| | | |
| | | const setUserInfo = () => { |
| | | form.value.recorderId = userStore.id; |
| | | form.value.recorderName = userStore.nickName; |
| | | // 设置å½å¤©æ¥æ |
| | | const today = new Date() |
| | | const year = today.getFullYear() |
| | | const month = String(today.getMonth() + 1).padStart(2, '0') |
| | | const day = String(today.getDate()).padStart(2, '0') |
| | | form.value.entryDate = `${year}-${month}-${day}` |
| | | }; |
| | | // ç¨çéæ©äºä»¶ |
| | | const onTaxRateSelect = item => { |
| | | productData.value[currentProductIndex.value].taxRate = item.value; |
| | | showTaxRatePicker.value = false; |
| | | // éæ°è®¡ç®ä¸å«ç¨æ»ä»· |
| | | const inclusiveTotalPrice = parseFloat( |
| | | productData.value[currentProductIndex.value].taxInclusiveTotalPrice |
| | | ); |
| | | const taxRate = parseFloat(item.value); |
| | | if (inclusiveTotalPrice && taxRate) { |
| | | productData.value[currentProductIndex.value].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice(inclusiveTotalPrice, taxRate); |
| | | } |
| | | }; |
| | | |
| | | // å¡«å
è¡¨åæ°æ®ï¼ç¼è¾æ¨¡å¼ï¼ |
| | | const fillFormData = () => { |
| | | if (!editData.value) return; |
| | | getPurchaseById({ id: editData.value.id, type: 2 }).then((res) => { |
| | | productData.value = res.productData; |
| | | }); |
| | | console.log(editData.value) |
| | | // å¡«å
åºæ¬ä¿¡æ¯ |
| | | form.value.salesContractNo = editData.value.salesContractNo || ''; |
| | | form.value.supplierName = editData.value.supplierName || ''; |
| | | form.value.projectName = editData.value.projectName || ''; |
| | | form.value.paymentMethod = editData.value.paymentMethod || ''; |
| | | form.value.salesLedgerId = editData.value.salesLedgerId || ''; |
| | | form.value.recorderId = editData.value.recorderId || ''; |
| | | form.value.recorderName = editData.value.recorderName || ''; |
| | | form.value.entryDate = editData.value.entryDate || ''; |
| | | form.value.id = editData.value.id || ''; |
| | | form.value.supplierId = editData.value.supplierId || ''; |
| | | }; |
| | | // å票类åéæ©äºä»¶ |
| | | const onInvoiceTypeSelect = item => { |
| | | productData.value[currentProductIndex.value].invoiceType = item.name; |
| | | showInvoiceTypePicker.value = false; |
| | | }; |
| | | |
| | | const getSalesNoList = () => { |
| | | getSalesNo().then((res) => { |
| | | // å°ç¨æ·æ°æ®ç»è£
æ picker éè¦çæ ¼å¼ |
| | | salesContractList.value = res.map(user => ({ |
| | | text: user.salesContractNo, |
| | | value: user.id |
| | | })); |
| | | }) |
| | | }; |
| | | // æ ¼å¼å彿° - åºå®ä¸¤ä½å°æ° |
| | | const formatTaxPrice = idx => { |
| | | if (productData.value[idx].taxInclusiveUnitPrice) { |
| | | const value = parseFloat(productData.value[idx].taxInclusiveUnitPrice); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].taxInclusiveUnitPrice = value.toFixed(2); |
| | | } |
| | | } |
| | | if (!productData.value[currentProductIndex.value].taxRate) { |
| | | uni.showToast({ |
| | | title: "请å
éæ©ç¨ç", |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | const quantity = parseFloat( |
| | | productData.value[currentProductIndex.value].quantity |
| | | ); |
| | | const unitPrice = parseFloat( |
| | | productData.value[currentProductIndex.value].taxInclusiveUnitPrice |
| | | ); |
| | | |
| | | const getOptionsLIst = () => { |
| | | getOptions().then((res) => { |
| | | // å°ç¨æ·æ°æ®ç»è£
æ picker éè¦çæ ¼å¼ |
| | | supplierList.value = res.data.map(item => ({ |
| | | text: item.supplierName, |
| | | value: item.id |
| | | })); |
| | | }) |
| | | }; |
| | | if (!quantity || quantity <= 0 || !unitPrice) { |
| | | return; |
| | | } |
| | | // 计ç®å«ç¨æ»ä»· |
| | | productData.value[currentProductIndex.value].taxInclusiveTotalPrice = ( |
| | | unitPrice * quantity |
| | | ).toFixed(2); |
| | | |
| | | const convertIdToValue = (data) => { |
| | | // å¦æä¼ å
¥ç䏿¯æ°ç»ï¼åè¿å空æ°ç» |
| | | if (!Array.isArray(data)) { |
| | | return []; |
| | | } |
| | | // é彿 å°å½æ° |
| | | return data.map(item => { |
| | | // å建æ°å¯¹è±¡ï¼æ å°å段 |
| | | const mappedItem = { |
| | | label: item.label, // å
³é®ï¼å° label æ å°ä¸º text |
| | | id: item.id, // ä¿ç id |
| | | }; |
| | | // 妿åå¨ children æ°ç»ï¼åéå½å¤ç |
| | | if (item.children && Array.isArray(item.children) && item.children.length > 0) { |
| | | mappedItem.children = convertIdToValue(item.children); |
| | | } |
| | | return mappedItem; |
| | | }); |
| | | }; |
| | | // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· |
| | | if (productData.value[currentProductIndex.value].taxRate) { |
| | | productData.value[currentProductIndex.value].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice( |
| | | productData.value[currentProductIndex.value].taxInclusiveTotalPrice, |
| | | productData.value[currentProductIndex.value].taxRate |
| | | ); |
| | | } |
| | | }; |
| | | |
| | | // è·å产å大类treeæ°æ® |
| | | const getProductOptions = () => { |
| | | productTreeList().then((res) => { |
| | | productOptions.value = convertIdToValue(res); |
| | | }); |
| | | }; |
| | | // æ°éè¾å
¥æ¡å¤±ç¦ |
| | | const formatAmount = idx => { |
| | | if (productData.value[idx].quantity) { |
| | | const value = parseFloat(productData.value[idx].quantity); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].quantity = value.toFixed(2); |
| | | } |
| | | } |
| | | if (!productData.value[currentProductIndex.value].taxRate) { |
| | | uni.showToast({ |
| | | title: "请å
éæ©ç¨ç", |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | const quantity = parseFloat( |
| | | productData.value[currentProductIndex.value].quantity |
| | | ); |
| | | const unitPrice = parseFloat( |
| | | productData.value[currentProductIndex.value].taxInclusiveUnitPrice |
| | | ); |
| | | |
| | | onMounted(() => { |
| | | // è·å页é¢åæ° |
| | | operationType.value = uni.getStorageSync('operationType') || ''; |
| | | |
| | | // è·åéå®ååå·å表 |
| | | getSalesNoList() |
| | | // è·åä¾åºåå表 |
| | | getOptionsLIst() |
| | | // è·å产å大类treeæ°æ® |
| | | getProductOptions() |
| | | // èµå¼é»è®¤ä¿¡æ¯ |
| | | if (operationType.value === 'add') { |
| | | setUserInfo() |
| | | createPurchaseNo().then((res) => { |
| | | form.value.purchaseContractNumber = res.data; |
| | | }); |
| | | } |
| | | |
| | | // è·åç¼è¾æ°æ®å¹¶å¡«å
表å |
| | | const editDataStr = uni.getStorageSync('editData'); |
| | | if (editDataStr) { |
| | | try { |
| | | editData.value = JSON.parse(editDataStr); |
| | | // 妿æ¯ç¼è¾æ¨¡å¼ï¼çå¾
æ°æ®å è½½å®æåå¡«å
è¡¨åæ°æ® |
| | | if (operationType.value !== 'add' && editData.value) { |
| | | // ä½¿ç¨ nextTick ç¡®ä¿æ°æ®å è½½å®æååå¡«å
|
| | | setTimeout(() => { |
| | | fillFormData(); |
| | | }, 100); |
| | | } |
| | | } catch (error) { |
| | | console.error('è§£æç¼è¾æ°æ®å¤±è´¥:', error); |
| | | } |
| | | } |
| | | }); |
| | | if (!quantity || quantity <= 0 || !unitPrice) { |
| | | return; |
| | | } |
| | | // 计ç®å«ç¨æ»ä»· |
| | | productData.value[currentProductIndex.value].taxInclusiveTotalPrice = ( |
| | | unitPrice * quantity |
| | | ).toFixed(2); |
| | | // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· |
| | | if (productData.value[currentProductIndex.value].taxRate) { |
| | | productData.value[currentProductIndex.value].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice( |
| | | productData.value[currentProductIndex.value].taxInclusiveTotalPrice, |
| | | productData.value[currentProductIndex.value].taxRate |
| | | ); |
| | | } |
| | | }; |
| | | |
| | | // å«ç¨æ»ä»·å¤±ç¦ï¼æ ¹æ®å«ç¨æ»ä»·è®¡ç®å«ç¨åä»·åæ°é |
| | | const formatTaxTotal = idx => { |
| | | if (productData.value[idx].taxInclusiveTotalPrice) { |
| | | const value = parseFloat(productData.value[idx].taxInclusiveTotalPrice); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].taxInclusiveTotalPrice = value.toFixed(2); |
| | | } |
| | | } |
| | | const totalPrice = parseFloat( |
| | | productData.value[currentProductIndex.value].taxInclusiveTotalPrice |
| | | ); |
| | | const quantity = parseFloat( |
| | | productData.value[currentProductIndex.value].quantity |
| | | ); |
| | | |
| | | if (!totalPrice || !quantity || quantity <= 0) { |
| | | return; |
| | | } |
| | | // 计ç®å«ç¨åä»· = å«ç¨æ»ä»· / æ°é |
| | | productData.value[currentProductIndex.value].taxInclusiveUnitPrice = ( |
| | | totalPrice / quantity |
| | | ).toFixed(2); |
| | | // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· |
| | | if (productData.value[currentProductIndex.value].taxRate) { |
| | | productData.value[currentProductIndex.value].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice( |
| | | totalPrice, |
| | | productData.value[currentProductIndex.value].taxRate |
| | | ); |
| | | } |
| | | }; |
| | | |
| | | // ä¸å«ç¨æ»ä»·å¤±ç¦, æ ¹æ®ä¸å«ç¨æ»ä»·è®¡ç®å«ç¨åä»·åæ°é |
| | | const formatNoTaxTotal = idx => { |
| | | if (productData.value[idx].taxExclusiveTotalPrice) { |
| | | const value = parseFloat(productData.value[idx].taxExclusiveTotalPrice); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].taxExclusiveTotalPrice = value.toFixed(2); |
| | | } |
| | | } |
| | | if (!productData.value[currentProductIndex.value].taxRate) { |
| | | uni.showToast({ |
| | | title: "请å
éæ©ç¨ç", |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | const exclusiveTotalPrice = parseFloat( |
| | | productData.value[currentProductIndex.value].taxExclusiveTotalPrice |
| | | ); |
| | | const quantity = parseFloat( |
| | | productData.value[currentProductIndex.value].quantity |
| | | ); |
| | | const taxRate = parseFloat( |
| | | productData.value[currentProductIndex.value].taxRate |
| | | ); |
| | | if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) { |
| | | return; |
| | | } |
| | | // å
计ç®å«ç¨æ»ä»· = ä¸å«ç¨æ»ä»· / (1 - ç¨ç/100) |
| | | const taxRateDecimal = taxRate / 100; |
| | | const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal); |
| | | productData.value[currentProductIndex.value].taxInclusiveTotalPrice = |
| | | inclusiveTotalPrice.toFixed(2); |
| | | // 计ç®å«ç¨åä»· = å«ç¨æ»ä»· / æ°é |
| | | productData.value[currentProductIndex.value].taxInclusiveUnitPrice = ( |
| | | inclusiveTotalPrice / quantity |
| | | ).toFixed(2); |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | // æ¸
çæ¬å°åå¨çæ°æ® |
| | | uni.removeStorageSync("operationType"); |
| | | uni.removeStorageSync("editData"); |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | const onSubmit = () => { |
| | | const hasEmptyApprover = approverNodes.value.some(node => !node.userId); |
| | | if (hasEmptyApprover) { |
| | | uni.showToast({ |
| | | title: "请为ææå®¡æ¹èç¹éæ©å®¡æ¹äººï¼", |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | const approveUserIds = approverNodes.value.map(node => node.userId).join(","); |
| | | |
| | | if (productData.value !== null && productData.value.length > 0) { |
| | | form.value.productData = JSON.parse(JSON.stringify(productData.value)); |
| | | } else { |
| | | uni.showToast({ |
| | | title: "请添å 产åä¿¡æ¯", |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | // 妿salesLedgerId为空ï¼åä¸ä¼ ésalesContractNo |
| | | if (!form.value.salesLedgerId) { |
| | | form.value.salesContractNo = ""; |
| | | } |
| | | if (operationType.value == "add") { |
| | | delete form.value.id; |
| | | } |
| | | form.value.approveUserIds = approveUserIds; |
| | | form.value.type = 2; |
| | | addOrEditPurchase(form.value).then(res => { |
| | | uni.showToast({ |
| | | title: "æäº¤æå", |
| | | icon: "success", |
| | | }); |
| | | goBack(); |
| | | }); |
| | | }; |
| | | |
| | | const setUserInfo = () => { |
| | | form.value.recorderId = userStore.id; |
| | | form.value.recorderName = userStore.nickName; |
| | | // 设置å½å¤©æ¥æ |
| | | const today = new Date(); |
| | | const year = today.getFullYear(); |
| | | const month = String(today.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(today.getDate()).padStart(2, "0"); |
| | | form.value.entryDate = `${year}-${month}-${day}`; |
| | | }; |
| | | |
| | | // ç¡®è®¤æ¥æéæ© |
| | | const onDateConfirm = e => { |
| | | form.value.executionDate = formatDateToYMD(e.value); |
| | | currentDate.value = e.value; |
| | | showTimePicker.value = false; |
| | | }; |
| | | |
| | | // å¡«å
è¡¨åæ°æ®ï¼ç¼è¾æ¨¡å¼ï¼ |
| | | const fillFormData = () => { |
| | | if (!editData.value) return; |
| | | getPurchaseById({ id: editData.value.id, type: 2 }).then(res => { |
| | | productData.value = res.productData; |
| | | if (res && res.approveUserIds) { |
| | | const userIds = res.approveUserIds.split(","); |
| | | approverNodes.value = userIds.map((userId, idx) => { |
| | | const userIdNum = parseInt(userId.trim()); |
| | | // ä»userList䏿¾å°å¯¹åºçç¨æ·ä¿¡æ¯ |
| | | console.log(userList.value, "userList.value"); |
| | | const userInfo = userList.value.find(user => user.userId === userIdNum); |
| | | return { |
| | | id: idx + 1, |
| | | userId: userIdNum, |
| | | nickName: userInfo ? userInfo.nickName : null, |
| | | }; |
| | | }); |
| | | nextApproverId = userIds.length + 1; |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ï¼åå§åä¸ä¸ªç©ºç审æ¹èç¹ |
| | | approverNodes.value = [{ id: 1, userId: null, nickName: null }]; |
| | | nextApproverId = 2; |
| | | } |
| | | }); |
| | | console.log(editData.value); |
| | | // å¡«å
åºæ¬ä¿¡æ¯ |
| | | form.value.purchaseContractNumber = |
| | | editData.value.purchaseContractNumber || ""; |
| | | form.value.salesContractNo = editData.value.salesContractNo || ""; |
| | | form.value.supplierName = editData.value.supplierName || ""; |
| | | form.value.projectName = editData.value.projectName || ""; |
| | | form.value.paymentMethod = editData.value.paymentMethod || ""; |
| | | form.value.salesLedgerId = editData.value.salesLedgerId || ""; |
| | | form.value.recorderId = editData.value.recorderId || ""; |
| | | form.value.recorderName = editData.value.recorderName || ""; |
| | | form.value.entryDate = editData.value.entryDate || ""; |
| | | form.value.id = editData.value.id || ""; |
| | | form.value.supplierId = editData.value.supplierId || ""; |
| | | form.value.executionDate = editData.value.executionDate || ""; |
| | | }; |
| | | |
| | | const getSalesNoList = () => { |
| | | getSalesNo().then(res => { |
| | | // å°ç¨æ·æ°æ®ç»è£
æ picker éè¦çæ ¼å¼ |
| | | salesContractList.value = res.map(user => ({ |
| | | text: user.salesContractNo, |
| | | value: user.id, |
| | | })); |
| | | }); |
| | | }; |
| | | |
| | | const getOptionsLIst = () => { |
| | | getOptions().then(res => { |
| | | // å°ç¨æ·æ°æ®ç»è£
æ picker éè¦çæ ¼å¼ |
| | | supplierList.value = res.data.map(item => ({ |
| | | text: item.supplierName, |
| | | value: item.id, |
| | | })); |
| | | }); |
| | | }; |
| | | |
| | | const convertIdToValue = data => { |
| | | // å¦æä¼ å
¥ç䏿¯æ°ç»ï¼åè¿å空æ°ç» |
| | | if (!Array.isArray(data)) { |
| | | return []; |
| | | } |
| | | // é彿 å°å½æ° |
| | | return data.map(item => { |
| | | // å建æ°å¯¹è±¡ï¼æ å°å段 |
| | | const mappedItem = { |
| | | label: item.label, // å
³é®ï¼å° label æ å°ä¸º text |
| | | id: item.id, // ä¿ç id |
| | | }; |
| | | // 妿åå¨ children æ°ç»ï¼åéå½å¤ç |
| | | if ( |
| | | item.children && |
| | | Array.isArray(item.children) && |
| | | item.children.length > 0 |
| | | ) { |
| | | mappedItem.children = convertIdToValue(item.children); |
| | | } |
| | | return mappedItem; |
| | | }); |
| | | }; |
| | | |
| | | // è·å产å大类treeæ°æ® |
| | | const getProductOptions = () => { |
| | | productTreeList().then(res => { |
| | | productOptions.value = convertIdToValue(res); |
| | | }); |
| | | }; |
| | | const approverNodes = ref([]); |
| | | let nextApproverId = 2; |
| | | const userList = ref([]); |
| | | onMounted(() => { |
| | | // è·å页é¢åæ° |
| | | operationType.value = uni.getStorageSync("operationType") || ""; |
| | | userListNoPageByTenantId().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | // è·åéå®ååå·å表 |
| | | getSalesNoList(); |
| | | // è·åä¾åºåå表 |
| | | getOptionsLIst(); |
| | | // è·å产å大类treeæ°æ® |
| | | getProductOptions(); |
| | | // èµå¼é»è®¤ä¿¡æ¯ |
| | | if (operationType.value === "add") { |
| | | setUserInfo(); |
| | | createPurchaseNo().then(res => { |
| | | form.value.purchaseContractNumber = res.data; |
| | | }); |
| | | } |
| | | |
| | | // çå¬èç³»äººéæ©äºä»¶ |
| | | uni.$on("selectContact", handleSelectContact); |
| | | |
| | | // è·åç¼è¾æ°æ®å¹¶å¡«å
表å |
| | | const editDataStr = uni.getStorageSync("editData"); |
| | | if (editDataStr) { |
| | | try { |
| | | editData.value = JSON.parse(editDataStr); |
| | | // 妿æ¯ç¼è¾æ¨¡å¼ï¼çå¾
æ°æ®å è½½å®æåå¡«å
è¡¨åæ°æ® |
| | | if (operationType.value !== "add" && editData.value) { |
| | | // ä½¿ç¨ nextTick ç¡®ä¿æ°æ®å è½½å®æååå¡«å
|
| | | setTimeout(() => { |
| | | fillFormData(); |
| | | }, 100); |
| | | } |
| | | } catch (error) { |
| | | console.error("è§£æç¼è¾æ°æ®å¤±è´¥:", error); |
| | | } |
| | | } else { |
| | | approverNodes.value = [{ id: 1, userId: null }]; |
| | | } |
| | | }); |
| | | // å¤çèç³»äººéæ©ç»æ |
| | | const handleSelectContact = data => { |
| | | const { stepIndex, contact } = data; |
| | | // å°éä¸çè系人设置为对åºå®¡æ¹æ¥éª¤ç审æ¹äºº |
| | | console.log(contact); |
| | | console.log(stepIndex, "stepIndex"); |
| | | console.log(approverNodes.value[stepIndex], "审æ¹äºº"); |
| | | approverNodes.value[stepIndex].userId = contact.userId; |
| | | approverNodes.value[stepIndex].nickName = contact.nickName; |
| | | }; |
| | | |
| | | const addApprover = stepIndex => { |
| | | // 跳转å°èç³»äººéæ©é¡µé¢ |
| | | uni.setStorageSync("stepIndex", stepIndex); |
| | | uni.navigateTo({ |
| | | url: "/pages/cooperativeOffice/collaborativeApproval/contactSelect", |
| | | }); |
| | | }; |
| | | |
| | | const addApprovalStep = () => { |
| | | // æ·»å æ°çå®¡æ¹æ¥éª¤ |
| | | approverNodes.value.push({ userId: null, nickName: null }); |
| | | console.log(approverNodes.value, "approverNodes.value"); |
| | | }; |
| | | |
| | | const removeApprover = stepIndex => { |
| | | // ç§»é¤å®¡æ¹äºº |
| | | approverNodes.value[stepIndex].userId = null; |
| | | approverNodes.value[stepIndex].nickName = null; |
| | | }; |
| | | |
| | | const removeApprovalStep = stepIndex => { |
| | | // ç¡®ä¿è³å°ä¿çä¸ä¸ªå®¡æ¹æ¥éª¤ |
| | | if (approverNodes.value.length > 1) { |
| | | approverNodes.value.splice(stepIndex, 1); |
| | | } else { |
| | | uni.showToast({ |
| | | title: "è³å°éè¦ä¸ä¸ªå®¡æ¹æ¥éª¤", |
| | | icon: "none", |
| | | }); |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import '@/static/scss/form-common.scss'; |
| | | </style> |
| | | @import "@/static/scss/form-common.scss"; |
| | | |
| | | .approval-process { |
| | | background: #fff; |
| | | margin: 16px; |
| | | border-radius: 16px; |
| | | padding: 16px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); |
| | | } |
| | | |
| | | .approval-header { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .approval-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | display: block; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .approval-desc { |
| | | font-size: 12px; |
| | | color: #999; |
| | | } |
| | | |
| | | /* æ ·å¼å¢å¼ºä¸ºâç®æ´å°åå飿 ¼â */ |
| | | .approval-steps { |
| | | padding-left: 22px; |
| | | position: relative; |
| | | |
| | | &::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 11px; |
| | | top: 40px; |
| | | bottom: 40px; |
| | | width: 2px; |
| | | background: linear-gradient( |
| | | to bottom, |
| | | #e6f7ff 0%, |
| | | #bae7ff 50%, |
| | | #91d5ff 100% |
| | | ); |
| | | border-radius: 1px; |
| | | } |
| | | } |
| | | |
| | | .approval-step { |
| | | position: relative; |
| | | margin-bottom: 24px; |
| | | |
| | | &::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: -18px; |
| | | top: 14px; // ä» 8px è°æ´ä¸º 14pxï¼ä¸æåä¸å¿å¯¹é½ |
| | | width: 12px; |
| | | height: 12px; |
| | | background: #fff; |
| | | border: 3px solid #006cfb; |
| | | border-radius: 50%; |
| | | z-index: 2; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | } |
| | | |
| | | .step-title { |
| | | top: 12px; |
| | | margin-bottom: 12px; |
| | | position: relative; |
| | | margin-left: 6px; |
| | | } |
| | | |
| | | .step-title text { |
| | | font-size: 14px; |
| | | color: #666; |
| | | background: #f0f0f0; |
| | | padding: 4px 12px; |
| | | border-radius: 12px; |
| | | position: relative; |
| | | line-height: 1.4; // ç¡®ä¿æåè¡é«ä¸è´ |
| | | } |
| | | |
| | | .approver-item { |
| | | display: flex; |
| | | align-items: center; |
| | | background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%); |
| | | border-radius: 16px; |
| | | padding: 16px; |
| | | gap: 12px; |
| | | position: relative; |
| | | border: 1px solid #e6f7ff; |
| | | box-shadow: 0 4px 12px rgba(0, 108, 251, 0.08); |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .approver-avatar { |
| | | width: 48px; |
| | | height: 48px; |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | position: relative; |
| | | box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3); |
| | | } |
| | | |
| | | .avatar-text { |
| | | color: #fff; |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .approver-info { |
| | | flex: 1; |
| | | position: relative; |
| | | } |
| | | |
| | | .approver-name { |
| | | display: block; |
| | | font-size: 16px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | position: relative; |
| | | } |
| | | |
| | | .approver-dept { |
| | | font-size: 12px; |
| | | color: #999; |
| | | background: rgba(0, 108, 251, 0.05); |
| | | padding: 2px 8px; |
| | | border-radius: 8px; |
| | | display: inline-block; |
| | | position: relative; |
| | | |
| | | &::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 4px; |
| | | top: 50%; |
| | | transform: translateY(-50%); |
| | | width: 2px; |
| | | height: 2px; |
| | | background: #006cfb; |
| | | border-radius: 50%; |
| | | } |
| | | } |
| | | |
| | | .delete-approver-btn { |
| | | font-size: 16px; |
| | | color: #ff4d4f; |
| | | background: linear-gradient( |
| | | 135deg, |
| | | rgba(255, 77, 79, 0.1) 0%, |
| | | rgba(255, 77, 79, 0.05) 100% |
| | | ); |
| | | width: 28px; |
| | | height: 28px; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | transition: all 0.3s ease; |
| | | position: relative; |
| | | } |
| | | |
| | | .add-approver-btn { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background: linear-gradient(135deg, #f0f8ff 0%, #e6f7ff 100%); |
| | | border: 2px dashed #006cfb; |
| | | border-radius: 16px; |
| | | padding: 20px; |
| | | color: #006cfb; |
| | | font-size: 14px; |
| | | position: relative; |
| | | transition: all 0.3s ease; |
| | | |
| | | &::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 50%; |
| | | top: 50%; |
| | | transform: translate(-50%, -50%); |
| | | width: 32px; |
| | | height: 32px; |
| | | border: 2px solid #006cfb; |
| | | border-radius: 50%; |
| | | opacity: 0; |
| | | transition: all 0.3s ease; |
| | | } |
| | | } |
| | | |
| | | .delete-step-btn { |
| | | color: #ff4d4f; |
| | | font-size: 12px; |
| | | background: linear-gradient( |
| | | 135deg, |
| | | rgba(255, 77, 79, 0.1) 0%, |
| | | rgba(255, 77, 79, 0.05) 100% |
| | | ); |
| | | padding: 6px 12px; |
| | | border-radius: 12px; |
| | | display: inline-block; |
| | | position: relative; |
| | | transition: all 0.3s ease; |
| | | |
| | | &::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 6px; |
| | | top: 50%; |
| | | transform: translateY(-50%); |
| | | width: 4px; |
| | | height: 4px; |
| | | background: #ff4d4f; |
| | | border-radius: 50%; |
| | | } |
| | | } |
| | | |
| | | .step-line { |
| | | display: none; // éè忥ç线æ¡ï¼ä½¿ç¨ä¼ªå
ç´ ä»£æ¿ |
| | | } |
| | | |
| | | .add-step-btn { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | .footer-btns { |
| | | position: fixed; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: #fff; |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | padding: 0.75rem 0; |
| | | box-shadow: 0 -0.125rem 0.5rem rgba(0, 0, 0, 0.05); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | .cancel-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #ffffff; |
| | | width: 6.375rem; |
| | | background: #c7c9cc; |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3, 88, 185, 0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | .save-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #ffffff; |
| | | width: 14rem; |
| | | background: linear-gradient(140deg, #00baff 0%, #006cfb 100%); |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3, 88, 185, 0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | // å¨ç»å®ä¹ |
| | | @keyframes pulse { |
| | | 0% { |
| | | transform: scale(1); |
| | | opacity: 1; |
| | | } |
| | | 50% { |
| | | transform: scale(1.2); |
| | | opacity: 0.7; |
| | | } |
| | | 100% { |
| | | transform: scale(1); |
| | | opacity: 1; |
| | | } |
| | | } |
| | | |
| | | @keyframes rotate { |
| | | 0% { |
| | | transform: rotate(0deg); |
| | | } |
| | | 100% { |
| | | transform: rotate(360deg); |
| | | } |
| | | } |
| | | |
| | | @keyframes ripple { |
| | | 0% { |
| | | transform: translate(-50%, -50%) scale(0.8); |
| | | opacity: 1; |
| | | } |
| | | 100% { |
| | | transform: translate(-50%, -50%) scale(1.6); |
| | | opacity: 0; |
| | | } |
| | | } |
| | | |
| | | /* 妿已æ .step-lineï¼è¿éæ´ç²¾åå®ä½å°å·¦ä¾§ä¸å°åç¹å¯¹é½ */ |
| | | .step-line { |
| | | position: absolute; |
| | | left: 4px; |
| | | top: 48px; |
| | | width: 2px; |
| | | height: calc(100% - 48px); |
| | | background: #e5e7eb; |
| | | } |
| | | |
| | | .approver-container { |
| | | display: flex; |
| | | align-items: center; |
| | | background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%); |
| | | border-radius: 16px; |
| | | gap: 12px; |
| | | padding: 10px 0; |
| | | background: transparent; |
| | | border: none; |
| | | box-shadow: none; |
| | | } |
| | | |
| | | .approver-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | padding: 8px 10px; |
| | | background: transparent; |
| | | border: none; |
| | | box-shadow: none; |
| | | border-radius: 0; |
| | | } |
| | | |
| | | .approver-avatar { |
| | | position: relative; |
| | | width: 40px; |
| | | height: 40px; |
| | | border-radius: 50%; |
| | | background: #f3f4f6; |
| | | border: 2px solid #e5e7eb; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | animation: none; /* ç¦ç¨æè½¬çå¨ç»ï¼åå½ç®æ´ */ |
| | | } |
| | | |
| | | .avatar-text { |
| | | font-size: 14px; |
| | | color: #374151; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .add-approver-btn { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | background: transparent; |
| | | border: none; |
| | | box-shadow: none; |
| | | padding: 0; |
| | | } |
| | | |
| | | .add-approver-btn .add-circle { |
| | | width: 40px; |
| | | height: 40px; |
| | | border: 2px dashed #a0aec0; |
| | | border-radius: 50%; |
| | | color: #6b7280; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 22px; |
| | | line-height: 1; |
| | | } |
| | | |
| | | .add-approver-btn .add-label { |
| | | color: #3b82f6; |
| | | font-size: 14px; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <view class="sales-account"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="éè´å°è´¦" @back="goBack" /> |
| | | |
| | | <!-- æç´¢åçéåºå --> |
| | | <view class="search-section"> |
| | | <view class="search-bar"> |
| | | <view class="search-input"> |
| | | <up-input |
| | | class="search-text" |
| | | placeholder="请è¾å
¥éè´ååå·æç´¢" |
| | | v-model="purchaseContractNumber" |
| | | @change="getList" |
| | | clearable |
| | | /> |
| | | </view> |
| | | <view class="filter-button" @click="getList"> |
| | | <up-icon name="search" size="24" color="#999"></up-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- éè´å°è´¦ç叿µ --> |
| | | <view class="ledger-list" v-if="ledgerList.length > 0"> |
| | | <view v-for="(item, index) in ledgerList" :key="index"> |
| | | <view class="ledger-item" @click="handleInfo('edit', item)"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <view class="document-icon"> |
| | | <up-icon name="file-text" size="16" color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="item-id">{{ item.purchaseContractNumber }}</text> |
| | | </view> |
| | | <!-- <view class="item-tag">--> |
| | | <!-- <text class="tag-text">{{ item.recorder }}</text>--> |
| | | <!-- </view>--> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">éå®ååå·</text> |
| | | <text class="detail-value">{{ item.salesContractNo }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ä¾åºååç§°</text> |
| | | <text class="detail-value">{{ item.supplierName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">项ç®åç§°</text> |
| | | <text class="detail-value">{{ item.projectName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">仿¬¾æ¹å¼</text> |
| | | <text class="detail-value">{{ item.paymentMethod }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ååéé¢(å
)</text> |
| | | <text class="detail-value highlight">{{ item.contractAmount }}</text> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="detail-info"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å½å
¥äºº</text> |
| | | <text class="detail-value">{{ item.recorderName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å½å
¥æ¥æ</text> |
| | | <text class="detail-value">{{ item.entryDate }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else class="no-data"> |
| | | <text>ææ éè´å°è´¦æ°æ®</text> |
| | | </view> |
| | | |
| | | <!-- æµ®å¨æä½æé® --> |
| | | <view class="fab-button" @click="handleInfo('add')"> |
| | | <up-icon name="plus" size="24" color="#ffffff"></up-icon> |
| | | </view> |
| | | </view> |
| | | <view class="sales-account"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="éè´å°è´¦" |
| | | @back="goBack" /> |
| | | <!-- æç´¢åçéåºå --> |
| | | <view class="search-section"> |
| | | <view class="search-bar"> |
| | | <view class="search-input"> |
| | | <up-input class="search-text" |
| | | placeholder="请è¾å
¥éè´ååå·æç´¢" |
| | | v-model="purchaseContractNumber" |
| | | @change="getList" |
| | | clearable /> |
| | | </view> |
| | | <view class="filter-button" |
| | | @click="getList"> |
| | | <up-icon name="search" |
| | | size="24" |
| | | color="#999"></up-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- éè´å°è´¦ç叿µ --> |
| | | <view class="ledger-list" |
| | | v-if="ledgerList.length > 0"> |
| | | <view v-for="(item, index) in ledgerList" |
| | | :key="index"> |
| | | <view class="ledger-item" |
| | | @click="handleInfo('edit', item)"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <view class="document-icon"> |
| | | <up-icon name="file-text" |
| | | size="16" |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="item-id">{{ item.purchaseContractNumber }}</text> |
| | | </view> |
| | | <view class="item-tag"> |
| | | <u-tag :type="getApprovalStatusType(item.approvalStatus)"> |
| | | {{ approvalStatusText[item.approvalStatus] || 'æªç¥ç¶æ' }} |
| | | </u-tag> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">éå®ååå·</text> |
| | | <text class="detail-value">{{ item.salesContractNo }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ä¾åºååç§°</text> |
| | | <text class="detail-value">{{ item.supplierName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">项ç®åç§°</text> |
| | | <text class="detail-value">{{ item.projectName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">仿¬¾æ¹å¼</text> |
| | | <text class="detail-value">{{ item.paymentMethod }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ååéé¢(å
)</text> |
| | | <text class="detail-value highlight">{{ item.contractAmount }}</text> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="detail-info"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å½å
¥äºº</text> |
| | | <text class="detail-value">{{ item.recorderName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å½å
¥æ¥æ</text> |
| | | <text class="detail-value">{{ item.entryDate }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else |
| | | class="no-data"> |
| | | <text>ææ éè´å°è´¦æ°æ®</text> |
| | | </view> |
| | | <!-- æµ®å¨æä½æé® --> |
| | | <view class="fab-button" |
| | | @click="handleInfo('add')"> |
| | | <up-icon name="plus" |
| | | size="24" |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from 'vue'; |
| | | import { onShow } from '@dcloudio/uni-app'; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import {purchaseListPage} from "@/api/procurementManagement/procurementLedger"; |
| | | const userStore = useUserStore() |
| | | import { ref } from "vue"; |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import { purchaseListPage } from "@/api/procurementManagement/procurementLedger"; |
| | | const userStore = useUserStore(); |
| | | const approvalStatusText = { |
| | | 1: "å¾
å®¡æ ¸", |
| | | 2: "审æ¹ä¸", |
| | | 3: "审æ¹éè¿", |
| | | 4: "审æ¹å¤±è´¥", |
| | | }; |
| | | // è·å审æ¹ç¶ææ ç¾ç±»å |
| | | const getApprovalStatusType = status => { |
| | | const typeMap = { |
| | | 1: "info", // å¾
å®¡æ ¸ - ç°è² |
| | | 2: "warning", // 审æ¹ä¸ - æ©è² |
| | | 3: "success", // 审æ¹éè¿ - ç»¿è² |
| | | 4: "error", // 审æ¹å¤±è´¥ - çº¢è² |
| | | }; |
| | | return typeMap[status] || ""; |
| | | }; |
| | | // æç´¢å
³é®è¯ |
| | | const purchaseContractNumber = ref(""); |
| | | |
| | | // æç´¢å
³é®è¯ |
| | | const purchaseContractNumber = ref(''); |
| | | // éè´å°è´¦æ°æ® |
| | | const ledgerList = ref([]); |
| | | |
| | | // éè´å°è´¦æ°æ® |
| | | const ledgerList = ref([]); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | // æ¥è¯¢å表 |
| | | const getList = () => { |
| | | showLoadingToast('å è½½ä¸...') |
| | | const page = { |
| | | current: -1, |
| | | size: -1 |
| | | } |
| | | purchaseListPage({...page, purchaseContractNumber: purchaseContractNumber.value}).then((res) => { |
| | | ledgerList.value = res.data.records; |
| | | closeToast() |
| | | }).catch(() => { |
| | | closeToast() |
| | | }); |
| | | }; |
| | | |
| | | // æ¾ç¤ºå è½½æç¤º |
| | | const showLoadingToast = (message) => { |
| | | uni.showLoading({ |
| | | title: message, |
| | | mask: true |
| | | }); |
| | | }; |
| | | |
| | | // å
³éæç¤º |
| | | const closeToast = () => { |
| | | uni.hideLoading(); |
| | | }; |
| | | |
| | | // å¤çå°è´¦ä¿¡æ¯æä½ï¼æ¥ç/ç¼è¾/æ°å¢ï¼ |
| | | const handleInfo = (type, row) => { |
| | | try { |
| | | // 设置æä½ç±»å |
| | | uni.setStorageSync('operationType', type); |
| | | |
| | | // å¦ææ¯æ¥çæç¼è¾æä½ |
| | | if (type !== 'add') { |
| | | // éªè¯è¡æ°æ®æ¯å¦åå¨ |
| | | if (!row) { |
| | | uni.showToast({ |
| | | title: 'æ°æ®ä¸åå¨', |
| | | icon: 'error' |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // æ£æ¥æéï¼åªæå½å
¥äººæè½ç¼è¾ |
| | | if (row.recorderName != userStore.nickName) { |
| | | // éå½å
¥äººè·³è½¬å°åªè¯»è¯¦æ
é¡µé¢ |
| | | uni.setStorageSync('editData', JSON.stringify(row)); |
| | | uni.navigateTo({ |
| | | url: '/pages/procurementManagement/procurementLedger/view' |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // å½å
¥äººç¼è¾ï¼å卿°æ®å¹¶è·³è½¬å°ç¼è¾é¡µé¢ |
| | | uni.setStorageSync('editData', JSON.stringify(row)); |
| | | uni.navigateTo({ |
| | | url: '/pages/procurementManagement/procurementLedger/detail' |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | // æ¥è¯¢å表 |
| | | const getList = () => { |
| | | showLoadingToast("å è½½ä¸..."); |
| | | const page = { |
| | | current: -1, |
| | | size: -1, |
| | | }; |
| | | purchaseListPage({ |
| | | ...page, |
| | | purchaseContractNumber: purchaseContractNumber.value, |
| | | }) |
| | | .then(res => { |
| | | ledgerList.value = res.data.records; |
| | | closeToast(); |
| | | }) |
| | | .catch(() => { |
| | | closeToast(); |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // æ°å¢æä½ï¼ç´æ¥è·³è½¬å°ç¼è¾é¡µé¢ |
| | | uni.navigateTo({ |
| | | url: '/pages/procurementManagement/procurementLedger/detail' |
| | | }); |
| | | |
| | | } catch (error) { |
| | | console.error('å¤çå°è´¦ä¿¡æ¯æä½å¤±è´¥:', error); |
| | | uni.showToast({ |
| | | title: 'æä½å¤±è´¥ï¼è¯·éè¯', |
| | | icon: 'error' |
| | | }); |
| | | } |
| | | }; |
| | | }; |
| | | |
| | | onShow(() => { |
| | | // æ¾ç¤ºå è½½æç¤º |
| | | const showLoadingToast = message => { |
| | | uni.showLoading({ |
| | | title: message, |
| | | mask: true, |
| | | }); |
| | | }; |
| | | |
| | | // å
³éæç¤º |
| | | const closeToast = () => { |
| | | uni.hideLoading(); |
| | | }; |
| | | |
| | | // å¤çå°è´¦ä¿¡æ¯æä½ï¼æ¥ç/ç¼è¾/æ°å¢ï¼ |
| | | const handleInfo = (type, row) => { |
| | | try { |
| | | // 设置æä½ç±»å |
| | | uni.setStorageSync("operationType", type); |
| | | |
| | | // å¦ææ¯æ¥çæç¼è¾æä½ |
| | | if (type !== "add") { |
| | | // éªè¯è¡æ°æ®æ¯å¦åå¨ |
| | | if (!row) { |
| | | uni.showToast({ |
| | | title: "æ°æ®ä¸åå¨", |
| | | icon: "error", |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // æ£æ¥æéï¼åªæå½å
¥äººæè½ç¼è¾ |
| | | if (row.recorderName != userStore.nickName) { |
| | | // éå½å
¥äººè·³è½¬å°åªè¯»è¯¦æ
é¡µé¢ |
| | | uni.setStorageSync("editData", JSON.stringify(row)); |
| | | uni.navigateTo({ |
| | | url: "/pages/procurementManagement/procurementLedger/view", |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // å½å
¥äººç¼è¾ï¼å卿°æ®å¹¶è·³è½¬å°ç¼è¾é¡µé¢ |
| | | uni.setStorageSync("editData", JSON.stringify(row)); |
| | | uni.navigateTo({ |
| | | url: "/pages/procurementManagement/procurementLedger/detail", |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // æ°å¢æä½ï¼ç´æ¥è·³è½¬å°ç¼è¾é¡µé¢ |
| | | uni.navigateTo({ |
| | | url: "/pages/procurementManagement/procurementLedger/detail", |
| | | }); |
| | | } catch (error) { |
| | | console.error("å¤çå°è´¦ä¿¡æ¯æä½å¤±è´¥:", error); |
| | | uni.showToast({ |
| | | title: "æä½å¤±è´¥ï¼è¯·éè¯", |
| | | icon: "error", |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | onShow(() => { |
| | | // 页颿¾ç¤ºæ¶å·æ°å表 |
| | | getList(); |
| | | }); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import '@/styles/procurement-common.scss'; |
| | | @import "@/styles/procurement-common.scss"; |
| | | |
| | | // éè´å°è´¦ç¹ææ ·å¼ï¼å¦æéè¦å¯å¨æ¤æ·»å ï¼ |
| | | // éè´å°è´¦ç¹ææ ·å¼ï¼å¦æéè¦å¯å¨æ¤æ·»å ï¼ |
| | | </style> |
| | |
| | | <template> |
| | | <view class="account-detail"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="å°è´¦è¯¦æ
" @back="goBack" /> |
| | | |
| | | <!-- 表ååºå --> |
| | | <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 |
| | | @click="showPicker = true" |
| | | > |
| | | <up-input |
| | | v-model="form.salesman" |
| | | readonly |
| | | @click="showPicker = true" |
| | | placeholder="ç¹å»éæ©ä¸å¡å" |
| | | /> |
| | | <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="请è¾å
¥å®¢æ·ååå·" |
| | | /> |
| | | </up-form-item> |
| | | <up-form-item |
| | | label="客æ·åç§°" |
| | | prop="customerName" |
| | | required |
| | | > |
| | | <up-input |
| | | v-model="form.customerName" |
| | | readonly |
| | | placeholder="ç¹å»éæ©å®¢æ·" |
| | | @click="showCustomerPicker = true" |
| | | /> |
| | | <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 |
| | | > |
| | | <up-input |
| | | v-model="form.executionDate" |
| | | readonly |
| | | placeholder="ç¹å»éæ©æ¶é´" |
| | | @click="showDatePicker = true" |
| | | /> |
| | | <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" |
| | | /> |
| | | </up-popup> |
| | | <!-- 客æ·éæ© --> |
| | | <up-action-sheet |
| | | :show="showCustomerPicker" |
| | | :actions="customerActionList" |
| | | title="鿩客æ·" |
| | | @select="onCustomerSelect" |
| | | @close="showCustomerPicker = false" |
| | | /> |
| | | |
| | | <!-- 产åå¤§ç±»éæ©å¨ --> |
| | | <up-popup :show="showCategoryPicker" mode="bottom"> |
| | | <!-- 头鍿é®åºå --> |
| | | <view class="popup-header"> |
| | | <view @click="showCategoryPicker = false" class="cancelButton">åæ¶</view> |
| | | <view @click="confirmCategorySelection" class="confirmButton">ç¡®å®</view> |
| | | </view> |
| | | <u-tree |
| | | :data="productOptions" |
| | | :props="defaultProps" |
| | | show-checkbox |
| | | default-expand-all |
| | | check-strictly |
| | | @check-change="onCategoryConfirm" |
| | | /> |
| | | </up-popup> |
| | | |
| | | <!-- è§æ ¼åå·éæ©å¨ --> |
| | | <up-action-sheet |
| | | :show="showSpecificationPicker" |
| | | :actions="specificationActionList" |
| | | title="éæ©è§æ ¼åå·" |
| | | @select="onSpecificationSelect" |
| | | @close="showSpecificationPicker = false" |
| | | /> |
| | | |
| | | <!-- ç¨çéæ©å¨ --> |
| | | <up-action-sheet |
| | | :show="showTaxRatePicker" |
| | | :actions="taxRateActionList" |
| | | title="éæ©ç¨ç" |
| | | @select="onTaxRateSelect" |
| | | @close="showTaxRatePicker = false" |
| | | /> |
| | | |
| | | <!-- å票类åéæ©å¨ --> |
| | | <up-action-sheet |
| | | :show="showInvoiceTypePicker" |
| | | :actions="invoiceTypeActionList" |
| | | title="éæ©å票类å" |
| | | @select="onInvoiceTypeSelect" |
| | | @close="showInvoiceTypePicker = false" |
| | | /> |
| | | <!-- 产åä¿¡æ¯ --> |
| | | <view class="product-section"> |
| | | <view class="section-header"> |
| | | <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"> |
| | | <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'"> |
| | | <up-button type="error" size="mini" @click="removeProduct(idx)" class="del-btn"> |
| | | å é¤ |
| | | </up-button> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 产åä¿¡æ¯è¡¨å --> |
| | | <view class="product-form"> |
| | | <!-- 产å大类 --> |
| | | <up-form-item |
| | | label="产å大类" |
| | | prop="productCategory" |
| | | required |
| | | > |
| | | <up-input |
| | | v-model="product.productCategory" |
| | | readonly |
| | | placeholder="è¯·éæ©" |
| | | @click="openCategoryPicker(idx)" |
| | | /> |
| | | <template #right> |
| | | <up-icon |
| | | name="arrow-right" |
| | | @click="showCategoryPicker = true" |
| | | ></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | |
| | | <!-- è§æ ¼åå· --> |
| | | <up-form-item |
| | | label="è§æ ¼åå·" |
| | | prop="specificationModel" |
| | | required |
| | | > |
| | | <up-input |
| | | v-model="product.specificationModel" |
| | | readonly |
| | | placeholder="è¯·éæ©" |
| | | @click="openSpecificationPicker(idx)" |
| | | /> |
| | | <template #right> |
| | | <up-icon |
| | | name="arrow-right" |
| | | @click="showSpecificationPicker = true" |
| | | ></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | <!-- ç»å®æºå¨ --> |
| | | <up-form-item |
| | | <PageHeader title="å°è´¦è¯¦æ
" |
| | | @back="goBack" /> |
| | | <!-- 表ååºå --> |
| | | <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 |
| | | @click="showPicker = true"> |
| | | <up-input v-model="form.salesman" |
| | | readonly |
| | | @click="showPicker = true" |
| | | placeholder="ç¹å»éæ©ä¸å¡å" /> |
| | | <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="请è¾å
¥å®¢æ·ååå·" /> |
| | | </up-form-item> |
| | | <up-form-item label="客æ·åç§°" |
| | | prop="customerName" |
| | | required> |
| | | <up-input v-model="form.customerName" |
| | | readonly |
| | | placeholder="ç¹å»éæ©å®¢æ·" |
| | | @click="showCustomerPicker = true" /> |
| | | <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> |
| | | <up-input v-model="form.executionDate" |
| | | readonly |
| | | placeholder="ç¹å»éæ©æ¶é´" |
| | | @click="showDatePicker = true" /> |
| | | <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" /> |
| | | </up-popup> |
| | | <!-- 客æ·éæ© --> |
| | | <up-action-sheet :show="showCustomerPicker" |
| | | :actions="customerActionList" |
| | | title="鿩客æ·" |
| | | @select="onCustomerSelect" |
| | | @close="showCustomerPicker = false" /> |
| | | <!-- 产åå¤§ç±»éæ©å¨ --> |
| | | <up-popup :show="showCategoryPicker" |
| | | mode="bottom"> |
| | | <!-- 头鍿é®åºå --> |
| | | <view class="popup-header"> |
| | | <view @click="showCategoryPicker = false" |
| | | class="cancelButton">åæ¶</view> |
| | | <view @click="confirmCategorySelection" |
| | | class="confirmButton">ç¡®å®</view> |
| | | </view> |
| | | <u-tree :data="productOptions" |
| | | :props="defaultProps" |
| | | show-checkbox |
| | | default-expand-all |
| | | check-strictly |
| | | @check-change="onCategoryConfirm" /> |
| | | </up-popup> |
| | | <!-- è§æ ¼åå·éæ©å¨ --> |
| | | <up-action-sheet :show="showSpecificationPicker" |
| | | :actions="specificationActionList" |
| | | title="éæ©è§æ ¼åå·" |
| | | @select="onSpecificationSelect" |
| | | @close="showSpecificationPicker = false" /> |
| | | <!-- ç¨çéæ©å¨ --> |
| | | <up-action-sheet :show="showTaxRatePicker" |
| | | :actions="taxRateActionList" |
| | | title="éæ©ç¨ç" |
| | | @select="onTaxRateSelect" |
| | | @close="showTaxRatePicker = false" /> |
| | | <!-- å票类åéæ©å¨ --> |
| | | <up-action-sheet :show="showInvoiceTypePicker" |
| | | :actions="invoiceTypeActionList" |
| | | title="éæ©å票类å" |
| | | @select="onInvoiceTypeSelect" |
| | | @close="showInvoiceTypePicker = false" /> |
| | | <!-- 产åä¿¡æ¯ --> |
| | | <view class="product-section"> |
| | | <view class="section-header"> |
| | | <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"> |
| | | <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'"> |
| | | <up-button type="error" |
| | | size="mini" |
| | | @click="removeProduct(idx)" |
| | | class="del-btn"> |
| | | å é¤ |
| | | </up-button> |
| | | </view> |
| | | </view> |
| | | <!-- 产åä¿¡æ¯è¡¨å --> |
| | | <view class="product-form"> |
| | | <!-- 产å大类 --> |
| | | <up-form-item label="产å大类" |
| | | prop="productCategory" |
| | | required> |
| | | <up-input v-model="product.productCategory" |
| | | readonly |
| | | placeholder="è¯·éæ©" |
| | | @click="openCategoryPicker(idx)" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showCategoryPicker = true"></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | <!-- è§æ ¼åå· --> |
| | | <up-form-item label="è§æ ¼åå·" |
| | | prop="specificationModel" |
| | | required> |
| | | <up-input v-model="product.specificationModel" |
| | | readonly |
| | | placeholder="è¯·éæ©" |
| | | @click="openSpecificationPicker(idx)" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showSpecificationPicker = true"></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | <!-- ç»å®æºå¨ --> |
| | | <!-- <up-form-item |
| | | label="ç»å®æºå¨" |
| | | prop="speculativeTradingName" |
| | | required |
| | |
| | | v-model="product.speculativeTradingName" |
| | | placeholder="请è¾å
¥" |
| | | /> |
| | | </up-form-item> |
| | | |
| | | <!-- åä½ --> |
| | | <up-form-item |
| | | label="åä½" |
| | | prop="unit" |
| | | required |
| | | > |
| | | <up-input |
| | | v-model="product.unit" |
| | | placeholder="请è¾å
¥" |
| | | /> |
| | | </up-form-item> |
| | | |
| | | <!-- ç¨ç --> |
| | | <up-form-item |
| | | label="ç¨ç(%)" |
| | | prop="taxRate" |
| | | required |
| | | > |
| | | <up-input |
| | | v-model="product.taxRate" |
| | | readonly |
| | | placeholder="è¯·éæ©" |
| | | @click="openTaxRatePicker(idx)" |
| | | /> |
| | | <template #right> |
| | | <up-icon |
| | | name="arrow-right" |
| | | @click="showTaxRatePicker = true" |
| | | ></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | |
| | | <!-- å«ç¨åä»· --> |
| | | <up-form-item |
| | | label="å«ç¨åä»·(å
)" |
| | | prop="taxInclusiveUnitPrice" |
| | | required |
| | | > |
| | | <up-input |
| | | v-model="product.taxInclusiveUnitPrice" |
| | | type="number" |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatTaxPrice(idx)" |
| | | /> |
| | | </up-form-item> |
| | | |
| | | <!-- æ°é --> |
| | | <up-form-item |
| | | label="æ°é" |
| | | prop="quantity" |
| | | required |
| | | > |
| | | <up-input |
| | | v-model="product.quantity" |
| | | type="number" |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatAmount(idx)" |
| | | /> |
| | | </up-form-item> |
| | | |
| | | <!-- å«ç¨æ»ä»· --> |
| | | <up-form-item |
| | | label="å«ç¨æ»ä»·(å
)" |
| | | prop="taxInclusiveTotalPrice" |
| | | required |
| | | > |
| | | <up-input |
| | | v-model="product.taxInclusiveTotalPrice" |
| | | type="number" |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatTaxTotal(idx)" |
| | | /> |
| | | </up-form-item> |
| | | |
| | | <!-- ä¸å«ç¨æ»ä»· --> |
| | | <up-form-item |
| | | label="ä¸å«ç¨æ»ä»·(å
)" |
| | | prop="taxExclusiveTotalPrice" |
| | | required |
| | | > |
| | | <up-input |
| | | v-model="product.taxExclusiveTotalPrice" |
| | | type="number" |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatNoTaxTotal(idx)" |
| | | /> |
| | | </up-form-item> |
| | | |
| | | <!-- å票类å --> |
| | | <up-form-item |
| | | label="å票类å" |
| | | prop="invoiceType" |
| | | required |
| | | > |
| | | <up-input |
| | | v-model="product.invoiceType" |
| | | readonly |
| | | placeholder="è¯·éæ©" |
| | | @click="openInvoiceTypePicker(idx)" |
| | | /> |
| | | <template #right> |
| | | <up-icon |
| | | name="arrow-right" |
| | | @click="showInvoiceTypePicker = true" |
| | | ></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </up-form> |
| | | |
| | | <!-- 使ç¨å
Œ
±åºé¨æé®ç»ä»¶ --> |
| | | <FooterButtons |
| | | :show="operationType !== 'view'" |
| | | cancelText="åæ¶" |
| | | confirmText="ä¿å" |
| | | @cancel="goBack" |
| | | @confirm="onSubmit" |
| | | /> |
| | | </up-form-item> --> |
| | | <!-- åä½ --> |
| | | <up-form-item label="åä½" |
| | | prop="unit" |
| | | required> |
| | | <up-input v-model="product.unit" |
| | | placeholder="请è¾å
¥" /> |
| | | </up-form-item> |
| | | <!-- ç¨ç --> |
| | | <up-form-item label="ç¨ç(%)" |
| | | prop="taxRate" |
| | | required> |
| | | <up-input v-model="product.taxRate" |
| | | readonly |
| | | placeholder="è¯·éæ©" |
| | | @click="openTaxRatePicker(idx)" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showTaxRatePicker = true"></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | <!-- å«ç¨åä»· --> |
| | | <up-form-item label="å«ç¨åä»·(å
)" |
| | | prop="taxInclusiveUnitPrice" |
| | | required> |
| | | <up-input v-model="product.taxInclusiveUnitPrice" |
| | | type="number" |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatTaxPrice(idx)" /> |
| | | </up-form-item> |
| | | <!-- æ°é --> |
| | | <up-form-item label="æ°é" |
| | | prop="quantity" |
| | | required> |
| | | <up-input v-model="product.quantity" |
| | | type="number" |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatAmount(idx)" /> |
| | | </up-form-item> |
| | | <!-- å«ç¨æ»ä»· --> |
| | | <up-form-item label="å«ç¨æ»ä»·(å
)" |
| | | prop="taxInclusiveTotalPrice" |
| | | required> |
| | | <up-input v-model="product.taxInclusiveTotalPrice" |
| | | type="number" |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatTaxTotal(idx)" /> |
| | | </up-form-item> |
| | | <!-- ä¸å«ç¨æ»ä»· --> |
| | | <up-form-item label="ä¸å«ç¨æ»ä»·(å
)" |
| | | prop="taxExclusiveTotalPrice" |
| | | required> |
| | | <up-input v-model="product.taxExclusiveTotalPrice" |
| | | type="number" |
| | | placeholder="请è¾å
¥" |
| | | @blur="formatNoTaxTotal(idx)" /> |
| | | </up-form-item> |
| | | <!-- å票类å --> |
| | | <up-form-item label="å票类å" |
| | | prop="invoiceType" |
| | | required> |
| | | <up-input v-model="product.invoiceType" |
| | | readonly |
| | | placeholder="è¯·éæ©" |
| | | @click="openInvoiceTypePicker(idx)" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showInvoiceTypePicker = true"></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </up-form> |
| | | <!-- 使ç¨å
Œ
±åºé¨æé®ç»ä»¶ --> |
| | | <FooterButtons :show="operationType !== 'view'" |
| | | cancelText="åæ¶" |
| | | confirmText="ä¿å" |
| | | @cancel="goBack" |
| | | @confirm="onSubmit" /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {onMounted, ref, computed} from 'vue'; |
| | | import {userListNoPage} from "@/api/system/user"; |
| | | import { formatDateToYMD } from '@/utils/ruoyi' |
| | | import { |
| | | addOrUpdateSalesLedger, |
| | | customerList, |
| | | getSalesLedgerWithProducts, |
| | | modelList, |
| | | productTreeList |
| | | } from "@/api/salesManagement/salesLedger"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import {calculateTaxExclusiveTotalPrice} from "@/utils/summarizeTable"; |
| | | import PageHeader from '@/components/PageHeader.vue'; |
| | | import FooterButtons from '@/components/FooterButtons.vue'; |
| | | import { onMounted, ref, computed } from "vue"; |
| | | import { userListNoPage } from "@/api/system/user"; |
| | | import { formatDateToYMD } from "@/utils/ruoyi"; |
| | | import { |
| | | addOrUpdateSalesLedger, |
| | | customerList, |
| | | getSalesLedgerWithProducts, |
| | | modelList, |
| | | productTreeList, |
| | | } from "@/api/salesManagement/salesLedger"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { calculateTaxExclusiveTotalPrice } from "@/utils/summarizeTable"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import FooterButtons from "@/components/FooterButtons.vue"; |
| | | |
| | | // è·å页é¢åæ° |
| | | const operationType = ref(''); |
| | | const editData = ref(null); |
| | | const formRef = ref(null); |
| | | // è·å页é¢åæ° |
| | | const operationType = ref(""); |
| | | const editData = ref(null); |
| | | const formRef = ref(null); |
| | | |
| | | const userStore = useUserStore() |
| | | const form = ref({ |
| | | id: '', |
| | | salesContractNo: '', |
| | | customerContractNo: '', |
| | | customerId: '', |
| | | customerName: '', |
| | | projectName: '', |
| | | executionDate: '', |
| | | paymentMethod: '', |
| | | entryPerson: '', |
| | | entryPersonName: '', |
| | | entryDate: '', |
| | | }); |
| | | const showPicker = ref(false); |
| | | const showDatePicker = ref(false); |
| | | 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, |
| | | speculativeTradingName: model.speculativeTradingName |
| | | })) |
| | | }) |
| | | |
| | | // ç¨çéæ©å表 |
| | | 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 showCategoryPicker = ref(false); |
| | | const showSpecificationPicker = ref(false); |
| | | const showTaxRatePicker = ref(false); |
| | | const showInvoiceTypePicker = ref(false); |
| | | // 鿩卿¾ç¤ºç¶æåéå·²å¨ä¸é¢å®ä¹ |
| | | |
| | | // 临æ¶åéå·²ä¸åéè¦ |
| | | const currentProductIndex = ref(0); |
| | | |
| | | // éé¡¹æ°æ® |
| | | const productOptions = ref([]); |
| | | const selectedCategoryNode = ref(null); |
| | | const defaultProps = ref({ |
| | | children: 'children', |
| | | label: 'label', |
| | | nodeKey: 'id' |
| | | }); |
| | | |
| | | const modelOptions = ref([]); |
| | | // 鲿¢å¾ªç¯è®¡ç®çæ å¿ |
| | | // const isCalculating = ref(false); |
| | | const taxRateOptions = ref([ |
| | | { text: '1', value: '1' }, |
| | | { text: '6', value: '6' }, |
| | | { text: '13', value: '13' }, |
| | | ]); |
| | | |
| | | const invoiceTypeOptions = ref([ |
| | | { text: '墿®ç¥¨', value: '墿®ç¥¨' }, |
| | | { text: 'å¢ä¸ç¥¨', value: 'å¢ä¸ç¥¨' }, |
| | | ]); |
| | | |
| | | // è¡¨åæ ¡éªè§å |
| | | const rules = { |
| | | salesman: [ |
| | | { required: true, message: 'è¯·éæ©ä¸å¡å', trigger: 'change' } |
| | | ], |
| | | customerContractNo: [ |
| | | { required: true, message: '请è¾å
¥å®¢æ·ååå·', trigger: 'blur' } |
| | | ], |
| | | customerName: [ |
| | | { required: true, message: 'è¯·éæ©å®¢æ·åç§°', trigger: 'change' } |
| | | ], |
| | | projectName: [ |
| | | { required: true, message: '请è¾å
¥é¡¹ç®åç§°', trigger: 'blur' } |
| | | ], |
| | | executionDate: [ |
| | | { required: true, message: 'è¯·éæ©ç¾è®¢æ¥æ', trigger: 'change' } |
| | | ] |
| | | }; |
| | | |
| | | |
| | | |
| | | const addProduct = () => { |
| | | if (productData.value === null) { |
| | | productData.value = [] |
| | | } |
| | | productData.value.push({ |
| | | productCategory: '', |
| | | specificationModel: '', |
| | | productModelId: '', |
| | | unit: '', |
| | | speculativeTradingName: '', |
| | | taxRate: '', |
| | | taxInclusiveUnitPrice: '', |
| | | quantity: '', |
| | | taxInclusiveTotalPrice: '', |
| | | taxExclusiveTotalPrice: '', |
| | | invoiceType: '' |
| | | const userStore = useUserStore(); |
| | | const form = ref({ |
| | | id: "", |
| | | salesContractNo: "", |
| | | customerContractNo: "", |
| | | customerId: "", |
| | | customerName: "", |
| | | projectName: "", |
| | | executionDate: "", |
| | | paymentMethod: "", |
| | | entryPerson: "", |
| | | entryPersonName: "", |
| | | entryDate: "", |
| | | }); |
| | | }; |
| | | // ä¸å¡åéæ©äºä»¶ |
| | | const onSalesmanSelect = (item) => { |
| | | form.value.salesman = item.name |
| | | } |
| | | |
| | | // æ¥æç¡®è®¤äºä»¶ |
| | | const onDateConfirm = (e) => { |
| | | form.value.executionDate = formatDateToYMD(e.value) |
| | | // ä¿æpickerDateValue为æ¶é´æ³æ ¼å¼ï¼è䏿¯æ ¼å¼åçå符串 |
| | | pickerDateValue.value = 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); |
| | | }; |
| | | |
| | | // æ¾ç¤ºéæ©å¨ |
| | | const openCategoryPicker = (idx) => { |
| | | currentProductIndex.value = idx; |
| | | showCategoryPicker.value = true; |
| | | }; |
| | | |
| | | const openSpecificationPicker = (idx) => { |
| | | currentProductIndex.value = idx; |
| | | showSpecificationPicker.value = true; |
| | | }; |
| | | |
| | | const openTaxRatePicker = (idx) => { |
| | | currentProductIndex.value = idx; |
| | | showTaxRatePicker.value = true; |
| | | }; |
| | | |
| | | const openInvoiceTypePicker = (idx) => { |
| | | currentProductIndex.value = idx; |
| | | showInvoiceTypePicker.value = true; |
| | | }; |
| | | |
| | | // éæ©å¨ç¡®è®¤äºä»¶ |
| | | const onCategoryConfirm = (node) => { |
| | | // è·åéä¸çèç¹ä¿¡æ¯ |
| | | console.log('selected node---', node); |
| | | // åå¨éä¸çèç¹ï¼ç¨äºç¡®è®¤æ¶è·åæ°æ® |
| | | selectedCategoryNode.value = node; |
| | | }; |
| | | |
| | | // 确认产åå¤§ç±»éæ© |
| | | const confirmCategorySelection = () => { |
| | | if (selectedCategoryNode.value) { |
| | | // 设置éä¸ç产å大类 |
| | | productData.value[currentProductIndex.value].productCategory = selectedCategoryNode.value.label; |
| | | const id = selectedCategoryNode.value.id |
| | | // éç½®éä¸çèç¹ |
| | | selectedCategoryNode.value = null; |
| | | productData.value[currentProductIndex.value].specificationModel = '' |
| | | productData.value[currentProductIndex.value].productModelId = '' |
| | | getModels(id) |
| | | } |
| | | showCategoryPicker.value = false; |
| | | }; |
| | | // è·åè§æ ¼åå· |
| | | const getModels = (value) => { |
| | | modelList({ id: value }).then((res) => { |
| | | modelOptions.value = res.map(user => ({ |
| | | text: user.model, |
| | | value: user.id, |
| | | unit: user.unit, |
| | | speculativeTradingName: user.speculativeTradingName, |
| | | })); |
| | | }); |
| | | }; |
| | | // è§æ ¼åå·éæ©äºä»¶ |
| | | const onSpecificationSelect = (item) => { |
| | | console.log('selected item---', item); |
| | | productData.value[currentProductIndex.value].specificationModel = item.name |
| | | productData.value[currentProductIndex.value].productModelId = item.value |
| | | productData.value[currentProductIndex.value].unit = item.unit |
| | | productData.value[currentProductIndex.value].speculativeTradingName = item.speculativeTradingName |
| | | } |
| | | // ç¨çéæ©äºä»¶ |
| | | 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) |
| | | } |
| | | }; |
| | | |
| | | // å票类åéæ©äºä»¶ |
| | | const onInvoiceTypeSelect = (item) => { |
| | | productData.value[currentProductIndex.value].invoiceType = item.name |
| | | }; |
| | | |
| | | // æ ¼å¼å彿° - åºå®ä¸¤ä½å°æ° |
| | | const formatTaxPrice = (idx) => { |
| | | if (productData.value[idx].taxInclusiveUnitPrice) { |
| | | const value = parseFloat(productData.value[idx].taxInclusiveUnitPrice); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].taxInclusiveUnitPrice = value.toFixed(2); |
| | | const showPicker = ref(false); |
| | | const showDatePicker = ref(false); |
| | | 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 (!productData.value[idx].taxRate) { |
| | | uni.showToast({ |
| | | title: '请å
éæ©ç¨ç', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | const quantity = parseFloat(productData.value[idx].quantity); |
| | | const unitPrice = parseFloat(productData.value[idx].taxInclusiveUnitPrice); |
| | | |
| | | if (!quantity || quantity <= 0 || !unitPrice) { |
| | | return; |
| | | } |
| | | // 计ç®å«ç¨æ»ä»· |
| | | productData.value[idx].taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2); |
| | | |
| | | // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· |
| | | if (productData.value[idx].taxRate) { |
| | | productData.value[idx].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice( |
| | | productData.value[idx].taxInclusiveTotalPrice, |
| | | productData.value[idx].taxRate |
| | | ); |
| | | } |
| | | }; |
| | | // æ°éè¾å
¥æ¡å¤±ç¦ |
| | | const formatAmount = (idx) => { |
| | | if (productData.value[idx].quantity) { |
| | | const value = parseFloat(productData.value[idx].quantity); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].quantity = value.toFixed(2); |
| | | if (type === "month") { |
| | | return `${value}`; |
| | | } |
| | | } |
| | | if (!productData.value[idx].taxRate) { |
| | | uni.showToast({ |
| | | title: '请å
éæ©ç¨ç', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | const quantity = parseFloat(productData.value[idx].quantity); |
| | | const unitPrice = parseFloat(productData.value[idx].taxInclusiveUnitPrice); |
| | | |
| | | if (!quantity || quantity <= 0 || !unitPrice) { |
| | | return; |
| | | } |
| | | // 计ç®å«ç¨æ»ä»· |
| | | productData.value[idx].taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2); |
| | | // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· |
| | | if (productData.value[idx].taxRate) { |
| | | productData.value[idx].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice( |
| | | productData.value[idx].taxInclusiveTotalPrice, |
| | | productData.value[idx].taxRate |
| | | ); |
| | | } |
| | | }; |
| | | // å«ç¨æ»ä»·å¤±ç¦ï¼æ ¹æ®å«ç¨æ»ä»·è®¡ç®å«ç¨åä»·åæ°é |
| | | const formatTaxTotal = (idx) => { |
| | | if (productData.value[idx].taxInclusiveTotalPrice) { |
| | | const value = parseFloat(productData.value[idx].taxInclusiveTotalPrice); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].taxInclusiveTotalPrice = value.toFixed(2); |
| | | if (type === "day") { |
| | | return `${value}`; |
| | | } |
| | | } |
| | | const totalPrice = parseFloat(productData.value[idx].taxInclusiveTotalPrice); |
| | | const quantity = parseFloat(productData.value[idx].quantity); |
| | | |
| | | if (!totalPrice || !quantity || quantity <= 0) { |
| | | return; |
| | | } |
| | | // 计ç®å«ç¨åä»· = å«ç¨æ»ä»· / æ°é |
| | | productData.value[idx].taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2); |
| | | // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· |
| | | if (productData.value[idx].taxRate) { |
| | | productData.value[idx].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice( |
| | | totalPrice, |
| | | productData.value[idx].taxRate |
| | | ); |
| | | } |
| | | }; |
| | | // ä¸å«ç¨æ»ä»·å¤±ç¦, æ ¹æ®ä¸å«ç¨æ»ä»·è®¡ç®å«ç¨åä»·åæ°é |
| | | const formatNoTaxTotal = (idx) => { |
| | | if (productData.value[idx].taxExclusiveTotalPrice) { |
| | | const value = parseFloat(productData.value[idx].taxExclusiveTotalPrice); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].taxExclusiveTotalPrice = value.toFixed(2); |
| | | } |
| | | } |
| | | if (!productData.value[idx].taxRate) { |
| | | uni.showToast({ |
| | | title: '请å
éæ©ç¨ç', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | const exclusiveTotalPrice = parseFloat(productData.value[idx].taxExclusiveTotalPrice); |
| | | const quantity = parseFloat(productData.value[idx].quantity); |
| | | const taxRate = parseFloat(productData.value[idx].taxRate); |
| | | if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) { |
| | | return; |
| | | } |
| | | // å
计ç®å«ç¨æ»ä»· = ä¸å«ç¨æ»ä»· / (1 - ç¨ç/100) |
| | | const taxRateDecimal = taxRate / 100; |
| | | const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal); |
| | | productData.value[idx].taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2); |
| | | // 计ç®å«ç¨åä»· = å«ç¨æ»ä»· / æ°é |
| | | productData.value[idx].taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(2); |
| | | }; |
| | | const goBack = () => { |
| | | // æ¸
çæ¬å°åå¨çæ°æ® |
| | | uni.removeStorageSync('operationType'); |
| | | uni.removeStorageSync('editData'); |
| | | uni.navigateBack(); |
| | | }; |
| | | 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; |
| | | } |
| | | |
| | | // æ£æ¥æ¯ä¸ªäº§åæ¯å¦å¡«å宿´ |
| | | for (let i = 0; i < productData.value.length; i++) { |
| | | const product = productData.value[i]; |
| | | // ä¼åæ°ååæ®µéªè¯ï¼å¤çå¯è½çåç¬¦ä¸²æ ¼å¼æ°å¼ |
| | | const taxInclusiveUnitPrice = parseFloat(product.taxInclusiveUnitPrice); |
| | | const quantity = parseFloat(product.quantity); |
| | | const taxInclusiveTotalPrice = parseFloat(product.taxInclusiveTotalPrice); |
| | | const taxExclusiveTotalPrice = parseFloat(product.taxExclusiveTotalPrice); |
| | | |
| | | if (!product.productCategory) { |
| | | uni.showToast({ |
| | | title: `产å${i + 1}ï¼è¯·éæ©äº§å大类`, |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | if (!product.specificationModel) { |
| | | uni.showToast({ |
| | | title: `产å${i + 1}ï¼è¯·éæ©è§æ ¼åå·`, |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | if (!product.unit) { |
| | | uni.showToast({ |
| | | title: `产å${i + 1}ï¼è¯·è¾å
¥åä½`, |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | if (!product.taxRate) { |
| | | uni.showToast({ |
| | | title: `产å${i + 1}ï¼è¯·éæ©ç¨ç`, |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | if (isNaN(taxInclusiveUnitPrice) || taxInclusiveUnitPrice <= 0) { |
| | | uni.showToast({ |
| | | title: `产å${i + 1}ï¼è¯·è¾å
¥ææçå«ç¨åä»·`, |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | if (isNaN(quantity) || quantity <= 0) { |
| | | uni.showToast({ |
| | | title: `产å${i + 1}ï¼è¯·è¾å
¥ææçæ°é`, |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | if (isNaN(taxInclusiveTotalPrice) || taxInclusiveTotalPrice <= 0) { |
| | | uni.showToast({ |
| | | title: `产å${i + 1}ï¼è¯·è¾å
¥ææçå«ç¨æ»ä»·`, |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | if (isNaN(taxExclusiveTotalPrice) || taxExclusiveTotalPrice <= 0) { |
| | | uni.showToast({ |
| | | title: `产å${i + 1}ï¼è¯·è¾å
¥ææçä¸å«ç¨æ»ä»·`, |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | if (!product.invoiceType) { |
| | | uni.showToast({ |
| | | title: `产å${i + 1}ï¼è¯·éæ©å票类å`, |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // è¡¨åæ ¡éªéè¿ï¼æäº¤æ°æ® |
| | | form.value.productData = JSON.parse(JSON.stringify(productData.value)); |
| | | form.value.type = 1; |
| | | addOrUpdateSalesLedger(form.value).then((res) => { |
| | | uni.showToast({ |
| | | title: 'æäº¤æå', |
| | | icon: 'success', |
| | | }); |
| | | goBack(); |
| | | }); |
| | | }; |
| | | const setUserInfo = () => { |
| | | form.value.entryPerson = userStore.id; |
| | | form.value.entryPersonName = userStore.nickName; |
| | | // 设置å½å¤©æ¥æ |
| | | const today = new Date() |
| | | const year = today.getFullYear() |
| | | const month = String(today.getMonth() + 1).padStart(2, '0') |
| | | const day = String(today.getDate()).padStart(2, '0') |
| | | form.value.entryDate = `${year}-${month}-${day}` |
| | | // è®¾ç½®æ¥æéæ©å¨é»è®¤å¼ä¸ºä»å¤© |
| | | pickerDateValue.value = `${year}-${month}-${day}` |
| | | } |
| | | // å¡«å
è¡¨åæ°æ®ï¼ç¼è¾æ¨¡å¼ï¼ |
| | | const fillFormData = () => { |
| | | if (!editData.value) return; |
| | | getSalesLedgerWithProducts({ id: editData.value.id, type: 1 }).then((res) => { |
| | | productData.value = res.productData; |
| | | }); |
| | | console.log(editData.value) |
| | | // å¡«å
åºæ¬ä¿¡æ¯ |
| | | form.value.salesContractNo = editData.value.salesContractNo || ''; |
| | | form.value.customerContractNo = editData.value.customerContractNo || ''; |
| | | form.value.customerName = editData.value.customerName || ''; |
| | | form.value.projectName = editData.value.projectName || ''; |
| | | form.value.executionDate = editData.value.executionDate || ''; |
| | | form.value.paymentMethod = editData.value.paymentMethod || ''; |
| | | form.value.salesman = editData.value.salesman || ''; |
| | | form.value.entryPerson = editData.value.entryPerson || ''; |
| | | form.value.entryPersonName = editData.value.entryPersonName || ''; |
| | | form.value.entryDate = editData.value.entryDate || ''; |
| | | form.value.id = editData.value.id || ''; |
| | | form.value.customerId = editData.value.customerId || ''; |
| | | |
| | | // è®¾ç½®æ¥æéæ©å¨çå¼ |
| | | if (editData.value.executionDate) { |
| | | pickerDateValue.value = editData.value.executionDate |
| | | } |
| | | }; |
| | | const getUserList = () => { |
| | | userListNoPage().then((res) => { |
| | | // ç§»é¤å¤ä½çæ°ç»å
è£
|
| | | userList.value = res.data.map(user => ({ |
| | | text: user.nickName, |
| | | value: user.nickName |
| | | })); |
| | | }) |
| | | }; |
| | | const getCustomerList = () => { |
| | | customerList().then((res) => { |
| | | // ç§»é¤å¤ä½çæ°ç»å
è£
|
| | | customerOption.value = res.map(item => ({ |
| | | text: item.customerName, |
| | | value: item.id |
| | | })); |
| | | }) |
| | | }; |
| | | const convertIdToValue = (data) => { |
| | | // å¦æä¼ å
¥ç䏿¯æ°ç»ï¼åè¿å空æ°ç» |
| | | if (!Array.isArray(data)) { |
| | | return []; |
| | | } |
| | | // é彿 å°å½æ° |
| | | return data.map(item => { |
| | | // å建æ°å¯¹è±¡ï¼æ å°å段 |
| | | const mappedItem = { |
| | | label: item.label, // å
³é®ï¼å° label æ å°ä¸º text |
| | | id: item.id, // ä¿ç id |
| | | }; |
| | | // 妿åå¨ children æ°ç»ï¼åéå½å¤ç |
| | | if (item.children && Array.isArray(item.children) && item.children.length > 0) { |
| | | mappedItem.children = convertIdToValue(item.children); |
| | | } |
| | | return mappedItem; |
| | | }); |
| | | }; |
| | | // è·å产å大类treeæ°æ® |
| | | const getProductOptions = () => { |
| | | productTreeList().then((res) => { |
| | | productOptions.value = convertIdToValue(res); |
| | | }); |
| | | }; |
| | | return value; |
| | | }; |
| | | const customerActionList = computed(() => { |
| | | return customerOption.value.map(customer => ({ |
| | | name: customer.text, |
| | | value: customer.value, |
| | | })); |
| | | }); |
| | | |
| | | // æ¥æéæ©å表已移é¤ï¼æ¹ç¨ up-datetime-picker |
| | | |
| | | onMounted(() => { |
| | | // è·å页é¢åæ° |
| | | operationType.value = uni.getStorageSync('operationType') || ''; |
| | | |
| | | // è·å人åå表 |
| | | getUserList() |
| | | // è·å客æ·å表 |
| | | getCustomerList() |
| | | // è·å产å大类treeæ°æ® |
| | | getProductOptions() |
| | | // èµå¼é»è®¤ä¿¡æ¯ |
| | | if (operationType.value === 'add') { |
| | | setUserInfo() |
| | | } |
| | | |
| | | // è·åç¼è¾æ°æ®å¹¶å¡«å
表å |
| | | const editDataStr = uni.getStorageSync('editData'); |
| | | if (editDataStr) { |
| | | try { |
| | | editData.value = JSON.parse(editDataStr); |
| | | // 妿æ¯ç¼è¾æ¨¡å¼ï¼çå¾
æ°æ®å è½½å®æåå¡«å
è¡¨åæ°æ® |
| | | if (operationType.value !== 'add' && editData.value) { |
| | | // ä½¿ç¨ nextTick ç¡®ä¿æ°æ®å è½½å®æååå¡«å
|
| | | setTimeout(() => { |
| | | fillFormData(); |
| | | }, 100); |
| | | } |
| | | } catch (error) { |
| | | console.error('è§£æç¼è¾æ°æ®å¤±è´¥:', error); |
| | | } |
| | | } |
| | | }); |
| | | // 产åå¤§ç±»éæ©å表 |
| | | 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, |
| | | speculativeTradingName: model.speculativeTradingName, |
| | | })); |
| | | }); |
| | | |
| | | // ç¨çéæ©å表 |
| | | 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 showCategoryPicker = ref(false); |
| | | const showSpecificationPicker = ref(false); |
| | | const showTaxRatePicker = ref(false); |
| | | const showInvoiceTypePicker = ref(false); |
| | | // 鿩卿¾ç¤ºç¶æåéå·²å¨ä¸é¢å®ä¹ |
| | | |
| | | // 临æ¶åéå·²ä¸åéè¦ |
| | | const currentProductIndex = ref(0); |
| | | |
| | | // éé¡¹æ°æ® |
| | | const productOptions = ref([]); |
| | | const selectedCategoryNode = ref(null); |
| | | const defaultProps = ref({ |
| | | children: "children", |
| | | label: "label", |
| | | nodeKey: "id", |
| | | }); |
| | | |
| | | const modelOptions = ref([]); |
| | | // 鲿¢å¾ªç¯è®¡ç®çæ å¿ |
| | | // const isCalculating = ref(false); |
| | | const taxRateOptions = ref([ |
| | | { text: "1", value: "1" }, |
| | | { text: "6", value: "6" }, |
| | | { text: "13", value: "13" }, |
| | | ]); |
| | | |
| | | const invoiceTypeOptions = ref([ |
| | | { text: "墿®ç¥¨", value: "墿®ç¥¨" }, |
| | | { text: "å¢ä¸ç¥¨", value: "å¢ä¸ç¥¨" }, |
| | | ]); |
| | | |
| | | // è¡¨åæ ¡éªè§å |
| | | const rules = { |
| | | salesman: [{ required: true, message: "è¯·éæ©ä¸å¡å", trigger: "change" }], |
| | | customerContractNo: [ |
| | | { required: true, message: "请è¾å
¥å®¢æ·ååå·", trigger: "blur" }, |
| | | ], |
| | | customerName: [ |
| | | { required: true, message: "è¯·éæ©å®¢æ·åç§°", trigger: "change" }, |
| | | ], |
| | | projectName: [{ required: true, message: "请è¾å
¥é¡¹ç®åç§°", trigger: "blur" }], |
| | | executionDate: [ |
| | | { required: true, message: "è¯·éæ©ç¾è®¢æ¥æ", trigger: "change" }, |
| | | ], |
| | | }; |
| | | |
| | | const addProduct = () => { |
| | | if (productData.value === null) { |
| | | productData.value = []; |
| | | } |
| | | productData.value.push({ |
| | | productCategory: "", |
| | | specificationModel: "", |
| | | productModelId: "", |
| | | unit: "", |
| | | speculativeTradingName: "", |
| | | taxRate: "", |
| | | taxInclusiveUnitPrice: "", |
| | | quantity: "", |
| | | taxInclusiveTotalPrice: "", |
| | | taxExclusiveTotalPrice: "", |
| | | invoiceType: "", |
| | | }); |
| | | }; |
| | | // ä¸å¡åéæ©äºä»¶ |
| | | const onSalesmanSelect = item => { |
| | | form.value.salesman = item.name; |
| | | }; |
| | | |
| | | // æ¥æç¡®è®¤äºä»¶ |
| | | const onDateConfirm = e => { |
| | | form.value.executionDate = formatDateToYMD(e.value); |
| | | // ä¿æpickerDateValue为æ¶é´æ³æ ¼å¼ï¼è䏿¯æ ¼å¼åçå符串 |
| | | pickerDateValue.value = 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); |
| | | }; |
| | | |
| | | // æ¾ç¤ºéæ©å¨ |
| | | const openCategoryPicker = idx => { |
| | | currentProductIndex.value = idx; |
| | | showCategoryPicker.value = true; |
| | | }; |
| | | |
| | | const openSpecificationPicker = idx => { |
| | | currentProductIndex.value = idx; |
| | | showSpecificationPicker.value = true; |
| | | }; |
| | | |
| | | const openTaxRatePicker = idx => { |
| | | currentProductIndex.value = idx; |
| | | showTaxRatePicker.value = true; |
| | | }; |
| | | |
| | | const openInvoiceTypePicker = idx => { |
| | | currentProductIndex.value = idx; |
| | | showInvoiceTypePicker.value = true; |
| | | }; |
| | | |
| | | // éæ©å¨ç¡®è®¤äºä»¶ |
| | | const onCategoryConfirm = node => { |
| | | // è·åéä¸çèç¹ä¿¡æ¯ |
| | | console.log("selected node---", node); |
| | | // åå¨éä¸çèç¹ï¼ç¨äºç¡®è®¤æ¶è·åæ°æ® |
| | | selectedCategoryNode.value = node; |
| | | }; |
| | | |
| | | // 确认产åå¤§ç±»éæ© |
| | | const confirmCategorySelection = () => { |
| | | if (selectedCategoryNode.value) { |
| | | // 设置éä¸ç产å大类 |
| | | productData.value[currentProductIndex.value].productCategory = |
| | | selectedCategoryNode.value.label; |
| | | const id = selectedCategoryNode.value.id; |
| | | // éç½®éä¸çèç¹ |
| | | selectedCategoryNode.value = null; |
| | | productData.value[currentProductIndex.value].specificationModel = ""; |
| | | productData.value[currentProductIndex.value].productModelId = ""; |
| | | getModels(id); |
| | | } |
| | | showCategoryPicker.value = false; |
| | | }; |
| | | // è·åè§æ ¼åå· |
| | | const getModels = value => { |
| | | modelList({ id: value }).then(res => { |
| | | modelOptions.value = res.map(user => ({ |
| | | text: user.model, |
| | | value: user.id, |
| | | unit: user.unit, |
| | | speculativeTradingName: user.speculativeTradingName, |
| | | })); |
| | | }); |
| | | }; |
| | | // è§æ ¼åå·éæ©äºä»¶ |
| | | const onSpecificationSelect = item => { |
| | | console.log("selected item---", item); |
| | | productData.value[currentProductIndex.value].specificationModel = item.name; |
| | | productData.value[currentProductIndex.value].productModelId = item.value; |
| | | productData.value[currentProductIndex.value].unit = item.unit; |
| | | productData.value[currentProductIndex.value].speculativeTradingName = |
| | | item.speculativeTradingName; |
| | | }; |
| | | // ç¨çéæ©äºä»¶ |
| | | 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); |
| | | } |
| | | }; |
| | | |
| | | // å票类åéæ©äºä»¶ |
| | | const onInvoiceTypeSelect = item => { |
| | | productData.value[currentProductIndex.value].invoiceType = item.name; |
| | | }; |
| | | |
| | | // æ ¼å¼å彿° - åºå®ä¸¤ä½å°æ° |
| | | const formatTaxPrice = idx => { |
| | | if (productData.value[idx].taxInclusiveUnitPrice) { |
| | | const value = parseFloat(productData.value[idx].taxInclusiveUnitPrice); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].taxInclusiveUnitPrice = value.toFixed(2); |
| | | } |
| | | } |
| | | if (!productData.value[idx].taxRate) { |
| | | uni.showToast({ |
| | | title: "请å
éæ©ç¨ç", |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | const quantity = parseFloat(productData.value[idx].quantity); |
| | | const unitPrice = parseFloat(productData.value[idx].taxInclusiveUnitPrice); |
| | | |
| | | if (!quantity || quantity <= 0 || !unitPrice) { |
| | | return; |
| | | } |
| | | // 计ç®å«ç¨æ»ä»· |
| | | productData.value[idx].taxInclusiveTotalPrice = ( |
| | | unitPrice * quantity |
| | | ).toFixed(2); |
| | | |
| | | // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· |
| | | if (productData.value[idx].taxRate) { |
| | | productData.value[idx].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice( |
| | | productData.value[idx].taxInclusiveTotalPrice, |
| | | productData.value[idx].taxRate |
| | | ); |
| | | } |
| | | }; |
| | | // æ°éè¾å
¥æ¡å¤±ç¦ |
| | | const formatAmount = idx => { |
| | | if (productData.value[idx].quantity) { |
| | | const value = parseFloat(productData.value[idx].quantity); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].quantity = value.toFixed(2); |
| | | } |
| | | } |
| | | if (!productData.value[idx].taxRate) { |
| | | uni.showToast({ |
| | | title: "请å
éæ©ç¨ç", |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | const quantity = parseFloat(productData.value[idx].quantity); |
| | | const unitPrice = parseFloat(productData.value[idx].taxInclusiveUnitPrice); |
| | | |
| | | if (!quantity || quantity <= 0 || !unitPrice) { |
| | | return; |
| | | } |
| | | // 计ç®å«ç¨æ»ä»· |
| | | productData.value[idx].taxInclusiveTotalPrice = ( |
| | | unitPrice * quantity |
| | | ).toFixed(2); |
| | | // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· |
| | | if (productData.value[idx].taxRate) { |
| | | productData.value[idx].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice( |
| | | productData.value[idx].taxInclusiveTotalPrice, |
| | | productData.value[idx].taxRate |
| | | ); |
| | | } |
| | | }; |
| | | // å«ç¨æ»ä»·å¤±ç¦ï¼æ ¹æ®å«ç¨æ»ä»·è®¡ç®å«ç¨åä»·åæ°é |
| | | const formatTaxTotal = idx => { |
| | | if (productData.value[idx].taxInclusiveTotalPrice) { |
| | | const value = parseFloat(productData.value[idx].taxInclusiveTotalPrice); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].taxInclusiveTotalPrice = value.toFixed(2); |
| | | } |
| | | } |
| | | const totalPrice = parseFloat(productData.value[idx].taxInclusiveTotalPrice); |
| | | const quantity = parseFloat(productData.value[idx].quantity); |
| | | |
| | | if (!totalPrice || !quantity || quantity <= 0) { |
| | | return; |
| | | } |
| | | // 计ç®å«ç¨åä»· = å«ç¨æ»ä»· / æ°é |
| | | productData.value[idx].taxInclusiveUnitPrice = ( |
| | | totalPrice / quantity |
| | | ).toFixed(2); |
| | | // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· |
| | | if (productData.value[idx].taxRate) { |
| | | productData.value[idx].taxExclusiveTotalPrice = |
| | | calculateTaxExclusiveTotalPrice( |
| | | totalPrice, |
| | | productData.value[idx].taxRate |
| | | ); |
| | | } |
| | | }; |
| | | // ä¸å«ç¨æ»ä»·å¤±ç¦, æ ¹æ®ä¸å«ç¨æ»ä»·è®¡ç®å«ç¨åä»·åæ°é |
| | | const formatNoTaxTotal = idx => { |
| | | if (productData.value[idx].taxExclusiveTotalPrice) { |
| | | const value = parseFloat(productData.value[idx].taxExclusiveTotalPrice); |
| | | if (!isNaN(value)) { |
| | | productData.value[idx].taxExclusiveTotalPrice = value.toFixed(2); |
| | | } |
| | | } |
| | | if (!productData.value[idx].taxRate) { |
| | | uni.showToast({ |
| | | title: "请å
éæ©ç¨ç", |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | const exclusiveTotalPrice = parseFloat( |
| | | productData.value[idx].taxExclusiveTotalPrice |
| | | ); |
| | | const quantity = parseFloat(productData.value[idx].quantity); |
| | | const taxRate = parseFloat(productData.value[idx].taxRate); |
| | | if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) { |
| | | return; |
| | | } |
| | | // å
计ç®å«ç¨æ»ä»· = ä¸å«ç¨æ»ä»· / (1 - ç¨ç/100) |
| | | const taxRateDecimal = taxRate / 100; |
| | | const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal); |
| | | productData.value[idx].taxInclusiveTotalPrice = |
| | | inclusiveTotalPrice.toFixed(2); |
| | | // 计ç®å«ç¨åä»· = å«ç¨æ»ä»· / æ°é |
| | | productData.value[idx].taxInclusiveUnitPrice = ( |
| | | inclusiveTotalPrice / quantity |
| | | ).toFixed(2); |
| | | }; |
| | | const goBack = () => { |
| | | // æ¸
çæ¬å°åå¨çæ°æ® |
| | | uni.removeStorageSync("operationType"); |
| | | uni.removeStorageSync("editData"); |
| | | uni.navigateBack(); |
| | | }; |
| | | 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; |
| | | } |
| | | |
| | | // æ£æ¥æ¯ä¸ªäº§åæ¯å¦å¡«å宿´ |
| | | for (let i = 0; i < productData.value.length; i++) { |
| | | const product = productData.value[i]; |
| | | // ä¼åæ°ååæ®µéªè¯ï¼å¤çå¯è½çåç¬¦ä¸²æ ¼å¼æ°å¼ |
| | | const taxInclusiveUnitPrice = parseFloat(product.taxInclusiveUnitPrice); |
| | | const quantity = parseFloat(product.quantity); |
| | | const taxInclusiveTotalPrice = parseFloat(product.taxInclusiveTotalPrice); |
| | | const taxExclusiveTotalPrice = parseFloat(product.taxExclusiveTotalPrice); |
| | | |
| | | if (!product.productCategory) { |
| | | uni.showToast({ |
| | | title: `产å${i + 1}ï¼è¯·éæ©äº§å大类`, |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | if (!product.specificationModel) { |
| | | uni.showToast({ |
| | | title: `产å${i + 1}ï¼è¯·éæ©è§æ ¼åå·`, |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | if (!product.unit) { |
| | | uni.showToast({ |
| | | title: `产å${i + 1}ï¼è¯·è¾å
¥åä½`, |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | if (!product.taxRate) { |
| | | uni.showToast({ |
| | | title: `产å${i + 1}ï¼è¯·éæ©ç¨ç`, |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | if (isNaN(taxInclusiveUnitPrice) || taxInclusiveUnitPrice <= 0) { |
| | | uni.showToast({ |
| | | title: `产å${i + 1}ï¼è¯·è¾å
¥ææçå«ç¨åä»·`, |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | if (isNaN(quantity) || quantity <= 0) { |
| | | uni.showToast({ |
| | | title: `产å${i + 1}ï¼è¯·è¾å
¥ææçæ°é`, |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | if (isNaN(taxInclusiveTotalPrice) || taxInclusiveTotalPrice <= 0) { |
| | | uni.showToast({ |
| | | title: `产å${i + 1}ï¼è¯·è¾å
¥ææçå«ç¨æ»ä»·`, |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | if (isNaN(taxExclusiveTotalPrice) || taxExclusiveTotalPrice <= 0) { |
| | | uni.showToast({ |
| | | title: `产å${i + 1}ï¼è¯·è¾å
¥ææçä¸å«ç¨æ»ä»·`, |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | if (!product.invoiceType) { |
| | | uni.showToast({ |
| | | title: `产å${i + 1}ï¼è¯·éæ©å票类å`, |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // è¡¨åæ ¡éªéè¿ï¼æäº¤æ°æ® |
| | | form.value.productData = JSON.parse(JSON.stringify(productData.value)); |
| | | form.value.type = 1; |
| | | addOrUpdateSalesLedger(form.value).then(res => { |
| | | uni.showToast({ |
| | | title: "æäº¤æå", |
| | | icon: "success", |
| | | }); |
| | | goBack(); |
| | | }); |
| | | }; |
| | | const setUserInfo = () => { |
| | | form.value.entryPerson = userStore.id; |
| | | form.value.entryPersonName = userStore.nickName; |
| | | // 设置å½å¤©æ¥æ |
| | | const today = new Date(); |
| | | const year = today.getFullYear(); |
| | | const month = String(today.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(today.getDate()).padStart(2, "0"); |
| | | form.value.entryDate = `${year}-${month}-${day}`; |
| | | // è®¾ç½®æ¥æéæ©å¨é»è®¤å¼ä¸ºä»å¤© |
| | | pickerDateValue.value = `${year}-${month}-${day}`; |
| | | }; |
| | | // å¡«å
è¡¨åæ°æ®ï¼ç¼è¾æ¨¡å¼ï¼ |
| | | const fillFormData = () => { |
| | | if (!editData.value) return; |
| | | getSalesLedgerWithProducts({ id: editData.value.id, type: 1 }).then(res => { |
| | | productData.value = res.productData; |
| | | }); |
| | | console.log(editData.value); |
| | | // å¡«å
åºæ¬ä¿¡æ¯ |
| | | form.value.salesContractNo = editData.value.salesContractNo || ""; |
| | | form.value.customerContractNo = editData.value.customerContractNo || ""; |
| | | form.value.customerName = editData.value.customerName || ""; |
| | | form.value.projectName = editData.value.projectName || ""; |
| | | form.value.executionDate = editData.value.executionDate || ""; |
| | | form.value.paymentMethod = editData.value.paymentMethod || ""; |
| | | form.value.salesman = editData.value.salesman || ""; |
| | | form.value.entryPerson = editData.value.entryPerson || ""; |
| | | form.value.entryPersonName = editData.value.entryPersonName || ""; |
| | | form.value.entryDate = editData.value.entryDate || ""; |
| | | form.value.id = editData.value.id || ""; |
| | | form.value.customerId = editData.value.customerId || ""; |
| | | |
| | | // è®¾ç½®æ¥æéæ©å¨çå¼ |
| | | if (editData.value.executionDate) { |
| | | pickerDateValue.value = editData.value.executionDate; |
| | | } |
| | | }; |
| | | const getUserList = () => { |
| | | userListNoPage().then(res => { |
| | | // ç§»é¤å¤ä½çæ°ç»å
è£
|
| | | userList.value = res.data.map(user => ({ |
| | | text: user.nickName, |
| | | value: user.nickName, |
| | | })); |
| | | }); |
| | | }; |
| | | const getCustomerList = () => { |
| | | customerList().then(res => { |
| | | // ç§»é¤å¤ä½çæ°ç»å
è£
|
| | | customerOption.value = res.map(item => ({ |
| | | text: item.customerName, |
| | | value: item.id, |
| | | })); |
| | | }); |
| | | }; |
| | | const convertIdToValue = data => { |
| | | // å¦æä¼ å
¥ç䏿¯æ°ç»ï¼åè¿å空æ°ç» |
| | | if (!Array.isArray(data)) { |
| | | return []; |
| | | } |
| | | // é彿 å°å½æ° |
| | | return data.map(item => { |
| | | // å建æ°å¯¹è±¡ï¼æ å°å段 |
| | | const mappedItem = { |
| | | label: item.label, // å
³é®ï¼å° label æ å°ä¸º text |
| | | id: item.id, // ä¿ç id |
| | | }; |
| | | // 妿åå¨ children æ°ç»ï¼åéå½å¤ç |
| | | if ( |
| | | item.children && |
| | | Array.isArray(item.children) && |
| | | item.children.length > 0 |
| | | ) { |
| | | mappedItem.children = convertIdToValue(item.children); |
| | | } |
| | | return mappedItem; |
| | | }); |
| | | }; |
| | | // è·å产å大类treeæ°æ® |
| | | const getProductOptions = () => { |
| | | productTreeList().then(res => { |
| | | productOptions.value = convertIdToValue(res); |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | // è·å页é¢åæ° |
| | | operationType.value = uni.getStorageSync("operationType") || ""; |
| | | |
| | | // è·å人åå表 |
| | | getUserList(); |
| | | // è·å客æ·å表 |
| | | getCustomerList(); |
| | | // è·å产å大类treeæ°æ® |
| | | getProductOptions(); |
| | | // èµå¼é»è®¤ä¿¡æ¯ |
| | | if (operationType.value === "add") { |
| | | setUserInfo(); |
| | | } |
| | | |
| | | // è·åç¼è¾æ°æ®å¹¶å¡«å
表å |
| | | const editDataStr = uni.getStorageSync("editData"); |
| | | if (editDataStr) { |
| | | try { |
| | | editData.value = JSON.parse(editDataStr); |
| | | // 妿æ¯ç¼è¾æ¨¡å¼ï¼çå¾
æ°æ®å è½½å®æåå¡«å
è¡¨åæ°æ® |
| | | if (operationType.value !== "add" && editData.value) { |
| | | // ä½¿ç¨ nextTick ç¡®ä¿æ°æ®å è½½å®æååå¡«å
|
| | | setTimeout(() => { |
| | | fillFormData(); |
| | | }, 100); |
| | | } |
| | | } catch (error) { |
| | | console.error("è§£æç¼è¾æ°æ®å¤±è´¥:", error); |
| | | } |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | @import '@/static/scss/form-common.scss'; |
| | | @import "@/static/scss/form-common.scss"; |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="account-detail"> |
| | | <PageHeader title="åè´§" |
| | | @back="goBack" /> |
| | | <!-- 表ååºå --> |
| | | <u-form ref="formRef" |
| | | @submit="submitForm" |
| | | :rules="rules" |
| | | :model="form" |
| | | label-width="140rpx"> |
| | | <u-form-item prop="typeValue" |
| | | label="åè´§ç±»å" |
| | | required> |
| | | <u-input v-model="typeValue" |
| | | readonly |
| | | placeholder="è¯·éæ©åè´§æ¹å¼" |
| | | @click="showPicker = true" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showPicker = true"></up-icon> |
| | | </template> |
| | | </u-form-item> |
| | | </u-form> |
| | | <!-- éæ©å¨å¼¹çª --> |
| | | <up-action-sheet :show="showPicker" |
| | | :actions="productOptions" |
| | | title="åè´§æ¹å¼" |
| | | @select="onConfirm" |
| | | @close="showPicker = false" /> |
| | | <!-- å®¡æ ¸æµç¨åºå --> |
| | | <view class="approval-process"> |
| | | <view class="approval-header"> |
| | | <text class="approval-title">å®¡æ ¸æµç¨</text> |
| | | <text class="approval-desc">æ¯ä¸ªæ¥éª¤åªè½éæ©ä¸ä¸ªå®¡æ¹äºº</text> |
| | | </view> |
| | | <view class="approval-steps"> |
| | | <view v-for="(step, stepIndex) in approverNodes" |
| | | :key="stepIndex" |
| | | class="approval-step"> |
| | | <view class="step-dot"></view> |
| | | <view class="step-title"> |
| | | <text>审æ¹äºº</text> |
| | | </view> |
| | | <view class="approver-container"> |
| | | <view v-if="step.nickName" |
| | | class="approver-item"> |
| | | <view class="approver-avatar"> |
| | | <text class="avatar-text">{{ step.nickName.charAt(0) }}</text> |
| | | <view class="status-dot"></view> |
| | | </view> |
| | | <view class="approver-info"> |
| | | <text class="approver-name">{{ step.nickName }}</text> |
| | | </view> |
| | | <view class="delete-approver-btn" |
| | | @click="removeApprover(stepIndex)">Ã</view> |
| | | </view> |
| | | <view v-else |
| | | class="add-approver-btn" |
| | | @click="addApprover(stepIndex)"> |
| | | <view class="add-circle">+</view> |
| | | <text class="add-label">鿩审æ¹äºº</text> |
| | | </view> |
| | | </view> |
| | | <view class="step-line" |
| | | v-if="stepIndex < approverNodes.length - 1"></view> |
| | | <view class="delete-step-btn" |
| | | v-if="approverNodes.length > 1" |
| | | @click="removeApprovalStep(stepIndex)">å é¤èç¹</view> |
| | | </view> |
| | | </view> |
| | | <view class="add-step-btn"> |
| | | <u-button icon="plus" |
| | | plain |
| | | type="primary" |
| | | style="width: 100%" |
| | | @click="addApprovalStep">æ°å¢èç¹</u-button> |
| | | </view> |
| | | </view> |
| | | <!-- åºé¨æé® --> |
| | | <view class="footer-btns"> |
| | | <u-button class="cancel-btn" |
| | | @click="goBack">åæ¶</u-button> |
| | | <u-button class="save-btn" |
| | | @click="submitForm">åè´§</u-button> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import { addShippingInfo } from "@/api/salesManagement/salesLedger"; |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | import { userListNoPageByTenantId } from "@/api/system/user"; |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | approveTime: "", |
| | | approveId: "", |
| | | approveUser: "", |
| | | approveUserName: "", |
| | | approveDeptName: "", |
| | | approveDeptId: "", |
| | | approveReason: "", |
| | | checkResult: "", |
| | | tempFileIds: [], |
| | | approverList: [], // æ°å¢å段ï¼å卿æèç¹ç审æ¹äººid |
| | | startDate: "", |
| | | endDate: "", |
| | | location: "", |
| | | price: "", |
| | | }, |
| | | rules: { |
| | | typeValue: [{ required: false, message: "è¯·éæ©", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | const showPicker = ref(false); |
| | | const productOptions = ref([ |
| | | { |
| | | value: "货车", |
| | | name: "货车", |
| | | }, |
| | | { |
| | | value: "å¿«é", |
| | | name: "å¿«é", |
| | | }, |
| | | ]); |
| | | const operationType = ref(""); |
| | | const currentApproveStatus = ref(""); |
| | | const approverNodes = ref([]); |
| | | const userList = ref([]); |
| | | const formRef = ref(null); |
| | | const approveType = ref(0); |
| | | const goOutData = ref({}); |
| | | onMounted(async () => { |
| | | try { |
| | | userListNoPageByTenantId().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | // 仿¬å°åå¨è·åå货详æ
|
| | | goOutData.value = JSON.parse(uni.getStorageSync("goOutData")); |
| | | console.log(goOutData.value, "goOutData.value"); |
| | | |
| | | // åå§åå®¡æ¹æµç¨èç¹ï¼é»è®¤ä¸ä¸ªèç¹ |
| | | approverNodes.value = [{ id: 1, userId: null }]; |
| | | |
| | | // çå¬èç³»äººéæ©äºä»¶ |
| | | uni.$on("selectContact", handleSelectContact); |
| | | } catch (error) { |
| | | console.error("è·å失败:", error); |
| | | } |
| | | }); |
| | | |
| | | onUnmounted(() => { |
| | | // ç§»é¤äºä»¶çå¬ |
| | | uni.$off("selectContact", handleSelectContact); |
| | | }); |
| | | const typeValue = ref("货车"); |
| | | const onConfirm = item => { |
| | | // 设置éä¸çé¨é¨ |
| | | typeValue.value = item.name; |
| | | showPicker.value = false; |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | // æ¸
餿¬å°åå¨çæ°æ® |
| | | uni.removeStorageSync("operationType"); |
| | | uni.removeStorageSync("invoiceLedgerEditRow"); |
| | | uni.removeStorageSync("approveType"); |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | // æ£æ¥æ¯ä¸ªå®¡æ¹æ¥éª¤æ¯å¦é½æå®¡æ¹äºº |
| | | const hasEmptyStep = approverNodes.value.some(step => !step.nickName); |
| | | if (hasEmptyStep) { |
| | | showToast("请为æ¯ä¸ªå®¡æ¹æ¥éª¤éæ©å®¡æ¹äºº"); |
| | | return; |
| | | } |
| | | formRef.value |
| | | .validate() |
| | | .then(valid => { |
| | | if (valid) { |
| | | // è¡¨åæ ¡éªéè¿ï¼å¯ä»¥æäº¤æ°æ® |
| | | // æ¶éææèç¹ç审æ¹äººid |
| | | console.log("approverNodes---", approverNodes.value); |
| | | const approveUserIds = approverNodes.value |
| | | .map(node => node.userId) |
| | | .join(","); |
| | | const params = { |
| | | salesLedgerId: goOutData.value.salesLedgerId, |
| | | salesLedgerProductId: goOutData.value.id, |
| | | type: typeValue.value, |
| | | approveUserIds, |
| | | }; |
| | | console.log(params, "params"); |
| | | |
| | | addShippingInfo(params).then(res => { |
| | | showToast("åè´§æå"); |
| | | setTimeout(() => { |
| | | goBack(); |
| | | }, 500); |
| | | }); |
| | | } |
| | | }) |
| | | .catch(error => { |
| | | console.error("è¡¨åæ ¡éªå¤±è´¥:", error); |
| | | // å°è¯è·åå
·ä½çéè¯¯åæ®µ |
| | | if (error && error.errors) { |
| | | const firstError = error.errors[0]; |
| | | if (firstError) { |
| | | uni.showToast({ |
| | | title: firstError.message || "è¡¨åæ ¡éªå¤±è´¥ï¼è¯·æ£æ¥å¿
填项", |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | } |
| | | // æ¾ç¤ºéç¨éè¯¯ä¿¡æ¯ |
| | | uni.showToast({ |
| | | title: "è¡¨åæ ¡éªå¤±è´¥ï¼è¯·æ£æ¥å¿
填项", |
| | | icon: "none", |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | // å¤çèç³»äººéæ©ç»æ |
| | | const handleSelectContact = data => { |
| | | const { stepIndex, contact } = data; |
| | | // å°éä¸çè系人设置为对åºå®¡æ¹æ¥éª¤ç审æ¹äºº |
| | | approverNodes.value[stepIndex].userId = contact.userId; |
| | | approverNodes.value[stepIndex].nickName = contact.nickName; |
| | | }; |
| | | |
| | | const addApprover = stepIndex => { |
| | | // 跳转å°èç³»äººéæ©é¡µé¢ |
| | | uni.setStorageSync("stepIndex", stepIndex); |
| | | uni.navigateTo({ |
| | | url: "/pages/cooperativeOffice/collaborativeApproval/contactSelect", |
| | | }); |
| | | }; |
| | | |
| | | const addApprovalStep = () => { |
| | | // æ·»å æ°çå®¡æ¹æ¥éª¤ |
| | | approverNodes.value.push({ userId: null, nickName: null }); |
| | | }; |
| | | |
| | | const removeApprover = stepIndex => { |
| | | // ç§»é¤å®¡æ¹äºº |
| | | approverNodes.value[stepIndex].userId = null; |
| | | approverNodes.value[stepIndex].nickName = null; |
| | | }; |
| | | |
| | | const removeApprovalStep = stepIndex => { |
| | | // ç¡®ä¿è³å°ä¿çä¸ä¸ªå®¡æ¹æ¥éª¤ |
| | | if (approverNodes.value.length > 1) { |
| | | approverNodes.value.splice(stepIndex, 1); |
| | | } else { |
| | | uni.showToast({ |
| | | title: "è³å°éè¦ä¸ä¸ªå®¡æ¹æ¥éª¤", |
| | | icon: "none", |
| | | }); |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "@/static/scss/form-common.scss"; |
| | | |
| | | .approval-process { |
| | | background: #fff; |
| | | margin: 16px; |
| | | border-radius: 16px; |
| | | padding: 16px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); |
| | | } |
| | | |
| | | .approval-header { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .approval-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | display: block; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .approval-desc { |
| | | font-size: 12px; |
| | | color: #999; |
| | | } |
| | | |
| | | /* æ ·å¼å¢å¼ºä¸ºâç®æ´å°åå飿 ¼â */ |
| | | .approval-steps { |
| | | padding-left: 22px; |
| | | position: relative; |
| | | |
| | | &::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 11px; |
| | | top: 40px; |
| | | bottom: 40px; |
| | | width: 2px; |
| | | background: linear-gradient( |
| | | to bottom, |
| | | #e6f7ff 0%, |
| | | #bae7ff 50%, |
| | | #91d5ff 100% |
| | | ); |
| | | border-radius: 1px; |
| | | } |
| | | } |
| | | |
| | | .approval-step { |
| | | position: relative; |
| | | margin-bottom: 24px; |
| | | |
| | | &::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: -18px; |
| | | top: 14px; // ä» 8px è°æ´ä¸º 14pxï¼ä¸æåä¸å¿å¯¹é½ |
| | | width: 12px; |
| | | height: 12px; |
| | | background: #fff; |
| | | border: 3px solid #006cfb; |
| | | border-radius: 50%; |
| | | z-index: 2; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | } |
| | | |
| | | .step-title { |
| | | top: 12px; |
| | | margin-bottom: 12px; |
| | | position: relative; |
| | | margin-left: 6px; |
| | | } |
| | | |
| | | .step-title text { |
| | | font-size: 14px; |
| | | color: #666; |
| | | background: #f0f0f0; |
| | | padding: 4px 12px; |
| | | border-radius: 12px; |
| | | position: relative; |
| | | line-height: 1.4; // ç¡®ä¿æåè¡é«ä¸è´ |
| | | } |
| | | |
| | | .approver-item { |
| | | display: flex; |
| | | align-items: center; |
| | | background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%); |
| | | border-radius: 16px; |
| | | padding: 16px; |
| | | gap: 12px; |
| | | position: relative; |
| | | border: 1px solid #e6f7ff; |
| | | box-shadow: 0 4px 12px rgba(0, 108, 251, 0.08); |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .approver-avatar { |
| | | width: 48px; |
| | | height: 48px; |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | position: relative; |
| | | box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3); |
| | | } |
| | | |
| | | .avatar-text { |
| | | color: #fff; |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .approver-info { |
| | | flex: 1; |
| | | position: relative; |
| | | } |
| | | |
| | | .approver-name { |
| | | display: block; |
| | | font-size: 16px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | position: relative; |
| | | } |
| | | |
| | | .approver-dept { |
| | | font-size: 12px; |
| | | color: #999; |
| | | background: rgba(0, 108, 251, 0.05); |
| | | padding: 2px 8px; |
| | | border-radius: 8px; |
| | | display: inline-block; |
| | | position: relative; |
| | | |
| | | &::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 4px; |
| | | top: 50%; |
| | | transform: translateY(-50%); |
| | | width: 2px; |
| | | height: 2px; |
| | | background: #006cfb; |
| | | border-radius: 50%; |
| | | } |
| | | } |
| | | |
| | | .delete-approver-btn { |
| | | font-size: 16px; |
| | | color: #ff4d4f; |
| | | background: linear-gradient( |
| | | 135deg, |
| | | rgba(255, 77, 79, 0.1) 0%, |
| | | rgba(255, 77, 79, 0.05) 100% |
| | | ); |
| | | width: 28px; |
| | | height: 28px; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | transition: all 0.3s ease; |
| | | position: relative; |
| | | } |
| | | |
| | | .add-approver-btn { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background: linear-gradient(135deg, #f0f8ff 0%, #e6f7ff 100%); |
| | | border: 2px dashed #006cfb; |
| | | border-radius: 16px; |
| | | padding: 20px; |
| | | color: #006cfb; |
| | | font-size: 14px; |
| | | position: relative; |
| | | transition: all 0.3s ease; |
| | | |
| | | &::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 50%; |
| | | top: 50%; |
| | | transform: translate(-50%, -50%); |
| | | width: 32px; |
| | | height: 32px; |
| | | border: 2px solid #006cfb; |
| | | border-radius: 50%; |
| | | opacity: 0; |
| | | transition: all 0.3s ease; |
| | | } |
| | | } |
| | | |
| | | .delete-step-btn { |
| | | color: #ff4d4f; |
| | | font-size: 12px; |
| | | background: linear-gradient( |
| | | 135deg, |
| | | rgba(255, 77, 79, 0.1) 0%, |
| | | rgba(255, 77, 79, 0.05) 100% |
| | | ); |
| | | padding: 6px 12px; |
| | | border-radius: 12px; |
| | | display: inline-block; |
| | | position: relative; |
| | | transition: all 0.3s ease; |
| | | |
| | | &::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 6px; |
| | | top: 50%; |
| | | transform: translateY(-50%); |
| | | width: 4px; |
| | | height: 4px; |
| | | background: #ff4d4f; |
| | | border-radius: 50%; |
| | | } |
| | | } |
| | | |
| | | .step-line { |
| | | display: none; // éè忥ç线æ¡ï¼ä½¿ç¨ä¼ªå
ç´ ä»£æ¿ |
| | | } |
| | | |
| | | .add-step-btn { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | .footer-btns { |
| | | position: fixed; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: #fff; |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | padding: 0.75rem 0; |
| | | box-shadow: 0 -0.125rem 0.5rem rgba(0, 0, 0, 0.05); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | .cancel-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #ffffff; |
| | | width: 6.375rem; |
| | | background: #c7c9cc; |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3, 88, 185, 0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | .save-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #ffffff; |
| | | width: 14rem; |
| | | background: linear-gradient(140deg, #00baff 0%, #006cfb 100%); |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3, 88, 185, 0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | // å¨ç»å®ä¹ |
| | | @keyframes pulse { |
| | | 0% { |
| | | transform: scale(1); |
| | | opacity: 1; |
| | | } |
| | | 50% { |
| | | transform: scale(1.2); |
| | | opacity: 0.7; |
| | | } |
| | | 100% { |
| | | transform: scale(1); |
| | | opacity: 1; |
| | | } |
| | | } |
| | | |
| | | @keyframes rotate { |
| | | 0% { |
| | | transform: rotate(0deg); |
| | | } |
| | | 100% { |
| | | transform: rotate(360deg); |
| | | } |
| | | } |
| | | |
| | | @keyframes ripple { |
| | | 0% { |
| | | transform: translate(-50%, -50%) scale(0.8); |
| | | opacity: 1; |
| | | } |
| | | 100% { |
| | | transform: translate(-50%, -50%) scale(1.6); |
| | | opacity: 0; |
| | | } |
| | | } |
| | | |
| | | /* 妿已æ .step-lineï¼è¿éæ´ç²¾åå®ä½å°å·¦ä¾§ä¸å°åç¹å¯¹é½ */ |
| | | .step-line { |
| | | position: absolute; |
| | | left: 4px; |
| | | top: 48px; |
| | | width: 2px; |
| | | height: calc(100% - 48px); |
| | | background: #e5e7eb; |
| | | } |
| | | |
| | | .approver-container { |
| | | display: flex; |
| | | align-items: center; |
| | | background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%); |
| | | border-radius: 16px; |
| | | gap: 12px; |
| | | padding: 10px 0; |
| | | background: transparent; |
| | | border: none; |
| | | box-shadow: none; |
| | | } |
| | | |
| | | .approver-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | padding: 8px 10px; |
| | | background: transparent; |
| | | border: none; |
| | | box-shadow: none; |
| | | border-radius: 0; |
| | | } |
| | | |
| | | .approver-avatar { |
| | | position: relative; |
| | | width: 40px; |
| | | height: 40px; |
| | | border-radius: 50%; |
| | | background: #f3f4f6; |
| | | border: 2px solid #e5e7eb; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | animation: none; /* ç¦ç¨æè½¬çå¨ç»ï¼åå½ç®æ´ */ |
| | | } |
| | | |
| | | .avatar-text { |
| | | font-size: 14px; |
| | | color: #374151; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .add-approver-btn { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | background: transparent; |
| | | border: none; |
| | | box-shadow: none; |
| | | padding: 0; |
| | | } |
| | | |
| | | .add-approver-btn .add-circle { |
| | | width: 40px; |
| | | height: 40px; |
| | | border: 2px dashed #a0aec0; |
| | | border-radius: 50%; |
| | | color: #6b7280; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 22px; |
| | | line-height: 1; |
| | | } |
| | | |
| | | .add-approver-btn .add-label { |
| | | color: #3b82f6; |
| | | font-size: 14px; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <view class="sales-account"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="éå®å°è´¦" @back="goBack" /> |
| | | |
| | | <!-- æç´¢åçéåºå --> |
| | | <view class="search-section"> |
| | | <view class="search-bar"> |
| | | <view class="search-input"> |
| | | <up-input |
| | | class="search-text" |
| | | placeholder="请è¾å
¥éå®ååå·æç´¢" |
| | | v-model="salesContractNo" |
| | | @change="getList" |
| | | clearable |
| | | /> |
| | | </view> |
| | | <view class="filter-button" @click="getList"> |
| | | <up-icon name="search" size="24" color="#999"></up-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- éå®å°è´¦ç叿µ --> |
| | | <view class="ledger-list" v-if="ledgerList.length > 0"> |
| | | <view v-for="(item, index) in ledgerList" :key="index"> |
| | | <view class="ledger-item" @click="handleInfo('edit', item)"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <view class="document-icon"> |
| | | <up-icon name="file-text" size="16" color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="item-id">{{ item.salesContractNo }}</text> |
| | | </view> |
| | | <!-- <view class="item-tag">--> |
| | | <!-- <text class="tag-text">{{ item.recorder }}</text>--> |
| | | <!-- </view>--> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">客æ·åç§°</text> |
| | | <text class="detail-value">{{ item.customerName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">客æ·ååå·</text> |
| | | <text class="detail-value">{{ item.customerContractNo }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ä¸å¡å</text> |
| | | <text class="detail-value">{{ item.salesman }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">项ç®åç§°</text> |
| | | <text class="detail-value">{{ item.projectName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">仿¬¾æ¹å¼</text> |
| | | <text class="detail-value">{{ item.paymentMethod }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ååéé¢(å
)</text> |
| | | <text class="detail-value highlight">{{ item.contractAmount }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç¾è®¢æ¥æ</text> |
| | | <text class="detail-value">{{ item.executionDate }}</text> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="detail-info"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å½å
¥äºº</text> |
| | | <text class="detail-value">{{ item.entryPersonName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å½å
¥æ¥æ</text> |
| | | <text class="detail-value">{{ item.entryDate }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else class="no-data"> |
| | | <text>ææ éå®å°è´¦æ°æ®</text> |
| | | </view> |
| | | |
| | | <!-- æµ®å¨æä½æé® --> |
| | | <view class="fab-button" @click="handleInfo('add')"> |
| | | <up-icon name="plus" size="24" color="#ffffff"></up-icon> |
| | | </view> |
| | | </view> |
| | | <view class="sales-account"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="éå®å°è´¦" |
| | | @back="goBack" /> |
| | | <!-- æç´¢åçéåºå --> |
| | | <view class="search-section"> |
| | | <view class="search-bar"> |
| | | <view class="search-input"> |
| | | <up-input class="search-text" |
| | | placeholder="请è¾å
¥éå®ååå·æç´¢" |
| | | v-model="salesContractNo" |
| | | @change="getList" |
| | | clearable /> |
| | | </view> |
| | | <view class="filter-button" |
| | | @click="getList"> |
| | | <up-icon name="search" |
| | | size="24" |
| | | color="#999"></up-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- éå®å°è´¦ç叿µ --> |
| | | <view class="ledger-list" |
| | | v-if="ledgerList.length > 0"> |
| | | <view v-for="(item, index) in ledgerList" |
| | | :key="index"> |
| | | <view class="ledger-item" |
| | | @click="handleInfo('edit', item)"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <view class="document-icon"> |
| | | <up-icon name="file-text" |
| | | size="16" |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="item-id">{{ item.salesContractNo }}</text> |
| | | </view> |
| | | <!-- <view class="item-tag">--> |
| | | <!-- <text class="tag-text">{{ item.recorder }}</text>--> |
| | | <!-- </view>--> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">客æ·åç§°</text> |
| | | <text class="detail-value">{{ item.customerName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">客æ·ååå·</text> |
| | | <text class="detail-value">{{ item.customerContractNo }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ä¸å¡å</text> |
| | | <text class="detail-value">{{ item.salesman }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">项ç®åç§°</text> |
| | | <text class="detail-value">{{ item.projectName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">仿¬¾æ¹å¼</text> |
| | | <text class="detail-value">{{ item.paymentMethod }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ååéé¢(å
)</text> |
| | | <text class="detail-value highlight">{{ item.contractAmount }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç¾è®¢æ¥æ</text> |
| | | <text class="detail-value">{{ item.executionDate }}</text> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="detail-info"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å½å
¥äºº</text> |
| | | <text class="detail-value">{{ item.entryPersonName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å½å
¥æ¥æ</text> |
| | | <text class="detail-value">{{ item.entryDate }}</text> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <u-button class="detail-button" |
| | | size="small" |
| | | type="primary" |
| | | @click="openOut(item)"> |
| | | åè´§ç¶æ |
| | | </u-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else |
| | | class="no-data"> |
| | | <text>ææ éå®å°è´¦æ°æ®</text> |
| | | </view> |
| | | <!-- æµ®å¨æä½æé® --> |
| | | <view class="fab-button" |
| | | @click="handleInfo('add')"> |
| | | <up-icon name="plus" |
| | | size="24" |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from 'vue'; |
| | | import { onShow } from '@dcloudio/uni-app'; |
| | | import {ledgerListPage} from "@/api/salesManagement/salesLedger"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | const userStore = useUserStore() |
| | | const showLoadingToast = (message) => { |
| | | uni.showLoading({ |
| | | title: message, |
| | | mask: true |
| | | }) |
| | | } |
| | | const closeToast = () => { |
| | | uni.hideLoading() |
| | | } |
| | | import { ref } from "vue"; |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import { ledgerListPage } from "@/api/salesManagement/salesLedger"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | const userStore = useUserStore(); |
| | | const showLoadingToast = message => { |
| | | uni.showLoading({ |
| | | title: message, |
| | | mask: true, |
| | | }); |
| | | }; |
| | | const closeToast = () => { |
| | | uni.hideLoading(); |
| | | }; |
| | | |
| | | // æç´¢å
³é®è¯ |
| | | const salesContractNo = ref(''); |
| | | // æç´¢å
³é®è¯ |
| | | const salesContractNo = ref(""); |
| | | |
| | | // éå®å°è´¦æ°æ® |
| | | const ledgerList = ref([]); |
| | | // éå®å°è´¦æ°æ® |
| | | const ledgerList = ref([]); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | // æ¥è¯¢å表 |
| | | const getList = () => { |
| | | showLoadingToast('å è½½ä¸...') |
| | | const page = { |
| | | current: -1, |
| | | size: -1 |
| | | } |
| | | ledgerListPage({...page, salesContractNo: salesContractNo.value}).then((res) => { |
| | | console.log('éå®å°è´¦----', res); |
| | | ledgerList.value = res.records; |
| | | closeToast() |
| | | }).catch(() => { |
| | | closeToast() |
| | | }); |
| | | }; |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | // æ¥è¯¢å表 |
| | | const getList = () => { |
| | | showLoadingToast("å è½½ä¸..."); |
| | | const page = { |
| | | current: -1, |
| | | size: -1, |
| | | }; |
| | | ledgerListPage({ ...page, salesContractNo: salesContractNo.value }) |
| | | .then(res => { |
| | | console.log("éå®å°è´¦----", res); |
| | | ledgerList.value = res.records; |
| | | closeToast(); |
| | | }) |
| | | .catch(() => { |
| | | closeToast(); |
| | | }); |
| | | }; |
| | | const openOut = item => { |
| | | uni.setStorageSync("outData", JSON.stringify(item)); |
| | | uni.navigateTo({ |
| | | url: "/pages/sales/salesAccount/out", |
| | | }); |
| | | }; |
| | | // å¤çå°è´¦ä¿¡æ¯æä½ï¼æ¥ç/ç¼è¾/æ°å¢ï¼ |
| | | const handleInfo = (type, row) => { |
| | | try { |
| | | // 设置æä½ç±»å |
| | | uni.setStorageSync("operationType", type); |
| | | |
| | | // å¤çå°è´¦ä¿¡æ¯æä½ï¼æ¥ç/ç¼è¾/æ°å¢ï¼ |
| | | const handleInfo = (type, row) => { |
| | | try { |
| | | // 设置æä½ç±»å |
| | | uni.setStorageSync('operationType', type); |
| | | |
| | | // å¦ææ¯æ¥çæç¼è¾æä½ |
| | | if (type !== 'add') { |
| | | // éªè¯è¡æ°æ®æ¯å¦åå¨ |
| | | if (!row) { |
| | | uni.showToast({ |
| | | title: 'æ°æ®ä¸åå¨', |
| | | icon: 'error' |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // æ£æ¥æéï¼åªæå½å
¥äººæè½ç¼è¾ |
| | | if (row.entryPerson != userStore.id) { |
| | | // éå½å
¥äººè·³è½¬å°åªè¯»è¯¦æ
é¡µé¢ |
| | | uni.setStorageSync('editData', JSON.stringify(row)); |
| | | uni.navigateTo({ |
| | | url: '/pages/sales/salesAccount/view' |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // å½å
¥äººç¼è¾ï¼å卿°æ®å¹¶è·³è½¬å°ç¼è¾é¡µé¢ |
| | | uni.setStorageSync('editData', JSON.stringify(row)); |
| | | uni.navigateTo({ |
| | | url: '/pages/sales/salesAccount/detail' |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // æ°å¢æä½ï¼ç´æ¥è·³è½¬å°ç¼è¾é¡µé¢ |
| | | uni.navigateTo({ |
| | | url: '/pages/sales/salesAccount/detail' |
| | | }); |
| | | |
| | | } catch (error) { |
| | | console.error('å¤çå°è´¦ä¿¡æ¯æä½å¤±è´¥:', error); |
| | | uni.showToast({ |
| | | title: 'æä½å¤±è´¥ï¼è¯·éè¯', |
| | | icon: 'error' |
| | | }); |
| | | } |
| | | }; |
| | | // å¦ææ¯æ¥çæç¼è¾æä½ |
| | | if (type !== "add") { |
| | | // éªè¯è¡æ°æ®æ¯å¦åå¨ |
| | | if (!row) { |
| | | uni.showToast({ |
| | | title: "æ°æ®ä¸åå¨", |
| | | icon: "error", |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | onShow(() => { |
| | | // 页颿¾ç¤ºæ¶å·æ°å表 |
| | | getList(); |
| | | }); |
| | | // æ£æ¥æéï¼åªæå½å
¥äººæè½ç¼è¾ |
| | | if (row.entryPerson != userStore.id) { |
| | | // éå½å
¥äººè·³è½¬å°åªè¯»è¯¦æ
é¡µé¢ |
| | | uni.setStorageSync("editData", JSON.stringify(row)); |
| | | uni.navigateTo({ |
| | | url: "/pages/sales/salesAccount/view", |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // å½å
¥äººç¼è¾ï¼å卿°æ®å¹¶è·³è½¬å°ç¼è¾é¡µé¢ |
| | | uni.setStorageSync("editData", JSON.stringify(row)); |
| | | uni.navigateTo({ |
| | | url: "/pages/sales/salesAccount/detail", |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // æ°å¢æä½ï¼ç´æ¥è·³è½¬å°ç¼è¾é¡µé¢ |
| | | uni.navigateTo({ |
| | | url: "/pages/sales/salesAccount/detail", |
| | | }); |
| | | } catch (error) { |
| | | console.error("å¤çå°è´¦ä¿¡æ¯æä½å¤±è´¥:", error); |
| | | uni.showToast({ |
| | | title: "æä½å¤±è´¥ï¼è¯·éè¯", |
| | | icon: "error", |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | onShow(() => { |
| | | // 页颿¾ç¤ºæ¶å·æ°å表 |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import '@/styles/sales-common.scss'; |
| | | @import "@/styles/sales-common.scss"; |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="receipt-payment-detail"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="åè´§ç¶æ" |
| | | @back="goBack" /> |
| | | <!-- ç»è®¡ä¿¡æ¯ --> |
| | | <view class="summary-info"> |
| | | <view class="summary-item"> |
| | | <text class="summary-label">客æ·åç§°</text> |
| | | <text class="summary-value">{{ outData.customerName }}</text> |
| | | </view> |
| | | <view class="summary-item"> |
| | | <text class="summary-label">ååéé¢</text> |
| | | <text class="summary-value">{{ outData.contractAmount }}</text> |
| | | </view> |
| | | <view class="summary-item"> |
| | | <text class="summary-label">ç¾è®¢æ¥æ</text> |
| | | <text class="summary-value">{{ outData.executionDate }}</text> |
| | | </view> |
| | | </view> |
| | | <!-- 忬¾è®°å½æç»å表 --> |
| | | <view class="detail-list" |
| | | v-if="tableData.length > 0"> |
| | | <view v-for="(item, index) in tableData" |
| | | :key="index" |
| | | class="detail-item"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <view class="record-icon"> |
| | | <up-icon name="file-text" |
| | | size="16" |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="item-index">{{ item.productCategory }}</text> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">产å大类</text> |
| | | <text class="detail-value">{{ item.productCategory }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">è§æ ¼åå·</text> |
| | | <text class="detail-value">{{ item.specificationModel }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åä½</text> |
| | | <text class="detail-value">{{ item.unit }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">产åç¶æ</text> |
| | | <text v-if="item.approveStatus === 1" |
| | | class="detail-value highlight">å
è¶³</text> |
| | | <text v-else |
| | | class="detail-value danger">ä¸è¶³</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åè´§ç¶æ</text> |
| | | <u-tag size="mini" |
| | | :type="getShippingStatusType(item)">{{ getShippingStatusText(item) }}</u-tag> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å¿«éå
¬å¸</text> |
| | | <text class="detail-value">{{ item.expressCompany }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å¿«éåå·</text> |
| | | <text class="detail-value">{{ item.expressNumber }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å货车ç</text> |
| | | <u-tag size="mini" |
| | | v-if="item.shippingCarNumber" |
| | | type="success">{{ item.shippingCarNumber }}</u-tag> |
| | | <u-tag v-else |
| | | size="mini" |
| | | type="info">-</u-tag> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åè´§æ¥æ</text> |
| | | <text class="detail-value">{{ item.shippingDate || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ°é</text> |
| | | <text class="detail-value">{{ item.quantity }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç¨çï¼%ï¼</text> |
| | | <text class="detail-value">{{ item.taxRate }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å«ç¨åä»·ï¼å
ï¼</text> |
| | | <text class="detail-value">{{ item.taxInclusiveUnitPrice }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å«ç¨æ»ä»·ï¼å
ï¼</text> |
| | | <text class="detail-value">{{ item.taxInclusiveTotalPrice }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ä¸å«ç¨æ»ä»·ï¼å
ï¼</text> |
| | | <text class="detail-value">{{ item.taxExclusiveTotalPrice }}</text> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <u-button class="detail-button" |
| | | size="small" |
| | | type="primary" |
| | | :disabled="!canShip(item)" |
| | | @click="goout(item)"> |
| | | åè´§ |
| | | </u-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else |
| | | class="no-data"> |
| | | <text>ææ åæ¬¾è®°å½</text> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted } from "vue"; |
| | | import { productList } from "@/api/salesManagement/salesLedger"; |
| | | |
| | | // 客æ·ä¿¡æ¯ |
| | | const supplierId = ref(""); |
| | | |
| | | // è¡¨æ ¼æ°æ® |
| | | const tableData = ref([]); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.removeStorageSync("supplierId"); |
| | | uni.navigateBack(); |
| | | }; |
| | | const getShippingStatusType = row => { |
| | | // 妿已åè´§ï¼æåè´§æ¥ææè½¦çå·ï¼ï¼æ¾ç¤ºç»¿è² |
| | | if (row.shippingDate || row.shippingCarNumber) { |
| | | return "success"; |
| | | } |
| | | |
| | | // è·ååè´§ç¶æåæ®µ |
| | | const status = row.shippingStatus; |
| | | |
| | | // å¦æç¶æä¸ºç©ºææªå®ä¹ï¼é»è®¤ä¸ºç°è²ï¼å¾
åè´§ï¼ |
| | | if (status === null || status === undefined || status === "") { |
| | | return "info"; |
| | | } |
| | | |
| | | // ç¶ææ¯å符串 |
| | | const statusStr = String(status).trim(); |
| | | const typeTextMap = { |
| | | å¾
åè´§: "info", |
| | | å¾
å®¡æ ¸: "info", |
| | | å®¡æ ¸ä¸: "warning", |
| | | å®¡æ ¸æç»: "danger", |
| | | å®¡æ ¸éè¿: "success", |
| | | å·²åè´§: "success", |
| | | }; |
| | | return typeTextMap[statusStr] || "info"; |
| | | }; |
| | | const getShippingStatusText = row => { |
| | | // 妿已åè´§ï¼æåè´§æ¥ææè½¦çå·ï¼ï¼æ¾ç¤º"å·²åè´§" |
| | | if (row.shippingDate || row.shippingCarNumber) { |
| | | return "å·²åè´§"; |
| | | } |
| | | |
| | | // è·ååè´§ç¶æåæ®µ |
| | | const status = row.shippingStatus; |
| | | |
| | | // å¦æç¶æä¸ºç©ºææªå®ä¹ï¼é»è®¤ä¸º"å¾
åè´§" |
| | | if (status === null || status === undefined || status === "") { |
| | | return "å¾
åè´§"; |
| | | } |
| | | |
| | | // ç¶ææ¯å符串 |
| | | const statusStr = String(status).trim(); |
| | | const statusTextMap = { |
| | | å¾
åè´§: "å¾
åè´§", |
| | | å¾
å®¡æ ¸: "å¾
å®¡æ ¸", |
| | | å®¡æ ¸ä¸: "å®¡æ ¸ä¸", |
| | | å®¡æ ¸æç»: "å®¡æ ¸æç»", |
| | | å®¡æ ¸éè¿: "å®¡æ ¸éè¿", |
| | | å·²åè´§: "å·²åè´§", |
| | | }; |
| | | return statusTextMap[statusStr] || "å¾
åè´§"; |
| | | }; |
| | | // è·å页é¢åæ° |
| | | const getPageParams = () => { |
| | | // 仿¬å°åå¨è·åä¾åºåID |
| | | const storedSupplierId = uni.getStorageSync("supplierId"); |
| | | if (storedSupplierId) { |
| | | supplierId.value = storedSupplierId; |
| | | } |
| | | }; |
| | | const goout = item => { |
| | | uni.setStorageSync("goOutData", JSON.stringify(item)); |
| | | uni.navigateTo({ |
| | | url: "/pages/sales/salesAccount/goOut", |
| | | }); |
| | | }; |
| | | |
| | | // æ¥è¯¢å表 |
| | | const getList = () => { |
| | | showLoadingToast("å è½½ä¸..."); |
| | | productList({ |
| | | salesLedgerId: outData.value.id, |
| | | type: 1, |
| | | }) |
| | | .then(res => { |
| | | tableData.value = res.data; |
| | | closeToast(); |
| | | }) |
| | | .catch(() => { |
| | | closeToast(); |
| | | uni.showToast({ |
| | | title: "æ¥è¯¢å¤±è´¥", |
| | | icon: "error", |
| | | }); |
| | | }); |
| | | }; |
| | | const canShip = row => { |
| | | // 产åç¶æå¿
é¡»æ¯å
è¶³ï¼approveStatus === 1ï¼ |
| | | if (row.approveStatus !== 1) { |
| | | return false; |
| | | } |
| | | |
| | | // è·ååè´§ç¶æ |
| | | const shippingStatus = row.shippingStatus; |
| | | |
| | | // 妿已åè´§ï¼æåè´§æ¥ææè½¦çå·ï¼ï¼ä¸è½å次åè´§ |
| | | if (row.shippingDate || row.shippingCarNumber) { |
| | | return false; |
| | | } |
| | | |
| | | // åè´§ç¶æå¿
é¡»æ¯"å¾
åè´§"æ"å®¡æ ¸æç»" |
| | | const statusStr = shippingStatus ? String(shippingStatus).trim() : ""; |
| | | return statusStr === "å¾
åè´§" || statusStr === "å®¡æ ¸æç»"; |
| | | }; |
| | | |
| | | // æ¾ç¤ºå è½½æç¤º |
| | | const showLoadingToast = message => { |
| | | uni.showLoading({ |
| | | title: message, |
| | | mask: true, |
| | | }); |
| | | }; |
| | | |
| | | // å
³éæç¤º |
| | | const closeToast = () => { |
| | | uni.hideLoading(); |
| | | }; |
| | | const outData = ref({}); |
| | | |
| | | onMounted(() => { |
| | | // 页é¢å è½½æ¶è·ååæ°å¹¶å·æ°å表 |
| | | getPageParams(); |
| | | // 仿¬å°åå¨è·ååè´§ç¶ææ°æ® |
| | | outData.value = JSON.parse(uni.getStorageSync("outData")); |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .receipt-payment-detail { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | position: relative; |
| | | } |
| | | |
| | | .u-divider { |
| | | margin: 0 !important; |
| | | } |
| | | |
| | | .summary-info { |
| | | background: #ffffff; |
| | | margin: 20px 20px 0 20px; |
| | | border-radius: 12px; |
| | | padding: 16px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .summary-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 8px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | .summary-label { |
| | | font-size: 14px; |
| | | color: #666; |
| | | } |
| | | |
| | | .summary-value { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .summary-value.highlight { |
| | | color: #2979ff; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .summary-value.danger { |
| | | color: #ff4757; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .detail-list { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .detail-item { |
| | | background: #ffffff; |
| | | border-radius: 12px; |
| | | margin-bottom: 16px; |
| | | overflow: hidden; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | padding: 0 16px; |
| | | } |
| | | |
| | | .item-header { |
| | | padding: 10px 0; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .item-left { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .record-icon { |
| | | width: 24px; |
| | | height: 24px; |
| | | background: #2979ff; |
| | | border-radius: 4px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .item-index { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .item-date { |
| | | font-size: 12px; |
| | | color: #666; |
| | | } |
| | | |
| | | .item-details { |
| | | padding: 16px 0; |
| | | } |
| | | |
| | | .detail-row { |
| | | display: flex; |
| | | align-items: flex-end; |
| | | justify-content: space-between; |
| | | margin-bottom: 8px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | .detail-label { |
| | | font-size: 12px; |
| | | color: #777777; |
| | | min-width: 60px; |
| | | } |
| | | |
| | | .detail-value { |
| | | font-size: 12px; |
| | | color: #000000; |
| | | text-align: right; |
| | | flex: 1; |
| | | margin-left: 16px; |
| | | } |
| | | |
| | | .detail-value.highlight { |
| | | color: #2979ff; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .detail-value.danger { |
| | | color: #ff4757; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .no-data { |
| | | padding: 40px 0; |
| | | text-align: center; |
| | | color: #999; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <view class="account-view"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="å°è´¦è¯¦æ
" @back="goBack" /> |
| | | |
| | | <PageHeader title="å°è´¦è¯¦æ
" |
| | | @back="goBack" /> |
| | | <!-- åºæ¬ä¿¡æ¯å±ç¤º --> |
| | | <view class="info-section"> |
| | | <view class="section-title">åºæ¬ä¿¡æ¯</view> |
| | |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 产åä¿¡æ¯å±ç¤º --> |
| | | <view class="product-section" v-if="productData && productData.length > 0"> |
| | | <view class="product-section" |
| | | v-if="productData && productData.length > 0"> |
| | | <view class="section-title">产åä¿¡æ¯</view> |
| | | <view class="product-card" v-for="(product, idx) in productData" :key="idx"> |
| | | <view class="product-card" |
| | | v-for="(product, idx) in productData" |
| | | :key="idx"> |
| | | <view class="product-header"> |
| | | <view class="product-title"> |
| | | <!-- æ¿æ¢ van-icon 为 u-icon --> |
| | | <u-icon name="file-text" color="#2979ff" size="15" /> |
| | | <u-icon name="file-text" |
| | | color="#2979ff" |
| | | size="15" /> |
| | | <text class="product-productCategory">产å {{ idx + 1 }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="product-info"> |
| | | <view class="info-grid"> |
| | | <view class="info-item"> |
| | |
| | | <text class="info-label">è§æ ¼åå·</text> |
| | | <text class="info-value">{{ product.specificationModel }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <!-- <view class="info-item"> |
| | | <text class="info-label">ç»å®æºå¨</text> |
| | | <text class="info-value">{{ product.speculativeTradingName }}</text> |
| | | </view> |
| | | </view> --> |
| | | <view class="info-item"> |
| | | <text class="info-label">åä½</text> |
| | | <text class="info-value">{{ product.unit }}</text> |
| | |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-else class="no-product"> |
| | | <view v-else |
| | | class="no-product"> |
| | | <text>ææ äº§åä¿¡æ¯</text> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {onMounted, ref} from 'vue'; |
| | | import {getSalesLedgerWithProducts} from "@/api/salesManagement/salesLedger"; |
| | | import PageHeader from '@/components/PageHeader.vue'; |
| | | import { onMounted, ref } from "vue"; |
| | | import { getSalesLedgerWithProducts } from "@/api/salesManagement/salesLedger"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | |
| | | // è·å页é¢åæ° |
| | | const editData = ref(null); |
| | | // è·å页é¢åæ° |
| | | const editData = ref(null); |
| | | |
| | | const form = ref({ |
| | | id: '', |
| | | salesContractNo: '', |
| | | customerContractNo: '', |
| | | customerId: '', |
| | | customerName: '', |
| | | projectName: '', |
| | | executionDate: '', |
| | | paymentMethod: '', |
| | | entryPerson: '', |
| | | entryPersonName: '', |
| | | entryDate: '', |
| | | salesman: '' |
| | | }); |
| | | |
| | | // äº§åæ°æ® |
| | | const productData = ref([]); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | // æ¸
çæ¬å°åå¨çæ°æ® |
| | | uni.removeStorageSync('editData'); |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // å¡«å
è¡¨åæ°æ® |
| | | const fillFormData = () => { |
| | | if (!editData.value) return; |
| | | |
| | | // è·å宿´ç产åä¿¡æ¯ |
| | | getSalesLedgerWithProducts({ id: editData.value.id, type: 1 }).then((res) => { |
| | | productData.value = res.productData || []; |
| | | form.value = {...res} |
| | | const form = ref({ |
| | | id: "", |
| | | salesContractNo: "", |
| | | customerContractNo: "", |
| | | customerId: "", |
| | | customerName: "", |
| | | projectName: "", |
| | | executionDate: "", |
| | | paymentMethod: "", |
| | | entryPerson: "", |
| | | entryPersonName: "", |
| | | entryDate: "", |
| | | salesman: "", |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | // è·åç¼è¾æ°æ®å¹¶å¡«å
表å |
| | | const editDataStr = uni.getStorageSync('editData'); |
| | | if (editDataStr) { |
| | | try { |
| | | editData.value = JSON.parse(editDataStr); |
| | | // ä½¿ç¨ nextTick ç¡®ä¿æ°æ®å è½½å®æååå¡«å
|
| | | setTimeout(() => { |
| | | fillFormData(); |
| | | }, 100); |
| | | } catch (error) { |
| | | console.error('è§£æç¼è¾æ°æ®å¤±è´¥:', error); |
| | | // äº§åæ°æ® |
| | | const productData = ref([]); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | // æ¸
çæ¬å°åå¨çæ°æ® |
| | | uni.removeStorageSync("editData"); |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // å¡«å
è¡¨åæ°æ® |
| | | const fillFormData = () => { |
| | | if (!editData.value) return; |
| | | |
| | | // è·å宿´ç产åä¿¡æ¯ |
| | | getSalesLedgerWithProducts({ id: editData.value.id, type: 1 }).then(res => { |
| | | productData.value = res.productData || []; |
| | | form.value = { ...res }; |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | // è·åç¼è¾æ°æ®å¹¶å¡«å
表å |
| | | const editDataStr = uni.getStorageSync("editData"); |
| | | if (editDataStr) { |
| | | try { |
| | | editData.value = JSON.parse(editDataStr); |
| | | // ä½¿ç¨ nextTick ç¡®ä¿æ°æ®å è½½å®æååå¡«å
|
| | | setTimeout(() => { |
| | | fillFormData(); |
| | | }, 100); |
| | | } catch (error) { |
| | | console.error("è§£æç¼è¾æ°æ®å¤±è´¥:", error); |
| | | } |
| | | } |
| | | } |
| | | }); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .account-view { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 2rem; |
| | | } |
| | | .account-view { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 2rem; |
| | | } |
| | | |
| | | .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; |
| | | } |
| | | .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; |
| | | } |
| | | |
| | | .title { |
| | | flex: 1; |
| | | text-align: center; |
| | | font-size: 1.125rem; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | .title { |
| | | flex: 1; |
| | | text-align: center; |
| | | font-size: 1.125rem; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .info-section { |
| | | background: #fff; |
| | | margin: 1rem; |
| | | padding: 1rem; |
| | | border-radius: 0.5rem; |
| | | box-shadow: 0 0.125rem 0.5rem rgba(0,0,0,0.04); |
| | | } |
| | | .info-section { |
| | | background: #fff; |
| | | margin: 1rem; |
| | | padding: 1rem; |
| | | border-radius: 0.5rem; |
| | | box-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.04); |
| | | } |
| | | |
| | | .product-section { |
| | | background: #fff; |
| | | margin: 1rem; |
| | | padding: 1rem; |
| | | border-radius: 0.5rem; |
| | | box-shadow: 0 0.125rem 0.5rem rgba(0,0,0,0.04); |
| | | } |
| | | .product-section { |
| | | background: #fff; |
| | | margin: 1rem; |
| | | padding: 1rem; |
| | | border-radius: 0.5rem; |
| | | box-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.04); |
| | | } |
| | | |
| | | .section-title { |
| | | font-size: 1rem; |
| | | font-weight: 600; |
| | | color: #333; |
| | | margin-bottom: 1rem; |
| | | padding-bottom: 0.5rem; |
| | | border-bottom: 0.0625rem solid #e8e8e8; |
| | | } |
| | | .section-title { |
| | | font-size: 1rem; |
| | | font-weight: 600; |
| | | color: #333; |
| | | margin-bottom: 1rem; |
| | | padding-bottom: 0.5rem; |
| | | border-bottom: 0.0625rem solid #e8e8e8; |
| | | } |
| | | |
| | | .info-grid { |
| | | display: grid; |
| | | grid-template-columns: 1fr 1fr; |
| | | gap: 0.75rem; |
| | | } |
| | | .info-grid { |
| | | display: grid; |
| | | grid-template-columns: 1fr 1fr; |
| | | gap: 0.75rem; |
| | | } |
| | | |
| | | .info-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 0.25rem; |
| | | } |
| | | .info-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 0.25rem; |
| | | } |
| | | |
| | | .info-label { |
| | | font-size: 0.75rem; |
| | | color: #666; |
| | | font-weight: 400; |
| | | } |
| | | .info-label { |
| | | font-size: 0.75rem; |
| | | color: #666; |
| | | font-weight: 400; |
| | | } |
| | | |
| | | .info-value { |
| | | font-size: 0.875rem; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | .info-value { |
| | | font-size: 0.875rem; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .info-value.highlight { |
| | | color: #2979ff; |
| | | font-weight: 600; |
| | | } |
| | | .info-value.highlight { |
| | | color: #2979ff; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .product-card { |
| | | background: #f8f9fa; |
| | | border-radius: 0.5rem; |
| | | padding: 1rem; |
| | | margin-bottom: 1rem; |
| | | border: 0.0625rem solid #e8e8e8; |
| | | } |
| | | .product-card { |
| | | background: #f8f9fa; |
| | | border-radius: 0.5rem; |
| | | padding: 1rem; |
| | | margin-bottom: 1rem; |
| | | border: 0.0625rem solid #e8e8e8; |
| | | } |
| | | |
| | | .product-header { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-bottom: 1rem; |
| | | padding-bottom: 0.5rem; |
| | | border-bottom: 0.0625rem solid #e8e8e8; |
| | | } |
| | | .product-header { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-bottom: 1rem; |
| | | padding-bottom: 0.5rem; |
| | | border-bottom: 0.0625rem solid #e8e8e8; |
| | | } |
| | | |
| | | .product-title { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 0.5rem; |
| | | } |
| | | .product-title { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 0.5rem; |
| | | } |
| | | |
| | | .product-productCategory { |
| | | font-size: 0.875rem; |
| | | font-weight: 500; |
| | | color: #333; |
| | | } |
| | | .product-productCategory { |
| | | font-size: 0.875rem; |
| | | font-weight: 500; |
| | | color: #333; |
| | | } |
| | | |
| | | .product-info .info-grid { |
| | | grid-template-columns: 1fr 1fr; |
| | | gap: 0.5rem; |
| | | } |
| | | .product-info .info-grid { |
| | | grid-template-columns: 1fr 1fr; |
| | | gap: 0.5rem; |
| | | } |
| | | |
| | | .no-product { |
| | | text-align: center; |
| | | padding: 2rem; |
| | | color: #999; |
| | | font-size: 0.875rem; |
| | | } |
| | | .no-product { |
| | | text-align: center; |
| | | padding: 2rem; |
| | | color: #999; |
| | | font-size: 0.875rem; |
| | | } |
| | | </style> |