已添加7个文件
已修改5个文件
2759 ■■■■■ 文件已修改
src/api/personnelManagement/class.js 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/purchase_return_order.js 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/salesManagement/returnOrder.js 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/attendanceCheckin/checkinRules/components/form.vue 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/attendanceCheckin/checkinRules/index.vue 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/attendanceCheckin/index.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/classsSheduling/index.vue 1044 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/purchaseReturnOrder/New.vue 492 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/purchaseReturnOrder/ProductList.vue 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/purchaseReturnOrder/index.vue 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/returnOrder/components/formDia.vue 489 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/returnOrder/index.vue 294 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/personnelManagement/class.js
@@ -5,7 +5,7 @@
// ç»©æ•ˆç®¡ç†-班次-分页查询
export function page(query) {
  return request({
    url: "/performanceShift/page",
    url: "/personalShift/page",
    method: "get",
    params: query,
  });
@@ -14,7 +14,7 @@
// ç»©æ•ˆç®¡ç†-班次-年份分页查询
export function pageYear(query) {
  return request({
    url: "/performanceShift/pageYear",
    url: "/personalShift/pageYear",
    method: "get",
    params: query,
  });
@@ -23,7 +23,7 @@
// ç»©æ•ˆç®¡ç†-班次-排班
export function add(data) {
  return request({
    url: "/performanceShift/add",
    url: "/personalShift/add",
    method: "post",
    data: data,
  });
@@ -68,7 +68,7 @@
// ç»©æ•ˆç®¡ç†-班次-导出
export function exportFile(query) {
  return request({
    url: "/performanceShift/export",
    url: "/personalShift/export",
    method: "get",
    params: query,
  });
@@ -86,7 +86,7 @@
// ç»©æ•ˆç®¡ç†-班次-班次状态修改
export function update(data) {
  return request({
    url: "/performanceShift/update",
    url: "/personalShift/update",
    method: "post",
    data: data,
  });
@@ -105,4 +105,13 @@
    url: '/system/user/userListNoPage',
    method: 'get'
  })
}
// æŸ¥è¯¢åœ¨èŒå‘˜å·¥å°è´¦
export function staffOnJobListPage(query) {
    return request({
        url: '/staff/staffOnJob/listPage',
        method: 'get',
        params: query,
    })
}
src/api/procurementManagement/purchase_return_order.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
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
    });
}
src/api/salesManagement/returnOrder.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,82 @@
import request from "@/utils/request";
// é”€å”®é€€è´§-查询
// /returnManagement/listPage
export function returnManagementList(query) {
  return request({
    url: "/returnManagement/listPage",
    method: "get",
    params: query,
  });
}
// é”€å”®é€€è´§-添加
// /returnManagement/add
export function returnManagementAdd(data) {
  return request({
    url: "/returnManagement/add",
    method: "post",
    data: data,
  });
}
// é”€å”®é€€è´§-修改
// /returnManagement/update
export function returnManagementUpdate(data) {
  return request({
    url: "/returnManagement/update",
    method: "post",
    data: data,
  });
}
// é”€å”®é€€è´§-删除
// /returnManagement/del
export function returnManagementDel(query) {
  return request({
    url: "/returnManagement/del",
    method: "get",
    params: query,
  });
}
// é”€å”®é€€è´§-查询
// /returnManagement/getById
export function returnManagementGetById(query) {
  return request({
    url: "/returnManagement/getById",
    method: "get",
    params: query,
  });
}
// é”€å”®é€€è´§-根据出库单查询销售订单以及产品信息
// /returnManagement/getByShippingId
export function returnManagementGetByShippingId(query) {
  return request({
    url: "/returnManagement/getByShippingId",
    method: "get",
    params: query,
  });
}
// é€šè¿‡å®¢æˆ·åç§°æŸ¥è¯¢
// /shippingInfo/getByCustomerName
export function getSalesLedger(query) {
    return request({
        url: '/shippingInfo/getByCustomerName',
        method: 'get',
        params: query,
    })
}
// å¤„理
// /returnManagement/handle
export function returnManagementHandle(data) {
  return request({
    url: "/returnManagement/handle",
    method: "get",
    params: data,
  });
}
src/views/personnelManagement/attendanceCheckin/checkinRules/components/form.vue
@@ -28,6 +28,18 @@
                  :disabled="operationType === 'view'" />
      </el-form-item> -->
      <!-- æ‰“卡范围 -->
      <el-form-item label="班次"
                    prop="shift">
        <el-select v-model="form.shift"
                   placeholder="请选择班次"
                   :disabled="operationType === 'view'"
                   style="width: 100%">
          <el-option v-for="item in shifts_list"
                     :key="item.value"
                     :label="item.label"
                     :value="item.value" />
        </el-select>
      </el-form-item>
      <el-form-item label="打卡范围(m)"
                    prop="radius">
        <el-input-number v-model="form.radius"
@@ -115,6 +127,7 @@
  import { Position } from "@element-plus/icons-vue";
  import { deptTreeSelect } from "@/api/system/user.js";
  import { addAttendanceRule } from "@/api/personnelManagement/attendanceRules.js";
  import { useDict } from "@/utils/dict";
  const props = defineProps({
    modelValue: {
@@ -145,6 +158,9 @@
    return "查看班次";
  });
  // èŽ·å–ç­æ¬¡å­—å…¸å€¼
  const { shifts_list } = useDict("shifts_list");
  // è¡¨å•数据
  const formRef = ref();
  const form = reactive({
@@ -156,6 +172,7 @@
    radius: 100,
    startAt: "09:00",
    endAt: "18:00",
    shift: "",
  });
  // è¡¨å•验证规则
@@ -166,6 +183,7 @@
    ],
    longitude: [{ required: true, message: "请选择打卡位置", trigger: "blur" }],
    latitude: [{ required: true, message: "请选择打卡位置", trigger: "blur" }],
    shift: [{ required: true, message: "请选择班次", trigger: "change" }],
    radius: [{ required: true, message: "请输入打卡范围", trigger: "blur" }],
    startAt: [{ required: true, message: "请选择上班时间", trigger: "change" }],
    endAt: [
@@ -405,6 +423,7 @@
          radius: 100,
          startAt: "09:00",
          endAt: "18:00",
          shift: "",
        });
        // å¦‚果是编辑或查看,填充数据
src/views/personnelManagement/attendanceCheckin/checkinRules/index.vue
@@ -67,6 +67,11 @@
            {{ getDeptNameById(scope.row.sysDeptId) }}
          </template>
        </el-table-column>
        <el-table-column label="班次">
          <template #default="scope">
            {{ getShiftNameByValue(scope.row.shift) }}
          </template>
        </el-table-column>
        <el-table-column prop="locationName"
                         label="地点名称" />
        <el-table-column prop="longitude"
@@ -122,7 +127,14 @@
<script setup>
  import { ref, reactive, onMounted } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import { Plus, Edit, Delete, Search, Refresh } from "@element-plus/icons-vue";
  import {
    Plus,
    Edit,
    Delete,
    Search,
    Refresh,
    ArrowLeft,
  } from "@element-plus/icons-vue";
  import Pagination from "@/components/Pagination/index.vue";
  import RuleForm from "./components/form.vue";
  import { deptTreeSelect } from "@/api/system/user.js";
@@ -130,6 +142,7 @@
    getAttendanceRules,
    deleteAttendanceRule,
  } from "@/api/personnelManagement/attendanceRules.js";
  import { useDict } from "@/utils/dict";
  const { proxy } = getCurrentInstance();
@@ -152,6 +165,8 @@
  // éƒ¨é—¨é€‰é¡¹
  const deptOptions = ref([]);
  // èŽ·å–ç­æ¬¡å­—å…¸å€¼
  const { shifts_list } = useDict("shifts_list");
  // å¼¹çª—控制
  const dialogVisible = ref(false);
