gaoluyang
2025-08-13 dfe413e25a9684a2d0efedbf26603bc0e48da9bb
1.销售台账模块开发联调
已修改4个文件
已添加1个文件
409 ■■■■■ 文件已修改
src/config.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages.json 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/sales/salesAccount/detail.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/sales/salesAccount/index.vue 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/sales/salesAccount/view.vue 311 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config.js
@@ -1,7 +1,7 @@
// åº”用全局配置
const config = {
  // baseUrl: 'http://114.132.189.42:8089', // æµ‹è¯•库
  baseUrl: 'http://192.168.1.147:7003', // æœ¬åœ°è”è°ƒ
  baseUrl: 'http://114.132.189.42:8089', // æµ‹è¯•库
  // baseUrl: 'http://192.168.1.147:7003', // æœ¬åœ°è”è°ƒ
   //cloud后台网关地址
  //  baseUrl: 'http://192.168.10.3:8080',
   // åº”用信息
src/pages.json
@@ -59,6 +59,13 @@
    {
      "path": "pages/sales/salesAccount/detail",
      "style": {
        "navigationBarTitleText": "修改台账",
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/sales/salesAccount/view",
      "style": {
        "navigationBarTitleText": "台账详情",
        "navigationStyle": "custom"
      }
src/pages/sales/salesAccount/detail.vue
@@ -58,9 +58,9 @@
            </van-popup>
            <van-field label="付款方式" name="paymentMethod" borderBottom="true" v-model="form.paymentMethod" placeholder="请输入付款方式">
            </van-field>
            <van-field label="录入人" name="entryPersonName" borderBottom="true" v-model="form.entryPersonName" placeholder="请输入" readonly>
            <van-field label="录入人" name="entryPersonName" borderBottom="true" v-model="form.entryPersonName" placeholder="请输入" disabled>
            </van-field>
            <van-field label="录入日期" name="entryDate" borderBottom="true" v-model="form.entryDate" placeholder="请输入" readonly>
            <van-field label="录入日期" name="entryDate" borderBottom="true" v-model="form.entryDate" placeholder="请输入" disabled>
            </van-field>
            <van-popup v-model:show="showPicker" destroy-on-close position="bottom">
                <van-picker
@@ -129,7 +129,7 @@
            <view class="product-section">
                <view class="section-header">
                    <text class="section-title">产品信息</text>
                    <van-button type="primary" size="small" @click="addProduct" class="add-btn" icon="plus">新增</van-button>
                    <van-button type="primary" size="small" @click="addProduct" class="add-btn" icon="plus"  v-if="operationType !== 'view'">新增</van-button>
                </view>
                <view class="product-card" v-for="(product, idx) in productData" :key="idx">
                    <!-- äº§å“ç±» -->
@@ -139,7 +139,7 @@
                            <text class="product-productCategory">产品 {{ idx + 1 }}</text>
                        </view>
                        <!-- æ“ä½œæŒ‰é’® -->
                        <view class="product-actions">
                        <view class="product-actions"  v-if="operationType !== 'view'">
                            <van-button type="danger" size="mini" @click="removeProduct(idx)" class="del-btn" icon="delete">删除</van-button>
                        </view>
                    </view>
@@ -258,7 +258,7 @@
                    </view>
                </view>
            </view>
            <view class="footer-btns">
            <view class="footer-btns" v-if="operationType !== 'view'">
                <van-button class="cancel-btn" @click="goBack">取消</van-button>
                <van-button class="save-btn" native-type="submit" form-type="submit">保存</van-button>
            </view>
@@ -343,6 +343,9 @@
]);
const addProduct = () => {
    if (productData.value === null) {
        productData.value = []
    }
    productData.value.push({
    productCategory: '',
    specificationModel: '',
@@ -596,6 +599,7 @@
            title: '请添加产品信息',
            icon: 'none'
        });
        return
    }
    form.value.type = 1;
    addOrUpdateSalesLedger(form.value).then((res) => {
@@ -724,7 +728,7 @@
        try {
            editData.value = JSON.parse(editDataStr);
            // å¦‚果是编辑模式,等待数据加载完成后填充表单数据
            if (operationType.value === 'edit' && editData.value) {
            if (operationType.value !== 'add' && editData.value) {
                // ä½¿ç”¨ nextTick ç¡®ä¿æ•°æ®åŠ è½½å®ŒæˆåŽå†å¡«å……
                setTimeout(() => {
                    fillFormData();
src/pages/sales/salesAccount/index.vue
@@ -102,6 +102,8 @@
import { ref } from 'vue';
import { onShow } from '@dcloudio/uni-app';
import {ledgerListPage} from "@/api/salesManagement/salesLedger";
import useUserStore from "@/store/modules/user";
const userStore = useUserStore()
// æœç´¢å…³é”®è¯
const searchKeyword = ref('');
@@ -127,33 +129,54 @@
            // tableLoading.value = false;
    });
};
// æ˜¾ç¤ºç­›é€‰é€‰é¡¹
const showFilterOptions = () => {
    uni.showActionSheet({
        itemList: ['按日期筛选', '按状态筛选', '按金额筛选'],
        success: (res) => {
            console.log('选择了筛选选项:', res.tapIndex);
        }
    });
};
// ç‚¹å‡»åˆ—表项
const handleItemClick = (item) => {
    uni.showToast({
        title: `查看合同: ${item.contractId}`,
        icon: 'none'
    });
};
// æ·»åŠ æ–°è®°å½•
// å¤„理台账信息操作(查看/编辑/新增)
const handleInfo = (type, row) => {
  uni.setStorageSync('operationType', type);
  if (row) {
    uni.setStorageSync('editData', JSON.stringify(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'
    });
  }
  uni.navigateTo({
    url: '/pages/sales/salesAccount/detail'
  });
};
onShow(() => {
src/pages/sales/salesAccount/view.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,311 @@
<template>
  <view class="account-view">
    <!-- é¡¶éƒ¨æ ‡é¢˜æ  -->
    <view class="header">
      <up-icon name="arrow-left" size="20" color="#333" @click="goBack" />
      <text class="title">台账详情</text>
    </view>
    <!-- åŸºæœ¬ä¿¡æ¯å±•示 -->
    <view class="info-section">
      <view class="section-title">基本信息</view>
      <view class="info-grid">
        <view class="info-item">
          <text class="info-label">销售合同号</text>
          <text class="info-value">{{ form.salesContractNo }}</text>
        </view>
        <view class="info-item">
          <text class="info-label">客户合同号</text>
          <text class="info-value highlight">{{ form.customerContractNo }}</text>
        </view>
        <view class="info-item">
          <text class="info-label">客户名称</text>
          <text class="info-value">{{ form.customerName }}</text>
        </view>
        <view class="info-item">
          <text class="info-label">业务员</text>
          <text class="info-value">{{ form.salesman }}</text>
        </view>
        <view class="info-item">
          <text class="info-label">项目名称</text>
          <text class="info-value">{{ form.projectName }}</text>
        </view>
        <view class="info-item">
          <text class="info-label">签订日期</text>
          <text class="info-value">{{ form.executionDate }}</text>
        </view>
        <view class="info-item">
          <text class="info-label">付款方式</text>
          <text class="info-value">{{ form.paymentMethod }}</text>
        </view>
        <view class="info-item">
          <text class="info-label">录入人</text>
          <text class="info-value">{{ form.entryPersonName }}</text>
        </view>
        <view class="info-item">
          <text class="info-label">录入日期</text>
          <text class="info-value">{{ form.entryDate }}</text>
        </view>
      </view>
    </view>
    <!-- äº§å“ä¿¡æ¯å±•示 -->
    <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-header">
          <view class="product-title">
            <van-icon name="description" 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.productCategory }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">规格型号</text>
              <text class="info-value">{{ product.specificationModel }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">单位</text>
              <text class="info-value">{{ product.unit }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">税率(%)</text>
              <text class="info-value">{{ product.taxRate }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">含税单价(元)</text>
              <text class="info-value highlight">{{ product.taxInclusiveUnitPrice }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">数量</text>
              <text class="info-value highlight">{{ product.quantity }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">含税总价(元)</text>
              <text class="info-value highlight">{{ product.taxInclusiveTotalPrice }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">不含税总价(元)</text>
              <text class="info-value highlight">{{ product.taxExclusiveTotalPrice }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">发票类型</text>
              <text class="info-value">{{ product.invoiceType }}</text>
            </view>
          </view>
        </view>
      </view>
    </view>
    <!-- æ— äº§å“ä¿¡æ¯æç¤º -->
    <view class="no-product" v-else>
      <text>暂无产品信息</text>
    </view>
  </view>
</template>
<script setup>
import { onMounted, ref } from 'vue';
import { getSalesLedgerWithProducts } from "@/api/salesManagement/salesLedger";
// è¡¨å•数据
const form = ref({
  id: '',
  salesContractNo: '',
  customerContractNo: '',
  customerId: '',
  customerName: '',
  projectName: '',
  executionDate: '',
  paymentMethod: '',
  entryPerson: '',
  entryPersonName: '',
  entryDate: '',
  salesman: ''
});
// äº§å“æ•°æ®
const productData = ref([]);
// ç¼–辑数据
const editData = ref(null);
// è¿”回上一页
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.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 || '';
};
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;
}
.header {
  display: flex;
  align-items: center;
  background: #fff;
  padding: 1rem 1.25rem;
  border-bottom: 0.0625rem solid #f0f0f0;
  position: sticky;
  top: 0;
  z-index: 100;
  /* å…¼å®¹ iOS åˆ˜æµ·/灵动岛安全区 */
  padding-top: env(safe-area-inset-top);
}
.title {
  flex: 1;
  text-align: center;
  font-size: 1.125rem;
  font-weight: 600;
  color: #333;
}
.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);
}
.section-title {
  font-size: 1rem;
  font-weight: 600;
  color: #333;
  margin-bottom: 1rem;
  padding-bottom: 1rem;
  border-bottom: 0.0625rem solid #e8e8e8;
}
.info-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
}
.info-item {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.info-label {
  font-size: 0.75rem;
  color: #666;
  font-weight: 400;
}
.info-value {
  font-size: 0.875rem;
  color: #333;
  font-weight: 500;
}
.info-value.highlight {
  color: #2979ff;
  font-weight: 600;
}
.product-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-card {
  background: #f8f9fa;
  border-radius: 0.5rem;
  padding: 1rem;
  margin-bottom: 1rem;
}
.product-card:last-child {
  margin-bottom: 0;
}
.product-header {
  display: flex;
  align-items: center;
  padding-bottom: 0.75rem;
  border-bottom: 0.0625rem solid #e8e8e8;
  margin-bottom: 1rem;
}
.product-title {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.product-productCategory {
  font-size: 0.875rem;
  font-weight: 500;
  color: #333;
}
.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;
}
</style>