| .vscode/settings.json | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/api/procurementManagement/purchaseReturnOrder.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages.json | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/basicData/supplierManage/edit.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/basicData/supplierManage/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/indexItem.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/procurementManagement/purchaseReturnOrder/add.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/procurementManagement/purchaseReturnOrder/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/procurementManagement/purchaseReturnOrder/productList.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/procurementManagement/purchaseReturnOrder/view.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/works.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
.vscode/settings.json
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,15 @@ { "i18n-ally.localesPaths": [ "src/pages_template/common/locales", "src/uni_modules/uni-table/i18n", "src/uni_modules/uni-countdown/components/uni-countdown/i18n", "src/uni_modules/uni-calendar/components/uni-calendar/i18n", "src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n", "src/uni_modules/uni-fav/components/uni-fav/i18n", "src/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n", "src/uni_modules/uni-load-more/components/uni-load-more/i18n", "src/uni_modules/uni-popup/components/uni-popup/i18n", "src/uni_modules/uni-pagination/components/uni-pagination/i18n", "src/uni_modules/uni-search-bar/components/uni-search-bar/i18n" ] } src/api/procurementManagement/purchaseReturnOrder.js
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,31 @@ import request from "@/utils/request"; export function findPurchaseReturnOrderListPage(query) { return request({ url: "/purchaseReturnOrders/listPage", method: "get", params: query, }); } export function createPurchaseReturnOrder(data) { return request({ url: "/purchaseReturnOrders/add", method: "post", data, }); } export function getPurchaseReturnOrderDetail(id) { return request({ url: "/purchaseReturnOrders/selectById/" + id, method: "get", }); } export function deletePurchaseReturnOrder(id) { return request({ url: "/purchaseReturnOrders/deleteById/" + id, method: "post", }); } src/pages.json
@@ -262,6 +262,48 @@ } }, { "path": "pages/procurementManagement/purchaseReturnOrder/index", "style": { "navigationBarTitleText": "éè´éè´§å", "navigationStyle": "custom" } }, { "path": "pages/procurementManagement/purchaseReturnOrder/add", "style": { "navigationBarTitleText": "æ°å¢éè´éè´§", "navigationStyle": "custom" } }, { "path": "pages/procurementManagement/purchaseReturnOrder/view", "style": { "navigationBarTitleText": "éè´é货详æ ", "navigationStyle": "custom" } }, { "path": "pages/procurementManagement/purchaseReturnOrder/productList", "style": { "navigationBarTitleText": "éæ©äº§å", "navigationStyle": "custom" } }, { "path": "pages/basicData/supplierManage/index", "style": { "navigationBarTitleText": "ä¾åºå管ç", "navigationStyle": "custom" } }, { "path": "pages/basicData/supplierManage/edit", "style": { "navigationBarTitleText": "ä¾åºåä¿¡æ¯", "navigationStyle": "custom" } }, { "path": "pages/cooperativeOffice/collaborativeApproval/index1", "style": { "navigationBarTitleText": "å ¬åºç®¡ç", src/pages/basicData/supplierManage/edit.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,216 @@ <template> <view class="account-detail"> <PageHeader :title="pageTitle" @back="goBack" /> <up-form ref="formRef" :model="form" :rules="rules" label-width="120"> <u-cell-group title="ä¾åºåä¿¡æ¯" class="form-section"> <up-form-item label="ä¾åºååç§°" prop="supplierName" required> <up-input v-model="form.supplierName" placeholder="请è¾å ¥" clearable /> </up-form-item> <up-form-item label="纳ç¨äººè¯å«å·" prop="taxpayerIdentificationNum" required> <up-input v-model="form.taxpayerIdentificationNum" placeholder="请è¾å ¥" clearable /> </up-form-item> <up-form-item label="å ¬å¸å°å" prop="companyAddress" required> <up-input v-model="form.companyAddress" placeholder="请è¾å ¥" clearable /> </up-form-item> <up-form-item label="å ¬å¸çµè¯" prop="companyPhone" required> <up-input v-model="form.companyPhone" placeholder="请è¾å ¥" clearable /> </up-form-item> <up-form-item label="弿·è¡" prop="bankAccountName" required> <up-input v-model="form.bankAccountName" placeholder="请è¾å ¥" clearable /> </up-form-item> <up-form-item label="è´¦å·" prop="bankAccountNum" required> <up-input v-model="form.bankAccountNum" placeholder="请è¾å ¥" clearable /> </up-form-item> <up-form-item label="è系人" prop="contactUserName"> <up-input v-model="form.contactUserName" placeholder="请è¾å ¥" clearable /> </up-form-item> <up-form-item label="èç³»çµè¯" prop="contactUserPhone"> <up-input v-model="form.contactUserPhone" placeholder="请è¾å ¥" clearable /> </up-form-item> <up-form-item label="ä¾åºåç±»å" prop="supplierType" required> <up-input v-model="supplierTypeText" placeholder="è¯·éæ©" readonly @click="showSupplierTypeSheet = true" /> <template #right> <up-icon name="arrow-right" @click="showSupplierTypeSheet = true"></up-icon> </template> </up-form-item> <up-form-item label="æ¯å¦ç½åå" prop="isWhite" required> <up-input v-model="isWhiteText" placeholder="è¯·éæ©" readonly @click="showIsWhiteSheet = true" /> <template #right> <up-icon name="arrow-right" @click="showIsWhiteSheet = true"></up-icon> </template> </up-form-item> <up-form-item label="ç»´æ¤äºº" prop="maintainUserName"> <up-input v-model="form.maintainUserName" disabled placeholder="èªå¨å¡«å " /> </up-form-item> <up-form-item label="ç»´æ¤æ¶é´" prop="maintainTime"> <up-input v-model="form.maintainTime" disabled placeholder="èªå¨å¡«å " /> </up-form-item> </u-cell-group> </up-form> <FooterButtons :loading="loading" confirmText="ä¿å" @cancel="goBack" @confirm="handleSubmit" /> <up-action-sheet :show="showSupplierTypeSheet" title="éæ©ä¾åºåç±»å" :actions="supplierTypeActions" @select="onSelectSupplierType" @close="showSupplierTypeSheet = false" /> <up-action-sheet :show="showIsWhiteSheet" title="éæ©ç½åå" :actions="isWhiteActions" @select="onSelectIsWhite" @close="showIsWhiteSheet = false" /> </view> </template> <script setup> import { computed, onMounted, ref } from "vue"; import { onLoad } from "@dcloudio/uni-app"; import FooterButtons from "@/components/FooterButtons.vue"; import useUserStore from "@/store/modules/user"; import { formatDateToYMD } from "@/utils/ruoyi"; import { addSupplier, getSupplier, updateSupplier } from "@/api/basicData/supplierManageFile"; const userStore = useUserStore(); const formRef = ref(); const loading = ref(false); const supplierId = ref(undefined); const form = ref({ supplierName: "", taxpayerIdentificationNum: "", companyAddress: "", companyPhone: "", bankAccountName: "", bankAccountNum: "", contactUserName: "", contactUserPhone: "", maintainUserId: "", maintainUserName: "", maintainTime: "", supplierType: "", isWhite: 0, }); const rules = { supplierName: [{ required: true, message: "请è¾å ¥ä¾åºååç§°", trigger: "blur" }], taxpayerIdentificationNum: [{ required: true, message: "请è¾å ¥çº³ç¨äººè¯å«å·", trigger: "blur" }], companyAddress: [{ required: true, message: "请è¾å ¥å ¬å¸å°å", trigger: "blur" }], companyPhone: [{ required: true, message: "请è¾å ¥å ¬å¸çµè¯", trigger: "blur" }], bankAccountName: [{ required: true, message: "请è¾å ¥å¼æ·è¡", trigger: "blur" }], bankAccountNum: [{ required: true, message: "请è¾å ¥è´¦å·", trigger: "blur" }], supplierType: [{ required: true, message: "è¯·éæ©ä¾åºåç±»å", trigger: "change" }], isWhite: [{ required: true, message: "è¯·éæ©ç½åå", trigger: "change" }], }; const pageTitle = computed(() => { return supplierId.value ? "ç¼è¾ä¾åºå" : "æ°å¢ä¾åºå"; }); const supplierTypeActions = [ { name: "ç²", value: "ç²" }, { name: "ä¹", value: "ä¹" }, { name: "ä¸", value: "ä¸" }, { name: "ä¸", value: "ä¸" }, ]; const isWhiteActions = [ { name: "æ¯", value: 0 }, { name: "å¦", value: 1 }, ]; const showSupplierTypeSheet = ref(false); const showIsWhiteSheet = ref(false); const supplierTypeText = computed(() => form.value.supplierType || ""); const isWhiteText = computed(() => { return String(form.value.isWhite) === "0" ? "æ¯" : "å¦"; }); const onSelectSupplierType = action => { form.value.supplierType = action.value; showSupplierTypeSheet.value = false; }; const onSelectIsWhite = action => { form.value.isWhite = action.value; showIsWhiteSheet.value = false; }; const initForAdd = () => { form.value.maintainUserId = userStore.id; form.value.maintainUserName = userStore.nickName; form.value.maintainTime = formatDateToYMD(Date.now()); form.value.isWhite = 0; }; const loadDetail = () => { if (!supplierId.value) return; uni.showLoading({ title: "å è½½ä¸...", mask: true }); getSupplier(supplierId.value) .then(res => { form.value = { ...form.value, ...(res.data || {}) }; }) .catch(() => { uni.showToast({ title: "è·å详æ 失败", icon: "error" }); }) .finally(() => { uni.hideLoading(); }); }; const goBack = () => { uni.navigateBack(); }; const handleSubmit = async () => { const valid = await formRef.value.validate().catch(() => false); if (!valid) return; loading.value = true; const action = supplierId.value ? updateSupplier : addSupplier; action({ ...form.value, id: supplierId.value }) .then(() => { uni.showToast({ title: "ä¿åæå", icon: "success" }); uni.navigateBack(); }) .catch(() => { uni.showToast({ title: "ä¿å失败", icon: "error" }); }) .finally(() => { loading.value = false; }); }; onMounted(() => { userStore.getInfo(); initForAdd(); }); onLoad(options => { if (options?.id) supplierId.value = options.id; if (supplierId.value) loadDetail(); }); </script> <style scoped lang="scss"> @import "@/styles/procurement-common.scss"; .account-detail { min-height: 100vh; background: #f8f9fa; padding-bottom: 90px; } .form-section { margin: 12px; border-radius: 12px; overflow: hidden; } </style> src/pages/basicData/supplierManage/index.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,197 @@ <template> <view class="sales-account"> <PageHeader title="ä¾åºå管ç" @back="goBack"> <template #right> <up-button type="primary" size="small" text="æ°å¢" :customStyle="{ marginRight: '12px' }" @click="goAdd" /> </template> </PageHeader> <view class="search-section"> <view class="search-bar"> <view class="search-input"> <up-input class="search-text" placeholder="请è¾å ¥ä¾åºååç§°" v-model="supplierName" @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="tabs-section"> <up-tabs v-model="tabValue" :list="tabList" itemStyle="width: 50%;height: 80rpx;" @change="onTabChange" > </up-tabs> </view> <view class="ledger-list" v-if="list.length > 0"> <view v-for="item in list" :key="item.id"> <view class="ledger-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.supplierName || "-" }}</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.supplierType || "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">纳ç¨äººè¯å«å·</text> <text class="detail-value">{{ item.taxpayerIdentificationNum || "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">å ¬å¸çµè¯</text> <text class="detail-value">{{ item.companyPhone || "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">è系人</text> <text class="detail-value">{{ item.contactUserName || "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">èç³»çµè¯</text> <text class="detail-value">{{ item.contactUserPhone || "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">ç»´æ¤äºº</text> <text class="detail-value">{{ item.maintainUserName || "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">ç»´æ¤æ¶é´</text> <text class="detail-value">{{ item.maintainTime || "-" }}</text> </view> </view> <view class="action-buttons"> <u-button size="small" class="action-btn" @click="goEdit(item)">ç¼è¾</u-button> <u-button type="error" size="small" class="action-btn" @click="handleDelete(item)" > å é¤ </u-button> </view> </view> </view> </view> <view v-else class="no-data"> <text>ææ ä¾åºåæ°æ®</text> </view> </view> </template> <script setup> import { reactive, ref } from "vue"; import { onShow } from "@dcloudio/uni-app"; import useUserStore from "@/store/modules/user"; import { delSupplier, listSupplier } from "@/api/basicData/supplierManageFile"; const userStore = useUserStore(); const supplierName = ref(""); const list = ref([]); const tabList = reactive([ { name: "æ£å¸¸ä¾åºå", value: 0 }, { name: "é»åå", value: 1 }, ]); const tabValue = ref(0); const page = { current: -1, size: -1, }; const goBack = () => { uni.navigateBack(); }; const goAdd = () => { uni.navigateTo({ url: "/pages/basicData/supplierManage/edit" }); }; const goEdit = item => { uni.navigateTo({ url: `/pages/basicData/supplierManage/edit?id=${item.id}` }); }; const onTabChange = val => { tabValue.value = val.value; getList(); }; const getList = () => { uni.showLoading({ title: "å è½½ä¸...", mask: true }); listSupplier({ ...page, supplierName: supplierName.value, isWhite: tabValue.value, }) .then(res => { list.value = res?.data?.records || []; }) .catch(() => { uni.showToast({ title: "æ¥è¯¢å¤±è´¥", icon: "error" }); }) .finally(() => { uni.hideLoading(); }); }; const handleDelete = item => { if (!item?.id) return; if (item.maintainUserName && item.maintainUserName !== userStore.nickName) { uni.showToast({ title: "ä¸å¯å é¤ä»äººç»´æ¤çæ°æ®", icon: "none" }); return; } uni.showModal({ title: "å é¤æç¤º", content: "ç¡®å®è¦å é¤åï¼å é¤åæ æ³æ¢å¤", success: res => { if (!res.confirm) return; uni.showLoading({ title: "å é¤ä¸...", mask: true }); delSupplier([item.id]) .then(() => { uni.showToast({ title: "å 餿å", icon: "success" }); getList(); }) .catch(() => { uni.showToast({ title: "å é¤å¤±è´¥", icon: "error" }); }) .finally(() => { uni.hideLoading(); }); }, }); }; onShow(() => { userStore.getInfo(); getList(); }); </script> <style scoped lang="scss"> @import "@/styles/procurement-common.scss"; .tabs-section { background: #fff; padding: 0 12px 8px 12px; } </style> src/pages/indexItem.vue
@@ -49,6 +49,8 @@ "仿¬¾ç»è®°": "/pages/procurementManagement/paymentEntry/index", "仿¬¾æµæ°´": "/pages/procurementManagement/receiptPaymentHistory/index", "ä¾åºå徿¥": "/pages/procurementManagement/paymentLedger/index", "éè´éè´§å": "/pages/procurementManagement/purchaseReturnOrder/index", "ä¾åºå管ç": "/pages/basicData/supplierManage/index", "å ¬åºç®¡ç": "/pages/cooperativeOffice/collaborativeApproval/index1", "请å管ç": "/pages/cooperativeOffice/collaborativeApproval/index2", "åºå·®ç®¡ç": "/pages/cooperativeOffice/collaborativeApproval/index3", src/pages/procurementManagement/purchaseReturnOrder/add.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,758 @@ <template> <view class="account-detail"> <PageHeader title="æ°å¢éè´éè´§" @back="goBack" /> <up-form ref="formRef" :model="form" :rules="rules" label-width="120"> <u-cell-group title="åºæ¬ä¿¡æ¯" class="form-section"> <up-form-item label="éæåå·" prop="no"> <up-input v-model="form.no" :disabled="form.isDefaultNo" :placeholder="form.isDefaultNo ? '使ç¨ç³»ç»ç¼å·' : '请è¾å ¥éæåå·'" clearable /> <template #right> <up-switch v-model="form.isDefaultNo" @change="onDefaultNoChange" /> </template> </up-form-item> <up-form-item label="éè´§æ¹å¼" prop="returnType" required> <up-input v-model="returnTypeText" placeholder="è¯·éæ©" readonly @click="showReturnTypeSheet = true" /> <template #right> <up-icon name="arrow-right" @click="showReturnTypeSheet = true"></up-icon> </template> </up-form-item> <up-form-item label="ä¾åºå" prop="supplierId" required> <up-input v-model="supplierText" placeholder="è¯·éæ©" readonly @click="showSupplierSheet = true" /> <template #right> <up-icon name="arrow-right" @click="showSupplierSheet = true"></up-icon> </template> </up-form-item> <up-form-item label="项ç®é¶æ®µ" prop="projectPhase"> <up-input v-model="projectPhaseText" placeholder="è¯·éæ©" readonly @click="showProjectPhaseSheet = true" /> <template #right> <up-icon name="arrow-right" @click="showProjectPhaseSheet = true"></up-icon> </template> </up-form-item> <up-form-item label="å¶ä½æ¥æ" prop="preparedAt" required> <up-input v-model="form.preparedAt" placeholder="è¯·éæ©" readonly @click="showPreparedAtPicker = true" /> <template #right> <up-icon name="arrow-right" @click="showPreparedAtPicker = true"></up-icon> </template> </up-form-item> <up-form-item label="å¶å人" prop="preparedUserId" required> <up-input v-model="preparedUserText" placeholder="è¯·éæ©" readonly @click="showPreparedUserSheet = true" /> <template #right> <up-icon name="arrow-right" @click="showPreparedUserSheet = true"></up-icon> </template> </up-form-item> <up-form-item label="éæäºº" prop="returnUserId" required> <up-input v-model="returnUserText" placeholder="è¯·éæ©" readonly @click="showReturnUserSheet = true" /> <template #right> <up-icon name="arrow-right" @click="showReturnUserSheet = true"></up-icon> </template> </up-form-item> <up-form-item label="éè´ååå·" prop="purchaseLedgerId" required> <up-input v-model="purchaseContractText" placeholder="è¯·éæ©" readonly @click="showPurchaseLedgerSheet = true" /> <template #right> <up-icon name="arrow-right" @click="showPurchaseLedgerSheet = true"></up-icon> </template> </up-form-item> <up-form-item label="夿³¨" prop="remark"> <up-textarea v-model="form.remark" placeholder="请è¾å ¥" auto-height /> </up-form-item> </u-cell-group> <u-cell-group title="产åå表" class="form-section"> <view class="product-actions"> <up-button type="primary" size="small" text="éæ©äº§å" :disabled="!form.purchaseLedgerId" @click="goSelectProducts" /> <view class="amount-summary"> <text class="amount-text">å计ï¼{{ formatAmount(baseAmount) }}</text> </view> </view> <view v-if="form.purchaseReturnOrderProductsDtos.length === 0" class="empty-products"> <text>ææ äº§åï¼è¯·å éæ©äº§å</text> </view> <view v-else class="product-list"> <view v-for="(item, index) in form.purchaseReturnOrderProductsDtos" :key="item.salesLedgerProductId || item.id || index" class="product-card" > <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-name">产å {{ index + 1 }}</text> </view> <up-icon name="trash" size="18" color="#ee0a24" @click="removeProduct(index)" /> </view> <up-divider></up-divider> <view class="product-body"> <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.quantity ?? "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">å«ç¨åä»·(å )</text> <text class="detail-value">{{ formatAmount(item.taxInclusiveUnitPrice) }}</text> </view> <view class="qty-row"> <text class="qty-label">éè´§æ°é</text> <up-number-box v-model="item.returnQuantity" :min="0" :max="getReturnQtyMax(item)" :step="1" @change="syncRowTotal(item)" /> </view> <view class="detail-row"> <text class="detail-label">éè´§æ»ä»·(å )</text> <text class="detail-value highlight">{{ formatAmount(item.taxInclusiveTotalPrice) }}</text> </view> </view> </view> </view> </u-cell-group> <u-cell-group title="è´¹ç¨ä¿¡æ¯" class="form-section"> <up-form-item label="æ´åææ£é¢" prop="totalDiscountAmount"> <up-input v-model="form.totalDiscountAmount" type="number" placeholder="请è¾å ¥" @blur="onDiscountAmountBlur" clearable /> </up-form-item> <up-form-item label="æ´åææ£ç(%)" prop="totalDiscountRate"> <up-input v-model="form.totalDiscountRate" type="number" placeholder="请è¾å ¥" @blur="onDiscountRateBlur" clearable /> </up-form-item> <up-form-item label="æäº¤éé¢" prop="totalAmount" required> <up-input v-model="form.totalAmount" disabled placeholder="èªå¨è®¡ç®" /> </up-form-item> <up-form-item label="æ¶æ¬¾æ¹å¼" prop="incomeType" required> <up-input v-model="incomeTypeText" placeholder="è¯·éæ©" readonly @click="showIncomeTypeSheet = true" /> <template #right> <up-icon name="arrow-right" @click="showIncomeTypeSheet = true"></up-icon> </template> </up-form-item> </u-cell-group> </up-form> <FooterButtons :loading="loading" confirmText="æäº¤" @cancel="goBack" @confirm="handleSubmit" /> <up-action-sheet :show="showReturnTypeSheet" title="éæ©éè´§æ¹å¼" :actions="returnTypeActions" @select="onSelectReturnType" @close="showReturnTypeSheet = false" /> <up-action-sheet :show="showProjectPhaseSheet" title="鿩项ç®é¶æ®µ" :actions="projectPhaseActions" @select="onSelectProjectPhase" @close="showProjectPhaseSheet = false" /> <up-action-sheet :show="showSupplierSheet" title="éæ©ä¾åºå" :actions="supplierActions" @select="onSelectSupplier" @close="showSupplierSheet = false" /> <up-action-sheet :show="showPreparedUserSheet" title="éæ©å¶å人" :actions="userActions" @select="onSelectPreparedUser" @close="showPreparedUserSheet = false" /> <up-action-sheet :show="showReturnUserSheet" title="éæ©éæäºº" :actions="userActions" @select="onSelectReturnUser" @close="showReturnUserSheet = false" /> <up-action-sheet :show="showPurchaseLedgerSheet" title="éæ©éè´ååå·" :actions="purchaseLedgerActions" @select="onSelectPurchaseLedger" @close="showPurchaseLedgerSheet = false" /> <up-action-sheet :show="showIncomeTypeSheet" title="éæ©æ¶æ¬¾æ¹å¼" :actions="incomeTypeActions" @select="onSelectIncomeType" @close="showIncomeTypeSheet = false" /> <up-datetime-picker :show="showPreparedAtPicker" v-model="preparedAtPickerValue" mode="date" @confirm="onPreparedAtConfirm" @cancel="showPreparedAtPicker = false" /> </view> </template> <script setup> import { computed, onMounted, ref, watch } from "vue"; import { onShow } from "@dcloudio/uni-app"; import FooterButtons from "@/components/FooterButtons.vue"; import { formatDateToYMD } from "@/utils/ruoyi"; import { createPurchaseReturnOrder } from "@/api/procurementManagement/purchaseReturnOrder"; import { getOptions, purchaseListPage, productList } from "@/api/procurementManagement/procurementLedger"; import { userListNoPageByTenantId } from "@/api/system/user"; const formRef = ref(); const loading = ref(false); const form = ref({ no: "", isDefaultNo: true, returnType: 0, supplierId: undefined, supplierName: "", projectPhase: undefined, preparedAt: "", preparedUserId: undefined, preparedUserName: "", returnUserId: undefined, returnUserName: "", purchaseLedgerId: undefined, purchaseContractNumber: "", remark: "", totalDiscountAmount: 0, totalDiscountRate: "", totalAmount: 0, incomeType: undefined, purchaseReturnOrderProductsDtos: [], }); const rules = { returnType: [{ required: true, message: "è¯·éæ©éè´§æ¹å¼", trigger: "change" }], supplierId: [{ required: true, message: "è¯·éæ©ä¾åºå", trigger: "change" }], preparedAt: [{ required: true, message: "è¯·éæ©å¶ä½æ¥æ", trigger: "change" }], preparedUserId: [{ required: true, message: "è¯·éæ©å¶å人", trigger: "change" }], returnUserId: [{ required: true, message: "è¯·éæ©éæäºº", trigger: "change" }], purchaseLedgerId: [{ required: true, message: "è¯·éæ©éè´ååå·", trigger: "change" }], totalAmount: [{ required: true, message: "æäº¤éé¢ä¸è½ä¸ºç©º", trigger: "change" }], incomeType: [{ required: true, message: "è¯·éæ©æ¶æ¬¾æ¹å¼", trigger: "change" }], }; const showReturnTypeSheet = ref(false); const showProjectPhaseSheet = ref(false); const showSupplierSheet = ref(false); const showPreparedUserSheet = ref(false); const showReturnUserSheet = ref(false); const showPurchaseLedgerSheet = ref(false); const showIncomeTypeSheet = ref(false); const showPreparedAtPicker = ref(false); const preparedAtPickerValue = ref(Date.now()); const supplierOptions = ref([]); const userOptions = ref([]); const purchaseLedgerOptions = ref([]); const returnTypeActions = [ { name: "éè´§éæ¬¾", value: 0 }, { name: "ææ¶", value: 1 }, ]; const projectPhaseActions = [ { name: "ç«é¡¹", value: 0 }, { name: "设计", value: 1 }, { name: "éè´", value: 2 }, { name: "ç产", value: 3 }, { name: "åºè´§", value: 4 }, ]; const incomeTypeActions = [ { name: "ç°é", value: "0" }, { name: "æ¯ç¥¨", value: "1" }, { name: "é¶è¡è½¬è´¦", value: "2" }, { name: "å ¶ä»", value: "3" }, ]; const returnTypeText = computed(() => { return returnTypeActions.find(i => String(i.value) === String(form.value.returnType))?.name || ""; }); const projectPhaseText = computed(() => { return projectPhaseActions.find(i => String(i.value) === String(form.value.projectPhase))?.name || ""; }); const supplierText = computed(() => { return supplierOptions.value.find(i => String(i.id) === String(form.value.supplierId))?.supplierName || ""; }); const preparedUserText = computed(() => { return userOptions.value.find(i => String(i.userId) === String(form.value.preparedUserId))?.nickName || ""; }); const returnUserText = computed(() => { return userOptions.value.find(i => String(i.userId) === String(form.value.returnUserId))?.nickName || ""; }); const purchaseContractText = computed(() => { return purchaseLedgerOptions.value.find(i => String(i.id) === String(form.value.purchaseLedgerId))?.purchaseContractNumber || ""; }); const incomeTypeText = computed(() => { return incomeTypeActions.find(i => String(i.value) === String(form.value.incomeType))?.name || ""; }); const supplierActions = computed(() => { return supplierOptions.value.map(i => ({ name: i.supplierName, value: i.id })); }); const userActions = computed(() => { return userOptions.value.map(i => ({ name: i.nickName, value: i.userId })); }); const purchaseLedgerActions = computed(() => { return purchaseLedgerOptions.value.map(i => ({ name: i.purchaseContractNumber, value: i.id })); }); const toNumber = val => { const num = Number(val); return Number.isNaN(num) ? 0 : num; }; const formatAmount = value => { if (value === null || value === undefined || value === "") return "0.00"; const num = Number(value); if (Number.isNaN(num)) return "0.00"; return num.toFixed(2); }; const baseAmount = computed(() => { const rows = form.value.purchaseReturnOrderProductsDtos || []; return rows.reduce((sum, item) => sum + toNumber(item.taxInclusiveTotalPrice), 0); }); const syncTotalAmount = () => { const total = baseAmount.value - toNumber(form.value.totalDiscountAmount); form.value.totalAmount = Number(total.toFixed(2)); }; const getReturnQtyMax = row => { const qty = Number(row?.quantity); if (Number.isNaN(qty) || qty < 0) return 0; return qty; }; const syncRowTotal = row => { if (!row) return; const qty = toNumber(row.returnQuantity); const unitPrice = toNumber(row.taxInclusiveUnitPrice); row.taxInclusiveTotalPrice = Number((qty * unitPrice).toFixed(2)); syncTotalAmount(); }; const removeProduct = index => { form.value.purchaseReturnOrderProductsDtos.splice(index, 1); syncTotalAmount(); }; const resetFeeInfo = () => { form.value.totalDiscountAmount = 0; form.value.totalDiscountRate = ""; form.value.totalAmount = 0; form.value.incomeType = undefined; }; const onDefaultNoChange = checked => { if (checked) form.value.no = ""; }; const onSelectReturnType = action => { form.value.returnType = action.value; showReturnTypeSheet.value = false; }; const onSelectProjectPhase = action => { form.value.projectPhase = action.value; showProjectPhaseSheet.value = false; }; const onSelectSupplier = action => { form.value.supplierId = action.value; form.value.supplierName = supplierOptions.value.find(i => String(i.id) === String(action.value))?.supplierName || ""; form.value.purchaseLedgerId = undefined; form.value.purchaseContractNumber = ""; form.value.purchaseReturnOrderProductsDtos = []; resetFeeInfo(); showSupplierSheet.value = false; fetchPurchaseLedgerOptions(); }; const onSelectPreparedUser = action => { form.value.preparedUserId = action.value; form.value.preparedUserName = userOptions.value.find(i => String(i.userId) === String(action.value))?.nickName || ""; showPreparedUserSheet.value = false; }; const onSelectReturnUser = action => { form.value.returnUserId = action.value; form.value.returnUserName = userOptions.value.find(i => String(i.userId) === String(action.value))?.nickName || ""; showReturnUserSheet.value = false; }; const onSelectPurchaseLedger = action => { form.value.purchaseLedgerId = action.value; form.value.purchaseContractNumber = purchaseLedgerOptions.value.find(i => String(i.id) === String(action.value))?.purchaseContractNumber || ""; form.value.purchaseReturnOrderProductsDtos = []; resetFeeInfo(); showPurchaseLedgerSheet.value = false; }; const onSelectIncomeType = action => { form.value.incomeType = action.value; showIncomeTypeSheet.value = false; }; const onPreparedAtConfirm = e => { form.value.preparedAt = formatDateToYMD(e.value); showPreparedAtPicker.value = false; }; const onDiscountRateBlur = () => { const rate = toNumber(form.value.totalDiscountRate); if (rate < 0 || rate > 100) { uni.showToast({ title: "ææ£çéå¨0-100", icon: "none" }); return; } form.value.totalDiscountAmount = Number((baseAmount.value * (rate / 100)).toFixed(2)); syncTotalAmount(); }; const onDiscountAmountBlur = () => { const amount = toNumber(form.value.totalDiscountAmount); if (amount < 0) { form.value.totalDiscountAmount = 0; } const base = baseAmount.value; if (base <= 0) { form.value.totalDiscountRate = ""; syncTotalAmount(); return; } if (toNumber(form.value.totalDiscountAmount) > base) { form.value.totalDiscountAmount = Number(base.toFixed(2)); } const rate = (toNumber(form.value.totalDiscountAmount) / base) * 100; form.value.totalDiscountRate = Number(rate.toFixed(2)); syncTotalAmount(); }; const goBack = () => { uni.removeStorageSync("purchaseReturnOrderSelectedProducts"); uni.navigateBack(); }; const goSelectProducts = () => { if (!form.value.purchaseLedgerId) return; uni.navigateTo({ url: `/pages/procurementManagement/purchaseReturnOrder/productList?purchaseLedgerId=${form.value.purchaseLedgerId}`, }); }; const fetchSupplierOptions = () => { getOptions() .then(res => { supplierOptions.value = res.data || []; }) .catch(() => { supplierOptions.value = []; }); }; const fetchUserOptions = () => { userListNoPageByTenantId() .then(res => { userOptions.value = res.data || []; }) .catch(() => { userOptions.value = []; }); }; const fetchPurchaseLedgerOptions = () => { purchaseLedgerOptions.value = []; if (!form.value.supplierId) return; purchaseListPage({ current: -1, size: -1, supplierId: form.value.supplierId, approvalStatus: 3, }) .then(res => { purchaseLedgerOptions.value = res?.data?.records || []; }) .catch(() => { purchaseLedgerOptions.value = []; }); }; const mergeSelectedProducts = selectedRows => { const existing = new Set((form.value.purchaseReturnOrderProductsDtos || []).map(i => String(i.salesLedgerProductId || i.id))); const toAdd = (selectedRows || []) .filter(i => !existing.has(String(i.id))) .map(i => ({ ...i, salesLedgerProductId: i.id, returnQuantity: 0, taxInclusiveTotalPrice: 0, })); form.value.purchaseReturnOrderProductsDtos.push(...toAdd); syncTotalAmount(); }; const loadProductsFromPurchaseLedger = () => { if (!form.value.purchaseLedgerId) return; uni.showLoading({ title: "å 载产å...", mask: true }); productList({ salesLedgerId: form.value.purchaseLedgerId, type: 2 }) .then(res => { const rows = res.data || []; mergeSelectedProducts(rows); }) .catch(() => { uni.showToast({ title: "å 载产å失败", icon: "error" }); }) .finally(() => { uni.hideLoading(); }); }; const validateProducts = () => { const rows = form.value.purchaseReturnOrderProductsDtos || []; if (rows.length === 0) { uni.showToast({ title: "请å éæ©äº§å", icon: "none" }); return false; } const invalid = rows.findIndex(i => { const qty = toNumber(i.returnQuantity); if (qty <= 0) return true; if (qty > getReturnQtyMax(i)) return true; return false; }); if (invalid !== -1) { uni.showToast({ title: `第${invalid + 1}è¡éè´§æ°éä¸åæ³`, icon: "none" }); return false; } return true; }; const handleSubmit = async () => { if (!validateProducts()) return; const valid = await formRef.value.validate().catch(() => false); if (!valid) return; loading.value = true; const rows = (form.value.purchaseReturnOrderProductsDtos || []).map(i => { const cloned = { ...i }; syncRowTotal(cloned); return cloned; }); const payload = { ...form.value, purchaseReturnOrderProductsDtos: rows.filter(i => toNumber(i.returnQuantity) > 0), }; createPurchaseReturnOrder(payload) .then(() => { uni.showToast({ title: "æäº¤æå", icon: "success" }); uni.removeStorageSync("purchaseReturnOrderSelectedProducts"); uni.navigateBack(); }) .catch(() => { uni.showToast({ title: "æäº¤å¤±è´¥", icon: "error" }); }) .finally(() => { loading.value = false; }); }; watch( () => baseAmount.value, () => { syncTotalAmount(); } ); onMounted(() => { form.value.preparedAt = formatDateToYMD(Date.now()); preparedAtPickerValue.value = Date.now(); fetchSupplierOptions(); fetchUserOptions(); }); onShow(() => { const stored = uni.getStorageSync("purchaseReturnOrderSelectedProducts"); if (stored) { try { const rows = JSON.parse(stored); mergeSelectedProducts(rows); uni.removeStorageSync("purchaseReturnOrderSelectedProducts"); } catch { uni.removeStorageSync("purchaseReturnOrderSelectedProducts"); } } }); const showAutoLoadModalOnceKey = "purchaseReturnOrderAutoLoadShown"; watch( () => form.value.purchaseLedgerId, (val, oldVal) => { if (!val || String(val) === String(oldVal)) return; if (uni.getStorageSync(showAutoLoadModalOnceKey)) return; uni.setStorageSync(showAutoLoadModalOnceKey, "1"); uni.showModal({ title: "æç¤º", content: "æ¯å¦èªå¨å 载该éè´ååä¸å ¨é¨äº§åï¼", success: res => { if (res.confirm) loadProductsFromPurchaseLedger(); }, }); } ); </script> <style scoped lang="scss"> @import "@/styles/procurement-common.scss"; .account-detail { min-height: 100vh; background: #f8f9fa; padding-bottom: 90px; } .form-section { margin: 12px; border-radius: 12px; overflow: hidden; } .product-actions { display: flex; align-items: center; justify-content: space-between; padding: 12px 12px 0 12px; } .amount-summary { display: flex; align-items: center; } .amount-text { font-size: 14px; color: #333; font-weight: 600; } .empty-products { padding: 16px 12px; color: #999; font-size: 14px; } .product-list { padding: 12px; display: flex; flex-direction: column; gap: 12px; } .product-card { background: #fff; border-radius: 12px; padding: 0 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); } .product-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; } .product-title { display: flex; align-items: center; gap: 8px; } .product-name { font-size: 14px; color: #333; font-weight: 500; } .product-body { padding: 12px 0; } .qty-row { display: flex; align-items: center; justify-content: space-between; padding: 8px 0; } .qty-label { font-size: 12px; color: #777; } </style> src/pages/procurementManagement/purchaseReturnOrder/index.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,185 @@ <template> <view class="sales-account"> <PageHeader title="éè´éè´§å" @back="goBack"> <template #right> <up-button type="primary" size="small" text="æ°å¢" :customStyle="{ marginRight: '12px' }" @click="goAdd" /> </template> </PageHeader> <view class="search-section"> <view class="search-bar"> <view class="search-input"> <up-input class="search-text" placeholder="请è¾å ¥éæåå·" v-model="searchNo" @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="list.length > 0"> <view v-for="item in list" :key="item.id"> <view class="ledger-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.no || "-" }}</text> </view> </view> <up-divider></up-divider> <view class="item-details"> <view class="detail-row"> <text class="detail-label">éè´§æ¹å¼</text> <text class="detail-value">{{ getReturnTypeLabel(item.returnType) }}</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">{{ getProjectPhaseLabel(item.projectPhase) }}</text> </view> <view class="detail-row"> <text class="detail-label">å¶ä½æ¥æ</text> <text class="detail-value">{{ item.preparedAt || "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">æäº¤éé¢(å )</text> <text class="detail-value highlight">{{ formatAmount(item.totalAmount) }}</text> </view> </view> <view class="action-buttons"> <u-button size="small" class="action-btn" @click="goView(item)">详æ </u-button> <u-button type="error" size="small" class="action-btn" @click="handleDelete(item)" > å é¤ </u-button> </view> </view> </view> </view> <view v-else class="no-data"> <text>ææ éè´éè´§åæ°æ®</text> </view> </view> </template> <script setup> import { ref } from "vue"; import { onShow } from "@dcloudio/uni-app"; import { findPurchaseReturnOrderListPage, deletePurchaseReturnOrder } from "@/api/procurementManagement/purchaseReturnOrder"; const searchNo = ref(""); const list = ref([]); const page = { current: -1, size: -1, }; const goBack = () => { uni.navigateBack(); }; const goAdd = () => { uni.navigateTo({ url: "/pages/procurementManagement/purchaseReturnOrder/add", }); }; const goView = item => { uni.navigateTo({ url: `/pages/procurementManagement/purchaseReturnOrder/view?id=${item.id}`, }); }; const getReturnTypeLabel = value => { if (String(value) === "0") return "éè´§éæ¬¾"; if (String(value) === "1") return "ææ¶"; return "-"; }; const getProjectPhaseLabel = value => { const map = { 0: "ç«é¡¹", 1: "设计", 2: "éè´", 3: "ç产", 4: "åºè´§", }; const key = String(value); return map[key] || "-"; }; const formatAmount = value => { if (value === null || value === undefined || value === "") return "-"; const num = Number(value); if (Number.isNaN(num)) return "-"; return num.toFixed(2); }; const getList = () => { uni.showLoading({ title: "å è½½ä¸...", mask: true }); findPurchaseReturnOrderListPage({ ...page, no: searchNo.value, }) .then(res => { list.value = res?.data?.records || []; }) .catch(() => { uni.showToast({ title: "æ¥è¯¢å¤±è´¥", icon: "error" }); }) .finally(() => { uni.hideLoading(); }); }; const handleDelete = item => { if (!item?.id) return; uni.showModal({ title: "å é¤æç¤º", content: "ç¡®å®è¦å é¤åï¼å é¤åæ æ³æ¢å¤", success: res => { if (!res.confirm) return; uni.showLoading({ title: "å é¤ä¸...", mask: true }); deletePurchaseReturnOrder(item.id) .then(() => { uni.showToast({ title: "å 餿å", icon: "success" }); getList(); }) .catch(() => { uni.showToast({ title: "å é¤å¤±è´¥", icon: "error" }); }) .finally(() => { uni.hideLoading(); }); }, }); }; onShow(() => { getList(); }); </script> <style scoped lang="scss"> @import "@/styles/procurement-common.scss"; </style> src/pages/procurementManagement/purchaseReturnOrder/productList.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,163 @@ <template> <view class="sales-account"> <PageHeader title="éæ©äº§å" @back="goBack" /> <view class="ledger-list" v-if="list.length > 0"> <view v-for="item in list" :key="item.id"> <view class="ledger-item" @click="toggle(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.productCategory || "产å" }}</text> </view> <view class="item-right"> <u-tag :type="isSelected(item.id) ? 'success' : 'info'"> {{ isSelected(item.id) ? "å·²é" : "æªé" }} </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.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 class="detail-value">{{ item.quantity ?? "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">å«ç¨åä»·(å )</text> <text class="detail-value highlight">{{ formatAmount(item.taxInclusiveUnitPrice) }}</text> </view> </view> </view> </view> </view> <view v-else class="no-data"> <text>ææ äº§åæ°æ®</text> </view> <view class="footer"> <u-button class="cancel-btn" @click="goBack">åæ¶</u-button> <u-button class="save-btn" type="primary" @click="confirm">确认({{ selectedIds.size }})</u-button> </view> </view> </template> <script setup> import { onLoad } from "@dcloudio/uni-app"; import { ref } from "vue"; import { productList } from "@/api/procurementManagement/procurementLedger"; const purchaseLedgerId = ref(undefined); const list = ref([]); const selectedIds = ref(new Set()); const selectedRows = ref(new Map()); const formatAmount = value => { if (value === null || value === undefined || value === "") return "0.00"; const num = Number(value); if (Number.isNaN(num)) return "0.00"; return num.toFixed(2); }; const isSelected = id => { return selectedIds.value.has(String(id)); }; const toggle = item => { const key = String(item.id); if (selectedIds.value.has(key)) { selectedIds.value.delete(key); selectedRows.value.delete(key); } else { selectedIds.value.add(key); selectedRows.value.set(key, item); } }; const goBack = () => { uni.navigateBack(); }; const confirm = () => { const rows = Array.from(selectedRows.value.values()); uni.setStorageSync("purchaseReturnOrderSelectedProducts", JSON.stringify(rows)); uni.navigateBack(); }; const loadList = () => { if (!purchaseLedgerId.value) return; uni.showLoading({ title: "å è½½ä¸...", mask: true }); productList({ salesLedgerId: purchaseLedgerId.value, type: 2 }) .then(res => { list.value = res.data || []; }) .catch(() => { uni.showToast({ title: "å 载失败", icon: "error" }); }) .finally(() => { uni.hideLoading(); }); }; const initSelectionFromStorage = () => { const stored = uni.getStorageSync("purchaseReturnOrderSelectedProducts"); if (!stored) return; try { const rows = JSON.parse(stored) || []; rows.forEach(r => { const key = String(r.id); selectedIds.value.add(key); selectedRows.value.set(key, r); }); } catch {} }; onLoad(options => { if (options?.purchaseLedgerId) { purchaseLedgerId.value = options.purchaseLedgerId; } initSelectionFromStorage(); loadList(); }); </script> <style scoped lang="scss"> @import "@/styles/procurement-common.scss"; .footer { position: fixed; left: 0; right: 0; bottom: 0; background: #fff; display: flex; justify-content: space-around; align-items: center; padding: 12px 0; box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05); z-index: 1000; } .cancel-btn { width: 120px; background: #c7c9cc; color: #fff; border-radius: 40px; } .save-btn { width: 200px; border-radius: 40px; } .sales-account { padding-bottom: 90px; } </style> src/pages/procurementManagement/purchaseReturnOrder/view.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,202 @@ <template> <view class="sales-account"> <PageHeader title="éè´é货详æ " @back="goBack" /> <view class="ledger-list" v-if="loaded"> <view class="ledger-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">{{ detail.no || "-" }}</text> </view> </view> <up-divider></up-divider> <view class="item-details"> <view class="detail-row"> <text class="detail-label">éè´§æ¹å¼</text> <text class="detail-value">{{ getReturnTypeLabel(detail.returnType) }}</text> </view> <view class="detail-row"> <text class="detail-label">ä¾åºååç§°</text> <text class="detail-value">{{ detail.supplierName || "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">项ç®é¶æ®µ</text> <text class="detail-value">{{ getProjectPhaseLabel(detail.projectPhase) }}</text> </view> <view class="detail-row"> <text class="detail-label">å ³èåå·</text> <text class="detail-value">{{ detail.purchaseContractNumber || "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">å¶ä½æ¥æ</text> <text class="detail-value">{{ detail.preparedAt || "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">å¶å人</text> <text class="detail-value">{{ detail.preparedUserName || "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">éæäºº</text> <text class="detail-value">{{ detail.returnUserName || "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">æ´åææ£é¢</text> <text class="detail-value">{{ formatAmount(detail.totalDiscountAmount) }}</text> </view> <view class="detail-row"> <text class="detail-label">æ´åææ£ç</text> <text class="detail-value">{{ detail.totalDiscountRate ?? "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">æäº¤éé¢</text> <text class="detail-value highlight">{{ formatAmount(detail.totalAmount) }}</text> </view> <view class="detail-row"> <text class="detail-label">夿³¨</text> <text class="detail-value">{{ detail.remark || "-" }}</text> </view> </view> </view> <view class="section-title"> <text class="section-text">产åå表</text> </view> <view v-if="products.length === 0" class="no-data"> <text>ææ äº§åæ°æ®</text> </view> <view v-else> <view v-for="(p, idx) in products" :key="idx" class="ledger-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">产å {{ idx + 1 }}</text> </view> </view> <up-divider></up-divider> <view class="item-details"> <view class="detail-row"> <text class="detail-label">产å大类</text> <text class="detail-value">{{ p.productCategory || "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">è§æ ¼åå·</text> <text class="detail-value">{{ p.specificationModel || "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">åä½</text> <text class="detail-value">{{ p.unit || "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">æ°é</text> <text class="detail-value">{{ p.quantity ?? "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">éè´§æ°é</text> <text class="detail-value highlight">{{ p.returnQuantity ?? "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">ç¨ç(%)</text> <text class="detail-value">{{ p.taxRate ?? "-" }}</text> </view> <view class="detail-row"> <text class="detail-label">å«ç¨åä»·(å )</text> <text class="detail-value">{{ formatAmount(p.taxInclusiveUnitPrice) }}</text> </view> <view class="detail-row"> <text class="detail-label">å«ç¨æ»ä»·(å )</text> <text class="detail-value">{{ formatAmount(p.taxInclusiveTotalPrice) }}</text> </view> <view class="detail-row"> <text class="detail-label">æ¯å¦è´¨æ£</text> <text class="detail-value">{{ p.isChecked ? "æ¯" : "å¦" }}</text> </view> </view> </view> </view> </view> </view> </template> <script setup> import { ref } from "vue"; import { onLoad } from "@dcloudio/uni-app"; import { getPurchaseReturnOrderDetail } from "@/api/procurementManagement/purchaseReturnOrder"; const id = ref(undefined); const loaded = ref(false); const detail = ref({}); const products = ref([]); const goBack = () => { uni.navigateBack(); }; const getReturnTypeLabel = value => { if (String(value) === "0") return "éè´§éæ¬¾"; if (String(value) === "1") return "ææ¶"; return "-"; }; const getProjectPhaseLabel = value => { const map = { 0: "ç«é¡¹", 1: "设计", 2: "éè´", 3: "ç产", 4: "åºè´§", }; const key = String(value); return map[key] || "-"; }; const formatAmount = value => { if (value === null || value === undefined || value === "") return "-"; const num = Number(value); if (Number.isNaN(num)) return "-"; return num.toFixed(2); }; const loadDetail = () => { if (!id.value) return; uni.showLoading({ title: "å è½½ä¸...", mask: true }); getPurchaseReturnOrderDetail(id.value) .then(res => { const payload = res?.data || {}; detail.value = payload; const rows = payload.purchaseReturnOrderProductsDetailVoList || []; products.value = rows.map(i => ({ ...i, ...(i.salesLedgerProduct || {}) })); loaded.value = true; }) .catch(() => { uni.showToast({ title: "è·å详æ 失败", icon: "error" }); }) .finally(() => { uni.hideLoading(); }); }; onLoad(options => { if (options?.id) id.value = options.id; loadDetail(); }); </script> <style scoped lang="scss"> @import "@/styles/procurement-common.scss"; .section-title { padding: 0 20px; margin: 12px 0 8px 0; } .section-text { font-size: 14px; color: #333; font-weight: 600; } </style> src/pages/works.vue
@@ -297,6 +297,14 @@ icon: "/static/images/icon/gongyingshangwanglai.svg", label: "ä¾åºå徿¥", }, { icon: "/static/images/icon/caigouguanli.svg", label: "éè´éè´§", }, { icon: "/static/images/icon/gongchuguanli.svg", label: "ä¾åºåæ¡£æ¡", }, ]); // è´¢å¡ç®¡çåè½æ°æ® @@ -547,6 +555,16 @@ case "ä¾åºå徿¥": uni.navigateTo({ url: "/pages/procurementManagement/paymentLedger/index", }); break; case "éè´éè´§": uni.navigateTo({ url: "/pages/procurementManagement/purchaseReturnOrder/index", }); break; case "ä¾åºåæ¡£æ¡": uni.navigateTo({ url: "/pages/basicData/supplierManage/index", }); break; case "å ¬åºç®¡ç": @@ -963,6 +981,7 @@ // æ¶éææææéçèåæ é¢ï¼æ ¹æ® meta.titleï¼ const allowedMenuTitles = new Set(); const alwaysShowTitles = new Set(["éè´éè´§å", "ä¾åºå管ç"]); const collectMenuTitles = routes => { if (!Array.isArray(routes)) return; routes.forEach(route => { @@ -980,7 +999,7 @@ const menuMapping = { collaboration: { target: collaborationItems, specialMapping: { "è§ç« å¶åº¦": "è§ç« å¶åº¦ç®¡ç" } }, }; console.log(allowedMenuTitles) // éç¨è¿æ»¤å½æ° const filterArray = (targetArray, specialMapping) => { const filtered = targetArray.filter(item => {