@@ -168,6 +183,13 @@
    ).padStart(2, "0")}`;
  };
  // æ ¹æ®ç­æ¬¡å€¼èŽ·å–ç­æ¬¡åç§°
  const getShiftNameByValue = value => {
    if (!value) return "";
    const shift = shifts_list.value.find(item => item.value === value);
    return shift ? shift.label : value;
  };
  // èŽ·å–éƒ¨é—¨åˆ—è¡¨
  const fetchDeptOptions = () => {
    deptTreeSelect().then(response => {
src/views/personnelManagement/attendanceCheckin/index.vue
@@ -60,15 +60,6 @@
      </el-descriptions>
    </el-card> -->
    <div class="attendance-operation">
      <el-button @click="handleBack"
                 type="default"
                 size="small"
                 style="margin-right: 16px">
        <el-icon>
          <ArrowLeft />
        </el-icon>
        è¿”回排班管理
      </el-button>
      <!-- æŸ¥è¯¢æ¡ä»¶ï¼ˆç®¡ç†å‘˜è€ƒå‹¤æ—¥æŠ¥ï¼‰ -->
      <el-form :model="searchForm"
               :inline="true"
@@ -455,13 +446,6 @@
    fetchTodayData();
    fetchDeptOptions();
  });
  // è¿”回排班管理页面
  const handleBack = () => {
    router.push({
      path: "/personnelManagement/classsSheduling/index",
    });
  };
  onBeforeUnmount(() => {
    if (timer) {
src/views/personnelManagement/classsSheduling/index.vue
@@ -36,7 +36,7 @@
                      @keyup.enter="refreshTable()" />
          </div>
          <div class="search-item">
            <el-tree-select v-model="query.deptId"
            <el-tree-select v-model="query.sysDeptId"
                            :data="deptOptions"
                            :props="{ value: 'id', label: 'label', children: 'children' }"
                            value-key="id"
@@ -151,21 +151,21 @@
                               class="shift-dropdown">
                    <div class="shift-box"
                         :class="{
                      'shift-box-early': m.shift === '0',
                      'shift-box-mid': m.shift === '1',
                      'shift-box-night': m.shift === '2',
                      'shift-box-rest': m.shift === '3',
                      'shift-box-leave': m.shift === '4',
                      'shift-box-other': m.shift === '5',
                      'shift-box-business': m.shift === '6',
                      'shift-box-early': m.shift === '早班',
                      'shift-box-mid': m.shift === '中班',
                      'shift-box-night': m.shift === '夜班',
                      'shift-box-rest': m.shift === '休息',
                      'shift-box-leave': m.shift === '请假',
                      'shift-box-other': m.shift === '夜11',
                      'shift-box-business': m.shift === '夜12',
                    }">
                      <span class="shift-text">{{ getShiftByDic(m.shift) || '—' }}</span>
                      <span class="shift-text">{{ getShiftNameByValue(m.shift) || '—' }}</span>
                    </div>
                    <template #dropdown>
                      <el-dropdown-menu>
                        <el-dropdown-item v-for="(n, j) in classType"
                                          :key="'h' + j"
                                          :command="n.id">{{ n.locationName
                                          :command="n.id">{{ n.shift || '—'
                          }}</el-dropdown-item>
                      </el-dropdown-menu>
                    </template>
@@ -286,9 +286,9 @@
                     clearable
                     collapse-tags>
            <el-option v-for="item in personList"
                       :key="item.userId"
                       :label="item.nickName"
                       :value="item.userId">
                       :key="item.id"
                       :label="item.staffName"
                       :value="item.id">
            </el-option>
          </el-select>
        </div>
@@ -304,7 +304,7 @@
                     style="width: 100%">
            <el-option v-for="item in classType"
                       :key="item.id"
                       :label="item.locationName"
                       :label="getShiftNameByValue(item.shift)"
                       :value="item.id">
            </el-option>
          </el-select>
@@ -331,21 +331,23 @@
    add,
    exportFile,
    update,
    selectUserCondition,
    staffOnJobListPage,
  } from "@/api/personnelManagement/class";
  import { deptTreeSelect } from "@/api/system/user.js";
  import { getAttendanceRules } from "@/api/personnelManagement/attendanceRules.js";
  import { useDict } from "@/utils/dict";
  const { proxy } = getCurrentInstance();
  const router = useRouter();
  // æŸ¥è¯¢æ¡ä»¶
  const query = reactive({
    userName: "",
    deptId: "",
    sysDeptId: "",
    year: new Date(),
    month: new Date().getMonth() + 1,
  });
  // èŽ·å–ç­æ¬¡å­—å…¸å€¼
  const { shifts_list } = useDict("shifts_list");
  // æœˆä»½é€‰é¡¹
  const monthOptions = [
    { value: 1, label: "1月" },
@@ -366,38 +368,7 @@
  const deptOptions = ref([]);
  // å‘¨åˆ—表
  const weeks = ref([
    { weekNum: 1, week: "周一", day: "01" },
    { weekNum: 1, week: "周二", day: "02" },
    { weekNum: 1, week: "周三", day: "03" },
    { weekNum: 1, week: "周四", day: "04" },
    { weekNum: 1, week: "周五", day: "05" },
    { weekNum: 1, week: "周六", day: "06" },
    { weekNum: 2, week: "周日", day: "07" },
    { weekNum: 2, week: "周一", day: "08" },
    { weekNum: 2, week: "周二", day: "09" },
    { weekNum: 2, week: "周三", day: "10" },
    { weekNum: 2, week: "周四", day: "11" },
    { weekNum: 2, week: "周五", day: "12" },
    { weekNum: 2, week: "周六", day: "13" },
    { weekNum: 3, week: "周日", day: "14" },
    { weekNum: 3, week: "周一", day: "15" },
    { weekNum: 3, week: "周二", day: "16" },
    { weekNum: 3, week: "周三", day: "17" },
    { weekNum: 3, week: "周四", day: "18" },
    { weekNum: 3, week: "周五", day: "19" },
    { weekNum: 3, week: "周六", day: "20" },
    { weekNum: 4, week: "周日", day: "21" },
    { weekNum: 4, week: "周一", day: "22" },
    { weekNum: 4, week: "周二", day: "23" },
    { weekNum: 4, week: "周三", day: "24" },
    { weekNum: 4, week: "周四", day: "25" },
    { weekNum: 4, week: "周五", day: "26" },
    { weekNum: 4, week: "周六", day: "27" },
    { weekNum: 5, week: "周日", day: "28" },
    { weekNum: 5, week: "周一", day: "29" },
    { weekNum: 5, week: "周二", day: "30" },
  ]);
  const weeks = ref([]);
  // ç­æ¬¡ç±»åž‹
  const classType = ref([]);
@@ -422,314 +393,7 @@
  });
  // åˆ—表数据
  const listForm = ref([
    {
      id: 1,
      name: "张三",
      monthlyAttendance: {
        totalAttendance: 22,
        æ—©ç­: 10,
        ä¸­ç­: 8,
        å¤œç­: 4,
        ä¼‘息: 6,
        è¯·å‡: 0,
        å‡ºå·®: 0,
      },
      day0: 10,
      day1: 8,
      day2: 4,
      day3: 6,
      day4: 0,
      day6: 0,
      list: [
        { id: 1, shift: "0" },
        { id: 2, shift: "0" },
        { id: 3, shift: "1" },
        { id: 4, shift: "1" },
        { id: 5, shift: "2" },
        { id: 6, shift: "2" },
        { id: 7, shift: "3" },
        { id: 8, shift: "0" },
        { id: 9, shift: "0" },
        { id: 10, shift: "1" },
        { id: 11, shift: "1" },
        { id: 12, shift: "2" },
        { id: 13, shift: "2" },
        { id: 14, shift: "3" },
        { id: 15, shift: "0" },
        { id: 16, shift: "0" },
        { id: 17, shift: "1" },
        { id: 18, shift: "1" },
        { id: 19, shift: "2" },
        { id: 20, shift: "2" },
        { id: 21, shift: "3" },
        { id: 22, shift: "0" },
        { id: 23, shift: "0" },
        { id: 24, shift: "1" },
        { id: 25, shift: "1" },
        { id: 26, shift: "2" },
        { id: 27, shift: "2" },
        { id: 28, shift: "3" },
        { id: 29, shift: "0" },
        { id: 30, shift: "0" },
      ],
    },
    {
      id: 2,
      name: "李四",
      monthlyAttendance: {
        totalAttendance: 20,
        æ—©ç­: 8,
        ä¸­ç­: 6,
        å¤œç­: 6,
        ä¼‘息: 8,
        è¯·å‡: 2,
        å‡ºå·®: 0,
      },
      day0: 8,
      day1: 6,
      day2: 6,
      day3: 8,
      day4: 2,
      day6: 0,
      list: [
        { id: 31, shift: "1" },
        { id: 32, shift: "1" },
        { id: 33, shift: "2" },
        { id: 34, shift: "2" },
        { id: 35, shift: "3" },
        { id: 36, shift: "0" },
        { id: 37, shift: "0" },
        { id: 38, shift: "1" },
        { id: 39, shift: "1" },
        { id: 40, shift: "2" },
        { id: 41, shift: "2" },
        { id: 42, shift: "3" },
        { id: 43, shift: "0" },
        { id: 44, shift: "0" },
        { id: 45, shift: "1" },
        { id: 46, shift: "1" },
        { id: 47, shift: "2" },
        { id: 48, shift: "2" },
        { id: 49, shift: "3" },
        { id: 50, shift: "0" },
        { id: 51, shift: "0" },
        { id: 52, shift: "4" },
        { id: 53, shift: "4" },
        { id: 54, shift: "1" },
        { id: 55, shift: "1" },
        { id: 56, shift: "2" },
        { id: 57, shift: "2" },
        { id: 58, shift: "3" },
        { id: 59, shift: "0" },
        { id: 60, shift: "0" },
      ],
    },
    {
      id: 3,
      name: "王五",
      monthlyAttendance: {
        totalAttendance: 23,
        æ—©ç­: 9,
        ä¸­ç­: 9,
        å¤œç­: 5,
        ä¼‘息: 5,
        è¯·å‡: 0,
        å‡ºå·®: 2,
      },
      day0: 9,
      day1: 9,
      day2: 5,
      day3: 5,
      day4: 0,
      day6: 2,
      list: [
        { id: 61, shift: "2" },
        { id: 62, shift: "2" },
        { id: 63, shift: "3" },
        { id: 64, shift: "0" },
        { id: 65, shift: "0" },
        { id: 66, shift: "1" },
        { id: 67, shift: "1" },
        { id: 68, shift: "2" },
        { id: 69, shift: "2" },
        { id: 70, shift: "3" },
        { id: 71, shift: "0" },
        { id: 72, shift: "0" },
        { id: 73, shift: "1" },
        { id: 74, shift: "1" },
        { id: 75, shift: "2" },
        { id: 76, shift: "2" },
        { id: 77, shift: "3" },
        { id: 78, shift: "0" },
        { id: 79, shift: "0" },
        { id: 80, shift: "1" },
        { id: 81, shift: "1" },
        { id: 82, shift: "6" },
        { id: 83, shift: "6" },
        { id: 84, shift: "2" },
        { id: 85, shift: "2" },
        { id: 86, shift: "3" },
        { id: 87, shift: "0" },
        { id: 88, shift: "0" },
        { id: 89, shift: "1" },
        { id: 90, shift: "1" },
      ],
    },
    {
      id: 4,
      name: "张三",
      monthlyAttendance: {
        totalAttendance: 22,
        æ—©ç­: 10,
        ä¸­ç­: 8,
        å¤œç­: 4,
        ä¼‘息: 6,
        è¯·å‡: 0,
        å‡ºå·®: 0,
      },
      day0: 10,
      day1: 8,
      day2: 4,
      day3: 6,
      day4: 0,
      day6: 0,
      list: [
        { id: 1, shift: "0" },
        { id: 2, shift: "0" },
        { id: 3, shift: "1" },
        { id: 4, shift: "1" },
        { id: 5, shift: "2" },
        { id: 6, shift: "2" },
        { id: 7, shift: "3" },
        { id: 8, shift: "0" },
        { id: 9, shift: "0" },
        { id: 10, shift: "1" },
        { id: 11, shift: "1" },
        { id: 12, shift: "2" },
        { id: 13, shift: "2" },
        { id: 14, shift: "3" },
        { id: 15, shift: "0" },
        { id: 16, shift: "0" },
        { id: 17, shift: "1" },
        { id: 18, shift: "1" },
        { id: 19, shift: "2" },
        { id: 20, shift: "2" },
        { id: 21, shift: "3" },
        { id: 22, shift: "0" },
        { id: 23, shift: "0" },
        { id: 24, shift: "1" },
        { id: 25, shift: "1" },
        { id: 26, shift: "2" },
        { id: 27, shift: "2" },
        { id: 28, shift: "3" },
        { id: 29, shift: "0" },
        { id: 30, shift: "0" },
      ],
    },
    {
      id: 5,
      name: "张三",
      monthlyAttendance: {
        totalAttendance: 22,
        æ—©ç­: 10,
        ä¸­ç­: 8,
        å¤œç­: 4,
        ä¼‘息: 6,
        è¯·å‡: 0,
        å‡ºå·®: 0,
      },
      day0: 10,
      day1: 8,
      day2: 4,
      day3: 6,
      day4: 0,
      day6: 0,
      list: [
        { id: 1, shift: "0" },
        { id: 2, shift: "0" },
        { id: 3, shift: "1" },
        { id: 4, shift: "1" },
        { id: 5, shift: "2" },
        { id: 6, shift: "2" },
        { id: 7, shift: "3" },
        { id: 8, shift: "0" },
        { id: 9, shift: "0" },
        { id: 10, shift: "1" },
        { id: 11, shift: "1" },
        { id: 12, shift: "2" },
        { id: 13, shift: "2" },
        { id: 14, shift: "3" },
        { id: 15, shift: "0" },
        { id: 16, shift: "0" },
        { id: 17, shift: "1" },
        { id: 18, shift: "1" },
        { id: 19, shift: "2" },
        { id: 20, shift: "2" },
        { id: 21, shift: "3" },
        { id: 22, shift: "0" },
        { id: 23, shift: "0" },
        { id: 24, shift: "1" },
        { id: 25, shift: "1" },
        { id: 26, shift: "2" },
        { id: 27, shift: "2" },
        { id: 28, shift: "3" },
        { id: 29, shift: "0" },
        { id: 30, shift: "0" },
      ],
    },
    {
      id: 6,
      name: "张三",
      monthlyAttendance: {
        totalAttendance: 22,
        æ—©ç­: 10,
        ä¸­ç­: 8,
        å¤œç­: 4,
        ä¼‘息: 6,
        è¯·å‡: 0,
        å‡ºå·®: 0,
      },
      day0: 10,
      day1: 8,
      day2: 4,
      day3: 6,
      day4: 0,
      day6: 0,
      list: [
        { id: 1, shift: "0" },
        { id: 2, shift: "0" },
        { id: 3, shift: "1" },
        { id: 4, shift: "1" },
        { id: 5, shift: "2" },
        { id: 6, shift: "2" },
        { id: 7, shift: "3" },
        { id: 8, shift: "0" },
        { id: 9, shift: "0" },
        { id: 10, shift: "1" },
        { id: 11, shift: "1" },
        { id: 12, shift: "2" },
        { id: 13, shift: "2" },
        { id: 14, shift: "3" },
        { id: 15, shift: "0" },
        { id: 16, shift: "0" },
        { id: 17, shift: "1" },
        { id: 18, shift: "1" },
        { id: 19, shift: "2" },
        { id: 20, shift: "2" },
        { id: 21, shift: "3" },
        { id: 22, shift: "0" },
        { id: 23, shift: "0" },
        { id: 24, shift: "1" },
        { id: 25, shift: "1" },
        { id: 26, shift: "2" },
        { id: 27, shift: "2" },
        { id: 28, shift: "3" },
        { id: 29, shift: "0" },
        { id: 30, shift: "0" },
      ],
    },
  ]);
  const listForm = ref([]);
  // å½“前页
  const currentPage = ref(1);
@@ -747,613 +411,7 @@
  const monthList = ref([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
  // å¹´åº¦åˆ—表
  const yearList = ref([
    {
      id: 1,
      name: "张三",
      work_time: 260,
      day0: 98,
      day1: 78,
      day2: 46,
      day3: 74,
      day4: 14,
      day6: 10,
      monthList: [
        {
          totalMonthAttendance: 22,
          day0: 10,
          day1: 8,
          day2: 4,
          day3: 6,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 19,
          day0: 7,
          day1: 6,
          day2: 6,
          day3: 9,
          day4: 3,
          day6: 0,
        },
        {
          totalMonthAttendance: 23,
          day0: 9,
          day1: 9,
          day2: 5,
          day3: 5,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 7,
          day2: 6,
          day3: 7,
          day4: 0,
          day6: 1,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 18,
          day0: 6,
          day1: 6,
          day2: 6,
          day3: 8,
          day4: 4,
          day6: 0,
        },
        {
          totalMonthAttendance: 23,
          day0: 10,
          day1: 9,
          day2: 4,
          day3: 5,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 7,
          day2: 6,
          day3: 7,
          day4: 0,
          day6: 1,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 19,
          day0: 7,
          day1: 7,
          day2: 5,
          day3: 8,
          day4: 2,
          day6: 0,
        },
        {
          totalMonthAttendance: 23,
          day0: 10,
          day1: 9,
          day2: 4,
          day3: 5,
          day4: 0,
          day6: 0,
        },
      ],
    },
    {
      id: 2,
      name: "李四",
      work_time: 252,
      day0: 90,
      day1: 72,
      day2: 50,
      day3: 76,
      day4: 18,
      day6: 8,
      monthList: [
        {
          totalMonthAttendance: 21,
          day0: 9,
          day1: 7,
          day2: 5,
          day3: 7,
          day4: 2,
          day6: 0,
        },
        {
          totalMonthAttendance: 18,
          day0: 6,
          day1: 6,
          day2: 6,
          day3: 10,
          day4: 4,
          day6: 0,
        },
        {
          totalMonthAttendance: 22,
          day0: 8,
          day1: 8,
          day2: 6,
          day3: 6,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 20,
          day0: 7,
          day1: 6,
          day2: 7,
          day3: 8,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 7,
          day2: 6,
          day3: 7,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 17,
          day0: 5,
          day1: 5,
          day2: 7,
          day3: 9,
          day4: 5,
          day6: 0,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 7,
          day2: 6,
          day3: 7,
          day4: 1,
          day6: 0,
        },
        {
          totalMonthAttendance: 20,
          day0: 7,
          day1: 6,
          day2: 7,
          day3: 8,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 7,
          day2: 6,
          day3: 7,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 18,
          day0: 6,
          day1: 6,
          day2: 6,
          day3: 9,
          day4: 3,
          day6: 0,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 0,
          day6: 0,
        },
      ],
    },
    {
      id: 3,
      name: "王五",
      work_time: 268,
      day0: 102,
      day1: 82,
      day2: 48,
      day3: 70,
      day4: 10,
      day6: 14,
      monthList: [
        {
          totalMonthAttendance: 23,
          day0: 10,
          day1: 9,
          day2: 4,
          day3: 5,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 24,
          day0: 10,
          day1: 9,
          day2: 5,
          day3: 4,
          day4: 0,
          day6: 3,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 23,
          day0: 10,
          day1: 8,
          day2: 5,
          day3: 5,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 7,
          day2: 6,
          day3: 7,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 23,
          day0: 10,
          day1: 9,
          day2: 4,
          day3: 5,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 23,
          day0: 10,
          day1: 9,
          day2: 4,
          day3: 5,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 7,
          day2: 6,
          day3: 7,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 24,
          day0: 10,
          day1: 9,
          day2: 5,
          day3: 4,
          day4: 0,
          day6: 3,
        },
      ],
    },
    {
      id: 4,
      name: "赵六",
      work_time: 248,
      day0: 88,
      day1: 70,
      day2: 50,
      day3: 78,
      day4: 20,
      day6: 6,
      monthList: [
        {
          totalMonthAttendance: 20,
          day0: 8,
          day1: 6,
          day2: 6,
          day3: 8,
          day4: 3,
          day6: 0,
        },
        {
          totalMonthAttendance: 17,
          day0: 5,
          day1: 5,
          day2: 7,
          day3: 10,
          day4: 5,
          day6: 0,
        },
        {
          totalMonthAttendance: 21,
          day0: 7,
          day1: 7,
          day2: 7,
          day3: 7,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 19,
          day0: 6,
          day1: 6,
          day2: 7,
          day3: 9,
          day4: 2,
          day6: 1,
        },
        {
          totalMonthAttendance: 20,
          day0: 7,
          day1: 7,
          day2: 6,
          day3: 8,
          day4: 2,
          day6: 0,
        },
        {
          totalMonthAttendance: 16,
          day0: 4,
          day1: 4,
          day2: 8,
          day3: 10,
          day4: 6,
          day6: 0,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 7,
          day2: 6,
          day3: 7,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 20,
          day0: 7,
          day1: 7,
          day2: 6,
          day3: 8,
          day4: 2,
          day6: 0,
        },
        {
          totalMonthAttendance: 19,
          day0: 6,
          day1: 6,
          day2: 7,
          day3: 9,
          day4: 2,
          day6: 1,
        },
        {
          totalMonthAttendance: 20,
          day0: 7,
          day1: 7,
          day2: 6,
          day3: 8,
          day4: 2,
          day6: 0,
        },
        {
          totalMonthAttendance: 18,
          day0: 6,
          day1: 6,
          day2: 6,
          day3: 9,
          day4: 4,
          day6: 0,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 7,
          day2: 6,
          day3: 7,
          day4: 1,
          day6: 1,
        },
      ],
    },
    {
      id: 5,
      name: "钱七",
      work_time: 266,
      day0: 100,
      day1: 84,
      day2: 46,
      day3: 72,
      day4: 12,
      day6: 12,
      monthList: [
        {
          totalMonthAttendance: 23,
          day0: 10,
          day1: 9,
          day2: 4,
          day3: 5,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 21,
          day0: 8,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 23,
          day0: 9,
          day1: 9,
          day2: 5,
          day3: 5,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 20,
          day0: 7,
          day1: 7,
          day2: 6,
          day3: 8,
          day4: 2,
          day6: 0,
        },
        {
          totalMonthAttendance: 23,
          day0: 10,
          day1: 9,
          day2: 4,
          day3: 5,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 0,
          day6: 0,
        },
        {
          totalMonthAttendance: 23,
          day0: 9,
          day1: 9,
          day2: 5,
          day3: 5,
          day4: 0,
          day6: 2,
        },
        {
          totalMonthAttendance: 22,
          day0: 9,
          day1: 8,
          day2: 5,
          day3: 6,
          day4: 1,
          day6: 1,
        },
        {
          totalMonthAttendance: 20,
          day0: 7,
          day1: 7,
          day2: 6,
          day3: 8,
          day4: 2,
          day6: 0,
        },
        {
          totalMonthAttendance: 23,
          day0: 10,
          day1: 9,
          day2: 4,
          day3: 5,
          day4: 0,
          day6: 0,
        },
      ],
    },
  ]);
  const yearList = ref([]);
  // å¯¼å‡ºåŠ è½½çŠ¶æ€
  const downLoading = ref(false);
@@ -1365,6 +423,13 @@
        JSON.parse(JSON.stringify(response.data))
      );
    });
  };
  // æ ¹æ®ç­æ¬¡å€¼èŽ·å–ç­æ¬¡åç§°
  const getShiftNameByValue = value => {
    if (!value) return "";
    const shift = shifts_list.value.find(item => item.value === value);
    return shift ? shift.label : value;
  };
  // è¿‡æ»¤ç¦ç”¨çš„部门
@@ -1386,7 +451,7 @@
    yearList.value = [];
    currentPage.value = 1;
    query.userName = "";
    query.deptId = "";
    query.sysDeptId = "";
    query.year = new Date();
    query.month = new Date().getMonth() + 1;
    if (query.month) {
@@ -1455,7 +520,7 @@
      current: currentPage.value,
      time: year + "-" + month + "-01 00:00:00",
      userName: query.userName,
      deptId: query.deptId,
      sysDeptId: query.sysDeptId,
    })
      .then(res => {
        pageLoading.value = false;
@@ -1494,7 +559,7 @@
      current: currentPage.value,
      time: year + "-01-01 00:00:00",
      userName: query.userName,
      deptId: query.deptId,
      sysDeptId: query.sysDeptId,
    }).then(res => {
      pageLoading.value = false;
      total.value = res.data.total;
@@ -1560,8 +625,8 @@
    add({
      startWeek,
      endWeek,
      userId: schedulingQuery.userId.join(","),
      shift: schedulingQuery.shift,
      staffOnJobId: schedulingQuery.userId.join(","),
      personalAttendanceLocationConfigId: schedulingQuery.shift,
    })
      .then(res => {
        loading.value = false;
@@ -1604,7 +669,7 @@
    exportFile({
      time,
      userName: query.userName,
      deptId: query.deptId,
      sysDeptId: query.sysDeptId,
      isMonth: query.month ? true : false,
    })
      .then(res => {
@@ -1627,15 +692,20 @@
  };
  // å¤„理命令
  const handleCommand = (e, m) => {
    if (e != m.shift) {
      update({
        id: m.id,
        shift: e,
      }).then(res => {
        proxy.$modal.msgSuccess("操作成功");
        m.shift = e;
      });
    }
    // if (e != m.shift) {
    update({
      id: m.id,
      personalAttendanceLocationConfigId: e,
    }).then(res => {
      proxy.$modal.msgSuccess("操作成功");
      // m.shift = e;
      if (query.month) {
        init();
      } else {
        initYear();
      }
    });
    // }
  };
  // æŸ¥è¯¢è§„则列表
  const fetchData = () => {
@@ -1649,15 +719,19 @@
    //   let arr = res.data;
    //   personList.value = arr;
    // });
    selectUserCondition().then(res => {
      let arr = res.data;
    staffOnJobListPage({
      current: -1,
      size: -1,
      staffState: 1,
    }).then(res => {
      let arr = res.data.records;
      personList.value = arr;
    });
  };
  // æ ¹æ®å­—典获取日期
  const getDayByDic = e => {
    let obj = classType.value.find(m => m.locationName == e);
    let obj = classType.value.find(m => m.shift == e);
    if (obj) {
      return obj.id;
    }
@@ -1665,9 +739,9 @@
  // æ ¹æ®å­—典获取班次
  const getShiftByDic = e => {
    let obj = classType.value.find(m => m.id == e);
    let obj = classType.value.find(m => m.shift == e);
    if (obj) {
      return obj.locationName;
      return obj.shift;
    }
    return "无";
  };
@@ -1900,7 +974,7 @@
  .user-stats {
    /* display: flex; */
    /* flex-wrap: wrap;
                                                                                                                                                                        gap: 10px; */
                                                                                                                                                                                                                                                                                      gap: 10px; */
    margin-bottom: 4px;
  }
src/views/procurementManagement/purchaseReturnOrder/New.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,492 @@
<template>
  <div>
    <el-dialog
        v-model="isShow"
        title="新增采购退货"
        width="1200"
        @close="closeModal"
    >
      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef" :inline="true">
        <el-form-item
            label="退料单号"
            prop="no"
            :rules="[
                {
                  required: !formState.isDefaultNo,
                  message: '请输入退料单号',
                  trigger: 'blur',
                }
              ]"
        >
          <el-input
              v-model="formState.no"
              :placeholder="formState.isDefaultNo ? '使用系统编号' : '请输入退料单号'"
              :disabled="formState.isDefaultNo"
          >
            <template #append>
              <el-checkbox v-model="formState.isDefaultNo" size="large" @change="handleChangeIsDefaultNo" />
            </template>
          </el-input>
        </el-form-item>
        <el-form-item
            label="退货方式"
            prop="returnType"
            :rules="[
                {
                  required: true,
                  message: '请选择退货方式',
                  trigger: 'change',
                }
              ]"
        >
          <el-select
              v-model="formState.returnType"
              placeholder="请选择退货方式"
              style="width: 240px"
          >
            <el-option label="退货退款" :value="0" />
            <el-option label="拒收" :value="1" />
          </el-select>
        </el-form-item>
        <el-form-item
            label="供应商名称"
            prop="supplierId"
            :rules="[
              {
                required: true,
                message: '请选择供应商',
                trigger: 'change',
              }
            ]"
        >
          <el-select
              v-model="formState.supplierId"
              placeholder="请选择供应商"
              style="width: 240px"
              @focus="fetchSupplierOptions"
              @change="handleChangeSupplierId"
          >
            <el-option
              v-for="item in supplierOptions"
              :key="item.id"
              :label="item.supplierName"
              :value="item.id"
            />
          </el-select>
        </el-form-item>
        <el-form-item
            label="项目"
            prop="projectId"
        >
          <el-select
              v-model="formState.projectId"
              placeholder="请选择项目"
              style="width: 240px"
              @focus="fetchProjectOptions"
          >
            <el-option
                v-for="item in projectOptions"
                :key="item.id"
                :label="item.name"
                :value="item.id"
            />
          </el-select>
        </el-form-item>
        <el-form-item
          label="项目阶段"
          prop="projectPhase"
        >
          <el-select
              v-model="formState.projectPhase"
              placeholder="请选择项目阶段"
              style="width: 240px"
          >
            <el-option
                v-for="item in projectStageOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
            />
          </el-select>
        </el-form-item>
        <el-form-item
            label="制作日期"
            prop="preparedAt"
            :rules="[
            {
              required: true,
              message: '请选择制作日期',
              trigger: 'change',
            }
          ]"
        >
          <el-date-picker
              v-model="formState.preparedAt"
              value-format="YYYY-MM-DD"
              format="YYYY-MM-DD"
              type="date"
              placeholder="请选择制作日期"
              style="width: 240px"
              clearable />
        </el-form-item>
        <el-form-item
            label="制单人:"
            prop="preparedUserId"
            :rules="[
              {
                required: true,
                message: '请选择制单人',
                trigger: 'change',
              }
            ]"
        >
          <el-select
              v-model="formState.preparedUserId"
              placeholder="请选择"
              clearable
              filterable
              default-first-option
              :reserve-keyword="false"
              style="width: 240px"
              @focus="fetchUserOptions"
          >
            <el-option
                v-for="item in userOptions"
                :key="item.userId"
                :label="item.nickName"
                :value="item.userId"
            />
          </el-select>
        </el-form-item>
        <el-form-item
            label="退料人:"
            prop="returnUserId"
            :rules="[
              {
                required: true,
                message: '请选择退料人',
                trigger: 'change',
              }
            ]"
        >
          <el-select
              v-model="formState.returnUserId"
              placeholder="请选择"
              clearable
              filterable
              default-first-option
              style="width: 240px"
              :reserve-keyword="false"
              @focus="fetchUserOptions"
          >
            <el-option
                v-for="item in userOptions"
                :key="item.userId"
                :label="item.nickName"
                :value="item.userId"
            />
          </el-select>
        </el-form-item>
        <el-form-item
            label="采购合同号:"
            prop="purchaseLedgerId"
            :rules="[
              {
                required: true,
                message: '请选择采购合同号',
                trigger: 'change',
              }
            ]"
        >
          <el-select
              v-model="formState.purchaseLedgerId"
              placeholder="请选择"
              clearable
              filterable
              default-first-option
              style="width: 240px"
              :reserve-keyword="false"
              @change="handleChangePurchaseLedgerId"
          >
            <el-option
                v-for="item in purchaseLedgerOptions"
                :key="item.id"
                :label="item.purchaseContractNumber"
                :value="item.id"
            />
          </el-select>
        </el-form-item>
        <el-form-item
            label="备注:"
            prop="remark"
        >
          <el-input v-model="formState.remark" type="textarea" placeholder="请输入备注"/>
        </el-form-item>
      </el-form>
      <el-button type="primary" size="small" @click="isShowProductsModal = true" :disabled="!formState.purchaseLedgerId">添加产品</el-button>
      <el-table :data="products" border>
        <el-table-column align="center"
                         type="selection"
                         width="55" />
        <el-table-column align="center"
                         label="序号"
                         type="index"
                         width="60" />
        <el-table-column label="产品大类"
                         prop="productCategory" />
        <el-table-column label="规格型号"
                         prop="specificationModel" />
        <el-table-column label="单位"
                         prop="unit"
                         width="70" />
        <el-table-column label="数量"
                         prop="quantity"
                         width="70" />
        <el-table-column label="库存预警数量"
                         prop="warnNum"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column label="税率(%)"
                         prop="taxRate"
                         width="80" />
        <el-table-column label="含税单价(元)"
                         prop="taxInclusiveUnitPrice"
                         :formatter="formattedNumber"
                         width="150" />
        <el-table-column label="含税总价(元)"
                         prop="taxInclusiveTotalPrice"
                         :formatter="formattedNumber"
                         width="150" />
        <el-table-column label="不含税总价(元)"
                         prop="taxExclusiveTotalPrice"
                         :formatter="formattedNumber"
                         width="150" />
        <el-table-column label="是否质检"
                         prop="isChecked"
                         width="150">
          <template #default="scope">
            <el-tag :type="scope.row.isChecked ? 'success' : 'info'">
              {{ scope.row.isChecked ? '是' : '否' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column fixed="right"
                         label="操作"
                         width="120"
                         align="center">
          <template #default="scope">
            <el-button
              link
              type="primary"
              size="small"
            >
              ç¼–辑
            </el-button>
            <el-button
                link
                type="danger"
                size="small"
                @click="delProduct(scope.$index)"
            >
              åˆ é™¤
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="handleSubmit">确认</el-button>
          <el-button @click="closeModal">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <ProductList
        v-if="isShowProductsModal"
        v-model:visible="isShowProductsModal"
        :purchase-ledger-id="formState.purchaseLedgerId"
        @completed="handleAddProduct"
    />
  </div>
</template>
<script setup>
import {ref, computed, getCurrentInstance} from "vue";
import {createPurchaseReturnOrder} from "@/api/procurementManagement/purchase_return_order.js";
import {getOptions, purchaseList} from "@/api/procurementManagement/procurementLedger.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
const ProductList = defineAsyncComponent(() => import("@/views/procurementManagement/purchaseReturnOrder/ProductList.vue"));
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
  }
});
let { proxy } = getCurrentInstance()
const emit = defineEmits(['update:visible', 'completed']);
// å“åº”式数据(替代选项式的 data)
const formState = ref({
  no: '',
  isDefaultNo: true,
  returnType: 0,
  remark: '',
  supplierId: undefined,
  projectId: undefined,
  projectPhase: undefined,
  preparedAt: undefined,
  preparedUserId: undefined,
  returnUserId: undefined,
  purchaseLedgerId: undefined,
});
// ä¾›åº”商选项
const supplierOptions = ref([])
// é¡¹ç›®é€‰é¡¹
const projectOptions = ref([])
// é¡¹ç›®é˜¶æ®µé€‰é¡¹
const projectStageOptions = ref([
  {
    label: '立项',
    value: 0,
  },
  {
    label: '设计',
    value: 1,
  },
  {
    label: '采购',
    value: 2,
  },
  {
    label: '生产',
    value: 3,
  },
  {
    label: '出货',
    value: 4,
  }
])
// ç”¨æˆ·é€‰é¡¹
const userOptions = ref([])
// é‡‡è´­å°è´¦é€‰é¡¹
const purchaseLedgerOptions = ref([])
// äº§å“åˆ—表数据
const products = ref([])
// æ˜¯å¦å±•示产品列表数据
const isShowProductsModal = ref(false)
const isShow = computed({
  get() {
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
  },
});
const formattedNumber = (row, column, cellValue) => {
  return parseFloat(cellValue).toFixed(2);
};
const closeModal = () => {
  isShow.value = false;
};
// èŽ·å–ä¾›åº”å•†é€‰é¡¹
const fetchSupplierOptions = () => {
  if (supplierOptions.value.length > 0) {
    return
  }
  getOptions().then((res) => {
    supplierOptions.value = res.data;
  });
}
// èŽ·å–é¡¹ç›®é€‰é¡¹
const fetchProjectOptions = () => {
  if (projectOptions.value.length > 0) {
    return
  }
  // todo é¡¹ç›®é€‰é¡¹
}
// èŽ·å–ç”¨æˆ·é€‰é¡¹
const fetchUserOptions = () => {
  if (userOptions.value.length > 0) {
    return
  }
  userListNoPageByTenantId().then((res) => {
    userOptions.value = res.data;
  });
}
// å¤„理改变供应商数据
const handleChangeSupplierId = () => {
  formState.value.purchaseLedgerId = undefined
  fetchPurchaseLedgerOptions()
}
// èŽ·å–é‡‡è´­å°è´¦é€‰é¡¹
const fetchPurchaseLedgerOptions = () => {
  purchaseLedgerOptions.value = []
  if (formState.value.supplierId) {
    purchaseList({supplierId: formState.value.supplierId}).then((res) => {
      purchaseLedgerOptions.value = res.rows;
    });
  }
}
// å¤„理改变采购台账数据
const handleChangePurchaseLedgerId = () => {
  products.value = []
}
// å¤„理改变是否默认编号
const handleChangeIsDefaultNo = (checked) => {
  if (checked) {
    formState.value.no = ''
  }
}
// å¢žåŠ äº§å“
const handleAddProduct = (selectedRows) => {
  products.value.push(...selectedRows)
}
// åˆ é™¤å•项产品
const delProduct = (index) => {
  products.value.splice(index, 1)
}
// æäº¤è¡¨å•
const handleSubmit = () => {
  proxy.$refs["formRef"].validate(valid => {
    if (valid) {
      createPurchaseReturnOrder(formState.value).then(res => {
        // å…³é—­æ¨¡æ€æ¡†
        isShow.value = false;
        // å‘ŠçŸ¥çˆ¶ç»„件已完成
        emit('completed');
        proxy.$modal.msgSuccess("提交成功");
      })
    }
  })
};
defineExpose({
  closeModal,
  handleSubmit,
  isShow,
});
</script>
src/views/procurementManagement/purchaseReturnOrder/ProductList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,151 @@
<template>
  <template>
    <div>
      <el-dialog
          v-model="isShow"
          title="新增产品"
          width="1200"
          @close="closeModal"
      >
        <div class="table_list">
          <el-table :data="tableData"
                    border
                    @selection-change="handleChangeSelection">
            <el-table-column align="center"
                             type="selection"
                             width="55" />
            <el-table-column align="center"
                             label="序号"
                             type="index"
                             width="60" />
            <el-table-column label="产品大类"
                             prop="productCategory" />
            <el-table-column label="规格型号"
                             prop="specificationModel" />
            <el-table-column label="单位"
                             prop="unit"
                             width="70" />
            <el-table-column label="数量"
                             prop="quantity"
                             width="70" />
            <el-table-column label="库存预警数量"
                             prop="warnNum"
                             width="120"
                             show-overflow-tooltip />
            <el-table-column label="税率(%)"
                             prop="taxRate"
                             width="80" />
            <el-table-column label="含税单价(元)"
                             prop="taxInclusiveUnitPrice"
                             :formatter="formattedNumber"
                             width="150" />
            <el-table-column label="含税总价(元)"
                             prop="taxInclusiveTotalPrice"
                             :formatter="formattedNumber"
                             width="150" />
            <el-table-column label="不含税总价(元)"
                             prop="taxExclusiveTotalPrice"
                             :formatter="formattedNumber"
                             width="150" />
            <el-table-column label="是否质检"
                             prop="isChecked"
                             width="150">
              <template #default="scope">
                <el-tag :type="scope.row.isChecked ? 'success' : 'info'">
                  {{ scope.row.isChecked ? '是' : '否' }}
                </el-tag>
              </template>
            </el-table-column>
          </el-table>
          <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
                      :page="page.current" :limit="page.size" @pagination="paginationChange" />
        </div>
        <template #footer>
          <div class="dialog-footer">
            <el-button type="primary" :disabled="selectedRows.length === 0" @click="handleSubmit">确认</el-button>
            <el-button @click="closeModal">取消</el-button>
          </div>
        </template>
      </el-dialog>
    </div>
  </template>
</template>
<script setup>
import {computed, reactive, ref} from "vue";
import {productList} from "@/api/procurementManagement/procurementLedger.js";
import {ElMessage} from "element-plus";
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
  },
  purchaseLedgerId: {
    type: Number,
    required: true,
  }
});
const emit = defineEmits(['update:visible', 'completed']);
const isShow = computed({
  get() {
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
  },
});
const tableData = ref([])
const selectedRows = ref([])
const tableLoading = ref(false)
const page = reactive({
  current: 1,
  size: 100,
})
const total = ref(0)
const formattedNumber = (row, column, cellValue) => {
  return parseFloat(cellValue).toFixed(2);
};
const paginationChange = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList()
}
const handleChangeSelection = (val) => {
  selectedRows.value = val;
}
const fetchData = () => {
  tableLoading.value = true;
  productList({salesLedgerId: props.purchaseLedgerId, type: 2}).then((res) => {
    tableData.value = res.data;
  }).finally(() => {
    tableLoading.value = false;
  })
}
const handleSubmit = () => {
  if (selectedRows.value.length === 0) {
    ElMessage.warning("请选择一条产品");
    return;
  }
  emit('completed', selectedRows.value);
}
const closeModal = () => {
  isShow.value = false;
};
onMounted(() => {
  fetchData()
})
</script>
src/views/procurementManagement/purchaseReturnOrder/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,109 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <el-form :model="searchForm"
               :inline="true">
        <el-form-item label="退料单号:">
          <el-input v-model="searchForm.no"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    @change="handleQuery" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary"
                     @click="handleQuery"> æœç´¢ </el-button>
        </el-form-item>
      </el-form>
      <div>
        <el-button type="primary" @click="isShowNewModal = true">新增</el-button>
      </div>
    </div>
    <div class="table_list">
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange" :row-key="row => row.id" style="width: 100%" height="calc(100vh - 18.5em)">
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="退料单号" prop="no" show-overflow-tooltip />
        <el-table-column label="退货方式" prop="returnType" show-overflow-tooltip />
        <el-table-column label="供应商名称" prop="supplierName" show-overflow-tooltip />
        <el-table-column label="关联单号" prop="purchaseContractNumber" show-overflow-tooltip />
        <el-table-column label="退料人" prop="returnUserName" show-overflow-tooltip />
        <el-table-column label="备注" prop="remark"  show-overflow-tooltip />
        <el-table-column label="创建人" prop="createUserName"  show-overflow-tooltip />
        <el-table-column label="创建时间" prop="createTime" show-overflow-tooltip />
        <el-table-column label="最近更新时间" prop="updateTime" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="60" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small">详情</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
                  :page="page.current" :limit="page.size" @pagination="paginationChange" />
    </div>
    <new v-if="isShowNewModal"
         v-model:visible="isShowNewModal"
         @completed="handleQuery" />
  </div>
</template>
<script setup>
import pagination from '@/components/PIMTable/Pagination.vue'
import { ref, reactive, toRefs, onMounted } from 'vue'
import {findPurchaseReturnOrderListPage} from "@/api/procurementManagement/purchase_return_order.js";
const New = defineAsyncComponent(() => import("@/views/procurementManagement/purchaseReturnOrder/New.vue"));
const tableData = ref([])
const selectedRows = ref([])
const tableLoading = ref(false)
const page = reactive({
  current: 1,
  size: 100,
})
const total = ref(0)
// æ˜¯å¦æ˜¾ç¤ºæ–°å¢žå¼¹æ¡†
const isShowNewModal = ref(false)
const data = reactive({
  searchForm: {
    no: '',
  }
})
const { searchForm } = toRefs(data)
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1
  getList()
}
const paginationChange = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList()
}
const getList = () => {
  tableLoading.value = true
  findPurchaseReturnOrderListPage({ ...searchForm.value, ...page }).then(res => {
    tableLoading.value = false
    tableData.value = res.data.records
    total.value = res.data.total
  }).catch(() => {
    tableLoading.value = false
  })
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  // è¿‡æ»¤æŽ‰å­æ•°æ®
  selectedRows.value = selection.filter(item => item.id);
}
onMounted(() => {
  getList()
})
</script>
src/views/salesManagement/returnOrder/components/formDia.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,489 @@
<template>
  <div>
    <el-dialog v-model="dialogFormVisible" :title="operationType === 'edit' ? '编辑退货单' : '新增退货单'" width="90%" @close="closeDia">
      <div>
        <span class="descriptions">基本信息</span>
        <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
          <el-row :gutter="30">
            <el-col :span="4">
              <el-form-item label="退货单号:" prop="returnNo">
                <el-input
                  :disabled="operationType === 'edit' || form.returnNoCheckbox"
                  v-model="form.returnNo"
                  placeholder="使用系统编号"
                  class="input-with-select"
                >
                  <template v-if="operationType !== 'edit'" #append>
                    <el-checkbox v-model="form.returnNoCheckbox" @change="handleReturnNoCheckboxChange"></el-checkbox>
                  </template>
                </el-input>
              </el-form-item>
            </el-col>
            <el-col :span="4">
              <el-form-item label="客户名称:" prop="customerId">
                <el-select v-model="form.customerId" filterable placeholder="请选择客户" @change="customerNameChange">
                  <el-option
                    v-for="item in customerNameOptions"
                    :key="item.value"
                    :label="item.label"
                    :value="item.id"
                  />
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="4">
              <el-form-item label="关联出库单号:" prop="shippingId">
                <el-select v-model="form.shippingId" filterable placeholder="请选择出库单号" @change="outboundNoChange">
                  <el-option
                    v-for="item in outboundOptions"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                  />
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="4">
              <el-form-item label="项目阶段:" prop="projectStage">
                <el-input v-model="form.projectStage" placeholder="项目阶段" />
              </el-form-item>
            </el-col>
            <el-col :span="4">
              <el-form-item label="制单人:" prop="maker">
                <el-select v-model="form.maker" filterable placeholder="请选择制单人">
                  <el-option v-for="u in userOptions" :key="u.value" :label="u.label" :value="u.value" />
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="4">
              <el-form-item label="制单时间:" prop="makeTime">
                <el-date-picker v-model="form.makeTime" type="datetime" style="width:100%" value-format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss" />
              </el-form-item>
            </el-col>
            <el-col :span="4">
              <el-form-item label="结算人:" prop="settler">
                <el-select v-model="form.settler" filterable placeholder="请选择结算人">
                  <el-option v-for="u in userOptions" :key="u.value" :label="u.label" :value="u.value" />
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="4">
              <el-form-item label="状态:" prop="status">
                <el-select v-model="form.status" placeholder="请选择状态">
                  <el-option label="待审核" :value="0" />
                  <el-option label="审核中" :value="1" />
                  <el-option label="已审核" :value="2" />
                </el-select>
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
        <hr>
        <div style="padding-top: 20px">
          <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px">
            <span class="descriptions" style="margin-bottom:0">产品列表</span>
            <el-button type="primary" @click="openProductSelection" :disabled="!form.shippingId">添加产品</el-button>
          </div>
          <PIMTable :isShowPagination="false" rowKey="id" :column="tableColumn" :tableData="tableData">
            <template #returnQuantity="{ row }">
              <el-input
                v-model="row.returnQuantity"
                style="width:100px"
                placeholder="请输入"
                type="number"
                @input="(val) => handleReturnQuantityChange(val, row)"
              />
            </template>
            <template #action="{ row, index }">
              <el-button type="danger" link @click="deleteRow(index)">删除</el-button>
            </template>
          </PIMTable>
        </div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <el-dialog v-model="productSelectionVisible" title="选择产品" width="70%" append-to-body>
      <el-table
        :data="availableProducts"
        style="width: 100%"
        @selection-change="handleSelectionChange"
        ref="productTableRef"
        row-key="id"
      >
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" prop="productCategory" label="产品大类" />
        <el-table-column align="center" prop="specificationModel" label="规格型号" />
        <el-table-column align="center" prop="unit" label="单位" />
        <el-table-column align="center" prop="quantity" label="总数量" />
        <el-table-column align="center" prop="unQuantity" label="未退货数量" />
        <el-table-column align="center" label="已退货数量">
          <template #default="{ row }">{{ calcAlreadyReturned(row) }}</template>
        </el-table-column>
      </el-table>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="confirmProductSelection">确认添加</el-button>
          <el-button @click="productSelectionVisible = false">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { reactive, ref, toRefs, getCurrentInstance } from "vue";
import { returnManagementAdd, returnManagementUpdate, returnManagementGetByShippingId, getSalesLedger, returnManagementGetById } from "@/api/salesManagement/returnOrder.js";
import { getAllCustomerList } from "@/api/customerService/index.js";
import useUserStore from "@/store/modules/user.js";
import { userListNoPageByTenantId } from "@/api/system/user.js";
import { listProject } from "@/api/oaSystem/projectManagement.js";
const { proxy } = getCurrentInstance();
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
const operationType = ref('');
const formRef = ref(null);
const userStore = useUserStore();
const data = reactive({
  form: {
    returnNoCheckbox: true,
    returnNo: "",
    customerId: "",
    shippingId: "",
    projectId: "",
    projectStage: "",
    maker: "",
    makeTime: "",
    settler: "",
    status: 0,
  },
  rules: {
    returnNo: [{
      validator: (rule, value, callback) => {
        if (form.value.returnNoCheckbox) return callback();
        if (!value) return callback(new Error("请输入退货单号"));
        callback();
      }, trigger: "blur"
    }],
    customerId: [{ required: true, message: "请选择客户", trigger: "change" }],
    shippingId: [{ required: true, message: "请选择关联出库单号", trigger: "change" }],
  }
});
const { form, rules } = toRefs(data);
const calcAlreadyReturned = (row) => {
  const total = Number(row?.quantity ?? row?.totalQuantity ?? row?.totalReturnNum ?? 0);
  const un = Number(row?.unQuantity ?? 0);
  if (!Number.isFinite(total) || !Number.isFinite(un)) return 0;
  return Math.max(total - un, 0);
};
const tableColumn = ref([
  {align: "center", label: "产品大类", prop: "productCategory" },
  {align: "center", label: "规格型号", prop: "specificationModel" },
  {align: "center", label: "单位", prop: "unit", width: 80 },
  {align: "center", label: "总数量", prop: "quantity", width: 120 },
  {align: "center", label: "已退货数量", prop: "totalReturnNum", width: 120 },
  {align: "center", label: "未退货数量", prop: "unQuantity", width: 120 },
  {align: "center", label: "退货数量", prop: "returnQuantity", dataType: "slot", slot: "returnQuantity", width: 120 },
  {align: "center", label: "操作" , prop: "action", dataType: "slot", slot: "action", width: 120 },
]);
const tableData = ref([]);
const customerNameOptions = ref([]);
const outboundOptions = ref([]);
const userOptions = ref([]);
const projectOptions = ref([]);
const deleteRow = (index) => {
  tableData.value.splice(index, 1);
};
const normalizeDetailRow = (raw) => {
  const productId = raw?.returnSaleLedgerProductId ?? raw?.saleLedgerProductId ?? raw?.id;
  const returnSaleProductId = raw?.returnSaleProductId ?? raw?.id;
  const num = Number(raw?.num ?? raw?.returnQuantity ?? 0);
  return {
    ...raw,
    id: productId,
    returnSaleProductId,
    returnSaleLedgerProductId: productId,
    num,
    returnQuantity: Number.isFinite(num) ? num : 0,
  };
};
const setFormForEdit = async (row) => {
  const res = await returnManagementGetById({ returnManagementId: row?.id });
  console.log("res", res);
  const detail = res?.data ?? res ?? {};
  Object.assign(form.value, detail);
  form.value.returnNoCheckbox = true;
  if (form.value.customerId) {
    await customerNameChange(form.value.customerId, false);
  }
  if (form.value.shippingId) {
    await outboundNoChange(form.value.shippingId, false);
  }
  const list =
    detail?.returnSaleProducts ||
    detail?.returnSaleProductList ||
    detail?.returnSaleProductDtoData ||
    [];
  tableData.value = Array.isArray(list)
    ? list.map((raw) => {
        const normalized = normalizeDetailRow(raw);
        const product = availableProducts.value.find((p) => p.id === normalized.id);
        return product ? { ...product, ...normalized } : normalized;
      })
    : [];
};
const openDialog = async (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
  proxy.resetForm("formRef");
  await Promise.all([initCustomers(), initUsers(), initProjects()]);
  if (type === "edit") {
    await setFormForEdit(row);
  } else {
    tableData.value = [];
    Object.assign(form.value, {
      returnNoCheckbox: true,
      returnNo: "",
      customerId: "",
      shippingId: "",
      projectId: "",
      projectStage: "",
      maker: "",
      makeTime: "",
      settler: "",
      status: 0,
    });
    form.value.maker = userStore.nickName || userStore.name || "";
    form.value.makeTime = new Date().toISOString().replace('T', ' ').split('.')[0]; // Default to now
    form.value.status = 0; // Default status
  }
};
const submitForm = () => {
  proxy.$refs["formRef"].validate(valid => {
    if (!valid) return;
    const returnSaleProducts = (tableData.value || []).map(el => ({
      returnSaleLedgerProductId: el.returnSaleLedgerProductId ?? el.id,
      num: Number(el.num ?? el.returnQuantity ?? 0),
      id: operationType.value === "edit" ? (el.returnSaleProductId ?? "") : ""
    }));
    const payload = { ...form.value, returnSaleProducts };
    delete payload.returnNoCheckbox;
    if (operationType.value === "add" && form.value.returnNoCheckbox) delete payload.returnNo;
    if (operationType.value === "add") {
      returnManagementAdd(payload).then(() => {
        proxy.$modal.msgSuccess("新增成功");
        closeDia();
      });
    } else {
      returnManagementUpdate(payload).then(() => {
        proxy.$modal.msgSuccess("修改成功");
        closeDia();
      });
    }
  });
};
const closeDia = () => {
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
  emit('close');
};
const initCustomers = async () => {
  const res = await getAllCustomerList({});
  if (res?.records) {
    customerNameOptions.value = res.records.map(item => ({
      label: item.customerName,
      value: item.customerName, // Keep value as name if needed for other logic, but request says customerId
      id: item.id,
      code: item.customerCode
    }));
  }
};
const initUsers = async () => {
  const res = await userListNoPageByTenantId();
  if (res?.data) {
    userOptions.value = res.data.map(u => ({ label: u.nickName || u.userName, value: u.nickName || u.userName }));
  }
};
const initProjects = async () => {
  try {
    const res = await listProject({ pageSize: 1000 });
    if (res?.rows) {
      projectOptions.value = res.rows.map(p => ({ label: p.projectName, value: p.id }));
    }
  } catch (e) {
    console.error("Failed to load projects", e);
  }
};
const handleReturnNoCheckboxChange = (checked) => {
  if (checked) form.value.returnNo = "";
  formRef.value?.validateField('returnNo');
};
const customerNameChange = async (val, clearDownstream = true) => {
  // val is customerId now
  if (clearDownstream) {
    form.value.shippingId = "";
    outboundOptions.value = [];
  }
  // Find customer name for getSalesLedger if it requires name
  const customer = customerNameOptions.value.find(c => c.id === val);
  if (!customer) return;
  // Assuming getSalesLedger takes customerName. If it takes ID, adjust accordingly.
  // Previous code used customerName. Let's try passing customerName.
  getSalesLedger({
    customerName: customer.label,
  }).then(res => {
    if(res.code === 200){
      outboundOptions.value = res.data.map(item => ({
        label: item.salesContractNo, // Or whatever the outbound number field is
        value: item.id,
      }))
    }
  })
};
const outboundNoChange = async (val, clearTable = true) => {
  // val is shippingId
  let res = await returnManagementGetByShippingId({ shippingId: val });
  if(res.code === 200){
    // If backend returns project info, set it
    if (res.data.projectId) form.value.projectId = res.data.projectId;
    if (res.data.projectStage) form.value.projectStage = res.data.projectStage;
    // Store available products for selection
    availableProducts.value = res.data.productDtoData || [];
    if (clearTable) tableData.value = [];
  }
};
const handleReturnQuantityChange = (val, row) => {
  if (val === "" || val === null) return;
  const max = row.unQuantity === undefined || row.unQuantity === null ? Infinity : Number(row.unQuantity || 0);
  const current = Number(val);
  if (current > max) {
    // Need nextTick to ensure update if user typed too fast or pasted
    proxy.$nextTick(() => {
      row.returnQuantity = max;
      row.num = max;
    });
    proxy.$modal.msgWarning(`退货数量不能超过未退货数量(${max})`);
  } else if (current < 0) {
    proxy.$nextTick(() => {
      row.returnQuantity = 0;
      row.num = 0;
    });
  } else {
    row.num = current;
  }
};
const availableProducts = ref([]);
const productSelectionVisible = ref(false);
const selectedProducts = ref([]);
const openProductSelection = () => {
  productSelectionVisible.value = true;
  // Pre-select items already in tableData
  proxy.$nextTick(() => {
    if (proxy.$refs.productTableRef) {
      proxy.$refs.productTableRef.clearSelection();
      availableProducts.value.forEach(row => {
        if (tableData.value.some(item => item.id === row.id)) {
          proxy.$refs.productTableRef.toggleRowSelection(row, true);
        }
      });
    }
  });
};
const handleSelectionChange = (val) => {
  selectedProducts.value = val;
};
// Removed checkSelectable to allow toggling existing items
const confirmProductSelection = () => {
  // Rebuild tableData based on selection, preserving existing data (returnQuantity)
  const newTableData = [];
  selectedProducts.value.forEach(product => {
    // Check if product was already in tableData to preserve user input
    const existing = tableData.value.find(item => item.id === product.id);
    if (existing) {
      newTableData.push(existing);
    } else {
      // Create new entry
      newTableData.push({
        ...product, // Keep all product display fields (productName, model, unit, etc.)
        // Map to backend entity structure for submission
        returnSaleLedgerProductId: product.id,
        returnQuantity: 0, // Default input
        num: 0, // Backend quantity field
        // Ensure display fields are available if they come from 'product'
        // If product has different field names than tableColumn expects, map them here
        productName: product.productName,
        specificationModel: product.specificationModel,
        unit: product.unit,
        quantity: product.quantity,
        totalReturnNum: product.totalReturnNum,
        unQuantity: product.unQuantity
      });
    }
  });
  tableData.value = newTableData;
  productSelectionVisible.value = false;
};
defineExpose({ openDialog });
</script>
<style scoped lang="scss">
.descriptions {
  margin-bottom: 20px;
  display: inline-block;
  font-size: 1rem;
  font-weight: 600;
  padding-left: 12px;
  position: relative;
}
.descriptions::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 4px;
  height: 1rem;
  background-color: #002FA7;
  border-radius: 2px;
}
</style>
src/views/salesManagement/returnOrder/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,294 @@
<template>
  <div class="app-container">
    <div class="search-wrapper">
      <el-form :model="searchForm" class="demo-form-inline">
        <el-row :gutter="20">
          <el-col :span="4">
            <el-form-item>
              <el-input v-model="searchForm.returnNo" placeholder="请输入退货单号" clearable />
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <el-form-item>
              <el-input v-model="searchForm.customerName" placeholder="客户名称" clearable />
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <el-form-item>
              <el-input v-model="searchForm.salesContractNo" placeholder="销售单号" clearable />
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <el-form-item>
              <el-input v-model="searchForm.shippingNo" placeholder="关联出库单号" clearable />
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <el-form-item>
              <el-button type="primary" @click="handleQuery">搜索</el-button>
              <el-button @click="handleReset">重置</el-button>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
    </div>
    <div class="table_list">
      <div class="table_header" style="display:flex;justify-content:space-between;align-items:center;">
        <div>
          <el-button type="primary" @click="openForm('add')">新建销售退货</el-button>
        </div>
        <div>
          <el-button type="danger" plain @click="handleDelete">删除</el-button>
          <el-button @click="columnsDialogVisible = true">列表字段</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        :column="visibleColumns"
        :tableData="tableData"
        :page="page"
        :isSelection="true"
        @selection-change="handleSelectionChange"
        :tableLoading="tableLoading"
        @pagination="pagination"
      />
    </div>
    <form-dia ref="formDia" @close="handleQuery" />
    <el-dialog v-model="columnsDialogVisible" title="自定义显示列项" width="600px">
      <div class="columns-tip">注:列表项显示不得少于5项;拖动右侧把手可调整显示顺序</div>
      <ul class="columns-list">
        <li v-for="(col, idx) in allColumns" :key="col.prop"
            class="columns-item"
            draggable="true"
            @dragstart="onDragStart(idx)"
            @dragover.prevent
            @drop="onDrop(idx)">
          <el-checkbox v-model="col.selected">{{ col.label }}</el-checkbox>
          <span class="drag-handle">≡</span>
        </li>
      </ul>
      <template #footer>
        <el-button @click="resetColumns">恢复默认</el-button>
        <el-button type="primary" @click="saveColumns">保存</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { reactive, ref, toRefs, computed, getCurrentInstance, nextTick, onMounted } from "vue";
import { ElMessageBox } from "element-plus";
import FormDia from "./components/formDia.vue";
import { returnManagementList, returnManagementDel, returnManagementHandle } from "@/api/salesManagement/returnOrder.js";
const { proxy } = getCurrentInstance();
const formDia = ref();
const openForm = (type, row) => {
  nextTick(() => formDia.value?.openDialog(type, row));
};
const handleRowDelete = (row) => {
  if (!row?.id) return;
  ElMessageBox.confirm("该退货单将被删除,是否确认删除?", "删除提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    returnManagementDel({ ids: String(row.id) }).then(() => {
      proxy.$modal.msgSuccess("删除成功");
      getList();
    });
  });
};
const handleRowHandle = (row) => {
  if (!row?.id) return;
  ElMessageBox.confirm("是否处理该退货单?处理后将无法修改", "处理提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    returnManagementHandle({ returnManagementId: String(row.id) }).then(() => {
      proxy.$modal.msgSuccess("处理成功");
      getList();
    });
  });
}
const data = reactive({
  searchForm: {
    returnNo: "",
    status: "",
    customerName: "",
    salesContractNo: "",
    salesman: "",
    shippingNo: "",
    projectName: "",
    salesLedgerId: "",
    makeTime: ""
  }
});
const { searchForm } = toRefs(data);
const documentStatusOptions = ref([
  { label: "待审核", value: 0 },
  { label: "审核中", value: 1 },
  { label: "已审核", value: 2 }
]);
const defaultColumns = [
  { label: "退货单号", prop: "returnNo", minWidth: 160 },
  { label: "单据状态", prop: "status", minWidth: 120, formatData: (v) => ({ "0": "待审核", "1": "审核中", "2": "已审核" }[String(v)] ?? v) },
  { label: "制单时间", prop: "makeTime", minWidth: 170 },
  { label: "客户名称", prop: "customerName", minWidth: 220 },
  { label: "销售单号", prop: "salesContractNo", minWidth: 160 },
  { label: "业务员", prop: "salesman", minWidth: 120 },
  { label: "关联出库单号", prop: "shippingNo", minWidth: 170 },
  { label: "项目名称", prop: "projectName", minWidth: 180 },
  { label: "项目阶段", prop: "projectStage", minWidth: 120 },
  { label: "制单人", prop: "maker", minWidth: 120 },
  { label: "结算人", prop: "settler", minWidth: 120 },
  {
    label: "操作",
    prop: "operation",
    dataType: "action",
    align: "center",
    fixed: "right",
    width: 160,
    operation: [
      { name: "编辑", disabled: (row) => row.status !== 0, type: "text", clickFun: (row) => openForm("edit", row) },
      { name: "处理", disabled: (row) => row.status !== 0, type: "text", clickFun: (row) => handleRowHandle(row) },
      { name: "删除", disabled: (row) => row.status !== 0, type: "text", clickFun: (row) => handleRowDelete(row) },
    ],
  },
];
const COLUMNS_KEY = "return_order_columns_v2";
const columnsDialogVisible = ref(false);
const allColumns = ref([]);
const initColumns = () => {
  const saved = localStorage.getItem(COLUMNS_KEY);
  if (saved) {
    try {
      const parsed = JSON.parse(saved);
      // åˆå¹¶é»˜è®¤åˆ—与已保存配置,避免后续新增列丢失
      const map = new Map(parsed.map(c => [c.prop, c]));
      allColumns.value = defaultColumns.map(d => {
        const found = map.get(d.prop);
        return { ...d, selected: found ? !!found.selected : true };
      });
      // ä»¥ä¿å­˜çš„顺序为准
      const order = parsed.map(p => p.prop);
      allColumns.value.sort((a, b) => order.indexOf(a.prop) - order.indexOf(b.prop));
      return;
    } catch {}
  }
  allColumns.value = defaultColumns.map(c => ({ ...c, selected: true }));
};
initColumns();
const visibleColumns = computed(() => allColumns.value.filter(c => c.selected));
let dragFrom = -1;
const onDragStart = (idx) => {
  dragFrom = idx;
};
const onDrop = (to) => {
  if (dragFrom < 0 || dragFrom === to) return;
  const arr = [...allColumns.value];
  const [moved] = arr.splice(dragFrom, 1);
  arr.splice(to, 0, moved);
  allColumns.value = arr;
  dragFrom = -1;
};
const resetColumns = () => {
  allColumns.value = defaultColumns.map(c => ({ ...c, selected: true }));
  localStorage.removeItem(COLUMNS_KEY);
};
const saveColumns = () => {
  const toSave = allColumns.value.map(({ label, prop, width, selected }) => ({ label, prop, width, selected }));
  localStorage.setItem(COLUMNS_KEY, JSON.stringify(toSave));
  columnsDialogVisible.value = false;
};
const tableData = ref([]);
const tableLoading = ref(false);
const page = reactive({ current: 1, size: 10, total: 0 });
const selectedRows = ref([]);
const tableHeight = computed(() => "calc(100% - 80px)");
const handleReset = () => {
  Object.keys(searchForm.value).forEach(k => searchForm.value[k] = "");
};
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
const handleQuery = () => {
  page.current = 1;
  getList();
};
const pagination = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  returnManagementList({ ...searchForm.value, ...page }).then(res => {
    tableLoading.value = false;
    tableData.value = res?.data?.records || [];
    page.total = res?.data?.total || 0;
  }).finally(() => tableLoading.value = false);
};
const handleOut = () => {
  ElMessageBox.alert("导出功能待接入接口", "提示");
};
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length === 0) {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ids = selectedRows.value.map(i => i.id);
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    returnManagementDel({ ids: ids.join(",") }).then(() => {
      proxy.$modal.msgSuccess("删除成功");
      getList();
    });
  });
};
onMounted(() => {
  getList();
});
</script>
<style scoped lang="scss">
.search-wrapper {
  background: white;
  padding: 1rem 1rem 0 1rem;
  border: 8px;
  border-radius: 16px;
}
.table_list {
  height: calc(100vh - 230px);
  min-height: 360px;
  background: #fff;
  margin-top: 20px;
  display: flex;
  flex-direction: column;
}
.columns-tip{color:#909399;margin-bottom:10px;font-size:12px;}
.columns-list{list-style:none;padding:0;margin:0;max-height:360px;overflow:auto;}
.columns-item{display:flex;justify-content:space-between;align-items:center;padding:8px 10px;border:1px solid #f0f0f0;border-radius:6px;margin-bottom:8px;cursor:move;background:#fff;}
.columns-item .drag-handle{color:#909399;padding-left:12px;user-select:none;}
.table_header {
  margin-bottom: 15px;
}
</style>